diff --git a/service-course-api/build.gradle b/service-course-api/build.gradle index 94a1913..0270147 100644 --- a/service-course-api/build.gradle +++ b/service-course-api/build.gradle @@ -27,7 +27,9 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'com.netflix.graphql.dgs:graphql-dgs-extended-scalars' testImplementation "org.mockito:mockito-core:3.+" - implementation 'com.graphql-java:graphql-java:21.2' + implementation 'com.graphql-java:graphql-java:21.2' // Locked version to avoid problems + implementation 'org.springframework.boot:spring-boot-starter-cache' + implementation 'com.github.ben-manes.caffeine:caffeine:3.1.8' } generateJava { diff --git a/service-course-api/src/main/java/servicecourse/CacheConfiguration.java b/service-course-api/src/main/java/servicecourse/CacheConfiguration.java new file mode 100644 index 0000000..f45fa60 --- /dev/null +++ b/service-course-api/src/main/java/servicecourse/CacheConfiguration.java @@ -0,0 +1,16 @@ +package servicecourse; + +import org.springframework.cache.CacheManager; +import org.springframework.cache.caffeine.CaffeineCacheManager; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class CacheConfiguration { + public static final String BIKE_BRANDS = "bikeBrands"; + + @Bean + public CacheManager cacheManager() { + return new CaffeineCacheManager(BIKE_BRANDS); + } +} diff --git a/service-course-api/src/main/java/servicecourse/ServiceCourseApplication.java b/service-course-api/src/main/java/servicecourse/ServiceCourseApplication.java index 2abb576..ebfb17a 100644 --- a/service-course-api/src/main/java/servicecourse/ServiceCourseApplication.java +++ b/service-course-api/src/main/java/servicecourse/ServiceCourseApplication.java @@ -2,8 +2,10 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cache.annotation.EnableCaching; @SpringBootApplication +@EnableCaching public class ServiceCourseApplication { public static void main(String[] args) { SpringApplication.run(ServiceCourseApplication.class, args); diff --git a/service-course-api/src/main/java/servicecourse/datafetchers/BikeBrandsDataFetcher.java b/service-course-api/src/main/java/servicecourse/datafetchers/BikeBrandsDataFetcher.java index a9fc4cb..7556b50 100644 --- a/service-course-api/src/main/java/servicecourse/datafetchers/BikeBrandsDataFetcher.java +++ b/service-course-api/src/main/java/servicecourse/datafetchers/BikeBrandsDataFetcher.java @@ -2,33 +2,32 @@ import com.netflix.graphql.dgs.*; import lombok.RequiredArgsConstructor; +import org.dataloader.DataLoader; import servicecourse.generated.types.BikeBrand; import servicecourse.generated.types.CreateBikeBrandInput; import servicecourse.generated.types.Model; import servicecourse.services.bikebrands.BikeBrandsService; -import servicecourse.services.models.ModelsService; import java.util.List; +import java.util.concurrent.CompletableFuture; @DgsComponent @RequiredArgsConstructor public class BikeBrandsDataFetcher { - private final ModelsService modelsService; private final BikeBrandsService bikeBrandsService; /** - * This is an enhanced attribute. Services will not generate it ahead of time, unlike brand - * name. Therefore, if specified, it must be computed here. + * This is an enhanced attribute. Services will not generate it ahead of time (unlike brand + * name). Therefore, if specified by the user, it must be computed here. + *

