Skip to content

Commit 06e7e06

Browse files
author
Tarik Eshaq
committed
Adds persistence and clean up
1 parent 1ef579b commit 06e7e06

13 files changed

+320
-114
lines changed

Cargo.toml

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[package]
2-
name = "experiments"
2+
name = "nimbus_experiments"
33
version = "0.1.0"
44
authors = ["Tarik Eshaq <[email protected]>"]
55
edition = "2018"
@@ -11,17 +11,24 @@ url = "2.1"
1111
serde = { version = "1", features = ["rc"] }
1212
serde_derive = "1"
1313
serde_json = "1"
14-
# This now references a local path on my own device. If we end up actually using viaduct,
15-
# We should maybe publish it as crate (similar to ffi-support)
16-
viaduct = { path = "../application-services/components/viaduct" }
1714
anyhow = "1.0"
1815
rand = "0.7"
1916
log = "0.4"
17+
viaduct = { git = "https://github.com/mozilla/application-services" }
2018
ffi-support = "0.4"
2119
thiserror = "1"
20+
rkv = "0.10"
21+
lazy_static = "1.4"
22+
uuid = { version = "0.8", features = ["serde", "v4"]}
23+
prost = "0.6"
24+
25+
[build-dependencies]
26+
prost-build = { version = "0.6" }
27+
28+
[lib]
29+
name = "nimbus_experiments"
30+
crate-type = ["lib"]
2231

2332
[dev-dependencies]
24-
# This now references a local path on my own device. If we end up actually using viaduct,
25-
# We should maybe publish it as crate (similar to ffi-support)
26-
viaduct-reqwest = { path = "../application-services/components/support/viaduct-reqwest" }
33+
viaduct-reqwest = { git = "https://github.com/mozilla/application-services" }
2734

