diff --git a/ANSWERS.md b/ANSWERS.md index 9cd3f0d..e2e5a2a 100644 --- a/ANSWERS.md +++ b/ANSWERS.md @@ -1,11 +1,29 @@ # Challenge answers ## A - The entities +- I modify pom to add liquibase (https://www.liquibase.org/) dependency and the uml model image (ford-uml.png). ## B - Ingest the data +- I add a property to load a couple of brands, besides of create the need it services to process the DTOs +based on the XML file. +- Add the util class JacksonXmlDataConvert to convert XML file data to DTOs ## C - Expose data with a RESTful API +- Added Jackson annotation to support bidirectional relationships and get the entity as a json object ## D - Adding images +*Option 1: To manage the files in database, first will add a column with type CLOB or BLOB in the SubModel table, +and assign a filename according to a pattern given by the SubModel, like name-line, and limit the type of file extension +(.jpg, .png, .jpeg, etc), and add a few properties for restriction as limit the size of file. -## E - Improvements \ No newline at end of file +*Option 2: Add a column in SubModel table to refer a URL where the file is saved in a storage in cloud +like Amazon Simple Storage Service (Amazon S3). + +## E - Improvements +- Add some validation for value column as Model.Type where the values are constant. +- Add other REST services to have CRUDs of the left entities. +- Add Swagger API for document the REST services. +- Add a few Views as Welcome Page and other to manage information. +- Add log configuration to manage the different types to see in console (DEBUG,TRACE, etc). +- Add profile configuration to handle a Development and Production version for compilation. +- Add SonarQube configuration to ensure code quality. \ No newline at end of file diff --git a/cars/ford-uml.png b/cars/ford-uml.png new file mode 100644 index 0000000..d554ed6 Binary files /dev/null and b/cars/ford-uml.png differ diff --git a/cars/pom.xml b/cars/pom.xml index 43b1e4c..30affe4 100644 --- a/cars/pom.xml +++ b/cars/pom.xml @@ -17,6 +17,7 @@ 1.8 + src/main/resources/db/changelog/db.changelog-master.yaml @@ -49,6 +50,25 @@ spring-boot-starter-test test + + + org.liquibase + liquibase-core + + + org.springframework.boot + spring-boot-devtools + true + + + com.fasterxml.jackson.dataformat + jackson-dataformat-xml + + + org.springframework.boot + spring-boot-configuration-processor + true + @@ -57,6 +77,27 @@ org.springframework.boot spring-boot-maven-plugin + + org.liquibase + liquibase-maven-plugin + 3.6.3 + + ${diffChangeLogFile} + src/main/resources/liquibase.properties + + + + org.liquibase.ext + liquibase-hibernate5 + 3.6 + + + javax.validation + validation-api + 2.0.1.Final + + + diff --git a/cars/src/main/java/com/mooveit/cars/configurations/LoadDataConfiguration.java b/cars/src/main/java/com/mooveit/cars/configurations/LoadDataConfiguration.java new file mode 100644 index 0000000..2b6dfd2 --- /dev/null +++ b/cars/src/main/java/com/mooveit/cars/configurations/LoadDataConfiguration.java @@ -0,0 +1,18 @@ +package com.mooveit.cars.configurations; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@Component +@ConfigurationProperties(prefix="load-data") +public class LoadDataConfiguration { + + private String file; + private String brandName; + +} diff --git a/cars/src/main/java/com/mooveit/cars/controllers/SubModelController.java b/cars/src/main/java/com/mooveit/cars/controllers/SubModelController.java new file mode 100644 index 0000000..c0a1bf1 --- /dev/null +++ b/cars/src/main/java/com/mooveit/cars/controllers/SubModelController.java @@ -0,0 +1,31 @@ +package com.mooveit.cars.controllers; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import com.mooveit.cars.service.SubModelService; + +@RestController +@RequestMapping("/cars") +public class SubModelController { + + private final SubModelService modelService; + + public SubModelController(SubModelService modelService) { + super(); + this.modelService = modelService; + } + + @GetMapping("/by_id") + public Object getCarModelById(@RequestParam(value = "id", defaultValue = "0") Integer id) { + return modelService.getModelById(id); + } + + @GetMapping("/by_brand") + public Object getCarsByBrand (@RequestParam(value = "brandName") String brandName) { + return modelService.getAllModelsByBrandName(brandName); + } + +} diff --git a/cars/src/main/java/com/mooveit/cars/domain/BaseModel.java b/cars/src/main/java/com/mooveit/cars/domain/BaseModel.java new file mode 100644 index 0000000..582282c --- /dev/null +++ b/cars/src/main/java/com/mooveit/cars/domain/BaseModel.java @@ -0,0 +1,41 @@ +package com.mooveit.cars.domain; + +import javax.persistence.Column; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.MappedSuperclass; + +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.NonNull; + +@Data +@NoArgsConstructor +@MappedSuperclass +public class BaseModel { + + @Id + @GeneratedValue(strategy=GenerationType.IDENTITY) + private Integer idModel; + + private @NonNull String name; + + private String fromYear; + + private String toYear; + + @Column(columnDefinition = "boolean default true") + private boolean active; + + @ManyToOne + @JoinColumn (name = "wheel") + private @NonNull Wheel wheel; + + @ManyToOne + @JoinColumn (name = "engine") + private @NonNull Engine engine; + +} diff --git a/cars/src/main/java/com/mooveit/cars/domain/Brand.java b/cars/src/main/java/com/mooveit/cars/domain/Brand.java new file mode 100644 index 0000000..e022647 --- /dev/null +++ b/cars/src/main/java/com/mooveit/cars/domain/Brand.java @@ -0,0 +1,29 @@ +package com.mooveit.cars.domain; + +import java.util.Set; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.OneToMany; + +import com.fasterxml.jackson.annotation.JsonBackReference; + +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.NonNull; + +@Data +@NoArgsConstructor +@Entity +public class Brand { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Integer idBrand; + private @NonNull String name; + @JsonBackReference + @OneToMany + private Set model; +} diff --git a/cars/src/main/java/com/mooveit/cars/domain/Engine.java b/cars/src/main/java/com/mooveit/cars/domain/Engine.java new file mode 100644 index 0000000..f498af4 --- /dev/null +++ b/cars/src/main/java/com/mooveit/cars/domain/Engine.java @@ -0,0 +1,31 @@ +package com.mooveit.cars.domain; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; + +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.NonNull; + +@Data +@NoArgsConstructor +@Entity +public class Engine { + + @Id + @GeneratedValue(strategy=GenerationType.IDENTITY) + private Integer idEngine; + + private @NonNull Integer power; + + private @NonNull String type; + + public Engine(@NonNull Integer power, @NonNull String type) { + super(); + this.power = power; + this.type = type; + } + +} diff --git a/cars/src/main/java/com/mooveit/cars/domain/Model.java b/cars/src/main/java/com/mooveit/cars/domain/Model.java new file mode 100644 index 0000000..6855b51 --- /dev/null +++ b/cars/src/main/java/com/mooveit/cars/domain/Model.java @@ -0,0 +1,32 @@ +package com.mooveit.cars.domain; + +import java.util.Set; + +import javax.persistence.Entity; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.OneToMany; + +import com.fasterxml.jackson.annotation.JsonBackReference; +import com.fasterxml.jackson.annotation.JsonManagedReference; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.NonNull; + +@Data +@NoArgsConstructor +@EqualsAndHashCode(callSuper=false) +@Entity +public class Model extends BaseModel{ + + @JsonManagedReference + @ManyToOne + @JoinColumn (name = "brand") + private @NonNull Brand brand; + private @NonNull String type; + @JsonBackReference + @OneToMany + private Set subModels; +} diff --git a/cars/src/main/java/com/mooveit/cars/domain/SubModel.java b/cars/src/main/java/com/mooveit/cars/domain/SubModel.java new file mode 100644 index 0000000..adb9d77 --- /dev/null +++ b/cars/src/main/java/com/mooveit/cars/domain/SubModel.java @@ -0,0 +1,26 @@ +package com.mooveit.cars.domain; + +import javax.persistence.Entity; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; + +import com.fasterxml.jackson.annotation.JsonManagedReference; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.NonNull; + +@Data +@NoArgsConstructor +@EqualsAndHashCode(callSuper=false) +@Entity +public class SubModel extends BaseModel { + + @JsonManagedReference + @ManyToOne + @JoinColumn (name = "model") + private @NonNull Model model; + + private String line; +} diff --git a/cars/src/main/java/com/mooveit/cars/domain/Wheel.java b/cars/src/main/java/com/mooveit/cars/domain/Wheel.java new file mode 100644 index 0000000..fbee55a --- /dev/null +++ b/cars/src/main/java/com/mooveit/cars/domain/Wheel.java @@ -0,0 +1,31 @@ +package com.mooveit.cars.domain; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; + +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.NonNull; + +@Data +@NoArgsConstructor +@Entity +public class Wheel { + + @Id + @GeneratedValue(strategy=GenerationType.IDENTITY) + private Integer idWheel; + + private @NonNull String size; + + private @NonNull String type; + + public Wheel(@NonNull String size, @NonNull String type) { + super(); + this.size = size; + this.type = type; + } + +} diff --git a/cars/src/main/java/com/mooveit/cars/dto/CatalogueDTO.java b/cars/src/main/java/com/mooveit/cars/dto/CatalogueDTO.java new file mode 100644 index 0000000..98f683a --- /dev/null +++ b/cars/src/main/java/com/mooveit/cars/dto/CatalogueDTO.java @@ -0,0 +1,22 @@ +package com.mooveit.cars.dto; + +import java.util.List; + +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement; + +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + +@Getter +@Setter +@ToString +@JacksonXmlRootElement(localName = "CATALOGUE") +public class CatalogueDTO { + + @JacksonXmlProperty(localName = "MODEL") + @JacksonXmlElementWrapper(useWrapping = false) + private List model; +} diff --git a/cars/src/main/java/com/mooveit/cars/dto/DefaultMessageDTO.java b/cars/src/main/java/com/mooveit/cars/dto/DefaultMessageDTO.java new file mode 100644 index 0000000..9db424f --- /dev/null +++ b/cars/src/main/java/com/mooveit/cars/dto/DefaultMessageDTO.java @@ -0,0 +1,23 @@ +package com.mooveit.cars.dto; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; + +@NoArgsConstructor +@Getter +@Setter +@ToString +public class DefaultMessageDTO { + + private String message; + private boolean error; + + public DefaultMessageDTO(String message, boolean error) { + super(); + this.message = message; + this.error = error; + } + +} diff --git a/cars/src/main/java/com/mooveit/cars/dto/EngineDTO.java b/cars/src/main/java/com/mooveit/cars/dto/EngineDTO.java new file mode 100644 index 0000000..cef32fd --- /dev/null +++ b/cars/src/main/java/com/mooveit/cars/dto/EngineDTO.java @@ -0,0 +1,18 @@ +package com.mooveit.cars.dto; + +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; + +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + +@Getter +@Setter +@ToString +public class EngineDTO { + + @JacksonXmlProperty(isAttribute = true) + private Integer power; + @JacksonXmlProperty(isAttribute = true) + private String type; +} diff --git a/cars/src/main/java/com/mooveit/cars/dto/ModelDTO.java b/cars/src/main/java/com/mooveit/cars/dto/ModelDTO.java new file mode 100644 index 0000000..b33cef4 --- /dev/null +++ b/cars/src/main/java/com/mooveit/cars/dto/ModelDTO.java @@ -0,0 +1,36 @@ +package com.mooveit.cars.dto; + +import java.util.List; + +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; + +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + +@Getter +@Setter +@ToString +public class ModelDTO { + + @JacksonXmlProperty(isAttribute = true) + private String name; + @JacksonXmlProperty(isAttribute = true) + private String from; + @JacksonXmlProperty(isAttribute = true) + private String to; + @JacksonXmlProperty(isAttribute = true) + private String type; + @JacksonXmlProperty(isAttribute = true) + private String line; + @JacksonXmlProperty(localName = "ENGINE") + private EngineDTO engine; + @JacksonXmlProperty(localName = "WHEELS") + private WheelDTO wheel; + + @JacksonXmlProperty(localName = "SUBMODELS") + @JacksonXmlElementWrapper(useWrapping = true) + private List subModels; + +} diff --git a/cars/src/main/java/com/mooveit/cars/dto/SubModelDTO.java b/cars/src/main/java/com/mooveit/cars/dto/SubModelDTO.java new file mode 100644 index 0000000..920a248 --- /dev/null +++ b/cars/src/main/java/com/mooveit/cars/dto/SubModelDTO.java @@ -0,0 +1,29 @@ +package com.mooveit.cars.dto; + +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; + +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + +@Getter +@Setter +@ToString +public class SubModelDTO { + + @JacksonXmlProperty(isAttribute = true) + private String name; + @JacksonXmlProperty(isAttribute = true) + private String from; + @JacksonXmlProperty(isAttribute = true) + private String to; + @JacksonXmlProperty(isAttribute = true) + private String type; + @JacksonXmlProperty(isAttribute = true) + private String line; + @JacksonXmlProperty(localName = "ENGINE") + private EngineDTO engine; + @JacksonXmlProperty(localName = "WHEELS") + private WheelDTO wheel; + +} diff --git a/cars/src/main/java/com/mooveit/cars/dto/WheelDTO.java b/cars/src/main/java/com/mooveit/cars/dto/WheelDTO.java new file mode 100644 index 0000000..10c0576 --- /dev/null +++ b/cars/src/main/java/com/mooveit/cars/dto/WheelDTO.java @@ -0,0 +1,19 @@ +package com.mooveit.cars.dto; + +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; + +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + +@Getter +@Setter +@ToString +public class WheelDTO { + + @JacksonXmlProperty(isAttribute = true) + private String size; + @JacksonXmlProperty(isAttribute = true) + private String type; + +} diff --git a/cars/src/main/java/com/mooveit/cars/repositories/BrandRepository.java b/cars/src/main/java/com/mooveit/cars/repositories/BrandRepository.java new file mode 100644 index 0000000..78c9db4 --- /dev/null +++ b/cars/src/main/java/com/mooveit/cars/repositories/BrandRepository.java @@ -0,0 +1,13 @@ +package com.mooveit.cars.repositories; + +import java.util.List; + +import org.springframework.data.jpa.repository.JpaRepository; + +import com.mooveit.cars.domain.Brand; + +public interface BrandRepository extends JpaRepository { + + List findByName(String name); + +} diff --git a/cars/src/main/java/com/mooveit/cars/repositories/EngineRepository.java b/cars/src/main/java/com/mooveit/cars/repositories/EngineRepository.java new file mode 100644 index 0000000..eaa0bb9 --- /dev/null +++ b/cars/src/main/java/com/mooveit/cars/repositories/EngineRepository.java @@ -0,0 +1,9 @@ +package com.mooveit.cars.repositories; + +import org.springframework.data.jpa.repository.JpaRepository; + +import com.mooveit.cars.domain.Engine; + +public interface EngineRepository extends JpaRepository { + +} diff --git a/cars/src/main/java/com/mooveit/cars/repositories/ModelRepository.java b/cars/src/main/java/com/mooveit/cars/repositories/ModelRepository.java new file mode 100644 index 0000000..61377c1 --- /dev/null +++ b/cars/src/main/java/com/mooveit/cars/repositories/ModelRepository.java @@ -0,0 +1,9 @@ +package com.mooveit.cars.repositories; + +import org.springframework.data.jpa.repository.JpaRepository; + +import com.mooveit.cars.domain.Model; + +public interface ModelRepository extends JpaRepository { + +} diff --git a/cars/src/main/java/com/mooveit/cars/repositories/SubModelRepository.java b/cars/src/main/java/com/mooveit/cars/repositories/SubModelRepository.java new file mode 100644 index 0000000..49f678d --- /dev/null +++ b/cars/src/main/java/com/mooveit/cars/repositories/SubModelRepository.java @@ -0,0 +1,13 @@ +package com.mooveit.cars.repositories; + +import java.util.List; + +import org.springframework.data.jpa.repository.JpaRepository; + +import com.mooveit.cars.domain.Brand; +import com.mooveit.cars.domain.SubModel; + +public interface SubModelRepository extends JpaRepository { + + List findAllByModelBrand (Brand brand); +} diff --git a/cars/src/main/java/com/mooveit/cars/repositories/WheelRepository.java b/cars/src/main/java/com/mooveit/cars/repositories/WheelRepository.java new file mode 100644 index 0000000..141bde4 --- /dev/null +++ b/cars/src/main/java/com/mooveit/cars/repositories/WheelRepository.java @@ -0,0 +1,9 @@ +package com.mooveit.cars.repositories; + +import org.springframework.data.jpa.repository.JpaRepository; + +import com.mooveit.cars.domain.Wheel; + +public interface WheelRepository extends JpaRepository { + +} diff --git a/cars/src/main/java/com/mooveit/cars/service/BrandService.java b/cars/src/main/java/com/mooveit/cars/service/BrandService.java new file mode 100644 index 0000000..89b07fd --- /dev/null +++ b/cars/src/main/java/com/mooveit/cars/service/BrandService.java @@ -0,0 +1,21 @@ +package com.mooveit.cars.service; + +import org.springframework.stereotype.Service; + +import com.mooveit.cars.domain.Brand; +import com.mooveit.cars.repositories.BrandRepository; + +@Service +public class BrandService { + + private final BrandRepository brandRepo; + + public BrandService(BrandRepository brandRepo) { + super(); + this.brandRepo = brandRepo; + } + + public Brand findOneBrandByName (String brandName) { + return brandRepo.findByName(brandName).stream().findFirst().orElse(new Brand()); + } +} diff --git a/cars/src/main/java/com/mooveit/cars/service/EngineService.java b/cars/src/main/java/com/mooveit/cars/service/EngineService.java new file mode 100644 index 0000000..5fba663 --- /dev/null +++ b/cars/src/main/java/com/mooveit/cars/service/EngineService.java @@ -0,0 +1,35 @@ +package com.mooveit.cars.service; + +import java.util.Optional; + +import org.springframework.data.domain.Example; +import org.springframework.stereotype.Service; + +import com.mooveit.cars.domain.Engine; +import com.mooveit.cars.dto.EngineDTO; +import com.mooveit.cars.repositories.EngineRepository; + +@Service +public class EngineService { + + private Engine engine; + private final EngineRepository engineRepo; + + public EngineService(EngineRepository engineRepo) { + super(); + this.engineRepo = engineRepo; + } + + /** + * Method to get data and check for update/insert + * @param dto {@link EngineDTO} + * @return {@link Engine} the found entity + */ + public Engine processEngineEntityFromDto (EngineDTO dto) { + engine = new Engine(dto.getPower(), dto.getType()); + Optional opEngine = engineRepo.findOne(Example.of(engine)); + if(!opEngine.isPresent()) engine = engineRepo.saveAndFlush(engine); + else engine = opEngine.get(); + return engine; + } +} diff --git a/cars/src/main/java/com/mooveit/cars/service/LoadGeneralDataService.java b/cars/src/main/java/com/mooveit/cars/service/LoadGeneralDataService.java new file mode 100644 index 0000000..0fba56d --- /dev/null +++ b/cars/src/main/java/com/mooveit/cars/service/LoadGeneralDataService.java @@ -0,0 +1,48 @@ +package com.mooveit.cars.service; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import com.mooveit.cars.domain.Brand; +import com.mooveit.cars.domain.Model; +import com.mooveit.cars.dto.CatalogueDTO; +import com.mooveit.cars.dto.ModelDTO; + +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@Service +public class LoadGeneralDataService { + + private Model model; + + @Autowired + private BrandService brandService; + @Autowired + private ModelService modelService; + @Autowired + private SubModelService subModelService; + + /** + * Method to process {@link CatalogueDTO} object mapped from XML File + * by a given brand + * @param catDTO {@link CatalogueDTO} + * @param brandName {@link String} from application file + */ + public void loadDataFromCatalogueDTO (CatalogueDTO catDTO, String brandName) { + //Get the entity of the brand + log.info("Searching entity of the brand {}",brandName); + Brand brand = brandService.findOneBrandByName(brandName); + //Let's insert the Models + for(ModelDTO modelDto: catDTO.getModel()) { + log.info("processing model {} ",modelDto.getName()); + model = modelService.processModelEntityFromDto(modelDto, brand); + //Now check if has SubModel objects + modelDto.getSubModels().forEach(obModel -> { + log.info("processing subModel {}",obModel.getName()); + subModelService.processSubModelEntityFromDto(obModel, model); + }); + } + log.info("loadData finish..."); + } +} diff --git a/cars/src/main/java/com/mooveit/cars/service/ModelService.java b/cars/src/main/java/com/mooveit/cars/service/ModelService.java new file mode 100644 index 0000000..a19ba95 --- /dev/null +++ b/cars/src/main/java/com/mooveit/cars/service/ModelService.java @@ -0,0 +1,52 @@ +package com.mooveit.cars.service; + +import java.util.Optional; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Example; +import org.springframework.stereotype.Service; + +import com.mooveit.cars.domain.Brand; +import com.mooveit.cars.domain.Model; +import com.mooveit.cars.dto.ModelDTO; +import com.mooveit.cars.repositories.ModelRepository; + +@Service +public class ModelService { + + private final ModelRepository modelRepo; + private Model model; + + @Autowired + private EngineService engineService; + + @Autowired + private WheelService wheelService; + + public ModelService(ModelRepository modelRepo) { + super(); + this.modelRepo = modelRepo; + } + + /** + * Method to get data and check for update/insert + * @param dto {@link ModelDTO} + * @param brand + * @return {@link Model} the found entity + */ + public Model processModelEntityFromDto (ModelDTO dto, Brand brand) { + model = new Model(); + model.setFromYear(dto.getFrom()); + model.setToYear(dto.getTo()); + model.setType(dto.getType()); + model.setName(dto.getName()); + Optional opModel = modelRepo.findOne(Example.of(model)); + if (!opModel.isPresent()) { + model.setBrand(brand); + model.setEngine(engineService.processEngineEntityFromDto(dto.getEngine())); + model.setWheel(wheelService.processWheelEntityFromDto(dto.getWheel())); + model = modelRepo.saveAndFlush(model); + } else model = opModel.get(); + return model; + } +} diff --git a/cars/src/main/java/com/mooveit/cars/service/SubModelService.java b/cars/src/main/java/com/mooveit/cars/service/SubModelService.java new file mode 100644 index 0000000..102ed42 --- /dev/null +++ b/cars/src/main/java/com/mooveit/cars/service/SubModelService.java @@ -0,0 +1,93 @@ +package com.mooveit.cars.service; + +import java.util.List; +import java.util.Optional; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Example; +import org.springframework.stereotype.Service; + +import com.mooveit.cars.domain.Brand; +import com.mooveit.cars.domain.Model; +import com.mooveit.cars.domain.SubModel; +import com.mooveit.cars.dto.DefaultMessageDTO; +import com.mooveit.cars.dto.SubModelDTO; +import com.mooveit.cars.repositories.SubModelRepository; + +@Service +public class SubModelService { + + private SubModel subModel; + private DefaultMessageDTO dmDTO; + private Optional opSubModel; + private final SubModelRepository subModelRepo; + + @Autowired + private EngineService engineService; + @Autowired + private WheelService wheelService; + @Autowired + private BrandService brandService; + + public SubModelService(SubModelRepository subModelRepo) { + super(); + this.subModelRepo = subModelRepo; + } + + /** + * Method to get data and check for update/insert + * @param dto {@link SubModelDTO} + * @param model {@link Model} + * @return {@link SubModel} + */ + public SubModel processSubModelEntityFromDto (SubModelDTO dto, Model model) { + subModel = new SubModel(); + subModel.setLine(dto.getLine()); + subModel.setName(dto.getName()); + opSubModel = subModelRepo.findOne(Example.of(subModel)); + if(!opSubModel.isPresent()) { + subModel.setModel(model); + subModel.setFromYear(dto.getFrom()); + subModel.setToYear(dto.getTo()); + subModel.setEngine(engineService.processEngineEntityFromDto(dto.getEngine())); + subModel.setWheel(wheelService.processWheelEntityFromDto(dto.getWheel())); + subModel = subModelRepo.saveAndFlush(subModel); + } else subModel = opSubModel.get(); + return subModel; + } + + /** + * Method to consult a car model by id + * @param id + * @return {@link DefaultMessageDTO} if does not exit or the entity founded + */ + public Object getModelById (Integer id) { + opSubModel = subModelRepo.findById(id); + if (opSubModel.isPresent()) { + return opSubModel.get(); + } else { + dmDTO = new DefaultMessageDTO("The car with id "+id+" does not exist",false); + return dmDTO; + } + } + + /** + * Method to get all the cars by the brand name + * @param brandName + * @return {@link DefaultMessageDTO} if does not exit or the list entity + */ + public Object getAllModelsByBrandName (String brandName) { + Brand brand = brandService.findOneBrandByName(brandName); + if (brand.getIdBrand() != null) { + List listModel = subModelRepo.findAllByModelBrand(brand); + if (listModel.isEmpty()) { + dmDTO = new DefaultMessageDTO("The cars for the brand "+brandName+" does not exist",false); + return dmDTO; + } + return listModel; + } else { + dmDTO = new DefaultMessageDTO("The brand "+brandName+" was not identify",false); + return dmDTO; + } + } +} diff --git a/cars/src/main/java/com/mooveit/cars/service/WheelService.java b/cars/src/main/java/com/mooveit/cars/service/WheelService.java new file mode 100644 index 0000000..2a2d8b4 --- /dev/null +++ b/cars/src/main/java/com/mooveit/cars/service/WheelService.java @@ -0,0 +1,35 @@ +package com.mooveit.cars.service; + +import java.util.Optional; + +import org.springframework.data.domain.Example; +import org.springframework.stereotype.Service; + +import com.mooveit.cars.domain.Wheel; +import com.mooveit.cars.dto.WheelDTO; +import com.mooveit.cars.repositories.WheelRepository; + +@Service +public class WheelService { + + private Wheel wheel; + private final WheelRepository wheelRepo; + + public WheelService(WheelRepository wheelRepo) { + super(); + this.wheelRepo = wheelRepo; + } + + /** + * Method to get data and check for update/insert + * @param dto {@link WheelDTO} + * @return {@link Wheel} the found entity + */ + public Wheel processWheelEntityFromDto(WheelDTO dto) { + wheel = new Wheel(dto.getSize(), dto.getType()); + Optional opWheel = wheelRepo.findOne(Example.of(wheel)); + if(!opWheel.isPresent()) wheel = wheelRepo.saveAndFlush(wheel); + else wheel = opWheel.get(); + return wheel; + } +} diff --git a/cars/src/main/java/com/mooveit/cars/tasks/FordIngesterTask.java b/cars/src/main/java/com/mooveit/cars/tasks/FordIngesterTask.java index a04f791..f9e9fac 100644 --- a/cars/src/main/java/com/mooveit/cars/tasks/FordIngesterTask.java +++ b/cars/src/main/java/com/mooveit/cars/tasks/FordIngesterTask.java @@ -1,15 +1,36 @@ package com.mooveit.cars.tasks; -import lombok.extern.slf4j.Slf4j; +import java.io.IOException; + +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; +import com.mooveit.cars.configurations.LoadDataConfiguration; +import com.mooveit.cars.service.LoadGeneralDataService; +import com.mooveit.cars.utils.JacksonXmlDataConvert; + +import lombok.extern.slf4j.Slf4j; + @Slf4j @Service public class FordIngesterTask { - @Scheduled(cron = "${cars.ford.ingester.runCron}") - public void ingestFile() { - log.warn("Not implemented yet."); - } + @Autowired + private LoadGeneralDataService loadGDService; + + @Autowired + private LoadDataConfiguration ldConfig; + + @Scheduled(cron = "${cars.ford.ingester.runCron}") + public void ingestFile() { + log.debug("Starting process ingestFile..."); + try { + loadGDService.loadDataFromCatalogueDTO( + JacksonXmlDataConvert.getInstance().getFordDataXml(ldConfig.getFile()), ldConfig.getBrandName()); + } catch (IOException e) { + log.error("Error reading file ->", e); + } + log.debug("Task ingestFile finish"); + } } diff --git a/cars/src/main/java/com/mooveit/cars/utils/JacksonXmlDataConvert.java b/cars/src/main/java/com/mooveit/cars/utils/JacksonXmlDataConvert.java new file mode 100644 index 0000000..a11c507 --- /dev/null +++ b/cars/src/main/java/com/mooveit/cars/utils/JacksonXmlDataConvert.java @@ -0,0 +1,45 @@ +package com.mooveit.cars.utils; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; + +import com.fasterxml.jackson.dataformat.xml.XmlMapper; +import com.mooveit.cars.dto.CatalogueDTO; + +public class JacksonXmlDataConvert { + + public static JacksonXmlDataConvert getInstance() { + return new JacksonXmlDataConvert(); + } + /** + * Method to process xml data into a CatalogueDTO + * with Jackson lib + * @param url XmlFile directory + * @return {@link CatalogueDTO} + * @throws FileNotFoundException + * @throws IOException + */ + public CatalogueDTO getFordDataXml (String url) throws FileNotFoundException, IOException { + File file = new File(url); + XmlMapper xmlMapper = new XmlMapper(); + String xml = inputStreamToString(new FileInputStream(file)); + CatalogueDTO catalogueData = xmlMapper.readValue(xml, CatalogueDTO.class); + return catalogueData; + } + + public String inputStreamToString(InputStream is) throws IOException { + StringBuilder sb = new StringBuilder(); + String line; + BufferedReader br = new BufferedReader(new InputStreamReader(is)); + while ((line = br.readLine()) != null) { + sb.append(line); + } + br.close(); + return sb.toString(); + } +} diff --git a/cars/src/main/resources/application.yml b/cars/src/main/resources/application.yml index 436f591..9865e49 100644 --- a/cars/src/main/resources/application.yml +++ b/cars/src/main/resources/application.yml @@ -12,3 +12,9 @@ spring: jpa: database-platform: 'org.hibernate.dialect.H2Dialect' h2.console.enabled: true + +load-data: + file: + src/main/resources/ford-example.xml + brandName: + ford diff --git a/cars/src/main/resources/csv/brands.csv b/cars/src/main/resources/csv/brands.csv new file mode 100644 index 0000000..9a1809d --- /dev/null +++ b/cars/src/main/resources/csv/brands.csv @@ -0,0 +1,3 @@ +name +ford +chevrolet diff --git a/cars/src/main/resources/db/changelog/changes/cars1-test.yaml b/cars/src/main/resources/db/changelog/changes/cars1-test.yaml new file mode 100644 index 0000000..b6509dc --- /dev/null +++ b/cars/src/main/resources/db/changelog/changes/cars1-test.yaml @@ -0,0 +1,190 @@ +databaseChangeLog: +- changeSet: + id: 1-1 + author: mitzyvo + changes: + - createTable: + columns: + - column: + constraints: + primaryKey: true + primaryKeyName: brand_id_pk + autoIncrement: true + name: id_brand + type: INT + - column: + constraints: + nullable: false + name: name + type: VARCHAR(50) + tableName: brand + - createTable: + columns: + - column: + autoIncrement: true + constraints: + primaryKey: true + primaryKeyName: engine_id_pk + name: id_engine + type: INT + - column: + constraints: + nullable: false + name: power + type: INT + - column: + constraints: + nullable: false + name: type + type: VARCHAR(50) + tableName: engine + - createTable: + columns: + - column: + autoIncrement: true + constraints: + primaryKey: true + primaryKeyName: wheel_id_pk + name: id_wheel + type: INT + - column: + constraints: + nullable: false + name: size + type: VARCHAR(10) + - column: + constraints: + nullable: false + name: type + type: VARCHAR(50) + tableName: wheel + - createTable: + columns: + - column: + autoIncrement: true + constraints: + primaryKey: true + primaryKeyName: model_id_pk + name: id_model + type: INT + - column: + constraints: + nullable: false + name: name + type: VARCHAR(50) + - column: + name: type + type: VARCHAR(50) + - column: + constraints: + nullable: false + name: from_year + type: VARCHAR(4) + - column: + name: to_year + type: VARCHAR(4) + - column: + name: active + type: BOOLEAN + defaultValueBoolean: true + - column: + constraints: + nullable: false + foreignKeyName: fk_model_brand + referencedTableName: brand + referencedColumnNames: id_brand + validateForeignKey: true + name: brand + type: INT + - column: + constraints: + nullable: false + foreignKeyName: fk_model_engine + referencedTableName: engine + referencedColumnNames: id_engine + validateForeignKey: true + name: engine + type: INT + - column: + constraints: + nullable: false + foreignKeyName: fk_model_wheel + referencedTableName: wheel + referencedColumnNames: id_wheel + validateForeignKey: true + name: wheel + type: INT + tableName: model + - createTable: + columns: + - column: + autoIncrement: true + constraints: + primaryKey: true + primaryKeyName: sub_model_id_pk + name: id_model + type: INT + - column: + constraints: + nullable: false + name: name + type: VARCHAR(50) + - column: + name: line + type: VARCHAR(50) + - column: + name: from_year + type: VARCHAR(4) + - column: + name: to_year + type: VARCHAR(4) + - column: + name: active + type: BOOLEAN + defaultValueBoolean: true + - column: + constraints: + nullable: false + foreignKeyName: fk_sub_model_model + referencedTableName: model + referencedColumnNames: id_model + validateForeignKey: true + name: model + type: INT + - column: + constraints: + nullable: false + foreignKeyName: fk_sub_model_engine + referencedTableName: engine + referencedColumnNames: id_engine + validateForeignKey: true + name: engine + type: INT + - column: + constraints: + nullable: false + foreignKeyName: fk_sub_model_wheel + referencedTableName: wheel + referencedColumnNames: id_wheel + validateForeignKey: true + name: wheel + type: INT + tableName: sub_model +- changeSet: + id: 1-2 + author: mitzyvo + changes: + - loadData: + columns: + - column: + name: name + type: STRING + commentLineStartsWith: A String + encoding: UTF-8 + file: csv/brands.csv + quotchar: '''' + relativeToChangelogFile: false + separator: ',' + tableName: brand + usePreparedStatements: true + \ No newline at end of file diff --git a/cars/src/main/resources/db/changelog/db.changelog-master.yaml b/cars/src/main/resources/db/changelog/db.changelog-master.yaml new file mode 100644 index 0000000..83d4224 --- /dev/null +++ b/cars/src/main/resources/db/changelog/db.changelog-master.yaml @@ -0,0 +1,4 @@ +databaseChangeLog: + - include: + file: db/changelog/changes/cars1-test.yaml + \ No newline at end of file diff --git a/cars/src/main/resources/liquibase.properties b/cars/src/main/resources/liquibase.properties new file mode 100644 index 0000000..552d6d0 --- /dev/null +++ b/cars/src/main/resources/liquibase.properties @@ -0,0 +1,6 @@ +changeLogFile=classpath:db/changelog/db.changelog-master.yaml +url=jdbc:h2:mem:carsdb;DB_CLOSE_ON_EXIT=FALSE +username=sa +password= +driver=org.h2.Driver +referenceUrl=hibernate:spring:com.mooveit.cars.domain?dialect=org.hibernate.dialect.H2Dialect&hibernate.physical_naming_strategy=org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy&hibernate.implicit_naming_strategy=org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy \ No newline at end of file diff --git a/cars/src/test/java/com/mooveit/cars/controllers/SubModelControllerTest.java b/cars/src/test/java/com/mooveit/cars/controllers/SubModelControllerTest.java new file mode 100644 index 0000000..b93d5c3 --- /dev/null +++ b/cars/src/test/java/com/mooveit/cars/controllers/SubModelControllerTest.java @@ -0,0 +1,66 @@ +package com.mooveit.cars.controllers; + +import static org.junit.Assert.fail; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.MockitoAnnotations; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.MediaType; +import org.springframework.http.converter.HttpMessageConverter; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; + +import com.mooveit.cars.service.SubModelService; + +@RunWith(SpringRunner.class) +@SpringBootTest +public class SubModelControllerTest { + + private MockMvc restMvc; + + @Autowired + private HttpMessageConverter[] httpMessageConverters; + + @Autowired + private SubModelService modelService; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + SubModelController modelController = new SubModelController(modelService); + this.restMvc = MockMvcBuilders.standaloneSetup(modelController).setMessageConverters(httpMessageConverters) + .build(); + } + + @Test + public void testGetCarModelById() { + try { + restMvc.perform(get("/cars/by_id?id=1") + .accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andDo(print()); + } catch (Exception e) { + fail("Something go wrong calling the service "); + } + } + + @Test + public void testGetCarsByBrand() { + try { + restMvc.perform(get("/cars/by_brand?brandName=ford") + .accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andDo(print()); + } catch (Exception e) { + fail("Something go wrong calling the service "); + } + } + +} diff --git a/cars/src/test/java/com/mooveit/cars/service/EngineServiceTest.java b/cars/src/test/java/com/mooveit/cars/service/EngineServiceTest.java new file mode 100644 index 0000000..61adab2 --- /dev/null +++ b/cars/src/test/java/com/mooveit/cars/service/EngineServiceTest.java @@ -0,0 +1,32 @@ +package com.mooveit.cars.service; + +import static org.junit.Assert.*; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +import com.mooveit.cars.domain.Engine; +import com.mooveit.cars.dto.EngineDTO; + +@RunWith(SpringRunner.class) +@SpringBootTest +public class EngineServiceTest { + + @Autowired + private EngineService engineService; + private EngineDTO engineDTO; + + @Test + public void testProcessEngineEntityFromDto() { + engineDTO = new EngineDTO(); + engineDTO.setPower(1000); + engineDTO.setType("GAS"); + Engine engine = engineService.processEngineEntityFromDto(engineDTO); + assertNotNull(engine); + assertEquals(engineDTO.getType(), engine.getType()); + } + +} diff --git a/cars/src/test/java/com/mooveit/cars/service/SubModelServiceTest.java b/cars/src/test/java/com/mooveit/cars/service/SubModelServiceTest.java new file mode 100644 index 0000000..0c30131 --- /dev/null +++ b/cars/src/test/java/com/mooveit/cars/service/SubModelServiceTest.java @@ -0,0 +1,33 @@ +package com.mooveit.cars.service; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@SpringBootTest +public class SubModelServiceTest { + + @Autowired + private SubModelService modelService; + + @Test + public void testGetModelById() { + Object response = modelService.getModelById(10000); + assertNotNull(response); + assertTrue(response.getClass().getName().equals("com.mooveit.cars.dto.DefaultMessageDTO")); + } + + @Test + public void testGetAllModelsByBrandName() { + Object listModel = modelService.getAllModelsByBrandName("kawasaki"); + assertNotNull(listModel); + assertTrue(listModel.getClass().getName().equals("com.mooveit.cars.dto.DefaultMessageDTO")); + } + +} diff --git a/cars/src/test/java/com/mooveit/cars/utils/JacksonXmlDataConvertTest.java b/cars/src/test/java/com/mooveit/cars/utils/JacksonXmlDataConvertTest.java new file mode 100644 index 0000000..461151d --- /dev/null +++ b/cars/src/test/java/com/mooveit/cars/utils/JacksonXmlDataConvertTest.java @@ -0,0 +1,34 @@ +package com.mooveit.cars.utils; + +import static org.junit.Assert.*; + +import java.io.FileNotFoundException; +import java.io.IOException; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +import com.mooveit.cars.dto.CatalogueDTO; + +@RunWith(SpringRunner.class) +@SpringBootTest +public class JacksonXmlDataConvertTest { + + @Test + public void testGetFordDataXml() { + JacksonXmlDataConvert mapper = new JacksonXmlDataConvert(); + try { + String url = "src/test/resources/ford-example.xml"; + CatalogueDTO result = mapper.getFordDataXml(url); + assertNotNull(result); + assertTrue(result.getModel().get(0).getName().equals("Aspire")); + } catch (FileNotFoundException e) { + fail("Something go wrong with file location..."); + } catch (IOException e) { + fail("Something go wrong while reading the file..."); + } + } + +} diff --git a/cars/src/test/resources/ford-example.xml b/cars/src/test/resources/ford-example.xml new file mode 100644 index 0000000..c852dae --- /dev/null +++ b/cars/src/test/resources/ford-example.xml @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file