STREAMS-204 | Added in a Google Plus Deserializer and Processor for Comments. The processor takes any comments found for the passed in Activity (via a to-be-seen provider) and appends them as ActivityObjects. A total "comment_count" attribute is also stored in the "extensions" section of the Activity.
Project: http://git-wip-us.apache.org/repos/asf/incubator-streams/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-streams/commit/08e21453 Tree: http://git-wip-us.apache.org/repos/asf/incubator-streams/tree/08e21453 Diff: http://git-wip-us.apache.org/repos/asf/incubator-streams/diff/08e21453 Branch: refs/heads/STREAMS-49 Commit: 08e21453084e79661f1687a47f1310ec44568eaa Parents: 2358b4e Author: Robert Douglas <[email protected]> Authored: Wed Oct 29 17:44:39 2014 -0500 Committer: Robert Douglas <[email protected]> Committed: Thu Nov 6 14:04:27 2014 -0600 ---------------------------------------------------------------------- .../processor/GooglePlusCommentProcessor.java | 88 +++++++++++++- .../util/GPlusCommentDeserializer.java | 99 +++++++++++++++- .../serializer/util/GooglePlusActivityUtil.java | 50 ++++++++ .../gplus/GooglePLusCommentSerDeTest.java | 115 ++++++++++++++++++- .../resources/google_plus_comments_jsons.txt | 3 + 5 files changed, 343 insertions(+), 12 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/08e21453/streams-contrib/streams-provider-google/google-gplus/src/main/java/com/google/gplus/processor/GooglePlusCommentProcessor.java ---------------------------------------------------------------------- diff --git a/streams-contrib/streams-provider-google/google-gplus/src/main/java/com/google/gplus/processor/GooglePlusCommentProcessor.java b/streams-contrib/streams-provider-google/google-gplus/src/main/java/com/google/gplus/processor/GooglePlusCommentProcessor.java index 7dc4d56..583c741 100644 --- a/streams-contrib/streams-provider-google/google-gplus/src/main/java/com/google/gplus/processor/GooglePlusCommentProcessor.java +++ b/streams-contrib/streams-provider-google/google-gplus/src/main/java/com/google/gplus/processor/GooglePlusCommentProcessor.java @@ -1,7 +1,87 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + package com.google.gplus.processor; -/** - * Created by rdouglas on 10/29/14. - */ -public class GooglePlusCommentProcessor { +import com.google.api.client.util.Lists; +import com.google.api.services.plus.model.Comment; +import com.google.api.services.plus.model.Person; +import com.google.gplus.serializer.util.GPlusActivityDeserializer; +import com.google.gplus.serializer.util.GooglePlusActivityUtil; +import org.apache.streams.core.StreamsDatum; +import org.apache.streams.core.StreamsProcessor; +import org.apache.streams.pojo.json.Activity; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.List; + +public class GooglePlusCommentProcessor implements StreamsProcessor { + private final static String STREAMS_ID = "GooglePlusCommentProcessor"; + private final static Logger LOGGER = LoggerFactory.getLogger(GooglePlusCommentProcessor.class); + private GooglePlusActivityUtil googlePlusActivityUtil; + private int count; + + @Override + public List<StreamsDatum> process(StreamsDatum entry) { + StreamsDatum result = null; + + try { + Object item = entry.getDocument(); + LOGGER.debug("{} processing {}", STREAMS_ID, item.getClass()); + + //Get G+ activity ID from our own activity ID + if (item instanceof Activity) { + Activity activity = (Activity) item; + String activityId = getGPlusID(activity.getId()); + + //Call Google Plus API to get list of comments for this activity ID + /**TODO: FILL ME OUT WITH THE API CALL**/ + List<Comment> comments = Lists.newArrayList(); + + googlePlusActivityUtil.updateActivity(comments, activity); + result = new StreamsDatum(activity); + } + } catch (Exception e) { + e.printStackTrace(); + LOGGER.error("Exception while converting Comment to Activity: {}", e.getMessage()); + } + + if( result != null ) + return com.google.common.collect.Lists.newArrayList(result); + else + return com.google.common.collect.Lists.newArrayList(); + } + + @Override + public void prepare(Object configurationObject) { + googlePlusActivityUtil = new GooglePlusActivityUtil(); + count = 0; + } + + @Override + public void cleanUp() { + + } + + private String getGPlusID(String activityID) { + String[] activityParts = activityID.split(":"); + return (activityParts.length > 0) ? activityParts[activityParts.length - 1] : ""; + } } http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/08e21453/streams-contrib/streams-provider-google/google-gplus/src/main/java/com/google/gplus/serializer/util/GPlusCommentDeserializer.java ---------------------------------------------------------------------- diff --git a/streams-contrib/streams-provider-google/google-gplus/src/main/java/com/google/gplus/serializer/util/GPlusCommentDeserializer.java b/streams-contrib/streams-provider-google/google-gplus/src/main/java/com/google/gplus/serializer/util/GPlusCommentDeserializer.java index 51446e0..513bdc7 100644 --- a/streams-contrib/streams-provider-google/google-gplus/src/main/java/com/google/gplus/serializer/util/GPlusCommentDeserializer.java +++ b/streams-contrib/streams-provider-google/google-gplus/src/main/java/com/google/gplus/serializer/util/GPlusCommentDeserializer.java @@ -1,7 +1,98 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + package com.google.gplus.serializer.util; -/** - * Created by rdouglas on 10/29/14. - */ -public class GPlusCommentDeserializer { +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.api.client.util.DateTime; +import com.google.api.client.util.Lists; +import com.google.api.services.plus.model.Comment; +import org.apache.streams.jackson.StreamsJacksonMapper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.util.List; + +public class GPlusCommentDeserializer extends JsonDeserializer<Comment> { + private final static Logger LOGGER = LoggerFactory.getLogger(GPlusActivityDeserializer.class); + + /** + * Because the GooglePlus Comment object {@link com.google.api.services.plus.model.Comment} contains complex objects + * within its hierarchy, we have to use a custom deserializer + * + * @param jsonParser + * @param deserializationContext + * @return The deserialized {@link com.google.api.services.plus.model.Comment} object + * @throws java.io.IOException + * @throws com.fasterxml.jackson.core.JsonProcessingException + */ + @Override + public Comment deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException { + + JsonNode node = jsonParser.getCodec().readTree(jsonParser); + ObjectMapper objectMapper = new StreamsJacksonMapper(); + Comment comment = new Comment(); + + try { + comment.setEtag(node.get("etag").asText()); + comment.setVerb(node.get("verb").asText()); + comment.setId(node.get("id").asText()); + comment.setPublished(DateTime.parseRfc3339(node.get("published").asText())); + comment.setUpdated(DateTime.parseRfc3339(node.get("updated").asText())); + + Comment.Actor actor = new Comment.Actor(); + JsonNode actorNode = node.get("actor"); + actor.setDisplayName(actorNode.get("displayName").asText()); + actor.setUrl(actorNode.get("url").asText()); + + Comment.Actor.Image image = new Comment.Actor.Image(); + JsonNode imageNode = actorNode.get("image"); + image.setUrl(imageNode.get("url").asText()); + + actor.setImage(image); + + comment.setObject(objectMapper.readValue(objectMapper.writeValueAsString(node.get("object")), Comment.PlusObject.class)); + + comment.setSelfLink(node.get("selfLink").asText()); + + List<Comment.InReplyTo> replies = Lists.newArrayList(); + for(JsonNode reply : node.get("inReplyTo")) { + Comment.InReplyTo r = objectMapper.readValue(objectMapper.writeValueAsString(reply), Comment.InReplyTo.class); + replies.add(r); + } + + comment.setInReplyTo(replies); + + Comment.Plusoners plusoners = new Comment.Plusoners(); + JsonNode plusonersNode = node.get("plusoners"); + plusoners.setTotalItems(plusonersNode.get("totalItems").asLong()); + comment.setPlusoners(plusoners); + } catch (Exception e) { + LOGGER.error("Exception while trying to deserialize activity object: {}", e); + } + + return comment; + } } http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/08e21453/streams-contrib/streams-provider-google/google-gplus/src/main/java/com/google/gplus/serializer/util/GooglePlusActivityUtil.java ---------------------------------------------------------------------- diff --git a/streams-contrib/streams-provider-google/google-gplus/src/main/java/com/google/gplus/serializer/util/GooglePlusActivityUtil.java b/streams-contrib/streams-provider-google/google-gplus/src/main/java/com/google/gplus/serializer/util/GooglePlusActivityUtil.java index 73b4b0d..4e330fa 100644 --- a/streams-contrib/streams-provider-google/google-gplus/src/main/java/com/google/gplus/serializer/util/GooglePlusActivityUtil.java +++ b/streams-contrib/streams-provider-google/google-gplus/src/main/java/com/google/gplus/serializer/util/GooglePlusActivityUtil.java @@ -19,6 +19,8 @@ package com.google.gplus.serializer.util; +import com.google.api.client.util.Maps; +import com.google.api.services.plus.model.Comment; import com.google.api.services.plus.model.Person; import org.apache.streams.pojo.json.*; import org.apache.streams.pojo.json.Activity; @@ -31,6 +33,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.*; +import java.util.List; import static org.apache.streams.data.util.ActivityUtil.ensureExtensions; @@ -58,6 +61,22 @@ public class GooglePlusActivityUtil { } /** + * Given a {@link List} of {@link com.google.api.services.plus.model.Comment} objects and an + * {@link org.apache.streams.pojo.json.Activity}, update that Activity to contain all comments + * + * @param comments + * @param activity + */ + public static void updateActivity(List<Comment> comments, Activity activity) { + for(Comment comment : comments) { + addComment(activity, comment); + } + + Map<String, Object> extensions = ensureExtensions(activity); + extensions.put("comment_count", comments.size()); + } + + /** * Given a Google Plus {@link com.google.api.services.plus.model.Activity}, * convert that into an Activity streams formatted {@link org.apache.streams.pojo.json.Activity} * @@ -88,6 +107,37 @@ public class GooglePlusActivityUtil { } /** + * Adds a single {@link com.google.api.services.plus.model.Comment} to the Object.Attachments + * section of the passed in {@link org.apache.streams.pojo.json.Activity} + * + * @param activity + * @param comment + */ + private static void addComment(Activity activity, Comment comment) { + ActivityObject obj = new ActivityObject(); + + obj.setId(comment.getId()); + obj.setPublished(new DateTime(String.valueOf(comment.getPublished()))); + obj.setUpdated(new DateTime(String.valueOf(comment.getUpdated()))); + obj.setContent(comment.getObject().getContent()); + obj.setObjectType(comment.getObject().getObjectType()); + + Map<String, Object> extensions = Maps.newHashMap(); + extensions.put("googlePlus", comment); + + obj.setAdditionalProperty("extensions", extensions); + + if(activity.getObject() == null) { + activity.setObject(new ActivityObject()); + } + if(activity.getObject().getAttachments() == null) { + activity.getObject().setAttachments(new ArrayList<ActivityObject>()); + } + + activity.getObject().getAttachments().add(obj); + } + + /** * Add in necessary extensions from the passed in {@link com.google.api.services.plus.model.Activity} to the * {@link org.apache.streams.pojo.json.Activity} object * http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/08e21453/streams-contrib/streams-provider-google/google-gplus/src/test/java/com/google/gplus/GooglePLusCommentSerDeTest.java ---------------------------------------------------------------------- diff --git a/streams-contrib/streams-provider-google/google-gplus/src/test/java/com/google/gplus/GooglePLusCommentSerDeTest.java b/streams-contrib/streams-provider-google/google-gplus/src/test/java/com/google/gplus/GooglePLusCommentSerDeTest.java index b3479f9..9fea22c 100644 --- a/streams-contrib/streams-provider-google/google-gplus/src/test/java/com/google/gplus/GooglePLusCommentSerDeTest.java +++ b/streams-contrib/streams-provider-google/google-gplus/src/test/java/com/google/gplus/GooglePLusCommentSerDeTest.java @@ -1,7 +1,114 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + package com.google.gplus; -/** - * Created by rdouglas on 10/29/14. - */ -public class GooglePLusCommentSerDeTest { +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.module.SimpleModule; +import com.google.api.client.util.Lists; +import com.google.api.services.plus.model.Comment; +import com.google.gplus.serializer.util.GPlusCommentDeserializer; +import com.google.gplus.serializer.util.GooglePlusActivityUtil; +import org.apache.commons.lang.StringUtils; +import org.apache.streams.jackson.StreamsJacksonMapper; +import org.apache.streams.pojo.json.Activity; +import org.junit.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.BufferedReader; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; + +public class GooglePlusCommentSerDeTest { + private final static Logger LOGGER = LoggerFactory.getLogger(GooglePlusCommentSerDeTest.class); + private ObjectMapper objectMapper; + private GooglePlusActivityUtil googlePlusActivityUtil; + + @Before + public void setup() { + objectMapper = new StreamsJacksonMapper(); + SimpleModule simpleModule = new SimpleModule(); + simpleModule.addDeserializer(Comment.class, new GPlusCommentDeserializer()); + objectMapper.registerModule(simpleModule); + objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + + googlePlusActivityUtil = new GooglePlusActivityUtil(); + } + + @org.junit.Test + public void testCommentObjects() { + InputStream is = GooglePlusPersonSerDeTest.class.getResourceAsStream("/google_plus_comments_jsons.txt"); + InputStreamReader isr = new InputStreamReader(is); + BufferedReader br = new BufferedReader(isr); + + Activity activity = new Activity(); + List<Comment> comments = Lists.newArrayList(); + + try { + while (br.ready()) { + String line = br.readLine(); + if (!StringUtils.isEmpty(line)) { + LOGGER.info("raw: {}", line); + Comment comment = objectMapper.readValue(line, Comment.class); + + LOGGER.info("comment: {}", comment); + + assertNotNull(comment); + assertNotNull(comment.getEtag()); + assertNotNull(comment.getId()); + assertNotNull(comment.getInReplyTo()); + assertNotNull(comment.getObject()); + assertNotNull(comment.getPlusoners()); + assertNotNull(comment.getPublished()); + assertNotNull(comment.getUpdated()); + assertNotNull(comment.getSelfLink()); + assertEquals(comment.getVerb(), "post"); + + comments.add(comment); + } + } + + assertEquals(comments.size(), 3); + + googlePlusActivityUtil.updateActivity(comments, activity); + assertNotNull(activity); + assertNotNull(activity.getObject()); + assertEquals(activity.getObject().getAttachments().size(), 3); + } catch (Exception e) { + LOGGER.error("Exception while testing serializability: {}", e); + } + } + + @org.junit.Test + public void testEmptyComments() { + Activity activity = new Activity(); + + googlePlusActivityUtil.updateActivity(new ArrayList<Comment>(), activity); + + assertNull(activity.getObject()); + } } http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/08e21453/streams-contrib/streams-provider-google/google-gplus/src/test/resources/google_plus_comments_jsons.txt ---------------------------------------------------------------------- diff --git a/streams-contrib/streams-provider-google/google-gplus/src/test/resources/google_plus_comments_jsons.txt b/streams-contrib/streams-provider-google/google-gplus/src/test/resources/google_plus_comments_jsons.txt index e69de29..e52abf1 100644 --- a/streams-contrib/streams-provider-google/google-gplus/src/test/resources/google_plus_comments_jsons.txt +++ b/streams-contrib/streams-provider-google/google-gplus/src/test/resources/google_plus_comments_jsons.txt @@ -0,0 +1,3 @@ +{ "kind": "plus#comment", "etag": "\"Vea_b94Y77GDGgRK7gFNPnolKQw/ANtsF6kh7Ztc8-ufvOzWL234qfY\"", "verb": "post", "id": "z132c114nq3eijq4g04cdlo4yo33x3u5zvg0k.1414517802192122", "published": "2014-10-28T17:36:42.192Z", "updated": "2014-10-28T17:36:42.192Z", "actor": { "id": "110922531601810522565", "displayName": "Adalberto Hernandez", "url": "https://plus.google.com/110922531601810522565", "image": { "url": "https://lh4.googleusercontent.com/-VpIxk0xr8i0/AAAAAAAAAAI/AAAAAAAADl4/HgEI76iIDPk/photo.jpg?sz=50" } }, "object": { "objectType": "comment", "content": "I would buy the nexus 6 I still think the screen size is perfect for me" }, "selfLink": "https://content.googleapis.com/plus/v1/comments/z132c114nq3eijq4g04cdlo4yo33x3u5zvg0k#1414517802192122", "inReplyTo": [ { "id": "z132c114nq3eijq4g04cdlo4yo33x3u5zvg0k", "url": "https://plus.google.com/101127444819996140229/posts/QXuZzrgguaK" } ], "plusoners": { "totalItems": 6 } } +{ "kind": "plus#comment", "etag": "\"Vea_b94Y77GDGgRK7gFNPnolKQw/81qALbxT1WteuRVngMKhNsvF2hM\"", "verb": "post", "id": "z132c114nq3eijq4g04cdlo4yo33x3u5zvg0k.1414517816125433", "published": "2014-10-28T17:36:56.125Z", "updated": "2014-10-28T17:38:14.000Z", "actor": { "id": "107131626164728505473", "displayName": "Vance McAlister", "url": "https://plus.google.com/107131626164728505473", "image": { "url": "https://lh4.googleusercontent.com/-ENY-GPKzlpM/AAAAAAAAAAI/AAAAAAAAQPs/qTzDXHiDs6c/photo.jpg?sz=50" } }, "object": { "objectType": "comment", "content": "Only if they have gotten rid of Blur and it is as much pure Android as the Moto X." }, "selfLink": "https://content.googleapis.com/plus/v1/comments/z132c114nq3eijq4g04cdlo4yo33x3u5zvg0k#1414517816125433", "inReplyTo": [ { "id": "z132c114nq3eijq4g04cdlo4yo33x3u5zvg0k", "url": "https://plus.google.com/101127444819996140229/posts/QXuZzrgguaK" } ], "plusoners": { "totalItems": 7 } }, { "kind": "plus#comment", "etag": "\"Vea_b94Y77GDGgR K7gFNPnolKQw/3tvcbp0tAYDvMPPViM-Iq8CsVLU\"", "verb": "post", "id": "z132c114nq3eijq4g04cdlo4yo33x3u5zvg0k.1414517894284415", "published": "2014-10-28T17:38:14.284Z", "updated": "2014-10-28T17:38:14.284Z", "actor": { "id": "116526038399605835276", "displayName": "Thiago Vinhas", "url": "https://plus.google.com/116526038399605835276", "image": { "url": "https://lh6.googleusercontent.com/-ze6E8Zj8LM8/AAAAAAAAAAI/AAAAAAAAcII/2htWB7qbHs4/photo.jpg?sz=50" } }, "object": { "objectType": "comment", "content": "Verizon? No, thank you. I prefer to be kicked on the balls." }, "selfLink": "https://content.googleapis.com/plus/v1/comments/z132c114nq3eijq4g04cdlo4yo33x3u5zvg0k#1414517894284415", "inReplyTo": [ { "id": "z132c114nq3eijq4g04cdlo4yo33x3u5zvg0k", "url": "https://plus.google.com/101127444819996140229/posts/QXuZzrgguaK" } ], "plusoners": { "totalItems": 52 } } +{ "kind": "plus#comment", "etag": "\"Vea_b94Y77GDGgRK7gFNPnolKQw/DCSYhsbyQnRvdVQ8U7e1B6Rw9F8\"", "verb": "post", "id": "z132c114nq3eijq4g04cdlo4yo33x3u5zvg0k.1414518523966061", "published": "2014-10-28T17:48:43.966Z", "updated": "2014-10-28T17:48:43.966Z", "actor": { "id": "115669911467014111133", "displayName": "Eduardo Castaneda", "url": "https://plus.google.com/115669911467014111133", "image": { "url": "https://lh4.googleusercontent.com/-I0ghHQLUDC4/AAAAAAAAAAI/AAAAAAAABFk/YkeCQCLzwk8/photo.jpg?sz=50" } }, "object": { "objectType": "comment", "content": "how the fuck is this not the new nexus?!?! " }, "selfLink": "https://content.googleapis.com/plus/v1/comments/z132c114nq3eijq4g04cdlo4yo33x3u5zvg0k#1414518523966061", "inReplyTo": [ { "id": "z132c114nq3eijq4g04cdlo4yo33x3u5zvg0k", "url": "https://plus.google.com/101127444819996140229/posts/QXuZzrgguaK" } ], "plusoners": { "totalItems": 18 } } \ No newline at end of file