+ * A data loader is used to avoid sending multiple separate requests to the models service when + * handling a request that involves multiple bike brands (see {@link #bikeBrands()}). */ @DgsData(parentType = "BikeBrand", field = "models") - public List models(DgsDataFetchingEnvironment dfe) { + public CompletableFuture> models(DgsDataFetchingEnvironment dfe) { BikeBrand bikeBrand = dfe.getSource(); - // Data loader here - // Why? - // Because if some wind up merchant decides to ask for models on all bike brands - // after running bikeBrands query - // Models service will get pinged N+1 times - return modelsService.findByBrandName(bikeBrand.getName()); + DataLoader> modelsDataLoader = dfe.getDataLoader("models"); + return modelsDataLoader.load(bikeBrand.getName()); } @DgsQuery diff --git a/service-course-api/src/main/java/servicecourse/repo/ModelRepository.java b/service-course-api/src/main/java/servicecourse/repo/ModelRepository.java index 955f6e8..104b560 100644 --- a/service-course-api/src/main/java/servicecourse/repo/ModelRepository.java +++ b/service-course-api/src/main/java/servicecourse/repo/ModelRepository.java @@ -8,5 +8,7 @@ @Repository public interface ModelRepository extends JpaRepository, JpaSpecificationExecutor { - List findAllByBrandName(String brandName); + List findByBrandName(String brandName); + + List findByBrandNameIn(List brandNames); } diff --git a/service-course-api/src/main/java/servicecourse/repo/URLConverter.java b/service-course-api/src/main/java/servicecourse/repo/URLConverter.java index 82fe8ae..4d57f8d 100644 --- a/service-course-api/src/main/java/servicecourse/repo/URLConverter.java +++ b/service-course-api/src/main/java/servicecourse/repo/URLConverter.java @@ -5,6 +5,7 @@ import java.net.MalformedURLException; import java.net.URL; +/** Converts a potentially null URL */ public class URLConverter implements AttributeConverter { @Override public String convertToDatabaseColumn(URL url) { diff --git a/service-course-api/src/main/java/servicecourse/repo/specification/StringFilterSpecification.java b/service-course-api/src/main/java/servicecourse/repo/specification/StringFilterSpecification.java new file mode 100644 index 0000000..8b2591a --- /dev/null +++ b/service-course-api/src/main/java/servicecourse/repo/specification/StringFilterSpecification.java @@ -0,0 +1,16 @@ +package servicecourse.repo.specification; + +import servicecourse.generated.types.StringFilterInput; + +import java.util.Optional; +import java.util.function.Predicate; + +public class StringFilterSpecification { + private StringFilterSpecification() { } + + public static Predicate from(StringFilterInput input) { + return arg -> Optional.ofNullable(input.getEquals()).map(arg::equals).orElse(true) + || Optional.ofNullable(input.getContains()).map(arg::contains).orElse(true) + || Optional.ofNullable(input.getIn()).map(in -> in.contains(arg)).orElse(true); + } +} diff --git a/service-course-api/src/main/java/servicecourse/services/bikebrands/RelationalBikeBrandsService.java b/service-course-api/src/main/java/servicecourse/services/bikebrands/BikeBrandsServiceImpl.java similarity index 72% rename from service-course-api/src/main/java/servicecourse/services/bikebrands/RelationalBikeBrandsService.java rename to service-course-api/src/main/java/servicecourse/services/bikebrands/BikeBrandsServiceImpl.java index 0a75557..0062812 100644 --- a/service-course-api/src/main/java/servicecourse/services/bikebrands/RelationalBikeBrandsService.java +++ b/service-course-api/src/main/java/servicecourse/services/bikebrands/BikeBrandsServiceImpl.java @@ -1,7 +1,10 @@ package servicecourse.services.bikebrands; import lombok.RequiredArgsConstructor; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; +import servicecourse.CacheConfiguration; import servicecourse.generated.types.BikeBrand; import servicecourse.generated.types.CreateBikeBrandInput; import servicecourse.repo.BikeBrandEntity; @@ -13,10 +16,11 @@ @Service @RequiredArgsConstructor -public class RelationalBikeBrandsService implements BikeBrandsService { +public class BikeBrandsServiceImpl implements BikeBrandsService { private final BikeBrandRepository bikeBrandRepository; @Override + @CacheEvict(value = CacheConfiguration.BIKE_BRANDS) public BikeBrand createBikeBrand(CreateBikeBrandInput input) { // Validate that the brand doesn't already exist bikeBrandRepository.findById(input.getName()) @@ -26,6 +30,7 @@ public BikeBrand createBikeBrand(CreateBikeBrandInput input) { } @Override + @CacheEvict(value = CacheConfiguration.BIKE_BRANDS) public String deleteBikeBrand(String name) { return bikeBrandRepository.findById(name).map((entity) -> { bikeBrandRepository.deleteById(name); @@ -33,7 +38,12 @@ public String deleteBikeBrand(String name) { }).orElseThrow(Errors::newBikeBrandNotFoundError); } + /** + * This method is cached. The cache is invalidated by {@link #createBikeBrand} and + * {@link #deleteBikeBrand}. + */ @Override + @Cacheable(value = CacheConfiguration.BIKE_BRANDS, sync = true) public List bikeBrands() { return bikeBrandRepository.findAll() .stream() diff --git a/service-course-api/src/main/java/servicecourse/services/bikes/RelationalBikesService.java b/service-course-api/src/main/java/servicecourse/services/bikes/BikesServiceImpl.java similarity index 98% rename from service-course-api/src/main/java/servicecourse/services/bikes/RelationalBikesService.java rename to service-course-api/src/main/java/servicecourse/services/bikes/BikesServiceImpl.java index 50217f6..11a6d38 100644 --- a/service-course-api/src/main/java/servicecourse/services/bikes/RelationalBikesService.java +++ b/service-course-api/src/main/java/servicecourse/services/bikes/BikesServiceImpl.java @@ -16,7 +16,7 @@ @Service @RequiredArgsConstructor -public class RelationalBikesService implements BikesService { +public class BikesServiceImpl implements BikesService { private final BikeRepository bikeRepository; private final ModelRepository modelRepository; private final GroupsetRespository groupsetRespository; diff --git a/service-course-api/src/main/java/servicecourse/services/groupsets/GroupsetServiceImpl.java b/service-course-api/src/main/java/servicecourse/services/groupsets/GroupsetServiceImpl.java new file mode 100644 index 0000000..214f22b --- /dev/null +++ b/service-course-api/src/main/java/servicecourse/services/groupsets/GroupsetServiceImpl.java @@ -0,0 +1,4 @@ +package servicecourse.services.groupsets; + +public class GroupsetServiceImpl implements GroupsetService { +} diff --git a/service-course-api/src/main/java/servicecourse/services/groupsets/RelationalGroupsetService.java b/service-course-api/src/main/java/servicecourse/services/groupsets/RelationalGroupsetService.java deleted file mode 100644 index 16a2088..0000000 --- a/service-course-api/src/main/java/servicecourse/services/groupsets/RelationalGroupsetService.java +++ /dev/null @@ -1,4 +0,0 @@ -package servicecourse.services.groupsets; - -public class RelationalGroupsetService implements GroupsetService { -} diff --git a/service-course-api/src/main/java/servicecourse/services/models/ModelsService.java b/service-course-api/src/main/java/servicecourse/services/models/ModelsService.java index a56421d..e3bc449 100644 --- a/service-course-api/src/main/java/servicecourse/services/models/ModelsService.java +++ b/service-course-api/src/main/java/servicecourse/services/models/ModelsService.java @@ -7,11 +7,17 @@ import java.util.Map; public interface ModelsService { + /** + * @return all models belonging to the brand + */ List findByBrandName(String brandName); Model createModel(CreateModelInput input); String deleteModel(String id); + /** + * @return a map; bike brand name -> models for that brand + */ Map> modelsForBikeBrands(List brandNames); } diff --git a/service-course-api/src/main/java/servicecourse/services/models/RelationalModelsService.java b/service-course-api/src/main/java/servicecourse/services/models/ModelsServiceImpl.java similarity index 72% rename from service-course-api/src/main/java/servicecourse/services/models/RelationalModelsService.java rename to service-course-api/src/main/java/servicecourse/services/models/ModelsServiceImpl.java index fbac659..2a8d261 100644 --- a/service-course-api/src/main/java/servicecourse/services/models/RelationalModelsService.java +++ b/service-course-api/src/main/java/servicecourse/services/models/ModelsServiceImpl.java @@ -2,7 +2,6 @@ import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; -import servicecourse.generated.types.BikeBrand; import servicecourse.generated.types.CreateModelInput; import servicecourse.generated.types.Model; import servicecourse.repo.BikeBrandRepository; @@ -10,20 +9,19 @@ import servicecourse.repo.ModelRepository; import servicecourse.services.Errors; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; @Service @RequiredArgsConstructor -public class RelationalModelsService implements ModelsService { +public class ModelsServiceImpl implements ModelsService { private final ModelRepository modelRepository; private final BikeBrandRepository bikeBrandRepository; @Override public List findByBrandName(String brandName) { - return modelRepository.findAllByBrandName(brandName) + return modelRepository.findByBrandName(brandName) .stream() .map(ModelEntity::asModel) .collect(Collectors.toList()); @@ -55,14 +53,10 @@ public String deleteModel(String id) { } public Map> modelsForBikeBrands(List brandNames) { - Map> result = new HashMap<>(); - result.put("hello", List.of(Model.newBuilder() - .name("hello") - .brand(BikeBrand.newBuilder() - .name("hello").build()) - .modelYear(2022) - .build())); - return result; + return modelRepository.findByBrandNameIn(brandNames) + .stream() + .map(ModelEntity::asModel) + .collect(Collectors.groupingBy(m -> m.getBrand().getName())); } } diff --git a/service-course-api/src/main/resources/schema/schema.graphqls b/service-course-api/src/main/resources/schema/schema.graphqls index 786f888..636e98c 100644 --- a/service-course-api/src/main/resources/schema/schema.graphqls +++ b/service-course-api/src/main/resources/schema/schema.graphqls @@ -15,15 +15,40 @@ scalar Date # Utils +enum Op { + OR + AND +} + input DateRangeFilterInput { from: Date! to: Date! } +""" +Fields are combined with OR logic +""" +input StringFilterInput { + contains: String + in: [String!] + equals: String +} + +input IntegerFilterInput { + lessThanOrEqualTo: Int + greaterThanOrEqualTo: Int + equals: Int + in: [Int!] + operator: Op +} + # Queries and mutations type Query { - bikes(filter: BikesFilterInput): [Bike!] + bikes(filter: BikesFilterInput): [Bike!]! + """ + All bike brands + """ bikeBrands: [BikeBrand!]! # """ # Get the available days to book for a give bike for a given date range. @@ -47,7 +72,7 @@ type Mutation { deleteBikeBrand(name: String!): String } -# Models +# Bike brands type BikeBrand { name: String! @@ -58,6 +83,8 @@ input CreateBikeBrandInput { name: String! } +# Models + type Model { id: ID! name: String! @@ -71,6 +98,15 @@ input CreateModelInput { brandName: String! } +""" +Fields are combined with AND logic +""" +input ModelFilterInput { + name: StringFilterInput + modelYear: IntegerFilterInput + brandName: String +} + # Bikes type Bike { @@ -85,11 +121,23 @@ input BikesFilterInput { """ If specified, return only bikes that are available in the provided date range """ - availableDateRangeFilter: DateRangeFilterInput + availableDateRange: DateRangeFilterInput + """ + If specified, return only bikes whose model matches the criteria + """ + model: ModelFilterInput + """ + If specified, return only bikes with a groupset matching the criteria + """ + groupset: GroupsetFilterInput + size: StringFilterInput } input CreateBikeInput { modelId: ID! + """ + There must be a groupset with this name already saved otherwise the mutation will fail + """ groupsetName: String! size: String! heroImageUrl: Url @@ -97,6 +145,9 @@ input CreateBikeInput { input UpdateBikeInput { bikeId: ID! + """ + IF specified, there must be a groupset with this name already saved otherwise the mutation will fail + """ groupsetName: String heroImageUrl: Url } @@ -114,3 +165,12 @@ type Groupset { brand: GroupsetBrand! isElectronic: Boolean! } + +""" +Fields are combined with AND logic +""" +input GroupsetFilterInput { + name: String + brand: GroupsetBrand + isElectronic: Boolean +} diff --git a/service-course-api/src/test/java/servicecourse/services/bikebrands/RelationalBikeBrandsServiceTest.java b/service-course-api/src/test/java/servicecourse/services/bikebrands/BikeBrandsServiceImplTest.java similarity index 82% rename from service-course-api/src/test/java/servicecourse/services/bikebrands/RelationalBikeBrandsServiceTest.java rename to service-course-api/src/test/java/servicecourse/services/bikebrands/BikeBrandsServiceImplTest.java index 7ff46c3..9e9a438 100644 --- a/service-course-api/src/test/java/servicecourse/services/bikebrands/RelationalBikeBrandsServiceTest.java +++ b/service-course-api/src/test/java/servicecourse/services/bikebrands/BikeBrandsServiceImplTest.java @@ -20,14 +20,14 @@ import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) -public class RelationalBikeBrandsServiceTest { +public class BikeBrandsServiceImplTest { @Mock BikeBrandRepository mockBikeBrandRepository; - RelationalBikeBrandsService relationalBikesBrandsService; + BikeBrandsServiceImpl bikeBrandsService; @BeforeEach void setup() { - relationalBikesBrandsService = new RelationalBikeBrandsService(mockBikeBrandRepository); + bikeBrandsService = new BikeBrandsServiceImpl(mockBikeBrandRepository); } @Nested @@ -42,9 +42,9 @@ void fail_because_bike_brand_already_exists() { // When we call the createBikeBrand method with that name // Then the method should throw assertThrows(IllegalArgumentException.class, - () -> relationalBikesBrandsService.createBikeBrand(CreateBikeBrandInput.newBuilder() - .name(bikeBrandName) - .build())); + () -> bikeBrandsService.createBikeBrand(CreateBikeBrandInput.newBuilder() + .name(bikeBrandName) + .build())); } @Test @@ -61,9 +61,9 @@ void success() { .thenReturn(expectedEntity); // When we call the createBikeBrand method with that name - BikeBrand result = relationalBikesBrandsService.createBikeBrand(CreateBikeBrandInput.newBuilder() - .name(bikeBrandName) - .build()); + BikeBrand result = bikeBrandsService.createBikeBrand(CreateBikeBrandInput.newBuilder() + .name(bikeBrandName) + .build()); // Then we should have received the expected BikeBrand object assertThat(result).isEqualTo(expectedEntity.asBikeBrand()); @@ -81,7 +81,7 @@ void fail_because_no_bike_brand() { // When we call the deleteBikeBrand method with that name // Then the method should throw assertThrows(NoSuchElementException.class, - () -> relationalBikesBrandsService.deleteBikeBrand(ghostBikeBrandName)); + () -> bikeBrandsService.deleteBikeBrand(ghostBikeBrandName)); } @Test @@ -92,7 +92,7 @@ void success() { .thenReturn(Optional.of(BikeBrandEntity.ofName(bikeBrandName))); // When we call deleteBikeBrand with that name - String result = relationalBikesBrandsService.deleteBikeBrand(bikeBrandName); + String result = bikeBrandsService.deleteBikeBrand(bikeBrandName); // Then the repository should have been asked to delete the bike brand verify(mockBikeBrandRepository).deleteById(bikeBrandName); diff --git a/service-course-api/src/test/java/servicecourse/services/bikes/RelationalBikesServiceTest.java b/service-course-api/src/test/java/servicecourse/services/bikes/BikesServiceImplTest.java similarity index 78% rename from service-course-api/src/test/java/servicecourse/services/bikes/RelationalBikesServiceTest.java rename to service-course-api/src/test/java/servicecourse/services/bikes/BikesServiceImplTest.java index 9fbc1fe..f497c3c 100644 --- a/service-course-api/src/test/java/servicecourse/services/bikes/RelationalBikesServiceTest.java +++ b/service-course-api/src/test/java/servicecourse/services/bikes/BikesServiceImplTest.java @@ -24,20 +24,20 @@ import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) -public class RelationalBikesServiceTest { +public class BikesServiceImplTest { @Mock BikeRepository mockBikeRepository; @Mock ModelRepository mockModelRepository; @Mock GroupsetRespository mockGroupsetRepository; - RelationalBikesService relationalBikesService; + BikesServiceImpl bikesService; @BeforeEach void beforeEach() { - relationalBikesService = new RelationalBikesService(mockBikeRepository, - mockModelRepository, - mockGroupsetRepository); + bikesService = new BikesServiceImpl(mockBikeRepository, + mockModelRepository, + mockGroupsetRepository); } @Test @@ -47,7 +47,7 @@ void bikes() { // When when(mockBikeRepository.findAll()).thenReturn(List.of(result)); - List bikes = relationalBikesService.bikes(null); + List bikes = bikesService.bikes(null); // Then assertThat(bikes).isEqualTo(List.of(result.asBike())); @@ -64,12 +64,12 @@ void model_entity_not_found() { // When trying to create a bike with that model // Then createBike should throw assertThrows(NoSuchElementException.class, - () -> relationalBikesService.createBike(CreateBikeInput.newBuilder() - .modelId(BikeId.serialize( - ghostModelId)) - .groupsetName("name") - .size("Medium") - .build())); + () -> bikesService.createBike(CreateBikeInput.newBuilder() + .modelId(BikeId.serialize( + ghostModelId)) + .groupsetName("name") + .size("Medium") + .build())); } @Test @@ -86,13 +86,13 @@ void groupset_entity_not_found() { // When trying to create a bike with that groupset // Then createBike should throw assertThrows(NoSuchElementException.class, - () -> relationalBikesService.createBike(CreateBikeInput.newBuilder() - .modelId(BikeId.serialize( - modelId)) - .groupsetName( - ghostGroupsetName) - .size("Medium") - .build())); + () -> bikesService.createBike(CreateBikeInput.newBuilder() + .modelId(BikeId.serialize( + modelId)) + .groupsetName( + ghostGroupsetName) + .size("Medium") + .build())); } @Test @@ -126,13 +126,13 @@ void successful_save() { .thenReturn(expectedSavedBikeEntity); // When we call the createBike method - Bike result = relationalBikesService.createBike(CreateBikeInput.newBuilder() - .size(mockSize) - .groupsetName(mockGroupsetName) - .modelId(BikeId.serialize( - mockModelId)) - .heroImageUrl(mockUrl) - .build()); + Bike result = bikesService.createBike(CreateBikeInput.newBuilder() + .size(mockSize) + .groupsetName(mockGroupsetName) + .modelId(BikeId.serialize( + mockModelId)) + .heroImageUrl(mockUrl) + .build()); // Then we should have received the expected Bike object assertThat(result).isEqualTo(expectedSavedBikeEntity.asBike()); @@ -155,7 +155,7 @@ void bike_entity_not_found() { // When we call the updateBike method with this input // Then it should throw assertThrows(NoSuchElementException.class, - () -> relationalBikesService.updateBike(input)); + () -> bikesService.updateBike(input)); } @Test @@ -179,7 +179,7 @@ void groupset_entity_not_found() { // When we call the updateBike method with this input // Then it should throw assertThrows(NoSuchElementException.class, - () -> relationalBikesService.updateBike(input)); + () -> bikesService.updateBike(input)); } @Test @@ -215,7 +215,7 @@ void success() { .build(); // When we call the updateBike method - Bike result = relationalBikesService.updateBike(input); + Bike result = bikesService.updateBike(input); // Then we should get back the bike we expected assertThat(result).isEqualTo(expectedNewBikeEntity.asBike()); @@ -236,7 +236,7 @@ void fail_because_no_bike() { // When we call the deleteBike method with that id // Then the method should throw assertThrows(NoSuchElementException.class, - () -> relationalBikesService.deleteBike(BikeId.serialize(ghostBikeId))); + () -> bikesService.deleteBike(BikeId.serialize(ghostBikeId))); } @Test @@ -247,7 +247,7 @@ void success() { .thenReturn(Optional.of(EntityFactory.newBikeEntityWithId(bikeId))); // When we call the deleteBike method with that id - Long result = relationalBikesService.deleteBike(BikeId.serialize(bikeId)); + Long result = bikesService.deleteBike(BikeId.serialize(bikeId)); // Then the repository should have been asked to delete the bike verify(mockBikeRepository).deleteById(bikeId); diff --git a/service-course-api/src/test/java/servicecourse/services/models/RelationalModelsServiceTest.java b/service-course-api/src/test/java/servicecourse/services/models/ModelsServiceImplTest.java similarity index 87% rename from service-course-api/src/test/java/servicecourse/services/models/RelationalModelsServiceTest.java rename to service-course-api/src/test/java/servicecourse/services/models/ModelsServiceImplTest.java index 19f1754..a26f6a7 100644 --- a/service-course-api/src/test/java/servicecourse/services/models/RelationalModelsServiceTest.java +++ b/service-course-api/src/test/java/servicecourse/services/models/ModelsServiceImplTest.java @@ -23,17 +23,17 @@ import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) -public class RelationalModelsServiceTest { +public class ModelsServiceImplTest { @Mock ModelRepository mockModelRepository; @Mock BikeBrandRepository mockBikeBrandRepository; - RelationalModelsService relationalModelsService; + ModelsServiceImpl modelsService; @BeforeEach void setup() { - relationalModelsService = new RelationalModelsService(mockModelRepository, - mockBikeBrandRepository); + modelsService = new ModelsServiceImpl(mockModelRepository, + mockBikeBrandRepository); } @Nested @@ -54,7 +54,7 @@ void fail_because_no_bike_brand() { // When we call the createModel method with that bike brand // Then the method should throw assertThrows(NoSuchElementException.class, - () -> relationalModelsService.createModel(input)); + () -> modelsService.createModel(input)); } @Test @@ -88,7 +88,7 @@ void success() { .thenReturn(expectedSavedModelEntity); // When we call the createModel method with that input - Model result = relationalModelsService.createModel(input); + Model result = modelsService.createModel(input); // Then we should have received the expected Model object assertThat(result).isEqualTo(expectedSavedModelEntity.asModel()); @@ -106,7 +106,7 @@ void fail_because_no_model() { // When we call the deleteModel method with that id // Then the method should throw assertThrows(NoSuchElementException.class, - () -> relationalModelsService.deleteModel(ModelId.serialize(ghostModelId))); + () -> modelsService.deleteModel(ModelId.serialize(ghostModelId))); } @Test @@ -117,7 +117,7 @@ void success() { .thenReturn(Optional.of(EntityFactory.newModelEntityWithId(modelId))); // When we call the deleteModel method with that id - String result = relationalModelsService.deleteModel(ModelId.serialize(modelId)); + String result = modelsService.deleteModel(ModelId.serialize(modelId)); // Then the repository should have been asked to delete the model verify(mockModelRepository).deleteById(modelId);