embulk-input-marketo 0.6.11 → 0.6.12

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1b375807158e07d67abc81e6319ef6ec9a087f9b
4
- data.tar.gz: 596f26cb4804ce01819b27499ead25b35c567100
3
+ metadata.gz: edba80ca03303c028b5ca8a2d70f7a7c801ec844
4
+ data.tar.gz: 4d935d696fb3b8adca5a0ac3c44684378f3b67a0
5
5
  SHA512:
6
- metadata.gz: d5743056090cbad09a2a75a6f460f2d843e721e254b2e898e7e809faad8f0309732244282c91f5dd6bbed9cafb95d0be519bba6305e8bee2726e49b0905867f8
7
- data.tar.gz: ab9fe2a479f4ccc842efb62e80d14dc4bac8941b64dc6c7d589c35a412a6f782fc902edd75a014e48b410889dca3e63c99a184dd533675c0707c44f520da0696
6
+ metadata.gz: 20c5faedf4785d9fc4bfc4f296a851a5b1c009b470ec91a86a870517b73a9333caf85c9a7e892a6d209cd9b52fb387d09b2b02937bb0cfa6a1676f41114a42b4
7
+ data.tar.gz: 8fa3f62aec0903509f2723cb08e1fb1af5b8dffd196fa6f3e5c519085cffe0fca726f453d730f021ba00a3ebed3c4ea8dadcaaa2846932b26c8743443e88f8e5
data/.gitignore CHANGED
@@ -12,4 +12,3 @@ build/
12
12
  .classpath
13
13
  .project
14
14
 
