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.
- checksums.yaml +7 -0
- data/.github/CODEOWNERS +1 -0
- data/.github/PULL_REQUEST_TEMPLATE.md +37 -0
- data/.github/workflows/build.yml +38 -0
- data/.gitignore +14 -0
- data/CHANGELOG.md +178 -0
- data/LICENSE.txt +21 -0
- data/README.md +231 -0
- data/build.gradle +105 -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 +6 -0
- data/gradlew +169 -0
- data/gradlew.bat +84 -0
- data/lib/embulk/input/marketo.rb +3 -0
- data/settings.gradle +1 -0
- data/src/main/java/org/embulk/input/marketo/CsvTokenizer.java +695 -0
- data/src/main/java/org/embulk/input/marketo/MarketoInputPlugin.java +15 -0
- data/src/main/java/org/embulk/input/marketo/MarketoInputPluginDelegate.java +100 -0
- data/src/main/java/org/embulk/input/marketo/MarketoService.java +47 -0
- data/src/main/java/org/embulk/input/marketo/MarketoServiceImpl.java +258 -0
- data/src/main/java/org/embulk/input/marketo/MarketoUtils.java +212 -0
- data/src/main/java/org/embulk/input/marketo/delegate/ActivityBulkExtractInputPlugin.java +169 -0
- data/src/main/java/org/embulk/input/marketo/delegate/CampaignInputPlugin.java +48 -0
- data/src/main/java/org/embulk/input/marketo/delegate/CustomObjectInputPlugin.java +124 -0
- data/src/main/java/org/embulk/input/marketo/delegate/CustomObjectResponseMapperBuilder.java +81 -0
- data/src/main/java/org/embulk/input/marketo/delegate/LeadBulkExtractInputPlugin.java +68 -0
- data/src/main/java/org/embulk/input/marketo/delegate/LeadServiceResponseMapperBuilder.java +85 -0
- data/src/main/java/org/embulk/input/marketo/delegate/LeadWithListInputPlugin.java +89 -0
- data/src/main/java/org/embulk/input/marketo/delegate/LeadWithProgramInputPlugin.java +85 -0
- data/src/main/java/org/embulk/input/marketo/delegate/MarketoBaseBulkExtractInputPlugin.java +448 -0
- data/src/main/java/org/embulk/input/marketo/delegate/MarketoBaseInputPluginDelegate.java +160 -0
- data/src/main/java/org/embulk/input/marketo/delegate/ProgramInputPlugin.java +234 -0
- data/src/main/java/org/embulk/input/marketo/exception/MarketoAPIException.java +30 -0
- data/src/main/java/org/embulk/input/marketo/model/BulkExtractRangeHeader.java +26 -0
- data/src/main/java/org/embulk/input/marketo/model/MarketoAccessTokenResponse.java +92 -0
- data/src/main/java/org/embulk/input/marketo/model/MarketoBulkExtractRequest.java +68 -0
- data/src/main/java/org/embulk/input/marketo/model/MarketoError.java +40 -0
- data/src/main/java/org/embulk/input/marketo/model/MarketoField.java +126 -0
- data/src/main/java/org/embulk/input/marketo/model/MarketoResponse.java +82 -0
- data/src/main/java/org/embulk/input/marketo/model/filter/DateRangeFilter.java +40 -0
- data/src/main/java/org/embulk/input/marketo/rest/MarketoBaseRestClient.java +344 -0
- data/src/main/java/org/embulk/input/marketo/rest/MarketoInputStreamResponseEntityReader.java +69 -0
- data/src/main/java/org/embulk/input/marketo/rest/MarketoRESTEndpoint.java +47 -0
- data/src/main/java/org/embulk/input/marketo/rest/MarketoResponseJetty92EntityReader.java +89 -0
- data/src/main/java/org/embulk/input/marketo/rest/MarketoRestClient.java +601 -0
- data/src/main/java/org/embulk/input/marketo/rest/RecordPagingIterable.java +180 -0
- data/src/test/java/org/embulk/input/marketo/MarketoServiceImplTest.java +147 -0
- data/src/test/java/org/embulk/input/marketo/MarketoUtilsTest.java +89 -0
- data/src/test/java/org/embulk/input/marketo/delegate/ActivityBulkExtractInputPluginTest.java +129 -0
- data/src/test/java/org/embulk/input/marketo/delegate/CampaignInputPluginTest.java +73 -0
- data/src/test/java/org/embulk/input/marketo/delegate/CustomObjectInputPluginTest.java +175 -0
- data/src/test/java/org/embulk/input/marketo/delegate/LeadBulkExtractInputPluginTest.java +102 -0
- data/src/test/java/org/embulk/input/marketo/delegate/LeadServiceResponseMapperBuilderTest.java +119 -0
- data/src/test/java/org/embulk/input/marketo/delegate/LeadWithListInputPluginTest.java +132 -0
- data/src/test/java/org/embulk/input/marketo/delegate/LeadWithProgramInputPluginTest.java +134 -0
- data/src/test/java/org/embulk/input/marketo/delegate/MarketoBaseBulkExtractInputPluginTest.java +171 -0
- data/src/test/java/org/embulk/input/marketo/delegate/MarketoBaseInputPluginDelegateTest.java +60 -0
- data/src/test/java/org/embulk/input/marketo/delegate/ProgramInputPluginTest.java +325 -0
- data/src/test/java/org/embulk/input/marketo/rest/MarketoBaseRestClientTest.java +368 -0
- data/src/test/java/org/embulk/input/marketo/rest/MarketoRestClientTest.java +649 -0
- data/src/test/resources/config/activity_bulk_extract_config.yaml +7 -0
- data/src/test/resources/config/custom_object_config.yaml +8 -0
- data/src/test/resources/config/lead_bulk_extract_config.yaml +8 -0
- data/src/test/resources/config/rest_config.yaml +3 -0
- data/src/test/resources/fixtures/activity_extract1.csv +35 -0
- data/src/test/resources/fixtures/activity_extract2.csv +22 -0
- data/src/test/resources/fixtures/activity_types.json +22 -0
- data/src/test/resources/fixtures/all_program_full.json +53 -0
- data/src/test/resources/fixtures/campaign_response.json +38 -0
- data/src/test/resources/fixtures/campaign_response_full.json +102 -0
- data/src/test/resources/fixtures/custom_object_describe.json +124 -0
- data/src/test/resources/fixtures/custom_object_describe_marketo_fields_full.json +22 -0
- data/src/test/resources/fixtures/custom_object_expected.json +66 -0
- data/src/test/resources/fixtures/custom_object_response.json +24 -0
- data/src/test/resources/fixtures/custom_object_response_full.json +23 -0
- data/src/test/resources/fixtures/lead_by_list.json +33 -0
- data/src/test/resources/fixtures/lead_by_program_response.json +47 -0
- data/src/test/resources/fixtures/lead_describe.json +221 -0
- data/src/test/resources/fixtures/lead_describe_expected.json +66 -0
- data/src/test/resources/fixtures/lead_describe_marketo_fields_full.json +518 -0
- data/src/test/resources/fixtures/lead_extract1.csv +11 -0
- data/src/test/resources/fixtures/lead_response_full.json +2402 -0
- data/src/test/resources/fixtures/lead_with_program_full.json +17 -0
- data/src/test/resources/fixtures/leads_extract2.csv +10 -0
- data/src/test/resources/fixtures/list_reponse_full.json +191 -0
- data/src/test/resources/fixtures/lists_response.json +31 -0
- data/src/test/resources/fixtures/program_response.json +71 -0
- 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
|
+
}
|