mirror of
https://github.com/chirpstack/chirpstack.git
synced 2025-04-24 21:10:03 +00:00
702 lines
29 KiB
Rust
702 lines
29 KiB
Rust
use std::str::FromStr;
|
|
|
|
use tonic::{Request, Response, Status};
|
|
use uuid::Uuid;
|
|
|
|
use chirpstack_api::api;
|
|
use chirpstack_api::api::device_profile_service_server::DeviceProfileService;
|
|
|
|
use super::auth::validator;
|
|
use super::error::ToStatus;
|
|
use super::helpers;
|
|
use super::helpers::{FromProto, ToProto};
|
|
use crate::adr;
|
|
use crate::storage::{device_profile, fields};
|
|
|
|
pub struct DeviceProfile {
|
|
validator: validator::RequestValidator,
|
|
}
|
|
|
|
impl DeviceProfile {
|
|
pub fn new(validator: validator::RequestValidator) -> Self {
|
|
DeviceProfile { validator }
|
|
}
|
|
}
|
|
|
|
#[tonic::async_trait]
|
|
impl DeviceProfileService for DeviceProfile {
|
|
async fn create(
|
|
&self,
|
|
request: Request<api::CreateDeviceProfileRequest>,
|
|
) -> Result<Response<api::CreateDeviceProfileResponse>, Status> {
|
|
let req_dp = match &request.get_ref().device_profile {
|
|
Some(v) => v,
|
|
None => {
|
|
return Err(Status::invalid_argument("device_profile is missing"));
|
|
}
|
|
};
|
|
let tenant_id = Uuid::from_str(&req_dp.tenant_id).map_err(|e| e.status())?;
|
|
|
|
self.validator
|
|
.validate(
|
|
request.extensions(),
|
|
validator::ValidateDeviceProfilesAccess::new(validator::Flag::Create, tenant_id),
|
|
)
|
|
.await?;
|
|
|
|
let mut dp = device_profile::DeviceProfile {
|
|
tenant_id: tenant_id.into(),
|
|
name: req_dp.name.clone(),
|
|
description: req_dp.description.clone(),
|
|
region: req_dp.region().from_proto(),
|
|
mac_version: req_dp.mac_version().from_proto(),
|
|
reg_params_revision: req_dp.reg_params_revision().from_proto(),
|
|
adr_algorithm_id: req_dp.adr_algorithm_id.clone(),
|
|
payload_codec_runtime: req_dp.payload_codec_runtime().from_proto(),
|
|
payload_codec_script: req_dp.payload_codec_script.clone(),
|
|
flush_queue_on_activate: req_dp.flush_queue_on_activate,
|
|
uplink_interval: req_dp.uplink_interval as i32,
|
|
device_status_req_interval: req_dp.device_status_req_interval as i32,
|
|
supports_otaa: req_dp.supports_otaa,
|
|
supports_class_b: req_dp.supports_class_b,
|
|
supports_class_c: req_dp.supports_class_c,
|
|
tags: fields::KeyValue::new(req_dp.tags.clone()),
|
|
measurements: fields::Measurements::new(
|
|
req_dp
|
|
.measurements
|
|
.iter()
|
|
.map(|(k, v)| {
|
|
(
|
|
k.to_string(),
|
|
fields::Measurement {
|
|
name: v.name.clone(),
|
|
kind: v.kind().from_proto(),
|
|
},
|
|
)
|
|
})
|
|
.collect(),
|
|
),
|
|
auto_detect_measurements: req_dp.auto_detect_measurements,
|
|
region_config_id: (!req_dp.region_config_id.is_empty())
|
|
.then(|| req_dp.region_config_id.clone()),
|
|
allow_roaming: req_dp.allow_roaming,
|
|
rx1_delay: req_dp.rx1_delay as i16,
|
|
abp_params: if req_dp.supports_otaa {
|
|
None
|
|
} else {
|
|
Some(fields::AbpParams {
|
|
rx1_delay: req_dp.abp_rx1_delay as u8,
|
|
rx1_dr_offset: req_dp.abp_rx1_dr_offset as u8,
|
|
rx2_dr: req_dp.abp_rx2_dr as u8,
|
|
rx2_freq: req_dp.abp_rx2_freq as u32,
|
|
})
|
|
},
|
|
class_b_params: if req_dp.supports_class_b {
|
|
Some(fields::ClassBParams {
|
|
timeout: req_dp.class_b_timeout as u16,
|
|
ping_slot_nb_k: req_dp.class_b_ping_slot_nb_k as u8,
|
|
ping_slot_dr: req_dp.class_b_ping_slot_dr as u8,
|
|
ping_slot_freq: req_dp.class_b_ping_slot_freq as u32,
|
|
})
|
|
} else {
|
|
None
|
|
},
|
|
class_c_params: if req_dp.supports_class_c {
|
|
Some(fields::ClassCParams {
|
|
timeout: req_dp.class_c_timeout as u16,
|
|
})
|
|
} else {
|
|
None
|
|
},
|
|
relay_params: if req_dp.is_relay || req_dp.is_relay_ed {
|
|
Some(fields::RelayParams {
|
|
is_relay: req_dp.is_relay,
|
|
is_relay_ed: req_dp.is_relay_ed,
|
|
ed_relay_only: req_dp.relay_ed_relay_only,
|
|
relay_enabled: req_dp.relay_enabled,
|
|
relay_cad_periodicity: req_dp.relay_cad_periodicity as u8,
|
|
default_channel_index: req_dp.relay_default_channel_index as u8,
|
|
second_channel_freq: req_dp.relay_second_channel_freq as u32,
|
|
second_channel_dr: req_dp.relay_second_channel_dr as u8,
|
|
second_channel_ack_offset: req_dp.relay_second_channel_ack_offset as u8,
|
|
ed_activation_mode: req_dp.relay_ed_activation_mode().from_proto(),
|
|
ed_smart_enable_level: req_dp.relay_ed_smart_enable_level as u8,
|
|
ed_back_off: req_dp.relay_ed_back_off as u8,
|
|
ed_uplink_limit_bucket_size: req_dp.relay_ed_uplink_limit_bucket_size as u8,
|
|
ed_uplink_limit_reload_rate: req_dp.relay_ed_uplink_limit_reload_rate as u8,
|
|
relay_join_req_limit_reload_rate: req_dp.relay_join_req_limit_reload_rate as u8,
|
|
relay_notify_limit_reload_rate: req_dp.relay_notify_limit_reload_rate as u8,
|
|
relay_global_uplink_limit_reload_rate: req_dp
|
|
.relay_global_uplink_limit_reload_rate
|
|
as u8,
|
|
relay_overall_limit_reload_rate: req_dp.relay_overall_limit_reload_rate as u8,
|
|
relay_join_req_limit_bucket_size: req_dp.relay_join_req_limit_bucket_size as u8,
|
|
relay_notify_limit_bucket_size: req_dp.relay_notify_limit_bucket_size as u8,
|
|
relay_global_uplink_limit_bucket_size: req_dp
|
|
.relay_global_uplink_limit_bucket_size
|
|
as u8,
|
|
relay_overall_limit_bucket_size: req_dp.relay_overall_limit_bucket_size as u8,
|
|
})
|
|
} else {
|
|
None
|
|
},
|
|
app_layer_params: {
|
|
let app_layer_params = req_dp.app_layer_params.unwrap_or_default();
|
|
|
|
fields::AppLayerParams {
|
|
ts003_version: app_layer_params.ts003_version().from_proto(),
|
|
ts004_version: app_layer_params.ts004_version().from_proto(),
|
|
ts005_version: app_layer_params.ts005_version().from_proto(),
|
|
..Default::default()
|
|
}
|
|
},
|
|
..Default::default()
|
|
};
|
|
|
|
dp = device_profile::create(dp).await.map_err(|e| e.status())?;
|
|
|
|
let mut resp = Response::new(api::CreateDeviceProfileResponse {
|
|
id: dp.id.to_string(),
|
|
});
|
|
resp.metadata_mut().insert(
|
|
"x-log-device_profile_id",
|
|
dp.id.to_string().parse().unwrap(),
|
|
);
|
|
|
|
Ok(resp)
|
|
}
|
|
|
|
async fn get(
|
|
&self,
|
|
request: Request<api::GetDeviceProfileRequest>,
|
|
) -> Result<Response<api::GetDeviceProfileResponse>, Status> {
|
|
let req = request.get_ref();
|
|
let dp_id = Uuid::from_str(&req.id).map_err(|e| e.status())?;
|
|
|
|
self.validator
|
|
.validate(
|
|
request.extensions(),
|
|
validator::ValidateDeviceProfileAccess::new(validator::Flag::Read, dp_id),
|
|
)
|
|
.await?;
|
|
|
|
let dp = device_profile::get(&dp_id).await.map_err(|e| e.status())?;
|
|
let abp_params = dp.abp_params.clone().unwrap_or_default();
|
|
let class_b_params = dp.class_b_params.clone().unwrap_or_default();
|
|
let class_c_params = dp.class_c_params.clone().unwrap_or_default();
|
|
let relay_params = dp.relay_params.clone().unwrap_or_default();
|
|
|
|
let mut resp = Response::new(api::GetDeviceProfileResponse {
|
|
device_profile: Some(api::DeviceProfile {
|
|
id: dp.id.to_string(),
|
|
tenant_id: dp.tenant_id.to_string(),
|
|
name: dp.name,
|
|
description: dp.description,
|
|
region: dp.region.to_proto().into(),
|
|
mac_version: dp.mac_version.to_proto().into(),
|
|
reg_params_revision: dp.reg_params_revision.to_proto().into(),
|
|
adr_algorithm_id: dp.adr_algorithm_id,
|
|
payload_codec_runtime: dp.payload_codec_runtime.to_proto().into(),
|
|
payload_codec_script: dp.payload_codec_script,
|
|
flush_queue_on_activate: dp.flush_queue_on_activate,
|
|
uplink_interval: dp.uplink_interval as u32,
|
|
device_status_req_interval: dp.device_status_req_interval as u32,
|
|
supports_otaa: dp.supports_otaa,
|
|
supports_class_b: dp.supports_class_b,
|
|
supports_class_c: dp.supports_class_c,
|
|
class_b_timeout: class_b_params.timeout as u32,
|
|
class_b_ping_slot_nb_k: class_b_params.ping_slot_nb_k as u32,
|
|
class_b_ping_slot_dr: class_b_params.ping_slot_dr as u32,
|
|
class_b_ping_slot_freq: class_b_params.ping_slot_freq as u32,
|
|
class_c_timeout: class_c_params.timeout as u32,
|
|
abp_rx1_delay: abp_params.rx1_delay as u32,
|
|
abp_rx1_dr_offset: abp_params.rx1_dr_offset as u32,
|
|
abp_rx2_dr: abp_params.rx2_dr as u32,
|
|
abp_rx2_freq: abp_params.rx2_freq as u32,
|
|
tags: dp.tags.into_hashmap(),
|
|
measurements: dp
|
|
.measurements
|
|
.into_hashmap()
|
|
.iter()
|
|
.map(|(k, v)| {
|
|
(
|
|
k.to_string(),
|
|
api::Measurement {
|
|
name: v.name.clone(),
|
|
kind: v.kind.to_proto().into(),
|
|
},
|
|
)
|
|
})
|
|
.collect(),
|
|
auto_detect_measurements: dp.auto_detect_measurements,
|
|
region_config_id: dp.region_config_id.clone().unwrap_or_default(),
|
|
is_relay: relay_params.is_relay,
|
|
is_relay_ed: relay_params.is_relay_ed,
|
|
relay_ed_relay_only: relay_params.ed_relay_only,
|
|
relay_enabled: relay_params.relay_enabled,
|
|
relay_cad_periodicity: relay_params.relay_cad_periodicity as i32,
|
|
relay_default_channel_index: relay_params.default_channel_index as u32,
|
|
relay_second_channel_freq: relay_params.second_channel_freq as u32,
|
|
relay_second_channel_dr: relay_params.second_channel_dr as u32,
|
|
relay_second_channel_ack_offset: relay_params.second_channel_ack_offset as i32,
|
|
relay_ed_activation_mode: relay_params.ed_activation_mode.to_proto().into(),
|
|
relay_ed_smart_enable_level: relay_params.ed_smart_enable_level as u32,
|
|
relay_ed_back_off: relay_params.ed_back_off as u32,
|
|
relay_ed_uplink_limit_bucket_size: relay_params.ed_uplink_limit_bucket_size as u32,
|
|
relay_ed_uplink_limit_reload_rate: relay_params.ed_uplink_limit_reload_rate as u32,
|
|
relay_join_req_limit_reload_rate: relay_params.relay_join_req_limit_reload_rate
|
|
as u32,
|
|
relay_notify_limit_reload_rate: relay_params.relay_notify_limit_reload_rate as u32,
|
|
relay_global_uplink_limit_reload_rate: relay_params
|
|
.relay_global_uplink_limit_reload_rate
|
|
as u32,
|
|
relay_overall_limit_reload_rate: relay_params.relay_overall_limit_reload_rate
|
|
as u32,
|
|
relay_join_req_limit_bucket_size: relay_params.relay_join_req_limit_bucket_size
|
|
as u32,
|
|
relay_notify_limit_bucket_size: relay_params.relay_notify_limit_bucket_size as u32,
|
|
relay_global_uplink_limit_bucket_size: relay_params
|
|
.relay_global_uplink_limit_bucket_size
|
|
as u32,
|
|
relay_overall_limit_bucket_size: relay_params.relay_overall_limit_bucket_size
|
|
as u32,
|
|
allow_roaming: dp.allow_roaming,
|
|
rx1_delay: dp.rx1_delay as u32,
|
|
app_layer_params: Some(api::AppLayerParams {
|
|
ts003_version: dp.app_layer_params.ts003_version.to_proto().into(),
|
|
ts003_f_port: dp.app_layer_params.ts003_f_port as u32,
|
|
ts004_version: dp.app_layer_params.ts004_version.to_proto().into(),
|
|
ts004_f_port: dp.app_layer_params.ts004_f_port as u32,
|
|
ts005_version: dp.app_layer_params.ts005_version.to_proto().into(),
|
|
ts005_f_port: dp.app_layer_params.ts005_f_port as u32,
|
|
}),
|
|
}),
|
|
created_at: Some(helpers::datetime_to_prost_timestamp(&dp.created_at)),
|
|
updated_at: Some(helpers::datetime_to_prost_timestamp(&dp.updated_at)),
|
|
});
|
|
resp.metadata_mut()
|
|
.insert("x-log-device_profile_id", req.id.parse().unwrap());
|
|
|
|
Ok(resp)
|
|
}
|
|
|
|
async fn update(
|
|
&self,
|
|
request: Request<api::UpdateDeviceProfileRequest>,
|
|
) -> Result<Response<()>, Status> {
|
|
let req_dp = match &request.get_ref().device_profile {
|
|
Some(v) => v,
|
|
None => {
|
|
return Err(Status::invalid_argument("device_profile is missing"));
|
|
}
|
|
};
|
|
let dp_id = Uuid::from_str(&req_dp.id).map_err(|e| e.status())?;
|
|
|
|
self.validator
|
|
.validate(
|
|
request.extensions(),
|
|
validator::ValidateDeviceProfileAccess::new(validator::Flag::Update, dp_id),
|
|
)
|
|
.await?;
|
|
|
|
// update
|
|
let _ = device_profile::update(device_profile::DeviceProfile {
|
|
id: dp_id.into(),
|
|
name: req_dp.name.clone(),
|
|
description: req_dp.description.clone(),
|
|
region: req_dp.region().from_proto(),
|
|
mac_version: req_dp.mac_version().from_proto(),
|
|
reg_params_revision: req_dp.reg_params_revision().from_proto(),
|
|
adr_algorithm_id: req_dp.adr_algorithm_id.clone(),
|
|
payload_codec_runtime: req_dp.payload_codec_runtime().from_proto(),
|
|
payload_codec_script: req_dp.payload_codec_script.clone(),
|
|
flush_queue_on_activate: req_dp.flush_queue_on_activate,
|
|
uplink_interval: req_dp.uplink_interval as i32,
|
|
device_status_req_interval: req_dp.device_status_req_interval as i32,
|
|
supports_otaa: req_dp.supports_otaa,
|
|
supports_class_b: req_dp.supports_class_b,
|
|
supports_class_c: req_dp.supports_class_c,
|
|
tags: fields::KeyValue::new(req_dp.tags.clone()),
|
|
measurements: fields::Measurements::new(
|
|
req_dp
|
|
.measurements
|
|
.iter()
|
|
.map(|(k, v)| {
|
|
(
|
|
k.to_string(),
|
|
fields::Measurement {
|
|
name: v.name.clone(),
|
|
kind: v.kind().from_proto(),
|
|
},
|
|
)
|
|
})
|
|
.collect(),
|
|
),
|
|
auto_detect_measurements: req_dp.auto_detect_measurements,
|
|
region_config_id: (!req_dp.region_config_id.is_empty())
|
|
.then(|| req_dp.region_config_id.clone()),
|
|
allow_roaming: req_dp.allow_roaming,
|
|
rx1_delay: req_dp.rx1_delay as i16,
|
|
abp_params: if req_dp.supports_otaa {
|
|
None
|
|
} else {
|
|
Some(fields::AbpParams {
|
|
rx1_delay: req_dp.abp_rx1_delay as u8,
|
|
rx1_dr_offset: req_dp.abp_rx1_dr_offset as u8,
|
|
rx2_dr: req_dp.abp_rx2_dr as u8,
|
|
rx2_freq: req_dp.abp_rx2_freq as u32,
|
|
})
|
|
},
|
|
class_b_params: if req_dp.supports_class_b {
|
|
Some(fields::ClassBParams {
|
|
timeout: req_dp.class_b_timeout as u16,
|
|
ping_slot_nb_k: req_dp.class_b_ping_slot_nb_k as u8,
|
|
ping_slot_dr: req_dp.class_b_ping_slot_dr as u8,
|
|
ping_slot_freq: req_dp.class_b_ping_slot_freq as u32,
|
|
})
|
|
} else {
|
|
None
|
|
},
|
|
class_c_params: if req_dp.supports_class_c {
|
|
Some(fields::ClassCParams {
|
|
timeout: req_dp.class_c_timeout as u16,
|
|
})
|
|
} else {
|
|
None
|
|
},
|
|
relay_params: if req_dp.is_relay || req_dp.is_relay_ed {
|
|
Some(fields::RelayParams {
|
|
is_relay: req_dp.is_relay,
|
|
is_relay_ed: req_dp.is_relay_ed,
|
|
ed_relay_only: req_dp.relay_ed_relay_only,
|
|
relay_enabled: req_dp.relay_enabled,
|
|
relay_cad_periodicity: req_dp.relay_cad_periodicity as u8,
|
|
default_channel_index: req_dp.relay_default_channel_index as u8,
|
|
second_channel_freq: req_dp.relay_second_channel_freq as u32,
|
|
second_channel_dr: req_dp.relay_second_channel_dr as u8,
|
|
second_channel_ack_offset: req_dp.relay_second_channel_ack_offset as u8,
|
|
ed_activation_mode: req_dp.relay_ed_activation_mode().from_proto(),
|
|
ed_smart_enable_level: req_dp.relay_ed_smart_enable_level as u8,
|
|
ed_back_off: req_dp.relay_ed_back_off as u8,
|
|
ed_uplink_limit_bucket_size: req_dp.relay_ed_uplink_limit_bucket_size as u8,
|
|
ed_uplink_limit_reload_rate: req_dp.relay_ed_uplink_limit_reload_rate as u8,
|
|
relay_join_req_limit_reload_rate: req_dp.relay_join_req_limit_reload_rate as u8,
|
|
relay_notify_limit_reload_rate: req_dp.relay_notify_limit_reload_rate as u8,
|
|
relay_global_uplink_limit_reload_rate: req_dp
|
|
.relay_global_uplink_limit_reload_rate
|
|
as u8,
|
|
relay_overall_limit_reload_rate: req_dp.relay_overall_limit_reload_rate as u8,
|
|
relay_join_req_limit_bucket_size: req_dp.relay_join_req_limit_bucket_size as u8,
|
|
relay_notify_limit_bucket_size: req_dp.relay_notify_limit_bucket_size as u8,
|
|
relay_global_uplink_limit_bucket_size: req_dp
|
|
.relay_global_uplink_limit_bucket_size
|
|
as u8,
|
|
relay_overall_limit_bucket_size: req_dp.relay_overall_limit_bucket_size as u8,
|
|
})
|
|
} else {
|
|
None
|
|
},
|
|
app_layer_params: {
|
|
let app_layer_params = req_dp.app_layer_params.unwrap_or_default();
|
|
|
|
fields::AppLayerParams {
|
|
ts003_version: app_layer_params.ts003_version().from_proto(),
|
|
ts003_f_port: app_layer_params.ts003_f_port as u8,
|
|
ts004_version: app_layer_params.ts004_version().from_proto(),
|
|
ts004_f_port: app_layer_params.ts004_f_port as u8,
|
|
ts005_version: app_layer_params.ts005_version().from_proto(),
|
|
ts005_f_port: app_layer_params.ts005_f_port as u8,
|
|
..Default::default()
|
|
}
|
|
},
|
|
..Default::default()
|
|
})
|
|
.await
|
|
.map_err(|e| e.status())?;
|
|
|
|
let mut resp = Response::new(());
|
|
resp.metadata_mut()
|
|
.insert("x-log-device_profile_id", req_dp.id.parse().unwrap());
|
|
|
|
Ok(resp)
|
|
}
|
|
|
|
async fn delete(
|
|
&self,
|
|
request: Request<api::DeleteDeviceProfileRequest>,
|
|
) -> Result<Response<()>, Status> {
|
|
let req = request.get_ref();
|
|
let dp_id = Uuid::from_str(&req.id).map_err(|e| e.status())?;
|
|
|
|
self.validator
|
|
.validate(
|
|
request.extensions(),
|
|
validator::ValidateDeviceProfileAccess::new(validator::Flag::Delete, dp_id),
|
|
)
|
|
.await?;
|
|
|
|
device_profile::delete(&dp_id)
|
|
.await
|
|
.map_err(|e| e.status())?;
|
|
|
|
let mut resp = Response::new(());
|
|
resp.metadata_mut()
|
|
.insert("x-log-device_profile_id", req.id.parse().unwrap());
|
|
|
|
Ok(resp)
|
|
}
|
|
|
|
async fn list(
|
|
&self,
|
|
request: Request<api::ListDeviceProfilesRequest>,
|
|
) -> Result<Response<api::ListDeviceProfilesResponse>, Status> {
|
|
let req = request.get_ref();
|
|
let tenant_id = Uuid::from_str(&req.tenant_id).map_err(|e| e.status())?;
|
|
|
|
self.validator
|
|
.validate(
|
|
request.extensions(),
|
|
validator::ValidateDeviceProfilesAccess::new(validator::Flag::List, tenant_id),
|
|
)
|
|
.await?;
|
|
|
|
let filters = device_profile::Filters {
|
|
tenant_id: Some(tenant_id),
|
|
search: if req.search.is_empty() {
|
|
None
|
|
} else {
|
|
Some(req.search.to_string())
|
|
},
|
|
};
|
|
|
|
let count = device_profile::get_count(&filters)
|
|
.await
|
|
.map_err(|e| e.status())?;
|
|
let items = device_profile::list(req.limit as i64, req.offset as i64, &filters)
|
|
.await
|
|
.map_err(|e| e.status())?;
|
|
|
|
let mut resp = Response::new(api::ListDeviceProfilesResponse {
|
|
total_count: count as u32,
|
|
result: items
|
|
.iter()
|
|
.map(|dp| api::DeviceProfileListItem {
|
|
id: dp.id.to_string(),
|
|
created_at: Some(helpers::datetime_to_prost_timestamp(&dp.created_at)),
|
|
updated_at: Some(helpers::datetime_to_prost_timestamp(&dp.updated_at)),
|
|
name: dp.name.clone(),
|
|
region: dp.region.to_proto().into(),
|
|
mac_version: dp.mac_version.to_proto().into(),
|
|
reg_params_revision: dp.reg_params_revision.to_proto().into(),
|
|
supports_otaa: dp.supports_otaa,
|
|
supports_class_b: dp.supports_class_b,
|
|
supports_class_c: dp.supports_class_c,
|
|
})
|
|
.collect(),
|
|
});
|
|
resp.metadata_mut()
|
|
.insert("x-log-tenant_id", req.tenant_id.parse().unwrap());
|
|
|
|
Ok(resp)
|
|
}
|
|
|
|
async fn list_adr_algorithms(
|
|
&self,
|
|
request: Request<()>,
|
|
) -> Result<Response<api::ListDeviceProfileAdrAlgorithmsResponse>, Status> {
|
|
self.validator
|
|
.validate(
|
|
request.extensions(),
|
|
validator::ValidateActiveUserOrKey::new(),
|
|
)
|
|
.await?;
|
|
|
|
let items = adr::get_algorithms().await;
|
|
let mut result: Vec<api::AdrAlgorithmListItem> = items
|
|
.iter()
|
|
.map(|(k, v)| api::AdrAlgorithmListItem {
|
|
id: k.clone(),
|
|
name: v.clone(),
|
|
})
|
|
.collect();
|
|
result.sort_by(|a, b| a.name.cmp(&b.name));
|
|
|
|
Ok(Response::new(api::ListDeviceProfileAdrAlgorithmsResponse {
|
|
total_count: items.len() as u32,
|
|
result,
|
|
}))
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
pub mod test {
|
|
use super::*;
|
|
use crate::api::auth::validator::RequestValidator;
|
|
use crate::api::auth::AuthID;
|
|
use crate::storage::{tenant, user};
|
|
use crate::test;
|
|
use chirpstack_api::common;
|
|
|
|
#[tokio::test]
|
|
async fn test_device_profile() {
|
|
let _guard = test::prepare().await;
|
|
|
|
// setup admin user
|
|
let u = user::User {
|
|
is_admin: true,
|
|
is_active: true,
|
|
email: "admin@admin".into(),
|
|
email_verified: true,
|
|
..Default::default()
|
|
};
|
|
let u = user::create(u).await.unwrap();
|
|
|
|
// create tenant
|
|
let t = tenant::create(tenant::Tenant {
|
|
name: "test-tenant".into(),
|
|
can_have_gateways: true,
|
|
max_gateway_count: 10,
|
|
..Default::default()
|
|
})
|
|
.await
|
|
.unwrap();
|
|
|
|
// setup the api
|
|
let service = DeviceProfile::new(RequestValidator::new());
|
|
|
|
// create
|
|
let create_req = get_request(
|
|
&u.id,
|
|
api::CreateDeviceProfileRequest {
|
|
device_profile: Some(api::DeviceProfile {
|
|
tenant_id: t.id.to_string(),
|
|
name: "test-dp".into(),
|
|
region: common::Region::Eu868.into(),
|
|
mac_version: common::MacVersion::Lorawan103.into(),
|
|
reg_params_revision: common::RegParamsRevision::A.into(),
|
|
adr_algorithm_id: "default".into(),
|
|
..Default::default()
|
|
}),
|
|
},
|
|
);
|
|
let create_resp = service.create(create_req).await.unwrap();
|
|
let dp_id = Uuid::from_str(&create_resp.get_ref().id).unwrap();
|
|
|
|
// get
|
|
let get_req = get_request(
|
|
&u.id,
|
|
api::GetDeviceProfileRequest {
|
|
id: dp_id.to_string(),
|
|
},
|
|
);
|
|
let get_resp = service.get(get_req).await.unwrap();
|
|
assert_eq!(
|
|
Some(api::DeviceProfile {
|
|
id: dp_id.to_string(),
|
|
tenant_id: t.id.to_string(),
|
|
name: "test-dp".into(),
|
|
region: common::Region::Eu868.into(),
|
|
mac_version: common::MacVersion::Lorawan103.into(),
|
|
reg_params_revision: common::RegParamsRevision::A.into(),
|
|
adr_algorithm_id: "default".into(),
|
|
app_layer_params: Some(api::AppLayerParams::default()),
|
|
..Default::default()
|
|
}),
|
|
get_resp.get_ref().device_profile
|
|
);
|
|
|
|
// update
|
|
let update_req = get_request(
|
|
&u.id,
|
|
api::UpdateDeviceProfileRequest {
|
|
device_profile: Some(api::DeviceProfile {
|
|
id: dp_id.to_string(),
|
|
tenant_id: t.id.to_string(),
|
|
name: "test-dp-updated".into(),
|
|
region: common::Region::Us915.into(),
|
|
mac_version: common::MacVersion::Lorawan103.into(),
|
|
reg_params_revision: common::RegParamsRevision::A.into(),
|
|
adr_algorithm_id: "default".into(),
|
|
..Default::default()
|
|
}),
|
|
},
|
|
);
|
|
let _ = service.update(update_req).await.unwrap();
|
|
|
|
// get
|
|
let get_req = get_request(
|
|
&u.id,
|
|
api::GetDeviceProfileRequest {
|
|
id: dp_id.to_string(),
|
|
},
|
|
);
|
|
let get_resp = service.get(get_req).await.unwrap();
|
|
assert_eq!(
|
|
Some(api::DeviceProfile {
|
|
id: dp_id.to_string(),
|
|
tenant_id: t.id.to_string(),
|
|
name: "test-dp-updated".into(),
|
|
region: common::Region::Us915.into(),
|
|
mac_version: common::MacVersion::Lorawan103.into(),
|
|
reg_params_revision: common::RegParamsRevision::A.into(),
|
|
adr_algorithm_id: "default".into(),
|
|
app_layer_params: Some(api::AppLayerParams::default()),
|
|
..Default::default()
|
|
}),
|
|
get_resp.get_ref().device_profile
|
|
);
|
|
|
|
// list
|
|
let list_req = get_request(
|
|
&u.id,
|
|
api::ListDeviceProfilesRequest {
|
|
tenant_id: t.id.to_string(),
|
|
limit: 10,
|
|
search: "update".into(),
|
|
..Default::default()
|
|
},
|
|
);
|
|
let list_resp = service.list(list_req).await.unwrap();
|
|
let list_resp = list_resp.get_ref();
|
|
assert_eq!(1, list_resp.total_count);
|
|
assert_eq!(1, list_resp.result.len());
|
|
assert_eq!(dp_id.to_string(), list_resp.result[0].id);
|
|
|
|
// delete
|
|
let del_req = get_request(
|
|
&u.id,
|
|
api::DeleteDeviceProfileRequest {
|
|
id: dp_id.to_string(),
|
|
},
|
|
);
|
|
let _ = service.delete(del_req).await.unwrap();
|
|
let del_req = get_request(
|
|
&u.id,
|
|
api::DeleteDeviceProfileRequest {
|
|
id: dp_id.to_string(),
|
|
},
|
|
);
|
|
let del_resp = service.delete(del_req).await;
|
|
assert!(del_resp.is_err());
|
|
|
|
// list adr algorithms
|
|
let list_adr_algs_req = get_request(&u.id, ());
|
|
let list_adr_algs_resp = service
|
|
.list_adr_algorithms(list_adr_algs_req)
|
|
.await
|
|
.unwrap();
|
|
let list_adr_algs_resp = list_adr_algs_resp.get_ref();
|
|
assert_eq!(3, list_adr_algs_resp.total_count);
|
|
assert_eq!(3, list_adr_algs_resp.result.len());
|
|
assert_eq!("default", list_adr_algs_resp.result[0].id);
|
|
assert_eq!("lr_fhss", list_adr_algs_resp.result[1].id);
|
|
assert_eq!("lora_lr_fhss", list_adr_algs_resp.result[2].id);
|
|
}
|
|
|
|
fn get_request<T>(user_id: &Uuid, req: T) -> Request<T> {
|
|
let mut req = Request::new(req);
|
|
req.extensions_mut().insert(AuthID::User(*user_id));
|
|
req
|
|
}
|
|
}
|