1 /*
2  * Copyright (C) 2017 The Android Open Source Project
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.entity;
18 
19 import com.google.appengine.api.datastore.Entity;
20 import com.google.appengine.api.datastore.Key;
21 import com.google.appengine.api.datastore.KeyFactory;
22 import com.google.appengine.api.datastore.Text;
23 import com.google.appengine.api.users.User;
24 import com.google.gson.Gson;
25 import com.google.gson.JsonElement;
26 import com.google.gson.JsonObject;
27 import com.google.gson.JsonPrimitive;
28 import com.google.gson.reflect.TypeToken;
29 import com.googlecode.objectify.annotation.Cache;
30 import com.googlecode.objectify.annotation.Id;
31 import com.googlecode.objectify.annotation.Ignore;
32 import com.googlecode.objectify.annotation.Index;
33 import lombok.Data;
34 
35 import java.lang.reflect.Type;
36 import java.util.ArrayList;
37 import java.util.HashSet;
38 import java.util.List;
39 import java.util.Set;
40 import java.util.concurrent.TimeUnit;
41 import java.util.logging.Level;
42 import java.util.logging.Logger;
43 
44 import static com.googlecode.objectify.ObjectifyService.ofy;
45 
46 @com.googlecode.objectify.annotation.Entity(name = "TestAcknowledgment")
47 @Cache
48 @Data
49 /** Entity describing a test failure acknowledgment. */
50 public class TestAcknowledgmentEntity implements DashboardEntity {
51     protected static final Logger logger =
52             Logger.getLogger(TestAcknowledgmentEntity.class.getName());
53 
54     public static final String KIND = "TestAcknowledgment";
55     public static final String KEY = "key";
56     public static final String TEST_KEY = "testKey";
57     public static final String TEST_NAME = "testName";
58     public static final String USER_OBJ = "userObj";
59     public static final String CREATED = "created";
60     public static final String BRANCHES = "branches";
61     public static final String DEVICES = "devices";
62     public static final String TEST_CASE_NAMES = "testCaseNames";
63     public static final String NOTE = "note";
64 
65     @Ignore private final Key key;
66     @Ignore public final Key test;
67     @Ignore public final User userObj;
68 
69     @Id private Long id;
70 
71     private com.googlecode.objectify.Key testKey;
72     private Set<String> branches;
73     private Set<String> devices;
74     private Set<String> testCaseNames;
75     private String note;
76     private String user;
77 
78     @Index private final long created;
79 
80     /**
81      * Create a AcknowledgmentEntity object.
82      *
83      * @param key The key of the AcknowledgmentEntity in the database.
84      * @param created The timestamp when the entity was created (in microseconds).
85      * @param test The key of the test.
86      * @param userObj The user who created or last modified the entity.
87      * @param branches The list of branch names for which the acknowledgment applies (or null if
88      *     all).
89      * @param devices The list of device build flavors for which the acknowledgment applies (or null
90      *     if all).
91      * @param testCaseNames The list of test case names known to fail (or null if all).
92      * @param note A text blob with details about the failure (or null if all).
93      */
TestAcknowledgmentEntity( Key key, long created, Key test, User userObj, List<String> branches, List<String> devices, List<String> testCaseNames, Text note)94     private TestAcknowledgmentEntity(
95             Key key,
96             long created,
97             Key test,
98             User userObj,
99             List<String> branches,
100             List<String> devices,
101             List<String> testCaseNames,
102             Text note) {
103         this.test = test;
104         this.userObj = userObj;
105         if (branches != null) this.branches = new HashSet(branches);
106         else this.branches = new HashSet<>();
107 
108         if (devices != null) this.devices = new HashSet(devices);
109         else this.devices = new HashSet<>();
110 
111         if (testCaseNames != null) this.testCaseNames = new HashSet(testCaseNames);
112         else this.testCaseNames = new HashSet<>();
113 
114         if (note != null) this.note = note.getValue();
115         else this.note = null;
116 
117         this.key = key;
118         this.created = created;
119     }
120 
121     /**
122      * Create a AcknowledgmentEntity object.
123      *
124      * @param test The key of the test.
125      * @param userObj The user who created or last modified the entity.
126      * @param branches The list of branch names for which the acknowledgment applies (or null if
127      *     all).
128      * @param devices The list of device build flavors for which the acknowledgment applies (or null
129      *     if all).
130      * @param testCaseNames The list of test case names known to fail (or null if all).
131      * @param note A text blob with details about the failure (or null if all).
132      */
TestAcknowledgmentEntity( Key test, User userObj, List<String> branches, List<String> devices, List<String> testCaseNames, Text note)133     public TestAcknowledgmentEntity(
134             Key test,
135             User userObj,
136             List<String> branches,
137             List<String> devices,
138             List<String> testCaseNames,
139             Text note) {
140         this(null, -1, test, userObj, branches, devices, testCaseNames, note);
141     }
142 
143     /** Saving function for the instance of this class */
144     @Override
save()145     public com.googlecode.objectify.Key<TestAcknowledgmentEntity> save() {
146         return ofy().save().entity(this).now();
147     }
148 
toEntity()149     public Entity toEntity() {
150         Entity ackEntity;
151         if (this.key == null) ackEntity = new Entity(KIND);
152         else ackEntity = new Entity(key);
153 
154         ackEntity.setProperty(TEST_KEY, this.test);
155         ackEntity.setProperty(USER_OBJ, this.userObj);
156 
157         long created = this.created;
158         if (created < 0) created = TimeUnit.MILLISECONDS.toMicros(System.currentTimeMillis());
159         ackEntity.setProperty(CREATED, created);
160 
161         if (this.branches != null && this.branches.size() > 0)
162             ackEntity.setUnindexedProperty(BRANCHES, new ArrayList<>(this.branches));
163 
164         if (this.devices != null && this.devices.size() > 0)
165             ackEntity.setUnindexedProperty(DEVICES, new ArrayList<>(this.devices));
166 
167         if (this.testCaseNames != null && this.testCaseNames.size() > 0)
168             ackEntity.setUnindexedProperty(TEST_CASE_NAMES, new ArrayList<>(this.testCaseNames));
169 
170         if (this.note != null) ackEntity.setUnindexedProperty(NOTE, new Text(this.note));
171         return ackEntity;
172     }
173 
174     /**
175      * Convert an Entity object to a TestAcknowledgmentEntity.
176      *
177      * @param e The entity to process.
178      * @return TestEntity object with the properties from e processed, or null if incompatible.
179      */
180     @SuppressWarnings("unchecked")
fromEntity(Entity e)181     public static TestAcknowledgmentEntity fromEntity(Entity e) {
182         if (!e.getKind().equals(KIND)
183                 || !e.hasProperty(TEST_KEY)
184                 || !e.hasProperty(USER_OBJ)
185                 || !e.hasProperty(CREATED)) {
186             logger.log(
187                     Level.WARNING, "Missing attributes in acknowledgment entity: " + e.toString());
188             return null;
189         }
190         try {
191             Key test = (Key) e.getProperty(TEST_KEY);
192             User user = (User) e.getProperty(USER_OBJ);
193             long created = (long) e.getProperty(CREATED);
194 
195             List<String> branches;
196             if (e.hasProperty(BRANCHES)) branches = (List<String>) e.getProperty(BRANCHES);
197             else branches = null;
198 
199             List<String> devices;
200             if (e.hasProperty(DEVICES)) devices = (List<String>) e.getProperty(DEVICES);
201             else devices = null;
202 
203             List<String> testCaseNames;
204             if (e.hasProperty(TEST_CASE_NAMES))
205                 testCaseNames = (List<String>) e.getProperty(TEST_CASE_NAMES);
206             else testCaseNames = null;
207 
208             Text note = null;
209             if (e.hasProperty(NOTE)) note = (Text) e.getProperty(NOTE);
210             return new TestAcknowledgmentEntity(
211                     e.getKey(), created, test, user, branches, devices, testCaseNames, note);
212         } catch (ClassCastException exception) {
213             logger.log(Level.WARNING, "Corrupted data in entity: " + e.getKey());
214             return null;
215         }
216     }
217 
218     /**
219      * Convert a JSON object to a TestAcknowledgmentEntity.
220      *
221      * @param user The user requesting the conversion.
222      * @param json The json object to convert.
223      * @return TestAcknowledgmentEntity with the data from the json object.
224      */
fromJson(User user, JsonObject json)225     public static TestAcknowledgmentEntity fromJson(User user, JsonObject json) {
226         try {
227             if (!json.has(TEST_NAME)) return null;
228             String testName = json.get(TEST_NAME).getAsString();
229             Key testKey = KeyFactory.createKey(TestEntity.KIND, testName);
230             List<String> branches = null;
231 
232             Type listType = new TypeToken<List<String>>() {}.getType();
233             if (json.has(BRANCHES)) {
234                 branches = new Gson().fromJson(json.get(BRANCHES), listType);
235             }
236 
237             List<String> devices = null;
238             if (json.has(DEVICES)) {
239                 devices = new Gson().fromJson(json.get(DEVICES), listType);
240             }
241 
242             List<String> testCaseNames = null;
243             if (json.has(TEST_CASE_NAMES)) {
244                 testCaseNames = new Gson().fromJson(json.get(TEST_CASE_NAMES), listType);
245             }
246 
247             Text note = null;
248             if (json.has(NOTE)) {
249                 note = new Text(json.get(NOTE).getAsString());
250             }
251 
252             Key key = null;
253             if (json.has(KEY)) {
254                 key = KeyFactory.stringToKey(json.get(KEY).getAsString());
255             }
256             return new TestAcknowledgmentEntity(
257                     key, -1l, testKey, user, branches, devices, testCaseNames, note);
258         } catch (ClassCastException | IllegalStateException e) {
259             return null;
260         }
261     }
262 
263     /**
264      * Convert the entity to a json object.
265      *
266      * @return The entity serialized in JSON format.
267      */
toJson()268     public JsonObject toJson() {
269         JsonObject json = new JsonObject();
270         json.add(KEY, new JsonPrimitive(KeyFactory.keyToString(this.key)));
271         json.add(TEST_NAME, new JsonPrimitive(this.test.getName()));
272         json.add(USER_OBJ, new JsonPrimitive(this.userObj.getEmail()));
273         json.add(CREATED, new JsonPrimitive(this.created));
274 
275         List<JsonElement> branches = new ArrayList<>();
276         if (this.branches != null) {
277             for (String branch : this.branches) {
278                 branches.add(new JsonPrimitive(branch));
279             }
280         }
281         json.add(BRANCHES, new Gson().toJsonTree(branches));
282 
283         List<JsonElement> devices = new ArrayList<>();
284         if (this.devices != null) {
285             for (String device : this.devices) {
286                 devices.add(new JsonPrimitive(device));
287             }
288         }
289         json.add(DEVICES, new Gson().toJsonTree(devices));
290 
291         List<JsonElement> testCaseNames = new ArrayList<>();
292         if (this.testCaseNames != null) {
293             for (String testCaseName : this.testCaseNames) {
294                 testCaseNames.add(new JsonPrimitive(testCaseName));
295             }
296         }
297         json.add(TEST_CASE_NAMES, new Gson().toJsonTree(testCaseNames));
298 
299         String note = "";
300         if (this.note != null) note = this.note;
301         json.add(NOTE, new JsonPrimitive(note));
302         return json;
303     }
304 }
305