diff --git a/README.md b/README.md index e3b9f568..b83f5a3c 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ Please note that the artifact name changed from "virtualschema-common" to "virtu | Dependency | Purpose | License | |------------------------------------------------------------------------------|--------------------------------------------------------|-------------------------------| | [JSON-P](https://javaee.github.io/jsonp/) | JSON Processing | CDDL-1.0 | -| [Exasol Script API](https://www.exasol.com/portal/display/DOC/User+Manual+6.1.0 (Sections 3.6, 3.7)) |Accessing objects | MIT License | +| [Exasol Script API](https://www.exasol.com/portal/display/DOC/User+Manual+6.1.0 (Sections 3.6, 3.7)) | Accessing Exasol features | MIT License | | [Google Guava](https://github.com/google/guava/) | Open-source set of common libraries for Java | Apache License 2.0 | ### Build Time Dependencies diff --git a/launch/virtual-schema-common all tests.launch b/launch/virtual-schema-common all tests.launch new file mode 100644 index 00000000..58f87b40 --- /dev/null +++ b/launch/virtual-schema-common all tests.launch @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/launch/virtual-schema-common clean.launch b/launch/virtual-schema-common clean.launch index d8a25115..5bf0d8f2 100644 --- a/launch/virtual-schema-common clean.launch +++ b/launch/virtual-schema-common clean.launch @@ -12,5 +12,8 @@ + + + diff --git a/launch/virtual-schema-common install.launch b/launch/virtual-schema-common install.launch index 6ab15efe..b3b911b8 100644 --- a/launch/virtual-schema-common install.launch +++ b/launch/virtual-schema-common install.launch @@ -12,5 +12,8 @@ + + + diff --git a/launch/virtual-schema-common package.launch b/launch/virtual-schema-common package.launch index 43c4cb1f..73b1d9cf 100644 --- a/launch/virtual-schema-common package.launch +++ b/launch/virtual-schema-common package.launch @@ -12,5 +12,8 @@ + + + diff --git a/pom.xml b/pom.xml index c8915ba6..46e10625 100644 --- a/pom.xml +++ b/pom.xml @@ -1,9 +1,9 @@ + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 com.exasol virtual-schema-common-java - 1.1.1 + 2.0.0 Common module of Exasol Virtual Schemas Adapters This is one of the modules of Virtual Schemas Adapters. The libraries provided by this project are the foundation of the adapter development, i.e. adapters must be implemented on top of them. https://github.com/exasol/virtual-schema-common-java @@ -11,6 +11,9 @@ UTF-8 UTF-8 1.8 + 5.4.0 + 1.3.2 + 3.0.0-M3 true @@ -75,17 +78,6 @@ guava 18.0 - - junit - junit - 4.12 - test - - - org.mockito - mockito-core - 2.24.0 - org.hamcrest hamcrest-junit @@ -101,20 +93,19 @@ org.junit.jupiter junit-jupiter-engine - 5.3.2 + ${junit.version} test org.junit.platform junit-platform-runner - 1.3.2 + ${junit.platform.version} test - org.junit.vintage - junit-vintage-engine - 5.3.2 - test + org.mockito + mockito-core + 2.24.0 org.mockito @@ -156,7 +147,7 @@ org.apache.maven.plugins maven-surefire-plugin - 3.0.0-M3 + ${maven.surefire.version} org.apache.maven.plugins @@ -207,4 +198,4 @@ - + \ No newline at end of file diff --git a/src/main/java/com/exasol/adapter/capabilities/Capabilities.java b/src/main/java/com/exasol/adapter/capabilities/Capabilities.java index cf21db79..62e83652 100644 --- a/src/main/java/com/exasol/adapter/capabilities/Capabilities.java +++ b/src/main/java/com/exasol/adapter/capabilities/Capabilities.java @@ -1,91 +1,163 @@ package com.exasol.adapter.capabilities; -import java.util.HashSet; +import java.util.EnumSet; import java.util.Set; /** - * Manages a set of supported Capabilities + * Manages a set of supported capabilities */ public class Capabilities { - private final Set mainCapabilities = new HashSet<>(); - private final Set scalarFunctionCaps = new HashSet<>(); - private final Set predicateCaps = new HashSet<>(); - private final Set aggregateFunctionCaps = new HashSet<>(); - private final Set literalCaps = new HashSet<>(); - - public void supportAllCapabilities() { - for (final MainCapability cap : MainCapability.values()) { - supportMainCapability(cap); - } - for (final ScalarFunctionCapability function : ScalarFunctionCapability.values()) { - supportScalarFunction(function); - } - for (final PredicateCapability pred : PredicateCapability.values()) { - supportPredicate(pred); - } - for (final AggregateFunctionCapability function : AggregateFunctionCapability.values()) { - supportAggregateFunction(function); - } - for (final LiteralCapability cap : LiteralCapability.values()) { - supportLiteral(cap); - } - } + private final Set mainCapabilities; + private final Set literalCapabilities; + private final Set predicateCapabilities; + private final Set scalarFunctionCapabilities; + private final Set aggregateFunctionCapabilities; - public void subtractCapabilities(final Capabilities capabilitiesToSubtract) { - for (final MainCapability cap : capabilitiesToSubtract.mainCapabilities) { - mainCapabilities.remove(cap); - } - for (final ScalarFunctionCapability cap : capabilitiesToSubtract.getScalarFunctionCapabilities()) { - scalarFunctionCaps.remove(cap); - } - for (final PredicateCapability cap : capabilitiesToSubtract.getPredicateCapabilities()) { - predicateCaps.remove(cap); - } - for (final AggregateFunctionCapability cap : capabilitiesToSubtract.getAggregateFunctionCapabilities()) { - aggregateFunctionCaps.remove(cap); - } - for (final LiteralCapability cap : capabilitiesToSubtract.getLiteralCapabilities()) { - literalCaps.remove(cap); - } + private Capabilities(final Builder builder) { + this.mainCapabilities = EnumSet.copyOf(builder.mainCapabilities); + this.literalCapabilities = EnumSet.copyOf(builder.literalCapabilities); + this.predicateCapabilities = EnumSet.copyOf(builder.predicateCapabilities); + this.scalarFunctionCapabilities = EnumSet.copyOf(builder.scalarFunctionCapabilities); + this.aggregateFunctionCapabilities = EnumSet.copyOf(builder.aggregateFunctionCapabilities); } - public void supportMainCapability(final MainCapability cap) { - mainCapabilities.add(cap); + /** + * Get the Virtual Schema's adapters main capabilities + * + * @return main capabilities + */ + public Set getMainCapabilities() { + return this.mainCapabilities; } - public void supportScalarFunction(final ScalarFunctionCapability functionType) { - scalarFunctionCaps.add(functionType); + /** + * Get the Virtual Schema's adapters literal capabilities + * + * @return scalar literal capabilities + */ + public Set getLiteralCapabilities() { + return this.literalCapabilities; } - public void supportPredicate(final PredicateCapability predicate) { - predicateCaps.add(predicate); + /** + * Get the Virtual Schema's adapters predicate capabilities + * + * @return predicate capabilities + */ + public Set getPredicateCapabilities() { + return this.predicateCapabilities; } - public void supportAggregateFunction(final AggregateFunctionCapability functionType) { - aggregateFunctionCaps.add(functionType); + /** + * Get the Virtual Schema's adapters scalar function capabilities + * + * @return scalar function capabilities + */ + public Set getScalarFunctionCapabilities() { + return this.scalarFunctionCapabilities; } - public void supportLiteral(final LiteralCapability literal) { - literalCaps.add(literal); + /** + * Get the Virtual Schema's adapters aggregate function capabilities + * + * @return aggregate function capabilities + */ + public Set getAggregateFunctionCapabilities() { + return this.aggregateFunctionCapabilities; } - public Set getMainCapabilities() { - return mainCapabilities; + /** + * Get a {@link Capabilities} builder + * + * @return builder instance + */ + public static Builder builder() { + return new Builder(); } - public Set getScalarFunctionCapabilities() { - return scalarFunctionCaps; - } + /** + * Builder for {@link Capabilities} + */ + public static final class Builder { + final Set mainCapabilities = EnumSet.noneOf(MainCapability.class); + final Set literalCapabilities = EnumSet.noneOf(LiteralCapability.class); + final Set predicateCapabilities = EnumSet.noneOf(PredicateCapability.class); + final Set scalarFunctionCapabilities = EnumSet.noneOf(ScalarFunctionCapability.class); + final Set aggregateFunctionCapabilities = EnumSet + .noneOf(AggregateFunctionCapability.class); - public Set getPredicateCapabilities() { - return predicateCaps; - } + /** + * Create new capability instance + * + * @return new capability instance + */ + public Capabilities build() { + return new Capabilities(this); + } - public Set getAggregateFunctionCapabilities() { - return aggregateFunctionCaps; - } + /** + * Add one or more main capabilities + * + * @param capabilities capabilities to be added + * @return builder instance for fluent programming + */ + public Builder addMain(final MainCapability... capabilities) { + for (final MainCapability capability : capabilities) { + this.mainCapabilities.add(capability); + } + return this; + } - public Set getLiteralCapabilities() { - return literalCaps; + /** + * Add one or more literal capabilities + * + * @param capabilities capabilities to be added + * @return builder instance for fluent programming + */ + public Builder addLiteral(final LiteralCapability... capabilities) { + for (final LiteralCapability capability : capabilities) { + this.literalCapabilities.add(capability); + } + return this; + } + + /** + * Add one or more predicate capabilities + * + * @param capabilities capabilities to be added + * @return builder instance for fluent programming + */ + public Builder addPredicate(final PredicateCapability... capabilities) { + for (final PredicateCapability capability : capabilities) { + this.predicateCapabilities.add(capability); + } + return this; + } + + /** + * Add one or more scalar function capabilities + * + * @param capabilities capabilities to be added + * @return builder instance for fluent programming + */ + public Builder addScalarFunction(final ScalarFunctionCapability... capabilities) { + for (final ScalarFunctionCapability capability : capabilities) { + this.scalarFunctionCapabilities.add(capability); + } + return this; + } + + /** + * Add one or more aggregate function capabilities + * + * @param capabilities capabilities to be added + * @return builder instance for fluent programming + */ + public Builder addAggregateFunction(final AggregateFunctionCapability... capabilities) { + for (final AggregateFunctionCapability capability : capabilities) { + this.aggregateFunctionCapabilities.add(capability); + } + return this; + } } -} +} \ No newline at end of file diff --git a/src/test/java/com/exasol/adapter/capabilities/CapabilitiesTest.java b/src/test/java/com/exasol/adapter/capabilities/CapabilitiesTest.java new file mode 100644 index 00000000..fe3b85c0 --- /dev/null +++ b/src/test/java/com/exasol/adapter/capabilities/CapabilitiesTest.java @@ -0,0 +1,84 @@ +package com.exasol.adapter.capabilities; + +import static com.exasol.adapter.capabilities.CapabilityAssertions.*; +import static org.junit.jupiter.api.Assertions.assertAll; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class CapabilitiesTest { + private Capabilities.Builder builder; + + @BeforeEach + void beforeEach() { + this.builder = Capabilities.builder(); + } + + @Test + void testCreateEmptyCapabilities() { + final Capabilities capabilities = this.builder.build(); + assertAll(() -> assertEmptyMainCapabilities(capabilities), // + () -> assertEmptyLiteralCapabilities(capabilities), // + () -> assertEmptyPredicateCapabilities(capabilities), // + () -> assertEmptyScalarFunctionCapabilities(capabilities), // + () -> assertEmptyAggregateFunctionCapatilities(capabilities)); + } + + @Test + void buildWithMainCapabilities() { + final MainCapability[] expectedCapabilities = { MainCapability.AGGREGATE_GROUP_BY_COLUMN, + MainCapability.AGGREGATE_GROUP_BY_EXPRESSION }; + final Capabilities capabilities = this.builder.addMain(expectedCapabilities).build(); + assertAll(() -> assertCapabilitesContainAllOf(capabilities, expectedCapabilities), // + () -> assertEmptyLiteralCapabilities(capabilities), // + () -> assertEmptyPredicateCapabilities(capabilities), // + () -> assertEmptyScalarFunctionCapabilities(capabilities), // + () -> assertEmptyAggregateFunctionCapatilities(capabilities)); + } + + @Test + void buildWithLiteralCapabilities() { + final LiteralCapability[] expectedCapabilities = { LiteralCapability.BOOL, LiteralCapability.DATE }; + final Capabilities capabilities = this.builder.addLiteral(expectedCapabilities).build(); + assertAll(() -> assertEmptyMainCapabilities(capabilities), // + () -> assertCapabilitesContainAllOf(capabilities, expectedCapabilities), // + () -> assertEmptyPredicateCapabilities(capabilities), // + () -> assertEmptyScalarFunctionCapabilities(capabilities), // + () -> assertEmptyAggregateFunctionCapatilities(capabilities)); + } + + @Test + void buildWithPredicateCapabilities() { + final PredicateCapability[] expectedCapabilities = { PredicateCapability.AND, PredicateCapability.BETWEEN }; + final Capabilities capabilities = this.builder.addPredicate(expectedCapabilities).build(); + assertAll(() -> assertEmptyMainCapabilities(capabilities), // + () -> assertEmptyLiteralCapabilities(capabilities), // + () -> assertCapabilitesContainAllOf(capabilities, expectedCapabilities), // + () -> assertEmptyScalarFunctionCapabilities(capabilities), // + () -> assertEmptyAggregateFunctionCapatilities(capabilities)); + } + + @Test + void buildWithScalarFunctionCapabilities() { + final ScalarFunctionCapability[] expectedCapabilities = { ScalarFunctionCapability.ABS, + ScalarFunctionCapability.ACOS }; + final Capabilities capabilities = this.builder.addScalarFunction(expectedCapabilities).build(); + assertAll(() -> assertEmptyMainCapabilities(capabilities), // + () -> assertEmptyLiteralCapabilities(capabilities), // + () -> assertEmptyPredicateCapabilities(capabilities), // + () -> assertCapabilitesContainAllOf(capabilities, expectedCapabilities), // + () -> assertEmptyAggregateFunctionCapatilities(capabilities)); + } + + @Test + void buildWithAggregateFunctionCapabilities() { + final AggregateFunctionCapability[] expectedCapabilities = { + AggregateFunctionCapability.APPROXIMATE_COUNT_DISTINCT, AggregateFunctionCapability.AVG }; + final Capabilities capabilities = this.builder.addAggregateFunction(expectedCapabilities).build(); + assertAll(() -> assertEmptyMainCapabilities(capabilities), // + () -> assertEmptyLiteralCapabilities(capabilities), // + () -> assertEmptyPredicateCapabilities(capabilities), // + () -> assertEmptyScalarFunctionCapabilities(capabilities), // + () -> assertCapabilitesContainAllOf(capabilities, expectedCapabilities)); + } +} \ No newline at end of file diff --git a/src/test/java/com/exasol/adapter/capabilities/CapabilityAssertions.java b/src/test/java/com/exasol/adapter/capabilities/CapabilityAssertions.java new file mode 100644 index 00000000..896c7660 --- /dev/null +++ b/src/test/java/com/exasol/adapter/capabilities/CapabilityAssertions.java @@ -0,0 +1,62 @@ +package com.exasol.adapter.capabilities; + +import static org.hamcrest.collection.IsEmptyIterable.emptyIterableOf; +import static org.hamcrest.collection.IsIterableContainingInAnyOrder.containsInAnyOrder; +import static org.junit.Assert.assertThat; + +public final class CapabilityAssertions { + private CapabilityAssertions() { + // prevent instantiation + } + + public static void assertEmptyMainCapabilities(final Capabilities capabilities) { + assertThat(capabilities.getMainCapabilities(), emptyIterableOf(MainCapability.class)); + } + + public static void assertEmptyLiteralCapabilities(final Capabilities capabilities) { + assertThat(capabilities.getLiteralCapabilities(), emptyIterableOf(LiteralCapability.class)); + } + + public static void assertEmptyPredicateCapabilities(final Capabilities capabilities) { + assertThat(capabilities.getPredicateCapabilities(), emptyIterableOf(PredicateCapability.class)); + } + + public static void assertEmptyScalarFunctionCapabilities(final Capabilities capabilities) { + assertThat(capabilities.getScalarFunctionCapabilities(), emptyIterableOf(ScalarFunctionCapability.class)); + } + + public static void assertEmptyAggregateFunctionCapatilities(final Capabilities capabilities) { + assertThat(capabilities.getAggregateFunctionCapabilities(), emptyIterableOf(AggregateFunctionCapability.class)); + } + + public static void assertCapabilitesContainAllOf(final Capabilities capabilities, + final MainCapability... expectedCapabilities) { + assertThat("Contains main capabilities", capabilities.getMainCapabilities(), + containsInAnyOrder(expectedCapabilities)); + } + + public static void assertCapabilitesContainAllOf(final Capabilities capabilities, + final LiteralCapability... expectedCapabilities) { + assertThat("Contains literal capabilities", capabilities.getLiteralCapabilities(), + containsInAnyOrder(expectedCapabilities)); + } + + public static void assertCapabilitesContainAllOf(final Capabilities capabilities, + final PredicateCapability... expectedCapabilities) { + assertThat("Contains predicate capabilities", capabilities.getPredicateCapabilities(), + containsInAnyOrder(expectedCapabilities)); + } + + public static void assertCapabilitesContainAllOf(final Capabilities capabilities, + final ScalarFunctionCapability... expectedCapabilities) { + assertThat("Contains scalar function capabilities", capabilities.getScalarFunctionCapabilities(), + containsInAnyOrder(expectedCapabilities)); + } + + public static void assertCapabilitesContainAllOf(final Capabilities capabilities, + final AggregateFunctionCapability... expectedCapabilities) { + assertThat("Contains aggregate function capabilities", capabilities.getAggregateFunctionCapabilities(), + containsInAnyOrder(expectedCapabilities)); + } + +} \ No newline at end of file diff --git a/src/test/java/com/exasol/adapter/json/ResponseJsonSerializerTest.java b/src/test/java/com/exasol/adapter/json/ResponseJsonSerializerTest.java index 723eb9db..dc21b367 100644 --- a/src/test/java/com/exasol/adapter/json/ResponseJsonSerializerTest.java +++ b/src/test/java/com/exasol/adapter/json/ResponseJsonSerializerTest.java @@ -1,75 +1,80 @@ package com.exasol.adapter.json; -import com.exasol.adapter.capabilities.*; -import com.exasol.adapter.metadata.SchemaMetadata; +import java.util.Collections; + import org.json.JSONException; import org.junit.jupiter.api.Test; import org.skyscreamer.jsonassert.JSONAssert; -import java.util.Collections; +import com.exasol.adapter.capabilities.*; +import com.exasol.adapter.metadata.SchemaMetadata; class ResponseJsonSerializerTest { @Test void testMakeCreateVirtualSchemaResponse() throws JSONException { JSONAssert.assertEquals("{\"type\":\"createVirtualSchema\"," // - + "\"schemaMetadata\":{\"tables\":[]," // - + "\"adapterNotes\":\"notes\"}}",// - ResponseJsonSerializer.makeCreateVirtualSchemaResponse( // - new SchemaMetadata("notes", // - Collections.emptyList())), false); + + "\"schemaMetadata\":{\"tables\":[]," // + + "\"adapterNotes\":\"notes\"}}", // + ResponseJsonSerializer.makeCreateVirtualSchemaResponse( // + new SchemaMetadata("notes", // + Collections.emptyList())), + false); } @Test void testMakeDropVirtualSchemaResponse() throws JSONException { JSONAssert.assertEquals("{\"type\":\"dropVirtualSchema\"}", - ResponseJsonSerializer.makeDropVirtualSchemaResponse(), false); + ResponseJsonSerializer.makeDropVirtualSchemaResponse(), false); } @Test void testMakeGetCapabilitiesResponse() throws JSONException { - final Capabilities capabilities = new Capabilities(); - capabilities.supportMainCapability(MainCapability.LIMIT); - capabilities.supportLiteral(LiteralCapability.DATE); - capabilities.supportPredicate(PredicateCapability.EQUAL); - capabilities.supportScalarFunction(ScalarFunctionCapability.ADD); - capabilities.supportAggregateFunction(AggregateFunctionCapability.AVG); + final Capabilities capabilities = Capabilities.builder() // + .addMain(MainCapability.LIMIT) // + .addLiteral(LiteralCapability.DATE) // + .addPredicate(PredicateCapability.EQUAL) // + .addScalarFunction(ScalarFunctionCapability.ADD) // + .addAggregateFunction(AggregateFunctionCapability.AVG) // + .build(); JSONAssert.assertEquals("{\"type\":\"getCapabilities\"," // - + "\"capabilities\":[\"LIMIT\"," // - + "\"LITERAL_DATE\"," // - + "\"FN_PRED_EQUAL\", " // - + "\"FN_AGG_AVG\", " // - + "\"FN_ADD\"]}", // - ResponseJsonSerializer.makeGetCapabilitiesResponse(capabilities), false); + + "\"capabilities\":[\"LIMIT\"," // + + "\"LITERAL_DATE\"," // + + "\"FN_PRED_EQUAL\", " // + + "\"FN_AGG_AVG\", " // + + "\"FN_ADD\"]}", // + ResponseJsonSerializer.makeGetCapabilitiesResponse(capabilities), false); } @Test void testMakePushdownResponse() throws JSONException { JSONAssert.assertEquals("{\"type\":\"pushdown\"," // - + "\"sql\":\"PUSH DOWN\"}", // - ResponseJsonSerializer.makePushdownResponse("PUSH DOWN"), false); + + "\"sql\":\"PUSH DOWN\"}", // + ResponseJsonSerializer.makePushdownResponse("PUSH DOWN"), false); } @Test void testMakeSetPropertiesResponse() throws JSONException { JSONAssert.assertEquals("{\"type\":\"setProperties\"," // - + "\"schemaMetadata\":{\"tables\":[]," // - + "\"adapterNotes\":\"notes\"}}", // - ResponseJsonSerializer.makeSetPropertiesResponse(new SchemaMetadata("notes", // - Collections.emptyList())), false); + + "\"schemaMetadata\":{\"tables\":[]," // + + "\"adapterNotes\":\"notes\"}}", // + ResponseJsonSerializer.makeSetPropertiesResponse(new SchemaMetadata("notes", // + Collections.emptyList())), + false); } @Test void testMakeSetPropertiesResponseWhenMetadataIsNull() throws JSONException { - JSONAssert.assertEquals("{\"type\":\"setProperties\"}", - ResponseJsonSerializer.makeSetPropertiesResponse(null), false); + JSONAssert.assertEquals("{\"type\":\"setProperties\"}", ResponseJsonSerializer.makeSetPropertiesResponse(null), + false); } @Test void testMakeRefreshResponse() throws JSONException { JSONAssert.assertEquals("{\"type\":\"refresh\"," // - + "\"schemaMetadata\":{\"tables\":[]," // - + "\"adapterNotes\":\"notes\"}}", // - ResponseJsonSerializer.makeRefreshResponse( // - new SchemaMetadata("notes", Collections.emptyList())), false); + + "\"schemaMetadata\":{\"tables\":[]," // + + "\"adapterNotes\":\"notes\"}}", // + ResponseJsonSerializer.makeRefreshResponse( // + new SchemaMetadata("notes", Collections.emptyList())), + false); } } \ No newline at end of file