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

Issue:
Replay missing in Rust library
It seems like the Rust library is missing the ability to replay missing messages.


Discussion / hints:
Oh, you're right. Replay missing in a newer addition, I think it's not exposed in any of the libs.

We'll get it added, thanks for letting us know!

Fix patch:
diff --git a/csharp/Svix/Endpoint.cs b/csharp/Svix/Endpoint.cs
index beb9cd62a..c6d4f39dc 100644
--- a/csharp/Svix/Endpoint.cs
+++ b/csharp/Svix/Endpoint.cs
@@ -584,5 +584,54 @@ public async Task<EndpointStats> GetStatsAsync(string appId, string endpointId,
                 return null;
             }
         }
+
+        public bool Replay(string appId, string endpointId, ReplayIn replayIn,
+            string idempotencyKey = default)
+        {
+            try
+            {
+                var response = _endpointApi.ReplayMissingWebhooksApiV1AppAppIdEndpointEndpointIdReplayMissingPostWithHttpInfo(
+                    appId,
+                    endpointId,
+                    replayIn,
+                    idempotencyKey);
+
+                return response.StatusCode == HttpStatusCode.Accepted;
+            }
+            catch (ApiException e)
+            {
+                Logger?.LogError(e, $"{nameof(Replay)} failed");
+
+                if (Throw)
+                    throw;
+
+                return false;
+            }
+        }
+
+        public async Task<bool> ReplayAsync(string appId, string endpointId, ReplayIn replayIn,
+            string idempotencyKey = default, CancellationToken cancellationToken = default)
+        {
+            try
+            {
+                var response = await _endpointApi.ReplayMissingWebhooksApiV1AppAppIdEndpointEndpointIdReplayMissingPostWithHttpInfoAsync(
+                    appId,
+                    endpointId,
+                    replayIn,
+                    idempotencyKey,
+                    cancellationToken);
+
+                return response.StatusCode == HttpStatusCode.Accepted;
+            }
+            catch (ApiException e)
+            {
+                Logger?.LogError(e, $"{nameof(ReplayAsync)} failed");
+
+                if (Throw)
+                    throw;
+
+                return false;
+            }
+        }
     }
 }
diff --git a/go/endpoint.go b/go/endpoint.go
index fe066bb87..5520c02ee 100644
--- a/go/endpoint.go
+++ b/go/endpoint.go
@@ -14,10 +14,11 @@ type (
 	EndpointSecretOut       openapi.EndpointSecretOut
 	EndpointSecretRotateIn  openapi.EndpointSecretRotateIn
 	RecoverIn               openapi.RecoverIn
+	ReplayIn                openapi.ReplayIn
 	EndpointHeadersIn       openapi.EndpointHeadersIn
 	EndpointHeadersPatchIn  openapi.EndpointHeadersPatchIn
 	EndpointHeadersOut      openapi.EndpointHeadersOut
-	EndpointStats			openapi.EndpointStats
+	EndpointStats           openapi.EndpointStats
 )
 
 type Endpoint struct {
@@ -181,3 +182,27 @@ func (e *Endpoint) GetStats(appId string, endpointId string) (*EndpointStats, er
 	ret := EndpointStats(out)
 	return &ret, nil
 }
+
+func (e *Endpoint) Replay(appId string, endpointId string, replayIn *ReplayIn) error {
+	return e.ReplayWithOptions(appId, endpointId, replayIn, nil)
+}
+
+func (e *Endpoint) ReplayWithOptions(
+	appId string,
+	endpointId string,
+	replayIn *ReplayIn,
+	options *PostOptions,
+) error {
+	req := e.api.EndpointApi.ReplayMissingWebhooksApiV1AppAppIdEndpointEndpointIdReplayMissingPost(context.Background(), appId, endpointId)
+	req.ReplayIn(openapi.ReplayIn(*replayIn))
+	if options != nil {
+		if options.IdempotencyKey != nil {
+			req = req.IdempotencyKey(*options.IdempotencyKey)
+		}
+	}
+	_, res, err := req.Execute()
+	if err != nil {
+		return wrapError(err, res)
+	}
+	return nil
+}
diff --git a/java/lib/src/main/java/com/svix/Endpoint.java b/java/lib/src/main/java/com/svix/Endpoint.java
index 6d38a236e..c892c6752 100644
--- a/java/lib/src/main/java/com/svix/Endpoint.java
+++ b/java/lib/src/main/java/com/svix/Endpoint.java
@@ -12,6 +12,7 @@
 import com.svix.models.EndpointSecretRotateIn;
 import com.svix.models.ListResponseEndpointOut;
 import com.svix.models.RecoverIn;
+import com.svix.models.ReplayIn;
 import com.svix.models.EndpointStats;
 
 public final class Endpoint {
@@ -128,4 +129,16 @@ public EndpointStats getStats(final String appId, final String endpointId) throw
 			throw Utils.wrapInternalApiException(e);
 		}
 	}
+
+	public void replay(final String appId, final String endpointId, final ReplayIn replayIn) throws ApiException {
+		this.replay(appId, endpointId, replayIn, new PostOptions());
+	}
+
+	public void replay(final String appId, final String endpointId, final ReplayIn replayIn, final PostOptions options) throws ApiException {
+		try {
+			api.replayMissingWebhooksApiV1AppAppIdEndpointEndpointIdReplayMissingPostWithHttpInfo(appId, endpointId, replayIn, options.getIdempotencyKey());
+		} catch (com.svix.internal.ApiException e) {
+			throw Utils.wrapInternalApiException(e);
+		}
+	}
 }
diff --git a/kotlin/lib/src/main/kotlin/Endpoint.kt b/kotlin/lib/src/main/kotlin/Endpoint.kt
index 73e01d335..067298010 100644
--- a/kotlin/lib/src/main/kotlin/Endpoint.kt
+++ b/kotlin/lib/src/main/kotlin/Endpoint.kt
@@ -13,6 +13,7 @@ import com.svix.kotlin.models.EndpointStats
 import com.svix.kotlin.models.EndpointUpdate
 import com.svix.kotlin.models.ListResponseEndpointOut
 import com.svix.kotlin.models.RecoverIn
+import com.svix.kotlin.models.ReplayIn
 
 class Endpoint internal constructor(token: String, options: SvixOptions) {
     val api = EndpointApi(options.serverUrl)
@@ -194,4 +195,22 @@ class Endpoint internal constructor(token: String, options: SvixOptions) {
             throw ApiException.wrap(e)
         }
     }
+
+    suspend fun replay(
+        appId: String,
+        endpointId: String,
+        replayIn: ReplayIn,
+        options: PostOptions = PostOptions()
+    ) {
+        try {
+            api.replayMissingWebhooksApiV1AppAppIdEndpointEndpointIdReplayMissingPost(
+                appId,
+                endpointId,
+                replayIn,
+                options.idempotencyKey
+            )
+        } catch (e: Exception) {
+            throw ApiException.wrap(e)
+        }
+    }
 }
diff --git a/python/svix/api.py b/python/svix/api.py
index 8979b2f80..da498ce07 100644
--- a/python/svix/api.py
+++ b/python/svix/api.py
@@ -26,6 +26,7 @@
     list_endpoints_api_v1_app_app_id_endpoint_get,
     patch_endpoint_headers_api_v1_app_app_id_endpoint_endpoint_id_headers_patch,
     recover_failed_webhooks_api_v1_app_app_id_endpoint_endpoint_id_recover_post,
+    replay_missing_webhooks_api_v1_app_app_id_endpoint_endpoint_id_replay_missing_post,
     rotate_endpoint_secret_api_v1_app_app_id_endpoint_endpoint_id_secret_rotate_post,
     update_endpoint_api_v1_app_app_id_endpoint_endpoint_id_put,
     update_endpoint_headers_api_v1_app_app_id_endpoint_endpoint_id_headers_put,
@@ -102,6 +103,8 @@
 from .internal.openapi_client.models.message_status import MessageStatus
 from .internal.openapi_client.models.recover_in import RecoverIn
 from .internal.openapi_client.models.recover_out import RecoverOut
+from .internal.openapi_client.models.replay_in import ReplayIn
+from .internal.openapi_client.models.replay_out import ReplayOut
 from .internal.openapi_client.models.status_code_class import StatusCodeClass
 
 DEFAULT_SERVER_URL = "https://api.svix.com"
@@ -356,6 +359,13 @@ async def patch_headers(self, app_id: str, endpoint_id: str, endpoint_headers_in
             json_body=endpoint_headers_in,
         )
 
+    async def replay(
+        self, app_id: str, endpoint_id: str, replay_in: ReplayIn, options: PostOptions = PostOptions()
+    ) -> ReplayOut:
+        return await replay_missing_webhooks_api_v1_app_app_id_endpoint_endpoint_id_replay_missing_post.asyncio(
+            client=self._client, app_id=app_id, endpoint_id=endpoint_id, json_body=replay_in, **options.to_dict()
+        )
+
 
 class Endpoint(ApiBase):
     def list(self, app_id: str, options: EndpointListOptions = EndpointListOptions()) -> ListResponseEndpointOut:
@@ -456,6 +466,13 @@ def get_stats(self, app_id: str, endpoint_id: str) -> EndpointStats:
             endpoint_id=endpoint_id,
         )
 
+    def replay(
+        self, app_id: str, endpoint_id: str, replay_in: ReplayIn, options: PostOptions = PostOptions()
+    ) -> ReplayOut:
+        return replay_missing_webhooks_api_v1_app_app_id_endpoint_endpoint_id_replay_missing_post.sync(
+            client=self._client, app_id=app_id, endpoint_id=endpoint_id, json_body=replay_in, **options.to_dict()
+        )
+
 
 class EventTypeAsync(ApiBase):
     async def list(self, options: EventTypeListOptions = EventTypeListOptions()) -> ListResponseEventTypeOut:
diff --git a/ruby/lib/svix/endpoint_api.rb b/ruby/lib/svix/endpoint_api.rb
index 305d94173..43b44a02d 100644
--- a/ruby/lib/svix/endpoint_api.rb
+++ b/ruby/lib/svix/endpoint_api.rb
@@ -55,5 +55,10 @@ def get_stats(app_id, endpoint_id)
       return @api.get_endpoint_stats_api_v1_app_app_id_endpoint_endpoint_id_stats_get(endpoint_id app_id)
     end
 
+    def replay(app_id, endpoint_id, replay_in, options = {})
+      @api.replay_missing_webhooks_api_v1_app_app_id_endpoint_endpoint_id_replay_missing_post(app_id, endpoint_id, replay_in, options)
+      nil
+    end
+
   end
 end
diff --git a/rust/src/api.rs b/rust/src/api.rs
index 822268cb0..0e3bc4da5 100644
--- a/rust/src/api.rs
+++ b/rust/src/api.rs
@@ -6,7 +6,7 @@ use crate::apis::{
 use crate::error::Result;
 use crate::models::{
     AppPortalAccessIn, AppPortalAccessOut, IntegrationIn, IntegrationKeyOut, IntegrationOut,
-    IntegrationUpdate, ListResponseIntegrationOut,
+    IntegrationUpdate, ListResponseIntegrationOut, ReplayIn,
 };
 pub use crate::models::{
     ApplicationIn, ApplicationOut, DashboardAccessOut, EndpointHeadersIn, EndpointHeadersOut,
@@ -479,6 +479,27 @@ impl<'a> Endpoint<'a> {
             .await?,
         )
     }
+
+    pub async fn replay_missing(
+        &self,
+        app_id: String,
+        endpoint_id: String,
+        replay_in: ReplayIn,
+        options: Option<PostOptions>,
+    ) -> Result<()> {
+        let PostOptions { idempotency_key } = options.unwrap_or_default();
+        endpoint_api::replay_missing_webhooks_api_v1_app_app_id_endpoint_endpoint_id_replay_missing_post(
+            self.cfg,
+            endpoint_api::ReplayMissingWebhooksApiV1AppAppIdEndpointEndpointIdReplayMissingPostParams{
+                app_id,
+                endpoint_id,
+                replay_in,
+                idempotency_key,
+            },
+        )
+        .await?;
+        Ok(())
+    }
 }
 
 pub type IntegrationListOptions = ListOptions;


Test patch: