find Person using HTTP GET in CreateSelfDebitorForPartner instead of using the UUID directly

This commit is contained in:
Michael Hoennig 2024-10-25 15:09:39 +02:00
parent 87e8576ce7
commit e12d0b0ba2
4 changed files with 112 additions and 5 deletions

View File

@ -112,7 +112,7 @@ class HsOfficeScenarioTests extends ScenarioTest {
@Produces("Debitor: Test AG - main debitor") @Produces("Debitor: Test AG - main debitor")
void shouldCreateSelfDebitorForPartner() { void shouldCreateSelfDebitorForPartner() {
new CreateSelfDebitorForPartner(this, "Debitor: Test AG - main debitor") new CreateSelfDebitorForPartner(this, "Debitor: Test AG - main debitor")
.given("partnerPersonUuid", "%{Person: Test AG}") .given("partnerPersonTradeName", "Test AG")
.given("billingContactCaption", "Test AG - billing department") .given("billingContactCaption", "Test AG - billing department")
.given("billingContactEmailAddress", "billing@test-ag.example.org") .given("billingContactEmailAddress", "billing@test-ag.example.org")
.given("debitorNumberSuffix", "00") // TODO.impl: could be assigned automatically, but is not yet .given("debitorNumberSuffix", "00") // TODO.impl: could be assigned automatically, but is not yet

View File

@ -179,12 +179,12 @@ public abstract class ScenarioTest extends ContextBasedTest {
return map; return map;
} }
static String resolve(final String text) { public static String resolve(final String text) {
final var resolved = new TemplateResolver(text, ScenarioTest.knowVariables()).resolve(); final var resolved = new TemplateResolver(text, ScenarioTest.knowVariables()).resolve();
return resolved; return resolved;
} }
static Object resolveTyped(final String text) { public static Object resolveTyped(final String text) {
final var resolved = resolve(text); final var resolved = resolve(text);
try { try {
return UUID.fromString(resolved); return UUID.fromString(resolved);

View File

@ -1,6 +1,9 @@
package net.hostsharing.hsadminng.hs.office.scenarios; package net.hostsharing.hsadminng.hs.office.scenarios;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.restassured.http.ContentType; import io.restassured.http.ContentType;
import lombok.Getter;
import lombok.SneakyThrows; import lombok.SneakyThrows;
import net.hostsharing.hsadminng.reflection.AnnotationFinder; import net.hostsharing.hsadminng.reflection.AnnotationFinder;
import org.apache.commons.collections4.map.LinkedMap; import org.apache.commons.collections4.map.LinkedMap;
@ -14,6 +17,7 @@ import java.net.URI;
import java.net.http.HttpClient; import java.net.http.HttpClient;
import java.net.http.HttpRequest; import java.net.http.HttpRequest;
import java.net.http.HttpRequest.BodyPublishers; import java.net.http.HttpRequest.BodyPublishers;
import java.net.http.HttpResponse;
import java.net.http.HttpResponse.BodyHandlers; import java.net.http.HttpResponse.BodyHandlers;
import java.time.Duration; import java.time.Duration;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
@ -22,6 +26,7 @@ import java.util.UUID;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.Supplier; import java.util.function.Supplier;
import static java.net.URLEncoder.encode;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.platform.commons.util.StringUtils.isBlank; import static org.junit.platform.commons.util.StringUtils.isBlank;
import static org.junit.platform.commons.util.StringUtils.isNotBlank; import static org.junit.platform.commons.util.StringUtils.isNotBlank;
@ -29,8 +34,9 @@ import static org.junit.platform.commons.util.StringUtils.isNotBlank;
public abstract class UseCase<T extends UseCase<?>> { public abstract class UseCase<T extends UseCase<?>> {
private static final HttpClient client = HttpClient.newHttpClient(); private static final HttpClient client = HttpClient.newHttpClient();
final ObjectMapper objectMapper = new ObjectMapper();
private final ScenarioTest testSuite; protected final ScenarioTest testSuite;
private final Map<String, Function<String, UseCase<?>>> requirements = new LinkedMap<>(); private final Map<String, Function<String, UseCase<?>>> requirements = new LinkedMap<>();
private final String resultAlias; private final String resultAlias;
private final Map<String, Object> givenProperties = new LinkedHashMap<>(); private final Map<String, Object> givenProperties = new LinkedHashMap<>();
@ -76,17 +82,35 @@ public abstract class UseCase<T extends UseCase<?>> {
return new JsonTemplate(jsonTemplate); return new JsonTemplate(jsonTemplate);
} }
public final void keep(final String alias, final Supplier<HttpResponse> http, final Function<HttpResponse, String> extractor) {
this.nextTitle = ScenarioTest.resolve(alias);
http.get().keep(extractor);
this.nextTitle = null;
}
public final void keep(final String alias, final Supplier<HttpResponse> http) { public final void keep(final String alias, final Supplier<HttpResponse> http) {
this.nextTitle = ScenarioTest.resolve(alias); this.nextTitle = ScenarioTest.resolve(alias);
http.get().keep(); http.get().keep();
this.nextTitle = null; this.nextTitle = null;
} }
@SneakyThrows
public final HttpResponse httpGet(final String uriPath) {
final var request = HttpRequest.newBuilder()
.GET()
.uri(new URI("http://localhost:" + testSuite.port + uriPath))
.header("current-subject", ScenarioTest.RUN_AS_USER)
.timeout(Duration.ofSeconds(10))
.build();
final var response = client.send(request, BodyHandlers.ofString());
return new HttpResponse(HttpMethod.POST, uriPath, null, response);
}
@SneakyThrows @SneakyThrows
public final HttpResponse httpPost(final String uriPath, final JsonTemplate bodyJsonTemplate) { public final HttpResponse httpPost(final String uriPath, final JsonTemplate bodyJsonTemplate) {
final var requestBody = bodyJsonTemplate.resolvePlaceholders(); final var requestBody = bodyJsonTemplate.resolvePlaceholders();
final var request = HttpRequest.newBuilder() final var request = HttpRequest.newBuilder()
.method(HttpMethod.POST.toString(), BodyPublishers.ofString(requestBody)) .POST(BodyPublishers.ofString(requestBody))
.uri(new URI("http://localhost:" + testSuite.port + uriPath)) .uri(new URI("http://localhost:" + testSuite.port + uriPath))
.header("Content-Type", "application/json") .header("Content-Type", "application/json")
.header("current-subject", ScenarioTest.RUN_AS_USER) .header("current-subject", ScenarioTest.RUN_AS_USER)
@ -127,6 +151,10 @@ public abstract class UseCase<T extends UseCase<?>> {
return ScenarioTest.uuid(alias); return ScenarioTest.uuid(alias);
} }
public String uriEncoded(final String text) {
return encode(ScenarioTest.resolve(text));
}
public static class JsonTemplate { public static class JsonTemplate {
private final String template; private final String template;
@ -142,8 +170,12 @@ public abstract class UseCase<T extends UseCase<?>> {
public class HttpResponse { public class HttpResponse {
@Getter
private final java.net.http.HttpResponse<String> response; private final java.net.http.HttpResponse<String> response;
@Getter
private final HttpStatus status; private final HttpStatus status;
private UUID locationUuid; private UUID locationUuid;
public HttpResponse( public HttpResponse(
@ -187,6 +219,16 @@ public abstract class UseCase<T extends UseCase<?>> {
return this; return this;
} }
public void keep(final Function<HttpResponse, String> extractor) {
final var alias = nextTitle != null ? nextTitle : resultAlias;
assertThat(alias).as("cannot keep result, no alias found").isNotNull();
final var value = extractor.apply(this);
ScenarioTest.putAlias(
alias,
new ScenarioTest.Alias<>(UseCase.this.getClass(), UUID.fromString(value)));
}
public void keep() { public void keep() {
final var alias = nextTitle != null ? nextTitle : resultAlias; final var alias = nextTitle != null ? nextTitle : resultAlias;
assertThat(alias).as("cannot keep result, no alias found").isNotNull(); assertThat(alias).as("cannot keep result, no alias found").isNotNull();
@ -194,6 +236,12 @@ public abstract class UseCase<T extends UseCase<?>> {
alias, alias,
new ScenarioTest.Alias<>(UseCase.this.getClass(), locationUuid)); new ScenarioTest.Alias<>(UseCase.this.getClass(), locationUuid));
} }
@SneakyThrows
public String getFromBody(final String path) {
final var rootNode = objectMapper.readTree(response.body());
return getPropertyFromJson(rootNode, path);
}
} }
public void print(final String output) { public void print(final String output) {
@ -231,4 +279,55 @@ public abstract class UseCase<T extends UseCase<?>> {
private final String title(String resultAlias) { private final String title(String resultAlias) {
return getClass().getSimpleName().replaceAll("([a-z])([A-Z]+)", "$1 $2") + " => " + resultAlias; return getClass().getSimpleName().replaceAll("([a-z])([A-Z]+)", "$1 $2") + " => " + resultAlias;
} }
// FIXME: refactor to own class
/**
* Extracts a property from a JsonNode based on a dotted path.
* Supports array notation like "users[0].address.city" and root arrays like "[0].user.address.city".
*
* @param rootNode the root JsonNode
* @param propertyPath the property path in dot notation (e.g., "[0].user.address.city")
* @return the extracted property value as a String
*/
public static String getPropertyFromJson(final JsonNode rootNode, final String propertyPath) {
final var pathParts = propertyPath.split("\\.");
var currentNode = rootNode;
// Traverse the JSON structure based on the path parts
for (final var part : pathParts) {
// Check if the part contains array notation like "[0]"
if (part.contains("[")) {
String arrayName;
final var arrayIndex = Integer.parseInt(part.substring(part.indexOf("[") + 1, part.indexOf("]")));
if (part.startsWith("[")) {
// This is a root-level array access (e.g., "[0]")
arrayName = null;
} else {
// This is a nested array access (e.g., "users[0]")
arrayName = part.substring(0, part.indexOf("["));
}
// If there's an array name, traverse to it
if (arrayName != null) {
currentNode = currentNode.path(arrayName);
}
// Ensure the current node is an array, then access the element at the index
if (currentNode.isArray()) {
currentNode = currentNode.get(arrayIndex);
}
} else {
// Traverse as a normal field
currentNode = currentNode.path(part);
}
// If at any point, the node is missing, return null
if (currentNode.isMissingNode()) {
return null;
}
}
return currentNode.asText(); // Return the final value as a String
}
} }

View File

@ -5,6 +5,7 @@ import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest;
import static io.restassured.http.ContentType.JSON; import static io.restassured.http.ContentType.JSON;
import static org.springframework.http.HttpStatus.CREATED; import static org.springframework.http.HttpStatus.CREATED;
import static org.springframework.http.HttpStatus.OK;
public class CreateSelfDebitorForPartner extends UseCase<CreateSelfDebitorForPartner> { public class CreateSelfDebitorForPartner extends UseCase<CreateSelfDebitorForPartner> {
@ -14,6 +15,12 @@ public class CreateSelfDebitorForPartner extends UseCase<CreateSelfDebitorForPar
@Override @Override
protected HttpResponse run() { protected HttpResponse run() {
keep("partnerPersonUuid", () ->
httpGet("/api/hs/office/relations?personData=" + uriEncoded("%{partnerPersonTradeName}"))
.expecting(OK).expecting(JSON),
response -> response.getFromBody("[0].holder.uuid")
);
keep("BankAccount: Test AG - refund bank account", () -> keep("BankAccount: Test AG - refund bank account", () ->
httpPost("/api/hs/office/bankaccounts", usingJsonBody(""" httpPost("/api/hs/office/bankaccounts", usingJsonBody("""
{ {
@ -57,4 +64,5 @@ public class CreateSelfDebitorForPartner extends UseCase<CreateSelfDebitorForPar
""")) """))
.expecting(CREATED).expecting(JSON); .expecting(CREATED).expecting(JSON);
} }
} }