embulk-output-kintone 0.4.1 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (95) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +63 -5
  3. data/build.gradle +1 -0
  4. data/classpath/commons-csv-1.9.0.jar +0 -0
  5. data/classpath/embulk-output-kintone-1.1.0.jar +0 -0
  6. data/classpath/externalsortinginjava-0.6.2.jar +0 -0
  7. data/classpath/{shadow-kintone-java-client-0.4.1-all.jar → shadow-kintone-java-client-1.1.0-all.jar} +0 -0
  8. data/src/main/java/org/embulk/output/kintone/KintoneColumnOption.java +6 -2
  9. data/src/main/java/org/embulk/output/kintone/KintoneColumnType.java +572 -0
  10. data/src/main/java/org/embulk/output/kintone/KintoneColumnVisitor.java +214 -135
  11. data/src/main/java/org/embulk/output/kintone/KintoneMode.java +0 -1
  12. data/src/main/java/org/embulk/output/kintone/KintoneOutputPlugin.java +12 -5
  13. data/src/main/java/org/embulk/output/kintone/KintonePageOutput.java +180 -160
  14. data/src/main/java/org/embulk/output/kintone/KintoneSortColumn.java +33 -0
  15. data/src/main/java/org/embulk/output/kintone/PluginTask.java +35 -0
  16. data/src/main/java/org/embulk/output/kintone/deserializer/DeserializeApplier.java +19 -0
  17. data/src/main/java/org/embulk/output/kintone/deserializer/DeserializeException.java +7 -0
  18. data/src/main/java/org/embulk/output/kintone/deserializer/Deserializer.java +279 -0
  19. data/src/main/java/org/embulk/output/kintone/reducer/CSVInputColumnVisitor.java +78 -0
  20. data/src/main/java/org/embulk/output/kintone/reducer/CSVOutputColumnVisitor.java +79 -0
  21. data/src/main/java/org/embulk/output/kintone/reducer/ReduceException.java +11 -0
  22. data/src/main/java/org/embulk/output/kintone/reducer/ReduceType.java +190 -0
  23. data/src/main/java/org/embulk/output/kintone/reducer/ReducedPageOutput.java +100 -0
  24. data/src/main/java/org/embulk/output/kintone/reducer/Reducer.java +355 -0
  25. data/src/test/java/org/embulk/output/kintone/KintoneColumnOptionBuilder.java +9 -3
  26. data/src/test/java/org/embulk/output/kintone/KintoneColumnTypeTest.java +194 -0
  27. data/src/test/java/org/embulk/output/kintone/KintoneColumnVisitorTest.java +703 -61
  28. data/src/test/java/org/embulk/output/kintone/KintoneColumnVisitorVerifier.java +45 -14
  29. data/src/test/java/org/embulk/output/kintone/KintonePageOutputVerifier.java +43 -5
  30. data/src/test/java/org/embulk/output/kintone/TestKintoneOutputPlugin.java +106 -16
  31. data/src/test/java/org/embulk/output/kintone/TestTaskMode.java +12 -0
  32. data/src/test/java/org/embulk/output/kintone/TestTaskReduce.java +46 -0
  33. data/src/test/java/org/embulk/output/kintone/TestTaskReduceException.java +50 -0
  34. data/src/test/java/org/embulk/output/kintone/TestTaskReduceSubtable.java +46 -0
  35. data/src/test/java/org/embulk/output/kintone/deserializer/DeserializerTest.java +165 -0
  36. data/src/test/java/org/embulk/output/kintone/reducer/ReduceTypeTest.java +154 -0
  37. data/src/test/resources/org/embulk/output/kintone/task/config.yml +1 -1
  38. data/src/test/resources/org/embulk/output/kintone/task/mode/config.yml +110 -0
  39. data/src/test/resources/org/embulk/output/kintone/task/mode/input.csv +7 -7
  40. data/src/test/resources/org/embulk/output/kintone/task/mode/insert_add_ignore_nulls_records.jsonl +6 -0
  41. data/src/test/resources/org/embulk/output/kintone/task/mode/insert_add_prefer_nulls_records.jsonl +6 -0
  42. data/src/test/resources/org/embulk/output/kintone/task/mode/insert_add_records.jsonl +6 -6
  43. data/src/test/resources/org/embulk/output/kintone/task/mode/update_update_ignore_nulls_records.jsonl +3 -0
  44. data/src/test/resources/org/embulk/output/kintone/task/mode/update_update_prefer_nulls_records.jsonl +3 -0
  45. data/src/test/resources/org/embulk/output/kintone/task/mode/update_update_records.jsonl +6 -3
  46. data/src/test/resources/org/embulk/output/kintone/task/mode/upsert_add_ignore_nulls_records.jsonl +3 -0
  47. data/src/test/resources/org/embulk/output/kintone/task/mode/upsert_add_prefer_nulls_records.jsonl +3 -0
  48. data/src/test/resources/org/embulk/output/kintone/task/mode/upsert_add_records.jsonl +2 -2
  49. data/src/test/resources/org/embulk/output/kintone/task/mode/upsert_update_ignore_nulls_records.jsonl +3 -0
  50. data/src/test/resources/org/embulk/output/kintone/task/mode/upsert_update_prefer_nulls_records.jsonl +3 -0
  51. data/src/test/resources/org/embulk/output/kintone/task/mode/upsert_update_records.jsonl +4 -4
  52. data/src/test/resources/org/embulk/output/kintone/task/mode/values_ignore_nulls.json +1 -0
  53. data/src/test/resources/org/embulk/output/kintone/task/mode/values_prefer_nulls.json +1 -0
  54. data/src/test/resources/org/embulk/output/kintone/task/reduce/config.yml +171 -0
  55. data/src/test/resources/org/embulk/output/kintone/task/reduce/input.csv +7 -0
  56. data/src/test/resources/org/embulk/output/kintone/task/reduce/insert_add_ignore_nulls_records.jsonl +6 -0
  57. data/src/test/resources/org/embulk/output/kintone/task/reduce/insert_add_prefer_nulls_records.jsonl +6 -0
  58. data/src/test/resources/org/embulk/output/kintone/task/reduce/insert_add_records.jsonl +6 -0
  59. data/src/test/resources/org/embulk/output/kintone/task/reduce/update_update_ignore_nulls_records.jsonl +3 -0
  60. data/src/test/resources/org/embulk/output/kintone/task/reduce/update_update_prefer_nulls_records.jsonl +3 -0
  61. data/src/test/resources/org/embulk/output/kintone/task/reduce/update_update_records.jsonl +6 -0
  62. data/src/test/resources/org/embulk/output/kintone/task/reduce/upsert_add_ignore_nulls_records.jsonl +3 -0
  63. data/src/test/resources/org/embulk/output/kintone/task/reduce/upsert_add_prefer_nulls_records.jsonl +3 -0
  64. data/src/test/resources/org/embulk/output/kintone/task/reduce/upsert_add_records.jsonl +2 -0
  65. data/src/test/resources/org/embulk/output/kintone/task/reduce/upsert_update_ignore_nulls_records.jsonl +3 -0
  66. data/src/test/resources/org/embulk/output/kintone/task/reduce/upsert_update_prefer_nulls_records.jsonl +3 -0
  67. data/src/test/resources/org/embulk/output/kintone/task/reduce/upsert_update_records.jsonl +4 -0
  68. data/src/test/resources/org/embulk/output/kintone/task/reduce/values.json +1 -0
  69. data/src/test/resources/org/embulk/output/kintone/task/reduce/values_ignore_nulls.json +1 -0
  70. data/src/test/resources/org/embulk/output/kintone/task/reduce/values_prefer_nulls.json +1 -0
  71. data/src/test/resources/org/embulk/output/kintone/task/reduce_exception/config.yml +36 -0
  72. data/src/test/resources/org/embulk/output/kintone/task/reduce_exception/derived_columns.json +1 -0
  73. data/src/test/resources/org/embulk/output/kintone/task/reduce_exception/input.csv +13 -0
  74. data/src/test/resources/org/embulk/output/kintone/task/reduce_exception/insert_add_records.jsonl +2 -0
  75. data/src/test/resources/org/embulk/output/kintone/task/reduce_exception/update_update_records.jsonl +2 -0
  76. data/src/test/resources/org/embulk/output/kintone/task/reduce_subtable/config.yml +343 -0
  77. data/src/test/resources/org/embulk/output/kintone/task/reduce_subtable/derived_columns.json +1 -0
  78. data/src/test/resources/org/embulk/output/kintone/task/reduce_subtable/input.csv +13 -0
  79. data/src/test/resources/org/embulk/output/kintone/task/reduce_subtable/insert_add_ignore_nulls_records.jsonl +6 -0
  80. data/src/test/resources/org/embulk/output/kintone/task/reduce_subtable/insert_add_prefer_nulls_records.jsonl +6 -0
  81. data/src/test/resources/org/embulk/output/kintone/task/reduce_subtable/insert_add_records.jsonl +6 -0
  82. data/src/test/resources/org/embulk/output/kintone/task/reduce_subtable/update_update_ignore_nulls_records.jsonl +3 -0
  83. data/src/test/resources/org/embulk/output/kintone/task/reduce_subtable/update_update_prefer_nulls_records.jsonl +3 -0
  84. data/src/test/resources/org/embulk/output/kintone/task/reduce_subtable/update_update_records.jsonl +6 -0
  85. data/src/test/resources/org/embulk/output/kintone/task/reduce_subtable/upsert_add_ignore_nulls_records.jsonl +3 -0
  86. data/src/test/resources/org/embulk/output/kintone/task/reduce_subtable/upsert_add_prefer_nulls_records.jsonl +3 -0
  87. data/src/test/resources/org/embulk/output/kintone/task/reduce_subtable/upsert_add_records.jsonl +0 -0
  88. data/src/test/resources/org/embulk/output/kintone/task/reduce_subtable/upsert_update_ignore_nulls_records.jsonl +3 -0
  89. data/src/test/resources/org/embulk/output/kintone/task/reduce_subtable/upsert_update_prefer_nulls_records.jsonl +3 -0
  90. data/src/test/resources/org/embulk/output/kintone/task/reduce_subtable/upsert_update_records.jsonl +6 -0
  91. data/src/test/resources/org/embulk/output/kintone/task/reduce_subtable/values.json +1 -0
  92. data/src/test/resources/org/embulk/output/kintone/task/reduce_subtable/values_ignore_nulls.json +1 -0
  93. data/src/test/resources/org/embulk/output/kintone/task/reduce_subtable/values_prefer_nulls.json +1 -0
  94. metadata +73 -4
  95. data/classpath/embulk-output-kintone-0.4.1.jar +0 -0
