embulk-input-marketo-extended 0.6.18

Sign up to get free protection for your applications and to get access to all the features.
Files changed (88) hide show
  1. checksums.yaml +7 -0
  2. data/.github/PULL_REQUEST_TEMPLATE.md +37 -0
  3. data/.gitignore +14 -0
  4. data/.travis.yml +6 -0
  5. data/CHANGELOG.md +170 -0
  6. data/LICENSE.txt +21 -0
  7. data/README.md +213 -0
  8. data/build.gradle +103 -0
  9. data/config/checkstyle/checkstyle.xml +128 -0
  10. data/config/checkstyle/default.xml +108 -0
  11. data/gradle/wrapper/gradle-wrapper.jar +0 -0
  12. data/gradle/wrapper/gradle-wrapper.properties +6 -0
  13. data/gradlew +169 -0
  14. data/gradlew.bat +84 -0
  15. data/lib/embulk/input/marketo.rb +3 -0
  16. data/settings.gradle +1 -0
  17. data/src/main/java/org/embulk/input/marketo/CsvTokenizer.java +700 -0
  18. data/src/main/java/org/embulk/input/marketo/MarketoInputPlugin.java +15 -0
  19. data/src/main/java/org/embulk/input/marketo/MarketoInputPluginDelegate.java +100 -0
  20. data/src/main/java/org/embulk/input/marketo/MarketoService.java +38 -0
  21. data/src/main/java/org/embulk/input/marketo/MarketoServiceImpl.java +245 -0
  22. data/src/main/java/org/embulk/input/marketo/MarketoUtils.java +212 -0
  23. data/src/main/java/org/embulk/input/marketo/delegate/ActivityBulkExtractInputPlugin.java +167 -0
  24. data/src/main/java/org/embulk/input/marketo/delegate/CampaignInputPlugin.java +48 -0
  25. data/src/main/java/org/embulk/input/marketo/delegate/CustomObjectInputPlugin.java +75 -0
  26. data/src/main/java/org/embulk/input/marketo/delegate/CustomObjectResponseMapperBuilder.java +81 -0
  27. data/src/main/java/org/embulk/input/marketo/delegate/LeadBulkExtractInputPlugin.java +66 -0
  28. data/src/main/java/org/embulk/input/marketo/delegate/LeadServiceResponseMapperBuilder.java +85 -0
  29. data/src/main/java/org/embulk/input/marketo/delegate/LeadWithListInputPlugin.java +64 -0
  30. data/src/main/java/org/embulk/input/marketo/delegate/LeadWithProgramInputPlugin.java +60 -0
  31. data/src/main/java/org/embulk/input/marketo/delegate/MarketoBaseBulkExtractInputPlugin.java +441 -0
  32. data/src/main/java/org/embulk/input/marketo/delegate/MarketoBaseInputPluginDelegate.java +92 -0
  33. data/src/main/java/org/embulk/input/marketo/delegate/ProgramInputPlugin.java +228 -0
  34. data/src/main/java/org/embulk/input/marketo/exception/MarketoAPIException.java +30 -0
  35. data/src/main/java/org/embulk/input/marketo/model/BulkExtractRangeHeader.java +26 -0
  36. data/src/main/java/org/embulk/input/marketo/model/MarketoAccessTokenResponse.java +92 -0
  37. data/src/main/java/org/embulk/input/marketo/model/MarketoBulkExtractRequest.java +68 -0
  38. data/src/main/java/org/embulk/input/marketo/model/MarketoError.java +40 -0
  39. data/src/main/java/org/embulk/input/marketo/model/MarketoField.java +126 -0
  40. data/src/main/java/org/embulk/input/marketo/model/MarketoResponse.java +82 -0
  41. data/src/main/java/org/embulk/input/marketo/model/filter/DateRangeFilter.java +40 -0
  42. data/src/main/java/org/embulk/input/marketo/rest/MarketoBaseRestClient.java +306 -0
  43. data/src/main/java/org/embulk/input/marketo/rest/MarketoInputStreamResponseEntityReader.java +69 -0
  44. data/src/main/java/org/embulk/input/marketo/rest/MarketoRESTEndpoint.java +47 -0
  45. data/src/main/java/org/embulk/input/marketo/rest/MarketoResponseJetty92EntityReader.java +89 -0
  46. data/src/main/java/org/embulk/input/marketo/rest/MarketoRestClient.java +569 -0
  47. data/src/main/java/org/embulk/input/marketo/rest/RecordPagingIterable.java +180 -0
  48. data/src/test/java/org/embulk/input/marketo/MarketoServiceImplTest.java +140 -0
  49. data/src/test/java/org/embulk/input/marketo/MarketoUtilsTest.java +87 -0
  50. data/src/test/java/org/embulk/input/marketo/delegate/ActivityBulkExtractInputPluginTest.java +128 -0
  51. data/src/test/java/org/embulk/input/marketo/delegate/CampaignInputPluginTest.java +73 -0
  52. data/src/test/java/org/embulk/input/marketo/delegate/CustomObjectInputPluginTest.java +102 -0
  53. data/src/test/java/org/embulk/input/marketo/delegate/LeadBulkExtractInputPluginTest.java +99 -0
  54. data/src/test/java/org/embulk/input/marketo/delegate/LeadServiceResponseMapperBuilderTest.java +119 -0
  55. data/src/test/java/org/embulk/input/marketo/delegate/LeadWithListInputPluginTest.java +101 -0
  56. data/src/test/java/org/embulk/input/marketo/delegate/LeadWithProgramInputPluginTest.java +103 -0
  57. data/src/test/java/org/embulk/input/marketo/delegate/MarketoBaseBulkExtractInputPluginTest.java +169 -0
  58. data/src/test/java/org/embulk/input/marketo/delegate/ProgramInputPluginTest.java +343 -0
  59. data/src/test/java/org/embulk/input/marketo/rest/MarketoBaseRestClientTest.java +368 -0
  60. data/src/test/java/org/embulk/input/marketo/rest/MarketoRestClientTest.java +584 -0
  61. data/src/test/resources/config/activity_bulk_extract_config.yaml +7 -0
  62. data/src/test/resources/config/custom_object_config.yaml +8 -0
  63. data/src/test/resources/config/lead_bulk_extract_config.yaml +8 -0
  64. data/src/test/resources/config/rest_config.yaml +3 -0
  65. data/src/test/resources/fixtures/activity_extract1.csv +35 -0
  66. data/src/test/resources/fixtures/activity_extract2.csv +22 -0
  67. data/src/test/resources/fixtures/activity_types.json +22 -0
  68. data/src/test/resources/fixtures/all_program_full.json +53 -0
  69. data/src/test/resources/fixtures/campaign_response.json +38 -0
  70. data/src/test/resources/fixtures/campaign_response_full.json +102 -0
  71. data/src/test/resources/fixtures/custom_object_describe.json +124 -0
  72. data/src/test/resources/fixtures/custom_object_describe_marketo_fields_full.json +22 -0
  73. data/src/test/resources/fixtures/custom_object_expected.json +66 -0
  74. data/src/test/resources/fixtures/custom_object_response.json +24 -0
  75. data/src/test/resources/fixtures/custom_object_response_full.json +23 -0
  76. data/src/test/resources/fixtures/lead_by_list.json +33 -0
  77. data/src/test/resources/fixtures/lead_by_program_response.json +47 -0
  78. data/src/test/resources/fixtures/lead_describe.json +221 -0
  79. data/src/test/resources/fixtures/lead_describe_expected.json +66 -0
  80. data/src/test/resources/fixtures/lead_describe_marketo_fields_full.json +518 -0
  81. data/src/test/resources/fixtures/lead_extract1.csv +11 -0
  82. data/src/test/resources/fixtures/lead_response_full.json +2402 -0
  83. data/src/test/resources/fixtures/lead_with_program_full.json +17 -0
  84. data/src/test/resources/fixtures/leads_extract2.csv +10 -0
  85. data/src/test/resources/fixtures/list_reponse_full.json +191 -0
  86. data/src/test/resources/fixtures/lists_response.json +31 -0
  87. data/src/test/resources/fixtures/program_response.json +71 -0
  88. metadata +171 -0
