embulk-output-kintone 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (90) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +57 -1
  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-1.0.0-all.jar → shadow-kintone-java-client-1.1.0-all.jar} +0 -0
  8. data/src/main/java/org/embulk/output/kintone/KintoneColumnOption.java +5 -0
  9. data/src/main/java/org/embulk/output/kintone/KintoneColumnType.java +209 -5
  10. data/src/main/java/org/embulk/output/kintone/KintoneColumnVisitor.java +28 -9
  11. data/src/main/java/org/embulk/output/kintone/KintoneOutputPlugin.java +12 -3
  12. data/src/main/java/org/embulk/output/kintone/KintonePageOutput.java +20 -12
  13. data/src/main/java/org/embulk/output/kintone/KintoneSortColumn.java +33 -0
  14. data/src/main/java/org/embulk/output/kintone/PluginTask.java +27 -0
  15. data/src/main/java/org/embulk/output/kintone/deserializer/DeserializeApplier.java +19 -0
  16. data/src/main/java/org/embulk/output/kintone/deserializer/DeserializeException.java +7 -0
  17. data/src/main/java/org/embulk/output/kintone/deserializer/Deserializer.java +279 -0
  18. data/src/main/java/org/embulk/output/kintone/reducer/CSVInputColumnVisitor.java +78 -0
  19. data/src/main/java/org/embulk/output/kintone/reducer/CSVOutputColumnVisitor.java +79 -0
  20. data/src/main/java/org/embulk/output/kintone/reducer/ReduceException.java +11 -0
  21. data/src/main/java/org/embulk/output/kintone/reducer/ReduceType.java +190 -0
  22. data/src/main/java/org/embulk/output/kintone/reducer/ReducedPageOutput.java +100 -0
  23. data/src/main/java/org/embulk/output/kintone/reducer/Reducer.java +355 -0
  24. data/src/test/java/org/embulk/output/kintone/KintoneColumnOptionBuilder.java +7 -0
  25. data/src/test/java/org/embulk/output/kintone/KintoneColumnTypeTest.java +194 -0
  26. data/src/test/java/org/embulk/output/kintone/KintoneColumnVisitorTest.java +153 -34
  27. data/src/test/java/org/embulk/output/kintone/KintoneColumnVisitorVerifier.java +13 -3
  28. data/src/test/java/org/embulk/output/kintone/KintonePageOutputVerifier.java +44 -1
  29. data/src/test/java/org/embulk/output/kintone/TestKintoneOutputPlugin.java +89 -12
  30. data/src/test/java/org/embulk/output/kintone/TestTaskReduce.java +46 -0
  31. data/src/test/java/org/embulk/output/kintone/TestTaskReduceException.java +50 -0
  32. data/src/test/java/org/embulk/output/kintone/TestTaskReduceSubtable.java +46 -0
  33. data/src/test/java/org/embulk/output/kintone/deserializer/DeserializerTest.java +165 -0
  34. data/src/test/java/org/embulk/output/kintone/reducer/ReduceTypeTest.java +154 -0
  35. data/src/test/resources/org/embulk/output/kintone/task/config.yml +1 -1
  36. data/src/test/resources/org/embulk/output/kintone/task/mode/config.yml +6 -0
  37. data/src/test/resources/org/embulk/output/kintone/task/mode/input.csv +7 -7
  38. data/src/test/resources/org/embulk/output/kintone/task/mode/insert_add_ignore_nulls_records.jsonl +2 -2
  39. data/src/test/resources/org/embulk/output/kintone/task/mode/insert_add_prefer_nulls_records.jsonl +6 -6
  40. data/src/test/resources/org/embulk/output/kintone/task/mode/insert_add_records.jsonl +6 -6
  41. data/src/test/resources/org/embulk/output/kintone/task/mode/update_update_ignore_nulls_records.jsonl +2 -2
  42. data/src/test/resources/org/embulk/output/kintone/task/mode/update_update_prefer_nulls_records.jsonl +3 -3
  43. data/src/test/resources/org/embulk/output/kintone/task/mode/update_update_records.jsonl +6 -6
  44. data/src/test/resources/org/embulk/output/kintone/task/mode/upsert_add_prefer_nulls_records.jsonl +3 -3
  45. data/src/test/resources/org/embulk/output/kintone/task/mode/upsert_add_records.jsonl +2 -2
  46. data/src/test/resources/org/embulk/output/kintone/task/mode/upsert_update_ignore_nulls_records.jsonl +2 -2
  47. data/src/test/resources/org/embulk/output/kintone/task/mode/upsert_update_prefer_nulls_records.jsonl +3 -3
  48. data/src/test/resources/org/embulk/output/kintone/task/mode/upsert_update_records.jsonl +4 -4
  49. data/src/test/resources/org/embulk/output/kintone/task/reduce/config.yml +171 -0
  50. data/src/test/resources/org/embulk/output/kintone/task/reduce/input.csv +7 -0
  51. data/src/test/resources/org/embulk/output/kintone/task/reduce/insert_add_ignore_nulls_records.jsonl +6 -0
  52. data/src/test/resources/org/embulk/output/kintone/task/reduce/insert_add_prefer_nulls_records.jsonl +6 -0
  53. data/src/test/resources/org/embulk/output/kintone/task/reduce/insert_add_records.jsonl +6 -0
  54. data/src/test/resources/org/embulk/output/kintone/task/reduce/update_update_ignore_nulls_records.jsonl +3 -0
  55. data/src/test/resources/org/embulk/output/kintone/task/reduce/update_update_prefer_nulls_records.jsonl +3 -0
  56. data/src/test/resources/org/embulk/output/kintone/task/reduce/update_update_records.jsonl +6 -0
  57. data/src/test/resources/org/embulk/output/kintone/task/reduce/upsert_add_ignore_nulls_records.jsonl +3 -0
  58. data/src/test/resources/org/embulk/output/kintone/task/reduce/upsert_add_prefer_nulls_records.jsonl +3 -0
  59. data/src/test/resources/org/embulk/output/kintone/task/reduce/upsert_add_records.jsonl +2 -0
  60. data/src/test/resources/org/embulk/output/kintone/task/reduce/upsert_update_ignore_nulls_records.jsonl +3 -0
  61. data/src/test/resources/org/embulk/output/kintone/task/reduce/upsert_update_prefer_nulls_records.jsonl +3 -0
  62. data/src/test/resources/org/embulk/output/kintone/task/reduce/upsert_update_records.jsonl +4 -0
  63. data/src/test/resources/org/embulk/output/kintone/task/reduce/values.json +1 -0
  64. data/src/test/resources/org/embulk/output/kintone/task/reduce/values_ignore_nulls.json +1 -0
  65. data/src/test/resources/org/embulk/output/kintone/task/reduce/values_prefer_nulls.json +1 -0
  66. data/src/test/resources/org/embulk/output/kintone/task/reduce_exception/config.yml +36 -0
  67. data/src/test/resources/org/embulk/output/kintone/task/reduce_exception/derived_columns.json +1 -0
  68. data/src/test/resources/org/embulk/output/kintone/task/reduce_exception/input.csv +13 -0
  69. data/src/test/resources/org/embulk/output/kintone/task/reduce_exception/insert_add_records.jsonl +2 -0
  70. data/src/test/resources/org/embulk/output/kintone/task/reduce_exception/update_update_records.jsonl +2 -0
  71. data/src/test/resources/org/embulk/output/kintone/task/reduce_subtable/config.yml +343 -0
  72. data/src/test/resources/org/embulk/output/kintone/task/reduce_subtable/derived_columns.json +1 -0
  73. data/src/test/resources/org/embulk/output/kintone/task/reduce_subtable/input.csv +13 -0
  74. data/src/test/resources/org/embulk/output/kintone/task/reduce_subtable/insert_add_ignore_nulls_records.jsonl +6 -0
  75. data/src/test/resources/org/embulk/output/kintone/task/reduce_subtable/insert_add_prefer_nulls_records.jsonl +6 -0
  76. data/src/test/resources/org/embulk/output/kintone/task/reduce_subtable/insert_add_records.jsonl +6 -0
  77. data/src/test/resources/org/embulk/output/kintone/task/reduce_subtable/update_update_ignore_nulls_records.jsonl +3 -0
  78. data/src/test/resources/org/embulk/output/kintone/task/reduce_subtable/update_update_prefer_nulls_records.jsonl +3 -0
  79. data/src/test/resources/org/embulk/output/kintone/task/reduce_subtable/update_update_records.jsonl +6 -0
  80. data/src/test/resources/org/embulk/output/kintone/task/reduce_subtable/upsert_add_ignore_nulls_records.jsonl +3 -0
  81. data/src/test/resources/org/embulk/output/kintone/task/reduce_subtable/upsert_add_prefer_nulls_records.jsonl +3 -0
  82. data/src/test/resources/org/embulk/output/kintone/task/reduce_subtable/upsert_add_records.jsonl +0 -0
  83. data/src/test/resources/org/embulk/output/kintone/task/reduce_subtable/upsert_update_ignore_nulls_records.jsonl +3 -0
  84. data/src/test/resources/org/embulk/output/kintone/task/reduce_subtable/upsert_update_prefer_nulls_records.jsonl +3 -0
  85. data/src/test/resources/org/embulk/output/kintone/task/reduce_subtable/upsert_update_records.jsonl +6 -0
  86. data/src/test/resources/org/embulk/output/kintone/task/reduce_subtable/values.json +1 -0
  87. data/src/test/resources/org/embulk/output/kintone/task/reduce_subtable/values_ignore_nulls.json +1 -0
  88. data/src/test/resources/org/embulk/output/kintone/task/reduce_subtable/values_prefer_nulls.json +1 -0
  89. metadata +62 -4
  90. data/classpath/embulk-output-kintone-1.0.0.jar +0 -0
