Skip to content

Commit b179e20

Browse files
mfaferek93Michał Fąferek
andauthored
[#15] feat: implement GET /components endpoint (#18)
Add endpoint to list all discovered components across all areas. Changes: - Add GET /components REST endpoint in rest_server.cpp - Implement handle_list_components() to return component metadata - Add test_03_list_components integration test - Update README with API reference and endpoint documentation - Update Postman collection with new endpoint Co-authored-by: Michał Fąferek <michal.faferek@42dot.ai>
1 parent 3ef1591 commit b179e20

6 files changed

Lines changed: 146 additions & 3 deletions

File tree

postman/README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ This folder contains Postman collections for testing the ROS 2 Medkit Gateway RE
99
Includes below endpoints:
1010
- ✅ GET `/` - Gateway info
1111
- ✅ GET `/areas` - List all areas
12+
- ✅ GET `/components` - List all components
1213

1314
## Quick Start
1415

@@ -49,6 +50,10 @@ ros2 launch ros2_medkit_gateway demo_nodes.launch.py
4950
6. Click **Send**
5051
7. You should see areas: `[{"id": "powertrain", ...}, {"id": "chassis", ...}, ...]`
5152

53+
8. Click **"GET List Components"**
54+
9. Click **Send**
55+
10. You should see components: `[{"id": "temp_sensor", "namespace": "/powertrain/engine", ...}, ...]`
56+
5257
## API Variables
5358

5459
The environment includes:

postman/collections/ros2-medkit-gateway.postman_collection.json

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"info": {
33
"name": "ROS 2 Medkit Gateway API",
4-
"description": "Minimal collection: GET / and GET /areas endpoints only",
4+
"description": "Collection for ROS 2 Medkit Gateway REST API endpoints",
55
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
66
},
77
"item": [
@@ -43,6 +43,24 @@
4343
"description": "List all discovered areas (powertrain, chassis, body, root)"
4444
},
4545
"response": []
46+
},
47+
{
48+
"name": "GET List Components",
49+
"request": {
50+
"method": "GET",
51+
"header": [],
52+
"url": {
53+
"raw": "{{base_url}}/components",
54+
"host": [
55+
"{{base_url}}"
56+
],
57+
"path": [
58+
"components"
59+
]
60+
},
61+
"description": "List all discovered components across all areas. Returns component metadata including id, namespace, fqn, type, and parent area."
62+
},
63+
"response": []
4664
}
4765
]
4866
}

src/ros2_medkit_gateway/README.md

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,75 @@ The ROS 2 Medkit Gateway exposes ROS 2 system information and data through a RES
1414

1515
## Endpoints
1616

17-
Current available endpoints:
17+
### Discovery Endpoints
1818

1919
- `GET /health` - Health check endpoint (returns healthy status)
2020
- `GET /` - Gateway status and version information
2121
- `GET /areas` - List all discovered areas (powertrain, chassis, body, root)
22+
- `GET /components` - List all discovered components across all areas
23+
24+
### API Reference
25+
26+
#### GET /areas
27+
28+
Lists all discovered areas in the system.
29+
30+
**Example:**
31+
```bash
32+
curl http://localhost:8080/areas
33+
```
34+
35+
**Response:**
36+
```json
37+
[
38+
{
39+
"id": "powertrain",
40+
"namespace": "/powertrain",
41+
"type": "Area"
42+
},
43+
{
44+
"id": "chassis",
45+
"namespace": "/chassis",
46+
"type": "Area"
47+
}
48+
]
49+
```
50+
51+
#### GET /components
52+
53+
Lists all discovered components across all areas.
54+
55+
**Example:**
56+
```bash
57+
curl http://localhost:8080/components
58+
```
59+
60+
**Response:**
61+
```json
62+
[
63+
{
64+
"id": "temp_sensor",
65+
"namespace": "/powertrain/engine",
66+
"fqn": "/powertrain/engine/temp_sensor",
67+
"type": "Component",
68+
"area": "powertrain"
69+
},
70+
{
71+
"id": "rpm_sensor",
72+
"namespace": "/powertrain/engine",
73+
"fqn": "/powertrain/engine/rpm_sensor",
74+
"type": "Component",
75+
"area": "powertrain"
76+
}
77+
]
78+
```
79+
80+
**Response Fields:**
81+
- `id` - Component name (node name)
82+
- `namespace` - ROS 2 namespace where the component is running
83+
- `fqn` - Fully qualified name (namespace + node name)
84+
- `type` - Always "Component"
85+
- `area` - Parent area this component belongs to
2286