@@ -0,0 +1,279 @@
1
+ package org.embulk.output.kintone.deserializer;
2
+
3
+ import com.fasterxml.jackson.core.JsonParser;
4
+ import com.fasterxml.jackson.core.ObjectCodec;
5
+ import com.fasterxml.jackson.databind.DeserializationContext;
6
+ import com.fasterxml.jackson.databind.JsonNode;
7
+ import com.fasterxml.jackson.databind.ObjectMapper;
8
+ import com.fasterxml.jackson.databind.module.SimpleModule;
9
+ import com.fasterxml.jackson.databind.node.TextNode;
10
+ import com.kintone.client.model.FileBody;
11
+ import com.kintone.client.model.Group;
12
+ import com.kintone.client.model.Organization;
13
+ import com.kintone.client.model.User;
14
+ import com.kintone.client.model.record.CalcFieldValue;
15
+ import com.kintone.client.model.record.CheckBoxFieldValue;
16
+ import com.kintone.client.model.record.DateFieldValue;
17
+ import com.kintone.client.model.record.DateTimeFieldValue;
18
+ import com.kintone.client.model.record.DropDownFieldValue;
19
+ import com.kintone.client.model.record.FieldType;
20
+ import com.kintone.client.model.record.FieldValue;
21
+ import com.kintone.client.model.record.FileFieldValue;
22
+ import com.kintone.client.model.record.GroupSelectFieldValue;
23
+ import com.kintone.client.model.record.LinkFieldValue;
24
+ import com.kintone.client.model.record.MultiLineTextFieldValue;
25
+ import com.kintone.client.model.record.MultiSelectFieldValue;
26
+ import com.kintone.client.model.record.NumberFieldValue;
27
+ import com.kintone.client.model.record.OrganizationSelectFieldValue;
28
+ import com.kintone.client.model.record.RadioButtonFieldValue;
29
+ import com.kintone.client.model.record.RichTextFieldValue;
30
+ import com.kintone.client.model.record.SingleLineTextFieldValue;
31
+ import com.kintone.client.model.record.SubtableFieldValue;
32
+ import com.kintone.client.model.record.TableRow;
33
+ import com.kintone.client.model.record.TimeFieldValue;
34
+ import com.kintone.client.model.record.UserSelectFieldValue;
35
+ import java.io.IOException;
36
+ import java.lang.invoke.MethodHandles;
37
+ import java.math.BigDecimal;
38
+ import java.time.LocalDate;
39
+ import java.time.LocalTime;
40
+ import java.time.ZonedDateTime;
41
+ import java.util.Iterator;
42
+ import java.util.List;
43
+ import java.util.Spliterator;
44
+ import java.util.Spliterators;
45
+ import java.util.function.BiFunction;
46
+ import java.util.function.Function;
47
+ import java.util.stream.Collectors;
48
+ import java.util.stream.Stream;
49
+ import java.util.stream.StreamSupport;
50
+ import org.slf4j.Logger;
51
+ import org.slf4j.LoggerFactory;
52
+
53
+ public class Deserializer {
54
+ private static final Logger LOGGER =
55
+ LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
56
+ private static final JsonNode NULL = new TextNode(null);
57
+ private final ObjectMapper mapper = new ObjectMapper();
58
+
59
+ public Deserializer() {
60
+ SimpleModule module = new SimpleModule();
61
+ // spotless:off
62
+ addDeserializer(module, SubtableFieldValue.class, this::deserializeSubtable);
63
+ addDeserializer(module, TableRow.class, this::deserializeTableRow);
64
+ addDeserializer(module, SingleLineTextFieldValue.class, this::deserializeSingleLineText);
65
+ addDeserializer(module, MultiLineTextFieldValue.class, this::deserializeMultiLineText);
66
+ addDeserializer(module, RichTextFieldValue.class, this::deserializeRichText);
67
+ addDeserializer(module, NumberFieldValue.class, this::deserializeNumber);
68
+ addDeserializer(module, CalcFieldValue.class, this::deserializeCalc);
69
+ addDeserializer(module, CheckBoxFieldValue.class, this::deserializeCheckBox);
70
+ addDeserializer(module, RadioButtonFieldValue.class, this::deserializeRadioButton);
71
+ addDeserializer(module, MultiSelectFieldValue.class, this::deserializeMultiSelect);
72
+ addDeserializer(module, DropDownFieldValue.class, this::deserializeDropDown);
73
+ addDeserializer(module, UserSelectFieldValue.class, this::deserializeUserSelect);
74
+ addDeserializer(module, User.class, this::deserializeUser);
75
+ addDeserializer(module, OrganizationSelectFieldValue.class, this::deserializeOrganizationSelect);
76
+ addDeserializer(module, Organization.class, this::deserializeOrganization);
77
+ addDeserializer(module, GroupSelectFieldValue.class, this::deserializeGroupSelect);
78
+ addDeserializer(module, Group.class, this::deserializeGroup);
79
+ addDeserializer(module, DateFieldValue.class, this::deserializeDate);
80
+ addDeserializer(module, TimeFieldValue.class, this::deserializeTime);
81
+ addDeserializer(module, DateTimeFieldValue.class, this::deserializeDateTime);
82
+ addDeserializer(module, LinkFieldValue.class, this::deserializeLink);
83
+ addDeserializer(module, FileFieldValue.class, this::deserializeFile);
84
+ addDeserializer(module, FileBody.class, this::deserializeFileBody);
85
+ // spotless:on
86
+ mapper.registerModule(module);
87
+ }
88
+
89
+ public <T> T deserialize(String content, Class<T> type) {
90
+ try {
91
+ return mapper.readValue(content, type);
92
+ } catch (IOException e) {
93
+ throw new DeserializeException(e);
94
+ }
95
+ }
96
+
97
+ private <T> void addDeserializer(
98
+ SimpleModule module,
99
+ Class<T> type,
100
+ BiFunction<JsonParser, DeserializationContext, T> deserializer) {
101
+ module.addDeserializer(type, new DeserializeApplier<>(deserializer));
102
+ }
103
+
104
+ private SubtableFieldValue deserializeSubtable(
105
+ JsonParser parser, DeserializationContext context) {
106
+ return new SubtableFieldValue(readList(parser, TableRow.class));
107
+ }
108
+
109
+ private TableRow deserializeTableRow(JsonParser parser, DeserializationContext context) {
110
+ ObjectCodec codec = parser.getCodec();
111
+ JsonNode node = readTree(codec, parser);
112
+ TableRow row = new TableRow(get(node, "id", JsonNode::asLong));
113
+ stream(node.get("value").fields())
114
+ .forEach(entry -> row.putField(entry.getKey(), readValue(entry.getValue(), codec)));
115
+ return row;
116
+ }
117
+
118
+ private SingleLineTextFieldValue deserializeSingleLineText(
119
+ JsonParser parser, DeserializationContext context) {
120
+ return new SingleLineTextFieldValue(readText(parser));
121
+ }
122
+
123
+ private MultiLineTextFieldValue deserializeMultiLineText(
124
+ JsonParser parser, DeserializationContext context) {
125
+ return new MultiLineTextFieldValue(readText(parser));
126
+ }
127
+
128
+ private RichTextFieldValue deserializeRichText(
129
+ JsonParser parser, DeserializationContext context) {
130
+ return new RichTextFieldValue(readText(parser));
131
+ }
132
+
133
+ private NumberFieldValue deserializeNumber(JsonParser parser, DeserializationContext context) {
134
+ return new NumberFieldValue(read(parser, BigDecimal::new));
135
+ }
136
+
137
+ private CalcFieldValue deserializeCalc(JsonParser parser, DeserializationContext context) {
138
+ LOGGER.warn(
139
+ String.format(
140
+ "Ignore field: %s", new CalcFieldValue((BigDecimal) read(parser, BigDecimal::new))));
141
+ return null;
142
+ }
143
+
144
+ private CheckBoxFieldValue deserializeCheckBox(
145
+ JsonParser parser, DeserializationContext context) {
146
+ return new CheckBoxFieldValue(readList(parser, String.class));
147
+ }
148
+
149
+ private RadioButtonFieldValue deserializeRadioButton(
150
+ JsonParser parser, DeserializationContext context) {
151
+ return new RadioButtonFieldValue(readText(parser));
152
+ }
153
+
154
+ private MultiSelectFieldValue deserializeMultiSelect(
155
+ JsonParser parser, DeserializationContext context) {
156
+ return new MultiSelectFieldValue(readList(parser, String.class));
157
+ }
158
+
159
+ private DropDownFieldValue deserializeDropDown(
160
+ JsonParser parser, DeserializationContext context) {
161
+ return new DropDownFieldValue(readText(parser));
162
+ }
163
+
164
+ private UserSelectFieldValue deserializeUserSelect(
165
+ JsonParser parser, DeserializationContext context) {
166
+ return new UserSelectFieldValue(readList(parser, User.class));
167
+ }
168
+
169
+ private User deserializeUser(JsonParser parser, DeserializationContext context) {
170
+ JsonNode node = readTree(parser);
171
+ return new User(get(node, "name", JsonNode::asText), get(node, "code", JsonNode::asText));
172
+ }
173
+
174
+ private OrganizationSelectFieldValue deserializeOrganizationSelect(
175
+ JsonParser parser, DeserializationContext context) {
176
+ return new OrganizationSelectFieldValue(readList(parser, Organization.class));
177
+ }
178
+
179
+ private Organization deserializeOrganization(JsonParser parser, DeserializationContext context) {
180
+ JsonNode node = readTree(parser);
181
+ return new Organization(
182
+ get(node, "name", JsonNode::asText), get(node, "code", JsonNode::asText));
183
+ }
184
+
185
+ private GroupSelectFieldValue deserializeGroupSelect(
186
+ JsonParser parser, DeserializationContext context) {
187
+ return new GroupSelectFieldValue(readList(parser, Group.class));
188
+ }
189
+
190
+ private Group deserializeGroup(JsonParser parser, DeserializationContext context) {
191
+ JsonNode node = readTree(parser);
192
+ return new Group(get(node, "name", JsonNode::asText), get(node, "code", JsonNode::asText));
193
+ }
194
+
195
+ private DateFieldValue deserializeDate(JsonParser parser, DeserializationContext context) {
196
+ return new DateFieldValue(read(parser, LocalDate::parse));
197
+ }
198
+
199
+ private TimeFieldValue deserializeTime(JsonParser parser, DeserializationContext context) {
200
+ return new TimeFieldValue(read(parser, LocalTime::parse));
201
+ }
202
+
203
+ private DateTimeFieldValue deserializeDateTime(
204
+ JsonParser parser, DeserializationContext context) {
205
+ return new DateTimeFieldValue(read(parser, ZonedDateTime::parse));
206
+ }
207
+
208
+ private LinkFieldValue deserializeLink(JsonParser parser, DeserializationContext context) {
209
+ return new LinkFieldValue(readText(parser));
210
+ }
211
+
212
+ private FileFieldValue deserializeFile(JsonParser parser, DeserializationContext context) {
213
+ return new FileFieldValue(readList(parser, FileBody.class));
214
+ }
215
+
216
+ private FileBody deserializeFileBody(JsonParser parser, DeserializationContext context) {
217
+ JsonNode node = readTree(parser);
218
+ return new FileBody()
219
+ .setContentType(get(node, "contentType", JsonNode::asText))
220
+ .setFileKey(get(node, "fileKey", JsonNode::asText))
221
+ .setName(get(node, "name", JsonNode::asText))
222
+ .setSize(get(node, "size", JsonNode::asInt));
223
+ }
224
+
225
+ private static <T> T get(JsonNode node, String name, Function<JsonNode, T> as) {
226
+ return node.has(name) ? as.apply(node.get(name)) : null;
227
+ }
228
+
229
+ private static FieldValue readValue(JsonNode node, ObjectCodec codec) {
230
+ return node == null
231
+ ? null
232
+ : readValueAs(
233
+ node.has("value") ? node.get("value") : NULL,
234
+ codec,
235
+ FieldType.valueOf(node.get("type").asText()).getFieldValueClass());
236
+ }
237
+
238
+ private static <T> List<T> readList(JsonParser parser, Class<T> type) {
239
+ ObjectCodec codec = parser.getCodec();
240
+ return stream(readTree(codec, parser).elements())
241
+ .map(node -> readValueAs(node, codec, type))
242
+ .collect(Collectors.toList());
243
+ }
244
+
245
+ private static <T> Stream<T> stream(Iterator<T> iterator) {
246
+ return StreamSupport.stream(
247
+ Spliterators.spliteratorUnknownSize(iterator, Spliterator.ORDERED), false);
248
+ }
249
+
250
+ private static <T> T read(JsonParser parser, Function<String, T> as) {
251
+ String text = readText(parser);
252
+ return text == null || text.isEmpty() ? null : as.apply(text);
253
+ }
254
+
255
+ private static String readText(JsonParser parser) {
256
+ JsonNode node = readTree(parser);
257
+ return node == null || node.isNull() ? null : node.asText();
258
+ }
259
+
260
+ private static JsonNode readTree(JsonParser parser) {
261
+ return readTree(parser.getCodec(), parser);
262
+ }
263
+
264
+ private static JsonNode readTree(ObjectCodec codec, JsonParser parser) {
265
+ try {
266
+ return codec.readTree(parser);
267
+ } catch (IOException e) {
268
+ throw new DeserializeException(e);
269
+ }
270
+ }
271
+
272
+ private static <T> T readValueAs(JsonNode node, ObjectCodec codec, Class<T> type) {
273
+ try (JsonParser parser = node.traverse(codec)) {
274
+ return parser.readValueAs(type);
275
+ } catch (IOException e) {
276
+ throw new DeserializeException(e);
277
+ }
278
+ }
279
+ }
@@ -0,0 +1,78 @@
1
+ package org.embulk.output.kintone.reducer;
2
+
3
+ import java.time.Instant;
4
+ import java.util.List;
5
+ import org.embulk.spi.Column;
6
+ import org.embulk.spi.ColumnVisitor;
7
+ import org.embulk.spi.PageBuilder;
8
+ import org.embulk.spi.time.Timestamp;
9
+
10
+ public class CSVInputColumnVisitor implements ColumnVisitor {
11
+ private final PageBuilder builder;
12
+ private final List<String> values;
13
+
14
+ public CSVInputColumnVisitor(PageBuilder builder, List<String> values) {
15
+ this.builder = builder;
16
+ this.values = values;
17
+ }
18
+
19
+ @Override
20
+ public void booleanColumn(Column column) {
21
+ String value = values.get(column.getIndex());
22
+ if (value == null) {
23
+ builder.setNull(column);
24
+ } else {
25
+ builder.setBoolean(column, Boolean.parseBoolean(value));
26
+ }
27
+ }
28
+
29
+ @Override
30
+ public void longColumn(Column column) {
31
+ String value = values.get(column.getIndex());
32
+ if (value == null) {
33
+ builder.setNull(column);
34
+ } else {
35
+ builder.setLong(column, Long.parseLong(value));
36
+ }
37
+ }
38
+
39
+ @Override
40
+ public void doubleColumn(Column column) {
41
+ String value = values.get(column.getIndex());
42
+ if (value == null) {
43
+ builder.setNull(column);
44
+ } else {
45
+ builder.setDouble(column, Double.parseDouble(value));
46
+ }
47
+ }
48
+
49
+ @Override
50
+ public void stringColumn(Column column) {
51
+ String value = values.get(column.getIndex());
52
+ if (value == null) {
53
+ builder.setNull(column);
54
+ } else {
55
+ builder.setString(column, value);
56
+ }
57
+ }
58
+
59
+ @Override
60
+ public void timestampColumn(Column column) {
61
+ String value = values.get(column.getIndex());
62
+ if (value == null) {
63
+ builder.setNull(column);
64
+ } else {
65
+ builder.setTimestamp(column, Timestamp.ofInstant(Instant.parse(value)));
66
+ }
67
+ }
68
+
69
+ @Override
70
+ public void jsonColumn(Column column) {
71
+ String value = values.get(column.getIndex());
72
+ if (value == null) {
73
+ builder.setNull(column);
74
+ } else {
75
+ builder.setJson(column, Reducer.PARSER.parse(value));
76
+ }
77
+ }
78
+ }
@@ -0,0 +1,79 @@
1
+ package org.embulk.output.kintone.reducer;
2
+
3
+ import java.io.IOException;
4
+ import org.apache.commons.csv.CSVPrinter;
5
+ import org.embulk.spi.Column;
6
+ import org.embulk.spi.ColumnVisitor;
7
+ import org.embulk.spi.PageReader;
8
+
9
+ public class CSVOutputColumnVisitor implements ColumnVisitor {
10
+ private final PageReader reader;
11
+ private final CSVPrinter printer;
12
+
13
+ public CSVOutputColumnVisitor(PageReader reader, CSVPrinter printer) {
14
+ this.reader = reader;
15
+ this.printer = printer;
16
+ }
17
+
18
+ @Override
19
+ public void booleanColumn(Column column) {
20
+ if (reader.isNull(column)) {
21
+ print(null);
22
+ } else {
23
+ print(reader.getBoolean(column));
24
+ }
25
+ }
26
+
27
+ @Override
28
+ public void longColumn(Column column) {
29
+ if (reader.isNull(column)) {
30
+ print(null);
31
+ } else {
32
+ print(reader.getLong(column));
33
+ }
34
+ }
35
+
36
+ @Override
37
+ public void doubleColumn(Column column) {
38
+ if (reader.isNull(column)) {
39
+ print(null);
40
+ } else {
41
+ print(reader.getDouble(column));
42
+ }
43
+ }
44
+
45
+ @Override
46
+ public void stringColumn(Column column) {
47
+ if (reader.isNull(column)) {
48
+ print(null);
49
+ } else {
50
+ print(reader.getString(column));
51
+ }
52
+ }
53
+
54
+ @Override
55
+ public void timestampColumn(Column column) {
56
+ if (reader.isNull(column)) {
57
+ print(null);
58
+ } else {
59
+ print(reader.getTimestamp(column).getInstant());
60
+ }
61
+ }
62
+
63
+ @Override
64
+ public void jsonColumn(Column column) {
65
+ if (reader.isNull(column)) {
66
+ print(null);
67
+ } else {
68
+ print(reader.getJson(column).toJson());
69
+ }
70
+ }
71
+
72
+ private void print(Object value) {
73
+ try {
74
+ printer.print(value);
75
+ } catch (IOException e) {
76
+ throw new ReduceException(e);
77
+ }
78
+ }
79
+ }
@@ -0,0 +1,11 @@
1
+ package org.embulk.output.kintone.reducer;
2
+
3
+ public class ReduceException extends RuntimeException {
4
+ public ReduceException(String message) {
5
+ super(message);
6
+ }
7
+
8
+ public ReduceException(Throwable cause) {
9
+ super(cause);
10
+ }
11
+ }
@@ -0,0 +1,190 @@
1
+ package org.embulk.output.kintone.reducer;
2
+
3
+ import java.time.Instant;
4
+ import java.util.ArrayList;
5
+ import java.util.Comparator;
6
+ import java.util.List;
7
+ import java.util.Map;
8
+ import java.util.function.Supplier;
9
+ import org.embulk.output.kintone.KintoneColumnOption;
10
+ import org.embulk.output.kintone.KintoneColumnType;
11
+ import org.embulk.output.kintone.KintoneSortColumn;
12
+ import org.embulk.spi.Column;
13
+ import org.embulk.spi.time.Timestamp;
14
+ import org.msgpack.value.ArrayValue;
15
+ import org.msgpack.value.MapValue;
16
+ import org.msgpack.value.Value;
17
+ import org.msgpack.value.ValueFactory;
18
+
19
+ public enum ReduceType {
20
+ BOOLEAN {
21
+ @Override
22
+ public MapValue value(String value, KintoneColumnOption option) {
23
+ KintoneColumnType type = KintoneColumnType.getType(option, KintoneColumnType.NUMBER);
24
+ Supplier<Value> supplier =
25
+ () -> type.asValue(type.getFieldValue(Boolean.parseBoolean(value), option));
26
+ return value(type, value, supplier);
27
+ }
28
+
29
+ @Override
30
+ public Comparator<String> comparator(KintoneSortColumn.Order order) {
31
+ return Comparator.comparing(Boolean::parseBoolean, order(order));
32
+ }
33
+ },
34
+ LONG {
35
+ @Override
36
+ public MapValue value(String value, KintoneColumnOption option) {
37
+ KintoneColumnType type = KintoneColumnType.getType(option, KintoneColumnType.NUMBER);
38
+ Supplier<Value> supplier =
39
+ () -> type.asValue(type.getFieldValue(Long.parseLong(value), option));
40
+ return value(type, value, supplier);
41
+ }
42
+
43
+ @Override
44
+ public Comparator<String> comparator(KintoneSortColumn.Order order) {
45
+ return Comparator.comparing(Long::parseLong, order(order));
46
+ }
47
+ },
48
+ DOUBLE {
49
+ @Override
50
+ public MapValue value(String value, KintoneColumnOption option) {
51
+ KintoneColumnType type = KintoneColumnType.getType(option, KintoneColumnType.NUMBER);
52
+ Supplier<Value> supplier =
53
+ () -> type.asValue(type.getFieldValue(Double.parseDouble(value), option));
54
+ return value(type, value, supplier);
55
+ }
56
+
57
+ @Override
58
+ public Comparator<String> comparator(KintoneSortColumn.Order order) {
59
+ return Comparator.comparing(Double::parseDouble, order(order));
60
+ }
61
+ },
62
+ STRING {
63
+ @Override
64
+ public MapValue value(String value, KintoneColumnOption option) {
65
+ KintoneColumnType type = KintoneColumnType.getType(option, KintoneColumnType.MULTI_LINE_TEXT);
66
+ Supplier<Value> supplier = () -> type.asValue(type.getFieldValue(value, option));
67
+ return value(type, value, supplier);
68
+ }
69
+
70
+ @Override
71
+ public Comparator<String> comparator(KintoneSortColumn.Order order) {
72
+ return order(order);
73
+ }
74
+ },
75
+ TIMESTAMP {
76
+ @Override
77
+ public MapValue value(String value, KintoneColumnOption option) {
78
+ KintoneColumnType type = KintoneColumnType.getType(option, KintoneColumnType.DATETIME);
79
+ Supplier<Value> supplier =
80
+ () -> type.asValue(type.getFieldValue(Timestamp.ofInstant(Instant.parse(value)), option));
81
+ return value(type, value, supplier);
82
+ }
83
+
84
+ @Override
85
+ public Comparator<String> comparator(KintoneSortColumn.Order order) {
86
+ return Comparator.comparing(Instant::parse, order(order));
87
+ }
88
+ },
89
+ JSON {
90
+ @Override
91
+ public MapValue value(String value, KintoneColumnOption option) {
92
+ KintoneColumnType type = KintoneColumnType.getType(option, KintoneColumnType.MULTI_LINE_TEXT);
93
+ Supplier<Value> supplier =
94
+ () -> type.asValue(type.getFieldValue(Reducer.PARSER.parse(value), option));
95
+ return value(type, value, supplier);
96
+ }
97
+
98
+ @Override
99
+ public Comparator<String> comparator(KintoneSortColumn.Order order) {
100
+ return order(order);
101
+ }
102
+ };
103
+ private static final Value NIL = ValueFactory.newNil();
104
+ private static final Value ID = ValueFactory.newString("id");
105
+ private static final Value TYPE = ValueFactory.newString("type");
106
+ private static final Value VALUE = ValueFactory.newString("value");
107
+ private static final Value KEY_SET = ValueFactory.newString("$$key_set");
108
+ private static final Value SORT_VALUE = ValueFactory.newString("$$sort_value");
109
+
110
+ public abstract MapValue value(String value, KintoneColumnOption option);
111
+
112
+ public abstract Comparator<String> comparator(KintoneSortColumn.Order order);
113
+
114
+ public static Comparator<String> comparator(Column column, KintoneSortColumn.Order order) {
115
+ return valueOf(column).comparator(order);
116
+ }
117
+
118
+ public static String asString(Value value, KintoneSortColumn sortColumn) {
119
+ return asString(sortValue(value).map().get(value(sortColumn.getName())));
120
+ }
121
+
122
+ public static boolean isEmpty(MapValue value) {
123
+ return value.values().stream()
124
+ .map(Value::asMapValue)
125
+ .map(MapValue::map)
126
+ .map(map -> map.get(VALUE))
127
+ .allMatch(Value::isNilValue);
128
+ }
129
+
130
+ public static Value value(String value) {
131
+ return value == null ? NIL : ValueFactory.newString(value);
132
+ }
133
+
134
+ public static MapValue value(Value value) {
135
+ ValueFactory.MapBuilder builder = ValueFactory.newMapBuilder();
136
+ Map<Value, Value> map = value.asMapValue().map();
137
+ builder.put(ID, map.get(ID));
138
+ builder.put(VALUE, value(map.get(VALUE).asMapValue().map(), map.get(KEY_SET).asArrayValue()));
139
+ return builder.build();
140
+ }
141
+
142
+ public static MapValue value(Long id, MapValue value, MapValue sortValue) {
143
+ ValueFactory.MapBuilder builder = ValueFactory.newMapBuilder();
144
+ builder.put(ID, id == null ? NIL : ValueFactory.newString(id.toString()));
145
+ builder.put(VALUE, value == null ? ValueFactory.emptyMap() : value);
146
+ builder.put(KEY_SET, value == null ? ValueFactory.emptyArray() : keySet(value));
147
+ builder.put(SORT_VALUE, sortValue == null ? ValueFactory.emptyMap() : sortValue);
148
+ return builder.build();
149
+ }
150
+
151
+ public static MapValue value(Column column, List<String> values, KintoneColumnOption option) {
152
+ return valueOf(column).value(values.get(column.getIndex()), option);
153
+ }
154
+
155
+ protected static MapValue value(KintoneColumnType type, String value, Supplier<Value> supplier) {
156
+ ValueFactory.MapBuilder builder = ValueFactory.newMapBuilder();
157
+ builder.put(TYPE, value(type.name()));
158
+ builder.put(VALUE, value == null ? NIL : supplier.get());
159
+ return builder.build();
160
+ }
161
+
162
+ private static <T extends Comparable<? super T>> Comparator<T> order(
163
+ KintoneSortColumn.Order order) {
164
+ return order == KintoneSortColumn.Order.DESC
165
+ ? Comparator.reverseOrder()
166
+ : Comparator.naturalOrder();
167
+ }
168
+
169
+ private static String asString(Value value) {
170
+ return value.isNilValue() ? null : value.asStringValue().asString();
171
+ }
172
+
173
+ private static MapValue sortValue(Value value) {
174
+ return value.asMapValue().map().get(SORT_VALUE).asMapValue();
175
+ }
176
+
177
+ private static MapValue value(Map<Value, Value> map, ArrayValue keySet) {
178
+ ValueFactory.MapBuilder builder = ValueFactory.newMapBuilder();
179
+ keySet.forEach(key -> builder.put(key, map.get(key)));
180
+ return builder.build();
181
+ }
182
+
183
+ private static ArrayValue keySet(MapValue value) {
184
+ return ValueFactory.newArray(new ArrayList<>(value.asMapValue().keySet()));
185
+ }
186
+
187
+ private static ReduceType valueOf(Column column) {
188
+ return valueOf(column.getType().getName().toUpperCase());
189
+ }
190
+ }