# Decoder Middleware
## Overview
The `decoder` middleware processes `SourceChange` events (specifically `Insert` and `Update`) to decode a string value found in a specified property of an `Element`. It supports various common encoding formats and allows configuration for handling errors and specifying the output property.
## Functionality
1. **Identify Target:** For each incoming `Insert` or `Update` change, the middleware looks for the property specified in `target_property`.
2. **Validate Type:** It checks if the value of the `target_property` is a string (`ElementValue::String`). If not, it either skips the element or fails, based on the `on_error` setting.
3. **Strip Quotes (Optional):** If `strip_quotes` is set to `true`, it removes leading and trailing double quotes (`"`) from the string value before decoding.
4. **Decode:** It decodes the (potentially quote-stripped) string using the method specified by `encoding_type`.
5. **Handle Errors:** If decoding fails (e.g., invalid input format), it either skips the element (keeping the original value) or fails the processing entirely, based on the `on_error` setting.
6. **Store Result:** If decoding is successful, the resulting decoded string is stored:
* In the property specified by `output_property`, if provided.
* Otherwise, it overwrites the original `target_property`.
* If `output_property` is specified and already exists, it will be overwritten (a warning will be logged).
7. **Pass Through:** `Delete` and `Future` changes are passed through without modification.
## Configuration Options
The middleware is configured using a JSON object with the following fields:
| `encoding_type` | String | Yes | | The encoding format of the `target_property` value. Must be one of: `base64`, `base64url`, `hex`, `url`, `json_escape`. |
| `target_property` | String | Yes | | The name of the element property containing the encoded string to be decoded. |
| `output_property` | String | No | `null` | Optional. The name of the property where the decoded string should be stored. If omitted or `null`, `target_property` will be overwritten. |
| `strip_quotes` | Boolean | No | `false` | If `true`, removes surrounding double quotes (`"`) from the `target_property` value *before* decoding. |
| `on_error` | String (`"skip"` or `"fail"`) | No | `"fail"`| Defines behavior when an error occurs (missing target, wrong type, decoding failure): `"skip"` logs a warning and skips the element; `"fail"` stops processing and returns an error. |
## Encoding Types
* **`base64`**: Standard Base64 encoding (RFC 4648).
* **`base64url`**: URL-safe Base64 encoding (RFC 4648 ยง5), without padding.
* **`hex`**: Hexadecimal encoding (e.g., `48656c6c6f`).
* **`url`**: Percent-encoding (e.g., `Hello%20World`).
* **`json_escape`**: Decodes JSON string escape sequences (e.g., `\"`, `\\`, `\n`). Assumes the input is the *content* of a JSON string literal.
## Example Configuration
```json
{
"name": "decode_user_data",
"kind": "decoder",
"config": {
"encoding_type": "base64",
"target_property": "raw_user_payload",
"output_property": "user_data",
"strip_quotes": true,
"on_error": "skip"
}
}
```
This configuration will:
- Look for the raw_user_payload property.
- If it's a string, remove surrounding quotes.
- Decode the result using Base64.
- Store the decoded string in the user_data property.
- If any step fails, log a warning and leave the element unchanged.
## Transformation Examples
### Example 1: Basic Base64 Decode (Overwrite)
- Configuration
```json
{
"encoding_type": "base64",
"target_property": "data"
}
```
- Input:
```rust
SourceChange::Insert {
element: Element::Node {
metadata: ,
properties: { "data": ElementValue::String("SGVsbG8gV29ybGQh".into()) }.into(),
},
}
```
Output:
```rust
SourceChange::Insert {
element: Element::Node {
metadata: /* ... */,
properties: { "data": ElementValue::String("Hello World!".into()) }.into(), // "data" is overwritten
},
}
```
### Example 2: Hex Decode with Output Property and Strip Quotes
- Configuration
```json
{
"encoding_type": "hex",
"target_property": "raw",
"output_property": "decoded",
"strip_quotes": true
}
```
- Input:
```rust
SourceChange::Update {
element: Element::Node {
metadata: ,
properties: { "raw": ElementValue::String("\"4865782044617461\"".into()) }.into(),
},
}
```
Output:
```rust
SourceChange::Update {
element: Element::Node {
metadata: /* ... */,
properties: {
"raw": ElementValue::String("\"4865782044617461\"".into()), // Original remains
"decoded": ElementValue::String("Hex Data".into()) // Decoded value added
}.into(),
},
}
```
Explanation: Quotes are stripped from "4865782044617461" -> 4865782044617461, which is then hex-decoded to Hex Data and stored in decoded.
### Example 3: URL Decode Failure with Skip
- Configuration
```json
{
"encoding_type": "url",
"target_property": "url_encoded",
"on_error": "skip"
}
```
- Input:
```rust
SourceChange::Insert {
element: Element::Node {
metadata: ,
properties: { "url_encoded": ElementValue::String("Invalid%URL%Encoding".into()) }.into(),
},
}
```
Output:
```rust
SourceChange::Insert {
element: Element::Node {
metadata: /* ... */,
// Properties remain unchanged due to decoding error and on_error: skip
properties: { "url_encoded": ElementValue::String("Invalid%URL%Encoding".into()) }.into(),
},
}
```
Explanation: The URL decoding fails because %UR and %En are invalid percent-encodings. Since on_error is skip, a warning is logged, and the original SourceChange is passed through unmodified.