2387
## Quick Start
2488

src/ros2_medkit_gateway/include/ros2_medkit_gateway/rest_server.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ class RESTServer {
4040
void handle_health(const httplib::Request& req, httplib::Response& res);
4141
void handle_root(const httplib::Request& req, httplib::Response& res);
4242
void handle_list_areas(const httplib::Request& req, httplib::Response& res);
43+
void handle_list_components(const httplib::Request& req, httplib::Response& res);
4344

4445
GatewayNode* node_;
4546
std::string host_;

src/ros2_medkit_gateway/src/rest_server.cpp

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,11 @@ void RESTServer::setup_routes() {
4646
server_->Get("/areas", [this](const httplib::Request& req, httplib::Response& res) {
4747
handle_list_areas(req, res);
4848
});
49+
50+
// Components
51+
server_->Get("/components", [this](const httplib::Request& req, httplib::Response& res) {
52+
handle_list_components(req, res);
53+
});
4954
}
5055

5156
void RESTServer::start() {
@@ -129,4 +134,30 @@ void RESTServer::handle_list_areas(const httplib::Request& req, httplib::Respons
129134
}
130135
}
131136

137+
void RESTServer::handle_list_components(const httplib::Request& req, httplib::Response& res) {
138+
(void)req; // Unused parameter
139+
140+
try {
141+
const auto cache = node_->get_entity_cache();
142+
143+
json components_json = json::array();
144+
for (const auto& component : cache.components) {
145+
components_json.push_back(component.to_json());
146+
}
147+
148+
res.set_content(components_json.dump(2), "application/json");
149+
} catch (const std::exception& e) {
150+
res.status = 500;
151+
res.set_content(
152+
json{{"error", "Internal server error"}}.dump(),
153+
"application/json"
154+
);
155+
RCLCPP_ERROR(
156+
rclcpp::get_logger("rest_server"),
157+
"Error in handle_list_components: %s",
158+
e.what()
159+
);
160+
}
161+
}
162+
132163
} // namespace ros2_medkit_gateway

src/ros2_medkit_gateway/test/test_integration.test.py

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,31 @@ def test_02_list_areas(self):
178178
self.assertIn('root', area_ids)
179179
print(f'✓ Areas test passed: {len(areas)} areas discovered')
180180

181-
def test_21_automotive_areas_discovery(self):
181+
def test_03_list_components(self):
182+
"""Test GET /components returns all discovered components."""
183+
components = self._get_json('/components')
184+
self.assertIsInstance(components, list)
185+
# Should have at least 7 demo nodes + gateway node
186+
self.assertGreaterEqual(len(components), 7)
187+
188+
# Verify response structure - all components should have required fields
189+
for component in components:
190+
self.assertIn('id', component)
191+
self.assertIn('namespace', component)
192+
self.assertIn('fqn', component)
193+
self.assertIn('type', component)
194+
self.assertIn('area', component)
195+
self.assertEqual(component['type'], 'Component')
196+
197+
# Verify some expected component IDs are present
198+
component_ids = [comp['id'] for comp in components]
199+
self.assertIn('temp_sensor', component_ids)
200+
self.assertIn('rpm_sensor', component_ids)
201+
self.assertIn('pressure_sensor', component_ids)
202+
203+
print(f'✓ Components test passed: {len(components)} components discovered')
204+
205+
def test_04_automotive_areas_discovery(self):
182206
"""Test that automotive areas are properly discovered."""
183207
areas = self._get_json('/areas')
184208
area_ids = [area['id'] for area in areas]

0 commit comments

Comments
 (0)