Compare commits

...

7 Commits

Author SHA1 Message Date
b8ae633ed5 Update readme 2023-05-12 20:53:44 +02:00
ea690f0479 Remove deprecated product functions 2023-05-11 19:54:23 +02:00
fc79f5c579 Remove purchase product code and add details to exceptions 2023-05-11 17:05:55 +02:00
ed8f93d7d0 Set required python version to 3.7 2023-04-23 11:51:06 +02:00
dfd582a439 Set required python version to 3.7 2023-04-23 11:48:11 +02:00
96adaac01a Update readme 2023-04-23 10:06:25 +02:00
fb6d1276e6 Better error handling 2023-04-23 10:03:50 +02:00
7 changed files with 85 additions and 70 deletions

View File

@ -1,7 +1,7 @@
# FairHopper JavaScript SDK # FairHopper JavaScript SDK
Requirements: Requirements:
- Node 1.16+ - Node 18+
## Running demo ## Running demo
@ -10,8 +10,7 @@ Check `demo.js` for usage example.
Edit `demo.js` and configure FairHopper Game host settings: Edit `demo.js` and configure FairHopper Game host settings:
```javascript ```javascript
const FAIRHOPPER_HOST = "http://127.0.0.1"; const FAIRHOPPER_HOST = "http://127.0.0.1:8010";
const FAIRHOPPER_PORT = 8010;
``` ```
Run example using node: Run example using node:

View File

@ -1,3 +1,13 @@
class ValidationError extends Error {
constructor(message) {
if (!message) {
message = "ValidationError";
}
super(message);
this.name = "ValidationError";
}
}
class PlayerNotFoundError extends Error { class PlayerNotFoundError extends Error {
constructor(message) { constructor(message) {
if (!message) { if (!message) {
@ -65,6 +75,10 @@ class FairHopper {
headers: this.defaultHeaders, headers: this.defaultHeaders,
body: JSON.stringify(payload), body: JSON.stringify(payload),
}); });
switch (r.status) {
case 422:
throw new ValidationError();
}
return await r.json(); return await r.json();
} }
@ -84,6 +98,8 @@ class FairHopper {
throw new PlayerInactiveError(); throw new PlayerInactiveError();
case 404: case 404:
throw new PlayerNotFoundError(); throw new PlayerNotFoundError();
case 422:
throw new ValidationError();
} }
return await r.json(); return await r.json();
} }
@ -117,34 +133,8 @@ class FairHopper {
throw new PlayerNotFoundError(); throw new PlayerNotFoundError();
case 409: case 409:
throw new PositionError(); throw new PositionError();
} case 422:
return await r.json(); throw new ValidationError();
}
async getProducts() {
const r = await fetch(this.formatUrl("/products"), {
headers: this.defaultHeaders,
});
return await r.json();
}
async purchaseProduct(playerId, productId) {
const url = this.formatUrl(`/player/${playerId}/product/purchase`);
const postData = {
product_id: productId,
};
const r = await fetch(url, {
method: "post",
headers: this.defaultHeaders,
body: JSON.stringify(postData),
});
switch (r.status) {
case 403:
throw new PlayerInactiveError();
case 404:
throw new PlayerNotFoundError();
case 409:
throw new PositionError();
} }
return await r.json(); return await r.json();
} }

View File

