embulk-input-marketo 0.6.11 → 0.6.12

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 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: