Skip to content

Commit

Permalink
Merge branch 'develop' into feature/env-vars-on-ui-build
Browse files Browse the repository at this point in the history
And resolving README merge in UI.
  • Loading branch information
longdogz committed Jun 7, 2017
2 parents 8688df3 + 2c29e6b commit b7fc610
Show file tree
Hide file tree
Showing 112 changed files with 1,621 additions and 518 deletions.
25 changes: 24 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
**Support:** [![Join the chat at https://gitter.im/intuit/wasabi](https://badges.gitter.im/intuit/wasabi.svg)](https://gitter.im/intuit/wasabi?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) <br/>
**Documentation:** [User Guide](https://intuit.github.io/wasabi/v1/guide/index.html), [JavaDocs](https://intuit.github.io/wasabi/v1/javadocs/latest/)
<br/>
**A/B Testing Overview:** [![A/B Testing Overview](http://img.shields.io/badge/video-A%2FB%20Testing%20Overview-red.svg)](https://www.youtube.com/watch?v=_HtvJwBPUqk&feature=youtu.be) <br/>
**A/B Testing Overview:** [![A/B Testing Overview](http://img.shields.io/badge/video-A%2FB%20Testing%20Overview-red.svg)](https://www.youtube.com/watch?v=_HtvJwBPUqk&feature=youtu.be) [![Blog Meet Wasabi](https://img.shields.io/badge/blog-Meet%20Wasabi-brightgreen.svg)](https://medium.com/blueprint-by-intuit/open-sourcing-wasabi-the-a-b-testing-platform-by-intuit-a8d5abc958d) [![Blog Architecture Behind Wasabi](https://img.shields.io/badge/blog-Architecture%20Behind%20Wasabi-orange.svg)](https://medium.com/blueprint-by-intuit/the-architecture-behind-wasabi-an-open-source-a-b-testing-platform-b52430d3fd80) <br/>
**Continuous Integration:** [![Build Status](https://api.travis-ci.org/intuit/wasabi.svg?branch=develop)](https://travis-ci.org/intuit/wasabi)
[![Coverage Status](https://coveralls.io/repos/github/intuit/wasabi/badge.svg)](https://coveralls.io/github/intuit/wasabi?branch=develop)
[![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.intuit.wasabi/wasabi/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.intuit.wasabi/wasabi) <br/>
Expand Down Expand Up @@ -359,6 +359,29 @@ Code changes can readily be verified by running the growing collection of includ
```bash
% ./bin/wasabi.sh start test stop
```
##### Troubleshooting

Integration tests might fail intermittently due to a time drift issue in docker containers on Mac OSX.

When the Mac sleeps and wakes back up, there is a lag created between the clock in the Mac vs the
running docker containers. This is a known issue in Docker for Mac.

This can be fixed by running the following command:

```bash
% docker run --rm --privileged alpine hwclock -s
```

The above command will need to be run every time when there is a time drift.

To automatically run this command and update the time each time the Mac wakes up, you could install
the following agent:

```bash
% curl https://raw.githubusercontent.com/arunvelsriram/docker-time-sync-agent/master/install.sh | bash
```

You can read more about this at: [quick-tip-fixing-time-drift-issue-on-docker-for-mac](https://blog.shameerc.com/2017/03/quick-tip-fixing-time-drift-issue-on-docker-for-mac)

## Package and Deploy at Scale

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -56,6 +57,8 @@ public class ExperimentDetail {

private String description;

private Set<String> tags;


/**
* This class holds the details for the Buckets. This is especially interesting for
Expand Down Expand Up @@ -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());
}

/**
Expand All @@ -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<String> tags) {
setId(id);
setState(state);
setLabel(label);
Expand All @@ -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() {
Expand Down Expand Up @@ -337,6 +343,14 @@ public void setDescription(String description) {
this.description = description;
}

public Set<String> getTags() {
return tags;
}

public void setTags(Set<String> 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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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<String> tags = new TreeSet<>(java.util.Arrays.asList("tag1", "tag2", "tag3"));


private Experiment exp = Experiment.withID(expId).withApplicationName(appName).withModificationTime(modTime.getTime())
Expand All @@ -58,44 +61,45 @@ 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);
assertEquals(expDetail.getState(), expState);
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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@
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.authorizationobjects.UserPermissions;
import com.intuit.wasabi.exceptions.AuthenticationException;
import com.intuit.wasabi.experiment.Experiments;
import com.intuit.wasabi.experiment.Pages;
Expand All @@ -43,8 +45,10 @@
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Response;
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;
Expand Down Expand Up @@ -373,4 +377,42 @@ 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 {
UserInfo.Username userName = authorization.getUser(authorizationHeader);
Set<Application.Name> allowed = new HashSet<>();

// get the for the user visible Applications
List<UserPermissions> authorized = authorization.getUserPermissionsList(userName).getPermissionsList();
for (UserPermissions perm : authorized) {
allowed.add(perm.getApplicationName());
}

// get their associated tags
Map<Application.Name, Set<String>> 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;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import com.intuit.wasabi.api.pagination.filters.PaginationFilterProperty;
import org.apache.commons.lang3.StringUtils;

import java.util.Arrays;
import java.util.function.BiPredicate;
import java.util.function.Function;

Expand Down Expand Up @@ -68,7 +69,10 @@ public enum Property implements PaginationFilterProperty<ExperimentDetail> {
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
-> Arrays.asList(filter.split(";")).contains(tag))),
tags_and(ExperimentDetail::getTags, (tagsSet, filter) -> tagsSet.containsAll(Arrays.asList(filter.split(";"))));

private final Function<ExperimentDetail, ?> propertyExtractor;
private final BiPredicate<?, String> filterPredicate;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import org.apache.commons.lang3.StringUtils;

import java.time.LocalDate;
import java.util.Arrays;
import java.util.Date;
import java.util.function.BiPredicate;
import java.util.function.Function;
Expand Down Expand Up @@ -73,7 +74,10 @@ public enum Property implements PaginationFilterProperty<Experiment> {
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
-> Arrays.asList(filter.split(";")).contains(tag))),
tags_and(Experiment::getTags, (tagsSet, filter) -> tagsSet.containsAll(Arrays.asList(filter.split(";"))));

private final Function<Experiment, ?> propertyExtractor;
private final BiPredicate<?, String> filterPredicate;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@

import com.intuit.wasabi.authenticationobjects.UserInfo;
import com.intuit.wasabi.authorization.Authorization;
import com.intuit.wasabi.authorizationobjects.Permission;
import com.intuit.wasabi.authorizationobjects.UserPermissions;
import com.intuit.wasabi.authorizationobjects.UserPermissionsList;
import com.intuit.wasabi.exceptions.AuthenticationException;
import com.intuit.wasabi.experiment.Experiments;
import com.intuit.wasabi.experiment.Pages;
Expand All @@ -38,8 +41,13 @@

import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.ResponseBuilder;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import static com.intuit.wasabi.authorizationobjects.Permission.READ;
import static com.intuit.wasabi.authorizationobjects.Permission.UPDATE;
Expand All @@ -51,6 +59,7 @@
import static org.mockito.Matchers.anyCollection;
import static org.mockito.Mockito.anyObject;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
Expand Down Expand Up @@ -246,4 +255,43 @@ public void getExperimentsByPages() throws Exception {
assertThat(pageExperimentsCaptor.getValue().size(), is(1));
assertThat(pageExperimentsCaptor.getValue(), hasEntry("experiments", pageExperiments));
}

@Test
public void testTagsRetrieval() {
when(authorization.getUser("foo")).thenReturn(username);

Iterator<UserPermissions> userPermIterator = mock(Iterator.class);
when(userPermIterator.hasNext()).thenReturn(true, true, true, false);
List<Permission> permissions = Arrays.asList(Permission.READ);
when(userPermIterator.next())
.thenReturn(UserPermissions.newInstance(Application.Name.valueOf("app01"), permissions).build())
.thenReturn(UserPermissions.newInstance(Application.Name.valueOf("app02"), permissions).build())
.thenReturn(UserPermissions.newInstance(Application.Name.valueOf("app03"), permissions).build());


UserPermissionsList wrapClass = mock(UserPermissionsList.class);
List<UserPermissions> userPermList = mock(List.class);
when(wrapClass.getPermissionsList()).thenReturn(userPermList);

when(userPermList.iterator()).thenReturn(userPermIterator);
when(authorization.getUserPermissionsList(username)).thenReturn(wrapClass);


when(httpHeader.headers()).thenReturn(responseBuilder);
when(responseBuilder.entity(anyCollection())).thenReturn(responseBuilder);
when(responseBuilder.build()).thenReturn(response);

Map<Application.Name, Set<String>> tags = mock(HashMap.class);

when(experiments.getTagsForApplications(applicationNames)).thenReturn(tags);

// all Applications are allowed in this test
Set<Application.Name> allowed = new HashSet<>(Arrays.asList(Application.Name.valueOf("app01"),
Application.Name.valueOf("app02"), Application.Name.valueOf("app03")));

applicationsResource.getAllExperimentTags("foo");

verify(experiments).getTagsForApplications(allowed);

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;


/**
Expand All @@ -54,9 +56,10 @@ public void setup() {
Date startTime = cal.getTime();
cal.add(Calendar.DATE, 14);
Date endTime = cal.getTime();
Set<String> 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();
Expand All @@ -71,8 +74,12 @@ public void setup() {
@Parameterized.Parameters(name = "expDetailFilter({index})")
public static Collection<Object[]> data() {
return Arrays.asList(new Object[][]{
{"experiment_label=ExperimentLabel", true}, {"bucket_label=Bucket2", true},
{"application_name=testApp", false}, {"mod_time=Summer", false}, {"Experiment", true}
{"experiment_label=ExperimentLabel", true},
{"bucket_label=Bucket2", true},
{"application_name=testApp", false},
{"mod_time=Summer", false},
{"Experiment", true},
{"tags=tag1", true},
});
}

Expand Down

0 comments on commit b7fc610

Please sign in to comment.