diff --git a/src/Akeneo/Akeneo.csproj b/src/Akeneo/Akeneo.csproj index d3c8b00..497af55 100644 --- a/src/Akeneo/Akeneo.csproj +++ b/src/Akeneo/Akeneo.csproj @@ -1,7 +1,7 @@  - netstandard1.5;net451 + netstandard2.0 Akeneo.NET 0.1.14 pardahlman @@ -13,19 +13,26 @@ https://github.com/pardahlman/akeneo-csharp git akeneo, pim + true + 1.5 - - NETSTANDARD1_5, LIBLOG_PORTABLE + + NETSTANDARD2_0, LIBLOG_PORTABLE - + + + - - - + + + all + runtime; build; native; contentfiles; analyzers + + \ No newline at end of file diff --git a/src/Akeneo/AkeneoClient.cs b/src/Akeneo/AkeneoClient.cs index c986b0d..ede363d 100644 --- a/src/Akeneo/AkeneoClient.cs +++ b/src/Akeneo/AkeneoClient.cs @@ -26,7 +26,7 @@ public class AkeneoClient : AkenioClientBase, IAkeneoClient private readonly SearchQueryBuilder _searchBuilder; private readonly ILog _logger = LogProvider.For(); - public AkeneoClient(AkeneoOptions options) + public AkeneoClient(AkeneoOptions options) : this(options.ApiEndpoint, new AuthenticationClient(options.ApiEndpoint, options.ClientId, options.ClientSecret, options.UserName, options.Password)) { } public AkeneoClient(Uri apiEndPoint, IAuthenticationClient authClient) : base(apiEndPoint, authClient) @@ -46,7 +46,14 @@ public AkeneoClient(Uri apiEndPoint, IAuthenticationClient authClient) : base(ap : default(TModel); } - public async Task GetAsync(string parentCode, string code, CancellationToken ct = default(CancellationToken)) where TModel : ModelBase + public async Task GetByUrl(string url, CancellationToken ct = default(CancellationToken)) + { + _logger.Debug($"Getting resource from URL '{url}'."); + HttpResponseMessage response = await GetAsync(url, ct); + return await response.Content.ReadAsStringAsync(); + } + + public async Task GetAsync(string parentCode, string code, CancellationToken ct = default(CancellationToken)) where TModel : ModelBase { var endpoint = _endpointResolver.ForResource(parentCode, code); _logger.Debug($"Getting resource '{typeof(TModel).Name}' from URL '{endpoint}'."); @@ -62,7 +69,13 @@ public AkeneoClient(Uri apiEndPoint, IAuthenticationClient authClient) : base(ap return FilterAsync(queryString, ct); } - public async Task> FilterAsync(string queryString, CancellationToken ct = default(CancellationToken)) where TModel : ModelBase + public Task> SearchAndGetAllAsync(IEnumerable criterias, CancellationToken ct = default(CancellationToken)) where TModel : ModelBase + { + var queryString = _searchBuilder.GetQueryStringParam(criterias); + return FilterAndGetAllAsync(queryString, null, ct); + } + + public async Task> FilterAsync(string queryString, CancellationToken ct = default(CancellationToken)) where TModel : ModelBase { var endpoint = _endpointResolver.ForResourceType(); _logger.Debug($"Filtering resource '{typeof(TModel).Name}' from URL '{endpoint}' with query '{queryString}'."); @@ -71,13 +84,36 @@ public AkeneoClient(Uri apiEndPoint, IAuthenticationClient authClient) : base(ap result.Code = response.StatusCode; return result; } + public async Task> FilterAndGetAllAsync(string queryString, string cursor = null, CancellationToken ct = default(CancellationToken)) where TModel : ModelBase + { + var endpoint = _endpointResolver.WithSearchAfter(100, cursor); + _logger.Debug($"Filtering resource '{typeof(TModel).Name}' from URL '{endpoint}' with query '{queryString}'."); + var response = await GetAsync($"{endpoint}{queryString}", ct); + + if (!response.IsSuccessStatusCode) throw new HttpRequestException($"Error while executing GET reques: {endpoint}{queryString}"); + + PaginationResult paginationResult = await response.Content.ReadAsJsonAsync>(); + paginationResult.Code = response.StatusCode; + + List items = paginationResult.GetItems(); - public Task> GetManyAsync(int page = 1, int limit = 10, bool withCount = false, CancellationToken ct = default(CancellationToken)) where TModel : ModelBase + var nextCursor = paginationResult.Links.GetCursor(); + + if (nextCursor != null) + { + var nextItems = await FilterAndGetAllAsync(queryString, nextCursor, ct); + items.AddRange(nextItems); + } + + return items; + } + + public Task> GetManyAsync(int page = 1, int limit = 10, bool withCount = false, CancellationToken ct = default(CancellationToken)) where TModel : ModelBase { return GetManyAsync(null, page, limit, withCount, ct); } - public async Task> GetManyAsync(string parentCode, int page = 1, int limit = 10, bool withCount = false, CancellationToken ct = default(CancellationToken)) where TModel : ModelBase + public async Task> GetManyAsync(string parentCode, int page = 1, int limit = 10, bool withCount = false, CancellationToken ct = default(CancellationToken)) where TModel : ModelBase { var endpoint = _endpointResolver.ForPagination(parentCode, page, limit, withCount); _logger.Debug($"Getting multiple resource '{typeof(TModel).Name}' from URL '{endpoint}'."); @@ -87,6 +123,28 @@ public AkeneoClient(Uri apiEndPoint, IAuthenticationClient authClient) : base(ap : PaginationResult.Empty; } + public async Task> GetAllAsync(string cursor = null, CancellationToken ct = default(CancellationToken)) where TModel : ModelBase + { + var endpoint = _endpointResolver.WithSearchAfter(100, cursor); + _logger.Debug($"Getting multiple resource '{typeof(TModel).Name}' from URL '{endpoint}'."); + var response = await GetAsync(endpoint, ct); + + if (!response.IsSuccessStatusCode) return new List(); + + PaginationResult paginationResult = await response.Content.ReadAsJsonAsync>(); + var items = paginationResult.GetItems(); + + var nextCursor = paginationResult.Links.GetCursor(); + + if (nextCursor != null) + { + var nextItems = await GetAllAsync(nextCursor, ct); + items.AddRange(nextItems); + } + + return items; + } + public async Task CreateAsync(TModel model, CancellationToken ct = default(CancellationToken)) where TModel : ModelBase { var option = model as AttributeOption; diff --git a/src/Akeneo/Client/PaginationResult.cs b/src/Akeneo/Client/PaginationResult.cs index b81187f..e556328 100644 --- a/src/Akeneo/Client/PaginationResult.cs +++ b/src/Akeneo/Client/PaginationResult.cs @@ -1,7 +1,9 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Net; +using System.Web; using Newtonsoft.Json; namespace Akeneo.Client @@ -39,11 +41,22 @@ public static List GetItems(this PaginationResult result) public static class PaginationDictionaryExtensions { private static readonly string Next = "next"; + private static readonly string SearchAfter = "search_after"; public static PaginationLink GetNext(this IDictionary links) { return links.ContainsKey(Next) ? links[Next] : null; } + + public static string GetCursor(this IDictionary links) + { + if (!links.TryGetValue(Next, out var link)) return null; + + Uri myUri = new Uri(link.Href); + string cursor = HttpUtility.ParseQueryString(myUri.Query).Get(SearchAfter); + cursor = WebUtility.UrlEncode(cursor); + return cursor; + } } diff --git a/src/Akeneo/Common/EndpointResolver.cs b/src/Akeneo/Common/EndpointResolver.cs index 9c9d45d..bff3d10 100644 --- a/src/Akeneo/Common/EndpointResolver.cs +++ b/src/Akeneo/Common/EndpointResolver.cs @@ -15,6 +15,8 @@ public class EndpointResolver private static readonly Type Product = typeof(Product); private static readonly Type MediaFile = typeof(MediaFile); private static readonly Type Locale = typeof(Locale); + private static readonly Type ProductModel = typeof(ProductModel); + private static readonly Type FamilyVariant = typeof(FamilyVariant); private readonly ConcurrentDictionary _typeToEndpointCache; @@ -116,6 +118,14 @@ protected virtual string GetResourceEndpoint(Type modelType) { return Endpoints.Locale; } + if (ProductModel.GetTypeInfo().IsAssignableFrom(type)) + { + return Endpoints.ProductModel; + } + if (FamilyVariant.GetTypeInfo().IsAssignableFrom(type)) + { + return Endpoints.FamilyVariant; + } throw new NotSupportedException($"Unable to find API endpoint for type {modelType.FullName}"); }); } @@ -131,5 +141,13 @@ public string ForPagination(string parentCode, int page = 1, int limit = var baseUrl = ForResourceType(parentCode); return $"{baseUrl}?page={page}&limit={limit}&with_count={withCount.ToString().ToLower()}"; } + + public string WithSearchAfter(int limit = 100, string searchAfter = null) where TModel : ModelBase + { + var baseUrl = ForResourceType(); + return searchAfter == null + ? $"{baseUrl}?pagination_type=search_after&limit={limit}" + : $"{baseUrl}?pagination_type=search_after&limit={limit}&search_after={searchAfter}"; + } } } diff --git a/src/Akeneo/Common/Endpoints.cs b/src/Akeneo/Common/Endpoints.cs index cc2a46b..64febf8 100644 --- a/src/Akeneo/Common/Endpoints.cs +++ b/src/Akeneo/Common/Endpoints.cs @@ -9,5 +9,7 @@ public class Endpoints public const string Families = "api/rest/v1/families"; public const string MediaFiles = "api/rest/v1/media-files"; public const string Locale = "api/rest/v1/locales"; + public const string ProductModel = "api/rest/v1/product-models"; + public const string FamilyVariant = "/api/rest/v1/families/{1}/variants"; } -} +} \ No newline at end of file diff --git a/src/Akeneo/Logging/LibLog.cs b/src/Akeneo/Logging/LibLog.cs deleted file mode 100644 index c90d5ff..0000000 --- a/src/Akeneo/Logging/LibLog.cs +++ /dev/null @@ -1,2380 +0,0 @@ -//=============================================================================== -// LibLog -// -// https://github.com/damianh/LibLog -//=============================================================================== -// Copyright © 2011-2015 Damian Hickey. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -//=============================================================================== - -// ReSharper disable PossibleNullReferenceException - -// Define LIBLOG_PORTABLE conditional compilation symbol for PCL compatibility -// -// Define LIBLOG_PUBLIC to enable ability to GET a logger (LogProvider.For<>() etc) from outside this library. NOTE: -// this can have unintended consequences of consumers of your library using your library to resolve a logger. If the -// reason is because you want to open this functionality to other projects within your solution, -// consider [InternalsVisibleTo] instead. -// -// Define LIBLOG_PROVIDERS_ONLY if your library provides its own logging API and you just want to use the -// LibLog providers internally to provide built in support for popular logging frameworks. - -#pragma warning disable 1591 - -using System.Diagnostics.CodeAnalysis; - -[assembly: SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "Akeneo.Logging")] -[assembly: SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Scope = "member", Target = "Akeneo.Logging.Logger.#Invoke(Akeneo.Logging.LogLevel,System.Func`1,System.Exception,System.Object[])")] - -// If you copied this file manually, you need to change all "Akeneo" so not to clash with other libraries -// that use LibLog -#if LIBLOG_PROVIDERS_ONLY -namespace Akeneo.LibLog -#else -namespace Akeneo.Logging -#endif -{ - using System.Collections.Generic; - using System.Diagnostics.CodeAnalysis; -#if LIBLOG_PROVIDERS_ONLY - using Akeneo.LibLog.LogProviders; -#else - using Akeneo.Logging.LogProviders; -#endif - using System; -#if !LIBLOG_PROVIDERS_ONLY - using System.Diagnostics; -#if !LIBLOG_PORTABLE - using System.Runtime.CompilerServices; -#endif -#endif - -#if LIBLOG_PROVIDERS_ONLY - internal -#else - public -#endif - delegate bool Logger(LogLevel logLevel, Func messageFunc, Exception exception = null, params object[] formatParameters); - -#if !LIBLOG_PROVIDERS_ONLY - /// - /// Simple interface that represent a logger. - /// -#if LIBLOG_PUBLIC - public -#else - internal -#endif - interface ILog - { - /// - /// Log a message the specified log level. - /// - /// The log level. - /// The message function. - /// An optional exception. - /// Optional format parameters for the message generated by the messagefunc. - /// true if the message was logged. Otherwise false. - /// - /// Note to implementers: the message func should not be called if the loglevel is not enabled - /// so as not to incur performance penalties. - /// - /// To check IsEnabled call Log with only LogLevel and check the return value, no event will be written. - /// - bool Log(LogLevel logLevel, Func messageFunc, Exception exception = null, params object[] formatParameters ); - } -#endif - - /// - /// The log level. - /// -#if LIBLOG_PROVIDERS_ONLY - internal -#else - public -#endif - enum LogLevel - { - Trace, - Debug, - Info, - Warn, - Error, - Fatal - } - -#if !LIBLOG_PROVIDERS_ONLY -#if !LIBLOG_PORTABLE - [ExcludeFromCodeCoverage] -#endif -#if LIBLOG_PUBLIC - public -#else - internal -#endif - static partial class LogExtensions - { - public static bool IsDebugEnabled(this ILog logger) - { - GuardAgainstNullLogger(logger); - return logger.Log(LogLevel.Debug, null); - } - - public static bool IsErrorEnabled(this ILog logger) - { - GuardAgainstNullLogger(logger); - return logger.Log(LogLevel.Error, null); - } - - public static bool IsFatalEnabled(this ILog logger) - { - GuardAgainstNullLogger(logger); - return logger.Log(LogLevel.Fatal, null); - } - - public static bool IsInfoEnabled(this ILog logger) - { - GuardAgainstNullLogger(logger); - return logger.Log(LogLevel.Info, null); - } - - public static bool IsTraceEnabled(this ILog logger) - { - GuardAgainstNullLogger(logger); - return logger.Log(LogLevel.Trace, null); - } - - public static bool IsWarnEnabled(this ILog logger) - { - GuardAgainstNullLogger(logger); - return logger.Log(LogLevel.Warn, null); - } - - public static void Debug(this ILog logger, Func messageFunc) - { - GuardAgainstNullLogger(logger); - logger.Log(LogLevel.Debug, messageFunc); - } - - public static void Debug(this ILog logger, string message) - { - if (logger.IsDebugEnabled()) - { - logger.Log(LogLevel.Debug, message.AsFunc()); - } - } - - public static void Debug(this ILog logger, string message, params object[] args) - { - logger.DebugFormat(message, args); - } - - public static void Debug(this ILog logger, Exception exception, string message, params object[] args) - { - logger.DebugException(message, exception, args); - } - - public static void DebugFormat(this ILog logger, string message, params object[] args) - { - if (logger.IsDebugEnabled()) - { - logger.LogFormat(LogLevel.Debug, message, args); - } - } - - public static void DebugException(this ILog logger, string message, Exception exception) - { - if (logger.IsDebugEnabled()) - { - logger.Log(LogLevel.Debug, message.AsFunc(), exception); - } - } - - public static void DebugException(this ILog logger, string message, Exception exception, params object[] formatParams) - { - if (logger.IsDebugEnabled()) - { - logger.Log(LogLevel.Debug, message.AsFunc(), exception, formatParams); - } - } - - public static void Error(this ILog logger, Func messageFunc) - { - GuardAgainstNullLogger(logger); - logger.Log(LogLevel.Error, messageFunc); - } - - public static void Error(this ILog logger, string message) - { - if (logger.IsErrorEnabled()) - { - logger.Log(LogLevel.Error, message.AsFunc()); - } - } - - public static void Error(this ILog logger, string message, params object[] args) - { - logger.ErrorFormat(message, args); - } - - public static void Error(this ILog logger, Exception exception, string message, params object[] args) - { - logger.ErrorException(message, exception, args); - } - - public static void ErrorFormat(this ILog logger, string message, params object[] args) - { - if (logger.IsErrorEnabled()) - { - logger.LogFormat(LogLevel.Error, message, args); - } - } - - public static void ErrorException(this ILog logger, string message, Exception exception, params object[] formatParams) - { - if (logger.IsErrorEnabled()) - { - logger.Log(LogLevel.Error, message.AsFunc(), exception, formatParams); - } - } - - public static void Fatal(this ILog logger, Func messageFunc) - { - logger.Log(LogLevel.Fatal, messageFunc); - } - - public static void Fatal(this ILog logger, string message) - { - if (logger.IsFatalEnabled()) - { - logger.Log(LogLevel.Fatal, message.AsFunc()); - } - } - - public static void Fatal(this ILog logger, string message, params object[] args) - { - logger.FatalFormat(message, args); - } - - public static void Fatal(this ILog logger, Exception exception, string message, params object[] args) - { - logger.FatalException(message, exception, args); - } - - public static void FatalFormat(this ILog logger, string message, params object[] args) - { - if (logger.IsFatalEnabled()) - { - logger.LogFormat(LogLevel.Fatal, message, args); - } - } - - public static void FatalException(this ILog logger, string message, Exception exception, params object[] formatParams) - { - if (logger.IsFatalEnabled()) - { - logger.Log(LogLevel.Fatal, message.AsFunc(), exception, formatParams); - } - } - - public static void Info(this ILog logger, Func messageFunc) - { - GuardAgainstNullLogger(logger); - logger.Log(LogLevel.Info, messageFunc); - } - - public static void Info(this ILog logger, string message) - { - if (logger.IsInfoEnabled()) - { - logger.Log(LogLevel.Info, message.AsFunc()); - } - } - - public static void Info(this ILog logger, string message, params object[] args) - { - logger.InfoFormat(message, args); - } - - public static void Info(this ILog logger, Exception exception, string message, params object[] args) - { - logger.InfoException(message, exception, args); - } - - public static void InfoFormat(this ILog logger, string message, params object[] args) - { - if (logger.IsInfoEnabled()) - { - logger.LogFormat(LogLevel.Info, message, args); - } - } - - public static void InfoException(this ILog logger, string message, Exception exception, params object[] formatParams) - { - if (logger.IsInfoEnabled()) - { - logger.Log(LogLevel.Info, message.AsFunc(), exception, formatParams); - } - } - - public static void Trace(this ILog logger, Func messageFunc) - { - GuardAgainstNullLogger(logger); - logger.Log(LogLevel.Trace, messageFunc); - } - - public static void Trace(this ILog logger, string message) - { - if (logger.IsTraceEnabled()) - { - logger.Log(LogLevel.Trace, message.AsFunc()); - } - } - - public static void Trace(this ILog logger, string message, params object[] args) - { - logger.TraceFormat(message, args); - } - - public static void Trace(this ILog logger, Exception exception, string message, params object[] args) - { - logger.TraceException(message, exception, args); - } - - public static void TraceFormat(this ILog logger, string message, params object[] args) - { - if (logger.IsTraceEnabled()) - { - logger.LogFormat(LogLevel.Trace, message, args); - } - } - - public static void TraceException(this ILog logger, string message, Exception exception, params object[] formatParams) - { - if (logger.IsTraceEnabled()) - { - logger.Log(LogLevel.Trace, message.AsFunc(), exception, formatParams); - } - } - - public static void Warn(this ILog logger, Func messageFunc) - { - GuardAgainstNullLogger(logger); - logger.Log(LogLevel.Warn, messageFunc); - } - - public static void Warn(this ILog logger, string message) - { - if (logger.IsWarnEnabled()) - { - logger.Log(LogLevel.Warn, message.AsFunc()); - } - } - - public static void Warn(this ILog logger, string message, params object[] args) - { - logger.WarnFormat(message, args); - } - - public static void Warn(this ILog logger, Exception exception, string message, params object[] args) - { - logger.WarnException(message, exception, args); - } - - public static void WarnFormat(this ILog logger, string message, params object[] args) - { - if (logger.IsWarnEnabled()) - { - logger.LogFormat(LogLevel.Warn, message, args); - } - } - - public static void WarnException(this ILog logger, string message, Exception exception, params object[] formatParams) - { - if (logger.IsWarnEnabled()) - { - logger.Log(LogLevel.Warn, message.AsFunc(), exception, formatParams); - } - } - - // ReSharper disable once UnusedParameter.Local - private static void GuardAgainstNullLogger(ILog logger) - { - if (logger == null) - { - throw new ArgumentNullException("logger"); - } - } - - private static void LogFormat(this ILog logger, LogLevel logLevel, string message, params object[] args) - { - logger.Log(logLevel, message.AsFunc(), null, args); - } - - // Avoid the closure allocation, see https://gist.github.com/AArnott/d285feef75c18f6ecd2b - private static Func AsFunc(this T value) where T : class - { - return value.Return; - } - - private static T Return(this T value) - { - return value; - } - } -#endif - - /// - /// Represents a way to get a - /// -#if LIBLOG_PROVIDERS_ONLY - internal -#else - public -#endif - interface ILogProvider - { - /// - /// Gets the specified named logger. - /// - /// Name of the logger. - /// The logger reference. - Logger GetLogger(string name); - - /// - /// Opens a nested diagnostics context. Not supported in EntLib logging. - /// - /// The message to add to the diagnostics context. - /// A disposable that when disposed removes the message from the context. - IDisposable OpenNestedContext(string message); - - /// - /// Opens a mapped diagnostics context. Not supported in EntLib logging. - /// - /// A key. - /// A value. - /// A disposable that when disposed removes the map from the context. - IDisposable OpenMappedContext(string key, string value); - } - - /// - /// Provides a mechanism to create instances of objects. - /// -#if !LIBLOG_PORTABLE - [ExcludeFromCodeCoverage] -#endif -#if LIBLOG_PROVIDERS_ONLY - internal -#else - public -#endif - static class LogProvider - { -#if !LIBLOG_PROVIDERS_ONLY - private const string NullLogProvider = "Current Log Provider is not set. Call SetCurrentLogProvider " + - "with a non-null value first."; - private static dynamic s_currentLogProvider; - private static Action s_onCurrentLogProviderSet; - - [SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline")] - static LogProvider() - { - IsDisabled = false; - } - - /// - /// Sets the current log provider. - /// - /// The log provider. - public static void SetCurrentLogProvider(ILogProvider logProvider) - { - s_currentLogProvider = logProvider; - - RaiseOnCurrentLogProviderSet(); - } - - /// - /// Gets or sets a value indicating whether this is logging is disabled. - /// - /// - /// true if logging is disabled; otherwise, false. - /// - public static bool IsDisabled { get; set; } - - /// - /// Sets an action that is invoked when a consumer of your library has called SetCurrentLogProvider. It is - /// important that hook into this if you are using child libraries (especially ilmerged ones) that are using - /// LibLog (or other logging abstraction) so you adapt and delegate to them. - /// - /// - internal static Action OnCurrentLogProviderSet - { - set - { - s_onCurrentLogProviderSet = value; - RaiseOnCurrentLogProviderSet(); - } - } - - internal static ILogProvider CurrentLogProvider - { - get - { - return s_currentLogProvider; - } - } - - /// - /// Gets a logger for the specified type. - /// - /// The type whose name will be used for the logger. - /// An instance of -#if LIBLOG_PUBLIC - public -#else - internal -#endif - static ILog For() - { - return GetLogger(typeof(T)); - } - -#if !LIBLOG_PORTABLE - /// - /// Gets a logger for the current class. - /// - /// An instance of - [MethodImpl(MethodImplOptions.NoInlining)] -#if LIBLOG_PUBLIC - public -#else - internal -#endif - static ILog GetCurrentClassLogger() - { - var stackFrame = new StackFrame(1, false); - return GetLogger(stackFrame.GetMethod().DeclaringType); - } -#endif - - /// - /// Gets a logger for the specified type. - /// - /// The type whose name will be used for the logger. - /// If the type is null then this name will be used as the log name instead - /// An instance of -#if LIBLOG_PUBLIC - public -#else - internal -#endif - static ILog GetLogger(Type type, string fallbackTypeName = "System.Object") - { - // If the type passed in is null then fallback to the type name specified - return GetLogger(type != null ? type.FullName : fallbackTypeName); - } - - /// - /// Gets a logger with the specified name. - /// - /// The name. - /// An instance of -#if LIBLOG_PUBLIC - public -#else - internal -#endif - static ILog GetLogger(string name) - { - ILogProvider logProvider = CurrentLogProvider ?? ResolveLogProvider(); - return logProvider == null - ? NoOpLogger.Instance - : (ILog)new LoggerExecutionWrapper(logProvider.GetLogger(name), () => IsDisabled); - } - - /// - /// Opens a nested diagnostics context. - /// - /// A message. - /// An that closes context when disposed. - [SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "SetCurrentLogProvider")] -#if LIBLOG_PUBLIC - public -#else - internal -#endif - static IDisposable OpenNestedContext(string message) - { - ILogProvider logProvider = CurrentLogProvider ?? ResolveLogProvider(); - - return logProvider == null - ? new DisposableAction(() => { }) - : logProvider.OpenNestedContext(message); - } - - /// - /// Opens a mapped diagnostics context. - /// - /// A key. - /// A value. - /// An that closes context when disposed. - [SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "SetCurrentLogProvider")] -#if LIBLOG_PUBLIC - public -#else - internal -#endif - static IDisposable OpenMappedContext(string key, string value) - { - ILogProvider logProvider = CurrentLogProvider ?? ResolveLogProvider(); - - return logProvider == null - ? new DisposableAction(() => { }) - : logProvider.OpenMappedContext(key, value); - } -#endif - -#if LIBLOG_PROVIDERS_ONLY - private -#else - internal -#endif - delegate bool IsLoggerAvailable(); - -#if LIBLOG_PROVIDERS_ONLY - private -#else - internal -#endif - delegate ILogProvider CreateLogProvider(); - -#if LIBLOG_PROVIDERS_ONLY - private -#else - internal -#endif - static readonly List> LogProviderResolvers = - new List> - { - new Tuple(SerilogLogProvider.IsLoggerAvailable, () => new SerilogLogProvider()), - new Tuple(NLogLogProvider.IsLoggerAvailable, () => new NLogLogProvider()), - new Tuple(Log4NetLogProvider.IsLoggerAvailable, () => new Log4NetLogProvider()), - new Tuple(EntLibLogProvider.IsLoggerAvailable, () => new EntLibLogProvider()), - new Tuple(LoupeLogProvider.IsLoggerAvailable, () => new LoupeLogProvider()), - }; - -#if !LIBLOG_PROVIDERS_ONLY - private static void RaiseOnCurrentLogProviderSet() - { - if (s_onCurrentLogProviderSet != null) - { - s_onCurrentLogProviderSet(s_currentLogProvider); - } - } -#endif - - [SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters", MessageId = "System.Console.WriteLine(System.String,System.Object,System.Object)")] - [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] - internal static ILogProvider ResolveLogProvider() - { - try - { - foreach (var providerResolver in LogProviderResolvers) - { - if (providerResolver.Item1()) - { - return providerResolver.Item2(); - } - } - } - catch (Exception ex) - { -#if LIBLOG_PORTABLE - Debug.WriteLine( -#else - Console.WriteLine( -#endif - "Exception occurred resolving a log provider. Logging for this assembly {0} is disabled. {1}", - typeof(LogProvider).GetAssemblyPortable().FullName, - ex); - } - return null; - } - -#if !LIBLOG_PROVIDERS_ONLY -#if !LIBLOG_PORTABLE - [ExcludeFromCodeCoverage] -#endif - internal class NoOpLogger : ILog - { - internal static readonly NoOpLogger Instance = new NoOpLogger(); - - public bool Log(LogLevel logLevel, Func messageFunc, Exception exception, params object[] formatParameters) - { - return false; - } - } -#endif - } - -#if !LIBLOG_PROVIDERS_ONLY -#if !LIBLOG_PORTABLE - [ExcludeFromCodeCoverage] -#endif - internal class LoggerExecutionWrapper : ILog - { - private readonly Logger _logger; - private readonly Func _getIsDisabled; - internal const string FailedToGenerateLogMessage = "Failed to generate log message"; - - internal LoggerExecutionWrapper(Logger logger, Func getIsDisabled = null) - { - _logger = logger; - _getIsDisabled = getIsDisabled ?? (() => false); - } - - internal Logger WrappedLogger - { - get { return _logger; } - } - - [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] - public bool Log(LogLevel logLevel, Func messageFunc, Exception exception = null, params object[] formatParameters) - { - if (_getIsDisabled()) - { - return false; - } - if (messageFunc == null) - { - return _logger(logLevel, null); - } - - Func wrappedMessageFunc = () => - { - try - { - return messageFunc(); - } - catch (Exception ex) - { - Log(LogLevel.Error, () => FailedToGenerateLogMessage, ex); - } - return null; - }; - return _logger(logLevel, wrappedMessageFunc, exception, formatParameters); - } - } -#endif -} - -#if LIBLOG_PROVIDERS_ONLY -namespace Akeneo.LibLog.LogProviders -#else -namespace Akeneo.Logging.LogProviders -#endif -{ - using System; - using System.Collections.Generic; - using System.Diagnostics.CodeAnalysis; -#if !LIBLOG_PORTABLE - using System.Diagnostics; -#endif - using System.Globalization; - using System.Linq; - using System.Linq.Expressions; - using System.Reflection; -#if !LIBLOG_PORTABLE - using System.Text; -#endif - using System.Text.RegularExpressions; - -#if !LIBLOG_PORTABLE - [ExcludeFromCodeCoverage] -#endif - internal abstract class LogProviderBase : ILogProvider - { - protected delegate IDisposable OpenNdc(string message); - protected delegate IDisposable OpenMdc(string key, string value); - - private readonly Lazy _lazyOpenNdcMethod; - private readonly Lazy _lazyOpenMdcMethod; - private static readonly IDisposable NoopDisposableInstance = new DisposableAction(); - - protected LogProviderBase() - { - _lazyOpenNdcMethod - = new Lazy(GetOpenNdcMethod); - _lazyOpenMdcMethod - = new Lazy(GetOpenMdcMethod); - } - - public abstract Logger GetLogger(string name); - - public IDisposable OpenNestedContext(string message) - { - return _lazyOpenNdcMethod.Value(message); - } - - public IDisposable OpenMappedContext(string key, string value) - { - return _lazyOpenMdcMethod.Value(key, value); - } - - protected virtual OpenNdc GetOpenNdcMethod() - { - return _ => NoopDisposableInstance; - } - - protected virtual OpenMdc GetOpenMdcMethod() - { - return (_, __) => NoopDisposableInstance; - } - } - -#if !LIBLOG_PORTABLE - [ExcludeFromCodeCoverage] -#endif - internal class NLogLogProvider : LogProviderBase - { - private readonly Func _getLoggerByNameDelegate; - private static bool s_providerIsAvailableOverride = true; - - [SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "LogManager")] - [SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "NLog")] - public NLogLogProvider() - { - if (!IsLoggerAvailable()) - { - throw new InvalidOperationException("NLog.LogManager not found"); - } - _getLoggerByNameDelegate = GetGetLoggerMethodCall(); - } - - public static bool ProviderIsAvailableOverride - { - get { return s_providerIsAvailableOverride; } - set { s_providerIsAvailableOverride = value; } - } - - public override Logger GetLogger(string name) - { - return new NLogLogger(_getLoggerByNameDelegate(name)).Log; - } - - public static bool IsLoggerAvailable() - { - return ProviderIsAvailableOverride && GetLogManagerType() != null; - } - - protected override OpenNdc GetOpenNdcMethod() - { - Type ndcContextType = Type.GetType("NLog.NestedDiagnosticsContext, NLog"); - MethodInfo pushMethod = ndcContextType.GetMethodPortable("Push", typeof(string)); - ParameterExpression messageParam = Expression.Parameter(typeof(string), "message"); - MethodCallExpression pushMethodCall = Expression.Call(null, pushMethod, messageParam); - return Expression.Lambda(pushMethodCall, messageParam).Compile(); - } - - protected override OpenMdc GetOpenMdcMethod() - { - Type mdcContextType = Type.GetType("NLog.MappedDiagnosticsContext, NLog"); - - MethodInfo setMethod = mdcContextType.GetMethodPortable("Set", typeof(string), typeof(string)); - MethodInfo removeMethod = mdcContextType.GetMethodPortable("Remove", typeof(string)); - ParameterExpression keyParam = Expression.Parameter(typeof(string), "key"); - ParameterExpression valueParam = Expression.Parameter(typeof(string), "value"); - - MethodCallExpression setMethodCall = Expression.Call(null, setMethod, keyParam, valueParam); - MethodCallExpression removeMethodCall = Expression.Call(null, removeMethod, keyParam); - - Action set = Expression - .Lambda>(setMethodCall, keyParam, valueParam) - .Compile(); - Action remove = Expression - .Lambda>(removeMethodCall, keyParam) - .Compile(); - - return (key, value) => - { - set(key, value); - return new DisposableAction(() => remove(key)); - }; - } - - private static Type GetLogManagerType() - { - return Type.GetType("NLog.LogManager, NLog"); - } - - private static Func GetGetLoggerMethodCall() - { - Type logManagerType = GetLogManagerType(); - MethodInfo method = logManagerType.GetMethodPortable("GetLogger", typeof(string)); - ParameterExpression nameParam = Expression.Parameter(typeof(string), "name"); - MethodCallExpression methodCall = Expression.Call(null, method, nameParam); - return Expression.Lambda>(methodCall, nameParam).Compile(); - } - -#if !LIBLOG_PORTABLE - [ExcludeFromCodeCoverage] -#endif - internal class NLogLogger - { - private readonly dynamic _logger; - - private static Func _logEventInfoFact; - - private static readonly object _levelTrace; - private static readonly object _levelDebug; - private static readonly object _levelInfo; - private static readonly object _levelWarn; - private static readonly object _levelError; - private static readonly object _levelFatal; - - static NLogLogger() - { - try - { - var logEventLevelType = Type.GetType("NLog.LogLevel, NLog"); - if (logEventLevelType == null) - { - throw new InvalidOperationException("Type NLog.LogLevel was not found."); - } - - var levelFields = logEventLevelType.GetFieldsPortable().ToList(); - _levelTrace = levelFields.First(x => x.Name == "Trace").GetValue(null); - _levelDebug = levelFields.First(x => x.Name == "Debug").GetValue(null); - _levelInfo = levelFields.First(x => x.Name == "Info").GetValue(null); - _levelWarn = levelFields.First(x => x.Name == "Warn").GetValue(null); - _levelError = levelFields.First(x => x.Name == "Error").GetValue(null); - _levelFatal = levelFields.First(x => x.Name == "Fatal").GetValue(null); - - var logEventInfoType = Type.GetType("NLog.LogEventInfo, NLog"); - if (logEventInfoType == null) - { - throw new InvalidOperationException("Type NLog.LogEventInfo was not found."); - } - MethodInfo createLogEventInfoMethodInfo = logEventInfoType.GetMethodPortable("Create", - logEventLevelType, typeof(string), typeof(Exception), typeof(IFormatProvider), typeof(string), typeof(object[])); - ParameterExpression loggerNameParam = Expression.Parameter(typeof(string)); - ParameterExpression levelParam = Expression.Parameter(typeof(object)); - ParameterExpression messageParam = Expression.Parameter(typeof(string)); - ParameterExpression exceptionParam = Expression.Parameter(typeof(Exception)); - UnaryExpression levelCast = Expression.Convert(levelParam, logEventLevelType); - MethodCallExpression createLogEventInfoMethodCall = Expression.Call(null, - createLogEventInfoMethodInfo, - levelCast, loggerNameParam, exceptionParam, - Expression.Constant(null, typeof(IFormatProvider)), messageParam, Expression.Constant(null, typeof(object[]))); - _logEventInfoFact = Expression.Lambda>(createLogEventInfoMethodCall, - loggerNameParam, levelParam, messageParam, exceptionParam).Compile(); - } - catch { } - } - - internal NLogLogger(dynamic logger) - { - _logger = logger; - } - - [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] - public bool Log(LogLevel logLevel, Func messageFunc, Exception exception, params object[] formatParameters) - { - if (messageFunc == null) - { - return IsLogLevelEnable(logLevel); - } - messageFunc = LogMessageFormatter.SimulateStructuredLogging(messageFunc, formatParameters); - - if (_logEventInfoFact != null) - { - if (IsLogLevelEnable(logLevel)) - { - var nlogLevel = this.TranslateLevel(logLevel); - Type s_callerStackBoundaryType; -#if !LIBLOG_PORTABLE - StackTrace stack = new StackTrace(); - Type thisType = GetType(); - Type knownType0 = typeof(LoggerExecutionWrapper); - Type knownType1 = typeof(LogExtensions); - //Maybe inline, so we may can't found any LibLog classes in stack - s_callerStackBoundaryType = null; - for (var i = 0; i < stack.FrameCount; i++) - { - var declaringType = stack.GetFrame(i).GetMethod().DeclaringType; - if (!IsInTypeHierarchy(thisType, declaringType) && - !IsInTypeHierarchy(knownType0, declaringType) && - !IsInTypeHierarchy(knownType1, declaringType)) - { - if (i > 1) - s_callerStackBoundaryType = stack.GetFrame(i - 1).GetMethod().DeclaringType; - break; - } - } -#else - s_callerStackBoundaryType = null; -#endif - if (s_callerStackBoundaryType != null) - _logger.Log(s_callerStackBoundaryType, _logEventInfoFact(_logger.Name, nlogLevel, messageFunc(), exception)); - else - _logger.Log(_logEventInfoFact(_logger.Name, nlogLevel, messageFunc(), exception)); - return true; - } - return false; - } - - if(exception != null) - { - return LogException(logLevel, messageFunc, exception); - } - switch (logLevel) - { - case LogLevel.Debug: - if (_logger.IsDebugEnabled) - { - _logger.Debug(messageFunc()); - return true; - } - break; - case LogLevel.Info: - if (_logger.IsInfoEnabled) - { - _logger.Info(messageFunc()); - return true; - } - break; - case LogLevel.Warn: - if (_logger.IsWarnEnabled) - { - _logger.Warn(messageFunc()); - return true; - } - break; - case LogLevel.Error: - if (_logger.IsErrorEnabled) - { - _logger.Error(messageFunc()); - return true; - } - break; - case LogLevel.Fatal: - if (_logger.IsFatalEnabled) - { - _logger.Fatal(messageFunc()); - return true; - } - break; - default: - if (_logger.IsTraceEnabled) - { - _logger.Trace(messageFunc()); - return true; - } - break; - } - return false; - } - - private static bool IsInTypeHierarchy(Type currentType, Type checkType) - { - while (currentType != null && currentType != typeof(object)) - { - if (currentType == checkType) - { - return true; - } - currentType = currentType.GetBaseTypePortable(); - } - return false; - } - - [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] - private bool LogException(LogLevel logLevel, Func messageFunc, Exception exception) - { - switch (logLevel) - { - case LogLevel.Debug: - if (_logger.IsDebugEnabled) - { - _logger.DebugException(messageFunc(), exception); - return true; - } - break; - case LogLevel.Info: - if (_logger.IsInfoEnabled) - { - _logger.InfoException(messageFunc(), exception); - return true; - } - break; - case LogLevel.Warn: - if (_logger.IsWarnEnabled) - { - _logger.WarnException(messageFunc(), exception); - return true; - } - break; - case LogLevel.Error: - if (_logger.IsErrorEnabled) - { - _logger.ErrorException(messageFunc(), exception); - return true; - } - break; - case LogLevel.Fatal: - if (_logger.IsFatalEnabled) - { - _logger.FatalException(messageFunc(), exception); - return true; - } - break; - default: - if (_logger.IsTraceEnabled) - { - _logger.TraceException(messageFunc(), exception); - return true; - } - break; - } - return false; - } - - private bool IsLogLevelEnable(LogLevel logLevel) - { - switch (logLevel) - { - case LogLevel.Debug: - return _logger.IsDebugEnabled; - case LogLevel.Info: - return _logger.IsInfoEnabled; - case LogLevel.Warn: - return _logger.IsWarnEnabled; - case LogLevel.Error: - return _logger.IsErrorEnabled; - case LogLevel.Fatal: - return _logger.IsFatalEnabled; - default: - return _logger.IsTraceEnabled; - } - } - - private object TranslateLevel(LogLevel logLevel) - { - switch (logLevel) - { - case LogLevel.Trace: - return _levelTrace; - case LogLevel.Debug: - return _levelDebug; - case LogLevel.Info: - return _levelInfo; - case LogLevel.Warn: - return _levelWarn; - case LogLevel.Error: - return _levelError; - case LogLevel.Fatal: - return _levelFatal; - default: - throw new ArgumentOutOfRangeException("logLevel", logLevel, null); - } - } - } - } - -#if !LIBLOG_PORTABLE - [ExcludeFromCodeCoverage] -#endif - internal class Log4NetLogProvider : LogProviderBase - { - private readonly Func _getLoggerByNameDelegate; - private static bool s_providerIsAvailableOverride = true; - - [SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "LogManager")] - public Log4NetLogProvider() - { - if (!IsLoggerAvailable()) - { - throw new InvalidOperationException("log4net.LogManager not found"); - } - _getLoggerByNameDelegate = GetGetLoggerMethodCall(); - } - - public static bool ProviderIsAvailableOverride - { - get { return s_providerIsAvailableOverride; } - set { s_providerIsAvailableOverride = value; } - } - - public override Logger GetLogger(string name) - { - return new Log4NetLogger(_getLoggerByNameDelegate(name)).Log; - } - - internal static bool IsLoggerAvailable() - { - return ProviderIsAvailableOverride && GetLogManagerType() != null; - } - - protected override OpenNdc GetOpenNdcMethod() - { - Type logicalThreadContextType = Type.GetType("log4net.LogicalThreadContext, log4net"); - PropertyInfo stacksProperty = logicalThreadContextType.GetPropertyPortable("Stacks"); - Type logicalThreadContextStacksType = stacksProperty.PropertyType; - PropertyInfo stacksIndexerProperty = logicalThreadContextStacksType.GetPropertyPortable("Item"); - Type stackType = stacksIndexerProperty.PropertyType; - MethodInfo pushMethod = stackType.GetMethodPortable("Push"); - - ParameterExpression messageParameter = - Expression.Parameter(typeof(string), "message"); - - // message => LogicalThreadContext.Stacks.Item["NDC"].Push(message); - MethodCallExpression callPushBody = - Expression.Call( - Expression.Property(Expression.Property(null, stacksProperty), - stacksIndexerProperty, - Expression.Constant("NDC")), - pushMethod, - messageParameter); - - OpenNdc result = - Expression.Lambda(callPushBody, messageParameter) - .Compile(); - - return result; - } - - protected override OpenMdc GetOpenMdcMethod() - { - Type logicalThreadContextType = Type.GetType("log4net.LogicalThreadContext, log4net"); - PropertyInfo propertiesProperty = logicalThreadContextType.GetPropertyPortable("Properties"); - Type logicalThreadContextPropertiesType = propertiesProperty.PropertyType; - PropertyInfo propertiesIndexerProperty = logicalThreadContextPropertiesType.GetPropertyPortable("Item"); - - MethodInfo removeMethod = logicalThreadContextPropertiesType.GetMethodPortable("Remove"); - - ParameterExpression keyParam = Expression.Parameter(typeof(string), "key"); - ParameterExpression valueParam = Expression.Parameter(typeof(string), "value"); - - MemberExpression propertiesExpression = Expression.Property(null, propertiesProperty); - - // (key, value) => LogicalThreadContext.Properties.Item[key] = value; - BinaryExpression setProperties = Expression.Assign(Expression.Property(propertiesExpression, propertiesIndexerProperty, keyParam), valueParam); - - // key => LogicalThreadContext.Properties.Remove(key); - MethodCallExpression removeMethodCall = Expression.Call(propertiesExpression, removeMethod, keyParam); - - Action set = Expression - .Lambda>(setProperties, keyParam, valueParam) - .Compile(); - - Action remove = Expression - .Lambda>(removeMethodCall, keyParam) - .Compile(); - - return (key, value) => - { - set(key, value); - return new DisposableAction(() => remove(key)); - }; - } - - private static Type GetLogManagerType() - { - return Type.GetType("log4net.LogManager, log4net"); - } - - private static Func GetGetLoggerMethodCall() - { - Type logManagerType = GetLogManagerType(); - MethodInfo method = logManagerType.GetMethodPortable("GetLogger", typeof(string)); - ParameterExpression nameParam = Expression.Parameter(typeof(string), "name"); - MethodCallExpression methodCall = Expression.Call(null, method, nameParam); - return Expression.Lambda>(methodCall, nameParam).Compile(); - } - -#if !LIBLOG_PORTABLE - [ExcludeFromCodeCoverage] -#endif - internal class Log4NetLogger - { - private readonly dynamic _logger; - private static Type s_callerStackBoundaryType; - private static readonly object CallerStackBoundaryTypeSync = new object(); - - private readonly object _levelDebug; - private readonly object _levelInfo; - private readonly object _levelWarn; - private readonly object _levelError; - private readonly object _levelFatal; - private readonly Func _isEnabledForDelegate; - private readonly Action _logDelegate; - private readonly Func _createLoggingEvent; - private Action _loggingEventPropertySetter; - - [SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "ILogger")] - internal Log4NetLogger(dynamic logger) - { - _logger = logger.Logger; - - var logEventLevelType = Type.GetType("log4net.Core.Level, log4net"); - if (logEventLevelType == null) - { - throw new InvalidOperationException("Type log4net.Core.Level was not found."); - } - - var levelFields = logEventLevelType.GetFieldsPortable().ToList(); - _levelDebug = levelFields.First(x => x.Name == "Debug").GetValue(null); - _levelInfo = levelFields.First(x => x.Name == "Info").GetValue(null); - _levelWarn = levelFields.First(x => x.Name == "Warn").GetValue(null); - _levelError = levelFields.First(x => x.Name == "Error").GetValue(null); - _levelFatal = levelFields.First(x => x.Name == "Fatal").GetValue(null); - - // Func isEnabledFor = (logger, level) => { return ((log4net.Core.ILogger)logger).IsEnabled(level); } - var loggerType = Type.GetType("log4net.Core.ILogger, log4net"); - if (loggerType == null) - { - throw new InvalidOperationException("Type log4net.Core.ILogger, was not found."); - } - ParameterExpression instanceParam = Expression.Parameter(typeof(object)); - UnaryExpression instanceCast = Expression.Convert(instanceParam, loggerType); - ParameterExpression levelParam = Expression.Parameter(typeof(object)); - UnaryExpression levelCast = Expression.Convert(levelParam, logEventLevelType); - _isEnabledForDelegate = GetIsEnabledFor(loggerType, logEventLevelType, instanceCast, levelCast, instanceParam, levelParam); - - Type loggingEventType = Type.GetType("log4net.Core.LoggingEvent, log4net"); - - _createLoggingEvent = GetCreateLoggingEvent(instanceParam, instanceCast, levelParam, levelCast, loggingEventType); - - _logDelegate = GetLogDelegate(loggerType, loggingEventType, instanceCast, instanceParam); - - _loggingEventPropertySetter = GetLoggingEventPropertySetter(loggingEventType); - } - - private static Action GetLogDelegate(Type loggerType, Type loggingEventType, UnaryExpression instanceCast, - ParameterExpression instanceParam) - { - //Action Log = - //(logger, callerStackBoundaryDeclaringType, level, message, exception) => { ((ILogger)logger).Log(new LoggingEvent(callerStackBoundaryDeclaringType, logger.Repository, logger.Name, level, message, exception)); } - MethodInfo writeExceptionMethodInfo = loggerType.GetMethodPortable("Log", - loggingEventType); - - ParameterExpression loggingEventParameter = - Expression.Parameter(typeof(object), "loggingEvent"); - - UnaryExpression loggingEventCasted = - Expression.Convert(loggingEventParameter, loggingEventType); - - var writeMethodExp = Expression.Call( - instanceCast, - writeExceptionMethodInfo, - loggingEventCasted); - - var logDelegate = Expression.Lambda>( - writeMethodExp, - instanceParam, - loggingEventParameter).Compile(); - - return logDelegate; - } - - private static Func GetCreateLoggingEvent(ParameterExpression instanceParam, UnaryExpression instanceCast, ParameterExpression levelParam, UnaryExpression levelCast, Type loggingEventType) - { - ParameterExpression callerStackBoundaryDeclaringTypeParam = Expression.Parameter(typeof(Type)); - ParameterExpression messageParam = Expression.Parameter(typeof(string)); - ParameterExpression exceptionParam = Expression.Parameter(typeof(Exception)); - - PropertyInfo repositoryProperty = loggingEventType.GetPropertyPortable("Repository"); - PropertyInfo levelProperty = loggingEventType.GetPropertyPortable("Level"); - - ConstructorInfo loggingEventConstructor = - loggingEventType.GetConstructorPortable(typeof(Type), repositoryProperty.PropertyType, typeof(string), levelProperty.PropertyType, typeof(object), typeof(Exception)); - - //Func Log = - //(logger, callerStackBoundaryDeclaringType, level, message, exception) => new LoggingEvent(callerStackBoundaryDeclaringType, ((ILogger)logger).Repository, ((ILogger)logger).Name, (Level)level, message, exception); } - NewExpression newLoggingEventExpression = - Expression.New(loggingEventConstructor, - callerStackBoundaryDeclaringTypeParam, - Expression.Property(instanceCast, "Repository"), - Expression.Property(instanceCast, "Name"), - levelCast, - messageParam, - exceptionParam); - - var createLoggingEvent = - Expression.Lambda>( - newLoggingEventExpression, - instanceParam, - callerStackBoundaryDeclaringTypeParam, - levelParam, - messageParam, - exceptionParam) - .Compile(); - - return createLoggingEvent; - } - - private static Func GetIsEnabledFor(Type loggerType, Type logEventLevelType, - UnaryExpression instanceCast, - UnaryExpression levelCast, - ParameterExpression instanceParam, - ParameterExpression levelParam) - { - MethodInfo isEnabledMethodInfo = loggerType.GetMethodPortable("IsEnabledFor", logEventLevelType); - MethodCallExpression isEnabledMethodCall = Expression.Call(instanceCast, isEnabledMethodInfo, levelCast); - - Func result = - Expression.Lambda>(isEnabledMethodCall, instanceParam, levelParam) - .Compile(); - - return result; - } - - private static Action GetLoggingEventPropertySetter(Type loggingEventType) - { - ParameterExpression loggingEventParameter = Expression.Parameter(typeof(object), "loggingEvent"); - ParameterExpression keyParameter = Expression.Parameter(typeof(string), "key"); - ParameterExpression valueParameter = Expression.Parameter(typeof(object), "value"); - - PropertyInfo propertiesProperty = loggingEventType.GetPropertyPortable("Properties"); - PropertyInfo item = propertiesProperty.PropertyType.GetPropertyPortable("Item"); - - // ((LoggingEvent)loggingEvent).Properties[key] = value; - var body = - Expression.Assign( - Expression.Property( - Expression.Property(Expression.Convert(loggingEventParameter, loggingEventType), - propertiesProperty), item, keyParameter), valueParameter); - - Action result = - Expression.Lambda> - (body, loggingEventParameter, keyParameter, - valueParameter) - .Compile(); - - return result; - } - - public bool Log(LogLevel logLevel, Func messageFunc, Exception exception, params object[] formatParameters) - { - if (messageFunc == null) - { - return IsLogLevelEnable(logLevel); - } - - if (!IsLogLevelEnable(logLevel)) - { - return false; - } - - string message = messageFunc(); - - IEnumerable patternMatches; - - string formattedMessage = - LogMessageFormatter.FormatStructuredMessage(message, - formatParameters, - out patternMatches); - - // determine correct caller - this might change due to jit optimizations with method inlining - if (s_callerStackBoundaryType == null) - { - lock (CallerStackBoundaryTypeSync) - { -#if !LIBLOG_PORTABLE - StackTrace stack = new StackTrace(); - Type thisType = GetType(); - s_callerStackBoundaryType = Type.GetType("LoggerExecutionWrapper"); - for (var i = 1; i < stack.FrameCount; i++) - { - if (!IsInTypeHierarchy(thisType, stack.GetFrame(i).GetMethod().DeclaringType)) - { - s_callerStackBoundaryType = stack.GetFrame(i - 1).GetMethod().DeclaringType; - break; - } - } -#else - s_callerStackBoundaryType = typeof (LoggerExecutionWrapper); -#endif - } - } - - var translatedLevel = TranslateLevel(logLevel); - - object loggingEvent = _createLoggingEvent(_logger, s_callerStackBoundaryType, translatedLevel, formattedMessage, exception); - - PopulateProperties(loggingEvent, patternMatches, formatParameters); - - _logDelegate(_logger, loggingEvent); - - return true; - } - - private void PopulateProperties(object loggingEvent, IEnumerable patternMatches, object[] formatParameters) - { - IEnumerable> keyToValue = - patternMatches.Zip(formatParameters, - (key, value) => new KeyValuePair(key, value)); - - foreach (KeyValuePair keyValuePair in keyToValue) - { - _loggingEventPropertySetter(loggingEvent, keyValuePair.Key, keyValuePair.Value); - } - } - - private static bool IsInTypeHierarchy(Type currentType, Type checkType) - { - while (currentType != null && currentType != typeof(object)) - { - if (currentType == checkType) - { - return true; - } - currentType = currentType.GetBaseTypePortable(); - } - return false; - } - - private bool IsLogLevelEnable(LogLevel logLevel) - { - var level = TranslateLevel(logLevel); - return _isEnabledForDelegate(_logger, level); - } - - private object TranslateLevel(LogLevel logLevel) - { - switch (logLevel) - { - case LogLevel.Trace: - case LogLevel.Debug: - return _levelDebug; - case LogLevel.Info: - return _levelInfo; - case LogLevel.Warn: - return _levelWarn; - case LogLevel.Error: - return _levelError; - case LogLevel.Fatal: - return _levelFatal; - default: - throw new ArgumentOutOfRangeException("logLevel", logLevel, null); - } - } - } - } - -#if !LIBLOG_PORTABLE - [ExcludeFromCodeCoverage] -#endif - internal class EntLibLogProvider : LogProviderBase - { - private const string TypeTemplate = "Microsoft.Practices.EnterpriseLibrary.Logging.{0}, Microsoft.Practices.EnterpriseLibrary.Logging"; - private static bool s_providerIsAvailableOverride = true; - private static readonly Type LogEntryType; - private static readonly Type LoggerType; - private static readonly Type TraceEventTypeType; - private static readonly Action WriteLogEntry; - private static readonly Func ShouldLogEntry; - - [SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline")] - static EntLibLogProvider() - { - LogEntryType = Type.GetType(string.Format(CultureInfo.InvariantCulture, TypeTemplate, "LogEntry")); - LoggerType = Type.GetType(string.Format(CultureInfo.InvariantCulture, TypeTemplate, "Logger")); - TraceEventTypeType = TraceEventTypeValues.Type; - if (LogEntryType == null - || TraceEventTypeType == null - || LoggerType == null) - { - return; - } - WriteLogEntry = GetWriteLogEntry(); - ShouldLogEntry = GetShouldLogEntry(); - } - - [SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "EnterpriseLibrary")] - public EntLibLogProvider() - { - if (!IsLoggerAvailable()) - { - throw new InvalidOperationException("Microsoft.Practices.EnterpriseLibrary.Logging.Logger not found"); - } - } - - public static bool ProviderIsAvailableOverride - { - get { return s_providerIsAvailableOverride; } - set { s_providerIsAvailableOverride = value; } - } - - public override Logger GetLogger(string name) - { - return new EntLibLogger(name, WriteLogEntry, ShouldLogEntry).Log; - } - - internal static bool IsLoggerAvailable() - { - return ProviderIsAvailableOverride - && TraceEventTypeType != null - && LogEntryType != null; - } - - private static Action GetWriteLogEntry() - { - // new LogEntry(...) - var logNameParameter = Expression.Parameter(typeof(string), "logName"); - var messageParameter = Expression.Parameter(typeof(string), "message"); - var severityParameter = Expression.Parameter(typeof(int), "severity"); - - MemberInitExpression memberInit = GetWriteLogExpression( - messageParameter, - Expression.Convert(severityParameter, TraceEventTypeType), - logNameParameter); - - //Logger.Write(new LogEntry(....)); - MethodInfo writeLogEntryMethod = LoggerType.GetMethodPortable("Write", LogEntryType); - var writeLogEntryExpression = Expression.Call(writeLogEntryMethod, memberInit); - - return Expression.Lambda>( - writeLogEntryExpression, - logNameParameter, - messageParameter, - severityParameter).Compile(); - } - - private static Func GetShouldLogEntry() - { - // new LogEntry(...) - var logNameParameter = Expression.Parameter(typeof(string), "logName"); - var severityParameter = Expression.Parameter(typeof(int), "severity"); - - MemberInitExpression memberInit = GetWriteLogExpression( - Expression.Constant("***dummy***"), - Expression.Convert(severityParameter, TraceEventTypeType), - logNameParameter); - - //Logger.Write(new LogEntry(....)); - MethodInfo writeLogEntryMethod = LoggerType.GetMethodPortable("ShouldLog", LogEntryType); - var writeLogEntryExpression = Expression.Call(writeLogEntryMethod, memberInit); - - return Expression.Lambda>( - writeLogEntryExpression, - logNameParameter, - severityParameter).Compile(); - } - - private static MemberInitExpression GetWriteLogExpression(Expression message, - Expression severityParameter, ParameterExpression logNameParameter) - { - var entryType = LogEntryType; - MemberInitExpression memberInit = Expression.MemberInit(Expression.New(entryType), - Expression.Bind(entryType.GetPropertyPortable("Message"), message), - Expression.Bind(entryType.GetPropertyPortable("Severity"), severityParameter), - Expression.Bind( - entryType.GetPropertyPortable("TimeStamp"), - Expression.Property(null, typeof (DateTime).GetPropertyPortable("UtcNow"))), - Expression.Bind( - entryType.GetPropertyPortable("Categories"), - Expression.ListInit( - Expression.New(typeof (List)), - typeof (List).GetMethodPortable("Add", typeof (string)), - logNameParameter))); - return memberInit; - } - -#if !LIBLOG_PORTABLE - [ExcludeFromCodeCoverage] -#endif - internal class EntLibLogger - { - private readonly string _loggerName; - private readonly Action _writeLog; - private readonly Func _shouldLog; - - internal EntLibLogger(string loggerName, Action writeLog, Func shouldLog) - { - _loggerName = loggerName; - _writeLog = writeLog; - _shouldLog = shouldLog; - } - - public bool Log(LogLevel logLevel, Func messageFunc, Exception exception, params object[] formatParameters) - { - var severity = MapSeverity(logLevel); - if (messageFunc == null) - { - return _shouldLog(_loggerName, severity); - } - - - messageFunc = LogMessageFormatter.SimulateStructuredLogging(messageFunc, formatParameters); - if (exception != null) - { - return LogException(logLevel, messageFunc, exception); - } - _writeLog(_loggerName, messageFunc(), severity); - return true; - } - - public bool LogException(LogLevel logLevel, Func messageFunc, Exception exception) - { - var severity = MapSeverity(logLevel); - var message = messageFunc() + Environment.NewLine + exception; - _writeLog(_loggerName, message, severity); - return true; - } - - private static int MapSeverity(LogLevel logLevel) - { - switch (logLevel) - { - case LogLevel.Fatal: - return TraceEventTypeValues.Critical; - case LogLevel.Error: - return TraceEventTypeValues.Error; - case LogLevel.Warn: - return TraceEventTypeValues.Warning; - case LogLevel.Info: - return TraceEventTypeValues.Information; - default: - return TraceEventTypeValues.Verbose; - } - } - } - } - -#if !LIBLOG_PORTABLE - [ExcludeFromCodeCoverage] -#endif - internal class SerilogLogProvider : LogProviderBase - { - private readonly Func _getLoggerByNameDelegate; - private static bool s_providerIsAvailableOverride = true; - - [SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "Serilog")] - public SerilogLogProvider() - { - if (!IsLoggerAvailable()) - { - throw new InvalidOperationException("Serilog.Log not found"); - } - _getLoggerByNameDelegate = GetForContextMethodCall(); - } - - public static bool ProviderIsAvailableOverride - { - get { return s_providerIsAvailableOverride; } - set { s_providerIsAvailableOverride = value; } - } - - public override Logger GetLogger(string name) - { - return new SerilogLogger(_getLoggerByNameDelegate(name)).Log; - } - - internal static bool IsLoggerAvailable() - { - return ProviderIsAvailableOverride && GetLogManagerType() != null; - } - - protected override OpenNdc GetOpenNdcMethod() - { - return message => GetPushProperty()("NDC", message); - } - - protected override OpenMdc GetOpenMdcMethod() - { - return (key, value) => GetPushProperty()(key, value); - } - - private static Func GetPushProperty() - { - Type ndcContextType = Type.GetType("Serilog.Context.LogContext, Serilog") ?? - Type.GetType("Serilog.Context.LogContext, Serilog.FullNetFx"); - - MethodInfo pushPropertyMethod = ndcContextType.GetMethodPortable( - "PushProperty", - typeof(string), - typeof(object), - typeof(bool)); - - ParameterExpression nameParam = Expression.Parameter(typeof(string), "name"); - ParameterExpression valueParam = Expression.Parameter(typeof(object), "value"); - ParameterExpression destructureObjectParam = Expression.Parameter(typeof(bool), "destructureObjects"); - MethodCallExpression pushPropertyMethodCall = Expression - .Call(null, pushPropertyMethod, nameParam, valueParam, destructureObjectParam); - var pushProperty = Expression - .Lambda>( - pushPropertyMethodCall, - nameParam, - valueParam, - destructureObjectParam) - .Compile(); - - return (key, value) => pushProperty(key, value, false); - } - - private static Type GetLogManagerType() - { - return Type.GetType("Serilog.Log, Serilog"); - } - - private static Func GetForContextMethodCall() - { - Type logManagerType = GetLogManagerType(); - MethodInfo method = logManagerType.GetMethodPortable("ForContext", typeof(string), typeof(object), typeof(bool)); - ParameterExpression propertyNameParam = Expression.Parameter(typeof(string), "propertyName"); - ParameterExpression valueParam = Expression.Parameter(typeof(object), "value"); - ParameterExpression destructureObjectsParam = Expression.Parameter(typeof(bool), "destructureObjects"); - MethodCallExpression methodCall = Expression.Call(null, method, new Expression[] - { - propertyNameParam, - valueParam, - destructureObjectsParam - }); - var func = Expression.Lambda>( - methodCall, - propertyNameParam, - valueParam, - destructureObjectsParam) - .Compile(); - return name => func("SourceContext", name, false); - } - -#if !LIBLOG_PORTABLE - [ExcludeFromCodeCoverage] -#endif - internal class SerilogLogger - { - private readonly object _logger; - private static readonly object DebugLevel; - private static readonly object ErrorLevel; - private static readonly object FatalLevel; - private static readonly object InformationLevel; - private static readonly object VerboseLevel; - private static readonly object WarningLevel; - private static readonly Func IsEnabled; - private static readonly Action Write; - private static readonly Action WriteException; - - [SuppressMessage("Microsoft.Design", "CA1065:DoNotRaiseExceptionsInUnexpectedLocations")] - [SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline")] - [SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "ILogger")] - [SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "LogEventLevel")] - [SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "Serilog")] - static SerilogLogger() - { - var logEventLevelType = Type.GetType("Serilog.Events.LogEventLevel, Serilog"); - if (logEventLevelType == null) - { - throw new InvalidOperationException("Type Serilog.Events.LogEventLevel was not found."); - } - DebugLevel = Enum.Parse(logEventLevelType, "Debug", false); - ErrorLevel = Enum.Parse(logEventLevelType, "Error", false); - FatalLevel = Enum.Parse(logEventLevelType, "Fatal", false); - InformationLevel = Enum.Parse(logEventLevelType, "Information", false); - VerboseLevel = Enum.Parse(logEventLevelType, "Verbose", false); - WarningLevel = Enum.Parse(logEventLevelType, "Warning", false); - - // Func isEnabled = (logger, level) => { return ((SeriLog.ILogger)logger).IsEnabled(level); } - var loggerType = Type.GetType("Serilog.ILogger, Serilog"); - if (loggerType == null) - { - throw new InvalidOperationException("Type Serilog.ILogger was not found."); - } - MethodInfo isEnabledMethodInfo = loggerType.GetMethodPortable("IsEnabled", logEventLevelType); - ParameterExpression instanceParam = Expression.Parameter(typeof(object)); - UnaryExpression instanceCast = Expression.Convert(instanceParam, loggerType); - ParameterExpression levelParam = Expression.Parameter(typeof(object)); - UnaryExpression levelCast = Expression.Convert(levelParam, logEventLevelType); - MethodCallExpression isEnabledMethodCall = Expression.Call(instanceCast, isEnabledMethodInfo, levelCast); - IsEnabled = Expression.Lambda>(isEnabledMethodCall, instanceParam, levelParam).Compile(); - - // Action Write = - // (logger, level, message, params) => { ((SeriLog.ILoggerILogger)logger).Write(level, message, params); } - MethodInfo writeMethodInfo = loggerType.GetMethodPortable("Write", logEventLevelType, typeof(string), typeof(object[])); - ParameterExpression messageParam = Expression.Parameter(typeof(string)); - ParameterExpression propertyValuesParam = Expression.Parameter(typeof(object[])); - MethodCallExpression writeMethodExp = Expression.Call( - instanceCast, - writeMethodInfo, - levelCast, - messageParam, - propertyValuesParam); - var expression = Expression.Lambda>( - writeMethodExp, - instanceParam, - levelParam, - messageParam, - propertyValuesParam); - Write = expression.Compile(); - - // Action WriteException = - // (logger, level, exception, message) => { ((ILogger)logger).Write(level, exception, message, new object[]); } - MethodInfo writeExceptionMethodInfo = loggerType.GetMethodPortable("Write", - logEventLevelType, - typeof(Exception), - typeof(string), - typeof(object[])); - ParameterExpression exceptionParam = Expression.Parameter(typeof(Exception)); - writeMethodExp = Expression.Call( - instanceCast, - writeExceptionMethodInfo, - levelCast, - exceptionParam, - messageParam, - propertyValuesParam); - WriteException = Expression.Lambda>( - writeMethodExp, - instanceParam, - levelParam, - exceptionParam, - messageParam, - propertyValuesParam).Compile(); - } - - internal SerilogLogger(object logger) - { - _logger = logger; - } - - public bool Log(LogLevel logLevel, Func messageFunc, Exception exception, params object[] formatParameters) - { - var translatedLevel = TranslateLevel(logLevel); - if (messageFunc == null) - { - return IsEnabled(_logger, translatedLevel); - } - - if (!IsEnabled(_logger, translatedLevel)) - { - return false; - } - - if (exception != null) - { - LogException(translatedLevel, messageFunc, exception, formatParameters); - } - else - { - LogMessage(translatedLevel, messageFunc, formatParameters); - } - - return true; - } - - private void LogMessage(object translatedLevel, Func messageFunc, object[] formatParameters) - { - Write(_logger, translatedLevel, messageFunc(), formatParameters); - } - - private void LogException(object logLevel, Func messageFunc, Exception exception, object[] formatParams) - { - WriteException(_logger, logLevel, exception, messageFunc(), formatParams); - } - - private static object TranslateLevel(LogLevel logLevel) - { - switch (logLevel) - { - case LogLevel.Fatal: - return FatalLevel; - case LogLevel.Error: - return ErrorLevel; - case LogLevel.Warn: - return WarningLevel; - case LogLevel.Info: - return InformationLevel; - case LogLevel.Trace: - return VerboseLevel; - default: - return DebugLevel; - } - } - } - } - -#if !LIBLOG_PORTABLE - [ExcludeFromCodeCoverage] -#endif - internal class LoupeLogProvider : LogProviderBase - { - /// - /// The form of the Loupe Log.Write method we're using - /// - internal delegate void WriteDelegate( - int severity, - string logSystem, - int skipFrames, - Exception exception, - bool attributeToException, - int writeMode, - string detailsXml, - string category, - string caption, - string description, - params object[] args - ); - - private static bool s_providerIsAvailableOverride = true; - private readonly WriteDelegate _logWriteDelegate; - - public LoupeLogProvider() - { - if (!IsLoggerAvailable()) - { - throw new InvalidOperationException("Gibraltar.Agent.Log (Loupe) not found"); - } - - _logWriteDelegate = GetLogWriteDelegate(); - } - - /// - /// Gets or sets a value indicating whether [provider is available override]. Used in tests. - /// - /// - /// true if [provider is available override]; otherwise, false. - /// - public static bool ProviderIsAvailableOverride - { - get { return s_providerIsAvailableOverride; } - set { s_providerIsAvailableOverride = value; } - } - - public override Logger GetLogger(string name) - { - return new LoupeLogger(name, _logWriteDelegate).Log; - } - - public static bool IsLoggerAvailable() - { - return ProviderIsAvailableOverride && GetLogManagerType() != null; - } - - private static Type GetLogManagerType() - { - return Type.GetType("Gibraltar.Agent.Log, Gibraltar.Agent"); - } - - private static WriteDelegate GetLogWriteDelegate() - { - Type logManagerType = GetLogManagerType(); - Type logMessageSeverityType = Type.GetType("Gibraltar.Agent.LogMessageSeverity, Gibraltar.Agent"); - Type logWriteModeType = Type.GetType("Gibraltar.Agent.LogWriteMode, Gibraltar.Agent"); - - MethodInfo method = logManagerType.GetMethodPortable( - "Write", - logMessageSeverityType, typeof(string), typeof(int), typeof(Exception), typeof(bool), - logWriteModeType, typeof(string), typeof(string), typeof(string), typeof(string), typeof(object[])); - - var callDelegate = (WriteDelegate)method.CreateDelegate(typeof(WriteDelegate)); - return callDelegate; - } - -#if !LIBLOG_PORTABLE - [ExcludeFromCodeCoverage] -#endif - internal class LoupeLogger - { - private const string LogSystem = "LibLog"; - - private readonly string _category; - private readonly WriteDelegate _logWriteDelegate; - private readonly int _skipLevel; - - internal LoupeLogger(string category, WriteDelegate logWriteDelegate) - { - _category = category; - _logWriteDelegate = logWriteDelegate; -#if DEBUG - _skipLevel = 2; -#else - _skipLevel = 1; -#endif - } - - public bool Log(LogLevel logLevel, Func messageFunc, Exception exception, params object[] formatParameters) - { - if (messageFunc == null) - { - //nothing to log.. - return true; - } - - messageFunc = LogMessageFormatter.SimulateStructuredLogging(messageFunc, formatParameters); - - _logWriteDelegate(ToLogMessageSeverity(logLevel), LogSystem, _skipLevel, exception, true, 0, null, - _category, null, messageFunc.Invoke()); - - return true; - } - - private static int ToLogMessageSeverity(LogLevel logLevel) - { - switch (logLevel) - { - case LogLevel.Trace: - return TraceEventTypeValues.Verbose; - case LogLevel.Debug: - return TraceEventTypeValues.Verbose; - case LogLevel.Info: - return TraceEventTypeValues.Information; - case LogLevel.Warn: - return TraceEventTypeValues.Warning; - case LogLevel.Error: - return TraceEventTypeValues.Error; - case LogLevel.Fatal: - return TraceEventTypeValues.Critical; - default: - throw new ArgumentOutOfRangeException("logLevel"); - } - } - } - } - -#if !LIBLOG_PORTABLE - [ExcludeFromCodeCoverage] -#endif - internal static class TraceEventTypeValues - { - internal static readonly Type Type; - internal static readonly int Verbose; - internal static readonly int Information; - internal static readonly int Warning; - internal static readonly int Error; - internal static readonly int Critical; - - [SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline")] - static TraceEventTypeValues() - { - var assembly = typeof(Uri).GetAssemblyPortable(); // This is to get to the System.dll assembly in a PCL compatible way. - if (assembly == null) - { - return; - } - Type = assembly.GetType("System.Diagnostics.TraceEventType"); - if (Type == null) return; - Verbose = (int)Enum.Parse(Type, "Verbose", false); - Information = (int)Enum.Parse(Type, "Information", false); - Warning = (int)Enum.Parse(Type, "Warning", false); - Error = (int)Enum.Parse(Type, "Error", false); - Critical = (int)Enum.Parse(Type, "Critical", false); - } - } - -#if !LIBLOG_PORTABLE - [ExcludeFromCodeCoverage] -#endif - internal static class LogMessageFormatter - { - //private static readonly Regex Pattern = new Regex(@"\{@?\w{1,}\}"); -#if LIBLOG_PORTABLE - private static readonly Regex Pattern = new Regex(@"(?[^\d{][^ }]*)}"); -#else - private static readonly Regex Pattern = new Regex(@"(?[^ :{}]+)(?:[^}]+)?}", RegexOptions.Compiled); -#endif - - /// - /// Some logging frameworks support structured logging, such as serilog. This will allow you to add names to structured data in a format string: - /// For example: Log("Log message to {user}", user). This only works with serilog, but as the user of LibLog, you don't know if serilog is actually - /// used. So, this class simulates that. it will replace any text in {curly braces} with an index number. - /// - /// "Log {message} to {user}" would turn into => "Log {0} to {1}". Then the format parameters are handled using regular .net string.Format. - /// - /// The message builder. - /// The format parameters. - /// - public static Func SimulateStructuredLogging(Func messageBuilder, object[] formatParameters) - { - if (formatParameters == null || formatParameters.Length == 0) - { - return messageBuilder; - } - - return () => - { - string targetMessage = messageBuilder(); - IEnumerable patternMatches; - return FormatStructuredMessage(targetMessage, formatParameters, out patternMatches); - }; - } - - private static string ReplaceFirst(string text, string search, string replace) - { - int pos = text.IndexOf(search, StringComparison.Ordinal); - if (pos < 0) - { - return text; - } - return text.Substring(0, pos) + replace + text.Substring(pos + search.Length); - } - - public static string FormatStructuredMessage(string targetMessage, object[] formatParameters, out IEnumerable patternMatches) - { - if (formatParameters.Length == 0) - { - patternMatches = Enumerable.Empty(); - return targetMessage; - } - - List processedArguments = new List(); - patternMatches = processedArguments; - - foreach (Match match in Pattern.Matches(targetMessage)) - { - var arg = match.Groups["arg"].Value; - - int notUsed; - if (!int.TryParse(arg, out notUsed)) - { - int argumentIndex = processedArguments.IndexOf(arg); - if (argumentIndex == -1) - { - argumentIndex = processedArguments.Count; - processedArguments.Add(arg); - } - - targetMessage = ReplaceFirst(targetMessage, match.Value, - "{" + argumentIndex + match.Groups["format"].Value + "}"); - } - } - try - { - return string.Format(CultureInfo.InvariantCulture, targetMessage, formatParameters); - } - catch (FormatException ex) - { - throw new FormatException("The input string '" + targetMessage + "' could not be formatted using string.Format", ex); - } - } - } - -#if !LIBLOG_PORTABLE - [ExcludeFromCodeCoverage] -#endif - internal static class TypeExtensions - { - internal static ConstructorInfo GetConstructorPortable(this Type type, params Type[] types) - { -#if LIBLOG_PORTABLE - return type.GetTypeInfo().DeclaredConstructors.FirstOrDefault - (constructor => - constructor.GetParameters() - .Select(parameter => parameter.ParameterType) - .SequenceEqual(types)); -#else - return type.GetConstructor(types); -#endif - } - - internal static MethodInfo GetMethodPortable(this Type type, string name) - { -#if LIBLOG_PORTABLE - return type.GetRuntimeMethods().SingleOrDefault(m => m.Name == name); -#else - return type.GetMethod(name); -#endif - } - - internal static MethodInfo GetMethodPortable(this Type type, string name, params Type[] types) - { -#if LIBLOG_PORTABLE - return type.GetRuntimeMethod(name, types); -#else - return type.GetMethod(name, types); -#endif - } - - internal static PropertyInfo GetPropertyPortable(this Type type, string name) - { -#if LIBLOG_PORTABLE - return type.GetRuntimeProperty(name); -#else - return type.GetProperty(name); -#endif - } - - internal static IEnumerable GetFieldsPortable(this Type type) - { -#if LIBLOG_PORTABLE - return type.GetRuntimeFields(); -#else - return type.GetFields(); -#endif - } - - internal static Type GetBaseTypePortable(this Type type) - { -#if LIBLOG_PORTABLE - return type.GetTypeInfo().BaseType; -#else - return type.BaseType; -#endif - } - -#if LIBLOG_PORTABLE - internal static MethodInfo GetGetMethod(this PropertyInfo propertyInfo) - { - return propertyInfo.GetMethod; - } - - internal static MethodInfo GetSetMethod(this PropertyInfo propertyInfo) - { - return propertyInfo.SetMethod; - } -#endif - -#if !LIBLOG_PORTABLE - internal static object CreateDelegate(this MethodInfo methodInfo, Type delegateType) - { - return Delegate.CreateDelegate(delegateType, methodInfo); - } -#endif - - internal static Assembly GetAssemblyPortable(this Type type) - { -#if LIBLOG_PORTABLE - return type.GetTypeInfo().Assembly; -#else - return type.Assembly; -#endif - } - } - -#if !LIBLOG_PORTABLE - [ExcludeFromCodeCoverage] -#endif - internal class DisposableAction : IDisposable - { - private readonly Action _onDispose; - - public DisposableAction(Action onDispose = null) - { - _onDispose = onDispose; - } - - public void Dispose() - { - if(_onDispose != null) - { - _onDispose(); - } - } - } -} diff --git a/src/Akeneo/Model/Download.cs b/src/Akeneo/Model/Download.cs new file mode 100644 index 0000000..00e4d3b --- /dev/null +++ b/src/Akeneo/Model/Download.cs @@ -0,0 +1,7 @@ +namespace Akeneo.Model +{ + public class Download + { + public string Href { get; set; } + } +} \ No newline at end of file diff --git a/src/Akeneo/Model/FamilyVariant.cs b/src/Akeneo/Model/FamilyVariant.cs new file mode 100644 index 0000000..2a5a899 --- /dev/null +++ b/src/Akeneo/Model/FamilyVariant.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Akeneo.Model +{ + public class FamilyVariant : ModelBase + { + public string Code { get; set; } + + public Dictionary Labels { get; set; } + + public List VariantAttributeSets { get; set; } + } +} \ No newline at end of file diff --git a/src/Akeneo/Model/Links.cs b/src/Akeneo/Model/Links.cs new file mode 100644 index 0000000..0e5ec2c --- /dev/null +++ b/src/Akeneo/Model/Links.cs @@ -0,0 +1,7 @@ +namespace Akeneo.Model +{ + public class Links + { + public Download Download { get; set; } + } +} \ No newline at end of file diff --git a/src/Akeneo/Model/Product.cs b/src/Akeneo/Model/Product.cs index 6820aae..52b680e 100644 --- a/src/Akeneo/Model/Product.cs +++ b/src/Akeneo/Model/Product.cs @@ -10,30 +10,30 @@ public class Product : ModelBase /// public string Identifier { get; set; } - /// - /// Whether the product is enable - /// - public bool Enabled { get; set; } - /// /// Family code from which the product inherits its attributes and attributes requirements /// public string Family { get; set; } - /// - /// Codes of the categories in which the product is classified - /// - public List Categories { get; set; } - + /// + /// Code of the parent product model when the product is a variant (only available since the 2.0). This parent can be modified since the 2.3. + /// + public string Parent { get; set; } + /// /// Codes of the groups to which the product belong /// public List Groups { get; set; } - /// - /// Code of the variant group in which the product is - /// - public string VariantGroup { get; set; } + /// + /// Codes of the categories in which the product is classified + /// + public List Categories { get; set; } + + /// + /// Whether the product is enable + /// + public bool Enabled { get; set; } /// /// Product attributes values @@ -55,12 +55,18 @@ public class Product : ModelBase /// public Dictionary Associations { get; set; } - public Product() + /// + /// Status of the product regarding the user permissions (only available since the v2.0 in the Enterprise Edition) + /// + public Dictionary Metadata { get; set; } + + public Product() { Groups = new List(); Categories = new List(); Associations = new Dictionary(); Values = new Dictionary>(); + Metadata = new Dictionary(); } } } diff --git a/src/Akeneo/Model/ProductAssociation.cs b/src/Akeneo/Model/ProductAssociation.cs index a386109..2111814 100644 --- a/src/Akeneo/Model/ProductAssociation.cs +++ b/src/Akeneo/Model/ProductAssociation.cs @@ -4,7 +4,8 @@ namespace Akeneo.Model { public class ProductAssociation { - public List Products { get; set; } - public List Groups { get; set; } + public List Groups { get; set; } + public List Products { get; set; } + public List ProductModels { get; set; } } } \ No newline at end of file diff --git a/src/Akeneo/Model/ProductModel.cs b/src/Akeneo/Model/ProductModel.cs new file mode 100644 index 0000000..c2fd8b6 --- /dev/null +++ b/src/Akeneo/Model/ProductModel.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Akeneo.Model +{ + public class ProductModel : ModelBase + { + public string Code { get; set; } + + public string FamilyVariant { get; set; } + + public string Parent { get; set; } + + public List Categories { get; set; } + + public Dictionary> Values { get; set; } + + public DateTime? Created { get; set; } + + /// + /// Date of the last update + /// + public DateTime? Updated { get; set; } + + /// + /// Several associations related to groups and/or other products, grouped by association types + /// + public Dictionary Associations { get; set; } + } +} \ No newline at end of file diff --git a/src/Akeneo/Model/ProductValue.cs b/src/Akeneo/Model/ProductValue.cs index 3839f8d..33b73c7 100644 --- a/src/Akeneo/Model/ProductValue.cs +++ b/src/Akeneo/Model/ProductValue.cs @@ -20,5 +20,11 @@ public class ProductValue /// [JsonConverter(typeof(ProductValueDataConverter))] public object Data { get; set; } - } + + /// + /// URI to get the metadata of the media file + /// + [JsonProperty("_links", NullValueHandling = NullValueHandling.Ignore)] + public Links Links { get; set; } + } } \ No newline at end of file diff --git a/src/Akeneo/Model/VariantAttributeSet.cs b/src/Akeneo/Model/VariantAttributeSet.cs new file mode 100644 index 0000000..f992531 --- /dev/null +++ b/src/Akeneo/Model/VariantAttributeSet.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; + +namespace Akeneo.Model +{ + public partial class VariantAttributeSet + { + public long Level { get; set; } + public List Axes { get; set; } + public List Attributes { get; set; } + } +} \ No newline at end of file diff --git a/src/Akeneo/Search/Created.cs b/src/Akeneo/Search/Created.cs index 663376b..07ef3cb 100644 --- a/src/Akeneo/Search/Created.cs +++ b/src/Akeneo/Search/Created.cs @@ -12,8 +12,8 @@ public static Created Equals(DateTime date) return new Created { Operator = Operators.Equal, - Value = date - }; + Value = date.ToString("yyyy-MM-dd HH:mm:ss") + }; } public static Created NotEquals(DateTime date) @@ -21,16 +21,16 @@ public static Created NotEquals(DateTime date) return new Created { Operator = Operators.NotEqual, - Value = date - }; + Value = date.ToString("yyyy-MM-dd HH:mm:ss") + }; } public static Created Greater(DateTime date) { - return new Created + return new Created { Operator = Operators.Greater, - Value = date + Value = date.ToString("yyyy-MM-dd HH:mm:ss") }; } @@ -39,8 +39,8 @@ public static Created Lower(DateTime date) return new Created { Operator = Operators.Lower, - Value = date - }; + Value = date.ToString("yyyy-MM-dd HH:mm:ss") + }; } public static Created Between(DateTime start, DateTime stop) diff --git a/src/Akeneo/Search/SearchQueryBuilder.cs b/src/Akeneo/Search/SearchQueryBuilder.cs index 1891d96..fa52963 100644 --- a/src/Akeneo/Search/SearchQueryBuilder.cs +++ b/src/Akeneo/Search/SearchQueryBuilder.cs @@ -10,6 +10,7 @@ namespace Akeneo.Search public class SearchQueryBuilder { private const string SearchStart = "?search="; + private const string SearchParam = "&search="; private const string SearchLocale = "&search_locale="; private const string SearchScope = "&search_scope="; @@ -21,6 +22,14 @@ public string GetQueryString(IEnumerable criterias) return GetQueryString(searchDictionary, channel, locale); } + public string GetQueryStringParam(IEnumerable criterias) + { + var searchDictionary = GetSearchDictionary(criterias); + var channel = GetChannels(criterias); + var locale = GetLocales(criterias); + return GetQueryStringParam(searchDictionary, channel, locale); + } + public string GetQueryString(Dictionary> criterias, string scope = null, string locale = null) { if (!criterias?.Keys.Any() ?? true) @@ -46,6 +55,31 @@ public string GetQueryString(Dictionary> criterias, strin return builder.ToString(); } + public string GetQueryStringParam(Dictionary> criterias, string scope = null, string locale = null) + { + if (!criterias?.Keys.Any() ?? true) + { + throw new ArgumentException("Unable to build search query without any provided criteras."); + } + + var builder = new StringBuilder(SearchParam); + builder.Append(JsonConvert.SerializeObject(criterias, AkeneoSerializerSettings.Create)); + + if (!string.IsNullOrEmpty(scope)) + { + builder.Append(SearchScope); + builder.Append(scope); + } + + if (!string.IsNullOrEmpty(locale)) + { + builder.Append(SearchLocale); + builder.Append(locale); + } + + return builder.ToString(); + } + public Dictionary> GetSearchDictionary(IEnumerable criterias) { criterias = criterias.ToList(); diff --git a/src/Akeneo/Search/UpdatedCriteria.cs b/src/Akeneo/Search/UpdatedCriteria.cs index 52b0462..72cdb36 100644 --- a/src/Akeneo/Search/UpdatedCriteria.cs +++ b/src/Akeneo/Search/UpdatedCriteria.cs @@ -12,8 +12,8 @@ public static UpdatedCriteria Equals(DateTime date) return new UpdatedCriteria { Operator = Operators.Equal, - Value = date - }; + Value = date.ToString("yyyy-MM-dd HH:mm:ss") + }; } public static UpdatedCriteria NotEquals(DateTime date) @@ -21,8 +21,8 @@ public static UpdatedCriteria NotEquals(DateTime date) return new UpdatedCriteria { Operator = Operators.NotEqual, - Value = date - }; + Value = date.ToString("yyyy-MM-dd HH:mm:ss") + }; } public static UpdatedCriteria Greater(DateTime date) @@ -30,8 +30,8 @@ public static UpdatedCriteria Greater(DateTime date) return new UpdatedCriteria { Operator = Operators.Greater, - Value = date - }; + Value = date.ToString("yyyy-MM-dd HH:mm:ss") + }; } public static UpdatedCriteria Lower(DateTime date) @@ -39,8 +39,8 @@ public static UpdatedCriteria Lower(DateTime date) return new UpdatedCriteria { Operator = Operators.Lower, - Value = date - }; + Value = date.ToString("yyyy-MM-dd HH:mm:ss") + }; } public static UpdatedCriteria Between(DateTime start, DateTime stop) diff --git a/src/Akeneo/Serialization/ProductValueConverter.cs b/src/Akeneo/Serialization/ProductValueConverter.cs index 263fba3..a88c841 100644 --- a/src/Akeneo/Serialization/ProductValueConverter.cs +++ b/src/Akeneo/Serialization/ProductValueConverter.cs @@ -37,7 +37,10 @@ public override void WriteJson(JsonWriter writer, object value, JsonSerializer s writer.WriteValue(productValue.Scope); } - writer.WriteEndObject(); + writer.WritePropertyName("_links"); + serializer.Serialize(writer, productValue.Links); + + writer.WriteEndObject(); } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) diff --git a/tests/Akeneo.IntegrationTests/Akeneo.IntegrationTests.csproj b/tests/Akeneo.IntegrationTests/Akeneo.IntegrationTests.csproj index 57c3cd2..82c67bc 100644 --- a/tests/Akeneo.IntegrationTests/Akeneo.IntegrationTests.csproj +++ b/tests/Akeneo.IntegrationTests/Akeneo.IntegrationTests.csproj @@ -1,14 +1,17 @@  - netcoreapp1.0 + netcoreapp2.0 - - - - + + + + + all + runtime; build; native; contentfiles; analyzers + diff --git a/tests/Akeneo.IntegrationTests/AttributeTests.cs b/tests/Akeneo.IntegrationTests/AttributeTests.cs index 931ae8c..e8ad8b6 100644 --- a/tests/Akeneo.IntegrationTests/AttributeTests.cs +++ b/tests/Akeneo.IntegrationTests/AttributeTests.cs @@ -23,7 +23,7 @@ public async Task Get_Many_Async() var pagination = await Client.GetManyAsync(); Assert.NotNull(pagination); - Assert.Equal(pagination.CurrentPage, 1); + Assert.Equal(1, pagination.CurrentPage); } [Fact] @@ -48,7 +48,7 @@ public async Task Create_Many_Async() }); Assert.NotNull(result); - Assert.Equal(result.Count, 2); + Assert.Equal(2, result.Count); } [Fact] @@ -65,13 +65,13 @@ public async Task Create_Update_Delete_Number_Atttribute() var createResponse = await Client.CreateAsync(number); - Assert.Equal(createResponse.Code, HttpStatusCode.Created); + Assert.Equal(HttpStatusCode.Created, createResponse.Code); /* Update */ number.NegativeAllowed = false; var updateResponse = await Client.UpdateAsync(number); - Assert.Equal(updateResponse.Code, HttpStatusCode.NoContent); + Assert.Equal(HttpStatusCode.NoContent, updateResponse.Code); /* Delete */ var deleteResponse = await Client.DeleteAsync(number.Code); diff --git a/tests/Akeneo.IntegrationTests/IntegrationTestBase.cs b/tests/Akeneo.IntegrationTests/IntegrationTestBase.cs index 3e286ac..329e4b1 100644 --- a/tests/Akeneo.IntegrationTests/IntegrationTestBase.cs +++ b/tests/Akeneo.IntegrationTests/IntegrationTestBase.cs @@ -10,12 +10,12 @@ protected IntegrationTestBase() { Client = new AkeneoClient(new AkeneoOptions { - ApiEndpoint = new Uri("http://localhost:8081"), - ClientId = "1_27xlkd53wou8ogggwwwksk48s0sgsoogwkowws8wko88gcs0os", - ClientSecret = "65rfqpc5a3okws0w4k0kgcwswwg0ggwg48wc40gcckso88sk44", - UserName = "admin", - Password = "admin" - }); + ApiEndpoint = new Uri("http://localhost:8081"), + ClientId = "1_27xlkd53wou8ogggwwwksk48s0sgsoogwkowws8wko88gcs0os", + ClientSecret = "65rfqpc5a3okws0w4k0kgcwswwg0ggwg48wc40gcckso88sk44", + UserName = "admin", + Password = "admin" + }); } public void Dispose() diff --git a/tests/Akeneo.IntegrationTests/ProductTests.cs b/tests/Akeneo.IntegrationTests/ProductTests.cs index 3e02c2a..2d09f18 100644 --- a/tests/Akeneo.IntegrationTests/ProductTests.cs +++ b/tests/Akeneo.IntegrationTests/ProductTests.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Net; using System.Threading; using System.Threading.Tasks; @@ -27,23 +28,23 @@ public async Task Should_Be_Able_To_Add_Update_And_Remove_Product() var createResponse = await Client.CreateAsync(product); var updateResponse = await Client.UpdateAsync(product); var deleteResponse = await Client.DeleteAsync(product.Identifier); - Assert.Equal(createResponse.Code, HttpStatusCode.Created); - Assert.Equal(updateResponse.Code, HttpStatusCode.NoContent); - Assert.Equal(deleteResponse.Code, HttpStatusCode.NoContent); + Assert.Equal(HttpStatusCode.Created, createResponse.Code); + Assert.Equal(HttpStatusCode.NoContent, updateResponse.Code); + Assert.Equal(HttpStatusCode.NoContent, deleteResponse.Code); } [Fact] public async Task Should_Be_Able_To_Search() { - var result = await Client.SearchAsync(new List - { - ProductValue.Contains("sku", "tv"), - Category.In("Default_Base_Pack_Template"), - Family.In("Default_Base_Pack_Template"), - Completeness.Equal(100, AkeneoDefaults.Channel), - Status.Enabled() - }); - } + var result = await Client.SearchAsync(new List + { + ProductValue.Contains("sku", "tv"), + Category.In("Default_Base_Pack_Template"), + Family.In("Default_Base_Pack_Template"), + Completeness.Equal(100, AkeneoDefaults.Channel), + Status.Enabled() + }); + } [Fact] public async Task Should_Be_Able_To_Update_With_Dynamic_Object() diff --git a/tests/Akeneo.Tests/Akeneo.Tests.csproj b/tests/Akeneo.Tests/Akeneo.Tests.csproj index 5cb56c2..af13e29 100644 --- a/tests/Akeneo.Tests/Akeneo.Tests.csproj +++ b/tests/Akeneo.Tests/Akeneo.Tests.csproj @@ -1,13 +1,16 @@  - netcoreapp1.0 + netcoreapp2.0 - - - + + + + all + runtime; build; native; contentfiles; analyzers + diff --git a/tests/Akeneo.Tests/Serialization/ProductValueDataConverterTests.cs b/tests/Akeneo.Tests/Serialization/ProductValueDataConverterTests.cs index 634d7a7..b151200 100644 --- a/tests/Akeneo.Tests/Serialization/ProductValueDataConverterTests.cs +++ b/tests/Akeneo.Tests/Serialization/ProductValueDataConverterTests.cs @@ -102,7 +102,7 @@ public void Should_Deserialize_Metric_Values_With_Decimals_Not_Allowed() /* Assert */ Assert.IsType(result); - Assert.Equal(((MetricProductValue)result).Amount, 13f); + Assert.Equal(13f, ((MetricProductValue)result).Amount); Assert.Equal(((MetricProductValue)result).Unit, PowerUnits.Kilowatt); } @@ -122,9 +122,9 @@ public void Should_Deserialize_List_Of_Strings() /* Assert */ if (result is List deserliazed) { - Assert.Equal(deserliazed[0], "red"); - Assert.Equal(deserliazed[1], "black"); - Assert.Equal(deserliazed[2], "grey"); + Assert.Equal("red", deserliazed[0]); + Assert.Equal("black", deserliazed[1]); + Assert.Equal("grey", deserliazed[2]); } else { @@ -148,9 +148,9 @@ public void Should_Deserialize_Array_Of_Price_Values() /* Assert */ if (result is List deserliazed) { - Assert.Equal(deserliazed[0].Amount, 45d); + Assert.Equal(45d, deserliazed[0].Amount); Assert.Equal(deserliazed[0].Currency, Currency.USD); - Assert.Equal(deserliazed[1].Amount, 56); + Assert.Equal(56, deserliazed[1].Amount); Assert.Equal(deserliazed[1].Currency, Currency.EUR); } else