Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
ed028f9
Merge pull request #121 from contentstack/main
reeshika-h Oct 29, 2025
84b75f6
Enhance retry mechanism with comprehensive configuration options
nadeem-cs Dec 10, 2025
435e9cc
Merge pull request #122 from contentstack/feat/DX-3695
nadeem-cs Dec 10, 2025
669bf50
chore: license update
cs-raj Jan 2, 2026
72feaf8
Refactor error messages in Contentstack Management Core
reeshika-h Jan 7, 2026
d426b96
Refactor DefaultRetryPolicy and improve status code handling in Retry…
reeshika-h Jan 7, 2026
70cd9fa
Update retry condition handling in RetryDelayCalculator to include ad…
reeshika-h Jan 7, 2026
b9257eb
Update copyright year in LICENSE file to 2026
reeshika-h Jan 7, 2026
eb05dd2
Merge pull request #124 from contentstack/fix/licenseUpdate
cs-raj Jan 9, 2026
88d674c
Refactor retry policy implementation to improve exception handling an…
nadeem-cs Jan 19, 2026
e8c035c
Merge pull request #127 from contentstack/fix/failing-unit-test
reeshika-h Jan 19, 2026
92e913a
Merge branch 'development' into fix/DX-3748-improve-error-msg
nadeem-cs Jan 19, 2026
01fcc87
Update project file to enable C# 8.0 language features and nullable r…
nadeem-cs Jan 19, 2026
8acd311
Merge pull request #125 from contentstack/fix/DX-3748-improve-error-msg
harshithad0703 Jan 19, 2026
850b637
Update version to 0.5.1 and enhance error messages in the changelog
reeshika-h Jan 19, 2026
47383af
Update version to 0.6.0 and refactor retry policy implementation for …
reeshika-h Jan 19, 2026
dc10ed5
Merge pull request #128 from contentstack/fix/DX-3748-improve-error-msg
harshithad0703 Jan 19, 2026
327186a
Merge pull request #126 from contentstack/development
harshithad0703 Jan 19, 2026
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
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
# Changelog

## [v0.6.0](https://github.com/contentstack/contentstack-management-dotnet/tree/v0.6.0)
- Enhancement
- Refactor retry policy implementation to improve exception handling and retry logic across various scenarios
- Improved error messages

## [v0.5.0](https://github.com/contentstack/contentstack-management-dotnet/tree/v0.5.0)
- Feat
- **Variant Group Management**: Added comprehensive support for variant group operations
Expand Down
2 changes: 1 addition & 1 deletion Contentstack.Management.ASPNETCore/LICENSE.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright © 2012-2025 Contentstack. All Rights Reserved
Copyright © 2012-2026 Contentstack. All Rights Reserved

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<PackageId>contentstack.management.aspnetcore</PackageId>
<PackageVersion>$(Version)</PackageVersion>
<Authors>Contentstack</Authors>
<Copyright>Copyright © 2012-2025 Contentstack. All Rights Reserved</Copyright>
<Copyright>Copyright © 2012-2026 Contentstack. All Rights Reserved</Copyright>
<Owners>Contentstack </Owners>
<PackageProjectUrl>https://github.com/contentstack/contentstack-management-dotnet</PackageProjectUrl>
<PackageReleaseNotes>Initial Release</PackageReleaseNotes>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ public class CustomJsonConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
throw new NotImplementedException();
return false; // Mock converter - not actually used for conversion
}

public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
Expand All @@ -28,7 +28,7 @@ public class CustomConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
throw new NotImplementedException();
return false; // Mock converter - not actually used for conversion
}

public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using Contentstack.Management.Core.Http;
using Contentstack.Management.Core.Internal;
using Contentstack.Management.Core.Runtime.Contexts;
using Contentstack.Management.Core.Runtime.Pipeline;
using Newtonsoft.Json;

