Skip to content

Commit 4b4f2b4

Browse files
committed
Support universal connector processes polling
1 parent 934e0e7 commit 4b4f2b4

File tree

7 files changed

+169
-14
lines changed

7 files changed

+169
-14
lines changed

src/main/java/com/gooddata/connector/ConnectorService.java

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import java.util.Collection;
2323

2424
import static com.gooddata.util.Validate.notNull;
25+
import static java.lang.String.format;
2526

2627
/**
2728
* Service for connector integration creation, update of its settings or execution of its process.
@@ -224,4 +225,42 @@ public void handlePollException(final GoodDataRestException e) {
224225
}
225226

226227

228+
/**
229+
* Polls on provided connector process
230+
*
231+
* @param process process to be executed
232+
* @return executed process
233+
* @throws ConnectorException if process execution fails
234+
*/
235+
public FutureResult<ProcessStatus> pollProcess(final IntegrationProcessStatus process) {
236+
notNull(process, "process");
237+
notNull(process.getUri(), "process.getUri");
238+
239+
try {
240+
return new PollResult<>(this, new SimplePollHandler<ProcessStatus>(process.getUri(), ProcessStatus.class) {
241+
@Override
242+
public boolean isFinished(final ClientHttpResponse response) throws IOException {
243+
final ProcessStatus process = extractData(response, ProcessStatus.class);
244+
return process.isFinished();
245+
}
246+
247+
@Override
248+
public void handlePollResult(final ProcessStatus pollResult) {
249+
super.handlePollResult(pollResult);
250+
if (pollResult.isFailed()) {
251+
throw new ConnectorException(format("process %s failed: %s", process.getId(),
252+
pollResult.getStatus().getDescription()));
253+
}
254+
}
255+
256+
@Override
257+
public void handlePollException(final GoodDataRestException e) {
258+
throw new ConnectorException(format("process %s failed: %s", process.getId(), e.getText()), e);
259+
}
260+
});
261+
} catch (GoodDataRestException | RestClientException e) {
262+
throw new ConnectorException("Unable to poll process " + process.getId(), e);
263+
}
264+
}
265+
227266
}

src/main/java/com/gooddata/connector/IntegrationProcessStatus.java

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@
1212
import org.codehaus.jackson.map.annotate.JsonDeserialize;
1313
import org.codehaus.jackson.map.annotate.JsonSerialize;
1414
import org.joda.time.DateTime;
15+
import org.springframework.web.util.UriTemplate;
16+
17+
import java.util.Map;
1518

