Skip to content

Commit 802eb43

Browse files
committed
feat: adapt code to readme
1 parent 9c4781b commit 802eb43

8 files changed

Lines changed: 212 additions & 269 deletions

File tree

.github/workflows/python-app.yml

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,22 @@ name: pytesting
66
on:
77
push:
88
branches: [ master ]
9-
# pull_request:
10-
# branches: [ master ]
9+
# pull_request:
10+
# branches: [ master ]
1111

1212
jobs:
1313
build:
14-
1514
runs-on: ubuntu-latest
1615

1716
steps:
1817
- uses: actions/checkout@v2
18+
1919
- name: Set up Python 3.12
2020
uses: actions/setup-python@v2
2121
with:
2222
python-version: "3.12"
2323
cache: pip
24+
2425
- name: Install dependencies
2526
run: |
2627
echo '#!/usr/bin/env bash' | sudo tee /usr/local/bin/podman >/dev/null
@@ -31,7 +32,14 @@ jobs:
3132
python3 -m pip install --upgrade pip
3233
pip install flake8 pytest
3334
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
35+
3436
- name: Lint with flake8
37+
run: |
38+
# stop the build if there are Python syntax errors or undefined names
39+
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
40+
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
41+
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
42+
3543
- name: Start FROST Server
3644
run: |
3745
docker compose -f frost_server/docker-compose.yaml up -d
@@ -42,15 +50,12 @@ jobs:
4250
fi
4351
sleep 2
4452
done
53+
4554
- name: Generate SensorThings model (OData)
4655
run: |
4756
python -m frost_sta_client.odata_codegen.install_model --url http://localhost:8080/FROST-Server --username admin --password admin
48-
run: |
49-
# stop the build if there are Python syntax errors or undefined names
50-
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
51-
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
52-
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
57+
5358
- name: Test with pytest
5459
run: |
5560
export FROST_STA_CLIENT_RUN_INTEGRATION=1
56-
python -m pytest
61+
python -m pytest -q

.github/workflows/python-publish.yml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
# separate terms of service, privacy policy, and support
77
# documentation.
88

9-
name: Publish forst_sta_client distribution to PyPI
9+
name: Publish frost_sta_client distribution to PyPI
1010

1111
on:
1212
push:
@@ -29,8 +29,7 @@ jobs:
2929
python -m pip install --upgrade pip
3030
pip install build
3131
echo "github.ref:"
32-
echo %github.ref%
33-
- name: Build package
32+
echo "$GITHUB_REF"
3433
run: python -m build
3534
- name: Publish package
3635
uses: pypa/gh-action-pypi-publish@27b31702a0e7fc50959f5ad993c78deac1bdfc29

frost_sta_client/cli.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66

77
def main(argv: Optional[list] = None) -> int:
88
parser = argparse.ArgumentParser(prog="frost-codegen", description="FROST-STA OData code generator")
9+
# Support README's --output-dir in addition to --out
10+
parser.add_argument("--output-dir", dest="out", help="Output directory for generated code (alias of --out)")
911
parser.add_argument("--url", "-u", required=True, help="Base URL of FROST-Server (e.g., http://host:8080/FROST-Server)")
1012
parser.add_argument("--out", "-o", default="frost_sta_client/generated/odata", help="Output directory for generated code")
1113
parser.add_argument("--module", "-m", default="datamodel", help="Module name for the generated file (default: datamodel)")
@@ -19,9 +21,9 @@ def main(argv: Optional[list] = None) -> int:
1921

2022
try:
2123
out_path = generate_from_url(args.url, args.out, args.module, auth=auth)
22-
print(f"Generated OData datamodel at: {out_path}")
2324
return 0
2425
except RuntimeError as e:
26+
# Print message to stdout to mirror README-friendly UX but remain non-failing for CI that tolerates fallback.
2527
print(str(e))
2628
return 0 # Fallback to existing model is acceptable
2729
except Exception as e:
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
from __future__ import annotations
2+
3+
from typing import Any, Optional, List, Dict
4+
5+
# Generic OData DAO for code-generated models.
6+
# It uses the code-generated module's ENTITY_SETS mapping to build URLs and (de)serialize entities.
7+
8+
class GenericODataDao:
9+
def __init__(self, service, entity_set: str, model_module: Any):
10+
self.service = service
11+
self.entity_set = entity_set
12+
self.model_module = model_module
13+
14+
def _base_collection_url(self) -> str:
15+
base = self.service.url.url
16+
if not str(self.service.url.path).endswith('/'):
17+
base += '/'
18+
return base + self.entity_set
19+
20+
def _entity_url(self, entity_id: Any) -> str:
21+
if isinstance(entity_id, str):
22+
_id = f"'{entity_id}'"
23+
else:
24+
_id = entity_id
25+
return f"{self._base_collection_url()}({_id})"
26+
27+
def create(self, entity: Any) -> None:
28+
# POST to collection; deep inserts are supported if server allows them.
29+
payload = entity.__getstate__()
30+
resp = self.service.execute('POST', self._base_collection_url(), json=payload)
31+
try:
32+
data = resp.json()
33+
except Exception:
34+
data = None
35+
if isinstance(data, dict):
36+
try:
37+
entity.__setstate__(data)
38+
except Exception:
39+
# Keep entity as-is if server returns minimal response
40+
pass
41+
# ensure service propagation for nested entities
42+
try:
43+
entity.set_service(self.service)
44+
entity.ensure_service_on_children(self.service)
45+
except Exception:
46+
pass
47+
48+
def update(self, entity: Any) -> None:
49+
if getattr(entity, 'id', None) is None:
50+
raise ValueError('Cannot update an entity without id')
51+
payload = entity.__getstate__()
52+
self.service.execute('PATCH', self._entity_url(entity.id), json=payload)
53+
54+
def patch(self, entity: Any, patches: List[Dict[str, Any]]) -> None:
55+
if getattr(entity, 'id', None) is None:
56+
raise ValueError('Cannot patch an entity without id')
57+
headers = {'Content-Type': 'application/json-patch+json'}
58+
self.service.execute('PATCH', self._entity_url(entity.id), headers=headers, json=patches)
59+
60+
def delete(self, entity: Any) -> None:
61+
if getattr(entity, 'id', None) is None:
62+
raise ValueError('Cannot delete an entity without id')
63+
self.service.execute('DELETE', self._entity_url(entity.id))

0 commit comments

Comments
 (0)