chore: Initial import of FLEX training material
This commit is contained in:
parent
c01246d4f7
commit
12235acc42
1020 changed files with 53940 additions and 0 deletions
|
|
@ -0,0 +1,4 @@
|
|||
target/
|
||||
!.mvn/wrapper/maven-wrapper.jar
|
||||
!**/src/main/**/target/
|
||||
!**/src/test/**/target/
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
<?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>
|
||||
|
||||
<parent>
|
||||
<groupId>de.accso</groupId>
|
||||
<artifactId>flexinale-distributed</artifactId>
|
||||
<version>2024.3.0</version>
|
||||
<relativePath>../pom.xml</relativePath> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
|
||||
<artifactId>flexinale-distributed-besucherportal_api_contract</artifactId>
|
||||
<version>2024.3.0</version>
|
||||
<name>Flexinale Distributed Besucherportal API Contract</name>
|
||||
<description>Flexinale - FLEX case-study "film festival", distributed services, besucherportal_api-contract</description>
|
||||
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
<version>${commons-lang3.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>de.accso</groupId>
|
||||
<artifactId>flexinale-distributed-common</artifactId>
|
||||
<version>2024.3.0</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>com.github.spotbugs</groupId>
|
||||
<artifactId>spotbugs-maven-plugin</artifactId>
|
||||
<version>${spotbugs-maven-plugin.version}</version>
|
||||
<configuration>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>com.h3xstream.findsecbugs</groupId>
|
||||
<artifactId>findsecbugs-plugin</artifactId>
|
||||
<version>${findsecbugs-maven-plugin.version}</version>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</configuration>
|
||||
<dependencies>
|
||||
<!-- overwrite dependency on spotbugs if you want to specify the version of spotbugs -->
|
||||
<dependency>
|
||||
<groupId>com.github.spotbugs</groupId>
|
||||
<artifactId>spotbugs</artifactId>
|
||||
<version>${spotbugs.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>verify</phase>
|
||||
<goals>
|
||||
<goal>check</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
package de.accso.flexinale.besucherportal.api_contract.event;
|
||||
|
||||
import de.accso.flexinale.common.api.event.Event;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
public final class AllBesucherportalEvents {
|
||||
|
||||
public static final Set<Class<? extends Event>> eventTypes =
|
||||
Set.of(
|
||||
GutscheinEinloesenBeauftragtEvent.class
|
||||
);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
package de.accso.flexinale.besucherportal.api_contract.event;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonGetter;
|
||||
import de.accso.flexinale.besucherportal.api_contract.event.model.GutscheinEinloesenAuftragTO;
|
||||
import de.accso.flexinale.common.api.event.AbstractEvent;
|
||||
import de.accso.flexinale.common.api.event.Event;
|
||||
import de.accso.flexinale.common.shared_kernel.DoNotCheckInArchitectureTests;
|
||||
import de.accso.flexinale.common.shared_kernel.Versionable;
|
||||
import org.apache.commons.lang3.builder.EqualsBuilder;
|
||||
import org.apache.commons.lang3.builder.HashCodeBuilder;
|
||||
|
||||
@SuppressWarnings({"unused", "CanBeFinal"})
|
||||
public class GutscheinEinloesenBeauftragtEvent extends AbstractEvent implements Event {
|
||||
private static Version version = Versionable.initialVersion().inc();
|
||||
|
||||
@DoNotCheckInArchitectureTests
|
||||
public GutscheinEinloesenAuftragTO gutscheinEinloesenAuftrag;
|
||||
|
||||
private GutscheinEinloesenBeauftragtEvent() {} // needed for (de)serialization via Jackson
|
||||
|
||||
public GutscheinEinloesenBeauftragtEvent(final GutscheinEinloesenAuftragTO gutscheinEinloesenAuftrag) {
|
||||
this.gutscheinEinloesenAuftrag = gutscheinEinloesenAuftrag;
|
||||
}
|
||||
|
||||
@Override
|
||||
@JsonGetter("version") // needed as otherwise the static field version is not (de)serialized
|
||||
public Version version() {
|
||||
return version;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) {return true;}
|
||||
|
||||
if (o == null || getClass() != o.getClass()) {return false;}
|
||||
|
||||
final GutscheinEinloesenBeauftragtEvent that = (GutscheinEinloesenBeauftragtEvent) o;
|
||||
|
||||
return new EqualsBuilder().appendSuper(super.equals(o))
|
||||
.append(this.version(), that.version())
|
||||
.append(gutscheinEinloesenAuftrag, that.gutscheinEinloesenAuftrag)
|
||||
.isEquals();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return new HashCodeBuilder(17, 37)
|
||||
.appendSuper(super.hashCode())
|
||||
.append(this.version())
|
||||
.append(gutscheinEinloesenAuftrag)
|
||||
.toHashCode();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
package de.accso.flexinale.besucherportal.api_contract.event.model;
|
||||
|
||||
import de.accso.flexinale.common.shared_kernel.*;
|
||||
import org.apache.commons.lang3.builder.EqualsBuilder;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public record GutscheinEinloesenAuftragTO(Id id, Version version,
|
||||
Id filmId, Id vorfuehrungId, Id besucherId,
|
||||
AnzahlTickets anzahlTickets)
|
||||
implements Identifiable, Versionable, EqualsByContent, Serializable
|
||||
{
|
||||
public record AnzahlTickets(Integer raw) implements RawWrapper<Integer> {}
|
||||
|
||||
public enum VerkaufsKanal {
|
||||
ONLINE,
|
||||
ZENTRAL,
|
||||
KINOKASSE
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equalsByContent(final Object o) {
|
||||
if (this == o) {return true;}
|
||||
|
||||
if (o == null || getClass() != o.getClass()) {return false;}
|
||||
|
||||
GutscheinEinloesenAuftragTO that = (GutscheinEinloesenAuftragTO) o;
|
||||
|
||||
return new EqualsBuilder()
|
||||
.append(id, that.id)
|
||||
.append(filmId, that.filmId)
|
||||
.append(vorfuehrungId, that.vorfuehrungId)
|
||||
.append(besucherId, that.besucherId)
|
||||
.append(anzahlTickets, that.anzahlTickets)
|
||||
.isEquals();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
<configuration>
|
||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>%d{YYYYMMdd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<logger name="org.springframework.orm.jpa" level="INFO"/>
|
||||
<logger name="org.springframework.boot.autoconfigure.domain.EntityScan" level="INFO"/>
|
||||
|
||||
<logger name="org.apache.kafka" level="WARN"/>
|
||||
<logger name="org.apache.kafka.clients.admin.AdminClient" level="INFO"/>
|
||||
<logger name="org.apache.kafka.clients.consumer.ConsumerConfig" level="INFO"/>
|
||||
<logger name="org.apache.kafka.clients.producer.ProducerConfig" level="INFO"/>
|
||||
|
||||
<logger name="de.accso" level="INFO"/>
|
||||
<root level="WARN">
|
||||
<appender-ref ref="STDOUT" />
|
||||
</root>
|
||||
</configuration>
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
package de.accso.flexinale.besucherportal.api_contract.event;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import de.accso.flexinale.besucherportal.api_contract.event.model.GutscheinEinloesenAuftragTO;
|
||||
import de.accso.flexinale.common.api.event.Event;
|
||||
import de.accso.flexinale.common.infrastructure.eventbus.EventSerializationHelper;
|
||||
import de.accso.flexinale.common.shared_kernel.Identifiable;
|
||||
import de.accso.flexinale.common.shared_kernel.RawWrapper;
|
||||
import de.accso.flexinale.common.shared_kernel.Versionable;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static de.accso.flexinale.besucherportal.api_contract.event.ReflectionHelper.setField;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class EventSerializationTest {
|
||||
private static Stream<Arguments> allGutscheinEventTypes() {
|
||||
return Set.of(GutscheinEinloesenBeauftragtEvent.class).stream().map(Arguments::of);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("allGutscheinEventTypes")
|
||||
<E extends Event> void testSerializeGutscheinEventClass2JsonString(final Class<E> gutscheinEventType)
|
||||
throws JsonProcessingException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchMethodException {
|
||||
// arrange
|
||||
Constructor<E> constructor = gutscheinEventType.getDeclaredConstructor();
|
||||
constructor.setAccessible(true);
|
||||
Event event = constructor.newInstance();
|
||||
|
||||
GutscheinEinloesenAuftragTO gutscheinEinloesenAuftragTO = createGutscheinTO();
|
||||
setField(gutscheinEventType, event, "gutscheinEinloesenAuftrag", gutscheinEinloesenAuftragTO, true);
|
||||
|
||||
// act
|
||||
String jsonString = EventSerializationHelper.serializeEvent2JsonString(event);
|
||||
|
||||
// assert
|
||||
assertThat(jsonString).contains(getExpectedJsonStringForVersion(event.version()));
|
||||
assertThat(jsonString).contains(getExpectedJsonStringForRawTypeInteger("anzahlTickets", gutscheinEinloesenAuftragTO.anzahlTickets()));
|
||||
}
|
||||
|
||||
private static GutscheinEinloesenAuftragTO createGutscheinTO() {
|
||||
GutscheinEinloesenAuftragTO.AnzahlTickets anzahlTickets = new GutscheinEinloesenAuftragTO.AnzahlTickets(42);
|
||||
return new GutscheinEinloesenAuftragTO(Identifiable.Id.of(), Versionable.unknownVersion(),
|
||||
Identifiable.Id.of(), Identifiable.Id.of(), Identifiable.Id.of(),
|
||||
anzahlTickets);
|
||||
}
|
||||
|
||||
private static String getExpectedJsonStringForVersion(Versionable.Version field) {
|
||||
return String.format("\"%s\":{\"version\":%d", "version", field.version());
|
||||
}
|
||||
private static String getExpectedJsonStringForRawTypeString(String fieldName, RawWrapper<String> field) {
|
||||
return String.format("\"%s\":{\"raw\":\"%s\"", fieldName, field.raw());
|
||||
}
|
||||
private static String getExpectedJsonStringForRawTypeInteger(String fieldName, RawWrapper<Integer> field) {
|
||||
return String.format("\"%s\":{\"raw\":%d", fieldName, field.raw());
|
||||
}
|
||||
private static String getExpectedJsonStringForRawTypeLocalDateTime(String fieldName, RawWrapper<LocalDateTime> field) {
|
||||
return String.format("\"%s\":{\"raw\":\"%s\"", fieldName, field.raw().toString());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
package de.accso.flexinale.besucherportal.api_contract.event;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import de.accso.flexinale.common.api.event.Event;
|
||||
import de.accso.flexinale.common.infrastructure.eventbus.EventSerializationHelper;
|
||||
import de.accso.flexinale.common.shared_kernel.EventClassHelper;
|
||||
import de.accso.flexinale.common.shared_kernel.Identifiable;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static de.accso.flexinale.besucherportal.api_contract.event.ReflectionHelper.setField;
|
||||
import static org.assertj.core.api.Assertions.assertThatCode;
|
||||
|
||||
class EventStructureTest {
|
||||
private static Stream<Arguments> allEventTypes() {
|
||||
return AllBesucherportalEvents.eventTypes.stream().map(Arguments::of);
|
||||
}
|
||||
|
||||
// check for all Event subclasses if no exception is thrown for (de)serialization and reflection access
|
||||
// a) default constructor is needed
|
||||
// b) field 'version' is needed
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("allEventTypes")
|
||||
<E extends Event> void testCheckEventStructure(final Class<E> eventType)
|
||||
throws InstantiationException, IllegalAccessException, JsonProcessingException, NoSuchMethodException, InvocationTargetException {
|
||||
// arrange
|
||||
Constructor<E> constructor = eventType.getDeclaredConstructor();
|
||||
constructor.setAccessible(true);
|
||||
Event event = constructor.newInstance();
|
||||
|
||||
// set id, timestamp and version explicitely, so that (de)serialization is somewhat more realistic
|
||||
setField(eventType, event, "id", Identifiable.Id.of(), false);
|
||||
setField(eventType, event, "timestamp", LocalDateTime.now(), false);
|
||||
|
||||
// act - check serialization and deserialization
|
||||
String jsonString = EventSerializationHelper.serializeEvent2JsonString(event);
|
||||
EventSerializationHelper.deserializeJsonString2Event(jsonString, eventType);
|
||||
|
||||
// act - check instantiation and version attribute
|
||||
assertThatCode(() -> {
|
||||
EventClassHelper.getEventClazzVersion(eventType);
|
||||
})
|
||||
.doesNotThrowAnyException();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
package de.accso.flexinale.besucherportal.api_contract.event;
|
||||
|
||||
import de.accso.flexinale.common.api.event.Event;
|
||||
import de.accso.flexinale.common.shared_kernel.FlexinaleIllegalArgumentException;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
public final class ReflectionHelper {
|
||||
public static void setField(final Class<?> eventType, final Event event, final String fieldName, final Object fieldValue,
|
||||
boolean includeDerivedFieldsFromSuperClasses) throws IllegalAccessException {
|
||||
if (eventType.equals(Object.class)) {
|
||||
throw new FlexinaleIllegalArgumentException("event hierarchy does not have a field " + fieldName);
|
||||
}
|
||||
try {
|
||||
Field field = (includeDerivedFieldsFromSuperClasses)
|
||||
? eventType.getField(fieldName)
|
||||
: eventType.getDeclaredField(fieldName);
|
||||
field.setAccessible(true);
|
||||
field.set(event, fieldValue);
|
||||
}
|
||||
catch (NoSuchFieldException nosfex) {
|
||||
setField(eventType.getSuperclass(), event, fieldName, fieldValue, includeDerivedFieldsFromSuperClasses);
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue