From ee04d538ad3b94b7fce0ca6b0240e9b517401fc8 Mon Sep 17 00:00:00 2001 From: "Signed-off-by: g.dimitropoulos" Date: Fri, 28 Jul 2023 14:59:45 +0200 Subject: [PATCH 1/4] Implement gateway filter for JDBC (H2 & Postgresql) device registry: - add optional parameter "isGateway" - functionality for listing only devices or gateways - add documentation Also-by: Matthias Feurer m.feurer@sotec.eu Signed-off-by: g.dimitropoulos --- .../PubSubBasedInternalCommandSender.java | 5 +- .../util/RegistryManagementConstants.java | 7 +- .../service/http/AbstractHttpEndpoint.java | 9 +- .../store/device/TableManagementStore.java | 162 +++++++++++------- .../base/jdbc/store/device/base.h2.sql.yaml | 56 +++++- .../store/device/base.postgresql.sql.yaml | 62 ++++++- .../AbstractDeviceManagementService.java | 9 +- ...elegatingDeviceManagementHttpEndpoint.java | 9 +- .../device/DeviceManagementService.java | 4 +- ...ractDeviceManagementSearchDevicesTest.java | 27 +-- ...atingDeviceManagementHttpEndpointTest.java | 6 +- .../impl/DeviceManagementServiceImpl.java | 5 +- ...Test.java => BaseRegistryServiceTest.java} | 0 ...asedDeviceManagementSearchDevicesTest.java | 82 +++++++-- .../MongoDbBasedDeviceManagementService.java | 3 +- .../api/management/device-registry-v1.yaml | 17 +- .../content/user-guide/device-registry.md | 17 +- 17 files changed, 363 insertions(+), 117 deletions(-) rename services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/{RegistryServiceTest.java => BaseRegistryServiceTest.java} (100%) diff --git a/clients/command-pubsub/src/main/java/org/eclipse/hono/client/command/pubsub/PubSubBasedInternalCommandSender.java b/clients/command-pubsub/src/main/java/org/eclipse/hono/client/command/pubsub/PubSubBasedInternalCommandSender.java index bd5a9cd69e..d93904f33e 100644 --- a/clients/command-pubsub/src/main/java/org/eclipse/hono/client/command/pubsub/PubSubBasedInternalCommandSender.java +++ b/clients/command-pubsub/src/main/java/org/eclipse/hono/client/command/pubsub/PubSubBasedInternalCommandSender.java @@ -102,10 +102,7 @@ private Map getAttributes(final PubSubBasedCommand command) { attributes.put(PubSubMessageHelper.PUBSUB_PROPERTY_PROJECT_ID, projectId); attributes.put(PubSubMessageHelper.PUBSUB_PROPERTY_RESPONSE_REQUIRED, !command.isOneWay()); Optional.ofNullable(command.getGatewayId()).ifPresent( - id -> { - attributes.put(MessageHelper.APP_PROPERTY_GATEWAY_ID, id); - attributes.put(MessageHelper.APP_PROPERTY_CMD_VIA, id); - }); + id -> attributes.put(MessageHelper.APP_PROPERTY_GATEWAY_ID, id)); return attributes; } } diff --git a/core/src/main/java/org/eclipse/hono/util/RegistryManagementConstants.java b/core/src/main/java/org/eclipse/hono/util/RegistryManagementConstants.java index 94771ffec6..12d8c00ee5 100644 --- a/core/src/main/java/org/eclipse/hono/util/RegistryManagementConstants.java +++ b/core/src/main/java/org/eclipse/hono/util/RegistryManagementConstants.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2019, 2021 Contributors to the Eclipse Foundation + * Copyright (c) 2019, 2021, 2023 Contributors to the Eclipse Foundation * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. @@ -118,6 +118,11 @@ public final class RegistryManagementConstants extends RequestResponseApiConstan */ public static final String PARAM_SORT_JSON = "sortJson"; + /** + * The name of the boolean filter query parameter for searching gateways or only devices. + */ + public static final String PARAM_IS_GATEWAY = "isGateway"; + // DEVICES diff --git a/service-base/src/main/java/org/eclipse/hono/service/http/AbstractHttpEndpoint.java b/service-base/src/main/java/org/eclipse/hono/service/http/AbstractHttpEndpoint.java index 908662e03f..c5efaaea89 100644 --- a/service-base/src/main/java/org/eclipse/hono/service/http/AbstractHttpEndpoint.java +++ b/service-base/src/main/java/org/eclipse/hono/service/http/AbstractHttpEndpoint.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2016, 2022 Contributors to the Eclipse Foundation + * Copyright (c) 2016, 2022, 2023 Contributors to the Eclipse Foundation * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. @@ -83,6 +83,13 @@ public abstract class AbstractHttpEndpoint ex } }; + /** + * A function that tries to parse a string into an Optional boolean. + */ + protected static final Function> CONVERTER_BOOLEAN = s -> { + return Strings.isNullOrEmpty(s) ? Optional.empty() : Optional.of(Boolean.valueOf(s)); + }; + /** * The configuration properties for this endpoint. */ diff --git a/services/base-jdbc/src/main/java/org/eclipse/hono/service/base/jdbc/store/device/TableManagementStore.java b/services/base-jdbc/src/main/java/org/eclipse/hono/service/base/jdbc/store/device/TableManagementStore.java index 5cb64508bf..c49a6dabfa 100644 --- a/services/base-jdbc/src/main/java/org/eclipse/hono/service/base/jdbc/store/device/TableManagementStore.java +++ b/services/base-jdbc/src/main/java/org/eclipse/hono/service/base/jdbc/store/device/TableManagementStore.java @@ -97,9 +97,13 @@ public class TableManagementStore extends AbstractDeviceStore { private final Statement updateDeviceVersionStatement; private final Statement countDevicesOfTenantStatement; - private final Statement countDevicesWithFilter; + private final Statement countDevicesWithFilterStatement; + private final Statement countGatewaysOfTenantStatement; + private final Statement countOnlyDevicesOfTenantStatement; - private final Statement findDevicesStatement; + private final Statement findDevicesOfTenantStatement; + private final Statement findGatewaysOfTenantStatement; + private final Statement findOnlyDevicesOfTenantStatement; private final Statement findDevicesOfTenantWithFilterStatement; /** @@ -204,20 +208,48 @@ public TableManagementStore(final JDBCClient client, final Tracer tracer, final .validateParameters( TENANT_ID); - this.countDevicesWithFilter = cfg + this.countGatewaysOfTenantStatement = cfg + .getRequiredStatement("countGatewaysOfTenant") + .validateParameters( + TENANT_ID, + DEVICE_ID); + + this.countOnlyDevicesOfTenantStatement = cfg + .getRequiredStatement("countOnlyDevicesOfTenant") + .validateParameters( + TENANT_ID, + DEVICE_ID); + + this.countDevicesWithFilterStatement = cfg .getRequiredStatement("countDevicesOfTenantWithFilter") .validateParameters( TENANT_ID, FIELD, VALUE); - this.findDevicesStatement = cfg + this.findDevicesOfTenantStatement = cfg .getRequiredStatement("findDevicesOfTenant") .validateParameters( TENANT_ID, PAGE_SIZE, PAGE_OFFSET); + this.findOnlyDevicesOfTenantStatement = cfg + .getRequiredStatement("findOnlyDevicesOfTenant") + .validateParameters( + TENANT_ID, + DEVICE_ID, + PAGE_SIZE, + PAGE_OFFSET); + + this.findGatewaysOfTenantStatement = cfg + .getRequiredStatement("findGatewaysOfTenant") + .validateParameters( + TENANT_ID, + DEVICE_ID, + PAGE_SIZE, + PAGE_OFFSET); + this.findDevicesOfTenantWithFilterStatement = cfg .getRequiredStatement("findDevicesOfTenantWithFilter") .validateParameters( @@ -228,46 +260,6 @@ public TableManagementStore(final JDBCClient client, final Tracer tracer, final PAGE_OFFSET); } - private static Future checkUpdateOutcome(final UpdateResult updateResult) { - - if (updateResult.getUpdated() < 0) { - // conflict - log.debug("Optimistic lock broke"); - return Future.failedFuture(new OptimisticLockingException()); - } - - return Future.succeededFuture(); - - } - - private static Future extractVersionForUpdate(final ResultSet device, final Optional resourceVersion) { - final Optional version = device.getRows(true).stream().map(o -> o.getString(VERSION)).findAny(); - - if (version.isEmpty()) { - log.debug("No version or no row found -> entity not found"); - return Future.failedFuture(new EntityNotFoundException()); - } - - final var currentVersion = version.get(); - - return resourceVersion - // if we expect a certain version - .>map(expected -> { - // check ... - if (expected.equals(currentVersion)) { - // version matches, continue with current version - return Future.succeededFuture(currentVersion); - } else { - // version does not match, abort - return Future.failedFuture(new OptimisticLockingException()); - } - } - ) - // if we don't expect a version, continue with the current - .orElseGet(() -> Future.succeededFuture(currentVersion)); - - } - /** * Read a device and lock it for updates. *

