Skip to content

Commit

Permalink
support reading relative context urls with
Browse files Browse the repository at this point in the history
  • Loading branch information
ElizabethOkerio committed Feb 27, 2023
1 parent 0294c76 commit ef02ee9
Show file tree
Hide file tree
Showing 6 changed files with 82 additions and 21 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -71,19 +71,51 @@ internal static ODataJsonLightContextUriParseResult Parse(
ODataPayloadKind payloadKind,
Func<IEdmType, string, IEdmType> clientCustomTypeResolver,
bool needParseFragment,
bool throwIfMetadataConflict = true)
bool throwIfMetadataConflict = true,
Uri baseUri = null,
IEdmNavigationSource navigationSource = null)
{
if (contextUriFromPayload == null)
{
throw new ODataException(ODataErrorStrings.ODataJsonLightContextUriParser_NullMetadataDocumentUri);
}

// Create an absolute URI from the payload string
// TODO: Support relative context uri and resolving other relative uris
// Create a context URI from the payload string.
Uri contextUri;
if (!Uri.TryCreate(contextUriFromPayload, UriKind.Absolute, out contextUri))
{
throw new ODataException(ODataErrorStrings.ODataJsonLightContextUriParser_TopLevelContextUrlShouldBeAbsolute(contextUriFromPayload));
if (baseUri != null)
{
ODataUri oDataUri = new ODataUri() { ServiceRoot = baseUri };

// This caters for this format: #$delta.
if (contextUriFromPayload.Equals(ODataConstants.HashDeltaResourceSet, StringComparison.OrdinalIgnoreCase))
{
if (string.IsNullOrEmpty(navigationSource?.Name))
{
throw new ODataException(ODataErrorStrings.ODataJsonLightContextUriParser_InvalidContextUrl(contextUriFromPayload));
}
else
{
contextUriFromPayload = oDataUri.MetadataDocumentUri.ToString() + ODataConstants.ContextUriFragmentIndicator + navigationSource.Name + ODataConstants.UriSegmentSeparator + ODataConstants.DeltaResourceSet;
}
}
else
{
contextUriFromPayload = contextUriFromPayload.StartsWith(ODataConstants.UriMetadataSegmentHash, StringComparison.OrdinalIgnoreCase)
? baseUri + contextUriFromPayload
: oDataUri.MetadataDocumentUri.ToString() + ODataConstants.ContextUriFragmentIndicator + contextUriFromPayload;
}

if (!Uri.TryCreate(baseUri, contextUriFromPayload, out contextUri))
{
throw new ODataException(ODataErrorStrings.ODataJsonLightContextUriParser_InvalidContextUrl(contextUriFromPayload));
}
}
else
{
throw new ODataException(ODataErrorStrings.ODataJsonLightContextUriParser_InvalidContextUrl(contextUriFromPayload));
}
}

ODataJsonLightContextUriParser parser = new ODataJsonLightContextUriParser(model, contextUri);
Expand Down
12 changes: 8 additions & 4 deletions src/Microsoft.OData.Core/JsonLight/ODataJsonLightDeserializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,8 @@ internal void ReadPayloadStart(
ODataPayloadKind payloadKind,
PropertyAndAnnotationCollector propertyAndAnnotationCollector,
bool isReadingNestedPayload,
bool allowEmptyPayload)
bool allowEmptyPayload,
IEdmNavigationSource navigationSource = null)
{
this.JsonReader.AssertNotBuffering();
Debug.Assert(isReadingNestedPayload || this.JsonReader.NodeType == JsonNodeType.None, "Pre-Condition: JSON reader must not have been used yet when not reading a nested payload.");
Expand Down Expand Up @@ -298,7 +299,8 @@ internal void ReadPayloadStart(
payloadKind,
this.MessageReaderSettings.ClientCustomTypeResolver,
this.JsonLightInputContext.ReadingResponse || payloadKind == ODataPayloadKind.Delta,
this.JsonLightInputContext.MessageReaderSettings.ThrowIfTypeConflictsWithMetadata);
this.JsonLightInputContext.MessageReaderSettings.ThrowIfTypeConflictsWithMetadata,
this.BaseUri, navigationSource);
}

