embulk-input-zendesk 0.2.14 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +9 -3
- data/.travis.yml +5 -44
- data/CHANGELOG.md +3 -0
- data/README.md +5 -5
- data/build.gradle +123 -0
- data/classpath/commons-codec-1.10.jar +0 -0
- data/classpath/commons-logging-1.2.jar +0 -0
- data/classpath/embulk-input-zendesk-0.3.0.jar +0 -0
- data/classpath/httpclient-4.5.6.jar +0 -0
- data/classpath/httpcore-4.4.10.jar +0 -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 -9
- data/src/main/java/org/embulk/input/zendesk/ZendeskInputPlugin.java +471 -0
- data/src/main/java/org/embulk/input/zendesk/clients/ZendeskRestClient.java +268 -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 +46 -0
- data/src/main/java/org/embulk/input/zendesk/models/ZendeskException.java +25 -0
- data/src/main/java/org/embulk/input/zendesk/services/ZendeskSupportAPIService.java +109 -0
- data/src/main/java/org/embulk/input/zendesk/utils/ZendeskConstants.java +61 -0
- data/src/main/java/org/embulk/input/zendesk/utils/ZendeskDateUtils.java +51 -0
- data/src/main/java/org/embulk/input/zendesk/utils/ZendeskUtils.java +150 -0
- data/src/main/java/org/embulk/input/zendesk/utils/ZendeskValidatorUtils.java +92 -0
- data/src/test/java/org/embulk/input/zendesk/TestZendeskInputPlugin.java +232 -0
- data/src/test/java/org/embulk/input/zendesk/clients/TestZendeskRestClient.java +351 -0
- data/src/test/java/org/embulk/input/zendesk/services/TestZendeskSupportAPIService.java +172 -0
- data/src/test/java/org/embulk/input/zendesk/utils/TestZendeskDateUtils.java +36 -0
- data/src/test/java/org/embulk/input/zendesk/utils/TestZendeskUtil.java +160 -0
- data/src/test/java/org/embulk/input/zendesk/utils/TestZendeskValidatorUtils.java +138 -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 +63 -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/util.yml +18 -0
- data/src/test/resources/data/client.json +293 -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/ticket_fields.json +225 -0
- data/src/test/resources/data/ticket_metrics.json +397 -0
- data/src/test/resources/data/ticket_with_related_objects.json +67 -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/util.json +19 -0
- data/src/test/resources/data/util_page.json +227 -0
- metadata +65 -221
- data/.ruby-version +0 -1
- data/.travis.yml.erb +0 -43
- data/Gemfile +0 -2
- data/Rakefile +0 -21
- data/embulk-input-zendesk.gemspec +0 -29
- data/gemfiles/embulk-0.8.0-latest +0 -4
- data/gemfiles/embulk-0.8.1 +0 -4
- data/gemfiles/embulk-latest +0 -4
- data/gemfiles/template.erb +0 -4
- data/lib/embulk/input/zendesk/client.rb +0 -434
- data/lib/embulk/input/zendesk/plugin.rb +0 -199
- data/test/capture_io.rb +0 -45
- data/test/embulk/input/zendesk/test_client.rb +0 -722
- data/test/embulk/input/zendesk/test_plugin.rb +0 -628
- data/test/fixture_helper.rb +0 -11
- data/test/fixtures/invalid_app_marketplace_lack_one_property.yml +0 -13
- data/test/fixtures/invalid_app_marketplace_lack_two_property.yml +0 -12
- data/test/fixtures/invalid_lack_username.yml +0 -9
- data/test/fixtures/invalid_unknown_auth.yml +0 -9
- data/test/fixtures/tickets.json +0 -44
- data/test/fixtures/valid_app_marketplace.yml +0 -14
- data/test/fixtures/valid_auth_basic.yml +0 -11
- data/test/fixtures/valid_auth_oauth.yml +0 -10
- data/test/fixtures/valid_auth_token.yml +0 -11
- data/test/override_assert_raise.rb +0 -21
- data/test/run-test.rb +0 -26
@@ -0,0 +1,61 @@
|
|
1
|
+
package org.embulk.input.zendesk.utils;
|
2
|
+
|
3
|
+
public class ZendeskConstants
|
4
|
+
{
|
5
|
+
private ZendeskConstants()
|
6
|
+
{
|
7
|
+
}
|
8
|
+
|
9
|
+
public static class Header
|
10
|
+
{
|
11
|
+
public static final String APPLICATION_JSON = "application/json";
|
12
|
+
|
13
|
+
public static final String ZENDESK_MARKETPLACE_NAME = "X-Zendesk-Marketplace-Name";
|
14
|
+
public static final String ZENDESK_MARKETPLACE_ORGANIZATION_ID = "X-Zendesk-Marketplace-Organization-Id";
|
15
|
+
public static final String ZENDESK_MARKETPLACE_APP_ID = "X-Zendesk-Marketplace-App-Id";
|
16
|
+
}
|
17
|
+
|
18
|
+
public static class Field
|
19
|
+
{
|
20
|
+
public static final String START_TIME = "start_time";
|
21
|
+
public static final String END_TIME = "end_time";
|
22
|
+
public static final String COUNT = "count";
|
23
|
+
public static final String GENERATED_TIMESTAMP = "generated_timestamp";
|
24
|
+
public static final String UPDATED_AT = "updated_at";
|
25
|
+
public static final String ID = "id";
|
26
|
+
}
|
27
|
+
|
28
|
+
public static class Url
|
29
|
+
{
|
30
|
+
public static final String API = "api/v2";
|
31
|
+
public static final String API_INCREMENTAL = API + "/incremental";
|
32
|
+
}
|
33
|
+
|
34
|
+
public static class Misc
|
35
|
+
{
|
36
|
+
public static final String RUBY_TIMESTAMP_FORMAT = "%Y-%m-%dT%H:%M:%S%z";
|
37
|
+
public static final String RUBY_TIMESTAMP_FORMAT_INPUT = "yyyy-MM-dd HH:mm:ss Z";
|
38
|
+
public static final String JAVA_TIMESTAMP_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'";
|
39
|
+
public static final String ISO_TIMESTAMP_FORMAT = "yyyy-MM-dd'T'HH:mm:ssXXX";
|
40
|
+
public static final String ISO_INSTANT = "yyyy-MM-dd'T'HH:mm:ss'Z'";
|
41
|
+
public static final String TOO_RECENT_START_TIME = "Too recent start_time.";
|
42
|
+
public static final String INVALID_END_POINT_RESPONSE = "\"error\":\"InvalidEndpoint\"";
|
43
|
+
public static final int RECORDS_SIZE_PER_PAGE = 100;
|
44
|
+
public static final int MAXIMUM_RECORDS_INCREMENTAL = 1000;
|
45
|
+
public static final int TOO_MANY_REQUEST = 429;
|
46
|
+
|
47
|
+
// 1 MB
|
48
|
+
public static final int GUESS_BUFFER_SIZE = 1024 * 1024;
|
49
|
+
}
|
50
|
+
|
51
|
+
public static class Regex
|
52
|
+
{
|
53
|
+
public static final String ID = "_id$";
|
54
|
+
public static final String HOST = "^(https:\\/\\/)?(www.)?([a-zA-Z0-9]+).zendesk.com/$";
|
55
|
+
}
|
56
|
+
|
57
|
+
public static class HttpStatus
|
58
|
+
{
|
59
|
+
public static final int TOO_MANY_REQUEST = 429;
|
60
|
+
}
|
61
|
+
}
|
@@ -0,0 +1,51 @@
|
|
1
|
+
package org.embulk.input.zendesk.utils;
|
2
|
+
|
3
|
+
import com.google.common.base.Joiner;
|
4
|
+
import org.embulk.spi.DataException;
|
5
|
+
|
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
|
+
import java.util.Arrays;
|
12
|
+
|
13
|
+
import java.util.List;
|
14
|
+
import java.util.Optional;
|
15
|
+
|
16
|
+
public class ZendeskDateUtils
|
17
|
+
{
|
18
|
+
private ZendeskDateUtils()
|
19
|
+
{
|
20
|
+
}
|
21
|
+
|
22
|
+
private static final List<String> supportedFormats = Arrays.asList(ZendeskConstants.Misc.ISO_INSTANT, ZendeskConstants.Misc.RUBY_TIMESTAMP_FORMAT_INPUT,
|
23
|
+
ZendeskConstants.Misc.JAVA_TIMESTAMP_FORMAT, ZendeskConstants.Misc.ISO_TIMESTAMP_FORMAT);
|
24
|
+
|
25
|
+
public static long isoToEpochSecond(final String time)
|
26
|
+
{
|
27
|
+
Optional<String> pattern = supportedTimeFormat(time, supportedFormats);
|
28
|
+
if (pattern.isPresent()) {
|
29
|
+
final DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern.get()).withZone(ZoneOffset.UTC);
|
30
|
+
final OffsetDateTime offsetDateTime = LocalDateTime.parse(time, formatter).atOffset(ZoneOffset.UTC);
|
31
|
+
return offsetDateTime.toInstant().getEpochSecond();
|
32
|
+
}
|
33
|
+
|
34
|
+
throw new DataException("Fail to parse value '" + time + "' follow formats " + "[ " + Joiner.on(",").join(supportedFormats) + "]");
|
35
|
+
}
|
36
|
+
|
37
|
+
private static Optional<String> supportedTimeFormat(final String value, final List<String> supportedFormats)
|
38
|
+
{
|
39
|
+
for (final String fmt : supportedFormats) {
|
40
|
+
final DateTimeFormatter formatter = DateTimeFormatter.ofPattern(fmt);
|
41
|
+
try {
|
42
|
+
formatter.parse(value);
|
43
|
+
return Optional.of(fmt);
|
44
|
+
}
|
45
|
+
catch (final DateTimeParseException e) {
|
46
|
+
// Do nothing
|
47
|
+
}
|
48
|
+
}
|
49
|
+
return Optional.empty();
|
50
|
+
}
|
51
|
+
}
|
@@ -0,0 +1,150 @@
|
|
1
|
+
package org.embulk.input.zendesk.utils;
|
2
|
+
|
3
|
+
import com.fasterxml.jackson.databind.JsonNode;
|
4
|
+
import com.google.common.base.Charsets;
|
5
|
+
import org.embulk.input.zendesk.models.Target;
|
6
|
+
|
7
|
+
import org.embulk.spi.Column;
|
8
|
+
import org.embulk.spi.ColumnVisitor;
|
9
|
+
|
10
|
+
import org.embulk.spi.Exec;
|
11
|
+
import org.embulk.spi.PageBuilder;
|
12
|
+
import org.embulk.spi.Schema;
|
13
|
+
import org.embulk.spi.json.JsonParser;
|
14
|
+
import org.embulk.spi.time.Timestamp;
|
15
|
+
import org.slf4j.Logger;
|
16
|
+
|
17
|
+
import java.util.Base64;
|
18
|
+
import java.util.function.Function;
|
19
|
+
|
20
|
+
public class ZendeskUtils
|
21
|
+
{
|
22
|
+
private static final Logger logger = Exec.getLogger(ZendeskUtils.class);
|
23
|
+
|
24
|
+
private ZendeskUtils()
|
25
|
+
{}
|
26
|
+
|
27
|
+
public static boolean isSupportAPIIncremental(final Target target)
|
28
|
+
{
|
29
|
+
return !Target.TICKET_FORMS.equals(target)
|
30
|
+
&& !Target.TICKET_FIELDS.equals(target);
|
31
|
+
}
|
32
|
+
|
33
|
+
public static String convertBase64(final String text)
|
34
|
+
{
|
35
|
+
return Base64.getEncoder().encodeToString(text.getBytes(Charsets.UTF_8));
|
36
|
+
}
|
37
|
+
|
38
|
+
public static int numberToSplitWithHintingInTask(int count)
|
39
|
+
{
|
40
|
+
return (int) Math.ceil((double) count / ZendeskConstants.Misc.RECORDS_SIZE_PER_PAGE);
|
41
|
+
}
|
42
|
+
|
43
|
+
public static synchronized void addRecord(JsonNode record, Schema schema, PageBuilder pageBuilder)
|
44
|
+
{
|
45
|
+
schema.visitColumns(new ColumnVisitor() {
|
46
|
+
@Override
|
47
|
+
public void jsonColumn(Column column)
|
48
|
+
{
|
49
|
+
JsonNode data = record.get(column.getName());
|
50
|
+
|
51
|
+
setColumn(column, data, (value) -> {
|
52
|
+
pageBuilder.setJson(column, new JsonParser().parse(value.toString()));
|
53
|
+
return null;
|
54
|
+
});
|
55
|
+
}
|
56
|
+
|
57
|
+
@Override
|
58
|
+
public void stringColumn(Column column)
|
59
|
+
{
|
60
|
+
JsonNode data = record.get(column.getName());
|
61
|
+
|
62
|
+
setColumn(column, data, (value) -> {
|
63
|
+
pageBuilder.setString(column, value.asText());
|
64
|
+
return null;
|
65
|
+
});
|
66
|
+
}
|
67
|
+
|
68
|
+
@Override
|
69
|
+
public void timestampColumn(Column column)
|
70
|
+
{
|
71
|
+
JsonNode data = record.get(column.getName());
|
72
|
+
setColumn(column, data, (value) -> {
|
73
|
+
Timestamp timestamp = getTimestampValue(value.asText());
|
74
|
+
if (timestamp == null) {
|
75
|
+
pageBuilder.setNull(column);
|
76
|
+
}
|
77
|
+
else {
|
78
|
+
pageBuilder.setTimestamp(column, timestamp);
|
79
|
+
}
|
80
|
+
return null;
|
81
|
+
});
|
82
|
+
}
|
83
|
+
|
84
|
+
@Override
|
85
|
+
public void booleanColumn(Column column)
|
86
|
+
{
|
87
|
+
JsonNode data = record.get(column.getName());
|
88
|
+
|
89
|
+
setColumn(column, data, (value) -> {
|
90
|
+
pageBuilder.setBoolean(column, value.asBoolean());
|
91
|
+
return null;
|
92
|
+
});
|
93
|
+
}
|
94
|
+
|
95
|
+
@Override
|
96
|
+
public void longColumn(Column column)
|
97
|
+
{
|
98
|
+
JsonNode data = record.get(column.getName());
|
99
|
+
|
100
|
+
setColumn(column, data, (value) -> {
|
101
|
+
pageBuilder.setLong(column, value.asLong());
|
102
|
+
return null;
|
103
|
+
});
|
104
|
+
}
|
105
|
+
|
106
|
+
@Override
|
107
|
+
public void doubleColumn(Column column)
|
108
|
+
{
|
109
|
+
JsonNode data = record.get(column.getName());
|
110
|
+
|
111
|
+
setColumn(column, data, (value) -> {
|
112
|
+
pageBuilder.setDouble(column, value.asDouble());
|
113
|
+
return null;
|
114
|
+
});
|
115
|
+
}
|
116
|
+
|
117
|
+
private void setColumn(Column column, JsonNode data, Function<JsonNode, Void> setter)
|
118
|
+
{
|
119
|
+
if (isNull(data)) {
|
120
|
+
pageBuilder.setNull(column);
|
121
|
+
return;
|
122
|
+
}
|
123
|
+
setter.apply(data);
|
124
|
+
}
|
125
|
+
});
|
126
|
+
pageBuilder.addRecord();
|
127
|
+
}
|
128
|
+
|
129
|
+
private static boolean isNull(JsonNode jsonNode)
|
130
|
+
{
|
131
|
+
return jsonNode == null || jsonNode.isNull();
|
132
|
+
}
|
133
|
+
|
134
|
+
/*
|
135
|
+
* For getting the timestamp value of the node
|
136
|
+
* Sometime if the parser could not parse the value then return null
|
137
|
+
* */
|
138
|
+
private static Timestamp getTimestampValue(String value)
|
139
|
+
{
|
140
|
+
Timestamp result = null;
|
141
|
+
try {
|
142
|
+
long timeStamp = ZendeskDateUtils.isoToEpochSecond(value);
|
143
|
+
result = Timestamp.ofEpochSecond(timeStamp);
|
144
|
+
}
|
145
|
+
catch (Exception e) {
|
146
|
+
logger.warn("Error when parse time stamp data " + value);
|
147
|
+
}
|
148
|
+
return result;
|
149
|
+
}
|
150
|
+
}
|
@@ -0,0 +1,92 @@
|
|
1
|
+
package org.embulk.input.zendesk.utils;
|
2
|
+
|
3
|
+
import org.embulk.config.ConfigException;
|
4
|
+
import org.embulk.input.zendesk.ZendeskInputPlugin;
|
5
|
+
import org.embulk.input.zendesk.services.ZendeskSupportAPIService;
|
6
|
+
import org.embulk.spi.Exec;
|
7
|
+
import org.slf4j.Logger;
|
8
|
+
|
9
|
+
import java.util.regex.Matcher;
|
10
|
+
import java.util.regex.Pattern;
|
11
|
+
|
12
|
+
public class ZendeskValidatorUtils
|
13
|
+
{
|
14
|
+
private ZendeskValidatorUtils(){}
|
15
|
+
|
16
|
+
private static final Logger logger = Exec.getLogger(ZendeskValidatorUtils.class);
|
17
|
+
|
18
|
+
public static void validateInputTask(final ZendeskInputPlugin.PluginTask task, final ZendeskSupportAPIService zendeskSupportAPIService)
|
19
|
+
{
|
20
|
+
validateHost(task.getLoginUrl());
|
21
|
+
validateAppMarketPlace(task.getAppMarketPlaceIntegrationName().isPresent(),
|
22
|
+
task.getAppMarketPlaceAppId().isPresent(),
|
23
|
+
task.getAppMarketPlaceOrgId().isPresent());
|
24
|
+
validateCredentials(task);
|
25
|
+
validateIncremental(task);
|
26
|
+
}
|
27
|
+
|
28
|
+
private static void validateHost(final String loginUrl)
|
29
|
+
{
|
30
|
+
final Matcher matcher = Pattern.compile(ZendeskConstants.Regex.HOST).matcher(loginUrl);
|
31
|
+
if (!matcher.matches()) {
|
32
|
+
throw new ConfigException(String.format("Login URL, '%s', is unmatched expectation. " +
|
33
|
+
"It should be followed this format: https://abc.zendesk.com/", loginUrl));
|
34
|
+
}
|
35
|
+
}
|
36
|
+
|
37
|
+
private static void validateCredentials(final ZendeskInputPlugin.PluginTask task)
|
38
|
+
{
|
39
|
+
switch (task.getAuthenticationMethod()) {
|
40
|
+
case OAUTH:
|
41
|
+
if (!task.getAccessToken().isPresent()) {
|
42
|
+
throw new ConfigException(String.format("access_token is required for authentication method '%s'",
|
43
|
+
task.getAuthenticationMethod().name().toLowerCase()));
|
44
|
+
}
|
45
|
+
break;
|
46
|
+
case TOKEN:
|
47
|
+
if (!task.getUsername().isPresent() || !task.getToken().isPresent()) {
|
48
|
+
throw new ConfigException(String.format("username and token are required for authentication method '%s'",
|
49
|
+
task.getAuthenticationMethod().name().toLowerCase()));
|
50
|
+
}
|
51
|
+
break;
|
52
|
+
case BASIC:
|
53
|
+
if (!task.getUsername().isPresent() || !task.getPassword().isPresent()) {
|
54
|
+
throw new ConfigException(String.format("username and password are required for authentication method '%s'",
|
55
|
+
task.getAuthenticationMethod().name().toLowerCase()));
|
56
|
+
}
|
57
|
+
break;
|
58
|
+
default:
|
59
|
+
throw new ConfigException("Unknown authentication method");
|
60
|
+
}
|
61
|
+
}
|
62
|
+
|
63
|
+
private static void validateAppMarketPlace(final boolean isAppMarketIntegrationNamePresent,
|
64
|
+
final boolean isAppMarketAppIdPresent,
|
65
|
+
final boolean isAppMarketOrgIdPresent)
|
66
|
+
{
|
67
|
+
final boolean isAllAvailable =
|
68
|
+
isAppMarketIntegrationNamePresent && isAppMarketAppIdPresent && isAppMarketOrgIdPresent;
|
69
|
+
final boolean isAllUnAvailable =
|
70
|
+
!isAppMarketIntegrationNamePresent && !isAppMarketAppIdPresent && !isAppMarketOrgIdPresent;
|
71
|
+
// All or nothing needed
|
72
|
+
if (!(isAllAvailable || isAllUnAvailable)) {
|
73
|
+
throw new ConfigException("All of app_marketplace_integration_name, app_marketplace_org_id, " +
|
74
|
+
"app_marketplace_app_id " +
|
75
|
+
"are required to fill out for Apps Marketplace API header");
|
76
|
+
}
|
77
|
+
}
|
78
|
+
|
79
|
+
private static void validateIncremental(final ZendeskInputPlugin.PluginTask task)
|
80
|
+
{
|
81
|
+
if (task.getIncremental()) {
|
82
|
+
if (!task.getDedup()) {
|
83
|
+
logger.warn("You've selected to skip de-duplicating records, result may contain duplicated data");
|
84
|
+
}
|
85
|
+
|
86
|
+
if (!ZendeskUtils.isSupportAPIIncremental(task.getTarget()) && task.getStartTime().isPresent()) {
|
87
|
+
logger.warn(String.format("Target: '%s' doesn't support incremental export API. Will be ignored start_time option",
|
88
|
+
task.getTarget()));
|
89
|
+
}
|
90
|
+
}
|
91
|
+
}
|
92
|
+
}
|
@@ -0,0 +1,232 @@
|
|
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.services.ZendeskSupportAPIService;
|
11
|
+
import org.embulk.input.zendesk.utils.ZendeskPluginTestRuntime;
|
12
|
+
import org.embulk.input.zendesk.utils.ZendeskTestHelper;
|
13
|
+
|
14
|
+
import org.embulk.spi.InputPlugin;
|
15
|
+
import org.embulk.spi.PageBuilder;
|
16
|
+
import org.embulk.spi.PageOutput;
|
17
|
+
import org.embulk.spi.Schema;
|
18
|
+
import org.embulk.spi.TestPageBuilderReader;
|
19
|
+
import org.junit.Assert;
|
20
|
+
|
21
|
+
import org.junit.Before;
|
22
|
+
import org.junit.Rule;
|
23
|
+
import org.junit.Test;
|
24
|
+
import org.mockito.Mockito;
|
25
|
+
|
26
|
+
import static org.junit.Assert.assertEquals;
|
27
|
+
|
28
|
+
import static org.mockito.ArgumentMatchers.any;
|
29
|
+
import static org.mockito.ArgumentMatchers.anyBoolean;
|
30
|
+
import static org.mockito.ArgumentMatchers.anyInt;
|
31
|
+
import static org.mockito.ArgumentMatchers.anyLong;
|
32
|
+
import static org.mockito.ArgumentMatchers.anyString;
|
33
|
+
|
34
|
+
import static org.mockito.Mockito.doReturn;
|
35
|
+
import static org.mockito.Mockito.mock;
|
36
|
+
import static org.mockito.Mockito.spy;
|
37
|
+
import static org.mockito.Mockito.times;
|
38
|
+
import static org.mockito.Mockito.verify;
|
39
|
+
import static org.mockito.Mockito.when;
|
40
|
+
|
41
|
+
import java.util.Collections;
|
42
|
+
import java.util.List;
|
43
|
+
import java.util.stream.Collectors;
|
44
|
+
import java.util.stream.IntStream;
|
45
|
+
|
46
|
+
public class TestZendeskInputPlugin
|
47
|
+
{
|
48
|
+
@Rule
|
49
|
+
@SuppressFBWarnings("URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
|
50
|
+
public ZendeskPluginTestRuntime embulk = new ZendeskPluginTestRuntime();
|
51
|
+
|
52
|
+
private ZendeskSupportAPIService zendeskSupportAPIService = mock(ZendeskSupportAPIService.class);
|
53
|
+
|
54
|
+
private ZendeskInputPlugin zendeskInputPlugin;
|
55
|
+
|
56
|
+
private TestPageBuilderReader.MockPageOutput output = new TestPageBuilderReader.MockPageOutput();
|
57
|
+
|
58
|
+
private PageBuilder pageBuilder = Mockito.mock(PageBuilder.class);
|
59
|
+
|
60
|
+
@Before
|
61
|
+
public void prepare()
|
62
|
+
{
|
63
|
+
zendeskInputPlugin = spy(new ZendeskInputPlugin());
|
64
|
+
when(zendeskInputPlugin.getZendeskSupportAPIService(any(ZendeskInputPlugin.PluginTask.class))).thenReturn(zendeskSupportAPIService);
|
65
|
+
doReturn(pageBuilder).when(zendeskInputPlugin).getPageBuilder(any(Schema.class), any(PageOutput.class));
|
66
|
+
}
|
67
|
+
|
68
|
+
@Test
|
69
|
+
public void testGuessGenerateColumnsForIncrementalTarget()
|
70
|
+
{
|
71
|
+
final ConfigSource src = ZendeskTestHelper.getConfigSource("base.yml");
|
72
|
+
src.set("target", "tickets");
|
73
|
+
setupTestGuessGenerateColumn(src, "data/tickets.json", "data/expected/ticket_column.json");
|
74
|
+
}
|
75
|
+
|
76
|
+
@Test
|
77
|
+
public void testGuessGenerateColumnsForIncrementalTargetIncludeRelatedObject()
|
78
|
+
{
|
79
|
+
final ConfigSource src = ZendeskTestHelper.getConfigSource("base.yml");
|
80
|
+
src.set("target", "tickets");
|
81
|
+
src.set("includes", Collections.singletonList("organizations"));
|
82
|
+
setupTestGuessGenerateColumn(src, "data/tickets.json", "data/expected/ticket_column_with_related_objects.json");
|
83
|
+
}
|
84
|
+
|
85
|
+
@Test
|
86
|
+
public void testGuessGenerateColumnsForTicketMetrics()
|
87
|
+
{
|
88
|
+
final ConfigSource src = ZendeskTestHelper.getConfigSource("base.yml");
|
89
|
+
src.set("target", "ticket_metrics");
|
90
|
+
setupTestGuessGenerateColumn(src, "data/ticket_metrics.json", "data/expected/ticket_metrics_column.json");
|
91
|
+
}
|
92
|
+
|
93
|
+
@Test
|
94
|
+
public void testGuessGenerateColumnsForNonIncrementalTarget()
|
95
|
+
{
|
96
|
+
final ConfigSource src = ZendeskTestHelper.getConfigSource("base.yml");
|
97
|
+
src.set("target", "ticket_fields");
|
98
|
+
setupTestGuessGenerateColumn(src, "data/ticket_fields.json", "data/expected/ticket_fields_column.json");
|
99
|
+
}
|
100
|
+
|
101
|
+
@Test(expected = ConfigException.class)
|
102
|
+
public void testGuessFail()
|
103
|
+
{
|
104
|
+
final ConfigSource src = ZendeskTestHelper.getConfigSource("base.yml");
|
105
|
+
src.set("target", "tickets");
|
106
|
+
loadData("data/error_data.json");
|
107
|
+
|
108
|
+
zendeskInputPlugin.guess(src);
|
109
|
+
}
|
110
|
+
|
111
|
+
@Test
|
112
|
+
public void testRunIncrementalDedup()
|
113
|
+
{
|
114
|
+
final ConfigSource src = ZendeskTestHelper.getConfigSource("incremental.yml");
|
115
|
+
JsonNode dataJson = ZendeskTestHelper.getJsonFromFile("data/tickets.json");
|
116
|
+
|
117
|
+
when(zendeskSupportAPIService.getData(anyString(), anyInt(), anyBoolean(), anyLong())).thenReturn(dataJson);
|
118
|
+
|
119
|
+
ConfigDiff configDiff = zendeskInputPlugin.transaction(src, new Control());
|
120
|
+
String nextStartTime = configDiff.get(String.class, "start_time");
|
121
|
+
verify(pageBuilder, times(4)).addRecord();
|
122
|
+
verify(pageBuilder, times(1)).finish();
|
123
|
+
assertEquals("2019-02-20 07:17:34 +0000", nextStartTime);
|
124
|
+
}
|
125
|
+
|
126
|
+
@Test
|
127
|
+
public void testRunIncrementalWithNextPage()
|
128
|
+
{
|
129
|
+
final ConfigSource src = ZendeskTestHelper.getConfigSource("incremental.yml");
|
130
|
+
JsonNode dataJson = ZendeskTestHelper.getJsonFromFile("data/tickets_continue.json");
|
131
|
+
JsonNode dataJsonNext = ZendeskTestHelper.getJsonFromFile("data/tickets.json");
|
132
|
+
when(zendeskSupportAPIService.getData(anyString(), anyInt(), anyBoolean(), anyLong()))
|
133
|
+
.thenReturn(dataJson)
|
134
|
+
.thenReturn(dataJsonNext);
|
135
|
+
|
136
|
+
ConfigDiff configDiff = zendeskInputPlugin.transaction(src, new Control());
|
137
|
+
String nextStartTime = configDiff.get(String.class, "start_time");
|
138
|
+
verify(pageBuilder, times(1)).addRecord();
|
139
|
+
verify(pageBuilder, times(1)).finish();
|
140
|
+
assertEquals("2019-02-20 07:17:34 +0000", nextStartTime);
|
141
|
+
}
|
142
|
+
|
143
|
+
@Test
|
144
|
+
public void testRunIncrementalNonDedup()
|
145
|
+
{
|
146
|
+
final ConfigSource src = ZendeskTestHelper.getConfigSource("incremental.yml");
|
147
|
+
src.set("dedup", false);
|
148
|
+
|
149
|
+
JsonNode dataJsonNext = ZendeskTestHelper.getJsonFromFile("data/tickets.json");
|
150
|
+
when(zendeskSupportAPIService.getData(anyString(), anyInt(), anyBoolean(), anyLong())).thenReturn(dataJsonNext);
|
151
|
+
|
152
|
+
ConfigDiff configDiff = zendeskInputPlugin.transaction(src, new Control());
|
153
|
+
String nextStartTime = configDiff.get(String.class, "start_time");
|
154
|
+
verify(pageBuilder, times(5)).addRecord();
|
155
|
+
verify(pageBuilder, times(1)).finish();
|
156
|
+
assertEquals("2019-02-20 07:17:34 +0000", nextStartTime);
|
157
|
+
}
|
158
|
+
|
159
|
+
@Test
|
160
|
+
public void testRunIncrementalForTicketMetrics()
|
161
|
+
{
|
162
|
+
final ConfigSource src = ZendeskTestHelper.getConfigSource("incremental.yml");
|
163
|
+
src.set("target", "ticket_metrics");
|
164
|
+
JsonNode dataJson = ZendeskTestHelper.getJsonFromFile("data/ticket_metrics.json");
|
165
|
+
|
166
|
+
when(zendeskSupportAPIService.getData(anyString(), anyInt(), anyBoolean(), anyLong())).thenReturn(dataJson);
|
167
|
+
|
168
|
+
ConfigDiff configDiff = zendeskInputPlugin.transaction(src, new Control());
|
169
|
+
String nextStartTime = configDiff.get(String.class, "start_time");
|
170
|
+
verify(pageBuilder, times(3)).addRecord();
|
171
|
+
verify(pageBuilder, times(1)).finish();
|
172
|
+
assertEquals("2019-02-20 07:17:34 +0000", nextStartTime);
|
173
|
+
}
|
174
|
+
|
175
|
+
@Test
|
176
|
+
public void testRunIncrementalWithRelatedObject()
|
177
|
+
{
|
178
|
+
final ConfigSource src = ZendeskTestHelper.getConfigSource("incremental.yml");
|
179
|
+
src.set("includes", Collections.singletonList("organizations"));
|
180
|
+
|
181
|
+
JsonNode dataJson = ZendeskTestHelper.getJsonFromFile("data/tickets.json");
|
182
|
+
JsonNode dataJsonObject = ZendeskTestHelper.getJsonFromFile("data/ticket_with_related_objects.json");
|
183
|
+
when(zendeskSupportAPIService.getData(anyString(), anyInt(), anyBoolean(), anyLong()))
|
184
|
+
.thenReturn(dataJson)
|
185
|
+
.thenReturn(dataJsonObject);
|
186
|
+
|
187
|
+
ConfigDiff configDiff = zendeskInputPlugin.transaction(src, new Control());
|
188
|
+
|
189
|
+
verify(pageBuilder, times(1)).finish();
|
190
|
+
String nextStartTime = configDiff.get(String.class, "start_time");
|
191
|
+
assertEquals("2019-02-20 07:17:34 +0000", nextStartTime);
|
192
|
+
}
|
193
|
+
|
194
|
+
@Test
|
195
|
+
public void testRunNonIncremental()
|
196
|
+
{
|
197
|
+
final ConfigSource src = ZendeskTestHelper.getConfigSource("non-incremental.yml");
|
198
|
+
loadData("data/ticket_fields.json");
|
199
|
+
|
200
|
+
ConfigDiff configDiff = zendeskInputPlugin.transaction(src, new Control());
|
201
|
+
// running in 2 pages
|
202
|
+
verify(pageBuilder, times(7 * 2)).addRecord();
|
203
|
+
verify(pageBuilder, times(2)).finish();
|
204
|
+
Assert.assertTrue(configDiff.isEmpty());
|
205
|
+
}
|
206
|
+
|
207
|
+
private class Control implements InputPlugin.Control
|
208
|
+
{
|
209
|
+
@Override
|
210
|
+
public List<TaskReport> run(final TaskSource taskSource, final Schema schema, final int taskCount)
|
211
|
+
{
|
212
|
+
List<TaskReport> reports = IntStream.range(0, taskCount)
|
213
|
+
.mapToObj(i -> zendeskInputPlugin.run(taskSource, schema, i, output))
|
214
|
+
.collect(Collectors.toList());
|
215
|
+
return reports;
|
216
|
+
}
|
217
|
+
}
|
218
|
+
|
219
|
+
private void loadData(String fileName)
|
220
|
+
{
|
221
|
+
JsonNode dataJson = ZendeskTestHelper.getJsonFromFile(fileName);
|
222
|
+
when(zendeskSupportAPIService.getData(anyString(), anyInt(), anyBoolean(), anyLong())).thenReturn(dataJson);
|
223
|
+
}
|
224
|
+
|
225
|
+
private void setupTestGuessGenerateColumn(ConfigSource src, String fileName, String expectedSource)
|
226
|
+
{
|
227
|
+
loadData(fileName);
|
228
|
+
ConfigDiff configDiff = zendeskInputPlugin.guess(src);
|
229
|
+
JsonNode columns = configDiff.get(JsonNode.class, "columns");
|
230
|
+
assertEquals(ZendeskTestHelper.getJsonFromFile(expectedSource), columns);
|
231
|
+
}
|
232
|
+
}
|