• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Unleash / unleash-edge / #1444

21 Jan 2025 08:37AM UTC coverage: 19.689% (-53.4%) from 73.06%
#1444

push

web-flow
dep-update: bump serde_json from 1.0.135 to 1.0.137 (#678)

Bumps [serde_json](https://github.com/serde-rs/json) from 1.0.135 to 1.0.137.
- [Release notes](https://github.com/serde-rs/json/releases)
- [Commits](https://github.com/serde-rs/json/compare/v1.0.135...v1.0.137)

---
updated-dependencies:
- dependency-name: serde_json
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

405 of 2057 relevant lines covered (19.69%)

0.39 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

17.91
/server/src/feature_cache.rs
1
use dashmap::DashMap;
2
use tokio::sync::broadcast;
3
use unleash_types::{
4
    client_features::{ClientFeature, ClientFeatures, Segment},
5
    Deduplicate,
6
};
7
use unleash_types::client_features::ClientFeaturesDelta;
8
use crate::types::EdgeToken;
9

10
#[derive(Debug, Clone)]
11
pub enum UpdateType {
12
    Full(String),
13
    Update(String),
14
    Deletion,
15
}
16

17
#[derive(Debug, Clone)]
18
pub struct FeatureCache {
19
    features: DashMap<String, ClientFeatures>,
20
    update_sender: broadcast::Sender<UpdateType>,
21
}
22

23
impl FeatureCache {
24
    pub fn new(features: DashMap<String, ClientFeatures>) -> Self {
1✔
25
        let (tx, _rx) = tokio::sync::broadcast::channel::<UpdateType>(16);
2✔
26
        Self {
27
            features,
28
            update_sender: tx,
29
        }
30
    }
31

32
    pub fn len(&self) -> usize {
×
33
        self.features.len()
×
34
    }
35

36
    pub fn subscribe(&self) -> broadcast::Receiver<UpdateType> {
1✔
37
        self.update_sender.subscribe()
1✔
38
    }
39
    pub fn get(&self, key: &str) -> Option<dashmap::mapref::one::Ref<'_, String, ClientFeatures>> {
1✔
40
        self.features.get(key)
1✔
41
    }
42

43
    pub fn insert(&self, key: String, features: ClientFeatures) -> Option<ClientFeatures> {
1✔
44
        let v = self.features.insert(key.clone(), features);
2✔
45
        self.send_full_update(key);
1✔
46
        v
1✔
47
    }
48

49
    pub fn send_full_update(&self, cache_key: String) {
1✔
50
        let _ = self.update_sender.send(UpdateType::Full(cache_key));
1✔
51
    }
52

53
    pub fn remove(&self, key: &str) -> Option<(String, ClientFeatures)> {
×
54
        let v = self.features.remove(key);
×
55
        self.send_full_update(key.to_string());
×
56
        v
×
57
    }
58

59
    pub fn modify(&self, key: String, token: &EdgeToken, features: ClientFeatures) {
×
60
        self.features
×
61
            .entry(key.clone())
×
62
            .and_modify(|existing_features| {
×
63
                let updated = update_client_features(token, existing_features, &features);
×
64
                *existing_features = updated;
×
65
            })
66
            .or_insert(features);
×
67
        self.send_full_update(key);
×
68
    }
69

70
    pub fn apply_delta(&self, key: String, delta: &ClientFeaturesDelta) {
×
71
        let client_features = ClientFeatures {
72
            version : 2,
73
            features : delta.updated.clone(),
×
74
            segments: delta.segments.clone(),
×
75
            query: None,
76
            meta: None,
77
        };
78
        self.features
×
79
            .entry(key.clone())
×
80
            .and_modify(|existing_features| {
×
81
                existing_features.modify_in_place(delta);
×
82
            })
83
            .or_insert(client_features);
×
84
        self.send_full_update(key);
×
85
    }
86

87
    pub fn is_empty(&self) -> bool {
×
88
        self.features.is_empty()
×
89
    }
90

91
    pub fn iter(&self) -> dashmap::iter::Iter<'_, String, ClientFeatures> {
×
92
        self.features.iter()
×
93
    }
94
}
95

96
impl Default for FeatureCache {
97
    fn default() -> Self {
×
98
        FeatureCache::new(DashMap::default())
×
99
    }
100
}
101

102
fn update_client_features(
×
103
    token: &EdgeToken,
104
    old: &ClientFeatures,
105
    update: &ClientFeatures,
106
) -> ClientFeatures {
107
    let mut updated_features =
×
108
        update_projects_from_feature_update(token, &old.features, &update.features);
109
    updated_features.sort();
×
110
    let segments = merge_segments_update(old.segments.clone(), update.segments.clone());
×
111
    ClientFeatures {
112
        version: old.version.max(update.version),
×
113
        features: updated_features,
114
        segments: segments.map(|mut s| {
×
115
            s.sort();
116
            s
117
        }),
118
        query: old.query.clone().or(update.query.clone()),
×
119
        meta: old.meta.clone().or(update.meta.clone()),
×
120
    }
121
}
122

123
pub(crate) fn update_projects_from_feature_update(
×
124
    token: &EdgeToken,
125
    original: &[ClientFeature],
126
    updated: &[ClientFeature],
127
) -> Vec<ClientFeature> {
128
    let projects_to_update = &token.projects;
×
129
    if projects_to_update.contains(&"*".into()) {
×
130
        updated.into()
×
131
    } else {
132
        let mut to_keep: Vec<ClientFeature> = original
×
133
            .iter()
134
            .filter(|toggle| {
×
135
                let p = toggle.project.clone().unwrap_or_else(|| "default".into());
×
136
                !projects_to_update.contains(&p)
×
137
            })
138
            .cloned()
139
            .collect();
140
        to_keep.extend(updated.iter().cloned());
×
141
        to_keep
×
142
    }
143
}
144

145
fn merge_segments_update(
×
146
    segments: Option<Vec<Segment>>,
147
    updated_segments: Option<Vec<Segment>>,
148
) -> Option<Vec<Segment>> {
149
    match (segments, updated_segments) {
×
150
        (Some(s), Some(mut o)) => {
×
151
            o.extend(s);
×
152
            Some(o.deduplicate())
×
153
        }
154
        (Some(s), None) => Some(s),
×
155
        (None, Some(o)) => Some(o),
×
156
        (None, None) => None,
×
157
    }
158
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc