embulk-input-marketo-through-proxy 0.6.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. checksums.yaml +7 -0
  2. data/.github/CODEOWNERS +1 -0
  3. data/.github/PULL_REQUEST_TEMPLATE.md +37 -0
  4. data/.github/workflows/build.yml +38 -0
  5. data/.gitignore +14 -0
  6. data/CHANGELOG.md +178 -0
  7. data/LICENSE.txt +21 -0
  8. data/README.md +231 -0
  9. data/build.gradle +105 -0
  10. data/config/checkstyle/checkstyle.xml +128 -0
  11. data/config/checkstyle/default.xml +108 -0
  12. data/gradle/wrapper/gradle-wrapper.jar +0 -0
  13. data/gradle/wrapper/gradle-wrapper.properties +6 -0
  14. data/gradlew +169 -0
  15. data/gradlew.bat +84 -0
  16. data/lib/embulk/input/marketo.rb +3 -0
  17. data/settings.gradle +1 -0
  18. data/src/main/java/org/embulk/input/marketo/CsvTokenizer.java +695 -0
  19. data/src/main/java/org/embulk/input/marketo/MarketoInputPlugin.java +15 -0
  20. data/src/main/java/org/embulk/input/marketo/MarketoInputPluginDelegate.java +100 -0
  21. data/src/main/java/org/embulk/input/marketo/MarketoService.java +47 -0
  22. data/src/main/java/org/embulk/input/marketo/MarketoServiceImpl.java +258 -0
  23. data/src/main/java/org/embulk/input/marketo/MarketoUtils.java +212 -0
  24. data/src/main/java/org/embulk/input/marketo/delegate/ActivityBulkExtractInputPlugin.java +169 -0
  25. data/src/main/java/org/embulk/input/marketo/delegate/CampaignInputPlugin.java +48 -0
  26. data/src/main/java/org/embulk/input/marketo/delegate/CustomObjectInputPlugin.java +124 -0
  27. data/src/main/java/org/embulk/input/marketo/delegate/CustomObjectResponseMapperBuilder.java +81 -0
  28. data/src/main/java/org/embulk/input/marketo/delegate/LeadBulkExtractInputPlugin.java +68 -0
  29. data/src/main/java/org/embulk/input/marketo/delegate/LeadServiceResponseMapperBuilder.java +85 -0
  30. data/src/main/java/org/embulk/input/marketo/delegate/LeadWithListInputPlugin.java +89 -0
  31. data/src/main/java/org/embulk/input/marketo/delegate/LeadWithProgramInputPlugin.java +85 -0
  32. data/src/main/java/org/embulk/input/marketo/delegate/MarketoBaseBulkExtractInputPlugin.java +448 -0
  33. data/src/main/java/org/embulk/input/marketo/delegate/MarketoBaseInputPluginDelegate.java +160 -0
  34. data/src/main/java/org/embulk/input/marketo/delegate/ProgramInputPlugin.java +234 -0
  35. data/src/main/java/org/embulk/input/marketo/exception/MarketoAPIException.java +30 -0
  36. data/src/main/java/org/embulk/input/marketo/model/BulkExtractRangeHeader.java +26 -0
  37. data/src/main/java/org/embulk/input/marketo/model/MarketoAccessTokenResponse.java +92 -0
  38. data/src/main/java/org/embulk/input/marketo/model/MarketoBulkExtractRequest.java +68 -0
  39. data/src/main/java/org/embulk/input/marketo/model/MarketoError.java +40 -0
  40. data/src/main/java/org/embulk/input/marketo/model/MarketoField.java +126 -0
  41. data/src/main/java/org/embulk/input/marketo/model/MarketoResponse.java +82 -0
  42. data/src/main/java/org/embulk/input/marketo/model/filter/DateRangeFilter.java +40 -0
  43. data/src/main/java/org/embulk/input/marketo/rest/MarketoBaseRestClient.java +344 -0
  44. data/src/main/java/org/embulk/input/marketo/rest/MarketoInputStreamResponseEntityReader.java +69 -0
  45. data/src/main/java/org/embulk/input/marketo/rest/MarketoRESTEndpoint.java +47 -0
  46. data/src/main/java/org/embulk/input/marketo/rest/MarketoResponseJetty92EntityReader.java +89 -0
  47. data/src/main/java/org/embulk/input/marketo/rest/MarketoRestClient.java +601 -0
  48. data/src/main/java/org/embulk/input/marketo/rest/RecordPagingIterable.java +180 -0
  49. data/src/test/java/org/embulk/input/marketo/MarketoServiceImplTest.java +147 -0
  50. data/src/test/java/org/embulk/input/marketo/MarketoUtilsTest.java +89 -0
  51. data/src/test/java/org/embulk/input/marketo/delegate/ActivityBulkExtractInputPluginTest.java +129 -0
  52. data/src/test/java/org/embulk/input/marketo/delegate/CampaignInputPluginTest.java +73 -0
  53. data/src/test/java/org/embulk/input/marketo/delegate/CustomObjectInputPluginTest.java +175 -0
  54. data/src/test/java/org/embulk/input/marketo/delegate/LeadBulkExtractInputPluginTest.java +102 -0
  55. data/src/test/java/org/embulk/input/marketo/delegate/LeadServiceResponseMapperBuilderTest.java +119 -0
  56. data/src/test/java/org/embulk/input/marketo/delegate/LeadWithListInputPluginTest.java +132 -0
  57. data/src/test/java/org/embulk/input/marketo/delegate/LeadWithProgramInputPluginTest.java +134 -0
  58. data/src/test/java/org/embulk/input/marketo/delegate/MarketoBaseBulkExtractInputPluginTest.java +171 -0
  59. data/src/test/java/org/embulk/input/marketo/delegate/MarketoBaseInputPluginDelegateTest.java +60 -0
  60. data/src/test/java/org/embulk/input/marketo/delegate/ProgramInputPluginTest.java +325 -0
  61. data/src/test/java/org/embulk/input/marketo/rest/MarketoBaseRestClientTest.java +368 -0
  62. data/src/test/java/org/embulk/input/marketo/rest/MarketoRestClientTest.java +649 -0
  63. data/src/test/resources/config/activity_bulk_extract_config.yaml +7 -0
  64. data/src/test/resources/config/custom_object_config.yaml +8 -0
  65. data/src/test/resources/config/lead_bulk_extract_config.yaml +8 -0
  66. data/src/test/resources/config/rest_config.yaml +3 -0
  67. data/src/test/resources/fixtures/activity_extract1.csv +35 -0
  68. data/src/test/resources/fixtures/activity_extract2.csv +22 -0
  69. data/src/test/resources/fixtures/activity_types.json +22 -0
  70. data/src/test/resources/fixtures/all_program_full.json +53 -0
  71. data/src/test/resources/fixtures/campaign_response.json +38 -0
  72. data/src/test/resources/fixtures/campaign_response_full.json +102 -0
  73. data/src/test/resources/fixtures/custom_object_describe.json +124 -0
  74. data/src/test/resources/fixtures/custom_object_describe_marketo_fields_full.json +22 -0
  75. data/src/test/resources/fixtures/custom_object_expected.json +66 -0
  76. data/src/test/resources/fixtures/custom_object_response.json +24 -0
  77. data/src/test/resources/fixtures/custom_object_response_full.json +23 -0
  78. data/src/test/resources/fixtures/lead_by_list.json +33 -0
  79. data/src/test/resources/fixtures/lead_by_program_response.json +47 -0
  80. data/src/test/resources/fixtures/lead_describe.json +221 -0
  81. data/src/test/resources/fixtures/lead_describe_expected.json +66 -0
  82. data/src/test/resources/fixtures/lead_describe_marketo_fields_full.json +518 -0
  83. data/src/test/resources/fixtures/lead_extract1.csv +11 -0
  84. data/src/test/resources/fixtures/lead_response_full.json +2402 -0
  85. data/src/test/resources/fixtures/lead_with_program_full.json +17 -0
  86. data/src/test/resources/fixtures/leads_extract2.csv +10 -0
  87. data/src/test/resources/fixtures/list_reponse_full.json +191 -0
  88. data/src/test/resources/fixtures/lists_response.json +31 -0
  89. data/src/test/resources/fixtures/program_response.json +71 -0
  90. metadata +173 -0
