Skip to content

Commit 4a6fe4e

Browse files
committed
fixes according to review
1 parent 2c8d2d0 commit 4a6fe4e

File tree

13 files changed

+158
-90
lines changed

13 files changed

+158
-90
lines changed

docs/SendGrid-batchsource.md

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,22 @@
22

33
Description
44
-----------
5-
This plugin used to query SendGrid v3 API.
6-
5+
Plugin fetches data from SendGrid. SendGrid is a cloud-based service that assists businesses with email delivery. For
6+
the end user SendGrid provides information about existing Marketing Campaigns, Email Analytics, Bounces, Spam Reports.
7+
78
Properties
89
----------
910
### General
1011

1112
**Reference Name:** Name used to uniquely identify this source for lineage, annotating metadata, etc.
1213

13-
**Authentication type:** The way, how user would like to be authenticated to the SendGrid acoount
14+
**Authentication type:** The way, how user would like to be authenticated to the SendGrid account
1415

1516
**API Key:** The SendGrid API Key taken from the SendGrid account
1617

17-
**Username:** Login name for SendGrid
18+
**Username:** Login name for the SendGrid account
1819

19-
**Password:** Login password for the username specified above
20+
**Password:** Password for the SendGrid account
2021

2122
**Data Source Types:** List of data source groups
2223

@@ -25,7 +26,7 @@ Available:
2526
- Stats Fields
2627
- Suppressions Fields
2728

28-
**Data Source:** One of the above sources picked from list
29+
**Data Source:** SendGrid source object
2930

3031
Available:
3132
- Marketing Campaigns Fields
@@ -42,7 +43,18 @@ Available:
4243
- Bounces
4344
- Global Unsubscribes
4445
- Group Unsubscribes
45-
**Data Source Fields:**
46+
47+
**Data Source Fields:** The list of fields available for the retrieval
48+
49+
- *Automation:* id, name, status, type, message_count, created_at, updated_at, live_at
50+
- *SingleSends:* id, name, status, created_at, updated_at,
51+
- *Senders:* id, nickname, address, address_2, city, country, state, zip, locked, created_at, updated_at, from(email, name), verified(status, reason), reply_to(name, email)
52+
- *Contacts:* id, first_name, last_name, list_ids, created_at, updated_at, email, Segments, id, name, parent_list_id, created_at, updated_at, sample_updated_at, contacts_count
53+
- *GlobalStats:* date ,blocks ,bounce_drops ,bounces ,clicks ,deferred ,invalid_emails ,opens ,processed ,requests ,spam_report_drops ,spam_reports ,unique_clicks ,unique_opens ,unsubscribe_drops ,unsubscribes
54+
- *CategoryStats:* name, type, date, blocks, bounce_drops, bounces, clicks, deferred, delivered, invalid_emails, opens, processed, requests, spam_report_drops, spam_reports, unique_clicks, unique_opens, unsubscribe_drops, unsubscribes
55+
- *AdvancedStats:* name, type, date, clicks, opens, unique_clicks, unique_opens
56+
- *Bounces:* created, email, reason, status
57+
- *GroupUnsubscribes:* id ,name ,description ,is_default ,last_email_send_at ,unsubscribes
4658

4759
**Start Date:** The date in format YYYY-MM-DD, starting from which the data is requested
4860

pom.xml

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,29 @@
2828
<properties>
2929
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
3030
<cdap.version>6.1.0-SNAPSHOT</cdap.version>
31-
<cdap.plugin.version>2.2.0-SNAPSHOT</cdap.plugin.version>
32-
<hadoop.version>2.3.0</hadoop.version>
33-
<hydrator.version>2.2.0</hydrator.version>
31+
<hadoop.version>2.8.0</hadoop.version>
32+
<hydrator.version>2.3.0-SNAPSHOT</hydrator.version>
3433
<gson.version>2.2.4</gson.version>
3534
<common.logging.version>1.2</common.logging.version>
3635
</properties>
3736

37+
<repositories>
38+
<repository>
39+
<id>sonatype</id>
40+
<url>https://oss.sonatype.org/content/groups/public</url>
41+
</repository>
42+
<repository>
43+
<id>sonatype-snapshots</id>
44+
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
45+
</repository>
46+
</repositories>
47+
<pluginRepositories>
48+
<pluginRepository>
49+
<id>sonatype</id>
50+
<url>https://oss.sonatype.org/content/groups/public/</url>
51+
</pluginRepository>
52+
</pluginRepositories>
53+
3854

