Skip to content

Commit 5f9df76

Browse files
committed
Merge branch 'master' into 2.0.x
2 parents d8439a9 + 370fceb commit 5f9df76

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+1309
-1548
lines changed

.travis.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,5 @@ branches:
2222
only:
2323
- master
2424
- /^\d+\.\d+\.\d+(-SNAPSHOT|-alpha|-beta)?\d*$/ # trigger builds on tags which are semantically versioned to ship the SDK.
25+
after_success:
26+
- ./gradlew coveralls uploadArchives --console plain

CHANGELOG.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,18 @@
11
# Optimizely Java X SDK Changelog
22

3+
## 1.9.0
4+
5+
January 30, 2018
6+
7+
This release adds support for bucketing id (By passing in `$opt_bucketing_id` in the attribute map to override the user id as the bucketing variable. This is useful when wanting a set of users to share the same experience such as two players in a game).
8+
9+
This release also depricates the old notification broadcaster in favor of a notification center that supports a wide range of notifications. The notification listener is now registered for the specific notification type such as ACTIVATE and TRACK. This is accomplished by allowing for a variable argument call to notify (a new var arg method added to the NotificationListener). Specific abstract classes exist for the associated notification type (ActivateNotification and TrackNotification). These abstract classes enforce the strong typing that exists in Java. You may also add custom notification types and fire them through the notification center. The notification center is implemented using this var arg approach in all Optimizely SDKs.
10+
11+
### New Features
12+
13+
- Added `$opt_bucketing_id` in the attribute map for overriding bucketing using the user id. It is available as a static string in DecisionService.ATTRIBUTE_BUCKETING_ID
14+
- Optimizely notification center for activate and track notifications.
15+
316
## 2.0.0 Beta 3
417
January 5, 2018
518

build.gradle

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,24 @@ buildscript {
1212
}
1313

1414
plugins {
15-
id 'nebula.optional-base' version '3.2.0'
15+
id 'com.github.kt3k.coveralls' version '2.8.2'
16+
id 'jacoco'
1617
id 'me.champeau.gradle.jmh' version '0.3.1'
18+
id 'nebula.optional-base' version '3.2.0'
1719
}
1820

1921
allprojects {
2022
group = 'com.optimizely.ab'
2123
apply plugin: 'idea'
24+
apply plugin: 'jacoco'
25+
26+
repositories {
27+
jcenter()
28+
}
29+
30+
jacoco {
31+
toolVersion = '0.8.0'
32+
}
2233
}
2334

