Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for json-schema in configuration source type #66

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: %Bundle-Name
Bundle-Vendor: %Bundle-Vendor
Bundle-SymbolicName: org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests
Bundle-SymbolicName: org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests;singleton:=true
Bundle-Version: 0.1.9.qualifier
Bundle-Localization: plugin
Bundle-ActivationPolicy: lazy
Expand All @@ -29,6 +29,7 @@ Export-Package: org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core
org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs.webapp,
org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.utils
Import-Package: com.fasterxml.jackson.annotation,
com.fasterxml.jackson.core,
com.fasterxml.jackson.jaxrs.base,
com.fasterxml.jackson.jaxrs.json,
com.google.common.base,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,6 @@ output.. = bin/
bin.includes = META-INF/,\
.,\
about.html,\
plugin.properties
plugin.properties,\
plugin.xml,\
schema/
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<?eclipse version="3.4"?>
<plugin>
<extension
point="org.eclipse.tracecompass.tmf.core.config">
<source
class="org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs.config.TestSchemaConfigurationSource"
id="org.eclipse.tracecompass.tmf.core.config.xmlsourcetype">
</source>
</extension>
</plugin>
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://org.eclipse.tracecompass/custom-execution-analysis.json",
"title": "Custom Execution Analysis",
"description": "Custom Execution Analysis schema",
"type": "object",
"properties": {
"cpus": {
"description": "array of integer",
"type": "array",
"items": {
"type": "number"
}
},
"thread": {
"description": "Thread regular expression pattern",
"type": "string"
},
"phone": {
"description": "Phone number",
"type": "string",
"pattern": "^(\\([0-9]{3}\\))?[0-9]{3}-[0-9]{4}$"
},
"label": {
"description": "Optional label text",
"type": "string"
}
},
"required": ["thread"]
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@
import org.junit.Test;
import org.osgi.framework.Bundle;

import com.fasterxml.jackson.databind.JsonNode;

/**
* Basic test for the {@link ConfigurationManagerService}.
*
Expand Down Expand Up @@ -142,6 +144,14 @@ public void testSourceType() {
assertEquals(EXPECTED_DATA_TYPE, desc.getDataType());
assertEquals(EXPECTED_PARAM_DESCRIPTION, desc.getDescription());
assertTrue(desc.isRequired());

// Verify configuration source type with schema
Optional<TmfConfigurationSourceTypeStub> optional2 = configurations.stream().filter(config -> config.getId().equals("org.eclipse.tracecompass.tmf.core.config.testschemasourcetype")).findAny();
assertTrue(optional2.isPresent());
TmfConfigurationSourceTypeStub type2 = optional2.get();
JsonNode schema = type2.getSchema();
// Verify that schema exists
assertNotNull(schema);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.JsonNode;

/**
* Basic Implementation of the serialized {@link ITmfConfigurationSourceType} model used by clients.
Expand All @@ -36,6 +37,7 @@ public class TmfConfigurationSourceTypeStub implements Serializable, ITmfConfigu
*/
private static final long serialVersionUID = 6934234848155424428L;
private final ITmfConfigurationSourceType fConfig;
private final JsonNode fSchema;

