Skip to content

Commit 6e95494

Browse files
committed
support setting default format via env vars for Instant and DateTime types
1 parent af77a12 commit 6e95494

File tree

5 files changed

+98
-11
lines changed

5 files changed

+98
-11
lines changed

tessellate-main/build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ dependencies {
125125
testImplementation("io.hosuaby:inject-resources-junit-jupiter:$injectResources")
126126

127127
// https://github.com/webcompere/system-stubs
128-
val systemStubs = "2.1.6"
128+
val systemStubs = "2.1.8"
129129
testImplementation("uk.org.webcompere:system-stubs-core:$systemStubs")
130130
testImplementation("uk.org.webcompere:system-stubs-jupiter:$systemStubs")
131131
testImplementation("org.mockito:mockito-inline:5.2.0")

tessellate-main/src/main/antora/modules/reference/pages/types.adoc

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,16 @@ Some types are canonically a custom or special Java type, but also have metadata
3131
These types take the form `type[|metadata]` where the optional metadata can be a format string, in the case of the temporal types.
3232

3333
`Instant|format`:: Canonically a `java.time.Instant`.
34-
Supports nanos precision, format defaults to ISO-8601
35-
instant format, e.g. `2011-12-03T10:15:30Z`
36-
`DateTime|format`:: Canonically a `Long`, format defaults to `yyyy-MM-dd HH:mm:ss.SSSSSS z`.
37-
`json`:: Canonically a `com.fasterxml.jackson.databind.JsonNode` values, nested objects, and arrays.
34+
- Supports nanos precision, format defaults to ISO-8601 instant format, e.g. `2011-12-03T10:15:30Z`
35+
- May also be a Clusterless time interval: `Day`, `Hours`, `Fourths`, `Sixth`, or `Twelfths`
36+
- The default format may be changed via the `INSTANT_TYPE_FORMAT` environment variable
37+
38+
`DateTime|format`:: Canonically a `Long`.
39+
- Format defaults to `yyyy-MM-dd HH:mm:ss.SSSSSS z`.
40+
- The default format may be changed via the `DATE_TYPE_FORMAT` environment variable
41+
42+
`json`:: Canonically `com.fasterxml.jackson.databind.JsonNode`.
43+
- Maybe either a value, nested object, or array.
3844

3945
When reading source data, fields can be parsed and coerced into specific types, as declared, or when using formats like Parquet, type information will be inherited.
4046

tessellate-main/src/main/java/io/clusterless/tessellate/parser/FieldParser.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,15 @@ public class FieldParser {
110110
public static final Parser<List<Rel>> RELATION_LIST =
111111
RELATION.sepBy1(Parsers.sequence(Scanners.many(IS_WHITESPACE)));
112112

113+
public static Optional<FieldTypeParam> parseFieldTypeParam(String param) {
114+
if (param == null || param.isEmpty()) {
115+
return Optional.empty();
116+
}
117+
118+
// parser expects the |, hack so we don't mess with the parser
119+
return BaseParser.parse(typeParam, "|" + param);
120+
}
121+
113122
public static Field parseField(String field) {
114123
return BaseParser.parse(fullFieldDeclaration, field);
115124
}

tessellate-main/src/main/java/io/clusterless/tessellate/parser/FieldsParser.java

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import java.time.temporal.TemporalUnit;
3030
import java.util.List;
3131
import java.util.Locale;
32+
import java.util.Optional;
3233
import java.util.TimeZone;
3334

3435
/**
@@ -49,6 +50,8 @@
4950
*/
5051
public class FieldsParser {
5152
public static final FieldsParser INSTANCE = new FieldsParser();
53+
public static final String INSTANT_TYPE_FORMAT = "INSTANT_TYPE_FORMAT";
54+
public static final String DATE_TYPE_FORMAT = "DATE_TYPE_FORMAT";
5255

5356
private DateType defaultDateTimeType = new DateType("yyyy-MM-dd HH:mm:ss.SSSSSS z", TimeZone.getTimeZone("UTC"));
5457
private InstantType defaultInstantType = InstantType.ISO_MICROS;
@@ -128,8 +131,18 @@ protected Type resolveType(FieldType fieldType) {
128131

129132
String typeName = fieldType.name().name();
130133

131-
String first = fieldType.param().map(FieldTypeParam::param1).orElse(null);
132-
String second = fieldType.param().map(FieldTypeParam::param2).orElse(null);
134+
Optional<FieldTypeParam> param = fieldType.param();
135+
136+
if (param.isEmpty()) {
137+
if (typeName.equalsIgnoreCase("DateTime")) {
138+
param = FieldParser.parseFieldTypeParam(System.getenv(DATE_TYPE_FORMAT));
139+
} else if (typeName.equalsIgnoreCase("Instant")) {
140+
param = FieldParser.parseFieldTypeParam(System.getenv(INSTANT_TYPE_FORMAT));
141+
}
142+
}
143+
144+
String first = param.map(FieldTypeParam::param1).orElse(null);
145+
String second = param.map(FieldTypeParam::param2).orElse(null);
133146

134147
if (typeName.equalsIgnoreCase("string")) {
135148
type = String.class;
@@ -194,8 +207,8 @@ public String resolveTypeName(Type type) {
194207
return typeNames[0];
195208
}
196209

197-
private static DateTimeFormatter createPattern(String splitType) {
198-
return DateTimeFormatter.ofPattern(splitType)
210+
public static DateTimeFormatter createPattern(String format) {
211+
return DateTimeFormatter.ofPattern(format)
199212
.withZone(ZoneId.of("UTC"));
200213
}
201214

tessellate-main/src/test/java/io/clusterless/tessellate/parser/FieldParserTest.java

Lines changed: 61 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,23 @@
88

99
package io.clusterless.tessellate.parser;
1010

11+
import cascading.tuple.type.DateType;
12+
import cascading.tuple.type.InstantType;
13+
import clusterless.commons.temporal.IntervalUnits;
1114
import org.junit.jupiter.api.Test;
15+
import org.junit.jupiter.api.extension.ExtendWith;
16+
import uk.org.webcompere.systemstubs.environment.EnvironmentVariables;
17+
import uk.org.webcompere.systemstubs.jupiter.SystemStub;
18+
import uk.org.webcompere.systemstubs.jupiter.SystemStubsExtension;
1219

13-
import static org.junit.jupiter.api.Assertions.assertEquals;
14-
import static org.junit.jupiter.api.Assertions.assertNotNull;
20+
import static org.junit.jupiter.api.Assertions.*;
1521

22+
@ExtendWith(SystemStubsExtension.class)
1623
public class FieldParserTest {
24+
25+
@SystemStub
26+
private EnvironmentVariables variables = new EnvironmentVariables();
27+
1728
@Test
1829
void parseFields() {
1930
assertNotNull(FieldParser.parseField("@field"));
@@ -56,4 +67,52 @@ void parseFieldsList() {
5667
assertEquals(4, FieldParser.parseFieldList("@field1 + @field2+@field3 +@field4").size());
5768
assertEquals(5, FieldParser.parseFieldList("@field1+ @field|DateTime +@field|DateTime|yyyyMMdd+1|DateTime|yyyyMMdd + @field|Instant|twelfths|yyyyMMdd").size());
5869
}
70+
71+
@Test
72+
void parseFieldsWithTypeDefaults() {
73+
assertEquals(
74+
"yyyyMMdd",
75+
assertInstanceOf(
76+
DateType.class,
77+
FieldsParser.INSTANCE.asFields(FieldParser.parseField("@field|DateTime|yyyyMMdd")).getType(0)
78+
).getDateFormat().toLocalizedPattern()
79+
);
80+
81+
variables.set(FieldsParser.DATE_TYPE_FORMAT, "yyyyMMdd");
82+
assertEquals(
83+
"yyyyMMdd",
84+
assertInstanceOf(
85+
DateType.class,
86+
FieldsParser.INSTANCE.asFields(FieldParser.parseField("@field|DateTime")).getType(0)
87+
).getDateFormat().toLocalizedPattern()
88+
);
89+
90+
assertEquals(
91+
FieldsParser.createPattern("yyyyMMdd").toString(),
92+
assertInstanceOf(
93+
InstantType.class,
94+
FieldsParser.INSTANCE.asFields(FieldParser.parseField("@field|Instant|twelfths|yyyyMMdd")).getType(0)
95+
).getDateTimeFormatter().toString()
96+
);
97+
98+
variables.set(FieldsParser.INSTANT_TYPE_FORMAT, "twelfths|yyyyMMdd");
99+
100+
assertEquals(
101+
FieldsParser.createPattern("yyyyMMdd").toString(),
102+
assertInstanceOf(
103+
InstantType.class,
104+
FieldsParser.INSTANCE.asFields(FieldParser.parseField("@field|Instant")).getType(0)
105+
).getDateTimeFormatter().toString()
106+
);
107+
108+
variables.set(FieldsParser.INSTANT_TYPE_FORMAT, "twelfths");
109+
110+
assertEquals(
111+
IntervalUnits.formatter(IntervalUnits.find("twelfths")).toString(),
112+
assertInstanceOf(
113+
InstantType.class,
114+
FieldsParser.INSTANCE.asFields(FieldParser.parseField("@field|Instant")).getType(0)
115+
).getDateTimeFormatter().toString()
116+
);
117+
}
59118
}

0 commit comments

Comments
 (0)