@@ -50,7 +50,6 @@ public class KintonePageOutput implements TransactionalPageOutput {
50
50
  "GAIA_DA02" // データベースのロックに失敗したため、変更を保存できませんでした。時間をおいて再度お試しください。
51
51
  );
52
52
  private static final int UPSERT_BATCH_SIZE = 10000;
53
- private static final int CHUNK_SIZE = 100;
54
53
  private final Map<String, Pair<FieldType, FieldType>> wrongTypeFields = new TreeMap<>();
55
54
  private final PluginTask task;
56
55
  private final PageReader reader;
@@ -113,7 +112,7 @@ public class KintonePageOutput implements TransactionalPageOutput {
113
112
  return Exec.newTaskReport();
114
113
  }
115
114
 
116
- public void connectIfNeeded() {
115
+ private void connectIfNeeded() {
117
116
  if (client != null) {
118
117
  return; // Already connected
119
118
  }
@@ -203,14 +202,19 @@ public class KintonePageOutput implements TransactionalPageOutput {
203
202
  reader.setPage(page);
204
203
  KintoneColumnVisitor visitor =
205
204
  new KintoneColumnVisitor(
206
- reader, task.getColumnOptions(), task.getPreferNulls(), task.getIgnoreNulls());
205
+ reader,
206
+ task.getDerivedColumns(),
207
+ task.getColumnOptions(),
208
+ task.getPreferNulls(),
209
+ task.getIgnoreNulls(),
210
+ task.getReduceKeyName().orElse(null));
207
211
  while (reader.nextRecord()) {
208
212
  Record record = new Record();
209
213
  visitor.setRecord(record);
210
214
  reader.getSchema().visitColumns(visitor);
211
215
  putWrongTypeFields(record);
212
216
  records.add(record);
213
- if (records.size() == CHUNK_SIZE) {
217
+ if (records.size() == task.getChunkSize()) {
214
218
  insert(records);
215
219
  records.clear();
216
220
  }
@@ -226,9 +230,11 @@ public class KintonePageOutput implements TransactionalPageOutput {
226
230
  KintoneColumnVisitor visitor =
227
231
  new KintoneColumnVisitor(
228
232
  reader,
233
+ task.getDerivedColumns(),
229
234
  task.getColumnOptions(),
230
235
  task.getPreferNulls(),
231
236
  task.getIgnoreNulls(),
237
+ task.getReduceKeyName().orElse(null),
232
238
  task.getUpdateKeyName()
233
239
  .orElseThrow(() -> new RuntimeException("unreachable"))); // Already validated
234
240
  while (reader.nextRecord()) {
@@ -243,7 +249,7 @@ public class KintonePageOutput implements TransactionalPageOutput {
243
249
  continue;
244
250
  }
245
251
  records.add(new RecordForUpdate(updateKey, record.removeField(updateKey.getField())));
246
- if (records.size() == CHUNK_SIZE) {
252
+ if (records.size() == task.getChunkSize()) {
247
253
  update(records);
248
254
  records.clear();
249
255
  }
@@ -260,9 +266,11 @@ public class KintonePageOutput implements TransactionalPageOutput {
260
266
  KintoneColumnVisitor visitor =
261
267
  new KintoneColumnVisitor(
262
268
  reader,
269
+ task.getDerivedColumns(),
263
270
  task.getColumnOptions(),
264
271
  task.getPreferNulls(),
265
272
  task.getIgnoreNulls(),
273
+ task.getReduceKeyName().orElse(null),
266
274
  task.getUpdateKeyName()
267
275
  .orElseThrow(() -> new RuntimeException("unreachable"))); // Already validated
268
276
  while (reader.nextRecord()) {
@@ -300,10 +308,10 @@ public class KintonePageOutput implements TransactionalPageOutput {
300
308
  } else {
301
309
  insertRecords.add(record);
302
310
  }
303
- if (insertRecords.size() == CHUNK_SIZE) {
311
+ if (insertRecords.size() == task.getChunkSize()) {
304
312
  insert(insertRecords);
305
313
  insertRecords.clear();
306
- } else if (updateRecords.size() == CHUNK_SIZE) {
314
+ } else if (updateRecords.size() == task.getChunkSize()) {
307
315
  update(updateRecords);
308
316
  updateRecords.clear();
309
317
  }
@@ -365,11 +373,6 @@ public class KintonePageOutput implements TransactionalPageOutput {
365
373
  .collect(Collectors.toList());
366
374
  }
367
375
 
368
- private boolean existsRecord(List<String> existingValues, UpdateKey updateKey) {
369
- String value = toString(updateKey.getValue());
370
- return value != null && existingValues.stream().anyMatch(v -> v.equals(value));
371
- }
372
-
373
376
  private void putWrongTypeFields(Record record) {
374
377
  record.getFieldCodes(true).stream()
375
378
  .map(
@@ -386,6 +389,11 @@ public class KintonePageOutput implements TransactionalPageOutput {
386
389
  return field == null ? null : field.getType();
387
390
  }
388
391
 
392
+ private static boolean existsRecord(List<String> existingValues, UpdateKey updateKey) {
393
+ String value = toString(updateKey.getValue());
394
+ return value != null && existingValues.stream().anyMatch(v -> v.equals(value));
395
+ }
396
+
389
397
  private static String toString(Object value) {
390
398
  return value == null
391
399
  ? null
@@ -0,0 +1,33 @@
1
+ package org.embulk.output.kintone;
2
+
3
+ import com.fasterxml.jackson.annotation.JsonCreator;
4
+ import com.fasterxml.jackson.annotation.JsonProperty;
5
+
6
+ public class KintoneSortColumn {
7
+ private final String name;
8
+ private final Order order;
9
+
10
+ @JsonCreator
11
+ public KintoneSortColumn(@JsonProperty("name") String name, @JsonProperty("order") Order order) {
12
+ this.name = name;
13
+ this.order = order;
14
+ }
15
+
16
+ public String getName() {
17
+ return name;
18
+ }
19
+
20
+ public Order getOrder() {
21
+ return order;
22
+ }
23
+
24
+ public enum Order {
25
+ ASC,
26
+ DESC;
27
+
28
+ @JsonCreator
29
+ public static Order of(String name) {
30
+ return valueOf(name.toUpperCase());
31
+ }
32
+ }
33
+ }
@@ -1,10 +1,13 @@
1
1
  package org.embulk.output.kintone;
2
2
 
3
+ import java.util.List;
3
4
  import java.util.Map;
4
5
  import java.util.Optional;
6
+ import java.util.Set;
5
7
  import org.embulk.config.Config;
6
8
  import org.embulk.config.ConfigDefault;
7
9
  import org.embulk.config.Task;
10
+ import org.embulk.spi.Column;
8
11
 
9
12
  public interface PluginTask extends Task {
10
13
  @Config("domain")
@@ -57,7 +60,31 @@ public interface PluginTask extends Task {
57
60
  @ConfigDefault("null")
58
61
  Optional<String> getUpdateKeyName();
59
62
 
63
+ @Config("reduce_key")
64
+ @ConfigDefault("null")
65
+ Optional<String> getReduceKeyName();
66
+
67
+ @Config("sort_columns")
68
+ @ConfigDefault("[]")
69
+ List<KintoneSortColumn> getSortColumns();
70
+
71
+ @Config("max_sort_tmp_files")
72
+ @ConfigDefault("null")
73
+ Optional<Integer> getMaxSortTmpFiles();
74
+
75
+ @Config("max_sort_memory")
76
+ @ConfigDefault("null")
77
+ Optional<Long> getMaxSortMemory();
78
+
79
+ @Config("chunk_size")
80
+ @ConfigDefault("100")
81
+ Integer getChunkSize();
82
+
60
83
  @Config("retry_options")
61
84
  @ConfigDefault("{}")
62
85
  KintoneRetryOption getRetryOptions();
86
+
87
+ Set<Column> getDerivedColumns();
88
+
89
+ void setDerivedColumns(Set<Column> columns);
63
90
  }
@@ -0,0 +1,19 @@
1
+ package org.embulk.output.kintone.deserializer;
2
+
3
+ import com.fasterxml.jackson.core.JsonParser;
4
+ import com.fasterxml.jackson.databind.DeserializationContext;
5
+ import com.fasterxml.jackson.databind.JsonDeserializer;
6
+ import java.util.function.BiFunction;
7
+
8
+ public class DeserializeApplier<T> extends JsonDeserializer<T> {
9
+ private final BiFunction<JsonParser, DeserializationContext, T> deserializer;
10
+
11
+ public DeserializeApplier(BiFunction<JsonParser, DeserializationContext, T> deserializer) {
12
+ this.deserializer = deserializer;
13
+ }
14
+
15
+ @Override
16
+ public T deserialize(JsonParser parser, DeserializationContext context) {
17
+ return deserializer.apply(parser, context);
18
+ }
19
+ }
@@ -0,0 +1,7 @@
1
+ package org.embulk.output.kintone.deserializer;
2
+
3
+ public class DeserializeException extends RuntimeException {
4
+ public DeserializeException(Throwable cause) {
5
+ super(cause);
6
+ }
7
+ }
@@ -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
+ }