@@ -0,0 +1,68 @@
1
+ package org.embulk.input.marketo.model;
2
+
3
+ import java.util.HashMap;
4
+ import java.util.List;
5
+ import java.util.Map;
6
+
7
+ /**
8
+ * Created by tai.khuu on 8/27/17.
9
+ */
10
+ public class MarketoBulkExtractRequest
11
+ {
12
+ private List<String> fields;
13
+ private String format;
14
+
15
+ private Map<String, String> columnHeaderNames;
16
+
17
+ private Map<String, Object> filter = new HashMap<>();
18
+
19
+ public List<String> getFields()
20
+ {
21
+ return fields;
22
+ }
23
+
24
+ public void setFields(List<String> fields)
25
+ {
26
+ this.fields = fields;
27
+ }
28
+
29
+ public String getFormat()
30
+ {
31
+ return format;
32
+ }
33
+
34
+ public void setFormat(String format)
35
+ {
36
+ this.format = format;
37
+ }
38
+
39
+ public Map<String, String> getColumnHeaderNames()
40
+ {
41
+ return columnHeaderNames;
42
+ }
43
+
44
+ public void setColumnHeaderNames(Map<String, String> columnHeaderNames)
45
+ {
46
+ this.columnHeaderNames = columnHeaderNames;
47
+ }
48
+
49
+ public Map<String, Object> getFilter()
50
+ {
51
+ return filter;
52
+ }
53
+
54
+ public void setFilter(Map<String, Object> filter)
55
+ {
56
+ this.filter = filter;
57
+ }
58
+
59
+ @Override
60
+ public String toString()
61
+ {
62
+ return "MarketoBulkExtractRequest{" +
63
+ "format='" + format + '\'' +
64
+ ", columnHeaderNames=" + columnHeaderNames +
65
+ ", filter=" + filter +
66
+ '}';
67
+ }
68
+ }
@@ -0,0 +1,40 @@
1
+ package org.embulk.input.marketo.model;
2
+
3
+ /**
4
+ * Created by tai.khuu on 8/25/17.
5
+ */
6
+ public class MarketoError
7
+ {
8
+ private String code;
9
+
10
+ private String message;
11
+
12
+ public String getCode()
13
+ {
14
+ return code;
15
+ }
16
+
17
+ public void setCode(String code)
18
+ {
19
+ this.code = code;
20
+ }
21
+
22
+ public String getMessage()
23
+ {
24
+ return message;
25
+ }
26
+
27
+ public void setMessage(String message)
28
+ {
29
+ this.message = message;
30
+ }
31
+
32
+ @Override
33
+ public String toString()
34
+ {
35
+ return "MarketoError{" +
36
+ "code='" + code + '\'' +
37
+ ", message='" + message + '\'' +
38
+ '}';
39
+ }
40
+ }
@@ -0,0 +1,126 @@
1
+ package org.embulk.input.marketo.model;
2
+
3
+ import com.google.common.base.Optional;
4
+ import org.embulk.input.marketo.MarketoUtils;
5
+ import org.embulk.spi.type.Type;
6
+ import org.embulk.spi.type.Types;
7
+
8
+ /**
9
+ * Created by tai.khuu on 9/22/17.
10
+ */
11
+
12
+ public class MarketoField
13
+ {
14
+ private String name;
15
+
16
+ private MarketoDataType marketoDataType;
17
+
18
+ public MarketoField(){}
19
+
20
+ public MarketoField(String name, String dataType)
21
+ {
22
+ this.name = name;
23
+ try {
24
+ marketoDataType = MarketoDataType.valueOf(dataType.toUpperCase());
25
+ }
26
+ catch (IllegalArgumentException ex) {
27
+ marketoDataType = MarketoDataType.STRING;
28
+ }
29
+ }
30
+
31
+ public MarketoField(String name, MarketoDataType marketoDataType)
32
+ {
33
+ this.name = name;
34
+ this.marketoDataType = marketoDataType;
35
+ }
36
+
37
+ public String getName()
38
+ {
39
+ return name;
40
+ }
41
+
42
+ public MarketoDataType getMarketoDataType()
43
+ {
44
+ return marketoDataType;
45
+ }
46
+
47
+ @Override
48
+ public boolean equals(Object o)
49
+ {
50
+ if (this == o) {
51
+ return true;
52
+ }
53
+ if (o == null || getClass() != o.getClass()) {
54
+ return false;
55
+ }
56
+
57
+ MarketoField field = (MarketoField) o;
58
+
59
+ if (name != null ? !name.equals(field.name) : field.name != null) {
60
+ return false;
61
+ }
62
+ return marketoDataType == field.marketoDataType;
63
+ }
64
+
65
+ @Override
66
+ public int hashCode()
67
+ {
68
+ int result = name != null ? name.hashCode() : 0;
69
+ result = 31 * result + (marketoDataType != null ? marketoDataType.hashCode() : 0);
70
+ return result;
71
+ }
72
+
73
+ public enum MarketoDataType
74
+ {
75
+ DATETIME(Types.TIMESTAMP, MarketoUtils.MARKETO_DATE_TIME_FORMAT),
76
+ EMAIL(Types.STRING),
77
+ FLOAT(Types.DOUBLE),
78
+ INTEGER(Types.LONG),
79
+ FORMULA(Types.STRING),
80
+ PERCENT(Types.DOUBLE),
81
+ URL(Types.STRING),
82
+ PHONE(Types.STRING),
83
+ TEXTAREA(Types.STRING),
84
+ TEXT(Types.STRING),
85
+ STRING(Types.STRING),
86
+ SCORE(Types.LONG),
87
+ BOOLEAN(Types.BOOLEAN),
88
+ CURRENCY(Types.DOUBLE),
89
+ DATE(Types.TIMESTAMP, MarketoUtils.MARKETO_DATE_FORMAT),
90
+ REFERENCE(Types.STRING);
91
+
92
+ private Type type;
93
+
94
+ private String format;
95
+
96
+ MarketoDataType(Type type, String format)
97
+ {
98
+ this.type = type;
99
+ this.format = format;
100
+ }
101
+
102
+ MarketoDataType(Type type)
103
+ {
104
+ this.type = type;
105
+ }
106
+
107
+ public Type getType()
108
+ {
109
+ return type;
110
+ }
111
+
112
+ public Optional<String> getFormat()
113
+ {
114
+ return Optional.fromNullable(format);
115
+ }
116
+ }
117
+
118
+ @Override
119
+ public String toString()
120
+ {
121
+ return "MarketoField{" +
122
+ "name='" + name + '\'' +
123
+ ", marketoDataType=" + marketoDataType +
124
+ '}';
125
+ }
126
+ }
@@ -0,0 +1,82 @@
1
+ package org.embulk.input.marketo.model;
2
+
3
+ import java.util.ArrayList;
4
+ import java.util.List;
5
+
6
+ /**
7
+ * Created by tai.khuu on 8/25/17.
8
+ */
9
+ public class MarketoResponse<T>
10
+ {
11
+ private String requestId;
12
+
13
+ private boolean success;
14
+
15
+ private String nextPageToken;
16
+
17
+ private boolean moreResult;
18
+
19
+ private List<MarketoError> errors;
20
+
21
+ private List<T> result = new ArrayList<>();
22
+
23
+ public String getRequestId()
24
+ {
25
+ return requestId;
26
+ }
27
+
28
+ public void setRequestId(String requestId)
29
+ {
30
+ this.requestId = requestId;
31
+ }
32
+
33
+ public boolean isSuccess()
34
+ {
35
+ return success;
36
+ }
37
+
38
+ public void setSuccess(boolean success)
39
+ {
40
+ this.success = success;
41
+ }
42
+
43
+ public List<MarketoError> getErrors()
44
+ {
45
+ return errors;
46
+ }
47
+
48
+ public void setErrors(List<MarketoError> errors)
49
+ {
50
+ this.errors = errors;
51
+ }
52
+
53
+ public List<T> getResult()
54
+ {
55
+ return result;
56
+ }
57
+
58
+ public void setResult(List<T> result)
59
+ {
60
+ this.result = result;
61
+ }
62
+
63
+ public String getNextPageToken()
64
+ {
65
+ return nextPageToken;
66
+ }
67
+
68
+ public void setNextPageToken(String nextPageToken)
69
+ {
70
+ this.nextPageToken = nextPageToken;
71
+ }
72
+
73
+ public boolean isMoreResult()
74
+ {
75
+ return moreResult;
76
+ }
77
+
78
+ public void setMoreResult(boolean moreResult)
79
+ {
80
+ this.moreResult = moreResult;
81
+ }
82
+ }
@@ -0,0 +1,40 @@
1
+ package org.embulk.input.marketo.model.filter;
2
+
3
+ /**
4
+ * Created by tai.khuu on 8/27/17.
5
+ */
6
+ public class DateRangeFilter
7
+ {
8
+ private String startAt;
9
+
10
+ private String endAt;
11
+
12
+ public String getStartAt()
13
+ {
14
+ return startAt;
15
+ }
16
+
17
+ public void setStartAt(String startAt)
18
+ {
19
+ this.startAt = startAt;
20
+ }
21
+
22
+ public String getEndAt()
23
+ {
24
+ return endAt;
25
+ }
26
+
27
+ public void setEndAt(String endAt)
28
+ {
29
+ this.endAt = endAt;
30
+ }
31
+
32
+ @Override
33
+ public String toString()
34
+ {
35
+ return "DateRangeFilter{" +
36
+ "startAt='" + startAt + '\'' +
37
+ ", endAt='" + endAt + '\'' +
38
+ '}';
39
+ }
40
+ }
@@ -0,0 +1,306 @@
1
+ package org.embulk.input.marketo.rest;
2
+
3
+ import com.fasterxml.jackson.databind.ObjectMapper;
4
+ import com.google.common.annotations.VisibleForTesting;
5
+ import com.google.common.base.Optional;
6
+ import com.google.common.collect.ArrayListMultimap;
7
+ import com.google.common.collect.Multimap;
8
+ import org.apache.commons.lang3.StringUtils;
9
+ import org.eclipse.jetty.client.HttpClient;
10
+ import org.eclipse.jetty.client.HttpResponseException;
11
+ import org.eclipse.jetty.client.api.ContentProvider;
12
+ import org.eclipse.jetty.client.api.Request;
13
+ import org.eclipse.jetty.client.api.Response;
14
+ import org.eclipse.jetty.client.util.StringContentProvider;
15
+ import org.eclipse.jetty.http.HttpMethod;
16
+ import org.embulk.config.ConfigException;
17
+ import org.embulk.input.marketo.exception.MarketoAPIException;
18
+ import org.embulk.input.marketo.model.MarketoAccessTokenResponse;
19
+ import org.embulk.input.marketo.model.MarketoError;
20
+ import org.embulk.spi.DataException;
21
+ import org.embulk.spi.Exec;
22
+ import org.embulk.util.retryhelper.jetty92.Jetty92ResponseReader;
23
+ import org.embulk.util.retryhelper.jetty92.Jetty92RetryHelper;
24
+ import org.embulk.util.retryhelper.jetty92.Jetty92SingleRequester;
25
+ import org.embulk.util.retryhelper.jetty92.StringJetty92ResponseEntityReader;
26
+ import org.slf4j.Logger;
27
+
28
+ import java.io.EOFException;
29
+ import java.io.IOException;
30
+ import java.net.SocketTimeoutException;
31
+ import java.nio.charset.StandardCharsets;
32
+ import java.util.Map;
33
+ import java.util.concurrent.ExecutionException;
34
+ import java.util.concurrent.TimeoutException;
35
+
36
+ import static com.fasterxml.jackson.core.JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS;
37
+ import static com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES;
38
+
39
+ /**
40
+ * Marketo base rest client
41
+ * Created by tai.khuu on 9/7/17.
42
+ */
43
+ public class MarketoBaseRestClient implements AutoCloseable
44
+ {
45
+ private static final Logger LOGGER = Exec.getLogger(MarketoBaseRestClient.class);
46
+
47
+ private static final String APPLICATION_JSON = "application/json";
48
+
49
+ private static final String AUTHORIZATION_HEADER = "Authorization";
50
+
51
+ private String identityEndPoint;
52
+
53
+ private String clientId;
54
+
55
+ private String clientSecret;
56
+
57
+ private String accessToken;
58
+
59
+ private int marketoLimitIntervalMillis;
60
+
61
+ private Jetty92RetryHelper retryHelper;
62
+
63
+ protected long readTimeoutMillis;
64
+
65
+ private Optional<String> partnerApiKey;
66
+
67
+ protected static final ObjectMapper OBJECT_MAPPER = new ObjectMapper().configure(FAIL_ON_UNKNOWN_PROPERTIES, false).configure(ALLOW_UNQUOTED_CONTROL_CHARS, false);
68
+
69
+ MarketoBaseRestClient(String identityEndPoint,
70
+ String clientId,
71
+ String clientSecret,
72
+ Optional<String> partnerApiKey,
73
+ int marketoLimitIntervalMillis,
74
+ long readTimeoutMillis,
75
+ Jetty92RetryHelper retryHelper)
76
+ {
77
+ this.identityEndPoint = identityEndPoint;
78
+ this.clientId = clientId;
79
+ this.clientSecret = clientSecret;
80
+ this.readTimeoutMillis = readTimeoutMillis;
81
+ this.retryHelper = retryHelper;
82
+ this.marketoLimitIntervalMillis = marketoLimitIntervalMillis;
83
+ this.partnerApiKey = partnerApiKey;
84
+ }
85
+
86
+ private void renewAccessToken()
87
+ {
88
+ accessToken = getAccessTokenWithWrappedException();
89
+ }
90
+
91
+ @VisibleForTesting
92
+ public String getAccessToken()
93
+ {
94
+ if (accessToken == null) {
95
+ synchronized (this) {
96
+ if (accessToken == null) {
97
+ accessToken = getAccessTokenWithWrappedException();
98
+ }
99
+ }
100
+ }
101
+ return accessToken;
102
+ }
103
+
104
+ private String requestAccessToken()
105
+ {
106
+ final Multimap<String, String> params = ArrayListMultimap.create();
107
+ params.put("client_id", clientId);
108
+ params.put("client_secret", clientSecret);
109
+ params.put("grant_type", "client_credentials");
110
+
111
+ // add partner api key to the request
112
+ if (partnerApiKey.isPresent()) {
113
+ LOGGER.info("> Request access_token with partner_id: {}", StringUtils.abbreviate(partnerApiKey.get(), 8));
114
+ params.put("partner_id", partnerApiKey.get());
115
+ }
116
+
117
+ String response = retryHelper.requestWithRetry(new StringJetty92ResponseEntityReader(readTimeoutMillis), new Jetty92SingleRequester()
118
+ {
119
+ @Override
120
+ public void requestOnce(HttpClient client, Response.Listener responseListener)
121
+ {
122
+ Request request = client.newRequest(identityEndPoint + MarketoRESTEndpoint.ACCESS_TOKEN.getEndpoint()).method(HttpMethod.GET);
123
+ for (String key : params.keySet()) {
124
+ for (String value : params.get(key)) {
125
+ request.param(key, value);
126
+ }
127
+ }
128
+ request.send(responseListener);
129
+ }
130
+
131
+ @Override
132
+ protected boolean isResponseStatusToRetry(Response response)
133
+ {
134
+ return response.getStatus() == 502;
135
+ }
136
+
137
+ @Override
138
+ protected boolean isExceptionToRetry(Exception exception)
139
+ {
140
+ if (exception instanceof TimeoutException || exception instanceof SocketTimeoutException || exception instanceof EOFException || super.isExceptionToRetry(exception)) {
141
+ return true;
142
+ }
143
+ // unwrap
144
+ if (exception instanceof ExecutionException || (exception instanceof IOException && exception.getCause() != null)) {
145
+ return this.toRetry((Exception) exception.getCause());
146
+ }
147
+ return false;
148
+ }
149
+ });
150
+
151
+ MarketoAccessTokenResponse accessTokenResponse;
152
+
153
+ try {
154
+ accessTokenResponse = OBJECT_MAPPER.readValue(response, MarketoAccessTokenResponse.class);
155
+ }
156
+ catch (IOException e) {
157
+ LOGGER.error("Exception when parse access token response", e);
158
+ throw new DataException("Can't parse access token response");
159
+ }
160
+ if (accessTokenResponse.hasError()) {
161
+ throw new DataException(accessTokenResponse.getErrorDescription());
162
+ }
163
+ LOGGER.info("Acquired new access token");
164
+ return accessTokenResponse.getAccessToken();
165
+ }
166
+
167
+ protected <T> T doGet(final String target, final Map<String, String> headers, final Multimap<String, String> params, Jetty92ResponseReader<T> responseReader)
168
+ {
169
+ return doRequestWithWrappedException(target, HttpMethod.GET, headers, params, null, responseReader);
170
+ }
171
+
172
+ protected <T> T doPost(final String target, final Map<String, String> headers, final Multimap<String, String> params, final String content, Jetty92ResponseReader<T> responseReader)
173
+ {
174
+ StringContentProvider contentProvider = null;
175
+ if (content != null) {
176
+ contentProvider = new StringContentProvider(APPLICATION_JSON, content, StandardCharsets.UTF_8);
177
+ }
178
+ return doPost(target, headers, params, responseReader, contentProvider);
179
+ }
180
+
181
+ protected <T> T doPost(final String target, final Map<String, String> headers, final Multimap<String, String> params, Jetty92ResponseReader<T> responseReader, final ContentProvider content)
182
+ {
183
+ return doRequestWithWrappedException(target, HttpMethod.POST, headers, params, content, responseReader);
184
+ }
185
+
186
+ private String getAccessTokenWithWrappedException()
187
+ {
188
+ try {
189
+ return requestAccessToken();
190
+ }
191
+ catch (Exception e) {
192
+ if (e instanceof HttpResponseException) {
193
+ throw new ConfigException(e.getMessage());
194
+ }
195
+ if (e.getCause() instanceof HttpResponseException) {
196
+ throw new ConfigException(e.getCause().getMessage());
197
+ }
198
+ throw e;
199
+ }
200
+ }
201
+
202
+ private <T> T doRequestWithWrappedException(final String target, final HttpMethod method, final Map<String, String> headers, final Multimap<String, String> params, final ContentProvider contentProvider, Jetty92ResponseReader<T> responseReader)
203
+ {
204
+ try {
205
+ return doRequest(target, method, headers, params, contentProvider, responseReader);
206
+ }
207
+ catch (Exception e) {
208
+ if (e instanceof MarketoAPIException || e instanceof HttpResponseException) {
209
+ throw new DataException(e.getMessage());
210
+ }
211
+ if (e.getCause() instanceof MarketoAPIException || e.getCause() instanceof HttpResponseException) {
212
+ throw new DataException(e.getCause().getMessage());
213
+ }
214
+ throw e;
215
+ }
216
+ }
217
+
218
+ protected <T> T doRequest(final String target, final HttpMethod method, final Map<String, String> headers, final Multimap<String, String> params, final ContentProvider contentProvider, Jetty92ResponseReader<T> responseReader)
219
+ {
220
+ return retryHelper.requestWithRetry(responseReader, new Jetty92SingleRequester()
221
+ {
222
+ @Override
223
+ public void requestOnce(HttpClient client, Response.Listener responseListener)
224
+ {
225
+ Request request = client.newRequest(target).method(method);
226
+ if (headers != null) {
227
+ for (String key : headers.keySet()) {
228
+ request.header(key, headers.get(key));
229
+ }
230
+ }
231
+ request.header(AUTHORIZATION_HEADER, "Bearer " + getAccessToken());
232
+ if (params != null) {
233
+ for (String key : params.keySet()) {
234
+ for (String value : params.get(key)) {
235
+ request.param(key, value);
236
+ }
237
+ }
238
+ }
239
+ LOGGER.info("CALLING {} -> {} - params: {}", method, target, params);
240
+ if (contentProvider != null) {
241
+ request.content(contentProvider);
242
+ }
243
+ request.send(responseListener);
244
+ }
245
+
246
+ @Override
247
+ protected boolean isResponseStatusToRetry(Response response)
248
+ {
249
+ //413 failed job
250
+ //414 failed job
251
+ //502 retry
252
+ return response.getStatus() / 4 != 100;
253
+ }
254
+
255
+ @Override
256
+ protected boolean isExceptionToRetry(Exception exception)
257
+ {
258
+ if (exception instanceof EOFException || exception instanceof TimeoutException || exception instanceof SocketTimeoutException || super.isExceptionToRetry(exception)) {
259
+ return true;
260
+ }
261
+ if (exception instanceof ExecutionException || (exception instanceof IOException && exception.getCause() != null)) {
262
+ return this.toRetry((Exception) exception.getCause());
263
+ }
264
+ if (exception instanceof MarketoAPIException) {
265
+ //Retry Authenticate Exception
266
+ MarketoError error = ((MarketoAPIException) exception).getMarketoErrors().get(0);
267
+ String code = error.getCode();
268
+ switch (code) {
269
+ case "602":
270
+ case "601":
271
+ LOGGER.info("Access token expired");
272
+ renewAccessToken();
273
+ return true;
274
+ case "606":
275
+ try {
276
+ Thread.sleep(marketoLimitIntervalMillis);
277
+ }
278
+ catch (InterruptedException e) {
279
+ LOGGER.error("Encounter exception when waiting for interval limit", e);
280
+ throw new DataException("Exception when wait for interval limit");
281
+ }
282
+ return true;
283
+ case "604":
284
+ case "608":
285
+ case "611":
286
+ case "615":
287
+ case "713":
288
+ case "1029":
289
+ return true;
290
+ default:
291
+ return false;
292
+ }
293
+ }
294
+ return false;
295
+ }
296
+ });
297
+ }
298
+
299
+ @Override
300
+ public void close()
301
+ {
302
+ if (retryHelper != null) {
303
+ retryHelper.close();
304
+ }
305
+ }
306
+ }