/**
* {@link JsonCreator} Constructor for final fields
Expand All @@ -46,7 +48,9 @@ public class TmfConfigurationSourceTypeStub implements Serializable, ITmfConfigu
* the name
* @param description
* the help text
* @param queryParamKeys
* @param schema
* the JSON schema JsonNode
* @param parameterDescriptors
* the list of keys
*
*/
Expand All @@ -55,7 +59,8 @@ public class TmfConfigurationSourceTypeStub implements Serializable, ITmfConfigu
public TmfConfigurationSourceTypeStub(@JsonProperty("id") String id,
@JsonProperty("name") String name,
@JsonProperty("description") String description,
@JsonProperty("parameterDescriptors") List<ConfigParamDescriptorStub> parameterDescriptors) {
@JsonProperty("parameterDescriptors") List<ConfigParamDescriptorStub> parameterDescriptors,
@JsonProperty("schema") JsonNode schema) {
super();

TmfConfigurationSourceType.Builder builder = new TmfConfigurationSourceType.Builder()
Expand All @@ -66,33 +71,32 @@ public TmfConfigurationSourceTypeStub(@JsonProperty("id") String id,
builder.setConfigParamDescriptors(parameterDescriptors.stream().map(stub -> stub.getConfig()).collect(Collectors.toList()));
}
fConfig = builder.build();
fSchema = schema;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should it be added to the builder and to TmfConfigurationSourceType class?

Copy link
Contributor Author

@bhufmann bhufmann Sep 30, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would that TmfConfigurationSourceType would require the jackson JSON parser because fSchema is of type JsonNode. Besides the TmfConfigurationSourceTypeStub a client-side implementation for testing the trace server as part of the unit tests.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, if I understand correctly, this stub class is on the client side of the test, not the trace-server side, and it uses TmfConfigurationSourceType as simplification to deserialize the trace-server response?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, it's on the client side of the test.

}

/**
* @return the JSON schema string
*/
public JsonNode getSchema() {
PatrickTasse marked this conversation as resolved.
Show resolved Hide resolved
return fSchema;
}

ITmfConfigurationSourceType getConfig() {
return fConfig;
}

@Override
public int hashCode() {
return fConfig.hashCode();
return Objects.hash(fConfig, fSchema == null ? "null" : fSchema);
}

@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
if (!(obj instanceof TmfConfigurationSourceTypeStub)) {
return false;
}
if (obj instanceof TmfConfigurationSourceTypeStub) {
return Objects.equals(this.getConfig(), ((TmfConfigurationSourceTypeStub) obj).getConfig());
}
if (obj instanceof TmfConfigurationSourceType) {
return Objects.equals(this.getConfig(), obj);
}
return false;
TmfConfigurationSourceTypeStub other = (TmfConfigurationSourceTypeStub) obj;
return Objects.equals(fConfig, other.fConfig) && Objects.equals(fSchema, other.fSchema);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/*******************************************************************************
* Copyright (c) 2023 Ericsson
*
* All rights reserved. This program and the accompanying materials are
* made available under the terms of the Eclipse Public License 2.0 which
* accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*******************************************************************************/
package org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs.config;

import static org.eclipse.tracecompass.common.core.NonNullUtils.nullToEmptyString;

import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Collections;
import java.util.List;
import java.util.Map;

import org.eclipse.core.runtime.FileLocator;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Platform;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.tracecompass.tmf.core.config.ITmfConfiguration;
import org.eclipse.tracecompass.tmf.core.config.ITmfConfigurationSource;
import org.eclipse.tracecompass.tmf.core.config.ITmfConfigurationSourceType;
import org.eclipse.tracecompass.tmf.core.config.TmfConfigurationSourceType;
import org.eclipse.tracecompass.tmf.core.exceptions.TmfConfigurationException;
import org.osgi.framework.Bundle;