@ -1,10 +1,12 @@
# FairHopper Python SDK # FairHopper Python SDK
Requirements: Requirements:
- Python 3.10+ - Python 3.7+
## Setting up environment ## Setting up environment
### Recommended: Using Poetry
Project uses [Poetry](https://python-poetry.org), ultimate dependency management software for Python. Project uses [Poetry](https://python-poetry.org), ultimate dependency management software for Python.
Install Poetry: Install Poetry:
@ -17,6 +19,23 @@ Install virtual environment:
poetry install poetry install
``` ```
### Simple: Using virtualenv
Create virtual environment:
```sh
virtualenv .env
```
Activate virtual environment:
```sh
source .env/bin/activate
```
Install required dependencies:
```sh
pip install -r requirements.txt
```
## Running demo ## Running demo
Check `demo.py` for usage example. Check `demo.py` for usage example.
@ -24,13 +43,20 @@ Check `demo.py` for usage example.
Edit `demo.py` and configure FairHopper Game host settings: Edit `demo.py` and configure FairHopper Game host settings:
```python ```python
FAIRHOPPER_HOST = "http://127.0.0.1" FAIRHOPPER_HOST = "http://127.0.0.1:8010"
FAIRHOPPER_PORT = 8010
``` ```
Activate environment and run example: Activate environment and run example:
### Poetry
```sh ```sh
poetry shell poetry shell
python demo.py python demo.py
``` ```
### Virtualenv
```sh
source .env/bin/activate
python demo.py
```

View File

@ -1,24 +1,35 @@
from typing import Optional
from pydantic import BaseModel
from enum import Enum from enum import Enum
from typing import List, Optional
import requests import requests
from pydantic import BaseModel
class BaseError(Exception): class BaseError(Exception):
... detail: str = "BaseError"
def __str__(self) -> str:
return self.detail
class PositionError(BaseError): class PositionError(BaseError):
... detail = "Invalid position"
class PlayerInactiveError(BaseError): class PlayerInactiveError(BaseError):
... detail = "Player inactive"
class PlayerNotFoundError(BaseError): class PlayerNotFoundError(BaseError):
... detail = "Player not found"
class GameLockedError(BaseError):
detail = "Game locked. Product selection in progress."
class ValidationError(BaseError):
detail = "Validation error"
class Direction(str, Enum): class Direction(str, Enum):
@ -59,12 +70,6 @@ class Player(BaseModel):
state: PlayerState state: PlayerState
class Product(BaseModel):
name: str
id: str
description: Optional[str] = None
class PingResponse(BaseModel): class PingResponse(BaseModel):
message: str message: str
@ -84,10 +89,6 @@ class GameInfoResponse(BaseModel):
destination: Destination destination: Destination
class ProductListResponse(BaseModel):
products: list[Product]
class FairHopper: class FairHopper:
def __init__(self, host: str) -> None: def __init__(self, host: str) -> None:
self.host = host self.host = host
@ -105,7 +106,12 @@ class FairHopper:
"player_name": player_name, "player_name": player_name,
} }
r = requests.post(self.format_url("/game"), json=payload) r = requests.post(self.format_url("/game"), json=payload)
if r.status_code == 422:
raise ValidationError()
else:
r.raise_for_status() r.raise_for_status()
return StartGameResponse(**r.json()) return StartGameResponse(**r.json())
def get_game_info(self) -> GameInfoResponse: def get_game_info(self) -> GameInfoResponse:
@ -113,13 +119,15 @@ class FairHopper:
r.raise_for_status() r.raise_for_status()
return GameInfoResponse(**r.json()) return GameInfoResponse(**r.json())
def get_player_info(self, id: str) -> PlayerInfoResponse: def get_player_info(self, player_id: str) -> PlayerInfoResponse:
r = requests.get(self.format_url(f"/player/{id}")) r = requests.get(self.format_url(f"/player/{player_id}"))
if r.status_code == 403: if r.status_code == 403:
raise PlayerInactiveError() raise PlayerInactiveError()
elif r.status_code == 404: elif r.status_code == 404:
raise PlayerNotFoundError() raise PlayerNotFoundError()
elif r.status_code == 422:
raise ValidationError()
else: else:
r.raise_for_status() r.raise_for_status()
@ -147,21 +155,11 @@ class FairHopper:
raise PlayerNotFoundError() raise PlayerNotFoundError()
elif r.status_code == 409: elif r.status_code == 409:
raise PositionError() raise PositionError()
elif r.status_code == 422:
raise ValidationError()
elif r.status_code == 423:
raise GameLockedError()
else: else:
r.raise_for_status() r.raise_for_status()
return PlayerInfoResponse(**r.json()) return PlayerInfoResponse(**r.json())
def get_products(self) -> list[Product]:
r = requests.get(self.format_url("/products"))
response_data = ProductListResponse(**r.json())
return response_data.products
def purchase_product(self, player_id: str, product_id: str) -> Product:
url = self.format_url(f"/player/{player_id}/product/purchase")
payload = {
"product_id": product_id,
}
r = requests.post(url, json=payload)
r.raise_for_status()
return Product(**r.json())

4
python/poetry.lock generated
View File

@ -215,5 +215,5 @@ socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"]
[metadata] [metadata]
lock-version = "2.0" lock-version = "2.0"
python-versions = "^3.10" python-versions = "^3.7"
content-hash = "a8d95adf4819c22b47d4cac44f18a3bc5040d1c0487ead04cb2f09692de0d411" content-hash = "6d7fc4c2ae40fa75f772d15817348dd97078b9aa25ecd3ad52e71bec25732be8"

View File

@ -7,7 +7,7 @@ readme = "README.md"
packages = [{include = "fh_sdk"}] packages = [{include = "fh_sdk"}]
[tool.poetry.dependencies] [tool.poetry.dependencies]
python = "^3.10" python = "^3.7"
requests = "^2.28.2" requests = "^2.28.2"
pydantic = "^1.10.7" pydantic = "^1.10.7"

2
python/requirements.txt Normal file
View File

@ -0,0 +1,2 @@
pydantic
requests