@@ -0,0 +1,325 @@
1
+ package org.embulk.input.marketo.delegate;
2
+
3
+ import com.fasterxml.jackson.databind.JavaType;
4
+ import com.fasterxml.jackson.databind.ObjectMapper;
5
+ import com.fasterxml.jackson.databind.node.ObjectNode;
6
+ import com.google.common.base.Optional;
7
+ import com.google.common.base.Predicate;
8
+ import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
9
+ import org.embulk.EmbulkTestRuntime;
10
+ import org.embulk.base.restclient.ServiceResponseMapper;
11
+ import org.embulk.base.restclient.record.RecordImporter;
12
+ import org.embulk.base.restclient.record.ValueLocator;
13
+ import org.embulk.config.ConfigDiff;
14
+ import org.embulk.config.ConfigException;
15
+ import org.embulk.config.ConfigLoader;
16
+ import org.embulk.config.ConfigSource;
17
+ import org.embulk.config.TaskReport;
18
+ import org.embulk.input.marketo.MarketoUtils;
19
+ import org.embulk.input.marketo.delegate.ProgramInputPlugin.PluginTask;
20
+ import org.embulk.input.marketo.delegate.ProgramInputPlugin.QueryBy;
21
+ import org.embulk.input.marketo.rest.MarketoRestClient;
22
+ import org.embulk.input.marketo.rest.RecordPagingIterable;
23
+ import org.embulk.spi.PageBuilder;
24
+ import org.embulk.spi.Schema;
25
+ import org.junit.Before;
26
+ import org.junit.Rule;
27
+ import org.junit.Test;
28
+ import org.junit.rules.ExpectedException;
29
+ import org.junit.rules.RuleChain;
30
+ import org.junit.rules.TestRule;
31
+ import org.mockito.ArgumentCaptor;
32
+ import org.mockito.Mockito;
33
+
34
+ import java.io.IOException;
35
+ import java.time.Duration;
36
+ import java.time.OffsetDateTime;
37
+ import java.time.ZoneOffset;
38
+ import java.time.format.DateTimeFormatter;
39
+ import java.util.Arrays;
40
+ import java.util.Date;
41
+ import java.util.List;
42
+
43
+ import static org.junit.Assert.assertArrayEquals;
44
+ import static org.junit.Assert.assertEquals;
45
+
46
+ public class ProgramInputPluginTest
47
+ {
48
+ private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern(MarketoUtils.MARKETO_DATE_SIMPLE_DATE_FORMAT);
49
+
50
+ private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
51
+
52
+ @Rule
53
+ public EmbulkTestRuntime runtime = new EmbulkTestRuntime();
54
+
55
+ private ExpectedException thrown = ExpectedException.none();
56
+
57
+ @Rule
58
+ @SuppressFBWarnings("URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
59
+ public TestRule chain = RuleChain.outerRule(runtime).around(thrown);
60
+
61
+ private ConfigSource baseConfig;
62
+
63
+ private ProgramInputPlugin mockPlugin;
64
+
65
+ private MarketoRestClient mockRestClient;
66
+
67
+ @Before
68
+ public void setUp() throws Exception
69
+ {
70
+ mockPlugin = Mockito.spy(new ProgramInputPlugin());
71
+ baseConfig = config();
72
+ mockRestClient = Mockito.mock(MarketoRestClient.class);
73
+ Mockito.doReturn(mockRestClient).when(mockPlugin).createMarketoRestClient(Mockito.any(ProgramInputPlugin.PluginTask.class));
74
+ }
75
+
76
+ // -----------Verify configs --------------
77
+ @Test
78
+ public void testQueryByTagTypeConfigMissingTagType()
79
+ {
80
+ thrown.expect(ConfigException.class);
81
+ thrown.expectMessage("tag_type and tag_value are required when query by Tag Type");
82
+ ConfigSource config = baseConfig.set("query_by", Optional.of(QueryBy.TAG_TYPE)).set("tag_value", Optional.of("dummy"));
83
+ mockPlugin.validateInputTask(config.loadConfig(PluginTask.class));
84
+ }
85
+
86
+ @Test
87
+ public void testQueryByTagTypeConfigMissingTagValue()
88
+ {
89
+ thrown.expect(ConfigException.class);
90
+ thrown.expectMessage("tag_type and tag_value are required when query by Tag Type");
91
+ ConfigSource config = baseConfig.set("query_by", Optional.of(QueryBy.TAG_TYPE)).set("tag_type", Optional.of("dummy"));
92
+ mockPlugin.validateInputTask(config.loadConfig(PluginTask.class));
93
+ }
94
+
95
+ @Test
96
+ public void testQueryByDateRangeConfigMissingEarliestUpatedAt()
97
+ {
98
+ thrown.expect(ConfigException.class);
99
+ thrown.expectMessage("`earliest_updated_at` is required when query by Date Range");
100
+ ConfigSource config = baseConfig.set("query_by", Optional.of(QueryBy.DATE_RANGE)).set("latest_updated_at", Optional.of(Date.from(OffsetDateTime.now(ZoneOffset.UTC).minusDays(10).toInstant())));
101
+ mockPlugin.validateInputTask(config.loadConfig(PluginTask.class));
102
+ }
103
+
104
+ @Test
105
+ public void testQueryByDateRangeConfigMissingLatestUpdatedAt()
106
+ {
107
+ thrown.expect(ConfigException.class);
108
+ thrown.expectMessage("`latest_updated_at` is required when query by Date Range");
109
+ ConfigSource config = baseConfig.set("query_by", Optional.of(QueryBy.DATE_RANGE)).set("earliest_updated_at", Optional.of(Date.from(OffsetDateTime.now(ZoneOffset.UTC).minusDays(10).toInstant())));
110
+ mockPlugin.validateInputTask(config.loadConfig(PluginTask.class));
111
+ }
112
+
113
+ @Test
114
+ public void testQueryByDateRangeConfigMissingLatestUpdatedAtNonIncremental()
115
+ {
116
+ thrown.expect(ConfigException.class);
117
+ thrown.expectMessage("`latest_updated_at` is required when query by Date Range");
118
+ ConfigSource config = baseConfig
119
+ .set("query_by", Optional.of(QueryBy.DATE_RANGE))
120
+ .set("incremental", Boolean.FALSE)
121
+ .set("earliest_updated_at", Optional.of(Date.from(OffsetDateTime.now(ZoneOffset.UTC).minusDays(10).toInstant())));
122
+ mockPlugin.validateInputTask(config.loadConfig(PluginTask.class));
123
+ }
124
+
125
+ @Test
126
+ public void testNoErrorQueryByDateRangeConfigHasReportDurationNonIncremental()
127
+ {
128
+ ConfigSource config = baseConfig
129
+ .set("query_by", Optional.of(QueryBy.DATE_RANGE))
130
+ .set("report_duration", Optional.of(60L * 1000))
131
+ .set("incremental", Boolean.FALSE)
132
+ .set("earliest_updated_at", Optional.of(Date.from(OffsetDateTime.now(ZoneOffset.UTC).minusDays(10).toInstant())));
133
+ mockPlugin.validateInputTask(config.loadConfig(PluginTask.class));
134
+ }
135
+
136
+ @Test
137
+ public void testNoErrorQueryByDateRangeConfigHasReportDuration()
138
+ {
139
+ ConfigSource config = baseConfig
140
+ .set("report_duration", Optional.of(60L * 1000))
141
+ .set("query_by", Optional.of(QueryBy.DATE_RANGE))
142
+ .set("earliest_updated_at", Optional.of(Date.from(OffsetDateTime.now(ZoneOffset.UTC).minusDays(10).toInstant())));
143
+ mockPlugin.validateInputTask(config.loadConfig(PluginTask.class));
144
+ }
145
+
146
+ @Test
147
+ public void testQueryByDateRangeConfigHasEarliestUpdatedAtExceededNow()
148
+ {
149
+ OffsetDateTime earliestUpdatedAt = OffsetDateTime.now(ZoneOffset.UTC).plusDays(1);
150
+ OffsetDateTime latestUpdatedAt = OffsetDateTime.now(ZoneOffset.UTC).minusDays(2);
151
+ thrown.expect(ConfigException.class);
152
+ thrown.expectMessage(String.format("`earliest_updated_at` (%s) cannot precede the current date ", earliestUpdatedAt.format(DATE_FORMATTER)));
153
+ ConfigSource config = baseConfig
154
+ .set("query_by", Optional.of(QueryBy.DATE_RANGE))
155
+ .set("earliest_updated_at", Optional.of(Date.from(earliestUpdatedAt.toInstant())))
156
+ .set("latest_updated_at", Optional.of(Date.from(latestUpdatedAt.toInstant())));
157
+ mockPlugin.validateInputTask(config.loadConfig(PluginTask.class));
158
+ }
159
+
160
+ @Test
161
+ public void testQueryByDateRangeConfigHasEarliestUpdatedAtExceededLatestUpdatedAt()
162
+ {
163
+ OffsetDateTime earliestUpdatedAt = OffsetDateTime.now(ZoneOffset.UTC).minusDays(10);
164
+ OffsetDateTime latestUpdatedAt = OffsetDateTime.now(ZoneOffset.UTC).minusDays(20);
165
+ thrown.expect(ConfigException.class);
166
+ thrown.expectMessage(String.format("Invalid date range. `earliest_updated_at` (%s) cannot precede the `latest_updated_at` (%s).",
167
+ earliestUpdatedAt.format(DATE_FORMATTER),
168
+ latestUpdatedAt.format(DATE_FORMATTER)));
169
+
170
+ ConfigSource config = baseConfig
171
+ .set("query_by", Optional.of(QueryBy.DATE_RANGE))
172
+ .set("earliest_updated_at", Optional.of(Date.from(earliestUpdatedAt.toInstant())))
173
+ .set("latest_updated_at", Optional.of(Date.from(latestUpdatedAt.toInstant())));
174
+ mockPlugin.validateInputTask(config.loadConfig(PluginTask.class));
175
+ }
176
+
177
+ @Test
178
+ public void testHasFilterTypeButMissingFilterValue()
179
+ {
180
+ OffsetDateTime earliestUpdatedAt = OffsetDateTime.now(ZoneOffset.UTC).minusDays(20);
181
+ OffsetDateTime latestUpdatedAt = OffsetDateTime.now(ZoneOffset.UTC).minusDays(10);
182
+ thrown.expect(ConfigException.class);
183
+ thrown.expectMessage("filter_value is required when selected filter_type");
184
+
185
+ ConfigSource config = baseConfig
186
+ .set("query_by", Optional.of(QueryBy.DATE_RANGE))
187
+ .set("earliest_updated_at", Optional.of(Date.from(earliestUpdatedAt.toInstant())))
188
+ .set("latest_updated_at", Optional.of(Date.from(latestUpdatedAt.toInstant())))
189
+ .set("filter_type", Optional.of("dummy"));
190
+ mockPlugin.validateInputTask(config.loadConfig(PluginTask.class));
191
+ }
192
+
193
+ @Test
194
+ public void testSkipIncrementalRunIfLastUpdatedAtExceedsNow()
195
+ {
196
+ OffsetDateTime earliestUpdatedAt = OffsetDateTime.now(ZoneOffset.UTC).minusDays(20);
197
+ OffsetDateTime latestUpdatedAt = earliestUpdatedAt.plusDays(21);
198
+ //21 days
199
+ long reportDuration = 21 * 24 * 60 * 60 * 1000;
200
+
201
+ ConfigSource config = baseConfig
202
+ .set("query_by", Optional.of(QueryBy.DATE_RANGE))
203
+ .set("earliest_updated_at", Optional.of(Date.from(earliestUpdatedAt.toInstant())))
204
+ .set("latest_updated_at", Optional.of(Date.from(latestUpdatedAt.toInstant())))
205
+ .set("report_duration", reportDuration)
206
+ .set("incremental", true);
207
+ ServiceResponseMapper<? extends ValueLocator> mapper = mockPlugin.buildServiceResponseMapper(config.loadConfig(PluginTask.class));
208
+ RecordImporter recordImporter = mapper.createRecordImporter();
209
+ PageBuilder mockPageBuilder = Mockito.mock(PageBuilder.class);
210
+ TaskReport taskReport = mockPlugin.ingestServiceData(config.loadConfig(PluginTask.class), recordImporter, 1, mockPageBuilder);
211
+ // page builder object should never get called.
212
+ Mockito.verify(mockPageBuilder, Mockito.never()).addRecord();
213
+
214
+ String earliestUpdatedAtStr = taskReport.get(String.class, "earliest_updated_at");
215
+ long duration = taskReport.get(Long.class, "report_duration");
216
+ assertEquals(duration, reportDuration);
217
+ assertEquals(earliestUpdatedAtStr, earliestUpdatedAt.format(DATE_FORMATTER));
218
+ }
219
+
220
+ @Test
221
+ public void testBuildConfigDiff()
222
+ {
223
+ TaskReport taskReport1 = Mockito.mock(TaskReport.class);
224
+ OffsetDateTime earliestUpdatedAt = OffsetDateTime.now(ZoneOffset.UTC).minusDays(20);
225
+ OffsetDateTime latestUpdatedAt = OffsetDateTime.now(ZoneOffset.UTC).minusDays(10);
226
+ ConfigSource config = baseConfig
227
+ .set("query_by", Optional.of(QueryBy.DATE_RANGE))
228
+ .set("earliest_updated_at", Optional.of(Date.from(earliestUpdatedAt.toInstant())))
229
+ .set("latest_updated_at", Optional.of(Date.from(latestUpdatedAt.toInstant())))
230
+ .set("incremental", true);
231
+
232
+ ConfigDiff diff = mockPlugin.buildConfigDiff(config.loadConfig(PluginTask.class), Mockito.mock(Schema.class), 1, Arrays.asList(taskReport1));
233
+
234
+ long reportDuration = diff.get(Long.class, "report_duration");
235
+ String nextErliestUpdatedAt = diff.get(String.class, "earliest_updated_at");
236
+
237
+ assertEquals(reportDuration, Duration.between(earliestUpdatedAt, latestUpdatedAt).toMillis());
238
+ assertEquals(nextErliestUpdatedAt, latestUpdatedAt.plusSeconds(1).format(DATE_FORMATTER));
239
+ }
240
+
241
+ @SuppressWarnings("unchecked")
242
+ private void testRun(ConfigSource config, Predicate<MarketoRestClient> expectedCall) throws IOException
243
+ {
244
+ // Mock response data
245
+ RecordPagingIterable<ObjectNode> mockRecordPagingIterable = Mockito.mock(RecordPagingIterable.class);
246
+ JavaType javaType = OBJECT_MAPPER.getTypeFactory().constructParametrizedType(List.class, List.class, ObjectNode.class);
247
+ List<ObjectNode> objectNodeList = OBJECT_MAPPER.readValue(this.getClass().getResourceAsStream("/fixtures/all_program_full.json"), javaType);
248
+ Mockito.when(mockRecordPagingIterable.iterator()).thenReturn(objectNodeList.iterator());
249
+ Mockito.when(mockRestClient.getProgramsByTag(Mockito.anyString(), Mockito.anyString())).thenReturn(mockRecordPagingIterable);
250
+ Mockito.when(mockRestClient.getPrograms()).thenReturn(mockRecordPagingIterable);
251
+ Mockito.when(mockRestClient.getProgramsByDateRange(
252
+ Mockito.any(Date.class),
253
+ Mockito.any(Date.class),
254
+ Mockito.nullable(String.class),
255
+ Mockito.nullable(List.class))).thenReturn(mockRecordPagingIterable);
256
+
257
+ ServiceResponseMapper<? extends ValueLocator> mapper = mockPlugin.buildServiceResponseMapper(config.loadConfig(PluginTask.class));
258
+ RecordImporter recordImporter = mapper.createRecordImporter();
259
+ PageBuilder mockPageBuilder = Mockito.mock(PageBuilder.class);
260
+ mockPlugin.ingestServiceData(config.loadConfig(PluginTask.class), recordImporter, 1, mockPageBuilder);
261
+
262
+ // The method getProgramByTag is called 1 time
263
+ // Mockito.verify(mockRestClient, Mockito.times(1)).getProgramsByTag(Mockito.anyString(), Mockito.anyString());
264
+ expectedCall.apply(mockRestClient);
265
+
266
+ Schema embulkSchema = mapper.getEmbulkSchema();
267
+ // 17 columns
268
+ assertEquals(embulkSchema.size(), 17);
269
+ // verify 3 times the method setLong for column id has been called
270
+ ArgumentCaptor<Long> longArgumentCaptor = ArgumentCaptor.forClass(Long.class);
271
+ Mockito.verify(mockPageBuilder, Mockito.times(3)).setLong(Mockito.eq(embulkSchema.lookupColumn("id")), longArgumentCaptor.capture());
272
+ List<Long> allValues = longArgumentCaptor.getAllValues();
273
+ assertArrayEquals(new Long[]{1004L, 1001L, 1003L}, allValues.toArray());
274
+ }
275
+
276
+ @Test
277
+ public void testRunQueryByTagType() throws IOException
278
+ {
279
+ ConfigSource config = baseConfig
280
+ .set("query_by", Optional.of(QueryBy.TAG_TYPE))
281
+ .set("tag_type", Optional.of("dummy"))
282
+ .set("tag_value", Optional.of("dummy"));
283
+ Predicate<MarketoRestClient> expectedCall = mockRest -> {
284
+ Mockito.verify(mockRest, Mockito.times(1)).getProgramsByTag(Mockito.anyString(), Mockito.anyString());
285
+ return true;
286
+ };
287
+ testRun(config, expectedCall);
288
+ }
289
+
290
+ @Test
291
+ public void testRunWithoutQueryBy() throws IOException
292
+ {
293
+ Predicate<MarketoRestClient> expectedCall = input -> {
294
+ Mockito.verify(input, Mockito.times(1)).getPrograms();
295
+ return true;
296
+ };
297
+ testRun(baseConfig, expectedCall);
298
+ }
299
+
300
+ @Test
301
+ public void testRunQueryByDateRange() throws IOException
302
+ {
303
+ OffsetDateTime earliestUpdatedAt = OffsetDateTime.now(ZoneOffset.UTC).minusDays(20);
304
+ OffsetDateTime latestUpdatedAt = OffsetDateTime.now(ZoneOffset.UTC).minusDays(10);
305
+ ConfigSource config = baseConfig
306
+ .set("query_by", Optional.of(QueryBy.DATE_RANGE))
307
+ .set("earliest_updated_at", Optional.of(Date.from(earliestUpdatedAt.toInstant())))
308
+ .set("latest_updated_at", Optional.of(Date.from(latestUpdatedAt.toInstant())));
309
+ Predicate<MarketoRestClient> expectedCall = input -> {
310
+ Mockito.verify(input, Mockito.times(1)).getProgramsByDateRange(
311
+ Mockito.any(Date.class),
312
+ Mockito.any(Date.class),
313
+ Mockito.nullable(String.class),
314
+ Mockito.nullable(List.class));
315
+ return true;
316
+ };
317
+ testRun(config, expectedCall);
318
+ }
319
+
320
+ public ConfigSource config() throws IOException
321
+ {
322
+ ConfigLoader configLoader = runtime.getInjector().getInstance(ConfigLoader.class);
323
+ return configLoader.fromYaml(this.getClass().getResourceAsStream("/config/rest_config.yaml"));
324
+ }
325
+ }
@@ -0,0 +1,368 @@
1
+ package org.embulk.input.marketo.rest;
2
+
3
+ import com.google.common.base.Optional;
4
+ import com.google.common.collect.ImmutableListMultimap;
5
+ import com.google.common.collect.Lists;
6
+ import com.google.common.collect.Maps;
7
+ import com.google.common.collect.Multimap;
8
+ import org.eclipse.jetty.client.HttpClient;
9
+ import org.eclipse.jetty.client.HttpResponseException;
10
+ import org.eclipse.jetty.client.api.ContentProvider;
11
+ import org.eclipse.jetty.client.api.Request;
12
+ import org.eclipse.jetty.client.api.Response;
13
+ import org.eclipse.jetty.client.util.StringContentProvider;
14
+ import org.eclipse.jetty.http.HttpMethod;
15
+ import org.embulk.EmbulkTestRuntime;
16
+ import org.embulk.config.ConfigException;
17
+ import org.embulk.input.marketo.exception.MarketoAPIException;
18
+ import org.embulk.input.marketo.model.MarketoError;
19
+ import org.embulk.input.marketo.model.MarketoResponse;
20
+ import org.embulk.spi.DataException;
21
+ import org.embulk.util.retryhelper.jetty92.Jetty92ClientCreator;
22
+ import org.embulk.util.retryhelper.jetty92.Jetty92RetryHelper;
23
+ import org.embulk.util.retryhelper.jetty92.Jetty92SingleRequester;
24
+ import org.embulk.util.retryhelper.jetty92.StringJetty92ResponseEntityReader;
25
+ import org.junit.Assert;
26
+ import org.junit.Before;
27
+ import org.junit.Rule;
28
+ import org.junit.Test;
29
+ import org.junit.function.ThrowingRunnable;
30
+ import org.mockito.ArgumentCaptor;
31
+ import org.mockito.Mockito;
32
+
33
+ import java.io.EOFException;
34
+ import java.io.IOException;
35
+ import java.net.SocketTimeoutException;
36
+ import java.nio.charset.StandardCharsets;
37
+ import java.util.HashMap;
38
+ import java.util.Map;
39
+ import java.util.concurrent.ExecutionException;
40
+ import java.util.concurrent.TimeoutException;
41
+
42
+ /**
43
+ * Created by tai.khuu on 9/21/17.
44
+ */
45
+ public class MarketoBaseRestClientTest
46
+ {
47
+ private static final String IDENTITY_END_POINT = "identityEndPoint";
48
+
49
+ private static final int MARKETO_LIMIT_INTERVAL_MILIS = 1000;
50
+
51
+ private MarketoBaseRestClient marketoBaseRestClient;
52
+
53
+ private Jetty92RetryHelper mockJetty92;
54
+
55
+ @Rule
56
+ public EmbulkTestRuntime runtime = new EmbulkTestRuntime();
57
+ @Before
58
+ public void prepare()
59
+ {
60
+ mockJetty92 = Mockito.mock(Jetty92RetryHelper.class);
61
+ marketoBaseRestClient = new MarketoBaseRestClient("identityEndPoint", "clientId", "clientSecret", Optional.<String>absent(), MARKETO_LIMIT_INTERVAL_MILIS, 60000, mockJetty92);
62
+ }
63
+
64
+ @Test
65
+ public void testGetAccessToken()
66
+ {
67
+ Mockito.when(mockJetty92.requestWithRetry(Mockito.any(StringJetty92ResponseEntityReader.class), Mockito.any(Jetty92SingleRequester.class))).thenReturn("{\n" +
68
+ " \"access_token\": \"access_token\",\n" +
69
+ " \"token_type\": \"bearer\",\n" +
70
+ " \"expires_in\": 3599,\n" +
71
+ " \"scope\": \"tai@treasure-data.com\"\n" +
72
+ "}");
73
+ String accessToken = marketoBaseRestClient.getAccessToken();
74
+ Assert.assertEquals("access_token", accessToken);
75
+ }
76
+
77
+ @Test
78
+ public void testGetAccessTokenRequester()
79
+ {
80
+ ArgumentCaptor<Jetty92SingleRequester> jetty92SingleRequesterArgumentCaptor = ArgumentCaptor.forClass(Jetty92SingleRequester.class);
81
+ Mockito.when(mockJetty92.requestWithRetry(Mockito.any(StringJetty92ResponseEntityReader.class), jetty92SingleRequesterArgumentCaptor.capture())).thenReturn("{\"access_token\": \"access_token\"}");
82
+ String accessToken = marketoBaseRestClient.getAccessToken();
83
+ Assert.assertEquals("access_token", accessToken);
84
+ Jetty92SingleRequester jetty92SingleRequester = jetty92SingleRequesterArgumentCaptor.getValue();
85
+ HttpClient client = Mockito.mock(HttpClient.class);
86
+ Response.Listener listener = Mockito.mock(Response.Listener.class);
87
+ Request mockRequest = Mockito.mock(Request.class);
88
+ Mockito.when(client.newRequest(Mockito.eq(IDENTITY_END_POINT + MarketoRESTEndpoint.ACCESS_TOKEN.getEndpoint()))).thenReturn(mockRequest);
89
+ Request request1 = Mockito.mock(Request.class);
90
+ Mockito.when(mockRequest.method(Mockito.eq(HttpMethod.GET))).thenReturn(request1);
91
+ jetty92SingleRequester.requestOnce(client, listener);
92
+ Mockito.verify(request1, Mockito.times(1)).param(Mockito.eq("client_id"), Mockito.eq("clientId"));
93
+ Mockito.verify(request1, Mockito.times(1)).param(Mockito.eq("client_secret"), Mockito.eq("clientSecret"));
94
+ Mockito.verify(request1, Mockito.times(1)).param(Mockito.eq("grant_type"), Mockito.eq("client_credentials"));
95
+
96
+ // By default the partner id is not set
97
+ Mockito.verify(request1, Mockito.never()).param(Mockito.eq("partner_id"), Mockito.anyString());
98
+
99
+ Assert.assertTrue(jetty92SingleRequester.toRetry(createHttpResponseException(502)));
100
+ Assert.assertTrue(jetty92SingleRequester.toRetry(new ExecutionException(new TimeoutException())));
101
+ Assert.assertTrue(jetty92SingleRequester.toRetry(new ExecutionException(new EOFException())));
102
+ Assert.assertTrue(jetty92SingleRequester.toRetry(new ExecutionException(new SocketTimeoutException())));
103
+ // Retry SocketTimeoutException, TimeoutException and EOFException
104
+ Assert.assertTrue(jetty92SingleRequester.toRetry(new SocketTimeoutException()));
105
+ Assert.assertTrue(jetty92SingleRequester.toRetry(new TimeoutException()));
106
+ Assert.assertTrue(jetty92SingleRequester.toRetry(new EOFException()));
107
+ // When EOFException is wrapped in IOException it should be retried too
108
+ Assert.assertTrue(jetty92SingleRequester.toRetry(new IOException(new EOFException())));
109
+ // Retry TimeoutException when it is wrapped in IOException
110
+ Assert.assertTrue(jetty92SingleRequester.toRetry(new IOException(new TimeoutException())));
111
+ }
112
+
113
+ @Test
114
+ public void testGetAccessTokenRequestShouldHavePartnerId()
115
+ {
116
+ final String partnerId = "sample_partner_id";
117
+ mockJetty92 = Mockito.mock(Jetty92RetryHelper.class);
118
+ ArgumentCaptor<Jetty92SingleRequester> jetty92SingleRequesterArgumentCaptor = ArgumentCaptor.forClass(Jetty92SingleRequester.class);
119
+ Mockito.when(mockJetty92.requestWithRetry(Mockito.any(StringJetty92ResponseEntityReader.class), jetty92SingleRequesterArgumentCaptor.capture())).thenReturn("{\"access_token\": \"access_token\"}");
120
+
121
+ MarketoBaseRestClient restClient = Mockito.spy(new MarketoBaseRestClient("identityEndPoint",
122
+ "clientId",
123
+ "clientSecret",
124
+ Optional.of(partnerId),
125
+ MARKETO_LIMIT_INTERVAL_MILIS,
126
+ 60000,
127
+ mockJetty92));
128
+
129
+ // call method for evaluation
130
+ restClient.getAccessToken();
131
+
132
+ Jetty92SingleRequester singleRequester = jetty92SingleRequesterArgumentCaptor.getValue();
133
+
134
+ HttpClient client = Mockito.mock(HttpClient.class);
135
+ Request request = Mockito.mock(Request.class);
136
+
137
+ Request mockRequest = Mockito.mock(Request.class);
138
+ Mockito.when(client.newRequest(Mockito.eq(IDENTITY_END_POINT + MarketoRESTEndpoint.ACCESS_TOKEN.getEndpoint()))).thenReturn(mockRequest);
139
+ Mockito.when(mockRequest.method(Mockito.eq(HttpMethod.GET))).thenReturn(request);
140
+ singleRequester.requestOnce(client, Mockito.mock(Response.Listener.class));
141
+
142
+ Mockito.verify(request, Mockito.times(1)).param(Mockito.eq("client_id"), Mockito.eq("clientId"));
143
+ Mockito.verify(request, Mockito.times(1)).param(Mockito.eq("client_secret"), Mockito.eq("clientSecret"));
144
+ Mockito.verify(request, Mockito.times(1)).param(Mockito.eq("grant_type"), Mockito.eq("client_credentials"));
145
+ Mockito.verify(request, Mockito.times(1)).param(Mockito.eq("partner_id"), Mockito.eq(partnerId));
146
+ }
147
+
148
+ @Test
149
+ public void testGetAccessTokenWithError()
150
+ {
151
+ ArgumentCaptor<Jetty92SingleRequester> jetty92SingleRequesterArgumentCaptor = ArgumentCaptor.forClass(Jetty92SingleRequester.class);
152
+ Mockito.when(mockJetty92.requestWithRetry(Mockito.any(StringJetty92ResponseEntityReader.class), jetty92SingleRequesterArgumentCaptor.capture())).thenReturn("{\n" +
153
+ " \"error\": \"invalid_client\",\n" +
154
+ " \"error_description\": \"Bad client credentials\"\n" +
155
+ "}");
156
+ try {
157
+ marketoBaseRestClient.getAccessToken();
158
+ }
159
+ catch (DataException ex) {
160
+ Assert.assertEquals("Bad client credentials", ex.getMessage());
161
+ return;
162
+ }
163
+ Assert.fail();
164
+ }
165
+
166
+ @Test
167
+ public void testGetAccessTokenThrowHttpResponseException() throws Exception
168
+ {
169
+ HttpClient client = Mockito.mock(HttpClient.class);
170
+
171
+ Jetty92ClientCreator clientCreator = Mockito.mock(Jetty92ClientCreator.class);
172
+ Mockito.doReturn(client).when(clientCreator).createAndStart();
173
+
174
+ Request request = Mockito.mock(Request.class);
175
+ Mockito.doReturn(request).when(client).newRequest(Mockito.anyString());
176
+ Mockito.doReturn(request).when(request).method(HttpMethod.GET);
177
+
178
+ HttpResponseException exception = new HttpResponseException("{\"error\":\"invalid_client\",\"error_description\":\"Bad client credentials\"}", Mockito.mock(Response.class));
179
+ Mockito.doThrow(exception).when(request).send(Mockito.any(Response.Listener.class));
180
+
181
+ Jetty92RetryHelper retryHelper = new Jetty92RetryHelper(1, 1, 1, clientCreator);
182
+ final MarketoBaseRestClient restClient = new MarketoBaseRestClient("identityEndPoint", "clientId", "clientSecret", Optional.<String>absent(), MARKETO_LIMIT_INTERVAL_MILIS, 1000, retryHelper);
183
+
184
+ // calling method should wrap the HttpResponseException by ConfigException
185
+ Assert.assertThrows(ConfigException.class, new ThrowingRunnable()
186
+ {
187
+ @Override
188
+ public void run() throws Throwable
189
+ {
190
+ restClient.getAccessToken();
191
+ }
192
+ });
193
+ }
194
+
195
+ @Test
196
+ public void tetDoGetThrowHttpResponseException() throws Exception
197
+ {
198
+ final MarketoBaseRestClient client = doRequestWithWrapper(HttpMethod.GET);
199
+ // calling method should wrap the HttpResponseException by DataException
200
+ Assert.assertThrows(DataException.class, new ThrowingRunnable()
201
+ {
202
+ @Override
203
+ public void run() throws Throwable
204
+ {
205
+ client.doGet("test_target", null, null, new MarketoResponseJetty92EntityReader<String>(1000));
206
+ }
207
+ });
208
+ }
209
+
210
+ @Test
211
+ public void tetDoPostThrowHttpResponseException() throws Exception
212
+ {
213
+ final MarketoBaseRestClient client = doRequestWithWrapper(HttpMethod.POST);
214
+ // calling method should wrap the HttpResponseException by DataException
215
+ Assert.assertThrows(DataException.class, new ThrowingRunnable()
216
+ {
217
+ @Override
218
+ public void run() throws Throwable
219
+ {
220
+ client.doPost("test_target", null, null, "{\"any\": \"any\"}", new MarketoResponseJetty92EntityReader<String>(1000));
221
+ }
222
+ });
223
+ }
224
+
225
+ private MarketoBaseRestClient doRequestWithWrapper(HttpMethod method) throws Exception
226
+ {
227
+ HttpClient client = Mockito.mock(HttpClient.class);
228
+
229
+ Jetty92ClientCreator clientCreator = Mockito.mock(Jetty92ClientCreator.class);
230
+ Mockito.doReturn(client).when(clientCreator).createAndStart();
231
+
232
+ Request request = Mockito.mock(Request.class);
233
+ Mockito.doReturn(request).when(client).newRequest(Mockito.anyString());
234
+ Mockito.doReturn(request).when(request).method(method);
235
+
236
+ HttpResponseException exception = new HttpResponseException("{\"error\":\"1035\",\"error_description\":\"Unsupported filter type for target subscription: updatedAt\"}", Mockito.mock(Response.class));
237
+ Mockito.doThrow(exception).when(request).send(Mockito.any(Response.Listener.class));
238
+
239
+ Jetty92RetryHelper retryHelper = new Jetty92RetryHelper(1, 1, 1, clientCreator);
240
+ final MarketoBaseRestClient restClient = Mockito.spy(new MarketoBaseRestClient("identityEndPoint", "clientId", "clientSecret", Optional.<String>absent(), MARKETO_LIMIT_INTERVAL_MILIS, 1000, retryHelper));
241
+ Mockito.doReturn("test_access_token").when(restClient).getAccessToken();
242
+
243
+ return restClient;
244
+ }
245
+
246
+ @Test
247
+ public void testDoPost() throws Exception
248
+ {
249
+ MarketoBaseRestClient spy = Mockito.spy(marketoBaseRestClient);
250
+ spy.doPost("target", Maps.<String, String>newHashMap(), new ImmutableListMultimap.Builder<String, String>().build(), "test_content", new StringJetty92ResponseEntityReader(10));
251
+ Mockito.verify(spy, Mockito.times(1)).doRequest(Mockito.anyString(), Mockito.eq(HttpMethod.POST), Mockito.any(Map.class), Mockito.any(Multimap.class), Mockito.any(StringContentProvider.class), Mockito.any(StringJetty92ResponseEntityReader.class));
252
+ }
253
+
254
+ @Test
255
+ public void testDoGet() throws Exception
256
+ {
257
+ MarketoBaseRestClient spy = Mockito.spy(marketoBaseRestClient);
258
+ spy.doGet("target", Maps.<String, String>newHashMap(), new ImmutableListMultimap.Builder<String, String>().build(), new StringJetty92ResponseEntityReader(10));
259
+ Mockito.verify(spy, Mockito.times(1)).doRequest(Mockito.anyString(), Mockito.eq(HttpMethod.GET), Mockito.any(Map.class), Mockito.any(Multimap.class), Mockito.isNull(ContentProvider.class), Mockito.any(StringJetty92ResponseEntityReader.class));
260
+ }
261
+
262
+ @Test
263
+ public void testDoRequestRequester() throws Exception
264
+ {
265
+ MarketoBaseRestClient spy = Mockito.spy(marketoBaseRestClient);
266
+ StringContentProvider contentProvider = new StringContentProvider("Content", StandardCharsets.UTF_8);
267
+ ArgumentCaptor<Jetty92SingleRequester> jetty92SingleRequesterArgumentCaptor = ArgumentCaptor.forClass(Jetty92SingleRequester.class);
268
+
269
+ MarketoResponse<Object> expectedMarketoResponse = new MarketoResponse<>();
270
+
271
+ Mockito.when(mockJetty92.requestWithRetry(Mockito.any(MarketoResponseJetty92EntityReader.class), jetty92SingleRequesterArgumentCaptor.capture())).thenReturn(expectedMarketoResponse);
272
+ Mockito.when(mockJetty92.requestWithRetry(Mockito.any(StringJetty92ResponseEntityReader.class), Mockito.any(Jetty92SingleRequester.class))).thenReturn("{\"access_token\": \"access_token\"}");
273
+
274
+ String target = "target";
275
+ HashMap<String, String> headers = Maps.<String, String>newHashMap();
276
+ headers.put("testHeader1", "testHeaderValue1");
277
+
278
+ ImmutableListMultimap<String, String> build = new ImmutableListMultimap.Builder<String, String>().put("param", "param1").build();
279
+
280
+ MarketoResponse<Object> marketoResponse = spy.doRequest(target, HttpMethod.POST, headers, build, contentProvider, new MarketoResponseJetty92EntityReader<Object>(10));
281
+
282
+ HttpClient client = Mockito.mock(HttpClient.class);
283
+ Response.Listener listener = Mockito.mock(Response.Listener.class);
284
+ Request mockRequest = Mockito.mock(Request.class);
285
+ Mockito.when(client.newRequest(Mockito.eq(target))).thenReturn(mockRequest);
286
+
287
+ Mockito.when(mockRequest.method(Mockito.eq(HttpMethod.POST))).thenReturn(mockRequest);
288
+ Jetty92SingleRequester jetty92SingleRequester = jetty92SingleRequesterArgumentCaptor.getValue();
289
+ jetty92SingleRequester.requestOnce(client, listener);
290
+
291
+ Assert.assertEquals(expectedMarketoResponse, marketoResponse);
292
+
293
+ Mockito.verify(mockRequest, Mockito.times(1)).header(Mockito.eq("testHeader1"), Mockito.eq("testHeaderValue1"));
294
+ Mockito.verify(mockRequest, Mockito.times(1)).header(Mockito.eq("Authorization"), Mockito.eq("Bearer access_token"));
295
+ Mockito.verify(mockRequest, Mockito.times(1)).param(Mockito.eq("param"), Mockito.eq("param1"));
296
+ Mockito.verify(mockRequest, Mockito.times(1)).content(Mockito.eq(contentProvider));
297
+ }
298
+
299
+ @Test
300
+ public void testDoRequesterRetry() throws Exception
301
+ {
302
+ MarketoBaseRestClient spy = Mockito.spy(marketoBaseRestClient);
303
+ ArgumentCaptor<Jetty92SingleRequester> jetty92SingleRequesterArgumentCaptor = ArgumentCaptor.forClass(Jetty92SingleRequester.class);
304
+
305
+ Mockito.when(mockJetty92.requestWithRetry(Mockito.any(MarketoResponseJetty92EntityReader.class), jetty92SingleRequesterArgumentCaptor.capture())).thenReturn(new MarketoResponse<>());
306
+ Mockito.when(mockJetty92.requestWithRetry(Mockito.any(StringJetty92ResponseEntityReader.class), Mockito.any(Jetty92SingleRequester.class))).thenReturn("{\"access_token\": \"access_token\"}");
307
+
308
+ spy.doRequest("", HttpMethod.POST, null, null, null, new MarketoResponseJetty92EntityReader<Object>(10));
309
+
310
+ HttpClient client = Mockito.mock(HttpClient.class);
311
+ Response.Listener listener = Mockito.mock(Response.Listener.class);
312
+ Request mockRequest = Mockito.mock(Request.class);
313
+ Mockito.when(client.newRequest(Mockito.anyString())).thenReturn(mockRequest);
314
+
315
+ Mockito.when(mockRequest.method(Mockito.eq(HttpMethod.POST))).thenReturn(mockRequest);
316
+
317
+ Jetty92SingleRequester jetty92SingleRequester = jetty92SingleRequesterArgumentCaptor.getValue();
318
+ jetty92SingleRequester.requestOnce(client, listener);
319
+ Assert.assertTrue(jetty92SingleRequester.toRetry(createHttpResponseException(502)));
320
+
321
+ Assert.assertFalse(jetty92SingleRequester.toRetry(createHttpResponseException(400)));
322
+
323
+ Assert.assertFalse(jetty92SingleRequester.toRetry(createMarketoAPIException("ERR", "ERR")));
324
+ Assert.assertTrue(jetty92SingleRequester.toRetry(createMarketoAPIException("606", "")));
325
+ Assert.assertTrue(jetty92SingleRequester.toRetry(createMarketoAPIException("615", "")));
326
+ Assert.assertTrue(jetty92SingleRequester.toRetry(createMarketoAPIException("602", "")));
327
+ // Should retry 601 error too
328
+ Assert.assertTrue(jetty92SingleRequester.toRetry(createMarketoAPIException("601", "")));
329
+ Assert.assertTrue(jetty92SingleRequester.toRetry(createMarketoAPIException("604", "")));
330
+ Assert.assertTrue(jetty92SingleRequester.toRetry(createMarketoAPIException("608", "")));
331
+ Assert.assertTrue(jetty92SingleRequester.toRetry(createMarketoAPIException("611", "")));
332
+ Assert.assertTrue(jetty92SingleRequester.toRetry(createMarketoAPIException("615", "")));
333
+ Assert.assertTrue(jetty92SingleRequester.toRetry(createMarketoAPIException("713", "")));
334
+ Assert.assertTrue(jetty92SingleRequester.toRetry(createMarketoAPIException("1029", "")));
335
+ // Retry wrap SocketTimeoutException, TimeoutException and EOFException
336
+ Assert.assertTrue(jetty92SingleRequester.toRetry(new ExecutionException(new TimeoutException())));
337
+ Assert.assertTrue(jetty92SingleRequester.toRetry(new ExecutionException(new EOFException())));
338
+ Assert.assertTrue(jetty92SingleRequester.toRetry(new ExecutionException(new SocketTimeoutException())));
339
+ // When EOFException is wrapped in IOException it should be retried too
340
+ Assert.assertTrue(jetty92SingleRequester.toRetry(new IOException(new EOFException())));
341
+ // Retry TimeoutException when it is wrapped in IOException
342
+ Assert.assertTrue(jetty92SingleRequester.toRetry(new IOException(new TimeoutException())));
343
+
344
+ // Retry SocketTimeoutException, TimeoutException and EOFException
345
+ Assert.assertTrue(jetty92SingleRequester.toRetry(new SocketTimeoutException()));
346
+ Assert.assertTrue(jetty92SingleRequester.toRetry(new TimeoutException()));
347
+ Assert.assertTrue(jetty92SingleRequester.toRetry(new EOFException()));
348
+ // Call 3 times First call then 602 error and 601 error
349
+ Mockito.verify(mockJetty92, Mockito.times(3)).requestWithRetry(Mockito.any(StringJetty92ResponseEntityReader.class), Mockito.any(Jetty92SingleRequester.class));
350
+ }
351
+
352
+ private HttpResponseException createHttpResponseException(int statusCode)
353
+ {
354
+ HttpResponseException exception = Mockito.mock(HttpResponseException.class);
355
+ Response response = Mockito.mock(Response.class);
356
+ Mockito.when(exception.getResponse()).thenReturn(response);
357
+ Mockito.when(response.getStatus()).thenReturn(statusCode);
358
+ return exception;
359
+ }
360
+
361
+ private MarketoAPIException createMarketoAPIException(String code, String error)
362
+ {
363
+ MarketoError marketoError = new MarketoError();
364
+ marketoError.setCode(code);
365
+ marketoError.setMessage(error);
366
+ return new MarketoAPIException(Lists.newArrayList(marketoError));
367
+ }
368
+ }