From 3cf400328562e7005d013b3a3c6c76cfb2711b99 Mon Sep 17 00:00:00 2001 From: Andrea Date: Tue, 28 Mar 2017 16:47:21 +0200 Subject: [PATCH 01/43] adding support for reading tags per application --- .../wasabi/api/ApplicationsResource.java | 48 +++++++++++++++++++ .../intuit/wasabi/experiment/Experiments.java | 12 +++++ .../experiment/impl/ExperimentsImpl.java | 10 ++++ .../repository/ExperimentRepository.java | 7 +++ .../accessor/ExperimentTagAccessor.java | 40 ++++++++++++++++ .../impl/CassandraExperimentRepository.java | 26 ++++++++++ .../index/ExperimentTagsByApplication.java | 41 ++++++++++++++++ .../DatabaseExperimentRepository.java | 10 ++-- 8 files changed, 190 insertions(+), 4 deletions(-) create mode 100644 modules/repository-datastax/src/main/java/com/intuit/wasabi/repository/cassandra/accessor/ExperimentTagAccessor.java create mode 100644 modules/repository-datastax/src/main/java/com/intuit/wasabi/repository/cassandra/pojo/index/ExperimentTagsByApplication.java diff --git a/modules/api/src/main/java/com/intuit/wasabi/api/ApplicationsResource.java b/modules/api/src/main/java/com/intuit/wasabi/api/ApplicationsResource.java index 5800966d6..9a06391c1 100644 --- a/modules/api/src/main/java/com/intuit/wasabi/api/ApplicationsResource.java +++ b/modules/api/src/main/java/com/intuit/wasabi/api/ApplicationsResource.java @@ -19,6 +19,7 @@ import com.google.common.collect.ImmutableMap; import com.google.inject.Inject; import com.google.inject.Singleton; +import com.intuit.wasabi.authenticationobjects.UserInfo; import com.intuit.wasabi.authorization.Authorization; import com.intuit.wasabi.exceptions.AuthenticationException; import com.intuit.wasabi.experiment.Experiments; @@ -43,8 +44,11 @@ import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.core.Response; +import java.util.Collection; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; import static com.intuit.wasabi.api.APISwaggerResource.DEFAULT_MODEXP; import static com.intuit.wasabi.api.APISwaggerResource.EXAMPLE_AUTHORIZATION_HEADER; @@ -373,4 +377,48 @@ public Response getPagesAndAssociatedExperimentsForApplication( throw exception; } } + + /** + * Returns a Map of allowed Applications to the corresponding + * tags that have been stored with them. + * + * @param authorizationHeader the authorization headers + * @return Response object containing the Applications with their tags + */ + @GET + @Path("/tags") + @Produces(APPLICATION_JSON) + @ApiOperation(value = "Returns a Map of Applications to their tags", + response = Map.class) + @Timed + public Response getAllExperimentTags( + @HeaderParam(AUTHORIZATION) + @ApiParam(value = EXAMPLE_AUTHORIZATION_HEADER, required = true) + final String authorizationHeader) { + try { + + // get the for the user visible Applications + List allApplications = experiments.getApplications(); + Set allowed = new HashSet<>(); + UserInfo.Username userName = authorization.getUser(authorizationHeader); + + for (Application.Name applicationName : allApplications) { + try { + authorization.checkUserPermissions(userName, applicationName, READ); + allowed.add(applicationName); + } catch (AuthenticationException ignored) { + LOGGER.trace("ignoring authentication exception", ignored); + } + } + + // get their associated tags + Map> allTags = experiments.getTagsForApplications(allowed); + + return httpHeader.headers().entity(allTags).build(); + } catch (Exception exception) { + LOGGER.error("Retrieving the Experiment tags failed with error:", + exception); + throw exception; + } + } } diff --git a/modules/experiment/src/main/java/com/intuit/wasabi/experiment/Experiments.java b/modules/experiment/src/main/java/com/intuit/wasabi/experiment/Experiments.java index a1193f5c2..f080c7435 100644 --- a/modules/experiment/src/main/java/com/intuit/wasabi/experiment/Experiments.java +++ b/modules/experiment/src/main/java/com/intuit/wasabi/experiment/Experiments.java @@ -22,7 +22,9 @@ import com.intuit.wasabi.experimentobjects.NewExperiment; import com.intuit.wasabi.experimentobjects.exceptions.InvalidExperimentStateTransitionException; +import java.util.Collection; import java.util.List; +import java.util.Map; /** * Interface to perform CRUD operations on experiment. In addition, it also @@ -155,4 +157,14 @@ boolean buildUpdatedExperiment(Experiment experiment, Experiment updates, Experi * @return a list of Application.Name types */ List getApplications(); + + /** + * Returns all tags that belong to the Applications specified in the given + * {@link com.intuit.wasabi.experimentobjects.Application.Name}s. + * + * @param applicationNames the {@link com.intuit.wasabi.experimentobjects.Application.Name}s for which the + * tags should be retrieved + * @return a {@link Map} containing the Applications and their tags + */ + Map> getTagsForApplications(Collection applicationNames); } diff --git a/modules/experiment/src/main/java/com/intuit/wasabi/experiment/impl/ExperimentsImpl.java b/modules/experiment/src/main/java/com/intuit/wasabi/experiment/impl/ExperimentsImpl.java index b594af70f..9e45982b2 100644 --- a/modules/experiment/src/main/java/com/intuit/wasabi/experiment/impl/ExperimentsImpl.java +++ b/modules/experiment/src/main/java/com/intuit/wasabi/experiment/impl/ExperimentsImpl.java @@ -42,8 +42,10 @@ import javax.inject.Inject; import java.util.ArrayList; +import java.util.Collection; import java.util.Date; import java.util.List; +import java.util.Map; import static com.intuit.wasabi.experimentobjects.Experiment.State.DELETED; import static com.intuit.wasabi.experimentobjects.Experiment.State.DRAFT; @@ -105,6 +107,14 @@ public List getApplications() { return cassandraRepository.getApplicationsList(); } + /** + * {@inheritDoc} + */ + @Override + public Map> getTagsForApplications(Collection applicationNames) { + return cassandraRepository.getTagListForApplications(applicationNames); + } + /** * {@inheritDoc} */ diff --git a/modules/repository-datastax/src/main/java/com/intuit/wasabi/repository/ExperimentRepository.java b/modules/repository-datastax/src/main/java/com/intuit/wasabi/repository/ExperimentRepository.java index 89091ddec..6335da0b4 100644 --- a/modules/repository-datastax/src/main/java/com/intuit/wasabi/repository/ExperimentRepository.java +++ b/modules/repository-datastax/src/main/java/com/intuit/wasabi/repository/ExperimentRepository.java @@ -265,5 +265,12 @@ void logBucketChanges(Experiment.ID experimentID, Bucket.Label bucketLabel, */ void createApplication(Application.Name applicationName); + /** + * Gets a list of tags associated with the given {@link Application.Name}. + * + * @param applicationNames the list of {@link Application.Name}s the tags should be retrieved for + * @return a Map of {@link Application.Name}s to their tags + */ + Map> getTagListForApplications(Collection applicationNames); } diff --git a/modules/repository-datastax/src/main/java/com/intuit/wasabi/repository/cassandra/accessor/ExperimentTagAccessor.java b/modules/repository-datastax/src/main/java/com/intuit/wasabi/repository/cassandra/accessor/ExperimentTagAccessor.java new file mode 100644 index 000000000..1f592e05b --- /dev/null +++ b/modules/repository-datastax/src/main/java/com/intuit/wasabi/repository/cassandra/accessor/ExperimentTagAccessor.java @@ -0,0 +1,40 @@ +/******************************************************************************* + * Copyright 2017 Intuit + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *******************************************************************************/ +package com.intuit.wasabi.repository.cassandra.accessor; + +import com.datastax.driver.core.Statement; +import com.datastax.driver.mapping.Result; +import com.datastax.driver.mapping.annotations.Accessor; +import com.datastax.driver.mapping.annotations.Query; +import com.intuit.wasabi.experimentobjects.Application; +import com.intuit.wasabi.repository.cassandra.pojo.index.ExperimentTagsByApplication; + +import java.util.Collection; +import java.util.Set; + +/** + * Accessor for the tags associated with Experiments. + */ +@Accessor +public interface ExperimentTagAccessor { + + @Query("select * from experiment_tags where application IN ? ") + Result getExperimentTags(Collection applicationNames); + + @Query("insert into experiment_tags(app_name, tags) values(?,?)") + Statement insert(Application.Name appName, Set tags); + +} diff --git a/modules/repository-datastax/src/main/java/com/intuit/wasabi/repository/cassandra/impl/CassandraExperimentRepository.java b/modules/repository-datastax/src/main/java/com/intuit/wasabi/repository/cassandra/impl/CassandraExperimentRepository.java index 8081a8440..fd012dfcd 100644 --- a/modules/repository-datastax/src/main/java/com/intuit/wasabi/repository/cassandra/impl/CassandraExperimentRepository.java +++ b/modules/repository-datastax/src/main/java/com/intuit/wasabi/repository/cassandra/impl/CassandraExperimentRepository.java @@ -45,12 +45,14 @@ import com.intuit.wasabi.repository.cassandra.accessor.ApplicationListAccessor; import com.intuit.wasabi.repository.cassandra.accessor.BucketAccessor; import com.intuit.wasabi.repository.cassandra.accessor.ExperimentAccessor; +import com.intuit.wasabi.repository.cassandra.accessor.ExperimentTagAccessor; import com.intuit.wasabi.repository.cassandra.accessor.audit.BucketAuditLogAccessor; import com.intuit.wasabi.repository.cassandra.accessor.audit.ExperimentAuditLogAccessor; import com.intuit.wasabi.repository.cassandra.accessor.index.ExperimentLabelIndexAccessor; import com.intuit.wasabi.repository.cassandra.accessor.index.ExperimentState; import com.intuit.wasabi.repository.cassandra.accessor.index.StateExperimentIndexAccessor; import com.intuit.wasabi.repository.cassandra.pojo.index.ExperimentByAppNameLabel; +import com.intuit.wasabi.repository.cassandra.pojo.index.ExperimentTagsByApplication; import com.intuit.wasabi.repository.cassandra.pojo.index.StateExperimentIndex; import org.slf4j.Logger; @@ -90,6 +92,8 @@ public class CassandraExperimentRepository implements ExperimentRepository { private ExperimentAuditLogAccessor experimentAuditLogAccessor; + private ExperimentTagAccessor experimentTagAccessor; + /** * @return the experimentAccessor */ @@ -1194,4 +1198,26 @@ public void createApplication(Application.Name applicationName) { } } + /** + * {@inheritDoc} + */ + @Override + public Map> getTagListForApplications(Collection applicationNames) { + + LOGGER.debug("Retrieving Experiment Tags for applications {}", applicationNames); + + try { + + Map> result = experimentTagAccessor.getExperimentTags(applicationNames) + .all().stream().collect(Collectors.toMap(exp -> Application.Name.valueOf(exp.getAppName()), + ExperimentTagsByApplication::getTags)); + + return result; + } catch (Exception e) { + LOGGER.error("Error while retieving ExperimentTags for {}", applicationNames, e); + throw new RepositoryException("Unable to get ExperimentTags for applications: \"" + + applicationNames.toString() + "\"" + e); + } + } + } diff --git a/modules/repository-datastax/src/main/java/com/intuit/wasabi/repository/cassandra/pojo/index/ExperimentTagsByApplication.java b/modules/repository-datastax/src/main/java/com/intuit/wasabi/repository/cassandra/pojo/index/ExperimentTagsByApplication.java new file mode 100644 index 000000000..a1bee88c4 --- /dev/null +++ b/modules/repository-datastax/src/main/java/com/intuit/wasabi/repository/cassandra/pojo/index/ExperimentTagsByApplication.java @@ -0,0 +1,41 @@ +/******************************************************************************* + * Copyright 2017 Intuit + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *******************************************************************************/ +package com.intuit.wasabi.repository.cassandra.pojo.index; + +import com.datastax.driver.mapping.annotations.Column; +import com.datastax.driver.mapping.annotations.PartitionKey; +import com.datastax.driver.mapping.annotations.Table; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.Collection; + +@Table(name = "experiment_tags") +@Data +@Builder(toBuilder = true) +@NoArgsConstructor +@AllArgsConstructor +public class ExperimentTagsByApplication { + + @PartitionKey(0) + @Column(name = "app_name") + String appName; + + @Column(name = "tags") + Collection tags; +} diff --git a/modules/repository-datastax/src/main/java/com/intuit/wasabi/repository/database/DatabaseExperimentRepository.java b/modules/repository-datastax/src/main/java/com/intuit/wasabi/repository/database/DatabaseExperimentRepository.java index bcb7ad6db..e22e6eabc 100644 --- a/modules/repository-datastax/src/main/java/com/intuit/wasabi/repository/database/DatabaseExperimentRepository.java +++ b/modules/repository-datastax/src/main/java/com/intuit/wasabi/repository/database/DatabaseExperimentRepository.java @@ -745,9 +745,7 @@ public BucketList getBuckets(Experiment.ID experimentID, boolean checkExperiment } /** - * Creates an application at top level - * - * @param applicationName Application Name + * {@inheritDoc} */ @Override public void createApplication(Application.Name applicationName) { @@ -755,9 +753,13 @@ public void createApplication(Application.Name applicationName) { } @Override - public void updateStateIndex(Experiment experiment) { + public Map> getTagListForApplications(Collection applicationNames) { throw new UnsupportedOperationException("Not supported "); } + @Override + public void updateStateIndex(Experiment experiment) { + throw new UnsupportedOperationException("Not supported "); + } } From fada2a8bd7626140a9303b279a37132e826a88d4 Mon Sep 17 00:00:00 2001 From: Andrea Date: Mon, 10 Apr 2017 12:34:29 +0200 Subject: [PATCH 02/43] adding tags to experiment objects --- .../wasabi/experimentobjects/Experiment.java | 35 +++++++++++++------ .../experimentobjects/ExperimentBase.java | 8 +++++ .../experimentobjects/NewExperiment.java | 23 ++++++++++-- .../PrioritizedExperiment.java | 26 ++++++++++++-- .../wasabi/experimentobjects/ContextTest.java | 2 -- .../ExperimentBatchTest.java | 2 -- .../ExperimentIDListTest.java | 2 -- .../experimentobjects/ExperimentListTest.java | 2 -- .../experimentobjects/NewExperimentTest.java | 14 ++++++-- .../PrioritizedExperimentTest.java | 19 +++++++--- 10 files changed, 103 insertions(+), 30 deletions(-) diff --git a/modules/experiment-objects/src/main/java/com/intuit/wasabi/experimentobjects/Experiment.java b/modules/experiment-objects/src/main/java/com/intuit/wasabi/experimentobjects/Experiment.java index a40548fbb..63df5d4a0 100644 --- a/modules/experiment-objects/src/main/java/com/intuit/wasabi/experimentobjects/Experiment.java +++ b/modules/experiment-objects/src/main/java/com/intuit/wasabi/experimentobjects/Experiment.java @@ -44,6 +44,8 @@ import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Set; +import java.util.TreeSet; import java.util.UUID; import static com.intuit.wasabi.experimentobjects.Experiment.State.DRAFT; @@ -99,6 +101,8 @@ public class Experiment implements Cloneable, ExperimentBase, Serializable { private Integer userCap; @ApiModelProperty(value = "creator of the experiment", required = false) private String creatorID; + @ApiModelProperty(value = "a set of experiment tags") + private Set tags; private Boolean favorite; @@ -318,6 +322,7 @@ public int hashCode() { .append(isRapidExperiment) .append(userCap) .append(creatorID) + .append(tags) .toHashCode(); } @@ -351,6 +356,7 @@ public boolean equals(Object obj) { .append(isRapidExperiment, other.getIsRapidExperiment()) .append(userCap, other.getUserCap()) .append(creatorID, other.getCreatorID()) + .append(tags, other.getTags()) .isEquals(); } @@ -429,6 +435,18 @@ public DateMidnight calculateLastDay() { return earliestDay; } + @Override + public Set getTags() { + return tags; + } + + public void setTags(Set tags) { + if (null != tags) + this.tags = new TreeSet<>(tags); + else + this.tags = new TreeSet<>(); + } + //TODO: redesign state and state transition to be state machine public enum State { @@ -529,6 +547,7 @@ private Builder(Experiment other) { instance.isRapidExperiment = other.isRapidExperiment; instance.userCap = other.userCap; instance.creatorID = other.creatorID; + instance.tags = other.tags; } private Date copyDate(Date date) { @@ -564,31 +583,26 @@ public Builder withModelVersion(String modelVersion) { public Builder withCreationTime(final Date creationTime) { this.instance.creationTime = creationTime; - return this; } public Builder withModificationTime(final Date modificationTime) { instance.modificationTime = modificationTime; - return this; } public Builder withDescription(final String description) { instance.description = description; - return this; } public Builder withHypothesisIsCorrect(final String hypothesisIsCorrect) { instance.hypothesisIsCorrect = hypothesisIsCorrect; - return this; } public Builder withResults(final String results) { instance.results = results; - return this; } @@ -599,37 +613,31 @@ public Builder withRule(final String rule) { public Builder withSamplingPercent(final Double samplingPercent) { instance.samplingPercent = samplingPercent; - return this; } public Builder withStartTime(final Date startTime) { instance.startTime = startTime; - return this; } public Builder withEndTime(final Date endTime) { instance.endTime = endTime; - return this; } public Builder withState(final State state) { instance.state = state; - return this; } public Builder withLabel(final Experiment.Label label) { instance.label = label; - return this; } public Builder withApplicationName(final Application.Name appName) { instance.applicationName = appName; - return this; } @@ -638,6 +646,11 @@ public Builder withCreatorID(final String creatorID) { return this; } + public Builder withTags(final Set tags) { + instance.setTags(tags); + return this; + } + public Experiment build() { Experiment result = instance; instance = null; diff --git a/modules/experiment-objects/src/main/java/com/intuit/wasabi/experimentobjects/ExperimentBase.java b/modules/experiment-objects/src/main/java/com/intuit/wasabi/experimentobjects/ExperimentBase.java index 91926e33b..d33bb9842 100644 --- a/modules/experiment-objects/src/main/java/com/intuit/wasabi/experimentobjects/ExperimentBase.java +++ b/modules/experiment-objects/src/main/java/com/intuit/wasabi/experimentobjects/ExperimentBase.java @@ -16,6 +16,7 @@ package com.intuit.wasabi.experimentobjects; import java.util.Date; +import java.util.Set; /** * This interface is a quick workaround for the problem that {@link Experiment}, {@link PrioritizedExperiment} @@ -88,4 +89,11 @@ public interface ExperimentBase { */ Boolean getIsPersonalizationEnabled(); + /** + * Returns the set of tags associated with ths experiment. + * + * @return a sorted set of Strings for all labels stored with the experiment. + */ + Set getTags(); + } diff --git a/modules/experiment-objects/src/main/java/com/intuit/wasabi/experimentobjects/NewExperiment.java b/modules/experiment-objects/src/main/java/com/intuit/wasabi/experimentobjects/NewExperiment.java index 01446cf11..b0e8440a4 100644 --- a/modules/experiment-objects/src/main/java/com/intuit/wasabi/experimentobjects/NewExperiment.java +++ b/modules/experiment-objects/src/main/java/com/intuit/wasabi/experimentobjects/NewExperiment.java @@ -23,6 +23,8 @@ import org.apache.commons.lang3.builder.ToStringStyle; import java.util.Date; +import java.util.Set; +import java.util.TreeSet; /** * Specification of a new experiment @@ -64,6 +66,8 @@ public class NewExperiment implements ExperimentBase { private Integer userCap = Integer.MAX_VALUE; @ApiModelProperty(required = false) private String creatorID = ""; + @ApiModelProperty(value = "a set of experiment tags") + private Set tags; public NewExperiment(Experiment.ID id) { super(); @@ -223,7 +227,6 @@ public Application.Name getApplicationName() { } public void setApplicationName(Application.Name value) { -// Preconditions.checkNotNull(value); applicationName = value; } @@ -232,10 +235,21 @@ public String getCreatorID() { } public void setCreatorID(String value) { -// Preconditions.checkNotNull(value); creatorID = value; } + @Override + public Set getTags() { + return tags; + } + + public void setTags(Set tags) { + if (null != tags) + this.tags = new TreeSet<>(tags); + else + this.tags = new TreeSet<>(); + } + @Override public String toString() { return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE); @@ -317,6 +331,11 @@ public Builder withCreatorID(final String value) { return this; } + public Builder withTags(final Set tags) { + instance.setTags(tags); + return this; + } + public NewExperiment build() { new ExperimentValidator().validateNewExperiment(instance); diff --git a/modules/experiment-objects/src/main/java/com/intuit/wasabi/experimentobjects/PrioritizedExperiment.java b/modules/experiment-objects/src/main/java/com/intuit/wasabi/experimentobjects/PrioritizedExperiment.java index 8aa2cf9e5..9acb3aef5 100644 --- a/modules/experiment-objects/src/main/java/com/intuit/wasabi/experimentobjects/PrioritizedExperiment.java +++ b/modules/experiment-objects/src/main/java/com/intuit/wasabi/experimentobjects/PrioritizedExperiment.java @@ -25,6 +25,8 @@ import java.io.Serializable; import java.util.Date; +import java.util.Set; +import java.util.TreeSet; /** * Object for holding an experiment with Priotization. @@ -69,9 +71,10 @@ public class PrioritizedExperiment implements Cloneable, ExperimentBase, Seriali private Integer userCap; @ApiModelProperty(required = false) private String creatorID = ""; - @ApiModelProperty(value = "priority within the application", required = true) private Integer priority; + @ApiModelProperty(value = "a set of experiment tags") + private Set tags; protected PrioritizedExperiment() { super(); @@ -250,6 +253,7 @@ public int hashCode() { .append(isRapidExperiment) .append(userCap) .append(creatorID) + .append(tags) .toHashCode(); } @@ -282,6 +286,7 @@ public boolean equals(Object obj) { .append(userCap, other.getUserCap()) .append(isRapidExperiment, other.getIsRapidExperiment()) .append(creatorID, other.getCreatorID()) + .append(tags, other.getTags()) .isEquals(); } @@ -306,6 +311,18 @@ public boolean isDeleted() { return state.equals(Experiment.State.DELETED); } + @Override + public Set getTags() { + return tags; + } + + public void setTags(Set tags) { + if (null != tags) + this.tags = new TreeSet<>(tags); + else + this.tags = new TreeSet<>(); + } + public static class Builder { private PrioritizedExperiment instance; @@ -336,6 +353,7 @@ private Builder(Experiment other, Integer priority) { instance.isRapidExperiment = other.getIsRapidExperiment(); instance.userCap = other.getUserCap(); instance.creatorID = other.getCreatorID(); + instance.tags = other.getTags(); } private Date copyDate(Date date) { @@ -396,7 +414,6 @@ public Builder withModelVersion(String modelVersion) { public Builder withLabel(final Experiment.Label label) { instance.label = label; - return this; } @@ -415,6 +432,11 @@ public Builder withCreatorID(final String creatorID) { return this; } + public Builder withTags(final Set tags) { + instance.setTags(tags); + return this; + } + public PrioritizedExperiment build() { PrioritizedExperiment result = instance; instance = null; diff --git a/modules/experiment-objects/src/test/java/com/intuit/wasabi/experimentobjects/ContextTest.java b/modules/experiment-objects/src/test/java/com/intuit/wasabi/experimentobjects/ContextTest.java index f31ee91be..bfa59b31e 100644 --- a/modules/experiment-objects/src/test/java/com/intuit/wasabi/experimentobjects/ContextTest.java +++ b/modules/experiment-objects/src/test/java/com/intuit/wasabi/experimentobjects/ContextTest.java @@ -28,8 +28,6 @@ /** * This class tests the functionality of the Context object. - *

- * Created by asuckro on 8/12/15. */ public class ContextTest { diff --git a/modules/experiment-objects/src/test/java/com/intuit/wasabi/experimentobjects/ExperimentBatchTest.java b/modules/experiment-objects/src/test/java/com/intuit/wasabi/experimentobjects/ExperimentBatchTest.java index c0451c577..8afd10f3c 100644 --- a/modules/experiment-objects/src/test/java/com/intuit/wasabi/experimentobjects/ExperimentBatchTest.java +++ b/modules/experiment-objects/src/test/java/com/intuit/wasabi/experimentobjects/ExperimentBatchTest.java @@ -28,8 +28,6 @@ /** * This class is testing the functionality of the {@link ExperimentBatch} - *

- * Created by asuckro on 8/19/15. */ public class ExperimentBatchTest { diff --git a/modules/experiment-objects/src/test/java/com/intuit/wasabi/experimentobjects/ExperimentIDListTest.java b/modules/experiment-objects/src/test/java/com/intuit/wasabi/experimentobjects/ExperimentIDListTest.java index 0b5529eeb..876d3d095 100644 --- a/modules/experiment-objects/src/test/java/com/intuit/wasabi/experimentobjects/ExperimentIDListTest.java +++ b/modules/experiment-objects/src/test/java/com/intuit/wasabi/experimentobjects/ExperimentIDListTest.java @@ -26,8 +26,6 @@ /** * This class tests the functionality of the {@link ExperimentIDList} - *

- * Created by asuckro on 8/19/15. */ public class ExperimentIDListTest { diff --git a/modules/experiment-objects/src/test/java/com/intuit/wasabi/experimentobjects/ExperimentListTest.java b/modules/experiment-objects/src/test/java/com/intuit/wasabi/experimentobjects/ExperimentListTest.java index 7e56b7e63..c50c8356a 100644 --- a/modules/experiment-objects/src/test/java/com/intuit/wasabi/experimentobjects/ExperimentListTest.java +++ b/modules/experiment-objects/src/test/java/com/intuit/wasabi/experimentobjects/ExperimentListTest.java @@ -24,8 +24,6 @@ /** * This tests the functionality of the {@link ExperimentList} - *

- * Created by asuckro on 8/20/15. */ public class ExperimentListTest { diff --git a/modules/experiment-objects/src/test/java/com/intuit/wasabi/experimentobjects/NewExperimentTest.java b/modules/experiment-objects/src/test/java/com/intuit/wasabi/experimentobjects/NewExperimentTest.java index 901cb20c1..e7faec88f 100644 --- a/modules/experiment-objects/src/test/java/com/intuit/wasabi/experimentobjects/NewExperimentTest.java +++ b/modules/experiment-objects/src/test/java/com/intuit/wasabi/experimentobjects/NewExperimentTest.java @@ -17,14 +17,17 @@ import org.junit.Test; +import java.util.Arrays; import java.util.Date; +import java.util.HashSet; +import java.util.Set; import static org.hamcrest.core.Is.is; import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; /** - * Created on 3/10/16. + * Test for the {@link NewExperiment} class. */ public class NewExperimentTest { @@ -32,6 +35,8 @@ public class NewExperimentTest { public void testBuilderCreation() { Experiment.ID id = Experiment.ID.newInstance(); Date date = new Date(); + String[] tagsArray = {"ui", "mobile", "12&"}; + Set tags = new HashSet<>(Arrays.asList(tagsArray)); NewExperiment.Builder builder = NewExperiment.withID(id); NewExperiment newExperiment = builder.withDescription("desc") .withIsPersonalizationEnabled(true) @@ -46,6 +51,7 @@ public void testBuilderCreation() { .withIsRapidExperiment(true) .withUserCap(1000) .withCreatorID("c1") + .withTags(tags) .build(); assertThat(newExperiment.getApplicationName(), is(Application.Name.valueOf("app"))); @@ -57,6 +63,10 @@ public void testBuilderCreation() { assertThat(newExperiment.getID(), is(id)); assertThat(newExperiment.getDescription(), is("desc")); assertThat(newExperiment.getLabel(), is(Experiment.Label.valueOf("label"))); + assertThat(newExperiment.getTags().size(), is(3)); + Arrays.sort(tagsArray); // the tags should be sorted + assertThat(newExperiment.getTags().toArray(), is(tagsArray)); + newExperiment.setApplicationName(Application.Name.valueOf("NewApp")); newExperiment.setCreatorID("c2"); @@ -72,7 +82,7 @@ public void testBuildBadSamplePercentage() { .withModelName("m1") .withModelVersion("v1") .withRule("r1") - .withSamplingPercent(null) //<-- this is what causes the execption + .withSamplingPercent(null) //<-- this is what causes the exception .withLabel(Experiment.Label.valueOf("label")) .withAppName(Application.Name.valueOf("app")) .withIsRapidExperiment(true) diff --git a/modules/experiment-objects/src/test/java/com/intuit/wasabi/experimentobjects/PrioritizedExperimentTest.java b/modules/experiment-objects/src/test/java/com/intuit/wasabi/experimentobjects/PrioritizedExperimentTest.java index 93cf272e5..4c55efeb1 100644 --- a/modules/experiment-objects/src/test/java/com/intuit/wasabi/experimentobjects/PrioritizedExperimentTest.java +++ b/modules/experiment-objects/src/test/java/com/intuit/wasabi/experimentobjects/PrioritizedExperimentTest.java @@ -17,18 +17,21 @@ import org.junit.Test; +import java.util.Arrays; import java.util.Date; +import java.util.HashSet; +import java.util.Set; +import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; /** * Test class for the {@link PrioritizedExperiment} - *

- *

- * Created by asuckro on 8/12/15. */ public class PrioritizedExperimentTest { + private String[] tagsArray = {"ui", "mobile", "12&"}; + private Set tags = new HashSet<>(Arrays.asList(tagsArray)); private PrioritizedExperiment prioExp = PrioritizedExperiment.withID(Experiment.ID.newInstance()) .withApplicationName(Application.Name.valueOf("appName")) @@ -39,6 +42,7 @@ public class PrioritizedExperimentTest { .withPriority(3) .withUserCap(42) .withIsRapidExperiment(false) + .withTags(tags) .build(); private Experiment exp = Experiment.withID(Experiment.ID.newInstance()) @@ -49,6 +53,7 @@ public class PrioritizedExperimentTest { .withState(Experiment.State.RUNNING) .withIsRapidExperiment(true) .withUserCap(300) + .withTags(tags) .build(); @Test @@ -85,6 +90,7 @@ public void testBuilderFromOtherExperiment() { assertEquals(fromExp.getLabel(), exp.getLabel()); assertEquals(fromExp.getApplicationName(), exp.getApplicationName()); assertEquals(fromExp.getIsRapidExperiment(), exp.getIsRapidExperiment()); + assertEquals(fromExp.getTags(), exp.getTags()); } @Test @@ -96,14 +102,17 @@ public void testBuilderMethods() { .withModelVersion("1") .withCreatorID("c1") .withStartTime(startTime) - .withCreationTime(startTime).build(); + .withCreationTime(startTime) + .withTags(tags) + .build(); assertEquals(startTime, exp.getCreationTime()); assertEquals(startTime, exp.getStartTime()); assertEquals("m1", exp.getModelName()); assertEquals("1", exp.getModelVersion()); assertEquals("c1", exp.getCreatorID()); - + Arrays.sort(tagsArray); + assertArrayEquals(tagsArray, exp.getTags().toArray()); } @Test From 6fba7d1df37aa11ea61a03a53a427e0ed56fd9f7 Mon Sep 17 00:00:00 2001 From: Andrea Date: Tue, 11 Apr 2017 15:46:25 +0200 Subject: [PATCH 03/43] finishing database layer and update tags with experiments --- .../experiment/impl/ExperimentsImpl.java | 9 ++++ .../V035__Create_ExperimentTag_table.cql | 5 ++ .../V036__Alter_Experiment_Add_Tags.cql | 1 + .../accessor/ExperimentAccessor.java | 15 ++++-- .../accessor/ExperimentTagAccessor.java | 11 +++++ .../impl/CassandraExperimentRepository.java | 36 +++++++++++++- .../accessor/ExperimentAccessorITest.java | 6 +-- .../CassandraExperimentRepositoryTest.java | 48 ++++++++++++++++++- .../impl/CassandraMutexRepositoryITest.java | 16 +++---- .../CassandraPrioritiesRepositoryITest.java | 4 +- 10 files changed, 131 insertions(+), 20 deletions(-) create mode 100644 modules/repository-datastax/db/mutation/V035__Create_ExperimentTag_table.cql create mode 100644 modules/repository-datastax/db/mutation/V036__Alter_Experiment_Add_Tags.cql diff --git a/modules/experiment/src/main/java/com/intuit/wasabi/experiment/impl/ExperimentsImpl.java b/modules/experiment/src/main/java/com/intuit/wasabi/experiment/impl/ExperimentsImpl.java index 9e45982b2..ce0d5cad4 100644 --- a/modules/experiment/src/main/java/com/intuit/wasabi/experiment/impl/ExperimentsImpl.java +++ b/modules/experiment/src/main/java/com/intuit/wasabi/experiment/impl/ExperimentsImpl.java @@ -460,6 +460,15 @@ public boolean buildUpdatedExperiment(Experiment experiment, Experiment updates, changeList.add(changeData); } + if (updates.getTags() != null && !updates.getTags().equals(experiment.getTags())) { + builder.withTags(updates.getTags()); + requiresUpdate = true; + changeData = new ExperimentAuditInfo("tags", + experiment.getTags().toString(), + updates.getTags().toString()); + changeList.add(changeData); + } + /* * Application name and label cannot be changed once the experiment is beyond the DRAFT state. * Hence, we are not including them as a part of the audit log. diff --git a/modules/repository-datastax/db/mutation/V035__Create_ExperimentTag_table.cql b/modules/repository-datastax/db/mutation/V035__Create_ExperimentTag_table.cql new file mode 100644 index 000000000..a45575d1d --- /dev/null +++ b/modules/repository-datastax/db/mutation/V035__Create_ExperimentTag_table.cql @@ -0,0 +1,5 @@ +create table experiment_tag ( + app_name text, + tags set, + PRIMARY KEY (app_name) +); \ No newline at end of file diff --git a/modules/repository-datastax/db/mutation/V036__Alter_Experiment_Add_Tags.cql b/modules/repository-datastax/db/mutation/V036__Alter_Experiment_Add_Tags.cql new file mode 100644 index 000000000..8c85991f6 --- /dev/null +++ b/modules/repository-datastax/db/mutation/V036__Alter_Experiment_Add_Tags.cql @@ -0,0 +1 @@ +alter table experiment ADD tags set; \ No newline at end of file diff --git a/modules/repository-datastax/src/main/java/com/intuit/wasabi/repository/cassandra/accessor/ExperimentAccessor.java b/modules/repository-datastax/src/main/java/com/intuit/wasabi/repository/cassandra/accessor/ExperimentAccessor.java index 1c4e24e5c..144ecb0fd 100644 --- a/modules/repository-datastax/src/main/java/com/intuit/wasabi/repository/cassandra/accessor/ExperimentAccessor.java +++ b/modules/repository-datastax/src/main/java/com/intuit/wasabi/repository/cassandra/accessor/ExperimentAccessor.java @@ -20,10 +20,12 @@ import com.datastax.driver.mapping.annotations.Accessor; import com.datastax.driver.mapping.annotations.Query; import com.google.common.util.concurrent.ListenableFuture; +import com.intuit.wasabi.experimentobjects.Application; import com.intuit.wasabi.repository.cassandra.pojo.Experiment; import java.util.Date; import java.util.List; +import java.util.Set; import java.util.UUID; /** @@ -53,13 +55,13 @@ public interface ExperimentAccessor { "rule = ?, sample_percent = ?, " + "start_time = ?, end_time = ?, " + "state=?, label=?, app_name=?, modified=? , is_personalized=?, model_name=?, model_version=?," + - " is_rapid_experiment=?, user_cap=?" + + " is_rapid_experiment=?, user_cap=?, tags = ?" + " where id = ?") ResultSet updateExperiment(String description, String hypothesisIsCorrect, String results, String rule, double sample_percent, Date start_time, Date end_time, String state, String label, String app_name, Date modified, boolean is_personalized, String model_name, String model_version, - boolean is_rapid_experiment, int user_cap, UUID experimentId); + boolean is_rapid_experiment, int user_cap, Set tags, UUID experimentId); @Query("select * from experiment where app_name = ?") @@ -71,14 +73,19 @@ ResultSet updateExperiment(String description, String hypothesisIsCorrect, Strin @Query("select * from experiment where id = ?") Result selectBy(UUID experimentId); + @Query("insert into experiment " + "(id, description, hypothesis_is_correct, results, rule, sample_percent, start_time, end_time, " + " state, label, app_name, created, modified, is_personalized, model_name, model_version," + - " is_rapid_experiment, user_cap, creatorid) " + + " is_rapid_experiment, user_cap, creatorid, tags) " + "values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)") void insertExperiment(UUID experimentId, String description, String hypothesisIsCorrect, String results, String rule, double samplePercent, Date startTime, Date endTime, String state, String label, String appName, Date created, Date modified, boolean isPersonalized, String modelName, - String modelVersion, boolean isRapidExperiment, int userCap, String creatorid); + String modelVersion, boolean isRapidExperiment, int userCap, String creatorid, + Set tags); + + @Query("select tags from experiment where app_name = ?;") + Result> getAllTags(Application.Name appName); } diff --git a/modules/repository-datastax/src/main/java/com/intuit/wasabi/repository/cassandra/accessor/ExperimentTagAccessor.java b/modules/repository-datastax/src/main/java/com/intuit/wasabi/repository/cassandra/accessor/ExperimentTagAccessor.java index 1f592e05b..2ef5927d2 100644 --- a/modules/repository-datastax/src/main/java/com/intuit/wasabi/repository/cassandra/accessor/ExperimentTagAccessor.java +++ b/modules/repository-datastax/src/main/java/com/intuit/wasabi/repository/cassandra/accessor/ExperimentTagAccessor.java @@ -34,7 +34,18 @@ public interface ExperimentTagAccessor { @Query("select * from experiment_tags where application IN ? ") Result getExperimentTags(Collection applicationNames); + @Query("select * from experiment_tags where application = ? ") + Result getExperimentTagsForApplication(Application.Name applicationName); + @Query("insert into experiment_tags(app_name, tags) values(?,?)") Statement insert(Application.Name appName, Set tags); + @Query("update experiment_tags set tags = tags + {?} WHERE application = ?;") + Statement update(Set tags, Application.Name appName); + + @Query("update experiment_tags set tags = tags - {?} WHERE application = ?;") + Statement remove(Set tags, Application.Name appName); + + @Query("delete tags from experiment_tags WHERE application = ?;") + Statement removeAll(Application.Name appName); } diff --git a/modules/repository-datastax/src/main/java/com/intuit/wasabi/repository/cassandra/impl/CassandraExperimentRepository.java b/modules/repository-datastax/src/main/java/com/intuit/wasabi/repository/cassandra/impl/CassandraExperimentRepository.java index fd012dfcd..1be026548 100644 --- a/modules/repository-datastax/src/main/java/com/intuit/wasabi/repository/cassandra/impl/CassandraExperimentRepository.java +++ b/modules/repository-datastax/src/main/java/com/intuit/wasabi/repository/cassandra/impl/CassandraExperimentRepository.java @@ -63,6 +63,8 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; +import java.util.TreeSet; import java.util.stream.Collectors; import static org.slf4j.LoggerFactory.getLogger; @@ -211,7 +213,8 @@ public CassandraExperimentRepository(CassandraDriver driver, BucketAuditLogAccessor bucketAuditLogAccessor, ExperimentAuditLogAccessor experimentAuditLogAccessor, StateExperimentIndexAccessor stateExperimentIndexAccessor, - ExperimentValidator validator) { + ExperimentValidator validator, + ExperimentTagAccessor experimentTagAccessor) { this.driver = driver; this.experimentAccessor = experimentAccessor; this.experimentLabelIndexAccessor = experimentLabelIndexAccessor; @@ -221,6 +224,7 @@ public CassandraExperimentRepository(CassandraDriver driver, this.bucketAuditLogAccessor = bucketAuditLogAccessor; this.experimentAuditLogAccessor = experimentAuditLogAccessor; this.validator = validator; + this.experimentTagAccessor = experimentTagAccessor; } /** @@ -393,10 +397,16 @@ public Experiment.ID createExperiment(NewExperiment newExperiment) { newExperiment.getModelVersion(), newExperiment.getIsRapidExperiment(), newExperiment.getUserCap(), - (newExperiment.getCreatorID() != null) ? newExperiment.getCreatorID() : ""); + (newExperiment.getCreatorID() != null) ? newExperiment.getCreatorID() : "", + newExperiment.getTags()); + // TODO - Do we need to create an application while creating a new experiment ? createApplication(newExperiment.getApplicationName()); + + // update tags + updateExperimentTags(newExperiment.getApplicationName(), newExperiment.getTags()); + } catch (Exception e) { LOGGER.error("Error while creating experiment {}", newExperiment, e); throw new RepositoryException("Exception while creating experiment " + newExperiment + " message " + e, e); @@ -405,6 +415,25 @@ public Experiment.ID createExperiment(NewExperiment newExperiment) { } + /** + * Updates the tags for a given Application. + * + * @param applicationName the Application for which the tags should be added + * @param tags list of tags for the experiment + */ + protected void updateExperimentTags(Application.Name applicationName, Set tags) { + if (tags != null && !tags.isEmpty()) { + // update the tag table, just rewrite the complete entry + List> listAllTags = experimentAccessor.getAllTags(applicationName).all(); + Set allTags = new TreeSet<>(); + allTags.addAll(tags); + for (Set tagsSet : listAllTags) { + allTags.addAll(tagsSet); + } + experimentTagAccessor.insert(applicationName, allTags); + } + } + /** * {@inheritDoc} */ @@ -523,8 +552,11 @@ public Experiment updateExperiment(Experiment experiment) { experiment.getModelVersion(), experiment.getIsRapidExperiment(), experiment.getUserCap(), + experiment.getTags(), experiment.getID().getRawID()); + updateExperimentTags(experiment.getApplicationName(), experiment.getTags()); + // Point the experiment index to this experiment updateExperimentLabelIndex(experiment.getID(), experiment.getApplicationName(), experiment.getLabel(), experiment.getStartTime(), experiment.getEndTime(), experiment.getState()); diff --git a/modules/repository-datastax/src/test/java/com/intuit/wasabi/repository/cassandra/accessor/ExperimentAccessorITest.java b/modules/repository-datastax/src/test/java/com/intuit/wasabi/repository/cassandra/accessor/ExperimentAccessorITest.java index 5786ae991..6162e8933 100644 --- a/modules/repository-datastax/src/test/java/com/intuit/wasabi/repository/cassandra/accessor/ExperimentAccessorITest.java +++ b/modules/repository-datastax/src/test/java/com/intuit/wasabi/repository/cassandra/accessor/ExperimentAccessorITest.java @@ -49,7 +49,7 @@ public void insertOneExperiments() { "d1", "yes", "r1", "", 1.0, date1, date2, com.intuit.wasabi.experimentobjects.Experiment.State.DRAFT.name(), "l1", "app1", date1, date2, true, - "m1", "v1", true, 5000, "c1"); + "m1", "v1", true, 5000, "c1", null); Result experiment1 = accessor.getExperimentById(experimentId1); List experimentResult = experiment1.all(); @@ -80,12 +80,12 @@ public void insertTwoExperiments() { "d1", "yes", "r1", "", 1.0, date1, date2, com.intuit.wasabi.experimentobjects.Experiment.State.DRAFT.name(), "l1", "app1", date1, date2, true, - "m1", "v1", true, 5000, "c1"); + "m1", "v1", true, 5000, "c1", null); accessor.insertExperiment(experimentId2, "d2", "no", "r2", "", 1.0, date1, date2, com.intuit.wasabi.experimentobjects.Experiment.State.DRAFT.name(), "l2", "app2", date1, date2, true, - "m2", "v2", true, 5000, "c2"); + "m2", "v2", true, 5000, "c2", null); List experimentIds = new ArrayList<>(); diff --git a/modules/repository-datastax/src/test/java/com/intuit/wasabi/repository/cassandra/impl/CassandraExperimentRepositoryTest.java b/modules/repository-datastax/src/test/java/com/intuit/wasabi/repository/cassandra/impl/CassandraExperimentRepositoryTest.java index 5066fa6e4..cbd9e2d29 100644 --- a/modules/repository-datastax/src/test/java/com/intuit/wasabi/repository/cassandra/impl/CassandraExperimentRepositoryTest.java +++ b/modules/repository-datastax/src/test/java/com/intuit/wasabi/repository/cassandra/impl/CassandraExperimentRepositoryTest.java @@ -36,6 +36,7 @@ import com.intuit.wasabi.repository.cassandra.accessor.ApplicationListAccessor; import com.intuit.wasabi.repository.cassandra.accessor.BucketAccessor; import com.intuit.wasabi.repository.cassandra.accessor.ExperimentAccessor; +import com.intuit.wasabi.repository.cassandra.accessor.ExperimentTagAccessor; import com.intuit.wasabi.repository.cassandra.accessor.audit.BucketAuditLogAccessor; import com.intuit.wasabi.repository.cassandra.accessor.audit.ExperimentAuditLogAccessor; import com.intuit.wasabi.repository.cassandra.accessor.index.ExperimentLabelIndexAccessor; @@ -45,11 +46,13 @@ import org.mockito.Mockito; import java.util.ArrayList; +import java.util.Arrays; import java.util.Date; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.TreeSet; import java.util.UUID; import java.util.concurrent.ExecutionException; @@ -59,6 +62,8 @@ import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; import static org.mockito.Mockito.when; public class CassandraExperimentRepositoryTest { @@ -86,6 +91,7 @@ public class CassandraExperimentRepositoryTest { private ExperimentAccessor mockExperimentAccessor; private ExperimentAuditLogAccessor mockExperimentAuditLogAccessor; + private ExperimentTagAccessor mockExperimentTagAccessor; private StateExperimentIndexAccessor mockStateExperimentIndexAccessor; @@ -102,6 +108,7 @@ public void setUp() throws Exception { mockExperimentAuditLogAccessor = Mockito.mock(ExperimentAuditLogAccessor.class); mockApplicationListAccessor = Mockito.mock(ApplicationListAccessor.class); mockExperimentLabelIndexAccessor = Mockito.mock(ExperimentLabelIndexAccessor.class); + mockExperimentTagAccessor = Mockito.mock(ExperimentTagAccessor.class); if (repository != null) return; @@ -114,7 +121,8 @@ public void setUp() throws Exception { mockDriver, mockExperimentAccessor, mockExperimentLabelIndexAccessor, mockBucketAccessor, mockApplicationListAccessor, mockBucketAuditLogAccessor, mockExperimentAuditLogAccessor, - mockStateExperimentIndexAccessor, new ExperimentValidator()); + mockStateExperimentIndexAccessor, new ExperimentValidator(), + mockExperimentTagAccessor); bucket1 = Bucket.newInstance(experimentID1, Bucket.Label.valueOf("bl1")).withAllocationPercent(.23) .withControl(true) .withDescription("b1").withPayload("p1") @@ -641,4 +649,42 @@ public void testGetBucketList() throws ExecutionException, InterruptedException assertThat(actualResultMap.get(expId1).getBuckets().get(0).getLabel().toString(), is("Test-Bucket-1")); } + @Test + public void testUpdateTags() { + //------ Input -------- + Application.Name appName = Application.Name.valueOf("AppName"); + Set tags = new TreeSet<>(Arrays.asList("tag1", "tag2", "tag3")); + + Set exp1 = new TreeSet<>(Arrays.asList("tag1", "tag3")); + Set exp2 = new TreeSet<>(Arrays.asList("tag4")); + + List> dbResultList = Arrays.asList(exp1, exp2); + + //------ Mocking interacting calls + Result> dbResult = mock(Result.class); + when(mockExperimentAccessor.getAllTags(appName)).thenReturn(dbResult); + when(dbResult.all()).thenReturn(dbResultList); + + //------ Make call + repository.updateExperimentTags(appName, tags); + + //------ Verify result + verify(mockExperimentTagAccessor).insert(appName, new TreeSet<>(Arrays.asList("tag1", "tag2", "tag3", "tag4"))); + + } + + @Test + public void testNoUpdateTags() { + //------ Input -------- + Application.Name appName = Application.Name.valueOf("AppName"); + Set tags = new TreeSet<>(); // if there are no tags we want no interaction with the db + + //------ Make call + repository.updateExperimentTags(appName, tags); + + //------ Verify result + verifyZeroInteractions(mockExperimentAccessor); + verifyZeroInteractions(mockExperimentTagAccessor); + + } } diff --git a/modules/repository-datastax/src/test/java/com/intuit/wasabi/repository/cassandra/impl/CassandraMutexRepositoryITest.java b/modules/repository-datastax/src/test/java/com/intuit/wasabi/repository/cassandra/impl/CassandraMutexRepositoryITest.java index ce0ecda38..2ebd4c022 100644 --- a/modules/repository-datastax/src/test/java/com/intuit/wasabi/repository/cassandra/impl/CassandraMutexRepositoryITest.java +++ b/modules/repository-datastax/src/test/java/com/intuit/wasabi/repository/cassandra/impl/CassandraMutexRepositoryITest.java @@ -78,12 +78,12 @@ public void testGetExclusionsSuccess() { "d1", "yes", "r1", "", 1.0, date1, date2, com.intuit.wasabi.experimentobjects.Experiment.State.DRAFT.name(), "l1", "app1", date1, date2, true, - "m1", "v1", true, 5000, "c1"); + "m1", "v1", true, 5000, "c1", null); experimentAccessor.insertExperiment(pair.getRawID(), "d2", "no", "r2", "", 1.0, date1, date2, com.intuit.wasabi.experimentobjects.Experiment.State.DRAFT.name(), "l2", "app2", date1, date2, true, - "m2", "v2", true, 5000, "c2"); + "m2", "v2", true, 5000, "c2", null); repository.createExclusion(base, pair); @@ -107,17 +107,17 @@ public void testGetExclusionsWithTwoExclusiosSuccess() { "d1", "yes", "r1", "", 1.0, date1, date2, com.intuit.wasabi.experimentobjects.Experiment.State.DRAFT.name(), "l1", "app1", date1, date2, true, - "m1", "v1", true, 5000, "c1"); + "m1", "v1", true, 5000, "c1", null); experimentAccessor.insertExperiment(pair1.getRawID(), "d2", "yes", "r2", "", 1.0, date1, date2, com.intuit.wasabi.experimentobjects.Experiment.State.DRAFT.name(), "l2", "app2", date1, date2, true, - "m2", "v2", true, 5000, "c2"); + "m2", "v2", true, 5000, "c2", null); experimentAccessor.insertExperiment(pair2.getRawID(), "d2", "yes", "r2", "", 1.0, date1, date2, com.intuit.wasabi.experimentobjects.Experiment.State.DRAFT.name(), "l2", "app2", date1, date2, true, - "m2", "v2", true, 5000, "c2"); + "m2", "v2", true, 5000, "c2", null); repository.createExclusion(base, pair1); repository.createExclusion(base, pair2); @@ -154,19 +154,19 @@ public void testGetNotExclusionsSuccess() { "d1", "yes", "r1", "", 1.0, date1, date2, com.intuit.wasabi.experimentobjects.Experiment.State.DRAFT.name(), "l1", appName, date1, date2, true, - "m1", "v1", true, 5000, "c1"); + "m1", "v1", true, 5000, "c1", null); experimentAccessor.insertExperiment(pair1.getRawID(), "d2", "yes", "r2", "", 1.0, date1, date2, com.intuit.wasabi.experimentobjects.Experiment.State.DRAFT.name(), "l2", appName, date1, date2, true, - "m2", "v2", true, 5000, "c2"); + "m2", "v2", true, 5000, "c2", null); experimentAccessor.insertExperiment(notExclusion.getRawID(), "d2", "yes", "r2", "", 1.0, date1, date2, com.intuit.wasabi.experimentobjects.Experiment.State.DRAFT.name(), "l2", appName, date1, date2, true, - "m2", "v2", true, 5000, "c2"); + "m2", "v2", true, 5000, "c2", null); repository.createExclusion(base, pair1); diff --git a/modules/repository-datastax/src/test/java/com/intuit/wasabi/repository/cassandra/impl/CassandraPrioritiesRepositoryITest.java b/modules/repository-datastax/src/test/java/com/intuit/wasabi/repository/cassandra/impl/CassandraPrioritiesRepositoryITest.java index a999674d6..55cd34bab 100644 --- a/modules/repository-datastax/src/test/java/com/intuit/wasabi/repository/cassandra/impl/CassandraPrioritiesRepositoryITest.java +++ b/modules/repository-datastax/src/test/java/com/intuit/wasabi/repository/cassandra/impl/CassandraPrioritiesRepositoryITest.java @@ -83,13 +83,13 @@ public void testTwoGetPrioritiesSuccess() { "d1", "yes", "r1", "", 1.0, date1, date2, com.intuit.wasabi.experimentobjects.Experiment.State.DRAFT.name(), "l1", applicationName.toString(), date1, date2, true, - "m1", "v1", true, 5000, "c1"); + "m1", "v1", true, 5000, "c1", null); experimentAccessor.insertExperiment(experimentId2, "d2", "yes", "r2", "", 1.0, date1, date2, com.intuit.wasabi.experimentobjects.Experiment.State.DRAFT.name(), "l2", applicationName.toString(), date1, date2, true, - "m2", "v2", true, 5000, "c2"); + "m2", "v2", true, 5000, "c2", null); List priorityIds = new ArrayList<>(); priorityIds.add(experimentId1); From e12908dce5fb2da789ac0941ce601cfb0ae60ef5 Mon Sep 17 00:00:00 2001 From: Andrea Date: Tue, 11 Apr 2017 17:28:41 +0200 Subject: [PATCH 04/43] adding tags to the filtering logic --- .../wrapper/ExperimentDetail.java | 18 ++++++++++++++++-- .../wrapper/ExperimentDetailTest.java | 16 ++++++++++------ .../filters/impl/ExperimentDetailFilter.java | 4 +++- .../filters/impl/ExperimentFilter.java | 4 +++- .../impl/ExperimentDetailFilterTest.java | 5 ++++- 5 files changed, 36 insertions(+), 11 deletions(-) diff --git a/modules/analytics-objects/src/main/java/com/intuit/wasabi/analyticsobjects/wrapper/ExperimentDetail.java b/modules/analytics-objects/src/main/java/com/intuit/wasabi/analyticsobjects/wrapper/ExperimentDetail.java index 91dfd32ac..cc03cf8bf 100644 --- a/modules/analytics-objects/src/main/java/com/intuit/wasabi/analyticsobjects/wrapper/ExperimentDetail.java +++ b/modules/analytics-objects/src/main/java/com/intuit/wasabi/analyticsobjects/wrapper/ExperimentDetail.java @@ -24,6 +24,7 @@ import java.util.Date; import java.util.List; +import java.util.Set; import java.util.stream.Collectors; import static org.apache.commons.lang3.StringUtils.isEmpty; @@ -56,6 +57,8 @@ public class ExperimentDetail { private String description; + private Set tags; + /** * This class holds the details for the Buckets. This is especially interesting for @@ -209,7 +212,8 @@ public void setDescription(String description) { */ public ExperimentDetail(Experiment exp) { this(exp.getID(), exp.getState(), exp.getLabel(), exp.getApplicationName(), - exp.getModificationTime(), exp.getStartTime(), exp.getEndTime(), exp.getDescription()); + exp.getModificationTime(), exp.getStartTime(), exp.getEndTime(), exp.getDescription(), + exp.getTags()); } /** @@ -223,10 +227,11 @@ public ExperimentDetail(Experiment exp) { * @param startTime the startTime of the experiment to determine the winner so far * @param endTime the endtime of the experiment * @param description the description of the experiment + * @param tags the tags of the experiment */ public ExperimentDetail(Experiment.ID id, Experiment.State state, Experiment.Label label, Application.Name appName, Date modificationTime, Date startTime, - Date endTime, String description) { + Date endTime, String description, Set tags) { setId(id); setState(state); setLabel(label); @@ -235,6 +240,7 @@ public ExperimentDetail(Experiment.ID id, Experiment.State state, Experiment.Lab setStartTime(startTime); setEndTime(endTime); setDescription(description); + setTags(tags); } public Experiment.ID getId() { @@ -337,6 +343,14 @@ public void setDescription(String description) { this.description = description; } + public Set getTags() { + return tags; + } + + public void setTags(Set tags) { + this.tags = tags; + } + /** * This method takes a list of buckets and transforms it to the {@link BucketDetail}s that are needed * for later extension. diff --git a/modules/analytics-objects/src/test/java/com/intuit/wasabi/analyticsobjects/wrapper/ExperimentDetailTest.java b/modules/analytics-objects/src/test/java/com/intuit/wasabi/analyticsobjects/wrapper/ExperimentDetailTest.java index a36754fbe..fcd5a2fcc 100644 --- a/modules/analytics-objects/src/test/java/com/intuit/wasabi/analyticsobjects/wrapper/ExperimentDetailTest.java +++ b/modules/analytics-objects/src/test/java/com/intuit/wasabi/analyticsobjects/wrapper/ExperimentDetailTest.java @@ -24,6 +24,8 @@ import java.util.ArrayList; import java.util.Calendar; import java.util.List; +import java.util.Set; +import java.util.TreeSet; import java.util.stream.Collectors; import static org.junit.Assert.assertEquals; @@ -43,6 +45,7 @@ public class ExperimentDetailTest { private static Calendar modTime = Calendar.getInstance(); private static Calendar startTime = Calendar.getInstance(); private static Calendar endTime = Calendar.getInstance(); + private static Set tags = new TreeSet<>(java.util.Arrays.asList("tag1", "tag2", "tag3")); private Experiment exp = Experiment.withID(expId).withApplicationName(appName).withModificationTime(modTime.getTime()) @@ -58,7 +61,7 @@ public static void setUp() { @Test public void testConstructor() { ExperimentDetail expDetail = new ExperimentDetail(expId, expState, expLabel, appName, modTime.getTime(), - startTime.getTime(), endTime.getTime(), description); + startTime.getTime(), endTime.getTime(), description, tags); assertEquals(expDetail.getApplicationName(), appName); assertEquals(expDetail.getId(), expId); assertEquals(expDetail.getLabel(), expLabel); @@ -66,36 +69,37 @@ public void testConstructor() { assertEquals(expDetail.getModificationTime(), modTime.getTime()); assertEquals(expDetail.getEndTime(), endTime.getTime()); assertEquals(expDetail.getDescription(), description); + assertEquals(expDetail.getTags(), tags); } @Test(expected = IllegalArgumentException.class) public void testConstraintsId() { new ExperimentDetail(null, expState, expLabel, appName, modTime.getTime(), - startTime.getTime(), endTime.getTime(), description); + startTime.getTime(), endTime.getTime(), description, tags); } @Test(expected = IllegalArgumentException.class) public void testConstraintsState() { new ExperimentDetail(expId, null, expLabel, appName, modTime.getTime(), - startTime.getTime(), endTime.getTime(), description); + startTime.getTime(), endTime.getTime(), description, tags); } @Test(expected = IllegalArgumentException.class) public void testConstraintsStateDeleted() { new ExperimentDetail(expId, Experiment.State.DELETED, expLabel, appName, modTime.getTime(), - startTime.getTime(), endTime.getTime(), description); + startTime.getTime(), endTime.getTime(), description, tags); } @Test(expected = IllegalArgumentException.class) public void testConstraintsLabel() { new ExperimentDetail(expId, expState, null, appName, modTime.getTime(), - startTime.getTime(), endTime.getTime(), description); + startTime.getTime(), endTime.getTime(), description, tags); } @Test(expected = IllegalArgumentException.class) public void testConstraintsAppName() { new ExperimentDetail(expId, expState, expLabel, null, modTime.getTime(), - startTime.getTime(), endTime.getTime(), description); + startTime.getTime(), endTime.getTime(), description, tags); } @Test diff --git a/modules/api/src/main/java/com/intuit/wasabi/api/pagination/filters/impl/ExperimentDetailFilter.java b/modules/api/src/main/java/com/intuit/wasabi/api/pagination/filters/impl/ExperimentDetailFilter.java index 5d811e9e0..4d0ba7985 100644 --- a/modules/api/src/main/java/com/intuit/wasabi/api/pagination/filters/impl/ExperimentDetailFilter.java +++ b/modules/api/src/main/java/com/intuit/wasabi/api/pagination/filters/impl/ExperimentDetailFilter.java @@ -68,7 +68,9 @@ public enum Property implements PaginationFilterProperty { start_time(ExperimentDetail::getStartTime, FilterUtil::extractTimeZoneAndTestDate), end_time(ExperimentDetail::getEndTime, FilterUtil::extractTimeZoneAndTestDate), date_constraint_start(ExperimentDetail::getStartTime, ExperimentFilter::constraintTest), - date_constraint_end(ExperimentDetail::getEndTime, ExperimentFilter::constraintTest); + date_constraint_end(ExperimentDetail::getEndTime, ExperimentFilter::constraintTest), + tags(ExperimentDetail::getTags, (tagsSet, filter) -> tagsSet.stream().anyMatch(tag + -> StringUtils.containsIgnoreCase(tag, filter))); private final Function propertyExtractor; private final BiPredicate filterPredicate; diff --git a/modules/api/src/main/java/com/intuit/wasabi/api/pagination/filters/impl/ExperimentFilter.java b/modules/api/src/main/java/com/intuit/wasabi/api/pagination/filters/impl/ExperimentFilter.java index 5c55a87ae..ae0c37094 100644 --- a/modules/api/src/main/java/com/intuit/wasabi/api/pagination/filters/impl/ExperimentFilter.java +++ b/modules/api/src/main/java/com/intuit/wasabi/api/pagination/filters/impl/ExperimentFilter.java @@ -73,7 +73,9 @@ public enum Property implements PaginationFilterProperty { state_exact(Experiment::getState, ExperimentFilter::stateTest), date_constraint_start(Experiment::getStartTime, ExperimentFilter::constraintTest), date_constraint_end(Experiment::getEndTime, ExperimentFilter::constraintTest), - favorite(Experiment::isFavorite, (isFavorite, filter) -> Boolean.parseBoolean(filter) == isFavorite); + favorite(Experiment::isFavorite, (isFavorite, filter) -> Boolean.parseBoolean(filter) == isFavorite), + tags(Experiment::getTags, (tagsSet, filter) -> tagsSet.stream().anyMatch(tag + -> StringUtils.containsIgnoreCase(tag, filter))); private final Function propertyExtractor; private final BiPredicate filterPredicate; diff --git a/modules/api/src/test/java/com/intuit/wasabi/api/pagination/filters/impl/ExperimentDetailFilterTest.java b/modules/api/src/test/java/com/intuit/wasabi/api/pagination/filters/impl/ExperimentDetailFilterTest.java index dd8996596..b207617ce 100644 --- a/modules/api/src/test/java/com/intuit/wasabi/api/pagination/filters/impl/ExperimentDetailFilterTest.java +++ b/modules/api/src/test/java/com/intuit/wasabi/api/pagination/filters/impl/ExperimentDetailFilterTest.java @@ -31,7 +31,9 @@ import java.util.Collection; import java.util.Date; import java.util.List; +import java.util.Set; import java.util.TimeZone; +import java.util.TreeSet; /** @@ -54,9 +56,10 @@ public void setup() { Date startTime = cal.getTime(); cal.add(Calendar.DATE, 14); Date endTime = cal.getTime(); + Set tags = new TreeSet<>(java.util.Arrays.asList("tag1", "tag2", "tag3")); experimentDetail = new ExperimentDetail(expId, Experiment.State.RUNNING, expLabel, appName, - modTime, startTime, endTime, "testDescription"); + modTime, startTime, endTime, "testDescription", tags); Bucket b1 = Bucket.newInstance(expId, Bucket.Label.valueOf("Bucket1")).withAllocationPercent(0.6).build(); Bucket b2 = Bucket.newInstance(expId, Bucket.Label.valueOf("Bucket2")).withAllocationPercent(0.4).build(); From 35c0ab599003e39cd8fc19c9624df7520b6d0497 Mon Sep 17 00:00:00 2001 From: Andrea Date: Wed, 12 Apr 2017 22:07:40 +0200 Subject: [PATCH 05/43] adding accessor object for tags and returning them for experiments call --- .../cassandra/CassandraRepositoryModule.java | 4 +- .../accessor/ExperimentAccessor.java | 7 ++-- .../accessor/ExperimentTagAccessor.java | 15 +++---- .../impl/CassandraExperimentRepository.java | 6 +-- .../cassandra/impl/ExperimentHelper.java | 1 + .../repository/cassandra/pojo/Experiment.java | 4 ++ .../index/ExperimentTagsByApplication.java | 2 +- .../ExperimentTagAccessorProvider.java | 41 +++++++++++++++++++ .../CassandraExperimentRepositoryTest.java | 9 +++- 9 files changed, 70 insertions(+), 19 deletions(-) create mode 100644 modules/repository-datastax/src/main/java/com/intuit/wasabi/repository/cassandra/provider/ExperimentTagAccessorProvider.java diff --git a/modules/repository-datastax/src/main/java/com/intuit/wasabi/repository/cassandra/CassandraRepositoryModule.java b/modules/repository-datastax/src/main/java/com/intuit/wasabi/repository/cassandra/CassandraRepositoryModule.java index f1803b0c6..486a31628 100644 --- a/modules/repository-datastax/src/main/java/com/intuit/wasabi/repository/cassandra/CassandraRepositoryModule.java +++ b/modules/repository-datastax/src/main/java/com/intuit/wasabi/repository/cassandra/CassandraRepositoryModule.java @@ -35,6 +35,7 @@ import com.intuit.wasabi.repository.cassandra.accessor.ExclusionAccessor; import com.intuit.wasabi.repository.cassandra.accessor.ExperimentAccessor; import com.intuit.wasabi.repository.cassandra.accessor.ExperimentPageAccessor; +import com.intuit.wasabi.repository.cassandra.accessor.ExperimentTagAccessor; import com.intuit.wasabi.repository.cassandra.accessor.PrioritiesAccessor; import com.intuit.wasabi.repository.cassandra.accessor.StagingAccessor; import com.intuit.wasabi.repository.cassandra.accessor.UserFeedbackAccessor; @@ -65,6 +66,7 @@ import com.intuit.wasabi.repository.cassandra.provider.ExclusionAccessorProvider; import com.intuit.wasabi.repository.cassandra.provider.ExperimentAccessorProvider; import com.intuit.wasabi.repository.cassandra.provider.ExperimentPageAccessorProvider; +import com.intuit.wasabi.repository.cassandra.provider.ExperimentTagAccessorProvider; import com.intuit.wasabi.repository.cassandra.provider.MappingManagerProvider; import com.intuit.wasabi.repository.cassandra.provider.PrioritiesAccessorProvider; import com.intuit.wasabi.repository.cassandra.provider.StagingAccessorProvider; @@ -90,7 +92,6 @@ import static com.google.inject.name.Names.named; import static com.intuit.autumn.utils.PropertyFactory.create; import static com.intuit.autumn.utils.PropertyFactory.getProperty; -import static java.lang.Boolean.TRUE; import static java.lang.Integer.parseInt; import static org.slf4j.LoggerFactory.getLogger; @@ -137,6 +138,7 @@ protected void configure() { bind(UserFeedbackAccessor.class).toProvider(UserFeedbackAccessorProvider.class).in(Singleton.class); bind(UserInfoAccessor.class).toProvider(UserInfoAccessorProvider.class).in(Singleton.class); bind(UserRoleAccessor.class).toProvider(UserRoleAccessorProvider.class).in(Singleton.class); + bind(ExperimentTagAccessor.class).toProvider(ExperimentTagAccessorProvider.class).in(Singleton.class); //Bind those indexes bind(AppPageIndexAccessor.class).toProvider(AppPageIndexAccessorProvider.class).in(Singleton.class); diff --git a/modules/repository-datastax/src/main/java/com/intuit/wasabi/repository/cassandra/accessor/ExperimentAccessor.java b/modules/repository-datastax/src/main/java/com/intuit/wasabi/repository/cassandra/accessor/ExperimentAccessor.java index 144ecb0fd..f394f40aa 100644 --- a/modules/repository-datastax/src/main/java/com/intuit/wasabi/repository/cassandra/accessor/ExperimentAccessor.java +++ b/modules/repository-datastax/src/main/java/com/intuit/wasabi/repository/cassandra/accessor/ExperimentAccessor.java @@ -22,6 +22,7 @@ import com.google.common.util.concurrent.ListenableFuture; import com.intuit.wasabi.experimentobjects.Application; import com.intuit.wasabi.repository.cassandra.pojo.Experiment; +import com.intuit.wasabi.repository.cassandra.pojo.index.ExperimentTagsByApplication; import java.util.Date; import java.util.List; @@ -55,7 +56,7 @@ public interface ExperimentAccessor { "rule = ?, sample_percent = ?, " + "start_time = ?, end_time = ?, " + "state=?, label=?, app_name=?, modified=? , is_personalized=?, model_name=?, model_version=?," + - " is_rapid_experiment=?, user_cap=?, tags = ?" + + " is_rapid_experiment=?, user_cap=?, tags=?" + " where id = ?") ResultSet updateExperiment(String description, String hypothesisIsCorrect, String results, String rule, double sample_percent, @@ -78,7 +79,7 @@ ResultSet updateExperiment(String description, String hypothesisIsCorrect, Strin "(id, description, hypothesis_is_correct, results, rule, sample_percent, start_time, end_time, " + " state, label, app_name, created, modified, is_personalized, model_name, model_version," + " is_rapid_experiment, user_cap, creatorid, tags) " + - "values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)") + "values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)") void insertExperiment(UUID experimentId, String description, String hypothesisIsCorrect, String results, String rule, double samplePercent, Date startTime, Date endTime, String state, String label, String appName, @@ -87,5 +88,5 @@ void insertExperiment(UUID experimentId, String description, String hypothesisIs Set tags); @Query("select tags from experiment where app_name = ?;") - Result> getAllTags(Application.Name appName); + Result getAllTags(Application.Name appName); } diff --git a/modules/repository-datastax/src/main/java/com/intuit/wasabi/repository/cassandra/accessor/ExperimentTagAccessor.java b/modules/repository-datastax/src/main/java/com/intuit/wasabi/repository/cassandra/accessor/ExperimentTagAccessor.java index 2ef5927d2..1fe37c2e5 100644 --- a/modules/repository-datastax/src/main/java/com/intuit/wasabi/repository/cassandra/accessor/ExperimentTagAccessor.java +++ b/modules/repository-datastax/src/main/java/com/intuit/wasabi/repository/cassandra/accessor/ExperimentTagAccessor.java @@ -31,21 +31,18 @@ @Accessor public interface ExperimentTagAccessor { - @Query("select * from experiment_tags where application IN ? ") + @Query("select * from experiment_tag where app_name IN ? ") Result getExperimentTags(Collection applicationNames); - - @Query("select * from experiment_tags where application = ? ") - Result getExperimentTagsForApplication(Application.Name applicationName); - - @Query("insert into experiment_tags(app_name, tags) values(?,?)") + + @Query("insert into experiment_tag(app_name, tags) values(?,?)") Statement insert(Application.Name appName, Set tags); - @Query("update experiment_tags set tags = tags + {?} WHERE application = ?;") + @Query("update experiment_tag set tags = tags + ? WHERE app_name = ?;") Statement update(Set tags, Application.Name appName); - @Query("update experiment_tags set tags = tags - {?} WHERE application = ?;") + @Query("update experiment_tag set tags = tags - ? WHERE app_name = ?;") Statement remove(Set tags, Application.Name appName); - @Query("delete tags from experiment_tags WHERE application = ?;") + @Query("delete tags from experiment_tag WHERE app_name = ?;") Statement removeAll(Application.Name appName); } diff --git a/modules/repository-datastax/src/main/java/com/intuit/wasabi/repository/cassandra/impl/CassandraExperimentRepository.java b/modules/repository-datastax/src/main/java/com/intuit/wasabi/repository/cassandra/impl/CassandraExperimentRepository.java index 1be026548..68b99edd2 100644 --- a/modules/repository-datastax/src/main/java/com/intuit/wasabi/repository/cassandra/impl/CassandraExperimentRepository.java +++ b/modules/repository-datastax/src/main/java/com/intuit/wasabi/repository/cassandra/impl/CassandraExperimentRepository.java @@ -424,11 +424,11 @@ public Experiment.ID createExperiment(NewExperiment newExperiment) { protected void updateExperimentTags(Application.Name applicationName, Set tags) { if (tags != null && !tags.isEmpty()) { // update the tag table, just rewrite the complete entry - List> listAllTags = experimentAccessor.getAllTags(applicationName).all(); + List listAllTags = experimentAccessor.getAllTags(applicationName).all(); Set allTags = new TreeSet<>(); allTags.addAll(tags); - for (Set tagsSet : listAllTags) { - allTags.addAll(tagsSet); + for (ExperimentTagsByApplication tagsSet : listAllTags) { + allTags.addAll(tagsSet.getTags()); } experimentTagAccessor.insert(applicationName, allTags); } diff --git a/modules/repository-datastax/src/main/java/com/intuit/wasabi/repository/cassandra/impl/ExperimentHelper.java b/modules/repository-datastax/src/main/java/com/intuit/wasabi/repository/cassandra/impl/ExperimentHelper.java index fef9bed37..604ce621a 100644 --- a/modules/repository-datastax/src/main/java/com/intuit/wasabi/repository/cassandra/impl/ExperimentHelper.java +++ b/modules/repository-datastax/src/main/java/com/intuit/wasabi/repository/cassandra/impl/ExperimentHelper.java @@ -83,6 +83,7 @@ public static Experiment makeExperiment( experimentObject.setRuleJson(convertRuleToJson(experimentPojo.getRule())); experimentObject.setHypothesisIsCorrect(experimentPojo.getHypothesisIsCorrect()); experimentObject.setResults(experimentPojo.getResults()); + experimentObject.setTags(experimentPojo.getTags()); return experimentObject; } diff --git a/modules/repository-datastax/src/main/java/com/intuit/wasabi/repository/cassandra/pojo/Experiment.java b/modules/repository-datastax/src/main/java/com/intuit/wasabi/repository/cassandra/pojo/Experiment.java index 7c53014c2..245a6244a 100644 --- a/modules/repository-datastax/src/main/java/com/intuit/wasabi/repository/cassandra/pojo/Experiment.java +++ b/modules/repository-datastax/src/main/java/com/intuit/wasabi/repository/cassandra/pojo/Experiment.java @@ -24,6 +24,7 @@ import lombok.NoArgsConstructor; import java.util.Date; +import java.util.Set; import java.util.UUID; /***** @@ -85,6 +86,9 @@ public class Experiment { @Column(name = "creatorid") private String creatorId; + @Column(name = "tags") + private Set tags; + @Column(name = "hypothesis_is_correct") private String hypothesisIsCorrect; diff --git a/modules/repository-datastax/src/main/java/com/intuit/wasabi/repository/cassandra/pojo/index/ExperimentTagsByApplication.java b/modules/repository-datastax/src/main/java/com/intuit/wasabi/repository/cassandra/pojo/index/ExperimentTagsByApplication.java index a1bee88c4..9c9c6f3bf 100644 --- a/modules/repository-datastax/src/main/java/com/intuit/wasabi/repository/cassandra/pojo/index/ExperimentTagsByApplication.java +++ b/modules/repository-datastax/src/main/java/com/intuit/wasabi/repository/cassandra/pojo/index/ExperimentTagsByApplication.java @@ -25,7 +25,7 @@ import java.util.Collection; -@Table(name = "experiment_tags") +@Table(name = "experiment_tag") @Data @Builder(toBuilder = true) @NoArgsConstructor diff --git a/modules/repository-datastax/src/main/java/com/intuit/wasabi/repository/cassandra/provider/ExperimentTagAccessorProvider.java b/modules/repository-datastax/src/main/java/com/intuit/wasabi/repository/cassandra/provider/ExperimentTagAccessorProvider.java new file mode 100644 index 000000000..b7ab01d86 --- /dev/null +++ b/modules/repository-datastax/src/main/java/com/intuit/wasabi/repository/cassandra/provider/ExperimentTagAccessorProvider.java @@ -0,0 +1,41 @@ +/******************************************************************************* + * Copyright 2017 Intuit + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *******************************************************************************/ +package com.intuit.wasabi.repository.cassandra.provider; + +import com.datastax.driver.core.Session; +import com.datastax.driver.mapping.MappingManager; +import com.google.inject.Inject; +import com.google.inject.Provider; +import com.intuit.wasabi.cassandra.datastax.CassandraDriver; +import com.intuit.wasabi.repository.cassandra.accessor.ExperimentTagAccessor; + + +public class ExperimentTagAccessorProvider implements Provider { + private final Session session; + private final MappingManager manager; + + @Inject + public ExperimentTagAccessorProvider(CassandraDriver driver) { + this.session = driver.getSession(); + this.manager = new MappingManager(session); + } + + + @Override + public ExperimentTagAccessor get() { + return manager.createAccessor(ExperimentTagAccessor.class); + } +} \ No newline at end of file diff --git a/modules/repository-datastax/src/test/java/com/intuit/wasabi/repository/cassandra/impl/CassandraExperimentRepositoryTest.java b/modules/repository-datastax/src/test/java/com/intuit/wasabi/repository/cassandra/impl/CassandraExperimentRepositoryTest.java index cbd9e2d29..d8b185ee4 100644 --- a/modules/repository-datastax/src/test/java/com/intuit/wasabi/repository/cassandra/impl/CassandraExperimentRepositoryTest.java +++ b/modules/repository-datastax/src/test/java/com/intuit/wasabi/repository/cassandra/impl/CassandraExperimentRepositoryTest.java @@ -41,6 +41,7 @@ import com.intuit.wasabi.repository.cassandra.accessor.audit.ExperimentAuditLogAccessor; import com.intuit.wasabi.repository.cassandra.accessor.index.ExperimentLabelIndexAccessor; import com.intuit.wasabi.repository.cassandra.accessor.index.StateExperimentIndexAccessor; +import com.intuit.wasabi.repository.cassandra.pojo.index.ExperimentTagsByApplication; import org.junit.Before; import org.junit.Test; import org.mockito.Mockito; @@ -657,11 +658,15 @@ public void testUpdateTags() { Set exp1 = new TreeSet<>(Arrays.asList("tag1", "tag3")); Set exp2 = new TreeSet<>(Arrays.asList("tag4")); + ExperimentTagsByApplication exp2Tags = ExperimentTagsByApplication.builder() + .tags(exp2).appName(appName.toString()).build(); + ExperimentTagsByApplication exp1Tags = ExperimentTagsByApplication.builder() + .tags(exp1).appName(appName.toString()).build(); - List> dbResultList = Arrays.asList(exp1, exp2); + List dbResultList = Arrays.asList(exp1Tags, exp2Tags); //------ Mocking interacting calls - Result> dbResult = mock(Result.class); + Result dbResult = mock(Result.class); when(mockExperimentAccessor.getAllTags(appName)).thenReturn(dbResult); when(dbResult.all()).thenReturn(dbResultList); From 9deecb3e67a50d3801f4851cd72e0788a06bf66d Mon Sep 17 00:00:00 2001 From: Andrea Date: Wed, 12 Apr 2017 22:38:23 +0200 Subject: [PATCH 06/43] changing collection type to set --- .../java/com/intuit/wasabi/api/ApplicationsResource.java | 3 +-- .../java/com/intuit/wasabi/experiment/Experiments.java | 3 ++- .../com/intuit/wasabi/experiment/impl/ExperimentsImpl.java | 3 ++- .../com/intuit/wasabi/repository/ExperimentRepository.java | 3 ++- .../cassandra/accessor/ExperimentTagAccessor.java | 6 +++--- .../cassandra/impl/CassandraExperimentRepository.java | 7 ++++--- .../cassandra/pojo/index/ExperimentTagsByApplication.java | 4 ++-- .../repository/database/DatabaseExperimentRepository.java | 3 ++- 8 files changed, 18 insertions(+), 14 deletions(-) diff --git a/modules/api/src/main/java/com/intuit/wasabi/api/ApplicationsResource.java b/modules/api/src/main/java/com/intuit/wasabi/api/ApplicationsResource.java index 9a06391c1..93c304c6c 100644 --- a/modules/api/src/main/java/com/intuit/wasabi/api/ApplicationsResource.java +++ b/modules/api/src/main/java/com/intuit/wasabi/api/ApplicationsResource.java @@ -44,7 +44,6 @@ import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.core.Response; -import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -412,7 +411,7 @@ public Response getAllExperimentTags( } // get their associated tags - Map> allTags = experiments.getTagsForApplications(allowed); + Map> allTags = experiments.getTagsForApplications(allowed); return httpHeader.headers().entity(allTags).build(); } catch (Exception exception) { diff --git a/modules/experiment/src/main/java/com/intuit/wasabi/experiment/Experiments.java b/modules/experiment/src/main/java/com/intuit/wasabi/experiment/Experiments.java index f080c7435..12d69886c 100644 --- a/modules/experiment/src/main/java/com/intuit/wasabi/experiment/Experiments.java +++ b/modules/experiment/src/main/java/com/intuit/wasabi/experiment/Experiments.java @@ -25,6 +25,7 @@ import java.util.Collection; import java.util.List; import java.util.Map; +import java.util.Set; /** * Interface to perform CRUD operations on experiment. In addition, it also @@ -166,5 +167,5 @@ boolean buildUpdatedExperiment(Experiment experiment, Experiment updates, Experi * tags should be retrieved * @return a {@link Map} containing the Applications and their tags */ - Map> getTagsForApplications(Collection applicationNames); + Map> getTagsForApplications(Collection applicationNames); } diff --git a/modules/experiment/src/main/java/com/intuit/wasabi/experiment/impl/ExperimentsImpl.java b/modules/experiment/src/main/java/com/intuit/wasabi/experiment/impl/ExperimentsImpl.java index ce0d5cad4..713ddd80e 100644 --- a/modules/experiment/src/main/java/com/intuit/wasabi/experiment/impl/ExperimentsImpl.java +++ b/modules/experiment/src/main/java/com/intuit/wasabi/experiment/impl/ExperimentsImpl.java @@ -46,6 +46,7 @@ import java.util.Date; import java.util.List; import java.util.Map; +import java.util.Set; import static com.intuit.wasabi.experimentobjects.Experiment.State.DELETED; import static com.intuit.wasabi.experimentobjects.Experiment.State.DRAFT; @@ -111,7 +112,7 @@ public List getApplications() { * {@inheritDoc} */ @Override - public Map> getTagsForApplications(Collection applicationNames) { + public Map> getTagsForApplications(Collection applicationNames) { return cassandraRepository.getTagListForApplications(applicationNames); } diff --git a/modules/repository-datastax/src/main/java/com/intuit/wasabi/repository/ExperimentRepository.java b/modules/repository-datastax/src/main/java/com/intuit/wasabi/repository/ExperimentRepository.java index 6335da0b4..a0eb4a83d 100644 --- a/modules/repository-datastax/src/main/java/com/intuit/wasabi/repository/ExperimentRepository.java +++ b/modules/repository-datastax/src/main/java/com/intuit/wasabi/repository/ExperimentRepository.java @@ -31,6 +31,7 @@ import java.util.Collection; import java.util.List; import java.util.Map; +import java.util.Set; /** * Mid-level interface for the experiments repository @@ -271,6 +272,6 @@ void logBucketChanges(Experiment.ID experimentID, Bucket.Label bucketLabel, * @param applicationNames the list of {@link Application.Name}s the tags should be retrieved for * @return a Map of {@link Application.Name}s to their tags */ - Map> getTagListForApplications(Collection applicationNames); + Map> getTagListForApplications(Collection applicationNames); } diff --git a/modules/repository-datastax/src/main/java/com/intuit/wasabi/repository/cassandra/accessor/ExperimentTagAccessor.java b/modules/repository-datastax/src/main/java/com/intuit/wasabi/repository/cassandra/accessor/ExperimentTagAccessor.java index 1fe37c2e5..60900aab8 100644 --- a/modules/repository-datastax/src/main/java/com/intuit/wasabi/repository/cassandra/accessor/ExperimentTagAccessor.java +++ b/modules/repository-datastax/src/main/java/com/intuit/wasabi/repository/cassandra/accessor/ExperimentTagAccessor.java @@ -22,7 +22,7 @@ import com.intuit.wasabi.experimentobjects.Application; import com.intuit.wasabi.repository.cassandra.pojo.index.ExperimentTagsByApplication; -import java.util.Collection; +import java.util.List; import java.util.Set; /** @@ -32,8 +32,8 @@ public interface ExperimentTagAccessor { @Query("select * from experiment_tag where app_name IN ? ") - Result getExperimentTags(Collection applicationNames); - + Result getExperimentTags(List applicationNames); + @Query("insert into experiment_tag(app_name, tags) values(?,?)") Statement insert(Application.Name appName, Set tags); diff --git a/modules/repository-datastax/src/main/java/com/intuit/wasabi/repository/cassandra/impl/CassandraExperimentRepository.java b/modules/repository-datastax/src/main/java/com/intuit/wasabi/repository/cassandra/impl/CassandraExperimentRepository.java index 68b99edd2..bc0a3999a 100644 --- a/modules/repository-datastax/src/main/java/com/intuit/wasabi/repository/cassandra/impl/CassandraExperimentRepository.java +++ b/modules/repository-datastax/src/main/java/com/intuit/wasabi/repository/cassandra/impl/CassandraExperimentRepository.java @@ -63,6 +63,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; import java.util.TreeSet; import java.util.stream.Collectors; @@ -1234,13 +1235,13 @@ public void createApplication(Application.Name applicationName) { * {@inheritDoc} */ @Override - public Map> getTagListForApplications(Collection applicationNames) { + public Map> getTagListForApplications(Collection applicationNames) { LOGGER.debug("Retrieving Experiment Tags for applications {}", applicationNames); try { - - Map> result = experimentTagAccessor.getExperimentTags(applicationNames) + Map> result = experimentTagAccessor.getExperimentTags( + applicationNames.stream().map(appName -> Objects.toString(appName)).collect(Collectors.toList())) .all().stream().collect(Collectors.toMap(exp -> Application.Name.valueOf(exp.getAppName()), ExperimentTagsByApplication::getTags)); diff --git a/modules/repository-datastax/src/main/java/com/intuit/wasabi/repository/cassandra/pojo/index/ExperimentTagsByApplication.java b/modules/repository-datastax/src/main/java/com/intuit/wasabi/repository/cassandra/pojo/index/ExperimentTagsByApplication.java index 9c9c6f3bf..a490b4bee 100644 --- a/modules/repository-datastax/src/main/java/com/intuit/wasabi/repository/cassandra/pojo/index/ExperimentTagsByApplication.java +++ b/modules/repository-datastax/src/main/java/com/intuit/wasabi/repository/cassandra/pojo/index/ExperimentTagsByApplication.java @@ -23,7 +23,7 @@ import lombok.Data; import lombok.NoArgsConstructor; -import java.util.Collection; +import java.util.Set; @Table(name = "experiment_tag") @Data @@ -37,5 +37,5 @@ public class ExperimentTagsByApplication { String appName; @Column(name = "tags") - Collection tags; + Set tags; } diff --git a/modules/repository-datastax/src/main/java/com/intuit/wasabi/repository/database/DatabaseExperimentRepository.java b/modules/repository-datastax/src/main/java/com/intuit/wasabi/repository/database/DatabaseExperimentRepository.java index e22e6eabc..559f7bc19 100644 --- a/modules/repository-datastax/src/main/java/com/intuit/wasabi/repository/database/DatabaseExperimentRepository.java +++ b/modules/repository-datastax/src/main/java/com/intuit/wasabi/repository/database/DatabaseExperimentRepository.java @@ -41,6 +41,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; import static com.intuit.wasabi.experimentobjects.Experiment.State.DELETED; @@ -753,7 +754,7 @@ public void createApplication(Application.Name applicationName) { } @Override - public Map> getTagListForApplications(Collection applicationNames) { + public Map> getTagListForApplications(Collection applicationNames) { throw new UnsupportedOperationException("Not supported "); } From a2c741228beb5642667af73fc5f8a09685efd6d3 Mon Sep 17 00:00:00 2001 From: Scott Cressler Date: Fri, 14 Apr 2017 08:52:53 -0700 Subject: [PATCH 07/43] Bunch of name changes in Angular Bootstrap components for version change of angular-bootstrap that was needed because of AngularJS version change that was needed to be able to use ng-tags-input . --- modules/ui/app/index.html | 8 ++-- .../controllers/AddApplicationModalCtrl.js | 16 ++++---- .../AddExperimentsToPageModalCtrl.js | 10 ++--- .../controllers/AddSuperadminModalCtrl.js | 8 ++-- .../scripts/controllers/AddUserModalCtrl.js | 12 +++--- .../scripts/controllers/AdminListModalCtrl.js | 6 +-- .../controllers/AllApplicationsCtrl.js | 4 +- .../scripts/controllers/AnalysisGraphModal.js | 6 +-- .../scripts/controllers/ApplicationsCtrl.js | 6 +-- .../controllers/BucketAssignmentModalCtrl.js | 18 ++++----- .../scripts/controllers/BucketModalCtrl.js | 14 +++---- .../ui/app/scripts/controllers/BucketsCtrl.js | 6 +-- .../controllers/ChangeDateModalCtrl.js | 10 ++--- .../controllers/ChangeRapidExperimentCtrl.js | 8 ++-- .../controllers/ChangeSamplingModalCtrl.js | 8 ++-- .../CloseBucketConfirmModalCtrl.js | 8 ++-- .../scripts/controllers/DialogModalCtrl.js | 8 ++-- .../controllers/EditApplicationModalCtrl.js | 6 +-- .../controllers/EditApplicationOnPageCtrl.js | 6 +-- .../EmptyBucketConfirmModalCtrl.js | 8 ++-- .../controllers/ExperimentDetailsCtrl.js | 22 +++++----- .../controllers/ExperimentModalCtrl.js | 20 +++++----- .../scripts/controllers/ExperimentsCtrl.js | 6 +-- .../app/scripts/controllers/FeedbackCtrl.js | 6 +-- .../scripts/controllers/FeedbackModalCtrl.js | 16 ++++---- .../scripts/controllers/FeedbackTableCtrl.js | 4 +- .../controllers/GetAssignmentsModalCtrl.js | 8 ++-- .../app/scripts/controllers/LogModalCtrl.js | 6 +-- .../ui/app/scripts/controllers/LogsCtrl.js | 6 +-- .../controllers/MutualExclusionModalCtrl.js | 12 +++--- .../controllers/MutualExclusionsCtrl.js | 8 ++-- .../scripts/controllers/PageManagementCtrl.js | 6 +-- .../ui/app/scripts/controllers/PagesCtrl.js | 4 +- .../ui/app/scripts/controllers/PluginsCtrl.js | 4 +- .../app/scripts/controllers/PrioritiesCtrl.js | 6 +-- .../app/scripts/controllers/ResultsModal.js | 8 ++-- .../controllers/SegmentationTestModal.js | 8 ++-- .../controllers/SuperadminsTableCtrl.js | 6 +-- .../app/scripts/controllers/UserModalCtrl.js | 12 +++--- .../ui/app/scripts/controllers/UsersCtrl.js | 6 +-- .../ui/app/scripts/services/DialogsFactory.js | 8 ++-- .../app/scripts/services/UtilitiesFactory.js | 8 ++-- modules/ui/app/views/BucketModal.html | 2 +- modules/ui/app/views/ChangeDateModal.html | 4 +- .../ui/app/views/EditApplicationOnPage.html | 2 +- modules/ui/app/views/ExperimentDetails.html | 36 ++++++++--------- modules/ui/app/views/ExperimentModal.html | 40 +++++++++---------- modules/ui/app/views/ExperimentTable.html | 4 +- modules/ui/app/views/GetAssignmentsModal.html | 8 ++-- modules/ui/app/views/LogsTable.html | 2 +- modules/ui/app/views/UsersTable.html | 2 +- modules/ui/bower.json | 17 ++++---- 52 files changed, 243 insertions(+), 240 deletions(-) diff --git a/modules/ui/app/index.html b/modules/ui/app/index.html index 416d7d3fe..5853e1c49 100644 --- a/modules/ui/app/index.html +++ b/modules/ui/app/index.html @@ -15,6 +15,7 @@ + @@ -43,9 +44,9 @@