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.
- checksums.yaml +7 -0
- data/.gitignore +12 -0
- data/.travis.yml +5 -0
- data/CHANGELOG.md +126 -0
- data/LICENSE.txt +21 -0
- data/README.md +91 -0
- data/build.gradle +123 -0
- data/config/checkstyle/checkstyle.xml +128 -0
- data/config/checkstyle/default.xml +108 -0
- data/gradle/wrapper/gradle-wrapper.jar +0 -0
- data/gradle/wrapper/gradle-wrapper.properties +5 -0
- data/gradlew +172 -0
- data/gradlew.bat +84 -0
- data/lib/embulk/guess/zendesk.rb +21 -0
- data/lib/embulk/input/zendesk.rb +3 -0
- data/src/main/java/org/embulk/input/zendesk/RecordImporter.java +134 -0
- data/src/main/java/org/embulk/input/zendesk/ZendeskInputPlugin.java +513 -0
- data/src/main/java/org/embulk/input/zendesk/clients/ZendeskRestClient.java +291 -0
- data/src/main/java/org/embulk/input/zendesk/models/AuthenticationMethod.java +23 -0
- data/src/main/java/org/embulk/input/zendesk/models/Target.java +47 -0
- data/src/main/java/org/embulk/input/zendesk/models/ZendeskException.java +25 -0
- data/src/main/java/org/embulk/input/zendesk/services/ZendeskCustomObjectService.java +110 -0
- data/src/main/java/org/embulk/input/zendesk/services/ZendeskNPSService.java +30 -0
- data/src/main/java/org/embulk/input/zendesk/services/ZendeskNormalServices.java +347 -0
- data/src/main/java/org/embulk/input/zendesk/services/ZendeskService.java +14 -0
- data/src/main/java/org/embulk/input/zendesk/services/ZendeskSupportAPIService.java +63 -0
- data/src/main/java/org/embulk/input/zendesk/services/ZendeskUserEventService.java +158 -0
- data/src/main/java/org/embulk/input/zendesk/stream/PagingSpliterator.java +40 -0
- data/src/main/java/org/embulk/input/zendesk/stream/paginator/sunshine/CustomObjectSpliterator.java +42 -0
- data/src/main/java/org/embulk/input/zendesk/stream/paginator/sunshine/SunshineSpliterator.java +66 -0
- data/src/main/java/org/embulk/input/zendesk/stream/paginator/sunshine/UserEventSpliterator.java +35 -0
- data/src/main/java/org/embulk/input/zendesk/stream/paginator/support/OrganizationSpliterator.java +13 -0
- data/src/main/java/org/embulk/input/zendesk/stream/paginator/support/SupportSpliterator.java +44 -0
- data/src/main/java/org/embulk/input/zendesk/stream/paginator/support/UserSpliterator.java +13 -0
- data/src/main/java/org/embulk/input/zendesk/utils/ZendeskConstants.java +72 -0
- data/src/main/java/org/embulk/input/zendesk/utils/ZendeskDateUtils.java +68 -0
- data/src/main/java/org/embulk/input/zendesk/utils/ZendeskUtils.java +92 -0
- data/src/test/java/org/embulk/input/zendesk/TestRecordImporter.java +114 -0
- data/src/test/java/org/embulk/input/zendesk/TestZendeskInputPlugin.java +402 -0
- data/src/test/java/org/embulk/input/zendesk/clients/TestZendeskRestClient.java +337 -0
- data/src/test/java/org/embulk/input/zendesk/services/TestZendeskCustomObjectService.java +161 -0
- data/src/test/java/org/embulk/input/zendesk/services/TestZendeskNPSService.java +56 -0
- data/src/test/java/org/embulk/input/zendesk/services/TestZendeskNormalService.java +261 -0
- data/src/test/java/org/embulk/input/zendesk/services/TestZendeskSupportAPIService.java +130 -0
- data/src/test/java/org/embulk/input/zendesk/services/TestZendeskUserEventService.java +158 -0
- data/src/test/java/org/embulk/input/zendesk/utils/TestZendeskDateUtils.java +87 -0
- data/src/test/java/org/embulk/input/zendesk/utils/TestZendeskUtil.java +22 -0
- data/src/test/java/org/embulk/input/zendesk/utils/ZendeskPluginTestRuntime.java +133 -0
- data/src/test/java/org/embulk/input/zendesk/utils/ZendeskTestHelper.java +92 -0
- data/src/test/resources/config/base.yml +14 -0
- data/src/test/resources/config/base_validator.yml +48 -0
- data/src/test/resources/config/incremental.yml +54 -0
- data/src/test/resources/config/non-incremental.yml +39 -0
- data/src/test/resources/config/nps.yml +31 -0
- data/src/test/resources/config/object_records.yml +24 -0
- data/src/test/resources/config/relationship_records.yml +23 -0
- data/src/test/resources/config/user_events.yml +29 -0
- data/src/test/resources/config/util.yml +18 -0
- data/src/test/resources/data/client.json +293 -0
- data/src/test/resources/data/duplicate_user.json +0 -0
- data/src/test/resources/data/empty_result.json +7 -0
- data/src/test/resources/data/error_data.json +187 -0
- data/src/test/resources/data/expected/ticket_column.json +148 -0
- data/src/test/resources/data/expected/ticket_column_with_related_objects.json +152 -0
- data/src/test/resources/data/expected/ticket_fields_column.json +92 -0
- data/src/test/resources/data/expected/ticket_metrics_column.json +98 -0
- data/src/test/resources/data/expected/user_events_column.json +40 -0
- data/src/test/resources/data/object_records.json +30 -0
- data/src/test/resources/data/organization.json +39 -0
- data/src/test/resources/data/relationship_records.json +57 -0
- data/src/test/resources/data/scores.json +21 -0
- data/src/test/resources/data/scores_share_same_time_with_next_page.json +35 -0
- data/src/test/resources/data/scores_share_same_time_without_next_page.json +35 -0
- data/src/test/resources/data/simple_organization.json +23 -0
- data/src/test/resources/data/simple_user.json +50 -0
- data/src/test/resources/data/simple_user_event.json +19 -0
- data/src/test/resources/data/ticket_events_share_same_time_with_next_page.json +279 -0
- data/src/test/resources/data/ticket_events_share_same_time_without_next_page.json +279 -0
- data/src/test/resources/data/ticket_events_updated_by_system_records.json +279 -0
- data/src/test/resources/data/ticket_fields.json +225 -0
- data/src/test/resources/data/ticket_metrics.json +397 -0
- data/src/test/resources/data/ticket_share_same_time_with_next_page.json +232 -0
- data/src/test/resources/data/ticket_share_same_time_without_next_page.json +232 -0
- data/src/test/resources/data/ticket_with_related_objects.json +67 -0
- data/src/test/resources/data/ticket_with_updated_by_system_records.json +187 -0
- data/src/test/resources/data/tickets.json +232 -0
- data/src/test/resources/data/tickets_continue.json +52 -0
- data/src/test/resources/data/user_event.json +19 -0
- data/src/test/resources/data/user_event_contain_latter_create_at.json +19 -0
- data/src/test/resources/data/user_event_multiple.json +33 -0
- data/src/test/resources/data/util.json +19 -0
- data/src/test/resources/data/util_page.json +227 -0
- 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
|
+
}
|