fix formatting, fix models, add more tests

This commit is contained in:
cuqmbr 2023-10-11 14:27:05 +03:00
parent 60bc38ee37
commit 5c30fc21e0
Signed by: cuqmbr
GPG Key ID: 2D72ED98B6CB200F
14 changed files with 203 additions and 25 deletions

View File

@ -12,6 +12,7 @@ public static class GraphQlExtention
.AddQueryType()
.AddTypeExtension<UsersQuery>()
.AddTypeExtension<RolesQuery>()
.AddTypeExtension<WishlistsQuery>()
.AddMutationType()
.AddTypeExtension<AccessMutation>()
.AddTypeExtension<UsersMutation>()

View File

@ -0,0 +1,15 @@
using HotChocolate.Authorization;
using ShoppingAssistantApi.Application.IServices;
using ShoppingAssistantApi.Application.Models.Dtos;
using ShoppingAssistantApi.Application.Paging;
namespace ShoppingAssistantApi.Api.Queries;
[ExtendObjectType(OperationTypeNames.Query)]
public class WishlistsQuery
{
[Authorize]
public Task<PagedList<WishlistDto>> GetPersonalWishlistsPageAsync(int pageNumber, int pageSize, CancellationToken cancellationToken,
[Service] IWishlistsService wishlistsService)
=> wishlistsService.GetPersonalWishlistsPageAsync(pageNumber, pageSize, cancellationToken);
}

View File

@ -1,5 +1,9 @@
using System.Linq.Expressions;
using ShoppingAssistantApi.Domain.Entities;
namespace ShoppingAssistantApi.Application.IRepositories;
public interface IWishlistsRepository : IBaseRepository<Wishlist> { }
public interface IWishlistsRepository : IBaseRepository<Wishlist>
{
public Task<Wishlist> GetWishlistAsync(Expression<Func<Wishlist, bool>> predicate, CancellationToken cancellationToken);
}

View File

@ -1,5 +1,6 @@
using ShoppingAssistantApi.Application.Models.CreateDtos;
using ShoppingAssistantApi.Application.Models.Dtos;
using ShoppingAssistantApi.Application.Paging;
namespace ShoppingAssistantApi.Application.IServices;
@ -8,4 +9,6 @@ public interface IWishlistsService
Task<WishlistDto> StartPersonalWishlistAsync(WishlistCreateDto dto, CancellationToken cancellationToken);
Task<MessageDto> AddMessageToPersonalWishlistAsync(string wishlistId, MessageCreateDto dto, CancellationToken cancellationToken);
Task<PagedList<WishlistDto>> GetPersonalWishlistsPageAsync(int pageNumber, int pageSize, CancellationToken cancellationToken);
}

View File

@ -3,5 +3,6 @@ namespace ShoppingAssistantApi.Application.Models.CreateDtos;
public class WishlistCreateDto
{
public required string Type { get; set; }
public required string FirstMessageText { get; set; }
}

View File

@ -5,6 +5,7 @@ public class MessageDto
public required string Id { get; set; }
public required string Text { get; set; }
public required string Role { get; set; }
public string? CreatedById { get; set; } = null;

View File

@ -5,6 +5,7 @@ public class WishlistDto
public required string Id { get; set; }
public required string Name { get; set; }
public required string Type { get; set; }
public string CreatedById { get; set; } = null!;

View File

@ -6,7 +6,8 @@ namespace ShoppingAssistantApi.Domain.Entities;
public class Message : EntityBase
{
public required string Text { get; set; }
public required string Role { get; set; }
public ObjectId? WishlistId { get; set; } = null;
public ObjectId WishlistId { get; set; }
}

View File

@ -1,4 +1,3 @@
using MongoDB.Bson;
using ShoppingAssistantApi.Domain.Common;
namespace ShoppingAssistantApi.Domain.Entities;
@ -6,8 +5,8 @@ namespace ShoppingAssistantApi.Domain.Entities;
public class Wishlist : EntityBase
{
public required string Name { get; set; }
public required string Type { get; set; }
public ICollection<Message>? Messages { get; set; } = null;
public required ObjectId UserId { get; set; }
public required string Type { get; set; }
public ICollection<Message>? Messages { get; set; }
}

View File

@ -3,5 +3,5 @@ namespace ShoppingAssistantApi.Domain.Enums;
public enum MessageRoles
{
User = 0,
Application = 0
Application = 1
}

View File

