1 /* 2 * Copyright (c) 2017 Google Inc. All Rights Reserved. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you 5 * may not use this file except in compliance with the License. You may 6 * obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 13 * implied. See the License for the specific language governing 14 * permissions and limitations under the License. 15 */ 16 17 package com.android.vts.api; 18 19 import com.android.vts.entity.TestEntity; 20 import com.android.vts.entity.UserFavoriteEntity; 21 import com.google.appengine.api.datastore.DatastoreService; 22 import com.google.appengine.api.datastore.DatastoreServiceFactory; 23 import com.google.appengine.api.datastore.Entity; 24 import com.google.appengine.api.datastore.EntityNotFoundException; 25 import com.google.appengine.api.datastore.Key; 26 import com.google.appengine.api.datastore.KeyFactory; 27 import com.google.appengine.api.datastore.Query; 28 import com.google.appengine.api.datastore.Query.CompositeFilterOperator; 29 import com.google.appengine.api.datastore.Query.Filter; 30 import com.google.appengine.api.datastore.Query.FilterOperator; 31 import com.google.appengine.api.datastore.Query.FilterPredicate; 32 import com.google.appengine.api.datastore.Transaction; 33 import com.google.appengine.api.users.User; 34 import com.google.appengine.api.users.UserService; 35 import com.google.appengine.api.users.UserServiceFactory; 36 import com.google.gson.Gson; 37 import com.google.gson.JsonObject; 38 import com.google.gson.JsonPrimitive; 39 import java.io.IOException; 40 import java.io.PrintWriter; 41 import java.util.logging.Level; 42 import java.util.logging.Logger; 43 import javax.servlet.http.HttpServlet; 44 import javax.servlet.http.HttpServletRequest; 45 import javax.servlet.http.HttpServletResponse; 46 47 /** Servlet for handling requests to add or remove subscriptions. */ 48 public class UserFavoriteRestServlet extends BaseApiServlet { 49 protected static final Logger logger = 50 Logger.getLogger(UserFavoriteRestServlet.class.getName()); 51 52 /** 53 * Add a new favorite entity. 54 * 55 * @param user The user for which to add a favorite. 56 * @param test The name of the test. 57 * @param muteNotifications True if the subscriber has muted notifications, false otherwise. 58 * @param response The servlet response object. 59 * @return a json object with the generated key to the new favorite entity. 60 * @throws IOException 61 */ addFavorite( User user, String test, boolean muteNotifications, HttpServletResponse response)62 private static JsonObject addFavorite( 63 User user, String test, boolean muteNotifications, HttpServletResponse response) 64 throws IOException { 65 DatastoreService datastore = DatastoreServiceFactory.getDatastoreService(); 66 Key addedTestKey = KeyFactory.createKey(TestEntity.KIND, test); 67 // Filter the tests that exist from the set of tests to add 68 try { 69 datastore.get(addedTestKey); 70 } catch (EntityNotFoundException e) { 71 response.setStatus(HttpServletResponse.SC_BAD_REQUEST); 72 return null; 73 } 74 75 Filter userFilter = 76 new FilterPredicate(UserFavoriteEntity.USER, FilterOperator.EQUAL, user); 77 Filter testFilter = 78 new FilterPredicate( 79 UserFavoriteEntity.TEST_KEY, FilterOperator.EQUAL, addedTestKey); 80 Query q = 81 new Query(UserFavoriteEntity.KIND) 82 .setFilter(CompositeFilterOperator.and(userFilter, testFilter)) 83 .setKeysOnly(); 84 85 Key favoriteKey = null; 86 87 Transaction txn = datastore.beginTransaction(); 88 try { 89 for (Entity e : datastore.prepare(q).asIterable()) { 90 favoriteKey = e.getKey(); 91 break; 92 } 93 if (favoriteKey == null) { 94 UserFavoriteEntity favorite = 95 new UserFavoriteEntity(user, addedTestKey, muteNotifications); 96 Entity entity = favorite.toEntity(); 97 datastore.put(entity); 98 favoriteKey = entity.getKey(); 99 } 100 txn.commit(); 101 } finally { 102 if (txn.isActive()) { 103 logger.log( 104 Level.WARNING, 105 "Transaction rollback forced for favorite creation: " + test); 106 txn.rollback(); 107 } 108 } 109 JsonObject json = new JsonObject(); 110 json.add("key", new JsonPrimitive(KeyFactory.keyToString(favoriteKey))); 111 return json; 112 } 113 114 /** 115 * @param user The user for which to add a favorite. 116 * @param favoriteKey The database key to the favorite entity to update. 117 * @param muteNotifications True if the subscriber has muted notifications, false otherwise. 118 * @param response The servlet response object. 119 * @return a json object with the generated key to the new favorite entity. 120 * @throws IOException 121 */ updateFavorite( User user, Key favoriteKey, boolean muteNotifications, HttpServletResponse response)122 private static JsonObject updateFavorite( 123 User user, Key favoriteKey, boolean muteNotifications, HttpServletResponse response) 124 throws IOException { 125 DatastoreService datastore = DatastoreServiceFactory.getDatastoreService(); 126 Entity favoriteEntity; 127 try { 128 favoriteEntity = datastore.get(favoriteKey); 129 } catch (EntityNotFoundException e) { 130 response.setStatus(HttpServletResponse.SC_BAD_REQUEST); 131 return null; 132 } 133 UserFavoriteEntity favorite = UserFavoriteEntity.fromEntity(favoriteEntity); 134 if (favorite.user.getUserId() == user.getUserId()) { 135 response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); 136 return null; 137 } 138 if (favorite.muteNotifications != muteNotifications) { 139 Transaction txn = datastore.beginTransaction(); 140 try { 141 favorite.muteNotifications = muteNotifications; 142 datastore.put(favorite.toEntity()); 143 txn.commit(); 144 } finally { 145 if (txn.isActive()) { 146 logger.log( 147 Level.WARNING, 148 "Transaction rollback forced for favorite update: " + favoriteKey); 149 txn.rollback(); 150 } 151 } 152 } 153 JsonObject json = new JsonObject(); 154 json.add("key", new JsonPrimitive(KeyFactory.keyToString(favoriteKey))); 155 return json; 156 } 157 158 /** Add a test to the user's favorites. */ 159 @Override doPost(HttpServletRequest request, HttpServletResponse response)160 public void doPost(HttpServletRequest request, HttpServletResponse response) 161 throws IOException { 162 UserService userService = UserServiceFactory.getUserService(); 163 User currentUser = userService.getCurrentUser(); 164 165 boolean muteNotifications = false; 166 if (request.getParameter(UserFavoriteEntity.MUTE_NOTIFICATIONS) != null) { 167 muteNotifications = 168 Boolean.parseBoolean( 169 request.getParameter(UserFavoriteEntity.MUTE_NOTIFICATIONS)); 170 } 171 172 String userFavoritesKeyString = request.getParameter("userFavoritesKey"); 173 String testName = request.getParameter("testName"); 174 175 JsonObject returnData = null; 176 if (userFavoritesKeyString != null) { 177 Key userFavoritesKey = KeyFactory.stringToKey(userFavoritesKeyString); 178 returnData = updateFavorite(currentUser, userFavoritesKey, muteNotifications, response); 179 } else if (testName != null) { 180 returnData = addFavorite(currentUser, testName, muteNotifications, response); 181 } 182 183 if (returnData != null) { 184 response.setContentType("application/json"); 185 PrintWriter writer = response.getWriter(); 186 writer.print(new Gson().toJson(returnData)); 187 writer.flush(); 188 } 189 } 190 191 /** Remove a test from the user's favorites. */ 192 @Override doDelete(HttpServletRequest request, HttpServletResponse response)193 public void doDelete(HttpServletRequest request, HttpServletResponse response) 194 throws IOException { 195 DatastoreService datastore = DatastoreServiceFactory.getDatastoreService(); 196 String stringKey = request.getPathInfo(); 197 if (stringKey == null) { 198 response.setStatus(HttpServletResponse.SC_BAD_REQUEST); 199 return; 200 } 201 if (stringKey.startsWith("/")) { 202 stringKey = stringKey.substring(1); 203 } 204 datastore.delete(KeyFactory.stringToKey(stringKey)); 205 response.setStatus(HttpServletResponse.SC_OK); 206 } 207 } 208