15
- /bin/
@@ -1,3 +1,6 @@
1
+ ## 0.6.12 - 2018-11-09
2
+ * [enhance] Implement Custom Object [#90](https://github.com/treasure-data/embulk-input-marketo/pull/90)
3
+
1
4
  ## 0.6.11 - 2018-09-10
2
5
  * [enhance] Implement Assets Programs [#89](https://github.com/treasure-data/embulk-input-marketo/pull/89)
3
6
 
@@ -16,7 +16,7 @@ repositories {
16
16
  configurations {
17
17
  provided
18
18
  }
19
- version = "0.6.11"
19
+ version = "0.6.12"
20
20
  sourceCompatibility = 1.7
21
21
  targetCompatibility = 1.7
22
22
 
@@ -11,6 +11,7 @@ import org.embulk.config.ConfigDefault;
11
11
  import org.embulk.config.ConfigException;
12
12
  import org.embulk.input.marketo.delegate.ActivityBulkExtractInputPlugin;
13
13
  import org.embulk.input.marketo.delegate.CampaignInputPlugin;
14
+ import org.embulk.input.marketo.delegate.CustomObjectInputPlugin;
14
15
  import org.embulk.input.marketo.delegate.LeadBulkExtractInputPlugin;
15
16
  import org.embulk.input.marketo.delegate.LeadWithListInputPlugin;
16
17
  import org.embulk.input.marketo.delegate.LeadWithProgramInputPlugin;
@@ -29,7 +30,8 @@ public class MarketoInputPluginDelegate
29
30
  ActivityBulkExtractInputPlugin.PluginTask,
30
31
  CampaignInputPlugin.PluginTask,
31
32
  ProgramInputPlugin.PluginTask,
32
- MarketoRestClient.PluginTask
33
+ MarketoRestClient.PluginTask,
34
+ CustomObjectInputPlugin.PluginTask
33
35
  {
34
36
  @Config("target")
35
37
  Target getTarget();
@@ -73,7 +75,8 @@ public class MarketoInputPluginDelegate
73
75
  CAMPAIGN(new CampaignInputPlugin()),
74
76
  ALL_LEAD_WITH_LIST_ID(new LeadWithListInputPlugin()),
75
77
  ALL_LEAD_WITH_PROGRAM_ID(new LeadWithProgramInputPlugin()),
76
- PROGRAM(new ProgramInputPlugin());
78
+ PROGRAM(new ProgramInputPlugin()),
79
+ CUSTOM_OBJECT(new CustomObjectInputPlugin());
77
80
 
78
81
  private RestClientInputPluginDelegate restClientInputPluginDelegate;
79
82
 
@@ -29,4 +29,8 @@ public interface MarketoService
29
29
  Iterable<ObjectNode> getProgramsByTag(String tagType, String tagValue);
30
30
 
31
31
  Iterable<ObjectNode> getProgramsByDateRange(Date earliestUpdatedAt, Date latestUpdatedAt, String filterType, List<String> filterValues);
32
+
33
+ Iterable<ObjectNode> getCustomObject(String customObjectAPIName, String customObjectFilterType, String customObjectFields, Integer fromValue, Integer toValue);
34
+
35
+ List<MarketoField> describeCustomObject(String customObjectAPIName);
32
36
  }
@@ -224,4 +224,16 @@ public class MarketoServiceImpl implements MarketoService
224
224
  {
225
225
  return marketoRestClient.getProgramsByDateRange(earliestUpdatedAt, latestUpdatedAt, filterType, filterValues);
226
226
  }
227
+
228
+ @Override
229
+ public List<MarketoField> describeCustomObject(String customObjectAPIName)
230
+ {
231
+ return marketoRestClient.describeCustomObject(customObjectAPIName);
232
+ }
233
+
234
+ @Override
235
+ public Iterable<ObjectNode> getCustomObject(String customObjectAPIName, String customObjectFilterType, String customObjectFields, Integer fromValue, Integer toValue)
236
+ {
237
+ return marketoRestClient.getCustomObject(customObjectAPIName, customObjectFilterType, customObjectFields, fromValue, toValue);
238
+ }
227
239
  }
@@ -0,0 +1,75 @@
1
+ package org.embulk.input.marketo.delegate;
2
+
3
+ import com.google.common.base.Optional;
4
+ import com.google.common.collect.FluentIterable;
5
+ import org.apache.commons.lang3.StringUtils;
6
+ import org.embulk.base.restclient.ServiceResponseMapper;
7
+ import org.embulk.base.restclient.record.ServiceRecord;
8
+ import org.embulk.base.restclient.record.ValueLocator;
9
+ import org.embulk.config.Config;
10
+ import org.embulk.config.ConfigDefault;
11
+ import org.embulk.config.ConfigException;
12
+ import org.embulk.input.marketo.MarketoService;
13
+ import org.embulk.input.marketo.MarketoServiceImpl;
14
+ import org.embulk.input.marketo.MarketoUtils;
15
+ import org.embulk.input.marketo.rest.MarketoRestClient;
16
+ import org.embulk.spi.Exec;
17
+ import org.slf4j.Logger;
18
+ import java.util.Iterator;
19
+
20
+ public class CustomObjectInputPlugin extends MarketoBaseInputPluginDelegate<CustomObjectInputPlugin.PluginTask>
21
+ {
22
+ private final Logger logger = Exec.getLogger(getClass());
23
+
24
+ public interface PluginTask extends MarketoBaseInputPluginDelegate.PluginTask, CustomObjectResponseMapperBuilder.PluginTask
25
+ {
26
+ @Config("custom_object_filter_type")
27
+ @ConfigDefault("\"\"")
28
+ String getCustomObjectFilterType();
29
+
30
+ @Config("custom_object_filter_from_value")
31
+ @ConfigDefault("1")
32
+ Integer getFromValue();
33
+
34
+ @Config("custom_object_filter_to_value")
35
+ @ConfigDefault("null")
36
+ Optional<Integer> getToValue();
37
+ }
38
+
39
+ public CustomObjectInputPlugin()
40
+ {
41
+ }
42
+
43
+ @Override
44
+ public void validateInputTask(PluginTask task)
45
+ {
46
+ super.validateInputTask(task);
47
+ if (StringUtils.isBlank(task.getCustomObjectFilterType())) {
48
+ throw new ConfigException("`custom_object_filter_type` cannot be empty");
49
+ }
50
+ if (StringUtils.isBlank(task.getCustomObjectAPIName())) {
51
+ throw new ConfigException("`custom_object_api_name` cannot be empty");
52
+ }
53
+ if (task.getToValue().isPresent() && task.getToValue().get() < task.getFromValue()) {
54
+ throw new ConfigException(String.format("`to_value` (%s) cannot be less than the `from_value` (%s)", task.getToValue().get(), task.getFromValue()));
55
+ }
56
+ }
57
+ @Override
58
+ protected Iterator<ServiceRecord> getServiceRecords(MarketoService marketoService, PluginTask task)
59
+ {
60
+ return FluentIterable
61
+ .from(marketoService.getCustomObject(task.getCustomObjectAPIName(), task.getCustomObjectFilterType(), task.getCustomObjectFields().orNull(), task.getFromValue(), task.getToValue().orNull()))
62
+ .transform(MarketoUtils.TRANSFORM_OBJECT_TO_JACKSON_SERVICE_RECORD_FUNCTION).iterator();
63
+ }
64
+
65
+ @Override
66
+ public ServiceResponseMapper<? extends ValueLocator> buildServiceResponseMapper(PluginTask task)
67
+ {
68
+ try (MarketoRestClient marketoRestClient = createMarketoRestClient(task)) {
69
+ MarketoService marketoService = new MarketoServiceImpl(marketoRestClient);
70
+ CustomObjectResponseMapperBuilder<PluginTask> customObjectResponseMapperBuilder = new CustomObjectResponseMapperBuilder<>(task, marketoService);
71
+ return customObjectResponseMapperBuilder.buildServiceResponseMapper(task);
72
+ }
73
+ }
74
+
75
+ }
@@ -0,0 +1,80 @@
1
+ package org.embulk.input.marketo.delegate;
2
+
3
+ import com.google.common.base.Optional;
4
+ import org.apache.commons.lang3.StringUtils;
5
+ import org.embulk.base.restclient.ServiceResponseMapper;
6
+ import org.embulk.base.restclient.ServiceResponseMapperBuildable;
7
+ import org.embulk.base.restclient.record.ValueLocator;
8
+ import org.embulk.config.Config;
9
+ import org.embulk.config.ConfigDefault;
10
+ import org.embulk.input.marketo.MarketoService;
11
+ import org.embulk.input.marketo.MarketoUtils;
12
+ import org.embulk.input.marketo.model.MarketoField;
13
+ import org.embulk.spi.Exec;
14
+ import org.slf4j.Logger;
15
+ import java.util.ArrayList;
16
+ import java.util.Arrays;
17
+ import java.util.List;
18
+
19
+ public class CustomObjectResponseMapperBuilder<T extends CustomObjectResponseMapperBuilder.PluginTask> implements ServiceResponseMapperBuildable<T>
20
+ {
21
+ private static final Logger LOGGER = Exec.getLogger(CustomObjectResponseMapperBuilder.class);
22
+ private MarketoService marketoService;
23
+
24
+ private T pluginTask;
25
+
26
+ public interface PluginTask extends MarketoBaseInputPluginDelegate.PluginTask
27
+ {
28
+ @Config("custom_object_api_name")
29
+ @ConfigDefault("\"\"")
30
+ String getCustomObjectAPIName();
31
+
32
+ @Config("custom_object_fields")
33
+ @ConfigDefault("null")
34
+ Optional<String> getCustomObjectFields();
35
+ }
36
+
37
+ public CustomObjectResponseMapperBuilder(T task, MarketoService marketoService)
38
+ {
39
+ this.pluginTask = task;
40
+ this.marketoService = marketoService;
41
+ }
42
+
43
+ protected List<MarketoField> getCustomObjectColumns()
44
+ {
45
+ List<MarketoField> columns = marketoService.describeCustomObject(pluginTask.getCustomObjectAPIName());
46
+ if (pluginTask.getCustomObjectFields().isPresent() && StringUtils.isNotBlank(pluginTask.getCustomObjectFields().get())) {
47
+ List<MarketoField> filteredColumns = new ArrayList<>();
48
+ List<String> includedFields = Arrays.asList(pluginTask.getCustomObjectFields().get().split(","));
49
+ for (String fieldName : includedFields) {
50
+ Optional<MarketoField> includedField = lookupFieldIgnoreCase(columns, fieldName);
51
+ if (includedField.isPresent()) {
52
+ filteredColumns.add(includedField.get());
53
+ }
54
+ else {
55
+ LOGGER.warn("Included field [{}] not found in Marketo Custom Object [{}] field", fieldName, pluginTask.getCustomObjectAPIName());
56
+ }
57
+ }
58
+ columns = filteredColumns;
59
+ LOGGER.info("Included Fields option is set, included columns: [{}]", columns);
60
+ }
61
+ return columns;
62
+ }
63
+
64
+ private static Optional<MarketoField> lookupFieldIgnoreCase(List<MarketoField> inputList, String lookupFieldName)
65
+ {
66
+ for (MarketoField marketoField : inputList) {
67
+ if (marketoField.getName().equalsIgnoreCase(lookupFieldName)) {
68
+ return Optional.of(marketoField);
69
+ }
70
+ }
71
+ return Optional.absent();
72
+ }
73
+
74
+ @Override
75
+ public ServiceResponseMapper<? extends ValueLocator> buildServiceResponseMapper(T task)
76
+ {
77
+ List<MarketoField> customObjectColumns = getCustomObjectColumns();
78
+ return MarketoUtils.buildDynamicResponseMapper(pluginTask.getSchemaColumnPrefix(), customObjectColumns);
79
+ }
80
+ }
@@ -24,7 +24,9 @@ public enum MarketoRESTEndpoint
24
24
  GET_PROGRAMS("/rest/asset/v1/programs.json"),
25
25
  GET_LEADS_BY_PROGRAM("/rest/v1/leads/programs/${program_id}.json"),
26
26
  GET_CAMPAIGN("/rest/v1/campaigns.json"),
27
- GET_PROGRAMS_BY_TAG("/rest/asset/v1/program/byTag.json");
27
+ GET_PROGRAMS_BY_TAG("/rest/asset/v1/program/byTag.json"),
28
+ GET_CUSTOM_OBJECT("/rest/v1/customobjects/${api_name}.json"),
29
+ GET_CUSTOM_OBJECT_DESCRIBE("/rest/v1/customobjects/${api_name}/describe.json");
28
30
 
29
31
  private String endpoint;
30
32
 
@@ -1,16 +1,19 @@
1
1
  package org.embulk.input.marketo.rest;
2
2
 
3
3
  import com.fasterxml.jackson.core.JsonProcessingException;
4
+ import com.fasterxml.jackson.databind.JsonNode;
4
5
  import com.fasterxml.jackson.databind.node.ObjectNode;
5
6
  import com.google.common.collect.ArrayListMultimap;
6
7
  import com.google.common.collect.ImmutableListMultimap;
7
8
  import com.google.common.collect.ImmutableMap;
8
9
  import com.google.common.collect.Multimap;
9
10
 
11
+ import org.apache.commons.lang3.StringUtils;
10
12
  import org.eclipse.jetty.client.util.FormContentProvider;
11
13
  import org.eclipse.jetty.util.Fields;
12
14
  import org.embulk.config.Config;
13
15
  import org.embulk.config.ConfigDefault;
16
+ import org.embulk.config.ConfigException;
14
17
  import org.embulk.config.Task;
15
18
  import org.embulk.input.marketo.MarketoUtils;
16
19
  import org.embulk.input.marketo.model.BulkExtractRangeHeader;
@@ -55,6 +58,14 @@ public class MarketoRestClient extends MarketoBaseRestClient
55
58
 
56
59
  private static final String RANGE_HEADER = "Range";
57
60
 
61
+ private static final String FILTER_TYPE = "filterType";
62
+
63
+ private static final String FILTER_VALUES = "filterValues";
64
+
65
+ private static final String FIELDS = "fields";
66
+
67
+ private static final int MAX_REQUEST_SIZE = 300;
68
+
58
69
  private static final int CONNECT_TIMEOUT_IN_MILLIS = 30000;
59
70
  private static final int IDLE_TIMEOUT_IN_MILLIS = 60000;
60
71
 
@@ -435,4 +446,92 @@ public class MarketoRestClient extends MarketoBaseRestClient
435
446
  }
436
447
  return getRecordWithOffsetPagination(endPoint + MarketoRESTEndpoint.GET_PROGRAMS.getEndpoint(), multimap, ObjectNode.class);
437
448
  }
449
+
450
+ public List<MarketoField> describeCustomObject(String apiName)
451
+ {
452
+ MarketoResponse<ObjectNode> jsonResponse = doGet(endPoint + MarketoRESTEndpoint.GET_CUSTOM_OBJECT_DESCRIBE.getEndpoint(new ImmutableMap.Builder().put("api_name", apiName).build()), null, null, new MarketoResponseJetty92EntityReader<ObjectNode>(this.readTimeoutMillis));
453
+ if (jsonResponse.getResult().size() == 0) {
454
+ throw new ConfigException(String.format("Custom Object %s is not exits.", apiName));
455
+ }
456
+ List<MarketoField> marketoFields = new ArrayList<>();
457
+ JsonNode fieldNodes = jsonResponse.getResult().get(0).path("fields");
458
+ for (JsonNode node : fieldNodes) {
459
+ String dataType = node.get("dataType").asText();
460
+ String name = node.get("name").asText();
461
+ marketoFields.add(new MarketoField(name, dataType));
462
+ }
463
+ if (marketoFields.size() == 0) {
464
+ throw new ConfigException(String.format("Custom Object %s don't have any field data.", apiName));
465
+ }
466
+ return marketoFields;
467
+ }
468
+ private <T> RecordPagingIterable<T> getCustomObjectRecordWithPagination(final String endPoint, final String customObjectFilterType, final String customObjectFields, final Integer fromValue, final Integer toValue, final Class<T> recordClass)
469
+ {
470
+ return new RecordPagingIterable<>(new RecordPagingIterable.PagingFunction<RecordPagingIterable.OffsetWithTokenPage<T>>()
471
+ {
472
+ @Override
473
+ public RecordPagingIterable.OffsetWithTokenPage<T> getNextPage(RecordPagingIterable.OffsetWithTokenPage<T> currentPage)
474
+ {
475
+ return getOffsetPage(currentPage.getNextOffSet(), currentPage.getNextPageToken());
476
+ }
477
+
478
+ @Override
479
+ public RecordPagingIterable.OffsetWithTokenPage<T> getFirstPage()
480
+ {
481
+ return getOffsetPage(fromValue, "");
482
+ }
483
+
484
+ private RecordPagingIterable.OffsetWithTokenPage<T> getOffsetPage(int offset, String nextPageToken)
485
+ {
486
+ boolean isMoreResult = true;
487
+ boolean isEndOffset = false;
488
+ int nextOffset = offset + MAX_REQUEST_SIZE;
489
+
490
+ if (toValue != null) {
491
+ if (toValue <= nextOffset) {
492
+ nextOffset = toValue + 1;
493
+ isEndOffset = true;
494
+ }
495
+ }
496
+ StringBuilder filterValues = new StringBuilder();
497
+ for (int i = offset; i < (nextOffset - 1); i++) {
498
+ filterValues.append(String.valueOf(i)).append(",");
499
+ }
500
+ filterValues.append(String.valueOf(nextOffset - 1));
501
+
502
+ ImmutableListMultimap.Builder<String, String> params = new ImmutableListMultimap.Builder<>();
503
+ params.put(FILTER_TYPE, customObjectFilterType);
504
+ params.put(FILTER_VALUES, filterValues.toString());
505
+ if (StringUtils.isNotBlank(nextPageToken)) {
506
+ params.put(NEXT_PAGE_TOKEN, nextPageToken);
507
+ }
508
+ if (customObjectFields != null) {
509
+ params.put(FIELDS, customObjectFields);
510
+ }
511
+ MarketoResponse<T> marketoResponse = doGet(endPoint, null, params.build(), new MarketoResponseJetty92EntityReader<>(readTimeoutMillis, recordClass));
512
+ String nextToken = "";
513
+ if (StringUtils.isNotBlank(marketoResponse.getNextPageToken())) {
514
+ nextToken = marketoResponse.getNextPageToken();
515
+ //skip offset when nextPageToken is exits.
516
+ nextOffset = offset;
517
+ }
518
+
519
+ if (toValue == null) {
520
+ if (marketoResponse.getResult().isEmpty()) {
521
+ isMoreResult = false;
522
+ }
523
+ }
524
+ else {
525
+ if (isEndOffset && StringUtils.isBlank(nextToken)) {
526
+ isMoreResult = false;
527
+ }
528
+ }
529
+ return new RecordPagingIterable.OffsetWithTokenPage<>(marketoResponse.getResult(), nextOffset, nextToken, isMoreResult);
530
+ }
531
+ });
532
+ }
533
+ public Iterable<ObjectNode> getCustomObject(String customObjectAPIName, String customObjectFilterType, String customObjectFields, Integer fromValue, Integer toValue)
534
+ {
535
+ return getCustomObjectRecordWithPagination(endPoint + MarketoRESTEndpoint.GET_CUSTOM_OBJECT.getEndpoint(new ImmutableMap.Builder().put("api_name", customObjectAPIName).build()), customObjectFilterType, customObjectFields, fromValue, toValue, ObjectNode.class);
536
+ }
438
537
  }
