Skip to content

Commit

Permalink
Merge pull request #193 from martiner/liry-ff
Browse files Browse the repository at this point in the history
add listing of aggregated feature flags for project
  • Loading branch information
martiner committed Jun 17, 2015
2 parents a5ab759 + f65cc9f commit 12951fe
Show file tree
Hide file tree
Showing 8 changed files with 256 additions and 14 deletions.
53 changes: 53 additions & 0 deletions src/main/java/com/gooddata/gdc/FeatureFlag.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* Copyright (C) 2007-2015, GoodData(R) Corporation. All rights reserved.
*/
package com.gooddata.gdc;

import static com.gooddata.util.Validate.notNull;

/**
* Feature flag is a boolean flag used for enabling / disabling some specific feature of GoodData platform.
* It can be used in various scopes (per project, per project group, per user, global etc.).
*/
public class FeatureFlag {

private final String name;
private boolean enabled;

public FeatureFlag(final String name, final boolean enabled) {
this.name = notNull(name, "name cannot be null");
this.enabled = enabled;
}

public String getName() {
return name;
}

public boolean getEnabled() {
return enabled;
}

@Override
public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;

final FeatureFlag that = (FeatureFlag) o;

if (enabled != that.enabled) return false;
return !(name != null ? !name.equals(that.name) : that.name != null);

}

@Override
public int hashCode() {
int result = name != null ? name.hashCode() : 0;
result = 31 * result + (enabled ? 1 : 0);
return result;
}

@Override
public String toString() {
return "{name='" + name + '\'' + ", enabled=" + enabled + "}";
}
}
59 changes: 59 additions & 0 deletions src/main/java/com/gooddata/gdc/FeatureFlags.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* Copyright (C) 2007-2015, GoodData(R) Corporation. All rights reserved.
*/
package com.gooddata.gdc;

import org.codehaus.jackson.annotate.JsonAnySetter;
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
import org.codehaus.jackson.annotate.JsonTypeInfo;
import org.codehaus.jackson.annotate.JsonTypeName;
import org.springframework.web.util.UriTemplate;

import java.util.ArrayList;
import java.util.List;

import static org.springframework.util.Assert.notNull;
import static org.springframework.util.CollectionUtils.isEmpty;

@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME)
@JsonTypeName("featureFlags")
@JsonIgnoreProperties(ignoreUnknown = true)
public class FeatureFlags {

public static final String AGGREGATED_FEATURE_FLAGS_URI = "/gdc/internal/projects/{projectId}/featureFlags";
public static final UriTemplate AGGREGATED_FEATURE_FLAGS_TEMPLATE = new UriTemplate(AGGREGATED_FEATURE_FLAGS_URI);

private final List<FeatureFlag> featureFlags = new ArrayList<>();

/* protected helper method for JSON deserialization */
@JsonAnySetter
protected void addFlag(final String name, final boolean enabled) {
notNull(name);
featureFlags.add(new FeatureFlag(name, enabled));
}

public List<FeatureFlag> getFeatureFlags() {
return featureFlags;
}

@Override
public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;

final FeatureFlags that = (FeatureFlags) o;

return !(featureFlags != null ? !featureFlags.equals(that.featureFlags) : that.featureFlags != null);

}

@Override
public int hashCode() {
return featureFlags != null ? featureFlags.hashCode() : 0;
}

@Override
public String toString() {
return "FeatureFlags{" + featureFlags + "}";
}
}
56 changes: 42 additions & 14 deletions src/main/java/com/gooddata/project/ProjectService.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,16 @@
import com.gooddata.AbstractPollHandler;
import com.gooddata.AbstractService;
import com.gooddata.FutureResult;
import com.gooddata.PollResult;
import com.gooddata.GoodDataException;
import com.gooddata.GoodDataRestException;
import com.gooddata.PollResult;
import com.gooddata.SimplePollHandler;
import com.gooddata.account.AccountService;
import com.gooddata.collections.Page;
import com.gooddata.collections.PageableList;
import com.gooddata.gdc.AsyncTask;
import com.gooddata.gdc.FeatureFlag;
import com.gooddata.gdc.FeatureFlags;
import com.gooddata.gdc.UriResponse;
import org.springframework.http.HttpStatus;
import org.springframework.http.client.ClientHttpResponse;
Expand All @@ -28,8 +30,9 @@
import java.util.List;
import java.util.Set;

