embulk-output-mailchimp 0.3.28 → 0.3.31
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 +4 -4
- data/CHANGELOG.md +13 -0
- data/build.gradle +1 -1
- data/src/main/java/org/embulk/output/mailchimp/MailChimpClient.java +113 -116
- data/src/main/java/org/embulk/output/mailchimp/MailChimpOutputPluginDelegate.java +20 -6
- data/src/main/java/org/embulk/output/mailchimp/MailChimpRecordBuffer.java +5 -33
- data/src/main/java/org/embulk/output/mailchimp/helper/MailChimpHelper.java +22 -0
- data/src/main/java/org/embulk/output/mailchimp/helper/MailChimpRetryable.java +4 -5
- data/src/main/java/org/embulk/output/mailchimp/helper/PatchedStringJetty92ResponseEntityReader.java +55 -0
- data/src/main/java/org/embulk/output/mailchimp/model/{CategoriesResponse.java → Category.java} +1 -1
- data/src/main/java/org/embulk/output/mailchimp/model/{InterestResponse.java → Interest.java} +1 -1
- data/src/test/java/org/embulk/output/mailchimp/TestMailChimpHelper.java +13 -0
- metadata +6 -7
- data/src/main/java/org/embulk/output/mailchimp/model/InterestCategoriesResponse.java +0 -37
- data/src/main/java/org/embulk/output/mailchimp/model/InterestsResponse.java +0 -24
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 37dfc8c615f96c0c1652b421bb1b716fbf44e578
|
4
|
+
data.tar.gz: 563dfbcf427399d4d78b4f1fd277d5eb8a437835
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 95ecae408533dbea06c19215d01e6a77842f5ee23a5948768352fed63470fea2ae486fc3c037c2a4bda6fe20b0d71144bc0eb087f7643768c80c0057ab59a758
|
7
|
+
data.tar.gz: c577620282aeb24d5c3c4bed48ded23338ae4f271637bd6b247a39c536c3a83ef7b3168db49fc734f15bdd879c1cc3ea0ec76dd04addd5b0e377170d8f528099
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,16 @@
|
|
1
|
+
## 0.3.31 - 2019-09-30
|
2
|
+
|
3
|
+
- Fix: retries always fail
|
4
|
+
- Fix: correct pagination
|
5
|
+
|
6
|
+
## 0.3.30 - 2019-01-23
|
7
|
+
|
8
|
+
- Renamed some configurations
|
9
|
+
|
10
|
+
## 0.3.29 - 2018-11-30
|
11
|
+
|
12
|
+
- Fix crashes when data schema doesn't contain all of the target Mailchimp list's groups
|
13
|
+
|
1
14
|
## 0.3.28 - 2018-10-29
|
2
15
|
|
3
16
|
- Quick fix a typo
|
data/build.gradle
CHANGED
@@ -3,12 +3,10 @@ package org.embulk.output.mailchimp;
|
|
3
3
|
import com.fasterxml.jackson.core.JsonParser;
|
4
4
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
5
5
|
import com.fasterxml.jackson.databind.DeserializationFeature;
|
6
|
-
import com.fasterxml.jackson.databind.JsonNode;
|
7
6
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
8
7
|
import com.fasterxml.jackson.databind.node.ObjectNode;
|
9
8
|
import com.google.common.base.Function;
|
10
9
|
import com.google.common.collect.FluentIterable;
|
11
|
-
import com.google.common.collect.ImmutableList;
|
12
10
|
import com.google.common.collect.Maps;
|
13
11
|
import org.eclipse.jetty.client.HttpResponseException;
|
14
12
|
import org.embulk.base.restclient.jackson.StringJsonParser;
|
@@ -16,26 +14,27 @@ import org.embulk.config.ConfigException;
|
|
16
14
|
import org.embulk.output.mailchimp.MailChimpOutputPluginDelegate.PluginTask;
|
17
15
|
import org.embulk.output.mailchimp.helper.MailChimpHelper;
|
18
16
|
import org.embulk.output.mailchimp.helper.MailChimpRetryable;
|
19
|
-
import org.embulk.output.mailchimp.model.
|
17
|
+
import org.embulk.output.mailchimp.model.Category;
|
20
18
|
import org.embulk.output.mailchimp.model.ErrorResponse;
|
21
|
-
import org.embulk.output.mailchimp.model.
|
22
|
-
import org.embulk.output.mailchimp.model.InterestResponse;
|
23
|
-
import org.embulk.output.mailchimp.model.InterestsResponse;
|
19
|
+
import org.embulk.output.mailchimp.model.Interest;
|
24
20
|
import org.embulk.output.mailchimp.model.MergeField;
|
25
|
-
import org.embulk.output.mailchimp.model.MergeFields;
|
26
21
|
import org.embulk.output.mailchimp.model.ReportResponse;
|
27
22
|
import org.embulk.spi.DataException;
|
28
23
|
import org.embulk.spi.Exec;
|
24
|
+
import org.embulk.spi.Schema;
|
29
25
|
import org.slf4j.Logger;
|
30
26
|
|
31
27
|
import javax.annotation.Nullable;
|
32
28
|
|
33
|
-
import java.text.MessageFormat;
|
34
29
|
import java.util.ArrayList;
|
30
|
+
import java.util.Collections;
|
35
31
|
import java.util.HashMap;
|
36
32
|
import java.util.List;
|
37
33
|
import java.util.Map;
|
38
34
|
|
35
|
+
import static java.text.MessageFormat.format;
|
36
|
+
import static java.util.Arrays.asList;
|
37
|
+
|
39
38
|
/**
|
40
39
|
* Created by thangnc on 4/25/17.
|
41
40
|
*/
|
@@ -68,7 +67,7 @@ public class MailChimpClient
|
|
68
67
|
public ReportResponse push(final ObjectNode node, PluginTask task) throws JsonProcessingException
|
69
68
|
{
|
70
69
|
try (MailChimpRetryable retryable = new MailChimpRetryable(task)) {
|
71
|
-
String response = retryable.post(
|
70
|
+
String response = retryable.post(format("/lists/{0}", task.getListId()),
|
72
71
|
"application/json;utf-8",
|
73
72
|
node.toString());
|
74
73
|
if (response != null && !response.isEmpty()) {
|
@@ -90,7 +89,7 @@ public class MailChimpClient
|
|
90
89
|
StringBuilder errorMessage = new StringBuilder();
|
91
90
|
|
92
91
|
for (ErrorResponse errorResponse : errorResponses) {
|
93
|
-
errorMessage.append(
|
92
|
+
errorMessage.append(format("`{0}` failed cause `{1}`\n",
|
94
93
|
MailChimpHelper.maskEmail(errorResponse.getEmailAddress()),
|
95
94
|
MailChimpHelper.maskEmail(errorResponse.getError())));
|
96
95
|
}
|
@@ -104,130 +103,128 @@ public class MailChimpClient
|
|
104
103
|
* Extract interest categories by group names. Loop via categories and fetch category details
|
105
104
|
* Reference: https://developer.mailchimp.com/documentation/mailchimp/reference/lists/interest-categories/#read-get_lists_list_id_interest_categories
|
106
105
|
* https://developer.mailchimp.com/documentation/mailchimp/reference/lists/interest-categories/#read-get_lists_list_id_interest_categories_interest_category_id
|
107
|
-
*
|
108
|
-
* @param task the task
|
109
|
-
* @return the map
|
110
|
-
* @throws JsonProcessingException the json processing exception
|
111
106
|
*/
|
112
|
-
|
113
|
-
throws JsonProcessingException
|
107
|
+
Map<String, Map<String, Interest>> interestsByCategory(final PluginTask task, Schema schema) throws JsonProcessingException
|
114
108
|
{
|
109
|
+
if (!task.getGroupingColumns().isPresent() || task.getGroupingColumns().get().isEmpty()) {
|
110
|
+
return Collections.emptyMap();
|
111
|
+
}
|
115
112
|
try (MailChimpRetryable retryable = new MailChimpRetryable(task)) {
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
boolean hasMore = true;
|
124
|
-
JsonNode response;
|
125
|
-
List<CategoriesResponse> allCategoriesResponse = new ArrayList<>();
|
126
|
-
|
127
|
-
while (hasMore) {
|
128
|
-
String path = MessageFormat.format("/lists/{0}/interest-categories?count={1}&offset={2}",
|
129
|
-
task.getListId(),
|
130
|
-
count,
|
131
|
-
offset);
|
132
|
-
response = jsonParser.parseJsonObject(retryable.get(path));
|
133
|
-
InterestCategoriesResponse interestCategoriesResponse = mapper.treeToValue(response,
|
134
|
-
InterestCategoriesResponse.class);
|
135
|
-
|
136
|
-
allCategoriesResponse.addAll(interestCategoriesResponse.getCategories());
|
137
|
-
if (hasMorePage(interestCategoriesResponse.getTotalItems(), count, page)) {
|
138
|
-
offset = count;
|
139
|
-
page++;
|
140
|
-
}
|
141
|
-
else {
|
142
|
-
hasMore = false;
|
143
|
-
}
|
144
|
-
}
|
145
|
-
|
146
|
-
Function<CategoriesResponse, String> function = new Function<CategoriesResponse, String>()
|
147
|
-
{
|
148
|
-
@Override
|
149
|
-
public String apply(CategoriesResponse input)
|
150
|
-
{
|
151
|
-
return input.getTitle().toLowerCase();
|
152
|
-
}
|
153
|
-
};
|
154
|
-
|
155
|
-
// Transform to a list of available category names and validate with data that user input
|
156
|
-
ImmutableList<String> availableCategories = FluentIterable
|
157
|
-
.from(allCategoriesResponse)
|
158
|
-
.transform(function)
|
159
|
-
.toList();
|
160
|
-
|
161
|
-
for (String category : interestCategoryNames) {
|
162
|
-
if (!availableCategories.contains(category)) {
|
163
|
-
throw new ConfigException("Invalid interest category name: '" + category + "'");
|
164
|
-
}
|
113
|
+
List<Category> categories = fetchCategories(retryable, task.getListId(), task.getGroupingColumns().get());
|
114
|
+
Map<String, Map<String, Interest>> interestsByCategory = new HashMap<>();
|
115
|
+
for (Category category : categories) {
|
116
|
+
// Skip fetching interests if this category isn't specified in the task's grouping column.
|
117
|
+
// Assume task's grouping columns are always in lower case
|
118
|
+
if (!task.getGroupingColumns().get().contains(category.getTitle().toLowerCase())) {
|
119
|
+
continue;
|
165
120
|
}
|
121
|
+
avoidFloodAPI("Fetching next category's interests", task.getSleepBetweenRequestsMillis());
|
122
|
+
interestsByCategory.put(
|
123
|
+
category.getTitle().toLowerCase(),
|
124
|
+
convertInterestCategoryToMap(fetchInterests(retryable, task.getListId(), category.getId())));
|
125
|
+
}
|
126
|
+
return interestsByCategory;
|
127
|
+
}
|
128
|
+
}
|
166
129
|
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
130
|
+
/**
|
131
|
+
* @throws ConfigException if task having unexist category
|
132
|
+
*/
|
133
|
+
private List<Category> fetchCategories(MailChimpRetryable retryable,
|
134
|
+
String listId,
|
135
|
+
List<String> taskCategories)
|
136
|
+
throws JsonProcessingException
|
137
|
+
{
|
138
|
+
List<Category> categories = fetch(
|
139
|
+
retryable,
|
140
|
+
"/lists/" + listId + "/interest-categories",
|
141
|
+
"categories",
|
142
|
+
Category[].class);
|
143
|
+
// Fail early if one of the task's categories is not exist
|
144
|
+
taskCategories:
|
145
|
+
for (String taskCategoryName : taskCategories) {
|
146
|
+
for (Category category : categories) {
|
147
|
+
// Tasks's configured categories are (implicitly?) case-sensitive
|
148
|
+
if (category.getTitle().toLowerCase().equals(taskCategoryName)) {
|
149
|
+
continue taskCategories;
|
178
150
|
}
|
179
151
|
}
|
180
|
-
|
181
|
-
return categories;
|
152
|
+
throw new ConfigException("Invalid group category name: '" + taskCategoryName + "'");
|
182
153
|
}
|
154
|
+
return categories;
|
183
155
|
}
|
184
156
|
|
157
|
+
private List<Interest> fetchInterests(MailChimpRetryable retryable,
|
158
|
+
String listId,
|
159
|
+
String categoryId)
|
160
|
+
throws JsonProcessingException
|
161
|
+
{
|
162
|
+
return fetch(retryable,
|
163
|
+
"/lists/" + listId + "/interest-categories/" + categoryId + "/interests",
|
164
|
+
"interests",
|
165
|
+
Interest[].class);
|
166
|
+
}
|
185
167
|
/**
|
186
168
|
* Extract merge fields from the list, find correct merge fields from API and put into the map to use
|
187
169
|
* Reference: https://developer.mailchimp.com/documentation/mailchimp/reference/lists/merge-fields/#read-get_lists_list_id_merge_fields
|
188
|
-
*
|
189
|
-
* @param task the task
|
190
|
-
* @return the map
|
191
|
-
* @throws JsonProcessingException the json processing exception
|
192
170
|
*/
|
193
|
-
|
171
|
+
Map<String, MergeField> mergeFieldByTag(PluginTask task) throws JsonProcessingException
|
194
172
|
{
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
while (hasMore) {
|
203
|
-
String path = MessageFormat.format("/lists/{0}/merge-fields?count={1}&offset={2}",
|
204
|
-
task.getListId(),
|
205
|
-
count,
|
206
|
-
offset);
|
207
|
-
|
208
|
-
JsonNode response = jsonParser.parseJsonObject(retryable.get(path));
|
209
|
-
MergeFields mergeFields = mapper.treeToValue(response,
|
210
|
-
MergeFields.class);
|
211
|
-
|
212
|
-
allMergeFields.addAll(mergeFields.getMergeFields());
|
213
|
-
|
214
|
-
if (hasMorePage(mergeFields.getTotalItems(), count, page)) {
|
215
|
-
offset = count;
|
216
|
-
page++;
|
217
|
-
}
|
218
|
-
else {
|
219
|
-
hasMore = false;
|
220
|
-
}
|
221
|
-
}
|
173
|
+
return convertMergeFieldToMap(
|
174
|
+
fetch(task,
|
175
|
+
"/lists/" + task.getListId() + "/merge-fields",
|
176
|
+
"merge_fields",
|
177
|
+
MergeField[].class));
|
178
|
+
}
|
222
179
|
|
223
|
-
|
180
|
+
/**
|
181
|
+
* Like {@link MailChimpClient#fetch(MailChimpRetryable, String, String, Class)},
|
182
|
+
* with an automatically created MailchimpRetryable
|
183
|
+
*/
|
184
|
+
private <T> List<T> fetch(PluginTask task,
|
185
|
+
String url,
|
186
|
+
String recordsAttribute,
|
187
|
+
Class<T[]> entitiesClass)
|
188
|
+
throws JsonProcessingException
|
189
|
+
{
|
190
|
+
try (MailChimpRetryable retryable = new MailChimpRetryable(task)) {
|
191
|
+
return fetch(retryable, url, recordsAttribute, entitiesClass);
|
192
|
+
}
|
193
|
+
}
|
194
|
+
/**
|
195
|
+
* Fetch all (by pagination) records at the target URL,
|
196
|
+
* Assume that endpoint handles `count` and `offset` parameter and have a response scheme of:
|
197
|
+
* {
|
198
|
+
* recordsAttribute}: [...],
|
199
|
+
* "total_items": 10
|
200
|
+
* }
|
201
|
+
* @param recordsAttribute name of the attribute to extract records inside the response's body.
|
202
|
+
* @param entitiesClass *Array* class of the entity to deserialize into
|
203
|
+
*/
|
204
|
+
private <T> List<T> fetch(MailChimpRetryable retryable,
|
205
|
+
String url,
|
206
|
+
String recordsAttribute,
|
207
|
+
Class<T[]> entitiesClass)
|
208
|
+
throws JsonProcessingException
|
209
|
+
{
|
210
|
+
final int batchSize = 100;
|
211
|
+
int offset = 0;
|
212
|
+
List<T> entities = new ArrayList<>();
|
213
|
+
ObjectNode response;
|
214
|
+
do {
|
215
|
+
response = jsonParser.parseJsonObject(
|
216
|
+
retryable.get(url + "?count=" + batchSize + "&offset=" + offset));
|
217
|
+
entities.addAll(asList(mapper.treeToValue(response.get(recordsAttribute), entitiesClass)));
|
218
|
+
offset += batchSize;
|
224
219
|
}
|
220
|
+
while (offset < response.get("total_items").asInt());
|
221
|
+
return entities;
|
225
222
|
}
|
226
223
|
|
227
224
|
private void findList(final PluginTask task)
|
228
225
|
{
|
229
226
|
try (MailChimpRetryable retryable = new MailChimpRetryable(task)) {
|
230
|
-
jsonParser.parseJsonObject(retryable.get(
|
227
|
+
jsonParser.parseJsonObject(retryable.get(format("/lists/{0}",
|
231
228
|
task.getListId())));
|
232
229
|
}
|
233
230
|
catch (HttpResponseException hre) {
|
@@ -235,18 +232,18 @@ public class MailChimpClient
|
|
235
232
|
}
|
236
233
|
}
|
237
234
|
|
238
|
-
private Map<String,
|
235
|
+
private Map<String, Interest> convertInterestCategoryToMap(final List<Interest> interestList)
|
239
236
|
{
|
240
|
-
Function<
|
237
|
+
Function<Interest, String> function = new Function<Interest, String>()
|
241
238
|
{
|
242
239
|
@Override
|
243
|
-
public String apply(@Nullable
|
240
|
+
public String apply(@Nullable Interest input)
|
244
241
|
{
|
245
242
|
return input.getName();
|
246
243
|
}
|
247
244
|
};
|
248
245
|
|
249
|
-
return Maps.uniqueIndex(FluentIterable.from(
|
246
|
+
return Maps.uniqueIndex(FluentIterable.from(interestList)
|
250
247
|
.toList(),
|
251
248
|
function);
|
252
249
|
}
|
@@ -19,9 +19,13 @@ import org.embulk.spi.Exec;
|
|
19
19
|
import org.embulk.spi.Schema;
|
20
20
|
import org.slf4j.Logger;
|
21
21
|
|
22
|
+
import java.util.HashSet;
|
22
23
|
import java.util.List;
|
24
|
+
import java.util.Set;
|
23
25
|
|
26
|
+
import static com.google.common.base.Joiner.on;
|
24
27
|
import static com.google.common.base.Strings.isNullOrEmpty;
|
28
|
+
import static org.embulk.output.mailchimp.helper.MailChimpHelper.caseInsensitiveColumnNames;
|
25
29
|
import static org.embulk.output.mailchimp.validation.ColumnDataValidator.checkExistColumns;
|
26
30
|
|
27
31
|
/**
|
@@ -39,17 +43,17 @@ public class MailChimpOutputPluginDelegate
|
|
39
43
|
public interface PluginTask
|
40
44
|
extends RestClientOutputTaskBase
|
41
45
|
{
|
42
|
-
@Config("
|
46
|
+
@Config("retry_limit")
|
43
47
|
@ConfigDefault("6")
|
44
|
-
int
|
48
|
+
int getRetryLimit();
|
45
49
|
|
46
|
-
@Config("
|
50
|
+
@Config("retry_initial_wait_msec")
|
47
51
|
@ConfigDefault("1000")
|
48
|
-
int
|
52
|
+
int getRetryInitialWaitMSec();
|
49
53
|
|
50
|
-
@Config("
|
54
|
+
@Config("max_retry_wait_msec")
|
51
55
|
@ConfigDefault("32000")
|
52
|
-
int
|
56
|
+
int getMaxRetryWaitMSec();
|
53
57
|
|
54
58
|
@Config("timeout_millis")
|
55
59
|
@ConfigDefault("60000")
|
@@ -155,6 +159,16 @@ public class MailChimpOutputPluginDelegate
|
|
155
159
|
if (task.getAtomicUpsert()) {
|
156
160
|
LOG.info(" Treating upsert as atomic operation");
|
157
161
|
}
|
162
|
+
|
163
|
+
// Warn if schema doesn't have the task's grouping column (Group Category)
|
164
|
+
if (task.getGroupingColumns().isPresent()
|
165
|
+
&& !task.getGroupingColumns().get().isEmpty()) {
|
166
|
+
Set<String> categoryNames = new HashSet<>(task.getGroupingColumns().get());
|
167
|
+
categoryNames.removeAll(caseInsensitiveColumnNames(schema));
|
168
|
+
if (categoryNames.size() > 0) {
|
169
|
+
LOG.warn("Data schema doesn't contain the task's grouping column(s): {}", on(", ").join(categoryNames));
|
170
|
+
}
|
171
|
+
}
|
158
172
|
}
|
159
173
|
|
160
174
|
@Override
|
@@ -17,7 +17,7 @@ import org.embulk.base.restclient.record.RecordBuffer;
|
|
17
17
|
import org.embulk.base.restclient.record.ServiceRecord;
|
18
18
|
import org.embulk.config.TaskReport;
|
19
19
|
import org.embulk.output.mailchimp.model.AddressMergeFieldAttribute;
|
20
|
-
import org.embulk.output.mailchimp.model.
|
20
|
+
import org.embulk.output.mailchimp.model.Interest;
|
21
21
|
import org.embulk.output.mailchimp.model.MergeField;
|
22
22
|
import org.embulk.output.mailchimp.model.ReportResponse;
|
23
23
|
import org.embulk.spi.Column;
|
@@ -26,8 +26,6 @@ import org.embulk.spi.Exec;
|
|
26
26
|
import org.embulk.spi.Schema;
|
27
27
|
import org.slf4j.Logger;
|
28
28
|
|
29
|
-
import javax.annotation.Nullable;
|
30
|
-
|
31
29
|
import java.io.IOException;
|
32
30
|
import java.util.ArrayList;
|
33
31
|
import java.util.Arrays;
|
@@ -37,9 +35,7 @@ import java.util.List;
|
|
37
35
|
import java.util.Map;
|
38
36
|
import java.util.Set;
|
39
37
|
import java.util.TreeMap;
|
40
|
-
import java.util.TreeSet;
|
41
38
|
|
42
|
-
import static com.google.common.base.Joiner.on;
|
43
39
|
import static java.lang.String.CASE_INSENSITIVE_ORDER;
|
44
40
|
import static java.lang.String.format;
|
45
41
|
import static org.embulk.output.mailchimp.MailChimpOutputPluginDelegate.PluginTask;
|
@@ -65,7 +61,7 @@ public class MailChimpRecordBuffer
|
|
65
61
|
private int errorCount;
|
66
62
|
private long totalCount;
|
67
63
|
private List<JsonNode> records;
|
68
|
-
private Map<String, Map<String,
|
64
|
+
private Map<String, Map<String, Interest>> categories;
|
69
65
|
private Map<String, MergeField> availableMergeFields;
|
70
66
|
private List<JsonNode> uniqueRecords;
|
71
67
|
private List<JsonNode> duplicatedRecords;
|
@@ -173,19 +169,12 @@ public class MailChimpRecordBuffer
|
|
173
169
|
// Should loop the names and get the id of interest categories.
|
174
170
|
// The reason why we put categories validation here because we can not share data between instance.
|
175
171
|
if (categories == null) {
|
176
|
-
categories = mailChimpClient.
|
177
|
-
|
178
|
-
Set<String> categoriesNames = categories.keySet();
|
179
|
-
Set<String> columnNames = caseInsensitiveColumnNames();
|
180
|
-
if (!columnNames.containsAll(categoriesNames)) {
|
181
|
-
categoriesNames.removeAll(columnNames);
|
182
|
-
LOG.warn("Data column for category '{}' could not be found", on(", ").join(categoriesNames));
|
183
|
-
}
|
172
|
+
categories = mailChimpClient.interestsByCategory(task, schema);
|
184
173
|
}
|
185
174
|
|
186
175
|
// Extract merge fields detail
|
187
176
|
if (availableMergeFields == null) {
|
188
|
-
availableMergeFields = mailChimpClient.
|
177
|
+
availableMergeFields = mailChimpClient.mergeFieldByTag(task);
|
189
178
|
}
|
190
179
|
|
191
180
|
// Required merge fields
|
@@ -288,7 +277,7 @@ public class MailChimpRecordBuffer
|
|
288
277
|
}
|
289
278
|
List<String> recordInterests = fromCommaSeparatedString(inputValue.get().asText());
|
290
279
|
// `categories` is guaranteed to contain the `category` as it already did an early check
|
291
|
-
Map<String,
|
280
|
+
Map<String, Interest> availableInterests = categories.get(category);
|
292
281
|
|
293
282
|
// Only update user-predefined categories if replace interests != true
|
294
283
|
if (!task.getReplaceInterests()) {
|
@@ -359,21 +348,4 @@ public class MailChimpRecordBuffer
|
|
359
348
|
}
|
360
349
|
}
|
361
350
|
}
|
362
|
-
|
363
|
-
private Set<String> caseInsensitiveColumnNames()
|
364
|
-
{
|
365
|
-
Set<String> columns = new TreeSet<>(CASE_INSENSITIVE_ORDER);
|
366
|
-
columns.addAll(FluentIterable
|
367
|
-
.from(schema.getColumns())
|
368
|
-
.transform(new Function<Column, String>() {
|
369
|
-
@Nullable
|
370
|
-
@Override
|
371
|
-
public String apply(@Nullable Column col)
|
372
|
-
{
|
373
|
-
return col.getName();
|
374
|
-
}
|
375
|
-
})
|
376
|
-
.toSet());
|
377
|
-
return columns;
|
378
|
-
}
|
379
351
|
}
|
@@ -9,17 +9,23 @@ import com.fasterxml.jackson.databind.node.ObjectNode;
|
|
9
9
|
import com.google.common.base.Function;
|
10
10
|
import com.google.common.base.Optional;
|
11
11
|
import com.google.common.base.Splitter;
|
12
|
+
import com.google.common.collect.FluentIterable;
|
12
13
|
import com.google.common.collect.Lists;
|
13
14
|
import com.google.common.collect.Multimap;
|
14
15
|
import com.google.common.collect.Multimaps;
|
15
16
|
import org.embulk.output.mailchimp.model.AddressMergeFieldAttribute;
|
17
|
+
import org.embulk.spi.Column;
|
18
|
+
import org.embulk.spi.Schema;
|
16
19
|
|
17
20
|
import javax.annotation.Nullable;
|
18
21
|
|
19
22
|
import java.io.IOException;
|
20
23
|
import java.util.Iterator;
|
21
24
|
import java.util.List;
|
25
|
+
import java.util.Set;
|
26
|
+
import java.util.TreeSet;
|
22
27
|
|
28
|
+
import static java.lang.String.CASE_INSENSITIVE_ORDER;
|
23
29
|
import static java.util.Objects.requireNonNull;
|
24
30
|
|
25
31
|
/**
|
@@ -142,4 +148,20 @@ public final class MailChimpHelper
|
|
142
148
|
}
|
143
149
|
return Optional.absent();
|
144
150
|
}
|
151
|
+
|
152
|
+
public static Set<String> caseInsensitiveColumnNames(Schema schema)
|
153
|
+
{
|
154
|
+
Set<String> columns = new TreeSet<>(CASE_INSENSITIVE_ORDER);
|
155
|
+
columns.addAll(FluentIterable
|
156
|
+
.from(schema.getColumns())
|
157
|
+
.transform(new Function<Column, String>() {
|
158
|
+
@Override
|
159
|
+
public String apply(Column col)
|
160
|
+
{
|
161
|
+
return col.getName();
|
162
|
+
}
|
163
|
+
})
|
164
|
+
.toSet());
|
165
|
+
return columns;
|
166
|
+
}
|
145
167
|
}
|
@@ -17,7 +17,6 @@ import org.embulk.spi.Exec;
|
|
17
17
|
import org.embulk.util.retryhelper.jetty92.DefaultJetty92ClientCreator;
|
18
18
|
import org.embulk.util.retryhelper.jetty92.Jetty92RetryHelper;
|
19
19
|
import org.embulk.util.retryhelper.jetty92.Jetty92SingleRequester;
|
20
|
-
import org.embulk.util.retryhelper.jetty92.StringJetty92ResponseEntityReader;
|
21
20
|
import org.slf4j.Logger;
|
22
21
|
|
23
22
|
import java.text.MessageFormat;
|
@@ -44,9 +43,9 @@ public class MailChimpRetryable implements AutoCloseable
|
|
44
43
|
|
45
44
|
public MailChimpRetryable(final PluginTask pluginTask)
|
46
45
|
{
|
47
|
-
this.retryHelper = new Jetty92RetryHelper(pluginTask.
|
48
|
-
pluginTask.
|
49
|
-
pluginTask.
|
46
|
+
this.retryHelper = new Jetty92RetryHelper(pluginTask.getRetryLimit(),
|
47
|
+
pluginTask.getRetryInitialWaitMSec(),
|
48
|
+
pluginTask.getMaxRetryWaitMSec(),
|
50
49
|
new DefaultJetty92ClientCreator(pluginTask.getTimeoutMillis(),
|
51
50
|
pluginTask.getTimeoutMillis()));
|
52
51
|
this.pluginTask = pluginTask;
|
@@ -67,7 +66,7 @@ public class MailChimpRetryable implements AutoCloseable
|
|
67
66
|
{
|
68
67
|
try {
|
69
68
|
return retryHelper.requestWithRetry(
|
70
|
-
new
|
69
|
+
new PatchedStringJetty92ResponseEntityReader(READER_TIMEOUT_MILLIS),
|
71
70
|
new Jetty92SingleRequester()
|
72
71
|
{
|
73
72
|
@Override
|
data/src/main/java/org/embulk/output/mailchimp/helper/PatchedStringJetty92ResponseEntityReader.java
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
package org.embulk.output.mailchimp.helper;
|
2
|
+
|
3
|
+
import com.google.common.io.CharStreams;
|
4
|
+
import org.eclipse.jetty.client.api.Response;
|
5
|
+
import org.eclipse.jetty.client.util.InputStreamResponseListener;
|
6
|
+
import org.embulk.util.retryhelper.jetty92.Jetty92ResponseReader;
|
7
|
+
|
8
|
+
import java.io.InputStream;
|
9
|
+
import java.io.InputStreamReader;
|
10
|
+
import java.util.concurrent.TimeUnit;
|
11
|
+
|
12
|
+
/**
|
13
|
+
* A copy of {@link org.embulk.util.retryhelper.jetty92.StringJetty92ResponseEntityReader} with the only
|
14
|
+
* modification is {@link PatchedStringJetty92ResponseEntityReader#getListener()} to return a new instance every time.
|
15
|
+
* This might eventually get fixed upstream (Jetty92RetryHelper aware of Jetty92ResponseReader is stateful),
|
16
|
+
*/
|
17
|
+
public class PatchedStringJetty92ResponseEntityReader implements Jetty92ResponseReader<String>
|
18
|
+
{
|
19
|
+
private InputStreamResponseListener listener;
|
20
|
+
private final long timeoutMillis;
|
21
|
+
|
22
|
+
public PatchedStringJetty92ResponseEntityReader(long timeoutMillis)
|
23
|
+
{
|
24
|
+
this.listener = new InputStreamResponseListener();
|
25
|
+
this.timeoutMillis = timeoutMillis;
|
26
|
+
}
|
27
|
+
|
28
|
+
@Override
|
29
|
+
public final Response.Listener getListener()
|
30
|
+
{
|
31
|
+
this.listener = new InputStreamResponseListener();
|
32
|
+
return this.listener;
|
33
|
+
}
|
34
|
+
|
35
|
+
@Override
|
36
|
+
public final Response getResponse() throws Exception
|
37
|
+
{
|
38
|
+
return this.listener.get(this.timeoutMillis, TimeUnit.MILLISECONDS);
|
39
|
+
}
|
40
|
+
|
41
|
+
@Override
|
42
|
+
public final String readResponseContent() throws Exception
|
43
|
+
{
|
44
|
+
final InputStream inputStream = this.listener.getInputStream();
|
45
|
+
try (InputStreamReader inputStreamReader = new InputStreamReader(inputStream)) {
|
46
|
+
return CharStreams.toString(inputStreamReader);
|
47
|
+
}
|
48
|
+
}
|
49
|
+
|
50
|
+
@Override
|
51
|
+
public final String readResponseContentInString() throws Exception
|
52
|
+
{
|
53
|
+
return this.readResponseContent();
|
54
|
+
}
|
55
|
+
}
|
@@ -4,15 +4,20 @@ import com.fasterxml.jackson.databind.JsonNode;
|
|
4
4
|
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
|
5
5
|
import com.fasterxml.jackson.databind.node.NullNode;
|
6
6
|
import com.fasterxml.jackson.databind.node.ObjectNode;
|
7
|
+
import com.google.common.collect.ImmutableList;
|
7
8
|
import com.google.common.collect.Multimap;
|
8
9
|
import org.embulk.output.mailchimp.helper.MailChimpHelper;
|
9
10
|
import org.embulk.output.mailchimp.model.AddressMergeFieldAttribute;
|
11
|
+
import org.embulk.spi.Column;
|
12
|
+
import org.embulk.spi.Schema;
|
13
|
+
import org.embulk.spi.type.Types;
|
10
14
|
import org.junit.Test;
|
11
15
|
|
12
16
|
import java.util.ArrayList;
|
13
17
|
import java.util.Arrays;
|
14
18
|
import java.util.List;
|
15
19
|
|
20
|
+
import static org.embulk.output.mailchimp.helper.MailChimpHelper.caseInsensitiveColumnNames;
|
16
21
|
import static org.embulk.output.mailchimp.helper.MailChimpHelper.containsCaseInsensitive;
|
17
22
|
import static org.embulk.output.mailchimp.helper.MailChimpHelper.extractMemberStatus;
|
18
23
|
import static org.embulk.output.mailchimp.helper.MailChimpHelper.maskEmail;
|
@@ -95,4 +100,12 @@ public class TestMailChimpHelper
|
|
95
100
|
assertEquals("Should be JSON", ObjectNode.class, orderJsonNode(toJsonNode(given), attributes).getClass());
|
96
101
|
assertEquals("Should be match", expect, orderJsonNode(toJsonNode(given), attributes).toString());
|
97
102
|
}
|
103
|
+
|
104
|
+
@Test
|
105
|
+
public void test_caseInsensitiveColumnNames()
|
106
|
+
{
|
107
|
+
Schema schema = new Schema(ImmutableList.of(
|
108
|
+
new Column(0, "InCONSisTENT", Types.LONG)));
|
109
|
+
assertTrue(caseInsensitiveColumnNames(schema).contains("inConsIstent"));
|
110
|
+
}
|
98
111
|
}
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: embulk-output-mailchimp
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.31
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Thang Nguyen
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2019-09-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
requirement: !ruby/object:Gem::Requirement
|
@@ -67,13 +67,12 @@ files:
|
|
67
67
|
- src/main/java/org/embulk/output/mailchimp/MailChimpRecordBuffer.java
|
68
68
|
- src/main/java/org/embulk/output/mailchimp/helper/MailChimpHelper.java
|
69
69
|
- src/main/java/org/embulk/output/mailchimp/helper/MailChimpRetryable.java
|
70
|
+
- src/main/java/org/embulk/output/mailchimp/helper/PatchedStringJetty92ResponseEntityReader.java
|
70
71
|
- src/main/java/org/embulk/output/mailchimp/model/AddressMergeFieldAttribute.java
|
71
72
|
- src/main/java/org/embulk/output/mailchimp/model/AuthMethod.java
|
72
|
-
- src/main/java/org/embulk/output/mailchimp/model/
|
73
|
+
- src/main/java/org/embulk/output/mailchimp/model/Category.java
|
73
74
|
- src/main/java/org/embulk/output/mailchimp/model/ErrorResponse.java
|
74
|
-
- src/main/java/org/embulk/output/mailchimp/model/
|
75
|
-
- src/main/java/org/embulk/output/mailchimp/model/InterestResponse.java
|
76
|
-
- src/main/java/org/embulk/output/mailchimp/model/InterestsResponse.java
|
75
|
+
- src/main/java/org/embulk/output/mailchimp/model/Interest.java
|
77
76
|
- src/main/java/org/embulk/output/mailchimp/model/MemberStatus.java
|
78
77
|
- src/main/java/org/embulk/output/mailchimp/model/MergeField.java
|
79
78
|
- src/main/java/org/embulk/output/mailchimp/model/MergeFields.java
|
@@ -89,12 +88,12 @@ files:
|
|
89
88
|
- test/override_assert_raise.rb
|
90
89
|
- test/run-test.rb
|
91
90
|
- classpath/jetty-io-9.2.14.v20151106.jar
|
91
|
+
- classpath/embulk-output-mailchimp-0.3.31.jar
|
92
92
|
- classpath/jetty-util-9.2.14.v20151106.jar
|
93
93
|
- classpath/jetty-http-9.2.14.v20151106.jar
|
94
94
|
- classpath/jetty-client-9.2.14.v20151106.jar
|
95
95
|
- classpath/embulk-base-restclient-0.5.3.jar
|
96
96
|
- classpath/embulk-util-retryhelper-jetty92-0.5.3.jar
|
97
|
-
- classpath/embulk-output-mailchimp-0.3.28.jar
|
98
97
|
homepage: https://github.com/treasure-data/embulk-output-mailchimp
|
99
98
|
licenses:
|
100
99
|
- MIT
|
@@ -1,37 +0,0 @@
|
|
1
|
-
package org.embulk.output.mailchimp.model;
|
2
|
-
|
3
|
-
import com.fasterxml.jackson.annotation.JsonProperty;
|
4
|
-
|
5
|
-
import java.util.List;
|
6
|
-
|
7
|
-
/**
|
8
|
-
* Created by thangnc on 5/8/17.
|
9
|
-
*/
|
10
|
-
public class InterestCategoriesResponse
|
11
|
-
{
|
12
|
-
@JsonProperty("categories")
|
13
|
-
private List<CategoriesResponse> categories;
|
14
|
-
|
15
|
-
@JsonProperty("total_items")
|
16
|
-
private int totalItems;
|
17
|
-
|
18
|
-
public List<CategoriesResponse> getCategories()
|
19
|
-
{
|
20
|
-
return categories;
|
21
|
-
}
|
22
|
-
|
23
|
-
public void setCategories(List<CategoriesResponse> categories)
|
24
|
-
{
|
25
|
-
this.categories = categories;
|
26
|
-
}
|
27
|
-
|
28
|
-
public int getTotalItems()
|
29
|
-
{
|
30
|
-
return totalItems;
|
31
|
-
}
|
32
|
-
|
33
|
-
public void setTotalItems(int totalItems)
|
34
|
-
{
|
35
|
-
this.totalItems = totalItems;
|
36
|
-
}
|
37
|
-
}
|
@@ -1,24 +0,0 @@
|
|
1
|
-
package org.embulk.output.mailchimp.model;
|
2
|
-
|
3
|
-
import com.fasterxml.jackson.annotation.JsonProperty;
|
4
|
-
|
5
|
-
import java.util.List;
|
6
|
-
|
7
|
-
/**
|
8
|
-
* Created by thangnc on 5/8/17.
|
9
|
-
*/
|
10
|
-
public class InterestsResponse
|
11
|
-
{
|
12
|
-
@JsonProperty("interests")
|
13
|
-
private List<InterestResponse> interests;
|
14
|
-
|
15
|
-
public List<InterestResponse> getInterests()
|
16
|
-
{
|
17
|
-
return interests;
|
18
|
-
}
|
19
|
-
|
20
|
-
public void setInterests(List<InterestResponse> interests)
|
21
|
-
{
|
22
|
-
this.interests = interests;
|
23
|
-
}
|
24
|
-
}
|