You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@beam.apache.org by bc...@apache.org on 2016/04/08 18:58:37 UTC
[1/2] incubator-beam git commit: Additional APIs for registering
DisplayData
Repository: incubator-beam
Updated Branches:
refs/heads/master d827e1b96 -> 245cffa5e
Additional APIs for registering DisplayData
- Support overriding namespace when adding display data
- Support inferring display data type during registration
- Add APIs to conditionally register display data if not null/default.
- Move additional matchers into DisplayDataMatchers
Project: http://git-wip-us.apache.org/repos/asf/incubator-beam/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-beam/commit/5da22920
Tree: http://git-wip-us.apache.org/repos/asf/incubator-beam/tree/5da22920
Diff: http://git-wip-us.apache.org/repos/asf/incubator-beam/diff/5da22920
Branch: refs/heads/master
Commit: 5da22920c7c5d20695d4309429341c39af760044
Parents: d827e1b
Author: Scott Wegner <sw...@google.com>
Authored: Tue Apr 5 11:42:30 2016 -0700
Committer: bchambers <bc...@google.com>
Committed: Fri Apr 8 09:46:07 2016 -0700
----------------------------------------------------------------------
.../sdk/transforms/display/DisplayData.java | 306 +++++++++++++++++--
.../sdk/transforms/display/HasDisplayData.java | 1 +
.../transforms/display/DisplayDataMatchers.java | 43 +++
.../display/DisplayDataMatchersTest.java | 34 ++-
.../sdk/transforms/display/DisplayDataTest.java | 206 ++++++++++---
5 files changed, 517 insertions(+), 73 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-beam/blob/5da22920/sdks/java/core/src/main/java/com/google/cloud/dataflow/sdk/transforms/display/DisplayData.java
----------------------------------------------------------------------
diff --git a/sdks/java/core/src/main/java/com/google/cloud/dataflow/sdk/transforms/display/DisplayData.java b/sdks/java/core/src/main/java/com/google/cloud/dataflow/sdk/transforms/display/DisplayData.java
index 63ec7fc..d23fc0b 100644
--- a/sdks/java/core/src/main/java/com/google/cloud/dataflow/sdk/transforms/display/DisplayData.java
+++ b/sdks/java/core/src/main/java/com/google/cloud/dataflow/sdk/transforms/display/DisplayData.java
@@ -76,6 +76,30 @@ public class DisplayData {
return InternalBuilder.forRoot(component).build();
}
+ /**
+ * Infer the {@link Type} for the given object.
+ *
+ * <p>Use this method if the type of metadata is not known at compile time. For example:
+ *
+ * <pre>
+ * {@code
+ * @Override
+ * public void populateDisplayData(DisplayData.Builder builder) {
+ * Optional<DisplayData.Type> type = DisplayData.inferType(foo);
+ * if (type.isPresent()) {
+ * builder.add("foo", type.get(), foo);
+ * }
+ * }
+ * }
+ * </pre>
+ *
+ * @return The inferred {@link Type}, or null if the type cannot be inferred,
+ */
+ @Nullable
+ public static Type inferType(@Nullable Object value) {
+ return Type.tryInferFrom(value);
+ }
+
public Collection<Item> items() {
return entries.values();
}
@@ -101,6 +125,10 @@ public class DisplayData {
return builder.toString();
}
+ private static String namespaceOf(Class<?> clazz) {
+ return clazz.getName();
+ }
+
/**
* Utility to build up display metadata from a component and its included
* subcomponents.
@@ -127,6 +155,20 @@ public class DisplayData {
ItemBuilder add(String key, String value);
/**
+ * Register the given string display data if the value is not null.
+ *
+ * @see DisplayData.Builder#add(String, String)
+ */
+ ItemBuilder addIfNotNull(String key, @Nullable String value);
+
+ /**
+ * Register the given string display data if the value is different than the specified default.
+ *
+ * @see DisplayData.Builder#add(String, String)
+ */
+ ItemBuilder addIfNotDefault(String key, @Nullable String value, @Nullable String defaultValue);
+
+ /**
* Register the given numeric display metadata. The metadata item will be registered with type
* {@link DisplayData.Type#INTEGER}, and is identified by the specified key and namespace from
* the current transform or component.
@@ -134,6 +176,13 @@ public class DisplayData {
ItemBuilder add(String key, long value);
/**
+ * Register the given numeric display data if the value is different than the specified default.
+ *
+ * @see DisplayData.Builder#add(String, long)
+ */
+ ItemBuilder addIfNotDefault(String key, long value, long defaultValue);
+
+ /**
* Register the given floating point display metadata. The metadata item will be registered with
* type {@link DisplayData.Type#FLOAT}, and is identified by the specified key and namespace
* from the current transform or component.
@@ -141,13 +190,28 @@ public class DisplayData {
ItemBuilder add(String key, double value);
/**
- * Register the given floating point display metadata. The metadata item will be registered with
+ * Register the given floating point display data if the value is different than the specified
+ * default.
+ *
+ * @see DisplayData.Builder#add(String, double)
+ */
+ ItemBuilder addIfNotDefault(String key, double value, double defaultValue);
+
+ /**
+ * Register the given boolean display metadata. The metadata item will be registered with
* type {@link DisplayData.Type#BOOLEAN}, and is identified by the specified key and namespace
* from the current transform or component.
*/
ItemBuilder add(String key, boolean value);
/**
+ * Register the given boolean display data if the value is different than the specified default.
+ *
+ * @see DisplayData.Builder#add(String, boolean)
+ */
+ ItemBuilder addIfNotDefault(String key, boolean value, boolean defaultValue);
+
+ /**
* Register the given timestamp display metadata. The metadata item will be registered with type
* {@link DisplayData.Type#TIMESTAMP}, and is identified by the specified key and namespace from
* the current transform or component.
@@ -155,6 +219,22 @@ public class DisplayData {
ItemBuilder add(String key, Instant value);
/**
+ * Register the given timestamp display data if the value is not null.
+ *
+ * @see DisplayData.Builder#add(String, Instant)
+ */
+ ItemBuilder addIfNotNull(String key, @Nullable Instant value);
+
+ /**
+ * Register the given timestamp display data if the value is different than the specified
+ * default.
+ *
+ * @see DisplayData.Builder#add(String, Instant)
+ */
+ ItemBuilder addIfNotDefault(
+ String key, @Nullable Instant value, @Nullable Instant defaultValue);
+
+ /**
* Register the given duration display metadata. The metadata item will be registered with type
* {@link DisplayData.Type#DURATION}, and is identified by the specified key and namespace from
* the current transform or component.
@@ -162,11 +242,53 @@ public class DisplayData {
ItemBuilder add(String key, Duration value);
/**
+ * Register the given duration display data if the value is not null.
+ *
+ * @see DisplayData.Builder#add(String, Duration)
+ */
+ ItemBuilder addIfNotNull(String key, @Nullable Duration value);
+
+ /**
+ * Register the given duration display data if the value is different than the specified
+ * default.
+ *
+ * @see DisplayData.Builder#add(String, Duration)
+ */
+ ItemBuilder addIfNotDefault(
+ String key, @Nullable Duration value, @Nullable Duration defaultValue);
+
+ /**
* Register the given class display metadata. The metadata item will be registered with type
* {@link DisplayData.Type#JAVA_CLASS}, and is identified by the specified key and namespace
* from the current transform or component.
*/
ItemBuilder add(String key, Class<?> value);
+
+ /**
+ * Register the given class display data if the value is not null.
+ *
+ * @see DisplayData.Builder#add(String, Class)
+ */
+ ItemBuilder addIfNotNull(String key, @Nullable Class<?> value);
+
+ /**
+ * Register the given class display data if the value is different than the specified default.
+ *
+ * @see DisplayData.Builder#add(String, Class)
+ */
+ ItemBuilder addIfNotDefault(
+ String key, @Nullable Class<?> value, @Nullable Class<?> defaultValue);
+
+ /**
+ * Register the given display metadata with the specified type.
+ *
+ * <p> The added display data is identified by the specified key and namespace from the current
+ * transform or component.
+ *
+ * @throws ClassCastException if the value cannot be safely cast to the specified type.
+ * @see DisplayData#inferType(Object)
+ */
+ ItemBuilder add(String key, Type type, Object value);
}
/**
@@ -190,6 +312,14 @@ public class DisplayData {
* <p>Specifying a null value will clear the URL if it was previously defined.
*/
ItemBuilder withLinkUrl(@Nullable String url);
+
+ /**
+ * Adds an explicit namespace to the most-recently added display metadata. The namespace
+ * and key uniquely identify the display metadata.
+ *
+ * <p>Leaving the namespace unspecified will default to the registering instance's class.
+ */
+ ItemBuilder withNamespace(Class<?> namespace);
}
/**
@@ -207,10 +337,11 @@ public class DisplayData {
private final String label;
private final String url;
- private static <T> Item create(String namespace, String key, Type type, T value) {
+ private static Item create(Class<?> nsClass, String key, Type type, Object value) {
FormattedItemValue formatted = type.format(value);
+ String namespace = namespaceOf(nsClass);
return new Item(
- namespace, key, type, formatted.getLongValue(), formatted.getShortValue(), null, null);
+ namespace, key, type, formatted.getLongValue(), formatted.getShortValue(), null, null);
}
private Item(
@@ -337,6 +468,12 @@ public class DisplayData {
private Item withUrl(String url) {
return new Item(this.ns, this.key, this.type, this.value, this.shortValue, url, this.label);
}
+
+ private Item withNamespace(Class<?> nsClass) {
+ String namespace = namespaceOf(nsClass);
+ return new Item(
+ namespace, this.key, this.type, this.value, this.shortValue, this.url, this.label);
+ }
}
/**
@@ -354,7 +491,7 @@ public class DisplayData {
private final String key;
public static Identifier of(Class<?> namespace, String key) {
- return of(namespace.getName(), key);
+ return of(namespaceOf(namespace), key);
}
public static Identifier of(String namespace, String key) {
@@ -403,13 +540,14 @@ public class DisplayData {
STRING {
@Override
FormattedItemValue format(Object value) {
- return new FormattedItemValue((String) value);
+ return new FormattedItemValue(value.toString());
}
},
INTEGER {
@Override
FormattedItemValue format(Object value) {
- return new FormattedItemValue(Long.toString((long) value));
+ Number number = (Number) value;
+ return new FormattedItemValue(Long.toString(number.longValue()));
}
},
FLOAT {
@@ -451,6 +589,28 @@ public class DisplayData {
* <p>Internal-only. Value objects can be safely cast to the expected Java type.
*/
abstract FormattedItemValue format(Object value);
+
+ @Nullable
+ private static Type tryInferFrom(@Nullable Object value) {
+ Type type;
+ if (value instanceof Integer || value instanceof Long) {
+ return INTEGER;
+ } else if (value instanceof Double || value instanceof Float) {
+ return FLOAT;
+ } else if (value instanceof Boolean) {
+ return BOOLEAN;
+ } else if (value instanceof Instant) {
+ return TIMESTAMP;
+ } else if (value instanceof Duration) {
+ return DURATION;
+ } else if (value instanceof Class<?>) {
+ return JAVA_CLASS;
+ } else if (value instanceof String) {
+ return STRING;
+ } else {
+ return null;
+ }
+ }
}
static class FormattedItemValue {
@@ -480,8 +640,9 @@ public class DisplayData {
private final Set<Object> visited;
private Class<?> latestNs;
+
+ @Nullable
private Item latestItem;
- private Identifier latestIdentifier;
private InternalBuilder() {
this.entries = Maps.newHashMap();
@@ -503,6 +664,9 @@ public class DisplayData {
@Override
public Builder include(HasDisplayData subComponent, Class<?> namespace) {
checkNotNull(subComponent);
+ checkNotNull(namespace);
+
+ commitLatest();
boolean newComponent = visited.add(subComponent);
if (newComponent) {
Class prevNs = this.latestNs;
@@ -517,75 +681,165 @@ public class DisplayData {
@Override
public ItemBuilder add(String key, String value) {
checkNotNull(value);
- return addItem(key, Type.STRING, value);
+ return addItemIf(true, key, Type.STRING, value);
+ }
+
+ @Override
+ public ItemBuilder addIfNotNull(String key, @Nullable String value) {
+ return addItemIf(value != null, key, Type.STRING, value);
+ }
+
+ @Override
+ public ItemBuilder addIfNotDefault(
+ String key, @Nullable String value, @Nullable String defaultValue) {
+ return addItemIf(!Objects.equals(value, defaultValue), key, Type.STRING, value);
}
@Override
public ItemBuilder add(String key, long value) {
- return addItem(key, Type.INTEGER, value);
+ return addItemIf(true, key, Type.INTEGER, value);
+ }
+
+ @Override
+ public ItemBuilder addIfNotDefault(String key, long value, long defaultValue) {
+ return addItemIf(value != defaultValue, key, Type.INTEGER, value);
}
@Override
public ItemBuilder add(String key, double value) {
- return addItem(key, Type.FLOAT, value);
+ return addItemIf(true, key, Type.FLOAT, value);
+ }
+
+ @Override
+ public ItemBuilder addIfNotDefault(String key, double value, double defaultValue) {
+ return addItemIf(value != defaultValue, key, Type.FLOAT, value);
}
@Override
public ItemBuilder add(String key, boolean value) {
- return addItem(key, Type.BOOLEAN, value);
+ return addItemIf(true, key, Type.BOOLEAN, value);
+ }
+
+ @Override
+ public ItemBuilder addIfNotDefault(String key, boolean value, boolean defaultValue) {
+ return addItemIf(value != defaultValue, key, Type.BOOLEAN, value);
}
@Override
public ItemBuilder add(String key, Instant value) {
checkNotNull(value);
- return addItem(key, Type.TIMESTAMP, value);
+ return addItemIf(true, key, Type.TIMESTAMP, value);
+ }
+
+ @Override
+ public ItemBuilder addIfNotNull(String key, @Nullable Instant value) {
+ return addItemIf(value != null, key, Type.TIMESTAMP, value);
+ }
+
+ @Override
+ public ItemBuilder addIfNotDefault(
+ String key, @Nullable Instant value, @Nullable Instant defaultValue) {
+ return addItemIf(!Objects.equals(value, defaultValue), key, Type.TIMESTAMP, value);
}
@Override
public ItemBuilder add(String key, Duration value) {
checkNotNull(value);
- return addItem(key, Type.DURATION, value);
+ return addItemIf(true, key, Type.DURATION, value);
+ }
+
+ @Override
+ public ItemBuilder addIfNotNull(String key, @Nullable Duration value) {
+ return addItemIf(value != null, key, Type.DURATION, value);
+ }
+
+ @Override
+ public ItemBuilder addIfNotDefault(
+ String key, @Nullable Duration value, @Nullable Duration defaultValue) {
+ return addItemIf(!Objects.equals(value, defaultValue), key, Type.DURATION, value);
}
@Override
public ItemBuilder add(String key, Class<?> value) {
checkNotNull(value);
- return addItem(key, Type.JAVA_CLASS, value);
+ return addItemIf(true, key, Type.JAVA_CLASS, value);
+ }
+
+ @Override
+ public ItemBuilder addIfNotNull(String key, @Nullable Class<?> value) {
+ return addItemIf(value != null, key, Type.JAVA_CLASS, value);
+ }
+
+ @Override
+ public ItemBuilder addIfNotDefault(
+ String key, @Nullable Class<?> value, @Nullable Class<?> defaultValue) {
+ return addItemIf(!Objects.equals(value, defaultValue), key, Type.JAVA_CLASS, value);
+ }
+
+ @Override
+ public ItemBuilder add(String key, Type type, Object value) {
+ checkNotNull(value);
+ checkNotNull(type);
+ return addItemIf(true, key, type, value);
}
- private <T> ItemBuilder addItem(String key, Type type, T value) {
+ private ItemBuilder addItemIf(boolean condition, String key, Type type, Object value) {
checkNotNull(key);
checkArgument(!key.isEmpty());
- Identifier id = Identifier.of(latestNs, key);
+ commitLatest();
+ if (condition) {
+ latestItem = Item.create(latestNs, key, type, value);
+ }
+
+ return this;
+ }
+
+ private void commitLatest() {
+ if (latestItem == null) {
+ return;
+ }
+
+ Identifier id = Identifier.of(latestItem.getNamespace(), latestItem.getKey());
if (entries.containsKey(id)) {
throw new IllegalArgumentException("DisplayData key already exists. All display data "
+ "for a component must be registered with a unique key.\nKey: " + id);
}
- Item item = Item.create(id.getNamespace(), key, type, value);
- entries.put(id, item);
- latestItem = item;
- latestIdentifier = id;
+ entries.put(id, latestItem);
+ latestItem = null;
+ }
+
+ @Override
+ public ItemBuilder withLabel(@Nullable String label) {
+ if (latestItem != null) {
+ latestItem = latestItem.withLabel(label);
+ }
return this;
}
@Override
- public ItemBuilder withLabel(String label) {
- latestItem = latestItem.withLabel(label);
- entries.put(latestIdentifier, latestItem);
+ public ItemBuilder withLinkUrl(@Nullable String url) {
+ if (latestItem != null) {
+ latestItem = latestItem.withUrl(url);
+ }
+
return this;
}
@Override
- public ItemBuilder withLinkUrl(String url) {
- latestItem = latestItem.withUrl(url);
- entries.put(latestIdentifier, latestItem);
+ public ItemBuilder withNamespace(Class<?> namespace) {
+ checkNotNull(namespace);
+ if (latestItem != null) {
+ latestItem = latestItem.withNamespace(namespace);
+ }
+
return this;
}
private DisplayData build() {
+ commitLatest();
return new DisplayData(this.entries);
}
}
http://git-wip-us.apache.org/repos/asf/incubator-beam/blob/5da22920/sdks/java/core/src/main/java/com/google/cloud/dataflow/sdk/transforms/display/HasDisplayData.java
----------------------------------------------------------------------
diff --git a/sdks/java/core/src/main/java/com/google/cloud/dataflow/sdk/transforms/display/HasDisplayData.java b/sdks/java/core/src/main/java/com/google/cloud/dataflow/sdk/transforms/display/HasDisplayData.java
index 6ec1eca..36f7a31 100644
--- a/sdks/java/core/src/main/java/com/google/cloud/dataflow/sdk/transforms/display/HasDisplayData.java
+++ b/sdks/java/core/src/main/java/com/google/cloud/dataflow/sdk/transforms/display/HasDisplayData.java
@@ -40,6 +40,7 @@ public interface HasDisplayData {
* builder
* .include(subComponent)
* .add("minFilter", 42)
+ * .addIfNotDefault("useTransactions", this.txn, false)
* .add("topic", "projects/myproject/topics/mytopic")
* .withLabel("Pub/Sub Topic")
* .add("serviceInstance", "myservice.com/fizzbang")
http://git-wip-us.apache.org/repos/asf/incubator-beam/blob/5da22920/sdks/java/core/src/test/java/com/google/cloud/dataflow/sdk/transforms/display/DisplayDataMatchers.java
----------------------------------------------------------------------
diff --git a/sdks/java/core/src/test/java/com/google/cloud/dataflow/sdk/transforms/display/DisplayDataMatchers.java b/sdks/java/core/src/test/java/com/google/cloud/dataflow/sdk/transforms/display/DisplayDataMatchers.java
index d540b4b..2832414 100644
--- a/sdks/java/core/src/test/java/com/google/cloud/dataflow/sdk/transforms/display/DisplayDataMatchers.java
+++ b/sdks/java/core/src/test/java/com/google/cloud/dataflow/sdk/transforms/display/DisplayDataMatchers.java
@@ -248,10 +248,44 @@ public class DisplayDataMatchers {
};
}
+ /**
+ * Creates a matcher that matches if the examined {@link DisplayData.Item} contains the
+ * specified namespace.
+ */
+ public static Matcher<DisplayData.Item> hasNamespace(Class<?> namespace) {
+ return hasNamespace(Matchers.<Class<?>>is(namespace));
+ }
+
+ /**
+ * Creates a matcher that matches if the examined {@link DisplayData.Item} contains a namespace
+ * matching the specified namespace matcher.
+ */
+ public static Matcher<DisplayData.Item> hasNamespace(Matcher<Class<?>> namespaceMatcher) {
+ return new FeatureMatcher<DisplayData.Item, Class<?>>(
+ namespaceMatcher, "display item with namespace", "namespace") {
+ @Override
+ protected Class<?> featureValueOf(DisplayData.Item actual) {
+ try {
+ return Class.forName(actual.getNamespace());
+ } catch (ClassNotFoundException e) {
+ return null;
+ }
+ }
+ };
+ }
+
+ /**
+ * Creates a matcher that matches if the examined {@link DisplayData.Item} matches the
+ * specified type.
+ */
public static Matcher<DisplayData.Item> hasType(DisplayData.Type type) {
return hasType(Matchers.is(type));
}
+ /**
+ * Creates a matcher that matches if the examined {@link DisplayData.Item} has a type
+ * matching the specified type matcher.
+ */
public static Matcher<DisplayData.Item> hasType(Matcher<DisplayData.Type> typeMatcher) {
return new FeatureMatcher<DisplayData.Item, DisplayData.Type>(
typeMatcher, "with type", "type") {
@@ -262,10 +296,19 @@ public class DisplayDataMatchers {
};
}
+ /**
+ * Creates a matcher that matches if the examined {@link DisplayData.Item} has the specified
+ * value.
+ */
+
public static Matcher<DisplayData.Item> hasValue(String value) {
return hasValue(Matchers.is(value));
}
+ /**
+ * Creates a matcher that matches if the examined {@link DisplayData.Item} contains a value
+ * matching the specified value matcher.
+ */
public static Matcher<DisplayData.Item> hasValue(Matcher<String> valueMatcher) {
return new FeatureMatcher<DisplayData.Item, String>(
valueMatcher, "with value", "value") {
http://git-wip-us.apache.org/repos/asf/incubator-beam/blob/5da22920/sdks/java/core/src/test/java/com/google/cloud/dataflow/sdk/transforms/display/DisplayDataMatchersTest.java
----------------------------------------------------------------------
diff --git a/sdks/java/core/src/test/java/com/google/cloud/dataflow/sdk/transforms/display/DisplayDataMatchersTest.java b/sdks/java/core/src/test/java/com/google/cloud/dataflow/sdk/transforms/display/DisplayDataMatchersTest.java
index 1b43ff7..f24288e 100644
--- a/sdks/java/core/src/test/java/com/google/cloud/dataflow/sdk/transforms/display/DisplayDataMatchersTest.java
+++ b/sdks/java/core/src/test/java/com/google/cloud/dataflow/sdk/transforms/display/DisplayDataMatchersTest.java
@@ -19,6 +19,7 @@ package com.google.cloud.dataflow.sdk.transforms.display;
import static com.google.cloud.dataflow.sdk.transforms.display.DisplayDataMatchers.hasDisplayItem;
import static com.google.cloud.dataflow.sdk.transforms.display.DisplayDataMatchers.hasKey;
+import static com.google.cloud.dataflow.sdk.transforms.display.DisplayDataMatchers.hasNamespace;
import static com.google.cloud.dataflow.sdk.transforms.display.DisplayDataMatchers.hasType;
import static com.google.cloud.dataflow.sdk.transforms.display.DisplayDataMatchers.hasValue;
import static com.google.cloud.dataflow.sdk.transforms.display.DisplayDataMatchers.includes;
@@ -97,6 +98,15 @@ public class DisplayDataMatchersTest {
}
@Test
+ public void testHasNamespace() {
+ Matcher<DisplayData> matcher = hasDisplayItem(hasNamespace(SampleTransform.class));
+
+ assertFalse(matcher.matches(DisplayData.from(
+ new PTransform<PCollection<String>, PCollection<String>>(){})));
+ assertThat(createDisplayDataWithItem("foo", "bar"), matcher);
+ }
+
+ @Test
public void testIncludes() {
final HasDisplayData subComponent = new HasDisplayData() {
@Override
@@ -125,13 +135,23 @@ public class DisplayDataMatchersTest {
assertThat(DisplayData.from(subComponent), matcher);
}
+
private DisplayData createDisplayDataWithItem(final String key, final String value) {
- return DisplayData.from(
- new PTransform<PCollection<String>, PCollection<String>>() {
- @Override
- public void populateDisplayData(Builder builder) {
- builder.add(key, value);
- }
- });
+ return DisplayData.from(new SampleTransform(key, value));
+ }
+
+ static class SampleTransform extends PTransform<PCollection<String>, PCollection<String>> {
+ private final String key;
+ private final String value;
+
+ SampleTransform(String key, String value) {
+ this.key = key;
+ this.value = value;
+ }
+
+ @Override
+ public void populateDisplayData(Builder builder) {
+ builder.add(key, value);
+ }
}
}
http://git-wip-us.apache.org/repos/asf/incubator-beam/blob/5da22920/sdks/java/core/src/test/java/com/google/cloud/dataflow/sdk/transforms/display/DisplayDataTest.java
----------------------------------------------------------------------
diff --git a/sdks/java/core/src/test/java/com/google/cloud/dataflow/sdk/transforms/display/DisplayDataTest.java b/sdks/java/core/src/test/java/com/google/cloud/dataflow/sdk/transforms/display/DisplayDataTest.java
index 4d75e26..9f8d509 100644
--- a/sdks/java/core/src/test/java/com/google/cloud/dataflow/sdk/transforms/display/DisplayDataTest.java
+++ b/sdks/java/core/src/test/java/com/google/cloud/dataflow/sdk/transforms/display/DisplayDataTest.java
@@ -19,6 +19,7 @@ package com.google.cloud.dataflow.sdk.transforms.display;
import static com.google.cloud.dataflow.sdk.transforms.display.DisplayDataMatchers.hasDisplayItem;
import static com.google.cloud.dataflow.sdk.transforms.display.DisplayDataMatchers.hasKey;
+import static com.google.cloud.dataflow.sdk.transforms.display.DisplayDataMatchers.hasNamespace;
import static com.google.cloud.dataflow.sdk.transforms.display.DisplayDataMatchers.hasType;
import static com.google.cloud.dataflow.sdk.transforms.display.DisplayDataMatchers.hasValue;
import static com.google.cloud.dataflow.sdk.transforms.display.DisplayDataMatchers.includes;
@@ -86,20 +87,25 @@ public class DisplayDataTest {
}
};
+
PTransform<?, ?> transform =
new PTransform<PCollection<String>, PCollection<String>>() {
+ final Instant defaultStartTime = new Instant(0);
+ Instant startTime = defaultStartTime;
+
@Override
public void populateDisplayData(DisplayData.Builder builder) {
builder
.include(subComponent1)
.include(subComponent2)
- .add("MinSproggles", 200)
- .withLabel("Mimimum Required Sproggles")
- .add("FireLazers", true)
- .add("TimeBomb", Instant.now().plus(Duration.standardDays(1)))
- .add("FilterLogic", subComponent1.getClass())
- .add("ServiceUrl", "google.com/fizzbang")
- .withLinkUrl("http://www.google.com/fizzbang");
+ .add("minSproggles", 200)
+ .withLabel("Mimimum Required Sproggles")
+ .add("fireLazers", true)
+ .addIfNotDefault("startTime", startTime, defaultStartTime)
+ .add("timeBomb", Instant.now().plus(Duration.standardDays(1)))
+ .add("filterLogic", subComponent1.getClass())
+ .add("serviceUrl", "google.com/fizzbang")
+ .withLinkUrl("http://www.google.com/fizzbang");
}
};
@@ -158,14 +164,22 @@ public class DisplayDataTest {
@Test
public void testItemProperties() {
final Instant value = Instant.now();
- DisplayData data = DisplayData.from(new ConcreteComponent(value));
+ DisplayData data = DisplayData.from(new HasDisplayData() {
+ @Override
+ public void populateDisplayData(DisplayData.Builder builder) {
+ builder.add("now", value)
+ .withLabel("the current instant")
+ .withLinkUrl("http://time.gov")
+ .withNamespace(DisplayDataTest.class);
+ }
+ });
@SuppressWarnings("unchecked")
DisplayData.Item item = (DisplayData.Item) data.items().toArray()[0];
assertThat(
item,
- allOf(
- hasNamespace(Matchers.<Class<?>>is(ConcreteComponent.class)),
+ Matchers.allOf(
+ hasNamespace(DisplayDataTest.class),
hasKey("now"),
hasType(DisplayData.Type.TIMESTAMP),
hasValue(ISO_FORMATTER.print(value)),
@@ -174,19 +188,6 @@ public class DisplayDataTest {
hasUrl(is("http://time.gov"))));
}
- static class ConcreteComponent implements HasDisplayData {
- private Instant value;
-
- ConcreteComponent(Instant value) {
- this.value = value;
- }
-
- @Override
- public void populateDisplayData(DisplayData.Builder builder) {
- builder.add("now", value).withLabel("the current instant").withLinkUrl("http://time.gov");
- }
- }
-
@Test
public void testUnspecifiedOptionalProperties() {
DisplayData data =
@@ -204,6 +205,54 @@ public class DisplayDataTest {
}
@Test
+ public void testAddIfNotDefault() {
+ final int defaultValue = 10;
+
+ DisplayData data = DisplayData.from(new HasDisplayData() {
+ @Override
+ public void populateDisplayData(Builder builder) {
+ builder
+ .addIfNotDefault("isDefault", defaultValue, defaultValue)
+ .addIfNotDefault("notDefault", defaultValue + 1, defaultValue);
+ }
+ });
+
+ assertThat(data, not(hasDisplayItem(hasKey("isDefault"))));
+ assertThat(data, hasDisplayItem("notDefault", defaultValue + 1));
+ }
+
+ @Test
+ public void testAddIfNotNull() {
+ DisplayData data = DisplayData.from(new HasDisplayData() {
+ @Override
+ public void populateDisplayData(Builder builder) {
+ builder
+ .addIfNotNull("isNull", (Class<?>) null)
+ .addIfNotNull("notNull", DisplayDataTest.class);
+ }
+ });
+
+ assertThat(data, not(hasDisplayItem(hasKey("isNull"))));
+ assertThat(data, hasDisplayItem(hasKey("notNull")));
+ }
+
+ @Test
+ public void testModifyingConditionalItemIsSafe() {
+ HasDisplayData component = new HasDisplayData() {
+ @Override
+ public void populateDisplayData(Builder builder) {
+ builder.addIfNotNull("nullItem", (Class<?>) null)
+ .withLinkUrl("http://abc")
+ .withNamespace(DisplayDataTest.class)
+ .withLabel("Null item shoudl be safe");
+ }
+ };
+
+ DisplayData.from(component); // should not throw
+ }
+
+
+ @Test
public void testIncludes() {
final HasDisplayData subComponent =
new HasDisplayData() {
@@ -250,6 +299,18 @@ public class DisplayDataTest {
assertThat(data, includes(subComponent, namespaceOverride.getClass()));
}
+ @Test
+ public void testNullNamespaceOverride() {
+ thrown.expect(NullPointerException.class);
+
+ DisplayData.from(new HasDisplayData() {
+ @Override
+ public void populateDisplayData(Builder builder) {
+ builder.add("foo", "bar")
+ .withNamespace(null);
+ }
+ });
+ }
@Test
public void testIdentifierEquality() {
@@ -350,6 +411,22 @@ public class DisplayDataTest {
}
@Test
+ public void testDuplicateKeyWithNamespaceOverrideDoesntThrow() {
+ DisplayData displayData = DisplayData.from(
+ new HasDisplayData() {
+ @Override
+ public void populateDisplayData(DisplayData.Builder builder) {
+ builder
+ .add("foo", "bar")
+ .add("foo", "baz")
+ .withNamespace(DisplayDataTest.class);
+ }
+ });
+
+ assertThat(displayData.items(), hasSize(2));
+ }
+
+ @Test
public void testToString() {
HasDisplayData component = new HasDisplayData() {
@Override
@@ -428,6 +505,7 @@ public class DisplayDataTest {
}
@Override
+ @SuppressWarnings("EqualsWhichDoesntCheckParameterClass")
public boolean equals(Object obj) {
return true;
}
@@ -480,6 +558,50 @@ public class DisplayDataTest {
}
@Test
+ public void testExplicitItemType() {
+ DisplayData data = DisplayData.from(new HasDisplayData() {
+ @Override
+ public void populateDisplayData(Builder builder) {
+ builder
+ .add("integer", DisplayData.Type.INTEGER, 1234)
+ .add("string", DisplayData.Type.STRING, "foobar");
+ }
+ });
+
+ assertThat(data, hasDisplayItem("integer", 1234));
+ assertThat(data, hasDisplayItem("string", "foobar"));
+ }
+
+ @Test
+ public void testInvalidExplicitItemType() {
+ HasDisplayData component = new HasDisplayData() {
+ @Override
+ public void populateDisplayData(Builder builder) {
+ builder.add("integer", DisplayData.Type.INTEGER, "foobar");
+ }
+ };
+
+ thrown.expect(ClassCastException.class);
+ DisplayData.from(component);
+ }
+
+ @Test
+ public void testKnownTypeInference() {
+ assertEquals(DisplayData.Type.INTEGER, DisplayData.inferType(1234));
+ assertEquals(DisplayData.Type.INTEGER, DisplayData.inferType(1234L));
+ assertEquals(DisplayData.Type.FLOAT, DisplayData.inferType(12.3));
+ assertEquals(DisplayData.Type.FLOAT, DisplayData.inferType(12.3f));
+ assertEquals(DisplayData.Type.BOOLEAN, DisplayData.inferType(true));
+ assertEquals(DisplayData.Type.TIMESTAMP, DisplayData.inferType(Instant.now()));
+ assertEquals(DisplayData.Type.DURATION, DisplayData.inferType(Duration.millis(1234)));
+ assertEquals(DisplayData.Type.JAVA_CLASS, DisplayData.inferType(DisplayDataTest.class));
+ assertEquals(DisplayData.Type.STRING, DisplayData.inferType("hello world"));
+
+ assertEquals(null, DisplayData.inferType(null));
+ assertEquals(null, DisplayData.inferType(new Object() {}));
+ }
+
+ @Test
public void testStringFormatting() throws IOException {
final Instant now = Instant.now();
final Duration oneHour = Duration.standardHours(1);
@@ -534,7 +656,7 @@ public class DisplayDataTest {
hasItem(
allOf(
hasKey("alpha"),
- hasNamespace(Matchers.<Class<?>>is(component.getClass())))));
+ hasNamespace(component.getClass()))));
}
@Test
@@ -556,6 +678,23 @@ public class DisplayDataTest {
}
@Test
+ public void testIncludeNullNamespace() {
+ final HasDisplayData subComponent = new HasDisplayData() {
+ @Override
+ public void populateDisplayData(Builder builder) {
+ }
+ };
+
+ thrown.expect(NullPointerException.class);
+ DisplayData.from(new HasDisplayData() {
+ @Override
+ public void populateDisplayData(Builder builder) {
+ builder.include(subComponent, null);
+ }
+ });
+ }
+
+ @Test
public void testNullKey() {
thrown.expect(NullPointerException.class);
DisplayData.from(
@@ -610,28 +749,15 @@ public class DisplayDataTest {
@Override
public void populateDisplayData(Builder builder) {
builder.add("key", "value")
- .withLabel(null)
- .withLinkUrl(null);
+ .withLabel(null)
+ .withLinkUrl(null)
+ .withNamespace(null);
}
});
// Should not throw
}
- private static Matcher<DisplayData.Item> hasNamespace(Matcher<Class<?>> nsMatcher) {
- return new FeatureMatcher<DisplayData.Item, Class<?>>(
- nsMatcher, "display item with namespace", "namespace") {
- @Override
- protected Class<?> featureValueOf(DisplayData.Item actual) {
- try {
- return Class.forName(actual.getNamespace());
- } catch (ClassNotFoundException e) {
- return null;
- }
- }
- };
- }
-
private static Matcher<DisplayData.Item> hasLabel(Matcher<String> labelMatcher) {
return new FeatureMatcher<DisplayData.Item, String>(
labelMatcher, "display item with label", "label") {
[2/2] incubator-beam git commit: This closes #145
Posted by bc...@apache.org.
This closes #145
Project: http://git-wip-us.apache.org/repos/asf/incubator-beam/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-beam/commit/245cffa5
Tree: http://git-wip-us.apache.org/repos/asf/incubator-beam/tree/245cffa5
Diff: http://git-wip-us.apache.org/repos/asf/incubator-beam/diff/245cffa5
Branch: refs/heads/master
Commit: 245cffa5e96663707f7d38c6f2b5e529573e7cae
Parents: d827e1b 5da2292
Author: bchambers <bc...@google.com>
Authored: Fri Apr 8 09:47:08 2016 -0700
Committer: bchambers <bc...@google.com>
Committed: Fri Apr 8 09:47:08 2016 -0700
----------------------------------------------------------------------
.../sdk/transforms/display/DisplayData.java | 306 +++++++++++++++++--
.../sdk/transforms/display/HasDisplayData.java | 1 +
.../transforms/display/DisplayDataMatchers.java | 43 +++
.../display/DisplayDataMatchersTest.java | 34 ++-
.../sdk/transforms/display/DisplayDataTest.java | 206 ++++++++++---
5 files changed, 517 insertions(+), 73 deletions(-)
----------------------------------------------------------------------