Skip to content

Commit

Permalink
OData error with target and details OData#76
Browse files Browse the repository at this point in the history
Add writer and reader tests.
Update public API baseline.
  • Loading branch information
congysu committed Nov 16, 2015
1 parent bfd173f commit e41e22f
Show file tree
Hide file tree
Showing 16 changed files with 651 additions and 68 deletions.
173 changes: 173 additions & 0 deletions src/Microsoft.OData.Core/Json/BufferingJsonReader.cs
Expand Up @@ -530,6 +530,43 @@ private bool TryReadInStreamErrorPropertyValue(out ODataError error)

break;

case JsonConstants.ODataErrorTargetName:
if (!ODataJsonLightReaderUtils.ErrorPropertyNotFound(
ref propertiesFoundBitmask,
ODataJsonLightReaderUtils.ErrorPropertyBitMask.Target))
{
return false;
}

string errorTarget;
if (this.TryReadErrorStringPropertyValue(out errorTarget))
{
error.Target = errorTarget;
}
else
{
return false;
}

break;

case JsonConstants.ODataErrorDetailsName:
if (!ODataJsonLightReaderUtils.ErrorPropertyNotFound(
ref propertiesFoundBitmask,
ODataJsonLightReaderUtils.ErrorPropertyBitMask.Details))
{
return false;
}

ICollection<ODataErrorDetail> details;
if (!this.TryReadErrorDetailsPropertyValue(out details))
{
return false;
}

error.Details = details;
break;

case JsonConstants.ODataErrorInnerErrorName:
if (!ODataJsonLightReaderUtils.ErrorPropertyNotFound(ref propertiesFoundBitmask, ODataJsonLightReaderUtils.ErrorPropertyBitMask.InnerError))
{
Expand Down Expand Up @@ -561,6 +598,142 @@ private bool TryReadInStreamErrorPropertyValue(out ODataError error)
return propertiesFoundBitmask != ODataJsonLightReaderUtils.ErrorPropertyBitMask.None;
}

private bool TryReadErrorDetailsPropertyValue(out ICollection<ODataErrorDetail> details)
{
Debug.Assert(
this.currentBufferedNode.NodeType == JsonNodeType.Property,
"this.currentBufferedNode.NodeType == JsonNodeType.Property");
Debug.Assert(this.parsingInStreamError, "this.parsingInStreamError");
this.AssertBuffering();

// move the reader onto the property value
this.ReadInternal();

// we expect a start-array node here
if (this.currentBufferedNode.NodeType != JsonNodeType.StartArray)
{
details = null;
return false;
}

// [
ReadInternal();

details = new List<ODataErrorDetail>();
ODataErrorDetail detail;
if (TryReadErrorDetail(out detail))
{
details.Add(detail);
}

// ]
ReadInternal();

return true;
}

private bool TryReadErrorDetail(out ODataErrorDetail detail)
{
Debug.Assert(
this.currentBufferedNode.NodeType == JsonNodeType.StartObject,
"this.currentBufferedNode.NodeType == JsonNodeType.StartObject");
Debug.Assert(this.parsingInStreamError, "this.parsingInStreamError");
this.AssertBuffering();

if (this.currentBufferedNode.NodeType != JsonNodeType.StartObject)
{
detail = null;
return false;
}

// {
ReadInternal();

detail = new ODataErrorDetail();
// we expect one of the supported properties for the value (or end-object)
var propertiesFoundBitmask = ODataJsonLightReaderUtils.ErrorPropertyBitMask.None;
while (this.currentBufferedNode.NodeType == JsonNodeType.Property)
{
var propertyName = (string)this.currentBufferedNode.Value;

switch (propertyName)
{
case JsonConstants.ODataErrorCodeName:
if (!ODataJsonLightReaderUtils.ErrorPropertyNotFound(
ref propertiesFoundBitmask,
ODataJsonLightReaderUtils.ErrorPropertyBitMask.Code))
{
return false;
}

string code;
if (this.TryReadErrorStringPropertyValue(out code))
{
detail.ErrorCode = code;
}
else
{
return false;
}

break;

case JsonConstants.ODataErrorTargetName:
if (!ODataJsonLightReaderUtils.ErrorPropertyNotFound(
ref propertiesFoundBitmask,
ODataJsonLightReaderUtils.ErrorPropertyBitMask.Target))
{
return false;
}

string target;
if (this.TryReadErrorStringPropertyValue(out target))
{
detail.Target = target;
}
else
{
return false;
}

break;

case JsonConstants.ODataErrorMessageName:
if (!ODataJsonLightReaderUtils.ErrorPropertyNotFound(
ref propertiesFoundBitmask,
ODataJsonLightReaderUtils.ErrorPropertyBitMask.MessageValue))
{
return false;
}

string message;
if (this.TryReadErrorStringPropertyValue(out message))
{
detail.Message = message;
}
else
{
return false;
}

break;

default:
// if we find a non-supported property in an inner error, we skip it
this.SkipValueInternal();
break;
}

this.ReadInternal();
}

Debug.Assert(
this.currentBufferedNode.NodeType == JsonNodeType.EndObject,
"this.currentBufferedNode.NodeType == JsonNodeType.EndObject");

return true;
}

/// <summary>
/// Try to read an inner error property value.
/// </summary>
Expand Down
10 changes: 10 additions & 0 deletions src/Microsoft.OData.Core/Json/JsonConstants.cs
Expand Up @@ -49,6 +49,16 @@ internal static class JsonConstants
/// </summary>
internal const string ODataErrorMessageName = "message";

/// <summary>
/// "target" header for the error message property
/// </summary>
internal const string ODataErrorTargetName = "target";

