diff --git a/.gitignore b/.gitignore index d81f12e..9d5c76c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,11 @@ -/target -/.idea +.classpath +.project +.settings +.idea +*.iws +*.iml +*.ipr +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..4229483 --- /dev/null +++ b/README.md @@ -0,0 +1,40 @@ +Cake Manager Micro Service (fictitious) +======================================= + +Cake Manager App is a Java SpringBoot application. + +mvn clean install
+mvn spring-boot:run + + +### How to Test +``` +git clone https://github.com/s4sushil/cake-manager.git +cd cake-manager +mvn spring-boot:run +``` + +## Access URL +http://localhost:8181/ + + +### REST End points +To get the cakes - +``` +* GET http://localhost:8181/ +``` +To post the cakes - +``` +curl -X POST "http://localhost:8181/cakes" -H "accept: */*" -H "Content-Type: application/json" -d "{ \"description\": \"ME\", \"imageUrl\": \"URL\", \"name\": \"Sushil\"}" +``` + +### Welcome Page +![Flow](images/homePage.jpg) + + +### Add Cake By Normal User Page +![Flow](images/addCakeForm.jpg) + + +### Swagger Api docs and Rest endpoints - Adding Cakes by rest Json format +![Flow](images/swaggerHomePage.jpg) \ No newline at end of file diff --git a/README.txt b/README.txt deleted file mode 100644 index 3a66e0d..0000000 --- a/README.txt +++ /dev/null @@ -1,57 +0,0 @@ -Cake Manager Micro Service (fictitious) -======================================= - -A summer intern started on this project but never managed to get it finished. -The developer assured us that some of the above is complete, but at the moment accessing the /cakes endpoint -returns a 404, so getting this working should be the first priority. - -Requirements: -* By accessing the root of the server (/) it should be possible to list the cakes currently in the system. This must be presented in an acceptable format for a human to read. - -* It must be possible for a human to add a new cake to the server. - -* By accessing an alternative endpoint (/cakes) with an appropriate client it must be possible to download a list of -the cakes currently in the system as JSON data. - -* The /cakes endpoint must also allow new cakes to be created. - -Comments: -* We feel like the software stack used by the original developer is quite outdated, it would be good to migrate the entire application to something more modern. -* Would be good to change the application to implement proper client-server separation via REST API. - -Bonus points: -* Tests -* Authentication via OAuth2 -* Continuous Integration via any cloud CI system -* Containerisation - - -Original Project Info -===================== - -To run a server locally execute the following command: - -`mvn jetty:run` - -and access the following URL: - -`http://localhost:8282/` - -Feel free to change how the project is run, but clear instructions must be given in README -You can use any IDE you like, so long as the project can build and run with Maven or Gradle. - -The project loads some pre-defined data in to an in-memory database, which is acceptable for this exercise. There is -no need to create persistent storage. - - -Submission -========== - -Please provide your version of this project as a git repository (e.g. Github, BitBucket, etc). - -Alternatively, you can submit the project as a zip or gzip. Use Google Drive or some other file sharing service to -share it with us. - -Please also keep a log of the changes you make as a text file and provide this to us with your submission. - -Good luck! diff --git a/images/addCakeForm.jpg b/images/addCakeForm.jpg new file mode 100644 index 0000000..80934c2 Binary files /dev/null and b/images/addCakeForm.jpg differ diff --git a/images/homePage.jpg b/images/homePage.jpg new file mode 100644 index 0000000..de1bffe Binary files /dev/null and b/images/homePage.jpg differ diff --git a/images/swaggerHomePage.jpg b/images/swaggerHomePage.jpg new file mode 100644 index 0000000..84d5802 Binary files /dev/null and b/images/swaggerHomePage.jpg differ diff --git a/pom.xml b/pom.xml index c8cbf9d..aa207ca 100644 --- a/pom.xml +++ b/pom.xml @@ -1,77 +1,116 @@ - 4.0.0 - com.waracle - cake-manager - war - 1.0-SNAPSHOT - cake-manager Maven Webapp - http://maven.apache.org - + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + 4.0.0 + com.waracle + cake-manager + 1.0 + cake-manager + http://maven.apache.org - - - javax.servlet - javax.servlet-api - 3.1.0 - + + org.springframework.boot + spring-boot-starter-parent + 2.4.0 + + - - - com.fasterxml.jackson.core - jackson-core - 2.8.0 - + + 11 + - - - org.hibernate - hibernate-entitymanager - 4.3.6.Final - + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-data-jpa + + + org.springframework.boot + spring-boot-starter-actuator + + + org.springframework.boot + spring-boot-starter-thymeleaf + - - - org.hsqldb - hsqldb - 2.3.4 - + + org.hsqldb + hsqldb + 2.5.1 + - - - junit - junit - 4.1 - test - + + io.springfox + springfox-boot-starter + 3.0.0 + - - - cake-manager - + + org.springframework.boot + spring-boot-starter-validation + + + org.springframework.boot + spring-boot-starter-test + test + + + junit + junit + 4.12 + test + - - maven-compiler-plugin - 2.3.2 - - 1.8 - 1.8 - - + + org.projectlombok + lombok + 1.18.20 + + + org.modelmapper + modelmapper + 2.3.7 + - - org.eclipse.jetty - jetty-maven-plugin - - 10 - STOP - 8005 - - 8282 - - - + - - - + + spring-boot:run + + + + maven-compiler-plugin + 3.8.0 + + + maven-surefire-plugin + 2.22.1 + + + maven-jar-plugin + 3.0.2 + + + maven-install-plugin + 2.5.2 + + + maven-deploy-plugin + 2.8.2 + + + maven-project-info-reports-plugin + 3.0.0 + + + org.springframework.boot + spring-boot-maven-plugin + + + + + + \ No newline at end of file diff --git a/src/main/java/com.waracle.cakemgr/CakeEntity.java b/src/main/java/com.waracle.cakemgr/CakeEntity.java deleted file mode 100644 index 7927bd5..0000000 --- a/src/main/java/com.waracle.cakemgr/CakeEntity.java +++ /dev/null @@ -1,52 +0,0 @@ -package com.waracle.cakemgr; - -import java.io.Serializable; - -import javax.persistence.*; - -@Entity -@org.hibernate.annotations.Entity(dynamicUpdate = true) -@Table(name = "Employee", uniqueConstraints = {@UniqueConstraint(columnNames = "ID"), @UniqueConstraint(columnNames = "EMAIL")}) -public class CakeEntity implements Serializable { - - private static final long serialVersionUID = -1798070786993154676L; - - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - @Column(name = "ID", unique = true, nullable = false) - private Integer employeeId; - - @Column(name = "EMAIL", unique = true, nullable = false, length = 100) - private String title; - - @Column(name = "FIRST_NAME", unique = false, nullable = false, length = 100) - private String description; - - @Column(name = "LAST_NAME", unique = false, nullable = false, length = 300) - private String image; - - public String getTitle() { - return title; - } - - public void setTitle(String title) { - this.title = title; - } - - public String getDescription() { - return description; - } - - public void setDescription(String description) { - this.description = description; - } - - public String getImage() { - return image; - } - - public void setImage(String image) { - this.image = image; - } - -} \ No newline at end of file diff --git a/src/main/java/com.waracle.cakemgr/CakeServlet.java b/src/main/java/com.waracle.cakemgr/CakeServlet.java deleted file mode 100644 index 9bd32f7..0000000 --- a/src/main/java/com.waracle.cakemgr/CakeServlet.java +++ /dev/null @@ -1,106 +0,0 @@ -package com.waracle.cakemgr; - -import com.fasterxml.jackson.core.JsonFactory; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonToken; -import org.hibernate.Session; -import org.hibernate.exception.ConstraintViolationException; - -import javax.servlet.ServletException; -import javax.servlet.annotation.WebServlet; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.*; -import java.net.URL; -import java.util.List; - -@WebServlet("/cakes") -public class CakeServlet extends HttpServlet { - - @Override - public void init() throws ServletException { - super.init(); - - System.out.println("init started"); - - - System.out.println("downloading cake json"); - try (InputStream inputStream = new URL("https://gist.githubusercontent.com/hart88/198f29ec5114a3ec3460/raw/8dd19a88f9b8d24c23d9960f3300d0c917a4f07c/cake.json").openStream()) { - BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); - - StringBuffer buffer = new StringBuffer(); - String line = reader.readLine(); - while (line != null) { - buffer.append(line); - line = reader.readLine(); - } - - System.out.println("parsing cake json"); - JsonParser parser = new JsonFactory().createParser(buffer.toString()); - if (JsonToken.START_ARRAY != parser.nextToken()) { - throw new Exception("bad token"); - } - - JsonToken nextToken = parser.nextToken(); - while(nextToken == JsonToken.START_OBJECT) { - System.out.println("creating cake entity"); - - CakeEntity cakeEntity = new CakeEntity(); - System.out.println(parser.nextFieldName()); - cakeEntity.setTitle(parser.nextTextValue()); - - System.out.println(parser.nextFieldName()); - cakeEntity.setDescription(parser.nextTextValue()); - - System.out.println(parser.nextFieldName()); - cakeEntity.setImage(parser.nextTextValue()); - - Session session = HibernateUtil.getSessionFactory().openSession(); - try { - session.beginTransaction(); - session.persist(cakeEntity); - System.out.println("adding cake entity"); - session.getTransaction().commit(); - } catch (ConstraintViolationException ex) { - - } - session.close(); - - nextToken = parser.nextToken(); - System.out.println(nextToken); - - nextToken = parser.nextToken(); - System.out.println(nextToken); - } - - } catch (Exception ex) { - throw new ServletException(ex); - } - - System.out.println("init finished"); - } - - @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - - Session session = HibernateUtil.getSessionFactory().openSession(); - List list = session.createCriteria(CakeEntity.class).list(); - - resp.getWriter().println("["); - - for (CakeEntity entity : list) { - resp.getWriter().println("\t{"); - - resp.getWriter().println("\t\t\"title\" : " + entity.getTitle() + ", "); - resp.getWriter().println("\t\t\"desc\" : " + entity.getDescription() + ","); - resp.getWriter().println("\t\t\"image\" : " + entity.getImage()); - - resp.getWriter().println("\t}"); - } - - resp.getWriter().println("]"); - - } - -} diff --git a/src/main/java/com.waracle.cakemgr/HibernateUtil.java b/src/main/java/com.waracle.cakemgr/HibernateUtil.java deleted file mode 100644 index 41ef137..0000000 --- a/src/main/java/com.waracle.cakemgr/HibernateUtil.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.waracle.cakemgr; - -import org.hibernate.SessionFactory; -import org.hibernate.boot.registry.StandardServiceRegistryBuilder; -import org.hibernate.cfg.Configuration; -import org.hibernate.service.ServiceRegistry; - -public class HibernateUtil { - - private static SessionFactory sessionFactory = buildSessionFactory(); - - private static SessionFactory buildSessionFactory() { - try { - if (sessionFactory == null) { - Configuration configuration = new Configuration().configure(HibernateUtil.class.getResource("/hibernate.cfg.xml")); - StandardServiceRegistryBuilder serviceRegistryBuilder = new StandardServiceRegistryBuilder(); - serviceRegistryBuilder.applySettings(configuration.getProperties()); - ServiceRegistry serviceRegistry = serviceRegistryBuilder.build(); - sessionFactory = configuration.buildSessionFactory(serviceRegistry); - } - return sessionFactory; - } catch (Throwable ex) { - System.err.println("Initial SessionFactory creation failed." + ex); - throw new ExceptionInInitializerError(ex); - } - } - - public static SessionFactory getSessionFactory() { - return sessionFactory; - } - - public static void shutdown() { - getSessionFactory().close(); - } - -} diff --git a/src/main/java/com/waracle/cakemgr/Application.java b/src/main/java/com/waracle/cakemgr/Application.java new file mode 100644 index 0000000..d2eb073 --- /dev/null +++ b/src/main/java/com/waracle/cakemgr/Application.java @@ -0,0 +1,13 @@ +package com.waracle.cakemgr; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration; + +@SpringBootApplication(exclude = { SecurityAutoConfiguration.class }) +public class Application { + + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } +} diff --git a/src/main/java/com/waracle/cakemgr/config/CakeManagerConfig.java b/src/main/java/com/waracle/cakemgr/config/CakeManagerConfig.java new file mode 100644 index 0000000..6174940 --- /dev/null +++ b/src/main/java/com/waracle/cakemgr/config/CakeManagerConfig.java @@ -0,0 +1,30 @@ +package com.waracle.cakemgr.config; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.jdbc.datasource.DriverManagerDataSource; + +import javax.sql.DataSource; + +@Configuration +public class CakeManagerConfig { + + @Value("${db.driver.class.name}") + private String dbDriverClassName; + + @Value("${db.url}") + private String dbUrl; + + @Bean + public DataSource dataSource() { + DriverManagerDataSource dataSource = new DriverManagerDataSource(); + + dataSource.setDriverClassName(dbDriverClassName); + dataSource.setUrl(dbUrl); + + return dataSource; + } + + +} diff --git a/src/main/java/com/waracle/cakemgr/config/SwaggerConfig.java b/src/main/java/com/waracle/cakemgr/config/SwaggerConfig.java new file mode 100644 index 0000000..022a353 --- /dev/null +++ b/src/main/java/com/waracle/cakemgr/config/SwaggerConfig.java @@ -0,0 +1,22 @@ +package com.waracle.cakemgr.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import springfox.documentation.builders.PathSelectors; +import springfox.documentation.builders.RequestHandlerSelectors; +import springfox.documentation.spi.DocumentationType; +import springfox.documentation.spring.web.plugins.Docket; + +@Configuration +public class SwaggerConfig { + + @Bean + public Docket api() { + return new Docket(DocumentationType.SWAGGER_2) + .select() + .apis(RequestHandlerSelectors.any()) + .paths(PathSelectors.ant("/**")) + .build(); + } + +} \ No newline at end of file diff --git a/src/main/java/com/waracle/cakemgr/controller/CakeController.java b/src/main/java/com/waracle/cakemgr/controller/CakeController.java new file mode 100644 index 0000000..e9deac8 --- /dev/null +++ b/src/main/java/com/waracle/cakemgr/controller/CakeController.java @@ -0,0 +1,46 @@ +package com.waracle.cakemgr.controller; + +import com.waracle.cakemgr.dao.Cake; +import com.waracle.cakemgr.dto.CakeRequestDto; +import com.waracle.cakemgr.dto.CakeResponseDto; +import com.waracle.cakemgr.mapper.CakeMapper; +import com.waracle.cakemgr.service.CakeService; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.validation.Valid; +import java.util.List; +import java.util.stream.Collectors; + +@RestController +@RequestMapping("/cakes") +public class CakeController { + + private final CakeService service; + + public CakeController(CakeService service) { + this.service = service; + } + + @GetMapping + public ResponseEntity> getCakes() { + List cakes = service.getCakes().stream() + .map(CakeMapper::fromEntityToResponseDto) + .collect(Collectors.toList()); + + return ResponseEntity.ok().body(cakes); + } + + @PostMapping + public ResponseEntity addCake(@Valid @RequestBody CakeRequestDto cake) { + Cake cakeObj = CakeMapper.fromRequestDtoToEntity(cake); + service.writeCake(cakeObj); + return ResponseEntity.ok().build(); + } + +} diff --git a/src/main/java/com/waracle/cakemgr/controller/WelcomeController.java b/src/main/java/com/waracle/cakemgr/controller/WelcomeController.java new file mode 100644 index 0000000..05eb4cb --- /dev/null +++ b/src/main/java/com/waracle/cakemgr/controller/WelcomeController.java @@ -0,0 +1,46 @@ +package com.waracle.cakemgr.controller; + +import com.waracle.cakemgr.dao.Cake; +import com.waracle.cakemgr.dto.CakeRequestDto; +import com.waracle.cakemgr.mapper.CakeMapper; +import com.waracle.cakemgr.service.CakeService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; + +@Controller +public class WelcomeController { + + private final CakeService service; + + @Autowired + public WelcomeController(CakeService service) { + this.service = service; + } + + @GetMapping("/") + public String welcomePage(Model model) { + model.addAttribute("cakes", service.getCakes()); + + return "index"; + } + + @GetMapping("/cake") + public String viewAddCakeHtml(Model model) { + CakeRequestDto cake = new CakeRequestDto(); + model.addAttribute("cake", cake); + return "addCake"; + } + + @PostMapping(path = "/cake") + public String addCake( CakeRequestDto cake, Model model) { + model.addAttribute("cake", cake); + Cake cakeObj = CakeMapper.fromRequestDtoToEntity(cake); + service.writeCake(cakeObj); + + return welcomePage(model); + } + +} diff --git a/src/main/java/com/waracle/cakemgr/dao/Cake.java b/src/main/java/com/waracle/cakemgr/dao/Cake.java new file mode 100644 index 0000000..cb9d165 --- /dev/null +++ b/src/main/java/com/waracle/cakemgr/dao/Cake.java @@ -0,0 +1,41 @@ +package com.waracle.cakemgr.dao; + + +import lombok.Data; +import org.hibernate.annotations.DynamicUpdate; +import org.hibernate.annotations.GenericGenerator; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.Table; +import javax.persistence.UniqueConstraint; +import javax.validation.constraints.NotNull; +import java.io.Serializable; +import java.util.UUID; + +@Data +@Entity +@DynamicUpdate +@Table(name = "cake", uniqueConstraints = {@UniqueConstraint(columnNames = "title")}) +public class Cake implements Serializable { + + private static final long serialVersionUID = -2417760290457013668L; + + @Id + @GeneratedValue(generator = "UUID") + @GenericGenerator(name = "UUID", strategy = "org.hibernate.id.UUIDGenerator") + @Column(name = "id", updatable = false, nullable = false) + private UUID id; + + @NotNull + @Column(name = "title", unique = true, nullable = false) + private String title; + + @Column(name = "desc", nullable = false) + private String desc; + + @Column(name = "image", nullable = true) + private String image; +} diff --git a/src/main/java/com/waracle/cakemgr/dao/CakeRepository.java b/src/main/java/com/waracle/cakemgr/dao/CakeRepository.java new file mode 100644 index 0000000..ed237cb --- /dev/null +++ b/src/main/java/com/waracle/cakemgr/dao/CakeRepository.java @@ -0,0 +1,11 @@ +package com.waracle.cakemgr.dao; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.UUID; + +@Repository +public interface CakeRepository extends JpaRepository { + +} diff --git a/src/main/java/com/waracle/cakemgr/dto/CakeRequestDto.java b/src/main/java/com/waracle/cakemgr/dto/CakeRequestDto.java new file mode 100644 index 0000000..9c6eef8 --- /dev/null +++ b/src/main/java/com/waracle/cakemgr/dto/CakeRequestDto.java @@ -0,0 +1,42 @@ +package com.waracle.cakemgr.dto; + +import lombok.Data; + +import javax.validation.constraints.NotBlank; + +@Data +public class CakeRequestDto { + + @NotBlank + private String name; + + @NotBlank + private String description; + + @NotBlank + private String imageUrl; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getImageUrl() { + return imageUrl; + } + + public void setImageUrl(String imageUrl) { + this.imageUrl = imageUrl; + } +} diff --git a/src/main/java/com/waracle/cakemgr/dto/CakeResponseDto.java b/src/main/java/com/waracle/cakemgr/dto/CakeResponseDto.java new file mode 100644 index 0000000..b495335 --- /dev/null +++ b/src/main/java/com/waracle/cakemgr/dto/CakeResponseDto.java @@ -0,0 +1,13 @@ +package com.waracle.cakemgr.dto; + +import lombok.Data; +import lombok.NoArgsConstructor; + +@NoArgsConstructor +@Data +public class CakeResponseDto { + + private String name; + private String description; + private String imageUrl; +} diff --git a/src/main/java/com/waracle/cakemgr/mapper/CakeMapper.java b/src/main/java/com/waracle/cakemgr/mapper/CakeMapper.java new file mode 100644 index 0000000..9f6b958 --- /dev/null +++ b/src/main/java/com/waracle/cakemgr/mapper/CakeMapper.java @@ -0,0 +1,33 @@ +package com.waracle.cakemgr.mapper; + +import com.waracle.cakemgr.dao.Cake; +import com.waracle.cakemgr.dto.CakeRequestDto; +import com.waracle.cakemgr.dto.CakeResponseDto; +import org.modelmapper.ModelMapper; + +public class CakeMapper { + + private static final ModelMapper modelMapper = new ModelMapper(); + + private CakeMapper() { + } + + public static CakeResponseDto fromEntityToResponseDto(Cake cake) { + modelMapper.typeMap(Cake.class, CakeResponseDto.class) + .addMapping(Cake::getTitle, CakeResponseDto::setName) + .addMapping(Cake::getDesc, CakeResponseDto::setDescription) + .addMapping(Cake::getImage, CakeResponseDto::setImageUrl); + + return modelMapper.map(cake, CakeResponseDto.class); + } + + public static Cake fromRequestDtoToEntity(CakeRequestDto cakeDto) { + modelMapper.typeMap(CakeRequestDto.class, Cake.class) + .addMapping(CakeRequestDto::getName, Cake::setTitle) + .addMapping(CakeRequestDto::getDescription, Cake::setDesc) + .addMapping(CakeRequestDto::getImageUrl, Cake::setImage); + + return modelMapper.map(cakeDto, Cake.class); + } + +} diff --git a/src/main/java/com/waracle/cakemgr/service/CakeService.java b/src/main/java/com/waracle/cakemgr/service/CakeService.java new file mode 100644 index 0000000..aa7b56a --- /dev/null +++ b/src/main/java/com/waracle/cakemgr/service/CakeService.java @@ -0,0 +1,45 @@ +package com.waracle.cakemgr.service; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.waracle.cakemgr.dao.Cake; +import com.waracle.cakemgr.dao.CakeRepository; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +import javax.annotation.PostConstruct; +import java.io.IOException; +import java.net.URL; +import java.util.List; +import java.util.Set; + +@Service +public class CakeService { + + @Value("${cake.url}") + private String cakeUrl; + + private final CakeRepository repository; + private final ObjectMapper objectMapper = new ObjectMapper(); + + public CakeService(CakeRepository repository) { + this.repository = repository; + } + + @PostConstruct + public void populateCakes() throws IOException { + Set cakeEntities = objectMapper + .readValue(new URL(cakeUrl), new TypeReference<>() { + }); + repository.saveAll(cakeEntities); + } + + public List getCakes() { + + return repository.findAll(); + } + + public void writeCake(Cake cake) { + repository.save(cake); + } +} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties new file mode 100644 index 0000000..a5bdeae --- /dev/null +++ b/src/main/resources/application.properties @@ -0,0 +1,11 @@ +db.url=jdbc:hsqldb:mem:db +db.driver.class.name=org.hsqldb.jdbcDriver + +server.port=8181 + +logging.level.org.hibernate=INFO +logging.level.org.hibernate.SQL=DEBUG +logging.level.org.hibernate.cache=DEBUG +logging.level.org.hibernate.stat=DEBUG + +cake.url=https://gist.githubusercontent.com/hart88/198f29ec5114a3ec3460/raw/8dd19a88f9b8d24c23d9960f3300d0c917a4f07c/cakeEntity.json \ No newline at end of file diff --git a/src/main/resources/hibernate.cfg.xml b/src/main/resources/hibernate.cfg.xml deleted file mode 100644 index 0ae06d6..0000000 --- a/src/main/resources/hibernate.cfg.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - class,hbm - org.hibernate.dialect.HSQLDialect - true - org.hsqldb.jdbcDriver - sa - - jdbc:hsqldb:mem:db - create - - - \ No newline at end of file diff --git a/src/main/resources/templates/addCake.html b/src/main/resources/templates/addCake.html new file mode 100644 index 0000000..47da023 --- /dev/null +++ b/src/main/resources/templates/addCake.html @@ -0,0 +1,37 @@ + + + + + Cake Manager App - Add Cake + + +
+
+