@@ -151,4 +151,30 @@ public class RecordPagingIterable<T> implements Iterable<T>
151
151
  return nextOffSet;
152
152
  }
153
153
  }
154
+ public static class OffsetWithTokenPage<T> extends Page<T>
155
+ {
156
+ private int nextOffSet;
157
+ private String nextPageToken;
158
+ public OffsetWithTokenPage(Iterable<T> records, int nextOffSet, String nextPageToken, boolean moreResult)
159
+ {
160
+ super(records, moreResult);
161
+ this.nextOffSet = nextOffSet;
162
+ this.nextPageToken = nextPageToken;
163
+ }
164
+
165
+ public int getNextOffSet()
166
+ {
167
+ return nextOffSet;
168
+ }
169
+
170
+ public String getNextPageToken()
171
+ {
172
+ return nextPageToken;
173
+ }
174
+
175
+ public void setNextPageToken(String nextPageToken)
176
+ {
177
+ this.nextPageToken = nextPageToken;
178
+ }
179
+ }
154
180
  }
@@ -0,0 +1,101 @@
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 org.embulk.EmbulkTestRuntime;
8
+ import org.embulk.base.restclient.ServiceResponseMapper;
9
+ import org.embulk.base.restclient.record.RecordImporter;
10
+ import org.embulk.base.restclient.record.ValueLocator;
11
+ import org.embulk.config.ConfigException;
12
+ import org.embulk.config.ConfigLoader;
13
+ import org.embulk.config.ConfigSource;
14
+ import org.embulk.input.marketo.model.MarketoField;
15
+ import org.embulk.input.marketo.rest.MarketoRestClient;
16
+ import org.embulk.input.marketo.rest.RecordPagingIterable;
17
+ import org.embulk.spi.PageBuilder;
18
+ import org.embulk.spi.Schema;
19
+ import org.junit.Before;
20
+ import org.junit.Rule;
21
+ import org.junit.Test;
22
+ import org.mockito.ArgumentCaptor;
23
+ import org.mockito.Mockito;
24
+ import java.io.IOException;
25
+ import java.util.List;
26
+
27
+ import static org.junit.Assert.assertArrayEquals;
28
+
29
+ public class CustomObjectInputPluginTest
30
+ {
31
+ private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
32
+
33
+ @Rule
34
+ public EmbulkTestRuntime embulkTestRuntime = new EmbulkTestRuntime();
35
+
36
+ private CustomObjectInputPlugin customObjectInputPlugin;
37
+
38
+ private ConfigSource configSource;
39
+
40
+ private MarketoRestClient mockMarketoRestClient;
41
+
42
+ @Before
43
+ public void setUp() throws Exception
44
+ {
45
+ customObjectInputPlugin = Mockito.spy(new CustomObjectInputPlugin());
46
+ ConfigLoader configLoader = embulkTestRuntime.getInjector().getInstance(ConfigLoader.class);
47
+ configSource = configLoader.fromYaml(this.getClass().getResourceAsStream("/config/custom_object_config.yaml"));
48
+ mockMarketoRestClient = Mockito.mock(MarketoRestClient.class);
49
+ Mockito.doReturn(mockMarketoRestClient).when(customObjectInputPlugin).createMarketoRestClient(Mockito.any(CustomObjectInputPlugin.PluginTask.class));
50
+ }
51
+
52
+ @Test(expected = ConfigException.class)
53
+ public void validateCustomObjectFilterTypeError()
54
+ {
55
+ CustomObjectInputPlugin.PluginTask pluginTask = Mockito.mock(CustomObjectInputPlugin.PluginTask.class);
56
+ Mockito.when(pluginTask.getCustomObjectFilterType()).thenReturn("");
57
+ customObjectInputPlugin.validateInputTask(pluginTask);
58
+ }
59
+
60
+ @Test(expected = ConfigException.class)
61
+ public void validateCustomObjectAPINameError()
62
+ {
63
+ CustomObjectInputPlugin.PluginTask pluginTask = Mockito.mock(CustomObjectInputPlugin.PluginTask.class);
64
+ Mockito.when(pluginTask.getCustomObjectAPIName()).thenReturn("");
65
+ customObjectInputPlugin.validateInputTask(pluginTask);
66
+ }
67
+
68
+ @Test(expected = ConfigException.class)
69
+ public void validateFromValueGreaterThanToValueError()
70
+ {
71
+ CustomObjectInputPlugin.PluginTask pluginTask = Mockito.mock(CustomObjectInputPlugin.PluginTask.class);
72
+ Mockito.when(pluginTask.getFromValue()).thenReturn(100);
73
+ Mockito.when(pluginTask.getToValue()).thenReturn(Optional.of(90));
74
+ customObjectInputPlugin.validateInputTask(pluginTask);
75
+ }
76
+
77
+ @Test
78
+ public void testRun() throws IOException
79
+ {
80
+ RecordPagingIterable<ObjectNode> mockRecordPagingIterable = Mockito.mock(RecordPagingIterable.class);
81
+ JavaType javaType = OBJECT_MAPPER.getTypeFactory().constructParametrizedType(List.class, List.class, ObjectNode.class);
82
+ List<ObjectNode> objectNodeList = OBJECT_MAPPER.readValue(this.getClass().getResourceAsStream("/fixtures/custom_object_response_full.json"), javaType);
83
+ JavaType marketoFieldsType = OBJECT_MAPPER.getTypeFactory().constructParametrizedType(List.class, List.class, MarketoField.class);
84
+ List<MarketoField> marketoFields = OBJECT_MAPPER.readValue(this.getClass().getResourceAsStream("/fixtures/custom_object_describe_marketo_fields_full.json"), marketoFieldsType);
85
+ Mockito.when(mockRecordPagingIterable.iterator()).thenReturn(objectNodeList.iterator());
86
+ Mockito.when(mockMarketoRestClient.getCustomObject(Mockito.anyString(), Mockito.anyString(), Mockito.anyString(), Mockito.anyInt(), Mockito.anyInt())).thenReturn(mockRecordPagingIterable);
87
+ Mockito.when(mockMarketoRestClient.describeCustomObject(Mockito.anyString())).thenReturn(marketoFields);
88
+ CustomObjectInputPlugin.PluginTask task = configSource.loadConfig(CustomObjectInputPlugin.PluginTask.class);
89
+ ServiceResponseMapper<? extends ValueLocator> mapper = customObjectInputPlugin.buildServiceResponseMapper(task);
90
+ RecordImporter recordImporter = mapper.createRecordImporter();
91
+ PageBuilder mockPageBuilder = Mockito.mock(PageBuilder.class);
92
+ customObjectInputPlugin.ingestServiceData(task, recordImporter, 1, mockPageBuilder);
93
+ Mockito.verify(mockMarketoRestClient, Mockito.times(1)).getCustomObject(Mockito.anyString(), Mockito.anyString(), Mockito.anyString(), Mockito.anyInt(), Mockito.anyInt());
94
+ Mockito.verify(mockMarketoRestClient, Mockito.times(1)).describeCustomObject(Mockito.anyString());
95
+ Schema embulkSchema = mapper.getEmbulkSchema();
96
+ ArgumentCaptor<Long> longArgumentCaptor = ArgumentCaptor.forClass(Long.class);
97
+ Mockito.verify(mockPageBuilder, Mockito.times(3)).setLong(Mockito.eq(embulkSchema.lookupColumn("mk_id")), longArgumentCaptor.capture());
98
+ List<Long> allValues = longArgumentCaptor.getAllValues();
99
+ assertArrayEquals(new Long[]{1L, 2L, 3L}, allValues.toArray());
100
+ }
101
+ }
@@ -544,4 +544,39 @@ public class MarketoRestClientTest
544
544
  Assert.assertEquals("filter1", params2.get("filterType").get(0));
545
545
  Assert.assertEquals(String.join(",", filterValues), params2.get("filterValues").get(0));
546
546
  }
547
+
548
+ @Test
549
+ public void describeCustomObject() throws Exception
550
+ {
551
+ String customObjectSchema = new String(ByteStreams.toByteArray(this.getClass().getResourceAsStream("/fixtures/custom_object_describe.json")));
552
+ MarketoResponse<ObjectNode> marketoResponse = OBJECT_MAPPER.readValue(customObjectSchema, RESPONSE_TYPE);
553
+ String apiName = "custom_object";
554
+ Map<String, String> pathParams = new HashMap<>();
555
+ pathParams.put("api_name", apiName);
556
+ Mockito.doReturn(marketoResponse).when(marketoRestClient).doGet(Mockito.eq(END_POINT + MarketoRESTEndpoint.GET_CUSTOM_OBJECT_DESCRIBE.getEndpoint(pathParams)), Mockito.isNull(Map.class), Mockito.isNull(ImmutableListMultimap.class), Mockito.any(MarketoResponseJetty92EntityReader.class));
557
+ List<MarketoField> marketoFields = marketoRestClient.describeCustomObject(apiName);
558
+ Assert.assertEquals(16, marketoFields.size());
559
+ JavaType marketoFieldType = OBJECT_MAPPER.getTypeFactory().constructParametrizedType(List.class, List.class, MarketoField.class);
560
+ List<MarketoField> expectedFields = OBJECT_MAPPER.readValue(new String(ByteStreams.toByteArray(this.getClass().getResourceAsStream("/fixtures/custom_object_expected.json"))), marketoFieldType);
561
+ Assert.assertArrayEquals(expectedFields.toArray(), marketoFields.toArray());
562
+ }
563
+
564
+ @Test
565
+ public void getCustomObject() throws Exception
566
+ {
567
+ String apiName = "custom_object";
568
+ Map<String, String> pathParams = new HashMap<>();
569
+ pathParams.put("api_name", apiName);
570
+
571
+ ArrayNode listPages = (ArrayNode) OBJECT_MAPPER.readTree(new String(ByteStreams.toByteArray(this.getClass().getResourceAsStream("/fixtures/custom_object_response.json")))).get("responses");
572
+ MarketoResponse<ObjectNode> page1 = OBJECT_MAPPER.readValue(listPages.get(0).toString(), RESPONSE_TYPE);
573
+ Mockito.doReturn(page1).when(marketoRestClient).doGet(Mockito.eq(END_POINT + MarketoRESTEndpoint.GET_CUSTOM_OBJECT.getEndpoint(pathParams)), Mockito.isNull(Map.class), Mockito.any(ImmutableListMultimap.class), Mockito.any(MarketoResponseJetty92EntityReader.class));
574
+ RecordPagingIterable<ObjectNode> pages = (RecordPagingIterable<ObjectNode>) marketoRestClient.getCustomObject(apiName, "id", null, 1, 2);
575
+ Iterator<ObjectNode> iterator = pages.iterator();
576
+ ObjectNode customObject1 = iterator.next();
577
+ ObjectNode customObject2 = iterator.next();
578
+ Assert.assertFalse(iterator.hasNext());
579
+ Assert.assertEquals("1", customObject1.get("id").asText());
580
+ Assert.assertEquals("2", customObject2.get("id").asText());
581
+ }
547
582
  }