build.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
/* This Source Code Form is subject to the terms of the Mozilla Public
2+
* License, v. 2.0. If a copy of the MPL was not distributed with this
3+
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4+
5+
fn main() {
6+
prost_build::compile_protos(&["src/experiments_msg_types.proto"], &["src/"]).unwrap();
7+
}

examples/experiment.rs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,8 @@
11
use anyhow::Result;
2-
use experiments::Experiments;
2+
use nimbus_experiments::{AppContext, Experiments};
33
fn main() -> Result<()> {
44
viaduct_reqwest::use_reqwest_backend();
5-
let exp = Experiments::new(
6-
"https://kinto.dev.mozaws.net/v1/",
7-
"default",
8-
"messaging-collection",
9-
);
5+
let exp = Experiments::new(AppContext::default(), "./mydb");
106
let enrolled_exp = exp.get_enrolled_experiments();
117
exp.get_experiments().iter().for_each(|e| {
128
print!(

ffi/Cargo.toml

Lines changed: 0 additions & 24 deletions
This file was deleted.

ffi/src/lib.rs

Lines changed: 0 additions & 37 deletions
This file was deleted.

src/buckets.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
* License, v. 2.0. If a copy of the MPL was not distributed with this
33
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
44

5-
65
//! This might be where the bucketing logic can go
76
//! It would be different from current experimentation tools
87
//! There is a namespacing concept to allow users to be in multiple

src/error.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@
22
* License, v. 2.0. If a copy of the MPL was not distributed with this
33
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
44

5-
// Not implemented yet!!!
6-
// This is purely boilerplate to communicate over the ffi
5+
//! Not implemented yet!!!
6+
//! This is purely boilerplate to communicate over the ffi
7+
//! We should define real variants for our error and use proper
8+
//! error propegation (we can use the `thiserror` crate for that)
79
use ffi_support::{ErrorCode, ExternError};
810
#[derive(Debug, thiserror::Error)]
911
pub enum Error {

src/experiments.idl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
# This is a test file for defining WebIDL for uniffi
2+
# For the time being, it is not used for anything!
3+
# However, if we use uniffi in the future, we could define
4+
# The api here. (Unless uniffi changes to a non WebIDL way (looking at you proc-macros))
25
namespace experiments {};
36
interface Experiments {
47
constructor();

src/experiments_msg_types.proto

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
syntax = "proto2";
2+
3+
// This kinda beats the purpose of using protobufs since we have one file here/
4+
// And a duplicate in the glean PR, but bear with me :)
5+
// Eventually once we figure out the details of where each part lives, we'll merge the proto files
6+
// into one
7+
8+
package mozilla.telemetry.glean.protobuf;
9+
10+
option java_package = "mozilla.telemtery.glean";
11+
option java_outer_classname = "MsgTypes";
12+
option swift_prefix = "MsgTypes_";
13+
option optimize_for = LITE_RUNTIME;
14+
15+
message AppContext {
16+
optional string app_id = 1;
17+
optional string app_display_version = 2;
18+
optional string app_version = 3;
19+
optional string locale_language = 4;
20+
optional string locale_country = 5;
21+
optional string device_manufacturer = 6;
22+
optional string device_model = 7;
23+
optional string region = 8;
24+
repeated string debug_tags = 9;
25+
}

src/ffi.rs

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/* This Source Code Form is subject to the terms of the Mozilla Public
2+
* License, v. 2.0. If a copy of the MPL was not distributed with this
3+
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4+
5+
use std::os::raw::c_char;
6+
7+
use super::{error::Result, msg_types, AppContext, Experiments};
8+
use ffi_support::{define_handle_map_deleter, ConcurrentHandleMap, ExternError, FfiStr};
9+
10+
lazy_static::lazy_static! {
11+
static ref EXPERIMENTS: ConcurrentHandleMap<Experiments> = ConcurrentHandleMap::new();
12+
}
13+
14+
#[no_mangle]
15+
pub extern "C" fn experiments_new(
16+
app_ctx: *const u8,
17+
app_ctx_len: i32,
18+
db_path: FfiStr<'_>,
19+
error: &mut ExternError,
20+
) -> u64 {
21+
EXPERIMENTS.insert_with_result(error, || -> Result<Experiments> {
22+
let app_ctx = unsafe {
23+
from_protobuf_ptr::<AppContext, msg_types::AppContext>(app_ctx, app_ctx_len).unwrap()
24+
}; // Todo: make the whole function unsafe and implement proper error handling in error.rs
25+
log::info!("=================== Initializing experiments ========================");
26+
Ok(Experiments::new(app_ctx, db_path.as_str()))
27+
})
28+
}
29+
30+
#[no_mangle]
31+
pub extern "C" fn experiments_get_branch(
32+
handle: u64,
33+
branch: FfiStr<'_>,
34+
error: &mut ExternError,
35+
) -> *mut c_char {
36+
EXPERIMENTS.call_with_result(error, handle, |experiment| -> Result<String> {
37+
log::info!("==================== Getting branch ========================");
38+
let branch_name = experiment.get_experiment_branch(branch.as_str())?;
39+
Ok(branch_name)
40+
})
41+
}
42+
43+
define_handle_map_deleter!(EXPERIMENTS, experiements_destroy);
44+
45+
/// # Safety
46+
/// data is a raw pointer to the protobuf data
47+
/// get_buffer will return an error if the length is invalid,
48+
/// or if the pointer is a null pointer
49+
pub unsafe fn from_protobuf_ptr<T, F: prost::Message + Default + Into<T>>(
50+
data: *const u8,
51+
len: i32,
52+
) -> anyhow::Result<T> {
53+
let buffer = get_buffer(data, len)?;
54+
let item: Result<F, _> = prost::Message::decode(buffer);
55+
item.map(|inner| inner.into()).map_err(|e| e.into())
56+
}
57+
58+
unsafe fn get_buffer<'a>(data: *const u8, len: i32) -> anyhow::Result<&'a [u8]> {
59+
match len {
60+
len if len < 0 => anyhow::bail!("Invalid length"),
61+
0 => Ok(&[]),
62+
_ => {
63+
if data.is_null() {
64+
anyhow::bail!("Null pointer")
65+
}
66+
Ok(std::slice::from_raw_parts(data, len as usize))
67+
}
68+
}
69+
}

src/http_client.rs

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22
* License, v. 2.0. If a copy of the MPL was not distributed with this
33
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
44

5+
//! This is a simple Http client that uses viaduct to retrieve experiment data from the server
6+
//! Currently configured to use Kinto and the old schema, although that would change once we start
7+
//! Working on the real Nimbus schema.
8+
59
use super::Experiment;
610
use anyhow::Result;
711
use serde_derive::*;
@@ -66,13 +70,11 @@ impl SettingsClient for Client {
6670
&self.bucket_name, &self.collection_name
6771
);
6872
let url = self.base_url.join(&path)?;
69-
let req = Request::get(url)
70-
.header(
71-
"User-Agent",
72-
"Experiments Rust Component <[email protected]>",
73-
)?;
74-
// Note: I removed the auth info which was for a test account that is public
75-
// But gitgaurdian complained so I removed it.
73+
let req = Request::get(url).header(
74+
"User-Agent",
75+
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:77.0) Gecko/20100101 Firefox/77.0",
76+
)?;
77+
// TODO: Add authentication based on server requirements
7678
let resp = self.make_request(req)?.json::<RecordsResponse>()?;
7779
Ok(resp.data)
7880
}

0 commit comments

Comments
 (0)