2022-02 - 33

This commit is contained in:
ydvorzhetskiy
2022-07-02 13:47:41 +06:00
parent c7b41fe7a3
commit a74875b635
26 changed files with 779 additions and 0 deletions
+45
View File
@@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>ru.otus</groupId>
<artifactId>manual-hateoas</artifactId>
<version>1.0</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.6</version>
<relativePath/>
</parent>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-hateoas</artifactId>
</dependency>
</dependencies>
</project>
@@ -0,0 +1,27 @@
package ru.otus.spring.microservice;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import ru.otus.spring.microservice.domain.Person;
import ru.otus.spring.microservice.repostory.PersonRepository;
import javax.annotation.PostConstruct;
@SpringBootApplication
public class App {
@Autowired
private PersonRepository repository;
public static void main(String[] args) {
SpringApplication.run(App.class);
}
@PostConstruct
public void init() {
for(int i = 0 ; i < 18; ++i) {
repository.save(new Person("Пёрсона №" + i));
}
}
}
@@ -0,0 +1,37 @@
package ru.otus.spring.microservice.domain;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
@Entity
public class Person {
@Id
@GeneratedValue
private int id;
private String name;
public Person() {
}
public Person(String name) {
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
@@ -0,0 +1,7 @@
package ru.otus.spring.microservice.repostory;
import org.springframework.data.jpa.repository.JpaRepository;
import ru.otus.spring.microservice.domain.Person;
public interface PersonRepository extends JpaRepository<Person, Integer> {
}
@@ -0,0 +1,19 @@
package ru.otus.spring.microservice.rest;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.linkTo;
import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.methodOn;
@RestController
public class EntrypointRestController {
@GetMapping("api/entrypoint")
public ResponseEntity<?> getApiEntrypoint() {
var link = linkTo(methodOn(PersonsRestController.class).findAllPersons()).withRel("all");
return new ResponseEntity<>(link, HttpStatus.OK);
}
}
@@ -0,0 +1,67 @@
package ru.otus.spring.microservice.rest;
import org.springframework.hateoas.RepresentationModel;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import ru.otus.spring.microservice.domain.Person;
import ru.otus.spring.microservice.repostory.PersonRepository;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.linkTo;
import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.methodOn;
@RestController
public class PersonsRestController {
private final PersonRepository repository;
public PersonsRestController(PersonRepository repository) {
this.repository = repository;
}
@GetMapping("api/persons")
public ResponseEntity<?> findAllPersons() {
List<Person> persons = repository.findAll();
List<PersonResource> resources = persons.stream().map(this::person2Resource)
.collect(Collectors.toList());
return new ResponseEntity<>(resources, HttpStatus.OK);
}
@GetMapping("api/persons/{id}")
public ResponseEntity<?> findById(@PathVariable("id") int id) {
Optional<Person> person = repository.findById(id);
return new ResponseEntity<>(person2Resource(person.orElseThrow()), HttpStatus.OK);
}
private PersonResource person2Resource(Person person) {
var resource = new PersonResource(person);
resource.add(linkTo(methodOn(PersonsRestController.class).findById(person.getId())).withSelfRel());
resource.add(linkTo(methodOn(PersonsRestController.class).findAllPersons()).withRel("all"));
return resource;
}
private static class PersonResource extends RepresentationModel<PersonResource> {
private final Person person;
public PersonResource(Person person) {
this.person = person;
}
public int getId() {
return person.getId();
}
public String getName() {
return person.getName();
}
}
}
@@ -0,0 +1,67 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Пёрсоны</title>
<script>
function insertPersonsTableRow(table, cellValue1, cellValue2, cellValue3) {
const newRow = table.insertRow(-1);
let newCell = newRow.insertCell(0);
newCell.innerHTML = cellValue1
newCell = newRow.insertCell(1);
newCell.innerHTML = cellValue2
if (cellValue3) {
newCell = newRow.insertCell(2);
newCell.innerHTML = cellValue3
}
}
function preparePersonView(selfHref, allHref) {
const content = document.getElementById("content");
content.innerHTML = "";
fetch(selfHref)
.then(data => data.json())
.then(person => {
const table = document.createElement("table")
insertPersonsTableRow(table, "Id", "Name")
insertPersonsTableRow(table, person.id, person.name)
const backButton = document.createElement("button")
content.appendChild(table)
content.innerHTML += `<button onclick="preparePersonsListView('${allHref}')">Назад</button>`
})
}
function preparePersonsListView(entryPointHref) {
const content = document.getElementById("content")
content.innerHTML = ""
fetch(entryPointHref)
.then(data => data.json())
.then(persons => {
const table = document.createElement("table")
insertPersonsTableRow(table, "Id", "Name", "Action")
content.appendChild(table)
persons.forEach(person => {
insertPersonsTableRow(table, person.id, person.name,
`<button onclick="preparePersonView('${person.links[0].href}', '${person.links[1].href}')">Просмотр</button>`)
})
})
}
function getEntryPointHref() {
fetch("api/entrypoint")
.then(data => data.json())
.then(data => preparePersonsListView(data.href))
}
getEntryPointHref();
</script>
</head>
<body>
<div id="content"></div>
</body>
</html>
+40
View File
@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>ru.otus</groupId>
<artifactId>plain-api</artifactId>
<version>1.0</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.6</version>
<relativePath/>
</parent>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
</dependencies>
</project>
@@ -0,0 +1,27 @@
package ru.otus.spring.microservice;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import ru.otus.spring.microservice.domain.Person;
import ru.otus.spring.microservice.repostory.PersonRepository;
import javax.annotation.PostConstruct;
@SpringBootApplication
public class App {
@Autowired
private PersonRepository repository;
public static void main(String[] args) {
SpringApplication.run(App.class);
}
@PostConstruct
public void init() {
for(int i = 0 ; i < 18; ++i) {
repository.save(new Person("Пёрсона №" + i));
}
}
}
@@ -0,0 +1,39 @@
package ru.otus.spring.microservice.domain;
import com.fasterxml.jackson.annotation.JsonProperty;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
@Entity
public class Person {
@Id
@GeneratedValue
private int id;
private String name;
public Person() {
}
public Person(String name) {
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
@@ -0,0 +1,7 @@
package ru.otus.spring.microservice.repostory;
import org.springframework.data.jpa.repository.JpaRepository;
import ru.otus.spring.microservice.domain.Person;
public interface PersonRepository extends JpaRepository<Person, Integer> {
}
@@ -0,0 +1,34 @@
package ru.otus.spring.microservice.rest;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import ru.otus.spring.microservice.domain.Person;
import ru.otus.spring.microservice.repostory.PersonRepository;
import java.util.List;
@RestController
public class PersonsRestController {
private final PersonRepository repository;
public PersonsRestController(PersonRepository repository) {
this.repository = repository;
}
@GetMapping("api/persons")
public ResponseEntity<?> findAllPersons() {
List<Person> persons = repository.findAll();
return new ResponseEntity<>(persons, HttpStatus.OK);
}
@GetMapping("api/persons/{id}")
public ResponseEntity<?> findById(@PathVariable("id") int id) {
Person person = repository.findById(id).orElseThrow();
return new ResponseEntity<>(person, HttpStatus.OK);
}
}
@@ -0,0 +1,62 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Пёрсоны</title>
<script>
function insertPersonsTableRow(table, cellValue1, cellValue2, cellValue3) {
const newRow = table.insertRow(-1);
let newCell = newRow.insertCell(0);
newCell.innerHTML = cellValue1
newCell = newRow.insertCell(1);
newCell.innerHTML = cellValue2
if (cellValue3) {
newCell = newRow.insertCell(2);
newCell.innerHTML = cellValue3
}
}
function preparePersonView(id) {
const content = document.getElementById("content");
content.innerHTML = "";
fetch(`/api/persons/${id}`)
.then(data => data.json())
.then(person => {
const table = document.createElement("table")
insertPersonsTableRow(table, "Id", "Name")
insertPersonsTableRow(table, person.id, person.name)
const backButton = document.createElement("button")
backButton.innerHTML = "Назад"
backButton.addEventListener("click", preparePersonsListView, false)
content.appendChild(table)
content.appendChild(backButton)
})
}
function preparePersonsListView() {
const content = document.getElementById("content")
content.innerHTML = ""
fetch("/api/persons/")
.then(data => data.json())
.then(persons => {
const table = document.createElement("table")
insertPersonsTableRow(table, "Id", "Name", "Action")
content.appendChild(table)
persons.forEach(person => {
insertPersonsTableRow(table, person.id, person.name,
`<button onclick="preparePersonView(${person.id})">Просмотр</button>`)
})
})
}
</script>
</head>
<body onload="preparePersonsListView()">
<div id="content"></div>
</body>
</html>
+18
View File
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>ru.otus</groupId>
<artifactId>spring-data-rest-demo</artifactId>
<version>1.0</version>
<packaging>pom</packaging>
<modules>
<module>plain-api</module>
<module>manual-hateoas</module>
<module>spring-data-rest</module>
</modules>
</project>
@@ -0,0 +1,51 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>ru.otus</groupId>
<artifactId>spring-data-rest</artifactId>
<version>1.0</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.6</version>
<relativePath/>
</parent>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-rest-hal-explorer</artifactId>
</dependency>
</dependencies>
</project>
@@ -0,0 +1,27 @@
package ru.otus.spring.microservice;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import ru.otus.spring.microservice.domain.Person;
import ru.otus.spring.microservice.repostory.PersonRepository;
import javax.annotation.PostConstruct;
@SpringBootApplication
public class App {
@Autowired
private PersonRepository repository;
public static void main(String[] args) {
SpringApplication.run(App.class);
}
@PostConstruct
public void init() {
for(int i = 0 ; i < 18; ++i) {
repository.save(new Person("Пёрсона №" + i));
}
}
}
@@ -0,0 +1,20 @@
package ru.otus.spring.microservice.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.rest.core.config.RepositoryRestConfiguration;
import org.springframework.data.rest.webmvc.config.RepositoryRestConfigurer;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import ru.otus.spring.microservice.projections.PersonLowerNameProjection;
import ru.otus.spring.microservice.projections.PersonUpperNameProjection;
//@Configuration
public class RestConfig implements RepositoryRestConfigurer {
@Override
public void configureRepositoryRestConfiguration(RepositoryRestConfiguration repositoryRestConfiguration,
CorsRegistry cors) {
repositoryRestConfiguration.getProjectionConfiguration()
.addProjection(PersonUpperNameProjection.class)
.addProjection(PersonLowerNameProjection.class);
}
}
@@ -0,0 +1,41 @@
package ru.otus.spring.microservice.domain;
import ru.otus.spring.microservice.listeners.PersonEntityEventListener;
import javax.persistence.Entity;
import javax.persistence.EntityListeners;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
@Entity
@EntityListeners(PersonEntityEventListener.class)
public class Person {
@Id
@GeneratedValue
private int id;
private String name;
public Person() {
}
public Person(String name) {
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
@@ -0,0 +1,12 @@
package ru.otus.spring.microservice.listeners;
import ru.otus.spring.microservice.domain.Person;
import javax.persistence.PrePersist;
public class PersonEntityEventListener {
@PrePersist
public void prePersist(Person p) {
p.setName(p.getName() + " (Человек и пароход)");
}
}
@@ -0,0 +1,14 @@
package ru.otus.spring.microservice.projections;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.rest.core.config.Projection;
import ru.otus.spring.microservice.domain.Person;
@Projection(name = "withlowername", types = Person.class)
public interface PersonLowerNameProjection {
String getName();
@Value("#{target.name.toLowerCase()}")
String getNameLowerCase();
}
@@ -0,0 +1,14 @@
package ru.otus.spring.microservice.projections;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.rest.core.config.Projection;
import ru.otus.spring.microservice.domain.Person;
@Projection(name = "withuppername", types = Person.class)
public interface PersonUpperNameProjection {
String getName();
@Value("#{target.name.toUpperCase()}")
String getNameUpperCase();
}
@@ -0,0 +1,11 @@
package ru.otus.spring.microservice.repostory;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;
import ru.otus.spring.microservice.domain.Person;
import java.util.List;
@RepositoryRestResource(path = "persons", collectionResourceRel = "persons")
public interface PersonRepository extends JpaRepository<Person, Integer> {
}
@@ -0,0 +1,4 @@
spring:
data:
rest:
basePath: /api
@@ -0,0 +1,89 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Пёрсоны</title>
<script>
function insertPersonsTableRow(table, cellValue1, cellValue2) {
const newRow = table.insertRow(-1);
let newCell = newRow.insertCell(0);
newCell.innerHTML = cellValue1
if (cellValue2) {
newCell = newRow.insertCell(1);
newCell.innerHTML = cellValue2
}
}
function preparePersonView(selfHref, allHref) {
const content = document.getElementById("content");
content.innerHTML = "";
fetch(selfHref)
.then(data => data.json())
.then(person => {
const table = document.createElement("table")
insertPersonsTableRow(table, "Name")
insertPersonsTableRow(table, person.name)
const backButton = document.createElement("button")
content.appendChild(table)
content.innerHTML += `<button onclick="preparePersonsListView('${allHref}')">Назад</button>`
})
}
function preparePersonsListView(entryPointHref) {
const content = document.getElementById("content")
content.innerHTML = ""
fetch(entryPointHref + "?size=100500")
.then(data => data.json())
.then(data => data._embedded.persons)
.then(persons => {
const table = document.createElement("table")
insertPersonsTableRow(table, "Name", "Action")
content.appendChild(table)
persons.forEach(person => {
insertPersonsTableRow(table, person.name,
`<button onclick="preparePersonView('${person._links.self.href}', '${entryPointHref}')">Просмотр</button>`)
})
})
}
function getEntryPointHref() {
return fetch("api/")
.then(data => data.json())
.then(data => data._links.persons)
.then(data => data.href.substr(0, data.href.indexOf("{")))
}
function addPerson(entryPointHref) {
const newPersonName = document.getElementById("newPersonName").value
fetch(entryPointHref, {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({"name": newPersonName})
})
.then(res => preparePersonsListView(entryPointHref))
}
function addPersonButtonClick() {
getEntryPointHref().then(entryPointHref => addPerson(entryPointHref))
}
getEntryPointHref().then(entryPointHref => preparePersonsListView(entryPointHref));
</script>
</head>
<body>
<div id="content"></div>
<br>
<br>
<div>
<input type="text" id="newPersonName" value="Иван Фёдорович Крузенштерн" readonly style="width: 300px">
<input type="button" onclick="addPersonButtonClick()" value="Добавить нового пёрсона">
</div>
</body>
</html>