Added task04
This commit is contained in:
parent
48fa43ea2d
commit
d586d1557c
26 changed files with 1163 additions and 0 deletions
33
task04/.gitignore
vendored
Normal file
33
task04/.gitignore
vendored
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
README.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/
|
89
task04/pom.xml
Normal file
89
task04/pom.xml
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<parent>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-parent</artifactId>
|
||||||
|
<version>3.0.5</version>
|
||||||
|
<relativePath/> <!-- lookup parent from repository -->
|
||||||
|
</parent>
|
||||||
|
<groupId>de.hststuttgart.vs</groupId>
|
||||||
|
<artifactId>task04</artifactId>
|
||||||
|
<version>0.0.1-SNAPSHOT</version>
|
||||||
|
<name>task04</name>
|
||||||
|
<description>task04</description>
|
||||||
|
<properties>
|
||||||
|
<java.version>17</java.version>
|
||||||
|
</properties>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-hateoas</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.webjars</groupId>
|
||||||
|
<artifactId>webjars-locator</artifactId>
|
||||||
|
<version>0.45</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.webjars</groupId>
|
||||||
|
<artifactId>hal-explorer</artifactId>
|
||||||
|
<version>1.2.0</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-test</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.restdocs</groupId>
|
||||||
|
<artifactId>spring-restdocs-mockmvc</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.asciidoctor</groupId>
|
||||||
|
<artifactId>asciidoctor-maven-plugin</artifactId>
|
||||||
|
<version>2.2.1</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>generate-docs</id>
|
||||||
|
<phase>prepare-package</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>process-asciidoc</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<backend>html</backend>
|
||||||
|
<doctype>book</doctype>
|
||||||
|
<sourceDirectory>src/test/resources/templates</sourceDirectory>
|
||||||
|
<sourceDocumentName>index.adoc</sourceDocumentName>
|
||||||
|
<outputDirectory>target/generated-docs</outputDirectory>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.restdocs</groupId>
|
||||||
|
<artifactId>spring-restdocs-asciidoctor</artifactId>
|
||||||
|
<version>${spring-restdocs.version}</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
</project>
|
|
@ -0,0 +1,13 @@
|
||||||
|
package de.hststuttgart.vs.task04;
|
||||||
|
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
|
||||||
|
@SpringBootApplication
|
||||||
|
public class Task04Application {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
SpringApplication.run(Task04Application.class, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
package de.hststuttgart.vs.task04.api.v1;
|
||||||
|
|
||||||
|
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.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import de.hststuttgart.vs.task04.api.v1.mapper.CreditNoteMapper;
|
||||||
|
import de.hststuttgart.vs.task04.bm.InvoiceController;
|
||||||
|
import de.hststuttgart.vs.task04.bm.exceptions.CreditNoteNotFound;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/credit-notes")
|
||||||
|
public class CreditNotesAPI {
|
||||||
|
|
||||||
|
private final InvoiceController invoiceController;
|
||||||
|
|
||||||
|
public CreditNotesAPI(final InvoiceController invoiceController) {
|
||||||
|
this.invoiceController = invoiceController;
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/{creditNoteId}")
|
||||||
|
public ResponseEntity getCreditNote(@PathVariable final String creditNoteId) {
|
||||||
|
try {
|
||||||
|
final var creditNote = invoiceController.getCreditNote(creditNoteId);
|
||||||
|
final var mappedCreditNote = CreditNoteMapper.map(creditNote);
|
||||||
|
return ResponseEntity.ok(mappedCreditNote);
|
||||||
|
} catch (CreditNoteNotFound e) {
|
||||||
|
return ResponseEntity.notFound().build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,104 @@
|
||||||
|
package de.hststuttgart.vs.task04.api.v1;
|
||||||
|
|
||||||
|
import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.linkTo;
|
||||||
|
import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.methodOn;
|
||||||
|
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
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.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import de.hststuttgart.vs.task04.api.v1.mapper.CreditNoteMapper;
|
||||||
|
import de.hststuttgart.vs.task04.api.v1.mapper.InvoiceMapper;
|
||||||
|
import de.hststuttgart.vs.task04.api.v1.models.FullCreditNoteRequest;
|
||||||
|
import de.hststuttgart.vs.task04.api.v1.models.Invoices;
|
||||||
|
import de.hststuttgart.vs.task04.bm.InvoiceController;
|
||||||
|
import de.hststuttgart.vs.task04.bm.exceptions.CreditNoteAlreadyExists;
|
||||||
|
import de.hststuttgart.vs.task04.bm.exceptions.InvalidOffset;
|
||||||
|
import de.hststuttgart.vs.task04.bm.exceptions.InvoiceNotFound;
|
||||||
|
import de.hststuttgart.vs.task04.bm.model.PagedInvoices;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/invoices")
|
||||||
|
public class InvoiceAPI {
|
||||||
|
|
||||||
|
private final InvoiceController invoiceController;
|
||||||
|
|
||||||
|
public InvoiceAPI(final InvoiceController invoiceController) {
|
||||||
|
this.invoiceController = invoiceController;
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping
|
||||||
|
public ResponseEntity getInvoices(
|
||||||
|
@RequestParam(defaultValue = "1") final int offset,
|
||||||
|
@RequestParam(defaultValue = "5") final int limit
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
final PagedInvoices pagedInvoices = invoiceController.getInvoices(offset, limit);
|
||||||
|
|
||||||
|
final var mappedInvoices =
|
||||||
|
pagedInvoices.getInvoices().stream().map(InvoiceMapper::map).collect(Collectors.toList());
|
||||||
|
|
||||||
|
final var invoices = new Invoices();
|
||||||
|
invoices.setInvoices(mappedInvoices);
|
||||||
|
// TODO 01: Add self, next and previous link
|
||||||
|
// next should only be displayed if there is a next page
|
||||||
|
// previous should only be displayed if it isn't the first page
|
||||||
|
|
||||||
|
return ResponseEntity.ok(invoices);
|
||||||
|
} catch (final InvalidOffset e) {
|
||||||
|
System.out.println("Offset was invalid");
|
||||||
|
return ResponseEntity.notFound().build();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/{invoiceId}")
|
||||||
|
public ResponseEntity getInvoice(@PathVariable("invoiceId") final String invoiceId) {
|
||||||
|
try {
|
||||||
|
final var invoice = invoiceController.getInvoice(invoiceId);
|
||||||
|
final var mappedInvoice = InvoiceMapper.map(invoice);
|
||||||
|
return ResponseEntity.ok(mappedInvoice);
|
||||||
|
} catch (final InvoiceNotFound e) {
|
||||||
|
System.out.println("Invoice " + invoiceId + " not found");
|
||||||
|
return ResponseEntity.notFound().build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/{invoiceId}/full-credit-notes")
|
||||||
|
public ResponseEntity createCreditNote(
|
||||||
|
@PathVariable("invoiceId") final String invoiceId,
|
||||||
|
@RequestBody final FullCreditNoteRequest fullCreditNoteRequest) {
|
||||||
|
try {
|
||||||
|
final var creditNote = invoiceController.createCreditNotes(invoiceId, fullCreditNoteRequest.getReason());
|
||||||
|
final var mappedCreditNote = CreditNoteMapper.map(creditNote);
|
||||||
|
return ResponseEntity.ok(mappedCreditNote);
|
||||||
|
} catch (final InvoiceNotFound e) {
|
||||||
|
System.out.println("Invoice " + invoiceId + " not found");
|
||||||
|
return ResponseEntity.notFound().build();
|
||||||
|
} catch (final CreditNoteAlreadyExists e) {
|
||||||
|
System.out.println("Credit Note already exists");
|
||||||
|
return ResponseEntity.status(409).build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/{invoiceId}/credit-notes")
|
||||||
|
public ResponseEntity getCreditNotes(@PathVariable("invoiceId") final String invoiceId) {
|
||||||
|
try {
|
||||||
|
final var creditNotes = invoiceController.getCreditNotes(invoiceId);
|
||||||
|
final var mappedCreditNotes = creditNotes.stream().map(CreditNoteMapper::map).toList();
|
||||||
|
return ResponseEntity.ok(mappedCreditNotes);
|
||||||
|
} catch (final InvoiceNotFound e) {
|
||||||
|
System.out.println("Invoice " + invoiceId + " not found");
|
||||||
|
return ResponseEntity.notFound().build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
package de.hststuttgart.vs.task04.api.v1.mapper;
|
||||||
|
|
||||||
|
import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.methodOn;
|
||||||
|
|
||||||
|
import org.springframework.hateoas.server.mvc.WebMvcLinkBuilder;
|
||||||
|
|
||||||
|
import de.hststuttgart.vs.task04.api.v1.CreditNotesAPI;
|
||||||
|
import de.hststuttgart.vs.task04.api.v1.InvoiceAPI;
|
||||||
|
import de.hststuttgart.vs.task04.api.v1.models.CreditNoteDO;
|
||||||
|
import de.hststuttgart.vs.task04.bm.model.CreditNote;
|
||||||
|
|
||||||
|
|
||||||
|
public class CreditNoteMapper {
|
||||||
|
|
||||||
|
public static CreditNoteDO map(final CreditNote creditNote) {
|
||||||
|
final var creditNoteDO = new CreditNoteDO();
|
||||||
|
|
||||||
|
creditNoteDO.setCreditNoteId(creditNote.getCreditNoteId());
|
||||||
|
creditNoteDO.setInvoiceId(creditNote.getInvoiceId());
|
||||||
|
creditNoteDO.setOrderId(creditNote.getOrderId());
|
||||||
|
creditNoteDO.setTotalNetAmount(creditNote.getTotalNetAmount());
|
||||||
|
creditNoteDO.setTotalTaxAmount(creditNote.getTotalTaxAmount());
|
||||||
|
creditNoteDO.setTotalGrossAmount(creditNote.getTotalGrossAmount());
|
||||||
|
creditNoteDO.setCustomer(creditNote.getCustomer());
|
||||||
|
creditNoteDO.setReason(creditNote.getReason());
|
||||||
|
|
||||||
|
// TODO 02: Add a self rel and one that links to the original invoice
|
||||||
|
|
||||||
|
|
||||||
|
return creditNoteDO;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
package de.hststuttgart.vs.task04.api.v1.mapper;
|
||||||
|
|
||||||
|
import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.linkTo;
|
||||||
|
import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.methodOn;
|
||||||
|
|
||||||
|
import org.springframework.hateoas.server.mvc.WebMvcLinkBuilder;
|
||||||
|
|
||||||
|
import de.hststuttgart.vs.task04.api.v1.InvoiceAPI;
|
||||||
|
import de.hststuttgart.vs.task04.api.v1.models.InvoiceDO;
|
||||||
|
import de.hststuttgart.vs.task04.bm.model.Invoice;
|
||||||
|
|
||||||
|
public class InvoiceMapper {
|
||||||
|
|
||||||
|
public static InvoiceDO map(final Invoice invoice) {
|
||||||
|
final var invoiceDO = new InvoiceDO();
|
||||||
|
|
||||||
|
invoiceDO.setInvoiceId(invoice.getInvoiceId());
|
||||||
|
invoiceDO.setOrderId(invoice.getOrderId());
|
||||||
|
invoiceDO.setTotalNetAmount(invoice.getTotalNetAmount());
|
||||||
|
invoiceDO.setTotalTaxAmount(invoice.getTotalTaxAmount());
|
||||||
|
invoiceDO.setTotalGrossAmount(invoice.getTotalGrossAmount());
|
||||||
|
invoiceDO.setCustomer(invoice.getCustomer());
|
||||||
|
|
||||||
|
// TODO 03: Add self rel and one rel to either create a credit note or return a list of credit notes
|
||||||
|
|
||||||
|
return invoiceDO;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,82 @@
|
||||||
|
package de.hststuttgart.vs.task04.api.v1.models;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
|
import org.springframework.hateoas.RepresentationModel;
|
||||||
|
|
||||||
|
public class CreditNoteDO extends RepresentationModel<CreditNoteDO> {
|
||||||
|
|
||||||
|
private String creditNoteId;
|
||||||
|
private String invoiceId;
|
||||||
|
private String orderId;
|
||||||
|
private BigDecimal totalNetAmount;
|
||||||
|
private BigDecimal totalTaxAmount;
|
||||||
|
private BigDecimal totalGrossAmount;
|
||||||
|
private Customer customer;
|
||||||
|
private String reason;
|
||||||
|
|
||||||
|
public String getCreditNoteId() {
|
||||||
|
return creditNoteId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCreditNoteId(final String creditNoteId) {
|
||||||
|
this.creditNoteId = creditNoteId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getInvoiceId() {
|
||||||
|
return invoiceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setInvoiceId(final String invoiceId) {
|
||||||
|
this.invoiceId = invoiceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getOrderId() {
|
||||||
|
return orderId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOrderId(final String orderId) {
|
||||||
|
this.orderId = orderId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BigDecimal getTotalNetAmount() {
|
||||||
|
return totalNetAmount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTotalNetAmount(final BigDecimal totalNetAmount) {
|
||||||
|
this.totalNetAmount = totalNetAmount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BigDecimal getTotalTaxAmount() {
|
||||||
|
return totalTaxAmount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTotalTaxAmount(final BigDecimal totalTaxAmount) {
|
||||||
|
this.totalTaxAmount = totalTaxAmount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BigDecimal getTotalGrossAmount() {
|
||||||
|
return totalGrossAmount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTotalGrossAmount(final BigDecimal totalGrossAmount) {
|
||||||
|
this.totalGrossAmount = totalGrossAmount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Customer getCustomer() {
|
||||||
|
return customer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCustomer(final Customer customer) {
|
||||||
|
this.customer = customer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getReason() {
|
||||||
|
return reason;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setReason(final String reason) {
|
||||||
|
this.reason = reason;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
package de.hststuttgart.vs.task04.api.v1.models;
|
||||||
|
|
||||||
|
public class Customer {
|
||||||
|
|
||||||
|
private String firstname;
|
||||||
|
private String lastname;
|
||||||
|
|
||||||
|
public String getFirstname() {
|
||||||
|
return firstname;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFirstname(final String firstname) {
|
||||||
|
this.firstname = firstname;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLastname() {
|
||||||
|
return lastname;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLastname(final String lastname) {
|
||||||
|
this.lastname = lastname;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Customer firstname(final String firstname) {
|
||||||
|
this.firstname = firstname;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Customer lastname(final String lastname) {
|
||||||
|
this.lastname = lastname;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
package de.hststuttgart.vs.task04.api.v1.models;
|
||||||
|
|
||||||
|
public class FullCreditNoteRequest {
|
||||||
|
|
||||||
|
private String reason;
|
||||||
|
|
||||||
|
public String getReason() {
|
||||||
|
return reason;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setReason(final String reason) {
|
||||||
|
this.reason = reason;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,64 @@
|
||||||
|
package de.hststuttgart.vs.task04.api.v1.models;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
|
import org.springframework.hateoas.RepresentationModel;
|
||||||
|
|
||||||
|
public class InvoiceDO extends RepresentationModel<InvoiceDO> {
|
||||||
|
|
||||||
|
private String invoiceId;
|
||||||
|
private String orderId;
|
||||||
|
private BigDecimal totalNetAmount;
|
||||||
|
private BigDecimal totalTaxAmount;
|
||||||
|
private BigDecimal totalGrossAmount;
|
||||||
|
private Customer customer;
|
||||||
|
|
||||||
|
public String getInvoiceId() {
|
||||||
|
return invoiceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setInvoiceId(final String invoiceId) {
|
||||||
|
this.invoiceId = invoiceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getOrderId() {
|
||||||
|
return orderId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOrderId(final String orderId) {
|
||||||
|
this.orderId = orderId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BigDecimal getTotalNetAmount() {
|
||||||
|
return totalNetAmount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTotalNetAmount(final BigDecimal totalNetAmount) {
|
||||||
|
this.totalNetAmount = totalNetAmount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BigDecimal getTotalTaxAmount() {
|
||||||
|
return totalTaxAmount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTotalTaxAmount(final BigDecimal totalTaxAmount) {
|
||||||
|
this.totalTaxAmount = totalTaxAmount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BigDecimal getTotalGrossAmount() {
|
||||||
|
return totalGrossAmount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTotalGrossAmount(final BigDecimal totalGrossAmount) {
|
||||||
|
this.totalGrossAmount = totalGrossAmount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Customer getCustomer() {
|
||||||
|
return customer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCustomer(final Customer customer) {
|
||||||
|
this.customer = customer;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
package de.hststuttgart.vs.task04.api.v1.models;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.springframework.hateoas.RepresentationModel;
|
||||||
|
|
||||||
|
public class Invoices extends RepresentationModel<Invoices> {
|
||||||
|
|
||||||
|
private List<InvoiceDO> invoices;
|
||||||
|
|
||||||
|
public List<InvoiceDO> getInvoices() {
|
||||||
|
return invoices;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setInvoices(final List<InvoiceDO> invoices) {
|
||||||
|
this.invoices = invoices;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
package de.hststuttgart.vs.task04.bm;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import de.hststuttgart.vs.task04.bm.exceptions.CreditNoteAlreadyExists;
|
||||||
|
import de.hststuttgart.vs.task04.bm.exceptions.CreditNoteNotFound;
|
||||||
|
import de.hststuttgart.vs.task04.bm.exceptions.InvoiceNotFound;
|
||||||
|
import de.hststuttgart.vs.task04.bm.model.CreditNote;
|
||||||
|
|
||||||
|
public interface CreditNoteRepository {
|
||||||
|
|
||||||
|
public CreditNote getCreditNote(final String creditNoteId) throws CreditNoteNotFound;
|
||||||
|
|
||||||
|
public List<CreditNote> getCreditNotes(final String invoiceId) throws InvoiceNotFound;
|
||||||
|
|
||||||
|
public CreditNote createCreditNotes(final String invoiceId, final String reason) throws InvoiceNotFound,
|
||||||
|
CreditNoteAlreadyExists;
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,119 @@
|
||||||
|
package de.hststuttgart.vs.task04.bm;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import de.hststuttgart.vs.task04.api.v1.models.Customer;
|
||||||
|
import de.hststuttgart.vs.task04.bm.exceptions.CreditNoteAlreadyExists;
|
||||||
|
import de.hststuttgart.vs.task04.bm.exceptions.CreditNoteNotFound;
|
||||||
|
import de.hststuttgart.vs.task04.bm.exceptions.InvalidOffset;
|
||||||
|
import de.hststuttgart.vs.task04.bm.exceptions.InvoiceNotFound;
|
||||||
|
import de.hststuttgart.vs.task04.bm.model.CreditNote;
|
||||||
|
import de.hststuttgart.vs.task04.bm.model.Invoice;
|
||||||
|
import de.hststuttgart.vs.task04.bm.model.PagedInvoices;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class InvoiceController implements InvoiceRepository, CreditNoteRepository {
|
||||||
|
|
||||||
|
private final List<Invoice> invoices;
|
||||||
|
private final List<CreditNote> creditNotes;
|
||||||
|
|
||||||
|
public InvoiceController() {
|
||||||
|
// fake some invoices
|
||||||
|
final var johnDoe = new Customer().firstname("John").lastname("Doe");
|
||||||
|
|
||||||
|
final var invoice = new Invoice()
|
||||||
|
.invoiceId("2023-04-16-0001")
|
||||||
|
.orderId("c082bcbf-550a-4b10-baeb-4047957f70a2")
|
||||||
|
.totalNetAmount(BigDecimal.valueOf(9.34))
|
||||||
|
.totalTaxAmount(BigDecimal.valueOf(0.65))
|
||||||
|
.totalGrossAmount(BigDecimal.valueOf(9.99))
|
||||||
|
.customer(johnDoe)
|
||||||
|
.creditNotePossible(true)
|
||||||
|
.creditNotes(new ArrayList<>());
|
||||||
|
|
||||||
|
invoices = new ArrayList<>();
|
||||||
|
invoices.add(invoice);
|
||||||
|
|
||||||
|
creditNotes = new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PagedInvoices getInvoices(final int offset, final int limit) throws InvalidOffset {
|
||||||
|
if (offset < 1 || ((offset - 1) * limit) > invoices.size()) {
|
||||||
|
throw new InvalidOffset();
|
||||||
|
}
|
||||||
|
|
||||||
|
final var invoicePage = invoices
|
||||||
|
.stream()
|
||||||
|
.skip((offset - 1) * limit)
|
||||||
|
.limit(limit)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
final var hasNext = invoices.size() > (offset * limit);
|
||||||
|
final var hasPrevious = offset > 1;
|
||||||
|
|
||||||
|
final var pagedInvoices = new PagedInvoices();
|
||||||
|
pagedInvoices.setPrevious(hasPrevious);
|
||||||
|
pagedInvoices.setNext(hasNext);
|
||||||
|
pagedInvoices.setInvoices(invoicePage);
|
||||||
|
|
||||||
|
return pagedInvoices;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Invoice getInvoice(final String invoiceId) throws InvoiceNotFound {
|
||||||
|
return invoices
|
||||||
|
.stream()
|
||||||
|
.filter(inv -> inv.getInvoiceId().equals(invoiceId))
|
||||||
|
.findFirst()
|
||||||
|
.orElseThrow(InvoiceNotFound::new);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CreditNote createCreditNotes(final String invoiceId, final String reason)
|
||||||
|
throws InvoiceNotFound, CreditNoteAlreadyExists {
|
||||||
|
final var invoice = getInvoice(invoiceId);
|
||||||
|
|
||||||
|
if (!invoice.isCreditNotePossible()) {
|
||||||
|
throw new CreditNoteAlreadyExists();
|
||||||
|
}
|
||||||
|
|
||||||
|
final var creditNote = new CreditNote();
|
||||||
|
|
||||||
|
creditNote.setCreditNoteId("CREDITNOTE-" + invoiceId);
|
||||||
|
creditNote.setInvoiceId(invoiceId);
|
||||||
|
creditNote.setOrderId(invoice.getOrderId());
|
||||||
|
creditNote.setTotalNetAmount(invoice.getTotalNetAmount());
|
||||||
|
creditNote.setTotalTaxAmount(invoice.getTotalTaxAmount());
|
||||||
|
creditNote.setTotalGrossAmount(invoice.getTotalGrossAmount());
|
||||||
|
creditNote.setCustomer(invoice.getCustomer());
|
||||||
|
creditNote.setReason(reason);
|
||||||
|
|
||||||
|
creditNotes.add(creditNote);
|
||||||
|
invoice.setCreditNotePossible(false);
|
||||||
|
invoice.setCreditNotes(List.of(creditNote));
|
||||||
|
|
||||||
|
return creditNote;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<CreditNote> getCreditNotes(final String invoiceId) throws InvoiceNotFound {
|
||||||
|
final var invoice = getInvoice(invoiceId);
|
||||||
|
return invoice.getCreditNotes();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CreditNote getCreditNote(final String creditNoteId) throws CreditNoteNotFound {
|
||||||
|
return creditNotes
|
||||||
|
.stream()
|
||||||
|
.filter(creditNote -> creditNote.getCreditNoteId().equals(creditNoteId))
|
||||||
|
.findFirst()
|
||||||
|
.orElseThrow(CreditNoteNotFound::new);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
package de.hststuttgart.vs.task04.bm;
|
||||||
|
|
||||||
|
import de.hststuttgart.vs.task04.bm.exceptions.InvalidOffset;
|
||||||
|
import de.hststuttgart.vs.task04.bm.exceptions.InvoiceNotFound;
|
||||||
|
import de.hststuttgart.vs.task04.bm.model.Invoice;
|
||||||
|
import de.hststuttgart.vs.task04.bm.model.PagedInvoices;
|
||||||
|
|
||||||
|
public interface InvoiceRepository {
|
||||||
|
|
||||||
|
public PagedInvoices getInvoices(final int offset, final int limit) throws InvalidOffset;
|
||||||
|
|
||||||
|
public Invoice getInvoice(final String invoiceId) throws InvoiceNotFound;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
package de.hststuttgart.vs.task04.bm.exceptions;
|
||||||
|
|
||||||
|
public class CreditNoteAlreadyExists extends Exception{
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
package de.hststuttgart.vs.task04.bm.exceptions;
|
||||||
|
|
||||||
|
public class CreditNoteNotFound extends Exception {
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
package de.hststuttgart.vs.task04.bm.exceptions;
|
||||||
|
|
||||||
|
public class InvalidOffset extends Exception{
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
package de.hststuttgart.vs.task04.bm.exceptions;
|
||||||
|
|
||||||
|
public class InvoiceNotFound extends Exception{
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,82 @@
|
||||||
|
package de.hststuttgart.vs.task04.bm.model;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
|
import de.hststuttgart.vs.task04.api.v1.models.Customer;
|
||||||
|
|
||||||
|
public class CreditNote {
|
||||||
|
|
||||||
|
private String creditNoteId;
|
||||||
|
private String invoiceId;
|
||||||
|
private String orderId;
|
||||||
|
private BigDecimal totalNetAmount;
|
||||||
|
private BigDecimal totalTaxAmount;
|
||||||
|
private BigDecimal totalGrossAmount;
|
||||||
|
private Customer customer;
|
||||||
|
private String reason;
|
||||||
|
|
||||||
|
public String getCreditNoteId() {
|
||||||
|
return creditNoteId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCreditNoteId(final String creditNoteId) {
|
||||||
|
this.creditNoteId = creditNoteId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getInvoiceId() {
|
||||||
|
return invoiceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setInvoiceId(final String invoiceId) {
|
||||||
|
this.invoiceId = invoiceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getOrderId() {
|
||||||
|
return orderId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOrderId(final String orderId) {
|
||||||
|
this.orderId = orderId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BigDecimal getTotalNetAmount() {
|
||||||
|
return totalNetAmount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTotalNetAmount(final BigDecimal totalNetAmount) {
|
||||||
|
this.totalNetAmount = totalNetAmount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BigDecimal getTotalTaxAmount() {
|
||||||
|
return totalTaxAmount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTotalTaxAmount(final BigDecimal totalTaxAmount) {
|
||||||
|
this.totalTaxAmount = totalTaxAmount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BigDecimal getTotalGrossAmount() {
|
||||||
|
return totalGrossAmount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTotalGrossAmount(final BigDecimal totalGrossAmount) {
|
||||||
|
this.totalGrossAmount = totalGrossAmount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Customer getCustomer() {
|
||||||
|
return customer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCustomer(final Customer customer) {
|
||||||
|
this.customer = customer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getReason() {
|
||||||
|
return reason;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setReason(final String reason) {
|
||||||
|
this.reason = reason;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,125 @@
|
||||||
|
package de.hststuttgart.vs.task04.bm.model;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import de.hststuttgart.vs.task04.api.v1.models.Customer;
|
||||||
|
|
||||||
|
public class Invoice {
|
||||||
|
|
||||||
|
private String invoiceId;
|
||||||
|
private String orderId;
|
||||||
|
private BigDecimal totalNetAmount;
|
||||||
|
private BigDecimal totalTaxAmount;
|
||||||
|
private BigDecimal totalGrossAmount;
|
||||||
|
private Customer customer;
|
||||||
|
private boolean isCreditNotePossible;
|
||||||
|
private List<CreditNote> creditNotes;
|
||||||
|
|
||||||
|
public List<CreditNote> getCreditNotes() {
|
||||||
|
return creditNotes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCreditNotes(final List<CreditNote> creditNotes) {
|
||||||
|
this.creditNotes = creditNotes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isCreditNotePossible() {
|
||||||
|
return isCreditNotePossible;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCreditNotePossible(final boolean creditNotePossible) {
|
||||||
|
isCreditNotePossible = creditNotePossible;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getInvoiceId() {
|
||||||
|
return invoiceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setInvoiceId(final String invoiceId) {
|
||||||
|
this.invoiceId = invoiceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getOrderId() {
|
||||||
|
return orderId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOrderId(final String orderId) {
|
||||||
|
this.orderId = orderId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BigDecimal getTotalNetAmount() {
|
||||||
|
return totalNetAmount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTotalNetAmount(final BigDecimal totalNetAmount) {
|
||||||
|
this.totalNetAmount = totalNetAmount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BigDecimal getTotalTaxAmount() {
|
||||||
|
return totalTaxAmount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTotalTaxAmount(final BigDecimal totalTaxAmount) {
|
||||||
|
this.totalTaxAmount = totalTaxAmount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BigDecimal getTotalGrossAmount() {
|
||||||
|
return totalGrossAmount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTotalGrossAmount(final BigDecimal totalGrossAmount) {
|
||||||
|
this.totalGrossAmount = totalGrossAmount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Customer getCustomer() {
|
||||||
|
return customer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCustomer(final Customer customer) {
|
||||||
|
this.customer = customer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Invoice invoiceId(final String invoiceId) {
|
||||||
|
this.invoiceId = invoiceId;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Invoice orderId(final String orderId) {
|
||||||
|
this.orderId = orderId;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Invoice totalNetAmount(final BigDecimal totalNetAmount) {
|
||||||
|
this.totalNetAmount = totalNetAmount;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Invoice totalTaxAmount(final BigDecimal totalTaxAmount) {
|
||||||
|
this.totalTaxAmount = totalTaxAmount;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Invoice totalGrossAmount(final BigDecimal totalGrossAmount) {
|
||||||
|
this.totalGrossAmount = totalGrossAmount;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Invoice customer(final Customer customer) {
|
||||||
|
this.customer = customer;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Invoice creditNotePossible(final boolean creditNotePossible) {
|
||||||
|
this.isCreditNotePossible = creditNotePossible;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Invoice creditNotes(final List<CreditNote> creditNotes) {
|
||||||
|
this.creditNotes = creditNotes;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
package de.hststuttgart.vs.task04.bm.model;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class PagedInvoices {
|
||||||
|
|
||||||
|
private boolean previous;
|
||||||
|
private boolean next;
|
||||||
|
|
||||||
|
private List<Invoice> invoices;
|
||||||
|
|
||||||
|
public boolean hasPrevious() {
|
||||||
|
return previous;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPrevious(final boolean previous) {
|
||||||
|
this.previous = previous;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasNext() {
|
||||||
|
return next;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNext(final boolean next) {
|
||||||
|
this.next = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Invoice> getInvoices() {
|
||||||
|
return invoices;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setInvoices(final List<Invoice> invoices) {
|
||||||
|
this.invoices = invoices;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
package de.hststuttgart.vs.task04;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
|
||||||
|
@SpringBootTest
|
||||||
|
class Task04ApplicationTests {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void contextLoads() {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,152 @@
|
||||||
|
package de.hststuttgart.vs.task04.api.v1;
|
||||||
|
|
||||||
|
import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.halLinks;
|
||||||
|
|
||||||
|
import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.linkWithRel;
|
||||||
|
import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.links;
|
||||||
|
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
|
||||||
|
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration;
|
||||||
|
import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessRequest;
|
||||||
|
import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessResponse;
|
||||||
|
import static org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint;
|
||||||
|
import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
|
||||||
|
import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields;
|
||||||
|
import static org.springframework.restdocs.payload.PayloadDocumentation.subsectionWithPath;
|
||||||
|
import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName;
|
||||||
|
import static org.springframework.restdocs.request.RequestDocumentation.queryParameters;
|
||||||
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
|
||||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.DisplayNameGeneration;
|
||||||
|
import org.junit.jupiter.api.DisplayNameGenerator;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
import org.mockito.Mockito;
|
||||||
|
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
|
||||||
|
import org.springframework.boot.test.mock.mockito.MockBean;
|
||||||
|
import org.springframework.restdocs.RestDocumentationContextProvider;
|
||||||
|
import org.springframework.restdocs.RestDocumentationExtension;
|
||||||
|
import org.springframework.restdocs.mockmvc.RestDocumentationResultHandler;
|
||||||
|
import org.springframework.test.web.servlet.MockMvc;
|
||||||
|
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||||
|
import org.springframework.web.context.WebApplicationContext;
|
||||||
|
|
||||||
|
import de.hststuttgart.vs.task04.api.v1.models.Customer;
|
||||||
|
import de.hststuttgart.vs.task04.bm.InvoiceController;
|
||||||
|
import de.hststuttgart.vs.task04.bm.model.Invoice;
|
||||||
|
import de.hststuttgart.vs.task04.bm.model.PagedInvoices;
|
||||||
|
|
||||||
|
@ExtendWith(RestDocumentationExtension.class)
|
||||||
|
@WebMvcTest(InvoiceAPI.class)
|
||||||
|
@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class)
|
||||||
|
class InvoiceAPITest {
|
||||||
|
|
||||||
|
private MockMvc mockMvc;
|
||||||
|
|
||||||
|
@MockBean
|
||||||
|
private InvoiceController invoiceController;
|
||||||
|
|
||||||
|
private RestDocumentationResultHandler documentationHandler;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setUp(final WebApplicationContext webApplicationContext,
|
||||||
|
final RestDocumentationContextProvider restDocumentationContextProvider) {
|
||||||
|
documentationHandler =
|
||||||
|
document("{method-name}", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint()));
|
||||||
|
|
||||||
|
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext)
|
||||||
|
.apply(documentationConfiguration(restDocumentationContextProvider).uris()
|
||||||
|
.withScheme("https")
|
||||||
|
.withPort(443))
|
||||||
|
.alwaysDo(documentationHandler)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void should_return_invoices() throws Exception {
|
||||||
|
// We mock the Object so that we can test it easier
|
||||||
|
final var pagedInvoices = new PagedInvoices();
|
||||||
|
pagedInvoices.setInvoices(List.of());
|
||||||
|
pagedInvoices.setNext(false);
|
||||||
|
pagedInvoices.setPrevious(false);
|
||||||
|
|
||||||
|
Mockito.doReturn(pagedInvoices).when(invoiceController).getInvoices(Mockito.anyInt(), Mockito.anyInt());
|
||||||
|
|
||||||
|
this.mockMvc.perform(get("/invoices"))
|
||||||
|
.andDo(print())
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andDo(documentationHandler.document(
|
||||||
|
links(
|
||||||
|
halLinks(),
|
||||||
|
linkWithRel("self").description("Link to this resource")
|
||||||
|
),
|
||||||
|
responseFields(
|
||||||
|
fieldWithPath("invoices").description("Array that contains all invoices"),
|
||||||
|
subsectionWithPath("_links").description("Link to resources")
|
||||||
|
)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO 04: test /invoices which should return a list with a next and previous link
|
||||||
|
@Test
|
||||||
|
void should_return_invoices_with_links() throws Exception {
|
||||||
|
// We mock the Object so that we can test it easier
|
||||||
|
final var johnDoe = new Customer().firstname("John").lastname("Doe");
|
||||||
|
|
||||||
|
final var invoice = new Invoice()
|
||||||
|
.invoiceId("2023-04-16-0001")
|
||||||
|
.orderId("c082bcbf-550a-4b10-baeb-4047957f70a2")
|
||||||
|
.totalNetAmount(BigDecimal.valueOf(9.34))
|
||||||
|
.totalTaxAmount(BigDecimal.valueOf(0.65))
|
||||||
|
.totalGrossAmount(BigDecimal.valueOf(9.99))
|
||||||
|
.customer(johnDoe)
|
||||||
|
.creditNotePossible(true)
|
||||||
|
.creditNotes(new ArrayList<>());
|
||||||
|
|
||||||
|
final var pagedInvoices = new PagedInvoices();
|
||||||
|
pagedInvoices.setInvoices(List.of(invoice));
|
||||||
|
pagedInvoices.setNext(true);
|
||||||
|
pagedInvoices.setPrevious(true);
|
||||||
|
|
||||||
|
Mockito.doReturn(pagedInvoices).when(invoiceController).getInvoices(Mockito.anyInt(), Mockito.anyInt());
|
||||||
|
|
||||||
|
this.mockMvc.perform(get("/invoices")
|
||||||
|
.queryParam("offset", "2")
|
||||||
|
.queryParam("limit", "1"))
|
||||||
|
.andDo(print())
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andDo(documentationHandler.document(
|
||||||
|
links(
|
||||||
|
halLinks(),
|
||||||
|
linkWithRel("self").description("Link to this resource"),
|
||||||
|
linkWithRel("next").description("Link to next page"),
|
||||||
|
linkWithRel("previous").description("Link to previous page")
|
||||||
|
),
|
||||||
|
queryParameters(
|
||||||
|
parameterWithName("offset").description("Indicates the page. Starts with 1").optional(),
|
||||||
|
parameterWithName("limit").description("Limits the amount of invoices per page").optional()
|
||||||
|
),
|
||||||
|
responseFields(
|
||||||
|
fieldWithPath("invoices").description("Array that contains all invoices"),
|
||||||
|
subsectionWithPath("_links").description("Link to resources")
|
||||||
|
)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO 05 add a test for /invoices/[invoiceId}
|
||||||
|
// The endpoint should return an invoice that can be refunded (credit note creation is possible)
|
||||||
|
|
||||||
|
// TODO 06 add a test for /invoices/[invoiceId}
|
||||||
|
// The endpoint should return an invoice that can't be refunded (credit note already exists)
|
||||||
|
|
||||||
|
// TODO 07 add a test for /invoices/[invoiceId}
|
||||||
|
// The endpoint should return no invoce (not found)
|
||||||
|
|
||||||
|
|
||||||
|
}
|
24
task04/src/test/resources/templates/all_invoices.adoc
Normal file
24
task04/src/test/resources/templates/all_invoices.adoc
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
This endpoint returns all invoices. There is no way to change the order of the elements.
|
||||||
|
It uses pagination by default. The default values are `1` for `offset` and `5` for `limit`. This gives you the first page which contains 5 invoices.
|
||||||
|
|
||||||
|
To navigate through the invoices you can use the provided HATEOAS links.
|
||||||
|
|
||||||
|
=== Request structure
|
||||||
|
|
||||||
|
include::{snippets}/should_return_invoices/http-request.adoc[]
|
||||||
|
|
||||||
|
=== Response fields
|
||||||
|
|
||||||
|
include::{snippets}/should_return_invoices/response-fields.adoc[]
|
||||||
|
|
||||||
|
=== Response links
|
||||||
|
|
||||||
|
include::{snippets}/should_return_invoices/links.adoc[]
|
||||||
|
|
||||||
|
=== Example response
|
||||||
|
|
||||||
|
include::{snippets}/should_return_invoices/http-response.adoc[]
|
||||||
|
|
||||||
|
=== CURL request
|
||||||
|
|
||||||
|
include::{snippets}/should_return_invoices/curl-request.adoc[]
|
9
task04/src/test/resources/templates/index.adoc
Normal file
9
task04/src/test/resources/templates/index.adoc
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
= Invoice Manager
|
||||||
|
:doctype: book
|
||||||
|
:icons: font
|
||||||
|
:source-highlighter: highlightjs
|
||||||
|
:toc: left
|
||||||
|
:toclevels: 4
|
||||||
|
:sectlinks:
|
||||||
|
|
||||||
|
include::all_invoices.adoc[]
|
Loading…
Reference in a new issue