Initial commit

This commit is contained in:
Sebastian Proksch 2025-11-13 22:26:09 +01:00
commit e4c00f10e8
37 changed files with 2512 additions and 0 deletions

76
server/pom.xml Normal file
View file

@ -0,0 +1,76 @@
<?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.5.7</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<groupId>csep</groupId>
<artifactId>server</artifactId>
<version>0.0.1-SNAPSHOT</version>
<properties>
<java.version>25</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>csep</groupId>
<artifactId>commons</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<!-- test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<version>3.3.0</version>
<configuration>
<configLocation>../checkstyle.xml</configLocation>
<sourceDirectories>${project.basedir}</sourceDirectories>
<includes>src/**/*.java,**/*.xml,**/*.yml</includes>
<excludes>**/target/**</excludes>
</configuration>
<executions>
<execution>
<id>validate</id>
<phase>validate</phase>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View file

@ -0,0 +1,30 @@
/*
* Copyright 2021 Delft University of Technology
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package server;
import java.util.Random;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class Config {
@Bean
public Random getRandom() {
return new Random();
}
}

View file

@ -0,0 +1,29 @@
/*
* Copyright 2021 Delft University of Technology
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package server;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
@SpringBootApplication
@EntityScan(basePackages = { "commons", "server" })
public class Main {
public static void main(String[] args) {
SpringApplication.run(Main.class, args);
}
}

View file

@ -0,0 +1,17 @@
package server;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
@RequestMapping("/")
public class SomeController {
@GetMapping("/")
@ResponseBody
public String index() {
return "Hello world!";
}
}

View file

@ -0,0 +1,52 @@
/**
* Copyright 2024 Sebastian Proksch
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package server.api;
import java.util.LinkedList;
import java.util.List;
import org.springframework.web.bind.annotation.GetMapping;
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.RestController;
import commons.Person;
@RestController
@RequestMapping("/api/people")
public class PersonListingController {
private List<Person> people = new LinkedList<>();
public PersonListingController() {
people.add(new Person("Mickey", "Mouse"));
people.add(new Person("Donald", "Duck"));
}
@GetMapping("/")
public List<Person> list() {
return people;
}
@PostMapping("/")
public List<Person> add(@RequestBody Person p) {
if (!people.contains(p)) {
people.add(p);
}
return people;
}
}

View file

@ -0,0 +1,79 @@
/*
* Copyright 2021 Delft University of Technology
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package server.api;
import java.util.List;
import java.util.Random;
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.RestController;
import commons.Quote;
import server.database.QuoteRepository;
@RestController
@RequestMapping("/api/quotes")
public class QuoteController {
private final Random random;
private final QuoteRepository repo;
public QuoteController(Random random, QuoteRepository repo) {
this.random = random;
this.repo = repo;
}
@GetMapping(path = { "", "/" })
public List<Quote> getAll() {
return repo.findAll();
}
@GetMapping("/{id}")
public ResponseEntity<Quote> getById(@PathVariable("id") long id) {
if (id < 0 || !repo.existsById(id)) {
return ResponseEntity.badRequest().build();
}
return ResponseEntity.ok(repo.findById(id).get());
}
@PostMapping(path = { "", "/" })
public ResponseEntity<Quote> add(@RequestBody Quote quote) {
if (quote.person == null || isNullOrEmpty(quote.person.firstName) || isNullOrEmpty(quote.person.lastName)
|| isNullOrEmpty(quote.quote)) {
return ResponseEntity.badRequest().build();
}
Quote saved = repo.save(quote);
return ResponseEntity.ok(saved);
}
private static boolean isNullOrEmpty(String s) {
return s == null || s.isEmpty();
}
@GetMapping("rnd")
public ResponseEntity<Quote> getRandom() {
var quotes = repo.findAll();
var idx = random.nextInt((int) repo.count());
return ResponseEntity.ok(quotes.get(idx));
}
}

View file

@ -0,0 +1,22 @@
/*
* Copyright 2021 Delft University of Technology
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package server.database;
import org.springframework.data.jpa.repository.JpaRepository;
import commons.Quote;
public interface QuoteRepository extends JpaRepository<Quote, Long> {}

View file

@ -0,0 +1,18 @@
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
# use one of these alternatives...
# ... purely in-memory, wiped on restart, but great for testing
#spring.datasource.url=jdbc:h2:mem:testdb
# ... persisted on disk (in project directory)
spring.datasource.url=jdbc:h2:file:./h2-database
# enable DB view on http://localhost:8080/h2-console
spring.h2.console.enabled=true
# strategy for table (re-)generation
spring.jpa.hibernate.ddl-auto=update
# show auto-generated SQL commands
#spring.jpa.hibernate.show_sql=true

View file

@ -0,0 +1,53 @@
/**
* Copyright 2024 Sebastian Proksch
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package server.api;
import static org.junit.jupiter.api.Assertions.assertEquals;
import java.util.List;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import commons.Person;
public class PersonListingControllerTest {
private static final Person MICKEY = new Person("Mickey", "Mouse");
private static final Person DONALD = new Person("Donald", "Duck");
private static final Person SCROOGE = new Person("Scrooge", "McDuck");
private PersonListingController sut;
@BeforeEach
public void setup() {
sut = new PersonListingController();
}
@Test
public void containsTwoDefaultNames() {
var actual = sut.list();
var expected = List.of(MICKEY, DONALD);
assertEquals(expected, actual);
}
@Test
public void canAddPeople() {
var actual = sut.add(SCROOGE);
var expected = List.of(MICKEY, DONALD, SCROOGE);
assertEquals(expected, actual);
}
}

View file

@ -0,0 +1,83 @@
/*
* Copyright 2021 Delft University of Technology
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package server.api;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.springframework.http.HttpStatus.BAD_REQUEST;
import java.util.Random;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import commons.Person;
import commons.Quote;
public class QuoteControllerTest {
public int nextInt;
private MyRandom random;
private TestQuoteRepository repo;
private QuoteController sut;
@BeforeEach
public void setup() {
random = new MyRandom();
repo = new TestQuoteRepository();
sut = new QuoteController(random, repo);
}
@Test
public void cannotAddNullPerson() {
var actual = sut.add(getQuote(null));
assertEquals(BAD_REQUEST, actual.getStatusCode());
}
@Test
public void randomSelection() {
sut.add(getQuote("q1"));
sut.add(getQuote("q2"));
nextInt = 1;
var actual = sut.getRandom();
assertTrue(random.wasCalled);
assertEquals("q2", actual.getBody().quote);
}
@Test
public void databaseIsUsed() {
sut.add(getQuote("q1"));
repo.calledMethods.contains("save");
}
private static Quote getQuote(String q) {
return new Quote(new Person(q, q), q);
}
@SuppressWarnings("serial")
public class MyRandom extends Random {
public boolean wasCalled = false;
@Override
public int nextInt(int bound) {
wasCalled = true;
return nextInt;
}
}
}

View file

@ -0,0 +1,225 @@
/*
* Copyright 2021 Delft University of Technology
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package server.api;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.function.Function;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery;
import commons.Quote;
import server.database.QuoteRepository;
public class TestQuoteRepository implements QuoteRepository {
public final List<Quote> quotes = new ArrayList<>();
public final List<String> calledMethods = new ArrayList<>();
private void call(String name) {
calledMethods.add(name);
}
@Override
public List<Quote> findAll() {
calledMethods.add("findAll");
return quotes;
}
@Override
public List<Quote> findAll(Sort sort) {
// TODO Auto-generated method stub
return null;
}
@Override
public List<Quote> findAllById(Iterable<Long> ids) {
// TODO Auto-generated method stub
return null;
}
@Override
public <S extends Quote> List<S> saveAll(Iterable<S> entities) {
// TODO Auto-generated method stub
return null;
}
@Override
public void flush() {
// TODO Auto-generated method stub
}
@Override
public <S extends Quote> S saveAndFlush(S entity) {
// TODO Auto-generated method stub
return null;
}
@Override
public <S extends Quote> List<S> saveAllAndFlush(Iterable<S> entities) {
// TODO Auto-generated method stub
return null;
}
@Override
public void deleteAllInBatch(Iterable<Quote> entities) {
// TODO Auto-generated method stub
}
@Override
public void deleteAllByIdInBatch(Iterable<Long> ids) {
// TODO Auto-generated method stub
}
@Override
public void deleteAllInBatch() {
// TODO Auto-generated method stub
}
@Override
public Quote getOne(Long id) {
// TODO Auto-generated method stub
return null;
}
@Override
public Quote getById(Long id) {
call("getById");
return find(id).get();
}
@Override
public Quote getReferenceById(Long id) {
call("getReferenceById");
return find(id).get();
}
private Optional<Quote> find(Long id) {
return quotes.stream().filter(q -> q.id == id).findFirst();
}
@Override
public <S extends Quote> List<S> findAll(Example<S> example) {
// TODO Auto-generated method stub
return null;
}
@Override
public <S extends Quote> List<S> findAll(Example<S> example, Sort sort) {
// TODO Auto-generated method stub
return null;
}
@Override
public Page<Quote> findAll(Pageable pageable) {
// TODO Auto-generated method stub
return null;
}
@Override
public <S extends Quote> S save(S entity) {
call("save");
entity.id = (long) quotes.size();
quotes.add(entity);
return entity;
}
@Override
public Optional<Quote> findById(Long id) {
// TODO Auto-generated method stub
return null;
}
@Override
public boolean existsById(Long id) {
call("existsById");
return find(id).isPresent();
}
@Override
public long count() {
return quotes.size();
}
@Override
public void deleteById(Long id) {
// TODO Auto-generated method stub
}
@Override
public void delete(Quote entity) {
// TODO Auto-generated method stub
}
@Override
public void deleteAllById(Iterable<? extends Long> ids) {
// TODO Auto-generated method stub
}
@Override
public void deleteAll(Iterable<? extends Quote> entities) {
// TODO Auto-generated method stub
}
@Override
public void deleteAll() {
// TODO Auto-generated method stub
}
@Override
public <S extends Quote> Optional<S> findOne(Example<S> example) {
// TODO Auto-generated method stub
return null;
}
@Override
public <S extends Quote> Page<S> findAll(Example<S> example, Pageable pageable) {
// TODO Auto-generated method stub
return null;
}
@Override
public <S extends Quote> long count(Example<S> example) {
// TODO Auto-generated method stub
return 0;
}
@Override
public <S extends Quote> boolean exists(Example<S> example) {
// TODO Auto-generated method stub
return false;
}
@Override
public <S extends Quote, R> R findBy(Example<S> example, Function<FetchableFluentQuery<S>, R> queryFunction) {
// TODO Auto-generated method stub
return null;
}
}