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,160 @@
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.collect.Lists;
6
+ import org.apache.commons.lang3.StringUtils;
7
+ import org.embulk.base.restclient.DefaultServiceDataSplitter;
8
+ import org.embulk.base.restclient.RestClientInputPluginDelegate;
9
+ import org.embulk.base.restclient.RestClientInputTaskBase;
10
+ import org.embulk.base.restclient.ServiceDataSplitter;
11
+ import org.embulk.base.restclient.record.RecordImporter;
12
+ import org.embulk.base.restclient.record.ServiceRecord;
13
+ import org.embulk.config.Config;
14
+ import org.embulk.config.ConfigDefault;
15
+ import org.embulk.config.ConfigDiff;
16
+ import org.embulk.config.ConfigException;
17
+ import org.embulk.config.TaskReport;
18
+ import org.embulk.input.marketo.MarketoService;
19
+ import org.embulk.input.marketo.MarketoServiceImpl;
20
+ import org.embulk.input.marketo.rest.MarketoRestClient;
21
+ import org.embulk.spi.Exec;
22
+ import org.embulk.spi.PageBuilder;
23
+ import org.embulk.spi.Schema;
24
+ import org.slf4j.Logger;
25
+ import org.slf4j.LoggerFactory;
26
+
27
+ import java.time.OffsetDateTime;
28
+ import java.time.ZoneOffset;
29
+ import java.time.format.DateTimeFormatter;
30
+ import java.util.ArrayList;
31
+ import java.util.HashSet;
32
+ import java.util.Iterator;
33
+ import java.util.List;
34
+ import java.util.Set;
35
+ import java.util.function.Function;
36
+ import java.util.stream.Collectors;
37
+
38
+ /**
39
+ * Created by tai.khuu on 9/18/17.
40
+ */
41
+ public abstract class MarketoBaseInputPluginDelegate<T extends MarketoBaseInputPluginDelegate.PluginTask> implements RestClientInputPluginDelegate<T>
42
+ {
43
+ private final Logger logger = LoggerFactory.getLogger(getClass());
44
+ public static final int PREVIEW_RECORD_LIMIT = 15;
45
+ public static final String ID_LIST_SEPARATOR_CHAR = ",";
46
+
47
+ public interface PluginTask
48
+ extends RestClientInputTaskBase, MarketoRestClient.PluginTask
49
+ {
50
+ @Config("schema_column_prefix")
51
+ @ConfigDefault("\"mk\"")
52
+ String getSchemaColumnPrefix();
53
+
54
+ @Config("incremental")
55
+ @ConfigDefault("true")
56
+ Boolean getIncremental();
57
+
58
+ String getJobStartTime();
59
+
60
+ void setJobStartTime(String dateTime);
61
+ }
62
+
63
+ @Override
64
+ public ConfigDiff buildConfigDiff(T task, Schema schema, int taskCount, List<TaskReport> taskReports)
65
+ {
66
+ return Exec.newConfigDiff();
67
+ }
68
+
69
+ @Override
70
+ public void validateInputTask(T task)
71
+ {
72
+ task.setJobStartTime(OffsetDateTime.now(ZoneOffset.UTC).format(DateTimeFormatter.ISO_OFFSET_DATE_TIME));
73
+ }
74
+
75
+ @Override
76
+ public TaskReport ingestServiceData(T task, RecordImporter recordImporter, int taskIndex, PageBuilder pageBuilder)
77
+ {
78
+ if (Exec.isPreview()) {
79
+ task.setBatchSize(PREVIEW_RECORD_LIMIT);
80
+ }
81
+ try (MarketoRestClient restClient = createMarketoRestClient(task)) {
82
+ MarketoService marketoService = new MarketoServiceImpl(restClient);
83
+ Iterator<ServiceRecord> serviceRecords = getServiceRecords(marketoService, task);
84
+ int imported = 0;
85
+ while (serviceRecords.hasNext() && (imported < PREVIEW_RECORD_LIMIT || !Exec.isPreview())) {
86
+ ServiceRecord next = serviceRecords.next();
87
+ recordImporter.importRecord(next, pageBuilder);
88
+ imported++;
89
+ }
90
+ return Exec.newTaskReport();
91
+ }
92
+ }
93
+
94
+ protected abstract Iterator<ServiceRecord> getServiceRecords(MarketoService marketoService, T task);
95
+
96
+ @VisibleForTesting
97
+ public MarketoRestClient createMarketoRestClient(PluginTask task)
98
+ {
99
+ return new MarketoRestClient(task);
100
+ }
101
+
102
+ @Override
103
+ public ServiceDataSplitter<T> buildServiceDataSplitter(T task)
104
+ {
105
+ return new DefaultServiceDataSplitter();
106
+ }
107
+
108
+ protected Iterable<ObjectNode> getObjectsByIds(String[] inputIds, Function<Set<String>, Iterable<ObjectNode>> getByIdFunction)
109
+ {
110
+ final Set<String> ids = new HashSet<>();
111
+ final List<String> invalidIds = new ArrayList<>();
112
+
113
+ for (int i = 0; i < inputIds.length; i++) {
114
+ String currentId = StringUtils.trimToNull(inputIds[i]);
115
+ // ignore null or empty ids
116
+ if (currentId == null) {
117
+ continue;
118
+ }
119
+
120
+ // ignore or throw for NaN ids
121
+ if (StringUtils.isNumeric(currentId)) {
122
+ ids.add(currentId);
123
+ }
124
+ else {
125
+ invalidIds.add(inputIds[i]);
126
+ }
127
+ }
128
+
129
+ if (ids.isEmpty()) {
130
+ throw new ConfigException("No valid Id specified");
131
+ }
132
+
133
+ if (!invalidIds.isEmpty()) {
134
+ logger.warn("Ignore invalid Id(s): {}", invalidIds);
135
+ }
136
+
137
+ List<ObjectNode> actualList = Lists.newArrayList(getByIdFunction.apply(ids));
138
+ if (actualList.isEmpty()) {
139
+ throw new ConfigException("No valid Id found");
140
+ }
141
+
142
+ if (actualList.size() != ids.size()) {
143
+ logNoneExistedIds(ids, actualList);
144
+ }
145
+
146
+ return actualList;
147
+ }
148
+
149
+ private void logNoneExistedIds(Set<String> ids, List<ObjectNode> actualList)
150
+ {
151
+ List<String> actualIds = actualList.parallelStream().map(n -> String.valueOf(n.get("id").asInt())).collect(Collectors.toList());
152
+ List<String> missingIds = new ArrayList<>();
153
+ for (String id : ids) {
154
+ if (!actualIds.contains(id)) {
155
+ missingIds.add(id);
156
+ }
157
+ }
158
+ logger.warn("Ignore not exists Id(s): {}", missingIds);
159
+ }
160
+ }
@@ -0,0 +1,234 @@
1
+ package org.embulk.input.marketo.delegate;
2
+
3
+ import com.fasterxml.jackson.annotation.JsonCreator;
4
+ import com.fasterxml.jackson.databind.node.ObjectNode;
5
+ import com.google.common.base.Optional;
6
+ import com.google.common.collect.FluentIterable;
7
+ import org.embulk.base.restclient.ServiceResponseMapper;
8
+ import org.embulk.base.restclient.jackson.JacksonServiceResponseMapper;
9
+ import org.embulk.base.restclient.record.RecordImporter;
10
+ import org.embulk.base.restclient.record.ServiceRecord;
11
+ import org.embulk.base.restclient.record.ValueLocator;
12
+ import org.embulk.config.Config;
13
+ import org.embulk.config.ConfigDefault;
14
+ import org.embulk.config.ConfigDiff;
15
+ import org.embulk.config.ConfigException;
16
+ import org.embulk.config.TaskReport;
17
+ import org.embulk.input.marketo.MarketoService;
18
+ import org.embulk.input.marketo.MarketoUtils;
19
+ import org.embulk.spi.Exec;
20
+ import org.embulk.spi.PageBuilder;
21
+ import org.embulk.spi.Schema;
22
+ import org.embulk.spi.type.Types;
23
+ import org.slf4j.Logger;
24
+
25
+ import java.time.Duration;
26
+ import java.time.OffsetDateTime;
27
+ import java.time.ZoneOffset;
28
+ import java.time.format.DateTimeFormatter;
29
+ import java.time.temporal.ChronoUnit;
30
+ import java.util.Date;
31
+ import java.util.Iterator;
32
+ import java.util.List;
33
+
34
+ public class ProgramInputPlugin extends MarketoBaseInputPluginDelegate<ProgramInputPlugin.PluginTask>
35
+ {
36
+ private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern(MarketoUtils.MARKETO_DATE_SIMPLE_DATE_FORMAT);
37
+ private final Logger logger = Exec.getLogger(getClass());
38
+
39
+ public interface PluginTask extends MarketoBaseInputPluginDelegate.PluginTask
40
+ {
41
+ @Config("query_by")
42
+ @ConfigDefault("null")
43
+ Optional<QueryBy> getQueryBy();
44
+
45
+ @Config("tag_type")
46
+ @ConfigDefault("null")
47
+ Optional<String> getTagType();
48
+
49
+ @Config("tag_value")
50
+ @ConfigDefault("null")
51
+ Optional<String> getTagVallue();
52
+
53
+ @Config("earliest_updated_at")
54
+ @ConfigDefault("null")
55
+ Optional<Date> getEarliestUpdatedAt();
56
+
57
+ @Config("latest_updated_at")
58
+ @ConfigDefault("null")
59
+ Optional<Date> getLatestUpdatedAt();
60
+
61
+ @Config("filter_type")
62
+ @ConfigDefault("null")
63
+ Optional<String> getFilterType();
64
+
65
+ @Config("filter_values")
66
+ @ConfigDefault("null")
67
+ Optional<List<String>> getFilterValues();
68
+
69
+ @Config("report_duration")
70
+ @ConfigDefault("null")
71
+ Optional<Long> getReportDuration();
72
+
73
+ void setLatestUpdatedAt(Optional<Date> latestUpdatedAt);
74
+ }
75
+
76
+ public ProgramInputPlugin()
77
+ {
78
+ }
79
+
80
+ @Override
81
+ public void validateInputTask(PluginTask task)
82
+ {
83
+ super.validateInputTask(task);
84
+ // validate if query_by is selected
85
+ if (task.getQueryBy().isPresent()) {
86
+ switch(task.getQueryBy().get()) {
87
+ case TAG_TYPE:
88
+ //make sure tag type and tag value are not empty
89
+ if (!task.getTagType().isPresent() || !task.getTagVallue().isPresent()) {
90
+ throw new ConfigException("tag_type and tag_value are required when query by Tag Type");
91
+ }
92
+ break;
93
+ case DATE_RANGE:
94
+ // make sure earliest_updated_at is not empty
95
+ if (!task.getEarliestUpdatedAt().isPresent()) {
96
+ throw new ConfigException("`earliest_updated_at` is required when query by Date Range");
97
+ }
98
+
99
+ OffsetDateTime earliest = OffsetDateTime.ofInstant(task.getEarliestUpdatedAt().get().toInstant(), ZoneOffset.UTC);
100
+ if (task.getReportDuration().isPresent()) {
101
+ logger.info("`report_duration` is present, Prefer `report_duration` over `latest_updated_at`");
102
+ // Update the latestUpdatedAt for the config
103
+ OffsetDateTime latest = earliest.plus(task.getReportDuration().get(), ChronoUnit.MILLIS);
104
+ task.setLatestUpdatedAt(Optional.of(Date.from(latest.toInstant())));
105
+ }
106
+
107
+ // latest_updated_at is required calculate time range
108
+ if (!task.getLatestUpdatedAt().isPresent()) {
109
+ throw new ConfigException("`latest_updated_at` is required when query by Date Range");
110
+ }
111
+
112
+ OffsetDateTime latest = OffsetDateTime.ofInstant(task.getLatestUpdatedAt().get().toInstant(), ZoneOffset.UTC);
113
+ if (earliest.isAfter(OffsetDateTime.now(ZoneOffset.UTC))) {
114
+ throw new ConfigException(String.format("`earliest_updated_at` (%s) cannot precede the current date (%s)",
115
+ earliest.format(DATE_FORMATTER),
116
+ (OffsetDateTime.now(ZoneOffset.UTC).format(DATE_FORMATTER))));
117
+ }
118
+
119
+ if (earliest.isAfter(latest)) {
120
+ throw new ConfigException(String.format("Invalid date range. `earliest_updated_at` (%s) cannot precede the `latest_updated_at` (%s).",
121
+ earliest.format(DATE_FORMATTER),
122
+ latest.format(DATE_FORMATTER)));
123
+ }
124
+ // if filter type is selected, filter value must be presented
125
+ if (task.getFilterType().isPresent() && (!task.getFilterValues().isPresent() || task.getFilterValues().get().isEmpty())) {
126
+ throw new ConfigException("filter_value is required when selected filter_type");
127
+ }
128
+ }
129
+ }
130
+ }
131
+
132
+ @Override
133
+ public TaskReport ingestServiceData(PluginTask task, RecordImporter recordImporter, int taskIndex, PageBuilder pageBuilder)
134
+ {
135
+ // query by date range and incremental import and not preview
136
+ if (task.getQueryBy().isPresent() && task.getQueryBy().get() == QueryBy.DATE_RANGE && task.getIncremental() && !Exec.isPreview()) {
137
+ OffsetDateTime latestUpdateAt = OffsetDateTime.ofInstant(task.getLatestUpdatedAt().get().toInstant(), ZoneOffset.UTC);
138
+ OffsetDateTime now = OffsetDateTime.now(ZoneOffset.UTC);
139
+ // Do not run incremental import if latest_updated_at precede current time
140
+ if (latestUpdateAt.isAfter(now)) {
141
+ logger.warn("`latest_updated_at` ({}) preceded current time ({}). Will try to import next run",
142
+ latestUpdateAt.format(DATE_FORMATTER), now.format(DATE_FORMATTER));
143
+
144
+ OffsetDateTime earliest = OffsetDateTime.ofInstant(task.getEarliestUpdatedAt().get().toInstant(), ZoneOffset.UTC);
145
+ TaskReport taskReport = Exec.newTaskReport();
146
+ taskReport.set("earliest_updated_at", earliest.format(DATE_FORMATTER));
147
+ if (task.getReportDuration().isPresent()) {
148
+ taskReport.set("report_duration", task.getReportDuration().get());
149
+ }
150
+ return taskReport;
151
+ }
152
+ }
153
+ return super.ingestServiceData(task, recordImporter, taskIndex, pageBuilder);
154
+ }
155
+
156
+ @Override
157
+ protected Iterator<ServiceRecord> getServiceRecords(MarketoService marketoService, PluginTask task)
158
+ {
159
+ Iterable<ObjectNode> nodes = null;
160
+ if (task.getQueryBy().isPresent()) {
161
+ switch (task.getQueryBy().get()) {
162
+ case TAG_TYPE:
163
+ nodes = marketoService.getProgramsByTag(task.getTagType().get(), task.getTagVallue().get());
164
+ break;
165
+ case DATE_RANGE:
166
+ nodes = marketoService.getProgramsByDateRange(task.getEarliestUpdatedAt().get(),
167
+ task.getLatestUpdatedAt().get(),
168
+ task.getFilterType().orNull(),
169
+ task.getFilterValues().orNull());
170
+ }
171
+ }
172
+ else {
173
+ nodes = marketoService.getPrograms();
174
+ }
175
+ return FluentIterable.from(nodes).transform(MarketoUtils.TRANSFORM_OBJECT_TO_JACKSON_SERVICE_RECORD_FUNCTION).iterator();
176
+ }
177
+
178
+ @Override
179
+ public ConfigDiff buildConfigDiff(PluginTask task, Schema schema, int taskCount, List<TaskReport> taskReports)
180
+ {
181
+ ConfigDiff configDiff = super.buildConfigDiff(task, schema, taskCount, taskReports);
182
+ // set next next earliestUpdatedAt, latestUpdatedAt
183
+ if (task.getQueryBy().isPresent() && task.getQueryBy().get() == QueryBy.DATE_RANGE && task.getIncremental()) {
184
+ OffsetDateTime earliest = task.getEarliestUpdatedAt().isPresent() ?
185
+ OffsetDateTime.ofInstant(task.getEarliestUpdatedAt().get().toInstant(), ZoneOffset.UTC) :
186
+ OffsetDateTime.now(ZoneOffset.UTC);
187
+ OffsetDateTime latest = task.getLatestUpdatedAt().isPresent() ?
188
+ OffsetDateTime.ofInstant(task.getLatestUpdatedAt().get().toInstant(), ZoneOffset.UTC) :
189
+ OffsetDateTime.now(ZoneOffset.UTC);
190
+
191
+ Duration d = Duration.between(earliest, latest);
192
+ OffsetDateTime nextEarliestUpdatedAt = latest.plusSeconds(1);
193
+
194
+ configDiff.set("earliest_updated_at", nextEarliestUpdatedAt.format(DATE_FORMATTER));
195
+ configDiff.set("report_duration", task.getReportDuration().or(d.toMillis()));
196
+ }
197
+ return configDiff;
198
+ }
199
+
200
+ @Override
201
+ public ServiceResponseMapper<? extends ValueLocator> buildServiceResponseMapper(PluginTask task)
202
+ {
203
+ JacksonServiceResponseMapper.Builder builder = JacksonServiceResponseMapper.builder();
204
+ builder.add("id", Types.LONG)
205
+ .add("name", Types.STRING)
206
+ .add("sfdcId", Types.STRING)
207
+ .add("sfdcName", Types.STRING)
208
+ .add("description", Types.STRING)
209
+ .add("createdAt", Types.TIMESTAMP, MarketoUtils.MARKETO_DATE_TIME_FORMAT)
210
+ .add("updatedAt", Types.TIMESTAMP, MarketoUtils.MARKETO_DATE_TIME_FORMAT)
211
+ .add("startDate", Types.TIMESTAMP, MarketoUtils.MARKETO_DATE_TIME_FORMAT)
212
+ .add("endDate", Types.TIMESTAMP, MarketoUtils.MARKETO_DATE_TIME_FORMAT)
213
+ .add("url", Types.STRING)
214
+ .add("type", Types.STRING)
215
+ .add("channel", Types.STRING)
216
+ .add("folder", Types.JSON)
217
+ .add("status", Types.STRING)
218
+ .add("costs", Types.JSON)
219
+ .add("tags", Types.JSON)
220
+ .add("workspace", Types.STRING);
221
+ return builder.build();
222
+ }
223
+
224
+ public enum QueryBy {
225
+ TAG_TYPE,
226
+ DATE_RANGE;
227
+
228
+ @JsonCreator
229
+ public static QueryBy of(String value)
230
+ {
231
+ return QueryBy.valueOf(value.toUpperCase());
232
+ }
233
+ }
234
+ }
@@ -0,0 +1,30 @@
1
+ package org.embulk.input.marketo.exception;
2
+
3
+ import org.embulk.input.marketo.model.MarketoError;
4
+
5
+ import java.util.List;
6
+
7
+ /**
8
+ * Exception class for all API Exception
9
+ * Created by tai.khuu on 9/5/17.
10
+ */
11
+ public class MarketoAPIException extends Exception
12
+ {
13
+ private final List<MarketoError> marketoErrors;
14
+ public MarketoAPIException(List<MarketoError> marketoErrors)
15
+ {
16
+ this.marketoErrors = marketoErrors;
17
+ }
18
+
19
+ public List<MarketoError> getMarketoErrors()
20
+ {
21
+ return marketoErrors;
22
+ }
23
+
24
+ @Override
25
+ public String getMessage()
26
+ {
27
+ MarketoError error = getMarketoErrors().get(0);
28
+ return "Marketo API Error, code: " + error.getCode() + ", message: " + error.getMessage();
29
+ }
30
+ }
@@ -0,0 +1,26 @@
1
+ package org.embulk.input.marketo.model;
2
+
3
+ /**
4
+ * Created by tai.khuu on 10/12/17.
5
+ */
6
+ public class BulkExtractRangeHeader
7
+ {
8
+ private Long start;
9
+ private Long end;
10
+
11
+ public BulkExtractRangeHeader(long start)
12
+ {
13
+ this.start = start;
14
+ }
15
+
16
+ public BulkExtractRangeHeader(long start, long end)
17
+ {
18
+ this.start = start;
19
+ this.end = end;
20
+ }
21
+
22
+ public String toRangeHeaderValue()
23
+ {
24
+ return "bytes=" + start + "-" + (end != null ? end : "");
25
+ }
26
+ }