this.contextUriParseResult = parseResult;
Expand All @@ -325,7 +327,8 @@ internal async Task ReadPayloadStartAsync(
ODataPayloadKind payloadKind,
PropertyAndAnnotationCollector propertyAndAnnotationCollector,
bool isReadingNestedPayload,
bool allowEmptyPayload)
bool allowEmptyPayload,
IEdmNavigationSource navigationSource = null)
{
this.JsonReader.AssertNotBuffering();
Debug.Assert(
Expand All @@ -349,7 +352,8 @@ internal async Task ReadPayloadStartAsync(
payloadKind,
this.MessageReaderSettings.ClientCustomTypeResolver,
this.JsonLightInputContext.ReadingResponse || payloadKind == ODataPayloadKind.Delta,
this.JsonLightInputContext.MessageReaderSettings.ThrowIfTypeConflictsWithMetadata);
this.JsonLightInputContext.MessageReaderSettings.ThrowIfTypeConflictsWithMetadata,
this.BaseUri, navigationSource);
}

#if DEBUG
Expand Down
19 changes: 15 additions & 4 deletions src/Microsoft.OData.Core/JsonLight/ODataJsonLightReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,8 @@ await this.jsonLightResourceDeserializer.ReadPayloadStartAsync(
payloadKind,
propertyAndAnnotationCollector,
this.IsReadingNestedPayload,
allowEmptyPayload: false).ConfigureAwait(false);
allowEmptyPayload: false,
this.CurrentNavigationSource).ConfigureAwait(false);

ResolveScopeInfoFromContextUrl();

Expand Down Expand Up @@ -1231,7 +1232,10 @@ private ODataUri ResolveODataUriFromContextUrl(ODataNestedResourceInfo nestedInf
UriUtils.UriToString(nestedInfo.ContextUrl),
payloadKind,
this.jsonLightResourceDeserializer.MessageReaderSettings.ClientCustomTypeResolver,
this.jsonLightResourceDeserializer.JsonLightInputContext.ReadingResponse).Path;
this.jsonLightResourceDeserializer.JsonLightInputContext.ReadingResponse,
true,
this.jsonLightResourceDeserializer.MessageReaderSettings.BaseUri,
this.CurrentNavigationSource).Path;

return new ODataUri() { Path = odataPath };
}
Expand Down Expand Up @@ -1931,7 +1935,11 @@ private void ReadResourceSetItemStart(PropertyAndAnnotationCollector propertyAnd
contextUriStr,
this.ReadingDelta ? ODataPayloadKind.Delta : ODataPayloadKind.Resource,
this.jsonLightResourceDeserializer.MessageReaderSettings.ClientCustomTypeResolver,
this.jsonLightInputContext.ReadingResponse || this.ReadingDelta);
this.jsonLightInputContext.ReadingResponse || this.ReadingDelta,
true,
this.jsonLightResourceDeserializer.BaseUri,
this.CurrentNavigationSource);

if (parseResult != null)
{
// a top-level (deleted) resource in a delta response can come from any entity set
Expand Down Expand Up @@ -3459,7 +3467,10 @@ await this.jsonLightResourceDeserializer.JsonReader.ReadAsync()
contextUriFromPayload: contextUriStr,
payloadKind: this.ReadingDelta ? ODataPayloadKind.Delta : ODataPayloadKind.Resource,
clientCustomTypeResolver: this.jsonLightResourceDeserializer.MessageReaderSettings.ClientCustomTypeResolver,
needParseFragment: this.jsonLightInputContext.ReadingResponse || this.ReadingDelta);
needParseFragment: this.jsonLightInputContext.ReadingResponse || this.ReadingDelta,
true,
this.jsonLightResourceDeserializer.BaseUri,
this.CurrentNavigationSource);

