diff --git a/.idea/dataSources.local.xml b/.idea/dataSources.local.xml new file mode 100644 index 0000000..d80b8c7 --- /dev/null +++ b/.idea/dataSources.local.xml @@ -0,0 +1,11 @@ + + + + + + master_key + postgres + + + + \ No newline at end of file diff --git a/.idea/dataSources.xml b/.idea/dataSources.xml new file mode 100644 index 0000000..2857aeb --- /dev/null +++ b/.idea/dataSources.xml @@ -0,0 +1,12 @@ + + + + + postgresql + true + org.postgresql.Driver + jdbc:postgresql://localhost:5432/db_invoice + $ProjectFileDir$ + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..639900d --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..b139bdc --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/training_microservices.iml b/.idea/training_microservices.iml new file mode 100644 index 0000000..d6ebd48 --- /dev/null +++ b/.idea/training_microservices.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/workspace.xml b/.idea/workspace.xml new file mode 100644 index 0000000..02f665e --- /dev/null +++ b/.idea/workspace.xml @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + 1707353540000 + + + + + + \ No newline at end of file diff --git a/02-todo-web-application-h2/Dockerfile b/02-todo-web-application-h2/Dockerfile new file mode 100644 index 0000000..f0deecb --- /dev/null +++ b/02-todo-web-application-h2/Dockerfile @@ -0,0 +1,5 @@ +FROM tomcat:8.0.51-jre8-alpine +EXPOSE 8080 +RUN rm -rf /usr/local/tomcat/webapps/* +COPY target/*.war /usr/local/tomcat/webapps/ROOT.war +CMD ["catalina.sh","run"] \ No newline at end of file diff --git a/02-todo-web-application-h2/backup/deployment.yaml b/02-todo-web-application-h2/backup/deployment.yaml new file mode 100644 index 0000000..2c11e40 --- /dev/null +++ b/02-todo-web-application-h2/backup/deployment.yaml @@ -0,0 +1,47 @@ +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + labels: + app: todo-web-application-h2 + name: todo-web-application-h2 + namespace: default +spec: + replicas: 2 + minReadySeconds: 45 + selector: + matchLabels: + app: todo-web-application-h2 + strategy: + rollingUpdate: + maxSurge: 25% + maxUnavailable: 25% + type: RollingUpdate + template: + metadata: + labels: + app: todo-web-application-h2 + spec: + containers: + - image: in28min/todo-web-application-h2:0.0.1-SNAPSHOT + imagePullPolicy: IfNotPresent + name: todo-web-application-h2 + restartPolicy: Always + terminationGracePeriodSeconds: 30 +--- +apiVersion: v1 +kind: Service +metadata: + labels: #PODS + app: todo-web-application-h2 + name: todo-web-application-h2 + namespace: default +spec: + ports: + - nodePort: 30702 + port: 8080 + protocol: TCP + targetPort: 8080 + selector: + app: todo-web-application-h2 + sessionAffinity: ClientIP + type: LoadBalancer \ No newline at end of file diff --git a/02-todo-web-application-h2/pom.xml b/02-todo-web-application-h2/pom.xml new file mode 100644 index 0000000..bbfa3d7 --- /dev/null +++ b/02-todo-web-application-h2/pom.xml @@ -0,0 +1,143 @@ + + + 4.0.0 + + com.in28minutes.springboot.web + 02-todo-web-application-h2 + 0.0.1-SNAPSHOT + war + todo-web-application-h2 + + Demo project for Spring Boot + + + org.springframework.boot + spring-boot-starter-parent + 2.1.7.RELEASE + + + + + UTF-8 + UTF-8 + 1.8 + 3.1.1 + + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + + com.h2database + h2 + runtime + + + + org.springframework.boot + spring-boot-starter-security + + + + javax.servlet + jstl + + + + org.webjars + bootstrap + 3.3.6 + + + + org.webjars + bootstrap-datepicker + 1.0.1 + + + + org.webjars + jquery + 1.9.1 + + + + org.apache.tomcat.embed + tomcat-embed-jasper + provided + + + + org.springframework.boot + spring-boot-starter-tomcat + provided + + + + org.springframework.boot + spring-boot-devtools + runtime + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + todo-web-application-h2 + + + org.springframework.boot + spring-boot-maven-plugin + + + com.spotify + dockerfile-maven-plugin + 1.4.6 + + + default + + build + + + + + in28min/${project.name} + ${project.version} + true + + + + + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestones + + + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestones + + + + \ No newline at end of file diff --git a/02-todo-web-application-h2/readme.md b/02-todo-web-application-h2/readme.md new file mode 100644 index 0000000..ce6adef --- /dev/null +++ b/02-todo-web-application-h2/readme.md @@ -0,0 +1,81 @@ +# Todo Web Application using Spring Boot and H2 In memory database + +Run com.in28minutes.springboot.web.SpringBootFirstWebApplication as a Java Application. + +Runs on default port of Spring Boot - 8080 + +## Can be run as a Jar or a WAR + +`mvn clean install` generate a war which can deployed to your favorite web server. + +We will deploy to Docker as a WAR + +## Web Application + +- http://localhost:8080/login with in28minutes/dummy as credentials +- You can add, delete and update your todos +- Spring Security is used to secure the application +- `com.in28minutes.springboot.web.security.SecurityConfiguration` contains the in memory security credential configuration. + +## H2 Console + +- http://localhost:8080/h2-console +- Use `jdbc:h2:mem:testdb` as JDBC URL + + +## Plugins + +### Dockerfile Maven + +- https://github.com/spotify/dockerfile-maven + +``` + + com.spotify + dockerfile-maven-plugin + 1.4.6 + + + default + + build + + + + + in28min/${project.name} + ${project.version} + true + + +``` +### JIB + +- https://github.com/GoogleContainerTools/jib/tree/master/jib-maven-plugin#quickstart +- https://github.com/GoogleContainerTools/jib/blob/master/docs/faq.md +- `true` - https://github.com/GoogleContainerTools/jib/issues/413 + +``` + + com.google.cloud.tools + jib-maven-plugin + 1.6.1 + + + tomcat:8.5-jre8-alpine + + + /usr/local/tomcat/webapps/my-webapp + USE_CURRENT_TIMESTAMP + + + + + package + + dockerBuild + + + + +``` \ No newline at end of file diff --git a/02-todo-web-application-h2/src/main/java/com/in28minutes/springboot/web/EnvironmentConfigurationLogger.java b/02-todo-web-application-h2/src/main/java/com/in28minutes/springboot/web/EnvironmentConfigurationLogger.java new file mode 100644 index 0000000..027dcb7 --- /dev/null +++ b/02-todo-web-application-h2/src/main/java/com/in28minutes/springboot/web/EnvironmentConfigurationLogger.java @@ -0,0 +1,41 @@ +package com.in28minutes.springboot.web; + +import java.util.Arrays; +import java.util.stream.StreamSupport; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.context.event.ContextRefreshedEvent; +import org.springframework.context.event.EventListener; +import org.springframework.core.env.AbstractEnvironment; +import org.springframework.core.env.EnumerablePropertySource; +import org.springframework.core.env.Environment; +import org.springframework.core.env.MutablePropertySources; +import org.springframework.stereotype.Component; + +@Component +public class EnvironmentConfigurationLogger { + + private static final Logger LOGGER = LoggerFactory.getLogger(EnvironmentConfigurationLogger.class); + + @SuppressWarnings("rawtypes") + @EventListener + public void handleContextRefresh(ContextRefreshedEvent event) { + final Environment environment = event.getApplicationContext().getEnvironment(); + LOGGER.info("====== Environment and configuration ======"); + LOGGER.info("Active profiles: {}", Arrays.toString(environment.getActiveProfiles())); + final MutablePropertySources sources = ((AbstractEnvironment) environment).getPropertySources(); + StreamSupport.stream(sources.spliterator(), false).filter(ps -> ps instanceof EnumerablePropertySource) + .map(ps -> ((EnumerablePropertySource) ps).getPropertyNames()).flatMap(Arrays::stream).distinct() + .forEach(prop -> { + Object resolved = environment.getProperty(prop, Object.class); + if (resolved instanceof String) { + LOGGER.info("{} - {}", prop, environment.getProperty(prop)); + } else { + LOGGER.info("{} - {}", prop, "NON-STRING-VALUE"); + } + + }); + LOGGER.debug("==========================================="); + } +} diff --git a/02-todo-web-application-h2/src/main/java/com/in28minutes/springboot/web/SpringBootFirstWebApplication.java b/02-todo-web-application-h2/src/main/java/com/in28minutes/springboot/web/SpringBootFirstWebApplication.java new file mode 100644 index 0000000..4fe5897 --- /dev/null +++ b/02-todo-web-application-h2/src/main/java/com/in28minutes/springboot/web/SpringBootFirstWebApplication.java @@ -0,0 +1,22 @@ +package com.in28minutes.springboot.web; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; +import org.springframework.context.annotation.ComponentScan; + +@SpringBootApplication +@ComponentScan("com.in28minutes.springboot.web") +public class SpringBootFirstWebApplication extends SpringBootServletInitializer { + + @Override + protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { + return application.sources(SpringBootFirstWebApplication.class); + } + + public static void main(String[] args) { + SpringApplication.run(SpringBootFirstWebApplication.class, args); + } + +} \ No newline at end of file diff --git a/02-todo-web-application-h2/src/main/java/com/in28minutes/springboot/web/controller/ErrorController.java b/02-todo-web-application-h2/src/main/java/com/in28minutes/springboot/web/controller/ErrorController.java new file mode 100644 index 0000000..79e0956 --- /dev/null +++ b/02-todo-web-application-h2/src/main/java/com/in28minutes/springboot/web/controller/ErrorController.java @@ -0,0 +1,24 @@ +package com.in28minutes.springboot.web.controller; + +import javax.servlet.http.HttpServletRequest; + +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.servlet.ModelAndView; + +@Controller("error") +public class ErrorController { + + @ExceptionHandler(Exception.class) + public ModelAndView handleException + (HttpServletRequest request, Exception ex){ + ModelAndView mv = new ModelAndView(); + + mv.addObject("exception", ex.getLocalizedMessage()); + mv.addObject("url", request.getRequestURL()); + + mv.setViewName("error"); + return mv; + } + +} diff --git a/02-todo-web-application-h2/src/main/java/com/in28minutes/springboot/web/controller/LogoutController.java b/02-todo-web-application-h2/src/main/java/com/in28minutes/springboot/web/controller/LogoutController.java new file mode 100644 index 0000000..5f426a6 --- /dev/null +++ b/02-todo-web-application-h2/src/main/java/com/in28minutes/springboot/web/controller/LogoutController.java @@ -0,0 +1,30 @@ +package com.in28minutes.springboot.web.controller; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; + +@Controller +public class LogoutController { + + @RequestMapping(value = "/logout", method = RequestMethod.GET) + public String logout(HttpServletRequest request, + HttpServletResponse response) { + + Authentication authentication = SecurityContextHolder.getContext() + .getAuthentication(); + + if (authentication != null) { + new SecurityContextLogoutHandler().logout(request, response, + authentication); + } + + return "redirect:/"; + } +} diff --git a/02-todo-web-application-h2/src/main/java/com/in28minutes/springboot/web/controller/TodoController.java b/02-todo-web-application-h2/src/main/java/com/in28minutes/springboot/web/controller/TodoController.java new file mode 100644 index 0000000..75489e0 --- /dev/null +++ b/02-todo-web-application-h2/src/main/java/com/in28minutes/springboot/web/controller/TodoController.java @@ -0,0 +1,111 @@ +package com.in28minutes.springboot.web.controller; + +import java.text.SimpleDateFormat; +import java.util.Date; + +import javax.validation.Valid; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.propertyeditors.CustomDateEditor; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.stereotype.Controller; +import org.springframework.ui.ModelMap; +import org.springframework.validation.BindingResult; +import org.springframework.web.bind.WebDataBinder; +import org.springframework.web.bind.annotation.InitBinder; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; + +import com.in28minutes.springboot.web.model.Todo; +import com.in28minutes.springboot.web.service.TodoRepository; + +@Controller +public class TodoController { + + @Autowired + TodoRepository repository; + + @InitBinder + public void initBinder(WebDataBinder binder) { + // Date - dd/MM/yyyy + SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy"); + binder.registerCustomEditor(Date.class, new CustomDateEditor( + dateFormat, false)); + } + + @RequestMapping(value = "/list-todos", method = RequestMethod.GET) + public String showTodos(ModelMap model) { + String name = getLoggedInUserName(model); + model.put("todos", repository.findByUser(name)); + //model.put("todos", service.retrieveTodos(name)); + return "list-todos"; + } + + private String getLoggedInUserName(ModelMap model) { + Object principal = SecurityContextHolder.getContext() + .getAuthentication().getPrincipal(); + + if (principal instanceof UserDetails) { + return ((UserDetails) principal).getUsername(); + } + + return principal.toString(); + } + + @RequestMapping(value = "/add-todo", method = RequestMethod.GET) + public String showAddTodoPage(ModelMap model) { + model.addAttribute("todo", new Todo(0, getLoggedInUserName(model), + "Default Desc", new Date(), false)); + return "todo"; + } + + @RequestMapping(value = "/delete-todo", method = RequestMethod.GET) + public String deleteTodo(@RequestParam int id) { + + //if(id==1) + //throw new RuntimeException("Something went wrong"); + repository.deleteById(id); + //service.deleteTodo(id); + return "redirect:/list-todos"; + } + + @RequestMapping(value = "/update-todo", method = RequestMethod.GET) + public String showUpdateTodoPage(@RequestParam int id, ModelMap model) { + Todo todo = repository.findById(id).get(); + //Todo todo = service.retrieveTodo(id); + model.put("todo", todo); + return "todo"; + } + + @RequestMapping(value = "/update-todo", method = RequestMethod.POST) + public String updateTodo(ModelMap model, @Valid Todo todo, + BindingResult result) { + + if (result.hasErrors()) { + return "todo"; + } + + todo.setUser(getLoggedInUserName(model)); + + repository.save(todo); + //service.updateTodo(todo); + + return "redirect:/list-todos"; + } + + @RequestMapping(value = "/add-todo", method = RequestMethod.POST) + public String addTodo(ModelMap model, @Valid Todo todo, BindingResult result) { + + if (result.hasErrors()) { + return "todo"; + } + + todo.setUser(getLoggedInUserName(model)); + repository.save(todo); + /*service.addTodo(getLoggedInUserName(model), todo.getDesc(), todo.getTargetDate(), + false);*/ + return "redirect:/list-todos"; + } +} diff --git a/02-todo-web-application-h2/src/main/java/com/in28minutes/springboot/web/controller/WelcomeController.java b/02-todo-web-application-h2/src/main/java/com/in28minutes/springboot/web/controller/WelcomeController.java new file mode 100644 index 0000000..da5018c --- /dev/null +++ b/02-todo-web-application-h2/src/main/java/com/in28minutes/springboot/web/controller/WelcomeController.java @@ -0,0 +1,30 @@ +package com.in28minutes.springboot.web.controller; + +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.stereotype.Controller; +import org.springframework.ui.ModelMap; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; + +@Controller +public class WelcomeController { + + @RequestMapping(value = "/", method = RequestMethod.GET) + public String showWelcomePage(ModelMap model) { + model.put("name", getLoggedinUserName()); + return "welcome"; + } + + private String getLoggedinUserName() { + Object principal = SecurityContextHolder.getContext() + .getAuthentication().getPrincipal(); + + if (principal instanceof UserDetails) { + return ((UserDetails) principal).getUsername(); + } + + return principal.toString(); + } + +} diff --git a/02-todo-web-application-h2/src/main/java/com/in28minutes/springboot/web/model/Todo.java b/02-todo-web-application-h2/src/main/java/com/in28minutes/springboot/web/model/Todo.java new file mode 100644 index 0000000..e794ac9 --- /dev/null +++ b/02-todo-web-application-h2/src/main/java/com/in28minutes/springboot/web/model/Todo.java @@ -0,0 +1,112 @@ +package com.in28minutes.springboot.web.model; + +import java.util.Date; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.validation.constraints.Size; + +@Entity +public class Todo { + + @Id + @GeneratedValue + private int id; + + private String user; + + @Size(min=10, message="Enter at least 10 Characters...") + private String desc; + + private Date targetDate; + private boolean isDone; + + public Todo() { + super(); + } + + public Todo(int id, String user, String desc, Date targetDate, + boolean isDone) { + super(); + this.id = id; + this.user = user; + this.desc = desc; + this.targetDate = targetDate; + this.isDone = isDone; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getUser() { + return user; + } + + public void setUser(String user) { + this.user = user; + } + + public String getDesc() { + return desc; + } + + public void setDesc(String desc) { + this.desc = desc; + } + + public Date getTargetDate() { + return targetDate; + } + + public void setTargetDate(Date targetDate) { + this.targetDate = targetDate; + } + + public boolean isDone() { + return isDone; + } + + public void setDone(boolean isDone) { + this.isDone = isDone; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + id; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + Todo other = (Todo) obj; + if (id != other.id) { + return false; + } + return true; + } + + @Override + public String toString() { + return String.format( + "Todo [id=%s, user=%s, desc=%s, targetDate=%s, isDone=%s]", id, + user, desc, targetDate, isDone); + } + +} \ No newline at end of file diff --git a/02-todo-web-application-h2/src/main/java/com/in28minutes/springboot/web/security/SecurityConfiguration.java b/02-todo-web-application-h2/src/main/java/com/in28minutes/springboot/web/security/SecurityConfiguration.java new file mode 100644 index 0000000..7d92bd8 --- /dev/null +++ b/02-todo-web-application-h2/src/main/java/com/in28minutes/springboot/web/security/SecurityConfiguration.java @@ -0,0 +1,28 @@ +package com.in28minutes.springboot.web.security; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; + +@Configuration +public class SecurityConfiguration extends WebSecurityConfigurerAdapter{ + //Create User - in28Minutes/dummy + @Autowired + public void configureGlobalSecurity(AuthenticationManagerBuilder auth) + throws Exception { + auth.inMemoryAuthentication().withUser("in28minutes").password("{noop}dummy") + .roles("USER", "ADMIN"); + } + + @Override + protected void configure(HttpSecurity http) throws Exception { + http.authorizeRequests().antMatchers("/login", "/h2-console/**").permitAll() + .antMatchers("/", "/*todo*/**").access("hasRole('USER')").and() + .formLogin(); + + http.csrf().disable(); + http.headers().frameOptions().disable(); + } +} diff --git a/02-todo-web-application-h2/src/main/java/com/in28minutes/springboot/web/service/TodoRepository.java b/02-todo-web-application-h2/src/main/java/com/in28minutes/springboot/web/service/TodoRepository.java new file mode 100644 index 0000000..0e7fcea --- /dev/null +++ b/02-todo-web-application-h2/src/main/java/com/in28minutes/springboot/web/service/TodoRepository.java @@ -0,0 +1,11 @@ +package com.in28minutes.springboot.web.service; + +import java.util.List; + +import org.springframework.data.jpa.repository.JpaRepository; + +import com.in28minutes.springboot.web.model.Todo; + +public interface TodoRepository extends JpaRepository { + List findByUser(String user); +} \ No newline at end of file diff --git a/02-todo-web-application-h2/src/main/resources/application.properties b/02-todo-web-application-h2/src/main/resources/application.properties new file mode 100644 index 0000000..bff8631 --- /dev/null +++ b/02-todo-web-application-h2/src/main/resources/application.properties @@ -0,0 +1,7 @@ +spring.mvc.view.prefix=/WEB-INF/jsp/ +spring.mvc.view.suffix=.jsp +logging.level.org.springframework.web=INFO + +spring.jpa.show-sql=true +spring.h2.console.enabled=true +spring.h2.console.settings.web-allow-others=true \ No newline at end of file diff --git a/02-todo-web-application-h2/src/main/resources/data.sql b/02-todo-web-application-h2/src/main/resources/data.sql new file mode 100644 index 0000000..2f16341 --- /dev/null +++ b/02-todo-web-application-h2/src/main/resources/data.sql @@ -0,0 +1,6 @@ +insert into TODO +values(10001, 'Learn Spring Boot', false, sysdate(), 'in28minutes'); +insert into TODO +values(10002, 'Learn Angular JS', false, sysdate(), 'in28minutes'); +insert into TODO +values(10003, 'Learn to Dance', false, sysdate(), 'in28minutes'); diff --git a/02-todo-web-application-h2/src/main/webapp/WEB-INF/jsp/common/footer.jspf b/02-todo-web-application-h2/src/main/webapp/WEB-INF/jsp/common/footer.jspf new file mode 100644 index 0000000..879b276 --- /dev/null +++ b/02-todo-web-application-h2/src/main/webapp/WEB-INF/jsp/common/footer.jspf @@ -0,0 +1,12 @@ + + + + + + + \ No newline at end of file diff --git a/02-todo-web-application-h2/src/main/webapp/WEB-INF/jsp/common/header.jspf b/02-todo-web-application-h2/src/main/webapp/WEB-INF/jsp/common/header.jspf new file mode 100644 index 0000000..6c8761f --- /dev/null +++ b/02-todo-web-application-h2/src/main/webapp/WEB-INF/jsp/common/header.jspf @@ -0,0 +1,14 @@ +<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> +<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%> +<%@taglib uri="http://www.springframework.org/tags/form" prefix="form"%> + + + + +First Web Application + + + + + diff --git a/02-todo-web-application-h2/src/main/webapp/WEB-INF/jsp/common/navigation.jspf b/02-todo-web-application-h2/src/main/webapp/WEB-INF/jsp/common/navigation.jspf new file mode 100644 index 0000000..de0a5d6 --- /dev/null +++ b/02-todo-web-application-h2/src/main/webapp/WEB-INF/jsp/common/navigation.jspf @@ -0,0 +1,15 @@ + + diff --git a/02-todo-web-application-h2/src/main/webapp/WEB-INF/jsp/error.jsp b/02-todo-web-application-h2/src/main/webapp/WEB-INF/jsp/error.jsp new file mode 100644 index 0000000..d44949d --- /dev/null +++ b/02-todo-web-application-h2/src/main/webapp/WEB-INF/jsp/error.jsp @@ -0,0 +1,6 @@ +<%@ include file="common/header.jspf"%> +<%@ include file="common/navigation.jspf"%> +
+An exception occurred! Please contact Support! +
+<%@ include file="common/footer.jspf"%> \ No newline at end of file diff --git a/02-todo-web-application-h2/src/main/webapp/WEB-INF/jsp/list-todos.jsp b/02-todo-web-application-h2/src/main/webapp/WEB-INF/jsp/list-todos.jsp new file mode 100644 index 0000000..5f3abdd --- /dev/null +++ b/02-todo-web-application-h2/src/main/webapp/WEB-INF/jsp/list-todos.jsp @@ -0,0 +1,34 @@ +<%@ include file="common/header.jspf" %> +<%@ include file="common/navigation.jspf" %> + +
+ + + + + + + + + + + + + + + + + + + + + + +
Your todos are
DescriptionTarget DateIs it Done?
${todo.desc}${todo.done}UpdateDelete
+
+ Add a Todo +
+
+<%@ include file="common/footer.jspf" %> \ No newline at end of file diff --git a/02-todo-web-application-h2/src/main/webapp/WEB-INF/jsp/todo.jsp b/02-todo-web-application-h2/src/main/webapp/WEB-INF/jsp/todo.jsp new file mode 100644 index 0000000..77cf14f --- /dev/null +++ b/02-todo-web-application-h2/src/main/webapp/WEB-INF/jsp/todo.jsp @@ -0,0 +1,23 @@ +<%@ include file="common/header.jspf" %> +<%@ include file="common/navigation.jspf" %> +
+ + +
+ Description + + +
+ +
+ Target Date + + +
+ + +
+
+<%@ include file="common/footer.jspf" %> \ No newline at end of file diff --git a/02-todo-web-application-h2/src/main/webapp/WEB-INF/jsp/welcome.jsp b/02-todo-web-application-h2/src/main/webapp/WEB-INF/jsp/welcome.jsp new file mode 100644 index 0000000..c94ed31 --- /dev/null +++ b/02-todo-web-application-h2/src/main/webapp/WEB-INF/jsp/welcome.jsp @@ -0,0 +1,7 @@ +<%@ include file="common/header.jspf"%> +<%@ include file="common/navigation.jspf"%> +
+ Welcome ${name}!! Click here to manage your + todo's. +
+<%@ include file="common/footer.jspf"%> \ No newline at end of file diff --git a/02-todo-web-application-h2/src/test/java/com/in28minutes/springboot/web/SpringBootFirstWebApplicationTests.java b/02-todo-web-application-h2/src/test/java/com/in28minutes/springboot/web/SpringBootFirstWebApplicationTests.java new file mode 100644 index 0000000..f207136 --- /dev/null +++ b/02-todo-web-application-h2/src/test/java/com/in28minutes/springboot/web/SpringBootFirstWebApplicationTests.java @@ -0,0 +1,16 @@ +package com.in28minutes.springboot.web; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@SpringBootTest +public class SpringBootFirstWebApplicationTests { + + @Test + public void contextLoads() { + } + +} diff --git a/02-todo-web-application-h2/target/classes/application.properties b/02-todo-web-application-h2/target/classes/application.properties new file mode 100644 index 0000000..bff8631 --- /dev/null +++ b/02-todo-web-application-h2/target/classes/application.properties @@ -0,0 +1,7 @@ +spring.mvc.view.prefix=/WEB-INF/jsp/ +spring.mvc.view.suffix=.jsp +logging.level.org.springframework.web=INFO + +spring.jpa.show-sql=true +spring.h2.console.enabled=true +spring.h2.console.settings.web-allow-others=true \ No newline at end of file diff --git a/02-todo-web-application-h2/target/classes/com/in28minutes/springboot/web/EnvironmentConfigurationLogger.class b/02-todo-web-application-h2/target/classes/com/in28minutes/springboot/web/EnvironmentConfigurationLogger.class new file mode 100644 index 0000000..1d23e62 Binary files /dev/null and b/02-todo-web-application-h2/target/classes/com/in28minutes/springboot/web/EnvironmentConfigurationLogger.class differ diff --git a/02-todo-web-application-h2/target/classes/com/in28minutes/springboot/web/SpringBootFirstWebApplication.class b/02-todo-web-application-h2/target/classes/com/in28minutes/springboot/web/SpringBootFirstWebApplication.class new file mode 100644 index 0000000..09ec1e1 Binary files /dev/null and b/02-todo-web-application-h2/target/classes/com/in28minutes/springboot/web/SpringBootFirstWebApplication.class differ diff --git a/02-todo-web-application-h2/target/classes/com/in28minutes/springboot/web/controller/ErrorController.class b/02-todo-web-application-h2/target/classes/com/in28minutes/springboot/web/controller/ErrorController.class new file mode 100644 index 0000000..1071f5c Binary files /dev/null and b/02-todo-web-application-h2/target/classes/com/in28minutes/springboot/web/controller/ErrorController.class differ diff --git a/02-todo-web-application-h2/target/classes/com/in28minutes/springboot/web/controller/LogoutController.class b/02-todo-web-application-h2/target/classes/com/in28minutes/springboot/web/controller/LogoutController.class new file mode 100644 index 0000000..0e443fc Binary files /dev/null and b/02-todo-web-application-h2/target/classes/com/in28minutes/springboot/web/controller/LogoutController.class differ diff --git a/02-todo-web-application-h2/target/classes/com/in28minutes/springboot/web/controller/TodoController.class b/02-todo-web-application-h2/target/classes/com/in28minutes/springboot/web/controller/TodoController.class new file mode 100644 index 0000000..2c0bd7d Binary files /dev/null and b/02-todo-web-application-h2/target/classes/com/in28minutes/springboot/web/controller/TodoController.class differ diff --git a/02-todo-web-application-h2/target/classes/com/in28minutes/springboot/web/controller/WelcomeController.class b/02-todo-web-application-h2/target/classes/com/in28minutes/springboot/web/controller/WelcomeController.class new file mode 100644 index 0000000..236f1e7 Binary files /dev/null and b/02-todo-web-application-h2/target/classes/com/in28minutes/springboot/web/controller/WelcomeController.class differ diff --git a/02-todo-web-application-h2/target/classes/com/in28minutes/springboot/web/model/Todo.class b/02-todo-web-application-h2/target/classes/com/in28minutes/springboot/web/model/Todo.class new file mode 100644 index 0000000..ea05df5 Binary files /dev/null and b/02-todo-web-application-h2/target/classes/com/in28minutes/springboot/web/model/Todo.class differ diff --git a/02-todo-web-application-h2/target/classes/com/in28minutes/springboot/web/security/SecurityConfiguration.class b/02-todo-web-application-h2/target/classes/com/in28minutes/springboot/web/security/SecurityConfiguration.class new file mode 100644 index 0000000..abff7b5 Binary files /dev/null and b/02-todo-web-application-h2/target/classes/com/in28minutes/springboot/web/security/SecurityConfiguration.class differ diff --git a/02-todo-web-application-h2/target/classes/com/in28minutes/springboot/web/service/TodoRepository.class b/02-todo-web-application-h2/target/classes/com/in28minutes/springboot/web/service/TodoRepository.class new file mode 100644 index 0000000..2ec3721 Binary files /dev/null and b/02-todo-web-application-h2/target/classes/com/in28minutes/springboot/web/service/TodoRepository.class differ diff --git a/02-todo-web-application-h2/target/classes/data.sql b/02-todo-web-application-h2/target/classes/data.sql new file mode 100644 index 0000000..2f16341 --- /dev/null +++ b/02-todo-web-application-h2/target/classes/data.sql @@ -0,0 +1,6 @@ +insert into TODO +values(10001, 'Learn Spring Boot', false, sysdate(), 'in28minutes'); +insert into TODO +values(10002, 'Learn Angular JS', false, sysdate(), 'in28minutes'); +insert into TODO +values(10003, 'Learn to Dance', false, sysdate(), 'in28minutes'); diff --git a/02-todo-web-application-h2/target/test-classes/com/in28minutes/springboot/web/SpringBootFirstWebApplicationTests.class b/02-todo-web-application-h2/target/test-classes/com/in28minutes/springboot/web/SpringBootFirstWebApplicationTests.class new file mode 100644 index 0000000..450afd4 Binary files /dev/null and b/02-todo-web-application-h2/target/test-classes/com/in28minutes/springboot/web/SpringBootFirstWebApplicationTests.class differ diff --git a/03-todo-web-application-mysql/.DS_Store b/03-todo-web-application-mysql/.DS_Store new file mode 100644 index 0000000..d0799f7 Binary files /dev/null and b/03-todo-web-application-mysql/.DS_Store differ diff --git a/03-todo-web-application-mysql/Dockerfile b/03-todo-web-application-mysql/Dockerfile new file mode 100644 index 0000000..b07ac12 --- /dev/null +++ b/03-todo-web-application-mysql/Dockerfile @@ -0,0 +1,4 @@ +From tomcat:8.0.51-jre8-alpine +RUN rm -rf /usr/local/tomcat/webapps/* +COPY ./target/*.war /usr/local/tomcat/webapps/ROOT.war +CMD ["catalina.sh","run"] \ No newline at end of file diff --git a/03-todo-web-application-mysql/backup/00-generated-by-kompose/mysql-database-data-volume-persistentvolumeclaim-original-generated-by-kompose.yaml b/03-todo-web-application-mysql/backup/00-generated-by-kompose/mysql-database-data-volume-persistentvolumeclaim-original-generated-by-kompose.yaml new file mode 100644 index 0000000..0db8c5b --- /dev/null +++ b/03-todo-web-application-mysql/backup/00-generated-by-kompose/mysql-database-data-volume-persistentvolumeclaim-original-generated-by-kompose.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + creationTimestamp: null + labels: + io.kompose.service: mysql-database-data-volume + name: mysql-database-data-volume +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 100Mi +status: {} diff --git a/03-todo-web-application-mysql/backup/00-generated-by-kompose/mysql-deployment-original-generated-by-kompose.yaml b/03-todo-web-application-mysql/backup/00-generated-by-kompose/mysql-deployment-original-generated-by-kompose.yaml new file mode 100644 index 0000000..555da3c --- /dev/null +++ b/03-todo-web-application-mysql/backup/00-generated-by-kompose/mysql-deployment-original-generated-by-kompose.yaml @@ -0,0 +1,47 @@ +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + annotations: + kompose.cmd: kompose convert + kompose.version: 1.19.0 () + creationTimestamp: null + labels: + io.kompose.service: mysql + name: mysql +spec: + replicas: 1 + strategy: + type: Recreate + template: + metadata: + annotations: + kompose.cmd: kompose convert + kompose.version: 1.19.0 () + creationTimestamp: null + labels: + io.kompose.service: mysql + spec: + containers: + - env: + - name: MYSQL_DATABASE + value: todos + - name: MYSQL_PASSWORD + value: dummytodos + - name: MYSQL_ROOT_PASSWORD + value: dummypassword + - name: MYSQL_USER + value: todos-user + image: mysql:5.7 + name: mysql + ports: + - containerPort: 3306 + resources: {} + volumeMounts: + - mountPath: /var/lib/mysql + name: mysql-database-data-volume + restartPolicy: Always + volumes: + - name: mysql-database-data-volume + persistentVolumeClaim: + claimName: mysql-database-data-volume +status: {} diff --git a/03-todo-web-application-mysql/backup/00-generated-by-kompose/mysql-service-original-generated-by-kompose.yaml b/03-todo-web-application-mysql/backup/00-generated-by-kompose/mysql-service-original-generated-by-kompose.yaml new file mode 100644 index 0000000..4c09bd9 --- /dev/null +++ b/03-todo-web-application-mysql/backup/00-generated-by-kompose/mysql-service-original-generated-by-kompose.yaml @@ -0,0 +1,19 @@ +apiVersion: v1 +kind: Service +metadata: + annotations: + kompose.cmd: kompose convert + kompose.version: 1.19.0 () + creationTimestamp: null + labels: + io.kompose.service: mysql + name: mysql +spec: + ports: + - name: "3306" + port: 3306 + targetPort: 3306 + selector: + io.kompose.service: mysql +status: + loadBalancer: {} diff --git a/03-todo-web-application-mysql/backup/00-generated-by-kompose/todo-web-application-deployment-original-generated-by-kompose.yaml b/03-todo-web-application-mysql/backup/00-generated-by-kompose/todo-web-application-deployment-original-generated-by-kompose.yaml new file mode 100644 index 0000000..3dd01c6 --- /dev/null +++ b/03-todo-web-application-mysql/backup/00-generated-by-kompose/todo-web-application-deployment-original-generated-by-kompose.yaml @@ -0,0 +1,41 @@ +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + annotations: + kompose.cmd: kompose convert + kompose.version: 1.19.0 () + creationTimestamp: null + labels: + io.kompose.service: todo-web-application + name: todo-web-application +spec: + replicas: 1 + strategy: {} + template: + metadata: + annotations: + kompose.cmd: kompose convert + kompose.version: 1.19.0 () + creationTimestamp: null + labels: + io.kompose.service: todo-web-application + spec: + containers: + - env: + - name: RDS_DB_NAME + value: todos + - name: RDS_HOSTNAME + value: mysql + - name: RDS_PASSWORD + value: dummytodos + - name: RDS_PORT + value: "3306" + - name: RDS_USERNAME + value: todos-user + image: in28min/todo-web-application-mysql:0.0.1-SNAPSHOT + name: todo-web-application + ports: + - containerPort: 8080 + resources: {} + restartPolicy: Always +status: {} diff --git a/03-todo-web-application-mysql/backup/00-generated-by-kompose/todo-web-application-service-original-generated-by-kompose.yaml b/03-todo-web-application-mysql/backup/00-generated-by-kompose/todo-web-application-service-original-generated-by-kompose.yaml new file mode 100644 index 0000000..a464170 --- /dev/null +++ b/03-todo-web-application-mysql/backup/00-generated-by-kompose/todo-web-application-service-original-generated-by-kompose.yaml @@ -0,0 +1,19 @@ +apiVersion: v1 +kind: Service +metadata: + annotations: + kompose.cmd: kompose convert + kompose.version: 1.19.0 () + creationTimestamp: null + labels: + io.kompose.service: todo-web-application + name: todo-web-application +spec: + ports: + - name: "8080" + port: 8080 + targetPort: 8080 + selector: + io.kompose.service: todo-web-application +status: + loadBalancer: {} diff --git a/03-todo-web-application-mysql/backup/01-updated-manually/mysql-database-data-volume-persistentvolumeclaim.yaml b/03-todo-web-application-mysql/backup/01-updated-manually/mysql-database-data-volume-persistentvolumeclaim.yaml new file mode 100644 index 0000000..0db8c5b --- /dev/null +++ b/03-todo-web-application-mysql/backup/01-updated-manually/mysql-database-data-volume-persistentvolumeclaim.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + creationTimestamp: null + labels: + io.kompose.service: mysql-database-data-volume + name: mysql-database-data-volume +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 100Mi +status: {} diff --git a/03-todo-web-application-mysql/backup/01-updated-manually/mysql-deployment.yaml b/03-todo-web-application-mysql/backup/01-updated-manually/mysql-deployment.yaml new file mode 100644 index 0000000..03258ea --- /dev/null +++ b/03-todo-web-application-mysql/backup/01-updated-manually/mysql-deployment.yaml @@ -0,0 +1,39 @@ +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + labels: + io.kompose.service: mysql + name: mysql +spec: + replicas: 1 + strategy: + type: Recreate + template: + metadata: + labels: + io.kompose.service: mysql + spec: + containers: + - env: + - name: MYSQL_DATABASE + value: todos + - name: MYSQL_PASSWORD + value: dummytodos + - name: MYSQL_ROOT_PASSWORD + value: dummypassword + - name: MYSQL_USER + value: todos-user + image: mysql:5.7 + args: + - "--ignore-db-dir=lost+found" #CHANGE + name: mysql + ports: + - containerPort: 3306 + volumeMounts: + - mountPath: /var/lib/mysql + name: mysql-database-data-volume + restartPolicy: Always + volumes: + - name: mysql-database-data-volume + persistentVolumeClaim: + claimName: mysql-database-data-volume \ No newline at end of file diff --git a/03-todo-web-application-mysql/backup/01-updated-manually/mysql-service.yaml b/03-todo-web-application-mysql/backup/01-updated-manually/mysql-service.yaml new file mode 100644 index 0000000..373b2f9 --- /dev/null +++ b/03-todo-web-application-mysql/backup/01-updated-manually/mysql-service.yaml @@ -0,0 +1,18 @@ +apiVersion: v1 +kind: Service +metadata: + annotations: + kompose.cmd: kompose convert + kompose.version: 1.19.0 () + creationTimestamp: null + labels: + io.kompose.service: mysql + name: mysql +spec: + type: LoadBalancer + ports: + - name: "3306" + port: 3306 + targetPort: 3306 + selector: + io.kompose.service: mysql diff --git a/03-todo-web-application-mysql/backup/01-updated-manually/todo-web-application-deployment.yaml b/03-todo-web-application-mysql/backup/01-updated-manually/todo-web-application-deployment.yaml new file mode 100644 index 0000000..4b9116f --- /dev/null +++ b/03-todo-web-application-mysql/backup/01-updated-manually/todo-web-application-deployment.yaml @@ -0,0 +1,30 @@ +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + labels: + io.kompose.service: todo-web-application + name: todo-web-application +spec: + replicas: 1 + template: + metadata: + labels: + io.kompose.service: todo-web-application + spec: + containers: + - env: + - name: RDS_DB_NAME + value: todos + - name: RDS_HOSTNAME + value: mysql + - name: RDS_PASSWORD + value: dummytodos + - name: RDS_PORT + value: "3306" + - name: RDS_USERNAME + value: todos-user + image: in28min/todo-web-application-mysql:0.0.1-SNAPSHOT + name: todo-web-application + ports: + - containerPort: 8080 + restartPolicy: Always diff --git a/03-todo-web-application-mysql/backup/01-updated-manually/todo-web-application-service.yaml b/03-todo-web-application-mysql/backup/01-updated-manually/todo-web-application-service.yaml new file mode 100644 index 0000000..239fff6 --- /dev/null +++ b/03-todo-web-application-mysql/backup/01-updated-manually/todo-web-application-service.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: Service +metadata: + labels: + io.kompose.service: todo-web-application + name: todo-web-application +spec: + type: LoadBalancer + ports: + - name: "8080" + port: 8080 + targetPort: 8080 + selector: + io.kompose.service: todo-web-application diff --git a/03-todo-web-application-mysql/backup/02-final-backup-at-end-of-course/config-map.yaml b/03-todo-web-application-mysql/backup/02-final-backup-at-end-of-course/config-map.yaml new file mode 100644 index 0000000..13c6372 --- /dev/null +++ b/03-todo-web-application-mysql/backup/02-final-backup-at-end-of-course/config-map.yaml @@ -0,0 +1,10 @@ +apiVersion: v1 +data: + RDS_DB_NAME: todos + RDS_HOSTNAME: mysql + RDS_PORT: "3306" + RDS_USERNAME: todos-user +kind: ConfigMap +metadata: + name: todo-web-application-config + namespace: default \ No newline at end of file diff --git a/03-todo-web-application-mysql/backup/02-final-backup-at-end-of-course/mysql-database-data-volume-persistentvolumeclaim-aws.yaml b/03-todo-web-application-mysql/backup/02-final-backup-at-end-of-course/mysql-database-data-volume-persistentvolumeclaim-aws.yaml new file mode 100644 index 0000000..aa69178 --- /dev/null +++ b/03-todo-web-application-mysql/backup/02-final-backup-at-end-of-course/mysql-database-data-volume-persistentvolumeclaim-aws.yaml @@ -0,0 +1,22 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: mysql-database-data-volume + labels: + io.kompose.service: mysql-database-data-volume +spec: + storageClassName: mysql-database-ssd + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 100Mi +status: {} +--- +apiVersion: storage.k8s.io/v1 +kind: StorageClass +metadata: + name: mysql-database-ssd +provisioner: kubernetes.io/aws-ebs +parameters: + type: gp2 \ No newline at end of file diff --git a/03-todo-web-application-mysql/backup/02-final-backup-at-end-of-course/mysql-database-data-volume-persistentvolumeclaim.yaml b/03-todo-web-application-mysql/backup/02-final-backup-at-end-of-course/mysql-database-data-volume-persistentvolumeclaim.yaml new file mode 100644 index 0000000..0db8c5b --- /dev/null +++ b/03-todo-web-application-mysql/backup/02-final-backup-at-end-of-course/mysql-database-data-volume-persistentvolumeclaim.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + creationTimestamp: null + labels: + io.kompose.service: mysql-database-data-volume + name: mysql-database-data-volume +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 100Mi +status: {} diff --git a/03-todo-web-application-mysql/backup/02-final-backup-at-end-of-course/mysql-deployment.yaml b/03-todo-web-application-mysql/backup/02-final-backup-at-end-of-course/mysql-deployment.yaml new file mode 100644 index 0000000..03258ea --- /dev/null +++ b/03-todo-web-application-mysql/backup/02-final-backup-at-end-of-course/mysql-deployment.yaml @@ -0,0 +1,39 @@ +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + labels: + io.kompose.service: mysql + name: mysql +spec: + replicas: 1 + strategy: + type: Recreate + template: + metadata: + labels: + io.kompose.service: mysql + spec: + containers: + - env: + - name: MYSQL_DATABASE + value: todos + - name: MYSQL_PASSWORD + value: dummytodos + - name: MYSQL_ROOT_PASSWORD + value: dummypassword + - name: MYSQL_USER + value: todos-user + image: mysql:5.7 + args: + - "--ignore-db-dir=lost+found" #CHANGE + name: mysql + ports: + - containerPort: 3306 + volumeMounts: + - mountPath: /var/lib/mysql + name: mysql-database-data-volume + restartPolicy: Always + volumes: + - name: mysql-database-data-volume + persistentVolumeClaim: + claimName: mysql-database-data-volume \ No newline at end of file diff --git a/03-todo-web-application-mysql/backup/02-final-backup-at-end-of-course/mysql-service.yaml b/03-todo-web-application-mysql/backup/02-final-backup-at-end-of-course/mysql-service.yaml new file mode 100644 index 0000000..3510da0 --- /dev/null +++ b/03-todo-web-application-mysql/backup/02-final-backup-at-end-of-course/mysql-service.yaml @@ -0,0 +1,18 @@ +apiVersion: v1 +kind: Service +metadata: + annotations: + kompose.cmd: kompose convert + kompose.version: 1.19.0 () + creationTimestamp: null + labels: + io.kompose.service: mysql + name: mysql +spec: + type: ClusterIP + ports: + - name: "3306" + port: 3306 + targetPort: 3306 + selector: + io.kompose.service: mysql \ No newline at end of file diff --git a/03-todo-web-application-mysql/backup/02-final-backup-at-end-of-course/secret.yaml b/03-todo-web-application-mysql/backup/02-final-backup-at-end-of-course/secret.yaml new file mode 100644 index 0000000..ef3cabe --- /dev/null +++ b/03-todo-web-application-mysql/backup/02-final-backup-at-end-of-course/secret.yaml @@ -0,0 +1,10 @@ +apiVersion: v1 +kind: Secret +data: + RDS_PASSWORD: ZHVtbXl0b2Rvcw== +metadata: + name: todo-web-application-secrets + namespace: default +type: Opaque +# kubectl create secret generic todo-web-application-secrets --from-literal=RDS_PASSWORD=dummytodos +# echo -n dummytodos | base64 \ No newline at end of file diff --git a/03-todo-web-application-mysql/backup/02-final-backup-at-end-of-course/todo-web-application-deployment.yaml b/03-todo-web-application-mysql/backup/02-final-backup-at-end-of-course/todo-web-application-deployment.yaml new file mode 100644 index 0000000..f6e4a21 --- /dev/null +++ b/03-todo-web-application-mysql/backup/02-final-backup-at-end-of-course/todo-web-application-deployment.yaml @@ -0,0 +1,46 @@ +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + labels: + io.kompose.service: todo-web-application + name: todo-web-application +spec: + replicas: 1 + template: + metadata: + labels: + io.kompose.service: todo-web-application + spec: + containers: + - env: + - name: RDS_DB_NAME +# value: todos + valueFrom: + configMapKeyRef: + key: RDS_DB_NAME + name: todo-web-application-config + - name: RDS_HOSTNAME + valueFrom: + configMapKeyRef: + key: RDS_HOSTNAME + name: todo-web-application-config + - name: RDS_PASSWORD + valueFrom: + secretKeyRef: + key: RDS_PASSWORD + name: todo-web-application-secrets + - name: RDS_PORT + valueFrom: + configMapKeyRef: + key: RDS_PORT + name: todo-web-application-config + - name: RDS_USERNAME + valueFrom: + configMapKeyRef: + key: RDS_USERNAME + name: todo-web-application-config + image: in28min/todo-web-application-mysql:0.0.1-SNAPSHOT + name: todo-web-application + ports: + - containerPort: 8080 + restartPolicy: Always diff --git a/03-todo-web-application-mysql/backup/02-final-backup-at-end-of-course/todo-web-application-service.yaml b/03-todo-web-application-mysql/backup/02-final-backup-at-end-of-course/todo-web-application-service.yaml new file mode 100644 index 0000000..239fff6 --- /dev/null +++ b/03-todo-web-application-mysql/backup/02-final-backup-at-end-of-course/todo-web-application-service.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: Service +metadata: + labels: + io.kompose.service: todo-web-application + name: todo-web-application +spec: + type: LoadBalancer + ports: + - name: "8080" + port: 8080 + targetPort: 8080 + selector: + io.kompose.service: todo-web-application diff --git a/03-todo-web-application-mysql/backup/backup-mid-course-03-mysql.zip b/03-todo-web-application-mysql/backup/backup-mid-course-03-mysql.zip new file mode 100644 index 0000000..688a1c6 Binary files /dev/null and b/03-todo-web-application-mysql/backup/backup-mid-course-03-mysql.zip differ diff --git a/03-todo-web-application-mysql/docker-compose.yml b/03-todo-web-application-mysql/docker-compose.yml new file mode 100644 index 0000000..f6d6cc3 --- /dev/null +++ b/03-todo-web-application-mysql/docker-compose.yml @@ -0,0 +1,42 @@ +version: '3.7' +# Removed subprocess.CalledProcessError: Command '['/usr/local/bin/docker-credential-desktop', 'get']' returned non-zero exit status 1 +# I had this: +# cat ~/.docker/config.json +# {"auths":{},"credsStore":"", "credsStore":"desktop","stackOrchestrator":"swarm"} +# I updated to this: +# {"auths":{},"credsStore":"","stackOrchestrator":"swarm"} +services: + todo-web-application: + image: in28min/todo-web-application-mysql:0.0.1-SNAPSHOT + #build: + #context: . + #dockerfile: Dockerfile + ports: + - "8080:8080" + restart: always + depends_on: # Start the depends_on first + - mysql + environment: + RDS_HOSTNAME: mysql + RDS_PORT: 3306 + RDS_DB_NAME: todos + RDS_USERNAME: todos-user + RDS_PASSWORD: dummytodos + + mysql: + image: mysql:5.7 + ports: + - "3306:3306" + restart: always + environment: + MYSQL_ROOT_PASSWORD: root + MYSQL_ROOT_PASSWORD: dummypassword + MYSQL_USER: todos-user + MYSQL_PASSWORD: dummytodos + MYSQL_DATABASE: todos + volumes: + - mysql-database-data-volume:/var/lib/mysql + +# Volumes +volumes: + mysql-database-data-volume: diff --git a/03-todo-web-application-mysql/pom.xml b/03-todo-web-application-mysql/pom.xml new file mode 100644 index 0000000..06f5f2f --- /dev/null +++ b/03-todo-web-application-mysql/pom.xml @@ -0,0 +1,158 @@ + + + 4.0.0 + + com.in28minutes.springboot.web + 03-todo-web-application-mysql + 0.0.1-SNAPSHOT + war + todo-web-application-mysql + + Demo project for Spring Boot + + + org.springframework.boot + spring-boot-starter-parent + 2.1.7.RELEASE + + + + + UTF-8 + UTF-8 + 1.8 + 3.1.1 + + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-actuator + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + + com.h2database + h2 + test + + + + mysql + mysql-connector-java + + + + org.springframework.boot + spring-boot-starter-security + + + + javax.servlet + jstl + + + + org.webjars + bootstrap + 3.3.6 + + + + org.webjars + bootstrap-datepicker + 1.0.1 + + + + org.webjars + jquery + 1.9.1 + + + + org.apache.tomcat.embed + tomcat-embed-jasper + provided + + + + org.springframework.boot + spring-boot-starter-tomcat + provided + + + + org.springframework.boot + spring-boot-devtools + runtime + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + todo-web-application-mysql + + + org.springframework.boot + spring-boot-maven-plugin + + + + + com.spotify + dockerfile-maven-plugin + 1.4.6 + + + default + + build + + + + + + + in28min/${project.name} + ${project.version} + true + + + + + + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestones + + + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestones + + + + \ No newline at end of file diff --git a/03-todo-web-application-mysql/readme.md b/03-todo-web-application-mysql/readme.md new file mode 100644 index 0000000..29c05ab --- /dev/null +++ b/03-todo-web-application-mysql/readme.md @@ -0,0 +1,143 @@ +# Todo Web Application using Spring Boot and MySQL as Database + +Run com.in28minutes.springboot.web.SpringBootFirstWebApplication as a Java Application. + +Runs on default port of Spring Boot - 8080 + +Application uses h2 database to run the tests. + + +## Can be run as a Jar or a WAR + +`mvn clean install` generate a war which can deployed to your favorite web server. + +We will deploy to Cloud as a WAR + +## Web Application + +- http://localhost:8080/login with in28minutes/dummy as credentials +- You can add, delete and update your todos +- Spring Security is used to secure the application +- `com.in28minutes.springboot.web.security.SecurityConfiguration` contains the in memory security credential configuration. + + +## KOMPOSE +Installation - https://kompose.io/installation/ + +``` +args: #https://github.com/docker-library/mysql/issues/69 + - "--ignore-db-dir=lost+found" #CHANGE +``` +## Changes from H2 Application + +#### pom.xml + +``` + + com.h2database + h2 + test + + + mysql + mysql-connector-java + +``` + +#### src/main/resources/application.properties + +``` +#spring.h2.console.enabled=true +#spring.h2.console.settings.web-allow-others=true + +spring.jpa.hibernate.ddl-auto=update +spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver +spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL55Dialect +spring.datasource.url=jdbc:mysql://localhost:3306/todos +spring.datasource.username=todos-user +spring.datasource.password=dummytodos +``` + +#### src/test/resources/application.properties + +``` +spring.jpa.hibernate.ddl-auto=create-drop +spring.datasource.driver-class-name=org.h2.Driver +spring.datasource.url=jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1 +spring.datasource.username=sa +spring.datasource.password=sa +``` + +#### public class Todo + +``` +@Size(min=10, message="Enter at least 10 Characters...") +@Column(name="description") +private String desc; +``` +## My SQL + +### Launching MySQL using Docker + +``` +docker run --detach --env MYSQL_ROOT_PASSWORD=dummypassword --env MYSQL_USER=todos-user --env MYSQL_PASSWORD=dummytodos --env MYSQL_DATABASE=todos --name mysql --publish 3306:3306 mysql:5.7 +``` + +### Launching Web App using Docker + +Using Link + +``` +docker container run -p 8080:8080 --link=mysql -e RDS_HOSTNAME=mysql in28min/todo-web-application-mysql:0.0.1-SNAPSHOT +``` + + +### My SQL Shell Client + +- https://dev.mysql.com/downloads/shell/ + +- Install on mac using `brew install caskroom/cask/mysql-shell`. + + +``` +Rangas-MacBook-Air:projects in28min$ mysqlsh +MySQL Shell 8.0.15 +Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved. +Oracle is a registered trademark of Oracle Corporation and/or its affiliates. +Other names may be trademarks of their respective owners. + +Type '\help' or '\?' for help; '\quit' to exit. + +MySQL JS > \connect todos-user@localhost:3306 +Creating a session to 'todos-user@localhost:3306' +Please provide the password for 'todos-user@localhost:3306': +Save password for 'todos-user@localhost:3306'? [Y]es/[N]o/Ne[v]er (default No): v +Fetching schema names for autocompletion... Press ^C to stop. +Your MySQL connection id is 37 +Server version: 5.7.26 MySQL Community Server (GPL) +No default schema selected; type \use to set one. + + MySQL localhost:3306 ssl JS > \sql +Switching to SQL mode... Commands end with ; + + MySQL localhost:3306 ssl SQL > use todos +Default schema set to `todos`. +Fetching table and column names from `todos` for auto-completion... Press ^C to stop. + + MySQL localhost:3306 ssl todos SQL > select * from todo ; ++----+--------------+---------+----------------------------+-------------+ +| id | description | is_done | target_date | user | ++----+--------------+---------+----------------------------+-------------+ +| 1 | Default Desc | 0 | 2019-06-26 18:30:00.000000 | in28minutes | ++----+--------------+---------+----------------------------+-------------+ +1 row in set (0.0032 sec) + +``` + +### Create Todo Table for Production + +``` +create table hibernate_sequence (next_val bigint) engine=InnoDB +insert into hibernate_sequence values ( 1 ) +create table todo (id integer not null, description varchar(255), is_done bit not null, target_date datetime(6), user varchar(255), primary key (id)) engine=InnoDB +``` \ No newline at end of file diff --git a/03-todo-web-application-mysql/src/main/java/com/in28minutes/springboot/web/EnvironmentConfigurationLogger.java b/03-todo-web-application-mysql/src/main/java/com/in28minutes/springboot/web/EnvironmentConfigurationLogger.java new file mode 100644 index 0000000..be07275 --- /dev/null +++ b/03-todo-web-application-mysql/src/main/java/com/in28minutes/springboot/web/EnvironmentConfigurationLogger.java @@ -0,0 +1,42 @@ +package com.in28minutes.springboot.web; + +import java.util.Arrays; +import java.util.stream.StreamSupport; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.context.event.ContextRefreshedEvent; +import org.springframework.context.event.EventListener; +import org.springframework.core.env.AbstractEnvironment; +import org.springframework.core.env.EnumerablePropertySource; +import org.springframework.core.env.Environment; +import org.springframework.core.env.MutablePropertySources; +import org.springframework.stereotype.Component; + +@Component +public class EnvironmentConfigurationLogger { + + private static final Logger LOGGER = LoggerFactory.getLogger(EnvironmentConfigurationLogger.class); + + @SuppressWarnings("rawtypes") + @EventListener + public void handleContextRefresh(ContextRefreshedEvent event) { + final Environment environment = event.getApplicationContext().getEnvironment(); + LOGGER.info("====== Environment and configuration ======"); + LOGGER.info("Active profiles: {}", Arrays.toString(environment.getActiveProfiles())); + final MutablePropertySources sources = ((AbstractEnvironment) environment).getPropertySources(); + StreamSupport.stream(sources.spliterator(), false).filter(ps -> ps instanceof EnumerablePropertySource) + .map(ps -> ((EnumerablePropertySource) ps).getPropertyNames()).flatMap(Arrays::stream).distinct() + .forEach(prop -> { + Object resolved = environment.getProperty(prop, Object.class); + if (resolved instanceof String) { + LOGGER.info("{} - {}", prop, environment.getProperty(prop)); + } else { + LOGGER.info("{} - {}", prop, "NON-STRING-VALUE"); + } + + }); + LOGGER.debug("==========================================="); + } + +} diff --git a/03-todo-web-application-mysql/src/main/java/com/in28minutes/springboot/web/SpringBootFirstWebApplication.java b/03-todo-web-application-mysql/src/main/java/com/in28minutes/springboot/web/SpringBootFirstWebApplication.java new file mode 100644 index 0000000..2aa239b --- /dev/null +++ b/03-todo-web-application-mysql/src/main/java/com/in28minutes/springboot/web/SpringBootFirstWebApplication.java @@ -0,0 +1,22 @@ +package com.in28minutes.springboot.web; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; +import org.springframework.context.annotation.ComponentScan; + +@SpringBootApplication +@ComponentScan("com.in28minutes.springboot.web") +public class SpringBootFirstWebApplication extends SpringBootServletInitializer { + + @Override + protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { + return application.sources(SpringBootFirstWebApplication.class); + } + + public static void main(String[] args) { + SpringApplication.run(SpringBootFirstWebApplication.class, args); + } + +} diff --git a/03-todo-web-application-mysql/src/main/java/com/in28minutes/springboot/web/controller/ErrorController.java b/03-todo-web-application-mysql/src/main/java/com/in28minutes/springboot/web/controller/ErrorController.java new file mode 100644 index 0000000..79e0956 --- /dev/null +++ b/03-todo-web-application-mysql/src/main/java/com/in28minutes/springboot/web/controller/ErrorController.java @@ -0,0 +1,24 @@ +package com.in28minutes.springboot.web.controller; + +import javax.servlet.http.HttpServletRequest; + +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.servlet.ModelAndView; + +@Controller("error") +public class ErrorController { + + @ExceptionHandler(Exception.class) + public ModelAndView handleException + (HttpServletRequest request, Exception ex){ + ModelAndView mv = new ModelAndView(); + + mv.addObject("exception", ex.getLocalizedMessage()); + mv.addObject("url", request.getRequestURL()); + + mv.setViewName("error"); + return mv; + } + +} diff --git a/03-todo-web-application-mysql/src/main/java/com/in28minutes/springboot/web/controller/LogoutController.java b/03-todo-web-application-mysql/src/main/java/com/in28minutes/springboot/web/controller/LogoutController.java new file mode 100644 index 0000000..5f426a6 --- /dev/null +++ b/03-todo-web-application-mysql/src/main/java/com/in28minutes/springboot/web/controller/LogoutController.java @@ -0,0 +1,30 @@ +package com.in28minutes.springboot.web.controller; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; + +@Controller +public class LogoutController { + + @RequestMapping(value = "/logout", method = RequestMethod.GET) + public String logout(HttpServletRequest request, + HttpServletResponse response) { + + Authentication authentication = SecurityContextHolder.getContext() + .getAuthentication(); + + if (authentication != null) { + new SecurityContextLogoutHandler().logout(request, response, + authentication); + } + + return "redirect:/"; + } +} diff --git a/03-todo-web-application-mysql/src/main/java/com/in28minutes/springboot/web/controller/TodoController.java b/03-todo-web-application-mysql/src/main/java/com/in28minutes/springboot/web/controller/TodoController.java new file mode 100644 index 0000000..75489e0 --- /dev/null +++ b/03-todo-web-application-mysql/src/main/java/com/in28minutes/springboot/web/controller/TodoController.java @@ -0,0 +1,111 @@ +package com.in28minutes.springboot.web.controller; + +import java.text.SimpleDateFormat; +import java.util.Date; + +import javax.validation.Valid; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.propertyeditors.CustomDateEditor; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.stereotype.Controller; +import org.springframework.ui.ModelMap; +import org.springframework.validation.BindingResult; +import org.springframework.web.bind.WebDataBinder; +import org.springframework.web.bind.annotation.InitBinder; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; + +import com.in28minutes.springboot.web.model.Todo; +import com.in28minutes.springboot.web.service.TodoRepository; + +@Controller +public class TodoController { + + @Autowired + TodoRepository repository; + + @InitBinder + public void initBinder(WebDataBinder binder) { + // Date - dd/MM/yyyy + SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy"); + binder.registerCustomEditor(Date.class, new CustomDateEditor( + dateFormat, false)); + } + + @RequestMapping(value = "/list-todos", method = RequestMethod.GET) + public String showTodos(ModelMap model) { + String name = getLoggedInUserName(model); + model.put("todos", repository.findByUser(name)); + //model.put("todos", service.retrieveTodos(name)); + return "list-todos"; + } + + private String getLoggedInUserName(ModelMap model) { + Object principal = SecurityContextHolder.getContext() + .getAuthentication().getPrincipal(); + + if (principal instanceof UserDetails) { + return ((UserDetails) principal).getUsername(); + } + + return principal.toString(); + } + + @RequestMapping(value = "/add-todo", method = RequestMethod.GET) + public String showAddTodoPage(ModelMap model) { + model.addAttribute("todo", new Todo(0, getLoggedInUserName(model), + "Default Desc", new Date(), false)); + return "todo"; + } + + @RequestMapping(value = "/delete-todo", method = RequestMethod.GET) + public String deleteTodo(@RequestParam int id) { + + //if(id==1) + //throw new RuntimeException("Something went wrong"); + repository.deleteById(id); + //service.deleteTodo(id); + return "redirect:/list-todos"; + } + + @RequestMapping(value = "/update-todo", method = RequestMethod.GET) + public String showUpdateTodoPage(@RequestParam int id, ModelMap model) { + Todo todo = repository.findById(id).get(); + //Todo todo = service.retrieveTodo(id); + model.put("todo", todo); + return "todo"; + } + + @RequestMapping(value = "/update-todo", method = RequestMethod.POST) + public String updateTodo(ModelMap model, @Valid Todo todo, + BindingResult result) { + + if (result.hasErrors()) { + return "todo"; + } + + todo.setUser(getLoggedInUserName(model)); + + repository.save(todo); + //service.updateTodo(todo); + + return "redirect:/list-todos"; + } + + @RequestMapping(value = "/add-todo", method = RequestMethod.POST) + public String addTodo(ModelMap model, @Valid Todo todo, BindingResult result) { + + if (result.hasErrors()) { + return "todo"; + } + + todo.setUser(getLoggedInUserName(model)); + repository.save(todo); + /*service.addTodo(getLoggedInUserName(model), todo.getDesc(), todo.getTargetDate(), + false);*/ + return "redirect:/list-todos"; + } +} diff --git a/03-todo-web-application-mysql/src/main/java/com/in28minutes/springboot/web/controller/WelcomeController.java b/03-todo-web-application-mysql/src/main/java/com/in28minutes/springboot/web/controller/WelcomeController.java new file mode 100644 index 0000000..da5018c --- /dev/null +++ b/03-todo-web-application-mysql/src/main/java/com/in28minutes/springboot/web/controller/WelcomeController.java @@ -0,0 +1,30 @@ +package com.in28minutes.springboot.web.controller; + +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.stereotype.Controller; +import org.springframework.ui.ModelMap; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; + +@Controller +public class WelcomeController { + + @RequestMapping(value = "/", method = RequestMethod.GET) + public String showWelcomePage(ModelMap model) { + model.put("name", getLoggedinUserName()); + return "welcome"; + } + + private String getLoggedinUserName() { + Object principal = SecurityContextHolder.getContext() + .getAuthentication().getPrincipal(); + + if (principal instanceof UserDetails) { + return ((UserDetails) principal).getUsername(); + } + + return principal.toString(); + } + +} diff --git a/03-todo-web-application-mysql/src/main/java/com/in28minutes/springboot/web/model/Todo.java b/03-todo-web-application-mysql/src/main/java/com/in28minutes/springboot/web/model/Todo.java new file mode 100644 index 0000000..286302f --- /dev/null +++ b/03-todo-web-application-mysql/src/main/java/com/in28minutes/springboot/web/model/Todo.java @@ -0,0 +1,115 @@ +package com.in28minutes.springboot.web.model; + +import java.util.Date; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.validation.constraints.Size; + +@Entity +public class Todo { + + @Id + @GeneratedValue + private int id; + + private String user; + + @Size(min=10, message="Enter at least 10 Characters...") + @Column(name="description") + private String desc; + + private Date targetDate; + + private boolean isDone; + + public Todo() { + super(); + } + + public Todo(int id, String user, String desc, Date targetDate, + boolean isDone) { + super(); + this.id = id; + this.user = user; + this.desc = desc; + this.targetDate = targetDate; + this.isDone = isDone; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getUser() { + return user; + } + + public void setUser(String user) { + this.user = user; + } + + public String getDesc() { + return desc; + } + + public void setDesc(String desc) { + this.desc = desc; + } + + public Date getTargetDate() { + return targetDate; + } + + public void setTargetDate(Date targetDate) { + this.targetDate = targetDate; + } + + public boolean isDone() { + return isDone; + } + + public void setDone(boolean isDone) { + this.isDone = isDone; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + id; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + Todo other = (Todo) obj; + if (id != other.id) { + return false; + } + return true; + } + + @Override + public String toString() { + return String.format( + "Todo [id=%s, user=%s, desc=%s, targetDate=%s, isDone=%s]", id, + user, desc, targetDate, isDone); + } + +} \ No newline at end of file diff --git a/03-todo-web-application-mysql/src/main/java/com/in28minutes/springboot/web/security/SecurityConfiguration.java b/03-todo-web-application-mysql/src/main/java/com/in28minutes/springboot/web/security/SecurityConfiguration.java new file mode 100644 index 0000000..7d92bd8 --- /dev/null +++ b/03-todo-web-application-mysql/src/main/java/com/in28minutes/springboot/web/security/SecurityConfiguration.java @@ -0,0 +1,28 @@ +package com.in28minutes.springboot.web.security; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; + +@Configuration +public class SecurityConfiguration extends WebSecurityConfigurerAdapter{ + //Create User - in28Minutes/dummy + @Autowired + public void configureGlobalSecurity(AuthenticationManagerBuilder auth) + throws Exception { + auth.inMemoryAuthentication().withUser("in28minutes").password("{noop}dummy") + .roles("USER", "ADMIN"); + } + + @Override + protected void configure(HttpSecurity http) throws Exception { + http.authorizeRequests().antMatchers("/login", "/h2-console/**").permitAll() + .antMatchers("/", "/*todo*/**").access("hasRole('USER')").and() + .formLogin(); + + http.csrf().disable(); + http.headers().frameOptions().disable(); + } +} diff --git a/03-todo-web-application-mysql/src/main/java/com/in28minutes/springboot/web/service/TodoRepository.java b/03-todo-web-application-mysql/src/main/java/com/in28minutes/springboot/web/service/TodoRepository.java new file mode 100644 index 0000000..15f1f57 --- /dev/null +++ b/03-todo-web-application-mysql/src/main/java/com/in28minutes/springboot/web/service/TodoRepository.java @@ -0,0 +1,18 @@ +package com.in28minutes.springboot.web.service; + +import java.util.List; + +import org.springframework.data.jpa.repository.JpaRepository; + +import com.in28minutes.springboot.web.model.Todo; + +public interface TodoRepository extends JpaRepository{ + List findByUser(String user); + + //service.retrieveTodos(name) + + //service.deleteTodo(id); + //service.retrieveTodo(id) + //service.updateTodo(todo) + //service.addTodo(getLoggedInUserName(model), todo.getDesc(), todo.getTargetDate(),false); +} diff --git a/03-todo-web-application-mysql/src/main/java/com/in28minutes/springboot/web/service/TodoService.java b/03-todo-web-application-mysql/src/main/java/com/in28minutes/springboot/web/service/TodoService.java new file mode 100644 index 0000000..3e4584a --- /dev/null +++ b/03-todo-web-application-mysql/src/main/java/com/in28minutes/springboot/web/service/TodoService.java @@ -0,0 +1,63 @@ +package com.in28minutes.springboot.web.service; + +import java.util.ArrayList; +import java.util.Date; +import java.util.Iterator; +import java.util.List; + +import org.springframework.stereotype.Service; + +import com.in28minutes.springboot.web.model.Todo; + +@Service +public class TodoService { + private static List todos = new ArrayList(); + private static int todoCount = 3; + + static { + todos.add(new Todo(1, "in28minutes", "Learn Spring MVC", new Date(), + false)); + todos.add(new Todo(2, "in28minutes", "Learn Struts", new Date(), false)); + todos.add(new Todo(3, "in28minutes", "Learn Hibernate", new Date(), + false)); + } + + public List retrieveTodos(String user) { + List filteredTodos = new ArrayList(); + for (Todo todo : todos) { + if (todo.getUser().equalsIgnoreCase(user)) { + filteredTodos.add(todo); + } + } + return filteredTodos; + } + + public Todo retrieveTodo(int id) { + for (Todo todo : todos) { + if (todo.getId()==id) { + return todo; + } + } + return null; + } + + public void updateTodo(Todo todo){ + todos.remove(todo); + todos.add(todo); + } + + public void addTodo(String name, String desc, Date targetDate, + boolean isDone) { + todos.add(new Todo(++todoCount, name, desc, targetDate, isDone)); + } + + public void deleteTodo(int id) { + Iterator iterator = todos.iterator(); + while (iterator.hasNext()) { + Todo todo = iterator.next(); + if (todo.getId() == id) { + iterator.remove(); + } + } + } +} \ No newline at end of file diff --git a/03-todo-web-application-mysql/src/main/resources/application.properties b/03-todo-web-application-mysql/src/main/resources/application.properties new file mode 100644 index 0000000..3f2789a --- /dev/null +++ b/03-todo-web-application-mysql/src/main/resources/application.properties @@ -0,0 +1,20 @@ +spring.mvc.view.prefix=/WEB-INF/jsp/ +spring.mvc.view.suffix=.jsp +logging.level.org.springframework.web=INFO + +management.endpoints.web.base-path=/manage +management.endpoints.web.exposure.include=* + +spring.jpa.show-sql=true +#spring.h2.console.enabled=true +#spring.h2.console.settings.web-allow-others=true + +spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver +spring.jpa.hibernate.ddl-auto=update +spring.datasource.url=jdbc:mysql://${RDS_HOSTNAME:localhost}:${RDS_PORT:3306}/${RDS_DB_NAME:todos} +spring.datasource.username=${RDS_USERNAME:todos-user} +spring.datasource.password=${RDS_PASSWORD:dummytodos} +#spring.datasource.url=jdbc:mysql://localhost:3306/todos +#spring.datasource.username=todos-user +#spring.datasource.password=dummytodos +spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL57Dialect \ No newline at end of file diff --git a/03-todo-web-application-mysql/src/main/webapp/WEB-INF/jsp/common/footer.jspf b/03-todo-web-application-mysql/src/main/webapp/WEB-INF/jsp/common/footer.jspf new file mode 100644 index 0000000..879b276 --- /dev/null +++ b/03-todo-web-application-mysql/src/main/webapp/WEB-INF/jsp/common/footer.jspf @@ -0,0 +1,12 @@ + + + + + + + \ No newline at end of file diff --git a/03-todo-web-application-mysql/src/main/webapp/WEB-INF/jsp/common/header.jspf b/03-todo-web-application-mysql/src/main/webapp/WEB-INF/jsp/common/header.jspf new file mode 100644 index 0000000..6c8761f --- /dev/null +++ b/03-todo-web-application-mysql/src/main/webapp/WEB-INF/jsp/common/header.jspf @@ -0,0 +1,14 @@ +<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> +<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%> +<%@taglib uri="http://www.springframework.org/tags/form" prefix="form"%> + + + + +First Web Application + + + + + diff --git a/03-todo-web-application-mysql/src/main/webapp/WEB-INF/jsp/common/navigation.jspf b/03-todo-web-application-mysql/src/main/webapp/WEB-INF/jsp/common/navigation.jspf new file mode 100644 index 0000000..de0a5d6 --- /dev/null +++ b/03-todo-web-application-mysql/src/main/webapp/WEB-INF/jsp/common/navigation.jspf @@ -0,0 +1,15 @@ + + diff --git a/03-todo-web-application-mysql/src/main/webapp/WEB-INF/jsp/error.jsp b/03-todo-web-application-mysql/src/main/webapp/WEB-INF/jsp/error.jsp new file mode 100644 index 0000000..d44949d --- /dev/null +++ b/03-todo-web-application-mysql/src/main/webapp/WEB-INF/jsp/error.jsp @@ -0,0 +1,6 @@ +<%@ include file="common/header.jspf"%> +<%@ include file="common/navigation.jspf"%> +
+An exception occurred! Please contact Support! +
+<%@ include file="common/footer.jspf"%> \ No newline at end of file diff --git a/03-todo-web-application-mysql/src/main/webapp/WEB-INF/jsp/list-todos.jsp b/03-todo-web-application-mysql/src/main/webapp/WEB-INF/jsp/list-todos.jsp new file mode 100644 index 0000000..5f3abdd --- /dev/null +++ b/03-todo-web-application-mysql/src/main/webapp/WEB-INF/jsp/list-todos.jsp @@ -0,0 +1,34 @@ +<%@ include file="common/header.jspf" %> +<%@ include file="common/navigation.jspf" %> + +
+ + + + + + + + + + + + + + + + + + + + + + +
Your todos are
DescriptionTarget DateIs it Done?
${todo.desc}${todo.done}UpdateDelete
+ +
+<%@ include file="common/footer.jspf" %> \ No newline at end of file diff --git a/03-todo-web-application-mysql/src/main/webapp/WEB-INF/jsp/todo.jsp b/03-todo-web-application-mysql/src/main/webapp/WEB-INF/jsp/todo.jsp new file mode 100644 index 0000000..77cf14f --- /dev/null +++ b/03-todo-web-application-mysql/src/main/webapp/WEB-INF/jsp/todo.jsp @@ -0,0 +1,23 @@ +<%@ include file="common/header.jspf" %> +<%@ include file="common/navigation.jspf" %> +
+ + +
+ Description + + +
+ +
+ Target Date + + +
+ + +
+
+<%@ include file="common/footer.jspf" %> \ No newline at end of file diff --git a/03-todo-web-application-mysql/src/main/webapp/WEB-INF/jsp/welcome.jsp b/03-todo-web-application-mysql/src/main/webapp/WEB-INF/jsp/welcome.jsp new file mode 100644 index 0000000..c94ed31 --- /dev/null +++ b/03-todo-web-application-mysql/src/main/webapp/WEB-INF/jsp/welcome.jsp @@ -0,0 +1,7 @@ +<%@ include file="common/header.jspf"%> +<%@ include file="common/navigation.jspf"%> +
+ Welcome ${name}!! Click here to manage your + todo's. +
+<%@ include file="common/footer.jspf"%> \ No newline at end of file diff --git a/03-todo-web-application-mysql/src/test/java/com/in28minutes/springboot/web/SpringBootFirstWebApplicationTests.java b/03-todo-web-application-mysql/src/test/java/com/in28minutes/springboot/web/SpringBootFirstWebApplicationTests.java new file mode 100644 index 0000000..f207136 --- /dev/null +++ b/03-todo-web-application-mysql/src/test/java/com/in28minutes/springboot/web/SpringBootFirstWebApplicationTests.java @@ -0,0 +1,16 @@ +package com.in28minutes.springboot.web; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@SpringBootTest +public class SpringBootFirstWebApplicationTests { + + @Test + public void contextLoads() { + } + +} diff --git a/03-todo-web-application-mysql/src/test/resources/application.properties b/03-todo-web-application-mysql/src/test/resources/application.properties new file mode 100644 index 0000000..e9c1ca3 --- /dev/null +++ b/03-todo-web-application-mysql/src/test/resources/application.properties @@ -0,0 +1,5 @@ +spring.jpa.hibernate.ddl-auto=create-drop +spring.datasource.driver-class-name=org.h2.Driver +spring.datasource.url=jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1 +spring.datasource.username=sa +spring.datasource.password=sa diff --git a/03-todo-web-application-mysql/target/classes/application.properties b/03-todo-web-application-mysql/target/classes/application.properties new file mode 100644 index 0000000..3f2789a --- /dev/null +++ b/03-todo-web-application-mysql/target/classes/application.properties @@ -0,0 +1,20 @@ +spring.mvc.view.prefix=/WEB-INF/jsp/ +spring.mvc.view.suffix=.jsp +logging.level.org.springframework.web=INFO + +management.endpoints.web.base-path=/manage +management.endpoints.web.exposure.include=* + +spring.jpa.show-sql=true +#spring.h2.console.enabled=true +#spring.h2.console.settings.web-allow-others=true + +spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver +spring.jpa.hibernate.ddl-auto=update +spring.datasource.url=jdbc:mysql://${RDS_HOSTNAME:localhost}:${RDS_PORT:3306}/${RDS_DB_NAME:todos} +spring.datasource.username=${RDS_USERNAME:todos-user} +spring.datasource.password=${RDS_PASSWORD:dummytodos} +#spring.datasource.url=jdbc:mysql://localhost:3306/todos +#spring.datasource.username=todos-user +#spring.datasource.password=dummytodos +spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL57Dialect \ No newline at end of file diff --git a/03-todo-web-application-mysql/target/classes/com/in28minutes/springboot/web/EnvironmentConfigurationLogger.class b/03-todo-web-application-mysql/target/classes/com/in28minutes/springboot/web/EnvironmentConfigurationLogger.class new file mode 100644 index 0000000..1d23e62 Binary files /dev/null and b/03-todo-web-application-mysql/target/classes/com/in28minutes/springboot/web/EnvironmentConfigurationLogger.class differ diff --git a/03-todo-web-application-mysql/target/classes/com/in28minutes/springboot/web/SpringBootFirstWebApplication.class b/03-todo-web-application-mysql/target/classes/com/in28minutes/springboot/web/SpringBootFirstWebApplication.class new file mode 100644 index 0000000..09ec1e1 Binary files /dev/null and b/03-todo-web-application-mysql/target/classes/com/in28minutes/springboot/web/SpringBootFirstWebApplication.class differ diff --git a/03-todo-web-application-mysql/target/classes/com/in28minutes/springboot/web/controller/ErrorController.class b/03-todo-web-application-mysql/target/classes/com/in28minutes/springboot/web/controller/ErrorController.class new file mode 100644 index 0000000..1071f5c Binary files /dev/null and b/03-todo-web-application-mysql/target/classes/com/in28minutes/springboot/web/controller/ErrorController.class differ diff --git a/03-todo-web-application-mysql/target/classes/com/in28minutes/springboot/web/controller/LogoutController.class b/03-todo-web-application-mysql/target/classes/com/in28minutes/springboot/web/controller/LogoutController.class new file mode 100644 index 0000000..0e443fc Binary files /dev/null and b/03-todo-web-application-mysql/target/classes/com/in28minutes/springboot/web/controller/LogoutController.class differ diff --git a/03-todo-web-application-mysql/target/classes/com/in28minutes/springboot/web/controller/TodoController.class b/03-todo-web-application-mysql/target/classes/com/in28minutes/springboot/web/controller/TodoController.class new file mode 100644 index 0000000..2c0bd7d Binary files /dev/null and b/03-todo-web-application-mysql/target/classes/com/in28minutes/springboot/web/controller/TodoController.class differ diff --git a/03-todo-web-application-mysql/target/classes/com/in28minutes/springboot/web/controller/WelcomeController.class b/03-todo-web-application-mysql/target/classes/com/in28minutes/springboot/web/controller/WelcomeController.class new file mode 100644 index 0000000..236f1e7 Binary files /dev/null and b/03-todo-web-application-mysql/target/classes/com/in28minutes/springboot/web/controller/WelcomeController.class differ diff --git a/03-todo-web-application-mysql/target/classes/com/in28minutes/springboot/web/model/Todo.class b/03-todo-web-application-mysql/target/classes/com/in28minutes/springboot/web/model/Todo.class new file mode 100644 index 0000000..bc9153b Binary files /dev/null and b/03-todo-web-application-mysql/target/classes/com/in28minutes/springboot/web/model/Todo.class differ diff --git a/03-todo-web-application-mysql/target/classes/com/in28minutes/springboot/web/security/SecurityConfiguration.class b/03-todo-web-application-mysql/target/classes/com/in28minutes/springboot/web/security/SecurityConfiguration.class new file mode 100644 index 0000000..abff7b5 Binary files /dev/null and b/03-todo-web-application-mysql/target/classes/com/in28minutes/springboot/web/security/SecurityConfiguration.class differ diff --git a/03-todo-web-application-mysql/target/classes/com/in28minutes/springboot/web/service/TodoRepository.class b/03-todo-web-application-mysql/target/classes/com/in28minutes/springboot/web/service/TodoRepository.class new file mode 100644 index 0000000..2ec3721 Binary files /dev/null and b/03-todo-web-application-mysql/target/classes/com/in28minutes/springboot/web/service/TodoRepository.class differ diff --git a/03-todo-web-application-mysql/target/classes/com/in28minutes/springboot/web/service/TodoService.class b/03-todo-web-application-mysql/target/classes/com/in28minutes/springboot/web/service/TodoService.class new file mode 100644 index 0000000..c89012d Binary files /dev/null and b/03-todo-web-application-mysql/target/classes/com/in28minutes/springboot/web/service/TodoService.class differ diff --git a/03-todo-web-application-mysql/target/test-classes/application.properties b/03-todo-web-application-mysql/target/test-classes/application.properties new file mode 100644 index 0000000..e9c1ca3 --- /dev/null +++ b/03-todo-web-application-mysql/target/test-classes/application.properties @@ -0,0 +1,5 @@ +spring.jpa.hibernate.ddl-auto=create-drop +spring.datasource.driver-class-name=org.h2.Driver +spring.datasource.url=jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1 +spring.datasource.username=sa +spring.datasource.password=sa diff --git a/03-todo-web-application-mysql/target/test-classes/com/in28minutes/springboot/web/SpringBootFirstWebApplicationTests.class b/03-todo-web-application-mysql/target/test-classes/com/in28minutes/springboot/web/SpringBootFirstWebApplicationTests.class new file mode 100644 index 0000000..450afd4 Binary files /dev/null and b/03-todo-web-application-mysql/target/test-classes/com/in28minutes/springboot/web/SpringBootFirstWebApplicationTests.class differ diff --git a/README.md b/README.md index c7e8ba0..45f255f 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,42 @@ -# sd-workshop2 2022-1 -sd workshop2 +# Microservices -- Completar la lógica de la applicación de pagos de tal manera que al hacer un pago através del microservicio de pagos, el monto de las facturas sea correctamente debitado, es decir, actualmente si una factura debe 1000 y yo hago un pago por 400 a esa factura, el microservicio invoice me sobreescribe el 1000 por 400 en vez de mostrarme el saldo restante 1000-400=600. -- Completar la lógica de la aplicación de tal manera que haya 3 estados para las facturas. 0=debe 1=pagadoparcialmente 2=pagado -- Hacer que las applicaciones se puedan registrar con consul -- Debe ser un pull request a este repositorio sd-workshop2 +![alt text](https://i.ibb.co/3cQdDny/microservices.png) -Bonus: -- Subir las imagenes de la app a Docker hub -- Crear un script en bash que lance toda la aplicación. +There is no accepted definition of a microservice, you will find for example: +- > "Small autonomous services that work together" By: Sam Newman +- > "In short, the microservice architectural style is a approach to developing a single application as a suite of small services, each running in its own process and communicating with lightweight mecahcnism, often and HTTP..." +- > "There is a bare minimun of centralized management of these services, which may be written in different programming languages and use different data storage technologies" by: Martin Fowler + +- REST +- And Small whell chosen deployable units +- And cloud enabled + +### How Does it Look? + +![alt tex](https://i.ibb.co/CWvrFry/microservices2.png) + +## Microservices Advantages + +- New Technology and Process Adaption +- Dynamic Scaling +- Faster Release Cycles + +## Microservices Challenges + +- Bounded Context: ¿How do you identify the boundary for each os these microservices? ¿How do you identify what to do in each of these microservices? ¿How do you decide what you should do and what no? +- Configuration Management +- Dynamic Scale Up and Scale Down +- Visibility: If I say the functionality is now distributed among 10 microservices and there is a bug, How do you identify where the bug is? +- Pack of cards: How do i prevent one microservices being down taking down entire application? ¿How do i build fault-tolerance? + +### How we can solve all those challenges? + +- CONTAINERIZATION +- ORCHESTATION + +![alt text](https://i.ibb.co/8mbbpJ2/microservices3.png) + +### References +· https://www.geeksforgeeks.org/todo-list-app-using-flask-python/ +· https://github.com/cosmoshadab/todo-app-flask +· https://github.com/bhavaniravi/flask-tutorial/blob/master/app/app.py diff --git a/hello-world-java/Dockerfile b/hello-world-java/Dockerfile new file mode 100644 index 0000000..4a45b5c --- /dev/null +++ b/hello-world-java/Dockerfile @@ -0,0 +1,11 @@ +# Build a JAR File +FROM maven:3.6.3-jdk-8-slim AS stage1 +WORKDIR /home/app +COPY . /home/app/ +RUN mvn -f /home/app/pom.xml clean package + +# Create an Image +FROM openjdk:8-jdk-alpine +EXPOSE 5000 +COPY --from=stage1 /home/app/target/hello-world-java.jar hello-world-java.jar +ENTRYPOINT ["sh", "-c", "java -jar /hello-world-java.jar"] diff --git a/hello-world-java/pom.xml b/hello-world-java/pom.xml new file mode 100644 index 0000000..bfc7742 --- /dev/null +++ b/hello-world-java/pom.xml @@ -0,0 +1,95 @@ + + + 4.0.0 + + com.in28minutes.rest.webservices + hello-world-java + 0.0.1-SNAPSHOT + jar + Demo project for Spring Boot + hello-world-java + + org.springframework.boot + spring-boot-starter-parent + 2.1.7.RELEASE + + + + + UTF-8 + UTF-8 + 1.8 + 3.1.1 + + + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-devtools + runtime + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + hello-world-java + + + org.springframework.boot + spring-boot-maven-plugin + + + + + + + + spring-snapshots + Spring Snapshots + https://repo.spring.io/snapshot + + true + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + + + + spring-snapshots + Spring Snapshots + https://repo.spring.io/snapshot + + true + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + diff --git a/hello-world-java/src/main/java/com/in28minutes/rest/webservices/restfulwebservices/HelloWorldController.java b/hello-world-java/src/main/java/com/in28minutes/rest/webservices/restfulwebservices/HelloWorldController.java new file mode 100644 index 0000000..a408503 --- /dev/null +++ b/hello-world-java/src/main/java/com/in28minutes/rest/webservices/restfulwebservices/HelloWorldController.java @@ -0,0 +1,13 @@ +package com.in28minutes.rest.webservices.restfulwebservices; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class HelloWorldController { + + @GetMapping(path = "/") + public String helloWorld() { + return "{\"message\":\"Hello World Java v1\"}"; + } +} diff --git a/hello-world-java/src/main/java/com/in28minutes/rest/webservices/restfulwebservices/RestfulWebServicesApplication.java b/hello-world-java/src/main/java/com/in28minutes/rest/webservices/restfulwebservices/RestfulWebServicesApplication.java new file mode 100644 index 0000000..e2b1467 --- /dev/null +++ b/hello-world-java/src/main/java/com/in28minutes/rest/webservices/restfulwebservices/RestfulWebServicesApplication.java @@ -0,0 +1,12 @@ +package com.in28minutes.rest.webservices.restfulwebservices; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class RestfulWebServicesApplication { + + public static void main(String[] args) { + SpringApplication.run(RestfulWebServicesApplication.class, args); + } +} \ No newline at end of file diff --git a/hello-world-java/src/main/resources/application.properties b/hello-world-java/src/main/resources/application.properties new file mode 100644 index 0000000..975d0b0 --- /dev/null +++ b/hello-world-java/src/main/resources/application.properties @@ -0,0 +1,2 @@ +logging.level.org.springframework = debug +server.port = 5000 \ No newline at end of file diff --git a/hello-world-java/src/test/java/com/in28minutes/rest/webservices/restfulwebservices/RestfulWebServicesApplicationTests.java b/hello-world-java/src/test/java/com/in28minutes/rest/webservices/restfulwebservices/RestfulWebServicesApplicationTests.java new file mode 100644 index 0000000..55d01b9 --- /dev/null +++ b/hello-world-java/src/test/java/com/in28minutes/rest/webservices/restfulwebservices/RestfulWebServicesApplicationTests.java @@ -0,0 +1,16 @@ +package com.in28minutes.rest.webservices.restfulwebservices; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@SpringBootTest +public class RestfulWebServicesApplicationTests { + + @Test + public void contextLoads() { + } + +} diff --git a/hello-world-java/target/classes/application.properties b/hello-world-java/target/classes/application.properties new file mode 100644 index 0000000..975d0b0 --- /dev/null +++ b/hello-world-java/target/classes/application.properties @@ -0,0 +1,2 @@ +logging.level.org.springframework = debug +server.port = 5000 \ No newline at end of file diff --git a/hello-world-java/target/classes/com/in28minutes/rest/webservices/restfulwebservices/HelloWorldController.class b/hello-world-java/target/classes/com/in28minutes/rest/webservices/restfulwebservices/HelloWorldController.class new file mode 100644 index 0000000..45372d2 Binary files /dev/null and b/hello-world-java/target/classes/com/in28minutes/rest/webservices/restfulwebservices/HelloWorldController.class differ diff --git a/hello-world-java/target/classes/com/in28minutes/rest/webservices/restfulwebservices/RestfulWebServicesApplication.class b/hello-world-java/target/classes/com/in28minutes/rest/webservices/restfulwebservices/RestfulWebServicesApplication.class new file mode 100644 index 0000000..c9b9069 Binary files /dev/null and b/hello-world-java/target/classes/com/in28minutes/rest/webservices/restfulwebservices/RestfulWebServicesApplication.class differ diff --git a/hello-world-java/target/test-classes/com/in28minutes/rest/webservices/restfulwebservices/RestfulWebServicesApplicationTests.class b/hello-world-java/target/test-classes/com/in28minutes/rest/webservices/restfulwebservices/RestfulWebServicesApplicationTests.class new file mode 100644 index 0000000..d115fb1 Binary files /dev/null and b/hello-world-java/target/test-classes/com/in28minutes/rest/webservices/restfulwebservices/RestfulWebServicesApplicationTests.class differ diff --git a/hello-world-nodejs/Dockerfile b/hello-world-nodejs/Dockerfile new file mode 100644 index 0000000..374f6d4 --- /dev/null +++ b/hello-world-nodejs/Dockerfile @@ -0,0 +1,9 @@ +FROM node:8.16.1-alpine +WORKDIR /app +COPY . /app +RUN npm install +EXPOSE 5000 +CMD node index.js + +#ENTRYPOINT ["node", "index.js"] +#COPY package.json /app diff --git a/hello-world-nodejs/index.js b/hello-world-nodejs/index.js new file mode 100644 index 0000000..3d23b49 --- /dev/null +++ b/hello-world-nodejs/index.js @@ -0,0 +1,15 @@ +var express = require('express') +const process = require('process'); + +var expressapp = express() +expressapp.get('/', function (req, res) { + res.send('{"message":"Hello World JavaScript v1"}') +}) +expressapp.listen(5000, function () { + console.log('Ready on port 5000!') +}) + +/* To Handle Ctrl C*/ +process.on('SIGINT', function () { + process.exit(); +}); \ No newline at end of file diff --git a/hello-world-nodejs/package.json b/hello-world-nodejs/package.json new file mode 100644 index 0000000..09aea29 --- /dev/null +++ b/hello-world-nodejs/package.json @@ -0,0 +1,18 @@ +{ + "name": "nodejs-hello-world", + "version": "1.0.0", + "description": "nodejs-hello-world", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "Ranga", + "dependencies": { + "express": "^4.17.1" + }, + "devDependencies": { + "webpack": "^4.41.5" + }, + "license": "MIT", + "repository": {} +} \ No newline at end of file diff --git a/hello-world-python/Dockerfile b/hello-world-python/Dockerfile new file mode 100644 index 0000000..5b542c2 --- /dev/null +++ b/hello-world-python/Dockerfile @@ -0,0 +1,9 @@ +FROM python:alpine3.10 +WORKDIR /app +COPY . /app +RUN pip install -r requirements.txt +EXPOSE 5000 +CMD python ./launch.py + +#COPY requirements.txt /app/requirements.txt +#ENTRYPOINT ["python", "./launch.py"] diff --git a/hello-world-python/Jenkinsfile b/hello-world-python/Jenkinsfile new file mode 100644 index 0000000..9058fd5 --- /dev/null +++ b/hello-world-python/Jenkinsfile @@ -0,0 +1,49 @@ +// Uses Declarative syntax to run commands inside a container. +pipeline { + agent { + kubernetes { + yaml ''' +apiVersion: v1 +kind: Pod +spec: + containers: + - name: python + image: python + command: + - sleep + args: + - infinity + +''' + defaultContainer 'python' + } + } + + stages { + stage('Build') { + steps ('Install dependencies'){ + dir('./hello-world-python'){ + sh 'pip install -r requirements.txt' + } + } + } + stage('Run unit tests'){ + steps { + dir('./hello-world-python'){ + sh 'pytest testRoutes.py' + } + } + } + stage('Generate artifact'){ + steps { + dir('./hello-world-python'){ + sh 'ls -lha' + sh 'pwd' + sh 'mkdir artifacts' + sh "tar -czvf artifacts/python-app:${env.BUILD_ID}.tar.gz ." + } + } + } + } +} + diff --git a/hello-world-python/app.py b/hello-world-python/app.py new file mode 100644 index 0000000..fe42bc4 --- /dev/null +++ b/hello-world-python/app.py @@ -0,0 +1,7 @@ +from flask import Flask +app = Flask(__name__) +@app.route("/") +def run(): + return "

Hello World Python v1

" +if __name__ == "__main__": + app.run(host="0.0.0.0", port=int("5000"), debug=True) diff --git a/hello-world-python/readme.md b/hello-world-python/readme.md new file mode 100644 index 0000000..2734d7e --- /dev/null +++ b/hello-world-python/readme.md @@ -0,0 +1 @@ +# hello friend great diff --git a/hello-world-python/requirements.txt b/hello-world-python/requirements.txt new file mode 100644 index 0000000..7c5d187 --- /dev/null +++ b/hello-world-python/requirements.txt @@ -0,0 +1,3 @@ +flask +Jinja2 +pytest diff --git a/hello-world-python/testRoutes.py b/hello-world-python/testRoutes.py new file mode 100644 index 0000000..64afd54 --- /dev/null +++ b/hello-world-python/testRoutes.py @@ -0,0 +1,6 @@ +from app import * + +def test_Home(): + response = app.test_client().get('/') + assert b"Hello World Python v1" in response.data + assert response.status_code == 200 diff --git a/icesiHealthApp/backend.zip b/icesiHealthApp/backend.zip new file mode 100644 index 0000000..4616d1a Binary files /dev/null and b/icesiHealthApp/backend.zip differ diff --git a/icesiHealthApp/frontend.zip b/icesiHealthApp/frontend.zip new file mode 100644 index 0000000..56edbd3 Binary files /dev/null and b/icesiHealthApp/frontend.zip differ diff --git a/pay-app-spring-microservices/README.md b/pay-app-spring-microservices/README.md new file mode 100644 index 0000000..bc7c0ba --- /dev/null +++ b/pay-app-spring-microservices/README.md @@ -0,0 +1,25 @@ +# Microservicios con Spring Boot + +![Architecture](./resources/microservicesarchitecture.png) + +## Información de los microservicios +El microservicio de invoices, debe listar las facturas de clientes y además debe consumir una cola para cambiar el estado de la factura cuando esta se paga a través del microservicio de pago. +El microservicio de pago debe registrar el pago en su respectiva bd y además debe dejar un mensaje en una cola para actualizar la factura en el microservicio de facturas y además debe dejar un mensaje en una cola para registrar el movimiento en el microservicio de transacciones. +El microservicio de transacciones debe listar las transacciones de una factura, además debe consumir una cola para obtener las transacciones de pago del microservicio de pago. +Todos los microservicios deben consumir la cadena de conexión desde el servicio de configuración centralizada. + +La información de los endpoints disponibles por microservicio se incluyen en el documento de INFO.md +## Scripts de creación de bases de datos + +La informacion de como crear las bases de datos y sus respectivas tablas se incluyen en google.com + +## Tecnologías utilizadas + +- Spring Boot (Java Framework JDK v11+) +- Gradle (Gestor de dependencias) +- Postman (Test de endpoints/servicios rest) +- Postgresql (Base de Datos) +- MySQL (Base de Datos) +- MongoDB (Base de Datos NoSQL) +- Kafka (Gestor de Mensajería) +- Github (Repositorio para proyecto y Configuraciones de micorservicios) diff --git a/pay-app-spring-microservices/STEPS.md b/pay-app-spring-microservices/STEPS.md new file mode 100644 index 0000000..82ac3fe --- /dev/null +++ b/pay-app-spring-microservices/STEPS.md @@ -0,0 +1,69 @@ +# Configuration +## 1. Network creation +```bash +docker network create distribuidos +``` +## 2. Database creation +- Postgres +```bash +docker run -p 5434:5432 --name postgres --network distribuidos -e POSTGRES_PASSWORD=postgres -e POSTGRES_DB=db_invoice -d postgres:12-alpine +``` +## 3. Kafka creation +```bash +docker run -p 2181:2181 -d -p 9092:9092 --name servicekafka --network distribuidos -e ADVERTISED_HOST=servicekafka -e NUM_PARTITIONS=3 johnnypark/kafka-zookeeper +``` +## 4. Running app-config service +### 4.1. Dockerfile modification +```Dockerfile +FROM openjdk:13-alpine as stage1 +COPY . /app +WORKDIR /app +RUN chmod 777 gradlew +RUN ./gradlew clean +RUN ./gradlew bootJar + + +FROM openjdk:13-alpine +EXPOSE 8888 +COPY --from=stage1 /app/build/libs/app-config-1.0.0.jar app-config.jar +#ADD ./build/libs/app-config-1.0.0.jar app-config.jar +ENTRYPOINT ["java", "-jar", "/app-config.jar"] +``` +### 4.2. Build +```bash +docker build -t alexjr2002/app-config . +``` +### 4.3. Run +```bash +docker run -p 8888:8888 --name app-config --network distribuidos -d alexjr2002/app-config +``` +### 4.4. Working proof +![alt text](app-config-proof.png) +## 5. Running app-invoice service +### 5.1. Dockerfile modification +```Dockerfile +FROM openjdk:13-alpine as stage1 +COPY . /app +WORKDIR /app +RUN chmod 777 gradlew +RUN ./gradlew clean +RUN ./gradlew bootJar + + +FROM openjdk:13-alpine +EXPOSE 8006 +COPY --from=stage1 /app/build/libs/app-invoice-1.0.0.jar app-invoice.jar +#ADD ./build/libs/app-config-1.0.0.jar app-config.jar +ENTRYPOINT ["java", "-jar", "/app-invoice.jar"] +``` +### 5.2. Build +```bash +docker build -t alexjr2002/app-invoice . +``` +### 5.3. Run +```bash +docker run -p 8006:8006 --name app-invoice --network distribuidos -d alexjr2002/app-invoice +``` +### 5.4. Working proof +![alt text](app-invoice-proof.png) +![alt text](app-invoice-data.png) \ No newline at end of file diff --git a/pay-app-spring-microservices/app-config-proof.png b/pay-app-spring-microservices/app-config-proof.png new file mode 100644 index 0000000..a0fd592 Binary files /dev/null and b/pay-app-spring-microservices/app-config-proof.png differ diff --git a/pay-app-spring-microservices/app-config/.gitignore b/pay-app-spring-microservices/app-config/.gitignore new file mode 100644 index 0000000..c2065bc --- /dev/null +++ b/pay-app-spring-microservices/app-config/.gitignore @@ -0,0 +1,37 @@ +HELP.md +.gradle +build/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/**/build/ +!**/src/test/**/build/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ diff --git a/pay-app-spring-microservices/app-config/Dockerfile b/pay-app-spring-microservices/app-config/Dockerfile new file mode 100644 index 0000000..6caa515 --- /dev/null +++ b/pay-app-spring-microservices/app-config/Dockerfile @@ -0,0 +1,13 @@ +FROM openjdk:13-alpine as stage1 +COPY . /app +WORKDIR /app +RUN chmod 777 gradlew +RUN ./gradlew clean +RUN ./gradlew bootJar + + +FROM openjdk:13-alpine +EXPOSE 8888 +COPY --from=stage1 /app/build/libs/app-config-1.0.0.jar app-config.jar +#ADD ./build/libs/app-config-1.0.0.jar app-config.jar +ENTRYPOINT ["java", "-jar", "/app-config.jar"] diff --git a/pay-app-spring-microservices/app-config/build.gradle b/pay-app-spring-microservices/app-config/build.gradle new file mode 100644 index 0000000..091db72 --- /dev/null +++ b/pay-app-spring-microservices/app-config/build.gradle @@ -0,0 +1,36 @@ +plugins { + id 'org.springframework.boot' version '2.3.10.RELEASE' + id 'io.spring.dependency-management' version '1.0.11.RELEASE' + id 'java' +} + +group = 'com.aforo' +version = '1.0.0' +sourceCompatibility = '11' + +repositories { + mavenCentral() +} + +ext { + set('springCloudVersion', "Hoxton.SR11") +} + +dependencies { + implementation 'org.springframework.cloud:spring-cloud-config-server' + //implementation 'org.springframework.cloud:spring-cloud-starter-consul-discovery' + + testImplementation('org.springframework.boot:spring-boot-starter-test') { + exclude group: 'org.junit.vintage', module: 'junit-vintage-engine' + } +} + +dependencyManagement { + imports { + mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}" + } +} + +test { + useJUnitPlatform() +} diff --git a/pay-app-spring-microservices/app-config/gradle/wrapper/gradle-wrapper.jar b/pay-app-spring-microservices/app-config/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..e708b1c Binary files /dev/null and b/pay-app-spring-microservices/app-config/gradle/wrapper/gradle-wrapper.jar differ diff --git a/pay-app-spring-microservices/app-config/gradle/wrapper/gradle-wrapper.properties b/pay-app-spring-microservices/app-config/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..549d844 --- /dev/null +++ b/pay-app-spring-microservices/app-config/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.9-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/pay-app-spring-microservices/app-config/gradlew b/pay-app-spring-microservices/app-config/gradlew new file mode 100644 index 0000000..8f89047 --- /dev/null +++ b/pay-app-spring-microservices/app-config/gradlew @@ -0,0 +1,184 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ]; do + ls=$(ls -ld "$PRG") + link=$(expr "$ls" : '.*-> \(.*\)$') + if expr "$link" : '/.*' >/dev/null; then + PRG="$link" + else + PRG=$(dirname "$PRG")"/$link" + fi +done +SAVED="$(pwd)" +cd "$(dirname \"$PRG\")/" >/dev/null +APP_HOME="$(pwd -P)" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=$(basename "$0") + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn() { + echo "$*" +} + +die() { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$(uname)" in +CYGWIN*) + cygwin=true + ;; +Darwin*) + darwin=true + ;; +MINGW*) + msys=true + ;; +NONSTOP*) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ]; then + if [ -x "$JAVA_HOME/jre/sh/java" ]; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ]; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ]; then + MAX_FD_LIMIT=$(ulimit -H -n) + if [ $? -eq 0 ]; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ]; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ]; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ]; then + APP_HOME=$(cygpath --path --mixed "$APP_HOME") + CLASSPATH=$(cygpath --path --mixed "$CLASSPATH") + + JAVACMD=$(cygpath --unix "$JAVACMD") + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=$(find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null) + SEP="" + for dir in $ROOTDIRSRAW; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ]; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@"; do + CHECK=$(echo "$arg" | egrep -c "$OURCYGPATTERN" -) + CHECK2=$(echo "$arg" | egrep -c "^-") ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ]; then ### Added a condition + eval $(echo args$i)=$(cygpath --path --ignore --mixed "$arg") + else + eval $(echo args$i)="\"$arg\"" + fi + i=$(expr $i + 1) + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save() { + for i; do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/"; done + echo " " +} +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/pay-app-spring-microservices/app-config/gradlew.bat b/pay-app-spring-microservices/app-config/gradlew.bat new file mode 100644 index 0000000..107acd3 --- /dev/null +++ b/pay-app-spring-microservices/app-config/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/pay-app-spring-microservices/app-config/settings.gradle b/pay-app-spring-microservices/app-config/settings.gradle new file mode 100644 index 0000000..7c9d23a --- /dev/null +++ b/pay-app-spring-microservices/app-config/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'app-config' diff --git a/pay-app-spring-microservices/app-config/src/main/java/com/aforo/appconfig/AppConfigApplication.java b/pay-app-spring-microservices/app-config/src/main/java/com/aforo/appconfig/AppConfigApplication.java new file mode 100644 index 0000000..c64a6b2 --- /dev/null +++ b/pay-app-spring-microservices/app-config/src/main/java/com/aforo/appconfig/AppConfigApplication.java @@ -0,0 +1,15 @@ +package com.aforo.appconfig; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.config.server.EnableConfigServer; + +@SpringBootApplication +@EnableConfigServer +public class AppConfigApplication { + + public static void main(String[] args) { + SpringApplication.run(AppConfigApplication.class, args); + } + +} diff --git a/pay-app-spring-microservices/app-config/src/main/resources/application.properties b/pay-app-spring-microservices/app-config/src/main/resources/application.properties new file mode 100644 index 0000000..7fd2213 --- /dev/null +++ b/pay-app-spring-microservices/app-config/src/main/resources/application.properties @@ -0,0 +1,15 @@ +# Server +spring.application.name=app-config +server.port=8888 + +# Config +spring.cloud.config.server.git.uri=https://github.com/icesi-ops/training_microservices.git +spring.cloud.config.server.default-label=master +spring.cloud.config.server.git.search-paths=pay-app-spring-microservices/config +spring.cloud.config.server.git.skip-ssl-validation=true + +# Consul +spring.cloud.consul.host=consul +spring.cloud.consul.port=8500 +spring.cloud.consul.discovery.health-check-interval=5s +spring.cloud.consul.discovery.prefer-ip-address=true \ No newline at end of file diff --git a/pay-app-spring-microservices/app-config/src/test/java/com/aforo/appconfig/AppConfigApplicationTests.java b/pay-app-spring-microservices/app-config/src/test/java/com/aforo/appconfig/AppConfigApplicationTests.java new file mode 100644 index 0000000..90d6ce9 --- /dev/null +++ b/pay-app-spring-microservices/app-config/src/test/java/com/aforo/appconfig/AppConfigApplicationTests.java @@ -0,0 +1,13 @@ +package com.aforo.appconfig; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class AppConfigApplicationTests { + + @Test + void contextLoads() { + } + +} diff --git a/pay-app-spring-microservices/app-invoice-data.png b/pay-app-spring-microservices/app-invoice-data.png new file mode 100644 index 0000000..f0abfeb Binary files /dev/null and b/pay-app-spring-microservices/app-invoice-data.png differ diff --git a/pay-app-spring-microservices/app-invoice-proof.png b/pay-app-spring-microservices/app-invoice-proof.png new file mode 100644 index 0000000..fdd84fb Binary files /dev/null and b/pay-app-spring-microservices/app-invoice-proof.png differ diff --git a/pay-app-spring-microservices/app-invoice/.gitignore b/pay-app-spring-microservices/app-invoice/.gitignore new file mode 100644 index 0000000..c2065bc --- /dev/null +++ b/pay-app-spring-microservices/app-invoice/.gitignore @@ -0,0 +1,37 @@ +HELP.md +.gradle +build/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/**/build/ +!**/src/test/**/build/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ diff --git a/pay-app-spring-microservices/app-invoice/Dockerfile b/pay-app-spring-microservices/app-invoice/Dockerfile new file mode 100644 index 0000000..5bd3cf9 --- /dev/null +++ b/pay-app-spring-microservices/app-invoice/Dockerfile @@ -0,0 +1,13 @@ +FROM openjdk:13-alpine as stage1 +COPY . /app +WORKDIR /app +RUN chmod 777 gradlew +RUN ./gradlew clean +RUN ./gradlew bootJar + + +FROM openjdk:13-alpine +EXPOSE 8006 +COPY --from=stage1 /app/build/libs/app-invoice-1.0.0.jar app-invoice.jar +#ADD ./build/libs/app-config-1.0.0.jar app-config.jar +ENTRYPOINT ["java", "-jar", "/app-invoice.jar"] diff --git a/pay-app-spring-microservices/app-invoice/build.gradle b/pay-app-spring-microservices/app-invoice/build.gradle new file mode 100644 index 0000000..76d0556 --- /dev/null +++ b/pay-app-spring-microservices/app-invoice/build.gradle @@ -0,0 +1,47 @@ +plugins { + id 'org.springframework.boot' version '2.3.10.RELEASE' + id 'io.spring.dependency-management' version '1.0.11.RELEASE' + id 'java' +} + + +group = 'com.aforo' +version = '1.0.0' +sourceCompatibility = '11' + +configurations { + compileOnly { + extendsFrom annotationProcessor + } +} + +repositories { + mavenCentral() +} + +ext { + set('springCloudVersion', "Hoxton.SR11") +} + +dependencies { + implementation 'org.springframework.boot:spring-boot-starter-data-jpa' + implementation 'org.springframework.boot:spring-boot-starter-web' + implementation 'org.springframework.kafka:spring-kafka' + implementation 'org.springframework.cloud:spring-cloud-starter-config' + + compileOnly 'org.projectlombok:lombok' + runtimeOnly 'org.postgresql:postgresql' + annotationProcessor 'org.projectlombok:lombok' + testImplementation 'org.springframework.boot:spring-boot-starter-test' + testImplementation 'org.springframework.kafka:spring-kafka-test' +} + +dependencyManagement { + imports { + mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}" + } +} + +test { + useJUnitPlatform() +} diff --git a/pay-app-spring-microservices/app-invoice/gradle/wrapper/gradle-wrapper.jar b/pay-app-spring-microservices/app-invoice/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..e708b1c Binary files /dev/null and b/pay-app-spring-microservices/app-invoice/gradle/wrapper/gradle-wrapper.jar differ diff --git a/pay-app-spring-microservices/app-invoice/gradle/wrapper/gradle-wrapper.properties b/pay-app-spring-microservices/app-invoice/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..549d844 --- /dev/null +++ b/pay-app-spring-microservices/app-invoice/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.9-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/pay-app-spring-microservices/app-invoice/gradlew b/pay-app-spring-microservices/app-invoice/gradlew new file mode 100644 index 0000000..8f89047 --- /dev/null +++ b/pay-app-spring-microservices/app-invoice/gradlew @@ -0,0 +1,184 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ]; do + ls=$(ls -ld "$PRG") + link=$(expr "$ls" : '.*-> \(.*\)$') + if expr "$link" : '/.*' >/dev/null; then + PRG="$link" + else + PRG=$(dirname "$PRG")"/$link" + fi +done +SAVED="$(pwd)" +cd "$(dirname \"$PRG\")/" >/dev/null +APP_HOME="$(pwd -P)" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=$(basename "$0") + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn() { + echo "$*" +} + +die() { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$(uname)" in +CYGWIN*) + cygwin=true + ;; +Darwin*) + darwin=true + ;; +MINGW*) + msys=true + ;; +NONSTOP*) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ]; then + if [ -x "$JAVA_HOME/jre/sh/java" ]; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ]; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ]; then + MAX_FD_LIMIT=$(ulimit -H -n) + if [ $? -eq 0 ]; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ]; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ]; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ]; then + APP_HOME=$(cygpath --path --mixed "$APP_HOME") + CLASSPATH=$(cygpath --path --mixed "$CLASSPATH") + + JAVACMD=$(cygpath --unix "$JAVACMD") + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=$(find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null) + SEP="" + for dir in $ROOTDIRSRAW; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ]; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@"; do + CHECK=$(echo "$arg" | egrep -c "$OURCYGPATTERN" -) + CHECK2=$(echo "$arg" | egrep -c "^-") ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ]; then ### Added a condition + eval $(echo args$i)=$(cygpath --path --ignore --mixed "$arg") + else + eval $(echo args$i)="\"$arg\"" + fi + i=$(expr $i + 1) + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save() { + for i; do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/"; done + echo " " +} +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/pay-app-spring-microservices/app-invoice/gradlew.bat b/pay-app-spring-microservices/app-invoice/gradlew.bat new file mode 100644 index 0000000..107acd3 --- /dev/null +++ b/pay-app-spring-microservices/app-invoice/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/pay-app-spring-microservices/app-invoice/settings.gradle b/pay-app-spring-microservices/app-invoice/settings.gradle new file mode 100644 index 0000000..e21d591 --- /dev/null +++ b/pay-app-spring-microservices/app-invoice/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'app-invoice' diff --git a/pay-app-spring-microservices/app-invoice/src/main/java/com/aforo/AppInvoiceApplication.java b/pay-app-spring-microservices/app-invoice/src/main/java/com/aforo/AppInvoiceApplication.java new file mode 100644 index 0000000..da591f6 --- /dev/null +++ b/pay-app-spring-microservices/app-invoice/src/main/java/com/aforo/AppInvoiceApplication.java @@ -0,0 +1,13 @@ +package com.aforo; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class AppInvoiceApplication { + + public static void main(String[] args) { + SpringApplication.run(AppInvoiceApplication.class, args); + } + +} diff --git a/pay-app-spring-microservices/app-invoice/src/main/java/com/aforo/controller/InvoiceController.java b/pay-app-spring-microservices/app-invoice/src/main/java/com/aforo/controller/InvoiceController.java new file mode 100644 index 0000000..9694557 --- /dev/null +++ b/pay-app-spring-microservices/app-invoice/src/main/java/com/aforo/controller/InvoiceController.java @@ -0,0 +1,29 @@ +package com.aforo.controller; + +import com.aforo.model.Invoice; +import com.aforo.service.InvoiceService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +@RestController +public class InvoiceController { + + @Autowired + private InvoiceService service; + + private Logger log = LoggerFactory.getLogger(InvoiceController.class); + + @GetMapping("/all") + public ResponseEntity> findAllInvoices() { + log.info("Consultando Invoices"); + var respose = service.findAllInvoices(); + return ResponseEntity.status(HttpStatus.CREATED).body(respose); + } +} diff --git a/pay-app-spring-microservices/app-invoice/src/main/java/com/aforo/dao/InvoiceDao.java b/pay-app-spring-microservices/app-invoice/src/main/java/com/aforo/dao/InvoiceDao.java new file mode 100644 index 0000000..b22452f --- /dev/null +++ b/pay-app-spring-microservices/app-invoice/src/main/java/com/aforo/dao/InvoiceDao.java @@ -0,0 +1,15 @@ +package com.aforo.dao; + +import com.aforo.model.Invoice; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.CrudRepository; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +public interface InvoiceDao extends CrudRepository { + + @Query("select i from Invoice i order by i.idInvoice") + List findAllInvoices(); +} diff --git a/pay-app-spring-microservices/app-invoice/src/main/java/com/aforo/kafka/consumer/ConsumerConfig.java b/pay-app-spring-microservices/app-invoice/src/main/java/com/aforo/kafka/consumer/ConsumerConfig.java new file mode 100644 index 0000000..4c0b63d --- /dev/null +++ b/pay-app-spring-microservices/app-invoice/src/main/java/com/aforo/kafka/consumer/ConsumerConfig.java @@ -0,0 +1,9 @@ +package com.aforo.kafka.consumer; + +import org.springframework.context.annotation.Configuration; +import org.springframework.kafka.annotation.EnableKafka; + +@Configuration +@EnableKafka +public class ConsumerConfig { +} diff --git a/pay-app-spring-microservices/app-invoice/src/main/java/com/aforo/kafka/consumer/TransactionEvents.java b/pay-app-spring-microservices/app-invoice/src/main/java/com/aforo/kafka/consumer/TransactionEvents.java new file mode 100644 index 0000000..2f8c67f --- /dev/null +++ b/pay-app-spring-microservices/app-invoice/src/main/java/com/aforo/kafka/consumer/TransactionEvents.java @@ -0,0 +1,32 @@ +package com.aforo.kafka.consumer; + +import com.aforo.dao.InvoiceDao; +import com.aforo.model.Invoice; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.kafka.clients.consumer.ConsumerRecord; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class TransactionEvents { + + @Autowired + private InvoiceDao _dao; + + @Autowired + private ObjectMapper objectMapper; + + private Logger log = LoggerFactory.getLogger(TransactionEvents.class); + + public void processTransactionEvent(ConsumerRecord consumerRecord) throws JsonProcessingException { + Invoice event = objectMapper.readValue(consumerRecord.value(), Invoice.class); + log.info("Actulizando Invoice ***" + event.getIdInvoice()); + event.setState(1); + log.info("Se ha pagado la factura # " + event.getIdInvoice()); + + _dao.save(event); + } +} diff --git a/pay-app-spring-microservices/app-invoice/src/main/java/com/aforo/kafka/listener/ConsumerListener.java b/pay-app-spring-microservices/app-invoice/src/main/java/com/aforo/kafka/listener/ConsumerListener.java new file mode 100644 index 0000000..29f4394 --- /dev/null +++ b/pay-app-spring-microservices/app-invoice/src/main/java/com/aforo/kafka/listener/ConsumerListener.java @@ -0,0 +1,27 @@ +package com.aforo.kafka.listener; + +import com.aforo.kafka.consumer.TransactionEvents; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonMappingException; +import org.apache.kafka.clients.consumer.ConsumerRecord; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.kafka.annotation.KafkaListener; +import org.springframework.stereotype.Component; + +@Component +public class ConsumerListener { + + @Autowired + private TransactionEvents events; + + private Logger log = LoggerFactory.getLogger(ConsumerListener.class); + + @KafkaListener(topics = {"transaction-events"}) + public void onMessage(ConsumerRecord consumerRecord) throws JsonMappingException, JsonProcessingException { + log.info("*************** MICROSERVICE APP INVOICE *******************"); + log.info("ConsumerRecord : {}", consumerRecord.value()); + events.processTransactionEvent(consumerRecord); + } +} diff --git a/pay-app-spring-microservices/app-invoice/src/main/java/com/aforo/model/Invoice.java b/pay-app-spring-microservices/app-invoice/src/main/java/com/aforo/model/Invoice.java new file mode 100644 index 0000000..bd907fd --- /dev/null +++ b/pay-app-spring-microservices/app-invoice/src/main/java/com/aforo/model/Invoice.java @@ -0,0 +1,18 @@ +package com.aforo.model; + +import lombok.Data; + +import javax.persistence.*; +import java.io.Serializable; + +@Entity +@Table(name="invoice") +@Data +public class Invoice implements Serializable { + + private static final long serialVersionUID = 1L; + @Id + private Integer idInvoice; + private Double amount ; + private Integer state ; +} diff --git a/pay-app-spring-microservices/app-invoice/src/main/java/com/aforo/service/InvoiceService.java b/pay-app-spring-microservices/app-invoice/src/main/java/com/aforo/service/InvoiceService.java new file mode 100644 index 0000000..f77a162 --- /dev/null +++ b/pay-app-spring-microservices/app-invoice/src/main/java/com/aforo/service/InvoiceService.java @@ -0,0 +1,19 @@ +package com.aforo.service; + +import com.aforo.dao.InvoiceDao; +import com.aforo.model.Invoice; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +public class InvoiceService { + + @Autowired + private InvoiceDao _dao; + + public List findAllInvoices() { + return _dao.findAllInvoices(); + } +} diff --git a/pay-app-spring-microservices/app-invoice/src/main/resources/application.properties b/pay-app-spring-microservices/app-invoice/src/main/resources/application.properties new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/pay-app-spring-microservices/app-invoice/src/main/resources/application.properties @@ -0,0 +1 @@ + diff --git a/pay-app-spring-microservices/app-invoice/src/main/resources/bootstrap.properties b/pay-app-spring-microservices/app-invoice/src/main/resources/bootstrap.properties new file mode 100644 index 0000000..b254c68 --- /dev/null +++ b/pay-app-spring-microservices/app-invoice/src/main/resources/bootstrap.properties @@ -0,0 +1,3 @@ +spring.application.name=app-invoice +spring.profiles.active=dev +spring.cloud.config.uri=http://app-config:8888 diff --git a/pay-app-spring-microservices/app-invoice/src/test/java/com/aforo/AppInvoiceApplicationTests.java b/pay-app-spring-microservices/app-invoice/src/test/java/com/aforo/AppInvoiceApplicationTests.java new file mode 100644 index 0000000..faa2489 --- /dev/null +++ b/pay-app-spring-microservices/app-invoice/src/test/java/com/aforo/AppInvoiceApplicationTests.java @@ -0,0 +1,13 @@ +package com.aforo; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class AppInvoiceApplicationTests { + + @Test + void contextLoads() { + } + +} diff --git a/pay-app-spring-microservices/app-pay/.gitignore b/pay-app-spring-microservices/app-pay/.gitignore new file mode 100644 index 0000000..c2065bc --- /dev/null +++ b/pay-app-spring-microservices/app-pay/.gitignore @@ -0,0 +1,37 @@ +HELP.md +.gradle +build/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/**/build/ +!**/src/test/**/build/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ diff --git a/pay-app-spring-microservices/app-pay/Dockerfile b/pay-app-spring-microservices/app-pay/Dockerfile new file mode 100644 index 0000000..95034c5 --- /dev/null +++ b/pay-app-spring-microservices/app-pay/Dockerfile @@ -0,0 +1,13 @@ +FROM openjdk:13-alpine as stage1 +COPY . /app +WORKDIR /app +RUN chmod 777 gradlew +RUN ./gradlew clean +RUN ./gradlew bootJar + + +FROM openjdk:13-alpine +EXPOSE 8010 +COPY --from=stage1 /app/build/libs/app-pay-1.0.0.jar app-pay.jar +#ADD ./build/libs/app-config-1.0.0.jar app-config.jar +ENTRYPOINT ["java", "-jar", "/app-pay.jar"] \ No newline at end of file diff --git a/pay-app-spring-microservices/app-pay/PROOF.md b/pay-app-spring-microservices/app-pay/PROOF.md new file mode 100644 index 0000000..89eeb63 --- /dev/null +++ b/pay-app-spring-microservices/app-pay/PROOF.md @@ -0,0 +1,5 @@ +# Request proof +1. +![alt text](request-1.png) +2. +![alt text](request-2.png) diff --git a/pay-app-spring-microservices/app-pay/README.md b/pay-app-spring-microservices/app-pay/README.md new file mode 100644 index 0000000..253df8a --- /dev/null +++ b/pay-app-spring-microservices/app-pay/README.md @@ -0,0 +1,12 @@ +# App Pay + +Microservicio encargado de registrar el pago de una factura y dejar un mensaje a la cola de kafka +para actualizar la factura desde el microservicio de app-invoice. También debe dejar un mensaje +para registrar el movimiento en el microservicio de transacciones. + +## Dependencias +* JPA (Hibernate) +* Apache Kafka +* Spring Cloud Config +* PostgreSQL +* Lombok diff --git a/pay-app-spring-microservices/app-pay/build.gradle b/pay-app-spring-microservices/app-pay/build.gradle new file mode 100644 index 0000000..fdaf2e0 --- /dev/null +++ b/pay-app-spring-microservices/app-pay/build.gradle @@ -0,0 +1,46 @@ +plugins { + id 'org.springframework.boot' version '2.3.10.RELEASE' + id 'io.spring.dependency-management' version '1.0.11.RELEASE' + id 'java' +} + +group = 'com.aforo' +version = '1.0.0' +sourceCompatibility = '11' + +configurations { + compileOnly { + extendsFrom annotationProcessor + } +} + +repositories { + mavenCentral() +} + +ext { + set('springCloudVersion', "Hoxton.SR11") +} + + +dependencies { + implementation 'org.springframework.boot:spring-boot-starter-data-jpa' + implementation 'org.springframework.boot:spring-boot-starter-web' + implementation 'org.springframework.kafka:spring-kafka' + implementation 'org.springframework.cloud:spring-cloud-starter-config' + compileOnly 'org.projectlombok:lombok' + runtimeOnly 'mysql:mysql-connector-java' + annotationProcessor 'org.projectlombok:lombok' + testImplementation 'org.springframework.boot:spring-boot-starter-test' + testImplementation 'org.springframework.kafka:spring-kafka-test' +} + +dependencyManagement { + imports { + mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}" + } +} + +test { + useJUnitPlatform() +} diff --git a/pay-app-spring-microservices/app-pay/gradle/wrapper/gradle-wrapper.jar b/pay-app-spring-microservices/app-pay/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..41d9927 Binary files /dev/null and b/pay-app-spring-microservices/app-pay/gradle/wrapper/gradle-wrapper.jar differ diff --git a/pay-app-spring-microservices/app-pay/gradle/wrapper/gradle-wrapper.properties b/pay-app-spring-microservices/app-pay/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..41dfb87 --- /dev/null +++ b/pay-app-spring-microservices/app-pay/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/pay-app-spring-microservices/app-pay/gradlew b/pay-app-spring-microservices/app-pay/gradlew new file mode 100644 index 0000000..1b6c787 --- /dev/null +++ b/pay-app-spring-microservices/app-pay/gradlew @@ -0,0 +1,234 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +APP_NAME="Gradle" +APP_BASE_NAME=${0##*/} + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/pay-app-spring-microservices/app-pay/gradlew.bat b/pay-app-spring-microservices/app-pay/gradlew.bat new file mode 100644 index 0000000..107acd3 --- /dev/null +++ b/pay-app-spring-microservices/app-pay/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/pay-app-spring-microservices/app-pay/request-1.png b/pay-app-spring-microservices/app-pay/request-1.png new file mode 100644 index 0000000..9daba2c Binary files /dev/null and b/pay-app-spring-microservices/app-pay/request-1.png differ diff --git a/pay-app-spring-microservices/app-pay/request-2.png b/pay-app-spring-microservices/app-pay/request-2.png new file mode 100644 index 0000000..5a18f15 Binary files /dev/null and b/pay-app-spring-microservices/app-pay/request-2.png differ diff --git a/pay-app-spring-microservices/app-pay/settings.gradle b/pay-app-spring-microservices/app-pay/settings.gradle new file mode 100644 index 0000000..36f0da7 --- /dev/null +++ b/pay-app-spring-microservices/app-pay/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'app-pay' diff --git a/pay-app-spring-microservices/app-pay/src/main/java/com/aforo/AppPayApplication.java b/pay-app-spring-microservices/app-pay/src/main/java/com/aforo/AppPayApplication.java new file mode 100644 index 0000000..6a38973 --- /dev/null +++ b/pay-app-spring-microservices/app-pay/src/main/java/com/aforo/AppPayApplication.java @@ -0,0 +1,13 @@ +package com.aforo; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class AppPayApplication { + + public static void main(String[] args) { + SpringApplication.run(AppPayApplication.class, args); + } + +} diff --git a/pay-app-spring-microservices/app-pay/src/main/java/com/aforo/config/AutoCreateConfig.java b/pay-app-spring-microservices/app-pay/src/main/java/com/aforo/config/AutoCreateConfig.java new file mode 100644 index 0000000..9ef4dcf --- /dev/null +++ b/pay-app-spring-microservices/app-pay/src/main/java/com/aforo/config/AutoCreateConfig.java @@ -0,0 +1,19 @@ +package com.aforo.config; + +import org.apache.kafka.clients.admin.NewTopic; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.kafka.config.TopicBuilder; + +@Configuration +public class AutoCreateConfig { + + @Bean + public NewTopic depositEvent() { + return TopicBuilder + .name("trasaction-events") + .partitions(3) + .replicas(1) + .build(); + } +} diff --git a/pay-app-spring-microservices/app-pay/src/main/java/com/aforo/controller/PayController.java b/pay-app-spring-microservices/app-pay/src/main/java/com/aforo/controller/PayController.java new file mode 100644 index 0000000..d627039 --- /dev/null +++ b/pay-app-spring-microservices/app-pay/src/main/java/com/aforo/controller/PayController.java @@ -0,0 +1,36 @@ +package com.aforo.controller; + +import com.aforo.kafka.producer.PayEventProducer; +import com.aforo.model.Pay; +import com.aforo.service.PayService; +import com.fasterxml.jackson.core.JsonProcessingException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class PayController { + + @Autowired + private PayService service; + + @Autowired + PayEventProducer producer; + + private Logger log = LoggerFactory.getLogger(PayController.class); + + @PostMapping("/pay") + public ResponseEntity postDepositEvent(@RequestBody Pay pay) throws JsonProcessingException { + log.info("Registrando nuevo pago"); + var respose = service.registerPay(pay); + log.info("Enviando mensaje a Kafka"); + producer.sendPayEvent(respose); + log.info("Mensaje agregado a la cola correctamente"); + return ResponseEntity.status(HttpStatus.CREATED).body(respose); + } +} diff --git a/pay-app-spring-microservices/app-pay/src/main/java/com/aforo/dao/PayDao.java b/pay-app-spring-microservices/app-pay/src/main/java/com/aforo/dao/PayDao.java new file mode 100644 index 0000000..4a7ee91 --- /dev/null +++ b/pay-app-spring-microservices/app-pay/src/main/java/com/aforo/dao/PayDao.java @@ -0,0 +1,9 @@ +package com.aforo.dao; + +import com.aforo.model.Pay; +import org.springframework.data.repository.CrudRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface PayDao extends CrudRepository { +} diff --git a/pay-app-spring-microservices/app-pay/src/main/java/com/aforo/kafka/producer/PayEventProducer.java b/pay-app-spring-microservices/app-pay/src/main/java/com/aforo/kafka/producer/PayEventProducer.java new file mode 100644 index 0000000..1f970f4 --- /dev/null +++ b/pay-app-spring-microservices/app-pay/src/main/java/com/aforo/kafka/producer/PayEventProducer.java @@ -0,0 +1,73 @@ +package com.aforo.kafka.producer; + +import com.aforo.model.Pay; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.kafka.clients.producer.ProducerRecord; +import org.apache.kafka.common.header.Header; +import org.apache.kafka.common.header.internals.RecordHeader; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.kafka.core.KafkaTemplate; +import org.springframework.kafka.support.SendResult; +import org.springframework.stereotype.Component; +import org.springframework.util.concurrent.ListenableFuture; +import org.springframework.util.concurrent.ListenableFutureCallback; + +import java.util.List; + + +@Component +public class PayEventProducer { + + @Autowired + private KafkaTemplate kafkaTemplate; + + @Autowired + private ObjectMapper objectMapper; + + private Logger log = LoggerFactory.getLogger(PayEventProducer.class); + String topic = "transaction-events"; + + public ListenableFuture> sendPayEvent(Pay pay) throws JsonProcessingException { + Integer key = pay.getIdOperation(); + String value = objectMapper.writeValueAsString(pay); + + ProducerRecord producerRecord = buildProducerRecord(key, value, topic); + ListenableFuture> listenableFuture = kafkaTemplate.send(producerRecord); + listenableFuture.addCallback(new ListenableFutureCallback<>() { + + @Override + public void onSuccess(SendResult result) { + handleSuccess(key, value, result); + } + + @Override + public void onFailure(Throwable ex) { + handleFailure(key, value, ex); + } + }); + + return listenableFuture; + } + + private ProducerRecord buildProducerRecord(Integer key, String value, String topic) { + List
recordHeaders = List.of(new RecordHeader("payment-event-source", "scanner".getBytes())); + return new ProducerRecord<>(topic, null, key, value, recordHeaders); + } + + private void handleFailure(Integer key, String value, Throwable e) { + log.error("Error enviando el mensaje, error: {} ", e.getMessage()); + try { + + } catch (Throwable ex) { + log.error("Error OnFailure: {}", ex.getMessage()); + } + } + + private void handleSuccess(Integer key, String value, SendResult result) { + log.info("Message Sent Successfully for the key :{} and the value is {},partition is {}", key, value, + result.getRecordMetadata().partition()); + } +} diff --git a/pay-app-spring-microservices/app-pay/src/main/java/com/aforo/model/Pay.java b/pay-app-spring-microservices/app-pay/src/main/java/com/aforo/model/Pay.java new file mode 100644 index 0000000..2379867 --- /dev/null +++ b/pay-app-spring-microservices/app-pay/src/main/java/com/aforo/model/Pay.java @@ -0,0 +1,24 @@ +package com.aforo.model; + +import lombok.Data; + +import javax.persistence.*; +import java.io.Serializable; +import java.util.Date; + +@Entity +@Table(name="pay") +@Data +public class Pay implements Serializable { + + private static final long serialVersionUID = 1L; + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id_operation") + private Integer idOperation; + @Column(name = "id_invoice") + private Integer idInvoice; + private Double amount; + @Temporal(TemporalType.TIMESTAMP) + private Date dateTime; +} diff --git a/pay-app-spring-microservices/app-pay/src/main/java/com/aforo/service/PayService.java b/pay-app-spring-microservices/app-pay/src/main/java/com/aforo/service/PayService.java new file mode 100644 index 0000000..ae70de3 --- /dev/null +++ b/pay-app-spring-microservices/app-pay/src/main/java/com/aforo/service/PayService.java @@ -0,0 +1,17 @@ +package com.aforo.service; + +import com.aforo.dao.PayDao; +import com.aforo.model.Pay; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class PayService { + + @Autowired + private PayDao _dao; + + public Pay registerPay(Pay pay) { + return _dao.save(pay); + } +} diff --git a/pay-app-spring-microservices/app-pay/src/main/resources/application.properties b/pay-app-spring-microservices/app-pay/src/main/resources/application.properties new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/pay-app-spring-microservices/app-pay/src/main/resources/application.properties @@ -0,0 +1 @@ + diff --git a/pay-app-spring-microservices/app-pay/src/main/resources/bootstrap.properties b/pay-app-spring-microservices/app-pay/src/main/resources/bootstrap.properties new file mode 100644 index 0000000..91cb925 --- /dev/null +++ b/pay-app-spring-microservices/app-pay/src/main/resources/bootstrap.properties @@ -0,0 +1,3 @@ +spring.application.name=app-pay +spring.profiles.active=dev +spring.cloud.config.uri=http://app-config:8888 diff --git a/pay-app-spring-microservices/app-pay/src/test/java/com/aforo/AppPayApplicationTests.java b/pay-app-spring-microservices/app-pay/src/test/java/com/aforo/AppPayApplicationTests.java new file mode 100644 index 0000000..d45f2ea --- /dev/null +++ b/pay-app-spring-microservices/app-pay/src/test/java/com/aforo/AppPayApplicationTests.java @@ -0,0 +1,13 @@ +package com.aforo; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class AppPayApplicationTests { + + @Test + void contextLoads() { + } + +} diff --git a/pay-app-spring-microservices/app-transaction/.gitignore b/pay-app-spring-microservices/app-transaction/.gitignore new file mode 100644 index 0000000..c2065bc --- /dev/null +++ b/pay-app-spring-microservices/app-transaction/.gitignore @@ -0,0 +1,37 @@ +HELP.md +.gradle +build/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/**/build/ +!**/src/test/**/build/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ diff --git a/pay-app-spring-microservices/app-transaction/Dockerfile b/pay-app-spring-microservices/app-transaction/Dockerfile new file mode 100644 index 0000000..e413cd3 --- /dev/null +++ b/pay-app-spring-microservices/app-transaction/Dockerfile @@ -0,0 +1,13 @@ +FROM openjdk:13-alpine as stage1 +COPY . /app +WORKDIR /app +RUN chmod 777 gradlew +RUN ./gradlew clean +RUN ./gradlew bootJar + + +FROM openjdk:13-alpine +EXPOSE 8082 +COPY --from=stage1 /app/build/libs/app-transaction-1.0.0.jar app-transaction.jar +#ADD ./build/libs/app-config-1.0.0.jar app-config.jar +ENTRYPOINT ["java", "-jar", "/app-transaction.jar"] diff --git a/pay-app-spring-microservices/app-transaction/PROOF.md b/pay-app-spring-microservices/app-transaction/PROOF.md new file mode 100644 index 0000000..1c034da --- /dev/null +++ b/pay-app-spring-microservices/app-transaction/PROOF.md @@ -0,0 +1,4 @@ +# Request proof +![alt text](transaction-request.png) + +As we can see, there they are, the 2 payments that we made with the according amounts \ No newline at end of file diff --git a/pay-app-spring-microservices/app-transaction/build.gradle b/pay-app-spring-microservices/app-transaction/build.gradle new file mode 100644 index 0000000..1ca3403 --- /dev/null +++ b/pay-app-spring-microservices/app-transaction/build.gradle @@ -0,0 +1,44 @@ +plugins { + id 'org.springframework.boot' version '2.3.10.RELEASE' + id 'io.spring.dependency-management' version '1.0.11.RELEASE' + id 'java' +} + +group = 'com.aforo' +version = '1.0.0' +sourceCompatibility = '11' + +configurations { + compileOnly { + extendsFrom annotationProcessor + } +} + +repositories { + mavenCentral() +} + +ext { + set('springCloudVersion', "Hoxton.SR11") +} + +dependencies { + implementation 'org.springframework.boot:spring-boot-starter-data-mongodb' + implementation 'org.springframework.boot:spring-boot-starter-web' + implementation 'org.springframework.kafka:spring-kafka' + implementation 'org.springframework.cloud:spring-cloud-starter-config' + compileOnly 'org.projectlombok:lombok' + annotationProcessor 'org.projectlombok:lombok' + testImplementation 'org.springframework.boot:spring-boot-starter-test' + testImplementation 'org.springframework.kafka:spring-kafka-test' +} + +dependencyManagement { + imports { + mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}" + } +} + +test { + useJUnitPlatform() +} diff --git a/pay-app-spring-microservices/app-transaction/gradle/wrapper/gradle-wrapper.jar b/pay-app-spring-microservices/app-transaction/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..41d9927 Binary files /dev/null and b/pay-app-spring-microservices/app-transaction/gradle/wrapper/gradle-wrapper.jar differ diff --git a/pay-app-spring-microservices/app-transaction/gradle/wrapper/gradle-wrapper.properties b/pay-app-spring-microservices/app-transaction/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..41dfb87 --- /dev/null +++ b/pay-app-spring-microservices/app-transaction/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/pay-app-spring-microservices/app-transaction/gradlew b/pay-app-spring-microservices/app-transaction/gradlew new file mode 100644 index 0000000..1b6c787 --- /dev/null +++ b/pay-app-spring-microservices/app-transaction/gradlew @@ -0,0 +1,234 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +APP_NAME="Gradle" +APP_BASE_NAME=${0##*/} + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/pay-app-spring-microservices/app-transaction/gradlew.bat b/pay-app-spring-microservices/app-transaction/gradlew.bat new file mode 100644 index 0000000..107acd3 --- /dev/null +++ b/pay-app-spring-microservices/app-transaction/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/pay-app-spring-microservices/app-transaction/settings.gradle b/pay-app-spring-microservices/app-transaction/settings.gradle new file mode 100644 index 0000000..22a90a6 --- /dev/null +++ b/pay-app-spring-microservices/app-transaction/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'app-transaction' diff --git a/pay-app-spring-microservices/app-transaction/src/main/java/com/aforo/AppTransactionApplication.java b/pay-app-spring-microservices/app-transaction/src/main/java/com/aforo/AppTransactionApplication.java new file mode 100644 index 0000000..83b1bf1 --- /dev/null +++ b/pay-app-spring-microservices/app-transaction/src/main/java/com/aforo/AppTransactionApplication.java @@ -0,0 +1,13 @@ +package com.aforo; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class AppTransactionApplication { + + public static void main(String[] args) { + SpringApplication.run(AppTransactionApplication.class, args); + } + +} diff --git a/pay-app-spring-microservices/app-transaction/src/main/java/com/aforo/controller/TransactionController.java b/pay-app-spring-microservices/app-transaction/src/main/java/com/aforo/controller/TransactionController.java new file mode 100644 index 0000000..ed44a2d --- /dev/null +++ b/pay-app-spring-microservices/app-transaction/src/main/java/com/aforo/controller/TransactionController.java @@ -0,0 +1,29 @@ +package com.aforo.controller; + +import com.aforo.model.Transaction; +import com.aforo.service.TransactionService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +@RestController +public class TransactionController { + + @Autowired + private TransactionService service; + + private Logger log = LoggerFactory.getLogger(TransactionController.class); + + @GetMapping("/all") + public ResponseEntity> findAllInvoices() { + log.info("Consultando Invoices"); + var respose = service.findAllTransaction(); + return ResponseEntity.status(HttpStatus.CREATED).body(respose); + } +} diff --git a/pay-app-spring-microservices/app-transaction/src/main/java/com/aforo/dao/TransactionDao.java b/pay-app-spring-microservices/app-transaction/src/main/java/com/aforo/dao/TransactionDao.java new file mode 100644 index 0000000..bb01f57 --- /dev/null +++ b/pay-app-spring-microservices/app-transaction/src/main/java/com/aforo/dao/TransactionDao.java @@ -0,0 +1,9 @@ +package com.aforo.dao; + +import com.aforo.model.Transaction; +import org.springframework.data.mongodb.repository.MongoRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface TransactionDao extends MongoRepository { +} diff --git a/pay-app-spring-microservices/app-transaction/src/main/java/com/aforo/kafka/consumer/ConsumerConfig.java b/pay-app-spring-microservices/app-transaction/src/main/java/com/aforo/kafka/consumer/ConsumerConfig.java new file mode 100644 index 0000000..4c0b63d --- /dev/null +++ b/pay-app-spring-microservices/app-transaction/src/main/java/com/aforo/kafka/consumer/ConsumerConfig.java @@ -0,0 +1,9 @@ +package com.aforo.kafka.consumer; + +import org.springframework.context.annotation.Configuration; +import org.springframework.kafka.annotation.EnableKafka; + +@Configuration +@EnableKafka +public class ConsumerConfig { +} diff --git a/pay-app-spring-microservices/app-transaction/src/main/java/com/aforo/kafka/consumer/TransactionEvents.java b/pay-app-spring-microservices/app-transaction/src/main/java/com/aforo/kafka/consumer/TransactionEvents.java new file mode 100644 index 0000000..3e54492 --- /dev/null +++ b/pay-app-spring-microservices/app-transaction/src/main/java/com/aforo/kafka/consumer/TransactionEvents.java @@ -0,0 +1,29 @@ +package com.aforo.kafka.consumer; + +import com.aforo.dao.TransactionDao; +import com.aforo.model.Transaction; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.kafka.clients.consumer.ConsumerRecord; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class TransactionEvents { + + @Autowired + private TransactionDao _dao; + + @Autowired + private ObjectMapper objectMapper; + + private Logger log = LoggerFactory.getLogger(TransactionEvents.class); + + public void processTransactionEvent(ConsumerRecord consumerRecord) throws JsonProcessingException { + Transaction event = objectMapper.readValue(consumerRecord.value(), Transaction.class); + log.info("Registrando Transaccion Invoice ***" + event.getIdInvoice()); + _dao.save(event); + } +} diff --git a/pay-app-spring-microservices/app-transaction/src/main/java/com/aforo/kafka/listener/ConsumerListener.java b/pay-app-spring-microservices/app-transaction/src/main/java/com/aforo/kafka/listener/ConsumerListener.java new file mode 100644 index 0000000..f82cbd5 --- /dev/null +++ b/pay-app-spring-microservices/app-transaction/src/main/java/com/aforo/kafka/listener/ConsumerListener.java @@ -0,0 +1,27 @@ +package com.aforo.kafka.listener; + +import com.aforo.kafka.consumer.TransactionEvents; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonMappingException; +import org.apache.kafka.clients.consumer.ConsumerRecord; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.kafka.annotation.KafkaListener; +import org.springframework.stereotype.Component; + +@Component +public class ConsumerListener { + + @Autowired + private TransactionEvents events; + + private Logger log = LoggerFactory.getLogger(ConsumerListener.class); + + @KafkaListener(topics = {"transaction-events"}) + public void onMessage(ConsumerRecord consumerRecord) throws JsonMappingException, JsonProcessingException { + log.info("*************** MICROSERVICE APP TRANSACTION *******************"); + log.info("ConsumerRecord : {}", consumerRecord.value()); + events.processTransactionEvent(consumerRecord); + } +} diff --git a/pay-app-spring-microservices/app-transaction/src/main/java/com/aforo/model/Transaction.java b/pay-app-spring-microservices/app-transaction/src/main/java/com/aforo/model/Transaction.java new file mode 100644 index 0000000..1b4dcfb --- /dev/null +++ b/pay-app-spring-microservices/app-transaction/src/main/java/com/aforo/model/Transaction.java @@ -0,0 +1,20 @@ +package com.aforo.model; + +import lombok.Data; +import org.bson.codecs.pojo.annotations.BsonId; +import org.springframework.data.mongodb.core.mapping.Document; + +import java.io.Serializable; +import java.util.Date; + +@Document +@Data +public class Transaction implements Serializable { + + private static final long serialVersionUID = 1L; + @BsonId + private String idTransaction; + private Integer idInvoice; + private Double amount ; + private Date dateTime; +} diff --git a/pay-app-spring-microservices/app-transaction/src/main/java/com/aforo/service/TransactionService.java b/pay-app-spring-microservices/app-transaction/src/main/java/com/aforo/service/TransactionService.java new file mode 100644 index 0000000..e92eb45 --- /dev/null +++ b/pay-app-spring-microservices/app-transaction/src/main/java/com/aforo/service/TransactionService.java @@ -0,0 +1,19 @@ +package com.aforo.service; + +import com.aforo.dao.TransactionDao; +import com.aforo.model.Transaction; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +public class TransactionService { + + @Autowired + private TransactionDao _dao; + + public List findAllTransaction() { + return _dao.findAll(); + } +} diff --git a/pay-app-spring-microservices/app-transaction/src/main/resources/application.properties b/pay-app-spring-microservices/app-transaction/src/main/resources/application.properties new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/pay-app-spring-microservices/app-transaction/src/main/resources/application.properties @@ -0,0 +1 @@ + diff --git a/pay-app-spring-microservices/app-transaction/src/main/resources/bootstrap.properties b/pay-app-spring-microservices/app-transaction/src/main/resources/bootstrap.properties new file mode 100644 index 0000000..2bd5d08 --- /dev/null +++ b/pay-app-spring-microservices/app-transaction/src/main/resources/bootstrap.properties @@ -0,0 +1,3 @@ +spring.application.name=app-transaction +spring.profiles.active=dev +spring.cloud.config.uri=http://app-config:8888 diff --git a/pay-app-spring-microservices/app-transaction/src/test/java/com/aforo/AppTransactionApplicationTests.java b/pay-app-spring-microservices/app-transaction/src/test/java/com/aforo/AppTransactionApplicationTests.java new file mode 100644 index 0000000..9416622 --- /dev/null +++ b/pay-app-spring-microservices/app-transaction/src/test/java/com/aforo/AppTransactionApplicationTests.java @@ -0,0 +1,13 @@ +package com.aforo; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class AppTransactionApplicationTests { + + @Test + void contextLoads() { + } + +} diff --git a/pay-app-spring-microservices/app-transaction/transaction-request.png b/pay-app-spring-microservices/app-transaction/transaction-request.png new file mode 100644 index 0000000..b491287 Binary files /dev/null and b/pay-app-spring-microservices/app-transaction/transaction-request.png differ diff --git a/pay-app-spring-microservices/appgw/gateway.config.yml b/pay-app-spring-microservices/appgw/gateway.config.yml new file mode 100644 index 0000000..1a7f68b --- /dev/null +++ b/pay-app-spring-microservices/appgw/gateway.config.yml @@ -0,0 +1,35 @@ +http: + port: 8080 +admin: + port: 9876 + host: localhost +apiEndpoints: + appconfig: + host: localhost + paths: ['/config','/config/*'] +serviceEndpoints: + appconfig: + url: 'http://loadbalancer/config/' +policies: + - basic-auth + - cors + - expression + - key-auth + - log + - oauth2 + - proxy + - rate-limit +pipelines: + default: + apiEndpoints: + - appconfig + policies: + # Uncomment `key-auth:` when instructed to in the Getting Started guide. + - key-auth: + - proxy: + - action: + serviceEndpoint: appconfig + changeOrigin: true + prependPath: false + ignorePath: false + stripPath: false diff --git a/pay-app-spring-microservices/appgw/models/applications.json b/pay-app-spring-microservices/appgw/models/applications.json new file mode 100644 index 0000000..8b9c248 --- /dev/null +++ b/pay-app-spring-microservices/appgw/models/applications.json @@ -0,0 +1,16 @@ +{ + "$id": "http://express-gateway.io/models/applications.json", + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "redirectUri": { + "type": "string", + "format": "uri" + } + }, + "required": [ + "name" + ] +} diff --git a/pay-app-spring-microservices/appgw/models/credentials.json b/pay-app-spring-microservices/appgw/models/credentials.json new file mode 100644 index 0000000..3762505 --- /dev/null +++ b/pay-app-spring-microservices/appgw/models/credentials.json @@ -0,0 +1,85 @@ +{ + "$id": "http://express-gateway.io/models/credentials.json", + "type": "object", + "definitions": { + "credentialBase": { + "type": "object", + "properties": { + "autoGeneratePassword": { + "type": "boolean", + "default": true + }, + "scopes": { + "type": [ + "string", + "array" + ], + "items": { + "type": "string" + } + } + }, + "required": [ + "autoGeneratePassword" + ] + } + }, + "properties": { + "basic-auth": { + "allOf": [ + { + "$ref": "#/definitions/credentialBase" + }, + { + "type": "object", + "properties": { + "passwordKey": { + "type": "string", + "default": "password" + } + }, + "required": [ + "passwordKey" + ] + } + ] + }, + "key-auth": { + "type": "object", + "properties": { + "scopes": { + "type": [ + "string", + "array" + ], + "items": { + "type": "string" + } + } + } + }, + "jwt": { + "type": "object", + "properties": {} + }, + "oauth2": { + "allOf": [ + { + "$ref": "#/definitions/credentialBase" + }, + { + "type": "object", + "properties": { + "passwordKey": { + "type": "string", + "default": "secret" + } + }, + "required": [ + "passwordKey" + ] + } + ] + } + } +} diff --git a/pay-app-spring-microservices/appgw/models/users.json b/pay-app-spring-microservices/appgw/models/users.json new file mode 100644 index 0000000..0ad047f --- /dev/null +++ b/pay-app-spring-microservices/appgw/models/users.json @@ -0,0 +1,28 @@ +{ + "$id": "http://express-gateway.io/models/users.json", + "type": "object", + "properties": { + "firstname": { + "type": "string" + }, + "lastname": { + "type": "string" + }, + "username": { + "type": "string" + }, + "email": { + "type": "string", + "format": "email" + }, + "redirectUri": { + "type": "string", + "format": "uri" + } + }, + "required": [ + "username", + "firstname", + "lastname" + ] +} diff --git a/pay-app-spring-microservices/appgw/system.config.yml b/pay-app-spring-microservices/appgw/system.config.yml new file mode 100644 index 0000000..7f3e32a --- /dev/null +++ b/pay-app-spring-microservices/appgw/system.config.yml @@ -0,0 +1,27 @@ +# Core +db: + redis: + host: express-gateway-data-store + port: 6379 + namespace: EG + +#plugins: + # express-gateway-plugin-example: + # param1: 'param from system.config' + +crypto: + cipherKey: sensitiveKey + algorithm: aes256 + saltRounds: 10 + +# OAuth2 Settings +session: + secret: keyboard cat + resave: false + saveUninitialized: false +accessTokens: + timeToExpiry: 7200000 +refreshTokens: + timeToExpiry: 7200000 +authorizationCodes: + timeToExpiry: 300000 diff --git a/pay-app-spring-microservices/commands.md b/pay-app-spring-microservices/commands.md new file mode 100644 index 0000000..4c7fa26 --- /dev/null +++ b/pay-app-spring-microservices/commands.md @@ -0,0 +1,98 @@ +docker network create distribuidos +docker run -p 5432:5432 --name postgres --network distribuidos -e POSTGRES_PASSWORD=postgres -e POSTGRES_DB=db_invoice -d icesiops/postgres:0.1.0 + +docker run -p 3306:3306 --name mysql --network distribuidos -e MYSQL_ROOT_PASSWORD=mysql -e MYSQL_DATABASE=db_operation -d icesiops/mysql:0.1.0 + +docker run -p 27017:27017 --network distribuidos --name mongodb -d mongo + +docker run -p 2181:2181 -d -p 9092:9092 --name servicekafka --network distribuidos -e ADVERTISED_HOST=servicekafka -e NUM_PARTITIONS=3 johnnypark/kafka-zookeeper + +docker run -d -p 8888:8888 --network distribuidos --name app-config icesiops/appconfig:0.1.0 + +docker run -d -p 8006:8006 --network distribuidos --name app-invoice icesiops/appinvoice:0.1.0 + +docker run -d -p 8010:8010 --network distribuidos --name app-pay icesiops/apppay:0.1.0 + +docker run -d -p 8082:8082 --network distribuidos --name app-transaction icesiops/apptransaction:0.1.0 + +psql -h localhost -d db_invoice -U postgres -f data.sql + +#### consul + +Modify application.properties file according to consul server information. +Add the line implementation 'org.springframework.cloud:spring-cloud-starter-consul-discovery' into build.gradle depedencies +Install dnsmasq +Create a config file for dnsmasq below the path /etc/dnsmasq.d +Add the next line server=/consul/127.0.0.1#8600 +start dnsmasq +modifiy resolv.conf to add ip loopback like dns server +run command: dig app-service.service.consul + +docker run -d -p 8500:8500 -p 8600:8600/udp --network distribuidos --name consul consul:latest agent -server -bootstrap-expect 1 -ui -data-dir /tmp -client=0.0.0.0 + +### Load Balancer +Create dockerfile +FROM haproxy:2.3 +COPY haproxy.cfg /usr/local/etc/haproxy/haproxy.cfg + +Create haproxy config +defaults + timeout connect 5s + timeout client 1m + timeout server 1m + +frontend stats + bind *:1936 + mode http + stats uri / + stats show-legends + no log + +frontend http_front + bind *:80 + default_backend http_back + +backend http_back + balance roundrobin + server-template mywebapp 1-10 _web._tcp.service.consul resolvers consul resolve-opts allow-dup-ip resolve-prefer ipv4 check + +resolvers consul + nameserver consul 127.0.0.1:8600 + accepted_payload_size 8192 + hold valid 5s + +docker build -t icesiops/loadbalancer:0.1.0 . + +### Application Gateway + +In order to use Identity features, we need to have a data storage like Redis. + +docker run --network distribuidos -d --name express-gateway-data-store \ + -p 6379:6379 \ + redis:alpine +2. Start the Express-Gateway instance +Run the command inside appgw directory o keep in mind change the volume path to pointing to gateway.config.yml +docker run -d --name express-gateway \ + --network distribuidos \ + -v .:/var/lib/eg \ + -p 8080:8080 \ + -p 9876:9876 \ + express-gateway + +3. uncoment #key-auth +4. connect to gw container +docker exec -it express-gateway sh + +5. create users +eg users create + +6. assign auth key +eg credentials create -c sebas -t key-auth -q + +7. copy key 3DvE2HCfZCyfgxAjF40tOk:2U4Cojm11JaPJF6WRUcFBL + +8. Curl API endpoint as Sebas with key credentials - SUCCESS! + +curl -H "Authorization: apiKey ${keyId}:${keySecret}" http://localhost:8080/config/app-pay/dev + +curl -H "Authorization: apiKey 3DvE2HCfZCyfgxAjF40tOk:2U4Cojm11JaPJF6WRUcFBL" http://localhost:8080/config/app-pay/dev diff --git a/pay-app-spring-microservices/config/app-invoice-dev.properties b/pay-app-spring-microservices/config/app-invoice-dev.properties new file mode 100644 index 0000000..23d5f14 --- /dev/null +++ b/pay-app-spring-microservices/config/app-invoice-dev.properties @@ -0,0 +1,31 @@ +# Server +spring.application.name=app-invoice +server.port=8006 + +# Kafka +spring.kafka.consumer.bootstrap-servers=servicekafka:9092 +spring.kafka.consumer.bootstrap-servers=servicekafka:9092 +spring.kafka.admin.properties.bootstrap.servers=servicekafka:9092 +spring.kafka.admin.properties.bootstrap.servers=servicekafka:9092 +spring.kafka.consumer.key-deserializer=org.apache.kafka.common.serialization.IntegerDeserializer +spring.kafka.consumer.value-deserializer=org.apache.kafka.common.serialization.StringDeserializer +spring.kafka.consumer.group-id=invoice-events-listener-group + +# JPA +logging.level.org.hibernate.SQL=debug +spring.jpa.properties.hibernate.enable_lazy_load_no_trans=true +spring.jpa.hibernate.ddl-auto=create + +# Postgresql +spring.datasource.url=jdbc:postgresql://postgres:5432/db_invoice +#spring.datasource.url=jdbc:postgresql://postgres:5432/db_invoice +spring.datasource.username=postgres +spring.datasource.password=postgres +spring.datasource.driver-class-name=org.postgresql.Driver +spring.jpa.database-platform=org.hibernate.dialect.PostgreSQL95Dialect + +# Consul +spring.cloud.consul.host=consul +spring.cloud.consul.port=8500 +spring.cloud.consul.discovery.health-check-interval=5s +spring.cloud.consul.discovery.prefer-ip-address=true diff --git a/pay-app-spring-microservices/config/app-pay-dev.properties b/pay-app-spring-microservices/config/app-pay-dev.properties new file mode 100644 index 0000000..b7959d8 --- /dev/null +++ b/pay-app-spring-microservices/config/app-pay-dev.properties @@ -0,0 +1,25 @@ +# Server +spring.application.name=app-pay +server.port=8010 + +# Kafka +spring.kafka.template.default-topic=transaction-events +spring.kafka.producer.bootstrap-servers=servicekafka:9092 +#spring.kafka.producer.bootstrap-servers=servicekafka:9092 +spring.kafka.producer.key-serializer=org.apache.kafka.common.serialization.IntegerSerializer +spring.kafka.producer.value-serializer=org.apache.kafka.common.serialization.StringSerializer +spring.kafka.admin.properties.bootstrap.servers=servicekafka:9092 +#spring.kafka.admin.properties.bootstrap.servers=servicekafka:9092 + +# JPA +logging.level.org.hibernate.SQL=debug +spring.jpa.properties.hibernate.enable_lazy_load_no_trans=true +spring.jpa.hibernate.ddl-auto=create + +# MySQL +spring.datasource.url=jdbc:mysql://mysql:3306/db_operation?serverTimezone=America/Lima&allowPublicKeyRetrieval=true&useSSL=false +#spring.datasource.url=jdbc:mysql://mysql:3306/db_operation?serverTimezone=America/Lima&allowPublicKeyRetrieval=true&useSSL=false +spring.datasource.username=root +spring.datasource.password=mysql +spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver +spring.jpa.database-platform=org.hibernate.dialect.MySQL8Dialect \ No newline at end of file diff --git a/pay-app-spring-microservices/config/app-transaction-dev.properties b/pay-app-spring-microservices/config/app-transaction-dev.properties new file mode 100644 index 0000000..17d29f4 --- /dev/null +++ b/pay-app-spring-microservices/config/app-transaction-dev.properties @@ -0,0 +1,16 @@ +# Server +spring.application.name=app-transaction +server.port=8082 + +# Kafka +spring.kafka.consumer.bootstrap-servers=servicekafka:9092 +#spring.kafka.consumer.bootstrap-servers=servicekafka:9092 +spring.kafka.admin.properties.bootstrap.servers=servicekafka:9092 +#spring.kafka.admin.properties.bootstrap.servers=servicekafka:9092 +spring.kafka.consumer.key-deserializer=org.apache.kafka.common.serialization.IntegerDeserializer +spring.kafka.consumer.value-deserializer=org.apache.kafka.common.serialization.StringDeserializer +spring.kafka.consumer.group-id=transaction-events-listener-group + +# MongoDB +spring.data.mongodb.uri=mongodb://mongodb:27017/db_transaction +#spring.data.mongodb.uri=mongodb://mongodb:27017/db_transaction \ No newline at end of file diff --git a/pay-app-spring-microservices/haproxy/Dockerfile b/pay-app-spring-microservices/haproxy/Dockerfile new file mode 100644 index 0000000..9e9a3ad --- /dev/null +++ b/pay-app-spring-microservices/haproxy/Dockerfile @@ -0,0 +1,2 @@ +FROM haproxy:2.3 +COPY haproxy.cfg /usr/local/etc/haproxy/haproxy.cfg \ No newline at end of file diff --git a/pay-app-spring-microservices/haproxy/haproxy.cfg b/pay-app-spring-microservices/haproxy/haproxy.cfg new file mode 100644 index 0000000..3b54b0c --- /dev/null +++ b/pay-app-spring-microservices/haproxy/haproxy.cfg @@ -0,0 +1,34 @@ +defaults + timeout connect 5s + timeout client 1m + timeout server 1m + +frontend stats + bind *:1936 + mode http + stats uri / + stats show-legends + no log + +frontend http_front + bind *:80 + mode http + acl url_config path_beg /config + use_backend config_back if url_config + + default_backend http_back + +backend config_back + mode http + balance roundrobin + http-request set-path "%[path,regsub(^/config/,/)]" + server appconfig app-config.service.consul:8888 resolvers consul resolve-prefer ipv4 check +backend http_back + mode http + balance roundrobin + server-template mywebapp 1-10 _web._tcp.service.consul resolvers consul resolve-prefer ipv4 check + +resolvers consul + nameserver consul consul:8600 + accepted_payload_size 8192 + hold valid 5s diff --git a/pay-app-spring-microservices/resources/DB.md b/pay-app-spring-microservices/resources/DB.md new file mode 100644 index 0000000..231dbb3 --- /dev/null +++ b/pay-app-spring-microservices/resources/DB.md @@ -0,0 +1,54 @@ +# Endpoints + +A continuacion se detallan los scripts para la creación de las bases de datos respectivas para los microservicios + + +## PostgreSQL + +1. Levantar docker de PostgreSQL (Al levantar el docker automaticamente se incluye la base de datos a crear) +``` +$ docker run -p 5434:5432 --name postgres --network aforo255-test -e POSTGRES_PASSWORD=postgres -e POSTGRES_DB=db_invoice -d postgres:12-alpine +``` + +2. Si se desea agregar nueva base de datos se debe ingresar al docker de Postgres y crear la base: +``` +$ docker exec -it postgres bash +$ psql -U postgres +$ CREATE TABLE db_invoice; +``` + +3. Conectarse a la base de datos desde cualquier cliente SQL (Ejemplo: Dbeaver) y ejecutar el script: +``` +CREATE TABLE IF NOT EXISTS invoice +( + id_invoice integer not null, + amount numeric, + state integer, + primary key (id_invoice) +); +``` + +## MySQL + +1. Levantar docker de MySQL (Al levantar el docker automaticamente se incluye la base de datos a crear) +``` +$ docker run -p 3307:3306 --name microservicio-mysql8 --network aforo255-test -e MYSQL_ROOT_PASSWORD=mysql -e MYSQL_DATABASE=db_operation -d mysql:8 +``` + +2. Conectarse a la base de datos desde cualquier cliente SQL (Ejemplo: Dbeaver) y ejecutar el script: +``` +CREATE TABLE pay +( + id_invoice integer not null, + amount numeric, + state integer, + primary key (id_invoice) +); +``` + +## MongoDB + +1. Levantar docker de MongoDB (Al levantar el microservicio y almacenar un registro se crea automaticamente la coleccion en mongo) +``` +$ docker run -p 27018:27017 --network aforo255-test --name mongodb -d mongo +``` \ No newline at end of file diff --git a/pay-app-spring-microservices/resources/INFO.md b/pay-app-spring-microservices/resources/INFO.md new file mode 100644 index 0000000..7bfc0c1 --- /dev/null +++ b/pay-app-spring-microservices/resources/INFO.md @@ -0,0 +1,63 @@ +# Endpoints + +A continuacion se detallan los endpoints y se adjuntan los Curl respectivos de cada microservicio para cumplir con las funcionalidad del trabajo + + +## Microservicios + +#### app-config + +Microservicio que se encarga de manejar las configuraciones de los microservicios, las configuraciones están en el siguiente directorio: + +[Configuraciones](https://github.com/icesi-ops/training_microservices.git) + +Endpoints para consultar configuraciones de los microservicios: + +* Curl consultar las configuraciones de los microservicios almacenadas en el repositorio +``` +curl --location --request GET 'http://localhost:8888/app-pay/dev' + +curl --location --request GET 'http://localhost:8888/app-invoice/dev' + +curl --location --request GET 'http://localhost:8888/app-transaction/dev' +``` + +#### app-pay + +Microservicio que se encarga de registrar los pagos de una factura + +[Dockerfile](https://github.com/icesi-ops/training_microservices.git) + +* Curl del servicio para registrar pagos +``` +curl --location --request POST 'http://localhost:8010/pay' \ +--header 'Content-Type: application/json' \ +--data-raw '{ + "idOperation": 1, + "idInvoice": 1, + "amount": 900, + "dateTime": "2021-05-21" +}' +``` + +#### app-inovice + +Microservicio que se encarga de obtener los mensajes de kafka y actualizar el estado de una factura segun el pago registrado + +[Dockerfile](https://github.com/icesi-ops/training_microservices.git) + +* Curl del servicio para obtener el detalle de todos los invoices +``` +curl --location --request GET 'http://localhost:8006/all' +``` + +#### app-transaction + +Microservicio que se encarga de obtener los mensajes de kafka y registrar las transacciones que se realizan con el pago de las facturas + +[Dockerfile](https://github.com/icesi-ops/training_microservices.git) + +* Curl del servicio para obtener el detalle de todos los invoices +``` +curl --location --request GET 'http://localhost:8082/all' +``` diff --git a/pay-app-spring-microservices/resources/microservicesarchitecture.png b/pay-app-spring-microservices/resources/microservicesarchitecture.png new file mode 100644 index 0000000..c10185b Binary files /dev/null and b/pay-app-spring-microservices/resources/microservicesarchitecture.png differ diff --git a/pay-app-spring-microservices/resources/mysql/Dockerfile b/pay-app-spring-microservices/resources/mysql/Dockerfile new file mode 100644 index 0000000..32bc267 --- /dev/null +++ b/pay-app-spring-microservices/resources/mysql/Dockerfile @@ -0,0 +1,6 @@ +FROM mysql:8 + +env MYSQL_ROOT_PASSWORD=MYSQL +env MYSQL_DATABASE=db_operation +COPY ./mysql.sql /docker-entrypoint-initdb.d + diff --git a/pay-app-spring-microservices/resources/mysql/mysql.sql b/pay-app-spring-microservices/resources/mysql/mysql.sql new file mode 100644 index 0000000..37a094a --- /dev/null +++ b/pay-app-spring-microservices/resources/mysql/mysql.sql @@ -0,0 +1,6 @@ +CREATE TABLE pay ( + id_invoice integer not null, + amount numeric, + state integer, + primary key (id_invoice) +); \ No newline at end of file diff --git a/pay-app-spring-microservices/resources/postgres/Dockerfile b/pay-app-spring-microservices/resources/postgres/Dockerfile new file mode 100644 index 0000000..450d83f --- /dev/null +++ b/pay-app-spring-microservices/resources/postgres/Dockerfile @@ -0,0 +1,6 @@ +FROM postgres:12-alpine + +env POSTGRES_PASSWORD=postgres +env POSTGRES_DB=db_invoice +COPY ./postgres.sql /docker-entrypoint-initdb.d + diff --git a/pay-app-spring-microservices/resources/postgres/data.sql b/pay-app-spring-microservices/resources/postgres/data.sql new file mode 100644 index 0000000..dac1339 --- /dev/null +++ b/pay-app-spring-microservices/resources/postgres/data.sql @@ -0,0 +1,5 @@ +INSERT INTO invoice(id_invoice, amount, state) VALUES(1, 1000, 0); +INSERT INTO invoice(id_invoice, amount, state) VALUES(2, 5000, 1); +INSERT INTO invoice(id_invoice, amount, state) VALUES(3, 300, 0); +INSERT INTO invoice(id_invoice, amount, state) VALUES(4, 600, 0); +INSERT INTO invoice(id_invoice, amount, state) VALUES(5, 400, 0); \ No newline at end of file diff --git a/pay-app-spring-microservices/resources/postgres/postgres.sql b/pay-app-spring-microservices/resources/postgres/postgres.sql new file mode 100644 index 0000000..3ffced7 --- /dev/null +++ b/pay-app-spring-microservices/resources/postgres/postgres.sql @@ -0,0 +1,5 @@ +CREATE TABLE IF NOT EXISTS invoice ( + id_invoice integer GENERATED by default as IDENTITY PRIMARY KEY, + amount numeric, + state integer +)