├── .gitignore ├── .travis.yml ├── Procfile ├── README.md ├── app-check.bat ├── pom.xml ├── src ├── main │ ├── java │ │ └── com │ │ │ └── kaluzny │ │ │ ├── Application.java │ │ │ ├── config │ │ │ ├── CORSFilter.java │ │ │ └── SecurityConfig.java │ │ │ ├── domain │ │ │ ├── User.java │ │ │ └── UserRepository.java │ │ │ ├── service │ │ │ ├── UserService.java │ │ │ ├── UserServiceBean.java │ │ │ └── exception │ │ │ │ └── UserAlreadyExistsException.java │ │ │ └── web │ │ │ └── UserController.java │ └── resources │ │ ├── application.yml │ │ ├── keystore.p12 │ │ └── static │ │ ├── css │ │ ├── app.css │ │ └── bootstrap.css │ │ ├── index.html │ │ └── js │ │ ├── app.js │ │ ├── controller │ │ └── user_controller.js │ │ ├── interceptor │ │ └── authInterceptor.js │ │ └── service │ │ └── user_service.js └── test │ └── java │ └── com │ └── kaluzny │ ├── repository │ └── UserRepositoryTest.java │ ├── service │ └── UserServiceTest.java │ └── web │ └── UserControllerTest.java └── travis-ci.bat /.gitignore: -------------------------------------------------------------------------------- 1 | # Maven 2 | target 3 | 4 | # Eclipse 5 | .project 6 | .settings 7 | .classpath 8 | 9 | # IntelliJ IDEA 10 | .idea 11 | *.iws 12 | *.iml 13 | *.ipr 14 | *.bat 15 | 16 | # Logging 17 | logs 18 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | 3 | jdk: 4 | - oraclejdk8 5 | 6 | sudo: 7 | - required 8 | 9 | after_success: 10 | - mvn clean test jacoco:report coveralls:report -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: java -Dserver.port=$PORT $JAVA_OPTS -jar target/springboot-rest-api-angularjs-https-1.0-SNAPSHOT.jar -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## REST HTTPS API with Spring Boot and Angular JS. 2 | 3 | [](https://travis-ci.org/OKaluzny/springboot-rest-api-angularjs-https) 4 | [](https://coveralls.io/github/OKaluzny/springboot-rest-api-angularjs-https?branch=master) 5 | [](https://codebeat.co/projects/github-com-okaluzny-springboot-rest-api-angularjs-https-master) 6 | 7 | ### Technology stack: 8 | 9 | * Maven; 10 | * FindBugs; 11 | * Travis CI; 12 | * Tomcat embedded; 13 | * Spring Boot; 14 | * JUnit; 15 | * Mockito; 16 | * Logback (as SLF4J facade); 17 | * Spring Web; 18 | * Spring Data JPA; 19 | * Hibernate (as JPA implementation); 20 | * MySQL Relation Database; 21 | * Spring Security (as basic authentication); 22 | * Angular JS, HTML, CSS. 23 | 24 | ### To run this application use: 25 | 26 | ```bash 27 | mvn spring-boot:run 28 | ``` 29 | 30 | ### This is what my REST API does: 31 | 32 | Go to `https://localhost:8443` to test and must specify a username: `user` and password: `user` 33 | 34 | * POST request to `/api/v1/objects/` with a "object" object as JSON creates a new "object"; 35 | * GET request to `/api/v1/objects/` returns a list of "objects"; 36 | * GET request to `/api/v1/objects/1` returns the "object" with ID 1; 37 | * PUT request to `/api/v1/objects/3` with a "object" object as JSON updates the "object" with ID 3; 38 | * DELETE request to `/api/v1/objects/4` deletes the "object" with ID 4; 39 | * DELETE request to `/api/v1/objects/` deletes all the "objects". 40 | 41 | ### The view in the Postman: 42 | 43 | [https://localhost:8443/api/v1/objects](https://localhost:8443/api/v1/objects) 44 | 45 | Message body: `{"title":"Absorber", "value":"123123"}` 46 | 47 |  48 | 49 | _**Open browser and browse at: 50 | [https://localhost:8443](https://localhost:8443)**_ 51 | 52 |  53 | 54 | _**To run the SonarQube use:**_ 55 | 56 | ```bash 57 | mvn clean install sonar:sonar 58 | ``` -------------------------------------------------------------------------------- /app-check.bat: -------------------------------------------------------------------------------- 1 | mvn clean install sonar:sonar -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.kaluzny 7 | springboot-rest-api-angularjs-https 8 | 1.0-SNAPSHOT 9 | jar 10 | 11 | springboot-rest-api-angularjs-https 12 | Test task project 13 | 14 | 15 | org.springframework.boot 16 | spring-boot-starter-parent 17 | 1.5.1.RELEASE 18 | 19 | 20 | 21 | 1.8 22 | UTF-8 23 | UTF-8 24 | 25 | 26 | 27 | 28 | 29 | org.springframework.boot 30 | spring-boot-starter-web 31 | 32 | 33 | org.springframework.boot 34 | spring-boot-starter 35 | 36 | 37 | org.springframework.boot 38 | spring-boot-starter-data-rest 39 | 40 | 41 | org.springframework.boot 42 | spring-boot-starter-hateoas 43 | 44 | 45 | com.fasterxml.jackson.core 46 | jackson-databind 47 | 48 | 49 | org.springframework.boot 50 | spring-boot-starter-data-jpa 51 | 52 | 53 | org.springframework.boot 54 | spring-boot-starter-test 55 | test 56 | 57 | 58 | 59 | org.springframework.boot 60 | spring-boot-starter-security 61 | 62 | 63 | 64 | mysql 65 | mysql-connector-java 66 | runtime 67 | 68 | 69 | 75 | 76 | 77 | org.hibernate 78 | hibernate-core 79 | 80 | 81 | org.hibernate 82 | hibernate-entitymanager 83 | 84 | 85 | 86 | javax.inject 87 | javax.inject 88 | 1 89 | 90 | 91 | 92 | org.webjars 93 | angularjs 94 | 2.0.0-alpha.22 95 | 96 | 97 | 98 | org.webjars 99 | bootstrap 100 | 4.0.0-alpha.5 101 | runtime 102 | 103 | 104 | com.h2database 105 | h2 106 | 107 | 108 | junit 109 | junit 110 | 111 | 112 | com.jayway.jsonpath 113 | json-path 114 | 115 | 116 | org.springframework.security 117 | spring-security-test 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | org.springframework.boot 127 | spring-boot-maven-plugin 128 | 129 | 130 | 131 | org.codehaus.mojo 132 | findbugs-maven-plugin 133 | 3.0.4 134 | 135 | 136 | verify 137 | 138 | check 139 | 140 | 141 | 142 | 143 | 144 | 145 | org.eluder.coveralls 146 | coveralls-maven-plugin 147 | 4.3.0 148 | 149 | DMEO7olDZpWmSQ4j89hImq8QM0qwKN2Mu 150 | 151 | 152 | 153 | 154 | org.jacoco 155 | jacoco-maven-plugin 156 | 0.7.6.201602180812 157 | 158 | 159 | prepare-agent 160 | 161 | prepare-agent 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | -------------------------------------------------------------------------------- /src/main/java/com/kaluzny/Application.java: -------------------------------------------------------------------------------- 1 | package com.kaluzny; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | import org.springframework.boot.SpringApplication; 6 | import org.springframework.boot.autoconfigure.SpringBootApplication; 7 | import org.springframework.context.annotation.Bean; 8 | import org.springframework.context.annotation.ComponentScan; 9 | import org.springframework.web.servlet.config.annotation.CorsRegistry; 10 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; 11 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; 12 | 13 | @SpringBootApplication 14 | @ComponentScan("com.kaluzny") 15 | public class Application { 16 | 17 | private static final Logger LOGGER = LoggerFactory.getLogger(Application.class); 18 | 19 | public static void main(String[] args) { 20 | LOGGER.info(">>> Entered application..."); 21 | SpringApplication.run(Application.class, args); 22 | } 23 | 24 | @Bean 25 | public WebMvcConfigurer CORSConfig() { 26 | return new WebMvcConfigurerAdapter() { 27 | @Override 28 | public void addCorsMappings(CorsRegistry registry) { 29 | registry.addMapping("/api/v1/**"); 30 | } 31 | }; 32 | } 33 | } -------------------------------------------------------------------------------- /src/main/java/com/kaluzny/config/CORSFilter.java: -------------------------------------------------------------------------------- 1 | package com.kaluzny.config; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | import org.springframework.stereotype.Component; 6 | 7 | import javax.servlet.*; 8 | import javax.servlet.http.HttpServletResponse; 9 | import java.io.IOException; 10 | 11 | @Component 12 | public class CORSFilter implements Filter { 13 | 14 | private static final Logger LOGGER = LoggerFactory.getLogger(CORSFilter.class); 15 | 16 | @Override 17 | public void init(FilterConfig filterConfig) throws ServletException { 18 | } 19 | 20 | @Override 21 | public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) 22 | throws IOException, ServletException { 23 | 24 | LOGGER.debug("Adding CORS Headers....................."); 25 | 26 | HttpServletResponse response = (HttpServletResponse) res; 27 | response.setHeader("Access-Control-Allow-Origin", "*"); 28 | response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS"); 29 | response.setHeader("Access-Control-Max-Age", "3600"); 30 | response.setHeader("Access-Control-Allow-Headers", "X-PINGOTHER,Content-Type,X-Requested-With,accept," + 31 | "Origin,Access-Control-Request-Method,Access-Control-Request-Headers,Authorization"); 32 | response.setHeader("Access-Control-Expose-Headers", "xsrf-token"); 33 | chain.doFilter(req, res); 34 | } 35 | 36 | @Override 37 | public void destroy() { 38 | } 39 | } -------------------------------------------------------------------------------- /src/main/java/com/kaluzny/config/SecurityConfig.java: -------------------------------------------------------------------------------- 1 | package com.kaluzny.config; 2 | 3 | import org.springframework.context.annotation.Configuration; 4 | import org.springframework.http.HttpMethod; 5 | import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; 6 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 7 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; 8 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; 9 | import org.springframework.security.web.access.channel.ChannelProcessingFilter; 10 | 11 | @Configuration 12 | @EnableWebSecurity 13 | public class SecurityConfig extends WebSecurityConfigurerAdapter { 14 | 15 | @Override 16 | protected void configure(AuthenticationManagerBuilder auth) throws Exception { 17 | auth.inMemoryAuthentication() 18 | .withUser("user") 19 | .password("user") 20 | .roles("USER"); 21 | } 22 | 23 | @Override 24 | protected void configure(HttpSecurity http) throws Exception { 25 | http 26 | .addFilterBefore(new CORSFilter(), ChannelProcessingFilter.class) 27 | .csrf().disable() 28 | .authorizeRequests() 29 | .antMatchers(HttpMethod.OPTIONS, "/**").permitAll()//allow CORS option calls 30 | .anyRequest().authenticated() 31 | .and() 32 | .logout() 33 | .permitAll() 34 | .and() 35 | .httpBasic(); 36 | http.httpBasic(); 37 | http.csrf().disable(); 38 | } 39 | } -------------------------------------------------------------------------------- /src/main/java/com/kaluzny/domain/User.java: -------------------------------------------------------------------------------- 1 | package com.kaluzny.domain; 2 | 3 | import javax.persistence.*; 4 | 5 | @Entity 6 | @Table(name = "user_title") 7 | public class User { 8 | 9 | @Id 10 | @GeneratedValue(strategy = GenerationType.AUTO) 11 | @Column(name = "ID", nullable = false, updatable = false) 12 | private long id; 13 | 14 | @Column(name = "TITLE", nullable = false) 15 | private String title; 16 | 17 | @Column(name = "VALUE", nullable = false) 18 | private long value; 19 | 20 | public User() { 21 | } 22 | 23 | public User(long id) { 24 | this.id = id; 25 | } 26 | 27 | public User(long id, String title, long value) { 28 | this.id = id; 29 | this.title = title; 30 | this.value = value; 31 | } 32 | 33 | public long getId() { 34 | return id; 35 | } 36 | 37 | public String getTitle() { 38 | return title; 39 | } 40 | 41 | public void setTitle(String title) { 42 | this.title = title; 43 | } 44 | 45 | public long getValue() { 46 | return value; 47 | } 48 | 49 | public void setValue(long value) { 50 | this.value = value; 51 | } 52 | 53 | @Override 54 | public String toString() { 55 | return "User{" + 56 | "id=" + id + 57 | ", title='" + title + '\'' + 58 | ", value=" + value + 59 | '}'; 60 | } 61 | } -------------------------------------------------------------------------------- /src/main/java/com/kaluzny/domain/UserRepository.java: -------------------------------------------------------------------------------- 1 | package com.kaluzny.domain; 2 | 3 | import org.springframework.data.jpa.repository.JpaRepository; 4 | import org.springframework.data.rest.core.annotation.RepositoryRestResource; 5 | 6 | import java.util.List; 7 | 8 | @RepositoryRestResource 9 | public interface UserRepository extends JpaRepository { 10 | List findByTitle(String title); 11 | } -------------------------------------------------------------------------------- /src/main/java/com/kaluzny/service/UserService.java: -------------------------------------------------------------------------------- 1 | package com.kaluzny.service; 2 | 3 | import com.kaluzny.domain.User; 4 | 5 | import java.util.List; 6 | 7 | public interface UserService { 8 | 9 | boolean isUserExist(User user); 10 | 11 | User saveUser(User user); 12 | 13 | User findUserById(long id); 14 | 15 | List findAllUsers(); 16 | 17 | User updateUser(User user); 18 | 19 | void deleteUser(Long id); 20 | 21 | void deleteAllUsers(); 22 | } -------------------------------------------------------------------------------- /src/main/java/com/kaluzny/service/UserServiceBean.java: -------------------------------------------------------------------------------- 1 | package com.kaluzny.service; 2 | 3 | import com.kaluzny.domain.User; 4 | import com.kaluzny.domain.UserRepository; 5 | import com.kaluzny.service.exception.UserAlreadyExistsException; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | import org.springframework.stereotype.Service; 9 | import org.springframework.transaction.annotation.Transactional; 10 | import org.springframework.validation.annotation.Validated; 11 | 12 | import javax.inject.Inject; 13 | import javax.persistence.EntityManager; 14 | import javax.persistence.PersistenceContext; 15 | import java.util.List; 16 | 17 | @Service 18 | @Validated 19 | @Transactional 20 | public class UserServiceBean implements UserService { 21 | 22 | private static final Logger LOGGER = LoggerFactory.getLogger(UserServiceBean.class); 23 | 24 | private UserRepository repository; 25 | 26 | @PersistenceContext 27 | private EntityManager entityManager; 28 | 29 | @Inject 30 | public UserServiceBean(UserRepository repository) { 31 | this.repository = repository; 32 | } 33 | 34 | @Override 35 | public boolean isUserExist(User user) { 36 | return findUserById(user.getId()) != null; 37 | } 38 | 39 | @Override 40 | public User saveUser(User user) { 41 | LOGGER.debug("Save {}", user); 42 | User existing = repository.findOne(user.getId()); 43 | if (existing != null) { 44 | throw new UserAlreadyExistsException( 45 | String.format("There already exists a user with id = %s", user.getId())); 46 | } 47 | return repository.save(user); 48 | } 49 | 50 | @Override 51 | public User findUserById(long id) { 52 | LOGGER.debug("Search user by id: " + id); 53 | return repository.findOne(id); 54 | } 55 | 56 | @Override 57 | public List findAllUsers() { 58 | LOGGER.debug("Retrieve the list of all users!"); 59 | return repository.findAll(); 60 | } 61 | 62 | @Override 63 | public User updateUser(User user) { 64 | LOGGER.debug("User with id: " + user.getId() + " updated! "); 65 | if (!entityManager.contains(user)) 66 | user = entityManager.merge(user); 67 | return user; 68 | } 69 | 70 | @Override 71 | public void deleteUser(Long id) { 72 | LOGGER.debug("User by id: " + id + " Deleted!"); 73 | repository.delete(id); 74 | } 75 | 76 | @Override 77 | public void deleteAllUsers() { 78 | LOGGER.debug("The list all users deleted!"); 79 | repository.deleteAll(); 80 | } 81 | } -------------------------------------------------------------------------------- /src/main/java/com/kaluzny/service/exception/UserAlreadyExistsException.java: -------------------------------------------------------------------------------- 1 | package com.kaluzny.service.exception; 2 | 3 | public class UserAlreadyExistsException extends RuntimeException { 4 | 5 | public UserAlreadyExistsException(final String message) { 6 | super(message); 7 | } 8 | } -------------------------------------------------------------------------------- /src/main/java/com/kaluzny/web/UserController.java: -------------------------------------------------------------------------------- 1 | package com.kaluzny.web; 2 | 3 | import com.kaluzny.domain.User; 4 | import com.kaluzny.service.UserService; 5 | import com.kaluzny.service.exception.UserAlreadyExistsException; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | import org.springframework.http.HttpHeaders; 9 | import org.springframework.http.HttpStatus; 10 | import org.springframework.http.MediaType; 11 | import org.springframework.http.ResponseEntity; 12 | import org.springframework.stereotype.Service; 13 | import org.springframework.web.bind.annotation.*; 14 | import org.springframework.web.util.UriComponentsBuilder; 15 | 16 | import javax.inject.Inject; 17 | import java.util.List; 18 | 19 | @Service 20 | @RestController("user") 21 | @RequestMapping("/api/v1/") 22 | public class UserController { 23 | 24 | private static final Logger LOGGER = LoggerFactory.getLogger(UserController.class); 25 | 26 | private UserService userService; 27 | 28 | @Inject 29 | public UserController(UserService userService) { 30 | this.userService = userService; 31 | } 32 | 33 | /* Create a user */ 34 | @RequestMapping( 35 | value = "objects", 36 | method = RequestMethod.POST) 37 | public ResponseEntity createUser(@RequestBody User user, UriComponentsBuilder ucBuilder) { 38 | LOGGER.debug(">>> Creating user with id: " + user.getId()); 39 | if (userService.isUserExist(user)) { 40 | LOGGER.debug("A user with name " + user.getId() + "exist."); 41 | return new ResponseEntity<>(HttpStatus.CONFLICT); 42 | } 43 | userService.saveUser(user); 44 | HttpHeaders headers = new HttpHeaders(); 45 | headers.setLocation(ucBuilder.path("user/{id}").buildAndExpand(user.getId()).toUri()); 46 | return new ResponseEntity<>(user, headers, HttpStatus.CREATED); 47 | } 48 | 49 | /* Reading single user */ 50 | @RequestMapping( 51 | value = "objects/{id}", 52 | method = RequestMethod.GET, 53 | produces = MediaType.APPLICATION_JSON_VALUE) 54 | public ResponseEntity getUser(@PathVariable("id") Long id) { 55 | LOGGER.debug("Fetching user with id: " + id); 56 | User user = userService.findUserById(id); 57 | if (user == null) { 58 | LOGGER.debug("User with id: " + id + ", not found!"); 59 | return new ResponseEntity<>(HttpStatus.NOT_FOUND); 60 | } 61 | return new ResponseEntity<>(user, HttpStatus.OK); 62 | } 63 | 64 | /* Reads all users */ 65 | @RequestMapping( 66 | value = "objects", 67 | method = RequestMethod.GET, 68 | produces = MediaType.APPLICATION_JSON_VALUE) 69 | public ResponseEntity> listAllUsers() { 70 | LOGGER.debug("Received request to list all users"); 71 | List users = userService.findAllUsers(); 72 | if (users.isEmpty()) { 73 | LOGGER.debug("Users do not have."); 74 | return new ResponseEntity<>(HttpStatus.NO_CONTENT); 75 | } 76 | return new ResponseEntity<>(users, HttpStatus.OK); 77 | } 78 | 79 | /* Update a user */ 80 | @RequestMapping( 81 | value = "objects/{id}", 82 | method = RequestMethod.PUT) 83 | public ResponseEntity updateUserFromDB(@PathVariable("id") long id, 84 | @RequestBody User user) { 85 | LOGGER.debug(">>> Updating user with id: " + id); 86 | User currentUser = userService.findUserById(id); 87 | 88 | if (currentUser == null) { 89 | LOGGER.debug("<<< User with id: " + id + ", not found!"); 90 | return new ResponseEntity<>(HttpStatus.NOT_FOUND); 91 | } 92 | 93 | currentUser.setTitle(user.getTitle()); 94 | currentUser.setValue(user.getValue()); 95 | 96 | userService.updateUser(currentUser); 97 | return new ResponseEntity<>(currentUser, HttpStatus.OK); 98 | } 99 | 100 | /* Delete a user */ 101 | @RequestMapping( 102 | value = "objects/{id}", 103 | method = RequestMethod.DELETE) 104 | public ResponseEntity deleteUserFromDB(@PathVariable("id") long id) { 105 | LOGGER.debug("Fetching & Deleting User with id: " + id + " is successfully removed from database!"); 106 | 107 | User user = userService.findUserById(id); 108 | if (user == null) { 109 | LOGGER.debug("Unable to delete. User with id: " + id + ", not found!"); 110 | return new ResponseEntity<>(HttpStatus.NOT_FOUND); 111 | } 112 | 113 | userService.deleteUser(id); 114 | return new ResponseEntity<>(HttpStatus.NO_CONTENT); 115 | } 116 | 117 | /* Delete all users */ 118 | @RequestMapping( 119 | value = "objects", 120 | method = RequestMethod.DELETE) 121 | public ResponseEntity deleteAllUsers() { 122 | userService.deleteAllUsers(); 123 | LOGGER.debug("Removed all users from database!"); 124 | return new ResponseEntity<>(HttpStatus.NO_CONTENT); 125 | } 126 | 127 | @ExceptionHandler 128 | @ResponseStatus(HttpStatus.CONFLICT) 129 | public String handleUserAlreadyExistsException(UserAlreadyExistsException exception) { 130 | return exception.getMessage(); 131 | } 132 | } -------------------------------------------------------------------------------- /src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | # Spring Boot configuration 2 | spring: 3 | profiles: 4 | active: production 5 | # Database 6 | datasource: 7 | driver-class-name: com.mysql.jdbc.Driver 8 | url: jdbc:mysql://localhost:3306/userdb?createDatabaseIfNotExist=true 9 | username: user 10 | password: user 11 | # JPA properties 12 | jpa: 13 | hibernate: 14 | # Hibernate ddl auto (create, create-drop, update) 15 | ddl-auto: update 16 | # Show or not log for each sql query 17 | show-sql: true 18 | database: mysql 19 | # The SQL dialect makes Hibernate generate better SQL for the chosen database 20 | database-platform: org.hibernate.dialect.MySQL5InnoDBDialect 21 | # Logging 22 | logging: 23 | file: logs/dev_app.log 24 | pattern: 25 | console: "[%-5level] %date{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %logger{15}#%line %msg\n" 26 | file: "%d %-5level [%thread] %logger : %msg%n" 27 | level: 28 | com.kaluzny: DEBUG 29 | org.springframework: INFO 30 | org.hibernate: INFO 31 | # Configure the server to run with SSL/TLS and using HTTPS 32 | server: 33 | tomcat: 34 | basedir: my-tomcat 35 | accesslog: 36 | enabled: true 37 | #pattern: "%t %a %r %s (%D ms)" 38 | port: 8443 39 | ssl: 40 | key-store: classpath:keystore.p12 41 | key-store-password: tomcatadmin 42 | keyStoreType: PKCS12 43 | keyAlias: tomcat 44 | -------------------------------------------------------------------------------- /src/main/resources/keystore.p12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OKaluzny/springboot-rest-api-angularjs-https/d057f4bad81a8a888a7f057dc77e53c0dec111e0/src/main/resources/keystore.p12 -------------------------------------------------------------------------------- /src/main/resources/static/css/app.css: -------------------------------------------------------------------------------- 1 | body, #mainWrapper { 2 | height: 70%; 3 | background-color: rgb(245, 245, 245); 4 | } 5 | 6 | body, .form-control { 7 | font-size: 12px !important; 8 | } 9 | 10 | .floatRight { 11 | float: right; 12 | margin-right: 18px; 13 | } 14 | 15 | .has-error { 16 | color: red; 17 | } 18 | 19 | .formcontainer { 20 | background-color: rgba(234, 231, 231, 0.82); 21 | padding: 30px; 22 | } 23 | 24 | .tablecontainer { 25 | padding-left: 20px; 26 | } 27 | 28 | .generic-container { 29 | width: 80%; 30 | margin-left: 20px; 31 | margin-top: 20px; 32 | margin-bottom: 20px; 33 | padding: 20px; 34 | background-color: #EAE7E7; 35 | border: 1px solid #ddd; 36 | border-radius: 4px; 37 | box-shadow: 0 0 30px black; 38 | } 39 | 40 | .custom-width { 41 | width: 80px !important; 42 | } 43 | -------------------------------------------------------------------------------- /src/main/resources/static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | AngularJS 6 | 7 | 8 | 9 | 10 | 11 | 13 | 14 | Object Registration Form 15 | 16 | 17 | 18 | 19 | 20 | 21 | Title 22 | 23 | 27 | 28 | 29 | 30 | 31 | Value 32 | 33 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 44 | Reset Form 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | List of Objects 56 | 57 | 58 | 59 | 60 | Id 61 | Title 62 | Value 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | Update 73 | 74 | Delete 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /src/main/resources/static/js/app.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var App = angular.module('myApp',[]); 4 | 5 | App.config(['$httpProvider', function ($httpProvider) { 6 | $httpProvider.interceptors.push('AuthInterceptor'); 7 | }]); 8 | -------------------------------------------------------------------------------- /src/main/resources/static/js/controller/user_controller.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('myApp').controller('UserController', ['$scope', 'UserService', function ($scope, UserService) { 4 | var self = this; 5 | self.user = {id: null, title: '', value: ''}; 6 | self.users = []; 7 | 8 | self.submit = submit; 9 | self.edit = edit; 10 | self.remove = remove; 11 | self.reset = reset; 12 | 13 | fetchAllUsers(); 14 | 15 | function fetchAllUsers() { 16 | UserService.fetchAllUsers() 17 | .then( 18 | function (d) { 19 | self.users = d; 20 | }, 21 | function (errResponse) { 22 | console.error('Error while fetching Users'); 23 | } 24 | ); 25 | } 26 | 27 | function createUser(user) { 28 | UserService.createUser(user) 29 | .then( 30 | fetchAllUsers, 31 | function (errResponse) { 32 | console.error('Error while creating User'); 33 | } 34 | ); 35 | } 36 | 37 | function updateUser(user, id) { 38 | UserService.updateUser(user, id) 39 | .then( 40 | fetchAllUsers, 41 | function (errResponse) { 42 | console.error('Error while updating User'); 43 | } 44 | ); 45 | } 46 | 47 | function deleteUser(id) { 48 | UserService.deleteUser(id) 49 | .then( 50 | fetchAllUsers, 51 | function (errResponse) { 52 | console.error('Error while deleting User'); 53 | } 54 | ); 55 | } 56 | 57 | function submit() { 58 | if (self.user.id === null) { 59 | console.log('Saving New User', self.user); 60 | createUser(self.user); 61 | } else { 62 | updateUser(self.user, self.user.id); 63 | console.log('User updated with id ', self.user.id); 64 | } 65 | reset(); 66 | } 67 | 68 | function edit(id) { 69 | console.log('id to be edited', id); 70 | for (var i = 0; i < self.users.length; i++) { 71 | if (self.users[i].id === id) { 72 | self.user = angular.copy(self.users[i]); 73 | break; 74 | } 75 | } 76 | } 77 | 78 | function remove(id) { 79 | console.log('id to be deleted', id); 80 | if (self.user.id === id) {//clean form if the user to be deleted is shown there. 81 | reset(); 82 | } 83 | deleteUser(id); 84 | } 85 | 86 | function reset() { 87 | self.user = {id: null, title: '', value: ''}; 88 | $scope.myForm.$setPristine(); //reset Form 89 | } 90 | 91 | }]); -------------------------------------------------------------------------------- /src/main/resources/static/js/interceptor/authInterceptor.js: -------------------------------------------------------------------------------- 1 | angular.module('myApp') 2 | .factory('AuthInterceptor', [function () { 3 | return { 4 | // Sent the token (if present) with each request 5 | 'request': function (config) { 6 | config.headers = config.headers || {}; 7 | var encodedString = btoa("user:user"); 8 | config.headers.Authorization = 'Basic ' + encodedString; 9 | return config; 10 | } 11 | }; 12 | }]); 13 | 14 | -------------------------------------------------------------------------------- /src/main/resources/static/js/service/user_service.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('myApp').factory('UserService', ['$http', '$q', function ($http, $q) { 4 | 5 | var REST_SERVICE_URI = 'https://localhost:8443/api/v1/objects/'; 6 | 7 | var factory = { 8 | fetchAllUsers: fetchAllUsers, 9 | createUser: createUser, 10 | updateUser: updateUser, 11 | deleteUser: deleteUser 12 | }; 13 | 14 | return factory; 15 | 16 | function fetchAllUsers() { 17 | var deferred = $q.defer(); 18 | $http.get(REST_SERVICE_URI) 19 | .then( 20 | function (response) { 21 | deferred.resolve(response.data); 22 | }, 23 | function (errResponse) { 24 | console.error('Error while fetching Users'); 25 | deferred.reject(errResponse); 26 | } 27 | ); 28 | return deferred.promise; 29 | } 30 | 31 | function createUser(user) { 32 | var deferred = $q.defer(); 33 | $http.post(REST_SERVICE_URI, user) 34 | .then( 35 | function (response) { 36 | deferred.resolve(response.data); 37 | }, 38 | function (errResponse) { 39 | console.error('Error while creating User'); 40 | deferred.reject(errResponse); 41 | } 42 | ); 43 | return deferred.promise; 44 | } 45 | 46 | function updateUser(user, id) { 47 | var deferred = $q.defer(); 48 | $http.put(REST_SERVICE_URI + id, user) 49 | .then( 50 | function (response) { 51 | deferred.resolve(response.data); 52 | }, 53 | function (errResponse) { 54 | console.error('Error while updating User'); 55 | deferred.reject(errResponse); 56 | } 57 | ); 58 | return deferred.promise; 59 | } 60 | 61 | function deleteUser(id) { 62 | var deferred = $q.defer(); 63 | $http.delete(REST_SERVICE_URI + id) 64 | .then( 65 | function (response) { 66 | deferred.resolve(response.data); 67 | }, 68 | function (errResponse) { 69 | console.error('Error while deleting User'); 70 | deferred.reject(errResponse); 71 | } 72 | ); 73 | return deferred.promise; 74 | } 75 | 76 | }]); -------------------------------------------------------------------------------- /src/test/java/com/kaluzny/repository/UserRepositoryTest.java: -------------------------------------------------------------------------------- 1 | package com.kaluzny.repository; 2 | 3 | import com.kaluzny.domain.User; 4 | import com.kaluzny.domain.UserRepository; 5 | import org.junit.Test; 6 | import org.junit.runner.RunWith; 7 | import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; 8 | import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager; 9 | import org.springframework.test.context.junit4.SpringRunner; 10 | 11 | import javax.inject.Inject; 12 | import java.util.List; 13 | 14 | import static org.junit.Assert.assertEquals; 15 | 16 | @RunWith(SpringRunner.class) 17 | @DataJpaTest 18 | public class UserRepositoryTest { 19 | 20 | @Inject 21 | private UserRepository repository; 22 | @Inject 23 | private TestEntityManager entityManager; 24 | 25 | @Test 26 | public void findByTitle_CorrectString_Success() { 27 | entityManager.merge(new User(1, "test title", 20)); 28 | List list = this.repository.findByTitle("test title"); 29 | list.stream().forEach(x -> System.out.println(x.getTitle())); 30 | assertEquals(list.size(), 1); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/test/java/com/kaluzny/service/UserServiceTest.java: -------------------------------------------------------------------------------- 1 | package com.kaluzny.service; 2 | 3 | import com.kaluzny.domain.User; 4 | import com.kaluzny.domain.UserRepository; 5 | import org.assertj.core.util.Arrays; 6 | import org.junit.Test; 7 | import org.junit.runner.RunWith; 8 | import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; 9 | import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager; 10 | import org.springframework.test.context.junit4.SpringRunner; 11 | 12 | import javax.inject.Inject; 13 | import java.util.List; 14 | 15 | import static org.junit.Assert.*; 16 | 17 | @RunWith(SpringRunner.class) 18 | @DataJpaTest 19 | public class UserServiceTest { 20 | 21 | @Inject 22 | private UserService userService; 23 | @Inject 24 | private UserRepository userRepository; 25 | @Inject 26 | private TestEntityManager testEntityManager; 27 | 28 | @Test 29 | public void contains_NotValidId_Success() { 30 | testEntityManager.merge(new User(1, "test title 1", 1)); 31 | testEntityManager.merge(new User(2, "test title 2", 2)); 32 | User isContains = userService.findUserById(3); 33 | assertEquals(null, isContains); 34 | } 35 | 36 | @Test 37 | public void contains_ValidId_Success() { 38 | User object = new User(1, "test title 1", 1); 39 | testEntityManager.merge(object); 40 | boolean isContains = userService.isUserExist(object); 41 | assertEquals(false, isContains); 42 | } 43 | 44 | @Test 45 | public void findAll_Success() { 46 | testEntityManager.merge(new User(1, "test title 1", 1)); 47 | testEntityManager.merge(new User(2, "test title 2", 2)); 48 | List list = userService.findAllUsers(); 49 | List repositoryList = userRepository.findAll(); 50 | assertArrayEquals(Arrays.array(repositoryList), Arrays.array(list)); 51 | } 52 | 53 | @Test 54 | public void findAll_SuccessEmptyList() { 55 | List list = userService.findAllUsers(); 56 | assertEquals(true, list.isEmpty()); 57 | } 58 | 59 | @Test 60 | public void save_IdIsEqualsZero_Success() { 61 | User object = new User(0, "test title save", 1); 62 | userService.saveUser(object); 63 | Object lastIndex = testEntityManager.getId(object); 64 | User repositoryObject = testEntityManager.find(User.class, lastIndex); 65 | assertEquals("test title save", repositoryObject.getTitle()); 66 | assertNotEquals(0, repositoryObject.getId()); 67 | } 68 | 69 | @Test 70 | public void save_IdIsNotEqualsZero_Success() { 71 | User object = new User(2, "test title update", 1); 72 | testEntityManager.merge(object); 73 | int objectId = (int) object.getId(); 74 | userService.saveUser(object); 75 | Object lastIndex = testEntityManager.getId(object); 76 | User repositoryObject = testEntityManager.find(User.class, lastIndex); 77 | assertEquals("test title update", repositoryObject.getTitle()); 78 | assertNotEquals(0, repositoryObject.getId()); 79 | } 80 | 81 | @Test 82 | public void findOne_NotValidId_Success() { 83 | User object = new User(1, "test title findOne", 5); 84 | testEntityManager.merge(object); 85 | int objectId = 100; 86 | User repositoryObject = userService.findUserById(objectId); 87 | assertNull(repositoryObject); 88 | assertNotEquals(object, repositoryObject); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/test/java/com/kaluzny/web/UserControllerTest.java: -------------------------------------------------------------------------------- 1 | package com.kaluzny.web; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import com.kaluzny.domain.User; 5 | import com.kaluzny.service.UserService; 6 | import org.hamcrest.Matchers; 7 | import org.junit.Before; 8 | import org.junit.Test; 9 | import org.junit.runner.RunWith; 10 | import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; 11 | import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; 12 | import org.springframework.boot.test.mock.mockito.MockBean; 13 | import org.springframework.http.MediaType; 14 | import org.springframework.test.context.junit4.SpringRunner; 15 | import org.springframework.test.web.servlet.MockMvc; 16 | import org.springframework.test.web.servlet.setup.MockMvcBuilders; 17 | import org.springframework.web.context.WebApplicationContext; 18 | 19 | import javax.inject.Inject; 20 | import java.util.Arrays; 21 | 22 | import static org.mockito.BDDMockito.given; 23 | import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user; 24 | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; 25 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; 26 | 27 | @RunWith(SpringRunner.class) 28 | @AutoConfigureMockMvc 29 | @WebMvcTest(UserController.class) 30 | public class UserControllerTest { 31 | 32 | private static final ObjectMapper MAPPER = new ObjectMapper(); 33 | @Inject 34 | private MockMvc mockMvc; 35 | @Inject 36 | private WebApplicationContext context; 37 | @MockBean 38 | private UserService service; 39 | 40 | @Before 41 | public void setup() { 42 | mockMvc = MockMvcBuilders.webAppContextSetup(context) 43 | .defaultRequest(get("/").with(user("user").password("user").roles("USER"))).build(); 44 | } 45 | 46 | @Test 47 | public void findObjects_StorageIsNotEmpty_OneObjectIsReturned() throws Exception { 48 | given(service.findAllUsers()).willReturn(Arrays.asList(new User())); 49 | mockMvc 50 | .perform(get("/api/v1/objects")) 51 | .andExpect(status().isOk()) 52 | .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)) 53 | .andExpect(jsonPath("$", Matchers.hasSize(1))); 54 | } 55 | 56 | @Test 57 | public void saveUser_validUser_UserIsReturned() throws Exception { 58 | User user = new User(); 59 | user.setValue(100); 60 | user.setTitle("Java 8"); 61 | mockMvc 62 | .perform(post("/api/v1/objects") 63 | .contentType(MediaType.APPLICATION_JSON_UTF8_VALUE) 64 | .content(MAPPER.writeValueAsString(user))) 65 | .andExpect(status().isCreated()) 66 | .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)) 67 | .andExpect(jsonPath("$.title", Matchers.equalTo("Java 8"))); 68 | 69 | } 70 | 71 | @Test 72 | public void saveUser_NotValidUser_BadRequest() throws Exception { 73 | User user = new User(); 74 | user.setValue(15); 75 | mockMvc 76 | .perform(post("/api/v1/objects") 77 | .contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)) 78 | //.content(MAPPER.writeValueAsString(user))) 79 | .andExpect(status().isBadRequest()); 80 | } 81 | 82 | @Test 83 | public void updateUser_validUser_UserIsReturned() throws Exception { 84 | User user = new User(2, "Java 8", 20); 85 | mockMvc 86 | .perform(post("/api/v1/objects") 87 | .contentType(MediaType.APPLICATION_JSON_UTF8_VALUE) 88 | .content(MAPPER.writeValueAsString(user))); 89 | 90 | User user2 = new User(9, "Java 9", 22); 91 | given(service.findUserById(2)).willReturn(user2); 92 | mockMvc 93 | .perform(put("/api/v1/objects/2").contentType(MediaType.APPLICATION_JSON_UTF8_VALUE) 94 | .content(MAPPER.writeValueAsString(user2))).andExpect(status().isOk()) 95 | .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)) 96 | .andExpect(jsonPath("$.title", Matchers.equalTo("Java 9"))); 97 | 98 | } 99 | 100 | @Test 101 | public void findUserByID_ValidId_OneObjectIsReturned() throws Exception { 102 | User user = new User(2, "Condenser", 100); 103 | given(service.findUserById(2)).willReturn(user); 104 | given(service.isUserExist(user)).willReturn(true); 105 | mockMvc 106 | .perform(get("/api/v1/objects/2")) 107 | .andExpect(status().isOk()) 108 | .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)) 109 | .andExpect(jsonPath("$.id", Matchers.equalTo(2))); 110 | } 111 | 112 | @Test 113 | public void findUserByID_NotValidId_NotFound() throws Exception { 114 | given(service.findUserById(1)).willReturn(null); 115 | mockMvc 116 | .perform(get("/api/v1/objects/1")) 117 | .andExpect(status().isNotFound()); 118 | } 119 | 120 | @Test 121 | public void deleteObjectById_NotValidId_NotFound() throws Exception { 122 | User user = new User(10, "Java 9", 20); 123 | given(service.isUserExist(user)).willReturn(false); 124 | mockMvc 125 | .perform(delete("/api/v1/objects/10")) 126 | .andExpect(status().isNotFound()); 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /travis-ci.bat: -------------------------------------------------------------------------------- 1 | mvn install -DskipTests=true -Dmaven.javadoc.skip=true -B -V 2 | --------------------------------------------------------------------------------