embulk-input-zendesk-all 0.3.7

Sign up to get free protection for your applications and to get access to all the features.
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
+ }