when you open a branch, make sure that IT IS ON A REPOSITORY WITH YOUR NAME IN IT.
if you don't see your branch its because you opened it on the wrong repository.
https://start.spring.io/ -> spring boot - 2.5.2
controller/StudentsController.java
@RestController
@RequestMapping("/api/students")
public class StudentsController {
@RequestMapping(value = "", method = RequestMethod.GET)
public ResponseEntity<?> hello()
{
return new ResponseEntity<>("Hello World!", HttpStatus.OK);
}
}commit - hello world
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.6.1</version>
</dependency><!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger2 -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.6.1</version>
</dependency>
config/SwaggerConfig.java
@Configuration
@EnableSwagger2
public class SwaggerConfig {
@Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build();
}
}http://localhost:8080/swagger-ui.html#
commit - with swagger
docker run -d -p 5432:5432 -v postgresdata:/var/lib/postgresql/data -e POSTGRES_PASSWORD=postgres postgres
docker ps
docker logs [containerid]
NOTE: IF TABLEPLUS IS NOT CONNECTING: run from in terminal
~/Downloads/tableplus_work.sh
and password is sample123
docker-compose.yml
version: "3" services: db: image: postgres environment: POSTGRES_PASSWORD: postgres ports: - 5432:5432 volumes: - ./postgresdata:/var/lib/postgresql/data privileged: true
REMEMBER TO KILL THE DOCKER THAT WAS RUNNING BEFORE AS IT IS USING THE SAME PORT (5432) <br>
docker-compose up -d
commit - with docker compose
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.1.5.Final</version>
</dependency>
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>2.10.13</version>
</dependency>
try to run app, will not load
application.properties:
spring.datasource.url=jdbc:postgresql://localhost:5432/postgres
spring.datasource.username=postgres
spring.datasource.password=postgres
spring.datasource.driver-class-name=org.postgresql.Driver
#JPA properties
spring.jpa.show-sql = true
spring.jpa.hibernate.ddl-auto = update
spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect
app will run - still no tables
util/Dates.java
import org.joda.time.*;
import org.springframework.lang.Nullable;
import java.text.SimpleDateFormat;
import java.time.OffsetDateTime;
import java.util.Calendar;
import java.util.Date;
import java.util.Objects;
import java.util.TimeZone;
public class Dates {
public static SimpleDateFormat shortDate = new SimpleDateFormat("YYYY-MM-dd");
public static TimeZone TIME_ZONE = TimeZone.getTimeZone("Asia/Jerusalem");
public Dates() {
}
public static String dateToStr(@Nullable LocalDate date) {
return date == null ? null : shortDate.format(date);
}
public static Date atUtc(LocalDateTime date) {
return atUtc(date, TIME_ZONE);
}
public static Date atUtc(LocalDateTime date, TimeZone zone) {
if (date == null) return null;
Calendar calendar = Calendar.getInstance();
calendar.setFirstDayOfWeek(Calendar.SUNDAY);
calendar.setTimeZone(zone);
calendar.set(date.getYear(), date.getMonthOfYear()-1, date.getDayOfMonth());//convert from locatDateTime to Calender time
calendar.set(Calendar.HOUR_OF_DAY, date.getHourOfDay());
calendar.set(Calendar.MINUTE, date.getMinuteOfHour());
calendar.set(Calendar.SECOND, date.getSecondOfMinute());
calendar.set(Calendar.MILLISECOND, 0);
return calendar.getTime();
}
public static Date atUtc(@Nullable LocalDate date) {
return atUtc(date, TIME_ZONE);
}
public static Date atUtc(@Nullable LocalDate date, TimeZone zone) {
return date == null ? null : atUtc(date.toLocalDateTime(LocalTime.MIDNIGHT), zone);
}
public static LocalDateTime atLocalTime(Date date) {
return atLocalTime(date, TIME_ZONE);
}
public static LocalDateTime atLocalTime(Date date, TimeZone zone) {
if (date == null) return null;
var localDate = OffsetDateTime.ofInstant(date.toInstant(), zone.toZoneId()).toLocalDateTime();
Calendar c = Calendar.getInstance();
c.set(localDate.getYear(), localDate.getMonthValue() - 1, localDate.getDayOfMonth());
c.set(Calendar.HOUR_OF_DAY, localDate.getHour());
c.set(Calendar.MINUTE, localDate.getMinute());
c.set(Calendar.SECOND, localDate.getSecond());
c.set(Calendar.MILLISECOND, 0);
LocalDateTime res = LocalDateTime.fromCalendarFields(c);
return res;
}
public static Date nowUTC() {
return DateTime.now().withZone(DateTimeZone.UTC).toDate();
}
public static String getFullDateTime() {
return DateTime.now().withZone(DateTimeZone.UTC).toDateTimeISO().toString();
}
public static boolean equals(@Nullable Date date1, @Nullable Date date2) {
if (date1 != null && date2 != null) {
return date1.getTime() == date2.getTime();
} else {
return Objects.equals(date1, date2);
}
}
}model/Student.java
@Entity
@Table(name="student")
public class Student implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy= GenerationType.AUTO)
private Long id;
@NotNull
@Column(nullable = false, updatable = false)
private Date createdAt = Dates.nowUTC();
@NotEmpty
@Length(max = 60)
private String fullname;
private Date birthDate;
@Min(100)
@Max(800)
private Integer satScore;
@Min(30)
@Max(110)
private Double graduationScore;
@Length(max = 20)
private String phone;
@Length(max = 500)
private String profilePicture;
}explain builder plugin
commit - with spring data
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-joda</artifactId>
<version>2.12.3</version>
</dependency>
repo/StudentRepository.java
public interface StudentRepository extends CrudRepository<Student,Long> {
}repo/StudentService.java
@Service
public class StudentService {
@Autowired
StudentRepository repository;
public Iterable<Student> all() {
return repository.findAll();
}
public Optional<Student> findById(Long id) {
return repository.findById(id);
}
public Student save(Student student) {
return repository.save(student);
}
public void delete(Student student) {
repository.delete(student);
}
}model/StudentIn.java
public class StudentIn implements Serializable {
@Length(max = 60)
private String fullname;
@JsonFormat(pattern="yyyy-MM-dd")
private LocalDate birthDate;
@Min(100)
@Max(800)
private Integer satScore;
@Min(30)
@Max(110)
private Double graduationScore;
@Length(max = 20)
private String phone;
public Student toStudent() {
return aStudent().createdAt(Dates.nowUTC()).birthDate(Dates.atUtc(birthDate)).fullname(fullname)
.satScore(satScore).graduationScore(graduationScore)
.phone(phone)
.build();
}
public void updateStudent(Student student) {
student.setBirthDate(Dates.atUtc(birthDate));
student.setFullname(fullname);
student.setSatScore(satScore);
student.setGraduationScore(graduationScore);
student.setPhone(phone);
}
}Student.java
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss")
@JsonProperty("createdAt")
public LocalDateTime calcCreatedAt() {
return Dates.atLocalTime(createdAt);
}
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd")
@JsonProperty("birthDate")
public LocalDateTime calcBirthDate() {
return Dates.atLocalTime(birthDate);
}controller/StudentsController.java
@Autowired
StudentService studentService;
@RequestMapping(value = "", method = RequestMethod.GET)
public ResponseEntity<?> getAllStudents()
{
return new ResponseEntity<>(studentService.all(), HttpStatus.OK);
}
@RequestMapping(value = "/{id}", method = RequestMethod.GET)
public ResponseEntity<?> getOneStudent(@PathVariable Long id)
{
return new ResponseEntity<>(studentService.findById(id), HttpStatus.OK);
}
@RequestMapping(value = "", method = RequestMethod.POST)
public ResponseEntity<?> insertStudent(@RequestBody StudentIn studentIn)
{
Student student = studentIn.toStudent();
student = studentService.save(student);
return new ResponseEntity<>(student, HttpStatus.OK);
}
@RequestMapping(value = "/{id}", method = RequestMethod.PUT)
public ResponseEntity<?> updateStudent(@PathVariable Long id, @RequestBody StudentIn student)
{
Optional<Student> dbStudent = studentService.findById(id);
if (dbStudent.isEmpty()) throw new RuntimeException("Student with id: " + id + " not found");
student.updateStudent(dbStudent.get());
Student updatedStudent = studentService.save(dbStudent.get());
return new ResponseEntity<>(updatedStudent, HttpStatus.OK);
}
@RequestMapping(value = "/{id}", method = RequestMethod.DELETE)
public ResponseEntity<?> deleteStudent(@PathVariable Long id)
{
Optional<Student> dbStudent = studentService.findById(id);
if (dbStudent.isEmpty()) throw new RuntimeException("Student with id: " + id + " not found");
studentService.delete(dbStudent.get());
return new ResponseEntity<>("DELETED", HttpStatus.OK);
}commit - with students CRUD + REST
###FPS - Filter Pagination Sort
####simple filter StudentRepository.java
List<Student> findAllBySatScoreGreaterThan(Integer satScore);StudentService.java
public List<Student> getStudentWithSatHigherThan(Integer sat) {
return repository.findAllBySatScoreGreaterThan(sat);
}StudentController.java
@RequestMapping(value = "/highSat", method = RequestMethod.GET)
public ResponseEntity<?> getHighSatStudents(@RequestParam Integer sat)
{
return new ResponseEntity<>(studentService.getStudentWithSatHigherThan(sat), HttpStatus.OK);
}####FPS
apply fps.patch
model/StudentOut:
@Entity
@SqlResultSetMapping(name = "StudentOut")
@JsonInclude(JsonInclude.Include.NON_NULL)
public class StudentOut {
@Id
private Long id;
private Date createdat;
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss")
@JsonProperty("createdat")
public LocalDateTime calcCreatedAt() {
return Dates.atLocalTime(createdat);
}
private String fullname;
private Date birthdate;
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd")
@JsonProperty("birthdate")
public LocalDateTime calcBirthDate() {
return Dates.atLocalTime(birthdate);
}
private Integer satscore;
private Double graduationscore;
private String phone;
private String profilepicture;
public Integer getSatScore() {
return satscore;
}
public Double getGraduationScore() {
return graduationscore;
}
public Date getCreatedat() {
return createdat;
}
public String getFullname() {
return fullname;
}
public Date getBirthdate() {
return birthdate;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getPhone() {
return phone;
}
public String getProfilePicture() {
return profilepicture;
}
}model/StudentSortField.java
public enum StudentSortField {
id("id") ,
createdAt ("created_at"),
fullName ("fullname"),
birthDate ("birth_date"),
satScore ("sat_score"),
graduationScore ("graduation_score"),
phone ("phone"),
profilepicture ("profile_picture");
public final String fieldName;
private StudentSortField(String fieldName) {
this.fieldName = fieldName;
}
}StudentsController.java
@Autowired
EntityManager em;
@Autowired
ObjectMapper om;
@RequestMapping(value = "", method = RequestMethod.GET)
public ResponseEntity<PaginationAndList> search(@RequestParam(required = false) String fullName,
@RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate fromBirthDate,
@RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate toBirthDate,
@RequestParam(required = false) Integer fromSatScore,
@RequestParam(required = false) Integer toSatScore,
@RequestParam(defaultValue = "1") Integer page,
@RequestParam(defaultValue = "50") @Min(1) Integer count,
@RequestParam(defaultValue = "id") StudentSortField sort, @RequestParam(defaultValue = "asc") SortDirection sortDirection) throws JsonProcessingException {
var res =aFPS().select(List.of(
aFPSField().field("id").alias("id").build(),
aFPSField().field("created_at").alias("createdat").build(),
aFPSField().field("fullname").alias("fullname").build(),
aFPSField().field("birth_date").alias("birthdate").build(),
aFPSField().field("sat_score").alias("satscore").build(),
aFPSField().field("graduation_score").alias("graduationscore").build(),
aFPSField().field("phone").alias("phone").build(),
aFPSField().field("profile_picture").alias("profilepicture").build()
))
.from(List.of(" student s"))
.conditions(List.of(
aFPSCondition().condition("( lower(fullname) like :fullName )").parameterName("fullName").value(likeLowerOrNull(fullName)).build(),
aFPSCondition().condition("( s.birth_Date >= :fromBirthDate )").parameterName("fromBirthDate").value(atUtc(fromBirthDate)).build(),
aFPSCondition().condition("( s.birth_Date <= :toBirthDate )").parameterName("toBirthDate").value(atUtc(toBirthDate)).build(),
aFPSCondition().condition("( sat_score >= :fromSatScore )").parameterName("fromSatScore").value(fromSatScore).build(),
aFPSCondition().condition("( sat_score <= :toSatScore )").parameterName("toSatScore").value(toSatScore).build()
)).sortField(sort.fieldName).sortDirection(sortDirection).page(page).count(count)
.itemClass(StudentOut.class)
.build().exec(em, om);
return ResponseEntity.ok(res);
}commit - with FPS
apply one_to_many_grades.patch
Student.java
@OneToMany(mappedBy = "student", cascade = {CascadeType.PERSIST, CascadeType.REMOVE})
private Collection<StudentGrade> studentGrades = new ArrayList<>();StudentOut.java
private Double avgscore;StudentsController.java
public ResponseEntity<PaginationAndList> search(@RequestParam(required = false) String fullName,
@RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate fromBirthDate,
@RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate toBirthDate,
@RequestParam(required = false) Integer fromSatScore,
@RequestParam(required = false) Integer toSatScore,
@RequestParam(required = false) Integer fromAvgScore,
@RequestParam(defaultValue = "1") Integer page,
@RequestParam(defaultValue = "50") @Min(1) Integer count,
@RequestParam(defaultValue = "id") StudentSortField sort, @RequestParam(defaultValue = "asc") SortDirection sortDirection) throws JsonProcessingException {
var res =aFPS().select(List.of(
aFPSField().field("s.id").alias("id").build(),
aFPSField().field("s.created_at").alias("createdat").build(),
aFPSField().field("s.fullname").alias("fullname").build(),
aFPSField().field("s.birth_date").alias("birthdate").build(),
aFPSField().field("s.sat_score").alias("satscore").build(),
aFPSField().field("s.graduation_score").alias("graduationscore").build(),
aFPSField().field("s.phone").alias("phone").build(),
aFPSField().field("s.profile_picture").alias("profilepicture").build(),
aFPSField().field("(select avg(sg.course_score) from student_grade sg where sg.student_id = s.id ) ").alias("avgscore").build()
))
.from(List.of(" student s"))
.conditions(List.of(
aFPSCondition().condition("( lower(fullname) like :fullName )").parameterName("fullName").value(likeLowerOrNull(fullName)).build(),
aFPSCondition().condition("( s.birth_Date >= :fromBirthDate )").parameterName("fromBirthDate").value(atUtc(fromBirthDate)).build(),
aFPSCondition().condition("( s.birth_Date <= :toBirthDate )").parameterName("toBirthDate").value(atUtc(toBirthDate)).build(),
aFPSCondition().condition("( sat_score >= :fromSatScore )").parameterName("fromSatScore").value(fromSatScore).build(),
aFPSCondition().condition("( sat_score <= :toSatScore )").parameterName("toSatScore").value(toSatScore).build(),
aFPSCondition().condition("( (select avg(sg.course_score) from student_grade sg where sg.student_id = s.id ) >= :fromAvgScore )").parameterName("fromAvgScore").value(fromAvgScore).build()
)).sortField(sort.fieldName).sortDirection(sortDirection).page(page).count(count)
.itemClass(StudentOut.class)
.build().exec(em, om);
return ResponseEntity.ok(res);
}StudentSortField.java
id("s.id") ,
createdAt ("s.created_at"),
fullName ("s.fullname"),
birthDate ("s.birth_date"),
satScore ("s.at_score"),
graduationScore ("s.graduation_score"),
phone ("s.phone"),
profilepicture ("s.profile_picture"),
avgScore (" (select avg(sg.course_score) from student_grade sg where sg.student_id = s.id ) ");commit - with one to many
login to aws:
aws account: 995553441267
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-s3</artifactId>
<version>1.11.908</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.11.0</version>
</dependency>
application.properies
amazon.aws.accesskey=AKIA6PS436XZZSSPURH2
amazon.aws.secretkey=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
bucket.url=files.handson.academy
apply patch awsFileService
model/StudentOut.java
public static StudentOut of(Student student, AWSService awsService) {
StudentOut res = new StudentOut();
res.id = student.getId();
res.createdat = student.getCreatedAt();
res.fullname = student.getFullname();
res.birthdate = student.getBirthDate();
res.satscore = student.getSatScore();
res.graduationscore = student.getGraduationScore();
res.phone = student.getPhone();
res.profilepicture = awsService.generateLink(student.getProfilePicture());
res.avgscore = null;
return res;
}controller/StudentsController.java
@RequestMapping(value = "/{id}/image", method = RequestMethod.PUT)
public ResponseEntity<?> uploadStudentImage(@PathVariable Long id, @RequestParam("image") MultipartFile image)
{
Optional<Student> dbStudent = studentService.findById(id);
if (dbStudent.isEmpty()) throw new RuntimeException("Student with id: " + id + " not found");
String bucketPath = "apps/niv/student-" + id + ".png" ;
awsService.putInBucket(image, bucketPath);
dbStudent.get().setProfilePicture(bucketPath);
Student updatedStudent = studentService.save(dbStudent.get());
return new ResponseEntity<>(StudentOut.of(updatedStudent, awsService) , HttpStatus.OK);
}make user of student.of in get and put
commit - AWS S3 & presigned link
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.8.1</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.4</version>
</dependency>
application.properties
sms4free.key=J2IX1eEa9
sms4free.user=0525236451
sms4free.password=66534228
util/SmsService.java
import okhttp3.*;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
// copied from documentation https://www.sms4free.co.il/outcome-sms-api.html
@Service
public class SmsService {
protected final Log logger = LogFactory.getLog(getClass());
OkHttpClient client = new OkHttpClient.Builder().build();
@Value("${sms4free.key}")
private String ACCOUNT_KEY;
@Value("${sms4free.user}")
private String ACCOUNT_USER;
@Value("${sms4free.password}")
private String ACCOUNT_PASS;
//Overloaded constructor to send a message to both a phone number and an email address.
public boolean send(String text, String phoneNumber) {
if (phoneNumber == null) return false;
MediaType mediaType = MediaType.parse("application/text");
String url = "https://www.sms4free.co.il/ApiSMS/SendSMS";
String key = ACCOUNT_KEY;
String user = ACCOUNT_USER;
String pass = ACCOUNT_PASS;
try {
RequestBody formBody = new FormBody.Builder()
.add("key", key)
.add("user", user)
.add("pass", pass)
.add("sender", "HANDSON")
.add("recipient", phoneNumber)
.add("msg", text)
.build();
Request request = new Request.Builder()
.url(url)
.post(formBody)
.addHeader("content-type", "application/json")
.addHeader("Accept-Language", "en-US,en;q=0.5")
.build();
String data = client.newCall(request).execute().body().string();
//print result
boolean success = Integer.parseInt(data) > 0;
return success;
}catch(Exception ex) {
ex.printStackTrace();
return false;
}
}
}controller/studentsController.java
@Autowired
SmsService smsService;
@RequestMapping(value = "/sms/all", method = RequestMethod.POST)
public ResponseEntity<?> smsAll(@RequestParam String text)
{
new Thread(()-> {
IteratorUtils.toList(studentService.all().iterator())
.parallelStream()
.map(student -> student.getPhone())
.filter(phone -> !isEmpty(phone))
.forEach(phone -> smsService.send(text, phone));
}).start();
return new ResponseEntity<>("SENDING", HttpStatus.OK);
}check: https://www.sms4free.co.il/SMSLog.html
commit - Async -> SMS integration
copy to basicSpring-ms
use notepad to edit pom.xml add -ms to name and artifactId
model/MessageAndPhones.java
public class MessageAndPhones {
String message;
List<String> phones;
public String getMessage() {
return message;
}
public List<String> getPhones() {
return phones;
}
public MessageAndPhones(String message, List<String> phones) {
this.message = message;
this.phones = phones;
}
}controller/SmsController.java
@RestController
@RequestMapping("/api")
public class SmsController {
@Autowired
SmsService smsService;
@RequestMapping(value = "/sms", method = RequestMethod.POST)
public ResponseEntity<?> smsAll(@RequestBody MessageAndPhones messageAndPhones)
{
new Thread(()-> {
messageAndPhones.getPhones()
.parallelStream()
.forEach(phone -> smsService.send(messageAndPhones.getMessage(), phone));
}).start();
return new ResponseEntity<>("SENDING", HttpStatus.OK);
}
}config/RestTemplateConfig.java
@Component
public class RestTemplateConfig {
@Bean
@RequestScope
public RestTemplate getRestTemplate(HttpServletRequest inReq) {
final String authHeader =
inReq.getHeader(HttpHeaders.AUTHORIZATION);
final RestTemplate restTemplate = new RestTemplate();
if (authHeader != null && !authHeader.isEmpty()) {
restTemplate.getInterceptors().add(
(outReq, bytes, clientHttpReqExec) -> {
outReq.getHeaders().set(
HttpHeaders.AUTHORIZATION, authHeader
);
return clientHttpReqExec.execute(outReq, bytes);
});
}
return restTemplate;
}
}model/MessageAndPhones.java
public class MessageAndPhones {
String message;
List<String> phones;
public String getMessage() {
return message;
}
public List<String> getPhones() {
return phones;
}
public MessageAndPhones(String message, List<String> phones) {
this.message = message;
this.phones = phones;
}
}application.properties
sms.ms.url=http://localhost:8081/
util/SmsService.java
@Value("${sms.ms.url}")
String SMS_MS_URL;
protected final Log logger = LogFactory.getLog(getClass());
OkHttpClient client = new OkHttpClient.Builder().build();
@Autowired
RestTemplate rTemplate;
public String sendSms(MessageAndPhones messageAndPhones) {
return rTemplate.postForObject(SMS_MS_URL + "/api/sms/", messageAndPhones, String.class);
}controller/StudentsController.java
@RequestMapping(value = "/sms/all", method = RequestMethod.POST)
public ResponseEntity<?> smsAll(@RequestParam String text)
{
List<String> phones =
IteratorUtils.toList(studentService.all().iterator())
.parallelStream()
.map(student -> student.getPhone())
.filter(phone -> !isEmpty(phone))
.collect(Collectors.toList());
return new ResponseEntity<>(smsService.send(new MessageAndPhones(text, phones)), HttpStatus.OK);
}StudentsControllerTest.java
verify(smsService, atLeastOnce()).send(any()); <dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
apply jwt patch
https://bcrypt-generator.com/
config/SwaggerConfig.java
import com.google.common.collect.Lists;
import org.slf4j.Logger;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.domain.Pageable;
import org.springframework.http.ResponseEntity;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.*;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import static springfox.documentation.builders.PathSelectors.regex;
@Configuration
@EnableSwagger2
public class SwaggerConfig {
public static final String AUTHORIZATION_HEADER = "Authorization";
public static final String DEFAULT_INCLUDE_PATTERN = "/.*";
@Bean
public Docket swaggerSpringfoxDocket() {
Contact contact = new Contact(
"handson",
"https://hansdon-academy.com",
"admin@handson-academy.com");
List<VendorExtension> vext = new ArrayList<>();
Docket docket = new Docket(DocumentationType.SWAGGER_2)
.pathMapping("/")
.apiInfo(ApiInfo.DEFAULT)
.forCodeGeneration(true)
.genericModelSubstitutes(ResponseEntity.class)
.ignoredParameterTypes(Pageable.class)
.ignoredParameterTypes(java.sql.Date.class)
.directModelSubstitute(java.time.LocalDate.class, java.sql.Date.class)
.directModelSubstitute(java.time.ZonedDateTime.class, Date.class)
.directModelSubstitute(java.time.LocalDateTime.class, Date.class)
.securityContexts(Lists.newArrayList(securityContext()))
.securitySchemes(Lists.newArrayList(apiKey()))
.useDefaultResponseMessages(false);
docket = docket.select()
.paths(regex(DEFAULT_INCLUDE_PATTERN))
.build();
return docket;
}
private ApiKey apiKey() {
return new ApiKey("Authorization", AUTHORIZATION_HEADER, "header");
}
private SecurityContext securityContext() {
return SecurityContext.builder()
.securityReferences(defaultAuth())
.forPaths(regex(DEFAULT_INCLUDE_PATTERN))
.build();
}
List<SecurityReference> defaultAuth() {
AuthorizationScope authorizationScope
= new AuthorizationScope("global", "accessEverything");
AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
authorizationScopes[0] = authorizationScope;
return Lists.newArrayList(
new SecurityReference("Authorization", authorizationScopes));
}
}commit - with JWT
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
apply patch -> exception_handling.patch
in studentsController change RuntimeException to HandsonException
commit - with actuator & global exception
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.4</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.200</version>
<scope>test</scope>
</dependency>application.properties
spring.datasource.url=jdbc:postgresql://postgres:5432/postgres
sudo vi /etc/hosts (add 127.0.0.1 postgres, 127.0.0.1 appserver
apply path test_docker.patch
docker build . -t basic-spring
docker-compose -f docker-compose-local.yml up --force-recreate
docker login
nivitzhaky
Jul201789#
docker tag basic-spring nivitzhaky/basic-spring:niv-001
docker push nivitzhaky/basic-spring:niv-001
in docker-comopse-local.yml change image nivitzhaky/basic-spring:niv-001
test coverage
commit - with tests & dockrize
apply patch postman_newman.patch
import into postman
docker-compose -f docker-compose-ci.yml up -d --force-recreate --build
docker-compose -f docker-compose-ci.yml run wait -c server:8080 -t 120
docker exec niv-basicspring_newman_1 newman run STUDENTS_TEST.postman_collection.json --reporters cli,junit,htmlextra --reporter-junit-export "newman/report.xml" --reporter-htmlextra-export "newman/report.html"
check test/newman/report.html
commit - with postman newman
pull request
http://ec2-3-125-50-55.eu-central-1.compute.amazonaws.com:8080/
user:student
password:student
docker login
user:hoacademy
password:Hands-On!
docker tag basic-spring hoacademy/basic-spring:niv-001
docker push hoacademy/basic-spring:niv-001
login to aws: https://995553441267.signin.aws.amazon.com/console
change pem permissions
chmod 400 ~/Downloads/ec2.pem
sudo yum update -y
sudo yum install -y docker
sudo service docker start
sudo curl -L https://github.com/docker/compose/releases/download/1.22.0/docker-compose-$(uname -s)-$(uname -m) -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
echo "
version: \"3\"
services:
appserver:
container_name: server
hostname: localhost
image: nivitzhaky/basic-spring:niv-001
ports:
- "8080:8080"
postgres:
image: postgres
environment:
POSTGRES_PASSWORD: postgres
ports:
- 5432:5432
volumes:
- ./postgresdata:/var/lib/postgresql/data
privileged: true
" >> docker-compose-aws.yml
sudo /usr/local/bin/docker-compose -f docker-compose-aws.yml up -d
connect with:
http://[your machine]:8080/swagger-ui.html
don't forget to terminate the machine
https://console-openshift-console.apps.cluster.oshift.xyz/
kubeadmin
xn4NF-MwGgM-CVEYe-RMneJ