Compare commits
7 Commits
6a64709d17
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| b8ae633ed5 | |||
| ea690f0479 | |||
| fc79f5c579 | |||
| ed8f93d7d0 | |||
| dfd582a439 | |||
| 96adaac01a | |||
| fb6d1276e6 |
@ -1,7 +1,7 @@
|
||||
# FairHopper JavaScript SDK
|
||||
|
||||
Requirements:
|
||||
- Node 1.16+
|
||||
- Node 18+
|
||||
|
||||
## Running demo
|
||||
|
||||
@ -10,8 +10,7 @@ Check `demo.js` for usage example.
|
||||
Edit `demo.js` and configure FairHopper Game host settings:
|
||||
|
||||
```javascript
|
||||
const FAIRHOPPER_HOST = "http://127.0.0.1";
|
||||
const FAIRHOPPER_PORT = 8010;
|
||||
const FAIRHOPPER_HOST = "http://127.0.0.1:8010";
|
||||
```
|
||||
|
||||
Run example using node:
|
||||
|
||||
@ -1,3 +1,13 @@
|
||||
class ValidationError extends Error {
|
||||
constructor(message) {
|
||||
if (!message) {
|
||||
message = "ValidationError";
|
||||
}
|
||||
super(message);
|
||||
this.name = "ValidationError";
|
||||
}
|
||||
}
|
||||
|
||||
class PlayerNotFoundError extends Error {
|
||||
constructor(message) {
|
||||
if (!message) {
|
||||
@ -65,6 +75,10 @@ class FairHopper {
|
||||
headers: this.defaultHeaders,
|
||||
body: JSON.stringify(payload),
|
||||
});
|
||||
switch (r.status) {
|
||||
case 422:
|
||||
throw new ValidationError();
|
||||
}
|
||||
return await r.json();
|
||||
}
|
||||
|
||||
@ -84,6 +98,8 @@ class FairHopper {
|
||||
throw new PlayerInactiveError();
|
||||
case 404:
|
||||
throw new PlayerNotFoundError();
|
||||
case 422:
|
||||
throw new ValidationError();
|
||||
}
|
||||
return await r.json();
|
||||
}
|
||||
@ -117,34 +133,8 @@ class FairHopper {
|
||||
throw new PlayerNotFoundError();
|
||||
case 409:
|
||||
throw new PositionError();
|
||||
}
|
||||
return await r.json();
|
||||
}
|
||||
|
||||
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();
|
||||
case 422:
|
||||
throw new ValidationError();
|
||||
}
|
||||
return await r.json();
|
||||
}
|
||||
|
||||
@ -1,10 +1,12 @@
|
||||
# FairHopper Python SDK
|
||||
|
||||
Requirements:
|
||||
- Python 3.10+
|
||||
- Python 3.7+
|
||||
|
||||
## Setting up environment
|
||||
|
||||
### Recommended: Using Poetry
|
||||
|
||||
Project uses [Poetry](https://python-poetry.org), ultimate dependency management software for Python.
|
||||
|
||||
Install Poetry:
|
||||
@ -17,6 +19,23 @@ Install virtual environment:
|
||||
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
|
||||
|
||||
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:
|
||||
|
||||
```python
|
||||
FAIRHOPPER_HOST = "http://127.0.0.1"
|
||||
FAIRHOPPER_PORT = 8010
|
||||
FAIRHOPPER_HOST = "http://127.0.0.1:8010"
|
||||
```
|
||||
|
||||
Activate environment and run example:
|
||||
|
||||
### Poetry
|
||||
|
||||
```sh
|
||||
poetry shell
|
||||
python demo.py
|
||||
```
|
||||
|
||||
### Virtualenv
|
||||
```sh
|
||||
source .env/bin/activate
|
||||
python demo.py
|
||||
```
|
||||
|
||||
@ -1,24 +1,35 @@
|
||||
from typing import Optional
|
||||
from pydantic import BaseModel
|
||||
from enum import Enum
|
||||
from typing import List, Optional
|
||||
|
||||
import requests
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class BaseError(Exception):
|
||||
...
|
||||
detail: str = "BaseError"
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.detail
|
||||
|
||||
|
||||
class PositionError(BaseError):
|
||||
...
|
||||
detail = "Invalid position"
|
||||
|
||||
|
||||
class PlayerInactiveError(BaseError):
|
||||
...
|
||||
detail = "Player inactive"
|
||||
|
||||
|
||||
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):
|
||||
@ -59,12 +70,6 @@ class Player(BaseModel):
|
||||
state: PlayerState
|
||||
|
||||
|
||||
class Product(BaseModel):
|
||||
name: str
|
||||
id: str
|
||||
description: Optional[str] = None
|
||||
|
||||
|
||||
class PingResponse(BaseModel):
|
||||
message: str
|
||||
|
||||
@ -84,10 +89,6 @@ class GameInfoResponse(BaseModel):
|
||||
destination: Destination
|
||||
|
||||
|
||||
class ProductListResponse(BaseModel):
|
||||
products: list[Product]
|
||||
|
||||
|
||||
class FairHopper:
|
||||
def __init__(self, host: str) -> None:
|
||||
self.host = host
|
||||
@ -105,7 +106,12 @@ class FairHopper:
|
||||
"player_name": player_name,
|
||||
}
|
||||
r = requests.post(self.format_url("/game"), json=payload)
|
||||
r.raise_for_status()
|
||||
|
||||
if r.status_code == 422:
|
||||
raise ValidationError()
|
||||
else:
|
||||
r.raise_for_status()
|
||||
|
||||
return StartGameResponse(**r.json())
|
||||
|
||||
def get_game_info(self) -> GameInfoResponse:
|
||||
@ -113,13 +119,15 @@ class FairHopper:
|
||||
r.raise_for_status()
|
||||
return GameInfoResponse(**r.json())
|
||||
|
||||
def get_player_info(self, id: str) -> PlayerInfoResponse:
|
||||
r = requests.get(self.format_url(f"/player/{id}"))
|
||||
def get_player_info(self, player_id: str) -> PlayerInfoResponse:
|
||||
r = requests.get(self.format_url(f"/player/{player_id}"))
|
||||
|
||||
if r.status_code == 403:
|
||||
raise PlayerInactiveError()
|
||||
elif r.status_code == 404:
|
||||
raise PlayerNotFoundError()
|
||||
elif r.status_code == 422:
|
||||
raise ValidationError()
|
||||
else:
|
||||
r.raise_for_status()
|
||||
|
||||
@ -147,21 +155,11 @@ class FairHopper:
|
||||
raise PlayerNotFoundError()
|
||||
elif r.status_code == 409:
|
||||
raise PositionError()
|
||||
elif r.status_code == 422:
|
||||
raise ValidationError()
|
||||
elif r.status_code == 423:
|
||||
raise GameLockedError()
|
||||
else:
|
||||
r.raise_for_status()
|
||||
|
||||
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
4
python/poetry.lock
generated
@ -215,5 +215,5 @@ socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"]
|
||||
|
||||
[metadata]
|
||||
lock-version = "2.0"
|
||||
python-versions = "^3.10"
|
||||
content-hash = "a8d95adf4819c22b47d4cac44f18a3bc5040d1c0487ead04cb2f09692de0d411"
|
||||
python-versions = "^3.7"
|
||||
content-hash = "6d7fc4c2ae40fa75f772d15817348dd97078b9aa25ecd3ad52e71bec25732be8"
|
||||
|
||||
@ -7,7 +7,7 @@ readme = "README.md"
|
||||
packages = [{include = "fh_sdk"}]
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = "^3.10"
|
||||
python = "^3.7"
|
||||
requests = "^2.28.2"
|
||||
pydantic = "^1.10.7"
|
||||
|
||||
|
||||
2
python/requirements.txt
Normal file
2
python/requirements.txt
Normal file
@ -0,0 +1,2 @@
|
||||
pydantic
|
||||
requests
|
||||
Reference in New Issue
Block a user