diff --git a/.env.example b/.env.example
index 46ca945..87573d6 100644
--- a/.env.example
+++ b/.env.example
@@ -2,3 +2,7 @@ BACKEND_SERVER_PORT=8080
BACKEND_DATASOURCE_URL=jdbc:postgresql://localhost:5433/postgres
BACKEND_DATASOURCE_USERNAME=postgres
BACKEND_DATASOURCE_PASSWORD=postgres
+
+PRETIX_DEFAULT_ORGANIZER=org
+PRETIX_DEFAULT_EVENT=event
+PRETIX_API_KEY=OwO
diff --git a/.gitignore b/.gitignore
index 7448e74..6288371 100644
--- a/.gitignore
+++ b/.gitignore
@@ -33,4 +33,8 @@ build/
.vscode/
### draw.io ###
-.$*.drawio.bkp
\ No newline at end of file
+.$*.drawio.bkp
+
+### Configurations ###
+.env
+.env.local
diff --git a/application/pom.xml b/application/pom.xml
index b2abb14..ada7b96 100644
--- a/application/pom.xml
+++ b/application/pom.xml
@@ -30,11 +30,6 @@
org.springframework.boot
spring-boot-starter-jooq
-
-
- org.springframework.boot
- spring-boot-starter-data-jpa
-
org.springframework.boot
spring-boot-starter-mail
@@ -78,28 +73,6 @@
caffeine
3.1.8
-
- com.github.pengrad
- java-telegram-bot-api
- 7.9.1
-
-
-
- org.json
- json
- 20240303
-
-
-
- org.apache.httpcomponents
- httpclient
- 4.5.14
-
-
- org.apache.httpcomponents.client5
- httpclient5
- 5.2
-
org.zalando
logbook-spring-boot-starter
@@ -137,6 +110,7 @@
org.springframework.boot
spring-boot-maven-plugin
+ ${spring.boot.version}
diff --git a/application/src/main/java/net/furizon/backend/db/entities/pretix/Event.java b/application/src/main/java/net/furizon/backend/db/entities/pretix/Event.java
deleted file mode 100644
index 7627450..0000000
--- a/application/src/main/java/net/furizon/backend/db/entities/pretix/Event.java
+++ /dev/null
@@ -1,53 +0,0 @@
-package net.furizon.backend.db.entities.pretix;
-
-import jakarta.persistence.Column;
-import jakarta.persistence.Entity;
-import jakarta.persistence.FetchType;
-import jakarta.persistence.Id;
-import jakarta.persistence.OneToMany;
-import jakarta.persistence.Table;
-import jakarta.persistence.Temporal;
-import jakarta.persistence.TemporalType;
-import jakarta.persistence.Transient;
-import lombok.AccessLevel;
-import lombok.Getter;
-import lombok.Setter;
-
-import java.util.Date;
-import java.util.Map;
-import java.util.Set;
-
-// TODO -> Replace on JOOQ
-
-@Entity
-@Table(name = "events")
-@Getter
-@Setter
-public final class Event {
- @Id
- @Column(name = "event_slug")
- @Setter(AccessLevel.NONE)
- private String slug;
-
- @Column(name = "event_public_url")
- private String publicUrl;
-
- @Transient
- @Setter(AccessLevel.NONE)
- @Getter(AccessLevel.NONE)
- private Map eventNames;
- private String eventNamesRaw; //map lang -> name
-
- @Column(name = "event_date_from")
- @Temporal(TemporalType.TIMESTAMP)
- private Date dateFrom;
- @Column(name = "event_date_end")
- @Temporal(TemporalType.TIMESTAMP)
- private Date dateEnd;
-
- @Column(name = "event_is_current")
- private boolean isCurrentEvent;
-
- @OneToMany(mappedBy = "orderEvent", fetch = FetchType.LAZY)
- private Set orders;
-}
diff --git a/application/src/main/java/net/furizon/backend/db/entities/pretix/Order.java b/application/src/main/java/net/furizon/backend/db/entities/pretix/Order.java
deleted file mode 100644
index db3df87..0000000
--- a/application/src/main/java/net/furizon/backend/db/entities/pretix/Order.java
+++ /dev/null
@@ -1,242 +0,0 @@
-package net.furizon.backend.db.entities.pretix;
-
-import jakarta.persistence.Entity;
-import jakarta.persistence.Id;
-import jakarta.persistence.JoinColumn;
-import jakarta.persistence.ManyToOne;
-import jakarta.persistence.Table;
-import jakarta.persistence.Transient;
-import lombok.AccessLevel;
-import lombok.Getter;
-import net.furizon.backend.db.entities.users.User;
-import net.furizon.backend.infrastructure.pretix.model.ExtraDays;
-import net.furizon.backend.infrastructure.pretix.model.OrderStatus;
-import net.furizon.backend.infrastructure.pretix.model.QuestionType;
-import net.furizon.backend.infrastructure.pretix.model.Sponsorship;
-import net.furizon.backend.service.pretix.PretixService;
-import net.furizon.backend.utils.pretix.Constants;
-import org.apache.http.entity.ContentType;
-import org.json.JSONArray;
-import org.json.JSONObject;
-
-import java.time.LocalDate;
-import java.time.LocalTime;
-import java.time.ZonedDateTime;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.TimeoutException;
-
-// TODO -> Replace on JOOQ
-
-@Entity
-@Table(name = "orders")
-@Getter
-public class Order {
- @Id
- private String code;
-
- private OrderStatus orderStatus;
-
- @Getter(AccessLevel.NONE)
- private long dailyDays = 0L; //bitmask of days
-
- private Sponsorship sponsorship = Sponsorship.NONE;
-
- private ExtraDays extraDays = ExtraDays.NONE;
-
- private int roomCapacity = 0; // 0 = has no room
-
- private String hotelLocation = "ITALY";
-
- private String pretixOrderSecret = "GABIBBO";
-
- private boolean hasMembership = false;
-
- private int answersMainPositionId = -1;
-
- @Getter(AccessLevel.NONE)
- private String answers;
-
- @ManyToOne
- @JoinColumn(name = "user_id")
- private User orderOwner;
-
- @ManyToOne
- @JoinColumn(name = "event_id")
- private Event orderEvent;
-
-
- @Transient
- @Getter(AccessLevel.NONE)
- private Map answersData = null;
-
- private void loadAnswers(PretixService ps) {
- JSONArray jsonArray = new JSONArray(answers);
- Map answersData = new HashMap();
- for (int i = 0; i < jsonArray.length(); i++) {
- JSONObject obj = jsonArray.getJSONObject(i);
- int questionId = obj.getInt("question");
- String answerIdentifier = ps.translateQuestionId(questionId);
- String value = obj.getString("answer");
- Object o = switch (ps.translateQuestionType(questionId)) {
- case NUMBER -> Float.parseFloat(value);
- case STRING_ONE_LINE -> value;
- case STRING_MULTI_LINE -> value;
- case BOOLEAN -> value.equalsIgnoreCase("true") || value.equalsIgnoreCase("yes");
- case LIST_SINGLE_CHOICE -> value;
- case LIST_MULTIPLE_CHOICE -> value;
- case FILE -> Constants.QUESTIONS_FILE_KEEP;
- case DATE -> LocalDate.parse(value);
- case TIME -> LocalTime.parse(value);
- case DATE_TIME -> ZonedDateTime.parse(value);
- case COUNTRY_CODE -> value;
- case PHONE_NUMBER -> value;
- };
- answersData.put(answerIdentifier, o);
- }
- this.answersData = answersData;
- }
-
- private void saveAnswers(PretixService ps) {
- if (answersData == null) {
- loadAnswers(ps);
- }
- JSONArray jsonArray = new JSONArray();
- for (String key : answersData.keySet()) {
- Object o = answersData.get(key);
- String out = switch (ps.translateQuestionType(key)) {
- case QuestionType.NUMBER -> String.valueOf(o);
- case STRING_ONE_LINE -> (String) o;
- case STRING_MULTI_LINE -> (String) o;
- case BOOLEAN -> ((boolean) o) ? "true" : "false";
- case LIST_SINGLE_CHOICE -> (String) o;
- case LIST_MULTIPLE_CHOICE -> (String) o;
- case FILE -> (String) o;
- case DATE -> o.toString();
- case TIME -> o.toString();
- case DATE_TIME -> o.toString();
- case COUNTRY_CODE -> (String) o;
- case PHONE_NUMBER -> (String) o;
- };
- out = out.strip();
- if (!out.isEmpty()) {
- jsonArray.put(out);
- }
- }
- answers = jsonArray.toString();
- }
-
- public Object getAnswerValue(String answer, PretixService ps) {
- if (answersData == null) {
- loadAnswers(ps);
- }
- return answersData.get(answer);
-
- }
-
- public void setAnswerFile(
- String answer,
- ContentType mimeType,
- String fileName,
- byte[] bytes,
- PretixService ps
- ) throws TimeoutException {
- String newAns = ps.uploadFile(mimeType, fileName, bytes);
- this.setAnswerValue(answer, newAns, ps);
- }
-
- public void setAnswerValue(String answer, Object value, PretixService ps) {
- if (answersData == null) {
- loadAnswers(ps);
- }
- answersData.put(answer, value);
- saveAnswers(ps);
- }
-
- public void removeAnswer(String answer, PretixService ps) {
- if (answersData == null) {
- loadAnswers(ps);
- }
- answersData.remove(answer);
- saveAnswers(ps);
- }
-
- public String getAnswersRaw() {
- return answers;
- }
-
- public void resetFileUploadAnswers(PretixService ps) {
- //After uploading a file, we need to change it's value to "file:keep"
- for (String key : answersData.keySet()) {
- if (ps.translateQuestionType(key) == QuestionType.FILE) {
- answersData.put(key, Constants.QUESTIONS_FILE_KEEP);
- }
- }
- saveAnswers(ps);
- }
-
- public boolean isDaily() {
- return dailyDays != 0L;
- }
-
- public boolean hasDay(int day) {
- return (dailyDays & (1L << day)) != 0L;
- }
-
- @Transient
- @Getter(AccessLevel.NONE)
- private Set dailyDaysSet = null;
-
- public Set getDays() {
- if (dailyDaysSet == null) {
- Set s = new HashSet<>();
- for (int i = 0; i < 64; i++) {
- if (hasDay(i)) {
- s.add(i);
- }
- }
- dailyDaysSet = s;
- }
- return dailyDaysSet;
- }
-
- public boolean ownsRoom() {
- return roomCapacity > 0;
- }
-
- public void update(
- String code,
- OrderStatus status,
- String pretixOrderSecret,
- int positionId,
- Set days,
- Sponsorship sponsorship,
- ExtraDays extraDays,
- int roomCapacity,
- String hotelLocation,
- boolean hasMembership,
- User orderOwner,
- Event orderEvent,
- JSONArray answersJson
- ) {
- this.code = code;
- this.orderStatus = status;
- this.extraDays = extraDays;
- this.sponsorship = sponsorship;
- this.roomCapacity = roomCapacity;
- this.hasMembership = hasMembership;
- this.hotelLocation = hotelLocation;
- this.pretixOrderSecret = pretixOrderSecret;
- this.answersMainPositionId = positionId;
- this.answers = answersJson.toString();
- this.answersData = null;
- this.dailyDaysSet = null;
-
- this.dailyDays = 0L;
- for (Integer day : days) {
- dailyDays |= 1L << day;
- }
- }
-}
diff --git a/application/src/main/java/net/furizon/backend/db/entities/users/User.java b/application/src/main/java/net/furizon/backend/db/entities/users/User.java
deleted file mode 100644
index d65879b..0000000
--- a/application/src/main/java/net/furizon/backend/db/entities/users/User.java
+++ /dev/null
@@ -1,62 +0,0 @@
-package net.furizon.backend.db.entities.users;
-
-import jakarta.persistence.Column;
-import jakarta.persistence.Entity;
-import jakarta.persistence.GeneratedValue;
-import jakarta.persistence.GenerationType;
-import jakarta.persistence.Id;
-import jakarta.persistence.OneToMany;
-import jakarta.persistence.Table;
-import lombok.Getter;
-import lombok.NoArgsConstructor;
-import lombok.Setter;
-import net.furizon.backend.db.entities.pretix.Order;
-
-import java.util.Set;
-
-// TODO -> Replace on JOOQ
-
-/**
- * The user class represent a past, present or future attendee at the convention,
- * it may be the author of published images, owner of a room or a roomie,
- * owns one or more membership cards, either expired or not.
- * May be linked to any orders placed in pretix.
- *
- * TODO: handle nickname and fursona name problem
- */
-@Entity
-@Table(name = "users")
-@NoArgsConstructor
-@Getter
-public final class User {
- @Id
- @GeneratedValue(strategy = GenerationType.IDENTITY)
- @Column(name = "user_id", nullable = false)
- private long id;
-
- @Column(name = "user_secret", nullable = false)
- private String secret;
-
- @Setter
- @Column(name = "user_first_name")
- // TODO -> nullable?
- private String firstName;
-
- @Setter
- @Column(name = "user_last_name")
- // TODO -> nullable?
- private String lastName;
-
- @Setter
- @Column(name = "user_locale")
- // TODO -> nullable?
- private String locale = "en";
-
- @OneToMany(mappedBy = "orderOwner")
- // TODO -> nullable?
- private Set orders;
-
- public User(String secret) {
- this.secret = secret;
- }
-}
diff --git a/application/src/main/java/net/furizon/backend/db/repositories/pretix/OrderRepository.java b/application/src/main/java/net/furizon/backend/db/repositories/pretix/OrderRepository.java
deleted file mode 100644
index 5a3cb67..0000000
--- a/application/src/main/java/net/furizon/backend/db/repositories/pretix/OrderRepository.java
+++ /dev/null
@@ -1,12 +0,0 @@
-package net.furizon.backend.db.repositories.pretix;
-
-import net.furizon.backend.db.entities.pretix.Order;
-import org.springframework.data.jpa.repository.Query;
-import org.springframework.data.repository.ListCrudRepository;
-
-import java.util.Optional;
-
-public interface OrderRepository extends ListCrudRepository {
- @Query(value = "SELECT O FROM Order O INNER JOIN Event E on O.orderEvent = E WHERE O.code = ?1 AND E.slug = ?2")
- Optional findByCodeAndEvent(String orderCode, String eventSlug);
-}
diff --git a/application/src/main/java/net/furizon/backend/feature/event/Event.java b/application/src/main/java/net/furizon/backend/feature/event/Event.java
deleted file mode 100644
index 8bf425c..0000000
--- a/application/src/main/java/net/furizon/backend/feature/event/Event.java
+++ /dev/null
@@ -1,61 +0,0 @@
-package net.furizon.backend.feature.event;
-
-import lombok.Builder;
-import lombok.Data;
-import lombok.RequiredArgsConstructor;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-import org.springframework.data.util.Pair;
-
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.time.OffsetDateTime;
-import java.util.Set;
-
-@RequiredArgsConstructor
-@Data
-@Builder
-public class Event {
- @NotNull
- private final String slug;
-
- @Nullable
- private final OffsetDateTime dateEnd;
-
- @Nullable
- private final OffsetDateTime dateFrom;
-
- @Nullable
- private final Boolean isCurrent;
-
- @Nullable
- private final String publicUrl;
-
- @Nullable
- private final Set eventNames;
-
- @Nullable
- public Pair getOrganizerAndEventPair() {
- if (publicUrl == null) {
- return null;
- }
-
- try {
- URI uri = new URI(publicUrl);
-
- // Split the path by "/" and filter out any empty parts
- String[] parts = uri.getPath().split("/");
-
- if (parts.length >= 3) {
- String organizer = parts[1]; // First part after base is the organizer
- String eventName = parts[2]; // Second part after base is the event name
-
- return Pair.of(organizer, eventName);
- } else {
- throw new IllegalArgumentException("URL does not contain organizer and event name");
- }
- } catch (URISyntaxException e) {
- return null;
- }
- }
-}
diff --git a/application/src/main/java/net/furizon/backend/feature/event/finder/JooqEventFinder.java b/application/src/main/java/net/furizon/backend/feature/event/finder/JooqEventFinder.java
deleted file mode 100644
index 06116bd..0000000
--- a/application/src/main/java/net/furizon/backend/feature/event/finder/JooqEventFinder.java
+++ /dev/null
@@ -1,36 +0,0 @@
-package net.furizon.backend.feature.event.finder;
-
-import lombok.RequiredArgsConstructor;
-import net.furizon.backend.feature.event.Event;
-import net.furizon.backend.feature.event.mapper.JooqEventMapper;
-import net.furizon.jooq.infrastructure.query.SqlQuery;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-import org.jooq.util.postgres.PostgresDSL;
-import org.springframework.stereotype.Component;
-
-import static net.furizon.jooq.generated.Tables.EVENTS;
-
-@Component
-@RequiredArgsConstructor
-public class JooqEventFinder implements EventFinder {
- public final SqlQuery query;
-
- @Override
- public @Nullable Event findEventBySlug(@NotNull String slug) {
- return query.fetchFirst(
- PostgresDSL
- .select(
- EVENTS.EVENT_SLUG,
- EVENTS.EVENT_DATE_END,
- EVENTS.EVENT_DATE_FROM,
- EVENTS.EVENT_IS_CURRENT,
- EVENTS.EVENT_PUBLIC_URL,
- EVENTS.EVENT_NAMES
- )
- .from(EVENTS)
- .where(EVENTS.EVENT_SLUG.eq(slug))
- )
- .mapOrNull(JooqEventMapper::map);
- }
-}
diff --git a/application/src/main/java/net/furizon/backend/feature/event/mapper/JooqEventMapper.java b/application/src/main/java/net/furizon/backend/feature/event/mapper/JooqEventMapper.java
deleted file mode 100644
index 04f5147..0000000
--- a/application/src/main/java/net/furizon/backend/feature/event/mapper/JooqEventMapper.java
+++ /dev/null
@@ -1,40 +0,0 @@
-package net.furizon.backend.feature.event.mapper;
-
-import net.furizon.backend.feature.event.Event;
-import org.jetbrains.annotations.NotNull;
-import org.jooq.Record;
-
-import java.time.OffsetDateTime;
-import java.util.Arrays;
-import java.util.Optional;
-import java.util.stream.Collectors;
-
-import static net.furizon.jooq.generated.Tables.EVENTS;
-
-public class JooqEventMapper {
- @NotNull
- public static Event map(Record record) {
- return Event.builder()
- .slug(record.get(EVENTS.EVENT_SLUG))
- .dateEnd(
- Optional.ofNullable(record.get(EVENTS.EVENT_DATE_END))
- .map(OffsetDateTime::parse)
- .orElse(null)
- )
- .dateFrom(
- Optional.ofNullable(record.get(EVENTS.EVENT_DATE_FROM))
- .map(OffsetDateTime::parse)
- .orElse(null)
- )
- .isCurrent(record.get(EVENTS.EVENT_IS_CURRENT))
- .publicUrl(record.get(EVENTS.EVENT_PUBLIC_URL))
- .eventNames(
- Optional.ofNullable(record.get(EVENTS.EVENT_NAMES))
- .map(it ->
- Arrays.stream(it.split(",")).collect(Collectors.toSet())
- )
- .orElse(null)
- )
- .build();
- }
-}
diff --git a/application/src/main/java/net/furizon/backend/feature/event/usecase/ReloadEventsUseCase.java b/application/src/main/java/net/furizon/backend/feature/event/usecase/ReloadEventsUseCase.java
deleted file mode 100644
index 7ddd56a..0000000
--- a/application/src/main/java/net/furizon/backend/feature/event/usecase/ReloadEventsUseCase.java
+++ /dev/null
@@ -1,104 +0,0 @@
-package net.furizon.backend.feature.event.usecase;
-
-import lombok.RequiredArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
-import net.furizon.backend.feature.event.Event;
-import net.furizon.backend.feature.event.action.insert.InsertNewEventAction;
-import net.furizon.backend.feature.event.finder.EventFinder;
-import net.furizon.backend.feature.event.finder.pretix.PretixEventFinder;
-import net.furizon.backend.feature.organizer.finder.OrganizersFinder;
-import net.furizon.backend.infrastructure.pretix.PretixConfig;
-import net.furizon.backend.infrastructure.pretix.PretixPagingUtil;
-import net.furizon.backend.infrastructure.pretix.dto.PretixPaging;
-import net.furizon.backend.infrastructure.usecase.UseCase;
-import net.furizon.backend.infrastructure.usecase.UseCaseInput;
-import net.furizon.backend.infrastructure.web.exception.ApiException;
-import org.jetbrains.annotations.NotNull;
-import org.springframework.stereotype.Component;
-import org.springframework.transaction.annotation.Transactional;
-
-import java.util.Collection;
-import java.util.Optional;
-import java.util.concurrent.atomic.AtomicReference;
-import java.util.stream.Collectors;
-
-/**
- * This use case should get all events from Pretix
- * Insert it to Database if not exist there (yet)
- * Returns the current event object or null if non was found
- */
-@Component
-@RequiredArgsConstructor
-@Slf4j
-public class ReloadEventsUseCase implements UseCase> {
- // TODO -> Think about cache version for organizers?
- private final OrganizersFinder organizersFinder;
-
- private final PretixEventFinder pretixEventFinder;
-
- private final EventFinder eventFinder;
-
- private final PretixConfig pretixConfig;
-
- private final InsertNewEventAction insertEventAction;
-
- @Transactional
- @NotNull
- @Override
- public Optional executor(@NotNull UseCaseInput input) {
- final var organizers = organizersFinder.getPagedOrganizers(PretixPaging.DEFAULT_PAGE).getResults();
- if (organizers == null) {
- throw new ApiException("Could not get organizers from pretix");
- }
-
- AtomicReference currentEvent = new AtomicReference<>(null);
- for (final var organizer : organizers) {
- PretixPagingUtil.fetchAll(
- paging -> pretixEventFinder.getPagedEvents(organizer.getSlug(), paging),
- result -> {
- final var events = result.getFirst();
- final var eventsName = events.stream().map(it -> it.getName().values())
- .flatMap(Collection::stream)
- .collect(Collectors.toSet());
-
- for (final var event : events) {
- final var databaseEvent = eventFinder.findEventBySlug(event.getSlug());
- if (databaseEvent == null) {
- final boolean isCurrentEvent = pretixConfig
- .getDefaultOrganizer()
- .equals(organizer.getSlug()) && pretixConfig
- .getDefaultEvent()
- .equals(event.getSlug());
-
- final var newEvent = Event.builder()
- .slug(event.getSlug())
- .publicUrl(event.getPublicUrl())
- .eventNames(eventsName)
- .isCurrent(isCurrentEvent)
- .dateEnd(event.getDateFrom()) // huh? namings in db not the same as in pretix?
- .dateFrom(event.getDateTo()) // is from is start?
- .build();
-
- insertEventAction.invoke(newEvent);
-
- // in case if we won't find an existed current event
- if (isCurrentEvent) {
- currentEvent.set(newEvent);
- }
- }
-
- if (databaseEvent != null && Boolean.TRUE.equals(databaseEvent.getIsCurrent())) {
- currentEvent.set(databaseEvent);
- }
- }
- }
- );
- }
-
- if (currentEvent.get() == null) {
- log.warn("Could not find the current event");
- }
-
- return Optional.ofNullable(currentEvent.get());
- }
-}
diff --git a/application/src/main/java/net/furizon/backend/feature/pretix/event/Event.java b/application/src/main/java/net/furizon/backend/feature/pretix/event/Event.java
new file mode 100644
index 0000000..dcbafa8
--- /dev/null
+++ b/application/src/main/java/net/furizon/backend/feature/pretix/event/Event.java
@@ -0,0 +1,58 @@
+package net.furizon.backend.feature.pretix.event;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.RequiredArgsConstructor;
+import net.furizon.backend.infrastructure.pretix.PretixGenericUtils;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.time.OffsetDateTime;
+import java.util.Map;
+
+@RequiredArgsConstructor
+@AllArgsConstructor
+@Data
+@Builder
+public class Event {
+ @NotNull
+ private final String slug;
+
+ @Nullable
+ private OffsetDateTime dateTo;
+
+ @Nullable
+ private OffsetDateTime dateFrom;
+
+ private boolean isCurrent;
+
+ @NotNull
+ private String publicUrl;
+
+ @Nullable
+ private Map eventNames;
+
+ public static class EventBuilder {
+ public EventBuilder slug(String fullSlug) {
+ this.slug = fullSlug;
+ return this;
+ }
+ public EventBuilder slug(String eventSlug, String organizerSlug) {
+ this.slug = PretixGenericUtils.buildOrgEventSlug(eventSlug, organizerSlug);
+ return this;
+ }
+ }
+
+ @Data
+ public static class OrganizerAndEventPair {
+ private final String organizer;
+ private final String event;
+ }
+
+ @NotNull
+ public OrganizerAndEventPair getOrganizerAndEventPair() {
+ String[] sp = slug.split("/");
+ return new OrganizerAndEventPair(sp[0], sp[1]);
+ }
+}
diff --git a/application/src/main/java/net/furizon/backend/feature/event/PretixEvent.java b/application/src/main/java/net/furizon/backend/feature/pretix/event/PretixEvent.java
similarity index 93%
rename from application/src/main/java/net/furizon/backend/feature/event/PretixEvent.java
rename to application/src/main/java/net/furizon/backend/feature/pretix/event/PretixEvent.java
index 2751d09..9106a58 100644
--- a/application/src/main/java/net/furizon/backend/feature/event/PretixEvent.java
+++ b/application/src/main/java/net/furizon/backend/feature/pretix/event/PretixEvent.java
@@ -1,4 +1,4 @@
-package net.furizon.backend.feature.event;
+package net.furizon.backend.feature.pretix.event;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
diff --git a/application/src/main/java/net/furizon/backend/feature/event/action/insert/InsertNewEventAction.java b/application/src/main/java/net/furizon/backend/feature/pretix/event/action/insert/InsertNewEventAction.java
similarity index 51%
rename from application/src/main/java/net/furizon/backend/feature/event/action/insert/InsertNewEventAction.java
rename to application/src/main/java/net/furizon/backend/feature/pretix/event/action/insert/InsertNewEventAction.java
index aaef8e9..2a2f81f 100644
--- a/application/src/main/java/net/furizon/backend/feature/event/action/insert/InsertNewEventAction.java
+++ b/application/src/main/java/net/furizon/backend/feature/pretix/event/action/insert/InsertNewEventAction.java
@@ -1,6 +1,6 @@
-package net.furizon.backend.feature.event.action.insert;
+package net.furizon.backend.feature.pretix.event.action.insert;
-import net.furizon.backend.feature.event.Event;
+import net.furizon.backend.feature.pretix.event.Event;
import org.jetbrains.annotations.NotNull;
public interface InsertNewEventAction {
diff --git a/application/src/main/java/net/furizon/backend/feature/event/action/insert/JooqInsertNewEventAction.java b/application/src/main/java/net/furizon/backend/feature/pretix/event/action/insert/JooqInsertNewEventAction.java
similarity index 65%
rename from application/src/main/java/net/furizon/backend/feature/event/action/insert/JooqInsertNewEventAction.java
rename to application/src/main/java/net/furizon/backend/feature/pretix/event/action/insert/JooqInsertNewEventAction.java
index 4515c95..af3e950 100644
--- a/application/src/main/java/net/furizon/backend/feature/event/action/insert/JooqInsertNewEventAction.java
+++ b/application/src/main/java/net/furizon/backend/feature/pretix/event/action/insert/JooqInsertNewEventAction.java
@@ -1,7 +1,8 @@
-package net.furizon.backend.feature.event.action.insert;
+package net.furizon.backend.feature.pretix.event.action.insert;
import lombok.RequiredArgsConstructor;
-import net.furizon.backend.feature.event.Event;
+import net.furizon.backend.feature.pretix.event.Event;
+import net.furizon.backend.infrastructure.jackson.JsonSerializer;
import net.furizon.jooq.infrastructure.command.SqlCommand;
import org.jetbrains.annotations.NotNull;
import org.jooq.util.postgres.PostgresDSL;
@@ -17,6 +18,8 @@
public class JooqInsertNewEventAction implements InsertNewEventAction {
private final SqlCommand command;
+ private final JsonSerializer jsonSerializer;
+
@Override
public void invoke(@NotNull Event event) {
command.execute(
@@ -24,7 +27,7 @@ public void invoke(@NotNull Event event) {
.insertInto(
EVENTS,
EVENTS.EVENT_SLUG,
- EVENTS.EVENT_DATE_END,
+ EVENTS.EVENT_DATE_TO,
EVENTS.EVENT_DATE_FROM,
EVENTS.EVENT_IS_CURRENT,
EVENTS.EVENT_PUBLIC_URL,
@@ -33,15 +36,18 @@ public void invoke(@NotNull Event event) {
.values(
event.getSlug(),
Optional
- .ofNullable(event.getDateEnd()).map(OffsetDateTime::toString)
+ .ofNullable(event.getDateTo())
+ .map(OffsetDateTime::toString)
.orElse(null),
Optional
- .ofNullable(event.getDateFrom()).map(OffsetDateTime::toString)
+ .ofNullable(event.getDateFrom())
+ .map(OffsetDateTime::toString)
.orElse(null),
- event.getIsCurrent(),
+ event.isCurrent(),
event.getPublicUrl(),
Optional
- .ofNullable(event.getEventNames()).map(it -> String.join(",", it))
+ .ofNullable(event.getEventNames())
+ .map(jsonSerializer::serializeAsString)
.orElse(null)
)
);
diff --git a/application/src/main/java/net/furizon/backend/feature/pretix/event/action/update/JooqUpdateEventAction.java b/application/src/main/java/net/furizon/backend/feature/pretix/event/action/update/JooqUpdateEventAction.java
new file mode 100644
index 0000000..732e307
--- /dev/null
+++ b/application/src/main/java/net/furizon/backend/feature/pretix/event/action/update/JooqUpdateEventAction.java
@@ -0,0 +1,45 @@
+package net.furizon.backend.feature.pretix.event.action.update;
+
+import lombok.RequiredArgsConstructor;
+import net.furizon.backend.feature.pretix.event.Event;
+import net.furizon.backend.infrastructure.jackson.JsonSerializer;
+import net.furizon.jooq.infrastructure.command.SqlCommand;
+import org.jetbrains.annotations.NotNull;
+import org.jooq.util.postgres.PostgresDSL;
+import org.springframework.stereotype.Component;
+
+import java.time.OffsetDateTime;
+import java.util.Optional;
+
+import static net.furizon.jooq.generated.Tables.EVENTS;
+
+@Component
+@RequiredArgsConstructor
+public class JooqUpdateEventAction implements UpdateEventAction {
+ private final SqlCommand command;
+
+ private final JsonSerializer jsonSerializer;
+
+ @Override
+ public void invoke(@NotNull final Event event) {
+ command.execute(
+ PostgresDSL
+ .update(EVENTS)
+ .set(EVENTS.EVENT_DATE_FROM, Optional
+ .ofNullable(event.getDateFrom())
+ .map(OffsetDateTime::toString)
+ .orElse(null))
+ .set(EVENTS.EVENT_IS_CURRENT, event.isCurrent())
+ .set(EVENTS.EVENT_PUBLIC_URL, event.getPublicUrl())
+ .set(EVENTS.EVENT_NAMES, Optional
+ .ofNullable(event.getEventNames())
+ .map(jsonSerializer::serializeAsString)
+ .orElse(null))
+ .set(EVENTS.EVENT_DATE_TO, Optional
+ .ofNullable(event.getDateTo())
+ .map(OffsetDateTime::toString)
+ .orElse(null))
+ .where(EVENTS.EVENT_SLUG.eq(event.getSlug()))
+ );
+ }
+}
diff --git a/application/src/main/java/net/furizon/backend/feature/pretix/event/action/update/UpdateEventAction.java b/application/src/main/java/net/furizon/backend/feature/pretix/event/action/update/UpdateEventAction.java
new file mode 100644
index 0000000..ebf4ee7
--- /dev/null
+++ b/application/src/main/java/net/furizon/backend/feature/pretix/event/action/update/UpdateEventAction.java
@@ -0,0 +1,8 @@
+package net.furizon.backend.feature.pretix.event.action.update;
+
+import net.furizon.backend.feature.pretix.event.Event;
+import org.jetbrains.annotations.NotNull;
+
+public interface UpdateEventAction {
+ void invoke(@NotNull final Event event);
+}
diff --git a/application/src/main/java/net/furizon/backend/feature/event/finder/EventFinder.java b/application/src/main/java/net/furizon/backend/feature/pretix/event/finder/EventFinder.java
similarity index 62%
rename from application/src/main/java/net/furizon/backend/feature/event/finder/EventFinder.java
rename to application/src/main/java/net/furizon/backend/feature/pretix/event/finder/EventFinder.java
index 779e51d..7929408 100644
--- a/application/src/main/java/net/furizon/backend/feature/event/finder/EventFinder.java
+++ b/application/src/main/java/net/furizon/backend/feature/pretix/event/finder/EventFinder.java
@@ -1,6 +1,6 @@
-package net.furizon.backend.feature.event.finder;
+package net.furizon.backend.feature.pretix.event.finder;
-import net.furizon.backend.feature.event.Event;
+import net.furizon.backend.feature.pretix.event.Event;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
diff --git a/application/src/main/java/net/furizon/backend/feature/pretix/event/finder/JooqEventFinder.java b/application/src/main/java/net/furizon/backend/feature/pretix/event/finder/JooqEventFinder.java
new file mode 100644
index 0000000..33b4695
--- /dev/null
+++ b/application/src/main/java/net/furizon/backend/feature/pretix/event/finder/JooqEventFinder.java
@@ -0,0 +1,37 @@
+package net.furizon.backend.feature.pretix.event.finder;
+
+import lombok.RequiredArgsConstructor;
+import net.furizon.backend.feature.pretix.event.Event;
+import net.furizon.backend.feature.pretix.event.mapper.JooqEventMapper;
+import net.furizon.jooq.infrastructure.query.SqlQuery;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jooq.util.postgres.PostgresDSL;
+import org.springframework.stereotype.Component;
+
+import static net.furizon.jooq.generated.Tables.EVENTS;
+
+@Component
+@RequiredArgsConstructor
+public class JooqEventFinder implements EventFinder {
+ private final SqlQuery query;
+
+ private final JooqEventMapper mapper;
+
+ @Override
+ public @Nullable Event findEventBySlug(@NotNull String slug) {
+ return query.fetchFirst(
+ PostgresDSL
+ .select(
+ EVENTS.EVENT_SLUG,
+ EVENTS.EVENT_DATE_TO,
+ EVENTS.EVENT_DATE_FROM,
+ EVENTS.EVENT_IS_CURRENT,
+ EVENTS.EVENT_PUBLIC_URL,
+ EVENTS.EVENT_NAMES
+ )
+ .from(EVENTS)
+ .where(EVENTS.EVENT_SLUG.eq(slug))
+ ).mapOrNull(mapper::map);
+ }
+}
diff --git a/application/src/main/java/net/furizon/backend/feature/event/finder/pretix/PretixEventFinder.java b/application/src/main/java/net/furizon/backend/feature/pretix/event/finder/pretix/PretixEventFinder.java
similarity index 66%
rename from application/src/main/java/net/furizon/backend/feature/event/finder/pretix/PretixEventFinder.java
rename to application/src/main/java/net/furizon/backend/feature/pretix/event/finder/pretix/PretixEventFinder.java
index 6f13855..fa60e7f 100644
--- a/application/src/main/java/net/furizon/backend/feature/event/finder/pretix/PretixEventFinder.java
+++ b/application/src/main/java/net/furizon/backend/feature/pretix/event/finder/pretix/PretixEventFinder.java
@@ -1,6 +1,6 @@
-package net.furizon.backend.feature.event.finder.pretix;
+package net.furizon.backend.feature.pretix.event.finder.pretix;
-import net.furizon.backend.feature.event.PretixEvent;
+import net.furizon.backend.feature.pretix.event.PretixEvent;
import net.furizon.backend.infrastructure.pretix.dto.PretixPaging;
import org.jetbrains.annotations.NotNull;
diff --git a/application/src/main/java/net/furizon/backend/feature/event/finder/pretix/RestPretixEventFinder.java b/application/src/main/java/net/furizon/backend/feature/pretix/event/finder/pretix/RestPretixEventFinder.java
similarity index 88%
rename from application/src/main/java/net/furizon/backend/feature/event/finder/pretix/RestPretixEventFinder.java
rename to application/src/main/java/net/furizon/backend/feature/pretix/event/finder/pretix/RestPretixEventFinder.java
index 556398e..c2f02b3 100644
--- a/application/src/main/java/net/furizon/backend/feature/event/finder/pretix/RestPretixEventFinder.java
+++ b/application/src/main/java/net/furizon/backend/feature/pretix/event/finder/pretix/RestPretixEventFinder.java
@@ -1,8 +1,8 @@
-package net.furizon.backend.feature.event.finder.pretix;
+package net.furizon.backend.feature.pretix.event.finder.pretix;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
-import net.furizon.backend.feature.event.PretixEvent;
+import net.furizon.backend.feature.pretix.event.PretixEvent;
import net.furizon.backend.infrastructure.http.client.HttpClient;
import net.furizon.backend.infrastructure.http.client.HttpRequest;
import net.furizon.backend.infrastructure.pretix.PretixConfig;
@@ -21,7 +21,7 @@
@Slf4j
public class RestPretixEventFinder implements PretixEventFinder {
private final ParameterizedTypeReference> pretixEvents =
- new ParameterizedTypeReference<>() {
+ new ParameterizedTypeReference<>() {
};
@Qualifier("pretixHttpClient")
@@ -32,7 +32,7 @@ public class RestPretixEventFinder implements PretixEventFinder {
public PretixPaging getPagedEvents(@NotNull String organizer, int page) {
final var request = HttpRequest.>create()
.method(HttpMethod.GET)
- .path("/organizers/{organizer}/events")
+ .path("/organizers/{organizer}/events/")
.queryParam("page", String.valueOf(page))
.uriVariable("organizer", organizer)
.responseParameterizedType(pretixEvents)
diff --git a/application/src/main/java/net/furizon/backend/feature/pretix/event/mapper/JooqEventMapper.java b/application/src/main/java/net/furizon/backend/feature/pretix/event/mapper/JooqEventMapper.java
new file mode 100644
index 0000000..4fff499
--- /dev/null
+++ b/application/src/main/java/net/furizon/backend/feature/pretix/event/mapper/JooqEventMapper.java
@@ -0,0 +1,57 @@
+package net.furizon.backend.feature.pretix.event.mapper;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import net.furizon.backend.feature.pretix.event.Event;
+import org.jetbrains.annotations.NotNull;
+import org.jooq.Record;
+import org.springframework.stereotype.Component;
+
+import java.time.OffsetDateTime;
+import java.util.HashMap;
+import java.util.Optional;
+
+import static net.furizon.jooq.generated.Tables.EVENTS;
+
+@Component
+@RequiredArgsConstructor
+@Slf4j
+public class JooqEventMapper {
+ private final TypeReference> typeRef = new TypeReference<>() {};
+
+ private final ObjectMapper objectMapper;
+
+ @NotNull
+ public Event map(Record record) {
+ return Event.builder()
+ .slug(record.get(EVENTS.EVENT_SLUG))
+ .dateTo(
+ Optional.ofNullable(record.get(EVENTS.EVENT_DATE_TO))
+ .map(OffsetDateTime::parse)
+ .orElse(null)
+ )
+ .dateFrom(
+ Optional.ofNullable(record.get(EVENTS.EVENT_DATE_FROM))
+ .map(OffsetDateTime::parse)
+ .orElse(null)
+ )
+ .isCurrent(record.get(EVENTS.EVENT_IS_CURRENT))
+ .publicUrl(record.get(EVENTS.EVENT_PUBLIC_URL))
+ .eventNames(
+ Optional.ofNullable(record.get(EVENTS.EVENT_NAMES))
+ .map(it -> {
+ try {
+ return objectMapper.readValue(it, typeRef);
+ } catch (JsonProcessingException e) {
+ log.error("Could not parse event names", e);
+ throw new RuntimeException(e);
+ }
+ })
+ .orElse(null)
+ )
+ .build();
+ }
+}
diff --git a/application/src/main/java/net/furizon/backend/feature/pretix/event/usecase/ReloadEventsUseCase.java b/application/src/main/java/net/furizon/backend/feature/pretix/event/usecase/ReloadEventsUseCase.java
new file mode 100644
index 0000000..08ef91c
--- /dev/null
+++ b/application/src/main/java/net/furizon/backend/feature/pretix/event/usecase/ReloadEventsUseCase.java
@@ -0,0 +1,106 @@
+package net.furizon.backend.feature.pretix.event.usecase;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import net.furizon.backend.feature.pretix.event.Event;
+import net.furizon.backend.feature.pretix.event.PretixEvent;
+import net.furizon.backend.feature.pretix.event.action.insert.InsertNewEventAction;
+import net.furizon.backend.feature.pretix.event.action.update.UpdateEventAction;
+import net.furizon.backend.feature.pretix.event.finder.EventFinder;
+import net.furizon.backend.feature.pretix.event.finder.pretix.PretixEventFinder;
+import net.furizon.backend.feature.pretix.organizer.PretixOrganizer;
+import net.furizon.backend.feature.pretix.organizer.finder.OrganizersFinder;
+import net.furizon.backend.infrastructure.pretix.PretixConfig;
+import net.furizon.backend.infrastructure.pretix.PretixPagingUtil;
+import net.furizon.backend.infrastructure.usecase.UseCase;
+import net.furizon.backend.infrastructure.usecase.UseCaseInput;
+import net.furizon.backend.infrastructure.pretix.PretixGenericUtils;
+import org.jetbrains.annotations.NotNull;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
+import java.util.Optional;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * This use case should get all events from Pretix
+ * Insert it to Database if not exist there (yet) or update them
+ * Returns the current event object or null if non was found
+ */
+@Component
+@RequiredArgsConstructor
+@Slf4j
+public class ReloadEventsUseCase implements UseCase> {
+ private final OrganizersFinder organizersFinder;
+ private final PretixEventFinder pretixEventFinder;
+ private final EventFinder eventFinder;
+
+ private final InsertNewEventAction insertEventAction;
+ private final UpdateEventAction updateEventAction;
+
+ private final PretixConfig pretixConfig;
+
+ @Transactional
+ @NotNull
+ @Override
+ public Optional executor(@NotNull UseCaseInput input) {
+ AtomicReference currentEvent = new AtomicReference<>(null);
+ List organizers = PretixPagingUtil.fetchAll(organizersFinder::getPagedOrganizers);
+ for (final PretixOrganizer organizer : organizers) {
+ PretixPagingUtil.forEachElement(
+ paging -> pretixEventFinder.getPagedEvents(organizer.getSlug(), paging),
+ result -> {
+ final PretixEvent event = result.getLeft();
+
+ Event dbEvent = eventFinder.findEventBySlug(
+ PretixGenericUtils.buildOrgEventSlug(event.getSlug(), organizer.getSlug()));
+ boolean isCurrentEvent = pretixConfig
+ .getDefaultOrganizer()
+ .equals(organizer.getSlug())
+ && pretixConfig
+ .getDefaultEvent()
+ .equals(event.getSlug());
+
+
+ if (dbEvent == null) {
+ //Create new event
+ Event newEvent = Event.builder()
+ .slug(event.getSlug(), organizer.getSlug())
+ .publicUrl(event.getPublicUrl())
+ .eventNames(event.getName())
+ .isCurrent(isCurrentEvent)
+ .dateTo(event.getDateTo()) // namings in db not the same as in pretix?
+ .dateFrom(event.getDateFrom()) // is from is start?
+ .build();
+
+ insertEventAction.invoke(newEvent);
+
+ // in case if we won't find an existed current event
+ if (isCurrentEvent) {
+ currentEvent.set(newEvent);
+ }
+ } else {
+ //Update existing event
+ dbEvent.setPublicUrl(event.getPublicUrl());
+ dbEvent.setDateFrom(event.getDateFrom());
+ dbEvent.setDateTo(event.getDateTo());
+ dbEvent.setEventNames(event.getName());
+ dbEvent.setCurrent(isCurrentEvent);
+ updateEventAction.invoke(dbEvent);
+ }
+
+ if (dbEvent != null && dbEvent.isCurrent()) {
+ currentEvent.set(dbEvent);
+ }
+ }
+ );
+ }
+
+ if (currentEvent.get() == null) {
+ log.warn("Could not find the current event");
+ }
+
+ return Optional.ofNullable(currentEvent.get());
+ }
+}
diff --git a/application/src/main/java/net/furizon/backend/feature/pretix/order/Order.java b/application/src/main/java/net/furizon/backend/feature/pretix/order/Order.java
new file mode 100644
index 0000000..f7c8434
--- /dev/null
+++ b/application/src/main/java/net/furizon/backend/feature/pretix/order/Order.java
@@ -0,0 +1,227 @@
+package net.furizon.backend.feature.pretix.order;
+
+import lombok.AccessLevel;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+import lombok.Setter;
+import lombok.extern.slf4j.Slf4j;
+import net.furizon.backend.feature.pretix.event.Event;
+import net.furizon.backend.feature.user.User;
+import net.furizon.backend.infrastructure.pretix.Const;
+import net.furizon.backend.infrastructure.pretix.PretixGenericUtils;
+import net.furizon.backend.infrastructure.pretix.model.ExtraDays;
+import net.furizon.backend.infrastructure.pretix.model.OrderStatus;
+import net.furizon.backend.infrastructure.pretix.model.Sponsorship;
+import net.furizon.backend.infrastructure.pretix.service.PretixInformation;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.time.LocalDate;
+import java.time.LocalTime;
+import java.time.ZonedDateTime;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.TreeSet;
+
+@RequiredArgsConstructor
+@AllArgsConstructor
+@Data
+@Builder
+@Slf4j
+public class Order {
+ @NotNull
+ @Setter(AccessLevel.NONE)
+ private final String code;
+
+ @NotNull
+ private OrderStatus orderStatus;
+
+ @Builder.Default
+ @NotNull
+ private Sponsorship sponsorship = Sponsorship.NONE;
+
+ @Builder.Default
+ @NotNull
+ private ExtraDays extraDays = ExtraDays.NONE;
+
+ @NotNull
+ private final Set dailyDays;
+
+ @Builder.Default
+ private short roomCapacity = 0; // 0 = has no room
+
+ @Nullable
+ private String hotelLocation;
+
+ @NotNull
+ private String pretixOrderSecret;
+
+ //Manually defining getters/setters because lombok's default names are ugly lol
+ @Builder.Default
+ @Getter(AccessLevel.NONE)
+ @Setter(AccessLevel.NONE)
+ private boolean hasMembership = false;
+
+ @Builder.Default
+ private int answersMainPositionId = -1;
+
+ @Nullable
+ private User orderOwner; //TODO load from db! (lazy load?)
+
+ @NotNull
+ private Event orderEvent; //TODO load from db! (lazy load?)
+
+ @NotNull
+ @Setter(AccessLevel.NONE)
+ private Map answers;
+
+ public boolean isDaily() {
+ return !dailyDays.isEmpty();
+ }
+
+ public boolean hasDay(int day) {
+ return dailyDays.contains(day);
+ }
+
+ public boolean hasMembership() {
+ return hasMembership;
+ }
+
+ public void setMembership(boolean membership) {
+ this.hasMembership = membership;
+ }
+
+ @NotNull
+ public Optional