embulk-output-mailchimp 0.3.13 → 0.3.14
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 +3 -0
- data/build.gradle +1 -1
- data/src/main/java/org/embulk/output/mailchimp/MailChimpClient.java +235 -0
- data/src/main/java/org/embulk/output/mailchimp/MailChimpRecordBuffer.java +209 -141
- metadata +4 -4
- data/src/main/java/org/embulk/output/mailchimp/MailChimpAbstractRecordBuffer.java +0 -338
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f89e9a5a69ae081a887e26d19c1a57695d265516
|
4
|
+
data.tar.gz: b3c53d62e62a9127520163a38b053a12b4be50f3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d27ebd29fa3ad09b9c942ef2b2276db918f7ab38c2ff8fb819ed4cbfe7ecbea3e4bd25c1926cef43ed317c2d431c030916f015f8c8aacd24f6a125360a8f0326
|
7
|
+
data.tar.gz: 376d7dadd0bec805f68d891930d0c1ebb60d618bbeb55519e90b80e92535826aa7e278b7c40d3d115c22a225aa56aed0ef46fff65ad1a01dae5d7022b57e1f6a
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,6 @@
|
|
1
|
+
## 0.3.14 - 2017-06-26
|
2
|
+
- Make clear the log message and renamed class `MailChimpClient` [#29](https://github.com/treasure-data/embulk-output-mailchimp/pull/29)
|
3
|
+
|
1
4
|
## 0.3.13 - 2017-06-16
|
2
5
|
- Upgraded `embulk-base-restclient` to v0.5.3 and fixed hang job [#28](https://github.com/treasure-data/embulk-output-mailchimp/pull/28)
|
3
6
|
|
data/build.gradle
CHANGED
@@ -0,0 +1,235 @@
|
|
1
|
+
package org.embulk.output.mailchimp;
|
2
|
+
|
3
|
+
import com.fasterxml.jackson.core.JsonParser;
|
4
|
+
import com.fasterxml.jackson.core.JsonProcessingException;
|
5
|
+
import com.fasterxml.jackson.databind.DeserializationFeature;
|
6
|
+
import com.fasterxml.jackson.databind.JsonNode;
|
7
|
+
import com.fasterxml.jackson.databind.ObjectMapper;
|
8
|
+
import com.fasterxml.jackson.databind.node.ObjectNode;
|
9
|
+
import com.google.common.base.Function;
|
10
|
+
import com.google.common.base.Joiner;
|
11
|
+
import com.google.common.collect.FluentIterable;
|
12
|
+
import com.google.common.collect.ImmutableList;
|
13
|
+
import com.google.common.collect.Maps;
|
14
|
+
import org.eclipse.jetty.http.HttpMethod;
|
15
|
+
import org.embulk.config.ConfigException;
|
16
|
+
import org.embulk.output.mailchimp.helper.MailChimpHelper;
|
17
|
+
import org.embulk.output.mailchimp.model.CategoriesResponse;
|
18
|
+
import org.embulk.output.mailchimp.model.ErrorResponse;
|
19
|
+
import org.embulk.output.mailchimp.model.InterestCategoriesResponse;
|
20
|
+
import org.embulk.output.mailchimp.model.InterestResponse;
|
21
|
+
import org.embulk.output.mailchimp.model.InterestsResponse;
|
22
|
+
import org.embulk.output.mailchimp.model.MergeField;
|
23
|
+
import org.embulk.output.mailchimp.model.MergeFields;
|
24
|
+
import org.embulk.output.mailchimp.model.MetaDataResponse;
|
25
|
+
import org.embulk.output.mailchimp.model.ReportResponse;
|
26
|
+
import org.embulk.spi.Exec;
|
27
|
+
import org.slf4j.Logger;
|
28
|
+
|
29
|
+
import javax.annotation.Nullable;
|
30
|
+
|
31
|
+
import java.text.MessageFormat;
|
32
|
+
import java.util.HashMap;
|
33
|
+
import java.util.List;
|
34
|
+
import java.util.Map;
|
35
|
+
|
36
|
+
import static org.embulk.output.mailchimp.model.AuthMethod.API_KEY;
|
37
|
+
import static org.embulk.output.mailchimp.model.AuthMethod.OAUTH;
|
38
|
+
|
39
|
+
/**
|
40
|
+
* Created by thangnc on 4/25/17.
|
41
|
+
*/
|
42
|
+
public class MailChimpClient
|
43
|
+
{
|
44
|
+
private static final Logger LOG = Exec.getLogger(MailChimpClient.class);
|
45
|
+
private static final String API_VERSION = "3.0";
|
46
|
+
private static String mailchimpEndpoint;
|
47
|
+
private MailChimpHttpClient client;
|
48
|
+
private final ObjectMapper mapper;
|
49
|
+
|
50
|
+
/**
|
51
|
+
* Instantiates a new Mail chimp client.
|
52
|
+
*
|
53
|
+
* @param task the task
|
54
|
+
*/
|
55
|
+
public MailChimpClient(final MailChimpOutputPluginDelegate.PluginTask task)
|
56
|
+
{
|
57
|
+
mailchimpEndpoint = Joiner.on("/").join("https://{0}.api.mailchimp.com", API_VERSION);
|
58
|
+
this.client = new MailChimpHttpClient(task);
|
59
|
+
this.mapper = new ObjectMapper()
|
60
|
+
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
|
61
|
+
.configure(JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS, false);
|
62
|
+
extractDataCenter(task);
|
63
|
+
}
|
64
|
+
|
65
|
+
/**
|
66
|
+
* Build an array of email subscribers and batch insert via bulk MailChimp API
|
67
|
+
* Reference: https://developer.mailchimp.com/documentation/mailchimp/reference/lists/#create-post_lists_list_id
|
68
|
+
*
|
69
|
+
* @param node the data
|
70
|
+
* @param task the task
|
71
|
+
* @return the report response
|
72
|
+
* @throws JsonProcessingException the json processing exception
|
73
|
+
*/
|
74
|
+
ReportResponse push(final ObjectNode node, MailChimpOutputPluginDelegate.PluginTask task)
|
75
|
+
throws JsonProcessingException
|
76
|
+
{
|
77
|
+
String endpoint = MessageFormat.format(mailchimpEndpoint + "/lists/{0}",
|
78
|
+
task.getListId());
|
79
|
+
|
80
|
+
JsonNode response = client.sendRequest(endpoint, HttpMethod.POST, node.toString(), task);
|
81
|
+
return mapper.treeToValue(response, ReportResponse.class);
|
82
|
+
}
|
83
|
+
|
84
|
+
/**
|
85
|
+
* Handle detail errors after call bulk MailChimp API
|
86
|
+
*
|
87
|
+
* @param errorResponses the error responses
|
88
|
+
*/
|
89
|
+
void handleErrors(List<ErrorResponse> errorResponses)
|
90
|
+
{
|
91
|
+
if (!errorResponses.isEmpty()) {
|
92
|
+
StringBuilder errorMessage = new StringBuilder();
|
93
|
+
|
94
|
+
for (ErrorResponse errorResponse : errorResponses) {
|
95
|
+
errorMessage.append(MessageFormat.format("`{0}` failed cause `{1}`\n",
|
96
|
+
MailChimpHelper.maskEmail(errorResponse.getEmailAddress()),
|
97
|
+
MailChimpHelper.maskEmail(errorResponse.getError())));
|
98
|
+
}
|
99
|
+
|
100
|
+
LOG.error("Error response from MailChimp: ");
|
101
|
+
LOG.error(errorMessage.toString());
|
102
|
+
}
|
103
|
+
}
|
104
|
+
|
105
|
+
/**
|
106
|
+
* Extract interest categories by group names. Loop via categories and fetch category details
|
107
|
+
* Reference: https://developer.mailchimp.com/documentation/mailchimp/reference/lists/interest-categories/#read-get_lists_list_id_interest_categories
|
108
|
+
* https://developer.mailchimp.com/documentation/mailchimp/reference/lists/interest-categories/#read-get_lists_list_id_interest_categories_interest_category_id
|
109
|
+
*
|
110
|
+
* @param task the task
|
111
|
+
* @return the map
|
112
|
+
* @throws JsonProcessingException the json processing exception
|
113
|
+
*/
|
114
|
+
Map<String, Map<String, InterestResponse>> extractInterestCategoriesByGroupNames(final MailChimpOutputPluginDelegate.PluginTask task)
|
115
|
+
throws JsonProcessingException
|
116
|
+
{
|
117
|
+
Map<String, Map<String, InterestResponse>> categories = new HashMap<>();
|
118
|
+
if (task.getGroupingColumns().isPresent() && !task.getGroupingColumns().get().isEmpty()) {
|
119
|
+
List<String> interestCategoryNames = task.getGroupingColumns().get();
|
120
|
+
|
121
|
+
String endpoint = MessageFormat.format(mailchimpEndpoint + "/lists/{0}/interest-categories",
|
122
|
+
task.getListId());
|
123
|
+
|
124
|
+
JsonNode response = client.sendRequest(endpoint, HttpMethod.GET, task);
|
125
|
+
InterestCategoriesResponse interestCategoriesResponse = mapper.treeToValue(response,
|
126
|
+
InterestCategoriesResponse.class);
|
127
|
+
|
128
|
+
Function<CategoriesResponse, String> function = new Function<CategoriesResponse, String>()
|
129
|
+
{
|
130
|
+
@Override
|
131
|
+
public String apply(CategoriesResponse input)
|
132
|
+
{
|
133
|
+
return input.getTitle().toLowerCase();
|
134
|
+
}
|
135
|
+
};
|
136
|
+
|
137
|
+
// Transform to a list of available category names and validate with data that user input
|
138
|
+
ImmutableList<String> availableCategories = FluentIterable
|
139
|
+
.from(interestCategoriesResponse.getCategories())
|
140
|
+
.transform(function)
|
141
|
+
.toList();
|
142
|
+
|
143
|
+
for (String category : interestCategoryNames) {
|
144
|
+
if (!availableCategories.contains(category)) {
|
145
|
+
throw new ConfigException("Invalid interest category name: '" + category + "'");
|
146
|
+
}
|
147
|
+
}
|
148
|
+
|
149
|
+
for (CategoriesResponse categoriesResponse : interestCategoriesResponse.getCategories()) {
|
150
|
+
String detailEndpoint = MessageFormat.format(mailchimpEndpoint + "/lists/{0}/interest-categories/{1}/interests",
|
151
|
+
task.getListId(),
|
152
|
+
categoriesResponse.getId());
|
153
|
+
response = client.sendRequest(detailEndpoint, HttpMethod.GET, task);
|
154
|
+
InterestsResponse interestsResponse = mapper.treeToValue(response, InterestsResponse.class);
|
155
|
+
categories.put(categoriesResponse.getTitle().toLowerCase(),
|
156
|
+
convertInterestCategoryToMap(interestsResponse.getInterests()));
|
157
|
+
}
|
158
|
+
}
|
159
|
+
|
160
|
+
return categories;
|
161
|
+
}
|
162
|
+
|
163
|
+
/**
|
164
|
+
* Extract merge fields from the list, find correct merge fields from API and put into the map to use
|
165
|
+
* Reference: https://developer.mailchimp.com/documentation/mailchimp/reference/lists/merge-fields/#read-get_lists_list_id_merge_fields
|
166
|
+
*
|
167
|
+
* @param task the task
|
168
|
+
* @return the map
|
169
|
+
* @throws JsonProcessingException the json processing exception
|
170
|
+
*/
|
171
|
+
Map<String, MergeField> extractMergeFieldsFromList(MailChimpOutputPluginDelegate.PluginTask task) throws JsonProcessingException
|
172
|
+
{
|
173
|
+
String endpoint = MessageFormat.format(mailchimpEndpoint + "/lists/{0}/merge-fields",
|
174
|
+
task.getListId());
|
175
|
+
JsonNode response = client.sendRequest(endpoint, HttpMethod.GET, task);
|
176
|
+
MergeFields mergeFields = mapper.treeToValue(response,
|
177
|
+
MergeFields.class);
|
178
|
+
return convertMergeFieldToMap(mergeFields.getMergeFields());
|
179
|
+
}
|
180
|
+
|
181
|
+
private void extractDataCenter(MailChimpOutputPluginDelegate.PluginTask task)
|
182
|
+
{
|
183
|
+
try {
|
184
|
+
if (task.getAuthMethod() == OAUTH) {
|
185
|
+
// Extract data center from meta data URL
|
186
|
+
JsonNode response = client.sendRequest("https://login.mailchimp.com/oauth2/metadata", HttpMethod.GET, task);
|
187
|
+
MetaDataResponse metaDataResponse = mapper.treeToValue(response, MetaDataResponse.class);
|
188
|
+
mailchimpEndpoint = MessageFormat.format(mailchimpEndpoint, metaDataResponse.getDc());
|
189
|
+
}
|
190
|
+
else if (task.getAuthMethod() == API_KEY && task.getApikey().isPresent()) {
|
191
|
+
// Authenticate and return data center
|
192
|
+
String domain = task.getApikey().get().split("-")[1];
|
193
|
+
String endpoint = MessageFormat.format(mailchimpEndpoint + "/", domain);
|
194
|
+
client.sendRequest(endpoint, HttpMethod.GET, task);
|
195
|
+
mailchimpEndpoint = MessageFormat.format(mailchimpEndpoint, domain);
|
196
|
+
}
|
197
|
+
}
|
198
|
+
catch (Exception e) {
|
199
|
+
throw new ConfigException("Could not get data center", e);
|
200
|
+
}
|
201
|
+
}
|
202
|
+
|
203
|
+
private Map<String, InterestResponse> convertInterestCategoryToMap(final List<InterestResponse> interestResponseList)
|
204
|
+
{
|
205
|
+
Function<InterestResponse, String> function = new Function<InterestResponse, String>()
|
206
|
+
{
|
207
|
+
@Override
|
208
|
+
public String apply(@Nullable InterestResponse input)
|
209
|
+
{
|
210
|
+
return input.getName();
|
211
|
+
}
|
212
|
+
};
|
213
|
+
|
214
|
+
return Maps.uniqueIndex(FluentIterable.from(interestResponseList)
|
215
|
+
.toList(),
|
216
|
+
function);
|
217
|
+
}
|
218
|
+
|
219
|
+
private Map<String, MergeField> convertMergeFieldToMap(final List<MergeField> mergeFieldList)
|
220
|
+
{
|
221
|
+
Function<MergeField, String> function = new Function<MergeField, String>()
|
222
|
+
{
|
223
|
+
@Nullable
|
224
|
+
@Override
|
225
|
+
public String apply(@Nullable MergeField input)
|
226
|
+
{
|
227
|
+
return input.getTag().toLowerCase();
|
228
|
+
}
|
229
|
+
};
|
230
|
+
|
231
|
+
return Maps.uniqueIndex(FluentIterable.from(mergeFieldList)
|
232
|
+
.toList(),
|
233
|
+
function);
|
234
|
+
}
|
235
|
+
}
|
@@ -1,208 +1,276 @@
|
|
1
1
|
package org.embulk.output.mailchimp;
|
2
2
|
|
3
|
+
import com.fasterxml.jackson.core.JsonParser;
|
3
4
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
5
|
+
import com.fasterxml.jackson.databind.DeserializationFeature;
|
4
6
|
import com.fasterxml.jackson.databind.JsonNode;
|
7
|
+
import com.fasterxml.jackson.databind.ObjectMapper;
|
8
|
+
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
|
9
|
+
import com.fasterxml.jackson.databind.node.NullNode;
|
5
10
|
import com.fasterxml.jackson.databind.node.ObjectNode;
|
6
11
|
import com.google.common.base.Function;
|
12
|
+
import com.google.common.base.Throwables;
|
7
13
|
import com.google.common.collect.FluentIterable;
|
8
|
-
import
|
9
|
-
import
|
10
|
-
import org.
|
11
|
-
import org.embulk.config.
|
12
|
-
import org.embulk.output.mailchimp.
|
13
|
-
import org.embulk.output.mailchimp.model.CategoriesResponse;
|
14
|
-
import org.embulk.output.mailchimp.model.ErrorResponse;
|
15
|
-
import org.embulk.output.mailchimp.model.InterestCategoriesResponse;
|
14
|
+
import org.embulk.base.restclient.jackson.JacksonServiceRecord;
|
15
|
+
import org.embulk.base.restclient.record.RecordBuffer;
|
16
|
+
import org.embulk.base.restclient.record.ServiceRecord;
|
17
|
+
import org.embulk.config.TaskReport;
|
18
|
+
import org.embulk.output.mailchimp.model.AddressMergeFieldAttribute;
|
16
19
|
import org.embulk.output.mailchimp.model.InterestResponse;
|
17
|
-
import org.embulk.output.mailchimp.model.InterestsResponse;
|
18
20
|
import org.embulk.output.mailchimp.model.MergeField;
|
19
|
-
import org.embulk.output.mailchimp.model.MergeFields;
|
20
|
-
import org.embulk.output.mailchimp.model.MetaDataResponse;
|
21
21
|
import org.embulk.output.mailchimp.model.ReportResponse;
|
22
|
+
import org.embulk.spi.Column;
|
23
|
+
import org.embulk.spi.DataException;
|
22
24
|
import org.embulk.spi.Exec;
|
23
25
|
import org.embulk.spi.Schema;
|
24
26
|
import org.slf4j.Logger;
|
25
27
|
|
26
|
-
import
|
27
|
-
|
28
|
-
import java.text.MessageFormat;
|
28
|
+
import java.io.IOException;
|
29
|
+
import java.util.ArrayList;
|
29
30
|
import java.util.HashMap;
|
30
31
|
import java.util.List;
|
31
32
|
import java.util.Map;
|
32
33
|
|
33
|
-
import static org.embulk.output.mailchimp.
|
34
|
-
import static org.embulk.output.mailchimp.
|
34
|
+
import static org.embulk.output.mailchimp.MailChimpOutputPluginDelegate.PluginTask;
|
35
|
+
import static org.embulk.output.mailchimp.helper.MailChimpHelper.containsCaseInsensitive;
|
36
|
+
import static org.embulk.output.mailchimp.helper.MailChimpHelper.fromCommaSeparatedString;
|
37
|
+
import static org.embulk.output.mailchimp.helper.MailChimpHelper.orderJsonNode;
|
38
|
+
import static org.embulk.output.mailchimp.helper.MailChimpHelper.toJsonNode;
|
39
|
+
import static org.embulk.output.mailchimp.model.MemberStatus.PENDING;
|
40
|
+
import static org.embulk.output.mailchimp.model.MemberStatus.SUBSCRIBED;
|
35
41
|
|
36
42
|
/**
|
37
|
-
* Created by thangnc on 4/
|
43
|
+
* Created by thangnc on 4/14/17.
|
38
44
|
*/
|
39
|
-
public class MailChimpRecordBuffer
|
45
|
+
public class MailChimpRecordBuffer
|
46
|
+
extends RecordBuffer
|
40
47
|
{
|
41
48
|
private static final Logger LOG = Exec.getLogger(MailChimpRecordBuffer.class);
|
42
|
-
private
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
{
|
53
|
-
}
|
54
|
-
|
55
|
-
@Override
|
56
|
-
public void close()
|
57
|
-
{
|
58
|
-
}
|
49
|
+
private static final int MAX_RECORD_PER_BATCH_REQUEST = 500;
|
50
|
+
private final MailChimpOutputPluginDelegate.PluginTask task;
|
51
|
+
private final MailChimpClient mailChimpClient;
|
52
|
+
private final ObjectMapper mapper;
|
53
|
+
private final Schema schema;
|
54
|
+
private int requestCount;
|
55
|
+
private long totalCount;
|
56
|
+
private List<JsonNode> records;
|
57
|
+
private Map<String, Map<String, InterestResponse>> categories;
|
58
|
+
private Map<String, MergeField> availableMergeFields;
|
59
59
|
|
60
60
|
/**
|
61
|
-
*
|
62
|
-
* Reference: https://developer.mailchimp.com/documentation/mailchimp/reference/lists/#create-post_lists_list_id
|
61
|
+
* Instantiates a new Mail chimp abstract record buffer.
|
63
62
|
*
|
64
|
-
* @param
|
65
|
-
* @param task
|
66
|
-
* @throws JsonProcessingException the json processing exception
|
63
|
+
* @param schema the schema
|
64
|
+
* @param task the task
|
67
65
|
*/
|
68
|
-
|
69
|
-
public ReportResponse push(final ObjectNode node, MailChimpOutputPluginDelegate.PluginTask task)
|
70
|
-
throws JsonProcessingException
|
66
|
+
public MailChimpRecordBuffer(final Schema schema, final PluginTask task)
|
71
67
|
{
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
68
|
+
this.schema = schema;
|
69
|
+
this.task = task;
|
70
|
+
this.mapper = new ObjectMapper()
|
71
|
+
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
|
72
|
+
.configure(JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS, false);
|
73
|
+
this.records = new ArrayList<>();
|
74
|
+
this.categories = new HashMap<>();
|
75
|
+
this.mailChimpClient = new MailChimpClient(task);
|
77
76
|
}
|
78
77
|
|
79
78
|
@Override
|
80
|
-
void
|
79
|
+
public void bufferRecord(ServiceRecord serviceRecord)
|
81
80
|
{
|
82
|
-
|
83
|
-
StringBuilder errorMessage = new StringBuilder();
|
81
|
+
JacksonServiceRecord jacksonServiceRecord;
|
84
82
|
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
MailChimpHelper.maskEmail(errorResponse.getError())));
|
89
|
-
}
|
83
|
+
try {
|
84
|
+
jacksonServiceRecord = (JacksonServiceRecord) serviceRecord;
|
85
|
+
JsonNode record = mapper.readTree(jacksonServiceRecord.toString()).get("record");
|
90
86
|
|
91
|
-
|
92
|
-
|
93
|
-
}
|
87
|
+
requestCount++;
|
88
|
+
totalCount++;
|
94
89
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
if (task.getGroupingColumns().isPresent() && !task.getGroupingColumns().get().isEmpty()) {
|
100
|
-
List<String> interestCategoryNames = task.getGroupingColumns().get();
|
90
|
+
records.add(record);
|
91
|
+
if (requestCount >= MAX_RECORD_PER_BATCH_REQUEST) {
|
92
|
+
ObjectNode subcribers = processSubcribers(records, task);
|
93
|
+
ReportResponse reportResponse = mailChimpClient.push(subcribers, task);
|
101
94
|
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
JsonNode response = client.sendRequest(endpoint, HttpMethod.GET, task);
|
106
|
-
InterestCategoriesResponse interestCategoriesResponse = getMapper().treeToValue(response,
|
107
|
-
InterestCategoriesResponse.class);
|
108
|
-
|
109
|
-
Function<CategoriesResponse, String> function = new Function<CategoriesResponse, String>()
|
110
|
-
{
|
111
|
-
@Override
|
112
|
-
public String apply(CategoriesResponse input)
|
113
|
-
{
|
114
|
-
return input.getTitle().toLowerCase();
|
95
|
+
if (totalCount % 1000 == 0) {
|
96
|
+
LOG.info("Pushed {} records", totalCount);
|
115
97
|
}
|
116
|
-
};
|
117
|
-
|
118
|
-
// Transform to a list of available category names and validate with data that user input
|
119
|
-
ImmutableList<String> availableCategories = FluentIterable
|
120
|
-
.from(interestCategoriesResponse.getCategories())
|
121
|
-
.transform(function)
|
122
|
-
.toList();
|
123
98
|
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
99
|
+
LOG.info("Response from MailChimp: {} records created, {} records updated, {} records failed",
|
100
|
+
reportResponse.getTotalCreated(),
|
101
|
+
reportResponse.getTotalUpdated(),
|
102
|
+
reportResponse.getErrorCount());
|
103
|
+
mailChimpClient.handleErrors(reportResponse.getErrors());
|
129
104
|
|
130
|
-
|
131
|
-
|
132
|
-
task.getListId(),
|
133
|
-
categoriesResponse.getId());
|
134
|
-
response = client.sendRequest(detailEndpoint, HttpMethod.GET, task);
|
135
|
-
InterestsResponse interestsResponse = getMapper().treeToValue(response, InterestsResponse.class);
|
136
|
-
categories.put(categoriesResponse.getTitle().toLowerCase(),
|
137
|
-
convertInterestCategoryToMap(interestsResponse.getInterests()));
|
105
|
+
records = new ArrayList<>();
|
106
|
+
requestCount = 0;
|
138
107
|
}
|
139
108
|
}
|
140
|
-
|
141
|
-
|
109
|
+
catch (JsonProcessingException jpe) {
|
110
|
+
throw new DataException(jpe);
|
111
|
+
}
|
112
|
+
catch (ClassCastException ex) {
|
113
|
+
throw new RuntimeException(ex);
|
114
|
+
}
|
115
|
+
catch (IOException ex) {
|
116
|
+
throw Throwables.propagate(ex);
|
117
|
+
}
|
142
118
|
}
|
143
119
|
|
144
120
|
@Override
|
145
|
-
|
121
|
+
public TaskReport commitWithTaskReportUpdated(TaskReport taskReport)
|
146
122
|
{
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
123
|
+
try {
|
124
|
+
if (records.size() > 0) {
|
125
|
+
ObjectNode subcribers = processSubcribers(records, task);
|
126
|
+
ReportResponse reportResponse = mailChimpClient.push(subcribers, task);
|
127
|
+
LOG.info("Pushed {} records", records.size());
|
128
|
+
LOG.info("Response from MailChimp: {} records created, {} records updated, {} records failed",
|
129
|
+
reportResponse.getTotalCreated(),
|
130
|
+
reportResponse.getTotalUpdated(),
|
131
|
+
reportResponse.getErrorCount());
|
132
|
+
mailChimpClient.handleErrors(reportResponse.getErrors());
|
133
|
+
}
|
134
|
+
|
135
|
+
return Exec.newTaskReport().set("pushed", totalCount);
|
152
136
|
}
|
153
|
-
|
154
|
-
|
155
|
-
String domain = task.getApikey().get().split("-")[1];
|
156
|
-
String endpoint = MessageFormat.format(mailchimpEndpoint + "/", domain);
|
157
|
-
client.sendRequest(endpoint, HttpMethod.GET, task);
|
158
|
-
return domain;
|
137
|
+
catch (JsonProcessingException jpe) {
|
138
|
+
throw new DataException(jpe);
|
159
139
|
}
|
160
|
-
|
161
|
-
throw
|
140
|
+
catch (Exception ex) {
|
141
|
+
throw Throwables.propagate(ex);
|
162
142
|
}
|
163
143
|
}
|
164
144
|
|
165
145
|
@Override
|
166
|
-
|
146
|
+
public void finish()
|
147
|
+
{
|
148
|
+
// Do not close here
|
149
|
+
}
|
150
|
+
|
151
|
+
@Override
|
152
|
+
public void close()
|
167
153
|
{
|
168
|
-
|
169
|
-
task.getListId());
|
170
|
-
JsonNode response = client.sendRequest(endpoint, HttpMethod.GET, task);
|
171
|
-
MergeFields mergeFields = getMapper().treeToValue(response,
|
172
|
-
MergeFields.class);
|
173
|
-
return convertMergeFieldToMap(mergeFields.getMergeFields());
|
154
|
+
// Do not implement
|
174
155
|
}
|
175
156
|
|
176
|
-
|
157
|
+
/**
|
158
|
+
* Receive data and build payload json that contains subscribers
|
159
|
+
*
|
160
|
+
* @param data the data
|
161
|
+
* @param task the task
|
162
|
+
* @return the object node
|
163
|
+
*/
|
164
|
+
private ObjectNode processSubcribers(final List<JsonNode> data, final PluginTask task)
|
165
|
+
throws JsonProcessingException
|
177
166
|
{
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
167
|
+
LOG.info("Start to process subscriber data");
|
168
|
+
|
169
|
+
// Should loop the names and get the id of interest categories.
|
170
|
+
// The reason why we put categories validation here because we can not share data between instance.
|
171
|
+
categories = mailChimpClient.extractInterestCategoriesByGroupNames(task);
|
172
|
+
|
173
|
+
// Extract merge fields detail
|
174
|
+
availableMergeFields = mailChimpClient.extractMergeFieldsFromList(task);
|
175
|
+
|
176
|
+
// Required merge fields
|
177
|
+
Map<String, String> map = new HashMap<>();
|
178
|
+
map.put("FNAME", task.getFnameColumn());
|
179
|
+
map.put("LNAME", task.getLnameColumn());
|
186
180
|
|
187
|
-
|
188
|
-
|
189
|
-
|
181
|
+
List<JsonNode> subscribersList = FluentIterable.from(data)
|
182
|
+
.transform(contactMapper(map))
|
183
|
+
.toList();
|
184
|
+
|
185
|
+
ObjectNode subscribers = JsonNodeFactory.instance.objectNode();
|
186
|
+
subscribers.putArray("members").addAll(subscribersList);
|
187
|
+
subscribers.put("update_existing", task.getUpdateExisting());
|
188
|
+
return subscribers;
|
190
189
|
}
|
191
190
|
|
192
|
-
private
|
191
|
+
private Function<JsonNode, JsonNode> contactMapper(final Map<String, String> allowColumns)
|
193
192
|
{
|
194
|
-
|
193
|
+
return new Function<JsonNode, JsonNode>()
|
195
194
|
{
|
196
|
-
@Nullable
|
197
195
|
@Override
|
198
|
-
public
|
196
|
+
public JsonNode apply(JsonNode input)
|
199
197
|
{
|
200
|
-
|
198
|
+
ObjectNode property = JsonNodeFactory.instance.objectNode();
|
199
|
+
property.put("email_address", input.findPath(task.getEmailColumn()).asText());
|
200
|
+
property.put("status", task.getDoubleOptIn() ? PENDING.getType() : SUBSCRIBED.getType());
|
201
|
+
ObjectNode mergeFields = JsonNodeFactory.instance.objectNode();
|
202
|
+
for (String allowColumn : allowColumns.keySet()) {
|
203
|
+
String value = input.hasNonNull(allowColumns.get(allowColumn)) ? input.findValue(allowColumns.get(allowColumn)).asText() : "";
|
204
|
+
mergeFields.put(allowColumn, value);
|
205
|
+
}
|
206
|
+
|
207
|
+
// Update additional merge fields if exist
|
208
|
+
if (task.getMergeFields().isPresent() && !task.getMergeFields().get().isEmpty()) {
|
209
|
+
for (final Column column : schema.getColumns()) {
|
210
|
+
if (!"".equals(containsCaseInsensitive(column.getName(), task.getMergeFields().get()))) {
|
211
|
+
String value = input.hasNonNull(column.getName()) ? input.findValue(column.getName()).asText() : "";
|
212
|
+
|
213
|
+
// Try to convert to Json from string with the merge field's type is address
|
214
|
+
if (availableMergeFields.get(column.getName()).getType()
|
215
|
+
.equals(MergeField.MergeFieldType.ADDRESS.getType())) {
|
216
|
+
JsonNode addressNode = toJsonNode(value);
|
217
|
+
if (addressNode instanceof NullNode) {
|
218
|
+
mergeFields.put(column.getName().toUpperCase(), value);
|
219
|
+
}
|
220
|
+
else {
|
221
|
+
mergeFields.set(column.getName().toUpperCase(),
|
222
|
+
orderJsonNode(addressNode, AddressMergeFieldAttribute.values()));
|
223
|
+
}
|
224
|
+
}
|
225
|
+
else {
|
226
|
+
mergeFields.put(column.getName().toUpperCase(), value);
|
227
|
+
}
|
228
|
+
}
|
229
|
+
}
|
230
|
+
}
|
231
|
+
|
232
|
+
property.set("merge_fields", mergeFields);
|
233
|
+
|
234
|
+
// Update interest categories if exist
|
235
|
+
if (task.getGroupingColumns().isPresent() && !task.getGroupingColumns().get().isEmpty()) {
|
236
|
+
property.set("interests", buildInterestCategories(task, input));
|
237
|
+
}
|
238
|
+
|
239
|
+
return property;
|
201
240
|
}
|
202
241
|
};
|
242
|
+
}
|
243
|
+
|
244
|
+
private ObjectNode buildInterestCategories(final MailChimpOutputPluginDelegate.PluginTask task,
|
245
|
+
final JsonNode input)
|
246
|
+
{
|
247
|
+
ObjectNode interests = JsonNodeFactory.instance.objectNode();
|
248
|
+
|
249
|
+
for (String category : task.getGroupingColumns().get()) {
|
250
|
+
String inputValue = input.findValue(category).asText();
|
251
|
+
List<String> interestValues = fromCommaSeparatedString(inputValue);
|
252
|
+
Map<String, InterestResponse> availableCategories = categories.get(category);
|
253
|
+
|
254
|
+
// Only update user-predefined categories if replace interests != true
|
255
|
+
if (!task.getReplaceInterests()) {
|
256
|
+
for (String interestValue : interestValues) {
|
257
|
+
if (availableCategories.get(interestValue) != null) {
|
258
|
+
interests.put(availableCategories.get(interestValue).getId(), true);
|
259
|
+
}
|
260
|
+
}
|
261
|
+
} // Otherwise, force update all categories include user-predefined categories
|
262
|
+
else if (task.getReplaceInterests()) {
|
263
|
+
for (String availableCategory : availableCategories.keySet()) {
|
264
|
+
if (interestValues.contains(availableCategory)) {
|
265
|
+
interests.put(availableCategories.get(availableCategory).getId(), true);
|
266
|
+
}
|
267
|
+
else {
|
268
|
+
interests.put(availableCategories.get(availableCategory).getId(), false);
|
269
|
+
}
|
270
|
+
}
|
271
|
+
}
|
272
|
+
}
|
203
273
|
|
204
|
-
return
|
205
|
-
.toList(),
|
206
|
-
function);
|
274
|
+
return interests;
|
207
275
|
}
|
208
276
|
}
|
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.14
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Thang Nguyen
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-06-
|
11
|
+
date: 2017-06-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
requirement: !ruby/object:Gem::Requirement
|
@@ -61,7 +61,7 @@ files:
|
|
61
61
|
- gradlew
|
62
62
|
- gradlew.bat
|
63
63
|
- lib/embulk/output/mailchimp.rb
|
64
|
-
- src/main/java/org/embulk/output/mailchimp/
|
64
|
+
- src/main/java/org/embulk/output/mailchimp/MailChimpClient.java
|
65
65
|
- src/main/java/org/embulk/output/mailchimp/MailChimpHttpClient.java
|
66
66
|
- src/main/java/org/embulk/output/mailchimp/MailChimpOutputPlugin.java
|
67
67
|
- src/main/java/org/embulk/output/mailchimp/MailChimpOutputPluginDelegate.java
|
@@ -89,7 +89,7 @@ files:
|
|
89
89
|
- test/override_assert_raise.rb
|
90
90
|
- test/run-test.rb
|
91
91
|
- classpath/embulk-base-restclient-0.5.3.jar
|
92
|
-
- classpath/embulk-output-mailchimp-0.3.
|
92
|
+
- classpath/embulk-output-mailchimp-0.3.14.jar
|
93
93
|
- classpath/embulk-util-retryhelper-jetty92-0.5.3.jar
|
94
94
|
- classpath/jetty-client-9.2.14.v20151106.jar
|
95
95
|
- classpath/jetty-http-9.2.14.v20151106.jar
|
@@ -1,338 +0,0 @@
|
|
1
|
-
package org.embulk.output.mailchimp;
|
2
|
-
|
3
|
-
import com.fasterxml.jackson.core.JsonParser;
|
4
|
-
import com.fasterxml.jackson.core.JsonProcessingException;
|
5
|
-
import com.fasterxml.jackson.databind.DeserializationFeature;
|
6
|
-
import com.fasterxml.jackson.databind.JsonNode;
|
7
|
-
import com.fasterxml.jackson.databind.ObjectMapper;
|
8
|
-
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
|
9
|
-
import com.fasterxml.jackson.databind.node.NullNode;
|
10
|
-
import com.fasterxml.jackson.databind.node.ObjectNode;
|
11
|
-
import com.google.common.base.Function;
|
12
|
-
import com.google.common.base.Throwables;
|
13
|
-
import com.google.common.collect.FluentIterable;
|
14
|
-
import org.embulk.base.restclient.jackson.JacksonServiceRecord;
|
15
|
-
import org.embulk.base.restclient.record.RecordBuffer;
|
16
|
-
import org.embulk.base.restclient.record.ServiceRecord;
|
17
|
-
import org.embulk.config.TaskReport;
|
18
|
-
import org.embulk.output.mailchimp.model.AddressMergeFieldAttribute;
|
19
|
-
import org.embulk.output.mailchimp.model.ErrorResponse;
|
20
|
-
import org.embulk.output.mailchimp.model.InterestResponse;
|
21
|
-
import org.embulk.output.mailchimp.model.MergeField;
|
22
|
-
import org.embulk.output.mailchimp.model.ReportResponse;
|
23
|
-
import org.embulk.spi.Column;
|
24
|
-
import org.embulk.spi.DataException;
|
25
|
-
import org.embulk.spi.Exec;
|
26
|
-
import org.embulk.spi.Schema;
|
27
|
-
import org.slf4j.Logger;
|
28
|
-
|
29
|
-
import java.io.IOException;
|
30
|
-
import java.text.MessageFormat;
|
31
|
-
import java.util.ArrayList;
|
32
|
-
import java.util.HashMap;
|
33
|
-
import java.util.List;
|
34
|
-
import java.util.Map;
|
35
|
-
|
36
|
-
import static org.embulk.output.mailchimp.helper.MailChimpHelper.containsCaseInsensitive;
|
37
|
-
import static org.embulk.output.mailchimp.helper.MailChimpHelper.fromCommaSeparatedString;
|
38
|
-
import static org.embulk.output.mailchimp.helper.MailChimpHelper.orderJsonNode;
|
39
|
-
import static org.embulk.output.mailchimp.helper.MailChimpHelper.toJsonNode;
|
40
|
-
import static org.embulk.output.mailchimp.model.MemberStatus.PENDING;
|
41
|
-
import static org.embulk.output.mailchimp.model.MemberStatus.SUBSCRIBED;
|
42
|
-
|
43
|
-
/**
|
44
|
-
* Created by thangnc on 4/14/17.
|
45
|
-
*/
|
46
|
-
public abstract class MailChimpAbstractRecordBuffer
|
47
|
-
extends RecordBuffer
|
48
|
-
{
|
49
|
-
private static final Logger LOG = Exec.getLogger(MailChimpAbstractRecordBuffer.class);
|
50
|
-
private static final int MAX_RECORD_PER_BATCH_REQUEST = 500;
|
51
|
-
/**
|
52
|
-
* The constant mailchimpEndpoint.
|
53
|
-
*/
|
54
|
-
protected static String mailchimpEndpoint = "https://{0}.api.mailchimp.com/3.0";
|
55
|
-
private final MailChimpOutputPluginDelegate.PluginTask task;
|
56
|
-
private final ObjectMapper mapper;
|
57
|
-
private final Schema schema;
|
58
|
-
private int requestCount;
|
59
|
-
private long totalCount;
|
60
|
-
private List<JsonNode> records;
|
61
|
-
private Map<String, Map<String, InterestResponse>> categories;
|
62
|
-
private Map<String, MergeField> availableMergeFields;
|
63
|
-
|
64
|
-
/**
|
65
|
-
* Instantiates a new Mail chimp abstract record buffer.
|
66
|
-
*
|
67
|
-
* @param schema the schema
|
68
|
-
* @param task the task
|
69
|
-
*/
|
70
|
-
public MailChimpAbstractRecordBuffer(final Schema schema, final MailChimpOutputPluginDelegate.PluginTask task)
|
71
|
-
{
|
72
|
-
this.schema = schema;
|
73
|
-
this.task = task;
|
74
|
-
this.mapper = new ObjectMapper()
|
75
|
-
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
|
76
|
-
.configure(JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS, false);
|
77
|
-
this.records = new ArrayList<>();
|
78
|
-
this.categories = new HashMap<>();
|
79
|
-
}
|
80
|
-
|
81
|
-
@Override
|
82
|
-
public void bufferRecord(ServiceRecord serviceRecord)
|
83
|
-
{
|
84
|
-
JacksonServiceRecord jacksonServiceRecord;
|
85
|
-
|
86
|
-
try {
|
87
|
-
jacksonServiceRecord = (JacksonServiceRecord) serviceRecord;
|
88
|
-
JsonNode record = mapper.readTree(jacksonServiceRecord.toString()).get("record");
|
89
|
-
|
90
|
-
requestCount++;
|
91
|
-
totalCount++;
|
92
|
-
|
93
|
-
records.add(record);
|
94
|
-
if (requestCount >= MAX_RECORD_PER_BATCH_REQUEST) {
|
95
|
-
ObjectNode subcribers = processSubcribers(records, task);
|
96
|
-
ReportResponse reportResponse = push(subcribers, task);
|
97
|
-
|
98
|
-
if (totalCount % 1000 == 0) {
|
99
|
-
LOG.info("Pushed {} records", totalCount);
|
100
|
-
}
|
101
|
-
|
102
|
-
LOG.info("{} records created, {} records updated, {} records failed",
|
103
|
-
reportResponse.getTotalCreated(),
|
104
|
-
reportResponse.getTotalUpdated(),
|
105
|
-
reportResponse.getErrorCount());
|
106
|
-
handleErrors(reportResponse.getErrors());
|
107
|
-
|
108
|
-
records = new ArrayList<>();
|
109
|
-
requestCount = 0;
|
110
|
-
}
|
111
|
-
}
|
112
|
-
catch (JsonProcessingException jpe) {
|
113
|
-
throw new DataException(jpe);
|
114
|
-
}
|
115
|
-
catch (ClassCastException ex) {
|
116
|
-
throw new RuntimeException(ex);
|
117
|
-
}
|
118
|
-
catch (IOException ex) {
|
119
|
-
throw Throwables.propagate(ex);
|
120
|
-
}
|
121
|
-
}
|
122
|
-
|
123
|
-
@Override
|
124
|
-
public TaskReport commitWithTaskReportUpdated(TaskReport taskReport)
|
125
|
-
{
|
126
|
-
try {
|
127
|
-
if (records.size() > 0) {
|
128
|
-
ObjectNode subcribers = processSubcribers(records, task);
|
129
|
-
ReportResponse reportResponse = push(subcribers, task);
|
130
|
-
LOG.info("Pushed {} records", records.size());
|
131
|
-
LOG.info("{} records created, {} records updated, {} records failed",
|
132
|
-
reportResponse.getTotalCreated(),
|
133
|
-
reportResponse.getTotalUpdated(),
|
134
|
-
reportResponse.getErrorCount());
|
135
|
-
handleErrors(reportResponse.getErrors());
|
136
|
-
}
|
137
|
-
|
138
|
-
return Exec.newTaskReport().set("pushed", totalCount);
|
139
|
-
}
|
140
|
-
catch (JsonProcessingException jpe) {
|
141
|
-
throw new DataException(jpe);
|
142
|
-
}
|
143
|
-
catch (Exception ex) {
|
144
|
-
throw Throwables.propagate(ex);
|
145
|
-
}
|
146
|
-
}
|
147
|
-
|
148
|
-
/**
|
149
|
-
* Receive data and build payload json that contains subscribers
|
150
|
-
*
|
151
|
-
* @param data the data
|
152
|
-
* @param task the task
|
153
|
-
* @return the object node
|
154
|
-
*/
|
155
|
-
ObjectNode processSubcribers(final List<JsonNode> data, final MailChimpOutputPluginDelegate.PluginTask task)
|
156
|
-
throws JsonProcessingException
|
157
|
-
{
|
158
|
-
LOG.info("Start to process subscriber data");
|
159
|
-
// Extract data center from meta data URL
|
160
|
-
String dc = extractDataCenter(task);
|
161
|
-
mailchimpEndpoint = MessageFormat.format(mailchimpEndpoint, dc);
|
162
|
-
|
163
|
-
// Should loop the names and get the id of interest categories.
|
164
|
-
// The reason why we put categories validation here because we can not share data between instance.
|
165
|
-
categories = extractInterestCategoriesByGroupNames(task);
|
166
|
-
|
167
|
-
// Extract merge fields detail
|
168
|
-
availableMergeFields = extractMergeFieldsFromList(task);
|
169
|
-
|
170
|
-
// Required merge fields
|
171
|
-
Map<String, String> map = new HashMap<>();
|
172
|
-
map.put("FNAME", task.getFnameColumn());
|
173
|
-
map.put("LNAME", task.getLnameColumn());
|
174
|
-
|
175
|
-
List<JsonNode> subscribersList = FluentIterable.from(data)
|
176
|
-
.transform(contactMapper(map))
|
177
|
-
.toList();
|
178
|
-
|
179
|
-
ObjectNode subscribers = JsonNodeFactory.instance.objectNode();
|
180
|
-
subscribers.putArray("members").addAll(subscribersList);
|
181
|
-
subscribers.put("update_existing", task.getUpdateExisting());
|
182
|
-
return subscribers;
|
183
|
-
}
|
184
|
-
|
185
|
-
/**
|
186
|
-
* Gets mapper.
|
187
|
-
*
|
188
|
-
* @return the mapper
|
189
|
-
*/
|
190
|
-
public ObjectMapper getMapper()
|
191
|
-
{
|
192
|
-
return mapper;
|
193
|
-
}
|
194
|
-
|
195
|
-
/**
|
196
|
-
* Gets categories.
|
197
|
-
*
|
198
|
-
* @return the categories
|
199
|
-
*/
|
200
|
-
public Map<String, Map<String, InterestResponse>> getCategories()
|
201
|
-
{
|
202
|
-
return categories;
|
203
|
-
}
|
204
|
-
|
205
|
-
/**
|
206
|
-
* Push payload data to MailChimp API and get @{@link ReportResponse}
|
207
|
-
*
|
208
|
-
* @param node the content
|
209
|
-
* @param task the task
|
210
|
-
* @return the report response
|
211
|
-
* @throws JsonProcessingException the json processing exception
|
212
|
-
*/
|
213
|
-
abstract ReportResponse push(final ObjectNode node, final MailChimpOutputPluginDelegate.PluginTask task)
|
214
|
-
throws JsonProcessingException;
|
215
|
-
|
216
|
-
/**
|
217
|
-
* Handle @{@link ErrorResponse} from MailChimp API if exists.
|
218
|
-
*
|
219
|
-
* @param errorResponses the error responses
|
220
|
-
*/
|
221
|
-
abstract void handleErrors(final List<ErrorResponse> errorResponses);
|
222
|
-
|
223
|
-
/**
|
224
|
-
* Find interest category ids by pre-defined group name which user input.
|
225
|
-
*
|
226
|
-
* @param task the task
|
227
|
-
* @return the map
|
228
|
-
* @throws JsonProcessingException the json processing exception
|
229
|
-
*/
|
230
|
-
abstract Map<String, Map<String, InterestResponse>> extractInterestCategoriesByGroupNames(final MailChimpOutputPluginDelegate.PluginTask task)
|
231
|
-
throws JsonProcessingException;
|
232
|
-
|
233
|
-
/**
|
234
|
-
* Extract data center from MailChimp v3 metadata.
|
235
|
-
*
|
236
|
-
* @param task the task
|
237
|
-
* @return the string
|
238
|
-
* @throws JsonProcessingException the json processing exception
|
239
|
-
*/
|
240
|
-
abstract String extractDataCenter(final MailChimpOutputPluginDelegate.PluginTask task)
|
241
|
-
throws JsonProcessingException;
|
242
|
-
|
243
|
-
/**
|
244
|
-
* Extract all merge fields from MailChimp list.
|
245
|
-
*
|
246
|
-
* @param task the task
|
247
|
-
* @return the map
|
248
|
-
* @throws JsonProcessingException the json processing exception
|
249
|
-
*/
|
250
|
-
abstract Map<String, MergeField> extractMergeFieldsFromList(final MailChimpOutputPluginDelegate.PluginTask task)
|
251
|
-
throws JsonProcessingException;
|
252
|
-
|
253
|
-
private Function<JsonNode, JsonNode> contactMapper(final Map<String, String> allowColumns)
|
254
|
-
{
|
255
|
-
return new Function<JsonNode, JsonNode>()
|
256
|
-
{
|
257
|
-
@Override
|
258
|
-
public JsonNode apply(JsonNode input)
|
259
|
-
{
|
260
|
-
ObjectNode property = JsonNodeFactory.instance.objectNode();
|
261
|
-
property.put("email_address", input.findPath(task.getEmailColumn()).asText());
|
262
|
-
property.put("status", task.getDoubleOptIn() ? PENDING.getType() : SUBSCRIBED.getType());
|
263
|
-
ObjectNode mergeFields = JsonNodeFactory.instance.objectNode();
|
264
|
-
for (String allowColumn : allowColumns.keySet()) {
|
265
|
-
String value = input.hasNonNull(allowColumns.get(allowColumn)) ? input.findValue(allowColumns.get(allowColumn)).asText() : "";
|
266
|
-
mergeFields.put(allowColumn, value);
|
267
|
-
}
|
268
|
-
|
269
|
-
// Update additional merge fields if exist
|
270
|
-
if (task.getMergeFields().isPresent() && !task.getMergeFields().get().isEmpty()) {
|
271
|
-
for (final Column column : schema.getColumns()) {
|
272
|
-
if (!"".equals(containsCaseInsensitive(column.getName(), task.getMergeFields().get()))) {
|
273
|
-
String value = input.hasNonNull(column.getName()) ? input.findValue(column.getName()).asText() : "";
|
274
|
-
|
275
|
-
// Try to convert to Json from string with the merge field's type is address
|
276
|
-
if (availableMergeFields.get(column.getName()).getType()
|
277
|
-
.equals(MergeField.MergeFieldType.ADDRESS.getType())) {
|
278
|
-
JsonNode addressNode = toJsonNode(value);
|
279
|
-
if (addressNode instanceof NullNode) {
|
280
|
-
mergeFields.put(column.getName().toUpperCase(), value);
|
281
|
-
}
|
282
|
-
else {
|
283
|
-
mergeFields.set(column.getName().toUpperCase(),
|
284
|
-
orderJsonNode(addressNode, AddressMergeFieldAttribute.values()));
|
285
|
-
}
|
286
|
-
}
|
287
|
-
else {
|
288
|
-
mergeFields.put(column.getName().toUpperCase(), value);
|
289
|
-
}
|
290
|
-
}
|
291
|
-
}
|
292
|
-
}
|
293
|
-
|
294
|
-
property.set("merge_fields", mergeFields);
|
295
|
-
|
296
|
-
// Update interest categories if exist
|
297
|
-
if (task.getGroupingColumns().isPresent() && !task.getGroupingColumns().get().isEmpty()) {
|
298
|
-
property.set("interests", buildInterestCategories(task, input));
|
299
|
-
}
|
300
|
-
|
301
|
-
return property;
|
302
|
-
}
|
303
|
-
};
|
304
|
-
}
|
305
|
-
|
306
|
-
private ObjectNode buildInterestCategories(final MailChimpOutputPluginDelegate.PluginTask task,
|
307
|
-
final JsonNode input)
|
308
|
-
{
|
309
|
-
ObjectNode interests = JsonNodeFactory.instance.objectNode();
|
310
|
-
|
311
|
-
for (String category : task.getGroupingColumns().get()) {
|
312
|
-
String inputValue = input.findValue(category).asText();
|
313
|
-
List<String> interestValues = fromCommaSeparatedString(inputValue);
|
314
|
-
Map<String, InterestResponse> availableCategories = categories.get(category);
|
315
|
-
|
316
|
-
// Only update user-predefined categories if replace interests != true
|
317
|
-
if (!task.getReplaceInterests()) {
|
318
|
-
for (String interestValue : interestValues) {
|
319
|
-
if (availableCategories.get(interestValue) != null) {
|
320
|
-
interests.put(availableCategories.get(interestValue).getId(), true);
|
321
|
-
}
|
322
|
-
}
|
323
|
-
} // Otherwise, force update all categories include user-predefined categories
|
324
|
-
else if (task.getReplaceInterests()) {
|
325
|
-
for (String availableCategory : availableCategories.keySet()) {
|
326
|
-
if (interestValues.contains(availableCategory)) {
|
327
|
-
interests.put(availableCategories.get(availableCategory).getId(), true);
|
328
|
-
}
|
329
|
-
else {
|
330
|
-
interests.put(availableCategories.get(availableCategory).getId(), false);
|
331
|
-
}
|
332
|
-
}
|
333
|
-
}
|
334
|
-
}
|
335
|
-
|
336
|
-
return interests;
|
337
|
-
}
|
338
|
-
}
|