Skip to content

Commit 8fa357b

Browse files
authored
Improve dynamic configuration docs (#523)
1 parent 0f17707 commit 8fa357b

File tree

1 file changed

+335
-8
lines changed

1 file changed

+335
-8
lines changed

docs/quix-cloud/managed-services/dynamic-configuration.md

Lines changed: 335 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -132,14 +132,341 @@ This service can leverage a blob storage configured on our platform (see [blob s
132132

133133
The blob storage configuration is automatically injected only when `contentStore` is set to `file`.
134134

135-
## SDK Integration
135+
## API Reference
136136

137-
The **Quix Streams SDK** provides built-in functionality to:
137+
The Dynamic Configuration Manager provides a REST API for managing configurations. The API is available at the service endpoint once deployed.
138138

139-
- Subscribe to configuration change events.
140-
- Download and cache the latest configuration from the API.
141-
- Join configuration values with live data streams at the right time.
139+
### Base URL
140+
```
141+
http://<service-name>:<port>/api/v1
142+
```
143+
144+
### Authentication
145+
All API requests require authentication via the `Authorization` header:
146+
```
147+
Authorization: Bearer <your-token>
148+
```
149+
150+
### Create Configuration
151+
152+
Create a new configuration:
153+
154+
```http
155+
POST /api/v1/configurations
156+
Content-Type: application/json
157+
158+
{
159+
"metadata": {
160+
"type": "device-config",
161+
"target_key": "sensor-001",
162+
"valid_from": "2024-01-01T00:00:00Z",
163+
"category": "sensors"
164+
},
165+
"content": {
166+
"device": {
167+
"name": "Temperature Sensor 001",
168+
"model": "TS-2000",
169+
"location": "Building A, Floor 2"
170+
},
171+
"calibration": {
172+
"offset": 0.5,
173+
"scale": 1.02,
174+
"last_calibrated": "2024-01-01T00:00:00Z"
175+
},
176+
"firmware": {
177+
"version": "2.1.3",
178+
"features": ["temperature", "humidity"]
179+
}
180+
},
181+
"replace": false
182+
}
183+
```
184+
185+
### Request Body
186+
187+
- **metadata.type** (string, required): Configuration type identifier
188+
- **metadata.target_key** (string, required): Target key for configuration matching
189+
- **metadata.valid_from** (ISO8601 datetime, optional): When this configuration becomes valid
190+
- **metadata.category** (string, optional): Category for grouping configurations
191+
- **content** (object, optional): The actual configuration data (JSON object)
192+
- **replace** (boolean, optional): If true, creates a new version if configuration already exists (default: false)
193+
194+
**Note:** The configuration `id` is automatically generated from `type` and `target_key` using SHA-1 hash.
195+
196+
### Update Configuration
197+
198+
Update an existing configuration (creates a new version):
199+
200+
```http
201+
PUT /api/v1/configurations/{id}
202+
Content-Type: application/json
203+
204+
{
205+
"metadata": {
206+
"valid_from": "2024-01-15T00:00:00Z",
207+
"category": "sensors"
208+
},
209+
"content": {
210+
"device": {
211+
"name": "Temperature Sensor 001",
212+
"model": "TS-2000",
213+
"location": "Building A, Floor 3"
214+
},
215+
"calibration": {
216+
"offset": 0.3,
217+
"scale": 1.01,
218+
"last_calibrated": "2024-01-15T10:30:00Z"
219+
}
220+
}
221+
}
222+
```
223+
224+
### Request Body
225+
226+
- **metadata.valid_from** (ISO8601 datetime, optional): Update when this configuration becomes valid
227+
- **metadata.category** (string, optional): Update the category
228+
- **content** (object, optional): Updated configuration data
229+
230+
**Note:** Only fields you provide will be updated. Omitted fields remain unchanged.
231+
232+
### Upload Binary Configuration
233+
234+
For non-JSON configurations (firmware files, calibration data, etc.), use the file upload endpoint:
235+
236+
```http
237+
POST /api/v1/configurations/{id}/versions/{version}/content
238+
Content-Type: multipart/form-data
239+
240+
file: <binary-file-data>
241+
```
242+
243+
**Note:** Binary content must be uploaded separately after creating the configuration metadata. The service automatically detects and stores binary content with appropriate `content_type`.
244+
245+
### Search Configurations
246+
247+
Search for configurations using various criteria:
248+
249+
```http
250+
GET /api/v1/configurations/search?type=device-config&target_key=sensor-001&limit=10&offset=0
251+
```
252+
253+
### Query Parameters
254+
255+
- **type** (string, optional): Filter by configuration type
256+
- **target_key** (string, optional): Filter by target key
257+
- **category** (string, optional): Filter by category
258+
- **limit** (integer, optional): Maximum number of results (default: 20)
259+
- **offset** (integer, optional): Number of results to skip (default: 0)
260+
261+
### Get Configuration
262+
263+
Retrieve a specific configuration:
264+
265+
```http
266+
GET /api/v1/configurations/{id}
267+
```
268+
269+
### Get Configuration Version
270+
271+
Retrieve a specific version of a configuration:
272+
273+
```http
274+
GET /api/v1/configurations/{id}/versions/{version}
275+
```
276+
277+
### Delete Configuration
278+
279+
Delete a configuration (all versions):
280+
281+
```http
282+
DELETE /api/v1/configurations/{id}
283+
```
284+
285+
### Storage Modes
286+
287+
#### MongoDB Mode (Default)
288+
- **Content Type**: JSON only
289+
- **Size Limit**: 16 MB per configuration
290+
- **Use Case**: Structured configuration data
291+
- **Setup**: Configure MongoDB connection parameters
292+
293+
#### File Mode (Blob Storage)
294+
- **Content Type**: Any binary data
295+
- **Size Limit**: Depends on blob storage provider
296+
- **Use Case**: Large files, firmware, binary data
297+
- **Setup**: Configure blob storage credentials
298+
299+
To use file mode, set `contentStore: file` in your deployment configuration.
300+
301+
## Using with Quix Streams join_lookup
302+
303+
The Dynamic Configuration Manager integrates seamlessly with Quix Streams' `join_lookup` feature to enrich streaming data with configuration data in real-time.
304+
305+
### Basic Integration
306+
307+
```python
308+
from quixstreams import Application
309+
from quixstreams.dataframe.joins.lookups import QuixConfigurationService
310+
311+
# Initialize the application
312+
app = Application()
313+
314+
# Create a lookup instance pointing to your configuration topic
315+
lookup = QuixConfigurationService(
316+
topic=app.topic("device-configurations"),
317+
app_config=app.config
318+
)
319+
320+
# Create your main data stream
321+
sdf = app.dataframe(app.topic("sensor-data"))
322+
323+
# Enrich sensor data with device configuration
324+
sdf = sdf.join_lookup(
325+
lookup=lookup,
326+
fields={
327+
"device_name": lookup.json_field(
328+
jsonpath="$.device.name",
329+
type="device-config"
330+
),
331+
"calibration_params": lookup.json_field(
332+
jsonpath="$.calibration",
333+
type="device-config"
334+
),
335+
"firmware_version": lookup.json_field(
336+
jsonpath="$.firmware.version",
337+
type="device-config"
338+
)
339+
},
340+
on="device_id" # The field to match on
341+
)
342+
343+
# Process the enriched data
344+
sdf = sdf.apply(lambda value: {
345+
**value,
346+
"device_info": f"{value['device_name']} (v{value['firmware_version']})"
347+
})
348+
349+
# Output to destination topic
350+
sdf.to_topic(app.topic("enriched-sensor-data"))
351+
352+
if __name__ == "__main__":
353+
app.run()
354+
```
355+
356+
### Advanced Configuration Matching
357+
358+
Use custom key matching logic for complex scenarios:
359+
360+
```python
361+
def custom_key_matcher(value, key):
362+
"""Custom logic to determine configuration key"""
363+
device_type = value.get("device_type", "unknown")
364+
location = value.get("location", "default")
365+
return f"{device_type}-{location}"
366+
367+
# Use custom key matching
368+
sdf = sdf.join_lookup(
369+
lookup=lookup,
370+
fields={
371+
"config": lookup.json_field(
372+
jsonpath="$",
373+
type="location-config"
374+
)
375+
},
376+
on=custom_key_matcher
377+
)
378+
```
379+
380+
### Binary Configuration Support
381+
382+
For non-JSON configurations (firmware files, calibration data, etc.):
383+
384+
```python
385+
sdf = sdf.join_lookup(
386+
lookup=lookup,
387+
fields={
388+
"firmware_binary": lookup.bytes_field(
389+
type="firmware"
390+
),
391+
"calibration_data": lookup.bytes_field(
392+
type="calibration"
393+
)
394+
},
395+
on="device_id"
396+
)
397+
```
398+
399+
### How join_lookup Works with Dynamic Configuration
400+
401+
1. **Configuration Events**: When configurations are updated via the API, lightweight Kafka events are published to your configuration topic.
402+
403+
2. **Real-time Processing**: The `join_lookup` feature listens to these events, fetches the latest configuration content, and caches it locally.
404+
405+
3. **Stream Enrichment**: As your main data stream processes records, `join_lookup` automatically enriches each record with the appropriate configuration data based on the matching key and timestamp.
406+
407+
4. **Version Management**: The system automatically handles configuration versioning, ensuring that each record is enriched with the configuration version that was valid at the time the record was created.
408+
409+
5. **Performance Optimization**: Local caching minimizes API calls and reduces latency for high-throughput applications.
410+
411+
### Advanced Use Cases
412+
413+
#### Custom Target Key Matching
414+
415+
For complex matching logic that goes beyond simple field matching:
416+
417+
```python
418+
class CustomLookup(QuixConfigurationService):
419+
def _config_id(self, type: str, key: str) -> str:
420+
"""Override to implement custom ID generation"""
421+
# Custom logic for generating configuration IDs
422+
if type == "device-config":
423+
# Extract device type and location from key
424+
parts = key.split("-")
425+
device_type = parts[0]
426+
location = parts[1] if len(parts) > 1 else "default"
427+
return f"{device_type}-{location}"
428+
return super()._config_id(type, key)
429+
430+
# Use custom lookup
431+
custom_lookup = CustomLookup(
432+
topic=app.topic("device-configurations"),
433+
app_config=app.config
434+
)
435+
```
436+
437+
#### Multi-Type Configuration Enrichment
438+
439+
Enrich with multiple configuration types:
440+
441+
```python
442+
sdf = sdf.join_lookup(
443+
lookup=lookup,
444+
fields={
445+
# Device configuration
446+
"device_info": lookup.json_field(
447+
jsonpath="$.device",
448+
type="device-config"
449+
),
450+
# Location configuration
451+
"location_info": lookup.json_field(
452+
jsonpath="$.location",
453+
type="location-config"
454+
),
455+
# Calibration data
456+
"calibration": lookup.json_field(
457+
jsonpath="$",
458+
type="calibration-config"
459+
)
460+
},
461+
on="device_id"
462+
)
463+
```
464+
465+
### Benefits
142466

143-
👉 See [SDK
144-
documentation](../../quix-streams/api-reference/dataframe.html#streamingdataframejoin_lookup)
145-
for details on `join_lookup` and related features.
467+
- **Real-time Updates**: Configuration changes are immediately available to your streaming applications
468+
- **Large File Support**: Handle configuration files too large for direct Kafka streaming
469+
- **Version Control**: Automatic versioning ensures data consistency
470+
- **Performance**: Local caching minimizes API calls and latency
471+
- **Flexibility**: Support for both JSON and binary configuration content
472+
- **Scalability**: Efficient handling of high-throughput data streams

0 commit comments

Comments
 (0)