diff --git a/pom.xml b/pom.xml
index 04d3200..21a3a2d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -22,6 +22,7 @@
+
org.springframework.boot
spring-boot-starter-web
@@ -30,18 +31,12 @@
org.springframework.boot
spring-boot-starter-data-jdbc
-
+
- com.h2database
- h2
- runtime
+ com.fasterxml.jackson.dataformat
+ jackson-dataformat-csv
-
- org.projectlombok
- lombok
- true
-
-
+
org.springdoc
springdoc-openapi-ui
@@ -52,7 +47,18 @@
jackson-databind-nullable
0.2.6
-
+
+
+ com.h2database
+ h2
+ runtime
+
+
+
+ org.projectlombok
+ lombok
+ true
+
@@ -60,6 +66,11 @@
spring-boot-starter-test
test
+
+ org.mockito
+ mockito-core
+ test
+
@@ -79,7 +90,7 @@
spring
de.etecture.ga.api
- de.etecture.ga.model
+ de.etecture.ga.dto
ApiUtil.java
diff --git a/src/main/java/de/etecture/ga/api/GarageApiController.java b/src/main/java/de/etecture/ga/api/GarageApiController.java
new file mode 100644
index 0000000..6110bdc
--- /dev/null
+++ b/src/main/java/de/etecture/ga/api/GarageApiController.java
@@ -0,0 +1,46 @@
+/**
+ *
+ */
+package de.etecture.ga.api;
+
+import java.util.List;
+
+import org.springframework.http.ResponseEntity;
+
+import de.etecture.ga.dto.Termin;
+import de.etecture.ga.dto.TerminRequest;
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotNull;
+
+/**
+ *
+ */
+public class GarageApiController implements WerkstattApi {
+
+ @Override
+ public ResponseEntity getTermin(String werkstattId, String terminId) {
+ // TODO Auto-generated method stub
+ return WerkstattApi.super.getTermin(werkstattId, terminId);
+ }
+
+ @Override
+ public ResponseEntity> getTermine(String werkstattId, @Valid String von, @Valid String bis,
+ @Valid String leistungsId) {
+ // TODO Auto-generated method stub
+ return WerkstattApi.super.getTermine(werkstattId, von, bis, leistungsId);
+ }
+
+ @Override
+ public ResponseEntity> getTerminvorschlaege(String werkstattId, @NotNull @Valid String leistungsId,
+ @Valid String von, @Valid String bis) {
+ // TODO Auto-generated method stub
+ return WerkstattApi.super.getTerminvorschlaege(werkstattId, leistungsId, von, bis);
+ }
+
+ @Override
+ public ResponseEntity postTermin(String werkstattId, @Valid TerminRequest termin) {
+ // TODO Auto-generated method stub
+ return WerkstattApi.super.postTermin(werkstattId, termin);
+ }
+
+}
diff --git a/src/main/java/de/etecture/ga/config/Setup.java b/src/main/java/de/etecture/ga/config/Setup.java
new file mode 100644
index 0000000..645b766
--- /dev/null
+++ b/src/main/java/de/etecture/ga/config/Setup.java
@@ -0,0 +1,29 @@
+package de.etecture.ga.config;
+
+import java.util.List;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import de.etecture.ga.model.Garage;
+import de.etecture.ga.service.GarageImportService;
+import jakarta.annotation.PostConstruct;
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+@Component
+public class Setup {
+
+ @Autowired
+ private GarageImportService importService;
+
+ @PostConstruct
+ private void setupData() {
+
+ List garages = importService.importGarageData();
+
+ // Daten in DB übertragen
+
+ }
+
+}
diff --git a/src/main/java/de/etecture/ga/model/Appointment.java b/src/main/java/de/etecture/ga/model/Appointment.java
new file mode 100644
index 0000000..34d9559
--- /dev/null
+++ b/src/main/java/de/etecture/ga/model/Appointment.java
@@ -0,0 +1,25 @@
+package de.etecture.ga.model;
+
+import java.time.Duration;
+import java.util.Date;
+
+import org.springframework.data.annotation.Id;
+
+import lombok.Data;
+
+@Data
+public class Appointment {
+
+ @Id
+ private Long id;
+
+ private Garage garage;
+
+ private String serviceCode;
+
+ private String serviceName;
+
+ private Date appointmentTime;
+
+ private Duration duration;
+}
diff --git a/src/main/java/de/etecture/ga/model/Garage.java b/src/main/java/de/etecture/ga/model/Garage.java
new file mode 100644
index 0000000..4afbed7
--- /dev/null
+++ b/src/main/java/de/etecture/ga/model/Garage.java
@@ -0,0 +1,29 @@
+package de.etecture.ga.model;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.springframework.data.annotation.Id;
+
+import lombok.Data;
+
+@Data
+public class Garage {
+
+ @Id
+ private Long id;
+
+ private String name;
+
+ private List appointments;
+
+ public Garage addAppointment(Appointment appointment) {
+
+ if(this.appointments == null) {
+ this.appointments = new ArrayList<>();
+ }
+
+ this.appointments.add(appointment);
+ return this;
+ }
+}
diff --git a/src/main/java/de/etecture/ga/model/MDService.java b/src/main/java/de/etecture/ga/model/MDService.java
new file mode 100644
index 0000000..2981a4c
--- /dev/null
+++ b/src/main/java/de/etecture/ga/model/MDService.java
@@ -0,0 +1,20 @@
+package de.etecture.ga.model;
+
+import java.time.Duration;
+
+import org.springframework.data.annotation.Id;
+
+import lombok.Data;
+
+@Data
+public class MDService {
+
+ @Id
+ private Long id;
+
+ private String code;
+
+ private String name;
+
+ private Duration duration;
+}
diff --git a/src/main/java/de/etecture/ga/service/GarageImportService.java b/src/main/java/de/etecture/ga/service/GarageImportService.java
new file mode 100644
index 0000000..f82a923
--- /dev/null
+++ b/src/main/java/de/etecture/ga/service/GarageImportService.java
@@ -0,0 +1,105 @@
+package de.etecture.ga.service;
+
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.stream.Stream;
+
+import org.springframework.stereotype.Service;
+
+import com.fasterxml.jackson.databind.MappingIterator;
+import com.fasterxml.jackson.dataformat.csv.CsvMapper;
+import com.fasterxml.jackson.dataformat.csv.CsvSchema;
+
+import de.etecture.ga.model.Appointment;
+import de.etecture.ga.model.Garage;
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+@Service
+public class GarageImportService {
+
+ private static final String IMPORT_FOLDER = "import";
+
+
+ public List importGarageData() {
+
+ List importData = new ArrayList<>();
+
+ try (Stream files = Files
+ .list(Paths.get(getClass().getClassLoader().getResource(IMPORT_FOLDER).toURI()))) {
+
+ files.filter(Files::isRegularFile).filter(path -> path.toString().endsWith(".csv"))
+ .map(this::loadGarageData).mapMulti(Optional::ifPresent).forEach(importData::add);
+
+ } catch (IOException | URISyntaxException e) {
+ log.error("Can't read file", e);
+ }
+
+ return importData;
+ }
+
+ public Optional loadGarageData(final Path file) {
+
+ Garage garage = new Garage();
+ garage.setName(getGarageNameFromFile(file));
+
+ List appointments = loadObjectsFromFile(file);
+ appointments.stream().map(data -> this.fromCSVData(data, garage)).filter(Objects::nonNull)
+ .forEach(garage::addAppointment);
+
+ return Optional.of(garage);
+ }
+
+ private String getGarageNameFromFile(final Path fileName) {
+
+ String name = fileName.getFileName().toString();
+
+ int pos = name.lastIndexOf(".");
+ if (pos > 0 && pos < (name.length() - 1)) {
+ name = name.substring(0, pos);
+ }
+
+ return name;
+ }
+
+ private List loadObjectsFromFile(final Path file) {
+
+ try {
+ CsvSchema bootstrapSchema = CsvSchema.emptySchema().withHeader();
+ CsvMapper mapper = new CsvMapper();
+ MappingIterator readValues = mapper.readerFor(CSVData.class).with(bootstrapSchema)
+ .readValues(file.toFile());
+
+ return readValues.readAll();
+
+ } catch (Exception e) {
+ log.error("Error occurred while loading object list from file {}", file, e);
+ return Collections.emptyList();
+ }
+ }
+
+ private Appointment fromCSVData(CSVData data, Garage forGarage) {
+
+ if (data == null)
+ return null;
+
+ Appointment appointment = new Appointment();
+ appointment.setGarage(forGarage);
+ appointment.setAppointmentTime(data.APP_DATE);
+ appointment.setServiceCode(data.SERVICE);
+
+ return appointment;
+ }
+
+ private record CSVData(Date APP_DATE, String SERVICE) {
+ }
+}
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
index 7aa7eca..4d495be 100644
--- a/src/main/resources/application.properties
+++ b/src/main/resources/application.properties
@@ -1 +1,10 @@
spring.application.name=Garage appointment management
+
+# Datasource settings
+spring.datasource.url=jdbc:h2:mem:etecture
+spring.datasource.driverClassName=org.h2.Driver
+spring.datasource.username=sa
+spring.datasource.password=password
+
+# init DB by script
+spring.sql.init.mode=always
\ No newline at end of file
diff --git a/src/main/resources/termine/autohaus-schmidt.csv b/src/main/resources/import/autohaus-schmidt.csv
similarity index 100%
rename from src/main/resources/termine/autohaus-schmidt.csv
rename to src/main/resources/import/autohaus-schmidt.csv
diff --git a/src/main/resources/termine/meisterbetrieb-bachstraße.csv b/src/main/resources/import/meisterbetrieb-bachstraße.csv
similarity index 100%
rename from src/main/resources/termine/meisterbetrieb-bachstraße.csv
rename to src/main/resources/import/meisterbetrieb-bachstraße.csv
diff --git a/src/test/java/de/etecture/ga/api/GarageApiControllerTest.java b/src/test/java/de/etecture/ga/api/GarageApiControllerTest.java
new file mode 100644
index 0000000..6b27d58
--- /dev/null
+++ b/src/test/java/de/etecture/ga/api/GarageApiControllerTest.java
@@ -0,0 +1,53 @@
+package de.etecture.ga.api;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.fail;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.MvcResult;
+import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
+import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
+import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
+
+/**
+ * Integrationtest for the {@link WerkstattApi}
+ */
+@SpringBootTest
+@AutoConfigureMockMvc
+class GarageApiControllerTest {
+
+ @Autowired
+ private MockMvc mockMvc;
+
+ @Test
+ void testGetTermin() throws Exception {
+
+ MvcResult mvcResult = this.mockMvc.perform(MockMvcRequestBuilders.get("/werkstatt/{werkstattId}/termin/{terminId}", "1", "1"))
+ .andDo(MockMvcResultHandlers.print())
+ .andExpect(MockMvcResultMatchers.status().isOk())
+// .andExpect(MockMvcResultMatchers.jsonPath("$.message").value("Hello World!!!"))
+ .andReturn();
+
+ assertEquals("application/json;charset=UTF-8", mvcResult.getResponse().getContentType());
+ }
+
+ @Test
+ void testGetTermine() {
+ fail("Not yet implemented");
+ }
+
+ @Test
+ void testGetTerminvorschlaege() {
+ fail("Not yet implemented");
+ }
+
+ @Test
+ void testPostTermin() {
+ fail("Not yet implemented");
+ }
+
+}
diff --git a/src/test/java/de/etecture/ga/service/GarageImportServiceTest.java b/src/test/java/de/etecture/ga/service/GarageImportServiceTest.java
new file mode 100644
index 0000000..b165d6f
--- /dev/null
+++ b/src/test/java/de/etecture/ga/service/GarageImportServiceTest.java
@@ -0,0 +1,40 @@
+package de.etecture.ga.service;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.net.URISyntaxException;
+import java.util.List;
+
+import org.junit.jupiter.api.MethodOrderer;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestMethodOrder;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.InjectMocks;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import de.etecture.ga.model.Appointment;
+import de.etecture.ga.model.Garage;
+import lombok.extern.slf4j.Slf4j;
+
+@ExtendWith(MockitoExtension.class)
+@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
+@Slf4j
+class GarageImportServiceTest {
+
+ @InjectMocks
+ private GarageImportService service;
+
+
+ @Test
+ void testImportGarageData() throws URISyntaxException {
+
+ List garageData = service.importGarageData();
+
+ assertThat(garageData).isNotEmpty().hasOnlyElementsOfType(Garage.class).hasSize(1);
+
+ Garage garageToTest = garageData.get(0);
+ assertThat(garageToTest.getName()).isEqualTo("test_data");
+ assertThat(garageToTest.getAppointments()).hasOnlyElementsOfType(Appointment.class).hasSize(19);
+ }
+
+}
diff --git a/src/test/resources/application.properties b/src/test/resources/application.properties
new file mode 100644
index 0000000..2cc4925
--- /dev/null
+++ b/src/test/resources/application.properties
@@ -0,0 +1,10 @@
+spring.application.name=Garage appointment management - test
+
+# Datasource settings
+spring.datasource.url=jdbc:h2:mem:etecture_test
+spring.datasource.driverClassName=org.h2.Driver
+spring.datasource.username=sa
+spring.datasource.password=password
+
+# init DB by script
+spring.sql.init.mode=always
\ No newline at end of file
diff --git a/src/test/resources/import/test_data.csv b/src/test/resources/import/test_data.csv
new file mode 100644
index 0000000..f3b382e
--- /dev/null
+++ b/src/test/resources/import/test_data.csv
@@ -0,0 +1,20 @@
+APP_DATE,SERVICE
+2019-01-02T13:00Z,WHE
+2019-01-02T15:00Z,WHE
+2019-01-04T08:15Z,MOT
+2019-01-04T09:00Z,OIL
+2019-01-04T09:00Z,OIL
+2019-01-05T10:15Z,MOT
+2019-01-05T13:00Z,WHE
+2019-01-07T08:15Z,OIL
+2019-01-07T08:45Z,OIL
+2019-01-08T09:15Z,MOT
+2019-01-08T09:30Z,MOT
+2019-01-08T08:00Z,WHE
+2019-01-09T08:30Z,WHE
+2019-01-09T10:45Z,OIL
+2019-01-09T13:00Z,OIL
+2019-01-10T15:15Z,WHE
+2019-01-10T14:10Z,MOT
+2019-01-11T12:15Z,OIL
+2019-01-11T08:15Z,WHE