Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](http://semver.org).
- Get Event Subscription by ID: Retrieve detailed information about specific event subscriptions
- Delete Event Subscription: Remove event subscriptions programmatically
- Get Callbacks History: Comprehensive webhook callback event history with advanced filtering and sorting capabilities
- Get Callbacks History by ID: webhook callback event history related to subscription ID with advanced filtering and sorting capabilities
- Document Template Operations:
- Template Routing Management: Create, retrieve, and update routing configurations for document templates
- Bulk Invite from Template: Send signing invitations to multiple recipients using templates
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using SignNow.Net.Model.Requests;
using SignNow.Net.Model.Requests.GetFolderQuery;

namespace SignNow.Net.Examples
{
[TestClass]
public class GetCallbacksBySubscriptionIdExample : ExamplesBase
{
/// <summary>
/// Demonstrates how to get webhook callback events for a specific event subscription.
/// This example shows how to retrieve callback history filtered by subscription ID and analyze webhook delivery results.
/// </summary>
/// <see cref="https://docs.signnow.com/docs/signnow/callbacks-info/operations/list-v-2-event-subscription-callbacks-1"/>
[TestMethod]
public async Task GetCallbacksBySubscriptionIdAsync()
{
var subscriptions = await testContext.Events
.GetEventSubscriptionsListAsync()
.ConfigureAwait(false);

if (subscriptions?.Data == null || subscriptions.Data.Count == 0)
{
Console.WriteLine("No event subscriptions found. Please create an event subscription first.");
return;
}

var subscriptionId = subscriptions.Data.First().Id;
Console.WriteLine($"Using subscription ID: {subscriptionId}");

// Example 1: Get all callbacks for a specific subscription with default options
Console.WriteLine("\n=== Example 1: Get all callbacks for specific subscription ===");
var allCallbacks = await testContext.Events
.GetCallbacksBySubscriptionIdAsync(subscriptionId)
.ConfigureAwait(false);

Console.WriteLine($"Total callbacks found for subscription {subscriptionId}: {allCallbacks.Data.Count}");
Console.WriteLine($"Current page: {allCallbacks.Meta.Pagination.CurrentPage}");
Console.WriteLine($"Per page: {allCallbacks.Meta.Pagination.PerPage}");
Console.WriteLine($"Total pages: {allCallbacks.Meta.Pagination.TotalPages}");
Console.WriteLine($"Total items: {allCallbacks.Meta.Pagination.Total}");

// Example 2: Filter successful callbacks for the subscription
Console.WriteLine($"\n=== Example 2: Get successful callbacks for subscription {subscriptionId} ===");
var successfulCallbacks = await testContext.Events
.GetCallbacksBySubscriptionIdAsync(subscriptionId, new GetCallbacksOptions
{
Filters = f => f.Or(
f => f.Date.Between(DateTime.UtcNow.AddDays(-30), DateTime.UtcNow),
f => f.Code.Between(200, 299),
f => f.CallbackUrl.Like("example")
),
Sortings = s => s.Code(SortOrder.Descending),
PerPage = 10
})
.ConfigureAwait(false);

Console.WriteLine($"Successful callbacks: {successfulCallbacks.Data.Count}");
foreach (var callback in successfulCallbacks.Data.Take(3))
{
Console.WriteLine($" ID: {callback.Id}");
Console.WriteLine($" Subscription ID: {callback.EventSubscriptionId}");
Console.WriteLine($" Status Code: {callback.ResponseStatusCode}");
Console.WriteLine($" Event: {callback.EventName}");
Console.WriteLine($" Entity ID: {callback.EntityId}");
Console.WriteLine($" Duration: {callback.Duration:F3}s");
Console.WriteLine($" Start Time: {callback.RequestStartTime}");
Console.WriteLine($" Callback Url: {callback.CallbackUrl}");
Console.WriteLine();
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using SignNow.Net.Model.Requests;
using SignNow.Net.Model.Requests.GetFolderQuery;
using UnitTests;

namespace AcceptanceTests
{
[TestClass]
public class GetCallbacksBySubscriptionIdServiceTest : AuthorizedApiTestBase
{
[TestMethod]
public async Task GetCallbacksBySubscriptionIdAsync_WithFilters_ReturnsFilteredResults()
{
var eventSubscriptions = await SignNowTestContext.Events
.GetEventSubscriptionsListAsync()
.ConfigureAwait(false);

if (eventSubscriptions?.Data == null || eventSubscriptions.Data.Count == 0)
{
Assert.Inconclusive("No event subscriptions available for testing");
return;
}

var subscriptionId = eventSubscriptions.Data[0].Id;

var options = new GetCallbacksOptions
{
Filters = f => f.Code.Between(200, 599),
Sortings = s => s.StartTime(SortOrder.Descending),
PerPage = 15
};

var response = await SignNowTestContext.Events
.GetCallbacksBySubscriptionIdAsync(subscriptionId, options)
.ConfigureAwait(false);

Assert.IsNotNull(response);
Assert.IsNotNull(response.Data);
Assert.IsNotNull(response.Meta);
Assert.AreEqual(15, response.Meta.Pagination.PerPage);

// Verify all callbacks belong to the specified subscription and match filter criteria
foreach (var callback in response.Data)
{
Assert.AreEqual(subscriptionId, callback.EventSubscriptionId);
Assert.IsTrue(callback.ResponseStatusCode >= 200 && callback.ResponseStatusCode <= 299,
$"Callback status code {callback.ResponseStatusCode} should be in range 200-299");
}
}
}
}
100 changes: 99 additions & 1 deletion SignNow.Net.Test/UnitTests/Services/EventSubscriptionServiceTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -386,4 +386,102 @@ public async Task GetCallbacksAsync_WithNullRequestHeaders_ShouldHandleGracefull
Assert.IsNotNull(callback.RequestContent);
Assert.IsNotNull(callback.RequestContent.Content);
}
}}

[TestMethod]
public async Task GetCallbacksBySubscriptionIdAsync_WithValidSubscriptionId_ShouldReturnCallbacks()
{
var subscriptionId = "8a49e32e267e42a18e4a3967669f347e06e3b71e";
var mockResponse = TestUtils.SerializeToJsonFormatted(new
{
data = new[]
{
new
{
id = "callback_123",
application_name = "TestApp",
entity_id = "doc_456",
event_subscription_id = subscriptionId,
event_subscription_active = true,
entity_type = "document",
event_name = "document.complete",
callback_url = "https://example.com/webhook",
request_method = "POST",
duration = 1.5,
request_start_time = 1609459200,
request_end_time = 1609459205,
request_headers = new
{
string_head = "test_value",
int_head = 42,
bool_head = true,
float_head = 3.14f
},
response_content = "OK",
response_status_code = 200,
event_subscription_owner_email = "owner@example.com",
request_content = new
{
meta = new
{
timestamp = 1609459200,
@event = "document.complete",
environment = "https://api.signnow.com/",
initiator_id = "user_789",
callback_url = "https://example.com/webhook",
access_token = "***masked***"
},
content = new
{
document_id = "doc_456",
document_name = "Test Document.pdf",
user_id = "user_789",
initiator_id = "user_789",
initiator_email = "initiator@example.com"
}
}
}
},
meta = new
{
pagination = new
{
total = 1,
count = 1,
per_page = 50,
current_page = 1,
total_pages = 1
}
}
});

var service = new EventSubscriptionService(ApiBaseUrl, new Token(), SignNowClientMock(mockResponse));
var options = new GetCallbacksOptions
{
Page = 1,
PerPage = 50
};

var response = await service.GetCallbacksBySubscriptionIdAsync(subscriptionId, options).ConfigureAwait(false);

Assert.IsNotNull(response);
Assert.IsNotNull(response.Data);
Assert.IsNotNull(response.Meta);
Assert.AreEqual(1, response.Data.Count);

var callback = response.Data[0];
Assert.AreEqual("callback_123", callback.Id);
Assert.AreEqual("TestApp", callback.ApplicationName);
Assert.AreEqual("doc_456", callback.EntityId);
Assert.AreEqual(subscriptionId, callback.EventSubscriptionId);
Assert.IsTrue(callback.EventSubscriptionActive);
Assert.AreEqual(EventSubscriptionEntityType.Document, callback.EntityType);
Assert.AreEqual(EventType.DocumentComplete, callback.EventName);
Assert.AreEqual("https://example.com/webhook", callback.CallbackUrl.ToString());
Assert.AreEqual(200, callback.ResponseStatusCode);

Assert.AreEqual(1, response.Meta.Pagination.Total);
Assert.AreEqual(1, response.Meta.Pagination.Count);
Assert.AreEqual(50, response.Meta.Pagination.PerPage);
}
}
}
11 changes: 11 additions & 0 deletions SignNow.Net/Interfaces/IEventSubscriptionService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -97,5 +97,16 @@ public interface IEventSubscriptionService
/// <param name="cancellationToken">Propagates notification that operations should be canceled.</param>
/// <returns>List of callback events with metadata</returns>
Task<CallbacksResponse> GetCallbacksAsync(GetCallbacksOptions options = default, CancellationToken cancellationToken = default);

