Skip to content

Commit ac7f0ff

Browse files
committed
CTR update asString() semantics to throw IllegalArgumentException with null inputs for casting step consistency
1 parent 6f767e9 commit ac7f0ff

File tree

14 files changed

+28
-34
lines changed

14 files changed

+28
-34
lines changed

CHANGELOG.asciidoc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ This release also includes changes from <<release-3-7-XXX, 3.7.XXX>>.
104104
* Introduced step interfaces for all parameterizable steps
105105
* Switched `gremlin-net` byte serializers to signed bytes (`sbyte`) to be consistent with IO doc
106106
* Removed auto-unfold of singleton collections from `range()`, `limit()`, and `tail()` local scope steps to improve consistency of output.
107+
* Updated `asString()` step to throw `IllegalArgumentException` with `null` inputs for casting step consistency
107108
* Renamed `MergeElementStep` to `MergeElementStep` as it is a base class to `mergeV()` and `mergeE()`.
108109
* Renamed `MergeStep` of `merge()` to `MergeElementStep` for consistency.
109110

docs/src/dev/provider/gremlin-semantics.asciidoc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1026,7 +1026,7 @@ list of string instead.
10261026
Null values from the incoming traverser are not processed and remain as null when returned.
10271027
10281028
*Exceptions*
1029-
None
1029+
* If the incoming traverser is a `null` value then an `IllegalArgumentException` will be thrown.
10301030
10311031
See: link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AsStringGlobalStep.java[source],
10321032
link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AsStringLocalStep.java[source (local)],

docs/src/reference/the-traversal.asciidoc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -786,7 +786,7 @@ link:++https://tinkerpop.apache.org/javadocs/x.y.z/core/org/apache/tinkerpop/gre
786786
[[asString-step]]
787787
=== AsString Step
788788
789-
The `asString()`-step (*map*) returns the value of incoming traverser as strings. Null values are returned unchanged.
789+
The `asString()`-step (*map*) returns the value of incoming traverser as strings. Any `null` value will cause an `IllegalArgumentException`.
790790
791791
[gremlin-groovy,modern]
792792
----

docs/src/upgrade/release-3.8.x.asciidoc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,11 @@ g.inject("Hello").split("")
272272
273273
See: link:https://issues.apache.org/jira/browse/TINKERPOP-3083[TINKERPOP-3083]
274274
275+
==== asString() No Longer Allow Nulls
276+
The `asString()` step will no longer allow `null` input. An `IllegalArgumentException` will be thrown for consistency with all other parsing steps (i.e. `asDate()`, `asBool()`, `asNumber()`).
277+
278+
See: link:https://lists.apache.org/thread/q76pgrvhprosb4lty63bnsnbw2ljyl7m[DISCUSS] thread
279+
275280
==== Javascript Set Deserialization
276281
277282
Starting from this version, `gremlin-javascript` will deserialize `Set` data into a ECMAScript 2015 Set. Previously,

gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AsStringGlobalStep.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,9 @@ public AsStringGlobalStep(final Traversal.Admin traversal) {
4141
@Override
4242
protected E map(final Traverser.Admin<S> traverser) {
4343
S traverserObject = traverser.get();
44-
return (E) ((traverserObject == null) ? null : String.valueOf(traverserObject));
44+
if (traverserObject == null)
45+
throw new IllegalArgumentException("Can't parse null as String.");
46+
return (E) String.valueOf(traverserObject);
4547
}
4648

4749
@Override

gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AsStringLocalStep.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,14 +47,15 @@ protected E map(final Traverser.Admin<S> traverser) {
4747
final S item = traverser.get();
4848

4949
if (null == item) {
50-
// we will pass null lists to next step
51-
return null;
50+
throw new IllegalArgumentException("Can't parse null as String.");
5251
} else if ((item instanceof Iterable) || (item instanceof Iterator) || item.getClass().isArray()) {
5352
final List<String> resList = new ArrayList<>();
5453
final Iterator<E> iterator = IteratorUtils.asIterator(item);
5554
while (iterator.hasNext()) {
5655
final E i = iterator.next();
57-
resList.add((i == null) ? null : String.valueOf(i));
56+
if (i == null)
57+
throw new IllegalArgumentException("Can't parse null as String.");
58+
resList.add(String.valueOf(i));
5859
}
5960
return (E) resList;
6061
} else {

gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AsStringGlobalStepTest.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,6 @@ public void testReturnTypes() {
4949
assertEquals("1", __.__(Arrays.asList(1, 2)).unfold().asString().next());
5050
assertArrayEquals(new String[]{"1", "2"}, __.inject(Arrays.asList(1, 2)).unfold().asString().toList().toArray());
5151

52-
assertEquals(null, __.__(null).asString().next());
53-
5452
assertEquals("[1, 2]test", __.__(Arrays.asList(1, 2)).asString().concat("test").next());
5553
assertEquals("1test", __.__(Arrays.asList(1, 2)).unfold().asString().concat("test").next());
5654
assertArrayEquals(new String[]{"1test", "2test"},
@@ -59,4 +57,10 @@ public void testReturnTypes() {
5957
__.__(Arrays.asList(1, 2)).unfold().asString().concat("test").fold().next().toArray());
6058
}
6159

60+
61+
@Test(expected = IllegalArgumentException.class)
62+
public void shouldThrowExceptionWhenNullInput() {
63+
__.__(null).asString().next();
64+
}
65+
6266
}

gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AsStringLocalStepTest.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,14 @@ public void testReturnTypes() {
4646
assertEquals("1", __.__(1).asString(Scope.local).next());
4747
assertArrayEquals(new String[]{"1", "2"}, __.inject(1, 2).asString(Scope.local).toList().toArray());
4848
assertArrayEquals(new String[]{}, ((List<?>) __.__(Collections.emptyList()).asString(Scope.local).next()).toArray());
49-
assertArrayEquals(new String[]{"1", "2", null}, ((List<?>) __.__(Arrays.asList(1, 2, null)).asString(Scope.local).next()).toArray());
5049

5150
assertArrayEquals(new String[]{"1test", "2test"},
5251
__.__(Arrays.asList(1, 2)).asString(Scope.local).unfold().concat("test").toList().toArray());
5352
}
5453

54+
@Test(expected = IllegalArgumentException.class)
55+
public void shouldThrowExceptionWhenNullInput() {
56+
__.__(Arrays.asList(1, 2, null)).asString(Scope.local).next();
57+
}
58+
5559
}

gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1488,7 +1488,6 @@ private static IDictionary<string, List<Func<GraphTraversalSource, IDictionary<s
14881488
{"g_V_valuesXnameX_order_fold_toLowerXlocalX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name", "MARKO").Property("age", 29).As("marko").AddV("person").Property("name", "VADAS").Property("age", 27).As("vadas").AddV("software").Property("name", "LOP").Property("lang", "java").As("lop").AddV("person").Property("name", "JOSH").Property("age", 32).As("josh").AddV("software").Property("name", "RIPPLE").Property("lang", "java").As("ripple").AddV("person").Property("name", "PETER").Property("age", 35).As("peter").AddE("knows").From("marko").To("vadas").Property("weight", 0.5d).AddE("knows").From("marko").To("josh").Property("weight", 1.0d).AddE("created").From("marko").To("lop").Property("weight", 0.4d).AddE("created").From("josh").To("ripple").Property("weight", 1.0d).AddE("created").From("josh").To("lop").Property("weight", 0.4d).AddE("created").From("peter").To("lop").Property("weight", 0.2d), (g,p) =>g.V().Values<object>("name").Order().Fold().ToLower<object>(Scope.Local)}},
14891489
{"g_injectXfeature_test_nullX_toUpper", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>("feature", "tESt", null).ToUpper()}},
14901490
{"g_injectXfeature_test_nullX_toUpperXlocalX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>(new List<object> { "feature", "tESt", null }).ToUpper<object>(Scope.Local)}},
1491-
{"g_injectXfeature_test_nullX_asString_toUpper", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>("feature", "tESt", null).AsString().ToUpper()}},
14921491
{"g_injectXListXa_bXX_toUpper", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>(new List<object> { "a", "b" }).ToUpper()}},
14931492
{"g_V_valuesXnameX_toUpper", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Values<object>("name").ToUpper()}},
14941493
{"g_V_valuesXnameX_toUpperXlocalX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Values<object>("name").ToUpper<object>(Scope.Local)}},

gremlin-go/driver/cucumber/gremlin.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1458,7 +1458,6 @@ var translationMap = map[string][]func(g *gremlingo.GraphTraversalSource, p map[
14581458
"g_V_valuesXnameX_order_fold_toLowerXlocalX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV("person").Property("name", "MARKO").Property("age", 29).As("marko").AddV("person").Property("name", "VADAS").Property("age", 27).As("vadas").AddV("software").Property("name", "LOP").Property("lang", "java").As("lop").AddV("person").Property("name", "JOSH").Property("age", 32).As("josh").AddV("software").Property("name", "RIPPLE").Property("lang", "java").As("ripple").AddV("person").Property("name", "PETER").Property("age", 35).As("peter").AddE("knows").From("marko").To("vadas").Property("weight", 0.5).AddE("knows").From("marko").To("josh").Property("weight", 1.0).AddE("created").From("marko").To("lop").Property("weight", 0.4).AddE("created").From("josh").To("ripple").Property("weight", 1.0).AddE("created").From("josh").To("lop").Property("weight", 0.4).AddE("created").From("peter").To("lop").Property("weight", 0.2)}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Values("name").Order().Fold().ToLower(gremlingo.Scope.Local)}},
14591459
"g_injectXfeature_test_nullX_toUpper": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.Inject("feature", "tESt", nil).ToUpper()}},
14601460
"g_injectXfeature_test_nullX_toUpperXlocalX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.Inject([]interface{}{"feature", "tESt", nil}).ToUpper(gremlingo.Scope.Local)}},
1461-
"g_injectXfeature_test_nullX_asString_toUpper": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.Inject("feature", "tESt", nil).AsString().ToUpper()}},
14621461
"g_injectXListXa_bXX_toUpper": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.Inject([]interface{}{"a", "b"}).ToUpper()}},
14631462
"g_V_valuesXnameX_toUpper": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Values("name").ToUpper()}},
14641463
"g_V_valuesXnameX_toUpperXlocalX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Values("name").ToUpper(gremlingo.Scope.Local)}},

0 commit comments

Comments
 (0)