/// <summary> /// Writes an EPM attribute target. /// </summary> /// <param name="writer">The writer to write to.</param> /// <param name="targetSegment">The target segment describing the attribute to write.</param> /// <param name="epmValueCache">The entry properties value cache to use to access the properties.</param> /// <param name="resourceType">The resource type of the entry.</param> /// <param name="metadata">The metadata provider to use.</param> /// <param name="alreadyDeclaredPrefix">The name of the prefix if it was already declared.</param> private static void WriteAttributeEpm( XmlWriter writer, EpmTargetPathSegment targetSegment, EntryPropertiesValueCache epmValueCache, ResourceType resourceType, DataServiceMetadataProviderWrapper metadata, ref string alreadyDeclaredPrefix) { Debug.Assert(writer != null, "writer != null"); Debug.Assert(targetSegment != null && targetSegment.IsAttribute, "Only attribute target segments are supported by this method."); Debug.Assert(targetSegment.HasContent, "Attribute target segments must have content."); string textPropertyValue = GetEntryPropertyValueAsText(targetSegment, epmValueCache, resourceType, metadata); Debug.Assert(textPropertyValue != null, "Text value of a property mapped to attribute must not be null, the GetEntryPropertyValueAsText should take care of that."); // If the prefix is null, the WCF DS will still write it as the default namespace, so we need it to be an empty string. string attributePrefix = targetSegment.SegmentNamespacePrefix ?? string.Empty; writer.WriteAttributeString(attributePrefix, targetSegment.AttributeName, targetSegment.SegmentNamespaceUri, textPropertyValue); // Write out the declaration explicitely only if the prefix is not empty (just like the WCF DS does) if (attributePrefix.Length > 0) { WriteNamespaceDeclaration(writer, targetSegment, ref alreadyDeclaredPrefix); } }
public void AskJustOncesForTypeTest() { var typesToTest = new Func <DSPMetadata, ResourceType>[] { (metadata) => { var t = metadata.AddEntityType("EntityType", null, null, false); metadata.AddKeyProperty(t, "ID", typeof(int)); return(t); }, (metadata) => metadata.AddComplexType("ComplexType", null, null, false) }; AstoriaTestNS.TestUtil.RunCombinations( typesToTest, (typeCreate) => { var metadataProvider = new CallCountMetadataProvider("Test", "TestNS"); var type = typeCreate(metadataProvider); metadataProvider.SetReadOnly(); var wrapper = new DataServiceMetadataProviderWrapper(metadataProvider); var resourceType = wrapper.TryResolveResourceType(type.FullName); Assert.AreSame(type, resourceType, "The returned type is not the one expected."); wrapper.TryResolveResourceType(type.FullName); wrapper.TryResolveResourceType(type.FullName); Assert.AreEqual(1, metadataProvider.TryResolveResourceTypeCallCount, "The underlying metadata provider should be called just once."); }); }
/// <summary> /// Writes out the value of a complex property. /// </summary> /// <param name="writer">The <see cref="XmlWriter"/> to write to.</param> /// <param name="metadata">The metadata provider to use or null if no metadata is available.</param> /// <param name="complexValue">The complex value to write.</param> /// <param name="metadataType">The metadata type for the complex value.</param> /// <param name="isOpenPropertyType">True if the type name belongs to an open property.</param> /// <param name="isWritingCollection">True if we are writing a collection instead of an entry.</param> /// <param name="version">The protocol version used for writing.</param> /// <param name="epmValueCache">Cache of values used in EPM so that we avoid multiple enumerations of properties/items. (can be null)</param> /// <param name="epmSourcePathSegment">The EPM source path segment which points to the property we're writing. (can be null)</param> internal static void WriteComplexValue( XmlWriter writer, DataServiceMetadataProviderWrapper metadata, ODataComplexValue complexValue, ResourceType metadataType, bool isOpenPropertyType, bool isWritingCollection, ODataVersion version, EpmValueCache epmValueCache, EpmSourcePathSegment epmSourcePathSegment) { DebugUtils.CheckNoExternalCallers(); Debug.Assert(complexValue != null, "complexValue != null"); string typeName = complexValue.TypeName; // resolve the type name to the resource type; if no type name is specified we will use the // type inferred from metadata ResourceType complexValueType = MetadataUtils.ResolveTypeName(metadata, metadataType, ref typeName, ResourceTypeKind.ComplexType, isOpenPropertyType); if (typeName != null) { WritePropertyTypeAttribute(writer, typeName); } WriteProperties( writer, metadata, complexValueType, EpmValueCache.GetComplexValueProperties(epmValueCache, epmSourcePathSegment, complexValue, true), version, isWritingCollection, epmValueCache, epmSourcePathSegment); }
/// <summary> /// Writes the custom mapped EPM properties to an XML writer which is expected to be positioned such to write /// a child element of the entry element. /// </summary> /// <param name="writer">The XmlWriter to write to.</param> /// <param name="epmTargetTree">The EPM target tree to use.</param> /// <param name="epmValueCache">The entry properties value cache to use to access the properties.</param> /// <param name="resourceType">The resource type of the entry.</param> /// <param name="metadata">The metadata provider to use.</param> internal static void WriteEntryEpm( XmlWriter writer, EpmTargetTree epmTargetTree, EntryPropertiesValueCache epmValueCache, ResourceType resourceType, DataServiceMetadataProviderWrapper metadata) { DebugUtils.CheckNoExternalCallers(); Debug.Assert(writer != null, "writer != null"); Debug.Assert(epmTargetTree != null, "epmTargetTree != null"); Debug.Assert(epmValueCache != null, "epmValueCache != null"); Debug.Assert(resourceType != null, "For any EPM to exist the metadata must be available."); // If there are no custom mappings, just return null. EpmTargetPathSegment customRootSegment = epmTargetTree.NonSyndicationRoot; Debug.Assert(customRootSegment != null, "EPM Target tree must always have non-syndication root."); if (customRootSegment.SubSegments.Count == 0) { return; } foreach (EpmTargetPathSegment targetSegment in customRootSegment.SubSegments) { Debug.Assert(!targetSegment.IsAttribute, "Target segments under the custom root must be for elements only."); string alreadyDeclaredPrefix = null; WriteElementEpm(writer, targetSegment, epmValueCache, resourceType, metadata, ref alreadyDeclaredPrefix); } }
/// <summary> /// Validates a type name to ensure that it's not an empty string. /// </summary> /// <param name="metadata">The metadata provider to use or null if no metadata is available.</param> /// <param name="typeName">The type name to validate.</param> /// <param name="typeKind">The expected type kind for the given type name.</param> /// <param name="isOpenPropertyType">True if the type name belongs to an open property.</param> /// <returns>The resource type with the given name and kind if the metadata was available, otherwise null.</returns> internal static ResourceType ValidateTypeName(DataServiceMetadataProviderWrapper metadata, string typeName, ResourceTypeKind typeKind, bool isOpenPropertyType) { DebugUtils.CheckNoExternalCallers(); if (typeName == null) { // if we have metadata the type name of an entry or a complex value of an open property must not be null if (metadata != null && (typeKind == ResourceTypeKind.EntityType || isOpenPropertyType)) { throw new ODataException(Strings.ODataWriterCore_MissingTypeNameWithMetadata); } return(null); } // we do not allow empty type names if (typeName.Length == 0) { throw new ODataException(Strings.ODataWriter_TypeNameMustNotBeEmpty); } // If we do have metadata, lookup the type and translate it to ResourceType. ResourceType resourceType = null; if (metadata != null) { resourceType = typeKind == ResourceTypeKind.MultiValue ? ValidateMultiValueTypeName(metadata, typeName) : ValidateNonMultiValueTypeName(metadata, typeName, typeKind); } return(resourceType); }
/// <summary> /// Constructor. /// </summary> /// <param name="stream">The stream to write to.</param> /// <param name="odataWriterSettings">Configuration settings for the writer to create.</param> /// <param name="encoding">The encoding to use for writing.</param> /// <param name="writingResponse">True if the writer is to write a response payload; false if it's to write a request payload.</param> /// <param name="metadataProvider">The metadata provider to use.</param> /// <param name="writingFeed">True if the writer is created for writing a feed; false when it is created for writing an entry.</param> /// <param name="synchronous">True if the writer is created for synchronous operation; false for asynchronous.</param> internal ODataJsonWriter( Stream stream, ODataWriterSettings odataWriterSettings, Encoding encoding, bool writingResponse, DataServiceMetadataProviderWrapper metadataProvider, bool writingFeed, bool synchronous) : base( odataWriterSettings.Version, odataWriterSettings.BaseUri, writingResponse, metadataProvider, writingFeed, synchronous) { DebugUtils.CheckNoExternalCallers(); Debug.Assert(stream != null, "stream != null"); Debug.Assert(odataWriterSettings != null, "odataWriterSettings != null"); this.outputStream = new AsyncBufferedStream(stream); this.textWriter = new StreamWriter(this.outputStream, encoding); this.jsonWriter = new JsonWriter(this.textWriter, odataWriterSettings.Indent); }
/// <summary> /// Reads a property value starting on a complex value. /// </summary> /// <param name="complexValue">The complex value to start with.</param> /// <param name="complexPropertySegment">The EPM source path segment which points to the <paramref name="complexValue"/>.</param> /// <param name="epmValueCache">The EPM value cache to use.</param> /// <param name="sourceSegmentIndex">The index in the property value path to start with.</param> /// <param name="resourceType">The resource type of the complex value.</param> /// <param name="metadata">The metadata provider to use.</param> /// <param name="nullOnParentProperty">true if the value of the property is null because one of its parent properties was null, in this case /// the return value of the method is always null. false if the value of the property is the actual property value which may or may not be null.</param> /// <returns>The value of the property (may be null), or null if the property itself was not found due to one of its parent properties being null.</returns> private object ReadComplexPropertyValue( ODataComplexValue complexValue, EpmSourcePathSegment complexPropertySegment, EpmValueCache epmValueCache, int sourceSegmentIndex, ResourceType resourceType, DataServiceMetadataProviderWrapper metadata, out bool nullOnParentProperty) { Debug.Assert(this.propertyValuePath != null, "The propertyValuePath should have been initialized by now."); Debug.Assert(this.propertyValuePath.Length > sourceSegmentIndex, "The propertyValuePath must be at least as long as the source segment index."); Debug.Assert(epmValueCache != null, "epmValueCache != null"); Debug.Assert(sourceSegmentIndex >= 0, "sourceSegmentIndex >= 0"); Debug.Assert(resourceType != null, "resourceType != null"); if (complexValue == null) { nullOnParentProperty = true; return(null); } return(this.ReadPropertyValue( EpmValueCache.GetComplexValueProperties(epmValueCache, complexPropertySegment, complexValue, false), sourceSegmentIndex, resourceType, metadata, epmValueCache, out nullOnParentProperty)); }
private static void CreateCallCounterService(out CallCountMetadataProvider metadataProvider, out DSPServiceDefinition service) { metadataProvider = new CallCountMetadataProvider("DefaultContainer", "Default"); var entityType = metadataProvider.AddEntityType("EntityType", null, null, false /*isAbstract*/); metadataProvider.AddKeyProperty(entityType, "ID", typeof(int)); metadataProvider.AddPrimitiveProperty(entityType, "LastUpdatedAuthor", typeof(string), true /*eTag*/); var derivedType = metadataProvider.AddEntityType("DerivedType", null, entityType, false /*isAbstract*/); metadataProvider.AddPrimitiveProperty(derivedType, "LastModifiedAuthor", typeof(string), true /*eTag*/); metadataProvider.AddResourceSet("Customers", entityType); var wrapper = new DataServiceMetadataProviderWrapper(metadataProvider); DSPContext dataContext = new DSPContext(); service = new DSPServiceDefinition() { Metadata = metadataProvider, CreateDataSource = (m) => dataContext, ForceVerboseErrors = true, MediaResourceStorage = new DSPMediaResourceStorage(), SupportNamedStream = true, Writable = true, DataServiceBehavior = new OpenWebDataServiceDefinition.OpenWebDataServiceBehavior() { IncludeRelationshipLinksInResponse = true }, }; }
/// <summary> /// Creates an <see cref="ODataCollectionWriter"/> for the specified message and its stream. /// </summary> /// <param name="format">The serialization format to create the writer for.</param> /// <param name="encoding">The encoding to create the writer with.</param> /// <param name="stream">The response stream to write to.</param> /// <param name="settings">Writer settings to use.</param> /// <param name="metadataProvider">The metadata provider to use.</param> /// <param name="synchronous">True if the writer is created for synchronous operation; false for asynchronous.</param> /// <returns>The newly created <see cref="ODataCollectionWriter"/> instance.</returns> /// <remarks>This is used to create the collection writer once we've obtained the stream from the response.</remarks> private static ODataCollectionWriter CreateCollectionWriter( ODataFormat format, Encoding encoding, Stream stream, ODataWriterSettings settings, DataServiceMetadataProviderWrapper metadataProvider, bool synchronous) { if (settings.BaseUri != null && !settings.BaseUri.IsAbsoluteUri) { throw new ODataException(Strings.ODataWriter_BaseUriMustBeNullOrAbsolute(UriUtils.UriToString(settings.BaseUri))); } switch (format) { case ODataFormat.Json: return(new ODataJsonCollectionWriter(stream, settings, encoding, metadataProvider, synchronous)); case ODataFormat.Atom: return(new ODataAtomCollectionWriter(stream, settings, encoding, metadataProvider, synchronous)); case ODataFormat.Default: Debug.Assert(false, "Should never get here as content-type negotiation should not return Default format for collection."); throw new ODataException(Strings.ODataCollectionWriter_CannotCreateCollectionWriterForFormat(format.ToString())); default: throw new ODataException(Strings.General_InternalError(InternalErrorCodes.ODataCollectionWriter_CreateCollectionWriter_UnreachableCodePath)); } }
/// <summary> /// Given a target segment the method returns the text value of the property mapped to that segment to be used in EPM. /// </summary> /// <param name="targetSegment">The target segment to read the value for.</param> /// <param name="epmValueCache">The entry EPM value cache to use.</param> /// <param name="resourceType">The resource type of the entry being processed.</param> /// <param name="metadata">The metadata provider to use.</param> /// <returns>The test representation of the value, or the method throws if the text representation was not possible to obtain.</returns> private static string GetEntryPropertyValueAsText( EpmTargetPathSegment targetSegment, EntryPropertiesValueCache epmValueCache, ResourceType resourceType, DataServiceMetadataProviderWrapper metadata) { Debug.Assert(targetSegment != null, "targetSegment != null"); Debug.Assert(targetSegment.HasContent, "The target segment to read property for must have content."); Debug.Assert(targetSegment.EpmInfo != null, "The EPM info must be available on the target segment to read its property."); Debug.Assert(epmValueCache != null, "epmValueCache != null"); Debug.Assert(resourceType != null, "resourceType != null"); bool nullOnParentProperty; object propertyValue = targetSegment.EpmInfo.ReadEntryPropertyValue(epmValueCache, resourceType, metadata, out nullOnParentProperty); if (propertyValue == null) { // TODO: In V3 when we use new format for null values using the m:null attribute we need to check here // if we're mapping into an attribute, in which case we should fail (can't write null to attribute in V3) // or if we're writing into element return true null, to use the m:null attribute. // In V2 nulls are written out as empty string always (and they're written into content as well) return(string.Empty); } else { return(EpmWriterUtils.GetPropertyValueAsText(propertyValue)); } }
/// <summary> /// Constructor. /// </summary> /// <param name="version">The version of the OData protocol to use.</param> /// <param name="metadataProvider">The metadata provider to use.</param> /// <param name="synchronous">True if the writer is created for synchronous operation; false for asynchronous.</param> protected ODataCollectionWriterCore(ODataVersion version, DataServiceMetadataProviderWrapper metadataProvider, bool synchronous) { DebugUtils.CheckNoExternalCallers(); this.version = version; this.metadataProvider = metadataProvider; this.synchronous = synchronous; this.scopes.Push(new Scope(CollectionWriterState.Start, null)); }
/// <summary> /// Writes an EPM element target. /// </summary> /// <param name="writer">The writer to write to.</param> /// <param name="targetSegment">The target segment describing the element to write.</param> /// <param name="epmValueCache">The entry properties value cache to use to access the properties.</param> /// <param name="resourceType">The resource type of the entry.</param> /// <param name="metadata">The metadata provider to use.</param> /// <param name="alreadyDeclaredPrefix">The name of the prefix if it was already declared.</param> private static void WriteElementEpm( XmlWriter writer, EpmTargetPathSegment targetSegment, EntryPropertiesValueCache epmValueCache, ResourceType resourceType, DataServiceMetadataProviderWrapper metadata, ref string alreadyDeclaredPrefix) { Debug.Assert(writer != null, "writer != null"); Debug.Assert(targetSegment != null && !targetSegment.IsAttribute, "Only element target segments are supported by this method."); // If the prefix is null, the WCF DS will still write it as the default namespace, so we need it to be an empty string. string elementPrefix = targetSegment.SegmentNamespacePrefix ?? string.Empty; writer.WriteStartElement(elementPrefix, targetSegment.SegmentName, targetSegment.SegmentNamespaceUri); // Write out the declaration explicitly only if the prefix is not empty (just like the WCF DS does) if (elementPrefix.Length > 0) { WriteNamespaceDeclaration(writer, targetSegment, ref alreadyDeclaredPrefix); } // Serialize the sub segment attributes first foreach (EpmTargetPathSegment subSegment in targetSegment.SubSegments) { if (subSegment.IsAttribute) { WriteAttributeEpm(writer, subSegment, epmValueCache, resourceType, metadata, ref alreadyDeclaredPrefix); } } if (targetSegment.HasContent) { Debug.Assert(!targetSegment.SubSegments.Any(subSegment => !subSegment.IsAttribute), "If the segment has a content, it must not have any element children."); string textPropertyValue = GetEntryPropertyValueAsText(targetSegment, epmValueCache, resourceType, metadata); // TODO: In V3 we should check for textPropertyValue == null and write out the m:null in that case. Debug.Assert(textPropertyValue != null, "Null property value should not get here, the GetEntryPropertyValueAsText should take care of that for now."); writer.WriteString(textPropertyValue); } else { // Serialize the sub segment elements now foreach (EpmTargetPathSegment subSegment in targetSegment.SubSegments) { if (!subSegment.IsAttribute) { WriteElementEpm(writer, subSegment, epmValueCache, resourceType, metadata, ref alreadyDeclaredPrefix); } } } // Close the element writer.WriteEndElement(); }
public void AskJustOncesForServiceOperationTest() { var metadataProvider = new CallCountMetadataProvider("Test", "TestNS"); var serviceOperation = metadataProvider.AddServiceOperation("ServiceOperation", ServiceOperationResultKind.DirectValue, ResourceType.GetPrimitiveResourceType(typeof(string)), null, "GET", new ServiceOperationParameter[0]); metadataProvider.SetReadOnly(); var wrapper = new DataServiceMetadataProviderWrapper(metadataProvider); wrapper.TryResolveServiceOperation(serviceOperation.Name); wrapper.TryResolveServiceOperation(serviceOperation.Name); wrapper.TryResolveServiceOperation(serviceOperation.Name); Assert.AreEqual(1, metadataProvider.TryResolveServiceOperationCallCount, "The underlying metadata provider should be called just once."); }
/// <summary> /// Writes a name/value pair for a property. /// </summary> /// <param name="jsonWriter">The <see cref="JsonWriter"/> to write to.</param> /// <param name="metadata">The metadata provider to use or null if no metadata is available.</param> /// <param name="property">The property to write out.</param> /// <param name="owningType">The type owning the property (or null if no metadata is available).</param> /// <param name="version">The protocol version used for writing.</param> private static void WriteProperty( JsonWriter jsonWriter, DataServiceMetadataProviderWrapper metadata, ODataProperty property, ResourceType owningType, ODataVersion version) { DebugUtils.CheckNoExternalCallers(); Debug.Assert(jsonWriter != null, "jsonWriter != null"); ValidationUtils.ValidateProperty(property); ResourceProperty resourceProperty = ValidationUtils.ValidatePropertyDefined(property.Name, owningType); bool isOpenPropertyType = owningType != null && owningType.IsOpenType && resourceProperty == null; jsonWriter.WriteName(property.Name); object value = property.Value; if (value == null) { // verify that MultiValue properties are not null if (resourceProperty != null && resourceProperty.Kind == ResourcePropertyKind.MultiValue) { throw new ODataException(Strings.ODataWriter_MultiValuePropertiesMustNotHaveNullValue(resourceProperty.Name)); } jsonWriter.WriteValue(null); } else { ResourceType resourcePropertyType = resourceProperty == null ? null : resourceProperty.ResourceType; ODataComplexValue complexValue = value as ODataComplexValue; if (complexValue != null) { WriteComplexValue(jsonWriter, metadata, complexValue, resourcePropertyType, isOpenPropertyType, version); } else { ODataMultiValue multiValue = value as ODataMultiValue; if (multiValue != null) { ODataVersionChecker.CheckMultiValueProperties(version, property.Name); WriteMultiValue(jsonWriter, metadata, multiValue, resourcePropertyType, isOpenPropertyType, version); } else { WritePrimitiveValue(jsonWriter, value, resourcePropertyType); } } } }
/// <summary> /// Create a func which created an <see cref="ODataCollectionWriter"/> for a given request message and stream. /// </summary> /// <param name="message">The message to create the writer for.</param> /// <param name="settings">Configuration settings for the writer to create.</param> /// <param name="format">The OData format used for serialization of the payload.</param> /// <param name="encoding">The encoding used for serialization of the payload.</param> /// <param name="metadataProvider">The metadata provider to use.</param> /// <param name="synchronous">True if the writer is created for synchronous operation; false for asynchronous.</param> /// <returns>A task returning an OData collection writer to write the payload of the request/response.</returns> internal static Func <Stream, ODataCollectionWriter> Create( ODataMessage message, ODataWriterSettings settings, ODataFormat format, Encoding encoding, DataServiceMetadataProviderWrapper metadataProvider, bool synchronous) { DebugUtils.CheckNoExternalCallers(); ExceptionUtils.CheckArgumentNotNull(message, "message"); ExceptionUtils.CheckArgumentNotNull(settings, "settings"); return((stream) => CreateCollectionWriter(format, encoding, stream, settings, metadataProvider, synchronous)); }
/// <summary> /// Constructor. /// </summary> /// <param name="stream">The stream to write to.</param> /// <param name="writerSettings">Configuration settings for the writer to create.</param> /// <param name="encoding">The encoding to use for writing.</param> /// <param name="metadataProvider">The metadata provider to use.</param> /// <param name="synchronous">True if the writer is created for synchronous operation; false for asynchronous.</param> internal ODataAtomCollectionWriter( Stream stream, ODataWriterSettings writerSettings, Encoding encoding, DataServiceMetadataProviderWrapper metadataProvider, bool synchronous) : base(writerSettings.Version, metadataProvider, synchronous) { DebugUtils.CheckNoExternalCallers(); Debug.Assert(stream != null, "stream != null"); Debug.Assert(writerSettings != null, "writerSettings != null"); this.outputStream = new AsyncBufferedStream(stream); this.writer = ODataAtomWriterUtils.CreateXmlWriter(this.outputStream, writerSettings, encoding); }
public void AllowRedefiningConcurrencyTokenOnDerivedType() { var metadataProvider = new DSPMetadata("DefaultContainer", "Default"); var entityType = metadataProvider.AddEntityType("EntityType", null, null, false /*isAbstract*/); metadataProvider.AddKeyProperty(entityType, "ID", typeof(int)); metadataProvider.AddPrimitiveProperty(entityType, "LastUpdatedAuthor", typeof(string), true /*eTag*/); var derivedType = metadataProvider.AddEntityType("DerivedType", null, entityType, false /*isAbstract*/); metadataProvider.AddPrimitiveProperty(derivedType, "LastModifiedAuthor", typeof(string), true /*eTag*/); metadataProvider.AddResourceSet("Customers", entityType); var wrapper = new DataServiceMetadataProviderWrapper(metadataProvider); DSPResource baseTypeInstance = new DSPResource(entityType); baseTypeInstance.SetValue("ID", 1); baseTypeInstance.SetValue("LastUpdatedAuthor", "Phani"); DSPResource derivedTypeInstance = new DSPResource(derivedType); derivedTypeInstance.SetValue("ID", 1); derivedTypeInstance.SetValue("LastModifiedAuthor", "Raj"); DSPContext dataContext = new DSPContext(); var entities = dataContext.GetResourceSetEntities("Customers"); entities.AddRange(new object[] { baseTypeInstance, derivedTypeInstance }); var service = new DSPUnitTestServiceDefinition(metadataProvider, DSPDataProviderKind.Reflection, dataContext); using (TestWebRequest request = service.CreateForInProcess()) { try { request.StartService(); request.RequestUriString = "/$metadata"; request.SendRequestAndCheckResponse(); } finally { request.StopService(); } } }
/// <summary> /// Resolve a type name against the provided metadata. If no type name is given we either throw (if a type name on the value is required, e.g., on entries) /// or infer the type from metadata (if available). /// </summary> /// <param name="metadata">The metadata provider to use or null if no metadata is available.</param> /// <param name="typeFromMetadata">The type inferred from metadata or null if no metadata is available.</param> /// <param name="typeName">The type name to be resolved.</param> /// <param name="typeKind">The expected type kind of the resolved type.</param> /// <param name="isOpenPropertyType">True if the type name belongs to an open property.</param> /// <returns>A resource type for the <paramref name="typeName"/> or null if no metadata is available.</returns> internal static ResourceType ResolveTypeName(DataServiceMetadataProviderWrapper metadata, ResourceType typeFromMetadata, ref string typeName, ResourceTypeKind typeKind, bool isOpenPropertyType) { DebugUtils.CheckNoExternalCallers(); ResourceType typeFromValue = ValidationUtils.ValidateTypeName(metadata, typeName, typeKind, isOpenPropertyType); typeFromValue = ValidationUtils.ValidateMetadataType(typeFromMetadata, typeFromValue, typeKind); // derive the type name from the metadata if available if (typeName == null && typeFromValue != null) { typeName = typeFromValue.FullName; } return(typeFromValue); }
public void AskJustOncesForResourceSetTest() { var metadataProvider = new CallCountMetadataProvider("Test", "TestNS"); var type = metadataProvider.AddEntityType("Entity", null, null, false); metadataProvider.AddKeyProperty(type, "ID", typeof(int)); var resourceSet = metadataProvider.AddResourceSet("Entities", type); metadataProvider.SetReadOnly(); var wrapper = new DataServiceMetadataProviderWrapper(metadataProvider); wrapper.TryResolveResourceSet(resourceSet.Name); wrapper.TryResolveResourceSet(resourceSet.Name); wrapper.TryResolveResourceSet(resourceSet.Name); Assert.AreEqual(1, metadataProvider.TryResolveResourceSetCallCount, "The underlying metadata provider should be called just once."); }
public void ResourceSetNamesMustBeUniqueTest() { var metadataProvider = new ResourceSetsOverrideMetadataProvider("Test", "TestNS"); metadataProvider.ResourceSetValues = new List <ResourceSet>(); var type = metadataProvider.AddEntityType("EntityType", null, null, false); metadataProvider.ResourceSetValues.Add(new ResourceSet("Entities", type)); metadataProvider.ResourceSetValues.Add(new ResourceSet("Entities", type)); var wrapper = new DataServiceMetadataProviderWrapper(metadataProvider); ExceptionUtils.ExpectedException <DataServiceException>( () => wrapper.ResourceSets.Cast <object>().Count(), "The resource set 'Entities' returned by the provider is not read-only. Please make sure that all the resource sets are set to read-only.", "Two resource sets with the same name should fail in the wrapper."); }
/// <summary> /// Given a target segment the method returns the text value of the property mapped to that segment to be used in EPM. /// </summary> /// <param name="targetSegment">The target segment to read the value for.</param> /// <param name="epmValueCache">The entry EPM value cache to use.</param> /// <param name="resourceType">The resource type of the entry being processed.</param> /// <param name="metadata">The metadata provider to use.</param> /// <returns>The test representation of the value, or the method throws if the text representation was not possible to obtain.</returns> private static string GetEntryPropertyValueAsText( EpmTargetPathSegment targetSegment, EntryPropertiesValueCache epmValueCache, ResourceType resourceType, DataServiceMetadataProviderWrapper metadata) { Debug.Assert(targetSegment != null, "targetSegment != null"); Debug.Assert(targetSegment.HasContent, "The target segment to read property for must have content."); Debug.Assert(targetSegment.EpmInfo != null, "The EPM info must be available on the target segment to read its property."); Debug.Assert(epmValueCache != null, "epmValueCache != null"); Debug.Assert(resourceType != null, "resourceType != null"); bool nullOnParentProperty; object propertyValue = targetSegment.EpmInfo.ReadEntryPropertyValue(epmValueCache, resourceType, metadata, out nullOnParentProperty); return(EpmWriterUtils.GetPropertyValueAsText(propertyValue)); }
/// <summary> /// Constructor. /// </summary> /// <param name="version">The version of the OData protocol to use.</param> /// <param name="baseUri">The Base URI to use for all the URIs being written.</param> /// <param name="writingResponse">True if the writer is to write a response payload; false if it's to write a request payload.</param> /// <param name="metadataProvider">The metadata provider to use.</param> /// <param name="writingFeed">True if the writer is created for writing a feed; false when it is created for writing an entry.</param> /// <param name="synchronous">True if the writer is created for synchronous operation; false for asynchronous.</param> protected ODataWriterCore( ODataVersion version, Uri baseUri, bool writingResponse, DataServiceMetadataProviderWrapper metadataProvider, bool writingFeed, bool synchronous) { DebugUtils.CheckNoExternalCallers(); Debug.Assert(baseUri == null || baseUri.IsAbsoluteUri, "baseUri must be either null or absolute"); this.version = version; this.baseUri = baseUri; this.writingResponse = writingResponse; this.metadataProvider = metadataProvider; this.writingFeed = writingFeed; this.synchronous = synchronous; this.scopes.Push(Scope.Create(WriterState.Start, null)); }
/// <summary> /// Validates that <paramref name="typeName"/> is a valid type name of the specified kind (<paramref name="typeKind"/>). /// </summary> /// <param name="metadata">The metadata against which to validate the type name.</param> /// <param name="typeName">The type name to validate.</param> /// <param name="typeKind">The expected <see cref="ResourceTypeKind"/> of the type.</param> /// <returns>A <see cref="ResourceType"/> for the <paramref name="typeName"/>.</returns> internal static ResourceType ValidateNonMultiValueTypeName(DataServiceMetadataProviderWrapper metadata, string typeName, ResourceTypeKind typeKind) { DebugUtils.CheckNoExternalCallers(); Debug.Assert(metadata != null, "metadata != null"); ResourceType resourceType = metadata.TryResolveResourceType(typeName); if (resourceType == null) { throw new ODataException(Strings.ODataWriterCore_UnrecognizedTypeName(typeName, typeKind)); } if (resourceType.ResourceTypeKind != typeKind) { throw new ODataException(Strings.ODataWriterCore_IncorrectTypeKind(typeName, typeKind.ToString(), resourceType.ResourceTypeKind.ToString())); } return(resourceType); }
/// <summary> /// Writes a service document in JSON format. /// </summary> /// <param name="jsonWriter">The <see cref="JsonWriter"/> to write to.</param> /// <param name="metadata">The metadata provider to use or null if no metadata is available.</param> /// <param name="defaultWorkspace">The default workspace to write in the service document.</param> internal static void WriteServiceDocument( JsonWriter jsonWriter, DataServiceMetadataProviderWrapper metadata, ODataWorkspace defaultWorkspace) { DebugUtils.CheckNoExternalCallers(); Debug.Assert(jsonWriter != null, "jsonWriter != null"); Debug.Assert(defaultWorkspace != null, "defaultWorkspace != null"); IEnumerable <ODataResourceCollectionInfo> collections = ValidationUtils.ValidateWorkspace(metadata == null ? null : metadata.ResourceSets, defaultWorkspace); Debug.Assert(collections != null, "collections != null"); WriteDataWrapper( jsonWriter, true, () => { // "{" jsonWriter.StartObjectScope(); // "EntitySets": jsonWriter.WriteName(JsonConstants.ODataServiceDocumentEntitySetsName); // "[" jsonWriter.StartArrayScope(); foreach (ODataResourceCollectionInfo collectionInfo in collections) { ValidationUtils.ValidateResourceCollectionInfo(collectionInfo); // <collection name> jsonWriter.WriteValue(collectionInfo.Name); } // "]" jsonWriter.EndArrayScope(); // "}" jsonWriter.EndObjectScope(); }); }
public void ServiceOperationsMustBeReadOnlyTest() { var serviceOperationEntryPoints = new Action <DataServiceMetadataProviderWrapper, string>[] { (wrapper, name) => wrapper.TryResolveServiceOperation(name), }; AstoriaTestNS.TestUtil.RunCombinations( serviceOperationEntryPoints, (serviceOperationEntryPoint) => { var metadataProvider = new DSPMetadata("Test", "TestNS"); var serviceOperation = metadataProvider.AddServiceOperation("ServiceOperation", ServiceOperationResultKind.DirectValue, ResourceType.GetPrimitiveResourceType(typeof(string)), null, "GET", new ServiceOperationParameter[0]); var wrapper = new DataServiceMetadataProviderWrapper(metadataProvider); ExceptionUtils.ExpectedException <DataServiceException>( () => serviceOperationEntryPoint(wrapper, serviceOperation.Name), "The service operation '" + serviceOperation.Name + "' returned by the provider is not read-only. Please make sure that all the service operations are set to read-only.", "Accessing writable service operation should fail."); }); }
/// <summary> /// Writes out the value of a complex property. /// </summary> /// <param name="jsonWriter">The <see cref="JsonWriter"/> to write to.</param> /// <param name="metadata">The metadata provider to use or null if no metadata is available.</param> /// <param name="complexValue">The complex value to write.</param> /// <param name="resourcePropertyType">The metadata type for the complex value.</param> /// <param name="isOpenPropertyType">True if the type name belongs to an open property.</param> /// <param name="version">The protocol version used for writing.</param> internal static void WriteComplexValue( JsonWriter jsonWriter, DataServiceMetadataProviderWrapper metadata, ODataComplexValue complexValue, ResourceType resourcePropertyType, bool isOpenPropertyType, ODataVersion version) { DebugUtils.CheckNoExternalCallers(); Debug.Assert(complexValue != null, "complexValue != null"); // Start the object scope which will represent the entire complex instance jsonWriter.StartObjectScope(); // Write the "__metadata" : { "type": "typename" } // But only if we actually have a typename to write, otherwise we need the __metadata to be omitted entirely string typeName = complexValue.TypeName; // resolve the type name to the resource type; if no type name is specified we will use the // type inferred from metadata ResourceType complexValueType = MetadataUtils.ResolveTypeName(metadata, resourcePropertyType, ref typeName, ResourceTypeKind.ComplexType, isOpenPropertyType); if (typeName != null) { // Write the __metadata object jsonWriter.WriteName(JsonConstants.ODataMetadataName); jsonWriter.StartObjectScope(); // "type": "typename" jsonWriter.WriteName(JsonConstants.ODataMetadataTypeName); jsonWriter.WriteValue(typeName); // End the __metadata jsonWriter.EndObjectScope(); } // Write the properties of the complex value as usual WriteProperties(jsonWriter, metadata, complexValueType, complexValue.Properties, version); // End the object scope which represents the complex instance jsonWriter.EndObjectScope(); }
public void TypesMustBeReadOnlyTest() { var typesToTest = new Func <DSPMetadata, ResourceType>[] { (metadata) => metadata.AddEntityType("EntityType", null, null, false), (metadata) => metadata.AddComplexType("ComplexType", null, null, false) }; AstoriaTestNS.TestUtil.RunCombinations( typesToTest, (typeCreate) => { var metadataProvider = new DSPMetadata("Test", "TestNS"); var type = typeCreate(metadataProvider); var wrapper = new DataServiceMetadataProviderWrapper(metadataProvider); ExceptionUtils.ExpectedException <DataServiceException>( () => wrapper.TryResolveResourceType(type.FullName), "The resource type '" + type.FullName + "' returned by the provider is not read-only. Please make sure that all the types are set to read-only.", "Resolving resource type to writable type should fail."); }); }
/// <summary> /// Writes property names and value pairs. /// </summary> /// <param name="jsonWriter">The <see cref="JsonWriter"/> to write to.</param> /// <param name="metadata">The metadata provider to use or null if no metadata is available.</param> /// <param name="owningType">The <see cref="ResourceType"/> of the entry (or null if not metadata is available).</param> /// <param name="properties">The enumeration of properties to write out.</param> /// <param name="version">The protocol version used for writing.</param> internal static void WriteProperties( JsonWriter jsonWriter, DataServiceMetadataProviderWrapper metadata, ResourceType owningType, IEnumerable <ODataProperty> properties, ODataVersion version) { DebugUtils.CheckNoExternalCallers(); Debug.Assert(jsonWriter != null, "jsonWriter != null"); if (properties == null) { return; } foreach (ODataProperty property in properties) { WriteProperty(jsonWriter, metadata, property, owningType, version); } }
public void TypesMustBeReadOnlyTest() { var typesToTest = new Func<DSPMetadata, ResourceType>[] { (metadata) => metadata.AddEntityType("EntityType", null, null, false), (metadata) => metadata.AddComplexType("ComplexType", null, null, false) }; AstoriaTestNS.TestUtil.RunCombinations( typesToTest, (typeCreate) => { var metadataProvider = new DSPMetadata("Test", "TestNS"); var type = typeCreate(metadataProvider); var wrapper = new DataServiceMetadataProviderWrapper(metadataProvider); ExceptionUtils.ExpectedException<DataServiceException>( () => wrapper.TryResolveResourceType(type.FullName), "The resource type '" + type.FullName + "' returned by the provider is not read-only. Please make sure that all the types are set to read-only.", "Resolving resource type to writable type should fail."); }); }
/// <summary> /// Reads a property value starting on an entry. /// </summary> /// <param name="epmValueCache">The EPM value cache for the entry to read from.</param> /// <param name="resourceType">The resource type of the entry.</param> /// <param name="metadata">The metadata provider to use.</param> /// <param name="nullOnParentProperty">true if the value of the property is null because one of its parent properties was null, in this case /// the return value of the method is always null. false if the value of the property is the actual property value which may or may not be null.</param> /// <returns>The value of the property (may be null), or null if the property itself was not found due to one of its parent properties being null.</returns> internal object ReadEntryPropertyValue( EntryPropertiesValueCache epmValueCache, ResourceType resourceType, DataServiceMetadataProviderWrapper metadata, out bool nullOnParentProperty) { DebugUtils.CheckNoExternalCallers(); Debug.Assert(this.propertyValuePath != null, "The propertyValuePath should have been initialized by now."); Debug.Assert(this.propertyValuePath.Length > 0, "The propertyValuePath must not be empty for an entry property."); Debug.Assert(resourceType != null, "resourceType != null"); // TODO - verify that we actually need the internal property PropertyValuePath // TODO - It might be possible to avoid the "value" type checks below if we do property value validation based on the resource type return(this.ReadPropertyValue( epmValueCache.EntryProperties, 0, resourceType, metadata, epmValueCache, out nullOnParentProperty)); }
public void SetsMustBeReadOnlyTest() { var resourceSetEntryPoints = new Action <DataServiceMetadataProviderWrapper, string>[] { (wrapper, name) => wrapper.TryResolveResourceSet(name), (wrapper, name) => wrapper.ResourceSets.Cast <object>().Count() }; AstoriaTestNS.TestUtil.RunCombinations( resourceSetEntryPoints, (resourceSetEntryPoint) => { var metadataProvider = new DSPMetadata("Test", "TestNS"); var resourceType = metadataProvider.AddEntityType("EntityType", null, null, false); var resourceSet = metadataProvider.AddResourceSet("Entities", resourceType); resourceType.SetReadOnly(); var wrapper = new DataServiceMetadataProviderWrapper(metadataProvider); ExceptionUtils.ExpectedException <DataServiceException>( () => resourceSetEntryPoint(wrapper, resourceSet.Name), "The resource set '" + resourceSet.Name + "' returned by the provider is not read-only. Please make sure that all the resource sets are set to read-only.", "Accessing writable resource set should fail."); }); }
public void SetsMustBeReadOnlyTest() { var resourceSetEntryPoints = new Action<DataServiceMetadataProviderWrapper, string>[] { (wrapper, name) => wrapper.TryResolveResourceSet(name), (wrapper, name) => wrapper.ResourceSets.Cast<object>().Count() }; AstoriaTestNS.TestUtil.RunCombinations( resourceSetEntryPoints, (resourceSetEntryPoint) => { var metadataProvider = new DSPMetadata("Test", "TestNS"); var resourceType = metadataProvider.AddEntityType("EntityType", null, null, false); var resourceSet = metadataProvider.AddResourceSet("Entities", resourceType); resourceType.SetReadOnly(); var wrapper = new DataServiceMetadataProviderWrapper(metadataProvider); ExceptionUtils.ExpectedException<DataServiceException>( () => resourceSetEntryPoint(wrapper, resourceSet.Name), "The resource set '" + resourceSet.Name + "' returned by the provider is not read-only. Please make sure that all the resource sets are set to read-only.", "Accessing writable resource set should fail."); }); }
public void ServiceOperationsMustBeReadOnlyTest() { var serviceOperationEntryPoints = new Action<DataServiceMetadataProviderWrapper, string>[] { (wrapper, name) => wrapper.TryResolveServiceOperation(name), }; AstoriaTestNS.TestUtil.RunCombinations( serviceOperationEntryPoints, (serviceOperationEntryPoint) => { var metadataProvider = new DSPMetadata("Test", "TestNS"); var serviceOperation = metadataProvider.AddServiceOperation("ServiceOperation", ServiceOperationResultKind.DirectValue, ResourceType.GetPrimitiveResourceType(typeof(string)), null, "GET", new ServiceOperationParameter[0]); var wrapper = new DataServiceMetadataProviderWrapper(metadataProvider); ExceptionUtils.ExpectedException<DataServiceException>( () => serviceOperationEntryPoint(wrapper, serviceOperation.Name), "The service operation '" + serviceOperation.Name + "' returned by the provider is not read-only. Please make sure that all the service operations are set to read-only.", "Accessing writable service operation should fail."); }); }
public void AskJustOncesForTypeTest() { var typesToTest = new Func<DSPMetadata, ResourceType>[] { (metadata) => { var t = metadata.AddEntityType("EntityType", null, null, false); metadata.AddKeyProperty(t, "ID", typeof(int)); return t; }, (metadata) => metadata.AddComplexType("ComplexType", null, null, false) }; AstoriaTestNS.TestUtil.RunCombinations( typesToTest, (typeCreate) => { var metadataProvider = new CallCountMetadataProvider("Test", "TestNS"); var type = typeCreate(metadataProvider); metadataProvider.SetReadOnly(); var wrapper = new DataServiceMetadataProviderWrapper(metadataProvider); var resourceType = wrapper.TryResolveResourceType(type.FullName); Assert.AreSame(type, resourceType, "The returned type is not the one expected."); wrapper.TryResolveResourceType(type.FullName); wrapper.TryResolveResourceType(type.FullName); Assert.AreEqual(1, metadataProvider.TryResolveResourceTypeCallCount, "The underlying metadata provider should be called just once."); }); }
public void ResourceSetNamesMustBeUniqueTest() { var metadataProvider = new ResourceSetsOverrideMetadataProvider("Test", "TestNS"); metadataProvider.ResourceSetValues = new List<ResourceSet>(); var type = metadataProvider.AddEntityType("EntityType", null, null, false); metadataProvider.ResourceSetValues.Add(new ResourceSet("Entities", type)); metadataProvider.ResourceSetValues.Add(new ResourceSet("Entities", type)); var wrapper = new DataServiceMetadataProviderWrapper(metadataProvider); ExceptionUtils.ExpectedException<DataServiceException>( () => wrapper.ResourceSets.Cast<object>().Count(), "The resource set 'Entities' returned by the provider is not read-only. Please make sure that all the resource sets are set to read-only.", "Two resource sets with the same name should fail in the wrapper."); }