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