Welcome to the Cake Manager

+
+ +
+

+
+ +
+

Add Cake

+ +
+ +
+
+

+
+ +
+
+

+
+ +
+
+
+ + \ No newline at end of file diff --git a/src/main/resources/templates/index.html b/src/main/resources/templates/index.html new file mode 100644 index 0000000..3c0fa49 --- /dev/null +++ b/src/main/resources/templates/index.html @@ -0,0 +1,54 @@ + + + + + Cake Manager + + +
+
+

Welcome to the Cake Manager

+
+ + + +
+

+
+ + + +
+
+

List of Initial Cakes

+ + + + + + + + + + + + + + +
UUIDTitleDescriptionImage
+ + +
+
+
+
+ + \ No newline at end of file diff --git a/src/main/webapp/WEB-INF/web.xml b/src/main/webapp/WEB-INF/web.xml deleted file mode 100644 index d004447..0000000 --- a/src/main/webapp/WEB-INF/web.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - Archetype Created Web Application - - diff --git a/src/main/webapp/index.jsp b/src/main/webapp/index.jsp deleted file mode 100644 index c38169b..0000000 --- a/src/main/webapp/index.jsp +++ /dev/null @@ -1,5 +0,0 @@ - - -

Hello World!

- - diff --git a/src/test/java/com/waracle/cakemgr/controller/CakeControllerTest.java b/src/test/java/com/waracle/cakemgr/controller/CakeControllerTest.java new file mode 100644 index 0000000..f43fd6c --- /dev/null +++ b/src/test/java/com/waracle/cakemgr/controller/CakeControllerTest.java @@ -0,0 +1,82 @@ +package com.waracle.cakemgr.controller; + +import com.waracle.cakemgr.dao.Cake; +import com.waracle.cakemgr.dto.CakeRequestDto; +import com.waracle.cakemgr.dto.CakeResponseDto; +import com.waracle.cakemgr.service.CakeService; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.http.ResponseEntity; + +import java.util.ArrayList; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +class CakeControllerTest { + + @Mock + private CakeService cakeService; + + @InjectMocks + private CakeController cakeController; + + @Test + void testListOfCakes() { + Cake cake1 = new Cake(); + cake1.setTitle("Cake Title 123"); + cake1.setDesc("Cake desc 123"); + cake1.setImage("Cake Img Url "); + + CakeResponseDto cakeResponse1 = new CakeResponseDto(); + cakeResponse1.setName("Cake Title 123"); + cakeResponse1.setDescription("Cake desc 123"); + cakeResponse1.setImageUrl("Cake Img Url "); + + Cake cake2 = new Cake(); + cake2.setTitle("Cake title 909"); + cake2.setDesc("Cake desc 909"); + cake2.setImage("Cake Img Url 9090"); + + CakeResponseDto cakeResponse2 = new CakeResponseDto(); + cakeResponse2.setName("Cake title 909"); + cakeResponse2.setDescription("Cake desc 909"); + cakeResponse2.setImageUrl("Cake Img Url 9090"); + + List cakeList = new ArrayList<>(); + cakeList.add(cake1); + cakeList.add(cake2); + + List expectedCakeResponse = new ArrayList<>(); + expectedCakeResponse.add(cakeResponse1); + expectedCakeResponse.add(cakeResponse2); + + when(cakeService.getCakes()).thenReturn(cakeList); + + ResponseEntity> response = cakeController.getCakes(); + + assertThat(response.getStatusCodeValue()).isEqualTo(200); + assertThat(response.getBody()).isEqualTo(expectedCakeResponse); + } + + @Test + void testPostMethod() { + CakeRequestDto cakeRequestDto = new CakeRequestDto(); + cakeRequestDto.setName("Title_123"); + cakeRequestDto.setDescription("Description_123"); + cakeRequestDto.setImageUrl("ImageUrl_123"); + + ResponseEntity response = cakeController.addCake(cakeRequestDto); + + assertThat(response.getStatusCodeValue()).isEqualTo(200); + verify(cakeService, times(1)).writeCake(any(Cake.class)); + } +} diff --git a/src/test/java/com/waracle/cakemgr/mapper/CakeMapperTest.java b/src/test/java/com/waracle/cakemgr/mapper/CakeMapperTest.java new file mode 100644 index 0000000..e5746e1 --- /dev/null +++ b/src/test/java/com/waracle/cakemgr/mapper/CakeMapperTest.java @@ -0,0 +1,46 @@ +package com.waracle.cakemgr.mapper; + +import com.waracle.cakemgr.dao.Cake; +import com.waracle.cakemgr.dto.CakeRequestDto; +import com.waracle.cakemgr.dto.CakeResponseDto; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +class CakeMapperTest { + + @Test + void testEntityToResponseDto() { + Cake cake = new Cake(); + cake.setTitle("Cake Title"); + cake.setDesc("Cake Desc123"); + cake.setImage("Cake Img Url"); + + CakeResponseDto expectedResponseDto = new CakeResponseDto(); + expectedResponseDto.setName("Cake Title"); + expectedResponseDto.setDescription("Cake Desc123"); + expectedResponseDto.setImageUrl("Cake Img Url"); + + CakeResponseDto actualResponseDto = CakeMapper.fromEntityToResponseDto(cake); + + assertThat(actualResponseDto).isEqualTo(expectedResponseDto); + } + + @Test + void testRequestDtoToEntity() { + CakeRequestDto cakeRequestDto = new CakeRequestDto(); + cakeRequestDto.setName("Cake Title"); + cakeRequestDto.setDescription("Cake Desc123"); + cakeRequestDto.setImageUrl("Cake Img Url"); + + Cake expectedCake = new Cake(); + expectedCake.setTitle("Cake Title"); + expectedCake.setDesc("Cake Desc123"); + expectedCake.setImage("Cake Img Url"); + + Cake actualEntity = CakeMapper.fromRequestDtoToEntity(cakeRequestDto); + + assertThat(actualEntity).isEqualTo(expectedCake); + } + +} diff --git a/src/test/java/com/waracle/cakemgr/service/CakeServiceTest.java b/src/test/java/com/waracle/cakemgr/service/CakeServiceTest.java new file mode 100644 index 0000000..1173131 --- /dev/null +++ b/src/test/java/com/waracle/cakemgr/service/CakeServiceTest.java @@ -0,0 +1,74 @@ +package com.waracle.cakemgr.service; + +import com.waracle.cakemgr.dao.Cake; +import com.waracle.cakemgr.dao.CakeRepository; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +import java.io.IOException; +import java.util.List; +import java.util.Set; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class CakeServiceTest { + + @Mock + private CakeRepository repository; + + @InjectMocks + private CakeService cakeService; + + @Test + public void testPopulateCakesIsCalled() throws IOException { + ReflectionTestUtils.setField(cakeService, "cakeUrl", + "https://gist.githubusercontent.com/hart88/198f29ec5114a3ec3460/raw/8dd19a88f9b8d24c23d9960f3300d0c917a4f07c/cake.json"); + + cakeService.populateCakes(); + + verify(repository).saveAll(any(Set.class)); + } + + @Test + public void testGetCakesReturnsListOfEntitiesFromRepository() { + Cake cake1 = new Cake(); + cake1.setTitle("Cake Title"); + cake1.setDesc("Cake desc"); + cake1.setImage("Cake Img. Url"); + + Cake cake2 = new Cake(); + cake2.setTitle("Cake Title 123"); + cake2.setDesc("Cake desc 123"); + cake2.setImage("Cake Img. Url 123"); + + when(repository.findAll()).thenReturn(List.of(cake1, cake2)); + + List cakeEntities = cakeService.getCakes(); + + assertThat(cakeEntities.size()).isEqualTo(2); + assertThat(cakeEntities.get(0)).isEqualTo(cake1); + assertThat(cakeEntities.get(1)).isEqualTo(cake2); + } + + @Test + public void testWriteCakeSavesToRepository() { + Cake cake = new Cake(); + cake.setTitle("Cake Title 9090"); + cake.setDesc("Cake desc 9090"); + cake.setImage("Cake Img Url 9090"); + + cakeService.writeCake(cake); + + verify(repository, times(1)).save(cake); + } + +} diff --git a/src/test/resources/application.properties b/src/test/resources/application.properties new file mode 100644 index 0000000..5325300 --- /dev/null +++ b/src/test/resources/application.properties @@ -0,0 +1 @@ +logging.level.org.springframework=ERROR \ No newline at end of file