Skip to content
Merged
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 Assets/Plugins/StreamChat/Changelog.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
Unreleased:
Features:

* Add a public StreamApiException constructor (statusCode, code, errorMessage, moreInfo, duration, exceptionFields). StreamApiException is a public, catch-and-branch type (via the StreamApiExceptionExtensions.Is* helpers), but until now it could only be constructed inside the SDK from the internal APIErrorInternalDTO, so integrators could not build one to unit-test their own error handling (e.g. simulating a 403 / code 70 "no access to channels" response). The new constructor maps directly to the type's public properties and keeps APIErrorInternalDTO internal.
* Add IStreamClientConfig.OptimisticMessageInsert (default true). When true (the existing behavior), a message you send is inserted into the local channel state and raised via IStreamChannel.MessageReceived immediately, before the server's message.new echo arrives. Set it to false to skip the optimistic local insert and wait for the server echo instead, so every participant - including the sender - observes messages in the same server-defined order. Useful when consistent cross-client ordering matters more than instant local feedback (e.g. a shared, broadcast-ordered feed).

v5.5.0:
Expand Down
59 changes: 46 additions & 13 deletions Assets/Plugins/StreamChat/Core/Exceptions/StreamApiException.cs
Original file line number Diff line number Diff line change
Expand Up @@ -79,37 +79,70 @@ public class StreamApiException : Exception
public IReadOnlyDictionary<string, string> ExceptionFields => _exceptionFields;

internal StreamApiException(APIErrorInternalDTO apiError)
: base(
$"{apiError.Message}, Error Code: {apiError.Code}, Http Status Code: {apiError.StatusCode}, More info: {apiError.MoreInfo}, Exception fields: {PrintExceptionFields(apiError)}")
: this(apiError.StatusCode, apiError.Code, apiError.Message, apiError.MoreInfo, apiError.Duration,
apiError.ExceptionFields)
{
StatusCode = apiError.StatusCode;
Code = apiError.Code;
Duration = apiError.Duration;
ErrorMessage = apiError.Message;
MoreInfo = apiError.MoreInfo;
}

if (apiError.ExceptionFields != null && apiError.ExceptionFields.Count > 0)
/// <summary>
/// Construct an exception representing a specific API error. Intended for integrators who need to
/// exercise their own <see cref="StreamApiException"/> handling in unit tests (e.g. simulating a
/// 403 / code 70 "no access to channels" response) without depending on the internal
/// <see cref="APIErrorInternalDTO"/> type. Every argument maps directly to a public property of this type.
/// </summary>
public StreamApiException(
int statusCode,
int code,
string errorMessage = null,
string moreInfo = null,
string duration = null,
IReadOnlyDictionary<string, string> exceptionFields = null)
: this((int?)statusCode, code, errorMessage, moreInfo, duration, exceptionFields)
{
}

private StreamApiException(
int? statusCode,
int? code,
string errorMessage,
string moreInfo,
string duration,
IReadOnlyDictionary<string, string> exceptionFields)
: base(BuildMessage(errorMessage, code, statusCode, moreInfo, exceptionFields))
{
StatusCode = statusCode;
Code = code;
Duration = duration;
ErrorMessage = errorMessage;
MoreInfo = moreInfo;

if (exceptionFields != null && exceptionFields.Count > 0)
{
_exceptionFields = new Dictionary<string, string>(apiError.ExceptionFields);
_exceptionFields = new Dictionary<string, string>(exceptionFields);
}
}

private static readonly StringBuilder _sb = new StringBuilder();

private readonly Dictionary<string, string> _exceptionFields;

private static string PrintExceptionFields(APIErrorInternalDTO apiError)
// Shared by both constructors so the message format stays identical however the exception is built.
private static string BuildMessage(string message, int? code, int? statusCode, string moreInfo,
IReadOnlyDictionary<string, string> exceptionFields)
=> $"{message}, Error Code: {code}, Http Status Code: {statusCode}, More info: {moreInfo}, Exception fields: {PrintExceptionFields(exceptionFields)}";

private static string PrintExceptionFields(IReadOnlyDictionary<string, string> exceptionFields)
{
if (apiError.ExceptionFields == null)
if (exceptionFields == null)
{
return "None";
}

_sb.Length = 0;

var count = apiError.ExceptionFields.Count;
var count = exceptionFields.Count;
var index = 0;
foreach (var keyValuePair in apiError.ExceptionFields)
foreach (var keyValuePair in exceptionFields)
{
_sb.Append(keyValuePair.Key);
_sb.Append(": ");
Expand Down
Loading