@@ -0,0 +1,8 @@
1
+ account_id: 'account_id'
2
+ client_id: client_id
3
+ client_secret: client_secret
4
+ custom_object_api_name: custom_object_api_name
5
+ custom_object_filter_type: custom_object_filter_type
6
+ custom_object_fields: ""
7
+ custom_object_filter_from_value: 1
8
+ custom_object_filter_to_value: 2
@@ -0,0 +1,124 @@
1
+ {
2
+ "requestId":"185d6#14b51985ff0",
3
+ "success":true,
4
+ "result":[
5
+ {
6
+ "name":"custom_object",
7
+ "displayName":"Custom Object",
8
+ "description":"Custom Object",
9
+ "createdAt":"2015-02-03T22:36:23Z",
10
+ "updatedAt":"2015-02-03T22:36:24Z",
11
+ "idField":"marketoGUID",
12
+ "dedupeFields":["mKtestcurrency"],
13
+ "searchableFields":[
14
+ ["mKtestcurrency"],
15
+ ["marketoGUID"]
16
+ ],
17
+ "fields":[
18
+ {
19
+ "updateable":true,
20
+ "displayName": "Marketo_test_boolean",
21
+ "dataType": "boolean",
22
+ "name": "mKtestboolean"
23
+ },
24
+ {
25
+ "updateable":true,
26
+ "displayName": "Marketo_test_currency",
27
+ "dataType": "currency",
28
+ "length": 255,
29
+ "name": "mKtestcurrency"
30
+ },
31
+ {
32
+ "updateable":true,
33
+ "displayName": "Marketo_test_date",
34
+ "dataType": "date",
35
+ "name": "mKtestdate"
36
+ },
37
+ {
38
+ "updateable":true,
39
+ "displayName": "Marketo_test_Datetime",
40
+ "dataType": "datetime",
41
+ "name": "mKtestDatetime"
42
+ },
43
+ {
44
+ "updateable":true,
45
+ "displayName": "Marketo_test_email",
46
+ "dataType": "email",
47
+ "length": 255,
48
+ "name": "mKtestemail"
49
+ },
50
+ {
51
+ "updateable":true,
52
+ "displayName": "Marketo_test_float",
53
+ "dataType": "float",
54
+ "name": "mKtestfloat"
55
+ },
56
+ {
57
+ "updateable":true,
58
+ "displayName": "Marketo_test_integer",
59
+ "dataType": "integer",
60
+ "name": "mKtestinteger"
61
+ },
62
+ {
63
+ "updateable":true,
64
+ "displayName": "Marketo_test_percent",
65
+ "dataType": "percent",
66
+ "name": "mKtestpercent"
67
+ },
68
+ {
69
+ "updateable":true,
70
+ "displayName": "Marketo_test_phone",
71
+ "dataType": "phone",
72
+ "length": 255,
73
+ "name": "mKtestphone"
74
+ },
75
+ {
76
+ "updateable":true,
77
+ "displayName": "Marketo_test_score",
78
+ "dataType": "score",
79
+ "name": "mKtestscore"
80
+ },
81
+ {
82
+ "updateable":true,
83
+ "displayName": "Marketo_test_string",
84
+ "dataType": "string",
85
+ "length": 255,
86
+ "name": "mKteststring"
87
+ },
88
+ {
89
+ "updateable":true,
90
+ "displayName": "Marketo_test_textarea",
91
+ "dataType": "textarea",
92
+ "name": "mKtesttextarea"
93
+ },
94
+ {
95
+ "updateable":true,
96
+ "displayName": "Marketo_test_url",
97
+ "dataType": "url",
98
+ "length": 255,
99
+ "name": "mKtesturl"
100
+ },
101
+ {
102
+ "updateable":true,
103
+ "displayName": "Marketo_test_reference",
104
+ "dataType": "reference",
105
+ "length": 255,
106
+ "name": "mKTestReference"
107
+ },
108
+ {
109
+ "updateable":true,
110
+ "displayName": "Marketo_test_formula",
111
+ "dataType": "formula",
112
+ "length": 255,
113
+ "name": "mKTestFormula"
114
+ },
115
+ {
116
+ "updateable":true,
117
+ "displayName": "Marketo_test_text",
118
+ "dataType": "text",
119
+ "name": "mKtesttext"
120
+ }
121
+ ]
122
+ }
123
+ ]
124
+ }
@@ -0,0 +1,22 @@
1
+ [
2
+ {
3
+ "name": "seq",
4
+ "marketoDataType": "INTEGER"
5
+ },
6
+ {
7
+ "name": "marketoGUID",
8
+ "marketoDataType": "STRING"
9
+ },
10
+ {
11
+ "name": "id",
12
+ "marketoDataType": "INTEGER"
13
+ },
14
+ {
15
+ "name": "createdAt",
16
+ "marketoDataType": "DATETIME"
17
+ },
18
+ {
19
+ "name": "updatedAt",
20
+ "marketoDataType": "DATETIME"
21
+ }
22
+ ]
@@ -0,0 +1,66 @@
1
+ [
2
+ {
3
+ "name": "mKtestboolean",
4
+ "marketoDataType": "BOOLEAN"
5
+ },
6
+ {
7
+ "name": "mKtestcurrency",
8
+ "marketoDataType": "CURRENCY"
9
+ },
10
+ {
11
+ "name": "mKtestdate",
12
+ "marketoDataType": "DATE"
13
+ },
14
+ {
15
+ "name": "mKtestDatetime",
16
+ "marketoDataType": "DATETIME"
17
+ },
18
+ {
19
+ "name": "mKtestemail",
20
+ "marketoDataType": "EMAIL"
21
+ },
22
+ {
23
+ "name": "mKtestfloat",
24
+ "marketoDataType": "FLOAT"
25
+ },
26
+ {
27
+ "name": "mKtestinteger",
28
+ "marketoDataType": "INTEGER"
29
+ },
30
+ {
31
+ "name": "mKtestpercent",
32
+ "marketoDataType": "PERCENT"
33
+ },
34
+ {
35
+ "name": "mKtestphone",
36
+ "marketoDataType": "PHONE"
37
+ },
38
+ {
39
+ "name": "mKtestscore",
40
+ "marketoDataType": "SCORE"
41
+ },
42
+ {
43
+ "name": "mKteststring",
44
+ "marketoDataType": "STRING"
45
+ },
46
+ {
47
+ "name": "mKtesttextarea",
48
+ "marketoDataType": "TEXTAREA"
49
+ },
50
+ {
51
+ "name": "mKtesturl",
52
+ "marketoDataType": "URL"
53
+ },
54
+ {
55
+ "name": "mKTestReference",
56
+ "marketoDataType": "REFERENCE"
57
+ },
58
+ {
59
+ "name": "mKTestFormula",
60
+ "marketoDataType": "FORMULA"
61
+ },
62
+ {
63
+ "name": "mKtesttext",
64
+ "marketoDataType": "TEXT"
65
+ }
66
+ ]
@@ -0,0 +1,24 @@
1
+ {
2
+ "responses":[
3
+ {
4
+ "requestId": "83e9#166f835a592",
5
+ "result": [
6
+ {
7
+ "seq": 0,
8
+ "marketoGUID": "aa168cfa-626d-49b4-b735-526e0b8c069e",
9
+ "id": 1,
10
+ "createdAt": "2018-11-06T12:01:49Z",
11
+ "updatedAt": "2018-11-06T12:01:49Z"
12
+ },
13
+ {
14
+ "seq": 1,
15
+ "marketoGUID": "da7c347d-754b-44f3-9821-d1233f0852f6",
16
+ "id": 2,
17
+ "createdAt": "2018-11-06T12:01:49Z",
18
+ "updatedAt": "2018-11-06T12:01:49Z"
19
+ }
20
+ ],
21
+ "success": true
22
+ }
23
+ ]
24
+ }
@@ -0,0 +1,23 @@
1
+ [
2
+ {
3
+ "seq": 0,
4
+ "marketoGUID": "aa168cfa-626d-49b4-b735-526e0b8c069e",
5
+ "id": 1,
6
+ "createdAt": "2018-11-06T12:01:49Z",
7
+ "updatedAt": "2018-11-06T12:01:49Z"
8
+ },
9
+ {
10
+ "seq": 1,
11
+ "marketoGUID": "da7c347d-754b-44f3-9821-d1233f0852f6",
12
+ "id": 2,
13
+ "createdAt": "2018-11-06T12:01:49Z",
14
+ "updatedAt": "2018-11-06T12:01:49Z"
15
+ },
16
+ {
17
+ "seq": 2,
18
+ "marketoGUID": "sdfdc347d-754b-5645-9821-d1233f065456",
19
+ "id": 3,
20
+ "createdAt": "2018-11-06T12:01:49Z",
21
+ "updatedAt": "2018-11-06T12:01:49Z"
22
+ }
23
+ ]
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: embulk-input-marketo
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.11
4
+ version: 0.6.12
5
5
  platform: ruby
