embulk-input-marketo-extended 0.6.18

Sign up to get free protection for your applications and to get access to all the features.
Files changed (88) hide show
  1. checksums.yaml +7 -0
  2. data/.github/PULL_REQUEST_TEMPLATE.md +37 -0
  3. data/.gitignore +14 -0
  4. data/.travis.yml +6 -0
  5. data/CHANGELOG.md +170 -0
  6. data/LICENSE.txt +21 -0
  7. data/README.md +213 -0
  8. data/build.gradle +103 -0
  9. data/config/checkstyle/checkstyle.xml +128 -0
  10. data/config/checkstyle/default.xml +108 -0
  11. data/gradle/wrapper/gradle-wrapper.jar +0 -0
  12. data/gradle/wrapper/gradle-wrapper.properties +6 -0
  13. data/gradlew +169 -0
  14. data/gradlew.bat +84 -0
  15. data/lib/embulk/input/marketo.rb +3 -0
  16. data/settings.gradle +1 -0
  17. data/src/main/java/org/embulk/input/marketo/CsvTokenizer.java +700 -0
  18. data/src/main/java/org/embulk/input/marketo/MarketoInputPlugin.java +15 -0
  19. data/src/main/java/org/embulk/input/marketo/MarketoInputPluginDelegate.java +100 -0
  20. data/src/main/java/org/embulk/input/marketo/MarketoService.java +38 -0
  21. data/src/main/java/org/embulk/input/marketo/MarketoServiceImpl.java +245 -0
  22. data/src/main/java/org/embulk/input/marketo/MarketoUtils.java +212 -0
  23. data/src/main/java/org/embulk/input/marketo/delegate/ActivityBulkExtractInputPlugin.java +167 -0
  24. data/src/main/java/org/embulk/input/marketo/delegate/CampaignInputPlugin.java +48 -0
  25. data/src/main/java/org/embulk/input/marketo/delegate/CustomObjectInputPlugin.java +75 -0
  26. data/src/main/java/org/embulk/input/marketo/delegate/CustomObjectResponseMapperBuilder.java +81 -0
  27. data/src/main/java/org/embulk/input/marketo/delegate/LeadBulkExtractInputPlugin.java +66 -0
  28. data/src/main/java/org/embulk/input/marketo/delegate/LeadServiceResponseMapperBuilder.java +85 -0
  29. data/src/main/java/org/embulk/input/marketo/delegate/LeadWithListInputPlugin.java +64 -0
  30. data/src/main/java/org/embulk/input/marketo/delegate/LeadWithProgramInputPlugin.java +60 -0
  31. data/src/main/java/org/embulk/input/marketo/delegate/MarketoBaseBulkExtractInputPlugin.java +441 -0
  32. data/src/main/java/org/embulk/input/marketo/delegate/MarketoBaseInputPluginDelegate.java +92 -0
  33. data/src/main/java/org/embulk/input/marketo/delegate/ProgramInputPlugin.java +228 -0
  34. data/src/main/java/org/embulk/input/marketo/exception/MarketoAPIException.java +30 -0
  35. data/src/main/java/org/embulk/input/marketo/model/BulkExtractRangeHeader.java +26 -0
  36. data/src/main/java/org/embulk/input/marketo/model/MarketoAccessTokenResponse.java +92 -0
  37. data/src/main/java/org/embulk/input/marketo/model/MarketoBulkExtractRequest.java +68 -0
  38. data/src/main/java/org/embulk/input/marketo/model/MarketoError.java +40 -0
  39. data/src/main/java/org/embulk/input/marketo/model/MarketoField.java +126 -0
  40. data/src/main/java/org/embulk/input/marketo/model/MarketoResponse.java +82 -0
  41. data/src/main/java/org/embulk/input/marketo/model/filter/DateRangeFilter.java +40 -0
  42. data/src/main/java/org/embulk/input/marketo/rest/MarketoBaseRestClient.java +306 -0
  43. data/src/main/java/org/embulk/input/marketo/rest/MarketoInputStreamResponseEntityReader.java +69 -0
  44. data/src/main/java/org/embulk/input/marketo/rest/MarketoRESTEndpoint.java +47 -0
  45. data/src/main/java/org/embulk/input/marketo/rest/MarketoResponseJetty92EntityReader.java +89 -0
  46. data/src/main/java/org/embulk/input/marketo/rest/MarketoRestClient.java +569 -0
  47. data/src/main/java/org/embulk/input/marketo/rest/RecordPagingIterable.java +180 -0
  48. data/src/test/java/org/embulk/input/marketo/MarketoServiceImplTest.java +140 -0
  49. data/src/test/java/org/embulk/input/marketo/MarketoUtilsTest.java +87 -0
  50. data/src/test/java/org/embulk/input/marketo/delegate/ActivityBulkExtractInputPluginTest.java +128 -0
  51. data/src/test/java/org/embulk/input/marketo/delegate/CampaignInputPluginTest.java +73 -0
  52. data/src/test/java/org/embulk/input/marketo/delegate/CustomObjectInputPluginTest.java +102 -0
  53. data/src/test/java/org/embulk/input/marketo/delegate/LeadBulkExtractInputPluginTest.java +99 -0
  54. data/src/test/java/org/embulk/input/marketo/delegate/LeadServiceResponseMapperBuilderTest.java +119 -0
  55. data/src/test/java/org/embulk/input/marketo/delegate/LeadWithListInputPluginTest.java +101 -0
  56. data/src/test/java/org/embulk/input/marketo/delegate/LeadWithProgramInputPluginTest.java +103 -0
  57. data/src/test/java/org/embulk/input/marketo/delegate/MarketoBaseBulkExtractInputPluginTest.java +169 -0
  58. data/src/test/java/org/embulk/input/marketo/delegate/ProgramInputPluginTest.java +343 -0
  59. data/src/test/java/org/embulk/input/marketo/rest/MarketoBaseRestClientTest.java +368 -0
  60. data/src/test/java/org/embulk/input/marketo/rest/MarketoRestClientTest.java +584 -0
  61. data/src/test/resources/config/activity_bulk_extract_config.yaml +7 -0
  62. data/src/test/resources/config/custom_object_config.yaml +8 -0
  63. data/src/test/resources/config/lead_bulk_extract_config.yaml +8 -0
  64. data/src/test/resources/config/rest_config.yaml +3 -0
  65. data/src/test/resources/fixtures/activity_extract1.csv +35 -0
  66. data/src/test/resources/fixtures/activity_extract2.csv +22 -0
  67. data/src/test/resources/fixtures/activity_types.json +22 -0
  68. data/src/test/resources/fixtures/all_program_full.json +53 -0
  69. data/src/test/resources/fixtures/campaign_response.json +38 -0
  70. data/src/test/resources/fixtures/campaign_response_full.json +102 -0
  71. data/src/test/resources/fixtures/custom_object_describe.json +124 -0
  72. data/src/test/resources/fixtures/custom_object_describe_marketo_fields_full.json +22 -0
  73. data/src/test/resources/fixtures/custom_object_expected.json +66 -0
  74. data/src/test/resources/fixtures/custom_object_response.json +24 -0
  75. data/src/test/resources/fixtures/custom_object_response_full.json +23 -0
  76. data/src/test/resources/fixtures/lead_by_list.json +33 -0
  77. data/src/test/resources/fixtures/lead_by_program_response.json +47 -0
  78. data/src/test/resources/fixtures/lead_describe.json +221 -0
  79. data/src/test/resources/fixtures/lead_describe_expected.json +66 -0
  80. data/src/test/resources/fixtures/lead_describe_marketo_fields_full.json +518 -0
  81. data/src/test/resources/fixtures/lead_extract1.csv +11 -0
  82. data/src/test/resources/fixtures/lead_response_full.json +2402 -0
  83. data/src/test/resources/fixtures/lead_with_program_full.json +17 -0
  84. data/src/test/resources/fixtures/leads_extract2.csv +10 -0
  85. data/src/test/resources/fixtures/list_reponse_full.json +191 -0
  86. data/src/test/resources/fixtures/lists_response.json +31 -0
  87. data/src/test/resources/fixtures/program_response.json +71 -0
  88. metadata +171 -0
