private void ApplyMetadataToResource(IHalResource resource, ResourceContext resourceContext) { resourceContext.Resource = resource; resourceContext.MediaModule.ApplyResourceMeta(InternetMediaTypes.HalJson, resourceContext); // Add metadata to each embedded resource if present. if (resource.Embedded == null) { return; } foreach (IResource embeddedResource in resource.Embedded.Values) { // Check if the embedded resource is a collection of resources and if so // apply the metadata to each contained resource. if (embeddedResource is IEnumerable resourceColl) { foreach (IHalResource childResource in resourceColl.OfType <IHalResource>()) { ApplyMetadataToResource(childResource, resourceContext); } return; } // Add metadata to single embedded resource. if (embeddedResource is IHalResource halResource) { ApplyMetadataToResource(halResource, resourceContext); } } }
/// <summary> /// Creates an instance of <see cref="HalEmbeddedResourceBuilder"/>. /// </summary> /// <param name="resource">The target resource.</param> public HalEmbeddedResourceBuilder(IHalResource resource) { if (resource == null) { throw new ArgumentNullException("resource"); } _resource = resource; _linkCollection = new HalLinkCollection(); }
/// <summary> /// Adds a link to the HAL resource. /// </summary> /// <param name="resource">The resource to which the link is added.</param> /// <param name="target">The resource that the link represents.</param> /// <param name="property">The property information of the target resource.</param> /// <param name="url">The URL helper used to create an HREF.</param> protected virtual void AddLink(IHalResource resource, IHalResource target, PropertyInfo property, UrlHelper url) { if (resource == null || url == null || target == null) { return; } try { var controller = GetControllerName(target); if (string.IsNullOrEmpty(controller)) { Throw.NotSupported("Generating a HAL URI without a controller name is not supported."); } var key = GetKey(target, resource).ToString(); string href = url.Link(this.RouteName, controller, key); AddLink(resource, href, object.ReferenceEquals(resource, target) ? "self" : property.Name); } catch (NotSupportedException ex) { if (ex.Message == "The resource type does not have a KeyAttribute defined.") { return; } } }
/// <summary> /// Recursively serializes the HAL resource to the output stream. /// </summary> /// <param name="writer">The <see cref="T:System.Xml.XmlWriter" /> to write to.</param> /// <param name="resource">The resource being written.</param> protected virtual void SerializeInnerResource(XmlWriter writer, IHalResource resource, string rel = "") { if (resource == null) { return; } // Open the node OpenResourceElement(writer, resource, rel); // Write the links collection into the output stream WriteLinks(writer, resource); // Write each non-HAL property (i.e. Resource State) WriteNonHalProperties(writer, resource); // Write each HAL property WriteHalProperties(writer, resource); if (typeof(IHalResourceCollection).IsAssignableFrom(resource.GetType())) { foreach (IHalResource innerResource in (IHalResourceCollection)resource) { SerializeInnerResource(writer, innerResource); } } // Close out the node CloseResourceElement(writer); }
public static Uri GetLink(this IHalResource resource, string key, string tokenKey, object tokenValue) { if (resource == null) { throw new MissingLinkException(key, (Type)null); } return(resource.Links.GetLink(key, tokenKey, tokenValue)); }
/// <summary> /// Opens the resource element node. /// </summary> /// <param name="writer">The <see cref="T:System.Xml.XmlWriter" /> to write to.</param> /// <param name="value">The value.</param> protected virtual void OpenResourceElement(XmlWriter writer, IHalResource value, string rel = "") { writer.WriteStartElement("resource"); if (value != null) { writer.WriteAttributeString("rel", !string.IsNullOrEmpty(rel) ? rel : value.Rel); writer.WriteAttributeString("href", value.HRef); } }
public static Uri GetSelf(this IHalResource resource) { if (resource == null) { throw new MissingLinkException(HalResource.LinkKeySelf, (Type)null); } return(resource.Links.GetSelf()); }
public static bool HasLink(this IHalResource resource, string key) { if (resource == null || string.IsNullOrWhiteSpace(key)) { return(false); } return(resource.Links.HasLink(key)); }
/// <summary> /// Embeds a resource within parent resource. /// </summary> /// <param name="embeddedResource">The resource to embed.</param> /// <param name="named">Optional name used to identity the embedded resource.</param> public void Embed(IHalResource embeddedResource, string named = null) { if (embeddedResource == null) { throw new ArgumentNullException(nameof(embeddedResource), "Resource to embedded not specified."); } EmbedResource(embeddedResource, named); }
public static Uri GetLink(this IHalResource resource, string key, IDictionary <string, object> tokens) { if (resource == null) { throw new MissingLinkException(key, (Type)null); } return(resource.Links.GetLink(key, tokens)); }
public static Uri GetLink(this IHalResource resource, string key) { if (resource == null) { throw new MissingLinkException(key, (Type)null); } return(resource.Links.GetLink(key)); }
/// <summary> /// Adds a link to a resource that points to a collection. /// </summary> /// <param name="resource">The resource to which the link is added.</param> /// <param name="collection">The collection that the link targets.</param> /// <param name="parent">The parent resource containing the key attribute that links the <paramref name="resource" /> to the <paramref name="collection" />.</param> /// <param name="property">The property information of the target resource.</param> /// <param name="url">The URL helper used to create an HREF.</param> /// <remarks> /// There are scenarios that define what the relation is between the <paramref name="resource" /> and the <paramref name="collection" />: /// <list type="bullet"> /// <item><strong>The resource and the collection reference the same object:</strong> the relation is <em>self</em></item> /// <item><strong>The resource and the collection refer to different objects:</strong> the relation is the property name of the collection</item> /// </list> /// </remarks> protected virtual void AddCollectionLink(IHalResource resource, IHalResourceCollection collection, IHalResource parent, PropertyInfo property, UrlHelper url) { if (resource == null || collection == null || parent == null || property == null || url == null) { return; } string key = GetKey(resource, parent).ToString(); string rel = object.ReferenceEquals(resource, collection) ? "self" : property.Name; string filter = string.Empty; var keys = parent.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance) .Where(x => x.GetCustomAttributes(typeof(KeyAttribute), true).Length > 0); PropertyInfo keyInfo = null; if (keys.Count() > 1) // multiple key properties { // sort them by [DataMember(Order)] and take the first one var keysWithDataMember = keys.Where(x => x.GetCustomAttributes(typeof(DataMemberAttribute), true).Length > 0); keyInfo = keysWithDataMember.OrderBy(x => ((DataMemberAttribute)x.GetCustomAttributes(typeof(DataMemberAttribute), true).First()) .Order).First(); } else { keyInfo = keys.First(); } var navigationKey = GetNavigationProperty(collection, parent); if (navigationKey == null) { return; } string controller = GetControllerName(collection); if (typeof(IEnumerable).IsAssignableFrom(navigationKey.PropertyType)) { filter = string.Format("{0}/any({1}: {1}/{2} eq {3})", navigationKey.Name, parent.GetType().Name, keyInfo.Name, (keyInfo.PropertyType == typeof(string)) ? string.Format("'{0}'", key) : key); } else { filter = string.Format("{0}/{1} eq {2}", navigationKey.Name, keyInfo.Name, (keyInfo.PropertyType == typeof(string)) ? string.Format("'{0}'", key) : key); } string href = System.Web.HttpUtility.UrlDecode(url.Link(this.RouteName, new Dictionary <string, object> { { "controller", controller }, { "$filter", filter }, { "mainargument", null } })); AddLink(resource, href, rel); }
/// <summary> /// Adds the specified resource to the collection. /// </summary> /// <param name="resource">The HAL resource.</param> /// <exception cref="T:System.NotSupportedException"> /// The 'Add' method requires a type derived from Geocrest.Model.Resource. /// </exception> public void Add(IHalResource resource) { if (resource.GetType().IsSubclassOf(typeof(Resource))) { this.resources.Add((T)resource); } else { throw new NotSupportedException("The 'Add' method requires a type derived from Geocrest.Model.Resource."); } }
public Task <IHalDeleteResult> Delete(IHalResource resource, IHalPersisterStrategy strategy = null) { strategy = strategy ?? GetDefaultPersisterStrategy(resource); if (strategy == null) { throw new HalPersisterException("No persister found for resource: " + resource); } return(Task <IHalDeleteResult> .Factory .StartNew(() => strategy.Delete(resource))); }
/// <summary> /// Write the HAL links collection into the output stream /// </summary> /// <param name="writer">The <see cref="T:System.Xml.XmlWriter" /> to write to.</param> /// <param name="value">The value.</param> protected virtual void WriteLinks(XmlWriter writer, IHalResource value) { Throw.IfArgumentNull(writer, "writer"); if (value != null && value.Links != null) { foreach (var link in value.Links.Where(x => !(x is SelfLink))) { writer.WriteStartElement("link"); writer.WriteAttributeString("rel", link.Rel); writer.WriteAttributeString("href", link.HRef); writer.WriteEndElement(); } } }
public IHalDeleteResult Delete(IHalResource resource) { var link = resource.Links.FirstOrDefault(l => l.Rel == "self"); if (link == null) { throw new HalPersisterException("No link found for deleting: " + resource); } var result = HttpClient.DeleteAsync(link.Href).Result; return(new HalDeleteResult { Success = result.IsSuccessStatusCode }); }
/// <summary> /// Writes each property of the type <see cref="T:Geocrest.Model.IHalResource"/> or /// <see cref="T:Geocrest.Model.IHalResourceCollection"/> into the output stream /// </summary> /// <param name="writer">The <see cref="T:System.Xml.XmlWriter" /> to write to.</param> /// <param name="value">The value.</param> protected virtual void WriteHalProperties(XmlWriter writer, IHalResource value) { Throw.IfArgumentNull(writer, "writer"); Throw.IfArgumentNull(value, "value"); // add all HalResources to a dictionary for embedding. var properties = value.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance) .Where(x => typeof(IHalResource).IsAssignableFrom(x.PropertyType) && x.GetIndexParameters() .Length == 0); foreach (var property in properties) { var item = property.GetValue(value, null); SerializeInnerResource(writer, (IHalResource)item, GetDataMemberName(property)); } }
/// <summary> /// Gets the name of the controller first by configuration then by convention. /// </summary> /// <param name="resource">The resource.</param> /// <returns></returns> private static string GetControllerName(IHalResource resource) { var type = resource.GetType(); if (typeof(IHalResourceCollection).IsAssignableFrom(type)) { type = type.GetGenericArguments()[0]; } string controllerName; if (!configMappings.TryGetValue(type, out controllerName)) { var p = PluralizationService.CreateService(new System.Globalization.CultureInfo("en-US")); controllerName = p.Pluralize(type.Name).ToLower(); } return(controllerName); }
/// <summary> /// Write the HAL links collection into the reserved <i>_links</i> JSON property /// http://tools.ietf.org/html/draft-kelly-json-hal-06#section-4.1.1 /// </summary> /// <param name="writer">The <see cref="T:Newtonsoft.Json.JsonWriter" /> to write to.</param> /// <param name="value">The value.</param> /// <param name="serializer">The calling serializer.</param> protected virtual void WriteLinks(JsonWriter writer, IHalResource value, JsonSerializer serializer) { Throw.IfArgumentNull(writer, "writer"); Throw.IfArgumentNull(serializer, "serializer"); var linksConverter = serializer.Converters.FirstOrDefault(x => x.CanConvert(typeof(IList <Link>))); if (linksConverter != null) { writer.WritePropertyName("_links"); writer.WriteStartArray(); if (value != null) { linksConverter.WriteJson(writer, value.Links, serializer); } writer.WriteEndArray(); } }
/// <summary> /// Enriches the entity with HAL links by recursively enriching any property /// that inherits from <see cref="T:Geocrest.Model.IHalResource"/> /// </summary> /// <param name="resource">The resource.</param> /// <param name="url">The URL.</param> protected virtual void EnrichEntity(IHalResource resource, UrlHelper url) { if (resource == null) { return; } var properties = resource.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance) .Where(x => typeof(IHalResource).IsAssignableFrom(x.PropertyType)); // add a self link to the resource AddLink(resource, resource, null, url); foreach (PropertyInfo property in properties) { var propValue = property.GetValue(resource, null); var propType = property.PropertyType; if (propValue == null) { continue; } if (typeof(IHalResourceCollection).IsAssignableFrom(propType)) { var child = (IHalResourceCollection)propValue; // add a self link to the collection (will use oData to create the link) AddCollectionLink(child, child, resource, property, url); // add link to parent AddCollectionLink(resource, child, resource, property, url); foreach (IHalResource item in (IHalResourceCollection)propValue) { // continue recursive enriching EnrichEntity(item, url); } } else { // add a link to the parent resource to the child resource AddLink(resource, (IHalResource)propValue, property, url); // continue recursive enriching EnrichEntity((IHalResource)propValue, url); } } }
/// <summary> /// Adds a link to the HAL resource. /// </summary> /// <param name="resource">The resource to which the link is added.</param> /// <param name="href">The HREF to the target resource.</param> /// <param name="rel">The relation of the target resource.</param> protected virtual void AddLink(IHalResource resource, string href, string rel) { if (resource == null) { return; } if (string.IsNullOrEmpty(href)) { Throw.NotSupported("Links must have a valid HREF."); } if (string.IsNullOrEmpty(rel)) { Throw.NotSupported("Links must have a relation defined."); } resource.AddLink(rel.ToLower() == "self" ? new SelfLink(href) : new Link(rel, href)); }
private static PropertyInfo GetNavigationProperty(IHalResource primary, IHalResource foreign) { Type type = null; if (typeof(IHalResourceCollection).IsAssignableFrom(primary.GetType())) { type = primary.GetType().GetGenericArguments()[0]; } else { type = primary.GetType(); } var property = type.GetProperties(BindingFlags.Public | BindingFlags.Instance) .SingleOrDefault(x => (typeof(IHalResourceCollection).IsAssignableFrom(x.PropertyType) && x.PropertyType.GetGenericArguments()[0] == foreign.GetType()) || x.PropertyType == foreign.GetType()); return(property); }
/// <summary> /// Returns the value of the property decorated with a /// <see cref="T:System.ComponentModel.DataAnnotations.KeyAttribute" />. /// </summary> /// <param name="resource">A HAL resource containing a key attribute.</param> /// <param name="parent">The parent resource if <paramref name="resource"/> is a HAL collection.</param> /// <returns> /// Returns an object containing the value of the key. /// </returns> private static object GetKey(IHalResource resource, IHalResource parent) { Type type = null; PropertyInfo keyProp = null; IHalResource keyHolder = null; if (typeof(IHalResourceCollection).IsAssignableFrom(resource.GetType())) // if collection then get key from parent { type = parent.GetType(); keyHolder = parent; } else // get key from resource itself { type = resource.GetType(); keyHolder = resource; } var properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance); var keys = properties.Where(x => x.GetCustomAttributes(typeof(KeyAttribute), true).Length > 0); if (keys.Count() > 1) // multiple key properties { // sort them by [DataMember(Order)] and take the first one var keysWithDataMember = keys.Where(x => x.GetCustomAttributes(typeof(DataMemberAttribute), true).Length > 0); keyProp = keysWithDataMember.OrderBy(x => ((DataMemberAttribute)x.GetCustomAttributes(typeof(DataMemberAttribute), true).First()) .Order).FirstOrDefault(); } else { keyProp = keys.FirstOrDefault(); } if (keyProp == null) { Throw.NotSupported("The resource type does not have a KeyAttribute defined."); } var key = keyProp.GetGetMethod().Invoke(keyHolder, null); if (key == null) { Throw.NotSupported(string.Format("Unable to retrieve the key for resource type '{0}'.", type.FullName)); } return(key); }
/// <summary> /// Called during serialization to write an object of the specified type to the specified writeStream. /// </summary> /// <param name="type">The type of object to write.</param> /// <param name="value">The object to write.</param> /// <param name="writeStream">The <see cref="T:System.IO.Stream" /> to which to write.</param> /// <param name="content">The <see cref="T:System.Net.Http.HttpContent" /> for the content being written.</param> /// <param name="transportContext">The <see cref="T:System.Net.TransportContext" />.</param> /// <returns> /// A <see cref="T:System.Threading.Tasks.Task" /> that will write the value to the stream. /// </returns> public override System.Threading.Tasks.Task WriteToStreamAsync(System.Type type, object value, System.IO.Stream writeStream, System.Net.Http.HttpContent content, System.Net.TransportContext transportContext) { return(Task.Factory.StartNew(() => { var settings = new XmlWriterSettings(); settings.Indent = false; settings.OmitXmlDeclaration = false; var writer = XmlWriter.Create(writeStream, settings); IHalResource resource = null; SerializeInnerResource(writer, (IHalResource)value); writer.Flush(); writer.Close(); return resource; })); }
/// <summary> /// Writes the properties that are not HAL properties (i.e. are not of the type /// <see cref="T:Geocrest.Model.IHalResource"/> or /// <see cref="T:Geocrest.Model.IHalResourceCollection"/>) /// to the output stream. This is the resource state. /// </summary> /// <param name="writer">The <see cref="T:System.Xml.XmlWriter" /> to write to.</param> /// <param name="value">The value.</param> protected virtual void WriteNonHalProperties(XmlWriter writer, IHalResource value) { Throw.IfArgumentNull(writer, "writer"); Throw.IfArgumentNull(value, "value"); // exclude HalResource types and IList<Link> type var properties = value.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance) .Where(x => !typeof(IHalResource).IsAssignableFrom(x.PropertyType) && !typeof(IList <Link>) .IsAssignableFrom(x.PropertyType)); // serialize each property and it's value foreach (var property in properties) { var propertyValue = property.GetValue(value, null); // class members must opt in to be included in the ouput serialization by having a // [DataMember] on the property var dataMember = property.GetCustomAttributes(typeof(DataMemberAttribute), false).Length == 0 ? false : true; // classes can exclude serialization of properties that contain default // values by adding [DataMember(EmitDefaultValue = false)] var emitDefault = property.GetCustomAttributes(typeof(DataMemberAttribute), false).Length == 0 ? true : // <-default behavior ((DataMemberAttribute)property.GetCustomAttributes(typeof(DataMemberAttribute), false) .Single()).EmitDefaultValue; // classes can also exclude serialization of properties regardless of value // by adding [IgnoreDataMember] to the property var ignore = property.GetCustomAttributes(typeof(IgnoreDataMemberAttribute), false).Length == 0 ? false : true; // and serialize if (dataMember && !ignore && (emitDefault == true || !IsDefault(propertyValue))) { var dataContract = (DataContractAttribute)property.PropertyType.GetCustomAttributes(typeof(DataContractAttribute), false).SingleOrDefault(); var ns = dataContract != null ? dataContract.Namespace : string.Empty; var serializer = new DataContractSerializer(property.PropertyType, GetDataMemberName(property), ns); serializer.WriteObject(writer, propertyValue); } } }
/// <summary> /// Writes each property of the type <see cref="T:Geocrest.Model.IHalResource"/> or /// <see cref="T:Geocrest.Model.IHalResourceCollection"/> into the reserved /// <i>_embedded</i> JSON property /// http://tools.ietf.org/html/draft-kelly-json-hal-06#section-4.1.2 /// </summary> /// <param name="writer">The <see cref="T:Newtonsoft.Json.JsonWriter" /> to write to.</param> /// <param name="value">The value.</param> /// <param name="serializer">The calling serializer.</param> protected virtual void WriteHalProperties(JsonWriter writer, IHalResource value, JsonSerializer serializer) { Throw.IfArgumentNull(writer, "writer"); Throw.IfArgumentNull(value, "value"); Throw.IfArgumentNull(serializer, "serializer"); writer.WritePropertyName("_embedded"); writer.WriteStartObject(); // add all HalResources to a dictionary for embedding. var properties = value.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance) .Where(x => typeof(IHalResource).IsAssignableFrom(x.PropertyType)); foreach (var property in properties) { writer.WritePropertyName(GetDataMemberName(property)); var item = property.GetValue(value, null); serializer.Serialize(writer, item); } writer.WriteEndObject(); }
/// <summary> /// Writes the properties that are not HAL properties (i.e. are not of the type /// <see cref="T:Geocrest.Model.IHalResource"/> or /// <see cref="T:Geocrest.Model.IHalResourceCollection"/>) /// to the output JSON. This is the resource state. /// </summary> /// <param name="writer">The <see cref="T:Newtonsoft.Json.JsonWriter" /> to write to.</param> /// <param name="value">The value.</param> /// <param name="serializer">The calling serializer.</param> protected virtual void WriteNonHalProperties(JsonWriter writer, IHalResource value, JsonSerializer serializer) { Throw.IfArgumentNull(writer, "writer"); Throw.IfArgumentNull(value, "value"); Throw.IfArgumentNull(serializer, "serializer"); // exclude HalResource types and IList<Link> type var properties = value.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance) .Where(x => !typeof(IHalResource).IsAssignableFrom(x.PropertyType) && !typeof(IList <Link>) .IsAssignableFrom(x.PropertyType)); // serialize each property and it's value foreach (var property in properties) { var propertyValue = property.GetValue(value, null); // class members must opt in to be included in the ouput serialization by having a // [DataMember] on the property var dataMember = property.GetCustomAttributes(typeof(DataMemberAttribute), false).Length == 0 ? false : true; // classes can exclude serialization of properties that contain null/default // values by adding [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] var emitDefault = property.GetCustomAttributes(typeof(JsonPropertyAttribute), false).Length == 0 ? NullValueHandling.Include : // <-default behavior ((JsonPropertyAttribute)property.GetCustomAttributes(typeof(JsonPropertyAttribute), false) .Single()).NullValueHandling; // classes can also exclude serialization of properties regardless of value // by adding [JsonIgnore] to the property var ignore = property.GetCustomAttributes(typeof(JsonIgnoreAttribute), false).Length == 0 ? false : true; // and serialize if (dataMember && !ignore && emitDefault == NullValueHandling.Include) { writer.WritePropertyName(GetDataMemberName(property)); serializer.Serialize(writer, propertyValue); } } }
/// <summary> /// Recursively serializes the HAL resource to the output JSON. /// </summary> /// <param name="writer">The <see cref="T:Newtonsoft.Json.JsonWriter" /> to write to.</param> /// <param name="resource">The resource being written.</param> /// <param name="serializer">The calling serializer.</param> protected virtual void SerializeInnerResource(JsonWriter writer, IHalResource resource, JsonSerializer serializer) { if (resource == null) { return; } // Open the object writer.WriteStartObject(); // Write the links collection into the reserved _links JSON property WriteLinks(writer, resource, serializer); // Write each non-HAL property (i.e. Resource State) WriteNonHalProperties(writer, resource, serializer); // Write each HAL property into the reserved _embedded JSON property // http://tools.ietf.org/html/draft-kelly-json-hal-06#section-4.1.2 WriteHalProperties(writer, resource, serializer); // End the object writer.WriteEndObject(); }
/// <summary> /// Creates an instance of <see cref="HalDocument"/>. /// </summary> /// <param name="resource">The hypermedia aware resource.</param> /// <param name="linkCollection">A collection of hypermedia links.</param> /// <param name="embeddedResourceCollection">A collection of embedded resources.</param> internal HalDocument(IHalResource resource, HalLinkCollection linkCollection, HalEmbeddedResourceCollection embeddedResourceCollection) : this(resource, linkCollection) { _embeddedResourceCollection = embeddedResourceCollection; }
/// <summary> /// Creates an instance of <see cref="HalDocument"/>. /// </summary> /// <param name="resource">The hypermedia aware resource.</param> /// <param name="linkCollection">A collection of hypermedia links.</param> internal HalDocument(IHalResource resource, HalLinkCollection linkCollection) { _resource = resource; _linkCollection = linkCollection; }