From 448edf334df846945a577c97de7c4bb805e0421f Mon Sep 17 00:00:00 2001 From: Julia Ilasova <1julka1il@gmail.com> Date: Thu, 9 May 2024 11:11:00 +0200 Subject: [PATCH 1/7] collections repository and service added, will be adding events next --- Core/Services/CollectionsService.cs | 65 +++++++++++++++++++ Core/Services/PlantService.cs | 4 +- .../Repositories/CollectionsRepository.cs | 50 +++++++++++++- .../Collections/CreateCollectionDto.cs | 6 ++ .../Collections/UpdateCollectionDto.cs | 7 ++ Shared/Models/Collection.cs | 8 +-- .../Client/ClientWantsToCreatePlant.cs | 6 +- 7 files changed, 136 insertions(+), 10 deletions(-) create mode 100644 Core/Services/CollectionsService.cs create mode 100644 Shared/Dtos/FromClient/Collections/CreateCollectionDto.cs create mode 100644 Shared/Dtos/FromClient/Collections/UpdateCollectionDto.cs diff --git a/Core/Services/CollectionsService.cs b/Core/Services/CollectionsService.cs new file mode 100644 index 0000000..539f16b --- /dev/null +++ b/Core/Services/CollectionsService.cs @@ -0,0 +1,65 @@ +using Infrastructure.Repositories; +using Shared.Dtos.FromClient.Collections; +using Shared.Exceptions; +using Shared.Models; + +namespace Core.Services; + +public class CollectionsService(CollectionsRepository collectionsRepository, PlantService plantService) +{ + public async Task> GetCollectionsForUser(string userEmail) + { + return await collectionsRepository.GetCollectionsForUser(userEmail); + } + + public async Task GetCollection(Guid collectionId, string userEmail) + { + return await VerifyCollectionExistsAndUserHasAccess(collectionId, userEmail); + } + + public async Task CreateCollection(CreateCollectionDto createCollectionDto, string loggedInUser) + { + var collection = new Collection + { + CollectionId = Guid.NewGuid(), + UserEmail = loggedInUser, + Name = createCollectionDto.Name + }; + return await collectionsRepository.CreateCollection(collection); + } + + public async Task UpdateCollection(UpdateCollectionDto updateCollectionDto, string loggedInUser) + { + var collection = await VerifyCollectionExistsAndUserHasAccess(updateCollectionDto.CollectionId, loggedInUser); + collection.Name = updateCollectionDto.Name; + return await collectionsRepository.UpdateCollection(collection); + } + + public async Task DeleteCollection(Guid collectionId, string loggedInUser) + { + var collection = await VerifyCollectionExistsAndUserHasAccess(collectionId, loggedInUser); + await collectionsRepository.DeleteCollection(collection); + } + + public async Task AddPlantToCollection(Guid collectionId, Guid plantId, string loggedInUser) + { + var collection = await VerifyCollectionExistsAndUserHasAccess(collectionId, loggedInUser); + var plant = await plantService.GetPlantById(plantId, loggedInUser); + await collectionsRepository.AddPlantToCollection(collection, plant); + } + + public async Task RemovePlantFromCollection(Guid collectionId, Guid plantId, string loggedInUser) + { + var collection = await VerifyCollectionExistsAndUserHasAccess(collectionId, loggedInUser); + var plant = await plantService.GetPlantById(plantId, loggedInUser); + await collectionsRepository.RemovePlantFromCollection(collection, plant); + } + + private async Task VerifyCollectionExistsAndUserHasAccess(Guid collectionId, string userEmail) + { + var collection = await collectionsRepository.GetCollection(collectionId); + if (collection is null) throw new NotFoundException("Collection not found"); + if (collection.UserEmail != userEmail) throw new NoAccessException("You don't have access to this collection"); + return collection; + } +} \ No newline at end of file diff --git a/Core/Services/PlantService.cs b/Core/Services/PlantService.cs index b052ad8..2fecc48 100644 --- a/Core/Services/PlantService.cs +++ b/Core/Services/PlantService.cs @@ -15,10 +15,8 @@ public class PlantService( IOptions azureBlobStorageOptions) { - public async Task CreatePlant(CreatePlantDto createPlantDto, string loggedInUserEmail) + public async Task CreatePlant(CreatePlantDto createPlantDto) { - if (loggedInUserEmail != createPlantDto.UserEmail) throw new NoAccessException("You can't create a plant for another user"); - if (string.IsNullOrEmpty(createPlantDto.Nickname)) { createPlantDto.Nickname = GenerateRandomNickname(); diff --git a/Infrastructure/Repositories/CollectionsRepository.cs b/Infrastructure/Repositories/CollectionsRepository.cs index 5d160aa..820efa0 100644 --- a/Infrastructure/Repositories/CollectionsRepository.cs +++ b/Infrastructure/Repositories/CollectionsRepository.cs @@ -1,5 +1,53 @@ +using Microsoft.EntityFrameworkCore; +using Shared.Models; + namespace Infrastructure.Repositories; -public class CollectionsRepository +public class CollectionsRepository(ApplicationDbContext applicationDbContext) { + public async Task> GetCollectionsForUser(string userEmail) + { + return await applicationDbContext.Collections + .Where(collection => collection.UserEmail == userEmail) + .ToListAsync(); + } + + public async Task GetCollection(Guid collectionId) + { + return await applicationDbContext.Collections + .Include(collection => collection.Plants) + .FirstOrDefaultAsync(collection => collection.CollectionId == collectionId); + } + + public async Task CreateCollection(Collection collection) + { + var createdCollection = (await applicationDbContext.Collections.AddAsync(collection)).Entity; + await applicationDbContext.SaveChangesAsync(); + return createdCollection; + } + + public async Task UpdateCollection(Collection collection) + { + var updatedCollection = applicationDbContext.Collections.Update(collection).Entity; + await applicationDbContext.SaveChangesAsync(); + return updatedCollection; + } + + public async Task DeleteCollection(Collection collection) + { + applicationDbContext.Collections.Remove(collection); + await applicationDbContext.SaveChangesAsync(); + } + + public async Task AddPlantToCollection(Collection collection, Plant plant) + { + collection.Plants.Add(plant); + await applicationDbContext.SaveChangesAsync(); + } + + public async Task RemovePlantFromCollection(Collection collection, Plant plant) + { + collection.Plants.Remove(plant); + await applicationDbContext.SaveChangesAsync(); + } } \ No newline at end of file diff --git a/Shared/Dtos/FromClient/Collections/CreateCollectionDto.cs b/Shared/Dtos/FromClient/Collections/CreateCollectionDto.cs new file mode 100644 index 0000000..7fd98a0 --- /dev/null +++ b/Shared/Dtos/FromClient/Collections/CreateCollectionDto.cs @@ -0,0 +1,6 @@ +namespace Shared.Dtos.FromClient.Collections; + +public class CreateCollectionDto +{ + public string Name { get; set; } = null!; +} \ No newline at end of file diff --git a/Shared/Dtos/FromClient/Collections/UpdateCollectionDto.cs b/Shared/Dtos/FromClient/Collections/UpdateCollectionDto.cs new file mode 100644 index 0000000..97b91e2 --- /dev/null +++ b/Shared/Dtos/FromClient/Collections/UpdateCollectionDto.cs @@ -0,0 +1,7 @@ +namespace Shared.Dtos.FromClient.Collections; + +public class UpdateCollectionDto +{ + public Guid CollectionId { get; set; } + public string Name { get; set; } = null!; +} \ No newline at end of file diff --git a/Shared/Models/Collection.cs b/Shared/Models/Collection.cs index fa6e6c5..2eca054 100644 --- a/Shared/Models/Collection.cs +++ b/Shared/Models/Collection.cs @@ -2,8 +2,8 @@ namespace Shared.Models; public class Collection { - public Guid CollectionId { get; set; } - public string UserEmail { get; set; } - public string Name { get; set; } = null!; - public List Plants { get; set; } = new(); + public required Guid CollectionId { get; set; } + public required string UserEmail { get; set; } + public required string Name { get; set; } = null!; + public List Plants { get; set; } = []; } \ No newline at end of file diff --git a/api/Events/PlantEvents/Client/ClientWantsToCreatePlant.cs b/api/Events/PlantEvents/Client/ClientWantsToCreatePlant.cs index 7c529f0..d3495eb 100644 --- a/api/Events/PlantEvents/Client/ClientWantsToCreatePlant.cs +++ b/api/Events/PlantEvents/Client/ClientWantsToCreatePlant.cs @@ -20,13 +20,15 @@ public class ClientWantsToCreatePlant(PlantService plantService, JwtService jwtS public override async Task Handle(ClientWantsToCreatePlantDto dto, IWebSocketConnection socket) { var createPlantDto = dto.CreatePlantDto; - var email = jwtService.GetEmailFromJwt(dto.Jwt); - var plant = await plantService.CreatePlant(createPlantDto, email); + createPlantDto.UserEmail = email; // TODO: check this thoroughly, remove email from dtos + var plant = await plantService.CreatePlant(createPlantDto); + var serverCreatesNewPlant = new ServerCreatesNewPlant { Plant = plant }; + socket.SendDto(serverCreatesNewPlant); } } \ No newline at end of file From 136d97189c4021755e77fd9aeed2d1a5163e8100 Mon Sep 17 00:00:00 2001 From: Julia Ilasova <1julka1il@gmail.com> Date: Thu, 9 May 2024 12:17:20 +0200 Subject: [PATCH 2/7] implemented collections all the way from a client request into the infrastructure layer --- Core/Services/CollectionsService.cs | 6 +++++ .../Repositories/CollectionsRepository.cs | 11 ++++++-- .../Client/ClientWantsAllCollections.cs | 24 +++++++++++++++++ .../Client/ClientWantsPlantsForCollection.cs | 26 ++++++++++++++++++ .../Client/ClientWantsToCreateCollection.cs | 27 +++++++++++++++++++ .../Client/ClientWantsToDeleteCollection.cs | 23 ++++++++++++++++ .../Client/ClientWantsToUpdateCollection.cs | 27 +++++++++++++++++++ .../Server/ServerDeletesCollection.cs | 5 ++++ .../Server/ServerSavesCollection.cs | 9 +++++++ .../Server/ServerSendsAllCollections.cs | 9 +++++++ .../Server/ServerSendsPlantsForCollection.cs | 9 +++++++ .../AddServicesAndRepositoriesExtension.cs | 1 + 12 files changed, 175 insertions(+), 2 deletions(-) create mode 100644 api/Events/Collections/Client/ClientWantsAllCollections.cs create mode 100644 api/Events/Collections/Client/ClientWantsPlantsForCollection.cs create mode 100644 api/Events/Collections/Client/ClientWantsToCreateCollection.cs create mode 100644 api/Events/Collections/Client/ClientWantsToDeleteCollection.cs create mode 100644 api/Events/Collections/Client/ClientWantsToUpdateCollection.cs create mode 100644 api/Events/Collections/Server/ServerDeletesCollection.cs create mode 100644 api/Events/Collections/Server/ServerSavesCollection.cs create mode 100644 api/Events/Collections/Server/ServerSendsAllCollections.cs create mode 100644 api/Events/Collections/Server/ServerSendsPlantsForCollection.cs diff --git a/Core/Services/CollectionsService.cs b/Core/Services/CollectionsService.cs index 539f16b..c8e6766 100644 --- a/Core/Services/CollectionsService.cs +++ b/Core/Services/CollectionsService.cs @@ -17,6 +17,12 @@ public async Task GetCollection(Guid collectionId, string userEmail) return await VerifyCollectionExistsAndUserHasAccess(collectionId, userEmail); } + public async Task> GetPlantsInCollection(Guid collectionId, string userEmail) + { + var collection = await VerifyCollectionExistsAndUserHasAccess(collectionId, userEmail); + return collection.Plants; + } + public async Task CreateCollection(CreateCollectionDto createCollectionDto, string loggedInUser) { var collection = new Collection diff --git a/Infrastructure/Repositories/CollectionsRepository.cs b/Infrastructure/Repositories/CollectionsRepository.cs index 820efa0..e86747a 100644 --- a/Infrastructure/Repositories/CollectionsRepository.cs +++ b/Infrastructure/Repositories/CollectionsRepository.cs @@ -3,10 +3,11 @@ namespace Infrastructure.Repositories; -public class CollectionsRepository(ApplicationDbContext applicationDbContext) +public class CollectionsRepository(IDbContextFactory dbContextFactory) { public async Task> GetCollectionsForUser(string userEmail) { + await using var applicationDbContext = await dbContextFactory.CreateDbContextAsync(); return await applicationDbContext.Collections .Where(collection => collection.UserEmail == userEmail) .ToListAsync(); @@ -14,6 +15,7 @@ public async Task> GetCollectionsForUser(string userEmai public async Task GetCollection(Guid collectionId) { + await using var applicationDbContext = await dbContextFactory.CreateDbContextAsync(); return await applicationDbContext.Collections .Include(collection => collection.Plants) .FirstOrDefaultAsync(collection => collection.CollectionId == collectionId); @@ -21,6 +23,7 @@ public async Task> GetCollectionsForUser(string userEmai public async Task CreateCollection(Collection collection) { + await using var applicationDbContext = await dbContextFactory.CreateDbContextAsync(); var createdCollection = (await applicationDbContext.Collections.AddAsync(collection)).Entity; await applicationDbContext.SaveChangesAsync(); return createdCollection; @@ -28,6 +31,7 @@ public async Task CreateCollection(Collection collection) public async Task UpdateCollection(Collection collection) { + await using var applicationDbContext = await dbContextFactory.CreateDbContextAsync(); var updatedCollection = applicationDbContext.Collections.Update(collection).Entity; await applicationDbContext.SaveChangesAsync(); return updatedCollection; @@ -35,18 +39,21 @@ public async Task UpdateCollection(Collection collection) public async Task DeleteCollection(Collection collection) { + await using var applicationDbContext = await dbContextFactory.CreateDbContextAsync(); applicationDbContext.Collections.Remove(collection); await applicationDbContext.SaveChangesAsync(); } public async Task AddPlantToCollection(Collection collection, Plant plant) { + await using var applicationDbContext = await dbContextFactory.CreateDbContextAsync(); collection.Plants.Add(plant); await applicationDbContext.SaveChangesAsync(); } public async Task RemovePlantFromCollection(Collection collection, Plant plant) - { + { + await using var applicationDbContext = await dbContextFactory.CreateDbContextAsync(); collection.Plants.Remove(plant); await applicationDbContext.SaveChangesAsync(); } diff --git a/api/Events/Collections/Client/ClientWantsAllCollections.cs b/api/Events/Collections/Client/ClientWantsAllCollections.cs new file mode 100644 index 0000000..0d220e0 --- /dev/null +++ b/api/Events/Collections/Client/ClientWantsAllCollections.cs @@ -0,0 +1,24 @@ +using api.Events.Collections.Server; +using api.Extensions; +using Core.Services; +using Fleck; +using lib; +using Shared.Models; + +namespace api.Events.Collections.Client; + +public class ClientWantsAllCollectionsDto : BaseDtoWithJwt; + +public class ClientWantsAllCollections(CollectionsService collectionsService, JwtService jwtService) + : BaseEventHandler +{ + public override async Task Handle(ClientWantsAllCollectionsDto dto, IWebSocketConnection socket) + { + var email = jwtService.GetEmailFromJwt(dto.Jwt!); + var collections = await collectionsService.GetCollectionsForUser(email); + socket.SendDto(new ServerSendsAllCollections + { + Collections = collections.ToList() + }); + } +} \ No newline at end of file diff --git a/api/Events/Collections/Client/ClientWantsPlantsForCollection.cs b/api/Events/Collections/Client/ClientWantsPlantsForCollection.cs new file mode 100644 index 0000000..05a2c6a --- /dev/null +++ b/api/Events/Collections/Client/ClientWantsPlantsForCollection.cs @@ -0,0 +1,26 @@ +using api.Events.Collections.Server; +using api.Extensions; +using Core.Services; +using Fleck; +using lib; +using Shared.Models; + +namespace api.Events.Collections.Client; + +public class ClientWantsPlantsForCollectionDto : BaseDtoWithJwt +{ + public Guid CollectionId { get; set; } +} + +public class ClientWantsPlantsForCollection(CollectionsService collectionsService, JwtService jwtService) : BaseEventHandler +{ + public override async Task Handle(ClientWantsPlantsForCollectionDto dto, IWebSocketConnection socket) + { + var email = jwtService.GetEmailFromJwt(dto.Jwt!); + var plants = await collectionsService.GetPlantsInCollection(dto.CollectionId, email); + socket.SendDto(new ServerSendsPlantsForCollection + { + Plants = plants.ToList() + }); + } +} \ No newline at end of file diff --git a/api/Events/Collections/Client/ClientWantsToCreateCollection.cs b/api/Events/Collections/Client/ClientWantsToCreateCollection.cs new file mode 100644 index 0000000..eee5f6f --- /dev/null +++ b/api/Events/Collections/Client/ClientWantsToCreateCollection.cs @@ -0,0 +1,27 @@ +using api.Events.Collections.Server; +using api.Extensions; +using Core.Services; +using Fleck; +using lib; +using Shared.Dtos.FromClient.Collections; +using Shared.Models; + +namespace api.Events.Collections.Client; + +public class ClientWantsToCreateCollectionDto : BaseDtoWithJwt +{ + public required CreateCollectionDto CreateCollectionDto { get; set; } +} + +public class ClientWantsToCreateCollection(CollectionsService collectionsService, JwtService jwtService) : BaseEventHandler +{ + public override async Task Handle(ClientWantsToCreateCollectionDto dto, IWebSocketConnection socket) + { + var email = jwtService.GetEmailFromJwt(dto.Jwt!); + var collection = await collectionsService.CreateCollection(dto.CreateCollectionDto, email); + socket.SendDto(new ServerSavesCollection + { + Collection = collection + }); + } +} \ No newline at end of file diff --git a/api/Events/Collections/Client/ClientWantsToDeleteCollection.cs b/api/Events/Collections/Client/ClientWantsToDeleteCollection.cs new file mode 100644 index 0000000..ee5a207 --- /dev/null +++ b/api/Events/Collections/Client/ClientWantsToDeleteCollection.cs @@ -0,0 +1,23 @@ +using api.Events.Collections.Server; +using api.Extensions; +using Core.Services; +using Fleck; +using lib; +using Shared.Models; + +namespace api.Events.Collections.Client; + +public class ClientWantsToDeleteCollectionDto : BaseDtoWithJwt +{ + public Guid CollectionId { get; set; } +} + +public class ClientWantsToDeleteCollection(CollectionsService collectionsService, JwtService jwtService) : BaseEventHandler +{ + public override async Task Handle(ClientWantsToDeleteCollectionDto dto, IWebSocketConnection socket) + { + var email = jwtService.GetEmailFromJwt(dto.Jwt!); + await collectionsService.DeleteCollection(dto.CollectionId, email); + socket.SendDto(new ServerDeletesCollection()); + } +} \ No newline at end of file diff --git a/api/Events/Collections/Client/ClientWantsToUpdateCollection.cs b/api/Events/Collections/Client/ClientWantsToUpdateCollection.cs new file mode 100644 index 0000000..0aeb351 --- /dev/null +++ b/api/Events/Collections/Client/ClientWantsToUpdateCollection.cs @@ -0,0 +1,27 @@ +using api.Events.Collections.Server; +using api.Extensions; +using Core.Services; +using Fleck; +using lib; +using Shared.Dtos.FromClient.Collections; +using Shared.Models; + +namespace api.Events.Collections.Client; + +public class ClientWantsToUpdateCollectionDto : BaseDtoWithJwt +{ + public required UpdateCollectionDto UpdateCollectionDto { get; set; } +} + +public class ClientWantsToUpdateCollection(CollectionsService collectionsService, JwtService jwtService) : BaseEventHandler +{ + public override async Task Handle(ClientWantsToUpdateCollectionDto dto, IWebSocketConnection socket) + { + var email = jwtService.GetEmailFromJwt(dto.Jwt!); + var collection = await collectionsService.UpdateCollection(dto.UpdateCollectionDto, email); + socket.SendDto(new ServerSavesCollection + { + Collection = collection + }); + } +} \ No newline at end of file diff --git a/api/Events/Collections/Server/ServerDeletesCollection.cs b/api/Events/Collections/Server/ServerDeletesCollection.cs new file mode 100644 index 0000000..f5a871a --- /dev/null +++ b/api/Events/Collections/Server/ServerDeletesCollection.cs @@ -0,0 +1,5 @@ +using lib; + +namespace api.Events.Collections.Server; + +public class ServerDeletesCollection : BaseDto; \ No newline at end of file diff --git a/api/Events/Collections/Server/ServerSavesCollection.cs b/api/Events/Collections/Server/ServerSavesCollection.cs new file mode 100644 index 0000000..a43d0f5 --- /dev/null +++ b/api/Events/Collections/Server/ServerSavesCollection.cs @@ -0,0 +1,9 @@ +using lib; +using Shared.Models; + +namespace api.Events.Collections.Server; + +public class ServerSavesCollection : BaseDto +{ + public Collection Collection { get; set; } +} \ No newline at end of file diff --git a/api/Events/Collections/Server/ServerSendsAllCollections.cs b/api/Events/Collections/Server/ServerSendsAllCollections.cs new file mode 100644 index 0000000..e8994d0 --- /dev/null +++ b/api/Events/Collections/Server/ServerSendsAllCollections.cs @@ -0,0 +1,9 @@ +using lib; +using Shared.Models; + +namespace api.Events.Collections.Server; + +public class ServerSendsAllCollections : BaseDto +{ + public List Collections { get; set; } +} \ No newline at end of file diff --git a/api/Events/Collections/Server/ServerSendsPlantsForCollection.cs b/api/Events/Collections/Server/ServerSendsPlantsForCollection.cs new file mode 100644 index 0000000..10d61d8 --- /dev/null +++ b/api/Events/Collections/Server/ServerSendsPlantsForCollection.cs @@ -0,0 +1,9 @@ +using lib; +using Shared.Models; + +namespace api.Events.Collections.Server; + +public class ServerSendsPlantsForCollection : BaseDto +{ + public List Plants { get; set; } +} \ No newline at end of file diff --git a/api/Extensions/AddServicesAndRepositoriesExtension.cs b/api/Extensions/AddServicesAndRepositoriesExtension.cs index a97a70d..bdf28df 100644 --- a/api/Extensions/AddServicesAndRepositoriesExtension.cs +++ b/api/Extensions/AddServicesAndRepositoriesExtension.cs @@ -22,6 +22,7 @@ public static void AddServicesAndRepositories(this IServiceCollection services) services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); + services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); From b171024d67fc99211654e6e50642dea29ef6f608 Mon Sep 17 00:00:00 2001 From: Julia Ilasova <1julka1il@gmail.com> Date: Thu, 9 May 2024 14:24:41 +0200 Subject: [PATCH 3/7] minor changes to collections retrieval and seed database on DbContext --- Core/Services/CollectionsService.cs | 16 +++- Core/Services/PlantService.cs | 13 ++- Infrastructure/ApplicationDbContext.cs | 91 ++++++++++++++++++- .../Repositories/CollectionsRepository.cs | 3 +- .../Repositories/PlantRepository.cs | 9 ++ .../Dtos/FromClient/Plant/CreatePlantDto.cs | 1 - Shared/Dtos/GetCollectionDto.cs | 7 ++ Shared/Models/Plant.cs | 6 +- Tests/PlantTests.cs | 1 - .../Server/ServerSendsAllCollections.cs | 4 +- .../Client/ClientWantsToCreatePlant.cs | 6 +- api/Program.cs | 11 +-- 12 files changed, 135 insertions(+), 33 deletions(-) create mode 100644 Shared/Dtos/GetCollectionDto.cs diff --git a/Core/Services/CollectionsService.cs b/Core/Services/CollectionsService.cs index c8e6766..eaddcff 100644 --- a/Core/Services/CollectionsService.cs +++ b/Core/Services/CollectionsService.cs @@ -1,4 +1,5 @@ using Infrastructure.Repositories; +using Shared.Dtos; using Shared.Dtos.FromClient.Collections; using Shared.Exceptions; using Shared.Models; @@ -7,9 +8,14 @@ namespace Core.Services; public class CollectionsService(CollectionsRepository collectionsRepository, PlantService plantService) { - public async Task> GetCollectionsForUser(string userEmail) + public async Task> GetCollectionsForUser(string userEmail) { - return await collectionsRepository.GetCollectionsForUser(userEmail); + var collectionsWithoutPlants = await collectionsRepository.GetCollectionsForUser(userEmail); + return collectionsWithoutPlants.Select(collection => new GetCollectionDto + { + CollectionId = collection.CollectionId, + Name = collection.Name, + }); } public async Task GetCollection(Guid collectionId, string userEmail) @@ -19,8 +25,8 @@ public async Task GetCollection(Guid collectionId, string userEmail) public async Task> GetPlantsInCollection(Guid collectionId, string userEmail) { - var collection = await VerifyCollectionExistsAndUserHasAccess(collectionId, userEmail); - return collection.Plants; + await VerifyCollectionExistsAndUserHasAccess(collectionId, userEmail); + return await plantService.GetPlantsForCollection(collectionId); } public async Task CreateCollection(CreateCollectionDto createCollectionDto, string loggedInUser) @@ -63,7 +69,7 @@ public async Task RemovePlantFromCollection(Guid collectionId, Guid plantId, str private async Task VerifyCollectionExistsAndUserHasAccess(Guid collectionId, string userEmail) { - var collection = await collectionsRepository.GetCollection(collectionId); + var collection = await collectionsRepository.GetCollectionWithoutPlants(collectionId); if (collection is null) throw new NotFoundException("Collection not found"); if (collection.UserEmail != userEmail) throw new NoAccessException("You don't have access to this collection"); return collection; diff --git a/Core/Services/PlantService.cs b/Core/Services/PlantService.cs index 2fecc48..a35bded 100644 --- a/Core/Services/PlantService.cs +++ b/Core/Services/PlantService.cs @@ -15,7 +15,7 @@ public class PlantService( IOptions azureBlobStorageOptions) { - public async Task CreatePlant(CreatePlantDto createPlantDto) + public async Task CreatePlant(CreatePlantDto createPlantDto, string loggedInUser) { if (string.IsNullOrEmpty(createPlantDto.Nickname)) { @@ -25,14 +25,14 @@ public async Task CreatePlant(CreatePlantDto createPlantDto) var ímageUrl = azureBlobStorageOptions.Value.DefaultPlantImageUrl; if (createPlantDto.Base64Image is not null) { - ímageUrl = await blobStorageService.SaveImageToBlobStorage(createPlantDto.Base64Image, createPlantDto.UserEmail); + ímageUrl = await blobStorageService.SaveImageToBlobStorage(createPlantDto.Base64Image, loggedInUser); } // Insert plant first to get the plantId var plant = new Plant { PlantId = Guid.NewGuid(), - UserEmail = createPlantDto.UserEmail, + UserEmail =loggedInUser, // CollectionId = Guid.Empty, // TODO: fix when collections are implemented Nickname = createPlantDto.Nickname, ImageUrl = ímageUrl, @@ -62,6 +62,13 @@ public async Task> GetPlantsForUser(string userEmail, int pageNumber plants.ForEach(plant => plant.ImageUrl = blobStorageService.GenerateSasUri(plant.ImageUrl)); // Otherwise the client can't access the image return plants; } + + public async Task> GetPlantsForCollection(Guid collectionId) + { + var plants = await plantRepository.GetPlantsForCollection(collectionId); + plants.ForEach(plant => plant.ImageUrl = blobStorageService.GenerateSasUri(plant.ImageUrl)); // Otherwise the client can't access the image + return plants; + } public async Task UpdatePlant(UpdatePlantDto updatePlantDto, string requesterEmail) { diff --git a/Infrastructure/ApplicationDbContext.cs b/Infrastructure/ApplicationDbContext.cs index 98a8fa7..739566e 100644 --- a/Infrastructure/ApplicationDbContext.cs +++ b/Infrastructure/ApplicationDbContext.cs @@ -1,4 +1,7 @@ +using Infrastructure.Repositories; using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Shared.Dtos.FromClient.Identity; using Shared.Models; using Shared.Models.Identity; using Shared.Models.Information; @@ -22,7 +25,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .HasMany(e => e.Collections) .WithOne() .HasForeignKey(e => e.UserEmail); - + modelBuilder.Entity() .HasMany(e => e.Plants) .WithOne() @@ -37,7 +40,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .HasMany(e => e.ConditionsLogs) .WithOne() .HasForeignKey(e => e.PlantId); - + modelBuilder.Entity() .HasOne(e => e.Requirements) .WithOne() @@ -45,10 +48,92 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) modelBuilder.Entity() .HasKey(e => e.RequirementsId); - + modelBuilder.Entity() .HasKey(e => e.ConditionsId); base.OnModelCreating(modelBuilder); } + + public async Task SeedDevelopmentDataAsync(IServiceScope scope, string defaultPlantImage) + { + var userRepository = scope.ServiceProvider.GetRequiredService(); + await userRepository.CreateUser(new RegisterUserDto + { + Email = "bob@app.com", + Password = "password", + Username = "bob" + }); + + var collectionsRepository = scope.ServiceProvider.GetRequiredService(); + var collection1 = await collectionsRepository.CreateCollection( + new Collection + { + CollectionId = Guid.NewGuid(), + Name = "Succulents", + UserEmail = "bob@app.com", + } + ); + var collection2 = await collectionsRepository.CreateCollection( + new Collection + { + CollectionId = Guid.NewGuid(), + Name = "Cacti", + UserEmail = "bob@app.com", + } + ); + + var plantRepository = scope.ServiceProvider.GetRequiredService(); + await plantRepository.CreatePlant( + new Plant + { + PlantId = Guid.NewGuid(), + Nickname = "Aloe Vera", + UserEmail = "bob@app.com", + ImageUrl = defaultPlantImage, + CollectionId = collection1.CollectionId, + } + ); + + await plantRepository.CreatePlant( + new Plant + { + PlantId = Guid.NewGuid(), + Nickname = "Prickly Pear", + UserEmail = "bob@app.com", + ImageUrl = defaultPlantImage, + CollectionId = collection2.CollectionId, + } + ); + + var plants = await plantRepository.GetPlantsForUser("bob@app.com", 1, 5); + Console.WriteLine(plants.Count); + + var requirementsRepository = scope.ServiceProvider.GetRequiredService(); + var requirements1 = await requirementsRepository.CreateRequirements( + new Requirements + { + RequirementsId = Guid.NewGuid(), + PlantId = plants[0].PlantId, + LightLevel = RequirementLevel.Low, + SoilMoistureLevel = RequirementLevel.Medium, + HumidityLevel = RequirementLevel.High, + TemperatureLevel = RequirementLevel.Medium, + } + ); + plants[0].Requirements = requirements1; + + var requirements2 = await requirementsRepository.CreateRequirements( + new Requirements + { + RequirementsId = Guid.NewGuid(), + PlantId = plants[1].PlantId, + LightLevel = RequirementLevel.High, + SoilMoistureLevel = RequirementLevel.Low, + HumidityLevel = RequirementLevel.Low, + TemperatureLevel = RequirementLevel.Medium, + } + ); + plants[1].Requirements = requirements2; + } } \ No newline at end of file diff --git a/Infrastructure/Repositories/CollectionsRepository.cs b/Infrastructure/Repositories/CollectionsRepository.cs index e86747a..0462bd0 100644 --- a/Infrastructure/Repositories/CollectionsRepository.cs +++ b/Infrastructure/Repositories/CollectionsRepository.cs @@ -13,11 +13,10 @@ public async Task> GetCollectionsForUser(string userEmai .ToListAsync(); } - public async Task GetCollection(Guid collectionId) + public async Task GetCollectionWithoutPlants(Guid collectionId) { await using var applicationDbContext = await dbContextFactory.CreateDbContextAsync(); return await applicationDbContext.Collections - .Include(collection => collection.Plants) .FirstOrDefaultAsync(collection => collection.CollectionId == collectionId); } diff --git a/Infrastructure/Repositories/PlantRepository.cs b/Infrastructure/Repositories/PlantRepository.cs index 6a7cddd..5a96581 100644 --- a/Infrastructure/Repositories/PlantRepository.cs +++ b/Infrastructure/Repositories/PlantRepository.cs @@ -35,6 +35,15 @@ public async Task> GetPlantsForUser(string userEmail, int pageNumber .Take(pageSize) .ToListAsync(); } + + public async Task> GetPlantsForCollection(Guid collectionId) + { + await using var context = await dbContextFactory.CreateDbContextAsync(); + return await context.Plants + .Include(plant => plant.Requirements) + .Where(p => p.CollectionId == collectionId) + .ToListAsync(); + } public async Task UpdatePlant(Plant plant) { diff --git a/Shared/Dtos/FromClient/Plant/CreatePlantDto.cs b/Shared/Dtos/FromClient/Plant/CreatePlantDto.cs index 0a351ec..9b18583 100644 --- a/Shared/Dtos/FromClient/Plant/CreatePlantDto.cs +++ b/Shared/Dtos/FromClient/Plant/CreatePlantDto.cs @@ -5,7 +5,6 @@ namespace Shared.Dtos.FromClient.Plant; public class CreatePlantDto { - [EmailAddress] public string UserEmail { get; set; } = null!; public Guid? CollectionId { get; set; } public string? DeviceId { get; set; } [MaxLength(50)] public string? Nickname { get; set; } diff --git a/Shared/Dtos/GetCollectionDto.cs b/Shared/Dtos/GetCollectionDto.cs new file mode 100644 index 0000000..a7882e3 --- /dev/null +++ b/Shared/Dtos/GetCollectionDto.cs @@ -0,0 +1,7 @@ +namespace Shared.Dtos; + +public class GetCollectionDto +{ + public required Guid CollectionId { get; set; } + public required string Name { get; set; } +} \ No newline at end of file diff --git a/Shared/Models/Plant.cs b/Shared/Models/Plant.cs index ed96b90..6acfa1b 100644 --- a/Shared/Models/Plant.cs +++ b/Shared/Models/Plant.cs @@ -4,12 +4,12 @@ namespace Shared.Models; public class Plant { - public Guid PlantId { get; set; } + public required Guid PlantId { get; set; } public string? DeviceId { get; set; } - public string UserEmail { get; set; } = null!; + public required string UserEmail { get; set; } public Guid? CollectionId { get; set; } public string? Nickname { get; set; } // if not provided make one up - public string ImageUrl { get; set; } = null!; + public required string ImageUrl { get; set; } public Requirements? Requirements { get; set; } public List ConditionsLogs { get; set; } = new(); } \ No newline at end of file diff --git a/Tests/PlantTests.cs b/Tests/PlantTests.cs index dc8b2e8..1eaa191 100644 --- a/Tests/PlantTests.cs +++ b/Tests/PlantTests.cs @@ -39,7 +39,6 @@ private CreatePlantDto GenerateRandomCreatePlantDto(string email) { var createPlantDto = new CreatePlantDto { - UserEmail = email, CollectionId = null, Nickname = "Nickname", Base64Image = "iVBORw0KGgoAAAANSUhEUgAAAFgAAABHCAYAAACDFYB6AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAAEnQAABJ0Ad5mH3gAAACmSURBVHhe7dAxAQAADMOg+TfdqeALEriFKhgrGCsYKxgrGCsYKxgrGCsYKxgrGCsYKxgrGCsYKxgrGCsYKxgrGCsYKxgrGCsYKxgrGCsYKxgrGCsYKxgrGCsYKxgrGCsYKxgrGCsYKxgrGCsYKxgrGCsYKxgrGCsYKxgrGCsYKxgrGCsYKxgrGCsYKxgrGCsYKxgrGCsYKxgrGCsYKxgrGCsYK5jaHjFvRBBJ1UDnAAAAAElFTkSuQmCC", diff --git a/api/Events/Collections/Server/ServerSendsAllCollections.cs b/api/Events/Collections/Server/ServerSendsAllCollections.cs index e8994d0..5f67982 100644 --- a/api/Events/Collections/Server/ServerSendsAllCollections.cs +++ b/api/Events/Collections/Server/ServerSendsAllCollections.cs @@ -1,9 +1,9 @@ using lib; -using Shared.Models; +using Shared.Dtos; namespace api.Events.Collections.Server; public class ServerSendsAllCollections : BaseDto { - public List Collections { get; set; } + public required List Collections { get; set; } } \ No newline at end of file diff --git a/api/Events/PlantEvents/Client/ClientWantsToCreatePlant.cs b/api/Events/PlantEvents/Client/ClientWantsToCreatePlant.cs index d3495eb..521356d 100644 --- a/api/Events/PlantEvents/Client/ClientWantsToCreatePlant.cs +++ b/api/Events/PlantEvents/Client/ClientWantsToCreatePlant.cs @@ -19,10 +19,8 @@ public class ClientWantsToCreatePlant(PlantService plantService, JwtService jwtS { public override async Task Handle(ClientWantsToCreatePlantDto dto, IWebSocketConnection socket) { - var createPlantDto = dto.CreatePlantDto; - var email = jwtService.GetEmailFromJwt(dto.Jwt); - createPlantDto.UserEmail = email; // TODO: check this thoroughly, remove email from dtos - var plant = await plantService.CreatePlant(createPlantDto); + var email = jwtService.GetEmailFromJwt(dto.Jwt!); + var plant = await plantService.CreatePlant(dto.CreatePlantDto, email); var serverCreatesNewPlant = new ServerCreatesNewPlant { diff --git a/api/Program.cs b/api/Program.cs index f222a90..65e87cd 100644 --- a/api/Program.cs +++ b/api/Program.cs @@ -106,18 +106,11 @@ public static async Task StartApi(string[] args) if (args.Contains("--db-init")) { var scope = app.Services.CreateScope(); - var db = app.Services.GetRequiredService>().CreateDbContext(); + var db = await app.Services.GetRequiredService>().CreateDbContextAsync(); await db.Database.EnsureDeletedAsync(); await db.Database.EnsureCreatedAsync(); await db.Database.MigrateAsync(); - - var userRepository = scope.ServiceProvider.GetRequiredService(); - await userRepository.CreateUser(new RegisterUserDto - { - Email = "bob@app.com", - Password = "password", - Username = "bob" - }); + await db.SeedDevelopmentDataAsync(scope, app.Configuration["AzureBlob:DefaultPlantImageUrl"] ?? "https://example.com"); } var port = Environment.GetEnvironmentVariable("PORT") ?? "8181"; From e3956bf2e618e7d2bd006b6a43d7bcb58e2fe6a6 Mon Sep 17 00:00:00 2001 From: Julia Ilasova <1julka1il@gmail.com> Date: Thu, 9 May 2024 17:15:29 +0200 Subject: [PATCH 4/7] retrieval of latest mood is possible, and changed client wants all plants to return the same server event as asking for plants by collection --- Core/Services/ConditionsLogsService.cs | 26 ++++++++++++------- Core/Services/PlantService.cs | 5 ++++ .../Repositories/ConditionsLogsRepository.cs | 9 +++++++ .../ClientWantsLatestConditionsForPlant.cs | 26 +++++++++++++++++++ .../ServerSendsLatestConditionsForPlant.cs | 9 +++++++ .../Client/ClientWantsAllPlants.cs | 5 ++-- .../Server/ServerSendsAllPlants.cs | 9 ------- 7 files changed, 69 insertions(+), 20 deletions(-) create mode 100644 api/Events/Conditions/ClientWantsLatestConditionsForPlant.cs create mode 100644 api/Events/Conditions/ServerSendsLatestConditionsForPlant.cs delete mode 100644 api/Events/PlantEvents/Server/ServerSendsAllPlants.cs diff --git a/Core/Services/ConditionsLogsService.cs b/Core/Services/ConditionsLogsService.cs index 538c4c0..566f4c0 100644 --- a/Core/Services/ConditionsLogsService.cs +++ b/Core/Services/ConditionsLogsService.cs @@ -5,11 +5,11 @@ namespace Core.Services; -public class ConditionsLogsService (ConditionsLogsRepository conditionsLogsRepository, PlantRepository plantRepository, MqttPublisherService mqttPublisherService) +public class ConditionsLogsService (ConditionsLogsRepository conditionsLogsRepository, PlantService plantService, RequirementService requirementService ,MqttPublisherService mqttPublisherService) { public async Task CreateConditionsLogAsync(CreateConditionsLogDto createConditionsLogDto) { - var plantId = await plantRepository.GetPlantIdByDeviceIdAsync(createConditionsLogDto.DeviceId.ToString()); + var plantId = await plantService.GetPlantIdByDeviceIdAsync(createConditionsLogDto.DeviceId.ToString()); if (plantId == Guid.Empty) { @@ -29,7 +29,7 @@ public async Task CreateConditionsLogAsync(CreateConditionsLogDto createConditio PlantId = plantId }; - var newMood = CalculateMood(conditionsLog); + var newMood = await CalculateMood(conditionsLog); conditionsLog.Mood = newMood; await conditionsLogsRepository.CreateConditionsLogAsync(conditionsLog); @@ -91,18 +91,18 @@ private RequirementLevel CalculateHumidityLevel (double value) }; } - private int CalculateMood (ConditionsLog conditionsLog) + private async Task CalculateMood (ConditionsLog conditionsLog) { // Compare ideal requirements for humidity, temperature, soil moisture and light level with actual conditions and calculate mood from 0-4 // get ideal requirements from plant - var requirementsForPlant = plantRepository.GetRequirementsForPlant(conditionsLog.PlantId); + var requirementsForPlant = await requirementService.GetRequirements(conditionsLog.PlantId); // compare with actual conditions var mood = 0; // calculate mood - mood += CalculateScore((int)requirementsForPlant.Result.HumidityLevel, (int)CalculateHumidityLevel(conditionsLog.Humidity)); - mood += CalculateScore((int)requirementsForPlant.Result.TemperatureLevel, (int)CalculateTemperatureLevel(conditionsLog.Temperature)); - mood += CalculateScore((int)requirementsForPlant.Result.SoilMoistureLevel, (int)CalculateSoilMoistureLevel(conditionsLog.SoilMoisture)); - mood += CalculateScore((int)requirementsForPlant.Result.LightLevel, (int)CalculateLightLevel(conditionsLog.Light)); + mood += CalculateScore((int)requirementsForPlant.HumidityLevel, (int)CalculateHumidityLevel(conditionsLog.Humidity)); + mood += CalculateScore((int)requirementsForPlant.TemperatureLevel, (int)CalculateTemperatureLevel(conditionsLog.Temperature)); + mood += CalculateScore((int)requirementsForPlant.SoilMoistureLevel, (int)CalculateSoilMoistureLevel(conditionsLog.SoilMoisture)); + mood += CalculateScore((int)requirementsForPlant.LightLevel, (int)CalculateLightLevel(conditionsLog.Light)); if (mood == 0) { @@ -124,4 +124,12 @@ private int CalculateScore(int ideal, int actual) return 0; // Two away } } + + public async Task GetLatestConditionsLogForPlant(Guid plantId, string loggedInUser) + { + var plant = await plantService.GetPlantById(plantId, loggedInUser); + var conditionsLog = await conditionsLogsRepository.GetLatestConditionsLogForPlant(plantId); + if (conditionsLog is null) throw new NotFoundException($"No conditions log found for plant with id {plantId}"); + return conditionsLog; + } } \ No newline at end of file diff --git a/Core/Services/PlantService.cs b/Core/Services/PlantService.cs index a35bded..58fa419 100644 --- a/Core/Services/PlantService.cs +++ b/Core/Services/PlantService.cs @@ -110,6 +110,11 @@ public async Task DeletePlant(Guid id, string requesterEmail) await plantRepository.DeletePlant(plant); } + public async Task GetPlantIdByDeviceIdAsync(string deviceId) + { + return await plantRepository.GetPlantIdByDeviceIdAsync(deviceId); + } + private string GenerateRandomNickname() { var firstName = new List diff --git a/Infrastructure/Repositories/ConditionsLogsRepository.cs b/Infrastructure/Repositories/ConditionsLogsRepository.cs index cfcd8d1..8182358 100644 --- a/Infrastructure/Repositories/ConditionsLogsRepository.cs +++ b/Infrastructure/Repositories/ConditionsLogsRepository.cs @@ -31,4 +31,13 @@ public async Task GetRecentMoodAsync(Guid plantId) return -1; } } + + public async Task GetLatestConditionsLogForPlant(Guid plantId) + { + await using var context = await dbContextFactory.CreateDbContextAsync(); + return await context.ConditionsLogs + .Where(log => log.PlantId == plantId) + .OrderByDescending(log => log.TimeStamp) + .FirstOrDefaultAsync(); + } } \ No newline at end of file diff --git a/api/Events/Conditions/ClientWantsLatestConditionsForPlant.cs b/api/Events/Conditions/ClientWantsLatestConditionsForPlant.cs new file mode 100644 index 0000000..51b71dc --- /dev/null +++ b/api/Events/Conditions/ClientWantsLatestConditionsForPlant.cs @@ -0,0 +1,26 @@ +using api.Extensions; +using Core.Services; +using Fleck; +using lib; +using Shared.Models; + +namespace api.Events.Conditions; + +public class ClientWantsLatestConditionsForPlantDto : BaseDtoWithJwt +{ + public Guid PlantId { get; set; } +} + +public class ClientWantsLatestConditionsForPlant(JwtService jwtService, ConditionsLogsService conditionsLogsService) : BaseEventHandler +{ + public override async Task Handle(ClientWantsLatestConditionsForPlantDto dto, IWebSocketConnection socket) + { + var email = jwtService.GetEmailFromJwt(dto.Jwt!); + var conditionsLog = await conditionsLogsService.GetLatestConditionsLogForPlant(dto.PlantId, email); + socket.SendDto(new ServerSendsLatestConditionsForPlant() + { + ConditionsLog = conditionsLog + } + ); + } +} \ No newline at end of file diff --git a/api/Events/Conditions/ServerSendsLatestConditionsForPlant.cs b/api/Events/Conditions/ServerSendsLatestConditionsForPlant.cs new file mode 100644 index 0000000..ad0e8b4 --- /dev/null +++ b/api/Events/Conditions/ServerSendsLatestConditionsForPlant.cs @@ -0,0 +1,9 @@ +using lib; +using Shared.Models.Information; + +namespace api.Events.Conditions; + +public class ServerSendsLatestConditionsForPlant : BaseDto +{ + public required ConditionsLog ConditionsLog; +} \ No newline at end of file diff --git a/api/Events/PlantEvents/Client/ClientWantsAllPlants.cs b/api/Events/PlantEvents/Client/ClientWantsAllPlants.cs index 3dfbcbb..0dd21f0 100644 --- a/api/Events/PlantEvents/Client/ClientWantsAllPlants.cs +++ b/api/Events/PlantEvents/Client/ClientWantsAllPlants.cs @@ -1,5 +1,6 @@ using System.ComponentModel.DataAnnotations; using api.EventFilters; +using api.Events.Collections.Server; using api.Events.PlantEvents.Server; using api.Extensions; using Core.Services; @@ -20,9 +21,9 @@ public class ClientWantsAllPlants(PlantService plantService, JwtService jwtServi { public override async Task Handle(ClientWantsAllPlantsDto dto, IWebSocketConnection socket) { - var email = jwtService.GetEmailFromJwt(dto.Jwt); + var email = jwtService.GetEmailFromJwt(dto.Jwt!); var plants = await plantService.GetPlantsForUser(email, dto.PageNumber, dto.PageSize); - var serverSendsAllPlantsDto = new ServerSendsAllPlants + var serverSendsAllPlantsDto = new ServerSendsPlantsForCollection { Plants = plants }; diff --git a/api/Events/PlantEvents/Server/ServerSendsAllPlants.cs b/api/Events/PlantEvents/Server/ServerSendsAllPlants.cs deleted file mode 100644 index 503c6d7..0000000 --- a/api/Events/PlantEvents/Server/ServerSendsAllPlants.cs +++ /dev/null @@ -1,9 +0,0 @@ -using lib; -using Shared.Models; - -namespace api.Events.PlantEvents.Server; - -public class ServerSendsAllPlants: BaseDto -{ - public List Plants { get; set; } -} \ No newline at end of file From 1cb7f02ef29c888910f00b42d8a94b88f9a209dd Mon Sep 17 00:00:00 2001 From: Julia Ilasova <1julka1il@gmail.com> Date: Thu, 9 May 2024 17:24:45 +0200 Subject: [PATCH 5/7] db initializer added condition log --- Infrastructure/ApplicationDbContext.cs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/Infrastructure/ApplicationDbContext.cs b/Infrastructure/ApplicationDbContext.cs index 739566e..98f5fbc 100644 --- a/Infrastructure/ApplicationDbContext.cs +++ b/Infrastructure/ApplicationDbContext.cs @@ -135,5 +135,18 @@ await plantRepository.CreatePlant( } ); plants[1].Requirements = requirements2; + + var conditionsLogRepository = scope.ServiceProvider.GetRequiredService(); + await conditionsLogRepository.CreateConditionsLogAsync( + new ConditionsLog + { + ConditionsId = Guid.NewGuid(), + PlantId = plants[0].PlantId, + Light = 33.0, + SoilMoisture = 74.0, + Humidity = 50.0, + Temperature = 22.0, + } + ); } } \ No newline at end of file From 0fb01da5e28a938e48a5a5d274391e3809e8605c Mon Sep 17 00:00:00 2001 From: Julia Ilasova <1julka1il@gmail.com> Date: Thu, 9 May 2024 20:27:15 +0200 Subject: [PATCH 6/7] minor changes --- Core/Services/ConditionsLogsService.cs | 3 ++- Infrastructure/ApplicationDbContext.cs | 2 ++ Shared/Models/Information/ConditionsLog.cs | 16 ++++++++-------- Tests/PlantTests.cs | 3 ++- .../ClientWantsLatestConditionsForPlant.cs | 12 +++++++----- .../ServerSendsLatestConditionsForPlant.cs | 2 +- .../PlantEvents/Client/ClientWantsPlantById.cs | 2 +- api/Extensions/WebSocketExtensions.cs | 2 ++ 8 files changed, 25 insertions(+), 17 deletions(-) diff --git a/Core/Services/ConditionsLogsService.cs b/Core/Services/ConditionsLogsService.cs index 566f4c0..874a5e8 100644 --- a/Core/Services/ConditionsLogsService.cs +++ b/Core/Services/ConditionsLogsService.cs @@ -26,7 +26,8 @@ public async Task CreateConditionsLogAsync(CreateConditionsLogDto createConditio Light = createConditionsLogDto.Light, Temperature = createConditionsLogDto.Temperature, Humidity = createConditionsLogDto.Humidity, - PlantId = plantId + PlantId = plantId, + Mood = -1 }; var newMood = await CalculateMood(conditionsLog); diff --git a/Infrastructure/ApplicationDbContext.cs b/Infrastructure/ApplicationDbContext.cs index 98f5fbc..3fe9893 100644 --- a/Infrastructure/ApplicationDbContext.cs +++ b/Infrastructure/ApplicationDbContext.cs @@ -142,6 +142,8 @@ await conditionsLogRepository.CreateConditionsLogAsync( { ConditionsId = Guid.NewGuid(), PlantId = plants[0].PlantId, + TimeStamp = DateTime.UtcNow, + Mood = 2, Light = 33.0, SoilMoisture = 74.0, Humidity = 50.0, diff --git a/Shared/Models/Information/ConditionsLog.cs b/Shared/Models/Information/ConditionsLog.cs index d35a31c..726fb0f 100644 --- a/Shared/Models/Information/ConditionsLog.cs +++ b/Shared/Models/Information/ConditionsLog.cs @@ -2,12 +2,12 @@ namespace Shared.Models.Information; public class ConditionsLog { - public Guid ConditionsId { get; set; } - public Guid PlantId { get; set; } - public DateTime TimeStamp { get; set; } - public int Mood { get; set; } - public double SoilMoisture { get; set; } - public double Light { get; set; } - public double Temperature { get; set; } - public double Humidity { get; set; } + public required Guid ConditionsId { get; set; } + public required Guid PlantId { get; set; } + public required DateTime TimeStamp { get; set; } + public required int Mood { get; set; } + public required double SoilMoisture { get; set; } + public required double Light { get; set; } + public required double Temperature { get; set; } + public required double Humidity { get; set; } } \ No newline at end of file diff --git a/Tests/PlantTests.cs b/Tests/PlantTests.cs index 1eaa191..017aef1 100644 --- a/Tests/PlantTests.cs +++ b/Tests/PlantTests.cs @@ -1,3 +1,4 @@ +using api.Events.Collections.Server; using api.Events.PlantEvents.Client; using api.Events.PlantEvents.Server; using lib; @@ -31,7 +32,7 @@ await webSocketTestClient.DoAndAssert(new ClientWantsAllPlantsDto PageSize = 10 }, receivedMessages => { - return receivedMessages.Count(e => e.eventType == nameof(ServerSendsAllPlants)) == 1; + return receivedMessages.Count(e => e.eventType == nameof(ServerSendsPlantsForCollection)) == 1; }); } diff --git a/api/Events/Conditions/ClientWantsLatestConditionsForPlant.cs b/api/Events/Conditions/ClientWantsLatestConditionsForPlant.cs index 51b71dc..0e8748a 100644 --- a/api/Events/Conditions/ClientWantsLatestConditionsForPlant.cs +++ b/api/Events/Conditions/ClientWantsLatestConditionsForPlant.cs @@ -17,10 +17,12 @@ public override async Task Handle(ClientWantsLatestConditionsForPlantDto dto, IW { var email = jwtService.GetEmailFromJwt(dto.Jwt!); var conditionsLog = await conditionsLogsService.GetLatestConditionsLogForPlant(dto.PlantId, email); - socket.SendDto(new ServerSendsLatestConditionsForPlant() - { - ConditionsLog = conditionsLog - } - ); + + var serverResponse = new ServerSendsLatestConditionsForPlant + { + ConditionsLog = conditionsLog + }; + + socket.SendDto(serverResponse); } } \ No newline at end of file diff --git a/api/Events/Conditions/ServerSendsLatestConditionsForPlant.cs b/api/Events/Conditions/ServerSendsLatestConditionsForPlant.cs index ad0e8b4..c483b81 100644 --- a/api/Events/Conditions/ServerSendsLatestConditionsForPlant.cs +++ b/api/Events/Conditions/ServerSendsLatestConditionsForPlant.cs @@ -5,5 +5,5 @@ namespace api.Events.Conditions; public class ServerSendsLatestConditionsForPlant : BaseDto { - public required ConditionsLog ConditionsLog; + public required ConditionsLog ConditionsLog { get; set; } } \ No newline at end of file diff --git a/api/Events/PlantEvents/Client/ClientWantsPlantById.cs b/api/Events/PlantEvents/Client/ClientWantsPlantById.cs index 2b4861c..0708b59 100644 --- a/api/Events/PlantEvents/Client/ClientWantsPlantById.cs +++ b/api/Events/PlantEvents/Client/ClientWantsPlantById.cs @@ -20,7 +20,7 @@ public class ClientWantsPlantById(PlantService plantService, JwtService jwtServi { public override async Task Handle(ClientWantsPlantByIdDto dto, IWebSocketConnection socket) { - var email = jwtService.GetEmailFromJwt(dto.Jwt); + var email = jwtService.GetEmailFromJwt(dto.Jwt!); var plant = await plantService.GetPlantById(dto.PlantId, email); socket.SendDto(new ServerSendsPlant { diff --git a/api/Extensions/WebSocketExtensions.cs b/api/Extensions/WebSocketExtensions.cs index 573ff1a..e051464 100644 --- a/api/Extensions/WebSocketExtensions.cs +++ b/api/Extensions/WebSocketExtensions.cs @@ -1,6 +1,7 @@ using System.Text.Json; using Fleck; using lib; +using Serilog; namespace api.Extensions; @@ -13,6 +14,7 @@ public static class WebSocketExtensions public static void SendDto(this IWebSocketConnection ws, T dto) where T : BaseDto { + Log.Information("Sending message: {message}", JsonSerializer.Serialize(dto, Options)); ws.Send(JsonSerializer.Serialize(dto, Options) ?? throw new ArgumentException("Failed to serialize dto")); } } \ No newline at end of file From 30be9fb705633266001e2f3fef46bc137cb9d648 Mon Sep 17 00:00:00 2001 From: Julia Ilasova <1julka1il@gmail.com> Date: Fri, 10 May 2024 10:01:42 +0200 Subject: [PATCH 7/7] PR comments --- Core/Services/CollectionsService.cs | 16 ++++++++-------- Tests/PlantTests.cs | 2 +- .../Client/ClientWantsPlantsForCollection.cs | 4 +++- .../Server/ServerSendsPlantsForCollection.cs | 9 --------- .../PlantEvents/Client/ClientWantsAllPlants.cs | 2 +- .../PlantEvents/Server/ServerSendsPlants.cs | 10 ++++++++++ 6 files changed, 23 insertions(+), 20 deletions(-) delete mode 100644 api/Events/Collections/Server/ServerSendsPlantsForCollection.cs create mode 100644 api/Events/PlantEvents/Server/ServerSendsPlants.cs diff --git a/Core/Services/CollectionsService.cs b/Core/Services/CollectionsService.cs index eaddcff..d0f4700 100644 --- a/Core/Services/CollectionsService.cs +++ b/Core/Services/CollectionsService.cs @@ -8,9 +8,9 @@ namespace Core.Services; public class CollectionsService(CollectionsRepository collectionsRepository, PlantService plantService) { - public async Task> GetCollectionsForUser(string userEmail) + public async Task> GetCollectionsForUser(string loggedInUser) { - var collectionsWithoutPlants = await collectionsRepository.GetCollectionsForUser(userEmail); + var collectionsWithoutPlants = await collectionsRepository.GetCollectionsForUser(loggedInUser); return collectionsWithoutPlants.Select(collection => new GetCollectionDto { CollectionId = collection.CollectionId, @@ -18,14 +18,14 @@ public async Task> GetCollectionsForUser(string us }); } - public async Task GetCollection(Guid collectionId, string userEmail) + public async Task GetCollection(Guid collectionId, string loggedInUser) { - return await VerifyCollectionExistsAndUserHasAccess(collectionId, userEmail); + return await VerifyCollectionExistsAndUserHasAccess(collectionId, loggedInUser); } - public async Task> GetPlantsInCollection(Guid collectionId, string userEmail) + public async Task> GetPlantsInCollection(Guid collectionId, string loggedInUser) { - await VerifyCollectionExistsAndUserHasAccess(collectionId, userEmail); + await VerifyCollectionExistsAndUserHasAccess(collectionId, loggedInUser); return await plantService.GetPlantsForCollection(collectionId); } @@ -67,11 +67,11 @@ public async Task RemovePlantFromCollection(Guid collectionId, Guid plantId, str await collectionsRepository.RemovePlantFromCollection(collection, plant); } - private async Task VerifyCollectionExistsAndUserHasAccess(Guid collectionId, string userEmail) + private async Task VerifyCollectionExistsAndUserHasAccess(Guid collectionId, string loggedInUser) { var collection = await collectionsRepository.GetCollectionWithoutPlants(collectionId); if (collection is null) throw new NotFoundException("Collection not found"); - if (collection.UserEmail != userEmail) throw new NoAccessException("You don't have access to this collection"); + if (collection.UserEmail != loggedInUser) throw new NoAccessException("You don't have access to this collection"); return collection; } } \ No newline at end of file diff --git a/Tests/PlantTests.cs b/Tests/PlantTests.cs index 017aef1..2cd4657 100644 --- a/Tests/PlantTests.cs +++ b/Tests/PlantTests.cs @@ -32,7 +32,7 @@ await webSocketTestClient.DoAndAssert(new ClientWantsAllPlantsDto PageSize = 10 }, receivedMessages => { - return receivedMessages.Count(e => e.eventType == nameof(ServerSendsPlantsForCollection)) == 1; + return receivedMessages.Count(e => e.eventType == nameof(ServerSendsPlants)) == 1; }); } diff --git a/api/Events/Collections/Client/ClientWantsPlantsForCollection.cs b/api/Events/Collections/Client/ClientWantsPlantsForCollection.cs index 05a2c6a..fba2a14 100644 --- a/api/Events/Collections/Client/ClientWantsPlantsForCollection.cs +++ b/api/Events/Collections/Client/ClientWantsPlantsForCollection.cs @@ -1,4 +1,5 @@ using api.Events.Collections.Server; +using api.Events.PlantEvents.Server; using api.Extensions; using Core.Services; using Fleck; @@ -18,8 +19,9 @@ public override async Task Handle(ClientWantsPlantsForCollectionDto dto, IWebSoc { var email = jwtService.GetEmailFromJwt(dto.Jwt!); var plants = await collectionsService.GetPlantsInCollection(dto.CollectionId, email); - socket.SendDto(new ServerSendsPlantsForCollection + socket.SendDto(new ServerSendsPlants { + CollectionId = dto.CollectionId, Plants = plants.ToList() }); } diff --git a/api/Events/Collections/Server/ServerSendsPlantsForCollection.cs b/api/Events/Collections/Server/ServerSendsPlantsForCollection.cs deleted file mode 100644 index 10d61d8..0000000 --- a/api/Events/Collections/Server/ServerSendsPlantsForCollection.cs +++ /dev/null @@ -1,9 +0,0 @@ -using lib; -using Shared.Models; - -namespace api.Events.Collections.Server; - -public class ServerSendsPlantsForCollection : BaseDto -{ - public List Plants { get; set; } -} \ No newline at end of file diff --git a/api/Events/PlantEvents/Client/ClientWantsAllPlants.cs b/api/Events/PlantEvents/Client/ClientWantsAllPlants.cs index 0dd21f0..973a49f 100644 --- a/api/Events/PlantEvents/Client/ClientWantsAllPlants.cs +++ b/api/Events/PlantEvents/Client/ClientWantsAllPlants.cs @@ -23,7 +23,7 @@ public override async Task Handle(ClientWantsAllPlantsDto dto, IWebSocketConnect { var email = jwtService.GetEmailFromJwt(dto.Jwt!); var plants = await plantService.GetPlantsForUser(email, dto.PageNumber, dto.PageSize); - var serverSendsAllPlantsDto = new ServerSendsPlantsForCollection + var serverSendsAllPlantsDto = new ServerSendsPlants { Plants = plants }; diff --git a/api/Events/PlantEvents/Server/ServerSendsPlants.cs b/api/Events/PlantEvents/Server/ServerSendsPlants.cs new file mode 100644 index 0000000..f3dcd3b --- /dev/null +++ b/api/Events/PlantEvents/Server/ServerSendsPlants.cs @@ -0,0 +1,10 @@ +using lib; +using Shared.Models; + +namespace api.Events.PlantEvents.Server; + +public class ServerSendsPlants : BaseDto +{ + public Guid? CollectionId { get; set; } + public required List Plants { get; set; } +} \ No newline at end of file