1619
/**
1720
* Connector process (i.e. single ETL run) status used in integration object. Deserialization only.
@@ -20,17 +23,24 @@
2023
@JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL)
2124
public class IntegrationProcessStatus {
2225

26+
public static final String URI = "/gdc/projects/{project}/connectors/{connector}/integration/processes/{process}";
27+
public static final UriTemplate TEMPLATE = new UriTemplate(URI);
28+
private static final String SELF_LINK = "self";
29+
2330
private final Status status;
2431
private final DateTime started;
2532
private final DateTime finished;
33+
private Map<String, String> links;
2634

2735
@JsonCreator
2836
protected IntegrationProcessStatus(@JsonProperty("status") Status status,
2937
@JsonProperty("started") @JsonDeserialize(using = ISODateTimeDeserializer.class) DateTime started,
30-
@JsonProperty("finished") @JsonDeserialize(using = ISODateTimeDeserializer.class) DateTime finished) {
38+
@JsonProperty("finished") @JsonDeserialize(using = ISODateTimeDeserializer.class) DateTime finished,
39+
@JsonProperty("links") Map<String, String> links) {
3140
this.status = status;
3241
this.started = started;
3342
this.finished = finished;
43+
this.links = links;
3444
}
3545

3646
public Status getStatus() {
@@ -68,4 +78,14 @@ public boolean isFinished() {
6878
public boolean isFailed() {
6979
return status != null && status.isFailed();
7080
}
81+
82+
@JsonIgnore
83+
public String getUri() {
84+
return links != null ? links.get(SELF_LINK): null;
85+
}
86+
87+
@JsonIgnore
88+
public String getId() {
89+
return TEMPLATE.match(getUri()).get("process");
90+
}
7191
}

src/main/java/com/gooddata/connector/ProcessStatus.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,10 @@
1010
import org.codehaus.jackson.annotate.JsonTypeInfo;
1111
import org.codehaus.jackson.annotate.JsonTypeName;
1212
import org.codehaus.jackson.map.annotate.JsonDeserialize;
13-
import org.codehaus.jackson.map.annotate.JsonSerialize;
1413
import org.joda.time.DateTime;
1514

15+
import java.util.Map;
16+
1617
/**
1718
* Connector process (i.e. single ETL run) status (standalone, not embedded in integration as its parent) .
1819
* Deserialization only.
@@ -27,8 +28,9 @@ public class ProcessStatus extends IntegrationProcessStatus {
2728
@JsonCreator
2829
ProcessStatus(@JsonProperty("status") Status status,
2930
@JsonProperty("started") @JsonDeserialize(using = ISODateTimeDeserializer.class) DateTime started,
30-
@JsonProperty("finished") @JsonDeserialize(using = ISODateTimeDeserializer.class) DateTime finished) {
31-
super(status, started, finished);
31+
@JsonProperty("finished") @JsonDeserialize(using = ISODateTimeDeserializer.class) DateTime finished,
32+
@JsonProperty("links") Map<String, String> links) {
33+
super(status, started, finished, links);
3234
}
3335

3436
}

src/test/java/com/gooddata/connector/ConnectorServiceIT.java

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import static com.gooddata.connector.Status.Code.ERROR;
1414
import static com.gooddata.connector.Status.Code.SYNCHRONIZED;
1515
import static com.gooddata.util.ResourceUtils.*;
16+
import static java.util.Collections.singletonMap;
1617
import static net.jadler.Jadler.onRequest;
1718
import static org.hamcrest.CoreMatchers.is;
1819
import static org.hamcrest.CoreMatchers.notNullValue;
@@ -149,6 +150,45 @@ public void shouldFailExecuteProcess() throws Exception {
149150
assertThat(process.getStatus().getCode(), is(ERROR.name()));
150151
}
151152

153+
@Test
154+
public void shouldPollProcess() throws Exception {
155+
onRequest()
156+
.havingPathEqualTo("/gdc/projects/PROJECT_ID/connectors/zendesk4/integration/processes/PROCESS_ID")
157+
.respond()
158+
.withBody(readFromResource("/connector/process-status-scheduled.json"))
159+
.thenRespond()
160+
.withBody(readFromResource("/connector/process-status-finished.json"));
161+
162+
final IntegrationProcessStatus runningProcess = new IntegrationProcessStatus(null, null, null,
163+
singletonMap("self", "/gdc/projects/PROJECT_ID/connectors/zendesk4/integration/processes/PROCESS_ID"));
164+
final ProcessStatus process = connectors.pollProcess(runningProcess).get();
165+
assertThat(process.getStatus().getCode(), is(SYNCHRONIZED.name()));
166+
}
167+
168+
@Test(expectedExceptions = ConnectorException.class, expectedExceptionsMessageRegExp = ".*process PROCESS_ID failed.*")
169+
public void shouldFailPollProcessPolling() throws Exception {
170+
onRequest()
171+
.havingPathEqualTo("/gdc/projects/PROJECT_ID/connectors/zendesk4/integration/processes/PROCESS_ID")
172+
.respond()
173+
.withStatus(400);
174+
final IntegrationProcessStatus runningProcess = new IntegrationProcessStatus(null, null, null,
175+
singletonMap("self", "/gdc/projects/PROJECT_ID/connectors/zendesk4/integration/processes/PROCESS_ID"));
176+
connectors.pollProcess(runningProcess).get();
177+
}
178+
179+
@Test(expectedExceptions = GoodDataException.class)
180+
public void shouldFailPollProcess() throws Exception {
181+
onRequest()
182+
.havingPathEqualTo("/gdc/projects/PROJECT_ID/connectors/zendesk4/integration/processes/PROCESS_ID")
183+
.respond()
184+
.withBody(readFromResource("/connector/process-status-error.json"));
185+
186+
final IntegrationProcessStatus runningProcess = new IntegrationProcessStatus(null, null, null,
187+
singletonMap("self", "/gdc/projects/PROJECT_ID/connectors/zendesk4/integration/processes/PROCESS_ID"));
188+
final ProcessStatus process = connectors.pollProcess(runningProcess).get();
189+
assertThat(process.getStatus().getCode(), is(ERROR.name()));
190+
}
191+
152192
@Test
153193
public void shouldGetSettings() throws Exception {
154194
onRequest()

src/test/java/com/gooddata/connector/IntegrationProcessStatusTest.java

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
import org.joda.time.DateTimeZone;
99
import org.testng.annotations.Test;
1010

11+
import java.util.Collections;
12+
1113
import static com.gooddata.connector.Status.Code.ERROR;
1214
import static com.gooddata.connector.Status.Code.SYNCHRONIZED;
1315
import static com.gooddata.connector.Status.Code.UPLOADING;
@@ -32,65 +34,67 @@ public void testShouldDeserialize() throws Exception {
3234
assertThat(process.getStatus().getCode(), is(ERROR.name()));
3335
assertThat(process.getStatus().getDetail(), is("GDC-INTERNAL-ERROR"));
3436
assertThat(process.getStatus().getDescription(), is(nullValue()));
37+
assertThat(process.getUri(), is("/gdc/projects/PROJECT_ID/connectors/zendesk4/integration/processes/FINISHED_PROCESS_ID"));
38+
assertThat(process.getId(), is("FINISHED_PROCESS_ID"));
3539
}
3640

3741
@Test
3842
public void testIsFinishedOnError() throws Exception {
39-
final IntegrationProcessStatus process = new IntegrationProcessStatus(new Status(ERROR.name(), "", ""), now(), now());
43+
final IntegrationProcessStatus process = new IntegrationProcessStatus(new Status(ERROR.name(), "", ""), now(), now(), Collections.<String, String>emptyMap());
4044
assertThat(process.isFinished(), is(true));
4145
}
4246

4347
@Test
4448
public void testIsFinishedOnSynchronized() throws Exception {
45-
final IntegrationProcessStatus process = new IntegrationProcessStatus(new Status(SYNCHRONIZED.name(), "", ""), now(), now());
49+
final IntegrationProcessStatus process = new IntegrationProcessStatus(new Status(SYNCHRONIZED.name(), "", ""), now(), now(), Collections.<String, String>emptyMap());
4650
assertThat(process.isFinished(), is(true));
4751
}
4852

4953
@Test
5054
public void testIsFinishedOnUploading() throws Exception {
51-
final IntegrationProcessStatus process = new IntegrationProcessStatus(new Status(UPLOADING.name(), "", ""), now(), now());
55+
final IntegrationProcessStatus process = new IntegrationProcessStatus(new Status(UPLOADING.name(), "", ""), now(), now(), Collections.<String, String>emptyMap());
5256
assertThat(process.isFinished(), is(false));
5357
}
5458

5559
@Test
5660
public void testIsFinishedOnNullCode() throws Exception {
57-
final IntegrationProcessStatus process = new IntegrationProcessStatus(new Status(null, "", ""), now(), now());
61+
final IntegrationProcessStatus process = new IntegrationProcessStatus(new Status(null, "", ""), now(), now(), Collections.<String, String>emptyMap());
5862
assertThat(process.isFinished(), is(false));
5963
}
6064

6165
@Test
6266
public void testIsFinishedOnUnknownCode() throws Exception {
63-
final IntegrationProcessStatus process = new IntegrationProcessStatus(new Status("unknown code", "", ""), now(), now());
67+
final IntegrationProcessStatus process = new IntegrationProcessStatus(new Status("unknown code", "", ""), now(), now(), Collections.<String, String>emptyMap());
6468
assertThat(process.isFinished(), is(false));
6569
}
6670

6771
@Test
6872
public void testIsFailedOnError() throws Exception {
69-
final IntegrationProcessStatus process = new IntegrationProcessStatus(new Status(ERROR.name(), "", ""), now(), now());
73+
final IntegrationProcessStatus process = new IntegrationProcessStatus(new Status(ERROR.name(), "", ""), now(), now(), Collections.<String, String>emptyMap());
7074
assertThat(process.isFailed(), is(true));
7175
}
7276

7377
@Test
7478
public void testIsFailedOnUserError() throws Exception {
75-
final IntegrationProcessStatus process = new IntegrationProcessStatus(new Status(USER_ERROR.name(), "", ""), now(), now());
79+
final IntegrationProcessStatus process = new IntegrationProcessStatus(new Status(USER_ERROR.name(), "", ""), now(), now(), Collections.<String, String>emptyMap());
7680
assertThat(process.isFailed(), is(true));
7781
}
7882

7983
@Test
8084
public void testIsFailedOnSynchronized() throws Exception {
81-
final IntegrationProcessStatus process = new IntegrationProcessStatus(new Status(SYNCHRONIZED.name(), "", ""), now(), now());
85+
final IntegrationProcessStatus process = new IntegrationProcessStatus(new Status(SYNCHRONIZED.name(), "", ""), now(), now(), Collections.<String, String>emptyMap());
8286
assertThat(process.isFailed(), is(false));
8387
}
8488

8589
@Test
8690
public void testIsFailedOnNullCode() throws Exception {
87-
final IntegrationProcessStatus process = new IntegrationProcessStatus(new Status(null, "", ""), now(), now());
91+
final IntegrationProcessStatus process = new IntegrationProcessStatus(new Status(null, "", ""), now(), now(), Collections.<String, String>emptyMap());
8892
assertThat(process.isFailed(), is(false));
8993
}
9094

9195
@Test
9296
public void testIsFailedOnUnknownCode() throws Exception {
93-
final IntegrationProcessStatus process = new IntegrationProcessStatus(new Status("unknown code", "", ""), now(), now());
97+
final IntegrationProcessStatus process = new IntegrationProcessStatus(new Status("unknown code", "", ""), now(), now(), Collections.<String, String>emptyMap());
9498
assertThat(process.isFailed(), is(false));
9599
}
96100

src/test/java/com/gooddata/connector/ProcessStatusTest.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ public void shouldDeserialize() throws Exception {
2222
assertThat(process.getStatus().getCode(), is(ERROR.name()));
2323
assertThat(process.getStatus().getDetail(), is("GDC-INTERNAL-ERROR"));
2424
assertThat(process.getStatus().getDescription(), is("Data load unsuccessful. Please check your settings and try again or contact us at support@gooddata.com"));
25+
assertThat(process.getUri(), is("/gdc/projects/PROJECT_ID/connectors/zendesk4/integration/processes/PROCESS_ID"));
26+
assertThat(process.getId(), is("PROCESS_ID"));
2527
}
2628

2729
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
{
2+
"integration" : {
3+
"projectTemplate" : "/projectTemplates/template",
4+
"active" : true,
5+
"lastFinishedProcess" : {
6+
"status" : {
7+
"code" : "ERROR",
8+
"detail" : "GDC-INTERNAL-ERROR",
9+
"description" : "Data load unsuccessful. Please check your settings and try again or contact us at support@gooddata.com"
10+
},
11+
"started" : "2014-05-30T07:50:15.000Z",
12+
"finished" : "2014-05-30T07:50:50.000Z",
13+
"links" : {
14+
"self" : "/gdc/projects/PROJECT_ID/connectors/zendesk4/integration/processes/FINISHED_PROCESS_ID"
15+
}
16+
},
17+
"lastSuccessfulProcess" : {
18+
"status" : {
19+
"code" : "SYNCHRONIZED",
20+
"detail" : null,
21+
"description" : null
22+
},
23+
"started" : "2014-04-30T07:50:15.000Z",
24+
"finished" : "2014-04-30T07:50:50.000Z",
25+
"links" : {
26+
"self" : "/gdc/projects/PROJECT_ID/connectors/zendesk4/integration/processes/SUCCESSFUL_PROCESS_ID"
27+
}
28+
},
29+
"runningProcess" : {
30+
"status" : {
31+
"code" : "SCHEDULED",
32+
"detail" : "",
33+
"description" : ""
34+
},
35+
"started" : "2014-04-30T07:50:15.000Z",
36+
"finished" : null,
37+
"links" : {
38+
"self" : "/gdc/projects/PROJECT_ID/connectors/zendesk4/integration/processes/PROCESS_ID"
39+
}
40+
},
41+
"links" : {
42+
"self" : "/gdc/projects/PROJECT_ID/connectors/zendesk4/integration",
43+
"processes" : "/gdc/projects/PROJECT_ID/connectors/zendesk4/integration/processes",
44+
"configuration" : "/gdc/projects/PROJECT_ID/connectors/zendesk4/integration/settings"
45+
},
46+
"ui" : { }
47+
}
48+
}

0 commit comments

Comments
 (0)