6
6
  authors:
7
7
  - uu59
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2018-09-10 00:00:00.000000000 Z
13
+ date: 2018-11-19 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  requirement: !ruby/object:Gem::Requirement
@@ -72,6 +72,8 @@ files:
72
72
  - src/main/java/org/embulk/input/marketo/MarketoUtils.java
73
73
  - src/main/java/org/embulk/input/marketo/delegate/ActivityBulkExtractInputPlugin.java
74
74
  - src/main/java/org/embulk/input/marketo/delegate/CampaignInputPlugin.java
75
+ - src/main/java/org/embulk/input/marketo/delegate/CustomObjectInputPlugin.java
76
+ - src/main/java/org/embulk/input/marketo/delegate/CustomObjectResponseMapperBuilder.java
75
77
  - src/main/java/org/embulk/input/marketo/delegate/LeadBulkExtractInputPlugin.java
76
78
  - src/main/java/org/embulk/input/marketo/delegate/LeadServiceResponseMapperBuilder.java
77
79
  - src/main/java/org/embulk/input/marketo/delegate/LeadWithListInputPlugin.java
@@ -98,6 +100,7 @@ files:
98
100
  - src/test/java/org/embulk/input/marketo/MarketoUtilsTest.java
99
101
  - src/test/java/org/embulk/input/marketo/delegate/ActivityBulkExtractInputPluginTest.java
