diff --git a/FoundationV3/FiftyOne.Foundation 4.csproj b/FoundationV3/FiftyOne.Foundation 4.csproj index 8c96669..2796770 100644 --- a/FoundationV3/FiftyOne.Foundation 4.csproj +++ b/FoundationV3/FiftyOne.Foundation 4.csproj @@ -183,6 +183,7 @@ + diff --git a/FoundationV3/Mobile/Detection/DataSet.cs b/FoundationV3/Mobile/Detection/DataSet.cs index f489356..4b3ae07 100644 --- a/FoundationV3/Mobile/Detection/DataSet.cs +++ b/FoundationV3/Mobile/Detection/DataSet.cs @@ -916,7 +916,73 @@ internal SearchReadonlyList ValuesNameSearch } } private SearchReadonlyList _valuesNameSearch = null; - + +#if !SQL_BUILD && !NETCORE_BUILD + + /// + /// An array of the properties that are of type JavaScript. + /// + internal Property[] JavaScriptProperties + { + get + { + if (_javaScriptProperties == null) + { + lock (this) + { + if (_javaScriptProperties == null) + { + _javaScriptProperties = Properties.Where(i => + i._valueType == + Property.PropertyValueType.JavaScript).ToArray(); + } + } + } + return _javaScriptProperties; + } + } + private Property[] _javaScriptProperties = null; + + /// + /// Find all the properties that are of type JavaScript and are marked + /// with the property value override category. + /// + internal Property[] PropertyValueOverrideProperties + { + get + { + if (_propertyValueOverrideProperties == null) + { + lock (this) + { + if (_propertyValueOverrideProperties == null) + { + _propertyValueOverrideProperties = + JavaScriptProperties.Where(i => + i.Category.Equals( + Constants.PropertyValueOverrideCategory) + ).ToArray(); + } + } + } + return _propertyValueOverrideProperties; + } + } + private Property[] _propertyValueOverrideProperties; + + /// + /// Find all the properties that are of type JavaScript and are marked + /// with the property value override category. + /// + /// Array of JavaScript properties for this feature. + private static Property[] GetJavaScriptProperties() + { + return WebProvider.ActiveProvider.DataSet.JavaScriptProperties. + Where(i => i.Category.Equals( + Constants.PropertyValueOverrideCategory)).ToArray(); + } + +#endif #endregion #region Constructors diff --git a/FoundationV3/Mobile/Detection/DetectorModule.cs b/FoundationV3/Mobile/Detection/DetectorModule.cs index fcf23ba..d65f182 100644 --- a/FoundationV3/Mobile/Detection/DetectorModule.cs +++ b/FoundationV3/Mobile/Detection/DetectorModule.cs @@ -133,6 +133,9 @@ protected void Initialise(HttpApplication application) // Configure the profile override component. Feature.ProfileOverride.Init(application.Application); + // Configure the property value override component. + Feature.PropertyValueOverride.Init(application.Application); + // Register for an event to capture javascript requests // for static resources and record bandwidth information. application.BeginRequest += OnBeginRequestJavascript; @@ -331,7 +334,14 @@ private static void AppendCoreJavaScript(HttpContext context, StringBuilder sb) var profileOverrideJavascript = Feature.ProfileOverride.GetJavascript(context); if (String.IsNullOrEmpty(profileOverrideJavascript) == false) { - AppendJavascript(sb, Feature.ProfileOverride.GetJavascript(context)); + AppendJavascript(sb, profileOverrideJavascript); + } + + // Add property value override javascript if available for this request. + var propertyValueJavaScript = Feature.PropertyValueOverride.GetJavascript(context); + if (String.IsNullOrEmpty(propertyValueJavaScript) == false) + { + AppendJavascript(sb, propertyValueJavaScript); } } diff --git a/FoundationV3/Mobile/Detection/Entities/Value.cs b/FoundationV3/Mobile/Detection/Entities/Value.cs index bc03714..39dab5a 100644 --- a/FoundationV3/Mobile/Detection/Entities/Value.cs +++ b/FoundationV3/Mobile/Detection/Entities/Value.cs @@ -274,8 +274,32 @@ internal Value( _urlOffset = reader.ReadInt32(); } + /// + /// Constructs a new instance of . + /// + /// + /// The data set the value is contained within. + /// + /// + /// The property the dynamic value will relate to. + /// + /// + /// The string name of the dynamic value. + /// + internal Value( + DataSet dataSet, + Property property, + string value) : base(dataSet, -1) + { + _propertyIndex = property.Index; + _name = value; + _nameOffset = -1; + _descriptionOffset = -1; + _urlOffset = -1; + } + #endregion - + #region Internal Methods /// diff --git a/FoundationV3/Mobile/Detection/Entities/Values.cs b/FoundationV3/Mobile/Detection/Entities/Values.cs index 0e28886..7fb06d4 100644 --- a/FoundationV3/Mobile/Detection/Entities/Values.cs +++ b/FoundationV3/Mobile/Detection/Entities/Values.cs @@ -51,6 +51,11 @@ public class Values : IList /// private readonly int[] _valueIndexes; + /// + /// An array of values to expose. + /// + private readonly IList _values; + #endregion #region Properties @@ -67,6 +72,19 @@ public bool IsDefault } } + /// + /// True if the value does not relate to a static one contained + /// in the data set and was created from an HTTP cookie set via + /// JavaScript or in some other manner. + /// + public bool IsDynamic + { + get + { + return _values != null; + } + } + #endregion #region Constructor @@ -78,14 +96,32 @@ public bool IsDefault /// Property the values list relates to. /// /// - /// An array of values to use with the list. + /// An array of value indexes to use with the list. /// internal Values(Property property, int[] valueIndexes) { _property = property; _valueIndexes = valueIndexes; + _values = null; + } + + /// + /// Constructs a new instance of the values list. + /// + /// + /// Property the values list relates to. + /// + /// + /// An array of values to use with the list. + /// + internal Values(Property property, IList values) + { + _property = property; + _values = values; + _valueIndexes = null; } + #endregion #region Public Methods @@ -99,9 +135,16 @@ public Value this[string valueName] { get { - var index = _property.DataSet.ValuesNameSearch.BinarySearch( - _valueIndexes, valueName); - return index >= 0 ? this[index] : null; + if (_valueIndexes != null) + { + var index = _property.DataSet.ValuesNameSearch.BinarySearch( + _valueIndexes, valueName); + return index >= 0 ? this[index] : null; + } + else + { + return _values.FirstOrDefault(i => valueName.Equals(i.Name)); + } } } @@ -189,13 +232,20 @@ public override string ToString() /// The index of value if found in the list; otherwise, -1. public int IndexOf(Value item) { - for (var index = 0; index < _valueIndexes.Length; index++) + if (_valueIndexes != null) { - if (_valueIndexes[index].Equals(item)) + for (var index = 0; index < _valueIndexes.Length; index++) { - return index; + if (_valueIndexes[index].Equals(item)) + { + return index; + } } } + else if (_values != null) + { + return _values.IndexOf(item); + } return -1; } @@ -228,7 +278,9 @@ public Value this[int index] { get { - return _property.DataSet.Values[_valueIndexes[index]]; + return _valueIndexes != null ? + _property.DataSet.Values[_valueIndexes[index]] : + _values[index]; } set { @@ -260,7 +312,15 @@ public void Clear() /// true if the value is found in the values list; otherwise, false. public bool Contains(Value item) { - return item != null && _valueIndexes.Contains(item.Index); + if (item != null) + { + if (_valueIndexes != null) + { + return _valueIndexes.Contains(item.Index); + } + return _values.Contains(item); + } + return false; } /// @@ -270,9 +330,16 @@ public bool Contains(Value item) /// The zero-based index in array at which copying begins. public void CopyTo(Value[] array, int arrayIndex) { - for (int i = arrayIndex; i < _valueIndexes.Length; i++) + if (_valueIndexes != null) + { + for (int i = arrayIndex; i < _valueIndexes.Length; i++) + { + array[i] = this[i]; + } + } + else { - array[i] = this[i]; + _values.CopyTo(array, arrayIndex); } } @@ -281,7 +348,12 @@ public void CopyTo(Value[] array, int arrayIndex) /// public int Count { - get { return _valueIndexes.Length; } + get + { + return _valueIndexes != null ? + _valueIndexes.Length : + _values.Count; + } } /// @@ -308,7 +380,11 @@ public bool Remove(Value item) /// An IEnumerator<> object that can be used to iterate through the collection. public IEnumerator GetEnumerator() { - return _valueIndexes.Select(i => _property.DataSet.Values[i]).GetEnumerator(); + if (_valueIndexes != null) + { + return _valueIndexes.Select(i => _property.DataSet.Values[i]).GetEnumerator(); + } + return _values.GetEnumerator(); } /// @@ -317,7 +393,7 @@ public IEnumerator GetEnumerator() /// An IEnumerator<> object that can be used to iterate through the collection. System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { - return _valueIndexes.GetEnumerator(); + return GetEnumerator(); } #endregion diff --git a/FoundationV3/Mobile/Detection/Feature/PropertyValueOverride.cs b/FoundationV3/Mobile/Detection/Feature/PropertyValueOverride.cs new file mode 100644 index 0000000..0188315 --- /dev/null +++ b/FoundationV3/Mobile/Detection/Feature/PropertyValueOverride.cs @@ -0,0 +1,111 @@ +/* ********************************************************************* + * This Source Code Form is copyright of 51Degrees Mobile Experts Limited. + * Copyright © 2015 51Degrees Mobile Experts Limited, 5 Charlotte Close, + * Caversham, Reading, Berkshire, United Kingdom RG4 7BY + * + * This Source Code Form is the subject of the following patent + * applications, owned by 51Degrees Mobile Experts Limited of 5 Charlotte + * Close, Caversham, Reading, Berkshire, United Kingdom RG4 7BY: + * European Patent Application No. 13192291.6; and + * United States Patent Application Nos. 14/085,223 and 14/085,301. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. + * + * If a copy of the MPL was not distributed with this file, You can obtain + * one at http://mozilla.org/MPL/2.0/. + * + * This Source Code Form is “Incompatible With Secondary Licenses”, as + * defined by the Mozilla Public License, v. 2.0. + * ********************************************************************* */ + +using FiftyOne.Foundation.Mobile.Detection.Entities; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Web; + +namespace FiftyOne.Foundation.Mobile.Detection.Feature +{ + internal static class PropertyValueOverride + { + #region Internal Methods + + /// + /// Set a property in the application state to quickly determine if property + /// value override is supported. + /// + /// + internal static void Init(HttpApplicationState application) + { + if (Configuration.Manager.FeatureDetectionEnabled == false || + WebProvider.ActiveProvider == null) + { + application[Constants.PropertyValueOverrideFlag] = + new bool?(false); + } + else + { + application[Constants.PropertyValueOverrideFlag] = new bool?( + WebProvider.GetActiveProvider().DataSet. + PropertyValueOverrideProperties.Length > 0); + } + EventLog.Debug(String.Format( + "Property Value Override '{0}'", + application[Constants.PropertyValueOverrideFlag])); + } + + /// + /// Returns the JavaScript for the Property Value Override feature. + /// + /// + /// JavaScript for the device, otherwise null. + internal static string GetJavascript(HttpContext context) + { + var enabled = context.Application[Constants.PropertyValueOverrideFlag] as bool?; + if (enabled.HasValue && enabled.Value) + { + var jsvalues = GetJavascriptValues(context.Request); + if (jsvalues.Count > 0) + { + return String.Format( + "// Property Value Overrides - Start\r" + + "{0}\r" + + "// Property Value Overrides - End\r", + String.Join("\r", jsvalues.Select(i => i.ToString()))); + } + } + return null; + } + + #endregion + + #region Private Methods + + /// + /// Returns all the JavaScript for both the Propery Value Override + /// feature AND the request provided. + /// + /// Request the javascript is needed for + /// Javascript to support the feature if present + private static List GetJavascriptValues(HttpRequest request) + { + var javaScriptValues = new List(); + var match = WebProvider.GetMatch(request); + if (match != null) + { + foreach (var property in WebProvider.GetActiveProvider(). + DataSet.PropertyValueOverrideProperties) + { + foreach (var value in match[property]) + { + javaScriptValues.Add(value); + } + } + } + return javaScriptValues; + } + + #endregion + } +} diff --git a/FoundationV3/Mobile/Detection/Match.cs b/FoundationV3/Mobile/Detection/Match.cs index cc1c272..c64a140 100644 --- a/FoundationV3/Mobile/Detection/Match.cs +++ b/FoundationV3/Mobile/Detection/Match.cs @@ -21,6 +21,7 @@ using System; using System.Collections.Generic; +using System.Collections.Specialized; using System.Linq; using System.Text; using FiftyOne.Foundation.Mobile.Detection.Entities; @@ -52,7 +53,7 @@ namespace FiftyOne.Foundation.Mobile.Detection /// public class Match : IMatch { - #region Private Fields + #region Internal Fields /// /// The result of a completed match. @@ -69,6 +70,13 @@ public class Match : IMatch /// internal readonly Provider Provider; + /// + /// Reference to the cookie header used to populate this + /// match. Used to get the values for properties which + /// support property override. + /// + internal string _cookie; + #endregion #region Public Fields @@ -323,12 +331,109 @@ public SortedList Results #endregion + #region Private Properties + +#if SQL_BUILD || NETCORE_BUILD + /// + /// If building the SQL or .NET Core project, just return an empty + /// dictionary as this is not used and required unneccesary includes. + /// + private Dictionary PropertyValueOverrideCookies + { + get + { + if (_propertyValueOverrideCookies == null) + { + lock (this) + { + if (_propertyValueOverrideCookies == null) + { + _propertyValueOverrideCookies = + new Dictionary(); + } + } + } + return _propertyValueOverrideCookies; + } + } +#else + /// + /// A dictionary of property names that have alterantive values based + /// on cookie header data if provided when the Match was populated. + /// Used when JavaScript is available to provide alternative values + /// based on a client side JavaScript code. + /// + /// + /// Ideally the implementation would reuse standard .NET cookie + /// handling. However as the public interface can only accept a + /// containing all the HTTP headers + /// if is not possible to use this functionality at this time. As such + /// there is some duplication in this implementation and .NET. + /// + /// + /// The presence of _cookie should be checked before accessing this + /// method. + /// + private Dictionary PropertyValueOverrideCookies + { + get + { + if (_propertyValueOverrideCookies == null) + { + lock (this) + { + if (_propertyValueOverrideCookies == null) + { + var propertyValueOverrideCookies = + new Dictionary(); + try + { + var pairs = _cookie.Split(Constants.CookieSplitter, + StringSplitOptions.RemoveEmptyEntries); + foreach (var pair in pairs.Select(i => + i.Trim()).Where(i => + i.StartsWith(Constants.PropertyValueOverrideCookiePrefix))) + { + var equalsIndex = pair.IndexOf('='); + if (equalsIndex > Constants.PropertyValueOverrideCookiePrefix.Length) + { + var key = pair.Substring( + Constants.PropertyValueOverrideCookiePrefix.Length, + equalsIndex - Constants.PropertyValueOverrideCookiePrefix.Length); + var value = pair.Substring(equalsIndex + 1); + propertyValueOverrideCookies.Add(key, value); + } + } + } + catch (Exception ex) + { + // There is no opportunity to throw an exception as the + // method is core and is not essential to processing. + // Record the exception in the log file. + EventLog.Warn(ex); + EventLog.Warn("Exception processing cookie '{0}'.", _cookie); + } + _propertyValueOverrideCookies = propertyValueOverrideCookies; + } + } + } + return _propertyValueOverrideCookies; + } + } +#endif + private Dictionary _propertyValueOverrideCookies = null; + + #endregion + #region Public Accessors /// /// Gets the values associated with the property name using the profiles /// found by the match. If matched profiles don't contain a value then /// the default profiles for each of the components are also checked. + /// If a value is provided via the property value override functionality + /// then this will be returned in place of the static value from the + /// dataset. /// /// The property whose values are required /// Array of the values associated with the property, or null if the property does not exist @@ -339,15 +444,34 @@ public Values this[Property property] Values value = null; if (property != null) { - // Get the property value from the profile returned - // from the match. - Profile profile = Profiles.FirstOrDefault(i => - property.Component.Equals(i.Component)); - if (profile != null) + // Does the property support Property Value Override, and + // is there a value in the cookies dictionary? + string cookieValue; + if (_cookie != null && + PropertyValueOverrideCookies.TryGetValue( + property.Name, + out cookieValue)) { - value = profile[property]; - } + // Create a dynamic values instance for this value which does + // not reference a static value index in the dataset. + value = new Values( + property, + new Value[] { + new Value(DataSet, property, cookieValue) + }); + } + else + { + // Get the property value from the profile returned + // from the match. + Profile profile = Profiles.FirstOrDefault(i => + property.Component.Equals(i.Component)); + if (profile != null) + { + value = profile[property]; + } + } // If the value has not been found use the default profile. if (value == null) { @@ -414,6 +538,8 @@ internal void Reset() { State.Reset(); _overriddenProfiles = null; + _cookie = null; + _propertyValueOverrideCookies = null; } /// diff --git a/FoundationV3/Mobile/Detection/Provider.cs b/FoundationV3/Mobile/Detection/Provider.cs index df1f674..8ba77c6 100644 --- a/FoundationV3/Mobile/Detection/Provider.cs +++ b/FoundationV3/Mobile/Detection/Provider.cs @@ -476,6 +476,8 @@ public Match Match(string targetUserAgent, Match match) /// public Match Match(NameValueCollection headers, Match match) { + match.Reset(); + if (headers == null || headers.Count == 0) { // Empty headers all default match result. @@ -516,6 +518,10 @@ public Match Match(NameValueCollection headers, Match match) match.State.Signature = null; match.State.TargetUserAgent = null; } + + // If the Cookie header is present then record this as it maybe + // needed when a Property Value Override property is requested. + match._cookie = headers["Cookie"]; } return match; } diff --git a/FoundationV3/Properties/AssemblyInfo.cs b/FoundationV3/Properties/AssemblyInfo.cs index fd9ede2..f9d7454 100644 --- a/FoundationV3/Properties/AssemblyInfo.cs +++ b/FoundationV3/Properties/AssemblyInfo.cs @@ -47,7 +47,7 @@ // Build Number // Revision -[assembly: AssemblyVersion("3.2.16.2")] -[assembly: AssemblyFileVersion("3.2.16.2")] +[assembly: AssemblyVersion("3.2.17.2")] +[assembly: AssemblyFileVersion("3.2.17.2")] [assembly: NeutralResourcesLanguage("en-GB")] [assembly: AllowPartiallyTrustedCallers] diff --git a/FoundationV3/Properties/DetectionConstants.cs b/FoundationV3/Properties/DetectionConstants.cs index 95f5654..df39013 100644 --- a/FoundationV3/Properties/DetectionConstants.cs +++ b/FoundationV3/Properties/DetectionConstants.cs @@ -18,7 +18,7 @@ * This Source Code Form is “Incompatible With Secondary Licenses”, as * defined by the Mozilla Public License, v. 2.0. * ********************************************************************* */ - + using System; using System.Collections.Generic; @@ -31,10 +31,21 @@ public static class Constants { #region Public Constants + /// + /// The prefix to apply to the cookie name when used in the client. + /// + public const string PropertyValueOverrideCookiePrefix = "51D_"; + + /// + /// Used to indicate if property value override is enabled. + /// + public const string PropertyValueOverrideFlag = "51D_PVO"; + /// /// The dafult name of the profile override cookie. /// - public const string ProfileOverrideCookieName = "51D_ProfileIds"; + public const string ProfileOverrideCookieName = + PropertyValueOverrideCookiePrefix + "ProfileIds"; /// /// The default path to use for the binary data file. @@ -104,6 +115,23 @@ public static class Constants #region Internal Constants + /// + /// The name of the category + /// + /// properties which should be used for + /// + /// methods must be assigned. + /// + internal const string PropertyValueOverrideCategory = "Property Value Override"; + + /// + /// Used to split up a cookie string. + /// + internal static readonly char[] CookieSplitter = new char[] { ';' }; + + /// + /// Name of the cookie used by MVC to override browser details. + /// internal const string MvcBrowserOverrideCookie = ".ASPXBrowserOverride"; /// diff --git a/FoundationV3/SerializableAttribute.cs b/FoundationV3/SerializableAttribute.cs index 7e8066b..fdbfbea 100644 --- a/FoundationV3/SerializableAttribute.cs +++ b/FoundationV3/SerializableAttribute.cs @@ -1,6 +1,9 @@ #if !NET40 namespace System.Runtime.Serialization { + /// + /// Required for .NET Standard build to work. + /// public class SerializableAttribute : Attribute { } diff --git a/FoundationV3/project.json b/FoundationV3/project.json index 8749b69..131a91d 100644 --- a/FoundationV3/project.json +++ b/FoundationV3/project.json @@ -12,7 +12,10 @@ "frameworks": { "netstandard1.6": { - "imports": "dnxcore50" + "imports": "dnxcore50", + "buildOptions": { + "define": ["NETCORE_BUILD"] + } } }, "buildOptions": { diff --git a/FoundationV3/project.lock.json b/FoundationV3/project.lock.json index 1ee49cf..130a533 100644 --- a/FoundationV3/project.lock.json +++ b/FoundationV3/project.lock.json @@ -1152,7 +1152,7 @@ ] }, "Microsoft.Win32.Primitives/4.0.1": { - "sha512": "fQnBHO9DgcmkC9dYSJoBqo6sH1VJwJprUHh8F3hbcRlxiQiBUuTntdk8tUwV490OqC2kQUrinGwZyQHTieuXRA==", + "sha512": "QvxLxm8lz3s7wXEXe2kQJ19VBAmcDeS+cgjc+0BIXqcJb4NSEtnOfeyD6//mGZcW8pI7DPqbmyPLMwpJNRE6Ug==", "type": "package", "path": "Microsoft.Win32.Primitives/4.0.1", "files": [ @@ -1188,7 +1188,7 @@ ] }, "NETStandard.Library/1.6.0": { - "sha512": "ypsCvIdCZ4IoYASJHt6tF2fMo7N30NLgV1EbmC+snO490OMl9FvVxmumw14rhReWU3j3g7BYudG6YCrchwHJlA==", + "sha512": "mWzx2Ub1tYWZNhcUszHpG3ChPpkAiZB76KbBED+nI3yApxIN7Jv1GhXh1EA3z1T+ZUrZJ8iWbFHuAuYECcnRGg==", "type": "package", "path": "NETStandard.Library/1.6.0", "files": [ @@ -1199,7 +1199,7 @@ ] }, "runtime.native.System/4.0.0": { - "sha512": "QfS/nQI7k/BLgmLrw7qm7YBoULEvgWnPI+cYsbfCVFTW8Aj+i8JhccxcFMu1RWms0YZzF+UHguNBK4Qn89e2Sg==", + "sha512": "Nn6mLQgkW7Aqc4v9Bs7MN8cZDWfDyL9q7zxGmgqMSQ0sLRW/3x6zZTm/9sHWNsSY5863LEh4dMi1puOeB82d7g==", "type": "package", "path": "runtime.native.System/4.0.0", "files": [ @@ -1211,7 +1211,7 @@ ] }, "runtime.native.System.IO.Compression/4.1.0": { - "sha512": "Ob7nvnJBox1aaB222zSVZSkf4WrebPG4qFscfK7vmD7P7NxoSxACQLtO7ytWpqXDn2wcd/+45+EAZ7xjaPip8A==", + "sha512": "xnzRnBrD4Myb4+E+7SDonK8SpTRUPQ3FKwA63LYUJBgHBX1r4zvESOC9DwcQPkdGiHkoceKQnp6KVln6J9TxMg==", "type": "package", "path": "runtime.native.System.IO.Compression/4.1.0", "files": [ @@ -1223,7 +1223,7 @@ ] }, "runtime.native.System.Net.Http/4.0.1": { - "sha512": "Nh0UPZx2Vifh8r+J+H2jxifZUD3sBrmolgiFWJd2yiNrxO0xTa6bAw3YwRn1VOiSen/tUXMS31ttNItCZ6lKuA==", + "sha512": "1CviQyiB9+/ecS/htud8AkJWhRv3o9afY8l8/7jIa5YaXlUfTlthHlgjlSCEQEIJBCoyf4VFQ0Cj6oakYWt2mA==", "type": "package", "path": "runtime.native.System.Net.Http/4.0.1", "files": [ @@ -1235,7 +1235,7 @@ ] }, "runtime.native.System.Security.Cryptography/4.0.0": { - "sha512": "2CQK0jmO6Eu7ZeMgD+LOFbNJSXHFVQbCJJkEyEwowh1SCgYnrn9W9RykMfpeeVGw7h4IBvYikzpGUlmZTUafJw==", + "sha512": "3KNwzv53LkErnQIMuLfBraohpq+jmCCV4c+BF0HL7JTZNYDMIySkv56z1LzoylwEQ26BQ4ffk2BfMr05/Bj7kw==", "type": "package", "path": "runtime.native.System.Security.Cryptography/4.0.0", "files": [ @@ -1247,7 +1247,7 @@ ] }, "System.AppContext/4.1.0": { - "sha512": "3QjO4jNV7PdKkmQAVp9atA+usVnKRwI3Kx1nMwJ93T0LcQfx7pKAYk0nKz5wn1oP5iqlhZuy6RXOFdhr7rDwow==", + "sha512": "pFOvx3IoQXYvsmsgtorrGwBCcu8sIAaTawqT9AAXcBGpbUGXoa0t7XX9HkR5Xf8efx6c3hDoH+Zw1ZL6nUgJiA==", "type": "package", "path": "System.AppContext/4.1.0", "files": [ @@ -1300,7 +1300,7 @@ ] }, "System.Buffers/4.0.0": { - "sha512": "msXumHfjjURSkvxUjYuq4N2ghHoRi2VpXcKMA7gK6ujQfU3vGpl+B6ld0ATRg+FZFpRyA6PgEPA+VlIkTeNf2w==", + "sha512": "nDKzldTIZ8OWaV0NBMFiVGJRr1zFOgzpGAa7/nRvjHHwy3HgTb7esIU8eYpKPggKgbiqJh7lQ+BQ67J5SZ1oGQ==", "type": "package", "path": "System.Buffers/4.0.0", "files": [ @@ -1519,7 +1519,7 @@ ] }, "System.Console/4.0.0": { - "sha512": "qSKUSOIiYA/a0g5XXdxFcUFmv1hNICBD7QZ0QhGYVipPIhvpiydY8VZqr1thmCXvmn8aipMg64zuanB4eotK9A==", + "sha512": "JFEk4NJeJYyWhLlbouukLNWNbocpt2nUYkN6SzCfbvUputUP4CBCvOhzdOljCKVKHA32yzhuLGkmTZgxuBikPw==", "type": "package", "path": "System.Console/4.0.0", "files": [ @@ -1621,7 +1621,7 @@ ] }, "System.Diagnostics.DiagnosticSource/4.0.0": { - "sha512": "YKglnq4BMTJxfcr6nuT08g+yJ0UxdePIHxosiLuljuHIUR6t4KhFsyaHOaOc1Ofqp0PUvJ0EmcgiEz6T7vEx3w==", + "sha512": "5SnE1tpeEdWkaxktOVVbmvNiZpmc6+qoO86r9YIeizZo5Ph3JG6c1YjpAc2ApCj1I4Jd3t0BSBzCKpQits/QAg==", "type": "package", "path": "System.Diagnostics.DiagnosticSource/4.0.0", "files": [ @@ -1640,7 +1640,7 @@ ] }, "System.Diagnostics.Tools/4.0.1": { - "sha512": "xBfJ8pnd4C17dWaC9FM6aShzbJcRNMChUMD42I6772KGGrqaFdumwhn9OdM68erj1ueNo3xdQ1EwiFjK5k8p0g==", + "sha512": "2QUW4gY8zwgd1Fc2JlWGGykHx8gEPsG7ETnS/xm/drqqDAGhUXuxkUJ6ZkgpkyDgMfvwj9Ad2KddTRHMvMcsYA==", "type": "package", "path": "System.Diagnostics.Tools/4.0.1", "files": [ @@ -1849,7 +1849,7 @@ ] }, "System.Globalization.Calendars/4.0.1": { - "sha512": "L1c6IqeQ88vuzC1P81JeHmHA8mxq8a18NUBNXnIY/BVb+TCyAaGIFbhpZt60h9FJNmisymoQkHEFSE9Vslja1Q==", + "sha512": "DwE9/01N350tMNDZnFfFUDRJp+yxP3Ju/5XxiWLVqM1iMZ/2xqK5+e3qdzC+3mBk65cKI+/M8OysRll6X5h/7Q==", "type": "package", "path": "System.Globalization.Calendars/4.0.1", "files": [ @@ -1885,7 +1885,7 @@ ] }, "System.Globalization.Extensions/4.0.1": { - "sha512": "KKo23iKeOaIg61SSXwjANN7QYDr/3op3OWGGzDzz7mypx0Za0fZSeG0l6cco8Ntp8YMYkIQcAqlk8yhm5/Uhcg==", + "sha512": "UKb+GVZf9SBxZzMcDHo7QqdPofARSOhTlcCaMBcwNlFQjIJ8bOcl7WXEPzENAlWYHFxNU5jSPxS3Qr4WrJLueQ==", "type": "package", "path": "System.Globalization.Extensions/4.0.1", "files": [ @@ -2003,7 +2003,7 @@ ] }, "System.IO.Compression/4.1.0": { - "sha512": "TjnBS6eztThSzeSib+WyVbLzEdLKUcEHN69VtS3u8aAsSc18FU6xCZlNWWsEd8SKcXAE+y1sOu7VbU8sUeM0sg==", + "sha512": "RMXC6/H6pofXM4i+loGQcLpCTNzy0122ZNKIsdHEeaUElgd+2BjiO1UNLYsIdOvJ+dp2XbJgwWF3NWto/fqUmQ==", "type": "package", "path": "System.IO.Compression/4.1.0", "files": [ @@ -2072,7 +2072,7 @@ ] }, "System.IO.Compression.ZipFile/4.0.1": { - "sha512": "hBQYJzfTbQURF10nLhd+az2NHxsU6MU7AB8RUf4IolBP5lOAm4Luho851xl+CqslmhI5ZH/el8BlngEk4lBkaQ==", + "sha512": "Oy3Akg0KOieMayZvRyS+lAqQjJNosZ5leadjYn8xEuZe5pZRUNtiQ8ZRVJeEfNZzeJKAtMa7Hr2WwL/BcJst9Q==", "type": "package", "path": "System.IO.Compression.ZipFile/4.0.1", "files": [ @@ -2109,7 +2109,7 @@ ] }, "System.IO.FileSystem/4.0.1": { - "sha512": "IBErlVq5jOggAD69bg1t0pJcHaDbJbWNUZTPI96fkYWzwYbN6D9wRHMULLDd9dHsl7C2YsxXL31LMfPI1SWt8w==", + "sha512": "lVjxTJhqz3Y1ORF6tkOWXAePl/Dk6K9QIjyLNrDWIFi+a9ShXjDjk0gPqaqS7HXDVVrAZoDDlsjTSk+4kDYLRA==", "type": "package", "path": "System.IO.FileSystem/4.0.1", "files": [ @@ -2145,7 +2145,7 @@ ] }, "System.IO.FileSystem.Primitives/4.0.1": { - "sha512": "kWkKD203JJKxJeE74p8aF8y4Qc9r9WQx4C0cHzHPrY3fv/L/IhWnyCHaFJ3H1QPOH6A93whlQ2vG5nHlBDvzWQ==", + "sha512": "aZy3yQ3eeDpWU7QqKfXETovqiYeKP2OJxGeFr7KQ50XzPnTz/QbRk7dwKRqdTMq4HItcdCsvF0CTDY6gUnZjvA==", "type": "package", "path": "System.IO.FileSystem.Primitives/4.0.1", "files": [ @@ -2182,7 +2182,7 @@ ] }, "System.Linq/4.1.0": { - "sha512": "bQ0iYFOQI0nuTnt+NQADns6ucV4DUvMdwN6CbkB1yj8i7arTGiTN5eok1kQwdnnNWSDZfIUySQY+J3d5KjWn0g==", + "sha512": "+OyooXL/awnD5o2thquMLC9DE8Aw5awzvJi69nBEbxcZBcXJlwwDTF9Lnb7OTCxh6TyaNNWBDikSh9qDY6Mm4g==", "type": "package", "path": "System.Linq/4.1.0", "files": [ @@ -2252,7 +2252,7 @@ ] }, "System.Linq.Expressions/4.1.0": { - "sha512": "I+y02iqkgmCAyfbqOmSDOgqdZQ5tTj80Akm5BPSS8EeB0VGWdy6X1KCoYe8Pk6pwDoAKZUOdLVxnTJcExiv5zw==", + "sha512": "hxwcPNn9nAJBw8w6ACiUuFKWMeSUuISK9kkS+0vTXr1nIXfhR78+GPUeB7AwxqCiwJiFIZfpTT81qzLNHIiG0w==", "type": "package", "path": "System.Linq.Expressions/4.1.0", "files": [ @@ -2334,7 +2334,7 @@ ] }, "System.Net.Http/4.1.0": { - "sha512": "ULq9g3SOPVuupt+Y3U+A37coXzdNisB1neFCSKzBwo182u0RDddKJF8I5+HfyXqK6OhJPgeoAwWXrbiUXuRDsg==", + "sha512": "q/aTJTH0JB+vjxwrL9l3U9D+BSPZ0vbS7q3znoqAw2EQ1F9XZdmBoGYkJmlaMK/AwNk8DzGriI+TzTKysMI3+g==", "type": "package", "path": "System.Net.Http/4.1.0", "files": [ @@ -2414,7 +2414,7 @@ ] }, "System.Net.Primitives/4.0.11": { - "sha512": "hVvfl4405DRjA2408luZekbPhplJK03j2Y2lSfMlny7GHXlkByw1iLnc9mgKW0GdQn73vvMcWrWewAhylXA4Nw==", + "sha512": "CKPRdX6yHxqsfncDY/12QJvpLBETtZJawgZ2xwV2NiP2gaDwsrAHmnjFXSO/Fu+4TLqQlPYaSaE0Q5J5FtWcHw==", "type": "package", "path": "System.Net.Primitives/4.0.11", "files": [ @@ -2491,7 +2491,7 @@ ] }, "System.Net.Sockets/4.1.0": { - "sha512": "xAz0N3dAV/aR/9g8r0Y5oEqU1JRsz29F5EGb/WVHmX3jVSLqi2/92M5hTad2aNWovruXrJpJtgZ9fccPMG9uSw==", + "sha512": "1lbMK4+NqN3zpc0p0Jy66O5DUi/trwQt1XKHew9qbtEfqpW1p2GZejb3I+kJqY1tigir7xYWK0BFzCz2oNzIEQ==", "type": "package", "path": "System.Net.Sockets/4.1.0", "files": [ @@ -2527,7 +2527,7 @@ ] }, "System.ObjectModel/4.0.12": { - "sha512": "tAgJM1xt3ytyMoW4qn4wIqgJYm7L7TShRZG4+Q4Qsi2PCcj96pXN7nRywS9KkB3p/xDUjc2HSwP9SROyPYDYKQ==", + "sha512": "aC1UfGV+5RW2iP/fVA/duLih1GmenUa+pwOBmgXFKsMS6/lINozNdYRJCh+93jYRD1qXbfzwtMeo9QiH/nk4+Q==", "type": "package", "path": "System.ObjectModel/4.0.12", "files": [ @@ -2674,7 +2674,7 @@ ] }, "System.Reflection.Emit/4.0.1": { - "sha512": "P2wqAj72fFjpP6wb9nSfDqNBMab+2ovzSDzUZK7MVIm54tBJEPr9jWfSjjoTpPwj1LeKcmX3vr0ttyjSSFM47g==", + "sha512": "VK4zjOfBgbMtoxrB/IQtdOrujEKV9f8H+0vf3CKq2VGTVAOicU3KINLkRonNQGT/CapbquOJ7j/6f05RXhfU2A==", "type": "package", "path": "System.Reflection.Emit/4.0.1", "files": [ @@ -2704,7 +2704,7 @@ ] }, "System.Reflection.Emit.ILGeneration/4.0.1": { - "sha512": "Ov6dU8Bu15Bc7zuqttgHF12J5lwSWyTf1S+FJouUXVMSqImLZzYaQ+vRr1rQ0OZ0HqsrwWl4dsKHELckQkVpgA==", + "sha512": "GPQnYTCzks4OcarwsC8XmWkX66QopzSo6K2OTpvgcsU3NdZ4YPiuwh++wAdvRvI5LrMjlWhG4xXe5lEdoMFrpA==", "type": "package", "path": "System.Reflection.Emit.ILGeneration/4.0.1", "files": [ @@ -2735,7 +2735,7 @@ ] }, "System.Reflection.Emit.Lightweight/4.0.1": { - "sha512": "sSzHHXueZ5Uh0OLpUQprhr+ZYJrLPA2Cmr4gn0wj9+FftNKXx8RIMKvO9qnjk2ebPYUjZ+F2ulGdPOsvj+MEjA==", + "sha512": "Rr5OGKFikL+qgt7g6FAPGurxmLJB3WJG9fm0oClu9E4q9kZeHm52vMhPnqMElr5jqUx7nL/v3Jae6szqIlX63Q==", "type": "package", "path": "System.Reflection.Emit.Lightweight/4.0.1", "files": [ @@ -2766,7 +2766,7 @@ ] }, "System.Reflection.Extensions/4.0.1": { - "sha512": "GYrtRsZcMuHF3sbmRHfMYpvxZoIN2bQGrYGerUiWLEkqdEUQZhH3TRSaC/oI4wO0II1RKBPlpIa1TOMxIcOOzQ==", + "sha512": "2+uKFAOi6eai3yhXrffxn2/S1mQjN2Ukew187A4Sa8oArC9whs8WI4qscSlYtrVErvs2wwRvcgsVEgA0cpZiIA==", "type": "package", "path": "System.Reflection.Extensions/4.0.1", "files": [ @@ -2876,7 +2876,7 @@ ] }, "System.Reflection.TypeExtensions/4.1.0": { - "sha512": "tsQ/ptQ3H5FYfON8lL4MxRk/8kFyE0A+tGPXmVP967cT/gzLHYxIejIYSxp4JmIeFHVP78g/F2FE1mUUTbDtrg==", + "sha512": "VUsqyXj4LVB4jSY0ato1zQREGQCn3qAybJBiUIt/pMn2FbTGskTK2OSxTV/vH61uq3Q2YnDmJklWOPJpUYUIgQ==", "type": "package", "path": "System.Reflection.TypeExtensions/4.1.0", "files": [ @@ -3152,7 +3152,7 @@ ] }, "System.Runtime.Handles/4.0.1": { - "sha512": "nCJvEKguXEvk2ymk1gqj625vVnlK3/xdGzx0vOKicQkoquaTBJTP13AIYkocSUwHCLNBwUbXTqTWGDxBTWpt7g==", + "sha512": "rXQQ5LSBzi0SQkqsZZpB/Gffx/LY5FdfEexbWhdKSW02MPWnoJe2ImiNRqrUwBeQScvwMZj4QXgAewVCGhTBYw==", "type": "package", "path": "System.Runtime.Handles/4.0.1", "files": [ @@ -3188,7 +3188,7 @@ ] }, "System.Runtime.InteropServices/4.1.0": { - "sha512": "16eu3kjHS633yYdkjwShDHZLRNMKVi/s0bY8ODiqJ2RfMhDMAwxZaUaWVnZ2P71kr/or+X9o/xFWtNqz8ivieQ==", + "sha512": "X5t5llytiwrtHDsPmT+owPJuTcRmQEHvtVBOjgmUh45/C3L92Kc18hHzDuYDV4ARGElrsXxgSLn6rnHbZt8l/g==", "type": "package", "path": "System.Runtime.InteropServices/4.1.0", "files": [ @@ -3276,7 +3276,7 @@ ] }, "System.Runtime.InteropServices.RuntimeInformation/4.0.0": { - "sha512": "hWPhJxc453RCa8Z29O91EmfGeZIHX1ZH2A8L6lYQVSaKzku2DfArSfMEb1/MYYzPQRJZeu0c9dmYeJKxW5Fgng==", + "sha512": "XoEtFWJOa1oQcw8RYlEF3ZlovBdeOnUXK2TTAYR6t/D2OhtQPgTvQFpaND2ZLT2W13qPk0wdeqFo5cPa92tA/w==", "type": "package", "path": "System.Runtime.InteropServices.RuntimeInformation/4.0.0", "files": [ @@ -3308,7 +3308,7 @@ ] }, "System.Runtime.Numerics/4.0.1": { - "sha512": "+XbKFuzdmLP3d1o9pdHu2nxjNr2OEPqGzKeegPLCUMM71a0t50A/rOcIRmGs9wR7a8KuHX6hYs/7/TymIGLNqg==", + "sha512": "rx2sj+4FB4jrOeDOlUM3BK1RPJsvkyL8MH08U9gjcjhnvh3HhPCyO+8hnuzCISqujdwvwzB+VTcbzYHtD6/b3g==", "type": "package", "path": "System.Runtime.Numerics/4.0.1", "files": [ @@ -3363,7 +3363,7 @@ ] }, "System.Security.Cryptography.Algorithms/4.2.0": { - "sha512": "8JQFxbLVdrtIOKMDN38Fn0GWnqYZw/oMlwOUG/qz1jqChvyZlnUmu+0s7wLx7JYua/nAXoESpHA3iw11QFWhXg==", + "sha512": "ea7mIUY5psJwrzXvHIOCD3cAo168LieEuymMsKa7kJZb2PVYAr7BXrtHoC5QamgWDVFOuj0LN+dJ7aBtRtHaOA==", "type": "package", "path": "System.Security.Cryptography.Algorithms/4.2.0", "files": [ @@ -3401,7 +3401,7 @@ ] }, "System.Security.Cryptography.Cng/4.2.0": { - "sha512": "cUJ2h+ZvONDe28Szw3st5dOHdjndhJzQ2WObDEXAWRPEQBtVItVoxbXM/OEsTthl3cNn2dk2k0I3y45igCQcLw==", + "sha512": "0hE5GTVatEoq8uMiG5lsR42Rd2uNbboCdUgqcB3SBBb23JquqbrCht96xvl0taQz43CpC2ugCEDzIya8fR+lXQ==", "type": "package", "path": "System.Security.Cryptography.Cng/4.2.0", "files": [ @@ -3427,7 +3427,7 @@ ] }, "System.Security.Cryptography.Csp/4.0.0": { - "sha512": "/i1Usuo4PgAqgbPNC0NjbO3jPW//BoBlTpcWFD1EHVbidH21y4c1ap5bbEMSGAXjAShhMH4abi/K8fILrnu4BQ==", + "sha512": "Mgjfw9b0vHD7HRK+wcl2HQAgKeVmcjYk15UIuCwJ4u5HUH5E6QhzBMMfF/cGB+3PEzk71I8CMqKLBDHEGd9rHA==", "type": "package", "path": "System.Security.Cryptography.Csp/4.0.0", "files": [ @@ -3457,7 +3457,7 @@ ] }, "System.Security.Cryptography.Encoding/4.0.0": { - "sha512": "FbKgE5MbxSQMPcSVRgwM6bXN3GtyAh04NkV8E5zKCBE26X0vYW0UtTa2FIgkH33WVqBVxRgxljlVYumWtU+HcQ==", + "sha512": "4YkdNoH4HExW8yvGAXtCAVs8Njzm5mrSEdsMqDkPrJrW1Z4kovKCKk4j8eJsiYovvZ+rZa38+u1mYoiXzh+XQw==", "type": "package", "path": "System.Security.Cryptography.Encoding/4.0.0", "files": [ @@ -3496,7 +3496,7 @@ ] }, "System.Security.Cryptography.OpenSsl/4.0.0": { - "sha512": "HUG/zNUJwEiLkoURDixzkzZdB5yGA5pQhDP93ArOpDPQMteURIGERRNzzoJlmTreLBWr5lkFSjjMSk8ySEpQMw==", + "sha512": "fIxfQe05AAInWziE63aPGlG801RN+I+YtgLdj25dEVm6nsiFIlknAmGuBVTNOoVFHtRrJdU3kq8IISlkDf8HNQ==", "type": "package", "path": "System.Security.Cryptography.OpenSsl/4.0.0", "files": [ @@ -3510,7 +3510,7 @@ ] }, "System.Security.Cryptography.Primitives/4.0.0": { - "sha512": "Wkd7QryWYjkQclX0bngpntW5HSlMzeJU24UaLJQ7YTfI8ydAVAaU2J+HXLLABOVJlKTVvAeL0Aj39VeTe7L+oA==", + "sha512": "sPQTp6ewVb8zFQvuxnQ8vTmQgpJRtssA0UV8w771X4IOQy4AJ+MXxDKb3mRMyKAD4nYq8aYlGBfqxZL7nOvqEQ==", "type": "package", "path": "System.Security.Cryptography.Primitives/4.0.0", "files": [ @@ -3537,7 +3537,7 @@ ] }, "System.Security.Cryptography.X509Certificates/4.1.0": { - "sha512": "4HEfsQIKAhA1+ApNn729Gi09zh+lYWwyIuViihoMDWp1vQnEkL2ct7mAbhBlLYm+x/L4Rr/pyGge1lIY635e0w==", + "sha512": "501DVyqQTMe2YuuIEWmZUErmlPFmR3OhaKYwFcQelQj2JTyoE/+Lth8wnvHXIGBsHNqlgZRay40NjXuEZ9qvDw==", "type": "package", "path": "System.Security.Cryptography.X509Certificates/4.1.0", "files": [ @@ -3657,7 +3657,7 @@ ] }, "System.Text.Encoding.Extensions/4.0.11": { - "sha512": "jtbiTDtvfLYgXn8PTfWI+SiBs51rrmO4AAckx4KR6vFK9Wzf6tI8kcRdsYQNwriUeQ1+CtQbM1W4cMbLXnj/OQ==", + "sha512": "j7vL3qNxgIyCHMzx3n6XW3J0DWE/pJeeqJeLIbv0f4mCziiDidOSChbzkV4BbREEK8IqFC/lGb1Y+IrUMccgEA==", "type": "package", "path": "System.Text.Encoding.Extensions/4.0.11", "files": [ @@ -3723,7 +3723,7 @@ ] }, "System.Text.RegularExpressions/4.1.0": { - "sha512": "i88YCXpRTjCnoSQZtdlHkAOx4KNNik4hMy83n0+Ftlb7jvV6ZiZWMpnEZHhjBp6hQVh8gWd/iKNPzlPF7iyA2g==", + "sha512": "peZyjXkgWVVVM1TKFcelOB9scGdUPBh3JNsTBSUi3a89hgOwRjPDOxIMU31sZHLdHW+iom5Uj+R2SWotl/IlNg==", "type": "package", "path": "System.Text.RegularExpressions/4.1.0", "files": [ @@ -3939,7 +3939,7 @@ ] }, "System.Threading.Tasks.Extensions/4.0.0": { - "sha512": "pH4FZDsZQ/WmgJtN4LWYmRdJAEeVkyriSwrv2Teoe5FOU0Yxlb6II6GL8dBPOfRmutHGATduj3ooMt7dJ2+i+w==", + "sha512": "jH56WWaDM3zAXFs2i7KXiOr8by9voW5JGqIvuUELj/Voq68wp5hf69lR4KiubNFjW8bOn6ycnKcX4eg3G2BLMg==", "type": "package", "path": "System.Threading.Tasks.Extensions/4.0.0", "files": [ @@ -4009,7 +4009,7 @@ ] }, "System.Threading.Timer/4.0.1": { - "sha512": "saGfUV8uqVW6LeURiqxcGhZ24PzuRNaUBtbhVeuUAvky1naH395A/1nY0P2bWvrw/BreRtIB/EzTDkGBpqCwEw==", + "sha512": "6i8it2jcsCQlE26tDyabKqRQIvHEyQhEKvtUF+Ks2NRq/Yge4ZWNPphAecd8Z0bZbfdTqqjS53sCKvWAriDv9Q==", "type": "package", "path": "System.Threading.Timer/4.0.1", "files": [ @@ -4062,7 +4062,7 @@ ] }, "System.Xml.ReaderWriter/4.0.11": { - "sha512": "ZIiLPsf67YZ9zgr31vzrFaYQqxRPX9cVHjtPSnmx4eN6lbS/yEyYNr2vs1doGDEscF0tjCZFsk9yUg1sC9e8tg==", + "sha512": "m30o2uiMNmsdfdNMaNx00qt6E9nUlIsS+DwkRbZ5XxeGTYpsvqb8sZM1iDQrRL3ziViHmSMNyR9O1xuzs4pU5Q==", "type": "package", "path": "System.Xml.ReaderWriter/4.0.11", "files": [ @@ -4130,7 +4130,7 @@ ] }, "System.Xml.XDocument/4.0.11": { - "sha512": "Mk2mKmPi0nWaoiYeotq1dgeNK1fqWh61+EK+w4Wu8SWuTYLzpUnschb59bJtGywaPq7SmTuPf44wrXRwbIrukg==", + "sha512": "7jMRvkdt108o4SRX9nQj7RJUPKe2gs+kGnyYDJgCf3iBfsFyzrGwr9XNoF+1WToK8rCH/DcBHhXyYe3h9zXcmw==", "type": "package", "path": "System.Xml.XDocument/4.0.11", "files": [ diff --git a/Integration Tests/Cookies/Base.cs b/Integration Tests/Cookies/Base.cs new file mode 100644 index 0000000..09a8e33 --- /dev/null +++ b/Integration Tests/Cookies/Base.cs @@ -0,0 +1,125 @@ +/* ********************************************************************* + * This Source Code Form is copyright of 51Degrees Mobile Experts Limited. + * Copyright © 2015 51Degrees Mobile Experts Limited, 5 Charlotte Close, + * Caversham, Reading, Berkshire, United Kingdom RG4 7BY + * + * This Source Code Form is the subject of the following patent + * applications, owned by 51Degrees Mobile Experts Limited of 5 Charlotte + * Close, Caversham, Reading, Berkshire, United Kingdom RG4 7BY: + * European Patent Application No. 13192291.6; and + * United States Patent Application Nos. 14/085,223 and 14/085,301. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. + * + * If a copy of the MPL was not distributed with this file, You can obtain + * one at http://mozilla.org/MPL/2.0/. + * + * This Source Code Form is “Incompatible With Secondary Licenses”, as + * defined by the Mozilla Public License, v. 2.0. + * ********************************************************************* */ + +using FiftyOne.Foundation.Mobile.Detection; +using FiftyOne.Foundation.Mobile.Detection.Entities; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Linq; +using System.Net; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using System.Web; + +namespace FiftyOne.Tests.Integration.Cookies +{ + public abstract class Base : IDisposable + { + protected abstract string DataFile { get; } + + /// + /// The data set to be used for the tests. + /// + protected DataSet _dataSet; + + /// + /// Sets random cookie values for properties which support JavaScript + /// Property Value Override and checks that they are returned + /// correctly. + /// + /// + /// The test checks that the multiple behaviours of + /// work when dynamic values are provided. + /// + /// + internal Utils.Results ProcessAll() + { + var target = new Uri("http://localhost"); + var provider = new Provider(_dataSet); + var match = provider.CreateMatch(); + var results = new FiftyOne.Tests.Integration.Utils.Results(); + var random = new Random(0); + var httpHeaders = _dataSet.HttpHeaders.Where(i => i.Equals("User-Agent") == false).ToArray(); + + // Loop through setting 2 User-Agent headers. + var userAgentIterator = UserAgentGenerator.GetRandomUserAgents().GetEnumerator(); + while (userAgentIterator.MoveNext()) + { + var headers = new NameValueCollection(); + headers.Add("User-Agent", userAgentIterator.Current); + + // Add a random value to the cookie. + var cookies = new CookieContainer(); + var testValues = new Dictionary(); + foreach (var property in _dataSet.JavaScriptProperties.Where(i => + i.Category.Equals(FiftyOne.Foundation.Mobile.Detection.Constants.PropertyValueOverrideCategory))) + { + var propertyName = property.Name.Replace("JavaScript", ""); + var key = FiftyOne.Foundation.Mobile.Detection.Constants.PropertyValueOverrideCookiePrefix + propertyName; + var value = UserAgentGenerator.GetRandomUserAgent(20); + cookies.Add(new Cookie(key, HttpUtility.UrlEncode(value), "/", target.Host)); + testValues.Add(_dataSet.Properties[propertyName], value); + } + headers.Add("Cookie", cookies.GetCookieHeader(target)); + + // Now check the match object returns the correct result + // for the property. This is the primary test in this + // integration test. + + // Note: + // The value returned from the match accessor is passed through + // UrlDecode as in this test .NET will not have already processed + // the cookie and decoded it. + provider.Match(headers, match); + foreach (var test in testValues) + { + Assert.IsTrue( + HttpUtility.UrlDecode(match[test.Key].ToString()).Equals(test.Value), + String.Format( + "Test failed for property '{0}'", + test.Key)); + } + + results.Methods[match.Method]++; + } + + return results; + } + + [TestCleanup] + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (_dataSet != null) + { + _dataSet.Dispose(); + } + } + } +} diff --git a/Integration Tests/Cookies/Enterprise/V31Array.cs b/Integration Tests/Cookies/Enterprise/V31Array.cs new file mode 100644 index 0000000..b7d8191 --- /dev/null +++ b/Integration Tests/Cookies/Enterprise/V31Array.cs @@ -0,0 +1,50 @@ +/* ********************************************************************* + * This Source Code Form is copyright of 51Degrees Mobile Experts Limited. + * Copyright © 2015 51Degrees Mobile Experts Limited, 5 Charlotte Close, + * Caversham, Reading, Berkshire, United Kingdom RG4 7BY + * + * This Source Code Form is the subject of the following patent + * applications, owned by 51Degrees Mobile Experts Limited of 5 Charlotte + * Close, Caversham, Reading, Berkshire, United Kingdom RG4 7BY: + * European Patent Application No. 13192291.6; and + * United States Patent Application Nos. 14/085,223 and 14/085,301. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. + * + * If a copy of the MPL was not distributed with this file, You can obtain + * one at http://mozilla.org/MPL/2.0/. + * + * This Source Code Form is “Incompatible With Secondary Licenses”, as + * defined by the Mozilla Public License, v. 2.0. + * ********************************************************************* */ +using System; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using FiftyOne.Foundation.Mobile.Detection.Factories; +using System.IO; + +namespace FiftyOne.Tests.Integration.Cookies.Enterprise +{ + [TestClass] + public class V31Array : Base + { + protected override string DataFile + { + get { return Utils.GetDataFile(Constants.ENTERPRISE_PATTERN_V31); } + } + + [TestInitialize()] + public void CreateDataSet() + { + Utils.CheckFileExists(DataFile); + _dataSet = StreamFactory.Create(File.ReadAllBytes(DataFile)); + } + + [TestMethod] + [TestCategory("Enterprise"), TestCategory("V31"), TestCategory("Cookies"), TestCategory("ProcessAll")] + public void EnterpriseV31Array_Cookies() + { + base.ProcessAll(); + } + } +} diff --git a/Integration Tests/Cookies/Enterprise/V32Array.cs b/Integration Tests/Cookies/Enterprise/V32Array.cs new file mode 100644 index 0000000..f9cd87f --- /dev/null +++ b/Integration Tests/Cookies/Enterprise/V32Array.cs @@ -0,0 +1,50 @@ +/* ********************************************************************* + * This Source Code Form is copyright of 51Degrees Mobile Experts Limited. + * Copyright © 2015 51Degrees Mobile Experts Limited, 5 Charlotte Close, + * Caversham, Reading, Berkshire, United Kingdom RG4 7BY + * + * This Source Code Form is the subject of the following patent + * applications, owned by 51Degrees Mobile Experts Limited of 5 Charlotte + * Close, Caversham, Reading, Berkshire, United Kingdom RG4 7BY: + * European Patent Application No. 13192291.6; and + * United States Patent Application Nos. 14/085,223 and 14/085,301. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. + * + * If a copy of the MPL was not distributed with this file, You can obtain + * one at http://mozilla.org/MPL/2.0/. + * + * This Source Code Form is “Incompatible With Secondary Licenses”, as + * defined by the Mozilla Public License, v. 2.0. + * ********************************************************************* */ +using System; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using FiftyOne.Foundation.Mobile.Detection.Factories; +using System.IO; + +namespace FiftyOne.Tests.Integration.Cookies.Enterprise +{ + [TestClass] + public class V32Array : Base + { + protected override string DataFile + { + get { return Utils.GetDataFile(Constants.ENTERPRISE_PATTERN_V32); } + } + + [TestInitialize()] + public void CreateDataSet() + { + Utils.CheckFileExists(DataFile); + _dataSet = StreamFactory.Create(File.ReadAllBytes(DataFile)); + } + + [TestMethod] + [TestCategory("Enterprise"), TestCategory("V32"), TestCategory("Cookies"), TestCategory("ProcessAll")] + public void EnterpriseV32Array_Cookies() + { + base.ProcessAll(); + } + } +} diff --git a/Integration Tests/Cookies/Lite/V31Array.cs b/Integration Tests/Cookies/Lite/V31Array.cs new file mode 100644 index 0000000..cb2e369 --- /dev/null +++ b/Integration Tests/Cookies/Lite/V31Array.cs @@ -0,0 +1,50 @@ +/* ********************************************************************* + * This Source Code Form is copyright of 51Degrees Mobile Experts Limited. + * Copyright © 2015 51Degrees Mobile Experts Limited, 5 Charlotte Close, + * Caversham, Reading, Berkshire, United Kingdom RG4 7BY + * + * This Source Code Form is the subject of the following patent + * applications, owned by 51Degrees Mobile Experts Limited of 5 Charlotte + * Close, Caversham, Reading, Berkshire, United Kingdom RG4 7BY: + * European Patent Application No. 13192291.6; and + * United States Patent Application Nos. 14/085,223 and 14/085,301. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. + * + * If a copy of the MPL was not distributed with this file, You can obtain + * one at http://mozilla.org/MPL/2.0/. + * + * This Source Code Form is “Incompatible With Secondary Licenses”, as + * defined by the Mozilla Public License, v. 2.0. + * ********************************************************************* */ +using System; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using FiftyOne.Foundation.Mobile.Detection.Factories; +using System.IO; + +namespace FiftyOne.Tests.Integration.Cookies.Lite +{ + [TestClass] + public class V31Array : Base + { + protected override string DataFile + { + get { return Utils.GetDataFile(Constants.LITE_PATTERN_V31); } + } + + [TestInitialize()] + public void CreateDataSet() + { + Utils.CheckFileExists(DataFile); + _dataSet = StreamFactory.Create(File.ReadAllBytes(DataFile)); + } + + [TestMethod] + [TestCategory("Lite"), TestCategory("V31"), TestCategory("Cookies"), TestCategory("ProcessAll")] + public void LiteV31Array_Cookies() + { + base.ProcessAll(); + } + } +} diff --git a/Integration Tests/Cookies/Lite/V32Array.cs b/Integration Tests/Cookies/Lite/V32Array.cs new file mode 100644 index 0000000..d77485f --- /dev/null +++ b/Integration Tests/Cookies/Lite/V32Array.cs @@ -0,0 +1,50 @@ +/* ********************************************************************* + * This Source Code Form is copyright of 51Degrees Mobile Experts Limited. + * Copyright © 2015 51Degrees Mobile Experts Limited, 5 Charlotte Close, + * Caversham, Reading, Berkshire, United Kingdom RG4 7BY + * + * This Source Code Form is the subject of the following patent + * applications, owned by 51Degrees Mobile Experts Limited of 5 Charlotte + * Close, Caversham, Reading, Berkshire, United Kingdom RG4 7BY: + * European Patent Application No. 13192291.6; and + * United States Patent Application Nos. 14/085,223 and 14/085,301. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. + * + * If a copy of the MPL was not distributed with this file, You can obtain + * one at http://mozilla.org/MPL/2.0/. + * + * This Source Code Form is “Incompatible With Secondary Licenses”, as + * defined by the Mozilla Public License, v. 2.0. + * ********************************************************************* */ +using System; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using FiftyOne.Foundation.Mobile.Detection.Factories; +using System.IO; + +namespace FiftyOne.Tests.Integration.Cookies.Lite +{ + [TestClass] + public class V32Array : Base + { + protected override string DataFile + { + get { return Utils.GetDataFile(Constants.LITE_PATTERN_V32); } + } + + [TestInitialize()] + public void CreateDataSet() + { + Utils.CheckFileExists(DataFile); + _dataSet = StreamFactory.Create(File.ReadAllBytes(DataFile)); + } + + [TestMethod] + [TestCategory("Lite"), TestCategory("V32"), TestCategory("Cookies"), TestCategory("ProcessAll")] + public void LiteV32Array_Cookies() + { + base.ProcessAll(); + } + } +} diff --git a/Integration Tests/Cookies/Premium/V31Array.cs b/Integration Tests/Cookies/Premium/V31Array.cs new file mode 100644 index 0000000..df0d31b --- /dev/null +++ b/Integration Tests/Cookies/Premium/V31Array.cs @@ -0,0 +1,50 @@ +/* ********************************************************************* + * This Source Code Form is copyright of 51Degrees Mobile Experts Limited. + * Copyright © 2015 51Degrees Mobile Experts Limited, 5 Charlotte Close, + * Caversham, Reading, Berkshire, United Kingdom RG4 7BY + * + * This Source Code Form is the subject of the following patent + * applications, owned by 51Degrees Mobile Experts Limited of 5 Charlotte + * Close, Caversham, Reading, Berkshire, United Kingdom RG4 7BY: + * European Patent Application No. 13192291.6; and + * United States Patent Application Nos. 14/085,223 and 14/085,301. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. + * + * If a copy of the MPL was not distributed with this file, You can obtain + * one at http://mozilla.org/MPL/2.0/. + * + * This Source Code Form is “Incompatible With Secondary Licenses”, as + * defined by the Mozilla Public License, v. 2.0. + * ********************************************************************* */ +using System; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using FiftyOne.Foundation.Mobile.Detection.Factories; +using System.IO; + +namespace FiftyOne.Tests.Integration.Cookies.Premium +{ + [TestClass] + public class V31Array : Base + { + protected override string DataFile + { + get { return Utils.GetDataFile(Constants.PREMIUM_PATTERN_V31); } + } + + [TestInitialize()] + public void CreateDataSet() + { + Utils.CheckFileExists(DataFile); + _dataSet = StreamFactory.Create(File.ReadAllBytes(DataFile)); + } + + [TestMethod] + [TestCategory("Premium"), TestCategory("V31"), TestCategory("Cookies"), TestCategory("ProcessAll")] + public void PremiumV31Array_Cookies() + { + base.ProcessAll(); + } + } +} diff --git a/Integration Tests/Cookies/Premium/V32Array.cs b/Integration Tests/Cookies/Premium/V32Array.cs new file mode 100644 index 0000000..880f3cb --- /dev/null +++ b/Integration Tests/Cookies/Premium/V32Array.cs @@ -0,0 +1,50 @@ +/* ********************************************************************* + * This Source Code Form is copyright of 51Degrees Mobile Experts Limited. + * Copyright © 2015 51Degrees Mobile Experts Limited, 5 Charlotte Close, + * Caversham, Reading, Berkshire, United Kingdom RG4 7BY + * + * This Source Code Form is the subject of the following patent + * applications, owned by 51Degrees Mobile Experts Limited of 5 Charlotte + * Close, Caversham, Reading, Berkshire, United Kingdom RG4 7BY: + * European Patent Application No. 13192291.6; and + * United States Patent Application Nos. 14/085,223 and 14/085,301. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. + * + * If a copy of the MPL was not distributed with this file, You can obtain + * one at http://mozilla.org/MPL/2.0/. + * + * This Source Code Form is “Incompatible With Secondary Licenses”, as + * defined by the Mozilla Public License, v. 2.0. + * ********************************************************************* */ +using System; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using FiftyOne.Foundation.Mobile.Detection.Factories; +using System.IO; + +namespace FiftyOne.Tests.Integration.Cookies.Premium +{ + [TestClass] + public class V32Array : Base + { + protected override string DataFile + { + get { return Utils.GetDataFile(Constants.PREMIUM_PATTERN_V32); } + } + + [TestInitialize()] + public void CreateDataSet() + { + Utils.CheckFileExists(DataFile); + _dataSet = StreamFactory.Create(File.ReadAllBytes(DataFile)); + } + + [TestMethod] + [TestCategory("Premium"), TestCategory("V32"), TestCategory("Cookies"), TestCategory("ProcessAll")] + public void PremiumV32Array_Cookies() + { + base.ProcessAll(); + } + } +} diff --git a/Integration Tests/Integration Tests.csproj b/Integration Tests/Integration Tests.csproj index 278111f..9d0fd74 100644 --- a/Integration Tests/Integration Tests.csproj +++ b/Integration Tests/Integration Tests.csproj @@ -65,6 +65,7 @@ + @@ -106,6 +107,13 @@ + + + + + + + diff --git a/Integration Tests/Performance/Asserts.cs b/Integration Tests/Performance/Asserts.cs index 41a648e..fe0993e 100644 --- a/Integration Tests/Performance/Asserts.cs +++ b/Integration Tests/Performance/Asserts.cs @@ -51,7 +51,7 @@ internal static void AssertCacheMissesGood(DataSet dataSet) internal static void AssertCacheMissesBadAll(DataSet dataSet) { Assert.IsTrue(dataSet.PercentageSignatureCacheMisses < 0.4, "Signature Cache Misses"); - Assert.IsTrue(dataSet.PercentageStringsCacheMisses < 0.5, "Strings Cache Misses"); + Assert.IsTrue(dataSet.PercentageStringsCacheMisses < 0.7, "Strings Cache Misses"); Assert.IsTrue(dataSet.PercentageNodeCacheMisses < 0.5, "Node Cache Misses"); Assert.IsTrue(dataSet.PercentageValuesCacheMisses < 0.3, "Value Cache Misses"); Assert.IsTrue(dataSet.PercentageProfilesCacheMisses < 0.3, "Profile Cache Misses"); diff --git a/README.md b/README.md index c5b0fe1..734a127 100644 --- a/README.md +++ b/README.md @@ -49,11 +49,10 @@ Data files which are updated weekly and daily, automatically, and with more prop ## Recent Changes -### Version 3.2.16 Highlights +### Version 3.2.17 Highlights -* .NET Core build of the core device detection API. Note that this utilises links to the existing source code files and excludes certain features like data file automatic updates and ASP.NET integration. Both an ASP.NET Core integration package and a native .NET Standard implementation of the API are planned for a future release. -* Updated cache template values and comments based on testing -* Updated lite data files for April 2017 +* Property value overrides have now been introduced from beta to stable +* Updated Lite data files for May 2017 ### Major Changes in Version 3.2