embulk-output-mailchimp 0.3.28 → 0.3.31
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
-
}
|