Skip to content

Commit 19dc399

Browse files
committed
Implement missing /submodels functions
1 parent 2995264 commit 19dc399

5 files changed

Lines changed: 385 additions & 17 deletions

File tree

README.md

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -107,28 +107,28 @@ The tables below show the mapping between BaSyx AAS REST API endpoints and the i
107107

108108
### Submodels
109109

110-
| Method | BaSyx Endpoint | Shellsmith Function |
111-
|--------|---------------------------------------------|---------------------|
112-
| GET | `/submodels` | `get_submodels` |
113-
| POST | `/submodels` | |
114-
| GET | `/submodels/{submodelIdentifier}` | `get_submodel` |
115-
| PUT | `/submodels/{submodelIdentifier}` | |
116-
| DELETE | `/submodels/{submodelIdentifier}` | `delete_submodel` |
117-
| GET | `/submodels/{submodelIdentifier}/$value` | |
118-
| PATCH | `/submodels/{submodelIdentifier}/$value` | |
119-
| GET | `/submodels/{submodelIdentifier}/$metadata` | |
110+
| Method | BaSyx Endpoint | Shellsmith Function |
111+
|--------|---------------------------------------------|-------------------------|
112+
| GET | `/submodels` | `get_submodels` |
113+
| POST | `/submodels` | `post_submodel` |
114+
| GET | `/submodels/{submodelIdentifier}` | `get_submodel` |
115+
| PUT | `/submodels/{submodelIdentifier}` | `put_submodel` |
116+
| DELETE | `/submodels/{submodelIdentifier}` | `delete_submodel` |
117+
| GET | `/submodels/{submodelIdentifier}/$value` | `get_submodel_value` |
118+
| PATCH | `/submodels/{submodelIdentifier}/$value` | `patch_submodel_value` |
119+
| GET | `/submodels/{submodelIdentifier}/$metadata` | `get_submodel_metadata` |
120120

121121
### Submodel Elements
122122

123123
| Method | BaSyx Endpoint | Shellsmith Function |
124124
|--------|--------------------------------------------------------------------------|--------------------------------|
125125
| GET | `/submodels/{submodelIdentifier}/submodel-elements` | `get_submodel_elements` |
126-
| POST | `/submodels/{submodelIdentifier}/submodel-elements` | |
126+
| POST | `/submodels/{submodelIdentifier}/submodel-elements` | `post_submodel_element` |
127127
| GET | `/submodels/{submodelIdentifier}/submodel-elements/{idShortPath}` | `get_submodel_element` |
128-
| PUT | `/submodels/{submodelIdentifier}/submodel-elements/{idShortPath}` | |
129-
| POST | `/submodels/{submodelIdentifier}/submodel-elements/{idShortPath}` | |
128+
| PUT | `/submodels/{submodelIdentifier}/submodel-elements/{idShortPath}` | `put_submodel_element` |
129+
| POST | `/submodels/{submodelIdentifier}/submodel-elements/{idShortPath}` | `post_submodel_element` |
130130
| DELETE | `/submodels/{submodelIdentifier}/submodel-elements/{idShortPath}` | `delete_submodel_element` |
131-
| GET | `/submodels/{submodelIdentifier}/submodel-elements/{idShortPath}/$value` | |
131+
| GET | `/submodels/{submodelIdentifier}/submodel-elements/{idShortPath}/$value` | `get_submodel_element_value` |
132132
| PATCH | `/submodels/{submodelIdentifier}/submodel-elements/{idShortPath}/$value` | `patch_submodel_element_value` |
133133

134134
### Upload

src/shellsmith/__init__.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,15 @@
1515
delete_submodel_element,
1616
get_submodel,
1717
get_submodel_element,
18+
get_submodel_element_value,
1819
get_submodel_elements,
20+
get_submodel_metadata,
21+
get_submodel_value,
1922
get_submodels,
2023
patch_submodel_element_value,
24+
patch_submodel_value,
25+
post_submodel,
26+
post_submodel_element,
27+
put_submodel,
28+
put_submodel_element,
2129
)

src/shellsmith/crud/submodels.py

Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,28 @@ def get_submodels(host: str = config.host) -> list[dict]:
3232
return submodels
3333

3434

