Skip to content

Commit

Permalink
[rest] add semantics query to item resource
Browse files Browse the repository at this point in the history
Signed-off-by: Jan N. Klug <github@klug.nrw>
  • Loading branch information
J-N-K committed Oct 30, 2022
1 parent c24f0f3 commit 50b7a2e
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 6 deletions.
9 changes: 4 additions & 5 deletions bundles/org.openhab.core.io.rest.core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,13 @@
</dependency>
<dependency>
<groupId>org.openhab.core.bundles</groupId>
<artifactId>org.openhab.core.test</artifactId>
<artifactId>org.openhab.core.semantics</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
<groupId>org.openhab.core.bundles</groupId>
<artifactId>org.openhab.core.test</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.RawType;
import org.openhab.core.library.types.UpDownType;
import org.openhab.core.semantics.SemanticTags;
import org.openhab.core.semantics.SemanticsPredicates;
import org.openhab.core.types.Command;
import org.openhab.core.types.State;
import org.openhab.core.types.TypeParser;
Expand Down Expand Up @@ -776,6 +778,34 @@ public Response createOrUpdateItems(
return JSONResponse.createResponse(Status.OK, responseList, null);
}

@GET
@RolesAllowed({ Role.USER, Role.ADMIN })
@Path("/{itemName: \\w+}/semantic/{semanticClass: \\w+}")
@Operation(operationId = "getSemanticItem", summary = "Gets the item which defines the requested semantics of an item.", responses = {
@ApiResponse(responseCode = "200", description = "OK"),
@ApiResponse(responseCode = "404", description = "Item not found") })
public Response getSemanticItem(final @Context UriInfo uriInfo, final @Context HttpHeaders httpHeaders,
@HeaderParam(HttpHeaders.ACCEPT_LANGUAGE) @Parameter(description = "language") @Nullable String language,
@PathParam("itemName") @Parameter(description = "item name") String itemName,
@PathParam("semanticClass") @Parameter(description = "semantic class") String semanticClassName) {
Locale locale = localeService.getLocale(language);

Class<? extends org.openhab.core.semantics.Tag> semanticClass = SemanticTags.getById(semanticClassName);
if (semanticClass == null) {
return Response.status(Status.NOT_FOUND.getStatusCode()).build();
}

Item foundItem = findParentByTag(getItem(itemName), SemanticsPredicates.isA(semanticClass));
if (foundItem == null) {
return Response.status(Status.NOT_FOUND.getStatusCode()).build();
}

EnrichedItemDTO dto = EnrichedItemDTOMapper.map(foundItem, false, null, uriBuilder(uriInfo, httpHeaders),
locale);
dto.editable = isEditable(dto.name);
return JSONResponse.createResponse(Status.OK, dto, null);
}

private JsonObject buildStatusObject(String itemName, String status, @Nullable String message) {
JsonObject jo = new JsonObject();
jo.addProperty("name", itemName);
Expand All @@ -784,6 +814,20 @@ private JsonObject buildStatusObject(String itemName, String status, @Nullable S
return jo;
}

private @Nullable Item findParentByTag(@Nullable Item item, Predicate<Item> predicate) {
if (item == null) {
return null;
}

if (predicate.test(item)) {
return item;
}

// check parents
return item.getGroupNames().stream().map(this::getItem).map(i -> findParentByTag(i, predicate))
.filter(Objects::nonNull).findAny().orElse(null);
}

/**
* helper: Response to be sent to client if a Thing cannot be found
*
Expand Down
5 changes: 4 additions & 1 deletion itests/org.openhab.core.io.rest.core.tests/itest.bndrun
Original file line number Diff line number Diff line change
Expand Up @@ -98,4 +98,7 @@ Fragment-Host: org.openhab.core.io.rest.core
org.openhab.core.persistence;version='[3.4.0,3.4.1)',\
org.openhab.core.test;version='[3.4.0,3.4.1)',\
org.openhab.core.thing;version='[3.4.0,3.4.1)',\
org.openhab.core.transform;version='[3.4.0,3.4.1)'
org.openhab.core.transform;version='[3.4.0,3.4.1)',\
junit-jupiter-params;version='[5.8.1,5.8.2)',\
org.openhab.core.semantics;version='[3.4.0,3.4.1)',\
org.osgi.service.cm;version='[1.6.0,1.6.1)'
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.stream.Stream;

import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.Response;
Expand All @@ -35,9 +36,14 @@
import javax.ws.rs.core.UriInfo;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.hamcrest.Matcher;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.mockito.junit.jupiter.MockitoSettings;
Expand Down Expand Up @@ -333,4 +339,36 @@ public void testRemoveMetadataUnmanagedMetadata() {
Response response = itemResource.removeMetadata(ITEM_NAME1, "namespace");
assertEquals(409, response.getStatus());
}

@SuppressWarnings("unused")
private static Stream<Arguments> findTagTestSource() {
return Stream.of( //
Arguments.of(ITEM_NAME3, "Location", hasItems(ITEM_NAME1)), // super-class of set tag
Arguments.of(ITEM_NAME3, "HVAC", hasItems(ITEM_NAME2)), // tag
Arguments.of(ITEM_NAME3, "Point", hasItems(ITEM_NAME3)), // self
Arguments.of(ITEM_NAME3, "NotATag", null), // invalid tag
Arguments.of(ITEM_NAME3, "Outdoor", null) // valid tag, not present
);
}

@ParameterizedTest
@MethodSource("findTagTestSource")
public void findTagTest(String itemName, String semanticClassName, @Nullable Matcher<Iterable> matcher)
throws IOException {
// setup test: item1 has the location, item2 the equipment, item3 is the point
item1.addTag("Office");
item2.addTag("HVAC");
item2.addGroupName(ITEM_NAME1);
item3.addTag("Point");
item3.addGroupName(ITEM_NAME2);

// do test
Response response = itemResource.getSemanticItem(uriInfoMock, httpHeadersMock, null, itemName,
semanticClassName);
if (matcher != null) {
assertThat(readItemNamesFromResponse(response), matcher);
} else {
assertThat(response.getStatus(), is(404));
}
}
}

0 comments on commit 50b7a2e

Please sign in to comment.