From ff0854cd8bf2c39b480ec64104770f398ae322b6 Mon Sep 17 00:00:00 2001 From: "Signed-off-by: g.dimitropoulos" Date: Thu, 6 Jul 2023 15:54:13 +0200 Subject: [PATCH 1/9] Implement backend filter for H2 & Postgresql: - field/value filter - value filter of type bool/number/string - enable filter wildcards for string value - add documentation Also-by: Matthias Feurer m.feurer@sotec.eu Signed-off-by: g.dimitropoulos --- .../store/device/TableManagementStore.java | 545 ++++++++++-------- .../base/jdbc/store/device/base.h2.sql.yaml | 23 + .../store/device/base.postgresql.sql.yaml | 20 + .../base/jdbc/store/device/base.sql.yaml | 6 +- .../impl/DeviceManagementServiceImpl.java | 38 +- ...java => AbstractJdbcBaseRegistryTest.java} | 10 +- .../AbstractJdbcPostgresBaseRegistryTest.java | 37 ++ ...Test.java => BaseRegistryServiceTest.java} | 2 +- .../impl/JdbcBasedCredentialsServiceTest.java | 2 +- ...asedDeviceManagementSearchDevicesTest.java | 283 +++++++-- .../JdbcBasedRegistrationServiceTest.java | 2 +- ...asedTenantManagementSearchTenantsTest.java | 2 +- .../jdbc/impl/JdbcBasedTenantServiceTest.java | 2 +- ...gresDeviceManagementSearchDevicesTest.java | 312 ++++++++++ .../jdbc/impl/ResolveGroupsTest.java | 2 +- .../jdbc/impl/TenantServiceTest.java | 2 +- .../api/management/device-registry-v1.yaml | 3 +- .../content/user-guide/device-registry.md | 5 +- 18 files changed, 969 insertions(+), 327 deletions(-) create mode 100644 services/base-jdbc/src/main/resources/org/eclipse/hono/service/base/jdbc/store/device/base.h2.sql.yaml rename services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/{AbstractJdbcRegistryTest.java => AbstractJdbcBaseRegistryTest.java} (96%) create mode 100644 services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/AbstractJdbcPostgresBaseRegistryTest.java rename services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/{RegistryServiceTest.java => BaseRegistryServiceTest.java} (98%) create mode 100644 services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/JdbcPostgresDeviceManagementSearchDevicesTest.java 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 fcc12224a3..06b5af16f0 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 @@ -37,6 +37,7 @@ import org.eclipse.hono.service.base.jdbc.store.Statement; import org.eclipse.hono.service.base.jdbc.store.StatementConfiguration; import org.eclipse.hono.service.base.jdbc.store.model.JdbcBasedDeviceDto; +import org.eclipse.hono.service.management.Filter; import org.eclipse.hono.service.management.SearchResult; import org.eclipse.hono.service.management.credentials.CommonCredential; import org.eclipse.hono.service.management.credentials.CredentialsDto; @@ -68,6 +69,18 @@ public class TableManagementStore extends AbstractDeviceStore { private static final Logger log = LoggerFactory.getLogger(TableManagementStore.class); + private static final String TENANT_ID = "tenant_id"; + private static final String DEVICE_ID = "device_id"; + private static final String VERSION = "version"; + private static final String DATA = "data"; + private static final String EXPECTED_VERSION = "expected_version"; + private static final String PAGE_SIZE = "page_size"; + private static final String PAGE_OFFSET = "page_offset"; + private static final String CREATED = "created"; + private static final String AUTO_PROVISIONED = "auto_provisioned"; + private static final String NEXT_VERSION = "next_version"; + private static final String FIELD = "field"; + private static final String VALUE = "value"; private final Statement createStatement; private final Statement createMemberOfStatement; private final Statement deleteAllMemberOfStatement; @@ -85,15 +98,17 @@ public class TableManagementStore extends AbstractDeviceStore { private final Statement updateDeviceVersionStatement; private final Statement countDevicesOfTenantStatement; + private final Statement countDevicesWithFilter; private final Statement findDevicesStatement; + private final Statement findDevicesOfTenantWithFilterStatement; /** * Create a new instance. * * @param client The client to use for accessing the DB. * @param tracer The tracer to use. - * @param cfg The SQL statement configuration. + * @param cfg The SQL statement configuration. */ public TableManagementStore(final JDBCClient client, final Tracer tracer, final StatementConfiguration cfg) { super(client, tracer, cfg); @@ -102,100 +117,155 @@ public TableManagementStore(final JDBCClient client, final Tracer tracer, final this.createStatement = cfg .getRequiredStatement("create") .validateParameters( - "tenant_id", - "device_id", - "version", - "data", - "created", - "auto_provisioned"); + TENANT_ID, + DEVICE_ID, + VERSION, + DATA, + CREATED, + AUTO_PROVISIONED); this.createMemberOfStatement = cfg .getRequiredStatement("createMemberOf") .validateParameters( - "tenant_id", - "device_id", + TENANT_ID, + DEVICE_ID, "group_id"); this.deleteAllMemberOfStatement = cfg .getRequiredStatement("deleteAllMemberOf") .validateParameters( - "tenant_id", - "device_id"); + TENANT_ID, + DEVICE_ID); this.updateRegistrationVersionedStatement = cfg .getRequiredStatement("updateRegistrationVersioned") .validateParameters( - "tenant_id", - "device_id", - "next_version", - "data", - "expected_version", + TENANT_ID, + DEVICE_ID, + NEXT_VERSION, + DATA, + EXPECTED_VERSION, "updated_on", "auto_provisioning_notification_sent"); this.deleteStatement = cfg .getRequiredStatement("delete") .validateParameters( - "tenant_id", - "device_id"); + TENANT_ID, + DEVICE_ID); this.deleteVersionedStatement = cfg .getRequiredStatement("deleteVersioned") .validateParameters( - "tenant_id", - "device_id", - "expected_version"); + TENANT_ID, + DEVICE_ID, + EXPECTED_VERSION); this.dropTenantStatement = cfg .getRequiredStatement("dropTenant") .validateParameters( - "tenant_id"); + TENANT_ID); this.readForUpdateStatement = cfg.getRequiredStatement("readForUpdate") .validateParameters( - "tenant_id", - "device_id"); + TENANT_ID, + DEVICE_ID); this.readCredentialsStatement = cfg .getRequiredStatement("readCredentials") .validateParameters( - "tenant_id", - "device_id"); + TENANT_ID, + DEVICE_ID); this.insertCredentialEntryStatement = cfg .getRequiredStatement("insertCredentialEntry") .validateParameters( - "tenant_id", - "device_id", + TENANT_ID, + DEVICE_ID, "type", "auth_id", - "data"); + DATA); this.deleteAllCredentialsStatement = cfg .getRequiredStatement("deleteAllCredentials") .validateParameters( - "tenant_id", - "device_id"); + TENANT_ID, + DEVICE_ID); this.updateDeviceVersionStatement = cfg .getRequiredStatement("updateDeviceVersion") .validateParameters( - "tenant_id", - "device_id", - "next_version", - "expected_version"); + TENANT_ID, + DEVICE_ID, + NEXT_VERSION, + EXPECTED_VERSION); this.countDevicesOfTenantStatement = cfg .getRequiredStatement("countDevicesOfTenant") .validateParameters( - "tenant_id"); + TENANT_ID); + + this.countDevicesWithFilter = cfg + .getRequiredStatement("countDevicesOfTenantWithFilter") + .validateParameters( + TENANT_ID, + FIELD, + VALUE); this.findDevicesStatement = cfg .getRequiredStatement("findDevicesOfTenant") .validateParameters( - "tenant_id", - "page_size", - "page_offset"); + TENANT_ID, + PAGE_SIZE, + PAGE_OFFSET); + + this.findDevicesOfTenantWithFilterStatement = cfg + .getRequiredStatement("findDevicesOfTenantWithFilter") + .validateParameters( + TENANT_ID, + FIELD, + VALUE, + PAGE_SIZE, + 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)); } @@ -208,8 +278,8 @@ public TableManagementStore(final JDBCClient client, final Tracer tracer, final * It returns the plain result set from the query, which may also be empty. * * @param connection The connection to use. - * @param key The key of the device. - * @param span The span to contribute to. + * @param key The key of the device. + * @param span The span to contribute to. * @return A future tracking the outcome of the operation. */ protected Future readDeviceForUpdate(final SQLConnection connection, final DeviceKey key, final SpanContext span) { @@ -227,12 +297,12 @@ protected Future readDeviceForUpdate(final SQLConnection connection, * a duplicate entity or constraint violation. This will be translated into a * failed future with an {@link org.eclipse.hono.service.base.jdbc.store.DuplicateKeyException}. * - * @param key The key of the device to create. - * @param device The device data. - * @param tenant The configuration of the tenant that the device belongs to. + * @param key The key of the device to create. + * @param device The device data. + * @param tenant The configuration of the tenant that the device belongs to. * @param globalDevicesPerTenantLimit The globally defined maximum number of devices per tenant. A value * <= 0 will be interpreted as no limit being defined. - * @param spanContext The span to contribute to. + * @param spanContext The span to contribute to. * @return A future, tracking the outcome of the operation. */ public Future> createDevice( @@ -257,17 +327,17 @@ public Future> createDevice( .runTransactionally(this.client, this.tracer, span.context(), (connection, context) -> { final var expanded = this.createStatement.expand(params -> { - params.put("tenant_id", deviceDto.getTenantId()); - params.put("device_id", deviceDto.getDeviceId()); - params.put("version", deviceDto.getVersion()); - params.put("data", deviceDto.getDeviceJson()); - params.put("created", Timestamp.from(deviceDto.getCreationTime())); - params.put("auto_provisioned", deviceDto.isAutoProvisioned()); + params.put(TENANT_ID, deviceDto.getTenantId()); + params.put(DEVICE_ID, deviceDto.getDeviceId()); + params.put(VERSION, deviceDto.getVersion()); + params.put(DATA, deviceDto.getDeviceJson()); + params.put(CREATED, Timestamp.from(deviceDto.getCreationTime())); + params.put(AUTO_PROVISIONED, deviceDto.isAutoProvisioned()); }); log.debug("createDevice - statement: {}", expanded); - return getDeviceCount(key.getTenantId(), span.context()) + return getDeviceCount(key.getTenantId(), span.context(), this.countDevicesOfTenantStatement, null, null) .compose(currentDeviceCount -> tenant.checkDeviceLimitReached( key.getTenantId(), currentDeviceCount, @@ -293,33 +363,33 @@ private Future createGroups( final SpanContext context) { return CompositeFuture.all(memberOf.stream() - .map(groupId -> { + .map(groupId -> { - final var expanded = this.createMemberOfStatement.expand(params -> { - params.put("tenant_id", key.getTenantId()); - params.put("device_id", key.getDeviceId()); - params.put("group_id", groupId); - }); + final var expanded = this.createMemberOfStatement.expand(params -> { + params.put(TENANT_ID, key.getTenantId()); + params.put(DEVICE_ID, key.getDeviceId()); + params.put("group_id", groupId); + }); - log.debug("addToGroup - statement: {}", expanded); + log.debug("addToGroup - statement: {}", expanded); - return expanded - .trace(this.tracer, context) - .update(connection) - .recover(SQL::translateException); - }) - .collect(Collectors.toList())) + return expanded + .trace(this.tracer, context) + .update(connection) + .recover(SQL::translateException); + }) + .collect(Collectors.toList())) .mapEmpty(); } private Future deleteGroups(final SQLConnection connection, - final DeviceKey key, - final SpanContext context) { + final DeviceKey key, + final SpanContext context) { final var expanded = this.deleteAllMemberOfStatement.expand(params -> { - params.put("tenant_id", key.getTenantId()); - params.put("device_id", key.getDeviceId()); + params.put(TENANT_ID, key.getTenantId()); + params.put(DEVICE_ID, key.getDeviceId()); }); log.debug("deleteGroups - statement: {}", expanded); @@ -348,12 +418,12 @@ private Future deleteGroups(final SQLConnection connection, * not exists. If the device exists, but the resource version does not match, the result * will fail with an {@link OptimisticLockingException}. * - * @param key The key of the device to update. - * @param statement The statement to use for the update. - * @param jsonValue The value to set. + * @param key The key of the device to update. + * @param statement The statement to use for the update. + * @param jsonValue The value to set. * @param resourceVersion The optional resource version. - * @param nextVersion The new version to set. - * @param span The span to contribute to. + * @param nextVersion The new version to set. + * @param span The span to contribute to. * @return A future, tracking the outcome of the operation. */ protected Future updateJsonField( @@ -365,11 +435,11 @@ protected Future updateJsonField( final Span span) { final var expanded = statement.expand(map -> { - map.put("tenant_id", key.getTenantId()); - map.put("device_id", key.getDeviceId()); - map.put("next_version", nextVersion); - map.put("data", jsonValue); - resourceVersion.ifPresent(version -> map.put("expected_version", version)); + map.put(TENANT_ID, key.getTenantId()); + map.put(DEVICE_ID, key.getDeviceId()); + map.put(NEXT_VERSION, nextVersion); + map.put(DATA, jsonValue); + resourceVersion.ifPresent(version -> map.put(EXPECTED_VERSION, version)); }); log.debug("update - statement: {}", expanded); @@ -394,10 +464,10 @@ protected Future updateJsonField( * with either the {@code updateRegistration} or {@code updateRegistrationVersioned} * statement. * - * @param key The key of the device to update. - * @param device The device data to store. + * @param key The key of the device to update. + * @param device The device data to store. * @param resourceVersion The optional resource version. - * @param spanContext The span to contribute to. + * @param spanContext The span to contribute to. * @return A future, tracking the outcome of the operation. */ public Future> updateDevice( @@ -411,7 +481,7 @@ public Future> updateDevice( .withTag(TracingHelper.TAG_DEVICE_ID, key.getDeviceId()) .start(); - resourceVersion.ifPresent(version -> span.setTag("version", version)); + resourceVersion.ifPresent(version -> span.setTag(VERSION, version)); final var memberOf = Optional.ofNullable(device.getMemberOf()) .>map(HashSet::new) @@ -440,11 +510,11 @@ public Future> updateDevice( // update the version, this will release the lock .compose(version -> this.updateRegistrationVersionedStatement .expand(map -> { - map.put("tenant_id", deviceDto.getTenantId()); - map.put("device_id", deviceDto.getDeviceId()); - map.put("data", deviceDto.getDeviceJson()); - map.put("expected_version", version); - map.put("next_version", deviceDto.getVersion()); + map.put(TENANT_ID, deviceDto.getTenantId()); + map.put(DEVICE_ID, deviceDto.getDeviceId()); + map.put(DATA, deviceDto.getDeviceJson()); + map.put(EXPECTED_VERSION, version); + map.put(NEXT_VERSION, deviceDto.getVersion()); map.put("updated_on", Timestamp.from(deviceDto.getUpdatedOn())); map.put("auto_provisioning_notification_sent", deviceDto.isAutoProvisioningNotificationSent()); @@ -477,7 +547,7 @@ public Future> updateDevice( * If there is exactly one row, it will read the device registration information from the column * {@code data} and optionally current resource version from the column {@code version}. * - * @param key The key of the device to read. + * @param key The key of the device to read. * @param spanContext The span to contribute to. * @return A future, tracking the outcome of the operation. */ @@ -515,9 +585,9 @@ public Future> readDevice(final DeviceKey key, final * the named parameters {@code tenant_id}, {@code device_id}, and {@code expected_version} (if set). * It will return the plain update result of the operation. * - * @param key The key of the device to delete. + * @param key The key of the device to delete. * @param resourceVersion An optional resource version. - * @param spanContext The span to contribute to. + * @param spanContext The span to contribute to. * @return A future, tracking the outcome of the operation. */ public Future deleteDevice( @@ -530,7 +600,7 @@ public Future deleteDevice( .withTag(TracingHelper.TAG_DEVICE_ID, key.getDeviceId()) .start(); - resourceVersion.ifPresent(version -> span.setTag("version", version)); + resourceVersion.ifPresent(version -> span.setTag(VERSION, version)); final Statement statement; if (resourceVersion.isPresent()) { @@ -540,9 +610,9 @@ public Future deleteDevice( } final var expanded = statement.expand(map -> { - map.put("tenant_id", key.getTenantId()); - map.put("device_id", key.getDeviceId()); - resourceVersion.ifPresent(version -> map.put("expected_version", version)); + map.put(TENANT_ID, key.getTenantId()); + map.put(DEVICE_ID, key.getDeviceId()); + resourceVersion.ifPresent(version -> map.put(EXPECTED_VERSION, version)); }); log.debug("delete - statement: {}", expanded); @@ -562,7 +632,7 @@ public Future deleteDevice( /** * Delete all devices belonging to the provided tenant. * - * @param tenantId The tenant to clean up. + * @param tenantId The tenant to clean up. * @param spanContext The span to contribute to. * @return A future tracking the outcome of the operation. */ @@ -573,7 +643,7 @@ public Future dropTenant(final String tenantId, final SpanContext .start(); final var expanded = this.dropTenantStatement.expand(params -> { - params.put("tenant_id", tenantId); + params.put(TENANT_ID, tenantId); }); log.debug("delete - statement: {}", expanded); @@ -588,12 +658,15 @@ public Future dropTenant(final String tenantId, final SpanContext /** * Gets the number of devices that are registered for a tenant. * - * @param tenantId The tenant to count devices for. - * @param spanContext The span to contribute to. + * @param tenantId The tenant to count devices for. + * @param spanContext The span to contribute to. + * @param countStatement The count statement to use. + * @param field the field of filter expression + * @param value the value of the filter expression * @return A future tracking the outcome of the operation. * @throws NullPointerException if tenant is {@code null}. */ - public Future getDeviceCount(final String tenantId, final SpanContext spanContext) { + public Future getDeviceCount(final String tenantId, final SpanContext spanContext, final Statement countStatement, final String field, final String value) { Objects.requireNonNull(tenantId); @@ -601,8 +674,10 @@ public Future getDeviceCount(final String tenantId, final SpanContext s .withTag(TracingHelper.TAG_TENANT_ID, tenantId) .start(); - final var expanded = this.countDevicesOfTenantStatement.expand(params -> { - params.put("tenant_id", tenantId); + final var expanded = countStatement.expand(params -> { + params.put(TENANT_ID, tenantId); + params.put(FIELD, field); + params.put(VALUE, value); }); log.debug("count - statement: {}", expanded); @@ -633,10 +708,10 @@ public Future getDeviceCount(final String tenantId, final SpanContext s * If the resource version was provided, but the provided version was no longer the current version, * then the future will fail with a {@link OptimisticLockingException}. * - * @param key The key of the device to update. - * @param credentials The credentials to set. + * @param key The key of the device to update. + * @param credentials The credentials to set. * @param resourceVersion The optional resource version to update. - * @param spanContext The span to contribute to. + * @param spanContext The span to contribute to. * @return A future, tracking the outcome of the operation. */ public Future> setCredentials( @@ -651,94 +726,94 @@ public Future> setCredentials( .withTag("num_credentials", credentials.size()) .start(); - resourceVersion.ifPresent(version -> span.setTag("version", version)); + resourceVersion.ifPresent(version -> span.setTag(VERSION, version)); final String nextVersion = UUID.randomUUID().toString(); return SQL.runTransactionally(this.client, this.tracer, span.context(), (connection, context) -> - readDeviceForUpdate(connection, key, context) - - // check if we got back a result, if not this will abort early - .compose(result -> extractVersionForUpdate(result, resourceVersion)) - - // take the version and start processing on - .compose(version -> Future.succeededFuture() - - .compose(x -> { - final Promise result = Promise.promise(); - final var updatedCredentialsDto = CredentialsDto.forUpdate( - key.getTenantId(), - key.getDeviceId(), - credentials, - nextVersion); - - if (updatedCredentialsDto.requiresMerging()) { - getCredentialsDto(key, connection, span) - .map(updatedCredentialsDto::merge) - .onComplete(result); - } else { - // simply replace the existing credentials with the - // updated ones provided by the client - result.complete(updatedCredentialsDto); - } - return result.future(); - }) - - .compose(updatedCredentials -> this.deleteAllCredentialsStatement - // delete the existing entries - .expand(map -> { - map.put("tenant_id", key.getTenantId()); - map.put("device_id", key.getDeviceId()); - }) - .trace(this.tracer, span.context()) - .update(connection) - .map(updatedCredentials) - ) - - // then create new entries - .compose(updatedCredentials -> { - updatedCredentials.createMissingSecretIds(); - return CompositeFuture.all(updatedCredentials.getData().stream() - .map(JsonObject::mapFrom) - .filter(c -> c.containsKey("type") && c.containsKey("auth-id")) - .map(c -> this.insertCredentialEntryStatement - .expand(map -> { - map.put("tenant_id", key.getTenantId()); - map.put("device_id", key.getDeviceId()); - map.put("type", c.getString("type")); - map.put("auth_id", c.getString("auth-id")); - map.put("data", c.toString()); - }) - .trace(this.tracer, span.context()) - .update(connection)) - .collect(Collectors.toList())) - .mapEmpty(); - }) - - // update the version, this will release the lock - .compose(x -> this.updateDeviceVersionStatement - .expand(map -> { - map.put("tenant_id", key.getTenantId()); - map.put("device_id", key.getDeviceId()); - map.put("expected_version", version); - map.put("next_version", nextVersion); - }) - .trace(this.tracer, span.context()) - .update(connection) + readDeviceForUpdate(connection, key, context) - // check the update outcome - .compose(TableManagementStore::checkUpdateOutcome)) + // check if we got back a result, if not this will abort early + .compose(result -> extractVersionForUpdate(result, resourceVersion)) + + // take the version and start processing on + .compose(version -> Future.succeededFuture() + + .compose(x -> { + final Promise result = Promise.promise(); + final var updatedCredentialsDto = CredentialsDto.forUpdate( + key.getTenantId(), + key.getDeviceId(), + credentials, + nextVersion); + + if (updatedCredentialsDto.requiresMerging()) { + getCredentialsDto(key, connection, span) + .map(updatedCredentialsDto::merge) + .onComplete(result); + } else { + // simply replace the existing credentials with the + // updated ones provided by the client + result.complete(updatedCredentialsDto); + } + return result.future(); + }) + + .compose(updatedCredentials -> this.deleteAllCredentialsStatement + // delete the existing entries + .expand(map -> { + map.put(TENANT_ID, key.getTenantId()); + map.put(DEVICE_ID, key.getDeviceId()); + }) + .trace(this.tracer, span.context()) + .update(connection) + .map(updatedCredentials) + ) + + // then create new entries + .compose(updatedCredentials -> { + updatedCredentials.createMissingSecretIds(); + return CompositeFuture.all(updatedCredentials.getData().stream() + .map(JsonObject::mapFrom) + .filter(c -> c.containsKey("type") && c.containsKey("auth-id")) + .map(c -> this.insertCredentialEntryStatement + .expand(map -> { + map.put(TENANT_ID, key.getTenantId()); + map.put(DEVICE_ID, key.getDeviceId()); + map.put("type", c.getString("type")); + map.put("auth_id", c.getString("auth-id")); + map.put(DATA, c.toString()); + }) + .trace(this.tracer, span.context()) + .update(connection)) + .collect(Collectors.toList())) + .mapEmpty(); + }) + + // update the version, this will release the lock + .compose(x -> this.updateDeviceVersionStatement + .expand(map -> { + map.put(TENANT_ID, key.getTenantId()); + map.put(DEVICE_ID, key.getDeviceId()); + map.put(EXPECTED_VERSION, version); + map.put(NEXT_VERSION, nextVersion); + }) + .trace(this.tracer, span.context()) + .update(connection) - .map(true) + // check the update outcome + .compose(TableManagementStore::checkUpdateOutcome)) - )) + .map(true) - // when not found, then return "false" - .recover(err -> recoverNotFound(span, err, () -> false)) + )) - .map(ok -> new Versioned<>(nextVersion, ok)) - .onComplete(x -> span.finish()); + // when not found, then return "false" + .recover(err -> recoverNotFound(span, err, () -> false)) + + .map(ok -> new Versioned<>(nextVersion, ok)) + .onComplete(x -> span.finish()); } @@ -750,8 +825,8 @@ private Future getCredentialsDto( return readCredentialsStatement // get the current credentials set .expand(map -> { - map.put("tenant_id", key.getTenantId()); - map.put("device_id", key.getDeviceId()); + map.put(TENANT_ID, key.getTenantId()); + map.put(DEVICE_ID, key.getDeviceId()); }) .trace(this.tracer, span.context()) .query(connection) @@ -776,46 +851,6 @@ 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. *

@@ -823,7 +858,7 @@ private static Future extractVersionForUpdate(final ResultSet device, fi * result must be empty. If no credentials could be found for an existing device, * the result must not be empty, but provide an empty {@link CredentialsReadResult}. * - * @param key The key of the device. + * @param key The key of the device. * @param spanContext The span to contribute to. * @return A future, tracking the outcome of the operation. */ @@ -835,8 +870,8 @@ public Future> getCredentials(final DeviceKey ke .start(); final var expanded = this.readCredentialsStatement.expand(map -> { - map.put("tenant_id", key.getTenantId()); - map.put("device_id", key.getDeviceId()); + map.put(TENANT_ID, key.getTenantId()); + map.put(DEVICE_ID, key.getDeviceId()); }); final Promise promise = Promise.promise(); @@ -878,7 +913,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()); @@ -887,36 +922,52 @@ private List parseCredentials(final ResultSet result) { /** * Gets a list of devices of a specific tenant. * - * @param tenantId the tenantId to search devices - * @param pageSize the page size - * @param pageOffset the page offset + * @param tenantId The tenantId to search devices. + * @param pageSize The page size. + * @param pageOffset The page offset. + * @param filters The list of filters. * @param spanContext The span to contribute to. - * @return A future containing devices + * @return A future containing devices. */ - public Future> findDevices(final String tenantId, final int pageSize, final int pageOffset, - final SpanContext spanContext) { + public Future> findDevices(final String tenantId, final int pageSize, final int pageOffset, final List filters, + final SpanContext spanContext) { + + + final var filter = filters.stream().findFirst(); + final String field = filter.map(filter1 -> filter1.getField().toString().replace("/", "")).orElse(""); + final var value = filter.map(filter1 -> + filter1.getValue().toString() + .replace("/", "") + .replace("*", "%") + .replace("?", "_") + ).orElse(""); - final var expanded = this.findDevicesStatement.expand(map -> { - map.put("tenant_id", tenantId); - map.put("page_size", pageSize); - map.put("page_offset", pageOffset); + 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); + map.put(PAGE_SIZE, pageSize); + map.put(PAGE_OFFSET, pageOffset); + map.put(FIELD, field); + map.put(VALUE, value); }); final Span span = TracingHelper.buildChildSpan(this.tracer, spanContext, "find devices", getClass().getSimpleName()) - .withTag(TracingHelper.TAG_TENANT_ID, tenantId) - .start(); + .withTag(TracingHelper.TAG_TENANT_ID, tenantId) + .start(); - final Future deviceCountFuture = getDeviceCount(tenantId, span.context()); + final Future deviceCountFuture = getDeviceCount(tenantId, span.context(), countStatement, field, value); return deviceCountFuture .compose(count -> expanded.trace(this.tracer, span.context()).query(this.client)) .map(r -> { if (r.getNumRows() == 0) { - throw new ClientErrorException( - tenantId, - HttpURLConnection.HTTP_NOT_FOUND, - "no devices matching searching criteria"); + throw new ClientErrorException( + tenantId, + HttpURLConnection.HTTP_NOT_FOUND, + "no devices matching searching criteria"); } else { final var entries = r.getRows(true); span.log(Map.of( @@ -924,7 +975,7 @@ public Future> findDevices(final String tenantId, fin "rows", entries.size())); final List list = new ArrayList<>(); for (var entry : entries) { - final var id = entry.getString("device_id"); + final var id = entry.getString(DEVICE_ID); final JdbcBasedDeviceDto deviceDto = JdbcBasedDeviceDto.forRead(tenantId, id, entry); list.add(DeviceWithId.from(id, deviceDto.getDeviceWithStatus())); } 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 new file mode 100644 index 0000000000..d99cf57a1a --- /dev/null +++ b/services/base-jdbc/src/main/resources/org/eclipse/hono/service/base/jdbc/store/device/base.h2.sql.yaml @@ -0,0 +1,23 @@ + +countDevicesOfTenantWithFilter: | + 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, '"')) + + +findDevicesOfTenantWithFilter: | + SELECT * + FROM %s + WHERE + tenant_id=:tenant_id + AND + LOCATE(CONCAT_WS(':', :field, REPLACE(:value, '"')), REPLACE(data, '"')) + OR + REPLACE(data, '"') LIKE CONCAT('%%', :field, ':', REPLACE(:value, '"')) + ORDER BY device_id ASC + LIMIT :page_size + OFFSET :page_offset \ No newline at end of file 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 b77fd7ab04..062484cd00 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 @@ -53,3 +53,23 @@ resolveGroups: | tenant_id=:tenant_id AND group_id in (select unnest((string_to_array(:group_ids,','))::varchar[])) + +countDevicesOfTenantWithFilter: | + 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) + +findDevicesOfTenantWithFilter: | + SELECT * + FROM %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) + ORDER BY device_id ASC + LIMIT :page_size + OFFSET :page_offset \ No newline at end of file diff --git a/services/base-jdbc/src/main/resources/org/eclipse/hono/service/base/jdbc/store/device/base.sql.yaml b/services/base-jdbc/src/main/resources/org/eclipse/hono/service/base/jdbc/store/device/base.sql.yaml index 96a53ddf3f..d2ec8faab8 100644 --- a/services/base-jdbc/src/main/resources/org/eclipse/hono/service/base/jdbc/store/device/base.sql.yaml +++ b/services/base-jdbc/src/main/resources/org/eclipse/hono/service/base/jdbc/store/device/base.sql.yaml @@ -174,9 +174,9 @@ countDevicesOfTenant: | findDevicesOfTenant: | SELECT * - FROM %s - WHERE + FROM %s + WHERE tenant_id=:tenant_id - ORDER BY device_id + ORDER BY device_id ASC LIMIT :page_size OFFSET :page_offset 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 6f8015f6d9..9f067d4e2b 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 @@ -50,8 +50,8 @@ public class DeviceManagementServiceImpl extends AbstractDeviceManagementService /** * Create a new instance. * - * @param vertx The vert.x instance to use. - * @param store The backing store to use. + * @param vertx The vert.x instance to use. + * @param store The backing store to use. * @param properties The service properties. */ public DeviceManagementServiceImpl(final Vertx vertx, final TableManagementStore store, final DeviceServiceOptions properties) { @@ -79,7 +79,7 @@ protected Future> processCreateDevice(final DeviceKey key, f Optional.of(r.getVersion())) ) - .recover(e -> Services.recover(e)); + .recover(Services::recover); } @@ -112,7 +112,7 @@ protected Future> processUpdateDevice(final DeviceKey key, f Optional.empty(), Optional.of(r.getVersion()))) - .recover(e -> Services.recover(e)); + .recover(Services::recover); } @@ -131,7 +131,7 @@ protected Future> processDeleteDevice(final DeviceKey key, final Op return Result.from(HttpURLConnection.HTTP_NO_CONTENT); } }) - .recover(e -> Services.recover(e)); + .recover(Services::recover); } @@ -141,7 +141,7 @@ protected Future> processDeleteDevicesOfTenant(final String tenantI return this.store .dropTenant(tenantId, span.context()) .map(r -> Result.from(HttpURLConnection.HTTP_NO_CONTENT)) - .recover(e -> Services.recover(e)); + .recover(Services::recover); } @Override @@ -151,22 +151,22 @@ protected String generateDeviceId(final String tenantId) { @Override protected Future>> processSearchDevices( - final String tenantId, - final int pageSize, - final int pageOffset, - final List filters, - final List sortOptions, - final Span span) { + final String tenantId, + final int pageSize, + final int pageOffset, + final List filters, + final List sortOptions, + final Span span) { Objects.requireNonNull(tenantId); Objects.requireNonNull(span); - return store.findDevices(tenantId, pageSize, pageOffset, span.context()) - .map(result -> OperationResult.ok( - HttpURLConnection.HTTP_OK, - result, - Optional.empty(), - Optional.empty())) - .recover(e -> Services.recover(e)); + return store.findDevices(tenantId, pageSize, pageOffset, filters, span.context()) + .map(result -> OperationResult.ok( + HttpURLConnection.HTTP_OK, + result, + Optional.empty(), + Optional.empty())) + .recover(Services::recover); } } diff --git a/services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/AbstractJdbcRegistryTest.java b/services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/AbstractJdbcBaseRegistryTest.java similarity index 96% rename from services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/AbstractJdbcRegistryTest.java rename to services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/AbstractJdbcBaseRegistryTest.java index 53d6a500ed..052782755f 100644 --- a/services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/AbstractJdbcRegistryTest.java +++ b/services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/AbstractJdbcBaseRegistryTest.java @@ -69,24 +69,24 @@ import io.vertx.junit5.VertxExtension; @ExtendWith(VertxExtension.class) -abstract class AbstractJdbcRegistryTest { +abstract class AbstractJdbcBaseRegistryTest { enum DatabaseType { H2, POSTGRESQL } protected static final Span SPAN = NoopSpan.INSTANCE; - private static final DatabaseType DEFAULT_DATABASE_TYPE = DatabaseType.H2; - private static final DatabaseType DATABASE_TYPE = DatabaseType.valueOf(System.getProperty(AbstractJdbcRegistryTest.class.getSimpleName() + protected static DatabaseType DEFAULT_DATABASE_TYPE = DatabaseType.H2; + protected static DatabaseType DATABASE_TYPE = DatabaseType.valueOf(System.getProperty(AbstractJdbcBaseRegistryTest.class.getSimpleName() + ".databaseType", DEFAULT_DATABASE_TYPE.name()).toUpperCase()); private static final Map> DATABASE_CONTAINER_CACHE = new ConcurrentHashMap<>(); - private static final String POSTGRESQL_IMAGE_NAME = System.getProperty(AbstractJdbcRegistryTest.class.getSimpleName() + private static final String POSTGRESQL_IMAGE_NAME = System.getProperty(AbstractJdbcBaseRegistryTest.class.getSimpleName() + ".postgresqlImageName", "postgres:12-alpine"); private static final AtomicLong UNIQUE_ID_GENERATOR = new AtomicLong(System.currentTimeMillis()); private static final Tracer TRACER = NoopTracerFactory.create(); - private static final Path EXAMPLE_SQL_BASE = Path.of("..", "base-jdbc", "src", "main", "resources", "sql", DATABASE_TYPE.name().toLowerCase()); + protected static Path EXAMPLE_SQL_BASE = Path.of("..", "base-jdbc", "src", "main", "resources", "sql", DATABASE_TYPE.name().toLowerCase()); private static final Path BASE_DIR = Path.of("target").toAbsolutePath(); diff --git a/services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/AbstractJdbcPostgresBaseRegistryTest.java b/services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/AbstractJdbcPostgresBaseRegistryTest.java new file mode 100644 index 0000000000..450cc9fdd3 --- /dev/null +++ b/services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/AbstractJdbcPostgresBaseRegistryTest.java @@ -0,0 +1,37 @@ +/******************************************************************************* + * Copyright (c) 2020, 2022 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + + +package org.eclipse.hono.deviceregistry.jdbc.impl; + +import java.nio.file.Path; + +import org.junit.jupiter.api.extension.ExtendWith; + +import io.vertx.junit5.VertxExtension; + + + + + +@ExtendWith(VertxExtension.class) +abstract class AbstractJdbcPostgresBaseRegistryTest extends AbstractJdbcBaseRegistryTest { + + protected AbstractJdbcPostgresBaseRegistryTest() { + DEFAULT_DATABASE_TYPE = DatabaseType.POSTGRESQL; + DATABASE_TYPE = DatabaseType.valueOf(System.getProperty(AbstractJdbcPostgresBaseRegistryTest.class.getSimpleName() + + ".databaseType", DEFAULT_DATABASE_TYPE.name()).toUpperCase()); + EXAMPLE_SQL_BASE = Path.of("..", "base-jdbc", "src", "main", "resources", "sql", DATABASE_TYPE.name().toLowerCase()); + } + +} 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 98% 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 index ec3681e465..3d7948c797 100644 --- 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 @@ -35,7 +35,7 @@ import io.vertx.junit5.VertxTestContext; @ExtendWith(VertxExtension.class) -class RegistryServiceTest extends AbstractJdbcRegistryTest { +class BaseRegistryServiceTest extends AbstractJdbcBaseRegistryTest { private static final String DEFAULT_TENANT = "default"; diff --git a/services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/JdbcBasedCredentialsServiceTest.java b/services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/JdbcBasedCredentialsServiceTest.java index ed3c63bdcb..a9d57986bb 100644 --- a/services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/JdbcBasedCredentialsServiceTest.java +++ b/services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/JdbcBasedCredentialsServiceTest.java @@ -34,7 +34,7 @@ import io.vertx.core.Future; import io.vertx.junit5.VertxTestContext; -class JdbcBasedCredentialsServiceTest extends AbstractJdbcRegistryTest implements CredentialsServiceTestBase { +class JdbcBasedCredentialsServiceTest extends AbstractJdbcBaseRegistryTest implements CredentialsServiceTestBase { /** * Verifies that a request to update credentials of a device fails with a 403 status code 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 94cb313936..b44e665ac1 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 @@ -16,12 +16,14 @@ import static com.google.common.truth.Truth.assertThat; import java.net.HttpURLConnection; +import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Optional; import org.eclipse.hono.deviceregistry.util.Assertions; import org.eclipse.hono.deviceregistry.util.DeviceRegistryUtils; +import org.eclipse.hono.service.management.Filter; import org.eclipse.hono.service.management.SearchResult; import org.eclipse.hono.service.management.device.Device; import org.eclipse.hono.service.management.device.DeviceWithId; @@ -31,13 +33,13 @@ import io.vertx.core.Future; import io.vertx.junit5.VertxTestContext; -class JdbcBasedDeviceManagementSearchDevicesTest extends AbstractJdbcRegistryTest { +class JdbcBasedDeviceManagementSearchDevicesTest extends AbstractJdbcBaseRegistryTest { /** * 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. */ private Future createDevices(final String tenantId, final Map devices) { @@ -58,8 +60,8 @@ private Future createDevices(final String tenantId, final Map createDevices(final String tenantId, final Map { - ctx.verify(() -> Assertions.assertServiceInvocationException(t, HttpURLConnection.HTTP_NOT_FOUND)); - ctx.completeNow(); - })); + "tenant-id", + 10, + 0, + List.of(), + List.of(), + NoopSpan.INSTANCE) + .onComplete(ctx.failing(t -> { + ctx.verify(() -> Assertions.assertServiceInvocationException(t, HttpURLConnection.HTTP_NOT_FOUND)); + ctx.completeNow(); + })); } /** - * Verifies that a request to search devices with valid pageSize succeeds and the result is in accordance - * with the specified page size. + * Verifies that a request to search devices with valid pageSize succeeds and the result is in accordance with the + * specified page size. * * @param ctx The vert.x test context. */ @Test void testSearchDevicesWithPageSize(final VertxTestContext ctx) { final String tenantId = DeviceRegistryUtils.getUniqueIdentifier(); - final int pageSize = 2; + final int pageSize = 3; final int pageOffset = 0; createDevices(tenantId, Map.of( "testDevice1", new Device().setEnabled(true), "testDevice2", new Device().setEnabled(true), "testDevice3", new Device().setEnabled(true))) - .compose(ok -> getDeviceManagementService() - .searchDevices(tenantId, pageSize, pageOffset, List.of(), List.of(), NoopSpan.INSTANCE)) - .onComplete(ctx.succeeding(s -> { - ctx.verify(() -> { - assertThat(s.getStatus()).isEqualTo(HttpURLConnection.HTTP_OK); - assertThat(s.getPayload().getTotal()).isEqualTo(3); - assertThat(s.getPayload().getResult()).hasSize(pageSize); - assertThat(s.getPayload().getResult().get(0).getId()).isEqualTo("testDevice1"); - assertThat(s.getPayload().getResult().get(1).getId()).isEqualTo("testDevice2"); - }); - ctx.completeNow(); - })); + .compose(ok -> getDeviceManagementService() + .searchDevices(tenantId, pageSize, pageOffset, List.of(), List.of(), NoopSpan.INSTANCE)) + .onComplete(ctx.succeeding(s -> { + ctx.verify(() -> { + assertThat(s.getStatus()).isEqualTo(HttpURLConnection.HTTP_OK); + assertThat(s.getPayload().getTotal()).isEqualTo(3); + assertThat(s.getPayload().getResult()).hasSize(pageSize); + assertThat(s.getPayload().getResult().get(0).getId()).isEqualTo("testDevice1"); + assertThat(s.getPayload().getResult().get(1).getId()).isEqualTo("testDevice2"); + }); + ctx.completeNow(); + })); } /** @@ -119,24 +121,219 @@ void testSearchDevicesWithPageSize(final VertxTestContext ctx) { void testSearchDevicesWithPageOffset(final VertxTestContext ctx) { final String tenantId = DeviceRegistryUtils.getUniqueIdentifier(); final int pageSize = 1; - final int pageOffset = 2; + final int pageOffset = 0; createDevices(tenantId, Map.of( "testDevice1", new Device().setEnabled(true), "testDevice2", new Device().setEnabled(true), "testDevice3", new Device().setEnabled(true))) - .compose(ok -> getDeviceManagementService() - .searchDevices(tenantId, pageSize, pageOffset, List.of(), List.of(), NoopSpan.INSTANCE)) - .onComplete(ctx.succeeding(s -> { - ctx.verify(() -> { - assertThat(s.getStatus()).isEqualTo(HttpURLConnection.HTTP_OK); - - final SearchResult searchResult = s.getPayload(); - assertThat(searchResult.getTotal()).isEqualTo(3); - assertThat(searchResult.getResult()).hasSize(pageSize); - assertThat(searchResult.getResult().get(0).getId()).isEqualTo("testDevice3"); - }); - ctx.completeNow(); - })); + .compose(ok -> getDeviceManagementService() + .searchDevices(tenantId, pageSize, pageOffset, List.of(), List.of(), NoopSpan.INSTANCE)) + .onComplete(ctx.succeeding(s -> { + ctx.verify(() -> { + assertThat(s.getStatus()).isEqualTo(HttpURLConnection.HTTP_OK); + + final SearchResult searchResult = s.getPayload(); + assertThat(searchResult.getTotal()).isEqualTo(3); + assertThat(searchResult.getResult()).hasSize(pageSize); + assertThat(searchResult.getResult().size()).isEqualTo(1); + }); + ctx.completeNow(); + })); + } + + @Test + void testSearchAllDevices(final VertxTestContext ctx) { + final String tenantId = DeviceRegistryUtils.getUniqueIdentifier(); + final int pageSize = 3; + final int pageOffset = 0; + + createDevices(tenantId, Map.of( + "testDevice1", new Device().setEnabled(true).setVia(List.of("testDevice2")), + "testDevice2", new Device().setEnabled(true), + "testDevice3", new Device().setEnabled(true))) + .compose(ok -> getDeviceManagementService() + .searchDevices(tenantId, pageSize, pageOffset, List.of(), List.of(), NoopSpan.INSTANCE)) + .onComplete(ctx.succeeding(s -> { + ctx.verify(() -> { + assertThat(s.getStatus()).isEqualTo(HttpURLConnection.HTTP_OK); + assertThat(s.getPayload().getTotal()).isEqualTo(3); + assertThat(s.getPayload().getResult()).hasSize(pageSize); + assertThat(s.getPayload().getResult().get(0).getId()).isEqualTo("testDevice1"); + assertThat(s.getPayload().getResult().get(1).getId()).isEqualTo("testDevice2"); + assertThat(s.getPayload().getResult().get(2).getId()).isEqualTo("testDevice3"); + }); + ctx.completeNow(); + })); + } + + @Test + void TestSearchDevicesWithViaFilter(final VertxTestContext ctx) { + final String tenantId = DeviceRegistryUtils.getUniqueIdentifier(); + final int pageSize = 3; + final int pageOffset = 0; + final List filters = new ArrayList<>(); + final Filter filter1 = new Filter("/via", "[\"testDevice_Gateway\"]"); + filters.add(filter1); + + createDevices(tenantId, Map.of( + "testDevice1", new Device().setEnabled(true).setVia(List.of("testDevice_Gateway")), + "testDevice2", new Device().setEnabled(true).setVia(List.of("testDevice_Gateway")), + "testDevice3", new Device().setEnabled(true).setVia(List.of("testDevice_Gateway2")), + "testDevice_Gateway", new Device(), + "testDevice_Gateway2", new Device().setEnabled(true))) + .compose(ok -> getDeviceManagementService() + .searchDevices(tenantId, pageSize, pageOffset, filters, List.of(), 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("testDevice2"); + }); + ctx.completeNow(); + })); + } + + @Test + void TestSearchDevicesWithBooleanFilter(final VertxTestContext ctx) { + final String tenantId = DeviceRegistryUtils.getUniqueIdentifier(); + final int pageSize = 10; + final int pageOffset = 0; + final List filters = new ArrayList<>(); + final Filter filter1 = new Filter("/enabled", true); + filters.add(filter1); + + createDevices(tenantId, Map.of( + "testDevice1", new Device().setEnabled(true).setVia(List.of("testDevice2")), + "testDevice2", new Device().setEnabled(false).putExtension("count", 12), + "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)) + .onComplete(ctx.succeeding(s -> { + ctx.verify(() -> { + assertThat(s.getStatus()).isEqualTo(HttpURLConnection.HTTP_OK); + assertThat(s.getPayload().getTotal()).isEqualTo(3); + assertThat(s.getPayload().getResult()).hasSize(3); + assertThat(s.getPayload().getResult().get(0).getId()).isEqualTo("testDevice1"); + assertThat(s.getPayload().getResult().get(1).getId()).isEqualTo("testDevice3"); + assertThat(s.getPayload().getResult().get(2).getId()).isEqualTo("testDevice4"); + }); + ctx.completeNow(); + })); + } + + + @Test + void TestSearchDevicesWithIntegerFilter(final VertxTestContext ctx) { + final String tenantId = DeviceRegistryUtils.getUniqueIdentifier(); + final int pageSize = 10; + final int pageOffset = 0; + final List filters = new ArrayList<>(); + final Filter filter1 = new Filter("/count", 12); + filters.add(filter1); + + createDevices(tenantId, Map.of( + "testDevice1", new Device().setEnabled(true).putExtension("count", 12), + "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)) + .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 TestSearchDevicesWithStringFilter(final VertxTestContext ctx) { + final String tenantId = DeviceRegistryUtils.getUniqueIdentifier(); + final int pageSize = 10; + final int pageOffset = 0; + final List filters = new ArrayList<>(); + final Filter filter1 = new Filter("/type", "type_1"); + filters.add(filter1); + + createDevices(tenantId, Map.of( + "testDevice1", new Device().setEnabled(true).putExtension("type", "type_1"), + "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)) + .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 TestSearchDevicesWithStringAllCharsWildcardsFilter(final VertxTestContext ctx) { + final String tenantId = DeviceRegistryUtils.getUniqueIdentifier(); + final int pageSize = 10; + final int pageOffset = 0; + final List filters = new ArrayList<>(); + final Filter filter1 = new Filter("/type", "type%"); + filters.add(filter1); + + createDevices(tenantId, Map.of( + "testDevice1", new Device().setEnabled(true).putExtension("type", "type1"), + "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)) + .onComplete(ctx.succeeding(s -> { + ctx.verify(() -> { + assertThat(s.getStatus()).isEqualTo(HttpURLConnection.HTTP_OK); + assertThat(s.getPayload().getTotal()).isEqualTo(3); + assertThat(s.getPayload().getResult()).hasSize(3); + assertThat(s.getPayload().getResult().get(0).getId()).isEqualTo("testDevice1"); + assertThat(s.getPayload().getResult().get(1).getId()).isEqualTo("testDevice2"); + assertThat(s.getPayload().getResult().get(2).getId()).isEqualTo("testDevice3"); + }); + ctx.completeNow(); + })); + } + + @Test + void TestSearchDevicesWithStringOneCharWildcardsFilter(final VertxTestContext ctx) { + final String tenantId = DeviceRegistryUtils.getUniqueIdentifier(); + final int pageSize = 10; + final int pageOffset = 0; + final List filters = new ArrayList<>(); + final Filter filter1 = new Filter("/type", "type_1%"); + filters.add(filter1); + + createDevices(tenantId, Map.of( + "testDevice1", new Device().setEnabled(true).putExtension("type", "type11"), + "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)) + .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(); + })); } } diff --git a/services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/JdbcBasedRegistrationServiceTest.java b/services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/JdbcBasedRegistrationServiceTest.java index e7facbdaa2..1486037a8d 100644 --- a/services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/JdbcBasedRegistrationServiceTest.java +++ b/services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/JdbcBasedRegistrationServiceTest.java @@ -31,7 +31,7 @@ import io.vertx.core.Future; import io.vertx.junit5.VertxTestContext; -class JdbcBasedRegistrationServiceTest extends AbstractJdbcRegistryTest implements AbstractRegistrationServiceTest { +class JdbcBasedRegistrationServiceTest extends AbstractJdbcBaseRegistryTest implements AbstractRegistrationServiceTest { /** * Verifies that a request to create more devices than the globally configured limit fails with a 403. diff --git a/services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/JdbcBasedTenantManagementSearchTenantsTest.java b/services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/JdbcBasedTenantManagementSearchTenantsTest.java index 43252c3490..903cc56084 100644 --- a/services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/JdbcBasedTenantManagementSearchTenantsTest.java +++ b/services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/JdbcBasedTenantManagementSearchTenantsTest.java @@ -34,7 +34,7 @@ import io.vertx.core.Future; import io.vertx.junit5.VertxTestContext; -class JdbcBasedTenantManagementSearchTenantsTest extends AbstractJdbcRegistryTest { +class JdbcBasedTenantManagementSearchTenantsTest extends AbstractJdbcBaseRegistryTest { /** * Creates a set of tenants. diff --git a/services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/JdbcBasedTenantServiceTest.java b/services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/JdbcBasedTenantServiceTest.java index 8525e14994..004f5c388d 100644 --- a/services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/JdbcBasedTenantServiceTest.java +++ b/services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/JdbcBasedTenantServiceTest.java @@ -17,7 +17,7 @@ import io.vertx.junit5.VertxTestContext; -class JdbcBasedTenantServiceTest extends AbstractJdbcRegistryTest implements AbstractTenantServiceTest { +class JdbcBasedTenantServiceTest extends AbstractJdbcBaseRegistryTest implements AbstractTenantServiceTest { @Override public void testAddTenantWithTrustAnchorGroupAndDuplicateTrustAnchorFails( diff --git a/services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/JdbcPostgresDeviceManagementSearchDevicesTest.java b/services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/JdbcPostgresDeviceManagementSearchDevicesTest.java new file mode 100644 index 0000000000..e077b30ef7 --- /dev/null +++ b/services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/JdbcPostgresDeviceManagementSearchDevicesTest.java @@ -0,0 +1,312 @@ +/******************************************************************************* + * Copyright (c) 2020, 2023 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package org.eclipse.hono.deviceregistry.jdbc.impl; +import static com.google.common.truth.Truth.assertThat; + +import java.net.HttpURLConnection; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import org.eclipse.hono.deviceregistry.util.Assertions; +import org.eclipse.hono.deviceregistry.util.DeviceRegistryUtils; +import org.eclipse.hono.service.management.Filter; +import org.eclipse.hono.service.management.SearchResult; +import org.eclipse.hono.service.management.device.Device; +import org.eclipse.hono.service.management.device.DeviceWithId; +import org.junit.jupiter.api.Test; + +import io.opentracing.noop.NoopSpan; +import io.vertx.core.Future; +import io.vertx.junit5.VertxTestContext; + + + +class JdbcPostgresDeviceManagementSearchDevicesTest extends AbstractJdbcPostgresBaseRegistryTest { + + /** + * Creates a set of devices. + * + * @param tenantId The tenant identifier. + * @param devices The devices to create. + * @return A succeeded future if all devices have been created successfully. + */ + private Future createDevices(final String tenantId, final Map devices) { + Future current = Future.succeededFuture(); + + for (final Map.Entry entry : devices.entrySet()) { + + current = current.compose(ok -> getDeviceManagementService() + .createDevice(tenantId, Optional.of(entry.getKey()), entry.getValue(), NoopSpan.INSTANCE) + .map(r -> { + assertThat(r.getStatus()).isEqualTo(HttpURLConnection.HTTP_CREATED); + return null; + })); + + } + + return current; + } + + /** + * Verifies that a request to search devices fails with a {@value HttpURLConnection#HTTP_NOT_FOUND} when no matching + * devices are found. + * + * @param ctx The vert.x test context. + */ + @Test + void testSearchDevicesWhenNoDevicesAreFound(final VertxTestContext ctx) { + + getDeviceManagementService().searchDevices( + "tenant-id", + 10, + 0, + List.of(), + List.of(), + NoopSpan.INSTANCE) + .onComplete(ctx.failing(t -> { + ctx.verify(() -> Assertions.assertServiceInvocationException(t, HttpURLConnection.HTTP_NOT_FOUND)); + ctx.completeNow(); + })); + } + + /** + * Verifies that a request to search devices with valid pageSize succeeds and the result is in accordance with the + * specified page size. + * + * @param ctx The vert.x test context. + */ + @Test + void testSearchDevicesWithPageSize(final VertxTestContext ctx) { + final String tenantId = DeviceRegistryUtils.getUniqueIdentifier(); + final int pageSize = 3; + final int pageOffset = 0; + + createDevices(tenantId, Map.of( + "testDevice1", new Device().setEnabled(true), + "testDevice2", new Device().setEnabled(true), + "testDevice3", new Device().setEnabled(true))) + .compose(ok -> getDeviceManagementService() + .searchDevices(tenantId, pageSize, pageOffset, List.of(), List.of(), NoopSpan.INSTANCE)) + .onComplete(ctx.succeeding(s -> { + ctx.verify(() -> { + assertThat(s.getStatus()).isEqualTo(HttpURLConnection.HTTP_OK); + assertThat(s.getPayload().getTotal()).isEqualTo(3); + assertThat(s.getPayload().getResult()).hasSize(pageSize); + assertThat(s.getPayload().getResult().get(0).getId()).isEqualTo("testDevice1"); + assertThat(s.getPayload().getResult().get(1).getId()).isEqualTo("testDevice2"); + }); + ctx.completeNow(); + })); + } + + /** + * Verifies that a request to search devices with valid page offset succeeds and the result is in accordance with + * the specified page offset. + * + * @param ctx The vert.x test context. + */ + @Test + void testSearchDevicesWithPageOffset(final VertxTestContext ctx) { + final String tenantId = DeviceRegistryUtils.getUniqueIdentifier(); + final int pageSize = 1; + final int pageOffset = 0; + + createDevices(tenantId, Map.of( + "testDevice1", new Device().setEnabled(true), + "testDevice2", new Device().setEnabled(true), + "testDevice3", new Device().setEnabled(true))) + .compose(ok -> getDeviceManagementService() + .searchDevices(tenantId, pageSize, pageOffset, List.of(), List.of(), NoopSpan.INSTANCE)) + .onComplete(ctx.succeeding(s -> { + ctx.verify(() -> { + assertThat(s.getStatus()).isEqualTo(HttpURLConnection.HTTP_OK); + + final SearchResult searchResult = s.getPayload(); + assertThat(searchResult.getTotal()).isEqualTo(3); + assertThat(searchResult.getResult()).hasSize(pageSize); + assertThat(searchResult.getResult().size()).isEqualTo(1); + }); + ctx.completeNow(); + })); + } + + @Test + void testSearchAllDevices(final VertxTestContext ctx) { + final String tenantId = DeviceRegistryUtils.getUniqueIdentifier(); + final int pageSize = 3; + final int pageOffset = 0; + + createDevices(tenantId, Map.of( + "testDevice1", new Device().setEnabled(true).setVia(List.of("testDevice2")), + "testDevice2", new Device().setEnabled(true), + "testDevice3", new Device().setEnabled(true))) + .compose(ok -> getDeviceManagementService() + .searchDevices(tenantId, pageSize, pageOffset, List.of(), List.of(), NoopSpan.INSTANCE)) + .onComplete(ctx.succeeding(s -> { + ctx.verify(() -> { + assertThat(s.getStatus()).isEqualTo(HttpURLConnection.HTTP_OK); + assertThat(s.getPayload().getTotal()).isEqualTo(3); + assertThat(s.getPayload().getResult()).hasSize(pageSize); + assertThat(s.getPayload().getResult().get(0).getId()).isEqualTo("testDevice1"); + assertThat(s.getPayload().getResult().get(1).getId()).isEqualTo("testDevice2"); + assertThat(s.getPayload().getResult().get(2).getId()).isEqualTo("testDevice3"); + }); + ctx.completeNow(); + })); + } + + @Test + void TestSearchDevicesWithBooleanFilter(final VertxTestContext ctx) { + final String tenantId = DeviceRegistryUtils.getUniqueIdentifier(); + final int pageSize = 10; + final int pageOffset = 0; + final List filters = new ArrayList<>(); + final Filter filter1 = new Filter("/enabled", true); + filters.add(filter1); + + createDevices(tenantId, Map.of( + "testDevice1", new Device().setEnabled(true).setVia(List.of("testDevice2")), + "testDevice2", new Device().setEnabled(false), + "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)) + .onComplete(ctx.succeeding(s -> { + ctx.verify(() -> { + assertThat(s.getStatus()).isEqualTo(HttpURLConnection.HTTP_OK); + assertThat(s.getPayload().getTotal()).isEqualTo(3); + assertThat(s.getPayload().getResult()).hasSize(3); + assertThat(s.getPayload().getResult().get(0).getId()).isEqualTo("testDevice1"); + assertThat(s.getPayload().getResult().get(1).getId()).isEqualTo("testDevice3"); + assertThat(s.getPayload().getResult().get(2).getId()).isEqualTo("testDevice4"); + }); + ctx.completeNow(); + })); + } + + + @Test + void TestSearchDevicesWithIntegerFilter(final VertxTestContext ctx) { + final String tenantId = DeviceRegistryUtils.getUniqueIdentifier(); + final int pageSize = 10; + final int pageOffset = 0; + final List filters = new ArrayList<>(); + final Filter filter1 = new Filter("/count", 12); + filters.add(filter1); + + createDevices(tenantId, Map.of( + "testDevice1", new Device().putExtension("count", 12), + "testDevice2", new Device().putExtension("count", 8), + "testDevice3", new Device().putExtension("count", 12))) + .compose(ok -> getDeviceManagementService() + .searchDevices(tenantId, pageSize, pageOffset, filters, List.of(), 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 TestSearchDevicesWithStringFilter(final VertxTestContext ctx) { + final String tenantId = DeviceRegistryUtils.getUniqueIdentifier(); + final int pageSize = 10; + final int pageOffset = 0; + final List filters = new ArrayList<>(); + final Filter filter1 = new Filter("/type", "type_1"); + filters.add(filter1); + + createDevices(tenantId, Map.of( + "testDevice1", new Device().setEnabled(true).putExtension("type", "type_1"), + "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)) + .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 TestSearchDevicesWithStringAllCharsWildcardsFilter(final VertxTestContext ctx) { + final String tenantId = DeviceRegistryUtils.getUniqueIdentifier(); + final int pageSize = 10; + final int pageOffset = 0; + final List filters = new ArrayList<>(); + final Filter filter1 = new Filter("/type", "type1%"); + filters.add(filter1); + + createDevices(tenantId, Map.of( + "testDevice1", new Device().setEnabled(true).putExtension("type", "type111111"), + "testDevice2", new Device().setEnabled(false).putExtension("type", "type12222"), + "testDevice3", new Device().setEnabled(false).putExtension("type", "type133333"), + "testDevice4", new Device().setEnabled(true).putExtension("type", "typeN"), + "testDevice5", new Device().setEnabled(true).putExtension("type", "typeNone"))) + .compose(ok -> getDeviceManagementService() + .searchDevices(tenantId, pageSize, pageOffset, filters, List.of(), NoopSpan.INSTANCE)) + .onComplete(ctx.succeeding(s -> { + ctx.verify(() -> { + assertThat(s.getStatus()).isEqualTo(HttpURLConnection.HTTP_OK); + assertThat(s.getPayload().getTotal()).isEqualTo(3); + assertThat(s.getPayload().getResult()).hasSize(3); + assertThat(s.getPayload().getResult().get(0).getId()).isEqualTo("testDevice1"); + assertThat(s.getPayload().getResult().get(1).getId()).isEqualTo("testDevice2"); + assertThat(s.getPayload().getResult().get(2).getId()).isEqualTo("testDevice3"); + }); + ctx.completeNow(); + })); + } + + @Test + void TestSearchDevicesWithStringOneCharWildcardsFilter(final VertxTestContext ctx) { + final String tenantId = DeviceRegistryUtils.getUniqueIdentifier(); + final int pageSize = 10; + final int pageOffset = 0; + final List filters = new ArrayList<>(); + final Filter filter1 = new Filter("/type", "type_2%"); + filters.add(filter1); + + createDevices(tenantId, Map.of( + "testDevice1", new Device().setEnabled(true).putExtension("type", "type11"), + "testDevice2", new Device().setEnabled(false).putExtension("type", "type22"), + "testDevice3", new Device().setEnabled(true).putExtension("type", "type11"))) + .compose(ok -> getDeviceManagementService() + .searchDevices(tenantId, pageSize, pageOffset, filters, List.of(), 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(); + })); + } +} diff --git a/services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/ResolveGroupsTest.java b/services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/ResolveGroupsTest.java index 301a9b8f13..5e7d4aaa2a 100644 --- a/services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/ResolveGroupsTest.java +++ b/services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/ResolveGroupsTest.java @@ -40,7 +40,7 @@ import io.vertx.junit5.VertxTestContext; @ExtendWith(VertxExtension.class) -class ResolveGroupsTest extends AbstractJdbcRegistryTest { +class ResolveGroupsTest extends AbstractJdbcBaseRegistryTest { private Handler>> assertSuccess(final VertxTestContext context, final int statusCode) { return context.succeeding(result -> { diff --git a/services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/TenantServiceTest.java b/services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/TenantServiceTest.java index 166622e260..750b38c7b6 100644 --- a/services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/TenantServiceTest.java +++ b/services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/TenantServiceTest.java @@ -43,7 +43,7 @@ @ExtendWith(VertxExtension.class) @Timeout(value = 5, timeUnit = TimeUnit.SECONDS) -class TenantServiceTest extends AbstractJdbcRegistryTest { +class TenantServiceTest extends AbstractJdbcBaseRegistryTest { private Handler>> assertNotFound(final VertxTestContext context) { diff --git a/site/documentation/content/api/management/device-registry-v1.yaml b/site/documentation/content/api/management/device-registry-v1.yaml index 941bd375e8..7798534141 100644 --- a/site/documentation/content/api/management/device-registry-v1.yaml +++ b/site/documentation/content/api/management/device-registry-v1.yaml @@ -1687,10 +1687,11 @@ 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 + ``` { "type": "object", diff --git a/site/documentation/content/user-guide/device-registry.md b/site/documentation/content/user-guide/device-registry.md index ec0019cab6..290c6a7b9f 100644 --- a/site/documentation/content/user-guide/device-registry.md +++ b/site/documentation/content/user-guide/device-registry.md @@ -60,7 +60,7 @@ The tenants in the registry can be managed using the Device Registry Management 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 *filterJson* and *sortJson* query parameters are + operation defined by the Device Registry Management API, but the *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). @@ -105,7 +105,8 @@ 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, but the *filterJson* and *sortJson* query parameters are (currently) being ignored. + 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. {{% /notice %}} From edf501a814c51da2c6c785a0f7bc7b5815b625d6 Mon Sep 17 00:00:00 2001 From: "g.dimitropoulos" Date: Mon, 24 Jul 2023 10:37:04 +0200 Subject: [PATCH 2/9] - remove AbstractJdbcPostgresBaseRegistryTest class - run same unittests for all JDBC implementations Also-by: Matthias Feurer m.feurer@sotec.eu Signed-off-by: g.dimitropoulos --- .../AbstractJdbcPostgresBaseRegistryTest.java | 37 --- ...asedDeviceManagementSearchDevicesTest.java | 2 +- ...gresDeviceManagementSearchDevicesTest.java | 299 +----------------- 3 files changed, 9 insertions(+), 329 deletions(-) delete mode 100644 services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/AbstractJdbcPostgresBaseRegistryTest.java diff --git a/services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/AbstractJdbcPostgresBaseRegistryTest.java b/services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/AbstractJdbcPostgresBaseRegistryTest.java deleted file mode 100644 index 450cc9fdd3..0000000000 --- a/services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/AbstractJdbcPostgresBaseRegistryTest.java +++ /dev/null @@ -1,37 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2020, 2022 Contributors to the Eclipse Foundation - * - * See the NOTICE file(s) distributed with this work for additional - * information regarding copyright ownership. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ - - -package org.eclipse.hono.deviceregistry.jdbc.impl; - -import java.nio.file.Path; - -import org.junit.jupiter.api.extension.ExtendWith; - -import io.vertx.junit5.VertxExtension; - - - - - -@ExtendWith(VertxExtension.class) -abstract class AbstractJdbcPostgresBaseRegistryTest extends AbstractJdbcBaseRegistryTest { - - protected AbstractJdbcPostgresBaseRegistryTest() { - DEFAULT_DATABASE_TYPE = DatabaseType.POSTGRESQL; - DATABASE_TYPE = DatabaseType.valueOf(System.getProperty(AbstractJdbcPostgresBaseRegistryTest.class.getSimpleName() - + ".databaseType", DEFAULT_DATABASE_TYPE.name()).toUpperCase()); - EXAMPLE_SQL_BASE = Path.of("..", "base-jdbc", "src", "main", "resources", "sql", DATABASE_TYPE.name().toLowerCase()); - } - -} 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 b44e665ac1..6daf81f34f 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 @@ -42,7 +42,7 @@ class JdbcBasedDeviceManagementSearchDevicesTest extends AbstractJdbcBaseRegistr * @param devices The devices to create. * @return A succeeded future if all devices have been created successfully. */ - private Future createDevices(final String tenantId, final Map devices) { + Future createDevices(final String tenantId, final Map devices) { Future current = Future.succeededFuture(); for (final Map.Entry entry : devices.entrySet()) { diff --git a/services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/JdbcPostgresDeviceManagementSearchDevicesTest.java b/services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/JdbcPostgresDeviceManagementSearchDevicesTest.java index e077b30ef7..e4fd33136b 100644 --- a/services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/JdbcPostgresDeviceManagementSearchDevicesTest.java +++ b/services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/JdbcPostgresDeviceManagementSearchDevicesTest.java @@ -12,301 +12,18 @@ *******************************************************************************/ package org.eclipse.hono.deviceregistry.jdbc.impl; -import static com.google.common.truth.Truth.assertThat; -import java.net.HttpURLConnection; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Optional; +import java.nio.file.Path; -import org.eclipse.hono.deviceregistry.util.Assertions; -import org.eclipse.hono.deviceregistry.util.DeviceRegistryUtils; -import org.eclipse.hono.service.management.Filter; -import org.eclipse.hono.service.management.SearchResult; -import org.eclipse.hono.service.management.device.Device; -import org.eclipse.hono.service.management.device.DeviceWithId; -import org.junit.jupiter.api.Test; -import io.opentracing.noop.NoopSpan; -import io.vertx.core.Future; -import io.vertx.junit5.VertxTestContext; +class JdbcPostgresDeviceManagementSearchDevicesTest extends JdbcBasedDeviceManagementSearchDevicesTest { - - -class JdbcPostgresDeviceManagementSearchDevicesTest extends AbstractJdbcPostgresBaseRegistryTest { - - /** - * Creates a set of devices. - * - * @param tenantId The tenant identifier. - * @param devices The devices to create. - * @return A succeeded future if all devices have been created successfully. - */ - private Future createDevices(final String tenantId, final Map devices) { - Future current = Future.succeededFuture(); - - for (final Map.Entry entry : devices.entrySet()) { - - current = current.compose(ok -> getDeviceManagementService() - .createDevice(tenantId, Optional.of(entry.getKey()), entry.getValue(), NoopSpan.INSTANCE) - .map(r -> { - assertThat(r.getStatus()).isEqualTo(HttpURLConnection.HTTP_CREATED); - return null; - })); - - } - - return current; - } - - /** - * Verifies that a request to search devices fails with a {@value HttpURLConnection#HTTP_NOT_FOUND} when no matching - * devices are found. - * - * @param ctx The vert.x test context. - */ - @Test - void testSearchDevicesWhenNoDevicesAreFound(final VertxTestContext ctx) { - - getDeviceManagementService().searchDevices( - "tenant-id", - 10, - 0, - List.of(), - List.of(), - NoopSpan.INSTANCE) - .onComplete(ctx.failing(t -> { - ctx.verify(() -> Assertions.assertServiceInvocationException(t, HttpURLConnection.HTTP_NOT_FOUND)); - ctx.completeNow(); - })); + protected JdbcPostgresDeviceManagementSearchDevicesTest() { + super(); + DEFAULT_DATABASE_TYPE = DatabaseType.POSTGRESQL; + DATABASE_TYPE = DatabaseType.valueOf(System.getProperty(AbstractJdbcBaseRegistryTest.class.getSimpleName() + + "_postgresql" + ".databaseType", DEFAULT_DATABASE_TYPE.name()).toUpperCase()); + EXAMPLE_SQL_BASE = Path.of("..", "base-jdbc", "src", "main", "resources", "sql", DATABASE_TYPE.name().toLowerCase()); } - /** - * Verifies that a request to search devices with valid pageSize succeeds and the result is in accordance with the - * specified page size. - * - * @param ctx The vert.x test context. - */ - @Test - void testSearchDevicesWithPageSize(final VertxTestContext ctx) { - final String tenantId = DeviceRegistryUtils.getUniqueIdentifier(); - final int pageSize = 3; - final int pageOffset = 0; - - createDevices(tenantId, Map.of( - "testDevice1", new Device().setEnabled(true), - "testDevice2", new Device().setEnabled(true), - "testDevice3", new Device().setEnabled(true))) - .compose(ok -> getDeviceManagementService() - .searchDevices(tenantId, pageSize, pageOffset, List.of(), List.of(), NoopSpan.INSTANCE)) - .onComplete(ctx.succeeding(s -> { - ctx.verify(() -> { - assertThat(s.getStatus()).isEqualTo(HttpURLConnection.HTTP_OK); - assertThat(s.getPayload().getTotal()).isEqualTo(3); - assertThat(s.getPayload().getResult()).hasSize(pageSize); - assertThat(s.getPayload().getResult().get(0).getId()).isEqualTo("testDevice1"); - assertThat(s.getPayload().getResult().get(1).getId()).isEqualTo("testDevice2"); - }); - ctx.completeNow(); - })); - } - - /** - * Verifies that a request to search devices with valid page offset succeeds and the result is in accordance with - * the specified page offset. - * - * @param ctx The vert.x test context. - */ - @Test - void testSearchDevicesWithPageOffset(final VertxTestContext ctx) { - final String tenantId = DeviceRegistryUtils.getUniqueIdentifier(); - final int pageSize = 1; - final int pageOffset = 0; - - createDevices(tenantId, Map.of( - "testDevice1", new Device().setEnabled(true), - "testDevice2", new Device().setEnabled(true), - "testDevice3", new Device().setEnabled(true))) - .compose(ok -> getDeviceManagementService() - .searchDevices(tenantId, pageSize, pageOffset, List.of(), List.of(), NoopSpan.INSTANCE)) - .onComplete(ctx.succeeding(s -> { - ctx.verify(() -> { - assertThat(s.getStatus()).isEqualTo(HttpURLConnection.HTTP_OK); - - final SearchResult searchResult = s.getPayload(); - assertThat(searchResult.getTotal()).isEqualTo(3); - assertThat(searchResult.getResult()).hasSize(pageSize); - assertThat(searchResult.getResult().size()).isEqualTo(1); - }); - ctx.completeNow(); - })); - } - - @Test - void testSearchAllDevices(final VertxTestContext ctx) { - final String tenantId = DeviceRegistryUtils.getUniqueIdentifier(); - final int pageSize = 3; - final int pageOffset = 0; - - createDevices(tenantId, Map.of( - "testDevice1", new Device().setEnabled(true).setVia(List.of("testDevice2")), - "testDevice2", new Device().setEnabled(true), - "testDevice3", new Device().setEnabled(true))) - .compose(ok -> getDeviceManagementService() - .searchDevices(tenantId, pageSize, pageOffset, List.of(), List.of(), NoopSpan.INSTANCE)) - .onComplete(ctx.succeeding(s -> { - ctx.verify(() -> { - assertThat(s.getStatus()).isEqualTo(HttpURLConnection.HTTP_OK); - assertThat(s.getPayload().getTotal()).isEqualTo(3); - assertThat(s.getPayload().getResult()).hasSize(pageSize); - assertThat(s.getPayload().getResult().get(0).getId()).isEqualTo("testDevice1"); - assertThat(s.getPayload().getResult().get(1).getId()).isEqualTo("testDevice2"); - assertThat(s.getPayload().getResult().get(2).getId()).isEqualTo("testDevice3"); - }); - ctx.completeNow(); - })); - } - - @Test - void TestSearchDevicesWithBooleanFilter(final VertxTestContext ctx) { - final String tenantId = DeviceRegistryUtils.getUniqueIdentifier(); - final int pageSize = 10; - final int pageOffset = 0; - final List filters = new ArrayList<>(); - final Filter filter1 = new Filter("/enabled", true); - filters.add(filter1); - - createDevices(tenantId, Map.of( - "testDevice1", new Device().setEnabled(true).setVia(List.of("testDevice2")), - "testDevice2", new Device().setEnabled(false), - "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)) - .onComplete(ctx.succeeding(s -> { - ctx.verify(() -> { - assertThat(s.getStatus()).isEqualTo(HttpURLConnection.HTTP_OK); - assertThat(s.getPayload().getTotal()).isEqualTo(3); - assertThat(s.getPayload().getResult()).hasSize(3); - assertThat(s.getPayload().getResult().get(0).getId()).isEqualTo("testDevice1"); - assertThat(s.getPayload().getResult().get(1).getId()).isEqualTo("testDevice3"); - assertThat(s.getPayload().getResult().get(2).getId()).isEqualTo("testDevice4"); - }); - ctx.completeNow(); - })); - } - - - @Test - void TestSearchDevicesWithIntegerFilter(final VertxTestContext ctx) { - final String tenantId = DeviceRegistryUtils.getUniqueIdentifier(); - final int pageSize = 10; - final int pageOffset = 0; - final List filters = new ArrayList<>(); - final Filter filter1 = new Filter("/count", 12); - filters.add(filter1); - - createDevices(tenantId, Map.of( - "testDevice1", new Device().putExtension("count", 12), - "testDevice2", new Device().putExtension("count", 8), - "testDevice3", new Device().putExtension("count", 12))) - .compose(ok -> getDeviceManagementService() - .searchDevices(tenantId, pageSize, pageOffset, filters, List.of(), 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 TestSearchDevicesWithStringFilter(final VertxTestContext ctx) { - final String tenantId = DeviceRegistryUtils.getUniqueIdentifier(); - final int pageSize = 10; - final int pageOffset = 0; - final List filters = new ArrayList<>(); - final Filter filter1 = new Filter("/type", "type_1"); - filters.add(filter1); - - createDevices(tenantId, Map.of( - "testDevice1", new Device().setEnabled(true).putExtension("type", "type_1"), - "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)) - .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 TestSearchDevicesWithStringAllCharsWildcardsFilter(final VertxTestContext ctx) { - final String tenantId = DeviceRegistryUtils.getUniqueIdentifier(); - final int pageSize = 10; - final int pageOffset = 0; - final List filters = new ArrayList<>(); - final Filter filter1 = new Filter("/type", "type1%"); - filters.add(filter1); - - createDevices(tenantId, Map.of( - "testDevice1", new Device().setEnabled(true).putExtension("type", "type111111"), - "testDevice2", new Device().setEnabled(false).putExtension("type", "type12222"), - "testDevice3", new Device().setEnabled(false).putExtension("type", "type133333"), - "testDevice4", new Device().setEnabled(true).putExtension("type", "typeN"), - "testDevice5", new Device().setEnabled(true).putExtension("type", "typeNone"))) - .compose(ok -> getDeviceManagementService() - .searchDevices(tenantId, pageSize, pageOffset, filters, List.of(), NoopSpan.INSTANCE)) - .onComplete(ctx.succeeding(s -> { - ctx.verify(() -> { - assertThat(s.getStatus()).isEqualTo(HttpURLConnection.HTTP_OK); - assertThat(s.getPayload().getTotal()).isEqualTo(3); - assertThat(s.getPayload().getResult()).hasSize(3); - assertThat(s.getPayload().getResult().get(0).getId()).isEqualTo("testDevice1"); - assertThat(s.getPayload().getResult().get(1).getId()).isEqualTo("testDevice2"); - assertThat(s.getPayload().getResult().get(2).getId()).isEqualTo("testDevice3"); - }); - ctx.completeNow(); - })); - } - - @Test - void TestSearchDevicesWithStringOneCharWildcardsFilter(final VertxTestContext ctx) { - final String tenantId = DeviceRegistryUtils.getUniqueIdentifier(); - final int pageSize = 10; - final int pageOffset = 0; - final List filters = new ArrayList<>(); - final Filter filter1 = new Filter("/type", "type_2%"); - filters.add(filter1); - - createDevices(tenantId, Map.of( - "testDevice1", new Device().setEnabled(true).putExtension("type", "type11"), - "testDevice2", new Device().setEnabled(false).putExtension("type", "type22"), - "testDevice3", new Device().setEnabled(true).putExtension("type", "type11"))) - .compose(ok -> getDeviceManagementService() - .searchDevices(tenantId, pageSize, pageOffset, filters, List.of(), 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(); - })); - } } From 07ff350f3080e9214d1a6b0c808c53d05ab74dc4 Mon Sep 17 00:00:00 2001 From: "g.dimitropoulos" Date: Tue, 25 Jul 2023 10:56:42 +0200 Subject: [PATCH 3/9] make changes after PR review Also-by: Matthias Feurer m.feurer@sotec.eu Signed-off-by: g.dimitropoulos --- .../base/jdbc/store/device/TableManagementStore.java | 6 +++--- .../hono/service/base/jdbc/store/device/base.h2.sql.yaml | 2 +- .../service/base/jdbc/store/device/base.postgresql.sql.yaml | 2 +- .../hono/service/base/jdbc/store/device/base.sql.yaml | 4 ++-- ...cBaseRegistryTest.java => AbstractJdbcRegistryTest.java} | 6 +++--- .../jdbc/impl/JdbcBasedCredentialsServiceTest.java | 2 +- .../impl/JdbcBasedDeviceManagementSearchDevicesTest.java | 2 +- .../jdbc/impl/JdbcBasedRegistrationServiceTest.java | 2 +- .../impl/JdbcBasedTenantManagementSearchTenantsTest.java | 2 +- .../jdbc/impl/JdbcBasedTenantServiceTest.java | 2 +- .../impl/JdbcPostgresDeviceManagementSearchDevicesTest.java | 2 +- ...aseRegistryServiceTest.java => RegistryServiceTest.java} | 2 +- .../hono/deviceregistry/jdbc/impl/ResolveGroupsTest.java | 2 +- .../hono/deviceregistry/jdbc/impl/TenantServiceTest.java | 2 +- .../content/api/management/device-registry-v1.yaml | 2 +- site/documentation/content/user-guide/device-registry.md | 2 +- 16 files changed, 21 insertions(+), 21 deletions(-) rename services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/{AbstractJdbcBaseRegistryTest.java => AbstractJdbcRegistryTest.java} (98%) rename services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/{BaseRegistryServiceTest.java => RegistryServiceTest.java} (98%) 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 06b5af16f0..47cca040c0 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 @@ -661,8 +661,8 @@ public Future dropTenant(final String tenantId, final SpanContext * @param tenantId The tenant to count devices for. * @param spanContext The span to contribute to. * @param countStatement The count statement to use. - * @param field the field of filter expression - * @param value the value of the filter expression + * @param field The field of filter expression. + * @param value The value of the filter expression. * @return A future tracking the outcome of the operation. * @throws NullPointerException if tenant is {@code null}. */ @@ -925,7 +925,7 @@ private List parseCredentials(final ResultSet result) { * @param tenantId The tenantId to search devices. * @param pageSize The page size. * @param pageOffset The page offset. - * @param filters The list of filters. + * @param filters The list of filters (currently only the first value of the list will be used). * @param spanContext The span to contribute to. * @return A future containing devices. */ 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 d99cf57a1a..bf59f21565 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 @@ -20,4 +20,4 @@ findDevicesOfTenantWithFilter: | REPLACE(data, '"') LIKE CONCAT('%%', :field, ':', REPLACE(:value, '"')) ORDER BY device_id ASC LIMIT :page_size - OFFSET :page_offset \ No newline at end of file + 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 062484cd00..22f7b02532 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 @@ -72,4 +72,4 @@ findDevicesOfTenantWithFilter: | jsonb_extract_path(data,'ext')->>:field LIKE CAST(:value as varchar) ORDER BY device_id ASC LIMIT :page_size - OFFSET :page_offset \ No newline at end of file + OFFSET :page_offset diff --git a/services/base-jdbc/src/main/resources/org/eclipse/hono/service/base/jdbc/store/device/base.sql.yaml b/services/base-jdbc/src/main/resources/org/eclipse/hono/service/base/jdbc/store/device/base.sql.yaml index d2ec8faab8..e65f4323b6 100644 --- a/services/base-jdbc/src/main/resources/org/eclipse/hono/service/base/jdbc/store/device/base.sql.yaml +++ b/services/base-jdbc/src/main/resources/org/eclipse/hono/service/base/jdbc/store/device/base.sql.yaml @@ -174,8 +174,8 @@ countDevicesOfTenant: | findDevicesOfTenant: | SELECT * - FROM %s - WHERE + FROM %s + WHERE tenant_id=:tenant_id ORDER BY device_id ASC LIMIT :page_size diff --git a/services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/AbstractJdbcBaseRegistryTest.java b/services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/AbstractJdbcRegistryTest.java similarity index 98% rename from services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/AbstractJdbcBaseRegistryTest.java rename to services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/AbstractJdbcRegistryTest.java index 052782755f..33330c95ad 100644 --- a/services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/AbstractJdbcBaseRegistryTest.java +++ b/services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/AbstractJdbcRegistryTest.java @@ -69,7 +69,7 @@ import io.vertx.junit5.VertxExtension; @ExtendWith(VertxExtension.class) -abstract class AbstractJdbcBaseRegistryTest { +abstract class AbstractJdbcRegistryTest { enum DatabaseType { H2, POSTGRESQL @@ -77,10 +77,10 @@ enum DatabaseType { protected static final Span SPAN = NoopSpan.INSTANCE; protected static DatabaseType DEFAULT_DATABASE_TYPE = DatabaseType.H2; - protected static DatabaseType DATABASE_TYPE = DatabaseType.valueOf(System.getProperty(AbstractJdbcBaseRegistryTest.class.getSimpleName() + protected static DatabaseType DATABASE_TYPE = DatabaseType.valueOf(System.getProperty(AbstractJdbcRegistryTest.class.getSimpleName() + ".databaseType", DEFAULT_DATABASE_TYPE.name()).toUpperCase()); private static final Map> DATABASE_CONTAINER_CACHE = new ConcurrentHashMap<>(); - private static final String POSTGRESQL_IMAGE_NAME = System.getProperty(AbstractJdbcBaseRegistryTest.class.getSimpleName() + private static final String POSTGRESQL_IMAGE_NAME = System.getProperty(AbstractJdbcRegistryTest.class.getSimpleName() + ".postgresqlImageName", "postgres:12-alpine"); private static final AtomicLong UNIQUE_ID_GENERATOR = new AtomicLong(System.currentTimeMillis()); diff --git a/services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/JdbcBasedCredentialsServiceTest.java b/services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/JdbcBasedCredentialsServiceTest.java index a9d57986bb..ed3c63bdcb 100644 --- a/services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/JdbcBasedCredentialsServiceTest.java +++ b/services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/JdbcBasedCredentialsServiceTest.java @@ -34,7 +34,7 @@ import io.vertx.core.Future; import io.vertx.junit5.VertxTestContext; -class JdbcBasedCredentialsServiceTest extends AbstractJdbcBaseRegistryTest implements CredentialsServiceTestBase { +class JdbcBasedCredentialsServiceTest extends AbstractJdbcRegistryTest implements CredentialsServiceTestBase { /** * Verifies that a request to update credentials of a device fails with a 403 status code 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 6daf81f34f..1bdb12bb5c 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 @@ -33,7 +33,7 @@ import io.vertx.core.Future; import io.vertx.junit5.VertxTestContext; -class JdbcBasedDeviceManagementSearchDevicesTest extends AbstractJdbcBaseRegistryTest { +class JdbcBasedDeviceManagementSearchDevicesTest extends AbstractJdbcRegistryTest { /** * Creates a set of devices. diff --git a/services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/JdbcBasedRegistrationServiceTest.java b/services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/JdbcBasedRegistrationServiceTest.java index 1486037a8d..e7facbdaa2 100644 --- a/services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/JdbcBasedRegistrationServiceTest.java +++ b/services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/JdbcBasedRegistrationServiceTest.java @@ -31,7 +31,7 @@ import io.vertx.core.Future; import io.vertx.junit5.VertxTestContext; -class JdbcBasedRegistrationServiceTest extends AbstractJdbcBaseRegistryTest implements AbstractRegistrationServiceTest { +class JdbcBasedRegistrationServiceTest extends AbstractJdbcRegistryTest implements AbstractRegistrationServiceTest { /** * Verifies that a request to create more devices than the globally configured limit fails with a 403. diff --git a/services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/JdbcBasedTenantManagementSearchTenantsTest.java b/services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/JdbcBasedTenantManagementSearchTenantsTest.java index 903cc56084..43252c3490 100644 --- a/services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/JdbcBasedTenantManagementSearchTenantsTest.java +++ b/services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/JdbcBasedTenantManagementSearchTenantsTest.java @@ -34,7 +34,7 @@ import io.vertx.core.Future; import io.vertx.junit5.VertxTestContext; -class JdbcBasedTenantManagementSearchTenantsTest extends AbstractJdbcBaseRegistryTest { +class JdbcBasedTenantManagementSearchTenantsTest extends AbstractJdbcRegistryTest { /** * Creates a set of tenants. diff --git a/services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/JdbcBasedTenantServiceTest.java b/services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/JdbcBasedTenantServiceTest.java index 004f5c388d..8525e14994 100644 --- a/services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/JdbcBasedTenantServiceTest.java +++ b/services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/JdbcBasedTenantServiceTest.java @@ -17,7 +17,7 @@ import io.vertx.junit5.VertxTestContext; -class JdbcBasedTenantServiceTest extends AbstractJdbcBaseRegistryTest implements AbstractTenantServiceTest { +class JdbcBasedTenantServiceTest extends AbstractJdbcRegistryTest implements AbstractTenantServiceTest { @Override public void testAddTenantWithTrustAnchorGroupAndDuplicateTrustAnchorFails( diff --git a/services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/JdbcPostgresDeviceManagementSearchDevicesTest.java b/services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/JdbcPostgresDeviceManagementSearchDevicesTest.java index e4fd33136b..529cd18243 100644 --- a/services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/JdbcPostgresDeviceManagementSearchDevicesTest.java +++ b/services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/JdbcPostgresDeviceManagementSearchDevicesTest.java @@ -21,7 +21,7 @@ class JdbcPostgresDeviceManagementSearchDevicesTest extends JdbcBasedDeviceManag protected JdbcPostgresDeviceManagementSearchDevicesTest() { super(); DEFAULT_DATABASE_TYPE = DatabaseType.POSTGRESQL; - DATABASE_TYPE = DatabaseType.valueOf(System.getProperty(AbstractJdbcBaseRegistryTest.class.getSimpleName() + DATABASE_TYPE = DatabaseType.valueOf(System.getProperty(AbstractJdbcRegistryTest.class.getSimpleName() + "_postgresql" + ".databaseType", DEFAULT_DATABASE_TYPE.name()).toUpperCase()); EXAMPLE_SQL_BASE = Path.of("..", "base-jdbc", "src", "main", "resources", "sql", DATABASE_TYPE.name().toLowerCase()); } diff --git a/services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/BaseRegistryServiceTest.java b/services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/RegistryServiceTest.java similarity index 98% rename from services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/BaseRegistryServiceTest.java rename to services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/RegistryServiceTest.java index 3d7948c797..ec3681e465 100644 --- a/services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/BaseRegistryServiceTest.java +++ b/services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/RegistryServiceTest.java @@ -35,7 +35,7 @@ import io.vertx.junit5.VertxTestContext; @ExtendWith(VertxExtension.class) -class BaseRegistryServiceTest extends AbstractJdbcBaseRegistryTest { +class RegistryServiceTest extends AbstractJdbcRegistryTest { private static final String DEFAULT_TENANT = "default"; diff --git a/services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/ResolveGroupsTest.java b/services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/ResolveGroupsTest.java index 5e7d4aaa2a..301a9b8f13 100644 --- a/services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/ResolveGroupsTest.java +++ b/services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/ResolveGroupsTest.java @@ -40,7 +40,7 @@ import io.vertx.junit5.VertxTestContext; @ExtendWith(VertxExtension.class) -class ResolveGroupsTest extends AbstractJdbcBaseRegistryTest { +class ResolveGroupsTest extends AbstractJdbcRegistryTest { private Handler>> assertSuccess(final VertxTestContext context, final int statusCode) { return context.succeeding(result -> { diff --git a/services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/TenantServiceTest.java b/services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/TenantServiceTest.java index 750b38c7b6..166622e260 100644 --- a/services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/TenantServiceTest.java +++ b/services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/TenantServiceTest.java @@ -43,7 +43,7 @@ @ExtendWith(VertxExtension.class) @Timeout(value = 5, timeUnit = TimeUnit.SECONDS) -class TenantServiceTest extends AbstractJdbcBaseRegistryTest { +class TenantServiceTest extends AbstractJdbcRegistryTest { private Handler>> assertNotFound(final VertxTestContext context) { diff --git a/site/documentation/content/api/management/device-registry-v1.yaml b/site/documentation/content/api/management/device-registry-v1.yaml index 7798534141..e724739073 100644 --- a/site/documentation/content/api/management/device-registry-v1.yaml +++ b/site/documentation/content/api/management/device-registry-v1.yaml @@ -1687,7 +1687,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 diff --git a/site/documentation/content/user-guide/device-registry.md b/site/documentation/content/user-guide/device-registry.md index 290c6a7b9f..4facc44185 100644 --- a/site/documentation/content/user-guide/device-registry.md +++ b/site/documentation/content/user-guide/device-registry.md @@ -60,7 +60,7 @@ The tenants in the registry can be managed using the Device Registry Management 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 parameters are + operation defined by the Device Registry Management API, but the *sortJson* query parameter is (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). From db5ddf6e8388ef2d4cc7950474c8aad7a22807a3 Mon Sep 17 00:00:00 2001 From: "g.dimitropoulos" Date: Tue, 25 Jul 2023 11:10:01 +0200 Subject: [PATCH 4/9] remove tab from java docs Also-by: Matthias Feurer m.feurer@sotec.eu Signed-off-by: g.dimitropoulos --- .../deviceregistry/jdbc/impl/DeviceManagementServiceImpl.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 9f067d4e2b..aa04d2b163 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 @@ -50,8 +50,8 @@ public class DeviceManagementServiceImpl extends AbstractDeviceManagementService /** * Create a new instance. * - * @param vertx The vert.x instance to use. - * @param store The backing store to use. + * @param vertx The vert.x instance to use. + * @param store The backing store to use. * @param properties The service properties. */ public DeviceManagementServiceImpl(final Vertx vertx, final TableManagementStore store, final DeviceServiceOptions properties) { From d4e0c22b88c8aa3408dbdfaf9d5b64d6fe75e18e Mon Sep 17 00:00:00 2001 From: "g.dimitropoulos" Date: Tue, 25 Jul 2023 11:11:47 +0200 Subject: [PATCH 5/9] remove tabs from java docs Also-by: Matthias Feurer m.feurer@sotec.eu Signed-off-by: g.dimitropoulos --- .../service/base/jdbc/store/device/TableManagementStore.java | 4 ++-- 1 file changed, 2 insertions(+), 2 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 47cca040c0..526305a983 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 @@ -278,8 +278,8 @@ private static Future extractVersionForUpdate(final ResultSet device, fi * It returns the plain result set from the query, which may also be empty. * * @param connection The connection to use. - * @param key The key of the device. - * @param span The span to contribute to. + * @param key The key of the device. + * @param span The span to contribute to. * @return A future tracking the outcome of the operation. */ protected Future readDeviceForUpdate(final SQLConnection connection, final DeviceKey key, final SpanContext span) { From fd152b9955791b5457b965d930e84bab8a67c7f4 Mon Sep 17 00:00:00 2001 From: "g.dimitropoulos" Date: Tue, 25 Jul 2023 11:15:58 +0200 Subject: [PATCH 6/9] remove tabs and fix java doc Also-by: Matthias Feurer m.feurer@sotec.eu Signed-off-by: g.dimitropoulos --- .../service/base/jdbc/store/device/TableManagementStore.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 526305a983..8cae7e2945 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 @@ -925,7 +925,7 @@ private List parseCredentials(final ResultSet result) { * @param tenantId The tenantId to search devices. * @param pageSize The page size. * @param pageOffset The page offset. - * @param filters The list of filters (currently only the first value of the list will be used). + * @param filters The list of filters (currently only the first value of the list is used). * @param spanContext The span to contribute to. * @return A future containing devices. */ From 46edc022cd2acaacac17d1b497f8789e3fdc1e50 Mon Sep 17 00:00:00 2001 From: "g.dimitropoulos" Date: Wed, 26 Jul 2023 10:59:25 +0200 Subject: [PATCH 7/9] remove JdbcPostgresDeviceManagementSearchDevicesTest Also-by: Matthias Feurer m.feurer@sotec.eu Signed-off-by: g.dimitropoulos --- ...gresDeviceManagementSearchDevicesTest.java | 29 ------------------- 1 file changed, 29 deletions(-) delete mode 100644 services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/JdbcPostgresDeviceManagementSearchDevicesTest.java diff --git a/services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/JdbcPostgresDeviceManagementSearchDevicesTest.java b/services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/JdbcPostgresDeviceManagementSearchDevicesTest.java deleted file mode 100644 index 529cd18243..0000000000 --- a/services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/JdbcPostgresDeviceManagementSearchDevicesTest.java +++ /dev/null @@ -1,29 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2020, 2023 Contributors to the Eclipse Foundation - * - * See the NOTICE file(s) distributed with this work for additional - * information regarding copyright ownership. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ - -package org.eclipse.hono.deviceregistry.jdbc.impl; - -import java.nio.file.Path; - - -class JdbcPostgresDeviceManagementSearchDevicesTest extends JdbcBasedDeviceManagementSearchDevicesTest { - - protected JdbcPostgresDeviceManagementSearchDevicesTest() { - super(); - DEFAULT_DATABASE_TYPE = DatabaseType.POSTGRESQL; - DATABASE_TYPE = DatabaseType.valueOf(System.getProperty(AbstractJdbcRegistryTest.class.getSimpleName() - + "_postgresql" + ".databaseType", DEFAULT_DATABASE_TYPE.name()).toUpperCase()); - EXAMPLE_SQL_BASE = Path.of("..", "base-jdbc", "src", "main", "resources", "sql", DATABASE_TYPE.name().toLowerCase()); - } - -} From c4255a5813a314c92b8e65d6c0add545d86f3fe4 Mon Sep 17 00:00:00 2001 From: "g.dimitropoulos" Date: Thu, 27 Jul 2023 09:20:50 +0200 Subject: [PATCH 8/9] make DEFAULT_DATABASE_TYPE and type private final Also-by: Matthias Feurer m.feurer@sotec.eu Signed-off-by: g.dimitropoulos --- .../deviceregistry/jdbc/impl/AbstractJdbcRegistryTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/AbstractJdbcRegistryTest.java b/services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/AbstractJdbcRegistryTest.java index 33330c95ad..53d6a500ed 100644 --- a/services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/AbstractJdbcRegistryTest.java +++ b/services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/AbstractJdbcRegistryTest.java @@ -76,8 +76,8 @@ enum DatabaseType { } protected static final Span SPAN = NoopSpan.INSTANCE; - protected static DatabaseType DEFAULT_DATABASE_TYPE = DatabaseType.H2; - protected static DatabaseType DATABASE_TYPE = DatabaseType.valueOf(System.getProperty(AbstractJdbcRegistryTest.class.getSimpleName() + private static final DatabaseType DEFAULT_DATABASE_TYPE = DatabaseType.H2; + private static final DatabaseType DATABASE_TYPE = DatabaseType.valueOf(System.getProperty(AbstractJdbcRegistryTest.class.getSimpleName() + ".databaseType", DEFAULT_DATABASE_TYPE.name()).toUpperCase()); private static final Map> DATABASE_CONTAINER_CACHE = new ConcurrentHashMap<>(); private static final String POSTGRESQL_IMAGE_NAME = System.getProperty(AbstractJdbcRegistryTest.class.getSimpleName() @@ -86,7 +86,7 @@ enum DatabaseType { private static final AtomicLong UNIQUE_ID_GENERATOR = new AtomicLong(System.currentTimeMillis()); private static final Tracer TRACER = NoopTracerFactory.create(); - protected static Path EXAMPLE_SQL_BASE = Path.of("..", "base-jdbc", "src", "main", "resources", "sql", DATABASE_TYPE.name().toLowerCase()); + private static final Path EXAMPLE_SQL_BASE = Path.of("..", "base-jdbc", "src", "main", "resources", "sql", DATABASE_TYPE.name().toLowerCase()); private static final Path BASE_DIR = Path.of("target").toAbsolutePath(); From a77cff4f4ea15a3654c8e58ffea2cf00762615c7 Mon Sep 17 00:00:00 2001 From: "g.dimitropoulos" Date: Thu, 27 Jul 2023 09:29:15 +0200 Subject: [PATCH 9/9] remove tabs from javadoc Also-by: Matthias Feurer m.feurer@sotec.eu Signed-off-by: g.dimitropoulos --- .../store/device/TableManagementStore.java | 58 +++++++++---------- 1 file changed, 29 insertions(+), 29 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 8cae7e2945..e1a27ded42 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 @@ -108,7 +108,7 @@ public class TableManagementStore extends AbstractDeviceStore { * * @param client The client to use for accessing the DB. * @param tracer The tracer to use. - * @param cfg The SQL statement configuration. + * @param cfg The SQL statement configuration. */ public TableManagementStore(final JDBCClient client, final Tracer tracer, final StatementConfiguration cfg) { super(client, tracer, cfg); @@ -297,12 +297,12 @@ protected Future readDeviceForUpdate(final SQLConnection connection, * a duplicate entity or constraint violation. This will be translated into a * failed future with an {@link org.eclipse.hono.service.base.jdbc.store.DuplicateKeyException}. * - * @param key The key of the device to create. - * @param device The device data. - * @param tenant The configuration of the tenant that the device belongs to. + * @param key The key of the device to create. + * @param device The device data. + * @param tenant The configuration of the tenant that the device belongs to. * @param globalDevicesPerTenantLimit The globally defined maximum number of devices per tenant. A value * <= 0 will be interpreted as no limit being defined. - * @param spanContext The span to contribute to. + * @param spanContext The span to contribute to. * @return A future, tracking the outcome of the operation. */ public Future> createDevice( @@ -418,12 +418,12 @@ private Future deleteGroups(final SQLConnection connection, * not exists. If the device exists, but the resource version does not match, the result * will fail with an {@link OptimisticLockingException}. * - * @param key The key of the device to update. - * @param statement The statement to use for the update. - * @param jsonValue The value to set. + * @param key The key of the device to update. + * @param statement The statement to use for the update. + * @param jsonValue The value to set. * @param resourceVersion The optional resource version. - * @param nextVersion The new version to set. - * @param span The span to contribute to. + * @param nextVersion The new version to set. + * @param span The span to contribute to. * @return A future, tracking the outcome of the operation. */ protected Future updateJsonField( @@ -464,10 +464,10 @@ protected Future updateJsonField( * with either the {@code updateRegistration} or {@code updateRegistrationVersioned} * statement. * - * @param key The key of the device to update. - * @param device The device data to store. + * @param key The key of the device to update. + * @param device The device data to store. * @param resourceVersion The optional resource version. - * @param spanContext The span to contribute to. + * @param spanContext The span to contribute to. * @return A future, tracking the outcome of the operation. */ public Future> updateDevice( @@ -547,7 +547,7 @@ public Future> updateDevice( * If there is exactly one row, it will read the device registration information from the column * {@code data} and optionally current resource version from the column {@code version}. * - * @param key The key of the device to read. + * @param key The key of the device to read. * @param spanContext The span to contribute to. * @return A future, tracking the outcome of the operation. */ @@ -585,9 +585,9 @@ public Future> readDevice(final DeviceKey key, final * the named parameters {@code tenant_id}, {@code device_id}, and {@code expected_version} (if set). * It will return the plain update result of the operation. * - * @param key The key of the device to delete. + * @param key The key of the device to delete. * @param resourceVersion An optional resource version. - * @param spanContext The span to contribute to. + * @param spanContext The span to contribute to. * @return A future, tracking the outcome of the operation. */ public Future deleteDevice( @@ -632,7 +632,7 @@ public Future deleteDevice( /** * Delete all devices belonging to the provided tenant. * - * @param tenantId The tenant to clean up. + * @param tenantId The tenant to clean up. * @param spanContext The span to contribute to. * @return A future tracking the outcome of the operation. */ @@ -658,11 +658,11 @@ public Future dropTenant(final String tenantId, final SpanContext /** * Gets the number of devices that are registered for a tenant. * - * @param tenantId The tenant to count devices for. - * @param spanContext The span to contribute to. + * @param tenantId The tenant to count devices for. + * @param spanContext The span to contribute to. * @param countStatement The count statement to use. - * @param field The field of filter expression. - * @param value The value of the filter expression. + * @param field The field of filter expression. + * @param value The value of the filter expression. * @return A future tracking the outcome of the operation. * @throws NullPointerException if tenant is {@code null}. */ @@ -708,10 +708,10 @@ public Future getDeviceCount(final String tenantId, final SpanContext s * If the resource version was provided, but the provided version was no longer the current version, * then the future will fail with a {@link OptimisticLockingException}. * - * @param key The key of the device to update. - * @param credentials The credentials to set. + * @param key The key of the device to update. + * @param credentials The credentials to set. * @param resourceVersion The optional resource version to update. - * @param spanContext The span to contribute to. + * @param spanContext The span to contribute to. * @return A future, tracking the outcome of the operation. */ public Future> setCredentials( @@ -858,7 +858,7 @@ private Future recoverNotFound(final Span span, final Throwable err, fina * result must be empty. If no credentials could be found for an existing device, * the result must not be empty, but provide an empty {@link CredentialsReadResult}. * - * @param key The key of the device. + * @param key The key of the device. * @param spanContext The span to contribute to. * @return A future, tracking the outcome of the operation. */ @@ -922,10 +922,10 @@ private List parseCredentials(final ResultSet result) { /** * Gets a list of devices of a specific tenant. * - * @param tenantId The tenantId to search devices. - * @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 tenantId The tenantId to search devices. + * @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 spanContext The span to contribute to. * @return A future containing devices. */