Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: new component configuration #199

Merged
merged 5 commits into from
Feb 4, 2025
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
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -223,9 +223,9 @@ You simply need to add a new session to your configuration pointing to the WebAs
```toml
# edgee.toml
[[components.data_collection]]
name = "amplitude"
component = "/var/edgee/wasm/amplitude.wasm"
credentials.amplitude_api_key = "YOUR-API-KEY"
id = "amplitude"
file = "/var/edgee/wasm/amplitude.wasm"
settings.amplitude_api_key = "YOUR-API-KEY"
```

### Debugging a component
Expand Down
22 changes: 17 additions & 5 deletions crates/api-client/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -2887,6 +2887,11 @@
"description": "Unique identifier for the component",
"example": "d290f1ee-6c54-4b01-90e6-d701748f0851"
},
"component_slug": {
"type": "string",
"description": "Slug of the component",
"example": "edgee/google_analytics"
},
"component_version": {
"type": "string",
"description": "Version of the component",
Expand All @@ -2896,6 +2901,12 @@
"type": "string",
"description": "Category of the component",
"readOnly": true,
"example": "data_collection"
},
"subcategory": {
"type": "string",
"description": "Subcategory of the component",
"readOnly": true,
"example": "analytics"
},
"active": {
Expand All @@ -2905,7 +2916,8 @@
},
"settings": {
"type": "object",
"description": "Settings of the component"
"description": "Settings of the component",
"nullable": true
}
}
},
Expand Down Expand Up @@ -2944,7 +2956,7 @@
"description": "Category of the component",
"readOnly": true
},
"sub_category": {
"subcategory": {
"type": "string",
"description": "Subcategory of the component",
"readOnly": true
Expand Down Expand Up @@ -3080,7 +3092,7 @@
"data_collection"
]
},
"sub_category": {
"subcategory": {
"type": "string",
"description": "Subcategory of the component.",
"enum": [
Expand Down Expand Up @@ -3108,7 +3120,7 @@
"organization_id",
"name",
"category",
"sub_category"
"subcategory"
]
},
"ComponentUpdateParams" : {
Expand Down Expand Up @@ -3192,7 +3204,7 @@
"data_collection"
]
},
"sub_category": {
"subcategory": {
"type": "string",
"description": "Filter components by sub category.",
"enum": [
Expand Down
43 changes: 23 additions & 20 deletions crates/components-runtime/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,34 +13,37 @@ pub struct ComponentsConfiguration {

#[derive(Deserialize, Debug, Default, Clone)]
pub struct DataCollectionComponents {
#[serde(skip_deserializing)]
pub project_component_id: String,
#[serde(skip_deserializing)]
pub slug: String,
pub id: String, // could be a slug (edgee/amplitude) or an alias (amplitude)
pub file: String,
#[serde(default)]
pub id: String,
pub name: String,
pub component: String,
pub credentials: HashMap<String, String>,
#[serde(default)]
pub config: DataCollectionComponentConfig,
pub forward_client_headers: Option<bool>,
pub settings: DataCollectionComponentSettings,
}

#[derive(Deserialize, Debug, Clone)]
#[serde(default)]
pub struct DataCollectionComponentConfig {
pub anonymization: bool,
pub default_consent: String,
pub track_event_enabled: bool,
pub user_event_enabled: bool,
pub page_event_enabled: bool,
pub struct DataCollectionComponentSettings {
pub edgee_anonymization: bool,
pub edgee_default_consent: String,
pub edgee_track_event_enabled: bool,
pub edgee_user_event_enabled: bool,
pub edgee_page_event_enabled: bool,
#[serde(flatten)]
pub additional_settings: HashMap<String, String>,
}

impl Default for DataCollectionComponentConfig {
impl Default for DataCollectionComponentSettings {
fn default() -> Self {
DataCollectionComponentConfig {
anonymization: true,
default_consent: "pending".to_string(),
track_event_enabled: true,
user_event_enabled: true,
page_event_enabled: true,
DataCollectionComponentSettings {
edgee_anonymization: true,
edgee_default_consent: "pending".to_string(),
edgee_track_event_enabled: true,
edgee_user_event_enabled: true,
edgee_page_event_enabled: true,
additional_settings: HashMap::new(),
}
}
}
Expand Down
32 changes: 17 additions & 15 deletions crates/components-runtime/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,18 +41,18 @@ impl ComponentsContext {
.data_collection
.iter()
.map(|entry| {
let span = tracing::info_span!("component-context", component = %entry.name, category = "data-collection");
let span = tracing::info_span!("component-context", component = %entry.id, category = "data-collection");
let _span = span.enter();

tracing::debug!("Start pre-instantiate data collection component");

let component = Component::from_file(&engine, &entry.component)?;
let component = Component::from_file(&engine, &entry.file)?;
let instance_pre = linker.instantiate_pre(&component)?;
let instance_pre = DataCollectionPre::new(instance_pre)?;

tracing::debug!("Finished pre-instantiate data collection component");

Ok((entry.name.clone(), instance_pre))
Ok((entry.id.clone(), instance_pre))
})
.collect::<anyhow::Result<_>>()?;

Expand Down Expand Up @@ -90,33 +90,35 @@ impl ComponentsContext {

pub async fn get_data_collection_instance(
&self,
name: &str,
id: &str,
store: &mut Store<HostState>,
) -> anyhow::Result<DataCollection> {
let instance_pre = self
.components
.data_collection
.get(name)
.expect("Data collection component not found");
let instance_pre = self.components.data_collection.get(id);

if instance_pre.is_none() {
return Err(anyhow::anyhow!("component not found: {}", id));
}

instance_pre
.unwrap()
.instantiate_async(store)
.await
.map_err(Into::into)
}

pub async fn get_consent_mapping_instance(
&self,
name: &str,
id: &str,
store: &mut Store<HostState>,
) -> anyhow::Result<ConsentMapping> {
let instance_pre = self
.components
.consent_mapping
.get(name)
.expect("Consent mapping component not found");
let instance_pre = self.components.consent_mapping.get(id);

if instance_pre.is_none() {
return Err(anyhow::anyhow!("component not found: {}", id));
}

instance_pre
.unwrap()
.instantiate_async(store)
.await
.map_err(Into::into)
Expand Down
4 changes: 2 additions & 2 deletions crates/components-runtime/src/data_collection/debug.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ pub struct DebugParams {
impl DebugParams {
pub fn new(
ctx: &EventContext,
component_id: &str,
project_component_id: &str,
component_slug: &str,
event: &Event,
request: &EdgeeRequest,
Expand All @@ -106,7 +106,7 @@ impl DebugParams {
DebugParams {
from: ctx.get_from().clone(),
project_id: ctx.get_project_id().clone(),
component_id: component_id.to_string(),
component_id: project_component_id.to_string(),
component_slug: component_slug.to_string(),
event: event.clone(),
request: request.clone(),
Expand Down
53 changes: 33 additions & 20 deletions crates/components-runtime/src/data_collection/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,44 +73,44 @@ pub async fn send_events(
let span = span!(
Level::INFO,
"component",
name = cfg.name.as_str(),
name = cfg.id.as_str(),
event = ?event.event_type
);
let _enter = span.enter();

let mut event = event.clone();

let trace =
trace_component.is_some() && trace_component.as_ref().unwrap() == cfg.name.as_str();
trace_component.is_some() && trace_component.as_ref().unwrap() == cfg.id.as_str();

// if event_type is not enabled in config.config.get(component_id).unwrap(), skip the event
match event.event_type {
EventType::Page => {
if !cfg.config.page_event_enabled {
if !cfg.settings.edgee_page_event_enabled {
trace_disabled_event(trace, "page");
continue;
}
}
EventType::User => {
if !cfg.config.user_event_enabled {
if !cfg.settings.edgee_user_event_enabled {
trace_disabled_event(trace, "user");
continue;
}
}
EventType::Track => {
if !cfg.config.track_event_enabled {
if !cfg.settings.edgee_track_event_enabled {
trace_disabled_event(trace, "track");
continue;
}
}
}

if !event.is_component_enabled(&cfg.name) {
if !event.is_component_enabled(cfg) {
continue;
}

let initial_anonymization = cfg.config.anonymization;
let default_consent = cfg.config.default_consent.clone();
let initial_anonymization = cfg.settings.edgee_anonymization;
let default_consent = cfg.settings.edgee_default_consent.clone();

// Use the helper function to handle consent and determine anonymization
let (anonymization, outgoing_consent) = handle_consent_and_anonymization(
Expand All @@ -128,8 +128,9 @@ pub async fn send_events(

// Native cookie support
if let Some(ref ids) = event.context.user.native_cookie_ids {
if ids.contains_key(&cfg.id) {
event.context.user.edgee_id = ids.get(&cfg.id).unwrap().clone();
if ids.contains_key(&cfg.project_component_id) {
event.context.user.edgee_id =
ids.get(&cfg.project_component_id).unwrap().clone();
} else {
event.context.user.edgee_id = ctx.get_edgee_id().clone();
}
Expand All @@ -142,29 +143,41 @@ pub async fn send_events(
}

// get the instance of the component
let instance = component_ctx
.get_data_collection_instance(&cfg.name, &mut store)
.await?;
let instance = match component_ctx
.get_data_collection_instance(&cfg.id, &mut store)
.await
{
Ok(instance) => instance,
Err(err) => {
error!("Failed to get data collection instance. Error: {}", err);
continue;
}
};
let component = instance.edgee_protocols_data_collection();

let component_event: Component::Event = event.clone().into();
let credentials: Vec<(String, String)> = cfg.credentials.clone().into_iter().collect();
let component_settings: Vec<(String, String)> = cfg
.settings
.additional_settings
.clone()
.into_iter()
.collect();

// call the corresponding method of the component
let request = match component_event.event_type {
Component::EventType::Page => {
component
.call_page(&mut store, &component_event, &credentials)
.call_page(&mut store, &component_event, &component_settings)
.await
}
Component::EventType::Track => {
component
.call_track(&mut store, &component_event, &credentials)
.call_track(&mut store, &component_event, &component_settings)
.await
}
Component::EventType::User => {
component
.call_user(&mut store, &component_event, &credentials)
.call_user(&mut store, &component_event, &component_settings)
.await
}
};
Expand Down Expand Up @@ -195,7 +208,7 @@ pub async fn send_events(
headers.insert(HeaderName::from_str(key)?, HeaderValue::from_str(value)?);
}

if cfg.forward_client_headers.unwrap_or(true) {
if request.forward_client_headers {
insert_expected_headers(&mut headers, &event, &component_event)?;
}

Expand All @@ -206,8 +219,8 @@ pub async fn send_events(
trace_request(trace, &request, &headers, &outgoing_consent, anonymization);

// spawn a separated async thread
let cfg_project_component_id = cfg.project_component_id.to_string();
let cfg_id = cfg.id.to_string();
let cfg_name = cfg.name.to_string();
let ctx_clone = ctx.clone();

tokio::spawn(
Expand Down Expand Up @@ -241,8 +254,8 @@ pub async fn send_events(

let mut debug_params = DebugParams::new(
&ctx_clone,
&cfg_project_component_id,
&cfg_id,
&cfg_name,
&event,
&request_clone,
timer,
Expand Down
Loading