diff --git a/pom.xml b/pom.xml index 3049178d..7f26ffc5 100644 --- a/pom.xml +++ b/pom.xml @@ -50,6 +50,7 @@ UTF-8 + UTF-8 1.6.6 1.1.2 18.0 diff --git a/stream-core/src/main/java/io/getstream/client/model/activities/UpdateTargetResponse.java b/stream-core/src/main/java/io/getstream/client/model/activities/UpdateTargetResponse.java new file mode 100644 index 00000000..5074260c --- /dev/null +++ b/stream-core/src/main/java/io/getstream/client/model/activities/UpdateTargetResponse.java @@ -0,0 +1,80 @@ +package io.getstream.client.model.activities; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.List; + +/** + * update_to_targets's response wrapper class. + * @param Type of the activity in scope. + */ +public class UpdateTargetResponse { + + private T activity; + private String duration; + + @JsonProperty("added") + private List addedTargets; + + @JsonProperty("removed") + private List removedTargets; + + @JsonProperty("new") + private List newTargets; + + public void setDuration(String duration) { + this.duration = duration; + } + + /** + * Duration of the operation. + * @return Duration in human-readable format. + */ + public String getDuration() { + return duration; + } + + public T getActivity() { + return activity; + } + + public void setActivity(T activity) { + this.activity = activity; + } + + /** + * Get a list of added target(s). + * @return List of added target(s) + */ + public List getAddedTargets() { + return addedTargets; + } + + public void setAddedTargets(List addedTargets) { + this.addedTargets = addedTargets; + } + + /** + * Get a list of removed target(s). + * @return List of remove target(s) + */ + public List getRemovedTargets() { + return removedTargets; + } + + public void setRemovedTargets(List removedTargets) { + this.removedTargets = removedTargets; + } + + /** + * Get a list of newly created target(s). + * @return List of newly created target(s) + */ + public List getNewTargets() { + return newTargets; + } + + public void setNewTargets(List newTargets) { + this.newTargets = newTargets; + } +} diff --git a/stream-core/src/main/java/io/getstream/client/model/beans/Targets.java b/stream-core/src/main/java/io/getstream/client/model/beans/Targets.java new file mode 100644 index 00000000..6b31cc91 --- /dev/null +++ b/stream-core/src/main/java/io/getstream/client/model/beans/Targets.java @@ -0,0 +1,126 @@ +package io.getstream.client.model.beans; + +import com.google.common.collect.ImmutableList; + +import java.util.ArrayList; +import java.util.List; + +/** + * This class acts as container for a list of operations intended to alter the target(s) of a given activity. + * A {@link Targets.Builder} is required in order to build a {@link Targets} object. + * + */ +public class Targets { + + private ImmutableList newTargets; + private ImmutableList addedTargets; + private ImmutableList removedTargets; + + public Targets(List newTargets, List addedTargets, List removedTargets) { + if (!newTargets.isEmpty()) { + this.newTargets = ImmutableList.copyOf(newTargets); + } + + if (!addedTargets.isEmpty()) { + this.addedTargets = ImmutableList.copyOf(addedTargets); + } + + if (!removedTargets.isEmpty()) { + this.removedTargets = ImmutableList.copyOf(removedTargets); + } + } + + public List getNewTargets() { + return newTargets; + } + + public List getAddedTargets() { + return addedTargets; + } + + public List getRemovedTargets() { + return removedTargets; + } + + /** + * Builder class for the {@link Targets} object. + */ + public static class Builder { + + private List newTargets = new ArrayList<>(); + private List addedTargets = new ArrayList<>(); + private List removedTargets = new ArrayList<>(); + + /** + * Set a list of new target(s). + * @param newTargets New target(s) + * @return This builder + */ + public Builder setNewTargets(List newTargets) { + this.newTargets = newTargets; + return this; + } + + /** + * Set a list of target(s) to add. + * @param addedTargets Target(s) to add. + * @return This builder + */ + public Builder setAddedTargets(List addedTargets) { + this.addedTargets = addedTargets; + return this; + } + + /** + * Set a list of target(s) to be removed. + * @param removedTargets Target(s) to be removed. + * @return This builder + */ + public Builder setRemovedTargets(List removedTargets) { + this.removedTargets = removedTargets; + return this; + } + + /** + * Change the existing target by specifying a new one. + * @param target New target. + * @return This builder + */ + public Builder addNewTarget(String target) { + this.newTargets.add(target); + return this; + } + + /** + * Add a new target to be added. + * @param target Target to be added. + * @return This builder + */ + public Builder addTargetToAdd(String target) { + this.addedTargets.add(target); + return this; + } + + /** + * Add a target to be removed. + * @param target Target to be removed. + * @return This builder + */ + public Builder addTargetToRemove(String target) { + this.removedTargets.add(target); + return this; + } + + /** + * Build a {@link Targets} object. + * You can specify either new targets or added targets or removed targets or a combinations of added and removed targets. + * @return A valid {@link Targets} object. + */ + public Targets build() { + if (!newTargets.isEmpty() && (!addedTargets.isEmpty() || !removedTargets.isEmpty())) { + throw new IllegalArgumentException("You can specify either new targets or added targets or removed targets or a combinations of added and removed targets."); + } + return new Targets(newTargets, addedTargets, removedTargets); + } + } +} diff --git a/stream-core/src/main/java/io/getstream/client/model/beans/UpdateTo.java b/stream-core/src/main/java/io/getstream/client/model/beans/UpdateTo.java new file mode 100644 index 00000000..2809b00e --- /dev/null +++ b/stream-core/src/main/java/io/getstream/client/model/beans/UpdateTo.java @@ -0,0 +1,49 @@ +package io.getstream.client.model.beans; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import io.getstream.client.model.activities.BaseActivity; + +import java.util.List; + +/** + * This custom activity is required to perform the update_to_targets operation. + */ +public class UpdateTo extends BaseActivity { + + @JsonInclude(JsonInclude.Include.NON_NULL) + @JsonProperty("new_targets") + private List newTargets; + + @JsonInclude(JsonInclude.Include.NON_NULL) + @JsonProperty("added_targets") + private List addedTargets; + + @JsonInclude(JsonInclude.Include.NON_NULL) + @JsonProperty("removed_targets") + private List removedTargets; + + public List getNewTargets() { + return newTargets; + } + + public void setNewTargets(List newTargets) { + this.newTargets = newTargets; + } + + public List getAddedTargets() { + return addedTargets; + } + + public void setAddedTargets(List addedTargets) { + this.addedTargets = addedTargets; + } + + public List getRemovedTargets() { + return removedTargets; + } + + public void setRemovedTargets(List removedTargets) { + this.removedTargets = removedTargets; + } +} diff --git a/stream-core/src/main/java/io/getstream/client/repo/StreamRepository.java b/stream-core/src/main/java/io/getstream/client/repo/StreamRepository.java index 33c3f687..964439ab 100644 --- a/stream-core/src/main/java/io/getstream/client/repo/StreamRepository.java +++ b/stream-core/src/main/java/io/getstream/client/repo/StreamRepository.java @@ -4,11 +4,13 @@ import io.getstream.client.model.activities.AggregatedActivity; import io.getstream.client.model.activities.BaseActivity; import io.getstream.client.model.activities.NotificationActivity; +import io.getstream.client.model.activities.UpdateTargetResponse; import io.getstream.client.model.beans.FeedFollow; import io.getstream.client.model.beans.FollowMany; import io.getstream.client.model.beans.MarkedActivity; import io.getstream.client.model.beans.StreamActivitiesResponse; import io.getstream.client.model.beans.StreamResponse; +import io.getstream.client.model.beans.Targets; import io.getstream.client.model.beans.UnfollowMany; import io.getstream.client.model.feeds.BaseFeed; import io.getstream.client.model.filters.FeedFilter; @@ -163,6 +165,18 @@ public interface StreamRepository { */ StreamActivitiesResponse updateActivities(BaseFeed feed, Class type, List activities) throws IOException, StreamClientException; + /** + * Update the to target(s) of a given {@link T} activity. + * @param feed Feed which the activity belong to + * @param activity The activity in scope. + * @param targets A container for the operations that need to be performed on the to field. + * @param The type of the activity. + * @return Response containing the modified activity and a summary of the actions performed to the to field. + * @throws IOException in case of network/socket exceptions + * @throws StreamClientException in case of functional or server-side exception + */ + UpdateTargetResponse updateToTargets(BaseFeed feed, BaseActivity activity, Targets targets) throws StreamClientException, IOException; + /** * Add a new activity of type {@link T} to multiple feeds. * diff --git a/stream-core/src/main/java/io/getstream/client/service/AbstractActivityService.java b/stream-core/src/main/java/io/getstream/client/service/AbstractActivityService.java index 1076509e..091c9530 100644 --- a/stream-core/src/main/java/io/getstream/client/service/AbstractActivityService.java +++ b/stream-core/src/main/java/io/getstream/client/service/AbstractActivityService.java @@ -2,7 +2,9 @@ import io.getstream.client.exception.StreamClientException; import io.getstream.client.model.activities.BaseActivity; +import io.getstream.client.model.activities.UpdateTargetResponse; import io.getstream.client.model.beans.StreamActivitiesResponse; +import io.getstream.client.model.beans.Targets; import io.getstream.client.model.feeds.BaseFeed; import io.getstream.client.repo.StreamRepository; @@ -67,6 +69,19 @@ public StreamActivitiesResponse updateActivities(List activities) throws I return streamRepository.updateActivities(this.feed, type, activities); } + /** + * Update the to target(s) of a given {@link T} activity. + * @param activity The activity in scope. + * @param targets A container for the operations that need to be performed on the to field. + * @param The type of the activity. + * @return Response containing the modified activity and a summary of the actions performed to the to field. + * @throws IOException in case of network/socket exceptions + * @throws StreamClientException in case of functional or server-side exception + */ + public UpdateTargetResponse updateToTargets(BaseActivity activity, Targets targets) throws IOException, StreamClientException { + return streamRepository.updateToTargets(feed, activity, targets); + } + /** * Add a new activity of type {@link T} to multiple feeds. * diff --git a/stream-core/src/test/java/io/getstream/client/model/beans/TargetsTest.java b/stream-core/src/test/java/io/getstream/client/model/beans/TargetsTest.java new file mode 100644 index 00000000..0ad7c9b5 --- /dev/null +++ b/stream-core/src/test/java/io/getstream/client/model/beans/TargetsTest.java @@ -0,0 +1,39 @@ +package io.getstream.client.model.beans; + +import org.junit.Test; + +import java.util.Collections; + +import static org.hamcrest.CoreMatchers.hasItem; +import static org.junit.Assert.assertThat; + +public class TargetsTest { + + @Test + public void shouldBuildTheTargetWithNewTargets() { + Targets targets = new Targets.Builder() + .setNewTargets(Collections.singletonList("user:newUser1")) + .build(); + + assertThat(targets.getNewTargets(), hasItem("user:newUser1")); + } + + @Test + public void shouldBuildTheTargetWithAddAndRemoveTargets() { + Targets targets = new Targets.Builder() + .setAddedTargets(Collections.singletonList("user:newUser1")) + .setRemovedTargets(Collections.singletonList("user:newUser2")) + .build(); + + assertThat(targets.getAddedTargets(), hasItem("user:newUser1")); + assertThat(targets.getRemovedTargets(), hasItem("user:newUser2")); + } + + @Test(expected = IllegalArgumentException.class) + public void shouldFailBuildingTargets() { + Targets targets = new Targets.Builder() + .setNewTargets(Collections.singletonList("user:newUser1")) + .setRemovedTargets(Collections.singletonList("user:newUser2")) + .build(); + } +} \ No newline at end of file diff --git a/stream-repo-apache/src/main/java/io/getstream/client/apache/repo/StreamActivityRepository.java b/stream-repo-apache/src/main/java/io/getstream/client/apache/repo/StreamActivityRepository.java index 3400e0ac..4243df1e 100644 --- a/stream-repo-apache/src/main/java/io/getstream/client/apache/repo/StreamActivityRepository.java +++ b/stream-repo-apache/src/main/java/io/getstream/client/apache/repo/StreamActivityRepository.java @@ -9,10 +9,13 @@ import io.getstream.client.model.activities.AggregatedActivity; import io.getstream.client.model.activities.BaseActivity; import io.getstream.client.model.activities.NotificationActivity; +import io.getstream.client.model.activities.UpdateTargetResponse; import io.getstream.client.model.beans.AddMany; import io.getstream.client.model.beans.MarkedActivity; import io.getstream.client.model.beans.StreamActivitiesResponse; import io.getstream.client.model.beans.StreamResponse; +import io.getstream.client.model.beans.Targets; +import io.getstream.client.model.beans.UpdateTo; import io.getstream.client.model.feeds.BaseFeed; import io.getstream.client.model.filters.FeedFilter; import io.getstream.client.util.HttpSignatureHandler; @@ -228,6 +231,35 @@ public StreamActivitiesResponse updateActivities(Bas } } + public UpdateTargetResponse updateToTargets(BaseFeed feed, BaseActivity activity, Targets targets) throws StreamClientException, IOException { + HttpPost request = new HttpPost(UriBuilder.fromEndpoint(baseEndpoint) + .path("feed_targets/") + .path(feed.getFeedSlug().concat("/")) + .path(feed.getUserId().concat("/")) + .path("activity_to_targets/") + .queryParam(StreamRepositoryImpl.API_KEY, apiKey).build()); + LOG.debug("Invoking url: '{}'", request.getURI()); + + UpdateTo updateActivity = new UpdateTo(); + updateActivity.setForeignId(activity.getForeignId()); + updateActivity.setTime(activity.getTime()); + updateActivity.setNewTargets(targets.getNewTargets()); + updateActivity.setAddedTargets(targets.getAddedTargets()); + updateActivity.setRemovedTargets(targets.getRemovedTargets()); + + request.setEntity(new InputStreamEntity(new ByteArrayInputStream(objectMapper.writeValueAsBytes(updateActivity)), APPLICATION_JSON)); + + request = StreamRepoUtils.addJwtAuthentication(generateToken(secretKey, "write", "feed_targets", feed.getFeedSlug().concat(feed.getUserId()), null), request); + + LOG.debug("Type: " + activity.getClass()); + try (CloseableHttpResponse response = httpClient.execute(request, HttpClientContext.create())) { + handleResponseCode(response); + return objectMapper.readValue(response.getEntity().getContent(), + objectMapper.getTypeFactory().constructParametricType(UpdateTargetResponse.class, + objectMapper.constructType(activity.getClass()))); + } + } + private HttpRequestBase addAuthentication(BaseFeed feed, HttpRequestBase request) { return StreamRepoUtils.addAuthentication(feed, secretKey, request); } diff --git a/stream-repo-apache/src/main/java/io/getstream/client/apache/repo/StreamRepositoryImpl.java b/stream-repo-apache/src/main/java/io/getstream/client/apache/repo/StreamRepositoryImpl.java index ed03e069..b953e38d 100644 --- a/stream-repo-apache/src/main/java/io/getstream/client/apache/repo/StreamRepositoryImpl.java +++ b/stream-repo-apache/src/main/java/io/getstream/client/apache/repo/StreamRepositoryImpl.java @@ -11,12 +11,14 @@ import io.getstream.client.model.activities.AggregatedActivity; import io.getstream.client.model.activities.BaseActivity; import io.getstream.client.model.activities.NotificationActivity; +import io.getstream.client.model.activities.UpdateTargetResponse; import io.getstream.client.model.beans.FeedFollow; import io.getstream.client.model.beans.FollowMany; import io.getstream.client.model.beans.FollowRequest; import io.getstream.client.model.beans.MarkedActivity; import io.getstream.client.model.beans.StreamActivitiesResponse; import io.getstream.client.model.beans.StreamResponse; +import io.getstream.client.model.beans.Targets; import io.getstream.client.model.beans.UnfollowMany; import io.getstream.client.model.feeds.BaseFeed; import io.getstream.client.model.filters.FeedFilter; @@ -184,6 +186,11 @@ public StreamActivitiesResponse updateActivities(Bas return streamActivityRepository.updateActivities(feed, type, activities); } + @Override + public UpdateTargetResponse updateToTargets(BaseFeed feed, BaseActivity activity, Targets targets) throws StreamClientException, IOException { + return streamActivityRepository.updateToTargets(feed, activity, targets); + } + @Override public T addActivityToMany(List targetIds, T activity) throws StreamClientException, IOException { return streamActivityRepository.addToMany(targetIds, activity); diff --git a/stream-repo-apache/src/main/java/io/getstream/client/apache/repo/handlers/StreamExceptionHandler.java b/stream-repo-apache/src/main/java/io/getstream/client/apache/repo/handlers/StreamExceptionHandler.java index b6fbd9a7..f3177aab 100644 --- a/stream-repo-apache/src/main/java/io/getstream/client/apache/repo/handlers/StreamExceptionHandler.java +++ b/stream-repo-apache/src/main/java/io/getstream/client/apache/repo/handlers/StreamExceptionHandler.java @@ -1,5 +1,6 @@ package io.getstream.client.apache.repo.handlers; +import com.fasterxml.jackson.core.JsonParseException; import com.fasterxml.jackson.databind.ObjectMapper; import io.getstream.client.exception.AuthenticationFailedException; import io.getstream.client.exception.InternalServerException; @@ -69,12 +70,17 @@ private void parseException(final CloseableHttpResponse response) throws IOExcep private StreamClientException buildException(StreamClientException exception, CloseableHttpResponse response) throws IOException { - StreamErrorResponse error = objectMapper.readValue(response.getEntity().getContent(), StreamErrorResponse.class); - if (null != error) { - exception.setCode(error.getCode()); + try { + StreamErrorResponse error = objectMapper.readValue(response.getEntity().getContent(), StreamErrorResponse.class); + if (null != error) { + exception.setCode(error.getCode()); + exception.setHttpStatusCode(response.getStatusLine().getStatusCode()); + exception.setDetail(error.getDetail()); + exception.setExceptionField(error.getException()); + } + } catch (JsonParseException e) { exception.setHttpStatusCode(response.getStatusLine().getStatusCode()); - exception.setDetail(error.getDetail()); - exception.setExceptionField(error.getException()); + exception.setDetail(response.getStatusLine().getReasonPhrase()); } return exception; } diff --git a/stream-repo-apache/src/test/java/io/getstream/client/apache/IntegrationTest.java b/stream-repo-apache/src/test/java/io/getstream/client/apache/IntegrationTest.java index d74fb508..0f44151b 100644 --- a/stream-repo-apache/src/test/java/io/getstream/client/apache/IntegrationTest.java +++ b/stream-repo-apache/src/test/java/io/getstream/client/apache/IntegrationTest.java @@ -15,11 +15,13 @@ import io.getstream.client.model.activities.AggregatedActivity; import io.getstream.client.model.activities.NotificationActivity; import io.getstream.client.model.activities.SimpleActivity; +import io.getstream.client.model.activities.UpdateTargetResponse; import io.getstream.client.model.beans.FeedFollow; import io.getstream.client.model.beans.FollowMany; import io.getstream.client.model.beans.MarkedActivity; import io.getstream.client.model.beans.StreamActivitiesResponse; import io.getstream.client.model.beans.StreamResponse; +import io.getstream.client.model.beans.Targets; import io.getstream.client.model.beans.UnfollowMany; import io.getstream.client.model.feeds.Feed; import io.getstream.client.model.filters.FeedFilter; @@ -44,8 +46,7 @@ import static io.getstream.client.util.JwtAuthenticationUtil.ALL; import static junit.framework.TestCase.assertTrue; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.CoreMatchers.*; import static org.hamcrest.MatcherAssert.assertThat; public class IntegrationTest { @@ -368,6 +369,74 @@ public void shouldUpdateActivities() throws IOException, StreamClientException { streamClient.shutdown(); } + @Test + public void shouldUpdateToTargets() throws IOException, StreamClientException { + StreamClient streamClient = new StreamClientImpl(CLIENT_CONFIGURATION, API_KEY, + API_SECRET); + + String user1 = this.getTestUserId("shouldChangeTo1"); + Feed feed = streamClient.newFeed("user", user1); + FlatActivityServiceImpl flatActivityService1 = feed.newFlatActivityService(SimpleActivity.class); + + SimpleActivity activityUser1 = new SimpleActivity(); + activityUser1.setForeignId("activityUser1"); + activityUser1.setActor("user1"); + activityUser1.setVerb("like"); + activityUser1.setObject("object1"); + activityUser1.setTime(new Date()); + + /* add activity 'activityUser1' */ + flatActivityService1.addActivity(activityUser1); + + + String user2 = this.getTestUserId("shouldChangeTo2"); + Feed feed2 = streamClient.newFeed("user", user2); + FlatActivityServiceImpl flatActivityService2 = feed2.newFlatActivityService(SimpleActivity.class); + + SimpleActivity activityUser2 = new SimpleActivity(); + activityUser2.setForeignId("activityUser2"); + activityUser2.setActor("user2"); + activityUser2.setVerb("like"); + activityUser2.setObject("object1"); + activityUser2.setTime(new Date()); + + /* add activity 'activityUser2' */ + flatActivityService2.addActivity(activityUser2); + + + String user3 = this.getTestUserId("shouldChangeTo2"); + Feed feed3 = streamClient.newFeed("user", user3); + FlatActivityServiceImpl flatActivityService3 = feed2.newFlatActivityService(SimpleActivity.class); + + SimpleActivity activityUser3 = new SimpleActivity(); + activityUser3.setForeignId("activityUser3"); + activityUser3.setActor("user3"); + activityUser3.setVerb("like"); + activityUser3.setObject("object1"); + activityUser3.setTime(new Date()); + activityUser3.setForeignId("user:".concat(user1)); + activityUser3.setTo(Collections.singletonList("user:".concat(user1))); // 'to' field points to 'user1' feed + + /* add activity 'activityUser3' with 'to' field pointing to feed user1 */ + flatActivityService3.addActivity(activityUser3); + + /* change the 'to' field of activity 'activityUser3' from user1 to user2 */ + /* addNewTarget(new_activity) replaces the 'to' field. It's equivalent to run addTargetToAdd(new_activity) and + * addTargetToRemove(old_activity). See the below assertions. */ + Targets toTargets = new Targets.Builder() + .addNewTarget("user:".concat(user2)) + .build(); + + /* perform the change */ + UpdateTargetResponse response = flatActivityService3.updateToTargets(activityUser3, toTargets); + + assertThat(response.getRemovedTargets(), hasItem("user:".concat(user1))); + assertThat(response.getAddedTargets(), hasItem("user:".concat(user2))); + assertThat(response.getActivity().getTo(), hasItem("user:".concat(user2))); + + streamClient.shutdown(); + } + @Test public void shouldAddActivityToMany() throws IOException, StreamClientException { StreamClient streamClient = new StreamClientImpl(CLIENT_CONFIGURATION, API_KEY, diff --git a/stream-repo-okhttp/src/main/java/io/getstream/client/okhttp/repo/StreamActivityRepository.java b/stream-repo-okhttp/src/main/java/io/getstream/client/okhttp/repo/StreamActivityRepository.java index 78f1a007..ef9a156f 100644 --- a/stream-repo-okhttp/src/main/java/io/getstream/client/okhttp/repo/StreamActivityRepository.java +++ b/stream-repo-okhttp/src/main/java/io/getstream/client/okhttp/repo/StreamActivityRepository.java @@ -5,10 +5,13 @@ import io.getstream.client.model.activities.AggregatedActivity; import io.getstream.client.model.activities.BaseActivity; import io.getstream.client.model.activities.NotificationActivity; +import io.getstream.client.model.activities.UpdateTargetResponse; import io.getstream.client.model.beans.AddMany; import io.getstream.client.model.beans.MarkedActivity; import io.getstream.client.model.beans.StreamActivitiesResponse; import io.getstream.client.model.beans.StreamResponse; +import io.getstream.client.model.beans.Targets; +import io.getstream.client.model.beans.UpdateTo; import io.getstream.client.model.feeds.BaseFeed; import io.getstream.client.model.filters.FeedFilter; import io.getstream.client.okhttp.repo.handlers.StreamExceptionHandler; @@ -250,6 +253,36 @@ public StreamActivitiesResponse updateActivities(Bas } } + public UpdateTargetResponse updateToTargets(BaseFeed feed, BaseActivity activity, Targets targets) throws StreamClientException, IOException { + Request.Builder requestBuilder = new Request.Builder().delete().url(UriBuilder.fromEndpoint(baseEndpoint) + .path("feed_targets/") + .path(feed.getFeedSlug().concat("/")) + .path(feed.getUserId().concat("/")) + .path("activity_to_targets/") + .queryParam(StreamRepositoryImpl.API_KEY, apiKey).build().toURL()); + + UpdateTo updateActivity = new UpdateTo(); + updateActivity.setForeignId(activity.getForeignId()); + updateActivity.setTime(activity.getTime()); + updateActivity.setNewTargets(targets.getNewTargets()); + updateActivity.setAddedTargets(targets.getAddedTargets()); + updateActivity.setRemovedTargets(targets.getRemovedTargets()); + + requestBuilder.post(RequestBody.create(MediaType.parse(APPLICATION_JSON), objectMapper.writeValueAsBytes(updateActivity))); + + Request request = StreamRepoUtils.addJwtAuthentication(generateToken(secretKey, "write", "feed_targets", + feed.getFeedSlug().concat(feed.getUserId()), null), requestBuilder).build(); + + LOG.debug("Invoking url: '{}", request.url().toString()); + + try (Response response = httpClient.newCall(request).execute()) { + handleResponseCode(response); + return objectMapper.readValue(response.body().byteStream(), + objectMapper.getTypeFactory().constructParametricType(UpdateTargetResponse.class, + objectMapper.constructType(activity.getClass()))); + } + } + private void handleResponseCode(Response response) throws StreamClientException, IOException { exceptionHandler.handleResponseCode(response); } diff --git a/stream-repo-okhttp/src/main/java/io/getstream/client/okhttp/repo/StreamRepositoryImpl.java b/stream-repo-okhttp/src/main/java/io/getstream/client/okhttp/repo/StreamRepositoryImpl.java index a1d4f65f..2dfc36de 100644 --- a/stream-repo-okhttp/src/main/java/io/getstream/client/okhttp/repo/StreamRepositoryImpl.java +++ b/stream-repo-okhttp/src/main/java/io/getstream/client/okhttp/repo/StreamRepositoryImpl.java @@ -7,12 +7,14 @@ import io.getstream.client.model.activities.AggregatedActivity; import io.getstream.client.model.activities.BaseActivity; import io.getstream.client.model.activities.NotificationActivity; +import io.getstream.client.model.activities.UpdateTargetResponse; import io.getstream.client.model.beans.FeedFollow; import io.getstream.client.model.beans.FollowMany; import io.getstream.client.model.beans.FollowRequest; import io.getstream.client.model.beans.MarkedActivity; import io.getstream.client.model.beans.StreamActivitiesResponse; import io.getstream.client.model.beans.StreamResponse; +import io.getstream.client.model.beans.Targets; import io.getstream.client.model.beans.UnfollowMany; import io.getstream.client.model.feeds.BaseFeed; import io.getstream.client.model.filters.FeedFilter; @@ -192,6 +194,11 @@ public StreamActivitiesResponse updateActivities(Bas return streamActivityRepository.updateActivities(feed, type, activities); } + @Override + public UpdateTargetResponse updateToTargets(BaseFeed feed, BaseActivity activity, Targets targets) throws StreamClientException, IOException { + return streamActivityRepository.updateToTargets(feed, activity, targets); + } + @Override public T addActivityToMany(List targetIds, T activity) throws StreamClientException, IOException { return streamActivityRepository.addToMany(targetIds, activity); diff --git a/stream-repo-okhttp/src/test/java/io/getstream/client/okhttp/IntegrationTest.java b/stream-repo-okhttp/src/test/java/io/getstream/client/okhttp/IntegrationTest.java index 3c151849..0cde798b 100644 --- a/stream-repo-okhttp/src/test/java/io/getstream/client/okhttp/IntegrationTest.java +++ b/stream-repo-okhttp/src/test/java/io/getstream/client/okhttp/IntegrationTest.java @@ -15,11 +15,13 @@ import io.getstream.client.model.activities.AggregatedActivity; import io.getstream.client.model.activities.NotificationActivity; import io.getstream.client.model.activities.SimpleActivity; +import io.getstream.client.model.activities.UpdateTargetResponse; import io.getstream.client.model.beans.FeedFollow; import io.getstream.client.model.beans.FollowMany; import io.getstream.client.model.beans.MarkedActivity; import io.getstream.client.model.beans.StreamActivitiesResponse; import io.getstream.client.model.beans.StreamResponse; +import io.getstream.client.model.beans.Targets; import io.getstream.client.model.beans.UnfollowMany; import io.getstream.client.model.feeds.Feed; import io.getstream.client.model.filters.FeedFilter; @@ -40,8 +42,7 @@ import static io.getstream.client.util.JwtAuthenticationUtil.ALL; import static junit.framework.TestCase.assertTrue; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.CoreMatchers.*; import static org.hamcrest.MatcherAssert.assertThat; @@ -402,6 +403,74 @@ public void shouldUpdateActivity() throws IOException, StreamClientException { streamClient.shutdown(); } + @Test + public void shouldUpdateToTargets() throws IOException, StreamClientException { + StreamClient streamClient = new StreamClientImpl(CLIENT_CONFIGURATION, API_KEY, + API_SECRET); + + String user1 = this.getTestUserId("shouldChangeTo1"); + Feed feed = streamClient.newFeed("user", user1); + FlatActivityServiceImpl flatActivityService1 = feed.newFlatActivityService(SimpleActivity.class); + + SimpleActivity activityUser1 = new SimpleActivity(); + activityUser1.setForeignId("activityUser1"); + activityUser1.setActor("user1"); + activityUser1.setVerb("like"); + activityUser1.setObject("object1"); + activityUser1.setTime(new Date()); + + /* add activity 'activityUser1' */ + flatActivityService1.addActivity(activityUser1); + + + String user2 = this.getTestUserId("shouldChangeTo2"); + Feed feed2 = streamClient.newFeed("user", user2); + FlatActivityServiceImpl flatActivityService2 = feed2.newFlatActivityService(SimpleActivity.class); + + SimpleActivity activityUser2 = new SimpleActivity(); + activityUser2.setForeignId("activityUser2"); + activityUser2.setActor("user2"); + activityUser2.setVerb("like"); + activityUser2.setObject("object1"); + activityUser2.setTime(new Date()); + + /* add activity 'activityUser2' */ + flatActivityService2.addActivity(activityUser2); + + + String user3 = this.getTestUserId("shouldChangeTo2"); + Feed feed3 = streamClient.newFeed("user", user3); + FlatActivityServiceImpl flatActivityService3 = feed2.newFlatActivityService(SimpleActivity.class); + + SimpleActivity activityUser3 = new SimpleActivity(); + activityUser3.setForeignId("activityUser3"); + activityUser3.setActor("user3"); + activityUser3.setVerb("like"); + activityUser3.setObject("object1"); + activityUser3.setTime(new Date()); + activityUser3.setForeignId("user:".concat(user1)); + activityUser3.setTo(Collections.singletonList("user:".concat(user1))); // 'to' field points to 'user1' feed + + /* add activity 'activityUser3' with 'to' field pointing to feed user1 */ + flatActivityService3.addActivity(activityUser3); + + /* change the 'to' field of activity 'activityUser3' from user1 to user2 */ + /* addNewTarget(new_activity) replaces the 'to' field. It's equivalent to run addTargetToAdd(new_activity) and + * addTargetToRemove(old_activity). See the below assertions. */ + Targets toTargets = new Targets.Builder() + .addNewTarget("user:".concat(user2)) + .build(); + + /* perform the change */ + UpdateTargetResponse response = flatActivityService3.updateToTargets(activityUser3, toTargets); + + assertThat(response.getRemovedTargets(), hasItem("user:".concat(user1))); + assertThat(response.getAddedTargets(), hasItem("user:".concat(user2))); + assertThat(response.getActivity().getTo(), hasItem("user:".concat(user2))); + + streamClient.shutdown(); + } + @Test public void shouldAddActivityToMany() throws IOException, StreamClientException { StreamClient streamClient = new StreamClientImpl(CLIENT_CONFIGURATION, API_KEY,