public void UrlToStringShouldThrowWithNoMetadataAndMetadataDocumentUriIsNotProvided() { var stream = new MemoryStream(); var serializer = GetSerializer(stream, null, true, false); var uri = new Uri("TestUri", UriKind.Relative); Action uriToStrongError = () => serializer.UriToString(uri); uriToStrongError.Throws <ODataException>(Strings.ODataJsonLightSerializer_RelativeUriUsedWithoutMetadataDocumentUriOrMetadata(UriUtils.UriToString(uri))); }
/// <summary> /// Converts the given <paramref name="uri"/> Uri to a string. /// If the provided baseUri is not null and is a base Uri of the <paramref name="uri"/> Uri /// the method returns the string form of the relative Uri. /// </summary> /// <param name="uri">The Uri to convert.</param> /// <param name="failOnRelativeUriWithoutBaseUri">If set to true then this method will fail if the uri specified by <paramref name="uri"/> is relative /// and no base uri is specified.</param> /// <returns>The string form of the <paramref name="uri"/> Uri. If the Uri is absolute it returns the /// string form of the <paramref name="uri"/>. If the <paramref name="uri"/> Uri is not absolute /// it returns the original string of the Uri.</returns> internal string UriToUrlAttributeValue(Uri uri, bool failOnRelativeUriWithoutBaseUri) { Debug.Assert(uri != null, "uri != null"); if (this.UrlResolver != null) { // The resolver returns 'null' if no custom resolution is desired. Uri resultUri = this.UrlResolver.ResolveUrl(this.MessageWriterSettings.PayloadBaseUri, uri); if (resultUri != null) { return(UriUtils.UriToString(resultUri)); } } if (!uri.IsAbsoluteUri) { // NOTE: the only URIs that are allowed to be relative (e.g., failOnRelativeUriWithoutBaseUri is false) // are metadata URIs in operations; for such metadata URIs there is no base URI. if (this.MessageWriterSettings.PayloadBaseUri == null && failOnRelativeUriWithoutBaseUri) { throw new ODataException( ODataErrorStrings.ODataWriter_RelativeUriUsedWithoutBaseUriSpecified(UriUtils.UriToString(uri))); } uri = UriUtils.EnsureEscapedRelativeUri(uri); } return(UriUtils.UriToString(uri)); }
/// <summary> /// Resolves a type. /// </summary> /// <param name="typeName">The type name.</param> /// <param name="clientCustomTypeResolver">The function of client cuetom type resolver.</param> /// <param name="throwIfMetadataConflict">Whether to throw if a type specified in the ContextUri is not found in metadata.</param> /// <returns>The resolved Edm type.</returns> private ODataPayloadKind ResolveType(string typeName, Func <IEdmType, string, IEdmType> clientCustomTypeResolver, bool throwIfMetadataConflict) { string typeNameToResolve = EdmLibraryExtensions.GetCollectionItemTypeName(typeName) ?? typeName; bool isCollection = typeNameToResolve != typeName; EdmTypeKind typeKind; IEdmType resolvedType = MetadataUtils.ResolveTypeNameForRead(this.model, /*expectedType*/ null, typeNameToResolve, clientCustomTypeResolver, out typeKind); if (resolvedType == null && !throwIfMetadataConflict) { string namespaceName; string name; TypeUtils.ParseQualifiedTypeName(typeName, out namespaceName, out name, out isCollection); resolvedType = new EdmUntypedStructuredType(namespaceName, name); } if (resolvedType == null || resolvedType.TypeKind != EdmTypeKind.Primitive && resolvedType.TypeKind != EdmTypeKind.Enum && resolvedType.TypeKind != EdmTypeKind.Complex && resolvedType.TypeKind != EdmTypeKind.Entity && resolvedType.TypeKind != EdmTypeKind.TypeDefinition && resolvedType.TypeKind != EdmTypeKind.Untyped) { throw new ODataException(ODataErrorStrings.ODataJsonLightContextUriParser_InvalidEntitySetNameOrTypeName(UriUtils.UriToString(this.parseResult.ContextUri), typeName)); } if (resolvedType.TypeKind == EdmTypeKind.Entity || resolvedType.TypeKind == EdmTypeKind.Complex) { this.parseResult.EdmType = resolvedType; return(isCollection ? ODataPayloadKind.ResourceSet : ODataPayloadKind.Resource); } // For structured collection ,the EdmType is element type. for primitive collection, it is collection type resolvedType = isCollection ? EdmLibraryExtensions.GetCollectionType(resolvedType.ToTypeReference(true /*nullable*/)) : resolvedType; this.parseResult.EdmType = resolvedType; return(isCollection ? ODataPayloadKind.Collection : ODataPayloadKind.Property); }
/// <summary> /// Returns the string representation of the URI /// </summary> /// <param name="uri">The uri to process.</param> /// <returns>Returns the string representation of the URI.</returns> internal string UriToString(Uri uri) { Debug.Assert(uri != null, "uri != null"); // Get the metadataDocumentUri directly from MessageWriterSettings and not using ContextUriBuilder because in the case of getting the service document with nometadata // ContextUriBuilder returns null, but the metadataDocumentUri is needed to calculate Absolute Uris in the service document. In any other case jsonLightOutputContext.CreateContextUriBuilder() should be used. Uri metadataDocumentUri = this.jsonLightOutputContext.MessageWriterSettings.MetadataDocumentUri; Uri resultUri; if (this.jsonLightOutputContext.PayloadUriConverter != null) { // The resolver returns 'null' if no custom resolution is desired. resultUri = this.jsonLightOutputContext.PayloadUriConverter.ConvertPayloadUri(metadataDocumentUri, uri); if (resultUri != null) { return(UriUtils.UriToString(resultUri)); } } resultUri = uri; if (!resultUri.IsAbsoluteUri) { if (!this.allowRelativeUri) { // TODO: Check if it is dead code to be removed. if (metadataDocumentUri == null) { throw new ODataException(Strings.ODataJsonLightSerializer_RelativeUriUsedWithoutMetadataDocumentUriOrMetadata(UriUtils.UriToString(resultUri))); } resultUri = UriUtils.UriToAbsoluteUri(metadataDocumentUri, uri); } else { resultUri = UriUtils.EnsureEscapedRelativeUri(resultUri); } } return(UriUtils.UriToString(resultUri)); }
private ODataPayloadKind ParseContextUriFragment(string fragment, Func <IEdmType, string, IEdmType> clientCustomTypeResolver, bool throwIfMetadataConflict, out bool isUndeclared) { bool hasItemSelector = false; ODataDeltaKind kind = ODataDeltaKind.None; isUndeclared = false; // Deal with /$entity if (fragment.EndsWith(ODataConstants.ContextUriFragmentItemSelector, StringComparison.Ordinal)) { hasItemSelector = true; fragment = fragment.Substring(0, fragment.Length - ODataConstants.ContextUriFragmentItemSelector.Length); } else if (fragment.EndsWith(ODataConstants.ContextUriDeltaResourceSet, StringComparison.Ordinal)) { kind = ODataDeltaKind.ResourceSet; fragment = fragment.Substring(0, fragment.Length - ODataConstants.ContextUriDeltaResourceSet.Length); } else if (fragment.EndsWith(ODataConstants.ContextUriDeletedEntry, StringComparison.Ordinal)) { kind = ODataDeltaKind.DeletedEntry; fragment = fragment.Substring(0, fragment.Length - ODataConstants.ContextUriDeletedEntry.Length); } else if (fragment.EndsWith(ODataConstants.ContextUriDeltaLink, StringComparison.Ordinal)) { kind = ODataDeltaKind.Link; fragment = fragment.Substring(0, fragment.Length - ODataConstants.ContextUriDeltaLink.Length); } else if (fragment.EndsWith(ODataConstants.ContextUriDeletedLink, StringComparison.Ordinal)) { kind = ODataDeltaKind.DeletedLink; fragment = fragment.Substring(0, fragment.Length - ODataConstants.ContextUriDeletedLink.Length); } this.parseResult.DeltaKind = kind; // Deal with query option if (fragment.EndsWith(")", StringComparison.Ordinal)) { int index = fragment.Length - 2; for (int rcount = 1; rcount > 0 && index > 0; --index) { switch (fragment[index]) { case '(': rcount--; break; case ')': rcount++; break; } } if (index == 0) { throw new ODataException(ODataErrorStrings.ODataJsonLightContextUriParser_InvalidContextUrl(UriUtils.UriToString(this.parseResult.ContextUri))); } string previous = fragment.Substring(0, index + 1); // Don't treat Collection(Edm.Type) as SelectExpand segment if (!previous.Equals("Collection")) { string selectExpandStr = fragment.Substring(index + 2); selectExpandStr = selectExpandStr.Substring(0, selectExpandStr.Length - 1); // Do not treat Key as SelectExpand segment if (KeyPattern.IsMatch(selectExpandStr)) { throw new ODataException(ODataErrorStrings.ODataJsonLightContextUriParser_LastSegmentIsKeySegment(UriUtils.UriToString(this.parseResult.ContextUri))); } this.parseResult.SelectQueryOption = ExtractSelectQueryOption(selectExpandStr); fragment = previous; } } ODataPayloadKind detectedPayloadKind = ODataPayloadKind.Unsupported; EdmTypeResolver edmTypeResolver = new EdmTypeReaderResolver(this.model, clientCustomTypeResolver); if (!fragment.Contains(ODataConstants.UriSegmentSeparator) && !hasItemSelector && kind == ODataDeltaKind.None) { // Service document: no fragment if (fragment.Length == 0) { detectedPayloadKind = ODataPayloadKind.ServiceDocument; } else if (fragment.Equals(ODataConstants.CollectionPrefix + "(" + ODataConstants.EntityReferenceSegmentName + ")")) { detectedPayloadKind = ODataPayloadKind.EntityReferenceLinks; } else if (fragment.Equals(ODataConstants.EntityReferenceSegmentName)) { detectedPayloadKind = ODataPayloadKind.EntityReferenceLink; } else { var foundNavigationSource = this.model.FindDeclaredNavigationSource(fragment); if (foundNavigationSource != null) { // Resource Set: {schema.entity-container.entity-set} or Singleton: {schema.entity-container.singleton} this.parseResult.NavigationSource = foundNavigationSource; this.parseResult.EdmType = edmTypeResolver.GetElementType(foundNavigationSource); detectedPayloadKind = foundNavigationSource is IEdmSingleton ? ODataPayloadKind.Resource : ODataPayloadKind.ResourceSet; } else { // Property: {schema.type} or Collection({schema.type}) where schema.type is primitive or complex. detectedPayloadKind = this.ResolveType(fragment, clientCustomTypeResolver, throwIfMetadataConflict); Debug.Assert( this.parseResult.EdmType.TypeKind == EdmTypeKind.Primitive || this.parseResult.EdmType.TypeKind == EdmTypeKind.Enum || this.parseResult.EdmType.TypeKind == EdmTypeKind.TypeDefinition || this.parseResult.EdmType.TypeKind == EdmTypeKind.Complex || this.parseResult.EdmType.TypeKind == EdmTypeKind.Collection || this.parseResult.EdmType.TypeKind == EdmTypeKind.Entity || this.parseResult.EdmType.TypeKind == EdmTypeKind.Untyped, "The first context URI segment must be a set or a non-entity type."); } } } else { Debug.Assert(this.parseResult.MetadataDocumentUri.IsAbsoluteUri, "this.parseResult.MetadataDocumentUri.IsAbsoluteUri"); string metadataDocumentStr = UriUtils.UriToString(this.parseResult.MetadataDocumentUri); if (!metadataDocumentStr.EndsWith(ODataConstants.UriMetadataSegment, StringComparison.Ordinal)) { throw new ODataException(ODataErrorStrings.ODataJsonLightContextUriParser_InvalidContextUrl(UriUtils.UriToString(this.parseResult.ContextUri))); } Uri serviceRoot = new Uri(metadataDocumentStr.Substring(0, metadataDocumentStr.Length - ODataConstants.UriMetadataSegment.Length)); ODataUriParser odataUriParser = new ODataUriParser(this.model, serviceRoot, new Uri(serviceRoot, fragment)); ODataPath path; try { path = odataUriParser.ParsePath(); } catch (ODataException) { throw new ODataException(ODataErrorStrings.ODataJsonLightContextUriParser_InvalidContextUrl(UriUtils.UriToString(this.parseResult.ContextUri))); } if (path.Count == 0) { throw new ODataException(ODataErrorStrings.ODataJsonLightContextUriParser_InvalidContextUrl(UriUtils.UriToString(this.parseResult.ContextUri))); } this.parseResult.Path = path; parseResult.NavigationSource = path.NavigationSource(); parseResult.EdmType = path.LastSegment.EdmType; ODataPathSegment lastSegment = path.TrimEndingTypeSegment().LastSegment; if (lastSegment is EntitySetSegment || lastSegment is NavigationPropertySegment) { if (kind != ODataDeltaKind.None) { detectedPayloadKind = ODataPayloadKind.Delta; } else { detectedPayloadKind = hasItemSelector ? ODataPayloadKind.Resource : ODataPayloadKind.ResourceSet; } if (this.parseResult.EdmType is IEdmCollectionType) { var collectionTypeReference = this.parseResult.EdmType.ToTypeReference().AsCollection(); if (collectionTypeReference != null) { this.parseResult.EdmType = collectionTypeReference.ElementType().Definition; } } } else if (lastSegment is SingletonSegment) { detectedPayloadKind = ODataPayloadKind.Resource; } else if (path.IsIndividualProperty()) { isUndeclared = path.IsUndeclared(); detectedPayloadKind = ODataPayloadKind.Property; IEdmComplexType complexType = parseResult.EdmType as IEdmComplexType; if (complexType != null) { detectedPayloadKind = ODataPayloadKind.Resource; } else { IEdmCollectionType collectionType = parseResult.EdmType as IEdmCollectionType; if (collectionType != null) { if (collectionType.ElementType.IsComplex()) { this.parseResult.EdmType = collectionType.ElementType.Definition; detectedPayloadKind = ODataPayloadKind.ResourceSet; } else { detectedPayloadKind = ODataPayloadKind.Collection; } } } } else { throw new ODataException(ODataErrorStrings.ODataJsonLightContextUriParser_InvalidContextUrl(UriUtils.UriToString(this.parseResult.ContextUri))); } } return(detectedPayloadKind); }
/// <summary> /// Applies the model and validates the context URI against it. /// </summary> /// <param name="expectedPayloadKind">The payload kind we expect the context URI to conform to.</param> /// <param name="clientCustomTypeResolver">The function of client custom type resolver.</param> /// <param name="throwIfMetadataConflict">Whether to throw if a type specified in the ContextUri is not found in metadata.</param> private void ParseContextUri(ODataPayloadKind expectedPayloadKind, Func <IEdmType, string, IEdmType> clientCustomTypeResolver, bool throwIfMetadataConflict) { bool isUndeclared; ODataPayloadKind detectedPayloadKind = this.ParseContextUriFragment(this.parseResult.Fragment, clientCustomTypeResolver, throwIfMetadataConflict, out isUndeclared); // unsupported payload kind indicates that this is during payload kind detection, so we should not fail. bool detectedPayloadKindMatchesExpectation = detectedPayloadKind == expectedPayloadKind || expectedPayloadKind == ODataPayloadKind.Unsupported; IEdmType parseType = this.parseResult.EdmType; if (parseType != null && parseType.TypeKind == EdmTypeKind.Untyped) { if (string.Equals(parseType.FullTypeName(), ODataConstants.ContextUriFragmentUntyped, StringComparison.Ordinal)) { // Anything matches the built-in Edm.Untyped this.parseResult.DetectedPayloadKinds = new[] { ODataPayloadKind.ResourceSet, ODataPayloadKind.Property, ODataPayloadKind.Collection, ODataPayloadKind.Resource }; detectedPayloadKindMatchesExpectation = true; } else if (expectedPayloadKind == ODataPayloadKind.Property || expectedPayloadKind == ODataPayloadKind.Resource) { // If we created an untyped type because the name was not resolved it can match any single value this.parseResult.DetectedPayloadKinds = new[] { ODataPayloadKind.Property, ODataPayloadKind.Resource }; detectedPayloadKindMatchesExpectation = true; } } else if (parseType != null && parseType.TypeKind == EdmTypeKind.Collection && ((IEdmCollectionType)parseType).ElementType.TypeKind() == EdmTypeKind.Untyped) { this.parseResult.DetectedPayloadKinds = new[] { ODataPayloadKind.ResourceSet, ODataPayloadKind.Property, ODataPayloadKind.Collection }; if (expectedPayloadKind == ODataPayloadKind.ResourceSet || expectedPayloadKind == ODataPayloadKind.Property || expectedPayloadKind == ODataPayloadKind.Collection) { detectedPayloadKindMatchesExpectation = true; } } else if (detectedPayloadKind == ODataPayloadKind.ResourceSet && parseType.IsODataComplexTypeKind()) { this.parseResult.DetectedPayloadKinds = new[] { ODataPayloadKind.ResourceSet, ODataPayloadKind.Property, ODataPayloadKind.Collection }; if (expectedPayloadKind == ODataPayloadKind.Property || expectedPayloadKind == ODataPayloadKind.Collection) { detectedPayloadKindMatchesExpectation = true; } } else if (detectedPayloadKind == ODataPayloadKind.Resource && parseType.IsODataComplexTypeKind()) { this.parseResult.DetectedPayloadKinds = new[] { ODataPayloadKind.Resource, ODataPayloadKind.Property }; if (expectedPayloadKind == ODataPayloadKind.Property) { detectedPayloadKindMatchesExpectation = true; } } else if (detectedPayloadKind == ODataPayloadKind.Collection) { // If the detected payload kind is 'collection' it can always also be treated as a property. this.parseResult.DetectedPayloadKinds = new[] { ODataPayloadKind.Collection, ODataPayloadKind.Property }; if (expectedPayloadKind == ODataPayloadKind.Property) { detectedPayloadKindMatchesExpectation = true; } } else if (detectedPayloadKind == ODataPayloadKind.Resource) { this.parseResult.DetectedPayloadKinds = new[] { ODataPayloadKind.Resource, ODataPayloadKind.Delta }; if (expectedPayloadKind == ODataPayloadKind.Delta) { this.parseResult.DeltaKind = ODataDeltaKind.Resource; detectedPayloadKindMatchesExpectation = true; } } else if (detectedPayloadKind == ODataPayloadKind.Property && isUndeclared && (expectedPayloadKind == ODataPayloadKind.Resource || expectedPayloadKind == ODataPayloadKind.ResourceSet)) { // for undeclared, we don't know whether it is a resource/resource set or not. this.parseResult.DetectedPayloadKinds = new[] { expectedPayloadKind, ODataPayloadKind.Property }; detectedPayloadKindMatchesExpectation = true; } else { this.parseResult.DetectedPayloadKinds = new[] { detectedPayloadKind }; } // If the expected and detected payload kinds don't match and we are not running payload kind detection // right now (payloadKind == ODataPayloadKind.Unsupported) and we did not detect a collection kind for // an expected property kind (which is allowed), fail. if (!detectedPayloadKindMatchesExpectation) { throw new ODataException(ODataErrorStrings.ODataJsonLightContextUriParser_ContextUriDoesNotMatchExpectedPayloadKind(UriUtils.UriToString(this.parseResult.ContextUri), expectedPayloadKind.ToString())); } // NOTE: we interpret an empty select query option to mean that nothing should be projected // (whereas a missing select query option means everything should be projected). string selectQueryOption = this.parseResult.SelectQueryOption; if (selectQueryOption != null) { if (detectedPayloadKind != ODataPayloadKind.ResourceSet && detectedPayloadKind != ODataPayloadKind.Resource && detectedPayloadKind != ODataPayloadKind.Delta) { throw new ODataException(ODataErrorStrings.ODataJsonLightContextUriParser_InvalidPayloadKindWithSelectQueryOption(expectedPayloadKind.ToString())); } } }
/// <summary> /// Validates that a string is either a valid absolute URI, or (if it begins with '#') it is a valid URI fragment. /// </summary> /// <param name="metadataDocumentUri">The metadata document uri.</param> /// <param name="propertyName">The property name to validate.</param> internal static void ValidateMetadataReferencePropertyName(Uri metadataDocumentUri, string propertyName) { Debug.Assert(metadataDocumentUri != null, "metadataDocumentUri != null"); Debug.Assert(metadataDocumentUri.IsAbsoluteUri, "metadataDocumentUri.IsAbsoluteUri"); Debug.Assert(!String.IsNullOrEmpty(propertyName), "!string.IsNullOrEmpty(propertyName)"); string uriStringToValidate = propertyName; // If it starts with a '#', validate that the rest of the string is a valid Uri fragment. if (propertyName[0] == ODataConstants.ContextUriFragmentIndicator) { // In order to use System.Uri to validate a fragement, we first prepend the metadataDocumentUri // so that it becomes an absolute URI which we can validate with Uri.IsWellFormedUriString. uriStringToValidate = UriUtils.UriToString(metadataDocumentUri) + UriUtils.EnsureEscapedFragment(propertyName); } if (!Uri.IsWellFormedUriString(uriStringToValidate, UriKind.Absolute) || !ODataJsonLightUtils.IsMetadataReferenceProperty(propertyName) || propertyName[propertyName.Length - 1] == ODataConstants.ContextUriFragmentIndicator) { throw new ODataException(Strings.ValidationUtils_InvalidMetadataReferenceProperty(propertyName)); } if (IsOpenMetadataReferencePropertyName(metadataDocumentUri, propertyName)) { throw new ODataException(Strings.ODataJsonLightValidationUtils_OpenMetadataReferencePropertyNotSupported(propertyName, UriUtils.UriToString(metadataDocumentUri))); } }
/// <summary> /// Writes a set of links (Uris) in response to a $links query; includes optional count and next-page-link information. /// </summary> /// <param name="writer">The <see cref="XmlWriter"/> to write to.</param> /// <param name="links">The associated entity links to write.</param> internal static void WriteAssociatedEntityLinks(XmlWriter writer, ODataAssociatedEntityLinks links) { DebugUtils.CheckNoExternalCallers(); Debug.Assert(writer != null, "writer != null"); Debug.Assert(links != null, "links != null"); // <links> ... writer.WriteStartElement(string.Empty, AtomConstants.ODataLinksElementName, AtomConstants.ODataNamespace); // xmlns= writer.WriteAttributeString(AtomConstants.XmlnsNamespacePrefix, AtomConstants.ODataNamespace); if (links.InlineCount.HasValue) { // <m:count> WriteCount(writer, links.InlineCount.Value, true); } IEnumerable <ODataAssociatedEntityLink> associatedEntityLinks = links.Links; if (associatedEntityLinks != null) { foreach (ODataAssociatedEntityLink link in associatedEntityLinks) { WriteAssociatedEntityLink(writer, link, false); } } if (links.NextLink != null) { // <d:next> writer.WriteElementString(string.Empty, AtomConstants.ODataNextLinkElementName, AtomConstants.ODataNamespace, UriUtils.UriToString(links.NextLink)); } // </links> writer.WriteEndElement(); }
/// <summary> /// Converts the given <paramref name="uri"/> Uri to a string. /// If the provided baseUri is not null and is a base Uri of the <paramref name="uri"/> Uri /// the method returns the string form of the relative Uri. /// </summary> /// <param name="uri">The Uri to convert.</param> /// <param name="baseUri">An optional base Uri</param> /// <returns>The string form of the <paramref name="uri"/> Uri. If the Uri is absolute and /// the <paramref name="baseUri"/> is the base Uri of the <paramref name="uri"/> Uri it returns a relative Uri; /// otherwise the string form of the absolute Uri. If the <paramref name="uri"/> Uri is not absolute /// it returns the original string of the Uri.</returns> internal static string ToUrlAttributeValue(this Uri uri, Uri baseUri) { DebugUtils.CheckNoExternalCallers(); Debug.Assert(uri != null, "uri != null"); if (baseUri == null) { if (!uri.IsAbsoluteUri) { throw new ODataException(Strings.ODataWriter_RelativeUriUsedWithoutBaseUriSpecified(UriUtils.UriToString(uri))); } } else { if (uri.IsAbsoluteUri && UriUtils.UriInvariantInsensitiveIsBaseOf(baseUri, uri)) { uri = baseUri.MakeRelativeUri(uri); } } return(UriUtils.UriToString(uri)); }