Repository: svix/svix-webhooks (language: rust)

Issue:
Rust client does not pass supported query param format for arrays
## Bug Report

Chatted with @tasn and it looks like the Rust client library is passing in array query params as comma separate (e.g. `?event_types=a,b,c`) which Svix doesn't support.

### Version

0.85.1

### Platform

Linux: 5.19.0-41-generic

### Description

Code reproduction:
```
use svix::api::{MessageListOptions, Svix};

// Create an async main function
#[tokio::main]
async fn main() -> Result<(), anyhow::Error> {
    let app_uid = "somei-id";

    // Create a new Svix client
    let svix = Svix::new("REDACTED".to_string(), None);

    let event_types = vec![
        "card_transaction.updated".to_string(),
        "dispute.updated".to_string(),
    ];

    println!("event_types: {:?}", event_types);

    let options = MessageListOptions {
        event_types: Some(event_types),
       ..Default::default()
    };

    let result = svix
        .message()
        .list(app_uid.to_string(), Some(options))
        .await?;

    println!("got value from the server; result={:?}", result);

    Ok(())
}
```

I get the error:
```
Error: Varidation error Some(HttpValidationError { detail: [ValidationError { loc: ["query", "__all__"], msg: "String must match the following pattern: [a-zA-Z0-9\\-_.].", _type: "value_error" }] })
```


Discussion / hints:
Just to add more context: the problem is with the OpenAPI generator. We can try just updating the generator maybe it's fixed, or alternatively we need to override the template to do it correctly.
The current implementation is really quite silly, it doesn't even escape `,`, it just concatenates the array members with commas in between.

Fix patch:
diff --git a/server/svix-server/src/v1/utils/mod.rs b/server/svix-server/src/v1/utils/mod.rs
index 354b3e9a5..cf0dbc6e9 100644
--- a/server/svix-server/src/v1/utils/mod.rs
+++ b/server/svix-server/src/v1/utils/mod.rs
@@ -563,13 +563,14 @@ where
         let pairs = form_urlencoded::parse(parts.uri.query().unwrap_or_default().as_bytes());
 
         let event_types: HashSet<EventTypeName> = pairs
-            .filter_map(|(key, value)| {
+            .filter(|(key, _)|
                 // want to handle both `?event_types=`, `?event_types[]=`, and `?event_types[1]=`
-                if key == "event_types" || (key.starts_with("event_types[") && key.ends_with(']')) {
-                    Some(EventTypeName(value.into_owned()))
-                } else {
-                    None
-                }
+                key == "event_types" || (key.starts_with("event_types[") && key.ends_with(']')))
+            .flat_map(|(_, value)| {
+                value
+                    .split(',')
+                    .map(|x| EventTypeName(x.to_owned()))
+                    .collect::<Vec<_>>()
             })
             .collect();
 


Test patch:
diff --git a/server/svix-server/tests/e2e_attempt.rs b/server/svix-server/tests/e2e_attempt.rs
index 5d9f82840..55be43c16 100644
--- a/server/svix-server/tests/e2e_attempt.rs
+++ b/server/svix-server/tests/e2e_attempt.rs
@@ -295,14 +295,23 @@ async fn test_list_attempts_by_endpoint() {
         .unwrap();
     assert_eq!(regular_attempts.data.len(), 2);
 
-    let all_attempts: ListResponse<MessageAttemptOut> = client
+    let all_attempts_1: ListResponse<MessageAttemptOut> = client
     .get(
         &format!("api/v1/app/{app_id}/attempt/endpoint/{endp_id_2}/?event_types[0]=event.type&event_types[1]=user.exploded"),
         StatusCode::OK,
     )
     .await
     .unwrap();
-    assert_eq!(all_attempts.data.len(), 3);
+    assert_eq!(all_attempts_1.data.len(), 3);
+
+    let all_attempts_2: ListResponse<MessageAttemptOut> = client
+    .get(
+        &format!("api/v1/app/{app_id}/attempt/endpoint/{endp_id_2}/?event_types=event.type,user.exploded"),
+        StatusCode::OK,
+    )
+    .await
+    .unwrap();
+    assert_eq!(all_attempts_2.data.len(), 3);
 
     receiver_1.jh.abort();
     receiver_2.jh.abort();