diff --git a/eureka/.gitignore b/eureka/.gitignore new file mode 100644 index 0000000..549e00a --- /dev/null +++ b/eureka/.gitignore @@ -0,0 +1,33 @@ +HELP.md +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ diff --git a/eureka/discovery-server/.gitignore b/eureka/discovery-server/.gitignore new file mode 100644 index 0000000..549e00a --- /dev/null +++ b/eureka/discovery-server/.gitignore @@ -0,0 +1,33 @@ +HELP.md +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ diff --git a/eureka/discovery-server/pom.xml b/eureka/discovery-server/pom.xml new file mode 100644 index 0000000..fd67994 --- /dev/null +++ b/eureka/discovery-server/pom.xml @@ -0,0 +1,53 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.6.4 + + + com.example + discovery-server + 0.0.1-SNAPSHOT + discovery-server + discovery-server + + 11 + 2021.0.1 + + + + org.springframework.cloud + spring-cloud-starter-netflix-eureka-server + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + org.springframework.cloud + spring-cloud-dependencies + ${spring-cloud.version} + pom + import + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + diff --git a/eureka/discovery-server/src/main/java/com/example/discovery/DiscoveryServer.java b/eureka/discovery-server/src/main/java/com/example/discovery/DiscoveryServer.java new file mode 100644 index 0000000..01cad77 --- /dev/null +++ b/eureka/discovery-server/src/main/java/com/example/discovery/DiscoveryServer.java @@ -0,0 +1,27 @@ +package com.example.discovery; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer; + +import java.io.IOException; + +@SpringBootApplication +@EnableEurekaServer +public class DiscoveryServer { + + private static Logger logger = LoggerFactory.getLogger(DiscoveryServer.class); + + public static void main(String... args) throws IOException { + // Look for configuration in discovery-server.properties or discovery-server.yml + System.setProperty("spring.config.name", "discovery-server"); + + var ctx = SpringApplication.run(DiscoveryServer.class, args); + assert (ctx != null); + logger.info("Started ..."); + System.in.read(); + ctx.close(); + } +} diff --git a/eureka/discovery-server/src/main/resources/discovery-server.yml b/eureka/discovery-server/src/main/resources/discovery-server.yml new file mode 100644 index 0000000..8d23338 --- /dev/null +++ b/eureka/discovery-server/src/main/resources/discovery-server.yml @@ -0,0 +1,20 @@ +spring: + application: + name: discovery-service +# Configure this Discovery Server +#TODO here you add configurations for server +server: + port: 3000 + +eureka: + client: + register-with-eureka: false + fetch-registry: false + +logging: + pattern: + console: "%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n" + level: + root: INFO + org.springframework: DEBUG + com.apress.cems: DEBUG diff --git a/eureka/discovery-server/src/test/java/com/example/discoveryserver/DiscoveryServerApplicationTests.java b/eureka/discovery-server/src/test/java/com/example/discoveryserver/DiscoveryServerApplicationTests.java new file mode 100644 index 0000000..f102053 --- /dev/null +++ b/eureka/discovery-server/src/test/java/com/example/discoveryserver/DiscoveryServerApplicationTests.java @@ -0,0 +1,13 @@ +package com.example.discoveryserver; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class DiscoveryServerApplicationTests { + + @Test + void contextLoads() { + } + +} diff --git a/eureka/persons-server/.gitignore b/eureka/persons-server/.gitignore new file mode 100644 index 0000000..549e00a --- /dev/null +++ b/eureka/persons-server/.gitignore @@ -0,0 +1,33 @@ +HELP.md +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ diff --git a/eureka/persons-server/pom.xml b/eureka/persons-server/pom.xml new file mode 100644 index 0000000..b342d8e --- /dev/null +++ b/eureka/persons-server/pom.xml @@ -0,0 +1,96 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.6.4 + + + com.example + persons-server + 0.0.1-SNAPSHOT + persons-server + persons-server + + 11 + 2021.0.1 + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.cloud + spring-cloud-starter-netflix-eureka-client + + + org.springframework.cloud + spring-cloud-starter-netflix-eureka-server + + + + org.springframework.boot + spring-boot-starter-test + test + + + com.h2database + h2 + 2.1.210 + runtime + + + com.sun.jersey.contribs + jersey-apache-client4 + 1.19.4 + + + com.fasterxml.jackson.core + jackson-databind + 2.13.1 + + + com.fasterxml.jackson.core + jackson-core + 2.13.1 + + + javax.validation + validation-api + 2.0.1.Final + + + org.projectlombok + lombok + + + + + + + org.springframework.cloud + spring-cloud-dependencies + ${spring-cloud.version} + pom + import + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + diff --git a/eureka/persons-server/src/main/java/com/eureka/persons/PersonRepo.java b/eureka/persons-server/src/main/java/com/eureka/persons/PersonRepo.java new file mode 100644 index 0000000..5e7b169 --- /dev/null +++ b/eureka/persons-server/src/main/java/com/eureka/persons/PersonRepo.java @@ -0,0 +1,36 @@ +package com.eureka.persons; + +import com.eureka.persons.person.Person; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; +import org.springframework.stereotype.Repository; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.Optional; + +@Repository +public interface PersonRepo extends JpaRepository { + + @Query("select p from Person p where p.username like %?1%") + Optional findByUsername(String username); + + @Query("select p from Person p where p.username like %?1%") + List findByUsernameLike(String username); + + @Query("select p from Person p where p.firstName=:fn") + List findByFirstName(@Param("fn") String firstName); + + @Query("select p from Person p where p.firstName like %?1%") + List findByFirstNameLike(String firstName); + + @Query("select p from Person p where p.lastName=:ln") + List findByLastName(@Param("ln") String lastName); + + @Query("select p from Person p where p.lastName like %?1%") + List findByLastNameLike(String lastName); + + @Query("select p from Person p where p.hiringDate=:hd") + List findByHiringDate(@Param("hd") LocalDateTime date); +} diff --git a/eureka/persons-server/src/main/java/com/eureka/persons/PersonsController.java b/eureka/persons-server/src/main/java/com/eureka/persons/PersonsController.java new file mode 100644 index 0000000..75fc8a8 --- /dev/null +++ b/eureka/persons-server/src/main/java/com/eureka/persons/PersonsController.java @@ -0,0 +1,101 @@ +package com.eureka.persons; + +import com.eureka.persons.ex.NotFoundException; +import com.eureka.persons.person.Person; +import com.eureka.persons.services.PersonService; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.validation.BindingResult; +import org.springframework.web.bind.annotation.*; + +import java.util.Comparator; +import java.util.List; +import java.util.concurrent.atomic.AtomicReference; +import java.util.stream.Collectors; + +@RestController +@RequestMapping("/persons") +public class PersonsController { + private PersonService personService; + + public PersonsController(PersonService personService) { + this.personService = personService; + } + + /** + * Handles requests to list all persons. + */ + //TODO find all persons using the functions already implemented and sort them by id + @ResponseStatus(HttpStatus.OK) + @GetMapping(produces = MediaType.APPLICATION_JSON_VALUE) + public List list() { + return personService + .findAll().stream() + .sorted(Comparator.comparing(Person::getId)) + .collect(Collectors.toList()); + } + + /** + * Handles requests to create a person. + */ + //TODO save a person to the db or throw PersonsException + @ResponseStatus(HttpStatus.CREATED) + @PostMapping + public void create(@RequestBody Person person, BindingResult result) { + if (result.hasErrors()) + throw new PersonsException(HttpStatus.BAD_REQUEST, "Person couldn't be added!"); + + personService.save(person); + } + + /** + * Returns the {@code Person} instance with id {@code id} + * + * @param id + * @return + */ + //TODO find a person by id or throw NotFoundException + @ResponseStatus(HttpStatus.OK) + @GetMapping(value = "/{id}", produces = MediaType.APPLICATION_JSON_VALUE) + public Person show(@PathVariable Long id) { + var personIdQuery = personService.findById(id); + if (personIdQuery.isEmpty()) + throw new NotFoundException(Person.class, id); + + return personIdQuery.get(); + } + + /** + * Updates the {@code Person} instance with id {@code id} + * + * @param updatedPerson + * @param id + * @return + */ + //TODO update an existing person if found else throw NotFoundException + @ResponseStatus(HttpStatus.NO_CONTENT) + @PutMapping("/{id}") + public void update(@RequestBody Person updatedPerson, @PathVariable Long id) { + var personIdQuery = personService.findById(id); + + personIdQuery.ifPresent(person -> person = updatedPerson); + // else + throw new NotFoundException(Person.class, id); + } + + /** + * Delete the {@code Person} instance with id {@code id} + * + * @param id + */ + //TODO delete a person + @ResponseStatus(HttpStatus.NO_CONTENT) + @DeleteMapping("/{id}") + public void delete(@PathVariable Long id) { + var personIdQuery = personService.findById(id); + if (personIdQuery.isEmpty()) + throw new NotFoundException(Person.class, id); + + personService.delete(personIdQuery.get()); + } +} \ No newline at end of file diff --git a/eureka/persons-server/src/main/java/com/eureka/persons/PersonsException.java b/eureka/persons-server/src/main/java/com/eureka/persons/PersonsException.java new file mode 100644 index 0000000..781c4f6 --- /dev/null +++ b/eureka/persons-server/src/main/java/com/eureka/persons/PersonsException.java @@ -0,0 +1,17 @@ +package com.eureka.persons; + +import org.springframework.http.HttpStatus; + +public class PersonsException extends RuntimeException{ + private HttpStatus status; + + public PersonsException(HttpStatus status, String message) { + super(message); + this.status = status; + } + + public PersonsException(HttpStatus status, Throwable cause) { + super(cause); + this.status = status; + } +} diff --git a/eureka/persons-server/src/main/java/com/eureka/persons/PersonsServer.java b/eureka/persons-server/src/main/java/com/eureka/persons/PersonsServer.java new file mode 100644 index 0000000..5764a84 --- /dev/null +++ b/eureka/persons-server/src/main/java/com/eureka/persons/PersonsServer.java @@ -0,0 +1,29 @@ +package com.eureka.persons; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.domain.EntityScan; +import org.springframework.cloud.netflix.eureka.EnableEurekaClient; + +import java.io.IOException; + +@EntityScan(basePackages = "com.eureka.persons") +@SpringBootApplication +@EnableEurekaClient +public class PersonsServer { + + private static final Logger logger = LoggerFactory.getLogger(PersonsServer.class); + + public static void main(String... args) throws IOException { + // Look for configuration in persons-server.properties or persons-server.yml + System.setProperty("spring.config.name", "persons-server"); + + var ctx = SpringApplication.run(PersonsServer.class, args); + assert (ctx != null); + logger.info("Started ..."); + System.in.read(); + ctx.close(); + } +} diff --git a/eureka/persons-server/src/main/java/com/eureka/persons/base/AbstractEntity.java b/eureka/persons-server/src/main/java/com/eureka/persons/base/AbstractEntity.java new file mode 100644 index 0000000..199103b --- /dev/null +++ b/eureka/persons-server/src/main/java/com/eureka/persons/base/AbstractEntity.java @@ -0,0 +1,70 @@ +package com.eureka.persons.base; + +import com.eureka.persons.util.DateProcessor; +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Getter; +import lombok.Setter; +import org.springframework.format.annotation.DateTimeFormat; + +import javax.persistence.*; +import java.io.Serializable; +import java.time.LocalDateTime; +import java.util.Comparator; +import java.util.Objects; + +@MappedSuperclass +@Getter +@Setter +public abstract class AbstractEntity implements Serializable { + + public static Comparator COMPARATOR_BY_ID = Comparator.comparing(AbstractEntity::getId); + + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + @Column(updatable = false) + protected Long id; + + @Version + protected int version; + + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = DateProcessor.DATE_FORMAT) + @Column(name = "created_at", nullable = false) + @DateTimeFormat(pattern = DateProcessor.DATE_FORMAT) + protected LocalDateTime createdAt; + + + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = DateProcessor.DATE_FORMAT) + @Column(name = "modified_at", nullable = false) + @DateTimeFormat(pattern = DateProcessor.DATE_FORMAT) + protected LocalDateTime modifiedAt; + + /** + * This constructor is required by JPA. All subclasses of this class will inherit this constructor. + */ + protected AbstractEntity() { + createdAt = LocalDateTime.now(); + modifiedAt = LocalDateTime.now(); + } + + // IDE generated methods + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + var that = (AbstractEntity) o; + if (!Objects.equals(id, that.id)) return false; + return true; + } + + @Override + public int hashCode() { + return id != null ? id.hashCode() : 0; + } + + @Override + public String toString() { + return String.format("AbstractEntity[id='%d%n', createdAt='%s', modifiedAt='%s', version='%d%n']", + id, DateProcessor.toString(createdAt), DateProcessor.toString(modifiedAt), version); + } +} diff --git a/eureka/persons-server/src/main/java/com/eureka/persons/ex/NotFoundException.java b/eureka/persons-server/src/main/java/com/eureka/persons/ex/NotFoundException.java new file mode 100644 index 0000000..094ecec --- /dev/null +++ b/eureka/persons-server/src/main/java/com/eureka/persons/ex/NotFoundException.java @@ -0,0 +1,7 @@ +package com.eureka.persons.ex; + +public class NotFoundException extends RuntimeException { + public NotFoundException(Class cls, Long id) { + super(cls.getSimpleName() + " with id: " + id + " does not exist!"); + } +} diff --git a/eureka/persons-server/src/main/java/com/eureka/persons/person/Person.java b/eureka/persons-server/src/main/java/com/eureka/persons/person/Person.java new file mode 100644 index 0000000..9064df8 --- /dev/null +++ b/eureka/persons-server/src/main/java/com/eureka/persons/person/Person.java @@ -0,0 +1,82 @@ +package com.eureka.persons.person; + +import com.eureka.persons.base.AbstractEntity; +import com.eureka.persons.util.DateProcessor; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.springframework.format.annotation.DateTimeFormat; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Transient; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; +import java.time.LocalDateTime; +import java.util.Objects; + +@Entity +@Getter +@Setter +@NoArgsConstructor +public class Person extends AbstractEntity { + interface BasicValidation{} + + @NotNull(groups = BasicValidation.class) + @Size(min = 3, max = 30, groups = BasicValidation.class) + @Column(nullable = false, unique = true) + private String username; + + @NotNull(groups = BasicValidation.class) + @Size(min = 3, max = 30, groups = BasicValidation.class) + @Column(nullable = false) + private String firstName; + + @NotNull(groups = BasicValidation.class) + @Size(min = 3, max = 30, groups = BasicValidation.class) + @Column(nullable = false) + private String lastName; + + @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) + @NotNull + @Size(min = 4, max = 50) + @Column(nullable = false) + private String password; + + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = DateProcessor.DATE_FORMAT) + @NotNull(groups = BasicValidation.class) + @Column(nullable = false) + @DateTimeFormat(pattern = DateProcessor.DATE_FORMAT) + private LocalDateTime hiringDate; + + @JsonIgnore + @Transient + private String newPassword; + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + var person = (Person) o; + if (!Objects.equals(id, person.id)) return false; + return Objects.equals(firstName, person.firstName) && + Objects.equals(lastName, person.lastName) && + Objects.equals(hiringDate.toLocalDate(), person.hiringDate.toLocalDate()); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), firstName, lastName, hiringDate.toLocalDate()); + } + + @Override + public String toString() { + return String.format("Person[username='%s', firstName='%s', lastName='%s', hiringDate='%s']\n", + username, firstName, lastName, hiringDate.toString()); + + } + +} diff --git a/eureka/persons-server/src/main/java/com/eureka/persons/services/Initializer.java b/eureka/persons-server/src/main/java/com/eureka/persons/services/Initializer.java new file mode 100644 index 0000000..22024a4 --- /dev/null +++ b/eureka/persons-server/src/main/java/com/eureka/persons/services/Initializer.java @@ -0,0 +1,65 @@ +package com.eureka.persons.services; + +import com.eureka.persons.person.Person; +import com.eureka.persons.util.DateProcessor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import javax.annotation.PostConstruct; + +@Service +@Transactional +public class Initializer { + private Logger logger = LoggerFactory.getLogger(Initializer.class); + + private PersonService personService; + + public Initializer(PersonService personService) { + this.personService = personService; + } + + @PostConstruct + public void init() { + logger.info(" -->> Starting database initialization..."); + if (personService.findAll().isEmpty()) { + createPersons(); + } + logger.info(" -->> Database initialization finished."); + } + + private void createPersons() { + Person person = new Person(); + person.setUsername("sherlock.holmes"); + person.setFirstName("Sherlock"); + person.setLastName("Holmes"); + person.setPassword("dudu"); + person.setHiringDate(DateProcessor.toDate("1983-08-15 00:23")); + personService.save(person); + + person = new Person(); + person.setUsername("jackson.brodie"); + person.setFirstName("Jackson"); + person.setLastName("Brodie"); + person.setPassword("bagy"); + person.setHiringDate(DateProcessor.toDate("1983-06-22 00:23")); + personService.save(person); + + person = new Person(); + person.setUsername("nancy.drew"); + person.setFirstName("Nancy"); + person.setLastName("Drew"); + person.setPassword("dada45"); + person.setHiringDate(DateProcessor.toDate("1990-05-21 00:23")); + personService.save(person); + + person = new Person(); + person.setUsername("irene.adler"); + person.setFirstName("Irene"); + person.setLastName("Adler"); + person.setPassword("xxxyy"); + person.setHiringDate(DateProcessor.toDate("1987-03-11 00:23")); + personService.save(person); + } +} \ No newline at end of file diff --git a/eureka/persons-server/src/main/java/com/eureka/persons/services/PersonService.java b/eureka/persons-server/src/main/java/com/eureka/persons/services/PersonService.java new file mode 100644 index 0000000..0f86a73 --- /dev/null +++ b/eureka/persons-server/src/main/java/com/eureka/persons/services/PersonService.java @@ -0,0 +1,16 @@ +package com.eureka.persons.services; + +import com.eureka.persons.person.Person; + +import java.util.List; +import java.util.Optional; + +public interface PersonService { + List findAll(); + + Optional findById(Long id); + + Person save(Person person); + + void delete(Person person); +} \ No newline at end of file diff --git a/eureka/persons-server/src/main/java/com/eureka/persons/services/PersonServiceImpl.java b/eureka/persons-server/src/main/java/com/eureka/persons/services/PersonServiceImpl.java new file mode 100644 index 0000000..6c4c0c9 --- /dev/null +++ b/eureka/persons-server/src/main/java/com/eureka/persons/services/PersonServiceImpl.java @@ -0,0 +1,42 @@ +package com.eureka.persons.services; + +import com.eureka.persons.PersonRepo; +import com.eureka.persons.person.Person; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.Optional; + +@Service +@Transactional +public class PersonServiceImpl implements PersonService { + private final PersonRepo personRepo; + + public PersonServiceImpl(PersonRepo personRepo) { + this.personRepo = personRepo; + } + + @Override + public List findAll() { + return personRepo.findAll(); + } + + @Override + public Optional findById(Long id) { + return personRepo.findById(id); + } + + + @Override + public Person save(Person person) { + personRepo.save(person); + return person; + } + + @Override + public void delete(Person person) { + personRepo.delete(person); + } +} + diff --git a/eureka/persons-server/src/main/java/com/eureka/persons/util/DateProcessor.java b/eureka/persons-server/src/main/java/com/eureka/persons/util/DateProcessor.java new file mode 100644 index 0000000..0d2f624 --- /dev/null +++ b/eureka/persons-server/src/main/java/com/eureka/persons/util/DateProcessor.java @@ -0,0 +1,17 @@ +package com.eureka.persons.util; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + +public class DateProcessor { + public static final String DATE_FORMAT= "yyyy-MM-dd HH:mm"; + private static DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_FORMAT); + + public static LocalDateTime toDate(final String date) { + return LocalDateTime.parse(date, formatter); + } + + public static String toString(final LocalDateTime date){ + return date.format(formatter); + } +} diff --git a/eureka/persons-server/src/main/java/com/eureka/persons/util/NumberGenerator.java b/eureka/persons-server/src/main/java/com/eureka/persons/util/NumberGenerator.java new file mode 100644 index 0000000..92b1c40 --- /dev/null +++ b/eureka/persons-server/src/main/java/com/eureka/persons/util/NumberGenerator.java @@ -0,0 +1,18 @@ +package com.eureka.persons.util; + +import java.util.Random; + +public final class NumberGenerator { + private static final Random RAND = new Random(); + private static final String UPPER = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + private static final String DIGITS = "0123456789"; + + private static Character randomCharacter() { + final var all = UPPER.concat(UPPER.toLowerCase()).concat(DIGITS); + return all.charAt(RAND.nextInt(all.length() - 1)); + } + + private NumberGenerator() { + // prevent initialization fo this class + } +} diff --git a/eureka/persons-server/src/main/resources/banner.txt b/eureka/persons-server/src/main/resources/banner.txt new file mode 100644 index 0000000..be21922 --- /dev/null +++ b/eureka/persons-server/src/main/resources/banner.txt @@ -0,0 +1,7 @@ +__________ _________ .__ +\______ \ ___________ __________ ____ ______ / _____/ ______________ _|__| ____ ____ + | ___// __ \_ __ \/ ___/ _ \ / \ / ___/ \_____ \_/ __ \_ __ \ \/ / |/ ___\/ __ \ + | | \ ___/| | \/\___ ( <_> ) | \\___ \ / \ ___/| | \/\ /| \ \__\ ___/ + |____| \___ >__| /____ >____/|___| /____ > /_______ /\___ >__| \_/ |__|\___ >___ > + \/ \/ \/ \/ \/ \/ \/ \/ + :: Spring Boot :: (v2.2.4.RELEASE) diff --git a/eureka/persons-server/src/main/resources/persons-server.yml b/eureka/persons-server/src/main/resources/persons-server.yml new file mode 100644 index 0000000..5f367ae --- /dev/null +++ b/eureka/persons-server/src/main/resources/persons-server.yml @@ -0,0 +1,47 @@ +spring: + application: + name: persons-service # Service registers under this name + datasource: + driver-class-name: org.h2.Driver + jdbc-url: jdbc:h2:mem:personsdb;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE + username: sa + password: password + maximum-pool-size: 5 + connection-test-query: "SELECT 1" + pool-name: cemsPool + connection-timeout: 60000 + jpa: + generate-ddl: true + hibernate: + ddl-auto: create-drop + database-platform: org.hibernate.dialect.H2Dialect + h2: + console: + enabled: true +# HTTP Server +server: + port: 4001 # HTTP (Tomcat) port + address: 0.0.0.0 + +# Discovery Server Access +#TODO here you add configurations for eureka client +eureka: + client: + service-url: + defaultZone: http://localhost:3000/eureka + fetch-registry: true + register-with-eureka: true + +info: + app: + name: persons-server + description: Spring Cloud Application Managing Person Instances + version: 1.0-SNAPSHOT + +logging: + pattern: + console: "%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n" + level: + root: INFO + org.springframework: DEBUG + com.apress.cems: DEBUG diff --git a/eureka/persons-server/src/test/java/com/example/personsserver/PersonsServerApplicationTests.java b/eureka/persons-server/src/test/java/com/example/personsserver/PersonsServerApplicationTests.java new file mode 100644 index 0000000..2fd298e --- /dev/null +++ b/eureka/persons-server/src/test/java/com/example/personsserver/PersonsServerApplicationTests.java @@ -0,0 +1,17 @@ +package com.example.personsserver; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class PersonsServerApplicationTests { + + //TODO configure discovery-server to be the Eureka's server (on port 3000) and persons-server to be the client server (on port 4001) + // hint1: you need to add some configurations in resources -> .yml files + // After you start your server and your client you need to create the endpoints from PersonsController, you have more details there + // Use postman to test the endpoints. Create a new collection and add all 5 endpoints inside of it. + @Test + void contextLoads() { + } + +} diff --git a/eureka/pom.xml b/eureka/pom.xml new file mode 100644 index 0000000..1049986 --- /dev/null +++ b/eureka/pom.xml @@ -0,0 +1,66 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.6.4 + + + com.example + eureka + 0.0.1-SNAPSHOT + eureka + eureka + pom + + 11 + 2021.0.1 + + + discovery-server + persons-server + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.cloud + spring-cloud-starter-netflix-eureka-client + + + org.springframework.cloud + spring-cloud-starter-netflix-eureka-server + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + org.springframework.cloud + spring-cloud-dependencies + ${spring-cloud.version} + pom + import + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + diff --git a/eureka/src/main/java/com/example/eureka/EurekaApplication.java b/eureka/src/main/java/com/example/eureka/EurekaApplication.java new file mode 100644 index 0000000..20c67a9 --- /dev/null +++ b/eureka/src/main/java/com/example/eureka/EurekaApplication.java @@ -0,0 +1,13 @@ +package com.example.eureka; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class EurekaApplication { + + public static void main(String[] args) { + SpringApplication.run(EurekaApplication.class, args); + } + +} diff --git a/eureka/src/main/resources/application.properties b/eureka/src/main/resources/application.properties new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/eureka/src/main/resources/application.properties @@ -0,0 +1 @@ + diff --git a/eureka/src/test/java/com/example/eureka/EurekaApplicationTests.java b/eureka/src/test/java/com/example/eureka/EurekaApplicationTests.java new file mode 100644 index 0000000..1415105 --- /dev/null +++ b/eureka/src/test/java/com/example/eureka/EurekaApplicationTests.java @@ -0,0 +1,13 @@ +package com.example.eureka; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class EurekaApplicationTests { + + @Test + void contextLoads() { + } + +} diff --git a/lab-6-api-gateway/.gitignore b/lab-6-api-gateway/.gitignore new file mode 100644 index 0000000..549e00a --- /dev/null +++ b/lab-6-api-gateway/.gitignore @@ -0,0 +1,33 @@ +HELP.md +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ diff --git a/lab-6-api-gateway/api-gateway-project/.gitignore b/lab-6-api-gateway/api-gateway-project/.gitignore new file mode 100644 index 0000000..549e00a --- /dev/null +++ b/lab-6-api-gateway/api-gateway-project/.gitignore @@ -0,0 +1,33 @@ +HELP.md +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ diff --git a/lab-6-api-gateway/api-gateway-project/pom.xml b/lab-6-api-gateway/api-gateway-project/pom.xml new file mode 100644 index 0000000..5eaa00a --- /dev/null +++ b/lab-6-api-gateway/api-gateway-project/pom.xml @@ -0,0 +1,64 @@ + + + 4.0.0 + + lab-6-api-gateway + com.example + 0.0.1-SNAPSHOT + + api-gateway-project + 0.0.1-SNAPSHOT + api-gateway-project + api-gateway-project + + 11 + 2021.0.1 + + + + + org.springframework.boot + spring-boot-starter-actuator + + + org.springframework.boot + spring-boot-starter-webflux + + + org.springframework.cloud + spring-cloud-starter-gateway + + + + io.projectreactor + reactor-test + test + + + org.springframework.cloud + spring-cloud-netflix-eureka-server + + + + + + org.springframework.cloud + spring-cloud-dependencies + ${spring-cloud.version} + pom + import + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + diff --git a/lab-6-api-gateway/api-gateway-project/src/main/java/com/example/apigatewayproject/ApiGatewayProjectApplication.java b/lab-6-api-gateway/api-gateway-project/src/main/java/com/example/apigatewayproject/ApiGatewayProjectApplication.java new file mode 100644 index 0000000..e976e83 --- /dev/null +++ b/lab-6-api-gateway/api-gateway-project/src/main/java/com/example/apigatewayproject/ApiGatewayProjectApplication.java @@ -0,0 +1,14 @@ +package com.example.apigatewayproject; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer; + +@SpringBootApplication +public class ApiGatewayProjectApplication { + + public static void main(String[] args) { + SpringApplication.run(ApiGatewayProjectApplication.class, args); + } + +} diff --git a/lab-6-api-gateway/api-gateway-project/src/main/resources/application.properties b/lab-6-api-gateway/api-gateway-project/src/main/resources/application.properties new file mode 100644 index 0000000..66403d2 --- /dev/null +++ b/lab-6-api-gateway/api-gateway-project/src/main/resources/application.properties @@ -0,0 +1 @@ +spring.main.web-application-type=reactive \ No newline at end of file diff --git a/lab-6-api-gateway/api-gateway-project/src/main/resources/application.yml b/lab-6-api-gateway/api-gateway-project/src/main/resources/application.yml new file mode 100644 index 0000000..085e160 --- /dev/null +++ b/lab-6-api-gateway/api-gateway-project/src/main/resources/application.yml @@ -0,0 +1,33 @@ +server: + port: 8080 + +#TODO use eureka to discover the URL for the service1 and service2 +#TODO configure spring cloud gateway to route the request to downstream services (service1 and service2) based on the paths(/api/greeting, /product) +#TODO for greeting endpoint add a route to accept requests to /greeting but before calling service1 it must append api before the greeting path (HINT: rewrite path filter) +#and method types (GET,POST) +spring: + application: + name: gateway + cloud: + gateway: + routes: + - id: service1 + uri: http://localhost:8081 + predicates: + - Path=/greeting/** + filters: + - RewritePath=/greeting, /api/greeting/ + - id: service2 + uri: http://localhost:8082 + predicates: + - Path=/product/** + - id: persons + uri: http://localhost:4001 + predicates: + - Path=/persons/** +eureka: + client: + service-url: + defaultZone: http://localhost:3000/eureka + fetch-registry: true + register-with-eureka: true \ No newline at end of file diff --git a/lab-6-api-gateway/api-gateway-project/src/test/java/com/example/apigatewayproject/ApiGatewayProjectApplicationTests.java b/lab-6-api-gateway/api-gateway-project/src/test/java/com/example/apigatewayproject/ApiGatewayProjectApplicationTests.java new file mode 100644 index 0000000..6c58423 --- /dev/null +++ b/lab-6-api-gateway/api-gateway-project/src/test/java/com/example/apigatewayproject/ApiGatewayProjectApplicationTests.java @@ -0,0 +1,13 @@ +package com.example.apigatewayproject; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class ApiGatewayProjectApplicationTests { + + @Test + void contextLoads() { + } + +} diff --git a/lab-6-api-gateway/pom.xml b/lab-6-api-gateway/pom.xml new file mode 100644 index 0000000..491b65e --- /dev/null +++ b/lab-6-api-gateway/pom.xml @@ -0,0 +1,52 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.6.5 + + + com.example + lab-6-api-gateway + 0.0.1-SNAPSHOT + lab-6-api-gateway + lab-6-api-gateway + + 11 + + pom + + api-gateway-project + service1 + service2 + + + + org.projectlombok + lombok + + + + org.springframework.boot + spring-boot-starter + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + diff --git a/lab-6-api-gateway/service1/.gitignore b/lab-6-api-gateway/service1/.gitignore new file mode 100644 index 0000000..549e00a --- /dev/null +++ b/lab-6-api-gateway/service1/.gitignore @@ -0,0 +1,33 @@ +HELP.md +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ diff --git a/lab-6-api-gateway/service1/pom.xml b/lab-6-api-gateway/service1/pom.xml new file mode 100644 index 0000000..89f8966 --- /dev/null +++ b/lab-6-api-gateway/service1/pom.xml @@ -0,0 +1,57 @@ + + + 4.0.0 + + lab-6-api-gateway + com.example + 0.0.1-SNAPSHOT + + service1 + 0.0.1-SNAPSHOT + service1 + service1 + + 11 + 2021.0.1 + + + + + org.springframework.boot + spring-boot-starter-actuator + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.cloud + spring-cloud-starter-netflix-eureka-client + + + + + + org.springframework.cloud + spring-cloud-dependencies + ${spring-cloud.version} + pom + import + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + diff --git a/lab-6-api-gateway/service1/src/main/java/com/example/service1/Service1Application.java b/lab-6-api-gateway/service1/src/main/java/com/example/service1/Service1Application.java new file mode 100644 index 0000000..e89d29f --- /dev/null +++ b/lab-6-api-gateway/service1/src/main/java/com/example/service1/Service1Application.java @@ -0,0 +1,34 @@ +package com.example.service1; + +import lombok.extern.java.Log; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.netflix.eureka.EnableEurekaClient; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.util.Map; + +@SpringBootApplication +@EnableEurekaClient +@RestController +public class Service1Application { + + public static void main(String[] args) { + SpringApplication.run(Service1Application.class, args); + } + + @GetMapping("api/greeting") + public String greet(@RequestHeader Map headers, @RequestParam String name) { + System.out.println(headers); + return "Hello " + name; + } + // TODO + // 1. define a GET endpoint /api/greeting which should accept a query parameter "name" + // 2. return should be a string returning a greeting: Hello Brasov + // 3. print request headers + // 4. register the service in eureka + +} diff --git a/lab-6-api-gateway/service1/src/main/resources/application.yml b/lab-6-api-gateway/service1/src/main/resources/application.yml new file mode 100644 index 0000000..644b4ea --- /dev/null +++ b/lab-6-api-gateway/service1/src/main/resources/application.yml @@ -0,0 +1,13 @@ +server: + port: 8081 + +spring: + application: + name: service1 + +eureka: + client: + service-url: + defaultZone: http://localhost:3000/eureka + fetch-registry: true + register-with-eureka: true \ No newline at end of file diff --git a/lab-6-api-gateway/service1/src/test/java/com/example/service1/Service1ApplicationTests.java b/lab-6-api-gateway/service1/src/test/java/com/example/service1/Service1ApplicationTests.java new file mode 100644 index 0000000..539fcde --- /dev/null +++ b/lab-6-api-gateway/service1/src/test/java/com/example/service1/Service1ApplicationTests.java @@ -0,0 +1,13 @@ +package com.example.service1; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class Service1ApplicationTests { + + @Test + void contextLoads() { + } + +} diff --git a/lab-6-api-gateway/service2/.gitignore b/lab-6-api-gateway/service2/.gitignore new file mode 100644 index 0000000..549e00a --- /dev/null +++ b/lab-6-api-gateway/service2/.gitignore @@ -0,0 +1,33 @@ +HELP.md +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ diff --git a/lab-6-api-gateway/service2/pom.xml b/lab-6-api-gateway/service2/pom.xml new file mode 100644 index 0000000..b7d3748 --- /dev/null +++ b/lab-6-api-gateway/service2/pom.xml @@ -0,0 +1,53 @@ + + + 4.0.0 + + lab-6-api-gateway + com.example + 0.0.1-SNAPSHOT + + service2 + 0.0.1-SNAPSHOT + service2 + service2 + + 11 + 2021.0.1 + + + + org.springframework.boot + spring-boot-starter-actuator + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.cloud + spring-cloud-starter-netflix-eureka-client + + + + + + org.springframework.cloud + spring-cloud-dependencies + ${spring-cloud.version} + pom + import + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + diff --git a/lab-6-api-gateway/service2/src/main/java/com/example/service2/Product.java b/lab-6-api-gateway/service2/src/main/java/com/example/service2/Product.java new file mode 100644 index 0000000..a92ee16 --- /dev/null +++ b/lab-6-api-gateway/service2/src/main/java/com/example/service2/Product.java @@ -0,0 +1,13 @@ +package com.example.service2; + +import lombok.*; + +@Data +@Setter +@Getter +@AllArgsConstructor +@NoArgsConstructor +public class Product { + String name; + int quantity; +} diff --git a/lab-6-api-gateway/service2/src/main/java/com/example/service2/Service2Application.java b/lab-6-api-gateway/service2/src/main/java/com/example/service2/Service2Application.java new file mode 100644 index 0000000..5db7dbc --- /dev/null +++ b/lab-6-api-gateway/service2/src/main/java/com/example/service2/Service2Application.java @@ -0,0 +1,48 @@ +package com.example.service2; + +import com.example.service2.repository.ProductRepository; +import org.apache.http.HttpStatus; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.netflix.eureka.EnableEurekaClient; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; + +@SpringBootApplication +@EnableEurekaClient +@RestController +public class Service2Application { + + private final ProductRepository productRepository; + + public Service2Application(ProductRepository productRepository) { + this.productRepository = productRepository; + } + + public static void main(String[] args) { + SpringApplication.run(Service2Application.class, args); + } + + + @PostMapping("product") + public ResponseEntity postProduct(@RequestBody Product product) { + try { + var prod = productRepository.saveProduct(product); + return ResponseEntity.ok(prod); + } catch (RuntimeException exception) { + return ResponseEntity.status(HttpStatus.SC_BAD_REQUEST).body(exception.getMessage()); + } + } + + // TODO + // 1. define a POST endpoint /product which should accept a request body containing two properties -product name and quantity + //2. save the request body in memory + // 3. return 200 if OK + // 4. print request headers + // 5. register the service in eureka + // 6. define a GET endpoint /product to return the saved data using the POST endpoint - return type is List +} + diff --git a/lab-6-api-gateway/service2/src/main/java/com/example/service2/repository/ProductRepository.java b/lab-6-api-gateway/service2/src/main/java/com/example/service2/repository/ProductRepository.java new file mode 100644 index 0000000..f3202c2 --- /dev/null +++ b/lab-6-api-gateway/service2/src/main/java/com/example/service2/repository/ProductRepository.java @@ -0,0 +1,27 @@ +package com.example.service2.repository; + +import com.example.service2.Product; +import org.springframework.stereotype.Repository; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +@Repository +public class ProductRepository { + List products = new ArrayList<>(); + + public Optional findProductByName(String name) { + return products.stream().filter(product -> product.getName().equals(name)).findFirst(); + } + + public Product saveProduct(Product product) { + findProductByName(product.getName()).ifPresent(r -> { + throw new RuntimeException("Product with name " + r.getName() + " already exists!"); + }); + + products.add(product); + + return product; + } +} diff --git a/lab-6-api-gateway/service2/src/main/resources/application.yml b/lab-6-api-gateway/service2/src/main/resources/application.yml new file mode 100644 index 0000000..99027f1 --- /dev/null +++ b/lab-6-api-gateway/service2/src/main/resources/application.yml @@ -0,0 +1,13 @@ + +server: + port: 8082 +spring: + application: + name: service2 + +eureka: + client: + service-url: + defaultZone: http://localhost:3000/eureka + fetch-registry: true + register-with-eureka: true \ No newline at end of file diff --git a/lab-6-api-gateway/service2/src/test/java/com/example/service2/Service2ApplicationTests.java b/lab-6-api-gateway/service2/src/test/java/com/example/service2/Service2ApplicationTests.java new file mode 100644 index 0000000..3da138b --- /dev/null +++ b/lab-6-api-gateway/service2/src/test/java/com/example/service2/Service2ApplicationTests.java @@ -0,0 +1,13 @@ +package com.example.service2; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class Service2ApplicationTests { + + @Test + void contextLoads() { + } + +} diff --git a/lab-6-api-gateway/src/main/java/com/example/lab6apigateway/Lab6ApiGatewayApplication.java b/lab-6-api-gateway/src/main/java/com/example/lab6apigateway/Lab6ApiGatewayApplication.java new file mode 100644 index 0000000..eb83cff --- /dev/null +++ b/lab-6-api-gateway/src/main/java/com/example/lab6apigateway/Lab6ApiGatewayApplication.java @@ -0,0 +1,13 @@ +package com.example.lab6apigateway; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class Lab6ApiGatewayApplication { + + public static void main(String[] args) { + SpringApplication.run(Lab6ApiGatewayApplication.class, args); + } + +} diff --git a/lab-6-api-gateway/src/main/resources/application.properties b/lab-6-api-gateway/src/main/resources/application.properties new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/lab-6-api-gateway/src/main/resources/application.properties @@ -0,0 +1 @@ + diff --git a/lab-6-api-gateway/src/test/java/com/example/lab6apigateway/Lab6ApiGatewayApplicationTests.java b/lab-6-api-gateway/src/test/java/com/example/lab6apigateway/Lab6ApiGatewayApplicationTests.java new file mode 100644 index 0000000..af4ce01 --- /dev/null +++ b/lab-6-api-gateway/src/test/java/com/example/lab6apigateway/Lab6ApiGatewayApplicationTests.java @@ -0,0 +1,13 @@ +package com.example.lab6apigateway; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class Lab6ApiGatewayApplicationTests { + + @Test + void contextLoads() { + } + +}