├── src
├── main
│ ├── resources
│ │ ├── application.properties
│ │ ├── jmeter
│ │ │ ├── auth.txt
│ │ │ └── HTTP Request.jmx
│ │ └── templates
│ │ │ └── home.html
│ └── java
│ │ └── com
│ │ └── example
│ │ └── springpipelinedemo
│ │ ├── SpringPipelineDemoApplication.java
│ │ ├── repository
│ │ └── UserRepository.java
│ │ ├── dto
│ │ ├── mapper
│ │ │ ├── UserMapper.java
│ │ │ └── Mapper.java
│ │ └── UserDto.java
│ │ ├── config
│ │ ├── UtilityConfig.java
│ │ ├── JpaConfig.java
│ │ └── WebSecurityConfig.java
│ │ ├── controller
│ │ ├── AdminController.java
│ │ ├── forms
│ │ │ └── SignupForm.java
│ │ └── UserController.java
│ │ ├── tasks
│ │ └── InitializeDefaultUser.java
│ │ ├── service
│ │ └── UserService.java
│ │ └── model
│ │ └── User.java
├── test
│ └── java
│ │ └── com
│ │ └── example
│ │ └── springpipelinedemo
│ │ ├── SpringPipelineDemoApplicationTests.java
│ │ └── service
│ │ └── UserServiceTest.java
└── it
│ └── java
│ └── com
│ └── example
│ └── springpipelinedemo
│ ├── repository
│ └── UserRepositoryTest.java
│ └── controller
│ ├── UserControllerTest.java
│ └── AdminControllerTest.java
├── assets
└── pipeline-result.png
├── docker-hub-secret.yml
├── Dockerfile
├── LICENSE
├── .semaphore
├── docker-build.yml
└── semaphore.yml
├── pom.xml
├── README.md
└── .gitignore
/src/main/resources/application.properties:
--------------------------------------------------------------------------------
1 | defaultUser=test@email.com
2 | defaultPassword=testPass123
--------------------------------------------------------------------------------
/assets/pipeline-result.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/semaphoreci-demos/semaphore-demo-java-spring/HEAD/assets/pipeline-result.png
--------------------------------------------------------------------------------
/src/main/resources/jmeter/auth.txt:
--------------------------------------------------------------------------------
1 | # JMeter generated Authorization file
2 | http://localhost:8080/admin/home test@email.com BASIC_DIGEST
3 |
--------------------------------------------------------------------------------
/docker-hub-secret.yml:
--------------------------------------------------------------------------------
1 | apiVersion: v1beta
2 | kind: Secret
3 | metadata:
4 | name: docker-hub
5 | data:
6 | env_vars:
7 | - name: DOCKER_USERNAME
8 | value: "username"
9 | - name: DOCKER_PASSWORD
10 | value: "secret"
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM openjdk:17-jdk-alpine
2 | ARG ENVIRONMENT
3 | ENV ENVIRONMENT ${ENVIRONMENT}
4 | COPY target/*.jar app.jar
5 | ENTRYPOINT ["java","-Dspring.profiles.active=${ENVIRONMENT}", "-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]
6 |
--------------------------------------------------------------------------------
/src/main/resources/templates/home.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Welcome ''!
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/src/main/java/com/example/springpipelinedemo/SpringPipelineDemoApplication.java:
--------------------------------------------------------------------------------
1 | package com.example.springpipelinedemo;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 |
6 | @SpringBootApplication
7 | public class SpringPipelineDemoApplication {
8 |
9 | public static void main(String[] args) {
10 | SpringApplication.run(SpringPipelineDemoApplication.class, args);
11 | }
12 |
13 | }
14 |
15 |
--------------------------------------------------------------------------------
/src/test/java/com/example/springpipelinedemo/SpringPipelineDemoApplicationTests.java:
--------------------------------------------------------------------------------
1 | package com.example.springpipelinedemo;
2 |
3 | import org.junit.Test;
4 | import org.junit.runner.RunWith;
5 | import org.springframework.boot.test.context.SpringBootTest;
6 | import org.springframework.test.context.junit4.SpringRunner;
7 |
8 | @RunWith(SpringRunner.class)
9 | @SpringBootTest
10 | public class SpringPipelineDemoApplicationTests {
11 |
12 | @Test
13 | public void contextLoads() {
14 | }
15 |
16 | }
17 |
18 |
--------------------------------------------------------------------------------
/src/main/java/com/example/springpipelinedemo/repository/UserRepository.java:
--------------------------------------------------------------------------------
1 | package com.example.springpipelinedemo.repository;
2 |
3 | import com.example.springpipelinedemo.model.User;
4 | import org.springframework.data.repository.CrudRepository;
5 | import org.springframework.stereotype.Repository;
6 |
7 | import java.util.Optional;
8 |
9 | /**
10 | * Created by Rimantas Jacikevičius on 19.2.12.
11 | */
12 | @Repository
13 | public interface UserRepository extends CrudRepository {
14 |
15 | Optional findByEmail(String email);
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/src/main/java/com/example/springpipelinedemo/dto/mapper/UserMapper.java:
--------------------------------------------------------------------------------
1 | package com.example.springpipelinedemo.dto.mapper;
2 |
3 | import com.example.springpipelinedemo.dto.UserDto;
4 | import com.example.springpipelinedemo.model.User;
5 | import org.springframework.stereotype.Component;
6 |
7 | /**
8 | * Created by Rimantas Jacikevičius on 19.2.12.
9 | */
10 | @Component
11 | public class UserMapper implements Mapper {
12 |
13 | @Override
14 | public UserDto map(User user) {
15 | return new UserDto(user.getUsername());
16 | }
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/src/main/java/com/example/springpipelinedemo/dto/mapper/Mapper.java:
--------------------------------------------------------------------------------
1 | package com.example.springpipelinedemo.dto.mapper;
2 |
3 | import java.util.Collection;
4 | import java.util.List;
5 | import java.util.stream.Collectors;
6 |
7 | /**
8 | * Created by Rimantas Jacikevičius on 18.8.8.
9 | */
10 | public interface Mapper {
11 |
12 | To map(From from);
13 |
14 | default List map(Collection from) {
15 | if (from == null) {
16 | return null;
17 | }
18 | return from.stream().map(this::map).collect(Collectors.toList());
19 | }
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/src/main/java/com/example/springpipelinedemo/config/UtilityConfig.java:
--------------------------------------------------------------------------------
1 | package com.example.springpipelinedemo.config;
2 |
3 | import org.springframework.context.annotation.Bean;
4 | import org.springframework.context.annotation.Configuration;
5 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
6 | import org.springframework.security.crypto.password.PasswordEncoder;
7 |
8 | /**
9 | * Created by Rimantas Jacikevičius on 19.2.12.
10 | */
11 | @Configuration
12 | public class UtilityConfig {
13 |
14 | @Bean
15 | public PasswordEncoder passwordEncoder() {
16 | return new BCryptPasswordEncoder();
17 | }
18 |
19 | }
20 |
--------------------------------------------------------------------------------
/src/main/java/com/example/springpipelinedemo/config/JpaConfig.java:
--------------------------------------------------------------------------------
1 | package com.example.springpipelinedemo.config;
2 |
3 | import org.springframework.context.annotation.Bean;
4 | import org.springframework.context.annotation.Configuration;
5 | import org.springframework.data.jpa.domain.support.AuditingEntityListener;
6 | import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
7 |
8 | /**
9 | * Created by Rimantas Jacikevičius on 19.2.12.
10 | */
11 | @Configuration
12 | @EnableJpaAuditing
13 | public class JpaConfig {
14 |
15 | @Bean
16 | public AuditingEntityListener createAuditingListener() {
17 | return new AuditingEntityListener();
18 | }
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/src/main/java/com/example/springpipelinedemo/dto/UserDto.java:
--------------------------------------------------------------------------------
1 | package com.example.springpipelinedemo.dto;
2 |
3 | import com.fasterxml.jackson.annotation.JsonCreator;
4 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
5 | import com.fasterxml.jackson.annotation.JsonProperty;
6 |
7 | /**
8 | * Created by Rimantas Jacikevičius on 19.2.12.
9 | */
10 | @JsonIgnoreProperties(ignoreUnknown = true)
11 | public class UserDto {
12 |
13 | public final String username;
14 |
15 | @JsonCreator
16 | public UserDto(@JsonProperty("username") String username) {
17 | this.username = username;
18 | }
19 |
20 | @Override
21 | public String toString() {
22 | return "UserDto{" +
23 | "username='" + username + '\'' +
24 | '}';
25 | }
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/src/main/java/com/example/springpipelinedemo/controller/AdminController.java:
--------------------------------------------------------------------------------
1 | package com.example.springpipelinedemo.controller;
2 |
3 | import org.springframework.security.core.annotation.AuthenticationPrincipal;
4 | import org.springframework.security.core.userdetails.UserDetails;
5 | import org.springframework.stereotype.Controller;
6 | import org.springframework.ui.Model;
7 | import org.springframework.web.bind.annotation.GetMapping;
8 | import org.springframework.web.bind.annotation.RequestMapping;
9 | import org.springframework.web.servlet.ModelAndView;
10 |
11 | /**
12 | * Created by Rimantas Jacikevičius on 19.2.14.
13 | */
14 | @Controller
15 | @RequestMapping("/admin")
16 | public class AdminController {
17 |
18 | @GetMapping("/home")
19 | public ModelAndView home(@AuthenticationPrincipal UserDetails details, Model model) {
20 | model.addAttribute("user", details);
21 |
22 | return new ModelAndView("home");
23 | }
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/src/main/java/com/example/springpipelinedemo/controller/forms/SignupForm.java:
--------------------------------------------------------------------------------
1 | package com.example.springpipelinedemo.controller.forms;
2 |
3 | import com.fasterxml.jackson.annotation.JsonCreator;
4 | import com.fasterxml.jackson.annotation.JsonProperty;
5 |
6 | import javax.validation.constraints.Email;
7 | import javax.validation.constraints.NotNull;
8 |
9 | /**
10 | * Created by Rimantas Jacikevičius on 19.2.14.
11 | */
12 | public class SignupForm {
13 |
14 | public final String email;
15 | public final String password;
16 |
17 | @JsonCreator
18 | public SignupForm(
19 | @JsonProperty("email") @Email @NotNull String email,
20 | @JsonProperty("password") @NotNull String password) {
21 | this.email = email;
22 | this.password = password;
23 | }
24 |
25 | @Override
26 | public String toString() {
27 | return "SignupForm{" +
28 | "email='" + email + '\'' +
29 | ", password='" + password + '\'' +
30 | '}';
31 | }
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Rendered Text
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/src/main/java/com/example/springpipelinedemo/controller/UserController.java:
--------------------------------------------------------------------------------
1 | package com.example.springpipelinedemo.controller;
2 |
3 | import com.example.springpipelinedemo.controller.forms.SignupForm;
4 | import com.example.springpipelinedemo.dto.UserDto;
5 | import com.example.springpipelinedemo.service.UserService;
6 | import org.springframework.web.bind.annotation.PostMapping;
7 | import org.springframework.web.bind.annotation.RequestBody;
8 | import org.springframework.web.bind.annotation.RequestMapping;
9 | import org.springframework.web.bind.annotation.RestController;
10 |
11 | import javax.validation.Valid;
12 |
13 | /**
14 | * Created by Rimantas Jacikevičius on 19.2.14.
15 | */
16 | @RestController
17 | @RequestMapping("/users")
18 | public class UserController {
19 |
20 | private final UserService userService;
21 |
22 | public UserController(UserService userService) {
23 | this.userService = userService;
24 | }
25 |
26 | @PostMapping
27 | public UserDto signup(@RequestBody @Valid SignupForm signupForm) {
28 | return userService.createUser(signupForm.email, signupForm.password);
29 | }
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/.semaphore/docker-build.yml:
--------------------------------------------------------------------------------
1 | version: v1.0
2 | name: "Dockerize pipeline \U0001F433"
3 | agent:
4 | machine:
5 | type: e1-standard-2
6 | os_image: ubuntu2004
7 | blocks:
8 | - name: Build
9 | task:
10 | env_vars:
11 | - name: MAVEN_OPTS
12 | value: '-Dmaven.repo.local=.m2'
13 | - name: ENVIRONMENT
14 | value: dev
15 | secrets:
16 | - name: dockerhub
17 | prologue:
18 | commands:
19 | - checkout
20 | - 'cache restore spring-pipeline-build-$SEMAPHORE_GIT_BRANCH-$(checksum pom.xml),spring-pipeline-build-$SEMAPHORE_GIT_BRANCH,spring-pipeline-build'
21 | - cache restore
22 | jobs:
23 | - name: Docker build
24 | commands:
25 | - mvn -q package -Dmaven.test.skip=true
26 | - echo "$DOCKER_PASSWORD" | docker login --username "$DOCKER_USERNAME" --password-stdin
27 | - 'docker pull "$DOCKER_USERNAME"/semaphore-demo-java-spring:latest || true'
28 | - 'docker build --cache-from "$DOCKER_USERNAME"/semaphore-demo-java-spring:latest --build-arg ENVIRONMENT="${ENVIRONMENT}" -t "$DOCKER_USERNAME"/semaphore-demo-java-spring:latest .'
29 | - 'docker push "$DOCKER_USERNAME"/semaphore-demo-java-spring:latest'
30 |
--------------------------------------------------------------------------------
/src/main/java/com/example/springpipelinedemo/tasks/InitializeDefaultUser.java:
--------------------------------------------------------------------------------
1 | package com.example.springpipelinedemo.tasks;
2 |
3 | import com.example.springpipelinedemo.service.UserService;
4 | import org.springframework.beans.factory.annotation.Value;
5 | import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
6 | import org.springframework.boot.context.event.ApplicationReadyEvent;
7 | import org.springframework.context.ApplicationListener;
8 | import org.springframework.stereotype.Component;
9 |
10 | /**
11 | * Created by Rimantas Jacikevičius on 19.2.12.
12 | */
13 | @Component
14 | @ConditionalOnExpression("not ${testContext:false}")
15 | public class InitializeDefaultUser implements ApplicationListener {
16 |
17 | private final UserService userService;
18 | private final String defaultUsername;
19 | private final String defaultPassword;
20 |
21 | public InitializeDefaultUser(UserService userService,
22 | @Value("${defaultUser}") String defaultUsername,
23 | @Value("${defaultPassword}") String defaultPassword) {
24 | this.userService = userService;
25 | this.defaultUsername = defaultUsername;
26 | this.defaultPassword = defaultPassword;
27 | }
28 |
29 | @Override
30 | public void onApplicationEvent(ApplicationReadyEvent applicationReadyEvent) {
31 | userService.createUser(defaultUsername, defaultPassword);
32 | }
33 |
34 | }
--------------------------------------------------------------------------------
/.semaphore/semaphore.yml:
--------------------------------------------------------------------------------
1 | version: v1.0
2 | name: Java Spring example CI pipeline on Semaphore
3 | agent:
4 | machine:
5 | type: e1-standard-2
6 | os_image: ubuntu2004
7 | blocks:
8 | - name: Build
9 | task:
10 | env_vars:
11 | - name: MAVEN_OPTS
12 | value: '-Dmaven.repo.local=.m2'
13 | jobs:
14 | - name: Build
15 | commands:
16 | - sem-version java 17
17 | - checkout
18 | - cache restore
19 | - 'mvn -q package jmeter:configure -Dmaven.test.skip=true'
20 | - cache store
21 | - name: Test
22 | task:
23 | env_vars:
24 | - name: MAVEN_OPTS
25 | value: '-Dmaven.repo.local=.m2'
26 | prologue:
27 | commands:
28 | - sem-version java 17
29 | - checkout
30 | - cache restore
31 | - mvn -q test-compile -Dmaven.test.skip=true
32 | jobs:
33 | - name: Unit tests
34 | commands:
35 | - mvn test
36 | - name: Integration tests
37 | commands:
38 | - mvn test -Pintegration-testing
39 | - name: Performance tests
40 | task:
41 | env_vars:
42 | - name: MAVEN_OPTS
43 | value: '-Dmaven.repo.local=.m2'
44 | prologue:
45 | commands:
46 | - sem-version java 17
47 | - checkout
48 | - cache restore
49 | jobs:
50 | - name: Benchmark
51 | commands:
52 | - java -version
53 | - java -jar target/spring-pipeline-demo.jar > /dev/null &
54 | - sleep 20
55 | - 'mvn -q jmeter:jmeter'
56 | - 'mvn jmeter:results'
57 | promotions:
58 | - name: "Dockerize \U0001F433"
59 | pipeline_file: docker-build.yml
60 | auto_promote_on:
61 | - result: passed
62 |
--------------------------------------------------------------------------------
/src/main/java/com/example/springpipelinedemo/config/WebSecurityConfig.java:
--------------------------------------------------------------------------------
1 | package com.example.springpipelinedemo.config;
2 |
3 | import com.example.springpipelinedemo.service.UserService;
4 | import org.springframework.beans.factory.annotation.Autowired;
5 | import org.springframework.context.annotation.Bean;
6 | import org.springframework.context.annotation.Configuration;
7 | import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
8 | import org.springframework.security.config.annotation.web.builders.HttpSecurity;
9 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
10 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
11 | import org.springframework.security.core.userdetails.UserDetailsService;
12 |
13 | /**
14 | * Created by Rimantas Jacikevičius on 18.12.29.
15 | */
16 |
17 | @Configuration
18 | @EnableWebSecurity
19 | public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
20 |
21 | private final UserService userService;
22 |
23 | public WebSecurityConfig(UserService userService) {
24 | this.userService = userService;
25 | }
26 |
27 | @Override
28 | protected void configure(HttpSecurity http) throws Exception {
29 | http.csrf().disable()
30 | .authorizeRequests()
31 | .antMatchers("/admin/**").authenticated()
32 | .anyRequest().permitAll()
33 | .and()
34 | .formLogin()
35 | .permitAll()
36 | .and()
37 | .logout()
38 | .permitAll().and()
39 | .httpBasic();
40 | }
41 |
42 | @Autowired
43 | public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
44 | auth.userDetailsService(userService);
45 | }
46 |
47 | @Bean
48 | @Override
49 | public UserDetailsService userDetailsService() {
50 | return userService;
51 | }
52 |
53 | }
54 |
--------------------------------------------------------------------------------
/src/main/java/com/example/springpipelinedemo/service/UserService.java:
--------------------------------------------------------------------------------
1 | package com.example.springpipelinedemo.service;
2 |
3 | import com.example.springpipelinedemo.dto.UserDto;
4 | import com.example.springpipelinedemo.dto.mapper.UserMapper;
5 | import com.example.springpipelinedemo.model.User;
6 | import com.example.springpipelinedemo.repository.UserRepository;
7 | import org.springframework.security.core.userdetails.UserDetails;
8 | import org.springframework.security.core.userdetails.UserDetailsService;
9 | import org.springframework.security.core.userdetails.UsernameNotFoundException;
10 | import org.springframework.security.crypto.password.PasswordEncoder;
11 | import org.springframework.stereotype.Service;
12 |
13 | import static org.springframework.util.Assert.notNull;
14 |
15 | /**
16 | * Created by Rimantas Jacikevičius on 19.2.12.
17 | */
18 | @Service
19 | public class UserService implements UserDetailsService {
20 |
21 | private final UserRepository userRepository;
22 | private final PasswordEncoder passwordEncoder;
23 | private final UserMapper userMapper;
24 |
25 | public UserService(UserRepository userRepository,
26 | PasswordEncoder passwordEncoder,
27 | UserMapper userMapper) {
28 | this.userRepository = userRepository;
29 | this.passwordEncoder = passwordEncoder;
30 | this.userMapper = userMapper;
31 | }
32 |
33 | @Override
34 | public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
35 | return userRepository.findByEmail(username).orElseThrow(
36 | () -> new UsernameNotFoundException(String.format("User '%s' was not found", username)));
37 | }
38 |
39 | public UserDto createUser(String email, String password) {
40 | notNull(email, "User must have an email");
41 | notNull(password, "User must have a password");
42 |
43 | User user = userRepository.save(new User(email, passwordEncoder.encode(password)));
44 | return userMapper.map(user);
45 | }
46 |
47 | }
48 |
--------------------------------------------------------------------------------
/src/it/java/com/example/springpipelinedemo/repository/UserRepositoryTest.java:
--------------------------------------------------------------------------------
1 | package com.example.springpipelinedemo.repository;
2 |
3 | import com.example.springpipelinedemo.model.User;
4 | import org.junit.Before;
5 | import org.junit.Test;
6 | import org.junit.runner.RunWith;
7 | import org.springframework.beans.factory.annotation.Autowired;
8 | import org.springframework.boot.test.context.SpringBootTest;
9 | import org.springframework.dao.DataIntegrityViolationException;
10 | import org.springframework.test.context.TestPropertySource;
11 | import org.springframework.test.context.junit4.SpringRunner;
12 |
13 | import java.util.Date;
14 | import java.util.Optional;
15 |
16 | import static org.assertj.core.api.Assertions.assertThat;
17 | import static org.assertj.core.api.Assertions.fail;
18 |
19 |
20 | /**
21 | * Created by Rimantas Jacikevičius on 19.2.12.
22 | */
23 | @RunWith(SpringRunner.class)
24 | @SpringBootTest
25 | @TestPropertySource(properties = "testContext=true")
26 | public class UserRepositoryTest {
27 |
28 | @Autowired
29 | UserRepository userRepository;
30 |
31 | @Before
32 | public void setUp() {
33 | userRepository.deleteAll();
34 | }
35 |
36 | @Test
37 | public void save() {
38 |
39 | Date timestamp = new Date();
40 |
41 | User user = new User("email", "pass");
42 | User save = userRepository.save(user);
43 |
44 | assertThat(save.getId()).isNotNull();
45 |
46 | Optional result = userRepository.findById(save.getId());
47 |
48 | assertThat(result).isPresent();
49 |
50 | assertThat(result.get().getEmail()).isEqualTo(user.getEmail());
51 | assertThat(result.get().getPassword()).isEqualTo(user.getPassword());
52 | assertThat(result.get().getId()).isEqualTo(save.getId());
53 | assertThat(result.get().getCreatedDate()).isCloseTo(timestamp, 1000);
54 | assertThat(result.get().getModifiedDate()).isCloseTo(timestamp, 1000);
55 | }
56 |
57 | @Test(expected = DataIntegrityViolationException.class)
58 | public void save_duplicateKeys() {
59 | String email = "email";
60 |
61 | try {
62 | userRepository.save(new User(email, "pass1"));
63 | } catch (DataIntegrityViolationException e) {
64 | fail("Not expected at this point");
65 | }
66 |
67 | userRepository.save(new User(email, "pass2"));
68 | }
69 |
70 |
71 | @Test
72 | public void findByEmail() {
73 | String email = "email";
74 |
75 | User user = userRepository.save(new User(email, "pass1"));
76 |
77 | Optional result = userRepository.findByEmail(email);
78 |
79 | assertThat(result).isPresent();
80 | assertThat(result.get()).isEqualToIgnoringGivenFields(user, "createdDate", "modifiedDate");
81 | }
82 |
83 | @Test
84 | public void findByEmail_notFound() {
85 | Optional result = userRepository.findByEmail("email");
86 | assertThat(result).isEmpty();
87 | }
88 |
89 | }
--------------------------------------------------------------------------------
/src/test/java/com/example/springpipelinedemo/service/UserServiceTest.java:
--------------------------------------------------------------------------------
1 | package com.example.springpipelinedemo.service;
2 |
3 | import com.example.springpipelinedemo.dto.UserDto;
4 | import com.example.springpipelinedemo.model.User;
5 | import com.example.springpipelinedemo.repository.UserRepository;
6 | import org.junit.Test;
7 | import org.junit.runner.RunWith;
8 | import org.mockito.ArgumentCaptor;
9 | import org.springframework.beans.factory.annotation.Autowired;
10 | import org.springframework.boot.test.context.SpringBootTest;
11 | import org.springframework.boot.test.mock.mockito.MockBean;
12 | import org.springframework.security.core.userdetails.UserDetails;
13 | import org.springframework.security.core.userdetails.UsernameNotFoundException;
14 | import org.springframework.security.crypto.password.PasswordEncoder;
15 | import org.springframework.test.annotation.DirtiesContext;
16 | import org.springframework.test.context.TestPropertySource;
17 | import org.springframework.test.context.junit4.SpringRunner;
18 |
19 | import java.util.Optional;
20 |
21 | import static org.assertj.core.api.Assertions.assertThat;
22 | import static org.mockito.Mockito.*;
23 |
24 | /**
25 | * Created by Rimantas Jacikevičius on 19.2.14.
26 | */
27 | @RunWith(SpringRunner.class)
28 | @SpringBootTest
29 | @DirtiesContext
30 | @TestPropertySource(properties = "testContext=true")
31 | public class UserServiceTest {
32 |
33 | @MockBean
34 | UserRepository userRepository;
35 |
36 | @Autowired
37 | UserService userService;
38 | @Autowired
39 | PasswordEncoder encoder;
40 |
41 | @Test
42 | public void findByName() {
43 | String name = "name";
44 |
45 | User user = mock(User.class);
46 | doReturn(Optional.of(user)).when(userRepository).findByEmail(name);
47 |
48 | UserDetails result = userService.loadUserByUsername(name);
49 |
50 | assertThat(user).isEqualTo(result);
51 |
52 | verify(userRepository).findByEmail(name);
53 | verifyNoMoreInteractions(userRepository);
54 | }
55 |
56 | @Test(expected = UsernameNotFoundException.class)
57 | public void findByNameNotFound() {
58 |
59 | doReturn(Optional.empty()).when(userRepository).findByEmail(any());
60 |
61 | userService.loadUserByUsername("name");
62 | }
63 |
64 | @Test
65 | public void createUser() {
66 | String email = "email";
67 | String password = "pass";
68 |
69 | User user = mock(User.class);
70 | doReturn(email).when(user).getEmail();
71 | doReturn(email).when(user).getUsername();
72 | doReturn(user).when(userRepository).save(any());
73 |
74 | UserDto result = userService.createUser(email, password);
75 |
76 | assertThat(result.username).isEqualTo(email);
77 |
78 | ArgumentCaptor captor = ArgumentCaptor.forClass(User.class);
79 |
80 | verify(userRepository).save(captor.capture());
81 | verifyNoMoreInteractions(userRepository);
82 |
83 | User captured = captor.getValue();
84 | assertThat(captured.getEmail()).isEqualTo(email);
85 | assertThat(encoder.matches(password,captured.getPassword())).isTrue();
86 |
87 | }
88 |
89 | }
--------------------------------------------------------------------------------
/src/it/java/com/example/springpipelinedemo/controller/UserControllerTest.java:
--------------------------------------------------------------------------------
1 | package com.example.springpipelinedemo.controller;
2 |
3 | import com.example.springpipelinedemo.SpringPipelineDemoApplication;
4 | import com.example.springpipelinedemo.controller.forms.SignupForm;
5 | import com.example.springpipelinedemo.dto.UserDto;
6 | import com.example.springpipelinedemo.service.UserService;
7 | import com.fasterxml.jackson.databind.ObjectMapper;
8 | import org.junit.Before;
9 | import org.junit.Test;
10 | import org.junit.runner.RunWith;
11 | import org.springframework.beans.factory.annotation.Autowired;
12 | import org.springframework.boot.test.context.SpringBootTest;
13 | import org.springframework.boot.test.mock.mockito.MockBean;
14 | import org.springframework.http.MediaType;
15 | import org.springframework.test.annotation.DirtiesContext;
16 | import org.springframework.test.context.TestPropertySource;
17 | import org.springframework.test.context.junit4.SpringRunner;
18 | import org.springframework.test.context.web.WebAppConfiguration;
19 | import org.springframework.test.web.servlet.MockMvc;
20 | import org.springframework.test.web.servlet.setup.MockMvcBuilders;
21 | import org.springframework.web.context.WebApplicationContext;
22 |
23 | import javax.servlet.Filter;
24 |
25 | import static org.hamcrest.Matchers.is;
26 | import static org.mockito.Mockito.*;
27 | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
28 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
29 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
30 |
31 | /**
32 | * Created by Rimantas Jacikevičius on 19.2.14.
33 | */
34 | @RunWith(SpringRunner.class)
35 | @WebAppConfiguration
36 | @SpringBootTest(classes = SpringPipelineDemoApplication.class)
37 | @TestPropertySource(properties = "testContext=true")
38 | @DirtiesContext
39 | public class UserControllerTest {
40 |
41 | @MockBean
42 | UserService userService;
43 |
44 | @Autowired
45 | private WebApplicationContext webApplicationContext;
46 | @Autowired
47 | private Filter springSecurityFilterChain;
48 |
49 | private MockMvc mockMvc;
50 | private ObjectMapper objectMapper = new ObjectMapper();
51 |
52 | @Before
53 | public void setup() throws Exception {
54 | mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext)
55 | .addFilter(springSecurityFilterChain)
56 | .build();
57 | }
58 |
59 | @Test
60 | public void signup() throws Exception {
61 | SignupForm form = new SignupForm("a@b.test","somepassword");
62 |
63 | doReturn(new UserDto(form.email)).when(userService).createUser(form.email, form.password);
64 |
65 | mockMvc.perform(
66 | post("/users")
67 | .contentType(MediaType.APPLICATION_JSON)
68 | .content(objectMapper.writeValueAsBytes(form)))
69 | .andExpect(
70 | status().isOk())
71 | .andExpect(
72 | jsonPath("$.username", is(form.email))
73 | );
74 |
75 | verify(userService).createUser(form.email, form.password);
76 | verifyNoMoreInteractions(userService);
77 | }
78 |
79 | }
--------------------------------------------------------------------------------
/src/it/java/com/example/springpipelinedemo/controller/AdminControllerTest.java:
--------------------------------------------------------------------------------
1 | package com.example.springpipelinedemo.controller;
2 |
3 | import com.example.springpipelinedemo.SpringPipelineDemoApplication;
4 | import com.example.springpipelinedemo.model.User;
5 | import com.example.springpipelinedemo.service.UserService;
6 | import com.fasterxml.jackson.databind.ObjectMapper;
7 | import org.apache.tomcat.util.codec.binary.Base64;
8 | import org.junit.Before;
9 | import org.junit.Test;
10 | import org.junit.runner.RunWith;
11 | import org.springframework.beans.factory.annotation.Autowired;
12 | import org.springframework.boot.test.context.SpringBootTest;
13 | import org.springframework.boot.test.mock.mockito.MockBean;
14 | import org.springframework.http.HttpHeaders;
15 | import org.springframework.security.crypto.password.PasswordEncoder;
16 | import org.springframework.test.annotation.DirtiesContext;
17 | import org.springframework.test.context.TestPropertySource;
18 | import org.springframework.test.context.junit4.SpringRunner;
19 | import org.springframework.test.context.web.WebAppConfiguration;
20 | import org.springframework.test.web.servlet.MockMvc;
21 | import org.springframework.test.web.servlet.setup.MockMvcBuilders;
22 | import org.springframework.web.context.WebApplicationContext;
23 |
24 | import javax.servlet.Filter;
25 |
26 | import java.nio.charset.Charset;
27 |
28 | import static org.mockito.Mockito.doReturn;
29 | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
30 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
31 |
32 | /**
33 | * Created by Rimantas Jacikevičius on 19.2.14.
34 | */
35 | @RunWith(SpringRunner.class)
36 | @WebAppConfiguration
37 | @SpringBootTest(classes = SpringPipelineDemoApplication.class)
38 | @TestPropertySource(properties = "testContext=true")
39 | @DirtiesContext
40 | public class AdminControllerTest {
41 |
42 | @MockBean
43 | UserService userService;
44 |
45 | @Autowired
46 | PasswordEncoder encoder;
47 | @Autowired
48 | private WebApplicationContext webApplicationContext;
49 | @Autowired
50 | private Filter springSecurityFilterChain;
51 |
52 | private MockMvc mockMvc;
53 | private ObjectMapper objectMapper = new ObjectMapper();
54 |
55 | @Before
56 | public void setup() throws Exception {
57 | mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext)
58 | .addFilter(springSecurityFilterChain)
59 | .build();
60 | }
61 |
62 | @Test
63 | public void home() throws Exception {
64 | String username = "hello@world.com";
65 | String pass = "pass123";
66 |
67 | User user = new User(username, encoder.encode(pass));
68 | doReturn(user).when(userService).loadUserByUsername(username);
69 |
70 | String auth = username+":"+pass;
71 |
72 | String token = "Basic "+ new String(Base64.encodeBase64(
73 | auth.getBytes(Charset.forName("US-ASCII"))));
74 |
75 | mockMvc.perform(
76 | get("/admin/home")
77 | .header(HttpHeaders.AUTHORIZATION, token))
78 | .andExpect(
79 | status().isOk());
80 | }
81 |
82 | @Test
83 | public void home_unauthorised() throws Exception {
84 | mockMvc.perform(
85 | get("/admin/home"))
86 | .andExpect(
87 | status().isUnauthorized());
88 | }
89 | }
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 |
6 | org.springframework.boot
7 | spring-boot-starter-parent
8 | 2.1.2.RELEASE
9 |
10 |
11 | com.example
12 | spring-pipeline-demo
13 | 0.0.1-SNAPSHOT
14 | spring-pipeline-demo
15 | Demo project for Spring Boot
16 | jar
17 |
18 |
19 | ${project.basedir}/src/test/java
20 | 1.8
21 |
22 |
23 |
24 |
25 | org.springframework.boot
26 | spring-boot-starter-data-jpa
27 |
28 |
29 | org.springframework.boot
30 | spring-boot-starter-security
31 |
32 |
33 | org.springframework.boot
34 | spring-boot-starter-thymeleaf
35 |
36 |
37 | org.springframework.boot
38 | spring-boot-starter-web
39 |
40 |
41 | org.apache.commons
42 | commons-lang3
43 | 3.12.0
44 |
45 |
46 | com.fasterxml.jackson.core
47 | jackson-databind
48 |
49 |
50 |
51 |
52 | com.h2database
53 | h2
54 | runtime
55 |
56 |
57 | org.springframework.boot
58 | spring-boot-starter-test
59 | test
60 |
61 |
62 | org.assertj
63 | assertj-core
64 | 3.22.0
65 | test
66 |
67 |
68 |
69 |
70 | ${myTestSourceDirectory}
71 | spring-pipeline-demo
72 |
73 |
74 | org.springframework.boot
75 | spring-boot-maven-plugin
76 |
77 |
78 | com.lazerycode.jmeter
79 | jmeter-maven-plugin
80 | 3.5.0
81 |
82 |
83 | configuration
84 |
85 | configure
86 |
87 |
88 |
89 | jmeter-tests
90 |
91 | jmeter
92 |
93 |
94 |
95 | jmeter-check-results
96 |
97 | results
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 | integration-testing
108 |
109 | false
110 | ${project.basedir}/src/it/java
111 |
112 |
113 |
114 |
115 |
116 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Semaphore demo CI/CD pipeline using Java Spring
2 |
3 | [](https://semaphore-demos.semaphoreci.com/projects/semaphore-demo-java-spring)
4 |
5 | Example Spring Boot application and CI/CD pipeline showing how to run a Java
6 | project on [Semaphore 2.0](https://semaphoreci.com).
7 |
8 | ## Application overview
9 |
10 | ### Features
11 |
12 | - Simple login screen
13 | - User registration
14 | - An endpoint secured with `Basic` authentification layer
15 | - Persistence layer for storing users in database
16 |
17 | ### Endpoints
18 |
19 | - `"/admin/home"` a secured endpoint that returns a webpage in a form of `text/html`, generated with thymeleaf. [AdminController](src/main/java/com/example/springpipelinedemo/controller/AdminController.java)
20 | - `"/login"` standard spring login endpoint
21 | - `"/logout"` rest endpoint, ends user session, redirects to `"/login"`
22 | - `"/users/signup"` rest endpoint, adds a new user to the system. [UserController](src/main/java/com/example/springpipelinedemo/controller/UserController.java)
23 |
24 | ### Persistence
25 |
26 | Persistence for this project is set up using Spring Data JPA, and utilizes `m2` database,
27 | which is a runtime database for the ease of testing and continuous integration, however is fully compatible with many
28 | standard database technologies like Postgres.
29 |
30 | There is a single database entity [User](src/main/java/com/example/springpipelinedemo/model/User.java)
31 | and a corresponding repository [UserRepository](src/main/java/com/example/springpipelinedemo/repository/UserRepository.java)
32 |
33 | ### Tests
34 |
35 | Tests are separated into two classpaths (in order to run them as separate tasks):
36 | - [src/test](src/test) holds the unit tests
37 | - [src/it](src/it) holds the integration tests, in this case repository and rest endpoint tests.
38 |
39 |
40 | ## CI/CD pipeline on Semaphore
41 |
42 | The Semaphore pipeline is configured to:
43 |
44 | 1. Build the project
45 | 2. Run tests
46 | 3. Build Docker image
47 | 4. Push image to `hub.docker.com`
48 |
49 | Semaphore pipeline configuration is located at
50 | [.semaphore/semaphore.yml](.semaphore/semaphore.yml)
51 |
52 | ### Setting up
53 |
54 | To set up this pipeline on your Semaphore account:
55 |
56 | 1. If you don't have `sem` command line tool installed, do so using `curl
57 | https://storage.googleapis.com/sem-cli-releases/get.sh | bash` and then
58 | connect to your account using `sem connect .semaphoreci.com `. You can get the private
60 | key from your account dashboard at `semaphoreci.com`.
61 | 2. Add the project to Semaphore using `sem init`.
62 | 3. This pipeline relies on public Docker repository to push artifacts of successful builds. Create an account on `https://hub.docker.com/` if you don't have one.
63 | 4. Add your `hub.docker.com` credentials to `./docker-hub-secret.yml`. The credentials should remain private, so don't publish them to your Git repository by mistake.
64 | 5. Add your `./docker-hub-secret.yml` credentials to Semaphore with `sem create -f docker-hub-secret.yml`
65 |
66 |
67 | After pushing a new commit to master, Semaphore will initiate a workflow:
68 |
69 | 
70 |
71 |
72 | ## Build configuration
73 |
74 | This project is set up using Maven. Build configuration can be found at `pom.xml`.
75 |
76 | ##### Running the project
77 |
78 | `mvn spring-boot:run`
79 |
80 | ##### Running tests
81 |
82 | Tests are separated into two classpaths: `src/test` for unit tests, and `src/it` for integration tests.
83 |
84 | To run unit tests:
85 |
86 | `mvn clean test`
87 |
88 | To run integration tests
89 |
90 | `mvn clean test -Pintegration-testing`
91 |
92 | To run performance tests
93 |
94 | `mvn clean jmeter:jmeter`
95 |
96 | ##### JMeter GUI
97 | `mvn jmeter:gui`
98 |
99 | ## License
100 |
101 | Copyright (c) 2022 Rendered Text
102 |
103 | Distributed under the MIT License. See the file LICENSE.
104 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | # Created by https://www.gitignore.io/api/maven,gradle,eclipse,intellij
3 | # Edit at https://www.gitignore.io/?templates=maven,gradle,eclipse,intellij
4 |
5 | ### Eclipse ###
6 |
7 | .metadata
8 | bin/
9 | tmp/
10 | *.tmp
11 | *.bak
12 | *.swp
13 | *~.nib
14 | local.properties
15 | .settings/
16 | .loadpath
17 | .recommenders
18 |
19 | # External tool builders
20 | .externalToolBuilders/
21 |
22 | # Locally stored "Eclipse launch configurations"
23 | *.launch
24 |
25 | # PyDev specific (Python IDE for Eclipse)
26 | *.pydevproject
27 |
28 | # CDT-specific (C/C++ Development Tooling)
29 | .cproject
30 |
31 | # CDT- autotools
32 | .autotools
33 |
34 | # Java annotation processor (APT)
35 | .factorypath
36 |
37 | # PDT-specific (PHP Development Tools)
38 | .buildpath
39 |
40 | # sbteclipse plugin
41 | .target
42 |
43 | # Tern plugin
44 | .tern-project
45 |
46 | # TeXlipse plugin
47 | .texlipse
48 |
49 | # STS (Spring Tool Suite)
50 | .springBeans
51 |
52 | # Code Recommenders
53 | .recommenders/
54 |
55 | # Annotation Processing
56 | .apt_generated/
57 |
58 | # Scala IDE specific (Scala & Java development for Eclipse)
59 | .cache-main
60 | .scala_dependencies
61 | .worksheet
62 |
63 | ### Eclipse Patch ###
64 | # Eclipse Core
65 | .project
66 |
67 | # JDT-specific (Eclipse Java Development Tools)
68 | .classpath
69 |
70 | # Annotation Processing
71 | .apt_generated
72 |
73 | .sts4-cache/
74 |
75 | ### Intellij ###
76 | .idea
77 | *.iml
78 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
79 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
80 |
81 | # User-specific stuff
82 | .idea/**/workspace.xml
83 | .idea/**/tasks.xml
84 | .idea/**/usage.statistics.xml
85 | .idea/**/dictionaries
86 | .idea/**/shelf
87 |
88 | # Generated files
89 | .idea/**/contentModel.xml
90 |
91 | # Sensitive or high-churn files
92 | .idea/**/dataSources/
93 | .idea/**/dataSources.ids
94 | .idea/**/dataSources.local.xml
95 | .idea/**/sqlDataSources.xml
96 | .idea/**/dynamic.xml
97 | .idea/**/uiDesigner.xml
98 | .idea/**/dbnavigator.xml
99 |
100 | # Gradle
101 | .idea/**/gradle.xml
102 | .idea/**/libraries
103 |
104 | # Gradle and Maven with auto-import
105 | # When using Gradle or Maven with auto-import, you should exclude module files,
106 | # since they will be recreated, and may cause churn. Uncomment if using
107 | # auto-import.
108 | # .idea/modules.xml
109 | # .idea/*.iml
110 | # .idea/modules
111 |
112 | # CMake
113 | cmake-build-*/
114 |
115 | # Mongo Explorer plugin
116 | .idea/**/mongoSettings.xml
117 |
118 | # File-based project format
119 | *.iws
120 |
121 | # IntelliJ
122 | out/
123 |
124 | # mpeltonen/sbt-idea plugin
125 | .idea_modules/
126 |
127 | # JIRA plugin
128 | atlassian-ide-plugin.xml
129 |
130 | # Cursive Clojure plugin
131 | .idea/replstate.xml
132 |
133 | # Crashlytics plugin (for Android Studio and IntelliJ)
134 | com_crashlytics_export_strings.xml
135 | crashlytics.properties
136 | crashlytics-build.properties
137 | fabric.properties
138 |
139 | # Editor-based Rest Client
140 | .idea/httpRequests
141 |
142 | # Android studio 3.1+ serialized cache file
143 | .idea/caches/build_file_checksums.ser
144 |
145 | ### Intellij Patch ###
146 | # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
147 |
148 | # *.iml
149 | # modules.xml
150 | # .idea/misc.xml
151 | # *.ipr
152 |
153 | # Sonarlint plugin
154 | .idea/sonarlint
155 |
156 | ### Maven ###
157 | target/
158 | pom.xml.tag
159 | pom.xml.releaseBackup
160 | pom.xml.versionsBackup
161 | pom.xml.next
162 | release.properties
163 | dependency-reduced-pom.xml
164 | buildNumber.properties
165 | .mvn/timing.properties
166 | .mvn/wrapper/maven-wrapper.jar
167 |
168 | ### Gradle ###
169 | .gradle
170 | build/
171 |
172 | # Ignore Gradle GUI config
173 | gradle-app.setting
174 |
175 | # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
176 | !gradle-wrapper.jar
177 |
178 | # Cache of project
179 | .gradletasknamecache
180 |
181 | # # Work around https://youtrack.jetbrains.com/issue/IDEA-116898
182 | # gradle/wrapper/gradle-wrapper.properties
183 |
184 | ### Gradle Patch ###
185 | **/build/
186 |
187 | # End of https://www.gitignore.io/api/maven,gradle,eclipse,intellij
188 |
189 | /testResults
190 |
--------------------------------------------------------------------------------
/src/main/java/com/example/springpipelinedemo/model/User.java:
--------------------------------------------------------------------------------
1 | package com.example.springpipelinedemo.model;
2 |
3 | import org.apache.commons.lang3.builder.EqualsBuilder;
4 | import org.apache.commons.lang3.builder.HashCodeBuilder;
5 | import org.springframework.data.annotation.CreatedDate;
6 | import org.springframework.data.annotation.LastModifiedDate;
7 | import org.springframework.data.jpa.domain.support.AuditingEntityListener;
8 | import org.springframework.security.core.GrantedAuthority;
9 | import org.springframework.security.core.userdetails.UserDetails;
10 |
11 | import javax.persistence.*;
12 | import java.util.ArrayList;
13 | import java.util.Collection;
14 | import java.util.Date;
15 |
16 | /**
17 | * Created by Rimantas Jacikevičius on 19.2.12.
18 | */
19 | @Table(name = "users")
20 | @EntityListeners({AuditingEntityListener.class})
21 | @Entity
22 | public class User implements UserDetails {
23 |
24 | public User() {
25 | }
26 |
27 | public User(String email, String password) {
28 | this.email = email;
29 | this.password = password;
30 | }
31 |
32 | @Id
33 | @GeneratedValue(strategy = GenerationType.AUTO)
34 | private Long id;
35 | @Column(unique = true)
36 | private String email;
37 | private String password;
38 |
39 | @CreatedDate
40 | private Date createdDate;
41 | @LastModifiedDate
42 | private Date modifiedDate;
43 |
44 | public Long getId() {
45 | return id;
46 | }
47 |
48 | public void setId(Long id) {
49 | this.id = id;
50 | }
51 |
52 | public String getEmail() {
53 | return email;
54 | }
55 |
56 | public void setEmail(String email) {
57 | this.email = email;
58 | }
59 |
60 | public String getPassword() {
61 | return password;
62 | }
63 |
64 | // getAuthorities() {
68 | // TODO
69 | return new ArrayList<>();
70 | }
71 |
72 | @Override
73 | public String getUsername() {
74 | return email;
75 | }
76 |
77 | @Override
78 | public boolean isAccountNonExpired() {
79 | // TODO
80 | return true;
81 | }
82 |
83 | @Override
84 | public boolean isAccountNonLocked() {
85 | // TODO
86 | return true;
87 | }
88 |
89 | @Override
90 | public boolean isCredentialsNonExpired() {
91 | // TODO
92 | return true;
93 | }
94 |
95 | @Override
96 | public boolean isEnabled() {
97 | // TODO
98 | return true;
99 | }
100 |
101 | //
102 |
103 | public void setPassword(String password) {
104 | this.password = password;
105 | }
106 |
107 | public Date getCreatedDate() {
108 | return createdDate;
109 | }
110 |
111 | public void setCreatedDate(Date createdDate) {
112 | this.createdDate = createdDate;
113 | }
114 |
115 | public Date getModifiedDate() {
116 | return modifiedDate;
117 | }
118 |
119 | public void setModifiedDate(Date modifiedDate) {
120 | this.modifiedDate = modifiedDate;
121 | }
122 |
123 | @Override
124 | public boolean equals(Object o) {
125 | if (this == o) return true;
126 |
127 | if (o == null || getClass() != o.getClass()) return false;
128 |
129 | User user = (User) o;
130 |
131 | return new EqualsBuilder()
132 | .append(createdDate, user.createdDate)
133 | .append(modifiedDate, user.modifiedDate)
134 | .append(id, user.id)
135 | .append(email, user.email)
136 | .append(password, user.password)
137 | .isEquals();
138 | }
139 |
140 | @Override
141 | public int hashCode() {
142 | return new HashCodeBuilder(17, 37)
143 | .append(id)
144 | .append(email)
145 | .append(password)
146 | .append(createdDate)
147 | .append(modifiedDate)
148 | .toHashCode();
149 | }
150 |
151 | @Override
152 | public String toString() {
153 | return "User{" +
154 | "id='" + id + '\'' +
155 | ", email='" + email + '\'' +
156 | ", password='" + password + '\'' +
157 | ", createdDate=" + createdDate +
158 | ", modifiedDate=" + modifiedDate +
159 | '}';
160 | }
161 | }
162 |
--------------------------------------------------------------------------------
/src/main/resources/jmeter/HTTP Request.jmx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | false
7 | false
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | continue
16 |
17 | false
18 | 10
19 |
20 | 100
21 | 10
22 | 1550154649000
23 | 1550154649000
24 | false
25 |
26 |
27 |
28 |
29 |
30 | localhost
31 | 8080
32 |
33 |
34 |
35 | false
36 | false
37 | false
38 |
39 |
40 |
41 |
42 |
43 | http://localhost:8080/admin/home
44 | test@email.com
45 | testPass123
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 | localhost
57 | 8080
58 |
59 |
60 | /admin/home
61 | GET
62 | true
63 | false
64 | true
65 | false
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 | 200
74 |
75 | Assertion.response_code
76 | false
77 | 1
78 |
79 |
80 |
81 | false
82 |
83 | saveConfig
84 |
85 |
86 | true
87 | true
88 | true
89 |
90 | true
91 | true
92 | true
93 | true
94 | false
95 | true
96 | true
97 | false
98 | false
99 | false
100 | true
101 | false
102 | false
103 | false
104 | true
105 | 0
106 | true
107 | true
108 | true
109 | true
110 | true
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 | false
119 |
120 | saveConfig
121 |
122 |
123 | true
124 | true
125 | true
126 |
127 | true
128 | true
129 | true
130 | true
131 | false
132 | true
133 | true
134 | false
135 | false
136 | false
137 | true
138 | false
139 | false
140 | false
141 | true
142 | 0
143 | true
144 | true
145 | true
146 | true
147 | true
148 |
149 |
150 |
151 |
152 |
153 |
154 | false
155 |
156 | saveConfig
157 |
158 |
159 | true
160 | true
161 | true
162 |
163 | true
164 | true
165 | true
166 | true
167 | false
168 | true
169 | true
170 | false
171 | false
172 | false
173 | true
174 | false
175 | false
176 | false
177 | true
178 | 0
179 | true
180 | true
181 | true
182 | true
183 | true
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 | true
193 |
194 |
195 |
196 |
197 |
--------------------------------------------------------------------------------