You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@shindig.apache.org by ss...@apache.org on 2012/02/08 14:24:41 UTC

svn commit: r1241890 - in /shindig/trunk/java: samples/src/main/java/org/apache/shindig/social/opensocial/jpa/spi/ samples/src/test/java/org/apache/shindig/social/opensocial/jpa/spi/ server/src/test/java/org/apache/shindig/server/endtoend/ server/src/t...

Author: ssievers
Date: Wed Feb  8 13:24:41 2012
New Revision: 1241890

URL: http://svn.apache.org/viewvc?rev=1241890&view=rev
Log:
Fix for https://issues.apache.org/jira/browse/SHINDIG-1674 by Evgeny Bogdanov.  Thanks!

Modified:
    shindig/trunk/java/samples/src/main/java/org/apache/shindig/social/opensocial/jpa/spi/PersonServiceDb.java
    shindig/trunk/java/samples/src/test/java/org/apache/shindig/social/opensocial/jpa/spi/PersonServiceDbTest.java
    shindig/trunk/java/samples/src/test/java/org/apache/shindig/social/opensocial/jpa/spi/SpiTestUtil.java
    shindig/trunk/java/server/src/test/java/org/apache/shindig/server/endtoend/EndToEndTest.java
    shindig/trunk/java/server/src/test/resources/endtoend/osapi/personTest.xml
    shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/PersonHandler.java
    shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/spi/PersonService.java
    shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/sample/spi/JsonDbOpensocialService.java
    shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/opensocial/service/PersonHandlerTest.java
    shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/sample/spi/JsonDbOpensocialServiceTest.java

Modified: shindig/trunk/java/samples/src/main/java/org/apache/shindig/social/opensocial/jpa/spi/PersonServiceDb.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/samples/src/main/java/org/apache/shindig/social/opensocial/jpa/spi/PersonServiceDb.java?rev=1241890&r1=1241889&r2=1241890&view=diff
==============================================================================
--- shindig/trunk/java/samples/src/main/java/org/apache/shindig/social/opensocial/jpa/spi/PersonServiceDb.java (original)
+++ shindig/trunk/java/samples/src/main/java/org/apache/shindig/social/opensocial/jpa/spi/PersonServiceDb.java Wed Feb  8 13:24:41 2012
@@ -171,7 +171,74 @@ public class PersonServiceDb implements 
     return ImmediateFuture.newInstance(person);
   }
 