import static com.gooddata.project.ProjectFeatureFlags.FEATURE_FLAGS_TEMPLATE;
import static com.gooddata.gdc.FeatureFlags.AGGREGATED_FEATURE_FLAGS_TEMPLATE;
import static com.gooddata.project.ProjectFeatureFlag.FEATURE_FLAG_TEMPLATE;
import static com.gooddata.project.ProjectFeatureFlags.FEATURE_FLAGS_TEMPLATE;
import static com.gooddata.util.Validate.notEmpty;
import static com.gooddata.util.Validate.notNull;
import static java.util.Arrays.asList;
Expand Down Expand Up @@ -345,14 +348,39 @@ public Role getRoleByUri(String uri) {
}

/**
* Lists all feature flags for given project.
* It doesn't matter wheter feature flag is enabled or not, it'll be included in all cases.
* Lists aggregated feature flags for given project and current user (aggregates global, project group, project and user feature flags).
* It doesn't matter whether feature flag is enabled or not, it'll be included in all cases.
*
* @param project project, cannot be null
* @return list of aggregated feature flags for given project and current user
*/
public List<FeatureFlag> listAggregatedFeatureFlags(final Project project) {
notNull(project, "project");
try {
final FeatureFlags featureFlags =
restTemplate.getForObject(AGGREGATED_FEATURE_FLAGS_TEMPLATE.expand(project.getId()),
FeatureFlags.class);

if (featureFlags == null) {
throw new GoodDataException("empty response from API call");
}

return featureFlags.getFeatureFlags();
} catch (GoodDataException | RestClientException e) {
throw new GoodDataException("Unable to list aggregated feature flags for project ID=" + project.getId(), e);
}
}