35+
def post_submodel(submodel: dict, host: str = config.host) -> dict:
36+
"""Creates a new Submodel on the AAS server.
37+
38+
Corresponds to:
39+
POST /submodels
40+
41+
Args:
42+
submodel: A dictionary representing the Submodel to be created.
43+
host: The base URL of the AAS server. Defaults to the configured host.
44+
45+
Returns:
46+
A dictionary representing the created Submodel.
47+
48+
Raises:
49+
HTTPError: If the POST request fails.
50+
"""
51+
url = f"{host}/submodels"
52+
response = requests.post(url, json=submodel)
53+
response.raise_for_status()
54+
return response.json()
55+
56+
3557
def get_submodel(
3658
submodel_id: str, encode: bool = True, host: str = config.host
3759
) -> dict:
@@ -60,6 +82,32 @@ def get_submodel(
6082
return submodel
6183

6284

85+
def put_submodel(
86+
submodel_id: str,
87+
submodel: dict,
88+
encode: bool = True,
89+
host: str = config.host,
90+
) -> None:
91+
"""Updates an existing Submodel by its ID.
92+
93+
Corresponds to:
94+
PUT /submodels/{submodel_id}
95+
96+
Args:
97+
submodel_id: The unique identifier of the Submodel.
98+
submodel: A dictionary representing the updated Submodel content.
99+
encode: Whether to Base64-encode the Submodel ID. Defaults to True.
100+
host: The base URL of the AAS server. Defaults to the configured host.
101+
102+
Raises:
103+
HTTPError: If the PUT request fails.
104+
"""
105+
submodel_id = base64_encoded(submodel_id, encode)
106+
url = f"{host}/submodels/{submodel_id}"
107+
response = requests.put(url, json=submodel)
108+
response.raise_for_status()
109+
110+
63111
def delete_submodel(
64112
submodel_id: str,
65113
encode: bool = True,
@@ -85,6 +133,90 @@ def delete_submodel(
85133
response.raise_for_status()
86134

87135

136+
def get_submodel_value(
137+
submodel_id: str,
138+
encode: bool = True,
139+
host: str = config.host,
140+
) -> dict:
141+
"""Retrieves the raw value of a specific Submodel.
142+
143+
Corresponds to:
144+
GET /submodels/{submodel_id}/$value
145+
146+
Args:
147+
submodel_id: The unique identifier of the Submodel.
148+
encode: Whether to Base64-encode the Submodel ID. Defaults to True.
149+
host: The base URL of the AAS server. Defaults to the configured host.
150+
151+
Returns:
152+
A dictionary representing the Submodel value.
153+
154+
Raises:
155+
HTTPError: If the GET request fails.
156+
"""
157+
submodel_id = base64_encoded(submodel_id, encode)
158+
url = f"{host}/submodels/{submodel_id}/$value"
159+
response = requests.get(url)
160+
response.raise_for_status()
161+
return response.json()
162+
163+
164+
# TODO: Returns "Error: response status is 400"
165+
# See:
166+
def patch_submodel_value(
167+
submodel_id: str,
168+
value: dict,
169+
encode: bool = True,
170+
host: str = config.host,
171+
) -> None:
172+
"""Updates the value of a specific Submodel.
173+
174+
Corresponds to:
175+
PATCH /submodels/{submodel_id}/$value
176+
177+
Args:
178+
submodel_id: The unique identifier of the Submodel.
179+
value: A dictionary representing the updated Submodel value.
180+
encode: Whether to Base64-encode the Submodel ID. Defaults to True.
181+
host: The base URL of the AAS server. Defaults to the configured host.
182+
183+
Raises:
184+
HTTPError: If the PATCH request fails.
185+
"""
186+
submodel_id = base64_encoded(submodel_id, encode)
187+
url = f"{host}/submodels/{submodel_id}/$value"
188+
response = requests.patch(url, json=value)
189+
response.raise_for_status()
190+
191+
192+
def get_submodel_metadata(
193+
submodel_id: str,
194+
encode: bool = True,
195+
host: str = config.host,
196+
) -> dict:
197+
"""Retrieves the metadata of a specific Submodel.
198+
199+
Corresponds to:
200+
GET /submodels/{submodel_id}/$metadata
201+
202+
Args:
203+
submodel_id: The unique identifier of the Submodel.
204+
encode: Whether to Base64-encode the Submodel ID. Defaults to True.
205+
host: The base URL of the AAS server. Defaults to the configured host.
206+
207+
Returns:
208+
A dictionary representing the Submodel metadata.
209+
210+
Raises:
211+
HTTPError: If the GET request fails.
212+
"""
213+
submodel_id = base64_encoded(submodel_id, encode)
214+
url = f"{host}/submodels/{submodel_id}/$metadata"
215+
response = requests.get(url)
216+
response.raise_for_status()
217+
return response.json()
218+
219+
88220
# ─────────────────────────── Submodel elements ───────────────────────────
89221

90222

@@ -119,6 +251,39 @@ def get_submodel_elements(
119251
return elements
120252

121253

254+
def post_submodel_element(
255+
submodel_id: str,
256+
element: dict,
257+
id_short_path: str | None = None,
258+
encode: bool = True,
259+
host: str = config.host,
260+
) -> None:
261+
"""Creates a Submodel element.
262+
263+
If `id_short_path` is given, creates the element at that nested path.
264+
Otherwise, creates the element at the root level.
265+
266+
Corresponds to:
267+
POST /submodels/{submodel_id}/submodel-elements
268+
POST /submodels/{submodel_id}/submodel-elements/{idShortPath}
269+
270+
Args:
271+
submodel_id: The unique identifier of the Submodel.
272+
id_short_path: The idShort path for the new Submodel element.
273+
element: A dictionary representing the Submodel element to create.
274+
encode: Whether to Base64-encode the Submodel ID. Defaults to True.
275+
host: The base URL of the AAS server.
276+
277+
Raises:
278+
HTTPError: If the POST request fails.
279+
"""
280+
submodel_id = base64_encoded(submodel_id, encode)
281+
base = f"{host}/submodels/{submodel_id}/submodel-elements"
282+
url = f"{base}/{quote(id_short_path)}" if id_short_path else base
283+
response = requests.post(url, json=element)
284+
response.raise_for_status()
285+
286+
122287
def get_submodel_element(
123288
submodel_id: str,
124289
id_short_path: str,
@@ -151,6 +316,35 @@ def get_submodel_element(
151316
return element
152317

153318

319+
def put_submodel_element(
320+
submodel_id: str,
321+
id_short_path: str,
322+
element: dict,
323+
encode: bool = True,
324+
host: str = config.host,
325+
) -> None:
326+
"""Updates or creates a Submodel element by full replacement.
327+
328+
Corresponds to:
329+
PUT /submodels/{submodel_id}/submodel-elements/{idShortPath}
330+
331+
Args:
332+
submodel_id: The unique identifier of the Submodel.
333+
id_short_path: The idShort path of the Submodel element.
334+
element: A dictionary representing the new element content.
335+
encode: Whether to Base64-encode the Submodel ID. Defaults to True.
336+
host: The base URL of the AAS server.
337+
338+
Raises:
339+
HTTPError: If the PUT request fails.
340+
"""
341+
submodel_id = base64_encoded(submodel_id, encode)
342+
id_short_path = quote(id_short_path)
343+
url = f"{host}/submodels/{submodel_id}/submodel-elements/{id_short_path}"
344+
response = requests.put(url, json=element)
345+
response.raise_for_status()
346+
347+
154348
def delete_submodel_element(
155349
submodel_id: str,
156350
id_short_path: str,
@@ -179,6 +373,37 @@ def delete_submodel_element(
179373
response.raise_for_status()
180374

181375

376+
def get_submodel_element_value(
377+
submodel_id: str,
378+
id_short_path: str,
379+
encode: bool = True,
380+
host: str = config.host,
381+
) -> dict:
382+
"""Retrieves the raw value of a specific Submodel element.
383+
384+
Corresponds to:
385+
GET /submodels/{submodel_id}/submodel-elements/{idShortPath}/$value
386+
387+
Args:
388+
submodel_id: The unique identifier of the Submodel.
389+
id_short_path: The idShort path of the Submodel element.
390+
encode: Whether to Base64-encode the Submodel ID. Defaults to True.
391+
host: The base URL of the AAS server.
392+
393+
Returns:
394+
A dictionary representing the raw value.
395+
396+
Raises:
397+
HTTPError: If the GET request fails.
398+
"""
399+
submodel_id = base64_encoded(submodel_id, encode)
400+
id_short_path = quote(id_short_path)
401+
url = f"{host}/submodels/{submodel_id}/submodel-elements/{id_short_path}/$value"
402+
response = requests.get(url)
403+
response.raise_for_status()
404+
return response.json()
405+
406+
182407
def patch_submodel_element_value(
183408
submodel_id: str,
184409
id_short_path: str,

src/shellsmith/utils.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"""Utility functions for shellsmith."""
22

33
import base64
4+
import uuid
45

56

67
def base64_encode(text: str | None) -> str | None:
@@ -64,3 +65,8 @@ def base64_encoded(identifier: str, encode: bool) -> str:
6465
The encoded or original string, depending on the flag.
6566
"""
6667
return base64_encode(identifier) if encode else identifier
68+
69+
70+
def generate_uuid() -> str:
71+
"""Generates a Universally Unique Identifier (UUID) string."""
72+
return str(uuid.uuid4())

0 commit comments

Comments
 (0)