SEBSERV-2 #gbl, utils and tests

This commit is contained in:
anhefti 2018-11-14 15:14:40 +01:00
parent 25f829908e
commit f81a20bd03
12 changed files with 396 additions and 12 deletions

View file

@ -0,0 +1,26 @@
/*
* Copyright (c) 2018 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package ch.ethz.seb.sebserver.gbl.profile;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.context.annotation.Profile;
/** Profile annotation for SEB-Server dev-gui components.
*
* Use this as profile annotation on components that are only needed in the web-gui environment
* and only for development and/or testing */
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Profile({ "dev-gui", "test" })
public @interface DevGuiProfile {
}

View file

@ -0,0 +1,26 @@
/*
* Copyright (c) 2018 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package ch.ethz.seb.sebserver.gbl.profile;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.context.annotation.Profile;
/** Profile annotation for SEB-Server dev-ws components.
*
* Use this as profile annotation on components that are only needed in the web-service environment
* and only for development and/or testing */
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Profile({ "dev-ws", "test" })
public @interface DevWebServiceProfile {
}

View file

@ -15,6 +15,10 @@ import java.lang.annotation.Target;
import org.springframework.context.annotation.Profile;
/** Profile annotation for SEB-Server gui components.
*
* Use this as profile annotation on components that are only needed in the web-gui environment
* but for all vertical profiles like dev, prod and test */
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Profile({ "dev-gui", "prod-gui", "test" })

View file

@ -0,0 +1,26 @@
/*
* Copyright (c) 2018 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package ch.ethz.seb.sebserver.gbl.profile;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.context.annotation.Profile;
/** Profile annotation for SEB-Server prod-gui components.
*
* Use this as profile annotation on components that are only needed in the gui-service environment
* and only for production and/or testing */
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Profile({ "prod-gui", "test" })
public @interface ProdGuiProfile {
}

View file

@ -0,0 +1,26 @@
/*
* Copyright (c) 2018 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package ch.ethz.seb.sebserver.gbl.profile;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.context.annotation.Profile;
/** Profile annotation for SEB-Server prod-ws components.
*
* Use this as profile annotation on components that are only needed in the web-service environment
* and only for production and/or testing */
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Profile({ "prod-ws", "test" })
public @interface ProdWebServiceProfile {
}

View file

@ -15,6 +15,10 @@ import java.lang.annotation.Target;
import org.springframework.context.annotation.Profile;
/** Profile annotation for SEB-Server web-service components.
*
* Use this as profile annotation on components that are only needed in the web-service environment
* but for all vertical profiles like dev, prod and test */
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Profile({ "dev-ws", "prod-ws", "test" })

View file

