embulk-input-marketo-through-proxy 0.6.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. checksums.yaml +7 -0
  2. data/.github/CODEOWNERS +1 -0
  3. data/.github/PULL_REQUEST_TEMPLATE.md +37 -0
  4. data/.github/workflows/build.yml +38 -0
  5. data/.gitignore +14 -0
  6. data/CHANGELOG.md +178 -0
  7. data/LICENSE.txt +21 -0
  8. data/README.md +231 -0
  9. data/build.gradle +105 -0
  10. data/config/checkstyle/checkstyle.xml +128 -0
  11. data/config/checkstyle/default.xml +108 -0
  12. data/gradle/wrapper/gradle-wrapper.jar +0 -0
  13. data/gradle/wrapper/gradle-wrapper.properties +6 -0
  14. data/gradlew +169 -0
  15. data/gradlew.bat +84 -0
  16. data/lib/embulk/input/marketo.rb +3 -0
  17. data/settings.gradle +1 -0
  18. data/src/main/java/org/embulk/input/marketo/CsvTokenizer.java +695 -0
  19. data/src/main/java/org/embulk/input/marketo/MarketoInputPlugin.java +15 -0
  20. data/src/main/java/org/embulk/input/marketo/MarketoInputPluginDelegate.java +100 -0
  21. data/src/main/java/org/embulk/input/marketo/MarketoService.java +47 -0
  22. data/src/main/java/org/embulk/input/marketo/MarketoServiceImpl.java +258 -0
  23. data/src/main/java/org/embulk/input/marketo/MarketoUtils.java +212 -0
  24. data/src/main/java/org/embulk/input/marketo/delegate/ActivityBulkExtractInputPlugin.java +169 -0
  25. data/src/main/java/org/embulk/input/marketo/delegate/CampaignInputPlugin.java +48 -0
  26. data/src/main/java/org/embulk/input/marketo/delegate/CustomObjectInputPlugin.java +124 -0
  27. data/src/main/java/org/embulk/input/marketo/delegate/CustomObjectResponseMapperBuilder.java +81 -0
  28. data/src/main/java/org/embulk/input/marketo/delegate/LeadBulkExtractInputPlugin.java +68 -0
  29. data/src/main/java/org/embulk/input/marketo/delegate/LeadServiceResponseMapperBuilder.java +85 -0
  30. data/src/main/java/org/embulk/input/marketo/delegate/LeadWithListInputPlugin.java +89 -0
  31. data/src/main/java/org/embulk/input/marketo/delegate/LeadWithProgramInputPlugin.java +85 -0
  32. data/src/main/java/org/embulk/input/marketo/delegate/MarketoBaseBulkExtractInputPlugin.java +448 -0
  33. data/src/main/java/org/embulk/input/marketo/delegate/MarketoBaseInputPluginDelegate.java +160 -0
  34. data/src/main/java/org/embulk/input/marketo/delegate/ProgramInputPlugin.java +234 -0
  35. data/src/main/java/org/embulk/input/marketo/exception/MarketoAPIException.java +30 -0
  36. data/src/main/java/org/embulk/input/marketo/model/BulkExtractRangeHeader.java +26 -0
  37. data/src/main/java/org/embulk/input/marketo/model/MarketoAccessTokenResponse.java +92 -0
  38. data/src/main/java/org/embulk/input/marketo/model/MarketoBulkExtractRequest.java +68 -0
  39. data/src/main/java/org/embulk/input/marketo/model/MarketoError.java +40 -0
  40. data/src/main/java/org/embulk/input/marketo/model/MarketoField.java +126 -0
  41. data/src/main/java/org/embulk/input/marketo/model/MarketoResponse.java +82 -0
  42. data/src/main/java/org/embulk/input/marketo/model/filter/DateRangeFilter.java +40 -0
  43. data/src/main/java/org/embulk/input/marketo/rest/MarketoBaseRestClient.java +344 -0
  44. data/src/main/java/org/embulk/input/marketo/rest/MarketoInputStreamResponseEntityReader.java +69 -0
  45. data/src/main/java/org/embulk/input/marketo/rest/MarketoRESTEndpoint.java +47 -0
  46. data/src/main/java/org/embulk/input/marketo/rest/MarketoResponseJetty92EntityReader.java +89 -0
  47. data/src/main/java/org/embulk/input/marketo/rest/MarketoRestClient.java +601 -0
  48. data/src/main/java/org/embulk/input/marketo/rest/RecordPagingIterable.java +180 -0
  49. data/src/test/java/org/embulk/input/marketo/MarketoServiceImplTest.java +147 -0
  50. data/src/test/java/org/embulk/input/marketo/MarketoUtilsTest.java +89 -0
  51. data/src/test/java/org/embulk/input/marketo/delegate/ActivityBulkExtractInputPluginTest.java +129 -0
  52. data/src/test/java/org/embulk/input/marketo/delegate/CampaignInputPluginTest.java +73 -0
  53. data/src/test/java/org/embulk/input/marketo/delegate/CustomObjectInputPluginTest.java +175 -0
  54. data/src/test/java/org/embulk/input/marketo/delegate/LeadBulkExtractInputPluginTest.java +102 -0
  55. data/src/test/java/org/embulk/input/marketo/delegate/LeadServiceResponseMapperBuilderTest.java +119 -0
  56. data/src/test/java/org/embulk/input/marketo/delegate/LeadWithListInputPluginTest.java +132 -0
  57. data/src/test/java/org/embulk/input/marketo/delegate/LeadWithProgramInputPluginTest.java +134 -0
  58. data/src/test/java/org/embulk/input/marketo/delegate/MarketoBaseBulkExtractInputPluginTest.java +171 -0
  59. data/src/test/java/org/embulk/input/marketo/delegate/MarketoBaseInputPluginDelegateTest.java +60 -0
  60. data/src/test/java/org/embulk/input/marketo/delegate/ProgramInputPluginTest.java +325 -0
  61. data/src/test/java/org/embulk/input/marketo/rest/MarketoBaseRestClientTest.java +368 -0
  62. data/src/test/java/org/embulk/input/marketo/rest/MarketoRestClientTest.java +649 -0
  63. data/src/test/resources/config/activity_bulk_extract_config.yaml +7 -0
  64. data/src/test/resources/config/custom_object_config.yaml +8 -0
  65. data/src/test/resources/config/lead_bulk_extract_config.yaml +8 -0
  66. data/src/test/resources/config/rest_config.yaml +3 -0
  67. data/src/test/resources/fixtures/activity_extract1.csv +35 -0
  68. data/src/test/resources/fixtures/activity_extract2.csv +22 -0
  69. data/src/test/resources/fixtures/activity_types.json +22 -0
  70. data/src/test/resources/fixtures/all_program_full.json +53 -0
  71. data/src/test/resources/fixtures/campaign_response.json +38 -0
  72. data/src/test/resources/fixtures/campaign_response_full.json +102 -0
  73. data/src/test/resources/fixtures/custom_object_describe.json +124 -0
  74. data/src/test/resources/fixtures/custom_object_describe_marketo_fields_full.json +22 -0
  75. data/src/test/resources/fixtures/custom_object_expected.json +66 -0
  76. data/src/test/resources/fixtures/custom_object_response.json +24 -0
  77. data/src/test/resources/fixtures/custom_object_response_full.json +23 -0
  78. data/src/test/resources/fixtures/lead_by_list.json +33 -0
  79. data/src/test/resources/fixtures/lead_by_program_response.json +47 -0
  80. data/src/test/resources/fixtures/lead_describe.json +221 -0
  81. data/src/test/resources/fixtures/lead_describe_expected.json +66 -0
  82. data/src/test/resources/fixtures/lead_describe_marketo_fields_full.json +518 -0
  83. data/src/test/resources/fixtures/lead_extract1.csv +11 -0
  84. data/src/test/resources/fixtures/lead_response_full.json +2402 -0
  85. data/src/test/resources/fixtures/lead_with_program_full.json +17 -0
  86. data/src/test/resources/fixtures/leads_extract2.csv +10 -0
  87. data/src/test/resources/fixtures/list_reponse_full.json +191 -0
  88. data/src/test/resources/fixtures/lists_response.json +31 -0
  89. data/src/test/resources/fixtures/program_response.json +71 -0
  90. metadata +173 -0
@@ -0,0 +1,169 @@
1
+ package org.embulk.input.marketo.delegate;
2
+
3
+ import com.fasterxml.jackson.databind.node.ObjectNode;
4
+ import com.google.common.annotations.VisibleForTesting;
5
+ import com.google.common.base.Optional;
6
+ import org.apache.commons.lang3.StringUtils;
7
+ import org.embulk.base.restclient.ServiceResponseMapper;
8
+ import org.embulk.base.restclient.jackson.JacksonServiceResponseMapper;
9
+ import org.embulk.base.restclient.record.ValueLocator;
10
+ import org.embulk.config.Config;
11
+ import org.embulk.config.ConfigDefault;
12
+ import org.embulk.config.ConfigException;
13
+ import org.embulk.input.marketo.MarketoService;
14
+ import org.embulk.input.marketo.MarketoServiceImpl;
15
+ import org.embulk.input.marketo.MarketoUtils;
16
+ import org.embulk.input.marketo.rest.MarketoRestClient;
17
+ import org.embulk.spi.type.Types;
18
+
19
+ import java.io.FileInputStream;
20
+ import java.io.FileNotFoundException;
21
+ import java.io.InputStream;
22
+ import java.text.MessageFormat;
23
+ import java.time.OffsetDateTime;
24
+ import java.util.ArrayList;
25
+ import java.util.Date;
26
+ import java.util.Iterator;
27
+ import java.util.List;
28
+
29
+ /**
30
+ * Created by tai.khuu on 9/18/17.
31
+ */
32
+ public class ActivityBulkExtractInputPlugin extends MarketoBaseBulkExtractInputPlugin<ActivityBulkExtractInputPlugin.PluginTask>
33
+ {
34
+ public static final String INCREMENTAL_COLUMN = "activityDate";
35
+ public static final String UID_COLUMN = "marketoGUID";
36
+
37
+ public interface PluginTask extends MarketoBaseBulkExtractInputPlugin.PluginTask
38
+ {
39
+ @Config("activity_type_ids")
40
+ @ConfigDefault("[]")
41
+ List<String> getActivityTypeIds();
42
+
43
+ @Config("act_type_ids")
44
+ @ConfigDefault("[]")
45
+ List<Integer> getActTypeIds();
46
+
47
+ void setActTypeIds(List<Integer> activityIds);
48
+ }
49
+
50
+ @Override
51
+ public void validateInputTask(PluginTask task)
52
+ {
53
+ task.setIncrementalColumn(Optional.of(INCREMENTAL_COLUMN));
54
+ task.setUidColumn(Optional.of(UID_COLUMN));
55
+ if (!task.getActivityTypeIds().isEmpty()) {
56
+ List<Integer> activityIds = checkValidActivityTypeIds(task);
57
+
58
+ // check input with values from server
59
+ try (MarketoRestClient restClient = createMarketoRestClient(task)) {
60
+ MarketoService marketoService = new MarketoServiceImpl(restClient);
61
+ Iterable<ObjectNode> nodes = marketoService.getActivityTypes();
62
+ if (nodes != null) {
63
+ checkValidActivityTypeIds(nodes, activityIds);
64
+ }
65
+ // ignorable if unable to get activity type ids. If thing gone wrong, the bulk extract will throw errors
66
+ }
67
+
68
+ // task will use getActTypeIds instead of getActivityTypeIds method
69
+ task.setActTypeIds(activityIds);
70
+ }
71
+ super.validateInputTask(task);
72
+ }
73
+
74
+ /**
75
+ * Check if user input activity_type_ids valid
76
+ * @param task
77
+ * @return values transformed to array of Integer
78
+ */
79
+ private List<Integer> checkValidActivityTypeIds(PluginTask task)
80
+ {
81
+ List<String> invalidIds = new ArrayList<>();
82
+ for (String id : task.getActivityTypeIds()) {
83
+ if (StringUtils.isBlank(id) || !StringUtils.isNumeric(StringUtils.trimToEmpty(id))) {
84
+ invalidIds.add(id);
85
+ }
86
+ }
87
+
88
+ if (!invalidIds.isEmpty()) {
89
+ throw new ConfigException(MessageFormat.format("Invalid activity type id: [{0}]", StringUtils.join(invalidIds, ", ")));
90
+ }
91
+
92
+ // transform and set
93
+ List<Integer> activityIds = new ArrayList<>();
94
+ for (String id : task.getActivityTypeIds()) {
95
+ activityIds.add(Integer.valueOf(StringUtils.trimToEmpty(id)));
96
+ }
97
+
98
+ return activityIds;
99
+ }
100
+
101
+ @VisibleForTesting
102
+ protected void checkValidActivityTypeIds(Iterable<ObjectNode> nodes, List<Integer> activityIds)
103
+ {
104
+ Iterator<ObjectNode> it = nodes.iterator();
105
+
106
+ List<Integer> inputIds = new ArrayList<>(activityIds);
107
+
108
+ while (it.hasNext()) {
109
+ ObjectNode node = it.next();
110
+ int id = node.get("id").asInt(0);
111
+ if (id > 0) {
112
+ inputIds.remove(Integer.valueOf(id));
113
+ }
114
+ }
115
+
116
+ if (!inputIds.isEmpty()) {
117
+ throw new ConfigException(MessageFormat.format("Invalid activity type ids: [{0}], Available activity types: \n{1}",
118
+ StringUtils.join(inputIds, ", "),
119
+ buildActivityIdNameInfo(nodes)));
120
+ }
121
+ }
122
+
123
+ private String buildActivityIdNameInfo(Iterable<ObjectNode> nodes)
124
+ {
125
+ Iterator<ObjectNode> it = nodes.iterator();
126
+ StringBuilder messageBuilder = new StringBuilder();
127
+ while (it.hasNext()) {
128
+ ObjectNode node = it.next();
129
+ int id = node.get("id").asInt(0);
130
+ String name = node.get("name").asText("");
131
+ if (id > 0) {
132
+ messageBuilder.append("- activity id: ");
133
+ messageBuilder.append(String.valueOf(id));
134
+ messageBuilder.append(", name: ");
135
+ messageBuilder.append(name);
136
+ messageBuilder.append("\n");
137
+ }
138
+ }
139
+
140
+ return messageBuilder.toString();
141
+ }
142
+
143
+ @Override
144
+ protected InputStream getExtractedStream(MarketoService service, PluginTask task, OffsetDateTime fromDate, OffsetDateTime toDate)
145
+ {
146
+ try {
147
+ return new FileInputStream(service.extractAllActivity(task.getActTypeIds(), Date.from(fromDate.toInstant()),
148
+ Date.from(toDate.toInstant()), task.getPollingIntervalSecond(), task.getBulkJobTimeoutSecond()));
149
+ }
150
+ catch (FileNotFoundException e) {
151
+ throw new RuntimeException("Exception when trying to extract activity", e);
152
+ }
153
+ }
154
+
155
+ @Override
156
+ public ServiceResponseMapper<? extends ValueLocator> buildServiceResponseMapper(PluginTask task)
157
+ {
158
+ JacksonServiceResponseMapper.Builder builder = JacksonServiceResponseMapper.builder();
159
+ builder.add("marketoGUID", Types.STRING)
160
+ .add("leadId", Types.STRING)
161
+ .add("activityDate", Types.TIMESTAMP, MarketoUtils.MARKETO_DATE_TIME_FORMAT)
162
+ .add("activityTypeId", Types.STRING)
163
+ .add("campaignId", Types.STRING)
164
+ .add("primaryAttributeValueId", Types.STRING)
165
+ .add("primaryAttributeValue", Types.STRING)
166
+ .add("attributes", Types.JSON);
167
+ return builder.build();
168
+ }
169
+ }
@@ -0,0 +1,48 @@
1
+ package org.embulk.input.marketo.delegate;
2
+
3
+ import com.google.common.collect.FluentIterable;
4
+ import org.embulk.base.restclient.ServiceResponseMapper;
5
+ import org.embulk.base.restclient.jackson.JacksonServiceResponseMapper;
6
+ import org.embulk.base.restclient.record.ServiceRecord;
7
+ import org.embulk.base.restclient.record.ValueLocator;
8
+ import org.embulk.input.marketo.MarketoService;
9
+ import org.embulk.input.marketo.MarketoUtils;
10
+ import org.embulk.spi.type.Types;
11
+
12
+ import java.util.Iterator;
13
+
14
+ /**
15
+ * Input plugin use to import Campaign
16
+ * Created by tai.khuu on 9/18/17.
17
+ */
18
+ public class CampaignInputPlugin extends MarketoBaseInputPluginDelegate<CampaignInputPlugin.PluginTask>
19
+ {
20
+ public interface PluginTask extends MarketoBaseInputPluginDelegate.PluginTask
21
+ {
22
+ }
23
+
24
+ public CampaignInputPlugin()
25
+ {
26
+ }
27
+
28
+ @Override
29
+ protected Iterator<ServiceRecord> getServiceRecords(MarketoService marketoService, PluginTask task)
30
+ {
31
+ return FluentIterable.from(marketoService.getCampaign()).transform(MarketoUtils.TRANSFORM_OBJECT_TO_JACKSON_SERVICE_RECORD_FUNCTION).iterator();
32
+ }
33
+
34
+ @Override
35
+ public ServiceResponseMapper<? extends ValueLocator> buildServiceResponseMapper(PluginTask task)
36
+ {
37
+ JacksonServiceResponseMapper.Builder builder = JacksonServiceResponseMapper.builder();
38
+ builder.add("id", Types.LONG)
39
+ .add("name", Types.STRING)
40
+ .add("description", Types.STRING)
41
+ .add("type", Types.STRING)
42
+ .add("workspaceName", Types.STRING)
43
+ .add("createdAt", Types.TIMESTAMP, MarketoUtils.MARKETO_DATE_TIME_FORMAT)
44
+ .add("updatedAt", Types.TIMESTAMP, MarketoUtils.MARKETO_DATE_TIME_FORMAT)
45
+ .add("active", Types.BOOLEAN);
46
+ return builder.build();
47
+ }
48
+ }
@@ -0,0 +1,124 @@
1
+ package org.embulk.input.marketo.delegate;
2
+
3
+ import com.fasterxml.jackson.databind.node.ObjectNode;
4
+ import com.google.common.base.Optional;
5
+ import com.google.common.collect.FluentIterable;
6
+ import com.google.common.collect.Iterables;
7
+ import org.apache.commons.lang3.StringUtils;
8
+ import org.embulk.base.restclient.ServiceResponseMapper;
9
+ import org.embulk.base.restclient.record.ServiceRecord;
10
+ import org.embulk.base.restclient.record.ValueLocator;
11
+ import org.embulk.config.Config;
12
+ import org.embulk.config.ConfigDefault;
13
+ import org.embulk.config.ConfigException;
14
+ import org.embulk.input.marketo.MarketoService;
15
+ import org.embulk.input.marketo.MarketoServiceImpl;
16
+ import org.embulk.input.marketo.MarketoUtils;
17
+ import org.embulk.input.marketo.rest.MarketoRestClient;
18
+ import org.embulk.spi.Exec;
19
+ import org.slf4j.Logger;
20
+
21
+ import java.util.Iterator;
22
+ import java.util.Set;
23
+ import java.util.stream.Collectors;
24
+ import java.util.stream.Stream;
25
+
26
+ public class CustomObjectInputPlugin extends MarketoBaseInputPluginDelegate<CustomObjectInputPlugin.PluginTask>
27
+ {
28
+ private final Logger logger = Exec.getLogger(getClass());
29
+
30
+ public interface PluginTask extends MarketoBaseInputPluginDelegate.PluginTask, CustomObjectResponseMapperBuilder.PluginTask
31
+ {
32
+ @Config("custom_object_filter_type")
33
+ @ConfigDefault("\"\"")
34
+ String getCustomObjectFilterType();
35
+
36
+ @Config("custom_object_filter_from_value")
37
+ @ConfigDefault("1")
38
+ Integer getFromValue();
39
+
40
+ @Config("custom_object_filter_to_value")
41
+ @ConfigDefault("null")
42
+ Optional<Integer> getToValue();
43
+
44
+ @Config("custom_object_filter_values")
45
+ @ConfigDefault("null")
46
+ Optional<String> getCustomObjectFilterValues();
47
+ }
48
+
49
+ public CustomObjectInputPlugin()
50
+ {
51
+ }
52
+
53
+ @Override
54
+ public void validateInputTask(PluginTask task)
55
+ {
56
+ super.validateInputTask(task);
57
+ if (StringUtils.isBlank(task.getCustomObjectFilterType())) {
58
+ throw new ConfigException("`custom_object_filter_type` cannot be empty");
59
+ }
60
+ if (StringUtils.isBlank(task.getCustomObjectAPIName())) {
61
+ throw new ConfigException("`custom_object_api_name` cannot be empty");
62
+ }
63
+ if (!task.getCustomObjectFilterValues().isPresent()) {
64
+ if (task.getToValue().isPresent() && !isValidFilterRange(task)) {
65
+ throw new ConfigException(String.format("`to_value` (%s) cannot be less than the `from_value` (%s)", task.getToValue().get(), task.getFromValue()));
66
+ }
67
+ }
68
+ else if (refineFilterValues(task.getCustomObjectFilterValues().get()).isEmpty()) {
69
+ throw new ConfigException("`custom_object_filter_values` cannot contain empty values only");
70
+ }
71
+ }
72
+
73
+ private Set<String> refineFilterValues(String filterValues)
74
+ {
75
+ return Stream.of(StringUtils.split(filterValues, ",")).map(StringUtils::trimToEmpty).filter(StringUtils::isNotBlank).collect(Collectors.toSet());
76
+ }
77
+
78
+ private boolean isValidFilterRange(PluginTask task)
79
+ {
80
+ return task.getToValue().get() > task.getFromValue();
81
+ }
82
+
83
+ @Override
84
+ protected Iterator<ServiceRecord> getServiceRecords(MarketoService marketoService, PluginTask task)
85
+ {
86
+ Iterable<ObjectNode> responseObj;
87
+ if (task.getCustomObjectFilterValues().isPresent()) {
88
+ Set<String> refinedValues = refineFilterValues(task.getCustomObjectFilterValues().get());
89
+ responseObj = marketoService.getCustomObject(task.getCustomObjectAPIName(), task.getCustomObjectFilterType(), refinedValues, task.getCustomObjectFields().orNull());
90
+ }
91
+ else {
92
+ // When `to_value` is not set, will try to import all consecutive custom objects started from `from_value`
93
+ responseObj = marketoService.getCustomObject(task.getCustomObjectAPIName(), task.getCustomObjectFilterType(), task.getCustomObjectFields().orNull(), task.getFromValue(), task.getToValue().orNull());
94
+ }
95
+
96
+ return FluentIterable.from(filterInvalidRecords(responseObj)).transform(MarketoUtils.TRANSFORM_OBJECT_TO_JACKSON_SERVICE_RECORD_FUNCTION).iterator();
97
+ }
98
+
99
+ /**
100
+ * Marketo include error in invalid records. This method will filter those records and print it to output log
101
+ * @param resultIt
102
+ * @return
103
+ */
104
+ private Iterable<ObjectNode> filterInvalidRecords(Iterable<ObjectNode> resultIt)
105
+ {
106
+ return Iterables.filter(resultIt, (obj) -> {
107
+ if (obj.has("reasons")) {
108
+ logger.warn(obj.get("reasons").toString());
109
+ return false;
110
+ }
111
+ return true;
112
+ });
113
+ }
114
+
115
+ @Override
116
+ public ServiceResponseMapper<? extends ValueLocator> buildServiceResponseMapper(PluginTask task)
117
+ {
118
+ try (MarketoRestClient marketoRestClient = createMarketoRestClient(task)) {
119
+ MarketoService marketoService = new MarketoServiceImpl(marketoRestClient);
120
+ CustomObjectResponseMapperBuilder<PluginTask> customObjectResponseMapperBuilder = new CustomObjectResponseMapperBuilder<>(task, marketoService);
121
+ return customObjectResponseMapperBuilder.buildServiceResponseMapper(task);
122
+ }
123
+ }
124
+ }
@@ -0,0 +1,81 @@
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
+
16
+ import java.util.ArrayList;
17
+ import java.util.Arrays;
18
+ import java.util.List;
19
+
20
+ public class CustomObjectResponseMapperBuilder<T extends CustomObjectResponseMapperBuilder.PluginTask> implements ServiceResponseMapperBuildable<T>
21
+ {
22
+ private static final Logger LOGGER = Exec.getLogger(CustomObjectResponseMapperBuilder.class);
23
+ private MarketoService marketoService;
24
+
25
+ private T pluginTask;
26
+
27
+ public interface PluginTask extends MarketoBaseInputPluginDelegate.PluginTask
28
+ {
29
+ @Config("custom_object_api_name")
30
+ @ConfigDefault("\"\"")
31
+ String getCustomObjectAPIName();
32
+
33
+ @Config("custom_object_fields")
34
+ @ConfigDefault("null")
35
+ Optional<String> getCustomObjectFields();
36
+ }
37
+
38
+ public CustomObjectResponseMapperBuilder(T task, MarketoService marketoService)
39
+ {
40
+ this.pluginTask = task;
41
+ this.marketoService = marketoService;
42
+ }
43
+
44
+ protected List<MarketoField> getCustomObjectColumns()
45
+ {
46
+ List<MarketoField> columns = marketoService.describeCustomObject(pluginTask.getCustomObjectAPIName());
47
+ if (pluginTask.getCustomObjectFields().isPresent() && StringUtils.isNotBlank(pluginTask.getCustomObjectFields().get())) {
48
+ List<MarketoField> filteredColumns = new ArrayList<>();
49
+ List<String> includedFields = Arrays.asList(pluginTask.getCustomObjectFields().get().split(","));
50
+ for (String fieldName : includedFields) {
51
+ Optional<MarketoField> includedField = lookupFieldIgnoreCase(columns, fieldName);
52
+ if (includedField.isPresent()) {
53
+ filteredColumns.add(includedField.get());
54
+ }
55
+ else {
56
+ LOGGER.warn("Included field [{}] not found in Marketo Custom Object [{}] field", fieldName, pluginTask.getCustomObjectAPIName());
57
+ }
58
+ }
59
+ columns = filteredColumns;
60
+ LOGGER.info("Included Fields option is set, included columns: [{}]", columns);
61
+ }
62
+ return columns;
63
+ }
64
+
65
+ private static Optional<MarketoField> lookupFieldIgnoreCase(List<MarketoField> inputList, String lookupFieldName)
66
+ {
67
+ for (MarketoField marketoField : inputList) {
68
+ if (marketoField.getName().equalsIgnoreCase(lookupFieldName)) {
69
+ return Optional.of(marketoField);
70
+ }
71
+ }
72
+ return Optional.absent();
73
+ }
74
+
75
+ @Override
76
+ public ServiceResponseMapper<? extends ValueLocator> buildServiceResponseMapper(T task)
77
+ {
78
+ List<MarketoField> customObjectColumns = getCustomObjectColumns();
79
+ return MarketoUtils.buildDynamicResponseMapper(pluginTask.getSchemaColumnPrefix(), customObjectColumns);
80
+ }
81
+ }
@@ -0,0 +1,68 @@
1
+ package org.embulk.input.marketo.delegate;
2
+
3
+ import com.google.common.base.Optional;
4
+ import org.embulk.base.restclient.ServiceResponseMapper;
5
+ import org.embulk.base.restclient.record.ValueLocator;
6
+ import org.embulk.config.Config;
7
+ import org.embulk.config.ConfigDefault;
8
+ import org.embulk.input.marketo.MarketoService;
9
+ import org.embulk.input.marketo.MarketoServiceImpl;
10
+ import org.embulk.input.marketo.rest.MarketoRestClient;
11
+ import org.embulk.spi.Exec;
12
+ import org.slf4j.Logger;
13
+
14
+ import java.io.FileInputStream;
15
+ import java.io.FileNotFoundException;
16
+ import java.io.InputStream;
17
+ import java.time.OffsetDateTime;
18
+ import java.util.Date;
19
+ import java.util.List;
20
+
21
+ /**
22
+ * Created by tai.khuu on 9/18/17.
23
+ */
24
+ public class LeadBulkExtractInputPlugin extends MarketoBaseBulkExtractInputPlugin<LeadBulkExtractInputPlugin.PluginTask>
25
+ {
26
+ private static final Logger LOGGER = Exec.getLogger(LeadBulkExtractInputPlugin.class);
27
+
28
+ private static final String UPDATED_AT = "updatedAt";
29
+
30
+ public interface PluginTask extends MarketoBaseBulkExtractInputPlugin.PluginTask, LeadServiceResponseMapperBuilder.PluginTask
31
+ {
32
+ @Config("use_updated_at")
33
+ @ConfigDefault("false")
34
+ boolean getUseUpdatedAt();
35
+ }
36
+
37
+ @Override
38
+ public void validateInputTask(PluginTask task)
39
+ {
40
+ if (task.getUseUpdatedAt()) {
41
+ task.setIncrementalColumn(Optional.of(UPDATED_AT));
42
+ }
43
+ super.validateInputTask(task);
44
+ }
45
+
46
+ @Override
47
+ protected InputStream getExtractedStream(MarketoService service, PluginTask task, OffsetDateTime fromDate, OffsetDateTime toDate)
48
+ {
49
+ try {
50
+ List<String> fieldNames = task.getExtractedFields();
51
+ return new FileInputStream(service.extractLead(Date.from(fromDate.toInstant()), Date.from(toDate.toInstant()),
52
+ fieldNames, task.getIncrementalColumn().orNull(), task.getPollingIntervalSecond(), task.getBulkJobTimeoutSecond()));
53
+ }
54
+ catch (FileNotFoundException e) {
55
+ throw new RuntimeException("File not found", e);
56
+ }
57
+ }
58
+
59
+ @Override
60
+ public ServiceResponseMapper<? extends ValueLocator> buildServiceResponseMapper(PluginTask task)
61
+ {
62
+ try (MarketoRestClient marketoRestClient = createMarketoRestClient(task)) {
63
+ MarketoService marketoService = new MarketoServiceImpl(marketoRestClient);
64
+ LeadServiceResponseMapperBuilder<PluginTask> leadServiceResponseMapperBuilder = new LeadServiceResponseMapperBuilder<>(task, marketoService);
65
+ return leadServiceResponseMapperBuilder.buildServiceResponseMapper(task);
66
+ }
67
+ }
68
+ }