/// <summary>
/// Allows users to get the list of webhook events (events history) by the subscription ID.
/// The results can be filtered and sorted. If the sort parameter is not indicated,
/// the results are sorted by the start_time in descending order.
/// </summary>
/// <param name="subscriptionId">ID of the subscription</param>
/// <param name="options">Options for filtering and sorting callbacks</param>
/// <param name="cancellationToken">Propagates notification that operations should be canceled.</param>
/// <returns>List of callback events for the specified subscription with metadata</returns>
Task<CallbacksResponse> GetCallbacksBySubscriptionIdAsync(string subscriptionId, GetCallbacksOptions options = default, CancellationToken cancellationToken = default);
}
}
21 changes: 21 additions & 0 deletions SignNow.Net/Service/EventSubscriptionService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -197,5 +197,26 @@ public async Task<CallbacksResponse> GetCallbacksAsync(GetCallbacksOptions optio
.RequestAsync<CallbacksResponse>(requestOptions, cancellationToken)
.ConfigureAwait(false);
}

/// <inheritdoc />
public async Task<CallbacksResponse> GetCallbacksBySubscriptionIdAsync(string subscriptionId, GetCallbacksOptions options = default, CancellationToken cancellationToken = default)
{
Token.TokenType = TokenType.Bearer;

var query = options?.ToQueryString();
var filters = string.IsNullOrEmpty(query)
? string.Empty
: $"?{query}";

var requestOptions = new GetHttpRequestOptions
{
RequestUrl = new Uri(ApiBaseUrl, $"/v2/event-subscriptions/{subscriptionId.ValidateId()}/callbacks{filters}"),
Token = Token
};

return await SignNowClient
.RequestAsync<CallbacksResponse>(requestOptions, cancellationToken)
.ConfigureAwait(false);
}
}
}
Loading