@@ -850,6 +842,46 @@ private Future recoverNotFound(final Span span, final Throwable err, fina } } + private static Future checkUpdateOutcome(final UpdateResult updateResult) { + + if (updateResult.getUpdated() < 0) { + // conflict + log.debug("Optimistic lock broke"); + return Future.failedFuture(new OptimisticLockingException()); + } + + return Future.succeededFuture(); + + } + + private static Future extractVersionForUpdate(final ResultSet device, final Optional resourceVersion) { + final Optional version = device.getRows(true).stream().map(o -> o.getString("version")).findAny(); + + if (version.isEmpty()) { + log.debug("No version or no row found -> entity not found"); + return Future.failedFuture(new EntityNotFoundException()); + } + + final var currentVersion = version.get(); + + return resourceVersion + // if we expect a certain version + .>map(expected -> { + // check ... + if (expected.equals(currentVersion)) { + // version matches, continue with current version + return Future.succeededFuture(currentVersion); + } else { + // version does not match, abort + return Future.failedFuture(new OptimisticLockingException()); + } + } + ) + // if we don't expect a version, continue with the current + .orElseGet(() -> Future.succeededFuture(currentVersion)); + + } + /** * Get all credentials for a device. *

@@ -912,7 +944,7 @@ private List parseCredentials(final ResultSet result) { final var entries = result.getRows(true); return entries.stream() - .map(o -> o.getString(DATA)) + .map(o -> o.getString("data")) .map(s -> Json.decodeValue(s, CommonCredential.class)) .collect(Collectors.toList()); @@ -925,25 +957,41 @@ private List parseCredentials(final ResultSet result) { * @param pageSize The page size. * @param pageOffset The page offset. * @param filters The list of filters (currently only the first value of the list is used). + * @param isGateway Optional search gateway or only devices filter. * @param spanContext The span to contribute to. * @return A future containing devices. */ - public Future> findDevices(final String tenantId, final int pageSize, final int pageOffset, final List filters, + public Future> findDevices(final String tenantId, final int pageSize, final int pageOffset, final List filters, final Optional isGateway, final SpanContext spanContext) { - final var filter = filters.stream().findFirst(); + final Statement findDeviceSqlStatement; + final Statement countStatement; + final String field; + final String value; + + if (isGateway.isPresent()) { + field = ""; + value = ""; - final String field = filter.map(filter1 -> filter1.getField().toString().replace("/", "")).orElse(""); - final var value = filter.map(filter1 -> - filter1.getValue().toString() - .replace("/", "") - .replace("*", "%") - .replace("?", "_") - ).orElse(""); + findDeviceSqlStatement = isGateway.get() ? this.findGatewaysOfTenantStatement : this.findOnlyDevicesOfTenantStatement; + countStatement = isGateway.get() ? this.countGatewaysOfTenantStatement : this.countOnlyDevicesOfTenantStatement; + } else { + final var filter = filters.stream().findFirst(); + + field = filter.map(filter1 -> filter1.getField().toString().replace("/", "")).orElse(""); + value = filter.map(filter1 -> + filter1.getValue().toString() + .replace("/", "") + .replace("*", "%") + .replace("?", "_") + ).orElse(""); + + + findDeviceSqlStatement = (filter.isPresent()) ? findDevicesOfTenantWithFilterStatement : this.findDevicesOfTenantStatement; + countStatement = (filter.isPresent()) ? countDevicesWithFilterStatement : this.countDevicesOfTenantStatement; + } - final Statement findDeviceSqlStatement = (filter.isPresent()) ? findDevicesOfTenantWithFilterStatement : this.findDevicesStatement; - final Statement countStatement = (filter.isPresent()) ? countDevicesWithFilter : this.countDevicesOfTenantStatement; final var expanded = findDeviceSqlStatement.expand(map -> { map.put(TENANT_ID, tenantId); @@ -985,5 +1033,3 @@ public Future> findDevices(final String tenantId, fin .onComplete(x -> span.finish()); } } - - diff --git a/services/base-jdbc/src/main/resources/org/eclipse/hono/service/base/jdbc/store/device/base.h2.sql.yaml b/services/base-jdbc/src/main/resources/org/eclipse/hono/service/base/jdbc/store/device/base.h2.sql.yaml index bf59f21565..7a826f3c26 100644 --- a/services/base-jdbc/src/main/resources/org/eclipse/hono/service/base/jdbc/store/device/base.h2.sql.yaml +++ b/services/base-jdbc/src/main/resources/org/eclipse/hono/service/base/jdbc/store/device/base.h2.sql.yaml @@ -1,18 +1,36 @@ - countDevicesOfTenantWithFilter: | - SELECT COUNT(*) AS deviceCount FROM %1$s - WHERE + SELECT COUNT(*) AS deviceCount FROM %1$s + WHERE tenant_id=:tenant_id AND LOCATE(CONCAT_WS(':', :field, REPLACE(:value, '"')), REPLACE(data, '"')) OR REPLACE(data, '"') LIKE CONCAT('%%', :field, ':', REPLACE(:value, '"')) +countGatewaysOfTenant: | + SELECT COUNT(*) AS deviceCount + FROM %1$s + WHERE + tenant_id=:tenant_id + AND + LOCATE(CONCAT(device_id, '|'), + (SELECT CONCAT(REPLACE(group_concat(DISTINCT ids separator '|'), ',', '|'), '|') FROM + (SELECT DISTINCT REGEXP_REPLACE(REGEXP_SUBSTR(DATA, '"via":\[.*?\]' ), '"via":\[|\]|"', '') as ids FROM %1$s WHERE tenant_id=:tenant_id ))) > 0 + +countOnlyDevicesOfTenant: | + SELECT COUNT(*) AS deviceCount + FROM %1$s + WHERE + tenant_id=:tenant_id + AND + LOCATE(CONCAT(device_id, '|'), + (SELECT CONCAT(REPLACE(group_concat(DISTINCT ids separator '|'), ',', '|'), '|') FROM + (SELECT DISTINCT REGEXP_REPLACE(REGEXP_SUBSTR(DATA, '"via":\[.*?\]' ), '"via":\[|\]|"', '') as ids FROM %1$s WHERE tenant_id=:tenant_id ))) = 0 findDevicesOfTenantWithFilter: | - SELECT * - FROM %s - WHERE + SELECT * + FROM %1$s + WHERE tenant_id=:tenant_id AND LOCATE(CONCAT_WS(':', :field, REPLACE(:value, '"')), REPLACE(data, '"')) @@ -21,3 +39,29 @@ findDevicesOfTenantWithFilter: | ORDER BY device_id ASC LIMIT :page_size OFFSET :page_offset + +findGatewaysOfTenant: | + SELECT * + FROM %1$s + WHERE + tenant_id=:tenant_id + AND + LOCATE(CONCAT(device_id, '|'), + (SELECT CONCAT(REPLACE(group_concat(DISTINCT ids separator '|'), ',', '|'), '|') FROM + (SELECT DISTINCT REPLACE(REGEXP_REPLACE(REGEXP_SUBSTR(DATA, '"via":\[.*?\]' ), '"via":\[|\]|"', ''), '\') as ids FROM %1$s WHERE tenant_id=:tenant_id ))) > 0 + ORDER BY device_id ASC + LIMIT :page_size + OFFSET :page_offset + +findOnlyDevicesOfTenant: | + SELECT * + FROM %1$s + WHERE + tenant_id=:tenant_id + AND + LOCATE(CONCAT(device_id, '|'), + (SELECT CONCAT(REPLACE(group_concat(DISTINCT ids separator '|'), ',', '|'), '|') FROM + (SELECT DISTINCT REGEXP_REPLACE(REGEXP_SUBSTR(DATA, '"via":\[.*?\]' ), '"via":\[|\]|"', '') as ids FROM %1$s WHERE tenant_id=:tenant_id ))) = 0 + ORDER BY device_id ASC + LIMIT :page_size + OFFSET :page_offset diff --git a/services/base-jdbc/src/main/resources/org/eclipse/hono/service/base/jdbc/store/device/base.postgresql.sql.yaml b/services/base-jdbc/src/main/resources/org/eclipse/hono/service/base/jdbc/store/device/base.postgresql.sql.yaml index 22f7b02532..3984d17fe6 100644 --- a/services/base-jdbc/src/main/resources/org/eclipse/hono/service/base/jdbc/store/device/base.postgresql.sql.yaml +++ b/services/base-jdbc/src/main/resources/org/eclipse/hono/service/base/jdbc/store/device/base.postgresql.sql.yaml @@ -55,17 +55,39 @@ resolveGroups: | group_id in (select unnest((string_to_array(:group_ids,','))::varchar[])) countDevicesOfTenantWithFilter: | - SELECT COUNT(*) AS deviceCount FROM %1$s - WHERE + SELECT COUNT(*) AS deviceCount FROM %1$s + WHERE tenant_id=:tenant_id AND data->>:field LIKE CAST(:value AS VARCHAR) OR jsonb_extract_path(data,'ext')->>:field LIKE CAST(:value as varchar) +countGatewaysOfTenant: | + SELECT COUNT(*) AS deviceCount FROM %1$s + WHERE + tenant_id=:tenant_id + AND + device_id IN ( + SELECT replace(dr.device_id::text, '"', '') FROM ( + SELECT jsonb_array_elements(data -> 'via') AS device_id FROM device_registrations + WHERE tenant_id=:tenant_id AND data ->> 'via' != '') + as dr) + +countOnlyDevicesOfTenant: | + SELECT COUNT(*) AS deviceCount FROM %1$s + WHERE + tenant_id=:tenant_id + AND + device_id NOT IN ( + SELECT replace(dr.device_id::text, '"', '') FROM ( + SELECT jsonb_array_elements(data -> 'via') AS device_id FROM device_registrations + WHERE tenant_id=:tenant_id AND data ->> 'via' != '') + as dr) + findDevicesOfTenantWithFilter: | - SELECT * - FROM %s - WHERE + SELECT * + FROM %s + WHERE tenant_id=:tenant_id AND data->>:field LIKE CAST(:value AS VARCHAR) OR @@ -73,3 +95,33 @@ findDevicesOfTenantWithFilter: | ORDER BY device_id ASC LIMIT :page_size OFFSET :page_offset + +findGatewaysOfTenant: | + SELECT * + FROM %s + WHERE + tenant_id=:tenant_id + AND + device_id IN ( + SELECT replace(dr.device_id::text, '"', '') FROM ( + SELECT jsonb_array_elements(data -> 'via') AS device_id FROM device_registrations + WHERE tenant_id=:tenant_id AND data ->> 'via' != '') + as dr) + ORDER BY device_id ASC + LIMIT :page_size + OFFSET :page_offset + +findOnlyDevicesOfTenant: | + SELECT * + FROM %s + WHERE + tenant_id=:tenant_id + AND + device_id NOT IN ( + SELECT replace(dr.device_id::text, '"', '') FROM ( + SELECT jsonb_array_elements(data -> 'via') AS device_id FROM device_registrations + WHERE tenant_id=:tenant_id AND data ->> 'via' != '') + as dr) + ORDER BY device_id ASC + LIMIT :page_size + OFFSET :page_offset diff --git a/services/device-registry-base/src/main/java/org/eclipse/hono/deviceregistry/service/device/AbstractDeviceManagementService.java b/services/device-registry-base/src/main/java/org/eclipse/hono/deviceregistry/service/device/AbstractDeviceManagementService.java index a6e3b34e14..3bc927c71f 100644 --- a/services/device-registry-base/src/main/java/org/eclipse/hono/deviceregistry/service/device/AbstractDeviceManagementService.java +++ b/services/device-registry-base/src/main/java/org/eclipse/hono/deviceregistry/service/device/AbstractDeviceManagementService.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2020, 2022 Contributors to the Eclipse Foundation + * Copyright (c) 2020, 2022, 2023 Contributors to the Eclipse Foundation * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. @@ -213,7 +213,7 @@ protected Future> processDeleteDevicesOfTenant(final String tenantI /** * Finds devices for search criteria. *

- * This method is invoked by {@link #searchDevices(String, int, int, List, List, Span)} after all parameter checks + * This method is invoked by {@link #searchDevices(String, int, int, List, List, Optional, Span)} after all parameter checks * have succeeded. *

* This default implementation returns a future failed with a {@link org.eclipse.hono.client.ServerErrorException} @@ -225,6 +225,7 @@ protected Future> processDeleteDevicesOfTenant(final String tenantI * retrieve the whole result set page by page. * @param filters A list of filters. The filters are predicates that objects in the result set must match. * @param sortOptions A list of sort options. The sortOptions specify properties to sort the result set by. + * @param isGateway Optional filter for searching all gateways or only devices. * @param span The active OpenTracing span to use for tracking this operation. *

* Implementations must not invoke the {@link Span#finish()} nor the {@link Span#finish(long)} @@ -242,6 +243,7 @@ protected Future>> processSearchDevic final int pageOffset, final List filters, final List sortOptions, + final Optional isGateway, final Span span) { return Future.failedFuture(new ServerErrorException( @@ -408,6 +410,7 @@ public final Future>> searchDevices( final int pageOffset, final List filters, final List sortOptions, + final Optional isGateway, final Span span) { Objects.requireNonNull(tenantId); @@ -429,7 +432,7 @@ public final Future>> searchDevices( tenantId, result.getStatus(), "tenant does not exist")) - : processSearchDevices(tenantId, pageSize, pageOffset, filters, sortOptions, span)) + : processSearchDevices(tenantId, pageSize, pageOffset, filters, sortOptions, isGateway, span)) .recover(t -> DeviceRegistryUtils.mapError(t, tenantId)); } } diff --git a/services/device-registry-base/src/main/java/org/eclipse/hono/service/management/device/DelegatingDeviceManagementHttpEndpoint.java b/services/device-registry-base/src/main/java/org/eclipse/hono/service/management/device/DelegatingDeviceManagementHttpEndpoint.java index 09e45625ed..35f3673a01 100644 --- a/services/device-registry-base/src/main/java/org/eclipse/hono/service/management/device/DelegatingDeviceManagementHttpEndpoint.java +++ b/services/device-registry-base/src/main/java/org/eclipse/hono/service/management/device/DelegatingDeviceManagementHttpEndpoint.java @@ -185,8 +185,14 @@ private void doSearchDevices(final RoutingContext ctx) { RegistryManagementConstants.PARAM_FILTER_JSON, Filter.class); final Future> sortOptions = decodeJsonFromRequestParameter(ctx, RegistryManagementConstants.PARAM_SORT_JSON, Sort.class); + final Future> isGateway = getRequestParameter( + ctx, + RegistryManagementConstants.PARAM_IS_GATEWAY, + Optional.empty(), + CONVERTER_BOOLEAN, + value -> true); - Future.all(pageSize, pageOffset, filters, sortOptions) + Future.all(pageSize, pageOffset, filters, sortOptions, isGateway) .onSuccess(ok -> TracingHelper.TAG_TENANT_ID.set(span, tenantId)) .compose(ok -> getService().searchDevices( tenantId, @@ -194,6 +200,7 @@ private void doSearchDevices(final RoutingContext ctx) { pageOffset.result(), filters.result(), sortOptions.result(), + isGateway.result(), span)) .onSuccess(operationResult -> writeResponse(ctx, operationResult, span)) .onFailure(t -> failRequest(ctx, t, span)) diff --git a/services/device-registry-base/src/main/java/org/eclipse/hono/service/management/device/DeviceManagementService.java b/services/device-registry-base/src/main/java/org/eclipse/hono/service/management/device/DeviceManagementService.java index 3717ca7e51..fbdf79297b 100644 --- a/services/device-registry-base/src/main/java/org/eclipse/hono/service/management/device/DeviceManagementService.java +++ b/services/device-registry-base/src/main/java/org/eclipse/hono/service/management/device/DeviceManagementService.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2016, 2021 Contributors to the Eclipse Foundation + * Copyright (c) 2016, 2021, 2023 Contributors to the Eclipse Foundation * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. @@ -102,6 +102,7 @@ public interface DeviceManagementService { * Implementations must not invoke the {@link Span#finish()} nor the {@link Span#finish(long)} * methods. However,implementations may log (error) events on this span, set tags and use this span * as the parent for additional spans created as part of this method's execution. + * @param isGateway Optional filter for searching all gateways or only devices. * @return A future indicating the outcome of the operation. *

* The future will be succeeded with a result containing the matching devices. Otherwise, the future will @@ -118,6 +119,7 @@ default Future>> searchDevices( final int pageOffset, final List filters, final List sortOptions, + final Optional isGateway, final Span span) { return Future.failedFuture(new ServerErrorException( diff --git a/services/device-registry-base/src/test/java/org/eclipse/hono/service/management/device/AbstractDeviceManagementSearchDevicesTest.java b/services/device-registry-base/src/test/java/org/eclipse/hono/service/management/device/AbstractDeviceManagementSearchDevicesTest.java index aab5a6ee72..2a4f3cb937 100644 --- a/services/device-registry-base/src/test/java/org/eclipse/hono/service/management/device/AbstractDeviceManagementSearchDevicesTest.java +++ b/services/device-registry-base/src/test/java/org/eclipse/hono/service/management/device/AbstractDeviceManagementSearchDevicesTest.java @@ -70,6 +70,7 @@ default void testSearchDevicesWhenNoDevicesAreFound(final VertxTestContext ctx) pageOffset, List.of(filter), List.of(), + Optional.empty(), NoopSpan.INSTANCE)) .onComplete(ctx.failing(t -> { ctx.verify(() -> Assertions.assertServiceInvocationException(t, HttpURLConnection.HTTP_NOT_FOUND)); @@ -94,7 +95,7 @@ default void testSearchDevicesWithAFilterSucceeds(final VertxTestContext ctx) { "testDevice2", new Device().setEnabled(false))) .compose(ok -> getDeviceManagementService() .searchDevices(tenantId, pageSize, pageOffset, List.of(filter), List.of(), - NoopSpan.INSTANCE)) + Optional.empty(), NoopSpan.INSTANCE)) .onComplete(ctx.succeeding(s -> { ctx.verify(() -> { assertThat(s.getStatus()).isEqualTo(HttpURLConnection.HTTP_OK); @@ -124,12 +125,12 @@ default void testSearchDevicesWithMultipleFiltersSucceeds(final VertxTestContext createDevices(tenantId, Map.of( "testDevice1", new Device().setEnabled(true).setVia(List.of("gw-1")), "testDevice2", new Device().setEnabled(false))) - .compose(ok -> getDeviceManagementService() - .searchDevices(tenantId, pageSize, pageOffset, List.of(filter1, filter2, filter3), - List.of(), NoopSpan.INSTANCE)) - .onComplete(ctx.succeeding(s -> { - ctx.verify(() -> { - assertThat(s.getStatus()).isEqualTo(HttpURLConnection.HTTP_OK); + .compose(ok -> getDeviceManagementService() + .searchDevices(tenantId, pageSize, pageOffset, List.of(filter1, filter2, filter3), + List.of(), Optional.empty(), NoopSpan.INSTANCE)) + .onComplete(ctx.succeeding(s -> { + ctx.verify(() -> { + assertThat(s.getStatus()).isEqualTo(HttpURLConnection.HTTP_OK); final SearchResult searchResult = s.getPayload(); assertThat(searchResult.getTotal()).isEqualTo(1); @@ -158,7 +159,7 @@ default void testSearchDevicesWithPageSize(final VertxTestContext ctx) { "testDevice2", new Device().setEnabled(true))) .compose(ok -> getDeviceManagementService() .searchDevices(tenantId, pageSize, pageOffset, List.of(filter), List.of(), - NoopSpan.INSTANCE)) + Optional.empty(), NoopSpan.INSTANCE)) .onComplete(ctx.succeeding(s -> { ctx.verify(() -> { assertThat(s.getStatus()).isEqualTo(HttpURLConnection.HTTP_OK); @@ -196,6 +197,7 @@ default void testSearchDevicesWithPageOffset(final VertxTestContext ctx) { 0, List.of(filter), List.of(), + Optional.empty(), NoopSpan.INSTANCE)) .compose(response -> { ctx.verify(() -> { @@ -217,6 +219,7 @@ default void testSearchDevicesWithPageOffset(final VertxTestContext ctx) { 1, List.of(filter), List.of(), + Optional.empty(), NoopSpan.INSTANCE); }) .onComplete(ctx.succeeding(s -> { @@ -253,7 +256,7 @@ default void testSearchDevicesWithSortOption(final VertxTestContext ctx) { "testDevice2", new Device().setEnabled(true).setExtensions(Map.of("id", "bbb")))) .compose(ok -> getDeviceManagementService() .searchDevices(tenantId, pageSize, pageOffset, List.of(filter), List.of(sortOption), - NoopSpan.INSTANCE)) + Optional.empty(), NoopSpan.INSTANCE)) .onComplete(ctx.succeeding(s -> { ctx.verify(() -> { assertThat(s.getStatus()).isEqualTo(HttpURLConnection.HTTP_OK); @@ -291,6 +294,7 @@ default void testSearchDevicesSortsResultById(final VertxTestContext ctx) { 0, List.of(), List.of(), + Optional.empty(), NoopSpan.INSTANCE)) .compose(response -> { ctx.verify(() -> { @@ -309,7 +313,8 @@ default void testSearchDevicesSortsResultById(final VertxTestContext ctx) { pageSize, 1, List.of(), - List.of(), + List.of(), + Optional.empty(), NoopSpan.INSTANCE); }) .onComplete(ctx.succeeding(s -> { @@ -354,6 +359,7 @@ default void testSearchDevicesWithWildCardToMatchMultipleCharacters(final VertxT pageOffset, List.of(filter1, filter2), List.of(sortOption), + Optional.empty(), NoopSpan.INSTANCE)) .onComplete(ctx.succeeding(s -> { ctx.verify(() -> { @@ -396,6 +402,7 @@ default void testSearchDevicesWithCardToMatchSingleCharacter(final VertxTestCont pageOffset, List.of(filter1, filter2), List.of(sortOption), + Optional.empty(), NoopSpan.INSTANCE)) .onComplete(ctx.succeeding(s -> { ctx.verify(() -> { diff --git a/services/device-registry-base/src/test/java/org/eclipse/hono/service/management/device/DelegatingDeviceManagementHttpEndpointTest.java b/services/device-registry-base/src/test/java/org/eclipse/hono/service/management/device/DelegatingDeviceManagementHttpEndpointTest.java index c4570e8805..72065d3bb8 100644 --- a/services/device-registry-base/src/test/java/org/eclipse/hono/service/management/device/DelegatingDeviceManagementHttpEndpointTest.java +++ b/services/device-registry-base/src/test/java/org/eclipse/hono/service/management/device/DelegatingDeviceManagementHttpEndpointTest.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2020, 2021 Contributors to the Eclipse Foundation + * Copyright (c) 2020, 2021, 2023 Contributors to the Eclipse Foundation * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. @@ -91,6 +91,7 @@ public void setUp() { anyInt(), any(List.class), any(List.class), + any(Optional.class), any(Span.class))) .thenReturn(Future.succeededFuture(OperationResult.empty(HttpURLConnection.HTTP_OK))); final var endpoint = new DelegatingDeviceManagementHttpEndpoint<>(vertx, service); @@ -300,6 +301,7 @@ public void testSearchDevicesUsesDefaultSearchCriteria() { eq(DelegatingDeviceManagementHttpEndpoint.DEFAULT_PAGE_OFFSET), argThat(List::isEmpty), argThat(List::isEmpty), + any(Optional.class), any(Span.class)); } @@ -351,6 +353,7 @@ public void testSearchDevicesSucceedsWithSearchCriteria() { Direction.DESC == sortOption.getDirection(); } }), + any(Optional.class), any(Span.class)); } @@ -419,6 +422,7 @@ private void testSearchDevicesFailsForMalformedSearchCriteria(final MultiMap par anyInt(), any(List.class), any(List.class), + any(Optional.class), any(Span.class)); } diff --git a/services/device-registry-jdbc/src/main/java/org/eclipse/hono/deviceregistry/jdbc/impl/DeviceManagementServiceImpl.java b/services/device-registry-jdbc/src/main/java/org/eclipse/hono/deviceregistry/jdbc/impl/DeviceManagementServiceImpl.java index aa04d2b163..da27caf8df 100644 --- a/services/device-registry-jdbc/src/main/java/org/eclipse/hono/deviceregistry/jdbc/impl/DeviceManagementServiceImpl.java +++ b/services/device-registry-jdbc/src/main/java/org/eclipse/hono/deviceregistry/jdbc/impl/DeviceManagementServiceImpl.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2020, 2022 Contributors to the Eclipse Foundation + * Copyright (c) 2020, 2022, 2023 Contributors to the Eclipse Foundation * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. @@ -156,12 +156,13 @@ protected Future>> processSearchDevic final int pageOffset, final List filters, final List sortOptions, + final Optional isGateway, final Span span) { Objects.requireNonNull(tenantId); Objects.requireNonNull(span); - return store.findDevices(tenantId, pageSize, pageOffset, filters, span.context()) + return store.findDevices(tenantId, pageSize, pageOffset, filters, isGateway, span.context()) .map(result -> OperationResult.ok( HttpURLConnection.HTTP_OK, result, diff --git a/services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/RegistryServiceTest.java b/services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/BaseRegistryServiceTest.java similarity index 100% rename from services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/RegistryServiceTest.java rename to services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/BaseRegistryServiceTest.java diff --git a/services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/JdbcBasedDeviceManagementSearchDevicesTest.java b/services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/JdbcBasedDeviceManagementSearchDevicesTest.java index 1bdb12bb5c..e038a96ab7 100644 --- a/services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/JdbcBasedDeviceManagementSearchDevicesTest.java +++ b/services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/JdbcBasedDeviceManagementSearchDevicesTest.java @@ -39,7 +39,7 @@ class JdbcBasedDeviceManagementSearchDevicesTest extends AbstractJdbcRegistryTes * Creates a set of devices. * * @param tenantId The tenant identifier. - * @param devices The devices to create. + * @param devices The devices to create. * @return A succeeded future if all devices have been created successfully. */ Future createDevices(final String tenantId, final Map devices) { @@ -74,6 +74,7 @@ void testSearchDevicesWhenNoDevicesAreFound(final VertxTestContext ctx) { 0, List.of(), List.of(), + Optional.empty(), NoopSpan.INSTANCE) .onComplete(ctx.failing(t -> { ctx.verify(() -> Assertions.assertServiceInvocationException(t, HttpURLConnection.HTTP_NOT_FOUND)); @@ -98,7 +99,7 @@ void testSearchDevicesWithPageSize(final VertxTestContext ctx) { "testDevice2", new Device().setEnabled(true), "testDevice3", new Device().setEnabled(true))) .compose(ok -> getDeviceManagementService() - .searchDevices(tenantId, pageSize, pageOffset, List.of(), List.of(), NoopSpan.INSTANCE)) + .searchDevices(tenantId, pageSize, pageOffset, List.of(), List.of(), Optional.empty(), NoopSpan.INSTANCE)) .onComplete(ctx.succeeding(s -> { ctx.verify(() -> { assertThat(s.getStatus()).isEqualTo(HttpURLConnection.HTTP_OK); @@ -128,7 +129,7 @@ void testSearchDevicesWithPageOffset(final VertxTestContext ctx) { "testDevice2", new Device().setEnabled(true), "testDevice3", new Device().setEnabled(true))) .compose(ok -> getDeviceManagementService() - .searchDevices(tenantId, pageSize, pageOffset, List.of(), List.of(), NoopSpan.INSTANCE)) + .searchDevices(tenantId, pageSize, pageOffset, List.of(), List.of(), Optional.empty(), NoopSpan.INSTANCE)) .onComplete(ctx.succeeding(s -> { ctx.verify(() -> { assertThat(s.getStatus()).isEqualTo(HttpURLConnection.HTTP_OK); @@ -153,7 +154,7 @@ void testSearchAllDevices(final VertxTestContext ctx) { "testDevice2", new Device().setEnabled(true), "testDevice3", new Device().setEnabled(true))) .compose(ok -> getDeviceManagementService() - .searchDevices(tenantId, pageSize, pageOffset, List.of(), List.of(), NoopSpan.INSTANCE)) + .searchDevices(tenantId, pageSize, pageOffset, List.of(), List.of(), Optional.empty(), NoopSpan.INSTANCE)) .onComplete(ctx.succeeding(s -> { ctx.verify(() -> { assertThat(s.getStatus()).isEqualTo(HttpURLConnection.HTTP_OK); @@ -168,7 +169,7 @@ void testSearchAllDevices(final VertxTestContext ctx) { } @Test - void TestSearchDevicesWithViaFilter(final VertxTestContext ctx) { + void testSearchDevicesWithViaFilter(final VertxTestContext ctx) { final String tenantId = DeviceRegistryUtils.getUniqueIdentifier(); final int pageSize = 3; final int pageOffset = 0; @@ -183,7 +184,7 @@ void TestSearchDevicesWithViaFilter(final VertxTestContext ctx) { "testDevice_Gateway", new Device(), "testDevice_Gateway2", new Device().setEnabled(true))) .compose(ok -> getDeviceManagementService() - .searchDevices(tenantId, pageSize, pageOffset, filters, List.of(), NoopSpan.INSTANCE)) + .searchDevices(tenantId, pageSize, pageOffset, filters, List.of(), Optional.empty(), NoopSpan.INSTANCE)) .onComplete(ctx.succeeding(s -> { ctx.verify(() -> { assertThat(s.getStatus()).isEqualTo(HttpURLConnection.HTTP_OK); @@ -197,7 +198,7 @@ void TestSearchDevicesWithViaFilter(final VertxTestContext ctx) { } @Test - void TestSearchDevicesWithBooleanFilter(final VertxTestContext ctx) { + void testSearchDevicesWithBooleanFilter(final VertxTestContext ctx) { final String tenantId = DeviceRegistryUtils.getUniqueIdentifier(); final int pageSize = 10; final int pageOffset = 0; @@ -211,7 +212,7 @@ void TestSearchDevicesWithBooleanFilter(final VertxTestContext ctx) { "testDevice3", new Device().setEnabled(true).setVia(List.of("testDevice2")), "testDevice4", new Device().setEnabled(true).setVia(List.of("testDevice1")))) .compose(ok -> getDeviceManagementService() - .searchDevices(tenantId, pageSize, pageOffset, filters, List.of(), NoopSpan.INSTANCE)) + .searchDevices(tenantId, pageSize, pageOffset, filters, List.of(), Optional.empty(), NoopSpan.INSTANCE)) .onComplete(ctx.succeeding(s -> { ctx.verify(() -> { assertThat(s.getStatus()).isEqualTo(HttpURLConnection.HTTP_OK); @@ -227,7 +228,7 @@ void TestSearchDevicesWithBooleanFilter(final VertxTestContext ctx) { @Test - void TestSearchDevicesWithIntegerFilter(final VertxTestContext ctx) { + void testSearchDevicesWithIntegerFilter(final VertxTestContext ctx) { final String tenantId = DeviceRegistryUtils.getUniqueIdentifier(); final int pageSize = 10; final int pageOffset = 0; @@ -240,7 +241,7 @@ void TestSearchDevicesWithIntegerFilter(final VertxTestContext ctx) { "testDevice2", new Device().setEnabled(false).putExtension("count", 8), "testDevice3", new Device().setEnabled(true).putExtension("count", 12))) .compose(ok -> getDeviceManagementService() - .searchDevices(tenantId, pageSize, pageOffset, filters, List.of(), NoopSpan.INSTANCE)) + .searchDevices(tenantId, pageSize, pageOffset, filters, List.of(), Optional.empty(), NoopSpan.INSTANCE)) .onComplete(ctx.succeeding(s -> { ctx.verify(() -> { assertThat(s.getStatus()).isEqualTo(HttpURLConnection.HTTP_OK); @@ -255,7 +256,7 @@ void TestSearchDevicesWithIntegerFilter(final VertxTestContext ctx) { @Test - void TestSearchDevicesWithStringFilter(final VertxTestContext ctx) { + void testSearchDevicesWithStringFilter(final VertxTestContext ctx) { final String tenantId = DeviceRegistryUtils.getUniqueIdentifier(); final int pageSize = 10; final int pageOffset = 0; @@ -268,7 +269,7 @@ void TestSearchDevicesWithStringFilter(final VertxTestContext ctx) { "testDevice2", new Device().setEnabled(false).putExtension("type", "type_2"), "testDevice3", new Device().setEnabled(true).putExtension("type", "type_1"))) .compose(ok -> getDeviceManagementService() - .searchDevices(tenantId, pageSize, pageOffset, filters, List.of(), NoopSpan.INSTANCE)) + .searchDevices(tenantId, pageSize, pageOffset, filters, List.of(), Optional.empty(), NoopSpan.INSTANCE)) .onComplete(ctx.succeeding(s -> { ctx.verify(() -> { assertThat(s.getStatus()).isEqualTo(HttpURLConnection.HTTP_OK); @@ -283,7 +284,7 @@ void TestSearchDevicesWithStringFilter(final VertxTestContext ctx) { @Test - void TestSearchDevicesWithStringAllCharsWildcardsFilter(final VertxTestContext ctx) { + void testSearchDevicesWithStringAllCharsWildcardsFilter(final VertxTestContext ctx) { final String tenantId = DeviceRegistryUtils.getUniqueIdentifier(); final int pageSize = 10; final int pageOffset = 0; @@ -296,7 +297,7 @@ void TestSearchDevicesWithStringAllCharsWildcardsFilter(final VertxTestContext c "testDevice2", new Device().setEnabled(false).putExtension("type", "type2"), "testDevice3", new Device().setEnabled(true).putExtension("type", "type1"))) .compose(ok -> getDeviceManagementService() - .searchDevices(tenantId, pageSize, pageOffset, filters, List.of(), NoopSpan.INSTANCE)) + .searchDevices(tenantId, pageSize, pageOffset, filters, List.of(), Optional.empty(), NoopSpan.INSTANCE)) .onComplete(ctx.succeeding(s -> { ctx.verify(() -> { assertThat(s.getStatus()).isEqualTo(HttpURLConnection.HTTP_OK); @@ -311,7 +312,7 @@ void TestSearchDevicesWithStringAllCharsWildcardsFilter(final VertxTestContext c } @Test - void TestSearchDevicesWithStringOneCharWildcardsFilter(final VertxTestContext ctx) { + void testSearchDevicesWithStringOneCharWildcardsFilter(final VertxTestContext ctx) { final String tenantId = DeviceRegistryUtils.getUniqueIdentifier(); final int pageSize = 10; final int pageOffset = 0; @@ -324,7 +325,56 @@ void TestSearchDevicesWithStringOneCharWildcardsFilter(final VertxTestContext ct "testDevice2", new Device().setEnabled(false).putExtension("type", "type2"), "testDevice3", new Device().setEnabled(true).putExtension("type", "type11"))) .compose(ok -> getDeviceManagementService() - .searchDevices(tenantId, pageSize, pageOffset, filters, List.of(), NoopSpan.INSTANCE)) + .searchDevices(tenantId, pageSize, pageOffset, filters, List.of(), Optional.empty(), NoopSpan.INSTANCE)) + .onComplete(ctx.succeeding(s -> { + ctx.verify(() -> { + assertThat(s.getStatus()).isEqualTo(HttpURLConnection.HTTP_OK); + assertThat(s.getPayload().getTotal()).isEqualTo(2); + assertThat(s.getPayload().getResult()).hasSize(2); + assertThat(s.getPayload().getResult().get(0).getId()).isEqualTo("testDevice1"); + assertThat(s.getPayload().getResult().get(1).getId()).isEqualTo("testDevice3"); + }); + ctx.completeNow(); + })); + } + + + @Test + void testSearchAllGateways(final VertxTestContext ctx) { + final String tenantId = DeviceRegistryUtils.getUniqueIdentifier(); + final int pageSize = 10; + final int pageOffset = 0; + + createDevices(tenantId, Map.of( + "testDevice1", new Device().setVia(List.of("testDevice2")), + "testDevice2", new Device(), + "testDevice3", new Device().setVia(List.of("testDevice2")))) + .compose(ok -> getDeviceManagementService() + .searchDevices(tenantId, pageSize, pageOffset, List.of(), List.of(), Optional.of(true), NoopSpan.INSTANCE)) + .onComplete(ctx.succeeding(s -> { + ctx.verify(() -> { + assertThat(s.getStatus()).isEqualTo(HttpURLConnection.HTTP_OK); + assertThat(s.getPayload().getTotal()).isEqualTo(1); + assertThat(s.getPayload().getResult()).hasSize(1); + assertThat(s.getPayload().getResult().get(0).getId()).isEqualTo("testDevice2"); + }); + ctx.completeNow(); + })); + } + + + @Test + void testSearchOnlyDevices(final VertxTestContext ctx) { + final String tenantId = DeviceRegistryUtils.getUniqueIdentifier(); + final int pageSize = 10; + final int pageOffset = 0; + + createDevices(tenantId, Map.of( + "testDevice1", new Device().setVia(List.of("testDevice2")), + "testDevice2", new Device(), + "testDevice3", new Device().setVia(List.of("testDevice2")))) + .compose(ok -> getDeviceManagementService() + .searchDevices(tenantId, pageSize, pageOffset, List.of(), List.of(), Optional.of(false), NoopSpan.INSTANCE)) .onComplete(ctx.succeeding(s -> { ctx.verify(() -> { assertThat(s.getStatus()).isEqualTo(HttpURLConnection.HTTP_OK); diff --git a/services/device-registry-mongodb/src/main/java/org/eclipse/hono/deviceregistry/mongodb/service/MongoDbBasedDeviceManagementService.java b/services/device-registry-mongodb/src/main/java/org/eclipse/hono/deviceregistry/mongodb/service/MongoDbBasedDeviceManagementService.java index edb7d59cca..f60c2ef05a 100644 --- a/services/device-registry-mongodb/src/main/java/org/eclipse/hono/deviceregistry/mongodb/service/MongoDbBasedDeviceManagementService.java +++ b/services/device-registry-mongodb/src/main/java/org/eclipse/hono/deviceregistry/mongodb/service/MongoDbBasedDeviceManagementService.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2021, 2022 Contributors to the Eclipse Foundation + * Copyright (c) 2021, 2022, 2023 Contributors to the Eclipse Foundation * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. @@ -139,6 +139,7 @@ protected Future>> processSearchDevic final int pageOffset, final List filters, final List sortOptions, + final Optional isGateway, final Span span) { Objects.requireNonNull(tenantId); diff --git a/site/documentation/content/api/management/device-registry-v1.yaml b/site/documentation/content/api/management/device-registry-v1.yaml index e724739073..1affcfa3fa 100644 --- a/site/documentation/content/api/management/device-registry-v1.yaml +++ b/site/documentation/content/api/management/device-registry-v1.yaml @@ -370,6 +370,7 @@ paths: - $ref: '#/components/parameters/pageOffset' - $ref: '#/components/parameters/filterJson' - $ref: '#/components/parameters/sortJson' + - $ref: '#/components/parameters/isGateway' responses: 200: description: operation successful @@ -1687,7 +1688,7 @@ components: filterJson: name: filterJson in: query - description: | + description: | A predicate that objects in the result set must match. If this parameter is specified multiple times, objects in the result set must match all predicates. The predicate is specified as a string representing a JSON object complying with the following schema @@ -1772,6 +1773,20 @@ components: value: "{\"field\":\"/ext/brand\"}" sort descending: value: "{\"field\":\"/status/created\",\"direction\":\"desc\"}" + isGateway: + name: isGateway + in: query + description: | + Optional boolean filter parameter for searching only devices or gateways. If not used then result are both gateways and devices. + required: false + schema: + type: boolean + examples: + search only devices: + value: "isGateway=false" + search only gateways: + value: "isGateway=true" + responses: diff --git a/site/documentation/content/user-guide/device-registry.md b/site/documentation/content/user-guide/device-registry.md index 0d2369d6ff..1d3046ec11 100644 --- a/site/documentation/content/user-guide/device-registry.md +++ b/site/documentation/content/user-guide/device-registry.md @@ -57,14 +57,15 @@ The tenants in the registry can be managed using the Device Registry Management [tenant related resources]({{< relref "/api/management#tenants" >}}). {{% notice info %}} + The JDBC based registry implementation does not support the following features: * Tenants can be retrieved using the [search tenants]({{< relref "/api/management#tenants/searchTenants" >}}) - operation defined by the Device Registry Management API, but the *sortJson* query parameter is + operation defined by the Device Registry Management API, but the *filterJson* and *sortJson* query parameters are (currently) being ignored. The result set will always be sorted by the tenant Id in ascending order. * The *alias* and *trust-anchor-group* properties defined on a tenant are being ignored by the registry. Consequently, multiple tenants can not be configured to use the same trust anchor(s). -{{% /notice %}} +* {{% /notice %}} #### Registering a Certificate Authority @@ -138,17 +139,21 @@ The devices in the registry can be managed using the Device Registry Management [device related resources]({{< relref "/api/management#devices" >}}). {{% notice info %}} + The JDBC based registry implementation does not support the following features: -* Registration information can be retrieved using the - [search devices]({{< relref "/api/management#devices/searchDevicesForTenant" >}}) operation defined by the Device - Registry Management API. The *filterJson* query parameter currently only allows one filter expression per request, +* By [search devices]({{< relref "/api/management#devices/searchDevicesForTenant" >}}) operation defined by the Device + Registry Management API, the *filterJson* query parameter currently only allows one filter expression per request and the *sortJson* query parameter is (currently) being ignored. The result set will always be sorted by the device Id in ascending order. + +The Mongo DB based registry implementation does not support the following features: + +* By [search devices]({{< relref "/api/management#devices/searchDevicesForTenant" >}}) operation defined by the Device + Registry Management API, the *isGateway* query parameter is (currently) being ignored. {{% /notice %}} ### Managing Credentials The device's credentials can be managed using the Device Registry Management API's [credentials related resources]({{< relref "/api/management#credentials" >}}). - From f058ebd03d5b8b38d3fd2c8d5bd33a645f5bd770 Mon Sep 17 00:00:00 2001 From: "g.dimitropoulos" Date: Tue, 31 Oct 2023 08:39:41 +0100 Subject: [PATCH 2/4] fix review comments Also-by: Matthias Feurer m.feurer@sotec.eu Signed-off-by: georgios dimitropoulos --- .../org/eclipse/hono/util/RegistryManagementConstants.java | 2 +- .../org/eclipse/hono/service/http/AbstractHttpEndpoint.java | 4 +++- .../service/device/AbstractDeviceManagementService.java | 5 +++-- .../service/management/device/DeviceManagementService.java | 5 +++-- .../device/DelegatingDeviceManagementHttpEndpointTest.java | 2 +- .../jdbc/impl/DeviceManagementServiceImpl.java | 2 +- .../mongodb/service/MongoDbBasedDeviceManagementService.java | 2 +- 7 files changed, 13 insertions(+), 9 deletions(-) diff --git a/core/src/main/java/org/eclipse/hono/util/RegistryManagementConstants.java b/core/src/main/java/org/eclipse/hono/util/RegistryManagementConstants.java index 12d8c00ee5..47c79e2c0c 100644 --- a/core/src/main/java/org/eclipse/hono/util/RegistryManagementConstants.java +++ b/core/src/main/java/org/eclipse/hono/util/RegistryManagementConstants.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2019, 2021, 2023 Contributors to the Eclipse Foundation + * Copyright (c) 2019, 2023 Contributors to the Eclipse Foundation * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. diff --git a/service-base/src/main/java/org/eclipse/hono/service/http/AbstractHttpEndpoint.java b/service-base/src/main/java/org/eclipse/hono/service/http/AbstractHttpEndpoint.java index c5efaaea89..87ae4c3dd7 100644 --- a/service-base/src/main/java/org/eclipse/hono/service/http/AbstractHttpEndpoint.java +++ b/service-base/src/main/java/org/eclipse/hono/service/http/AbstractHttpEndpoint.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2016, 2022, 2023 Contributors to the Eclipse Foundation + * Copyright (c) 2016, 2023 Contributors to the Eclipse Foundation * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. @@ -85,6 +85,8 @@ public abstract class AbstractHttpEndpoint ex /** * A function that tries to parse a string into an Optional boolean. + * It takes a valid boolean string value (e.g "true", "false") and returns an Optional of boolean type, + * if input string can be parsed as boolean else Optional.empty. */ protected static final Function> CONVERTER_BOOLEAN = s -> { return Strings.isNullOrEmpty(s) ? Optional.empty() : Optional.of(Boolean.valueOf(s)); diff --git a/services/device-registry-base/src/main/java/org/eclipse/hono/deviceregistry/service/device/AbstractDeviceManagementService.java b/services/device-registry-base/src/main/java/org/eclipse/hono/deviceregistry/service/device/AbstractDeviceManagementService.java index 3bc927c71f..e82b05bc7f 100644 --- a/services/device-registry-base/src/main/java/org/eclipse/hono/deviceregistry/service/device/AbstractDeviceManagementService.java +++ b/services/device-registry-base/src/main/java/org/eclipse/hono/deviceregistry/service/device/AbstractDeviceManagementService.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2020, 2022, 2023 Contributors to the Eclipse Foundation + * Copyright (c) 2020, 2023 Contributors to the Eclipse Foundation * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. @@ -225,7 +225,8 @@ protected Future> processDeleteDevicesOfTenant(final String tenantI * retrieve the whole result set page by page. * @param filters A list of filters. The filters are predicates that objects in the result set must match. * @param sortOptions A list of sort options. The sortOptions specify properties to sort the result set by. - * @param isGateway Optional filter for searching all gateways or only devices. + * @param isGateway Optional filter for searching only gateways or only devices. + * If given parameter is Optional.empty() result will contain both gateways and devices. * @param span The active OpenTracing span to use for tracking this operation. *

* Implementations must not invoke the {@link Span#finish()} nor the {@link Span#finish(long)} diff --git a/services/device-registry-base/src/main/java/org/eclipse/hono/service/management/device/DeviceManagementService.java b/services/device-registry-base/src/main/java/org/eclipse/hono/service/management/device/DeviceManagementService.java index fbdf79297b..02a5058550 100644 --- a/services/device-registry-base/src/main/java/org/eclipse/hono/service/management/device/DeviceManagementService.java +++ b/services/device-registry-base/src/main/java/org/eclipse/hono/service/management/device/DeviceManagementService.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2016, 2021, 2023 Contributors to the Eclipse Foundation + * Copyright (c) 2016, 2023 Contributors to the Eclipse Foundation * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. @@ -102,7 +102,8 @@ public interface DeviceManagementService { * Implementations must not invoke the {@link Span#finish()} nor the {@link Span#finish(long)} * methods. However,implementations may log (error) events on this span, set tags and use this span * as the parent for additional spans created as part of this method's execution. - * @param isGateway Optional filter for searching all gateways or only devices. + * @param isGateway Optional filter for searching only gateways or only devices. + * If given parameter is Optional.empty() result will contain both gateways and devices. * @return A future indicating the outcome of the operation. *

* The future will be succeeded with a result containing the matching devices. Otherwise, the future will diff --git a/services/device-registry-base/src/test/java/org/eclipse/hono/service/management/device/DelegatingDeviceManagementHttpEndpointTest.java b/services/device-registry-base/src/test/java/org/eclipse/hono/service/management/device/DelegatingDeviceManagementHttpEndpointTest.java index 72065d3bb8..ea4361c2a7 100644 --- a/services/device-registry-base/src/test/java/org/eclipse/hono/service/management/device/DelegatingDeviceManagementHttpEndpointTest.java +++ b/services/device-registry-base/src/test/java/org/eclipse/hono/service/management/device/DelegatingDeviceManagementHttpEndpointTest.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2020, 2021, 2023 Contributors to the Eclipse Foundation + * Copyright (c) 2020, 2023 Contributors to the Eclipse Foundation * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. diff --git a/services/device-registry-jdbc/src/main/java/org/eclipse/hono/deviceregistry/jdbc/impl/DeviceManagementServiceImpl.java b/services/device-registry-jdbc/src/main/java/org/eclipse/hono/deviceregistry/jdbc/impl/DeviceManagementServiceImpl.java index da27caf8df..d8a569ffec 100644 --- a/services/device-registry-jdbc/src/main/java/org/eclipse/hono/deviceregistry/jdbc/impl/DeviceManagementServiceImpl.java +++ b/services/device-registry-jdbc/src/main/java/org/eclipse/hono/deviceregistry/jdbc/impl/DeviceManagementServiceImpl.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2020, 2022, 2023 Contributors to the Eclipse Foundation + * Copyright (c) 2020, 2023 Contributors to the Eclipse Foundation * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. diff --git a/services/device-registry-mongodb/src/main/java/org/eclipse/hono/deviceregistry/mongodb/service/MongoDbBasedDeviceManagementService.java b/services/device-registry-mongodb/src/main/java/org/eclipse/hono/deviceregistry/mongodb/service/MongoDbBasedDeviceManagementService.java index f60c2ef05a..c2f074d47d 100644 --- a/services/device-registry-mongodb/src/main/java/org/eclipse/hono/deviceregistry/mongodb/service/MongoDbBasedDeviceManagementService.java +++ b/services/device-registry-mongodb/src/main/java/org/eclipse/hono/deviceregistry/mongodb/service/MongoDbBasedDeviceManagementService.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2021, 2022, 2023 Contributors to the Eclipse Foundation + * Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. From 055052884cd868529fb3011d4812da2c23a14e08 Mon Sep 17 00:00:00 2001 From: "g.dimitropoulos" Date: Mon, 20 Nov 2023 09:46:54 +0100 Subject: [PATCH 3/4] fix review comments and revert changes Also-by: Matthias Feurer m.feurer@sotec.eu Signed-off-by: georgios dimitropoulos --- .../jdbc/store/device/TableManagementStore.java | 5 +++-- .../AbstractDeviceManagementSearchDevicesTest.java | 10 +++++----- .../content/api/management/device-registry-v1.yaml | 5 +++-- .../content/user-guide/device-registry.md | 14 +++++++------- 4 files changed, 18 insertions(+), 16 deletions(-) diff --git a/services/base-jdbc/src/main/java/org/eclipse/hono/service/base/jdbc/store/device/TableManagementStore.java b/services/base-jdbc/src/main/java/org/eclipse/hono/service/base/jdbc/store/device/TableManagementStore.java index c49a6dabfa..813008737e 100644 --- a/services/base-jdbc/src/main/java/org/eclipse/hono/service/base/jdbc/store/device/TableManagementStore.java +++ b/services/base-jdbc/src/main/java/org/eclipse/hono/service/base/jdbc/store/device/TableManagementStore.java @@ -944,7 +944,7 @@ private List parseCredentials(final ResultSet result) { final var entries = result.getRows(true); return entries.stream() - .map(o -> o.getString("data")) + .map(o -> o.getString(DATA)) .map(s -> Json.decodeValue(s, CommonCredential.class)) .collect(Collectors.toList()); @@ -957,7 +957,8 @@ private List parseCredentials(final ResultSet result) { * @param pageSize The page size. * @param pageOffset The page offset. * @param filters The list of filters (currently only the first value of the list is used). - * @param isGateway Optional search gateway or only devices filter. + * @param isGateway Optional filter for searching only gateways or only devices. + * If given parameter is Optional.empty() result will contain both gateways and devices. * @param spanContext The span to contribute to. * @return A future containing devices. */ diff --git a/services/device-registry-base/src/test/java/org/eclipse/hono/service/management/device/AbstractDeviceManagementSearchDevicesTest.java b/services/device-registry-base/src/test/java/org/eclipse/hono/service/management/device/AbstractDeviceManagementSearchDevicesTest.java index 2a4f3cb937..e8b725af47 100644 --- a/services/device-registry-base/src/test/java/org/eclipse/hono/service/management/device/AbstractDeviceManagementSearchDevicesTest.java +++ b/services/device-registry-base/src/test/java/org/eclipse/hono/service/management/device/AbstractDeviceManagementSearchDevicesTest.java @@ -126,11 +126,11 @@ default void testSearchDevicesWithMultipleFiltersSucceeds(final VertxTestContext "testDevice1", new Device().setEnabled(true).setVia(List.of("gw-1")), "testDevice2", new Device().setEnabled(false))) .compose(ok -> getDeviceManagementService() - .searchDevices(tenantId, pageSize, pageOffset, List.of(filter1, filter2, filter3), - List.of(), Optional.empty(), NoopSpan.INSTANCE)) - .onComplete(ctx.succeeding(s -> { - ctx.verify(() -> { - assertThat(s.getStatus()).isEqualTo(HttpURLConnection.HTTP_OK); + .searchDevices(tenantId, pageSize, pageOffset, List.of(filter1, filter2, filter3), + List.of(), Optional.empty(), NoopSpan.INSTANCE)) + .onComplete(ctx.succeeding(s -> { + ctx.verify(() -> { + assertThat(s.getStatus()).isEqualTo(HttpURLConnection.HTTP_OK); final SearchResult searchResult = s.getPayload(); assertThat(searchResult.getTotal()).isEqualTo(1); diff --git a/site/documentation/content/api/management/device-registry-v1.yaml b/site/documentation/content/api/management/device-registry-v1.yaml index 1affcfa3fa..0f1c161530 100644 --- a/site/documentation/content/api/management/device-registry-v1.yaml +++ b/site/documentation/content/api/management/device-registry-v1.yaml @@ -1688,7 +1688,7 @@ components: filterJson: name: filterJson in: query - description: | + description: | A predicate that objects in the result set must match. If this parameter is specified multiple times, objects in the result set must match all predicates. The predicate is specified as a string representing a JSON object complying with the following schema @@ -1777,7 +1777,8 @@ components: name: isGateway in: query description: | - Optional boolean filter parameter for searching only devices or gateways. If not used then result are both gateways and devices. + Optional boolean filter parameter for searching only gateways or only devices. + If not used then result will contain both gateways and devices. required: false schema: type: boolean diff --git a/site/documentation/content/user-guide/device-registry.md b/site/documentation/content/user-guide/device-registry.md index 1d3046ec11..cba11f8aba 100644 --- a/site/documentation/content/user-guide/device-registry.md +++ b/site/documentation/content/user-guide/device-registry.md @@ -57,7 +57,6 @@ The tenants in the registry can be managed using the Device Registry Management [tenant related resources]({{< relref "/api/management#tenants" >}}). {{% notice info %}} - The JDBC based registry implementation does not support the following features: * Tenants can be retrieved using the [search tenants]({{< relref "/api/management#tenants/searchTenants" >}}) @@ -65,7 +64,7 @@ The JDBC based registry implementation does not support the following features: (currently) being ignored. The result set will always be sorted by the tenant Id in ascending order. * The *alias* and *trust-anchor-group* properties defined on a tenant are being ignored by the registry. Consequently, multiple tenants can not be configured to use the same trust anchor(s). -* {{% /notice %}} +{{% /notice %}} #### Registering a Certificate Authority @@ -139,18 +138,19 @@ The devices in the registry can be managed using the Device Registry Management [device related resources]({{< relref "/api/management#devices" >}}). {{% notice info %}} - The JDBC based registry implementation does not support the following features: -* By [search devices]({{< relref "/api/management#devices/searchDevicesForTenant" >}}) operation defined by the Device - Registry Management API, the *filterJson* query parameter currently only allows one filter expression per request and +* Registration information can be retrieved using the + [search devices]({{< relref "/api/management#devices/searchDevicesForTenant" >}}) operation defined by the Device + Registry Management API. The *filterJson* query parameter currently only allows one filter expression per request, the *sortJson* query parameter is (currently) being ignored. The result set will always be sorted by the device Id in ascending order. The Mongo DB based registry implementation does not support the following features: -* By [search devices]({{< relref "/api/management#devices/searchDevicesForTenant" >}}) operation defined by the Device - Registry Management API, the *isGateway* query parameter is (currently) being ignored. +* Registration information can be retrieved using the + [search devices]({{< relref "/api/management#devices/searchDevicesForTenant" >}}) operation defined by the Device + Registry Management API. The *isGateway* query parameter is (currently) being ignored. {{% /notice %}} ### Managing Credentials From abea4da7ad2aa0e3fcb2106857e2a4fe4a6617db Mon Sep 17 00:00:00 2001 From: "g.dimitropoulos" Date: Wed, 22 Nov 2023 11:19:59 +0100 Subject: [PATCH 4/4] fix indentation and edit api documentation Also-by: Matthias Feurer m.feurer@sotec.eu Signed-off-by: georgios dimitropoulos --- .../store/device/TableManagementStore.java | 1 + ...ractDeviceManagementSearchDevicesTest.java | 116 ++++++++---------- .../api/management/device-registry-v1.yaml | 4 +- 3 files changed, 53 insertions(+), 68 deletions(-) diff --git a/services/base-jdbc/src/main/java/org/eclipse/hono/service/base/jdbc/store/device/TableManagementStore.java b/services/base-jdbc/src/main/java/org/eclipse/hono/service/base/jdbc/store/device/TableManagementStore.java index 813008737e..595d846a1a 100644 --- a/services/base-jdbc/src/main/java/org/eclipse/hono/service/base/jdbc/store/device/TableManagementStore.java +++ b/services/base-jdbc/src/main/java/org/eclipse/hono/service/base/jdbc/store/device/TableManagementStore.java @@ -957,6 +957,7 @@ private List parseCredentials(final ResultSet result) { * @param pageSize The page size. * @param pageOffset The page offset. * @param filters The list of filters (currently only the first value of the list is used). + * Will be ignored if parameter isGateway is being used. * @param isGateway Optional filter for searching only gateways or only devices. * If given parameter is Optional.empty() result will contain both gateways and devices. * @param spanContext The span to contribute to. diff --git a/services/device-registry-base/src/test/java/org/eclipse/hono/service/management/device/AbstractDeviceManagementSearchDevicesTest.java b/services/device-registry-base/src/test/java/org/eclipse/hono/service/management/device/AbstractDeviceManagementSearchDevicesTest.java index e8b725af47..4f6e384135 100644 --- a/services/device-registry-base/src/test/java/org/eclipse/hono/service/management/device/AbstractDeviceManagementSearchDevicesTest.java +++ b/services/device-registry-base/src/test/java/org/eclipse/hono/service/management/device/AbstractDeviceManagementSearchDevicesTest.java @@ -126,19 +126,19 @@ default void testSearchDevicesWithMultipleFiltersSucceeds(final VertxTestContext "testDevice1", new Device().setEnabled(true).setVia(List.of("gw-1")), "testDevice2", new Device().setEnabled(false))) .compose(ok -> getDeviceManagementService() - .searchDevices(tenantId, pageSize, pageOffset, List.of(filter1, filter2, filter3), - List.of(), Optional.empty(), NoopSpan.INSTANCE)) - .onComplete(ctx.succeeding(s -> { - ctx.verify(() -> { - assertThat(s.getStatus()).isEqualTo(HttpURLConnection.HTTP_OK); - - final SearchResult searchResult = s.getPayload(); - assertThat(searchResult.getTotal()).isEqualTo(1); - assertThat(searchResult.getResult()).hasSize(1); - assertThat(searchResult.getResult().get(0).getId()).isEqualTo("testDevice1"); - }); - ctx.completeNow(); - })); + .searchDevices(tenantId, pageSize, pageOffset, List.of(filter1, filter2, filter3), + List.of(), Optional.empty(), NoopSpan.INSTANCE)) + .onComplete(ctx.succeeding(s -> { + ctx.verify(() -> { + assertThat(s.getStatus()).isEqualTo(HttpURLConnection.HTTP_OK); + + final SearchResult searchResult = s.getPayload(); + assertThat(searchResult.getTotal()).isEqualTo(1); + assertThat(searchResult.getResult()).hasSize(1); + assertThat(searchResult.getResult().get(0).getId()).isEqualTo("testDevice1"); + }); + ctx.completeNow(); + })); } /** @@ -346,32 +346,24 @@ default void testSearchDevicesWithWildCardToMatchMultipleCharacters(final VertxT final Filter filter2 = new Filter("/ext/value", "test$1*e"); final Sort sortOption = new Sort("/id"); - createDevices( - tenantId, - Map.of( - "testDevice", new Device(), - "testDevice-1", new Device().setExtensions(Map.of("value", "test$1Value")), - "testDevice-2", new Device().setExtensions(Map.of("value", "test$2Value"))) - ) - .compose(ok -> getDeviceManagementService().searchDevices( - tenantId, - pageSize, - pageOffset, - List.of(filter1, filter2), - List.of(sortOption), - Optional.empty(), - NoopSpan.INSTANCE)) - .onComplete(ctx.succeeding(s -> { - ctx.verify(() -> { - assertThat(s.getStatus()).isEqualTo(HttpURLConnection.HTTP_OK); - - final SearchResult searchResult = s.getPayload(); - assertThat(searchResult.getTotal()).isEqualTo(1); - assertThat(searchResult.getResult()).hasSize(1); - assertThat(searchResult.getResult().get(0).getId()).isEqualTo("testDevice-1"); - }); - ctx.completeNow(); - })); + createDevices(tenantId, Map.of( + "testDevice", new Device(), + "testDevice-1", new Device().setExtensions(Map.of("value", "test$1Value")), + "testDevice-2", new Device().setExtensions(Map.of("value", "test$2Value")))) + .compose(ok -> getDeviceManagementService() + .searchDevices(tenantId, pageSize, pageOffset, List.of(filter1, filter2), + List.of(sortOption), Optional.empty(), NoopSpan.INSTANCE) + .onComplete(ctx.succeeding(s -> { + ctx.verify(() -> { + assertThat(s.getStatus()).isEqualTo(HttpURLConnection.HTTP_OK); + + final SearchResult searchResult = s.getPayload(); + assertThat(searchResult.getTotal()).isEqualTo(1); + assertThat(searchResult.getResult()).hasSize(1); + assertThat(searchResult.getResult().get(0).getId()).isEqualTo("testDevice-1"); + }); + ctx.completeNow(); + }))); } /** @@ -389,33 +381,25 @@ default void testSearchDevicesWithCardToMatchSingleCharacter(final VertxTestCont final Filter filter2 = new Filter("/ext/value", "test$?Value"); final Sort sortOption = new Sort("/id"); - createDevices( - tenantId, - Map.of( - "testDevice-x", new Device().setExtensions(Map.of("value", "test$Value")), - "testDevice-1", new Device().setExtensions(Map.of("value", "test$1Value")), - "testDevice-2", new Device().setExtensions(Map.of("value", "test$2Value"))) - ) - .compose(ok -> getDeviceManagementService().searchDevices( - tenantId, - pageSize, - pageOffset, - List.of(filter1, filter2), - List.of(sortOption), - Optional.empty(), - NoopSpan.INSTANCE)) - .onComplete(ctx.succeeding(s -> { - ctx.verify(() -> { - assertThat(s.getStatus()).isEqualTo(HttpURLConnection.HTTP_OK); - - final SearchResult searchResult = s.getPayload(); - assertThat(searchResult.getTotal()).isEqualTo(2); - assertThat(searchResult.getResult()).hasSize(2); - assertThat(searchResult.getResult().get(0).getId()).isEqualTo("testDevice-1"); - assertThat(searchResult.getResult().get(1).getId()).isEqualTo("testDevice-2"); - }); - ctx.completeNow(); - })); + createDevices(tenantId, Map.of( + "testDevice-x", new Device().setExtensions(Map.of("value", "test$Value")), + "testDevice-1", new Device().setExtensions(Map.of("value", "test$1Value")), + "testDevice-2", new Device().setExtensions(Map.of("value", "test$2Value")))) + .compose(ok -> getDeviceManagementService() + .searchDevices(tenantId, pageSize, pageOffset, List.of(filter1, filter2), + List.of(sortOption), Optional.empty(), NoopSpan.INSTANCE) + .onComplete(ctx.succeeding(s -> { + ctx.verify(() -> { + assertThat(s.getStatus()).isEqualTo(HttpURLConnection.HTTP_OK); + + final SearchResult searchResult = s.getPayload(); + assertThat(searchResult.getTotal()).isEqualTo(2); + assertThat(searchResult.getResult()).hasSize(2); + assertThat(searchResult.getResult().get(0).getId()).isEqualTo("testDevice-1"); + assertThat(searchResult.getResult().get(1).getId()).isEqualTo("testDevice-2"); + }); + ctx.completeNow(); + }))); } /** diff --git a/site/documentation/content/api/management/device-registry-v1.yaml b/site/documentation/content/api/management/device-registry-v1.yaml index 0f1c161530..997643ad6d 100644 --- a/site/documentation/content/api/management/device-registry-v1.yaml +++ b/site/documentation/content/api/management/device-registry-v1.yaml @@ -1689,8 +1689,8 @@ components: name: filterJson in: query description: | - A predicate that objects in the result set must match. If this parameter is specified multiple - times, objects in the result set must match all predicates. + A predicate that objects in the result set must match (will be ignored if parameter isGateway is being used). + If this parameter is specified multiple times, objects in the result set must match all predicates. The predicate is specified as a string representing a JSON object complying with the following schema ```