embulk-output-kintone 1.0.0 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (184) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +65 -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.2.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.2.0-all.jar} +0 -0
  8. data/src/main/java/org/embulk/output/kintone/KintoneClient.java +117 -0
  9. data/src/main/java/org/embulk/output/kintone/KintoneColumnOption.java +5 -0
  10. data/src/main/java/org/embulk/output/kintone/KintoneColumnType.java +226 -5
  11. data/src/main/java/org/embulk/output/kintone/KintoneColumnVisitor.java +82 -13
  12. data/src/main/java/org/embulk/output/kintone/KintoneMode.java +52 -4
  13. data/src/main/java/org/embulk/output/kintone/KintoneOutputPlugin.java +12 -20
  14. data/src/main/java/org/embulk/output/kintone/KintonePageOutput.java +123 -127
  15. data/src/main/java/org/embulk/output/kintone/KintoneSortColumn.java +33 -0
  16. data/src/main/java/org/embulk/output/kintone/PluginTask.java +32 -0
  17. data/src/main/java/org/embulk/output/kintone/deserializer/DeserializeApplier.java +19 -0
  18. data/src/main/java/org/embulk/output/kintone/deserializer/DeserializeException.java +7 -0
  19. data/src/main/java/org/embulk/output/kintone/deserializer/Deserializer.java +279 -0
  20. data/src/main/java/org/embulk/output/kintone/record/Id.java +18 -0
  21. data/src/main/java/org/embulk/output/kintone/record/IdOrUpdateKey.java +45 -0
  22. data/src/main/java/org/embulk/output/kintone/record/Skip.java +14 -0
  23. data/src/main/java/org/embulk/output/kintone/reducer/CSVInputColumnVisitor.java +78 -0
  24. data/src/main/java/org/embulk/output/kintone/reducer/CSVOutputColumnVisitor.java +79 -0
  25. data/src/main/java/org/embulk/output/kintone/reducer/ReduceException.java +11 -0
  26. data/src/main/java/org/embulk/output/kintone/reducer/ReduceType.java +190 -0
  27. data/src/main/java/org/embulk/output/kintone/reducer/ReducedPageOutput.java +100 -0
  28. data/src/main/java/org/embulk/output/kintone/reducer/Reducer.java +355 -0
  29. data/src/main/java/org/embulk/output/kintone/util/Lazy.java +20 -0
  30. data/src/test/java/org/embulk/output/kintone/KintoneClientTest.java +139 -0
  31. data/src/test/java/org/embulk/output/kintone/KintoneColumnOptionBuilder.java +7 -0
  32. data/src/test/java/org/embulk/output/kintone/KintoneColumnTypeTest.java +194 -0
  33. data/src/test/java/org/embulk/output/kintone/KintoneColumnVisitorTest.java +183 -64
  34. data/src/test/java/org/embulk/output/kintone/KintoneColumnVisitorVerifier.java +19 -9
  35. data/src/test/java/org/embulk/output/kintone/KintonePageOutputVerifier.java +120 -51
  36. data/src/test/java/org/embulk/output/kintone/MockClient.java +112 -0
  37. data/src/test/java/org/embulk/output/kintone/TestKintoneOutputPlugin.java +147 -22
  38. data/src/test/java/org/embulk/output/kintone/TestTaskMode.java +1 -0
  39. data/src/test/java/org/embulk/output/kintone/TestTaskReduce.java +47 -0
  40. data/src/test/java/org/embulk/output/kintone/TestTaskReduceException.java +50 -0
  41. data/src/test/java/org/embulk/output/kintone/TestTaskReduceSubtable.java +47 -0
  42. data/src/test/java/org/embulk/output/kintone/TestTaskSkip.java +80 -0
  43. data/src/test/java/org/embulk/output/kintone/TestTaskSkipId.java +80 -0
  44. data/src/test/java/org/embulk/output/kintone/deserializer/DeserializerTest.java +165 -0
  45. data/src/test/java/org/embulk/output/kintone/reducer/ReduceTypeTest.java +154 -0
  46. data/src/test/resources/org/embulk/output/kintone/client/config.yml +1 -0
  47. data/src/test/resources/org/embulk/output/kintone/task/config.yml +2 -1
  48. data/src/test/resources/org/embulk/output/kintone/task/mode/config.yml +6 -0
  49. data/src/test/resources/org/embulk/output/kintone/task/mode/input.csv +7 -7
  50. data/src/test/resources/org/embulk/output/kintone/task/mode/insert_add_records.jsonl +6 -6
  51. data/src/test/resources/org/embulk/output/kintone/task/mode/{insert_add_ignore_nulls_records.jsonl → insert_ignore_nulls_add_records.jsonl} +2 -2
  52. data/src/test/resources/org/embulk/output/kintone/task/mode/{insert_add_prefer_nulls_records.jsonl → insert_prefer_nulls_add_records.jsonl} +6 -6
  53. data/src/test/resources/org/embulk/output/kintone/task/mode/{update_update_ignore_nulls_records.jsonl → update_ignore_nulls_update_records.jsonl} +2 -2
  54. data/src/test/resources/org/embulk/output/kintone/task/mode/{update_update_prefer_nulls_records.jsonl → update_prefer_nulls_update_records.jsonl} +3 -3
  55. data/src/test/resources/org/embulk/output/kintone/task/mode/update_update_records.jsonl +6 -6
  56. data/src/test/resources/org/embulk/output/kintone/task/mode/{upsert_add_records.jsonl → upsert_never_skip_add_records.jsonl} +2 -2
  57. data/src/test/resources/org/embulk/output/kintone/task/mode/upsert_never_skip_double_single_line_text_add_values.json +1 -0
  58. data/src/test/resources/org/embulk/output/kintone/task/mode/{upsert_update_ignore_nulls_records.jsonl → upsert_never_skip_ignore_nulls_update_records.jsonl} +2 -2
  59. data/src/test/resources/org/embulk/output/kintone/task/mode/{upsert_add_prefer_nulls_records.jsonl → upsert_never_skip_prefer_nulls_add_records.jsonl} +3 -3
  60. data/src/test/resources/org/embulk/output/kintone/task/mode/{upsert_update_prefer_nulls_records.jsonl → upsert_never_skip_prefer_nulls_update_records.jsonl} +3 -3
  61. data/src/test/resources/org/embulk/output/kintone/task/mode/{upsert_update_records.jsonl → upsert_never_skip_update_records.jsonl} +4 -4
  62. data/src/test/resources/org/embulk/output/kintone/task/reduce/config.yml +171 -0
  63. data/src/test/resources/org/embulk/output/kintone/task/reduce/input.csv +7 -0
  64. data/src/test/resources/org/embulk/output/kintone/task/reduce/insert_add_records.jsonl +6 -0
  65. data/src/test/resources/org/embulk/output/kintone/task/reduce/insert_ignore_nulls_add_records.jsonl +6 -0
  66. data/src/test/resources/org/embulk/output/kintone/task/reduce/insert_prefer_nulls_add_records.jsonl +6 -0
  67. data/src/test/resources/org/embulk/output/kintone/task/reduce/update_ignore_nulls_update_records.jsonl +3 -0
  68. data/src/test/resources/org/embulk/output/kintone/task/reduce/update_prefer_nulls_update_records.jsonl +3 -0
  69. data/src/test/resources/org/embulk/output/kintone/task/reduce/update_update_records.jsonl +6 -0
  70. data/src/test/resources/org/embulk/output/kintone/task/reduce/upsert_never_skip_add_records.jsonl +2 -0
  71. data/src/test/resources/org/embulk/output/kintone/task/reduce/upsert_never_skip_double_single_line_text_add_values.json +1 -0
  72. data/src/test/resources/org/embulk/output/kintone/task/reduce/upsert_never_skip_double_single_line_text_values.json +1 -0
  73. data/src/test/resources/org/embulk/output/kintone/task/reduce/upsert_never_skip_ignore_nulls_add_records.jsonl +3 -0
  74. data/src/test/resources/org/embulk/output/kintone/task/reduce/upsert_never_skip_ignore_nulls_double_single_line_text_values.json +1 -0
  75. data/src/test/resources/org/embulk/output/kintone/task/reduce/upsert_never_skip_ignore_nulls_update_records.jsonl +3 -0
  76. data/src/test/resources/org/embulk/output/kintone/task/reduce/upsert_never_skip_prefer_nulls_add_records.jsonl +3 -0
  77. data/src/test/resources/org/embulk/output/kintone/task/reduce/upsert_never_skip_prefer_nulls_double_single_line_text_values.json +1 -0
  78. data/src/test/resources/org/embulk/output/kintone/task/reduce/upsert_never_skip_prefer_nulls_update_records.jsonl +3 -0
  79. data/src/test/resources/org/embulk/output/kintone/task/reduce/upsert_never_skip_update_records.jsonl +4 -0
  80. data/src/test/resources/org/embulk/output/kintone/task/reduce_exception/config.yml +36 -0
  81. data/src/test/resources/org/embulk/output/kintone/task/reduce_exception/derived_columns.json +1 -0
  82. data/src/test/resources/org/embulk/output/kintone/task/reduce_exception/input.csv +13 -0
  83. data/src/test/resources/org/embulk/output/kintone/task/reduce_exception/insert_add_records.jsonl +2 -0
  84. data/src/test/resources/org/embulk/output/kintone/task/reduce_exception/update_update_records.jsonl +2 -0
  85. data/src/test/resources/org/embulk/output/kintone/task/reduce_subtable/config.yml +343 -0
  86. data/src/test/resources/org/embulk/output/kintone/task/reduce_subtable/derived_columns.json +1 -0
  87. data/src/test/resources/org/embulk/output/kintone/task/reduce_subtable/input.csv +13 -0
  88. data/src/test/resources/org/embulk/output/kintone/task/reduce_subtable/insert_add_records.jsonl +6 -0
  89. data/src/test/resources/org/embulk/output/kintone/task/reduce_subtable/insert_ignore_nulls_add_records.jsonl +6 -0
  90. data/src/test/resources/org/embulk/output/kintone/task/reduce_subtable/insert_prefer_nulls_add_records.jsonl +6 -0
  91. data/src/test/resources/org/embulk/output/kintone/task/reduce_subtable/update_ignore_nulls_update_records.jsonl +3 -0
  92. data/src/test/resources/org/embulk/output/kintone/task/reduce_subtable/update_prefer_nulls_update_records.jsonl +3 -0
  93. data/src/test/resources/org/embulk/output/kintone/task/reduce_subtable/update_update_records.jsonl +6 -0
  94. data/src/test/resources/org/embulk/output/kintone/task/reduce_subtable/upsert_never_skip_double_single_line_text_values.json +1 -0
  95. data/src/test/resources/org/embulk/output/kintone/task/reduce_subtable/upsert_never_skip_ignore_nulls_add_records.jsonl +3 -0
  96. data/src/test/resources/org/embulk/output/kintone/task/reduce_subtable/upsert_never_skip_ignore_nulls_double_single_line_text_values.json +1 -0
  97. data/src/test/resources/org/embulk/output/kintone/task/reduce_subtable/upsert_never_skip_ignore_nulls_update_records.jsonl +3 -0
  98. data/src/test/resources/org/embulk/output/kintone/task/reduce_subtable/upsert_never_skip_prefer_nulls_add_records.jsonl +3 -0
  99. data/src/test/resources/org/embulk/output/kintone/task/reduce_subtable/upsert_never_skip_prefer_nulls_double_single_line_text_values.json +1 -0
  100. data/src/test/resources/org/embulk/output/kintone/task/reduce_subtable/upsert_never_skip_prefer_nulls_update_records.jsonl +3 -0
  101. data/src/test/resources/org/embulk/output/kintone/task/reduce_subtable/upsert_never_skip_update_records.jsonl +6 -0
  102. data/src/test/resources/org/embulk/output/kintone/task/skip/config.yml +1 -0
  103. data/src/test/resources/org/embulk/output/kintone/task/skip/input.csv +7 -0
  104. data/src/test/resources/org/embulk/output/kintone/task/skip/insert_add_records.jsonl +6 -0
  105. data/src/test/resources/org/embulk/output/kintone/task/skip/insert_always_skip_add_records.jsonl +6 -0
  106. data/src/test/resources/org/embulk/output/kintone/task/skip/insert_always_skip_prefer_nulls_add_records.jsonl +6 -0
  107. data/src/test/resources/org/embulk/output/kintone/task/skip/insert_never_skip_add_records.jsonl +6 -0
  108. data/src/test/resources/org/embulk/output/kintone/task/skip/insert_never_skip_prefer_nulls_add_records.jsonl +6 -0
  109. data/src/test/resources/org/embulk/output/kintone/task/skip/insert_prefer_nulls_add_records.jsonl +6 -0
  110. data/src/test/resources/org/embulk/output/kintone/task/skip/update_always_skip_long_number_add_values.json +1 -0
  111. data/src/test/resources/org/embulk/output/kintone/task/skip/update_always_skip_long_number_values.json +1 -0
  112. data/src/test/resources/org/embulk/output/kintone/task/skip/update_always_skip_prefer_nulls_long_number_add_values.json +1 -0
  113. data/src/test/resources/org/embulk/output/kintone/task/skip/update_always_skip_prefer_nulls_long_number_values.json +1 -0
  114. data/src/test/resources/org/embulk/output/kintone/task/skip/update_always_skip_prefer_nulls_update_records.jsonl +2 -0
  115. data/src/test/resources/org/embulk/output/kintone/task/skip/update_always_skip_update_records.jsonl +5 -0
  116. data/src/test/resources/org/embulk/output/kintone/task/skip/update_never_skip_update_records.jsonl +6 -0
  117. data/src/test/resources/org/embulk/output/kintone/task/skip/update_prefer_nulls_update_records.jsonl +3 -0
  118. data/src/test/resources/org/embulk/output/kintone/task/skip/update_update_records.jsonl +6 -0
  119. data/src/test/resources/org/embulk/output/kintone/task/skip/upsert_add_records.jsonl +1 -0
  120. data/src/test/resources/org/embulk/output/kintone/task/skip/upsert_always_skip_prefer_nulls_string_single_line_text_add_values.json +1 -0
  121. data/src/test/resources/org/embulk/output/kintone/task/skip/upsert_always_skip_prefer_nulls_string_single_line_text_values.json +1 -0
  122. data/src/test/resources/org/embulk/output/kintone/task/skip/upsert_always_skip_prefer_nulls_update_records.jsonl +2 -0
  123. data/src/test/resources/org/embulk/output/kintone/task/skip/upsert_always_skip_string_single_line_text_add_values.json +1 -0
  124. data/src/test/resources/org/embulk/output/kintone/task/skip/upsert_always_skip_string_single_line_text_values.json +1 -0
  125. data/src/test/resources/org/embulk/output/kintone/task/skip/upsert_always_skip_update_records.jsonl +2 -0
  126. data/src/test/resources/org/embulk/output/kintone/task/skip/upsert_never_skip_add_records.jsonl +4 -0
  127. data/src/test/resources/org/embulk/output/kintone/task/skip/upsert_never_skip_prefer_nulls_add_records.jsonl +4 -0
  128. data/src/test/resources/org/embulk/output/kintone/task/skip/upsert_never_skip_prefer_nulls_string_single_line_text_add_values.json +1 -0
  129. data/src/test/resources/org/embulk/output/kintone/task/skip/upsert_never_skip_prefer_nulls_string_single_line_text_values.json +1 -0
  130. data/src/test/resources/org/embulk/output/kintone/task/skip/upsert_never_skip_prefer_nulls_update_records.jsonl +2 -0
  131. data/src/test/resources/org/embulk/output/kintone/task/skip/upsert_never_skip_string_single_line_text_add_values.json +1 -0
  132. data/src/test/resources/org/embulk/output/kintone/task/skip/upsert_never_skip_string_single_line_text_values.json +1 -0
  133. data/src/test/resources/org/embulk/output/kintone/task/skip/upsert_never_skip_update_records.jsonl +2 -0
  134. data/src/test/resources/org/embulk/output/kintone/task/skip/upsert_prefer_nulls_add_records.jsonl +1 -0
  135. data/src/test/resources/org/embulk/output/kintone/task/skip/upsert_prefer_nulls_string_single_line_text_add_values.json +1 -0
  136. data/src/test/resources/org/embulk/output/kintone/task/skip/upsert_prefer_nulls_string_single_line_text_values.json +1 -0
  137. data/src/test/resources/org/embulk/output/kintone/task/skip/upsert_prefer_nulls_update_records.jsonl +2 -0
  138. data/src/test/resources/org/embulk/output/kintone/task/skip/upsert_string_single_line_text_add_values.json +1 -0
  139. data/src/test/resources/org/embulk/output/kintone/task/skip/upsert_string_single_line_text_values.json +1 -0
  140. data/src/test/resources/org/embulk/output/kintone/task/skip/upsert_update_records.jsonl +2 -0
  141. data/src/test/resources/org/embulk/output/kintone/task/skip_id/config.yml +1 -0
  142. data/src/test/resources/org/embulk/output/kintone/task/skip_id/input.csv +7 -0
  143. data/src/test/resources/org/embulk/output/kintone/task/skip_id/insert_add_records.jsonl +6 -0
  144. data/src/test/resources/org/embulk/output/kintone/task/skip_id/insert_always_skip_add_records.jsonl +6 -0
  145. data/src/test/resources/org/embulk/output/kintone/task/skip_id/insert_always_skip_prefer_nulls_add_records.jsonl +6 -0
  146. data/src/test/resources/org/embulk/output/kintone/task/skip_id/insert_never_skip_add_records.jsonl +6 -0
  147. data/src/test/resources/org/embulk/output/kintone/task/skip_id/insert_never_skip_prefer_nulls_add_records.jsonl +6 -0
  148. data/src/test/resources/org/embulk/output/kintone/task/skip_id/insert_prefer_nulls_add_records.jsonl +6 -0
  149. data/src/test/resources/org/embulk/output/kintone/task/skip_id/update_always_skip__id_add_values.json +1 -0
  150. data/src/test/resources/org/embulk/output/kintone/task/skip_id/update_always_skip__id_values.json +1 -0
  151. data/src/test/resources/org/embulk/output/kintone/task/skip_id/update_always_skip_prefer_nulls__id_add_values.json +1 -0
  152. data/src/test/resources/org/embulk/output/kintone/task/skip_id/update_always_skip_prefer_nulls__id_values.json +1 -0
  153. data/src/test/resources/org/embulk/output/kintone/task/skip_id/update_always_skip_prefer_nulls_update_records.jsonl +2 -0
  154. data/src/test/resources/org/embulk/output/kintone/task/skip_id/update_always_skip_update_records.jsonl +5 -0
  155. data/src/test/resources/org/embulk/output/kintone/task/skip_id/update_never_skip_update_records.jsonl +6 -0
  156. data/src/test/resources/org/embulk/output/kintone/task/skip_id/update_prefer_nulls_update_records.jsonl +3 -0
  157. data/src/test/resources/org/embulk/output/kintone/task/skip_id/update_update_records.jsonl +6 -0
  158. data/src/test/resources/org/embulk/output/kintone/task/skip_id/upsert__id_add_values.json +1 -0
  159. data/src/test/resources/org/embulk/output/kintone/task/skip_id/upsert__id_values.json +1 -0
  160. data/src/test/resources/org/embulk/output/kintone/task/skip_id/upsert_always_skip__id_add_values.json +1 -0
  161. data/src/test/resources/org/embulk/output/kintone/task/skip_id/upsert_always_skip__id_values.json +1 -0
  162. data/src/test/resources/org/embulk/output/kintone/task/skip_id/upsert_always_skip_prefer_nulls__id_add_values.json +1 -0
  163. data/src/test/resources/org/embulk/output/kintone/task/skip_id/upsert_always_skip_prefer_nulls__id_values.json +1 -0
  164. data/src/test/resources/org/embulk/output/kintone/task/skip_id/upsert_always_skip_prefer_nulls_update_records.jsonl +2 -0
  165. data/src/test/resources/org/embulk/output/kintone/task/skip_id/upsert_always_skip_update_records.jsonl +5 -0
  166. data/src/test/resources/org/embulk/output/kintone/task/skip_id/upsert_never_skip__id_add_values.json +1 -0
  167. data/src/test/resources/org/embulk/output/kintone/task/skip_id/upsert_never_skip__id_values.json +1 -0
  168. data/src/test/resources/org/embulk/output/kintone/task/skip_id/upsert_never_skip_add_records.jsonl +1 -0
  169. data/src/test/resources/org/embulk/output/kintone/task/skip_id/upsert_never_skip_prefer_nulls__id_add_values.json +1 -0
  170. data/src/test/resources/org/embulk/output/kintone/task/skip_id/upsert_never_skip_prefer_nulls__id_values.json +1 -0
  171. data/src/test/resources/org/embulk/output/kintone/task/skip_id/upsert_never_skip_prefer_nulls_add_records.jsonl +4 -0
  172. data/src/test/resources/org/embulk/output/kintone/task/skip_id/upsert_never_skip_prefer_nulls_update_records.jsonl +2 -0
  173. data/src/test/resources/org/embulk/output/kintone/task/skip_id/upsert_never_skip_update_records.jsonl +5 -0
  174. data/src/test/resources/org/embulk/output/kintone/task/skip_id/upsert_prefer_nulls__id_add_values.json +1 -0
  175. data/src/test/resources/org/embulk/output/kintone/task/skip_id/upsert_prefer_nulls__id_values.json +1 -0
  176. data/src/test/resources/org/embulk/output/kintone/task/skip_id/upsert_prefer_nulls_add_records.jsonl +3 -0
  177. data/src/test/resources/org/embulk/output/kintone/task/skip_id/upsert_prefer_nulls_update_records.jsonl +2 -0
  178. data/src/test/resources/org/embulk/output/kintone/task/skip_id/upsert_update_records.jsonl +5 -0
  179. metadata +163 -17
  180. data/classpath/embulk-output-kintone-1.0.0.jar +0 -0
  181. /data/src/test/resources/org/embulk/output/kintone/task/mode/{values.json → upsert_never_skip_double_single_line_text_values.json} +0 -0
  182. /data/src/test/resources/org/embulk/output/kintone/task/mode/{upsert_add_ignore_nulls_records.jsonl → upsert_never_skip_ignore_nulls_add_records.jsonl} +0 -0
  183. /data/src/test/resources/org/embulk/output/kintone/task/mode/{values_ignore_nulls.json → upsert_never_skip_ignore_nulls_double_single_line_text_values.json} +0 -0
  184. /data/src/test/resources/org/embulk/output/kintone/task/mode/{values_prefer_nulls.json → upsert_never_skip_prefer_nulls_double_single_line_text_values.json} +0 -0
@@ -5,15 +5,11 @@ import static org.embulk.spi.util.RetryExecutor.retryExecutor;
5
5
  import com.fasterxml.jackson.databind.JsonNode;
6
6
  import com.fasterxml.jackson.databind.ObjectMapper;
7
7
  import com.google.common.collect.Maps;
8
- import com.kintone.client.KintoneClient;
9
- import com.kintone.client.KintoneClientBuilder;
10
8
  import com.kintone.client.api.record.GetRecordsByCursorResponseBody;
11
9
  import com.kintone.client.exception.KintoneApiRuntimeException;
12
- import com.kintone.client.model.app.field.FieldProperty;
13
10
  import com.kintone.client.model.record.FieldType;
14
11
  import com.kintone.client.model.record.Record;
15
12
  import com.kintone.client.model.record.RecordForUpdate;
16
- import com.kintone.client.model.record.UpdateKey;
17
13
  import java.io.IOException;
18
14
  import java.lang.invoke.MethodHandles;
19
15
  import java.math.BigDecimal;
@@ -28,8 +24,11 @@ import java.util.function.Function;
28
24
  import java.util.function.Supplier;
29
25
  import java.util.stream.Collectors;
30
26
  import org.apache.commons.lang3.tuple.Pair;
31
- import org.embulk.config.ConfigException;
32
27
  import org.embulk.config.TaskReport;
28
+ import org.embulk.output.kintone.record.Id;
29
+ import org.embulk.output.kintone.record.IdOrUpdateKey;
30
+ import org.embulk.output.kintone.record.Skip;
31
+ import org.embulk.output.kintone.util.Lazy;
33
32
  import org.embulk.spi.Exec;
34
33
  import org.embulk.spi.Page;
35
34
  import org.embulk.spi.PageReader;
@@ -50,34 +49,20 @@ public class KintonePageOutput implements TransactionalPageOutput {
50
49
  "GAIA_DA02" // データベースのロックに失敗したため、変更を保存できませんでした。時間をおいて再度お試しください。
51
50
  );
52
51
  private static final int UPSERT_BATCH_SIZE = 10000;
53
- private static final int CHUNK_SIZE = 100;
54
52
  private final Map<String, Pair<FieldType, FieldType>> wrongTypeFields = new TreeMap<>();
55
53
  private final PluginTask task;
56
54
  private final PageReader reader;
57
- private KintoneClient client;
58
- private Map<String, FieldProperty> formFields;
55
+ private final Lazy<KintoneClient> client;
59
56
 
60
57
  public KintonePageOutput(PluginTask task, Schema schema) {
61
58
  this.task = task;
62
59
  reader = new PageReader(schema);
60
+ client = KintoneClient.lazy(() -> task, schema);
63
61
  }
64
62
 
65
63
  @Override
66
64
  public void add(Page page) {
67
- KintoneMode mode = KintoneMode.getKintoneModeByValue(task.getMode());
68
- switch (mode) {
69
- case INSERT:
70
- insertPage(page);
71
- break;
72
- case UPDATE:
73
- updatePage(page);
74
- break;
75
- case UPSERT:
76
- upsertPage(page);
77
- break;
78
- default:
79
- throw new UnsupportedOperationException(String.format("Unknown mode '%s'", task.getMode()));
80
- }
65
+ KintoneMode.of(task).add(page, task.getSkipIfNonExistingIdOrUpdateKey(), this);
81
66
  }
82
67
 
83
68
  @Override
@@ -87,14 +72,7 @@ public class KintonePageOutput implements TransactionalPageOutput {
87
72
 
88
73
  @Override
89
74
  public void close() {
90
- if (client == null) {
91
- return; // Not connected
92
- }
93
- try {
94
- client.close();
95
- } catch (Exception e) {
96
- throw new RuntimeException("kintone throw exception", e);
97
- }
75
+ client.get().close();
98
76
  }
99
77
 
100
78
  @Override
@@ -113,38 +91,15 @@ public class KintonePageOutput implements TransactionalPageOutput {
113
91
  return Exec.newTaskReport();
114
92
  }
115
93
 
116
- public void connectIfNeeded() {
117
- if (client != null) {
118
- return; // Already connected
119
- }
120
- KintoneClientBuilder builder = KintoneClientBuilder.create("https://" + task.getDomain());
121
- if (task.getGuestSpaceId().isPresent()) {
122
- builder.setGuestSpaceId(task.getGuestSpaceId().get());
123
- }
124
- if (task.getBasicAuthUsername().isPresent() && task.getBasicAuthPassword().isPresent()) {
125
- builder.withBasicAuth(task.getBasicAuthUsername().get(), task.getBasicAuthPassword().get());
126
- }
127
- if (task.getUsername().isPresent() && task.getPassword().isPresent()) {
128
- builder.authByPassword(task.getUsername().get(), task.getPassword().get());
129
- } else if (task.getToken().isPresent()) {
130
- builder.authByApiToken(task.getToken().get());
131
- } else {
132
- throw new ConfigException("Username and password or token must be configured.");
133
- }
134
- client = builder.build();
135
- formFields = client.app().getFormFields(task.getAppId());
136
- }
137
-
138
94
  private void insert(List<Record> records) {
139
- executeWithRetry(() -> client.record().addRecords(task.getAppId(), records));
95
+ executeWithRetry(() -> client.get().record().addRecords(task.getAppId(), records));
140
96
  }
141
97
 
142
98
  private void update(List<RecordForUpdate> records) {
143
- executeWithRetry(() -> client.record().updateRecords(task.getAppId(), records));
99
+ executeWithRetry(() -> client.get().record().updateRecords(task.getAppId(), records));
144
100
  }
145
101
 
146
102
  private <T> T executeWithRetry(Supplier<T> operation) {
147
- connectIfNeeded();
148
103
  KintoneRetryOption retryOption = task.getRetryOptions();
149
104
  try {
150
105
  return retryExecutor()
@@ -154,7 +109,7 @@ public class KintonePageOutput implements TransactionalPageOutput {
154
109
  .runInterruptible(
155
110
  new Retryable<T>() {
156
111
  @Override
157
- public T call() throws Exception {
112
+ public T call() {
158
113
  return operation.get();
159
114
  }
160
115
 
@@ -176,8 +131,7 @@ public class KintonePageOutput implements TransactionalPageOutput {
176
131
 
177
132
  @Override
178
133
  public void onRetry(
179
- Exception exception, int retryCount, int retryLimit, int retryWait)
180
- throws RetryGiveupException {
134
+ Exception exception, int retryCount, int retryLimit, int retryWait) {
181
135
  String message =
182
136
  String.format(
183
137
  "Retrying %d/%d after %d seconds. Message: %s",
@@ -190,27 +144,31 @@ public class KintonePageOutput implements TransactionalPageOutput {
190
144
  }
191
145
 
192
146
  @Override
193
- public void onGiveup(Exception firstException, Exception lastException)
194
- throws RetryGiveupException {}
147
+ public void onGiveup(Exception firstException, Exception lastException) {}
195
148
  });
196
149
  } catch (RetryGiveupException | InterruptedException e) {
197
150
  throw new RuntimeException("kintone throw exception", e);
198
151
  }
199
152
  }
200
153
 
201
- private void insertPage(Page page) {
154
+ public void insertPage(Page page) {
202
155
  List<Record> records = new ArrayList<>();
203
156
  reader.setPage(page);
204
157
  KintoneColumnVisitor visitor =
205
158
  new KintoneColumnVisitor(
206
- reader, task.getColumnOptions(), task.getPreferNulls(), task.getIgnoreNulls());
159
+ reader,
160
+ task.getDerivedColumns(),
161
+ task.getColumnOptions(),
162
+ task.getPreferNulls(),
163
+ task.getIgnoreNulls(),
164
+ task.getReduceKeyName().orElse(null));
207
165
  while (reader.nextRecord()) {
208
166
  Record record = new Record();
209
167
  visitor.setRecord(record);
210
168
  reader.getSchema().visitColumns(visitor);
211
169
  putWrongTypeFields(record);
212
170
  records.add(record);
213
- if (records.size() == CHUNK_SIZE) {
171
+ if (records.size() == task.getChunkSize()) {
214
172
  insert(records);
215
173
  records.clear();
216
174
  }
@@ -220,30 +178,34 @@ public class KintonePageOutput implements TransactionalPageOutput {
220
178
  }
221
179
  }
222
180
 
223
- private void updatePage(Page page) {
181
+ public void updatePage(Page page) {
182
+ Skip skip = task.getSkipIfNonExistingIdOrUpdateKey();
224
183
  List<RecordForUpdate> records = new ArrayList<>();
225
184
  reader.setPage(page);
226
185
  KintoneColumnVisitor visitor =
227
186
  new KintoneColumnVisitor(
228
187
  reader,
188
+ task.getDerivedColumns(),
229
189
  task.getColumnOptions(),
230
190
  task.getPreferNulls(),
231
191
  task.getIgnoreNulls(),
232
- task.getUpdateKeyName()
233
- .orElseThrow(() -> new RuntimeException("unreachable"))); // Already validated
192
+ task.getReduceKeyName().orElse(null),
193
+ task.getUpdateKeyName().orElse(Id.FIELD));
234
194
  while (reader.nextRecord()) {
235
195
  Record record = new Record();
236
- UpdateKey updateKey = new UpdateKey();
196
+ IdOrUpdateKey idOrUpdateKey = new IdOrUpdateKey();
237
197
  visitor.setRecord(record);
238
- visitor.setUpdateKey(updateKey);
198
+ visitor.setIdOrUpdateKey(idOrUpdateKey);
239
199
  reader.getSchema().visitColumns(visitor);
240
200
  putWrongTypeFields(record);
241
- if (updateKey.getValue() == null || updateKey.getValue().toString().isEmpty()) {
242
- LOGGER.warn("Record skipped because no update key value was specified");
201
+ if (skip == Skip.NEVER && !idOrUpdateKey.isPresent()) {
202
+ throw new RuntimeException("No id or update key value was specified");
203
+ } else if (!idOrUpdateKey.isPresent()) {
204
+ LOGGER.warn("Record skipped because no id or update key value was specified");
243
205
  continue;
244
206
  }
245
- records.add(new RecordForUpdate(updateKey, record.removeField(updateKey.getField())));
246
- if (records.size() == CHUNK_SIZE) {
207
+ records.add(idOrUpdateKey.forUpdate(record));
208
+ if (records.size() == task.getChunkSize()) {
247
209
  update(records);
248
210
  records.clear();
249
211
  }
@@ -253,57 +215,91 @@ public class KintonePageOutput implements TransactionalPageOutput {
253
215
  }
254
216
  }
255
217
 
256
- private void upsertPage(Page page) {
218
+ public void upsertPage(Page page) {
257
219
  List<Record> records = new ArrayList<>();
258
- List<UpdateKey> updateKeys = new ArrayList<>();
220
+ List<IdOrUpdateKey> idOrUpdateKeys = new ArrayList<>();
259
221
  reader.setPage(page);
260
222
  KintoneColumnVisitor visitor =
261
223
  new KintoneColumnVisitor(
262
224
  reader,
225
+ task.getDerivedColumns(),
263
226
  task.getColumnOptions(),
264
227
  task.getPreferNulls(),
265
228
  task.getIgnoreNulls(),
266
- task.getUpdateKeyName()
267
- .orElseThrow(() -> new RuntimeException("unreachable"))); // Already validated
229
+ task.getReduceKeyName().orElse(null),
230
+ task.getUpdateKeyName().orElse(Id.FIELD));
268
231
  while (reader.nextRecord()) {
269
232
  Record record = new Record();
270
- UpdateKey updateKey = new UpdateKey();
233
+ IdOrUpdateKey idOrUpdateKey = new IdOrUpdateKey();
271
234
  visitor.setRecord(record);
272
- visitor.setUpdateKey(updateKey);
235
+ visitor.setIdOrUpdateKey(idOrUpdateKey);
273
236
  reader.getSchema().visitColumns(visitor);
274
237
  putWrongTypeFields(record);
275
238
  records.add(record);
276
- updateKeys.add(updateKey);
239
+ idOrUpdateKeys.add(idOrUpdateKey);
277
240
  if (records.size() == UPSERT_BATCH_SIZE) {
278
- upsert(records, updateKeys);
241
+ upsert(records, idOrUpdateKeys);
279
242
  records.clear();
280
- updateKeys.clear();
243
+ idOrUpdateKeys.clear();
281
244
  }
282
245
  }
283
246
  if (!records.isEmpty()) {
284
- upsert(records, updateKeys);
247
+ upsert(records, idOrUpdateKeys);
285
248
  }
286
249
  }
287
250
 
288
- private void upsert(List<Record> records, List<UpdateKey> updateKeys) {
289
- if (records.size() != updateKeys.size()) {
290
- throw new RuntimeException("records.size() != updateKeys.size()");
251
+ private void upsert(List<Record> records, List<IdOrUpdateKey> idOrUpdateKeys) {
252
+ if (records.size() != idOrUpdateKeys.size()) {
253
+ throw new RuntimeException("records.size() != idOrUpdateKeys.size()");
291
254
  }
292
- List<String> existingValues = executeWithRetry(() -> getExistingValuesByUpdateKey(updateKeys));
255
+ Skip skip = task.getSkipIfNonExistingIdOrUpdateKey();
256
+ String columnName = task.getUpdateKeyName().orElse(Id.FIELD);
257
+ boolean isId = columnName.equals(Id.FIELD);
258
+ List<String> existingValues =
259
+ executeWithRetry(() -> getExistingValuesByIdOrUpdateKey(idOrUpdateKeys, columnName));
293
260
  List<Record> insertRecords = new ArrayList<>();
294
261
  List<RecordForUpdate> updateRecords = new ArrayList<>();
295
262
  for (int i = 0; i < records.size(); i++) {
263
+ RecordForUpdate recordForUpdate = null;
296
264
  Record record = records.get(i);
297
- UpdateKey updateKey = updateKeys.get(i);
298
- if (existsRecord(existingValues, updateKey)) {
299
- updateRecords.add(new RecordForUpdate(updateKey, record.removeField(updateKey.getField())));
265
+ IdOrUpdateKey idOrUpdateKey = idOrUpdateKeys.get(i);
266
+ if (existsRecord(existingValues, idOrUpdateKey)) {
267
+ recordForUpdate = idOrUpdateKey.forUpdate(record);
268
+ } else if (skip == Skip.ALWAYS && idOrUpdateKey.isPresent()) {
269
+ LOGGER.warn(
270
+ "Record skipped because non existing id or update key '"
271
+ + idOrUpdateKey.getValue()
272
+ + "' was specified");
273
+ continue;
274
+ } else if (skip == Skip.ALWAYS && !idOrUpdateKey.isPresent()) {
275
+ LOGGER.warn("Record skipped because no id or update key value was specified");
276
+ continue;
277
+ } else if (skip == Skip.AUTO && idOrUpdateKey.isIdPresent()) {
278
+ LOGGER.warn(
279
+ "Record skipped because non existing id '"
280
+ + idOrUpdateKey.getValue()
281
+ + "' was specified");
282
+ continue;
283
+ } else if (skip == Skip.AUTO && !isId && !idOrUpdateKey.isUpdateKeyPresent()) {
284
+ LOGGER.warn("Record skipped because no update key value was specified");
285
+ continue;
286
+ } else if (idOrUpdateKey.isIdPresent()) {
287
+ LOGGER.warn(
288
+ "Record inserted though non existing id '"
289
+ + idOrUpdateKey.getValue()
290
+ + "' was specified");
291
+ } else if (!isId && !idOrUpdateKey.isUpdateKeyPresent()) {
292
+ LOGGER.warn("Record inserted though no update key value was specified");
293
+ }
294
+ if (recordForUpdate != null) {
295
+ updateRecords.add(recordForUpdate);
300
296
  } else {
301
297
  insertRecords.add(record);
302
298
  }
303
- if (insertRecords.size() == CHUNK_SIZE) {
299
+ if (insertRecords.size() == task.getChunkSize()) {
304
300
  insert(insertRecords);
305
301
  insertRecords.clear();
306
- } else if (updateRecords.size() == CHUNK_SIZE) {
302
+ } else if (updateRecords.size() == task.getChunkSize()) {
307
303
  update(updateRecords);
308
304
  updateRecords.clear();
309
305
  }
@@ -316,60 +312,57 @@ public class KintonePageOutput implements TransactionalPageOutput {
316
312
  }
317
313
  }
318
314
 
319
- private List<String> getExistingValuesByUpdateKey(List<UpdateKey> updateKeys) {
320
- String fieldCode =
321
- updateKeys.stream()
322
- .map(UpdateKey::getField)
323
- .filter(Objects::nonNull)
324
- .findFirst()
325
- .orElse(null);
326
- if (fieldCode == null) {
327
- return Collections.emptyList();
328
- }
329
- Function<Record, String> fieldValueAsString;
330
- FieldType fieldType = getFieldType(fieldCode);
331
- if (fieldType == FieldType.SINGLE_LINE_TEXT) {
332
- fieldValueAsString = record -> record.getSingleLineTextFieldValue(fieldCode);
333
- } else if (fieldType == FieldType.NUMBER) {
334
- fieldValueAsString = record -> toString(record.getNumberFieldValue(fieldCode));
335
- } else {
336
- throw new ConfigException("The update_key must be 'SINGLE_LINE_TEXT' or 'NUMBER'.");
337
- }
315
+ private List<String> getExistingValuesByIdOrUpdateKey(
316
+ List<IdOrUpdateKey> idOrUpdateKeys, String columnName) {
338
317
  List<String> queryValues =
339
- updateKeys.stream()
340
- .filter(k -> k.getValue() != null && !k.getValue().toString().isEmpty())
318
+ idOrUpdateKeys.stream()
319
+ .filter(IdOrUpdateKey::isPresent)
341
320
  .map(k -> "\"" + k.getValue() + "\"")
342
321
  .collect(Collectors.toList());
343
322
  if (queryValues.isEmpty()) {
344
323
  return Collections.emptyList();
345
324
  }
325
+ return columnName.equals(Id.FIELD)
326
+ ? getExistingValuesById(queryValues)
327
+ : getExistingValuesByUpdateKey(columnName, queryValues);
328
+ }
329
+
330
+ private List<String> getExistingValuesById(List<String> queryValues) {
331
+ return getExistingValues(Id.FIELD, Record::getId, queryValues);
332
+ }
333
+
334
+ private List<String> getExistingValuesByUpdateKey(String columnName, List<String> queryValues) {
335
+ KintoneColumnOption option = task.getColumnOptions().get(columnName);
336
+ String fieldCode = option != null ? option.getFieldCode() : columnName;
337
+ KintoneColumnType type = KintoneColumnType.valueOf(getFieldType(fieldCode).name());
338
+ return getExistingValues(fieldCode, record -> type.getValue(record, fieldCode), queryValues);
339
+ }
340
+
341
+ private List<String> getExistingValues(
342
+ String fieldCode, Function<Record, Object> toValue, List<String> queryValues) {
346
343
  String cursorId =
347
344
  client
345
+ .get()
348
346
  .record()
349
347
  .createCursor(
350
348
  task.getAppId(),
351
349
  Collections.singletonList(fieldCode),
352
350
  fieldCode + " in (" + String.join(",", queryValues) + ")");
353
- List<Record> allRecords = new ArrayList<>();
351
+ List<Record> records = new ArrayList<>();
354
352
  while (true) {
355
- GetRecordsByCursorResponseBody resp = client.record().getRecordsByCursor(cursorId);
356
- List<Record> records = resp.getRecords();
357
- allRecords.addAll(records);
358
- if (!resp.hasNext()) {
353
+ GetRecordsByCursorResponseBody cursor = client.get().record().getRecordsByCursor(cursorId);
354
+ records.addAll(cursor.getRecords());
355
+ if (!cursor.hasNext()) {
359
356
  break;
360
357
  }
361
358
  }
362
- return allRecords.stream()
363
- .map(fieldValueAsString)
359
+ return records.stream()
360
+ .map(toValue)
361
+ .map(KintonePageOutput::toString)
364
362
  .filter(Objects::nonNull)
365
363
  .collect(Collectors.toList());
366
364
  }
367
365
 
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
366
  private void putWrongTypeFields(Record record) {
374
367
  record.getFieldCodes(true).stream()
375
368
  .map(
@@ -381,9 +374,12 @@ public class KintonePageOutput implements TransactionalPageOutput {
381
374
  }
382
375
 
383
376
  private FieldType getFieldType(String fieldCode) {
384
- connectIfNeeded();
385
- FieldProperty field = formFields.get(fieldCode);
386
- return field == null ? null : field.getType();
377
+ return client.get().getFieldType(fieldCode);
378
+ }
379
+
380
+ private static boolean existsRecord(List<String> existingValues, IdOrUpdateKey idOrUpdateKey) {
381
+ String value = toString(idOrUpdateKey.getValue());
382
+ return value != null && existingValues.stream().anyMatch(v -> v.equals(value));
387
383
  }
388
384
 
389
385
  private static String toString(Object value) {
@@ -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,14 @@
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.output.kintone.record.Skip;
11
+ import org.embulk.spi.Column;
8
12
 
9
13
  public interface PluginTask extends Task {
10
14
  @Config("domain")
@@ -49,6 +53,10 @@ public interface PluginTask extends Task {
49
53
  @ConfigDefault("\"false\"")
50
54
  boolean getIgnoreNulls();
51
55
 
56
+ @Config("skip_if_non_existing_id_or_update_key")
57
+ @ConfigDefault("\"auto\"")
58
+ Skip getSkipIfNonExistingIdOrUpdateKey();
59
+
52
60
  @Config("mode")
53
61
  @ConfigDefault("\"insert\"")
54
62
  String getMode();
@@ -57,7 +65,31 @@ public interface PluginTask extends Task {
57
65
  @ConfigDefault("null")
58
66
  Optional<String> getUpdateKeyName();
59
67
 
68
+ @Config("reduce_key")
69
+ @ConfigDefault("null")
70
+ Optional<String> getReduceKeyName();
71
+
72
+ @Config("sort_columns")
73
+ @ConfigDefault("[]")
74
+ List<KintoneSortColumn> getSortColumns();
75
+
76
+ @Config("max_sort_tmp_files")
77
+ @ConfigDefault("null")
78
+ Optional<Integer> getMaxSortTmpFiles();
79
+
80
+ @Config("max_sort_memory")
81
+ @ConfigDefault("null")
82
+ Optional<Long> getMaxSortMemory();
83
+
84
+ @Config("chunk_size")
85
+ @ConfigDefault("100")
86
+ Integer getChunkSize();
87
+
60
88
  @Config("retry_options")
61
89
  @ConfigDefault("{}")
62
90
  KintoneRetryOption getRetryOptions();
91
+
92
+ Set<Column> getDerivedColumns();
93
+
94
+ void setDerivedColumns(Set<Column> columns);
63
95
  }
@@ -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
+ }