+  /**
+   * {@inheritDoc}
+   */
+  public Future<Person> updatePerson(UserId id, Person person, SecurityToken token)
+      throws ProtocolException {
+    String viewer = token.getViewerId(); // viewer
+    String uid = id.getUserId(token); // person to update
+
+    if (!viewerCanUpdatePerson(viewer,uid)) {
+      throw new ProtocolException(HttpServletResponse.SC_FORBIDDEN, "User '" + viewer + "' does not have enough privileges to update person '"+uid+"'");
+    }
 
+    Query q = null;
+    // Get the person object from db
+    q = entiyManager.createNamedQuery(PersonDb.FINDBY_PERSONID);
+    q.setParameter(PersonDb.PARAM_PERSONID, uid);
+    q.setFirstResult(0);
+    q.setMaxResults(1);
+
+    List<?> plist = q.getResultList();
+    PersonDb personDb = null;
+    if (plist != null && !plist.isEmpty()) {
+      personDb = (PersonDb) plist.get(0);
+      // update person's fields: displayName, aboutMe, age
+      // add fields that has to be updated
+      personDb.setThumbnailUrl(person.getThumbnailUrl());
+    }
+
+    // TODO How should transactions be managed? Should samples be using warp-persist instead?
+    if (!entiyManager.getTransaction().isActive()) {
+      entiyManager.getTransaction().begin();
+    }
+    // update person's names object (in another db table)
+    // if (person.getName() != null) {
+    //   if (personDb.getName() != null) {
+    //     entiyManager.remove(personDb.getName());
+    //   }
+    //   personDb.setName(person.getName());
+    // }
+    // update person's emails object (in another db table)
+    // if (person.getEmails() != null) {
+    //   for (Object e : personDb.getEmails()) {
+    //     entiyManager.remove(e);
+    //   }
+    //   List<ListField> emails = Lists.newArrayList();
+    //   for (ListField c : person.getEmails()) {
+    //     c.setPerson(personDb);
+    //     emails.add(c);
+    //   }
+    //   personDb.setEmails(emails);
+    // }
+    // provide other person's data updates similarily to name and emails
+
+    entiyManager.persist(personDb);
+
+    entiyManager.getTransaction().commit();
+
+    // send personDb data back
+    return ImmediateFuture.newInstance((Person) personDb);
+  }
+
+  /** Check if a viewer is allowed to update the given person record. **/
+  protected boolean viewerCanUpdatePerson(String viewer, String person) {
+    // A person can only update his own personal data (by default)
+    // if you wish to allow other people to update the personal data of the user
+    // you should change the current function
+    return viewer.equals(person) ? true : false;
+  }
 
   /**
    * Add a filter clause specified by the collection options.

Modified: shindig/trunk/java/samples/src/test/java/org/apache/shindig/social/opensocial/jpa/spi/PersonServiceDbTest.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/samples/src/test/java/org/apache/shindig/social/opensocial/jpa/spi/PersonServiceDbTest.java?rev=1241890&r1=1241889&r2=1241890&view=diff
==============================================================================
--- shindig/trunk/java/samples/src/test/java/org/apache/shindig/social/opensocial/jpa/spi/PersonServiceDbTest.java (original)
+++ shindig/trunk/java/samples/src/test/java/org/apache/shindig/social/opensocial/jpa/spi/PersonServiceDbTest.java Wed Feb  8 13:24:41 2012
@@ -20,6 +20,9 @@ package org.apache.shindig.social.openso
 
 import static org.junit.Assert.assertEquals;
 
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.shindig.protocol.ProtocolException;
 import org.apache.shindig.protocol.RestfulCollection;
 import org.apache.shindig.protocol.model.SortOrder;
 import org.apache.shindig.social.opensocial.model.Person;
@@ -37,41 +40,46 @@ import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+
 /**
- * 
+ *
  * Test the PersonServiceDb implementation.
  *
  */
 public class PersonServiceDbTest {
 
   private final Person canonical = SpiTestUtil.buildCanonicalPerson();
-  
+
   private PersonServiceDb personServiceDb;
-  
+
   /** The bootstrap. */
   private SpiDatabaseBootstrap bootstrap;
-  
+
   @Before
   public void setup() throws Exception {
     EntityManager entityManager = SpiEntityManagerFactory.getEntityManager();
     this.personServiceDb = new PersonServiceDb(entityManager);
-    
+
     // Bootstrap hibernate and associated test db, and setup db with test data
     this.bootstrap = new SpiDatabaseBootstrap(entityManager);
     this.bootstrap.init();
   }
-  
+
   @After
   public void tearDown() throws Exception {
     bootstrap.tearDown();
   }
-  
+
   @Test
   public void getCanonicalPerson() throws Exception {
      Future<Person> person = this.personServiceDb.getPerson(new UserId(Type.userId, "canonical"), Person.Field.ALL_FIELDS, SpiTestUtil.DEFAULT_TEST_SECURITY_TOKEN);
      SpiTestUtil.assertPersonEquals(person.get(), canonical);
   }
-  
+
   @Test
   public void getJohnDoeFriendsOrderedByName() throws Exception {
     // Set collection options
@@ -79,46 +87,70 @@ public class PersonServiceDbTest {
     collectionOptions.setSortBy("name");
     collectionOptions.setSortOrder(SortOrder.ascending);
     collectionOptions.setMax(20);
-    
+
     // Get all friends of john.doe
     Future<RestfulCollection<Person>> result = this.personServiceDb.getPeople(SpiTestUtil.buildUserIds("john.doe"), new GroupId(GroupId.Type.friends, "@friends"), collectionOptions, Person.Field.ALL_FIELDS, SpiTestUtil.DEFAULT_TEST_SECURITY_TOKEN);
-    
+
     RestfulCollection<Person> peopleCollection = result.get();
     assertEquals(3, peopleCollection.getTotalResults());
-    assertEquals(0, peopleCollection.getStartIndex());    
-    List<Person> people = peopleCollection.getEntry();    
+    assertEquals(0, peopleCollection.getStartIndex());
+    List<Person> people = peopleCollection.getEntry();
     // The users should be in alphabetical order
     SpiTestUtil.assertPersonEquals(people.get(0), "george.doe", "George Doe");
-    SpiTestUtil.assertPersonEquals(people.get(1), "jane.doe", "Jane Doe");     
+    SpiTestUtil.assertPersonEquals(people.get(1), "jane.doe", "Jane Doe");
   }
-  
-  
+
+
   @Test
-  public void getJohnDoeFriendsOrderedByNameWithPagination() throws Exception {    
+  public void getJohnDoeFriendsOrderedByNameWithPagination() throws Exception {
     // Set collection options
     CollectionOptions collectionOptions = new CollectionOptions();
     collectionOptions.setSortBy("name");
     collectionOptions.setSortOrder(SortOrder.ascending);
     collectionOptions.setFirst(0);
     collectionOptions.setMax(1);
-    
+
     // Get first friend of john.doe
-    Future<RestfulCollection<Person>> result = this.personServiceDb.getPeople(SpiTestUtil.buildUserIds("john.doe"), new GroupId(GroupId.Type.friends, "@friends"), collectionOptions, Person.Field.ALL_FIELDS, SpiTestUtil.DEFAULT_TEST_SECURITY_TOKEN);    
+    Future<RestfulCollection<Person>> result = this.personServiceDb.getPeople(SpiTestUtil.buildUserIds("john.doe"), new GroupId(GroupId.Type.friends, "@friends"), collectionOptions, Person.Field.ALL_FIELDS, SpiTestUtil.DEFAULT_TEST_SECURITY_TOKEN);
     RestfulCollection<Person> peopleCollection = result.get();
     assertEquals(3, peopleCollection.getTotalResults());
-    assertEquals(0, peopleCollection.getStartIndex());    
-    List<Person> people = peopleCollection.getEntry();    
+    assertEquals(0, peopleCollection.getStartIndex());
+    List<Person> people = peopleCollection.getEntry();
     SpiTestUtil.assertPersonEquals(people.get(0), "george.doe", "George Doe");
-    
+
     // Get second friend of john.doe
     collectionOptions.setFirst(1);
     result = this.personServiceDb.getPeople(SpiTestUtil.buildUserIds("john.doe"), new GroupId(GroupId.Type.friends, "@friends"), collectionOptions, Person.Field.ALL_FIELDS, SpiTestUtil.DEFAULT_TEST_SECURITY_TOKEN);
     peopleCollection = result.get();
     assertEquals(3, peopleCollection.getTotalResults());
-    assertEquals(1, peopleCollection.getStartIndex());    
-    people = peopleCollection.getEntry();    
-    SpiTestUtil.assertPersonEquals(people.get(0), "jane.doe", "Jane Doe");    
+    assertEquals(1, peopleCollection.getStartIndex());
+    people = peopleCollection.getEntry();
+    SpiTestUtil.assertPersonEquals(people.get(0), "jane.doe", "Jane Doe");
   }
-  
-  
+
+  @Test
+  public void testViewerCanUpdatePerson() throws Exception {
+    Person p = SpiTestUtil.buildCanonicalPerson();
+    p.setThumbnailUrl("http://newthumbnail.url");
+    this.personServiceDb.updatePerson(new UserId(Type.userId, "canonical"),
+        p, SpiTestUtil.CANONICAL_PERSON_SECURITY_TOKEN);
+
+    Future<Person> person = this.personServiceDb.getPerson(new UserId(Type.userId, "canonical"), ImmutableSet.of("id","displayName","thumbnailUrl"), SpiTestUtil.DEFAULT_TEST_SECURITY_TOKEN);
+    assertEquals("http://newthumbnail.url", person.get().getThumbnailUrl());
+  }
+
+  @Test
+  public void testViewerNotAllowedUpdatePerson() throws Exception {
+    Person p = SpiTestUtil.buildCanonicalPerson();
+    p.setThumbnailUrl("http://newthumbnail.url");
+
+    try {
+      this.personServiceDb.updatePerson(new UserId(Type.userId, "canonical"),
+          p, SpiTestUtil.JOHN_DOE_SECURITY_TOKEN);
+      org.junit.Assert.fail();
+    } catch (ProtocolException sse) {
+      assertEquals(HttpServletResponse.SC_FORBIDDEN, sse.getCode());
+    }
+  }
+
 }

Modified: shindig/trunk/java/samples/src/test/java/org/apache/shindig/social/opensocial/jpa/spi/SpiTestUtil.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/samples/src/test/java/org/apache/shindig/social/opensocial/jpa/spi/SpiTestUtil.java?rev=1241890&r1=1241889&r2=1241890&view=diff
==============================================================================
--- shindig/trunk/java/samples/src/test/java/org/apache/shindig/social/opensocial/jpa/spi/SpiTestUtil.java (original)
+++ shindig/trunk/java/samples/src/test/java/org/apache/shindig/social/opensocial/jpa/spi/SpiTestUtil.java Wed Feb  8 13:24:41 2012
@@ -60,26 +60,29 @@ import com.google.common.collect.Sets;
 
 
 /**
- * 
+ *
  * Convenient build and assert methods used by spi unit tests
  *
  */
 public class SpiTestUtil {
-  
+
   public final static SecurityToken DEFAULT_TEST_SECURITY_TOKEN = (SecurityToken) new FakeGadgetToken();
+  // (String appId, String appUrl, String domain, String ownerId, String trustedJson, String viewerId, String moduleId)
+  public final static SecurityToken CANONICAL_PERSON_SECURITY_TOKEN = (SecurityToken) new FakeGadgetToken("appId", "appUrl", "domain", "canonical", "trustedJson", "canonical", "20");
+  public final static SecurityToken JOHN_DOE_SECURITY_TOKEN = (SecurityToken) new FakeGadgetToken("appId", "appUrl", "domain", "john.doe", "trustedJson", "john.doe", "20");
   public final static SimpleDateFormat DATE_FORMATTER = new SimpleDateFormat("yyyy-MM-dd");
-  
+
   /*
    * Build canonical test person instance
    */
-  public static Person buildCanonicalPerson() {    
+  public static Person buildCanonicalPerson() {
     NameImpl name = new NameImpl("Sir Shin H. Digg Social Butterfly");
     name.setAdditionalName("H");
     name.setFamilyName("Digg");
     name.setGivenName("Shin");
     name.setHonorificPrefix("Sir");
     name.setHonorificSuffix("Social Butterfly");
-    
+
     Person canonical = new PersonImpl("canonical", "Shin Digg", name);
 
     canonical.setAboutMe("I have an example of every piece of data");
@@ -200,10 +203,10 @@ public class SpiTestUtil {
     canonical.setUrls(Lists.<Url>newArrayList(
         new UrlImpl("http://www.example.org/?id=1", "my profile", "Profile"),
         new UrlImpl("http://www.example.org/pic/?id=1", "my awesome picture", "Thumbnail")));
-    
+
     return canonical;
   }
-  
+
   /*
    * Build userId set
    */
@@ -215,10 +218,10 @@ public class SpiTestUtil {
     }
     return userIdSet;
   }
-  
+
   /*
    * Asserts actual person instance has the expected person id and (formatted) name.
-   * 
+   *
    */
   public static void assertPersonEquals(Person actual, String expectedId, String expectedName) {
     assertEquals(actual.getId(), expectedId);
@@ -228,31 +231,31 @@ public class SpiTestUtil {
 
   /*
    * Asserts actual person instance equals expected person instance
-   * 
+   *
    * Verified each individual variable so to know which variable is causing it to fail.
    * Note that person.updated isn't verified as we can't expect this to be equal.
-   * 
+   *
    */
   public static void assertPersonEquals(Person actual, Person expected) {
-    
+
     assertEquals(actual.getAboutMe(), expected.getAboutMe());
-    assertEquals(actual.getActivities(), expected.getActivities());    
+    assertEquals(actual.getActivities(), expected.getActivities());
     assertCollectionSizeEquals(actual.getAddresses(), expected.getAddresses());
     for (int i = 0; i < actual.getAddresses().size(); i++) {
       assertAddressEquals(actual.getAddresses().get(i), expected.getAddresses().get(i));
-    }    
-    assertEquals(actual.getAge(), expected.getAge());    
+    }
+    assertEquals(actual.getAge(), expected.getAge());
     assertBodyTypeEquals(actual.getBodyType(), expected.getBodyType());
     assertEquals(actual.getBooks(), expected.getBooks());
     assertEquals(actual.getCars(), expected.getCars());
-    assertEquals(actual.getChildren(), expected.getChildren());    
-    assertAddressEquals(actual.getCurrentLocation(), expected.getCurrentLocation());    
+    assertEquals(actual.getChildren(), expected.getChildren());
+    assertAddressEquals(actual.getCurrentLocation(), expected.getCurrentLocation());
     assertEquals(actual.getDisplayName(), expected.getDisplayName());
-    assertEquals(actual.getBirthday(), expected.getBirthday());    
+    assertEquals(actual.getBirthday(), expected.getBirthday());
     assertCollectionSizeEquals(actual.getEmails(), expected.getEmails());
     for (int i = 0; i < actual.getEmails().size(); i++) {
       assertListFieldEquals(actual.getEmails().get(i), expected.getEmails().get(i));
-    }    
+    }
     assertEquals(actual.getEthnicity(), expected.getEthnicity());
     assertEquals(actual.getFashion(), expected.getFashion());
     assertEquals(actual.getFood(), expected.getFood());
@@ -263,29 +266,29 @@ public class SpiTestUtil {
     assertEquals(actual.getHumor(), expected.getHumor());
     assertEquals(actual.getId(), expected.getId());
     assertEquals(actual.getInterests(), expected.getInterests());
-    assertEquals(actual.getJobInterests(), expected.getJobInterests());    
+    assertEquals(actual.getJobInterests(), expected.getJobInterests());
     assertCollectionSizeEquals(actual.getOrganizations(), expected.getOrganizations());
     for (int i = 0; i < actual.getOrganizations().size(); i++) {
       assertOrganizationEquals(actual.getOrganizations().get(i), expected.getOrganizations().get(i));
-    }    
+    }
     assertEquals(actual.getLanguagesSpoken(), expected.getLanguagesSpoken());
-    assertEquals(actual.getLivingArrangement(), expected.getLivingArrangement());    
+    assertEquals(actual.getLivingArrangement(), expected.getLivingArrangement());
     assertCollectionSizeEquals(actual.getLookingFor(), expected.getLookingFor());
     for (int i = 0; i < actual.getLookingFor().size(); i++) {
       assertEquals(actual.getLookingFor().get(i).getValue(), expected.getLookingFor().get(i).getValue());
-    }    
+    }
     assertEquals(actual.getMovies(), expected.getMovies());
-    assertEquals(actual.getMusic(), expected.getMusic());    
+    assertEquals(actual.getMusic(), expected.getMusic());
     assertNameEquals(actual.getName(), expected.getName());
     assertEquals(actual.getNetworkPresence().getValue(), expected.getNetworkPresence().getValue());
     assertEquals(actual.getNickname(), expected.getNickname());
-    assertEquals(actual.getPets(), expected.getPets());    
+    assertEquals(actual.getPets(), expected.getPets());
     assertCollectionSizeEquals(actual.getPhoneNumbers(), expected.getPhoneNumbers());
     for (int i = 0; i < actual.getPhoneNumbers().size(); i++) {
       assertListFieldEquals(actual.getPhoneNumbers().get(i), expected.getPhoneNumbers().get(i));
-    }    
-    assertEquals(actual.getPoliticalViews(), expected.getPoliticalViews());    
-    assertUrlEquals(actual.getProfileSong(), expected.getProfileSong());    
+    }
+    assertEquals(actual.getPoliticalViews(), expected.getPoliticalViews());
+    assertUrlEquals(actual.getProfileSong(), expected.getProfileSong());
     assertEquals(actual.getProfileUrl(), expected.getProfileUrl());
     assertUrlEquals(actual.getProfileVideo(), expected.getProfileVideo());
     assertEquals(actual.getQuotes(), expected.getQuotes());
@@ -302,14 +305,14 @@ public class SpiTestUtil {
     assertEquals(actual.getUtcOffset(), expected.getUtcOffset());
     assertEquals(actual.getTurnOffs(), expected.getTurnOffs());
     assertEquals(actual.getTurnOns(), expected.getTurnOns());
-    assertEquals(actual.getTvShows(), expected.getTvShows());    
+    assertEquals(actual.getTvShows(), expected.getTvShows());
   }
-  
+
   private static void assertUrlEquals(Url actual, Url expected) {
     assertEquals(actual.getLinkText(), expected.getLinkText());
     assertEquals(actual.getPrimary(), expected.getPrimary());
     assertEquals(actual.getType(), expected.getType());
-    assertEquals(actual.getValue(), expected.getValue());    
+    assertEquals(actual.getValue(), expected.getValue());
   }
 
   private static void assertNameEquals(Name actual, Name expected) {
@@ -318,7 +321,7 @@ public class SpiTestUtil {
     assertEquals(actual.getGivenName(), expected.getGivenName());
     assertEquals(actual.getHonorificPrefix(), expected.getHonorificPrefix());
     assertEquals(actual.getHonorificSuffix(), expected.getHonorificSuffix());
-    assertEquals(actual.getFormatted(), expected.getFormatted());    
+    assertEquals(actual.getFormatted(), expected.getFormatted());
   }
 
   private static void assertOrganizationEquals(Organization actual, Organization expected) {
@@ -333,15 +336,15 @@ public class SpiTestUtil {
     assertEquals(actual.getSubField(), expected.getSubField());
     assertEquals(actual.getTitle(), expected.getTitle());
     assertEquals(actual.getType(), expected.getType());
-    assertEquals(actual.getWebpage(), expected.getWebpage());    
+    assertEquals(actual.getWebpage(), expected.getWebpage());
   }
 
   private static void assertCollectionSizeEquals(Collection<?> actual, Collection<?> expected) {
     assertTrue(actual != null && expected != null);
     assertEquals(actual.size(), expected.size());
   }
-  
-  private static void assertAddressEquals(Address actual, Address expected) {    
+
+  private static void assertAddressEquals(Address actual, Address expected) {
     assertEquals(actual.getCountry(), expected.getCountry());
     assertEquals(actual.getLatitude(), expected.getLatitude());
     assertEquals(actual.getLocality(), expected.getLocality());
@@ -350,7 +353,7 @@ public class SpiTestUtil {
     assertEquals(actual.getRegion(), expected.getRegion());
     assertEquals(actual.getStreetAddress(), expected.getStreetAddress());
     assertEquals(actual.getType(), expected.getType());
-    assertEquals(actual.getFormatted(), expected.getFormatted());    
+    assertEquals(actual.getFormatted(), expected.getFormatted());
   }
 
   private static void assertBodyTypeEquals(BodyType actual, BodyType expected) {
@@ -360,37 +363,37 @@ public class SpiTestUtil {
     assertEquals(actual.getHeight(), expected.getHeight());
     assertEquals(actual.getWeight(), expected.getWeight());
   }
-  
+
   private static void assertListFieldEquals(ListField actual, ListField expected) {
     assertEquals(actual.getPrimary(), expected.getPrimary());
     assertEquals(actual.getType(), expected.getType());
     assertEquals(actual.getValue(), expected.getValue());
   }
-  
+
   public static void assertActivityEquals(Activity actual, Activity expected) {
     assertEquals(actual.getId(), expected.getId());
     assertEquals(actual.getUserId(), expected.getUserId());
     assertEquals(actual.getTitle(), expected.getTitle());
     assertEquals(actual.getBody(), expected.getBody());
   }
-  
+
   public static Activity buildTestActivity(String id, String userId, String title, String body) {
-    Activity activity = new ActivityImpl(id, userId); 
+    Activity activity = new ActivityImpl(id, userId);
     activity.setTitle(title);
     activity.setBody(body);
     return activity;
   }
-  
-  public static Date buildDate(String dateAsString) {    
+
+  public static Date buildDate(String dateAsString) {
     try {
       return DATE_FORMATTER.parse(dateAsString);
     } catch (Exception e) {
       throw new RuntimeException("Failed to parse date - " + dateAsString, e);
     }
   }
-  
+
   public static Set<String> asSet(String... items) {
     return Sets.newHashSet(Arrays.asList(items));
   }
-  
+
 }

Modified: shindig/trunk/java/server/src/test/java/org/apache/shindig/server/endtoend/EndToEndTest.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/server/src/test/java/org/apache/shindig/server/endtoend/EndToEndTest.java?rev=1241890&r1=1241889&r2=1241890&view=diff
==============================================================================
--- shindig/trunk/java/server/src/test/java/org/apache/shindig/server/endtoend/EndToEndTest.java (original)
+++ shindig/trunk/java/server/src/test/java/org/apache/shindig/server/endtoend/EndToEndTest.java Wed Feb  8 13:24:41 2012
@@ -70,8 +70,8 @@ public class EndToEndTest {
     "jsonTest.xml",
     "viewLevelElementsTest.xml",
     "cajaTest.xml",
-    "failCajaTest.xml",      
-    "failCajaUrlTest.xml",      
+    "failCajaTest.xml",
+    "failCajaUrlTest.xml",
     "osapi/personTest.xml",
     "osapi/peopleTest.xml",
     "osapi/activitiesTest.xml",
@@ -120,7 +120,7 @@ public class EndToEndTest {
   public void viewLevelElements() throws Exception {
     executeAllPageTests("viewLevelElementsTest");
   }
-  
+
   @Test
   @Ignore("Issues with passing the neko dom to caja") // FIXME
   public void cajaJsonParse() throws Exception {
@@ -191,7 +191,7 @@ public class EndToEndTest {
   public void testFailCaja() throws Exception {
     HtmlPage page = executePageTest("failCajaTest", null);
     NodeList bodyList = page.getElementsByTagName("body");
-    
+
     // Result should contain just one body
     assertEquals(1, bodyList.getLength());
     DomNode body = (DomNode) bodyList.item(0);
@@ -219,17 +219,17 @@ public class EndToEndTest {
     for (int i = 0; i < array.length(); i++) {
       JSONObject jsonObj = array.getJSONObject(i);
       assertTrue(jsonObj.has("id"));
-      
+
       jsonObjects.put(jsonObj.getString("id"), jsonObj);
     }
-    
+
     JSONObject me = jsonObjects.get("me").getJSONObject("result");
     assertEquals("Digg", me.getJSONObject("name").getString("familyName"));
-    
+
     JSONObject json = jsonObjects.get("json").getJSONObject("result");
     JSONObject expected = new JSONObject("{content: {key: 'value'}, status: 200}");
     JsonAssert.assertJsonObjectEquals(expected, json);
-    
+
     JsonAssert.assertObjectEquals("{id: 'var', result: 'value'}", jsonObjects.get("var"));
   }
 
@@ -277,16 +277,16 @@ public class EndToEndTest {
   public void testCajaOsapiBatch() throws Exception {
     executeAllPageTests("osapi/batchTest", true /* caja */);
   }
-  
+
   @Test
   public void testTemplateRewrite() throws Exception {
     HtmlPage page = executePageTest("templateRewriter", null);
-    
+
     // Verify that iteration attributes were processed
     HtmlElement attrs = page.getElementById("attrs");
     List<HtmlElement> attrsList = attrs.getElementsByTagName("li");
     assertEquals(3, attrsList.size());
-    
+
     Element element = page.getElementById("id0");
     assertNotNull(element);
     assertEquals("Jane", element.getTextContent().trim());
@@ -294,20 +294,20 @@ public class EndToEndTest {
     element = page.getElementById("id2");
     assertNotNull(element);
     assertEquals("Maija", element.getTextContent().trim());
-    
+
     // Verify that the repeatTag was processed
     HtmlElement repeat = page.getElementById("repeatTag");
     List<HtmlElement> repeatList = repeat.getElementsByTagName("li");
     assertEquals(1, repeatList.size());
     assertEquals("George", repeatList.get(0).getTextContent().trim());
-    
+
     // Verify that the ifTag was processed
     HtmlElement ifTag = page.getElementById("ifTag");
     List<HtmlElement> ifList = ifTag.getElementsByTagName("li");
     assertEquals(3, ifList.size());
     assertEquals(1, page.getElementsByTagName("b").getLength());
     assertEquals(1, ifList.get(2).getElementsByTagName("b").size());
-    
+
     Element jsonPipeline = page.getElementById("json");
     assertEquals("value", jsonPipeline.getTextContent().trim());
 
@@ -317,7 +317,7 @@ public class EndToEndTest {
     // Test of oncreate
     Element oncreateSpan = page.getElementById("mutate");
     assertEquals("mutated", oncreateSpan.getTextContent().trim());
-    
+
     assertEquals("45", page.getElementById("sum").getTextContent().trim());
     assertEquals("25", page.getElementById("max").getTextContent().trim());
   }
@@ -327,11 +327,11 @@ public class EndToEndTest {
     HtmlPage page = executeAllPageTests("templateLibrary");
     String pageXml = page.asXml();
     assertTrue(pageXml.replaceAll("[\n\r ]", "").contains("p{color:red}"));
-    
+
     Node paragraph = page.getElementsByTagName("p").item(0);
     assertEquals("Hello world", paragraph.getTextContent().trim());
   }
-  
+
   @BeforeClass
   public static void setUpOnce() throws Exception {
     server = new EndToEndServer();
@@ -417,13 +417,15 @@ public class EndToEndTest {
     if (caja) {
       url += "&caja=1&libs=caja";
     }
-    
+
     url += "&nocache=1";
     if (language != null) {
       url += "&lang=" + language;
     }
 
     Page page = webClient.getPage(url);
+    // wait for window.setTimeout, window.setInterval or asynchronous XMLHttpRequest
+    webClient.waitForBackgroundJavaScript(10000);
     if (!(page instanceof HtmlPage)) {
       fail("Got wrong page type. Was: " + page.getWebResponse().getContentType());
     }

Modified: shindig/trunk/java/server/src/test/resources/endtoend/osapi/personTest.xml
URL: http://svn.apache.org/viewvc/shindig/trunk/java/server/src/test/resources/endtoend/osapi/personTest.xml?rev=1241890&r1=1241889&r2=1241890&view=diff
==============================================================================
--- shindig/trunk/java/server/src/test/resources/endtoend/osapi/personTest.xml (original)
+++ shindig/trunk/java/server/src/test/resources/endtoend/osapi/personTest.xml Wed Feb  8 13:24:41 2012
@@ -80,7 +80,35 @@
             }
 
             osapi.people.get({ userId : "not.a.real.id", fields : ["id", "displayName"]}).execute(receivedData);
-          }
+          },
+
+          /** Test updating a person */
+          updatePersonWhenViewerAllowed: function() {
+            function receivedData(response) {
+              var data = gadgets.json.stringify(response);
+              assertFalse('Data error', response.error);
+              assertEquals("Ids don't match - " + data, "john.doe", response.id);
+              assertEquals("Thumbnails URLs don't match - " + data, "http://url.com", response.thumbnailUrl);
+              finished();
+            }
+
+            // viewer is "john.doe" from security token
+            osapi.people.update({person : {thumbnailUrl: "http://url.com"}}).execute(receivedData);
+          },
+
+          /** Test updating a person */
+          updatePersonWhenViewerNotAllowed: function() {
+            function receivedData(response) {
+              var data = gadgets.json.stringify(response);
+              assertTrue('Should have error - ' + data, response.error);
+              assertEquals("Forbidden " + data, "403", response.error.code);
+              finished();
+            }
+
+            // viewer is "john.doe" from security token
+            osapi.people.update({userId: "canonical", person : {thumbnailUrl: "http://url.com"}}).execute(receivedData);
+          },
+
         };
 
       </script>

Modified: shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/PersonHandler.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/PersonHandler.java?rev=1241890&r1=1241889&r2=1241890&view=diff
==============================================================================
--- shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/PersonHandler.java (original)
+++ shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/PersonHandler.java Wed Feb  8 13:24:41 2012
@@ -37,6 +37,7 @@ import org.apache.shindig.social.opensoc
 
 import com.google.common.base.Objects;
 import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
 import com.google.inject.Inject;
 
 /**
@@ -111,6 +112,27 @@ public class PersonHandler {
     return personService.getPeople(userIds, groupId, options, fields, request.getToken());
   }
 
+  /**
+   * Allowed end-points /people/{userId}/{groupId}
+   *
+   * examples: /people/john.doe/@all /people/john.doe/@friends /people/john.doe/@self
+   */
+  @Operation(httpMethods = "PUT", bodyParam = "person")
+  public Future<?> update(SocialRequestItem request) throws ProtocolException {
+    Set<UserId> userIds = request.getUsers();
+
+    // Enforce preconditions - exactly one user is specified
+    HandlerPreconditions.requireNotEmpty(userIds, "No userId specified");
+    HandlerPreconditions.requireSingular(userIds, "Multiple userIds not supported");
+
+    UserId userId = userIds.iterator().next();
+
+    // Update person and return it
+    return personService.updatePerson(Iterables.getOnlyElement(userIds),
+        request.getTypedParameter("person", Person.class),
+        request.getToken());
+  }
+
   @Operation(httpMethods = "GET", path="/@supportedFields")
   public List<Object> supportedFields(RequestItem request) {
     // TODO: Would be nice if name in config matched name of service.

Modified: shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/spi/PersonService.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/spi/PersonService.java?rev=1241890&r1=1241889&r2=1241890&view=diff
==============================================================================
--- shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/spi/PersonService.java (original)
+++ shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/spi/PersonService.java Wed Feb  8 13:24:41 2012
@@ -80,4 +80,16 @@ public interface PersonService {
    */
   Future<Person> getPerson(UserId id, Set<String> fields, SecurityToken token)
       throws ProtocolException;
+
+  /**
+   * Updates person that corresponds to the passed in person id and updates him
+   *
+   * @param id The id of the person to fetch.
+   * @param request The request object
+   * @param fields The fields to fetch.
+   * @param token The gadget token
+   * @return a list of people.
+   */
+  Future<Person> updatePerson(UserId id, Person person, SecurityToken token)
+      throws ProtocolException;
 }

Modified: shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/sample/spi/JsonDbOpensocialService.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/sample/spi/JsonDbOpensocialService.java?rev=1241890&r1=1241889&r2=1241890&view=diff
==============================================================================
--- shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/sample/spi/JsonDbOpensocialService.java (original)
+++ shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/sample/spi/JsonDbOpensocialService.java Wed Feb  8 13:24:41 2012
@@ -151,7 +151,7 @@ public class JsonDbOpensocialService imp
   String jsonLocation, @Named("shindig.bean.converter.json")
   BeanConverter converter,
   @Named("shindig.contextroot") String contextroot) throws Exception {
-    String content = IOUtils.toString(ResourceLoader.openResource(jsonLocation), "UTF-8");    
+    String content = IOUtils.toString(ResourceLoader.openResource(jsonLocation), "UTF-8");
     this.db = new JSONObject(content.replace("%contextroot%", contextroot));
     this.converter = converter;
   }
@@ -306,7 +306,7 @@ public class JsonDbOpensocialService imp
           je);
     }
   }
-  
+
   /** {@inheritDoc} */
   public Future<RestfulCollection<Person>> getPeople(Set<UserId> userIds, GroupId groupId,
       CollectionOptions options, Set<String> fields, SecurityToken token) throws ProtocolException {
@@ -382,6 +382,54 @@ public class JsonDbOpensocialService imp
     }
   }
 
+  /** {@inheritDoc} */
+  public Future<Person> updatePerson(UserId id, Person person, SecurityToken token)
+      throws ProtocolException {
+    try {
+      String viewer = token.getViewerId(); // viewer
+      String user = id.getUserId(token); // person to update
+
+      if (!viewerCanUpdatePerson(viewer,user)) {
+        throw new ProtocolException(HttpServletResponse.SC_FORBIDDEN, "User '" + viewer + "' does not have enough privileges to update person '"+user+"'");
+      }
+
+      JSONArray people = db.getJSONArray(PEOPLE_TABLE);
+
+      for (int i = 0; i < people.length(); i++) {
+        JSONObject curPerson = people.getJSONObject(i);
+
+        if (user != null && curPerson.getString(Person.Field.ID.toString()).equals(user)) {
+          // Convert user to JSON and set ID
+          JSONObject jsonPerson = convertToJson(person);
+          // go through all properties to update in the submitted person object
+          // and change them in the current person object
+          for (String key : JSONObject.getNames(jsonPerson)) {
+            curPerson.put(key,jsonPerson.get(key));
+          }
+
+          people.put(i,curPerson);
+          return ImmediateFuture.newInstance(converter.convertToObject(curPerson.toString(), Person.class));
+        }
+      }
+
+      // Error - no album found to update with given ID
+      throw new ProtocolException(HttpServletResponse.SC_BAD_REQUEST, "User ID " + user + " does not exist");
+    } catch (JSONException je) {
+      throw new ProtocolException(
+          HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+          je.getMessage(), je);
+    }
+
+  }
+
+  /** Check if a viewer is allowed to update the given person record. **/
+  protected boolean viewerCanUpdatePerson(String viewer, String person) {
+    // A person can only update his own personal data (by default)
+    // if you wish to allow other people to update the personal data of the user
+    // you should change the current function
+    return viewer.equals(person) ? true : false;
+  }
+
   private Map<String, Object> getPersonAppData(String id, Set<String> fields) {
     try {
       Map<String, Object> appData = null;
@@ -1176,7 +1224,7 @@ public class JsonDbOpensocialService imp
           je.getMessage(), je);
     }
   }
-  
+
   // Are fields really needed here?
   /** {@inheritDoc} */
   public Future<ActivityEntry> updateActivityEntry(UserId userId, GroupId groupId, String appId,
@@ -1199,7 +1247,7 @@ public class JsonDbOpensocialService imp
         jsonArray = new JSONArray();
         db.getJSONObject(ACTIVITYSTREAMS_TABLE).put(userId.getUserId(token), jsonArray);
       }
-      
+
       // Find & replace activity
       for (int i = 0; i < jsonArray.length(); i++) {
         JSONObject entry = jsonArray.getJSONObject(i);
@@ -1213,7 +1261,7 @@ public class JsonDbOpensocialService imp
       throw new ProtocolException(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, je.getMessage(), je);
     }
   }
-  
+
   // Are fields really needed here?
   /** {@inheritDoc} */
   public Future<ActivityEntry> createActivityEntry(UserId userId, GroupId groupId, String appId,
@@ -1232,7 +1280,7 @@ public class JsonDbOpensocialService imp
         jsonArray = new JSONArray();
         db.getJSONObject(ACTIVITYSTREAMS_TABLE).put(userId.getUserId(token), jsonArray);
       }
-      
+
       // Ensure activity does not already exist
       for (int i = 0; i < jsonArray.length(); i++) {
         JSONObject entry = jsonArray.getJSONObject(i);
@@ -1277,7 +1325,7 @@ public class JsonDbOpensocialService imp
   public Future<ActivityEntry> getActivityEntry(UserId userId, GroupId groupId,
       String appId, Set<String> fields, String activityId, SecurityToken token)
       throws ProtocolException {
-    try {       
+    try {
       String user = userId.getUserId(token);
       if (db.getJSONObject(ACTIVITYSTREAMS_TABLE).has(user)) {
         JSONArray activityEntries = db.getJSONObject(ACTIVITYSTREAMS_TABLE).getJSONArray(user);
@@ -1293,7 +1341,7 @@ public class JsonDbOpensocialService imp
       throw new ProtocolException(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, je.getMessage(), je);
     }
   }
-  
+
 
 /** {@inheritDoc} */
   public Future<RestfulCollection<ActivityEntry>> getActivityEntries(
@@ -1325,7 +1373,7 @@ public class JsonDbOpensocialService imp
       UserId userId, GroupId groupId, String appId, Set<String> fields,
       CollectionOptions options, Set<String> activityIds, SecurityToken token)
       throws ProtocolException {
-    List<ActivityEntry> result = Lists.newArrayList();    
+    List<ActivityEntry> result = Lists.newArrayList();
     try {
       String user = userId.getUserId(token);
       if (db.getJSONObject(ACTIVITYSTREAMS_TABLE).has(user)) {
@@ -1359,7 +1407,7 @@ public class JsonDbOpensocialService imp
     // TODO Not using fields yet
     return new JSONObject(converter.convertToString(activity));
   }
-  
+
   private JSONObject convertFromActivityEntry(ActivityEntry activityEntry, Set<String> fields) throws JSONException {
     // TODO Not using fields yet
     return new JSONObject(converter.convertToString(activityEntry));

Modified: shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/opensocial/service/PersonHandlerTest.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/opensocial/service/PersonHandlerTest.java?rev=1241890&r1=1241889&r2=1241890&view=diff
==============================================================================
--- shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/opensocial/service/PersonHandlerTest.java (original)
+++ shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/opensocial/service/PersonHandlerTest.java Wed Feb  8 13:24:41 2012
@@ -51,6 +51,7 @@ import org.json.JSONObject;
 import org.junit.Before;
 import org.junit.Test;
 
+import java.io.StringReader;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -66,7 +67,7 @@ public class PersonHandlerTest extends E
       Person.Field.NAME.toString(),
       Person.Field.THUMBNAIL_URL.toString());
 
-  private static final Set<UserId> JOHN_DOE = 
+  private static final Set<UserId> JOHN_DOE =
       ImmutableSet.of(new UserId(UserId.Type.userId, "john.doe"));
 
   private static CollectionOptions DEFAULT_OPTIONS = new CollectionOptions();
@@ -239,6 +240,27 @@ public class PersonHandlerTest extends E
   }
 
   @Test
+  public void testHandlePut() throws Exception {
+    String jsonPerson = "{person: {aboutMe: 'A person'}}";
+
+    String path = "/people/john.doe/@self";
+    RestHandler operation = registry.getRestHandler(path, "PUT");
+
+    Person person = new PersonImpl();
+    expect(converter.convertToObject(eq(jsonPerson), eq(Person.class)))
+        .andReturn(person);
+
+    expect(personService.updatePerson(eq(JOHN_DOE.iterator().next()),
+        eq(person),
+        eq(token))).andReturn(ImmediateFuture.newInstance(person));
+
+    replay();
+    assertEquals(person, operation.execute(Maps.<String, String[]>newHashMap(),
+        new StringReader(jsonPerson), token, converter).get());
+    verify();
+  }
+
+  @Test
   public void testHandleGetSupportedFields() throws Exception {
     String path = "/people/@supportedFields";
     RestHandler operation = registry.getRestHandler(path, "GET");

Modified: shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/sample/spi/JsonDbOpensocialServiceTest.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/sample/spi/JsonDbOpensocialServiceTest.java?rev=1241890&r1=1241889&r2=1241890&view=diff
==============================================================================
--- shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/sample/spi/JsonDbOpensocialServiceTest.java (original)
+++ shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/sample/spi/JsonDbOpensocialServiceTest.java Wed Feb  8 13:24:41 2012
@@ -41,6 +41,10 @@ import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
 
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
 import com.google.inject.Guice;
@@ -275,7 +279,7 @@ public class JsonDbOpensocialServiceTest
     assertTrue(responseItem.getEntry().get(CANONICAL_USER_ID).containsKey("newvalue"));
     assertEquals("20", responseItem.getEntry().get(CANONICAL_USER_ID).get("newvalue"));
   }
-  
+
   @Test
   public void testGetExpectedActivityEntries() throws Exception {
     RestfulCollection<ActivityEntry> responseItem = db.getActivityEntries(
@@ -317,4 +321,58 @@ public class JsonDbOpensocialServiceTest
       assertEquals(HttpServletResponse.SC_BAD_REQUEST, sse.getCode());
     }
   }
+
+  @Test
+  public void testViewerCanUpdatePerson() throws Exception {
+    // Create new user
+    JSONArray people = db.getDb().getJSONArray("people");
+    JSONObject jsonPerson = new JSONObject();
+    jsonPerson.put("id", "updatePerson");
+    people.put(people.length(),jsonPerson);
+
+    SecurityToken updateToken = new FakeGadgetToken("appId", "appUrl", "domain", "updatePerson", "trustedJson", "updatePerson", "20");
+
+    // Get user
+    UserId userId = new UserId(UserId.Type.userId, "updatePerson");
+    Person person = db
+        .getPerson(userId, Person.Field.ALL_FIELDS, token).get();
+    assertNotNull("User 'updatePerson' not found", person);
+
+    // update a field in user object
+    person.setThumbnailUrl("http://newthumbnail.url");
+    // Save user to db
+    db.updatePerson(userId, person, updateToken);
+    // Get user again from db and check if the fields were properly updated
+    person = db.getPerson(userId, Person.Field.ALL_FIELDS, token).get();
+    assertNotNull("User 'updatePerson' not found", person);
+
+    assertEquals("http://newthumbnail.url", person.getThumbnailUrl());
+  }
+
+  @Test
+  public void testViewerNotAllowedUpdatePerson() throws Exception {
+    // Create new user
+    JSONArray people = db.getDb().getJSONArray("people");
+    JSONObject jsonPerson = new JSONObject();
+    jsonPerson.put("id", "updatePerson");
+    people.put(people.length(),jsonPerson);
+
+    SecurityToken updateToken = new FakeGadgetToken("appId", "appUrl", "domain", "viewer", "trustedJson", "viewer", "20");
+
+    // Get user
+    UserId userId = new UserId(UserId.Type.userId, "updatePerson");
+    Person person = db
+        .getPerson(userId, Person.Field.ALL_FIELDS, token).get();
+
+    // update a field in user object
+    person.setThumbnailUrl("http://newthumbnail.url");
+    // Save user to db, should throw an exception
+    try {
+      db.updatePerson(userId, person, updateToken);
+      fail();
+    } catch (ProtocolException sse) {
+      assertEquals(HttpServletResponse.SC_FORBIDDEN, sse.getCode());
+    }
+  }
+
 }