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