From 0a4bbf5fe413737bbbe57a63ee654d385eb34ca2 Mon Sep 17 00:00:00 2001 From: "Petti, Ken" Date: Sun, 15 Apr 2018 06:37:52 -0400 Subject: [PATCH 01/16] Adding pom updates w/Amazon SDK --- pom.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pom.xml b/pom.xml index c24b10c5..ebe2acc2 100644 --- a/pom.xml +++ b/pom.xml @@ -245,6 +245,11 @@ 1.35 test + + com.amazonaws + aws-java-sdk + 1.11.106 + From 779c8a5df09c313c65719467d3522095e3b538f1 Mon Sep 17 00:00:00 2001 From: "Petti, Ken" Date: Sun, 15 Apr 2018 06:38:51 -0400 Subject: [PATCH 02/16] adding dynamodbknowledgebase + tests --- .../plugins/bfa/db/DynamoDBKnowledgeBase.java | 284 ++++++++++++++++++ .../bfa/db/DynamoDBKnowledgeBaseTest.java | 235 +++++++++++++++ 2 files changed, 519 insertions(+) create mode 100644 src/main/java/com/sonyericsson/jenkins/plugins/bfa/db/DynamoDBKnowledgeBase.java create mode 100644 src/test/java/com/sonyericsson/jenkins/plugins/bfa/db/DynamoDBKnowledgeBaseTest.java diff --git a/src/main/java/com/sonyericsson/jenkins/plugins/bfa/db/DynamoDBKnowledgeBase.java b/src/main/java/com/sonyericsson/jenkins/plugins/bfa/db/DynamoDBKnowledgeBase.java new file mode 100644 index 00000000..0930ce0c --- /dev/null +++ b/src/main/java/com/sonyericsson/jenkins/plugins/bfa/db/DynamoDBKnowledgeBase.java @@ -0,0 +1,284 @@ +package com.sonyericsson.jenkins.plugins.bfa.db; + +import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapper; +import com.amazonaws.services.dynamodbv2.model.*; +import com.amazonaws.services.dynamodbv2.util.TableUtils; +import com.sonyericsson.jenkins.plugins.bfa.model.FailureCause; +import com.sonyericsson.jenkins.plugins.bfa.statistics.Statistics; +import hudson.model.Descriptor; +import com.amazonaws.AmazonClientException; +import com.amazonaws.auth.profile.ProfileCredentialsProvider; +import com.amazonaws.services.dynamodbv2.AmazonDynamoDB; +import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClientBuilder; +import org.kohsuke.stapler.DataBoundConstructor; + + +import java.util.*; + +public class DynamoDBKnowledgeBase extends KnowledgeBase { + + private static AmazonDynamoDB dynamoDB; + private static DynamoDBMapper dbMapper; + private String host; + private int port; + private String tableName; + + @DataBoundConstructor + public DynamoDBKnowledgeBase(String host, int port, String tableName) { + this.host = host; + this.port = port; + this.tableName = tableName; + } + + /** + * Get the list of {@link FailureCause}s. It is intended to be used in the scanning phase hence it should be + * returned as quickly as possible, so the list could be cached. + * + * @return the full list of causes. + * @throws Exception if something in the KnowledgeBase handling goes wrong. + */ + @Override + public Collection getCauses() throws Exception { + return null; + } + + /** + * Get the list of the {@link FailureCause}'s names and ids. The list should be the latest possible from the DB as + * they will be used for editing. The objects returned should contain at least the id and the name of the cause. + * + * @return the full list of the names and ids of the causes. + * @throws Exception if something in the KnowledgeBase handling goes wrong. + */ + @Override + public Collection getCauseNames() throws Exception { + return null; + } + + /** + * Get a shallow list of the {@link FailureCause}s. The list should be the latest possible from the DB as + * they will be used in the list of causes to edit. + * shallow meaning no indications but information enough to show a nice list; at least id and name but description, + * comment, lastOccurred and categories are preferred as well. + * + * @return a shallow list of all causes. + * @throws Exception if something in the KnowledgeBase handling goes wrong. + * @see #getCauseNames() + */ + @Override + public Collection getShallowCauses() throws Exception { + return null; + } + + /** + * Get the cause with the given id. The cause returned is intended to be edited right away, so it should be as fresh + * from the db as possible. + * + * @param id the id of the cause. + * @return the cause or null if a cause with that id could not be found. + * @throws Exception if something in the KnowledgeBase handling goes wrong. + */ + @Override + public FailureCause getCause(String id) throws Exception { + try { + return getDbMapper().load(FailureCause.class, id); + } catch (Exception e) { + throw e; + } + } + + /** + * Saves a new cause to the db and generates a new id for the cause. + * + * @param cause the cause to add. + * @return the same cause but with a new id. + * @throws Exception if something in the KnowledgeBase handling goes wrong. + */ + @Override + public FailureCause addCause(FailureCause cause) throws Exception { + return saveCause(cause); + } + + /** + * Removes the cause from the knowledge base. + * + * @param id the id of the cause to remove. + * @return the removed FailureCause. + * @throws Exception if so. + */ + @Override + public FailureCause removeCause(String id) throws Exception { + return null; + } + + /** + * Saves a cause to the db. Assumes that the id is kept from when it was fetched. Can also be an existing cause in + * another {@link KnowledgeBase} implementation with a preexisting id that is being converted via {@link + * #convertFrom(KnowledgeBase)}. + * + * @param cause the cause to add. + * @return the same cause but with a new id. + * @throws Exception if something in the KnowledgeBase handling goes wrong. + */ + @Override + public FailureCause saveCause(FailureCause cause) throws Exception { + try { + ListTablesResult tables = getDynamoDb().listTables(); + if (!tables.getTableNames().contains(tableName)) { + createTables(); + } + + getDbMapper().save(cause); + } catch (Exception e) { + throw e; + } + return cause; + } + + /** + * Converts the existing old knowledge base into this one. Will be called after the creation of a new object when + * then Jenkins config is saved, So it could just be that the old one is exactly the same as this one. + * + * @param oldKnowledgeBase the old one. + * @throws Exception if something in the KnowledgeBase handling goes wrong. + */ + @Override + public void convertFrom(KnowledgeBase oldKnowledgeBase) throws Exception { + + } + + /** + * Gets the unique categories of all FailureCauses. + * + * @return the list of categories. + * @throws Exception if something in the KnowledgeBase handling goes wrong. + */ + @Override + public List getCategories() throws Exception { + return null; + } + + /** + * Called to see if the configuration has changed. + * + * @param oldKnowledgeBase the previous config. + * @return true if it is the same. + */ + @Override + public boolean equals(KnowledgeBase oldKnowledgeBase) { + return false; + } + + /** + * Called when the KnowledgeBase should be up and running. + * + * @throws Exception if anything goes wrong during the startup. + */ + @Override + public void start() throws Exception { + getDynamoDb(); + } + + /** + * Called when it is time to clean up after the KnowledgeBase. + */ + @Override + public void stop() { + + } + + /** + * If Statistics logging is enabled on this knowledge base or not. + * + * @return true if so. False if not or not implemented. + */ + @Override + public boolean isStatisticsEnabled() { + return false; + } + + /** + * If all builds should be added to statistics logging, not just unsuccessful builds. + * Only relevant if {@link #isStatisticsEnabled()} is true. + * + * @return true if set, false otherwise or if not implemented + */ + @Override + public boolean isSuccessfulLoggingEnabled() { + return false; + } + + /** + * Saves the Statistics. + * + * @param stat the Statistics. + * @throws Exception if something in the KnowledgeBase handling goes wrong. + */ + @Override + public void saveStatistics(Statistics stat) throws Exception { + + } + + public AmazonDynamoDB getDynamoDb() { + /* + * The ProfileCredentialsProvider will return your [default] + * credential profile by reading from the credentials file located at + * (~/.aws/credentials). + */ + + if (dynamoDB != null) { + return dynamoDB; + } + + ProfileCredentialsProvider credentialsProvider = new ProfileCredentialsProvider(); + try { + credentialsProvider.getCredentials(); + } catch (Exception e) { + throw new AmazonClientException( + "Cannot load the credentials from the credential profiles file. " + + "Please make sure that your credentials file is at the correct " + + "location (~/.aws/credentials), and is in valid format.", + e); + } + + dynamoDB = AmazonDynamoDBClientBuilder.standard() + .withCredentials(credentialsProvider) + .withRegion("us-west-2") +// .withEndpointConfiguration( +// new AwsClientBuilder.EndpointConfiguration("http://localhost:8000", "us-west-2")) + .build(); + + return dynamoDB; + } + + public void createTables() { + try { + AmazonDynamoDB db = getDynamoDb(); + CreateTableRequest causeTableRequest = new CreateTableRequest() + .withTableName(tableName) + .withKeySchema(new KeySchemaElement().withAttributeName("id").withKeyType(KeyType.HASH)) + .withAttributeDefinitions(new AttributeDefinition().withAttributeName("id").withAttributeType(ScalarAttributeType.S)) + .withProvisionedThroughput(new ProvisionedThroughput().withReadCapacityUnits(1L).withWriteCapacityUnits(1L)); + + TableUtils.createTableIfNotExists(db, causeTableRequest); + TableUtils.waitUntilActive(db, tableName); + + dynamoDB.listTables(); + + } catch (Exception e) { + throw new AmazonClientException(e); + } + } + + public DynamoDBMapper getDbMapper() { + if (dbMapper != null) { + return dbMapper; + } + dbMapper = new DynamoDBMapper(getDynamoDb()); + return dbMapper; + } + + @Override + public Descriptor getDescriptor() { + return null; + } +} diff --git a/src/test/java/com/sonyericsson/jenkins/plugins/bfa/db/DynamoDBKnowledgeBaseTest.java b/src/test/java/com/sonyericsson/jenkins/plugins/bfa/db/DynamoDBKnowledgeBaseTest.java new file mode 100644 index 00000000..4f0747a6 --- /dev/null +++ b/src/test/java/com/sonyericsson/jenkins/plugins/bfa/db/DynamoDBKnowledgeBaseTest.java @@ -0,0 +1,235 @@ +/* + * The MIT License + * + * Copyright 2012 Sony Mobile Communications Inc. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.sonyericsson.jenkins.plugins.bfa.db; + +import com.mongodb.DBObject; +import com.mongodb.MongoException; +import com.amazonaws.services.dynamodbv2.AmazonDynamoDB; +import com.sonyericsson.jenkins.plugins.bfa.model.FailureCause; +import com.sonyericsson.jenkins.plugins.bfa.model.indication.BuildLogIndication; +import com.sonyericsson.jenkins.plugins.bfa.model.indication.Indication; +import com.sonyericsson.jenkins.plugins.bfa.statistics.Statistics; +import jenkins.model.Jenkins; +import net.vz.mongodb.jackson.DBCursor; +import net.vz.mongodb.jackson.JacksonDBCollection; +import net.vz.mongodb.jackson.WriteResult; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Matchers; +import org.mockito.Mock; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.powermock.core.classloader.annotations.PowerMockIgnore; +import org.powermock.reflect.Whitebox; + + +import java.util.*; + +import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertNotNull; +import static junit.framework.Assert.assertSame; +import static junit.framework.Assert.assertTrue; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyInt; +import static org.mockito.Matchers.anyString; +import static org.powermock.api.mockito.PowerMockito.doReturn; +import static org.powermock.api.mockito.PowerMockito.mock; +import static org.powermock.api.mockito.PowerMockito.when; + + +/** + * Tests for the DynamoDBKnowledgeBase. + * + * @author Tomas Westling <tomas.westling@sonyericsson.com> + */ +@RunWith(PowerMockRunner.class) +@PrepareForTest({Jenkins.class, Jenkins.DescriptorImpl.class}) +@PowerMockIgnore( {"javax.*"}) +public class DynamoDBKnowledgeBaseTest { + + + @Mock + private Jenkins jenkins; + + private JacksonDBCollection collection; + private JacksonDBCollection statisticsCollection; + private DynamoDBKnowledgeBase kb; + private List indications; + private Indication indication; + private FailureCause mockedCause; + private Statistics mockedStatistics; + private static final int PORT = 27017; + + /** + * Common stuff to set up for the tests. + */ + @Before + public void setUp() { + PowerMockito.mockStatic(Jenkins.class); + PowerMockito.when(Jenkins.getInstance()).thenReturn(jenkins); + kb = new DynamoDBKnowledgeBase("localhost", 8000, "failureCauses"); +// collection = mock(JacksonDBCollection.class); +// statisticsCollection = mock(JacksonDBCollection.class); +// Whitebox.setInternalState(kb, "jacksonCollection", collection); +// Whitebox.setInternalState(kb, "jacksonStatisticsCollection", statisticsCollection); +// indications = new LinkedList(); +// indication = new BuildLogIndication("something"); +// indications.add(indication); +// mockedCause = new FailureCause("id", "myFailureCause", "description", "comment", new Date(), +// "category", indications, null); +// mockedStatistics = new Statistics("projectName", 1, "", null, 1, null, "nodeName", "master", 0, "result", +// null, null); + } + + /** + * Tests finding one cause by its id. + * + * @throws Exception if so. + */ + @Test + public void testFindOneCause() throws Exception { +// when(collection.findOneById(anyString())).thenReturn(mockedCause); + FailureCause fetchedCause = kb.getCause("2ce2ae7b-7f66-4a8c-984a-802a43d3a9a4"); +// assertNotNull("The fetched cause should not be null", fetchedCause); +// assertSame(mockedCause, fetchedCause); + } + + /** + * Tests finding all causes. + * + * @throws Exception if so. + */ + @Test + public void testGetCauseNames() throws Exception { + DBCursor cursor = mock(DBCursor.class); + List list = new LinkedList(); + list.add(mockedCause); + when(cursor.next()).thenReturn(mockedCause); + when(cursor.hasNext()).thenReturn(true, false); + doReturn(cursor).when(collection).find(Matchers.any(), Matchers.any()); + Collection fetchedCauses = kb.getCauseNames(); + assertNotNull("The fetched cause should not be null", fetchedCauses); + Iterator fetch = fetchedCauses.iterator(); + assertTrue(fetch.hasNext()); + assertSame(mockedCause, fetch.next()); + } + + /** + * Tests adding one cause. + * + * @throws Exception if so. + */ + @Test + public void testAddCause() throws Exception { + WriteResult result = mock(WriteResult.class); + when(result.getSavedObject()).thenReturn(mockedCause); + MongoDBKnowledgeBaseCache cache = mock(MongoDBKnowledgeBaseCache.class); + Whitebox.setInternalState(kb, cache); + doReturn(result).when(collection).insert(Matchers.any()); + FailureCause addedCause = kb.addCause(mockedCause); + assertNotNull(addedCause); + assertSame(mockedCause, addedCause); + } + + /** + * Tests saving one cause. + * + * @throws Exception if so. + */ + @Test + public void testSaveCause() throws Exception { + indications = new LinkedList(); + indication = new BuildLogIndication("something"); + indications.add(indication); + mockedCause = new FailureCause(null, "myFailureCause", "description", "comment", new Date(), + "category", indications, null); + kb.saveCause(mockedCause); + + +// WriteResult result = mock(WriteResult.class); +// when(result.getSavedObject()).thenReturn(mockedCause); +// MongoDBKnowledgeBaseCache cache = mock(MongoDBKnowledgeBaseCache.class); +// Whitebox.setInternalState(kb, cache); +// doReturn(result).when(collection).save(Matchers.any()); +// FailureCause addedCause = kb.saveCause(mockedCause); +// assertNotNull(addedCause); +// assertSame(mockedCause, addedCause); + } + + /** + * Tests fetching statistics. + * + * @throws Exception if unable to fetch statistics. + */ + @Test + public void testGetStatistics() throws Exception { + DBCursor cursor = mock(DBCursor.class); + List list = new LinkedList(); + list.add(mockedStatistics); + + doReturn(cursor).when(statisticsCollection).find(Matchers.any()); + when(cursor.limit(anyInt())).thenReturn(cursor); + when(cursor.sort(any(DBObject.class))).thenReturn(cursor); + when(cursor.toArray()).thenReturn(list); + + List fetchedStatistics = kb.getStatistics(null, 1); + assertNotNull("The fetched statistics should not be null", fetchedStatistics); + assertFalse("The fetched statistics list should not be empty", fetchedStatistics.isEmpty()); + assertSame(mockedStatistics, fetchedStatistics.get(0)); + } + + /** + * Tests that the mongo exception caused by the collection gets thrown from the knowledgebase. + * + * @throws Exception if so. + */ + @Test(expected = MongoException.class) + public void testThrowMongo() throws Exception { + when(collection.find(Matchers.any(), Matchers.any())).thenThrow(MongoException.class); + kb.getCauseNames(); + } + + /** + * Tests that the mongo exception caused by the collection gets thrown from the knowledgebase. + * + * @throws Exception if so. + */ + @Test + public void testGetDynamoDB() throws Exception { + kb.getDynamoDb(); + } + + /** + * Tests that the mongo exception caused by the collection gets thrown from the knowledgebase. + * + * @throws Exception if so. + */ + @Test + public void testCreateTables() throws Exception { + kb.createTables(); + } +} From b4844a2106a8b1154bc2701ecea39b9722ff2b7a Mon Sep 17 00:00:00 2001 From: "Petti, Ken" Date: Sun, 15 Apr 2018 06:39:05 -0400 Subject: [PATCH 03/16] dynamodb-related updates for failurecause --- .../plugins/bfa/model/FailureCause.java | 104 +++++++++++++++++- 1 file changed, 99 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/sonyericsson/jenkins/plugins/bfa/model/FailureCause.java b/src/main/java/com/sonyericsson/jenkins/plugins/bfa/model/FailureCause.java index a9b36edc..ab7f351d 100644 --- a/src/main/java/com/sonyericsson/jenkins/plugins/bfa/model/FailureCause.java +++ b/src/main/java/com/sonyericsson/jenkins/plugins/bfa/model/FailureCause.java @@ -23,6 +23,9 @@ */ package com.sonyericsson.jenkins.plugins.bfa.model; +import com.amazonaws.services.dynamodbv2.datamodeling.*; +import org.codehaus.jackson.type.TypeReference; +import com.fasterxml.jackson.databind.type.MapType; import com.sonyericsson.jenkins.plugins.bfa.CauseManagement; import com.sonyericsson.jenkins.plugins.bfa.PluginImpl; import com.sonyericsson.jenkins.plugins.bfa.db.KnowledgeBase; @@ -47,6 +50,7 @@ import org.codehaus.jackson.annotate.JsonIgnoreProperties; import org.codehaus.jackson.annotate.JsonIgnoreType; import org.codehaus.jackson.annotate.JsonProperty; +import org.codehaus.jackson.map.ObjectMapper; import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.QueryParameter; import org.kohsuke.stapler.Stapler; @@ -54,10 +58,7 @@ import org.kohsuke.stapler.StaplerResponse; import java.io.Serializable; -import java.util.Arrays; -import java.util.Date; -import java.util.LinkedList; -import java.util.List; +import java.util.*; import java.util.logging.Level; import java.util.logging.Logger; @@ -67,16 +68,27 @@ * * @author Tomas Westling <thomas.westling@sonyericsson.com> */ +@DynamoDBTable(tableName = "failureCauses") @JsonIgnoreProperties(ignoreUnknown = true) public class FailureCause implements Serializable, Action, Describable { private static final Logger logger = Logger.getLogger(FailureCause.class.getName()); + @DynamoDBHashKey(attributeName = "id") + @DynamoDBAutoGeneratedKey private String id; + @DynamoDBAttribute(attributeName = "name") private String name; + @DynamoDBAttribute(attributeName = "description") private String description; + @DynamoDBAttribute(attributeName = "comment") private String comment; + @DynamoDBAttribute(attributeName = "lastOccurred") private Date lastOccurred; + @DynamoDBAttribute(attributeName = "categories") private List categories; + @DynamoDBTypeConverted(converter = IndicationsTypeConverter.class) + @DynamoDBAttribute(attributeName = "indications") private List indications; + @DynamoDBAttribute(attributeName = "modifications") private List modifications; /** @@ -339,7 +351,6 @@ public String getId() { /** * The id. * - * @param id the id. */ @Id @ObjectId @@ -356,6 +367,14 @@ public String getName() { return name; } + /** + * Setter for the name. + * + */ + public void setName(String name) { + this.name = name; + } + /** * Getter for the description. * @@ -365,6 +384,12 @@ public String getDescription() { return description; } + /** + * Setter for the description. + * + */ + public void setDescription(String description) { this.description = description; } + /** * Getter for the comment. * @@ -374,6 +399,12 @@ public String getComment() { return comment; } + /** + * Setter for the comment. + * + */ + public void setComment(String comment) { this.comment = comment; } + /** * Getter for the last occurrence. * @@ -393,6 +424,7 @@ public Date getLastOccurred() { * @return the last occurrence. */ @JsonIgnore + @DynamoDBIgnore public Date getAndInitiateLastOccurred() { if (lastOccurred == null && id != null) { loadLastOccurred(); @@ -427,12 +459,21 @@ public List getModifications() { return modifications; } + /** + * Setter for the list of modifications. + * + */ + public void setModifications(List modifications) { + this.modifications = modifications; + } + /** * Initiates the list of modifications if it's not already initiated * and then returns the list. * @return list of modifications */ @JsonIgnore + @DynamoDBIgnore public List getAndInitiateModifications() { if ((modifications == null || modifications.isEmpty()) && id != null) { @@ -456,6 +497,7 @@ public List getCategories() { * @return the categories as a String. */ @JsonIgnore + @DynamoDBIgnore public String getCategoriesAsString() { if (categories == null || categories.isEmpty()) { return null; @@ -515,6 +557,7 @@ private void initModifications() { * @return the latest modification */ @JsonIgnore + @DynamoDBIgnore public FailureCauseModification getLatestModification() { List mods = getAndInitiateModifications(); if (mods != null && !mods.isEmpty()) { @@ -566,6 +609,12 @@ public List getIndications() { return indications; } + /** + * Setter for the list of indications. + * + */ + public void setIndications(List indications) { this.indications = indications; } + //CS IGNORE JavadocMethod FOR NEXT 8 LINES. REASON: The exception can be thrown. /** @@ -575,6 +624,7 @@ public List getIndications() { * @throws IllegalStateException if no ancestor is found. */ @JsonIgnore + @DynamoDBIgnore public CauseManagement getAncestorCauseManagement() { StaplerRequest currentRequest = Stapler.getCurrentRequest(); if (currentRequest == null) { @@ -589,24 +639,28 @@ public CauseManagement getAncestorCauseManagement() { @Override @JsonIgnore + @DynamoDBIgnore public String getIconFileName() { return PluginImpl.getDefaultIcon(); } @Override @JsonIgnore + @DynamoDBIgnore public String getDisplayName() { return name; } @Override @JsonIgnore + @DynamoDBIgnore public String getUrlName() { return id; } @Override + @DynamoDBIgnore public FailureCauseDescriptor getDescriptor() { return Jenkins.getInstance().getDescriptorByType(FailureCauseDescriptor.class); } @@ -688,4 +742,44 @@ public AutoCompletionCandidates doAutoCompleteCategories(@QueryParameter String return candidates; } } + + static public class IndicationsTypeConverter implements DynamoDBTypeConverter>, List> { + private ObjectMapper objectMapper = new ObjectMapper(); + + @Override + public List> convert(List indications) { + List> stringedIndications = new ArrayList<>(); + for (Indication i:indications) { + try { +// Have to convert to the indication to a string to capture the indication type + String stringed = objectMapper.writeValueAsString(i); + TypeReference> typeRef + = new TypeReference>() {}; +// Then convert the stringed to a map, so the whole list of inications can be written as a string + Map mapped = objectMapper.readValue(stringed, typeRef); + stringedIndications.add(mapped); + } catch (Exception e) { + e.printStackTrace(); + } + } + + return stringedIndications; + } + + @Override + public List unconvert(List> s) { + List indications = new ArrayList<>(); + for (Map i:s) { + String clazz = i.get("@class"); + try { + Object obj = Class.forName(clazz).getConstructor(String.class).newInstance(i.get("pattern")); + Indication indication = Indication.class.cast(obj); + indications.add(indication); + } catch (Exception e) { + e.printStackTrace(); + } + } + return indications; + } + } } From bbd12d5ce93422c2ac129ac9914b33dbd31d855c Mon Sep 17 00:00:00 2001 From: "Petti, Ken" Date: Mon, 16 Apr 2018 12:58:13 -0400 Subject: [PATCH 04/16] adding more functions for DynamoDB --- .../plugins/bfa/db/DynamoDBKnowledgeBase.java | 226 +++++++++++++++--- .../plugins/bfa/model/FailureCause.java | 37 +++ .../bfa/model/FailureCauseModification.java | 18 ++ .../jenkins/plugins/bfa/Messages.properties | 3 + .../bfa/db/DynamoDBKnowledgeBase/config.jelly | 38 +++ .../bfa/db/DynamoDBKnowledgeBaseTest.java | 40 +++- 6 files changed, 323 insertions(+), 39 deletions(-) create mode 100644 src/main/resources/com/sonyericsson/jenkins/plugins/bfa/db/DynamoDBKnowledgeBase/config.jelly diff --git a/src/main/java/com/sonyericsson/jenkins/plugins/bfa/db/DynamoDBKnowledgeBase.java b/src/main/java/com/sonyericsson/jenkins/plugins/bfa/db/DynamoDBKnowledgeBase.java index 0930ce0c..5de1e994 100644 --- a/src/main/java/com/sonyericsson/jenkins/plugins/bfa/db/DynamoDBKnowledgeBase.java +++ b/src/main/java/com/sonyericsson/jenkins/plugins/bfa/db/DynamoDBKnowledgeBase.java @@ -1,33 +1,76 @@ package com.sonyericsson.jenkins.plugins.bfa.db; +import com.amazonaws.regions.Region; +import com.amazonaws.regions.RegionUtils; +import com.amazonaws.regions.Regions; import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapper; +import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBScanExpression; import com.amazonaws.services.dynamodbv2.model.*; import com.amazonaws.services.dynamodbv2.util.TableUtils; import com.sonyericsson.jenkins.plugins.bfa.model.FailureCause; +import com.sonyericsson.jenkins.plugins.bfa.Messages; import com.sonyericsson.jenkins.plugins.bfa.statistics.Statistics; +import hudson.Extension; import hudson.model.Descriptor; import com.amazonaws.AmazonClientException; import com.amazonaws.auth.profile.ProfileCredentialsProvider; import com.amazonaws.services.dynamodbv2.AmazonDynamoDB; import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClientBuilder; +import hudson.util.FormValidation; +import hudson.util.ListBoxModel; +import jenkins.model.Jenkins; import org.kohsuke.stapler.DataBoundConstructor; +import org.kohsuke.stapler.QueryParameter; +import javax.xml.stream.events.Attribute; +import java.io.File; +import java.text.DateFormat; +import java.text.SimpleDateFormat; import java.util.*; public class DynamoDBKnowledgeBase extends KnowledgeBase { + private static final String DYNAMODB_DEFAULT_REGION = Regions.DEFAULT_REGION.getName(); + public static final String DYNAMODB_DEFAULT_CREDENTIALS_PATH = System.getProperty("user.home") + "/.aws/credentials"; + private static final String DYNAMODB_DEFAULT_CREDENTIAL_PROFILE = "default"; + static final Map NOT_REMOVED_FILTER_EXPRESSION = new HashMap(){{ + put("_removed", new Condition().withComparisonOperator("NULL")); + }}; + private static AmazonDynamoDB dynamoDB; private static DynamoDBMapper dbMapper; - private String host; - private int port; - private String tableName; + private String region; + private String credentialsPath; + private String credentialsProfile; @DataBoundConstructor - public DynamoDBKnowledgeBase(String host, int port, String tableName) { - this.host = host; - this.port = port; - this.tableName = tableName; + public DynamoDBKnowledgeBase(String region, String credentialsPath, String credentialsProfile) { + if (region == null || region.isEmpty()) { + region = DYNAMODB_DEFAULT_REGION; + } + if (credentialsPath == null || credentialsPath.isEmpty()) { + credentialsPath = DYNAMODB_DEFAULT_CREDENTIALS_PATH; + } + if (credentialsProfile == null || credentialsProfile.isEmpty()) { + credentialsProfile = DYNAMODB_DEFAULT_CREDENTIAL_PROFILE; + } + + this.region = region; + this.credentialsPath = credentialsPath; + this.credentialsProfile = credentialsProfile; + } + + public String getRegion() { + return region; + } + + public String getCredentialsPath() { + return credentialsPath; + } + + public String getCredentialsProfile() { + return credentialsProfile; } /** @@ -39,7 +82,13 @@ public DynamoDBKnowledgeBase(String host, int port, String tableName) { */ @Override public Collection getCauses() throws Exception { - return null; + try { + DynamoDBScanExpression scan = new DynamoDBScanExpression(); + scan.setScanFilter(NOT_REMOVED_FILTER_EXPRESSION); + return getDbMapper().scan(FailureCause.class, scan); + } catch (Exception e) { + throw e; + } } /** @@ -51,7 +100,15 @@ public Collection getCauses() throws Exception { */ @Override public Collection getCauseNames() throws Exception { - return null; + try { + DynamoDBScanExpression scan = new DynamoDBScanExpression(); + scan.addExpressionAttributeNamesEntry("#n", "name"); + scan.setProjectionExpression("id,#n"); + scan.setScanFilter(NOT_REMOVED_FILTER_EXPRESSION); + return getDbMapper().scan(FailureCause.class, scan); + } catch (Exception e) { + throw e; + } } /** @@ -66,7 +123,17 @@ public Collection getCauseNames() throws Exception { */ @Override public Collection getShallowCauses() throws Exception { - return null; + try { + DynamoDBScanExpression scan = new DynamoDBScanExpression(); + scan.addExpressionAttributeNamesEntry("#n", "name"); + scan.addExpressionAttributeNamesEntry("#c", "comment"); + scan.addExpressionAttributeNamesEntry("#r", "_removed"); + scan.setProjectionExpression("id,#n,description,categories,#c,modifications,lastOccurred"); + scan.setFilterExpression(" attribute_not_exists(#r) "); + return getDbMapper().scan(FailureCause.class, scan); + } catch (Exception e) { + throw e; + } } /** @@ -99,7 +166,7 @@ public FailureCause addCause(FailureCause cause) throws Exception { } /** - * Removes the cause from the knowledge base. + * Marks the cause as removed in the knowledge base. * * @param id the id of the cause to remove. * @return the removed FailureCause. @@ -107,7 +174,14 @@ public FailureCause addCause(FailureCause cause) throws Exception { */ @Override public FailureCause removeCause(String id) throws Exception { - return null; + try { + FailureCause cause = getDbMapper().load(FailureCause.class, id); + cause.setRemoved(); + getDbMapper().save(cause); + return cause; + } catch (Exception e) { + throw e; + } } /** @@ -122,11 +196,6 @@ public FailureCause removeCause(String id) throws Exception { @Override public FailureCause saveCause(FailureCause cause) throws Exception { try { - ListTablesResult tables = getDynamoDb().listTables(); - if (!tables.getTableNames().contains(tableName)) { - createTables(); - } - getDbMapper().save(cause); } catch (Exception e) { throw e; @@ -229,7 +298,7 @@ public AmazonDynamoDB getDynamoDb() { return dynamoDB; } - ProfileCredentialsProvider credentialsProvider = new ProfileCredentialsProvider(); + ProfileCredentialsProvider credentialsProvider = new ProfileCredentialsProvider(credentialsPath, credentialsProfile); try { credentialsProvider.getCredentials(); } catch (Exception e) { @@ -242,43 +311,134 @@ public AmazonDynamoDB getDynamoDb() { dynamoDB = AmazonDynamoDBClientBuilder.standard() .withCredentials(credentialsProvider) - .withRegion("us-west-2") -// .withEndpointConfiguration( -// new AwsClientBuilder.EndpointConfiguration("http://localhost:8000", "us-west-2")) + .withRegion(region) .build(); return dynamoDB; } - public void createTables() { + private void createTable(CreateTableRequest request) { try { + String tableName = request.getTableName(); AmazonDynamoDB db = getDynamoDb(); - CreateTableRequest causeTableRequest = new CreateTableRequest() - .withTableName(tableName) - .withKeySchema(new KeySchemaElement().withAttributeName("id").withKeyType(KeyType.HASH)) - .withAttributeDefinitions(new AttributeDefinition().withAttributeName("id").withAttributeType(ScalarAttributeType.S)) - .withProvisionedThroughput(new ProvisionedThroughput().withReadCapacityUnits(1L).withWriteCapacityUnits(1L)); - - TableUtils.createTableIfNotExists(db, causeTableRequest); + request.setProvisionedThroughput(new ProvisionedThroughput().withReadCapacityUnits(1L).withWriteCapacityUnits(1L)); + TableUtils.createTableIfNotExists(db, request); TableUtils.waitUntilActive(db, tableName); - dynamoDB.listTables(); - } catch (Exception e) { throw new AmazonClientException(e); } } - public DynamoDBMapper getDbMapper() { + private DynamoDBMapper getDbMapper() { if (dbMapper != null) { return dbMapper; } dbMapper = new DynamoDBMapper(getDynamoDb()); + createTable(dbMapper.generateCreateTableRequest(FailureCause.class)); + return dbMapper; } @Override public Descriptor getDescriptor() { - return null; + return Jenkins.getInstance().getDescriptorByType(DynamoDBKnowledgeBaseDescriptor.class); + } + + /** + * Descriptor for {@link DynamoDBKnowledgeBase}. + */ + @Extension + public static class DynamoDBKnowledgeBaseDescriptor extends KnowledgeBaseDescriptor { + + @Override + public String getDisplayName() { + return Messages.DynamoDBKnowledgeBase_DisplayName(); + } + + /** + * Convenience method for jelly. + * @return the default region. + */ + public String getDefaultRegion() { + return DYNAMODB_DEFAULT_REGION; + } + + /** + * Convenience method for jelly. + * @return the default region. + */ + public String getDefaultCredentialsPath() { + return DYNAMODB_DEFAULT_CREDENTIALS_PATH; + } + + /** + * Convenience method for jelly. + * @return the default region. + */ + public String getDefaultCredentialProfile() { + return DYNAMODB_DEFAULT_CREDENTIAL_PROFILE; + } + + public ListBoxModel doFillRegionItems() { + ListBoxModel items = new ListBoxModel(); + for (Region r:RegionUtils.getRegions()) { + String regionName = r.getName(); + items.add(regionName, regionName); + } + return items; + } + + /** + * Checks that the credential file exists. + * + * @param value the pattern to check. + * @return {@link hudson.util.FormValidation#ok()} if everything is well. + */ + public FormValidation doCheckCredentialsPath(@QueryParameter("value") final String value) { + File f = new File(value); + if(!f.exists()) { + return FormValidation.error("Credential file does not exist!"); + } + + if (f.isDirectory()) { + return FormValidation.error("Credential file can not be a directory!"); + } + return FormValidation.ok(); + } + + /** + * Checks that the credential profile is set. + * + * @param value the pattern to check. + * @return {@link hudson.util.FormValidation#ok()} if everything is well. + */ + public FormValidation doCheckCredentialsProfile(@QueryParameter("value") final String value) { + if (value == null || value.isEmpty()) { + return FormValidation.warning("No credential profile entered, using \"default\" profile"); + } + + return FormValidation.ok(); + } + + /** + * Tests if the provided parameters can connect to the DynamoDB service. + * @param region the region name. + * @return {@link FormValidation#ok() } if can be done, + * {@link FormValidation#error(java.lang.String) } otherwise. + */ + public FormValidation doTestConnection( + @QueryParameter("region") final String region, + @QueryParameter("credentialsPath") final String credentialsPath, + @QueryParameter("credentialProfile") final String credentialProfile + ) { + DynamoDBKnowledgeBase base = new DynamoDBKnowledgeBase(region, credentialsPath, credentialProfile); + try { + base.getDynamoDb(); + } catch (Exception e) { + return FormValidation.error(e, Messages.DynamoDBKnowledgeBase_ConnectionError()); + } + return FormValidation.ok(Messages.DynamoDBKnowledgeBase_ConnectionOK()); + } } } diff --git a/src/main/java/com/sonyericsson/jenkins/plugins/bfa/model/FailureCause.java b/src/main/java/com/sonyericsson/jenkins/plugins/bfa/model/FailureCause.java index ab7f351d..764475ae 100644 --- a/src/main/java/com/sonyericsson/jenkins/plugins/bfa/model/FailureCause.java +++ b/src/main/java/com/sonyericsson/jenkins/plugins/bfa/model/FailureCause.java @@ -58,6 +58,8 @@ import org.kohsuke.stapler.StaplerResponse; import java.io.Serializable; +import java.text.DateFormat; +import java.text.SimpleDateFormat; import java.util.*; import java.util.logging.Level; import java.util.logging.Logger; @@ -90,6 +92,8 @@ public class FailureCause implements Serializable, Action, Describable indications; @DynamoDBAttribute(attributeName = "modifications") private List modifications; + @DynamoDBAttribute(attributeName = "_removed") + private Map removed; /** * Standard data bound constructor. @@ -405,6 +409,39 @@ public String getComment() { */ public void setComment(String comment) { this.comment = comment; } + /** + * Getter for removed info. + * + * @return the removed info. + */ + public Map getRemoved() { + return removed; + } + + /** + * Setter for the comment. + * + */ + public void setRemoved() { + TimeZone tz = TimeZone.getTimeZone("UTC"); + DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm'Z'"); // Quoted "Z" to indicate UTC, no timezone offset + df.setTimeZone(tz); + String nowAsISO = df.format(new Date()); + + Map removedInfo = new HashMap<>(); + removedInfo.put("by", Jenkins.getAuthentication().getName()); + removedInfo.put("timestamp", nowAsISO); + this.removed = removedInfo; + } + + /** + * Setter for the comment. + * + */ + public void setRemoved(Map removedInfo) { + this.removed = removedInfo; + } + /** * Getter for the last occurrence. * diff --git a/src/main/java/com/sonyericsson/jenkins/plugins/bfa/model/FailureCauseModification.java b/src/main/java/com/sonyericsson/jenkins/plugins/bfa/model/FailureCauseModification.java index 34d9843f..d054e79e 100644 --- a/src/main/java/com/sonyericsson/jenkins/plugins/bfa/model/FailureCauseModification.java +++ b/src/main/java/com/sonyericsson/jenkins/plugins/bfa/model/FailureCauseModification.java @@ -23,8 +23,10 @@ */ package com.sonyericsson.jenkins.plugins.bfa.model; +import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBDocument; import org.codehaus.jackson.annotate.JsonCreator; import org.codehaus.jackson.annotate.JsonProperty; +import org.kohsuke.stapler.DataBoundConstructor; import java.io.Serializable; import java.util.Date; @@ -34,6 +36,7 @@ * * @author Felix Hall <felix.hall@sonymobile.com> */ +@DynamoDBDocument public class FailureCauseModification implements Serializable { private String user; private Date time; @@ -54,6 +57,9 @@ public FailureCauseModification(@JsonProperty("user") String user, @JsonProperty } } + @DataBoundConstructor + public FailureCauseModification() {}; + /** * Getter for the time. * @@ -67,6 +73,12 @@ public Date getTime() { } } + /** + * Setter for the time. + * + */ + public void setTime(Date time) { this.time = time; } + /** * Getter for the user. * @@ -76,4 +88,10 @@ public String getUser() { return user; } + /** + * Setter for the user. + * + */ + public void setUser(String user) { this.user = user; } + } diff --git a/src/main/resources/com/sonyericsson/jenkins/plugins/bfa/Messages.properties b/src/main/resources/com/sonyericsson/jenkins/plugins/bfa/Messages.properties index 7162e396..6b39d9e2 100644 --- a/src/main/resources/com/sonyericsson/jenkins/plugins/bfa/Messages.properties +++ b/src/main/resources/com/sonyericsson/jenkins/plugins/bfa/Messages.properties @@ -12,6 +12,9 @@ LocalFileKnowledgeBase_DisplayName=Jenkins Local MongoDBKnowledgeBase_DisplayName=Mongo DB MongoDBKnowledgeBase_ConnectionError=Could not connect MongoDBKnowledgeBase_ConnectionOK=Connection OK! +DynamoDBKnowledgeBase_DisplayName=DynamoDB +DynamoDBKnowledgeBase_ConnectionError=Could not connect +DynamoDBKnowledgeBase_ConnectionOK=Connection OK! StringMatchesPattern=String matches pattern StringDoesNotMatchPattern=String does not match pattern InvalidPattern_Error=Invalid pattern diff --git a/src/main/resources/com/sonyericsson/jenkins/plugins/bfa/db/DynamoDBKnowledgeBase/config.jelly b/src/main/resources/com/sonyericsson/jenkins/plugins/bfa/db/DynamoDBKnowledgeBase/config.jelly new file mode 100644 index 00000000..73829ef3 --- /dev/null +++ b/src/main/resources/com/sonyericsson/jenkins/plugins/bfa/db/DynamoDBKnowledgeBase/config.jelly @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + diff --git a/src/test/java/com/sonyericsson/jenkins/plugins/bfa/db/DynamoDBKnowledgeBaseTest.java b/src/test/java/com/sonyericsson/jenkins/plugins/bfa/db/DynamoDBKnowledgeBaseTest.java index 4f0747a6..0e3a883e 100644 --- a/src/test/java/com/sonyericsson/jenkins/plugins/bfa/db/DynamoDBKnowledgeBaseTest.java +++ b/src/test/java/com/sonyericsson/jenkins/plugins/bfa/db/DynamoDBKnowledgeBaseTest.java @@ -28,6 +28,7 @@ import com.mongodb.MongoException; import com.amazonaws.services.dynamodbv2.AmazonDynamoDB; import com.sonyericsson.jenkins.plugins.bfa.model.FailureCause; +import com.sonyericsson.jenkins.plugins.bfa.model.FailureCauseModification; import com.sonyericsson.jenkins.plugins.bfa.model.indication.BuildLogIndication; import com.sonyericsson.jenkins.plugins.bfa.model.indication.Indication; import com.sonyericsson.jenkins.plugins.bfa.statistics.Statistics; @@ -35,6 +36,7 @@ import net.vz.mongodb.jackson.DBCursor; import net.vz.mongodb.jackson.JacksonDBCollection; import net.vz.mongodb.jackson.WriteResult; +import org.acegisecurity.Authentication; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -79,19 +81,23 @@ public class DynamoDBKnowledgeBaseTest { private JacksonDBCollection statisticsCollection; private DynamoDBKnowledgeBase kb; private List indications; + private List modifications; private Indication indication; private FailureCause mockedCause; private Statistics mockedStatistics; - private static final int PORT = 27017; /** * Common stuff to set up for the tests. */ @Before public void setUp() { + Authentication mockAuth = mock(Authentication.class); PowerMockito.mockStatic(Jenkins.class); PowerMockito.when(Jenkins.getInstance()).thenReturn(jenkins); - kb = new DynamoDBKnowledgeBase("localhost", 8000, "failureCauses"); + PowerMockito.when(Jenkins.getAuthentication()).thenReturn(mockAuth); + PowerMockito.when(mockAuth.getName()).thenReturn("tester"); + DynamoDBKnowledgeBase.DynamoDBKnowledgeBaseDescriptor foo = new DynamoDBKnowledgeBase.DynamoDBKnowledgeBaseDescriptor(); + kb = new DynamoDBKnowledgeBase("", "", "default"); // collection = mock(JacksonDBCollection.class); // statisticsCollection = mock(JacksonDBCollection.class); // Whitebox.setInternalState(kb, "jacksonCollection", collection); @@ -114,7 +120,7 @@ public void setUp() { public void testFindOneCause() throws Exception { // when(collection.findOneById(anyString())).thenReturn(mockedCause); FailureCause fetchedCause = kb.getCause("2ce2ae7b-7f66-4a8c-984a-802a43d3a9a4"); -// assertNotNull("The fetched cause should not be null", fetchedCause); + assertNotNull("The fetched cause should not be null", fetchedCause); // assertSame(mockedCause, fetchedCause); } @@ -165,8 +171,12 @@ public void testSaveCause() throws Exception { indications = new LinkedList(); indication = new BuildLogIndication("something"); indications.add(indication); + modifications = new LinkedList(); + FailureCauseModification modification = new FailureCauseModification("ken", new Date()); + modifications.add(modification); + mockedCause = new FailureCause(null, "myFailureCause", "description", "comment", new Date(), - "category", indications, null); + "category", indications, modifications); kb.saveCause(mockedCause); @@ -228,8 +238,26 @@ public void testGetDynamoDB() throws Exception { * * @throws Exception if so. */ +// @Test +// public void testCreateTables() throws Exception { +// kb.createTable(); +// } + + @Test + public void getCauses() throws Exception { + Collection causes = kb.getCauses(); + System.out.println("foo"); + } + + @Test + public void getShallowCauses() throws Exception { + Collection causes = kb.getShallowCauses(); + System.out.println("foo"); + } + @Test - public void testCreateTables() throws Exception { - kb.createTables(); + public void removeCause() throws Exception { + FailureCause cause = kb.removeCause("bc3e1c3d-222e-43dd-8efc-2ddec79485b0"); + System.out.println("foo"); } } From 0b87b7eed0fe17fa59adfddd18e461d8e0601799 Mon Sep 17 00:00:00 2001 From: "Petti, Ken" Date: Thu, 19 Apr 2018 08:12:02 -0400 Subject: [PATCH 05/16] adding get categories, refactoring dbmapper --- .../plugins/bfa/db/DynamoDBKnowledgeBase.java | 45 +++++++++++-------- 1 file changed, 27 insertions(+), 18 deletions(-) diff --git a/src/main/java/com/sonyericsson/jenkins/plugins/bfa/db/DynamoDBKnowledgeBase.java b/src/main/java/com/sonyericsson/jenkins/plugins/bfa/db/DynamoDBKnowledgeBase.java index 5de1e994..d4e25a97 100644 --- a/src/main/java/com/sonyericsson/jenkins/plugins/bfa/db/DynamoDBKnowledgeBase.java +++ b/src/main/java/com/sonyericsson/jenkins/plugins/bfa/db/DynamoDBKnowledgeBase.java @@ -5,6 +5,7 @@ import com.amazonaws.regions.Regions; import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapper; import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBScanExpression; +import com.amazonaws.services.dynamodbv2.datamodeling.PaginatedScanList; import com.amazonaws.services.dynamodbv2.model.*; import com.amazonaws.services.dynamodbv2.util.TableUtils; import com.sonyericsson.jenkins.plugins.bfa.model.FailureCause; @@ -28,6 +29,7 @@ import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.*; +import java.util.stream.Collectors; public class DynamoDBKnowledgeBase extends KnowledgeBase { @@ -39,7 +41,8 @@ public class DynamoDBKnowledgeBase extends KnowledgeBase { }}; private static AmazonDynamoDB dynamoDB; - private static DynamoDBMapper dbMapper; + private DynamoDBMapper dbMapper; + private String region; private String credentialsPath; private String credentialsProfile; @@ -223,7 +226,19 @@ public void convertFrom(KnowledgeBase oldKnowledgeBase) throws Exception { */ @Override public List getCategories() throws Exception { - return null; + try { + DynamoDBScanExpression scan = new DynamoDBScanExpression(); + scan.setProjectionExpression("categories"); + scan.setFilterExpression(" attribute_exists(categories) "); + List causes = getDbMapper().scan(FailureCause.class, scan); + Set categories = new HashSet<>(); + for (FailureCause c:causes) { + categories.addAll(c.getCategories()); + } + return new ArrayList<>(categories); + } catch (Exception e) { + throw e; + } } /** @@ -288,12 +303,6 @@ public void saveStatistics(Statistics stat) throws Exception { } public AmazonDynamoDB getDynamoDb() { - /* - * The ProfileCredentialsProvider will return your [default] - * credential profile by reading from the credentials file located at - * (~/.aws/credentials). - */ - if (dynamoDB != null) { return dynamoDB; } @@ -317,6 +326,16 @@ public AmazonDynamoDB getDynamoDb() { return dynamoDB; } + private DynamoDBMapper getDbMapper() { + if (dbMapper != null) { + return dbMapper; + } + dbMapper = new DynamoDBMapper(getDynamoDb()); + createTable(dbMapper.generateCreateTableRequest(FailureCause.class)); + + return dbMapper; + } + private void createTable(CreateTableRequest request) { try { String tableName = request.getTableName(); @@ -330,16 +349,6 @@ private void createTable(CreateTableRequest request) { } } - private DynamoDBMapper getDbMapper() { - if (dbMapper != null) { - return dbMapper; - } - dbMapper = new DynamoDBMapper(getDynamoDb()); - createTable(dbMapper.generateCreateTableRequest(FailureCause.class)); - - return dbMapper; - } - @Override public Descriptor getDescriptor() { return Jenkins.getInstance().getDescriptorByType(DynamoDBKnowledgeBaseDescriptor.class); From 227a559aa0e71cde966b41ecb4819eefb3783ee6 Mon Sep 17 00:00:00 2001 From: "Petti, Ken" Date: Thu, 19 Apr 2018 08:13:49 -0400 Subject: [PATCH 06/16] adding equals method --- .../plugins/bfa/model/FailureCause.java | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/main/java/com/sonyericsson/jenkins/plugins/bfa/model/FailureCause.java b/src/main/java/com/sonyericsson/jenkins/plugins/bfa/model/FailureCause.java index 764475ae..699ad493 100644 --- a/src/main/java/com/sonyericsson/jenkins/plugins/bfa/model/FailureCause.java +++ b/src/main/java/com/sonyericsson/jenkins/plugins/bfa/model/FailureCause.java @@ -58,6 +58,8 @@ import org.kohsuke.stapler.StaplerResponse; import java.io.Serializable; +import java.lang.reflect.Field; +import java.lang.reflect.Method; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.*; @@ -702,6 +704,28 @@ public FailureCauseDescriptor getDescriptor() { return Jenkins.getInstance().getDescriptorByType(FailureCauseDescriptor.class); } + @Override + public boolean equals(Object o) { + if (!(o instanceof FailureCause)) { + return false; + } + + Field[] fields = this.getClass().getDeclaredFields(); + for (Field f:fields) { + try { + if (f.get(this) == null && f.get(o) == null) { + continue; + } + if (!f.get(this).equals(f.get(o))) { + return false; + } + } catch (IllegalArgumentException | IllegalAccessException e) { + return false; + } + } + return true; + } + /** * Descriptor is only used for auto completion of categories. */ From 1820161dfdb6daa73cb7737a318a8a565f1f32c3 Mon Sep 17 00:00:00 2001 From: "Petti, Ken" Date: Thu, 19 Apr 2018 08:14:10 -0400 Subject: [PATCH 07/16] adding equals method --- .../jenkins/plugins/bfa/model/indication/Indication.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/main/java/com/sonyericsson/jenkins/plugins/bfa/model/indication/Indication.java b/src/main/java/com/sonyericsson/jenkins/plugins/bfa/model/indication/Indication.java index 6cd6e0ef..3b9d579b 100644 --- a/src/main/java/com/sonyericsson/jenkins/plugins/bfa/model/indication/Indication.java +++ b/src/main/java/com/sonyericsson/jenkins/plugins/bfa/model/indication/Indication.java @@ -106,6 +106,15 @@ public String toString() { return getUserProvidedExpression(); } + @Override + public boolean equals(Object o) { + if (!(o instanceof Indication)) { + return false; + } + + return this.pattern.equals(((Indication) o).pattern); + } + /** * The descriptor for this indicator. */ From b46b97c31704a0987b70966ceae2a84b5e4abf47 Mon Sep 17 00:00:00 2001 From: "Petti, Ken" Date: Thu, 19 Apr 2018 08:14:19 -0400 Subject: [PATCH 08/16] adding tests --- .../bfa/db/DynamoDBKnowledgeBaseTest.java | 216 +++++++++++------- 1 file changed, 136 insertions(+), 80 deletions(-) diff --git a/src/test/java/com/sonyericsson/jenkins/plugins/bfa/db/DynamoDBKnowledgeBaseTest.java b/src/test/java/com/sonyericsson/jenkins/plugins/bfa/db/DynamoDBKnowledgeBaseTest.java index 0e3a883e..8a3797a9 100644 --- a/src/test/java/com/sonyericsson/jenkins/plugins/bfa/db/DynamoDBKnowledgeBaseTest.java +++ b/src/test/java/com/sonyericsson/jenkins/plugins/bfa/db/DynamoDBKnowledgeBaseTest.java @@ -24,6 +24,18 @@ package com.sonyericsson.jenkins.plugins.bfa.db; +import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapper; +import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapperTableModel; +import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBScanExpression; +import com.amazonaws.services.dynamodbv2.datamodeling.PaginatedScanList; +import com.amazonaws.services.dynamodbv2.datamodeling.marshallers.ObjectToMapMarshaller; +import com.amazonaws.services.dynamodbv2.datamodeling.marshallers.ObjectToStringMarshaller; +import com.amazonaws.services.dynamodbv2.document.DynamoDB; +import com.amazonaws.services.dynamodbv2.document.Item; +import com.amazonaws.services.dynamodbv2.document.internal.InternalUtils; +import com.amazonaws.services.dynamodbv2.model.AttributeValue; +import com.amazonaws.services.dynamodbv2.model.ScanRequest; +import com.amazonaws.services.dynamodbv2.model.ScanResult; import com.mongodb.DBObject; import com.mongodb.MongoException; import com.amazonaws.services.dynamodbv2.AmazonDynamoDB; @@ -32,6 +44,7 @@ import com.sonyericsson.jenkins.plugins.bfa.model.indication.BuildLogIndication; import com.sonyericsson.jenkins.plugins.bfa.model.indication.Indication; import com.sonyericsson.jenkins.plugins.bfa.statistics.Statistics; +import hudson.model.Descriptor; import jenkins.model.Jenkins; import net.vz.mongodb.jackson.DBCursor; import net.vz.mongodb.jackson.JacksonDBCollection; @@ -42,6 +55,7 @@ import org.junit.runner.RunWith; import org.mockito.Matchers; import org.mockito.Mock; +import org.mockito.Mockito; import org.powermock.api.mockito.PowerMockito; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; @@ -51,16 +65,11 @@ import java.util.*; -import static junit.framework.Assert.assertFalse; -import static junit.framework.Assert.assertNotNull; -import static junit.framework.Assert.assertSame; -import static junit.framework.Assert.assertTrue; +import static junit.framework.Assert.*; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyString; -import static org.powermock.api.mockito.PowerMockito.doReturn; -import static org.powermock.api.mockito.PowerMockito.mock; -import static org.powermock.api.mockito.PowerMockito.when; +import static org.powermock.api.mockito.PowerMockito.*; /** @@ -69,13 +78,17 @@ * @author Tomas Westling <tomas.westling@sonyericsson.com> */ @RunWith(PowerMockRunner.class) -@PrepareForTest({Jenkins.class, Jenkins.DescriptorImpl.class}) @PowerMockIgnore( {"javax.*"}) +@PrepareForTest({DynamoDBKnowledgeBase.class, Jenkins.class, Jenkins.DescriptorImpl.class}) public class DynamoDBKnowledgeBaseTest { @Mock private Jenkins jenkins; + @Mock + private DynamoDBMapper dbMapper; + @Mock + private AmazonDynamoDB db; private JacksonDBCollection collection; private JacksonDBCollection statisticsCollection; @@ -90,27 +103,50 @@ public class DynamoDBKnowledgeBaseTest { * Common stuff to set up for the tests. */ @Before - public void setUp() { + public void setUp() throws Exception{ Authentication mockAuth = mock(Authentication.class); PowerMockito.mockStatic(Jenkins.class); PowerMockito.when(Jenkins.getInstance()).thenReturn(jenkins); PowerMockito.when(Jenkins.getAuthentication()).thenReturn(mockAuth); PowerMockito.when(mockAuth.getName()).thenReturn("tester"); - DynamoDBKnowledgeBase.DynamoDBKnowledgeBaseDescriptor foo = new DynamoDBKnowledgeBase.DynamoDBKnowledgeBaseDescriptor(); - kb = new DynamoDBKnowledgeBase("", "", "default"); -// collection = mock(JacksonDBCollection.class); + +// PowerMockito.doReturn(discriptor).when(jenkins).getDescriptorByType(FailureCause.FailureCauseDescriptor.class); + + + kb = PowerMockito.spy(new DynamoDBKnowledgeBase("", "", "default")); + db = mock(AmazonDynamoDB.class); + dbMapper = spy(new DynamoDBMapper(db)); + PowerMockito.doReturn(dbMapper).when(kb, "getDbMapper"); + + // collection = mock(JacksonDBCollection.class); // statisticsCollection = mock(JacksonDBCollection.class); // Whitebox.setInternalState(kb, "jacksonCollection", collection); // Whitebox.setInternalState(kb, "jacksonStatisticsCollection", statisticsCollection); -// indications = new LinkedList(); -// indication = new BuildLogIndication("something"); -// indications.add(indication); -// mockedCause = new FailureCause("id", "myFailureCause", "description", "comment", new Date(), -// "category", indications, null); + indications = new LinkedList(); + indication = new BuildLogIndication("something"); + indications.add(indication); +// mockedCause = createFailureCause(null); // mockedStatistics = new Statistics("projectName", 1, "", null, 1, null, "nodeName", "master", 0, "result", // null, null); } + public FailureCause createFailureCause(String id) throws Exception{ + return new FailureCause(id, "myFailureCause", "description", "comment", new Date(), + "category", indications, null); + } + + public void mockScanRequest(Collection causes) throws Exception { + DynamoDBMapperTableModel fcModel = dbMapper.getTableModel(FailureCause.class); + Collection> convertedFcs = new ArrayList<>(); + for (FailureCause fc:causes) { + Map convertedFc = fcModel.convert(fc); + convertedFcs.add(convertedFc); + } + + ScanResult mockedScanResult = spy(new ScanResult()).withItems(convertedFcs); + doReturn(mockedScanResult).when(db, "scan", Matchers.any()); + } + /** * Tests finding one cause by its id. * @@ -118,10 +154,12 @@ public void setUp() { */ @Test public void testFindOneCause() throws Exception { -// when(collection.findOneById(anyString())).thenReturn(mockedCause); - FailureCause fetchedCause = kb.getCause("2ce2ae7b-7f66-4a8c-984a-802a43d3a9a4"); + String id = "2ce2ae7b-7f66-4a8c-984a-802a43d3a9a4"; + FailureCause mockedCause = createFailureCause(id); + doReturn(mockedCause).when(dbMapper).load(FailureCause.class, id); + FailureCause fetchedCause = kb.getCause(id); assertNotNull("The fetched cause should not be null", fetchedCause); -// assertSame(mockedCause, fetchedCause); + assertSame(mockedCause, fetchedCause); } /** @@ -131,17 +169,43 @@ public void testFindOneCause() throws Exception { */ @Test public void testGetCauseNames() throws Exception { - DBCursor cursor = mock(DBCursor.class); - List list = new LinkedList(); - list.add(mockedCause); - when(cursor.next()).thenReturn(mockedCause); - when(cursor.hasNext()).thenReturn(true, false); - doReturn(cursor).when(collection).find(Matchers.any(), Matchers.any()); + Collection expectedCauses = new ArrayList<>(); + for (int i = 0; i < 5; i++) { + Integer id = i; + FailureCause cause = new FailureCause(id.toString(), "myFailureCause" + id.toString(), "description", "comment", new Date(), + "category", indications, null); + + // Null all fields except for Id and Name, as we are requesting only those two fields + cause.setDescription(null); + cause.setComment(null); + cause.setLastOccurred(null); + cause.setCategories(null); + cause.setIndications(null); + cause.setModifications(null); + + expectedCauses.add(cause); + } + + mockScanRequest(expectedCauses); + DynamoDBScanExpression scanExpression = PowerMockito.spy(new DynamoDBScanExpression()); + whenNew(DynamoDBScanExpression.class).withAnyArguments().thenReturn(scanExpression); + Collection fetchedCauses = kb.getCauseNames(); - assertNotNull("The fetched cause should not be null", fetchedCauses); - Iterator fetch = fetchedCauses.iterator(); - assertTrue(fetch.hasNext()); - assertSame(mockedCause, fetch.next()); + + Mockito.verify(scanExpression).addExpressionAttributeNamesEntry("#n", "name"); + Mockito.verify(scanExpression).setProjectionExpression("id,#n"); + Mockito.verify(scanExpression).setScanFilter(DynamoDBKnowledgeBase.NOT_REMOVED_FILTER_EXPRESSION); + + assertNotNull("The fetched cause should not be null", fetchedCauses);; + // Convert fetchedCauses to list, because PaginatedList does not allow iterators + List actualCauses = new ArrayList<>(fetchedCauses); + assertEquals(actualCauses, actualCauses); + + for (FailureCause ac:actualCauses) { + assertNotNull("Id should not be null", ac.getId()); + assertNotNull("Name should not be null", ac.getName()); + assertNull("Description should be null", ac.getDescription()); + } } /** @@ -151,14 +215,13 @@ public void testGetCauseNames() throws Exception { */ @Test public void testAddCause() throws Exception { - WriteResult result = mock(WriteResult.class); - when(result.getSavedObject()).thenReturn(mockedCause); - MongoDBKnowledgeBaseCache cache = mock(MongoDBKnowledgeBaseCache.class); - Whitebox.setInternalState(kb, cache); - doReturn(result).when(collection).insert(Matchers.any()); - FailureCause addedCause = kb.addCause(mockedCause); + // This is not a very effective test, since addCause is just a passthrough to saveCause + FailureCause noIdCause = createFailureCause(null); + FailureCause idCause = createFailureCause("foo"); + doReturn(idCause).when(kb).saveCause(noIdCause); + FailureCause addedCause = kb.addCause(noIdCause); assertNotNull(addedCause); - assertSame(mockedCause, addedCause); + assertNotSame(noIdCause, addedCause); } /** @@ -168,26 +231,11 @@ public void testAddCause() throws Exception { */ @Test public void testSaveCause() throws Exception { - indications = new LinkedList(); - indication = new BuildLogIndication("something"); - indications.add(indication); - modifications = new LinkedList(); - FailureCauseModification modification = new FailureCauseModification("ken", new Date()); - modifications.add(modification); - - mockedCause = new FailureCause(null, "myFailureCause", "description", "comment", new Date(), - "category", indications, modifications); - kb.saveCause(mockedCause); - - -// WriteResult result = mock(WriteResult.class); -// when(result.getSavedObject()).thenReturn(mockedCause); -// MongoDBKnowledgeBaseCache cache = mock(MongoDBKnowledgeBaseCache.class); -// Whitebox.setInternalState(kb, cache); -// doReturn(result).when(collection).save(Matchers.any()); -// FailureCause addedCause = kb.saveCause(mockedCause); -// assertNotNull(addedCause); -// assertSame(mockedCause, addedCause); + FailureCause cause = createFailureCause("foo"); + doNothing().when(dbMapper).save(cause); + FailureCause savedCause = kb.saveCause(cause); + assertNotNull(savedCause); + assertSame(cause, savedCause); } /** @@ -195,21 +243,21 @@ public void testSaveCause() throws Exception { * * @throws Exception if unable to fetch statistics. */ - @Test +// @Test public void testGetStatistics() throws Exception { - DBCursor cursor = mock(DBCursor.class); - List list = new LinkedList(); - list.add(mockedStatistics); - - doReturn(cursor).when(statisticsCollection).find(Matchers.any()); - when(cursor.limit(anyInt())).thenReturn(cursor); - when(cursor.sort(any(DBObject.class))).thenReturn(cursor); - when(cursor.toArray()).thenReturn(list); - - List fetchedStatistics = kb.getStatistics(null, 1); - assertNotNull("The fetched statistics should not be null", fetchedStatistics); - assertFalse("The fetched statistics list should not be empty", fetchedStatistics.isEmpty()); - assertSame(mockedStatistics, fetchedStatistics.get(0)); +// DBCursor cursor = mock(DBCursor.class); +// List list = new LinkedList(); +// list.add(mockedStatistics); +// +// doReturn(cursor).when(statisticsCollection).find(Matchers.any()); +// when(cursor.limit(anyInt())).thenReturn(cursor); +// when(cursor.sort(any(DBObject.class))).thenReturn(cursor); +// when(cursor.toArray()).thenReturn(list); +// +// List fetchedStatistics = kb.getStatistics(null, 1); +// assertNotNull("The fetched statistics should not be null", fetchedStatistics); +// assertFalse("The fetched statistics list should not be empty", fetchedStatistics.isEmpty()); +// assertSame(mockedStatistics, fetchedStatistics.get(0)); } /** @@ -223,15 +271,15 @@ public void testThrowMongo() throws Exception { kb.getCauseNames(); } - /** - * Tests that the mongo exception caused by the collection gets thrown from the knowledgebase. - * - * @throws Exception if so. - */ - @Test - public void testGetDynamoDB() throws Exception { - kb.getDynamoDb(); - } +// /** +// * Tests that the mongo exception caused by the collection gets thrown from the knowledgebase. +// * +// * @throws Exception if so. +// */ +// @Test +// public void testGetDynamoDB() throws Exception { +// kb.getDynamoDb(); +// } /** * Tests that the mongo exception caused by the collection gets thrown from the knowledgebase. @@ -245,6 +293,8 @@ public void testGetDynamoDB() throws Exception { @Test public void getCauses() throws Exception { + DynamoDBMapper mapper = mock(DynamoDBMapper.class); +// doReturn().when(mapper).save(); Collection causes = kb.getCauses(); System.out.println("foo"); } @@ -260,4 +310,10 @@ public void removeCause() throws Exception { FailureCause cause = kb.removeCause("bc3e1c3d-222e-43dd-8efc-2ddec79485b0"); System.out.println("foo"); } + + @Test + public void getCategories() throws Exception { + List foo = kb.getCategories(); + System.out.println("foo"); + } } From 3a9ed5ba875545e4e57bbc941a438309764603d7 Mon Sep 17 00:00:00 2001 From: "Petti, Ken" Date: Thu, 19 Apr 2018 09:27:10 -0400 Subject: [PATCH 09/16] adding converting functions --- .../plugins/bfa/db/DynamoDBKnowledgeBase.java | 40 ++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/sonyericsson/jenkins/plugins/bfa/db/DynamoDBKnowledgeBase.java b/src/main/java/com/sonyericsson/jenkins/plugins/bfa/db/DynamoDBKnowledgeBase.java index d4e25a97..26f3c3fc 100644 --- a/src/main/java/com/sonyericsson/jenkins/plugins/bfa/db/DynamoDBKnowledgeBase.java +++ b/src/main/java/com/sonyericsson/jenkins/plugins/bfa/db/DynamoDBKnowledgeBase.java @@ -128,6 +128,8 @@ public Collection getCauseNames() throws Exception { public Collection getShallowCauses() throws Exception { try { DynamoDBScanExpression scan = new DynamoDBScanExpression(); + // The following attributes are reserved words in Dynamo, so we need to substitute the actual name for + // something safe scan.addExpressionAttributeNamesEntry("#n", "name"); scan.addExpressionAttributeNamesEntry("#c", "comment"); scan.addExpressionAttributeNamesEntry("#r", "_removed"); @@ -215,7 +217,43 @@ public FailureCause saveCause(FailureCause cause) throws Exception { */ @Override public void convertFrom(KnowledgeBase oldKnowledgeBase) throws Exception { + if (oldKnowledgeBase instanceof DynamoDBKnowledgeBase) { + convertFromAbstract(oldKnowledgeBase); + convertRemoved((DynamoDBKnowledgeBase)oldKnowledgeBase); + } else { + for (FailureCause cause : oldKnowledgeBase.getCauseNames()) { + saveCause(cause); + } + } + } + + /** + * Copies all causes flagged as removed from the old database to this one. + * + * @param oldKnowledgeBase the old database. + * @throws Exception if something goes wrong. + */ + protected void convertRemoved(DynamoDBKnowledgeBase oldKnowledgeBase) throws Exception { + Collection removed = oldKnowledgeBase.getRemovedCauses(); + for (FailureCause obj : removed) { + saveCause(obj); + } + } + /** + * Gets all causes flagged as removed in a "raw" JSON format. + * + * @return the list of removed causes. + * @throws Exception if so. + */ + protected Collection getRemovedCauses() throws Exception { + try { + DynamoDBScanExpression scan = new DynamoDBScanExpression(); + scan.setFilterExpression(" attribute_exists(#r) "); + return getDbMapper().scan(FailureCause.class, scan); + } catch (Exception e) { + throw e; + } } /** @@ -302,7 +340,7 @@ public void saveStatistics(Statistics stat) throws Exception { } - public AmazonDynamoDB getDynamoDb() { + private AmazonDynamoDB getDynamoDb() { if (dynamoDB != null) { return dynamoDB; } From fcf8ff5e7ffeefeedfebbb04a6d15c006081b2fc Mon Sep 17 00:00:00 2001 From: "Petti, Ken" Date: Thu, 19 Apr 2018 09:27:18 -0400 Subject: [PATCH 10/16] more tests --- .../bfa/db/DynamoDBKnowledgeBaseTest.java | 204 ++++++++---------- 1 file changed, 92 insertions(+), 112 deletions(-) diff --git a/src/test/java/com/sonyericsson/jenkins/plugins/bfa/db/DynamoDBKnowledgeBaseTest.java b/src/test/java/com/sonyericsson/jenkins/plugins/bfa/db/DynamoDBKnowledgeBaseTest.java index 8a3797a9..766d9db5 100644 --- a/src/test/java/com/sonyericsson/jenkins/plugins/bfa/db/DynamoDBKnowledgeBaseTest.java +++ b/src/test/java/com/sonyericsson/jenkins/plugins/bfa/db/DynamoDBKnowledgeBaseTest.java @@ -27,29 +27,18 @@ import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapper; import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapperTableModel; import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBScanExpression; -import com.amazonaws.services.dynamodbv2.datamodeling.PaginatedScanList; -import com.amazonaws.services.dynamodbv2.datamodeling.marshallers.ObjectToMapMarshaller; -import com.amazonaws.services.dynamodbv2.datamodeling.marshallers.ObjectToStringMarshaller; -import com.amazonaws.services.dynamodbv2.document.DynamoDB; -import com.amazonaws.services.dynamodbv2.document.Item; -import com.amazonaws.services.dynamodbv2.document.internal.InternalUtils; import com.amazonaws.services.dynamodbv2.model.AttributeValue; -import com.amazonaws.services.dynamodbv2.model.ScanRequest; import com.amazonaws.services.dynamodbv2.model.ScanResult; import com.mongodb.DBObject; import com.mongodb.MongoException; import com.amazonaws.services.dynamodbv2.AmazonDynamoDB; import com.sonyericsson.jenkins.plugins.bfa.model.FailureCause; -import com.sonyericsson.jenkins.plugins.bfa.model.FailureCauseModification; import com.sonyericsson.jenkins.plugins.bfa.model.indication.BuildLogIndication; import com.sonyericsson.jenkins.plugins.bfa.model.indication.Indication; import com.sonyericsson.jenkins.plugins.bfa.statistics.Statistics; -import hudson.model.Descriptor; import jenkins.model.Jenkins; -import net.vz.mongodb.jackson.DBCursor; -import net.vz.mongodb.jackson.JacksonDBCollection; -import net.vz.mongodb.jackson.WriteResult; import org.acegisecurity.Authentication; +import org.apache.commons.collections.CollectionUtils; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -60,15 +49,12 @@ import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; import org.powermock.core.classloader.annotations.PowerMockIgnore; -import org.powermock.reflect.Whitebox; import java.util.*; import static junit.framework.Assert.*; import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyInt; -import static org.mockito.Matchers.anyString; import static org.powermock.api.mockito.PowerMockito.*; @@ -82,7 +68,6 @@ @PrepareForTest({DynamoDBKnowledgeBase.class, Jenkins.class, Jenkins.DescriptorImpl.class}) public class DynamoDBKnowledgeBaseTest { - @Mock private Jenkins jenkins; @Mock @@ -90,52 +75,52 @@ public class DynamoDBKnowledgeBaseTest { @Mock private AmazonDynamoDB db; - private JacksonDBCollection collection; - private JacksonDBCollection statisticsCollection; private DynamoDBKnowledgeBase kb; private List indications; - private List modifications; private Indication indication; - private FailureCause mockedCause; private Statistics mockedStatistics; + private String mockJenkinsUserName; /** * Common stuff to set up for the tests. */ @Before public void setUp() throws Exception{ + // Mock interactions with Jenkins Authentication mockAuth = mock(Authentication.class); PowerMockito.mockStatic(Jenkins.class); PowerMockito.when(Jenkins.getInstance()).thenReturn(jenkins); PowerMockito.when(Jenkins.getAuthentication()).thenReturn(mockAuth); - PowerMockito.when(mockAuth.getName()).thenReturn("tester"); - -// PowerMockito.doReturn(discriptor).when(jenkins).getDescriptorByType(FailureCause.FailureCauseDescriptor.class); - + PowerMockito.when(mockAuth.getName()).thenReturn(mockJenkinsUserName); + // Mock DynamoDB and DBMapper kb = PowerMockito.spy(new DynamoDBKnowledgeBase("", "", "default")); db = mock(AmazonDynamoDB.class); dbMapper = spy(new DynamoDBMapper(db)); PowerMockito.doReturn(dbMapper).when(kb, "getDbMapper"); - // collection = mock(JacksonDBCollection.class); -// statisticsCollection = mock(JacksonDBCollection.class); -// Whitebox.setInternalState(kb, "jacksonCollection", collection); -// Whitebox.setInternalState(kb, "jacksonStatisticsCollection", statisticsCollection); indications = new LinkedList(); indication = new BuildLogIndication("something"); indications.add(indication); -// mockedCause = createFailureCause(null); -// mockedStatistics = new Statistics("projectName", 1, "", null, 1, null, "nodeName", "master", 0, "result", -// null, null); } + /** + * Helper to create a FailureCause during testing + * @param id string id FailureCause to return + * @return FailureCause + * @throws Exception if so. + */ public FailureCause createFailureCause(String id) throws Exception{ return new FailureCause(id, "myFailureCause", "description", "comment", new Date(), "category", indications, null); } - public void mockScanRequest(Collection causes) throws Exception { + /** + * Sets up a mock scan result, which DynamoDB uses when creating a paginated list of results + * @param causes collection of FailureCauses + * @throws Exception if so. + */ + public void mockScanResult(Collection causes) throws Exception { DynamoDBMapperTableModel fcModel = dbMapper.getTableModel(FailureCause.class); Collection> convertedFcs = new ArrayList<>(); for (FailureCause fc:causes) { @@ -174,24 +159,16 @@ public void testGetCauseNames() throws Exception { Integer id = i; FailureCause cause = new FailureCause(id.toString(), "myFailureCause" + id.toString(), "description", "comment", new Date(), "category", indications, null); - - // Null all fields except for Id and Name, as we are requesting only those two fields - cause.setDescription(null); - cause.setComment(null); - cause.setLastOccurred(null); - cause.setCategories(null); - cause.setIndications(null); - cause.setModifications(null); - expectedCauses.add(cause); } - mockScanRequest(expectedCauses); + mockScanResult(expectedCauses); DynamoDBScanExpression scanExpression = PowerMockito.spy(new DynamoDBScanExpression()); whenNew(DynamoDBScanExpression.class).withAnyArguments().thenReturn(scanExpression); Collection fetchedCauses = kb.getCauseNames(); + // Ensure the scan filtering is actually called Mockito.verify(scanExpression).addExpressionAttributeNamesEntry("#n", "name"); Mockito.verify(scanExpression).setProjectionExpression("id,#n"); Mockito.verify(scanExpression).setScanFilter(DynamoDBKnowledgeBase.NOT_REMOVED_FILTER_EXPRESSION); @@ -199,13 +176,7 @@ public void testGetCauseNames() throws Exception { assertNotNull("The fetched cause should not be null", fetchedCauses);; // Convert fetchedCauses to list, because PaginatedList does not allow iterators List actualCauses = new ArrayList<>(fetchedCauses); - assertEquals(actualCauses, actualCauses); - - for (FailureCause ac:actualCauses) { - assertNotNull("Id should not be null", ac.getId()); - assertNotNull("Name should not be null", ac.getName()); - assertNull("Description should be null", ac.getDescription()); - } + assertEquals(expectedCauses, actualCauses); } /** @@ -238,82 +209,91 @@ public void testSaveCause() throws Exception { assertSame(cause, savedCause); } - /** - * Tests fetching statistics. - * - * @throws Exception if unable to fetch statistics. - */ -// @Test - public void testGetStatistics() throws Exception { -// DBCursor cursor = mock(DBCursor.class); -// List list = new LinkedList(); -// list.add(mockedStatistics); -// -// doReturn(cursor).when(statisticsCollection).find(Matchers.any()); -// when(cursor.limit(anyInt())).thenReturn(cursor); -// when(cursor.sort(any(DBObject.class))).thenReturn(cursor); -// when(cursor.toArray()).thenReturn(list); -// -// List fetchedStatistics = kb.getStatistics(null, 1); -// assertNotNull("The fetched statistics should not be null", fetchedStatistics); -// assertFalse("The fetched statistics list should not be empty", fetchedStatistics.isEmpty()); -// assertSame(mockedStatistics, fetchedStatistics.get(0)); - } + @Test + public void testGetShallowCauses() throws Exception { + Collection expectedCauses = new ArrayList<>(); + for (int i = 0; i < 5; i++) { + Integer id = i; + FailureCause cause = createFailureCause(id.toString()); + expectedCauses.add(cause); + } - /** - * Tests that the mongo exception caused by the collection gets thrown from the knowledgebase. - * - * @throws Exception if so. - */ - @Test(expected = MongoException.class) - public void testThrowMongo() throws Exception { - when(collection.find(Matchers.any(), Matchers.any())).thenThrow(MongoException.class); - kb.getCauseNames(); - } + mockScanResult(expectedCauses); + DynamoDBScanExpression scanExpression = PowerMockito.spy(new DynamoDBScanExpression()); + whenNew(DynamoDBScanExpression.class).withAnyArguments().thenReturn(scanExpression); -// /** -// * Tests that the mongo exception caused by the collection gets thrown from the knowledgebase. -// * -// * @throws Exception if so. -// */ -// @Test -// public void testGetDynamoDB() throws Exception { -// kb.getDynamoDb(); -// } + Collection fetchedCauses = kb.getShallowCauses(); + Mockito.verify(scanExpression).addExpressionAttributeNamesEntry("#n", "name"); + Mockito.verify(scanExpression).addExpressionAttributeNamesEntry("#c", "comment"); + Mockito.verify(scanExpression).addExpressionAttributeNamesEntry("#r", "_removed"); + Mockito.verify(scanExpression).setProjectionExpression("id,#n,description,categories,#c,modifications,lastOccurred"); + Mockito.verify(scanExpression).setFilterExpression(" attribute_not_exists(#r) "); + + assertNotNull("The fetched cause should not be null", fetchedCauses);; + // Convert fetchedCauses to list, because PaginatedList does not allow iterators + List actualCauses = new ArrayList<>(fetchedCauses); + assertEquals(expectedCauses, actualCauses); + } /** - * Tests that the mongo exception caused by the collection gets thrown from the knowledgebase. - * + * Test that the cause gets updated with removed info * @throws Exception if so. */ -// @Test -// public void testCreateTables() throws Exception { -// kb.createTable(); -// } - @Test - public void getCauses() throws Exception { - DynamoDBMapper mapper = mock(DynamoDBMapper.class); -// doReturn().when(mapper).save(); - Collection causes = kb.getCauses(); - System.out.println("foo"); - } + public void testRemoveCause() throws Exception { + String id = "test"; + FailureCause cause = createFailureCause(id); - @Test - public void getShallowCauses() throws Exception { - Collection causes = kb.getShallowCauses(); - System.out.println("foo"); + doReturn(cause).when(dbMapper).load(FailureCause.class, id); + doNothing().when(dbMapper).save(cause); + FailureCause actualCause = kb.removeCause(id); + assertNotNull(actualCause); + assertSame(cause, actualCause); + assertNotNull(actualCause.getRemoved()); + assertEquals(mockJenkinsUserName, actualCause.getRemoved().get("by")); } @Test - public void removeCause() throws Exception { - FailureCause cause = kb.removeCause("bc3e1c3d-222e-43dd-8efc-2ddec79485b0"); - System.out.println("foo"); + public void testGetCategories() throws Exception { + List expectedCategories = new ArrayList<>(); + expectedCategories.add("category0"); + expectedCategories.add("category1"); + expectedCategories.add("category2"); + + Collection causes = new ArrayList<>(); + for (int i = 0; i < 3; i++) { + Integer id = i; + FailureCause cause = new FailureCause(id.toString(), "myFailureCause" + id.toString(), "description", "comment", new Date(), + "category" + id.toString(), indications, null); + causes.add(cause); + } + + mockScanResult(causes); + DynamoDBScanExpression scanExpression = PowerMockito.spy(new DynamoDBScanExpression()); + whenNew(DynamoDBScanExpression.class).withAnyArguments().thenReturn(scanExpression); + + List actualCategories = kb.getCategories(); + Mockito.verify(scanExpression).setProjectionExpression("categories"); + Mockito.verify(scanExpression).setFilterExpression(" attribute_exists(categories) "); + assertNotNull(actualCategories); + + // Values should be the same, but might be in different order + assertTrue(CollectionUtils.isEqualCollection(expectedCategories, actualCategories)); } @Test - public void getCategories() throws Exception { - List foo = kb.getCategories(); - System.out.println("foo"); + public void testConvertFrom() throws Exception { + LocalFileKnowledgeBase localKb = spy(new LocalFileKnowledgeBase()); + Collection causes = new ArrayList<>(); + for (int i = 0; i < 3; i++) { + Integer id = i; + FailureCause cause = createFailureCause(id.toString()); + causes.add(cause); + } + + doReturn(causes).when(localKb).getCauseNames(); + doReturn(createFailureCause("foo")).when(kb).saveCause(Matchers.any(FailureCause.class)); + kb.convertFrom(localKb); + Mockito.verify(kb, Mockito.times(3)).saveCause(Matchers.any(FailureCause.class)); } } From 7fac5e94fa49263b5944fecd6be4ec19e912eee4 Mon Sep 17 00:00:00 2001 From: "Petti, Ken" Date: Thu, 19 Apr 2018 19:25:58 -0400 Subject: [PATCH 11/16] fixing style checks --- .../plugins/bfa/db/DynamoDBKnowledgeBase.java | 107 +++++++++++++----- .../plugins/bfa/model/FailureCause.java | 36 +++--- .../bfa/model/FailureCauseModification.java | 7 +- .../bfa/model/indication/Indication.java | 6 +- .../bfa/db/DynamoDBKnowledgeBaseTest.java | 7 +- .../plugins/bfa/model/FailureCauseTest.java | 28 ++++- 6 files changed, 128 insertions(+), 63 deletions(-) diff --git a/src/main/java/com/sonyericsson/jenkins/plugins/bfa/db/DynamoDBKnowledgeBase.java b/src/main/java/com/sonyericsson/jenkins/plugins/bfa/db/DynamoDBKnowledgeBase.java index 26f3c3fc..6c2d95d2 100644 --- a/src/main/java/com/sonyericsson/jenkins/plugins/bfa/db/DynamoDBKnowledgeBase.java +++ b/src/main/java/com/sonyericsson/jenkins/plugins/bfa/db/DynamoDBKnowledgeBase.java @@ -5,8 +5,9 @@ import com.amazonaws.regions.Regions; import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapper; import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBScanExpression; -import com.amazonaws.services.dynamodbv2.datamodeling.PaginatedScanList; -import com.amazonaws.services.dynamodbv2.model.*; +import com.amazonaws.services.dynamodbv2.model.Condition; +import com.amazonaws.services.dynamodbv2.model.CreateTableRequest; +import com.amazonaws.services.dynamodbv2.model.ProvisionedThroughput; import com.amazonaws.services.dynamodbv2.util.TableUtils; import com.sonyericsson.jenkins.plugins.bfa.model.FailureCause; import com.sonyericsson.jenkins.plugins.bfa.Messages; @@ -22,19 +23,25 @@ import jenkins.model.Jenkins; import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.QueryParameter; - - -import javax.xml.stream.events.Attribute; import java.io.File; -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.*; -import java.util.stream.Collectors; - +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * Handling of the Amazon Web Services DynamoDB way of saving the knowledge base. + * + * @author Ken Petti <kpetti@constantcontact.com> + */ public class DynamoDBKnowledgeBase extends KnowledgeBase { private static final String DYNAMODB_DEFAULT_REGION = Regions.DEFAULT_REGION.getName(); - public static final String DYNAMODB_DEFAULT_CREDENTIALS_PATH = System.getProperty("user.home") + "/.aws/credentials"; + private static final String DYNAMODB_DEFAULT_CREDENTIALS_PATH = + System.getProperty("user.home") + "/.aws/credentials"; private static final String DYNAMODB_DEFAULT_CREDENTIAL_PROFILE = "default"; static final Map NOT_REMOVED_FILTER_EXPRESSION = new HashMap(){{ put("_removed", new Condition().withComparisonOperator("NULL")); @@ -47,6 +54,36 @@ public class DynamoDBKnowledgeBase extends KnowledgeBase { private String credentialsPath; private String credentialsProfile; + /** + * Getter for the DynamoDB region. + * @return the region. + */ + public String getRegion() { + return region; + } + + /** + * Getter for the AWS credentials path. + * @return the credentialsPath. + */ + public String getCredentialsPath() { + return credentialsPath; + } + + /** + * Getter for the AWS credentials profile. + * @return the credentialsProfile string. + */ + public String getCredentialsProfile() { + return credentialsProfile; + } + + /** + * Standard constructor. + * @param region the AWS region to connect to DynamoDB with. + * @param credentialsPath the path to a local file containing AWS credentials. + * @param credentialsProfile the AWS credential profile to use for connecting to DynamoDB. + */ @DataBoundConstructor public DynamoDBKnowledgeBase(String region, String credentialsPath, String credentialsProfile) { if (region == null || region.isEmpty()) { @@ -64,18 +101,6 @@ public DynamoDBKnowledgeBase(String region, String credentialsPath, String crede this.credentialsProfile = credentialsProfile; } - public String getRegion() { - return region; - } - - public String getCredentialsPath() { - return credentialsPath; - } - - public String getCredentialsProfile() { - return credentialsProfile; - } - /** * Get the list of {@link FailureCause}s. It is intended to be used in the scanning phase hence it should be * returned as quickly as possible, so the list could be cached. @@ -287,7 +312,14 @@ public List getCategories() throws Exception { */ @Override public boolean equals(KnowledgeBase oldKnowledgeBase) { - return false; + if (getClass().isInstance(oldKnowledgeBase)) { + DynamoDBKnowledgeBase oldDynamoDBKnowledgeBase = (DynamoDBKnowledgeBase)oldKnowledgeBase; + return oldDynamoDBKnowledgeBase.getRegion().equals(region) + && oldDynamoDBKnowledgeBase.getCredentialsPath().equals(credentialsPath) + && oldDynamoDBKnowledgeBase.getCredentialsProfile().equals(credentialsProfile); + } else { + return false; + } } /** @@ -340,19 +372,25 @@ public void saveStatistics(Statistics stat) throws Exception { } + /** + * Get an instance of {@link AmazonDynamoDB}. Connects to the defined region with the defined AWS + * credentials file/profile. If this has been called before, it will return the cached version. + * @return instance of AmazonDynamoDB + */ private AmazonDynamoDB getDynamoDb() { if (dynamoDB != null) { return dynamoDB; } - ProfileCredentialsProvider credentialsProvider = new ProfileCredentialsProvider(credentialsPath, credentialsProfile); + ProfileCredentialsProvider credentialsProvider = + new ProfileCredentialsProvider(credentialsPath, credentialsProfile); try { credentialsProvider.getCredentials(); } catch (Exception e) { throw new AmazonClientException( - "Cannot load the credentials from the credential profiles file. " + - "Please make sure that your credentials file is at the correct " + - "location (~/.aws/credentials), and is in valid format.", + "Cannot load the credentials from the credential profiles file. " + + "Please make sure that your credentials file is at the correct " + + "location (~/.aws/credentials), and is in valid format.", e); } @@ -374,11 +412,16 @@ private DynamoDBMapper getDbMapper() { return dbMapper; } + /** + * Creates a DynamoDB table. + * @param request {@link CreateTableRequest} + */ private void createTable(CreateTableRequest request) { try { String tableName = request.getTableName(); AmazonDynamoDB db = getDynamoDb(); - request.setProvisionedThroughput(new ProvisionedThroughput().withReadCapacityUnits(1L).withWriteCapacityUnits(1L)); + request.setProvisionedThroughput(new ProvisionedThroughput() + .withReadCapacityUnits(1L).withWriteCapacityUnits(1L)); TableUtils.createTableIfNotExists(db, request); TableUtils.waitUntilActive(db, tableName); @@ -387,6 +430,10 @@ private void createTable(CreateTableRequest request) { } } + /** + * Use Jenkins to get and instance of {@link DynamoDBKnowledgeBaseDescriptor} + * @return Descriptor + */ @Override public Descriptor getDescriptor() { return Jenkins.getInstance().getDescriptorByType(DynamoDBKnowledgeBaseDescriptor.class); @@ -471,6 +518,8 @@ public FormValidation doCheckCredentialsProfile(@QueryParameter("value") final S /** * Tests if the provided parameters can connect to the DynamoDB service. * @param region the region name. + * @param credentialsPath the filepath to credentials. + * @param credentialProfile the credential profile to use. * @return {@link FormValidation#ok() } if can be done, * {@link FormValidation#error(java.lang.String) } otherwise. */ diff --git a/src/main/java/com/sonyericsson/jenkins/plugins/bfa/model/FailureCause.java b/src/main/java/com/sonyericsson/jenkins/plugins/bfa/model/FailureCause.java index 699ad493..abefabad 100644 --- a/src/main/java/com/sonyericsson/jenkins/plugins/bfa/model/FailureCause.java +++ b/src/main/java/com/sonyericsson/jenkins/plugins/bfa/model/FailureCause.java @@ -24,8 +24,8 @@ package com.sonyericsson.jenkins.plugins.bfa.model; import com.amazonaws.services.dynamodbv2.datamodeling.*; +import org.apache.commons.lang.builder.EqualsBuilder; import org.codehaus.jackson.type.TypeReference; -import com.fasterxml.jackson.databind.type.MapType; import com.sonyericsson.jenkins.plugins.bfa.CauseManagement; import com.sonyericsson.jenkins.plugins.bfa.PluginImpl; import com.sonyericsson.jenkins.plugins.bfa.db.KnowledgeBase; @@ -356,7 +356,7 @@ public String getId() { /** * The id. - * + * @param id String */ @Id @ObjectId @@ -375,7 +375,7 @@ public String getName() { /** * Setter for the name. - * + * @param name String */ public void setName(String name) { this.name = name; @@ -392,7 +392,7 @@ public String getDescription() { /** * Setter for the description. - * + * @param description String */ public void setDescription(String description) { this.description = description; } @@ -407,7 +407,7 @@ public String getComment() { /** * Setter for the comment. - * + * @param comment String */ public void setComment(String comment) { this.comment = comment; } @@ -421,8 +421,7 @@ public Map getRemoved() { } /** - * Setter for the comment. - * + * Setter for removed. This creates the removed info before setting it */ public void setRemoved() { TimeZone tz = TimeZone.getTimeZone("UTC"); @@ -433,12 +432,12 @@ public void setRemoved() { Map removedInfo = new HashMap<>(); removedInfo.put("by", Jenkins.getAuthentication().getName()); removedInfo.put("timestamp", nowAsISO); - this.removed = removedInfo; + setRemoved(removedInfo); } /** - * Setter for the comment. - * + * Setter for removed, when removed is defined elsewhere + * @param removedInfo map containing "by" and "timestamp" keys */ public void setRemoved(Map removedInfo) { this.removed = removedInfo; @@ -500,7 +499,7 @@ public List getModifications() { /** * Setter for the list of modifications. - * + * @param modifications List of {@link FailureCauseModification} */ public void setModifications(List modifications) { this.modifications = modifications; @@ -650,7 +649,7 @@ public List getIndications() { /** * Setter for the list of indications. - * + * @param indications List of {@link Indication} */ public void setIndications(List indications) { this.indications = indications; } @@ -706,24 +705,23 @@ public FailureCauseDescriptor getDescriptor() { @Override public boolean equals(Object o) { + if (o == this) return true; if (!(o instanceof FailureCause)) { return false; } + FailureCause fc = (FailureCause) o; + Field[] fields = this.getClass().getDeclaredFields(); + EqualsBuilder eb = new EqualsBuilder(); for (Field f:fields) { try { - if (f.get(this) == null && f.get(o) == null) { - continue; - } - if (!f.get(this).equals(f.get(o))) { - return false; - } + eb.append(f.get(this), f.get(fc)); } catch (IllegalArgumentException | IllegalAccessException e) { return false; } } - return true; + return eb.isEquals(); } /** diff --git a/src/main/java/com/sonyericsson/jenkins/plugins/bfa/model/FailureCauseModification.java b/src/main/java/com/sonyericsson/jenkins/plugins/bfa/model/FailureCauseModification.java index d054e79e..6eac0a3d 100644 --- a/src/main/java/com/sonyericsson/jenkins/plugins/bfa/model/FailureCauseModification.java +++ b/src/main/java/com/sonyericsson/jenkins/plugins/bfa/model/FailureCauseModification.java @@ -57,6 +57,9 @@ public FailureCauseModification(@JsonProperty("user") String user, @JsonProperty } } + /** + * Empty constructor used for DynamoDB converting. + */ @DataBoundConstructor public FailureCauseModification() {}; @@ -75,7 +78,7 @@ public Date getTime() { /** * Setter for the time. - * + * @param time {@link Date} */ public void setTime(Date time) { this.time = time; } @@ -90,7 +93,7 @@ public String getUser() { /** * Setter for the user. - * + * @param user String of user name */ public void setUser(String user) { this.user = user; } diff --git a/src/main/java/com/sonyericsson/jenkins/plugins/bfa/model/indication/Indication.java b/src/main/java/com/sonyericsson/jenkins/plugins/bfa/model/indication/Indication.java index 3b9d579b..242ad642 100644 --- a/src/main/java/com/sonyericsson/jenkins/plugins/bfa/model/indication/Indication.java +++ b/src/main/java/com/sonyericsson/jenkins/plugins/bfa/model/indication/Indication.java @@ -108,11 +108,7 @@ public String toString() { @Override public boolean equals(Object o) { - if (!(o instanceof Indication)) { - return false; - } - - return this.pattern.equals(((Indication) o).pattern); + return o instanceof Indication && this.getPattern().equals(((Indication) o).getPattern()); } /** diff --git a/src/test/java/com/sonyericsson/jenkins/plugins/bfa/db/DynamoDBKnowledgeBaseTest.java b/src/test/java/com/sonyericsson/jenkins/plugins/bfa/db/DynamoDBKnowledgeBaseTest.java index 766d9db5..2ca8b055 100644 --- a/src/test/java/com/sonyericsson/jenkins/plugins/bfa/db/DynamoDBKnowledgeBaseTest.java +++ b/src/test/java/com/sonyericsson/jenkins/plugins/bfa/db/DynamoDBKnowledgeBaseTest.java @@ -29,8 +29,6 @@ import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBScanExpression; import com.amazonaws.services.dynamodbv2.model.AttributeValue; import com.amazonaws.services.dynamodbv2.model.ScanResult; -import com.mongodb.DBObject; -import com.mongodb.MongoException; import com.amazonaws.services.dynamodbv2.AmazonDynamoDB; import com.sonyericsson.jenkins.plugins.bfa.model.FailureCause; import com.sonyericsson.jenkins.plugins.bfa.model.indication.BuildLogIndication; @@ -54,7 +52,6 @@ import java.util.*; import static junit.framework.Assert.*; -import static org.mockito.Matchers.any; import static org.powermock.api.mockito.PowerMockito.*; @@ -79,7 +76,7 @@ public class DynamoDBKnowledgeBaseTest { private List indications; private Indication indication; private Statistics mockedStatistics; - private String mockJenkinsUserName; + private String mockJenkinsUserName = "tester"; /** * Common stuff to set up for the tests. @@ -176,7 +173,7 @@ public void testGetCauseNames() throws Exception { assertNotNull("The fetched cause should not be null", fetchedCauses);; // Convert fetchedCauses to list, because PaginatedList does not allow iterators List actualCauses = new ArrayList<>(fetchedCauses); - assertEquals(expectedCauses, actualCauses); + assertTrue(CollectionUtils.isEqualCollection(expectedCauses, actualCauses)); } /** diff --git a/src/test/java/com/sonyericsson/jenkins/plugins/bfa/model/FailureCauseTest.java b/src/test/java/com/sonyericsson/jenkins/plugins/bfa/model/FailureCauseTest.java index eeafb5bb..48a0de7e 100644 --- a/src/test/java/com/sonyericsson/jenkins/plugins/bfa/model/FailureCauseTest.java +++ b/src/test/java/com/sonyericsson/jenkins/plugins/bfa/model/FailureCauseTest.java @@ -51,9 +51,7 @@ import java.util.LinkedList; import java.util.List; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertThat; +import static org.junit.Assert.*; import static org.junit.matchers.JUnitMatchers.hasItems; import static org.mockito.Matchers.any; import static org.mockito.Matchers.same; @@ -461,6 +459,30 @@ public void testDoConfigSubmitNewCloneAttempt() throws Exception { cause.doConfigSubmit(request, response); } + public FailureCause getCauseForEquality(String name, Date date, List i) { + + return new FailureCause(name, "myFailureCause", "description", "comment", date, + "category", i, null); + } + + @Test + public void testEquals() throws Exception { + Date d = new Date(); + Indication i = new BuildLogIndication("something"); + FailureCause cause1 = getCauseForEquality("foo", d, Collections.singletonList(i)); + FailureCause cause2 = getCauseForEquality("foo", d, Collections.singletonList(i)); + assertTrue(cause1.equals(cause2)); + } + + @Test + public void testNotEquals() throws Exception { + Date d = new Date(); + Indication i = new BuildLogIndication("something"); + FailureCause cause1 = getCauseForEquality("foo1", d, Collections.singletonList(i)); + FailureCause cause2 = getCauseForEquality("foo2", d, Collections.singletonList(i)); + assertTrue(cause1.equals(cause2)); + } + /** * Mocks a request to contain the form data for a {@code FailureCause} with the provided content. * From 7462216c5de4e8207b9eed436f5d1721043639d9 Mon Sep 17 00:00:00 2001 From: "Petti, Ken" Date: Thu, 19 Apr 2018 20:16:36 -0400 Subject: [PATCH 12/16] fixing style and tests --- .../plugins/bfa/model/FailureCause.java | 1 - .../bfa/model/indication/Indication.java | 3 +- .../bfa/db/DynamoDBKnowledgeBaseTest.java | 7 +++-- .../plugins/bfa/model/FailureCauseTest.java | 22 +++++++++++---- .../bfa/model/indication/IndicationTest.java | 28 +++++++++++++++++++ 5 files changed, 52 insertions(+), 9 deletions(-) create mode 100644 src/test/java/com/sonyericsson/jenkins/plugins/bfa/model/indication/IndicationTest.java diff --git a/src/main/java/com/sonyericsson/jenkins/plugins/bfa/model/FailureCause.java b/src/main/java/com/sonyericsson/jenkins/plugins/bfa/model/FailureCause.java index abefabad..1ac3ac9e 100644 --- a/src/main/java/com/sonyericsson/jenkins/plugins/bfa/model/FailureCause.java +++ b/src/main/java/com/sonyericsson/jenkins/plugins/bfa/model/FailureCause.java @@ -711,7 +711,6 @@ public boolean equals(Object o) { } FailureCause fc = (FailureCause) o; - Field[] fields = this.getClass().getDeclaredFields(); EqualsBuilder eb = new EqualsBuilder(); for (Field f:fields) { diff --git a/src/main/java/com/sonyericsson/jenkins/plugins/bfa/model/indication/Indication.java b/src/main/java/com/sonyericsson/jenkins/plugins/bfa/model/indication/Indication.java index 242ad642..2bfdd57c 100644 --- a/src/main/java/com/sonyericsson/jenkins/plugins/bfa/model/indication/Indication.java +++ b/src/main/java/com/sonyericsson/jenkins/plugins/bfa/model/indication/Indication.java @@ -108,7 +108,8 @@ public String toString() { @Override public boolean equals(Object o) { - return o instanceof Indication && this.getPattern().equals(((Indication) o).getPattern()); + return o instanceof Indication && this.getUserProvidedExpression().equals( + ((Indication) o).getUserProvidedExpression()); } /** diff --git a/src/test/java/com/sonyericsson/jenkins/plugins/bfa/db/DynamoDBKnowledgeBaseTest.java b/src/test/java/com/sonyericsson/jenkins/plugins/bfa/db/DynamoDBKnowledgeBaseTest.java index 2ca8b055..6aa165eb 100644 --- a/src/test/java/com/sonyericsson/jenkins/plugins/bfa/db/DynamoDBKnowledgeBaseTest.java +++ b/src/test/java/com/sonyericsson/jenkins/plugins/bfa/db/DynamoDBKnowledgeBaseTest.java @@ -29,6 +29,8 @@ import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBScanExpression; import com.amazonaws.services.dynamodbv2.model.AttributeValue; import com.amazonaws.services.dynamodbv2.model.ScanResult; +import com.mongodb.DBObject; +import com.mongodb.MongoException; import com.amazonaws.services.dynamodbv2.AmazonDynamoDB; import com.sonyericsson.jenkins.plugins.bfa.model.FailureCause; import com.sonyericsson.jenkins.plugins.bfa.model.indication.BuildLogIndication; @@ -52,6 +54,7 @@ import java.util.*; import static junit.framework.Assert.*; +import static org.mockito.Matchers.any; import static org.powermock.api.mockito.PowerMockito.*; @@ -76,7 +79,7 @@ public class DynamoDBKnowledgeBaseTest { private List indications; private Indication indication; private Statistics mockedStatistics; - private String mockJenkinsUserName = "tester"; + private String mockJenkinsUserName = "Jtester"; /** * Common stuff to set up for the tests. @@ -173,7 +176,7 @@ public void testGetCauseNames() throws Exception { assertNotNull("The fetched cause should not be null", fetchedCauses);; // Convert fetchedCauses to list, because PaginatedList does not allow iterators List actualCauses = new ArrayList<>(fetchedCauses); - assertTrue(CollectionUtils.isEqualCollection(expectedCauses, actualCauses)); + assertTrue(expectedCauses.equals(actualCauses)); } /** diff --git a/src/test/java/com/sonyericsson/jenkins/plugins/bfa/model/FailureCauseTest.java b/src/test/java/com/sonyericsson/jenkins/plugins/bfa/model/FailureCauseTest.java index 48a0de7e..7639286e 100644 --- a/src/test/java/com/sonyericsson/jenkins/plugins/bfa/model/FailureCauseTest.java +++ b/src/test/java/com/sonyericsson/jenkins/plugins/bfa/model/FailureCauseTest.java @@ -459,28 +459,40 @@ public void testDoConfigSubmitNewCloneAttempt() throws Exception { cause.doConfigSubmit(request, response); } + /** + * Helper to return instance of {@link FailureCause} + * @param name name of the cause + * @param date instance of {@link Date} + * @param i list of {@link Indication} + * @return {@link FailureCause} + */ public FailureCause getCauseForEquality(String name, Date date, List i) { - return new FailureCause(name, "myFailureCause", "description", "comment", date, "category", i, null); } + /** + * Test two {@link FailureCause} instances are equal + */ @Test - public void testEquals() throws Exception { + public void testEquals() { Date d = new Date(); Indication i = new BuildLogIndication("something"); FailureCause cause1 = getCauseForEquality("foo", d, Collections.singletonList(i)); FailureCause cause2 = getCauseForEquality("foo", d, Collections.singletonList(i)); - assertTrue(cause1.equals(cause2)); + assertEquals(cause1, cause2); } + /** + * Test two {@link FailureCause} instances are not equal + */ @Test - public void testNotEquals() throws Exception { + public void testNotEquals(){ Date d = new Date(); Indication i = new BuildLogIndication("something"); FailureCause cause1 = getCauseForEquality("foo1", d, Collections.singletonList(i)); FailureCause cause2 = getCauseForEquality("foo2", d, Collections.singletonList(i)); - assertTrue(cause1.equals(cause2)); + assertNotEquals(cause1, cause2); } /** diff --git a/src/test/java/com/sonyericsson/jenkins/plugins/bfa/model/indication/IndicationTest.java b/src/test/java/com/sonyericsson/jenkins/plugins/bfa/model/indication/IndicationTest.java new file mode 100644 index 00000000..a0cc366b --- /dev/null +++ b/src/test/java/com/sonyericsson/jenkins/plugins/bfa/model/indication/IndicationTest.java @@ -0,0 +1,28 @@ +package com.sonyericsson.jenkins.plugins.bfa.model.indication; + +import org.junit.Test; + +import static org.junit.Assert.*; + +public class IndicationTest { + + /** + * Test two {@link Indication} instances are equal + */ + @Test + public void testEquals() { + BuildLogIndication i1 = new BuildLogIndication("regex"); + BuildLogIndication i2 = new BuildLogIndication("regex"); + assertEquals(i1, i2); + } + + /** + * Test two {@link Indication} instances are not equal + */ + @Test + public void testNotEquals() { + BuildLogIndication i1 = new BuildLogIndication("foo"); + BuildLogIndication i2 = new BuildLogIndication("bar"); + assertNotEquals(i1, i2); + } +} \ No newline at end of file From 0af87556a72094dcdd3462d4f36b0bae492f98df Mon Sep 17 00:00:00 2001 From: "Petti, Ken" Date: Thu, 19 Apr 2018 23:10:08 -0400 Subject: [PATCH 13/16] fixing checkstyle --- .../plugins/bfa/db/DynamoDBKnowledgeBase.java | 34 ++++++++- .../plugins/bfa/model/FailureCause.java | 71 ++++++++++++++----- .../bfa/model/indication/Indication.java | 12 +++- .../bfa/db/DynamoDBKnowledgeBaseTest.java | 69 ++++++++++++------ .../plugins/bfa/model/FailureCauseTest.java | 11 +-- .../bfa/model/indication/IndicationTest.java | 14 ++-- 6 files changed, 164 insertions(+), 47 deletions(-) diff --git a/src/main/java/com/sonyericsson/jenkins/plugins/bfa/db/DynamoDBKnowledgeBase.java b/src/main/java/com/sonyericsson/jenkins/plugins/bfa/db/DynamoDBKnowledgeBase.java index 6c2d95d2..af1c5d94 100644 --- a/src/main/java/com/sonyericsson/jenkins/plugins/bfa/db/DynamoDBKnowledgeBase.java +++ b/src/main/java/com/sonyericsson/jenkins/plugins/bfa/db/DynamoDBKnowledgeBase.java @@ -322,6 +322,30 @@ public boolean equals(KnowledgeBase oldKnowledgeBase) { } } + /** + * Overrides base Object equals. + * @param other object to check + * @return boolean if values are equal + */ + @Override + public boolean equals(Object other) { + if (other instanceof KnowledgeBase) { + return this.equals((KnowledgeBase)other); + } else { + return false; + } + } + + /** + * Makes checkstyle happy. + * @return hashcode of class + */ + @Override + public int hashCode() { + //Making checkstyle happy. + return getClass().getName().hashCode(); + } + /** * Called when the KnowledgeBase should be up and running. * @@ -402,6 +426,10 @@ private AmazonDynamoDB getDynamoDb() { return dynamoDB; } + /** + * Get a cached or new instance of {@link DynamoDBMapper}. + * @return dbMapper + */ private DynamoDBMapper getDbMapper() { if (dbMapper != null) { return dbMapper; @@ -431,7 +459,7 @@ private void createTable(CreateTableRequest request) { } /** - * Use Jenkins to get and instance of {@link DynamoDBKnowledgeBaseDescriptor} + * Use Jenkins to get and instance of {@link DynamoDBKnowledgeBaseDescriptor}. * @return Descriptor */ @Override @@ -474,6 +502,10 @@ public String getDefaultCredentialProfile() { return DYNAMODB_DEFAULT_CREDENTIAL_PROFILE; } + /** + * Get a list of valid AWS regions for Jelly. + * @return ListBoxModel containing AWS regions + */ public ListBoxModel doFillRegionItems() { ListBoxModel items = new ListBoxModel(); for (Region r:RegionUtils.getRegions()) { diff --git a/src/main/java/com/sonyericsson/jenkins/plugins/bfa/model/FailureCause.java b/src/main/java/com/sonyericsson/jenkins/plugins/bfa/model/FailureCause.java index 1ac3ac9e..eecd1b81 100644 --- a/src/main/java/com/sonyericsson/jenkins/plugins/bfa/model/FailureCause.java +++ b/src/main/java/com/sonyericsson/jenkins/plugins/bfa/model/FailureCause.java @@ -23,7 +23,13 @@ */ package com.sonyericsson.jenkins.plugins.bfa.model; -import com.amazonaws.services.dynamodbv2.datamodeling.*; +import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBAttribute; +import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBHashKey; +import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBTable; +import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBAutoGeneratedKey; +import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBTypeConverted; +import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBIgnore; +import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBTypeConverter; import org.apache.commons.lang.builder.EqualsBuilder; import org.codehaus.jackson.type.TypeReference; import com.sonyericsson.jenkins.plugins.bfa.CauseManagement; @@ -59,10 +65,16 @@ import java.io.Serializable; import java.lang.reflect.Field; -import java.lang.reflect.Method; import java.text.DateFormat; import java.text.SimpleDateFormat; -import java.util.*; +import java.util.List; +import java.util.Date; +import java.util.LinkedList; +import java.util.HashMap; +import java.util.Map; +import java.util.TimeZone; +import java.util.ArrayList; +import java.util.Arrays; import java.util.logging.Level; import java.util.logging.Logger; @@ -95,7 +107,7 @@ public class FailureCause implements Serializable, Action, Describable modifications; @DynamoDBAttribute(attributeName = "_removed") - private Map removed; + private Map removed; /** * Standard data bound constructor. @@ -436,7 +448,7 @@ public void setRemoved() { } /** - * Setter for removed, when removed is defined elsewhere + * Setter for removed, when removed is defined elsewhere. * @param removedInfo map containing "by" and "timestamp" keys */ public void setRemoved(Map removedInfo) { @@ -705,12 +717,12 @@ public FailureCauseDescriptor getDescriptor() { @Override public boolean equals(Object o) { - if (o == this) return true; + if (o == this) { return true; } if (!(o instanceof FailureCause)) { return false; } - FailureCause fc = (FailureCause) o; + FailureCause fc = (FailureCause)o; Field[] fields = this.getClass().getDeclaredFields(); EqualsBuilder eb = new EqualsBuilder(); for (Field f:fields) { @@ -723,6 +735,16 @@ public boolean equals(Object o) { return eb.isEquals(); } + /** + * Makes checkstyle happy. + * @return hashcode of class + */ + @Override + public int hashCode() { + //Making checkstyle happy. + return getClass().getName().hashCode(); + } + /** * Descriptor is only used for auto completion of categories. */ @@ -801,35 +823,50 @@ public AutoCompletionCandidates doAutoCompleteCategories(@QueryParameter String } } - static public class IndicationsTypeConverter implements DynamoDBTypeConverter>, List> { + /** + * Defines methods to convert {@link Indication}s to/from DynamoDB objects. + */ + public static class IndicationsTypeConverter implements + DynamoDBTypeConverter>, List> { private ObjectMapper objectMapper = new ObjectMapper(); + /** + * Transform a list of {@link Indication} objects into a list of maps, + * for use as a {@link FailureCause} property. + * @param indications list of {@link Indication}s + * @return the converted list of {@link Indication}s + */ @Override - public List> convert(List indications) { - List> stringedIndications = new ArrayList<>(); + public List> convert(List indications) { + List> stringedIndications = new ArrayList<>(); for (Indication i:indications) { try { -// Have to convert to the indication to a string to capture the indication type + // Have to convert to the indication to a string to capture the indication type String stringed = objectMapper.writeValueAsString(i); - TypeReference> typeRef - = new TypeReference>() {}; -// Then convert the stringed to a map, so the whole list of inications can be written as a string + TypeReference> typeRef + = new TypeReference>() {}; + // Then convert the stringed to a map, so the whole list of indication can be written as a string Map mapped = objectMapper.readValue(stringed, typeRef); stringedIndications.add(mapped); } catch (Exception e) { e.printStackTrace(); } } - return stringedIndications; } + /** + * Transform a list of Indication maps into a list of {@link Indication}s. + * @param toUnConvert list of converted Indications + * @return list of {@link Indication}s + */ @Override - public List unconvert(List> s) { + public List unconvert(List> toUnConvert) { List indications = new ArrayList<>(); - for (Map i:s) { + for (Map i:toUnConvert) { String clazz = i.get("@class"); try { + // Create a new Indication instance based on the class that was stored when converting Object obj = Class.forName(clazz).getConstructor(String.class).newInstance(i.get("pattern")); Indication indication = Indication.class.cast(obj); indications.add(indication); diff --git a/src/main/java/com/sonyericsson/jenkins/plugins/bfa/model/indication/Indication.java b/src/main/java/com/sonyericsson/jenkins/plugins/bfa/model/indication/Indication.java index 2bfdd57c..c8832171 100644 --- a/src/main/java/com/sonyericsson/jenkins/plugins/bfa/model/indication/Indication.java +++ b/src/main/java/com/sonyericsson/jenkins/plugins/bfa/model/indication/Indication.java @@ -109,7 +109,17 @@ public String toString() { @Override public boolean equals(Object o) { return o instanceof Indication && this.getUserProvidedExpression().equals( - ((Indication) o).getUserProvidedExpression()); + ((Indication)o).getUserProvidedExpression()); + } + + /** + * Makes checkstyle happy. + * @return hashcode of class + */ + @Override + public int hashCode() { + //Making checkstyle happy. + return getClass().getName().hashCode(); } /** diff --git a/src/test/java/com/sonyericsson/jenkins/plugins/bfa/db/DynamoDBKnowledgeBaseTest.java b/src/test/java/com/sonyericsson/jenkins/plugins/bfa/db/DynamoDBKnowledgeBaseTest.java index 6aa165eb..5714bdbb 100644 --- a/src/test/java/com/sonyericsson/jenkins/plugins/bfa/db/DynamoDBKnowledgeBaseTest.java +++ b/src/test/java/com/sonyericsson/jenkins/plugins/bfa/db/DynamoDBKnowledgeBaseTest.java @@ -29,8 +29,6 @@ import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBScanExpression; import com.amazonaws.services.dynamodbv2.model.AttributeValue; import com.amazonaws.services.dynamodbv2.model.ScanResult; -import com.mongodb.DBObject; -import com.mongodb.MongoException; import com.amazonaws.services.dynamodbv2.AmazonDynamoDB; import com.sonyericsson.jenkins.plugins.bfa.model.FailureCause; import com.sonyericsson.jenkins.plugins.bfa.model.indication.BuildLogIndication; @@ -51,11 +49,25 @@ import org.powermock.core.classloader.annotations.PowerMockIgnore; -import java.util.*; +import java.util.LinkedList; +import java.util.Map; +import java.util.ArrayList; +import java.util.List; +import java.util.Collection; +import java.util.Date; + + +import static junit.framework.Assert.assertTrue; +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertSame; +import static junit.framework.Assert.assertNotSame; +import static junit.framework.Assert.assertNotNull; +import static org.powermock.api.mockito.PowerMockito.spy; +import static org.powermock.api.mockito.PowerMockito.mock; +import static org.powermock.api.mockito.PowerMockito.whenNew; +import static org.powermock.api.mockito.PowerMockito.doReturn; +import static org.powermock.api.mockito.PowerMockito.doNothing; -import static junit.framework.Assert.*; -import static org.mockito.Matchers.any; -import static org.powermock.api.mockito.PowerMockito.*; /** @@ -64,7 +76,7 @@ * @author Tomas Westling <tomas.westling@sonyericsson.com> */ @RunWith(PowerMockRunner.class) -@PowerMockIgnore( {"javax.*"}) +@PowerMockIgnore({"javax.*"}) @PrepareForTest({DynamoDBKnowledgeBase.class, Jenkins.class, Jenkins.DescriptorImpl.class}) public class DynamoDBKnowledgeBaseTest { @@ -80,9 +92,11 @@ public class DynamoDBKnowledgeBaseTest { private Indication indication; private Statistics mockedStatistics; private String mockJenkinsUserName = "Jtester"; + private static final int MAX_ITERATIONS = 3; /** * Common stuff to set up for the tests. + * @throws Exception if so. */ @Before public void setUp() throws Exception{ @@ -105,7 +119,7 @@ public void setUp() throws Exception{ } /** - * Helper to create a FailureCause during testing + * Helper to create a FailureCause during testing. * @param id string id FailureCause to return * @return FailureCause * @throws Exception if so. @@ -116,7 +130,7 @@ public FailureCause createFailureCause(String id) throws Exception{ } /** - * Sets up a mock scan result, which DynamoDB uses when creating a paginated list of results + * Sets up a mock scan result, which DynamoDB uses when creating a paginated list of results. * @param causes collection of FailureCauses * @throws Exception if so. */ @@ -155,9 +169,10 @@ public void testFindOneCause() throws Exception { @Test public void testGetCauseNames() throws Exception { Collection expectedCauses = new ArrayList<>(); - for (int i = 0; i < 5; i++) { + for (int i = 0; i < MAX_ITERATIONS; i++) { Integer id = i; - FailureCause cause = new FailureCause(id.toString(), "myFailureCause" + id.toString(), "description", "comment", new Date(), + FailureCause cause = new FailureCause(id.toString(), + "myFailureCause" + id.toString(), "description", "comment", new Date(), "category", indications, null); expectedCauses.add(cause); } @@ -173,7 +188,7 @@ public void testGetCauseNames() throws Exception { Mockito.verify(scanExpression).setProjectionExpression("id,#n"); Mockito.verify(scanExpression).setScanFilter(DynamoDBKnowledgeBase.NOT_REMOVED_FILTER_EXPRESSION); - assertNotNull("The fetched cause should not be null", fetchedCauses);; + assertNotNull("The fetched cause should not be null", fetchedCauses); // Convert fetchedCauses to list, because PaginatedList does not allow iterators List actualCauses = new ArrayList<>(fetchedCauses); assertTrue(expectedCauses.equals(actualCauses)); @@ -209,10 +224,14 @@ public void testSaveCause() throws Exception { assertSame(cause, savedCause); } + /** + * Tests getting a the shallow form of causes. + * @throws Exception if so + */ @Test public void testGetShallowCauses() throws Exception { Collection expectedCauses = new ArrayList<>(); - for (int i = 0; i < 5; i++) { + for (int i = 0; i < MAX_ITERATIONS; i++) { Integer id = i; FailureCause cause = createFailureCause(id.toString()); expectedCauses.add(cause); @@ -226,17 +245,18 @@ public void testGetShallowCauses() throws Exception { Mockito.verify(scanExpression).addExpressionAttributeNamesEntry("#n", "name"); Mockito.verify(scanExpression).addExpressionAttributeNamesEntry("#c", "comment"); Mockito.verify(scanExpression).addExpressionAttributeNamesEntry("#r", "_removed"); - Mockito.verify(scanExpression).setProjectionExpression("id,#n,description,categories,#c,modifications,lastOccurred"); + Mockito.verify(scanExpression) + .setProjectionExpression("id,#n,description,categories,#c,modifications,lastOccurred"); Mockito.verify(scanExpression).setFilterExpression(" attribute_not_exists(#r) "); - assertNotNull("The fetched cause should not be null", fetchedCauses);; + assertNotNull("The fetched cause should not be null", fetchedCauses); // Convert fetchedCauses to list, because PaginatedList does not allow iterators List actualCauses = new ArrayList<>(fetchedCauses); assertEquals(expectedCauses, actualCauses); } /** - * Test that the cause gets updated with removed info + * Test that the cause gets updated with removed info. * @throws Exception if so. */ @Test @@ -253,6 +273,10 @@ public void testRemoveCause() throws Exception { assertEquals(mockJenkinsUserName, actualCause.getRemoved().get("by")); } + /** + * Tests getting {@link FailureCause} categories. + * @throws Exception if so + */ @Test public void testGetCategories() throws Exception { List expectedCategories = new ArrayList<>(); @@ -261,9 +285,10 @@ public void testGetCategories() throws Exception { expectedCategories.add("category2"); Collection causes = new ArrayList<>(); - for (int i = 0; i < 3; i++) { + for (int i = 0; i < MAX_ITERATIONS; i++) { Integer id = i; - FailureCause cause = new FailureCause(id.toString(), "myFailureCause" + id.toString(), "description", "comment", new Date(), + FailureCause cause = new FailureCause(id.toString(), + "myFailureCause" + id.toString(), "description", "comment", new Date(), "category" + id.toString(), indications, null); causes.add(cause); } @@ -281,11 +306,15 @@ public void testGetCategories() throws Exception { assertTrue(CollectionUtils.isEqualCollection(expectedCategories, actualCategories)); } + /** + * Tests converting {@link FailureCause} from a different {@link KnowledgeBase} type. + * @throws Exception if so + */ @Test public void testConvertFrom() throws Exception { LocalFileKnowledgeBase localKb = spy(new LocalFileKnowledgeBase()); Collection causes = new ArrayList<>(); - for (int i = 0; i < 3; i++) { + for (int i = 0; i < MAX_ITERATIONS; i++) { Integer id = i; FailureCause cause = createFailureCause(id.toString()); causes.add(cause); @@ -294,6 +323,6 @@ public void testConvertFrom() throws Exception { doReturn(causes).when(localKb).getCauseNames(); doReturn(createFailureCause("foo")).when(kb).saveCause(Matchers.any(FailureCause.class)); kb.convertFrom(localKb); - Mockito.verify(kb, Mockito.times(3)).saveCause(Matchers.any(FailureCause.class)); + Mockito.verify(kb, Mockito.times(MAX_ITERATIONS)).saveCause(Matchers.any(FailureCause.class)); } } diff --git a/src/test/java/com/sonyericsson/jenkins/plugins/bfa/model/FailureCauseTest.java b/src/test/java/com/sonyericsson/jenkins/plugins/bfa/model/FailureCauseTest.java index 7639286e..9a8c5cf6 100644 --- a/src/test/java/com/sonyericsson/jenkins/plugins/bfa/model/FailureCauseTest.java +++ b/src/test/java/com/sonyericsson/jenkins/plugins/bfa/model/FailureCauseTest.java @@ -51,7 +51,10 @@ import java.util.LinkedList; import java.util.List; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertSame; import static org.junit.matchers.JUnitMatchers.hasItems; import static org.mockito.Matchers.any; import static org.mockito.Matchers.same; @@ -460,7 +463,7 @@ public void testDoConfigSubmitNewCloneAttempt() throws Exception { } /** - * Helper to return instance of {@link FailureCause} + * Helper to return instance of {@link FailureCause}. * @param name name of the cause * @param date instance of {@link Date} * @param i list of {@link Indication} @@ -472,7 +475,7 @@ public FailureCause getCauseForEquality(String name, Date date, List } /** - * Test two {@link FailureCause} instances are equal + * Test two {@link FailureCause} instances are equal. */ @Test public void testEquals() { @@ -484,7 +487,7 @@ public void testEquals() { } /** - * Test two {@link FailureCause} instances are not equal + * Test two {@link FailureCause} instances are not equal. */ @Test public void testNotEquals(){ diff --git a/src/test/java/com/sonyericsson/jenkins/plugins/bfa/model/indication/IndicationTest.java b/src/test/java/com/sonyericsson/jenkins/plugins/bfa/model/indication/IndicationTest.java index a0cc366b..4fe45921 100644 --- a/src/test/java/com/sonyericsson/jenkins/plugins/bfa/model/indication/IndicationTest.java +++ b/src/test/java/com/sonyericsson/jenkins/plugins/bfa/model/indication/IndicationTest.java @@ -2,12 +2,18 @@ import org.junit.Test; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +/** + * Tests for {@link Indication}. + * + * @author Ken Petti <kpetti@constantcontact.com> + */ public class IndicationTest { /** - * Test two {@link Indication} instances are equal + * Test two {@link Indication} instances are equal. */ @Test public void testEquals() { @@ -17,7 +23,7 @@ public void testEquals() { } /** - * Test two {@link Indication} instances are not equal + * Test two {@link Indication} instances are not equal. */ @Test public void testNotEquals() { @@ -25,4 +31,4 @@ public void testNotEquals() { BuildLogIndication i2 = new BuildLogIndication("bar"); assertNotEquals(i1, i2); } -} \ No newline at end of file +} From f763e832729e42e140283a8a6f650c3a86a3cf54 Mon Sep 17 00:00:00 2001 From: "Petti, Ken" Date: Thu, 19 Apr 2018 23:11:32 -0400 Subject: [PATCH 14/16] comment change --- .../sonyericsson/jenkins/plugins/bfa/model/FailureCause.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/sonyericsson/jenkins/plugins/bfa/model/FailureCause.java b/src/main/java/com/sonyericsson/jenkins/plugins/bfa/model/FailureCause.java index eecd1b81..8585c4f0 100644 --- a/src/main/java/com/sonyericsson/jenkins/plugins/bfa/model/FailureCause.java +++ b/src/main/java/com/sonyericsson/jenkins/plugins/bfa/model/FailureCause.java @@ -437,7 +437,8 @@ public Map getRemoved() { */ public void setRemoved() { TimeZone tz = TimeZone.getTimeZone("UTC"); - DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm'Z'"); // Quoted "Z" to indicate UTC, no timezone offset + // Quoted "Z" to indicate UTC, no timezone offset + DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm'Z'"); df.setTimeZone(tz); String nowAsISO = df.format(new Date()); From cd5e5a6d81eb1e3a95efaebcefd5de6728fd8c35 Mon Sep 17 00:00:00 2001 From: "Petti, Ken" Date: Thu, 19 Apr 2018 23:23:57 -0400 Subject: [PATCH 15/16] fixing exception throwing --- .../plugins/bfa/db/DynamoDBKnowledgeBase.java | 157 ++++++++---------- 1 file changed, 65 insertions(+), 92 deletions(-) diff --git a/src/main/java/com/sonyericsson/jenkins/plugins/bfa/db/DynamoDBKnowledgeBase.java b/src/main/java/com/sonyericsson/jenkins/plugins/bfa/db/DynamoDBKnowledgeBase.java index af1c5d94..d4f80715 100644 --- a/src/main/java/com/sonyericsson/jenkins/plugins/bfa/db/DynamoDBKnowledgeBase.java +++ b/src/main/java/com/sonyericsson/jenkins/plugins/bfa/db/DynamoDBKnowledgeBase.java @@ -102,21 +102,18 @@ public DynamoDBKnowledgeBase(String region, String credentialsPath, String crede } /** - * Get the list of {@link FailureCause}s. It is intended to be used in the scanning phase hence it should be - * returned as quickly as possible, so the list could be cached. + * Get the list of {@link FailureCause}s except those marked as removed. It is intended to be used in the scanning + * phase hence it should be returned as quickly as possible, so the list could be cached. * * @return the full list of causes. - * @throws Exception if something in the KnowledgeBase handling goes wrong. + * @throws UnsupportedOperationException if DynamoDB does not support the load/scan/save request. */ + // TODO: 4/19/18 Add caching here @Override - public Collection getCauses() throws Exception { - try { - DynamoDBScanExpression scan = new DynamoDBScanExpression(); - scan.setScanFilter(NOT_REMOVED_FILTER_EXPRESSION); - return getDbMapper().scan(FailureCause.class, scan); - } catch (Exception e) { - throw e; - } + public Collection getCauses() throws UnsupportedOperationException { + DynamoDBScanExpression scan = new DynamoDBScanExpression(); + scan.setScanFilter(NOT_REMOVED_FILTER_EXPRESSION); + return getDbMapper().scan(FailureCause.class, scan); } /** @@ -124,19 +121,15 @@ public Collection getCauses() throws Exception { * they will be used for editing. The objects returned should contain at least the id and the name of the cause. * * @return the full list of the names and ids of the causes. - * @throws Exception if something in the KnowledgeBase handling goes wrong. + * @throws UnsupportedOperationException if DynamoDB does not support the load/scan/save request. */ @Override - public Collection getCauseNames() throws Exception { - try { - DynamoDBScanExpression scan = new DynamoDBScanExpression(); - scan.addExpressionAttributeNamesEntry("#n", "name"); - scan.setProjectionExpression("id,#n"); - scan.setScanFilter(NOT_REMOVED_FILTER_EXPRESSION); - return getDbMapper().scan(FailureCause.class, scan); - } catch (Exception e) { - throw e; - } + public Collection getCauseNames() throws UnsupportedOperationException { + DynamoDBScanExpression scan = new DynamoDBScanExpression(); + scan.addExpressionAttributeNamesEntry("#n", "name"); + scan.setProjectionExpression("id,#n"); + scan.setScanFilter(NOT_REMOVED_FILTER_EXPRESSION); + return getDbMapper().scan(FailureCause.class, scan); } /** @@ -146,24 +139,20 @@ public Collection getCauseNames() throws Exception { * comment, lastOccurred and categories are preferred as well. * * @return a shallow list of all causes. - * @throws Exception if something in the KnowledgeBase handling goes wrong. + * @throws UnsupportedOperationException if DynamoDB does not support the load/scan/save request. * @see #getCauseNames() */ @Override - public Collection getShallowCauses() throws Exception { - try { - DynamoDBScanExpression scan = new DynamoDBScanExpression(); - // The following attributes are reserved words in Dynamo, so we need to substitute the actual name for - // something safe - scan.addExpressionAttributeNamesEntry("#n", "name"); - scan.addExpressionAttributeNamesEntry("#c", "comment"); - scan.addExpressionAttributeNamesEntry("#r", "_removed"); - scan.setProjectionExpression("id,#n,description,categories,#c,modifications,lastOccurred"); - scan.setFilterExpression(" attribute_not_exists(#r) "); - return getDbMapper().scan(FailureCause.class, scan); - } catch (Exception e) { - throw e; - } + public Collection getShallowCauses() throws UnsupportedOperationException { + DynamoDBScanExpression scan = new DynamoDBScanExpression(); + // The following attributes are reserved words in Dynamo, so we need to substitute the actual name for + // something safe + scan.addExpressionAttributeNamesEntry("#n", "name"); + scan.addExpressionAttributeNamesEntry("#c", "comment"); + scan.addExpressionAttributeNamesEntry("#r", "_removed"); + scan.setProjectionExpression("id,#n,description,categories,#c,modifications,lastOccurred"); + scan.setFilterExpression(" attribute_not_exists(#r) "); + return getDbMapper().scan(FailureCause.class, scan); } /** @@ -172,26 +161,22 @@ public Collection getShallowCauses() throws Exception { * * @param id the id of the cause. * @return the cause or null if a cause with that id could not be found. - * @throws Exception if something in the KnowledgeBase handling goes wrong. + * @throws UnsupportedOperationException if DynamoDB does not support the load/scan/save request. */ @Override - public FailureCause getCause(String id) throws Exception { - try { - return getDbMapper().load(FailureCause.class, id); - } catch (Exception e) { - throw e; - } + public FailureCause getCause(String id) throws UnsupportedOperationException { + return getDbMapper().load(FailureCause.class, id); } /** - * Saves a new cause to the db and generates a new id for the cause. + * Saves a new cause to the db, which generates a new id for the cause. * * @param cause the cause to add. * @return the same cause but with a new id. - * @throws Exception if something in the KnowledgeBase handling goes wrong. + * @throws UnsupportedOperationException if DynamoDB does not support the load/scan/save request. */ @Override - public FailureCause addCause(FailureCause cause) throws Exception { + public FailureCause addCause(FailureCause cause) throws UnsupportedOperationException { return saveCause(cause); } @@ -200,18 +185,14 @@ public FailureCause addCause(FailureCause cause) throws Exception { * * @param id the id of the cause to remove. * @return the removed FailureCause. - * @throws Exception if so. + * @throws UnsupportedOperationException if so. */ @Override - public FailureCause removeCause(String id) throws Exception { - try { - FailureCause cause = getDbMapper().load(FailureCause.class, id); - cause.setRemoved(); - getDbMapper().save(cause); - return cause; - } catch (Exception e) { - throw e; - } + public FailureCause removeCause(String id) throws UnsupportedOperationException { + FailureCause cause = getDbMapper().load(FailureCause.class, id); + cause.setRemoved(); + getDbMapper().save(cause); + return cause; } /** @@ -221,15 +202,11 @@ public FailureCause removeCause(String id) throws Exception { * * @param cause the cause to add. * @return the same cause but with a new id. - * @throws Exception if something in the KnowledgeBase handling goes wrong. + * @throws UnsupportedOperationException if DynamoDB does not support the load/scan/save request. */ @Override - public FailureCause saveCause(FailureCause cause) throws Exception { - try { - getDbMapper().save(cause); - } catch (Exception e) { - throw e; - } + public FailureCause saveCause(FailureCause cause) throws UnsupportedOperationException { + getDbMapper().save(cause); return cause; } @@ -238,7 +215,7 @@ public FailureCause saveCause(FailureCause cause) throws Exception { * then Jenkins config is saved, So it could just be that the old one is exactly the same as this one. * * @param oldKnowledgeBase the old one. - * @throws Exception if something in the KnowledgeBase handling goes wrong. + * @throws Exception if converting DB fails or something in the KnowledgeBase handling goes wrong. */ @Override public void convertFrom(KnowledgeBase oldKnowledgeBase) throws Exception { @@ -256,9 +233,9 @@ public void convertFrom(KnowledgeBase oldKnowledgeBase) throws Exception { * Copies all causes flagged as removed from the old database to this one. * * @param oldKnowledgeBase the old database. - * @throws Exception if something goes wrong. + * @throws UnsupportedOperationException if DynamoDB does not support the load/scan/save request. */ - protected void convertRemoved(DynamoDBKnowledgeBase oldKnowledgeBase) throws Exception { + private void convertRemoved(DynamoDBKnowledgeBase oldKnowledgeBase) throws UnsupportedOperationException { Collection removed = oldKnowledgeBase.getRemovedCauses(); for (FailureCause obj : removed) { saveCause(obj); @@ -269,39 +246,31 @@ protected void convertRemoved(DynamoDBKnowledgeBase oldKnowledgeBase) throws Exc * Gets all causes flagged as removed in a "raw" JSON format. * * @return the list of removed causes. - * @throws Exception if so. + * @throws UnsupportedOperationException if DynamoDB does not support the load/scan/save request. */ - protected Collection getRemovedCauses() throws Exception { - try { - DynamoDBScanExpression scan = new DynamoDBScanExpression(); - scan.setFilterExpression(" attribute_exists(#r) "); - return getDbMapper().scan(FailureCause.class, scan); - } catch (Exception e) { - throw e; - } + private Collection getRemovedCauses() throws UnsupportedOperationException { + DynamoDBScanExpression scan = new DynamoDBScanExpression(); + scan.setFilterExpression(" attribute_exists(#r) "); + return getDbMapper().scan(FailureCause.class, scan); } /** * Gets the unique categories of all FailureCauses. * * @return the list of categories. - * @throws Exception if something in the KnowledgeBase handling goes wrong. + * @throws UnsupportedOperationException if DynamoDB does not support the load/scan/save request. */ @Override - public List getCategories() throws Exception { - try { - DynamoDBScanExpression scan = new DynamoDBScanExpression(); - scan.setProjectionExpression("categories"); - scan.setFilterExpression(" attribute_exists(categories) "); - List causes = getDbMapper().scan(FailureCause.class, scan); - Set categories = new HashSet<>(); - for (FailureCause c:causes) { - categories.addAll(c.getCategories()); - } - return new ArrayList<>(categories); - } catch (Exception e) { - throw e; + public List getCategories() throws UnsupportedOperationException { + DynamoDBScanExpression scan = new DynamoDBScanExpression(); + scan.setProjectionExpression("categories"); + scan.setFilterExpression(" attribute_exists(categories) "); + List causes = getDbMapper().scan(FailureCause.class, scan); + Set categories = new HashSet<>(); + for (FailureCause c:causes) { + categories.addAll(c.getCategories()); } + return new ArrayList<>(categories); } /** @@ -349,16 +318,17 @@ public int hashCode() { /** * Called when the KnowledgeBase should be up and running. * - * @throws Exception if anything goes wrong during the startup. + * @throws AmazonClientException if anything goes wrong during the startup. */ @Override - public void start() throws Exception { + public void start() throws AmazonClientException { getDynamoDb(); } /** * Called when it is time to clean up after the KnowledgeBase. */ + // TODO: 4/19/18 Implement this @Override public void stop() { @@ -369,6 +339,7 @@ public void stop() { * * @return true if so. False if not or not implemented. */ + // TODO: 4/19/18 Implement this @Override public boolean isStatisticsEnabled() { return false; @@ -380,6 +351,7 @@ public boolean isStatisticsEnabled() { * * @return true if set, false otherwise or if not implemented */ + // TODO: 4/19/18 Implement this @Override public boolean isSuccessfulLoggingEnabled() { return false; @@ -391,6 +363,7 @@ public boolean isSuccessfulLoggingEnabled() { * @param stat the Statistics. * @throws Exception if something in the KnowledgeBase handling goes wrong. */ + // TODO: 4/19/18 Implement this @Override public void saveStatistics(Statistics stat) throws Exception { From c0284c94eda3f9969f54fc4b6857ef45eebdb97f Mon Sep 17 00:00:00 2001 From: "Petti, Ken" Date: Wed, 16 May 2018 13:19:33 -0400 Subject: [PATCH 16/16] fixing style issues --- pom.xml | 2 +- .../plugins/bfa/db/DynamoDBKnowledgeBase.java | 37 +++++++------------ .../bfa/model/FailureCauseModification.java | 2 +- 3 files changed, 15 insertions(+), 26 deletions(-) diff --git a/pom.xml b/pom.xml index ebe2acc2..0e65494c 100644 --- a/pom.xml +++ b/pom.xml @@ -2,7 +2,7 @@ 4.0.0 com.sonyericsson.jenkins.plugins.bfa build-failure-analyzer - 1.19.3-SNAPSHOT + 1.20.0 hpi Build Failure Analyzer Jenkins Build Failure Analyzer Plugin diff --git a/src/main/java/com/sonyericsson/jenkins/plugins/bfa/db/DynamoDBKnowledgeBase.java b/src/main/java/com/sonyericsson/jenkins/plugins/bfa/db/DynamoDBKnowledgeBase.java index d4f80715..fd7f6507 100644 --- a/src/main/java/com/sonyericsson/jenkins/plugins/bfa/db/DynamoDBKnowledgeBase.java +++ b/src/main/java/com/sonyericsson/jenkins/plugins/bfa/db/DynamoDBKnowledgeBase.java @@ -39,6 +39,7 @@ */ public class DynamoDBKnowledgeBase extends KnowledgeBase { + private static final long serialVersionUID = 1; private static final String DYNAMODB_DEFAULT_REGION = Regions.DEFAULT_REGION.getName(); private static final String DYNAMODB_DEFAULT_CREDENTIALS_PATH = System.getProperty("user.home") + "/.aws/credentials"; @@ -48,7 +49,7 @@ public class DynamoDBKnowledgeBase extends KnowledgeBase { }}; private static AmazonDynamoDB dynamoDB; - private DynamoDBMapper dbMapper; + private transient DynamoDBMapper dbMapper; private String region; private String credentialsPath; @@ -106,11 +107,10 @@ public DynamoDBKnowledgeBase(String region, String credentialsPath, String crede * phase hence it should be returned as quickly as possible, so the list could be cached. * * @return the full list of causes. - * @throws UnsupportedOperationException if DynamoDB does not support the load/scan/save request. */ // TODO: 4/19/18 Add caching here @Override - public Collection getCauses() throws UnsupportedOperationException { + public Collection getCauses() { DynamoDBScanExpression scan = new DynamoDBScanExpression(); scan.setScanFilter(NOT_REMOVED_FILTER_EXPRESSION); return getDbMapper().scan(FailureCause.class, scan); @@ -121,10 +121,9 @@ public Collection getCauses() throws UnsupportedOperationException * they will be used for editing. The objects returned should contain at least the id and the name of the cause. * * @return the full list of the names and ids of the causes. - * @throws UnsupportedOperationException if DynamoDB does not support the load/scan/save request. */ @Override - public Collection getCauseNames() throws UnsupportedOperationException { + public Collection getCauseNames() { DynamoDBScanExpression scan = new DynamoDBScanExpression(); scan.addExpressionAttributeNamesEntry("#n", "name"); scan.setProjectionExpression("id,#n"); @@ -139,11 +138,10 @@ public Collection getCauseNames() throws UnsupportedOperationExcep * comment, lastOccurred and categories are preferred as well. * * @return a shallow list of all causes. - * @throws UnsupportedOperationException if DynamoDB does not support the load/scan/save request. * @see #getCauseNames() */ @Override - public Collection getShallowCauses() throws UnsupportedOperationException { + public Collection getShallowCauses() { DynamoDBScanExpression scan = new DynamoDBScanExpression(); // The following attributes are reserved words in Dynamo, so we need to substitute the actual name for // something safe @@ -161,10 +159,9 @@ public Collection getShallowCauses() throws UnsupportedOperationEx * * @param id the id of the cause. * @return the cause or null if a cause with that id could not be found. - * @throws UnsupportedOperationException if DynamoDB does not support the load/scan/save request. */ @Override - public FailureCause getCause(String id) throws UnsupportedOperationException { + public FailureCause getCause(String id) { return getDbMapper().load(FailureCause.class, id); } @@ -173,10 +170,9 @@ public FailureCause getCause(String id) throws UnsupportedOperationException { * * @param cause the cause to add. * @return the same cause but with a new id. - * @throws UnsupportedOperationException if DynamoDB does not support the load/scan/save request. */ @Override - public FailureCause addCause(FailureCause cause) throws UnsupportedOperationException { + public FailureCause addCause(FailureCause cause) { return saveCause(cause); } @@ -185,10 +181,9 @@ public FailureCause addCause(FailureCause cause) throws UnsupportedOperationExce * * @param id the id of the cause to remove. * @return the removed FailureCause. - * @throws UnsupportedOperationException if so. */ @Override - public FailureCause removeCause(String id) throws UnsupportedOperationException { + public FailureCause removeCause(String id) { FailureCause cause = getDbMapper().load(FailureCause.class, id); cause.setRemoved(); getDbMapper().save(cause); @@ -202,10 +197,9 @@ public FailureCause removeCause(String id) throws UnsupportedOperationException * * @param cause the cause to add. * @return the same cause but with a new id. - * @throws UnsupportedOperationException if DynamoDB does not support the load/scan/save request. */ @Override - public FailureCause saveCause(FailureCause cause) throws UnsupportedOperationException { + public FailureCause saveCause(FailureCause cause) { getDbMapper().save(cause); return cause; } @@ -233,9 +227,8 @@ public void convertFrom(KnowledgeBase oldKnowledgeBase) throws Exception { * Copies all causes flagged as removed from the old database to this one. * * @param oldKnowledgeBase the old database. - * @throws UnsupportedOperationException if DynamoDB does not support the load/scan/save request. */ - private void convertRemoved(DynamoDBKnowledgeBase oldKnowledgeBase) throws UnsupportedOperationException { + private void convertRemoved(DynamoDBKnowledgeBase oldKnowledgeBase) { Collection removed = oldKnowledgeBase.getRemovedCauses(); for (FailureCause obj : removed) { saveCause(obj); @@ -246,9 +239,8 @@ private void convertRemoved(DynamoDBKnowledgeBase oldKnowledgeBase) throws Unsup * Gets all causes flagged as removed in a "raw" JSON format. * * @return the list of removed causes. - * @throws UnsupportedOperationException if DynamoDB does not support the load/scan/save request. */ - private Collection getRemovedCauses() throws UnsupportedOperationException { + private Collection getRemovedCauses() { DynamoDBScanExpression scan = new DynamoDBScanExpression(); scan.setFilterExpression(" attribute_exists(#r) "); return getDbMapper().scan(FailureCause.class, scan); @@ -258,10 +250,9 @@ private Collection getRemovedCauses() throws UnsupportedOperationE * Gets the unique categories of all FailureCauses. * * @return the list of categories. - * @throws UnsupportedOperationException if DynamoDB does not support the load/scan/save request. */ @Override - public List getCategories() throws UnsupportedOperationException { + public List getCategories() { DynamoDBScanExpression scan = new DynamoDBScanExpression(); scan.setProjectionExpression("categories"); scan.setFilterExpression(" attribute_exists(categories) "); @@ -317,11 +308,9 @@ public int hashCode() { /** * Called when the KnowledgeBase should be up and running. - * - * @throws AmazonClientException if anything goes wrong during the startup. */ @Override - public void start() throws AmazonClientException { + public void start() { getDynamoDb(); } diff --git a/src/main/java/com/sonyericsson/jenkins/plugins/bfa/model/FailureCauseModification.java b/src/main/java/com/sonyericsson/jenkins/plugins/bfa/model/FailureCauseModification.java index 6eac0a3d..603d8e68 100644 --- a/src/main/java/com/sonyericsson/jenkins/plugins/bfa/model/FailureCauseModification.java +++ b/src/main/java/com/sonyericsson/jenkins/plugins/bfa/model/FailureCauseModification.java @@ -80,7 +80,7 @@ public Date getTime() { * Setter for the time. * @param time {@link Date} */ - public void setTime(Date time) { this.time = time; } + public void setTime(Date time) { this.time = new Date(time.getTime()); } /** * Getter for the user.