diff --git a/Libs/Microsoft.AspNetCore.WebHooks.Sender.dll b/Libs/Microsoft.AspNetCore.WebHooks.Sender.dll new file mode 100644 index 0000000..8b37c4c Binary files /dev/null and b/Libs/Microsoft.AspNetCore.WebHooks.Sender.dll differ diff --git a/Nop.Plugin.Api/ApiAuthentication.cs b/Nop.Plugin.Api/ApiAuthentication.cs index 67281b3..7194c90 100644 --- a/Nop.Plugin.Api/ApiAuthentication.cs +++ b/Nop.Plugin.Api/ApiAuthentication.cs @@ -1,39 +1,39 @@ -namespace Nop.Plugin.Api -{ - using System.Collections.Generic; - using System.Linq; - using System.Security.Cryptography.X509Certificates; - using IdentityModel; - using Microsoft.AspNetCore.Authentication; - using Microsoft.AspNetCore.Authentication.JwtBearer; - using Microsoft.Extensions.DependencyInjection; - using Microsoft.IdentityModel.Tokens; - using Nop.Plugin.Api.Helpers; - using Nop.Services.Authentication.External; - using Org.BouncyCastle.Asn1.X509.Qualified; - - public class ApiAuthentication : IExternalAuthenticationRegistrar - { - public void Configure(AuthenticationBuilder builder) - { - RsaSecurityKey signingKey = CryptoHelper.CreateRsaSecurityKey(); - - builder.AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, jwt => - { - jwt.Audience = "nop_api"; - jwt.TokenValidationParameters = new TokenValidationParameters - { - ValidateActor = false, - ValidateIssuer = false, - NameClaimType = JwtClaimTypes.Name, - RoleClaimType = JwtClaimTypes.Role, - // Uncomment this if you are using an certificate to sign your tokens. - // IssuerSigningKey = new X509SecurityKey(cert), - IssuerSigningKeyResolver = (string token, SecurityToken securityToken, string kid, - TokenValidationParameters validationParameters) => - new List { signingKey } - }; - }); - } - } +namespace Nop.Plugin.Api +{ + using System.Collections.Generic; + using System.Linq; + using System.Security.Cryptography.X509Certificates; + using IdentityModel; + using Microsoft.AspNetCore.Authentication; + using Microsoft.AspNetCore.Authentication.JwtBearer; + using Microsoft.Extensions.DependencyInjection; + using Microsoft.IdentityModel.Tokens; + using Nop.Plugin.Api.Helpers; + using Nop.Services.Authentication.External; + using Org.BouncyCastle.Asn1.X509.Qualified; + + public class ApiAuthentication : IExternalAuthenticationRegistrar + { + public void Configure(AuthenticationBuilder builder) + { + RsaSecurityKey signingKey = CryptoHelper.CreateRsaSecurityKey(); + + builder.AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, jwt => + { + jwt.Audience = "nop_api"; + jwt.TokenValidationParameters = new TokenValidationParameters + { + ValidateActor = false, + ValidateIssuer = false, + NameClaimType = JwtClaimTypes.Name, + RoleClaimType = JwtClaimTypes.Role, + // Uncomment this if you are using an certificate to sign your tokens. + // IssuerSigningKey = new X509SecurityKey(cert), + IssuerSigningKeyResolver = (string token, SecurityToken securityToken, string kid, + TokenValidationParameters validationParameters) => + new List { signingKey } + }; + }); + } + } } \ No newline at end of file diff --git a/Nop.Plugin.Api/ApiMapperConfiguration.cs b/Nop.Plugin.Api/ApiMapperConfiguration.cs index 094ef8c..e933ae5 100644 --- a/Nop.Plugin.Api/ApiMapperConfiguration.cs +++ b/Nop.Plugin.Api/ApiMapperConfiguration.cs @@ -1,186 +1,202 @@ -namespace Nop.Plugin.Api -{ - using System.Collections.Generic; - using System.Linq; - using System.Net; - using global::AutoMapper; - using IdentityServer4.EntityFramework.Entities; - using Nop.Core.Domain.Catalog; - using Nop.Core.Domain.Common; - using Nop.Core.Domain.Customers; - using Nop.Core.Domain.Directory; - using Nop.Core.Domain.Localization; - using Nop.Core.Domain.Messages; - using Nop.Core.Domain.Orders; - using Nop.Core.Domain.Stores; - using Nop.Core.Infrastructure.Mapper; - using Nop.Plugin.Api.AutoMapper; - using Nop.Plugin.Api.Domain; - using Nop.Plugin.Api.DTOs; - using Nop.Plugin.Api.DTOs.Categories; - using Nop.Plugin.Api.DTOs.CustomerRoles; - using Nop.Plugin.Api.DTOs.Customers; - using Nop.Plugin.Api.DTOs.Languages; - using Nop.Plugin.Api.DTOs.OrderItems; - using Nop.Plugin.Api.DTOs.Orders; - using Nop.Plugin.Api.DTOs.ProductAttributes; - using Nop.Plugin.Api.DTOs.ProductCategoryMappings; - using Nop.Plugin.Api.DTOs.Products; - using Nop.Plugin.Api.DTOs.SpecificationAttributes; - using Nop.Plugin.Api.DTOs.ShoppingCarts; - using Nop.Plugin.Api.DTOs.Stores; - using Nop.Plugin.Api.MappingExtensions; - using Nop.Plugin.Api.Models; - - public class ApiMapperConfiguration : Profile, IMapperProfile - { - public ApiMapperConfiguration() - { - CreateMap(); - CreateMap(); - - CreateMap(); - CreateMap(); - - CreateMap(); - - CreateMap(); - - CreateMap(); - - CreateMap(); - - CreateClientToClientApiModelMap(); - - CreateAddressMap(); - CreateAddressDtoToEntityMap(); - CreateShoppingCartItemMap(); - - CreateCustomerToDTOMap(); - CreateCustomerToOrderCustomerDTOMap(); - CreateCustomerDTOToOrderCustomerDTOMap(); - CreateCustomerForShoppingCartItemMapFromCustomer(); - - CreateMap(); - CreateOrderEntityToOrderDtoMap(); - - CreateProductMap(); - - CreateMap(); - - CreateMap(); - - CreateMap(); - - CreateMap(); - CreateMap(); - - CreateMap(); - CreateMap(); - } - - private IMappingExpression CreateMap() - { - return AutoMapperApiConfiguration.MapperConfigurationExpression.CreateMap().IgnoreAllNonExisting(); - } - - private void CreateClientToClientApiModelMap() - { - AutoMapperApiConfiguration.MapperConfigurationExpression.CreateMap() - .ForMember(x => x.ClientSecret, y => y.MapFrom(src => src.ClientSecrets.FirstOrDefault().Description)) - .ForMember(x => x.RedirectUrl, y => y.MapFrom(src => src.RedirectUris.FirstOrDefault().RedirectUri)) - .ForMember(x => x.AccessTokenLifetime, y => y.MapFrom(src => src.AccessTokenLifetime)) - .ForMember(x => x.RefreshTokenLifetime, y => y.MapFrom(src => src.AbsoluteRefreshTokenLifetime)); - } - - private void CreateOrderEntityToOrderDtoMap() - { - AutoMapperApiConfiguration.MapperConfigurationExpression.CreateMap() - .IgnoreAllNonExisting() - .ForMember(x => x.Id, y => y.MapFrom(src => src.Id)) - .ForMember(x => x.OrderItemDtos, y => y.MapFrom(src => src.OrderItems.Select(x => x.ToDto()))); - } - - private void CreateAddressMap() - { - AutoMapperApiConfiguration.MapperConfigurationExpression.CreateMap() - .IgnoreAllNonExisting() - .ForMember(x => x.Id, y => y.MapFrom(src => src.Id)) - .ForMember(x => x.CountryName, y => y.MapFrom(src => src.Country.GetWithDefault(x => x, new Country()).Name)) - .ForMember(x => x.StateProvinceName, y => y.MapFrom(src => src.StateProvince.GetWithDefault(x => x, new StateProvince()).Name)); - } - - private void CreateAddressDtoToEntityMap() - { - AutoMapperApiConfiguration.MapperConfigurationExpression.CreateMap() - .IgnoreAllNonExisting() - .ForMember(x => x.Id, y => y.MapFrom(src => src.Id)); - } - - private void CreateCustomerForShoppingCartItemMapFromCustomer() - { - AutoMapperApiConfiguration.MapperConfigurationExpression.CreateMap() - .IgnoreAllNonExisting() - .ForMember(x => x.Id, y => y.MapFrom(src => src.Id)) - .ForMember(x => x.BillingAddress, y => y.MapFrom(src => src.BillingAddress.GetWithDefault(x => x, new Address()).ToDto())) - .ForMember(x => x.ShippingAddress, y => y.MapFrom(src => src.ShippingAddress.GetWithDefault(x => x, new Address()).ToDto())) - .ForMember(x => x.Addresses, y => y.MapFrom(src => src.Addresses.GetWithDefault(x => x, new List
()).Select(address => address.ToDto()))); - } - - private void CreateCustomerToDTOMap() - { - AutoMapperApiConfiguration.MapperConfigurationExpression.CreateMap() - .IgnoreAllNonExisting() - .ForMember(x => x.Id, y => y.MapFrom(src => src.Id)) - .ForMember(x => x.BillingAddress, - y => y.MapFrom(src => src.BillingAddress.GetWithDefault(x => x, new Address()).ToDto())) - .ForMember(x => x.ShippingAddress, - y => y.MapFrom(src => src.ShippingAddress.GetWithDefault(x => x, new Address()).ToDto())) - .ForMember(x => x.CustomerAddresses, - y => - y.MapFrom( - src => - src.Addresses.GetWithDefault(x => x, new List
()) - .Select(address => address.ToDto()))) - .ForMember(x => x.ShoppingCartItems, - y => - y.MapFrom( - src => - src.ShoppingCartItems.GetWithDefault(x => x, new List()) - .Select(item => item.ToDto()))) - .ForMember(x => x.RoleIds, y => y.MapFrom(src => src.CustomerRoles.Select(z => z.Id))); - } - - private void CreateCustomerToOrderCustomerDTOMap() - { - AutoMapperApiConfiguration.MapperConfigurationExpression.CreateMap() - .IgnoreAllNonExisting(); - } - - private void CreateCustomerDTOToOrderCustomerDTOMap() - { - AutoMapperApiConfiguration.MapperConfigurationExpression.CreateMap() - .IgnoreAllNonExisting(); - } - - private void CreateShoppingCartItemMap() - { - AutoMapperApiConfiguration.MapperConfigurationExpression.CreateMap() - .IgnoreAllNonExisting() - .ForMember(x => x.CustomerDto, y => y.MapFrom(src => src.Customer.GetWithDefault(x => x, new Customer()).ToCustomerForShoppingCartItemDto())) - .ForMember(x => x.ProductDto, y => y.MapFrom(src => src.Product.GetWithDefault(x => x, new Product()).ToDto())); - } - - private void CreateProductMap() - { - AutoMapperApiConfiguration.MapperConfigurationExpression.CreateMap() - .IgnoreAllNonExisting() - .ForMember(x => x.ProductAttributeMappings, y => y.Ignore()) - .ForMember(x => x.ProductSpecificationAttributes, y => y.Ignore()) - .ForMember(x => x.FullDescription, y => y.MapFrom(src => WebUtility.HtmlEncode(src.FullDescription))) - .ForMember(x => x.Tags, y => y.MapFrom(src => src.ProductTags.Select(x => x.Name))); - } - - public int Order { get; } - } +using AutoMapper; +using IdentityServer4.EntityFramework.Entities; +using Nop.Core.Domain.Catalog; +using Nop.Core.Domain.Common; +using Nop.Core.Domain.Customers; +using Nop.Core.Domain.Directory; +using Nop.Core.Domain.Localization; +using Nop.Core.Domain.Messages; +using Nop.Core.Domain.Orders; +using Nop.Core.Domain.Stores; +using Nop.Core.Infrastructure.Mapper; +using Nop.Plugin.Api.AutoMapper; +using Nop.Plugin.Api.Domain; +using Nop.Plugin.Api.DTOs; +using Nop.Plugin.Api.DTOs.Categories; +using Nop.Plugin.Api.DTOs.CustomerRoles; +using Nop.Plugin.Api.DTOs.Customers; +using Nop.Plugin.Api.DTOs.Languages; +using Nop.Plugin.Api.DTOs.Manufacturers; +using Nop.Plugin.Api.DTOs.OrderItems; +using Nop.Plugin.Api.DTOs.Orders; +using Nop.Plugin.Api.DTOs.ProductAttributes; +using Nop.Plugin.Api.DTOs.ProductCategoryMappings; +using Nop.Plugin.Api.DTOs.ProductManufacturerMappings; +using Nop.Plugin.Api.DTOs.Products; +using Nop.Plugin.Api.DTOs.ShoppingCarts; +using Nop.Plugin.Api.DTOs.SpecificationAttributes; +using Nop.Plugin.Api.DTOs.Stores; +using Nop.Plugin.Api.MappingExtensions; +using Nop.Plugin.Api.Models; +using System.Collections.Generic; +using System.Linq; +using System.Net; + +namespace Nop.Plugin.Api +{ + public class ApiMapperConfiguration : Profile, IOrderedMapperProfile + { + public ApiMapperConfiguration() + { + CreateMap(); + CreateMap(); + + CreateMap(); + CreateMap(); + + CreateMap(); + + CreateMap(); + + CreateMap(); + + CreateMap(); + + CreateMap(); + + CreateMap(); + + CreateClientToClientApiModelMap(); + + CreateAddressMap(); + CreateAddressDtoToEntityMap(); + CreateShoppingCartItemMap(); + + CreateCustomerToDTOMap(); + CreateCustomerToOrderCustomerDTOMap(); + CreateCustomerDTOToOrderCustomerDTOMap(); + CreateCustomerForShoppingCartItemMapFromCustomer(); + + CreateMap(); + CreateOrderEntityToOrderDtoMap(); + + CreateProductMap(); + + CreateMap(); + + CreateMap(); + + CreateMap(); + + CreateMap(); + CreateMap(); + + CreateMap(); + CreateMap(); + } + + public int Order => 0; + + private new static void CreateMap() + { + AutoMapperApiConfiguration.MapperConfigurationExpression.CreateMap() + .IgnoreAllNonExisting(); + } + + private static void CreateClientToClientApiModelMap() + { + AutoMapperApiConfiguration.MapperConfigurationExpression.CreateMap() + .ForMember(x => x.ClientSecret, y => y.MapFrom(src => src.ClientSecrets.FirstOrDefault().Description)) + .ForMember(x => x.RedirectUrl, y => y.MapFrom(src => src.RedirectUris.FirstOrDefault().RedirectUri)) + .ForMember(x => x.AccessTokenLifetime, y => y.MapFrom(src => src.AccessTokenLifetime)) + .ForMember(x => x.RefreshTokenLifetime, y => y.MapFrom(src => src.AbsoluteRefreshTokenLifetime)); + } + + private void CreateOrderEntityToOrderDtoMap() + { + AutoMapperApiConfiguration.MapperConfigurationExpression.CreateMap() + .IgnoreAllNonExisting() + .ForMember(x => x.Id, y => y.MapFrom(src => src.Id)) + .ForMember(x => x.OrderItems, y => y.MapFrom(src => src.OrderItems.Select(x => x.ToDto()))); + } + + private void CreateAddressMap() + { + AutoMapperApiConfiguration.MapperConfigurationExpression.CreateMap() + .IgnoreAllNonExisting() + .ForMember(x => x.Id, y => y.MapFrom(src => src.Id)) + .ForMember(x => x.CountryName, + y => y.MapFrom(src => src.Country.GetWithDefault(x => x, new Country()).Name)) + .ForMember(x => x.StateProvinceName, + y => y.MapFrom(src => src.StateProvince.GetWithDefault(x => x, new StateProvince()).Name)); + } + + private void CreateAddressDtoToEntityMap() + { + AutoMapperApiConfiguration.MapperConfigurationExpression.CreateMap() + .IgnoreAllNonExisting() + .ForMember(x => x.Id, y => y.MapFrom(src => src.Id)); + } + + private void CreateCustomerForShoppingCartItemMapFromCustomer() + { + AutoMapperApiConfiguration.MapperConfigurationExpression + .CreateMap() + .IgnoreAllNonExisting() + .ForMember(x => x.Id, y => y.MapFrom(src => src.Id)) + .ForMember(x => x.BillingAddress, + y => y.MapFrom(src => src.BillingAddress.GetWithDefault(x => x, new Address()).ToDto())) + .ForMember(x => x.ShippingAddress, + y => y.MapFrom(src => src.ShippingAddress.GetWithDefault(x => x, new Address()).ToDto())) + .ForMember(x => x.Addresses, + y => y.MapFrom(src => + src.Addresses.GetWithDefault(x => x, new List
()).Select(address => address.ToDto()))); + } + + private void CreateCustomerToDTOMap() + { + AutoMapperApiConfiguration.MapperConfigurationExpression.CreateMap() + .IgnoreAllNonExisting() + .ForMember(x => x.Id, y => y.MapFrom(src => src.Id)) + .ForMember(x => x.BillingAddress, + y => y.MapFrom(src => src.BillingAddress.GetWithDefault(x => x, new Address()).ToDto())) + .ForMember(x => x.ShippingAddress, + y => y.MapFrom(src => src.ShippingAddress.GetWithDefault(x => x, new Address()).ToDto())) + .ForMember(x => x.Addresses, + y => + y.MapFrom( + src => + src.Addresses.GetWithDefault(x => x, new List
()) + .Select(address => address.ToDto()))) + .ForMember(x => x.ShoppingCartItems, + y => + y.MapFrom( + src => + src.ShoppingCartItems.GetWithDefault(x => x, new List()) + .Select(item => item.ToDto()))) + .ForMember(x => x.RoleIds, y => y.MapFrom(src => src.CustomerRoles.Select(z => z.Id))); + } + + private void CreateCustomerToOrderCustomerDTOMap() + { + AutoMapperApiConfiguration.MapperConfigurationExpression.CreateMap() + .IgnoreAllNonExisting(); + } + + private void CreateCustomerDTOToOrderCustomerDTOMap() + { + AutoMapperApiConfiguration.MapperConfigurationExpression.CreateMap() + .IgnoreAllNonExisting(); + } + + private void CreateShoppingCartItemMap() + { + AutoMapperApiConfiguration.MapperConfigurationExpression.CreateMap() + .IgnoreAllNonExisting() + .ForMember(x => x.CustomerDto, + y => y.MapFrom(src => + src.Customer.GetWithDefault(x => x, new Customer()).ToCustomerForShoppingCartItemDto())) + .ForMember(x => x.ProductDto, + y => y.MapFrom(src => src.Product.GetWithDefault(x => x, new Product()).ToDto())); + } + + private void CreateProductMap() + { + AutoMapperApiConfiguration.MapperConfigurationExpression.CreateMap() + .IgnoreAllNonExisting() + .ForMember(x => x.FullDescription, y => y.MapFrom(src => WebUtility.HtmlEncode(src.FullDescription))) + .ForMember(x => x.Tags, + y => y.MapFrom(src => src.ProductProductTagMappings.Select(x => x.ProductTag.Name))); + } + } } \ No newline at end of file diff --git a/Nop.Plugin.Api/ApiPlugin.cs b/Nop.Plugin.Api/ApiPlugin.cs index 7b43ccd..ae5591d 100644 --- a/Nop.Plugin.Api/ApiPlugin.cs +++ b/Nop.Plugin.Api/ApiPlugin.cs @@ -1,242 +1,244 @@ -namespace Nop.Plugin.Api -{ - using IdentityServer4.EntityFramework.DbContexts; - using Microsoft.EntityFrameworkCore.Infrastructure; - using Microsoft.EntityFrameworkCore.Migrations; - using Nop.Core; - using Nop.Core.Infrastructure; - using Nop.Core.Plugins; - using Nop.Plugin.Api.Data; - using Nop.Plugin.Api.Domain; - using Nop.Plugin.Api.Helpers; - using Nop.Services.Configuration; - using Nop.Services.Localization; - using Nop.Web.Framework.Menu; - - public class ApiPlugin : BasePlugin, IAdminMenuPlugin - { - //private readonly IWebConfigMangerHelper _webConfigMangerHelper; - private readonly ApiObjectContext _objectContext; - private readonly ISettingService _settingService; - private readonly IWorkContext _workContext; - private readonly IWebHelper _webHelper; - private readonly ILocalizationService _localizationService; - - public ApiPlugin(ApiObjectContext objectContext,/*IWebConfigMangerHelper webConfigMangerHelper,*/ ISettingService settingService, IWorkContext workContext, - ILocalizationService localizationService, IWebHelper webHelper -/*, IConfiguration configuration*/) - { - _objectContext = objectContext; - //_webConfigMangerHelper = webConfigMangerHelper; - _settingService = settingService; - _workContext = workContext; - _localizationService = localizationService; - _webHelper = webHelper; - //_configuration = configuration; - } - - //private readonly IConfiguration _configuration; - - public override void Install() - { - var configManagerHelper = new NopConfigManagerHelper(); - - // some of third party libaries that we use for WebHooks and Swagger use older versions - // of certain assemblies so we need to redirect them to the those that nopCommerce uses - configManagerHelper.AddBindingRedirects(); - - // required by the WebHooks support - configManagerHelper.AddConnectionString(); - - _objectContext.Install(); - - //locales - this.AddOrUpdatePluginLocaleResource("Plugins.Api", "Api plugin"); - this.AddOrUpdatePluginLocaleResource("Plugins.Api.Admin.Menu.ManageClients", "Manage Api Clients"); - this.AddOrUpdatePluginLocaleResource("Plugins.Api.Admin.Configure", "Configure Web Api"); - this.AddOrUpdatePluginLocaleResource("Plugins.Api.Admin.GeneralSettings", "General Settings"); - this.AddOrUpdatePluginLocaleResource("Plugins.Api.Admin.EnableApi", "Enable Api"); - this.AddOrUpdatePluginLocaleResource("Plugins.Api.Admin.EnableApi.Hint", "By checking this settings you can Enable/Disable the Web Api"); - this.AddOrUpdatePluginLocaleResource("Plugins.Api.Admin.AllowRequestsFromSwagger", "Allow Requests From Swagger"); - this.AddOrUpdatePluginLocaleResource("Plugins.Api.Admin.AllowRequestsFromSwagger.Hint", "Swagger is the documentation generation tool used for the API (/Swagger). It has a client that enables it to make GET requests to the API endpoints. By enabling this option you will allow all requests from the swagger client. Do Not Enable on live site, it is only for demo sites or local testing!!!"); - - this.AddOrUpdatePluginLocaleResource("Plugins.Api.Admin.Menu.Title","API"); - this.AddOrUpdatePluginLocaleResource("Plugins.Api.Admin.Menu.Settings.Title","Settings"); - this.AddOrUpdatePluginLocaleResource("Plugins.Api.Admin.Menu.Clients.Title", "Clients"); - this.AddOrUpdatePluginLocaleResource("Plugins.Api.Admin.Menu.Docs.Title", "Docs"); - - this.AddOrUpdatePluginLocaleResource("Plugins.Api.Admin.Page.Settings.Title", "Api Settings"); - this.AddOrUpdatePluginLocaleResource("Plugins.Api.Admin.Page.Clients.Title", "Api Clients"); - - this.AddOrUpdatePluginLocaleResource("Plugins.Api.Admin.Page.Clients.Create.Title", "Add a new Api client"); - this.AddOrUpdatePluginLocaleResource("Plugins.Api.Admin.Page.Clients.Edit.Title", "Edit Api client"); - - this.AddOrUpdatePluginLocaleResource("Plugins.Api.Admin.Client.Name", "Name"); - this.AddOrUpdatePluginLocaleResource("Plugins.Api.Admin.Client.Name.Hint", "Name Hint"); - this.AddOrUpdatePluginLocaleResource("Plugins.Api.Admin.Client.ClientId", "Client Id"); - this.AddOrUpdatePluginLocaleResource("Plugins.Api.Admin.Client.ClientId.Hint", "The id of the client"); - this.AddOrUpdatePluginLocaleResource("Plugins.Api.Admin.Client.ClientSecret", "Client Secret"); - this.AddOrUpdatePluginLocaleResource("Plugins.Api.Admin.Client.ClientSecret.Hint", "The client secret is used during the authentication for obtaining the Access Token"); - this.AddOrUpdatePluginLocaleResource("Plugins.Api.Admin.Client.CallbackUrl", "Callback Url"); - this.AddOrUpdatePluginLocaleResource("Plugins.Api.Admin.Client.CallbackUrl.Hint", "The url where the Authorization code will be send"); - this.AddOrUpdatePluginLocaleResource("Plugins.Api.Admin.Client.IsActive", "Is Active"); - this.AddOrUpdatePluginLocaleResource("Plugins.Api.Admin.Client.IsActive.Hint", "You can use it to enable/disable the access to your store for the client"); - this.AddOrUpdatePluginLocaleResource("Plugins.Api.Admin.Client.AddNew", "Add New Client"); - this.AddOrUpdatePluginLocaleResource("Plugins.Api.Admin.Client.Edit", "Edit"); - this.AddOrUpdatePluginLocaleResource("Plugins.Api.Admin.Client.Created", "Created"); - this.AddOrUpdatePluginLocaleResource("Plugins.Api.Admin.Client.Deleted", "Deleted"); - this.AddOrUpdatePluginLocaleResource("Plugins.Api.Admin.Entities.Client.FieldValidationMessages.Name", "Name is required"); - this.AddOrUpdatePluginLocaleResource("Plugins.Api.Admin.Entities.Client.FieldValidationMessages.ClientId", "Client Id is required"); - this.AddOrUpdatePluginLocaleResource("Plugins.Api.Admin.Entities.Client.FieldValidationMessages.ClientSecret", "Client Secret is required"); - this.AddOrUpdatePluginLocaleResource("Plugins.Api.Admin.Entities.Client.FieldValidationMessages.CallbackUrl", "Callback Url is required"); - this.AddOrUpdatePluginLocaleResource("Plugins.Api.Admin.Settings.GeneralSettingsTitle", "General Settings"); - this.AddOrUpdatePluginLocaleResource("Plugins.Api.Admin.Edit", "Edit"); - this.AddOrUpdatePluginLocaleResource("Plugins.Api.Admin.Client.BackToList", "Back To List"); - - this.AddOrUpdatePluginLocaleResource("Api.Categories.Fields.Id.Invalid", "Id is invalid"); - this.AddOrUpdatePluginLocaleResource("Api.InvalidPropertyType", "Invalid Property Type"); - this.AddOrUpdatePluginLocaleResource("Api.InvalidType", "Invalid {0} type"); - this.AddOrUpdatePluginLocaleResource("Api.InvalidRequest", "Invalid request"); - this.AddOrUpdatePluginLocaleResource("Api.InvalidRootProperty", "Invalid root property"); - this.AddOrUpdatePluginLocaleResource("Api.NoJsonProvided", "No Json provided"); - this.AddOrUpdatePluginLocaleResource("Api.InvalidJsonFormat", "Json format is invalid"); - this.AddOrUpdatePluginLocaleResource("Api.Category.InvalidImageAttachmentFormat", "Invalid image attachment base64 format"); - this.AddOrUpdatePluginLocaleResource("Api.Category.InvalidImageSrc", "Invalid image source"); - this.AddOrUpdatePluginLocaleResource("Api.Category.InvalidImageSrcType", "You have provided an invalid image source/attachment "); - - this.AddOrUpdatePluginLocaleResource("Api.WebHooks.CouldNotRegisterWebhook", "Could not register WebHook due to error: {0}"); - this.AddOrUpdatePluginLocaleResource("Api.WebHooks.CouldNotRegisterDuplicateWebhook", "Could not register WebHook because a webhook with the same URI and Filters is already registered."); - this.AddOrUpdatePluginLocaleResource("Api.WebHooks.CouldNotUpdateWebhook", "Could not update WebHook due to error: {0}"); - this.AddOrUpdatePluginLocaleResource("Api.WebHooks.CouldNotDeleteWebhook", "Could not delete WebHook due to error: {0}"); - this.AddOrUpdatePluginLocaleResource("Api.WebHooks.CouldNotDeleteWebhooks", "Could not delete WebHooks due to error: {0}"); - this.AddOrUpdatePluginLocaleResource("Api.WebHooks.InvalidFilters", "The following filters are not valid: '{0}'. A list of valid filters can be obtained from the path '{1}'."); - - this.AddOrUpdatePluginLocaleResource("Plugins.Api.Admin.EnableLogging", "Enable Logging"); - this.AddOrUpdatePluginLocaleResource("Plugins.Api.Admin.EnableLogging.Hint", "By enable logging you will see webhook messages in the Log. These messages are needed ONLY for diagnostic purposes. NOTE: A restart is required when changing this setting in order to take effect"); - - ApiSettings settings = new ApiSettings - { - EnableApi = true, - AllowRequestsFromSwagger = false - }; - - _settingService.SaveSetting(settings); - - base.Install(); - - // Changes to Web.Config trigger application restart. - // This doesn't appear to affect the Install function, but just to be safe we will made web.config changes after the plugin was installed. - //_webConfigMangerHelper.AddConfiguration(); - } - - public override void Uninstall() - { - _objectContext.Uninstall(); - - var persistedGrantMigrator = EngineContext.Current.Resolve().GetService(); - persistedGrantMigrator.Migrate("0"); - - var configurationMigrator = EngineContext.Current.Resolve().GetService(); - configurationMigrator.Migrate("0"); - - // TODO: Delete all resources - //locales - this.DeletePluginLocaleResource("Plugins.Api"); - this.DeletePluginLocaleResource("Plugins.Api.Admin.Menu.ManageClients"); - - this.DeletePluginLocaleResource("Plugins.Api.Admin.Menu.Title"); - this.DeletePluginLocaleResource("Plugins.Api.Admin.Menu.Settings.Title"); - this.DeletePluginLocaleResource("Plugins.Api.Admin.Menu.Clients.Title"); - this.DeletePluginLocaleResource("Plugins.Api.Admin.Menu.Docs.Title"); - - this.DeletePluginLocaleResource("Plugins.Api.Admin.Configure"); - this.DeletePluginLocaleResource("Plugins.Api.Admin.GeneralSettings"); - this.DeletePluginLocaleResource("Plugins.Api.Admin.EnableApi"); - this.DeletePluginLocaleResource("Plugins.Api.Admin.EnableApi.Hint"); - this.DeletePluginLocaleResource("Plugins.Api.Admin.AllowRequestsFromSwagger"); - this.DeletePluginLocaleResource("Plugins.Api.Admin.AllowRequestsFromSwagger.Hint"); - - this.DeletePluginLocaleResource("Plugins.Api.Admin.Client.Name"); - this.DeletePluginLocaleResource("Plugins.Api.Admin.Client.ClientId"); - this.DeletePluginLocaleResource("Plugins.Api.Admin.Client.ClientSecret"); - this.DeletePluginLocaleResource("Plugins.Api.Admin.Client.CallbackUrl"); - this.DeletePluginLocaleResource("Plugins.Api.Admin.Client.IsActive"); - this.DeletePluginLocaleResource("Plugins.Api.Admin.Client.AddNew"); - this.DeletePluginLocaleResource("Plugins.Api.Admin.Client.Edit"); - this.DeletePluginLocaleResource("Plugins.Api.Admin.Client.Created"); - this.DeletePluginLocaleResource("Plugins.Api.Admin.Client.Deleted"); - this.DeletePluginLocaleResource("Plugins.Api.Admin.Entities.Client.FieldValidationMessages.Name"); - this.DeletePluginLocaleResource("Plugins.Api.Admin.Entities.Client.FieldValidationMessages.ClientId"); - this.DeletePluginLocaleResource("Plugins.Api.Admin.Entities.Client.FieldValidationMessages.ClientSecret"); - this.DeletePluginLocaleResource("Plugins.Api.Admin.Entities.Client.FieldValidationMessages.CallbackUrl"); - this.DeletePluginLocaleResource("Plugins.Api.Admin.Settings.GeneralSettingsTitle"); - this.DeletePluginLocaleResource("Plugins.Api.Admin.Edit"); - this.DeletePluginLocaleResource("Plugins.Api.Admin.Client.BackToList"); - - this.DeletePluginLocaleResource("Api.WebHooks.CouldNotRegisterWebhook"); - this.DeletePluginLocaleResource("Api.WebHooks.CouldNotRegisterDuplicateWebhook"); - this.DeletePluginLocaleResource("Api.WebHooks.CouldNotUpdateWebhook"); - this.DeletePluginLocaleResource("Api.WebHooks.CouldNotDeleteWebhook"); - this.DeletePluginLocaleResource("Api.WebHooks.CouldNotDeleteWebhooks"); - this.DeletePluginLocaleResource("Api.WebHooks.InvalidFilters"); - - base.Uninstall(); - - // Changes to Web.Config trigger application restart. - // This doesn't appear to affect the uninstall function, but just to be safe we will made web.config changes after the plugin was uninstalled. - //_webConfigMangerHelper.RemoveConfiguration(); - } - - public void ManageSiteMap(SiteMapNode rootNode) - { - string pluginMenuName = _localizationService.GetResource("Plugins.Api.Admin.Menu.Title",languageId: _workContext.WorkingLanguage.Id, defaultValue: "API"); - - string settingsMenuName = _localizationService.GetResource("Plugins.Api.Admin.Menu.Settings.Title", languageId: _workContext.WorkingLanguage.Id, defaultValue: "API"); - - string manageClientsMenuName = _localizationService.GetResource("Plugins.Api.Admin.Menu.Clients.Title", languageId: _workContext.WorkingLanguage.Id, defaultValue: "API"); - - const string adminUrlPart = "Admin/"; - - var pluginMainMenu = new SiteMapNode - { - Title = pluginMenuName, - Visible = true, - SystemName = "Api-Main-Menu", - IconClass = "fa-genderless" - }; - - pluginMainMenu.ChildNodes.Add(new SiteMapNode - { - Title = settingsMenuName, - Url = _webHelper.GetStoreLocation() + adminUrlPart + "ApiAdmin/Settings", - Visible = true, - SystemName = "Api-Settings-Menu", - IconClass = "fa-genderless" - }); - - pluginMainMenu.ChildNodes.Add(new SiteMapNode - { - Title = manageClientsMenuName, - Url = _webHelper.GetStoreLocation() + adminUrlPart + "ManageClientsAdmin/List", - Visible = true, - SystemName = "Api-Clients-Menu", - IconClass = "fa-genderless" - }); - - - string pluginDocumentationUrl = "https://github.com/SevenSpikes/api-plugin-for-nopcommerce"; - - pluginMainMenu.ChildNodes.Add(new SiteMapNode - { - Title = _localizationService.GetResource("Plugins.Api.Admin.Menu.Docs.Title"), - Url = pluginDocumentationUrl, - Visible = true, - SystemName = "Api-Docs-Menu", - IconClass = "fa-genderless" - });//TODO: target="_blank" - - - rootNode.ChildNodes.Add(pluginMainMenu); - } - } -} +namespace Nop.Plugin.Api +{ + using IdentityServer4.EntityFramework.DbContexts; + using Microsoft.EntityFrameworkCore.Infrastructure; + using Microsoft.EntityFrameworkCore.Migrations; + using Nop.Core; + using Nop.Core.Infrastructure; + using Nop.Core.Plugins; + using Nop.Plugin.Api.Data; + using Nop.Plugin.Api.Domain; + using Nop.Plugin.Api.Helpers; + using Nop.Services.Configuration; + using Nop.Services.Localization; + using Nop.Web.Framework.Menu; + + public class ApiPlugin : BasePlugin, IAdminMenuPlugin + { + //private readonly IWebConfigMangerHelper _webConfigMangerHelper; + private readonly ApiObjectContext _objectContext; + private readonly ISettingService _settingService; + private readonly IWorkContext _workContext; + private readonly IWebHelper _webHelper; + private readonly ILocalizationService _localizationService; + + public ApiPlugin(ApiObjectContext objectContext,/*IWebConfigMangerHelper webConfigMangerHelper,*/ ISettingService settingService, IWorkContext workContext, + ILocalizationService localizationService, IWebHelper webHelper +/*, IConfiguration configuration*/) + { + _objectContext = objectContext; + //_webConfigMangerHelper = webConfigMangerHelper; + _settingService = settingService; + _workContext = workContext; + _localizationService = localizationService; + _webHelper = webHelper; + //_configuration = configuration; + } + + //private readonly IConfiguration _configuration; + + public override void Install() + { + var configManagerHelper = new NopConfigManagerHelper(); + + // some of third party libaries that we use for WebHooks and Swagger use older versions + // of certain assemblies so we need to redirect them to the those that nopCommerce uses + // TODO: Upgrade 4.1. check this! + //configManagerHelper.AddBindingRedirects(); + + // required by the WebHooks support + // TODO: Upgrade 4.1. check this! + //configManagerHelper.AddConnectionString(); + + _objectContext.Install(); + + //locales + _localizationService.AddOrUpdatePluginLocaleResource("Plugins.Api", "Api plugin"); + _localizationService.AddOrUpdatePluginLocaleResource("Plugins.Api.Admin.Menu.ManageClients", "Manage Api Clients"); + _localizationService.AddOrUpdatePluginLocaleResource("Plugins.Api.Admin.Configure", "Configure Web Api"); + _localizationService.AddOrUpdatePluginLocaleResource("Plugins.Api.Admin.GeneralSettings", "General Settings"); + _localizationService.AddOrUpdatePluginLocaleResource("Plugins.Api.Admin.EnableApi", "Enable Api"); + _localizationService.AddOrUpdatePluginLocaleResource("Plugins.Api.Admin.EnableApi.Hint", "By checking this settings you can Enable/Disable the Web Api"); + _localizationService.AddOrUpdatePluginLocaleResource("Plugins.Api.Admin.AllowRequestsFromSwagger", "Allow Requests From Swagger"); + _localizationService.AddOrUpdatePluginLocaleResource("Plugins.Api.Admin.AllowRequestsFromSwagger.Hint", "Swagger is the documentation generation tool used for the API (/Swagger). It has a client that enables it to make GET requests to the API endpoints. By enabling this option you will allow all requests from the swagger client. Do Not Enable on live site, it is only for demo sites or local testing!!!"); + + _localizationService.AddOrUpdatePluginLocaleResource("Plugins.Api.Admin.Menu.Title","API"); + _localizationService.AddOrUpdatePluginLocaleResource("Plugins.Api.Admin.Menu.Settings.Title","Settings"); + _localizationService.AddOrUpdatePluginLocaleResource("Plugins.Api.Admin.Menu.Clients.Title", "Clients"); + _localizationService.AddOrUpdatePluginLocaleResource("Plugins.Api.Admin.Menu.Docs.Title", "Docs"); + + _localizationService.AddOrUpdatePluginLocaleResource("Plugins.Api.Admin.Page.Settings.Title", "Api Settings"); + _localizationService.AddOrUpdatePluginLocaleResource("Plugins.Api.Admin.Page.Clients.Title", "Api Clients"); + + _localizationService.AddOrUpdatePluginLocaleResource("Plugins.Api.Admin.Page.Clients.Create.Title", "Add a new Api client"); + _localizationService.AddOrUpdatePluginLocaleResource("Plugins.Api.Admin.Page.Clients.Edit.Title", "Edit Api client"); + + _localizationService.AddOrUpdatePluginLocaleResource("Plugins.Api.Admin.Client.Name", "Name"); + _localizationService.AddOrUpdatePluginLocaleResource("Plugins.Api.Admin.Client.Name.Hint", "Name Hint"); + _localizationService.AddOrUpdatePluginLocaleResource("Plugins.Api.Admin.Client.ClientId", "Client Id"); + _localizationService.AddOrUpdatePluginLocaleResource("Plugins.Api.Admin.Client.ClientId.Hint", "The id of the client"); + _localizationService.AddOrUpdatePluginLocaleResource("Plugins.Api.Admin.Client.ClientSecret", "Client Secret"); + _localizationService.AddOrUpdatePluginLocaleResource("Plugins.Api.Admin.Client.ClientSecret.Hint", "The client secret is used during the authentication for obtaining the Access Token"); + _localizationService.AddOrUpdatePluginLocaleResource("Plugins.Api.Admin.Client.CallbackUrl", "Callback Url"); + _localizationService.AddOrUpdatePluginLocaleResource("Plugins.Api.Admin.Client.CallbackUrl.Hint", "The url where the Authorization code will be send"); + _localizationService.AddOrUpdatePluginLocaleResource("Plugins.Api.Admin.Client.IsActive", "Is Active"); + _localizationService.AddOrUpdatePluginLocaleResource("Plugins.Api.Admin.Client.IsActive.Hint", "You can use it to enable/disable the access to your store for the client"); + _localizationService.AddOrUpdatePluginLocaleResource("Plugins.Api.Admin.Client.AddNew", "Add New Client"); + _localizationService.AddOrUpdatePluginLocaleResource("Plugins.Api.Admin.Client.Edit", "Edit"); + _localizationService.AddOrUpdatePluginLocaleResource("Plugins.Api.Admin.Client.Created", "Created"); + _localizationService.AddOrUpdatePluginLocaleResource("Plugins.Api.Admin.Client.Deleted", "Deleted"); + _localizationService.AddOrUpdatePluginLocaleResource("Plugins.Api.Admin.Entities.Client.FieldValidationMessages.Name", "Name is required"); + _localizationService.AddOrUpdatePluginLocaleResource("Plugins.Api.Admin.Entities.Client.FieldValidationMessages.ClientId", "Client Id is required"); + _localizationService.AddOrUpdatePluginLocaleResource("Plugins.Api.Admin.Entities.Client.FieldValidationMessages.ClientSecret", "Client Secret is required"); + _localizationService.AddOrUpdatePluginLocaleResource("Plugins.Api.Admin.Entities.Client.FieldValidationMessages.CallbackUrl", "Callback Url is required"); + _localizationService.AddOrUpdatePluginLocaleResource("Plugins.Api.Admin.Settings.GeneralSettingsTitle", "General Settings"); + _localizationService.AddOrUpdatePluginLocaleResource("Plugins.Api.Admin.Edit", "Edit"); + _localizationService.AddOrUpdatePluginLocaleResource("Plugins.Api.Admin.Client.BackToList", "Back To List"); + + _localizationService.AddOrUpdatePluginLocaleResource("Api.Categories.Fields.Id.Invalid", "Id is invalid"); + _localizationService.AddOrUpdatePluginLocaleResource("Api.InvalidPropertyType", "Invalid Property Type"); + _localizationService.AddOrUpdatePluginLocaleResource("Api.InvalidType", "Invalid {0} type"); + _localizationService.AddOrUpdatePluginLocaleResource("Api.InvalidRequest", "Invalid request"); + _localizationService.AddOrUpdatePluginLocaleResource("Api.InvalidRootProperty", "Invalid root property"); + _localizationService.AddOrUpdatePluginLocaleResource("Api.NoJsonProvided", "No Json provided"); + _localizationService.AddOrUpdatePluginLocaleResource("Api.InvalidJsonFormat", "Json format is invalid"); + _localizationService.AddOrUpdatePluginLocaleResource("Api.Category.InvalidImageAttachmentFormat", "Invalid image attachment base64 format"); + _localizationService.AddOrUpdatePluginLocaleResource("Api.Category.InvalidImageSrc", "Invalid image source"); + _localizationService.AddOrUpdatePluginLocaleResource("Api.Category.InvalidImageSrcType", "You have provided an invalid image source/attachment "); + + _localizationService.AddOrUpdatePluginLocaleResource("Api.WebHooks.CouldNotRegisterWebhook", "Could not register WebHook due to error: {0}"); + _localizationService.AddOrUpdatePluginLocaleResource("Api.WebHooks.CouldNotRegisterDuplicateWebhook", "Could not register WebHook because a webhook with the same URI and Filters is already registered."); + _localizationService.AddOrUpdatePluginLocaleResource("Api.WebHooks.CouldNotUpdateWebhook", "Could not update WebHook due to error: {0}"); + _localizationService.AddOrUpdatePluginLocaleResource("Api.WebHooks.CouldNotDeleteWebhook", "Could not delete WebHook due to error: {0}"); + _localizationService.AddOrUpdatePluginLocaleResource("Api.WebHooks.CouldNotDeleteWebhooks", "Could not delete WebHooks due to error: {0}"); + _localizationService.AddOrUpdatePluginLocaleResource("Api.WebHooks.InvalidFilters", "The following filters are not valid: '{0}'. A list of valid filters can be obtained from the path '{1}'."); + + _localizationService.AddOrUpdatePluginLocaleResource("Plugins.Api.Admin.EnableLogging", "Enable Logging"); + _localizationService.AddOrUpdatePluginLocaleResource("Plugins.Api.Admin.EnableLogging.Hint", "By enable logging you will see webhook messages in the Log. These messages are needed ONLY for diagnostic purposes. NOTE: A restart is required when changing this setting in order to take effect"); + + ApiSettings settings = new ApiSettings + { + EnableApi = true, + AllowRequestsFromSwagger = false + }; + + _settingService.SaveSetting(settings); + + base.Install(); + + // Changes to Web.Config trigger application restart. + // This doesn't appear to affect the Install function, but just to be safe we will made web.config changes after the plugin was installed. + //_webConfigMangerHelper.AddConfiguration(); + } + + public override void Uninstall() + { + _objectContext.Uninstall(); + + var persistedGrantMigrator = EngineContext.Current.Resolve().GetService(); + persistedGrantMigrator.Migrate("0"); + + var configurationMigrator = EngineContext.Current.Resolve().GetService(); + configurationMigrator.Migrate("0"); + + // TODO: Delete all resources + //locales + _localizationService.DeletePluginLocaleResource("Plugins.Api"); + _localizationService.DeletePluginLocaleResource("Plugins.Api.Admin.Menu.ManageClients"); + + _localizationService.DeletePluginLocaleResource("Plugins.Api.Admin.Menu.Title"); + _localizationService.DeletePluginLocaleResource("Plugins.Api.Admin.Menu.Settings.Title"); + _localizationService.DeletePluginLocaleResource("Plugins.Api.Admin.Menu.Clients.Title"); + _localizationService.DeletePluginLocaleResource("Plugins.Api.Admin.Menu.Docs.Title"); + + _localizationService.DeletePluginLocaleResource("Plugins.Api.Admin.Configure"); + _localizationService.DeletePluginLocaleResource("Plugins.Api.Admin.GeneralSettings"); + _localizationService.DeletePluginLocaleResource("Plugins.Api.Admin.EnableApi"); + _localizationService.DeletePluginLocaleResource("Plugins.Api.Admin.EnableApi.Hint"); + _localizationService.DeletePluginLocaleResource("Plugins.Api.Admin.AllowRequestsFromSwagger"); + _localizationService.DeletePluginLocaleResource("Plugins.Api.Admin.AllowRequestsFromSwagger.Hint"); + + _localizationService.DeletePluginLocaleResource("Plugins.Api.Admin.Client.Name"); + _localizationService.DeletePluginLocaleResource("Plugins.Api.Admin.Client.ClientId"); + _localizationService.DeletePluginLocaleResource("Plugins.Api.Admin.Client.ClientSecret"); + _localizationService.DeletePluginLocaleResource("Plugins.Api.Admin.Client.CallbackUrl"); + _localizationService.DeletePluginLocaleResource("Plugins.Api.Admin.Client.IsActive"); + _localizationService.DeletePluginLocaleResource("Plugins.Api.Admin.Client.AddNew"); + _localizationService.DeletePluginLocaleResource("Plugins.Api.Admin.Client.Edit"); + _localizationService.DeletePluginLocaleResource("Plugins.Api.Admin.Client.Created"); + _localizationService.DeletePluginLocaleResource("Plugins.Api.Admin.Client.Deleted"); + _localizationService.DeletePluginLocaleResource("Plugins.Api.Admin.Entities.Client.FieldValidationMessages.Name"); + _localizationService.DeletePluginLocaleResource("Plugins.Api.Admin.Entities.Client.FieldValidationMessages.ClientId"); + _localizationService.DeletePluginLocaleResource("Plugins.Api.Admin.Entities.Client.FieldValidationMessages.ClientSecret"); + _localizationService.DeletePluginLocaleResource("Plugins.Api.Admin.Entities.Client.FieldValidationMessages.CallbackUrl"); + _localizationService.DeletePluginLocaleResource("Plugins.Api.Admin.Settings.GeneralSettingsTitle"); + _localizationService.DeletePluginLocaleResource("Plugins.Api.Admin.Edit"); + _localizationService.DeletePluginLocaleResource("Plugins.Api.Admin.Client.BackToList"); + + _localizationService.DeletePluginLocaleResource("Api.WebHooks.CouldNotRegisterWebhook"); + _localizationService.DeletePluginLocaleResource("Api.WebHooks.CouldNotRegisterDuplicateWebhook"); + _localizationService.DeletePluginLocaleResource("Api.WebHooks.CouldNotUpdateWebhook"); + _localizationService.DeletePluginLocaleResource("Api.WebHooks.CouldNotDeleteWebhook"); + _localizationService.DeletePluginLocaleResource("Api.WebHooks.CouldNotDeleteWebhooks"); + _localizationService.DeletePluginLocaleResource("Api.WebHooks.InvalidFilters"); + + base.Uninstall(); + + // Changes to Web.Config trigger application restart. + // This doesn't appear to affect the uninstall function, but just to be safe we will made web.config changes after the plugin was uninstalled. + //_webConfigMangerHelper.RemoveConfiguration(); + } + + public void ManageSiteMap(SiteMapNode rootNode) + { + string pluginMenuName = _localizationService.GetResource("Plugins.Api.Admin.Menu.Title",languageId: _workContext.WorkingLanguage.Id, defaultValue: "API"); + + string settingsMenuName = _localizationService.GetResource("Plugins.Api.Admin.Menu.Settings.Title", languageId: _workContext.WorkingLanguage.Id, defaultValue: "API"); + + string manageClientsMenuName = _localizationService.GetResource("Plugins.Api.Admin.Menu.Clients.Title", languageId: _workContext.WorkingLanguage.Id, defaultValue: "API"); + + const string adminUrlPart = "Admin/"; + + var pluginMainMenu = new SiteMapNode + { + Title = pluginMenuName, + Visible = true, + SystemName = "Api-Main-Menu", + IconClass = "fa-genderless" + }; + + pluginMainMenu.ChildNodes.Add(new SiteMapNode + { + Title = settingsMenuName, + Url = _webHelper.GetStoreLocation() + adminUrlPart + "ApiAdmin/Settings", + Visible = true, + SystemName = "Api-Settings-Menu", + IconClass = "fa-genderless" + }); + + pluginMainMenu.ChildNodes.Add(new SiteMapNode + { + Title = manageClientsMenuName, + Url = _webHelper.GetStoreLocation() + adminUrlPart + "ManageClientsAdmin/List", + Visible = true, + SystemName = "Api-Clients-Menu", + IconClass = "fa-genderless" + }); + + + string pluginDocumentationUrl = "https://github.com/SevenSpikes/api-plugin-for-nopcommerce"; + + pluginMainMenu.ChildNodes.Add(new SiteMapNode + { + Title = _localizationService.GetResource("Plugins.Api.Admin.Menu.Docs.Title"), + Url = pluginDocumentationUrl, + Visible = true, + SystemName = "Api-Docs-Menu", + IconClass = "fa-genderless" + });//TODO: target="_blank" + + + rootNode.ChildNodes.Add(pluginMainMenu); + } + } +} diff --git a/Nop.Plugin.Api/ApiStartup.cs b/Nop.Plugin.Api/ApiStartup.cs index 6314501..17ad3eb 100644 --- a/Nop.Plugin.Api/ApiStartup.cs +++ b/Nop.Plugin.Api/ApiStartup.cs @@ -1,290 +1,314 @@ -namespace Nop.Plugin.Api -{ - using System; - using System.Collections.Generic; - using System.Globalization; - using System.IdentityModel.Tokens.Jwt; - using System.IO; - using System.Linq; - using System.Linq.Dynamic; - using System.Reflection; - using System.Security.Cryptography.X509Certificates; - using System.Xml.Linq; - using System.Xml.XPath; - using IdentityServer4.EntityFramework.DbContexts; - using IdentityServer4.EntityFramework.Entities; - using IdentityServer4.Hosting; - using IdentityServer4.Models; - using Microsoft.AspNet.WebHooks.Diagnostics; - using Microsoft.AspNetCore.Authentication.JwtBearer; - using Microsoft.AspNetCore.Authorization; - using Microsoft.AspNetCore.Builder; - using Microsoft.AspNetCore.Rewrite; - using Microsoft.EntityFrameworkCore; - using Microsoft.Extensions.Configuration; - using Microsoft.Extensions.DependencyInjection; - using Microsoft.IdentityModel.Tokens; - using Nop.Core; - using Nop.Core.Data; - using Nop.Core.Infrastructure; - using Nop.Plugin.Api.Authorization.Policies; - using Nop.Plugin.Api.Authorization.Requirements; - using Nop.Plugin.Api.Constants; - using Nop.Plugin.Api.Helpers; - using Nop.Plugin.Api.IdentityServer.Endpoints; - using Nop.Plugin.Api.IdentityServer.Generators; - using Nop.Plugin.Api.IdentityServer.Middlewares; - using Nop.Plugin.Api.WebHooks; - using ApiResource = IdentityServer4.EntityFramework.Entities.ApiResource; - - public class ApiStartup : INopStartup - { - // TODO: extract all methods into extensions. - public void ConfigureServices(IServiceCollection services, IConfigurationRoot configuration) - { - AddRequiredConfiguration(); - - AddBindingRedirectsFallbacks(); - - JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear(); - - AddTokenGenerationPipeline(services); - - AddAuthorizationPipeline(services); - } - - public void Configure(IApplicationBuilder app) - { - // The default route templates for the Swagger docs and swagger - ui are "swagger/docs/{apiVersion}" and "swagger/ui/index#/{assetPath}" respectively. - //app.UseSwagger(); - //app.UseSwaggerUI(options => - // { - // //var currentAssembly = Assembly.GetAssembly(this.GetType()); - // //var currentAssemblyName = currentAssembly.GetName().Name; - - // //Needeed for removing the "Try It Out" button from the post and put methods. - // //http://stackoverflow.com/questions/36772032/swagger-5-2-3-supportedsubmitmethods-removed/36780806#36780806 - - // //options.InjectOnCompleteJavaScript($"{currentAssemblyName}.Scripts.swaggerPostPutTryItOutButtonsRemoval.js"); - - // options.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1"); - // } - //); - - // This needs to be called here because in the plugin install method identity server is not yet registered. - ApplyIdentityServerMigrations(app); - - SeedData(app); - - var rewriteOptions = new RewriteOptions() - .AddRewrite("oauth/(.*)", "connect/$1",true) - .AddRewrite("api/token", "connect/token",true); - - app.UseRewriter(rewriteOptions); - - app.UseMiddleware(); - - ////uncomment only if the client is an angular application that directly calls the oauth endpoint - //// app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll); - UseIdentityServer(app); - } - - private void UseIdentityServer(IApplicationBuilder app) - { - // The code below is a copy of app.UseIdentityServer(); - // but the nopCommerce AuthenticationMiddleware is added by nopCommmerce and - // it has a try catch for the non-configured properly external authentication providers i.e Facebook - // So there is no need to call UseAuthentication again and thus not being able to catch exceptions thrown by Facebook - - //app.Validate(); - UseMiddlewareExtensions.UseMiddleware(app); - app.ConfigureCors(); - //app.UseAuthentication(); - UseMiddlewareExtensions.UseMiddleware(app); - } - - private void AddRequiredConfiguration() - { - var configManagerHelper = new NopConfigManagerHelper(); - - // some of third party libaries that we use for WebHooks and Swagger use older versions - // of certain assemblies so we need to redirect them to the once that nopCommerce uses - configManagerHelper.AddBindingRedirects(); - - // required by the WebHooks support - configManagerHelper.AddConnectionString(); - - // This is required only in development. - // It it is required only when you want to send a web hook to an https address with an invalid SSL certificate. (self-signed) - // The code marks all certificates as valid. - // We may want to extract this as a setting in the future. - - // NOTE: If this code is commented the certificates will be validated. - System.Net.ServicePointManager.ServerCertificateValidationCallback += (sender, cert, chain, sslPolicyErrors) => true; - } - - private void AddAuthorizationPipeline(IServiceCollection services) - { - services.AddAuthorization(options => - { - options.AddPolicy(JwtBearerDefaults.AuthenticationScheme, - policy => - { - policy.Requirements.Add(new ActiveApiPluginRequirement()); - policy.Requirements.Add(new AuthorizationSchemeRequirement()); - policy.Requirements.Add(new ActiveClientRequirement()); - policy.Requirements.Add(new RequestFromSwaggerOptional()); - policy.RequireAuthenticatedUser(); - }); - }); - - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - } - - private void AddTokenGenerationPipeline(IServiceCollection services) - { - RsaSecurityKey signingKey = CryptoHelper.CreateRsaSecurityKey(); - - DataSettingsManager dataSettingsManager = new DataSettingsManager(); - - DataSettings dataSettings = dataSettingsManager.LoadSettings(); - string connectionStringFromNop = dataSettings.DataConnectionString; - - var migrationsAssembly = typeof(ApiStartup).GetTypeInfo().Assembly.GetName().Name; - - services.AddIdentityServer() - .AddSigningCredential(signingKey) - .AddConfigurationStore(options => - { - options.ConfigureDbContext = builder => - builder.UseSqlServer(connectionStringFromNop, - sql => sql.MigrationsAssembly(migrationsAssembly)); - }) - .AddOperationalStore(options => - { - options.ConfigureDbContext = builder => - builder.UseSqlServer(connectionStringFromNop, - sql => sql.MigrationsAssembly(migrationsAssembly)); - }) - .AddAuthorizeInteractionResponseGenerator() - .AddEndpoint("Authorize", "/oauth/authorize/callback") - .AddEndpoint("Authorize", "/oauth/authorize") - .AddEndpoint("Token", "/oauth/token"); - } - - private void ApplyIdentityServerMigrations(IApplicationBuilder app) - { - using (var serviceScope = app.ApplicationServices.GetService().CreateScope()) - { - // the database.Migrate command will apply all pending migrations and will create the database if it is not created already. - var persistedGrantContext = serviceScope.ServiceProvider.GetRequiredService(); - persistedGrantContext.Database.Migrate(); - - var configurationContext = serviceScope.ServiceProvider.GetRequiredService(); - configurationContext.Database.Migrate(); - } - } - - private void SeedData(IApplicationBuilder app) - { - using (var serviceScope = app.ApplicationServices.GetService().CreateScope()) - { - var configurationContext = serviceScope.ServiceProvider.GetRequiredService(); - - if (!DynamicQueryable.Any(configurationContext.ApiResources)) - { - // In the simple case an API has exactly one scope. But there are cases where you might want to sub-divide the functionality of an API, and give different clients access to different parts. - configurationContext.ApiResources.Add(new ApiResource() - { - Enabled = true, - Scopes = new List() - { - new ApiScope() - { - Name = "nop_api", - DisplayName = "nop_api" - } - }, - Name = "nop_api" - }); - - configurationContext.SaveChanges(); - - TryRunUpgradeScript(configurationContext); - } - } - } - - private string LoadUpgradeScript() - { - string path = CommonHelper.MapPath("~/Plugins/Nop.Plugin.Api/upgrade_script.sql"); - string script = File.ReadAllText(path); - - return script; - } - - private void TryRunUpgradeScript(ConfigurationDbContext configurationContext) - { - try - { - // If there are no api resources we can assume that this is the first start after the upgrade and run the upgrade script. - string upgradeScript = LoadUpgradeScript(); - configurationContext.Database.ExecuteSqlCommand(upgradeScript); - - // All client secrets must be hashed otherwise the identity server validation will fail. - var allClients = - Enumerable.ToList(configurationContext.Clients.Include(client => client.ClientSecrets)); - foreach (var client in allClients) - { - foreach (var clientSecret in client.ClientSecrets) - { - clientSecret.Value = HashExtensions.Sha256(clientSecret.Value); - } - - client.AccessTokenLifetime = Configurations.DefaultAccessTokenExpiration; - client.AbsoluteRefreshTokenLifetime = Configurations.DefaultRefreshTokenExpiration; - } - - configurationContext.SaveChanges(); - } - catch (Exception ex) - { - // Probably the upgrade script was already executed and we don't need to do anything. - } - } - - public void AddBindingRedirectsFallbacks() - { - // If no binding redirects are present in the config file then this will perform the binding redirect - RedirectAssembly("Microsoft.AspNetCore.DataProtection.Abstractions", new Version(2, 0, 0, 0), "adb9793829ddae60"); - } - - ///Adds an AssemblyResolve handler to redirect all attempts to load a specific assembly name to the specified version. - public static void RedirectAssembly(string shortName, Version targetVersion, string publicKeyToken) - { - ResolveEventHandler handler = null; - - handler = (sender, args) => - { - // Use latest strong name & version when trying to load SDK assemblies - var requestedAssembly = new AssemblyName(args.Name); - if (requestedAssembly.Name != shortName) - return null; - - requestedAssembly.Version = targetVersion; - requestedAssembly.SetPublicKeyToken(new AssemblyName("x, PublicKeyToken=" + publicKeyToken).GetPublicKeyToken()); - requestedAssembly.CultureInfo = CultureInfo.InvariantCulture; - - AppDomain.CurrentDomain.AssemblyResolve -= handler; - - return Assembly.Load(requestedAssembly); - }; - AppDomain.CurrentDomain.AssemblyResolve += handler; - } - - public int Order { get; } - } +using Nop.Plugin.Api.Data; +using Nop.Web.Framework.Infrastructure.Extensions; + +namespace Nop.Plugin.Api +{ + using IdentityServer4.EntityFramework.DbContexts; + using IdentityServer4.EntityFramework.Entities; + using IdentityServer4.Hosting; + using IdentityServer4.Models; + using Microsoft.AspNetCore.Authentication.JwtBearer; + using Microsoft.AspNetCore.Authorization; + using Microsoft.AspNetCore.Builder; + using Microsoft.AspNetCore.Http; + using Microsoft.AspNetCore.Rewrite; + using Microsoft.EntityFrameworkCore; + using Microsoft.Extensions.Configuration; + using Microsoft.Extensions.DependencyInjection; + using Microsoft.IdentityModel.Tokens; + using Nop.Core.Data; + using Nop.Core.Infrastructure; + using Nop.Plugin.Api.Authorization.Policies; + using Nop.Plugin.Api.Authorization.Requirements; + using Nop.Plugin.Api.Constants; + using Nop.Plugin.Api.Helpers; + using Nop.Plugin.Api.IdentityServer.Endpoints; + using Nop.Plugin.Api.IdentityServer.Generators; + using Nop.Plugin.Api.IdentityServer.Middlewares; + using Nop.Web.Framework.Infrastructure; + using System; + using System.Collections.Generic; + using System.Globalization; + using System.IdentityModel.Tokens.Jwt; + using System.IO; + using System.Linq; + using System.Linq.Dynamic.Core; + using System.Reflection; + using ApiResource = IdentityServer4.EntityFramework.Entities.ApiResource; + + public class ApiStartup : INopStartup + { + private const string ObjectContextName = "nop_object_context_web_api"; + + // TODO: extract all methods into extensions. + public void ConfigureServices(IServiceCollection services, IConfiguration configuration) + { + services.AddDbContext(optionsBuilder => + { + optionsBuilder.UseSqlServerWithLazyLoading(services); + },ServiceLifetime.Transient); + + AddRequiredConfiguration(); + + AddBindingRedirectsFallbacks(); + + JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear(); + + AddTokenGenerationPipeline(services); + + AddAuthorizationPipeline(services); + } + + public void Configure(IApplicationBuilder app) + { + // During a clean install we should not register any middlewares i.e IdentityServer as it won't be able to create its + // tables without a connection string and will throw an exception + var dataSettings = DataSettingsManager.LoadSettings(); + if (!dataSettings?.IsValid ?? true) + return; + + // The default route templates for the Swagger docs and swagger - ui are "swagger/docs/{apiVersion}" and "swagger/ui/index#/{assetPath}" respectively. + //app.UseSwagger(); + //app.UseSwaggerUI(options => + // { + // //var currentAssembly = Assembly.GetAssembly(this.GetType()); + // //var currentAssemblyName = currentAssembly.GetName().Name; + + // //Needeed for removing the "Try It Out" button from the post and put methods. + // //http://stackoverflow.com/questions/36772032/swagger-5-2-3-supportedsubmitmethods-removed/36780806#36780806 + + // //options.InjectOnCompleteJavaScript($"{currentAssemblyName}.Scripts.swaggerPostPutTryItOutButtonsRemoval.js"); + + // options.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1"); + // } + //); + + // This needs to be called here because in the plugin install method identity server is not yet registered. + ApplyIdentityServerMigrations(app); + + SeedData(app); + + + var rewriteOptions = new RewriteOptions() + .AddRewrite("oauth/(.*)", "connect/$1", true) + .AddRewrite("api/token", "connect/token", true); + + app.UseRewriter(rewriteOptions); + + app.UseMiddleware(); + + ////uncomment only if the client is an angular application that directly calls the oauth endpoint + //// app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll); + UseIdentityServer(app); + + //need to enable rewind so we can read the request body multiple times (this should eventually be refactored, but both JsonModelBinder and all of the DTO validators need to read this stream) + app.Use(async (context, next) => + { + context.Request.EnableBuffering(); + await next(); + }); + } + + private void UseIdentityServer(IApplicationBuilder app) + { + // The code below is a copy of app.UseIdentityServer(); + // but the nopCommerce AuthenticationMiddleware is added by nopCommmerce and + // it has a try catch for the non-configured properly external authentication providers i.e Facebook + // So there is no need to call UseAuthentication again and thus not being able to catch exceptions thrown by Facebook + + //app.Validate(); + UseMiddlewareExtensions.UseMiddleware(app); + app.ConfigureCors(); + //app.UseAuthentication(); + UseMiddlewareExtensions.UseMiddleware(app); + } + + private void AddRequiredConfiguration() + { + var configManagerHelper = new NopConfigManagerHelper(); + + // some of third party libaries that we use for WebHooks and Swagger use older versions + // of certain assemblies so we need to redirect them to the once that nopCommerce uses + //TODO: Upgrade 4.10 check this! + //configManagerHelper.AddBindingRedirects(); + + // required by the WebHooks support + //TODO: Upgrade 4.10 check this! + //configManagerHelper.AddConnectionString(); + + // This is required only in development. + // It it is required only when you want to send a web hook to an https address with an invalid SSL certificate. (self-signed) + // The code marks all certificates as valid. + // We may want to extract this as a setting in the future. + + // NOTE: If this code is commented the certificates will be validated. + System.Net.ServicePointManager.ServerCertificateValidationCallback += (sender, cert, chain, sslPolicyErrors) => true; + } + + private void AddAuthorizationPipeline(IServiceCollection services) + { + services.AddAuthorization(options => + { + options.AddPolicy(JwtBearerDefaults.AuthenticationScheme, + policy => + { + policy.Requirements.Add(new ActiveApiPluginRequirement()); + policy.Requirements.Add(new AuthorizationSchemeRequirement()); + policy.Requirements.Add(new ActiveClientRequirement()); + policy.Requirements.Add(new RequestFromSwaggerOptional()); + policy.RequireAuthenticatedUser(); + }); + }); + + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + } + + private void AddTokenGenerationPipeline(IServiceCollection services) + { + RsaSecurityKey signingKey = CryptoHelper.CreateRsaSecurityKey(); + + DataSettings dataSettings = DataSettingsManager.LoadSettings(); + if (!dataSettings?.IsValid ?? true) + return; + + string connectionStringFromNop = dataSettings.DataConnectionString; + + var migrationsAssembly = typeof(ApiStartup).GetTypeInfo().Assembly.GetName().Name; + + services.AddIdentityServer() + .AddSigningCredential(signingKey) + .AddConfigurationStore(options => + { + options.ConfigureDbContext = builder => + builder.UseSqlServer(connectionStringFromNop, + sql => sql.MigrationsAssembly(migrationsAssembly)); + }) + .AddOperationalStore(options => + { + options.ConfigureDbContext = builder => + builder.UseSqlServer(connectionStringFromNop, + sql => sql.MigrationsAssembly(migrationsAssembly)); + }) + .AddAuthorizeInteractionResponseGenerator() + .AddEndpoint("Authorize", "/oauth/authorize/callback") + .AddEndpoint("Authorize", "/oauth/authorize") + .AddEndpoint("Token", "/oauth/token"); + } + + private void ApplyIdentityServerMigrations(IApplicationBuilder app) + { + using (var serviceScope = app.ApplicationServices.GetService().CreateScope()) + { + // the database.Migrate command will apply all pending migrations and will create the database if it is not created already. + var persistedGrantContext = serviceScope.ServiceProvider.GetRequiredService(); + persistedGrantContext.Database.Migrate(); + + var configurationContext = serviceScope.ServiceProvider.GetRequiredService(); + configurationContext.Database.Migrate(); + } + } + + private void SeedData(IApplicationBuilder app) + { + using (var serviceScope = app.ApplicationServices.GetService().CreateScope()) + { + var configurationContext = serviceScope.ServiceProvider.GetRequiredService(); + + if (!configurationContext.ApiResources.Any()) + { + // In the simple case an API has exactly one scope. But there are cases where you might want to sub-divide the functionality of an API, and give different clients access to different parts. + configurationContext.ApiResources.Add(new ApiResource() + { + Enabled = true, + Scopes = new List() + { + new ApiScope() + { + Name = "nop_api", + DisplayName = "nop_api" + } + }, + Name = "nop_api" + }); + + configurationContext.SaveChanges(); + + TryRunUpgradeScript(configurationContext); + } + } + } + + private string LoadUpgradeScript() + { + var fileProvider = EngineContext.Current.Resolve(); + string path = fileProvider.MapPath("~/Plugins/Nop.Plugin.Api/upgrade_script.sql"); + string script = File.ReadAllText(path); + + return script; + } + + private void TryRunUpgradeScript(ConfigurationDbContext configurationContext) + { + try + { + // If there are no api resources we can assume that this is the first start after the upgrade and run the upgrade script. + string upgradeScript = LoadUpgradeScript(); + configurationContext.Database.ExecuteSqlCommand(upgradeScript); + + // All client secrets must be hashed otherwise the identity server validation will fail. + var allClients = + Enumerable.ToList(configurationContext.Clients.Include(client => client.ClientSecrets)); + foreach (var client in allClients) + { + foreach (var clientSecret in client.ClientSecrets) + { + clientSecret.Value = HashExtensions.Sha256(clientSecret.Value); + } + + client.AccessTokenLifetime = Configurations.DefaultAccessTokenExpiration; + client.AbsoluteRefreshTokenLifetime = Configurations.DefaultRefreshTokenExpiration; + } + + configurationContext.SaveChanges(); + } + catch (Exception ex) + { + // Probably the upgrade script was already executed and we don't need to do anything. + } + } + + public void AddBindingRedirectsFallbacks() + { + // If no binding redirects are present in the config file then this will perform the binding redirect + RedirectAssembly("Microsoft.AspNetCore.DataProtection.Abstractions", new Version(2, 0, 0, 0), "adb9793829ddae60"); + } + + ///Adds an AssemblyResolve handler to redirect all attempts to load a specific assembly name to the specified version. + public static void RedirectAssembly(string shortName, Version targetVersion, string publicKeyToken) + { + ResolveEventHandler handler = null; + + handler = (sender, args) => + { + // Use latest strong name & version when trying to load SDK assemblies + var requestedAssembly = new AssemblyName(args.Name); + if (requestedAssembly.Name != shortName) + return null; + + requestedAssembly.Version = targetVersion; + requestedAssembly.SetPublicKeyToken(new AssemblyName("x, PublicKeyToken=" + publicKeyToken).GetPublicKeyToken()); + requestedAssembly.CultureInfo = CultureInfo.InvariantCulture; + + AppDomain.CurrentDomain.AssemblyResolve -= handler; + + return Assembly.Load(requestedAssembly); + }; + AppDomain.CurrentDomain.AssemblyResolve += handler; + } + + public int Order => new AuthenticationStartup().Order + 1; + } } \ No newline at end of file diff --git a/Nop.Plugin.Api/Attributes/ApiAuthorize.cs b/Nop.Plugin.Api/Attributes/ApiAuthorize.cs index 5ea2c11..86677b3 100644 --- a/Nop.Plugin.Api/Attributes/ApiAuthorize.cs +++ b/Nop.Plugin.Api/Attributes/ApiAuthorize.cs @@ -1,51 +1,39 @@ -namespace Nop.Plugin.Api.Attributes -{ - using Microsoft.AspNetCore.Authorization; - using Nop.Core.Plugins; - - // We need the ApiAuthorize attribute because when the api plugin assembly is loaded in memory by PluginManager - // all of its attributes are being initialized by the .NetFramework. - // The authorize attribute of the api plugin is marked with the Bearer authentication scheme, but the scheme is registered in the ApiStartup class, - // which is called on plugin install. - // If the plugin is not installed the authorize attribute will still be initialized when the assembly is loaded in memory, but the scheme won't be registered, - // which will cause an exception. - // That is why we need to make sure that the plugin is installed before setting the scheme. - public class ApiAuthorize : AuthorizeAttribute - { - public string Policy - { - get - { - return base.AuthenticationSchemes; - } - set - { - base.AuthenticationSchemes = GetAuthenticationSchemeName(value); - } - } - - public string AuthenticationSchemes - { - get - { - return base.AuthenticationSchemes; - } - set - { - base.AuthenticationSchemes = GetAuthenticationSchemeName(value); - } - } - - private string GetAuthenticationSchemeName(string value) - { - bool pluginInstalled = PluginManager.FindPlugin(typeof(ApiStartup))?.Installed ?? false; - - if (pluginInstalled) - { - return value; - } - - return default(string); - } - } +namespace Nop.Plugin.Api.Attributes +{ + using Microsoft.AspNetCore.Authorization; + using Core.Plugins; + + // We need the ApiAuthorize attribute because when the api plugin assembly is loaded in memory by PluginManager + // all of its attributes are being initialized by the .NetFramework. + // The authorize attribute of the api plugin is marked with the Bearer authentication scheme, but the scheme is registered in the ApiStartup class, + // which is called on plugin install. + // If the plugin is not installed the authorize attribute will still be initialized when the assembly is loaded in memory, but the scheme won't be registered, + // which will cause an exception. + // That is why we need to make sure that the plugin is installed before setting the scheme. + public class ApiAuthorize : AuthorizeAttribute + { + public new string Policy + { + get => base.AuthenticationSchemes; + set => base.AuthenticationSchemes = GetAuthenticationSchemeName(value); + } + + public new string AuthenticationSchemes + { + get => base.AuthenticationSchemes; + set => base.AuthenticationSchemes = GetAuthenticationSchemeName(value); + } + + private static string GetAuthenticationSchemeName(string value) + { + var pluginInstalled = PluginManager.FindPlugin(typeof(ApiStartup))?.Installed ?? false; + + if (pluginInstalled) + { + return value; + } + + return default(string); + } + } } \ No newline at end of file diff --git a/Nop.Plugin.Api/Attributes/GetRequestsErrorInterceptorActionFilter.cs b/Nop.Plugin.Api/Attributes/GetRequestsErrorInterceptorActionFilter.cs index 071d343..161a089 100644 --- a/Nop.Plugin.Api/Attributes/GetRequestsErrorInterceptorActionFilter.cs +++ b/Nop.Plugin.Api/Attributes/GetRequestsErrorInterceptorActionFilter.cs @@ -1,81 +1,77 @@ -using System.Collections.Generic; -using Microsoft.AspNetCore.Mvc.Filters; -using Newtonsoft.Json; -using Nop.Core.Infrastructure; -using Nop.Plugin.Api.DTOs.Errors; -using Nop.Plugin.Api.Models; - -namespace Nop.Plugin.Api.Attributes -{ - using System.IO; - using System.Net; - using Microsoft.AspNetCore.Http; - using Microsoft.AspNetCore.Mvc; - using Nop.Plugin.Api.JSON.ActionResults; - using Nop.Plugin.Api.JSON.Serializers; - - public class GetRequestsErrorInterceptorActionFilter : ActionFilterAttribute - { - private readonly IJsonFieldsSerializer _jsonFieldsSerializer; - - public GetRequestsErrorInterceptorActionFilter() - { - _jsonFieldsSerializer = EngineContext.Current.Resolve(); - } - - public override void OnActionExecuted(ActionExecutedContext actionExecutedContext) - { - if (actionExecutedContext.Exception != null && !actionExecutedContext.ExceptionHandled) - { - var error = new KeyValuePair>("internal_server_error", new List() { "please, contact the store owner" }); - - actionExecutedContext.Exception = null; - actionExecutedContext.ExceptionHandled = true; - SetError(actionExecutedContext, error); - } - else if (actionExecutedContext.HttpContext.Response != null && - (HttpStatusCode)actionExecutedContext.HttpContext.Response.StatusCode != HttpStatusCode.OK) - { - string responseBody = string.Empty; - - using (var streamReader = new StreamReader(actionExecutedContext.HttpContext.Response.Body)) - { - responseBody = streamReader.ReadToEnd(); - } - - // reset reader possition. - actionExecutedContext.HttpContext.Response.Body.Position = 0; - - DefaultWeApiErrorsModel defaultWebApiErrorsModel = JsonConvert.DeserializeObject(responseBody); - - // If both are null this means that it is not the default web api error format, - // which means that it the error is formatted by our standard and we don't need to do anything. - if (!string.IsNullOrEmpty(defaultWebApiErrorsModel.Message) && - !string.IsNullOrEmpty(defaultWebApiErrorsModel.MessageDetail)) - { - - var error = new KeyValuePair>("lookup_error", new List() { "not found" }); - - SetError(actionExecutedContext, error); - } - } - - base.OnActionExecuted(actionExecutedContext); - } - - private void SetError(ActionExecutedContext actionExecutedContext, KeyValuePair> error) - { - var bindingError = new Dictionary>(); - bindingError.Add(error.Key, error.Value); - - var errorsRootObject = new ErrorsRootObject() - { - Errors = bindingError - }; - - string errorJson = _jsonFieldsSerializer.Serialize(errorsRootObject, null); - - actionExecutedContext.Result = new ErrorActionResult(errorJson, HttpStatusCode.BadRequest); - } - } +using System.Collections.Generic; +using System.IO; +using System.Net; +using Microsoft.AspNetCore.Mvc.Filters; +using Newtonsoft.Json; +using Nop.Core.Infrastructure; +using Nop.Plugin.Api.DTOs.Errors; +using Nop.Plugin.Api.JSON.ActionResults; +using Nop.Plugin.Api.JSON.Serializers; +using Nop.Plugin.Api.Models; + +namespace Nop.Plugin.Api.Attributes +{ + public class GetRequestsErrorInterceptorActionFilter : ActionFilterAttribute + { + private readonly IJsonFieldsSerializer _jsonFieldsSerializer; + + public GetRequestsErrorInterceptorActionFilter() + { + _jsonFieldsSerializer = EngineContext.Current.Resolve(); + } + + public override void OnActionExecuted(ActionExecutedContext actionExecutedContext) + { + if (actionExecutedContext.Exception != null && !actionExecutedContext.ExceptionHandled) + { + var error = new KeyValuePair>("internal_server_error", + new List {"please, contact the store owner"}); + + actionExecutedContext.Exception = null; + actionExecutedContext.ExceptionHandled = true; + SetError(actionExecutedContext, error); + } + else if (actionExecutedContext.HttpContext.Response != null && + (HttpStatusCode) actionExecutedContext.HttpContext.Response.StatusCode != HttpStatusCode.OK) + { + string responseBody; + + using (var streamReader = new StreamReader(actionExecutedContext.HttpContext.Response.Body)) + { + responseBody = streamReader.ReadToEnd(); + } + + // reset reader possition. + actionExecutedContext.HttpContext.Response.Body.Position = 0; + + var defaultWebApiErrorsModel = JsonConvert.DeserializeObject(responseBody); + + // If both are null this means that it is not the default web api error format, + // which means that it the error is formatted by our standard and we don't need to do anything. + if (!string.IsNullOrEmpty(defaultWebApiErrorsModel.Message) && + !string.IsNullOrEmpty(defaultWebApiErrorsModel.MessageDetail)) + { + var error = new KeyValuePair>("lookup_error", new List {"not found"}); + + SetError(actionExecutedContext, error); + } + } + + base.OnActionExecuted(actionExecutedContext); + } + + private void SetError(ActionExecutedContext actionExecutedContext, KeyValuePair> error) + { + var bindingError = new Dictionary> {{error.Key, error.Value}}; + + var errorsRootObject = new ErrorsRootObject + { + Errors = bindingError + }; + + var errorJson = _jsonFieldsSerializer.Serialize(errorsRootObject, null); + + actionExecutedContext.Result = new ErrorActionResult(errorJson, HttpStatusCode.BadRequest); + } + } } \ No newline at end of file diff --git a/Nop.Plugin.Api/Attributes/ImageAttribute.cs b/Nop.Plugin.Api/Attributes/ImageAttribute.cs index b45a1ca..764c7a2 100644 --- a/Nop.Plugin.Api/Attributes/ImageAttribute.cs +++ b/Nop.Plugin.Api/Attributes/ImageAttribute.cs @@ -1,170 +1,170 @@ -using System.Collections.Generic; -using System.Net; -using Nop.Plugin.Api.DTOs.Images; -using System; -using System.Drawing; -using System.Drawing.Imaging; -using System.IO; -using System.Linq; -using System.Text.RegularExpressions; -using Nop.Core.Infrastructure; -using Nop.Services.Media; - -namespace Nop.Plugin.Api.Attributes -{ - public class ImageValidationAttribute : BaseValidationAttribute - { - private Dictionary _errors; - private readonly IPictureService _pictureService; - - public ImageValidationAttribute() - { - _errors = new Dictionary(); - _pictureService = EngineContext.Current.Resolve(); - } - - public override void Validate(object instance) - { - ImageDto imageDto = instance as ImageDto; - - bool imageSrcSet = imageDto != null && !string.IsNullOrEmpty(imageDto.Src); - bool imageAttachmentSet = imageDto != null && !string.IsNullOrEmpty(imageDto.Attachment); - - if (imageSrcSet || imageAttachmentSet) - { - byte[] imageBytes = null; - string mimeType = string.Empty; - - // Validation of the image object - - // We can't have both set. - CheckIfBothImageSourceTypesAreSet(imageSrcSet, imageAttachmentSet); - - // Here we ensure that the validation to this point has passed - // and try to download the image or convert base64 format to byte array - // depending on which format is passed. In both cases we should get a byte array and mime type. - if (_errors.Count == 0) - { - if (imageSrcSet) - { - DownloadFromSrc(imageDto.Src, ref imageBytes, ref mimeType); - } - else if (imageAttachmentSet) - { - ValidateAttachmentFormat(imageDto.Attachment); - - if (_errors.Count == 0) - { - ConvertAttachmentToByteArray(imageDto.Attachment, ref imageBytes, - ref mimeType); - } - } - } - - // We need to check because some of the validation above may have render the models state invalid. - if (_errors.Count == 0) - { - // Here we handle the check if the file passed is actual image and if the image is valid according to the - // restrictions set in the administration. - ValidatePictureBiteArray(imageBytes, mimeType); - } - - imageDto.Binary = imageBytes; - imageDto.MimeType = mimeType; - } - } - - public override Dictionary GetErrors() - { - return _errors; - } - - private void CheckIfBothImageSourceTypesAreSet(bool imageSrcSet, bool imageAttachmentSet) - { - if (imageSrcSet && - imageAttachmentSet) - { - var key = string.Format("{0} type", "image"); - _errors.Add(key, "Image src and Attachment are both set"); - } - } - - private void DownloadFromSrc(string imageSrc, ref byte[] imageBytes, ref string mimeType) - { - var key = string.Format("{0} type", "image"); - // TODO: discuss if we need our own web client so we can set a custom tmeout - this one's timeout is 100 sec. - var client = new WebClient(); - - try - { - imageBytes = client.DownloadData(imageSrc); - // This needs to be after the downloadData is called from client, otherwise there won't be any response headers. - mimeType = client.ResponseHeaders["content-type"]; - - if (imageBytes == null) - { - _errors.Add(key, "src is invalid"); - } - } - catch (Exception ex) - { - string message = string.Format("{0} - {1}", "src is invalid", ex.Message); - - _errors.Add(key, message); - } - } - - private void ValidateAttachmentFormat(string attachment) - { - Regex validBase64Pattern = - new Regex("^([A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{4}|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{2}==)$"); - bool isMatch = validBase64Pattern.IsMatch(attachment); - if (!isMatch) - { - var key = string.Format("{0} type", "image"); - _errors.Add(key, "attachment format is invalid"); - } - } - - private void ConvertAttachmentToByteArray(string attachment, ref byte[] imageBytes, ref string mimeType) - { - imageBytes = Convert.FromBase64String(attachment); - mimeType = GetMimeTypeFromByteArray(imageBytes); - } - - private static string GetMimeTypeFromByteArray(byte[] imageBytes) - { - MemoryStream stream = new MemoryStream(imageBytes, 0, imageBytes.Length); - Image image = Image.FromStream(stream, true); - ImageFormat format = image.RawFormat; - ImageCodecInfo codec = ImageCodecInfo.GetImageDecoders().First(c => c.FormatID == format.Guid); - return codec.MimeType; - } - - private void ValidatePictureBiteArray(byte[] imageBytes, string mimeType) - { - if (imageBytes != null) - { - try - { - imageBytes = _pictureService.ValidatePicture(imageBytes, mimeType); - } - catch (Exception ex) - { - var key = string.Format("{0} invalid", "image"); - string message = string.Format("{0} - {1}", "source is invalid", ex.Message); - - _errors.Add(key, message); - } - } - - if (imageBytes == null) - { - var key = string.Format("{0} invalid", "image"); - string message = "You have provided an invalid image source/attachment"; - - _errors.Add(key, message); - } - } - } +using System.Collections.Generic; +using System.Net; +using Nop.Plugin.Api.DTOs.Images; +using System; +using System.Drawing; +using System.Drawing.Imaging; +using System.IO; +using System.Linq; +using System.Text.RegularExpressions; +using Nop.Core.Infrastructure; +using Nop.Services.Media; + +namespace Nop.Plugin.Api.Attributes +{ + public class ImageValidationAttribute : BaseValidationAttribute + { + private Dictionary _errors; + private readonly IPictureService _pictureService; + + public ImageValidationAttribute() + { + _errors = new Dictionary(); + _pictureService = EngineContext.Current.Resolve(); + } + + public override void Validate(object instance) + { + var imageDto = instance as ImageDto; + + var imageSrcSet = imageDto != null && !string.IsNullOrEmpty(imageDto.Src); + var imageAttachmentSet = imageDto != null && !string.IsNullOrEmpty(imageDto.Attachment); + + if (imageSrcSet || imageAttachmentSet) + { + byte[] imageBytes = null; + var mimeType = string.Empty; + + // Validation of the image object + + // We can't have both set. + CheckIfBothImageSourceTypesAreSet(imageSrcSet, imageAttachmentSet); + + // Here we ensure that the validation to this point has passed + // and try to download the image or convert base64 format to byte array + // depending on which format is passed. In both cases we should get a byte array and mime type. + if (_errors.Count == 0) + { + if (imageSrcSet) + { + DownloadFromSrc(imageDto.Src, ref imageBytes, ref mimeType); + } + else if (imageAttachmentSet) + { + ValidateAttachmentFormat(imageDto.Attachment); + + if (_errors.Count == 0) + { + ConvertAttachmentToByteArray(imageDto.Attachment, ref imageBytes, + ref mimeType); + } + } + } + + // We need to check because some of the validation above may have render the models state invalid. + if (_errors.Count == 0) + { + // Here we handle the check if the file passed is actual image and if the image is valid according to the + // restrictions set in the administration. + ValidatePictureBiteArray(imageBytes, mimeType); + } + + imageDto.Binary = imageBytes; + imageDto.MimeType = mimeType; + } + } + + public override Dictionary GetErrors() + { + return _errors; + } + + private void CheckIfBothImageSourceTypesAreSet(bool imageSrcSet, bool imageAttachmentSet) + { + if (imageSrcSet && + imageAttachmentSet) + { + var key = string.Format("{0} type", "image"); + _errors.Add(key, "Image src and Attachment are both set"); + } + } + + private void DownloadFromSrc(string imageSrc, ref byte[] imageBytes, ref string mimeType) + { + var key = string.Format("{0} type", "image"); + // TODO: discuss if we need our own web client so we can set a custom tmeout - this one's timeout is 100 sec. + var client = new WebClient(); + + try + { + imageBytes = client.DownloadData(imageSrc); + // This needs to be after the downloadData is called from client, otherwise there won't be any response headers. + mimeType = client.ResponseHeaders["content-type"]; + + if (imageBytes == null) + { + _errors.Add(key, "src is invalid"); + } + } + catch (Exception ex) + { + var message = string.Format("{0} - {1}", "src is invalid", ex.Message); + + _errors.Add(key, message); + } + } + + private void ValidateAttachmentFormat(string attachment) + { + var validBase64Pattern = + new Regex("^([A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{4}|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{2}==)$"); + var isMatch = validBase64Pattern.IsMatch(attachment); + if (!isMatch) + { + var key = string.Format("{0} type", "image"); + _errors.Add(key, "attachment format is invalid"); + } + } + + private void ConvertAttachmentToByteArray(string attachment, ref byte[] imageBytes, ref string mimeType) + { + imageBytes = Convert.FromBase64String(attachment); + mimeType = GetMimeTypeFromByteArray(imageBytes); + } + + private static string GetMimeTypeFromByteArray(byte[] imageBytes) + { + var stream = new MemoryStream(imageBytes, 0, imageBytes.Length); + var image = Image.FromStream(stream, true); + var format = image.RawFormat; + var codec = ImageCodecInfo.GetImageDecoders().First(c => c.FormatID == format.Guid); + return codec.MimeType; + } + + private void ValidatePictureBiteArray(byte[] imageBytes, string mimeType) + { + if (imageBytes != null) + { + try + { + imageBytes = _pictureService.ValidatePicture(imageBytes, mimeType); + } + catch (Exception ex) + { + var key = string.Format("{0} invalid", "image"); + var message = string.Format("{0} - {1}", "source is invalid", ex.Message); + + _errors.Add(key, message); + } + } + + if (imageBytes == null) + { + var key = string.Format("{0} invalid", "image"); + var message = "You have provided an invalid image source/attachment"; + + _errors.Add(key, message); + } + } + } } \ No newline at end of file diff --git a/Nop.Plugin.Api/Attributes/ImageCollectionAttribute.cs b/Nop.Plugin.Api/Attributes/ImageCollectionAttribute.cs index 55aa8b3..eefe1d7 100644 --- a/Nop.Plugin.Api/Attributes/ImageCollectionAttribute.cs +++ b/Nop.Plugin.Api/Attributes/ImageCollectionAttribute.cs @@ -1,40 +1,40 @@ -using System.Collections.Generic; -using Nop.Plugin.Api.DTOs.Images; - -namespace Nop.Plugin.Api.Attributes -{ - public class ImageCollectionValidationAttribute : BaseValidationAttribute - { - private Dictionary _errors = new Dictionary(); - - public override void Validate(object instance) - { - // Images are not required so they could be null - // and there is nothing to validate in this case - if (instance == null) - return; - - var imagesCollection = instance as ICollection; - - foreach (var image in imagesCollection) - { - var imageValidationAttribute = new ImageValidationAttribute(); - - imageValidationAttribute.Validate(image); - - Dictionary errorsForImage = imageValidationAttribute.GetErrors(); - - if (errorsForImage.Count > 0) - { - _errors = errorsForImage; - break; - } - } - } - - public override Dictionary GetErrors() - { - return _errors; - } - } +using System.Collections.Generic; +using Nop.Plugin.Api.DTOs.Images; + +namespace Nop.Plugin.Api.Attributes +{ + public class ImageCollectionValidationAttribute : BaseValidationAttribute + { + private Dictionary _errors = new Dictionary(); + + public override void Validate(object instance) + { + // Images are not required so they could be null + // and there is nothing to validate in this case + if (instance == null) + return; + + var imagesCollection = instance as ICollection; + + foreach (var image in imagesCollection) + { + var imageValidationAttribute = new ImageValidationAttribute(); + + imageValidationAttribute.Validate(image); + + var errorsForImage = imageValidationAttribute.GetErrors(); + + if (errorsForImage.Count > 0) + { + _errors = errorsForImage; + break; + } + } + } + + public override Dictionary GetErrors() + { + return _errors; + } + } } \ No newline at end of file diff --git a/Nop.Plugin.Api/Attributes/ProductTypeValidationAttribute.cs b/Nop.Plugin.Api/Attributes/ProductTypeValidationAttribute.cs index c92f062..4cc2b67 100644 --- a/Nop.Plugin.Api/Attributes/ProductTypeValidationAttribute.cs +++ b/Nop.Plugin.Api/Attributes/ProductTypeValidationAttribute.cs @@ -1,33 +1,31 @@ -using System; -using System.Collections.Generic; -using Nop.Core.Domain.Catalog; - -namespace Nop.Plugin.Api.Attributes -{ - public class ProductTypeValidationAttribute : BaseValidationAttribute - { - private Dictionary _errors = new Dictionary(); - - public override void Validate(object instance) - { - // Product Type is not required so it could be null - // and there is nothing to validate in this case - if (instance == null) - return; - - ProductType productType; - - var isDefined = Enum.IsDefined(typeof(ProductType), instance); - - if (!isDefined) - { - _errors.Add("ProductType","Invalid product type"); - } - } - - public override Dictionary GetErrors() - { - return _errors; - } - } +using System; +using System.Collections.Generic; +using Nop.Core.Domain.Catalog; + +namespace Nop.Plugin.Api.Attributes +{ + public class ProductTypeValidationAttribute : BaseValidationAttribute + { + private readonly Dictionary _errors = new Dictionary(); + + public override void Validate(object instance) + { + // Product Type is not required so it could be null + // and there is nothing to validate in this case + if (instance == null) + return; + + var isDefined = Enum.IsDefined(typeof(ProductType), instance); + + if (!isDefined) + { + _errors.Add("ProductType","Invalid product type"); + } + } + + public override Dictionary GetErrors() + { + return _errors; + } + } } \ No newline at end of file diff --git a/Nop.Plugin.Api/Attributes/ValidateVendor.cs b/Nop.Plugin.Api/Attributes/ValidateVendor.cs index 3a4d440..3cebcad 100644 --- a/Nop.Plugin.Api/Attributes/ValidateVendor.cs +++ b/Nop.Plugin.Api/Attributes/ValidateVendor.cs @@ -1,55 +1,54 @@ -using System.Collections.Generic; -using Nop.Core.Domain.Vendors; -using Nop.Core.Infrastructure; -using Nop.Services.Vendors; - -namespace Nop.Plugin.Api.Attributes -{ - public class ValidateVendor : BaseValidationAttribute - { - private Dictionary _errors; - - private IVendorService _vendorService; - - private IVendorService VendorService - { - get - { - if (_vendorService == null) - { - _vendorService = EngineContext.Current.Resolve(); - } - - return _vendorService; - } - } - - public ValidateVendor() - { - _errors = new Dictionary(); - } - - public override void Validate(object instance) - { - int vendorId = 0; - - if (instance != null && int.TryParse(instance.ToString(), out vendorId)) - { - if (vendorId > 0) - { - Vendor vendor = VendorService.GetVendorById(vendorId); - - if (vendor == null) - { - _errors.Add("Invalid vendor id", "Non existing vendor"); - } - } - } - } - - public override Dictionary GetErrors() - { - return _errors; - } - } +using System.Collections.Generic; +using Nop.Core.Infrastructure; +using Nop.Services.Vendors; + +namespace Nop.Plugin.Api.Attributes +{ + public class ValidateVendor : BaseValidationAttribute + { + private Dictionary _errors; + + private IVendorService _vendorService; + + private IVendorService VendorService + { + get + { + if (_vendorService == null) + { + _vendorService = EngineContext.Current.Resolve(); + } + + return _vendorService; + } + } + + public ValidateVendor() + { + _errors = new Dictionary(); + } + + public override void Validate(object instance) + { + var vendorId = 0; + + if (instance != null && int.TryParse(instance.ToString(), out vendorId)) + { + if (vendorId > 0) + { + var vendor = VendorService.GetVendorById(vendorId); + + if (vendor == null) + { + _errors.Add("Invalid vendor id", "Non existing vendor"); + } + } + } + } + + public override Dictionary GetErrors() + { + return _errors; + } + } } \ No newline at end of file diff --git a/Nop.Plugin.Api/Authorization/Requirements/ActiveApiPluginRequirement.cs b/Nop.Plugin.Api/Authorization/Requirements/ActiveApiPluginRequirement.cs index fec6f9f..7ac1fce 100644 --- a/Nop.Plugin.Api/Authorization/Requirements/ActiveApiPluginRequirement.cs +++ b/Nop.Plugin.Api/Authorization/Requirements/ActiveApiPluginRequirement.cs @@ -1,21 +1,21 @@ -namespace Nop.Plugin.Api.Authorization.Requirements -{ - using Microsoft.AspNetCore.Authorization; - using Nop.Core.Infrastructure; - using Nop.Plugin.Api.Domain; - - public class ActiveApiPluginRequirement : IAuthorizationRequirement - { - public bool IsActive() - { - ApiSettings settings = EngineContext.Current.Resolve(); - - if (settings.EnableApi) - { - return true; - } - - return false; - } - } +namespace Nop.Plugin.Api.Authorization.Requirements +{ + using Microsoft.AspNetCore.Authorization; + using Nop.Core.Infrastructure; + using Nop.Plugin.Api.Domain; + + public class ActiveApiPluginRequirement : IAuthorizationRequirement + { + public bool IsActive() + { + var settings = EngineContext.Current.Resolve(); + + if (settings.EnableApi) + { + return true; + } + + return false; + } + } } \ No newline at end of file diff --git a/Nop.Plugin.Api/Authorization/Requirements/ActiveClientRequirement.cs b/Nop.Plugin.Api/Authorization/Requirements/ActiveClientRequirement.cs index 895fa77..bb2117f 100644 --- a/Nop.Plugin.Api/Authorization/Requirements/ActiveClientRequirement.cs +++ b/Nop.Plugin.Api/Authorization/Requirements/ActiveClientRequirement.cs @@ -1,43 +1,42 @@ -namespace Nop.Plugin.Api.Authorization.Requirements -{ - using Microsoft.AspNetCore.Authorization; - using Microsoft.AspNetCore.Http; - using Nop.Core.Infrastructure; - using Nop.Plugin.Api.Models; - using Nop.Plugin.Api.Services; - - public class ActiveClientRequirement : IAuthorizationRequirement - { - public bool IsClientActive() - { - if (!ClientExistsAndActive()) - { - // don't authorize if any of the above is not true - return false; - } - - return true; - } - - private bool ClientExistsAndActive() - { - var httpContextAccessor = EngineContext.Current.Resolve(); - - var clientId = - httpContextAccessor.HttpContext.User.FindFirst("client_id")?.Value; - - if (clientId != null) - { - var clientService = EngineContext.Current.Resolve(); - var client = clientService.FindClientByClientId(clientId); - - if (client != null && client.Enabled) - { - return true; - } - } - - return false; - } - } +namespace Nop.Plugin.Api.Authorization.Requirements +{ + using Microsoft.AspNetCore.Authorization; + using Microsoft.AspNetCore.Http; + using Nop.Core.Infrastructure; + using Nop.Plugin.Api.Services; + + public class ActiveClientRequirement : IAuthorizationRequirement + { + public bool IsClientActive() + { + if (!ClientExistsAndActive()) + { + // don't authorize if any of the above is not true + return false; + } + + return true; + } + + private bool ClientExistsAndActive() + { + var httpContextAccessor = EngineContext.Current.Resolve(); + + var clientId = + httpContextAccessor.HttpContext.User.FindFirst("client_id")?.Value; + + if (clientId != null) + { + var clientService = EngineContext.Current.Resolve(); + var client = clientService.FindClientByClientId(clientId); + + if (client != null && client.Enabled) + { + return true; + } + } + + return false; + } + } } \ No newline at end of file diff --git a/Nop.Plugin.Api/Authorization/Requirements/RequestFromSwaggerOptional.cs b/Nop.Plugin.Api/Authorization/Requirements/RequestFromSwaggerOptional.cs index 5a1e4fd..f1b7357 100644 --- a/Nop.Plugin.Api/Authorization/Requirements/RequestFromSwaggerOptional.cs +++ b/Nop.Plugin.Api/Authorization/Requirements/RequestFromSwaggerOptional.cs @@ -1,30 +1,30 @@ -namespace Nop.Plugin.Api.Authorization.Requirements -{ - using Microsoft.AspNetCore.Authorization; - using Nop.Core.Infrastructure; - using Nop.Plugin.Api.Domain; - - public class RequestFromSwaggerOptional : IAuthorizationRequirement - { - public bool IsRequestFromSwagger(string requestReferrer) - { - // Swagger client does not support BearerToken authentication. - // That is why we don't check for Bearer token authentication but check only 2 things: - // 1. The store owner explicitly has allowed Swagger to make requests to the API - // 2. Check if the request really comes from Swagger documentation page. Since Swagger documentation page is located on /swagger/index we simply check that the Refferer contains "swagger" - if (requestReferrer != null && requestReferrer.Contains("swagger")) - { - return true; - } - - return true; - } - - public bool AllowRequestsFromSwagger() - { - ApiSettings settings = EngineContext.Current.Resolve(); - - return settings.AllowRequestsFromSwagger; - } - } +namespace Nop.Plugin.Api.Authorization.Requirements +{ + using Microsoft.AspNetCore.Authorization; + using Nop.Core.Infrastructure; + using Nop.Plugin.Api.Domain; + + public class RequestFromSwaggerOptional : IAuthorizationRequirement + { + public bool IsRequestFromSwagger(string requestReferrer) + { + // Swagger client does not support BearerToken authentication. + // That is why we don't check for Bearer token authentication but check only 2 things: + // 1. The store owner explicitly has allowed Swagger to make requests to the API + // 2. Check if the request really comes from Swagger documentation page. Since Swagger documentation page is located on /swagger/index we simply check that the Refferer contains "swagger" + if (requestReferrer != null && requestReferrer.Contains("swagger")) + { + return true; + } + + return true; + } + + public bool AllowRequestsFromSwagger() + { + var settings = EngineContext.Current.Resolve(); + + return settings.AllowRequestsFromSwagger; + } + } } \ No newline at end of file diff --git a/Nop.Plugin.Api/AutoMapper/AutoMapperApiConfiguration.cs b/Nop.Plugin.Api/AutoMapper/AutoMapperApiConfiguration.cs index e5bfd7c..93291b5 100644 --- a/Nop.Plugin.Api/AutoMapper/AutoMapperApiConfiguration.cs +++ b/Nop.Plugin.Api/AutoMapper/AutoMapperApiConfiguration.cs @@ -1,58 +1,46 @@ -using AutoMapper; -using AutoMapper.Configuration; - -namespace Nop.Plugin.Api.AutoMapper -{ - public static class AutoMapperApiConfiguration - { - private static MapperConfigurationExpression _mapperConfigurationExpression; - private static IMapper _mapper; - - private static readonly object mapperConfigurationExpressionLockObject = new object(); - private static readonly object mapperLockObject = new object(); - - public static MapperConfigurationExpression MapperConfigurationExpression - { - get - { - if (_mapperConfigurationExpression == null) - { - _mapperConfigurationExpression = new MapperConfigurationExpression(); ; - } - - return _mapperConfigurationExpression; - } - } - - public static IMapper Mapper - { - get - { - if (_mapper == null) - { - lock (mapperLockObject) - { - if (_mapper == null) - { - var mapperConfiguration = new MapperConfiguration(MapperConfigurationExpression); - - _mapper = mapperConfiguration.CreateMapper(); - } - } - } - - return _mapper; - } - } - - public static TDestination MapTo(this TSource source) - { - return Mapper.Map(source); - } - - public static TDestination MapTo(this TSource source, TDestination destination) - { - return Mapper.Map(source, destination); - } - } +using AutoMapper; +using AutoMapper.Configuration; + +namespace Nop.Plugin.Api.AutoMapper +{ + public static class AutoMapperApiConfiguration + { + private static MapperConfigurationExpression s_mapperConfigurationExpression; + private static IMapper s_mapper; + private static readonly object s_mapperLockObject = new object(); + + public static MapperConfigurationExpression MapperConfigurationExpression => s_mapperConfigurationExpression ?? + (s_mapperConfigurationExpression = new MapperConfigurationExpression()); + + public static IMapper Mapper + { + get + { + if (s_mapper == null) + { + lock (s_mapperLockObject) + { + if (s_mapper == null) + { + var mapperConfiguration = new MapperConfiguration(MapperConfigurationExpression); + + s_mapper = mapperConfiguration.CreateMapper(); + } + } + } + + return s_mapper; + } + } + + public static TDestination MapTo(this TSource source) + { + return Mapper.Map(source); + } + + public static TDestination MapTo(this TSource source, TDestination destination) + { + return Mapper.Map(source, destination); + } + } } \ No newline at end of file diff --git a/Nop.Plugin.Api/Controllers/Admin/ApiAdminController.cs b/Nop.Plugin.Api/Controllers/Admin/ApiAdminController.cs index f08d7a2..d5e3ab9 100644 --- a/Nop.Plugin.Api/Controllers/Admin/ApiAdminController.cs +++ b/Nop.Plugin.Api/Controllers/Admin/ApiAdminController.cs @@ -1,95 +1,95 @@ -namespace Nop.Plugin.Api.Controllers.Admin -{ - using Microsoft.AspNetCore.Mvc; - using Nop.Core; - using Nop.Plugin.Api.Constants; - using Nop.Plugin.Api.Domain; - using Nop.Plugin.Api.MappingExtensions; - using Nop.Plugin.Api.Models; - using Nop.Services.Configuration; - using Nop.Services.Localization; - using Nop.Services.Logging; - using Nop.Services.Stores; - using Nop.Web.Framework; - using Nop.Web.Framework.Controllers; - using Nop.Web.Framework.Mvc.Filters; - - [AuthorizeAdmin] - [Area(AreaNames.Admin)] - public class ApiAdminController : BasePluginController - { - private readonly IStoreService _storeService; - private readonly IWorkContext _workContext; - private readonly ISettingService _settingService; - private readonly ICustomerActivityService _customerActivityService; - private readonly ILocalizationService _localizationService; - - public ApiAdminController( - IStoreService storeService, - IWorkContext workContext, - ISettingService settingService, - ICustomerActivityService customerActivityService, - ILocalizationService localizationService) - { - _storeService = storeService; - _workContext = workContext; - _settingService = settingService; - _customerActivityService = customerActivityService; - _localizationService = localizationService; - } - - [HttpGet] - public ActionResult Settings() - { - var storeScope = this.GetActiveStoreScopeConfiguration(_storeService, _workContext); - - ApiSettings apiSettings = _settingService.LoadSetting(storeScope); - - ConfigurationModel model = apiSettings.ToModel(); - - // Store Settings - model.ActiveStoreScopeConfiguration = storeScope; - - if (model.EnableApi_OverrideForStore || storeScope == 0) - _settingService.SaveSetting(apiSettings, x => x.EnableApi, storeScope, false); - if (model.AllowRequestsFromSwagger_OverrideForStore || storeScope == 0) - _settingService.SaveSetting(apiSettings, x => x.AllowRequestsFromSwagger, storeScope, false); - if (model.EnableLogging_OverrideForStore || storeScope == 0) - _settingService.SaveSetting(apiSettings, x => x.EnableLogging, storeScope, false); - - //now clear settings cache - _settingService.ClearCache(); - - return View(ViewNames.AdminApiSettings, model); - } - - [HttpPost] - public ActionResult Settings(ConfigurationModel configurationModel) - { - //load settings for a chosen store scope - var storeScope = this.GetActiveStoreScopeConfiguration(_storeService, _workContext); - - ApiSettings settings = configurationModel.ToEntity(); - - /* We do not clear cache after each setting update. - * This behavior can increase performance because cached settings will not be cleared - * and loaded from database after each update */ - - if (configurationModel.EnableApi_OverrideForStore || storeScope == 0) - _settingService.SaveSetting(settings, x => x.EnableApi, storeScope, false); - if (configurationModel.AllowRequestsFromSwagger_OverrideForStore || storeScope == 0) - _settingService.SaveSetting(settings, x => x.AllowRequestsFromSwagger, storeScope, false); - if (configurationModel.EnableLogging_OverrideForStore || storeScope == 0) - _settingService.SaveSetting(settings, x => x.EnableLogging, storeScope, false); - - //now clear settings cache - _settingService.ClearCache(); - - _customerActivityService.InsertActivity("EditApiSettings", "Edit Api Settings"); - - SuccessNotification(_localizationService.GetResource("Admin.Plugins.Saved")); - - return View(ViewNames.AdminApiSettings, configurationModel); - } - } +namespace Nop.Plugin.Api.Controllers.Admin +{ + using Microsoft.AspNetCore.Mvc; + using Core; + using Constants; + using Domain; + using MappingExtensions; + using Models; + using Nop.Services.Configuration; + using Nop.Services.Localization; + using Nop.Services.Logging; + using Nop.Services.Stores; + using Web.Framework; + using Nop.Web.Framework.Controllers; + using Web.Framework.Mvc.Filters; + + [AuthorizeAdmin] + [Area(AreaNames.Admin)] + public class ApiAdminController : BasePluginController + { + private readonly IStoreService _storeService; + private readonly IStoreContext _storeContext; + private readonly IWorkContext _workContext; + private readonly ISettingService _settingService; + private readonly ICustomerActivityService _customerActivityService; + private readonly ILocalizationService _localizationService; + + public ApiAdminController( + IStoreService storeService, + IStoreContext storeContext, + IWorkContext workContext, + ISettingService settingService, + ICustomerActivityService customerActivityService, + ILocalizationService localizationService) + { + _storeService = storeService; + _storeContext = storeContext; + _workContext = workContext; + _settingService = settingService; + _customerActivityService = customerActivityService; + _localizationService = localizationService; + } + + [HttpGet] + public ActionResult Settings() + { + + var storeScope = _storeContext.ActiveStoreScopeConfiguration; + + var apiSettings = _settingService.LoadSetting(storeScope); + + var model = apiSettings.ToModel(); + + // Store Settings + model.ActiveStoreScopeConfiguration = storeScope; + + if (model.EnableApi_OverrideForStore || storeScope == 0) + _settingService.SaveSetting(apiSettings, x => x.EnableApi, storeScope, false); + if (model.AllowRequestsFromSwagger_OverrideForStore || storeScope == 0) + _settingService.SaveSetting(apiSettings, x => x.AllowRequestsFromSwagger, storeScope, false); + + //now clear settings cache + _settingService.ClearCache(); + + return View(ViewNames.AdminApiSettings, model); + } + + [HttpPost] + public ActionResult Settings(ConfigurationModel configurationModel) + { + //load settings for a chosen store scope + var storeScope = _storeContext.ActiveStoreScopeConfiguration; + + var settings = configurationModel.ToEntity(); + + /* We do not clear cache after each setting update. + * This behavior can increase performance because cached settings will not be cleared + * and loaded from database after each update */ + + if (configurationModel.EnableApi_OverrideForStore || storeScope == 0) + _settingService.SaveSetting(settings, x => x.EnableApi, storeScope, false); + if (configurationModel.AllowRequestsFromSwagger_OverrideForStore || storeScope == 0) + _settingService.SaveSetting(settings, x => x.AllowRequestsFromSwagger, storeScope, false); + + //now clear settings cache + _settingService.ClearCache(); + + _customerActivityService.InsertActivity("EditApiSettings", "Edit Api Settings"); + + SuccessNotification(_localizationService.GetResource("Admin.Plugins.Saved")); + + return View(ViewNames.AdminApiSettings, configurationModel); + } + } } \ No newline at end of file diff --git a/Nop.Plugin.Api/Controllers/Admin/ManageClientsAdminController.cs b/Nop.Plugin.Api/Controllers/Admin/ManageClientsAdminController.cs index 5de5b73..ec63dfa 100644 --- a/Nop.Plugin.Api/Controllers/Admin/ManageClientsAdminController.cs +++ b/Nop.Plugin.Api/Controllers/Admin/ManageClientsAdminController.cs @@ -1,114 +1,116 @@ -namespace Nop.Plugin.Api.Controllers.Admin -{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Threading.Tasks; - using Microsoft.AspNetCore.Mvc; - using Nop.Plugin.Api.Constants; - using Nop.Services.Localization; - using Nop.Web.Framework; - using Nop.Web.Framework.Controllers; - using Nop.Web.Framework.Kendoui; - using Nop.Web.Framework.Mvc.Filters; - using Nop.Plugin.Api.Models; - using Nop.Plugin.Api.Services; - - [AuthorizeAdmin] - [Area(AreaNames.Admin)] - [Route("admin/manageClientsAdmin/")] - public class ManageClientsAdminController : BasePluginController - { - private readonly IClientService _clientService; - private readonly ILocalizationService _localizationService; - - public ManageClientsAdminController(ILocalizationService localizationService, IClientService clientService) - { - _localizationService = localizationService; - _clientService = clientService; - } - - [HttpGet] - [Route("list")] - public ActionResult List() - { - return View(ViewNames.AdminApiClientsList); - } - - [HttpPost] - [Route("list")] - public ActionResult List(DataSourceRequest command) - { - IList gridModel = _clientService.GetAllClients(); - - var grids = new DataSourceResult() - { - Data = gridModel, - Total = gridModel.Count() - }; - - return Json(grids); - } - - [HttpGet] - [Route("create")] - public ActionResult Create() - { - var clientModel = new ClientApiModel - { - Enabled = true - }; - - return View(ViewNames.AdminApiClientsCreate, clientModel); - } - - [HttpPost, ParameterBasedOnFormName("save-continue", "continueEditing")] - [Route("create")] - public ActionResult Create(ClientApiModel model, bool continueEditing) - { - if (ModelState.IsValid) - { - int clientId = _clientService.InsertClient(model); - - SuccessNotification(_localizationService.GetResource("Plugins.Api.Admin.Client.Created")); - return continueEditing ? RedirectToAction("Edit", new { id = clientId }) : RedirectToAction("List"); - } - - return RedirectToAction("List"); - } - - [HttpGet] - [Route("edit/{id}")] - public IActionResult Edit(int id) - { - ClientApiModel clientModel = _clientService.FindClientByIdAsync(id); - - return View(ViewNames.AdminApiClientsEdit, clientModel); - } - - [HttpPost, ParameterBasedOnFormName("save-continue", "continueEditing")] - [Route("edit/{id}")] - public async Task Edit(ClientApiModel model, bool continueEditing) - { - if (ModelState.IsValid) - { - _clientService.UpdateClient(model); - - SuccessNotification(_localizationService.GetResource("Plugins.Api.Admin.Client.Edit")); - return continueEditing ? RedirectToAction("Edit", new { id = model.Id }) : RedirectToAction("List"); - } - - return RedirectToAction("List"); - } - - [HttpPost, ActionName("Delete")] - [Route("delete/{id}")] - public IActionResult DeleteConfirmed(int id) - { - _clientService.DeleteClient(id); - - SuccessNotification(_localizationService.GetResource("Plugins.Api.Admin.Client.Deleted")); - return RedirectToAction("List"); - } - } +namespace Nop.Plugin.Api.Controllers.Admin +{ + using System.Linq; + using Microsoft.AspNetCore.Mvc; + using Constants; + using Nop.Services.Localization; + using Web.Framework; + using Nop.Web.Framework.Controllers; + using Web.Framework.Kendoui; + using Web.Framework.Mvc.Filters; + using Models; + using Services; + using System; + + [AuthorizeAdmin] + [Area(AreaNames.Admin)] + [Route("admin/manageClientsAdmin/")] + public class ManageClientsAdminController : BasePluginController + { + private readonly IClientService _clientService; + private readonly ILocalizationService _localizationService; + + public ManageClientsAdminController(ILocalizationService localizationService, IClientService clientService) + { + _localizationService = localizationService; + _clientService = clientService; + } + + [HttpGet] + [Route("list")] + public ActionResult List() + { + return View(ViewNames.AdminApiClientsList); + } + + [HttpPost] + [Route("list")] + public ActionResult List(DataSourceRequest command) + { + var gridModel = _clientService.GetAllClients(); + + var grids = new DataSourceResult() + { + Data = gridModel, + Total = gridModel.Count() + }; + + return Json(grids); + } + + [HttpGet] + [Route("create")] + public ActionResult Create() + { + var clientModel = new ClientApiModel + { + Enabled = true, + ClientSecret = Guid.NewGuid().ToString(), + ClientId = Guid.NewGuid().ToString(), + AccessTokenLifetime = Configurations.DefaultAccessTokenExpiration, + RefreshTokenLifetime = Configurations.DefaultRefreshTokenExpiration + }; + + return View(ViewNames.AdminApiClientsCreate, clientModel); + } + + [HttpPost, ParameterBasedOnFormName("save-continue", "continueEditing")] + [Route("create")] + public ActionResult Create(ClientApiModel model, bool continueEditing) + { + if (ModelState.IsValid) + { + var clientId = _clientService.InsertClient(model); + + SuccessNotification(_localizationService.GetResource("Plugins.Api.Admin.Client.Created")); + return continueEditing ? RedirectToAction("Edit", new { id = clientId }) : RedirectToAction("List"); + } + + return RedirectToAction("List"); + } + + [HttpGet] + [Route("edit/{id}")] + public IActionResult Edit(int id) + { + var clientModel = _clientService.FindClientByIdAsync(id); + + return View(ViewNames.AdminApiClientsEdit, clientModel); + } + + [HttpPost, ParameterBasedOnFormName("save-continue", "continueEditing")] + [Route("edit/{id}")] + public IActionResult Edit(ClientApiModel model, bool continueEditing) + { + if (ModelState.IsValid) + { + _clientService.UpdateClient(model); + + SuccessNotification(_localizationService.GetResource("Plugins.Api.Admin.Client.Edit")); + return continueEditing ? RedirectToAction("Edit", new { id = model.Id }) : RedirectToAction("List"); + } + + return RedirectToAction("List"); + } + + [HttpPost, ActionName("Delete")] + [Route("delete/{id}")] + public IActionResult DeleteConfirmed(int id) + { + _clientService.DeleteClient(id); + + SuccessNotification(_localizationService.GetResource("Plugins.Api.Admin.Client.Deleted")); + return RedirectToAction("List"); + } + } } \ No newline at end of file diff --git a/Nop.Plugin.Api/Controllers/BaseApiController.cs b/Nop.Plugin.Api/Controllers/BaseApiController.cs index e72991e..85ed195 100644 --- a/Nop.Plugin.Api/Controllers/BaseApiController.cs +++ b/Nop.Plugin.Api/Controllers/BaseApiController.cs @@ -1,155 +1,152 @@ -using System.Collections.Generic; -using System.Linq; -using System.Net; -using Nop.Core; -using Nop.Core.Domain.Security; -using Nop.Core.Domain.Stores; -using Nop.Plugin.Api.DTOs.Errors; -using Nop.Plugin.Api.JSON.ActionResults; -using Nop.Services.Customers; -using Nop.Services.Discounts; -using Nop.Services.Localization; -using Nop.Services.Logging; -using Nop.Services.Media; -using Nop.Services.Security; -using Nop.Services.Stores; -using Microsoft.AspNetCore.Mvc; - -namespace Nop.Plugin.Api.Controllers -{ - using Nop.Plugin.Api.JSON.Serializers; - - public class BaseApiController : Controller - { - protected readonly IJsonFieldsSerializer _jsonFieldsSerializer; - protected readonly IAclService _aclService; - protected readonly ICustomerService _customerService; - protected readonly IStoreMappingService _storeMappingService; - protected readonly IStoreService _storeService; - protected readonly IDiscountService _discountService; - protected readonly ICustomerActivityService _customerActivityService; - protected readonly ILocalizationService _localizationService; - protected readonly IPictureService _pictureService; - - public BaseApiController(IJsonFieldsSerializer jsonFieldsSerializer, - IAclService aclService, - ICustomerService customerService, - IStoreMappingService storeMappingService, - IStoreService storeService, - IDiscountService discountService, - ICustomerActivityService customerActivityService, - ILocalizationService localizationService, - IPictureService pictureService) - { - _jsonFieldsSerializer = jsonFieldsSerializer; - _aclService = aclService; - _customerService = customerService; - _storeMappingService = storeMappingService; - _storeService = storeService; - _discountService = discountService; - _customerActivityService = customerActivityService; - _localizationService = localizationService; - _pictureService = pictureService; - } - - protected IActionResult Error(HttpStatusCode statusCode = (HttpStatusCode)422, string propertyKey = "", string errorMessage = "") - { - var errors = new Dictionary>(); - - if (!string.IsNullOrEmpty(errorMessage) && !string.IsNullOrEmpty(propertyKey)) - { - var errorsList = new List() {errorMessage}; - errors.Add(propertyKey, errorsList); - } - - foreach (var item in ModelState) - { - var errorMessages = item.Value.Errors.Select(x => x.ErrorMessage); - - List validErrorMessages = new List(); - - if (errorMessages != null) - { - validErrorMessages.AddRange(errorMessages.Where(message => !string.IsNullOrEmpty(message))); - } - - if (validErrorMessages.Count > 0) - { - if (errors.ContainsKey(item.Key)) - { - errors[item.Key].AddRange(validErrorMessages); - } - else - { - errors.Add(item.Key, validErrorMessages.ToList()); - } - } - } - - var errorsRootObject = new ErrorsRootObject() - { - Errors = errors - }; - - var errorsJson = _jsonFieldsSerializer.Serialize(errorsRootObject, null); - - return new ErrorActionResult(errorsJson, statusCode); - } - - protected void UpdateAclRoles(TEntity entity, List passedRoleIds) where TEntity: BaseEntity, IAclSupported - { - if (passedRoleIds == null) - { - return; - } - - entity.SubjectToAcl = passedRoleIds.Any(); - - var existingAclRecords = _aclService.GetAclRecords(entity); - var allCustomerRoles = _customerService.GetAllCustomerRoles(true); - foreach (var customerRole in allCustomerRoles) - { - if (passedRoleIds.Contains(customerRole.Id)) - { - //new role - if (existingAclRecords.Count(acl => acl.CustomerRoleId == customerRole.Id) == 0) - _aclService.InsertAclRecord(entity, customerRole.Id); - } - else - { - //remove role - var aclRecordToDelete = existingAclRecords.FirstOrDefault(acl => acl.CustomerRoleId == customerRole.Id); - if (aclRecordToDelete != null) - _aclService.DeleteAclRecord(aclRecordToDelete); - } - } - } - - protected void UpdateStoreMappings(TEntity entity, List passedStoreIds) where TEntity : BaseEntity, IStoreMappingSupported - { - if(passedStoreIds == null) - return; - - entity.LimitedToStores = passedStoreIds.Any(); - - var existingStoreMappings = _storeMappingService.GetStoreMappings(entity); - var allStores = _storeService.GetAllStores(); - foreach (var store in allStores) - { - if (passedStoreIds.Contains(store.Id)) - { - //new store - if (existingStoreMappings.Count(sm => sm.StoreId == store.Id) == 0) - _storeMappingService.InsertStoreMapping(entity, store.Id); - } - else - { - //remove store - var storeMappingToDelete = existingStoreMappings.FirstOrDefault(sm => sm.StoreId == store.Id); - if (storeMappingToDelete != null) - _storeMappingService.DeleteStoreMapping(storeMappingToDelete); - } - } - } - } +using System.Collections.Generic; +using System.Linq; +using System.Net; +using Nop.Core; +using Nop.Core.Domain.Security; +using Nop.Core.Domain.Stores; +using Nop.Plugin.Api.DTOs.Errors; +using Nop.Plugin.Api.JSON.ActionResults; +using Nop.Services.Customers; +using Nop.Services.Discounts; +using Nop.Services.Localization; +using Nop.Services.Logging; +using Nop.Services.Media; +using Nop.Services.Security; +using Nop.Services.Stores; +using Microsoft.AspNetCore.Mvc; + +namespace Nop.Plugin.Api.Controllers +{ + using JSON.Serializers; + + public class BaseApiController : Controller + { + protected readonly IJsonFieldsSerializer JsonFieldsSerializer; + protected readonly IAclService AclService; + protected readonly ICustomerService CustomerService; + protected readonly IStoreMappingService StoreMappingService; + protected readonly IStoreService StoreService; + protected readonly IDiscountService DiscountService; + protected readonly ICustomerActivityService CustomerActivityService; + protected readonly ILocalizationService LocalizationService; + protected readonly IPictureService PictureService; + + public BaseApiController(IJsonFieldsSerializer jsonFieldsSerializer, + IAclService aclService, + ICustomerService customerService, + IStoreMappingService storeMappingService, + IStoreService storeService, + IDiscountService discountService, + ICustomerActivityService customerActivityService, + ILocalizationService localizationService, + IPictureService pictureService) + { + JsonFieldsSerializer = jsonFieldsSerializer; + AclService = aclService; + CustomerService = customerService; + StoreMappingService = storeMappingService; + StoreService = storeService; + DiscountService = discountService; + CustomerActivityService = customerActivityService; + LocalizationService = localizationService; + PictureService = pictureService; + } + + protected IActionResult Error(HttpStatusCode statusCode = (HttpStatusCode)422, string propertyKey = "", string errorMessage = "") + { + var errors = new Dictionary>(); + + if (!string.IsNullOrEmpty(errorMessage) && !string.IsNullOrEmpty(propertyKey)) + { + var errorsList = new List() {errorMessage}; + errors.Add(propertyKey, errorsList); + } + + foreach (var item in ModelState) + { + var errorMessages = item.Value.Errors.Select(x => x.ErrorMessage); + + var validErrorMessages = new List(); + + validErrorMessages.AddRange(errorMessages.Where(message => !string.IsNullOrEmpty(message))); + + if (validErrorMessages.Count > 0) + { + if (errors.ContainsKey(item.Key)) + { + errors[item.Key].AddRange(validErrorMessages); + } + else + { + errors.Add(item.Key, validErrorMessages.ToList()); + } + } + } + + var errorsRootObject = new ErrorsRootObject() + { + Errors = errors + }; + + var errorsJson = JsonFieldsSerializer.Serialize(errorsRootObject, null); + + return new ErrorActionResult(errorsJson, statusCode); + } + + protected void UpdateAclRoles(TEntity entity, List passedRoleIds) where TEntity: BaseEntity, IAclSupported + { + if (passedRoleIds == null) + { + return; + } + + entity.SubjectToAcl = passedRoleIds.Any(); + + var existingAclRecords = AclService.GetAclRecords(entity); + var allCustomerRoles = CustomerService.GetAllCustomerRoles(true); + foreach (var customerRole in allCustomerRoles) + { + if (passedRoleIds.Contains(customerRole.Id)) + { + //new role + if (existingAclRecords.Count(acl => acl.CustomerRoleId == customerRole.Id) == 0) + AclService.InsertAclRecord(entity, customerRole.Id); + } + else + { + //remove role + var aclRecordToDelete = existingAclRecords.FirstOrDefault(acl => acl.CustomerRoleId == customerRole.Id); + if (aclRecordToDelete != null) + AclService.DeleteAclRecord(aclRecordToDelete); + } + } + } + + protected void UpdateStoreMappings(TEntity entity, List passedStoreIds) where TEntity : BaseEntity, IStoreMappingSupported + { + if(passedStoreIds == null) + return; + + entity.LimitedToStores = passedStoreIds.Any(); + + var existingStoreMappings = StoreMappingService.GetStoreMappings(entity); + var allStores = StoreService.GetAllStores(); + foreach (var store in allStores) + { + if (passedStoreIds.Contains(store.Id)) + { + //new store + if (existingStoreMappings.Count(sm => sm.StoreId == store.Id) == 0) + StoreMappingService.InsertStoreMapping(entity, store.Id); + } + else + { + //remove store + var storeMappingToDelete = existingStoreMappings.FirstOrDefault(sm => sm.StoreId == store.Id); + if (storeMappingToDelete != null) + StoreMappingService.DeleteStoreMapping(storeMappingToDelete); + } + } + } + } } \ No newline at end of file diff --git a/Nop.Plugin.Api/Controllers/CategoriesController.cs b/Nop.Plugin.Api/Controllers/CategoriesController.cs index df53179..32e0870 100644 --- a/Nop.Plugin.Api/Controllers/CategoriesController.cs +++ b/Nop.Plugin.Api/Controllers/CategoriesController.cs @@ -1,392 +1,390 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net; -using Nop.Core.Domain.Catalog; -using Nop.Core.Domain.Discounts; -using Nop.Core.Domain.Media; -using Nop.Plugin.Api.Attributes; -using Nop.Plugin.Api.Constants; -using Nop.Plugin.Api.DTOs.Categories; -using Nop.Plugin.Api.Models.CategoriesParameters; -using Nop.Plugin.Api.Services; -using Nop.Services.Catalog; -using Nop.Services.Localization; -using Nop.Services.Logging; -using Nop.Services.Seo; -using Nop.Plugin.Api.Delta; -using Nop.Plugin.Api.DTOs.Images; -using Nop.Plugin.Api.Factories; -using Nop.Plugin.Api.JSON.ActionResults; -using Nop.Plugin.Api.ModelBinders; -using Nop.Services.Customers; -using Nop.Services.Discounts; -using Nop.Services.Media; -using Nop.Services.Security; -using Nop.Services.Stores; -using Nop.Plugin.Api.Helpers; -using Microsoft.AspNetCore.Mvc; - -namespace Nop.Plugin.Api.Controllers -{ - using Microsoft.AspNetCore.Authentication.JwtBearer; - using Nop.Plugin.Api.DTOs.Errors; - using Nop.Plugin.Api.JSON.Serializers; - - [ApiAuthorize(Policy = JwtBearerDefaults.AuthenticationScheme, AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] - public class CategoriesController : BaseApiController - { - private readonly ICategoryApiService _categoryApiService; - private readonly ICategoryService _categoryService; - private readonly IUrlRecordService _urlRecordService; - private readonly IFactory _factory; - private readonly IDTOHelper _dtoHelper; - - public CategoriesController(ICategoryApiService categoryApiService, - IJsonFieldsSerializer jsonFieldsSerializer, - ICategoryService categoryService, - IUrlRecordService urlRecordService, - ICustomerActivityService customerActivityService, - ILocalizationService localizationService, - IPictureService pictureService, - IStoreMappingService storeMappingService, - IStoreService storeService, - IDiscountService discountService, - IAclService aclService, - ICustomerService customerService, - IFactory factory, - IDTOHelper dtoHelper) : base(jsonFieldsSerializer, aclService, customerService, storeMappingService, storeService, discountService, customerActivityService, localizationService,pictureService) - { - _categoryApiService = categoryApiService; - _categoryService = categoryService; - _urlRecordService = urlRecordService; - _factory = factory; - _dtoHelper = dtoHelper; - } - - /// - /// Receive a list of all Categories - /// - /// OK - /// Bad Request - /// Unauthorized - [HttpGet] - [Route("/api/categories")] - [ProducesResponseType(typeof(CategoriesRootObject), (int)HttpStatusCode.OK)] - [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] - [GetRequestsErrorInterceptorActionFilter] - public IActionResult GetCategories(CategoriesParametersModel parameters) - { - if (parameters.Limit < Configurations.MinLimit || parameters.Limit > Configurations.MaxLimit) - { - return Error(HttpStatusCode.BadRequest, "limit", "Invalid limit parameter"); - } - - if (parameters.Page < Configurations.DefaultPageValue) - { - return Error(HttpStatusCode.BadRequest, "page", "Invalid page parameter"); - } - - var allCategories = _categoryApiService.GetCategories(parameters.Ids, parameters.CreatedAtMin, parameters.CreatedAtMax, - parameters.UpdatedAtMin, parameters.UpdatedAtMax, - parameters.Limit, parameters.Page, parameters.SinceId, - parameters.ProductId, parameters.PublishedStatus) - .Where(c => _storeMappingService.Authorize(c)); - - IList categoriesAsDtos = allCategories.Select(category => - { - return _dtoHelper.PrepareCategoryDTO(category); - - }).ToList(); - - var categoriesRootObject = new CategoriesRootObject() - { - Categories = categoriesAsDtos - }; - - var json = _jsonFieldsSerializer.Serialize(categoriesRootObject, parameters.Fields); - - return new RawJsonActionResult(json); - } - - /// - /// Receive a count of all Categories - /// - /// OK - /// Unauthorized - [HttpGet] - [Route("/api/categories/count")] - [ProducesResponseType(typeof(CategoriesCountRootObject), (int)HttpStatusCode.OK)] - [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] - [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] - [GetRequestsErrorInterceptorActionFilter] - public IActionResult GetCategoriesCount(CategoriesCountParametersModel parameters) - { - var allCategoriesCount = _categoryApiService.GetCategoriesCount(parameters.CreatedAtMin, parameters.CreatedAtMax, - parameters.UpdatedAtMin, parameters.UpdatedAtMax, - parameters.PublishedStatus, parameters.ProductId); - - var categoriesCountRootObject = new CategoriesCountRootObject() - { - Count = allCategoriesCount - }; - - return Ok(categoriesCountRootObject); - } - - /// - /// Retrieve category by spcified id - /// - /// Id of the category - /// Fields from the category you want your json to contain - /// OK - /// Not Found - /// Unauthorized - [HttpGet] - [Route("/api/categories/{id}")] - [ProducesResponseType(typeof(CategoriesRootObject), (int)HttpStatusCode.OK)] - [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] - [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] - [GetRequestsErrorInterceptorActionFilter] - public IActionResult GetCategoryById(int id, string fields = "") - { - if (id <= 0) - { - return Error(HttpStatusCode.BadRequest, "id", "invalid id"); - } - - Category category = _categoryApiService.GetCategoryById(id); - - if (category == null) - { - return Error(HttpStatusCode.NotFound, "category", "category not found"); - } - - CategoryDto categoryDto = _dtoHelper.PrepareCategoryDTO(category); - - var categoriesRootObject = new CategoriesRootObject(); - - categoriesRootObject.Categories.Add(categoryDto); - - var json = _jsonFieldsSerializer.Serialize(categoriesRootObject, fields); - - return new RawJsonActionResult(json); - } - - [HttpPost] - [Route("/api/categories")] - [ProducesResponseType(typeof(CategoriesRootObject), (int)HttpStatusCode.OK)] - [ProducesResponseType(typeof(ErrorsRootObject), 422)] - [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] - [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] - public IActionResult CreateCategory([ModelBinder(typeof(JsonModelBinder))] Delta categoryDelta) - { - // Here we display the errors if the validation has failed at some point. - if (!ModelState.IsValid) - { - return Error(); - } - - //If the validation has passed the categoryDelta object won't be null for sure so we don't need to check for this. - - Picture insertedPicture = null; - - // We need to insert the picture before the category so we can obtain the picture id and map it to the category. - if (categoryDelta.Dto.Image != null && categoryDelta.Dto.Image.Binary != null) - { - insertedPicture = _pictureService.InsertPicture(categoryDelta.Dto.Image.Binary, categoryDelta.Dto.Image.MimeType, string.Empty); - } - - // Inserting the new category - Category category = _factory.Initialize(); - categoryDelta.Merge(category); - - if (insertedPicture != null) - { - category.PictureId = insertedPicture.Id; - } - - _categoryService.InsertCategory(category); - - - UpdateAclRoles(category, categoryDelta.Dto.RoleIds); - - UpdateDiscounts(category, categoryDelta.Dto.DiscountIds); - - UpdateStoreMappings(category, categoryDelta.Dto.StoreIds); - - //search engine name - if (categoryDelta.Dto.SeName != null) - { - var seName = category.ValidateSeName(categoryDelta.Dto.SeName, category.Name, true); - _urlRecordService.SaveSlug(category, seName, 0); - } - - _customerActivityService.InsertActivity("AddNewCategory", - _localizationService.GetResource("ActivityLog.AddNewCategory"), category.Name); - - // Preparing the result dto of the new category - CategoryDto newCategoryDto = _dtoHelper.PrepareCategoryDTO(category); - - var categoriesRootObject = new CategoriesRootObject(); - - categoriesRootObject.Categories.Add(newCategoryDto); - - var json = _jsonFieldsSerializer.Serialize(categoriesRootObject, string.Empty); - - return new RawJsonActionResult(json); - } - - [HttpPut] - [Route("/api/categories/{id}")] - [ProducesResponseType(typeof(CategoriesRootObject), (int)HttpStatusCode.OK)] - [ProducesResponseType(typeof(ErrorsRootObject), 422)] - [ProducesResponseType(typeof(string), (int)HttpStatusCode.NotFound)] - [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] - [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] - public IActionResult UpdateCategory( - [ModelBinder(typeof (JsonModelBinder))] Delta categoryDelta) - { - // Here we display the errors if the validation has failed at some point. - if (!ModelState.IsValid) - { - return Error(); - } - - // We do not need to validate the category id, because this will happen in the model binder using the dto validator. - int updateCategoryId = int.Parse(categoryDelta.Dto.Id); - - Category category = _categoryApiService.GetCategoryById(updateCategoryId); - - if (category == null) - { - return Error(HttpStatusCode.NotFound, "category", "category not found"); - } - - categoryDelta.Merge(category); - - category.UpdatedOnUtc = DateTime.UtcNow; - - _categoryService.UpdateCategory(category); - - UpdatePicture(category, categoryDelta.Dto.Image); - - UpdateAclRoles(category, categoryDelta.Dto.RoleIds); - - UpdateDiscounts(category, categoryDelta.Dto.DiscountIds); - - UpdateStoreMappings(category, categoryDelta.Dto.StoreIds); - - //search engine name - if (categoryDelta.Dto.SeName != null) - { - var seName = category.ValidateSeName(categoryDelta.Dto.SeName, category.Name, true); - _urlRecordService.SaveSlug(category, seName, 0); - } - - _categoryService.UpdateCategory(category); - - _customerActivityService.InsertActivity("UpdateCategory", - _localizationService.GetResource("ActivityLog.UpdateCategory"), category.Name); - - CategoryDto categoryDto = _dtoHelper.PrepareCategoryDTO(category); - - var categoriesRootObject = new CategoriesRootObject(); - - categoriesRootObject.Categories.Add(categoryDto); - - var json = _jsonFieldsSerializer.Serialize(categoriesRootObject, string.Empty); - - return new RawJsonActionResult(json); - } - - [HttpDelete] - [Route("/api/categories/{id}")] - [ProducesResponseType(typeof(void), (int)HttpStatusCode.OK)] - [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] - [ProducesResponseType(typeof(string), (int)HttpStatusCode.NotFound)] - [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] - [GetRequestsErrorInterceptorActionFilter] - public IActionResult DeleteCategory(int id) - { - if (id <= 0) - { - return Error(HttpStatusCode.BadRequest, "id", "invalid id"); - } - - Category categoryToDelete = _categoryApiService.GetCategoryById(id); - - if (categoryToDelete == null) - { - return Error(HttpStatusCode.NotFound, "category", "category not found"); - } - - _categoryService.DeleteCategory(categoryToDelete); - - //activity log - _customerActivityService.InsertActivity("DeleteCategory", _localizationService.GetResource("ActivityLog.DeleteCategory"), categoryToDelete.Name); - - return new RawJsonActionResult("{}"); - } - - private void UpdatePicture(Category categoryEntityToUpdate, ImageDto imageDto) - { - // no image specified then do nothing - if (imageDto == null) - return; - - Picture updatedPicture = null; - Picture currentCategoryPicture = _pictureService.GetPictureById(categoryEntityToUpdate.PictureId); - - // when there is a picture set for the category - if (currentCategoryPicture != null) - { - _pictureService.DeletePicture(currentCategoryPicture); - - // When the image attachment is null or empty. - if (imageDto.Binary == null) - { - categoryEntityToUpdate.PictureId = 0; - } - else - { - updatedPicture = _pictureService.InsertPicture(imageDto.Binary, imageDto.MimeType, string.Empty); - categoryEntityToUpdate.PictureId = updatedPicture.Id; - } - } - // when there isn't a picture set for the category - else - { - if (imageDto.Binary != null) - { - updatedPicture = _pictureService.InsertPicture(imageDto.Binary, imageDto.MimeType, string.Empty); - categoryEntityToUpdate.PictureId = updatedPicture.Id; - } - } - } - - private void UpdateDiscounts(Category category, List passedDiscountIds) - { - if(passedDiscountIds == null) - return; - - var allDiscounts = _discountService.GetAllDiscounts(DiscountType.AssignedToCategories, showHidden: true); - foreach (var discount in allDiscounts) - { - if (passedDiscountIds.Contains(discount.Id)) - { - //new discount - if (category.AppliedDiscounts.Count(d => d.Id == discount.Id) == 0) - category.AppliedDiscounts.Add(discount); - } - else - { - //remove discount - if (category.AppliedDiscounts.Count(d => d.Id == discount.Id) > 0) - category.AppliedDiscounts.Remove(discount); - } - } - _categoryService.UpdateCategory(category); - } - } +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using Nop.Core.Domain.Catalog; +using Nop.Core.Domain.Discounts; +using Nop.Core.Domain.Media; +using Nop.Plugin.Api.Attributes; +using Nop.Plugin.Api.Constants; +using Nop.Plugin.Api.DTOs.Categories; +using Nop.Plugin.Api.Models.CategoriesParameters; +using Nop.Plugin.Api.Services; +using Nop.Services.Catalog; +using Nop.Services.Localization; +using Nop.Services.Logging; +using Nop.Services.Seo; +using Nop.Plugin.Api.Delta; +using Nop.Plugin.Api.DTOs.Images; +using Nop.Plugin.Api.Factories; +using Nop.Plugin.Api.JSON.ActionResults; +using Nop.Plugin.Api.ModelBinders; +using Nop.Services.Customers; +using Nop.Services.Discounts; +using Nop.Services.Media; +using Nop.Services.Security; +using Nop.Services.Stores; +using Nop.Plugin.Api.Helpers; +using Microsoft.AspNetCore.Mvc; + +namespace Nop.Plugin.Api.Controllers +{ + using Microsoft.AspNetCore.Authentication.JwtBearer; + using DTOs.Errors; + using JSON.Serializers; + + [ApiAuthorize(Policy = JwtBearerDefaults.AuthenticationScheme, AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] + public class CategoriesController : BaseApiController + { + private readonly ICategoryApiService _categoryApiService; + private readonly ICategoryService _categoryService; + private readonly IUrlRecordService _urlRecordService; + private readonly IFactory _factory; + private readonly IDTOHelper _dtoHelper; + + public CategoriesController(ICategoryApiService categoryApiService, + IJsonFieldsSerializer jsonFieldsSerializer, + ICategoryService categoryService, + IUrlRecordService urlRecordService, + ICustomerActivityService customerActivityService, + ILocalizationService localizationService, + IPictureService pictureService, + IStoreMappingService storeMappingService, + IStoreService storeService, + IDiscountService discountService, + IAclService aclService, + ICustomerService customerService, + IFactory factory, + IDTOHelper dtoHelper) : base(jsonFieldsSerializer, aclService, customerService, storeMappingService, storeService, discountService, customerActivityService, localizationService,pictureService) + { + _categoryApiService = categoryApiService; + _categoryService = categoryService; + _urlRecordService = urlRecordService; + _factory = factory; + _dtoHelper = dtoHelper; + } + + /// + /// Receive a list of all Categories + /// + /// OK + /// Bad Request + /// Unauthorized + [HttpGet] + [Route("/api/categories")] + [ProducesResponseType(typeof(CategoriesRootObject), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] + [GetRequestsErrorInterceptorActionFilter] + public IActionResult GetCategories(CategoriesParametersModel parameters) + { + if (parameters.Limit < Configurations.MinLimit || parameters.Limit > Configurations.MaxLimit) + { + return Error(HttpStatusCode.BadRequest, "limit", "Invalid limit parameter"); + } + + if (parameters.Page < Configurations.DefaultPageValue) + { + return Error(HttpStatusCode.BadRequest, "page", "Invalid page parameter"); + } + + var allCategories = _categoryApiService.GetCategories(parameters.Ids, parameters.CreatedAtMin, parameters.CreatedAtMax, + parameters.UpdatedAtMin, parameters.UpdatedAtMax, + parameters.Limit, parameters.Page, parameters.SinceId, + parameters.ProductId, parameters.PublishedStatus) + .Where(c => StoreMappingService.Authorize(c)); + + IList categoriesAsDtos = allCategories.Select(category => + { + return _dtoHelper.PrepareCategoryDTO(category); + + }).ToList(); + + var categoriesRootObject = new CategoriesRootObject() + { + Categories = categoriesAsDtos + }; + + var json = JsonFieldsSerializer.Serialize(categoriesRootObject, parameters.Fields); + + return new RawJsonActionResult(json); + } + + /// + /// Receive a count of all Categories + /// + /// OK + /// Unauthorized + [HttpGet] + [Route("/api/categories/count")] + [ProducesResponseType(typeof(CategoriesCountRootObject), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] + [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] + [GetRequestsErrorInterceptorActionFilter] + public IActionResult GetCategoriesCount(CategoriesCountParametersModel parameters) + { + var allCategoriesCount = _categoryApiService.GetCategoriesCount(parameters.CreatedAtMin, parameters.CreatedAtMax, + parameters.UpdatedAtMin, parameters.UpdatedAtMax, + parameters.PublishedStatus, parameters.ProductId); + + var categoriesCountRootObject = new CategoriesCountRootObject() + { + Count = allCategoriesCount + }; + + return Ok(categoriesCountRootObject); + } + + /// + /// Retrieve category by spcified id + /// + /// Id of the category + /// Fields from the category you want your json to contain + /// OK + /// Not Found + /// Unauthorized + [HttpGet] + [Route("/api/categories/{id}")] + [ProducesResponseType(typeof(CategoriesRootObject), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] + [GetRequestsErrorInterceptorActionFilter] + public IActionResult GetCategoryById(int id, string fields = "") + { + if (id <= 0) + { + return Error(HttpStatusCode.BadRequest, "id", "invalid id"); + } + + var category = _categoryApiService.GetCategoryById(id); + + if (category == null) + { + return Error(HttpStatusCode.NotFound, "category", "category not found"); + } + + var categoryDto = _dtoHelper.PrepareCategoryDTO(category); + + var categoriesRootObject = new CategoriesRootObject(); + + categoriesRootObject.Categories.Add(categoryDto); + + var json = JsonFieldsSerializer.Serialize(categoriesRootObject, fields); + + return new RawJsonActionResult(json); + } + + [HttpPost] + [Route("/api/categories")] + [ProducesResponseType(typeof(CategoriesRootObject), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(ErrorsRootObject), 422)] + [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] + public IActionResult CreateCategory([ModelBinder(typeof(JsonModelBinder))] Delta categoryDelta) + { + // Here we display the errors if the validation has failed at some point. + if (!ModelState.IsValid) + { + return Error(); + } + + //If the validation has passed the categoryDelta object won't be null for sure so we don't need to check for this. + + Picture insertedPicture = null; + + // We need to insert the picture before the category so we can obtain the picture id and map it to the category. + if (categoryDelta.Dto.Image != null && categoryDelta.Dto.Image.Binary != null) + { + insertedPicture = PictureService.InsertPicture(categoryDelta.Dto.Image.Binary, categoryDelta.Dto.Image.MimeType, string.Empty); + } + + // Inserting the new category + var category = _factory.Initialize(); + categoryDelta.Merge(category); + + if (insertedPicture != null) + { + category.PictureId = insertedPicture.Id; + } + + _categoryService.InsertCategory(category); + + + UpdateAclRoles(category, categoryDelta.Dto.RoleIds); + + UpdateDiscounts(category, categoryDelta.Dto.DiscountIds); + + UpdateStoreMappings(category, categoryDelta.Dto.StoreIds); + + //search engine name + if (categoryDelta.Dto.SeName != null) + { + var seName = _urlRecordService.ValidateSeName(category, categoryDelta.Dto.SeName, category.Name, true); + _urlRecordService.SaveSlug(category, seName, 0); + } + + CustomerActivityService.InsertActivity("AddNewCategory", + LocalizationService.GetResource("ActivityLog.AddNewCategory"), category); + + // Preparing the result dto of the new category + var newCategoryDto = _dtoHelper.PrepareCategoryDTO(category); + + var categoriesRootObject = new CategoriesRootObject(); + + categoriesRootObject.Categories.Add(newCategoryDto); + + var json = JsonFieldsSerializer.Serialize(categoriesRootObject, string.Empty); + + return new RawJsonActionResult(json); + } + + [HttpPut] + [Route("/api/categories/{id}")] + [ProducesResponseType(typeof(CategoriesRootObject), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(ErrorsRootObject), 422)] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.NotFound)] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] + [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] + public IActionResult UpdateCategory( + [ModelBinder(typeof (JsonModelBinder))] Delta categoryDelta) + { + // Here we display the errors if the validation has failed at some point. + if (!ModelState.IsValid) + { + return Error(); + } + + var category = _categoryApiService.GetCategoryById(categoryDelta.Dto.Id); + + if (category == null) + { + return Error(HttpStatusCode.NotFound, "category", "category not found"); + } + + categoryDelta.Merge(category); + + category.UpdatedOnUtc = DateTime.UtcNow; + + _categoryService.UpdateCategory(category); + + UpdatePicture(category, categoryDelta.Dto.Image); + + UpdateAclRoles(category, categoryDelta.Dto.RoleIds); + + UpdateDiscounts(category, categoryDelta.Dto.DiscountIds); + + UpdateStoreMappings(category, categoryDelta.Dto.StoreIds); + + //search engine name + if (categoryDelta.Dto.SeName != null) + { + + var seName = _urlRecordService.ValidateSeName(category, categoryDelta.Dto.SeName, category.Name, true); + _urlRecordService.SaveSlug(category, seName, 0); + } + + _categoryService.UpdateCategory(category); + + CustomerActivityService.InsertActivity("UpdateCategory", + LocalizationService.GetResource("ActivityLog.UpdateCategory"), category); + + var categoryDto = _dtoHelper.PrepareCategoryDTO(category); + + var categoriesRootObject = new CategoriesRootObject(); + + categoriesRootObject.Categories.Add(categoryDto); + + var json = JsonFieldsSerializer.Serialize(categoriesRootObject, string.Empty); + + return new RawJsonActionResult(json); + } + + [HttpDelete] + [Route("/api/categories/{id}")] + [ProducesResponseType(typeof(void), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.NotFound)] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] + [GetRequestsErrorInterceptorActionFilter] + public IActionResult DeleteCategory(int id) + { + if (id <= 0) + { + return Error(HttpStatusCode.BadRequest, "id", "invalid id"); + } + + var categoryToDelete = _categoryApiService.GetCategoryById(id); + + if (categoryToDelete == null) + { + return Error(HttpStatusCode.NotFound, "category", "category not found"); + } + + _categoryService.DeleteCategory(categoryToDelete); + + //activity log + CustomerActivityService.InsertActivity("DeleteCategory", LocalizationService.GetResource("ActivityLog.DeleteCategory"), categoryToDelete); + + return new RawJsonActionResult("{}"); + } + + private void UpdatePicture(Category categoryEntityToUpdate, ImageDto imageDto) + { + // no image specified then do nothing + if (imageDto == null) + return; + + Picture updatedPicture; + var currentCategoryPicture = PictureService.GetPictureById(categoryEntityToUpdate.PictureId); + + // when there is a picture set for the category + if (currentCategoryPicture != null) + { + PictureService.DeletePicture(currentCategoryPicture); + + // When the image attachment is null or empty. + if (imageDto.Binary == null) + { + categoryEntityToUpdate.PictureId = 0; + } + else + { + updatedPicture = PictureService.InsertPicture(imageDto.Binary, imageDto.MimeType, string.Empty); + categoryEntityToUpdate.PictureId = updatedPicture.Id; + } + } + // when there isn't a picture set for the category + else + { + if (imageDto.Binary != null) + { + updatedPicture = PictureService.InsertPicture(imageDto.Binary, imageDto.MimeType, string.Empty); + categoryEntityToUpdate.PictureId = updatedPicture.Id; + } + } + } + + private void UpdateDiscounts(Category category, List passedDiscountIds) + { + if(passedDiscountIds == null) + return; + + var allDiscounts = DiscountService.GetAllDiscounts(DiscountType.AssignedToCategories, showHidden: true); + foreach (var discount in allDiscounts) + { + if (passedDiscountIds.Contains(discount.Id)) + { + //new discount + if (category.AppliedDiscounts.Count(d => d.Id == discount.Id) == 0) + category.AppliedDiscounts.Add(discount); + } + else + { + //remove discount + if (category.AppliedDiscounts.Count(d => d.Id == discount.Id) > 0) + category.AppliedDiscounts.Remove(discount); + } + } + _categoryService.UpdateCategory(category); + } + } } \ No newline at end of file diff --git a/Nop.Plugin.Api/Controllers/CustomerRolesController.cs b/Nop.Plugin.Api/Controllers/CustomerRolesController.cs index 2afa84c..1c054db 100644 --- a/Nop.Plugin.Api/Controllers/CustomerRolesController.cs +++ b/Nop.Plugin.Api/Controllers/CustomerRolesController.cs @@ -1,77 +1,76 @@ -using System.Collections.Generic; -using System.Linq; -using Nop.Core.Domain.Customers; -using Nop.Plugin.Api.Attributes; -using Nop.Plugin.Api.DTOs.CustomerRoles; -using Nop.Plugin.Api.JSON.ActionResults; -using Nop.Plugin.Api.MappingExtensions; -using Nop.Services.Customers; -using Nop.Services.Discounts; -using Nop.Services.Localization; -using Nop.Services.Logging; -using Nop.Services.Media; -using Nop.Services.Security; -using Nop.Services.Stores; - -namespace Nop.Plugin.Api.Controllers -{ - using System.Net; - using Microsoft.AspNetCore.Authentication.JwtBearer; - using Microsoft.AspNetCore.Mvc; - using Nop.Plugin.Api.DTOs.Errors; - using Nop.Plugin.Api.JSON.Serializers; - - [ApiAuthorize(Policy = JwtBearerDefaults.AuthenticationScheme, AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] - public class CustomerRolesController : BaseApiController - { - public CustomerRolesController( - IJsonFieldsSerializer jsonFieldsSerializer, - IAclService aclService, - ICustomerService customerService, - IStoreMappingService storeMappingService, - IStoreService storeService, - IDiscountService discountService, - ICustomerActivityService customerActivityService, - ILocalizationService localizationService, - IPictureService pictureService) - : base(jsonFieldsSerializer, - aclService, - customerService, - storeMappingService, - storeService, - discountService, - customerActivityService, - localizationService, - pictureService) - { - } - - /// - /// Retrieve all customer roles - /// - /// Fields from the customer role you want your json to contain - /// OK - /// Unauthorized - [HttpGet] - [Route("/api/customer_roles")] - [ProducesResponseType(typeof(CustomerRolesRootObject), (int)HttpStatusCode.OK)] - [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] - [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] - [GetRequestsErrorInterceptorActionFilter] - public IActionResult GetAllCustomerRoles(string fields = "") - { - IList allCustomerRoles = _customerService.GetAllCustomerRoles(); - - IList customerRolesAsDto = allCustomerRoles.Select(role => role.ToDto()).ToList(); - - var customerRolesRootObject = new CustomerRolesRootObject() - { - CustomerRoles = customerRolesAsDto - }; - - var json = _jsonFieldsSerializer.Serialize(customerRolesRootObject, fields); - - return new RawJsonActionResult(json); - } - } -} +using System.Collections.Generic; +using System.Linq; +using Nop.Plugin.Api.Attributes; +using Nop.Plugin.Api.DTOs.CustomerRoles; +using Nop.Plugin.Api.JSON.ActionResults; +using Nop.Plugin.Api.MappingExtensions; +using Nop.Services.Customers; +using Nop.Services.Discounts; +using Nop.Services.Localization; +using Nop.Services.Logging; +using Nop.Services.Media; +using Nop.Services.Security; +using Nop.Services.Stores; + +namespace Nop.Plugin.Api.Controllers +{ + using System.Net; + using Microsoft.AspNetCore.Authentication.JwtBearer; + using Microsoft.AspNetCore.Mvc; + using DTOs.Errors; + using JSON.Serializers; + + [ApiAuthorize(Policy = JwtBearerDefaults.AuthenticationScheme, AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] + public class CustomerRolesController : BaseApiController + { + public CustomerRolesController( + IJsonFieldsSerializer jsonFieldsSerializer, + IAclService aclService, + ICustomerService customerService, + IStoreMappingService storeMappingService, + IStoreService storeService, + IDiscountService discountService, + ICustomerActivityService customerActivityService, + ILocalizationService localizationService, + IPictureService pictureService) + : base(jsonFieldsSerializer, + aclService, + customerService, + storeMappingService, + storeService, + discountService, + customerActivityService, + localizationService, + pictureService) + { + } + + /// + /// Retrieve all customer roles + /// + /// Fields from the customer role you want your json to contain + /// OK + /// Unauthorized + [HttpGet] + [Route("/api/customer_roles")] + [ProducesResponseType(typeof(CustomerRolesRootObject), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] + [GetRequestsErrorInterceptorActionFilter] + public IActionResult GetAllCustomerRoles(string fields = "") + { + var allCustomerRoles = CustomerService.GetAllCustomerRoles(); + + IList customerRolesAsDto = allCustomerRoles.Select(role => role.ToDto()).ToList(); + + var customerRolesRootObject = new CustomerRolesRootObject() + { + CustomerRoles = customerRolesAsDto + }; + + var json = JsonFieldsSerializer.Serialize(customerRolesRootObject, fields); + + return new RawJsonActionResult(json); + } + } +} diff --git a/Nop.Plugin.Api/Controllers/CustomersController.cs b/Nop.Plugin.Api/Controllers/CustomersController.cs index d589756..39e5fa5 100644 --- a/Nop.Plugin.Api/Controllers/CustomersController.cs +++ b/Nop.Plugin.Api/Controllers/CustomersController.cs @@ -1,568 +1,558 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net; -using Nop.Core.Domain.Common; -using Nop.Core.Domain.Customers; -using Nop.Core.Domain.Directory; -using Nop.Core.Infrastructure; -using Nop.Plugin.Api.Attributes; -using Nop.Plugin.Api.Constants; -using Nop.Plugin.Api.Delta; -using Nop.Plugin.Api.DTOs; -using Nop.Plugin.Api.DTOs.Customers; -using Nop.Plugin.Api.Factories; -using Nop.Plugin.Api.Helpers; -using Nop.Plugin.Api.JSON.ActionResults; -using Nop.Plugin.Api.MappingExtensions; -using Nop.Plugin.Api.ModelBinders; -using Nop.Plugin.Api.Models.CustomersParameters; -using Nop.Plugin.Api.Services; -using Nop.Services.Common; -using Nop.Services.Customers; -using Nop.Services.Directory; -using Nop.Services.Discounts; -using Nop.Services.Localization; -using Nop.Services.Logging; -using Nop.Services.Media; -using Nop.Services.Messages; -using Nop.Services.Security; -using Nop.Services.Stores; - -namespace Nop.Plugin.Api.Controllers -{ - using Microsoft.AspNetCore.Authentication.JwtBearer; - using Microsoft.AspNetCore.Authorization; - using Microsoft.AspNetCore.Mvc; - using Nop.Plugin.Api.DTOs.Errors; - using Nop.Plugin.Api.JSON.Serializers; - - [ApiAuthorize(Policy = JwtBearerDefaults.AuthenticationScheme, AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] - public class CustomersController : BaseApiController - { - private readonly ICustomerApiService _customerApiService; - private readonly ICustomerRolesHelper _customerRolesHelper; - private readonly IGenericAttributeService _genericAttributeService; - private readonly IEncryptionService _encryptionService; - private readonly ICountryService _countryService; - private readonly IMappingHelper _mappingHelper; - private readonly INewsLetterSubscriptionService _newsLetterSubscriptionService; - private readonly ILanguageService _languageService; - private readonly IFactory _factory; - - // We resolve the customer settings this way because of the tests. - // The auto mocking does not support concreate types as dependencies. It supports only interfaces. - private CustomerSettings _customerSettings; - - private CustomerSettings CustomerSettings - { - get - { - if (_customerSettings == null) - { - _customerSettings = EngineContext.Current.Resolve(); - } - - return _customerSettings; - } - } - - public CustomersController( - ICustomerApiService customerApiService, - IJsonFieldsSerializer jsonFieldsSerializer, - IAclService aclService, - ICustomerService customerService, - IStoreMappingService storeMappingService, - IStoreService storeService, - IDiscountService discountService, - ICustomerActivityService customerActivityService, - ILocalizationService localizationService, - ICustomerRolesHelper customerRolesHelper, - IGenericAttributeService genericAttributeService, - IEncryptionService encryptionService, - IFactory factory, - ICountryService countryService, - IMappingHelper mappingHelper, - INewsLetterSubscriptionService newsLetterSubscriptionService, - IPictureService pictureService, ILanguageService languageService) : - base(jsonFieldsSerializer, aclService, customerService, storeMappingService, storeService, discountService, customerActivityService, localizationService,pictureService) - { - _customerApiService = customerApiService; - _factory = factory; - _countryService = countryService; - _mappingHelper = mappingHelper; - _newsLetterSubscriptionService = newsLetterSubscriptionService; - _languageService = languageService; - _encryptionService = encryptionService; - _genericAttributeService = genericAttributeService; - _customerRolesHelper = customerRolesHelper; - } - - /// - /// Retrieve all customers of a shop - /// - /// OK - /// Bad request - /// Unauthorized - [HttpGet] - [Route("/api/customers")] - [ProducesResponseType(typeof(CustomersRootObject), (int)HttpStatusCode.OK)] - [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] - [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] - [GetRequestsErrorInterceptorActionFilter] - public IActionResult GetCustomers(CustomersParametersModel parameters) - { - if (parameters.Limit < Configurations.MinLimit || parameters.Limit > Configurations.MaxLimit) - { - return Error(HttpStatusCode.BadRequest, "limit", "Invalid limit parameter"); - } - - if (parameters.Page < Configurations.DefaultPageValue) - { - return Error(HttpStatusCode.BadRequest, "page", "Invalid request parameters"); - } - - IList allCustomers = _customerApiService.GetCustomersDtos(parameters.CreatedAtMin, parameters.CreatedAtMax, parameters.Limit, parameters.Page, parameters.SinceId); - - var customersRootObject = new CustomersRootObject() - { - Customers = allCustomers - }; - - var json = _jsonFieldsSerializer.Serialize(customersRootObject, parameters.Fields); - - return new RawJsonActionResult(json); - } - - /// - /// Retrieve customer by spcified id - /// - /// Id of the customer - /// Fields from the customer you want your json to contain - /// OK - /// Not Found - /// Unauthorized - [HttpGet] - [Route("/api/customers/{id}")] - [ProducesResponseType(typeof(CustomersRootObject), (int)HttpStatusCode.OK)] - [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] - [ProducesResponseType(typeof(string), (int)HttpStatusCode.NotFound)] - [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] - [GetRequestsErrorInterceptorActionFilter] - public IActionResult GetCustomerById(int id, string fields = "") - { - if (id <= 0) - { - return Error(HttpStatusCode.BadRequest, "id", "invalid id"); - } - - CustomerDto customer = _customerApiService.GetCustomerById(id); - - if (customer == null) - { - return Error(HttpStatusCode.NotFound, "customer", "not found"); - } - - var customersRootObject = new CustomersRootObject(); - customersRootObject.Customers.Add(customer); - - var json = _jsonFieldsSerializer.Serialize(customersRootObject, fields); - - return new RawJsonActionResult(json); - } - - - /// - /// Get a count of all customers - /// - /// OK - /// Unauthorized - [HttpGet] - [Route("/api/customers/count")] - [ProducesResponseType(typeof(CustomersCountRootObject), (int)HttpStatusCode.OK)] - [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] - [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] - public IActionResult GetCustomersCount() - { - var allCustomersCount = _customerApiService.GetCustomersCount(); - - var customersCountRootObject = new CustomersCountRootObject() - { - Count = allCustomersCount - }; - - return Ok(customersCountRootObject); - } - - /// - /// Search for customers matching supplied query - /// - /// OK - /// Bad Request - /// Unauthorized - [HttpGet] - [Route("/api/customers/search")] - [ProducesResponseType(typeof(CustomersRootObject), (int)HttpStatusCode.OK)] - [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] - [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] - public IActionResult Search(CustomersSearchParametersModel parameters) - { - if (parameters.Limit <= Configurations.MinLimit || parameters.Limit > Configurations.MaxLimit) - { - return Error(HttpStatusCode.BadRequest, "limit" ,"Invalid limit parameter"); - } - - if (parameters.Page <= 0) - { - return Error(HttpStatusCode.BadRequest, "page", "Invalid page parameter"); - } - - IList customersDto = _customerApiService.Search(parameters.Query, parameters.Order, parameters.Page, parameters.Limit); - - var customersRootObject = new CustomersRootObject() - { - Customers = customersDto - }; - - var json = _jsonFieldsSerializer.Serialize(customersRootObject, parameters.Fields); - - return new RawJsonActionResult(json); - } - - [HttpPost] - [Route("/api/customers")] - [ProducesResponseType(typeof(CustomersRootObject), (int)HttpStatusCode.OK)] - [ProducesResponseType(typeof(ErrorsRootObject), 422)] - [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] - public IActionResult CreateCustomer([ModelBinder(typeof(JsonModelBinder))] Delta customerDelta) - { - // Here we display the errors if the validation has failed at some point. - if (!ModelState.IsValid) - { - return Error(); - } - - //If the validation has passed the customerDelta object won't be null for sure so we don't need to check for this. - - // Inserting the new customer - Customer newCustomer = _factory.Initialize(); - customerDelta.Merge(newCustomer); - - foreach (var address in customerDelta.Dto.CustomerAddresses) - { - // we need to explicitly set the date as if it is not specified - // it will default to 01/01/0001 which is not supported by SQL Server and throws and exception - if (address.CreatedOnUtc == null) - { - address.CreatedOnUtc = DateTime.UtcNow; - } - newCustomer.Addresses.Add(address.ToEntity()); - } - - _customerService.InsertCustomer(newCustomer); - - InsertFirstAndLastNameGenericAttributes(customerDelta.Dto.FirstName, customerDelta.Dto.LastName, newCustomer); - - int languageId = 0; - - if (!string.IsNullOrEmpty(customerDelta.Dto.LanguageId) && int.TryParse(customerDelta.Dto.LanguageId, out languageId) - && _languageService.GetLanguageById(languageId) != null) - { - _genericAttributeService.SaveAttribute(newCustomer, SystemCustomerAttributeNames.LanguageId, languageId); - } - - //password - if (!string.IsNullOrWhiteSpace(customerDelta.Dto.Password)) - { - AddPassword(customerDelta.Dto.Password, newCustomer); - } - - // We need to insert the entity first so we can have its id in order to map it to anything. - // TODO: Localization - // TODO: move this before inserting the customer. - if (customerDelta.Dto.RoleIds.Count > 0) - { - AddValidRoles(customerDelta, newCustomer); - - _customerService.UpdateCustomer(newCustomer); - } - - // Preparing the result dto of the new customer - // We do not prepare the shopping cart items because we have a separate endpoint for them. - CustomerDto newCustomerDto = newCustomer.ToDto(); - - // This is needed because the entity framework won't populate the navigation properties automatically - // and the country will be left null. So we do it by hand here. - PopulateAddressCountryNames(newCustomerDto); - - // Set the fist and last name separately because they are not part of the customer entity, but are saved in the generic attributes. - newCustomerDto.FirstName = customerDelta.Dto.FirstName; - newCustomerDto.LastName = customerDelta.Dto.LastName; - - newCustomerDto.LanguageId = customerDelta.Dto.LanguageId; - - //activity log - _customerActivityService.InsertActivity("AddNewCustomer", _localizationService.GetResource("ActivityLog.AddNewCustomer"), newCustomer.Id); - - var customersRootObject = new CustomersRootObject(); - - customersRootObject.Customers.Add(newCustomerDto); - - var json = _jsonFieldsSerializer.Serialize(customersRootObject, string.Empty); - - return new RawJsonActionResult(json); - } - - [HttpPut] - [Route("/api/customers/{id}")] - [ProducesResponseType(typeof(CustomersRootObject), (int)HttpStatusCode.OK)] - [ProducesResponseType(typeof(ErrorsRootObject), 422)] - [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] - [ProducesResponseType(typeof(string), (int)HttpStatusCode.NotFound)] - [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] - public IActionResult UpdateCustomer([ModelBinder(typeof(JsonModelBinder))] Delta customerDelta) - { - // Here we display the errors if the validation has failed at some point. - if (!ModelState.IsValid) - { - return Error(); - } - - //If the validation has passed the customerDelta object won't be null for sure so we don't need to check for this. - - // Updateting the customer - Customer currentCustomer = _customerApiService.GetCustomerEntityById(int.Parse(customerDelta.Dto.Id)); - - if (currentCustomer == null) - { - return Error(HttpStatusCode.NotFound, "customer", "not found"); - } - - customerDelta.Merge(currentCustomer); - - if (customerDelta.Dto.RoleIds.Count > 0) - { - // Remove all roles - while (currentCustomer.CustomerRoles.Count > 0) - { - currentCustomer.CustomerRoles.Remove(currentCustomer.CustomerRoles.First()); - } - - AddValidRoles(customerDelta, currentCustomer); - } - - if (customerDelta.Dto.CustomerAddresses.Count > 0) - { - var currentCustomerAddresses = currentCustomer.Addresses.ToDictionary(address => address.Id, address => address); - - foreach (var passedAddress in customerDelta.Dto.CustomerAddresses) - { - int passedAddressId = int.Parse(passedAddress.Id); - Address addressEntity = passedAddress.ToEntity(); - - if (currentCustomerAddresses.ContainsKey(passedAddressId)) - { - _mappingHelper.Merge(passedAddress, currentCustomerAddresses[passedAddressId]); - } - else - { - currentCustomer.Addresses.Add(addressEntity); - } - } - } - - _customerService.UpdateCustomer(currentCustomer); - - InsertFirstAndLastNameGenericAttributes(customerDelta.Dto.FirstName, customerDelta.Dto.LastName, currentCustomer); - - - int languageId = 0; - - if (!string.IsNullOrEmpty(customerDelta.Dto.LanguageId) && int.TryParse(customerDelta.Dto.LanguageId, out languageId) - && _languageService.GetLanguageById(languageId) != null) - { - _genericAttributeService.SaveAttribute(currentCustomer, SystemCustomerAttributeNames.LanguageId, languageId); - } - - //password - if (!string.IsNullOrWhiteSpace(customerDelta.Dto.Password)) - { - AddPassword(customerDelta.Dto.Password, currentCustomer); - } - - // TODO: Localization - - // Preparing the result dto of the new customer - // We do not prepare the shopping cart items because we have a separate endpoint for them. - CustomerDto updatedCustomer = currentCustomer.ToDto(); - - // This is needed because the entity framework won't populate the navigation properties automatically - // and the country name will be left empty because the mapping depends on the navigation property - // so we do it by hand here. - PopulateAddressCountryNames(updatedCustomer); - - // Set the fist and last name separately because they are not part of the customer entity, but are saved in the generic attributes. - var firstNameGenericAttribute = _genericAttributeService.GetAttributesForEntity(currentCustomer.Id, typeof(Customer).Name) - .FirstOrDefault(x => x.Key == "FirstName"); - - if (firstNameGenericAttribute != null) - { - updatedCustomer.FirstName = firstNameGenericAttribute.Value; - } - - var lastNameGenericAttribute = _genericAttributeService.GetAttributesForEntity(currentCustomer.Id, typeof(Customer).Name) - .FirstOrDefault(x => x.Key == "LastName"); - - if (lastNameGenericAttribute != null) - { - updatedCustomer.LastName = lastNameGenericAttribute.Value; - } - - var languageIdGenericAttribute = _genericAttributeService.GetAttributesForEntity(currentCustomer.Id, typeof(Customer).Name) - .FirstOrDefault(x => x.Key == "LanguageId"); - - if (languageIdGenericAttribute != null) - { - updatedCustomer.LanguageId = languageIdGenericAttribute.Value; - } - - //activity log - _customerActivityService.InsertActivity("UpdateCustomer", _localizationService.GetResource("ActivityLog.UpdateCustomer"), currentCustomer.Id); - - var customersRootObject = new CustomersRootObject(); - - customersRootObject.Customers.Add(updatedCustomer); - - var json = _jsonFieldsSerializer.Serialize(customersRootObject, string.Empty); - - return new RawJsonActionResult(json); - } - - [HttpDelete] - [Route("/api/customers/{id}")] - [GetRequestsErrorInterceptorActionFilter] - [ProducesResponseType(typeof(void), (int)HttpStatusCode.OK)] - [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] - [ProducesResponseType(typeof(string), (int)HttpStatusCode.NotFound)] - [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] - public IActionResult DeleteCustomer(int id) - { - if (id <= 0) - { - return Error(HttpStatusCode.BadRequest, "id", "invalid id"); - } - - Customer customer = _customerApiService.GetCustomerEntityById(id); - - if (customer == null) - { - return Error(HttpStatusCode.NotFound, "customer", "not found"); - } - - _customerService.DeleteCustomer(customer); - - //remove newsletter subscription (if exists) - foreach (var store in _storeService.GetAllStores()) - { - var subscription = _newsLetterSubscriptionService.GetNewsLetterSubscriptionByEmailAndStoreId(customer.Email, store.Id); - if (subscription != null) - _newsLetterSubscriptionService.DeleteNewsLetterSubscription(subscription); - } - - //activity log - _customerActivityService.InsertActivity("DeleteCustomer", _localizationService.GetResource("ActivityLog.DeleteCustomer"), customer.Id); - - return new RawJsonActionResult("{}"); - } - - private void InsertFirstAndLastNameGenericAttributes(string firstName, string lastName, Customer newCustomer) - { - // we assume that if the first name is not sent then it will be null and in this case we don't want to update it - if (firstName != null) - { - _genericAttributeService.SaveAttribute(newCustomer, SystemCustomerAttributeNames.FirstName, firstName); - } - - if (lastName != null) - { - _genericAttributeService.SaveAttribute(newCustomer, SystemCustomerAttributeNames.LastName, lastName); - } - } - - private void AddValidRoles(Delta customerDelta, Customer currentCustomer) - { - IList validCustomerRoles = - _customerRolesHelper.GetValidCustomerRoles(customerDelta.Dto.RoleIds).ToList(); - - // Add all newly passed roles - foreach (var role in validCustomerRoles) - { - currentCustomer.CustomerRoles.Add(role); - } - } - - private void PopulateAddressCountryNames(CustomerDto newCustomerDto) - { - foreach (var address in newCustomerDto.CustomerAddresses) - { - SetCountryName(address); - } - - if (newCustomerDto.BillingAddress != null) - { - SetCountryName(newCustomerDto.BillingAddress); - } - - if (newCustomerDto.ShippingAddress != null) - { - SetCountryName(newCustomerDto.ShippingAddress); - } - } - - private void SetCountryName(AddressDto address) - { - if (string.IsNullOrEmpty(address.CountryName) && address.CountryId.HasValue) - { - Country country = _countryService.GetCountryById(address.CountryId.Value); - address.CountryName = country.Name; - } - } - - private void AddPassword(string newPassword, Customer customer) - { - // TODO: call this method before inserting the customer. - var customerPassword = new CustomerPassword - { - Customer = customer, - PasswordFormat = CustomerSettings.DefaultPasswordFormat, - CreatedOnUtc = DateTime.UtcNow - }; - - switch (CustomerSettings.DefaultPasswordFormat) - { - case PasswordFormat.Clear: - { - customerPassword.Password = newPassword; - } - break; - case PasswordFormat.Encrypted: - { - customerPassword.Password = _encryptionService.EncryptText(newPassword); - } - break; - case PasswordFormat.Hashed: - { - string saltKey = _encryptionService.CreateSaltKey(5); - customerPassword.PasswordSalt = saltKey; - customerPassword.Password = _encryptionService.CreatePasswordHash(newPassword, saltKey, CustomerSettings.HashedPasswordFormat); - } - break; - } - - _customerService.InsertCustomerPassword(customerPassword); - - // TODO: remove this. - _customerService.UpdateCustomer(customer); - } - } +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using Nop.Core.Domain.Customers; +using Nop.Core.Infrastructure; +using Nop.Plugin.Api.Attributes; +using Nop.Plugin.Api.Constants; +using Nop.Plugin.Api.Delta; +using Nop.Plugin.Api.DTOs; +using Nop.Plugin.Api.DTOs.Customers; +using Nop.Plugin.Api.Factories; +using Nop.Plugin.Api.Helpers; +using Nop.Plugin.Api.JSON.ActionResults; +using Nop.Plugin.Api.MappingExtensions; +using Nop.Plugin.Api.ModelBinders; +using Nop.Plugin.Api.Models.CustomersParameters; +using Nop.Plugin.Api.Services; +using Nop.Services.Common; +using Nop.Services.Customers; +using Nop.Services.Directory; +using Nop.Services.Discounts; +using Nop.Services.Localization; +using Nop.Services.Logging; +using Nop.Services.Media; +using Nop.Services.Messages; +using Nop.Services.Security; +using Nop.Services.Stores; + +namespace Nop.Plugin.Api.Controllers +{ + using Microsoft.AspNetCore.Authentication.JwtBearer; + using Microsoft.AspNetCore.Mvc; + using DTOs.Errors; + using JSON.Serializers; + + [ApiAuthorize(Policy = JwtBearerDefaults.AuthenticationScheme, AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] + public class CustomersController : BaseApiController + { + private readonly ICustomerApiService _customerApiService; + private readonly ICustomerRolesHelper _customerRolesHelper; + private readonly IGenericAttributeService _genericAttributeService; + private readonly IEncryptionService _encryptionService; + private readonly ICountryService _countryService; + private readonly IMappingHelper _mappingHelper; + private readonly INewsLetterSubscriptionService _newsLetterSubscriptionService; + private readonly ILanguageService _languageService; + private readonly IFactory _factory; + + // We resolve the customer settings this way because of the tests. + // The auto mocking does not support concreate types as dependencies. It supports only interfaces. + private CustomerSettings _customerSettings; + + private CustomerSettings CustomerSettings + { + get + { + if (_customerSettings == null) + { + _customerSettings = EngineContext.Current.Resolve(); + } + + return _customerSettings; + } + } + + public CustomersController( + ICustomerApiService customerApiService, + IJsonFieldsSerializer jsonFieldsSerializer, + IAclService aclService, + ICustomerService customerService, + IStoreMappingService storeMappingService, + IStoreService storeService, + IDiscountService discountService, + ICustomerActivityService customerActivityService, + ILocalizationService localizationService, + ICustomerRolesHelper customerRolesHelper, + IGenericAttributeService genericAttributeService, + IEncryptionService encryptionService, + IFactory factory, + ICountryService countryService, + IMappingHelper mappingHelper, + INewsLetterSubscriptionService newsLetterSubscriptionService, + IPictureService pictureService, ILanguageService languageService) : + base(jsonFieldsSerializer, aclService, customerService, storeMappingService, storeService, discountService, customerActivityService, localizationService,pictureService) + { + _customerApiService = customerApiService; + _factory = factory; + _countryService = countryService; + _mappingHelper = mappingHelper; + _newsLetterSubscriptionService = newsLetterSubscriptionService; + _languageService = languageService; + _encryptionService = encryptionService; + _genericAttributeService = genericAttributeService; + _customerRolesHelper = customerRolesHelper; + } + + /// + /// Retrieve all customers of a shop + /// + /// OK + /// Bad request + /// Unauthorized + [HttpGet] + [Route("/api/customers")] + [ProducesResponseType(typeof(CustomersRootObject), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] + [GetRequestsErrorInterceptorActionFilter] + public IActionResult GetCustomers(CustomersParametersModel parameters) + { + if (parameters.Limit < Configurations.MinLimit || parameters.Limit > Configurations.MaxLimit) + { + return Error(HttpStatusCode.BadRequest, "limit", "Invalid limit parameter"); + } + + if (parameters.Page < Configurations.DefaultPageValue) + { + return Error(HttpStatusCode.BadRequest, "page", "Invalid request parameters"); + } + + var allCustomers = _customerApiService.GetCustomersDtos(parameters.CreatedAtMin, parameters.CreatedAtMax, parameters.Limit, parameters.Page, parameters.SinceId); + + var customersRootObject = new CustomersRootObject() + { + Customers = allCustomers + }; + + var json = JsonFieldsSerializer.Serialize(customersRootObject, parameters.Fields); + + return new RawJsonActionResult(json); + } + + /// + /// Retrieve customer by spcified id + /// + /// Id of the customer + /// Fields from the customer you want your json to contain + /// OK + /// Not Found + /// Unauthorized + [HttpGet] + [Route("/api/customers/{id}")] + [ProducesResponseType(typeof(CustomersRootObject), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.NotFound)] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] + [GetRequestsErrorInterceptorActionFilter] + public IActionResult GetCustomerById(int id, string fields = "") + { + if (id <= 0) + { + return Error(HttpStatusCode.BadRequest, "id", "invalid id"); + } + + var customer = _customerApiService.GetCustomerById(id); + + if (customer == null) + { + return Error(HttpStatusCode.NotFound, "customer", "not found"); + } + + var customersRootObject = new CustomersRootObject(); + customersRootObject.Customers.Add(customer); + + var json = JsonFieldsSerializer.Serialize(customersRootObject, fields); + + return new RawJsonActionResult(json); + } + + + /// + /// Get a count of all customers + /// + /// OK + /// Unauthorized + [HttpGet] + [Route("/api/customers/count")] + [ProducesResponseType(typeof(CustomersCountRootObject), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] + [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] + public IActionResult GetCustomersCount() + { + var allCustomersCount = _customerApiService.GetCustomersCount(); + + var customersCountRootObject = new CustomersCountRootObject() + { + Count = allCustomersCount + }; + + return Ok(customersCountRootObject); + } + + /// + /// Search for customers matching supplied query + /// + /// OK + /// Bad Request + /// Unauthorized + [HttpGet] + [Route("/api/customers/search")] + [ProducesResponseType(typeof(CustomersRootObject), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] + public IActionResult Search(CustomersSearchParametersModel parameters) + { + if (parameters.Limit <= Configurations.MinLimit || parameters.Limit > Configurations.MaxLimit) + { + return Error(HttpStatusCode.BadRequest, "limit" ,"Invalid limit parameter"); + } + + if (parameters.Page <= 0) + { + return Error(HttpStatusCode.BadRequest, "page", "Invalid page parameter"); + } + + var customersDto = _customerApiService.Search(parameters.Query, parameters.Order, parameters.Page, parameters.Limit); + + var customersRootObject = new CustomersRootObject() + { + Customers = customersDto + }; + + var json = JsonFieldsSerializer.Serialize(customersRootObject, parameters.Fields); + + return new RawJsonActionResult(json); + } + + [HttpPost] + [Route("/api/customers")] + [ProducesResponseType(typeof(CustomersRootObject), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(ErrorsRootObject), 422)] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] + public IActionResult CreateCustomer([ModelBinder(typeof(JsonModelBinder))] Delta customerDelta) + { + // Here we display the errors if the validation has failed at some point. + if (!ModelState.IsValid) + { + return Error(); + } + + //If the validation has passed the customerDelta object won't be null for sure so we don't need to check for this. + + // Inserting the new customer + var newCustomer = _factory.Initialize(); + customerDelta.Merge(newCustomer); + + foreach (var address in customerDelta.Dto.Addresses) + { + // we need to explicitly set the date as if it is not specified + // it will default to 01/01/0001 which is not supported by SQL Server and throws and exception + if (address.CreatedOnUtc == null) + { + address.CreatedOnUtc = DateTime.UtcNow; + } + newCustomer.Addresses.Add(address.ToEntity()); + } + + CustomerService.InsertCustomer(newCustomer); + + InsertFirstAndLastNameGenericAttributes(customerDelta.Dto.FirstName, customerDelta.Dto.LastName, newCustomer); + + if (!string.IsNullOrEmpty(customerDelta.Dto.LanguageId) && int.TryParse(customerDelta.Dto.LanguageId, out var languageId) + && _languageService.GetLanguageById(languageId) != null) + { + _genericAttributeService.SaveAttribute(newCustomer, NopCustomerDefaults.LanguageIdAttribute, languageId); + } + + //password + if (!string.IsNullOrWhiteSpace(customerDelta.Dto.Password)) + { + AddPassword(customerDelta.Dto.Password, newCustomer); + } + + // We need to insert the entity first so we can have its id in order to map it to anything. + // TODO: Localization + // TODO: move this before inserting the customer. + if (customerDelta.Dto.RoleIds.Count > 0) + { + AddValidRoles(customerDelta, newCustomer); + + CustomerService.UpdateCustomer(newCustomer); + } + + // Preparing the result dto of the new customer + // We do not prepare the shopping cart items because we have a separate endpoint for them. + var newCustomerDto = newCustomer.ToDto(); + + // This is needed because the entity framework won't populate the navigation properties automatically + // and the country will be left null. So we do it by hand here. + PopulateAddressCountryNames(newCustomerDto); + + // Set the fist and last name separately because they are not part of the customer entity, but are saved in the generic attributes. + newCustomerDto.FirstName = customerDelta.Dto.FirstName; + newCustomerDto.LastName = customerDelta.Dto.LastName; + + newCustomerDto.LanguageId = customerDelta.Dto.LanguageId; + + //activity log + CustomerActivityService.InsertActivity("AddNewCustomer", LocalizationService.GetResource("ActivityLog.AddNewCustomer"), newCustomer); + + var customersRootObject = new CustomersRootObject(); + + customersRootObject.Customers.Add(newCustomerDto); + + var json = JsonFieldsSerializer.Serialize(customersRootObject, string.Empty); + + return new RawJsonActionResult(json); + } + + [HttpPut] + [Route("/api/customers/{id}")] + [ProducesResponseType(typeof(CustomersRootObject), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(ErrorsRootObject), 422)] + [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.NotFound)] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] + public IActionResult UpdateCustomer([ModelBinder(typeof(JsonModelBinder))] Delta customerDelta) + { + // Here we display the errors if the validation has failed at some point. + if (!ModelState.IsValid) + { + return Error(); + } + + // Updateting the customer + var currentCustomer = _customerApiService.GetCustomerEntityById(customerDelta.Dto.Id); + + if (currentCustomer == null) + { + return Error(HttpStatusCode.NotFound, "customer", "not found"); + } + + customerDelta.Merge(currentCustomer); + + if (customerDelta.Dto.RoleIds.Count > 0) + { + // Remove all roles + while (currentCustomer.CustomerRoles.Count > 0) + { + currentCustomer.CustomerRoles.Remove(currentCustomer.CustomerRoles.First()); + } + + AddValidRoles(customerDelta, currentCustomer); + } + + if (customerDelta.Dto.Addresses.Count > 0) + { + var currentCustomerAddresses = currentCustomer.Addresses.ToDictionary(address => address.Id, address => address); + + foreach (var passedAddress in customerDelta.Dto.Addresses) + { + var addressEntity = passedAddress.ToEntity(); + + if (currentCustomerAddresses.ContainsKey(passedAddress.Id)) + { + _mappingHelper.Merge(passedAddress, currentCustomerAddresses[passedAddress.Id]); + } + else + { + currentCustomer.Addresses.Add(addressEntity); + } + } + } + + CustomerService.UpdateCustomer(currentCustomer); + + InsertFirstAndLastNameGenericAttributes(customerDelta.Dto.FirstName, customerDelta.Dto.LastName, currentCustomer); + + + if (!string.IsNullOrEmpty(customerDelta.Dto.LanguageId) && int.TryParse(customerDelta.Dto.LanguageId, out var languageId) + && _languageService.GetLanguageById(languageId) != null) + { + _genericAttributeService.SaveAttribute(currentCustomer, NopCustomerDefaults.LanguageIdAttribute, languageId); + } + + //password + if (!string.IsNullOrWhiteSpace(customerDelta.Dto.Password)) + { + AddPassword(customerDelta.Dto.Password, currentCustomer); + } + + // TODO: Localization + + // Preparing the result dto of the new customer + // We do not prepare the shopping cart items because we have a separate endpoint for them. + var updatedCustomer = currentCustomer.ToDto(); + + // This is needed because the entity framework won't populate the navigation properties automatically + // and the country name will be left empty because the mapping depends on the navigation property + // so we do it by hand here. + PopulateAddressCountryNames(updatedCustomer); + + // Set the fist and last name separately because they are not part of the customer entity, but are saved in the generic attributes. + var firstNameGenericAttribute = _genericAttributeService.GetAttributesForEntity(currentCustomer.Id, typeof(Customer).Name) + .FirstOrDefault(x => x.Key == "FirstName"); + + if (firstNameGenericAttribute != null) + { + updatedCustomer.FirstName = firstNameGenericAttribute.Value; + } + + var lastNameGenericAttribute = _genericAttributeService.GetAttributesForEntity(currentCustomer.Id, typeof(Customer).Name) + .FirstOrDefault(x => x.Key == "LastName"); + + if (lastNameGenericAttribute != null) + { + updatedCustomer.LastName = lastNameGenericAttribute.Value; + } + + var languageIdGenericAttribute = _genericAttributeService.GetAttributesForEntity(currentCustomer.Id, typeof(Customer).Name) + .FirstOrDefault(x => x.Key == "LanguageId"); + + if (languageIdGenericAttribute != null) + { + updatedCustomer.LanguageId = languageIdGenericAttribute.Value; + } + + //activity log + CustomerActivityService.InsertActivity("UpdateCustomer", LocalizationService.GetResource("ActivityLog.UpdateCustomer"), currentCustomer); + + var customersRootObject = new CustomersRootObject(); + + customersRootObject.Customers.Add(updatedCustomer); + + var json = JsonFieldsSerializer.Serialize(customersRootObject, string.Empty); + + return new RawJsonActionResult(json); + } + + [HttpDelete] + [Route("/api/customers/{id}")] + [GetRequestsErrorInterceptorActionFilter] + [ProducesResponseType(typeof(void), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.NotFound)] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] + public IActionResult DeleteCustomer(int id) + { + if (id <= 0) + { + return Error(HttpStatusCode.BadRequest, "id", "invalid id"); + } + + var customer = _customerApiService.GetCustomerEntityById(id); + + if (customer == null) + { + return Error(HttpStatusCode.NotFound, "customer", "not found"); + } + + CustomerService.DeleteCustomer(customer); + + //remove newsletter subscription (if exists) + foreach (var store in StoreService.GetAllStores()) + { + var subscription = _newsLetterSubscriptionService.GetNewsLetterSubscriptionByEmailAndStoreId(customer.Email, store.Id); + if (subscription != null) + _newsLetterSubscriptionService.DeleteNewsLetterSubscription(subscription); + } + + //activity log + CustomerActivityService.InsertActivity("DeleteCustomer", LocalizationService.GetResource("ActivityLog.DeleteCustomer"), customer); + + return new RawJsonActionResult("{}"); + } + + private void InsertFirstAndLastNameGenericAttributes(string firstName, string lastName, Customer newCustomer) + { + // we assume that if the first name is not sent then it will be null and in this case we don't want to update it + if (firstName != null) + { + _genericAttributeService.SaveAttribute(newCustomer, NopCustomerDefaults.FirstNameAttribute, firstName); + } + + if (lastName != null) + { + _genericAttributeService.SaveAttribute(newCustomer, NopCustomerDefaults.LastNameAttribute, lastName); + } + } + + private void AddValidRoles(Delta customerDelta, Customer currentCustomer) + { + IList validCustomerRoles = + _customerRolesHelper.GetValidCustomerRoles(customerDelta.Dto.RoleIds).ToList(); + + // Add all newly passed roles + foreach (var role in validCustomerRoles) + { + currentCustomer.CustomerRoles.Add(role); + } + } + + private void PopulateAddressCountryNames(CustomerDto newCustomerDto) + { + foreach (var address in newCustomerDto.Addresses) + { + SetCountryName(address); + } + + if (newCustomerDto.BillingAddress != null) + { + SetCountryName(newCustomerDto.BillingAddress); + } + + if (newCustomerDto.ShippingAddress != null) + { + SetCountryName(newCustomerDto.ShippingAddress); + } + } + + private void SetCountryName(AddressDto address) + { + if (string.IsNullOrEmpty(address.CountryName) && address.CountryId.HasValue) + { + var country = _countryService.GetCountryById(address.CountryId.Value); + address.CountryName = country.Name; + } + } + + private void AddPassword(string newPassword, Customer customer) + { + // TODO: call this method before inserting the customer. + var customerPassword = new CustomerPassword + { + Customer = customer, + PasswordFormat = CustomerSettings.DefaultPasswordFormat, + CreatedOnUtc = DateTime.UtcNow + }; + + switch (CustomerSettings.DefaultPasswordFormat) + { + case PasswordFormat.Clear: + { + customerPassword.Password = newPassword; + } + break; + case PasswordFormat.Encrypted: + { + customerPassword.Password = _encryptionService.EncryptText(newPassword); + } + break; + case PasswordFormat.Hashed: + { + var saltKey = _encryptionService.CreateSaltKey(5); + customerPassword.PasswordSalt = saltKey; + customerPassword.Password = _encryptionService.CreatePasswordHash(newPassword, saltKey, CustomerSettings.HashedPasswordFormat); + } + break; + } + + CustomerService.InsertCustomerPassword(customerPassword); + + // TODO: remove this. + CustomerService.UpdateCustomer(customer); + } + } } \ No newline at end of file diff --git a/Nop.Plugin.Api/Controllers/LanguagesController.cs b/Nop.Plugin.Api/Controllers/LanguagesController.cs index c3ae061..98f6bd9 100644 --- a/Nop.Plugin.Api/Controllers/LanguagesController.cs +++ b/Nop.Plugin.Api/Controllers/LanguagesController.cs @@ -1,83 +1,82 @@ -using System.Collections.Generic; -using System.Linq; -using Nop.Core.Domain.Localization; -using Nop.Plugin.Api.Attributes; -using Nop.Plugin.Api.DTOs.Languages; -using Nop.Plugin.Api.Helpers; -using Nop.Plugin.Api.JSON.ActionResults; -using Nop.Services.Customers; -using Nop.Services.Discounts; -using Nop.Services.Localization; -using Nop.Services.Logging; -using Nop.Services.Media; -using Nop.Services.Security; -using Nop.Services.Stores; - -namespace Nop.Plugin.Api.Controllers -{ - using System.Net; - using Microsoft.AspNetCore.Authentication.JwtBearer; - using Microsoft.AspNetCore.Mvc; - using Nop.Plugin.Api.DTOs.Errors; - using Nop.Plugin.Api.JSON.Serializers; - - [ApiAuthorize(Policy = JwtBearerDefaults.AuthenticationScheme, AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] - public class LanguagesController : BaseApiController - { - private ILanguageService _languageService; - private readonly IDTOHelper _dtoHelper; - - public LanguagesController(IJsonFieldsSerializer jsonFieldsSerializer, - IAclService aclService, - ICustomerService customerService, - IStoreMappingService storeMappingService, - IStoreService storeService, - IDiscountService discountService, - ICustomerActivityService customerActivityService, - ILocalizationService localizationService, - IPictureService pictureService, - ILanguageService languageService, - IDTOHelper dtoHelper) - : base(jsonFieldsSerializer, - aclService, - customerService, - storeMappingService, - storeService, - discountService, - customerActivityService, - localizationService, - pictureService) - { - _languageService = languageService; - _dtoHelper = dtoHelper; - } - - /// - /// Retrieve all languages - /// - /// Fields from the language you want your json to contain - /// OK - /// Unauthorized - [HttpGet] - [Route("/api/languages")] - [ProducesResponseType(typeof(LanguagesRootObject), (int)HttpStatusCode.OK)] - [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] - [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] - [GetRequestsErrorInterceptorActionFilter] - public IActionResult GetAllLanguages(string fields = "") - { - IList allLanguages = _languageService.GetAllLanguages(); - - IList languagesAsDto = allLanguages.Select(language => _dtoHelper.PrepateLanguageDto(language)).ToList(); - - var languagesRootObject = new LanguagesRootObject() - { - Languages = languagesAsDto - }; - - var json = _jsonFieldsSerializer.Serialize(languagesRootObject, fields); - - return new RawJsonActionResult(json); - } - } -} +using System.Collections.Generic; +using System.Linq; +using Nop.Plugin.Api.Attributes; +using Nop.Plugin.Api.DTOs.Languages; +using Nop.Plugin.Api.Helpers; +using Nop.Plugin.Api.JSON.ActionResults; +using Nop.Services.Customers; +using Nop.Services.Discounts; +using Nop.Services.Localization; +using Nop.Services.Logging; +using Nop.Services.Media; +using Nop.Services.Security; +using Nop.Services.Stores; + +namespace Nop.Plugin.Api.Controllers +{ + using System.Net; + using Microsoft.AspNetCore.Authentication.JwtBearer; + using Microsoft.AspNetCore.Mvc; + using DTOs.Errors; + using JSON.Serializers; + + [ApiAuthorize(Policy = JwtBearerDefaults.AuthenticationScheme, AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] + public class LanguagesController : BaseApiController + { + private ILanguageService _languageService; + private readonly IDTOHelper _dtoHelper; + + public LanguagesController(IJsonFieldsSerializer jsonFieldsSerializer, + IAclService aclService, + ICustomerService customerService, + IStoreMappingService storeMappingService, + IStoreService storeService, + IDiscountService discountService, + ICustomerActivityService customerActivityService, + ILocalizationService localizationService, + IPictureService pictureService, + ILanguageService languageService, + IDTOHelper dtoHelper) + : base(jsonFieldsSerializer, + aclService, + customerService, + storeMappingService, + storeService, + discountService, + customerActivityService, + localizationService, + pictureService) + { + _languageService = languageService; + _dtoHelper = dtoHelper; + } + + /// + /// Retrieve all languages + /// + /// Fields from the language you want your json to contain + /// OK + /// Unauthorized + [HttpGet] + [Route("/api/languages")] + [ProducesResponseType(typeof(LanguagesRootObject), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] + [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] + [GetRequestsErrorInterceptorActionFilter] + public IActionResult GetAllLanguages(string fields = "") + { + var allLanguages = _languageService.GetAllLanguages(); + + IList languagesAsDto = allLanguages.Select(language => _dtoHelper.PrepateLanguageDto(language)).ToList(); + + var languagesRootObject = new LanguagesRootObject() + { + Languages = languagesAsDto + }; + + var json = JsonFieldsSerializer.Serialize(languagesRootObject, fields); + + return new RawJsonActionResult(json); + } + } +} diff --git a/Nop.Plugin.Api/Controllers/ManufacturersController.cs b/Nop.Plugin.Api/Controllers/ManufacturersController.cs new file mode 100644 index 0000000..d552b00 --- /dev/null +++ b/Nop.Plugin.Api/Controllers/ManufacturersController.cs @@ -0,0 +1,385 @@ +using Microsoft.AspNetCore.Authentication.JwtBearer; +using Microsoft.AspNetCore.Mvc; +using Nop.Core.Domain.Catalog; +using Nop.Core.Domain.Discounts; +using Nop.Core.Domain.Media; +using Nop.Plugin.Api.Attributes; +using Nop.Plugin.Api.Constants; +using Nop.Plugin.Api.Delta; +using Nop.Plugin.Api.DTOs.Errors; +using Nop.Plugin.Api.DTOs.Images; +using Nop.Plugin.Api.DTOs.Manufacturers; +using Nop.Plugin.Api.Factories; +using Nop.Plugin.Api.Helpers; +using Nop.Plugin.Api.JSON.ActionResults; +using Nop.Plugin.Api.JSON.Serializers; +using Nop.Plugin.Api.ModelBinders; +using Nop.Plugin.Api.Models.ManufacturersParameters; +using Nop.Plugin.Api.Services; +using Nop.Services.Catalog; +using Nop.Services.Customers; +using Nop.Services.Discounts; +using Nop.Services.Localization; +using Nop.Services.Logging; +using Nop.Services.Media; +using Nop.Services.Security; +using Nop.Services.Seo; +using Nop.Services.Stores; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; + +namespace Nop.Plugin.Api.Controllers +{ + [ApiAuthorize(Policy = JwtBearerDefaults.AuthenticationScheme, AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] + public class ManufacturersController : BaseApiController + { + private readonly IFactory _factory; + private readonly IDTOHelper _dtoHelper; + private readonly IManufacturerService _manufacturerService; + private readonly IManufacturerApiService _manufacturerApiService; + private readonly IUrlRecordService _urlRecordService; + + public ManufacturersController(IJsonFieldsSerializer jsonFieldsSerializer, + IAclService aclService, + ICustomerService customerService, + IStoreMappingService storeMappingService, + IStoreService storeService, + IDiscountService discountService, + ICustomerActivityService customerActivityService, + ILocalizationService localizationService, + IPictureService pictureService, + IDTOHelper dtoHelper, + IManufacturerService manufacturerService, + IManufacturerApiService manufacturerApiService, + IUrlRecordService urlRecordService, + IFactory factory) + : base(jsonFieldsSerializer, aclService, customerService, storeMappingService, storeService, discountService, customerActivityService, localizationService, pictureService) + { + _dtoHelper = dtoHelper; + _manufacturerService = manufacturerService; + _manufacturerApiService = manufacturerApiService; + _urlRecordService = urlRecordService; + _factory = factory; + } + + /// + /// Receive a list of all manufacturers + /// + /// OK + /// Bad Request + /// Unauthorized + [HttpGet] + [Route("/api/manufacturers")] + [ProducesResponseType(typeof(ManufacturersRootObject), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] + [GetRequestsErrorInterceptorActionFilter] + public IActionResult GetManufacturers(ManufacturersParametersModel parameters) + { + if (parameters.Limit < Configurations.MinLimit || parameters.Limit > Configurations.MaxLimit) + { + return Error(HttpStatusCode.BadRequest, "limit", "Invalid limit parameter"); + } + + if (parameters.Page < Configurations.DefaultPageValue) + { + return Error(HttpStatusCode.BadRequest, "page", "Invalid page parameter"); + } + + var allManufacturers = _manufacturerApiService.GetManufacturers(parameters.Ids, parameters.CreatedAtMin, parameters.CreatedAtMax, + parameters.UpdatedAtMin, parameters.UpdatedAtMax, + parameters.Limit, parameters.Page, parameters.SinceId, + parameters.ProductId, parameters.PublishedStatus, parameters.LanguageId) + .Where(c => StoreMappingService.Authorize(c)); + + IList manufacturersAsDtos = allManufacturers.Select(manufacturer => _dtoHelper.PrepareManufacturerDto(manufacturer)).ToList(); + + var manufacturersRootObject = new ManufacturersRootObject() + { + Manufacturers = manufacturersAsDtos + }; + + var json = JsonFieldsSerializer.Serialize(manufacturersRootObject, parameters.Fields); + + return new RawJsonActionResult(json); + } + + /// + /// Receive a count of all Manufacturers + /// + /// OK + /// Unauthorized + [HttpGet] + [Route("/api/manufacturers/count")] + [ProducesResponseType(typeof(ManufacturersCountRootObject), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] + [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] + [GetRequestsErrorInterceptorActionFilter] + public IActionResult GetManufacturersCount(ManufacturersCountParametersModel parameters) + { + var allManufacturersCount = _manufacturerApiService.GetManufacturersCount(parameters.CreatedAtMin, parameters.CreatedAtMax, + parameters.UpdatedAtMin, parameters.UpdatedAtMax, + parameters.PublishedStatus, parameters.ProductId); + + var manufacturersCountRootObject = new ManufacturersCountRootObject() + { + Count = allManufacturersCount + }; + + return Ok(manufacturersCountRootObject); + } + + /// + /// Retrieve manufacturer by spcified id + /// + /// Id of the manufacturer + /// Fields from the manufacturer you want your json to contain + /// OK + /// Not Found + /// Unauthorized + [HttpGet] + [Route("/api/manufacturers/{id}")] + [ProducesResponseType(typeof(ManufacturersRootObject), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] + [GetRequestsErrorInterceptorActionFilter] + public IActionResult GetManufacturerById(int id, string fields = "") + { + if (id <= 0) + { + return Error(HttpStatusCode.BadRequest, "id", "invalid id"); + } + + var manufacturer = _manufacturerApiService.GetManufacturerById(id); + + if (manufacturer == null) + { + return Error(HttpStatusCode.NotFound, "manufacturer", "manufacturer not found"); + } + + var manufacturerDto = _dtoHelper.PrepareManufacturerDto(manufacturer); + + var manufacturersRootObject = new ManufacturersRootObject(); + + manufacturersRootObject.Manufacturers.Add(manufacturerDto); + + var json = JsonFieldsSerializer.Serialize(manufacturersRootObject, fields); + + return new RawJsonActionResult(json); + } + + [HttpPost] + [Route("/api/manufacturers")] + [ProducesResponseType(typeof(ManufacturersRootObject), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(ErrorsRootObject), 422)] + [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] + public IActionResult CreateManufacturer([ModelBinder(typeof(JsonModelBinder))] Delta manufacturerDelta) + { + // Here we display the errors if the validation has failed at some point. + if (!ModelState.IsValid) + { + return Error(); + } + + //If the validation has passed the manufacturerDelta object won't be null for sure so we don't need to check for this. + + Picture insertedPicture = null; + + // We need to insert the picture before the manufacturer so we can obtain the picture id and map it to the manufacturer. + if (manufacturerDelta.Dto.Image != null && manufacturerDelta.Dto.Image.Binary != null) + { + insertedPicture = PictureService.InsertPicture(manufacturerDelta.Dto.Image.Binary, manufacturerDelta.Dto.Image.MimeType, string.Empty); + } + + // Inserting the new manufacturer + var manufacturer = _factory.Initialize(); + manufacturerDelta.Merge(manufacturer); + + if (insertedPicture != null) + { + manufacturer.PictureId = insertedPicture.Id; + } + + _manufacturerService.InsertManufacturer(manufacturer); + + + UpdateAclRoles(manufacturer, manufacturerDelta.Dto.RoleIds); + + UpdateDiscounts(manufacturer, manufacturerDelta.Dto.DiscountIds); + + UpdateStoreMappings(manufacturer, manufacturerDelta.Dto.StoreIds); + + //search engine name + if (manufacturerDelta.Dto.SeName != null) + { + var seName = _urlRecordService.ValidateSeName(manufacturer, manufacturerDelta.Dto.SeName, manufacturer.Name, true); + _urlRecordService.SaveSlug(manufacturer, seName, 0); + } + + CustomerActivityService.InsertActivity("AddNewManufacturer", + LocalizationService.GetResource("ActivityLog.AddNewManufacturer"), manufacturer); + + // Preparing the result dto of the new manufacturer + var newManufacturerDto = _dtoHelper.PrepareManufacturerDto(manufacturer); + + var manufacturersRootObject = new ManufacturersRootObject(); + + manufacturersRootObject.Manufacturers.Add(newManufacturerDto); + + var json = JsonFieldsSerializer.Serialize(manufacturersRootObject, string.Empty); + + return new RawJsonActionResult(json); + } + + [HttpPut] + [Route("/api/manufacturers/{id}")] + [ProducesResponseType(typeof(ManufacturersRootObject), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(ErrorsRootObject), 422)] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.NotFound)] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] + [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] + public IActionResult UpdateManufacturer([ModelBinder(typeof(JsonModelBinder))] Delta manufacturerDelta) + { + // Here we display the errors if the validation has failed at some point. + if (!ModelState.IsValid) + { + return Error(); + } + + var manufacturer = _manufacturerApiService.GetManufacturerById(manufacturerDelta.Dto.Id); + + if (manufacturer == null) + { + return Error(HttpStatusCode.NotFound, "manufacturer", "manufacturer not found"); + } + + manufacturerDelta.Merge(manufacturer); + + manufacturer.UpdatedOnUtc = DateTime.UtcNow; + + _manufacturerService.UpdateManufacturer(manufacturer); + + UpdatePicture(manufacturer, manufacturerDelta.Dto.Image); + + UpdateAclRoles(manufacturer, manufacturerDelta.Dto.RoleIds); + + UpdateDiscounts(manufacturer, manufacturerDelta.Dto.DiscountIds); + + UpdateStoreMappings(manufacturer, manufacturerDelta.Dto.StoreIds); + + //search engine name + if (manufacturerDelta.Dto.SeName != null) + { + + var seName = _urlRecordService.ValidateSeName(manufacturer, manufacturerDelta.Dto.SeName, manufacturer.Name, true); + _urlRecordService.SaveSlug(manufacturer, seName, 0); + } + + _manufacturerService.UpdateManufacturer(manufacturer); + + CustomerActivityService.InsertActivity("UpdateManufacturer", + LocalizationService.GetResource("ActivityLog.UpdateManufacturer"), manufacturer); + + var manufacturerDto = _dtoHelper.PrepareManufacturerDto(manufacturer); + + var manufacturersRootObject = new ManufacturersRootObject(); + + manufacturersRootObject.Manufacturers.Add(manufacturerDto); + + var json = JsonFieldsSerializer.Serialize(manufacturersRootObject, string.Empty); + + return new RawJsonActionResult(json); + } + + [HttpDelete] + [Route("/api/manufacturers/{id}")] + [ProducesResponseType(typeof(void), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.NotFound)] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] + [GetRequestsErrorInterceptorActionFilter] + public IActionResult DeleteManufacturer(int id) + { + if (id <= 0) + { + return Error(HttpStatusCode.BadRequest, "id", "invalid id"); + } + + var manufacturerToDelete = _manufacturerApiService.GetManufacturerById(id); + + if (manufacturerToDelete == null) + { + return Error(HttpStatusCode.NotFound, "manufacturer", "manufacturer not found"); + } + + _manufacturerService.DeleteManufacturer(manufacturerToDelete); + + //activity log + CustomerActivityService.InsertActivity("DeleteManufacturer", LocalizationService.GetResource("ActivityLog.DeleteManufacturer"), manufacturerToDelete); + + return new RawJsonActionResult("{}"); + } + + private void UpdatePicture(Manufacturer manufacturerEntityToUpdate, ImageDto imageDto) + { + // no image specified then do nothing + if (imageDto == null) + return; + + Picture updatedPicture; + var currentManufacturerPicture = PictureService.GetPictureById(manufacturerEntityToUpdate.PictureId); + + // when there is a picture set for the manufacturer + if (currentManufacturerPicture != null) + { + PictureService.DeletePicture(currentManufacturerPicture); + + // When the image attachment is null or empty. + if (imageDto.Binary == null) + { + manufacturerEntityToUpdate.PictureId = 0; + } + else + { + updatedPicture = PictureService.InsertPicture(imageDto.Binary, imageDto.MimeType, string.Empty); + manufacturerEntityToUpdate.PictureId = updatedPicture.Id; + } + } + // when there isn't a picture set for the manufacturer + else + { + if (imageDto.Binary != null) + { + updatedPicture = PictureService.InsertPicture(imageDto.Binary, imageDto.MimeType, string.Empty); + manufacturerEntityToUpdate.PictureId = updatedPicture.Id; + } + } + } + + private void UpdateDiscounts(Manufacturer manufacturer, List passedDiscountIds) + { + if (passedDiscountIds == null) + return; + + var allDiscounts = DiscountService.GetAllDiscounts(DiscountType.AssignedToManufacturers, showHidden: true); + foreach (var discount in allDiscounts) + { + if (passedDiscountIds.Contains(discount.Id)) + { + //new discount + if (manufacturer.AppliedDiscounts.Count(d => d.Id == discount.Id) == 0) + manufacturer.AppliedDiscounts.Add(discount); + } + else + { + //remove discount + if (manufacturer.AppliedDiscounts.Count(d => d.Id == discount.Id) > 0) + manufacturer.AppliedDiscounts.Remove(discount); + } + } + _manufacturerService.UpdateManufacturer(manufacturer); + } + } +} diff --git a/Nop.Plugin.Api/Controllers/NewsLetterSubscriptionController.cs b/Nop.Plugin.Api/Controllers/NewsLetterSubscriptionController.cs index 97bc0dd..14fff2f 100644 --- a/Nop.Plugin.Api/Controllers/NewsLetterSubscriptionController.cs +++ b/Nop.Plugin.Api/Controllers/NewsLetterSubscriptionController.cs @@ -1,127 +1,122 @@ -using System.Collections.Generic; -using System.Linq; -using System.Net; -using Microsoft.AspNetCore.Authentication.JwtBearer; -using Microsoft.AspNetCore.Mvc; -using Nop.Core; -using Nop.Core.Domain.Messages; -using Nop.Plugin.Api.Attributes; -using Nop.Plugin.Api.Constants; -using Nop.Plugin.Api.Delta; -using Nop.Plugin.Api.DTOs.Categories; -using Nop.Plugin.Api.DTOs.Errors; -using Nop.Plugin.Api.Factories; -using Nop.Plugin.Api.JSON.ActionResults; -using Nop.Plugin.Api.JSON.Serializers; -using Nop.Plugin.Api.MappingExtensions; -using Nop.Plugin.Api.ModelBinders; -using Nop.Plugin.Api.Models.CustomersParameters; -using Nop.Plugin.Api.Services; -using Nop.Services.Customers; -using Nop.Services.Discounts; -using Nop.Services.Localization; -using Nop.Services.Logging; -using Nop.Services.Media; -using Nop.Services.Messages; -using Nop.Services.Security; -using Nop.Services.Stores; - -namespace Nop.Plugin.Api.Controllers -{ - [ApiAuthorize(Policy = JwtBearerDefaults.AuthenticationScheme, AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] - public class NewsLetterSubscriptionController : BaseApiController - { - private readonly INewsLetterSubscriptionApiService _newsLetterSubscriptionApiService; - private readonly IStoreContext _storeContext; - private readonly INewsLetterSubscriptionService _newsLetterSubscriptionService; - - public NewsLetterSubscriptionController(IJsonFieldsSerializer jsonFieldsSerializer, - IAclService aclService, - ICustomerService customerService, - IStoreMappingService storeMappingService, - IStoreService storeService, - IDiscountService discountService, - ICustomerActivityService customerActivityService, - ILocalizationService localizationService, - IPictureService pictureService, - INewsLetterSubscriptionApiService newsLetterSubscriptionApiService, - IStoreContext storeContext, - INewsLetterSubscriptionService newsLetterSubscriptionService) : base(jsonFieldsSerializer, aclService, customerService, storeMappingService, storeService, discountService, customerActivityService, localizationService, pictureService) - { - _newsLetterSubscriptionApiService = newsLetterSubscriptionApiService; - _storeContext = storeContext; - _newsLetterSubscriptionService = newsLetterSubscriptionService; - } - - /// - /// Receive a list of all NewsLetters - /// - /// OK - /// Bad Request - /// Unauthorized - [HttpGet] - [Route("/api/news_letter_subscriptions")] - [ProducesResponseType(typeof(NewsLetterSubscriptionsRootObject), (int)HttpStatusCode.OK)] - [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] - [GetRequestsErrorInterceptorActionFilter] - public IActionResult GetNewsLetterSubscriptions(NewsLetterSubscriptionsParametersModel parameters) - { - if (parameters.Limit < Configurations.MinLimit || parameters.Limit > Configurations.MaxLimit) - { - return Error(HttpStatusCode.BadRequest, "limit", "Invalid limit parameter"); - } - - if (parameters.Page < Configurations.DefaultPageValue) - { - return Error(HttpStatusCode.BadRequest, "page", "Invalid page parameter"); - } - - var newsLetterSubscriptions = _newsLetterSubscriptionApiService.GetNewsLetterSubscriptions(parameters.CreatedAtMin, parameters.CreatedAtMax, - parameters.Limit, parameters.Page, parameters.SinceId, - parameters.OnlyActive); - - List newsLetterSubscriptionsDtos = newsLetterSubscriptions.Select(nls => nls.ToDto()).ToList(); - - var newsLetterSubscriptionsRootObject = new NewsLetterSubscriptionsRootObject() - { - NewsLetterSubscriptions = newsLetterSubscriptionsDtos - }; - - var json = _jsonFieldsSerializer.Serialize(newsLetterSubscriptionsRootObject, parameters.Fields); - - return new RawJsonActionResult(json); - } - - /// - /// Deactivate a NewsLetter subscriber by email - /// - /// OK - /// Bad Request - /// Unauthorized - [HttpPost] - [Route("/api/news_letter_subscriptions/{email}/deactivate")] - [ProducesResponseType(typeof(string), (int)HttpStatusCode.OK)] - [ProducesResponseType(typeof(ErrorsRootObject), 422)] - [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] - public IActionResult DeactivateNewsLetterSubscription(string email) - { - if (string.IsNullOrEmpty(email)) - { - return Error(HttpStatusCode.BadRequest, "The email parameter could not be empty."); - } - - var existingSubscription = _newsLetterSubscriptionService.GetNewsLetterSubscriptionByEmailAndStoreId(email, _storeContext.CurrentStore.Id); - - if (existingSubscription == null) - { - return Error(HttpStatusCode.BadRequest, "There is no news letter subscription with the specified email."); - } - - existingSubscription.Active = false; - - _newsLetterSubscriptionService.UpdateNewsLetterSubscription(existingSubscription); - - return Ok(); - } - } -} +using System.Linq; +using System.Net; +using Microsoft.AspNetCore.Authentication.JwtBearer; +using Microsoft.AspNetCore.Mvc; +using Nop.Core; +using Nop.Plugin.Api.Attributes; +using Nop.Plugin.Api.Constants; +using Nop.Plugin.Api.DTOs.Categories; +using Nop.Plugin.Api.DTOs.Errors; +using Nop.Plugin.Api.JSON.ActionResults; +using Nop.Plugin.Api.JSON.Serializers; +using Nop.Plugin.Api.MappingExtensions; +using Nop.Plugin.Api.Models.CustomersParameters; +using Nop.Plugin.Api.Services; +using Nop.Services.Customers; +using Nop.Services.Discounts; +using Nop.Services.Localization; +using Nop.Services.Logging; +using Nop.Services.Media; +using Nop.Services.Messages; +using Nop.Services.Security; +using Nop.Services.Stores; + +namespace Nop.Plugin.Api.Controllers +{ + [ApiAuthorize(Policy = JwtBearerDefaults.AuthenticationScheme, AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] + public class NewsLetterSubscriptionController : BaseApiController + { + private readonly INewsLetterSubscriptionApiService _newsLetterSubscriptionApiService; + private readonly IStoreContext _storeContext; + private readonly INewsLetterSubscriptionService _newsLetterSubscriptionService; + + public NewsLetterSubscriptionController(IJsonFieldsSerializer jsonFieldsSerializer, + IAclService aclService, + ICustomerService customerService, + IStoreMappingService storeMappingService, + IStoreService storeService, + IDiscountService discountService, + ICustomerActivityService customerActivityService, + ILocalizationService localizationService, + IPictureService pictureService, + INewsLetterSubscriptionApiService newsLetterSubscriptionApiService, + IStoreContext storeContext, + INewsLetterSubscriptionService newsLetterSubscriptionService) : base(jsonFieldsSerializer, aclService, customerService, storeMappingService, storeService, discountService, customerActivityService, localizationService, pictureService) + { + _newsLetterSubscriptionApiService = newsLetterSubscriptionApiService; + _storeContext = storeContext; + _newsLetterSubscriptionService = newsLetterSubscriptionService; + } + + /// + /// Receive a list of all NewsLetters + /// + /// OK + /// Bad Request + /// Unauthorized + [HttpGet] + [Route("/api/news_letter_subscriptions")] + [ProducesResponseType(typeof(NewsLetterSubscriptionsRootObject), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] + [GetRequestsErrorInterceptorActionFilter] + public IActionResult GetNewsLetterSubscriptions(NewsLetterSubscriptionsParametersModel parameters) + { + if (parameters.Limit < Configurations.MinLimit || parameters.Limit > Configurations.MaxLimit) + { + return Error(HttpStatusCode.BadRequest, "limit", "Invalid limit parameter"); + } + + if (parameters.Page < Configurations.DefaultPageValue) + { + return Error(HttpStatusCode.BadRequest, "page", "Invalid page parameter"); + } + + var newsLetterSubscriptions = _newsLetterSubscriptionApiService.GetNewsLetterSubscriptions(parameters.CreatedAtMin, parameters.CreatedAtMax, + parameters.Limit, parameters.Page, parameters.SinceId, + parameters.OnlyActive); + + var newsLetterSubscriptionsDtos = newsLetterSubscriptions.Select(nls => nls.ToDto()).ToList(); + + var newsLetterSubscriptionsRootObject = new NewsLetterSubscriptionsRootObject() + { + NewsLetterSubscriptions = newsLetterSubscriptionsDtos + }; + + var json = JsonFieldsSerializer.Serialize(newsLetterSubscriptionsRootObject, parameters.Fields); + + return new RawJsonActionResult(json); + } + + /// + /// Deactivate a NewsLetter subscriber by email + /// + /// OK + /// Bad Request + /// Unauthorized + [HttpPost] + [Route("/api/news_letter_subscriptions/{email}/deactivate")] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(ErrorsRootObject), 422)] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] + public IActionResult DeactivateNewsLetterSubscription(string email) + { + if (string.IsNullOrEmpty(email)) + { + return Error(HttpStatusCode.BadRequest, "The email parameter could not be empty."); + } + + var existingSubscription = _newsLetterSubscriptionService.GetNewsLetterSubscriptionByEmailAndStoreId(email, _storeContext.CurrentStore.Id); + + if (existingSubscription == null) + { + return Error(HttpStatusCode.BadRequest, "There is no news letter subscription with the specified email."); + } + + existingSubscription.Active = false; + + _newsLetterSubscriptionService.UpdateNewsLetterSubscription(existingSubscription); + + return Ok(); + } + } +} diff --git a/Nop.Plugin.Api/Controllers/OrderItemsController.cs b/Nop.Plugin.Api/Controllers/OrderItemsController.cs index 8c35e95..e4b42b4 100644 --- a/Nop.Plugin.Api/Controllers/OrderItemsController.cs +++ b/Nop.Plugin.Api/Controllers/OrderItemsController.cs @@ -1,394 +1,400 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net; -using Nop.Core.Domain.Catalog; -using Nop.Core.Domain.Orders; -using Nop.Plugin.Api.Attributes; -using Nop.Plugin.Api.Constants; -using Nop.Plugin.Api.Delta; -using Nop.Plugin.Api.DTOs.OrderItems; -using Nop.Plugin.Api.Helpers; -using Nop.Plugin.Api.JSON.ActionResults; -using Nop.Plugin.Api.MappingExtensions; -using Nop.Plugin.Api.ModelBinders; -using Nop.Plugin.Api.Models.OrderItemsParameters; -using Nop.Plugin.Api.Services; -using Nop.Services.Catalog; -using Nop.Services.Customers; -using Nop.Services.Discounts; -using Nop.Services.Localization; -using Nop.Services.Logging; -using Nop.Services.Media; -using Nop.Services.Orders; -using Nop.Services.Security; -using Nop.Services.Stores; -using Nop.Services.Tax; - -namespace Nop.Plugin.Api.Controllers -{ - using Microsoft.AspNetCore.Authentication.JwtBearer; - using Microsoft.AspNetCore.Mvc; - using Nop.Plugin.Api.DTOs.Errors; - using Nop.Plugin.Api.JSON.Serializers; - - [ApiAuthorize(Policy = JwtBearerDefaults.AuthenticationScheme, AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] - public class OrderItemsController : BaseApiController - { - private readonly IOrderItemApiService _orderItemApiService; - private readonly IOrderApiService _orderApiService; - private readonly IOrderService _orderService; - private readonly IProductApiService _productApiService; - private readonly IPriceCalculationService _priceCalculationService; - private readonly ITaxService _taxService; - private readonly IDTOHelper _dtoHelper; - private readonly IProductAttributeConverter _productAttributeConverter; - - public OrderItemsController(IJsonFieldsSerializer jsonFieldsSerializer, - IAclService aclService, - ICustomerService customerService, - IStoreMappingService storeMappingService, - IStoreService storeService, - IDiscountService discountService, - ICustomerActivityService customerActivityService, - ILocalizationService localizationService, - IOrderItemApiService orderItemApiService, - IOrderApiService orderApiService, - IOrderService orderService, - IProductApiService productApiService, - IPriceCalculationService priceCalculationService, - ITaxService taxService, - IPictureService pictureService, IDTOHelper dtoHelper) - : base(jsonFieldsSerializer, - aclService, - customerService, - storeMappingService, - storeService, - discountService, - customerActivityService, - localizationService, - pictureService) - { - _orderItemApiService = orderItemApiService; - _orderApiService = orderApiService; - _orderService = orderService; - _productApiService = productApiService; - _priceCalculationService = priceCalculationService; - _taxService = taxService; - _dtoHelper = dtoHelper; - } - - [HttpGet] - [Route("/api/orders/{orderId}/items")] - [ProducesResponseType(typeof(OrderItemsRootObject), (int)HttpStatusCode.OK)] - [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] - [ProducesResponseType(typeof(string), (int)HttpStatusCode.NotFound)] - [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] - [GetRequestsErrorInterceptorActionFilter] - public IActionResult GetOrderItems(int orderId, OrderItemsParametersModel parameters) - { - if (parameters.Limit < Configurations.MinLimit || parameters.Limit > Configurations.MaxLimit) - { - return Error(HttpStatusCode.BadRequest, "limit", "Invalid limit parameter"); - } - - if (parameters.Page < Configurations.DefaultPageValue) - { - return Error(HttpStatusCode.BadRequest, "page", "Invalid request parameters"); - } - - Order order = _orderApiService.GetOrderById(orderId); - - if (order == null) - { - return Error(HttpStatusCode.NotFound, "order", "not found"); - } - - IList allOrderItemsForOrder = _orderItemApiService.GetOrderItemsForOrder(order, parameters.Limit, parameters.Page, parameters.SinceId); - - var orderItemsRootObject = new OrderItemsRootObject() - { - OrderItems = allOrderItemsForOrder.Select(item => _dtoHelper.PrepareOrderItemDTO(item)).ToList() - }; - - var json = _jsonFieldsSerializer.Serialize(orderItemsRootObject, parameters.Fields); - - return new RawJsonActionResult(json); - } - - [HttpGet] - [Route("/api/orders/{orderId}/items/count")] - [ProducesResponseType(typeof(OrderItemsCountRootObject), (int)HttpStatusCode.OK)] - [ProducesResponseType(typeof(string), (int)HttpStatusCode.NotFound)] - [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] - [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] - [GetRequestsErrorInterceptorActionFilter] - public IActionResult GetOrderItemsCount(int orderId) - { - Order order = _orderApiService.GetOrderById(orderId); - - if (order == null) - { - return Error(HttpStatusCode.NotFound, "order", "not found"); - } - - int orderItemsCountForOrder = _orderItemApiService.GetOrderItemsCount(order); - - var orderItemsCountRootObject = new OrderItemsCountRootObject() - { - Count = orderItemsCountForOrder - }; - - return Ok(orderItemsCountRootObject); - } - - [HttpGet] - [Route("/api/orders/{orderId}/items/{orderItemId}")] - [ProducesResponseType(typeof(OrderItemsRootObject), (int)HttpStatusCode.OK)] - [ProducesResponseType(typeof(string), (int)HttpStatusCode.NotFound)] - [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] - [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] - [GetRequestsErrorInterceptorActionFilter] - public IActionResult GetOrderItemByIdForOrder(int orderId, int orderItemId, string fields = "") - { - Order order = _orderApiService.GetOrderById(orderId); - - if (order == null) - { - return Error(HttpStatusCode.NotFound, "order", "not found"); - } - - OrderItem orderItem = _orderService.GetOrderItemById(orderItemId); - - if (orderItem == null) - { - return Error(HttpStatusCode.NotFound, "order_item", "not found"); - } - - var orderItemDtos = new List(); - orderItemDtos.Add(_dtoHelper.PrepareOrderItemDTO(orderItem)); - - var orderItemsRootObject = new OrderItemsRootObject() - { - OrderItems = orderItemDtos - }; - - var json = _jsonFieldsSerializer.Serialize(orderItemsRootObject, fields); - - return new RawJsonActionResult(json); - } - - [HttpPost] - [Route("/api/orders/{orderId}/items")] - [ProducesResponseType(typeof(OrderItemsRootObject), (int)HttpStatusCode.OK)] - [ProducesResponseType(typeof(ErrorsRootObject), 422)] - [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] - [ProducesResponseType(typeof(string), (int)HttpStatusCode.NotFound)] - [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] - public IActionResult CreateOrderItem(int orderId, - [ModelBinder(typeof (JsonModelBinder))] Delta orderItemDelta) - { - // Here we display the errors if the validation has failed at some point. - if (!ModelState.IsValid) - { - return Error(); - } - - Order order = _orderApiService.GetOrderById(orderId); - - if (order == null) - { - return Error(HttpStatusCode.NotFound, "order", "not found"); - } - - Product product = GetProduct(orderItemDelta.Dto.ProductId); - - if (product == null) - { - return Error(HttpStatusCode.NotFound, "product", "not found"); - } - - if (product.IsRental) - { - if (orderItemDelta.Dto.RentalStartDateUtc == null) - { - return Error(HttpStatusCode.BadRequest, "rental_start_date_utc", "required"); - } - - if (orderItemDelta.Dto.RentalEndDateUtc == null) - { - return Error(HttpStatusCode.BadRequest, "rental_end_date_utc", "required"); - } - - if (orderItemDelta.Dto.RentalStartDateUtc > orderItemDelta.Dto.RentalEndDateUtc) - { - return Error(HttpStatusCode.BadRequest, "rental_start_date_utc", "should be before rental_end_date_utc"); - } - - if (orderItemDelta.Dto.RentalStartDateUtc < DateTime.UtcNow) - { - return Error(HttpStatusCode.BadRequest, "rental_start_date_utc", "should be a future date"); - } - } - - OrderItem newOrderItem = PrepareDefaultOrderItemFromProduct(order, product); - orderItemDelta.Merge(newOrderItem); - - order.OrderItems.Add(newOrderItem); - - _orderService.UpdateOrder(order); - - _customerActivityService.InsertActivity("AddNewOrderItem", - _localizationService.GetResource("ActivityLog.AddNewOrderItem"), newOrderItem.Id); - - var orderItemsRootObject = new OrderItemsRootObject(); - - orderItemsRootObject.OrderItems.Add(newOrderItem.ToDto()); - - var json = _jsonFieldsSerializer.Serialize(orderItemsRootObject, string.Empty); - - return new RawJsonActionResult(json); - } - - [HttpPut] - [Route("/api/orders/{orderId}/items/{orderItemId}")] - [ProducesResponseType(typeof(OrderItemsRootObject), (int)HttpStatusCode.OK)] - [ProducesResponseType(typeof(ErrorsRootObject), 422)] - [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] - [ProducesResponseType(typeof(string), (int)HttpStatusCode.NotFound)] - [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] - public IActionResult UpdateOrderItem(int orderId, int orderItemId, - [ModelBinder(typeof(JsonModelBinder))] Delta orderItemDelta) - { - // Here we display the errors if the validation has failed at some point. - if (!ModelState.IsValid) - { - return Error(); - } - - OrderItem orderItemToUpdate = _orderService.GetOrderItemById(orderItemId); - - if (orderItemToUpdate == null) - { - return Error(HttpStatusCode.NotFound, "order_item", "not found"); - } - - Order order = _orderApiService.GetOrderById(orderId); - - if (order == null) - { - return Error(HttpStatusCode.NotFound, "order", "not found"); - } - - // This is needed because those fields shouldn't be updatable. That is why we save them and after the merge set them back. - int? productId = orderItemToUpdate.ProductId; - DateTime? rentalStartDate = orderItemToUpdate.RentalStartDateUtc; - DateTime? rentalEndDate = orderItemToUpdate.RentalEndDateUtc; - - orderItemDelta.Merge(orderItemToUpdate); - - orderItemToUpdate.ProductId = productId ?? 0; - orderItemToUpdate.RentalStartDateUtc = rentalStartDate; - orderItemToUpdate.RentalEndDateUtc = rentalEndDate; - - _orderService.UpdateOrder(order); - - _customerActivityService.InsertActivity("UpdateOrderItem", - _localizationService.GetResource("ActivityLog.UpdateOrderItem"), orderItemToUpdate.Id); - - var orderItemsRootObject = new OrderItemsRootObject(); - - orderItemsRootObject.OrderItems.Add(orderItemToUpdate.ToDto()); - - var json = _jsonFieldsSerializer.Serialize(orderItemsRootObject, string.Empty); - - return new RawJsonActionResult(json); - } - - [HttpDelete] - [Route("/api/orders/{orderId}/items/{orderItemId}")] - [ProducesResponseType(typeof(void), (int)HttpStatusCode.OK)] - [ProducesResponseType(typeof(string), (int)HttpStatusCode.NotFound)] - [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] - [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] - [GetRequestsErrorInterceptorActionFilter] - public IActionResult DeleteOrderItemById(int orderId, int orderItemId) - { - Order order = _orderApiService.GetOrderById(orderId); - - if (order == null) - { - return Error(HttpStatusCode.NotFound, "order", "not found"); - } - - OrderItem orderItem = _orderService.GetOrderItemById(orderItemId); - _orderService.DeleteOrderItem(orderItem); - - return new RawJsonActionResult("{}"); - } - - [HttpDelete] - [Route("/api/orders/{orderId}/items")] - [ProducesResponseType(typeof(void), (int)HttpStatusCode.OK)] - [ProducesResponseType(typeof(string), (int)HttpStatusCode.NotFound)] - [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] - [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] - [GetRequestsErrorInterceptorActionFilter] - public IActionResult DeleteAllOrderItemsForOrder(int orderId) - { - Order order = _orderApiService.GetOrderById(orderId); - - if (order == null) - { - return Error(HttpStatusCode.NotFound, "order", "not found"); - } - - var orderItemsList = order.OrderItems.ToList(); - - for (int i = 0; i < orderItemsList.Count; i++) - { - _orderService.DeleteOrderItem(orderItemsList[i]); - } - - return new RawJsonActionResult("{}"); - } - - private Product GetProduct(int? productId) - { - Product product = null; - - if (productId.HasValue) - { - int id = productId.Value; - - product = _productApiService.GetProductById(id); - } - return product; - } - - private OrderItem PrepareDefaultOrderItemFromProduct(Order order, Product product) - { - var presetQty = 1; - var presetPrice = _priceCalculationService.GetFinalPrice(product, order.Customer, decimal.Zero, true, presetQty); - - decimal taxRate; - decimal presetPriceInclTax = _taxService.GetProductPrice(product, presetPrice, true, order.Customer, out taxRate); - decimal presetPriceExclTax = _taxService.GetProductPrice(product, presetPrice, false, order.Customer, out taxRate); - - OrderItem orderItem = new OrderItem() - { - OrderItemGuid = new Guid(), - UnitPriceExclTax = presetPriceExclTax, - UnitPriceInclTax = presetPriceInclTax, - PriceInclTax = presetPriceInclTax, - PriceExclTax = presetPriceExclTax, - OriginalProductCost = _priceCalculationService.GetProductCost(product, null), - Quantity = presetQty, - Product = product, - Order = order - }; - - return orderItem; - } - } +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using Microsoft.AspNetCore.Authentication.JwtBearer; +using Microsoft.AspNetCore.Mvc; +using Nop.Core.Domain.Catalog; +using Nop.Core.Domain.Orders; +using Nop.Plugin.Api.Attributes; +using Nop.Plugin.Api.Constants; +using Nop.Plugin.Api.Delta; +using Nop.Plugin.Api.DTOs.Errors; +using Nop.Plugin.Api.DTOs.OrderItems; +using Nop.Plugin.Api.Helpers; +using Nop.Plugin.Api.JSON.ActionResults; +using Nop.Plugin.Api.JSON.Serializers; +using Nop.Plugin.Api.MappingExtensions; +using Nop.Plugin.Api.ModelBinders; +using Nop.Plugin.Api.Models.OrderItemsParameters; +using Nop.Plugin.Api.Services; +using Nop.Services.Catalog; +using Nop.Services.Customers; +using Nop.Services.Discounts; +using Nop.Services.Localization; +using Nop.Services.Logging; +using Nop.Services.Media; +using Nop.Services.Orders; +using Nop.Services.Security; +using Nop.Services.Stores; +using Nop.Services.Tax; + +namespace Nop.Plugin.Api.Controllers +{ + [ApiAuthorize(Policy = JwtBearerDefaults.AuthenticationScheme, + AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] + public class OrderItemsController : BaseApiController + { + private readonly IDTOHelper _dtoHelper; + private readonly IOrderApiService _orderApiService; + private readonly IOrderItemApiService _orderItemApiService; + private readonly IOrderService _orderService; + private readonly IPriceCalculationService _priceCalculationService; + private readonly IProductApiService _productApiService; + private readonly ITaxService _taxService; + + public OrderItemsController(IJsonFieldsSerializer jsonFieldsSerializer, + IAclService aclService, + ICustomerService customerService, + IStoreMappingService storeMappingService, + IStoreService storeService, + IDiscountService discountService, + ICustomerActivityService customerActivityService, + ILocalizationService localizationService, + IOrderItemApiService orderItemApiService, + IOrderApiService orderApiService, + IOrderService orderService, + IProductApiService productApiService, + IPriceCalculationService priceCalculationService, + ITaxService taxService, + IPictureService pictureService, IDTOHelper dtoHelper) + : base(jsonFieldsSerializer, + aclService, + customerService, + storeMappingService, + storeService, + discountService, + customerActivityService, + localizationService, + pictureService) + { + _orderItemApiService = orderItemApiService; + _orderApiService = orderApiService; + _orderService = orderService; + _productApiService = productApiService; + _priceCalculationService = priceCalculationService; + _taxService = taxService; + _dtoHelper = dtoHelper; + } + + [HttpGet] + [Route("/api/orders/{orderId}/items")] + [ProducesResponseType(typeof(OrderItemsRootObject), (int) HttpStatusCode.OK)] + [ProducesResponseType(typeof(ErrorsRootObject), (int) HttpStatusCode.BadRequest)] + [ProducesResponseType(typeof(string), (int) HttpStatusCode.NotFound)] + [ProducesResponseType(typeof(string), (int) HttpStatusCode.Unauthorized)] + [GetRequestsErrorInterceptorActionFilter] + public IActionResult GetOrderItems(int orderId, OrderItemsParametersModel parameters) + { + if (parameters.Limit < Configurations.MinLimit || parameters.Limit > Configurations.MaxLimit) + { + return Error(HttpStatusCode.BadRequest, "limit", "Invalid limit parameter"); + } + + if (parameters.Page < Configurations.DefaultPageValue) + { + return Error(HttpStatusCode.BadRequest, "page", "Invalid request parameters"); + } + + var order = _orderApiService.GetOrderById(orderId); + + if (order == null) + { + return Error(HttpStatusCode.NotFound, "order", "not found"); + } + + var allOrderItemsForOrder = + _orderItemApiService.GetOrderItemsForOrder(order, parameters.Limit, parameters.Page, + parameters.SinceId); + + var orderItemsRootObject = new OrderItemsRootObject + { + OrderItems = allOrderItemsForOrder.Select(item => _dtoHelper.PrepareOrderItemDTO(item)).ToList() + }; + + var json = JsonFieldsSerializer.Serialize(orderItemsRootObject, parameters.Fields); + + return new RawJsonActionResult(json); + } + + [HttpGet] + [Route("/api/orders/{orderId}/items/count")] + [ProducesResponseType(typeof(OrderItemsCountRootObject), (int) HttpStatusCode.OK)] + [ProducesResponseType(typeof(string), (int) HttpStatusCode.NotFound)] + [ProducesResponseType(typeof(string), (int) HttpStatusCode.Unauthorized)] + [ProducesResponseType(typeof(ErrorsRootObject), (int) HttpStatusCode.BadRequest)] + [GetRequestsErrorInterceptorActionFilter] + public IActionResult GetOrderItemsCount(int orderId) + { + var order = _orderApiService.GetOrderById(orderId); + + if (order == null) + { + return Error(HttpStatusCode.NotFound, "order", "not found"); + } + + var orderItemsCountForOrder = _orderItemApiService.GetOrderItemsCount(order); + + var orderItemsCountRootObject = new OrderItemsCountRootObject + { + Count = orderItemsCountForOrder + }; + + return Ok(orderItemsCountRootObject); + } + + [HttpGet] + [Route("/api/orders/{orderId}/items/{orderItemId}")] + [ProducesResponseType(typeof(OrderItemsRootObject), (int) HttpStatusCode.OK)] + [ProducesResponseType(typeof(string), (int) HttpStatusCode.NotFound)] + [ProducesResponseType(typeof(string), (int) HttpStatusCode.Unauthorized)] + [ProducesResponseType(typeof(ErrorsRootObject), (int) HttpStatusCode.BadRequest)] + [GetRequestsErrorInterceptorActionFilter] + public IActionResult GetOrderItemByIdForOrder(int orderId, int orderItemId, string fields = "") + { + var order = _orderApiService.GetOrderById(orderId); + + if (order == null) + { + return Error(HttpStatusCode.NotFound, "order", "not found"); + } + + var orderItem = _orderService.GetOrderItemById(orderItemId); + + if (orderItem == null) + { + return Error(HttpStatusCode.NotFound, "order_item", "not found"); + } + + var orderItemDtos = new List {_dtoHelper.PrepareOrderItemDTO(orderItem)}; + + var orderItemsRootObject = new OrderItemsRootObject + { + OrderItems = orderItemDtos + }; + + var json = JsonFieldsSerializer.Serialize(orderItemsRootObject, fields); + + return new RawJsonActionResult(json); + } + + [HttpPost] + [Route("/api/orders/{orderId}/items")] + [ProducesResponseType(typeof(OrderItemsRootObject), (int) HttpStatusCode.OK)] + [ProducesResponseType(typeof(ErrorsRootObject), 422)] + [ProducesResponseType(typeof(ErrorsRootObject), (int) HttpStatusCode.BadRequest)] + [ProducesResponseType(typeof(string), (int) HttpStatusCode.NotFound)] + [ProducesResponseType(typeof(string), (int) HttpStatusCode.Unauthorized)] + public IActionResult CreateOrderItem(int orderId, + [ModelBinder(typeof(JsonModelBinder))] + Delta orderItemDelta) + { + // Here we display the errors if the validation has failed at some point. + if (!ModelState.IsValid) + { + return Error(); + } + + var order = _orderApiService.GetOrderById(orderId); + + if (order == null) + { + return Error(HttpStatusCode.NotFound, "order", "not found"); + } + + var product = GetProduct(orderItemDelta.Dto.ProductId); + + if (product == null) + { + return Error(HttpStatusCode.NotFound, "product", "not found"); + } + + if (product.IsRental) + { + if (orderItemDelta.Dto.RentalStartDateUtc == null) + { + return Error(HttpStatusCode.BadRequest, "rental_start_date_utc", "required"); + } + + if (orderItemDelta.Dto.RentalEndDateUtc == null) + { + return Error(HttpStatusCode.BadRequest, "rental_end_date_utc", "required"); + } + + if (orderItemDelta.Dto.RentalStartDateUtc > orderItemDelta.Dto.RentalEndDateUtc) + { + return Error(HttpStatusCode.BadRequest, "rental_start_date_utc", + "should be before rental_end_date_utc"); + } + + if (orderItemDelta.Dto.RentalStartDateUtc < DateTime.UtcNow) + { + return Error(HttpStatusCode.BadRequest, "rental_start_date_utc", "should be a future date"); + } + } + + var newOrderItem = PrepareDefaultOrderItemFromProduct(order, product); + orderItemDelta.Merge(newOrderItem); + + order.OrderItems.Add(newOrderItem); + + _orderService.UpdateOrder(order); + + CustomerActivityService.InsertActivity("AddNewOrderItem", + LocalizationService.GetResource("ActivityLog.AddNewOrderItem"), newOrderItem); + + var orderItemsRootObject = new OrderItemsRootObject(); + + orderItemsRootObject.OrderItems.Add(newOrderItem.ToDto()); + + var json = JsonFieldsSerializer.Serialize(orderItemsRootObject, string.Empty); + + return new RawJsonActionResult(json); + } + + [HttpPut] + [Route("/api/orders/{orderId}/items/{orderItemId}")] + [ProducesResponseType(typeof(OrderItemsRootObject), (int) HttpStatusCode.OK)] + [ProducesResponseType(typeof(ErrorsRootObject), 422)] + [ProducesResponseType(typeof(ErrorsRootObject), (int) HttpStatusCode.BadRequest)] + [ProducesResponseType(typeof(string), (int) HttpStatusCode.NotFound)] + [ProducesResponseType(typeof(string), (int) HttpStatusCode.Unauthorized)] + public IActionResult UpdateOrderItem(int orderId, int orderItemId, + [ModelBinder(typeof(JsonModelBinder))] + Delta orderItemDelta) + { + // Here we display the errors if the validation has failed at some point. + if (!ModelState.IsValid) + { + return Error(); + } + + var orderItemToUpdate = _orderService.GetOrderItemById(orderItemId); + + if (orderItemToUpdate == null) + { + return Error(HttpStatusCode.NotFound, "order_item", "not found"); + } + + var order = _orderApiService.GetOrderById(orderId); + + if (order == null) + { + return Error(HttpStatusCode.NotFound, "order", "not found"); + } + + // This is needed because those fields shouldn't be updatable. That is why we save them and after the merge set them back. + int? productId = orderItemToUpdate.ProductId; + var rentalStartDate = orderItemToUpdate.RentalStartDateUtc; + var rentalEndDate = orderItemToUpdate.RentalEndDateUtc; + + orderItemDelta.Merge(orderItemToUpdate); + + orderItemToUpdate.ProductId = (int) productId; + orderItemToUpdate.RentalStartDateUtc = rentalStartDate; + orderItemToUpdate.RentalEndDateUtc = rentalEndDate; + + _orderService.UpdateOrder(order); + + CustomerActivityService.InsertActivity("UpdateOrderItem", + LocalizationService.GetResource("ActivityLog.UpdateOrderItem"), orderItemToUpdate); + + var orderItemsRootObject = new OrderItemsRootObject(); + + orderItemsRootObject.OrderItems.Add(orderItemToUpdate.ToDto()); + + var json = JsonFieldsSerializer.Serialize(orderItemsRootObject, string.Empty); + + return new RawJsonActionResult(json); + } + + [HttpDelete] + [Route("/api/orders/{orderId}/items/{orderItemId}")] + [ProducesResponseType(typeof(void), (int) HttpStatusCode.OK)] + [ProducesResponseType(typeof(string), (int) HttpStatusCode.NotFound)] + [ProducesResponseType(typeof(string), (int) HttpStatusCode.Unauthorized)] + [ProducesResponseType(typeof(ErrorsRootObject), (int) HttpStatusCode.BadRequest)] + [GetRequestsErrorInterceptorActionFilter] + public IActionResult DeleteOrderItemById(int orderId, int orderItemId) + { + var order = _orderApiService.GetOrderById(orderId); + + if (order == null) + { + return Error(HttpStatusCode.NotFound, "order", "not found"); + } + + var orderItem = _orderService.GetOrderItemById(orderItemId); + _orderService.DeleteOrderItem(orderItem); + + return new RawJsonActionResult("{}"); + } + + [HttpDelete] + [Route("/api/orders/{orderId}/items")] + [ProducesResponseType(typeof(void), (int) HttpStatusCode.OK)] + [ProducesResponseType(typeof(string), (int) HttpStatusCode.NotFound)] + [ProducesResponseType(typeof(string), (int) HttpStatusCode.Unauthorized)] + [ProducesResponseType(typeof(ErrorsRootObject), (int) HttpStatusCode.BadRequest)] + [GetRequestsErrorInterceptorActionFilter] + public IActionResult DeleteAllOrderItemsForOrder(int orderId) + { + var order = _orderApiService.GetOrderById(orderId); + + if (order == null) + { + return Error(HttpStatusCode.NotFound, "order", "not found"); + } + + var orderItemsList = order.OrderItems.ToList(); + + foreach (var t in orderItemsList) + { + _orderService.DeleteOrderItem(t); + } + + return new RawJsonActionResult("{}"); + } + + private Product GetProduct(int? productId) + { + Product product = null; + + if (productId.HasValue) + { + var id = productId.Value; + + product = _productApiService.GetProductById(id); + } + + return product; + } + + private OrderItem PrepareDefaultOrderItemFromProduct(Order order, Product product) + { + var presetQty = 1; + var presetPrice = + _priceCalculationService.GetFinalPrice(product, order.Customer, decimal.Zero, true, presetQty); + + var presetPriceInclTax = + _taxService.GetProductPrice(product, presetPrice, true, order.Customer, out _); + var presetPriceExclTax = + _taxService.GetProductPrice(product, presetPrice, false, order.Customer, out _); + + var orderItem = new OrderItem + { + OrderItemGuid = new Guid(), + UnitPriceExclTax = presetPriceExclTax, + UnitPriceInclTax = presetPriceInclTax, + PriceInclTax = presetPriceInclTax, + PriceExclTax = presetPriceExclTax, + OriginalProductCost = _priceCalculationService.GetProductCost(product, null), + Quantity = presetQty, + Product = product, + Order = order + }; + + return orderItem; + } + } } \ No newline at end of file diff --git a/Nop.Plugin.Api/Controllers/OrdersController.cs b/Nop.Plugin.Api/Controllers/OrdersController.cs index 041a643..0348d21 100644 --- a/Nop.Plugin.Api/Controllers/OrdersController.cs +++ b/Nop.Plugin.Api/Controllers/OrdersController.cs @@ -1,690 +1,598 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net; -using FluentValidation.Results; -using Nop.Core; -using Nop.Core.Domain.Catalog; -using Nop.Core.Domain.Customers; -using Nop.Core.Domain.Orders; -using Nop.Core.Domain.Shipping; -using Nop.Core.Infrastructure; -using Nop.Plugin.Api.Attributes; -using Nop.Plugin.Api.Constants; -using Nop.Plugin.Api.Delta; -using Nop.Plugin.Api.DTOs; -using Nop.Plugin.Api.DTOs.OrderItems; -using Nop.Plugin.Api.DTOs.Orders; -using Nop.Plugin.Api.Factories; -using Nop.Plugin.Api.Helpers; -using Nop.Plugin.Api.JSON.ActionResults; -using Nop.Plugin.Api.ModelBinders; -using Nop.Plugin.Api.Models.OrdersParameters; -using Nop.Plugin.Api.Services; -using Nop.Plugin.Api.Validators; -using Nop.Services.Catalog; -using Nop.Services.Common; -using Nop.Services.Customers; -using Nop.Services.Discounts; -using Nop.Services.Localization; -using Nop.Services.Logging; -using Nop.Services.Media; -using Nop.Services.Orders; -using Nop.Services.Payments; -using Nop.Services.Security; -using Nop.Services.Shipping; -using Nop.Services.Stores; -using Microsoft.AspNetCore.Mvc; - -namespace Nop.Plugin.Api.Controllers -{ - using Microsoft.AspNetCore.Authentication.JwtBearer; - using Nop.Plugin.Api.DTOs.Errors; - using Nop.Plugin.Api.JSON.Serializers; - - [ApiAuthorize(Policy = JwtBearerDefaults.AuthenticationScheme, AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] - public class OrdersController : BaseApiController - { - private readonly IOrderApiService _orderApiService; - private readonly IProductService _productService; - private readonly IOrderProcessingService _orderProcessingService; - private readonly IOrderService _orderService; - private readonly IShoppingCartService _shoppingCartService; - private readonly IGenericAttributeService _genericAttributeService; - private readonly IShippingService _shippingService; - private readonly IDTOHelper _dtoHelper; - private readonly IProductAttributeConverter _productAttributeConverter; - private readonly IStoreContext _storeContext; - private readonly IFactory _factory; - - // We resolve the order settings this way because of the tests. - // The auto mocking does not support concreate types as dependencies. It supports only interfaces. - private OrderSettings _orderSettings; - - private OrderSettings OrderSettings - { - get - { - if (_orderSettings == null) - { - _orderSettings = EngineContext.Current.Resolve(); - } - - return _orderSettings; - } - } - - public OrdersController(IOrderApiService orderApiService, - IJsonFieldsSerializer jsonFieldsSerializer, - IAclService aclService, - ICustomerService customerService, - IStoreMappingService storeMappingService, - IStoreService storeService, - IDiscountService discountService, - ICustomerActivityService customerActivityService, - ILocalizationService localizationService, - IProductService productService, - IFactory factory, - IOrderProcessingService orderProcessingService, - IOrderService orderService, - IShoppingCartService shoppingCartService, - IGenericAttributeService genericAttributeService, - IStoreContext storeContext, - IShippingService shippingService, - IPictureService pictureService, - IDTOHelper dtoHelper, - IProductAttributeConverter productAttributeConverter) - : base(jsonFieldsSerializer, aclService, customerService, storeMappingService, - storeService, discountService, customerActivityService, localizationService,pictureService) - { - _orderApiService = orderApiService; - _factory = factory; - _orderProcessingService = orderProcessingService; - _orderService = orderService; - _shoppingCartService = shoppingCartService; - _genericAttributeService = genericAttributeService; - _storeContext = storeContext; - _shippingService = shippingService; - _dtoHelper = dtoHelper; - _productService = productService; - _productAttributeConverter = productAttributeConverter; - } - - /// - /// Receive a list of all Orders - /// - /// OK - /// Bad Request - /// Unauthorized - [HttpGet] - [Route("/api/orders")] - [ProducesResponseType(typeof(OrdersRootObject), (int)HttpStatusCode.OK)] - [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] - [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] - [GetRequestsErrorInterceptorActionFilter] - public IActionResult GetOrders(OrdersParametersModel parameters) - { - if (parameters.Page < Configurations.DefaultPageValue) - { - return Error(HttpStatusCode.BadRequest, "page", "Invalid page parameter"); - } - - if (parameters.Limit < Configurations.MinLimit || parameters.Limit > Configurations.MaxLimit) - { - return Error(HttpStatusCode.BadRequest, "page", "Invalid limit parameter"); - } - - var storeId = _storeContext.CurrentStore.Id; - - IList orders = _orderApiService.GetOrders(parameters.Ids, parameters.CreatedAtMin, - parameters.CreatedAtMax, - parameters.Limit, parameters.Page, parameters.SinceId, - parameters.Status, parameters.PaymentStatus, parameters.ShippingStatus, - parameters.CustomerId, storeId); - - IList ordersAsDtos = orders.Select(x => _dtoHelper.PrepareOrderDTO(x)).ToList(); - - var ordersRootObject = new OrdersRootObject() - { - Orders = ordersAsDtos - }; - - var json = _jsonFieldsSerializer.Serialize(ordersRootObject, parameters.Fields); - - return new RawJsonActionResult(json); - } - - /// - /// Receive a count of all Orders - /// - /// OK - /// Unauthorized - [HttpGet] - [Route("/api/orders/count")] - [ProducesResponseType(typeof(OrdersCountRootObject), (int)HttpStatusCode.OK)] - [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] - [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] - [GetRequestsErrorInterceptorActionFilter] - public IActionResult GetOrdersCount(OrdersCountParametersModel parameters) - { - var storeId = _storeContext.CurrentStore.Id; - - int ordersCount = _orderApiService.GetOrdersCount(parameters.CreatedAtMin, parameters.CreatedAtMax, parameters.Status, - parameters.PaymentStatus, parameters.ShippingStatus, parameters.CustomerId, storeId); - - var ordersCountRootObject = new OrdersCountRootObject() - { - Count = ordersCount - }; - - return Ok(ordersCountRootObject); - } - - /// - /// Retrieve order by spcified id - /// - /// /// Id of the order - /// Fields from the order you want your json to contain - /// OK - /// Not Found - /// Unauthorized - [HttpGet] - [Route("/api/orders/{id}")] - [ProducesResponseType(typeof(OrdersRootObject), (int)HttpStatusCode.OK)] - [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] - [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] - [ProducesResponseType(typeof(string), (int)HttpStatusCode.NotFound)] - [GetRequestsErrorInterceptorActionFilter] - public IActionResult GetOrderById(int id, string fields = "") - { - if (id <= 0) - { - return Error(HttpStatusCode.BadRequest, "id", "invalid id"); - } - - Order order = _orderApiService.GetOrderById(id); - - if (order == null) - { - return Error(HttpStatusCode.NotFound, "order", "not found"); - } - - var ordersRootObject = new OrdersRootObject(); - - OrderDto orderDto = _dtoHelper.PrepareOrderDTO(order); - ordersRootObject.Orders.Add(orderDto); - - var json = _jsonFieldsSerializer.Serialize(ordersRootObject, fields); - - return new RawJsonActionResult(json); - } - - /// - /// Retrieve all orders for customer - /// - /// Id of the customer whoes orders you want to get - /// OK - /// Unauthorized - [HttpGet] - [Route("/api/orders/customer/{customer_id}")] - [ProducesResponseType(typeof(OrdersRootObject), (int)HttpStatusCode.OK)] - [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] - [GetRequestsErrorInterceptorActionFilter] - public IActionResult GetOrdersByCustomerId(int customer_id) - { - IList ordersForCustomer = _orderApiService.GetOrdersByCustomerId(customer_id).Select(x => _dtoHelper.PrepareOrderDTO(x)).ToList(); - - var ordersRootObject = new OrdersRootObject() - { - Orders = ordersForCustomer - }; - - return Ok(ordersRootObject); - } - - [HttpPost] - [Route("/api/orders")] - [ProducesResponseType(typeof(OrdersRootObject), (int)HttpStatusCode.OK)] - [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] - [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] - [ProducesResponseType(typeof(string), (int)HttpStatusCode.NotFound)] - [ProducesResponseType(typeof(ErrorsRootObject), 422)] - public IActionResult CreateOrder([ModelBinder(typeof(JsonModelBinder))] Delta orderDelta) - { - // Here we display the errors if the validation has failed at some point. - if (!ModelState.IsValid) - { - return Error(); - } - - // We doesn't have to check for value because this is done by the order validator. - Customer customer = _customerService.GetCustomerById(orderDelta.Dto.CustomerId.Value); - - if (customer == null) - { - return Error(HttpStatusCode.NotFound, "customer", "not found"); - } - - bool shippingRequired = false; - - if (orderDelta.Dto.OrderItemDtos != null) - { - bool shouldReturnError = ValidateEachOrderItem(orderDelta.Dto.OrderItemDtos); - - if (shouldReturnError) - { - return Error(HttpStatusCode.BadRequest); - } - - shouldReturnError = AddOrderItemsToCart(orderDelta.Dto.OrderItemDtos, customer, orderDelta.Dto.StoreId ?? _storeContext.CurrentStore.Id); - - if (shouldReturnError) - { - return Error(HttpStatusCode.BadRequest); - } - - shippingRequired = IsShippingAddressRequired(orderDelta.Dto.OrderItemDtos); - } - - if (shippingRequired) - { - bool isValid = true; - - isValid &= SetShippingOption(orderDelta.Dto.ShippingRateComputationMethodSystemName, - orderDelta.Dto.ShippingMethod, - orderDelta.Dto.StoreId ?? _storeContext.CurrentStore.Id, - customer, - BuildShoppingCartItemsFromOrderItemDtos(orderDelta.Dto.OrderItemDtos.ToList(), - customer.Id, - orderDelta.Dto.StoreId ?? _storeContext.CurrentStore.Id)); - - isValid &= ValidateAddress(orderDelta.Dto.ShippingAddress, "shipping_address"); - - if (!isValid) - { - return Error(HttpStatusCode.BadRequest); - } - } - - if (!OrderSettings.DisableBillingAddressCheckoutStep) - { - bool isValid = ValidateAddress(orderDelta.Dto.BillingAddress, "billing_address"); - - if (!isValid) - { - return Error(HttpStatusCode.BadRequest); - } - } - - Order newOrder = _factory.Initialize(); - orderDelta.Merge(newOrder); - - customer.BillingAddress = newOrder.BillingAddress; - customer.ShippingAddress = newOrder.ShippingAddress; - // If the customer has something in the cart it will be added too. Should we clear the cart first? - newOrder.Customer = customer; - - // The default value will be the currentStore.id, but if it isn't passed in the json we need to set it by hand. - if (!orderDelta.Dto.StoreId.HasValue) - { - newOrder.StoreId = _storeContext.CurrentStore.Id; - } - - PlaceOrderResult placeOrderResult = PlaceOrder(newOrder, customer); - - if (!placeOrderResult.Success) - { - foreach (var error in placeOrderResult.Errors) - { - ModelState.AddModelError("order placement", error); - } - - return Error(HttpStatusCode.BadRequest); - } - - _customerActivityService.InsertActivity("AddNewOrder", - _localizationService.GetResource("ActivityLog.AddNewOrder"), newOrder.Id); - - var ordersRootObject = new OrdersRootObject(); - - OrderDto placedOrderDto = _dtoHelper.PrepareOrderDTO(placeOrderResult.PlacedOrder); - - ordersRootObject.Orders.Add(placedOrderDto); - - var json = _jsonFieldsSerializer.Serialize(ordersRootObject, string.Empty); - - return new RawJsonActionResult(json); - } - - [HttpDelete] - [Route("/api/orders/{id}")] - [ProducesResponseType(typeof(void), (int)HttpStatusCode.OK)] - [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] - [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] - [ProducesResponseType(typeof(string), (int)HttpStatusCode.NotFound)] - [ProducesResponseType(typeof(ErrorsRootObject), 422)] - [GetRequestsErrorInterceptorActionFilter] - public IActionResult DeleteOrder(int id) - { - if (id <= 0) - { - return Error(HttpStatusCode.BadRequest, "id", "invalid id"); - } - - Order orderToDelete = _orderApiService.GetOrderById(id); - - if (orderToDelete == null) - { - return Error(HttpStatusCode.NotFound, "order", "not found"); - } - - _orderProcessingService.DeleteOrder(orderToDelete); - - //activity log - _customerActivityService.InsertActivity("DeleteOrder", _localizationService.GetResource("ActivityLog.DeleteOrder"), orderToDelete.Id); - - return new RawJsonActionResult("{}"); - } - - [HttpPut] - [Route("/api/orders/{id}")] - [ProducesResponseType(typeof(OrdersRootObject), (int)HttpStatusCode.OK)] - [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] - [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] - [ProducesResponseType(typeof(string), (int)HttpStatusCode.NotFound)] - [ProducesResponseType(typeof(ErrorsRootObject), 422)] - public IActionResult UpdateOrder([ModelBinder(typeof(JsonModelBinder))] Delta orderDelta) - { - // Here we display the errors if the validation has failed at some point. - if (!ModelState.IsValid) - { - return Error(); - } - - Order currentOrder = _orderApiService.GetOrderById(int.Parse(orderDelta.Dto.Id)); - - if (currentOrder == null) - { - return Error(HttpStatusCode.NotFound, "order", "not found"); - } - - Customer customer = currentOrder.Customer; - - bool shippingRequired = currentOrder.OrderItems.Any(item => !item.Product.IsFreeShipping); - - if (shippingRequired) - { - bool isValid = true; - - if (!string.IsNullOrEmpty(orderDelta.Dto.ShippingRateComputationMethodSystemName) || - !string.IsNullOrEmpty(orderDelta.Dto.ShippingMethod)) - { - var storeId = orderDelta.Dto.StoreId ?? _storeContext.CurrentStore.Id; - - isValid &= SetShippingOption(orderDelta.Dto.ShippingRateComputationMethodSystemName ?? currentOrder.ShippingRateComputationMethodSystemName, - orderDelta.Dto.ShippingMethod, - storeId, - customer, BuildShoppingCartItemsFromOrderItems(currentOrder.OrderItems.ToList(), customer.Id, storeId)); - } - - if (orderDelta.Dto.ShippingAddress != null) - { - isValid &= ValidateAddress(orderDelta.Dto.ShippingAddress, "shipping_address"); - } - - if (isValid) - { - currentOrder.ShippingMethod = orderDelta.Dto.ShippingMethod; - } - else - { - return Error(HttpStatusCode.BadRequest); - } - } - - if (!OrderSettings.DisableBillingAddressCheckoutStep && orderDelta.Dto.BillingAddress != null) - { - bool isValid = ValidateAddress(orderDelta.Dto.BillingAddress, "billing_address"); - - if (!isValid) - { - return Error(HttpStatusCode.BadRequest); - } - } - - orderDelta.Merge(currentOrder); - - customer.BillingAddress = currentOrder.BillingAddress; - customer.ShippingAddress = currentOrder.ShippingAddress; - - _orderService.UpdateOrder(currentOrder); - - _customerActivityService.InsertActivity("UpdateOrder", - _localizationService.GetResource("ActivityLog.UpdateOrder"), currentOrder.Id); - - var ordersRootObject = new OrdersRootObject(); - - OrderDto placedOrderDto = _dtoHelper.PrepareOrderDTO(currentOrder); - placedOrderDto.ShippingMethod = orderDelta.Dto.ShippingMethod; - - ordersRootObject.Orders.Add(placedOrderDto); - - var json = _jsonFieldsSerializer.Serialize(ordersRootObject, string.Empty); - - return new RawJsonActionResult(json); - } - - private bool SetShippingOption(string shippingRateComputationMethodSystemName, string shippingOptionName, int storeId, Customer customer, List shoppingCartItems) - { - bool isValid = true; - - if (string.IsNullOrEmpty(shippingRateComputationMethodSystemName)) - { - isValid = false; - - ModelState.AddModelError("shipping_rate_computation_method_system_name", - "Please provide shipping_rate_computation_method_system_name"); - } - else if (string.IsNullOrEmpty(shippingOptionName)) - { - isValid = false; - - ModelState.AddModelError("shipping_option_name", "Please provide shipping_option_name"); - } - else - { - GetShippingOptionResponse shippingOptionResponse = _shippingService.GetShippingOptions(shoppingCartItems, customer.ShippingAddress, customer, - shippingRateComputationMethodSystemName, storeId); - - var shippingOptions = new List(); - - if (shippingOptionResponse.Success) - { - shippingOptions = shippingOptionResponse.ShippingOptions.ToList(); - - ShippingOption shippingOption = shippingOptions - .Find(so => !string.IsNullOrEmpty(so.Name) && so.Name.Equals(shippingOptionName, StringComparison.InvariantCultureIgnoreCase)); - - _genericAttributeService.SaveAttribute(customer, - SystemCustomerAttributeNames.SelectedShippingOption, - shippingOption, storeId); - } - else - { - isValid = false; - - foreach (var errorMessage in shippingOptionResponse.Errors) - { - ModelState.AddModelError("shipping_option", errorMessage); - } - } - } - - return isValid; - } - - private List BuildShoppingCartItemsFromOrderItems(List orderItems, int customerId, int storeId) - { - var shoppingCartItems = new List(); - - foreach (var orderItem in orderItems) - { - shoppingCartItems.Add(new ShoppingCartItem() - { - ProductId = orderItem.ProductId, - CustomerId = customerId, - Quantity = orderItem.Quantity, - RentalStartDateUtc = orderItem.RentalStartDateUtc, - RentalEndDateUtc = orderItem.RentalEndDateUtc, - StoreId = storeId, - Product = orderItem.Product, - ShoppingCartType = ShoppingCartType.ShoppingCart - }); - } - - return shoppingCartItems; - } - - private List BuildShoppingCartItemsFromOrderItemDtos(List orderItemDtos, int customerId, int storeId) - { - var shoppingCartItems = new List(); - - foreach (var orderItem in orderItemDtos) - { - shoppingCartItems.Add(new ShoppingCartItem() - { - ProductId = orderItem.ProductId.Value, // required field - CustomerId = customerId, - Quantity = orderItem.Quantity ?? 1, - RentalStartDateUtc = orderItem.RentalStartDateUtc, - RentalEndDateUtc = orderItem.RentalEndDateUtc, - StoreId = storeId, - Product = _productService.GetProductById(orderItem.ProductId.Value), - ShoppingCartType = ShoppingCartType.ShoppingCart - }); - } - - return shoppingCartItems; - } - - private PlaceOrderResult PlaceOrder(Order newOrder, Customer customer) - { - var processPaymentRequest = new ProcessPaymentRequest(); - - processPaymentRequest.StoreId = newOrder.StoreId; - processPaymentRequest.CustomerId = customer.Id; - processPaymentRequest.PaymentMethodSystemName = newOrder.PaymentMethodSystemName; - - PlaceOrderResult placeOrderResult = _orderProcessingService.PlaceOrder(processPaymentRequest); - - return placeOrderResult; - } - - private bool ValidateEachOrderItem(ICollection orderItems) - { - bool shouldReturnError = false; - - foreach (var orderItem in orderItems) - { - var orderItemDtoValidator = new OrderItemDtoValidator("post", null); - ValidationResult validation = orderItemDtoValidator.Validate(orderItem); - - if (validation.IsValid) - { - Product product = _productService.GetProductById(orderItem.ProductId.Value); - - if (product == null) - { - ModelState.AddModelError("order_item.product", string.Format("Product not found for order_item.product_id = {0}", orderItem.ProductId)); - shouldReturnError = true; - } - } - else - { - foreach (var error in validation.Errors) - { - ModelState.AddModelError("order_item", error.ErrorMessage); - } - - shouldReturnError = true; - } - } - - return shouldReturnError; - } - - private bool IsShippingAddressRequired(ICollection orderItems) - { - bool shippingAddressRequired = false; - - foreach (var orderItem in orderItems) - { - Product product = _productService.GetProductById(orderItem.ProductId.Value); - - shippingAddressRequired |= product.IsShipEnabled; - } - - return shippingAddressRequired; - } - - private bool AddOrderItemsToCart(ICollection orderItems, Customer customer, int storeId) - { - bool shouldReturnError = false; - - foreach (var orderItem in orderItems) - { - Product product = _productService.GetProductById(orderItem.ProductId.Value); - - if (!product.IsRental) - { - orderItem.RentalStartDateUtc = null; - orderItem.RentalEndDateUtc = null; - } - - string attributesXml = _productAttributeConverter.ConvertToXml(orderItem.Attributes, product.Id); - - IList errors = _shoppingCartService.AddToCart(customer, product, - ShoppingCartType.ShoppingCart, storeId,attributesXml, - 0M, orderItem.RentalStartDateUtc, orderItem.RentalEndDateUtc, - orderItem.Quantity ?? 1); - - if (errors.Count > 0) - { - foreach (var error in errors) - { - ModelState.AddModelError("order", error); - } - - shouldReturnError = true; - } - } - - return shouldReturnError; - } - - private bool ValidateAddress(AddressDto address, string addressKind) - { - bool addressValid = true; - - if (address == null) - { - ModelState.AddModelError(addressKind, string.Format("{0} address required", addressKind)); - addressValid = false; - } - else - { - var addressValidator = new AddressDtoValidator(); - ValidationResult validationResult = addressValidator.Validate(address); - - foreach (var validationFailure in validationResult.Errors) - { - ModelState.AddModelError(addressKind, validationFailure.ErrorMessage); - } - - addressValid = validationResult.IsValid; - } - - return addressValid; - } - } +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using Nop.Core; +using Nop.Core.Domain.Customers; +using Nop.Core.Domain.Orders; +using Nop.Core.Infrastructure; +using Nop.Plugin.Api.Attributes; +using Nop.Plugin.Api.Constants; +using Nop.Plugin.Api.Delta; +using Nop.Plugin.Api.DTOs; +using Nop.Plugin.Api.DTOs.OrderItems; +using Nop.Plugin.Api.DTOs.Orders; +using Nop.Plugin.Api.Factories; +using Nop.Plugin.Api.Helpers; +using Nop.Plugin.Api.JSON.ActionResults; +using Nop.Plugin.Api.ModelBinders; +using Nop.Plugin.Api.Models.OrdersParameters; +using Nop.Plugin.Api.Services; +using Nop.Plugin.Api.Validators; +using Nop.Services.Catalog; +using Nop.Services.Common; +using Nop.Services.Customers; +using Nop.Services.Discounts; +using Nop.Services.Localization; +using Nop.Services.Logging; +using Nop.Services.Media; +using Nop.Services.Orders; +using Nop.Services.Payments; +using Nop.Services.Security; +using Nop.Services.Shipping; +using Nop.Services.Stores; +using Microsoft.AspNetCore.Mvc; + +namespace Nop.Plugin.Api.Controllers +{ + using Microsoft.AspNetCore.Authentication.JwtBearer; + using DTOs.Errors; + using JSON.Serializers; + + [ApiAuthorize(Policy = JwtBearerDefaults.AuthenticationScheme, AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] + public class OrdersController : BaseApiController + { + private readonly IOrderApiService _orderApiService; + private readonly IProductService _productService; + private readonly IOrderProcessingService _orderProcessingService; + private readonly IOrderService _orderService; + private readonly IShoppingCartService _shoppingCartService; + private readonly IGenericAttributeService _genericAttributeService; + private readonly IShippingService _shippingService; + private readonly IDTOHelper _dtoHelper; + private readonly IProductAttributeConverter _productAttributeConverter; + private readonly IStoreContext _storeContext; + private readonly IFactory _factory; + + // We resolve the order settings this way because of the tests. + // The auto mocking does not support concreate types as dependencies. It supports only interfaces. + private OrderSettings _orderSettings; + + private OrderSettings OrderSettings => _orderSettings ?? (_orderSettings = EngineContext.Current.Resolve()); + + public OrdersController(IOrderApiService orderApiService, + IJsonFieldsSerializer jsonFieldsSerializer, + IAclService aclService, + ICustomerService customerService, + IStoreMappingService storeMappingService, + IStoreService storeService, + IDiscountService discountService, + ICustomerActivityService customerActivityService, + ILocalizationService localizationService, + IProductService productService, + IFactory factory, + IOrderProcessingService orderProcessingService, + IOrderService orderService, + IShoppingCartService shoppingCartService, + IGenericAttributeService genericAttributeService, + IStoreContext storeContext, + IShippingService shippingService, + IPictureService pictureService, + IDTOHelper dtoHelper, + IProductAttributeConverter productAttributeConverter) + : base(jsonFieldsSerializer, aclService, customerService, storeMappingService, + storeService, discountService, customerActivityService, localizationService,pictureService) + { + _orderApiService = orderApiService; + _factory = factory; + _orderProcessingService = orderProcessingService; + _orderService = orderService; + _shoppingCartService = shoppingCartService; + _genericAttributeService = genericAttributeService; + _storeContext = storeContext; + _shippingService = shippingService; + _dtoHelper = dtoHelper; + _productService = productService; + _productAttributeConverter = productAttributeConverter; + } + + /// + /// Receive a list of all Orders + /// + /// OK + /// Bad Request + /// Unauthorized + [HttpGet] + [Route("/api/orders")] + [ProducesResponseType(typeof(OrdersRootObject), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] + [GetRequestsErrorInterceptorActionFilter] + public IActionResult GetOrders(OrdersParametersModel parameters) + { + if (parameters.Page < Configurations.DefaultPageValue) + { + return Error(HttpStatusCode.BadRequest, "page", "Invalid page parameter"); + } + + if (parameters.Limit < Configurations.MinLimit || parameters.Limit > Configurations.MaxLimit) + { + return Error(HttpStatusCode.BadRequest, "page", "Invalid limit parameter"); + } + + var storeId = _storeContext.CurrentStore.Id; + + var orders = _orderApiService.GetOrders(parameters.Ids, parameters.CreatedAtMin, + parameters.CreatedAtMax, + parameters.Limit, parameters.Page, parameters.SinceId, + parameters.Status, parameters.PaymentStatus, parameters.ShippingStatus, + parameters.CustomerId, storeId); + + IList ordersAsDtos = orders.Select(x => _dtoHelper.PrepareOrderDTO(x)).ToList(); + + var ordersRootObject = new OrdersRootObject() + { + Orders = ordersAsDtos + }; + + var json = JsonFieldsSerializer.Serialize(ordersRootObject, parameters.Fields); + + return new RawJsonActionResult(json); + } + + /// + /// Receive a count of all Orders + /// + /// OK + /// Unauthorized + [HttpGet] + [Route("/api/orders/count")] + [ProducesResponseType(typeof(OrdersCountRootObject), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] + [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] + [GetRequestsErrorInterceptorActionFilter] + public IActionResult GetOrdersCount(OrdersCountParametersModel parameters) + { + var storeId = _storeContext.CurrentStore.Id; + + var ordersCount = _orderApiService.GetOrdersCount(parameters.CreatedAtMin, parameters.CreatedAtMax, parameters.Status, + parameters.PaymentStatus, parameters.ShippingStatus, parameters.CustomerId, storeId); + + var ordersCountRootObject = new OrdersCountRootObject() + { + Count = ordersCount + }; + + return Ok(ordersCountRootObject); + } + + /// + /// Retrieve order by spcified id + /// + /// /// Id of the order + /// Fields from the order you want your json to contain + /// OK + /// Not Found + /// Unauthorized + [HttpGet] + [Route("/api/orders/{id}")] + [ProducesResponseType(typeof(OrdersRootObject), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] + [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.NotFound)] + [GetRequestsErrorInterceptorActionFilter] + public IActionResult GetOrderById(int id, string fields = "") + { + if (id <= 0) + { + return Error(HttpStatusCode.BadRequest, "id", "invalid id"); + } + + var order = _orderApiService.GetOrderById(id); + + if (order == null) + { + return Error(HttpStatusCode.NotFound, "order", "not found"); + } + + var ordersRootObject = new OrdersRootObject(); + + var orderDto = _dtoHelper.PrepareOrderDTO(order); + ordersRootObject.Orders.Add(orderDto); + + var json = JsonFieldsSerializer.Serialize(ordersRootObject, fields); + + return new RawJsonActionResult(json); + } + + /// + /// Retrieve all orders for customer + /// + /// Id of the customer whoes orders you want to get + /// OK + /// Unauthorized + [HttpGet] + [Route("/api/orders/customer/{customer_id}")] + [ProducesResponseType(typeof(OrdersRootObject), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] + [GetRequestsErrorInterceptorActionFilter] + public IActionResult GetOrdersByCustomerId(int customerId) + { + IList ordersForCustomer = _orderApiService.GetOrdersByCustomerId(customerId).Select(x => _dtoHelper.PrepareOrderDTO(x)).ToList(); + + var ordersRootObject = new OrdersRootObject() + { + Orders = ordersForCustomer + }; + + return Ok(ordersRootObject); + } + + [HttpPost] + [Route("/api/orders")] + [ProducesResponseType(typeof(OrdersRootObject), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] + [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.NotFound)] + [ProducesResponseType(typeof(ErrorsRootObject), 422)] + public IActionResult CreateOrder([ModelBinder(typeof(JsonModelBinder))] Delta orderDelta) + { + // Here we display the errors if the validation has failed at some point. + if (!ModelState.IsValid) + { + return Error(); + } + + if (orderDelta.Dto.CustomerId == null) + { + return Error(); + } + + // We doesn't have to check for value because this is done by the order validator. + var customer = CustomerService.GetCustomerById(orderDelta.Dto.CustomerId.Value); + + if (customer == null) + { + return Error(HttpStatusCode.NotFound, "customer", "not found"); + } + + var shippingRequired = false; + + if (orderDelta.Dto.OrderItems != null) + { + var shouldReturnError = AddOrderItemsToCart(orderDelta.Dto.OrderItems, customer, orderDelta.Dto.StoreId ?? _storeContext.CurrentStore.Id); + if (shouldReturnError) + { + return Error(HttpStatusCode.BadRequest); + } + + shippingRequired = IsShippingAddressRequired(orderDelta.Dto.OrderItems); + } + + if (shippingRequired) + { + var isValid = true; + + isValid &= SetShippingOption(orderDelta.Dto.ShippingRateComputationMethodSystemName, + orderDelta.Dto.ShippingMethod, + orderDelta.Dto.StoreId ?? _storeContext.CurrentStore.Id, + customer, + BuildShoppingCartItemsFromOrderItemDtos(orderDelta.Dto.OrderItems.ToList(), + customer.Id, + orderDelta.Dto.StoreId ?? _storeContext.CurrentStore.Id)); + + if (!isValid) + { + return Error(HttpStatusCode.BadRequest); + } + } + + var newOrder = _factory.Initialize(); + orderDelta.Merge(newOrder); + + customer.BillingAddress = newOrder.BillingAddress; + customer.ShippingAddress = newOrder.ShippingAddress; + + // If the customer has something in the cart it will be added too. Should we clear the cart first? + newOrder.Customer = customer; + + // The default value will be the currentStore.id, but if it isn't passed in the json we need to set it by hand. + if (!orderDelta.Dto.StoreId.HasValue) + { + newOrder.StoreId = _storeContext.CurrentStore.Id; + } + + var placeOrderResult = PlaceOrder(newOrder, customer); + + if (!placeOrderResult.Success) + { + foreach (var error in placeOrderResult.Errors) + { + ModelState.AddModelError("order placement", error); + } + + return Error(HttpStatusCode.BadRequest); + } + + CustomerActivityService.InsertActivity("AddNewOrder", + LocalizationService.GetResource("ActivityLog.AddNewOrder"), newOrder); + + var ordersRootObject = new OrdersRootObject(); + + var placedOrderDto = _dtoHelper.PrepareOrderDTO(placeOrderResult.PlacedOrder); + + ordersRootObject.Orders.Add(placedOrderDto); + + var json = JsonFieldsSerializer.Serialize(ordersRootObject, string.Empty); + + return new RawJsonActionResult(json); + } + + [HttpDelete] + [Route("/api/orders/{id}")] + [ProducesResponseType(typeof(void), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] + [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.NotFound)] + [ProducesResponseType(typeof(ErrorsRootObject), 422)] + [GetRequestsErrorInterceptorActionFilter] + public IActionResult DeleteOrder(int id) + { + if (id <= 0) + { + return Error(HttpStatusCode.BadRequest, "id", "invalid id"); + } + + var orderToDelete = _orderApiService.GetOrderById(id); + + if (orderToDelete == null) + { + return Error(HttpStatusCode.NotFound, "order", "not found"); + } + + _orderProcessingService.DeleteOrder(orderToDelete); + + //activity log + CustomerActivityService.InsertActivity("DeleteOrder", LocalizationService.GetResource("ActivityLog.DeleteOrder"), orderToDelete); + + return new RawJsonActionResult("{}"); + } + + [HttpPut] + [Route("/api/orders/{id}")] + [ProducesResponseType(typeof(OrdersRootObject), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] + [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.NotFound)] + [ProducesResponseType(typeof(ErrorsRootObject), 422)] + public IActionResult UpdateOrder([ModelBinder(typeof(JsonModelBinder))] Delta orderDelta) + { + // Here we display the errors if the validation has failed at some point. + if (!ModelState.IsValid) + { + return Error(); + } + + var currentOrder = _orderApiService.GetOrderById(orderDelta.Dto.Id); + + if (currentOrder == null) + { + return Error(HttpStatusCode.NotFound, "order", "not found"); + } + + var customer = currentOrder.Customer; + + var shippingRequired = currentOrder.OrderItems.Any(item => !item.Product.IsFreeShipping); + + if (shippingRequired) + { + var isValid = true; + + if (!string.IsNullOrEmpty(orderDelta.Dto.ShippingRateComputationMethodSystemName) || + !string.IsNullOrEmpty(orderDelta.Dto.ShippingMethod)) + { + var storeId = orderDelta.Dto.StoreId ?? _storeContext.CurrentStore.Id; + + isValid &= SetShippingOption(orderDelta.Dto.ShippingRateComputationMethodSystemName ?? currentOrder.ShippingRateComputationMethodSystemName, + orderDelta.Dto.ShippingMethod, + storeId, + customer, BuildShoppingCartItemsFromOrderItems(currentOrder.OrderItems.ToList(), customer.Id, storeId)); + } + + if (isValid) + { + currentOrder.ShippingMethod = orderDelta.Dto.ShippingMethod; + } + else + { + return Error(HttpStatusCode.BadRequest); + } + } + + orderDelta.Merge(currentOrder); + + customer.BillingAddress = currentOrder.BillingAddress; + customer.ShippingAddress = currentOrder.ShippingAddress; + + _orderService.UpdateOrder(currentOrder); + + CustomerActivityService.InsertActivity("UpdateOrder", + LocalizationService.GetResource("ActivityLog.UpdateOrder"), currentOrder); + + var ordersRootObject = new OrdersRootObject(); + + var placedOrderDto = _dtoHelper.PrepareOrderDTO(currentOrder); + placedOrderDto.ShippingMethod = orderDelta.Dto.ShippingMethod; + + ordersRootObject.Orders.Add(placedOrderDto); + + var json = JsonFieldsSerializer.Serialize(ordersRootObject, string.Empty); + + return new RawJsonActionResult(json); + } + + private bool SetShippingOption(string shippingRateComputationMethodSystemName, string shippingOptionName, int storeId, Customer customer, List shoppingCartItems) + { + var isValid = true; + + if (string.IsNullOrEmpty(shippingRateComputationMethodSystemName)) + { + isValid = false; + + ModelState.AddModelError("shipping_rate_computation_method_system_name", + "Please provide shipping_rate_computation_method_system_name"); + } + else if (string.IsNullOrEmpty(shippingOptionName)) + { + isValid = false; + + ModelState.AddModelError("shipping_option_name", "Please provide shipping_option_name"); + } + else + { + var shippingOptionResponse = _shippingService.GetShippingOptions(shoppingCartItems, customer.ShippingAddress, customer, + shippingRateComputationMethodSystemName, storeId); + + if (shippingOptionResponse.Success) + { + var shippingOptions = shippingOptionResponse.ShippingOptions.ToList(); + + var shippingOption = shippingOptions + .Find(so => !string.IsNullOrEmpty(so.Name) && so.Name.Equals(shippingOptionName, StringComparison.InvariantCultureIgnoreCase)); + + _genericAttributeService.SaveAttribute(customer, + NopCustomerDefaults.SelectedShippingOptionAttribute, + shippingOption, storeId); + } + else + { + isValid = false; + + foreach (var errorMessage in shippingOptionResponse.Errors) + { + ModelState.AddModelError("shipping_option", errorMessage); + } + } + } + + return isValid; + } + + private List BuildShoppingCartItemsFromOrderItems(List orderItems, int customerId, int storeId) + { + var shoppingCartItems = new List(); + + foreach (var orderItem in orderItems) + { + shoppingCartItems.Add(new ShoppingCartItem() + { + ProductId = orderItem.ProductId, + CustomerId = customerId, + Quantity = orderItem.Quantity, + RentalStartDateUtc = orderItem.RentalStartDateUtc, + RentalEndDateUtc = orderItem.RentalEndDateUtc, + StoreId = storeId, + Product = orderItem.Product, + ShoppingCartType = ShoppingCartType.ShoppingCart + }); + } + + return shoppingCartItems; + } + + private List BuildShoppingCartItemsFromOrderItemDtos(List orderItemDtos, int customerId, int storeId) + { + var shoppingCartItems = new List(); + + foreach (var orderItem in orderItemDtos) + { + if (orderItem.ProductId != null) + { + shoppingCartItems.Add(new ShoppingCartItem() + { + ProductId = orderItem.ProductId.Value, // required field + CustomerId = customerId, + Quantity = orderItem.Quantity ?? 1, + RentalStartDateUtc = orderItem.RentalStartDateUtc, + RentalEndDateUtc = orderItem.RentalEndDateUtc, + StoreId = storeId, + Product = _productService.GetProductById(orderItem.ProductId.Value), + ShoppingCartType = ShoppingCartType.ShoppingCart + }); + } + } + + return shoppingCartItems; + } + + private PlaceOrderResult PlaceOrder(Order newOrder, Customer customer) + { + var processPaymentRequest = new ProcessPaymentRequest + { + StoreId = newOrder.StoreId, + CustomerId = customer.Id, + PaymentMethodSystemName = newOrder.PaymentMethodSystemName + }; + + + var placeOrderResult = _orderProcessingService.PlaceOrder(processPaymentRequest); + + return placeOrderResult; + } + + private bool IsShippingAddressRequired(ICollection orderItems) + { + var shippingAddressRequired = false; + + foreach (var orderItem in orderItems) + { + if (orderItem.ProductId != null) + { + var product = _productService.GetProductById(orderItem.ProductId.Value); + + shippingAddressRequired |= product.IsShipEnabled; + } + } + + return shippingAddressRequired; + } + + private bool AddOrderItemsToCart(ICollection orderItems, Customer customer, int storeId) + { + var shouldReturnError = false; + + foreach (var orderItem in orderItems) + { + if (orderItem.ProductId != null) + { + var product = _productService.GetProductById(orderItem.ProductId.Value); + + if (!product.IsRental) + { + orderItem.RentalStartDateUtc = null; + orderItem.RentalEndDateUtc = null; + } + + var attributesXml = _productAttributeConverter.ConvertToXml(orderItem.Attributes.ToList(), product.Id); + + var errors = _shoppingCartService.AddToCart(customer, product, + ShoppingCartType.ShoppingCart, storeId,attributesXml, + 0M, orderItem.RentalStartDateUtc, orderItem.RentalEndDateUtc, + orderItem.Quantity ?? 1); + + if (errors.Count > 0) + { + foreach (var error in errors) + { + ModelState.AddModelError("order", error); + } + + shouldReturnError = true; + } + } + } + + return shouldReturnError; + } + } } \ No newline at end of file diff --git a/Nop.Plugin.Api/Controllers/ProductAttributesController.cs b/Nop.Plugin.Api/Controllers/ProductAttributesController.cs index 98ce1e1..4b95520 100644 --- a/Nop.Plugin.Api/Controllers/ProductAttributesController.cs +++ b/Nop.Plugin.Api/Controllers/ProductAttributesController.cs @@ -1,270 +1,263 @@ -using Microsoft.AspNetCore.Authentication.JwtBearer; -using Microsoft.AspNetCore.Mvc; -using Nop.Core.Domain.Catalog; -using Nop.Plugin.Api.Attributes; -using Nop.Plugin.Api.Constants; -using Nop.Plugin.Api.Delta; -using Nop.Plugin.Api.DTOs.Errors; -using Nop.Plugin.Api.DTOs.ProductAttributes; -using Nop.Plugin.Api.Helpers; -using Nop.Plugin.Api.JSON.ActionResults; -using Nop.Plugin.Api.ModelBinders; -using Nop.Plugin.Api.Models.ProductAttributes; -using Nop.Plugin.Api.JSON.Serializers; -using Nop.Plugin.Api.Services; -using Nop.Services.Catalog; -using Nop.Services.Customers; -using Nop.Services.Discounts; -using Nop.Services.Localization; -using Nop.Services.Logging; -using Nop.Services.Media; -using Nop.Services.Security; -using Nop.Services.Stores; -using System.Collections.Generic; -using System.Linq; -using System.Net; - -namespace Nop.Plugin.Api.Controllers -{ - [ApiAuthorize(Policy = JwtBearerDefaults.AuthenticationScheme, AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] - public class ProductAttributesController : BaseApiController - { - private readonly IProductAttributeService _productAttributeService; - private readonly IProductAttributesApiService _productAttributesApiService; - private readonly IDTOHelper _dtoHelper; - - public ProductAttributesController(IJsonFieldsSerializer jsonFieldsSerializer, - ICustomerActivityService customerActivityService, - ILocalizationService localizationService, - IAclService aclService, - IStoreMappingService storeMappingService, - IStoreService storeService, - ICustomerService customerService, - IDiscountService discountService, - IPictureService pictureService, - IProductAttributeService productAttributeService, - IProductAttributesApiService productAttributesApiService, - IDTOHelper dtoHelper) : base(jsonFieldsSerializer, aclService, customerService, storeMappingService, storeService, discountService, customerActivityService, localizationService, pictureService) - { - _productAttributeService = productAttributeService; - _productAttributesApiService = productAttributesApiService; - _dtoHelper = dtoHelper; - } - - /// - /// Receive a list of all product attributes - /// - /// OK - /// Bad Request - /// Unauthorized - [HttpGet] - [Route("/api/productattributes")] - [ProducesResponseType(typeof(ProductAttributesRootObjectDto), (int)HttpStatusCode.OK)] - [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] - [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] - [GetRequestsErrorInterceptorActionFilter] - public IActionResult GetProductAttributes(ProductAttributesParametersModel parameters) - { - if (parameters.Limit < Configurations.MinLimit || parameters.Limit > Configurations.MaxLimit) - { - return Error(HttpStatusCode.BadRequest, "limit", "invalid limit parameter"); - } - - if (parameters.Page < Configurations.DefaultPageValue) - { - return Error(HttpStatusCode.BadRequest, "page", "invalid page parameter"); - } - - var allProductAttributes = _productAttributesApiService.GetProductAttributes(parameters.Limit, parameters.Page, parameters.SinceId); - - IList productAttributesAsDtos = allProductAttributes.Select(productAttribute => - { - return _dtoHelper.PrepareProductAttributeDTO(productAttribute); - - }).ToList(); - - var productAttributesRootObject = new ProductAttributesRootObjectDto() - { - ProductAttributes = productAttributesAsDtos - }; - - var json = _jsonFieldsSerializer.Serialize(productAttributesRootObject, parameters.Fields); - - return new RawJsonActionResult(json); - } - - /// - /// Receive a count of all product attributes - /// - /// OK - /// Unauthorized - [HttpGet] - [Route("/api/productattributes/count")] - [ProducesResponseType(typeof(ProductAttributesCountRootObject), (int)HttpStatusCode.OK)] - [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] - [GetRequestsErrorInterceptorActionFilter] - public IActionResult GetProductAttributesCount() - { - int allProductAttributesCount = _productAttributesApiService.GetProductAttributesCount(); - - var productAttributesCountRootObject = new ProductAttributesCountRootObject() - { - Count = allProductAttributesCount - }; - - return Ok(productAttributesCountRootObject); - } - - /// - /// Retrieve product attribute by spcified id - /// - /// Id of the product attribute - /// Fields from the product attribute you want your json to contain - /// OK - /// Not Found - /// Unauthorized - [HttpGet] - [Route("/api/productattributes/{id}")] - [ProducesResponseType(typeof(ProductAttributesRootObjectDto), (int)HttpStatusCode.OK)] - [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] - [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] - [GetRequestsErrorInterceptorActionFilter] - public IActionResult GetProductAttributeById(int id, string fields = "") - { - if (id <= 0) - { - return Error(HttpStatusCode.BadRequest, "id", "invalid id"); - } - - ProductAttribute productAttribute = _productAttributesApiService.GetById(id); - - if (productAttribute == null) - { - return Error(HttpStatusCode.NotFound, "product attribute", "not found"); - } - - ProductAttributeDto productAttributeDto = _dtoHelper.PrepareProductAttributeDTO(productAttribute); - - var productAttributesRootObject = new ProductAttributesRootObjectDto(); - - productAttributesRootObject.ProductAttributes.Add(productAttributeDto); - - var json = _jsonFieldsSerializer.Serialize(productAttributesRootObject, fields); - - return new RawJsonActionResult(json); - } - - [HttpPost] - [Route("/api/productattributes")] - [ProducesResponseType(typeof(ProductAttributesRootObjectDto), (int)HttpStatusCode.OK)] - [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] - [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] - [ProducesResponseType(typeof(string), (int)HttpStatusCode.NotFound)] - [ProducesResponseType(typeof(ErrorsRootObject), 422)] - [GetRequestsErrorInterceptorActionFilter] - public IActionResult CreateProductAttribute([ModelBinder(typeof(JsonModelBinder))] Delta productAttributeDelta) - { - // Here we display the errors if the validation has failed at some point. - if (!ModelState.IsValid) - { - return Error(); - } - - // Inserting the new product - ProductAttribute productAttribute = new ProductAttribute(); - productAttributeDelta.Merge(productAttribute); - - _productAttributeService.InsertProductAttribute(productAttribute); - - _customerActivityService.InsertActivity("AddNewProductAttribute", - _localizationService.GetResource("ActivityLog.AddNewProductAttribute"), productAttribute.Name); - - // Preparing the result dto of the new product - ProductAttributeDto productAttributeDto = _dtoHelper.PrepareProductAttributeDTO(productAttribute); - - var productAttributesRootObjectDto = new ProductAttributesRootObjectDto(); - - productAttributesRootObjectDto.ProductAttributes.Add(productAttributeDto); - - var json = _jsonFieldsSerializer.Serialize(productAttributesRootObjectDto, string.Empty); - - return new RawJsonActionResult(json); - } - - [HttpPut] - [Route("/api/productattributes/{id}")] - [ProducesResponseType(typeof(ProductAttributesRootObjectDto), (int)HttpStatusCode.OK)] - [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] - [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] - [ProducesResponseType(typeof(string), (int)HttpStatusCode.NotFound)] - [ProducesResponseType(typeof(ErrorsRootObject), 422)] - [GetRequestsErrorInterceptorActionFilter] - public IActionResult UpdateProductAttribute([ModelBinder(typeof(JsonModelBinder))] Delta productAttributeDelta) - { - // Here we display the errors if the validation has failed at some point. - if (!ModelState.IsValid) - { - return Error(); - } - - // We do not need to validate the product attribute id, because this will happen in the model binder using the dto validator. - int productAttributeId = int.Parse(productAttributeDelta.Dto.Id); - - ProductAttribute productAttribute = _productAttributesApiService.GetById(productAttributeId); - - if (productAttribute == null) - { - return Error(HttpStatusCode.NotFound, "product attribute", "not found"); - } - - productAttributeDelta.Merge(productAttribute); - - - _productAttributeService.UpdateProductAttribute(productAttribute); - - _customerActivityService.InsertActivity("EditProductAttribute", - _localizationService.GetResource("ActivityLog.EditProductAttribute"), productAttribute.Name); - - // Preparing the result dto of the new product attribute - ProductAttributeDto productAttributeDto = _dtoHelper.PrepareProductAttributeDTO(productAttribute); - - var productAttributesRootObjectDto = new ProductAttributesRootObjectDto(); - - productAttributesRootObjectDto.ProductAttributes.Add(productAttributeDto); - - var json = _jsonFieldsSerializer.Serialize(productAttributesRootObjectDto, string.Empty); - - return new RawJsonActionResult(json); - } - - [HttpDelete] - [Route("/api/productattributes/{id}")] - [ProducesResponseType(typeof(void), (int)HttpStatusCode.OK)] - [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] - [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] - [ProducesResponseType(typeof(string), (int)HttpStatusCode.NotFound)] - [ProducesResponseType(typeof(ErrorsRootObject), 422)] - [GetRequestsErrorInterceptorActionFilter] - public IActionResult DeleteProductAttribute(int id) - { - if (id <= 0) - { - return Error(HttpStatusCode.BadRequest, "id", "invalid id"); - } - - ProductAttribute productAttribute = _productAttributesApiService.GetById(id); - - if (productAttribute == null) - { - return Error(HttpStatusCode.NotFound, "product attribute", "not found"); - } - - _productAttributeService.DeleteProductAttribute(productAttribute); - - //activity log - _customerActivityService.InsertActivity("DeleteProductAttribute", _localizationService.GetResource("ActivityLog.DeleteProductAttribute"), productAttribute.Name); - - return new RawJsonActionResult("{}"); - } - } +using Microsoft.AspNetCore.Authentication.JwtBearer; +using Microsoft.AspNetCore.Mvc; +using Nop.Core.Domain.Catalog; +using Nop.Plugin.Api.Attributes; +using Nop.Plugin.Api.Constants; +using Nop.Plugin.Api.Delta; +using Nop.Plugin.Api.DTOs.Errors; +using Nop.Plugin.Api.DTOs.ProductAttributes; +using Nop.Plugin.Api.Helpers; +using Nop.Plugin.Api.JSON.ActionResults; +using Nop.Plugin.Api.ModelBinders; +using Nop.Plugin.Api.Models.ProductAttributes; +using Nop.Plugin.Api.JSON.Serializers; +using Nop.Plugin.Api.Services; +using Nop.Services.Catalog; +using Nop.Services.Customers; +using Nop.Services.Discounts; +using Nop.Services.Localization; +using Nop.Services.Logging; +using Nop.Services.Media; +using Nop.Services.Security; +using Nop.Services.Stores; +using System.Collections.Generic; +using System.Linq; +using System.Net; + +namespace Nop.Plugin.Api.Controllers +{ + [ApiAuthorize(Policy = JwtBearerDefaults.AuthenticationScheme, AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] + public class ProductAttributesController : BaseApiController + { + private readonly IProductAttributeService _productAttributeService; + private readonly IProductAttributesApiService _productAttributesApiService; + private readonly IDTOHelper _dtoHelper; + + public ProductAttributesController(IJsonFieldsSerializer jsonFieldsSerializer, + ICustomerActivityService customerActivityService, + ILocalizationService localizationService, + IAclService aclService, + IStoreMappingService storeMappingService, + IStoreService storeService, + ICustomerService customerService, + IDiscountService discountService, + IPictureService pictureService, + IProductAttributeService productAttributeService, + IProductAttributesApiService productAttributesApiService, + IDTOHelper dtoHelper) : base(jsonFieldsSerializer, aclService, customerService, storeMappingService, storeService, discountService, customerActivityService, localizationService, pictureService) + { + _productAttributeService = productAttributeService; + _productAttributesApiService = productAttributesApiService; + _dtoHelper = dtoHelper; + } + + /// + /// Receive a list of all product attributes + /// + /// OK + /// Bad Request + /// Unauthorized + [HttpGet] + [Route("/api/productattributes")] + [ProducesResponseType(typeof(ProductAttributesRootObjectDto), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] + [GetRequestsErrorInterceptorActionFilter] + public IActionResult GetProductAttributes(ProductAttributesParametersModel parameters) + { + if (parameters.Limit < Configurations.MinLimit || parameters.Limit > Configurations.MaxLimit) + { + return Error(HttpStatusCode.BadRequest, "limit", "invalid limit parameter"); + } + + if (parameters.Page < Configurations.DefaultPageValue) + { + return Error(HttpStatusCode.BadRequest, "page", "invalid page parameter"); + } + + var allProductAttributes = _productAttributesApiService.GetProductAttributes(parameters.Limit, parameters.Page, parameters.SinceId); + + IList productAttributesAsDtos = allProductAttributes.Select(productAttribute => _dtoHelper.PrepareProductAttributeDTO(productAttribute)).ToList(); + + var productAttributesRootObject = new ProductAttributesRootObjectDto() + { + ProductAttributes = productAttributesAsDtos + }; + + var json = JsonFieldsSerializer.Serialize(productAttributesRootObject, parameters.Fields); + + return new RawJsonActionResult(json); + } + + /// + /// Receive a count of all product attributes + /// + /// OK + /// Unauthorized + [HttpGet] + [Route("/api/productattributes/count")] + [ProducesResponseType(typeof(ProductAttributesCountRootObject), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] + [GetRequestsErrorInterceptorActionFilter] + public IActionResult GetProductAttributesCount() + { + var allProductAttributesCount = _productAttributesApiService.GetProductAttributesCount(); + + var productAttributesCountRootObject = new ProductAttributesCountRootObject() + { + Count = allProductAttributesCount + }; + + return Ok(productAttributesCountRootObject); + } + + /// + /// Retrieve product attribute by spcified id + /// + /// Id of the product attribute + /// Fields from the product attribute you want your json to contain + /// OK + /// Not Found + /// Unauthorized + [HttpGet] + [Route("/api/productattributes/{id}")] + [ProducesResponseType(typeof(ProductAttributesRootObjectDto), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] + [GetRequestsErrorInterceptorActionFilter] + public IActionResult GetProductAttributeById(int id, string fields = "") + { + if (id <= 0) + { + return Error(HttpStatusCode.BadRequest, "id", "invalid id"); + } + + var productAttribute = _productAttributesApiService.GetById(id); + + if (productAttribute == null) + { + return Error(HttpStatusCode.NotFound, "product attribute", "not found"); + } + + var productAttributeDto = _dtoHelper.PrepareProductAttributeDTO(productAttribute); + + var productAttributesRootObject = new ProductAttributesRootObjectDto(); + + productAttributesRootObject.ProductAttributes.Add(productAttributeDto); + + var json = JsonFieldsSerializer.Serialize(productAttributesRootObject, fields); + + return new RawJsonActionResult(json); + } + + [HttpPost] + [Route("/api/productattributes")] + [ProducesResponseType(typeof(ProductAttributesRootObjectDto), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] + [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.NotFound)] + [ProducesResponseType(typeof(ErrorsRootObject), 422)] + [GetRequestsErrorInterceptorActionFilter] + public IActionResult CreateProductAttribute([ModelBinder(typeof(JsonModelBinder))] Delta productAttributeDelta) + { + // Here we display the errors if the validation has failed at some point. + if (!ModelState.IsValid) + { + return Error(); + } + + // Inserting the new product + var productAttribute = new ProductAttribute(); + productAttributeDelta.Merge(productAttribute); + + _productAttributeService.InsertProductAttribute(productAttribute); + + CustomerActivityService.InsertActivity("AddNewProductAttribute", + LocalizationService.GetResource("ActivityLog.AddNewProductAttribute"), productAttribute); + + // Preparing the result dto of the new product + var productAttributeDto = _dtoHelper.PrepareProductAttributeDTO(productAttribute); + + var productAttributesRootObjectDto = new ProductAttributesRootObjectDto(); + + productAttributesRootObjectDto.ProductAttributes.Add(productAttributeDto); + + var json = JsonFieldsSerializer.Serialize(productAttributesRootObjectDto, string.Empty); + + return new RawJsonActionResult(json); + } + + [HttpPut] + [Route("/api/productattributes/{id}")] + [ProducesResponseType(typeof(ProductAttributesRootObjectDto), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] + [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.NotFound)] + [ProducesResponseType(typeof(ErrorsRootObject), 422)] + [GetRequestsErrorInterceptorActionFilter] + public IActionResult UpdateProductAttribute([ModelBinder(typeof(JsonModelBinder))] Delta productAttributeDelta) + { + // Here we display the errors if the validation has failed at some point. + if (!ModelState.IsValid) + { + return Error(); + } + + var productAttribute = _productAttributesApiService.GetById(productAttributeDelta.Dto.Id); + + if (productAttribute == null) + { + return Error(HttpStatusCode.NotFound, "product attribute", "not found"); + } + + productAttributeDelta.Merge(productAttribute); + + + _productAttributeService.UpdateProductAttribute(productAttribute); + + CustomerActivityService.InsertActivity("EditProductAttribute", + LocalizationService.GetResource("ActivityLog.EditProductAttribute"), productAttribute); + + // Preparing the result dto of the new product attribute + var productAttributeDto = _dtoHelper.PrepareProductAttributeDTO(productAttribute); + + var productAttributesRootObjectDto = new ProductAttributesRootObjectDto(); + + productAttributesRootObjectDto.ProductAttributes.Add(productAttributeDto); + + var json = JsonFieldsSerializer.Serialize(productAttributesRootObjectDto, string.Empty); + + return new RawJsonActionResult(json); + } + + [HttpDelete] + [Route("/api/productattributes/{id}")] + [ProducesResponseType(typeof(void), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] + [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.NotFound)] + [ProducesResponseType(typeof(ErrorsRootObject), 422)] + [GetRequestsErrorInterceptorActionFilter] + public IActionResult DeleteProductAttribute(int id) + { + if (id <= 0) + { + return Error(HttpStatusCode.BadRequest, "id", "invalid id"); + } + + var productAttribute = _productAttributesApiService.GetById(id); + + if (productAttribute == null) + { + return Error(HttpStatusCode.NotFound, "product attribute", "not found"); + } + + _productAttributeService.DeleteProductAttribute(productAttribute); + + //activity log + CustomerActivityService.InsertActivity("DeleteProductAttribute", LocalizationService.GetResource("ActivityLog.DeleteProductAttribute"), productAttribute); + + return new RawJsonActionResult("{}"); + } + } } \ No newline at end of file diff --git a/Nop.Plugin.Api/Controllers/ProductCategoryMappingsController.cs b/Nop.Plugin.Api/Controllers/ProductCategoryMappingsController.cs index 929dd00..92c0e42 100644 --- a/Nop.Plugin.Api/Controllers/ProductCategoryMappingsController.cs +++ b/Nop.Plugin.Api/Controllers/ProductCategoryMappingsController.cs @@ -1,317 +1,317 @@ -using System.Collections.Generic; -using System.Linq; -using System.Net; -using Nop.Core.Domain.Catalog; -using Nop.Plugin.Api.Attributes; -using Nop.Plugin.Api.Constants; -using Nop.Plugin.Api.Delta; -using Nop.Plugin.Api.DTOs.ProductCategoryMappings; -using Nop.Plugin.Api.JSON.ActionResults; -using Nop.Plugin.Api.MappingExtensions; -using Nop.Plugin.Api.ModelBinders; -using Nop.Plugin.Api.Models.ProductCategoryMappingsParameters; -using Nop.Plugin.Api.Services; -using Nop.Services.Catalog; -using Nop.Services.Customers; -using Nop.Services.Discounts; -using Nop.Services.Localization; -using Nop.Services.Logging; -using Nop.Services.Media; -using Nop.Services.Security; -using Nop.Services.Stores; - -namespace Nop.Plugin.Api.Controllers -{ - using Microsoft.AspNetCore.Authentication.JwtBearer; - using Microsoft.AspNetCore.Mvc; - using Nop.Plugin.Api.DTOs.Errors; - using Nop.Plugin.Api.JSON.Serializers; - - [ApiAuthorize(Policy = JwtBearerDefaults.AuthenticationScheme, AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] - public class ProductCategoryMappingsController : BaseApiController - { - private readonly IProductCategoryMappingsApiService _productCategoryMappingsService; - private readonly ICategoryService _categoryService; - private readonly ICategoryApiService _categoryApiService; - private readonly IProductApiService _productApiService; - - public ProductCategoryMappingsController(IProductCategoryMappingsApiService productCategoryMappingsService, - ICategoryService categoryService, - IJsonFieldsSerializer jsonFieldsSerializer, - IAclService aclService, - ICustomerService customerService, - IStoreMappingService storeMappingService, - IStoreService storeService, - IDiscountService discountService, - ICustomerActivityService customerActivityService, - ILocalizationService localizationService, - ICategoryApiService categoryApiService, - IProductApiService productApiService, - IPictureService pictureService) - : base(jsonFieldsSerializer, aclService, customerService, storeMappingService, storeService, discountService, customerActivityService, localizationService,pictureService) - { - _productCategoryMappingsService = productCategoryMappingsService; - _categoryService = categoryService; - _categoryApiService = categoryApiService; - _productApiService = productApiService; - } - - /// - /// Receive a list of all Product-Category mappings - /// - /// OK - /// Bad Request - /// Unauthorized - [HttpGet] - [Route("/api/product_category_mappings")] - [ProducesResponseType(typeof(ProductCategoryMappingsRootObject), (int)HttpStatusCode.OK)] - [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] - [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] - [GetRequestsErrorInterceptorActionFilter] - public IActionResult GetMappings(ProductCategoryMappingsParametersModel parameters) - { - if (parameters.Limit < Configurations.MinLimit || parameters.Limit > Configurations.MaxLimit) - { - return Error(HttpStatusCode.BadRequest, "limit", "invalid limit parameter"); - } - - if (parameters.Page < Configurations.DefaultPageValue) - { - return Error(HttpStatusCode.BadRequest, "page", "invalid page parameter"); - } - - IList mappingsAsDtos = - _productCategoryMappingsService.GetMappings(parameters.ProductId, - parameters.CategoryId, - parameters.Limit, - parameters.Page, - parameters.SinceId).Select(x => x.ToDto()).ToList(); - - var productCategoryMappingRootObject = new ProductCategoryMappingsRootObject() - { - ProductCategoryMappingDtos = mappingsAsDtos - }; - - var json = _jsonFieldsSerializer.Serialize(productCategoryMappingRootObject, parameters.Fields); - - return new RawJsonActionResult(json); - } - - /// - /// Receive a count of all Product-Category mappings - /// - /// OK - /// Unauthorized - [HttpGet] - [Route("/api/product_category_mappings/count")] - [ProducesResponseType(typeof(ProductCategoryMappingsCountRootObject), (int)HttpStatusCode.OK)] - [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] - [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] - [GetRequestsErrorInterceptorActionFilter] - public IActionResult GetMappingsCount(ProductCategoryMappingsCountParametersModel parameters) - { - if (parameters.ProductId < 0) - { - return Error(HttpStatusCode.BadRequest, "product_id", "invalid product_id"); - } - - if (parameters.CategoryId < 0) - { - return Error(HttpStatusCode.BadRequest, "category_id", "invalid category_id"); - } - - var mappingsCount = _productCategoryMappingsService.GetMappingsCount(parameters.ProductId, - parameters.CategoryId); - - var productCategoryMappingsCountRootObject = new ProductCategoryMappingsCountRootObject() - { - Count = mappingsCount - }; - - return Ok(productCategoryMappingsCountRootObject); - } - - /// - /// Retrieve Product-Category mappings by spcified id - /// - /// /// Id of the Product-Category mapping - /// Fields from the Product-Category mapping you want your json to contain - /// OK - /// Not Found - /// Unauthorized - [HttpGet] - [Route("/api/product_category_mappings/{id}")] - [ProducesResponseType(typeof(ProductCategoryMappingsRootObject), (int)HttpStatusCode.OK)] - [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] - [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] - [ProducesResponseType(typeof(string), (int)HttpStatusCode.NotFound)] - [GetRequestsErrorInterceptorActionFilter] - public IActionResult GetMappingById(int id, string fields = "") - { - if (id <= 0) - { - return Error(HttpStatusCode.BadRequest, "id", "invalid id"); - } - - ProductCategory mapping = _productCategoryMappingsService.GetById(id); - - if (mapping == null) - { - return Error(HttpStatusCode.NotFound, "product_category_mapping", "not found"); - } - - var productCategoryMappingsRootObject = new ProductCategoryMappingsRootObject(); - productCategoryMappingsRootObject.ProductCategoryMappingDtos.Add(mapping.ToDto()); - - var json = _jsonFieldsSerializer.Serialize(productCategoryMappingsRootObject, fields); - - return new RawJsonActionResult(json); - } - - [HttpPost] - [Route("/api/product_category_mappings")] - [ProducesResponseType(typeof(ProductCategoryMappingsRootObject), (int)HttpStatusCode.OK)] - [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] - [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] - [ProducesResponseType(typeof(string), (int)HttpStatusCode.NotFound)] - [ProducesResponseType(typeof(ErrorsRootObject), 422)] - public IActionResult CreateProductCategoryMapping([ModelBinder(typeof(JsonModelBinder))] Delta productCategoryDelta) - { - // Here we display the errors if the validation has failed at some point. - if (!ModelState.IsValid) - { - return Error(); - } - - Category category = _categoryApiService.GetCategoryById(productCategoryDelta.Dto.CategoryId.Value); - if (category == null) - { - return Error(HttpStatusCode.NotFound, "category_id", "not found"); - } - - Product product = _productApiService.GetProductById(productCategoryDelta.Dto.ProductId.Value); - if (product == null) - { - return Error(HttpStatusCode.NotFound, "product_id", "not found"); - } - - int mappingsCount = _productCategoryMappingsService.GetMappingsCount(product.Id, category.Id); - - if (mappingsCount > 0) - { - return Error(HttpStatusCode.BadRequest, "product_category_mapping", "already exist"); - } - - ProductCategory newProductCategory = new ProductCategory(); - productCategoryDelta.Merge(newProductCategory); - - //inserting new category - _categoryService.InsertProductCategory(newProductCategory); - - // Preparing the result dto of the new product category mapping - ProductCategoryMappingDto newProductCategoryMappingDto = newProductCategory.ToDto(); - - var productCategoryMappingsRootObject = new ProductCategoryMappingsRootObject(); - - productCategoryMappingsRootObject.ProductCategoryMappingDtos.Add(newProductCategoryMappingDto); - - var json = _jsonFieldsSerializer.Serialize(productCategoryMappingsRootObject, string.Empty); - - //activity log - _customerActivityService.InsertActivity("AddNewProductCategoryMapping", _localizationService.GetResource("ActivityLog.AddNewProductCategoryMapping"), newProductCategory.Id); - - return new RawJsonActionResult(json); - } - - [HttpPut] - [Route("/api/product_category_mappings/{id}")] - [ProducesResponseType(typeof(ProductCategoryMappingsRootObject), (int)HttpStatusCode.OK)] - [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] - [ProducesResponseType(typeof(string), (int)HttpStatusCode.NotFound)] - [ProducesResponseType(typeof(ErrorsRootObject), 422)] - [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] - public IActionResult UpdateProductCategoryMapping([ModelBinder(typeof(JsonModelBinder))] Delta productCategoryDelta) - { - // Here we display the errors if the validation has failed at some point. - if (!ModelState.IsValid) - { - return Error(); - } - - if (productCategoryDelta.Dto.CategoryId.HasValue) - { - Category category = _categoryApiService.GetCategoryById(productCategoryDelta.Dto.CategoryId.Value); - if (category == null) - { - return Error(HttpStatusCode.NotFound, "category_id", "not found"); - } - } - - if (productCategoryDelta.Dto.ProductId.HasValue) - { - Product product = _productApiService.GetProductById(productCategoryDelta.Dto.ProductId.Value); - if (product == null) - { - return Error(HttpStatusCode.NotFound, "product_id", "not found"); - } - } - - // We do not need to validate the category id, because this will happen in the model binder using the dto validator. - int updateProductCategoryId = productCategoryDelta.Dto.Id; - - ProductCategory productCategoryEntityToUpdate = _categoryService.GetProductCategoryById(updateProductCategoryId); - - if (productCategoryEntityToUpdate == null) - { - return Error(HttpStatusCode.NotFound, "product_category_mapping", "not found"); - } - - productCategoryDelta.Merge(productCategoryEntityToUpdate); - - _categoryService.UpdateProductCategory(productCategoryEntityToUpdate); - - //activity log - _customerActivityService.InsertActivity("UpdateProdutCategoryMapping", - _localizationService.GetResource("ActivityLog.UpdateProdutCategoryMapping"), productCategoryEntityToUpdate.Id); - - ProductCategoryMappingDto updatedProductCategoryDto = productCategoryEntityToUpdate.ToDto(); - - var productCategoriesRootObject = new ProductCategoryMappingsRootObject(); - - productCategoriesRootObject.ProductCategoryMappingDtos.Add(updatedProductCategoryDto); - - var json = _jsonFieldsSerializer.Serialize(productCategoriesRootObject, string.Empty); - - return new RawJsonActionResult(json); - } - - [HttpDelete] - [Route("/api/product_category_mappings/{id}")] - [ProducesResponseType(typeof(void), (int)HttpStatusCode.OK)] - [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] - [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] - [ProducesResponseType(typeof(string), (int)HttpStatusCode.NotFound)] - [GetRequestsErrorInterceptorActionFilter] - public IActionResult DeleteProductCategoryMapping(int id) - { - if (id <= 0) - { - return Error(HttpStatusCode.BadRequest, "id", "invalid id"); - } - - ProductCategory productCategory = _categoryService.GetProductCategoryById(id); - - if (productCategory == null) - { - return Error(HttpStatusCode.NotFound, "product_category_mapping", "not found"); - } - - _categoryService.DeleteProductCategory(productCategory); - - //activity log - _customerActivityService.InsertActivity("DeleteProductCategoryMapping", _localizationService.GetResource("ActivityLog.DeleteProductCategoryMapping"), productCategory.Id); - - return new RawJsonActionResult("{}"); - } - } +using System.Collections.Generic; +using System.Linq; +using System.Net; +using Nop.Core.Domain.Catalog; +using Nop.Plugin.Api.Attributes; +using Nop.Plugin.Api.Constants; +using Nop.Plugin.Api.Delta; +using Nop.Plugin.Api.DTOs.ProductCategoryMappings; +using Nop.Plugin.Api.JSON.ActionResults; +using Nop.Plugin.Api.MappingExtensions; +using Nop.Plugin.Api.ModelBinders; +using Nop.Plugin.Api.Models.ProductCategoryMappingsParameters; +using Nop.Plugin.Api.Services; +using Nop.Services.Catalog; +using Nop.Services.Customers; +using Nop.Services.Discounts; +using Nop.Services.Localization; +using Nop.Services.Logging; +using Nop.Services.Media; +using Nop.Services.Security; +using Nop.Services.Stores; + +namespace Nop.Plugin.Api.Controllers +{ + using Microsoft.AspNetCore.Authentication.JwtBearer; + using Microsoft.AspNetCore.Mvc; + using DTOs.Errors; + using JSON.Serializers; + + [ApiAuthorize(Policy = JwtBearerDefaults.AuthenticationScheme, AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] + public class ProductCategoryMappingsController : BaseApiController + { + private readonly IProductCategoryMappingsApiService _productCategoryMappingsService; + private readonly ICategoryService _categoryService; + private readonly ICategoryApiService _categoryApiService; + private readonly IProductApiService _productApiService; + + public ProductCategoryMappingsController(IProductCategoryMappingsApiService productCategoryMappingsService, + ICategoryService categoryService, + IJsonFieldsSerializer jsonFieldsSerializer, + IAclService aclService, + ICustomerService customerService, + IStoreMappingService storeMappingService, + IStoreService storeService, + IDiscountService discountService, + ICustomerActivityService customerActivityService, + ILocalizationService localizationService, + ICategoryApiService categoryApiService, + IProductApiService productApiService, + IPictureService pictureService) + : base(jsonFieldsSerializer, aclService, customerService, storeMappingService, storeService, discountService, customerActivityService, localizationService,pictureService) + { + _productCategoryMappingsService = productCategoryMappingsService; + _categoryService = categoryService; + _categoryApiService = categoryApiService; + _productApiService = productApiService; + } + + /// + /// Receive a list of all Product-Category mappings + /// + /// OK + /// Bad Request + /// Unauthorized + [HttpGet] + [Route("/api/product_category_mappings")] + [ProducesResponseType(typeof(ProductCategoryMappingsRootObject), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] + [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] + [GetRequestsErrorInterceptorActionFilter] + public IActionResult GetMappings(ProductCategoryMappingsParametersModel parameters) + { + if (parameters.Limit < Configurations.MinLimit || parameters.Limit > Configurations.MaxLimit) + { + return Error(HttpStatusCode.BadRequest, "limit", "invalid limit parameter"); + } + + if (parameters.Page < Configurations.DefaultPageValue) + { + return Error(HttpStatusCode.BadRequest, "page", "invalid page parameter"); + } + + IList mappingsAsDtos = + _productCategoryMappingsService.GetMappings(parameters.ProductId, + parameters.CategoryId, + parameters.Limit, + parameters.Page, + parameters.SinceId).Select(x => x.ToDto()).ToList(); + + var productCategoryMappingRootObject = new ProductCategoryMappingsRootObject() + { + ProductCategoryMappingDtos = mappingsAsDtos + }; + + var json = JsonFieldsSerializer.Serialize(productCategoryMappingRootObject, parameters.Fields); + + return new RawJsonActionResult(json); + } + + /// + /// Receive a count of all Product-Category mappings + /// + /// OK + /// Unauthorized + [HttpGet] + [Route("/api/product_category_mappings/count")] + [ProducesResponseType(typeof(ProductCategoryMappingsCountRootObject), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] + [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] + [GetRequestsErrorInterceptorActionFilter] + public IActionResult GetMappingsCount(ProductCategoryMappingsCountParametersModel parameters) + { + if (parameters.ProductId < 0) + { + return Error(HttpStatusCode.BadRequest, "product_id", "invalid product_id"); + } + + if (parameters.CategoryId < 0) + { + return Error(HttpStatusCode.BadRequest, "category_id", "invalid category_id"); + } + + var mappingsCount = _productCategoryMappingsService.GetMappingsCount(parameters.ProductId, + parameters.CategoryId); + + var productCategoryMappingsCountRootObject = new ProductCategoryMappingsCountRootObject() + { + Count = mappingsCount + }; + + return Ok(productCategoryMappingsCountRootObject); + } + + /// + /// Retrieve Product-Category mappings by spcified id + /// + /// /// Id of the Product-Category mapping + /// Fields from the Product-Category mapping you want your json to contain + /// OK + /// Not Found + /// Unauthorized + [HttpGet] + [Route("/api/product_category_mappings/{id}")] + [ProducesResponseType(typeof(ProductCategoryMappingsRootObject), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] + [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.NotFound)] + [GetRequestsErrorInterceptorActionFilter] + public IActionResult GetMappingById(int id, string fields = "") + { + if (id <= 0) + { + return Error(HttpStatusCode.BadRequest, "id", "invalid id"); + } + + var mapping = _productCategoryMappingsService.GetById(id); + + if (mapping == null) + { + return Error(HttpStatusCode.NotFound, "product_category_mapping", "not found"); + } + + var productCategoryMappingsRootObject = new ProductCategoryMappingsRootObject(); + productCategoryMappingsRootObject.ProductCategoryMappingDtos.Add(mapping.ToDto()); + + var json = JsonFieldsSerializer.Serialize(productCategoryMappingsRootObject, fields); + + return new RawJsonActionResult(json); + } + + [HttpPost] + [Route("/api/product_category_mappings")] + [ProducesResponseType(typeof(ProductCategoryMappingsRootObject), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] + [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.NotFound)] + [ProducesResponseType(typeof(ErrorsRootObject), 422)] + public IActionResult CreateProductCategoryMapping([ModelBinder(typeof(JsonModelBinder))] Delta productCategoryDelta) + { + // Here we display the errors if the validation has failed at some point. + if (!ModelState.IsValid) + { + return Error(); + } + + var category = _categoryApiService.GetCategoryById(productCategoryDelta.Dto.CategoryId.Value); + if (category == null) + { + return Error(HttpStatusCode.NotFound, "category_id", "not found"); + } + + var product = _productApiService.GetProductById(productCategoryDelta.Dto.ProductId.Value); + if (product == null) + { + return Error(HttpStatusCode.NotFound, "product_id", "not found"); + } + + var mappingsCount = _productCategoryMappingsService.GetMappingsCount(product.Id, category.Id); + + if (mappingsCount > 0) + { + return Error(HttpStatusCode.BadRequest, "product_category_mapping", "already exist"); + } + + var newProductCategory = new ProductCategory(); + productCategoryDelta.Merge(newProductCategory); + + //inserting new category + _categoryService.InsertProductCategory(newProductCategory); + + // Preparing the result dto of the new product category mapping + var newProductCategoryMappingDto = newProductCategory.ToDto(); + + var productCategoryMappingsRootObject = new ProductCategoryMappingsRootObject(); + + productCategoryMappingsRootObject.ProductCategoryMappingDtos.Add(newProductCategoryMappingDto); + + var json = JsonFieldsSerializer.Serialize(productCategoryMappingsRootObject, string.Empty); + + //activity log + CustomerActivityService.InsertActivity("AddNewProductCategoryMapping", LocalizationService.GetResource("ActivityLog.AddNewProductCategoryMapping"), newProductCategory); + + return new RawJsonActionResult(json); + } + + [HttpPut] + [Route("/api/product_category_mappings/{id}")] + [ProducesResponseType(typeof(ProductCategoryMappingsRootObject), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.NotFound)] + [ProducesResponseType(typeof(ErrorsRootObject), 422)] + [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] + public IActionResult UpdateProductCategoryMapping([ModelBinder(typeof(JsonModelBinder))] Delta productCategoryDelta) + { + // Here we display the errors if the validation has failed at some point. + if (!ModelState.IsValid) + { + return Error(); + } + + if (productCategoryDelta.Dto.CategoryId.HasValue) + { + var category = _categoryApiService.GetCategoryById(productCategoryDelta.Dto.CategoryId.Value); + if (category == null) + { + return Error(HttpStatusCode.NotFound, "category_id", "not found"); + } + } + + if (productCategoryDelta.Dto.ProductId.HasValue) + { + var product = _productApiService.GetProductById(productCategoryDelta.Dto.ProductId.Value); + if (product == null) + { + return Error(HttpStatusCode.NotFound, "product_id", "not found"); + } + } + + // We do not need to validate the category id, because this will happen in the model binder using the dto validator. + var updateProductCategoryId = productCategoryDelta.Dto.Id; + + var productCategoryEntityToUpdate = _categoryService.GetProductCategoryById(updateProductCategoryId); + + if (productCategoryEntityToUpdate == null) + { + return Error(HttpStatusCode.NotFound, "product_category_mapping", "not found"); + } + + productCategoryDelta.Merge(productCategoryEntityToUpdate); + + _categoryService.UpdateProductCategory(productCategoryEntityToUpdate); + + //activity log + CustomerActivityService.InsertActivity("UpdateProdutCategoryMapping", + LocalizationService.GetResource("ActivityLog.UpdateProdutCategoryMapping"), productCategoryEntityToUpdate); + + var updatedProductCategoryDto = productCategoryEntityToUpdate.ToDto(); + + var productCategoriesRootObject = new ProductCategoryMappingsRootObject(); + + productCategoriesRootObject.ProductCategoryMappingDtos.Add(updatedProductCategoryDto); + + var json = JsonFieldsSerializer.Serialize(productCategoriesRootObject, string.Empty); + + return new RawJsonActionResult(json); + } + + [HttpDelete] + [Route("/api/product_category_mappings/{id}")] + [ProducesResponseType(typeof(void), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] + [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.NotFound)] + [GetRequestsErrorInterceptorActionFilter] + public IActionResult DeleteProductCategoryMapping(int id) + { + if (id <= 0) + { + return Error(HttpStatusCode.BadRequest, "id", "invalid id"); + } + + var productCategory = _categoryService.GetProductCategoryById(id); + + if (productCategory == null) + { + return Error(HttpStatusCode.NotFound, "product_category_mapping", "not found"); + } + + _categoryService.DeleteProductCategory(productCategory); + + //activity log + CustomerActivityService.InsertActivity("DeleteProductCategoryMapping", LocalizationService.GetResource("ActivityLog.DeleteProductCategoryMapping"), productCategory); + + return new RawJsonActionResult("{}"); + } + } } \ No newline at end of file diff --git a/Nop.Plugin.Api/Controllers/ProductManufacturerMappingsController.cs b/Nop.Plugin.Api/Controllers/ProductManufacturerMappingsController.cs new file mode 100644 index 0000000..9af0b75 --- /dev/null +++ b/Nop.Plugin.Api/Controllers/ProductManufacturerMappingsController.cs @@ -0,0 +1,317 @@ +using System.Collections.Generic; +using System.Linq; +using System.Net; +using Nop.Core.Domain.Catalog; +using Nop.Plugin.Api.Attributes; +using Nop.Plugin.Api.Constants; +using Nop.Plugin.Api.Delta; +using Nop.Plugin.Api.DTOs.ProductManufacturerMappings; +using Nop.Plugin.Api.JSON.ActionResults; +using Nop.Plugin.Api.MappingExtensions; +using Nop.Plugin.Api.ModelBinders; +using Nop.Plugin.Api.Models.ProductManufacturerMappingsParameters; +using Nop.Plugin.Api.Services; +using Nop.Services.Catalog; +using Nop.Services.Customers; +using Nop.Services.Discounts; +using Nop.Services.Localization; +using Nop.Services.Logging; +using Nop.Services.Media; +using Nop.Services.Security; +using Nop.Services.Stores; + +namespace Nop.Plugin.Api.Controllers +{ + using Microsoft.AspNetCore.Authentication.JwtBearer; + using Microsoft.AspNetCore.Mvc; + using DTOs.Errors; + using JSON.Serializers; + + [ApiAuthorize(Policy = JwtBearerDefaults.AuthenticationScheme, AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] + public class ProductManufacturerMappingsController : BaseApiController + { + private readonly IProductManufacturerMappingsApiService _productManufacturerMappingsService; + private readonly IManufacturerService _manufacturerService; + private readonly IManufacturerApiService _manufacturerApiService; + private readonly IProductApiService _productApiService; + + public ProductManufacturerMappingsController(IProductManufacturerMappingsApiService productManufacturerMappingsService, + IManufacturerService manufacturerService, + IJsonFieldsSerializer jsonFieldsSerializer, + IAclService aclService, + ICustomerService customerService, + IStoreMappingService storeMappingService, + IStoreService storeService, + IDiscountService discountService, + ICustomerActivityService customerActivityService, + ILocalizationService localizationService, + IManufacturerApiService manufacturerApiService, + IProductApiService productApiService, + IPictureService pictureService) + : base(jsonFieldsSerializer, aclService, customerService, storeMappingService, storeService, discountService, customerActivityService, localizationService,pictureService) + { + _productManufacturerMappingsService = productManufacturerMappingsService; + _manufacturerService = manufacturerService; + _manufacturerApiService = manufacturerApiService; + _productApiService = productApiService; + } + + /// + /// Receive a list of all Product-Manufacturer mappings + /// + /// OK + /// Bad Request + /// Unauthorized + [HttpGet] + [Route("/api/product_manufacturer_mappings")] + [ProducesResponseType(typeof(ProductManufacturerMappingsRootObject), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] + [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] + [GetRequestsErrorInterceptorActionFilter] + public IActionResult GetMappings(ProductManufacturerMappingsParametersModel parameters) + { + if (parameters.Limit < Configurations.MinLimit || parameters.Limit > Configurations.MaxLimit) + { + return Error(HttpStatusCode.BadRequest, "limit", "invalid limit parameter"); + } + + if (parameters.Page < Configurations.DefaultPageValue) + { + return Error(HttpStatusCode.BadRequest, "page", "invalid page parameter"); + } + + IList mappingsAsDtos = + _productManufacturerMappingsService.GetMappings(parameters.ProductId, + parameters.ManufacturerId, + parameters.Limit, + parameters.Page, + parameters.SinceId).Select(x => x.ToDto()).ToList(); + + var productManufacturerMappingRootObject = new ProductManufacturerMappingsRootObject() + { + ProductManufacturerMappingsDtos = mappingsAsDtos + }; + + var json = JsonFieldsSerializer.Serialize(productManufacturerMappingRootObject, parameters.Fields); + + return new RawJsonActionResult(json); + } + + /// + /// Receive a count of all Product-Manufacturer mappings + /// + /// OK + /// Unauthorized + [HttpGet] + [Route("/api/product_manufacturer_mappings/count")] + [ProducesResponseType(typeof(ProductManufacturerMappingsCountRootObject), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] + [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] + [GetRequestsErrorInterceptorActionFilter] + public IActionResult GetMappingsCount(ProductManufacturerMappingsCountParametersModel parameters) + { + if (parameters.ProductId < 0) + { + return Error(HttpStatusCode.BadRequest, "product_id", "invalid product_id"); + } + + if (parameters.ManufacturerId < 0) + { + return Error(HttpStatusCode.BadRequest, "manufacturer_id", "invalid manufacturer_id"); + } + + var mappingsCount = _productManufacturerMappingsService.GetMappingsCount(parameters.ProductId, + parameters.ManufacturerId); + + var productManufacturerMappingsCountRootObject = new ProductManufacturerMappingsCountRootObject() + { + Count = mappingsCount + }; + + return Ok(productManufacturerMappingsCountRootObject); + } + + /// + /// Retrieve Product-Manufacturer mappings by spcified id + /// + /// /// Id of the Product-Manufacturer mapping + /// Fields from the Product-Manufacturer mapping you want your json to contain + /// OK + /// Not Found + /// Unauthorized + [HttpGet] + [Route("/api/product_manufacturer_mappings/{id}")] + [ProducesResponseType(typeof(ProductManufacturerMappingsRootObject), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] + [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.NotFound)] + [GetRequestsErrorInterceptorActionFilter] + public IActionResult GetMappingById(int id, string fields = "") + { + if (id <= 0) + { + return Error(HttpStatusCode.BadRequest, "id", "invalid id"); + } + + var mapping = _productManufacturerMappingsService.GetById(id); + + if (mapping == null) + { + return Error(HttpStatusCode.NotFound, "product_manufacturer_mapping", "not found"); + } + + var productManufacturerMappingsRootObject = new ProductManufacturerMappingsRootObject(); + productManufacturerMappingsRootObject.ProductManufacturerMappingsDtos.Add(mapping.ToDto()); + + var json = JsonFieldsSerializer.Serialize(productManufacturerMappingsRootObject, fields); + + return new RawJsonActionResult(json); + } + + [HttpPost] + [Route("/api/product_manufacturer_mappings")] + [ProducesResponseType(typeof(ProductManufacturerMappingsRootObject), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] + [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.NotFound)] + [ProducesResponseType(typeof(ErrorsRootObject), 422)] + public IActionResult CreateProductManufacturerMapping([ModelBinder(typeof(JsonModelBinder))] Delta productManufacturerDelta) + { + // Here we display the errors if the validation has failed at some point. + if (!ModelState.IsValid) + { + return Error(); + } + + var Manufacturer = _manufacturerApiService.GetManufacturerById(productManufacturerDelta.Dto.ManufacturerId.Value); + if (Manufacturer == null) + { + return Error(HttpStatusCode.NotFound, "manufacturer_id", "not found"); + } + + var product = _productApiService.GetProductById(productManufacturerDelta.Dto.ProductId.Value); + if (product == null) + { + return Error(HttpStatusCode.NotFound, "product_id", "not found"); + } + + var mappingsCount = _productManufacturerMappingsService.GetMappingsCount(product.Id, Manufacturer.Id); + + if (mappingsCount > 0) + { + return Error(HttpStatusCode.BadRequest, "product_manufacturer_mapping", "already exist"); + } + + var newProductManufacturer = new ProductManufacturer(); + productManufacturerDelta.Merge(newProductManufacturer); + + //inserting new Manufacturer + _manufacturerService.InsertProductManufacturer(newProductManufacturer); + + // Preparing the result dto of the new product Manufacturer mapping + var newProductManufacturerMappingDto = newProductManufacturer.ToDto(); + + var productManufacturerMappingsRootObject = new ProductManufacturerMappingsRootObject(); + + productManufacturerMappingsRootObject.ProductManufacturerMappingsDtos.Add(newProductManufacturerMappingDto); + + var json = JsonFieldsSerializer.Serialize(productManufacturerMappingsRootObject, string.Empty); + + //activity log + CustomerActivityService.InsertActivity("AddNewProductManufacturerMapping", LocalizationService.GetResource("ActivityLog.AddNewProductManufacturerMapping"), newProductManufacturer); + + return new RawJsonActionResult(json); + } + + [HttpPut] + [Route("/api/product_manufacturer_mappings/{id}")] + [ProducesResponseType(typeof(ProductManufacturerMappingsRootObject), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.NotFound)] + [ProducesResponseType(typeof(ErrorsRootObject), 422)] + [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] + public IActionResult UpdateProductManufacturerMapping([ModelBinder(typeof(JsonModelBinder))] Delta productManufacturerDelta) + { + // Here we display the errors if the validation has failed at some point. + if (!ModelState.IsValid) + { + return Error(); + } + + if (productManufacturerDelta.Dto.ManufacturerId.HasValue) + { + var Manufacturer = _manufacturerApiService.GetManufacturerById(productManufacturerDelta.Dto.ManufacturerId.Value); + if (Manufacturer == null) + { + return Error(HttpStatusCode.NotFound, "manufacturer_id", "not found"); + } + } + + if (productManufacturerDelta.Dto.ProductId.HasValue) + { + var product = _productApiService.GetProductById(productManufacturerDelta.Dto.ProductId.Value); + if (product == null) + { + return Error(HttpStatusCode.NotFound, "product_id", "not found"); + } + } + + // We do not need to validate the Manufacturer id, because this will happen in the model binder using the dto validator. + var updateProductManufacturerId = productManufacturerDelta.Dto.Id; + + var productManufacturerEntityToUpdate = _manufacturerService.GetProductManufacturerById(updateProductManufacturerId); + + if (productManufacturerEntityToUpdate == null) + { + return Error(HttpStatusCode.NotFound, "product_manufacturer_mapping", "not found"); + } + + productManufacturerDelta.Merge(productManufacturerEntityToUpdate); + + _manufacturerService.UpdateProductManufacturer(productManufacturerEntityToUpdate); + + //activity log + CustomerActivityService.InsertActivity("UpdateProdutManufacturerMapping", + LocalizationService.GetResource("ActivityLog.UpdateProdutManufacturerMapping"), productManufacturerEntityToUpdate); + + var updatedProductManufacturerDto = productManufacturerEntityToUpdate.ToDto(); + + var productManufacturersRootObject = new ProductManufacturerMappingsRootObject(); + + productManufacturersRootObject.ProductManufacturerMappingsDtos.Add(updatedProductManufacturerDto); + + var json = JsonFieldsSerializer.Serialize(productManufacturersRootObject, string.Empty); + + return new RawJsonActionResult(json); + } + + [HttpDelete] + [Route("/api/product_manufacturer_mappings/{id}")] + [ProducesResponseType(typeof(void), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] + [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.NotFound)] + [GetRequestsErrorInterceptorActionFilter] + public IActionResult DeleteProductManufacturerMapping(int id) + { + if (id <= 0) + { + return Error(HttpStatusCode.BadRequest, "id", "invalid id"); + } + + var productManufacturer = _manufacturerService.GetProductManufacturerById(id); + + if (productManufacturer == null) + { + return Error(HttpStatusCode.NotFound, "product_manufacturer_mapping", "not found"); + } + + _manufacturerService.DeleteProductManufacturer(productManufacturer); + + //activity log + CustomerActivityService.InsertActivity("DeleteProductManufacturerMapping", LocalizationService.GetResource("ActivityLog.DeleteProductManufacturerMapping"), productManufacturer); + + return new RawJsonActionResult("{}"); + } + } +} \ No newline at end of file diff --git a/Nop.Plugin.Api/Controllers/ProductSpecificationAttributesController.cs b/Nop.Plugin.Api/Controllers/ProductSpecificationAttributesController.cs index f87de4c..f122135 100644 --- a/Nop.Plugin.Api/Controllers/ProductSpecificationAttributesController.cs +++ b/Nop.Plugin.Api/Controllers/ProductSpecificationAttributesController.cs @@ -20,7 +20,6 @@ using Nop.Services.Media; using Nop.Services.Security; using Nop.Services.Stores; -using System; using System.Linq; using System.Net; @@ -84,7 +83,7 @@ public IActionResult GetProductSpecificationAttributes(ProductSpecifcationAttrib ProductSpecificationAttributes = productSpecificationAttributeDtos }; - var json = _jsonFieldsSerializer.Serialize(productSpecificationAttributesRootObject, parameters.Fields); + var json = JsonFieldsSerializer.Serialize(productSpecificationAttributesRootObject, parameters.Fields); return new RawJsonActionResult(json); } @@ -146,7 +145,7 @@ public IActionResult GetProductSpecificationAttributeById(int id, string fields var productSpecificationAttributesRootObject = new ProductSpecificationAttributesRootObjectDto(); productSpecificationAttributesRootObject.ProductSpecificationAttributes.Add(productSpecificationAttributeDto); - var json = _jsonFieldsSerializer.Serialize(productSpecificationAttributesRootObject, fields); + var json = JsonFieldsSerializer.Serialize(productSpecificationAttributesRootObject, fields); return new RawJsonActionResult(json); } @@ -172,7 +171,7 @@ public IActionResult CreateProductSpecificationAttribute([ModelBinder(typeof(Jso _specificationAttributeService.InsertProductSpecificationAttribute(productSpecificationAttribute); - _customerActivityService.InsertActivity("AddNewProductSpecificationAttribute", productSpecificationAttribute.Id.ToString()); + CustomerActivityService.InsertActivity("AddNewProductSpecificationAttribute", productSpecificationAttribute.Id.ToString()); // Preparing the result dto of the new product var productSpecificationAttributeDto = _dtoHelper.PrepareProductSpecificationAttributeDto(productSpecificationAttribute); @@ -180,7 +179,7 @@ public IActionResult CreateProductSpecificationAttribute([ModelBinder(typeof(Jso var productSpecificationAttributesRootObjectDto = new ProductSpecificationAttributesRootObjectDto(); productSpecificationAttributesRootObjectDto.ProductSpecificationAttributes.Add(productSpecificationAttributeDto); - var json = _jsonFieldsSerializer.Serialize(productSpecificationAttributesRootObjectDto, string.Empty); + var json = JsonFieldsSerializer.Serialize(productSpecificationAttributesRootObjectDto, string.Empty); return new RawJsonActionResult(json); } @@ -213,7 +212,7 @@ public IActionResult UpdateProductSpecificationAttribute([ModelBinder(typeof(Jso _specificationAttributeService.UpdateProductSpecificationAttribute(productSpecificationAttribute); - _customerActivityService.InsertActivity("EditProductSpecificationAttribute", productSpecificationAttribute.Id.ToString()); + CustomerActivityService.InsertActivity("EditProductSpecificationAttribute", productSpecificationAttribute.Id.ToString()); // Preparing the result dto of the new product attribute var productSpecificationAttributeDto = _dtoHelper.PrepareProductSpecificationAttributeDto(productSpecificationAttribute); @@ -221,7 +220,7 @@ public IActionResult UpdateProductSpecificationAttribute([ModelBinder(typeof(Jso var productSpecificatoinAttributesRootObjectDto = new ProductSpecificationAttributesRootObjectDto(); productSpecificatoinAttributesRootObjectDto.ProductSpecificationAttributes.Add(productSpecificationAttributeDto); - var json = _jsonFieldsSerializer.Serialize(productSpecificatoinAttributesRootObjectDto, string.Empty); + var json = JsonFieldsSerializer.Serialize(productSpecificatoinAttributesRootObjectDto, string.Empty); return new RawJsonActionResult(json); } @@ -249,7 +248,7 @@ public IActionResult DeleteProductSpecificationAttribute(int id) _specificationAttributeService.DeleteProductSpecificationAttribute(productSpecificationAttribute); //activity log - _customerActivityService.InsertActivity("DeleteProductSpecificationAttribute", _localizationService.GetResource("ActivityLog.DeleteProductSpecificationAttribute"), productSpecificationAttribute.Id.ToString()); + CustomerActivityService.InsertActivity("DeleteProductSpecificationAttribute", LocalizationService.GetResource("ActivityLog.DeleteProductSpecificationAttribute"), productSpecificationAttribute); return new RawJsonActionResult("{}"); } diff --git a/Nop.Plugin.Api/Controllers/ProductsController.cs b/Nop.Plugin.Api/Controllers/ProductsController.cs index eab0153..8b1914e 100644 --- a/Nop.Plugin.Api/Controllers/ProductsController.cs +++ b/Nop.Plugin.Api/Controllers/ProductsController.cs @@ -1,609 +1,609 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net; -using Nop.Core.Domain.Catalog; -using Nop.Core.Domain.Discounts; -using Nop.Core.Domain.Media; -using Nop.Plugin.Api.Attributes; -using Nop.Plugin.Api.Constants; -using Nop.Plugin.Api.Delta; -using Nop.Plugin.Api.DTOs.Images; -using Nop.Plugin.Api.DTOs.Products; -using Nop.Plugin.Api.Factories; -using Nop.Plugin.Api.JSON.ActionResults; -using Nop.Plugin.Api.ModelBinders; -using Nop.Plugin.Api.Models.ProductsParameters; -using Nop.Plugin.Api.Services; -using Nop.Services.Catalog; -using Nop.Services.Customers; -using Nop.Services.Discounts; -using Nop.Services.Localization; -using Nop.Services.Logging; -using Nop.Services.Media; -using Nop.Services.Security; -using Nop.Services.Seo; -using Nop.Services.Stores; -using Nop.Plugin.Api.Helpers; - -namespace Nop.Plugin.Api.Controllers -{ - using Microsoft.AspNetCore.Authentication.JwtBearer; - using Microsoft.AspNetCore.Mvc; - using Nop.Plugin.Api.DTOs.Errors; - using Nop.Plugin.Api.JSON.Serializers; - - [ApiAuthorize(Policy = JwtBearerDefaults.AuthenticationScheme, AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] - public class ProductsController : BaseApiController - { - private readonly IProductApiService _productApiService; - private readonly IProductService _productService; - private readonly IUrlRecordService _urlRecordService; - private readonly IPictureService _pictureService; - private readonly IManufacturerService _manufacturerService; - private readonly IFactory _factory; - private readonly IProductTagService _productTagService; - private readonly IProductAttributeService _productAttributeService; - private readonly IDTOHelper _dtoHelper; - - public ProductsController(IProductApiService productApiService, - IJsonFieldsSerializer jsonFieldsSerializer, - IProductService productService, - IUrlRecordService urlRecordService, - ICustomerActivityService customerActivityService, - ILocalizationService localizationService, - IFactory factory, - IAclService aclService, - IStoreMappingService storeMappingService, - IStoreService storeService, - ICustomerService customerService, - IDiscountService discountService, - IPictureService pictureService, - IManufacturerService manufacturerService, - IProductTagService productTagService, - IProductAttributeService productAttributeService, - IDTOHelper dtoHelper) : base(jsonFieldsSerializer, aclService, customerService, storeMappingService, storeService, discountService, customerActivityService, localizationService, pictureService) - { - _productApiService = productApiService; - _factory = factory; - _manufacturerService = manufacturerService; - _productTagService = productTagService; - _urlRecordService = urlRecordService; - _productService = productService; - _productAttributeService = productAttributeService; - _dtoHelper = dtoHelper; - _pictureService = pictureService; - } - - /// - /// Receive a list of all products - /// - /// OK - /// Bad Request - /// Unauthorized - [HttpGet] - [Route("/api/products")] - [ProducesResponseType(typeof(ProductsRootObjectDto), (int)HttpStatusCode.OK)] - [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] - [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] - [GetRequestsErrorInterceptorActionFilter] - public IActionResult GetProducts(ProductsParametersModel parameters) - { - if (parameters.Limit < Configurations.MinLimit || parameters.Limit > Configurations.MaxLimit) - { - return Error(HttpStatusCode.BadRequest, "limit", "invalid limit parameter"); - } - - if (parameters.Page < Configurations.DefaultPageValue) - { - return Error(HttpStatusCode.BadRequest, "page", "invalid page parameter"); - } - - var allProducts = _productApiService.GetProducts(parameters.Ids, parameters.CreatedAtMin, parameters.CreatedAtMax, parameters.UpdatedAtMin, - parameters.UpdatedAtMax, parameters.Limit, parameters.Page, parameters.SinceId, parameters.CategoryId, - parameters.VendorName, parameters.PublishedStatus) - .Where(p => _storeMappingService.Authorize(p)); - - IList productsAsDtos = allProducts.Select(product => - { - return _dtoHelper.PrepareProductDTO(product); - - }).ToList(); - - var productsRootObject = new ProductsRootObjectDto() - { - Products = productsAsDtos - }; - - var json = _jsonFieldsSerializer.Serialize(productsRootObject, parameters.Fields); - - return new RawJsonActionResult(json); - } - - /// - /// Receive a count of all products - /// - /// OK - /// Unauthorized - [HttpGet] - [Route("/api/products/count")] - [ProducesResponseType(typeof(ProductsCountRootObject), (int)HttpStatusCode.OK)] - [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] - [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] - [GetRequestsErrorInterceptorActionFilter] - public IActionResult GetProductsCount(ProductsCountParametersModel parameters) - { - int allProductsCount = _productApiService.GetProductsCount(parameters.CreatedAtMin, parameters.CreatedAtMax, parameters.UpdatedAtMin, - parameters.UpdatedAtMax, parameters.PublishedStatus, parameters.VendorName, - parameters.CategoryId); - - var productsCountRootObject = new ProductsCountRootObject() - { - Count = allProductsCount - }; - - return Ok(productsCountRootObject); - } - - /// - /// Retrieve product by spcified id - /// - /// Id of the product - /// Fields from the product you want your json to contain - /// OK - /// Not Found - /// Unauthorized - [HttpGet] - [Route("/api/products/{id}")] - [ProducesResponseType(typeof(ProductsRootObjectDto), (int)HttpStatusCode.OK)] - [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] - [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] - [ProducesResponseType(typeof(string), (int)HttpStatusCode.NotFound)] - [GetRequestsErrorInterceptorActionFilter] - public IActionResult GetProductById(int id, string fields = "") - { - if (id <= 0) - { - return Error(HttpStatusCode.BadRequest, "id", "invalid id"); - } - - Product product = _productApiService.GetProductById(id); - - if (product == null) - { - return Error(HttpStatusCode.NotFound, "product", "not found"); - } - - ProductDto productDto = _dtoHelper.PrepareProductDTO(product); - - var productsRootObject = new ProductsRootObjectDto(); - - productsRootObject.Products.Add(productDto); - - var json = _jsonFieldsSerializer.Serialize(productsRootObject, fields); - - return new RawJsonActionResult(json); - } - - [HttpPost] - [Route("/api/products")] - [ProducesResponseType(typeof(ProductsRootObjectDto), (int)HttpStatusCode.OK)] - [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] - [ProducesResponseType(typeof(ErrorsRootObject), 422)] - public IActionResult CreateProduct([ModelBinder(typeof(JsonModelBinder))] Delta productDelta) - { - // Here we display the errors if the validation has failed at some point. - if (!ModelState.IsValid) - { - return Error(); - } - - // Inserting the new product - Product product = _factory.Initialize(); - productDelta.Merge(product); - - _productService.InsertProduct(product); - - UpdateProductPictures(product, productDelta.Dto.Images); - - UpdateProductTags(product, productDelta.Dto.Tags); - - UpdateProductManufacturers(product, productDelta.Dto.ManufacturerIds); - - UpdateAssociatedProducts(product, productDelta.Dto.AssociatedProductIds); - - //search engine name - var seName = product.ValidateSeName(productDelta.Dto.SeName, product.Name, true); - _urlRecordService.SaveSlug(product, seName, 0); - - UpdateAclRoles(product, productDelta.Dto.RoleIds); - - UpdateDiscountMappings(product, productDelta.Dto.DiscountIds); - - UpdateStoreMappings(product, productDelta.Dto.StoreIds); - - _productService.UpdateProduct(product); - - _customerActivityService.InsertActivity("AddNewProduct", - _localizationService.GetResource("ActivityLog.AddNewProduct"), product.Name); - - // Preparing the result dto of the new product - ProductDto productDto = _dtoHelper.PrepareProductDTO(product); - - var productsRootObject = new ProductsRootObjectDto(); - - productsRootObject.Products.Add(productDto); - - var json = _jsonFieldsSerializer.Serialize(productsRootObject, string.Empty); - - return new RawJsonActionResult(json); - } - - [HttpPut] - [Route("/api/products/{id}")] - [ProducesResponseType(typeof(ProductsRootObjectDto), (int)HttpStatusCode.OK)] - [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] - [ProducesResponseType(typeof(string), (int)HttpStatusCode.NotFound)] - [ProducesResponseType(typeof(ErrorsRootObject), 422)] - [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] - public IActionResult UpdateProduct([ModelBinder(typeof(JsonModelBinder))] Delta productDelta) - { - // Here we display the errors if the validation has failed at some point. - if (!ModelState.IsValid) - { - return Error(); - } - - // We do not need to validate the product id, because this will happen in the model binder using the dto validator. - int productId = int.Parse(productDelta.Dto.Id); - - Product product = _productApiService.GetProductById(productId); - - if (product == null) - { - return Error(HttpStatusCode.NotFound, "product", "not found"); - } - - productDelta.Merge(product,true); - - product.UpdatedOnUtc = DateTime.UtcNow; - _productService.UpdateProduct(product); - - UpdateProductAttributes(product, productDelta); - - UpdateProductPictures(product, productDelta.Dto.Images); - - UpdateProductTags(product, productDelta.Dto.Tags); - - UpdateProductManufacturers(product, productDelta.Dto.ManufacturerIds); - - UpdateAssociatedProducts(product, productDelta.Dto.AssociatedProductIds); - - // Update the SeName if specified - if (productDelta.Dto.SeName != null) - { - var seName = product.ValidateSeName(productDelta.Dto.SeName, product.Name, true); - _urlRecordService.SaveSlug(product, seName, 0); - } - - UpdateDiscountMappings(product, productDelta.Dto.DiscountIds); - - UpdateStoreMappings(product, productDelta.Dto.StoreIds); - - UpdateAclRoles(product, productDelta.Dto.RoleIds); - - _productService.UpdateProduct(product); - - _customerActivityService.InsertActivity("UpdateProduct", - _localizationService.GetResource("ActivityLog.UpdateProduct"), product.Name); - - // Preparing the result dto of the new product - ProductDto productDto = _dtoHelper.PrepareProductDTO(product); - - var productsRootObject = new ProductsRootObjectDto(); - - productsRootObject.Products.Add(productDto); - - var json = _jsonFieldsSerializer.Serialize(productsRootObject, string.Empty); - - return new RawJsonActionResult(json); - } - - [HttpDelete] - [Route("/api/products/{id}")] - [ProducesResponseType(typeof(void), (int)HttpStatusCode.OK)] - [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] - [ProducesResponseType(typeof(string), (int)HttpStatusCode.NotFound)] - [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] - [GetRequestsErrorInterceptorActionFilter] - public IActionResult DeleteProduct(int id) - { - if (id <= 0) - { - return Error(HttpStatusCode.BadRequest, "id", "invalid id"); - } - - Product product = _productApiService.GetProductById(id); - - if (product == null) - { - return Error(HttpStatusCode.NotFound, "product", "not found"); - } - - _productService.DeleteProduct(product); - - //activity log - _customerActivityService.InsertActivity("DeleteProduct", _localizationService.GetResource("ActivityLog.DeleteProduct"), product.Name); - - return new RawJsonActionResult("{}"); - } - - private void UpdateProductPictures(Product entityToUpdate, List setPictures) - { - // If no pictures are specified means we don't have to update anything - if (setPictures == null) - return; - - // delete unused product pictures - var unusedProductPictures = entityToUpdate.ProductPictures.Where(x => setPictures.All(y => y.Id != x.Id)).ToList(); - foreach (var unusedProductPicture in unusedProductPictures) - { - var picture = _pictureService.GetPictureById(unusedProductPicture.PictureId); - if (picture == null) - throw new ArgumentException("No picture found with the specified id"); - _pictureService.DeletePicture(picture); - } - - foreach (var imageDto in setPictures) - { - if (imageDto.Id > 0) - { - // update existing product picture - var productPictureToUpdate = entityToUpdate.ProductPictures.FirstOrDefault(x => x.Id == imageDto.Id); - if (productPictureToUpdate != null && imageDto.Position > 0) - { - productPictureToUpdate.DisplayOrder = imageDto.Position; - _productService.UpdateProductPicture(productPictureToUpdate); - } - } - else - { - // add new product picture - Picture newPicture = _pictureService.InsertPicture(imageDto.Binary, imageDto.MimeType, string.Empty); - _productService.InsertProductPicture(new ProductPicture() - { - PictureId = newPicture.Id, - ProductId = entityToUpdate.Id, - DisplayOrder = imageDto.Position - }); - } - } - } - - private void UpdateProductAttributes(Product entityToUpdate, Delta productDtoDelta) - { - // If no product attribute mappings are specified means we don't have to update anything - if (productDtoDelta.Dto.ProductAttributeMappings == null) - return; - - // delete unused product attribute mappings - IEnumerable toBeUpdatedIds = productDtoDelta.Dto.ProductAttributeMappings.Where(y => y.Id != 0).Select(x => x.Id); - - var unusedProductAttributeMappings = entityToUpdate.ProductAttributeMappings.Where(x => !toBeUpdatedIds.Contains(x.Id)).ToList(); - - foreach (var unusedProductAttributeMapping in unusedProductAttributeMappings) - { - _productAttributeService.DeleteProductAttributeMapping(unusedProductAttributeMapping); - } - - foreach (var productAttributeMappingDto in productDtoDelta.Dto.ProductAttributeMappings) - { - if (productAttributeMappingDto.Id > 0) - { - // update existing product attribute mapping - var productAttributeMappingToUpdate = entityToUpdate.ProductAttributeMappings.FirstOrDefault(x => x.Id == productAttributeMappingDto.Id); - if (productAttributeMappingToUpdate != null) - { - productDtoDelta.Merge(productAttributeMappingDto,productAttributeMappingToUpdate,false); - - _productAttributeService.UpdateProductAttributeMapping(productAttributeMappingToUpdate); - - UpdateProductAttributeValues(productAttributeMappingDto, productDtoDelta); - } - } - else - { - ProductAttributeMapping newProductAttributeMapping = new ProductAttributeMapping(); - newProductAttributeMapping.ProductId = entityToUpdate.Id; - - productDtoDelta.Merge(productAttributeMappingDto, newProductAttributeMapping); - - // add new product attribute - _productAttributeService.InsertProductAttributeMapping(newProductAttributeMapping); - } - } - } - - private void UpdateProductAttributeValues(ProductAttributeMappingDto productAttributeMappingDto, Delta productDtoDelta) - { - // If no product attribute values are specified means we don't have to update anything - if (productAttributeMappingDto.ProductAttributeValues == null) - return; - - // delete unused product attribute values - IEnumerable toBeUpdatedIds = productAttributeMappingDto.ProductAttributeValues.Where(y => y.Id != 0).Select(x => x.Id); - - var unusedProductAttributeValues = - _productAttributeService.GetProductAttributeValues(productAttributeMappingDto.Id).Where(x => !toBeUpdatedIds.Contains(x.Id)).ToList(); ; - - foreach (var unusedProductAttributeValue in unusedProductAttributeValues) - { - _productAttributeService.DeleteProductAttributeValue(unusedProductAttributeValue); - } - - foreach (var productAttributeValueDto in productAttributeMappingDto.ProductAttributeValues) - { - if (productAttributeValueDto.Id > 0) - { - // update existing product attribute mapping - var productAttributeValueToUpdate = - _productAttributeService.GetProductAttributeValueById(productAttributeValueDto.Id); - if (productAttributeValueToUpdate != null) - { - productDtoDelta.Merge(productAttributeValueDto, productAttributeValueToUpdate, false); - - _productAttributeService.UpdateProductAttributeValue(productAttributeValueToUpdate); - } - } - else - { - ProductAttributeValue newProductAttributeValue = new ProductAttributeValue(); - productDtoDelta.Merge(productAttributeValueDto, newProductAttributeValue); - - newProductAttributeValue.ProductAttributeMappingId = productAttributeMappingDto.Id; - // add new product attribute value - _productAttributeService.InsertProductAttributeValue(newProductAttributeValue); - } - } - } - - private void UpdateProductTags(Product product, List productTags) - { - if (productTags == null) - return; - - if (product == null) - throw new ArgumentNullException("product"); - - //product tags - var existingProductTags = product.ProductTags.ToList(); - var productTagsToRemove = new List(); - foreach (var existingProductTag in existingProductTags) - { - bool found = false; - foreach (string newProductTag in productTags) - { - if (existingProductTag.Name.Equals(newProductTag, StringComparison.InvariantCultureIgnoreCase)) - { - found = true; - break; - } - } - if (!found) - { - productTagsToRemove.Add(existingProductTag); - } - } - foreach (var productTag in productTagsToRemove) - { - product.ProductTags.Remove(productTag); - _productService.UpdateProduct(product); - } - foreach (string productTagName in productTags) - { - ProductTag productTag; - var productTag2 = _productTagService.GetProductTagByName(productTagName); - if (productTag2 == null) - { - //add new product tag - productTag = new ProductTag - { - Name = productTagName - }; - _productTagService.InsertProductTag(productTag); - } - else - { - productTag = productTag2; - } - if (!product.ProductTagExists(productTag.Id)) - { - product.ProductTags.Add(productTag); - _productService.UpdateProduct(product); - } - } - } - - private void UpdateDiscountMappings(Product product, List passedDiscountIds) - { - if (passedDiscountIds == null) - return; - - var allDiscounts = _discountService.GetAllDiscounts(DiscountType.AssignedToSkus, showHidden: true); - - foreach (var discount in allDiscounts) - { - if (passedDiscountIds.Contains(discount.Id)) - { - //new discount - if (product.AppliedDiscounts.Count(d => d.Id == discount.Id) == 0) - product.AppliedDiscounts.Add(discount); - } - else - { - //remove discount - if (product.AppliedDiscounts.Count(d => d.Id == discount.Id) > 0) - product.AppliedDiscounts.Remove(discount); - } - } - - _productService.UpdateProduct(product); - _productService.UpdateHasDiscountsApplied(product); - } - - private void UpdateProductManufacturers(Product product, List passedManufacturerIds) - { - // If no manufacturers specified then there is nothing to map - if (passedManufacturerIds == null) - return; - - var unusedProductManufacturers = product.ProductManufacturers.Where(x => !passedManufacturerIds.Contains(x.ManufacturerId)).ToList(); - - // remove all manufacturers that are not passed - foreach (var unusedProductManufacturer in unusedProductManufacturers) - { - _manufacturerService.DeleteProductManufacturer(unusedProductManufacturer); - } - - foreach (var passedManufacturerId in passedManufacturerIds) - { - // not part of existing manufacturers so we will create a new one - if (product.ProductManufacturers.All(x => x.ManufacturerId != passedManufacturerId)) - { - // if manufacturer does not exist we simply ignore it, otherwise add it to the product - var manufacturer = _manufacturerService.GetManufacturerById(passedManufacturerId); - if (manufacturer != null) - { - _manufacturerService.InsertProductManufacturer(new ProductManufacturer() - { ProductId = product.Id, ManufacturerId = manufacturer.Id }); - } - } - } - } - - private void UpdateAssociatedProducts(Product product, List passedAssociatedProductIds) - { - // If no associated products specified then there is nothing to map - if (passedAssociatedProductIds == null) - return; - - var noLongerAssociatedProducts = - _productService.GetAssociatedProducts(product.Id, showHidden: true) - .Where(p => !passedAssociatedProductIds.Contains(p.Id)); - - // update all products that are no longer associated with our product - foreach (var noLongerAssocuatedProduct in noLongerAssociatedProducts) - { - noLongerAssocuatedProduct.ParentGroupedProductId = 0; - _productService.UpdateProduct(noLongerAssocuatedProduct); - } - - var newAssociatedProducts = _productService.GetProductsByIds(passedAssociatedProductIds.ToArray()); - foreach (var newAssociatedProduct in newAssociatedProducts) - { - newAssociatedProduct.ParentGroupedProductId = product.Id; - _productService.UpdateProduct(newAssociatedProduct); - } - } - } +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using Nop.Core.Domain.Catalog; +using Nop.Core.Domain.Discounts; +using Nop.Plugin.Api.Attributes; +using Nop.Plugin.Api.Constants; +using Nop.Plugin.Api.Delta; +using Nop.Plugin.Api.DTOs.Images; +using Nop.Plugin.Api.DTOs.Products; +using Nop.Plugin.Api.Factories; +using Nop.Plugin.Api.JSON.ActionResults; +using Nop.Plugin.Api.ModelBinders; +using Nop.Plugin.Api.Models.ProductsParameters; +using Nop.Plugin.Api.Services; +using Nop.Services.Catalog; +using Nop.Services.Customers; +using Nop.Services.Discounts; +using Nop.Services.Localization; +using Nop.Services.Logging; +using Nop.Services.Media; +using Nop.Services.Security; +using Nop.Services.Seo; +using Nop.Services.Stores; +using Nop.Plugin.Api.Helpers; + +namespace Nop.Plugin.Api.Controllers +{ + using Microsoft.AspNetCore.Authentication.JwtBearer; + using Microsoft.AspNetCore.Mvc; + using DTOs.Errors; + using JSON.Serializers; + + [ApiAuthorize(Policy = JwtBearerDefaults.AuthenticationScheme, AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] + public class ProductsController : BaseApiController + { + private readonly IProductApiService _productApiService; + private readonly IProductService _productService; + private readonly IUrlRecordService _urlRecordService; + private readonly IManufacturerService _manufacturerService; + private readonly IFactory _factory; + private readonly IProductTagService _productTagService; + private readonly IProductAttributeService _productAttributeService; + private readonly IDTOHelper _dtoHelper; + + public ProductsController(IProductApiService productApiService, + IJsonFieldsSerializer jsonFieldsSerializer, + IProductService productService, + IUrlRecordService urlRecordService, + ICustomerActivityService customerActivityService, + ILocalizationService localizationService, + IFactory factory, + IAclService aclService, + IStoreMappingService storeMappingService, + IStoreService storeService, + ICustomerService customerService, + IDiscountService discountService, + IPictureService pictureService, + IManufacturerService manufacturerService, + IProductTagService productTagService, + IProductAttributeService productAttributeService, + IDTOHelper dtoHelper) : base(jsonFieldsSerializer, aclService, customerService, storeMappingService, storeService, discountService, customerActivityService, localizationService, pictureService) + { + _productApiService = productApiService; + _factory = factory; + _manufacturerService = manufacturerService; + _productTagService = productTagService; + _urlRecordService = urlRecordService; + _productService = productService; + _productAttributeService = productAttributeService; + _dtoHelper = dtoHelper; + } + + /// + /// Receive a list of all products + /// + /// OK + /// Bad Request + /// Unauthorized + [HttpGet] + [Route("/api/products")] + [ProducesResponseType(typeof(ProductsRootObjectDto), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] + [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] + [GetRequestsErrorInterceptorActionFilter] + public IActionResult GetProducts(ProductsParametersModel parameters) + { + if (parameters.Limit < Configurations.MinLimit || parameters.Limit > Configurations.MaxLimit) + { + return Error(HttpStatusCode.BadRequest, "limit", "invalid limit parameter"); + } + + if (parameters.Page < Configurations.DefaultPageValue) + { + return Error(HttpStatusCode.BadRequest, "page", "invalid page parameter"); + } + + var allProducts = _productApiService.GetProducts(parameters.Ids, parameters.CreatedAtMin, parameters.CreatedAtMax, parameters.UpdatedAtMin, + parameters.UpdatedAtMax, parameters.Limit, parameters.Page, parameters.SinceId, parameters.CategoryId, + parameters.VendorName, parameters.PublishedStatus) + .Where(p => StoreMappingService.Authorize(p)); + + IList productsAsDtos = allProducts.Select(product => _dtoHelper.PrepareProductDTO(product)).ToList(); + + var productsRootObject = new ProductsRootObjectDto() + { + Products = productsAsDtos + }; + + var json = JsonFieldsSerializer.Serialize(productsRootObject, parameters.Fields); + + return new RawJsonActionResult(json); + } + + /// + /// Receive a count of all products + /// + /// OK + /// Unauthorized + [HttpGet] + [Route("/api/products/count")] + [ProducesResponseType(typeof(ProductsCountRootObject), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] + [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] + [GetRequestsErrorInterceptorActionFilter] + public IActionResult GetProductsCount(ProductsCountParametersModel parameters) + { + var allProductsCount = _productApiService.GetProductsCount(parameters.CreatedAtMin, parameters.CreatedAtMax, parameters.UpdatedAtMin, + parameters.UpdatedAtMax, parameters.PublishedStatus, parameters.VendorName, + parameters.CategoryId); + + var productsCountRootObject = new ProductsCountRootObject() + { + Count = allProductsCount + }; + + return Ok(productsCountRootObject); + } + + /// + /// Retrieve product by spcified id + /// + /// Id of the product + /// Fields from the product you want your json to contain + /// OK + /// Not Found + /// Unauthorized + [HttpGet] + [Route("/api/products/{id}")] + [ProducesResponseType(typeof(ProductsRootObjectDto), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] + [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.NotFound)] + [GetRequestsErrorInterceptorActionFilter] + public IActionResult GetProductById(int id, string fields = "") + { + if (id <= 0) + { + return Error(HttpStatusCode.BadRequest, "id", "invalid id"); + } + + var product = _productApiService.GetProductById(id); + + if (product == null) + { + return Error(HttpStatusCode.NotFound, "product", "not found"); + } + + var productDto = _dtoHelper.PrepareProductDTO(product); + + var productsRootObject = new ProductsRootObjectDto(); + + productsRootObject.Products.Add(productDto); + + var json = JsonFieldsSerializer.Serialize(productsRootObject, fields); + + return new RawJsonActionResult(json); + } + + [HttpPost] + [Route("/api/products")] + [ProducesResponseType(typeof(ProductsRootObjectDto), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] + [ProducesResponseType(typeof(ErrorsRootObject), 422)] + public IActionResult CreateProduct([ModelBinder(typeof(JsonModelBinder))] Delta productDelta) + { + // Here we display the errors if the validation has failed at some point. + if (!ModelState.IsValid) + { + return Error(); + } + + // Inserting the new product + var product = _factory.Initialize(); + productDelta.Merge(product); + + _productService.InsertProduct(product); + + UpdateProductPictures(product, productDelta.Dto.Images); + + UpdateProductTags(product, productDelta.Dto.Tags); + + UpdateProductManufacturers(product, productDelta.Dto.ManufacturerIds); + + UpdateAssociatedProducts(product, productDelta.Dto.AssociatedProductIds); + + //search engine name + var seName = _urlRecordService.ValidateSeName(product, productDelta.Dto.SeName, product.Name, true); + _urlRecordService.SaveSlug(product, seName, 0); + + UpdateAclRoles(product, productDelta.Dto.RoleIds); + + UpdateDiscountMappings(product, productDelta.Dto.DiscountIds); + + UpdateStoreMappings(product, productDelta.Dto.StoreIds); + + _productService.UpdateProduct(product); + + CustomerActivityService.InsertActivity("AddNewProduct", + LocalizationService.GetResource("ActivityLog.AddNewProduct"), product); + + // Preparing the result dto of the new product + var productDto = _dtoHelper.PrepareProductDTO(product); + + var productsRootObject = new ProductsRootObjectDto(); + + productsRootObject.Products.Add(productDto); + + var json = JsonFieldsSerializer.Serialize(productsRootObject, string.Empty); + + return new RawJsonActionResult(json); + } + + [HttpPut] + [Route("/api/products/{id}")] + [ProducesResponseType(typeof(ProductsRootObjectDto), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.NotFound)] + [ProducesResponseType(typeof(ErrorsRootObject), 422)] + [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] + public IActionResult UpdateProduct([ModelBinder(typeof(JsonModelBinder))] Delta productDelta) + { + // Here we display the errors if the validation has failed at some point. + if (!ModelState.IsValid) + { + return Error(); + } + + var product = _productApiService.GetProductById(productDelta.Dto.Id); + + if (product == null) + { + return Error(HttpStatusCode.NotFound, "product", "not found"); + } + + productDelta.Merge(product); + + product.UpdatedOnUtc = DateTime.UtcNow; + _productService.UpdateProduct(product); + + UpdateProductAttributes(product, productDelta); + + UpdateProductPictures(product, productDelta.Dto.Images); + + UpdateProductTags(product, productDelta.Dto.Tags); + + UpdateProductManufacturers(product, productDelta.Dto.ManufacturerIds); + + UpdateAssociatedProducts(product, productDelta.Dto.AssociatedProductIds); + + // Update the SeName if specified + if (productDelta.Dto.SeName != null) + { + var seName = _urlRecordService.ValidateSeName(product, productDelta.Dto.SeName, product.Name, true); + _urlRecordService.SaveSlug(product, seName, 0); + } + + UpdateDiscountMappings(product, productDelta.Dto.DiscountIds); + + UpdateStoreMappings(product, productDelta.Dto.StoreIds); + + UpdateAclRoles(product, productDelta.Dto.RoleIds); + + _productService.UpdateProduct(product); + + CustomerActivityService.InsertActivity("UpdateProduct", + LocalizationService.GetResource("ActivityLog.UpdateProduct"), product); + + // Preparing the result dto of the new product + var productDto = _dtoHelper.PrepareProductDTO(product); + + var productsRootObject = new ProductsRootObjectDto(); + + productsRootObject.Products.Add(productDto); + + var json = JsonFieldsSerializer.Serialize(productsRootObject, string.Empty); + + return new RawJsonActionResult(json); + } + + [HttpDelete] + [Route("/api/products/{id}")] + [ProducesResponseType(typeof(void), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.NotFound)] + [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] + [GetRequestsErrorInterceptorActionFilter] + public IActionResult DeleteProduct(int id) + { + if (id <= 0) + { + return Error(HttpStatusCode.BadRequest, "id", "invalid id"); + } + + var product = _productApiService.GetProductById(id); + + if (product == null) + { + return Error(HttpStatusCode.NotFound, "product", "not found"); + } + + _productService.DeleteProduct(product); + + //activity log + CustomerActivityService.InsertActivity("DeleteProduct", + string.Format(LocalizationService.GetResource("ActivityLog.DeleteProduct"), product.Name), product); + + return new RawJsonActionResult("{}"); + } + + private void UpdateProductPictures(Product entityToUpdate, List setPictures) + { + // If no pictures are specified means we don't have to update anything + if (setPictures == null) + return; + + // delete unused product pictures + var unusedProductPictures = entityToUpdate.ProductPictures.Where(x => setPictures.All(y => y.Id != x.Id)).ToList(); + foreach (var unusedProductPicture in unusedProductPictures) + { + var picture = PictureService.GetPictureById(unusedProductPicture.PictureId); + if (picture == null) + throw new ArgumentException("No picture found with the specified id"); + PictureService.DeletePicture(picture); + } + + foreach (var imageDto in setPictures) + { + if (imageDto.Id > 0) + { + // update existing product picture + var productPictureToUpdate = entityToUpdate.ProductPictures.FirstOrDefault(x => x.Id == imageDto.Id); + if (productPictureToUpdate != null && imageDto.Position > 0) + { + productPictureToUpdate.DisplayOrder = imageDto.Position; + _productService.UpdateProductPicture(productPictureToUpdate); + } + } + else + { + // add new product picture + var newPicture = PictureService.InsertPicture(imageDto.Binary, imageDto.MimeType, string.Empty); + _productService.InsertProductPicture(new ProductPicture() + { + PictureId = newPicture.Id, + ProductId = entityToUpdate.Id, + DisplayOrder = imageDto.Position + }); + } + } + } + + private void UpdateProductAttributes(Product entityToUpdate, Delta productDtoDelta) + { + // If no product attribute mappings are specified means we don't have to update anything + if (productDtoDelta.Dto.ProductAttributeMappings == null) + return; + + // delete unused product attribute mappings + var toBeUpdatedIds = productDtoDelta.Dto.ProductAttributeMappings.Where(y => y.Id != 0).Select(x => x.Id); + + var unusedProductAttributeMappings = entityToUpdate.ProductAttributeMappings.Where(x => !toBeUpdatedIds.Contains(x.Id)).ToList(); + + foreach (var unusedProductAttributeMapping in unusedProductAttributeMappings) + { + _productAttributeService.DeleteProductAttributeMapping(unusedProductAttributeMapping); + } + + foreach (var productAttributeMappingDto in productDtoDelta.Dto.ProductAttributeMappings) + { + if (productAttributeMappingDto.Id > 0) + { + // update existing product attribute mapping + var productAttributeMappingToUpdate = entityToUpdate.ProductAttributeMappings.FirstOrDefault(x => x.Id == productAttributeMappingDto.Id); + if (productAttributeMappingToUpdate != null) + { + productDtoDelta.Merge(productAttributeMappingDto,productAttributeMappingToUpdate,false); + + _productAttributeService.UpdateProductAttributeMapping(productAttributeMappingToUpdate); + + UpdateProductAttributeValues(productAttributeMappingDto, productDtoDelta); + } + } + else + { + var newProductAttributeMapping = new ProductAttributeMapping {ProductId = entityToUpdate.Id}; + + productDtoDelta.Merge(productAttributeMappingDto, newProductAttributeMapping); + + // add new product attribute + _productAttributeService.InsertProductAttributeMapping(newProductAttributeMapping); + } + } + } + + private void UpdateProductAttributeValues(ProductAttributeMappingDto productAttributeMappingDto, Delta productDtoDelta) + { + // If no product attribute values are specified means we don't have to update anything + if (productAttributeMappingDto.ProductAttributeValues == null) + return; + + // delete unused product attribute values + var toBeUpdatedIds = productAttributeMappingDto.ProductAttributeValues.Where(y => y.Id != 0).Select(x => x.Id); + + var unusedProductAttributeValues = + _productAttributeService.GetProductAttributeValues(productAttributeMappingDto.Id).Where(x => !toBeUpdatedIds.Contains(x.Id)).ToList(); + + foreach (var unusedProductAttributeValue in unusedProductAttributeValues) + { + _productAttributeService.DeleteProductAttributeValue(unusedProductAttributeValue); + } + + foreach (var productAttributeValueDto in productAttributeMappingDto.ProductAttributeValues) + { + if (productAttributeValueDto.Id > 0) + { + // update existing product attribute mapping + var productAttributeValueToUpdate = + _productAttributeService.GetProductAttributeValueById(productAttributeValueDto.Id); + if (productAttributeValueToUpdate != null) + { + productDtoDelta.Merge(productAttributeValueDto, productAttributeValueToUpdate, false); + + _productAttributeService.UpdateProductAttributeValue(productAttributeValueToUpdate); + } + } + else + { + var newProductAttributeValue = new ProductAttributeValue(); + productDtoDelta.Merge(productAttributeValueDto, newProductAttributeValue); + + newProductAttributeValue.ProductAttributeMappingId = productAttributeMappingDto.Id; + // add new product attribute value + _productAttributeService.InsertProductAttributeValue(newProductAttributeValue); + } + } + } + + private void UpdateProductTags(Product product, IReadOnlyCollection productTags) + { + if (productTags == null) + return; + + if (product == null) + throw new ArgumentNullException(nameof(product)); + + //Copied from UpdateProductTags method of ProductTagService + //product tags + var existingProductTags = _productTagService.GetAllProductTagsByProductId(product.Id); + var productTagsToRemove = new List(); + foreach (var existingProductTag in existingProductTags) + { + var found = false; + foreach (var newProductTag in productTags) + { + if (!existingProductTag.Name.Equals(newProductTag, StringComparison.InvariantCultureIgnoreCase)) + continue; + + found = true; + break; + } + + if (!found) + { + productTagsToRemove.Add(existingProductTag); + } + } + + foreach (var productTag in productTagsToRemove) + { + //product.ProductTags.Remove(productTag); + product.ProductProductTagMappings + .Remove(product.ProductProductTagMappings.FirstOrDefault(mapping => mapping.ProductTagId == productTag.Id)); + _productService.UpdateProduct(product); + } + + foreach (var productTagName in productTags) + { + ProductTag productTag; + var productTag2 = _productTagService.GetProductTagByName(productTagName); + if (productTag2 == null) + { + //add new product tag + productTag = new ProductTag + { + Name = productTagName + }; + _productTagService.InsertProductTag(productTag); + } + else + { + productTag = productTag2; + } + + if (!_productService.ProductTagExists(product, productTag.Id)) + { + product.ProductProductTagMappings.Add(new ProductProductTagMapping { ProductTag = productTag }); + _productService.UpdateProduct(product); + } + + var seName = _urlRecordService.ValidateSeName(productTag, string.Empty, productTag.Name, true); + _urlRecordService.SaveSlug(productTag, seName, 0); + } + } + + private void UpdateDiscountMappings(Product product, List passedDiscountIds) + { + if (passedDiscountIds == null) + return; + + var allDiscounts = DiscountService.GetAllDiscounts(DiscountType.AssignedToSkus, showHidden: true); + + foreach (var discount in allDiscounts) + { + if (passedDiscountIds.Contains(discount.Id)) + { + //new discount + if (product.AppliedDiscounts.Count(d => d.Id == discount.Id) == 0) + product.AppliedDiscounts.Add(discount); + } + else + { + //remove discount + if (product.AppliedDiscounts.Count(d => d.Id == discount.Id) > 0) + product.AppliedDiscounts.Remove(discount); + } + } + + _productService.UpdateProduct(product); + _productService.UpdateHasDiscountsApplied(product); + } + + private void UpdateProductManufacturers(Product product, List passedManufacturerIds) + { + // If no manufacturers specified then there is nothing to map + if (passedManufacturerIds == null) + return; + + var unusedProductManufacturers = product.ProductManufacturers.Where(x => !passedManufacturerIds.Contains(x.ManufacturerId)).ToList(); + + // remove all manufacturers that are not passed + foreach (var unusedProductManufacturer in unusedProductManufacturers) + { + _manufacturerService.DeleteProductManufacturer(unusedProductManufacturer); + } + + foreach (var passedManufacturerId in passedManufacturerIds) + { + // not part of existing manufacturers so we will create a new one + if (product.ProductManufacturers.All(x => x.ManufacturerId != passedManufacturerId)) + { + // if manufacturer does not exist we simply ignore it, otherwise add it to the product + var manufacturer = _manufacturerService.GetManufacturerById(passedManufacturerId); + if (manufacturer != null) + { + _manufacturerService.InsertProductManufacturer(new ProductManufacturer() + { ProductId = product.Id, ManufacturerId = manufacturer.Id }); + } + } + } + } + + private void UpdateAssociatedProducts(Product product, List passedAssociatedProductIds) + { + // If no associated products specified then there is nothing to map + if (passedAssociatedProductIds == null) + return; + + var noLongerAssociatedProducts = + _productService.GetAssociatedProducts(product.Id, showHidden: true) + .Where(p => !passedAssociatedProductIds.Contains(p.Id)); + + // update all products that are no longer associated with our product + foreach (var noLongerAssocuatedProduct in noLongerAssociatedProducts) + { + noLongerAssocuatedProduct.ParentGroupedProductId = 0; + _productService.UpdateProduct(noLongerAssocuatedProduct); + } + + var newAssociatedProducts = _productService.GetProductsByIds(passedAssociatedProductIds.ToArray()); + foreach (var newAssociatedProduct in newAssociatedProducts) + { + newAssociatedProduct.ParentGroupedProductId = product.Id; + _productService.UpdateProduct(newAssociatedProduct); + } + } + } } \ No newline at end of file diff --git a/Nop.Plugin.Api/Controllers/ShoppingCartItemsController.cs b/Nop.Plugin.Api/Controllers/ShoppingCartItemsController.cs index b1c8af1..da2d6bb 100644 --- a/Nop.Plugin.Api/Controllers/ShoppingCartItemsController.cs +++ b/Nop.Plugin.Api/Controllers/ShoppingCartItemsController.cs @@ -1,360 +1,358 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net; -using Nop.Core.Domain.Catalog; -using Nop.Core.Domain.Customers; -using Nop.Core.Domain.Orders; -using Nop.Plugin.Api.Attributes; -using Nop.Plugin.Api.Constants; -using Nop.Plugin.Api.Delta; -using Nop.Plugin.Api.DTOs.ShoppingCarts; -using Nop.Plugin.Api.Factories; -using Nop.Plugin.Api.JSON.ActionResults; -using Nop.Plugin.Api.ModelBinders; -using Nop.Plugin.Api.Models.ShoppingCartsParameters; -using Nop.Plugin.Api.Services; -using Nop.Services.Catalog; -using Nop.Services.Customers; -using Nop.Services.Discounts; -using Nop.Services.Localization; -using Nop.Services.Logging; -using Nop.Services.Media; -using Nop.Services.Orders; -using Nop.Services.Security; -using Nop.Services.Stores; -using Nop.Plugin.Api.Helpers; -using Nop.Core; - -namespace Nop.Plugin.Api.Controllers -{ - using Microsoft.AspNetCore.Authentication.JwtBearer; - using Microsoft.AspNetCore.Mvc; - using Nop.Plugin.Api.DTOs.Errors; - using Nop.Plugin.Api.JSON.Serializers; - - [ApiAuthorize(Policy = JwtBearerDefaults.AuthenticationScheme, AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] - public class ShoppingCartItemsController : BaseApiController - { - private readonly IShoppingCartItemApiService _shoppingCartItemApiService; - private readonly IShoppingCartService _shoppingCartService; - private readonly IProductService _productService; - private readonly IFactory _factory; - private readonly IProductAttributeConverter _productAttributeConverter; - private readonly IDTOHelper _dtoHelper; - private readonly IStoreContext _storeContext; - - public ShoppingCartItemsController(IShoppingCartItemApiService shoppingCartItemApiService, - IJsonFieldsSerializer jsonFieldsSerializer, - IAclService aclService, - ICustomerService customerService, - IStoreMappingService storeMappingService, - IStoreService storeService, - IDiscountService discountService, - ICustomerActivityService customerActivityService, - ILocalizationService localizationService, - IShoppingCartService shoppingCartService, - IProductService productService, - IFactory factory, - IPictureService pictureService, - IProductAttributeConverter productAttributeConverter, - IDTOHelper dtoHelper, - IStoreContext storeContext) - : base(jsonFieldsSerializer, - aclService, - customerService, - storeMappingService, - storeService, - discountService, - customerActivityService, - localizationService, - pictureService) - { - _shoppingCartItemApiService = shoppingCartItemApiService; - _shoppingCartService = shoppingCartService; - _productService = productService; - _factory = factory; - _productAttributeConverter = productAttributeConverter; - _dtoHelper = dtoHelper; - _storeContext = storeContext; - } - - /// - /// Receive a list of all shopping cart items - /// - /// OK - /// Bad Request - /// Unauthorized - [HttpGet] - [Route("/api/shopping_cart_items")] - [ProducesResponseType(typeof(ShoppingCartItemsRootObject), (int)HttpStatusCode.OK)] - [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] - [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] - [GetRequestsErrorInterceptorActionFilter] - public IActionResult GetShoppingCartItems(ShoppingCartItemsParametersModel parameters) - { - if (parameters.Limit < Configurations.MinLimit || parameters.Limit > Configurations.MaxLimit) - { - return Error(HttpStatusCode.BadRequest, "limit", "invalid limit parameter"); - } - - if (parameters.Page < Configurations.DefaultPageValue) - { - return Error(HttpStatusCode.BadRequest, "page", "invalid page parameter"); - } - - IList shoppingCartItems = _shoppingCartItemApiService.GetShoppingCartItems(customerId: null, - createdAtMin: parameters.CreatedAtMin, - createdAtMax: parameters.CreatedAtMax, - updatedAtMin: parameters.UpdatedAtMin, - updatedAtMax: parameters.UpdatedAtMax, - limit: parameters.Limit, - page: parameters.Page); - - List shoppingCartItemsDtos = shoppingCartItems.Select(shoppingCartItem => - { - return _dtoHelper.PrepareShoppingCartItemDTO(shoppingCartItem); - }).ToList(); - - var shoppingCartsRootObject = new ShoppingCartItemsRootObject() - { - ShoppingCartItems = shoppingCartItemsDtos - }; - - var json = _jsonFieldsSerializer.Serialize(shoppingCartsRootObject, parameters.Fields); - - return new RawJsonActionResult(json); - } - - /// - /// Receive a list of all shopping cart items by customer id - /// - /// Id of the customer whoes shopping cart items you want to get - /// OK - /// Bad Request - /// Not Found - /// Unauthorized - [HttpGet] - [Route("/api/shopping_cart_items/{customerId}")] - [ProducesResponseType(typeof(ShoppingCartItemsRootObject), (int)HttpStatusCode.OK)] - [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] - [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] - [ProducesResponseType(typeof(string), (int)HttpStatusCode.NotFound)] - [GetRequestsErrorInterceptorActionFilter] - public IActionResult GetShoppingCartItemsByCustomerId(int customerId, ShoppingCartItemsForCustomerParametersModel parameters) - { - if (customerId <= Configurations.DefaultCustomerId) - { - return Error(HttpStatusCode.BadRequest, "customer_id", "invalid customer_id"); - } - - if (parameters.Limit < Configurations.MinLimit || parameters.Limit > Configurations.MaxLimit) - { - return Error(HttpStatusCode.BadRequest, "limit", "invalid limit parameter"); - } - - if (parameters.Page < Configurations.DefaultPageValue) - { - return Error(HttpStatusCode.BadRequest, "page", "invalid page parameter"); - } - - IList shoppingCartItems = _shoppingCartItemApiService.GetShoppingCartItems(customerId, - parameters.CreatedAtMin, - parameters.CreatedAtMax, parameters.UpdatedAtMin, - parameters.UpdatedAtMax, parameters.Limit, - parameters.Page); - - if (shoppingCartItems == null) - { - return Error(HttpStatusCode.NotFound, "shopping_cart_item", "not found"); - } - - List shoppingCartItemsDtos = shoppingCartItems - .Select(shoppingCartItem => _dtoHelper.PrepareShoppingCartItemDTO(shoppingCartItem)) - .ToList(); - - var shoppingCartsRootObject = new ShoppingCartItemsRootObject() - { - ShoppingCartItems = shoppingCartItemsDtos - }; - - var json = _jsonFieldsSerializer.Serialize(shoppingCartsRootObject, parameters.Fields); - - return new RawJsonActionResult(json); - } - - [HttpPost] - [Route("/api/shopping_cart_items")] - [ProducesResponseType(typeof(ShoppingCartItemsRootObject), (int)HttpStatusCode.OK)] - [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] - [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] - [ProducesResponseType(typeof(string), (int)HttpStatusCode.NotFound)] - [ProducesResponseType(typeof(string), 422)] - public IActionResult CreateShoppingCartItem([ModelBinder(typeof(JsonModelBinder))] Delta shoppingCartItemDelta) - { - // Here we display the errors if the validation has failed at some point. - if (!ModelState.IsValid) - { - return Error(); - } - - ShoppingCartItem newShoppingCartItem = _factory.Initialize(); - shoppingCartItemDelta.Merge(newShoppingCartItem); - - // We know that the product id and customer id will be provided because they are required by the validator. - // TODO: validate - Product product = _productService.GetProductById(newShoppingCartItem.ProductId); - - if (product == null) - { - return Error(HttpStatusCode.NotFound, "product", "not found"); - } - - Customer customer = _customerService.GetCustomerById(newShoppingCartItem.CustomerId); - - if (customer == null) - { - return Error(HttpStatusCode.NotFound, "customer", "not found"); - } - - ShoppingCartType shoppingCartType = (ShoppingCartType)Enum.Parse(typeof(ShoppingCartType), shoppingCartItemDelta.Dto.ShoppingCartType); - - if (!product.IsRental) - { - newShoppingCartItem.RentalStartDateUtc = null; - newShoppingCartItem.RentalEndDateUtc = null; - } - - string attributesXml =_productAttributeConverter.ConvertToXml(shoppingCartItemDelta.Dto.Attributes, product.Id); - - int currentStoreId = _storeContext.CurrentStore.Id; - - IList warnings = _shoppingCartService.AddToCart(customer, product, shoppingCartType, currentStoreId, attributesXml, 0M, - newShoppingCartItem.RentalStartDateUtc, newShoppingCartItem.RentalEndDateUtc, - shoppingCartItemDelta.Dto.Quantity ?? 1); - - if (warnings.Count > 0) - { - foreach (var warning in warnings) - { - ModelState.AddModelError("shopping cart item", warning); - } - - return Error(HttpStatusCode.BadRequest); - } - else { - // the newly added shopping cart item should be the last one - newShoppingCartItem = customer.ShoppingCartItems.LastOrDefault(); - } - - // Preparing the result dto of the new product category mapping - ShoppingCartItemDto newShoppingCartItemDto = _dtoHelper.PrepareShoppingCartItemDTO(newShoppingCartItem); - - var shoppingCartsRootObject = new ShoppingCartItemsRootObject(); - - shoppingCartsRootObject.ShoppingCartItems.Add(newShoppingCartItemDto); - - var json = _jsonFieldsSerializer.Serialize(shoppingCartsRootObject, string.Empty); - - return new RawJsonActionResult(json); - } - - [HttpPut] - [Route("/api/shopping_cart_items/{id}")] - [ProducesResponseType(typeof(ShoppingCartItemsRootObject), (int)HttpStatusCode.OK)] - [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] - [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] - [ProducesResponseType(typeof(string), (int)HttpStatusCode.NotFound)] - [ProducesResponseType(typeof(ErrorsRootObject), 422)] - public IActionResult UpdateShoppingCartItem([ModelBinder(typeof(JsonModelBinder))] Delta shoppingCartItemDelta) - { - // Here we display the errors if the validation has failed at some point. - if (!ModelState.IsValid) - { - return Error(); - } - - // We kno that the id will be valid integer because the validation for this happens in the validator which is executed by the model binder. - ShoppingCartItem shoppingCartItemForUpdate = - _shoppingCartItemApiService.GetShoppingCartItem(int.Parse(shoppingCartItemDelta.Dto.Id)); - - if (shoppingCartItemForUpdate == null) - { - return Error(HttpStatusCode.NotFound, "shopping_cart_item", "not found"); - } - - shoppingCartItemDelta.Merge(shoppingCartItemForUpdate); - - if (!shoppingCartItemForUpdate.Product.IsRental) - { - shoppingCartItemForUpdate.RentalStartDateUtc = null; - shoppingCartItemForUpdate.RentalEndDateUtc = null; - } - - if (shoppingCartItemDelta.Dto.Attributes != null) - { - shoppingCartItemForUpdate.AttributesXml = _productAttributeConverter.ConvertToXml(shoppingCartItemDelta.Dto.Attributes, shoppingCartItemForUpdate.Product.Id); - } - - // The update time is set in the service. - var warnings = _shoppingCartService.UpdateShoppingCartItem(shoppingCartItemForUpdate.Customer, shoppingCartItemForUpdate.Id, - shoppingCartItemForUpdate.AttributesXml, shoppingCartItemForUpdate.CustomerEnteredPrice, - shoppingCartItemForUpdate.RentalStartDateUtc, shoppingCartItemForUpdate.RentalEndDateUtc, - shoppingCartItemForUpdate.Quantity); - - if (warnings.Count > 0) - { - foreach (var warning in warnings) - { - ModelState.AddModelError("shopping cart item", warning); - } - - return Error(HttpStatusCode.BadRequest); - } - else - { - shoppingCartItemForUpdate = _shoppingCartItemApiService.GetShoppingCartItem(shoppingCartItemForUpdate.Id); - } - - // Preparing the result dto of the new product category mapping - ShoppingCartItemDto newShoppingCartItemDto = _dtoHelper.PrepareShoppingCartItemDTO(shoppingCartItemForUpdate); - - var shoppingCartsRootObject = new ShoppingCartItemsRootObject(); - - shoppingCartsRootObject.ShoppingCartItems.Add(newShoppingCartItemDto); - - var json = _jsonFieldsSerializer.Serialize(shoppingCartsRootObject, string.Empty); - - return new RawJsonActionResult(json); - } - - [HttpDelete] - [Route("/api/shopping_cart_items/{id}")] - [ProducesResponseType(typeof(void), (int)HttpStatusCode.OK)] - [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] - [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] - [ProducesResponseType(typeof(string), (int)HttpStatusCode.NotFound)] - [GetRequestsErrorInterceptorActionFilter] - public IActionResult DeleteShoppingCartItem(int id) - { - if (id <= 0) - { - return Error(HttpStatusCode.BadRequest, "id", "invalid id"); - } - - ShoppingCartItem shoppingCartItemForDelete = _shoppingCartItemApiService.GetShoppingCartItem(id); - - if (shoppingCartItemForDelete == null) - { - return Error(HttpStatusCode.NotFound, "shopping_cart_item", "not found"); - } - - _shoppingCartService.DeleteShoppingCartItem(shoppingCartItemForDelete); - - //activity log - _customerActivityService.InsertActivity("DeleteShoppingCartItem", _localizationService.GetResource("ActivityLog.DeleteShoppingCartItem"), shoppingCartItemForDelete.Id); - - return new RawJsonActionResult("{}"); - } - } +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using Nop.Core.Domain.Orders; +using Nop.Plugin.Api.Attributes; +using Nop.Plugin.Api.Constants; +using Nop.Plugin.Api.Delta; +using Nop.Plugin.Api.DTOs.ShoppingCarts; +using Nop.Plugin.Api.Factories; +using Nop.Plugin.Api.JSON.ActionResults; +using Nop.Plugin.Api.ModelBinders; +using Nop.Plugin.Api.Models.ShoppingCartsParameters; +using Nop.Plugin.Api.Services; +using Nop.Services.Catalog; +using Nop.Services.Customers; +using Nop.Services.Discounts; +using Nop.Services.Localization; +using Nop.Services.Logging; +using Nop.Services.Media; +using Nop.Services.Orders; +using Nop.Services.Security; +using Nop.Services.Stores; +using Nop.Plugin.Api.Helpers; +using Nop.Core; + +namespace Nop.Plugin.Api.Controllers +{ + using Microsoft.AspNetCore.Authentication.JwtBearer; + using Microsoft.AspNetCore.Mvc; + using DTOs.Errors; + using JSON.Serializers; + + [ApiAuthorize(Policy = JwtBearerDefaults.AuthenticationScheme, AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] + public class ShoppingCartItemsController : BaseApiController + { + private readonly IShoppingCartItemApiService _shoppingCartItemApiService; + private readonly IShoppingCartService _shoppingCartService; + private readonly IProductService _productService; + private readonly IFactory _factory; + private readonly IProductAttributeConverter _productAttributeConverter; + private readonly IDTOHelper _dtoHelper; + private readonly IStoreContext _storeContext; + + public ShoppingCartItemsController(IShoppingCartItemApiService shoppingCartItemApiService, + IJsonFieldsSerializer jsonFieldsSerializer, + IAclService aclService, + ICustomerService customerService, + IStoreMappingService storeMappingService, + IStoreService storeService, + IDiscountService discountService, + ICustomerActivityService customerActivityService, + ILocalizationService localizationService, + IShoppingCartService shoppingCartService, + IProductService productService, + IFactory factory, + IPictureService pictureService, + IProductAttributeConverter productAttributeConverter, + IDTOHelper dtoHelper, + IStoreContext storeContext) + : base(jsonFieldsSerializer, + aclService, + customerService, + storeMappingService, + storeService, + discountService, + customerActivityService, + localizationService, + pictureService) + { + _shoppingCartItemApiService = shoppingCartItemApiService; + _shoppingCartService = shoppingCartService; + _productService = productService; + _factory = factory; + _productAttributeConverter = productAttributeConverter; + _dtoHelper = dtoHelper; + _storeContext = storeContext; + } + + /// + /// Receive a list of all shopping cart items + /// + /// OK + /// Bad Request + /// Unauthorized + [HttpGet] + [Route("/api/shopping_cart_items")] + [ProducesResponseType(typeof(ShoppingCartItemsRootObject), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] + [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] + [GetRequestsErrorInterceptorActionFilter] + public IActionResult GetShoppingCartItems(ShoppingCartItemsParametersModel parameters) + { + if (parameters.Limit < Configurations.MinLimit || parameters.Limit > Configurations.MaxLimit) + { + return Error(HttpStatusCode.BadRequest, "limit", "invalid limit parameter"); + } + + if (parameters.Page < Configurations.DefaultPageValue) + { + return Error(HttpStatusCode.BadRequest, "page", "invalid page parameter"); + } + + IList shoppingCartItems = _shoppingCartItemApiService.GetShoppingCartItems(customerId: null, + createdAtMin: parameters.CreatedAtMin, + createdAtMax: parameters.CreatedAtMax, + updatedAtMin: parameters.UpdatedAtMin, + updatedAtMax: parameters.UpdatedAtMax, + limit: parameters.Limit, + page: parameters.Page); + + var shoppingCartItemsDtos = shoppingCartItems.Select(shoppingCartItem => + { + return _dtoHelper.PrepareShoppingCartItemDTO(shoppingCartItem); + }).ToList(); + + var shoppingCartsRootObject = new ShoppingCartItemsRootObject() + { + ShoppingCartItems = shoppingCartItemsDtos + }; + + var json = JsonFieldsSerializer.Serialize(shoppingCartsRootObject, parameters.Fields); + + return new RawJsonActionResult(json); + } + + /// + /// Receive a list of all shopping cart items by customer id + /// + /// Id of the customer whoes shopping cart items you want to get + /// + /// OK + /// Bad Request + /// Not Found + /// Unauthorized + [HttpGet] + [Route("/api/shopping_cart_items/{customerId}")] + [ProducesResponseType(typeof(ShoppingCartItemsRootObject), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] + [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.NotFound)] + [GetRequestsErrorInterceptorActionFilter] + public IActionResult GetShoppingCartItemsByCustomerId(int customerId, ShoppingCartItemsForCustomerParametersModel parameters) + { + if (customerId <= Configurations.DefaultCustomerId) + { + return Error(HttpStatusCode.BadRequest, "customer_id", "invalid customer_id"); + } + + if (parameters.Limit < Configurations.MinLimit || parameters.Limit > Configurations.MaxLimit) + { + return Error(HttpStatusCode.BadRequest, "limit", "invalid limit parameter"); + } + + if (parameters.Page < Configurations.DefaultPageValue) + { + return Error(HttpStatusCode.BadRequest, "page", "invalid page parameter"); + } + + IList shoppingCartItems = _shoppingCartItemApiService.GetShoppingCartItems(customerId, + parameters.CreatedAtMin, + parameters.CreatedAtMax, parameters.UpdatedAtMin, + parameters.UpdatedAtMax, parameters.Limit, + parameters.Page); + + if (shoppingCartItems == null) + { + return Error(HttpStatusCode.NotFound, "shopping_cart_item", "not found"); + } + + var shoppingCartItemsDtos = shoppingCartItems + .Select(shoppingCartItem => _dtoHelper.PrepareShoppingCartItemDTO(shoppingCartItem)) + .ToList(); + + var shoppingCartsRootObject = new ShoppingCartItemsRootObject() + { + ShoppingCartItems = shoppingCartItemsDtos + }; + + var json = JsonFieldsSerializer.Serialize(shoppingCartsRootObject, parameters.Fields); + + return new RawJsonActionResult(json); + } + + [HttpPost] + [Route("/api/shopping_cart_items")] + [ProducesResponseType(typeof(ShoppingCartItemsRootObject), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] + [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.NotFound)] + [ProducesResponseType(typeof(string), 422)] + public IActionResult CreateShoppingCartItem([ModelBinder(typeof(JsonModelBinder))] Delta shoppingCartItemDelta) + { + // Here we display the errors if the validation has failed at some point. + if (!ModelState.IsValid) + { + return Error(); + } + + var newShoppingCartItem = _factory.Initialize(); + shoppingCartItemDelta.Merge(newShoppingCartItem); + + // We know that the product id and customer id will be provided because they are required by the validator. + // TODO: validate + var product = _productService.GetProductById(newShoppingCartItem.ProductId); + + if (product == null) + { + return Error(HttpStatusCode.NotFound, "product", "not found"); + } + + var customer = CustomerService.GetCustomerById(newShoppingCartItem.CustomerId); + + if (customer == null) + { + return Error(HttpStatusCode.NotFound, "customer", "not found"); + } + + var shoppingCartType = (ShoppingCartType)Enum.Parse(typeof(ShoppingCartType), shoppingCartItemDelta.Dto.ShoppingCartType); + + if (!product.IsRental) + { + newShoppingCartItem.RentalStartDateUtc = null; + newShoppingCartItem.RentalEndDateUtc = null; + } + + var attributesXml =_productAttributeConverter.ConvertToXml(shoppingCartItemDelta.Dto.Attributes, product.Id); + + var currentStoreId = _storeContext.CurrentStore.Id; + + var warnings = _shoppingCartService.AddToCart(customer, product, shoppingCartType, currentStoreId, attributesXml, 0M, + newShoppingCartItem.RentalStartDateUtc, newShoppingCartItem.RentalEndDateUtc, + shoppingCartItemDelta.Dto.Quantity ?? 1); + + if (warnings.Count > 0) + { + foreach (var warning in warnings) + { + ModelState.AddModelError("shopping cart item", warning); + } + + return Error(HttpStatusCode.BadRequest); + } + else { + // the newly added shopping cart item should be the last one + newShoppingCartItem = customer.ShoppingCartItems.LastOrDefault(); + } + + // Preparing the result dto of the new product category mapping + var newShoppingCartItemDto = _dtoHelper.PrepareShoppingCartItemDTO(newShoppingCartItem); + + var shoppingCartsRootObject = new ShoppingCartItemsRootObject(); + + shoppingCartsRootObject.ShoppingCartItems.Add(newShoppingCartItemDto); + + var json = JsonFieldsSerializer.Serialize(shoppingCartsRootObject, string.Empty); + + return new RawJsonActionResult(json); + } + + [HttpPut] + [Route("/api/shopping_cart_items/{id}")] + [ProducesResponseType(typeof(ShoppingCartItemsRootObject), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] + [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.NotFound)] + [ProducesResponseType(typeof(ErrorsRootObject), 422)] + public IActionResult UpdateShoppingCartItem([ModelBinder(typeof(JsonModelBinder))] Delta shoppingCartItemDelta) + { + // Here we display the errors if the validation has failed at some point. + if (!ModelState.IsValid) + { + return Error(); + } + + // We kno that the id will be valid integer because the validation for this happens in the validator which is executed by the model binder. + var shoppingCartItemForUpdate = _shoppingCartItemApiService.GetShoppingCartItem(shoppingCartItemDelta.Dto.Id); + + if (shoppingCartItemForUpdate == null) + { + return Error(HttpStatusCode.NotFound, "shopping_cart_item", "not found"); + } + + shoppingCartItemDelta.Merge(shoppingCartItemForUpdate); + + if (!shoppingCartItemForUpdate.Product.IsRental) + { + shoppingCartItemForUpdate.RentalStartDateUtc = null; + shoppingCartItemForUpdate.RentalEndDateUtc = null; + } + + if (shoppingCartItemDelta.Dto.Attributes != null) + { + shoppingCartItemForUpdate.AttributesXml = _productAttributeConverter.ConvertToXml(shoppingCartItemDelta.Dto.Attributes, shoppingCartItemForUpdate.Product.Id); + } + + // The update time is set in the service. + var warnings = _shoppingCartService.UpdateShoppingCartItem(shoppingCartItemForUpdate.Customer, shoppingCartItemForUpdate.Id, + shoppingCartItemForUpdate.AttributesXml, shoppingCartItemForUpdate.CustomerEnteredPrice, + shoppingCartItemForUpdate.RentalStartDateUtc, shoppingCartItemForUpdate.RentalEndDateUtc, + shoppingCartItemForUpdate.Quantity); + + if (warnings.Count > 0) + { + foreach (var warning in warnings) + { + ModelState.AddModelError("shopping cart item", warning); + } + + return Error(HttpStatusCode.BadRequest); + } + else + { + shoppingCartItemForUpdate = _shoppingCartItemApiService.GetShoppingCartItem(shoppingCartItemForUpdate.Id); + } + + // Preparing the result dto of the new product category mapping + var newShoppingCartItemDto = _dtoHelper.PrepareShoppingCartItemDTO(shoppingCartItemForUpdate); + + var shoppingCartsRootObject = new ShoppingCartItemsRootObject(); + + shoppingCartsRootObject.ShoppingCartItems.Add(newShoppingCartItemDto); + + var json = JsonFieldsSerializer.Serialize(shoppingCartsRootObject, string.Empty); + + return new RawJsonActionResult(json); + } + + [HttpDelete] + [Route("/api/shopping_cart_items/{id}")] + [ProducesResponseType(typeof(void), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] + [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.NotFound)] + [GetRequestsErrorInterceptorActionFilter] + public IActionResult DeleteShoppingCartItem(int id) + { + if (id <= 0) + { + return Error(HttpStatusCode.BadRequest, "id", "invalid id"); + } + + var shoppingCartItemForDelete = _shoppingCartItemApiService.GetShoppingCartItem(id); + + if (shoppingCartItemForDelete == null) + { + return Error(HttpStatusCode.NotFound, "shopping_cart_item", "not found"); + } + + _shoppingCartService.DeleteShoppingCartItem(shoppingCartItemForDelete); + + //activity log + CustomerActivityService.InsertActivity("DeleteShoppingCartItem", LocalizationService.GetResource("ActivityLog.DeleteShoppingCartItem"), shoppingCartItemForDelete); + + return new RawJsonActionResult("{}"); + } + } } \ No newline at end of file diff --git a/Nop.Plugin.Api/Controllers/SpecificationAttributesController.cs b/Nop.Plugin.Api/Controllers/SpecificationAttributesController.cs index 5d3cf0f..a6a3732 100644 --- a/Nop.Plugin.Api/Controllers/SpecificationAttributesController.cs +++ b/Nop.Plugin.Api/Controllers/SpecificationAttributesController.cs @@ -10,7 +10,6 @@ using Nop.Plugin.Api.JSON.ActionResults; using Nop.Plugin.Api.JSON.Serializers; using Nop.Plugin.Api.ModelBinders; -using Nop.Plugin.Api.Models.ProductSpecificationAttributes; using Nop.Plugin.Api.Models.SpecificationAttributes; using Nop.Plugin.Api.Services; using Nop.Services.Catalog; @@ -21,7 +20,6 @@ using Nop.Services.Media; using Nop.Services.Security; using Nop.Services.Stores; -using System; using System.Linq; using System.Net; @@ -85,7 +83,7 @@ public IActionResult GetSpecificationAttributes(SpecifcationAttributesParameters SpecificationAttributes = specificationAttributeDtos }; - var json = _jsonFieldsSerializer.Serialize(specificationAttributesRootObject, parameters.Fields); + var json = JsonFieldsSerializer.Serialize(specificationAttributesRootObject, parameters.Fields); return new RawJsonActionResult(json); } @@ -147,7 +145,7 @@ public IActionResult GetSpecificationAttributeById(int id, string fields = "") var specificationAttributesRootObject = new SpecificationAttributesRootObjectDto(); specificationAttributesRootObject.SpecificationAttributes.Add(specificationAttributeDto); - var json = _jsonFieldsSerializer.Serialize(specificationAttributesRootObject, fields); + var json = JsonFieldsSerializer.Serialize(specificationAttributesRootObject, fields); return new RawJsonActionResult(json); } @@ -173,7 +171,7 @@ public IActionResult CreateSpecificationAttribute([ModelBinder(typeof(JsonModelB _specificationAttributeService.InsertSpecificationAttribute(specificationAttribute); - _customerActivityService.InsertActivity("AddNewSpecAttribute", _localizationService.GetResource("ActivityLog.AddNewSpecAttribute"), specificationAttribute.Id.ToString()); + CustomerActivityService.InsertActivity("AddNewSpecAttribute", LocalizationService.GetResource("ActivityLog.AddNewSpecAttribute"), specificationAttribute); // Preparing the result dto of the new product var specificationAttributeDto = _dtoHelper.PrepareSpecificationAttributeDto(specificationAttribute); @@ -181,7 +179,7 @@ public IActionResult CreateSpecificationAttribute([ModelBinder(typeof(JsonModelB var specificationAttributesRootObjectDto = new SpecificationAttributesRootObjectDto(); specificationAttributesRootObjectDto.SpecificationAttributes.Add(specificationAttributeDto); - var json = _jsonFieldsSerializer.Serialize(specificationAttributesRootObjectDto, string.Empty); + var json = JsonFieldsSerializer.Serialize(specificationAttributesRootObjectDto, string.Empty); return new RawJsonActionResult(json); } @@ -214,7 +212,7 @@ public IActionResult UpdateSpecificationAttribute([ModelBinder(typeof(JsonModelB _specificationAttributeService.UpdateSpecificationAttribute(specificationAttribute); - _customerActivityService.InsertActivity("EditSpecAttribute", _localizationService.GetResource("ActivityLog.EditSpecAttribute"), specificationAttribute.Id.ToString()); + CustomerActivityService.InsertActivity("EditSpecAttribute", LocalizationService.GetResource("ActivityLog.EditSpecAttribute"), specificationAttribute); // Preparing the result dto of the new product attribute var specificationAttributeDto = _dtoHelper.PrepareSpecificationAttributeDto(specificationAttribute); @@ -222,7 +220,7 @@ public IActionResult UpdateSpecificationAttribute([ModelBinder(typeof(JsonModelB var specificatoinAttributesRootObjectDto = new SpecificationAttributesRootObjectDto(); specificatoinAttributesRootObjectDto.SpecificationAttributes.Add(specificationAttributeDto); - var json = _jsonFieldsSerializer.Serialize(specificatoinAttributesRootObjectDto, string.Empty); + var json = JsonFieldsSerializer.Serialize(specificatoinAttributesRootObjectDto, string.Empty); return new RawJsonActionResult(json); } @@ -250,7 +248,7 @@ public IActionResult DeleteSpecificationAttribute(int id) _specificationAttributeService.DeleteSpecificationAttribute(specificationAttribute); //activity log - _customerActivityService.InsertActivity("DeleteSpecAttribute", _localizationService.GetResource("ActivityLog.DeleteSpecAttribute"), specificationAttribute.Id.ToString()); + CustomerActivityService.InsertActivity("DeleteSpecAttribute", LocalizationService.GetResource("ActivityLog.DeleteSpecAttribute"), specificationAttribute); return new RawJsonActionResult("{}"); } diff --git a/Nop.Plugin.Api/Controllers/StoreController.cs b/Nop.Plugin.Api/Controllers/StoreController.cs index be77cb2..352de62 100644 --- a/Nop.Plugin.Api/Controllers/StoreController.cs +++ b/Nop.Plugin.Api/Controllers/StoreController.cs @@ -1,124 +1,123 @@ -using Nop.Core; -using Nop.Core.Domain.Stores; -using Nop.Plugin.Api.Attributes; -using Nop.Plugin.Api.DTOs.Stores; -using Nop.Plugin.Api.JSON.ActionResults; -using Nop.Services.Customers; -using Nop.Services.Discounts; -using Nop.Services.Localization; -using Nop.Services.Logging; -using Nop.Services.Media; -using Nop.Services.Security; -using Nop.Services.Stores; -using System.Collections.Generic; -using System.Net; -using Nop.Plugin.Api.Helpers; - -namespace Nop.Plugin.Api.Controllers -{ - using Microsoft.AspNetCore.Authentication.JwtBearer; - using Microsoft.AspNetCore.Mvc; - using Nop.Plugin.Api.DTOs.Errors; - using Nop.Plugin.Api.JSON.Serializers; - - [ApiAuthorize(Policy = JwtBearerDefaults.AuthenticationScheme, AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] - public class StoreController : BaseApiController - { - private readonly IStoreContext _storeContext; - private readonly IDTOHelper _dtoHelper; - - public StoreController(IJsonFieldsSerializer jsonFieldsSerializer, - IAclService aclService, - ICustomerService customerService, - IStoreMappingService storeMappingService, - IStoreService storeService, - IDiscountService discountService, - ICustomerActivityService customerActivityService, - ILocalizationService localizationService, - IPictureService pictureService, - IStoreContext storeContext, - IDTOHelper dtoHelper) - : base(jsonFieldsSerializer, - aclService, - customerService, - storeMappingService, - storeService, - discountService, - customerActivityService, - localizationService, - pictureService) - { - _storeContext = storeContext; - _dtoHelper = dtoHelper; - } - - /// - /// Retrieve category by spcified id - /// - /// Fields from the category you want your json to contain - /// OK - /// Not Found - /// Unauthorized - [HttpGet] - [Route("/api/current_store")] - [ProducesResponseType(typeof(StoresRootObject), (int)HttpStatusCode.OK)] - [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] - [ProducesResponseType(typeof(string), (int)HttpStatusCode.NotFound)] - [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] - [GetRequestsErrorInterceptorActionFilter] - public IActionResult GetCurrentStore(string fields = "") - { - Store store = _storeContext.CurrentStore; - - if (store == null) - { - return Error(HttpStatusCode.NotFound, "store", "store not found"); - } - - StoreDto storeDto = _dtoHelper.PrepareStoreDTO(store); - - var storesRootObject = new StoresRootObject(); - - storesRootObject.Stores.Add(storeDto); - - var json = _jsonFieldsSerializer.Serialize(storesRootObject, fields); - - return new RawJsonActionResult(json); - } - - /// - /// Retrieve all stores - /// - /// Fields from the store you want your json to contain - /// OK - /// Unauthorized - [HttpGet] - [Route("/api/stores")] - [ProducesResponseType(typeof(StoresRootObject), (int)HttpStatusCode.OK)] - [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] - [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] - [GetRequestsErrorInterceptorActionFilter] - public IActionResult GetAllStores(string fields = "") - { - IList allStores = _storeService.GetAllStores(); - - IList storesAsDto = new List(); - - foreach (var store in allStores) - { - var storeDto = _dtoHelper.PrepareStoreDTO(store); - - storesAsDto.Add(storeDto); - } - - var storesRootObject = new StoresRootObject() - { - Stores = storesAsDto - }; - - var json = _jsonFieldsSerializer.Serialize(storesRootObject, fields); - - return new RawJsonActionResult(json); - } - } -} +using Nop.Core; +using Nop.Plugin.Api.Attributes; +using Nop.Plugin.Api.DTOs.Stores; +using Nop.Plugin.Api.JSON.ActionResults; +using Nop.Services.Customers; +using Nop.Services.Discounts; +using Nop.Services.Localization; +using Nop.Services.Logging; +using Nop.Services.Media; +using Nop.Services.Security; +using Nop.Services.Stores; +using System.Collections.Generic; +using System.Net; +using Nop.Plugin.Api.Helpers; + +namespace Nop.Plugin.Api.Controllers +{ + using Microsoft.AspNetCore.Authentication.JwtBearer; + using Microsoft.AspNetCore.Mvc; + using DTOs.Errors; + using JSON.Serializers; + + [ApiAuthorize(Policy = JwtBearerDefaults.AuthenticationScheme, AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] + public class StoreController : BaseApiController + { + private readonly IStoreContext _storeContext; + private readonly IDTOHelper _dtoHelper; + + public StoreController(IJsonFieldsSerializer jsonFieldsSerializer, + IAclService aclService, + ICustomerService customerService, + IStoreMappingService storeMappingService, + IStoreService storeService, + IDiscountService discountService, + ICustomerActivityService customerActivityService, + ILocalizationService localizationService, + IPictureService pictureService, + IStoreContext storeContext, + IDTOHelper dtoHelper) + : base(jsonFieldsSerializer, + aclService, + customerService, + storeMappingService, + storeService, + discountService, + customerActivityService, + localizationService, + pictureService) + { + _storeContext = storeContext; + _dtoHelper = dtoHelper; + } + + /// + /// Retrieve category by spcified id + /// + /// Fields from the category you want your json to contain + /// OK + /// Not Found + /// Unauthorized + [HttpGet] + [Route("/api/current_store")] + [ProducesResponseType(typeof(StoresRootObject), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.NotFound)] + [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] + [GetRequestsErrorInterceptorActionFilter] + public IActionResult GetCurrentStore(string fields = "") + { + var store = _storeContext.CurrentStore; + + if (store == null) + { + return Error(HttpStatusCode.NotFound, "store", "store not found"); + } + + var storeDto = _dtoHelper.PrepareStoreDTO(store); + + var storesRootObject = new StoresRootObject(); + + storesRootObject.Stores.Add(storeDto); + + var json = JsonFieldsSerializer.Serialize(storesRootObject, fields); + + return new RawJsonActionResult(json); + } + + /// + /// Retrieve all stores + /// + /// Fields from the store you want your json to contain + /// OK + /// Unauthorized + [HttpGet] + [Route("/api/stores")] + [ProducesResponseType(typeof(StoresRootObject), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] + [ProducesResponseType(typeof(ErrorsRootObject), (int)HttpStatusCode.BadRequest)] + [GetRequestsErrorInterceptorActionFilter] + public IActionResult GetAllStores(string fields = "") + { + var allStores = StoreService.GetAllStores(); + + IList storesAsDto = new List(); + + foreach (var store in allStores) + { + var storeDto = _dtoHelper.PrepareStoreDTO(store); + + storesAsDto.Add(storeDto); + } + + var storesRootObject = new StoresRootObject() + { + Stores = storesAsDto + }; + + var json = JsonFieldsSerializer.Serialize(storesRootObject, fields); + + return new RawJsonActionResult(json); + } + } +} diff --git a/Nop.Plugin.Api/Controllers/WebHookFiltersController.cs b/Nop.Plugin.Api/Controllers/WebHookFiltersController.cs index 269dab7..7216c4e 100644 --- a/Nop.Plugin.Api/Controllers/WebHookFiltersController.cs +++ b/Nop.Plugin.Api/Controllers/WebHookFiltersController.cs @@ -1,60 +1,60 @@ -using System.Collections.Generic; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Mvc; -using Nop.Plugin.Api.Attributes; -using Nop.Plugin.Api.JSON.Serializers; -using Nop.Services.Customers; -using Nop.Services.Discounts; -using Nop.Services.Localization; -using Nop.Services.Logging; -using Nop.Services.Media; -using Nop.Services.Security; -using Nop.Services.Stores; - -namespace Nop.Plugin.Api.Controllers -{ - using System.Net; - using Microsoft.AspNet.WebHooks; - using Microsoft.AspNetCore.Authentication.JwtBearer; - using Microsoft.AspNetCore.Authorization; - using Nop.Plugin.Api.Services; - - [ApiAuthorize(Policy = JwtBearerDefaults.AuthenticationScheme, AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] - public class WebHookFiltersController : BaseApiController - { - private readonly IWebHookFilterManager _filterManager; - - public WebHookFiltersController(IJsonFieldsSerializer jsonFieldsSerializer, - IAclService aclService, - ICustomerService customerService, - IStoreMappingService storeMappingService, - IStoreService storeService, - IDiscountService discountService, - ICustomerActivityService customerActivityService, - ILocalizationService localizationService, - IPictureService pictureService, - IWebHookService webHookService) : - base(jsonFieldsSerializer, - aclService, - customerService, - storeMappingService, - storeService, - discountService, - customerActivityService, - localizationService, - pictureService) - { - _filterManager = webHookService.GetWebHookFilterManager(); - } - - [HttpGet] - [Route("/api/webhooks/filters")] - [ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK)] - [GetRequestsErrorInterceptorActionFilter] - public async Task> GetWebHookFilters() - { - IDictionary filters = await _filterManager.GetAllWebHookFiltersAsync(); - return filters.Values; - } - } -} +using System.Collections.Generic; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Nop.Plugin.Api.Attributes; +using Nop.Plugin.Api.JSON.Serializers; +using Nop.Services.Customers; +using Nop.Services.Discounts; +using Nop.Services.Localization; +using Nop.Services.Logging; +using Nop.Services.Media; +using Nop.Services.Security; +using Nop.Services.Stores; + +namespace Nop.Plugin.Api.Controllers +{ + using System.Net; + using Microsoft.AspNetCore.Authentication.JwtBearer; + using Microsoft.AspNetCore.Authorization; + using Microsoft.AspNetCore.WebHooks; + using Nop.Plugin.Api.Services; + + [ApiAuthorize(Policy = JwtBearerDefaults.AuthenticationScheme, AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] + public class WebHookFiltersController : BaseApiController + { + private readonly IWebHookFilterManager _filterManager; + + public WebHookFiltersController(IJsonFieldsSerializer jsonFieldsSerializer, + IAclService aclService, + ICustomerService customerService, + IStoreMappingService storeMappingService, + IStoreService storeService, + IDiscountService discountService, + ICustomerActivityService customerActivityService, + ILocalizationService localizationService, + IPictureService pictureService, + IWebHookService webHookService) : + base(jsonFieldsSerializer, + aclService, + customerService, + storeMappingService, + storeService, + discountService, + customerActivityService, + localizationService, + pictureService) + { + _filterManager = webHookService.GetWebHookFilterManager(); + } + + [HttpGet] + [Route("/api/webhooks/filters")] + [ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK)] + [GetRequestsErrorInterceptorActionFilter] + public async Task> GetWebHookFilters() + { + IDictionary filters = await _filterManager.GetAllWebHookFiltersAsync(); + return filters.Values; + } + } +} diff --git a/Nop.Plugin.Api/Controllers/WebHookRegistrationsController.cs b/Nop.Plugin.Api/Controllers/WebHookRegistrationsController.cs index c567046..b246167 100644 --- a/Nop.Plugin.Api/Controllers/WebHookRegistrationsController.cs +++ b/Nop.Plugin.Api/Controllers/WebHookRegistrationsController.cs @@ -1,434 +1,434 @@ -using Nop.Plugin.Api.Attributes; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Nop.Plugin.Api.JSON.Serializers; -using Nop.Services.Customers; -using Nop.Services.Discounts; -using Nop.Services.Localization; -using Nop.Services.Logging; -using Nop.Services.Security; -using Nop.Services.Stores; -using Nop.Core.Domain.Stores; -using System.Net.Http; -using System.Net; -using System.Globalization; -using Nop.Core; -using Nop.Plugin.Api.Constants; -using Nop.Services.Media; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNet.WebHooks; -using Nop.Plugin.Api.Services; - -namespace Nop.Plugin.Api.Controllers -{ - using System.Security; - using System.Security.Claims; - using IdentityServer4.EntityFramework.Entities; - using IdentityServer4.Stores; - using Microsoft.AspNetCore.Authentication.JwtBearer; - using Microsoft.AspNetCore.Authorization; - using Microsoft.AspNetCore.Http; - using Nop.Plugin.Api.JSON.Serializers; - - [ApiAuthorize(Policy = JwtBearerDefaults.AuthenticationScheme, AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] - public class WebHookRegistrationsController : BaseApiController - { - private const string ErrorPropertyKey = "webhook"; - private const string PRIVATE_FILTER_PREFIX = "MS_Private_"; - - private readonly IWebHookManager _manager; - private readonly IWebHookStore _store; - private readonly IWebHookFilterManager _filterManager; - private readonly IStoreContext _storeContext; - private readonly IHttpContextAccessor _httpContextAccessor; - private readonly IClientStore _clientStore; - - public WebHookRegistrationsController(IJsonFieldsSerializer jsonFieldsSerializer, - IAclService aclService, - ICustomerService customerService, - IStoreMappingService storeMappingService, - IStoreService storeService, - IDiscountService discountService, - ICustomerActivityService customerActivityService, - ILocalizationService localizationService, - IPictureService pictureService, - IStoreContext storeContext, - IWebHookService webHookService, - IHttpContextAccessor httpContextAccessor, - IClientStore clientStore) - : base(jsonFieldsSerializer, - aclService, customerService, - storeMappingService, - storeService, - discountService, - customerActivityService, - localizationService, - pictureService) - { - _storeContext = storeContext; - _manager = webHookService.GetWebHookManager(); - _store = webHookService.GetWebHookStore(); - _filterManager = webHookService.GetWebHookFilterManager(); - _httpContextAccessor = httpContextAccessor; - _clientStore = clientStore; - } - - /// - /// Gets all registered WebHooks for a given user. - /// - /// A collection containing the registered instances for a given user. - [HttpGet] - [Route("/api/webhooks/registrations")] - [ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK)] - [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] - [GetRequestsErrorInterceptorActionFilter] - public async Task> GetAllWebHooks() - { - string userId = GetUserId(); - IEnumerable webHooks = await _store.GetAllWebHooksAsync(userId); - RemovePrivateFilters(webHooks); - return webHooks; - } - - /// - /// Looks up a registered WebHook with the given for a given user. - /// - /// The registered instance for a given user. - [HttpGet] - [Route("/api/webhooks/registrations/{id}",Name = WebHookNames.GetWebhookByIdAction)] - [ProducesResponseType(typeof(WebHook), (int)HttpStatusCode.OK)] - [ProducesResponseType(typeof(string), (int)HttpStatusCode.NotFound)] - [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] - [GetRequestsErrorInterceptorActionFilter] - public async Task GetWebHookById(string id) - { - string userId = GetUserId(); - WebHook webHook = await _store.LookupWebHookAsync(userId, id); - if (webHook != null) - { - RemovePrivateFilters(new[] { webHook }); - return Ok(webHook); - } - - return NotFound(); - } - - /// - /// Registers a new WebHook for a given user. - /// - /// The to create. - [HttpPost] - [Route("/api/webhooks/registrations")] - [ProducesResponseType(typeof(StoreResult), (int)HttpStatusCode.OK)] - [ProducesResponseType(typeof(string), (int)HttpStatusCode.NotFound)] - [ProducesResponseType(typeof(HttpResponseMessage), (int)HttpStatusCode.InternalServerError)] - [ProducesResponseType(typeof(HttpResponseMessage), (int)HttpStatusCode.Conflict)] - [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] - public async Task RegisterWebHook([FromBody]WebHook webHook) - { - if (!ModelState.IsValid) - { - return Error(); - } - - if (webHook == null) - { - return BadRequest(); - } - - string userId = GetUserId(); - - try - { - await VerifyFilters(webHook); - await VerifyWebHook(webHook); - } - catch (VerificationException ex) - { - return BadRequest(ex.Message); - } - - // In order to ensure that a web hook filter is not registered multiple times for the same uri - // we remove the already registered filters from the current web hook. - // If the same filters are registered multiple times with the same uri, the web hook event will be - // sent for each registration. - IEnumerable existingWebhooks = await GetAllWebHooks(); - IEnumerable existingWebhooksForTheSameUri = existingWebhooks.Where(wh => wh.WebHookUri == webHook.WebHookUri); - - foreach (var existingWebHook in existingWebhooksForTheSameUri) - { - webHook.Filters.ExceptWith(existingWebHook.Filters); - - if (!webHook.Filters.Any()) - { - string msg = _localizationService.GetResource("Api.WebHooks.CouldNotRegisterDuplicateWebhook"); - return Error(HttpStatusCode.Conflict, ErrorPropertyKey, msg); - } - } - - try - { - // Validate the provided WebHook ID (or force one to be created on server side) - if (Request == null) - { - throw new ArgumentNullException(nameof(Request)); - } - - // Ensure we have a normalized ID for the WebHook - webHook.Id = null; - - // Add WebHook for this user. - StoreResult result = await _store.InsertWebHookAsync(userId, webHook); - - if (result == StoreResult.Success) - { - return CreatedAtRoute(WebHookNames.GetWebhookByIdAction, new { id = webHook.Id }, webHook); - } - return CreateHttpResult(result); - } - catch (Exception ex) - { - string msg = string.Format(CultureInfo.InvariantCulture, _localizationService.GetResource("Api.WebHooks.CouldNotRegisterWebhook"), ex.Message); - //Configuration.DependencyResolver.GetLogger().Error(msg, ex); - return Error(HttpStatusCode.Conflict, ErrorPropertyKey, msg); - } - } - - /// - /// Updates an existing WebHook registration. - /// - /// The WebHook ID. - /// The new to use. - [HttpPut] - [Route("/api/webhooks/registrations/{id}")] - [ProducesResponseType(typeof(StoreResult), (int)HttpStatusCode.OK)] - [ProducesResponseType(typeof(string), (int)HttpStatusCode.NotFound)] - [ProducesResponseType(typeof(HttpResponseMessage), (int)HttpStatusCode.InternalServerError)] - [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] - [ProducesResponseType(typeof(string), (int)HttpStatusCode.BadRequest)] - public async Task UpdateWebHook(string id, WebHook webHook) - { - if (webHook == null) - { - return BadRequest(); - } - if (!string.Equals(id, webHook.Id, StringComparison.OrdinalIgnoreCase)) - { - return BadRequest(); - } - - string userId = GetUserId(); - await VerifyFilters(webHook); - await VerifyWebHook(webHook); - - try - { - StoreResult result = await _store.UpdateWebHookAsync(userId, webHook); - return CreateHttpResult(result); - } - catch (Exception ex) - { - string msg = string.Format(CultureInfo.InvariantCulture, _localizationService.GetResource("Api.WebHooks.CouldNotUpdateWebhook"), ex.Message); - // Configuration.DependencyResolver.GetLogger().Error(msg, ex); - return Error(HttpStatusCode.InternalServerError, ErrorPropertyKey, msg); - } - } - - /// - /// Deletes an existing WebHook registration. - /// - /// The WebHook ID. - [HttpDelete] - [Route("/api/webhooks/registrations/{id}")] - [ProducesResponseType(typeof(StoreResult), (int)HttpStatusCode.OK)] - [ProducesResponseType(typeof(HttpResponseMessage), (int)HttpStatusCode.InternalServerError)] - [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] - public async Task DeleteWebHook(string id) - { - string userId = GetUserId(); - - try - { - StoreResult result = await _store.DeleteWebHookAsync(userId, id); - return CreateHttpResult(result); - } - catch (Exception ex) - { - string msg = string.Format(CultureInfo.InvariantCulture, _localizationService.GetResource("Api.WebHooks.CouldNotDeleteWebhook"), ex.Message); - //Configuration.DependencyResolver.GetLogger().Error(msg, ex); - return Error(HttpStatusCode.InternalServerError, ErrorPropertyKey, msg); - } - } - - /// - /// Deletes all existing WebHook registrations. - /// - [HttpDelete] - [Route("/api/webhooks/registrations")] - [ProducesResponseType(typeof(void), (int)HttpStatusCode.OK)] - [ProducesResponseType(typeof(HttpResponseMessage), (int)HttpStatusCode.InternalServerError)] - [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] - public async Task DeleteAllWebHooks() - { - string userId = GetUserId(); - - try - { - await _store.DeleteAllWebHooksAsync(userId); - return Ok(); - } - catch (Exception ex) - { - string msg = string.Format(CultureInfo.InvariantCulture, _localizationService.GetResource("Api.WebHooks.CouldNotDeleteWebhooks"), ex.Message); - // Configuration.DependencyResolver.GetLogger().Error(msg, ex); - return Error(HttpStatusCode.InternalServerError, ErrorPropertyKey, msg); - } - } - - /// - /// Ensure that the provided only has registered filters. - /// - protected virtual async Task VerifyFilters(WebHook webHook) - { - if (webHook == null) - { - throw new ArgumentNullException(nameof(webHook)); - } - - // If there are no filters then add our wildcard filter. - if (webHook.Filters.Count == 0) - { - webHook.Filters.Add(WildcardWebHookFilterProvider.Name); - return; - } - - IDictionary filters = await _filterManager.GetAllWebHookFiltersAsync(); - HashSet normalizedFilters = new HashSet(); - List invalidFilters = new List(); - foreach (string filter in webHook.Filters) - { - WebHookFilter hookFilter; - if (filters.TryGetValue(filter, out hookFilter)) - { - normalizedFilters.Add(hookFilter.Name); - } - else - { - invalidFilters.Add(filter); - } - } - - if (invalidFilters.Count > 0) - { - string invalidFiltersMsg = string.Join(", ", invalidFilters); - string link = Url.Link(WebHookNames.FiltersGetAction, null); - string msg = string.Format(CultureInfo.CurrentCulture, _localizationService.GetResource("Api.WebHooks.InvalidFilters"), invalidFiltersMsg, link); - //Configuration.DependencyResolver.GetLogger().Info(msg); - - throw new VerificationException(msg); - } - else - { - webHook.Filters.Clear(); - foreach (string filter in normalizedFilters) - { - webHook.Filters.Add(filter); - } - } - } - - /// - /// Removes all private filters from registered WebHooks. - /// - protected virtual void RemovePrivateFilters(IEnumerable webHooks) - { - if (webHooks == null) - { - throw new ArgumentNullException(nameof(webHooks)); - } - - foreach (WebHook webHook in webHooks) - { - var filters = webHook.Filters.Where(f => f.StartsWith(PRIVATE_FILTER_PREFIX, StringComparison.OrdinalIgnoreCase)).ToArray(); - foreach (string filter in filters) - { - webHook.Filters.Remove(filter); - } - } - } - - /// - /// Ensures that the provided has a reachable Web Hook URI unless - /// the WebHook URI has a NoEcho query parameter. - /// - private async Task VerifyWebHook(WebHook webHook) - { - if (webHook == null) - { - throw new ArgumentNullException(nameof(webHook)); - } - - // If no secret is provided then we create one here. This allows for scenarios - // where the caller may use a secret directly embedded in the WebHook URI, or - // has some other way of enforcing security. - if (string.IsNullOrEmpty(webHook.Secret)) - { - webHook.Secret = Guid.NewGuid().ToString("N"); - } - - try - { - await _manager.VerifyWebHookAsync(webHook); - } - catch (Exception ex) - { - throw new VerificationException(ex.Message); - } - } - - /// - /// Gets the user ID for this request. - /// - private string GetUserId() - { - // If we are here the client is already authorized. - // So there is a client ID and the client is active. - var clientId = - _httpContextAccessor.HttpContext.User.FindFirst("client_id")?.Value; - - var storeId = _storeContext.CurrentStore.Id; - - var webHookUser = clientId + "-" + storeId; - - return webHookUser; - } - - /// - /// Creates an based on the provided . - /// - /// The result to use when creating the . - /// An initialized . - private IActionResult CreateHttpResult(StoreResult result) - { - switch (result) - { - case StoreResult.Success: - return Ok(); - - case StoreResult.Conflict: - return Error(HttpStatusCode.Conflict); - - case StoreResult.NotFound: - return NotFound(); - - case StoreResult.OperationError: - return BadRequest(); - - default: - return Error(HttpStatusCode.InternalServerError); - } - } - } -} +using Nop.Plugin.Api.Attributes; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Nop.Plugin.Api.JSON.Serializers; +using Nop.Services.Customers; +using Nop.Services.Discounts; +using Nop.Services.Localization; +using Nop.Services.Logging; +using Nop.Services.Security; +using Nop.Services.Stores; +using Nop.Core.Domain.Stores; +using System.Net.Http; +using System.Net; +using System.Globalization; +using Nop.Core; +using Nop.Plugin.Api.Constants; +using Nop.Services.Media; +using Microsoft.AspNetCore.Mvc; +using Nop.Plugin.Api.Services; + +namespace Nop.Plugin.Api.Controllers +{ + using System.Security; + using System.Security.Claims; + using IdentityServer4.EntityFramework.Entities; + using IdentityServer4.Stores; + using Microsoft.AspNetCore.Authentication.JwtBearer; + using Microsoft.AspNetCore.Authorization; + using Microsoft.AspNetCore.Http; + using Microsoft.AspNetCore.WebHooks; + using Nop.Plugin.Api.JSON.Serializers; + + [ApiAuthorize(Policy = JwtBearerDefaults.AuthenticationScheme, AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] + public class WebHookRegistrationsController : BaseApiController + { + private const string ErrorPropertyKey = "webhook"; + private const string PRIVATE_FILTER_PREFIX = "MS_Private_"; + + private readonly IWebHookManager _manager; + private readonly IWebHookStore _store; + private readonly IWebHookFilterManager _filterManager; + private readonly IStoreContext _storeContext; + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly IClientStore _clientStore; + + public WebHookRegistrationsController(IJsonFieldsSerializer jsonFieldsSerializer, + IAclService aclService, + ICustomerService customerService, + IStoreMappingService storeMappingService, + IStoreService storeService, + IDiscountService discountService, + ICustomerActivityService customerActivityService, + ILocalizationService localizationService, + IPictureService pictureService, + IStoreContext storeContext, + IWebHookService webHookService, + IHttpContextAccessor httpContextAccessor, + IClientStore clientStore) + : base(jsonFieldsSerializer, + aclService, customerService, + storeMappingService, + storeService, + discountService, + customerActivityService, + localizationService, + pictureService) + { + _storeContext = storeContext; + _manager = webHookService.GetWebHookManager(); + _store = webHookService.GetWebHookStore(); + _filterManager = webHookService.GetWebHookFilterManager(); + _httpContextAccessor = httpContextAccessor; + _clientStore = clientStore; + } + + /// + /// Gets all registered WebHooks for a given user. + /// + /// A collection containing the registered instances for a given user. + [HttpGet] + [Route("/api/webhooks/registrations")] + [ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] + [GetRequestsErrorInterceptorActionFilter] + public async Task> GetAllWebHooks() + { + string userId = GetUserId(); + IEnumerable webHooks = await _store.GetAllWebHooksAsync(userId); + RemovePrivateFilters(webHooks); + return webHooks; + } + + /// + /// Looks up a registered WebHook with the given for a given user. + /// + /// The registered instance for a given user. + [HttpGet] + [Route("/api/webhooks/registrations/{id}", Name = WebHookNames.GetWebhookByIdAction)] + [ProducesResponseType(typeof(WebHook), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.NotFound)] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] + [GetRequestsErrorInterceptorActionFilter] + public async Task GetWebHookById(string id) + { + string userId = GetUserId(); + WebHook webHook = await _store.LookupWebHookAsync(userId, id); + if (webHook != null) + { + RemovePrivateFilters(new[] { webHook }); + return Ok(webHook); + } + + return NotFound(); + } + + /// + /// Registers a new WebHook for a given user. + /// + /// The to create. + [HttpPost] + [Route("/api/webhooks/registrations")] + [ProducesResponseType(typeof(StoreResult), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.NotFound)] + [ProducesResponseType(typeof(HttpResponseMessage), (int)HttpStatusCode.InternalServerError)] + [ProducesResponseType(typeof(HttpResponseMessage), (int)HttpStatusCode.Conflict)] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] + public async Task RegisterWebHook([FromBody]WebHook webHook) + { + if (!ModelState.IsValid) + { + return Error(); + } + + if (webHook == null) + { + return BadRequest(); + } + + string userId = GetUserId(); + + try + { + await VerifyFilters(webHook); + await VerifyWebHook(webHook); + } + catch (VerificationException ex) + { + return BadRequest(ex.Message); + } + + // In order to ensure that a web hook filter is not registered multiple times for the same uri + // we remove the already registered filters from the current web hook. + // If the same filters are registered multiple times with the same uri, the web hook event will be + // sent for each registration. + IEnumerable existingWebhooks = await GetAllWebHooks(); + IEnumerable existingWebhooksForTheSameUri = existingWebhooks.Where(wh => wh.WebHookUri == webHook.WebHookUri); + + foreach (var existingWebHook in existingWebhooksForTheSameUri) + { + webHook.Filters.ExceptWith(existingWebHook.Filters); + + if (!webHook.Filters.Any()) + { + string msg = LocalizationService.GetResource("Api.WebHooks.CouldNotRegisterDuplicateWebhook"); + return Error(HttpStatusCode.Conflict, ErrorPropertyKey, msg); + } + } + + try + { + // Validate the provided WebHook ID (or force one to be created on server side) + if (Request == null) + { + throw new ArgumentNullException(nameof(Request)); + } + + // Ensure we have a normalized ID for the WebHook + webHook.Id = null; + + // Add WebHook for this user. + StoreResult result = await _store.InsertWebHookAsync(userId, webHook); + + if (result == StoreResult.Success) + { + return CreatedAtRoute(WebHookNames.GetWebhookByIdAction, new { id = webHook.Id }, webHook); + } + return CreateHttpResult(result); + } + catch (Exception ex) + { + string msg = string.Format(CultureInfo.InvariantCulture, LocalizationService.GetResource("Api.WebHooks.CouldNotRegisterWebhook"), ex.Message); + //Configuration.DependencyResolver.GetLogger().Error(msg, ex); + return Error(HttpStatusCode.Conflict, ErrorPropertyKey, msg); + } + } + + /// + /// Updates an existing WebHook registration. + /// + /// The WebHook ID. + /// The new to use. + [HttpPut] + [Route("/api/webhooks/registrations/{id}")] + [ProducesResponseType(typeof(StoreResult), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.NotFound)] + [ProducesResponseType(typeof(HttpResponseMessage), (int)HttpStatusCode.InternalServerError)] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.BadRequest)] + public async Task UpdateWebHook(string id, WebHook webHook) + { + if (webHook == null) + { + return BadRequest(); + } + if (!string.Equals(id, webHook.Id, StringComparison.OrdinalIgnoreCase)) + { + return BadRequest(); + } + + string userId = GetUserId(); + await VerifyFilters(webHook); + await VerifyWebHook(webHook); + + try + { + StoreResult result = await _store.UpdateWebHookAsync(userId, webHook); + return CreateHttpResult(result); + } + catch (Exception ex) + { + string msg = string.Format(CultureInfo.InvariantCulture, LocalizationService.GetResource("Api.WebHooks.CouldNotUpdateWebhook"), ex.Message); + // Configuration.DependencyResolver.GetLogger().Error(msg, ex); + return Error(HttpStatusCode.InternalServerError, ErrorPropertyKey, msg); + } + } + + /// + /// Deletes an existing WebHook registration. + /// + /// The WebHook ID. + [HttpDelete] + [Route("/api/webhooks/registrations/{id}")] + [ProducesResponseType(typeof(StoreResult), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(HttpResponseMessage), (int)HttpStatusCode.InternalServerError)] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] + public async Task DeleteWebHook(string id) + { + string userId = GetUserId(); + + try + { + StoreResult result = await _store.DeleteWebHookAsync(userId, id); + return CreateHttpResult(result); + } + catch (Exception ex) + { + string msg = string.Format(CultureInfo.InvariantCulture, LocalizationService.GetResource("Api.WebHooks.CouldNotDeleteWebhook"), ex.Message); + //Configuration.DependencyResolver.GetLogger().Error(msg, ex); + return Error(HttpStatusCode.InternalServerError, ErrorPropertyKey, msg); + } + } + + /// + /// Deletes all existing WebHook registrations. + /// + [HttpDelete] + [Route("/api/webhooks/registrations")] + [ProducesResponseType(typeof(void), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(HttpResponseMessage), (int)HttpStatusCode.InternalServerError)] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)] + public async Task DeleteAllWebHooks() + { + string userId = GetUserId(); + + try + { + await _store.DeleteAllWebHooksAsync(userId); + return Ok(); + } + catch (Exception ex) + { + string msg = string.Format(CultureInfo.InvariantCulture, LocalizationService.GetResource("Api.WebHooks.CouldNotDeleteWebhooks"), ex.Message); + // Configuration.DependencyResolver.GetLogger().Error(msg, ex); + return Error(HttpStatusCode.InternalServerError, ErrorPropertyKey, msg); + } + } + + /// + /// Ensure that the provided only has registered filters. + /// + protected virtual async Task VerifyFilters(WebHook webHook) + { + if (webHook == null) + { + throw new ArgumentNullException(nameof(webHook)); + } + + // If there are no filters then add our wildcard filter. + if (webHook.Filters.Count == 0) + { + webHook.Filters.Add(WildcardWebHookFilterProvider.Name); + return; + } + + IDictionary filters = await _filterManager.GetAllWebHookFiltersAsync(); + HashSet normalizedFilters = new HashSet(); + List invalidFilters = new List(); + foreach (string filter in webHook.Filters) + { + WebHookFilter hookFilter; + if (filters.TryGetValue(filter, out hookFilter)) + { + normalizedFilters.Add(hookFilter.Name); + } + else + { + invalidFilters.Add(filter); + } + } + + if (invalidFilters.Count > 0) + { + string invalidFiltersMsg = string.Join(", ", invalidFilters); + string link = Url.Link(WebHookNames.FiltersGetAction, null); + string msg = string.Format(CultureInfo.CurrentCulture, LocalizationService.GetResource("Api.WebHooks.InvalidFilters"), invalidFiltersMsg, link); + //Configuration.DependencyResolver.GetLogger().Info(msg); + + throw new VerificationException(msg); + } + else + { + webHook.Filters.Clear(); + foreach (string filter in normalizedFilters) + { + webHook.Filters.Add(filter); + } + } + } + + /// + /// Removes all private filters from registered WebHooks. + /// + protected virtual void RemovePrivateFilters(IEnumerable webHooks) + { + if (webHooks == null) + { + throw new ArgumentNullException(nameof(webHooks)); + } + + foreach (WebHook webHook in webHooks) + { + var filters = webHook.Filters.Where(f => f.StartsWith(PRIVATE_FILTER_PREFIX, StringComparison.OrdinalIgnoreCase)).ToArray(); + foreach (string filter in filters) + { + webHook.Filters.Remove(filter); + } + } + } + + /// + /// Ensures that the provided has a reachable Web Hook URI unless + /// the WebHook URI has a NoEcho query parameter. + /// + private async Task VerifyWebHook(WebHook webHook) + { + if (webHook == null) + { + throw new ArgumentNullException(nameof(webHook)); + } + + // If no secret is provided then we create one here. This allows for scenarios + // where the caller may use a secret directly embedded in the WebHook URI, or + // has some other way of enforcing security. + if (string.IsNullOrEmpty(webHook.Secret)) + { + webHook.Secret = Guid.NewGuid().ToString("N"); + } + + try + { + await _manager.VerifyWebHookAsync(webHook); + } + catch (Exception ex) + { + throw new VerificationException(ex.Message); + } + } + + /// + /// Gets the user ID for this request. + /// + private string GetUserId() + { + // If we are here the client is already authorized. + // So there is a client ID and the client is active. + var clientId = + _httpContextAccessor.HttpContext.User.FindFirst("client_id")?.Value; + + var storeId = _storeContext.CurrentStore.Id; + + var webHookUser = clientId + "-" + storeId; + + return webHookUser; + } + + /// + /// Creates an based on the provided . + /// + /// The result to use when creating the . + /// An initialized . + private IActionResult CreateHttpResult(StoreResult result) + { + switch (result) + { + case StoreResult.Success: + return Ok(); + + case StoreResult.Conflict: + return Error(HttpStatusCode.Conflict); + + case StoreResult.NotFound: + return NotFound(); + + case StoreResult.OperationError: + return BadRequest(); + + default: + return Error(HttpStatusCode.InternalServerError); + } + } + } +} diff --git a/Nop.Plugin.Api/Converters/ApiTypeConverter.cs b/Nop.Plugin.Api/Converters/ApiTypeConverter.cs index 53a6237..b27c343 100644 --- a/Nop.Plugin.Api/Converters/ApiTypeConverter.cs +++ b/Nop.Plugin.Api/Converters/ApiTypeConverter.cs @@ -1,125 +1,125 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; - -namespace Nop.Plugin.Api.Converters -{ - public class ApiTypeConverter : IApiTypeConverter - { - /// - /// Converts the value, which should be in ISO 8601 format to UTC time or null if not valid - /// - /// The time format in ISO 8601. If no timezone or offset specified we assume it is in UTC - /// The time in UTC or null if the time is not valid - public DateTime? ToUtcDateTimeNullable(string value) - { - DateTime result; - - var formats = new string[] - { - "yyyy", - "yyyy-MM", - "yyyy-MM-dd", - "yyyy-MM-ddTHH:mm", - "yyyy-MM-ddTHH:mm:ss", - "yyyy-MM-ddTHH:mm:sszzz", - "yyyy-MM-ddTHH:mm:ss.FFFFFFFK" - }; - - if (DateTime.TryParseExact(value, formats, CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, out result)) - { - // only if parsed in Local time then we need to convert it to UTC - if (result.Kind == DateTimeKind.Local) - { - return result.ToUniversalTime(); - } - - return result; - } - - return null; - } - - public int ToInt(string value) - { - int result; - - if (int.TryParse(value, out result)) - { - return result; - } - - return 0; - } - - public int? ToIntNullable(string value) - { - int result; - - if (int.TryParse(value, out result)) - { - return result; - } - - return null; - } - - public IList ToListOfInts(string value) - { - if (!string.IsNullOrEmpty(value)) - { - List stringIds = value.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList(); - List intIds = new List(); - - foreach (var id in stringIds) - { - int intId; - if (int.TryParse(id, out intId)) - { - intIds.Add(intId); - } - } - - intIds = intIds.Distinct().ToList(); - return intIds.Count > 0 ? intIds : null; - } - - return null; - } - - public bool? ToStatus(string value) - { - if (!string.IsNullOrEmpty(value)) - { - if (value.Equals("published", StringComparison.InvariantCultureIgnoreCase)) - { - return true; - } - else if (value.Equals("unpublished", StringComparison.InvariantCultureIgnoreCase)) - { - return false; - } - } - - return null; - } - - public object ToEnumNullable(string value, Type type) - { - if (!string.IsNullOrEmpty(value)) - { - Type enumType = Nullable.GetUnderlyingType(type); - - var enumNames = enumType.GetEnumNames(); - - if (enumNames.Any(x => x.ToLowerInvariant().Equals(value.ToLowerInvariant()))) - { - return Enum.Parse(enumType, value, true); - } - } - - return null; - } - } +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; + +namespace Nop.Plugin.Api.Converters +{ + public class ApiTypeConverter : IApiTypeConverter + { + /// + /// Converts the value, which should be in ISO 8601 format to UTC time or null if not valid + /// + /// The time format in ISO 8601. If no timezone or offset specified we assume it is in UTC + /// The time in UTC or null if the time is not valid + public DateTime? ToUtcDateTimeNullable(string value) + { + DateTime result; + + var formats = new string[] + { + "yyyy", + "yyyy-MM", + "yyyy-MM-dd", + "yyyy-MM-ddTHH:mm", + "yyyy-MM-ddTHH:mm:ss", + "yyyy-MM-ddTHH:mm:sszzz", + "yyyy-MM-ddTHH:mm:ss.FFFFFFFK" + }; + + if (DateTime.TryParseExact(value, formats, CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, out result)) + { + // only if parsed in Local time then we need to convert it to UTC + if (result.Kind == DateTimeKind.Local) + { + return result.ToUniversalTime(); + } + + return result; + } + + return null; + } + + public int ToInt(string value) + { + int result; + + if (int.TryParse(value, out result)) + { + return result; + } + + return 0; + } + + public int? ToIntNullable(string value) + { + int result; + + if (int.TryParse(value, out result)) + { + return result; + } + + return null; + } + + public IList ToListOfInts(string value) + { + if (!string.IsNullOrEmpty(value)) + { + var stringIds = value.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList(); + var intIds = new List(); + + foreach (var id in stringIds) + { + int intId; + if (int.TryParse(id, out intId)) + { + intIds.Add(intId); + } + } + + intIds = intIds.Distinct().ToList(); + return intIds.Count > 0 ? intIds : null; + } + + return null; + } + + public bool? ToStatus(string value) + { + if (!string.IsNullOrEmpty(value)) + { + if (value.Equals("published", StringComparison.InvariantCultureIgnoreCase)) + { + return true; + } + else if (value.Equals("unpublished", StringComparison.InvariantCultureIgnoreCase)) + { + return false; + } + } + + return null; + } + + public object ToEnumNullable(string value, Type type) + { + if (!string.IsNullOrEmpty(value)) + { + var enumType = Nullable.GetUnderlyingType(type); + + var enumNames = enumType.GetEnumNames(); + + if (enumNames.Any(x => x.ToLowerInvariant().Equals(value.ToLowerInvariant()))) + { + return Enum.Parse(enumType, value, true); + } + } + + return null; + } + } } \ No newline at end of file diff --git a/Nop.Plugin.Api/Converters/ObjectConverter.cs b/Nop.Plugin.Api/Converters/ObjectConverter.cs index 89c9006..0d3c2a3 100644 --- a/Nop.Plugin.Api/Converters/ObjectConverter.cs +++ b/Nop.Plugin.Api/Converters/ObjectConverter.cs @@ -1,78 +1,78 @@ -using System; -using System.Collections.Generic; -using System.Reflection; - -namespace Nop.Plugin.Api.Converters -{ - public class ObjectConverter : IObjectConverter - { - private readonly IApiTypeConverter _apiTypeConverter; - - public ObjectConverter(IApiTypeConverter apiTypeConverter) - { - _apiTypeConverter = apiTypeConverter; - } - - public T ToObject(ICollection> source) - where T : class, new() - { - T someObject = new T(); - Type someObjectType = someObject.GetType(); - - if (source != null) - { - foreach (KeyValuePair item in source) - { - var itemKey = item.Key.Replace("_", string.Empty); - var currentProperty = someObjectType.GetProperty(itemKey, - BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance); - - if (currentProperty != null) - { - currentProperty.SetValue(someObject, To(item.Value, currentProperty.PropertyType), null); - } - } - } - - return someObject; - } - - private object To(string value, Type type) - { - if (type == typeof(DateTime?)) - { - return _apiTypeConverter.ToUtcDateTimeNullable(value); - } - else if (type == typeof (int?)) - { - return _apiTypeConverter.ToIntNullable(value); - } - else if (type == typeof(int)) - { - return _apiTypeConverter.ToInt(value); - } - else if (type == typeof(List)) - { - return _apiTypeConverter.ToListOfInts(value); - } - else if(type == typeof(bool?)) - { - // Because currently status is the only boolean and we need to accept published and unpublished statuses. - return _apiTypeConverter.ToStatus(value); - } - else if (IsNullableEnum(type)) - { - return _apiTypeConverter.ToEnumNullable(value, type); - } - - // It should be the last resort, because it is not exception safe. - return Convert.ChangeType(value, type); - } - - private bool IsNullableEnum(Type t) - { - Type u = Nullable.GetUnderlyingType(t); - return (u != null) && u.IsEnum; - } - } +using System; +using System.Collections.Generic; +using System.Reflection; + +namespace Nop.Plugin.Api.Converters +{ + public class ObjectConverter : IObjectConverter + { + private readonly IApiTypeConverter _apiTypeConverter; + + public ObjectConverter(IApiTypeConverter apiTypeConverter) + { + _apiTypeConverter = apiTypeConverter; + } + + public T ToObject(ICollection> source) + where T : class, new() + { + var someObject = new T(); + var someObjectType = someObject.GetType(); + + if (source != null) + { + foreach (var item in source) + { + var itemKey = item.Key.Replace("_", string.Empty); + var currentProperty = someObjectType.GetProperty(itemKey, + BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance); + + if (currentProperty != null) + { + currentProperty.SetValue(someObject, To(item.Value, currentProperty.PropertyType), null); + } + } + } + + return someObject; + } + + private object To(string value, Type type) + { + if (type == typeof(DateTime?)) + { + return _apiTypeConverter.ToUtcDateTimeNullable(value); + } + else if (type == typeof (int?)) + { + return _apiTypeConverter.ToIntNullable(value); + } + else if (type == typeof(int)) + { + return _apiTypeConverter.ToInt(value); + } + else if (type == typeof(List)) + { + return _apiTypeConverter.ToListOfInts(value); + } + else if(type == typeof(bool?)) + { + // Because currently status is the only boolean and we need to accept published and unpublished statuses. + return _apiTypeConverter.ToStatus(value); + } + else if (IsNullableEnum(type)) + { + return _apiTypeConverter.ToEnumNullable(value, type); + } + + // It should be the last resort, because it is not exception safe. + return Convert.ChangeType(value, type); + } + + private bool IsNullableEnum(Type t) + { + var u = Nullable.GetUnderlyingType(t); + return (u != null) && u.IsEnum; + } + } } \ No newline at end of file diff --git a/Nop.Plugin.Api/DTOs/AddressDto.cs b/Nop.Plugin.Api/DTOs/AddressDto.cs index 67f07a5..74bad77 100644 --- a/Nop.Plugin.Api/DTOs/AddressDto.cs +++ b/Nop.Plugin.Api/DTOs/AddressDto.cs @@ -1,19 +1,15 @@ using System; using FluentValidation.Attributes; using Newtonsoft.Json; +using Nop.Plugin.Api.DTOs.Base; using Nop.Plugin.Api.Validators; namespace Nop.Plugin.Api.DTOs { [JsonObject(Title = "address")] [Validator(typeof(AddressDtoValidator))] - public class AddressDto + public class AddressDto : BaseDto { - /// - /// Gets or sets the first name - /// - [JsonProperty("id")] - public string Id { get; set; } /// /// Gets or sets the first name /// diff --git a/Nop.Plugin.Api/DTOs/Base/BaseDto.cs b/Nop.Plugin.Api/DTOs/Base/BaseDto.cs new file mode 100644 index 0000000..539a687 --- /dev/null +++ b/Nop.Plugin.Api/DTOs/Base/BaseDto.cs @@ -0,0 +1,10 @@ +using Newtonsoft.Json; + +namespace Nop.Plugin.Api.DTOs.Base +{ + public abstract class BaseDto + { + [JsonProperty("id")] + public int Id { get; set; } + } +} \ No newline at end of file diff --git a/Nop.Plugin.Api/DTOs/Categories/CategoryDto.cs b/Nop.Plugin.Api/DTOs/Categories/CategoryDto.cs index 501b5ce..621a935 100644 --- a/Nop.Plugin.Api/DTOs/Categories/CategoryDto.cs +++ b/Nop.Plugin.Api/DTOs/Categories/CategoryDto.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using FluentValidation.Attributes; using Newtonsoft.Json; +using Nop.Plugin.Api.DTOs.Base; using Nop.Plugin.Api.DTOs.Images; using Nop.Plugin.Api.DTOs.Languages; using Nop.Plugin.Api.Validators; @@ -10,7 +11,7 @@ namespace Nop.Plugin.Api.DTOs.Categories { [Validator(typeof(CategoryDtoValidator))] [JsonObject(Title = "category")] - public class CategoryDto + public class CategoryDto : BaseDto { private ImageDto _imageDto; private List _localizedNames; @@ -18,9 +19,6 @@ public class CategoryDto private List _discountIds; private List _roleIds; - [JsonProperty("id")] - public string Id { get; set; } - [JsonProperty("name")] public string Name { get; set; } diff --git a/Nop.Plugin.Api/DTOs/CustomerRoles/CustomerRoleDto.cs b/Nop.Plugin.Api/DTOs/CustomerRoles/CustomerRoleDto.cs index 665c00f..56c88c6 100644 --- a/Nop.Plugin.Api/DTOs/CustomerRoles/CustomerRoleDto.cs +++ b/Nop.Plugin.Api/DTOs/CustomerRoles/CustomerRoleDto.cs @@ -1,16 +1,11 @@ using Newtonsoft.Json; +using Nop.Plugin.Api.DTOs.Base; namespace Nop.Plugin.Api.DTOs.CustomerRoles { [JsonObject(Title = "customer_role")] - public class CustomerRoleDto + public class CustomerRoleDto : BaseDto { - /// - /// Gets or sets the store ID - /// - [JsonProperty("id")] - public string Id { get; set; } - /// /// Gets or sets the customer role name /// diff --git a/Nop.Plugin.Api/DTOs/Customers/BaseCustomerDto.cs b/Nop.Plugin.Api/DTOs/Customers/BaseCustomerDto.cs index feafb29..13670dc 100644 --- a/Nop.Plugin.Api/DTOs/Customers/BaseCustomerDto.cs +++ b/Nop.Plugin.Api/DTOs/Customers/BaseCustomerDto.cs @@ -1,15 +1,16 @@ using System; using System.Collections.Generic; using Newtonsoft.Json; +using Nop.Plugin.Api.DTOs.Base; namespace Nop.Plugin.Api.DTOs.Customers { - public class BaseCustomerDto + public class BaseCustomerDto : BaseDto { private List _roleIds; - [JsonProperty("id")] - public string Id { get; set; } + [JsonProperty("customer_guid")] + public Guid CustomerGuid { get; set; } [JsonProperty("username")] public string Username { get; set; } diff --git a/Nop.Plugin.Api/DTOs/Customers/CustomerDto.cs b/Nop.Plugin.Api/DTOs/Customers/CustomerDto.cs index e4e56b6..61db1ac 100644 --- a/Nop.Plugin.Api/DTOs/Customers/CustomerDto.cs +++ b/Nop.Plugin.Api/DTOs/Customers/CustomerDto.cs @@ -55,7 +55,7 @@ public ICollection ShoppingCartItems /// Gets or sets customer addresses /// [JsonProperty("addresses")] - public ICollection CustomerAddresses + public ICollection Addresses { get { diff --git a/Nop.Plugin.Api/DTOs/Images/ImageMappingDto.cs b/Nop.Plugin.Api/DTOs/Images/ImageMappingDto.cs index c0133ad..8b3b305 100644 --- a/Nop.Plugin.Api/DTOs/Images/ImageMappingDto.cs +++ b/Nop.Plugin.Api/DTOs/Images/ImageMappingDto.cs @@ -9,6 +9,9 @@ public class ImageMappingDto : ImageDto [JsonProperty("id")] public int Id { get; set; } + [JsonProperty("picture_id")] + public int PictureId { get; set; } + [JsonProperty("position")] public int Position { get; set; } } diff --git a/Nop.Plugin.Api/DTOs/Languages/LanguageDto.cs b/Nop.Plugin.Api/DTOs/Languages/LanguageDto.cs index b418e3e..bab7703 100644 --- a/Nop.Plugin.Api/DTOs/Languages/LanguageDto.cs +++ b/Nop.Plugin.Api/DTOs/Languages/LanguageDto.cs @@ -1,19 +1,14 @@ using System.Collections.Generic; using Newtonsoft.Json; +using Nop.Plugin.Api.DTOs.Base; namespace Nop.Plugin.Api.DTOs.Languages { [JsonObject(Title = "language")] - public class LanguageDto + public class LanguageDto : BaseDto { private List _storeIds; - /// - /// Gets or sets the store ID - /// - [JsonProperty("id")] - public string Id { get; set; } - /// /// Gets or sets the name /// diff --git a/Nop.Plugin.Api/DTOs/Manufacturers/DiscountManufacturerMappingDto.cs b/Nop.Plugin.Api/DTOs/Manufacturers/DiscountManufacturerMappingDto.cs new file mode 100644 index 0000000..7051ce9 --- /dev/null +++ b/Nop.Plugin.Api/DTOs/Manufacturers/DiscountManufacturerMappingDto.cs @@ -0,0 +1,22 @@ +using Newtonsoft.Json; +using Nop.Core.Domain.Catalog; +using Nop.Core.Domain.Discounts; +using Nop.Plugin.Api.DTOs.Base; + +namespace Nop.Plugin.Api.DTOs.Manufacturers +{ + [JsonObject(Title = "discount")] + //[Validator(typeof(ProductDtoValidator))] + public class DiscountManufacturerMappingDto : BaseDto + { + /// + /// Gets or sets the discount identifier + /// + [JsonProperty("discount_id")] + public int DiscountId { get; set; } + + + [JsonProperty("discount_name")] + public string DiscountName { get; set; } + } +} diff --git a/Nop.Plugin.Api/DTOs/Manufacturers/ManufacturerDto.cs b/Nop.Plugin.Api/DTOs/Manufacturers/ManufacturerDto.cs new file mode 100644 index 0000000..11a58fa --- /dev/null +++ b/Nop.Plugin.Api/DTOs/Manufacturers/ManufacturerDto.cs @@ -0,0 +1,202 @@ +using FluentValidation.Attributes; +using Newtonsoft.Json; +using Nop.Plugin.Api.DTOs.Base; +using Nop.Plugin.Api.DTOs.Images; +using Nop.Plugin.Api.DTOs.Languages; +using Nop.Plugin.Api.Validators; +using System; +using System.Collections.Generic; + +namespace Nop.Plugin.Api.DTOs.Manufacturers +{ + [JsonObject(Title = "manufacturer")] + [Validator(typeof(ManufacturerDtoValidator))] + public class ManufacturerDto : BaseDto + { + private ImageDto _imageDto; + private List _localizedNames; + private List _storeIds; + private List _discountIds; + private List _roleIds; + + /// + /// Gets or sets the name + /// + [JsonProperty("name")] + public string Name { get; set; } + + + /// + /// Gets or sets the localized names + /// + [JsonProperty("localized_names")] + public List LocalizedNames + { + get + { + return _localizedNames; + } + set + { + _localizedNames = value; + } + } + + /// + /// Gets or sets the description + /// + [JsonProperty("description")] + public string Description { get; set; } + + /// + /// Gets or sets a value of used manufacturer template identifier + /// + [JsonProperty("manufacturer_template_id")] + public int ManufacturerTemplateId { get; set; } + + /// + /// Gets or sets the meta keywords + /// + [JsonProperty("meta_keywords")] + public string MetaKeywords { get; set; } + + /// + /// Gets or sets the meta description + /// + [JsonProperty("meta_description")] + public string MetaDescription { get; set; } + + /// + /// Gets or sets the meta title + /// + [JsonProperty("meta_title")] + public string MetaTitle { get; set; } + + /// + /// Gets or sets the parent picture identifier + /// + [JsonProperty("picture_id")] + public int PictureId { get; set; } + + /// + /// Gets or sets the page size + /// + [JsonProperty("page_size")] + public int PageSize { get; set; } + + /// + /// Gets or sets a value indicating whether customers can select the page size + /// + [JsonProperty("allow_customers_to_select_page_size")] + public bool AllowCustomersToSelectPageSize { get; set; } + + /// + /// Gets or sets the available customer selectable page size options + /// + [JsonProperty("page_size_options")] + public string PageSizeOptions { get; set; } + + /// + /// Gets or sets the available price ranges + /// + [JsonProperty("price_ranges")] + public string PriceRanges { get; set; } + + /// + /// Gets or sets a value indicating whether the entity is subject to ACL + /// + [JsonProperty("subject_to_acl")] + public bool SubjectToAcl { get; set; } + + /// + /// Gets or sets a value indicating whether the entity is limited/restricted to certain stores + /// + [JsonProperty("limited_to_stores")] + public bool LimitedToStores { get; set; } + + /// + /// Gets or sets a value indicating whether the entity is published + /// + [JsonProperty("published")] + public bool Published { get; set; } + + /// + /// Gets or sets a value indicating whether the entity has been deleted + /// + [JsonProperty("deleted")] + public bool Deleted { get; set; } + + /// + /// Gets or sets the display order + /// + [JsonProperty("display_order")] + public int DisplayOrder { get; set; } + + /// + /// Gets or sets the date and time of instance creation + /// + [JsonProperty("created_on_utc")] + public DateTime CreatedOnUtc { get; set; } + + /// + /// Gets or sets the date and time of instance update + /// + [JsonProperty("updated_on_utc")] + public DateTime UpdatedOnUtc { get; set; } + + [JsonProperty("role_ids")] + public List RoleIds + { + get + { + return _roleIds; + } + set + { + _roleIds = value; + } + } + + [JsonProperty("discount_ids")] + public List DiscountIds + { + get + { + return _discountIds; + } + set + { + _discountIds = value; + } + } + + [JsonProperty("store_ids")] + public List StoreIds + { + get + { + return _storeIds; + } + set + { + _storeIds = value; + } + } + + [JsonProperty("image")] + public ImageDto Image + { + get + { + return _imageDto; + } + set + { + _imageDto = value; + } + } + + [JsonProperty("se_name")] + public string SeName { get; set; } + } +} \ No newline at end of file diff --git a/Nop.Plugin.Api/DTOs/Manufacturers/ManufacturersCountRootObject.cs b/Nop.Plugin.Api/DTOs/Manufacturers/ManufacturersCountRootObject.cs new file mode 100644 index 0000000..311fc40 --- /dev/null +++ b/Nop.Plugin.Api/DTOs/Manufacturers/ManufacturersCountRootObject.cs @@ -0,0 +1,10 @@ +using Newtonsoft.Json; + +namespace Nop.Plugin.Api.DTOs.Manufacturers +{ + public class ManufacturersCountRootObject + { + [JsonProperty("count")] + public int Count { get; set; } + } +} \ No newline at end of file diff --git a/Nop.Plugin.Api/DTOs/Manufacturers/ManufacturersRootObject.cs b/Nop.Plugin.Api/DTOs/Manufacturers/ManufacturersRootObject.cs new file mode 100644 index 0000000..b264a77 --- /dev/null +++ b/Nop.Plugin.Api/DTOs/Manufacturers/ManufacturersRootObject.cs @@ -0,0 +1,27 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; + +namespace Nop.Plugin.Api.DTOs.Manufacturers +{ + public class ManufacturersRootObject : ISerializableObject + { + public ManufacturersRootObject() + { + Manufacturers = new List(); + } + + [JsonProperty("manufacturers")] + public IList Manufacturers { get; set; } + + public string GetPrimaryPropertyName() + { + return "manufacturers"; + } + + public Type GetPrimaryPropertyType() + { + return typeof(ManufacturerDto); + } + } +} \ No newline at end of file diff --git a/Nop.Plugin.Api/DTOs/NewsLetterSubscriptions/NewsLetterSubscriptionDto.cs b/Nop.Plugin.Api/DTOs/NewsLetterSubscriptions/NewsLetterSubscriptionDto.cs index 977408c..3edfcdc 100644 --- a/Nop.Plugin.Api/DTOs/NewsLetterSubscriptions/NewsLetterSubscriptionDto.cs +++ b/Nop.Plugin.Api/DTOs/NewsLetterSubscriptions/NewsLetterSubscriptionDto.cs @@ -1,41 +1,34 @@ -using System; -using FluentValidation.Attributes; -using Newtonsoft.Json; -using Nop.Plugin.Api.Validators; - -namespace Nop.Plugin.Api.DTOs.Categories -{ - [JsonObject(Title = "news_letter_subscription")] - public class NewsLetterSubscriptionDto - { - /// - /// Gets or sets the id - /// - [JsonProperty("id")] - public string Id { get; set; } - - /// - /// Gets or sets the email - /// - [JsonProperty("email")] - public string Email { get; set; } - - /// - /// Gets or sets whether the subscription is active - /// - [JsonProperty("active")] - public bool Active { get; set; } - - /// - /// Gets or sets whether the subscription is active - /// - [JsonProperty("store_id")] - public int StoreId { get; set; } - - /// - /// Gets or sets created on utc date - /// - [JsonProperty("created_on_utc")] - public DateTime? CreatedOnUtc { get; set; } - } +using System; +using Newtonsoft.Json; +using Nop.Plugin.Api.DTOs.Base; + +namespace Nop.Plugin.Api.DTOs.Categories +{ + [JsonObject(Title = "news_letter_subscription")] + public class NewsLetterSubscriptionDto : BaseDto + { + /// + /// Gets or sets the email + /// + [JsonProperty("email")] + public string Email { get; set; } + + /// + /// Gets or sets whether the subscription is active + /// + [JsonProperty("active")] + public bool Active { get; set; } + + /// + /// Gets or sets whether the subscription is active + /// + [JsonProperty("store_id")] + public int StoreId { get; set; } + + /// + /// Gets or sets created on utc date + /// + [JsonProperty("created_on_utc")] + public DateTime? CreatedOnUtc { get; set; } + } } \ No newline at end of file diff --git a/Nop.Plugin.Api/DTOs/OrderItems/OrderItemDto.cs b/Nop.Plugin.Api/DTOs/OrderItems/OrderItemDto.cs index 18fcb50..f6bc3d8 100644 --- a/Nop.Plugin.Api/DTOs/OrderItems/OrderItemDto.cs +++ b/Nop.Plugin.Api/DTOs/OrderItems/OrderItemDto.cs @@ -3,6 +3,7 @@ using FluentValidation.Attributes; using Newtonsoft.Json; using Nop.Plugin.Api.Attributes; +using Nop.Plugin.Api.DTOs.Base; using Nop.Plugin.Api.DTOs.Products; using Nop.Plugin.Api.Validators; @@ -10,21 +11,15 @@ namespace Nop.Plugin.Api.DTOs.OrderItems { [Validator(typeof(OrderItemDtoValidator))] [JsonObject(Title = "order_item")] - public class OrderItemDto + public class OrderItemDto : BaseDto { - private List _attributes; - - /// - /// Gets or sets the id - /// - [JsonProperty("id")] - public string Id { get; set; } + private ICollection _attributes; /// /// Gets or sets the selected attributes /// [JsonProperty("product_attributes")] - public List Attributes + public ICollection Attributes { get { diff --git a/Nop.Plugin.Api/DTOs/Orders/OrderDto.cs b/Nop.Plugin.Api/DTOs/Orders/OrderDto.cs index 5cd5e5b..317853a 100644 --- a/Nop.Plugin.Api/DTOs/Orders/OrderDto.cs +++ b/Nop.Plugin.Api/DTOs/Orders/OrderDto.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using FluentValidation.Attributes; using Newtonsoft.Json; +using Nop.Plugin.Api.DTOs.Base; using Nop.Plugin.Api.DTOs.Customers; using Nop.Plugin.Api.DTOs.OrderItems; using Nop.Plugin.Api.Validators; @@ -10,15 +11,9 @@ namespace Nop.Plugin.Api.DTOs.Orders { [JsonObject(Title = "order")] [Validator(typeof(OrderDtoValidator))] - public class OrderDto + public class OrderDto : BaseDto { - private ICollection _orderItemDtos; - - /// - /// Gets or sets a value indicating the order id - /// - [JsonProperty("id")] - public string Id { get; set; } + private ICollection _orderItems; [JsonProperty("store_id")] public int? StoreId { get; set; } @@ -264,10 +259,18 @@ public class OrderDto /// Gets or sets order items /// [JsonProperty("order_items")] - public ICollection OrderItemDtos + public ICollection OrderItems { - get { return _orderItemDtos; } - set { _orderItemDtos = value; } + get + { + if (_orderItems == null) + { + _orderItems = new List(); + } + + return _orderItems; + } + set { _orderItems = value; } } /// diff --git a/Nop.Plugin.Api/DTOs/ProductAttributes/ProductAttributeDto.cs b/Nop.Plugin.Api/DTOs/ProductAttributes/ProductAttributeDto.cs index 49fc0df..30202d9 100644 --- a/Nop.Plugin.Api/DTOs/ProductAttributes/ProductAttributeDto.cs +++ b/Nop.Plugin.Api/DTOs/ProductAttributes/ProductAttributeDto.cs @@ -1,19 +1,14 @@ using FluentValidation.Attributes; using Newtonsoft.Json; +using Nop.Plugin.Api.DTOs.Base; using Nop.Plugin.Api.Validators; namespace Nop.Plugin.Api.DTOs.ProductAttributes { [JsonObject(Title = "product_attribute")] [Validator(typeof(ProductAttributeDtoValidator))] - public class ProductAttributeDto + public class ProductAttributeDto : BaseDto { - /// - /// Gets or sets the product attribute id - /// - [JsonProperty("id")] - public string Id { get; set; } - /// /// Gets or sets the name /// diff --git a/Nop.Plugin.Api/DTOs/ProductCategoryMappings/ProductCategoryMappingsDto.cs b/Nop.Plugin.Api/DTOs/ProductCategoryMappings/ProductCategoryMappingsDto.cs index a05b1d4..b36aaeb 100644 --- a/Nop.Plugin.Api/DTOs/ProductCategoryMappings/ProductCategoryMappingsDto.cs +++ b/Nop.Plugin.Api/DTOs/ProductCategoryMappings/ProductCategoryMappingsDto.cs @@ -1,16 +1,14 @@ using FluentValidation.Attributes; using Newtonsoft.Json; +using Nop.Plugin.Api.DTOs.Base; using Nop.Plugin.Api.Validators; namespace Nop.Plugin.Api.DTOs.ProductCategoryMappings { [JsonObject(Title = "product_category_mapping")] [Validator(typeof(ProductCategoryMappingDtoValidator))] - public class ProductCategoryMappingDto + public class ProductCategoryMappingDto : BaseDto { - [JsonProperty("id")] - public int Id { get; set; } - /// /// Gets or sets the product identifier /// diff --git a/Nop.Plugin.Api/DTOs/ProductItemAttributeDto.cs b/Nop.Plugin.Api/DTOs/ProductItemAttributeDto.cs index de9cfa7..e6d5f36 100644 --- a/Nop.Plugin.Api/DTOs/ProductItemAttributeDto.cs +++ b/Nop.Plugin.Api/DTOs/ProductItemAttributeDto.cs @@ -1,13 +1,11 @@ using Newtonsoft.Json; +using Nop.Plugin.Api.DTOs.Base; namespace Nop.Plugin.Api.DTOs { [JsonObject(Title = "attribute")] - public class ProductItemAttributeDto + public class ProductItemAttributeDto : BaseDto { - [JsonProperty("id")] - public int Id { get; set; } - [JsonProperty("value")] public string Value { get; set; } } diff --git a/Nop.Plugin.Api/DTOs/ProductManufacturerMappings/ProductManufacturerMappingsCountRootObject.cs b/Nop.Plugin.Api/DTOs/ProductManufacturerMappings/ProductManufacturerMappingsCountRootObject.cs new file mode 100644 index 0000000..19bcf87 --- /dev/null +++ b/Nop.Plugin.Api/DTOs/ProductManufacturerMappings/ProductManufacturerMappingsCountRootObject.cs @@ -0,0 +1,10 @@ +using Newtonsoft.Json; + +namespace Nop.Plugin.Api.DTOs.ProductManufacturerMappings +{ + public class ProductManufacturerMappingsCountRootObject + { + [JsonProperty("count")] + public int Count { get; set; } + } +} \ No newline at end of file diff --git a/Nop.Plugin.Api/DTOs/ProductManufacturerMappings/ProductManufacturerMappingsDto.cs b/Nop.Plugin.Api/DTOs/ProductManufacturerMappings/ProductManufacturerMappingsDto.cs new file mode 100644 index 0000000..233664f --- /dev/null +++ b/Nop.Plugin.Api/DTOs/ProductManufacturerMappings/ProductManufacturerMappingsDto.cs @@ -0,0 +1,36 @@ +using FluentValidation.Attributes; +using Newtonsoft.Json; +using Nop.Plugin.Api.DTOs.Base; +using Nop.Plugin.Api.Validators; + +namespace Nop.Plugin.Api.DTOs.ProductManufacturerMappings +{ + [JsonObject(Title = "product_manufacturer_mapping")] + [Validator(typeof(ProductManufacturerMappingDtoValidator))] + public class ProductManufacturerMappingsDto : BaseDto + { + /// + /// Gets or sets the product identifier + /// + [JsonProperty("product_id")] + public int? ProductId { get; set; } + + /// + /// Gets or sets the manufacturer identifier + /// + [JsonProperty("manufacturer_id")] + public int? ManufacturerId { get; set; } + + /// + /// Gets or sets a value indicating whether the product is featured + /// + [JsonProperty("is_featured_product")] + public bool? IsFeaturedProduct { get; set; } + + /// + /// Gets or sets the display order + /// + [JsonProperty("display_order")] + public int? DisplayOrder { get; set; } + } +} \ No newline at end of file diff --git a/Nop.Plugin.Api/DTOs/ProductManufacturerMappings/ProductManufacturerMappingsRootObject.cs b/Nop.Plugin.Api/DTOs/ProductManufacturerMappings/ProductManufacturerMappingsRootObject.cs new file mode 100644 index 0000000..6b5ac59 --- /dev/null +++ b/Nop.Plugin.Api/DTOs/ProductManufacturerMappings/ProductManufacturerMappingsRootObject.cs @@ -0,0 +1,27 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; + +namespace Nop.Plugin.Api.DTOs.ProductManufacturerMappings +{ + public class ProductManufacturerMappingsRootObject : ISerializableObject + { + public ProductManufacturerMappingsRootObject() + { + ProductManufacturerMappingsDtos = new List(); + } + + [JsonProperty("product_manufacturer_mappings")] + public IList ProductManufacturerMappingsDtos { get; set; } + + public string GetPrimaryPropertyName() + { + return "product_manufacturer_mappings"; + } + + public Type GetPrimaryPropertyType() + { + return typeof (ProductManufacturerMappingsDto); + } + } +} \ No newline at end of file diff --git a/Nop.Plugin.Api/DTOs/Products/ProductAttributeCombinationDto.cs b/Nop.Plugin.Api/DTOs/Products/ProductAttributeCombinationDto.cs new file mode 100644 index 0000000..810db7d --- /dev/null +++ b/Nop.Plugin.Api/DTOs/Products/ProductAttributeCombinationDto.cs @@ -0,0 +1,60 @@ +using FluentValidation.Attributes; +using Newtonsoft.Json; +using Nop.Plugin.Api.DTOs.Base; +using Nop.Plugin.Api.Validators; + +namespace Nop.Plugin.Api.DTOs.Products +{ + [JsonObject(Title = "product_attribute_combination")] + [Validator(typeof(ProductAttributeCombinationDtoValidator))] + public class ProductAttributeCombinationDto : BaseDto + { + /// + /// Gets or sets the product identifier + /// + [JsonProperty("product_id")] + public int ProductId { get; set; } + + /// + /// Gets or sets the attributes + /// + [JsonProperty("attributes_xml")] + public string AttributesXml { get; set; } + + /// + /// Gets or sets the stock quantity + /// + [JsonProperty("stock_quantity")] + public int StockQuantity { get; set; } + + /// + /// Gets or sets the SKU + /// + [JsonProperty("sku")] + public string Sku { get; set; } + + /// + /// Gets or sets the manufacturer part number + /// + [JsonProperty("manufacturer_part_number")] + public string ManufacturerPartNumber { get; set; } + + /// + /// Gets or sets the Global Trade Item Number (GTIN). These identifiers include UPC (in North America), EAN (in Europe), JAN (in Japan), and ISBN (for books). + /// + [JsonProperty("gtin")] + public string Gtin { get; set; } + + /// + /// Gets or sets the attribute combination price. This way a store owner can override the default product price when this attribute combination is added to the cart. For example, you can give a discount this way. + /// + [JsonProperty("overridden_price")] + public decimal? OverriddenPrice { get; set; } + + /// + /// Gets or sets the identifier of picture associated with this combination + /// + [JsonProperty("picture_id")] + public int PictureId { get; set; } + } +} \ No newline at end of file diff --git a/Nop.Plugin.Api/DTOs/Products/ProductAttributeMappingDto.cs b/Nop.Plugin.Api/DTOs/Products/ProductAttributeMappingDto.cs index fe30f74..48f9db3 100644 --- a/Nop.Plugin.Api/DTOs/Products/ProductAttributeMappingDto.cs +++ b/Nop.Plugin.Api/DTOs/Products/ProductAttributeMappingDto.cs @@ -2,21 +2,16 @@ using System.Collections.Generic; using Newtonsoft.Json; using Nop.Core.Domain.Catalog; +using Nop.Plugin.Api.DTOs.Base; namespace Nop.Plugin.Api.DTOs.Products { [JsonObject(Title = "attribute")] //[Validator(typeof(ProductDtoValidator))] - public class ProductAttributeMappingDto + public class ProductAttributeMappingDto : BaseDto { private List _productAttributeValues; - /// - /// Gets or sets the product attribute identifier - /// - [JsonProperty("id")] - public int Id { get; set; } - /// /// Gets or sets the product attribute identifier /// diff --git a/Nop.Plugin.Api/DTOs/Products/ProductAttributeValueDto.cs b/Nop.Plugin.Api/DTOs/Products/ProductAttributeValueDto.cs index 55eb610..b8b0971 100644 --- a/Nop.Plugin.Api/DTOs/Products/ProductAttributeValueDto.cs +++ b/Nop.Plugin.Api/DTOs/Products/ProductAttributeValueDto.cs @@ -1,20 +1,15 @@ using System; using Newtonsoft.Json; using Nop.Core.Domain.Catalog; +using Nop.Plugin.Api.DTOs.Base; using Nop.Plugin.Api.DTOs.Images; namespace Nop.Plugin.Api.DTOs.Products { [JsonObject(Title = "attribute_value")] //[Validator(typeof(ProductDtoValidator))] - public class ProductAttributeValueDto + public class ProductAttributeValueDto : BaseDto { - /// - /// Gets or sets the product attribute value id - /// - [JsonProperty("id")] - public int Id { get; set; } - /// /// Gets or sets the attribute value type identifier /// diff --git a/Nop.Plugin.Api/DTOs/Products/ProductDto.cs b/Nop.Plugin.Api/DTOs/Products/ProductDto.cs index 451fcd8..406d469 100644 --- a/Nop.Plugin.Api/DTOs/Products/ProductDto.cs +++ b/Nop.Plugin.Api/DTOs/Products/ProductDto.cs @@ -1,644 +1,654 @@ -using System; -using System.Collections.Generic; -using FluentValidation.Attributes; -using Newtonsoft.Json; -using Nop.Core.Domain.Catalog; -using Nop.Plugin.Api.Attributes; -using Nop.Plugin.Api.DTOs.Images; -using Nop.Plugin.Api.DTOs.Languages; -using Nop.Plugin.Api.DTOs.SpecificationAttributes; -using Nop.Plugin.Api.Validators; - -namespace Nop.Plugin.Api.DTOs.Products -{ - [JsonObject(Title = "product")] - [Validator(typeof(ProductDtoValidator))] - public class ProductDto - { - private int? _productTypeId; - private List _storeIds; - private List _discountIds; - private List _roleIds; - private List _manufacturerIds; - private List _localizedNames; - private List _images; - private List _productAttributeMappings; - private List _productSpecificationAttributes; - private List _associatedProductIds; - private List _tags; - - /// - /// Gets or sets the product id - /// - [JsonProperty("id")] - public string Id { get; set; } - - /// - /// Gets or sets the values indicating whether this product is visible in catalog or search results. - /// It's used when this product is associated to some "grouped" one - /// This way associated products could be accessed/added/etc only from a grouped product details page - /// - [JsonProperty("visible_individually")] - public bool? VisibleIndividually { get; set; } - - /// - /// Gets or sets the name - /// - [JsonProperty("name")] - public string Name { get; set; } - - /// - /// Gets or sets the localized names - /// - [JsonProperty("localized_names")] - public List LocalizedNames - { - get - { - return _localizedNames; - } - set - { - _localizedNames = value; - } - } - - /// - /// Gets or sets the short description - /// - [JsonProperty("short_description")] - public string ShortDescription { get; set; } - /// - /// Gets or sets the full description - /// - [JsonProperty("full_description")] - public string FullDescription { get; set; } - - /// - /// Gets or sets a value indicating whether to show the product on home page - /// - [JsonProperty("show_on_home_page")] - public bool? ShowOnHomePage { get; set; } - - /// - /// Gets or sets the meta keywords - /// - [JsonProperty("meta_keywords")] - public string MetaKeywords { get; set; } - /// - /// Gets or sets the meta description - /// - [JsonProperty("meta_description")] - public string MetaDescription { get; set; } - /// - /// Gets or sets the meta title - /// - [JsonProperty("meta_title")] - public string MetaTitle { get; set; } - - /// - /// Gets or sets a value indicating whether the product allows customer reviews - /// - [JsonProperty("allow_customer_reviews")] - public bool? AllowCustomerReviews { get; set; } - /// - /// Gets or sets the rating sum (approved reviews) - /// - [JsonProperty("approved_rating_sum")] - public int? ApprovedRatingSum { get; set; } - /// - /// Gets or sets the rating sum (not approved reviews) - /// - [JsonProperty("not_approved_rating_sum")] - public int? NotApprovedRatingSum { get; set; } - /// - /// Gets or sets the total rating votes (approved reviews) - /// - [JsonProperty("approved_total_reviews")] - public int? ApprovedTotalReviews { get; set; } - /// - /// Gets or sets the total rating votes (not approved reviews) - /// - [JsonProperty("not_approved_total_reviews")] - public int? NotApprovedTotalReviews { get; set; } - - /// - /// Gets or sets the SKU - /// - [JsonProperty("sku")] - public string Sku { get; set; } - /// - /// Gets or sets the manufacturer part number - /// - [JsonProperty("manufacturer_part_number")] - public string ManufacturerPartNumber { get; set; } - /// - /// Gets or sets the Global Trade Item Number (GTIN). These identifiers include UPC (in North America), EAN (in Europe), JAN (in Japan), and ISBN (for books). - /// - [JsonProperty("gtin")] - public string Gtin { get; set; } - - /// - /// Gets or sets a value indicating whether the product is gift card - /// - [JsonProperty("is_gift_card")] - public bool? IsGiftCard { get; set; } - - /// - /// Gets or sets a value indicating whether the product requires that other products are added to the cart (Product X requires Product Y) - /// - [JsonProperty("require_other_products")] - public bool? RequireOtherProducts { get; set; } - - /// - /// Gets or sets a value indicating whether required products are automatically added to the cart - /// - [JsonProperty("automatically_add_required_products")] - public bool? AutomaticallyAddRequiredProducts { get; set; } - - /// - /// Gets or sets a value indicating whether the product is download - /// - [JsonProperty("is_download")] - public bool? IsDownload { get; set; } - - /// - /// Gets or sets a value indicating whether this downloadable product can be downloaded unlimited number of times - /// - [JsonProperty("unlimited_downloads")] - public bool? UnlimitedDownloads { get; set; } - /// - /// Gets or sets the maximum number of downloads - /// - [JsonProperty("max_number_of_downloads")] - public int? MaxNumberOfDownloads { get; set; } - /// - /// Gets or sets the number of days during customers keeps access to the file. - /// - [JsonProperty("download_expiration_days")] - public int? DownloadExpirationDays { get; set; } - - /// - /// Gets or sets a value indicating whether the product has a sample download file - /// - [JsonProperty("has_sample_download")] - public bool? HasSampleDownload { get; set; } - - /// - /// Gets or sets a value indicating whether the product has user agreement - /// - [JsonProperty("has_user_agreement")] - public bool? HasUserAgreement { get; set; } - - /// - /// Gets or sets a value indicating whether the product is recurring - /// - [JsonProperty("is_recurring")] - public bool? IsRecurring { get; set; } - /// - /// Gets or sets the cycle length - /// - [JsonProperty("recurring_cycle_length")] - public int? RecurringCycleLength { get; set; } - - /// - /// Gets or sets the total cycles - /// - [JsonProperty("recurring_total_cycles")] - public int? RecurringTotalCycles { get; set; } - - /// - /// Gets or sets a value indicating whether the product is rental - /// - [JsonProperty("is_rental")] - public bool? IsRental { get; set; } - /// - /// Gets or sets the rental length for some period (price for this period) - /// - [JsonProperty("rental_price_length")] - public int? RentalPriceLength { get; set; } - - /// - /// Gets or sets a value indicating whether the entity is ship enabled - /// - [JsonProperty("is_ship_enabled")] - public bool? IsShipEnabled { get; set; } - /// - /// Gets or sets a value indicating whether the entity is free shipping - /// - [JsonProperty("is_free_shipping")] - public bool? IsFreeShipping { get; set; } - /// - /// Gets or sets a value this product should be shipped separately (each item) - /// - [JsonProperty("ship_separately")] - public bool? ShipSeparately { get; set; } - /// - /// Gets or sets the additional shipping charge - /// - [JsonProperty("additional_shipping_charge")] - public decimal? AdditionalShippingCharge { get; set; } - - /// - /// Gets or sets a value indicating whether the product is marked as tax exempt - /// - [JsonProperty("is_tax_exempt")] - public bool? IsTaxExempt { get; set; } - - /// - /// Gets or sets a value indicating whether the product is telecommunications or broadcasting or electronic services - /// - [JsonProperty("is_telecommunications_or_broadcasting_or_electronic_services")] - public bool? IsTelecommunicationsOrBroadcastingOrElectronicServices { get; set; } - - /// - /// Gets or sets a value indicating whether multiple warehouses are used for this product - /// - [JsonProperty("use_multiple_warehouses")] - public bool? UseMultipleWarehouses { get; set; } - - /// - /// Gets or sets a value indicating how to manage inventory. - /// 0 - do not track inventory - /// 1 - track inventory - /// 2 - track invetory by attributes - /// - [JsonProperty("manage_inventory_method_id")] - public int? ManageInventoryMethodId { get; set; } - - /// - /// Gets or sets the stock quantity - /// - [JsonProperty("stock_quantity")] - public int? StockQuantity { get; set; } - /// - /// Gets or sets a value indicating whether to display stock availability - /// - [JsonProperty("display_stock_availability")] - public bool? DisplayStockAvailability { get; set; } - /// - /// Gets or sets a value indicating whether to display stock quantity - /// - [JsonProperty("display_stock_quantity")] - public bool? DisplayStockQuantity { get; set; } - /// - /// Gets or sets the minimum stock quantity - /// - [JsonProperty("min_stock_quantity")] - public int? MinStockQuantity { get; set; } - - /// - /// Gets or sets the quantity when admin should be notified - /// - [JsonProperty("notify_admin_for_quantity_below")] - public int? NotifyAdminForQuantityBelow { get; set; } - - /// - /// Gets or sets a value indicating whether to back in stock subscriptions are allowed - /// - [JsonProperty("allow_back_in_stock_subscriptions")] - public bool? AllowBackInStockSubscriptions { get; set; } - /// - /// Gets or sets the order minimum quantity - /// - [JsonProperty("order_minimum_quantity")] - public int? OrderMinimumQuantity { get; set; } - /// - /// Gets or sets the order maximum quantity - /// - [JsonProperty("order_maximum_quantity")] - public int? OrderMaximumQuantity { get; set; } - /// - /// Gets or sets the comma seperated list of allowed quantities. null or empty if any quantity is allowed - /// - [JsonProperty("allowed_quantities")] - public string AllowedQuantities { get; set; } - /// - /// Gets or sets a value indicating whether we allow adding to the cart/wishlist only attribute combinations that exist and have stock greater than zero. - /// This option is used only when we have "manage inventory" set to "track inventory by product attributes" - /// - [JsonProperty("allow_adding_only_existing_attribute_combinations")] - public bool? AllowAddingOnlyExistingAttributeCombinations { get; set; } - - /// - /// Gets or sets a value indicating whether to disable buy (Add to cart) button - /// - [JsonProperty("disable_buy_button")] - public bool? DisableBuyButton { get; set; } - /// - /// Gets or sets a value indicating whether to disable "Add to wishlist" button - /// - [JsonProperty("disable_wishlist_button")] - public bool? DisableWishlistButton { get; set; } - /// - /// Gets or sets a value indicating whether this item is available for Pre-Order - /// - [JsonProperty("available_for_pre_order")] - public bool? AvailableForPreOrder { get; set; } - /// - /// Gets or sets the start date and time of the product availability (for pre-order products) - /// - [JsonProperty("pre_order_availability_start_date_time_utc")] - public DateTime? PreOrderAvailabilityStartDateTimeUtc { get; set; } - /// - /// Gets or sets a value indicating whether to show "Call for Pricing" or "Call for quote" instead of price - /// - [JsonProperty("call_for_price")] - public bool? CallForPrice { get; set; } - /// - /// Gets or sets the price - /// - [JsonProperty("price")] - public decimal? Price { get; set; } - /// - /// Gets or sets the old price - /// - [JsonProperty("old_price")] - public decimal? OldPrice { get; set; } - /// - /// Gets or sets the product cost - /// - [JsonProperty("product_cost")] - public decimal? ProductCost { get; set; } - /// - /// Gets or sets the product special price - /// - [JsonProperty("special_price")] - public decimal? SpecialPrice { get; set; } - /// - /// Gets or sets the start date and time of the special price - /// - [JsonProperty("special_price_start_date_time_utc")] - public DateTime? SpecialPriceStartDateTimeUtc { get; set; } - /// - /// Gets or sets the end date and time of the special price - /// - [JsonProperty("special_price_end_date_time_utc")] - public DateTime? SpecialPriceEndDateTimeUtc { get; set; } - /// - /// Gets or sets a value indicating whether a customer enters price - /// - [JsonProperty("customer_enters_price")] - public bool? CustomerEntersPrice { get; set; } - /// - /// Gets or sets the minimum price entered by a customer - /// - [JsonProperty("minimum_customer_entered_price")] - public decimal? MinimumCustomerEnteredPrice { get; set; } - /// - /// Gets or sets the maximum price entered by a customer - /// - [JsonProperty("maximum_customer_entered_price")] - public decimal? MaximumCustomerEnteredPrice { get; set; } - - /// - /// Gets or sets a value indicating whether base price (PAngV) is enabled. Used by German users. - /// - [JsonProperty("baseprice_enabled")] - public bool? BasepriceEnabled { get; set; } - /// - /// Gets or sets an amount in product for PAngV - /// - [JsonProperty("baseprice_amount")] - public decimal? BasepriceAmount { get; set; } - - /// - /// Gets or sets a reference amount for PAngV - /// - [JsonProperty("baseprice_base_amount")] - public decimal? BasepriceBaseAmount { get; set; } - - /// - /// Gets or sets a value indicating whether this product has tier prices configured - /// The same as if we run this.TierPrices.Count > 0 - /// We use this property for performance optimization: - /// if this property is set to false, then we do not need to load tier prices navigation property - /// - /// - [JsonProperty("has_tier_prices")] - public bool? HasTierPrices { get; set; } - /// - /// Gets or sets a value indicating whether this product has discounts applied - /// The same as if we run this.AppliedDiscounts.Count > 0 - /// We use this property for performance optimization: - /// if this property is set to false, then we do not need to load Applied Discounts navigation property - /// - /// - [JsonProperty("has_discounts_applied")] - public bool? HasDiscountsApplied { get; set; } - - /// - /// Gets or sets the weight - /// - [JsonProperty("weight")] - public decimal? Weight { get; set; } - /// - /// Gets or sets the length - /// - [JsonProperty("length")] - public decimal? Length { get; set; } - /// - /// Gets or sets the width - /// - [JsonProperty("width")] - public decimal? Width { get; set; } - /// - /// Gets or sets the height - /// - [JsonProperty("height")] - public decimal? Height { get; set; } - - /// - /// Gets or sets the available start date and time - /// - [JsonProperty("available_start_date_time_utc")] - public DateTime? AvailableStartDateTimeUtc { get; set; } - /// - /// Gets or sets the available end date and time - /// - [JsonProperty("available_end_date_time_utc")] - public DateTime? AvailableEndDateTimeUtc { get; set; } - - /// - /// Gets or sets a display order. - /// This value is used when sorting associated products (used with "grouped" products) - /// This value is used when sorting home page products - /// - [JsonProperty("display_order")] - public int? DisplayOrder { get; set; } - /// - /// Gets or sets a value indicating whether the entity is published - /// - [JsonProperty("published")] - public bool? Published { get; set; } - /// - /// Gets or sets a value indicating whether the entity has been deleted - /// - [JsonProperty("deleted")] - public bool? Deleted { get; set; } - - /// - /// Gets or sets the date and time of product creation - /// - [JsonProperty("created_on_utc")] - public DateTime? CreatedOnUtc { get; set; } - /// - /// Gets or sets the date and time of product update - /// - [JsonProperty("updated_on_utc")] - public DateTime? UpdatedOnUtc { get; set; } - - /// - /// Gets or sets the product type - /// - [ProductTypeValidationAttribute] - [JsonProperty("product_type")] - public string ProductType - { - get - { - var productTypeId = this._productTypeId; - if (productTypeId != null) return ((ProductType)productTypeId).ToString(); - - return null; - } - set - { - ProductType productTypeId; - if (Enum.TryParse(value, out productTypeId)) - { - this._productTypeId = (int)productTypeId; - } - else this._productTypeId = null; - } - } - - [JsonProperty("parent_grouped_product_id")] - public int? ParentGroupedProductId { get; set; } - - [JsonProperty("role_ids")] - public List RoleIds - { - get - { - return _roleIds; - } - set - { - _roleIds = value; - } - } - - [JsonProperty("discount_ids")] - public List DiscountIds - { - get - { - return _discountIds; - } - set - { - _discountIds = value; - } - } - - [JsonProperty("store_ids")] - public List StoreIds - { - get - { - return _storeIds; - } - set - { - _storeIds = value; - } - } - - [JsonProperty("manufacturer_ids")] - public List ManufacturerIds - { - get - { - return _manufacturerIds; - } - set - { - _manufacturerIds = value; - } - } - - [ImageCollectionValidation] - [JsonProperty("images")] - public List Images - { - get - { - return _images; - } - set - { - _images = value; - } - } - - [JsonProperty("attributes")] - public List ProductAttributeMappings - { - get - { - return _productAttributeMappings; - } - set - { - _productAttributeMappings = value; - } - } - - [JsonProperty("product_specification_attributes")] - public List ProductSpecificationAttributes - { - get - { - return _productSpecificationAttributes; - } - set - { - _productSpecificationAttributes = value; - } - } - - [JsonProperty("associated_product_ids")] - public List AssociatedProductIds - { - get - { - return _associatedProductIds; - } - set - { - _associatedProductIds = value; - } - } - - [JsonProperty("tags")] - public List Tags - { - get - { - return _tags; - } - set - { - _tags = value; - } - } - - [ValidateVendor] - [JsonProperty("vendor_id")] - public int? VendorId { get; set; } - - [JsonProperty("se_name")] - public string SeName { get; set; } - } +using System; +using System.Collections.Generic; +using FluentValidation.Attributes; +using Newtonsoft.Json; +using Nop.Core.Domain.Catalog; +using Nop.Plugin.Api.Attributes; +using Nop.Plugin.Api.DTOs.Base; +using Nop.Plugin.Api.DTOs.Images; +using Nop.Plugin.Api.DTOs.Languages; +using Nop.Plugin.Api.DTOs.SpecificationAttributes; +using Nop.Plugin.Api.Validators; + +namespace Nop.Plugin.Api.DTOs.Products +{ + [JsonObject(Title = "product")] + [Validator(typeof(ProductDtoValidator))] + public class ProductDto : BaseDto + { + private int? _productTypeId; + private List _storeIds; + private List _discountIds; + private List _roleIds; + private List _manufacturerIds; + private List _localizedNames; + private List _images; + private List _productAttributeMappings; + private List _productAttributeCombinations; + private List _productSpecificationAttributes; + private List _associatedProductIds; + private List _tags; + + /// + /// Gets or sets the values indicating whether this product is visible in catalog or search results. + /// It's used when this product is associated to some "grouped" one + /// This way associated products could be accessed/added/etc only from a grouped product details page + /// + [JsonProperty("visible_individually")] + public bool? VisibleIndividually { get; set; } + + /// + /// Gets or sets the name + /// + [JsonProperty("name")] + public string Name { get; set; } + + /// + /// Gets or sets the localized names + /// + [JsonProperty("localized_names")] + public List LocalizedNames + { + get + { + return _localizedNames; + } + set + { + _localizedNames = value; + } + } + + /// + /// Gets or sets the short description + /// + [JsonProperty("short_description")] + public string ShortDescription { get; set; } + /// + /// Gets or sets the full description + /// + [JsonProperty("full_description")] + public string FullDescription { get; set; } + + /// + /// Gets or sets a value indicating whether to show the product on home page + /// + [JsonProperty("show_on_home_page")] + public bool? ShowOnHomePage { get; set; } + + /// + /// Gets or sets the meta keywords + /// + [JsonProperty("meta_keywords")] + public string MetaKeywords { get; set; } + /// + /// Gets or sets the meta description + /// + [JsonProperty("meta_description")] + public string MetaDescription { get; set; } + /// + /// Gets or sets the meta title + /// + [JsonProperty("meta_title")] + public string MetaTitle { get; set; } + + /// + /// Gets or sets a value indicating whether the product allows customer reviews + /// + [JsonProperty("allow_customer_reviews")] + public bool? AllowCustomerReviews { get; set; } + /// + /// Gets or sets the rating sum (approved reviews) + /// + [JsonProperty("approved_rating_sum")] + public int? ApprovedRatingSum { get; set; } + /// + /// Gets or sets the rating sum (not approved reviews) + /// + [JsonProperty("not_approved_rating_sum")] + public int? NotApprovedRatingSum { get; set; } + /// + /// Gets or sets the total rating votes (approved reviews) + /// + [JsonProperty("approved_total_reviews")] + public int? ApprovedTotalReviews { get; set; } + /// + /// Gets or sets the total rating votes (not approved reviews) + /// + [JsonProperty("not_approved_total_reviews")] + public int? NotApprovedTotalReviews { get; set; } + + /// + /// Gets or sets the SKU + /// + [JsonProperty("sku")] + public string Sku { get; set; } + /// + /// Gets or sets the manufacturer part number + /// + [JsonProperty("manufacturer_part_number")] + public string ManufacturerPartNumber { get; set; } + /// + /// Gets or sets the Global Trade Item Number (GTIN). These identifiers include UPC (in North America), EAN (in Europe), JAN (in Japan), and ISBN (for books). + /// + [JsonProperty("gtin")] + public string Gtin { get; set; } + + /// + /// Gets or sets a value indicating whether the product is gift card + /// + [JsonProperty("is_gift_card")] + public bool? IsGiftCard { get; set; } + + /// + /// Gets or sets a value indicating whether the product requires that other products are added to the cart (Product X requires Product Y) + /// + [JsonProperty("require_other_products")] + public bool? RequireOtherProducts { get; set; } + + /// + /// Gets or sets a value indicating whether required products are automatically added to the cart + /// + [JsonProperty("automatically_add_required_products")] + public bool? AutomaticallyAddRequiredProducts { get; set; } + + /// + /// Gets or sets a value indicating whether the product is download + /// + [JsonProperty("is_download")] + public bool? IsDownload { get; set; } + + /// + /// Gets or sets a value indicating whether this downloadable product can be downloaded unlimited number of times + /// + [JsonProperty("unlimited_downloads")] + public bool? UnlimitedDownloads { get; set; } + /// + /// Gets or sets the maximum number of downloads + /// + [JsonProperty("max_number_of_downloads")] + public int? MaxNumberOfDownloads { get; set; } + /// + /// Gets or sets the number of days during customers keeps access to the file. + /// + [JsonProperty("download_expiration_days")] + public int? DownloadExpirationDays { get; set; } + + /// + /// Gets or sets a value indicating whether the product has a sample download file + /// + [JsonProperty("has_sample_download")] + public bool? HasSampleDownload { get; set; } + + /// + /// Gets or sets a value indicating whether the product has user agreement + /// + [JsonProperty("has_user_agreement")] + public bool? HasUserAgreement { get; set; } + + /// + /// Gets or sets a value indicating whether the product is recurring + /// + [JsonProperty("is_recurring")] + public bool? IsRecurring { get; set; } + /// + /// Gets or sets the cycle length + /// + [JsonProperty("recurring_cycle_length")] + public int? RecurringCycleLength { get; set; } + + /// + /// Gets or sets the total cycles + /// + [JsonProperty("recurring_total_cycles")] + public int? RecurringTotalCycles { get; set; } + + /// + /// Gets or sets a value indicating whether the product is rental + /// + [JsonProperty("is_rental")] + public bool? IsRental { get; set; } + /// + /// Gets or sets the rental length for some period (price for this period) + /// + [JsonProperty("rental_price_length")] + public int? RentalPriceLength { get; set; } + + /// + /// Gets or sets a value indicating whether the entity is ship enabled + /// + [JsonProperty("is_ship_enabled")] + public bool? IsShipEnabled { get; set; } + /// + /// Gets or sets a value indicating whether the entity is free shipping + /// + [JsonProperty("is_free_shipping")] + public bool? IsFreeShipping { get; set; } + /// + /// Gets or sets a value this product should be shipped separately (each item) + /// + [JsonProperty("ship_separately")] + public bool? ShipSeparately { get; set; } + /// + /// Gets or sets the additional shipping charge + /// + [JsonProperty("additional_shipping_charge")] + public decimal? AdditionalShippingCharge { get; set; } + + /// + /// Gets or sets a value indicating whether the product is marked as tax exempt + /// + [JsonProperty("is_tax_exempt")] + public bool? IsTaxExempt { get; set; } + + /// + /// Gets or sets a value indicating whether the product is telecommunications or broadcasting or electronic services + /// + [JsonProperty("is_telecommunications_or_broadcasting_or_electronic_services")] + public bool? IsTelecommunicationsOrBroadcastingOrElectronicServices { get; set; } + + /// + /// Gets or sets a value indicating whether multiple warehouses are used for this product + /// + [JsonProperty("use_multiple_warehouses")] + public bool? UseMultipleWarehouses { get; set; } + + /// + /// Gets or sets a value indicating how to manage inventory. + /// 0 - do not track inventory + /// 1 - track inventory + /// 2 - track invetory by attributes + /// + [JsonProperty("manage_inventory_method_id")] + public int? ManageInventoryMethodId { get; set; } + + /// + /// Gets or sets the stock quantity + /// + [JsonProperty("stock_quantity")] + public int? StockQuantity { get; set; } + /// + /// Gets or sets a value indicating whether to display stock availability + /// + [JsonProperty("display_stock_availability")] + public bool? DisplayStockAvailability { get; set; } + /// + /// Gets or sets a value indicating whether to display stock quantity + /// + [JsonProperty("display_stock_quantity")] + public bool? DisplayStockQuantity { get; set; } + /// + /// Gets or sets the minimum stock quantity + /// + [JsonProperty("min_stock_quantity")] + public int? MinStockQuantity { get; set; } + + /// + /// Gets or sets the quantity when admin should be notified + /// + [JsonProperty("notify_admin_for_quantity_below")] + public int? NotifyAdminForQuantityBelow { get; set; } + + /// + /// Gets or sets a value indicating whether to back in stock subscriptions are allowed + /// + [JsonProperty("allow_back_in_stock_subscriptions")] + public bool? AllowBackInStockSubscriptions { get; set; } + /// + /// Gets or sets the order minimum quantity + /// + [JsonProperty("order_minimum_quantity")] + public int? OrderMinimumQuantity { get; set; } + /// + /// Gets or sets the order maximum quantity + /// + [JsonProperty("order_maximum_quantity")] + public int? OrderMaximumQuantity { get; set; } + /// + /// Gets or sets the comma seperated list of allowed quantities. null or empty if any quantity is allowed + /// + [JsonProperty("allowed_quantities")] + public string AllowedQuantities { get; set; } + /// + /// Gets or sets a value indicating whether we allow adding to the cart/wishlist only attribute combinations that exist and have stock greater than zero. + /// This option is used only when we have "manage inventory" set to "track inventory by product attributes" + /// + [JsonProperty("allow_adding_only_existing_attribute_combinations")] + public bool? AllowAddingOnlyExistingAttributeCombinations { get; set; } + + /// + /// Gets or sets a value indicating whether to disable buy (Add to cart) button + /// + [JsonProperty("disable_buy_button")] + public bool? DisableBuyButton { get; set; } + /// + /// Gets or sets a value indicating whether to disable "Add to wishlist" button + /// + [JsonProperty("disable_wishlist_button")] + public bool? DisableWishlistButton { get; set; } + /// + /// Gets or sets a value indicating whether this item is available for Pre-Order + /// + [JsonProperty("available_for_pre_order")] + public bool? AvailableForPreOrder { get; set; } + /// + /// Gets or sets the start date and time of the product availability (for pre-order products) + /// + [JsonProperty("pre_order_availability_start_date_time_utc")] + public DateTime? PreOrderAvailabilityStartDateTimeUtc { get; set; } + /// + /// Gets or sets a value indicating whether to show "Call for Pricing" or "Call for quote" instead of price + /// + [JsonProperty("call_for_price")] + public bool? CallForPrice { get; set; } + /// + /// Gets or sets the price + /// + [JsonProperty("price")] + public decimal? Price { get; set; } + /// + /// Gets or sets the old price + /// + [JsonProperty("old_price")] + public decimal? OldPrice { get; set; } + /// + /// Gets or sets the product cost + /// + [JsonProperty("product_cost")] + public decimal? ProductCost { get; set; } + /// + /// Gets or sets the product special price + /// + [JsonProperty("special_price")] + public decimal? SpecialPrice { get; set; } + /// + /// Gets or sets the start date and time of the special price + /// + [JsonProperty("special_price_start_date_time_utc")] + public DateTime? SpecialPriceStartDateTimeUtc { get; set; } + /// + /// Gets or sets the end date and time of the special price + /// + [JsonProperty("special_price_end_date_time_utc")] + public DateTime? SpecialPriceEndDateTimeUtc { get; set; } + /// + /// Gets or sets a value indicating whether a customer enters price + /// + [JsonProperty("customer_enters_price")] + public bool? CustomerEntersPrice { get; set; } + /// + /// Gets or sets the minimum price entered by a customer + /// + [JsonProperty("minimum_customer_entered_price")] + public decimal? MinimumCustomerEnteredPrice { get; set; } + /// + /// Gets or sets the maximum price entered by a customer + /// + [JsonProperty("maximum_customer_entered_price")] + public decimal? MaximumCustomerEnteredPrice { get; set; } + + /// + /// Gets or sets a value indicating whether base price (PAngV) is enabled. Used by German users. + /// + [JsonProperty("baseprice_enabled")] + public bool? BasepriceEnabled { get; set; } + /// + /// Gets or sets an amount in product for PAngV + /// + [JsonProperty("baseprice_amount")] + public decimal? BasepriceAmount { get; set; } + + /// + /// Gets or sets a reference amount for PAngV + /// + [JsonProperty("baseprice_base_amount")] + public decimal? BasepriceBaseAmount { get; set; } + + /// + /// Gets or sets a value indicating whether this product has tier prices configured + /// The same as if we run this.TierPrices.Count > 0 + /// We use this property for performance optimization: + /// if this property is set to false, then we do not need to load tier prices navigation property + /// + /// + [JsonProperty("has_tier_prices")] + public bool? HasTierPrices { get; set; } + /// + /// Gets or sets a value indicating whether this product has discounts applied + /// The same as if we run this.AppliedDiscounts.Count > 0 + /// We use this property for performance optimization: + /// if this property is set to false, then we do not need to load Applied Discounts navigation property + /// + /// + [JsonProperty("has_discounts_applied")] + public bool? HasDiscountsApplied { get; set; } + + /// + /// Gets or sets the weight + /// + [JsonProperty("weight")] + public decimal? Weight { get; set; } + /// + /// Gets or sets the length + /// + [JsonProperty("length")] + public decimal? Length { get; set; } + /// + /// Gets or sets the width + /// + [JsonProperty("width")] + public decimal? Width { get; set; } + /// + /// Gets or sets the height + /// + [JsonProperty("height")] + public decimal? Height { get; set; } + + /// + /// Gets or sets the available start date and time + /// + [JsonProperty("available_start_date_time_utc")] + public DateTime? AvailableStartDateTimeUtc { get; set; } + /// + /// Gets or sets the available end date and time + /// + [JsonProperty("available_end_date_time_utc")] + public DateTime? AvailableEndDateTimeUtc { get; set; } + + /// + /// Gets or sets a display order. + /// This value is used when sorting associated products (used with "grouped" products) + /// This value is used when sorting home page products + /// + [JsonProperty("display_order")] + public int? DisplayOrder { get; set; } + /// + /// Gets or sets a value indicating whether the entity is published + /// + [JsonProperty("published")] + public bool? Published { get; set; } + /// + /// Gets or sets a value indicating whether the entity has been deleted + /// + [JsonProperty("deleted")] + public bool? Deleted { get; set; } + + /// + /// Gets or sets the date and time of product creation + /// + [JsonProperty("created_on_utc")] + public DateTime? CreatedOnUtc { get; set; } + /// + /// Gets or sets the date and time of product update + /// + [JsonProperty("updated_on_utc")] + public DateTime? UpdatedOnUtc { get; set; } + + /// + /// Gets or sets the product type + /// + [ProductTypeValidationAttribute] + [JsonProperty("product_type")] + public string ProductType + { + get + { + var productTypeId = this._productTypeId; + if (productTypeId != null) return ((ProductType)productTypeId).ToString(); + + return null; + } + set + { + ProductType productTypeId; + if (Enum.TryParse(value, out productTypeId)) + { + this._productTypeId = (int)productTypeId; + } + else this._productTypeId = null; + } + } + + [JsonProperty("parent_grouped_product_id")] + public int? ParentGroupedProductId { get; set; } + + [JsonProperty("role_ids")] + public List RoleIds + { + get + { + return _roleIds; + } + set + { + _roleIds = value; + } + } + + [JsonProperty("discount_ids")] + public List DiscountIds + { + get + { + return _discountIds; + } + set + { + _discountIds = value; + } + } + + [JsonProperty("store_ids")] + public List StoreIds + { + get + { + return _storeIds; + } + set + { + _storeIds = value; + } + } + + [JsonProperty("manufacturer_ids")] + public List ManufacturerIds + { + get + { + return _manufacturerIds; + } + set + { + _manufacturerIds = value; + } + } + + [ImageCollectionValidation] + [JsonProperty("images")] + public List Images + { + get + { + return _images; + } + set + { + _images = value; + } + } + + [JsonProperty("attributes")] + public List ProductAttributeMappings + { + get + { + return _productAttributeMappings; + } + set + { + _productAttributeMappings = value; + } + } + + [JsonProperty("product_attribute_combinations")] + public List ProductAttributeCombinations + { + get + { + return _productAttributeCombinations; + } + set + { + _productAttributeCombinations = value; + } + } + + + [JsonProperty("product_specification_attributes")] + public List ProductSpecificationAttributes + { + get + { + return _productSpecificationAttributes; + } + set + { + _productSpecificationAttributes = value; + } + } + + [JsonProperty("associated_product_ids")] + public List AssociatedProductIds + { + get + { + return _associatedProductIds; + } + set + { + _associatedProductIds = value; + } + } + + [JsonProperty("tags")] + public List Tags + { + get + { + return _tags; + } + set + { + _tags = value; + } + } + + [ValidateVendor] + [JsonProperty("vendor_id")] + public int? VendorId { get; set; } + + [JsonProperty("se_name")] + public string SeName { get; set; } + } } \ No newline at end of file diff --git a/Nop.Plugin.Api/DTOs/ShoppingCarts/ShoppingCartItemDto.cs b/Nop.Plugin.Api/DTOs/ShoppingCarts/ShoppingCartItemDto.cs index 8e0db69..9c992c0 100644 --- a/Nop.Plugin.Api/DTOs/ShoppingCarts/ShoppingCartItemDto.cs +++ b/Nop.Plugin.Api/DTOs/ShoppingCarts/ShoppingCartItemDto.cs @@ -6,22 +6,17 @@ using Nop.Plugin.Api.DTOs.Products; using Nop.Plugin.Api.Validators; using System.Collections.Generic; +using Nop.Plugin.Api.DTOs.Base; namespace Nop.Plugin.Api.DTOs.ShoppingCarts { [Validator(typeof(ShoppingCartItemDtoValidator))] [JsonObject(Title = "shopping_cart_item")] - public class ShoppingCartItemDto + public class ShoppingCartItemDto : BaseDto { private int? _shoppingCartTypeId; private List _attributes; - /// - /// Gets or sets the id - /// - [JsonProperty("id")] - public string Id { get; set; } - /// /// Gets or sets the selected attributes /// diff --git a/Nop.Plugin.Api/DTOs/Stores/StoreDto.cs b/Nop.Plugin.Api/DTOs/Stores/StoreDto.cs index f56e0b1..a1a7a1a 100644 --- a/Nop.Plugin.Api/DTOs/Stores/StoreDto.cs +++ b/Nop.Plugin.Api/DTOs/Stores/StoreDto.cs @@ -1,19 +1,14 @@ using System.Collections.Generic; using Newtonsoft.Json; +using Nop.Plugin.Api.DTOs.Base; namespace Nop.Plugin.Api.DTOs.Stores { [JsonObject(Title = "store")] - public class StoreDto + public class StoreDto : BaseDto { private List _languageIds; - /// - /// Gets or sets the store ID - /// - [JsonProperty("id")] - public string Id { get; set; } - /// /// Gets or sets the store name /// diff --git a/Nop.Plugin.Api/Data/ApiObjectContext.cs b/Nop.Plugin.Api/Data/ApiObjectContext.cs index d35dc9a..8a06edb 100644 --- a/Nop.Plugin.Api/Data/ApiObjectContext.cs +++ b/Nop.Plugin.Api/Data/ApiObjectContext.cs @@ -1,136 +1,118 @@ -using System; -using System.Collections.Generic; -using System.Data.Entity; -using System.Data.Entity.Infrastructure; -using Nop.Core; -using Nop.Data; -using Nop.Plugin.Api.DataMappings; -using Nop.Plugin.Api.Domain; -using System.Linq; - -namespace Nop.Plugin.Api.Data -{ - public class ApiObjectContext : DbContext, IDbContext - { - public ApiObjectContext(string nameOrConnectionString) - : base(nameOrConnectionString) - { - //((IObjectContextAdapter) this).ObjectContext.ContextOptions.LazyLoadingEnabled = true; - } - - protected override void OnModelCreating(DbModelBuilder modelBuilder) - { - Database.SetInitializer(null); - - modelBuilder.Configurations.Add(new WebHooksMap()); - - //disable EdmMetadata generation - //modelBuilder.Conventions.Remove(); - base.OnModelCreating(modelBuilder); - } - - public string CreateDatabaseScript() - { - return ((IObjectContextAdapter)this).ObjectContext.CreateDatabaseScript(); - } - - /// - /// Install - /// - public void Install() - { - //create the table - var dbScript = CreateDatabaseScript(); - Database.ExecuteSqlCommand(dbScript); - SaveChanges(); - } - - /// - /// Uninstall - /// - public void Uninstall() - { - var webHooksName = this.GetTableName(); - DropPluginTableWithSchema(this, webHooksName); - } - - // The WebHook table has a different schema than the nopCommerce tables, - // so in order to drop the table we should use this method. - private void DropPluginTableWithSchema(DbContext context, string tableName) - { - if (context == null) - throw new ArgumentNullException("context"); - - if (String.IsNullOrEmpty(tableName)) - throw new ArgumentNullException("tableName"); - - //drop the table - var tableSchema = context.Database.SqlQuery("SELECT TABLE_SCHEMA FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = {0}", tableName).FirstOrDefault(); - - if (!String.IsNullOrEmpty(tableSchema)) - { - var dbScript = String.Format("DROP TABLE [{0}].[{1}]", tableSchema, tableName); - context.Database.ExecuteSqlCommand(dbScript); - } - context.SaveChanges(); - } - - public IDbSet Set() where TEntity : BaseEntity - { - return base.Set(); - } - - public IList ExecuteStoredProcedureList(string commandText, params object[] parameters) where TEntity : BaseEntity, new() - { - throw new NotImplementedException(); - } - - public IEnumerable SqlQuery(string sql, params object[] parameters) - { - throw new NotImplementedException(); - } - - public int ExecuteSqlCommand(string sql, bool doNotEnsureTransaction = false, int? timeout = null, params object[] parameters) - { - throw new NotImplementedException(); - } - - public void Detach(object entity) - { - if (entity == null) - throw new ArgumentNullException("entity"); - - ((IObjectContextAdapter)this).ObjectContext.Detach(entity); - } - - /// - /// Gets or sets a value indicating whether proxy creation setting is enabled (used in EF) - /// - public virtual bool ProxyCreationEnabled - { - get - { - return this.Configuration.ProxyCreationEnabled; - } - set - { - this.Configuration.ProxyCreationEnabled = value; - } - } - - /// - /// Gets or sets a value indicating whether auto detect changes setting is enabled (used in EF) - /// - public virtual bool AutoDetectChangesEnabled - { - get - { - return this.Configuration.AutoDetectChangesEnabled; - } - set - { - this.Configuration.AutoDetectChangesEnabled = value; - } - } - } +using System; +//using System.Data.Entity.Infrastructure; +using System.Linq; +using Microsoft.EntityFrameworkCore; +using Nop.Core; +using Nop.Data; +using Nop.Data.Extensions; +using Nop.Plugin.Api.DataMappings; + +namespace Nop.Plugin.Api.Data +{ + public class ApiObjectContext : DbContext, IDbContext + { + public ApiObjectContext(DbContextOptions options) + : base(options) + { + } + + public ApiObjectContext() + { + + } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder.ApplyConfiguration(new WebHooksMap()); + + base.OnModelCreating(modelBuilder); + } + + /// + /// Install + /// + public void Install() + { + this.ExecuteSqlScript(this.GenerateCreateScript()); + } + + /// + /// Uninstall + /// + public void Uninstall() + { + this.DropPluginTable(nameof(Domain.WebHooks)); + } + + /// + /// Creates a DbSet that can be used to query and save instances of entity + /// + /// Entity type + /// A set for the given entity type + public virtual new DbSet Set() where TEntity : BaseEntity + { + return base.Set(); + } + + /// + /// Generate a script to create all tables for the current model + /// + /// A SQL script + public virtual string GenerateCreateScript() + { + return this.Database.GenerateCreateScript(); + } + + /// + /// Creates a LINQ query for the query type based on a raw SQL query + /// + /// Query type + /// The raw SQL query + /// An IQueryable representing the raw SQL query + public virtual IQueryable QueryFromSql(string sql) where TQuery : class + { + throw new NotImplementedException(); + } + + /// + /// Creates a LINQ query for the entity based on a raw SQL query + /// + /// Entity type + /// The raw SQL query + /// The values to be assigned to parameters + /// An IQueryable representing the raw SQL query + public virtual IQueryable EntityFromSql(string sql, params object[] parameters) where TEntity : BaseEntity + { + throw new NotImplementedException(); + } + + /// + /// Executes the given SQL against the database + /// + /// The SQL to execute + /// true - the transaction creation is not ensured; false - the transaction creation is ensured. + /// The timeout to use for command. Note that the command timeout is distinct from the connection timeout, which is commonly set on the database connection string + /// Parameters to use with the SQL + /// The number of rows affected + public virtual int ExecuteSqlCommand(RawSqlString sql, bool doNotEnsureTransaction = false, int? timeout = null, params object[] parameters) + { + using (var transaction = this.Database.BeginTransaction()) + { + var result = this.Database.ExecuteSqlCommand(sql, parameters); + transaction.Commit(); + + return result; + } + } + + /// + /// Detach an entity from the context + /// + /// Entity type + /// Entity + public virtual void Detach(TEntity entity) where TEntity : BaseEntity + { + throw new NotImplementedException(); + } + } } \ No newline at end of file diff --git a/Nop.Plugin.Api/DataMappings/WebHooksMap.cs b/Nop.Plugin.Api/DataMappings/WebHooksMap.cs index f1568a0..c7f3415 100644 --- a/Nop.Plugin.Api/DataMappings/WebHooksMap.cs +++ b/Nop.Plugin.Api/DataMappings/WebHooksMap.cs @@ -1,23 +1,18 @@ -using System; -using System.Collections.Generic; -using System.Data.Entity.ModelConfiguration; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Nop.Plugin.Api.Domain; - -namespace Nop.Plugin.Api.DataMappings -{ - public class WebHooksMap : EntityTypeConfiguration - { - public WebHooksMap() - { - ToTable("WebHooks", "WebHooks"); - - HasKey(wh => new {wh.User, wh.Id}); - - Property(wh => wh.ProtectedData).IsRequired(); - Property(wh => wh.RowVer).IsRowVersion(); - } - } -} +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Nop.Data.Mapping; + +namespace Nop.Plugin.Api.DataMappings +{ + public class WebHooksMap : NopEntityTypeConfiguration + { + public override void Configure(EntityTypeBuilder builder) + { + builder.ToTable("WebHooks", "WebHooks"); + builder.HasKey(wh => new { wh.User, wh.Id }); + + builder.Property(wh => wh.ProtectedData).IsRequired(); + builder.Property(wh => wh.RowVer).IsRowVersion(); + } + } +} diff --git a/Nop.Plugin.Api/Delta/Delta.cs b/Nop.Plugin.Api/Delta/Delta.cs index 493a816..be6a313 100644 --- a/Nop.Plugin.Api/Delta/Delta.cs +++ b/Nop.Plugin.Api/Delta/Delta.cs @@ -1,148 +1,148 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using Nop.Core.Infrastructure; -using Nop.Plugin.Api.Helpers; -using Nop.Plugin.Api.Maps; - -namespace Nop.Plugin.Api.Delta -{ - public class Delta where TDto : class, new() - { - private TDto _dto; - - private readonly IMappingHelper _mappingHelper = new MappingHelper(); - - private readonly Dictionary _changedJsonPropertyNames; - - private readonly IJsonPropertyMapper _jsonPropertyMapper; - - public Dictionary ObjectPropertyNameValuePairs = new Dictionary(); - - private Dictionary _propertyValuePairs; - - private Dictionary PropertyValuePairs - { - get - { - if (_propertyValuePairs == null) - { - _propertyValuePairs = GetPropertyValuePairs(typeof(TDto), _changedJsonPropertyNames); - } - - return _propertyValuePairs; - } - } - - public TDto Dto - { - get - { - if (_dto == null) - { - _dto = new TDto(); - } - - return _dto; - } - } - - public Delta(Dictionary passedChangedJsonPropertyValuePaires) - { - _jsonPropertyMapper = EngineContext.Current.Resolve(); - _changedJsonPropertyNames = passedChangedJsonPropertyValuePaires; - - _mappingHelper.SetValues(PropertyValuePairs, Dto, typeof(TDto), ObjectPropertyNameValuePairs, true); - } - - public void Merge(TEntity entity, bool mergeComplexTypeCollections = true) - { - _mappingHelper.SetValues(PropertyValuePairs, entity, entity.GetType(), null,mergeComplexTypeCollections); - } - - public void Merge(object dto, TEntity entity, bool mergeComplexTypeCollections = true) - { - if (dto != null && ObjectPropertyNameValuePairs.ContainsKey(dto)) - { - var propertyValuePairs = ObjectPropertyNameValuePairs[dto] as Dictionary; - _mappingHelper.SetValues(propertyValuePairs, entity, entity.GetType(), null, mergeComplexTypeCollections); - } - } - - private Dictionary GetPropertyValuePairs(Type type, Dictionary changedJsonPropertyNames) - { - var propertyValuePairs = new Dictionary(); - - if (changedJsonPropertyNames == null) - return propertyValuePairs; - - Dictionary> typeMap = _jsonPropertyMapper.GetMap(type); - - foreach (var changedProperty in changedJsonPropertyNames) - { - string jsonName = changedProperty.Key; - - if (typeMap.ContainsKey(jsonName)) - { - Tuple propertyNameAndType = typeMap[jsonName]; - - string propertyName = propertyNameAndType.Item1; - Type propertyType = propertyNameAndType.Item2; - - // Handle system types - // This is also the recursion base - if (propertyType.Namespace == "System") - { - propertyValuePairs.Add(propertyName, changedProperty.Value); - } - // Handle collections - else if (propertyType.GetInterface(typeof(IEnumerable).FullName) != null) - { - // skip any collections that are passed as null - // we can handle only empty collection, which will delete any items if exist - // or collections that has some elements which need to be updated/added/deleted. - if(changedProperty.Value == null) - continue; - - var collection = changedProperty.Value as IEnumerable; - Type collectionElementsType = propertyType.GetGenericArguments()[0]; - var resultCollection = new List(); - - foreach (var item in collection) - { - // Simple types in collection - if (collectionElementsType.Namespace == "System") - { - resultCollection.Add(item); - } - // Complex types in collection - else - { - // the complex type could be null so we try a defensive cast - Dictionary itemDictionary = - item as Dictionary; - - resultCollection.Add(GetPropertyValuePairs(collectionElementsType,itemDictionary)); - } - } - - propertyValuePairs.Add(propertyName, resultCollection); - } - // Handle nested properties - else - { - // the complex type could be null so we try a defensive cast - Dictionary changedPropertyValueDictionary = - changedProperty.Value as Dictionary; - - var resultedNestedObject = GetPropertyValuePairs(propertyType, changedPropertyValueDictionary); - - propertyValuePairs.Add(propertyName, resultedNestedObject); - } - } - } - - return propertyValuePairs; - } - } +using System; +using System.Collections; +using System.Collections.Generic; +using Nop.Core.Infrastructure; +using Nop.Plugin.Api.Helpers; +using Nop.Plugin.Api.Maps; + +namespace Nop.Plugin.Api.Delta +{ + public class Delta where TDto : class, new() + { + private TDto _dto; + + private readonly IMappingHelper _mappingHelper = new MappingHelper(); + + private readonly Dictionary _changedJsonPropertyNames; + + private readonly IJsonPropertyMapper _jsonPropertyMapper; + + public Dictionary ObjectPropertyNameValuePairs = new Dictionary(); + + private Dictionary _propertyValuePairs; + + private Dictionary PropertyValuePairs + { + get + { + if (_propertyValuePairs == null) + { + _propertyValuePairs = GetPropertyValuePairs(typeof(TDto), _changedJsonPropertyNames); + } + + return _propertyValuePairs; + } + } + + public TDto Dto + { + get + { + if (_dto == null) + { + _dto = new TDto(); + } + + return _dto; + } + } + + public Delta(Dictionary passedChangedJsonPropertyValuePaires) + { + _jsonPropertyMapper = EngineContext.Current.Resolve(); + _changedJsonPropertyNames = passedChangedJsonPropertyValuePaires; + + _mappingHelper.SetValues(PropertyValuePairs, Dto, typeof(TDto), ObjectPropertyNameValuePairs, true); + } + + public void Merge(TEntity entity, bool mergeComplexTypeCollections = true) + { + _mappingHelper.SetValues(PropertyValuePairs, entity, entity.GetType(), null,mergeComplexTypeCollections); + } + + public void Merge(object dto, TEntity entity, bool mergeComplexTypeCollections = true) + { + if (dto != null && ObjectPropertyNameValuePairs.ContainsKey(dto)) + { + var propertyValuePairs = ObjectPropertyNameValuePairs[dto] as Dictionary; + _mappingHelper.SetValues(propertyValuePairs, entity, entity.GetType(), null, mergeComplexTypeCollections); + } + } + + private Dictionary GetPropertyValuePairs(Type type, Dictionary changedJsonPropertyNames) + { + var propertyValuePairs = new Dictionary(); + + if (changedJsonPropertyNames == null) + return propertyValuePairs; + + var typeMap = _jsonPropertyMapper.GetMap(type); + + foreach (var changedProperty in changedJsonPropertyNames) + { + var jsonName = changedProperty.Key; + + if (typeMap.ContainsKey(jsonName)) + { + var propertyNameAndType = typeMap[jsonName]; + + var propertyName = propertyNameAndType.Item1; + var propertyType = propertyNameAndType.Item2; + + // Handle system types + // This is also the recursion base + if (propertyType.Namespace == "System") + { + propertyValuePairs.Add(propertyName, changedProperty.Value); + } + // Handle collections + else if (propertyType.GetInterface(typeof(IEnumerable).FullName) != null) + { + // skip any collections that are passed as null + // we can handle only empty collection, which will delete any items if exist + // or collections that has some elements which need to be updated/added/deleted. + if(changedProperty.Value == null) + continue; + + var collection = changedProperty.Value as IEnumerable; + var collectionElementsType = propertyType.GetGenericArguments()[0]; + var resultCollection = new List(); + + foreach (var item in collection) + { + // Simple types in collection + if (collectionElementsType.Namespace == "System") + { + resultCollection.Add(item); + } + // Complex types in collection + else + { + // the complex type could be null so we try a defensive cast + var itemDictionary = + item as Dictionary; + + resultCollection.Add(GetPropertyValuePairs(collectionElementsType,itemDictionary)); + } + } + + propertyValuePairs.Add(propertyName, resultCollection); + } + // Handle nested properties + else + { + // the complex type could be null so we try a defensive cast + var changedPropertyValueDictionary = + changedProperty.Value as Dictionary; + + var resultedNestedObject = GetPropertyValuePairs(propertyType, changedPropertyValueDictionary); + + propertyValuePairs.Add(propertyName, resultedNestedObject); + } + } + } + + return propertyValuePairs; + } + } } \ No newline at end of file diff --git a/Nop.Plugin.Api/Domain/WebHooks.cs b/Nop.Plugin.Api/Domain/WebHooks.cs index 1a8788a..6314912 100644 --- a/Nop.Plugin.Api/Domain/WebHooks.cs +++ b/Nop.Plugin.Api/Domain/WebHooks.cs @@ -3,11 +3,12 @@ using System.Linq; using System.Text; using System.Threading.Tasks; +using Microsoft.AspNetCore.WebHooks.Storage; using Nop.Core; namespace Nop.Plugin.Api.Domain { - public class WebHooks : BaseEntity + public class WebHooks : BaseEntity, IRegistration { public string User { get; set; } diff --git a/Nop.Plugin.Api/Factories/ManufacturerFactory.cs b/Nop.Plugin.Api/Factories/ManufacturerFactory.cs new file mode 100644 index 0000000..cc4bd2f --- /dev/null +++ b/Nop.Plugin.Api/Factories/ManufacturerFactory.cs @@ -0,0 +1,32 @@ +using Nop.Core.Domain.Catalog; +using System; + +namespace Nop.Plugin.Api.Factories +{ + public class ManufacturerFactory : IFactory + { + private readonly CatalogSettings _catalogSettings; + + public ManufacturerFactory(CatalogSettings catalogSettings) + { + _catalogSettings = catalogSettings; + } + + public Manufacturer Initialize() + { + // TODO: cache the default entity. + var defaultManufacturer = new Manufacturer(); + + //default values + defaultManufacturer.PageSize = _catalogSettings.DefaultManufacturerPageSize; + defaultManufacturer.PageSizeOptions = _catalogSettings.DefaultManufacturerPageSizeOptions; + defaultManufacturer.Published = true; + defaultManufacturer.AllowCustomersToSelectPageSize = true; + + defaultManufacturer.CreatedOnUtc = DateTime.UtcNow; + defaultManufacturer.UpdatedOnUtc = DateTime.UtcNow; + + return defaultManufacturer; + } + } +} \ No newline at end of file diff --git a/Nop.Plugin.Api/Helpers/CryptoHelper.cs b/Nop.Plugin.Api/Helpers/CryptoHelper.cs index 1cbd609..4a00119 100644 --- a/Nop.Plugin.Api/Helpers/CryptoHelper.cs +++ b/Nop.Plugin.Api/Helpers/CryptoHelper.cs @@ -1,102 +1,106 @@ -namespace Nop.Plugin.Api.Helpers -{ - using Microsoft.IdentityModel.Tokens; - using Newtonsoft.Json; - using System.IO; - using System.Security.Cryptography; - using Nop.Core; - - public static class CryptoHelper - { - // Need to ensure that the key would be the same through the application lifetime. - private static RsaSecurityKey _key; - private const string TokenSigningKeyFileName = "api-token-signing-key.json"; - - public static RsaSecurityKey CreateRsaSecurityKey() - { - if (_key == null) - { - string pathToKey = CommonHelper.MapPath($"~/App_Data/{TokenSigningKeyFileName}"); - - if (!File.Exists(pathToKey)) - { - // generate random parameters - var randomParameters = GetRandomParameters(); - - var rsaParams = new RSAParametersWithPrivate(); - rsaParams.SetParameters(randomParameters); - string serializedParameters = JsonConvert.SerializeObject(rsaParams); - - // create file and save the key - File.WriteAllText(pathToKey, serializedParameters); - } - - // load the key - if (!File.Exists(pathToKey)) - throw new FileNotFoundException("Check configuration - cannot find auth key file: " + pathToKey); - - var keyParams = JsonConvert.DeserializeObject(File.ReadAllText(pathToKey)); - - // create signing key by the key above - _key = new RsaSecurityKey(keyParams.ToRSAParameters()); - } - - return _key; - } - - public static RSAParameters GetRandomParameters() - { - using (var rsa = new RSACryptoServiceProvider(2048)) - { - try - { - return rsa.ExportParameters(true); - } - finally - { - rsa.PersistKeyInCsp = false; - } - } - } - - // https://github.com/mrsheepuk/ASPNETSelfCreatedTokenAuthExample/blob/master/src/TokenAuthExampleWebApplication/RSAKeyUtils.cs - private class RSAParametersWithPrivate - { - public byte[] D { get; set; } - public byte[] DP { get; set; } - public byte[] DQ { get; set; } - public byte[] Exponent { get; set; } - public byte[] InverseQ { get; set; } - public byte[] Modulus { get; set; } - public byte[] P { get; set; } - public byte[] Q { get; set; } - - public void SetParameters(RSAParameters p) - { - D = p.D; - DP = p.DP; - DQ = p.DQ; - Exponent = p.Exponent; - InverseQ = p.InverseQ; - Modulus = p.Modulus; - P = p.P; - Q = p.Q; - } - public RSAParameters ToRSAParameters() - { - return new RSAParameters() - { - D = this.D, - DP = this.DP, - DQ = this.DQ, - Exponent = this.Exponent, - InverseQ = this.InverseQ, - Modulus = this.Modulus, - P = this.P, - Q = this.Q - - }; - } - } - } +using Nop.Core.Infrastructure; + +namespace Nop.Plugin.Api.Helpers +{ + using Microsoft.IdentityModel.Tokens; + using Newtonsoft.Json; + using System.IO; + using System.Security.Cryptography; + using Nop.Core; + using Microsoft.Extensions.DependencyInjection; + + + public static class CryptoHelper + { + // Need to ensure that the key would be the same through the application lifetime. + private static RsaSecurityKey _key; + private const string TokenSigningKeyFileName = "api-token-signing-key.json"; + + public static RsaSecurityKey CreateRsaSecurityKey() + { + if (_key == null) + { + string pathToKey = CommonHelper.DefaultFileProvider.MapPath($"~/App_Data/{TokenSigningKeyFileName}"); + + if (!File.Exists(pathToKey)) + { + // generate random parameters + var randomParameters = GetRandomParameters(); + + var rsaParams = new RSAParametersWithPrivate(); + rsaParams.SetParameters(randomParameters); + string serializedParameters = JsonConvert.SerializeObject(rsaParams); + + // create file and save the key + File.WriteAllText(pathToKey, serializedParameters); + } + + // load the key + if (!File.Exists(pathToKey)) + throw new FileNotFoundException("Check configuration - cannot find auth key file: " + pathToKey); + + var keyParams = JsonConvert.DeserializeObject(File.ReadAllText(pathToKey)); + + // create signing key by the key above + _key = new RsaSecurityKey(keyParams.ToRSAParameters()); + } + + return _key; + } + + public static RSAParameters GetRandomParameters() + { + using (var rsa = new RSACryptoServiceProvider(2048)) + { + try + { + return rsa.ExportParameters(true); + } + finally + { + rsa.PersistKeyInCsp = false; + } + } + } + + // https://github.com/mrsheepuk/ASPNETSelfCreatedTokenAuthExample/blob/master/src/TokenAuthExampleWebApplication/RSAKeyUtils.cs + private class RSAParametersWithPrivate + { + public byte[] D { get; set; } + public byte[] DP { get; set; } + public byte[] DQ { get; set; } + public byte[] Exponent { get; set; } + public byte[] InverseQ { get; set; } + public byte[] Modulus { get; set; } + public byte[] P { get; set; } + public byte[] Q { get; set; } + + public void SetParameters(RSAParameters p) + { + D = p.D; + DP = p.DP; + DQ = p.DQ; + Exponent = p.Exponent; + InverseQ = p.InverseQ; + Modulus = p.Modulus; + P = p.P; + Q = p.Q; + } + public RSAParameters ToRSAParameters() + { + return new RSAParameters() + { + D = this.D, + DP = this.DP, + DQ = this.DQ, + Exponent = this.Exponent, + InverseQ = this.InverseQ, + Modulus = this.Modulus, + P = this.P, + Q = this.Q + + }; + } + } + } } \ No newline at end of file diff --git a/Nop.Plugin.Api/Helpers/CustomerRolesHelper.cs b/Nop.Plugin.Api/Helpers/CustomerRolesHelper.cs index d07c50d..4aa7cd1 100644 --- a/Nop.Plugin.Api/Helpers/CustomerRolesHelper.cs +++ b/Nop.Plugin.Api/Helpers/CustomerRolesHelper.cs @@ -1,51 +1,51 @@ -using System.Collections.Generic; -using System.Linq; -using Nop.Core.Caching; -using Nop.Core.Domain.Customers; -using Nop.Services.Customers; - -namespace Nop.Plugin.Api.Helpers -{ - public class CustomerRolesHelper : ICustomerRolesHelper - { - private const string CUSTOMERROLES_ALL_KEY = "Nop.customerrole.all-{0}"; - - private readonly ICustomerService _customerService; - private readonly ICacheManager _cacheManager; - - public CustomerRolesHelper(ICustomerService customerService, ICacheManager cacheManager) - { - _customerService = customerService; - _cacheManager = cacheManager; - } - - public IList GetValidCustomerRoles(List roleIds) - { - // This is needed because the caching messeup the entity framework context - // and when you try to send something TO the database it throws an exeption. - _cacheManager.RemoveByPattern(CUSTOMERROLES_ALL_KEY); - - var allCustomerRoles = _customerService.GetAllCustomerRoles(true); - var newCustomerRoles = new List(); - foreach (var customerRole in allCustomerRoles) - { - if (roleIds != null && roleIds.Contains(customerRole.Id)) - { - newCustomerRoles.Add(customerRole); - } - } - - return newCustomerRoles; - } - - public bool IsInGuestsRole(IList customerRoles) - { - return customerRoles.FirstOrDefault(cr => cr.SystemName == SystemCustomerRoleNames.Guests) != null; - } - - public bool IsInRegisteredRole(IList customerRoles) - { - return customerRoles.FirstOrDefault(cr => cr.SystemName == SystemCustomerRoleNames.Registered) != null; - } - } +using System.Collections.Generic; +using System.Linq; +using Nop.Core.Caching; +using Nop.Core.Domain.Customers; +using Nop.Services.Customers; + +namespace Nop.Plugin.Api.Helpers +{ + public class CustomerRolesHelper : ICustomerRolesHelper + { + private const string CUSTOMERROLES_ALL_KEY = "Nop.customerrole.all-{0}"; + + private readonly ICustomerService _customerService; + private readonly ICacheManager _cacheManager; + + public CustomerRolesHelper(ICustomerService customerService, ICacheManager cacheManager) + { + _customerService = customerService; + _cacheManager = cacheManager; + } + + public IList GetValidCustomerRoles(List roleIds) + { + // This is needed because the caching messeup the entity framework context + // and when you try to send something TO the database it throws an exeption. + _cacheManager.RemoveByPattern(CUSTOMERROLES_ALL_KEY); + + var allCustomerRoles = _customerService.GetAllCustomerRoles(true); + var newCustomerRoles = new List(); + foreach (var customerRole in allCustomerRoles) + { + if (roleIds != null && roleIds.Contains(customerRole.Id)) + { + newCustomerRoles.Add(customerRole); + } + } + + return newCustomerRoles; + } + + public bool IsInGuestsRole(IList customerRoles) + { + return customerRoles.FirstOrDefault(cr => cr.SystemName == NopCustomerDefaults.GuestsRoleName) != null; + } + + public bool IsInRegisteredRole(IList customerRoles) + { + return customerRoles.FirstOrDefault(cr => cr.SystemName == NopCustomerDefaults.RegisteredRoleName) != null; + } + } } \ No newline at end of file diff --git a/Nop.Plugin.Api/Helpers/DTOHelper.cs b/Nop.Plugin.Api/Helpers/DTOHelper.cs index fc1351d..395ab62 100644 --- a/Nop.Plugin.Api/Helpers/DTOHelper.cs +++ b/Nop.Plugin.Api/Helpers/DTOHelper.cs @@ -1,353 +1,433 @@ -using Nop.Core; -using Nop.Core.Domain.Catalog; -using Nop.Core.Domain.Directory; -using Nop.Core.Domain.Localization; -using Nop.Core.Domain.Media; -using Nop.Core.Domain.Orders; -using Nop.Core.Domain.Stores; -using Nop.Plugin.Api.DTOs.Categories; -using Nop.Plugin.Api.DTOs.Customers; -using Nop.Plugin.Api.DTOs.Images; -using Nop.Plugin.Api.DTOs.Languages; -using Nop.Plugin.Api.DTOs.OrderItems; -using Nop.Plugin.Api.DTOs.Orders; -using Nop.Plugin.Api.DTOs.ProductAttributes; -using Nop.Plugin.Api.DTOs.Products; -using Nop.Plugin.Api.DTOs.ShoppingCarts; -using Nop.Plugin.Api.DTOs.SpecificationAttributes; -using Nop.Plugin.Api.DTOs.Stores; -using Nop.Plugin.Api.MappingExtensions; -using Nop.Plugin.Api.Services; -using Nop.Services.Catalog; -using Nop.Services.Directory; -using Nop.Services.Localization; -using Nop.Services.Media; -using Nop.Services.Security; -using Nop.Services.Seo; -using Nop.Services.Stores; -using System; -using System.Collections.Generic; -using System.Linq; - -namespace Nop.Plugin.Api.Helpers -{ - public class DTOHelper : IDTOHelper - { - private IStoreContext _storeContext; - private IProductService _productService; - private IAclService _aclService; - private IStoreMappingService _storeMappingService; - private IPictureService _pictureService; - private IProductAttributeService _productAttributeService; - private ILanguageService _languageService; - private ICurrencyService _currencyService; - private CurrencySettings _currencySettings; - private readonly IStoreService _storeService; - private ICustomerApiService _customerApiService; - private IProductAttributeConverter _productAttributeConverter; - - public DTOHelper(IStoreContext storeContext, - IProductService productService, - IAclService aclService, - IStoreMappingService storeMappingService, - IPictureService pictureService, - IProductAttributeService productAttributeService, - ICustomerApiService customerApiService, - IProductAttributeConverter productAttributeConverter, - ILanguageService languageService, - ICurrencyService currencyService, - CurrencySettings currencySettings, - IStoreService storeService) - { - _productService = productService; - _aclService = aclService; - _storeMappingService = storeMappingService; - _pictureService = pictureService; - _productAttributeService = productAttributeService; - _customerApiService = customerApiService; - _productAttributeConverter = productAttributeConverter; - _languageService = languageService; - _currencyService = currencyService; - _currencySettings = currencySettings; - _storeService = storeService; - _storeContext = storeContext; - } - - public ProductDto PrepareProductDTO(Product product) - { - ProductDto productDto = product.ToDto(); - - PrepareProductImages(product.ProductPictures, productDto); - PrepareProductAttributes(product.ProductAttributeMappings, productDto); - PrepareProductSpecificationAttributes(product.ProductSpecificationAttributes, productDto); - - productDto.SeName = product.GetSeName(); - productDto.DiscountIds = product.AppliedDiscounts.Select(discount => discount.Id).ToList(); - productDto.ManufacturerIds = product.ProductManufacturers.Select(pm => pm.ManufacturerId).ToList(); - productDto.RoleIds = _aclService.GetAclRecords(product).Select(acl => acl.CustomerRoleId).ToList(); - productDto.StoreIds = _storeMappingService.GetStoreMappings(product).Select(mapping => mapping.StoreId).ToList(); - productDto.Tags = product.ProductTags.Select(tag => tag.Name).ToList(); - - productDto.AssociatedProductIds = - _productService.GetAssociatedProducts(product.Id, showHidden: true) - .Select(associatedProduct => associatedProduct.Id) - .ToList(); - - IList allLanguages = _languageService.GetAllLanguages(); - - productDto.LocalizedNames = new List(); - - foreach (var language in allLanguages) - { - var localizedNameDto = new LocalizedNameDto - { - LanguageId = language.Id, - LocalizedName = product.GetLocalized(x => x.Name, language.Id) - }; - - productDto.LocalizedNames.Add(localizedNameDto); - } - - return productDto; - } - - public CategoryDto PrepareCategoryDTO(Category category) - { - CategoryDto categoryDto = category.ToDto(); - - Picture picture = _pictureService.GetPictureById(category.PictureId); - ImageDto imageDto = PrepareImageDto(picture); - - if (imageDto != null) - { - categoryDto.Image = imageDto; - } - - categoryDto.SeName = category.GetSeName(); - categoryDto.DiscountIds = category.AppliedDiscounts.Select(discount => discount.Id).ToList(); - categoryDto.RoleIds = _aclService.GetAclRecords(category).Select(acl => acl.CustomerRoleId).ToList(); - categoryDto.StoreIds = _storeMappingService.GetStoreMappings(category).Select(mapping => mapping.StoreId).ToList(); - - IList allLanguages = _languageService.GetAllLanguages(); - - categoryDto.LocalizedNames = new List(); - - foreach (var language in allLanguages) - { - var localizedNameDto = new LocalizedNameDto - { - LanguageId = language.Id, - LocalizedName = category.GetLocalized(x => x.Name, language.Id) - }; - - categoryDto.LocalizedNames.Add(localizedNameDto); - } - - return categoryDto; - } - - public OrderDto PrepareOrderDTO(Order order) - { - OrderDto orderDto = order.ToDto(); - - orderDto.OrderItemDtos = order.OrderItems.Select(orderItem => PrepareOrderItemDTO(orderItem)).ToList(); - - CustomerDto customerDto = _customerApiService.GetCustomerById(order.Customer.Id); - - if (customerDto != null) - { - orderDto.Customer = customerDto.ToOrderCustomerDto(); - } - - return orderDto; - } - - public ShoppingCartItemDto PrepareShoppingCartItemDTO(ShoppingCartItem shoppingCartItem) - { - var dto = shoppingCartItem.ToDto(); - dto.ProductDto = PrepareProductDTO(shoppingCartItem.Product); - dto.CustomerDto = shoppingCartItem.Customer.ToCustomerForShoppingCartItemDto(); - dto.Attributes = _productAttributeConverter.Parse(shoppingCartItem.AttributesXml); - return dto; - } - - public OrderItemDto PrepareOrderItemDTO(OrderItem orderItem) - { - var dto = orderItem.ToDto(); - dto.Product = PrepareProductDTO(orderItem.Product); - dto.Attributes = _productAttributeConverter.Parse(orderItem.AttributesXml); - return dto; - } - - public StoreDto PrepareStoreDTO(Store store) - { - StoreDto storeDto = store.ToDto(); - - Currency primaryCurrency = _currencyService.GetCurrencyById(_currencySettings.PrimaryStoreCurrencyId); - - if (!String.IsNullOrEmpty(primaryCurrency.DisplayLocale)) - { - storeDto.PrimaryCurrencyDisplayLocale = primaryCurrency.DisplayLocale; - } - - storeDto.LanguageIds = _languageService.GetAllLanguages(false, store.Id).Select(x => x.Id).ToList(); - - return storeDto; - } - - public LanguageDto PrepateLanguageDto(Language language) - { - LanguageDto languageDto = language.ToDto(); - - languageDto.StoreIds = _storeMappingService.GetStoreMappings(language).Select(mapping => mapping.StoreId).ToList(); - - if (languageDto.StoreIds.Count == 0) - { - languageDto.StoreIds = _storeService.GetAllStores().Select(s => s.Id).ToList(); - } - - return languageDto; - } - - private void PrepareProductImages(IEnumerable productPictures, ProductDto productDto) - { - if (productDto.Images == null) - productDto.Images = new List(); - - // Here we prepare the resulted dto image. - foreach (var productPicture in productPictures) - { - ImageDto imageDto = PrepareImageDto(productPicture.Picture); - - if (imageDto != null) - { - ImageMappingDto productImageDto = new ImageMappingDto(); - productImageDto.Id = productPicture.Id; - productImageDto.Position = productPicture.DisplayOrder; - productImageDto.Src = imageDto.Src; - productImageDto.Attachment = imageDto.Attachment; - - productDto.Images.Add(productImageDto); - } - } - } - - protected ImageDto PrepareImageDto(Picture picture) - { - ImageDto image = null; - - if (picture != null) - { - // We don't use the image from the passed dto directly - // because the picture may be passed with src and the result should only include the base64 format. - image = new ImageDto() - { - //Attachment = Convert.ToBase64String(picture.PictureBinary), - Src = _pictureService.GetPictureUrl(picture) - }; - } - - return image; - } - - private void PrepareProductAttributes(IEnumerable productAttributeMappings, ProductDto productDto) - { - if (productDto.ProductAttributeMappings == null) - productDto.ProductAttributeMappings = new List(); - - foreach (var productAttributeMapping in productAttributeMappings) - { - ProductAttributeMappingDto productAttributeMappingDto = PrepareProductAttributeMappingDto(productAttributeMapping); - - if (productAttributeMappingDto != null) - { - productDto.ProductAttributeMappings.Add(productAttributeMappingDto); - } - } - } - - private ProductAttributeMappingDto PrepareProductAttributeMappingDto(ProductAttributeMapping productAttributeMapping) - { - ProductAttributeMappingDto productAttributeMappingDto = null; - - if (productAttributeMapping != null) - { - productAttributeMappingDto = new ProductAttributeMappingDto() - { - Id = productAttributeMapping.Id, - ProductAttributeId = productAttributeMapping.ProductAttributeId, - ProductAttributeName = _productAttributeService.GetProductAttributeById(productAttributeMapping.ProductAttributeId).Name, - TextPrompt = productAttributeMapping.TextPrompt, - DefaultValue = productAttributeMapping.DefaultValue, - AttributeControlTypeId = productAttributeMapping.AttributeControlTypeId, - DisplayOrder = productAttributeMapping.DisplayOrder, - IsRequired = productAttributeMapping.IsRequired, - ProductAttributeValues = productAttributeMapping.ProductAttributeValues.Select(x => PrepareProductAttributeValueDto(x, productAttributeMapping.Product)).ToList() - }; - } - - return productAttributeMappingDto; - } - - private ProductAttributeValueDto PrepareProductAttributeValueDto(ProductAttributeValue productAttributeValue, Product product) - { - ProductAttributeValueDto productAttributeValueDto = null; - - if (productAttributeValue != null) - { - productAttributeValueDto = productAttributeValue.ToDto(); - if (productAttributeValue.ImageSquaresPictureId > 0) - { - Picture imageSquaresPicture = _pictureService.GetPictureById(productAttributeValue.ImageSquaresPictureId); - ImageDto imageDto = PrepareImageDto(imageSquaresPicture); - productAttributeValueDto.ImageSquaresImage = imageDto; - } - - if (productAttributeValue.PictureId > 0) - { - // make sure that the picture is mapped to the product - // This is needed since if you delete the product picture mapping from the nopCommerce administrationthe - // then the attribute value is not updated and it will point to a picture that has been deleted - var productPicture = product.ProductPictures.FirstOrDefault(pp => pp.PictureId == productAttributeValue.PictureId); - if (productPicture != null) - { - productAttributeValueDto.ProductPictureId = productPicture.Id; - } - } - } - - return productAttributeValueDto; - } - - public ProductAttributeDto PrepareProductAttributeDTO(ProductAttribute productAttribute) - { - return productAttribute.ToDto(); - } - - public void PrepareProductSpecificationAttributes(IEnumerable productSpecificationAttributes, ProductDto productDto) - { - if (productDto.ProductSpecificationAttributes == null) - productDto.ProductSpecificationAttributes = new List(); - - foreach (var productSpecificationAttribute in productSpecificationAttributes) - { - ProductSpecificationAttributeDto productSpecificationAttributeDto = PrepareProductSpecificationAttributeDto(productSpecificationAttribute); - - if (productSpecificationAttributeDto != null) - { - productDto.ProductSpecificationAttributes.Add(productSpecificationAttributeDto); - } - } - } - - public ProductSpecificationAttributeDto PrepareProductSpecificationAttributeDto(ProductSpecificationAttribute productSpecificationAttribute) - { - return productSpecificationAttribute.ToDto(); - } - - public SpecificationAttributeDto PrepareSpecificationAttributeDto(SpecificationAttribute specificationAttribute) - { - return specificationAttribute.ToDto(); - } - } -} +using Nop.Core; +using Nop.Core.Domain.Catalog; +using Nop.Core.Domain.Directory; +using Nop.Core.Domain.Localization; +using Nop.Core.Domain.Media; +using Nop.Core.Domain.Orders; +using Nop.Core.Domain.Stores; +using Nop.Plugin.Api.DTOs.Categories; +using Nop.Plugin.Api.DTOs.Customers; +using Nop.Plugin.Api.DTOs.Images; +using Nop.Plugin.Api.DTOs.Languages; +using Nop.Plugin.Api.DTOs.OrderItems; +using Nop.Plugin.Api.DTOs.Orders; +using Nop.Plugin.Api.DTOs.Manufacturers; +using Nop.Plugin.Api.DTOs.ProductAttributes; +using Nop.Plugin.Api.DTOs.Products; +using Nop.Plugin.Api.DTOs.ShoppingCarts; +using Nop.Plugin.Api.DTOs.SpecificationAttributes; +using Nop.Plugin.Api.DTOs.Stores; +using Nop.Plugin.Api.MappingExtensions; +using Nop.Plugin.Api.Services; +using Nop.Services.Catalog; +using Nop.Services.Directory; +using Nop.Services.Localization; +using Nop.Services.Media; +using Nop.Services.Security; +using Nop.Services.Seo; +using Nop.Services.Stores; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Nop.Plugin.Api.Helpers +{ + public class DTOHelper : IDTOHelper + { + private readonly IAclService _aclService; + private readonly ICurrencyService _currencyService; + private readonly CurrencySettings _currencySettings; + private readonly ICustomerApiService _customerApiService; + private readonly ILanguageService _languageService; + private readonly ILocalizationService _localizationService; + private readonly IPictureService _pictureService; + private readonly IProductAttributeConverter _productAttributeConverter; + private readonly IProductAttributeService _productAttributeService; + private readonly IProductService _productService; + private readonly IProductTagService _productTagService; + private readonly IStoreMappingService _storeMappingService; + private readonly IStoreService _storeService; + private readonly IUrlRecordService _urlRecordService; + + public DTOHelper(IProductService productService, + IAclService aclService, + IStoreMappingService storeMappingService, + IPictureService pictureService, + IProductAttributeService productAttributeService, + ICustomerApiService customerApiService, + IProductAttributeConverter productAttributeConverter, + ILanguageService languageService, + ICurrencyService currencyService, + CurrencySettings currencySettings, + IStoreService storeService, + ILocalizationService localizationService, + IUrlRecordService urlRecordService, + IProductTagService productTagService) + { + _productService = productService; + _aclService = aclService; + _storeMappingService = storeMappingService; + _pictureService = pictureService; + _productAttributeService = productAttributeService; + _customerApiService = customerApiService; + _productAttributeConverter = productAttributeConverter; + _languageService = languageService; + _currencyService = currencyService; + _currencySettings = currencySettings; + _storeService = storeService; + _localizationService = localizationService; + _urlRecordService = urlRecordService; + _productTagService = productTagService; + } + + public ProductDto PrepareProductDTO(Product product) + { + var productDto = product.ToDto(); + + PrepareProductImages(product.ProductPictures, productDto); + + productDto.SeName = _urlRecordService.GetSeName(product); + productDto.DiscountIds = product.AppliedDiscounts.Select(discount => discount.Id).ToList(); + productDto.ManufacturerIds = product.ProductManufacturers.Select(pm => pm.ManufacturerId).ToList(); + productDto.RoleIds = _aclService.GetAclRecords(product).Select(acl => acl.CustomerRoleId).ToList(); + productDto.StoreIds = _storeMappingService.GetStoreMappings(product).Select(mapping => mapping.StoreId) + .ToList(); + productDto.Tags = _productTagService.GetAllProductTagsByProductId(product.Id).Select(tag => tag.Name) + .ToList(); + + productDto.AssociatedProductIds = + _productService.GetAssociatedProducts(product.Id, showHidden: true) + .Select(associatedProduct => associatedProduct.Id) + .ToList(); + + var allLanguages = _languageService.GetAllLanguages(); + + productDto.LocalizedNames = new List(); + + foreach (var language in allLanguages) + { + var localizedNameDto = new LocalizedNameDto + { + LanguageId = language.Id, + LocalizedName = _localizationService.GetLocalized(product, x => x.Name, language.Id) + }; + + productDto.LocalizedNames.Add(localizedNameDto); + } + + return productDto; + } + + public CategoryDto PrepareCategoryDTO(Category category) + { + var categoryDto = category.ToDto(); + + var picture = _pictureService.GetPictureById(category.PictureId); + var imageDto = PrepareImageDto(picture); + + if (imageDto != null) + { + categoryDto.Image = imageDto; + } + + categoryDto.SeName = _urlRecordService.GetSeName(category); + categoryDto.DiscountIds = category.AppliedDiscounts.Select(discount => discount.Id).ToList(); + categoryDto.RoleIds = _aclService.GetAclRecords(category).Select(acl => acl.CustomerRoleId).ToList(); + categoryDto.StoreIds = _storeMappingService.GetStoreMappings(category).Select(mapping => mapping.StoreId) + .ToList(); + + var allLanguages = _languageService.GetAllLanguages(); + + categoryDto.LocalizedNames = new List(); + + foreach (var language in allLanguages) + { + var localizedNameDto = new LocalizedNameDto + { + LanguageId = language.Id, + LocalizedName = _localizationService.GetLocalized(category, x => x.Name, language.Id) + }; + + categoryDto.LocalizedNames.Add(localizedNameDto); + } + + return categoryDto; + } + + public OrderDto PrepareOrderDTO(Order order) + { + var orderDto = order.ToDto(); + + orderDto.OrderItems = order.OrderItems.Select(PrepareOrderItemDTO).ToList(); + + var customerDto = _customerApiService.GetCustomerById(order.Customer.Id); + + if (customerDto != null) + { + orderDto.Customer = customerDto.ToOrderCustomerDto(); + } + + return orderDto; + } + + public ShoppingCartItemDto PrepareShoppingCartItemDTO(ShoppingCartItem shoppingCartItem) + { + var dto = shoppingCartItem.ToDto(); + dto.ProductDto = PrepareProductDTO(shoppingCartItem.Product); + dto.CustomerDto = shoppingCartItem.Customer.ToCustomerForShoppingCartItemDto(); + dto.Attributes = _productAttributeConverter.Parse(shoppingCartItem.AttributesXml); + return dto; + } + + public OrderItemDto PrepareOrderItemDTO(OrderItem orderItem) + { + var dto = orderItem.ToDto(); + dto.Product = PrepareProductDTO(orderItem.Product); + dto.Attributes = _productAttributeConverter.Parse(orderItem.AttributesXml); + return dto; + } + + public StoreDto PrepareStoreDTO(Store store) + { + var storeDto = store.ToDto(); + + var primaryCurrency = _currencyService.GetCurrencyById(_currencySettings.PrimaryStoreCurrencyId); + + if (!string.IsNullOrEmpty(primaryCurrency.DisplayLocale)) + { + storeDto.PrimaryCurrencyDisplayLocale = primaryCurrency.DisplayLocale; + } + + storeDto.LanguageIds = _languageService.GetAllLanguages(false, store.Id).Select(x => x.Id).ToList(); + + return storeDto; + } + + public LanguageDto PrepateLanguageDto(Language language) + { + var languageDto = language.ToDto(); + + languageDto.StoreIds = _storeMappingService.GetStoreMappings(language).Select(mapping => mapping.StoreId) + .ToList(); + + if (languageDto.StoreIds.Count == 0) + { + languageDto.StoreIds = _storeService.GetAllStores().Select(s => s.Id).ToList(); + } + + return languageDto; + } + + public ProductAttributeDto PrepareProductAttributeDTO(ProductAttribute productAttribute) + { + return productAttribute.ToDto(); + } + + private void PrepareProductImages(IEnumerable productPictures, ProductDto productDto) + { + if (productDto.Images == null) + { + productDto.Images = new List(); + } + + // Here we prepare the resulted dto image. + foreach (var productPicture in productPictures) + { + var imageDto = PrepareImageDto(productPicture.Picture); + + if (imageDto != null) + { + var productImageDto = new ImageMappingDto + { + Id = productPicture.Id, + PictureId = productPicture.PictureId, + Position = productPicture.DisplayOrder, + Src = imageDto.Src, + Attachment = imageDto.Attachment + }; + + productDto.Images.Add(productImageDto); + } + } + } + + protected ImageDto PrepareImageDto(Picture picture) + { + ImageDto image = null; + + if (picture != null) + { + // We don't use the image from the passed dto directly + // because the picture may be passed with src and the result should only include the base64 format. + image = new ImageDto + { + //Attachment = Convert.ToBase64String(picture.PictureBinary), + Src = _pictureService.GetPictureUrl(picture) + }; + } + + return image; + } + + private void PrepareProductAttributes(IEnumerable productAttributeMappings, + ProductDto productDto) + { + if (productDto.ProductAttributeMappings == null) + { + productDto.ProductAttributeMappings = new List(); + } + + foreach (var productAttributeMapping in productAttributeMappings) + { + var productAttributeMappingDto = + PrepareProductAttributeMappingDto(productAttributeMapping); + + if (productAttributeMappingDto != null) + { + productDto.ProductAttributeMappings.Add(productAttributeMappingDto); + } + } + } + + private ProductAttributeMappingDto PrepareProductAttributeMappingDto( + ProductAttributeMapping productAttributeMapping) + { + ProductAttributeMappingDto productAttributeMappingDto = null; + + if (productAttributeMapping != null) + { + productAttributeMappingDto = new ProductAttributeMappingDto + { + Id = productAttributeMapping.Id, + ProductAttributeId = productAttributeMapping.ProductAttributeId, + ProductAttributeName = _productAttributeService + .GetProductAttributeById(productAttributeMapping.ProductAttributeId).Name, + TextPrompt = productAttributeMapping.TextPrompt, + DefaultValue = productAttributeMapping.DefaultValue, + AttributeControlTypeId = productAttributeMapping.AttributeControlTypeId, + DisplayOrder = productAttributeMapping.DisplayOrder, + IsRequired = productAttributeMapping.IsRequired, + ProductAttributeValues = productAttributeMapping.ProductAttributeValues + .Select(x => PrepareProductAttributeValueDto(x, productAttributeMapping.Product)).ToList() + }; + } + + return productAttributeMappingDto; + } + + private ProductAttributeValueDto PrepareProductAttributeValueDto(ProductAttributeValue productAttributeValue, + Product product) + { + ProductAttributeValueDto productAttributeValueDto = null; + + if (productAttributeValue != null) + { + productAttributeValueDto = productAttributeValue.ToDto(); + if (productAttributeValue.ImageSquaresPictureId > 0) + { + var imageSquaresPicture = + _pictureService.GetPictureById(productAttributeValue.ImageSquaresPictureId); + var imageDto = PrepareImageDto(imageSquaresPicture); + productAttributeValueDto.ImageSquaresImage = imageDto; + } + + if (productAttributeValue.PictureId > 0) + { + // make sure that the picture is mapped to the product + // This is needed since if you delete the product picture mapping from the nopCommerce administrationthe + // then the attribute value is not updated and it will point to a picture that has been deleted + var productPicture = + product.ProductPictures.FirstOrDefault(pp => pp.PictureId == productAttributeValue.PictureId); + if (productPicture != null) + { + productAttributeValueDto.ProductPictureId = productPicture.Id; + } + } + } + + return productAttributeValueDto; + } + + private void PrepareProductAttributeCombinations(IEnumerable productAttributeCombinations, + ProductDto productDto) + { + productDto.ProductAttributeCombinations = productDto.ProductAttributeCombinations ?? new List(); + + foreach (var productAttributeCombination in productAttributeCombinations) + { + var productAttributeCombinationDto = PrepareProductAttributeCombinationDto(productAttributeCombination); + if (productAttributeCombinationDto != null) + { + productDto.ProductAttributeCombinations.Add(productAttributeCombinationDto); + } + } + } + + private ProductAttributeCombinationDto PrepareProductAttributeCombinationDto(ProductAttributeCombination productAttributeCombination) + { + return productAttributeCombination.ToDto(); + } + + public void PrepareProductSpecificationAttributes(IEnumerable productSpecificationAttributes, ProductDto productDto) + { + if (productDto.ProductSpecificationAttributes == null) + productDto.ProductSpecificationAttributes = new List(); + + foreach (var productSpecificationAttribute in productSpecificationAttributes) + { + ProductSpecificationAttributeDto productSpecificationAttributeDto = PrepareProductSpecificationAttributeDto(productSpecificationAttribute); + + if (productSpecificationAttributeDto != null) + { + productDto.ProductSpecificationAttributes.Add(productSpecificationAttributeDto); + } + } + } + + public ProductSpecificationAttributeDto PrepareProductSpecificationAttributeDto(ProductSpecificationAttribute productSpecificationAttribute) + { + return productSpecificationAttribute.ToDto(); + } + + public SpecificationAttributeDto PrepareSpecificationAttributeDto(SpecificationAttribute specificationAttribute) + { + return specificationAttribute.ToDto(); + } + + public ManufacturerDto PrepareManufacturerDto(Manufacturer manufacturer) + { + var manufacturerDto = manufacturer.ToDto(); + + var picture = _pictureService.GetPictureById(manufacturer.PictureId); + var imageDto = PrepareImageDto(picture); + + if (imageDto != null) + { + manufacturerDto.Image = imageDto; + } + + manufacturerDto.SeName = _urlRecordService.GetSeName(manufacturer); + manufacturerDto.DiscountIds = manufacturer.AppliedDiscounts.Select(discount => discount.Id).ToList(); + manufacturerDto.RoleIds = _aclService.GetAclRecords(manufacturer).Select(acl => acl.CustomerRoleId).ToList(); + manufacturerDto.StoreIds = _storeMappingService.GetStoreMappings(manufacturer).Select(mapping => mapping.StoreId) + .ToList(); + + var allLanguages = _languageService.GetAllLanguages(); + + manufacturerDto.LocalizedNames = new List(); + + foreach (var language in allLanguages) + { + var localizedNameDto = new LocalizedNameDto + { + LanguageId = language.Id, + LocalizedName = _localizationService.GetLocalized(manufacturer, x => x.Name, language.Id) + }; + + manufacturerDto.LocalizedNames.Add(localizedNameDto); + } + + return manufacturerDto; + } + } +} \ No newline at end of file diff --git a/Nop.Plugin.Api/Helpers/IDTOHelper.cs b/Nop.Plugin.Api/Helpers/IDTOHelper.cs index a460c17..9f89596 100644 --- a/Nop.Plugin.Api/Helpers/IDTOHelper.cs +++ b/Nop.Plugin.Api/Helpers/IDTOHelper.cs @@ -1,30 +1,32 @@ -using Nop.Core.Domain.Catalog; -using Nop.Core.Domain.Localization; -using Nop.Core.Domain.Orders; -using Nop.Core.Domain.Stores; -using Nop.Plugin.Api.DTOs.Categories; -using Nop.Plugin.Api.DTOs.Languages; -using Nop.Plugin.Api.DTOs.OrderItems; -using Nop.Plugin.Api.DTOs.Orders; -using Nop.Plugin.Api.DTOs.ProductAttributes; -using Nop.Plugin.Api.DTOs.Products; -using Nop.Plugin.Api.DTOs.ShoppingCarts; -using Nop.Plugin.Api.DTOs.SpecificationAttributes; -using Nop.Plugin.Api.DTOs.Stores; - -namespace Nop.Plugin.Api.Helpers -{ - public interface IDTOHelper - { - ProductDto PrepareProductDTO(Product product); - CategoryDto PrepareCategoryDTO(Category category); - OrderDto PrepareOrderDTO(Order order); - ShoppingCartItemDto PrepareShoppingCartItemDTO(ShoppingCartItem shoppingCartItem); - OrderItemDto PrepareOrderItemDTO(OrderItem orderItem); - StoreDto PrepareStoreDTO(Store store); - LanguageDto PrepateLanguageDto(Language language); - ProductAttributeDto PrepareProductAttributeDTO(ProductAttribute productAttribute); - ProductSpecificationAttributeDto PrepareProductSpecificationAttributeDto(ProductSpecificationAttribute productSpecificationAttribute); - SpecificationAttributeDto PrepareSpecificationAttributeDto(SpecificationAttribute specificationAttribute); - } +using Nop.Core.Domain.Catalog; +using Nop.Core.Domain.Localization; +using Nop.Core.Domain.Orders; +using Nop.Core.Domain.Stores; +using Nop.Plugin.Api.DTOs.Categories; +using Nop.Plugin.Api.DTOs.Languages; +using Nop.Plugin.Api.DTOs.Manufacturers; +using Nop.Plugin.Api.DTOs.OrderItems; +using Nop.Plugin.Api.DTOs.Orders; +using Nop.Plugin.Api.DTOs.ProductAttributes; +using Nop.Plugin.Api.DTOs.Products; +using Nop.Plugin.Api.DTOs.ShoppingCarts; +using Nop.Plugin.Api.DTOs.SpecificationAttributes; +using Nop.Plugin.Api.DTOs.Stores; + +namespace Nop.Plugin.Api.Helpers +{ + public interface IDTOHelper + { + ProductDto PrepareProductDTO(Product product); + CategoryDto PrepareCategoryDTO(Category category); + OrderDto PrepareOrderDTO(Order order); + ShoppingCartItemDto PrepareShoppingCartItemDTO(ShoppingCartItem shoppingCartItem); + OrderItemDto PrepareOrderItemDTO(OrderItem orderItem); + StoreDto PrepareStoreDTO(Store store); + LanguageDto PrepateLanguageDto(Language language); + ProductAttributeDto PrepareProductAttributeDTO(ProductAttribute productAttribute); + ProductSpecificationAttributeDto PrepareProductSpecificationAttributeDto(ProductSpecificationAttribute productSpecificationAttribute); + SpecificationAttributeDto PrepareSpecificationAttributeDto(SpecificationAttribute specificationAttribute); + ManufacturerDto PrepareManufacturerDto(Manufacturer manufacturer); + } } \ No newline at end of file diff --git a/Nop.Plugin.Api/Helpers/IJsonHelper.cs b/Nop.Plugin.Api/Helpers/IJsonHelper.cs index 3da6b29..256dae1 100644 --- a/Nop.Plugin.Api/Helpers/IJsonHelper.cs +++ b/Nop.Plugin.Api/Helpers/IJsonHelper.cs @@ -1,9 +1,12 @@ -using System.Collections.Generic; +using Microsoft.AspNetCore.Http; +using System.Collections.Generic; +using System.IO; namespace Nop.Plugin.Api.Helpers { public interface IJsonHelper { - Dictionary DeserializeToDictionary(string json); + Dictionary GetRequestJsonDictionaryFromStream(Stream stream, bool rewindStream); + string GetRootPropertyName() where T : class, new(); } } \ No newline at end of file diff --git a/Nop.Plugin.Api/Helpers/JTokenHelper.cs b/Nop.Plugin.Api/Helpers/JTokenHelper.cs index 7b826a8..cf96693 100644 --- a/Nop.Plugin.Api/Helpers/JTokenHelper.cs +++ b/Nop.Plugin.Api/Helpers/JTokenHelper.cs @@ -1,71 +1,71 @@ -namespace Nop.Plugin.Api.Helpers -{ - using System.Collections.Generic; - using Newtonsoft.Json.Linq; - - public static class JTokenHelper - { - public static JToken RemoveEmptyChildrenAndFilterByFields(this JToken token, IList jsonFields, int level = 1) - { - if (token.Type == JTokenType.Object) - { - var copy = new JObject(); - - foreach (JProperty prop in token.Children()) - { - JToken child = prop.Value; - - if (child.HasValues) - { - child = child.RemoveEmptyChildrenAndFilterByFields(jsonFields, level + 1); - } - - // In the current json structure, the first level of properties is level 3. - // If the level is > 3 ( meaning we are not on the first level of properties ), we should not check if the current field is containing into the list with fields, - // so we need to serialize it always. - bool allowedFields = jsonFields.Contains(prop.Name.ToLowerInvariant()) || level > 3; - - // If the level == 3 ( meaning we are on the first level of properties ), we should not take into account if the current field is values, - // so we need to serialize it always. - bool notEmpty = !child.IsEmptyOrDefault() || level == 1 || level == 3; - - if (notEmpty && allowedFields) - { - copy.Add(prop.Name, child); - } - } - - return copy; - } - - if (token.Type == JTokenType.Array) - { - var copy = new JArray(); - - foreach (JToken item in token.Children()) - { - JToken child = item; - - if (child.HasValues) - { - child = child.RemoveEmptyChildrenAndFilterByFields(jsonFields, level + 1); - } - - if (!child.IsEmptyOrDefault()) - { - copy.Add(child); - } - } - - return copy; - } - - return token; - } - - private static bool IsEmptyOrDefault(this JToken token) - { - return (token.Type == JTokenType.Array && !token.HasValues) || (token.Type == JTokenType.Object && !token.HasValues); - } - } +namespace Nop.Plugin.Api.Helpers +{ + using System.Collections.Generic; + using Newtonsoft.Json.Linq; + + public static class JTokenHelper + { + public static JToken RemoveEmptyChildrenAndFilterByFields(this JToken token, IList jsonFields, int level = 1) + { + if (token.Type == JTokenType.Object) + { + var copy = new JObject(); + + foreach (var prop in token.Children()) + { + var child = prop.Value; + + if (child.HasValues) + { + child = child.RemoveEmptyChildrenAndFilterByFields(jsonFields, level + 1); + } + + // In the current json structure, the first level of properties is level 3. + // If the level is > 3 ( meaning we are not on the first level of properties ), we should not check if the current field is containing into the list with fields, + // so we need to serialize it always. + var allowedFields = jsonFields.Contains(prop.Name.ToLowerInvariant()) || level > 3; + + // If the level == 3 ( meaning we are on the first level of properties ), we should not take into account if the current field is values, + // so we need to serialize it always. + var notEmpty = !child.IsEmptyOrDefault() || level == 1 || level == 3; + + if (notEmpty && allowedFields) + { + copy.Add(prop.Name, child); + } + } + + return copy; + } + + if (token.Type == JTokenType.Array) + { + var copy = new JArray(); + + foreach (var item in token.Children()) + { + var child = item; + + if (child.HasValues) + { + child = child.RemoveEmptyChildrenAndFilterByFields(jsonFields, level + 1); + } + + if (!child.IsEmptyOrDefault()) + { + copy.Add(child); + } + } + + return copy; + } + + return token; + } + + private static bool IsEmptyOrDefault(this JToken token) + { + return (token.Type == JTokenType.Array && !token.HasValues) || (token.Type == JTokenType.Object && !token.HasValues); + } + } } \ No newline at end of file diff --git a/Nop.Plugin.Api/Helpers/JsonHelper.cs b/Nop.Plugin.Api/Helpers/JsonHelper.cs index d9e85c7..1fac1cb 100644 --- a/Nop.Plugin.Api/Helpers/JsonHelper.cs +++ b/Nop.Plugin.Api/Helpers/JsonHelper.cs @@ -1,14 +1,80 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; +using System.Text; +using Microsoft.AspNetCore.Http; using Newtonsoft.Json.Linq; +using Nop.Services.Localization; namespace Nop.Plugin.Api.Helpers { - // source - http://stackoverflow.com/questions/5546142/how-do-i-use-json-net-to-deserialize-into-nested-recursive-dictionary-and-list public class JsonHelper : IJsonHelper { - public Dictionary DeserializeToDictionary(string json) + + #region Private Fields + + private readonly ILocalizationService _localizationService; + + private readonly int _languageId; + + #endregion + + #region Constructors + + public JsonHelper(ILanguageService languageService, ILocalizationService localizationService) + { + _localizationService = localizationService; + + var language = languageService.GetAllLanguages().FirstOrDefault(); + _languageId = language != null ? language.Id : 0; + } + + #endregion + + #region Public Methods + + public Dictionary GetRequestJsonDictionaryFromStream(Stream stream, bool rewindStream) + { + var json = GetRequestBodyString(stream, rewindStream); + if (string.IsNullOrEmpty(json)) + { + throw new InvalidOperationException(_localizationService.GetResource("Api.NoJsonProvided", _languageId, false)); + } + + var requestBodyDictioanry = DeserializeToDictionary(json); + if (requestBodyDictioanry == null || requestBodyDictioanry.Count == 0) + { + throw new InvalidOperationException(_localizationService.GetResource("Api.InvalidJsonFormat", _languageId, false)); + } + + return requestBodyDictioanry; + } + + public string GetRootPropertyName() where T : class, new() + { + var rootProperty = ""; + + var jsonObjectAttribute = ReflectionHelper.GetJsonObjectAttribute(typeof(T)); + if (jsonObjectAttribute != null) + { + rootProperty = jsonObjectAttribute.Title; + } + + if (string.IsNullOrEmpty(rootProperty)) + { + throw new InvalidOperationException($"Error getting root property for type {typeof(T).FullName}."); + } + + return rootProperty; + } + + #endregion + + #region Private Methods + + // source - http://stackoverflow.com/questions/5546142/how-do-i-use-json-net-to-deserialize-into-nested-recursive-dictionary-and-list + private Dictionary DeserializeToDictionary(string json) { //TODO: JToken.Parse could throw an exeption if not valid JSON string is passed try @@ -18,9 +84,10 @@ public Dictionary DeserializeToDictionary(string json) catch (Exception) { return null; - } + } } + // source - http://stackoverflow.com/questions/5546142/how-do-i-use-json-net-to-deserialize-into-nested-recursive-dictionary-and-list private object ToObject(JToken token) { switch (token.Type) @@ -37,5 +104,24 @@ private object ToObject(JToken token) return ((JValue)token).Value; } } + + private string GetRequestBodyString(Stream stream, bool rewindStream) + { + var result = ""; + + using (var streamReader = new StreamReader(stream, Encoding.UTF8, true, 1024, rewindStream)) + { + result = streamReader.ReadToEnd(); + if (rewindStream) + { + stream.Position = 0; //reset position to allow reading again later + } + } + + return result; + } + + #endregion + } } \ No newline at end of file diff --git a/Nop.Plugin.Api/Helpers/MappingHelper.cs b/Nop.Plugin.Api/Helpers/MappingHelper.cs index 5e0b646..54aa952 100644 --- a/Nop.Plugin.Api/Helpers/MappingHelper.cs +++ b/Nop.Plugin.Api/Helpers/MappingHelper.cs @@ -1,238 +1,261 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.ComponentModel; -using System.Linq; -using System.Reflection; -using Nop.Core.Infrastructure; -using Nop.Plugin.Api.Factories; - -namespace Nop.Plugin.Api.Helpers -{ - // TODO: Think of moving the mapping helper in the delta folder - public class MappingHelper : IMappingHelper - { - public void Merge(object source, object destination) - { - var sourcePropertyValuePairs = source.GetType() - .GetProperties() - .ToDictionary(property => property.Name, property => property.GetValue(source)); - - SetValues(sourcePropertyValuePairs, destination, destination.GetType(),null); - } - - public void SetValues(Dictionary propertyNameValuePairs, object objectToBeUpdated, - Type propertyType, Dictionary objectPropertyNameValuePairs, bool handleComplexTypeCollections = false) - { - objectPropertyNameValuePairs?.Add(objectToBeUpdated, propertyNameValuePairs); - - foreach (var propertyNameValuePair in propertyNameValuePairs) - { - SetValue(objectToBeUpdated, propertyNameValuePair, objectPropertyNameValuePairs, handleComplexTypeCollections); - } - } - - // Used in the SetValue private method and also in the Delta. - private void ConvertAndSetValueIfValid(object objectToBeUpdated, PropertyInfo objectProperty, object propertyValue) - { - TypeConverter converter = TypeDescriptor.GetConverter(objectProperty.PropertyType); - - string propertyValueAsString = string.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}", propertyValue); - - if (converter.IsValid(propertyValueAsString)) - { - var convertedValue = converter.ConvertFromInvariantString(propertyValueAsString); - - objectProperty.SetValue(objectToBeUpdated, convertedValue); - } - } - - private void SetValue(object objectToBeUpdated, KeyValuePair propertyNameValuePair, Dictionary objectPropertyNameValuePairs, bool handleComplexTypeCollections) - { - string propertyName = propertyNameValuePair.Key; - object propertyValue = propertyNameValuePair.Value; - - PropertyInfo propertyToUpdate = objectToBeUpdated.GetType().GetProperty(propertyName, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance); - - if (propertyToUpdate != null) - { - // This case handles nested properties. - if (propertyValue != null && propertyValue is Dictionary) - { - var valueToUpdate = propertyToUpdate.GetValue(objectToBeUpdated); - - if (valueToUpdate == null) - { - // Check if there is registered factory for this type. - Type factoryType = typeof(IFactory<>); - var factoryTypeForCurrentProperty = factoryType.MakeGenericType(new Type[] { propertyToUpdate.PropertyType }); - var initializerFactory = ((NopEngine)EngineContext.Current).ServiceProvider.GetService(factoryTypeForCurrentProperty); - - if (initializerFactory != null) - { - var initializeMethod = factoryTypeForCurrentProperty.GetMethod("Initialize"); - - valueToUpdate = initializeMethod.Invoke(initializerFactory, null); - } - else - { - valueToUpdate = Activator.CreateInstance(propertyToUpdate.PropertyType); - } - - propertyToUpdate.SetValue(objectToBeUpdated, valueToUpdate); - } - - // We need to use GetValue method to get the actual instance of the jsonProperty. objectProperty is the jsonProperty info. - SetValues((Dictionary)propertyValue, valueToUpdate, - propertyToUpdate.PropertyType, objectPropertyNameValuePairs); - // We expect the nested properties to be classes which are refrence types. - return; - } - // This case hadles collections. - else if (propertyValue != null && propertyValue is ICollection) - { - ICollection propertyValueAsCollection = propertyValue as ICollection; - - Type collectionElementsType = propertyToUpdate.PropertyType.GetGenericArguments()[0]; - var collection = propertyToUpdate.GetValue(objectToBeUpdated); - - if (collection == null) - { - var listType = typeof(List<>); - var constructedListType = listType.MakeGenericType(collectionElementsType); - collection = Activator.CreateInstance(constructedListType); - propertyToUpdate.SetValue(objectToBeUpdated, collection); - } - - foreach (var item in propertyValueAsCollection) - { - if (collectionElementsType.Namespace != "System") - { - if (handleComplexTypeCollections) - { - AddOrUpdateComplexItemInCollection(item as Dictionary, - collection as IList, - collectionElementsType, objectPropertyNameValuePairs, handleComplexTypeCollections); - } - } - else - { - AddBaseItemInCollection(item, collection as IList, collectionElementsType); - } - } - - return; - } - - // This is where the new value is beeing set to the object jsonProperty using the SetValue function part of System.Reflection. - if (propertyValue == null) - { - propertyToUpdate.SetValue(objectToBeUpdated, null); - } - else if (propertyValue is IConvertible) - { - ConvertAndSetValueIfValid(objectToBeUpdated, propertyToUpdate, propertyValue); - // otherwise ignore the passed value. - } - else - { - propertyToUpdate.SetValue(objectToBeUpdated, propertyValue); - } - } - } - - private void AddBaseItemInCollection(object newItem, IList collection, Type collectionElementsType) - { - TypeConverter converter = TypeDescriptor.GetConverter(collectionElementsType); - - var newItemValueToString = newItem.ToString(); - - if (converter.IsValid(newItemValueToString)) - { - collection.Add(converter.ConvertFrom(newItemValueToString)); - } - } - - private void AddOrUpdateComplexItemInCollection(Dictionary newProperties, IList collection, Type collectionElementsType, - Dictionary objectPropertyNameValuePairs, bool handleComplexTypeCollections) - { - if (newProperties.ContainsKey("Id")) - { - // Every element in collection, that is not System type should have an id. - int id = int.Parse(newProperties["Id"].ToString()); - - object itemToBeUpdated = null; - - // Check if there is already an item with this id in the collection. - foreach (var item in collection) - { - if (int.Parse(item.GetType() - .GetProperty("Id", BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance) - .GetValue(item) - .ToString()) == id) - { - itemToBeUpdated = item; - break; - } - } - - if (itemToBeUpdated == null) - { - // We should create a new item and put it in the collection. - AddNewItemInCollection(newProperties, collection, collectionElementsType, objectPropertyNameValuePairs, handleComplexTypeCollections); - } - else - { - // We should update the existing element. - SetValues(newProperties, itemToBeUpdated, collectionElementsType, objectPropertyNameValuePairs, handleComplexTypeCollections); - } - } - // It is a new item. - else - { - AddNewItemInCollection(newProperties, collection, collectionElementsType, objectPropertyNameValuePairs, handleComplexTypeCollections); - } - } - - private void AddNewItemInCollection(Dictionary newProperties, IList collection, Type collectionElementsType,Dictionary objectPropertyNameValuePairs, bool handleComplexTypeCollections) - { - var newInstance = Activator.CreateInstance(collectionElementsType); - - var properties = collectionElementsType.GetProperties(); - - SetEveryDatePropertyThatIsNotSetToDateTimeUtcNow(newProperties, properties); - - SetValues(newProperties, newInstance, collectionElementsType, objectPropertyNameValuePairs, handleComplexTypeCollections); - - collection.Add(newInstance); - } - - // We need this method, because the default value of DateTime is not in the sql server DateTime range and we will get an exception if we use it. - private void SetEveryDatePropertyThatIsNotSetToDateTimeUtcNow(Dictionary newProperties, PropertyInfo[] properties) - { - foreach (var property in properties) - { - if (property.PropertyType == typeof(DateTime)) - { - bool keyFound = false; - - // We need to loop through the keys, because the key may contain underscores in its name, which won't match the jsonProperty name. - foreach (var key in newProperties.Keys) - { - if (key.Equals(property.Name, StringComparison.InvariantCultureIgnoreCase)) - { - keyFound = true; - break; - } - } - - if (!keyFound) - { - // Create the item with the DateTime.NowUtc. - newProperties.Add(property.Name, DateTime.UtcNow); - } - } - } - } - } +using System; +using System.Collections; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Reflection; +using Nop.Core.Infrastructure; +using Nop.Plugin.Api.Factories; + +namespace Nop.Plugin.Api.Helpers +{ + // TODO: Think of moving the mapping helper in the delta folder + public class MappingHelper : IMappingHelper + { + public void Merge(object source, object destination) + { + var sourcePropertyValuePairs = source.GetType() + .GetProperties() + .ToDictionary(property => property.Name, property => property.GetValue(source)); + + SetValues(sourcePropertyValuePairs, destination, destination.GetType(),null); + } + + public void SetValues(Dictionary propertyNameValuePairs, object objectToBeUpdated, + Type propertyType, Dictionary objectPropertyNameValuePairs, bool handleComplexTypeCollections = false) + { + objectPropertyNameValuePairs?.Add(objectToBeUpdated, propertyNameValuePairs); + + foreach (var propertyNameValuePair in propertyNameValuePairs) + { + SetValue(objectToBeUpdated, propertyNameValuePair, objectPropertyNameValuePairs, handleComplexTypeCollections); + } + } + + // Used in the SetValue private method and also in the Delta. + private void ConvertAndSetValueIfValid(object objectToBeUpdated, PropertyInfo objectProperty, object propertyValue) + { + var converter = TypeDescriptor.GetConverter(objectProperty.PropertyType); + + var propertyValueAsString = string.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}", propertyValue); + + if (converter.IsValid(propertyValueAsString)) + { + var convertedValue = converter.ConvertFromInvariantString(propertyValueAsString); + + objectProperty.SetValue(objectToBeUpdated, convertedValue); + } + } + + private void SetValue(object objectToBeUpdated, KeyValuePair propertyNameValuePair, Dictionary objectPropertyNameValuePairs, bool handleComplexTypeCollections) + { + var propertyName = propertyNameValuePair.Key; + var propertyValue = propertyNameValuePair.Value; + + var propertyToUpdate = objectToBeUpdated.GetType().GetProperty(propertyName, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance); + + if (propertyToUpdate != null) + { + // This case handles nested properties. + if (propertyValue != null && propertyValue is Dictionary) + { + var valueToUpdate = propertyToUpdate.GetValue(objectToBeUpdated); + + if (valueToUpdate == null) + { + // Check if there is registered factory for this type. + var factoryType = typeof(IFactory<>); + var factoryTypeForCurrentProperty = factoryType.MakeGenericType(new Type[] { propertyToUpdate.PropertyType }); + var initializerFactory = ((NopEngine)EngineContext.Current).ServiceProvider.GetService(factoryTypeForCurrentProperty); + + if (initializerFactory != null) + { + var initializeMethod = factoryTypeForCurrentProperty.GetMethod("Initialize"); + + valueToUpdate = initializeMethod.Invoke(initializerFactory, null); + } + else + { + valueToUpdate = Activator.CreateInstance(propertyToUpdate.PropertyType); + } + + propertyToUpdate.SetValue(objectToBeUpdated, valueToUpdate); + } + + // We need to use GetValue method to get the actual instance of the jsonProperty. objectProperty is the jsonProperty info. + SetValues((Dictionary)propertyValue, valueToUpdate, + propertyToUpdate.PropertyType, objectPropertyNameValuePairs); + // We expect the nested properties to be classes which are refrence types. + return; + } + // This case hadles collections. + else if (propertyValue != null && propertyValue is ICollection) + { + var propertyValueAsCollection = propertyValue as ICollection; + + var collectionElementsType = propertyToUpdate.PropertyType.GetGenericArguments()[0]; + var collection = propertyToUpdate.GetValue(objectToBeUpdated); + + if (collection == null) + { + collection = CreateEmptyList(collectionElementsType); + propertyToUpdate.SetValue(objectToBeUpdated, collection); + } + + //this is a hack to fix a bug when "collection" cannot be cast to IList (ex: it's a HashSet for Order.OrderItems) + var collectionAsList = collection as IList; + if (collectionAsList == null) + { + collectionAsList = CreateEmptyList(collectionElementsType); + + var collectionAsEnumerable = collection as IEnumerable; + foreach (var collectionItem in collectionAsEnumerable) + { + collectionAsList.Add(collectionItem); + } + + collection = collectionAsList; + propertyToUpdate.SetValue(objectToBeUpdated, collection); + } + + foreach (var item in propertyValueAsCollection) + { + if (collectionElementsType.Namespace != "System") + { + if (handleComplexTypeCollections) + { + AddOrUpdateComplexItemInCollection(item as Dictionary, + collection as IList, + collectionElementsType, objectPropertyNameValuePairs, handleComplexTypeCollections); + } + } + else + { + AddBaseItemInCollection(item, collection as IList, collectionElementsType); + } + } + + return; + } + + // This is where the new value is beeing set to the object jsonProperty using the SetValue function part of System.Reflection. + if (propertyValue == null) + { + propertyToUpdate.SetValue(objectToBeUpdated, null); + } + else if (propertyValue is IConvertible) + { + ConvertAndSetValueIfValid(objectToBeUpdated, propertyToUpdate, propertyValue); + // otherwise ignore the passed value. + } + else + { + propertyToUpdate.SetValue(objectToBeUpdated, propertyValue); + } + } + } + + private void AddBaseItemInCollection(object newItem, IList collection, Type collectionElementsType) + { + var converter = TypeDescriptor.GetConverter(collectionElementsType); + + var newItemValueToString = newItem.ToString(); + + if (converter.IsValid(newItemValueToString)) + { + collection.Add(converter.ConvertFrom(newItemValueToString)); + } + } + + private void AddOrUpdateComplexItemInCollection(Dictionary newProperties, IList collection, Type collectionElementsType, + Dictionary objectPropertyNameValuePairs, bool handleComplexTypeCollections) + { + if (newProperties.ContainsKey("Id")) + { + // Every element in collection, that is not System type should have an id. + var id = int.Parse(newProperties["Id"].ToString()); + + object itemToBeUpdated = null; + + // Check if there is already an item with this id in the collection. + foreach (var item in collection) + { + if (int.Parse(item.GetType() + .GetProperty("Id", BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance) + .GetValue(item) + .ToString()) == id) + { + itemToBeUpdated = item; + break; + } + } + + if (itemToBeUpdated == null) + { + // We should create a new item and put it in the collection. + AddNewItemInCollection(newProperties, collection, collectionElementsType, objectPropertyNameValuePairs, handleComplexTypeCollections); + } + else + { + // We should update the existing element. + SetValues(newProperties, itemToBeUpdated, collectionElementsType, objectPropertyNameValuePairs, handleComplexTypeCollections); + } + } + // It is a new item. + else + { + AddNewItemInCollection(newProperties, collection, collectionElementsType, objectPropertyNameValuePairs, handleComplexTypeCollections); + } + } + + private void AddNewItemInCollection(Dictionary newProperties, IList collection, Type collectionElementsType,Dictionary objectPropertyNameValuePairs, bool handleComplexTypeCollections) + { + var newInstance = Activator.CreateInstance(collectionElementsType); + + var properties = collectionElementsType.GetProperties(); + + SetEveryDatePropertyThatIsNotSetToDateTimeUtcNow(newProperties, properties); + + SetValues(newProperties, newInstance, collectionElementsType, objectPropertyNameValuePairs, handleComplexTypeCollections); + + collection.Add(newInstance); + } + + private IList CreateEmptyList(Type listItemType) + { + var listType = typeof(List<>); + var constructedListType = listType.MakeGenericType(listItemType); + var list = Activator.CreateInstance(constructedListType); + + return list as IList; + } + + // We need this method, because the default value of DateTime is not in the sql server DateTime range and we will get an exception if we use it. + private void SetEveryDatePropertyThatIsNotSetToDateTimeUtcNow(Dictionary newProperties, PropertyInfo[] properties) + { + foreach (var property in properties) + { + if (property.PropertyType == typeof(DateTime)) + { + var keyFound = false; + + // We need to loop through the keys, because the key may contain underscores in its name, which won't match the jsonProperty name. + foreach (var key in newProperties.Keys) + { + if (key.Equals(property.Name, StringComparison.InvariantCultureIgnoreCase)) + { + keyFound = true; + break; + } + } + + if (!keyFound) + { + // Create the item with the DateTime.NowUtc. + newProperties.Add(property.Name, DateTime.UtcNow); + } + } + } + } + } } \ No newline at end of file diff --git "a/Nop.Plugin.Api/Helpers/NopConfigMan\320\260gerHelper.cs" "b/Nop.Plugin.Api/Helpers/NopConfigMan\320\260gerHelper.cs" index bd6a5d5..68e1fc4 100644 --- "a/Nop.Plugin.Api/Helpers/NopConfigMan\320\260gerHelper.cs" +++ "b/Nop.Plugin.Api/Helpers/NopConfigMan\320\260gerHelper.cs" @@ -1,191 +1,189 @@ -using System.Xml.Linq; -using Nop.Core; -using Nop.Core.Data; - -namespace Nop.Plugin.Api.Helpers -{ - using System; - using System.Collections.Generic; - using System.Globalization; - using System.Reflection; - using System.Xml; - using System.Xml.XPath; - - public class NopConfigManagerHelper : IConfigManagerHelper - { - public NopConfigManagerHelper(DataSettings dataSettings) - { - DataSettings = dataSettings; - } - - public NopConfigManagerHelper() - { - var dataSettingsManager = new DataSettingsManager(); - DataSettings = dataSettingsManager.LoadSettings(); - } - - public void AddBindingRedirects() - { - bool hasChanged = false; - - // load Nop.Web.exe.config - XDocument appConfig = null; - - string nopWebAssemblyConfigLocation = $"{Assembly.GetEntryAssembly().Location}.config"; - - using (var fs = System.IO.File.OpenRead(nopWebAssemblyConfigLocation)) - { - appConfig = XDocument.Load(fs); - } - - if (appConfig != null) - { - appConfig.Changed += (o, e) => { hasChanged = true; }; - - var runtime = appConfig.XPathSelectElement("configuration//runtime"); - - if (runtime == null) - { - runtime = new XElement("runtime"); - appConfig.XPathSelectElement("configuration")?.Add(runtime); - } - - // Required by Swagger - //AddAssemblyBinding(runtime, "Microsoft.AspNetCore.StaticFiles", "adb9793829ddae60", "0.0.0.0-2.0.0.0", "2.0.0.0"); - //AddAssemblyBinding(runtime, "Microsoft.Extensions.FileProviders.Embedded", "adb9793829ddae60", "0.0.0.0-2.0.0.0", "2.0.0.0"); - //AddAssemblyBinding(runtime, "Microsoft.AspNetCore.Mvc.Formatters.Json", "adb9793829ddae60", "0.0.0.0-2.0.0.0", "2.0.0.0"); - - // Required by WebHooks - AddAssemblyBinding(runtime, "Microsoft.AspNetCore.DataProtection.Abstractions", "adb9793829ddae60", "0.0.0.0-2.0.0.0", "2.0.0.0"); - - if (hasChanged) - { - // only save when changes have been made - try - { - appConfig.Save(nopWebAssemblyConfigLocation); - - System.Configuration.ConfigurationManager.RefreshSection("runtime"); - } - catch (Exception ex) - { - // we should do nothing here as throwing an exception breaks nopCommerce. - // The right thing to do is to write a message in the Log that the user needs to provide Write access to Web.config - // but doing this will lead to many warnings in the Log added after each restart. - // So it is better to do nothing here. - //throw new NopException( - // "nopCommerce needs to be restarted due to a configuration change, but was unable to do so." + - // Environment.NewLine + - // "To prevent this issue in the future, a change to the web server configuration is required:" + - // Environment.NewLine + - // "- give the application write access to the 'web.config' file."); - } - } - } - } - - public DataSettings DataSettings { get; } - - public void AddConnectionString() - { - bool hasChanged = false; - - // load web.config - XDocument appConfig = null; - - string nopWebAssemblyConfigLocation = $"{Assembly.GetEntryAssembly().Location}.config"; - - using (var fs = System.IO.File.OpenRead(nopWebAssemblyConfigLocation)) - { - appConfig = XDocument.Load(fs); - } - - if (appConfig != null) - { - appConfig.Changed += (o, e) => { hasChanged = true; }; - - var connectionStrings = appConfig.XPathSelectElement("configuration//connectionStrings"); - - if (connectionStrings == null) - { - var configuration = appConfig.XPathSelectElement("configuration"); - connectionStrings = new XElement("connectionStrings"); - configuration.Add(connectionStrings); - } - - string connectionStringFromNop = DataSettings.DataConnectionString; - - var element = appConfig.XPathSelectElement("configuration//connectionStrings//add[@name='MS_SqlStoreConnectionString']"); - - // create the connection string if not exists - if (element == null) - { - element = new XElement("add"); - element.SetAttributeValue("name", "MS_SqlStoreConnectionString"); - element.SetAttributeValue("connectionString", connectionStringFromNop); - element.SetAttributeValue("providerName", "System.Data.SqlClient"); - connectionStrings.Add(element); - } - else - { - // Check if the connection string is changed. - // If so update the connection string in the config. - string connectionStringInConfig = element.Attribute("connectionString").Value; - - if (!String.Equals(connectionStringFromNop, connectionStringInConfig, StringComparison.InvariantCultureIgnoreCase)) - { - element.SetAttributeValue("connectionString", connectionStringFromNop); - } - } - - if (hasChanged) - { - // only save when changes have been made - try - { - appConfig.Save(nopWebAssemblyConfigLocation); - - System.Configuration.ConfigurationManager.RefreshSection("connectionStrings"); - } - catch - { - // we should do nothing here as throwing an exception breaks nopCommerce. - // The right thing to do is to write a message in the Log that the user needs to provide Write access to Web.config - // but doing this will lead to many warnings in the Log added after each restart. - // So it is better to do nothing here. - //throw new NopException( - // "nopCommerce needs to be restarted due to a configuration change, but was unable to do so." + - // Environment.NewLine + - // "To prevent this issue in the future, a change to the web server configuration is required:" + - // Environment.NewLine + - // "- give the application write access to the 'web.config' file."); - } - } - } - } - - private void AddAssemblyBinding(XElement runtime, string name, string publicToken, string oldVersion, string newVersion) - { - XmlNamespaceManager xmlNamespaceManager = new XmlNamespaceManager(new NameTable()); - xmlNamespaceManager.AddNamespace("bind", "urn:schemas-microsoft-com:asm.v1"); - - XElement assemblyBindingElement = runtime.XPathSelectElement( - $"bind:assemblyBinding//bind:dependentAssembly//bind:assemblyIdentity[@name='{name}']", xmlNamespaceManager); - - // create the binding redirect if it does not exist - if (assemblyBindingElement == null) - { - assemblyBindingElement = XElement.Parse($@" - - - - - - - "); - - runtime.Add(assemblyBindingElement); - } - } - } +using System.Xml.Linq; +using Nop.Core.Data; + +namespace Nop.Plugin.Api.Helpers +{ + using System; + using System.Reflection; + using System.Xml; + using System.Xml.XPath; + + public class NopConfigManagerHelper : IConfigManagerHelper + { + public NopConfigManagerHelper(DataSettings dataSettings) + { + DataSettings = dataSettings; + } + + public NopConfigManagerHelper() + { + DataSettings = DataSettingsManager.LoadSettings(); + } + + public void AddBindingRedirects() + { + var hasChanged = false; + + // load Nop.Web.exe.config + XDocument appConfig = null; + + var nopWebAssemblyConfigLocation = $"{Assembly.GetEntryAssembly().Location}.config"; + + using (var fs = System.IO.File.OpenRead(nopWebAssemblyConfigLocation)) + { + appConfig = XDocument.Load(fs); + } + + if (appConfig != null) + { + appConfig.Changed += (o, e) => { hasChanged = true; }; + + var runtime = appConfig.XPathSelectElement("configuration//runtime"); + + if (runtime == null) + { + runtime = new XElement("runtime"); + appConfig.XPathSelectElement("configuration")?.Add(runtime); + } + + // Required by Swagger + //AddAssemblyBinding(runtime, "Microsoft.AspNetCore.StaticFiles", "adb9793829ddae60", "0.0.0.0-2.0.0.0", "2.0.0.0"); + //AddAssemblyBinding(runtime, "Microsoft.Extensions.FileProviders.Embedded", "adb9793829ddae60", "0.0.0.0-2.0.0.0", "2.0.0.0"); + //AddAssemblyBinding(runtime, "Microsoft.AspNetCore.Mvc.Formatters.Json", "adb9793829ddae60", "0.0.0.0-2.0.0.0", "2.0.0.0"); + + // Required by WebHooks + //AddAssemblyBinding(runtime, "Microsoft.AspNetCore.DataProtection.Abstractions", "adb9793829ddae60", "0.0.0.0-2.0.0.0", "2.0.0.0"); + + if (hasChanged) + { + // only save when changes have been made + try + { + appConfig.Save(nopWebAssemblyConfigLocation); + + //TODO: Upgrade 4.10 Check this! + //System.Configuration.ConfigurationManager.RefreshSection("runtime"); + } + catch (Exception) + { + // we should do nothing here as throwing an exception breaks nopCommerce. + // The right thing to do is to write a message in the Log that the user needs to provide Write access to Web.config + // but doing this will lead to many warnings in the Log added after each restart. + // So it is better to do nothing here. + //throw new NopException( + // "nopCommerce needs to be restarted due to a configuration change, but was unable to do so." + + // Environment.NewLine + + // "To prevent this issue in the future, a change to the web server configuration is required:" + + // Environment.NewLine + + // "- give the application write access to the 'web.config' file."); + } + } + } + } + + public DataSettings DataSettings { get; } + + public void AddConnectionString() + { + var hasChanged = false; + + // load web.config + XDocument appConfig = null; + + var nopWebAssemblyConfigLocation = $"{Assembly.GetEntryAssembly().Location}.config"; + + using (var fs = System.IO.File.OpenRead(nopWebAssemblyConfigLocation)) + { + appConfig = XDocument.Load(fs); + } + + if (appConfig != null) + { + appConfig.Changed += (o, e) => { hasChanged = true; }; + + var connectionStrings = appConfig.XPathSelectElement("configuration//connectionStrings"); + + if (connectionStrings == null) + { + var configuration = appConfig.XPathSelectElement("configuration"); + connectionStrings = new XElement("connectionStrings"); + configuration.Add(connectionStrings); + } + + var connectionStringFromNop = DataSettings.DataConnectionString; + + var element = appConfig.XPathSelectElement("configuration//connectionStrings//add[@name='MS_SqlStoreConnectionString']"); + + // create the connection string if not exists + if (element == null) + { + element = new XElement("add"); + element.SetAttributeValue("name", "MS_SqlStoreConnectionString"); + element.SetAttributeValue("connectionString", connectionStringFromNop); + element.SetAttributeValue("providerName", "System.Data.SqlClient"); + connectionStrings.Add(element); + } + else + { + // Check if the connection string is changed. + // If so update the connection string in the config. + var connectionStringInConfig = element.Attribute("connectionString").Value; + + if (!String.Equals(connectionStringFromNop, connectionStringInConfig, StringComparison.InvariantCultureIgnoreCase)) + { + element.SetAttributeValue("connectionString", connectionStringFromNop); + } + } + + if (hasChanged) + { + // only save when changes have been made + try + { + appConfig.Save(nopWebAssemblyConfigLocation); + + //TODO: Upgrade 4.1. Check this! + //System.Configuration.ConfigurationManager.RefreshSection("connectionStrings"); + } + catch + { + // we should do nothing here as throwing an exception breaks nopCommerce. + // The right thing to do is to write a message in the Log that the user needs to provide Write access to Web.config + // but doing this will lead to many warnings in the Log added after each restart. + // So it is better to do nothing here. + //throw new NopException( + // "nopCommerce needs to be restarted due to a configuration change, but was unable to do so." + + // Environment.NewLine + + // "To prevent this issue in the future, a change to the web server configuration is required:" + + // Environment.NewLine + + // "- give the application write access to the 'web.config' file."); + } + } + } + } + + private void AddAssemblyBinding(XElement runtime, string name, string publicToken, string oldVersion, string newVersion) + { + var xmlNamespaceManager = new XmlNamespaceManager(new NameTable()); + xmlNamespaceManager.AddNamespace("bind", "urn:schemas-microsoft-com:asm.v1"); + + var assemblyBindingElement = runtime.XPathSelectElement( + $"bind:assemblyBinding//bind:dependentAssembly//bind:assemblyIdentity[@name='{name}']", xmlNamespaceManager); + + // create the binding redirect if it does not exist + if (assemblyBindingElement == null) + { + assemblyBindingElement = XElement.Parse($@" + + + + + + + "); + + runtime.Add(assemblyBindingElement); + } + } + } } \ No newline at end of file diff --git a/Nop.Plugin.Api/Helpers/ReflectionHelper.cs b/Nop.Plugin.Api/Helpers/ReflectionHelper.cs index 569779a..f593736 100644 --- a/Nop.Plugin.Api/Helpers/ReflectionHelper.cs +++ b/Nop.Plugin.Api/Helpers/ReflectionHelper.cs @@ -1,24 +1,24 @@ -using System; -using System.Reflection; -using Newtonsoft.Json; - -namespace Nop.Plugin.Api.Helpers -{ - public static class ReflectionHelper - { - public static bool HasProperty(string propertyName, Type type) - { - return type.GetProperty(propertyName, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance) != null; - } - - public static JsonObjectAttribute GetJsonObjectAttribute(Type objectType) - { - JsonObjectAttribute jsonObject = objectType.GetCustomAttribute(typeof(JsonObjectAttribute)) as JsonObjectAttribute; - - return jsonObject; - } - - public static Type GetGenericElementType(Type type) - => type.HasElementType ? type.GetElementType() : type.GetTypeInfo().GenericTypeArguments[0]; - } +using System; +using System.Reflection; +using Newtonsoft.Json; + +namespace Nop.Plugin.Api.Helpers +{ + public static class ReflectionHelper + { + public static bool HasProperty(string propertyName, Type type) + { + return type.GetProperty(propertyName, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance) != null; + } + + public static JsonObjectAttribute GetJsonObjectAttribute(Type objectType) + { + var jsonObject = objectType.GetCustomAttribute(typeof(JsonObjectAttribute)) as JsonObjectAttribute; + + return jsonObject; + } + + public static Type GetGenericElementType(Type type) + => type.HasElementType ? type.GetElementType() : type.GetTypeInfo().GenericTypeArguments[0]; + } } \ No newline at end of file diff --git a/Nop.Plugin.Api/IdentityServer/Generators/CustomAuthorizeInteractionResponseGenerator.cs b/Nop.Plugin.Api/IdentityServer/Generators/CustomAuthorizeInteractionResponseGenerator.cs index 2f05e21..34cfb7b 100644 --- a/Nop.Plugin.Api/IdentityServer/Generators/CustomAuthorizeInteractionResponseGenerator.cs +++ b/Nop.Plugin.Api/IdentityServer/Generators/CustomAuthorizeInteractionResponseGenerator.cs @@ -1,111 +1,110 @@ -// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - - -namespace Nop.Plugin.Api.IdentityServer.Generators -{ - using System; - using System.Threading.Tasks; - using IdentityModel; - using IdentityServer4; - using IdentityServer4.Extensions; - using IdentityServer4.Models; - using IdentityServer4.ResponseHandling; - using IdentityServer4.Services; - using IdentityServer4.Validation; - using Microsoft.AspNetCore.Authentication; - using Microsoft.AspNetCore.Http; - using Microsoft.Extensions.Logging; - using IAuthenticationService = Microsoft.AspNetCore.Authentication.IAuthenticationService; - - public class NopApiAuthorizeInteractionResponseGenerator : IAuthorizeInteractionResponseGenerator - { - /// - /// The logger. - /// - protected readonly ILogger Logger; - - /// - /// The consent service. - /// - protected readonly IConsentService Consent; - - /// - /// The profile service. - /// - protected readonly IProfileService Profile; - - /// - /// The clock - /// - protected readonly ISystemClock Clock; - - private readonly IHttpContextAccessor _httpContextAccessor; - - private readonly IAuthenticationService _authenticationService; - - /// - /// Initializes a new instance of the class. - /// - /// The clock. - /// The logger. - /// The consent. - /// The profile. - public NopApiAuthorizeInteractionResponseGenerator( - ISystemClock clock, - ILogger logger, - IConsentService consent, - IProfileService profile, IHttpContextAccessor httpContextAccessor, IAuthenticationService authenticationService) - { - Clock = clock; - Logger = logger; - Consent = consent; - Profile = profile; - _httpContextAccessor = httpContextAccessor; - _authenticationService = authenticationService; - } - - /// - /// Processes the interaction logic. - /// - /// The request. - /// The consent. - /// - public virtual async Task ProcessInteractionAsync(ValidatedAuthorizeRequest request, ConsentResponse consent = null) - { - Logger.LogTrace("ProcessInteractionAsync"); - - if (consent != null && consent.Granted == false && request.Subject.IsAuthenticated() == false) - { - // special case when anonymous user has issued a deny prior to authenticating - Logger.LogInformation("Error: User denied consent"); - return new InteractionResponse - { - Error = OidcConstants.AuthorizeErrors.AccessDenied - }; - } - - var identityServerUser = new IdentityServerUser(request.ClientId) - { - DisplayName = request.Client.ClientName, - AdditionalClaims = request.ClientClaims, - AuthenticationTime = new DateTime?(DateTime.UtcNow) - }; - - request.Subject = identityServerUser.CreatePrincipal(); - - var authenticationProperties = new AuthenticationProperties - { - IsPersistent = true, - IssuedUtc = DateTime.UtcNow - }; - - await _authenticationService.SignInAsync(_httpContextAccessor.HttpContext, - IdentityServerConstants.DefaultCookieAuthenticationScheme, request.Subject, authenticationProperties); - - var result = new InteractionResponse(); - - return result; - } - } +// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. +// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. + + +namespace Nop.Plugin.Api.IdentityServer.Generators +{ + using System; + using System.Threading.Tasks; + using IdentityModel; + using IdentityServer4; + using IdentityServer4.Extensions; + using IdentityServer4.Models; + using IdentityServer4.ResponseHandling; + using IdentityServer4.Services; + using IdentityServer4.Validation; + using Microsoft.AspNetCore.Authentication; + using Microsoft.AspNetCore.Http; + using Microsoft.Extensions.Logging; + using IAuthenticationService = Microsoft.AspNetCore.Authentication.IAuthenticationService; + + public class NopApiAuthorizeInteractionResponseGenerator : IAuthorizeInteractionResponseGenerator + { + /// + /// The logger. + /// + protected readonly ILogger Logger; + + /// + /// The consent service. + /// + protected readonly IConsentService Consent; + + /// + /// The profile service. + /// + protected readonly IProfileService Profile; + + /// + /// The clock + /// + protected readonly ISystemClock Clock; + + private readonly IHttpContextAccessor _httpContextAccessor; + + private readonly IAuthenticationService _authenticationService; + + /// + /// Initializes a new instance of the class. + /// + /// The clock. + /// The logger. + /// The consent. + /// The profile. + public NopApiAuthorizeInteractionResponseGenerator( + ISystemClock clock, + ILogger logger, + IConsentService consent, + IProfileService profile, IHttpContextAccessor httpContextAccessor, IAuthenticationService authenticationService) + { + Clock = clock; + Logger = logger; + Consent = consent; + Profile = profile; + _httpContextAccessor = httpContextAccessor; + _authenticationService = authenticationService; + } + + /// + /// Processes the interaction logic. + /// + /// The request. + /// The consent. + /// + public virtual async Task ProcessInteractionAsync(ValidatedAuthorizeRequest request, ConsentResponse consent = null) + { + Logger.LogTrace("ProcessInteractionAsync"); + + if (consent != null && consent.Granted == false && request.Subject.IsAuthenticated() == false) + { + // special case when anonymous user has issued a deny prior to authenticating + Logger.LogInformation("Error: User denied consent"); + return new InteractionResponse + { + Error = OidcConstants.AuthorizeErrors.AccessDenied + }; + } + + var identityServerUser = new IdentityServerUser(request.ClientId) + { + DisplayName = request.Client.ClientName, + AdditionalClaims = request.ClientClaims, + AuthenticationTime = new DateTime?(DateTime.UtcNow) + }; + + request.Subject = identityServerUser.CreatePrincipal(); + + var authenticationProperties = new AuthenticationProperties + { + IsPersistent = true, + IssuedUtc = DateTime.UtcNow + }; + + await _httpContextAccessor.HttpContext.SignInAsync(IdentityServerConstants.DefaultCookieAuthenticationScheme, request.Subject, authenticationProperties); + + var result = new InteractionResponse(); + + return result; + } + } } \ No newline at end of file diff --git a/Nop.Plugin.Api/IdentityServer/Services/ProfileService.cs b/Nop.Plugin.Api/IdentityServer/Services/ProfileService.cs index 1c6c800..57c5069 100644 --- a/Nop.Plugin.Api/IdentityServer/Services/ProfileService.cs +++ b/Nop.Plugin.Api/IdentityServer/Services/ProfileService.cs @@ -1,43 +1,42 @@ -namespace Nop.Plugin.Api.IdentityServer.Services -{ - using System.Linq; - using System.Security.Claims; - using System.Threading.Tasks; - using IdentityServer4.Models; - using IdentityServer4.Services; - using Nop.Plugin.Api.Services; - - public class ProfileService : IProfileService - { - private readonly IClientService _clientService; - - public ProfileService(IClientService clientService) - { - _clientService = clientService; - } - - // TODO: test this - public Task GetProfileDataAsync(ProfileDataRequestContext context) - { - var sub = context.Subject.Claims.FirstOrDefault(x => x.Type == "sub"); - - int userId = 0; - - if (int.TryParse(sub?.Value, out userId)) - { - // TODO: do we need claims?? - //IdentityServer4.EntityFramework.Entities.Client client = _clientService.GetClientByClientId(userId.ToString()); - //context.IssuedClaims = context.Subject.Claims.ToList(); - //context.IssuedClaims.Add(new Claim(type: ClaimTypes.NameIdentifier, value: client.Id.ToString())); - //context.IssuedClaims.Add(new Claim(type: ClaimTypes.Name, value: client.ClientName)); - } - - return Task.CompletedTask; - } - - public Task IsActiveAsync(IsActiveContext context) - { - return Task.CompletedTask; - } - } +namespace Nop.Plugin.Api.IdentityServer.Services +{ + using System.Linq; + using System.Threading.Tasks; + using IdentityServer4.Models; + using IdentityServer4.Services; + using Nop.Plugin.Api.Services; + + public class ProfileService : IProfileService + { + private readonly IClientService _clientService; + + public ProfileService(IClientService clientService) + { + _clientService = clientService; + } + + // TODO: test this + public Task GetProfileDataAsync(ProfileDataRequestContext context) + { + var sub = context.Subject.Claims.FirstOrDefault(x => x.Type == "sub"); + + var userId = 0; + + if (int.TryParse(sub?.Value, out userId)) + { + // TODO: do we need claims?? + //IdentityServer4.EntityFramework.Entities.Client client = _clientService.GetClientByClientId(userId.ToString()); + //context.IssuedClaims = context.Subject.Claims.ToList(); + //context.IssuedClaims.Add(new Claim(type: ClaimTypes.NameIdentifier, value: client.Id.ToString())); + //context.IssuedClaims.Add(new Claim(type: ClaimTypes.Name, value: client.ClientName)); + } + + return Task.CompletedTask; + } + + public Task IsActiveAsync(IsActiveContext context) + { + return Task.CompletedTask; + } + } } \ No newline at end of file diff --git a/Nop.Plugin.Api/Infrastructure/DependencyRegister.cs b/Nop.Plugin.Api/Infrastructure/DependencyRegister.cs index cc42dbf..1f02d9c 100644 --- a/Nop.Plugin.Api/Infrastructure/DependencyRegister.cs +++ b/Nop.Plugin.Api/Infrastructure/DependencyRegister.cs @@ -1,91 +1,98 @@ -using Autofac; -using Nop.Core.Configuration; -using Nop.Core.Infrastructure; -using Nop.Core.Infrastructure.DependencyManagement; -using Nop.Plugin.Api.Services; - -namespace Nop.Plugin.Api.Infrastructure -{ - using System; - using Nop.Core.Domain.Catalog; - using Nop.Core.Domain.Common; - using Nop.Core.Domain.Customers; - using Nop.Core.Domain.Orders; - using Nop.Plugin.Api.Converters; - using Nop.Plugin.Api.Data; - using Nop.Plugin.Api.Factories; - using Nop.Plugin.Api.Helpers; - using Nop.Plugin.Api.JSON.Serializers; - using Nop.Plugin.Api.ModelBinders; - using Nop.Plugin.Api.Validators; - using Nop.Plugin.Api.WebHooks; - using Nop.Web.Framework.Infrastructure; - - public class DependencyRegister : IDependencyRegistrar - { - private const string ObjectContextName = "nop_object_context_web_api"; - - public void Register(ContainerBuilder builder, ITypeFinder typeFinder, NopConfig config) - { - this.RegisterPluginDataContext(builder, ObjectContextName); - - RegisterPluginServices(builder); - - RegisterModelBinders(builder); - } - - private void RegisterModelBinders(ContainerBuilder builder) - { - builder.RegisterGeneric(typeof(ParametersModelBinder<>)).InstancePerLifetimeScope(); - builder.RegisterGeneric(typeof(JsonModelBinder<>)).InstancePerLifetimeScope(); - } - - private void RegisterPluginServices(ContainerBuilder builder) - { - builder.RegisterType().As().InstancePerLifetimeScope(); - builder.RegisterType().As().InstancePerLifetimeScope(); - builder.RegisterType().As().InstancePerLifetimeScope(); - builder.RegisterType().As().InstancePerLifetimeScope(); - builder.RegisterType().As().InstancePerLifetimeScope(); - builder.RegisterType().As().InstancePerLifetimeScope(); - builder.RegisterType().As().InstancePerLifetimeScope(); - builder.RegisterType().As().InstancePerLifetimeScope(); - builder.RegisterType().As().InstancePerLifetimeScope(); - builder.RegisterType().As().InstancePerLifetimeScope(); - builder.RegisterType().As().InstancePerLifetimeScope(); - builder.RegisterType().As().InstancePerLifetimeScope(); - builder.RegisterType().As().InstancePerLifetimeScope(); - - builder.RegisterType().As().InstancePerLifetimeScope(); - builder.RegisterType().As().InstancePerLifetimeScope(); - builder.RegisterType().As().InstancePerLifetimeScope(); - builder.RegisterType().As().InstancePerLifetimeScope(); - builder.RegisterType().As().InstancePerLifetimeScope(); - - builder.RegisterType().As().InstancePerLifetimeScope(); - - builder.RegisterType().As().InstancePerLifetimeScope(); - - builder.RegisterType().As().InstancePerLifetimeScope(); - - builder.RegisterType().As().SingleInstance(); - - builder.RegisterType().As().InstancePerLifetimeScope(); - builder.RegisterType().As().InstancePerLifetimeScope(); - - builder.RegisterType().As>().InstancePerLifetimeScope(); - builder.RegisterType().As>().InstancePerLifetimeScope(); - builder.RegisterType().As>().InstancePerLifetimeScope(); - builder.RegisterType().As>().InstancePerLifetimeScope(); - builder.RegisterType().As>().InstancePerLifetimeScope(); - builder.RegisterType().As>().InstancePerLifetimeScope(); - - builder.RegisterType().As().InstancePerLifetimeScope(); - } - - public virtual int Order - { - get { return Int16.MaxValue; } - } - } +using Autofac; +using Nop.Core.Configuration; +using Nop.Core.Infrastructure; +using Nop.Core.Infrastructure.DependencyManagement; +using Nop.Plugin.Api.Services; +using Nop.Web.Framework.Infrastructure.Extensions; + +namespace Nop.Plugin.Api.Infrastructure +{ + using Autofac.Core; + using Microsoft.AspNetCore.Http; + using Nop.Core.Domain.Catalog; + using Nop.Core.Domain.Common; + using Nop.Core.Domain.Customers; + using Nop.Core.Domain.Orders; + using Nop.Plugin.Api.Converters; + using Nop.Plugin.Api.Data; + using Nop.Plugin.Api.Factories; + using Nop.Plugin.Api.Helpers; + using Nop.Plugin.Api.JSON.Serializers; + using Nop.Plugin.Api.ModelBinders; + using Nop.Plugin.Api.Validators; + //using Nop.Plugin.Api.WebHooks; + using System; + using System.Collections.Generic; + + public class DependencyRegister : IDependencyRegistrar + { + public void Register(ContainerBuilder builder, ITypeFinder typeFinder, NopConfig config) + { + RegisterPluginServices(builder); + + RegisterModelBinders(builder); + } + + private void RegisterModelBinders(ContainerBuilder builder) + { + builder.RegisterGeneric(typeof(ParametersModelBinder<>)).InstancePerLifetimeScope(); + builder.RegisterGeneric(typeof(JsonModelBinder<>)).InstancePerLifetimeScope(); + } + + private void RegisterPluginServices(ContainerBuilder builder) + { + builder.RegisterType().As().InstancePerLifetimeScope(); + builder.RegisterType().As().InstancePerLifetimeScope(); + builder.RegisterType().As().InstancePerLifetimeScope(); + builder.RegisterType().As().InstancePerLifetimeScope(); + builder.RegisterType().As().InstancePerLifetimeScope(); + builder.RegisterType().As().InstancePerLifetimeScope(); + builder.RegisterType().As().InstancePerLifetimeScope(); + builder.RegisterType().As().InstancePerLifetimeScope(); + builder.RegisterType().As().InstancePerLifetimeScope(); + builder.RegisterType().As().InstancePerLifetimeScope(); + builder.RegisterType().As().InstancePerLifetimeScope(); + builder.RegisterType().As().InstancePerLifetimeScope(); + builder.RegisterType().As().InstancePerLifetimeScope(); + builder.RegisterType().As().InstancePerLifetimeScope(); + builder.RegisterType().As().InstancePerLifetimeScope(); + + builder.RegisterType().As().InstancePerLifetimeScope(); + builder.RegisterType().As().InstancePerLifetimeScope(); + builder.RegisterType().As().InstancePerLifetimeScope(); + builder.RegisterType().As().InstancePerLifetimeScope(); + builder.RegisterType().As().InstancePerLifetimeScope(); + + //TODO: Upgrade 4.1. Check this! + //builder.RegisterType().As().InstancePerLifetimeScope(); + builder.RegisterType().As().SingleInstance(); + + builder.RegisterType().As().InstancePerLifetimeScope(); + + builder.RegisterType().As().InstancePerLifetimeScope(); + + + builder.RegisterType().As().InstancePerLifetimeScope(); + builder.RegisterType().As().InstancePerLifetimeScope(); + + builder.RegisterType().As>().InstancePerLifetimeScope(); + builder.RegisterType().As>().InstancePerLifetimeScope(); + builder.RegisterType().As>().InstancePerLifetimeScope(); + builder.RegisterType().As>().InstancePerLifetimeScope(); + builder.RegisterType().As>().InstancePerLifetimeScope(); + builder.RegisterType().As>().InstancePerLifetimeScope(); + builder.RegisterType().As>().InstancePerLifetimeScope(); + + builder.RegisterType().As().InstancePerLifetimeScope(); + + builder.RegisterType().As().SingleInstance(); + + builder.RegisterType>().SingleInstance(); + } + + public virtual int Order + { + get { return Int16.MaxValue; } + } + } } \ No newline at end of file diff --git a/Nop.Plugin.Api/JSON/ActionResults/ErrorActionResult.cs b/Nop.Plugin.Api/JSON/ActionResults/ErrorActionResult.cs index 71f3cd4..da8f3b9 100644 --- a/Nop.Plugin.Api/JSON/ActionResults/ErrorActionResult.cs +++ b/Nop.Plugin.Api/JSON/ActionResults/ErrorActionResult.cs @@ -1,42 +1,41 @@ -using System.Net; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Mvc; - -namespace Nop.Plugin.Api.JSON.ActionResults -{ - using System; - using System.IO; - using System.Text; - using Microsoft.AspNetCore.Http; - using Microsoft.AspNetCore.WebUtilities; - - public class ErrorActionResult : IActionResult - { - private readonly string _jsonString; - private readonly HttpStatusCode _statusCode; - - public ErrorActionResult(string jsonString, HttpStatusCode statusCode) - { - _jsonString = jsonString; - _statusCode = statusCode; - } - - public Task ExecuteResultAsync(ActionContext context) - { - if (context == null) - throw new ArgumentNullException(nameof(context)); - - HttpResponse response = context.HttpContext.Response; - - response.StatusCode = (int)_statusCode; - response.ContentType = "application/json"; - - using (TextWriter writer = new HttpResponseStreamWriter(response.Body, Encoding.UTF8)) - { - writer.Write(_jsonString); - } - - return Task.CompletedTask; - } - } +using System.Net; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; + +namespace Nop.Plugin.Api.JSON.ActionResults +{ + using System; + using System.IO; + using System.Text; + using Microsoft.AspNetCore.WebUtilities; + + public class ErrorActionResult : IActionResult + { + private readonly string _jsonString; + private readonly HttpStatusCode _statusCode; + + public ErrorActionResult(string jsonString, HttpStatusCode statusCode) + { + _jsonString = jsonString; + _statusCode = statusCode; + } + + public Task ExecuteResultAsync(ActionContext context) + { + if (context == null) + throw new ArgumentNullException(nameof(context)); + + var response = context.HttpContext.Response; + + response.StatusCode = (int)_statusCode; + response.ContentType = "application/json"; + + using (TextWriter writer = new HttpResponseStreamWriter(response.Body, Encoding.UTF8)) + { + writer.Write(_jsonString); + } + + return Task.CompletedTask; + } + } } \ No newline at end of file diff --git a/Nop.Plugin.Api/JSON/ActionResults/RawJsonActionResult.cs b/Nop.Plugin.Api/JSON/ActionResults/RawJsonActionResult.cs index 220979d..0e8e1d6 100644 --- a/Nop.Plugin.Api/JSON/ActionResults/RawJsonActionResult.cs +++ b/Nop.Plugin.Api/JSON/ActionResults/RawJsonActionResult.cs @@ -1,43 +1,42 @@ -using System.Threading.Tasks; -using Microsoft.AspNetCore.Mvc; - -namespace Nop.Plugin.Api.JSON.ActionResults -{ - using System; - using System.IO; - using System.Text; - using Microsoft.AspNetCore.Http; - using Microsoft.AspNetCore.WebUtilities; - - // TODO: Move to BaseApiController as method. - public class RawJsonActionResult : IActionResult - { - private readonly string _jsonString; - - public RawJsonActionResult(object value) - { - if (value != null) - { - _jsonString = value.ToString(); - } - } - - public Task ExecuteResultAsync(ActionContext context) - { - if (context == null) - throw new ArgumentNullException(nameof(context)); - - HttpResponse response = context.HttpContext.Response; - - response.StatusCode = 200; - response.ContentType = "application/json"; - - using (TextWriter writer = new HttpResponseStreamWriter(response.Body, Encoding.UTF8)) - { - writer.Write(_jsonString); - } - - return Task.CompletedTask; - } - } -} +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; + +namespace Nop.Plugin.Api.JSON.ActionResults +{ + using System; + using System.IO; + using System.Text; + using Microsoft.AspNetCore.WebUtilities; + + // TODO: Move to BaseApiController as method. + public class RawJsonActionResult : IActionResult + { + private readonly string _jsonString; + + public RawJsonActionResult(object value) + { + if (value != null) + { + _jsonString = value.ToString(); + } + } + + public Task ExecuteResultAsync(ActionContext context) + { + if (context == null) + throw new ArgumentNullException(nameof(context)); + + var response = context.HttpContext.Response; + + response.StatusCode = 200; + response.ContentType = "application/json"; + + using (TextWriter writer = new HttpResponseStreamWriter(response.Body, Encoding.UTF8)) + { + writer.Write(_jsonString); + } + + return Task.CompletedTask; + } + } +} diff --git a/Nop.Plugin.Api/JSON/Serializers/JsonFieldsSerializer.cs b/Nop.Plugin.Api/JSON/Serializers/JsonFieldsSerializer.cs index 297a5ca..45a7b6d 100644 --- a/Nop.Plugin.Api/JSON/Serializers/JsonFieldsSerializer.cs +++ b/Nop.Plugin.Api/JSON/Serializers/JsonFieldsSerializer.cs @@ -1,61 +1,61 @@ -namespace Nop.Plugin.Api.JSON.Serializers -{ - using System; - using System.Collections.Generic; - using System.Linq; - using Newtonsoft.Json.Linq; - using Nop.Plugin.Api.DTOs; - using Nop.Plugin.Api.Helpers; - - public class JsonFieldsSerializer : IJsonFieldsSerializer - { - public string Serialize(ISerializableObject objectToSerialize, string jsonFields) - { - if (objectToSerialize == null) - { - throw new ArgumentNullException("objectToSerialize"); - } - - IList fieldsList = null; - - if (!string.IsNullOrEmpty(jsonFields)) - { - string primaryPropertyName = objectToSerialize.GetPrimaryPropertyName(); - - fieldsList = GetPropertiesIntoList(jsonFields); - - // Always add the root manually - fieldsList.Add(primaryPropertyName); - } - - string json = Serialize(objectToSerialize, fieldsList); - - return json; - } - - private string Serialize(object objectToSerialize, IList jsonFields = null) - { - JToken jToken = JToken.FromObject(objectToSerialize); - - if (jsonFields != null) - { - jToken = jToken.RemoveEmptyChildrenAndFilterByFields(jsonFields); - } - - string jTokenResult = jToken.ToString(); - - return jTokenResult; - } - - private IList GetPropertiesIntoList(string fields) - { - IList properties = fields.ToLowerInvariant() - .Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries) - .Select(x => x.Trim()) - .Distinct() - .ToList(); - - return properties; - } - } +namespace Nop.Plugin.Api.JSON.Serializers +{ + using System; + using System.Collections.Generic; + using System.Linq; + using Newtonsoft.Json.Linq; + using Nop.Plugin.Api.DTOs; + using Nop.Plugin.Api.Helpers; + + public class JsonFieldsSerializer : IJsonFieldsSerializer + { + public string Serialize(ISerializableObject objectToSerialize, string jsonFields) + { + if (objectToSerialize == null) + { + throw new ArgumentNullException(nameof(objectToSerialize)); + } + + IList fieldsList = null; + + if (!string.IsNullOrEmpty(jsonFields)) + { + var primaryPropertyName = objectToSerialize.GetPrimaryPropertyName(); + + fieldsList = GetPropertiesIntoList(jsonFields); + + // Always add the root manually + fieldsList.Add(primaryPropertyName); + } + + var json = Serialize(objectToSerialize, fieldsList); + + return json; + } + + private string Serialize(object objectToSerialize, IList jsonFields = null) + { + var jToken = JToken.FromObject(objectToSerialize); + + if (jsonFields != null) + { + jToken = jToken.RemoveEmptyChildrenAndFilterByFields(jsonFields); + } + + var jTokenResult = jToken.ToString(); + + return jTokenResult; + } + + private IList GetPropertiesIntoList(string fields) + { + IList properties = fields.ToLowerInvariant() + .Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries) + .Select(x => x.Trim()) + .Distinct() + .ToList(); + + return properties; + } + } } \ No newline at end of file diff --git a/Nop.Plugin.Api/MappingExtensions/ManufacturerDtoMappings.cs b/Nop.Plugin.Api/MappingExtensions/ManufacturerDtoMappings.cs new file mode 100644 index 0000000..92eb2d7 --- /dev/null +++ b/Nop.Plugin.Api/MappingExtensions/ManufacturerDtoMappings.cs @@ -0,0 +1,14 @@ +using Nop.Core.Domain.Catalog; +using Nop.Plugin.Api.AutoMapper; +using Nop.Plugin.Api.DTOs.Manufacturers; + +namespace Nop.Plugin.Api.MappingExtensions +{ + public static class ManufacturerDtoMappings + { + public static ManufacturerDto ToDto(this Manufacturer manufacturer) + { + return manufacturer.MapTo(); + } + } +} \ No newline at end of file diff --git a/Nop.Plugin.Api/MappingExtensions/ProductAttributeCombinationDtoMappings.cs b/Nop.Plugin.Api/MappingExtensions/ProductAttributeCombinationDtoMappings.cs new file mode 100644 index 0000000..acc6e7d --- /dev/null +++ b/Nop.Plugin.Api/MappingExtensions/ProductAttributeCombinationDtoMappings.cs @@ -0,0 +1,14 @@ +using Nop.Core.Domain.Catalog; +using Nop.Plugin.Api.AutoMapper; +using Nop.Plugin.Api.DTOs.Products; + +namespace Nop.Plugin.Api.MappingExtensions +{ + public static class ProductAttributeCombinationDtoMappings + { + public static ProductAttributeCombinationDto ToDto(this ProductAttributeCombination productAttributeCombination) + { + return productAttributeCombination.MapTo(); + } + } +} \ No newline at end of file diff --git a/Nop.Plugin.Api/MappingExtensions/ProductManufacturerMappingDtoMappings.cs b/Nop.Plugin.Api/MappingExtensions/ProductManufacturerMappingDtoMappings.cs new file mode 100644 index 0000000..583858e --- /dev/null +++ b/Nop.Plugin.Api/MappingExtensions/ProductManufacturerMappingDtoMappings.cs @@ -0,0 +1,14 @@ +using Nop.Core.Domain.Catalog; +using Nop.Plugin.Api.AutoMapper; +using Nop.Plugin.Api.DTOs.ProductManufacturerMappings; + +namespace Nop.Plugin.Api.MappingExtensions +{ + public static class ProductManufacturerMappingDtoMappings + { + public static ProductManufacturerMappingsDto ToDto(this ProductManufacturer mapping) + { + return mapping.MapTo(); + } + } +} \ No newline at end of file diff --git a/Nop.Plugin.Api/Maps/JsonPropertyMap.cs b/Nop.Plugin.Api/Maps/JsonPropertyMap.cs index 496ebb8..c37a5ec 100644 --- a/Nop.Plugin.Api/Maps/JsonPropertyMap.cs +++ b/Nop.Plugin.Api/Maps/JsonPropertyMap.cs @@ -1,77 +1,77 @@ -using System; -using System.Collections.Generic; -using System.Reflection; -using Newtonsoft.Json; -using Nop.Core.Caching; -using Nop.Core.Infrastructure; -using Nop.Plugin.Api.Attributes; -using Nop.Plugin.Api.Constants; - -namespace Nop.Plugin.Api.Maps -{ - public class JsonPropertyMapper : IJsonPropertyMapper - { - private IStaticCacheManager _cacheManager; - - private IStaticCacheManager StaticCacheManager - { - get - { - if (_cacheManager == null) - { - _cacheManager = EngineContext.Current.Resolve(); - } - - return _cacheManager; - } - } - - public Dictionary> GetMap(Type type) - { - if (!StaticCacheManager.IsSet(Configurations.JsonTypeMapsPattern)) - { - StaticCacheManager.Set(Configurations.JsonTypeMapsPattern, new Dictionary>>(), int.MaxValue); - } - - var typeMaps = StaticCacheManager.Get>>>(Configurations.JsonTypeMapsPattern); - - if (!typeMaps.ContainsKey(type.Name)) - { - Build(type); - } - - return typeMaps[type.Name]; - } - - private void Build(Type type) - { - Dictionary>> typeMaps = - StaticCacheManager.Get>>>(Configurations.JsonTypeMapsPattern); - - var mapForCurrentType = new Dictionary>(); - - var typeProps = type.GetProperties(); - - foreach (var property in typeProps) - { - JsonPropertyAttribute jsonAttribute = property.GetCustomAttribute(typeof(JsonPropertyAttribute)) as JsonPropertyAttribute; - DoNotMapAttribute doNotMapAttribute = property.GetCustomAttribute(typeof(DoNotMapAttribute)) as DoNotMapAttribute; - - // If it has json attribute set and is not marked as doNotMap - if (jsonAttribute != null && doNotMapAttribute == null) - { - if (!mapForCurrentType.ContainsKey(jsonAttribute.PropertyName)) - { - var value = new Tuple(property.Name, property.PropertyType); - mapForCurrentType.Add(jsonAttribute.PropertyName, value); - } - } - } - - if (!typeMaps.ContainsKey(type.Name)) - { - typeMaps.Add(type.Name, mapForCurrentType); - } - } - } +using System; +using System.Collections.Generic; +using System.Reflection; +using Newtonsoft.Json; +using Nop.Core.Caching; +using Nop.Core.Infrastructure; +using Nop.Plugin.Api.Attributes; +using Nop.Plugin.Api.Constants; + +namespace Nop.Plugin.Api.Maps +{ + public class JsonPropertyMapper : IJsonPropertyMapper + { + private IStaticCacheManager _cacheManager; + + private IStaticCacheManager StaticCacheManager + { + get + { + if (_cacheManager == null) + { + _cacheManager = EngineContext.Current.Resolve(); + } + + return _cacheManager; + } + } + + public Dictionary> GetMap(Type type) + { + if (!StaticCacheManager.IsSet(Configurations.JsonTypeMapsPattern)) + { + StaticCacheManager.Set(Configurations.JsonTypeMapsPattern, new Dictionary>>(), int.MaxValue); + } + + var typeMaps = StaticCacheManager.Get>>>(Configurations.JsonTypeMapsPattern, () => null, 0); + + if (!typeMaps.ContainsKey(type.Name)) + { + Build(type); + } + + return typeMaps[type.Name]; + } + + private void Build(Type type) + { + var typeMaps = + StaticCacheManager.Get>>>(Configurations.JsonTypeMapsPattern, () => null, 0); + + var mapForCurrentType = new Dictionary>(); + + var typeProps = type.GetProperties(); + + foreach (var property in typeProps) + { + var jsonAttribute = property.GetCustomAttribute(typeof(JsonPropertyAttribute)) as JsonPropertyAttribute; + var doNotMapAttribute = property.GetCustomAttribute(typeof(DoNotMapAttribute)) as DoNotMapAttribute; + + // If it has json attribute set and is not marked as doNotMap + if (jsonAttribute != null && doNotMapAttribute == null) + { + if (!mapForCurrentType.ContainsKey(jsonAttribute.PropertyName)) + { + var value = new Tuple(property.Name, property.PropertyType); + mapForCurrentType.Add(jsonAttribute.PropertyName, value); + } + } + } + + if (!typeMaps.ContainsKey(type.Name)) + { + typeMaps.Add(type.Name, mapForCurrentType); + } + } + } } \ No newline at end of file diff --git a/Nop.Plugin.Api/ModelBinders/JsonModelBinder.cs b/Nop.Plugin.Api/ModelBinders/JsonModelBinder.cs index a8af44b..7b60a8e 100644 --- a/Nop.Plugin.Api/ModelBinders/JsonModelBinder.cs +++ b/Nop.Plugin.Api/ModelBinders/JsonModelBinder.cs @@ -1,335 +1,203 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using System.Threading.Tasks; -using FluentValidation.Attributes; -using FluentValidation.Results; -using Newtonsoft.Json; -using Nop.Core.Domain.Localization; -using Nop.Plugin.Api.Attributes; -using Nop.Plugin.Api.Helpers; -using Nop.Plugin.Api.Delta; -using Nop.Plugin.Api.Validators; -using Nop.Services.Localization; - -namespace Nop.Plugin.Api.ModelBinders -{ - using System.IO; - using Microsoft.AspNetCore.Mvc; - using Microsoft.AspNetCore.Mvc.ModelBinding; - - public class JsonModelBinder : IModelBinder where T : class, new() - { - private readonly IJsonHelper _jsonHelper; - private readonly ILocalizationService _localizationService; - private readonly int FirstLanguageId; - - public JsonModelBinder(IJsonHelper jsonHelper, ILocalizationService localizationService, ILanguageService languageService) - { - _jsonHelper = jsonHelper; - _localizationService = localizationService; - - // Languages are ordered by display order so the first language will be with the smallest display order. - Language firstLanguage = languageService.GetAllLanguages().FirstOrDefault(); - - if (firstLanguage != null) - { - FirstLanguageId = firstLanguage.Id; - } - else - { - FirstLanguageId = 0; - } - } - - public Task BindModelAsync(ModelBindingContext bindingContext) - { - Dictionary result = GetResult(bindingContext); - - if (result == null) - { - bindingContext.Result = ModelBindingResult.Failed(); - return Task.CompletedTask; - } - - string rootProperty = GetRootProperty(bindingContext); - - // Now we need to validate the root property. - ValidateRootProperty(bindingContext, result, rootProperty); - - if (bindingContext.ModelState.IsValid) - { - // The validation for the key is in the Validate method. - Dictionary propertyValuePaires = - (Dictionary) result[rootProperty]; - - // You will have id parameter passed in the model binder only when you have put request. - // because get and delete do not use the model binder. - // Here we insert the id in the property value pairs to be validated by the dto validator in a later point. - object routeDataId = GetRouteDataId(bindingContext.ActionContext); - - if (routeDataId != null) - { - // Here we insert the route data id in the value paires. - // If id is contained in the category json body the one from the route data is used instead. - InsertIdInTheValuePaires(propertyValuePaires, routeDataId); - } - - // We need to call this method here so it will be certain that the routeDataId will be in the propertyValuePaires - // when the request is PUT. - ValidateValueTypes(bindingContext, propertyValuePaires); - - Delta delta = null; - - if (bindingContext.ModelState.IsValid) - { - delta = new Delta(propertyValuePaires); - ValidateModel(bindingContext, propertyValuePaires, delta.Dto); - } - - if (bindingContext.ModelState.IsValid) - { - bindingContext.Model = delta; - bindingContext.Result = ModelBindingResult.Success(bindingContext.Model); - } - else - { - bindingContext.Result = ModelBindingResult.Failed(); - } - } - else - { - bindingContext.Result = ModelBindingResult.Failed(); - } - - return Task.CompletedTask; - } - - private Dictionary GetResult(ModelBindingContext bindingContext) - { - Dictionary result = null; - - Stream requestPayloadStream = bindingContext.ActionContext.HttpContext.Request.Body; - - string requestPayload = string.Empty; - - using (requestPayloadStream) - { - if (requestPayloadStream != null) - { - var streamReader = new StreamReader(requestPayloadStream); - requestPayload = streamReader.ReadToEnd(); - streamReader.Close(); - } - } - - // We need to check if the request has a payload. - CheckIfJsonIsProvided(bindingContext, requestPayload); - - // After we are sure that the request payload and json are provided we need to deserialize this json. - result = DeserializeReqestPayload(bindingContext, requestPayload); - - // Next we have to validate the json format. - ValidateJsonFormat(bindingContext, result); - - return result; - } - - private object GetRouteDataId(ActionContext actionContext) - { - object routeDataId = null; - - if (actionContext.RouteData.Values.ContainsKey("id")) - { - routeDataId = actionContext.RouteData.Values["id"]; - } - - return routeDataId; - } - - private void ValidateValueTypes(ModelBindingContext bindingContext, Dictionary propertyValuePaires) - { - var errors = new Dictionary(); - - // Validate if the property value pairs passed maches the type. - var typeValidator = new TypeValidator(); - - if (!typeValidator.IsValid(propertyValuePaires)) - { - foreach (var invalidProperty in typeValidator.InvalidProperties) - { - var key = string.Format(_localizationService.GetResource("Api.InvalidType", FirstLanguageId, false), invalidProperty); - - if (!errors.ContainsKey(key)) - { - errors.Add(key, _localizationService.GetResource("Api.InvalidPropertyType", FirstLanguageId, false)); - } - } - } - - if (errors.Count > 0) - { - foreach (var error in errors) - { - bindingContext.ModelState.AddModelError(error.Key, error.Value); - } - } - } - - private void ValidateRootProperty(ModelBindingContext bindingContext, Dictionary result, string rootProperty) - { - if (bindingContext.ModelState.IsValid) - { - bool isRootPropertyValid = !string.IsNullOrEmpty(rootProperty) && result.ContainsKey(rootProperty); - - if (!isRootPropertyValid) - { - bindingContext.ModelState.AddModelError("rootProperty", _localizationService.GetResource("Api.InvalidRootProperty", FirstLanguageId, false)); - } - } - } - - private string GetRootProperty(ModelBindingContext bindingContext) - { - string rootProperty = null; - - if (bindingContext.ModelState.IsValid) - { - JsonObjectAttribute jsonObjectAttribute = ReflectionHelper.GetJsonObjectAttribute(typeof(T)); - - if (jsonObjectAttribute != null) - { - rootProperty = jsonObjectAttribute.Title; - } - } - - return rootProperty; - } - - private void ValidateJsonFormat(ModelBindingContext bindingContext, Dictionary result) - { - bool isJsonFormatValid = result != null && result.Count > 0; - - if (!isJsonFormatValid) - { - bindingContext.ModelState.AddModelError("json", - _localizationService.GetResource("Api.InvalidJsonFormat", FirstLanguageId, false)); - } - } - - private Dictionary DeserializeReqestPayload(ModelBindingContext bindingContext, string requestPayload) - { - Dictionary result = null; - - // Here we check if validation has passed to this point. - if (bindingContext.ModelState.IsValid) - { - result = _jsonHelper.DeserializeToDictionary(requestPayload); - } - - return result; - } - - private void CheckIfJsonIsProvided(ModelBindingContext bindingContext, string requestPayload) - { - if (string.IsNullOrEmpty(requestPayload) && - bindingContext.ModelState.IsValid) - { - bindingContext.ModelState.AddModelError("json", _localizationService.GetResource("Api.NoJsonProvided", FirstLanguageId, false)); - } - } - - private void ValidateModel(ModelBindingContext bindingContext, Dictionary propertyValuePaires, T dto) - { - ValidationResult validationResult = GetValidationResult(bindingContext.ActionContext, propertyValuePaires, dto); - - if (!validationResult.IsValid) - { - foreach (var validationFailure in validationResult.Errors) - { - bindingContext.ModelState.AddModelError(validationFailure.PropertyName, - validationFailure.ErrorMessage); - } - } - else - { - HandleValidationAttributes(dto, bindingContext); - } - } - - private ValidationResult GetValidationResult(ActionContext actionContext, Dictionary propertyValuePaires, T dto) - { - var validationResult = new ValidationResult(); - - // Needed so we can call the get the validator. - ValidatorAttribute validatorAttribute = - typeof (T).GetCustomAttribute(typeof (ValidatorAttribute)) as ValidatorAttribute; - - if (validatorAttribute != null) - { - Type validatorType = validatorAttribute.ValidatorType; - - // We need to pass the http method because there are some differences between the validation rules for post and put - // We need to pass the propertyValuePaires from the passed json because there are cases in which one field is required - // on post, but it is a valid case not to pass it when doing a put request. - var validator = Activator.CreateInstance(validatorType, - new object[] - { - //TODO: find this - actionContext.HttpContext.Request.Method, - propertyValuePaires - }); - - // We know that the validator will be AbstractValidator which means it will have Validate method. - validationResult = validatorType.GetMethod("Validate", new[] {typeof (T)}) - .Invoke(validator, new[] {dto}) as ValidationResult; - } - - return validationResult; - } - - private void HandleValidationAttributes(T dto, ModelBindingContext bindingContext) - { - var dtoProperties = dto.GetType().GetProperties(); - - foreach (var property in dtoProperties) - { - // Check property type - BaseValidationAttribute validationAttribute = property.PropertyType.GetCustomAttribute(typeof (BaseValidationAttribute)) as BaseValidationAttribute; - - // If not on property type, check the property itself. - if (validationAttribute == null) - { - validationAttribute = property.GetCustomAttribute(typeof (BaseValidationAttribute)) as BaseValidationAttribute; - } - - if (validationAttribute != null) - { - validationAttribute.Validate(property.GetValue(dto)); - Dictionary errors = validationAttribute.GetErrors(); - - if (errors.Count > 0) - { - foreach (var error in errors) - { - bindingContext.ModelState.AddModelError(error.Key, error.Value); - } - } - } - } - } - - private void InsertIdInTheValuePaires(Dictionary propertyValuePaires, object requestId) - { - if (propertyValuePaires.ContainsKey("id")) - { - propertyValuePaires["id"] = requestId; - } - else - { - propertyValuePaires.Add("id", requestId); - } - } - } +using Nop.Plugin.Api.Attributes; +using Nop.Plugin.Api.Delta; +using Nop.Plugin.Api.Helpers; +using Nop.Plugin.Api.Validators; +using Nop.Services.Localization; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Threading.Tasks; + +namespace Nop.Plugin.Api.ModelBinders +{ + using Microsoft.AspNetCore.Mvc; + using Microsoft.AspNetCore.Mvc.ModelBinding; + using System; + + public class JsonModelBinder : IModelBinder where T : class, new() + { + private readonly IJsonHelper _jsonHelper; + private readonly ILocalizationService _localizationService; + + private readonly int _languageId; + + public JsonModelBinder(IJsonHelper jsonHelper, ILocalizationService localizationService, ILanguageService languageService) + { + _jsonHelper = jsonHelper; + _localizationService = localizationService; + + // Languages are ordered by display order so the first language will be with the smallest display order. + var firstLanguage = languageService.GetAllLanguages().FirstOrDefault(); + if (firstLanguage != null) + { + _languageId = firstLanguage.Id; + } + else + { + _languageId = 0; + } + } + + public Task BindModelAsync(ModelBindingContext bindingContext) + { + var propertyValuePairs = GetPropertyValuePairs(bindingContext); + if (propertyValuePairs == null) + { + bindingContext.Result = ModelBindingResult.Failed(); + return Task.CompletedTask; + } + + if (bindingContext.ModelState.IsValid) + { + // You will have id parameter passed in the model binder only when you have put request. + // because get and delete do not use the model binder. + // Here we insert the id in the property value pairs to be validated by the dto validator in a later point. + var routeDataId = GetRouteDataId(bindingContext.ActionContext); + + if (routeDataId != null) + { + // Here we insert the route data id in the value paires. + // If id is contained in the category json body the one from the route data is used instead. + InsertIdInTheValuePaires(propertyValuePairs, routeDataId); + } + + // We need to call this method here so it will be certain that the routeDataId will be in the propertyValuePaires + // when the request is PUT. + ValidateValueTypes(bindingContext, propertyValuePairs); + + Delta delta = null; + + if (bindingContext.ModelState.IsValid) + { + delta = new Delta(propertyValuePairs); + ValidateModel(bindingContext, propertyValuePairs, delta.Dto); + } + + if (bindingContext.ModelState.IsValid) + { + bindingContext.Model = delta; + bindingContext.Result = ModelBindingResult.Success(bindingContext.Model); + } + else + { + bindingContext.Result = ModelBindingResult.Failed(); + } + } + else + { + bindingContext.Result = ModelBindingResult.Failed(); + } + + return Task.CompletedTask; + } + + private Dictionary GetPropertyValuePairs(ModelBindingContext bindingContext) + { + Dictionary result = null; + + if (bindingContext.ModelState.IsValid) + { + try + { + //get the root dictionary and root property (these will throw exceptions if they fail) + result = _jsonHelper.GetRequestJsonDictionaryFromStream(bindingContext.HttpContext.Request.Body, true); + var rootPropertyName = _jsonHelper.GetRootPropertyName(); + + result = (Dictionary)result[rootPropertyName]; + } + catch (Exception ex) + { + bindingContext.ModelState.AddModelError("json", ex.Message); + } + } + + return result; + } + + private object GetRouteDataId(ActionContext actionContext) + { + object routeDataId = null; + + if (actionContext.RouteData.Values.ContainsKey("id")) + { + routeDataId = actionContext.RouteData.Values["id"]; + } + + return routeDataId; + } + + private void ValidateValueTypes(ModelBindingContext bindingContext, Dictionary propertyValuePaires) + { + var errors = new Dictionary(); + + // Validate if the property value pairs passed maches the type. + var typeValidator = new TypeValidator(); + + if (!typeValidator.IsValid(propertyValuePaires)) + { + foreach (var invalidProperty in typeValidator.InvalidProperties) + { + var key = string.Format(_localizationService.GetResource("Api.InvalidType", _languageId, false), invalidProperty); + + if (!errors.ContainsKey(key)) + { + errors.Add(key, _localizationService.GetResource("Api.InvalidPropertyType", _languageId, false)); + } + } + } + + if (errors.Count > 0) + { + foreach (var error in errors) + { + bindingContext.ModelState.AddModelError(error.Key, error.Value); + } + } + } + + private void ValidateModel(ModelBindingContext bindingContext, Dictionary propertyValuePaires, T dto) + { + // this method validates each property by checking if it has an attribute that inherits from BaseValidationAttribute + // these attribtues are different than FluentValidation attributes, so they need to be validated manually + + var dtoProperties = dto.GetType().GetProperties(); + foreach (var property in dtoProperties) + { + // Check property type + var validationAttribute = property.PropertyType.GetCustomAttribute(typeof(BaseValidationAttribute)) as BaseValidationAttribute; + + // If not on property type, check the property itself. + if (validationAttribute == null) + { + validationAttribute = property.GetCustomAttribute(typeof(BaseValidationAttribute)) as BaseValidationAttribute; + } + + if (validationAttribute != null) + { + validationAttribute.Validate(property.GetValue(dto)); + var errors = validationAttribute.GetErrors(); + + if (errors.Count > 0) + { + foreach (var error in errors) + { + bindingContext.ModelState.AddModelError(error.Key, error.Value); + } + } + } + } + } + + private void InsertIdInTheValuePaires(Dictionary propertyValuePaires, object requestId) + { + if (propertyValuePaires.ContainsKey("id")) + { + propertyValuePaires["id"] = requestId; + } + else + { + propertyValuePaires.Add("id", requestId); + } + } + } } \ No newline at end of file diff --git a/Nop.Plugin.Api/ModelBinders/ParametersModelBinder.cs b/Nop.Plugin.Api/ModelBinders/ParametersModelBinder.cs index b1f01d9..1787e44 100644 --- a/Nop.Plugin.Api/ModelBinders/ParametersModelBinder.cs +++ b/Nop.Plugin.Api/ModelBinders/ParametersModelBinder.cs @@ -1,44 +1,43 @@ -using System.Collections.Generic; -using Nop.Plugin.Api.Converters; - -namespace Nop.Plugin.Api.ModelBinders -{ - using System; - using System.Linq; - using System.Threading.Tasks; - using Microsoft.AspNetCore.Mvc.ModelBinding; - - public class ParametersModelBinder : IModelBinder where T : class, new() - { - private readonly IObjectConverter _objectConverter; - - public ParametersModelBinder(IObjectConverter objectConverter) - { - _objectConverter = objectConverter; - } - - public Task BindModelAsync(ModelBindingContext bindingContext) - { - if (bindingContext == null) - { - throw new ArgumentNullException(nameof(bindingContext)); - } - - if (bindingContext.HttpContext.Request.QueryString.HasValue) - { - Dictionary queryParameters = bindingContext.HttpContext.Request.Query.ToDictionary(pair => pair.Key, pair => pair.Value.ToString()); - - bindingContext.Model = _objectConverter.ToObject(queryParameters); - } - else - { - bindingContext.Model = new T(); - } - - bindingContext.Result = ModelBindingResult.Success(bindingContext.Model); - - // This should be true otherwise the model will be null. - return Task.CompletedTask; - } - } +using Nop.Plugin.Api.Converters; + +namespace Nop.Plugin.Api.ModelBinders +{ + using System; + using System.Linq; + using System.Threading.Tasks; + using Microsoft.AspNetCore.Mvc.ModelBinding; + + public class ParametersModelBinder : IModelBinder where T : class, new() + { + private readonly IObjectConverter _objectConverter; + + public ParametersModelBinder(IObjectConverter objectConverter) + { + _objectConverter = objectConverter; + } + + public Task BindModelAsync(ModelBindingContext bindingContext) + { + if (bindingContext == null) + { + throw new ArgumentNullException(nameof(bindingContext)); + } + + if (bindingContext.HttpContext.Request.QueryString.HasValue) + { + var queryParameters = bindingContext.HttpContext.Request.Query.ToDictionary(pair => pair.Key, pair => pair.Value.ToString()); + + bindingContext.Model = _objectConverter.ToObject(queryParameters); + } + else + { + bindingContext.Model = new T(); + } + + bindingContext.Result = ModelBindingResult.Success(bindingContext.Model); + + // This should be true otherwise the model will be null. + return Task.CompletedTask; + } + } } \ No newline at end of file diff --git a/Nop.Plugin.Api/Models/ClientApiModel.cs b/Nop.Plugin.Api/Models/ClientApiModel.cs index 1ee1352..243c0c8 100644 --- a/Nop.Plugin.Api/Models/ClientApiModel.cs +++ b/Nop.Plugin.Api/Models/ClientApiModel.cs @@ -1,114 +1,33 @@ namespace Nop.Plugin.Api.Models { - using System; - using Nop.Plugin.Api.Constants; - public class ClientApiModel { - private int _accessTokenLifetime; - private int _refreshTokenLifetime; - private string _clientId; - private string _clientSecretRaw; - + public int Id { get; set; } public string ClientName { get; set; } public string ClientId { - get - { - if (string.IsNullOrEmpty(_clientId)) - { - _clientId = Guid.NewGuid().ToString(); - } - - return _clientId; - } - set - { - if (string.IsNullOrEmpty(value)) - { - _clientId = Guid.NewGuid().ToString(); - } - else - { - _clientId = value; - } - } + get; + set; } public string ClientSecret { - get - { - if (string.IsNullOrEmpty(_clientSecretRaw)) - { - _clientSecretRaw = Guid.NewGuid().ToString(); - } - - return _clientSecretRaw; - } - set - { - if (string.IsNullOrEmpty(value)) - { - _clientSecretRaw = Guid.NewGuid().ToString(); - } - else - { - _clientSecretRaw = value; - } - } + get; + set; } public string RedirectUrl { get; set; } public int AccessTokenLifetime { - get - { - if (_accessTokenLifetime <= 0) - { - _accessTokenLifetime = Configurations.DefaultAccessTokenExpiration; - } - - return _accessTokenLifetime; - } - set - { - if (value <= 0) - { - _accessTokenLifetime = Configurations.DefaultAccessTokenExpiration; - } - else - { - _accessTokenLifetime = value; - } - } + get;set; } public int RefreshTokenLifetime { - get - { - if (_refreshTokenLifetime <= 0) - { - _refreshTokenLifetime = Configurations.DefaultRefreshTokenExpiration; - } - - return _refreshTokenLifetime; - } - set - { - if (value <= 0) - { - _refreshTokenLifetime = Configurations.DefaultRefreshTokenExpiration; - } - else - { - _refreshTokenLifetime = value; - } - } + get;set; } public bool Enabled { get; set; } diff --git a/Nop.Plugin.Api/Models/ManufacturersParameters/BaseManufacturersParametersModel.cs b/Nop.Plugin.Api/Models/ManufacturersParameters/BaseManufacturersParametersModel.cs new file mode 100644 index 0000000..d173a99 --- /dev/null +++ b/Nop.Plugin.Api/Models/ManufacturersParameters/BaseManufacturersParametersModel.cs @@ -0,0 +1,59 @@ +using System; +using Newtonsoft.Json; + +namespace Nop.Plugin.Api.Models.ManufacturersParameters +{ + // JsonProperty is used only for swagger + public class BaseManufacturersParametersModel + { + public BaseManufacturersParametersModel() + { + ProductId = null; + CreatedAtMin = null; + CreatedAtMax = null; + UpdatedAtMin = null; + UpdatedAtMax = null; + PublishedStatus = null; + } + + /// + /// Show categories created after date (format: 2008-12-31 03:00) + /// + [JsonProperty("created_at_min")] + public DateTime? CreatedAtMin { get; set; } + + /// + /// Show categories created before date (format: 2008-12-31 03:00) + /// + [JsonProperty("created_at_max")] + public DateTime? CreatedAtMax { get; set; } + + /// + /// Show categories last updated after date (format: 2008-12-31 03:00) + /// + [JsonProperty("updated_at_min")] + public DateTime? UpdatedAtMin { get; set; } + + /// + /// Show categories last updated before date (format: 2008-12-31 03:00) + /// + [JsonProperty("updated_at_max")] + public DateTime? UpdatedAtMax { get; set; } + + /// + ///
    + ///
  • published - Show only published categories
  • + ///
  • unpublished - Show only unpublished categories
  • + ///
  • any - Show all categories(default)
  • + ///
+ ///
+ [JsonProperty("published_status")] + public bool? PublishedStatus { get; set; } + + /// + /// Show only the categories to which the product is mapped to + /// + [JsonProperty("product_id")] + public int? ProductId { get; set; } + } +} \ No newline at end of file diff --git a/Nop.Plugin.Api/Models/ManufacturersParameters/ManufacturersCountParametersModel.cs b/Nop.Plugin.Api/Models/ManufacturersParameters/ManufacturersCountParametersModel.cs new file mode 100644 index 0000000..87bf48c --- /dev/null +++ b/Nop.Plugin.Api/Models/ManufacturersParameters/ManufacturersCountParametersModel.cs @@ -0,0 +1,11 @@ +using Nop.Plugin.Api.ModelBinders; +namespace Nop.Plugin.Api.Models.ManufacturersParameters +{ + using Microsoft.AspNetCore.Mvc; + + [ModelBinder(typeof(ParametersModelBinder))] + public class ManufacturersCountParametersModel : BaseManufacturersParametersModel + { + // Nothing special here, created just for clarity. + } +} \ No newline at end of file diff --git a/Nop.Plugin.Api/Models/ManufacturersParameters/ManufacturersParametersModel.cs b/Nop.Plugin.Api/Models/ManufacturersParameters/ManufacturersParametersModel.cs new file mode 100644 index 0000000..c4ffa56 --- /dev/null +++ b/Nop.Plugin.Api/Models/ManufacturersParameters/ManufacturersParametersModel.cs @@ -0,0 +1,58 @@ +using System.Collections.Generic; +using Newtonsoft.Json; +using Nop.Plugin.Api.Constants; +using Nop.Plugin.Api.ModelBinders; +using Microsoft.AspNetCore.Mvc; + +namespace Nop.Plugin.Api.Models.ManufacturersParameters +{ + // JsonProperty is used only for swagger + [ModelBinder(typeof(ParametersModelBinder))] + public class ManufacturersParametersModel : BaseManufacturersParametersModel + { + public ManufacturersParametersModel() + { + Ids = null; + Limit = Configurations.DefaultLimit; + Page = Configurations.DefaultPageValue; + SinceId = Configurations.DefaultSinceId; + Fields = string.Empty; + } + + /// + /// A comma-separated list of manufacturer ids + /// + [JsonProperty("ids")] + public List Ids { get; set; } + + /// + /// Amount of results (default: 50) (maximum: 250) + /// + [JsonProperty("limit")] + public int Limit { get; set; } + + /// + /// Page to show (default: 1) + /// + [JsonProperty("page")] + public int Page { get; set; } + + /// + /// Restrict results to after the specified ID + /// + [JsonProperty("since_id")] + public int SinceId { get; set; } + + /// + /// comma-separated list of fields to include in the response + /// + [JsonProperty("fields")] + public string Fields { get; set; } + + /// + /// comma-separated list of fields to include in the response + /// + [JsonProperty("languageid")] + public int? LanguageId { get; set; } + } +} \ No newline at end of file diff --git a/Nop.Plugin.Api/Models/ProductAttributesParameters/ProductAttributesParametersModel.cs b/Nop.Plugin.Api/Models/ProductAttributesParameters/ProductAttributesParametersModel.cs index 4c5fbe0..690dcbc 100644 --- a/Nop.Plugin.Api/Models/ProductAttributesParameters/ProductAttributesParametersModel.cs +++ b/Nop.Plugin.Api/Models/ProductAttributesParameters/ProductAttributesParametersModel.cs @@ -1,52 +1,52 @@ -using System.Collections.Generic; -using System.Web.Http.ModelBinding; -using Newtonsoft.Json; -using Nop.Plugin.Api.Constants; -using Nop.Plugin.Api.ModelBinders; - -namespace Nop.Plugin.Api.Models.ProductAttributes -{ - // JsonProperty is used only for swagger - [ModelBinder(typeof(ParametersModelBinder))] - public class ProductAttributesParametersModel - { - public ProductAttributesParametersModel() - { - Ids = null; - Limit = Configurations.DefaultLimit; - Page = Configurations.DefaultPageValue; - SinceId = Configurations.DefaultSinceId; - Fields = string.Empty; - } - - /// - /// A comma-separated list of category ids - /// - [JsonProperty("ids")] - public List Ids { get; set; } - - /// - /// Amount of results (default: 50) (maximum: 250) - /// - [JsonProperty("limit")] - public int Limit { get; set; } - - /// - /// Page to show (default: 1) - /// - [JsonProperty("page")] - public int Page { get; set; } - - /// - /// Restrict results to after the specified ID - /// - [JsonProperty("since_id")] - public int SinceId { get; set; } - - /// - /// comma-separated list of fields to include in the response - /// - [JsonProperty("fields")] - public string Fields { get; set; } - } +using System.Collections.Generic; +using Microsoft.AspNetCore.Mvc; +using Newtonsoft.Json; +using Nop.Plugin.Api.Constants; +using Nop.Plugin.Api.ModelBinders; + +namespace Nop.Plugin.Api.Models.ProductAttributes +{ + // JsonProperty is used only for swagger + [ModelBinder(typeof(ParametersModelBinder))] + public class ProductAttributesParametersModel + { + public ProductAttributesParametersModel() + { + Ids = null; + Limit = Configurations.DefaultLimit; + Page = Configurations.DefaultPageValue; + SinceId = Configurations.DefaultSinceId; + Fields = string.Empty; + } + + /// + /// A comma-separated list of category ids + /// + [JsonProperty("ids")] + public List Ids { get; set; } + + /// + /// Amount of results (default: 50) (maximum: 250) + /// + [JsonProperty("limit")] + public int Limit { get; set; } + + /// + /// Page to show (default: 1) + /// + [JsonProperty("page")] + public int Page { get; set; } + + /// + /// Restrict results to after the specified ID + /// + [JsonProperty("since_id")] + public int SinceId { get; set; } + + /// + /// comma-separated list of fields to include in the response + /// + [JsonProperty("fields")] + public string Fields { get; set; } + } } \ No newline at end of file diff --git a/Nop.Plugin.Api/Models/ProductManufacturerMappingsParameters/BaseManufacturerMappingsParametersModel.cs b/Nop.Plugin.Api/Models/ProductManufacturerMappingsParameters/BaseManufacturerMappingsParametersModel.cs new file mode 100644 index 0000000..6a87463 --- /dev/null +++ b/Nop.Plugin.Api/Models/ProductManufacturerMappingsParameters/BaseManufacturerMappingsParametersModel.cs @@ -0,0 +1,20 @@ +using Newtonsoft.Json; + +namespace Nop.Plugin.Api.Models.ProductManufacturerMappingsParameters +{ + // JsonProperty is used only for swagger + public class BaseManufacturerMappingsParametersModel + { + /// + /// Show all the product-manufacturer mappings for this product + /// + [JsonProperty("product_id")] + public int? ProductId { get; set; } + + /// + /// Show all the product-manufacturer mappings for this manufacturer + /// + [JsonProperty("manufacturer_id")] + public int? ManufacturerId { get; set; } + } +} \ No newline at end of file diff --git a/Nop.Plugin.Api/Models/ProductManufacturerMappingsParameters/ProductManufacturerMappingsCountParametersModel.cs b/Nop.Plugin.Api/Models/ProductManufacturerMappingsParameters/ProductManufacturerMappingsCountParametersModel.cs new file mode 100644 index 0000000..4f383b8 --- /dev/null +++ b/Nop.Plugin.Api/Models/ProductManufacturerMappingsParameters/ProductManufacturerMappingsCountParametersModel.cs @@ -0,0 +1,12 @@ +using Nop.Plugin.Api.ModelBinders; + +namespace Nop.Plugin.Api.Models.ProductManufacturerMappingsParameters +{ + using Microsoft.AspNetCore.Mvc; + + [ModelBinder(typeof(ParametersModelBinder))] + public class ProductManufacturerMappingsCountParametersModel : BaseManufacturerMappingsParametersModel + { + // Nothing special here, created just for clarity. + } +} \ No newline at end of file diff --git a/Nop.Plugin.Api/Models/ProductManufacturerMappingsParameters/ProductManufacturerMappingsParametersModel.cs b/Nop.Plugin.Api/Models/ProductManufacturerMappingsParameters/ProductManufacturerMappingsParametersModel.cs new file mode 100644 index 0000000..afc08e6 --- /dev/null +++ b/Nop.Plugin.Api/Models/ProductManufacturerMappingsParameters/ProductManufacturerMappingsParametersModel.cs @@ -0,0 +1,45 @@ +using Newtonsoft.Json; +using Nop.Plugin.Api.Constants; +using Nop.Plugin.Api.ModelBinders; + +namespace Nop.Plugin.Api.Models.ProductManufacturerMappingsParameters +{ + using Microsoft.AspNetCore.Mvc; + + // JsonProperty is used only for swagger + [ModelBinder(typeof(ParametersModelBinder))] + public class ProductManufacturerMappingsParametersModel : BaseManufacturerMappingsParametersModel + { + public ProductManufacturerMappingsParametersModel() + { + SinceId = Configurations.DefaultSinceId; + Page = Configurations.DefaultPageValue; + Limit = Configurations.DefaultLimit; + Fields = string.Empty; + } + + /// + /// Restrict results to after the specified ID + /// + [JsonProperty("since_id")] + public int SinceId { get; set; } + + /// + /// Page to show (default: 1) + /// + [JsonProperty("page")] + public int Page { get; set; } + + /// + /// Amount of results (default: 50) (maximum: 250) + /// + [JsonProperty("limit")] + public int Limit { get; set; } + + /// + /// comma-separated list of fields to include in the response + /// + [JsonProperty("fields")] + public string Fields { get; set; } + } +} \ No newline at end of file diff --git a/Nop.Plugin.Api/Nop.Plugin.Api.csproj b/Nop.Plugin.Api/Nop.Plugin.Api.csproj index c0af2ff..6763ff2 100644 --- a/Nop.Plugin.Api/Nop.Plugin.Api.csproj +++ b/Nop.Plugin.Api/Nop.Plugin.Api.csproj @@ -1,106 +1,86 @@ - - - - net461 - Copyright © Seven Spikes, Ltd - Seven Spikes, Ltd (Nop-Templates.com) - Seven Spikes, Ltd (Nop-Templates.com) - 4.00 - This plugin allows you to access/create Nop resources outside of the system - - - - - ..\..\Presentation\Nop.Web\Plugins\Nop.Plugin.Api - $(OutputPath) - - - - ..\..\Presentation\Nop.Web\Plugins\Nop.Plugin.Api - $(OutputPath) - - - - - - - - - - - - - - - - - - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - - - 6.1.1 - - - 2.0.2 - - - 2.1.0 - - - - - 2.0.0 - - - - 2.0.1 - - - 2.0.1 - - - 10.0.3 - - - - - - - - - - - - - Always - - - Always - - - PreserveNewest - - + + + + netcoreapp2.1 + This plugin allows you to access/create Nop resources outside of the system + true + + + + ..\..\Presentation\Nop.Web\Plugins\Nop.Plugin.Api + $(OutputPath) + true + + + + ..\..\Presentation\Nop.Web\Plugins\Nop.Plugin.Api + $(OutputPath) + true + + + + + + + + + + + + + + + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + + + + + + + + + + + + + + + ..\Libs\Microsoft.AspNetCore.WebHooks.Sender.dll + + + + + + + + + \ No newline at end of file diff --git a/Nop.Plugin.Api/RouteProvider.cs b/Nop.Plugin.Api/RouteProvider.cs index 5ffb10f..d91ed59 100644 --- a/Nop.Plugin.Api/RouteProvider.cs +++ b/Nop.Plugin.Api/RouteProvider.cs @@ -1,51 +1,15 @@ -using Microsoft.AspNetCore.Routing; -using Microsoft.AspNetCore.Builder; -using Nop.Web.Framework.Mvc.Routing; - -namespace Nop.Plugin.Api -{ - public class RouteProvider : IRouteProvider - { - public void RegisterRoutes(IRouteBuilder routeBuilder) - { - //routeBuilder.MapRoute("Plugin.Api.Settings", - // "Admin/ApiAdmin/Settings", - // new { controller = "ApiAdmin", action = "Settings" } - //); - - //routeBuilder.MapRoute("Plugin.Api.ManageClients.List", - // "Admin/ManageClientsAdmin/List", - // new { controller = "ManageClientsAdmin", action = "List" } - //); - - //routeBuilder.MapRoute("Plugin.Api.ManageClients.Create", - // "Admin/ManageClientsAdmin/Create", - // new { controller = "ManageClientsAdmin", action = "Create" } - //); - - //routeBuilder.MapRoute("Plugin.Api.ManageClients.Edit", - // "Admin/ManageClientsAdmin/Edit/{clientId}", - // new { controller = "ManageClientsAdmin", action = "Edit", clientId = "" } - //); - - //routeBuilder.MapRoute("Plugin.Api.ManageClients.Delete", - // "Admin/ManageClientsAdmin/Delete/{clientId}", - // new { controller = "ManageClientsAdmin", action = "Delete", clientId = "" } - //); - - - //IWebConfigMangerHelper webConfigManagerHelper = EngineContext.Current.Resolve(); - - // make sure the connection string is added in the Web.config - //webConfigManagerHelper.AddConnectionString(); - - // make sure the OwinAutomaticAppStartup is enabled in the Web.config - //webConfigManagerHelper.AddConfiguration(); - } - - public int Priority - { - get { return -1; } - } - } -} +using Microsoft.AspNetCore.Routing; +using Nop.Web.Framework.Mvc.Routing; + +namespace Nop.Plugin.Api +{ + public class RouteProvider : IRouteProvider + { + public void RegisterRoutes(IRouteBuilder routeBuilder) + { + + } + + public int Priority => -1; + } +} diff --git a/Nop.Plugin.Api/Services/CategoryApiService.cs b/Nop.Plugin.Api/Services/CategoryApiService.cs index 73f1b88..db2ec85 100644 --- a/Nop.Plugin.Api/Services/CategoryApiService.cs +++ b/Nop.Plugin.Api/Services/CategoryApiService.cs @@ -1,126 +1,119 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Nop.Core.Data; -using Nop.Core.Domain.Catalog; -using Nop.Plugin.Api.Constants; -using Nop.Plugin.Api.DataStructures; -using Nop.Services.Stores; - -namespace Nop.Plugin.Api.Services -{ - public class CategoryApiService : ICategoryApiService - { - private readonly IStoreMappingService _storeMappingService; - private readonly IRepository _categoryRepository; - private readonly IRepository _productCategoryMappingRepository; - - public CategoryApiService(IRepository categoryRepository, - IRepository productCategoryMappingRepository, - IStoreMappingService storeMappingService) - { - _categoryRepository = categoryRepository; - _productCategoryMappingRepository = productCategoryMappingRepository; - _storeMappingService = storeMappingService; - } - - public IList GetCategories(IList ids = null, - DateTime? createdAtMin = null, DateTime? createdAtMax = null, DateTime? updatedAtMin = null, DateTime? updatedAtMax = null, - int limit = Configurations.DefaultLimit, int page = Configurations.DefaultPageValue, int sinceId = Configurations.DefaultSinceId, - int? productId = null, - bool? publishedStatus = null) - { - var query = GetCategoriesQuery(createdAtMin, createdAtMax, updatedAtMin, updatedAtMax, publishedStatus, productId, ids); - - - if (sinceId > 0) - { - query = query.Where(c => c.Id > sinceId); - } - - return new ApiList(query, page - 1, limit); - } - - public Category GetCategoryById(int id) - { - if (id <= 0) - return null; - - Category category = _categoryRepository.Table.FirstOrDefault(cat => cat.Id == id && !cat.Deleted); - - return category; - } - - public int GetCategoriesCount(DateTime? createdAtMin = null, DateTime? createdAtMax = null, - DateTime? updatedAtMin = null, DateTime? updatedAtMax = null, - bool? publishedStatus = null, int? productId = null) - { - var query = GetCategoriesQuery(createdAtMin, createdAtMax, updatedAtMin, updatedAtMax, - publishedStatus, productId); - - return query.ToList().Count(c => _storeMappingService.Authorize(c)); - } - - private IQueryable GetCategoriesQuery( - DateTime? createdAtMin = null, DateTime? createdAtMax = null, DateTime? updatedAtMin = null, DateTime? updatedAtMax = null, - bool? publishedStatus = null, int? productId = null, IList ids = null) - { - var query = _categoryRepository.TableNoTracking; - - if (ids != null && ids.Count > 0) - { - query = query.Where(c => ids.Contains(c.Id)); - } - - if (publishedStatus != null) - { - query = query.Where(c => c.Published == publishedStatus.Value); - } - - query = query.Where(c => !c.Deleted); - - if (createdAtMin != null) - { - query = query.Where(c => c.CreatedOnUtc > createdAtMin.Value); - } - - if (createdAtMax != null) - { - - query = query.Where(c => c.CreatedOnUtc < createdAtMax.Value); - } - - if (updatedAtMin != null) - { - query = query.Where(c => c.UpdatedOnUtc > updatedAtMin.Value); - } - - if (updatedAtMax != null) - { - query = query.Where(c => c.UpdatedOnUtc < updatedAtMax.Value); - } - - //only distinct categories (group by ID) - query = from c in query - group c by c.Id - into cGroup - orderby cGroup.Key - select cGroup.FirstOrDefault(); - - if (productId != null) - { - var categoryMappingsForProduct = from productCategoryMapping in _productCategoryMappingRepository.TableNoTracking - where productCategoryMapping.ProductId == productId - select productCategoryMapping; - - query = from category in query - join productCategoryMapping in categoryMappingsForProduct on category.Id equals productCategoryMapping.CategoryId - select category; - } - - query = query.OrderBy(category => category.Id); - - return query; - } - } +using System; +using System.Collections.Generic; +using System.Linq; +using Nop.Core.Data; +using Nop.Core.Domain.Catalog; +using Nop.Plugin.Api.Constants; +using Nop.Plugin.Api.DataStructures; +using Nop.Services.Stores; + +namespace Nop.Plugin.Api.Services +{ + public class CategoryApiService : ICategoryApiService + { + private readonly IStoreMappingService _storeMappingService; + private readonly IRepository _categoryRepository; + private readonly IRepository _productCategoryMappingRepository; + + public CategoryApiService(IRepository categoryRepository, + IRepository productCategoryMappingRepository, + IStoreMappingService storeMappingService) + { + _categoryRepository = categoryRepository; + _productCategoryMappingRepository = productCategoryMappingRepository; + _storeMappingService = storeMappingService; + } + + public IList GetCategories(IList ids = null, + DateTime? createdAtMin = null, DateTime? createdAtMax = null, DateTime? updatedAtMin = null, DateTime? updatedAtMax = null, + int limit = Configurations.DefaultLimit, int page = Configurations.DefaultPageValue, int sinceId = Configurations.DefaultSinceId, + int? productId = null, + bool? publishedStatus = null) + { + var query = GetCategoriesQuery(createdAtMin, createdAtMax, updatedAtMin, updatedAtMax, publishedStatus, productId, ids); + + + if (sinceId > 0) + { + query = query.Where(c => c.Id > sinceId); + } + + return new ApiList(query, page - 1, limit); + } + + public Category GetCategoryById(int id) + { + if (id <= 0) + return null; + + var category = _categoryRepository.Table.FirstOrDefault(cat => cat.Id == id && !cat.Deleted); + + return category; + } + + public int GetCategoriesCount(DateTime? createdAtMin = null, DateTime? createdAtMax = null, + DateTime? updatedAtMin = null, DateTime? updatedAtMax = null, + bool? publishedStatus = null, int? productId = null) + { + var query = GetCategoriesQuery(createdAtMin, createdAtMax, updatedAtMin, updatedAtMax, + publishedStatus, productId); + + return query.Count(c => _storeMappingService.Authorize(c)); + } + + private IQueryable GetCategoriesQuery( + DateTime? createdAtMin = null, DateTime? createdAtMax = null, DateTime? updatedAtMin = null, DateTime? updatedAtMax = null, + bool? publishedStatus = null, int? productId = null, IList ids = null) + { + var query = _categoryRepository.Table; + + if (ids != null && ids.Count > 0) + { + query = query.Where(c => ids.Contains(c.Id)); + } + + if (publishedStatus != null) + { + query = query.Where(c => c.Published == publishedStatus.Value); + } + + query = query.Where(c => !c.Deleted); + + if (createdAtMin != null) + { + query = query.Where(c => c.CreatedOnUtc > createdAtMin.Value); + } + + if (createdAtMax != null) + { + + query = query.Where(c => c.CreatedOnUtc < createdAtMax.Value); + } + + if (updatedAtMin != null) + { + query = query.Where(c => c.UpdatedOnUtc > updatedAtMin.Value); + } + + if (updatedAtMax != null) + { + query = query.Where(c => c.UpdatedOnUtc < updatedAtMax.Value); + } + + if (productId != null) + { + var categoryMappingsForProduct = from productCategoryMapping in _productCategoryMappingRepository.Table + where productCategoryMapping.ProductId == productId + select productCategoryMapping; + + query = from category in query + join productCategoryMapping in categoryMappingsForProduct on category.Id equals productCategoryMapping.CategoryId + select category; + } + + query = query.OrderBy(category => category.Id); + + return query; + } + } } \ No newline at end of file diff --git a/Nop.Plugin.Api/Services/ClientService.cs b/Nop.Plugin.Api/Services/ClientService.cs index 429d9b0..5ef4644 100644 --- a/Nop.Plugin.Api/Services/ClientService.cs +++ b/Nop.Plugin.Api/Services/ClientService.cs @@ -1,229 +1,229 @@ -using System; -using System.Collections.Generic; -using System.Linq; - -namespace Nop.Plugin.Api.Services -{ - using IdentityModel; - using IdentityServer4; - using IdentityServer4.EntityFramework.Entities; - using IdentityServer4.EntityFramework.Interfaces; - using IdentityServer4.Models; - using Microsoft.EntityFrameworkCore; - using Nop.Plugin.Api.MappingExtensions; - using Nop.Plugin.Api.Models; - using Client = IdentityServer4.EntityFramework.Entities.Client; - - public class ClientService : IClientService - { - private readonly IConfigurationDbContext _configurationDbContext; - - public ClientService(IConfigurationDbContext configurationDbContext) - { - _configurationDbContext = configurationDbContext; - } - - public IList GetAllClients() - { - IQueryable clientsQuery = _configurationDbContext.Clients - .Include(x => x.ClientSecrets) - .Include(x => x.RedirectUris); - - IList clients = clientsQuery.ToList(); - - IList clientApiModels = clients.Select(client => client.ToApiModel()).ToList(); - - return clientApiModels; - } - - public int InsertClient(ClientApiModel model) - { - if (model == null) - { - throw new ArgumentNullException(nameof(model)); - } - - var client = new Client() - { - ClientId = model.ClientId, - Enabled = model.Enabled, - ClientName = model.ClientName, - // Needed to be able to obtain refresh token. - AllowOfflineAccess = true, - AccessTokenLifetime = model.AccessTokenLifetime, - AbsoluteRefreshTokenLifetime = model.RefreshTokenLifetime - }; - - AddOrUpdateClientSecret(client, model.ClientSecret); - AddOrUpdateClientRedirectUrl(client, model.RedirectUrl); - - client.AllowedGrantTypes = new List() - { - new ClientGrantType() - { - Client = client, - GrantType = OidcConstants.GrantTypes.AuthorizationCode - }, - new ClientGrantType() - { - Client = client, - GrantType = OidcConstants.GrantTypes.RefreshToken - }, - new ClientGrantType() - { - Client = client, - GrantType = OidcConstants.GrantTypes.JwtBearer - } - }; - - client.AllowedScopes = new List() - { - new ClientScope() - { - Client = client, - Scope = "nop_api" - } - }; - - client.Claims = new List() - { - new ClientClaim() - { - Client = client, - Type = JwtClaimTypes.Subject, - Value = client.ClientId - }, - new ClientClaim() - { - Client = client, - Type = JwtClaimTypes.Name, - Value = client.ClientName - } - - }; - - _configurationDbContext.Clients.Add(client); - _configurationDbContext.SaveChanges(); - - return client.Id; - } - - public void UpdateClient(ClientApiModel model) - { - if (model == null) - { - throw new ArgumentNullException(nameof(model)); - } - - Client currentClient = _configurationDbContext.Clients - .Include(client => client.ClientSecrets) - .Include(client => client.RedirectUris) - .FirstOrDefault(client => client.ClientId == model.ClientId); - - if (currentClient == null) - { - throw new ArgumentNullException(nameof(currentClient)); - } - - AddOrUpdateClientSecret(currentClient, model.ClientSecret); - AddOrUpdateClientRedirectUrl(currentClient, model.RedirectUrl); - - currentClient.ClientId = model.ClientId; - currentClient.ClientName = model.ClientName; - currentClient.Enabled = model.Enabled; - currentClient.AccessTokenLifetime = model.AccessTokenLifetime; - currentClient.AbsoluteRefreshTokenLifetime = model.RefreshTokenLifetime; - - _configurationDbContext.Clients.Update(currentClient); - _configurationDbContext.SaveChanges(); - } - - public ClientApiModel FindClientByIdAsync(int id) - { - Client currentClient = _configurationDbContext.Clients - .Include(client => client.ClientSecrets) - .Include(client => client.RedirectUris) - .FirstOrDefault(client => client.Id == id); - - return currentClient?.ToApiModel(); - } - - public ClientApiModel FindClientByClientId(string clientId) - { - Client currentClient = _configurationDbContext.Clients - .Include(client => client.ClientSecrets) - .Include(client => client.RedirectUris) - .FirstOrDefault(client => client.ClientId == clientId); - - return currentClient?.ToApiModel(); - } - - public void DeleteClient(int id) - { - Client client = _configurationDbContext.Clients - .Include(entity => entity.ClientSecrets) - .Include(entity => entity.RedirectUris) - .FirstOrDefault(x => x.Id == id); - - if (client != null) - { - _configurationDbContext.Clients.Remove(client); - _configurationDbContext.SaveChanges(); - } - } - - private void AddOrUpdateClientRedirectUrl(Client currentClient, string modelRedirectUrl) - { - // Ensure the client redirect url collection is not null - if (currentClient.RedirectUris == null) - { - currentClient.RedirectUris = new List(); - } - - // Currently, the api works with only one client redirect uri. - ClientRedirectUri currentClientRedirectUri = currentClient.RedirectUris.FirstOrDefault(); - - // Add new redirectUri - if ((currentClientRedirectUri != null && currentClientRedirectUri.RedirectUri != modelRedirectUrl) || - currentClientRedirectUri == null) - { - // Remove all redirect uris as we may have only one. - currentClient.RedirectUris.Clear(); - - currentClient.RedirectUris.Add(new ClientRedirectUri() - { - Client = currentClient, - RedirectUri = modelRedirectUrl - }); - } - } - - private void AddOrUpdateClientSecret(Client currentClient, string modelClientSecretDescription) - { - // Ensure the client secrets collection is not null - if (currentClient.ClientSecrets == null) - { - currentClient.ClientSecrets = new List(); - } - - // Currently, the api works with only one client secret. - ClientSecret currentClientSecret = currentClient.ClientSecrets.FirstOrDefault(); - - // Add new secret - if ((currentClientSecret != null && currentClientSecret.Description != modelClientSecretDescription) || - currentClientSecret == null) - { - // Remove all secrets as we may have only one valid. - currentClient.ClientSecrets.Clear(); - - currentClient.ClientSecrets.Add(new ClientSecret() - { - Client = currentClient, - Value = modelClientSecretDescription.Sha256(), - Type = IdentityServerConstants.ParsedSecretTypes.SharedSecret, - Description = modelClientSecretDescription - }); - } - } - } +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Nop.Plugin.Api.Services +{ + using IdentityModel; + using IdentityServer4; + using IdentityServer4.EntityFramework.Entities; + using IdentityServer4.EntityFramework.Interfaces; + using IdentityServer4.Models; + using Microsoft.EntityFrameworkCore; + using MappingExtensions; + using Models; + using Client = IdentityServer4.EntityFramework.Entities.Client; + + public class ClientService : IClientService + { + private readonly IConfigurationDbContext _configurationDbContext; + + public ClientService(IConfigurationDbContext configurationDbContext) + { + _configurationDbContext = configurationDbContext; + } + + public IList GetAllClients() + { + IQueryable clientsQuery = _configurationDbContext.Clients + .Include(x => x.ClientSecrets) + .Include(x => x.RedirectUris); + + IList clients = clientsQuery.ToList(); + + IList clientApiModels = clients.Select(client => client.ToApiModel()).ToList(); + + return clientApiModels; + } + + public int InsertClient(ClientApiModel model) + { + if (model == null) + { + throw new ArgumentNullException(nameof(model)); + } + + var client = new Client + { + ClientId = model.ClientId, + Enabled = model.Enabled, + ClientName = model.ClientName, + // Needed to be able to obtain refresh token. + AllowOfflineAccess = true, + AccessTokenLifetime = model.AccessTokenLifetime, + AbsoluteRefreshTokenLifetime = model.RefreshTokenLifetime + }; + + AddOrUpdateClientSecret(client, model.ClientSecret); + AddOrUpdateClientRedirectUrl(client, model.RedirectUrl); + + client.AllowedGrantTypes = new List + { + new ClientGrantType + { + Client = client, + GrantType = OidcConstants.GrantTypes.AuthorizationCode + }, + new ClientGrantType + { + Client = client, + GrantType = OidcConstants.GrantTypes.RefreshToken + }, + new ClientGrantType + { + Client = client, + GrantType = OidcConstants.GrantTypes.JwtBearer + } + }; + + client.AllowedScopes = new List + { + new ClientScope + { + Client = client, + Scope = "nop_api" + } + }; + + client.Claims = new List + { + new ClientClaim + { + Client = client, + Type = JwtClaimTypes.Subject, + Value = client.ClientId + }, + new ClientClaim + { + Client = client, + Type = JwtClaimTypes.Name, + Value = client.ClientName + } + + }; + + _configurationDbContext.Clients.Add(client); + _configurationDbContext.SaveChanges(); + + return client.Id; + } + + public void UpdateClient(ClientApiModel model) + { + if (model == null) + { + throw new ArgumentNullException(nameof(model)); + } + + var currentClient = _configurationDbContext.Clients + .Include(client => client.ClientSecrets) + .Include(client => client.RedirectUris) + .FirstOrDefault(client => client.ClientId == model.ClientId); + + if (currentClient == null) + { + throw new ArgumentNullException(nameof(currentClient)); + } + + AddOrUpdateClientSecret(currentClient, model.ClientSecret); + AddOrUpdateClientRedirectUrl(currentClient, model.RedirectUrl); + + currentClient.ClientId = model.ClientId; + currentClient.ClientName = model.ClientName; + currentClient.Enabled = model.Enabled; + currentClient.AccessTokenLifetime = model.AccessTokenLifetime; + currentClient.AbsoluteRefreshTokenLifetime = model.RefreshTokenLifetime; + + _configurationDbContext.Clients.Update(currentClient); + _configurationDbContext.SaveChanges(); + } + + public ClientApiModel FindClientByIdAsync(int id) + { + var currentClient = _configurationDbContext.Clients + .Include(client => client.ClientSecrets) + .Include(client => client.RedirectUris) + .FirstOrDefault(client => client.Id == id); + + return currentClient?.ToApiModel(); + } + + public ClientApiModel FindClientByClientId(string clientId) + { + var currentClient = _configurationDbContext.Clients + .Include(client => client.ClientSecrets) + .Include(client => client.RedirectUris) + .FirstOrDefault(client => client.ClientId == clientId); + + return currentClient?.ToApiModel(); + } + + public void DeleteClient(int id) + { + var client = _configurationDbContext.Clients + .Include(entity => entity.ClientSecrets) + .Include(entity => entity.RedirectUris) + .FirstOrDefault(x => x.Id == id); + + if (client != null) + { + _configurationDbContext.Clients.Remove(client); + _configurationDbContext.SaveChanges(); + } + } + + private void AddOrUpdateClientRedirectUrl(Client currentClient, string modelRedirectUrl) + { + // Ensure the client redirect url collection is not null + if (currentClient.RedirectUris == null) + { + currentClient.RedirectUris = new List(); + } + + // Currently, the api works with only one client redirect uri. + var currentClientRedirectUri = currentClient.RedirectUris.FirstOrDefault(); + + // Add new redirectUri + if ((currentClientRedirectUri != null && currentClientRedirectUri.RedirectUri != modelRedirectUrl) || + currentClientRedirectUri == null) + { + // Remove all redirect uris as we may have only one. + currentClient.RedirectUris.Clear(); + + currentClient.RedirectUris.Add(new ClientRedirectUri + { + Client = currentClient, + RedirectUri = modelRedirectUrl + }); + } + } + + private void AddOrUpdateClientSecret(Client currentClient, string modelClientSecretDescription) + { + // Ensure the client secrets collection is not null + if (currentClient.ClientSecrets == null) + { + currentClient.ClientSecrets = new List(); + } + + // Currently, the api works with only one client secret. + var currentClientSecret = currentClient.ClientSecrets.FirstOrDefault(); + + // Add new secret + if ((currentClientSecret != null && currentClientSecret.Description != modelClientSecretDescription) || + currentClientSecret == null) + { + // Remove all secrets as we may have only one valid. + currentClient.ClientSecrets.Clear(); + + currentClient.ClientSecrets.Add(new ClientSecret + { + Client = currentClient, + Value = modelClientSecretDescription.Sha256(), + Type = IdentityServerConstants.ParsedSecretTypes.SharedSecret, + Description = modelClientSecretDescription + }); + } + } + } } \ No newline at end of file diff --git a/Nop.Plugin.Api/Services/CustomerApiService.cs b/Nop.Plugin.Api/Services/CustomerApiService.cs index d5fa6d5..d5fe55d 100644 --- a/Nop.Plugin.Api/Services/CustomerApiService.cs +++ b/Nop.Plugin.Api/Services/CustomerApiService.cs @@ -1,529 +1,523 @@ -using System; -using System.Collections.Generic; -using Nop.Core.Data; -using Nop.Core.Domain.Customers; -using Nop.Plugin.Api.DTOs.Customers; -using System.Linq; -using System.Linq.Dynamic; -using System.Text.RegularExpressions; -using Nop.Core; -using Nop.Core.Domain.Common; -using Nop.Plugin.Api.Constants; -using Nop.Plugin.Api.DataStructures; -using Nop.Plugin.Api.Helpers; -using Nop.Plugin.Api.MappingExtensions; -using Nop.Services.Localization; -using Nop.Services.Stores; -using Nop.Core.Domain.Messages; -using Nop.Core.Caching; - -namespace Nop.Plugin.Api.Services -{ - public class CustomerApiService : ICustomerApiService - { - private const string FirstName = "firstname"; - private const string LastName = "lastname"; - private const string LanguageId = "languageid"; - private const string DateOfBirth = "dateofbirth"; - private const string Gender = "gender"; - private const string KeyGroup = "customer"; - - private readonly IStoreContext _storeContext; - private readonly ILanguageService _languageService; - private readonly IStoreMappingService _storeMappingService; - private readonly IRepository _customerRepository; - private readonly IRepository _genericAttributeRepository; - private readonly IRepository _subscriptionRepository; - private readonly IStaticCacheManager _cacheManager; - - public CustomerApiService(IRepository customerRepository, - IRepository genericAttributeRepository, - IRepository subscriptionRepository, - IStoreContext storeContext, - ILanguageService languageService, - IStoreMappingService storeMappingService, - IStaticCacheManager staticCacheManager) - { - _customerRepository = customerRepository; - _genericAttributeRepository = genericAttributeRepository; - _subscriptionRepository = subscriptionRepository; - _storeContext = storeContext; - _languageService = languageService; - _storeMappingService = storeMappingService; - _cacheManager = staticCacheManager; - } - - public IList GetCustomersDtos(DateTime? createdAtMin = null, DateTime? createdAtMax = null, int limit = Configurations.DefaultLimit, - int page = Configurations.DefaultPageValue, int sinceId = Configurations.DefaultSinceId) - { - var query = GetCustomersQuery(createdAtMin, createdAtMax, sinceId); - - IList result = HandleCustomerGenericAttributes(null, query, limit, page); - - SetNewsletterSubscribtionStatus(result); - - return result; - } - - public int GetCustomersCount() - { - return _customerRepository.TableNoTracking.Count(customer => !customer.Deleted - && (customer.RegisteredInStoreId == 0 || customer.RegisteredInStoreId == _storeContext.CurrentStore.Id)); - } - - // Need to work with dto object so we can map the first and last name from generic attributes table. - public IList Search(string queryParams = "", string order = Configurations.DefaultOrder, - int page = Configurations.DefaultPageValue, int limit = Configurations.DefaultLimit) - { - IList result = new List(); - - Dictionary searchParams = EnsureSearchQueryIsValid(queryParams, ParseSearchQuery); - - if (searchParams != null) - { - IQueryable query = _customerRepository.TableNoTracking.Where(customer => !customer.Deleted); - - foreach (var searchParam in searchParams) - { - // Skip non existing properties. - if (ReflectionHelper.HasProperty(searchParam.Key, typeof(Customer))) - { - // @0 is a placeholder used by dynamic linq and it is used to prevent possible sql injections. - query = query.Where(string.Format("{0} = @0 || {0}.Contains(@0)", searchParam.Key), searchParam.Value); - } - // The code bellow will search in customer addresses as well. - //else if (HasProperty(searchParam.Key, typeof(Address))) - //{ - // query = query.Where(string.Format("Addresses.Where({0} == @0).Any()", searchParam.Key), searchParam.Value); - //} - } - - result = HandleCustomerGenericAttributes(searchParams, query, limit, page, order); - } - - return result; - } - - public Dictionary GetFirstAndLastNameByCustomerId(int customerId) - { - return _genericAttributeRepository.TableNoTracking.Where( - x => - x.KeyGroup == KeyGroup && x.EntityId == customerId && - (x.Key == FirstName || x.Key == LastName)).ToDictionary(x => x.Key.ToLowerInvariant(), y => y.Value); - } - - public Customer GetCustomerEntityById(int id) - { - Customer customer = _customerRepository.Table.FirstOrDefault(c => c.Id == id && !c.Deleted); - - return customer; - } - - public CustomerDto GetCustomerById(int id, bool showDeleted = false) - { - if (id == 0) - return null; - - // Here we expect to get two records, one for the first name and one for the last name. - List customerAttributeMappings = (from customer in _customerRepository.TableNoTracking - join attribute in _genericAttributeRepository.TableNoTracking on customer.Id equals attribute.EntityId - where customer.Id == id && - attribute.KeyGroup.Equals(KeyGroup, StringComparison.InvariantCultureIgnoreCase) && - (attribute.Key.Equals(FirstName, StringComparison.InvariantCultureIgnoreCase) || - attribute.Key.Equals(LastName, StringComparison.InvariantCultureIgnoreCase) || - attribute.Key.Equals(LanguageId, StringComparison.InvariantCultureIgnoreCase) || - attribute.Key.Equals(DateOfBirth, StringComparison.InvariantCultureIgnoreCase) || - attribute.Key.Equals(Gender, StringComparison.InvariantCultureIgnoreCase)) - select new CustomerAttributeMappingDto() - { - Attribute = attribute, - Customer = customer - }).ToList(); - - CustomerDto customerDto = null; - - // This is in case we have first and last names set for the customer. - if (customerAttributeMappings.Count > 0) - { - Customer customer = customerAttributeMappings.First().Customer; - // The customer object is the same in all mappings. - customerDto = customer.ToDto(); - - var defaultStoreLanguageId = GetDefaultStoreLangaugeId(); - - // If there is no Language Id generic attribute create one with the default language id. - if (!customerAttributeMappings.Any(cam => cam != null && cam.Attribute != null && cam.Attribute.Key.Equals(LanguageId, StringComparison.InvariantCultureIgnoreCase))) - { - GenericAttribute languageId = new GenericAttribute - { - Key = LanguageId, - Value = defaultStoreLanguageId.ToString() - }; - - CustomerAttributeMappingDto customerAttributeMappingDto = new CustomerAttributeMappingDto - { - Customer = customer, - Attribute = languageId - }; - - customerAttributeMappings.Add(customerAttributeMappingDto); - } - - foreach (var mapping in customerAttributeMappings) - { - if (!showDeleted && mapping.Customer.Deleted) - { - continue; - } - - if (mapping.Attribute != null) - { - if (mapping.Attribute.Key.Equals(FirstName, StringComparison.InvariantCultureIgnoreCase)) - { - customerDto.FirstName = mapping.Attribute.Value; - } - else if (mapping.Attribute.Key.Equals(LastName, StringComparison.InvariantCultureIgnoreCase)) - { - customerDto.LastName = mapping.Attribute.Value; - } - else if (mapping.Attribute.Key.Equals(LanguageId, StringComparison.InvariantCultureIgnoreCase)) - { - customerDto.LanguageId = mapping.Attribute.Value; - } - else if(mapping.Attribute.Key.Equals(DateOfBirth, StringComparison.InvariantCultureIgnoreCase)) - { - customerDto.DateOfBirth = string.IsNullOrEmpty(mapping.Attribute.Value) ? (DateTime?)null : DateTime.Parse(mapping.Attribute.Value); - } - else if (mapping.Attribute.Key.Equals(Gender, StringComparison.InvariantCultureIgnoreCase)) - { - customerDto.Gender = mapping.Attribute.Value; - } - } - } - } - else - { - // This is when we do not have first and last name set. - Customer currentCustomer = _customerRepository.TableNoTracking.FirstOrDefault(customer => customer.Id == id); - - if (currentCustomer != null) - { - if (showDeleted || !currentCustomer.Deleted) - { - customerDto = currentCustomer.ToDto(); - } - } - } - - SetNewsletterSubscribtionStatus(customerDto); - - return customerDto; - } - - private Dictionary EnsureSearchQueryIsValid(string query, Func> parseSearchQuery) - { - if (!string.IsNullOrEmpty(query)) - { - return parseSearchQuery(query); - } - - return null; - } - - private Dictionary ParseSearchQuery(string query) - { - var parsedQuery = new Dictionary(); - - string splitPattern = @"(\w+):"; - - var fieldValueList = Regex.Split(query, splitPattern).Where(s => s != String.Empty).ToList(); - - if (fieldValueList.Count < 2) - { - return parsedQuery; - } - - for (int i = 0; i < fieldValueList.Count; i += 2) - { - var field = fieldValueList[i]; - var value = fieldValueList[i + 1]; - - if (!string.IsNullOrEmpty(field) && !string.IsNullOrEmpty(value)) - { - field = field.Replace("_", string.Empty); - parsedQuery.Add(field.Trim(), value.Trim()); - } - } - - return parsedQuery; - } - - /// - /// The idea of this method is to get the first and last name from the GenericAttribute table and to set them in the CustomerDto object. - /// - /// Search parameters is used to shrinc the range of results from the GenericAttibutes table - /// to be only those with specific search parameter (i.e. currently we focus only on first and last name). - /// Query parameter represents the current customer records which we will join with GenericAttributes table. - /// - private IList HandleCustomerGenericAttributes(Dictionary searchParams, IQueryable query, - int limit = Configurations.DefaultLimit, int page = Configurations.DefaultPageValue, string order = Configurations.DefaultOrder) - { - // Here we join the GenericAttribute records with the customers and making sure that we are working only with the attributes - // that are in the customers keyGroup and their keys are either first or last name. - // We are returning a collection with customer record and attribute record. - // It will look something like: - // customer data for customer 1 - // attribute that contains the first name of customer 1 - // attribute that contains the last name of customer 1 - // customer data for customer 2, - // attribute that contains the first name of customer 2 - // attribute that contains the last name of customer 2 - // etc. - - IQueryable> allRecordsGroupedByCustomerId = - (from customer in query - from attribute in _genericAttributeRepository.TableNoTracking - .Where(attr => attr.EntityId == customer.Id && - attr.KeyGroup.Equals(KeyGroup, StringComparison.InvariantCultureIgnoreCase) && - (attr.Key.Equals(FirstName, StringComparison.InvariantCultureIgnoreCase) || - attr.Key.Equals(LastName, StringComparison.InvariantCultureIgnoreCase) || - attr.Key.Equals(LanguageId, StringComparison.InvariantCultureIgnoreCase) || - attr.Key.Equals(DateOfBirth, StringComparison.InvariantCultureIgnoreCase) || - attr.Key.Equals(Gender, StringComparison.InvariantCultureIgnoreCase))).DefaultIfEmpty() - select new CustomerAttributeMappingDto() - { - Attribute = attribute, - Customer = customer - }).GroupBy(x => x.Customer.Id); - - if (searchParams != null && searchParams.Count > 0) - { - if (searchParams.ContainsKey(FirstName)) - { - allRecordsGroupedByCustomerId = GetCustomerAttributesMappingsByKey(allRecordsGroupedByCustomerId, FirstName, searchParams[FirstName]); - } - - if (searchParams.ContainsKey(LastName)) - { - allRecordsGroupedByCustomerId = GetCustomerAttributesMappingsByKey(allRecordsGroupedByCustomerId, LastName, searchParams[LastName]); - } - - if (searchParams.ContainsKey(LanguageId)) - { - allRecordsGroupedByCustomerId = GetCustomerAttributesMappingsByKey(allRecordsGroupedByCustomerId, LanguageId, searchParams[LanguageId]); - } - - if(searchParams.ContainsKey(DateOfBirth)) - { - allRecordsGroupedByCustomerId = GetCustomerAttributesMappingsByKey(allRecordsGroupedByCustomerId, DateOfBirth, searchParams[DateOfBirth]); - } - - if (searchParams.ContainsKey(Gender)) - { - allRecordsGroupedByCustomerId = GetCustomerAttributesMappingsByKey(allRecordsGroupedByCustomerId, Gender, searchParams[Gender]); - } - } - - IList result = GetFullCustomerDtos(allRecordsGroupedByCustomerId, page, limit, order); - - return result; - } - - /// - /// This method is responsible for getting customer dto records with first and last names set from the attribute mappings. - /// - private IList GetFullCustomerDtos(IQueryable> customerAttributesMappings, - int page = Configurations.DefaultPageValue, int limit = Configurations.DefaultLimit, string order = Configurations.DefaultOrder) - { - var customerDtos = new List(); - - customerAttributesMappings = customerAttributesMappings.OrderBy(x => x.Key); - - IList> customerAttributeGroupsList = new ApiList>(customerAttributesMappings, page - 1, limit); - - // Get the default language id for the current store. - var defaultLanguageId = GetDefaultStoreLangaugeId(); - - foreach (var group in customerAttributeGroupsList) - { - IList mappingsForMerge = group.Select(x => x).ToList(); - - CustomerDto customerDto = Merge(mappingsForMerge, defaultLanguageId); - - customerDtos.Add(customerDto); - } - - // Needed so we can apply the order parameter - return customerDtos.AsQueryable().OrderBy(order).ToList(); - } - - private CustomerDto Merge(IList mappingsForMerge, int defaultLanguageId) - { - var customerDto = new CustomerDto(); - - // We expect the customer to be always set. - customerDto = mappingsForMerge.First().Customer.ToDto(); - - List attributes = mappingsForMerge.Select(x => x.Attribute).ToList(); - - // If there is no Language Id generic attribute create one with the default language id. - if (!attributes.Any(atr => atr != null && atr.Key.Equals(LanguageId, StringComparison.InvariantCultureIgnoreCase))) - { - GenericAttribute languageId = new GenericAttribute - { - Key = LanguageId, - Value = defaultLanguageId.ToString() - }; - - attributes.Add(languageId); - } - - foreach (var attribute in attributes) - { - if (attribute != null) - { - if (attribute.Key.Equals(FirstName, StringComparison.InvariantCultureIgnoreCase)) - { - customerDto.FirstName = attribute.Value; - } - else if (attribute.Key.Equals(LastName, StringComparison.InvariantCultureIgnoreCase)) - { - customerDto.LastName = attribute.Value; - } - else if (attribute.Key.Equals(LanguageId, StringComparison.InvariantCultureIgnoreCase)) - { - customerDto.LanguageId = attribute.Value; - } - else if(attribute.Key.Equals(DateOfBirth, StringComparison.InvariantCultureIgnoreCase)) - { - customerDto.DateOfBirth = string.IsNullOrEmpty(attribute.Value) ? (DateTime?)null : DateTime.Parse(attribute.Value); - } - else if (attribute.Key.Equals(Gender, StringComparison.InvariantCultureIgnoreCase)) - { - customerDto.Gender = attribute.Value; - } - } - } - - return customerDto; - } - - private IQueryable> GetCustomerAttributesMappingsByKey( - IQueryable> customerAttributesGroups, string key, string value) - { - // Here we filter the customerAttributesGroups to be only the ones that have the passed key parameter as a key. - var customerAttributesMappingByKey = from @group in customerAttributesGroups - where @group.Select(x => x.Attribute) - .Any(x => x.Key.Equals(key, StringComparison.InvariantCultureIgnoreCase) && - x.Value.Equals(value, StringComparison.InvariantCultureIgnoreCase)) - select @group; - - return customerAttributesMappingByKey; - } - - private IQueryable GetCustomersQuery(DateTime? createdAtMin = null, DateTime? createdAtMax = null, int sinceId = 0) - { - var query = _customerRepository.TableNoTracking.Where(customer => !customer.Deleted && !customer.IsSystemAccount && customer.Active); - - query = query.Where(customer => !customer.CustomerRoles.Any(cr => (cr.Active) && (cr.SystemName == SystemCustomerRoleNames.Guests)) - && (customer.RegisteredInStoreId == 0 || customer.RegisteredInStoreId == _storeContext.CurrentStore.Id)); - - if (createdAtMin != null) - { - query = query.Where(c => c.CreatedOnUtc > createdAtMin.Value); - } - - if (createdAtMax != null) - { - query = query.Where(c => c.CreatedOnUtc < createdAtMax.Value); - } - - query = query.OrderBy(customer => customer.Id); - - if (sinceId > 0) - { - query = query.Where(customer => customer.Id > sinceId); - } - - return query; - } - - private int GetDefaultStoreLangaugeId() - { - // Get the default language id for the current store. - var defaultLanguageId = _storeContext.CurrentStore.DefaultLanguageId; - - if (defaultLanguageId == 0) - { - var allLanguages = _languageService.GetAllLanguages(); - - var storeLanguages = allLanguages.Where(l => - _storeMappingService.Authorize(l, _storeContext.CurrentStore.Id)).ToList(); - - // If there is no language mapped to the current store, get all of the languages, - // and use the one with the first display order. This is a default nopCommerce workflow. - if (storeLanguages.Count == 0) - { - storeLanguages = allLanguages.ToList(); - } - - var defaultLanguage = storeLanguages.OrderBy(l => l.DisplayOrder).First(); - - defaultLanguageId = defaultLanguage.Id; - } - - return defaultLanguageId; - } - - private void SetNewsletterSubscribtionStatus(IList customerDtos) - { - if(customerDtos == null) - { - return; - } - - var allNewsletterCustomerEmail = getAllNewsletterCustomersEmails(); - - foreach(var customerDto in customerDtos) - { - SetNewsletterSubscribtionStatus(customerDto, allNewsletterCustomerEmail); - } - } - - private void SetNewsletterSubscribtionStatus(CustomerDto customerDto, IEnumerable allNewsletterCustomerEmail = null) - { - if(customerDto == null || String.IsNullOrEmpty(customerDto.Email)) - { - return; - } - - if(allNewsletterCustomerEmail == null) - { - allNewsletterCustomerEmail = getAllNewsletterCustomersEmails(); - } - - if (allNewsletterCustomerEmail != null && allNewsletterCustomerEmail.Contains(customerDto.Email.ToLowerInvariant())) - { - customerDto.SubscribedToNewsletter = true; - } - } - - private IEnumerable getAllNewsletterCustomersEmails() - { - return _cacheManager.Get(Configurations.NEWSLETTER_SUBSCRIBERS_KEY, () => - { - IEnumerable subscriberEmails = (from nls in _subscriptionRepository.TableNoTracking - where nls.StoreId == _storeContext.CurrentStore.Id - && nls.Active - select nls.Email).ToList(); - - if (subscriberEmails != null) - { - subscriberEmails = subscriberEmails.Where(e => !String.IsNullOrEmpty(e)).Select(e => e.ToLowerInvariant()); - } - - return subscriberEmails.Where(e => !String.IsNullOrEmpty(e)).Select(e => e.ToLowerInvariant()); - }); - } - } +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using Nop.Core.Data; +using Nop.Core.Domain.Customers; +using Nop.Plugin.Api.DTOs.Customers; +using System.Linq; +using System.Linq.Dynamic.Core; +using System.Text.RegularExpressions; +using Nop.Core; +using Nop.Core.Domain.Common; +using Nop.Plugin.Api.Constants; +using Nop.Plugin.Api.DataStructures; +using Nop.Plugin.Api.Helpers; +using Nop.Plugin.Api.MappingExtensions; +using Nop.Services.Localization; +using Nop.Services.Stores; +using Nop.Core.Domain.Messages; +using Nop.Core.Caching; + +namespace Nop.Plugin.Api.Services +{ + public class CustomerApiService : ICustomerApiService + { + private const string FirstName = "firstname"; + private const string LastName = "lastname"; + private const string LanguageId = "languageid"; + private const string DateOfBirth = "dateofbirth"; + private const string Gender = "gender"; + private const string KeyGroup = "customer"; + + private readonly IStoreContext _storeContext; + private readonly ILanguageService _languageService; + private readonly IStoreMappingService _storeMappingService; + private readonly IRepository _customerRepository; + private readonly IRepository _genericAttributeRepository; + private readonly IRepository _subscriptionRepository; + private readonly IStaticCacheManager _cacheManager; + + public CustomerApiService(IRepository customerRepository, + IRepository genericAttributeRepository, + IRepository subscriptionRepository, + IStoreContext storeContext, + ILanguageService languageService, + IStoreMappingService storeMappingService, + IStaticCacheManager staticCacheManager) + { + _customerRepository = customerRepository; + _genericAttributeRepository = genericAttributeRepository; + _subscriptionRepository = subscriptionRepository; + _storeContext = storeContext; + _languageService = languageService; + _storeMappingService = storeMappingService; + _cacheManager = staticCacheManager; + } + + public IList GetCustomersDtos(DateTime? createdAtMin = null, DateTime? createdAtMax = null, int limit = Configurations.DefaultLimit, + int page = Configurations.DefaultPageValue, int sinceId = Configurations.DefaultSinceId) + { + var query = GetCustomersQuery(createdAtMin, createdAtMax, sinceId); + + var result = HandleCustomerGenericAttributes(null, query, limit, page); + + SetNewsletterSubscribtionStatus(result); + + return result; + } + + public int GetCustomersCount() + { + return _customerRepository.Table.Count(customer => !customer.Deleted + && (customer.RegisteredInStoreId == 0 || customer.RegisteredInStoreId == _storeContext.CurrentStore.Id)); + } + + // Need to work with dto object so we can map the first and last name from generic attributes table. + public IList Search(string queryParams = "", string order = Configurations.DefaultOrder, + int page = Configurations.DefaultPageValue, int limit = Configurations.DefaultLimit) + { + IList result = new List(); + + var searchParams = EnsureSearchQueryIsValid(queryParams, ParseSearchQuery); + + if (searchParams != null) + { + var query = _customerRepository.Table.Where(customer => !customer.Deleted); + + foreach (var searchParam in searchParams) + { + // Skip non existing properties. + if (ReflectionHelper.HasProperty(searchParam.Key, typeof(Customer))) + { + + // @0 is a placeholder used by dynamic linq and it is used to prevent possible sql injections. + query = query.Where(string.Format("{0} = @0 || {0}.Contains(@0)", searchParam.Key), searchParam.Value); + } + // The code bellow will search in customer addresses as well. + //else if (HasProperty(searchParam.Key, typeof(Address))) + //{ + // query = query.Where(string.Format("Addresses.Where({0} == @0).Any()", searchParam.Key), searchParam.Value); + //} + } + + result = HandleCustomerGenericAttributes(searchParams, query, limit, page, order); + } + + return result; + } + + public Dictionary GetFirstAndLastNameByCustomerId(int customerId) + { + return _genericAttributeRepository.Table.Where( + x => + x.KeyGroup == KeyGroup && x.EntityId == customerId && + (x.Key == FirstName || x.Key == LastName)).ToDictionary(x => x.Key.ToLowerInvariant(), y => y.Value); + } + + public Customer GetCustomerEntityById(int id) + { + var customer = _customerRepository.Table.FirstOrDefault(c => c.Id == id && !c.Deleted); + + return customer; + } + + public CustomerDto GetCustomerById(int id, bool showDeleted = false) + { + if (id == 0) + return null; + + // Here we expect to get two records, one for the first name and one for the last name. + var customerAttributeMappings = (from customer in _customerRepository.Table //NoTracking + join attribute in _genericAttributeRepository.Table//NoTracking + on customer.Id equals attribute.EntityId + where customer.Id == id && + attribute.KeyGroup == "Customer" + select new CustomerAttributeMappingDto + { + Attribute = attribute, + Customer = customer + }).ToList(); + + CustomerDto customerDto = null; + + // This is in case we have first and last names set for the customer. + if (customerAttributeMappings.Count > 0) + { + var customer = customerAttributeMappings.First().Customer; + // The customer object is the same in all mappings. + customerDto = customer.ToDto(); + + var defaultStoreLanguageId = GetDefaultStoreLangaugeId(); + + // If there is no Language Id generic attribute create one with the default language id. + if (!customerAttributeMappings.Any(cam => cam?.Attribute != null && cam.Attribute.Key.Equals(LanguageId, StringComparison.InvariantCultureIgnoreCase))) + { + var languageId = new GenericAttribute + { + Key = LanguageId, + Value = defaultStoreLanguageId.ToString() + }; + + var customerAttributeMappingDto = new CustomerAttributeMappingDto + { + Customer = customer, + Attribute = languageId + }; + + customerAttributeMappings.Add(customerAttributeMappingDto); + } + + foreach (var mapping in customerAttributeMappings) + { + if (!showDeleted && mapping.Customer.Deleted) + { + continue; + } + + if (mapping.Attribute != null) + { + if (mapping.Attribute.Key.Equals(FirstName, StringComparison.InvariantCultureIgnoreCase)) + { + customerDto.FirstName = mapping.Attribute.Value; + } + else if (mapping.Attribute.Key.Equals(LastName, StringComparison.InvariantCultureIgnoreCase)) + { + customerDto.LastName = mapping.Attribute.Value; + } + else if (mapping.Attribute.Key.Equals(LanguageId, StringComparison.InvariantCultureIgnoreCase)) + { + customerDto.LanguageId = mapping.Attribute.Value; + } + else if (mapping.Attribute.Key.Equals(DateOfBirth, StringComparison.InvariantCultureIgnoreCase)) + { + customerDto.DateOfBirth = string.IsNullOrEmpty(mapping.Attribute.Value) ? (DateTime?)null : DateTime.Parse(mapping.Attribute.Value); + } + else if (mapping.Attribute.Key.Equals(Gender, StringComparison.InvariantCultureIgnoreCase)) + { + customerDto.Gender = mapping.Attribute.Value; + } + } + } + } + else + { + // This is when we do not have first and last name set. + var currentCustomer = _customerRepository.Table.FirstOrDefault(customer => customer.Id == id); + + if (currentCustomer != null) + { + if (showDeleted || !currentCustomer.Deleted) + { + customerDto = currentCustomer.ToDto(); + } + } + } + + SetNewsletterSubscribtionStatus(customerDto); + + return customerDto; + } + + private Dictionary EnsureSearchQueryIsValid(string query, Func> parseSearchQuery) + { + if (!string.IsNullOrEmpty(query)) + { + return parseSearchQuery(query); + } + + return null; + } + + private Dictionary ParseSearchQuery(string query) + { + var parsedQuery = new Dictionary(); + + var splitPattern = @"(\w+):"; + + var fieldValueList = Regex.Split(query, splitPattern).Where(s => s != String.Empty).ToList(); + + if (fieldValueList.Count < 2) + { + return parsedQuery; + } + + for (var i = 0; i < fieldValueList.Count; i += 2) + { + var field = fieldValueList[i]; + var value = fieldValueList[i + 1]; + + if (!string.IsNullOrEmpty(field) && !string.IsNullOrEmpty(value)) + { + field = field.Replace("_", string.Empty); + parsedQuery.Add(field.Trim(), value.Trim()); + } + } + + return parsedQuery; + } + + /// + /// The idea of this method is to get the first and last name from the GenericAttribute table and to set them in the CustomerDto object. + /// + /// Search parameters is used to shrinc the range of results from the GenericAttibutes table + /// to be only those with specific search parameter (i.e. currently we focus only on first and last name). + /// Query parameter represents the current customer records which we will join with GenericAttributes table. + /// + /// + /// + /// + private IList HandleCustomerGenericAttributes(IReadOnlyDictionary searchParams, IQueryable query, + int limit = Configurations.DefaultLimit, int page = Configurations.DefaultPageValue, string order = Configurations.DefaultOrder) + { + // Here we join the GenericAttribute records with the customers and making sure that we are working only with the attributes + // that are in the customers keyGroup and their keys are either first or last name. + // We are returning a collection with customer record and attribute record. + // It will look something like: + // customer data for customer 1 + // attribute that contains the first name of customer 1 + // attribute that contains the last name of customer 1 + // customer data for customer 2, + // attribute that contains the first name of customer 2 + // attribute that contains the last name of customer 2 + // etc. + + var allRecordsGroupedByCustomerId = + (from customer in query + from attribute in _genericAttributeRepository.Table + .Where(attr => attr.EntityId == customer.Id && + attr.KeyGroup == "Customer").DefaultIfEmpty() + select new CustomerAttributeMappingDto + { + Attribute = attribute, + Customer = customer + }).GroupBy(x => x.Customer.Id); + + if (searchParams != null && searchParams.Count > 0) + { + if (searchParams.ContainsKey(FirstName)) + { + allRecordsGroupedByCustomerId = GetCustomerAttributesMappingsByKey(allRecordsGroupedByCustomerId, FirstName, searchParams[FirstName]); + } + + if (searchParams.ContainsKey(LastName)) + { + allRecordsGroupedByCustomerId = GetCustomerAttributesMappingsByKey(allRecordsGroupedByCustomerId, LastName, searchParams[LastName]); + } + + if (searchParams.ContainsKey(LanguageId)) + { + allRecordsGroupedByCustomerId = GetCustomerAttributesMappingsByKey(allRecordsGroupedByCustomerId, LanguageId, searchParams[LanguageId]); + } + + if (searchParams.ContainsKey(DateOfBirth)) + { + allRecordsGroupedByCustomerId = GetCustomerAttributesMappingsByKey(allRecordsGroupedByCustomerId, DateOfBirth, searchParams[DateOfBirth]); + } + + if (searchParams.ContainsKey(Gender)) + { + allRecordsGroupedByCustomerId = GetCustomerAttributesMappingsByKey(allRecordsGroupedByCustomerId, Gender, searchParams[Gender]); + } + } + + var result = GetFullCustomerDtos(allRecordsGroupedByCustomerId, page, limit, order); + + return result; + } + + /// + /// This method is responsible for getting customer dto records with first and last names set from the attribute mappings. + /// + private IList GetFullCustomerDtos(IQueryable> customerAttributesMappings, + int page = Configurations.DefaultPageValue, int limit = Configurations.DefaultLimit, string order = Configurations.DefaultOrder) + { + var customerDtos = new List(); + + customerAttributesMappings = customerAttributesMappings.OrderBy(x => x.Key); + + IList> customerAttributeGroupsList = new ApiList>(customerAttributesMappings, page - 1, limit); + + // Get the default language id for the current store. + var defaultLanguageId = GetDefaultStoreLangaugeId(); + + foreach (var group in customerAttributeGroupsList) + { + IList mappingsForMerge = group.Select(x => x).ToList(); + + var customerDto = Merge(mappingsForMerge, defaultLanguageId); + + customerDtos.Add(customerDto); + } + + // Needed so we can apply the order parameter + return customerDtos.AsQueryable().OrderBy(order).ToList(); + } + + private static CustomerDto Merge(IList mappingsForMerge, int defaultLanguageId) + { + // We expect the customer to be always set. + var customerDto = mappingsForMerge.First().Customer.ToDto(); + + var attributes = mappingsForMerge.Select(x => x.Attribute).ToList(); + + // If there is no Language Id generic attribute create one with the default language id. + if (!attributes.Any(atr => atr != null && atr.Key.Equals(LanguageId, StringComparison.InvariantCultureIgnoreCase))) + { + var languageId = new GenericAttribute + { + Key = LanguageId, + Value = defaultLanguageId.ToString() + }; + + attributes.Add(languageId); + } + + foreach (var attribute in attributes) + { + if (attribute != null) + { + if (attribute.Key.Equals(FirstName, StringComparison.InvariantCultureIgnoreCase)) + { + customerDto.FirstName = attribute.Value; + } + else if (attribute.Key.Equals(LastName, StringComparison.InvariantCultureIgnoreCase)) + { + customerDto.LastName = attribute.Value; + } + else if (attribute.Key.Equals(LanguageId, StringComparison.InvariantCultureIgnoreCase)) + { + customerDto.LanguageId = attribute.Value; + } + else if (attribute.Key.Equals(DateOfBirth, StringComparison.InvariantCultureIgnoreCase)) + { + customerDto.DateOfBirth = string.IsNullOrEmpty(attribute.Value) ? (DateTime?)null : DateTime.Parse(attribute.Value); + } + else if (attribute.Key.Equals(Gender, StringComparison.InvariantCultureIgnoreCase)) + { + customerDto.Gender = attribute.Value; + } + } + } + + return customerDto; + } + + private IQueryable> GetCustomerAttributesMappingsByKey( + IQueryable> customerAttributesGroups, string key, string value) + { + // Here we filter the customerAttributesGroups to be only the ones that have the passed key parameter as a key. + var customerAttributesMappingByKey = from @group in customerAttributesGroups + where @group.Select(x => x.Attribute) + .Any(x => x.Key.Equals(key, StringComparison.InvariantCultureIgnoreCase) && + x.Value.Equals(value, StringComparison.InvariantCultureIgnoreCase)) + select @group; + + return customerAttributesMappingByKey; + } + + private IQueryable GetCustomersQuery(DateTime? createdAtMin = null, DateTime? createdAtMax = null, int sinceId = 0) + { + var query = _customerRepository.Table //NoTracking + .Where(customer => !customer.Deleted && !customer.IsSystemAccount && customer.Active); + + query = query.Where(customer => !customer.CustomerCustomerRoleMappings.Any(ccrm => ccrm.CustomerRole.Active && ccrm.CustomerRole.SystemName == NopCustomerDefaults.GuestsRoleName) + && (customer.RegisteredInStoreId == 0 || customer.RegisteredInStoreId == _storeContext.CurrentStore.Id)); + + if (createdAtMin != null) + { + query = query.Where(c => c.CreatedOnUtc > createdAtMin.Value); + } + + if (createdAtMax != null) + { + query = query.Where(c => c.CreatedOnUtc < createdAtMax.Value); + } + + query = query.OrderBy(customer => customer.Id); + + if (sinceId > 0) + { + query = query.Where(customer => customer.Id > sinceId); + } + + return query; + } + + private int GetDefaultStoreLangaugeId() + { + // Get the default language id for the current store. + var defaultLanguageId = _storeContext.CurrentStore.DefaultLanguageId; + + if (defaultLanguageId == 0) + { + var allLanguages = _languageService.GetAllLanguages(); + + var storeLanguages = allLanguages.Where(l => + _storeMappingService.Authorize(l, _storeContext.CurrentStore.Id)).ToList(); + + // If there is no language mapped to the current store, get all of the languages, + // and use the one with the first display order. This is a default nopCommerce workflow. + if (storeLanguages.Count == 0) + { + storeLanguages = allLanguages.ToList(); + } + + var defaultLanguage = storeLanguages.OrderBy(l => l.DisplayOrder).First(); + + defaultLanguageId = defaultLanguage.Id; + } + + return defaultLanguageId; + } + + [SuppressMessage("ReSharper", "PossibleMultipleEnumeration")] + private void SetNewsletterSubscribtionStatus(IList customerDtos) + { + if (customerDtos == null) + { + return; + } + + var allNewsletterCustomerEmail = GetAllNewsletterCustomersEmails(); + + foreach (var customerDto in customerDtos) + { + SetNewsletterSubscribtionStatus(customerDto, allNewsletterCustomerEmail); + } + } + + private void SetNewsletterSubscribtionStatus(BaseCustomerDto customerDto, IEnumerable allNewsletterCustomerEmail = null) + { + if (customerDto == null || String.IsNullOrEmpty(customerDto.Email)) + { + return; + } + + if (allNewsletterCustomerEmail == null) + { + allNewsletterCustomerEmail = GetAllNewsletterCustomersEmails(); + } + + if (allNewsletterCustomerEmail != null && allNewsletterCustomerEmail.Contains(customerDto.Email.ToLowerInvariant())) + { + customerDto.SubscribedToNewsletter = true; + } + } + + private IEnumerable GetAllNewsletterCustomersEmails() + { + return _cacheManager.Get(Configurations.NEWSLETTER_SUBSCRIBERS_KEY, () => + { + IEnumerable subscriberEmails = (from nls in _subscriptionRepository.Table + where nls.StoreId == _storeContext.CurrentStore.Id + && nls.Active + select nls.Email).ToList(); + + + subscriberEmails = subscriberEmails.Where(e => !String.IsNullOrEmpty(e)).Select(e => e.ToLowerInvariant()); + + return subscriberEmails.Where(e => !String.IsNullOrEmpty(e)).Select(e => e.ToLowerInvariant()); + }); + } + } } \ No newline at end of file diff --git a/Nop.Plugin.Api/Services/IClientService.cs b/Nop.Plugin.Api/Services/IClientService.cs index ea5b971..8e448da 100644 --- a/Nop.Plugin.Api/Services/IClientService.cs +++ b/Nop.Plugin.Api/Services/IClientService.cs @@ -1,16 +1,16 @@ -using System.Collections.Generic; - -namespace Nop.Plugin.Api.Services -{ - using Nop.Plugin.Api.Models; - - public interface IClientService - { - IList GetAllClients(); - void DeleteClient(int id); - int InsertClient(ClientApiModel model); - void UpdateClient(ClientApiModel model); - ClientApiModel FindClientByIdAsync(int id); - ClientApiModel FindClientByClientId(string clientId); - } +using System.Collections.Generic; + +namespace Nop.Plugin.Api.Services +{ + using Models; + + public interface IClientService + { + IList GetAllClients(); + void DeleteClient(int id); + int InsertClient(ClientApiModel model); + void UpdateClient(ClientApiModel model); + ClientApiModel FindClientByIdAsync(int id); + ClientApiModel FindClientByClientId(string clientId); + } } \ No newline at end of file diff --git a/Nop.Plugin.Api/Services/IManufacturerApiService.cs b/Nop.Plugin.Api/Services/IManufacturerApiService.cs new file mode 100644 index 0000000..6875587 --- /dev/null +++ b/Nop.Plugin.Api/Services/IManufacturerApiService.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using Nop.Core.Domain.Catalog; +using Nop.Plugin.Api.Constants; + +namespace Nop.Plugin.Api.Services +{ + public interface IManufacturerApiService + { + Manufacturer GetManufacturerById(int manufacturerId); + + IList GetManufacturers(IList ids = null, + DateTime? createdAtMin = null, DateTime? createdAtMax = null, DateTime? updatedAtMin = null, DateTime? updatedAtMax = null, + int limit = Configurations.DefaultLimit, int page = Configurations.DefaultPageValue, int sinceId = Configurations.DefaultSinceId, + int? productId = null, bool? publishedStatus = null, int? languageId = null); + + int GetManufacturersCount(DateTime? createdAtMin = null, DateTime? createdAtMax = null, DateTime? updatedAtMin = null, DateTime? updatedAtMax = null, + bool? publishedStatus = null, int? productId = null); + } +} \ No newline at end of file diff --git a/Nop.Plugin.Api/Services/IProductManufacturerMappingsApiService.cs b/Nop.Plugin.Api/Services/IProductManufacturerMappingsApiService.cs new file mode 100644 index 0000000..eca7627 --- /dev/null +++ b/Nop.Plugin.Api/Services/IProductManufacturerMappingsApiService.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; +using Nop.Core.Domain.Catalog; +using Nop.Plugin.Api.Constants; + +namespace Nop.Plugin.Api.Services +{ + public interface IProductManufacturerMappingsApiService + { + IList GetMappings(int? productId = null, int? manufacturerId = null, int limit = Configurations.DefaultLimit, + int page = Configurations.DefaultPageValue, int sinceId = Configurations.DefaultSinceId); + + int GetMappingsCount(int? productId = null, int? manufacturerId = null); + + ProductManufacturer GetById(int id); + } +} \ No newline at end of file diff --git a/Nop.Plugin.Api/Services/IWebHookService.cs b/Nop.Plugin.Api/Services/IWebHookService.cs index 8d098cb..7920fd6 100644 --- a/Nop.Plugin.Api/Services/IWebHookService.cs +++ b/Nop.Plugin.Api/Services/IWebHookService.cs @@ -1,12 +1,12 @@ -namespace Nop.Plugin.Api.Services -{ - using Microsoft.AspNet.WebHooks; - - public interface IWebHookService - { - IWebHookManager GetWebHookManager(); - IWebHookSender GetWebHookSender(); - IWebHookStore GetWebHookStore(); - IWebHookFilterManager GetWebHookFilterManager(); - } -} +namespace Nop.Plugin.Api.Services +{ + using Microsoft.AspNetCore.WebHooks; + + public interface IWebHookService + { + IWebHookManager GetWebHookManager(); + IWebHookSender GetWebHookSender(); + IWebHookStore GetWebHookStore(); + IWebHookFilterManager GetWebHookFilterManager(); + } +} diff --git a/Nop.Plugin.Api/Services/ManufacturerApiService.cs b/Nop.Plugin.Api/Services/ManufacturerApiService.cs new file mode 100644 index 0000000..edbe708 --- /dev/null +++ b/Nop.Plugin.Api/Services/ManufacturerApiService.cs @@ -0,0 +1,131 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Nop.Core.Data; +using Nop.Core.Domain.Catalog; +using Nop.Core.Domain.Localization; +using Nop.Plugin.Api.Constants; +using Nop.Plugin.Api.DataStructures; +using Nop.Services.Stores; + +namespace Nop.Plugin.Api.Services +{ + public class ManufacturerApiService : IManufacturerApiService + { + private readonly IRepository _manufacturerRepository; + private readonly IRepository _productManufacturerMappingRepository; + private readonly IRepository _localizedPropertyRepository; + + public ManufacturerApiService(IRepository manufacturerRepository, + IRepository productManufacturerMappingRepository, + IRepository localizedPropertyRepository) + { + _manufacturerRepository = manufacturerRepository; + _productManufacturerMappingRepository = productManufacturerMappingRepository; + _localizedPropertyRepository = localizedPropertyRepository; + } + + public IList GetManufacturers(IList ids = null, + DateTime? createdAtMin = null, DateTime? createdAtMax = null, DateTime? updatedAtMin = null, DateTime? updatedAtMax = null, + int limit = Configurations.DefaultLimit, int page = Configurations.DefaultPageValue, int sinceId = Configurations.DefaultSinceId, + int? productId = null, bool? publishedStatus = null, int? languageId = null) + { + var query = GetManufacturersQuery(createdAtMin, createdAtMax, updatedAtMin, updatedAtMax, publishedStatus, productId, ids); + + if (sinceId > 0) + { + query = query.Where(c => c.Id > sinceId); + } + var list = new ApiList(query, page - 1, limit); + + if (languageId.HasValue) + { + var localizedNames = _localizedPropertyRepository.TableNoTracking.Where(x => x.LocaleKeyGroup == "Manufacturer" && languageId == languageId.Value); + foreach (var cat in list) + { + var localizedName = localizedNames.FirstOrDefault(x => x.EntityId == cat.Id); + if (localizedName != null) + cat.Name = localizedName.LocaleValue; + } + } + + return list; + //return new ApiList(query, page - 1, limit); + } + + public Manufacturer GetManufacturerById(int id) + { + if (id <= 0) + return null; + + var manufacturer = _manufacturerRepository.Table.FirstOrDefault(man => man.Id == id && !man.Deleted); + + return manufacturer; + } + + public int GetManufacturersCount(DateTime? createdAtMin = null, DateTime? createdAtMax = null, + DateTime? updatedAtMin = null, DateTime? updatedAtMax = null, + bool? publishedStatus = null, int? productId = null) + { + var query = GetManufacturersQuery(createdAtMin, createdAtMax, updatedAtMin, updatedAtMax, + publishedStatus, productId); + + return query.Count(); + } + + private IQueryable GetManufacturersQuery( + DateTime? createdAtMin = null, DateTime? createdAtMax = null, DateTime? updatedAtMin = null, DateTime? updatedAtMax = null, + bool? publishedStatus = null, int? productId = null, IList ids = null) + { + var query = _manufacturerRepository.Table; + + if (ids != null && ids.Count > 0) + { + query = query.Where(c => ids.Contains(c.Id)); + } + + if (publishedStatus != null) + { + query = query.Where(c => c.Published == publishedStatus.Value); + } + + query = query.Where(c => !c.Deleted); + + if (createdAtMin != null) + { + query = query.Where(c => c.CreatedOnUtc > createdAtMin.Value); + } + + if (createdAtMax != null) + { + + query = query.Where(c => c.CreatedOnUtc < createdAtMax.Value); + } + + if (updatedAtMin != null) + { + query = query.Where(c => c.UpdatedOnUtc > updatedAtMin.Value); + } + + if (updatedAtMax != null) + { + query = query.Where(c => c.UpdatedOnUtc < updatedAtMax.Value); + } + + if (productId != null) + { + var manufacturerMappingsForProduct = from productManufacturerMapping in _productManufacturerMappingRepository.Table + where productManufacturerMapping.ProductId == productId + select productManufacturerMapping; + + query = from manufacturer in query + join productManufacturerMapping in manufacturerMappingsForProduct on manufacturer.Id equals productManufacturerMapping.ManufacturerId + select manufacturer; + } + + query = query.OrderBy(manufacturer => manufacturer.Id); + + return query; + } + } +} \ No newline at end of file diff --git a/Nop.Plugin.Api/Services/NewsLetterSubscriptionApiService.cs b/Nop.Plugin.Api/Services/NewsLetterSubscriptionApiService.cs index 8bcf3b2..dcd5c20 100644 --- a/Nop.Plugin.Api/Services/NewsLetterSubscriptionApiService.cs +++ b/Nop.Plugin.Api/Services/NewsLetterSubscriptionApiService.cs @@ -1,62 +1,62 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Nop.Core; -using Nop.Core.Data; -using Nop.Core.Domain.Messages; -using Nop.Plugin.Api.Constants; -using Nop.Plugin.Api.DataStructures; - -namespace Nop.Plugin.Api.Services -{ - public class NewsLetterSubscriptionApiService : INewsLetterSubscriptionApiService - { - private readonly IRepository _newsLetterSubscriptionRepository; - private readonly IStoreContext _storeContext; - - public NewsLetterSubscriptionApiService(IRepository newsLetterSubscriptionRepository, IStoreContext storeContext) - { - _newsLetterSubscriptionRepository = newsLetterSubscriptionRepository; - _storeContext = storeContext; - } - - public List GetNewsLetterSubscriptions(DateTime? createdAtMin = null, DateTime? createdAtMax = null, - int limit = Configurations.DefaultLimit, int page = Configurations.DefaultPageValue, int sinceId = Configurations.DefaultSinceId, - bool? onlyActive = true) - { - var query = GetNewsLetterSubscriptionsQuery(createdAtMin, createdAtMax, onlyActive); - - if (sinceId > 0) - { - query = query.Where(c => c.Id > sinceId); - } - - return new ApiList(query, page - 1, limit); - } - - private IQueryable GetNewsLetterSubscriptionsQuery(DateTime? createdAtMin = null, DateTime? createdAtMax = null, bool? onlyActive = true) - { - var query = _newsLetterSubscriptionRepository.TableNoTracking.Where(nls => nls.StoreId == _storeContext.CurrentStore.Id); - - if (onlyActive != null && onlyActive == true) - { - query = query.Where(nls => nls.Active == onlyActive); - } - - if (createdAtMin != null) - { - query = query.Where(c => c.CreatedOnUtc > createdAtMin.Value); - } - - if (createdAtMax != null) - { - - query = query.Where(c => c.CreatedOnUtc < createdAtMax.Value); - } - - query = query.OrderBy(nls => nls.Id); - - return query; - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using Nop.Core; +using Nop.Core.Data; +using Nop.Core.Domain.Messages; +using Nop.Plugin.Api.Constants; +using Nop.Plugin.Api.DataStructures; + +namespace Nop.Plugin.Api.Services +{ + public class NewsLetterSubscriptionApiService : INewsLetterSubscriptionApiService + { + private readonly IRepository _newsLetterSubscriptionRepository; + private readonly IStoreContext _storeContext; + + public NewsLetterSubscriptionApiService(IRepository newsLetterSubscriptionRepository, IStoreContext storeContext) + { + _newsLetterSubscriptionRepository = newsLetterSubscriptionRepository; + _storeContext = storeContext; + } + + public List GetNewsLetterSubscriptions(DateTime? createdAtMin = null, DateTime? createdAtMax = null, + int limit = Configurations.DefaultLimit, int page = Configurations.DefaultPageValue, int sinceId = Configurations.DefaultSinceId, + bool? onlyActive = true) + { + var query = GetNewsLetterSubscriptionsQuery(createdAtMin, createdAtMax, onlyActive); + + if (sinceId > 0) + { + query = query.Where(c => c.Id > sinceId); + } + + return new ApiList(query, page - 1, limit); + } + + private IQueryable GetNewsLetterSubscriptionsQuery(DateTime? createdAtMin = null, DateTime? createdAtMax = null, bool? onlyActive = true) + { + var query = _newsLetterSubscriptionRepository.Table.Where(nls => nls.StoreId == _storeContext.CurrentStore.Id); + + if (onlyActive != null && onlyActive == true) + { + query = query.Where(nls => nls.Active == onlyActive); + } + + if (createdAtMin != null) + { + query = query.Where(c => c.CreatedOnUtc > createdAtMin.Value); + } + + if (createdAtMax != null) + { + + query = query.Where(c => c.CreatedOnUtc < createdAtMax.Value); + } + + query = query.OrderBy(nls => nls.Id); + + return query; + } + } +} diff --git a/Nop.Plugin.Api/Services/OrderApiService.cs b/Nop.Plugin.Api/Services/OrderApiService.cs index 6a3f995..56b2d9b 100644 --- a/Nop.Plugin.Api/Services/OrderApiService.cs +++ b/Nop.Plugin.Api/Services/OrderApiService.cs @@ -1,117 +1,128 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Nop.Core.Data; -using Nop.Core.Domain.Orders; -using Nop.Core.Domain.Payments; -using Nop.Core.Domain.Shipping; -using Nop.Plugin.Api.Constants; -using Nop.Plugin.Api.DataStructures; - -namespace Nop.Plugin.Api.Services -{ - public class OrderApiService : IOrderApiService - { - private readonly IRepository _orderRepository; - - public OrderApiService(IRepository orderRepository) - { - _orderRepository = orderRepository; - } - - public IList GetOrdersByCustomerId(int customerId) - { - var query = from order in _orderRepository.TableNoTracking - where order.CustomerId == customerId && !order.Deleted - orderby order.Id - select order; - - return new ApiList(query, 0, Configurations.MaxLimit); - } - - public IList GetOrders(IList ids = null, DateTime? createdAtMin = null, DateTime? createdAtMax = null, - int limit = Configurations.DefaultLimit, int page = Configurations.DefaultPageValue, int sinceId = Configurations.DefaultSinceId, - OrderStatus? status = null, PaymentStatus? paymentStatus = null, ShippingStatus? shippingStatus = null, int? customerId = null, - int? storeId = null) - { - var query = GetOrdersQuery(createdAtMin, createdAtMax, status, paymentStatus, shippingStatus, ids, customerId, storeId); - - if (sinceId > 0) - { - query = query.Where(order => order.Id > sinceId); - } - - return new ApiList(query, page - 1, limit); - } - - public Order GetOrderById(int orderId) - { - if (orderId <= 0) - return null; - - return _orderRepository.Table.FirstOrDefault(order => order.Id == orderId && !order.Deleted); - } - - public int GetOrdersCount(DateTime? createdAtMin = null, DateTime? createdAtMax = null, OrderStatus? status = null, - PaymentStatus? paymentStatus = null, ShippingStatus? shippingStatus = null, - int? customerId = null, int? storeId = null) - { - var query = GetOrdersQuery(createdAtMin, createdAtMax, status, paymentStatus, shippingStatus, customerId: customerId, storeId: storeId); - - return query.Count(); - } - - private IQueryable GetOrdersQuery(DateTime? createdAtMin = null, DateTime? createdAtMax = null, OrderStatus? status = null, - PaymentStatus? paymentStatus = null, ShippingStatus? shippingStatus = null, IList ids = null, - int? customerId = null, int? storeId = null) - { - var query = _orderRepository.TableNoTracking; - - if (customerId != null) - { - query = query.Where(order => order.CustomerId == customerId); - } - - if (ids != null && ids.Count > 0) - { - query = query.Where(c => ids.Contains(c.Id)); - } - - if (status != null) - { - query = query.Where(order => order.OrderStatusId == (int)status); - } - - if (paymentStatus != null) - { - query = query.Where(order => order.PaymentStatusId == (int)paymentStatus); - } - - if (shippingStatus != null) - { - query = query.Where(order => order.ShippingStatusId == (int)shippingStatus); - } - - query = query.Where(order => !order.Deleted); - - if (createdAtMin != null) - { - query = query.Where(order => order.CreatedOnUtc > createdAtMin.Value.ToUniversalTime()); - } - - if (createdAtMax != null) - { - query = query.Where(order => order.CreatedOnUtc < createdAtMax.Value.ToUniversalTime()); - } - - if (storeId != null) - { - query = query.Where(order => order.StoreId == storeId); - } - - query = query.OrderBy(order => order.Id); - - return query; - } - } +using System; +using System.Collections.Generic; +using System.Linq; +using Nop.Core.Data; +using Nop.Core.Domain.Orders; +using Nop.Core.Domain.Payments; +using Nop.Core.Domain.Shipping; +using Nop.Plugin.Api.Constants; +using Nop.Plugin.Api.DataStructures; + +namespace Nop.Plugin.Api.Services +{ + public class OrderApiService : IOrderApiService + { + private readonly IRepository _orderRepository; + + public OrderApiService(IRepository orderRepository) + { + _orderRepository = orderRepository; + } + + public IList GetOrdersByCustomerId(int customerId) + { + var query = from order in _orderRepository.Table + where order.CustomerId == customerId && !order.Deleted + orderby order.Id + select order; + + return new ApiList(query, 0, Configurations.MaxLimit); + } + + public IList GetOrders(IList ids = null, DateTime? createdAtMin = null, DateTime? createdAtMax = null, + int limit = Configurations.DefaultLimit, int page = Configurations.DefaultPageValue, int sinceId = Configurations.DefaultSinceId, + OrderStatus? status = null, PaymentStatus? paymentStatus = null, ShippingStatus? shippingStatus = null, int? customerId = null, + int? storeId = null) + { + var query = GetOrdersQuery(createdAtMin, createdAtMax, status, paymentStatus, shippingStatus, ids, customerId, storeId); + + if (sinceId > 0) + { + query = query.Where(order => order.Id > sinceId); + } + + return new ApiList(query, page - 1, limit); + } + + public Order GetOrderById(int orderId) + { + if (orderId <= 0) + return null; + + return _orderRepository.Table.FirstOrDefault(order => order.Id == orderId && !order.Deleted); + } + + public int GetOrdersCount(DateTime? createdAtMin = null, DateTime? createdAtMax = null, OrderStatus? status = null, + PaymentStatus? paymentStatus = null, ShippingStatus? shippingStatus = null, + int? customerId = null, int? storeId = null) + { + var query = GetOrdersQuery(createdAtMin, createdAtMax, status, paymentStatus, shippingStatus, customerId: customerId, storeId: storeId); + + return query.Count(); + } + + private IQueryable GetOrdersQuery(DateTime? createdAtMin = null, DateTime? createdAtMax = null, OrderStatus? status = null, + PaymentStatus? paymentStatus = null, ShippingStatus? shippingStatus = null, IList ids = null, + int? customerId = null, int? storeId = null) + { + var query = _orderRepository.Table; + + if (customerId != null) + { + query = query.Where(order => order.CustomerId == customerId); + } + + if (ids != null && ids.Count > 0) + { + query = query.Where(c => ids.Contains(c.Id)); + } + + if (status != null) + { + query = query.Where(order => order.OrderStatusId == (int)status); + } + + if (paymentStatus != null) + { + query = query.Where(order => order.PaymentStatusId == (int)paymentStatus); + } + + if (shippingStatus != null) + { + query = query.Where(order => order.ShippingStatusId == (int)shippingStatus); + } + + query = query.Where(order => !order.Deleted); + + if (createdAtMin != null) + { + query = query.Where(order => order.CreatedOnUtc > createdAtMin.Value.ToUniversalTime()); + } + + if (createdAtMax != null) + { + query = query.Where(order => order.CreatedOnUtc < createdAtMax.Value.ToUniversalTime()); + } + + if (storeId != null) + { + query = query.Where(order => order.StoreId == storeId); + } + + query = query.OrderBy(order => order.Id); + + //query = query.Include(c => c.Customer); + //query = query.Include(c => c.BillingAddress); + //query = query.Include(c => c.ShippingAddress); + //query = query.Include(c => c.PickupAddress); + //query = query.Include(c => c.RedeemedRewardPointsEntry); + //query = query.Include(c => c.DiscountUsageHistory); + //query = query.Include(c => c.GiftCardUsageHistory); + //query = query.Include(c => c.OrderNotes); + //query = query.Include(c => c.OrderItems); + //query = query.Include(c => c.Shipments); + + return query; + } + } } \ No newline at end of file diff --git a/Nop.Plugin.Api/Services/ProductApiService.cs b/Nop.Plugin.Api/Services/ProductApiService.cs index f7ca2a1..0a55e9e 100644 --- a/Nop.Plugin.Api/Services/ProductApiService.cs +++ b/Nop.Plugin.Api/Services/ProductApiService.cs @@ -1,143 +1,136 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Nop.Core.Data; -using Nop.Core.Domain.Catalog; -using Nop.Core.Domain.Vendors; -using Nop.Plugin.Api.Constants; -using Nop.Plugin.Api.DataStructures; -using Nop.Services.Stores; - -namespace Nop.Plugin.Api.Services -{ - public class ProductApiService : IProductApiService - { - private readonly IStoreMappingService _storeMappingService; - private readonly IRepository _productRepository; - private readonly IRepository _productCategoryMappingRepository; - private readonly IRepository _vendorRepository; - - public ProductApiService(IRepository productRepository, - IRepository productCategoryMappingRepository, - IRepository vendorRepository, - IStoreMappingService storeMappingService) - { - _productRepository = productRepository; - _productCategoryMappingRepository = productCategoryMappingRepository; - _vendorRepository = vendorRepository; - _storeMappingService = storeMappingService; - } - - public IList GetProducts(IList ids = null, - DateTime? createdAtMin = null, DateTime? createdAtMax = null, DateTime? updatedAtMin = null, DateTime? updatedAtMax = null, - int limit = Configurations.DefaultLimit, int page = Configurations.DefaultPageValue, int sinceId = Configurations.DefaultSinceId, - int? categoryId = null, string vendorName = null, bool? publishedStatus = null) - { - var query = GetProductsQuery(createdAtMin, createdAtMax, updatedAtMin, updatedAtMax, vendorName, publishedStatus, ids, categoryId); - - if (sinceId > 0) - { - query = query.Where(c => c.Id > sinceId); - } - - return new ApiList(query, page - 1, limit); - } - - public int GetProductsCount(DateTime? createdAtMin = null, DateTime? createdAtMax = null, - DateTime? updatedAtMin = null, DateTime? updatedAtMax = null, bool? publishedStatus = null, string vendorName = null, - int? categoryId = null) - { - var query = GetProductsQuery(createdAtMin, createdAtMax, updatedAtMin, updatedAtMax, vendorName, - publishedStatus, categoryId: categoryId); - - return query.ToList().Count(p => _storeMappingService.Authorize(p)); - } - - public Product GetProductById(int productId) - { - if (productId == 0) - return null; - - return _productRepository.Table.FirstOrDefault(product => product.Id == productId && !product.Deleted); - } - - public Product GetProductByIdNoTracking(int productId) - { - if (productId == 0) - return null; - - return _productRepository.TableNoTracking.FirstOrDefault(product => product.Id == productId && !product.Deleted); - } - - private IQueryable GetProductsQuery(DateTime? createdAtMin = null, DateTime? createdAtMax = null, - DateTime? updatedAtMin = null, DateTime? updatedAtMax = null, string vendorName = null, - bool? publishedStatus = null, IList ids = null, int? categoryId = null) - - { - var query = _productRepository.TableNoTracking; - - if (ids != null && ids.Count > 0) - { - query = query.Where(c => ids.Contains(c.Id)); - } - - if (publishedStatus != null) - { - query = query.Where(c => c.Published == publishedStatus.Value); - } - - // always return products that are not deleted!!! - query = query.Where(c => !c.Deleted); - - if (createdAtMin != null) - { - query = query.Where(c => c.CreatedOnUtc > createdAtMin.Value); - } - - if (createdAtMax != null) - { - query = query.Where(c => c.CreatedOnUtc < createdAtMax.Value); - } - - if (updatedAtMin != null) - { - query = query.Where(c => c.UpdatedOnUtc > updatedAtMin.Value); - } - - if (updatedAtMax != null) - { - query = query.Where(c => c.UpdatedOnUtc < updatedAtMax.Value); - } - - if (!string.IsNullOrEmpty(vendorName)) - { - query = from vendor in _vendorRepository.TableNoTracking - join product in _productRepository.TableNoTracking on vendor.Id equals product.VendorId - where vendor.Name == vendorName && !vendor.Deleted && vendor.Active - select product; - } - - //only distinct products (group by ID) - query = from p in query - group p by p.Id - into pGroup - orderby pGroup.Key - select pGroup.FirstOrDefault(); - - if (categoryId != null) - { - var categoryMappingsForProduct = from productCategoryMapping in _productCategoryMappingRepository.TableNoTracking - where productCategoryMapping.CategoryId == categoryId - select productCategoryMapping; - - query = from product in query - join productCategoryMapping in categoryMappingsForProduct on product.Id equals productCategoryMapping.ProductId - select product; - } - - query = query.OrderBy(product => product.Id); - - return query; - } - } +using System; +using System.Collections.Generic; +using System.Linq; +using Nop.Core.Data; +using Nop.Core.Domain.Catalog; +using Nop.Core.Domain.Vendors; +using Nop.Plugin.Api.Constants; +using Nop.Plugin.Api.DataStructures; +using Nop.Services.Stores; + +namespace Nop.Plugin.Api.Services +{ + public class ProductApiService : IProductApiService + { + private readonly IStoreMappingService _storeMappingService; + private readonly IRepository _productRepository; + private readonly IRepository _productCategoryMappingRepository; + private readonly IRepository _vendorRepository; + + public ProductApiService(IRepository productRepository, + IRepository productCategoryMappingRepository, + IRepository vendorRepository, + IStoreMappingService storeMappingService) + { + _productRepository = productRepository; + _productCategoryMappingRepository = productCategoryMappingRepository; + _vendorRepository = vendorRepository; + _storeMappingService = storeMappingService; + } + + public IList GetProducts(IList ids = null, + DateTime? createdAtMin = null, DateTime? createdAtMax = null, DateTime? updatedAtMin = null, DateTime? updatedAtMax = null, + int limit = Configurations.DefaultLimit, int page = Configurations.DefaultPageValue, int sinceId = Configurations.DefaultSinceId, + int? categoryId = null, string vendorName = null, bool? publishedStatus = null) + { + var query = GetProductsQuery(createdAtMin, createdAtMax, updatedAtMin, updatedAtMax, vendorName, publishedStatus, ids, categoryId); + + if (sinceId > 0) + { + query = query.Where(c => c.Id > sinceId); + } + + return new ApiList(query, page - 1, limit); + } + + public int GetProductsCount(DateTime? createdAtMin = null, DateTime? createdAtMax = null, + DateTime? updatedAtMin = null, DateTime? updatedAtMax = null, bool? publishedStatus = null, string vendorName = null, + int? categoryId = null) + { + var query = GetProductsQuery(createdAtMin, createdAtMax, updatedAtMin, updatedAtMax, vendorName, + publishedStatus, categoryId: categoryId); + + return query.ToList().Count(p => _storeMappingService.Authorize(p)); + } + + public Product GetProductById(int productId) + { + if (productId == 0) + return null; + + return _productRepository.Table.FirstOrDefault(product => product.Id == productId && !product.Deleted); + } + + public Product GetProductByIdNoTracking(int productId) + { + if (productId == 0) + return null; + + return _productRepository.Table.FirstOrDefault(product => product.Id == productId && !product.Deleted); + } + + private IQueryable GetProductsQuery(DateTime? createdAtMin = null, DateTime? createdAtMax = null, + DateTime? updatedAtMin = null, DateTime? updatedAtMax = null, string vendorName = null, + bool? publishedStatus = null, IList ids = null, int? categoryId = null) + + { + var query = _productRepository.Table; + + if (ids != null && ids.Count > 0) + { + query = query.Where(c => ids.Contains(c.Id)); + } + + if (publishedStatus != null) + { + query = query.Where(c => c.Published == publishedStatus.Value); + } + + // always return products that are not deleted!!! + query = query.Where(c => !c.Deleted); + + if (createdAtMin != null) + { + query = query.Where(c => c.CreatedOnUtc > createdAtMin.Value); + } + + if (createdAtMax != null) + { + query = query.Where(c => c.CreatedOnUtc < createdAtMax.Value); + } + + if (updatedAtMin != null) + { + query = query.Where(c => c.UpdatedOnUtc > updatedAtMin.Value); + } + + if (updatedAtMax != null) + { + query = query.Where(c => c.UpdatedOnUtc < updatedAtMax.Value); + } + + if (!string.IsNullOrEmpty(vendorName)) + { + query = from vendor in _vendorRepository.Table + join product in _productRepository.Table on vendor.Id equals product.VendorId + where vendor.Name == vendorName && !vendor.Deleted && vendor.Active + select product; + } + + if (categoryId != null) + { + var categoryMappingsForProduct = from productCategoryMapping in _productCategoryMappingRepository.Table + where productCategoryMapping.CategoryId == categoryId + select productCategoryMapping; + + query = from product in query + join productCategoryMapping in categoryMappingsForProduct on product.Id equals productCategoryMapping.ProductId + select product; + } + + query = query.OrderBy(product => product.Id); + + return query; + } + } } \ No newline at end of file diff --git a/Nop.Plugin.Api/Services/ProductAttributeConverter.cs b/Nop.Plugin.Api/Services/ProductAttributeConverter.cs index 05ceed3..4404035 100644 --- a/Nop.Plugin.Api/Services/ProductAttributeConverter.cs +++ b/Nop.Plugin.Api/Services/ProductAttributeConverter.cs @@ -1,189 +1,189 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Nop.Plugin.Api.DTOs; -using System.Xml; -using Nop.Services.Catalog; -using Nop.Core.Domain.Catalog; -using Nop.Services.Media; -using Nop.Plugin.Api.Converters; -using System.Globalization; - -namespace Nop.Plugin.Api.Services -{ - public class ProductAttributeConverter : IProductAttributeConverter - { - private readonly IProductAttributeService _productAttributeService; - private readonly IProductAttributeParser _productAttributeParser; - private readonly IDownloadService _downloadService; - - public ProductAttributeConverter(IProductAttributeService productAttributeService, - IProductAttributeParser productAttributeParser, - IDownloadService downloadService, - IApiTypeConverter apiTypeConverter) - { - _productAttributeService = productAttributeService; - _productAttributeParser = productAttributeParser; - _downloadService = downloadService; - } - - public string ConvertToXml(List attributeDtos, int productId) - { - string attributesXml = ""; - - if (attributeDtos == null) - return attributesXml; - - var productAttributes = _productAttributeService.GetProductAttributeMappingsByProductId(productId); - foreach (var attribute in productAttributes) - { - switch (attribute.AttributeControlType) - { - case AttributeControlType.DropdownList: - case AttributeControlType.RadioList: - case AttributeControlType.ColorSquares: - case AttributeControlType.ImageSquares: - { - // there should be only one selected value for this attribute - var selectedAttribute = attributeDtos.Where(x => x.Id == attribute.Id).FirstOrDefault(); - if (selectedAttribute != null) - { - int selectedAttributeValue; - bool isInt = int.TryParse(selectedAttribute.Value, out selectedAttributeValue); - if (isInt && selectedAttributeValue > 0) - { - attributesXml = _productAttributeParser.AddProductAttribute(attributesXml, - attribute, selectedAttributeValue.ToString()); - } - } - } - break; - case AttributeControlType.Checkboxes: - { - // there could be more than one selected value for this attribute - var selectedAttributes = attributeDtos.Where(x => x.Id == attribute.Id); - foreach (var selectedAttribute in selectedAttributes) - { - int selectedAttributeValue; - bool isInt = int.TryParse(selectedAttribute.Value, out selectedAttributeValue); - if (isInt && selectedAttributeValue > 0) - { - // currently there is no support for attribute quantity - var quantity = 1; - - attributesXml = _productAttributeParser.AddProductAttribute(attributesXml, - attribute, selectedAttributeValue.ToString(), quantity); - } - - } - } - break; - case AttributeControlType.ReadonlyCheckboxes: - { - //load read-only(already server - side selected) values - var attributeValues = _productAttributeService.GetProductAttributeValues(attribute.Id); - foreach (var selectedAttributeId in attributeValues - .Where(v => v.IsPreSelected) - .Select(v => v.Id) - .ToList()) - { - attributesXml = _productAttributeParser.AddProductAttribute(attributesXml, - attribute, selectedAttributeId.ToString()); - } - } - break; - case AttributeControlType.TextBox: - case AttributeControlType.MultilineTextbox: - { - var selectedAttribute = attributeDtos.Where(x => x.Id == attribute.Id).FirstOrDefault(); - - if (selectedAttribute != null) - { - attributesXml = _productAttributeParser.AddProductAttribute(attributesXml, - attribute, selectedAttribute.Value); - } - - } - break; - case AttributeControlType.Datepicker: - { - var selectedAttribute = attributeDtos.Where(x => x.Id == attribute.Id).FirstOrDefault(); - - if (selectedAttribute != null) - { - DateTime selectedDate; - - // Since nopCommerce uses this format to keep the date in the database to keep it consisten we will expect the same format to be passed - bool validDate = DateTime.TryParseExact(selectedAttribute.Value, "D", CultureInfo.CurrentCulture, - DateTimeStyles.None, out selectedDate); - - if (validDate) - { - attributesXml = _productAttributeParser.AddProductAttribute(attributesXml, - attribute, selectedDate.ToString("D")); - } - } - } - break; - case AttributeControlType.FileUpload: - { - var selectedAttribute = attributeDtos.Where(x => x.Id == attribute.Id).FirstOrDefault(); - - if (selectedAttribute != null) - { - Guid downloadGuid; - Guid.TryParse(selectedAttribute.Value, out downloadGuid); - var download = _downloadService.GetDownloadByGuid(downloadGuid); - if (download != null) - { - attributesXml = _productAttributeParser.AddProductAttribute(attributesXml, - attribute, download.DownloadGuid.ToString()); - } - } - } - break; - default: - break; - } - } - - // No Gift Card attributes support yet - - return attributesXml; - } - - public List Parse(string attributesXml) - { - var attributeDtos = new List(); - if (string.IsNullOrEmpty(attributesXml)) - return attributeDtos; - - try - { - var xmlDoc = new XmlDocument(); - xmlDoc.LoadXml(attributesXml); - - foreach (XmlNode attributeNode in xmlDoc.SelectNodes(@"//Attributes/ProductAttribute")) - { - if (attributeNode.Attributes != null && attributeNode.Attributes["ID"] != null) - { - int attributeId; - if (int.TryParse(attributeNode.Attributes["ID"].InnerText.Trim(), out attributeId)) - { - foreach (XmlNode attributeValue in attributeNode.SelectNodes("ProductAttributeValue")) - { - var value = attributeValue.SelectSingleNode("Value").InnerText.Trim(); - // no support for quantity yet - //var quantityNode = attributeValue.SelectSingleNode("Quantity"); - attributeDtos.Add(new ProductItemAttributeDto() { Id = attributeId, Value = value }); - } - } - } - } - } - catch { } - - return attributeDtos; - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using Nop.Plugin.Api.DTOs; +using System.Xml; +using Nop.Services.Catalog; +using Nop.Core.Domain.Catalog; +using Nop.Services.Media; +using Nop.Plugin.Api.Converters; +using System.Globalization; + +namespace Nop.Plugin.Api.Services +{ + public class ProductAttributeConverter : IProductAttributeConverter + { + private readonly IProductAttributeService _productAttributeService; + private readonly IProductAttributeParser _productAttributeParser; + private readonly IDownloadService _downloadService; + + public ProductAttributeConverter(IProductAttributeService productAttributeService, + IProductAttributeParser productAttributeParser, + IDownloadService downloadService, + IApiTypeConverter apiTypeConverter) + { + _productAttributeService = productAttributeService; + _productAttributeParser = productAttributeParser; + _downloadService = downloadService; + } + + public string ConvertToXml(List attributeDtos, int productId) + { + var attributesXml = ""; + + if (attributeDtos == null) + return attributesXml; + + var productAttributes = _productAttributeService.GetProductAttributeMappingsByProductId(productId); + foreach (var attribute in productAttributes) + { + switch (attribute.AttributeControlType) + { + case AttributeControlType.DropdownList: + case AttributeControlType.RadioList: + case AttributeControlType.ColorSquares: + case AttributeControlType.ImageSquares: + { + // there should be only one selected value for this attribute + var selectedAttribute = attributeDtos.Where(x => x.Id == attribute.Id).FirstOrDefault(); + if (selectedAttribute != null) + { + int selectedAttributeValue; + var isInt = int.TryParse(selectedAttribute.Value, out selectedAttributeValue); + if (isInt && selectedAttributeValue > 0) + { + attributesXml = _productAttributeParser.AddProductAttribute(attributesXml, + attribute, selectedAttributeValue.ToString()); + } + } + } + break; + case AttributeControlType.Checkboxes: + { + // there could be more than one selected value for this attribute + var selectedAttributes = attributeDtos.Where(x => x.Id == attribute.Id); + foreach (var selectedAttribute in selectedAttributes) + { + int selectedAttributeValue; + var isInt = int.TryParse(selectedAttribute.Value, out selectedAttributeValue); + if (isInt && selectedAttributeValue > 0) + { + // currently there is no support for attribute quantity + var quantity = 1; + + attributesXml = _productAttributeParser.AddProductAttribute(attributesXml, + attribute, selectedAttributeValue.ToString(), quantity); + } + + } + } + break; + case AttributeControlType.ReadonlyCheckboxes: + { + //load read-only(already server - side selected) values + var attributeValues = _productAttributeService.GetProductAttributeValues(attribute.Id); + foreach (var selectedAttributeId in attributeValues + .Where(v => v.IsPreSelected) + .Select(v => v.Id) + .ToList()) + { + attributesXml = _productAttributeParser.AddProductAttribute(attributesXml, + attribute, selectedAttributeId.ToString()); + } + } + break; + case AttributeControlType.TextBox: + case AttributeControlType.MultilineTextbox: + { + var selectedAttribute = attributeDtos.Where(x => x.Id == attribute.Id).FirstOrDefault(); + + if (selectedAttribute != null) + { + attributesXml = _productAttributeParser.AddProductAttribute(attributesXml, + attribute, selectedAttribute.Value); + } + + } + break; + case AttributeControlType.Datepicker: + { + var selectedAttribute = attributeDtos.Where(x => x.Id == attribute.Id).FirstOrDefault(); + + if (selectedAttribute != null) + { + DateTime selectedDate; + + // Since nopCommerce uses this format to keep the date in the database to keep it consisten we will expect the same format to be passed + var validDate = DateTime.TryParseExact(selectedAttribute.Value, "D", CultureInfo.CurrentCulture, + DateTimeStyles.None, out selectedDate); + + if (validDate) + { + attributesXml = _productAttributeParser.AddProductAttribute(attributesXml, + attribute, selectedDate.ToString("D")); + } + } + } + break; + case AttributeControlType.FileUpload: + { + var selectedAttribute = attributeDtos.Where(x => x.Id == attribute.Id).FirstOrDefault(); + + if (selectedAttribute != null) + { + Guid downloadGuid; + Guid.TryParse(selectedAttribute.Value, out downloadGuid); + var download = _downloadService.GetDownloadByGuid(downloadGuid); + if (download != null) + { + attributesXml = _productAttributeParser.AddProductAttribute(attributesXml, + attribute, download.DownloadGuid.ToString()); + } + } + } + break; + default: + break; + } + } + + // No Gift Card attributes support yet + + return attributesXml; + } + + public List Parse(string attributesXml) + { + var attributeDtos = new List(); + if (string.IsNullOrEmpty(attributesXml)) + return attributeDtos; + + try + { + var xmlDoc = new XmlDocument(); + xmlDoc.LoadXml(attributesXml); + + foreach (XmlNode attributeNode in xmlDoc.SelectNodes(@"//Attributes/ProductAttribute")) + { + if (attributeNode.Attributes != null && attributeNode.Attributes["ID"] != null) + { + int attributeId; + if (int.TryParse(attributeNode.Attributes["ID"].InnerText.Trim(), out attributeId)) + { + foreach (XmlNode attributeValue in attributeNode.SelectNodes("ProductAttributeValue")) + { + var value = attributeValue.SelectSingleNode("Value").InnerText.Trim(); + // no support for quantity yet + //var quantityNode = attributeValue.SelectSingleNode("Quantity"); + attributeDtos.Add(new ProductItemAttributeDto { Id = attributeId, Value = value }); + } + } + } + } + } + catch { } + + return attributeDtos; + } + } +} diff --git a/Nop.Plugin.Api/Services/ProductAttributesApiService.cs b/Nop.Plugin.Api/Services/ProductAttributesApiService.cs index 5e2f472..9d8e8ec 100644 --- a/Nop.Plugin.Api/Services/ProductAttributesApiService.cs +++ b/Nop.Plugin.Api/Services/ProductAttributesApiService.cs @@ -1,54 +1,54 @@ -using System.Collections.Generic; -using System.Linq; -using Nop.Core.Data; -using Nop.Core.Domain.Catalog; -using Nop.Plugin.Api.Constants; -using Nop.Plugin.Api.DataStructures; - -namespace Nop.Plugin.Api.Services -{ - public class ProductAttributesApiService : IProductAttributesApiService - { - private readonly IRepository _productAttributesRepository; - - public ProductAttributesApiService(IRepository productAttributesRepository) - { - _productAttributesRepository = productAttributesRepository; - } - - public IList GetProductAttributes(int limit = Configurations.DefaultLimit, - int page = Configurations.DefaultPageValue, int sinceId = Configurations.DefaultSinceId) - { - var query = GetProductAttributesQuery(sinceId); - - return new ApiList(query, page - 1, limit); - } - - public int GetProductAttributesCount() - { - return GetProductAttributesQuery().Count(); - } - - ProductAttribute IProductAttributesApiService.GetById(int id) - { - if (id <= 0) - return null; - - return _productAttributesRepository.GetById(id); - } - - private IQueryable GetProductAttributesQuery(int sinceId = Configurations.DefaultSinceId) - { - var query = _productAttributesRepository.TableNoTracking; - - if (sinceId > 0) - { - query = query.Where(productAttribute => productAttribute.Id > sinceId); - } - - query = query.OrderBy(productAttribute => productAttribute.Id); - - return query; - } - } +using System.Collections.Generic; +using System.Linq; +using Nop.Core.Data; +using Nop.Core.Domain.Catalog; +using Nop.Plugin.Api.Constants; +using Nop.Plugin.Api.DataStructures; + +namespace Nop.Plugin.Api.Services +{ + public class ProductAttributesApiService : IProductAttributesApiService + { + private readonly IRepository _productAttributesRepository; + + public ProductAttributesApiService(IRepository productAttributesRepository) + { + _productAttributesRepository = productAttributesRepository; + } + + public IList GetProductAttributes(int limit = Configurations.DefaultLimit, + int page = Configurations.DefaultPageValue, int sinceId = Configurations.DefaultSinceId) + { + var query = GetProductAttributesQuery(sinceId); + + return new ApiList(query, page - 1, limit); + } + + public int GetProductAttributesCount() + { + return GetProductAttributesQuery().Count(); + } + + ProductAttribute IProductAttributesApiService.GetById(int id) + { + if (id <= 0) + return null; + + return _productAttributesRepository.GetById(id); + } + + private IQueryable GetProductAttributesQuery(int sinceId = Configurations.DefaultSinceId) + { + var query = _productAttributesRepository.Table; + + if (sinceId > 0) + { + query = query.Where(productAttribute => productAttribute.Id > sinceId); + } + + query = query.OrderBy(productAttribute => productAttribute.Id); + + return query; + } + } } \ No newline at end of file diff --git a/Nop.Plugin.Api/Services/ProductCategoryMappingsApiService.cs b/Nop.Plugin.Api/Services/ProductCategoryMappingsApiService.cs index 64876e6..1a950fb 100644 --- a/Nop.Plugin.Api/Services/ProductCategoryMappingsApiService.cs +++ b/Nop.Plugin.Api/Services/ProductCategoryMappingsApiService.cs @@ -1,66 +1,66 @@ -using System.Collections.Generic; -using System.Linq; -using Nop.Core.Data; -using Nop.Core.Domain.Catalog; -using Nop.Plugin.Api.Constants; -using Nop.Plugin.Api.DataStructures; - -namespace Nop.Plugin.Api.Services -{ - public class ProductCategoryMappingsApiService : IProductCategoryMappingsApiService - { - private readonly IRepository _productCategoryMappingsRepository; - - public ProductCategoryMappingsApiService(IRepository productCategoryMappingsRepository) - { - _productCategoryMappingsRepository = productCategoryMappingsRepository; - } - - public IList GetMappings(int? productId = null, - int? categoryId = null, int limit = Configurations.DefaultLimit, - int page = Configurations.DefaultPageValue, int sinceId = Configurations.DefaultSinceId) - { - var query = GetMappingsQuery(productId, categoryId, sinceId); - - return new ApiList(query, page - 1, limit); - } - - public int GetMappingsCount(int? productId = null, int? categoryId = null) - { - return GetMappingsQuery(productId, categoryId).Count(); - } - - public ProductCategory GetById(int id) - { - if (id <= 0) - return null; - - return _productCategoryMappingsRepository.GetById(id); - } - - private IQueryable GetMappingsQuery(int? productId = null, - int? categoryId = null, int sinceId = Configurations.DefaultSinceId) - { - var query = _productCategoryMappingsRepository.TableNoTracking; - - if (productId != null) - { - query = query.Where(mapping => mapping.ProductId == productId); - } - - if (categoryId != null) - { - query = query.Where(mapping => mapping.CategoryId == categoryId); - } - - if (sinceId > 0) - { - query = query.Where(mapping => mapping.Id > sinceId); - } - - query = query.OrderBy(mapping => mapping.Id); - - return query; - } - } +using System.Collections.Generic; +using System.Linq; +using Nop.Core.Data; +using Nop.Core.Domain.Catalog; +using Nop.Plugin.Api.Constants; +using Nop.Plugin.Api.DataStructures; + +namespace Nop.Plugin.Api.Services +{ + public class ProductCategoryMappingsApiService : IProductCategoryMappingsApiService + { + private readonly IRepository _productCategoryMappingsRepository; + + public ProductCategoryMappingsApiService(IRepository productCategoryMappingsRepository) + { + _productCategoryMappingsRepository = productCategoryMappingsRepository; + } + + public IList GetMappings(int? productId = null, + int? categoryId = null, int limit = Configurations.DefaultLimit, + int page = Configurations.DefaultPageValue, int sinceId = Configurations.DefaultSinceId) + { + var query = GetMappingsQuery(productId, categoryId, sinceId); + + return new ApiList(query, page - 1, limit); + } + + public int GetMappingsCount(int? productId = null, int? categoryId = null) + { + return GetMappingsQuery(productId, categoryId).Count(); + } + + public ProductCategory GetById(int id) + { + if (id <= 0) + return null; + + return _productCategoryMappingsRepository.GetById(id); + } + + private IQueryable GetMappingsQuery(int? productId = null, + int? categoryId = null, int sinceId = Configurations.DefaultSinceId) + { + var query = _productCategoryMappingsRepository.Table; + + if (productId != null) + { + query = query.Where(mapping => mapping.ProductId == productId); + } + + if (categoryId != null) + { + query = query.Where(mapping => mapping.CategoryId == categoryId); + } + + if (sinceId > 0) + { + query = query.Where(mapping => mapping.Id > sinceId); + } + + query = query.OrderBy(mapping => mapping.Id); + + return query; + } + } } \ No newline at end of file diff --git a/Nop.Plugin.Api/Services/ProductManufacturerMappingsApiService.cs b/Nop.Plugin.Api/Services/ProductManufacturerMappingsApiService.cs new file mode 100644 index 0000000..76f8c0d --- /dev/null +++ b/Nop.Plugin.Api/Services/ProductManufacturerMappingsApiService.cs @@ -0,0 +1,66 @@ +using Nop.Core.Data; +using Nop.Core.Domain.Catalog; +using Nop.Plugin.Api.Constants; +using Nop.Plugin.Api.DataStructures; +using System.Collections.Generic; +using System.Linq; + +namespace Nop.Plugin.Api.Services +{ + public class ProductManufacturerMappingsApiService : IProductManufacturerMappingsApiService + { + private readonly IRepository _productManufacturerMappingsRepository; + + public ProductManufacturerMappingsApiService(IRepository productManufacturerMappingsRepository) + { + _productManufacturerMappingsRepository = productManufacturerMappingsRepository; + } + + public IList GetMappings(int? productId = null, + int? manufacturerId = null, int limit = Configurations.DefaultLimit, + int page = Configurations.DefaultPageValue, int sinceId = Configurations.DefaultSinceId) + { + var query = GetMappingsQuery(productId, manufacturerId, sinceId); + + return new ApiList(query, page - 1, limit); + } + + public int GetMappingsCount(int? productId = null, int? manufacturerId = null) + { + return GetMappingsQuery(productId, manufacturerId).Count(); + } + + public ProductManufacturer GetById(int id) + { + if (id <= 0) + return null; + + return _productManufacturerMappingsRepository.GetById(id); + } + + private IQueryable GetMappingsQuery(int? productId = null, + int? manufacturerId = null, int sinceId = Configurations.DefaultSinceId) + { + var query = _productManufacturerMappingsRepository.Table; + + if (productId != null) + { + query = query.Where(mapping => mapping.ProductId == productId); + } + + if (manufacturerId != null) + { + query = query.Where(mapping => mapping.ManufacturerId == manufacturerId); + } + + if (sinceId > 0) + { + query = query.Where(mapping => mapping.Id > sinceId); + } + + query = query.OrderBy(mapping => mapping.Id); + + return query; + } + } +} \ No newline at end of file diff --git a/Nop.Plugin.Api/Services/ShoppingCartItemApiService.cs b/Nop.Plugin.Api/Services/ShoppingCartItemApiService.cs index 4eb1837..ca341e2 100644 --- a/Nop.Plugin.Api/Services/ShoppingCartItemApiService.cs +++ b/Nop.Plugin.Api/Services/ShoppingCartItemApiService.cs @@ -1,77 +1,77 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Nop.Core.Data; -using Nop.Core.Domain.Orders; -using Nop.Plugin.Api.Constants; -using Nop.Plugin.Api.DataStructures; -using Nop.Core; - -namespace Nop.Plugin.Api.Services -{ - public class ShoppingCartItemApiService : IShoppingCartItemApiService - { - private readonly IRepository _shoppingCartItemsRepository; - private readonly IStoreContext _storeContext; - - public ShoppingCartItemApiService(IRepository shoppingCartItemsRepository, IStoreContext storeContext) - { - _shoppingCartItemsRepository = shoppingCartItemsRepository; - _storeContext = storeContext; - } - - public List GetShoppingCartItems(int? customerId = null, DateTime? createdAtMin = null, DateTime? createdAtMax = null, - DateTime? updatedAtMin = null, DateTime? updatedAtMax = null, int limit = Configurations.DefaultLimit, - int page = Configurations.DefaultPageValue) - { - IQueryable query = GetShoppingCartItemsQuery(customerId, createdAtMin, createdAtMax, - updatedAtMin, updatedAtMax); - - return new ApiList(query, page - 1, limit); - } - - public ShoppingCartItem GetShoppingCartItem(int id) - { - return _shoppingCartItemsRepository.GetById(id); - } - - private IQueryable GetShoppingCartItemsQuery(int? customerId = null, DateTime? createdAtMin = null, DateTime? createdAtMax = null, - DateTime? updatedAtMin = null, DateTime? updatedAtMax = null) - { - var query = _shoppingCartItemsRepository.TableNoTracking; - - if (customerId != null) - { - query = query.Where(shoppingCartItem => shoppingCartItem.CustomerId == customerId); - } - - if (createdAtMin != null) - { - query = query.Where(c => c.CreatedOnUtc > createdAtMin.Value); - } - - if (createdAtMax != null) - { - query = query.Where(c => c.CreatedOnUtc < createdAtMax.Value); - } - - if (updatedAtMin != null) - { - query = query.Where(c => c.UpdatedOnUtc > updatedAtMin.Value); - } - - if (updatedAtMax != null) - { - query = query.Where(c => c.UpdatedOnUtc < updatedAtMax.Value); - } - - // items for the current store only - int currentStoreId = _storeContext.CurrentStore.Id; - query = query.Where(c => c.StoreId == currentStoreId); - - query = query.OrderBy(shoppingCartItem => shoppingCartItem.Id); - - return query; - } - } +using System; +using System.Collections.Generic; +using System.Linq; +using Nop.Core.Data; +using Nop.Core.Domain.Orders; +using Nop.Plugin.Api.Constants; +using Nop.Plugin.Api.DataStructures; +using Nop.Core; + +namespace Nop.Plugin.Api.Services +{ + public class ShoppingCartItemApiService : IShoppingCartItemApiService + { + private readonly IRepository _shoppingCartItemsRepository; + private readonly IStoreContext _storeContext; + + public ShoppingCartItemApiService(IRepository shoppingCartItemsRepository, IStoreContext storeContext) + { + _shoppingCartItemsRepository = shoppingCartItemsRepository; + _storeContext = storeContext; + } + + public List GetShoppingCartItems(int? customerId = null, DateTime? createdAtMin = null, DateTime? createdAtMax = null, + DateTime? updatedAtMin = null, DateTime? updatedAtMax = null, int limit = Configurations.DefaultLimit, + int page = Configurations.DefaultPageValue) + { + var query = GetShoppingCartItemsQuery(customerId, createdAtMin, createdAtMax, + updatedAtMin, updatedAtMax); + + return new ApiList(query, page - 1, limit); + } + + public ShoppingCartItem GetShoppingCartItem(int id) + { + return _shoppingCartItemsRepository.GetById(id); + } + + private IQueryable GetShoppingCartItemsQuery(int? customerId = null, DateTime? createdAtMin = null, DateTime? createdAtMax = null, + DateTime? updatedAtMin = null, DateTime? updatedAtMax = null) + { + var query = _shoppingCartItemsRepository.Table; + + if (customerId != null) + { + query = query.Where(shoppingCartItem => shoppingCartItem.CustomerId == customerId); + } + + if (createdAtMin != null) + { + query = query.Where(c => c.CreatedOnUtc > createdAtMin.Value); + } + + if (createdAtMax != null) + { + query = query.Where(c => c.CreatedOnUtc < createdAtMax.Value); + } + + if (updatedAtMin != null) + { + query = query.Where(c => c.UpdatedOnUtc > updatedAtMin.Value); + } + + if (updatedAtMax != null) + { + query = query.Where(c => c.UpdatedOnUtc < updatedAtMax.Value); + } + + // items for the current store only + var currentStoreId = _storeContext.CurrentStore.Id; + query = query.Where(c => c.StoreId == currentStoreId); + + query = query.OrderBy(shoppingCartItem => shoppingCartItem.Id); + + return query; + } + } } \ No newline at end of file diff --git a/Nop.Plugin.Api/Services/WebHookService.cs b/Nop.Plugin.Api/Services/WebHookService.cs index e5831b4..ab10c2e 100644 --- a/Nop.Plugin.Api/Services/WebHookService.cs +++ b/Nop.Plugin.Api/Services/WebHookService.cs @@ -1,81 +1,73 @@ -using Nop.Plugin.Api.WebHooks; - -namespace Nop.Plugin.Api.Services -{ - using Microsoft.AspNet.WebHooks; - using Microsoft.AspNet.WebHooks.Diagnostics; - using Microsoft.AspNet.WebHooks.Services; - using Nop.Plugin.Api.Domain; - using Nop.Plugin.Api.Helpers; - using System; - using System.Collections.Generic; - using System.Web.Http.Tracing; - - public class WebHookService : IWebHookService - { - private IWebHookManager _webHookManager; - private IWebHookSender _webHookSender; - private IWebHookStore _webHookStore; - private IWebHookFilterManager _webHookFilterManager; - private ILogger _logger; - - private readonly IConfigManagerHelper _configManagerHelper; - - public WebHookService(IConfigManagerHelper configManagerHelper,ILogger logger) - { - _configManagerHelper = configManagerHelper; - _logger = logger; - } - - public IWebHookFilterManager GetWebHookFilterManager() - { - if (_webHookFilterManager == null) - { - var filterProviders = new List(); - filterProviders.Add(new FilterProvider()); - _webHookFilterManager = new WebHookFilterManager(filterProviders); - } - - return _webHookFilterManager; - } - - public IWebHookManager GetWebHookManager() - { - if (_webHookManager == null) - { - _webHookManager = new WebHookManager(GetWebHookStore(), GetWebHookSender(), _logger); - } - - return _webHookManager; - } - - public IWebHookSender GetWebHookSender() - { - if (_webHookSender == null) - { - _webHookSender = new ApiWebHookSender(_logger); - } - - return _webHookSender; - } - - public IWebHookStore GetWebHookStore() - { - if (_webHookStore == null) - { - var dataSettings = _configManagerHelper.DataSettings; - Microsoft.AspNet.WebHooks.Config.SettingsDictionary settings = new Microsoft.AspNet.WebHooks.Config.SettingsDictionary(); - settings.Add("MS_SqlStoreConnectionString", dataSettings.DataConnectionString); - settings.Connections.Add("MS_SqlStoreConnectionString", new Microsoft.AspNet.WebHooks.Config.ConnectionSettings("MS_SqlStoreConnectionString", dataSettings.DataConnectionString)); - - Microsoft.AspNet.WebHooks.IWebHookStore store = new Microsoft.AspNet.WebHooks.SqlWebHookStore(settings, _logger); - - Microsoft.AspNet.WebHooks.Services.CustomServices.SetStore(store); - - _webHookStore = CustomServices.GetStore(); - } - - return _webHookStore; - } - } -} +using Nop.Plugin.Api.WebHooks; + +namespace Nop.Plugin.Api.Services +{ + using Microsoft.AspNetCore.WebHooks; + using Microsoft.Extensions.Logging; + using Nop.Plugin.Api.Helpers; + + using System.Collections.Generic; + + + public class WebHookService : IWebHookService + { + private IWebHookManager _webHookManager; + private IWebHookSender _webHookSender; + private IWebHookStore _webHookStore; + private IWebHookFilterManager _webHookFilterManager; + private ILogger _webHookManagerLogger; + private ILogger _apiWebHookSenderLogger; + + private readonly IConfigManagerHelper _configManagerHelper; + + public WebHookService(IConfigManagerHelper configManagerHelper, ILogger webHookManagerLogger, + ILogger apiWebHookSenderLogger) + { + _configManagerHelper = configManagerHelper; + _webHookManagerLogger = webHookManagerLogger; + _apiWebHookSenderLogger = apiWebHookSenderLogger; + } + + public IWebHookFilterManager GetWebHookFilterManager() + { + if (_webHookFilterManager == null) + { + var filterProviders = new List(); + filterProviders.Add(new FilterProvider()); + _webHookFilterManager = new WebHookFilterManager(filterProviders); + } + + return _webHookFilterManager; + } + + public IWebHookManager GetWebHookManager() + { + if (_webHookManager == null) + { + _webHookManager = new WebHookManager(GetWebHookStore(), GetWebHookSender(), _webHookManagerLogger); + } + + return _webHookManager; + } + + public IWebHookSender GetWebHookSender() + { + if (_webHookSender == null) + { + _webHookSender = new ApiWebHookSender(_apiWebHookSenderLogger); + } + + return _webHookSender; + } + + public IWebHookStore GetWebHookStore() + { + if (_webHookStore == null) + { + _webHookStore = new NopWebHookStore(); + } + + return _webHookStore; + } + } +} diff --git a/Nop.Plugin.Api/Validators/AddressDtoValidator.cs b/Nop.Plugin.Api/Validators/AddressDtoValidator.cs index 64773d8..483f540 100644 --- a/Nop.Plugin.Api/Validators/AddressDtoValidator.cs +++ b/Nop.Plugin.Api/Validators/AddressDtoValidator.cs @@ -1,37 +1,73 @@ -using System; -using System.Linq.Expressions; -using FluentValidation; +using Microsoft.AspNetCore.Http; using Nop.Plugin.Api.DTOs; +using Nop.Plugin.Api.Helpers; +using System.Collections.Generic; namespace Nop.Plugin.Api.Validators { - public class AddressDtoValidator : AbstractValidator + public class AddressDtoValidator : BaseDtoValidator { - public AddressDtoValidator() + + #region Constructors + + public AddressDtoValidator(IHttpContextAccessor httpContextAccessor, IJsonHelper jsonHelper, Dictionary requestJsonDictionary) : base(httpContextAccessor, jsonHelper, requestJsonDictionary) { - SetNotNullOrEmptyRule(dto => dto.FirstName, "first_name required"); + SetFirstNameRule(); + SetLastNameRule(); + SetEmailRule(); + + SetAddress1Rule(); + SetCityRule(); + SetZipPostalCodeRule(); + SetCountryIdRule(); + SetPhoneNumberRule(); + } - SetNotNullOrEmptyRule(dto => dto.LastName, "last_name required"); + #endregion - SetNotNullOrEmptyRule(dto => dto.Email, "email required"); + #region Private Methods - SetNotNullOrEmptyRule(dto => dto.CountryId <= 0 ? string.Empty : dto.CountryId.ToString(), "country_id required"); + private void SetAddress1Rule() + { + SetNotNullOrEmptyCreateOrUpdateRule(a => a.Address1, "address1 required", "address1"); + } + + private void SetCityRule() + { + SetNotNullOrEmptyCreateOrUpdateRule(a => a.City, "city required", "city"); + } + + private void SetCountryIdRule() + { + SetGreaterThanZeroCreateOrUpdateRule(a => a.CountryId, "country_id required", "country_id"); + } - SetNotNullOrEmptyRule(dto => dto.City, "city required"); + private void SetEmailRule() + { + SetNotNullOrEmptyCreateOrUpdateRule(a => a.Email, "email required", "email"); + } - SetNotNullOrEmptyRule(dto => dto.Address1, "address1 required"); + private void SetFirstNameRule() + { + SetNotNullOrEmptyCreateOrUpdateRule(a => a.FirstName, "first_name required", "first_name"); + } - SetNotNullOrEmptyRule(dto => dto.ZipPostalCode, "zip_postal_code required"); + private void SetLastNameRule() + { + SetNotNullOrEmptyCreateOrUpdateRule(a => a.LastName, "first_name required", "last_name"); + } - SetNotNullOrEmptyRule(dto => dto.PhoneNumber, "phone_number required"); + private void SetPhoneNumberRule() + { + SetNotNullOrEmptyCreateOrUpdateRule(a => a.PhoneNumber, "phone_number required", "phone_number"); } - - private void SetNotNullOrEmptyRule(Expression> expression, string errorMessage) + + private void SetZipPostalCodeRule() { - RuleFor(expression) - .NotNull() - .NotEmpty() - .WithMessage(errorMessage); + SetNotNullOrEmptyCreateOrUpdateRule(a => a.ZipPostalCode, "zip_postal_code required", "zip_postal_code"); } + + #endregion + } } \ No newline at end of file diff --git a/Nop.Plugin.Api/Validators/BaseDtoValidator.cs b/Nop.Plugin.Api/Validators/BaseDtoValidator.cs new file mode 100644 index 0000000..a047f1d --- /dev/null +++ b/Nop.Plugin.Api/Validators/BaseDtoValidator.cs @@ -0,0 +1,164 @@ +using FluentValidation; +using FluentValidation.Results; +using FluentValidation.Validators; +using Microsoft.AspNetCore.Http; +using Nop.Plugin.Api.DTOs.Base; +using Nop.Plugin.Api.Helpers; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Net.Http; + +namespace Nop.Plugin.Api.Validators +{ + public abstract class BaseDtoValidator : AbstractValidator where T : BaseDto, new() + { + + #region Private Fields + + private Dictionary _requestValuesDictionary; + + #endregion + + #region Constructors + + public BaseDtoValidator(IHttpContextAccessor httpContextAccessor, IJsonHelper jsonHelper, Dictionary requestJsonDictionary) + { + HttpContextAccessor = httpContextAccessor; + JsonHelper = jsonHelper; + + // this is hacky - can't make requestJsonDictionary an optional parameter because Nop tries to resolve it + // + // when DI (or the Nop Engine) resolves this class, requestJsonDictionary will be empty (length 0) + // in this case, HttpMethod should be whatever the current context is + // when we manually instantiate this class (from other validators to validate child objects), requestJsonDictionary will be null for "new" objects and populated for existing objects + // in this scenario, we want to check if there's an id, and force "create" (POST) validation if there isn't an id + + HttpMethod = new HttpMethod(HttpContextAccessor.HttpContext.Request.Method); + if (requestJsonDictionary == null || requestJsonDictionary.Count > 0 && !requestJsonDictionary.ContainsKey("id")) + { + HttpMethod = HttpMethod.Post; + } + + if (requestJsonDictionary != null && requestJsonDictionary.Count > 0) + { + _requestValuesDictionary = requestJsonDictionary; + } + + SetRequiredIdRule(); + } + + #endregion + + #region Protected Properties + + protected IHttpContextAccessor HttpContextAccessor { get; private set; } + + protected Dictionary RequestJsonDictionary + { + get + { + if (_requestValuesDictionary == null) + { + _requestValuesDictionary = GetRequestJsonDictionaryDictionaryFromHttpContext(); + } + + return _requestValuesDictionary; + } + } + + protected IJsonHelper JsonHelper { get; private set; } + + #endregion + + #region Public Properties + + public HttpMethod HttpMethod { get; set; } + + #endregion + + #region Protected Methods + + protected void MergeValidationResult(CustomContext validationContext, ValidationResult validationResult) + { + if (!validationResult.IsValid) + { + foreach (var validationFailure in validationResult.Errors) + { + validationContext.AddFailure(validationFailure); + } + } + } + + protected Dictionary GetRequestJsonDictionaryCollectionItemDictionary(string collectionKey, TDto dto) where TDto : BaseDto + { + var collectionItems = (List)RequestJsonDictionary[collectionKey]; + var collectionItemDictionary = collectionItems.FirstOrDefault(x => + ((Dictionary)x).ContainsKey("id") && ((int)(long)((Dictionary)x)["id"]) == dto.Id + ) as Dictionary; + + return collectionItemDictionary; + } + + protected void SetGreaterThanZeroCreateOrUpdateRule(Expression> expression, string errorMessage, string requestValueKey) + { + if (HttpMethod == HttpMethod.Post || RequestJsonDictionary.ContainsKey(requestValueKey)) + { + SetGreaterThanZeroRule(expression, errorMessage); + } + } + + protected void SetGreaterThanZeroRule(Expression> expression, string errorMessage) + { + RuleFor(expression) + .NotNull() + .NotEmpty() + .Must(id => id > 0); + } + + protected void SetNotNullOrEmptyCreateOrUpdateRule(Expression> expression, string errorMessage, string requestValueKey) + { + if (HttpMethod == HttpMethod.Post || RequestJsonDictionary.ContainsKey(requestValueKey)) + { + SetNotNullOrEmptyRule(expression, errorMessage); + } + } + + protected void SetNotNullOrEmptyRule(Expression> expression, string errorMessage) + { + RuleFor(expression) + .NotNull() + .NotEmpty() + .WithMessage(errorMessage); + } + + #endregion + + #region Private Methods + + private Dictionary GetRequestJsonDictionaryDictionaryFromHttpContext() + { + var requestJsonDictionary = JsonHelper.GetRequestJsonDictionaryFromStream(HttpContextAccessor.HttpContext.Request.Body, true); + var rootPropertyName = JsonHelper.GetRootPropertyName(); + + if (requestJsonDictionary.ContainsKey(rootPropertyName)) + { + requestJsonDictionary = (Dictionary)requestJsonDictionary[rootPropertyName]; + } + + return requestJsonDictionary; + } + + private void SetRequiredIdRule() + { + if (HttpMethod == HttpMethod.Put) + { + SetGreaterThanZeroCreateOrUpdateRule(x => x.Id, "invalid id", "id"); + } + } + + #endregion + + } +} \ No newline at end of file diff --git a/Nop.Plugin.Api/Validators/CategoryDtoValidator.cs b/Nop.Plugin.Api/Validators/CategoryDtoValidator.cs index 7d81a2f..c043760 100644 --- a/Nop.Plugin.Api/Validators/CategoryDtoValidator.cs +++ b/Nop.Plugin.Api/Validators/CategoryDtoValidator.cs @@ -1,41 +1,30 @@ -using System; -using System.Collections.Generic; -using FluentValidation; -using Nop.Plugin.Api.DTOs.Categories; - -namespace Nop.Plugin.Api.Validators -{ - public class CategoryDtoValidator : AbstractValidator - { - public CategoryDtoValidator(string httpMethod, Dictionary passedPropertyValuePaires) - { - if (string.IsNullOrEmpty(httpMethod) || httpMethod.Equals("post", StringComparison.InvariantCultureIgnoreCase)) - { - SetNameRule(); - } - else if (httpMethod.Equals("put", StringComparison.InvariantCultureIgnoreCase)) - { - int parsedId = 0; - - RuleFor(x => x.Id) - .NotNull() - .NotEmpty() - .Must(id => int.TryParse(id, out parsedId) && parsedId > 0) - .WithMessage("Invalid id"); - - if (passedPropertyValuePaires.ContainsKey("name")) - { - SetNameRule(); - } - } - } - - private void SetNameRule() - { - RuleFor(x => x.Name) - .NotNull() - .NotEmpty() - .WithMessage("name required"); - } - } +using Microsoft.AspNetCore.Http; +using Nop.Plugin.Api.DTOs.Categories; +using Nop.Plugin.Api.Helpers; +using System.Collections.Generic; + +namespace Nop.Plugin.Api.Validators +{ + public class CategoryDtoValidator : BaseDtoValidator + { + + #region Constructors + + public CategoryDtoValidator(IHttpContextAccessor httpContextAccessor, IJsonHelper jsonHelper, Dictionary requestJsonDictionary) : base(httpContextAccessor, jsonHelper, requestJsonDictionary) + { + SetNameRule(); + } + + #endregion + + #region Private Methods + + private void SetNameRule() + { + SetNotNullOrEmptyCreateOrUpdateRule(c => c.Name, "invalid name", "name"); + } + + #endregion + + } } \ No newline at end of file diff --git a/Nop.Plugin.Api/Validators/CustomerDtoValidator.cs b/Nop.Plugin.Api/Validators/CustomerDtoValidator.cs index d5d5f12..c3d475d 100644 --- a/Nop.Plugin.Api/Validators/CustomerDtoValidator.cs +++ b/Nop.Plugin.Api/Validators/CustomerDtoValidator.cs @@ -1,123 +1,171 @@ -using System; -using System.Collections.Generic; -using FluentValidation; -using Nop.Core.Domain.Customers; -using Nop.Core.Infrastructure; -using Nop.Plugin.Api.DTOs.Customers; -using Nop.Plugin.Api.Helpers; - -namespace Nop.Plugin.Api.Validators -{ - public class CustomerDtoValidator : AbstractValidator - { - private ICustomerRolesHelper _customerRolesHelper = EngineContext.Current.Resolve(); - - public CustomerDtoValidator(string httpMethod, Dictionary passedPropertyValuePaires) - { - if (string.IsNullOrEmpty(httpMethod) || - httpMethod.Equals("post", StringComparison.InvariantCultureIgnoreCase)) - { - SetRuleForRoles(); - SetRuleForEmail(); - } - else if (httpMethod.Equals("put", StringComparison.InvariantCultureIgnoreCase)) - { - int parsedId = 0; - - RuleFor(x => x.Id) - .NotNull() - .NotEmpty() - .Must(id => int.TryParse(id, out parsedId) && parsedId > 0) - .WithMessage("invalid id"); - - if (passedPropertyValuePaires.ContainsKey("email")) - { - SetRuleForEmail(); - } - - // TODO: think of a way to not hardcode the json property name. - if (passedPropertyValuePaires.ContainsKey("role_ids")) - { - SetRuleForRoles(); - } - } - - if (passedPropertyValuePaires.ContainsKey("password")) - { - RuleForEach(customer => customer.Password) - .NotNull() - .NotEmpty() - .WithMessage("invalid password"); - } - - // The fields below are not required, but if they are passed they should be validated. - if (passedPropertyValuePaires.ContainsKey("billing_address")) - { - RuleFor(x => x.BillingAddress) - .SetValidator(new AddressDtoValidator()); - } - - if (passedPropertyValuePaires.ContainsKey("shipping_address")) - { - RuleFor(x => x.ShippingAddress) - .SetValidator(new AddressDtoValidator()); - } - - if (passedPropertyValuePaires.ContainsKey("addresses")) - { - RuleForEach(x => x.CustomerAddresses) - .SetValidator(new AddressDtoValidator()); - } - } - - private void SetRuleForEmail() - { - RuleFor(customer => customer.Email) - .NotNull() - .NotEmpty() - .WithMessage("email can not be empty"); - } - - private void SetRuleForRoles() - { - IList customerRoles = null; - - RuleFor>(x => x.RoleIds) - .NotNull() - .Must(roles => roles.Count > 0) - .WithMessage("role_ids required") - .DependentRules(dependentRules => dependentRules.RuleFor(dto => dto.RoleIds) - .Must(roleIds => - { - if (customerRoles == null) - { - customerRoles = _customerRolesHelper.GetValidCustomerRoles(roleIds); - } - - bool isInGuestAndRegisterRoles = _customerRolesHelper.IsInGuestsRole(customerRoles) && - _customerRolesHelper.IsInRegisteredRole(customerRoles); - - // Customer can not be in guest and register roles simultaneously - return !isInGuestAndRegisterRoles; - }) - .WithMessage("must not be in guest and register roles simultaneously") - .DependentRules(dependentRule => dependentRules.RuleFor(dto => dto.RoleIds) - .Must(roleIds => - { - if (customerRoles == null) - { - customerRoles = _customerRolesHelper.GetValidCustomerRoles(roleIds); - } - - bool isInGuestOrRegisterRoles = _customerRolesHelper.IsInGuestsRole(customerRoles) || - _customerRolesHelper.IsInRegisteredRole(customerRoles); - - // Customer must be in either guest or register role. - return isInGuestOrRegisterRoles; - }) - .WithMessage("must be in guest or register role") - ) - ); - } - } +using FluentValidation; +using FluentValidation.Results; +using FluentValidation.Validators; +using Microsoft.AspNetCore.Http; +using Nop.Core.Domain.Customers; +using Nop.Plugin.Api.DTOs; +using Nop.Plugin.Api.DTOs.Customers; +using Nop.Plugin.Api.Helpers; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; + +namespace Nop.Plugin.Api.Validators +{ + public class CustomerDtoValidator : BaseDtoValidator + { + + #region Private Fields + + private readonly ICustomerRolesHelper _customerRolesHelper; + + #endregion + + #region Constructors + + public CustomerDtoValidator(IHttpContextAccessor httpContextAccessor, IJsonHelper jsonHelper, Dictionary requestJsonDictionary, ICustomerRolesHelper customerRolesHelper) : base(httpContextAccessor, jsonHelper, requestJsonDictionary) + { + _customerRolesHelper = customerRolesHelper; + + SetEmailRule(); + SetRolesRule(); + SetPasswordRule(); + + SetBillingAddressRule(); + SetShippingAddressRule(); + + SetCustomerAddressesRule(); + SetShoppingCartItemsRule(); + } + + #endregion + + #region Private Methods + + private void SetCustomerAddressesRule() + { + var key = "addresses"; + if (RequestJsonDictionary.ContainsKey(key)) + { + RuleForEach(c => c.Addresses) + .Custom((addressDto, validationContext) => + { + var addressJsonDictionary = GetRequestJsonDictionaryCollectionItemDictionary(key, addressDto); + + var validator = new AddressDtoValidator(HttpContextAccessor, JsonHelper, addressJsonDictionary); + + //force create validation for new addresses + if (addressDto.Id == 0) + { + validator.HttpMethod = HttpMethod.Post; + } + + var validationResult = validator.Validate(addressDto); + + MergeValidationResult(validationContext, validationResult); + }); + } + } + + private void SetBillingAddressRule() + { + var key = "billing_address"; + if (RequestJsonDictionary.ContainsKey(key)) + { + RuleFor(c => c.BillingAddress).SetValidator(new AddressDtoValidator(HttpContextAccessor, JsonHelper, (Dictionary)RequestJsonDictionary[key])); + } + } + + private void SetShippingAddressRule() + { + var key = "shipping_address"; + if (RequestJsonDictionary.ContainsKey(key)) + { + RuleFor(c => c.ShippingAddress).SetValidator(new AddressDtoValidator(HttpContextAccessor, JsonHelper, (Dictionary)RequestJsonDictionary[key])); + } + } + + private void SetEmailRule() + { + SetNotNullOrEmptyCreateOrUpdateRule(c => c.Email, "invalid email", "email"); + } + + private void SetPasswordRule() + { + SetNotNullOrEmptyCreateOrUpdateRule(c => c.Password, "invalid password", "password"); + } + + private void SetRolesRule() + { + if (HttpMethod == HttpMethod.Post || RequestJsonDictionary.ContainsKey("role_ids")) + { + IList customerRoles = null; + + RuleFor(x => x.RoleIds) + .NotNull() + .Must(roles => roles.Count > 0) + .WithMessage("role_ids required") + .DependentRules(() => RuleFor(dto => dto.RoleIds) + .Must(roleIds => + { + if (customerRoles == null) + { + customerRoles = _customerRolesHelper.GetValidCustomerRoles(roleIds); + } + + var isInGuestAndRegisterRoles = _customerRolesHelper.IsInGuestsRole(customerRoles) && + _customerRolesHelper.IsInRegisteredRole(customerRoles); + + // Customer can not be in guest and register roles simultaneously + return !isInGuestAndRegisterRoles; + }) + .WithMessage("must not be in guest and register roles simultaneously") + .DependentRules(() => RuleFor(dto => dto.RoleIds) + .Must(roleIds => + { + if (customerRoles == null) + { + customerRoles = _customerRolesHelper.GetValidCustomerRoles(roleIds); + } + + var isInGuestOrRegisterRoles = _customerRolesHelper.IsInGuestsRole(customerRoles) || + _customerRolesHelper.IsInRegisteredRole(customerRoles); + + // Customer must be in either guest or register role. + return isInGuestOrRegisterRoles; + }) + .WithMessage("must be in guest or register role") + ) + ); + } + } + + private void SetShoppingCartItemsRule() + { + var key = "shopping_cart_items"; + if (RequestJsonDictionary.ContainsKey(key)) + { + RuleForEach(c => c.ShoppingCartItems) + .Custom((shoppingCartItemDto, validationContext) => + { + var shoppingCartItemJsonDictionary = GetRequestJsonDictionaryCollectionItemDictionary(key, shoppingCartItemDto); + + var validator = new ShoppingCartItemDtoValidator(HttpContextAccessor, JsonHelper, shoppingCartItemJsonDictionary); + + //force create validation for new addresses + if (shoppingCartItemDto.Id == 0) + { + validator.HttpMethod = HttpMethod.Post; + } + + var validationResult = validator.Validate(shoppingCartItemDto); + + MergeValidationResult(validationContext, validationResult); + }); + } + } + + #endregion + + } } \ No newline at end of file diff --git a/Nop.Plugin.Api/Validators/FieldsValidator.cs b/Nop.Plugin.Api/Validators/FieldsValidator.cs index 1b6d84d..ce477cc 100644 --- a/Nop.Plugin.Api/Validators/FieldsValidator.cs +++ b/Nop.Plugin.Api/Validators/FieldsValidator.cs @@ -1,45 +1,45 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; - -namespace Nop.Plugin.Api.Validators -{ - public class FieldsValidator : IFieldsValidator - { - private List GetPropertiesIntoList(string fields) - { - var properties = fields.ToLowerInvariant() - .Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries) - .Select(x => x.Trim()) - .Distinct() - .ToList(); - - return properties; - } - - public Dictionary GetValidFields(string fields, Type type) - { - // This check ensures that the fields won't be null, because it can couse exception. - fields = fields ?? string.Empty; - // This is needed in case you pass the fields as you see them in the json representation of the objects. - // By specification if the property consists of several words, each word should be separetate from the others with underscore. - fields = fields.Replace("_", string.Empty); - - var validFields = new Dictionary(); - List fieldsAsList = GetPropertiesIntoList(fields); - - foreach (var field in fieldsAsList) - { - bool propertyExists = type.GetProperty(field, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance) != null; - - if (propertyExists) - { - validFields.Add(field, true); - } - } - - return validFields; - } - } +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +namespace Nop.Plugin.Api.Validators +{ + public class FieldsValidator : IFieldsValidator + { + private static IEnumerable GetPropertiesIntoList(string fields) + { + var properties = fields.ToLowerInvariant() + .Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries) + .Select(x => x.Trim()) + .Distinct() + .ToList(); + + return properties; + } + + public Dictionary GetValidFields(string fields, Type type) + { + // This check ensures that the fields won't be null, because it can couse exception. + fields = fields ?? string.Empty; + // This is needed in case you pass the fields as you see them in the json representation of the objects. + // By specification if the property consists of several words, each word should be separetate from the others with underscore. + fields = fields.Replace("_", string.Empty); + + var validFields = new Dictionary(); + var fieldsAsList = GetPropertiesIntoList(fields); + + foreach (var field in fieldsAsList) + { + var propertyExists = type.GetProperty(field, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance) != null; + + if (propertyExists) + { + validFields.Add(field, true); + } + } + + return validFields; + } + } } \ No newline at end of file diff --git a/Nop.Plugin.Api/Validators/ManufacturerDtoValidator.cs b/Nop.Plugin.Api/Validators/ManufacturerDtoValidator.cs new file mode 100644 index 0000000..6717dab --- /dev/null +++ b/Nop.Plugin.Api/Validators/ManufacturerDtoValidator.cs @@ -0,0 +1,30 @@ +using Microsoft.AspNetCore.Http; +using Nop.Plugin.Api.Helpers; +using System.Collections.Generic; +using Nop.Plugin.Api.DTOs.Manufacturers; + +namespace Nop.Plugin.Api.Validators +{ + public class ManufacturerDtoValidator : BaseDtoValidator + { + + #region Constructors + + public ManufacturerDtoValidator(IHttpContextAccessor httpContextAccessor, IJsonHelper jsonHelper, Dictionary requestJsonDictionary) : base(httpContextAccessor, jsonHelper, requestJsonDictionary) + { + SetNameRule(); + } + + #endregion + + #region Private Methods + + private void SetNameRule() + { + SetNotNullOrEmptyCreateOrUpdateRule(m => m.Name, "invalid name", "name"); + } + + #endregion + + } +} \ No newline at end of file diff --git a/Nop.Plugin.Api/Validators/OrderDtoValidator.cs b/Nop.Plugin.Api/Validators/OrderDtoValidator.cs index a659ea6..5d7d6cb 100644 --- a/Nop.Plugin.Api/Validators/OrderDtoValidator.cs +++ b/Nop.Plugin.Api/Validators/OrderDtoValidator.cs @@ -1,42 +1,78 @@ -using System; -using System.Collections.Generic; -using FluentValidation; -using Nop.Plugin.Api.DTOs.Orders; - -namespace Nop.Plugin.Api.Validators -{ - public class OrderDtoValidator : AbstractValidator - { - public OrderDtoValidator(string httpMethod, Dictionary passedPropertyValuePaires) - { - if (string.IsNullOrEmpty(httpMethod) || - httpMethod.Equals("post", StringComparison.InvariantCultureIgnoreCase)) - { - SetCustomerIdRule(); - } - else if(httpMethod.Equals("put", StringComparison.InvariantCultureIgnoreCase)) - { - int parsedId = 0; - - RuleFor(x => x.Id) - .NotNull() - .NotEmpty() - .Must(id => int.TryParse(id, out parsedId) && parsedId > 0) - .WithMessage("Invalid id"); - - if (passedPropertyValuePaires.ContainsKey("customer_id")) - { - SetCustomerIdRule(); - } - } - } - - private void SetCustomerIdRule() - { - RuleFor(x => x.CustomerId) - .NotNull() - .Must(id => id > 0) - .WithMessage("Invalid customer_id"); - } - } +using FluentValidation; +using Microsoft.AspNetCore.Http; +using Nop.Plugin.Api.DTOs.Orders; +using Nop.Plugin.Api.Helpers; +using System.Collections.Generic; +using System.Net.Http; + +namespace Nop.Plugin.Api.Validators +{ + public class OrderDtoValidator : BaseDtoValidator + { + + #region Constructors + + public OrderDtoValidator(IHttpContextAccessor httpContextAccessor, IJsonHelper jsonHelper, Dictionary requestJsonDictionary) : base(httpContextAccessor, jsonHelper, requestJsonDictionary) + { + SetCustomerIdRule(); + SetOrderItemsRule(); + + SetBillingAddressRule(); + SetShippingAddressRule(); + } + + #endregion + + #region Private Methods + + private void SetBillingAddressRule() + { + var key = "billing_address"; + if (RequestJsonDictionary.ContainsKey(key)) + { + RuleFor(o => o.BillingAddress).SetValidator(new AddressDtoValidator(HttpContextAccessor, JsonHelper, (Dictionary)RequestJsonDictionary[key])); + } + } + + private void SetShippingAddressRule() + { + var key = "shipping_address"; + if (RequestJsonDictionary.ContainsKey(key)) + { + RuleFor(o => o.ShippingAddress).SetValidator(new AddressDtoValidator(HttpContextAccessor, JsonHelper, (Dictionary)RequestJsonDictionary[key])); + } + } + + private void SetCustomerIdRule() + { + SetGreaterThanZeroCreateOrUpdateRule(o => o.CustomerId, "invalid customer_id", "customer_id"); + } + + private void SetOrderItemsRule() + { + var key = "order_items"; + if (RequestJsonDictionary.ContainsKey(key)) + { + RuleForEach(c => c.OrderItems) + .Custom((orderItemDto, validationContext) => + { + var orderItemJsonDictionary = GetRequestJsonDictionaryCollectionItemDictionary(key, orderItemDto); + + var validator = new OrderItemDtoValidator(HttpContextAccessor, JsonHelper, orderItemJsonDictionary); + + //force create validation for new addresses + if (orderItemDto.Id == 0) + { + validator.HttpMethod = HttpMethod.Post; + } + + var validationResult = validator.Validate(orderItemDto); + MergeValidationResult(validationContext, validationResult); + }); + } + } + + #endregion + + } } \ No newline at end of file diff --git a/Nop.Plugin.Api/Validators/OrderItemDtoValidator.cs b/Nop.Plugin.Api/Validators/OrderItemDtoValidator.cs index 5ee6529..886ec32 100644 --- a/Nop.Plugin.Api/Validators/OrderItemDtoValidator.cs +++ b/Nop.Plugin.Api/Validators/OrderItemDtoValidator.cs @@ -1,47 +1,36 @@ -using System; -using System.Collections.Generic; -using FluentValidation; -using Nop.Plugin.Api.DTOs.OrderItems; - -namespace Nop.Plugin.Api.Validators -{ - public class OrderItemDtoValidator : AbstractValidator - { - public OrderItemDtoValidator(string httpMethod, Dictionary passedPropertyValuePaires) - { - if (string.IsNullOrEmpty(httpMethod) || - httpMethod.Equals("post", StringComparison.InvariantCultureIgnoreCase)) - { - SetProductRule(); - SetQuantityRule(); - } - else if (httpMethod.Equals("put", StringComparison.InvariantCultureIgnoreCase)) - { - if (passedPropertyValuePaires.ContainsKey("product_id")) - { - SetProductRule(); - } - - if (passedPropertyValuePaires.ContainsKey("quantity")) - { - SetQuantityRule(); - } - } - } - - private void SetProductRule() - { - RuleFor(x => x.ProductId) - .NotNull() - .WithMessage("Invalid product id"); - } - - private void SetQuantityRule() - { - RuleFor(x => x.Quantity) - .NotNull() - .Must(quantity => quantity > 0) - .WithMessage("Invalid quantity"); - } - } +using Microsoft.AspNetCore.Http; +using Nop.Plugin.Api.DTOs.OrderItems; +using Nop.Plugin.Api.Helpers; +using System.Collections.Generic; + +namespace Nop.Plugin.Api.Validators +{ + public class OrderItemDtoValidator : BaseDtoValidator + { + + #region Constructors + + public OrderItemDtoValidator(IHttpContextAccessor httpContextAccessor, IJsonHelper jsonHelper, Dictionary requestJsonDictionary) : base(httpContextAccessor, jsonHelper, requestJsonDictionary) + { + SetProductIdRule(); + SetQuantityRule(); + } + + #endregion + + #region Private Methods + + private void SetProductIdRule() + { + SetGreaterThanZeroCreateOrUpdateRule(o => o.ProductId, "invalid product_id", "product_id"); + } + + private void SetQuantityRule() + { + SetGreaterThanZeroCreateOrUpdateRule(o => o.Quantity, "invalid quanitty", "quantity"); + } + + #endregion + + } } \ No newline at end of file diff --git a/Nop.Plugin.Api/Validators/ProductAttributeCombinationDtoValidator.cs b/Nop.Plugin.Api/Validators/ProductAttributeCombinationDtoValidator.cs new file mode 100644 index 0000000..2b8b643 --- /dev/null +++ b/Nop.Plugin.Api/Validators/ProductAttributeCombinationDtoValidator.cs @@ -0,0 +1,36 @@ +using Microsoft.AspNetCore.Http; +using Nop.Plugin.Api.DTOs.Products; +using Nop.Plugin.Api.Helpers; +using System.Collections.Generic; + +namespace Nop.Plugin.Api.Validators +{ + public class ProductAttributeCombinationDtoValidator : BaseDtoValidator + { + + #region Constructors + + public ProductAttributeCombinationDtoValidator(IHttpContextAccessor httpContextAccessor, IJsonHelper jsonHelper, Dictionary requestJsonDictionary) : base(httpContextAccessor, jsonHelper, requestJsonDictionary) + { + SetAttributesXmlRule(); + SetProductIdRule(); + } + + #endregion + + #region Private Methods + + private void SetAttributesXmlRule() + { + SetNotNullOrEmptyCreateOrUpdateRule(p => p.AttributesXml, "invalid attributes xml", "attributes_xml"); + } + + private void SetProductIdRule() + { + SetGreaterThanZeroCreateOrUpdateRule(p => p.ProductId, "invalid product id", "product_id"); + } + + #endregion + + } +} \ No newline at end of file diff --git a/Nop.Plugin.Api/Validators/ProductAttributeDtoValidator.cs b/Nop.Plugin.Api/Validators/ProductAttributeDtoValidator.cs index b298bc3..db0f379 100644 --- a/Nop.Plugin.Api/Validators/ProductAttributeDtoValidator.cs +++ b/Nop.Plugin.Api/Validators/ProductAttributeDtoValidator.cs @@ -1,41 +1,30 @@ -using System; -using System.Collections.Generic; -using FluentValidation; -using Nop.Plugin.Api.DTOs.ProductAttributes; - -namespace Nop.Plugin.Api.Validators -{ - public class ProductAttributeDtoValidator : AbstractValidator - { - public ProductAttributeDtoValidator(string httpMethod, Dictionary passedPropertyValuePaires) - { - if (string.IsNullOrEmpty(httpMethod) || httpMethod.Equals("post", StringComparison.InvariantCultureIgnoreCase)) - { - SetNameRule(); - } - else if (httpMethod.Equals("put", StringComparison.InvariantCultureIgnoreCase)) - { - int parsedId = 0; - - RuleFor(x => x.Id) - .NotNull() - .NotEmpty() - .Must(id => int.TryParse(id, out parsedId) && parsedId > 0) - .WithMessage("invalid id"); - - if (passedPropertyValuePaires.ContainsKey("name")) - { - SetNameRule(); - } - } - } - - private void SetNameRule() - { - RuleFor(x => x.Name) - .NotNull() - .NotEmpty() - .WithMessage("name is required"); - } - } +using Microsoft.AspNetCore.Http; +using Nop.Plugin.Api.DTOs.ProductAttributes; +using Nop.Plugin.Api.Helpers; +using System.Collections.Generic; + +namespace Nop.Plugin.Api.Validators +{ + public class ProductAttributeDtoValidator : BaseDtoValidator + { + + #region Constructors + + public ProductAttributeDtoValidator(IHttpContextAccessor httpContextAccessor, IJsonHelper jsonHelper, Dictionary requestJsonDictionary) : base(httpContextAccessor, jsonHelper, requestJsonDictionary) + { + SetNameRule(); + } + + #endregion + + #region Private Methods + + private void SetNameRule() + { + SetNotNullOrEmptyCreateOrUpdateRule(p => p.Name, "invalid name", "name"); + } + + #endregion + + } } \ No newline at end of file diff --git a/Nop.Plugin.Api/Validators/ProductCategoryMappingDtoValidator.cs b/Nop.Plugin.Api/Validators/ProductCategoryMappingDtoValidator.cs index 883203a..d6c4ede 100644 --- a/Nop.Plugin.Api/Validators/ProductCategoryMappingDtoValidator.cs +++ b/Nop.Plugin.Api/Validators/ProductCategoryMappingDtoValidator.cs @@ -1,48 +1,36 @@ -using System; -using System.Collections.Generic; -using FluentValidation; -using Nop.Plugin.Api.DTOs.ProductCategoryMappings; - -namespace Nop.Plugin.Api.Validators -{ - public class ProductCategoryMappingDtoValidator : AbstractValidator - { - public ProductCategoryMappingDtoValidator(string httpMethod, Dictionary passedPropertyValuePaires) - { - if (string.IsNullOrEmpty(httpMethod) || httpMethod.Equals("post", StringComparison.InvariantCultureIgnoreCase)) - { - RuleFor(mapping => mapping.CategoryId) - .Must(categoryId => categoryId > 0) - .WithMessage("invalid category_id") - .DependentRules(mapping => - { - mapping.RuleFor(a => a.ProductId) - .Must(productId => productId > 0) - .WithMessage("invalid product_id"); - }); - } - else if (httpMethod.Equals("put", StringComparison.InvariantCultureIgnoreCase)) - { - RuleFor(mapping => mapping.Id) - .NotNull() - .NotEmpty() - .Must(id => id > 0) - .WithMessage("invalid id"); - - if (passedPropertyValuePaires.ContainsKey("category_id")) - { - RuleFor(mapping => mapping.CategoryId) - .Must(categoryId => categoryId > 0) - .WithMessage("category_id invalid"); - } - - if (passedPropertyValuePaires.ContainsKey("product_id")) - { - RuleFor(mapping => mapping.ProductId) - .Must(productId => productId > 0) - .WithMessage("product_id invalid"); - } - } - } - } -} +using Microsoft.AspNetCore.Http; +using Nop.Plugin.Api.DTOs.ProductCategoryMappings; +using Nop.Plugin.Api.Helpers; +using System.Collections.Generic; + +namespace Nop.Plugin.Api.Validators +{ + public class ProductCategoryMappingDtoValidator : BaseDtoValidator + { + + #region Constructors + + public ProductCategoryMappingDtoValidator(IHttpContextAccessor httpContextAccessor, IJsonHelper jsonHelper, Dictionary requestJsonDictionary) : base(httpContextAccessor, jsonHelper, requestJsonDictionary) + { + SetCategoryIdRule(); + SetProductIdRule(); + } + + #endregion + + #region Private Methods + + private void SetCategoryIdRule() + { + SetGreaterThanZeroCreateOrUpdateRule(p => p.CategoryId, "invalid category_id", "category_id"); + } + + private void SetProductIdRule() + { + SetGreaterThanZeroCreateOrUpdateRule(p => p.ProductId, "invalid product_id", "product_id"); + } + + #endregion + + } +} \ No newline at end of file diff --git a/Nop.Plugin.Api/Validators/ProductDtoValidator.cs b/Nop.Plugin.Api/Validators/ProductDtoValidator.cs index f64e246..673e3ba 100644 --- a/Nop.Plugin.Api/Validators/ProductDtoValidator.cs +++ b/Nop.Plugin.Api/Validators/ProductDtoValidator.cs @@ -1,41 +1,30 @@ -using System; -using System.Collections.Generic; -using FluentValidation; -using Nop.Plugin.Api.DTOs.Products; - -namespace Nop.Plugin.Api.Validators -{ - public class ProductDtoValidator : AbstractValidator - { - public ProductDtoValidator(string httpMethod, Dictionary passedPropertyValuePaires) - { - if (string.IsNullOrEmpty(httpMethod) || httpMethod.Equals("post", StringComparison.InvariantCultureIgnoreCase)) - { - SetNameRule(); - } - else if (httpMethod.Equals("put", StringComparison.InvariantCultureIgnoreCase)) - { - int parsedId = 0; - - RuleFor(x => x.Id) - .NotNull() - .NotEmpty() - .Must(id => int.TryParse(id, out parsedId) && parsedId > 0) - .WithMessage("invalid id"); - - if (passedPropertyValuePaires.ContainsKey("name")) - { - SetNameRule(); - } - } - } - - private void SetNameRule() - { - RuleFor(x => x.Name) - .NotNull() - .NotEmpty() - .WithMessage("name is required"); - } - } +using Microsoft.AspNetCore.Http; +using Nop.Plugin.Api.DTOs.Products; +using Nop.Plugin.Api.Helpers; +using System.Collections.Generic; + +namespace Nop.Plugin.Api.Validators +{ + public class ProductDtoValidator : BaseDtoValidator + { + + #region Constructors + + public ProductDtoValidator(IHttpContextAccessor httpContextAccessor, IJsonHelper jsonHelper, Dictionary requestJsonDictionary) : base(httpContextAccessor, jsonHelper, requestJsonDictionary) + { + SetNameRule(); + } + + #endregion + + #region Private Methods + + private void SetNameRule() + { + SetNotNullOrEmptyCreateOrUpdateRule(p => p.Name, "invalid name", "name"); + } + + #endregion + + } } \ No newline at end of file diff --git a/Nop.Plugin.Api/Validators/ProductManufacturerMappingDtoValidator.cs b/Nop.Plugin.Api/Validators/ProductManufacturerMappingDtoValidator.cs new file mode 100644 index 0000000..177df5d --- /dev/null +++ b/Nop.Plugin.Api/Validators/ProductManufacturerMappingDtoValidator.cs @@ -0,0 +1,37 @@ + +using Microsoft.AspNetCore.Http; +using Nop.Plugin.Api.DTOs.ProductManufacturerMappings; +using Nop.Plugin.Api.Helpers; +using System.Collections.Generic; + +namespace Nop.Plugin.Api.Validators +{ + public class ProductManufacturerMappingDtoValidator : BaseDtoValidator + { + + #region Constructors + + public ProductManufacturerMappingDtoValidator(IHttpContextAccessor httpContextAccessor, IJsonHelper jsonHelper, Dictionary requestJsonDictionary) : base(httpContextAccessor, jsonHelper, requestJsonDictionary) + { + SetManufacturerIdRule(); + SetProductIdRule(); + } + + #endregion + + #region Private Methods + + private void SetManufacturerIdRule() + { + SetGreaterThanZeroCreateOrUpdateRule(p => p.ManufacturerId, "invalid manufacturer_id", "manufacturer_id"); + } + + private void SetProductIdRule() + { + SetGreaterThanZeroCreateOrUpdateRule(p => p.ProductId, "invalid product_id", "product_id"); + } + + #endregion + + } +} \ No newline at end of file diff --git a/Nop.Plugin.Api/Validators/ShoppingCartItemDtoValidator.cs b/Nop.Plugin.Api/Validators/ShoppingCartItemDtoValidator.cs index dd5908e..69c5d3e 100644 --- a/Nop.Plugin.Api/Validators/ShoppingCartItemDtoValidator.cs +++ b/Nop.Plugin.Api/Validators/ShoppingCartItemDtoValidator.cs @@ -1,113 +1,90 @@ -using System; -using System.Collections.Generic; -using FluentValidation; -using Nop.Core.Domain.Orders; -using Nop.Plugin.Api.DTOs.ShoppingCarts; - -namespace Nop.Plugin.Api.Validators -{ - public class ShoppingCartItemDtoValidator : AbstractValidator - { - public ShoppingCartItemDtoValidator(string httpMethod, Dictionary passedPropertyValuePaires) - { - if (string.IsNullOrEmpty(httpMethod) || httpMethod.Equals("post", StringComparison.InvariantCultureIgnoreCase)) - { - SetCustomerIdRule(); - - SetProductIdRule(); - - SetQuantityRule(); - - ValidateShoppingCartType(); - } - else if (httpMethod.Equals("put", StringComparison.InvariantCultureIgnoreCase)) - { - int parsedId = 0; - - RuleFor(x => x.Id) - .NotNull() - .NotEmpty() - .Must(id => int.TryParse(id, out parsedId) && parsedId > 0) - .WithMessage("Invalid Id"); - - if (passedPropertyValuePaires.ContainsKey("customer_id")) - { - SetCustomerIdRule(); - } - - if (passedPropertyValuePaires.ContainsKey("product_id")) - { - SetProductIdRule(); - } - - if (passedPropertyValuePaires.ContainsKey("quantity")) - { - SetQuantityRule(); - } - - if (passedPropertyValuePaires.ContainsKey("shopping_cart_type")) - { - ValidateShoppingCartType(); - } - } - - if (passedPropertyValuePaires.ContainsKey("rental_start_date_utc") || passedPropertyValuePaires.ContainsKey("rental_end_date_utc")) - { - RuleFor(x => x.RentalStartDateUtc) - .NotNull() - .WithMessage("Please provide a rental start date"); - - RuleFor(x => x.RentalEndDateUtc) - .NotNull() - .WithMessage("Please provide a rental end date"); - - RuleFor(dto => dto) - .Must(dto => dto.RentalStartDateUtc < dto.RentalEndDateUtc) - .WithMessage("Rental start date should be before rental end date"); - - RuleFor(dto => dto) - .Must(dto => dto.RentalStartDateUtc > dto.CreatedOnUtc) - .WithMessage("Rental start date should be the future date"); - - RuleFor(dto => dto) - .Must(dto => dto.RentalEndDateUtc > dto.CreatedOnUtc) - .WithMessage("Rental end date should be the future date"); - } - } - - private void SetCustomerIdRule() - { - RuleFor(x => x.CustomerId) - .NotNull() - .WithMessage("Please, set customer id"); - } - - private void SetProductIdRule() - { - RuleFor(x => x.ProductId) - .NotNull() - .WithMessage("Please, set product id"); - } - - private void SetQuantityRule() - { - RuleFor(x => x.Quantity) - .NotNull() - .WithMessage("Please, set quantity"); - } - - private void ValidateShoppingCartType() - { - ShoppingCartType shoppingCartType; - - RuleFor(x => x.ShoppingCartType) - .NotNull() - .Must(x => - { - var parsed = Enum.TryParse(x, true, out shoppingCartType); - return parsed; - }) - .WithMessage("Please provide a valid shopping cart type"); - } - } +using FluentValidation; +using Microsoft.AspNetCore.Http; +using Nop.Core.Domain.Orders; +using Nop.Plugin.Api.DTOs.ShoppingCarts; +using Nop.Plugin.Api.Helpers; +using System; +using System.Collections.Generic; +using System.Net.Http; + +namespace Nop.Plugin.Api.Validators +{ + public class ShoppingCartItemDtoValidator : BaseDtoValidator + { + + #region Constructors + + public ShoppingCartItemDtoValidator(IHttpContextAccessor httpContextAccessor, IJsonHelper jsonHelper, Dictionary requestJsonDictionary) : base(httpContextAccessor, jsonHelper, requestJsonDictionary) + { + SetCustomerIdRule(); + SetProductIdRule(); + SetQuantityRule(); + SetShoppingCartTypeRule(); + + SetRentalDateRules(); + } + + #endregion + + #region Private Methods + + private void SetCustomerIdRule() + { + SetGreaterThanZeroCreateOrUpdateRule(x => x.CustomerId, "invalid customer_id", "customer_id"); + } + + private void SetProductIdRule() + { + SetGreaterThanZeroCreateOrUpdateRule(x => x.ProductId, "invalid product_id", "product_id"); + } + + private void SetQuantityRule() + { + SetGreaterThanZeroCreateOrUpdateRule(x => x.Quantity, "invalid quantity", "quantity"); + } + + private void SetRentalDateRules() + { + if (RequestJsonDictionary.ContainsKey("rental_start_date_utc") || RequestJsonDictionary.ContainsKey("rental_end_date_utc")) + { + RuleFor(x => x.RentalStartDateUtc) + .NotNull() + .WithMessage("Please provide a rental start date"); + + RuleFor(x => x.RentalEndDateUtc) + .NotNull() + .WithMessage("Please provide a rental end date"); + + RuleFor(dto => dto) + .Must(dto => dto.RentalStartDateUtc < dto.RentalEndDateUtc) + .WithMessage("Rental start date should be before rental end date"); + + RuleFor(dto => dto) + .Must(dto => dto.RentalStartDateUtc > dto.CreatedOnUtc) + .WithMessage("Rental start date should be the future date"); + + RuleFor(dto => dto) + .Must(dto => dto.RentalEndDateUtc > dto.CreatedOnUtc) + .WithMessage("Rental end date should be the future date"); + } + } + + private void SetShoppingCartTypeRule() + { + if (HttpMethod == HttpMethod.Post || RequestJsonDictionary.ContainsKey("shopping_cart_type")) + { + RuleFor(x => x.ShoppingCartType) + .NotNull() + .Must(x => + { + var parsed = Enum.TryParse(x, true, out ShoppingCartType _); + return parsed; + }) + .WithMessage("Please provide a valid shopping cart type"); + } + } + + #endregion + + } } \ No newline at end of file diff --git a/Nop.Plugin.Api/Validators/TypeValidator.cs b/Nop.Plugin.Api/Validators/TypeValidator.cs index e25d53b..74463ad 100644 --- a/Nop.Plugin.Api/Validators/TypeValidator.cs +++ b/Nop.Plugin.Api/Validators/TypeValidator.cs @@ -1,129 +1,122 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Globalization; -using System.Reflection; -using Newtonsoft.Json; -using Nop.Plugin.Api.Helpers; - -namespace Nop.Plugin.Api.Validators -{ - public class TypeValidator - { - public List InvalidProperties { get; set; } - - public TypeValidator() - { - InvalidProperties = new List(); - } - - public bool IsValid(Dictionary propertyValuePaires) - { - bool isValid = true; - - var jsonPropertyNameTypePair = new Dictionary(); - - var objectProperties = typeof(T).GetProperties(); - - foreach (var property in objectProperties) - { - JsonPropertyAttribute jsonPropertyAttribute = property.GetCustomAttribute(typeof(JsonPropertyAttribute)) as JsonPropertyAttribute; - - if (jsonPropertyAttribute != null) - { - if (!jsonPropertyNameTypePair.ContainsKey(jsonPropertyAttribute.PropertyName)) - { - jsonPropertyNameTypePair.Add(jsonPropertyAttribute.PropertyName, property.PropertyType); - } - } - } - - foreach (var pair in propertyValuePaires) - { - bool isCurrentPropertyValid = true; - - if (jsonPropertyNameTypePair.ContainsKey(pair.Key)) - { - var propertyType = jsonPropertyNameTypePair[pair.Key]; - - // handle nested properties - if (pair.Value is Dictionary) - { - isCurrentPropertyValid = ValidateNestedProperty(propertyType, (Dictionary)pair.Value); - } - // This case hadles collections. - else if (pair.Value != null && pair.Value is ICollection && - propertyType.GetInterface("IEnumerable") != null) - { - var elementsType = ReflectionHelper.GetGenericElementType(propertyType); - - ICollection propertyValueAsCollection = pair.Value as ICollection; - - // Validate the collection items. - foreach (var item in propertyValueAsCollection) - { - isCurrentPropertyValid = IsCurrentPropertyValid(elementsType, item); - - if (!isCurrentPropertyValid) break; - } - } - else - { - isCurrentPropertyValid = IsCurrentPropertyValid(jsonPropertyNameTypePair[pair.Key], pair.Value); - } - - if (!isCurrentPropertyValid) - { - isValid = false; - InvalidProperties.Add(pair.Key); - } - } - } - - return isValid; - } - - private bool ValidateNestedProperty(Type propertyType, Dictionary value) - { - bool isCurrentPropertyValid = true; - - Type constructedType = typeof(TypeValidator<>).MakeGenericType(propertyType); - var typeValidatorForNestedProperty = Activator.CreateInstance(constructedType); - - var isValidMethod = constructedType.GetMethod("IsValid"); - - isCurrentPropertyValid = (bool)isValidMethod.Invoke(typeValidatorForNestedProperty, new object[] { value }); - - return isCurrentPropertyValid; - } - - private bool IsCurrentPropertyValid(Type type, object value) - { - bool isCurrentPropertyValid = true; - - if (type.Namespace == "System") - { - TypeConverter converter = TypeDescriptor.GetConverter(type); - - var valueToValidate = value; - - // This is needed because the isValid method does not work well if the value it is trying to validate is object. - if (value != null) - { - valueToValidate = string.Format(CultureInfo.InvariantCulture, "{0}", value); - } - - if (!converter.IsValid(valueToValidate)) isCurrentPropertyValid = false; - } - else - { - if (value != null) - { - isCurrentPropertyValid = ValidateNestedProperty(type, (Dictionary)value); - } - } - - return isCurrentPropertyValid; - } - } +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Globalization; +using System.Reflection; +using Newtonsoft.Json; +using Nop.Plugin.Api.Helpers; + +namespace Nop.Plugin.Api.Validators +{ + public class TypeValidator + { + public List InvalidProperties { get; set; } + + public TypeValidator() + { + InvalidProperties = new List(); + } + + public bool IsValid(Dictionary propertyValuePaires) + { + var isValid = true; + + var jsonPropertyNameTypePair = new Dictionary(); + + var objectProperties = typeof(T).GetProperties(); + + foreach (var property in objectProperties) + { + if (property.GetCustomAttribute(typeof(JsonPropertyAttribute)) is JsonPropertyAttribute jsonPropertyAttribute) + { + if (!jsonPropertyNameTypePair.ContainsKey(jsonPropertyAttribute.PropertyName)) + { + jsonPropertyNameTypePair.Add(jsonPropertyAttribute.PropertyName, property.PropertyType); + } + } + } + + foreach (var pair in propertyValuePaires) + { + var isCurrentPropertyValid = true; + + if (jsonPropertyNameTypePair.ContainsKey(pair.Key)) + { + var propertyType = jsonPropertyNameTypePair[pair.Key]; + + // handle nested properties + if (pair.Value is Dictionary objects) + { + isCurrentPropertyValid = ValidateNestedProperty(propertyType, objects); + } + // This case hadles collections. + else if (pair.Value is ICollection propertyValueAsCollection && propertyType.GetInterface("IEnumerable") != null) + { + var elementsType = ReflectionHelper.GetGenericElementType(propertyType); + + // Validate the collection items. + foreach (var item in propertyValueAsCollection) + { + isCurrentPropertyValid = IsCurrentPropertyValid(elementsType, item); + + if (!isCurrentPropertyValid) break; + } + } + else + { + isCurrentPropertyValid = IsCurrentPropertyValid(jsonPropertyNameTypePair[pair.Key], pair.Value); + } + + if (!isCurrentPropertyValid) + { + isValid = false; + InvalidProperties.Add(pair.Key); + } + } + } + + return isValid; + } + + private static bool ValidateNestedProperty(Type propertyType, Dictionary value) + { + var constructedType = typeof(TypeValidator<>).MakeGenericType(propertyType); + var typeValidatorForNestedProperty = Activator.CreateInstance(constructedType); + + var isValidMethod = constructedType.GetMethod("IsValid"); + + var isCurrentPropertyValid = (bool)isValidMethod.Invoke(typeValidatorForNestedProperty, new object[] { value }); + + return isCurrentPropertyValid; + } + + private static bool IsCurrentPropertyValid(Type type, object value) + { + var isCurrentPropertyValid = true; + + if (type.Namespace == "System") + { + var converter = TypeDescriptor.GetConverter(type); + + var valueToValidate = value; + + // This is needed because the isValid method does not work well if the value it is trying to validate is object. + if (value != null) + { + valueToValidate = string.Format(CultureInfo.InvariantCulture, "{0}", value); + } + + if (!converter.IsValid(valueToValidate)) isCurrentPropertyValid = false; + } + else + { + if (value != null) + { + isCurrentPropertyValid = ValidateNestedProperty(type, (Dictionary)value); + } + } + + return isCurrentPropertyValid; + } + } } \ No newline at end of file diff --git a/Nop.Plugin.Api/Views/Clients/ClientSettings.cshtml b/Nop.Plugin.Api/Views/Clients/ClientSettings.cshtml index 95c0762..23f7d9d 100644 --- a/Nop.Plugin.Api/Views/Clients/ClientSettings.cshtml +++ b/Nop.Plugin.Api/Views/Clients/ClientSettings.cshtml @@ -20,7 +20,7 @@
- +
@@ -29,7 +29,7 @@
- +
diff --git a/Nop.Plugin.Api/Views/Clients/List.cshtml b/Nop.Plugin.Api/Views/Clients/List.cshtml index 21d7016..f2e9050 100644 --- a/Nop.Plugin.Api/Views/Clients/List.cshtml +++ b/Nop.Plugin.Api/Views/Clients/List.cshtml @@ -74,7 +74,7 @@ field: "ClientId", title: "@T("Plugins.Api.Admin.Client.ClientId")" }, { - field: "ClientSecretDescription", + field: "ClientSecret", title: "@T("Plugins.Api.Admin.Client.ClientSecret")" }, { field: "RedirectUrl", diff --git a/Nop.Plugin.Api/WebHooks/ApiWebHookSender.cs b/Nop.Plugin.Api/WebHooks/ApiWebHookSender.cs index 1ee865b..789acfa 100644 --- a/Nop.Plugin.Api/WebHooks/ApiWebHookSender.cs +++ b/Nop.Plugin.Api/WebHooks/ApiWebHookSender.cs @@ -1,35 +1,35 @@ -using System.Collections.Generic; -using Newtonsoft.Json.Linq; - -namespace Nop.Plugin.Api.WebHooks -{ - using Microsoft.AspNet.WebHooks; - using Microsoft.AspNet.WebHooks.Diagnostics; - - public class ApiWebHookSender : DataflowWebHookSender - { - private const string WebHookIdKey = "WebHookId"; - - public ApiWebHookSender(ILogger logger) : base(logger) - { - } - - /// - protected override JObject CreateWebHookRequestBody(WebHookWorkItem workItem) - { - JObject data = base.CreateWebHookRequestBody(workItem); - - Dictionary body = data.ToObject>(); - - // The web hook id is added to the web hook body. - // This is required in order to properly validate the web hook. - // When a web hook is created, it is created with a Secred field. - // The web hook id and the secret can be stored in the client's database, so that when a web hook is received - // it can be validated with the secret in the database. - // This ensures that the web hook is send from the proper location and that it's content were not tampered with. - body[WebHookIdKey] = workItem.WebHook.Id; - - return JObject.FromObject(body); - } - } -} +using System.Collections.Generic; +using Newtonsoft.Json.Linq; + +namespace Nop.Plugin.Api.WebHooks +{ + using Microsoft.AspNetCore.WebHooks; + using Microsoft.Extensions.Logging; + + public class ApiWebHookSender : DataflowWebHookSender + { + private const string WebHookIdKey = "WebHookId"; + + public ApiWebHookSender(ILogger logger) : base(logger) + { + } + + /// + protected override JObject CreateWebHookRequestBody(WebHookWorkItem workItem) + { + JObject data = base.CreateWebHookRequestBody(workItem); + + Dictionary body = data.ToObject>(); + + // The web hook id is added to the web hook body. + // This is required in order to properly validate the web hook. + // When a web hook is created, it is created with a Secred field. + // The web hook id and the secret can be stored in the client's database, so that when a web hook is received + // it can be validated with the secret in the database. + // This ensures that the web hook is send from the proper location and that it's content were not tampered with. + body[WebHookIdKey] = workItem.WebHook.Id; + + return JObject.FromObject(body); + } + } +} diff --git a/Nop.Plugin.Api/WebHooks/FilterProvider.cs b/Nop.Plugin.Api/WebHooks/FilterProvider.cs index cbc7c16..694eadd 100644 --- a/Nop.Plugin.Api/WebHooks/FilterProvider.cs +++ b/Nop.Plugin.Api/WebHooks/FilterProvider.cs @@ -1,44 +1,44 @@ -using System.Collections.ObjectModel; -using System.Threading.Tasks; -using Nop.Plugin.Api.Constants; - -namespace Nop.Plugin.Api.WebHooks -{ - using Microsoft.AspNet.WebHooks; - - public class FilterProvider : IWebHookFilterProvider - { - private readonly Collection filters = new Collection - { - new WebHookFilter { Name = WebHookNames.CustomersCreate, Description = "A customer has been registered."}, - new WebHookFilter { Name = WebHookNames.CustomersUpdate, Description = "A customer has been updated."}, - new WebHookFilter { Name = WebHookNames.CustomersDelete, Description = "A customer has been deleted."}, - new WebHookFilter { Name = WebHookNames.ProductsCreate, Description = "A product has been created."}, - new WebHookFilter { Name = WebHookNames.ProductsUpdate, Description = "A product has been updated."}, - new WebHookFilter { Name = WebHookNames.ProductsDelete, Description = "A product has been deleted."}, - new WebHookFilter { Name = WebHookNames.ProductsUnmap, Description = "A product has been unmapped from the store."}, - new WebHookFilter { Name = WebHookNames.CategoriesCreate, Description = "A category has been created."}, - new WebHookFilter { Name = WebHookNames.CategoriesUpdate, Description = "A category has been updated."}, - new WebHookFilter { Name = WebHookNames.CategoriesDelete, Description = "A category has been deleted."}, - new WebHookFilter { Name = WebHookNames.CategoriesUnmap, Description = "A category has been unmapped from the store."}, - new WebHookFilter { Name = WebHookNames.OrdersCreate, Description = "An order has been created."}, - new WebHookFilter { Name = WebHookNames.OrdersUpdate, Description = "An order has been updated."}, - new WebHookFilter { Name = WebHookNames.OrdersDelete, Description = "An order has been deleted."}, - new WebHookFilter { Name = WebHookNames.ProductCategoryMapsCreate, Description = "A product category map has been created."}, - new WebHookFilter { Name = WebHookNames.ProductCategoryMapsUpdate, Description = "A product category map has been updated."}, - new WebHookFilter { Name = WebHookNames.ProductCategoryMapsDelete, Description = "A product category map has been deleted."}, - new WebHookFilter { Name = WebHookNames.StoresUpdate, Description = "A store has been updated."}, - new WebHookFilter { Name = WebHookNames.LanguagesCreate, Description = "A language has been created."}, - new WebHookFilter { Name = WebHookNames.LanguagesUpdate, Description = "A language has been updated."}, - new WebHookFilter { Name = WebHookNames.LanguagesDelete, Description = "A language has been deleted."}, - new WebHookFilter { Name = WebHookNames.NewsLetterSubscriptionCreate, Description = "A news letter subscription has been created."}, - new WebHookFilter { Name = WebHookNames.NewsLetterSubscriptionUpdate, Description = "A news letter subscription has been updated."}, - new WebHookFilter { Name = WebHookNames.NewsLetterSubscriptionDelete, Description = "A news letter subscription has been deleted."} - }; - - public Task> GetFiltersAsync() - { - return Task.FromResult(this.filters); - } - } -} +using System.Collections.ObjectModel; +using System.Threading.Tasks; +using Nop.Plugin.Api.Constants; + +namespace Nop.Plugin.Api.WebHooks +{ + using Microsoft.AspNetCore.WebHooks; + + public class FilterProvider : IWebHookFilterProvider + { + private readonly Collection filters = new Collection + { + new WebHookFilter { Name = WebHookNames.CustomersCreate, Description = "A customer has been registered."}, + new WebHookFilter { Name = WebHookNames.CustomersUpdate, Description = "A customer has been updated."}, + new WebHookFilter { Name = WebHookNames.CustomersDelete, Description = "A customer has been deleted."}, + new WebHookFilter { Name = WebHookNames.ProductsCreate, Description = "A product has been created."}, + new WebHookFilter { Name = WebHookNames.ProductsUpdate, Description = "A product has been updated."}, + new WebHookFilter { Name = WebHookNames.ProductsDelete, Description = "A product has been deleted."}, + new WebHookFilter { Name = WebHookNames.ProductsUnmap, Description = "A product has been unmapped from the store."}, + new WebHookFilter { Name = WebHookNames.CategoriesCreate, Description = "A category has been created."}, + new WebHookFilter { Name = WebHookNames.CategoriesUpdate, Description = "A category has been updated."}, + new WebHookFilter { Name = WebHookNames.CategoriesDelete, Description = "A category has been deleted."}, + new WebHookFilter { Name = WebHookNames.CategoriesUnmap, Description = "A category has been unmapped from the store."}, + new WebHookFilter { Name = WebHookNames.OrdersCreate, Description = "An order has been created."}, + new WebHookFilter { Name = WebHookNames.OrdersUpdate, Description = "An order has been updated."}, + new WebHookFilter { Name = WebHookNames.OrdersDelete, Description = "An order has been deleted."}, + new WebHookFilter { Name = WebHookNames.ProductCategoryMapsCreate, Description = "A product category map has been created."}, + new WebHookFilter { Name = WebHookNames.ProductCategoryMapsUpdate, Description = "A product category map has been updated."}, + new WebHookFilter { Name = WebHookNames.ProductCategoryMapsDelete, Description = "A product category map has been deleted."}, + new WebHookFilter { Name = WebHookNames.StoresUpdate, Description = "A store has been updated."}, + new WebHookFilter { Name = WebHookNames.LanguagesCreate, Description = "A language has been created."}, + new WebHookFilter { Name = WebHookNames.LanguagesUpdate, Description = "A language has been updated."}, + new WebHookFilter { Name = WebHookNames.LanguagesDelete, Description = "A language has been deleted."}, + new WebHookFilter { Name = WebHookNames.NewsLetterSubscriptionCreate, Description = "A news letter subscription has been created."}, + new WebHookFilter { Name = WebHookNames.NewsLetterSubscriptionUpdate, Description = "A news letter subscription has been updated."}, + new WebHookFilter { Name = WebHookNames.NewsLetterSubscriptionDelete, Description = "A news letter subscription has been deleted."} + }; + + public Task> GetFiltersAsync() + { + return Task.FromResult(this.filters); + } + } +} diff --git a/Nop.Plugin.Api/WebHooks/NopWebHookStore.cs b/Nop.Plugin.Api/WebHooks/NopWebHookStore.cs new file mode 100644 index 0000000..a910d5a --- /dev/null +++ b/Nop.Plugin.Api/WebHooks/NopWebHookStore.cs @@ -0,0 +1,29 @@ +using Microsoft.AspNetCore.WebHooks; +using Microsoft.EntityFrameworkCore; +using Nop.Core.Infrastructure; +using Nop.Plugin.Api.Data; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Nop.Plugin.Api.WebHooks +{ + public class NopWebHookStore : DbWebHookStore + { + //private ApiObjectContext _dbContext; + + //public NopWebHookStore(ApiObjectContext dbContext) + //{ + // _dbContext = dbContext; + //} + + public NopWebHookStore() + { + } + + protected override ApiObjectContext GetContext() + { + return EngineContext.Current.Resolve(); + } + } +} diff --git a/Nop.Plugin.Api/WebHooks/NopWebHooksLogger.cs b/Nop.Plugin.Api/WebHooks/NopWebHooksLogger.cs index 70ef670..748c436 100644 --- a/Nop.Plugin.Api/WebHooks/NopWebHooksLogger.cs +++ b/Nop.Plugin.Api/WebHooks/NopWebHooksLogger.cs @@ -1,65 +1,65 @@ -using Microsoft.AspNet.WebHooks.Diagnostics; -using Nop.Core; -using Nop.Core.Data; -using Nop.Core.Domain.Logging; -using Nop.Core.Infrastructure; -using Nop.Plugin.Api.Domain; -using System; -using System.IO; -using System.Web.Http.Tracing; - -namespace Nop.Plugin.Api.WebHooks -{ - /// - /// This Logger is injected into the WebHooks classes that use async calls, which are - /// executed in different threads and at the time of execution the HttpContext as well its - /// HttpContext.RequestServices may not be avilable. So any calls to EngineContext.Current.Resolve() will throw - /// an exception i.e we can't use the nopCommerce ILogger service which tries to resolve the current store etc. - /// - public class NopWebHooksLogger : ILogger - { - private readonly bool _enableLogging; - private readonly IRepository _logRepository; - - private static object lockObject = new object(); - - public NopWebHooksLogger(ApiSettings apiSettings, IRepository logRepository) - { - _enableLogging = apiSettings.EnableLogging; - _logRepository = logRepository; - } - - public void Log(TraceLevel level, string message, Exception ex) - { - try - { - if (_enableLogging) - { - if (message != null) - { - lock (lockObject) - { - var log = new Log - { - LogLevel = LogLevel.Information, - ShortMessage = message, - FullMessage = ex?.ToString(), - IpAddress = "", - Customer = null, - PageUrl = "", - ReferrerUrl = "", - CreatedOnUtc = DateTime.UtcNow - }; - - _logRepository.Insert(log); - } - } - - } - } - catch (Exception e) - { - } - } - } -} +//using Microsoft.AspNet.WebHooks.Diagnostics; +//using Nop.Core; +//using Nop.Core.Data; +//using Nop.Core.Domain.Logging; +//using Nop.Core.Infrastructure; +//using Nop.Plugin.Api.Domain; +//using System; +//using System.IO; +//using System.Web.Http.Tracing; + +//namespace Nop.Plugin.Api.WebHooks +//{ +// /// +// /// This Logger is injected into the WebHooks classes that use async calls, which are +// /// executed in different threads and at the time of execution the HttpContext as well its +// /// HttpContext.RequestServices may not be avilable. So any calls to EngineContext.Current.Resolve() will throw +// /// an exception i.e we can't use the nopCommerce ILogger service which tries to resolve the current store etc. +// /// +// public class NopWebHooksLogger : ILogger +// { +// private readonly bool _enableLogging; +// private readonly IRepository _logRepository; + +// private static object lockObject = new object(); + +// public NopWebHooksLogger(ApiSettings apiSettings, IRepository logRepository) +// { +// _enableLogging = apiSettings.EnableLogging; +// _logRepository = logRepository; +// } + +// public void Log(TraceLevel level, string message, Exception ex) +// { +// try +// { +// if (_enableLogging) +// { +// if (message != null) +// { +// lock (lockObject) +// { +// var log = new Log +// { +// LogLevel = LogLevel.Information, +// ShortMessage = message, +// FullMessage = ex?.ToString(), +// IpAddress = "", +// Customer = null, +// PageUrl = "", +// ReferrerUrl = "", +// CreatedOnUtc = DateTime.UtcNow +// }; + +// _logRepository.Insert(log); +// } +// } + +// } +// } +// catch (Exception e) +// { +// } +// } +// } +//} diff --git a/Nop.Plugin.Api/WebHooks/WebHookEventConsumer.cs b/Nop.Plugin.Api/WebHooks/WebHookEventConsumer.cs index b020708..0a7c7a0 100644 --- a/Nop.Plugin.Api/WebHooks/WebHookEventConsumer.cs +++ b/Nop.Plugin.Api/WebHooks/WebHookEventConsumer.cs @@ -1,562 +1,555 @@ -using System.Collections.Generic; -using System.Linq; -using Nop.Core; -using Nop.Core.Domain.Catalog; -using Nop.Core.Domain.Common; -using Nop.Core.Domain.Customers; -using Nop.Core.Domain.Localization; -using Nop.Core.Domain.Media; -using Nop.Core.Events; -using Nop.Core.Infrastructure; -using Nop.Plugin.Api.Services; -using Nop.Services.Events; -using Nop.Plugin.Api.DTOs.Customers; -using Nop.Plugin.Api.Constants; -using Nop.Plugin.Api.DTOs.Products; -using Nop.Plugin.Api.Helpers; -using Nop.Plugin.Api.DTOs.Categories; -using Nop.Core.Domain.Orders; -using Nop.Core.Domain.Stores; -using Nop.Plugin.Api.DTOs.Languages; -using Nop.Plugin.Api.DTOs.Orders; -using Nop.Plugin.Api.DTOs.ProductCategoryMappings; -using Nop.Plugin.Api.DTOs.Stores; -using Nop.Plugin.Api.MappingExtensions; -using Nop.Services.Catalog; -using Nop.Services.Stores; - -namespace Nop.Plugin.Api.WebHooks -{ - using Microsoft.AspNet.WebHooks; - using Nop.Core.Caching; - using Nop.Core.Domain.Messages; - - public class WebHookEventConsumer : IConsumer>, - IConsumer>, - IConsumer>, - IConsumer>, - IConsumer>, - IConsumer>, - IConsumer>, - IConsumer>, - IConsumer>, - IConsumer>, - IConsumer>, - IConsumer>, - IConsumer>, - IConsumer>, - IConsumer>, - IConsumer>, - IConsumer>, - IConsumer>, - IConsumer>, - IConsumer>, - IConsumer>, - IConsumer>, - IConsumer>, - IConsumer>, - IConsumer>, - IConsumer> - { - private IWebHookManager _webHookManager; - private readonly ICustomerApiService _customerApiService; - private readonly ICategoryApiService _categoryApiService; - private readonly IProductApiService _productApiService; - private readonly IProductService _productService; - private readonly ICategoryService _categoryService; - private readonly IStoreMappingService _storeMappingService; - private readonly IProductPictureService _productPictureService; - private IStoreService _storeService; - private IStoreContext _storeContext; - private readonly IStaticCacheManager _cacheManager; - - private IDTOHelper _dtoHelper; - - public WebHookEventConsumer(IStoreService storeService) - { - _customerApiService = EngineContext.Current.Resolve(); - _categoryApiService = EngineContext.Current.Resolve(); - _productApiService = EngineContext.Current.Resolve(); - _dtoHelper = EngineContext.Current.Resolve(); - _storeService = EngineContext.Current.Resolve(); - _productPictureService = EngineContext.Current.Resolve(); - _productService = EngineContext.Current.Resolve(); - _categoryService = EngineContext.Current.Resolve(); - _storeMappingService = EngineContext.Current.Resolve(); - _storeContext = EngineContext.Current.Resolve(); - _cacheManager = EngineContext.Current.Resolve(); - } - - private IWebHookManager WebHookManager - { - get - { - if (_webHookManager == null) - { - IWebHookService webHookService = EngineContext.Current.Resolve(); - _webHookManager = webHookService.GetWebHookManager(); - } - - return _webHookManager; - } - } - - public void HandleEvent(EntityInserted eventMessage) - { - // There is no need to send webhooks for guest customers. - if (eventMessage.Entity.IsGuest()) - { - return; - } - - CustomerDto customer = _customerApiService.GetCustomerById(eventMessage.Entity.Id); - var storeIds = new List(); - - if (customer.RegisteredInStoreId.HasValue) - { - storeIds.Add(customer.RegisteredInStoreId.Value); - } - - NotifyRegisteredWebHooks(customer, WebHookNames.CustomersCreate, storeIds); - } - - public void HandleEvent(EntityUpdated eventMessage) - { - // There is no need to send webhooks for guest customers. - if (eventMessage.Entity.IsGuest()) - { - return; - } - - CustomerDto customer = _customerApiService.GetCustomerById(eventMessage.Entity.Id, true); - - // In nopCommerce the Customer, Product, Category and Order entities are not deleted. - // Instead the Deleted property of the entity is set to true. - string webhookEvent = WebHookNames.CustomersUpdate; - - if (customer.Deleted == true) - { - webhookEvent = WebHookNames.CustomersDelete; - } - - var storeIds = new List(); - - if (customer.RegisteredInStoreId.HasValue) - { - storeIds.Add(customer.RegisteredInStoreId.Value); - } - - NotifyRegisteredWebHooks(customer, webhookEvent, storeIds); - } - - public void HandleEvent(EntityInserted eventMessage) - { - ProductDto productDto = _dtoHelper.PrepareProductDTO(eventMessage.Entity); - - // The Store mappings of the product are still not saved, so all webhooks will be triggered - // no matter for which store are registered. - NotifyRegisteredWebHooks(productDto, WebHookNames.ProductsCreate, productDto.StoreIds); - } - - public void HandleEvent(EntityUpdated eventMessage) - { - ProductDto productDto = _dtoHelper.PrepareProductDTO(eventMessage.Entity); - - ProductUpdated(productDto); - } - - public void HandleEvent(EntityInserted eventMessage) - { - CategoryDto categoryDto = _dtoHelper.PrepareCategoryDTO(eventMessage.Entity); - - // The Store mappings of the category are still not saved, so all webhooks will be triggered - // no matter for which store are registered. - NotifyRegisteredWebHooks(categoryDto, WebHookNames.CategoriesCreate, categoryDto.StoreIds); - } - - public void HandleEvent(EntityUpdated eventMessage) - { - CategoryDto categoryDto = _dtoHelper.PrepareCategoryDTO(eventMessage.Entity); - - string webhookEvent = WebHookNames.CategoriesUpdate; - - if (categoryDto.Deleted == true) - { - webhookEvent = WebHookNames.CategoriesDelete; - } - - NotifyRegisteredWebHooks(categoryDto, webhookEvent, categoryDto.StoreIds); - } - - public void HandleEvent(EntityInserted eventMessage) - { - OrderDto orderDto = _dtoHelper.PrepareOrderDTO(eventMessage.Entity); - - var storeIds = new List(); - - if (orderDto.StoreId.HasValue) - { - storeIds.Add(orderDto.StoreId.Value); - } - - NotifyRegisteredWebHooks(orderDto, WebHookNames.OrdersCreate, storeIds); - } - - public void HandleEvent(EntityUpdated eventMessage) - { - OrderDto orderDto = _dtoHelper.PrepareOrderDTO(eventMessage.Entity); - - string webhookEvent = WebHookNames.OrdersUpdate; - - if (orderDto.Deleted == true) - { - webhookEvent = WebHookNames.OrdersDelete; - } - - var storeIds = new List(); - - if (orderDto.StoreId.HasValue) - { - storeIds.Add(orderDto.StoreId.Value); - } - - NotifyRegisteredWebHooks(orderDto, webhookEvent, storeIds); - } - - public void HandleEvent(EntityInserted eventMessage) - { - HandleStoreMappingEvent(eventMessage.Entity.EntityId, eventMessage.Entity.EntityName); - } - - public void HandleEvent(EntityDeleted eventMessage) - { - HandleStoreMappingEvent(eventMessage.Entity.EntityId, eventMessage.Entity.EntityName); - } - - public void HandleEvent(EntityInserted eventMessage) - { - if (eventMessage.Entity.Key == SystemCustomerAttributeNames.FirstName || - eventMessage.Entity.Key == SystemCustomerAttributeNames.LastName || - eventMessage.Entity.Key == SystemCustomerAttributeNames.LanguageId) - { - var customerDto = _customerApiService.GetCustomerById(eventMessage.Entity.EntityId); - - var storeIds = new List(); - - if (customerDto.RegisteredInStoreId.HasValue) - { - storeIds.Add(customerDto.RegisteredInStoreId.Value); - } - - NotifyRegisteredWebHooks(customerDto, WebHookNames.CustomersUpdate, storeIds); - } - } - - public void HandleEvent(EntityUpdated eventMessage) - { - if (eventMessage.Entity.Key == SystemCustomerAttributeNames.FirstName || - eventMessage.Entity.Key == SystemCustomerAttributeNames.LastName || - eventMessage.Entity.Key == SystemCustomerAttributeNames.LanguageId) - { - var customerDto = _customerApiService.GetCustomerById(eventMessage.Entity.EntityId); - - var storeIds = new List(); - - if (customerDto.RegisteredInStoreId.HasValue) - { - storeIds.Add(customerDto.RegisteredInStoreId.Value); - } - - NotifyRegisteredWebHooks(customerDto, WebHookNames.CustomersUpdate, storeIds); - } - } - - public void HandleEvent(EntityUpdated eventMessage) - { - StoreDto storeDto = _dtoHelper.PrepareStoreDTO(eventMessage.Entity); - - int storeId; - - if (int.TryParse(storeDto.Id, out storeId)) - { - var storeIds = new List(); - storeIds.Add(storeId); - - NotifyRegisteredWebHooks(storeDto, WebHookNames.StoresUpdate, storeIds); - } - } - - public void HandleEvent(EntityInserted eventMessage) - { - NotifyProductCategoryMappingWebhook(eventMessage.Entity, WebHookNames.ProductCategoryMapsCreate); - } - - public void HandleEvent(EntityUpdated eventMessage) - { - NotifyProductCategoryMappingWebhook(eventMessage.Entity, WebHookNames.ProductCategoryMapsUpdate); - } - - public void HandleEvent(EntityDeleted eventMessage) - { - NotifyProductCategoryMappingWebhook(eventMessage.Entity, WebHookNames.ProductCategoryMapsDelete); - } - - public void HandleEvent(EntityInserted eventMessage) - { - LanguageDto langaDto = _dtoHelper.PrepateLanguageDto(eventMessage.Entity); - - NotifyRegisteredWebHooks(langaDto, WebHookNames.LanguagesCreate, langaDto.StoreIds); - } - - public void HandleEvent(EntityUpdated eventMessage) - { - LanguageDto langaDto = _dtoHelper.PrepateLanguageDto(eventMessage.Entity); - - NotifyRegisteredWebHooks(langaDto, WebHookNames.LanguagesUpdate, langaDto.StoreIds); - } - - public void HandleEvent(EntityDeleted eventMessage) - { - LanguageDto langaDto = _dtoHelper.PrepateLanguageDto(eventMessage.Entity); - - NotifyRegisteredWebHooks(langaDto, WebHookNames.LanguagesDelete, langaDto.StoreIds); - } - - public void HandleEvent(EntityInserted eventMessage) - { - var product = _productApiService.GetProductById(eventMessage.Entity.ProductId); - - if (product != null) - { - ProductDto productDto = _dtoHelper.PrepareProductDTO(product); - - ProductUpdated(productDto); - } - } - - public void HandleEvent(EntityUpdated eventMessage) - { - var product = _productApiService.GetProductById(eventMessage.Entity.ProductId); - - if (product != null) - { - ProductDto productDto = _dtoHelper.PrepareProductDTO(product); - - ProductUpdated(productDto); - } - } - - public void HandleEvent(EntityDeleted eventMessage) - { - var product = _productApiService.GetProductById(eventMessage.Entity.ProductId); - - if (product != null) - { - ProductDto productDto = _dtoHelper.PrepareProductDTO(product); - - ProductUpdated(productDto); - } - } - - // We trigger a product updated WebHook when a picture used in a product is updated. - // This is required, because when the product title is changed, the product is updated first - // and then the picture urls are chaged. In order for the WebHook consumer to have the latest - // product picture urls the following code is used. - public void HandleEvent(EntityUpdated eventMessage) - { - var productPicture = _productPictureService.GetProductPictureByPictureId(eventMessage.Entity.Id); - - if (productPicture != null) - { - var product = _productApiService.GetProductById(productPicture.ProductId); - - if (product != null) - { - ProductDto productDto = _dtoHelper.PrepareProductDTO(product); - - ProductUpdated(productDto); - } - } - } - - public void HandleEvent(EntityDeleted eventMessage) - { - _cacheManager.RemoveByPattern(Configurations.NEWSLETTER_SUBSCRIBERS_KEY); - - NewsLetterSubscriptionDto newsLetterSubscriptionDto = eventMessage.Entity.ToDto(); - - var storeIds = new List - { - newsLetterSubscriptionDto.StoreId - }; - - NotifyRegisteredWebHooks(newsLetterSubscriptionDto, WebHookNames.NewsLetterSubscriptionDelete, storeIds); - } - - public void HandleEvent(EntityInserted eventMessage) - { - _cacheManager.RemoveByPattern(Configurations.NEWSLETTER_SUBSCRIBERS_KEY); - - NewsLetterSubscriptionDto newsLetterSubscriptionDto = eventMessage.Entity.ToDto(); - - var storeIds = new List - { - newsLetterSubscriptionDto.StoreId - }; - - NotifyRegisteredWebHooks(newsLetterSubscriptionDto, WebHookNames.NewsLetterSubscriptionCreate, storeIds); - } - - public void HandleEvent(EntityUpdated eventMessage) - { - _cacheManager.RemoveByPattern(Configurations.NEWSLETTER_SUBSCRIBERS_KEY); - - NewsLetterSubscriptionDto newsLetterSubscriptionDto = eventMessage.Entity.ToDto(); - - var storeIds = new List - { - newsLetterSubscriptionDto.StoreId - }; - - NotifyRegisteredWebHooks(newsLetterSubscriptionDto, WebHookNames.NewsLetterSubscriptionUpdate, storeIds); - } - - private void NotifyProductCategoryMappingWebhook(ProductCategory productCategory, string eventName) - { - var storeIds = GetStoreIdsForProductCategoryMap(productCategory); - - if (storeIds == null) - { - return; - } - - ProductCategoryMappingDto productCategoryMappingDto = productCategory.ToDto(); - - NotifyRegisteredWebHooks(productCategoryMappingDto, eventName, storeIds); - } - - private List GetStoreIdsForProductCategoryMap(ProductCategory productCategory) - { - // Send a webhook event only for the stores that can access the product and category - // in the current product category map. - Product product = _productService.GetProductById(productCategory.ProductId); - Category category = _categoryService.GetCategoryById(productCategory.CategoryId); - - if (product == null || category == null) - { - return null; - } - - var productStoreIds = _storeMappingService.GetStoresIdsWithAccess(product); - - var categoryStoreIds = _storeMappingService.GetStoresIdsWithAccess(category); - - return productStoreIds.Intersect(categoryStoreIds).ToList(); - } - - private void ProductUpdated(ProductDto productDto) - { - string webhookEvent = WebHookNames.ProductsUpdate; - - if (productDto.Deleted == true) - { - webhookEvent = WebHookNames.ProductsDelete; - } - - NotifyRegisteredWebHooks(productDto, webhookEvent, productDto.StoreIds); - } - - private void HandleStoreMappingEvent(int entityId, string entityName) - { - // When creating or editing a category after saving the store mapping the category is not updated - // so we should listen for StoreMapping update/delete and fire a webhook with the updated entityDto(with correct storeIds). - if (entityName == "Category") - { - var category = _categoryApiService.GetCategoryById(entityId); - - if (category != null) - { - CategoryDto categoryDto = _dtoHelper.PrepareCategoryDTO(category); - - string webhookEvent = WebHookNames.CategoriesUpdate; - - if (categoryDto.Deleted == true) - { - webhookEvent = WebHookNames.CategoriesDelete; - } - - NotifyRegisteredWebHooks(categoryDto, webhookEvent, categoryDto.StoreIds); - } - } - else if (entityName == "Product") - { - var product = _productApiService.GetProductById(entityId); - - if (product != null) - { - ProductDto productDto = _dtoHelper.PrepareProductDTO(product); - - string webhookEvent = WebHookNames.ProductsUpdate; - - if (productDto.Deleted == true) - { - webhookEvent = WebHookNames.ProductsDelete; - } - - NotifyRegisteredWebHooks(productDto, webhookEvent, productDto.StoreIds); - } - } - } - - private void NotifyRegisteredWebHooks(T entityDto, string webhookEvent, List storeIds) - { - if (storeIds.Count > 0) - { - // Notify all webhooks that the entity is mapped to their store. - WebHookManager.NotifyAllAsync(webhookEvent, new { Item = entityDto }, (hook, hookUser) => IsEntityMatchingTheWebHookStoreId(hookUser, storeIds)); - - if (typeof(T) == typeof(ProductDto) || typeof(T) == typeof(CategoryDto)) - { - NotifyUnmappedEntityWebhooks(entityDto, storeIds); - } - } - else - { - WebHookManager.NotifyAllAsync(webhookEvent, new { Item = entityDto }); - } - } - - private void NotifyUnmappedEntityWebhooks(T entityDto, List storeIds) - { - if (typeof(T) == typeof(ProductDto)) - { - // The product is not mapped to the store. - // Notify all webhooks that the entity is not mapped to their store. - WebHookManager.NotifyAllAsync(WebHookNames.ProductsUnmap, new { Item = entityDto }, - (hook, hookUser) => !IsEntityMatchingTheWebHookStoreId(hookUser, storeIds)); - } - else if (typeof(T) == typeof(CategoryDto)) - { - // The category is not mapped to the store. - // Notify all webhooks that the entity is not mapped to their store. - WebHookManager.NotifyAllAsync(WebHookNames.CategoriesUnmap, new { Item = entityDto }, - (hook, hookUser) => !IsEntityMatchingTheWebHookStoreId(hookUser, storeIds)); - } - } - - private bool IsEntityMatchingTheWebHookStoreId(string webHookUser, List storeIds) - { - // When we register the webhooks we add "-storeId" at the end of the webHookUser string. - // That way we can check to which store is mapped the webHook. - foreach (var id in storeIds) - { - if (webHookUser.EndsWith("-" + id)) - { - return true; - } - } - - return false; - } - } -} +using System.Collections.Generic; +using System.Linq; +using Nop.Core; +using Nop.Core.Domain.Catalog; +using Nop.Core.Domain.Common; +using Nop.Core.Domain.Customers; +using Nop.Core.Domain.Localization; +using Nop.Core.Domain.Media; +using Nop.Core.Events; +using Nop.Core.Infrastructure; +using Nop.Plugin.Api.Services; +using Nop.Services.Events; +using Nop.Plugin.Api.DTOs.Customers; +using Nop.Plugin.Api.Constants; +using Nop.Plugin.Api.DTOs.Products; +using Nop.Plugin.Api.Helpers; +using Nop.Plugin.Api.DTOs.Categories; +using Nop.Core.Domain.Orders; +using Nop.Core.Domain.Stores; +using Nop.Plugin.Api.DTOs.Languages; +using Nop.Plugin.Api.DTOs.Orders; +using Nop.Plugin.Api.DTOs.ProductCategoryMappings; +using Nop.Plugin.Api.DTOs.Stores; +using Nop.Plugin.Api.MappingExtensions; +using Nop.Services.Catalog; +using Nop.Services.Stores; + +namespace Nop.Plugin.Api.WebHooks +{ + using Microsoft.AspNetCore.WebHooks; + using Nop.Core.Caching; + using Nop.Core.Domain.Messages; + + public class WebHookEventConsumer : IConsumer>, + IConsumer>, + IConsumer>, + IConsumer>, + IConsumer>, + IConsumer>, + IConsumer>, + IConsumer>, + IConsumer>, + IConsumer>, + IConsumer>, + IConsumer>, + IConsumer>, + IConsumer>, + IConsumer>, + IConsumer>, + IConsumer>, + IConsumer>, + IConsumer>, + IConsumer>, + IConsumer>, + IConsumer>, + IConsumer>, + IConsumer>, + IConsumer>, + IConsumer> + { + private IWebHookManager _webHookManager; + private readonly ICustomerApiService _customerApiService; + private readonly ICategoryApiService _categoryApiService; + private readonly IProductApiService _productApiService; + private readonly IProductService _productService; + private readonly ICategoryService _categoryService; + private readonly IStoreMappingService _storeMappingService; + private readonly IProductPictureService _productPictureService; + private readonly IStaticCacheManager _cacheManager; + + private IDTOHelper _dtoHelper; + + public WebHookEventConsumer(IStoreService storeService) + { + _customerApiService = EngineContext.Current.Resolve(); + _categoryApiService = EngineContext.Current.Resolve(); + _productApiService = EngineContext.Current.Resolve(); + _dtoHelper = EngineContext.Current.Resolve(); + _productPictureService = EngineContext.Current.Resolve(); + _productService = EngineContext.Current.Resolve(); + _categoryService = EngineContext.Current.Resolve(); + _storeMappingService = EngineContext.Current.Resolve(); + _cacheManager = EngineContext.Current.Resolve(); + } + + private IWebHookManager WebHookManager + { + get + { + if (_webHookManager == null) + { + IWebHookService webHookService = EngineContext.Current.Resolve(); + _webHookManager = webHookService.GetWebHookManager(); + } + + return _webHookManager; + } + } + + public void HandleEvent(EntityInsertedEvent eventMessage) + { + // There is no need to send webhooks for guest customers. + if (eventMessage.Entity.IsGuest()) + { + return; + } + + CustomerDto customer = _customerApiService.GetCustomerById(eventMessage.Entity.Id); + var storeIds = new List(); + + if (customer.RegisteredInStoreId.HasValue) + { + storeIds.Add(customer.RegisteredInStoreId.Value); + } + + NotifyRegisteredWebHooks(customer, WebHookNames.CustomersCreate, storeIds); + } + + public void HandleEvent(EntityUpdatedEvent eventMessage) + { + // There is no need to send webhooks for guest customers. + if (eventMessage.Entity.IsGuest()) + { + return; + } + + CustomerDto customer = _customerApiService.GetCustomerById(eventMessage.Entity.Id, true); + + // In nopCommerce the Customer, Product, Category and Order entities are not deleted. + // Instead the Deleted property of the entity is set to true. + string webhookEvent = WebHookNames.CustomersUpdate; + + if (customer.Deleted == true) + { + webhookEvent = WebHookNames.CustomersDelete; + } + + var storeIds = new List(); + + if (customer.RegisteredInStoreId.HasValue) + { + storeIds.Add(customer.RegisteredInStoreId.Value); + } + + NotifyRegisteredWebHooks(customer, webhookEvent, storeIds); + } + + public void HandleEvent(EntityInsertedEvent eventMessage) + { + ProductDto productDto = _dtoHelper.PrepareProductDTO(eventMessage.Entity); + + // The Store mappings of the product are still not saved, so all webhooks will be triggered + // no matter for which store are registered. + NotifyRegisteredWebHooks(productDto, WebHookNames.ProductsCreate, productDto.StoreIds); + } + + public void HandleEvent(EntityUpdatedEvent eventMessage) + { + ProductDto productDto = _dtoHelper.PrepareProductDTO(eventMessage.Entity); + + ProductUpdated(productDto); + } + + public void HandleEvent(EntityInsertedEvent eventMessage) + { + CategoryDto categoryDto = _dtoHelper.PrepareCategoryDTO(eventMessage.Entity); + + // The Store mappings of the category are still not saved, so all webhooks will be triggered + // no matter for which store are registered. + NotifyRegisteredWebHooks(categoryDto, WebHookNames.CategoriesCreate, categoryDto.StoreIds); + } + + public void HandleEvent(EntityUpdatedEvent eventMessage) + { + CategoryDto categoryDto = _dtoHelper.PrepareCategoryDTO(eventMessage.Entity); + + string webhookEvent = WebHookNames.CategoriesUpdate; + + if (categoryDto.Deleted == true) + { + webhookEvent = WebHookNames.CategoriesDelete; + } + + NotifyRegisteredWebHooks(categoryDto, webhookEvent, categoryDto.StoreIds); + } + + public void HandleEvent(EntityInsertedEvent eventMessage) + { + OrderDto orderDto = _dtoHelper.PrepareOrderDTO(eventMessage.Entity); + + var storeIds = new List(); + + if (orderDto.StoreId.HasValue) + { + storeIds.Add(orderDto.StoreId.Value); + } + + NotifyRegisteredWebHooks(orderDto, WebHookNames.OrdersCreate, storeIds); + } + + public void HandleEvent(EntityUpdatedEvent eventMessage) + { + OrderDto orderDto = _dtoHelper.PrepareOrderDTO(eventMessage.Entity); + + string webhookEvent = WebHookNames.OrdersUpdate; + + if (orderDto.Deleted == true) + { + webhookEvent = WebHookNames.OrdersDelete; + } + + var storeIds = new List(); + + if (orderDto.StoreId.HasValue) + { + storeIds.Add(orderDto.StoreId.Value); + } + + NotifyRegisteredWebHooks(orderDto, webhookEvent, storeIds); + } + + public void HandleEvent(EntityInsertedEvent eventMessage) + { + HandleStoreMappingEvent(eventMessage.Entity.EntityId, eventMessage.Entity.EntityName); + } + + public void HandleEvent(EntityDeletedEvent eventMessage) + { + HandleStoreMappingEvent(eventMessage.Entity.EntityId, eventMessage.Entity.EntityName); + } + + public void HandleEvent(EntityInsertedEvent eventMessage) + { + if (eventMessage.Entity.Key == NopCustomerDefaults.FirstNameAttribute || + eventMessage.Entity.Key == NopCustomerDefaults.LastNameAttribute || + eventMessage.Entity.Key == NopCustomerDefaults.LanguageIdAttribute) + { + var customerDto = _customerApiService.GetCustomerById(eventMessage.Entity.EntityId); + + var storeIds = new List(); + + if (customerDto.RegisteredInStoreId.HasValue) + { + storeIds.Add(customerDto.RegisteredInStoreId.Value); + } + + NotifyRegisteredWebHooks(customerDto, WebHookNames.CustomersUpdate, storeIds); + } + } + + public void HandleEvent(EntityUpdatedEvent eventMessage) + { + if (eventMessage.Entity.Key == NopCustomerDefaults.FirstNameAttribute || + eventMessage.Entity.Key == NopCustomerDefaults.LastNameAttribute || + eventMessage.Entity.Key == NopCustomerDefaults.LanguageIdAttribute) + { + var customerDto = _customerApiService.GetCustomerById(eventMessage.Entity.EntityId); + + var storeIds = new List(); + + if (customerDto.RegisteredInStoreId.HasValue) + { + storeIds.Add(customerDto.RegisteredInStoreId.Value); + } + + NotifyRegisteredWebHooks(customerDto, WebHookNames.CustomersUpdate, storeIds); + } + } + + public void HandleEvent(EntityUpdatedEvent eventMessage) + { + StoreDto storeDto = _dtoHelper.PrepareStoreDTO(eventMessage.Entity); + + var storeIds = new List(); + storeIds.Add(storeDto.Id); + + NotifyRegisteredWebHooks(storeDto, WebHookNames.StoresUpdate, storeIds); + } + + + public void HandleEvent(EntityInsertedEvent eventMessage) + { + NotifyProductCategoryMappingWebhook(eventMessage.Entity, WebHookNames.ProductCategoryMapsCreate); + } + + public void HandleEvent(EntityUpdatedEvent eventMessage) + { + NotifyProductCategoryMappingWebhook(eventMessage.Entity, WebHookNames.ProductCategoryMapsUpdate); + } + + public void HandleEvent(EntityDeletedEvent eventMessage) + { + NotifyProductCategoryMappingWebhook(eventMessage.Entity, WebHookNames.ProductCategoryMapsDelete); + } + + public void HandleEvent(EntityInsertedEvent eventMessage) + { + LanguageDto langaDto = _dtoHelper.PrepateLanguageDto(eventMessage.Entity); + + NotifyRegisteredWebHooks(langaDto, WebHookNames.LanguagesCreate, langaDto.StoreIds); + } + + public void HandleEvent(EntityUpdatedEvent eventMessage) + { + LanguageDto langaDto = _dtoHelper.PrepateLanguageDto(eventMessage.Entity); + + NotifyRegisteredWebHooks(langaDto, WebHookNames.LanguagesUpdate, langaDto.StoreIds); + } + + public void HandleEvent(EntityDeletedEvent eventMessage) + { + LanguageDto langaDto = _dtoHelper.PrepateLanguageDto(eventMessage.Entity); + + NotifyRegisteredWebHooks(langaDto, WebHookNames.LanguagesDelete, langaDto.StoreIds); + } + + public void HandleEvent(EntityInsertedEvent eventMessage) + { + var product = _productApiService.GetProductById(eventMessage.Entity.ProductId); + + if (product != null) + { + ProductDto productDto = _dtoHelper.PrepareProductDTO(product); + + ProductUpdated(productDto); + } + } + + public void HandleEvent(EntityUpdatedEvent eventMessage) + { + var product = _productApiService.GetProductById(eventMessage.Entity.ProductId); + + if (product != null) + { + ProductDto productDto = _dtoHelper.PrepareProductDTO(product); + + ProductUpdated(productDto); + } + } + + public void HandleEvent(EntityDeletedEvent eventMessage) + { + var product = _productApiService.GetProductById(eventMessage.Entity.ProductId); + + if (product != null) + { + ProductDto productDto = _dtoHelper.PrepareProductDTO(product); + + ProductUpdated(productDto); + } + } + + // We trigger a product updated WebHook when a picture used in a product is updated. + // This is required, because when the product title is changed, the product is updated first + // and then the picture urls are chaged. In order for the WebHook consumer to have the latest + // product picture urls the following code is used. + public void HandleEvent(EntityUpdatedEvent eventMessage) + { + var productPicture = _productPictureService.GetProductPictureByPictureId(eventMessage.Entity.Id); + + if (productPicture != null) + { + var product = _productApiService.GetProductById(productPicture.ProductId); + + if (product != null) + { + ProductDto productDto = _dtoHelper.PrepareProductDTO(product); + + ProductUpdated(productDto); + } + } + } + + public void HandleEvent(EntityDeletedEvent eventMessage) + { + _cacheManager.RemoveByPattern(Configurations.NEWSLETTER_SUBSCRIBERS_KEY); + + NewsLetterSubscriptionDto newsLetterSubscriptionDto = eventMessage.Entity.ToDto(); + + var storeIds = new List + { + newsLetterSubscriptionDto.StoreId + }; + + NotifyRegisteredWebHooks(newsLetterSubscriptionDto, WebHookNames.NewsLetterSubscriptionDelete, storeIds); + } + + public void HandleEvent(EntityInsertedEvent eventMessage) + { + _cacheManager.RemoveByPattern(Configurations.NEWSLETTER_SUBSCRIBERS_KEY); + + NewsLetterSubscriptionDto newsLetterSubscriptionDto = eventMessage.Entity.ToDto(); + + var storeIds = new List + { + newsLetterSubscriptionDto.StoreId + }; + + NotifyRegisteredWebHooks(newsLetterSubscriptionDto, WebHookNames.NewsLetterSubscriptionCreate, storeIds); + } + + public void HandleEvent(EntityUpdatedEvent eventMessage) + { + _cacheManager.RemoveByPattern(Configurations.NEWSLETTER_SUBSCRIBERS_KEY); + + NewsLetterSubscriptionDto newsLetterSubscriptionDto = eventMessage.Entity.ToDto(); + + var storeIds = new List + { + newsLetterSubscriptionDto.StoreId + }; + + NotifyRegisteredWebHooks(newsLetterSubscriptionDto, WebHookNames.NewsLetterSubscriptionUpdate, storeIds); + } + + private void NotifyProductCategoryMappingWebhook(ProductCategory productCategory, string eventName) + { + var storeIds = GetStoreIdsForProductCategoryMap(productCategory); + + if (storeIds == null) + { + return; + } + + ProductCategoryMappingDto productCategoryMappingDto = productCategory.ToDto(); + + NotifyRegisteredWebHooks(productCategoryMappingDto, eventName, storeIds); + } + + private List GetStoreIdsForProductCategoryMap(ProductCategory productCategory) + { + // Send a webhook event only for the stores that can access the product and category + // in the current product category map. + Product product = _productService.GetProductById(productCategory.ProductId); + Category category = _categoryService.GetCategoryById(productCategory.CategoryId); + + if (product == null || category == null) + { + return null; + } + + var productStoreIds = _storeMappingService.GetStoresIdsWithAccess(product); + + var categoryStoreIds = _storeMappingService.GetStoresIdsWithAccess(category); + + return productStoreIds.Intersect(categoryStoreIds).ToList(); + } + + private void ProductUpdated(ProductDto productDto) + { + string webhookEvent = WebHookNames.ProductsUpdate; + + if (productDto.Deleted == true) + { + webhookEvent = WebHookNames.ProductsDelete; + } + + NotifyRegisteredWebHooks(productDto, webhookEvent, productDto.StoreIds); + } + + private void HandleStoreMappingEvent(int entityId, string entityName) + { + // When creating or editing a category after saving the store mapping the category is not updated + // so we should listen for StoreMapping update/delete and fire a webhook with the updated entityDto(with correct storeIds). + if (entityName == "Category") + { + var category = _categoryApiService.GetCategoryById(entityId); + + if (category != null) + { + CategoryDto categoryDto = _dtoHelper.PrepareCategoryDTO(category); + + string webhookEvent = WebHookNames.CategoriesUpdate; + + if (categoryDto.Deleted == true) + { + webhookEvent = WebHookNames.CategoriesDelete; + } + + NotifyRegisteredWebHooks(categoryDto, webhookEvent, categoryDto.StoreIds); + } + } + else if (entityName == "Product") + { + var product = _productApiService.GetProductById(entityId); + + if (product != null) + { + ProductDto productDto = _dtoHelper.PrepareProductDTO(product); + + string webhookEvent = WebHookNames.ProductsUpdate; + + if (productDto.Deleted == true) + { + webhookEvent = WebHookNames.ProductsDelete; + } + + NotifyRegisteredWebHooks(productDto, webhookEvent, productDto.StoreIds); + } + } + } + + private void NotifyRegisteredWebHooks(T entityDto, string webhookEvent, List storeIds) + { + if (storeIds.Count > 0) + { + // Notify all webhooks that the entity is mapped to their store. + WebHookManager.NotifyAllAsync(new NotificationDictionary[] { new NotificationDictionary(webhookEvent, entityDto) } + , (hook, hookUser) => IsEntityMatchingTheWebHookStoreId(hookUser, storeIds)); + + if (typeof(T) == typeof(ProductDto) || typeof(T) == typeof(CategoryDto)) + { + NotifyUnmappedEntityWebhooks(entityDto, storeIds); + } + } + else + { + WebHookManager.NotifyAllAsync(new NotificationDictionary[] { new NotificationDictionary(webhookEvent, entityDto) }, predicate: null); + } + } + + private void NotifyUnmappedEntityWebhooks(T entityDto, List storeIds) + { + if (typeof(T) == typeof(ProductDto)) + { + // The product is not mapped to the store. + // Notify all webhooks that the entity is not mapped to their store. + WebHookManager.NotifyAllAsync(new NotificationDictionary[] { new NotificationDictionary(WebHookNames.ProductsUnmap, entityDto) }, + (hook, hookUser) => !IsEntityMatchingTheWebHookStoreId(hookUser, storeIds)); + } + else if (typeof(T) == typeof(CategoryDto)) + { + // The category is not mapped to the store. + // Notify all webhooks that the entity is not mapped to their store. + WebHookManager.NotifyAllAsync(new NotificationDictionary[] { new NotificationDictionary(WebHookNames.CategoriesUnmap, entityDto) }, + (hook, hookUser) => !IsEntityMatchingTheWebHookStoreId(hookUser, storeIds)); + } + } + + private bool IsEntityMatchingTheWebHookStoreId(string webHookUser, List storeIds) + { + // When we register the webhooks we add "-storeId" at the end of the webHookUser string. + // That way we can check to which store is mapped the webHook. + foreach (var id in storeIds) + { + if (webHookUser.EndsWith("-" + id)) + { + return true; + } + } + + return false; + } + } +} diff --git a/Nop.Plugin.Api/plugin.json b/Nop.Plugin.Api/plugin.json index 73402fd..d658f2d 100644 --- a/Nop.Plugin.Api/plugin.json +++ b/Nop.Plugin.Api/plugin.json @@ -1,11 +1,11 @@ -{ - "Group": "Api", - "FriendlyName": "Api plugin", - "SystemName": "Nop.Plugin.Api", - "Version": "4.0.1", - "SupportedVersions": [ "4.00" ], - "Author": "Nop-Templates team", - "DisplayOrder": -1, - "FileName": "Nop.Plugin.Api.dll", - "Description": "This plugin is Restfull API for nopCommerce" +{ + "Group": "Api", + "FriendlyName": "Api plugin", + "SystemName": "Nop.Plugin.Api", + "Version": "4.1.0", + "SupportedVersions": [ "4.10" ], + "Author": "Nop-Templates team", + "DisplayOrder": -1, + "FileName": "Nop.Plugin.Api.dll", + "Description": "This plugin is Restfull API for nopCommerce" } \ No newline at end of file diff --git a/renovate.json b/renovate.json new file mode 100644 index 0000000..f45d8f1 --- /dev/null +++ b/renovate.json @@ -0,0 +1,5 @@ +{ + "extends": [ + "config:base" + ] +}