/// <summary>
/// "details" header for the inner error property
/// </summary>
internal const string ODataErrorDetailsName = "details";

/// <summary>
/// "innererror" header for the inner error property
/// </summary>
Expand Down
74 changes: 71 additions & 3 deletions src/Microsoft.OData.Core/Json/ODataJsonWriterUtils.cs
Expand Up @@ -28,7 +28,9 @@ internal static class ODataJsonWriterUtils
/// <param name="includeDebugInformation">A flag indicating whether error details should be written (in debug mode only) or not.</param>
/// <param name="maxInnerErrorDepth">The maximum number of nested inner errors to allow.</param>
/// <param name="writingJsonLight">true if we're writing JSON lite, false if we're writing verbose JSON.</param>
internal static void WriteError(IJsonWriter jsonWriter, Action<IEnumerable<ODataInstanceAnnotation>> writeInstanceAnnotationsDelegate, ODataError error, bool includeDebugInformation, int maxInnerErrorDepth, bool writingJsonLight)
internal static void WriteError(IJsonWriter jsonWriter,
Action<IEnumerable<ODataInstanceAnnotation>> writeInstanceAnnotationsDelegate, ODataError error,
bool includeDebugInformation, int maxInnerErrorDepth, bool writingJsonLight)
{
Debug.Assert(jsonWriter != null, "jsonWriter != null");
Debug.Assert(error != null, "error != null");
Expand All @@ -38,7 +40,17 @@ internal static void WriteError(IJsonWriter jsonWriter, Action<IEnumerable<OData

ODataInnerError innerError = includeDebugInformation ? error.InnerError : null;

WriteError(jsonWriter, code, message, innerError, error.GetInstanceAnnotations(), writeInstanceAnnotationsDelegate, maxInnerErrorDepth, writingJsonLight);
WriteError(
jsonWriter,
code,
message,
error.Target,
error.Details,
innerError,
error.GetInstanceAnnotations(),
writeInstanceAnnotationsDelegate,
maxInnerErrorDepth,
writingJsonLight);
}

/// <summary>
Expand Down Expand Up @@ -85,7 +97,12 @@ internal static void EndJsonPaddingIfRequired(IJsonWriter jsonWriter, ODataMessa
/// <param name="writeInstanceAnnotationsDelegate">Action to write the instance annotations.</param>
/// <param name="maxInnerErrorDepth">The maximum number of nested inner errors to allow.</param>
/// <param name="writingJsonLight">true if we're writing JSON lite, false if we're writing verbose JSON.</param>
private static void WriteError(IJsonWriter jsonWriter, string code, string message, ODataInnerError innerError, IEnumerable<ODataInstanceAnnotation> instanceAnnotations, Action<IEnumerable<ODataInstanceAnnotation>> writeInstanceAnnotationsDelegate, int maxInnerErrorDepth, bool writingJsonLight)
private static void WriteError(IJsonWriter jsonWriter, string code, string message, string target,
IEnumerable<ODataErrorDetail> details,
ODataInnerError innerError,
IEnumerable<ODataInstanceAnnotation> instanceAnnotations,
Action<IEnumerable<ODataInstanceAnnotation>> writeInstanceAnnotationsDelegate, int maxInnerErrorDepth,
bool writingJsonLight)
{
Debug.Assert(jsonWriter != null, "jsonWriter != null");
Debug.Assert(code != null, "code != null");
Expand Down Expand Up @@ -113,6 +130,18 @@ private static void WriteError(IJsonWriter jsonWriter, string code, string messa
jsonWriter.WriteName(JsonConstants.ODataErrorMessageName);
jsonWriter.WriteValue(message);

// For example, "target": "query",
if (target != null)
{
jsonWriter.WriteName(JsonConstants.ODataErrorTargetName);
jsonWriter.WriteValue(target);
}

if (details != null)
{
WriteErrorDetails(jsonWriter, details, JsonConstants.ODataErrorDetailsName);
}

if (innerError != null)
{
WriteInnerError(jsonWriter, innerError, JsonConstants.ODataErrorInnerErrorName, /* recursionDepth */ 0, maxInnerErrorDepth);
Expand All @@ -129,6 +158,45 @@ private static void WriteError(IJsonWriter jsonWriter, string code, string messa
jsonWriter.EndObjectScope();
}

private static void WriteErrorDetails(IJsonWriter jsonWriter, IEnumerable<ODataErrorDetail> details,
string odataErrorDetailsName)
{
Debug.Assert(jsonWriter != null, "jsonWriter != null");
Debug.Assert(details != null, "details != null");
Debug.Assert(odataErrorDetailsName != null, "odataErrorDetailsName != null");

// "details": [
jsonWriter.WriteName(odataErrorDetailsName);
jsonWriter.StartArrayScope();

foreach (var detail in details.Where(d => d != null))
{
// {
jsonWriter.StartObjectScope();

// "code": "301",
jsonWriter.WriteName(JsonConstants.ODataErrorCodeName);
jsonWriter.WriteValue(detail.ErrorCode ?? string.Empty);

if (detail.Target != null)
{
// "target": "$search"
jsonWriter.WriteName(JsonConstants.ODataErrorTargetName);
jsonWriter.WriteValue(detail.Target);
}

// "message": "$search query option not supported",
jsonWriter.WriteName(JsonConstants.ODataErrorMessageName);
jsonWriter.WriteValue(detail.Message ?? string.Empty);

// }
jsonWriter.EndObjectScope();
}

// ]
jsonWriter.EndArrayScope();
}

/// <summary>
/// Write an inner error property and message.
/// </summary>
Expand Down

0 comments on commit e41e22f

Please sign in to comment.