2435
apply from: 'gradle/publish.gradle'
@@ -33,8 +44,8 @@ allprojects {
3344
subprojects {
3445
apply plugin: 'com.jfrog.bintray'
3546
apply plugin: 'findbugs'
36-
apply plugin: 'java'
3747
apply plugin: 'jacoco'
48+
apply plugin: 'java'
3849
apply plugin: 'maven-publish'
3950
apply plugin: 'me.champeau.gradle.jmh'
4051
apply plugin: 'nebula.optional-base'
@@ -170,9 +181,49 @@ subprojects {
170181
task ship() {
171182
dependsOn('bintrayUpload')
172183
}
184+
173185
}
174186

175187
task ship() {
176188
dependsOn(':core-api:ship', ':core-httpclient-impl:ship')
177189
}
178190

191+
// Only report code coverage for projects that are distributed
192+
def publishedProjects = subprojects.findAll { it.path != ':simulator' }
193+
194+
task jacocoMerge(type: JacocoMerge) {
195+
publishedProjects.each { subproject ->
196+
executionData subproject.tasks.withType(Test)
197+
}
198+
doFirst {
199+
executionData = files(executionData.findAll { it.exists() })
200+
}
201+
}
202+
203+
task jacocoRootReport(type: JacocoReport, group: 'Coverage reports') {
204+
description = 'Generates an aggregate report from all subprojects'
205+
dependsOn publishedProjects.test, jacocoMerge
206+
207+
additionalSourceDirs = files(publishedProjects.sourceSets.main.allSource.srcDirs)
208+
sourceDirectories = files(publishedProjects.sourceSets.main.allSource.srcDirs)
209+
classDirectories = files(publishedProjects.sourceSets.main.output)
210+
executionData jacocoMerge.destinationFile
211+
212+
reports {
213+
html.enabled = true // human readable
214+
xml.enabled = true // required by coveralls
215+
}
216+
}
217+
218+
coveralls {
219+
sourceDirs = publishedProjects.sourceSets.main.allSource.srcDirs.flatten()
220+
jacocoReportPath = "${buildDir}/reports/jacoco/jacocoRootReport/jacocoRootReport.xml"
221+
}
222+
223+
tasks.coveralls {
224+
group = 'Coverage reports'
225+
description = 'Uploads the aggregated coverage report to Coveralls'
226+
227+
dependsOn jacocoRootReport
228+
onlyIf { System.env.'CI' && !JavaVersion.current().isJava9Compatible() }
229+
}

core-api/src/main/java/com/optimizely/ab/Optimizely.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,7 @@
3737
import com.optimizely.ab.event.LogEvent;
3838
import com.optimizely.ab.event.internal.BuildVersionInfo;
3939
import com.optimizely.ab.event.internal.EventBuilder;
40-
import com.optimizely.ab.event.internal.EventBuilderV2;
41-
import com.optimizely.ab.event.internal.payload.Event.ClientEngine;
40+
import com.optimizely.ab.event.internal.payload.EventBatch.ClientEngine;
4241
import com.optimizely.ab.internal.EventTagUtils;
4342
import com.optimizely.ab.notification.NotificationBroadcaster;
4443
import com.optimizely.ab.notification.NotificationCenter;
@@ -948,7 +947,7 @@ public Optimizely build() throws ConfigParseException {
948947

949948

950949
if (eventBuilder == null) {
951-
eventBuilder = new EventBuilderV2(clientEngine, clientVersion);
950+
eventBuilder = new EventBuilder(clientEngine, clientVersion);
952951
}
953952

954953
if (errorHandler == null) {

core-api/src/main/java/com/optimizely/ab/event/internal/EventBuilder.java

Lines changed: 102 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/**
22
*
3-
* Copyright 2016-2017, Optimizely and contributors
3+
* Copyright 2016-2018, Optimizely and contributors
44
*
55
* Licensed under the Apache License, Version 2.0 (the "License");
66
* you may not use this file except in compliance with the License.
@@ -16,27 +16,122 @@
1616
*/
1717
package com.optimizely.ab.event.internal;
1818

19+
import com.optimizely.ab.annotations.VisibleForTesting;
20+
import com.optimizely.ab.bucketing.DecisionService;
21+
import com.optimizely.ab.config.EventType;
1922
import com.optimizely.ab.config.Experiment;
2023
import com.optimizely.ab.config.ProjectConfig;
2124
import com.optimizely.ab.config.Variation;
2225
import com.optimizely.ab.event.LogEvent;
23-
26+
import com.optimizely.ab.event.internal.payload.Attribute;
27+
import com.optimizely.ab.event.internal.payload.Decision;
28+
import com.optimizely.ab.event.internal.payload.EventBatch;
29+
import com.optimizely.ab.event.internal.payload.Event;
30+
import com.optimizely.ab.event.internal.payload.Snapshot;
31+
import com.optimizely.ab.event.internal.payload.Visitor;
32+
import com.optimizely.ab.event.internal.serializer.DefaultJsonSerializer;
33+
import com.optimizely.ab.event.internal.serializer.Serializer;
34+
import com.optimizely.ab.internal.EventTagUtils;
35+
import org.slf4j.Logger;
36+
import org.slf4j.LoggerFactory;
2437
import javax.annotation.Nonnull;
38+
import java.util.ArrayList;
39+
import java.util.Arrays;
40+
import java.util.Collections;
41+
import java.util.List;
2542
import java.util.Map;
43+
import java.util.UUID;
44+
45+
public class EventBuilder {
46+
private static final Logger logger = LoggerFactory.getLogger(EventBuilder.class);
47+
static final String ATTRIBUTE_KEY_FOR_BUCKETING_ATTRIBUTE = "optimizely_bucketing_id";
48+
static final String EVENT_ENDPOINT = "https://logx.optimizely.com/v1/events";
49+
static final String ACTIVATE_EVENT_KEY = "campaign_activated";
50+
51+
private Serializer serializer;
52+
@VisibleForTesting
53+
public final String clientVersion;
54+
@VisibleForTesting
55+
public final EventBatch.ClientEngine clientEngine;
56+
57+
public EventBuilder() {
58+
this(EventBatch.ClientEngine.JAVA_SDK, BuildVersionInfo.VERSION);
59+
}
60+
61+
public EventBuilder(EventBatch.ClientEngine clientEngine, String clientVersion) {
62+
this.clientEngine = clientEngine;
63+
this.clientVersion = clientVersion;
64+
this.serializer = DefaultJsonSerializer.getInstance();
65+
}
2666

27-
public abstract class EventBuilder {
2867

29-
public abstract LogEvent createImpressionEvent(@Nonnull ProjectConfig projectConfig,
68+
public LogEvent createImpressionEvent(@Nonnull ProjectConfig projectConfig,
3069
@Nonnull Experiment activatedExperiment,
3170
@Nonnull Variation variation,
3271
@Nonnull String userId,
33-
@Nonnull Map<String, String> attributes);
72+
@Nonnull Map<String, String> attributes) {
3473

35-
public abstract LogEvent createConversionEvent(@Nonnull ProjectConfig projectConfig,
74+
Decision decision = new Decision(activatedExperiment.getLayerId(), activatedExperiment.getId(),
75+
variation.getId(), false);
76+
Event impressionEvent = new Event(System.currentTimeMillis(),UUID.randomUUID().toString(), activatedExperiment.getLayerId(),
77+
ACTIVATE_EVENT_KEY, null, null, null, ACTIVATE_EVENT_KEY, null);
78+
Snapshot snapshot = new Snapshot(Arrays.asList(decision), Arrays.asList(impressionEvent));
79+
80+
Visitor visitor = new Visitor(userId, null, buildAttributeList(projectConfig, attributes), Arrays.asList(snapshot));
81+
List<Visitor> visitors = Arrays.asList(visitor);
82+
EventBatch eventBatch = new EventBatch(clientEngine.getClientEngineValue(), clientVersion, projectConfig.getAccountId(), visitors, projectConfig.getAnonymizeIP(), projectConfig.getProjectId(), projectConfig.getRevision());
83+
String payload = this.serializer.serialize(eventBatch);
84+
return new LogEvent(LogEvent.RequestMethod.POST, EVENT_ENDPOINT, Collections.<String, String>emptyMap(), payload);
85+
}
86+
87+
public LogEvent createConversionEvent(@Nonnull ProjectConfig projectConfig,
3688
@Nonnull Map<Experiment, Variation> experimentVariationMap,
3789
@Nonnull String userId,
3890
@Nonnull String eventId,
3991
@Nonnull String eventName,
4092
@Nonnull Map<String, String> attributes,
41-
@Nonnull Map<String, ?> eventTags);
93+
@Nonnull Map<String, ?> eventTags) {
94+
95+
if (experimentVariationMap.isEmpty()) {
96+
return null;
97+
}
98+
99+
ArrayList<Decision> decisions = new ArrayList<Decision>();
100+
for (Map.Entry<Experiment, Variation> entry : experimentVariationMap.entrySet()) {
101+
Decision decision = new Decision(entry.getKey().getLayerId(), entry.getKey().getId(), entry.getValue().getId(), false);
102+
decisions.add(decision);
103+
}
104+
105+
EventType eventType = projectConfig.getEventNameMapping().get(eventName);
106+
107+
Event conversionEvent = new Event(System.currentTimeMillis(),UUID.randomUUID().toString(), eventType.getId(),
108+
eventType.getKey(), null, EventTagUtils.getRevenueValue(eventTags), eventTags, eventType.getKey(), EventTagUtils.getNumericValue(eventTags));
109+
Snapshot snapshot = new Snapshot(decisions, Arrays.asList(conversionEvent));
110+
111+
Visitor visitor = new Visitor(userId, null, buildAttributeList(projectConfig, attributes), Arrays.asList(snapshot));
112+
List<Visitor> visitors = Arrays.asList(visitor);
113+
EventBatch eventBatch = new EventBatch(clientEngine.getClientEngineValue(), clientVersion, projectConfig.getAccountId(), visitors, projectConfig.getAnonymizeIP(), projectConfig.getProjectId(), projectConfig.getRevision());
114+
String payload = this.serializer.serialize(eventBatch);
115+
return new LogEvent(LogEvent.RequestMethod.POST, EVENT_ENDPOINT, Collections.<String, String>emptyMap(), payload);
116+
}
117+
118+
private List<Attribute> buildAttributeList(ProjectConfig projectConfig, Map<String, String> attributes) {
119+
List<Attribute> attributesList = new ArrayList<Attribute>();
120+
121+
Map<String, com.optimizely.ab.config.Attribute> attributeMap = projectConfig.getAttributeKeyMapping();
122+
for (Map.Entry<String, String> entry : attributes.entrySet()) {
123+
com.optimizely.ab.config.Attribute projectAttribute = attributeMap.get(entry.getKey());
124+
Attribute attribute = new Attribute((projectAttribute != null ? projectAttribute.getId() : null),
125+
entry.getKey(), Attribute.CUSTOM_ATTRIBUTE_TYPE, entry.getValue());
126+
127+
if (entry.getKey() == DecisionService.BUCKETING_ATTRIBUTE) {
128+
attribute = new Attribute(com.optimizely.ab.bucketing.DecisionService.BUCKETING_ATTRIBUTE,
129+
ATTRIBUTE_KEY_FOR_BUCKETING_ATTRIBUTE, Attribute.CUSTOM_ATTRIBUTE_TYPE, entry.getValue());
130+
}
131+
132+
attributesList.add(attribute);
133+
}
134+
135+
return attributesList;
136+
}
42137
}

0 commit comments

Comments
 (0)