@@ -0,0 +1,103 @@
1
+ package org.embulk.input.marketo.delegate;
2
+
3
+ import com.fasterxml.jackson.databind.JavaType;
4
+ import com.fasterxml.jackson.databind.ObjectMapper;
5
+ import com.fasterxml.jackson.databind.node.ObjectNode;
6
+ import org.apache.commons.lang3.StringUtils;
7
+ import org.embulk.EmbulkTestRuntime;
8
+ import org.embulk.base.restclient.ServiceResponseMapper;
9
+ import org.embulk.base.restclient.record.RecordImporter;
10
+ import org.embulk.base.restclient.record.ValueLocator;
11
+ import org.embulk.config.ConfigLoader;
12
+ import org.embulk.config.ConfigSource;
13
+ import org.embulk.input.marketo.MarketoUtils;
14
+ import org.embulk.input.marketo.model.MarketoField;
15
+ import org.embulk.input.marketo.rest.MarketoRestClient;
16
+ import org.embulk.input.marketo.rest.RecordPagingIterable;
17
+ import org.embulk.spi.PageBuilder;
18
+ import org.embulk.spi.Schema;
19
+ import org.junit.Before;
20
+ import org.junit.Rule;
21
+ import org.junit.Test;
22
+ import org.mockito.ArgumentCaptor;
23
+ import org.mockito.Mockito;
24
+
25
+ import java.io.IOException;
26
+ import java.util.ArrayList;
27
+ import java.util.List;
28
+
29
+ import static org.junit.Assert.assertEquals;
30
+ import static org.mockito.ArgumentMatchers.any;
31
+ import static org.mockito.ArgumentMatchers.anyString;
32
+ import static org.mockito.ArgumentMatchers.eq;
33
+
34
+ /**
35
+ * Created by tai.khuu on 10/10/17.
36
+ */
37
+ public class LeadWithProgramInputPluginTest
38
+ {
39
+ private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
40
+
41
+ @Rule
42
+ public EmbulkTestRuntime embulkTestRuntime = new EmbulkTestRuntime();
43
+
44
+ private ConfigSource configSource;
45
+
46
+ private LeadWithProgramInputPlugin leadWithProgramInputPlugin;
47
+
48
+ private MarketoRestClient mockMarketoRestClient;
49
+
50
+ @Before
51
+ public void setUp() throws Exception
52
+ {
53
+ leadWithProgramInputPlugin = Mockito.spy(new LeadWithProgramInputPlugin());
54
+ ConfigLoader configLoader = embulkTestRuntime.getInjector().getInstance(ConfigLoader.class);
55
+ configSource = configLoader.fromYaml(this.getClass().getResourceAsStream("/config/rest_config.yaml"));
56
+ mockMarketoRestClient = Mockito.mock(MarketoRestClient.class);
57
+ Mockito.doReturn(mockMarketoRestClient).when(leadWithProgramInputPlugin).createMarketoRestClient(any(LeadWithProgramInputPlugin.PluginTask.class));
58
+ }
59
+
60
+ @Test
61
+ public void testRun() throws IOException
62
+ {
63
+ RecordPagingIterable<ObjectNode> mockLeadRecordPagingIterable = Mockito.mock(RecordPagingIterable.class);
64
+ RecordPagingIterable<ObjectNode> mockLeadEmptyRecordPagingIterable = Mockito.mock(RecordPagingIterable.class);
65
+ RecordPagingIterable<ObjectNode> mockProgramRecords = Mockito.mock(RecordPagingIterable.class);
66
+
67
+ Mockito.when(mockLeadEmptyRecordPagingIterable.iterator()).thenReturn(new ArrayList<ObjectNode>().iterator());
68
+ JavaType objectNodeListType = OBJECT_MAPPER.getTypeFactory().constructParametrizedType(List.class, List.class, ObjectNode.class);
69
+ JavaType marketoFieldsType = OBJECT_MAPPER.getTypeFactory().constructParametrizedType(List.class, List.class, MarketoField.class);
70
+ List<ObjectNode> leads = OBJECT_MAPPER.readValue(this.getClass().getResourceAsStream("/fixtures/lead_with_program_full.json"), objectNodeListType);
71
+ List<ObjectNode> programs = OBJECT_MAPPER.readValue(this.getClass().getResourceAsStream("/fixtures/all_program_full.json"), objectNodeListType);
72
+ Mockito.when(mockProgramRecords.iterator()).thenReturn(programs.iterator());
73
+ List<MarketoField> marketoFields = OBJECT_MAPPER.readValue(this.getClass().getResourceAsStream("/fixtures/lead_describe_marketo_fields_full.json"), marketoFieldsType);
74
+ Mockito.when(mockLeadRecordPagingIterable.iterator()).thenReturn(leads.iterator());
75
+ Mockito.when(mockMarketoRestClient.describeLead()).thenReturn(marketoFields);
76
+ Mockito.when(mockMarketoRestClient.getPrograms()).thenReturn(mockProgramRecords);
77
+ List<String> fieldNameFromMarketoFields = MarketoUtils.getFieldNameFromMarketoFields(marketoFields);
78
+ String fieldNameString = StringUtils.join(fieldNameFromMarketoFields, ",");
79
+ Mockito.when(mockMarketoRestClient.getLeadsByProgram(anyString(), eq(fieldNameString))).thenReturn(mockLeadEmptyRecordPagingIterable);
80
+ Mockito.when(mockMarketoRestClient.getLeadsByProgram("1003", fieldNameString)).thenReturn(mockLeadRecordPagingIterable);
81
+
82
+ LeadWithProgramInputPlugin.PluginTask task = configSource.loadConfig(LeadWithProgramInputPlugin.PluginTask.class);
83
+ ServiceResponseMapper<? extends ValueLocator> mapper = leadWithProgramInputPlugin.buildServiceResponseMapper(task);
84
+
85
+ RecordImporter recordImporter = mapper.createRecordImporter();
86
+ PageBuilder mockPageBuilder = Mockito.mock(PageBuilder.class);
87
+
88
+ leadWithProgramInputPlugin.ingestServiceData(task, recordImporter, 1, mockPageBuilder);
89
+ Mockito.verify(mockMarketoRestClient, Mockito.times(1)).getPrograms();
90
+ Mockito.verify(mockMarketoRestClient, Mockito.times(3)).getLeadsByProgram(anyString(), eq(fieldNameString));
91
+ Mockito.verify(mockMarketoRestClient, Mockito.times(1)).describeLead();
92
+
93
+ Schema embulkSchema = mapper.getEmbulkSchema();
94
+ ArgumentCaptor<Long> longArgumentCaptor = ArgumentCaptor.forClass(Long.class);
95
+
96
+ Mockito.verify(mockPageBuilder, Mockito.times(1)).setLong(eq(embulkSchema.lookupColumn("mk_id")), longArgumentCaptor.capture());
97
+ Mockito.verify(mockPageBuilder, Mockito.times(1)).setString(eq(embulkSchema.lookupColumn("mk_programId")), eq("1003"));
98
+
99
+ List<Long> allValues = longArgumentCaptor.getAllValues();
100
+ long actualValue = allValues.get(0);
101
+ assertEquals(102519L, actualValue);
102
+ }
103
+ }
@@ -0,0 +1,169 @@
1
+ package org.embulk.input.marketo.delegate;
2
+
3
+ import com.google.common.base.Optional;
4
+
5
+ import org.embulk.EmbulkTestRuntime;
6
+ import org.embulk.config.ConfigDiff;
7
+ import org.embulk.config.ConfigException;
8
+ import org.embulk.config.ConfigLoader;
9
+ import org.embulk.config.ConfigSource;
10
+ import org.embulk.config.TaskReport;
11
+ import org.embulk.input.marketo.MarketoInputPluginDelegate;
12
+ import org.embulk.input.marketo.MarketoUtils;
13
+ import org.embulk.spi.Schema;
14
+ import org.joda.time.DateTime;
15
+ import org.junit.Before;
16
+ import org.junit.Rule;
17
+ import org.junit.Test;
18
+ import org.mockito.ArgumentCaptor;
19
+ import org.mockito.Mockito;
20
+
21
+ import java.io.IOException;
22
+ import java.text.DateFormat;
23
+ import java.text.SimpleDateFormat;
24
+ import java.util.Arrays;
25
+ import java.util.Date;
26
+
27
+ import static org.junit.Assert.assertEquals;
28
+ import static org.junit.Assert.fail;
29
+ import static org.mockito.AdditionalAnswers.delegatesTo;
30
+ import static org.mockito.Mockito.mock;
31
+ import static org.mockito.Mockito.when;
32
+
33
+ /**
34
+ * Created by khuutantaitai on 10/3/17.
35
+ */
36
+ public class MarketoBaseBulkExtractInputPluginTest
37
+ {
38
+ @Rule
39
+ public EmbulkTestRuntime embulkTestRuntime = new EmbulkTestRuntime();
40
+
41
+ private MarketoBaseBulkExtractInputPlugin<MarketoBaseBulkExtractInputPlugin.PluginTask> baseBulkExtractInputPlugin;
42
+ private MarketoBaseBulkExtractInputPlugin.PluginTask validBaseTask;
43
+
44
+ @Before
45
+ public void prepare() throws IOException
46
+ {
47
+ baseBulkExtractInputPlugin = Mockito.mock(MarketoBaseBulkExtractInputPlugin.class, Mockito.CALLS_REAL_METHODS);
48
+ ConfigLoader configLoader = embulkTestRuntime.getInjector().getInstance(ConfigLoader.class);
49
+ ConfigSource configSource = configLoader.fromYaml(
50
+ this.getClass().getResourceAsStream("/config/activity_bulk_extract_config.yaml"));
51
+ validBaseTask = configSource.loadConfig(MarketoBaseBulkExtractInputPlugin.PluginTask.class);
52
+ }
53
+
54
+ @Test(expected = ConfigException.class)
55
+ public void validateInputTaskError()
56
+ {
57
+ MarketoBaseBulkExtractInputPlugin.PluginTask pluginTask = Mockito.mock(MarketoBaseBulkExtractInputPlugin.PluginTask.class);
58
+ Mockito.when(pluginTask.getFromDate()).thenReturn(null);
59
+ baseBulkExtractInputPlugin.validateInputTask(pluginTask);
60
+ }
61
+
62
+ @Test(expected = ConfigException.class)
63
+ public void invalidInputTaskWhenIncrementalByUpdatedAt()
64
+ {
65
+ MarketoBaseBulkExtractInputPlugin.PluginTask task = mock(
66
+ MarketoBaseBulkExtractInputPlugin.PluginTask.class,
67
+ delegatesTo(validBaseTask));
68
+ when(task.getIncrementalColumn()).thenReturn(Optional.of("updatedAt"));
69
+ when(task.getIncremental()).thenReturn(true);
70
+ baseBulkExtractInputPlugin.validateInputTask(task);
71
+ }
72
+
73
+ @Test
74
+ public void validInputTaskWhenIncrementalOtherThanUpdatedAt()
75
+ {
76
+ MarketoBaseBulkExtractInputPlugin.PluginTask task = mock(
77
+ MarketoBaseBulkExtractInputPlugin.PluginTask.class,
78
+ delegatesTo(validBaseTask));
79
+ when(task.getIncremental()).thenReturn(true);
80
+ when(task.getIncrementalColumn()).thenReturn(Optional.of("anythingButUpdatedAt"));
81
+ baseBulkExtractInputPlugin.validateInputTask(task); // should not throw
82
+ }
83
+
84
+ @Test
85
+ public void validInputTaskWhenNonIncrementalWhileSetUpdatedAt()
86
+ {
87
+ MarketoBaseBulkExtractInputPlugin.PluginTask task = mock(
88
+ MarketoBaseBulkExtractInputPlugin.PluginTask.class,
89
+ delegatesTo(validBaseTask));
90
+ when(task.getIncremental()).thenReturn(false);
91
+ when(task.getIncrementalColumn()).thenReturn(Optional.of("updatedAt"));
92
+ baseBulkExtractInputPlugin.validateInputTask(task); // should not throw
93
+ }
94
+
95
+ @Test()
96
+ public void validateInputTaskToDateLessThanJobStartTime()
97
+ {
98
+ Date fromDate = new Date(1504224000000L);
99
+ DateTime jobStartTime = new DateTime(1506842144000L);
100
+ MarketoBaseBulkExtractInputPlugin.PluginTask pluginTask = Mockito.mock(MarketoBaseBulkExtractInputPlugin.PluginTask.class);
101
+ Mockito.when(pluginTask.getFromDate()).thenReturn(fromDate);
102
+ Mockito.when(pluginTask.getJobStartTime()).thenReturn(jobStartTime);
103
+ Mockito.when(pluginTask.getFetchDays()).thenReturn(7);
104
+ baseBulkExtractInputPlugin.validateInputTask(pluginTask);
105
+ ArgumentCaptor<Optional<Date>> argumentCaptor = ArgumentCaptor.forClass(Optional.class);
106
+ Mockito.verify(pluginTask, Mockito.times(1)).setToDate(argumentCaptor.capture());
107
+ assertEquals(1504828800000L, argumentCaptor.getValue().get().getTime());
108
+ }
109
+
110
+ @Test()
111
+ public void validateInputTaskFromDateMoreThanJobStartTime()
112
+ {
113
+ Date fromDate = new Date(1507619744000L);
114
+ DateTime jobStartTime = new DateTime(1506842144000L);
115
+ MarketoBaseBulkExtractInputPlugin.PluginTask pluginTask = Mockito.mock(MarketoBaseBulkExtractInputPlugin.PluginTask.class);
116
+ Mockito.when(pluginTask.getFromDate()).thenReturn(fromDate);
117
+ Mockito.when(pluginTask.getJobStartTime()).thenReturn(jobStartTime);
118
+
119
+ try {
120
+ baseBulkExtractInputPlugin.validateInputTask(pluginTask);
121
+ }
122
+ catch (ConfigException ex) {
123
+ return;
124
+ }
125
+ fail();
126
+ }
127
+
128
+ @Test()
129
+ public void validateInputTaskToDateMoreThanJobStartTime()
130
+ {
131
+ Date fromDate = new Date(1504224000000L);
132
+ DateTime jobStartTime = new DateTime(1504396800000L);
133
+ MarketoBaseBulkExtractInputPlugin.PluginTask pluginTask = Mockito.mock(MarketoBaseBulkExtractInputPlugin.PluginTask.class);
134
+ Mockito.when(pluginTask.getFromDate()).thenReturn(fromDate);
135
+ Mockito.when(pluginTask.getFetchDays()).thenReturn(7);
136
+ Mockito.when(pluginTask.getJobStartTime()).thenReturn(jobStartTime);
137
+ baseBulkExtractInputPlugin.validateInputTask(pluginTask);
138
+ ArgumentCaptor<Optional<Date>> toDateArgumentCaptor = ArgumentCaptor.forClass(Optional.class);
139
+ Mockito.verify(pluginTask, Mockito.times(1)).setToDate(toDateArgumentCaptor.capture());
140
+ assertEquals(jobStartTime.getMillis(), toDateArgumentCaptor.getValue().get().getTime());
141
+ }
142
+
143
+ @Test
144
+ public void getToDate() throws Exception
145
+ {
146
+ DateTime date = new DateTime(1505033728000L);
147
+ DateTime jobStartTime = new DateTime(1507625728000L);
148
+ MarketoBaseBulkExtractInputPlugin.PluginTask pluginTask = Mockito.mock(MarketoInputPluginDelegate.PluginTask.class);
149
+ Mockito.when(pluginTask.getFromDate()).thenReturn(date.toDate());
150
+ Mockito.when(pluginTask.getFetchDays()).thenReturn(30);
151
+ Mockito.when(pluginTask.getJobStartTime()).thenReturn(jobStartTime);
152
+ DateTime toDate = baseBulkExtractInputPlugin.getToDate(pluginTask);
153
+ assertEquals(toDate, jobStartTime);
154
+ }
155
+
156
+ @Test
157
+ public void buildConfigDiff() throws Exception
158
+ {
159
+ TaskReport taskReport1 = Mockito.mock(TaskReport.class);
160
+ MarketoInputPluginDelegate.PluginTask task = Mockito.mock(MarketoInputPluginDelegate.PluginTask.class);
161
+ Mockito.when(task.getIncremental()).thenReturn(true);
162
+ Mockito.when(task.getIncrementalColumn()).thenReturn(Optional.of("createdAt"));
163
+ Date toDate = new Date(1507625728000L);
164
+ Mockito.when(task.getToDate()).thenReturn(Optional.of(toDate));
165
+ ConfigDiff configDiff = baseBulkExtractInputPlugin.buildConfigDiff(task, Mockito.mock(Schema.class), 1, Arrays.asList(taskReport1));
166
+ DateFormat df = new SimpleDateFormat(MarketoUtils.MARKETO_DATE_SIMPLE_DATE_FORMAT);
167
+ assertEquals(df.format(toDate), configDiff.get(String.class, "from_date"));
168
+ }
169
+ }
@@ -0,0 +1,343 @@
1
+ package org.embulk.input.marketo.delegate;
2
+
3
+ import com.fasterxml.jackson.core.JsonParseException;
4
+ import com.fasterxml.jackson.databind.JavaType;
5
+ import com.fasterxml.jackson.databind.JsonMappingException;
6
+ import com.fasterxml.jackson.databind.ObjectMapper;
7
+ import com.fasterxml.jackson.databind.node.ObjectNode;
8
+ import com.google.common.base.Optional;
9
+ import com.google.common.base.Predicate;
10
+ import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
11
+ import org.embulk.EmbulkTestRuntime;
12
+ import org.embulk.base.restclient.ServiceResponseMapper;
13
+ import org.embulk.base.restclient.record.RecordImporter;
14
+ import org.embulk.base.restclient.record.ValueLocator;
15
+ import org.embulk.config.ConfigDiff;
16
+ import org.embulk.config.ConfigException;
17
+ import org.embulk.config.ConfigLoader;
18
+ import org.embulk.config.ConfigSource;
19
+ import org.embulk.config.TaskReport;
20
+ import org.embulk.input.marketo.MarketoUtils;
21
+ import org.embulk.input.marketo.delegate.ProgramInputPlugin.PluginTask;
22
+ import org.embulk.input.marketo.delegate.ProgramInputPlugin.QueryBy;
23
+ import org.embulk.input.marketo.rest.MarketoRestClient;
24
+ import org.embulk.input.marketo.rest.RecordPagingIterable;
25
+ import org.embulk.spi.PageBuilder;
26
+ import org.embulk.spi.Schema;
27
+ import org.joda.time.DateTime;
28
+ import org.joda.time.Duration;
29
+ import org.joda.time.format.DateTimeFormat;
30
+ import org.joda.time.format.DateTimeFormatter;
31
+ import org.junit.Before;
32
+ import org.junit.Rule;
33
+ import org.junit.Test;
34
+ import org.junit.rules.ExpectedException;
35
+ import org.junit.rules.RuleChain;
36
+ import org.junit.rules.TestRule;
37
+ import org.mockito.ArgumentCaptor;
38
+ import org.mockito.Mockito;
39
+
40
+ import java.io.IOException;
41
+ import java.util.Arrays;
42
+ import java.util.Date;
43
+ import java.util.List;
44
+
45
+ import static org.junit.Assert.assertArrayEquals;
46
+ import static org.junit.Assert.assertEquals;
47
+
48
+ public class ProgramInputPluginTest
49
+ {
50
+ private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormat.forPattern(MarketoUtils.MARKETO_DATE_SIMPLE_DATE_FORMAT);
51
+
52
+ private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
53
+
54
+ @Rule
55
+ public EmbulkTestRuntime runtime = new EmbulkTestRuntime();
56
+
57
+ private ExpectedException thrown = ExpectedException.none();
58
+
59
+ @Rule
60
+ @SuppressFBWarnings("URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
61
+ public TestRule chain = RuleChain.outerRule(runtime).around(thrown);
62
+
63
+ private ConfigSource baseConfig;
64
+
65
+ private ProgramInputPlugin mockPlugin;
66
+
67
+ private MarketoRestClient mockRestClient;
68
+
69
+ @Before
70
+ public void setUp() throws Exception
71
+ {
72
+ mockPlugin = Mockito.spy(new ProgramInputPlugin());
73
+ baseConfig = config();
74
+ mockRestClient = Mockito.mock(MarketoRestClient.class);
75
+ Mockito.doReturn(mockRestClient).when(mockPlugin).createMarketoRestClient(Mockito.any(ProgramInputPlugin.PluginTask.class));
76
+ }
77
+
78
+ // -----------Verify configs --------------
79
+ @Test
80
+ public void testQueryByTagTypeConfigMissingTagType()
81
+ {
82
+ thrown.expect(ConfigException.class);
83
+ thrown.expectMessage("tag_type and tag_value are required when query by Tag Type");
84
+ ConfigSource config = baseConfig.set("query_by", Optional.of(QueryBy.TAG_TYPE)).set("tag_value", Optional.of("dummy"));
85
+ mockPlugin.validateInputTask(config.loadConfig(PluginTask.class));
86
+ }
87
+
88
+ @Test
89
+ public void testQueryByTagTypeConfigMissingTagValue()
90
+ {
91
+ thrown.expect(ConfigException.class);
92
+ thrown.expectMessage("tag_type and tag_value are required when query by Tag Type");
93
+ ConfigSource config = baseConfig.set("query_by", Optional.of(QueryBy.TAG_TYPE)).set("tag_type", Optional.of("dummy"));
94
+ mockPlugin.validateInputTask(config.loadConfig(PluginTask.class));
95
+ }
96
+
97
+ @Test
98
+ public void testQueryByDateRangeConfigMissingEarliestUpatedAt()
99
+ {
100
+ thrown.expect(ConfigException.class);
101
+ thrown.expectMessage("`earliest_updated_at` is required when query by Date Range");
102
+ ConfigSource config = baseConfig.set("query_by", Optional.of(QueryBy.DATE_RANGE)).set("latest_updated_at", Optional.of(DateTime.now().minusDays(10).toDate()));
103
+ mockPlugin.validateInputTask(config.loadConfig(PluginTask.class));
104
+ }
105
+
106
+ @Test
107
+ public void testQueryByDateRangeConfigMissingLatestUpdatedAt()
108
+ {
109
+ thrown.expect(ConfigException.class);
110
+ thrown.expectMessage("`latest_updated_at` is required when query by Date Range");
111
+ ConfigSource config = baseConfig.set("query_by", Optional.of(QueryBy.DATE_RANGE)).set("earliest_updated_at", Optional.of(DateTime.now().minusDays(10).toDate()));
112
+ mockPlugin.validateInputTask(config.loadConfig(PluginTask.class));
113
+ }
114
+
115
+ @Test
116
+ public void testQueryByDateRangeConfigMissingLatestUpdatedAtNonIncremental()
117
+ {
118
+ thrown.expect(ConfigException.class);
119
+ thrown.expectMessage("`latest_updated_at` is required when query by Date Range");
120
+ ConfigSource config = baseConfig
121
+ .set("query_by", Optional.of(QueryBy.DATE_RANGE))
122
+ .set("incremental", Boolean.FALSE)
123
+ .set("earliest_updated_at", Optional.of(DateTime.now().minusDays(10).toDate()));
124
+ mockPlugin.validateInputTask(config.loadConfig(PluginTask.class));
125
+ }
126
+
127
+ @Test
128
+ public void testNoErrorQueryByDateRangeConfigHasReportDurationNonIncremental()
129
+ {
130
+ ConfigSource config = baseConfig
131
+ .set("query_by", Optional.of(QueryBy.DATE_RANGE))
132
+ .set("report_duration", Optional.of(60L * 1000))
133
+ .set("incremental", Boolean.FALSE)
134
+ .set("earliest_updated_at", Optional.of(DateTime.now().minusDays(10).toDate()));
135
+ mockPlugin.validateInputTask(config.loadConfig(PluginTask.class));
136
+ }
137
+
138
+ @Test
139
+ public void testNoErrorQueryByDateRangeConfigHasReportDuration()
140
+ {
141
+ ConfigSource config = baseConfig
142
+ .set("report_duration", Optional.of(60L * 1000))
143
+ .set("query_by", Optional.of(QueryBy.DATE_RANGE))
144
+ .set("earliest_updated_at", Optional.of(DateTime.now().minusDays(10).toDate()));
145
+ mockPlugin.validateInputTask(config.loadConfig(PluginTask.class));
146
+ }
147
+
148
+ @Test
149
+ public void testQueryByDateRangeConfigHasEarliestUpdatedAtExceededNow()
150
+ {
151
+ DateTime earliestUpdatedAt = DateTime.now().plusDays(1);
152
+ DateTime latestUpdatedAt = DateTime.now().minusDays(2);
153
+ thrown.expect(ConfigException.class);
154
+ thrown.expectMessage(String.format("`earliest_updated_at` (%s) cannot precede the current date ", earliestUpdatedAt.toString(DATE_FORMATTER)));
155
+ ConfigSource config = baseConfig
156
+ .set("query_by", Optional.of(QueryBy.DATE_RANGE))
157
+ .set("earliest_updated_at", Optional.of(earliestUpdatedAt))
158
+ .set("latest_updated_at", Optional.of(latestUpdatedAt));
159
+ mockPlugin.validateInputTask(config.loadConfig(PluginTask.class));
160
+ }
161
+
162
+ @Test
163
+ public void testQueryByDateRangeConfigHasEarliestUpdatedAtExceededLatestUpdatedAt()
164
+ {
165
+ DateTime earliestUpdatedAt = DateTime.now().minusDays(10);
166
+ DateTime latestUpdatedAt = DateTime.now().minusDays(20);
167
+ thrown.expect(ConfigException.class);
168
+ thrown.expectMessage(String.format("Invalid date range. `earliest_updated_at` (%s) cannot precede the `latest_updated_at` (%s).",
169
+ earliestUpdatedAt.toString(DATE_FORMATTER),
170
+ latestUpdatedAt.toString(DATE_FORMATTER)));
171
+
172
+ ConfigSource config = baseConfig
173
+ .set("query_by", Optional.of(QueryBy.DATE_RANGE))
174
+ .set("earliest_updated_at", Optional.of(earliestUpdatedAt))
175
+ .set("latest_updated_at", Optional.of(latestUpdatedAt));
176
+ mockPlugin.validateInputTask(config.loadConfig(PluginTask.class));
177
+ }
178
+
179
+ @Test
180
+ public void testHasFilterTypeButMissingFilterValue()
181
+ {
182
+ DateTime earliestUpdatedAt = DateTime.now().minusDays(20);
183
+ DateTime latestUpdatedAt = DateTime.now().minusDays(10);
184
+ thrown.expect(ConfigException.class);
185
+ thrown.expectMessage("filter_value is required when selected filter_type");
186
+
187
+ ConfigSource config = baseConfig
188
+ .set("query_by", Optional.of(QueryBy.DATE_RANGE))
189
+ .set("earliest_updated_at", Optional.of(earliestUpdatedAt))
190
+ .set("latest_updated_at", Optional.of(latestUpdatedAt))
191
+ .set("filter_type", Optional.of("dummy"));
192
+ mockPlugin.validateInputTask(config.loadConfig(PluginTask.class));
193
+ }
194
+
195
+ @Test
196
+ public void testSkipIncrementalRunIfLastUpdatedAtExceedsNow()
197
+ {
198
+ DateTime earliestUpdatedAt = DateTime.now().minusDays(20);
199
+ DateTime latestUpdatedAt = earliestUpdatedAt.plusDays(21);
200
+ //21 days
201
+ long reportDuration = 21 * 24 * 60 * 60 * 1000;
202
+
203
+ ConfigSource config = baseConfig
204
+ .set("query_by", Optional.of(QueryBy.DATE_RANGE))
205
+ .set("earliest_updated_at", Optional.of(earliestUpdatedAt))
206
+ .set("latest_updated_at", Optional.of(latestUpdatedAt))
207
+ .set("report_duration", reportDuration)
208
+ .set("incremental", true);
209
+ ServiceResponseMapper<? extends ValueLocator> mapper = mockPlugin.buildServiceResponseMapper(config.loadConfig(PluginTask.class));
210
+ RecordImporter recordImporter = mapper.createRecordImporter();
211
+ PageBuilder mockPageBuilder = Mockito.mock(PageBuilder.class);
212
+ TaskReport taskReport = mockPlugin.ingestServiceData(config.loadConfig(PluginTask.class), recordImporter, 1, mockPageBuilder);
213
+ // page builder object should never get called.
214
+ Mockito.verify(mockPageBuilder, Mockito.never()).addRecord();
215
+
216
+ String earliestUpdatedAtStr = taskReport.get(String.class, "earliest_updated_at");
217
+ long duration = taskReport.get(Long.class, "report_duration");
218
+ assertEquals(duration, reportDuration);
219
+ assertEquals(earliestUpdatedAtStr, earliestUpdatedAt.toString(DATE_FORMATTER));
220
+ }
221
+
222
+ @Test
223
+ public void testBuildConfigDiff() throws Exception
224
+ {
225
+ TaskReport taskReport1 = Mockito.mock(TaskReport.class);
226
+ DateTime earliestUpdatedAt = DateTime.now().minusDays(20);
227
+ DateTime latestUpdatedAt = DateTime.now().minusDays(10);
228
+ ConfigSource config = baseConfig
229
+ .set("query_by", Optional.of(QueryBy.DATE_RANGE))
230
+ .set("earliest_updated_at", Optional.of(earliestUpdatedAt))
231
+ .set("latest_updated_at", Optional.of(latestUpdatedAt))
232
+ .set("incremental", true);
233
+
234
+ ConfigDiff diff = mockPlugin.buildConfigDiff(config.loadConfig(PluginTask.class), Mockito.mock(Schema.class), 1, Arrays.asList(taskReport1));
235
+
236
+ long reportDuration = diff.get(Long.class, "report_duration");
237
+ String nextErliestUpdatedAt = diff.get(String.class, "earliest_updated_at");
238
+
239
+ assertEquals(reportDuration, new Duration(earliestUpdatedAt, latestUpdatedAt).getMillis());
240
+ assertEquals(nextErliestUpdatedAt, latestUpdatedAt.plusSeconds(1).toString(DATE_FORMATTER));
241
+ }
242
+
243
+ @SuppressWarnings("unchecked")
244
+ private void testRun(ConfigSource config, Predicate<MarketoRestClient> expectedCall) throws JsonParseException, JsonMappingException, IOException
245
+ {
246
+ // Mock response data
247
+ RecordPagingIterable<ObjectNode> mockRecordPagingIterable = Mockito.mock(RecordPagingIterable.class);
248
+ JavaType javaType = OBJECT_MAPPER.getTypeFactory().constructParametrizedType(List.class, List.class, ObjectNode.class);
249
+ List<ObjectNode> objectNodeList = OBJECT_MAPPER.readValue(this.getClass().getResourceAsStream("/fixtures/all_program_full.json"), javaType);
250
+ Mockito.when(mockRecordPagingIterable.iterator()).thenReturn(objectNodeList.iterator());
251
+ Mockito.when(mockRestClient.getProgramsByTag(Mockito.anyString(), Mockito.anyString())).thenReturn(mockRecordPagingIterable);
252
+ Mockito.when(mockRestClient.getPrograms()).thenReturn(mockRecordPagingIterable);
253
+ Mockito.when(mockRestClient.getProgramsByDateRange(
254
+ Mockito.any(Date.class),
255
+ Mockito.any(Date.class),
256
+ Mockito.nullable(String.class),
257
+ Mockito.nullable(List.class))).thenReturn(mockRecordPagingIterable);
258
+
259
+ ServiceResponseMapper<? extends ValueLocator> mapper = mockPlugin.buildServiceResponseMapper(config.loadConfig(PluginTask.class));
260
+ RecordImporter recordImporter = mapper.createRecordImporter();
261
+ PageBuilder mockPageBuilder = Mockito.mock(PageBuilder.class);
262
+ mockPlugin.ingestServiceData(config.loadConfig(PluginTask.class), recordImporter, 1, mockPageBuilder);
263
+
264
+ // The method getProgramByTag is called 1 time
265
+ // Mockito.verify(mockRestClient, Mockito.times(1)).getProgramsByTag(Mockito.anyString(), Mockito.anyString());
266
+ expectedCall.apply(mockRestClient);
267
+
268
+ Schema embulkSchema = mapper.getEmbulkSchema();
269
+ // 17 columns
270
+ assertEquals(embulkSchema.size(), 17);
271
+ // verify 3 times the method setLong for column id has been called
272
+ ArgumentCaptor<Long> longArgumentCaptor = ArgumentCaptor.forClass(Long.class);
273
+ Mockito.verify(mockPageBuilder, Mockito.times(3)).setLong(Mockito.eq(embulkSchema.lookupColumn("id")), longArgumentCaptor.capture());
274
+ List<Long> allValues = longArgumentCaptor.getAllValues();
275
+ assertArrayEquals(new Long[]{1004L, 1001L, 1003L}, allValues.toArray());
276
+ }
277
+
278
+ @Test
279
+ public void testRunQueryByTagType() throws JsonParseException, JsonMappingException, IOException
280
+ {
281
+ ConfigSource config = baseConfig
282
+ .set("query_by", Optional.of(QueryBy.TAG_TYPE))
283
+ .set("tag_type", Optional.of("dummy"))
284
+ .set("tag_value", Optional.of("dummy"));
285
+ Predicate<MarketoRestClient> expectedCall = new Predicate<MarketoRestClient>()
286
+ {
287
+ @Override
288
+ public boolean apply(MarketoRestClient mockRest)
289
+ {
290
+ Mockito.verify(mockRest, Mockito.times(1)).getProgramsByTag(Mockito.anyString(), Mockito.anyString());
291
+ return true;
292
+ }
293
+ };
294
+ testRun(config, expectedCall);
295
+ }
296
+
297
+ @Test
298
+ public void testRunWithoutQueryBy() throws JsonParseException, JsonMappingException, IOException
299
+ {
300
+ Predicate<MarketoRestClient> expectedCall = new Predicate<MarketoRestClient>()
301
+ {
302
+ @Override
303
+ public boolean apply(MarketoRestClient input)
304
+ {
305
+ Mockito.verify(input, Mockito.times(1)).getPrograms();
306
+ return true;
307
+ }
308
+ };
309
+ testRun(baseConfig, expectedCall);
310
+ }
311
+
312
+ @Test
313
+ public void testRunQueryByDateRange() throws JsonParseException, JsonMappingException, IOException
314
+ {
315
+ DateTime earliestUpdatedAt = DateTime.now().minusDays(20);
316
+ DateTime latestUpdatedAt = DateTime.now().minusDays(10);
317
+ ConfigSource config = baseConfig
318
+ .set("query_by", Optional.of(QueryBy.DATE_RANGE))
319
+ .set("earliest_updated_at", Optional.of(earliestUpdatedAt))
320
+ .set("latest_updated_at", Optional.of(latestUpdatedAt));
321
+ Predicate<MarketoRestClient> expectedCall = new Predicate<MarketoRestClient>()
322
+ {
323
+ @SuppressWarnings("unchecked")
324
+ @Override
325
+ public boolean apply(MarketoRestClient input)
326
+ {
327
+ Mockito.verify(input, Mockito.times(1)).getProgramsByDateRange(
328
+ Mockito.any(Date.class),
329
+ Mockito.any(Date.class),
330
+ Mockito.nullable(String.class),
331
+ Mockito.nullable(List.class));
332
+ return true;
333
+ }
334
+ };
335
+ testRun(config, expectedCall);
336
+ }
337
+
338
+ public ConfigSource config() throws IOException
339
+ {
340
+ ConfigLoader configLoader = runtime.getInjector().getInstance(ConfigLoader.class);
341
+ return configLoader.fromYaml(this.getClass().getResourceAsStream("/config/rest_config.yaml"));
342
+ }
343
+ }