if (parseResult != null)
{
Expand Down
6 changes: 6 additions & 0 deletions src/Microsoft.OData.Core/ODataConstants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,9 @@ internal static class ODataInternalConstants
/// <summary>A segment name in a URI that indicates metadata is being requested.</summary>
internal const string UriMetadataSegment = "$metadata";

/// <summary>A segment name in a URI that indicates metadata is being requested.</summary>
internal const string UriMetadataSegmentHash = "$metadata#";

/// <summary>The OData prefix</summary>
internal const string ODataPrefix = "odata";

Expand Down Expand Up @@ -191,6 +194,9 @@ internal static class ODataInternalConstants
/// <summary>The $delta token indicates delta resource set.</summary>
internal const string ContextUriDeltaResourceSet = UriSegmentSeparator + DeltaResourceSet;

/// <summary>The $delta token indicates delta resource set.</summary>
internal const string HashDeltaResourceSet = TypeNamePrefix + DeltaResourceSet;

/// <summary>The $deletedEntity token indicates deleted resource.</summary>
internal const string DeletedEntry = "$deletedEntity";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,21 @@ private EdmModel GetModel()
return model;
}

// TODO: Support relative context uri and resolving other relative uris
[Fact]
public void ParseRelativeContextUrlShouldThrowException()
public void ParseRelativeContextUrlShouldNotThrowException()
{
string relativeUrl = "$metadata#R";
Action parseContextUri = () => ODataJsonLightContextUriParser.Parse(new EdmModel(), relativeUrl, ODataPayloadKind.Unsupported, null, true);
parseContextUri.Throws<ODataException>(ErrorStrings.ODataJsonLightContextUriParser_TopLevelContextUrlShouldBeAbsolute(relativeUrl));
string relativeUrl = "$metadata#People";
Action parseContextUri = () => ODataJsonLightContextUriParser.Parse(this.GetModel(), relativeUrl, ODataPayloadKind.Unsupported, null, true, true, new Uri("https://www.example.com/api"));
parseContextUri.DoesNotThrow();
}

[Fact]
public void ParseRelativeContextUrlWithOnlyDeltaSegmentShouldNotThrowException()
{
string relativeUrl = "#$delta";
IEdmNavigationSource navigationSource = this.GetModel().FindDeclaredEntitySet("People") as IEdmNavigationSource;
Action parseContextUri = () => ODataJsonLightContextUriParser.Parse(this.GetModel(), relativeUrl, ODataPayloadKind.Unsupported, null, true, true, new Uri("https://www.example.com/api"), navigationSource);
parseContextUri.DoesNotThrow();
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,17 +88,17 @@ public void JsonLightContextUriParserErrorTest()
},
new ContextUriParserTestCase
{
DebugDescription = "empty string is a relative URI; context URIs have to absolute",
DebugDescription = "empty string is a relative URI; relative context URLs should not be null.",
ContextUri = string.Empty,
Model = this.testModel,
ExpectedException = ODataExpectedExceptions.ODataException("ODataJsonLightContextUriParser_TopLevelContextUrlShouldBeAbsolute", "")
ExpectedException = ODataExpectedExceptions.ODataException("ODataJsonLightContextUriParser_InvalidContextUrl")
},
new ContextUriParserTestCase
{
DebugDescription = "another relative URI; context URIs have to absolute",
DebugDescription = "another relative URI; context URIs should have null baseUri",
ContextUri = "relativeUri",
Model = this.testModel,
ExpectedException = ODataExpectedExceptions.ODataException("ODataJsonLightContextUriParser_TopLevelContextUrlShouldBeAbsolute", "relativeUri")
ExpectedException = ODataExpectedExceptions.ODataException("ODataJsonLightContextUriParser_InvalidContextUrl")
},
new ContextUriParserTestCase
{
Expand Down

0 comments on commit ef02ee9

Please sign in to comment.