namespace Contentstack.Management.Core.Unit.Tests.Mokes
{
/// <summary>
/// Mock HTTP handler that can simulate failures and successes for retry testing.
/// </summary>
public class MockHttpHandlerWithRetries : IPipelineHandler
{
private readonly Queue<Func<IExecutionContext, IResponse>> _responseQueue;
private readonly Queue<Exception> _exceptionQueue;
private int _callCount = 0;

public ILogManager LogManager { get; set; }
public IPipelineHandler InnerHandler { get; set; }
public int CallCount => _callCount;

public MockHttpHandlerWithRetries()
{
_responseQueue = new Queue<Func<IExecutionContext, IResponse>>();
_exceptionQueue = new Queue<Exception>();
}

/// <summary>
/// Adds a response that will be returned on the next call.
/// </summary>
public void AddResponse(HttpStatusCode statusCode, string body = null)
{
_responseQueue.Enqueue((context) =>
{
var response = new HttpResponseMessage(statusCode);
if (body != null)
{
response.Content = new StringContent(body);
}
return new ContentstackResponse(response, JsonSerializer.Create(new JsonSerializerSettings()));
});
}

/// <summary>
/// Adds a successful response (200 OK).
/// </summary>
public void AddSuccessResponse(string body = "{\"success\": true}")
{
AddResponse(HttpStatusCode.OK, body);
}

/// <summary>
/// Adds an exception that will be thrown on the next call.
/// </summary>
public void AddException(Exception exception)
{
_exceptionQueue.Enqueue(exception);
}

/// <summary>
/// Adds multiple failures followed by a success.
/// </summary>
public void AddFailuresThenSuccess(int failureCount, Exception failureException, string successBody = "{\"success\": true}")
{
for (int i = 0; i < failureCount; i++)
{
AddException(failureException);
}
AddSuccessResponse(successBody);
}

/// <summary>
/// Adds multiple HTTP error responses followed by a success.
/// </summary>
public void AddHttpErrorsThenSuccess(int errorCount, HttpStatusCode errorStatusCode, string successBody = "{\"success\": true}")
{
for (int i = 0; i < errorCount; i++)
{
AddResponse(errorStatusCode);
}
AddSuccessResponse(successBody);
}

public async System.Threading.Tasks.Task<T> InvokeAsync<T>(
IExecutionContext executionContext,
bool addAcceptMediaHeader = false,
string apiVersion = null)
{
_callCount++;

// Check for exceptions first
if (_exceptionQueue.Count > 0)
{
var exception = _exceptionQueue.Dequeue();
throw exception;
}

// Check for responses
if (_responseQueue.Count > 0)
{
var responseFactory = _responseQueue.Dequeue();
var response = responseFactory(executionContext);
executionContext.ResponseContext.httpResponse = response;
return await System.Threading.Tasks.Task.FromResult<T>((T)response);
}

// Default: return success
var defaultResponse = new HttpResponseMessage(HttpStatusCode.OK);
defaultResponse.Content = new StringContent("{\"success\": true}");
var contentstackResponse = new ContentstackResponse(defaultResponse, JsonSerializer.Create(new JsonSerializerSettings()));
executionContext.ResponseContext.httpResponse = contentstackResponse;
return await System.Threading.Tasks.Task.FromResult<T>((T)(IResponse)contentstackResponse);
}

public void InvokeSync(
IExecutionContext executionContext,
bool addAcceptMediaHeader = false,
string apiVersion = null)
{
_callCount++;

// Check for exceptions first
if (_exceptionQueue.Count > 0)
{
var exception = _exceptionQueue.Dequeue();
throw exception;
}

// Check for responses
if (_responseQueue.Count > 0)
{
var responseFactory = _responseQueue.Dequeue();
var response = responseFactory(executionContext);
executionContext.ResponseContext.httpResponse = response;
return;
}

// Default: return success
var defaultResponse = new HttpResponseMessage(HttpStatusCode.OK);
defaultResponse.Content = new StringContent("{\"success\": true}");
var contentstackResponse = new ContentstackResponse(defaultResponse, JsonSerializer.Create(new JsonSerializerSettings()));
executionContext.ResponseContext.httpResponse = contentstackResponse;
}
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
using System;
using System.Net;
using System.Net.Http;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;
using Contentstack.Management.Core.Exceptions;

namespace Contentstack.Management.Core.Unit.Tests.Mokes
{
/// <summary>
/// Utility to generate various network error exceptions for testing.
/// </summary>
public static class MockNetworkErrorGenerator
{
public static SocketException CreateSocketException(SocketError errorCode)
{
return new SocketException((int)errorCode);
}

public static HttpRequestException CreateHttpRequestExceptionWithSocketException(SocketError socketError)
{
var socketException = CreateSocketException(socketError);
return new HttpRequestException("Network error", socketException);
}

public static TaskCanceledException CreateTaskCanceledExceptionTimeout()
{
var cts = new CancellationTokenSource();
return new TaskCanceledException("Operation timed out", null, cts.Token);
}

public static TaskCanceledException CreateTaskCanceledExceptionUserCancellation()
{
var cts = new CancellationTokenSource();
cts.Cancel();
return new TaskCanceledException("User cancelled", null, cts.Token);
}

public static TimeoutException CreateTimeoutException()
{
return new TimeoutException("Operation timed out");
}

public static ContentstackErrorException CreateContentstackErrorException(HttpStatusCode statusCode)
{
return new ContentstackErrorException
{
StatusCode = statusCode,
Message = $"HTTP {statusCode} error"
};
}
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
using System;
using Contentstack.Management.Core.Exceptions;
using Contentstack.Management.Core.Runtime.Contexts;
using Contentstack.Management.Core.Runtime.Pipeline.RetryHandler;

namespace Contentstack.Management.Core.Unit.Tests.Mokes
{
/// <summary>
/// Mock retry policy for testing RetryHandler in isolation.
/// </summary>
public class MockRetryPolicy : RetryPolicy
{
public bool ShouldRetryValue { get; set; } = true;
public bool CanRetryValue { get; set; } = true;
public bool RetryLimitExceededValue { get; set; } = false;
public TimeSpan WaitDelay { get; set; } = TimeSpan.FromMilliseconds(100);
public Exception LastException { get; private set; }
public int RetryCallCount { get; private set; }

public MockRetryPolicy()
{
RetryOnError = true;
RetryLimit = 5;
}

public override bool RetryForException(IExecutionContext executionContext, Exception exception)
{
LastException = exception;
RetryCallCount++;
return ShouldRetryValue;
}

public override bool CanRetry(IExecutionContext executionContext)
{
return CanRetryValue;
}

public override bool RetryLimitExceeded(IExecutionContext executionContext)
{
return RetryLimitExceededValue;
}

internal override void WaitBeforeRetry(IExecutionContext executionContext)
{
System.Threading.Tasks.Task.Delay(WaitDelay).Wait();
}
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
HTTP/1.1 429 Too Many Requests
content-type: application/json
content-length: 45
retry-after: 5
date: Wed, 28 Apr 2021 11:11:34 GMT
connection: Keep-Alive

{"error_message": "Too many requests","error_code": 429}

Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
HTTP/1.1 500 Internal Server Error
content-type: application/json
content-length: 45
date: Wed, 28 Apr 2021 11:11:34 GMT
connection: Keep-Alive

{"error_message": "Internal server error","error_code": 500}

Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
HTTP/1.1 502 Bad Gateway
content-type: application/json
content-length: 40
date: Wed, 28 Apr 2021 11:11:34 GMT
connection: Keep-Alive

{"error_message": "Bad gateway","error_code": 502}

Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
HTTP/1.1 503 Service Unavailable
content-type: application/json
content-length: 48
date: Wed, 28 Apr 2021 11:11:34 GMT
connection: Keep-Alive

{"error_message": "Service unavailable","error_code": 503}

Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
HTTP/1.1 504 Gateway Timeout
content-type: application/json
content-length: 45
date: Wed, 28 Apr 2021 11:11:34 GMT
connection: Keep-Alive

{"error_message": "Gateway timeout","error_code": 504}

Loading
Loading