Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -92,14 +92,26 @@ public virtual IList<CustomAttribute> AddAddressAttribute(IList<CustomAttribute>
/// <param name="customAttributes">Attributes</param>
/// <returns>Warnings</returns>
public virtual async Task<IList<string>> GetAttributeWarnings(IList<CustomAttribute> customAttributes)
{
return await GetAttributeWarnings(customAttributes, string.Empty);
}

/// <summary>
/// Validates address attributes limited to the specified store
/// </summary>
/// <param name="customAttributes">Attributes</param>
/// <param name="storeId">Store identifier</param>
/// <returns>Warnings</returns>
public virtual async Task<IList<string>> GetAttributeWarnings(IList<CustomAttribute> customAttributes,
string storeId)
{
var warnings = new List<string>();

//ensure it's our attributes
var attributes1 = await ParseAddressAttributes(customAttributes);

//validate required address attributes (whether they're chosen/selected/entered)
var attributes2 = await _addressAttributeService.GetAllAddressAttributes();
var attributes2 = await _addressAttributeService.GetAllAddressAttributes(storeId ?? string.Empty);
foreach (var a2 in attributes2)
{
if (!a2.IsRequired) continue;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using Grand.Domain.Common;
using Grand.Infrastructure.Caching;
using Grand.Infrastructure.Caching.Constants;
using Grand.Infrastructure.Configuration;
using Grand.Infrastructure.Extensions;
using MediatR;

Expand All @@ -21,13 +22,16 @@ public class AddressAttributeService : IAddressAttributeService
/// <param name="cacheBase">Cache manager</param>
/// <param name="addressAttributeRepository">Address attribute repository</param>
/// <param name="mediator">Mediator</param>
/// <param name="accessControlConfig">Access control config</param>
public AddressAttributeService(ICacheBase cacheBase,
IRepository<AddressAttribute> addressAttributeRepository,
IMediator mediator)
IMediator mediator,
AccessControlConfig accessControlConfig)
{
_cacheBase = cacheBase;
_addressAttributeRepository = addressAttributeRepository;
_mediator = mediator;
_accessControlConfig = accessControlConfig;
}

#endregion
Expand All @@ -37,6 +41,7 @@ public AddressAttributeService(ICacheBase cacheBase,
private readonly IRepository<AddressAttribute> _addressAttributeRepository;
private readonly IMediator _mediator;
private readonly ICacheBase _cacheBase;
private readonly AccessControlConfig _accessControlConfig;

#endregion

Expand All @@ -48,12 +53,32 @@ public AddressAttributeService(ICacheBase cacheBase,
/// <returns>Address attributes</returns>
public virtual async Task<IList<AddressAttribute>> GetAllAddressAttributes()
{
var key = CacheKey.ADDRESSATTRIBUTES_ALL_KEY;
return await GetAllAddressAttributes(string.Empty);
}

/// <summary>
/// Gets all address attributes for the specified store
/// </summary>
/// <param name="storeId">Store identifier</param>
/// <returns>Address attributes</returns>
public virtual async Task<IList<AddressAttribute>> GetAllAddressAttributes(string storeId)
{
var key = string.IsNullOrEmpty(storeId)
? CacheKey.ADDRESSATTRIBUTES_ALL_KEY
: $"{CacheKey.ADDRESSATTRIBUTES_ALL_KEY}.{storeId}";
return await _cacheBase.GetAsync(key, async () =>
{
var query = from aa in _addressAttributeRepository.Table
orderby aa.DisplayOrder
select aa;

query = query.OrderBy(aa => aa.DisplayOrder);

//Store acl
if (!string.IsNullOrEmpty(storeId) && !_accessControlConfig.IgnoreStoreLimitations)
query = from aa in query
where !aa.LimitedToStores || aa.Stores.Contains(storeId)
select aa;

return await Task.FromResult(query.ToList());
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,14 @@ IList<CustomAttribute> AddAddressAttribute(IList<CustomAttribute> customAttribut
/// <returns>Warnings</returns>
Task<IList<string>> GetAttributeWarnings(IList<CustomAttribute> customAttributes);

/// <summary>
/// Validates address attributes limited to the specified store
/// </summary>
/// <param name="customAttributes">Attributes</param>
/// <param name="storeId">Store identifier</param>
/// <returns>Warnings</returns>
Task<IList<string>> GetAttributeWarnings(IList<CustomAttribute> customAttributes, string storeId);

/// <summary>
/// Formats attributes
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,13 @@ public interface IAddressAttributeService
/// <returns>Address attributes</returns>
Task<IList<AddressAttribute>> GetAllAddressAttributes();

/// <summary>
/// Gets all address attributes for the specified store
/// </summary>
/// <param name="storeId">Store identifier</param>
/// <returns>Address attributes</returns>
Task<IList<AddressAttribute>> GetAllAddressAttributes(string storeId);

/// <summary>
/// Gets an address attribute
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,14 @@ IList<CustomAttribute> AddCustomerAttribute(IList<CustomAttribute> customAttribu
/// <returns>Warnings</returns>
Task<IList<string>> GetAttributeWarnings(IList<CustomAttribute> customAttributes);

/// <summary>
/// Validates customer attributes limited to the specified store
/// </summary>
/// <param name="customAttributes">Attributes</param>
/// <param name="storeId">Store identifier</param>
/// <returns>Warnings</returns>
Task<IList<string>> GetAttributeWarnings(IList<CustomAttribute> customAttributes, string storeId);

/// <summary>
/// Formats attributes
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,13 @@ public interface ICustomerAttributeService
/// <returns>Customer attributes</returns>
Task<IList<CustomerAttribute>> GetAllCustomerAttributes();

/// <summary>
/// Gets all customer attributes for the specified store
/// </summary>
/// <param name="storeId">Store identifier</param>
/// <returns>Customer attributes</returns>
Task<IList<CustomerAttribute>> GetAllCustomerAttributes(string storeId);

/// <summary>
/// Gets a customer attribute
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,18 @@
/// <param name="customAttributes">Attributes</param>
/// <returns>Warnings</returns>
public virtual async Task<IList<string>> GetAttributeWarnings(IList<CustomAttribute> customAttributes)
{
return await GetAttributeWarnings(customAttributes, string.Empty);
}

/// <summary>
/// Validates customer attributes limited to the specified store
/// </summary>
/// <param name="customAttributes">Attributes</param>
/// <param name="storeId">Store identifier</param>
/// <returns>Warnings</returns>
public virtual async Task<IList<string>> GetAttributeWarnings(IList<CustomAttribute> customAttributes,
string storeId)
{
var warnings = new List<string>();

Expand All @@ -103,13 +115,13 @@
var attributes1 = await ParseCustomerAttributes(customAttributes);

//validate required customer attributes (whether they're chosen/selected/entered)
var attributes2 = await _customerAttributeService.GetAllCustomerAttributes();
var attributes2 = await _customerAttributeService.GetAllCustomerAttributes(storeId ?? string.Empty);
foreach (var a2 in attributes2)
{
if (!a2.IsRequired) continue;
var found = false;
//selected customer attributes
foreach (var a1 in attributes1)

Check warning on line 124 in src/Business/Grand.Business.Customers/Services/CustomerAttributeParser.cs

View workflow job for this annotation

GitHub Actions / Build and analyze

Loop should be simplified by calling Select(a1 => a1.Id))
{
if (a1.Id != a2.Id) continue;
var valuesStr = customAttributes.Where(x => x.Key == a1.Id).Select(x => x.Value);
Expand All @@ -133,7 +145,7 @@
/// <param name="separator">Separator</param>
/// <param name="htmlEncode">A value indicating whether to encode (HTML) values</param>
/// <returns>Attributes</returns>
public virtual async Task<string> FormatAttributes(Language language,

Check warning on line 148 in src/Business/Grand.Business.Customers/Services/CustomerAttributeParser.cs

View workflow job for this annotation

GitHub Actions / Build and analyze

Refactor this method to reduce its Cognitive Complexity from 36 to the 15 allowed.
IList<CustomAttribute> customAttributes, string separator = "<br />", bool htmlEncode = true)
{
var result = new StringBuilder();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using Grand.Domain.Customers;
using Grand.Infrastructure.Caching;
using Grand.Infrastructure.Caching.Constants;
using Grand.Infrastructure.Configuration;
using Grand.Infrastructure.Extensions;
using MediatR;

Expand All @@ -21,13 +22,16 @@ public class CustomerAttributeService : ICustomerAttributeService
/// <param name="cacheBase">Cache manager</param>
/// <param name="customerAttributeRepository">Customer attribute repository</param>
/// <param name="mediator">Mediator</param>
/// <param name="accessControlConfig">Access control config</param>
public CustomerAttributeService(ICacheBase cacheBase,
IRepository<CustomerAttribute> customerAttributeRepository,
IMediator mediator)
IMediator mediator,
AccessControlConfig accessControlConfig)
{
_cacheBase = cacheBase;
_customerAttributeRepository = customerAttributeRepository;
_mediator = mediator;
_accessControlConfig = accessControlConfig;
}

#endregion
Expand All @@ -37,6 +41,7 @@ public CustomerAttributeService(ICacheBase cacheBase,
private readonly IRepository<CustomerAttribute> _customerAttributeRepository;
private readonly IMediator _mediator;
private readonly ICacheBase _cacheBase;
private readonly AccessControlConfig _accessControlConfig;

#endregion

Expand All @@ -48,12 +53,32 @@ public CustomerAttributeService(ICacheBase cacheBase,
/// <returns>Customer attributes</returns>
public virtual async Task<IList<CustomerAttribute>> GetAllCustomerAttributes()
{
var key = CacheKey.CUSTOMERATTRIBUTES_ALL_KEY;
return await GetAllCustomerAttributes(string.Empty);
}

/// <summary>
/// Gets all customer attributes for the specified store
/// </summary>
/// <param name="storeId">Store identifier</param>
/// <returns>Customer attributes</returns>
public virtual async Task<IList<CustomerAttribute>> GetAllCustomerAttributes(string storeId)
{
var key = string.IsNullOrEmpty(storeId)
? CacheKey.CUSTOMERATTRIBUTES_ALL_KEY
: $"{CacheKey.CUSTOMERATTRIBUTES_ALL_KEY}.{storeId}";
return await _cacheBase.GetAsync(key, async () =>
{
var query = from ca in _customerAttributeRepository.Table
orderby ca.DisplayOrder
select ca;

query = query.OrderBy(ca => ca.DisplayOrder);

//Store acl
if (!string.IsNullOrEmpty(storeId) && !_accessControlConfig.IgnoreStoreLimitations)
query = from ca in query
where !ca.LimitedToStores || ca.Stores.Contains(storeId)
select ca;

return await Task.FromResult(query.ToList());
});
}
Expand Down
10 changes: 9 additions & 1 deletion src/Core/Grand.Domain/Common/AddressAttribute.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
using Grand.Domain.Catalog;
using Grand.Domain.Localization;
using Grand.Domain.Stores;

namespace Grand.Domain.Common;

/// <summary>
/// Represents an address attribute
/// </summary>
public class AddressAttribute : BaseEntity, ITranslationEntity
public class AddressAttribute : BaseEntity, ITranslationEntity, IStoreLinkEntity
{
private ICollection<AddressAttributeValue> _addressAttributeValues;

Expand Down Expand Up @@ -51,4 +52,11 @@ public virtual ICollection<AddressAttributeValue> AddressAttributeValues {
/// Gets or sets the collection of locales
/// </summary>
public IList<TranslationEntity> Locales { get; set; } = new List<TranslationEntity>();

/// <summary>
/// Gets or sets a value indicating whether the entity is limited/restricted to certain stores
/// </summary>
public bool LimitedToStores { get; set; }

public IList<string> Stores { get; set; } = new List<string>();
}
10 changes: 9 additions & 1 deletion src/Core/Grand.Domain/Customers/CustomerAttribute.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
using Grand.Domain.Catalog;
using Grand.Domain.Localization;
using Grand.Domain.Stores;

namespace Grand.Domain.Customers;

/// <summary>
/// Represents a customer attribute
/// </summary>
public class CustomerAttribute : BaseEntity, ITranslationEntity
public class CustomerAttribute : BaseEntity, ITranslationEntity, IStoreLinkEntity
{
private ICollection<CustomerAttributeValue> _customerAttributeValues;

Expand Down Expand Up @@ -47,4 +48,11 @@ public virtual ICollection<CustomerAttributeValue> CustomerAttributeValues {
/// Gets or sets the collection of locales
/// </summary>
public IList<TranslationEntity> Locales { get; set; } = new List<TranslationEntity>();

/// <summary>
/// Gets or sets a value indicating whether the entity is limited/restricted to certain stores
/// </summary>
public bool LimitedToStores { get; set; }

public IList<string> Stores { get; set; } = new List<string>();
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using Grand.Data.Mongo;
using Grand.Domain.Common;
using Grand.Infrastructure.Caching;
using Grand.Infrastructure.Configuration;
using Grand.Infrastructure.Events;
using Grand.SharedKernel.Extensions;
using MediatR;
Expand All @@ -27,7 +28,8 @@ public void Init()
_cacheMock = new Mock<ICacheBase>();
_repositoryMock = new Mock<MongoRepository<AddressAttribute>>(Mock.Of<IAuditInfoProvider>());
_mediatorMock = new Mock<IMediator>();
_service = new AddressAttributeService(_cacheMock.Object, _repositoryMock.Object, _mediatorMock.Object);
_service = new AddressAttributeService(_cacheMock.Object, _repositoryMock.Object, _mediatorMock.Object,
new AccessControlConfig());
}

[TestMethod]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ public void AddCustomerAttribute_ReturnExpectedValues()
[TestMethod]
public async Task GetAttributeWarnings_Return_NoWarning()
{
_customerAtrServiceMock.Setup(c => c.GetAllCustomerAttributes())
_customerAtrServiceMock.Setup(c => c.GetAllCustomerAttributes(It.IsAny<string>()))
.Returns(() => Task.FromResult((IList<CustomerAttribute>)_customerAtr));
_customerAtrServiceMock.Setup(c => c.GetCustomerAttributeById(It.IsAny<string>())).Returns((string w) =>
Task.FromResult(_customerAtr.FirstOrDefault(a => a.Id.Equals(w))));
Expand All @@ -111,7 +111,7 @@ public async Task GetAttributeWarnings_Return_NoWarning()
public async Task GetAttributeWarnings_Return_WithWarning()
{
_customerAtr.FirstOrDefault(x => x.Id == "key5").IsRequired = true;
_customerAtrServiceMock.Setup(c => c.GetAllCustomerAttributes())
_customerAtrServiceMock.Setup(c => c.GetAllCustomerAttributes(It.IsAny<string>()))
.Returns(() => Task.FromResult((IList<CustomerAttribute>)_customerAtr));
_customerAtrServiceMock.Setup(c => c.GetCustomerAttributeById(It.IsAny<string>())).Returns((string w) =>
Task.FromResult(_customerAtr.FirstOrDefault(a => a.Id.Equals(w))));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using Grand.Data;
using Grand.Domain.Customers;
using Grand.Infrastructure.Caching;
using Grand.Infrastructure.Configuration;
using Grand.Infrastructure.Events;
using MediatR;
using Microsoft.VisualStudio.TestTools.UnitTesting;
Expand All @@ -23,7 +24,8 @@ public void Init()
_cacheMock = new Mock<ICacheBase>();
_repositoryMock = new Mock<IRepository<CustomerAttribute>>();
_mediatorMock = new Mock<IMediator>();
_atrService = new CustomerAttributeService(_cacheMock.Object, _repositoryMock.Object, _mediatorMock.Object);
_atrService = new CustomerAttributeService(_cacheMock.Object, _repositoryMock.Object, _mediatorMock.Object,
new AccessControlConfig());
}

[TestMethod]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@
AttributeControlTypeId: 1,
DisplayOrder: 1,
AttributeControlType: DropdownList,
LimitedToStores: false,
Id: ObjectId_1
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@
IsReadOnly: false,
AttributeControlTypeId: RadioList,
DisplayOrder: 1,
LimitedToStores: false,
Id: ObjectId_1
}
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ public async Task RegisterValidator_ScopesEmailLookupToCurrentStore()
mediator.Setup(m => m.Send(It.IsAny<GetParseCustomAttributes>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(new List<CustomAttribute>());
var attrParser = new Mock<ICustomerAttributeParser>();
attrParser.Setup(p => p.GetAttributeWarnings(It.IsAny<IList<CustomAttribute>>()))
attrParser.Setup(p => p.GetAttributeWarnings(It.IsAny<IList<CustomAttribute>>(), It.IsAny<string>()))
.ReturnsAsync(new List<string>());

var validator = new RegisterValidator(
Expand Down Expand Up @@ -139,7 +139,7 @@ public async Task CustomerInfoValidator_ScopesEmailLookupToCurrentStore_WhenEmai
mediator.Setup(m => m.Send(It.IsAny<GetParseCustomAttributes>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(new List<CustomAttribute>());
var attrParser = new Mock<ICustomerAttributeParser>();
attrParser.Setup(p => p.GetAttributeWarnings(It.IsAny<IList<CustomAttribute>>()))
attrParser.Setup(p => p.GetAttributeWarnings(It.IsAny<IList<CustomAttribute>>(), It.IsAny<string>()))
.ReturnsAsync(new List<string>());

var validator = new CustomerInfoValidator(
Expand Down
Loading
Loading