@ -61,6 +61,10 @@ public final class Result<T> {
return this.error;
}
public boolean hasError() {
return this.error != null;
}
/** Use this to get the resulting value or (if null) to get a given other value
*
* @param other the other value to get if the computed value is null
@ -69,10 +73,19 @@ public final class Result<T> {
return this.value != null ? this.value : other;
}
/** Use this to get the resulting value or (if null) to get a given other value
*
* @param supplier supplier to get the value from if the computed value is null
* @return return either the computed value if existing or a given other value */
public T orElse(final Supplier<T> supplier) {
return this.value != null ? this.value : supplier.get();
}
/** Use this to map a given Result of type T to another Result of type U
* within a given mapping function.
*
* @param mapf the mapping function
* @return mapped Result of type U */
public <U> Result<U> map(final Function<? super T, ? extends U> mapf) {
if (this.error == null) {
return Result.of(mapf.apply(this.value));
@ -81,6 +94,11 @@ public final class Result<T> {
}
}
/** Use this to map a given Result of type T to another Result of type U
* within a given mapping function.
*
* @param mapf the mapping function
* @return mapped Result of type U */
public <U> Result<U> flatMap(final Function<? super T, Result<U>> mapf) {
if (this.error == null) {
return mapf.apply(this.value);
@ -89,18 +107,16 @@ public final class Result<T> {
}
}
/** Use this to get the resulting value. In an error case, a given error handling
* function is used that receives the error and returns a resulting value instead
* (or throw some error instead)
*
* @param errorHandler the error handling function
* @return */
public T onError(final Function<Throwable, T> errorHandler) {
return this.error != null ? errorHandler.apply(this.error) : this.value;
}
public static final <T> Result<T> of(final T value) {
return new Result<>(value);
}
public static final <T> Result<T> ofError(final Throwable error) {
return new Result<>(error);
}
/** Use this to get the resulting value of existing or throw an Runtime exception with
* given message otherwise.
*
@ -113,4 +129,23 @@ public final class Result<T> {
return this.value;
}
/** Use this to create a Result of a given resulting value.
*
* @param value resulting value
* @return Result instance contains a resulting value and no error */
public static final <T> Result<T> of(final T value) {
assert value != null : "value has null reference";
return new Result<>(value);
}
/** Use this to create a Result with error
*
* @param error the error that is wrapped within the created Result
* @return Result of specified error */
public static final <T> Result<T> ofError(final Throwable error) {
assert error != null : "error has null reference";
return new Result<>(error);
}
}

View file

@ -0,0 +1,60 @@
/*
* Copyright (c) 2018 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package ch.ethz.seb.sebserver.gbl.util;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;
public final class Utils {
/** Use this to extract a single element from a Collection. This also checks if there is only a single element
* within the Collection.
*
* @param collection the Collection to extract the single and only element
* @return The single element
* @throws IllegalArgumentException if the collection is null, or empty or has more then one element */
public static final <T> Result<T> getSingle(final Collection<T> collection) {
if (collection == null || collection.isEmpty() || collection.size() > 1) {
return Result.ofError(
new IllegalArgumentException(
"Collection has no or more then one element. Expected is exaclty one. Size: " +
((collection != null) ? collection.size() : "null")));
}
return Result.of(collection.iterator().next());
}
/** Use this to create an immutable Collection of specified type from varargs
*
* @param values elements of the new immutable Collection
* @return an immutable Collection of specified type with given elements */
@SafeVarargs
public static final <T> Collection<T> immutableCollectionOf(final T... values) {
if (values == null || values.length <= 0) {
return Collections.emptyList();
}
return Collections.unmodifiableCollection(Arrays.asList(values));
}
/** Use this to create an immutable Set of specified type from varargs
*
* @param values elements of the new immutable Set
* @return an immutable Set of specified type with given elements */
@SafeVarargs
public static final <T> Set<T> immutableSetOf(final T... items) {
if (items == null || items.length <= 0) {
return Collections.emptySet();
}
return Collections.unmodifiableSet(new LinkedHashSet<>(Arrays.asList(items)));
}
}

View file

@ -1,3 +1,4 @@
server.address=localhost
server.port=8080
server.servlet.context-path=/

View file

@ -1,7 +1,9 @@
server.address=localhost
server.port=8090
server.servlet.context-path=/api/
spring.datasource.initialize=true
spring.datasource.initialization-mode=always
spring.datasource.url=jdbc:mariadb://localhost:6603/SEBServer?useSSL=false&createDatabaseIfNotExist=true
spring.datasource.driver-class-name=org.mariadb.jdbc.Driver
spring.datasource.platform=dev
server.port=8090
spring.datasource.platform=dev

View file