100
102
  - src/test/java/org/embulk/input/marketo/delegate/CampaignInputPluginTest.java
103
+ - src/test/java/org/embulk/input/marketo/delegate/CustomObjectInputPluginTest.java
101
104
  - src/test/java/org/embulk/input/marketo/delegate/LeadBulkExtractInputPluginTest.java
102
105
  - src/test/java/org/embulk/input/marketo/delegate/LeadServiceResponseMapperBuilderTest.java
103
106
  - src/test/java/org/embulk/input/marketo/delegate/LeadWithListInputPluginTest.java
@@ -107,6 +110,7 @@ files:
107
110
  - src/test/java/org/embulk/input/marketo/rest/MarketoBaseRestClientTest.java
108
111
  - src/test/java/org/embulk/input/marketo/rest/MarketoRestClientTest.java
109
112
  - src/test/resources/config/activity_bulk_extract_config.yaml
113
+ - src/test/resources/config/custom_object_config.yaml
110
114
  - src/test/resources/config/lead_bulk_extract_config.yaml
111
115
  - src/test/resources/config/rest_config.yaml
112
116
  - src/test/resources/fixtures/activity_extract1.csv
@@ -114,6 +118,11 @@ files:
114
118
  - src/test/resources/fixtures/all_program_full.json
115
119
  - src/test/resources/fixtures/campaign_response.json
116
120
  - src/test/resources/fixtures/campaign_response_full.json
121
+ - src/test/resources/fixtures/custom_object_describe.json
122
+ - src/test/resources/fixtures/custom_object_describe_marketo_fields_full.json
123
+ - src/test/resources/fixtures/custom_object_expected.json
124
+ - src/test/resources/fixtures/custom_object_response.json
125
+ - src/test/resources/fixtures/custom_object_response_full.json
117
126
  - src/test/resources/fixtures/lead_by_list.json
118
127
  - src/test/resources/fixtures/lead_by_program_response.json
119
128
  - src/test/resources/fixtures/lead_describe.json
@@ -130,8 +139,8 @@ files:
130
139
  - classpath/jetty-util-9.2.14.v20151106.jar
131
140
  - classpath/jetty-http-9.2.14.v20151106.jar
132
141
  - classpath/jetty-client-9.2.14.v20151106.jar
142
+ - classpath/embulk-input-marketo-0.6.12.jar
133
143
  - classpath/embulk-base-restclient-0.5.3.jar
134
- - classpath/embulk-input-marketo-0.6.11.jar
135
144
  - classpath/embulk-util-retryhelper-jetty92-0.5.3.jar
136
145
  homepage: https://github.com/treasure-data/embulk-input-marketo
137
146
  licenses: