embulk-input-zendesk-all 0.3.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +12 -0
  3. data/.travis.yml +5 -0
  4. data/CHANGELOG.md +126 -0
  5. data/LICENSE.txt +21 -0
  6. data/README.md +91 -0
  7. data/build.gradle +123 -0
  8. data/config/checkstyle/checkstyle.xml +128 -0
  9. data/config/checkstyle/default.xml +108 -0
  10. data/gradle/wrapper/gradle-wrapper.jar +0 -0
  11. data/gradle/wrapper/gradle-wrapper.properties +5 -0
  12. data/gradlew +172 -0
  13. data/gradlew.bat +84 -0
  14. data/lib/embulk/guess/zendesk.rb +21 -0
  15. data/lib/embulk/input/zendesk.rb +3 -0
  16. data/src/main/java/org/embulk/input/zendesk/RecordImporter.java +134 -0
  17. data/src/main/java/org/embulk/input/zendesk/ZendeskInputPlugin.java +513 -0
  18. data/src/main/java/org/embulk/input/zendesk/clients/ZendeskRestClient.java +291 -0
  19. data/src/main/java/org/embulk/input/zendesk/models/AuthenticationMethod.java +23 -0
  20. data/src/main/java/org/embulk/input/zendesk/models/Target.java +47 -0
  21. data/src/main/java/org/embulk/input/zendesk/models/ZendeskException.java +25 -0
  22. data/src/main/java/org/embulk/input/zendesk/services/ZendeskCustomObjectService.java +110 -0
  23. data/src/main/java/org/embulk/input/zendesk/services/ZendeskNPSService.java +30 -0
  24. data/src/main/java/org/embulk/input/zendesk/services/ZendeskNormalServices.java +347 -0
  25. data/src/main/java/org/embulk/input/zendesk/services/ZendeskService.java +14 -0
  26. data/src/main/java/org/embulk/input/zendesk/services/ZendeskSupportAPIService.java +63 -0
  27. data/src/main/java/org/embulk/input/zendesk/services/ZendeskUserEventService.java +158 -0
  28. data/src/main/java/org/embulk/input/zendesk/stream/PagingSpliterator.java +40 -0
  29. data/src/main/java/org/embulk/input/zendesk/stream/paginator/sunshine/CustomObjectSpliterator.java +42 -0
  30. data/src/main/java/org/embulk/input/zendesk/stream/paginator/sunshine/SunshineSpliterator.java +66 -0
  31. data/src/main/java/org/embulk/input/zendesk/stream/paginator/sunshine/UserEventSpliterator.java +35 -0
  32. data/src/main/java/org/embulk/input/zendesk/stream/paginator/support/OrganizationSpliterator.java +13 -0
  33. data/src/main/java/org/embulk/input/zendesk/stream/paginator/support/SupportSpliterator.java +44 -0
  34. data/src/main/java/org/embulk/input/zendesk/stream/paginator/support/UserSpliterator.java +13 -0
  35. data/src/main/java/org/embulk/input/zendesk/utils/ZendeskConstants.java +72 -0
  36. data/src/main/java/org/embulk/input/zendesk/utils/ZendeskDateUtils.java +68 -0
  37. data/src/main/java/org/embulk/input/zendesk/utils/ZendeskUtils.java +92 -0
  38. data/src/test/java/org/embulk/input/zendesk/TestRecordImporter.java +114 -0
  39. data/src/test/java/org/embulk/input/zendesk/TestZendeskInputPlugin.java +402 -0
  40. data/src/test/java/org/embulk/input/zendesk/clients/TestZendeskRestClient.java +337 -0
  41. data/src/test/java/org/embulk/input/zendesk/services/TestZendeskCustomObjectService.java +161 -0
  42. data/src/test/java/org/embulk/input/zendesk/services/TestZendeskNPSService.java +56 -0
  43. data/src/test/java/org/embulk/input/zendesk/services/TestZendeskNormalService.java +261 -0
  44. data/src/test/java/org/embulk/input/zendesk/services/TestZendeskSupportAPIService.java +130 -0
  45. data/src/test/java/org/embulk/input/zendesk/services/TestZendeskUserEventService.java +158 -0
  46. data/src/test/java/org/embulk/input/zendesk/utils/TestZendeskDateUtils.java +87 -0
  47. data/src/test/java/org/embulk/input/zendesk/utils/TestZendeskUtil.java +22 -0
  48. data/src/test/java/org/embulk/input/zendesk/utils/ZendeskPluginTestRuntime.java +133 -0
  49. data/src/test/java/org/embulk/input/zendesk/utils/ZendeskTestHelper.java +92 -0
  50. data/src/test/resources/config/base.yml +14 -0
  51. data/src/test/resources/config/base_validator.yml +48 -0
  52. data/src/test/resources/config/incremental.yml +54 -0
  53. data/src/test/resources/config/non-incremental.yml +39 -0
  54. data/src/test/resources/config/nps.yml +31 -0
  55. data/src/test/resources/config/object_records.yml +24 -0
  56. data/src/test/resources/config/relationship_records.yml +23 -0
  57. data/src/test/resources/config/user_events.yml +29 -0
  58. data/src/test/resources/config/util.yml +18 -0
  59. data/src/test/resources/data/client.json +293 -0
  60. data/src/test/resources/data/duplicate_user.json +0 -0
  61. data/src/test/resources/data/empty_result.json +7 -0
  62. data/src/test/resources/data/error_data.json +187 -0
  63. data/src/test/resources/data/expected/ticket_column.json +148 -0
  64. data/src/test/resources/data/expected/ticket_column_with_related_objects.json +152 -0
  65. data/src/test/resources/data/expected/ticket_fields_column.json +92 -0
  66. data/src/test/resources/data/expected/ticket_metrics_column.json +98 -0
  67. data/src/test/resources/data/expected/user_events_column.json +40 -0
  68. data/src/test/resources/data/object_records.json +30 -0
  69. data/src/test/resources/data/organization.json +39 -0
  70. data/src/test/resources/data/relationship_records.json +57 -0
  71. data/src/test/resources/data/scores.json +21 -0
  72. data/src/test/resources/data/scores_share_same_time_with_next_page.json +35 -0
  73. data/src/test/resources/data/scores_share_same_time_without_next_page.json +35 -0
  74. data/src/test/resources/data/simple_organization.json +23 -0
  75. data/src/test/resources/data/simple_user.json +50 -0
  76. data/src/test/resources/data/simple_user_event.json +19 -0
  77. data/src/test/resources/data/ticket_events_share_same_time_with_next_page.json +279 -0
  78. data/src/test/resources/data/ticket_events_share_same_time_without_next_page.json +279 -0
  79. data/src/test/resources/data/ticket_events_updated_by_system_records.json +279 -0
  80. data/src/test/resources/data/ticket_fields.json +225 -0
  81. data/src/test/resources/data/ticket_metrics.json +397 -0
  82. data/src/test/resources/data/ticket_share_same_time_with_next_page.json +232 -0
  83. data/src/test/resources/data/ticket_share_same_time_without_next_page.json +232 -0
  84. data/src/test/resources/data/ticket_with_related_objects.json +67 -0
  85. data/src/test/resources/data/ticket_with_updated_by_system_records.json +187 -0
  86. data/src/test/resources/data/tickets.json +232 -0
  87. data/src/test/resources/data/tickets_continue.json +52 -0
  88. data/src/test/resources/data/user_event.json +19 -0
  89. data/src/test/resources/data/user_event_contain_latter_create_at.json +19 -0
  90. data/src/test/resources/data/user_event_multiple.json +33 -0
  91. data/src/test/resources/data/util.json +19 -0
  92. data/src/test/resources/data/util_page.json +227 -0
  93. metadata +168 -0
@@ -0,0 +1,68 @@
1
+ package org.embulk.input.zendesk.utils;
2
+
3
+ import org.embulk.spi.DataException;
4
+
5
+ import java.time.Instant;
6
+ import java.time.LocalDateTime;
7
+ import java.time.OffsetDateTime;
8
+ import java.time.ZoneOffset;
9
+ import java.time.format.DateTimeFormatter;
10
+ import java.time.format.DateTimeParseException;
11
+
12
+ import java.time.format.ResolverStyle;
13
+ import java.util.Optional;
14
+
15
+ public class ZendeskDateUtils
16
+ {
17
+ private ZendeskDateUtils()
18
+ {
19
+ }
20
+
21
+ public static long isoToEpochSecond(final String time)
22
+ {
23
+ final Optional<String> pattern = supportedTimeFormat(time);
24
+ if (pattern.isPresent()) {
25
+ final DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern.get()).withZone(ZoneOffset.UTC).withResolverStyle(ResolverStyle.STRICT);
26
+ try {
27
+ final OffsetDateTime offsetDateTime = LocalDateTime.parse(time, formatter).atOffset(ZoneOffset.UTC);
28
+ return offsetDateTime.toInstant().getEpochSecond();
29
+ }
30
+ catch (DateTimeParseException e) {
31
+ throw new DataException(e.getMessage());
32
+ }
33
+ }
34
+
35
+ throw new DataException("Fail to parse value '" + time + "' follow formats " + ZendeskConstants.Misc.SUPPORT_DATE_TIME_FORMAT.toString());
36
+ }
37
+
38
+ public static Optional<String> supportedTimeFormat(final String value)
39
+ {
40
+ for (final String fmt : ZendeskConstants.Misc.SUPPORT_DATE_TIME_FORMAT) {
41
+ final DateTimeFormatter formatter = DateTimeFormatter.ofPattern(fmt);
42
+ try {
43
+ formatter.parse(value);
44
+ return Optional.of(fmt);
45
+ }
46
+ catch (final DateTimeParseException e) {
47
+ // Do nothing
48
+ }
49
+ }
50
+ return Optional.empty();
51
+ }
52
+
53
+ public static String convertToDateTimeFormat(String datetime, String dateTimeFormat)
54
+ {
55
+ return Instant.ofEpochSecond(ZendeskDateUtils.isoToEpochSecond(datetime)).atOffset(ZoneOffset.UTC).format(DateTimeFormatter.ofPattern(dateTimeFormat));
56
+ }
57
+
58
+ // start_time should be start from 0
59
+ public static long getStartTime(final String time)
60
+ {
61
+ try {
62
+ return isoToEpochSecond(time);
63
+ }
64
+ catch (DataException ex) {
65
+ return 0;
66
+ }
67
+ }
68
+ }
@@ -0,0 +1,92 @@
1
+ package org.embulk.input.zendesk.utils;
2
+
3
+ import com.fasterxml.jackson.databind.DeserializationFeature;
4
+ import com.fasterxml.jackson.databind.JsonNode;
5
+ import com.fasterxml.jackson.databind.ObjectMapper;
6
+ import com.fasterxml.jackson.databind.node.ObjectNode;
7
+ import com.google.common.base.Charsets;
8
+ import com.google.common.base.Throwables;
9
+ import org.apache.http.client.utils.URIBuilder;
10
+ import org.embulk.config.ConfigException;
11
+ import org.embulk.spi.DataException;
12
+
13
+ import java.io.IOException;
14
+ import java.net.URI;
15
+ import java.net.URISyntaxException;
16
+ import java.util.Base64;
17
+ import java.util.Iterator;
18
+ import org.embulk.spi.Exec;
19
+ import org.slf4j.Logger;
20
+
21
+ public class ZendeskUtils
22
+ {
23
+ private static final ObjectMapper mapper = new ObjectMapper();
24
+ private static final Logger logger = Exec.getLogger(ZendeskUtils.class);
25
+
26
+ static {
27
+ mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
28
+ mapper.configure(com.fasterxml.jackson.core.JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS, false);
29
+ }
30
+
31
+ private ZendeskUtils()
32
+ {}
33
+
34
+ public static String convertBase64(final String text)
35
+ {
36
+ return Base64.getEncoder().encodeToString(text.getBytes(Charsets.UTF_8));
37
+ }
38
+
39
+ public static int numberToSplitWithHintingInTask(final int count)
40
+ {
41
+ return (int) Math.ceil((double) count / ZendeskConstants.Misc.RECORDS_SIZE_PER_PAGE);
42
+ }
43
+
44
+ public static ObjectNode parseJsonObject(final String jsonText)
45
+ {
46
+ final JsonNode node = ZendeskUtils.parseJsonNode(jsonText);
47
+ if (node.isObject()) {
48
+ return (ObjectNode) node;
49
+ }
50
+
51
+ throw new DataException("Expected object node to parse but doesn't get");
52
+ }
53
+
54
+ public static Iterator<JsonNode> getListRecords(final JsonNode result, final String targetJsonName)
55
+ {
56
+ if (!result.has(targetJsonName) || !result.get(targetJsonName).isArray()) {
57
+ throw new DataException(String.format("Missing '%s' from Zendesk API response", targetJsonName));
58
+ }
59
+ return result.get(targetJsonName).elements();
60
+ }
61
+
62
+ public static boolean isNull(final JsonNode jsonNode)
63
+ {
64
+ return jsonNode == null || jsonNode.isNull();
65
+ }
66
+
67
+ public static URIBuilder getURIBuilder(final String urlString)
68
+ {
69
+ final URI uri;
70
+ try {
71
+ uri = new URI(urlString);
72
+ }
73
+ catch (final URISyntaxException e) {
74
+ throw new ConfigException("URL is invalid format " + urlString);
75
+ }
76
+
77
+ return new URIBuilder()
78
+ .setScheme(uri.getScheme())
79
+ .setHost(uri.getHost());
80
+ }
81
+
82
+ private static JsonNode parseJsonNode(final String jsonText)
83
+ {
84
+ try {
85
+ logger.info("Text {}", jsonText);
86
+ return ZendeskUtils.mapper.readTree(jsonText);
87
+ }
88
+ catch (final IOException e) {
89
+ throw Throwables.propagate(e);
90
+ }
91
+ }
92
+ }
@@ -0,0 +1,114 @@
1
+ package org.embulk.input.zendesk;
2
+
3
+ import com.fasterxml.jackson.databind.JsonNode;
4
+ import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
5
+ import org.embulk.EmbulkTestRuntime;
6
+ import org.embulk.input.zendesk.utils.ZendeskTestHelper;
7
+ import org.embulk.spi.Column;
8
+ import org.embulk.spi.PageBuilder;
9
+ import org.embulk.spi.Schema;
10
+ import org.embulk.spi.json.JsonParser;
11
+ import org.embulk.spi.time.Timestamp;
12
+ import org.embulk.spi.time.TimestampParser;
13
+ import org.junit.Before;
14
+ import org.junit.BeforeClass;
15
+ import org.junit.Rule;
16
+ import org.junit.Test;
17
+ import org.mockito.Mockito;
18
+ import org.msgpack.value.Value;
19
+
20
+ import static org.mockito.Mockito.mock;
21
+ import static org.mockito.Mockito.spy;
22
+ import static org.mockito.Mockito.times;
23
+ import static org.mockito.Mockito.verify;
24
+
25
+ public class TestRecordImporter
26
+ {
27
+ @Rule
28
+ @SuppressFBWarnings("URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
29
+ public EmbulkTestRuntime runtime = new EmbulkTestRuntime();
30
+
31
+ public PageBuilder pageBuilder = mock(PageBuilder.class);
32
+
33
+ private static Schema schema;
34
+ private static Column booleanColumn;
35
+ private static Column longColumn;
36
+ private static Column doubleColumn;
37
+ private static Column stringColumn;
38
+ private static Column dateColumn;
39
+ private static Column jsonColumn;
40
+ private static ZendeskInputPlugin.PluginTask pluginTask;
41
+
42
+ private RecordImporter recordImporter;
43
+
44
+ @BeforeClass
45
+ public static void setUp()
46
+ {
47
+ pluginTask = ZendeskTestHelper.getConfigSource("util.yml").loadConfig(ZendeskInputPlugin.PluginTask.class);
48
+ schema = pluginTask.getColumns().toSchema();
49
+ booleanColumn = schema.getColumn(0);
50
+ longColumn = schema.getColumn(1);
51
+ doubleColumn = schema.getColumn(2);
52
+ stringColumn = schema.getColumn(3);
53
+ dateColumn = schema.getColumn(4);
54
+ jsonColumn = schema.getColumn(5);
55
+ }
56
+
57
+ @Before
58
+ public void prepare()
59
+ {
60
+ recordImporter = spy(new RecordImporter(schema, pageBuilder));
61
+ }
62
+
63
+ @Test
64
+ public void testAddRecordAllRight()
65
+ {
66
+ String name = "allRight";
67
+ JsonNode dataJson = ZendeskTestHelper.getJsonFromFile("data/util.json").get(name);
68
+
69
+ Boolean boolValue = Boolean.TRUE;
70
+ long longValue = 1;
71
+ double doubleValue = 1;
72
+ String stringValue = "string";
73
+ Timestamp dateValue = TimestampParser.of("%Y-%m-%dT%H:%M:%S%z", "UTC").parse("2019-01-01T00:00:00Z");
74
+ Value jsonValue = new JsonParser().parse("{}");
75
+
76
+ recordImporter.addRecord(dataJson);
77
+
78
+ verify(pageBuilder, times(1)).setBoolean(booleanColumn, boolValue);
79
+ verify(pageBuilder, times(1)).setLong(longColumn, longValue);
80
+ verify(pageBuilder, times(1)).setDouble(doubleColumn, doubleValue);
81
+ verify(pageBuilder, times(1)).setString(stringColumn, stringValue);
82
+ verify(pageBuilder, times(1)).setTimestamp(dateColumn, dateValue);
83
+ verify(pageBuilder, times(1)).setJson(jsonColumn, jsonValue);
84
+ }
85
+
86
+ @Test
87
+ public void testAddRecordAllWrong()
88
+ {
89
+ String name = "allWrong";
90
+ JsonNode dataJson = ZendeskTestHelper.getJsonFromFile("data/util.json").get(name);
91
+
92
+ Value jsonValue = new JsonParser().parse("{}");
93
+
94
+ recordImporter.addRecord(dataJson);
95
+
96
+ verify(pageBuilder, times(1)).setNull(booleanColumn);
97
+ verify(pageBuilder, times(1)).setNull(longColumn);
98
+ verify(pageBuilder, times(1)).setNull(doubleColumn);
99
+ verify(pageBuilder, times(1)).setNull(stringColumn);
100
+ verify(pageBuilder, times(1)).setNull(dateColumn);
101
+ verify(pageBuilder, times(1)).setJson(jsonColumn, jsonValue);
102
+ }
103
+
104
+ @Test
105
+ public void testAddRecordAllMissing()
106
+ {
107
+ String name = "allMissing";
108
+ JsonNode dataJson = ZendeskTestHelper.getJsonFromFile("data/util.json").get(name);
109
+
110
+ recordImporter.addRecord(dataJson);
111
+
112
+ verify(pageBuilder, times(6)).setNull(Mockito.any());
113
+ }
114
+ }
@@ -0,0 +1,402 @@
1
+ package org.embulk.input.zendesk;
2
+
3
+ import com.fasterxml.jackson.databind.JsonNode;
4
+ import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
5
+ import org.embulk.config.ConfigDiff;
6
+ import org.embulk.config.ConfigException;
7
+ import org.embulk.config.ConfigSource;
8
+ import org.embulk.config.TaskReport;
9
+ import org.embulk.config.TaskSource;
10
+ import org.embulk.input.zendesk.models.Target;
11
+ import org.embulk.input.zendesk.services.ZendeskCustomObjectService;
12
+ import org.embulk.input.zendesk.services.ZendeskNPSService;
13
+ import org.embulk.input.zendesk.services.ZendeskService;
14
+ import org.embulk.input.zendesk.services.ZendeskSupportAPIService;
15
+ import org.embulk.input.zendesk.services.ZendeskUserEventService;
16
+ import org.embulk.input.zendesk.utils.ZendeskConstants;
17
+ import org.embulk.input.zendesk.utils.ZendeskDateUtils;
18
+ import org.embulk.input.zendesk.utils.ZendeskPluginTestRuntime;
19
+ import org.embulk.input.zendesk.utils.ZendeskTestHelper;
20
+
21
+ import org.embulk.spi.Exec;
22
+ import org.embulk.spi.InputPlugin;
23
+ import org.embulk.spi.PageBuilder;
24
+ import org.embulk.spi.PageOutput;
25
+ import org.embulk.spi.Schema;
26
+
27
+ import org.embulk.spi.TestPageBuilderReader;
28
+ import org.junit.Assert;
29
+ import org.junit.Before;
30
+ import org.junit.Rule;
31
+ import org.junit.Test;
32
+
33
+ import static org.junit.Assert.assertEquals;
34
+
35
+ import static org.junit.Assert.assertTrue;
36
+ import static org.junit.Assert.fail;
37
+ import static org.mockito.ArgumentMatchers.any;
38
+ import static org.mockito.ArgumentMatchers.anyBoolean;
39
+ import static org.mockito.ArgumentMatchers.anyInt;
40
+ import static org.mockito.ArgumentMatchers.anyLong;
41
+ import static org.mockito.ArgumentMatchers.anyString;
42
+
43
+ import static org.mockito.Mockito.doReturn;
44
+ import static org.mockito.Mockito.mock;
45
+ import static org.mockito.Mockito.spy;
46
+
47
+ import static org.mockito.Mockito.times;
48
+ import static org.mockito.Mockito.verify;
49
+ import static org.mockito.Mockito.when;
50
+
51
+ import java.time.Instant;
52
+ import java.time.OffsetDateTime;
53
+ import java.time.ZoneOffset;
54
+ import java.time.format.DateTimeFormatter;
55
+ import java.util.Collections;
56
+ import java.util.List;
57
+ import java.util.stream.Collectors;
58
+ import java.util.stream.IntStream;
59
+
60
+ public class TestZendeskInputPlugin
61
+ {
62
+ @Rule
63
+ @SuppressFBWarnings("URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
64
+ public ZendeskPluginTestRuntime embulk = new ZendeskPluginTestRuntime();
65
+
66
+ private ZendeskService zendeskSupportAPIService;
67
+
68
+ private ZendeskInputPlugin zendeskInputPlugin;
69
+
70
+ private PageBuilder pageBuilder = mock(PageBuilder.class);
71
+
72
+ private TestPageBuilderReader.MockPageOutput output = new TestPageBuilderReader.MockPageOutput();
73
+
74
+ @Before
75
+ public void prepare()
76
+ {
77
+ zendeskInputPlugin = spy(new ZendeskInputPlugin());
78
+ setupSupportAPIService();
79
+ doReturn(pageBuilder).when(zendeskInputPlugin).getPageBuilder(any(Schema.class), any(PageOutput.class));
80
+ }
81
+
82
+ @Test
83
+ public void testGuessGenerateColumnsForIncrementalTarget()
84
+ {
85
+ final ConfigSource src = ZendeskTestHelper.getConfigSource("base.yml");
86
+ src.set("target", "tickets");
87
+ setupTestGuessGenerateColumn(src, "data/tickets.json", "data/expected/ticket_column.json");
88
+ }
89
+
90
+ @Test
91
+ public void testGuessGenerateColumnsForIncrementalTargetIncludeRelatedObject()
92
+ {
93
+ final ConfigSource src = ZendeskTestHelper.getConfigSource("base.yml");
94
+ src.set("target", "tickets");
95
+ src.set("includes", Collections.singletonList("organizations"));
96
+ setupTestGuessGenerateColumn(src, "data/tickets.json", "data/expected/ticket_column_with_related_objects.json");
97
+ }
98
+
99
+ @Test
100
+ public void testGuessGenerateColumnsForTicketMetrics()
101
+ {
102
+ final ConfigSource src = ZendeskTestHelper.getConfigSource("base.yml");
103
+ src.set("target", "ticket_metrics");
104
+ setupTestGuessGenerateColumn(src, "data/ticket_metrics.json", "data/expected/ticket_metrics_column.json");
105
+ }
106
+
107
+ @Test
108
+ public void testGuessGenerateColumnsForUserEvents()
109
+ {
110
+ final ConfigSource src = ZendeskTestHelper.getConfigSource("base.yml");
111
+ src.set("target", "user_events");
112
+ src.set("profile_source", "dummy");
113
+ setupTestGuessGenerateColumn(src, "data/user_event.json", "data/expected/user_events_column.json");
114
+ }
115
+
116
+ @Test
117
+ public void testGuessGenerateColumnsForNonIncrementalTarget()
118
+ {
119
+ final ConfigSource src = ZendeskTestHelper.getConfigSource("base.yml");
120
+ src.set("target", "ticket_fields");
121
+ setupTestGuessGenerateColumn(src, "data/ticket_fields.json", "data/expected/ticket_fields_column.json");
122
+ }
123
+
124
+ @Test(expected = ConfigException.class)
125
+ public void testGuessFail()
126
+ {
127
+ final ConfigSource src = ZendeskTestHelper.getConfigSource("base.yml");
128
+ src.set("target", "tickets");
129
+ loadData("data/error_data.json");
130
+
131
+ zendeskInputPlugin.guess(src);
132
+ }
133
+
134
+ @Test
135
+ public void testRunSupportAPINonIncremental()
136
+ {
137
+ final ConfigSource src = ZendeskTestHelper.getConfigSource("non-incremental.yml");
138
+ loadData("data/ticket_fields.json");
139
+
140
+ ConfigDiff configDiff = zendeskInputPlugin.transaction(src, new Control());
141
+ // running in 2 pages
142
+ verify(pageBuilder, times(2)).finish();
143
+ Assert.assertTrue(configDiff.isEmpty());
144
+ }
145
+
146
+ @Test
147
+ public void testRunIncrementalStoreStartTimeAndEndTime()
148
+ {
149
+ final ConfigSource src = ZendeskTestHelper.getConfigSource("incremental.yml")
150
+ .set("end_time", "2019-04-12 06:51:50 +0000");
151
+ TaskReport taskReport = Exec.newTaskReport();
152
+ taskReport.set(ZendeskConstants.Field.START_TIME, 1557026576);
153
+ taskReport.set(ZendeskConstants.Field.END_TIME, 1560309776);
154
+
155
+ when(zendeskSupportAPIService.isSupportIncremental()).thenReturn(true);
156
+ when(zendeskSupportAPIService.addRecordToImporter(anyInt(), any())).thenReturn(taskReport);
157
+
158
+ ConfigDiff configDiff = zendeskInputPlugin.transaction(src, new Control());
159
+ String nextStartTime = configDiff.get(String.class, ZendeskConstants.Field.START_TIME);
160
+ String nextEndTime = configDiff.get(String.class, ZendeskConstants.Field.END_TIME);
161
+ verify(pageBuilder, times(1)).finish();
162
+ assertEquals("2019-05-05 03:22:56 +0000", nextStartTime);
163
+ assertEquals("2019-06-12 03:22:56 +0000", nextEndTime);
164
+ }
165
+
166
+ @Test
167
+ public void testDispatchPerTargetShouldReturnSupportAPIService()
168
+ {
169
+ // create a new one so it doesn't mock the ZendeskService
170
+ zendeskInputPlugin = spy(new ZendeskInputPlugin());
171
+ testReturnSupportAPIService(Target.TICKETS);
172
+ testReturnSupportAPIService(Target.TICKET_METRICS);
173
+ testReturnSupportAPIService(Target.TICKET_EVENTS);
174
+ testReturnSupportAPIService(Target.TICKET_FORMS);
175
+ testReturnSupportAPIService(Target.TICKET_FIELDS);
176
+ testReturnSupportAPIService(Target.USERS);
177
+ testReturnSupportAPIService(Target.ORGANIZATIONS);
178
+ }
179
+
180
+ @Test
181
+ public void testDispatchPerTargetShouldReturnNPSService()
182
+ {
183
+ // create a new one so it doesn't mock the ZendeskService
184
+ zendeskInputPlugin = spy(new ZendeskInputPlugin());
185
+ testReturnNPSService(Target.SCORES);
186
+ testReturnNPSService(Target.RECIPIENTS);
187
+ }
188
+
189
+ @Test
190
+ public void testDispatchPerTargetShouldReturnCustomObjectService()
191
+ {
192
+ // create a new one so it doesn't mock the ZendeskService
193
+ zendeskInputPlugin = spy(new ZendeskInputPlugin());
194
+ testReturnCustomObjectService(Target.OBJECT_RECORDS);
195
+ testReturnCustomObjectService(Target.RELATIONSHIP_RECORDS);
196
+ }
197
+
198
+ @Test
199
+ public void testDispatchPerTargetShouldReturnUserEventService()
200
+ {
201
+ // create a new one so it doesn't mock the ZendeskService
202
+ zendeskInputPlugin = spy(new ZendeskInputPlugin());
203
+ final ConfigSource src = ZendeskTestHelper.getConfigSource("base.yml");
204
+ src.set("target", Target.USER_EVENTS.name().toLowerCase());
205
+ src.set("profile_source", "dummy");
206
+ src.set("columns", Collections.EMPTY_LIST);
207
+ ZendeskInputPlugin.PluginTask task = src.loadConfig(ZendeskInputPlugin.PluginTask.class);
208
+ ZendeskService zendeskService = zendeskInputPlugin.dispatchPerTarget(task);
209
+ assertTrue(zendeskService instanceof ZendeskUserEventService);
210
+ }
211
+
212
+ @Test
213
+ public void validateCredentialOauthShouldThrowException()
214
+ {
215
+ ConfigSource configSource = ZendeskTestHelper.getConfigSource("base_validator.yml");
216
+ configSource.set("auth_method", "oauth");
217
+ configSource.remove("access_token");
218
+ assertValidation(configSource, "access_token is required for authentication method 'oauth'");
219
+ }
220
+
221
+ @Test
222
+ public void validateCredentialBasicShouldThrowException()
223
+ {
224
+ ConfigSource configSource = ZendeskTestHelper.getConfigSource("base_validator.yml");
225
+ configSource.set("auth_method", "basic");
226
+ configSource.remove("username");
227
+ assertValidation(configSource, "username and password are required for authentication method 'basic'");
228
+
229
+ configSource.set("username", "");
230
+ configSource.remove("password");
231
+ assertValidation(configSource, "username and password are required for authentication method 'basic'");
232
+ }
233
+
234
+ @Test
235
+ public void validateCredentialTokenShouldThrowException()
236
+ {
237
+ ConfigSource configSource = ZendeskTestHelper.getConfigSource("base_validator.yml");
238
+ configSource.set("auth_method", "token");
239
+ configSource.remove("token");
240
+ assertValidation(configSource, "username and token are required for authentication method 'token'");
241
+
242
+ configSource.set("token", "");
243
+ configSource.remove("username");
244
+ assertValidation(configSource, "username and token are required for authentication method 'token'");
245
+ }
246
+
247
+ @Test
248
+ public void validateAppMarketPlaceShouldThrowException()
249
+ {
250
+ ConfigSource configSource = ZendeskTestHelper.getConfigSource("base_validator.yml");
251
+ configSource.remove("app_marketplace_integration_name");
252
+ assertValidation(configSource, "All of app_marketplace_integration_name, app_marketplace_org_id, " +
253
+ "app_marketplace_app_id " +
254
+ "are required to fill out for Apps Marketplace API header");
255
+ }
256
+
257
+ @Test
258
+ public void validateCustomObjectShouldThrowException()
259
+ {
260
+ ConfigSource configSource = ZendeskTestHelper.getConfigSource("base_validator.yml");
261
+ configSource.set("target", Target.OBJECT_RECORDS.name().toLowerCase());
262
+ assertValidation(configSource, "Should have at least one Object Type");
263
+
264
+ configSource.set("target", Target.RELATIONSHIP_RECORDS.name().toLowerCase());
265
+ assertValidation(configSource, "Should have at least one Relationship Type");
266
+ }
267
+
268
+ @Test
269
+ public void validateUserEventShouldThrowException()
270
+ {
271
+ ConfigSource configSource = ZendeskTestHelper.getConfigSource("base_validator.yml");
272
+ configSource.set("target", Target.USER_EVENTS.name().toLowerCase());
273
+ assertValidation(configSource, "Profile Source is required for User Event Target");
274
+ }
275
+
276
+ @Test
277
+ public void validateTimeShouldThrowException()
278
+ {
279
+ ConfigSource configSource = ZendeskTestHelper.getConfigSource("base_validator.yml");
280
+ when(zendeskSupportAPIService.isSupportIncremental()).thenReturn(true);
281
+ configSource.set("target", Target.TICKETS.name().toLowerCase());
282
+ configSource.set("start_time", OffsetDateTime.ofInstant(Instant.ofEpochSecond(Instant.now().getEpochSecond()), ZoneOffset.UTC).format(DateTimeFormatter.ISO_INSTANT));
283
+ configSource.set("end_time", "2019-12-2 22-12-22");
284
+ assertValidation(configSource, "End Time should follow these format " + ZendeskConstants.Misc.SUPPORT_DATE_TIME_FORMAT.toString());
285
+
286
+ configSource.set("start_time", OffsetDateTime.ofInstant(Instant.ofEpochSecond(Instant.now().getEpochSecond() + 3600), ZoneOffset.UTC).format(DateTimeFormatter.ISO_INSTANT));
287
+ configSource.set("end_time", OffsetDateTime.ofInstant(Instant.ofEpochSecond(Instant.now().getEpochSecond()), ZoneOffset.UTC).format(DateTimeFormatter.ISO_INSTANT));
288
+ assertValidation(configSource, "End Time should be later or equal than Start Time");
289
+ }
290
+
291
+ @Test
292
+ public void isValidTimeRangeShouldThrowException()
293
+ {
294
+ ZendeskTestHelper.setPreviewMode(embulk, true);
295
+ String expectedMessage = "Invalid End time. End time is greater than current time";
296
+ ConfigSource configSource = ZendeskTestHelper.getConfigSource("base_validator.yml");
297
+ when(zendeskSupportAPIService.isSupportIncremental()).thenReturn(true);
298
+ configSource.set("start_time", OffsetDateTime.ofInstant(Instant.ofEpochSecond(Instant.now().getEpochSecond()), ZoneOffset.UTC).format(DateTimeFormatter.ISO_INSTANT));
299
+ configSource.set("end_time", OffsetDateTime.ofInstant(Instant.ofEpochSecond(Instant.now().getEpochSecond() + 1000), ZoneOffset.UTC).format(DateTimeFormatter.ISO_INSTANT));
300
+
301
+ assertValidation(configSource, expectedMessage);
302
+
303
+ try {
304
+ zendeskInputPlugin.transaction(configSource, new Control());
305
+ fail("Should not reach here");
306
+ }
307
+ catch (final Exception e) {
308
+ assertEquals(expectedMessage, e.getMessage());
309
+ }
310
+ }
311
+
312
+ @Test
313
+ public void runShouldKeepOldStartTimeAndEndTimeInConfigDiff()
314
+ {
315
+ ConfigSource configSource = ZendeskTestHelper.getConfigSource("base_validator.yml");
316
+ ZendeskTestHelper.setPreviewMode(embulk, false);
317
+ when(zendeskSupportAPIService.isSupportIncremental()).thenReturn(true);
318
+ configSource.set("start_time", OffsetDateTime.ofInstant(Instant.ofEpochSecond(Instant.now().getEpochSecond()), ZoneOffset.UTC).format(DateTimeFormatter.ISO_INSTANT));
319
+ configSource.set("end_time", OffsetDateTime.ofInstant(Instant.ofEpochSecond(Instant.now().getEpochSecond() + 1000), ZoneOffset.UTC).format(DateTimeFormatter.ISO_INSTANT));
320
+
321
+ ConfigDiff configDiff = zendeskInputPlugin.transaction(configSource, new Control());
322
+ assertEquals(ZendeskDateUtils.convertToDateTimeFormat(configSource.get(String.class, ZendeskConstants.Field.START_TIME), ZendeskConstants.Misc.RUBY_TIMESTAMP_FORMAT_INPUT),
323
+ configDiff.get(String.class, ZendeskConstants.Field.START_TIME));
324
+ assertEquals(ZendeskDateUtils.convertToDateTimeFormat(configSource.get(String.class, ZendeskConstants.Field.END_TIME), ZendeskConstants.Misc.RUBY_TIMESTAMP_FORMAT_INPUT),
325
+ configDiff.get(String.class, ZendeskConstants.Field.END_TIME));
326
+ }
327
+
328
+ private void assertValidation(final ConfigSource configSource, final String message)
329
+ {
330
+ try {
331
+ zendeskInputPlugin.guess(configSource);
332
+ fail("Should not reach here");
333
+ }
334
+ catch (final Exception e) {
335
+ assertEquals(message, e.getMessage());
336
+ }
337
+ }
338
+
339
+ private void testReturnSupportAPIService(Target target)
340
+ {
341
+ final ConfigSource src = ZendeskTestHelper.getConfigSource("base.yml");
342
+ src.set("target", target.name().toLowerCase());
343
+ src.set("columns", Collections.EMPTY_LIST);
344
+ ZendeskInputPlugin.PluginTask task = src.loadConfig(ZendeskInputPlugin.PluginTask.class);
345
+ ZendeskService zendeskService = zendeskInputPlugin.dispatchPerTarget(task);
346
+ assertTrue(zendeskService instanceof ZendeskSupportAPIService);
347
+ }
348
+
349
+ private void testReturnNPSService(Target target)
350
+ {
351
+ final ConfigSource src = ZendeskTestHelper.getConfigSource("base.yml");
352
+ src.set("target", target.name().toLowerCase());
353
+ src.set("columns", Collections.EMPTY_LIST);
354
+ ZendeskInputPlugin.PluginTask task = src.loadConfig(ZendeskInputPlugin.PluginTask.class);
355
+ ZendeskService zendeskService = zendeskInputPlugin.dispatchPerTarget(task);
356
+ assertTrue(zendeskService instanceof ZendeskNPSService);
357
+ }
358
+
359
+ private void testReturnCustomObjectService(Target target)
360
+ {
361
+ final ConfigSource src = ZendeskTestHelper.getConfigSource("base.yml");
362
+ src.set("target", target.name().toLowerCase());
363
+ src.set("relationship_types", Collections.singletonList("dummy"));
364
+ src.set("object_types", Collections.singletonList("account"));
365
+ src.set("columns", Collections.EMPTY_LIST);
366
+ ZendeskInputPlugin.PluginTask task = src.loadConfig(ZendeskInputPlugin.PluginTask.class);
367
+ ZendeskService zendeskService = zendeskInputPlugin.dispatchPerTarget(task);
368
+ assertTrue(zendeskService instanceof ZendeskCustomObjectService);
369
+ }
370
+
371
+ private void loadData(String fileName)
372
+ {
373
+ JsonNode dataJson = ZendeskTestHelper.getJsonFromFile(fileName);
374
+ when(zendeskSupportAPIService.getDataFromPath(anyString(), anyInt(), anyBoolean(), anyLong())).thenReturn(dataJson);
375
+ }
376
+
377
+ private void setupTestGuessGenerateColumn(ConfigSource src, String fileName, String expectedSource)
378
+ {
379
+ loadData(fileName);
380
+ ConfigDiff configDiff = zendeskInputPlugin.guess(src);
381
+ JsonNode columns = configDiff.get(JsonNode.class, "columns");
382
+ assertEquals(ZendeskTestHelper.getJsonFromFile(expectedSource), columns);
383
+ }
384
+
385
+ private void setupSupportAPIService()
386
+ {
387
+ zendeskSupportAPIService = mock(ZendeskSupportAPIService.class);
388
+ doReturn(zendeskSupportAPIService).when(zendeskInputPlugin).dispatchPerTarget(any(ZendeskInputPlugin.PluginTask.class));
389
+ }
390
+
391
+ private class Control implements InputPlugin.Control
392
+ {
393
+ @Override
394
+ public List<TaskReport> run(final TaskSource taskSource, final Schema schema, final int taskCount)
395
+ {
396
+ List<TaskReport> reports = IntStream.range(0, taskCount)
397
+ .mapToObj(i -> zendeskInputPlugin.run(taskSource, schema, i, output))
398
+ .collect(Collectors.toList());
399
+ return reports;
400
+ }
401
+ }
402
+ }