@ -0,0 +1,103 @@
/*
* Copyright (c) 2018 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package ch.ethz.seb.sebserver.gbl.util;
import static org.junit.Assert.*;
import org.junit.Test;
public class ResultTest {
@Test
public void testCreate() {
final Result<String> of = Result.of("VALUE");
final Result<Object> ofError = Result.ofError(new RuntimeException("Some Error"));
assertNotNull(of.get());
assertEquals("VALUE", of.get());
assertTrue(ofError.hasError());
assertEquals("Some Error", ofError.getError().getMessage());
try {
Result.of(null);
fail("Exception expected");
} catch (final Throwable t) {
assertEquals("value has null reference", t.getMessage());
}
try {
Result.ofError(null);
fail("Exception expected");
} catch (final Throwable t) {
assertEquals("error has null reference", t.getMessage());
}
}
@Test
public void testMap() {
final Result<String> resultOf = Result.of("1");
final Result<String> resultOfError = Result.ofError(new RuntimeException("Some Error"));
final Result<Integer> numberResult = resultOf.map(r -> Integer.parseInt(r));
final Result<Integer> numberResultOfError = resultOfError.map(r -> Integer.parseInt(r));
assertNotNull(numberResult);
assertEquals(Integer.valueOf(1), numberResult.get());
assertTrue(numberResultOfError.hasError());
assertEquals("Some Error", numberResultOfError.getError().getMessage());
}
@Test
public void testFlatMap() {
final Result<String> resultOf = Result.of("1");
final Result<String> resultOfError = Result.ofError(new RuntimeException("Some Error"));
final Result<Integer> numberResult = resultOf.flatMap(r -> Result.of(Integer.parseInt(r)));
final Result<Integer> numberResultOfError = resultOfError.flatMap(r -> Result.of(Integer.parseInt(r)));
assertNotNull(numberResult);
assertEquals(Integer.valueOf(1), numberResult.get());
assertTrue(numberResultOfError.hasError());
assertEquals("Some Error", numberResultOfError.getError().getMessage());
}
@Test
public void testOrElse() {
final Result<String> resultOf = Result.of("ONE");
final Result<String> resultOfError = Result.ofError(new RuntimeException("Some Error"));
assertEquals("ONE", resultOf.orElse("TWO"));
assertEquals("TWO", resultOfError.orElse("TWO"));
assertEquals("ONE", resultOf.orElse(() -> "TWO"));
assertEquals("TWO", resultOfError.orElse(() -> "TWO"));
}
@Test
public void testOnError() {
final Result<String> resultOf = Result.of("ONE");
final Result<String> resultOfError = Result.ofError(new RuntimeException("Some Error"));
assertEquals("ONE", resultOf.onError(t -> t.getMessage()));
assertEquals("Some Error", resultOfError.onError(t -> t.getMessage()));
assertEquals("ONE", resultOf.onErrorThrow("Should not be thrown"));
try {
resultOfError.onErrorThrow("Should be thrown");
fail("Excpetion expected here");
} catch (final Throwable t) {
assertEquals("Should be thrown", t.getMessage());
assertEquals("Some Error", t.getCause().getMessage());
}
}
}

View file

@ -0,0 +1,71 @@
/*
* Copyright (c) 2018 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package ch.ethz.seb.sebserver.gbl.util;
import static org.junit.Assert.*;
import java.util.Collection;
import java.util.Collections;
import java.util.Set;
import org.junit.Test;
public class UtilsTest {
@Test
public void testGetSingle() {
final Collection<String> singleCollection = Utils.immutableCollectionOf("ONE");
final Collection<String> collection = Utils.immutableCollectionOf("ONE", "TWO");
final Result<String> r1 = Utils.getSingle(null);
final Result<String> r2 = Utils.getSingle(Collections.emptyList());
final Result<String> r3 = Utils.getSingle(singleCollection);
final Result<String> r4 = Utils.getSingle(collection);
assertTrue(r1.hasError());
assertTrue(r2.hasError());
assertFalse(r3.hasError());
assertTrue(r4.hasError());
assertEquals("ONE", r3.get());
assertEquals("Collection has no or more then one element. Expected is exaclty one. Size: null",
r1.getError().getMessage());
assertEquals("Collection has no or more then one element. Expected is exaclty one. Size: 2",
r4.getError().getMessage());
}
@Test
public void testImmutableCollectionOf() {
final Collection<String> r1 = Utils.immutableCollectionOf((String[]) null);
final Collection<String> r2 = Utils.immutableCollectionOf((String) null);
final Collection<String> r3 = Utils.immutableCollectionOf(null, null);
final Collection<String> r4 = Utils.immutableCollectionOf("ONE", "TWO");
assertEquals("[]", r1.toString());
assertEquals("[null]", r2.toString());
assertEquals("[null, null]", r3.toString());
assertEquals("[ONE, TWO]", r4.toString());
}
@Test
public void testImmutableSetOf() {
final Set<String> r1 = Utils.immutableSetOf((String[]) null);
final Set<String> r2 = Utils.immutableSetOf((String) null);
final Set<String> r3 = Utils.immutableSetOf(null, null);
final Set<String> r4 = Utils.immutableSetOf("ONE", "TWO");
final Set<String> r5 = Utils.immutableSetOf("ONE", "TWO", "ONE");
assertEquals("[]", r1.toString());
assertEquals("[null]", r2.toString());
assertEquals("[null]", r3.toString());
assertEquals("[ONE, TWO]", r4.toString());
assertEquals("[ONE, TWO]", r5.toString());
}
}