Skip to content

Commit cf8aad2

Browse files
committed
Merge pull request #232 from jakub-plichta/master
Support universal connector processes polling Close #239
2 parents 7d4e0be + 837c195 commit cf8aad2

File tree

7 files changed

+178
-38
lines changed

7 files changed

+178
-38
lines changed

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

Lines changed: 47 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,10 @@
2020

2121
import java.io.IOException;
2222
import java.util.Collection;
23+
import java.util.Map;
2324

2425
import static com.gooddata.util.Validate.notNull;
26+
import static java.lang.String.format;
2527

2628
/**
2729
* Service for connector integration creation, update of its settings or execution of its process.
@@ -197,31 +199,55 @@ public FutureResult<ProcessStatus> executeProcess(final Project project, final P
197199
try {
198200
final UriResponse response = restTemplate
199201
.postForObject(ProcessStatus.URL, execution, UriResponse.class, project.getId(), connectorType);
200-
return new PollResult<>(this, new SimplePollHandler<ProcessStatus>(response.getUri(), ProcessStatus.class) {
201-
@Override
202-
public boolean isFinished(final ClientHttpResponse response) throws IOException {
203-
final ProcessStatus process = extractData(response, ProcessStatus.class);
204-
return process.isFinished();
205-
}
206-
207-
@Override
208-
public void handlePollResult(final ProcessStatus pollResult) {
209-
super.handlePollResult(pollResult);
210-
if (pollResult.isFailed()) {
211-
throw new ConnectorException(connectorType + " process failed: " +
212-
pollResult.getStatus().getDescription());
213-
}
214-
}
215-
216-
@Override
217-
public void handlePollException(final GoodDataRestException e) {
218-
throw new ConnectorException(connectorType + " process failed: " + e.getText(), e);
219-
}
220-
});
202+
return createProcessPollResult(response.getUri());
221203
} catch (GoodDataRestException | RestClientException e) {
222204
throw new ConnectorException("Unable to execute " + connectorType + " process", e);
223205
}
224206
}
225207

226208

209+
/**
210+
* Gets status of provided connector process.
211+
* <p/>
212+
* You can use process retrieved by <code>getXXXProcess</code> methods on {@link Integration} as well as a result of
213+
* {@link ConnectorService#executeProcess(Project, ProcessExecution)}.
214+
*
215+
* @param process process to be executed
216+
* @return executed process
217+
* @throws ConnectorException if process execution fails
218+
*/
219+
public FutureResult<ProcessStatus> getProcessStatus(final IntegrationProcessStatus process) {
220+
notNull(process, "process");
221+
notNull(process.getUri(), "process.getUri");
222+
return createProcessPollResult(process.getUri());
223+
}
224+
225+
private FutureResult<ProcessStatus> createProcessPollResult(final String uri) {
226+
final Map<String, String> match = IntegrationProcessStatus.TEMPLATE.match(uri);
227+
final String connectorType = match.get("connector");
228+
final String processId = match.get("process");
229+
return new PollResult<>(this, new SimplePollHandler<ProcessStatus>(uri, ProcessStatus.class) {
230+
@Override
231+
public boolean isFinished(final ClientHttpResponse response) throws IOException {
232+
final ProcessStatus process = extractData(response, ProcessStatus.class);
233+
return process.isFinished();
234+
}
235+
236+
@Override
237+
public void handlePollResult(final ProcessStatus pollResult) {
238+
super.handlePollResult(pollResult);
239+
if (pollResult.isFailed()) {
240+
throw new ConnectorException(format("%s process %s failed: %s", connectorType, processId,
241+
pollResult.getStatus().getDescription()));
242+
}
243+
}
244+
245+
@Override
246+
public void handlePollException(final GoodDataRestException e) {
247+
throw new ConnectorException(format("%s process %s failed: %s", connectorType, processId,
248+
e.getText()), e);
249+
}
250+
});
251+
}
252+
227253
}

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: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,16 @@
22

33
import com.gooddata.AbstractGoodDataIT;
44
import com.gooddata.GoodDataException;
5-
import com.gooddata.JsonMatchers;
65
import com.gooddata.gdc.UriResponse;
76
import com.gooddata.project.Project;
8-
import com.gooddata.util.ResourceUtils;
97
import org.testng.annotations.BeforeMethod;
108
import org.testng.annotations.Test;
119

1210
import static com.gooddata.JsonMatchers.serializesToJson;
1311
import static com.gooddata.connector.Status.Code.ERROR;
1412
import static com.gooddata.connector.Status.Code.SYNCHRONIZED;
1513
import static com.gooddata.util.ResourceUtils.*;
14+
import static java.util.Collections.singletonMap;
1615
import static net.jadler.Jadler.onRequest;
1716
import static org.hamcrest.CoreMatchers.is;
1817
import static org.hamcrest.CoreMatchers.notNullValue;
@@ -118,7 +117,7 @@ public void shouldExecuteProcess() throws Exception {
118117
assertThat(process.getStatus().getCode(), is(SYNCHRONIZED.name()));
119118
}
120119

121-
@Test(expectedExceptions = ConnectorException.class, expectedExceptionsMessageRegExp = ".*process failed.*")
120+
@Test(expectedExceptions = ConnectorException.class, expectedExceptionsMessageRegExp = ".*zendesk4 process PROCESS failed.*")
122121
public void shouldFailExecuteProcessPolling() throws Exception {
123122
onRequest()
124123
.havingMethodEqualTo("POST")
@@ -149,6 +148,45 @@ public void shouldFailExecuteProcess() throws Exception {
149148
assertThat(process.getStatus().getCode(), is(ERROR.name()));
150149
}
151150

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