/* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); you * may not use this file except in compliance with the License. You may * obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. See the License for the specific language governing * permissions and limitations under the License. */ package com.android.vts.entity; import com.google.appengine.api.datastore.Entity; import com.google.appengine.api.datastore.Key; import com.google.appengine.api.datastore.KeyFactory; import com.google.appengine.api.datastore.Text; import com.google.appengine.api.users.User; import com.google.gson.Gson; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonPrimitive; import com.google.gson.reflect.TypeToken; import com.googlecode.objectify.annotation.Cache; import com.googlecode.objectify.annotation.Id; import com.googlecode.objectify.annotation.Ignore; import com.googlecode.objectify.annotation.Index; import lombok.Data; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.logging.Level; import java.util.logging.Logger; import static com.googlecode.objectify.ObjectifyService.ofy; @com.googlecode.objectify.annotation.Entity(name = "TestAcknowledgment") @Cache @Data /** Entity describing a test failure acknowledgment. */ public class TestAcknowledgmentEntity implements DashboardEntity { protected static final Logger logger = Logger.getLogger(TestAcknowledgmentEntity.class.getName()); public static final String KIND = "TestAcknowledgment"; public static final String KEY = "key"; public static final String TEST_KEY = "testKey"; public static final String TEST_NAME = "testName"; public static final String USER_OBJ = "userObj"; public static final String CREATED = "created"; public static final String BRANCHES = "branches"; public static final String DEVICES = "devices"; public static final String TEST_CASE_NAMES = "testCaseNames"; public static final String NOTE = "note"; @Ignore private final Key key; @Ignore public final Key test; @Ignore public final User userObj; @Id private Long id; private com.googlecode.objectify.Key testKey; private Set branches; private Set devices; private Set testCaseNames; private String note; private String user; @Index private final long created; /** * Create a AcknowledgmentEntity object. * * @param key The key of the AcknowledgmentEntity in the database. * @param created The timestamp when the entity was created (in microseconds). * @param test The key of the test. * @param userObj The user who created or last modified the entity. * @param branches The list of branch names for which the acknowledgment applies (or null if * all). * @param devices The list of device build flavors for which the acknowledgment applies (or null * if all). * @param testCaseNames The list of test case names known to fail (or null if all). * @param note A text blob with details about the failure (or null if all). */ private TestAcknowledgmentEntity( Key key, long created, Key test, User userObj, List branches, List devices, List testCaseNames, Text note) { this.test = test; this.userObj = userObj; if (branches != null) this.branches = new HashSet(branches); else this.branches = new HashSet<>(); if (devices != null) this.devices = new HashSet(devices); else this.devices = new HashSet<>(); if (testCaseNames != null) this.testCaseNames = new HashSet(testCaseNames); else this.testCaseNames = new HashSet<>(); if (note != null) this.note = note.getValue(); else this.note = null; this.key = key; this.created = created; } /** * Create a AcknowledgmentEntity object. * * @param test The key of the test. * @param userObj The user who created or last modified the entity. * @param branches The list of branch names for which the acknowledgment applies (or null if * all). * @param devices The list of device build flavors for which the acknowledgment applies (or null * if all). * @param testCaseNames The list of test case names known to fail (or null if all). * @param note A text blob with details about the failure (or null if all). */ public TestAcknowledgmentEntity( Key test, User userObj, List branches, List devices, List testCaseNames, Text note) { this(null, -1, test, userObj, branches, devices, testCaseNames, note); } /** Saving function for the instance of this class */ @Override public com.googlecode.objectify.Key save() { return ofy().save().entity(this).now(); } public Entity toEntity() { Entity ackEntity; if (this.key == null) ackEntity = new Entity(KIND); else ackEntity = new Entity(key); ackEntity.setProperty(TEST_KEY, this.test); ackEntity.setProperty(USER_OBJ, this.userObj); long created = this.created; if (created < 0) created = TimeUnit.MILLISECONDS.toMicros(System.currentTimeMillis()); ackEntity.setProperty(CREATED, created); if (this.branches != null && this.branches.size() > 0) ackEntity.setUnindexedProperty(BRANCHES, new ArrayList<>(this.branches)); if (this.devices != null && this.devices.size() > 0) ackEntity.setUnindexedProperty(DEVICES, new ArrayList<>(this.devices)); if (this.testCaseNames != null && this.testCaseNames.size() > 0) ackEntity.setUnindexedProperty(TEST_CASE_NAMES, new ArrayList<>(this.testCaseNames)); if (this.note != null) ackEntity.setUnindexedProperty(NOTE, new Text(this.note)); return ackEntity; } /** * Convert an Entity object to a TestAcknowledgmentEntity. * * @param e The entity to process. * @return TestEntity object with the properties from e processed, or null if incompatible. */ @SuppressWarnings("unchecked") public static TestAcknowledgmentEntity fromEntity(Entity e) { if (!e.getKind().equals(KIND) || !e.hasProperty(TEST_KEY) || !e.hasProperty(USER_OBJ) || !e.hasProperty(CREATED)) { logger.log( Level.WARNING, "Missing attributes in acknowledgment entity: " + e.toString()); return null; } try { Key test = (Key) e.getProperty(TEST_KEY); User user = (User) e.getProperty(USER_OBJ); long created = (long) e.getProperty(CREATED); List branches; if (e.hasProperty(BRANCHES)) branches = (List) e.getProperty(BRANCHES); else branches = null; List devices; if (e.hasProperty(DEVICES)) devices = (List) e.getProperty(DEVICES); else devices = null; List testCaseNames; if (e.hasProperty(TEST_CASE_NAMES)) testCaseNames = (List) e.getProperty(TEST_CASE_NAMES); else testCaseNames = null; Text note = null; if (e.hasProperty(NOTE)) note = (Text) e.getProperty(NOTE); return new TestAcknowledgmentEntity( e.getKey(), created, test, user, branches, devices, testCaseNames, note); } catch (ClassCastException exception) { logger.log(Level.WARNING, "Corrupted data in entity: " + e.getKey()); return null; } } /** * Convert a JSON object to a TestAcknowledgmentEntity. * * @param user The user requesting the conversion. * @param json The json object to convert. * @return TestAcknowledgmentEntity with the data from the json object. */ public static TestAcknowledgmentEntity fromJson(User user, JsonObject json) { try { if (!json.has(TEST_NAME)) return null; String testName = json.get(TEST_NAME).getAsString(); Key testKey = KeyFactory.createKey(TestEntity.KIND, testName); List branches = null; Type listType = new TypeToken>() {}.getType(); if (json.has(BRANCHES)) { branches = new Gson().fromJson(json.get(BRANCHES), listType); } List devices = null; if (json.has(DEVICES)) { devices = new Gson().fromJson(json.get(DEVICES), listType); } List testCaseNames = null; if (json.has(TEST_CASE_NAMES)) { testCaseNames = new Gson().fromJson(json.get(TEST_CASE_NAMES), listType); } Text note = null; if (json.has(NOTE)) { note = new Text(json.get(NOTE).getAsString()); } Key key = null; if (json.has(KEY)) { key = KeyFactory.stringToKey(json.get(KEY).getAsString()); } return new TestAcknowledgmentEntity( key, -1l, testKey, user, branches, devices, testCaseNames, note); } catch (ClassCastException | IllegalStateException e) { return null; } } /** * Convert the entity to a json object. * * @return The entity serialized in JSON format. */ public JsonObject toJson() { JsonObject json = new JsonObject(); json.add(KEY, new JsonPrimitive(KeyFactory.keyToString(this.key))); json.add(TEST_NAME, new JsonPrimitive(this.test.getName())); json.add(USER_OBJ, new JsonPrimitive(this.userObj.getEmail())); json.add(CREATED, new JsonPrimitive(this.created)); List branches = new ArrayList<>(); if (this.branches != null) { for (String branch : this.branches) { branches.add(new JsonPrimitive(branch)); } } json.add(BRANCHES, new Gson().toJsonTree(branches)); List devices = new ArrayList<>(); if (this.devices != null) { for (String device : this.devices) { devices.add(new JsonPrimitive(device)); } } json.add(DEVICES, new Gson().toJsonTree(devices)); List testCaseNames = new ArrayList<>(); if (this.testCaseNames != null) { for (String testCaseName : this.testCaseNames) { testCaseNames.add(new JsonPrimitive(testCaseName)); } } json.add(TEST_CASE_NAMES, new Gson().toJsonTree(testCaseNames)); String note = ""; if (this.note != null) note = this.note; json.add(NOTE, new JsonPrimitive(note)); return json; } }