Skip to content

Commit

Permalink
fixed that "condition" query param could not be provided as form field
Browse files Browse the repository at this point in the history
* which prevented long conditions to be used via POST /search/things

Signed-off-by: Thomas Jäckle <thomas.jaeckle@beyonnex.io>
  • Loading branch information
thjaeckle committed Dec 21, 2023
1 parent 9f0138b commit a8c3033
Show file tree
Hide file tree
Showing 4 changed files with 134 additions and 25 deletions.
Expand Up @@ -44,7 +44,7 @@ public DefaultDittoHeadersValidator(final ActorSystem actorSystem, final Config
}

@Override
public CompletionStage<DittoHeaders> validate(DittoHeaders dittoHeaders) {
public CompletionStage<DittoHeaders> validate(final DittoHeaders dittoHeaders) {
checkNotNull(dittoHeaders, "dittoHeaders");
return validateSize(dittoHeaders).thenCompose(this::validateAuthorizationContext);
}
Expand Down
Expand Up @@ -25,16 +25,15 @@
import javax.annotation.Nullable;
import javax.annotation.concurrent.NotThreadSafe;

import org.apache.pekko.http.javadsl.model.HttpHeader;
import org.apache.pekko.http.javadsl.model.HttpMessage;
import org.apache.pekko.http.javadsl.server.RequestContext;
import org.eclipse.ditto.base.model.headers.DittoHeaders;
import org.eclipse.ditto.base.model.headers.DittoHeadersBuilder;
import org.eclipse.ditto.base.model.headers.translator.HeaderTranslator;
import org.eclipse.ditto.edge.service.headers.DittoHeadersValidator;
import org.eclipse.ditto.gateway.api.GatewayDuplicateHeaderException;

import org.apache.pekko.http.javadsl.model.HttpHeader;
import org.apache.pekko.http.javadsl.model.HttpMessage;
import org.apache.pekko.http.javadsl.server.RequestContext;

/**
* This class provides a fluent API for building a CompletionStage that eventually supplies the {@link DittoHeaders} for
* usage within the RootRoute.
Expand Down Expand Up @@ -233,7 +232,7 @@ CompletionStage<DittoHeaders> build(final CustomHeadersHandler.RequestType reque
// At this point it is ensured that a correlation ID was set
final String correlationId = dittoDefaultHeaders.getCorrelationId().orElseThrow();

CompletionStage<DittoHeaders> result = customHeadersHandler.handleCustomHeaders(correlationId,
final CompletionStage<DittoHeaders> result = customHeadersHandler.handleCustomHeaders(correlationId,
requestContext,
requestType,
dittoDefaultHeaders);
Expand Down
Expand Up @@ -22,17 +22,17 @@

import javax.annotation.Nullable;

import org.apache.pekko.http.javadsl.server.Directives;
import org.apache.pekko.http.javadsl.server.PathMatchers;
import org.apache.pekko.http.javadsl.server.RequestContext;
import org.apache.pekko.http.javadsl.server.Route;
import org.eclipse.ditto.base.model.headers.DittoHeaderDefinition;
import org.eclipse.ditto.base.model.headers.DittoHeaders;
import org.eclipse.ditto.gateway.service.endpoints.routes.AbstractRoute;
import org.eclipse.ditto.gateway.service.endpoints.routes.RouteBaseProperties;
import org.eclipse.ditto.thingsearch.model.signals.commands.query.CountThings;
import org.eclipse.ditto.thingsearch.model.signals.commands.query.QueryThings;

import org.apache.pekko.http.javadsl.server.Directives;
import org.apache.pekko.http.javadsl.server.PathMatchers;
import org.apache.pekko.http.javadsl.server.RequestContext;
import org.apache.pekko.http.javadsl.server.Route;

/**
* Builder for creating Pekko HTTP routes for {@code /search/things}.
*/
Expand Down Expand Up @@ -146,7 +146,9 @@ private Route searchThings(final RequestContext ctx, final DittoHeaders dittoHea
calculateNamespaces(
formFields.getOrDefault(ThingSearchParameter.NAMESPACES.toString(),
List.of())),
dittoHeaders
calculateSearchPostDittoHeaders(dittoHeaders,
formFields.getOrDefault(DittoHeaderDefinition.CONDITION.getKey(),
List.of()))
)
)
))
Expand Down Expand Up @@ -211,4 +213,20 @@ public static List<String> calculateOptions(final List<String> optionsString) {
.toList();
}

private static DittoHeaders calculateSearchPostDittoHeaders(final DittoHeaders dittoHeaders,
final List<String> conditionsString) {

if (conditionsString.isEmpty()) {
return dittoHeaders;
} else {
return dittoHeaders.toBuilder()
.condition(
conditionsString
.stream()
.collect(Collectors.joining(",", "and(", ")"))
)
.build();
}
}

}
Expand Up @@ -15,16 +15,22 @@
import static org.eclipse.ditto.json.assertions.DittoJsonAssertions.assertThat;

import java.util.List;
import java.util.Optional;
import java.util.function.Function;

import org.apache.pekko.http.javadsl.model.FormData;
import org.apache.pekko.http.javadsl.model.HttpRequest;
import org.apache.pekko.http.javadsl.model.StatusCodes;
import org.apache.pekko.http.javadsl.server.Route;
import org.apache.pekko.http.javadsl.testkit.TestRoute;
import org.apache.pekko.japi.Pair;
import org.eclipse.ditto.base.model.headers.WithDittoHeaders;
import org.eclipse.ditto.base.model.json.Jsonifiable;
import org.eclipse.ditto.gateway.service.endpoints.EndpointTestBase;
import org.eclipse.ditto.json.JsonKey;
import org.eclipse.ditto.json.JsonArray;
import org.eclipse.ditto.json.JsonObject;
import org.eclipse.ditto.json.JsonPointer;
import org.eclipse.ditto.json.JsonValue;
import org.junit.Before;
import org.junit.Test;

Expand All @@ -33,9 +39,30 @@
*/
public final class ThingSearchRouteTest extends EndpointTestBase {

private static final Function<Jsonifiable<?>, Optional<Object>> DUMMY_RESPONSE_PROVIDER =
m -> DummyThingModifyCommandResponse.echo((Jsonifiable<JsonValue>) () -> {
if (m instanceof WithDittoHeaders withDittoHeaders) {
return JsonObject.newBuilder()
.set("payload", m.toJson())
.set("headers", withDittoHeaders.getDittoHeaders()
.toBuilder()
.removeHeader("correlation-id")
.build()
.toJson()
)
.build();
} else {
return m.toJson();
}
});

private ThingSearchRoute thingsSearchRoute;
private TestRoute underTest;

@Override
protected Function<Jsonifiable<?>, Optional<Object>> getResponseProvider() {
return DUMMY_RESPONSE_PROVIDER;
}

@Before
public void setUp() {
Expand All @@ -48,17 +75,62 @@ public void setUp() {
public void postSearchThingsShouldGetParametersFromBody() {
final var formData = FormData.create(
List.of(
new Pair<>("filter", "and(like(definition,\"*test*\"))"),
new Pair<>("option", "sort(+thingId,-attributes/type)"),
new Pair<>("option","limit(0,5)"),
new Pair<>("namespaces","org.eclipse.ditto,foo.bar")
new Pair<>("filter", "and(like(definition,\"*test*\"))"),
new Pair<>("option", "sort(+thingId,-attributes/type)"),
new Pair<>("option", "limit(0,5)"),
new Pair<>("namespaces", "org.eclipse.ditto,foo.bar")
));
final var result = underTest.run(HttpRequest.POST("/search/things")
.withEntity(formData.toEntity()));

result.assertStatusCode(StatusCodes.OK);
result.assertEntity("{\"type\":\"thing-search.commands:queryThings\",\"filter\":\"and(and(like(definition,\\\"*test*\\\")))\",\"options\":[\"limit(0,5)\",\"sort(+thingId,-attributes/type)\"],\"namespaces\":[\"foo.bar\",\"org.eclipse.ditto\"]}");
result.assertEntity(JsonObject.newBuilder()
.set("payload", JsonObject.newBuilder()
.set("type", "thing-search.commands:queryThings")
.set("filter", "and(and(like(definition,\"*test*\")))")
.set("options", JsonArray.newBuilder()
.add("limit(0,5)")
.add("sort(+thingId,-attributes/type)")
.build()
)
.set("namespaces", JsonArray.newBuilder()
.add("foo.bar")
.add("org.eclipse.ditto")
.build()
)
.build()
)
.set("headers", JsonObject.empty())
.build()
.toString()
);
}

@Test
public void postSearchThingsShouldGetMultipleFiltersAndConditionsFromBody() {
final var formData = FormData.create(
List.of(
new Pair<>("filter", "like(definition,\"*test1*\")"),
new Pair<>("filter", "like(definition,\"*test2*\")"),
new Pair<>("condition", "like(definition,\"*test1*\")"),
new Pair<>("condition", "like(definition,\"*test2*\")")
));
final var result = underTest.run(HttpRequest.POST("/search/things")
.withEntity(formData.toEntity()));

result.assertStatusCode(StatusCodes.OK);
result.assertEntity(JsonObject.newBuilder()
.set("payload", JsonObject.newBuilder()
.set("type", "thing-search.commands:queryThings")
.set("filter", "and(like(definition,\"*test2*\"),like(definition,\"*test1*\"))")
.build()
)
.set("headers", JsonObject.newBuilder()
.set("condition", "and(like(definition,\"*test2*\"),like(definition,\"*test1*\"))")
.build()
).build().toString());
}

@Test
public void searchThingsShouldGetParametersFromUrl() {

Expand All @@ -67,24 +139,44 @@ public void searchThingsShouldGetParametersFromUrl() {
"namespaces=org.eclipse.ditto&" +
"fields=thingId&" +
"option=sort(%2Bfeature/property,-attributes/type,%2BthingId),size(2),cursor(nextCursor)")
);
);

result.assertStatusCode(StatusCodes.OK);
result.assertEntity("{\"type\":\"thing-search.commands:queryThings\",\"options\":[\"sort(+feature/property,-attributes/type,+thingId)\",\"size(2)\",\"cursor(nextCursor)\"],\"fields\":\"/thingId\",\"namespaces\":[\"org.eclipse.ditto\"]}");
result.assertEntity(JsonObject.newBuilder()
.set("payload", JsonObject.newBuilder()
.set("type", "thing-search.commands:queryThings")
.set("options", JsonArray.newBuilder()
.add("sort(+feature/property,-attributes/type,+thingId)")
.add("size(2)")
.add("cursor(nextCursor)")
.build()
)
.set("fields", "/thingId")
.set("namespaces", JsonArray.newBuilder()
.add("org.eclipse.ditto")
.build()
)
.build()
)
.set("headers", JsonObject.empty())
.build()
.toString()
);
}

@Test
public void searchThingsShouldGetFilter() {
final var form = "and(and(like(definition,\"*test*\")))";

final var result = underTest.run(
HttpRequest.GET("/search/things?filter=and(like(definition,\"*test*\"))&option=sort(+thingId)&option=limit(0,5)&namespaces=org.eclipse.ditto,foo.bar"));
HttpRequest.GET(
"/search/things?filter=and(like(definition,\"*test*\"))&option=sort(+thingId)&option=limit(0,5)&namespaces=org.eclipse.ditto,foo.bar"));

result.assertStatusCode(StatusCodes.OK);

assertThat(JsonObject.of(result.entityString()))
.contains(
JsonKey.of("filter"),
JsonPointer.of("payload/filter"),
form
);
}
Expand All @@ -95,16 +187,16 @@ public void countThingsShouldGetFilterFromBody() {
List.of(
new Pair<>("filter", "and(like(definition,\"*test*\"))"),
new Pair<>("option", "sort(+thingId)"),
new Pair<>("option","limit(0,5)"),
new Pair<>("namespaces","org.eclipse.ditto,foo.bar")
new Pair<>("option", "limit(0,5)"),
new Pair<>("namespaces", "org.eclipse.ditto,foo.bar")
));
final var result = underTest.run(HttpRequest.POST("/search/things/count")
.withEntity(formData.toEntity()));

result.assertStatusCode(StatusCodes.OK);
assertThat(JsonObject.of(result.entityString()))
.contains(
JsonKey.of("filter"),
JsonPointer.of("payload/filter"),
"and(and(like(definition,\"*test*\")))"
);
}
Expand Down

0 comments on commit a8c3033

Please sign in to comment.