embulk-input-marketo_extended 0.6.18
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 +7 -0
- data/.github/PULL_REQUEST_TEMPLATE.md +37 -0
- data/.gitignore +14 -0
- data/.travis.yml +6 -0
- data/CHANGELOG.md +170 -0
- data/LICENSE.txt +21 -0
- data/README.md +213 -0
- data/build.gradle +103 -0
- data/config/checkstyle/checkstyle.xml +128 -0
- data/config/checkstyle/default.xml +108 -0
- data/gradle/wrapper/gradle-wrapper.jar +0 -0
- data/gradle/wrapper/gradle-wrapper.properties +6 -0
- data/gradlew +169 -0
- data/gradlew.bat +84 -0
- data/lib/embulk/input/marketo.rb +3 -0
- data/settings.gradle +1 -0
- data/src/main/java/org/embulk/input/marketo/CsvTokenizer.java +700 -0
- data/src/main/java/org/embulk/input/marketo/MarketoInputPlugin.java +15 -0
- data/src/main/java/org/embulk/input/marketo/MarketoInputPluginDelegate.java +100 -0
- data/src/main/java/org/embulk/input/marketo/MarketoService.java +38 -0
- data/src/main/java/org/embulk/input/marketo/MarketoServiceImpl.java +245 -0
- data/src/main/java/org/embulk/input/marketo/MarketoUtils.java +212 -0
- data/src/main/java/org/embulk/input/marketo/delegate/ActivityBulkExtractInputPlugin.java +167 -0
- data/src/main/java/org/embulk/input/marketo/delegate/CampaignInputPlugin.java +48 -0
- data/src/main/java/org/embulk/input/marketo/delegate/CustomObjectInputPlugin.java +75 -0
- data/src/main/java/org/embulk/input/marketo/delegate/CustomObjectResponseMapperBuilder.java +81 -0
- data/src/main/java/org/embulk/input/marketo/delegate/LeadBulkExtractInputPlugin.java +66 -0
- data/src/main/java/org/embulk/input/marketo/delegate/LeadServiceResponseMapperBuilder.java +85 -0
- data/src/main/java/org/embulk/input/marketo/delegate/LeadWithListInputPlugin.java +64 -0
- data/src/main/java/org/embulk/input/marketo/delegate/LeadWithProgramInputPlugin.java +60 -0
- data/src/main/java/org/embulk/input/marketo/delegate/MarketoBaseBulkExtractInputPlugin.java +441 -0
- data/src/main/java/org/embulk/input/marketo/delegate/MarketoBaseInputPluginDelegate.java +92 -0
- data/src/main/java/org/embulk/input/marketo/delegate/ProgramInputPlugin.java +228 -0
- data/src/main/java/org/embulk/input/marketo/exception/MarketoAPIException.java +30 -0
- data/src/main/java/org/embulk/input/marketo/model/BulkExtractRangeHeader.java +26 -0
- data/src/main/java/org/embulk/input/marketo/model/MarketoAccessTokenResponse.java +92 -0
- data/src/main/java/org/embulk/input/marketo/model/MarketoBulkExtractRequest.java +68 -0
- data/src/main/java/org/embulk/input/marketo/model/MarketoError.java +40 -0
- data/src/main/java/org/embulk/input/marketo/model/MarketoField.java +126 -0
- data/src/main/java/org/embulk/input/marketo/model/MarketoResponse.java +82 -0
- data/src/main/java/org/embulk/input/marketo/model/filter/DateRangeFilter.java +40 -0
- data/src/main/java/org/embulk/input/marketo/rest/MarketoBaseRestClient.java +306 -0
- data/src/main/java/org/embulk/input/marketo/rest/MarketoInputStreamResponseEntityReader.java +69 -0
- data/src/main/java/org/embulk/input/marketo/rest/MarketoRESTEndpoint.java +47 -0
- data/src/main/java/org/embulk/input/marketo/rest/MarketoResponseJetty92EntityReader.java +89 -0
- data/src/main/java/org/embulk/input/marketo/rest/MarketoRestClient.java +569 -0
- data/src/main/java/org/embulk/input/marketo/rest/RecordPagingIterable.java +180 -0
- data/src/test/java/org/embulk/input/marketo/MarketoServiceImplTest.java +140 -0
- data/src/test/java/org/embulk/input/marketo/MarketoUtilsTest.java +87 -0
- data/src/test/java/org/embulk/input/marketo/delegate/ActivityBulkExtractInputPluginTest.java +128 -0
- data/src/test/java/org/embulk/input/marketo/delegate/CampaignInputPluginTest.java +73 -0
- data/src/test/java/org/embulk/input/marketo/delegate/CustomObjectInputPluginTest.java +102 -0
- data/src/test/java/org/embulk/input/marketo/delegate/LeadBulkExtractInputPluginTest.java +99 -0
- data/src/test/java/org/embulk/input/marketo/delegate/LeadServiceResponseMapperBuilderTest.java +119 -0
- data/src/test/java/org/embulk/input/marketo/delegate/LeadWithListInputPluginTest.java +101 -0
- data/src/test/java/org/embulk/input/marketo/delegate/LeadWithProgramInputPluginTest.java +103 -0
- data/src/test/java/org/embulk/input/marketo/delegate/MarketoBaseBulkExtractInputPluginTest.java +169 -0
- data/src/test/java/org/embulk/input/marketo/delegate/ProgramInputPluginTest.java +343 -0
- data/src/test/java/org/embulk/input/marketo/rest/MarketoBaseRestClientTest.java +368 -0
- data/src/test/java/org/embulk/input/marketo/rest/MarketoRestClientTest.java +584 -0
- data/src/test/resources/config/activity_bulk_extract_config.yaml +7 -0
- data/src/test/resources/config/custom_object_config.yaml +8 -0
- data/src/test/resources/config/lead_bulk_extract_config.yaml +8 -0
- data/src/test/resources/config/rest_config.yaml +3 -0
- data/src/test/resources/fixtures/activity_extract1.csv +35 -0
- data/src/test/resources/fixtures/activity_extract2.csv +22 -0
- data/src/test/resources/fixtures/activity_types.json +22 -0
- data/src/test/resources/fixtures/all_program_full.json +53 -0
- data/src/test/resources/fixtures/campaign_response.json +38 -0
- data/src/test/resources/fixtures/campaign_response_full.json +102 -0
- data/src/test/resources/fixtures/custom_object_describe.json +124 -0
- data/src/test/resources/fixtures/custom_object_describe_marketo_fields_full.json +22 -0
- data/src/test/resources/fixtures/custom_object_expected.json +66 -0
- data/src/test/resources/fixtures/custom_object_response.json +24 -0
- data/src/test/resources/fixtures/custom_object_response_full.json +23 -0
- data/src/test/resources/fixtures/lead_by_list.json +33 -0
- data/src/test/resources/fixtures/lead_by_program_response.json +47 -0
- data/src/test/resources/fixtures/lead_describe.json +221 -0
- data/src/test/resources/fixtures/lead_describe_expected.json +66 -0
- data/src/test/resources/fixtures/lead_describe_marketo_fields_full.json +518 -0
- data/src/test/resources/fixtures/lead_extract1.csv +11 -0
- data/src/test/resources/fixtures/lead_response_full.json +2402 -0
- data/src/test/resources/fixtures/lead_with_program_full.json +17 -0
- data/src/test/resources/fixtures/leads_extract2.csv +10 -0
- data/src/test/resources/fixtures/list_reponse_full.json +191 -0
- data/src/test/resources/fixtures/lists_response.json +31 -0
- data/src/test/resources/fixtures/program_response.json +71 -0
- metadata +171 -0
@@ -0,0 +1,68 @@
|
|
1
|
+
package org.embulk.input.marketo.model;
|
2
|
+
|
3
|
+
import java.util.HashMap;
|
4
|
+
import java.util.List;
|
5
|
+
import java.util.Map;
|
6
|
+
|
7
|
+
/**
|
8
|
+
* Created by tai.khuu on 8/27/17.
|
9
|
+
*/
|
10
|
+
public class MarketoBulkExtractRequest
|
11
|
+
{
|
12
|
+
private List<String> fields;
|
13
|
+
private String format;
|
14
|
+
|
15
|
+
private Map<String, String> columnHeaderNames;
|
16
|
+
|
17
|
+
private Map<String, Object> filter = new HashMap<>();
|
18
|
+
|
19
|
+
public List<String> getFields()
|
20
|
+
{
|
21
|
+
return fields;
|
22
|
+
}
|
23
|
+
|
24
|
+
public void setFields(List<String> fields)
|
25
|
+
{
|
26
|
+
this.fields = fields;
|
27
|
+
}
|
28
|
+
|
29
|
+
public String getFormat()
|
30
|
+
{
|
31
|
+
return format;
|
32
|
+
}
|
33
|
+
|
34
|
+
public void setFormat(String format)
|
35
|
+
{
|
36
|
+
this.format = format;
|
37
|
+
}
|
38
|
+
|
39
|
+
public Map<String, String> getColumnHeaderNames()
|
40
|
+
{
|
41
|
+
return columnHeaderNames;
|
42
|
+
}
|
43
|
+
|
44
|
+
public void setColumnHeaderNames(Map<String, String> columnHeaderNames)
|
45
|
+
{
|
46
|
+
this.columnHeaderNames = columnHeaderNames;
|
47
|
+
}
|
48
|
+
|
49
|
+
public Map<String, Object> getFilter()
|
50
|
+
{
|
51
|
+
return filter;
|
52
|
+
}
|
53
|
+
|
54
|
+
public void setFilter(Map<String, Object> filter)
|
55
|
+
{
|
56
|
+
this.filter = filter;
|
57
|
+
}
|
58
|
+
|
59
|
+
@Override
|
60
|
+
public String toString()
|
61
|
+
{
|
62
|
+
return "MarketoBulkExtractRequest{" +
|
63
|
+
"format='" + format + '\'' +
|
64
|
+
", columnHeaderNames=" + columnHeaderNames +
|
65
|
+
", filter=" + filter +
|
66
|
+
'}';
|
67
|
+
}
|
68
|
+
}
|
@@ -0,0 +1,40 @@
|
|
1
|
+
package org.embulk.input.marketo.model;
|
2
|
+
|
3
|
+
/**
|
4
|
+
* Created by tai.khuu on 8/25/17.
|
5
|
+
*/
|
6
|
+
public class MarketoError
|
7
|
+
{
|
8
|
+
private String code;
|
9
|
+
|
10
|
+
private String message;
|
11
|
+
|
12
|
+
public String getCode()
|
13
|
+
{
|
14
|
+
return code;
|
15
|
+
}
|
16
|
+
|
17
|
+
public void setCode(String code)
|
18
|
+
{
|
19
|
+
this.code = code;
|
20
|
+
}
|
21
|
+
|
22
|
+
public String getMessage()
|
23
|
+
{
|
24
|
+
return message;
|
25
|
+
}
|
26
|
+
|
27
|
+
public void setMessage(String message)
|
28
|
+
{
|
29
|
+
this.message = message;
|
30
|
+
}
|
31
|
+
|
32
|
+
@Override
|
33
|
+
public String toString()
|
34
|
+
{
|
35
|
+
return "MarketoError{" +
|
36
|
+
"code='" + code + '\'' +
|
37
|
+
", message='" + message + '\'' +
|
38
|
+
'}';
|
39
|
+
}
|
40
|
+
}
|
@@ -0,0 +1,126 @@
|
|
1
|
+
package org.embulk.input.marketo.model;
|
2
|
+
|
3
|
+
import com.google.common.base.Optional;
|
4
|
+
import org.embulk.input.marketo.MarketoUtils;
|
5
|
+
import org.embulk.spi.type.Type;
|
6
|
+
import org.embulk.spi.type.Types;
|
7
|
+
|
8
|
+
/**
|
9
|
+
* Created by tai.khuu on 9/22/17.
|
10
|
+
*/
|
11
|
+
|
12
|
+
public class MarketoField
|
13
|
+
{
|
14
|
+
private String name;
|
15
|
+
|
16
|
+
private MarketoDataType marketoDataType;
|
17
|
+
|
18
|
+
public MarketoField(){}
|
19
|
+
|
20
|
+
public MarketoField(String name, String dataType)
|
21
|
+
{
|
22
|
+
this.name = name;
|
23
|
+
try {
|
24
|
+
marketoDataType = MarketoDataType.valueOf(dataType.toUpperCase());
|
25
|
+
}
|
26
|
+
catch (IllegalArgumentException ex) {
|
27
|
+
marketoDataType = MarketoDataType.STRING;
|
28
|
+
}
|
29
|
+
}
|
30
|
+
|
31
|
+
public MarketoField(String name, MarketoDataType marketoDataType)
|
32
|
+
{
|
33
|
+
this.name = name;
|
34
|
+
this.marketoDataType = marketoDataType;
|
35
|
+
}
|
36
|
+
|
37
|
+
public String getName()
|
38
|
+
{
|
39
|
+
return name;
|
40
|
+
}
|
41
|
+
|
42
|
+
public MarketoDataType getMarketoDataType()
|
43
|
+
{
|
44
|
+
return marketoDataType;
|
45
|
+
}
|
46
|
+
|
47
|
+
@Override
|
48
|
+
public boolean equals(Object o)
|
49
|
+
{
|
50
|
+
if (this == o) {
|
51
|
+
return true;
|
52
|
+
}
|
53
|
+
if (o == null || getClass() != o.getClass()) {
|
54
|
+
return false;
|
55
|
+
}
|
56
|
+
|
57
|
+
MarketoField field = (MarketoField) o;
|
58
|
+
|
59
|
+
if (name != null ? !name.equals(field.name) : field.name != null) {
|
60
|
+
return false;
|
61
|
+
}
|
62
|
+
return marketoDataType == field.marketoDataType;
|
63
|
+
}
|
64
|
+
|
65
|
+
@Override
|
66
|
+
public int hashCode()
|
67
|
+
{
|
68
|
+
int result = name != null ? name.hashCode() : 0;
|
69
|
+
result = 31 * result + (marketoDataType != null ? marketoDataType.hashCode() : 0);
|
70
|
+
return result;
|
71
|
+
}
|
72
|
+
|
73
|
+
public enum MarketoDataType
|
74
|
+
{
|
75
|
+
DATETIME(Types.TIMESTAMP, MarketoUtils.MARKETO_DATE_TIME_FORMAT),
|
76
|
+
EMAIL(Types.STRING),
|
77
|
+
FLOAT(Types.DOUBLE),
|
78
|
+
INTEGER(Types.LONG),
|
79
|
+
FORMULA(Types.STRING),
|
80
|
+
PERCENT(Types.DOUBLE),
|
81
|
+
URL(Types.STRING),
|
82
|
+
PHONE(Types.STRING),
|
83
|
+
TEXTAREA(Types.STRING),
|
84
|
+
TEXT(Types.STRING),
|
85
|
+
STRING(Types.STRING),
|
86
|
+
SCORE(Types.LONG),
|
87
|
+
BOOLEAN(Types.BOOLEAN),
|
88
|
+
CURRENCY(Types.DOUBLE),
|
89
|
+
DATE(Types.TIMESTAMP, MarketoUtils.MARKETO_DATE_FORMAT),
|
90
|
+
REFERENCE(Types.STRING);
|
91
|
+
|
92
|
+
private Type type;
|
93
|
+
|
94
|
+
private String format;
|
95
|
+
|
96
|
+
MarketoDataType(Type type, String format)
|
97
|
+
{
|
98
|
+
this.type = type;
|
99
|
+
this.format = format;
|
100
|
+
}
|
101
|
+
|
102
|
+
MarketoDataType(Type type)
|
103
|
+
{
|
104
|
+
this.type = type;
|
105
|
+
}
|
106
|
+
|
107
|
+
public Type getType()
|
108
|
+
{
|
109
|
+
return type;
|
110
|
+
}
|
111
|
+
|
112
|
+
public Optional<String> getFormat()
|
113
|
+
{
|
114
|
+
return Optional.fromNullable(format);
|
115
|
+
}
|
116
|
+
}
|
117
|
+
|
118
|
+
@Override
|
119
|
+
public String toString()
|
120
|
+
{
|
121
|
+
return "MarketoField{" +
|
122
|
+
"name='" + name + '\'' +
|
123
|
+
", marketoDataType=" + marketoDataType +
|
124
|
+
'}';
|
125
|
+
}
|
126
|
+
}
|
@@ -0,0 +1,82 @@
|
|
1
|
+
package org.embulk.input.marketo.model;
|
2
|
+
|
3
|
+
import java.util.ArrayList;
|
4
|
+
import java.util.List;
|
5
|
+
|
6
|
+
/**
|
7
|
+
* Created by tai.khuu on 8/25/17.
|
8
|
+
*/
|
9
|
+
public class MarketoResponse<T>
|
10
|
+
{
|
11
|
+
private String requestId;
|
12
|
+
|
13
|
+
private boolean success;
|
14
|
+
|
15
|
+
private String nextPageToken;
|
16
|
+
|
17
|
+
private boolean moreResult;
|
18
|
+
|
19
|
+
private List<MarketoError> errors;
|
20
|
+
|
21
|
+
private List<T> result = new ArrayList<>();
|
22
|
+
|
23
|
+
public String getRequestId()
|
24
|
+
{
|
25
|
+
return requestId;
|
26
|
+
}
|
27
|
+
|
28
|
+
public void setRequestId(String requestId)
|
29
|
+
{
|
30
|
+
this.requestId = requestId;
|
31
|
+
}
|
32
|
+
|
33
|
+
public boolean isSuccess()
|
34
|
+
{
|
35
|
+
return success;
|
36
|
+
}
|
37
|
+
|
38
|
+
public void setSuccess(boolean success)
|
39
|
+
{
|
40
|
+
this.success = success;
|
41
|
+
}
|
42
|
+
|
43
|
+
public List<MarketoError> getErrors()
|
44
|
+
{
|
45
|
+
return errors;
|
46
|
+
}
|
47
|
+
|
48
|
+
public void setErrors(List<MarketoError> errors)
|
49
|
+
{
|
50
|
+
this.errors = errors;
|
51
|
+
}
|
52
|
+
|
53
|
+
public List<T> getResult()
|
54
|
+
{
|
55
|
+
return result;
|
56
|
+
}
|
57
|
+
|
58
|
+
public void setResult(List<T> result)
|
59
|
+
{
|
60
|
+
this.result = result;
|
61
|
+
}
|
62
|
+
|
63
|
+
public String getNextPageToken()
|
64
|
+
{
|
65
|
+
return nextPageToken;
|
66
|
+
}
|
67
|
+
|
68
|
+
public void setNextPageToken(String nextPageToken)
|
69
|
+
{
|
70
|
+
this.nextPageToken = nextPageToken;
|
71
|
+
}
|
72
|
+
|
73
|
+
public boolean isMoreResult()
|
74
|
+
{
|
75
|
+
return moreResult;
|
76
|
+
}
|
77
|
+
|
78
|
+
public void setMoreResult(boolean moreResult)
|
79
|
+
{
|
80
|
+
this.moreResult = moreResult;
|
81
|
+
}
|
82
|
+
}
|
@@ -0,0 +1,40 @@
|
|
1
|
+
package org.embulk.input.marketo.model.filter;
|
2
|
+
|
3
|
+
/**
|
4
|
+
* Created by tai.khuu on 8/27/17.
|
5
|
+
*/
|
6
|
+
public class DateRangeFilter
|
7
|
+
{
|
8
|
+
private String startAt;
|
9
|
+
|
10
|
+
private String endAt;
|
11
|
+
|
12
|
+
public String getStartAt()
|
13
|
+
{
|
14
|
+
return startAt;
|
15
|
+
}
|
16
|
+
|
17
|
+
public void setStartAt(String startAt)
|
18
|
+
{
|
19
|
+
this.startAt = startAt;
|
20
|
+
}
|
21
|
+
|
22
|
+
public String getEndAt()
|
23
|
+
{
|
24
|
+
return endAt;
|
25
|
+
}
|
26
|
+
|
27
|
+
public void setEndAt(String endAt)
|
28
|
+
{
|
29
|
+
this.endAt = endAt;
|
30
|
+
}
|
31
|
+
|
32
|
+
@Override
|
33
|
+
public String toString()
|
34
|
+
{
|
35
|
+
return "DateRangeFilter{" +
|
36
|
+
"startAt='" + startAt + '\'' +
|
37
|
+
", endAt='" + endAt + '\'' +
|
38
|
+
'}';
|
39
|
+
}
|
40
|
+
}
|
@@ -0,0 +1,306 @@
|
|
1
|
+
package org.embulk.input.marketo.rest;
|
2
|
+
|
3
|
+
import com.fasterxml.jackson.databind.ObjectMapper;
|
4
|
+
import com.google.common.annotations.VisibleForTesting;
|
5
|
+
import com.google.common.base.Optional;
|
6
|
+
import com.google.common.collect.ArrayListMultimap;
|
7
|
+
import com.google.common.collect.Multimap;
|
8
|
+
import org.apache.commons.lang3.StringUtils;
|
9
|
+
import org.eclipse.jetty.client.HttpClient;
|
10
|
+
import org.eclipse.jetty.client.HttpResponseException;
|
11
|
+
import org.eclipse.jetty.client.api.ContentProvider;
|
12
|
+
import org.eclipse.jetty.client.api.Request;
|
13
|
+
import org.eclipse.jetty.client.api.Response;
|
14
|
+
import org.eclipse.jetty.client.util.StringContentProvider;
|
15
|
+
import org.eclipse.jetty.http.HttpMethod;
|
16
|
+
import org.embulk.config.ConfigException;
|
17
|
+
import org.embulk.input.marketo.exception.MarketoAPIException;
|
18
|
+
import org.embulk.input.marketo.model.MarketoAccessTokenResponse;
|
19
|
+
import org.embulk.input.marketo.model.MarketoError;
|
20
|
+
import org.embulk.spi.DataException;
|
21
|
+
import org.embulk.spi.Exec;
|
22
|
+
import org.embulk.util.retryhelper.jetty92.Jetty92ResponseReader;
|
23
|
+
import org.embulk.util.retryhelper.jetty92.Jetty92RetryHelper;
|
24
|
+
import org.embulk.util.retryhelper.jetty92.Jetty92SingleRequester;
|
25
|
+
import org.embulk.util.retryhelper.jetty92.StringJetty92ResponseEntityReader;
|
26
|
+
import org.slf4j.Logger;
|
27
|
+
|
28
|
+
import java.io.EOFException;
|
29
|
+
import java.io.IOException;
|
30
|
+
import java.net.SocketTimeoutException;
|
31
|
+
import java.nio.charset.StandardCharsets;
|
32
|
+
import java.util.Map;
|
33
|
+
import java.util.concurrent.ExecutionException;
|
34
|
+
import java.util.concurrent.TimeoutException;
|
35
|
+
|
36
|
+
import static com.fasterxml.jackson.core.JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS;
|
37
|
+
import static com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES;
|
38
|
+
|
39
|
+
/**
|
40
|
+
* Marketo base rest client
|
41
|
+
* Created by tai.khuu on 9/7/17.
|
42
|
+
*/
|
43
|
+
public class MarketoBaseRestClient implements AutoCloseable
|
44
|
+
{
|
45
|
+
private static final Logger LOGGER = Exec.getLogger(MarketoBaseRestClient.class);
|
46
|
+
|
47
|
+
private static final String APPLICATION_JSON = "application/json";
|
48
|
+
|
49
|
+
private static final String AUTHORIZATION_HEADER = "Authorization";
|
50
|
+
|
51
|
+
private String identityEndPoint;
|
52
|
+
|
53
|
+
private String clientId;
|
54
|
+
|
55
|
+
private String clientSecret;
|
56
|
+
|
57
|
+
private String accessToken;
|
58
|
+
|
59
|
+
private int marketoLimitIntervalMillis;
|
60
|
+
|
61
|
+
private Jetty92RetryHelper retryHelper;
|
62
|
+
|
63
|
+
protected long readTimeoutMillis;
|
64
|
+
|
65
|
+
private Optional<String> partnerApiKey;
|
66
|
+
|
67
|
+
protected static final ObjectMapper OBJECT_MAPPER = new ObjectMapper().configure(FAIL_ON_UNKNOWN_PROPERTIES, false).configure(ALLOW_UNQUOTED_CONTROL_CHARS, false);
|
68
|
+
|
69
|
+
MarketoBaseRestClient(String identityEndPoint,
|
70
|
+
String clientId,
|
71
|
+
String clientSecret,
|
72
|
+
Optional<String> partnerApiKey,
|
73
|
+
int marketoLimitIntervalMillis,
|
74
|
+
long readTimeoutMillis,
|
75
|
+
Jetty92RetryHelper retryHelper)
|
76
|
+
{
|
77
|
+
this.identityEndPoint = identityEndPoint;
|
78
|
+
this.clientId = clientId;
|
79
|
+
this.clientSecret = clientSecret;
|
80
|
+
this.readTimeoutMillis = readTimeoutMillis;
|
81
|
+
this.retryHelper = retryHelper;
|
82
|
+
this.marketoLimitIntervalMillis = marketoLimitIntervalMillis;
|
83
|
+
this.partnerApiKey = partnerApiKey;
|
84
|
+
}
|
85
|
+
|
86
|
+
private void renewAccessToken()
|
87
|
+
{
|
88
|
+
accessToken = getAccessTokenWithWrappedException();
|
89
|
+
}
|
90
|
+
|
91
|
+
@VisibleForTesting
|
92
|
+
public String getAccessToken()
|
93
|
+
{
|
94
|
+
if (accessToken == null) {
|
95
|
+
synchronized (this) {
|
96
|
+
if (accessToken == null) {
|
97
|
+
accessToken = getAccessTokenWithWrappedException();
|
98
|
+
}
|
99
|
+
}
|
100
|
+
}
|
101
|
+
return accessToken;
|
102
|
+
}
|
103
|
+
|
104
|
+
private String requestAccessToken()
|
105
|
+
{
|
106
|
+
final Multimap<String, String> params = ArrayListMultimap.create();
|
107
|
+
params.put("client_id", clientId);
|
108
|
+
params.put("client_secret", clientSecret);
|
109
|
+
params.put("grant_type", "client_credentials");
|
110
|
+
|
111
|
+
// add partner api key to the request
|
112
|
+
if (partnerApiKey.isPresent()) {
|
113
|
+
LOGGER.info("> Request access_token with partner_id: {}", StringUtils.abbreviate(partnerApiKey.get(), 8));
|
114
|
+
params.put("partner_id", partnerApiKey.get());
|
115
|
+
}
|
116
|
+
|
117
|
+
String response = retryHelper.requestWithRetry(new StringJetty92ResponseEntityReader(readTimeoutMillis), new Jetty92SingleRequester()
|
118
|
+
{
|
119
|
+
@Override
|
120
|
+
public void requestOnce(HttpClient client, Response.Listener responseListener)
|
121
|
+
{
|
122
|
+
Request request = client.newRequest(identityEndPoint + MarketoRESTEndpoint.ACCESS_TOKEN.getEndpoint()).method(HttpMethod.GET);
|
123
|
+
for (String key : params.keySet()) {
|
124
|
+
for (String value : params.get(key)) {
|
125
|
+
request.param(key, value);
|
126
|
+
}
|
127
|
+
}
|
128
|
+
request.send(responseListener);
|
129
|
+
}
|
130
|
+
|
131
|
+
@Override
|
132
|
+
protected boolean isResponseStatusToRetry(Response response)
|
133
|
+
{
|
134
|
+
return response.getStatus() == 502;
|
135
|
+
}
|
136
|
+
|
137
|
+
@Override
|
138
|
+
protected boolean isExceptionToRetry(Exception exception)
|
139
|
+
{
|
140
|
+
if (exception instanceof TimeoutException || exception instanceof SocketTimeoutException || exception instanceof EOFException || super.isExceptionToRetry(exception)) {
|
141
|
+
return true;
|
142
|
+
}
|
143
|
+
// unwrap
|
144
|
+
if (exception instanceof ExecutionException || (exception instanceof IOException && exception.getCause() != null)) {
|
145
|
+
return this.toRetry((Exception) exception.getCause());
|
146
|
+
}
|
147
|
+
return false;
|
148
|
+
}
|
149
|
+
});
|
150
|
+
|
151
|
+
MarketoAccessTokenResponse accessTokenResponse;
|
152
|
+
|
153
|
+
try {
|
154
|
+
accessTokenResponse = OBJECT_MAPPER.readValue(response, MarketoAccessTokenResponse.class);
|
155
|
+
}
|
156
|
+
catch (IOException e) {
|
157
|
+
LOGGER.error("Exception when parse access token response", e);
|
158
|
+
throw new DataException("Can't parse access token response");
|
159
|
+
}
|
160
|
+
if (accessTokenResponse.hasError()) {
|
161
|
+
throw new DataException(accessTokenResponse.getErrorDescription());
|
162
|
+
}
|
163
|
+
LOGGER.info("Acquired new access token");
|
164
|
+
return accessTokenResponse.getAccessToken();
|
165
|
+
}
|
166
|
+
|
167
|
+
protected <T> T doGet(final String target, final Map<String, String> headers, final Multimap<String, String> params, Jetty92ResponseReader<T> responseReader)
|
168
|
+
{
|
169
|
+
return doRequestWithWrappedException(target, HttpMethod.GET, headers, params, null, responseReader);
|
170
|
+
}
|
171
|
+
|
172
|
+
protected <T> T doPost(final String target, final Map<String, String> headers, final Multimap<String, String> params, final String content, Jetty92ResponseReader<T> responseReader)
|
173
|
+
{
|
174
|
+
StringContentProvider contentProvider = null;
|
175
|
+
if (content != null) {
|
176
|
+
contentProvider = new StringContentProvider(APPLICATION_JSON, content, StandardCharsets.UTF_8);
|
177
|
+
}
|
178
|
+
return doPost(target, headers, params, responseReader, contentProvider);
|
179
|
+
}
|
180
|
+
|
181
|
+
protected <T> T doPost(final String target, final Map<String, String> headers, final Multimap<String, String> params, Jetty92ResponseReader<T> responseReader, final ContentProvider content)
|
182
|
+
{
|
183
|
+
return doRequestWithWrappedException(target, HttpMethod.POST, headers, params, content, responseReader);
|
184
|
+
}
|
185
|
+
|
186
|
+
private String getAccessTokenWithWrappedException()
|
187
|
+
{
|
188
|
+
try {
|
189
|
+
return requestAccessToken();
|
190
|
+
}
|
191
|
+
catch (Exception e) {
|
192
|
+
if (e instanceof HttpResponseException) {
|
193
|
+
throw new ConfigException(e.getMessage());
|
194
|
+
}
|
195
|
+
if (e.getCause() instanceof HttpResponseException) {
|
196
|
+
throw new ConfigException(e.getCause().getMessage());
|
197
|
+
}
|
198
|
+
throw e;
|
199
|
+
}
|
200
|
+
}
|
201
|
+
|
202
|
+
private <T> T doRequestWithWrappedException(final String target, final HttpMethod method, final Map<String, String> headers, final Multimap<String, String> params, final ContentProvider contentProvider, Jetty92ResponseReader<T> responseReader)
|
203
|
+
{
|
204
|
+
try {
|
205
|
+
return doRequest(target, method, headers, params, contentProvider, responseReader);
|
206
|
+
}
|
207
|
+
catch (Exception e) {
|
208
|
+
if (e instanceof MarketoAPIException || e instanceof HttpResponseException) {
|
209
|
+
throw new DataException(e.getMessage());
|
210
|
+
}
|
211
|
+
if (e.getCause() instanceof MarketoAPIException || e.getCause() instanceof HttpResponseException) {
|
212
|
+
throw new DataException(e.getCause().getMessage());
|
213
|
+
}
|
214
|
+
throw e;
|
215
|
+
}
|
216
|
+
}
|
217
|
+
|
218
|
+
protected <T> T doRequest(final String target, final HttpMethod method, final Map<String, String> headers, final Multimap<String, String> params, final ContentProvider contentProvider, Jetty92ResponseReader<T> responseReader)
|
219
|
+
{
|
220
|
+
return retryHelper.requestWithRetry(responseReader, new Jetty92SingleRequester()
|
221
|
+
{
|
222
|
+
@Override
|
223
|
+
public void requestOnce(HttpClient client, Response.Listener responseListener)
|
224
|
+
{
|
225
|
+
Request request = client.newRequest(target).method(method);
|
226
|
+
if (headers != null) {
|
227
|
+
for (String key : headers.keySet()) {
|
228
|
+
request.header(key, headers.get(key));
|
229
|
+
}
|
230
|
+
}
|
231
|
+
request.header(AUTHORIZATION_HEADER, "Bearer " + getAccessToken());
|
232
|
+
if (params != null) {
|
233
|
+
for (String key : params.keySet()) {
|
234
|
+
for (String value : params.get(key)) {
|
235
|
+
request.param(key, value);
|
236
|
+
}
|
237
|
+
}
|
238
|
+
}
|
239
|
+
LOGGER.info("CALLING {} -> {} - params: {}", method, target, params);
|
240
|
+
if (contentProvider != null) {
|
241
|
+
request.content(contentProvider);
|
242
|
+
}
|
243
|
+
request.send(responseListener);
|
244
|
+
}
|
245
|
+
|
246
|
+
@Override
|
247
|
+
protected boolean isResponseStatusToRetry(Response response)
|
248
|
+
{
|
249
|
+
//413 failed job
|
250
|
+
//414 failed job
|
251
|
+
//502 retry
|
252
|
+
return response.getStatus() / 4 != 100;
|
253
|
+
}
|
254
|
+
|
255
|
+
@Override
|
256
|
+
protected boolean isExceptionToRetry(Exception exception)
|
257
|
+
{
|
258
|
+
if (exception instanceof EOFException || exception instanceof TimeoutException || exception instanceof SocketTimeoutException || super.isExceptionToRetry(exception)) {
|
259
|
+
return true;
|
260
|
+
}
|
261
|
+
if (exception instanceof ExecutionException || (exception instanceof IOException && exception.getCause() != null)) {
|
262
|
+
return this.toRetry((Exception) exception.getCause());
|
263
|
+
}
|
264
|
+
if (exception instanceof MarketoAPIException) {
|
265
|
+
//Retry Authenticate Exception
|
266
|
+
MarketoError error = ((MarketoAPIException) exception).getMarketoErrors().get(0);
|
267
|
+
String code = error.getCode();
|
268
|
+
switch (code) {
|
269
|
+
case "602":
|
270
|
+
case "601":
|
271
|
+
LOGGER.info("Access token expired");
|
272
|
+
renewAccessToken();
|
273
|
+
return true;
|
274
|
+
case "606":
|
275
|
+
try {
|
276
|
+
Thread.sleep(marketoLimitIntervalMillis);
|
277
|
+
}
|
278
|
+
catch (InterruptedException e) {
|
279
|
+
LOGGER.error("Encounter exception when waiting for interval limit", e);
|
280
|
+
throw new DataException("Exception when wait for interval limit");
|
281
|
+
}
|
282
|
+
return true;
|
283
|
+
case "604":
|
284
|
+
case "608":
|
285
|
+
case "611":
|
286
|
+
case "615":
|
287
|
+
case "713":
|
288
|
+
case "1029":
|
289
|
+
return true;
|
290
|
+
default:
|
291
|
+
return false;
|
292
|
+
}
|
293
|
+
}
|
294
|
+
return false;
|
295
|
+
}
|
296
|
+
});
|
297
|
+
}
|
298
|
+
|
299
|
+
@Override
|
300
|
+
public void close()
|
301
|
+
{
|
302
|
+
if (retryHelper != null) {
|
303
|
+
retryHelper.close();
|
304
|
+
}
|
305
|
+
}
|
306
|
+
}
|