Skip to content

Commit 626511a

Browse files
author
jianggang
authored
Merge pull request #15 from FeatureProbe/metric_dev
feat: Add delete flag to variation
2 parents 63aa1d2 + dfe5473 commit 626511a

4 files changed

Lines changed: 147 additions & 9 deletions

File tree

src/main/java/com/featureprobe/api/model/VariationAccessCounter.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,14 @@ public class VariationAccessCounter {
2424
@NotNull
2525
private Integer index;
2626

27+
private Boolean deleted;
2728

2829
public VariationAccessCounter(String value, Long count) {
2930
this.value = value;
3031
this.count = count;
32+
this.deleted = false;
3133
}
3234

3335

36+
3437
}

src/main/java/com/featureprobe/api/repository/EnvironmentRepository.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@
1111
@Repository
1212
public interface EnvironmentRepository extends JpaRepository<Environment, Long> {
1313

14-
Optional<Environment> findByKey(String key);
15-
1614
List<Environment> findAllByProjectKey(String projectKey);
1715

1816
Optional<Environment> findByServerSdkKey(String serverSdkKey);

src/main/java/com/featureprobe/api/service/MetricService.java

Lines changed: 52 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,16 @@
44
import com.featureprobe.api.dto.MetricResponse;
55
import com.featureprobe.api.entity.Environment;
66
import com.featureprobe.api.entity.Event;
7+
import com.featureprobe.api.entity.Targeting;
78
import com.featureprobe.api.entity.VariationHistory;
9+
import com.featureprobe.api.mapper.TargetingVersionMapper;
810
import com.featureprobe.api.model.AccessEventPoint;
11+
import com.featureprobe.api.model.TargetingContent;
12+
import com.featureprobe.api.model.Variation;
913
import com.featureprobe.api.model.VariationAccessCounter;
1014
import com.featureprobe.api.repository.EnvironmentRepository;
1115
import com.featureprobe.api.repository.EventRepository;
16+
import com.featureprobe.api.repository.TargetingRepository;
1217
import com.featureprobe.api.repository.VariationHistoryRepository;
1318
import com.google.common.collect.Lists;
1419
import lombok.AllArgsConstructor;
@@ -35,6 +40,7 @@ public class MetricService {
3540
private EnvironmentRepository environmentRepository;
3641
private EventRepository eventRepository;
3742
private VariationHistoryRepository variationHistoryRepository;
43+
private TargetingRepository targetingRepository;
3844

3945
private static final int MAX_QUERY_HOURS = 12 * 24;
4046
private static final int MAX_QUERY_POINT_COUNT = 12;
@@ -51,7 +57,12 @@ public MetricResponse query(String projectKey, String environmentKey, String tog
5157
List<AccessEventPoint> aggregatedAccessEventPoints = aggregatePointByMetricType(variationVersionMap,
5258
accessEventPoints, metricType);
5359

54-
return new MetricResponse(accessEventPoints, summaryAccessEvents(aggregatedAccessEventPoints));
60+
List<VariationAccessCounter> accessCounters = summaryAccessEvents(aggregatedAccessEventPoints);
61+
Targeting latestTargeting = targetingRepository.findByProjectKeyAndEnvironmentKeyAndToggleKey(projectKey,
62+
environmentKey, toggleKey).get();
63+
appendLatestVariations(accessCounters, latestTargeting, metricType);
64+
65+
return new MetricResponse(accessEventPoints, accessCounters);
5566
}
5667

5768
private Map<String, VariationHistory> buildVariationVersionMap(String projectKey, String environmentKey,
@@ -63,7 +74,7 @@ private Map<String, VariationHistory> buildVariationVersionMap(String projectKey
6374
return variationHistories.stream().collect(Collectors.toMap(this::toIndexValue, Function.identity()));
6475
}
6576

66-
private List<AccessEventPoint> aggregatePointByMetricType(Map<String, VariationHistory> variationVersionMap,
77+
protected List<AccessEventPoint> aggregatePointByMetricType(Map<String, VariationHistory> variationVersionMap,
6778
List<AccessEventPoint> accessEventPoints,
6879
MetricType metricType) {
6980

@@ -74,22 +85,22 @@ private List<AccessEventPoint> aggregatePointByMetricType(Map<String, VariationH
7485
VariationHistory variationHistory = variationVersionMap.get(variationAccessCounter.getValue());
7586
if (variationHistory != null) {
7687
variationAccessCounter.setValue(metricType.isNameType()
77-
? variationHistory.getName() : variationHistory.getValue());
88+
? variationHistory.getName() : variationHistory.getValue());
7889
}
7990
});
8091
Map<String, Long> variationCounts =
8192
accessEventPoint.getValues().stream().collect(Collectors.toMap(VariationAccessCounter::getValue,
8293
VariationAccessCounter::getCount, Long::sum));
8394

8495
List<VariationAccessCounter> values = variationCounts.entrySet().stream().map(e ->
85-
new VariationAccessCounter(e.getKey(), e.getValue(), null, null))
96+
new VariationAccessCounter(e.getKey(), e.getValue()))
8697
.collect(Collectors.toList());
8798
accessEventPoint.setValues(values);
8899
});
89100
return accessEventPoints;
90101
}
91102