/**
* Test class
*/
public class TestSchemaConfigurationSource implements ITmfConfigurationSource {

private static final @NonNull ITmfConfigurationSourceType fType;

private static final @NonNull String TEST_ANALYSIS_TYPE_ID = "org.eclipse.tracecompass.tmf.core.config.testschemasourcetype"; //$NON-NLS-1$
private static final @NonNull String NAME = nullToEmptyString("Test Schema Type"); //$NON-NLS-1$
private static final @NonNull String DESCRIPTION = nullToEmptyString("Test Type with schema"); //$NON-NLS-1$

static {
Bundle bundle = Platform.getBundle("org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests");
IPath defaultPath = new org.eclipse.core.runtime.Path("schema/custom-execution-analysis.json"); //$NON-NLS-1$
URL url = FileLocator.find(bundle, defaultPath, null);
File schemaFile = null;
try {
schemaFile = new File(FileLocator.toFileURL(url).toURI());
} catch (URISyntaxException | IOException e) {
e.printStackTrace();
}
fType = new TmfConfigurationSourceType.Builder()
.setId(TEST_ANALYSIS_TYPE_ID)
.setDescription(DESCRIPTION)
.setName(NAME)
.setSchemaFile(schemaFile)
PatrickTasse marked this conversation as resolved.
Show resolved Hide resolved
.setConfigParamDescriptors(Collections.emptyList()).build();
}

/**
* Constructor
*/
public TestSchemaConfigurationSource() {
}

@Override
public @NonNull ITmfConfigurationSourceType getConfigurationSourceType() {
return fType;
}

@Override
public @NonNull ITmfConfiguration create(@NonNull Map<@NonNull String, @NonNull Object> parameters) throws TmfConfigurationException {
throw new TmfConfigurationException("Not implemented yet"); //$NON-NLS-1$
}

@Override
public @NonNull ITmfConfiguration update(@NonNull String id, @NonNull Map<@NonNull String, @NonNull Object> parameters) throws TmfConfigurationException {
throw new TmfConfigurationException("Not implemented yet"); //$NON-NLS-1$
}

@Override
public @Nullable ITmfConfiguration get(@NonNull String id) {
return null;
}

@Override
public @Nullable ITmfConfiguration remove(@NonNull String id) {
return null;
}

@Override
public boolean contains(@NonNull String id) {
return false;
}

@Override
public @NonNull List<@NonNull ITmfConfiguration> getConfigurations() {
return Collections.emptyList();
}

@Override
public void dispose() {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,26 @@ public interface ConfigurationSourceType {
* configuration instance of this type. Use 'path' key for file
* URIs.
*/
@Schema(description = "A list of configuration parameter descriptors to be passed when creating or updating a configuration instance of this type.")
@Schema(description = "A list of configuration parameter descriptors to be "
+ "passed when creating or updating a configuration instance of this "
+ "type. Use this instead of schema. Omit if not used.")
List<ConfigurationParameterDescriptor> getParameterDescriptors();

/**
* @return a JSON schema that describes the parameters that the front-end
* needs to provide with corresponding values. Use this for complex
* parameter descriptions instead of parameterDescriptors.
*/
@Schema(description = "A JSON object that describes a JSON schema for parameters that "
+ "the front-end needs to provide with corresponding values. The schema has to "
+ "adhere to JSON schema specification (see https://json-schema.org/). "
+ "Use this for complex parameter descriptions instead of parameterDescriptors. "
+ "Omit if not used.")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please mention that the object should adhere to the JSON Schema specification.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

OptionalSchema getSchema();

/**
* A JSON Object defining a schema.
*/
interface OptionalSchema {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,16 @@

package org.eclipse.tracecompass.incubator.internal.trace.server.jersey.rest.core.webapp;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

import org.eclipse.tracecompass.tmf.core.config.ITmfConfigurationSourceType;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;

Expand Down Expand Up @@ -48,6 +53,15 @@ public void serialize(ITmfConfigurationSourceType value, JsonGenerator gen, Seri
if (!value.getConfigParamDescriptors().isEmpty()) {
gen.writeObjectField("parameterDescriptors", value.getConfigParamDescriptors()); //$NON-NLS-1$
}
File schemaFile = value.getSchemaFile();
PatrickTasse marked this conversation as resolved.
Show resolved Hide resolved
if (schemaFile != null) {
try (InputStream inputStream = new FileInputStream(schemaFile)) {
ObjectMapper mapper = new ObjectMapper();
JsonNode schema = mapper.readTree(inputStream);
gen.writeFieldName("schema"); //$NON-NLS-1$
gen.writeTree(schema);
}
}
gen.writeEndObject();
}
}
Loading