3955
<dependencies>
4056
<dependency>
@@ -84,7 +100,7 @@
84100
<dependency>
85101
<groupId>io.cdap.plugin</groupId>
86102
<artifactId>hydrator-common</artifactId>
87-
<version>${cdap.plugin.version}</version>
103+
<version>${hydrator.version}</version>
88104
</dependency>
89105
<dependency>
90106
<groupId>org.apache.hadoop</groupId>
@@ -213,11 +229,7 @@
213229
<extensions>true</extensions>
214230
<configuration>
215231
<instructions>
216-
<_exportcontents>
217-
io.cdap.plugin.*;
218-
org.apache.commons.lang;
219-
org.apache.commons.logging.*;
220-
</_exportcontents>
232+
<_exportcontents>io.cdap.plugin.sendgrid.*</_exportcontents>
221233
<Embed-Dependency>*;inline=false;scope=compile</Embed-Dependency>
222234
<Embed-Transitive>true</Embed-Transitive>
223235
<Embed-Directory>lib</Embed-Directory>

src/main/java/io/cdap/plugin/sendgrid/common/SendGridClient.java

Lines changed: 34 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@
3131
import java.io.IOException;
3232
import java.lang.reflect.ParameterizedType;
3333
import java.lang.reflect.Type;
34-
import java.net.ConnectException;
3534
import java.util.ArrayList;
3635
import java.util.Base64;
3736
import java.util.List;
@@ -85,7 +84,7 @@ public SendGridClient(SendGridBatchSourceConfig config) {
8584
} else if (config.getAuthType() == SendGridAuthType.BASIC) {
8685
sendGrid = new SendGridAPIClient(config.getAuthUserName(), config.getAuthPassword());
8786
} else {
88-
throw new IllegalArgumentException(String.format("Invalid %s authentication method",
87+
throw new IllegalArgumentException(String.format("Invalid authentication method '%s'",
8988
SendGridClient.class.getCanonicalName()));
9089
}
9190
}
@@ -107,10 +106,10 @@ public SendGridClient(String username, String password) {
107106
* @param endpoint relative uri to be queried
108107
* @param arguments arguments for the query
109108
* @return query body
110-
* @throws ConnectException if any issue with query the API happen
109+
* @throws IOException if any issue with query the API happen
111110
*/
112111
private String makeApiRequest(Method method, String endpoint, @Nullable Map<String, String> arguments)
113-
throws ConnectException {
112+
throws IOException {
114113

115114
Request request = new Request();
116115
request.setMethod(method);
@@ -124,8 +123,7 @@ private String makeApiRequest(Method method, String endpoint, @Nullable Map<Stri
124123
try {
125124
response = sendGrid.api(request);
126125
} catch (IOException e) {
127-
throw new ConnectException(String.format("Request to SendGrid API \"%s\"failed with: %s", endpoint,
128-
e.getMessage()));
126+
throw new IOException(String.format("Request to SendGrid API \"%s\"", endpoint), e);
129127
}
130128

131129
return response.getBody();
@@ -136,13 +134,17 @@ private String makeApiRequest(Method method, String endpoint, @Nullable Map<Stri
136134
*
137135
* @param objectInfo objects definition
138136
* @param arguments query arguments
139-
* @throws ConnectException if any validation issue
137+
* @throws IllegalArgumentException if any validation issue
140138
*/
141-
private void checkIncomingArguments(ObjectInfo objectInfo, Map<String, String> arguments) throws ConnectException {
139+
private void checkIncomingArguments(ObjectInfo objectInfo, Map<String, String> arguments)
140+
throws IllegalArgumentException {
141+
142142
if (objectInfo.getRequiredArguments() != null && !objectInfo.getRequiredArguments().isEmpty()) {
143143
if (arguments == null || arguments.isEmpty()) {
144-
throw new ConnectException(String.format("Object %s require input arguments to be passed, nothing found",
145-
objectInfo.getCdapObjectName()));
144+
throw new IllegalArgumentException(String.format(
145+
"Object '%s' require input arguments to be passed, nothing found",
146+
objectInfo.getCdapObjectName()
147+
));
146148
}
147149
List<String> exceptions = new ArrayList<>();
148150

@@ -155,14 +157,17 @@ private void checkIncomingArguments(ObjectInfo objectInfo, Map<String, String> a
155157
arguments.keySet().stream()
156158
.filter(x::equals)
157159
.findFirst()
158-
.orElseThrow(() -> new ConnectException(String.format("Object %s require %s argument, but nothing provided",
159-
objectInfo.getCdapObjectName(), x)));
160-
} catch (ConnectException e) {
160+
.orElseThrow(() -> new IllegalArgumentException(String.format(
161+
"Object '%s' require %s argument, but nothing provided",
162+
objectInfo.getCdapObjectName(),
163+
x
164+
)));
165+
} catch (IllegalArgumentException e) {
161166
exceptions.add(e.getMessage());
162167
}
163168
});
164169
if (!exceptions.isEmpty()) {
165-
throw new ConnectException(exceptions.stream().collect(Collectors.joining(System.lineSeparator())));
170+
throw new IllegalArgumentException(exceptions.stream().collect(Collectors.joining(System.lineSeparator())));
166171
}
167172
}
168173
}
@@ -173,9 +178,12 @@ private void checkIncomingArguments(ObjectInfo objectInfo, Map<String, String> a
173178
* @param objectInfo objects definition
174179
* @param arguments query arguments
175180
* @return object representation of the query
176-
* @throws ConnectException if any issue with query the API happen
181+
* @throws IOException if any issue with query the API happen
182+
* @throws IllegalStateException unsupported response type caught
183+
* @throws IllegalArgumentException if any validation issue
177184
*/
178-
public List<IBaseObject> getObject(ObjectInfo objectInfo, Map<String, String> arguments) throws ConnectException {
185+
public List<IBaseObject> getObject(ObjectInfo objectInfo, Map<String, String> arguments)
186+
throws IOException, IllegalStateException, IllegalArgumentException {
179187
checkIncomingArguments(objectInfo, arguments);
180188

181189
String endpoint = objectInfo.getSendGridAPIUrl();
@@ -222,8 +230,10 @@ public Type getOwnerType() {
222230

223231
return gson.fromJson(response, typeToken);
224232
} else {
225-
throw new ConnectException(String.format("Unsupported API Response type: %s",
226-
objectInfo.getApiResponseType().name()));
233+
throw new IllegalStateException(String.format(
234+
"Unsupported API Response type: %s",
235+
objectInfo.getApiResponseType().name()
236+
));
227237
}
228238
}
229239

@@ -232,13 +242,13 @@ public Type getOwnerType() {
232242
*
233243
* @param objectInfo objects definition
234244
* @return object representation of the query
235-
* @throws ConnectException if any issue with query the API happen
245+
* @throws IOException if any issue with query the API happen
246+
* @throws IllegalStateException unsupported response type caught
247+
* @throws IllegalArgumentException if any validation issue
236248
*/
237-
public List<IBaseObject> getObject(ObjectInfo objectInfo) throws ConnectException {
249+
public List<IBaseObject> getObject(ObjectInfo objectInfo)
250+
throws IOException, IllegalStateException, IllegalArgumentException {
251+
238252
return getObject(objectInfo, null);
239253
}
240-
241-
242-
243-
244254
}

src/main/java/io/cdap/plugin/sendgrid/common/config/BaseSourceConfig.java

Lines changed: 45 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -17,18 +17,22 @@
1717

1818
import com.google.common.base.Strings;
1919
import com.google.common.collect.ImmutableList;
20+
import com.google.common.collect.ImmutableMap;
21+
2022
import io.cdap.cdap.api.annotation.Description;
2123
import io.cdap.cdap.api.annotation.Macro;
2224
import io.cdap.cdap.api.annotation.Name;
2325
import io.cdap.cdap.api.data.schema.Schema;
2426
import io.cdap.cdap.etl.api.FailureCollector;
2527
import io.cdap.plugin.common.ReferencePluginConfig;
28+
import io.cdap.plugin.sendgrid.common.helpers.ObjectDefinition;
2629
import io.cdap.plugin.sendgrid.common.helpers.ObjectHelper;
2730
import io.cdap.plugin.sendgrid.common.objects.SendGridAuthType;
2831

2932
import java.util.Arrays;
3033
import java.util.Collections;
3134
import java.util.List;
35+
import java.util.Map;
3236

3337
import javax.annotation.Nullable;
3438

@@ -47,76 +51,83 @@ public class BaseSourceConfig extends ReferencePluginConfig {
4751
public static final String PROPERTY_DATA_SOURCE_SUPPRESSIONS = PROPERTY_DATA_SOURCE + "Suppressions";
4852

4953
public static final String PROPERTY_DATA_SOURCE_FIELDS = "dataSourceFields";
54+
public static final String PROPERTY_STAT_CATEGORIES = "statCategories";
5055
public static final String PROPERTY_START_DATE = "startDate";
5156
public static final String PROPERTY_END_DATE = "endDate";
5257

5358
@Name(PROPERTY_AUTH_TYPE)
54-
@Description("Auth type")
59+
@Description("The way, how user would like to be authenticated to the SendGrid account")
5560
@Macro
5661
private String authType;
5762

5863
@Name((PROPERTY_SENDGRID_API_KEY))
59-
@Description("Api key")
64+
@Description("The SendGrid API Key taken from the SendGrid account")
6065
@Macro
6166
@Nullable
6267
private String sendGridApiKey;
6368

6469
@Name(PROPERTY_AUTH_USERNAME)
65-
@Description("Username")
70+
@Description("Login name for the SendGrid account")
6671
@Macro
6772
@Nullable
6873
private String authUserName;
6974

7075
@Name(PROPERTY_AUTH_PASSWORD)
71-
@Description("Password")
76+
@Description("Password for the SendGrid account")
7277
@Macro
7378
@Nullable
7479
private String authPassword;
7580

7681
@Name(PROPERTY_DATA_SOURCE_TYPES)
77-
@Description("Data source categories")
82+
@Description("List of data source groups")
7883
@Macro
7984
private String dataSourceTypes;
8085

8186
@Name(PROPERTY_DATA_SOURCE_MARKETING)
82-
@Description("Data source marketing objects")
87+
@Description("SendGrid source objects for the Marketing group")
8388
@Macro
8489
@Nullable
8590
private String dataSourceMarketing;
8691

8792
@Name(PROPERTY_DATA_SOURCE_STATS)
88-
@Description("Data source stats objects")
93+
@Description("SendGrid source objects for the Statistics group")
8994
@Macro
9095
@Nullable
9196
private String dataSourceStats;
9297

9398
@Name(PROPERTY_DATA_SOURCE_SUPPRESSIONS)
94-
@Description("Data source suppression objects")
99+
@Description("SendGrid source objects for the Suppressions group")
95100
@Macro
96101
@Nullable
97102
private String dataSourceSuppressions;
98103

99104
@Name(PROPERTY_DATA_SOURCE_FIELDS)
100-
@Description("Data source objects fields")
105+
@Description("The list of fields available for the retrieval")
101106
@Macro
102107
@Nullable
103108
private String dataSourceFields;
104109

105110
@Name(PROPERTY_START_DATE)
106-
@Description("Request information starting from")
111+
@Description("The date in format YYYY-MM-DD, starting from which the data is requested")
107112
@Nullable
108113
@Macro
109114
private String startDate;
110115

111116
@Name(PROPERTY_END_DATE)
112-
@Description("End period of the requested information")
117+
@Description("The date in format YYYY-MM-DD, the end date for the requested data")
113118
@Nullable
114119
@Macro
115120
private String endDate;
116121

117-
private Schema schema;
118-
private List<String> dataSource;
119-
private Boolean multiObjectMode;
122+
@Name(PROPERTY_STAT_CATEGORIES)
123+
@Description("List of requested categories for the CategoryStats request")
124+
@Nullable
125+
@Macro
126+
private String statCategories;
127+
128+
private transient Schema schema;
129+
private transient List<String> dataSource;
130+
private transient Boolean multiObjectMode;
120131

121132
/**
122133
* Constructor
@@ -193,6 +204,25 @@ public Schema getSchema(List<String> dataSource) {
193204
return ObjectHelper.buildSchema(dataSource, getFields(), isMultiObjectMode());
194205
}
195206

207+
/**
208+
* Returns query properties required for some SendGrid Objects
209+
* marked with {@link ObjectDefinition#RequiredArguments()}
210+
*/
211+
public Map<String, String> getRequestArguments() {
212+
ImmutableMap.Builder<String, String> builder = new ImmutableMap.Builder<>();
213+
214+
if (!Strings.isNullOrEmpty(startDate)) {
215+
builder.put(PROPERTY_START_DATE, startDate);
216+
}
217+
if (!Strings.isNullOrEmpty(endDate)) {
218+
builder.put(PROPERTY_END_DATE, endDate);
219+
}
220+
if (!Strings.isNullOrEmpty(statCategories)) {
221+
builder.put(PROPERTY_END_DATE, statCategories);
222+
}
223+
return builder.build();
224+
}
225+
196226
public void validate(FailureCollector failureCollector) {
197227

198228
}
@@ -207,7 +237,7 @@ public SendGridAuthType getAuthType() {
207237
case "basic":
208238
return SendGridAuthType.BASIC;
209239
default:
210-
return SendGridAuthType.UNKNOWN;
240+
throw new IllegalArgumentException(String.format("Authentication using '%s' is not supported", authType));
211241
}
212242
}
213243

0 commit comments

Comments
 (0)