diff --git a/ext/hivemq-edge-openapi-2024.8-SNAPSHOT.yaml b/ext/hivemq-edge-openapi-2024.8-SNAPSHOT.yaml index b2610a0d0..5e0a0bdf9 100644 --- a/ext/hivemq-edge-openapi-2024.8-SNAPSHOT.yaml +++ b/ext/hivemq-edge-openapi-2024.8-SNAPSHOT.yaml @@ -3684,6 +3684,27 @@ paths: summary: Obtain a list of configured adapters for the specified type tags: - Protocol Adapters + /api/v1/management/sampling/schema/{topic}: + get: + description: Obtain a JsonSchema based in the stored samples for a given topic. + operationId: getSchemaForTopic + parameters: + - description: The topic. + in: path + name: topic + required: true + schema: + type: string + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/JsonNode' + description: Success + summary: Obtain a JsonSchema based in the stored samples for a given topic. + tags: + - Payload Sampling /api/v1/management/sampling/topic/{topic}: get: description: Obtain a list of samples that their gathered for the given topic. diff --git a/hivemq-edge/build.gradle.kts b/hivemq-edge/build.gradle.kts index 87b9aef95..4668b7263 100644 --- a/hivemq-edge/build.gradle.kts +++ b/hivemq-edge/build.gradle.kts @@ -95,6 +95,7 @@ java { repositories { mavenCentral() + maven { url = uri("https://jitpack.io") } exclusiveContent { forRepository { maven { @@ -194,6 +195,9 @@ dependencies { implementation(libs.jsonSchemaValidator) implementation(libs.victools.jsonschema.generator) implementation(libs.victools.jsonschema.jackson) + // TODO add to toml after changes are merged. + implementation("com.github.saasquatch:json-schema-inferrer:0.2.1") + } /* ******************** test ******************** */ diff --git a/hivemq-edge/src/main/java/com/hivemq/api/resources/SamplingApi.java b/hivemq-edge/src/main/java/com/hivemq/api/resources/SamplingApi.java index 38b8da4cd..99e8dfdd9 100644 --- a/hivemq-edge/src/main/java/com/hivemq/api/resources/SamplingApi.java +++ b/hivemq-edge/src/main/java/com/hivemq/api/resources/SamplingApi.java @@ -16,6 +16,7 @@ package com.hivemq.api.resources; +import com.fasterxml.jackson.databind.JsonNode; import com.hivemq.api.model.samples.PayloadSampleList; import com.hivemq.extension.sdk.api.annotations.NotNull; import io.swagger.v3.oas.annotations.Operation; @@ -64,6 +65,25 @@ Response getSamplesForTopic( required = true, in = ParameterIn.PATH) @PathParam("topic") String topic); + @GET + @Path("/schema/{topic}") + @Operation(summary = "Obtain a JsonSchema based in the stored samples for a given topic.", + operationId = "getSchemaForTopic", + description = "Obtain a JsonSchema based in the stored samples for a given topic.", + responses = { + @ApiResponse(responseCode = "200", + description = "Success", + content = @Content(mediaType = MediaType.APPLICATION_JSON, + schema = @Schema(implementation = JsonNode.class)))}) + @Produces(MediaType.APPLICATION_JSON) + @NotNull + Response getSchemaForTopic( + @NotNull @Parameter(name = "topic", + description = "The topic.", + required = true, + in = ParameterIn.PATH) @PathParam("topic") String topic); + + @POST @Path("/topic/{topic}") @Operation(summary = "Start sampling for the given topic.", diff --git a/hivemq-edge/src/main/java/com/hivemq/api/resources/impl/SamplingResourceImpl.java b/hivemq-edge/src/main/java/com/hivemq/api/resources/impl/SamplingResourceImpl.java index 3801843d7..fd29f7e46 100644 --- a/hivemq-edge/src/main/java/com/hivemq/api/resources/impl/SamplingResourceImpl.java +++ b/hivemq-edge/src/main/java/com/hivemq/api/resources/impl/SamplingResourceImpl.java @@ -15,15 +15,23 @@ */ package com.hivemq.api.resources.impl; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; import com.hivemq.api.model.samples.PayloadSample; import com.hivemq.api.model.samples.PayloadSampleList; import com.hivemq.api.resources.SamplingApi; import com.hivemq.extension.sdk.api.annotations.NotNull; import com.hivemq.sampling.SamplingService; +import com.hivemq.util.ErrorResponseUtil; +import com.saasquatch.jsonschemainferrer.AdditionalPropertiesPolicies; +import com.saasquatch.jsonschemainferrer.JsonSchemaInferrer; +import com.saasquatch.jsonschemainferrer.RequiredPolicies; +import com.saasquatch.jsonschemainferrer.SpecVersion; import javax.inject.Inject; import javax.inject.Singleton; import javax.ws.rs.core.Response; +import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Base64; @@ -34,10 +42,19 @@ public class SamplingResourceImpl implements SamplingApi { private final @NotNull SamplingService samplingService; + private final @NotNull ObjectMapper objectMapper; + private static final JsonSchemaInferrer INFERRER = JsonSchemaInferrer.newBuilder() + .setSpecVersion(SpecVersion.DRAFT_07) + .setAdditionalPropertiesPolicy(AdditionalPropertiesPolicies.notAllowed()) + .setRequiredPolicy(RequiredPolicies.nonNullCommonFields()) + .build(); + @Inject - public SamplingResourceImpl(final @NotNull SamplingService samplingService) { + public SamplingResourceImpl( + final @NotNull SamplingService samplingService, final @NotNull ObjectMapper objectMapper) { this.samplingService = samplingService; + this.objectMapper = objectMapper; } @Override @@ -55,6 +72,28 @@ public SamplingResourceImpl(final @NotNull SamplingService samplingService) { return Response.ok().entity(new PayloadSampleList(sampleArrayList)).build(); } + @Override + public @NotNull Response getSchemaForTopic(@NotNull final String topicBase64) { + final String topic = new String(Base64.getDecoder().decode(topicBase64), StandardCharsets.UTF_8); + final List samples = samplingService.getSamples(topic); + if (samples.isEmpty()) { + return ErrorResponseUtil.notFound("samples", "topic"); + } + + final ArrayList jsonSamples = new ArrayList<>(); + for (final byte[] sample : samples) { + try { + jsonSamples.add(objectMapper.readTree(sample)); + } catch (final IOException e) { + // TODO + return Response.serverError().build(); + } + } + + final JsonNode inferredSchema = INFERRER.inferForSamples(jsonSamples); + return Response.ok().entity(inferredSchema).build(); + } + @Override public @NotNull Response startSamplingForTopic(@NotNull final String topicBase64) { final String topic = new String(Base64.getDecoder().decode(topicBase64), StandardCharsets.UTF_8); diff --git a/modules/hivemq-edge-module-etherip/build.gradle.kts b/modules/hivemq-edge-module-etherip/build.gradle.kts index 7b8ee7440..7f836e25d 100644 --- a/modules/hivemq-edge-module-etherip/build.gradle.kts +++ b/modules/hivemq-edge-module-etherip/build.gradle.kts @@ -22,6 +22,7 @@ java { repositories { mavenCentral() + maven { url = uri("https://jitpack.io") } exclusiveContent { forRepository { maven { diff --git a/modules/hivemq-edge-module-file/build.gradle.kts b/modules/hivemq-edge-module-file/build.gradle.kts index e2063eace..2c4b899b5 100644 --- a/modules/hivemq-edge-module-file/build.gradle.kts +++ b/modules/hivemq-edge-module-file/build.gradle.kts @@ -21,6 +21,7 @@ java { repositories { mavenCentral() + maven { url = uri("https://jitpack.io") } exclusiveContent { forRepository { maven { diff --git a/modules/hivemq-edge-module-http/build.gradle.kts b/modules/hivemq-edge-module-http/build.gradle.kts index d14a91865..4044d87e9 100644 --- a/modules/hivemq-edge-module-http/build.gradle.kts +++ b/modules/hivemq-edge-module-http/build.gradle.kts @@ -21,6 +21,7 @@ java { repositories { mavenCentral() + maven { url = uri("https://jitpack.io") } exclusiveContent { forRepository { maven { diff --git a/modules/hivemq-edge-module-modbus/build.gradle.kts b/modules/hivemq-edge-module-modbus/build.gradle.kts index 29acef532..596d3d2e3 100644 --- a/modules/hivemq-edge-module-modbus/build.gradle.kts +++ b/modules/hivemq-edge-module-modbus/build.gradle.kts @@ -22,6 +22,7 @@ java { repositories { mavenCentral() + maven { url = uri("https://jitpack.io") } exclusiveContent { forRepository { maven { diff --git a/modules/hivemq-edge-module-opcua/build.gradle.kts b/modules/hivemq-edge-module-opcua/build.gradle.kts index eaa9871f1..154071421 100644 --- a/modules/hivemq-edge-module-opcua/build.gradle.kts +++ b/modules/hivemq-edge-module-opcua/build.gradle.kts @@ -21,6 +21,7 @@ java { repositories { mavenCentral() + maven { url = uri("https://jitpack.io") } exclusiveContent { forRepository { maven { diff --git a/modules/hivemq-edge-module-plc4x/build.gradle.kts b/modules/hivemq-edge-module-plc4x/build.gradle.kts index 8ddd8f52c..d9450676e 100644 --- a/modules/hivemq-edge-module-plc4x/build.gradle.kts +++ b/modules/hivemq-edge-module-plc4x/build.gradle.kts @@ -22,6 +22,7 @@ java { repositories { mavenCentral() + maven { url = uri("https://jitpack.io") } exclusiveContent { forRepository { maven {