92-
private List<AccessEventPoint> queryAccessEventPoints(String serverSdkKey, String toggleKey, int lastHours) {
103+
protected List<AccessEventPoint> queryAccessEventPoints(String serverSdkKey, String toggleKey, int lastHours) {
93104
int pointIntervalCount = getPointIntervalCount(lastHours);
94105
int pointCount = lastHours / pointIntervalCount;
95106

@@ -142,7 +153,7 @@ private List<VariationAccessCounter> queryAccessEvents(String serverSdkKey,
142153
return toAccessEvent(currentPointEvents);
143154
}
144155

145-
private List<VariationAccessCounter> toAccessEvent(List<Event> events) {
156+
protected List<VariationAccessCounter> toAccessEvent(List<Event> events) {
146157
if (CollectionUtils.isEmpty(events)) {
147158
return Collections.emptyList();
148159
}
@@ -199,6 +210,41 @@ protected List<VariationAccessCounter> summaryAccessEvents(List<AccessEventPoint
199210
return summaryEvents;
200211
}
201212

213+
protected void appendLatestVariations(List<VariationAccessCounter> accessCounters, Targeting latestTargeting,
214+
MetricType metricType) {
215+
TargetingContent targetingContent = TargetingVersionMapper.INSTANCE.toTargetingContent(
216+
latestTargeting.getContent());
217+
218+
if (CollectionUtils.isEmpty(targetingContent.getVariations())) {
219+
return;
220+
}
221+
List<String> latestVariations = targetingContent.getVariations()
222+
.stream()
223+
.map(metricType.isNameType() ? Variation::getName : Variation::getValue)
224+
.collect(Collectors.toList());
225+
226+
setVariationDeletedIfNotInLatest(accessCounters, latestVariations);
227+
appendVariationIfInLatest(accessCounters, latestVariations);
228+
}
229+
230+
private void setVariationDeletedIfNotInLatest(List<VariationAccessCounter> accessCounters,
231+
List<String> namesOrValues) {
232+
accessCounters.stream()
233+
.filter(accessCounter -> !namesOrValues.contains(accessCounter.getValue()))
234+
.forEach(accessCounter -> accessCounter.setDeleted(true));
235+
}
236+
237+
private void appendVariationIfInLatest(List<VariationAccessCounter> accessCounters, List<String> namesOrValues) {
238+
namesOrValues.forEach(value -> {
239+
if (!accessCounters.stream()
240+
.filter(accessCounter -> StringUtils.equals(accessCounter.getValue(), value))
241+
.findFirst()
242+
.isPresent()) {
243+
accessCounters.add(new VariationAccessCounter(value, 0L));
244+
}
245+
});
246+
}
247+
202248
private String queryEnvironmentServerSdkKey(String projectKey, String environmentKey) {
203249
Environment environment = this.environmentRepository.findByProjectKeyAndKey(projectKey, environmentKey).get();
204250
return environment.getServerSdkKey();

src/test/groovy/com/featureprobe/api/service/MetricServiceSpec.groovy

Lines changed: 92 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,18 @@
11
package com.featureprobe.api.service
22

3+
import com.featureprobe.api.base.constants.MetricType
4+
import com.featureprobe.api.dto.MetricResponse
5+
import com.featureprobe.api.entity.Environment
36
import com.featureprobe.api.entity.Event
7+
import com.featureprobe.api.entity.Targeting
8+
import com.featureprobe.api.entity.VariationHistory
49
import com.featureprobe.api.model.AccessEventPoint
10+
import com.featureprobe.api.model.TargetingContent
11+
import com.featureprobe.api.model.Variation
512
import com.featureprobe.api.model.VariationAccessCounter
613
import com.featureprobe.api.repository.EnvironmentRepository
714
import com.featureprobe.api.repository.EventRepository
15+
import com.featureprobe.api.repository.TargetingRepository
816
import com.featureprobe.api.repository.VariationHistoryRepository
917
import spock.lang.Specification
1018

@@ -17,12 +25,95 @@ class MetricServiceSpec extends Specification {
1725
EnvironmentRepository environmentRepository
1826
EventRepository eventRepository
1927
VariationHistoryRepository variationHistoryRepository
28+
TargetingRepository targetingRepository
2029

2130
def setup() {
2231
environmentRepository = Mock(EnvironmentRepository)
2332
eventRepository = Mock(EventRepository)
2433
variationHistoryRepository = Mock(VariationHistoryRepository)
25-
metricService = new MetricService(environmentRepository, eventRepository, variationHistoryRepository)
34+
targetingRepository = Mock(TargetingRepository)
35+
metricService = new MetricService(environmentRepository, eventRepository, variationHistoryRepository,
36+
targetingRepository)
37+
}
38+
39+
def "test find the last 3 hours of data by metric type"() {
40+
given:
41+
def toggleKey = "myToggle"
42+
def envKey = "test"
43+
def projectKey = "prj-key"
44+
def serverSdkKey = "sdkKey-001"
45+
46+
when:
47+
MetricResponse response = metricService.query("prj-key",
48+
"test", "myToggle", MetricType.NAME, 3)
49+
50+
then:
51+
1 * environmentRepository.findByProjectKeyAndKey(projectKey, envKey) >> Optional.of(new Environment(serverSdkKey: serverSdkKey))
52+
3 * eventRepository.findBySdkKeyAndToggleKeyAndStartDateGreaterThanEqualAndEndDateLessThanEqual(serverSdkKey, toggleKey,
53+
_, _) >> []
54+
1 * variationHistoryRepository.findByProjectKeyAndEnvironmentKeyAndToggleKey(projectKey, envKey, toggleKey) >> []
55+
1 * targetingRepository.findByProjectKeyAndEnvironmentKeyAndToggleKey(projectKey, envKey, toggleKey) >> Optional.of(new Targeting(content: "{}"))
56+
57+
0 == response.summary.size()
58+
}
59+
60+
def "test query access event points when event is empty"() {
61+
given:
62+
def lastHours = 10
63+
64+
when:
65+
List<AccessEventPoint> accessEventPoints = metricService.queryAccessEventPoints("test-sdk-key",
66+
"my_toggle1", lastHours)
67+
68+
then:
69+
10 * eventRepository.findBySdkKeyAndToggleKeyAndStartDateGreaterThanEqualAndEndDateLessThanEqual(_, _, _, _) >> []
70+
lastHours == accessEventPoints.size()
71+
}
72+
73+
def "test append latest variations"() {
74+
given:
75+
List<VariationAccessCounter> accessCounters = [
76+
new VariationAccessCounter("red", 10),
77+
new VariationAccessCounter("blue", 10)
78+
]
79+
Targeting latestTargeting = new Targeting(content: new TargetingContent(variations: [new Variation(name: "red"),
80+
new Variation(name: "green")]).toJson())
81+
82+
when:
83+
metricService.appendLatestVariations(accessCounters, latestTargeting, MetricType.NAME)
84+
85+
then:
86+
3 == accessCounters.size()
87+
}
88+
89+
def "test event entities convert to access counter"() {
90+
when:
91+
def accessCounters = metricService.toAccessEvent([
92+
new Event(valueIndex: 0, toggleVersion: 10, count: 10),
93+
new Event(valueIndex: 1, toggleVersion: 11, count: 20),
94+
new Event(valueIndex: 0, toggleVersion: 10, count: 30)
95+
])
96+
97+
then:
98+
2 == accessCounters.size()
99+
with(accessCounters.find { it.value == '10_0' }) {
100+
40 == count
101+
}
102+
}
103+
104+
def "test aggregate point by metric type "() {
105+
when:
106+
List<AccessEventPoint> accessEventPoints = metricService.aggregatePointByMetricType([
107+
"1_10": new VariationHistory(id: 3, name: "blue"),
108+
"1_11": new VariationHistory(id: 3, name: "blue")],
109+
[new AccessEventPoint("10", [new VariationAccessCounter(value: "1_10", count: 15),
110+
new VariationAccessCounter(value: "1_11", count: 5)])], MetricType.NAME)
111+
112+
then:
113+
1 == accessEventPoints.size()
114+
"blue" == accessEventPoints[0].values[0].value
115+
20 == accessEventPoints[0].values[0].count
116+
26117
}
27118

28119
def "test `isGroupByDay`"() {

0 commit comments

Comments
 (0)