/**
* Lists all project feature flags (only project scoped flags, use {@link #listAggregatedFeatureFlags(Project)} for
* aggregated flags from all scopes).
* It doesn't matter whether feature flag is enabled or not, it'll be included in all cases.
*
* @param project project, cannot be null
* @return list of all feature flags for given project
*/
public List<ProjectFeatureFlag> listFeatureFlags(Project project) {
notNull(project, "project cannot be null!");
notNull(project, "project");
try {
final ProjectFeatureFlags projectFeatureFlags =
restTemplate.getForObject(FEATURE_FLAGS_TEMPLATE.expand(project.getId()), ProjectFeatureFlags.class);
Expand All @@ -377,14 +405,14 @@ public List<ProjectFeatureFlag> listFeatureFlags(Project project) {
* @param featureFlag feature flag to be created, cannot be null
*/
public ProjectFeatureFlag createFeatureFlag(final Project project, final ProjectFeatureFlag featureFlag) {
notNull(project, "project cannot be null!");
notNull(featureFlag, "featureFlag cannot be null!");
notNull(project, "project");
notNull(featureFlag, "featureFlag");

final String featureFlagsUri = FEATURE_FLAGS_TEMPLATE.expand(project.getId()).toString();

try {
final URI featureFlagUri = restTemplate.postForLocation(featureFlagsUri, featureFlag);
notNull(featureFlagsUri, "URI of new featureFlag should not be null!");
notNull(featureFlagsUri, "URI of new featureFlag");
return getFeatureFlag(featureFlagUri.toString());
} catch (GoodDataException | RestClientException e) {
throw new GoodDataException("Unable to create feature flag: " + featureFlag, e);
Expand All @@ -399,8 +427,8 @@ public ProjectFeatureFlag createFeatureFlag(final Project project, final Project
* @return feature flag
*/
public ProjectFeatureFlag getFeatureFlag(final Project project, final String featureFlagName) {
notNull(project, "project cannot be null!");
notEmpty(featureFlagName, "featureFlagName cannot be empty!");
notNull(project, "project");
notEmpty(featureFlagName, "featureFlagName");

return restTemplate.getForObject(getFeatureFlagUri(project, featureFlagName), ProjectFeatureFlag.class);
}
Expand All @@ -413,8 +441,8 @@ public ProjectFeatureFlag getFeatureFlag(final Project project, final String fea
* @return updated feature flag
*/
public ProjectFeatureFlag updateFeatureFlag(final ProjectFeatureFlag featureFlag) {
notNull(featureFlag, "featureFlag cannot be null!");
notEmpty(featureFlag.getUri(), "featureFlag uri cannot be empty!");
notNull(featureFlag, "featureFlag");
notEmpty(featureFlag.getUri(), "featureFlag");

try {
restTemplate.put(featureFlag.getUri(), featureFlag);
Expand All @@ -430,8 +458,8 @@ public ProjectFeatureFlag updateFeatureFlag(final ProjectFeatureFlag featureFlag
* @param featureFlag existing project feature flag with links set properly, cannot be null
*/
public void deleteFeatureFlag(ProjectFeatureFlag featureFlag) {
notNull(featureFlag, "featureFlag cannot be null!");
notEmpty(featureFlag.getUri(), "featureFlag uri cannot be empty!");
notNull(featureFlag, "featureFlag");
notEmpty(featureFlag.getUri(), "featureFlag URI");

try {
restTemplate.delete(featureFlag.getUri());
Expand Down
10 changes: 10 additions & 0 deletions src/test/java/com/gooddata/ShowcaseAT.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import com.gooddata.dataset.DatasetManifest;
import com.gooddata.dataset.DatasetService;
import com.gooddata.gdc.DataStoreService;
import com.gooddata.gdc.FeatureFlag;
import com.gooddata.md.Attribute;
import com.gooddata.md.Entry;
import com.gooddata.md.Fact;
Expand Down Expand Up @@ -212,6 +213,15 @@ public void listProjectFeatureFlags() throws Exception {
new ProjectFeatureFlag(PROJECT_FEATURE_FLAG, true)));
}

@Test(groups = "project", dependsOnMethods = "listProjectFeatureFlags")
public void listAggregatedFeatureFlags() throws Exception {
final List<FeatureFlag> flags = gd.getProjectService().listAggregatedFeatureFlags(project);

assertThat(flags, containsInAnyOrder(
new FeatureFlag("mostRecentFeatureFlag", true),
new FeatureFlag(PROJECT_FEATURE_FLAG, true)));
}

@Test(groups = "project", dependsOnMethods = "createProjectFeatureFlag")
public void getProjectFeatureFlag() throws Exception {
final ProjectFeatureFlag featureFlag =
Expand Down
28 changes: 28 additions & 0 deletions src/test/java/com/gooddata/gdc/FeatureFlagsTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* Copyright (C) 2007-2015, GoodData(R) Corporation. All rights reserved.
*/
package com.gooddata.gdc;

import org.testng.annotations.Test;

import static com.gooddata.util.ResourceUtils.readObjectFromResource;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.hasSize;
import static org.testng.AssertJUnit.assertNotNull;

public class FeatureFlagsTest {

@Test
public void testDeserialize() {
final FeatureFlags featureFlags = readObjectFromResource(getClass(), "/gdc/featureFlags.json",
FeatureFlags.class);
assertNotNull(featureFlags);

assertThat(featureFlags.getFeatureFlags(), hasSize(2));
assertThat(featureFlags.getFeatureFlags(), contains(
new FeatureFlag("testFeature", true),
new FeatureFlag("testFeature2", false)));
}

}
19 changes: 19 additions & 0 deletions src/test/java/com/gooddata/project/ProjectServiceIT.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import com.gooddata.GoodDataException;
import com.gooddata.collections.PageRequest;
import com.gooddata.gdc.AsyncTask;
import com.gooddata.gdc.FeatureFlag;
import com.gooddata.gdc.FeatureFlags;
import com.gooddata.gdc.TaskStatus;
import com.gooddata.gdc.UriResponse;
import org.codehaus.jackson.map.ObjectMapper;
Expand Down Expand Up @@ -319,6 +321,23 @@ public void shouldListPagedUsers() throws Exception {
assertThat(secondPage, empty());
}

@Test
public void shouldListAggregatedFeatureFlags() throws Exception {
onRequest()
.havingMethodEqualTo("GET")
.havingPathEqualTo(FeatureFlags.AGGREGATED_FEATURE_FLAGS_TEMPLATE.expand(PROJECT_ID).toString())
.respond()
.withBody(readStringFromResource("/gdc/featureFlags.json"))
.withStatus(200);

final List<FeatureFlag> featureFlags = gd.getProjectService().listAggregatedFeatureFlags(enabled);

assertThat(featureFlags, hasSize(2));
assertThat(featureFlags, contains(
new FeatureFlag("testFeature", true),
new FeatureFlag("testFeature2", false)));
}

@Test
public void shouldListProjectFeatureFlags() throws Exception {

Expand Down
Loading

0 comments on commit 12951fe

Please sign in to comment.