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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b429373deb2a79bd444d223f70818c7a7517bd63
4
- data.tar.gz: 06fb08e83602804217e386754e39cd7cb542c2e8
3
+ metadata.gz: 37dfc8c615f96c0c1652b421bb1b716fbf44e578
4
+ data.tar.gz: 563dfbcf427399d4d78b4f1fd277d5eb8a437835
5
5
  SHA512:
6
- metadata.gz: b096372c93fcb6a0c2930d0b98ade33a1d52277f65fd1554e6d80178ee2179b300847c69c907d4e2cbb1279f7028e6eac3804d123f8e92681e1955182df5bc22
7
- data.tar.gz: efd31474670848d2457b2fdc5ea68b990902971543e884dd473fcb57064b34687a79beb95188bc06349e5448cac1a4919b91cadc90083a0fe39cca32d09c5c60
6
+ metadata.gz: 95ecae408533dbea06c19215d01e6a77842f5ee23a5948768352fed63470fea2ae486fc3c037c2a4bda6fe20b0d71144bc0eb087f7643768c80c0057ab59a758
7
+ data.tar.gz: c577620282aeb24d5c3c4bed48ded23338ae4f271637bd6b247a39c536c3a83ef7b3168db49fc734f15bdd879c1cc3ea0ec76dd04addd5b0e377170d8f528099
@@ -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
@@ -18,7 +18,7 @@ configurations {
18
18
  provided
19
19
  }
20
20
 
21
- version = "0.3.28"
21
+ version = "0.3.31"
22
22
 
23
23
  sourceCompatibility = 1.7
24
24
  targetCompatibility = 1.7
@@ -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.CategoriesResponse;
17
+ import org.embulk.output.mailchimp.model.Category;
20
18
  import org.embulk.output.mailchimp.model.ErrorResponse;
21
- import org.embulk.output.mailchimp.model.InterestCategoriesResponse;
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(MessageFormat.format("/lists/{0}", task.getListId()),
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(MessageFormat.format("`{0}` failed cause `{1}`\n",
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
- public Map<String, Map<String, InterestResponse>> extractInterestCategoriesByGroupNames(final PluginTask task)
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
- Map<String, Map<String, InterestResponse>> categories = new HashMap<>();
117
- if (task.getGroupingColumns().isPresent() && !task.getGroupingColumns().get().isEmpty()) {
118
- List<String> interestCategoryNames = task.getGroupingColumns().get();
119
-
120
- int count = 100;
121
- int offset = 0;
122
- int page = 1;
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
- for (CategoriesResponse categoriesResponse : allCategoriesResponse) {
168
- String detailPath = MessageFormat.format("/lists/{0}/interest-categories/{1}/interests",
169
- task.getListId(),
170
- categoriesResponse.getId());
171
- response = jsonParser.parseJsonObject(retryable.get(detailPath));
172
-
173
- // Avoid flood MailChimp API
174
- avoidFloodAPI("Fetching next category's interests", task.getSleepBetweenRequestsMillis());
175
- InterestsResponse interestsResponse = mapper.treeToValue(response, InterestsResponse.class);
176
- categories.put(categoriesResponse.getTitle().toLowerCase(),
177
- convertInterestCategoryToMap(interestsResponse.getInterests()));
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
- public Map<String, MergeField> extractMergeFieldsFromList(PluginTask task) throws JsonProcessingException
171
+ Map<String, MergeField> mergeFieldByTag(PluginTask task) throws JsonProcessingException
194
172
  {
195
- try (MailChimpRetryable retryable = new MailChimpRetryable(task)) {
196
- int count = 100;
197
- int offset = 0;
198
- int page = 1;
199
- boolean hasMore = true;
200
- List<MergeField> allMergeFields = new ArrayList<>();
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
- return convertMergeFieldToMap(allMergeFields);
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(MessageFormat.format("/lists/{0}",
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, InterestResponse> convertInterestCategoryToMap(final List<InterestResponse> interestResponseList)
235
+ private Map<String, Interest> convertInterestCategoryToMap(final List<Interest> interestList)
239
236
  {
240
- Function<InterestResponse, String> function = new Function<InterestResponse, String>()
237
+ Function<Interest, String> function = new Function<Interest, String>()
241
238
  {
242
239
  @Override
243
- public String apply(@Nullable InterestResponse input)
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(interestResponseList)
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("maximum_retries")
46
+ @Config("retry_limit")
43
47
  @ConfigDefault("6")
44
- int getMaximumRetries();
48
+ int getRetryLimit();
45
49
 
46
- @Config("initial_retry_interval_millis")
50
+ @Config("retry_initial_wait_msec")
47
51
  @ConfigDefault("1000")
48
- int getInitialRetryIntervalMillis();
52
+ int getRetryInitialWaitMSec();
49
53
 
50
- @Config("maximum_retry_interval_millis")
54
+ @Config("max_retry_wait_msec")
51
55
  @ConfigDefault("32000")
52
- int getMaximumRetryIntervalMillis();
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.InterestResponse;
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, InterestResponse>> categories;
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.extractInterestCategoriesByGroupNames(task);
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.extractMergeFieldsFromList(task);
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, InterestResponse> availableInterests = categories.get(category);
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.getMaximumRetries(),
48
- pluginTask.getInitialRetryIntervalMillis(),
49
- pluginTask.getMaximumRetryIntervalMillis(),
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 StringJetty92ResponseEntityReader(READER_TIMEOUT_MILLIS),
69
+ new PatchedStringJetty92ResponseEntityReader(READER_TIMEOUT_MILLIS),
71
70
  new Jetty92SingleRequester()
72
71
  {
73
72
  @Override
@@ -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
+ }
@@ -3,7 +3,7 @@ package org.embulk.output.mailchimp.model;
3
3
  /**
4
4
  * Created by thangnc on 5/5/17.
5
5
  */
6
- public class CategoriesResponse
6
+ public class Category
7
7
  {
8
8
  private String id;
9
9
  private String title;
@@ -3,7 +3,7 @@ package org.embulk.output.mailchimp.model;
3
3
  /**
4
4
  * Created by thangnc on 5/8/17.
5
5
  */
6
- public class InterestResponse
6
+ public class Interest
7
7
  {
8
8
  private String id;
9
9
  private String name;
@@ -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.28
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: 2018-10-29 00:00:00.000000000 Z
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/CategoriesResponse.java
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/InterestCategoriesResponse.java
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
- }