Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 3 additions & 22 deletions core-api/src/main/java/com/optimizely/ab/Optimizely.java
Original file line number Diff line number Diff line change
Expand Up @@ -725,27 +725,6 @@ public boolean setForcedVariation(@Nonnull String experimentKey,
return projectConfig;
}

/**
* @return a {@link ProjectConfig} instance given a json string
*/
private static ProjectConfig getProjectConfig(String datafile) throws ConfigParseException {
if (datafile == null) {
throw new ConfigParseException("Unable to parse null datafile.");
}
if (datafile.length() == 0) {
throw new ConfigParseException("Unable to parse empty datafile.");
}

ProjectConfig projectConfig = DefaultConfigParser.getInstance().parseProjectConfig(datafile);

if (projectConfig.getVersion().equals("1")) {
throw new ConfigParseException("This version of the Java SDK does not support version 1 datafiles. " +
"Please use a version 2 or 3 datafile with this SDK.");
}

return projectConfig;
}

@Nullable
public UserProfileService getUserProfileService() {
return userProfileService;
Expand Down Expand Up @@ -891,7 +870,9 @@ protected Builder withConfig(ProjectConfig projectConfig) {

public Optimizely build() throws ConfigParseException {
if (projectConfig == null) {
projectConfig = Optimizely.getProjectConfig(datafile);
projectConfig = new ProjectConfig.Builder()
.withDatafile(datafile)
.build();
}

if (bucketer == null) {
Expand Down
44 changes: 38 additions & 6 deletions core-api/src/main/java/com/optimizely/ab/config/ProjectConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,22 +21,19 @@
import com.optimizely.ab.UnknownExperimentException;
import com.optimizely.ab.config.audience.Audience;
import com.optimizely.ab.config.audience.Condition;
import com.optimizely.ab.config.parser.ConfigParseException;
import com.optimizely.ab.config.parser.DefaultConfigParser;
import com.optimizely.ab.error.ErrorHandler;
import com.optimizely.ab.error.NoOpErrorHandler;
import com.optimizely.ab.error.RaiseExceptionErrorHandler;
import com.optimizely.ab.internal.ControlAttribute;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
Expand Down Expand Up @@ -65,6 +62,12 @@ public String toString() {
}
}

private static final List<String> supportedVersions = Arrays.asList(
Version.V2.version,
Version.V3.version,
Version.V4.version
);

// logger
private static final Logger logger = LoggerFactory.getLogger(ProjectConfig.class);

Expand Down Expand Up @@ -599,4 +602,33 @@ public String toString() {
", variationIdToExperimentMapping=" + variationIdToExperimentMapping +
'}';
}

public static class Builder {
private String datafile;

public Builder withDatafile(String datafile) {
this.datafile = datafile;
return this;
}

/**
* @return a {@link ProjectConfig} instance given a JSON string datafile
*/
public ProjectConfig build() throws ConfigParseException{
if (datafile == null) {
throw new ConfigParseException("Unable to parse null datafile.");
}
if (datafile.isEmpty()) {
throw new ConfigParseException("Unable to parse empty datafile.");
}

ProjectConfig projectConfig = DefaultConfigParser.getInstance().parseProjectConfig(datafile);

if (!supportedVersions.contains(projectConfig.getVersion())) {
throw new ConfigParseException("This version of the Java SDK does not support the given datafile version: " + projectConfig.getVersion());
}

return projectConfig;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package com.optimizely.ab.config;

import com.optimizely.ab.config.parser.ConfigParseException;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;

import static com.optimizely.ab.config.ProjectConfigTestUtils.invalidProjectConfigV5;
import static com.optimizely.ab.config.ProjectConfigTestUtils.validConfigJsonV4;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;

/**
* Tests for {@link com.optimizely.ab.config.ProjectConfig.Builder}.
*/
public class ProjectConfigBuilderTest {
@Rule
public ExpectedException thrown = ExpectedException.none();

@Test
public void withNullDatafile() throws Exception {
thrown.expect(ConfigParseException.class);
new ProjectConfig.Builder()
.withDatafile(null)
.build();
}

@Test
public void withEmptyDatafile() throws Exception {
thrown.expect(ConfigParseException.class);
new ProjectConfig.Builder()
.withDatafile("")
.build();
}

@Test
public void withValidDatafile() throws Exception {
ProjectConfig projectConfig = new ProjectConfig.Builder()
.withDatafile(validConfigJsonV4())
.build();
assertNotNull(projectConfig);
assertEquals("4", projectConfig.getVersion());
}

@Test
public void withUnsupportedDatafile() throws Exception {
thrown.expect(ConfigParseException.class);
new ProjectConfig.Builder()
.withDatafile(invalidProjectConfigV5())
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,13 @@ public static ProjectConfig validProjectConfigV4() {
return VALID_PROJECT_CONFIG_V4;
}

/**
* @return the expected {@link ProjectConfig} for the json produced by {@link #invalidProjectConfigV5()}
*/
public static String invalidProjectConfigV5() throws IOException {
return Resources.toString(Resources.getResource("config/invalid-project-config-v5.json"), Charsets.UTF_8);
}

/**
* Asserts that the provided project configs are equivalent.
*/
Expand Down
129 changes: 129 additions & 0 deletions core-api/src/test/resources/config/invalid-project-config-v5.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
{
"accountId": "789",
"projectId": "1234",
"version": "5",
"revision": "42",
"experiments": [
{
"id": "223",
"key": "etag1",
"status": "Running",
"layerId": "1",
"percentageIncluded": 9000,
"audienceIds": [],
"variations": [{
"id": "276",
"key": "vtag1",
"variables": []
}, {
"id": "277",
"key": "vtag2",
"variables": []
}],
"forcedVariations": {
"testUser1": "vtag1",
"testUser2": "vtag2"
},
"trafficAllocation": [{
"entityId": "276",
"endOfRange": 3500
}, {
"entityId": "277",
"endOfRange": 9000
}]
},
{
"id": "118",
"key": "etag2",
"status": "Not started",
"layerId": "2",
"audienceIds": [],
"variations": [{
"id": "278",
"key": "vtag3",
"variables": []
}, {
"id": "279",
"key": "vtag4",
"variables": []
}],
"forcedVariations": {},
"trafficAllocation": [{
"entityId": "278",
"endOfRange": 4500
}, {
"entityId": "279",
"endOfRange": 9000
}]
},
{
"id": "119",
"key": "etag3",
"status": "Launched",
"layerId": "3",
"audienceIds": [],
"variations": [{
"id": "280",
"key": "vtag5"
}, {
"id": "281",
"key": "vtag6"
}],
"forcedVariations": {},
"trafficAllocation": [{
"entityId": "280",
"endOfRange": 5000
}, {
"entityId": "281",
"endOfRange": 10000
}]
}
],
"groups": [],
"audiences": [],
"attributes": [
{
"id": "134",
"key": "browser_type"
}
],
"events": [
{
"id": "971",
"key": "clicked_cart",
"experimentIds": [
"223"
]
},
{
"id": "098",
"key": "Total Revenue",
"experimentIds": [
"223"
]
},
{
"id": "099",
"key": "clicked_purchase",
"experimentIds": [
"118",
"223"
]
},
{
"id": "100",
"key": "launched_exp_event",
"experimentIds": [
"119"
]
},
{
"id": "101",
"key": "event_with_launched_and_running_experiments",
"experimentIds": [
"119",
"223"
]
}
]
}