@ -15,7 +15,9 @@ namespace ShoppingAssistantApi.Infrastructure.Services;
public class WishlistsService : IWishlistsService
{
private readonly IWishlistsRepository _wishlistsRepository;
private readonly IMessagesRepository _messagesRepository;
private readonly IMapper _mapper;
public WishlistsService(IWishlistsRepository wishlistRepository, IMessagesRepository messageRepository, IMapper mapper)
@ -29,6 +31,12 @@ public class WishlistsService : IWishlistsService
{
var newWishlist = _mapper.Map<Wishlist>(dto);
if (!Enum.TryParse<WishlistTypes>(newWishlist.Type, true, out var enumValue) ||
!Enum.GetValues<WishlistTypes>().Contains(enumValue))
{
throw new InvalidDataException("Provided type is invalid.");
}
newWishlist.CreatedById = (ObjectId) GlobalUser.Id;
newWishlist.CreatedDateUtc = DateTime.UtcNow;
newWishlist.Name = $"{newWishlist.Type} Search";
@ -59,8 +67,7 @@ public class WishlistsService : IWishlistsService
newMessage.CreatedById = (ObjectId) GlobalUser.Id;
newMessage.CreatedDateUtc = DateTime.UtcNow;
var relatedWishlistPage = await _wishlistsRepository.GetPageAsync(1, 1, x => x.Id == wishlistObjectId && x.CreatedById == GlobalUser.Id, cancellationToken);
var relatedWishlist = relatedWishlistPage.FirstOrDefault();
var relatedWishlist = await _wishlistsRepository.GetWishlistAsync(x => x.Id == wishlistObjectId && x.CreatedById == GlobalUser.Id, cancellationToken);
if (relatedWishlist == null)
{
@ -71,4 +78,12 @@ public class WishlistsService : IWishlistsService
return _mapper.Map<MessageDto>(createdMessage);
}
public async Task<PagedList<WishlistDto>> GetPersonalWishlistsPageAsync(int pageNumber, int pageSize, CancellationToken cancellationToken)
{
var entities = await _wishlistsRepository.GetPageAsync(pageNumber, pageSize, cancellationToken);
var dtos = _mapper.Map<List<WishlistDto>>(entities);
var count = await _wishlistsRepository.GetTotalCountAsync();
return new PagedList<WishlistDto>(dtos, pageNumber, pageSize, count);
}
}

View File

@ -16,9 +16,13 @@ namespace ShoppingAssistantApi.Persistance.PersistanceExtentions;
public class DbInitialaizer
{
private readonly IUsersService _usersService;
private readonly IUserManager _userManager;
private readonly IRolesService _rolesService;
private readonly ITokensService _tokensService;
private readonly IMongoCollection<Wishlist> _wishlistCollection;
public IEnumerable<RoleDto> Roles { get; set; }
@ -171,7 +175,7 @@ public class DbInitialaizer
{
Name = "Grandma's Birthday Gift",
Type = WishlistTypes.Gift.ToString(),
UserId = ObjectId.Parse(userList[0].Id),
CreatedById = ObjectId.Parse(userList[0].Id),
Messages = new Message[]
{
new Message
@ -190,7 +194,7 @@ public class DbInitialaizer
{
Name = "Gaming PC",
Type = WishlistTypes.Product.ToString(),
UserId = ObjectId.Parse(userList[1].Id),
CreatedById = ObjectId.Parse(userList[1].Id),
Messages = new Message[]
{
new Message

View File

@ -1,3 +1,5 @@
using System.Linq.Expressions;
using MongoDB.Driver;
using ShoppingAssistantApi.Application.IRepositories;
using ShoppingAssistantApi.Domain.Entities;
using ShoppingAssistantApi.Persistance.Database;
@ -7,4 +9,9 @@ namespace ShoppingAssistantApi.Persistance.Repositories;
public class WishlistsRepository : BaseRepository<Wishlist>, IWishlistsRepository
{
public WishlistsRepository(MongoDbContext db) : base(db, "Wishlists") { }
public async Task<Wishlist> GetWishlistAsync(Expression<Func<Wishlist, bool>> predicate, CancellationToken cancellationToken)
{
return await (await _collection.FindAsync(predicate)).FirstOrDefaultAsync(cancellationToken);
}
}

View File

@ -4,7 +4,6 @@ using System.Net;
using System.Text;
using Xunit;
using Newtonsoft.Json;
using ShoppingAssistantApi.Application.Models.Dtos;
using ShoppingAssistantApi.Domain.Enums;
namespace ShoppingAssistantApi.Tests.Tests;
@ -21,13 +20,13 @@ public class WishlistsTests : IClassFixture<TestingFactory<Program>>
}
[Fact]
public async Task StartPersonalWishlistAndAddMessageAsync_ValidWishlistAndMessageModels_ReturnsNewWishlistAndMessageModels()
public async Task StartPersonalWishlistAsync_ValidWishlistModel_ReturnsNewWishlistModels()
{
var tokensModel = await AccessExtention.CreateGuest(new Guid().ToString(), _httpClient);
var tokensModel = await AccessExtention.Login("shopping.assistant.team@gmail.com", "Yuiop12345", _httpClient);
_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", tokensModel.AccessToken);
var user = await UserExtention.GetCurrentUser(_httpClient);
var startPersonalWishlistMutation = new
var mutation = new
{
query = "mutation startPersonalWishlist($dto: WishlistCreateDtoInput!) { startPersonalWishlist (dto: $dto) { id, name, type, createdById } }",
variables = new
@ -40,33 +39,104 @@ public class WishlistsTests : IClassFixture<TestingFactory<Program>>
}
};
var jsonPayload = JsonConvert.SerializeObject(startPersonalWishlistMutation);
var jsonPayload = JsonConvert.SerializeObject(mutation);
var content = new StringContent(jsonPayload, Encoding.UTF8, "application/json");
using var startPersonalWishlistResponse = await _httpClient.PostAsync("graphql", content);
startPersonalWishlistResponse.EnsureSuccessStatusCode();
Assert.Equal(HttpStatusCode.OK, startPersonalWishlistResponse.StatusCode);
using var response = await _httpClient.PostAsync("graphql", content);
response.EnsureSuccessStatusCode();
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
var responseString = await startPersonalWishlistResponse.Content.ReadAsStringAsync();
var responseString = await response.Content.ReadAsStringAsync();
var document = JsonConvert.DeserializeObject<dynamic>(responseString);
var wishlistId = (string) document.data.startPersonalWishlist.id;
var wishlistCreatedById = (string) document.data.startPersonalWishlist.createdById;
var wishlistType = (string) document.data.startPersonalWishlist.type;
var wishlistName = (string) document.data.startPersonalWishlist.name;
Assert.Equal(user.Id, wishlistCreatedById);
Assert.Equal(WishlistTypes.Product.ToString(), wishlistType);
Assert.Equal($"{WishlistTypes.Product} Search", wishlistName);
}
[Fact]
public async Task GetPersonalWishlistsPage_ValidPageNumberAndSize_ReturnsPage()
{
var tokensModel = await AccessExtention.Login("shopping.assistant.team@gmail.com", "Yuiop12345", _httpClient);
_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", tokensModel.AccessToken);
var user = await UserExtention.GetCurrentUser(_httpClient);
var query = new
{
query = "query personalWishlistsPage($pageNumber: Int!, $pageSize: Int!) { personalWishlistsPage(pageNumber: $pageNumber, pageSize: $pageSize) { items { createdById, id, name, type } } }",
variables = new
{
pageNumber = 3,
pageSize = 1
}
};
var jsonPayload = JsonConvert.SerializeObject(query);
var content = new StringContent(jsonPayload, Encoding.UTF8, "application/json");
using var response = await _httpClient.PostAsync("graphql", content);
response.EnsureSuccessStatusCode();
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
var responseString = await response.Content.ReadAsStringAsync();
var document = JsonConvert.DeserializeObject<dynamic>(responseString);
var personalWishlistsPageItems = Enumerable.ToList(document.data.personalWishlistsPage.items);
var personalWishlistCreatedById = (string) personalWishlistsPageItems[0].createdById;
Assert.NotEmpty(personalWishlistsPageItems);
Assert.Equal(user.Id, personalWishlistCreatedById);
}
[Fact]
public async Task AddMessageToPersonalWishlist_ValidMessageModel_ReturnsNewMessageModel()
{
var tokensModel = await AccessExtention.Login("shopping.assistant.team@gmail.com", "Yuiop12345", _httpClient);
_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", tokensModel.AccessToken);
var user = await UserExtention.GetCurrentUser(_httpClient);
// Get personal wishlist
var query = new
{
query = "query personalWishlistsPage($pageNumber: Int!, $pageSize: Int!) { personalWishlistsPage(pageNumber: $pageNumber, pageSize: $pageSize) { items { createdById, id, name, type } } }",
variables = new
{
pageNumber = 3,
pageSize = 1
}
};
var jsonPayload = JsonConvert.SerializeObject(query);
var content = new StringContent(jsonPayload, Encoding.UTF8, "application/json");
using var personalWishlistPageResponse = await _httpClient.PostAsync("graphql", content);
personalWishlistPageResponse.EnsureSuccessStatusCode();
Assert.Equal(HttpStatusCode.OK, personalWishlistPageResponse.StatusCode);
var responseString = await personalWishlistPageResponse.Content.ReadAsStringAsync();
var document = JsonConvert.DeserializeObject<dynamic>(responseString);
var personalWishlistsPageItems = Enumerable.ToList(document.data.personalWishlistsPage.items);
var personalWishlistId = (string) personalWishlistsPageItems[0].id;
Assert.NotNull(personalWishlistId);
// Add message to personal wishlist
const string MESSAGE_TEXT = "Second Message";
var addMessageToPersonalWishlistMutation = new
var mutation = new
{
query = "mutation addMessageToPersonalWishlist($wishlistId: String!, $dto: MessageCreateDtoInput!) { addMessageToPersonalWishlist (wishlistId: $wishlistId, dto: $dto) { role, text, createdById } }",
variables = new
{
wishlistId = wishlistId,
wishlistId = personalWishlistId,
dto = new
{
text = MESSAGE_TEXT,
@ -74,7 +144,7 @@ public class WishlistsTests : IClassFixture<TestingFactory<Program>>
}
};
jsonPayload = JsonConvert.SerializeObject(addMessageToPersonalWishlistMutation);
jsonPayload = JsonConvert.SerializeObject(mutation);
content = new StringContent(jsonPayload, Encoding.UTF8, "application/json");
using var addMessageToPersonalWishlistResponse = await _httpClient.PostAsync("graphql", content);
@ -87,9 +157,65 @@ public class WishlistsTests : IClassFixture<TestingFactory<Program>>
var messageRole = (string) document.data.addMessageToPersonalWishlist.role;
var messageText = (string) document.data.addMessageToPersonalWishlist.text;
var messageCreatedById = (string) document.data.addMessageToPersonalWishlist.createdById;
Assert.Equal(MessageRoles.User.ToString(), messageRole);
Assert.Equal(MESSAGE_TEXT, messageText);
Assert.Equal(user.Id, messageCreatedById);
}
[Fact]
public async Task StartPersonalWishlistAsync_InvalidWishlistModel_ReturnsInternalServerError()
{
var tokensModel = await AccessExtention.Login("shopping.assistant.team@gmail.com", "Yuiop12345", _httpClient);
_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", tokensModel.AccessToken);
var user = await UserExtention.GetCurrentUser(_httpClient);
var mutation = new
{
query = "mutation startPersonalWishlist($dto: WishlistCreateDtoInput!) { startPersonalWishlist (dto: $dto) { id, name, type, createdById } }",
variables = new
{
dto = new
{
firstMessageText = "First message",
type = "Invalid type" // Invalid Wishlist Type
}
}
};
var jsonPayload = JsonConvert.SerializeObject(mutation);
var content = new StringContent(jsonPayload, Encoding.UTF8, "application/json");
using var response = await _httpClient.PostAsync("graphql", content);
Assert.Equal(HttpStatusCode.InternalServerError, response.StatusCode);
}
[Fact]
public async Task AddMessageToPersonalWishlist_InvalidMessageModel_ReturnsInternalServerError()
{
var tokensModel = await AccessExtention.Login("shopping.assistant.team@gmail.com", "Yuiop12345", _httpClient);
_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", tokensModel.AccessToken);
var user = await UserExtention.GetCurrentUser(_httpClient);
const string MESSAGE_TEXT = "Second Message";
var mutation = new
{
query = "mutation addMessageToPersonalWishlist($wishlistId: String!, $dto: MessageCreateDtoInput!) { addMessageToPersonalWishlist (wishlistId: $wishlistId, dto: $dto) { role, text, createdById } }",
variables = new
{
wishlistId = "8125jad7g12", // Invalid wishlistId
dto = new
{
text = MESSAGE_TEXT,
}
}
};
var jsonPayload = JsonConvert.SerializeObject(mutation);
var content = new StringContent(jsonPayload, Encoding.UTF8, "application/json");
using var response = await _httpClient.PostAsync("graphql", content);
Assert.Equal(HttpStatusCode.InternalServerError, response.StatusCode);
}
}