feat: add device ID override functionality to ShorebirdUpdater
ci / ✅ Semantic Pull Request (push) Has been cancelled
ci / 🔤 Check Spelling (push) Has been cancelled
ci / 👀 Detect Changes (push) Has been cancelled
Shorebird CI / changes (push) Has been cancelled
Shorebird CI / CSpell (push) Has been cancelled
ci / 🦀 Build ${{ matrix.crate }} (${{ matrix.os }}) (push) Has been cancelled
ci / 🎯 Build ${{ matrix.package }} (push) Has been cancelled
ci / ci (push) Has been cancelled
Shorebird CI / shorebird_code_push (push) Has been cancelled
Shorebird CI / shorebird_code_push_example (push) Has been cancelled
Shorebird CI / required (push) Has been cancelled
ci / ✅ Semantic Pull Request (push) Has been cancelled
ci / 🔤 Check Spelling (push) Has been cancelled
ci / 👀 Detect Changes (push) Has been cancelled
Shorebird CI / changes (push) Has been cancelled
Shorebird CI / CSpell (push) Has been cancelled
ci / 🦀 Build ${{ matrix.crate }} (${{ matrix.os }}) (push) Has been cancelled
ci / 🎯 Build ${{ matrix.package }} (push) Has been cancelled
ci / ci (push) Has been cancelled
Shorebird CI / shorebird_code_push (push) Has been cancelled
Shorebird CI / shorebird_code_push_example (push) Has been cancelled
Shorebird CI / required (push) Has been cancelled
- Introduced `setDeviceIdOverride` method in `ShorebirdUpdater` to allow clients to set a custom device ID for patch checks. - Implemented the method in `ShorebirdUpdaterImpl` for both IO and web platforms. - Updated the `Updater` class to handle the device ID override in native bindings. - Added tests for the new functionality in both IO and web test suites, ensuring proper behavior when the updater is available and unavailable.
This commit is contained in:
@@ -83,6 +83,15 @@ SHOREBIRD_EXPORT uintptr_t shorebird_next_boot_patch_number(void);
|
||||
SHOREBIRD_EXPORT
|
||||
bool shorebird_check_for_downloadable_update(const char *c_channel);
|
||||
|
||||
/**
|
||||
* Overrides the generated random per-install device/client id used in patch
|
||||
* check requests. This is for applications that need to bind patch delivery
|
||||
* to their own stable account/device identifier. The updater persists this
|
||||
* value in state.json after a successful call.
|
||||
*/
|
||||
SHOREBIRD_EXPORT
|
||||
bool shorebird_set_device_id_override(const char *c_device_id);
|
||||
|
||||
/**
|
||||
* Synchronously download an update on the first non-null channel of:
|
||||
* 1. `c_channel`
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
//! `c_api::engine` — that surface is unstable and changes freely.
|
||||
use std::os::raw::c_char;
|
||||
|
||||
use super::{allocate_c_string, free_c_string, log_on_error, to_rust_option};
|
||||
use super::{allocate_c_string, free_c_string, log_on_error, to_rust, to_rust_option};
|
||||
use crate::{updater, UpdateStatus};
|
||||
|
||||
/// An unknown error occurred while updating. The update was not installed.
|
||||
@@ -105,6 +105,23 @@ pub extern "C" fn shorebird_check_for_downloadable_update(c_channel: *const c_ch
|
||||
)
|
||||
}
|
||||
|
||||
/// Overrides the generated random per-install device/client id used in patch
|
||||
/// check requests. This is for applications that need to bind patch delivery
|
||||
/// to their own stable account/device identifier. The updater persists this
|
||||
/// value in state.json after a successful call.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn shorebird_set_device_id_override(c_device_id: *const c_char) -> bool {
|
||||
log_on_error(
|
||||
|| {
|
||||
let device_id = to_rust(c_device_id)?;
|
||||
updater::set_client_id_override(&device_id)?;
|
||||
Ok(true)
|
||||
},
|
||||
"setting device id override",
|
||||
false,
|
||||
)
|
||||
}
|
||||
|
||||
/// Synchronously download an update on the first non-null channel of:
|
||||
/// 1. `c_channel`
|
||||
/// 2. The channel specified in shorebird.yaml
|
||||
|
||||
Vendored
+36
-1
@@ -2,7 +2,7 @@
|
||||
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use anyhow::Result;
|
||||
use anyhow::{ensure, Result};
|
||||
#[cfg(test)]
|
||||
use anyhow::{bail, Context};
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -100,6 +100,23 @@ impl UpdaterState {
|
||||
pub fn client_id(&self) -> String {
|
||||
self.serialized_state.client_id.clone()
|
||||
}
|
||||
|
||||
pub fn set_client_id_override(&mut self, client_id: &str) -> Result<()> {
|
||||
ensure!(
|
||||
!client_id.is_empty(),
|
||||
"Device id override must not be empty."
|
||||
);
|
||||
ensure!(
|
||||
client_id.len() <= 256,
|
||||
"Device id override must be 256 bytes or fewer."
|
||||
);
|
||||
ensure!(
|
||||
!client_id.chars().any(char::is_control),
|
||||
"Device id override must not contain control characters."
|
||||
);
|
||||
self.serialized_state.client_id = client_id.to_string();
|
||||
self.save()
|
||||
}
|
||||
}
|
||||
|
||||
impl UpdaterState {
|
||||
@@ -529,6 +546,24 @@ mod tests {
|
||||
assert_eq!(next.client_id(), original_client_id);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn client_id_override_persists_across_release_changes() {
|
||||
let tmp = TempDir::new().unwrap();
|
||||
let mut original = load(&tmp, "1.0.0+1");
|
||||
original
|
||||
.set_client_id_override("developer-device-id")
|
||||
.unwrap();
|
||||
let next = load(&tmp, "1.0.0+2");
|
||||
assert_eq!(next.client_id(), "developer-device-id");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn client_id_override_rejects_empty_value() {
|
||||
let tmp = TempDir::new().unwrap();
|
||||
let mut state = load(&tmp, "1.0.0+1");
|
||||
assert!(state.set_client_id_override("").is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn corrupt_state_file_creates_new_state() {
|
||||
let tmp = TempDir::new().unwrap();
|
||||
|
||||
@@ -271,6 +271,13 @@ pub fn should_auto_update() -> anyhow::Result<bool> {
|
||||
with_config(|config| Ok(config.auto_update))
|
||||
}
|
||||
|
||||
/// Overrides the generated per-install device/client id used for patch checks
|
||||
/// and server-side device targeting. The value is persisted in updater state
|
||||
/// and survives release-version cache resets.
|
||||
pub fn set_client_id_override(client_id: &str) -> anyhow::Result<()> {
|
||||
with_mut_state(|state| state.set_client_id_override(client_id))
|
||||
}
|
||||
|
||||
/// Synchronously checks for an update on the first non-null channel of:
|
||||
/// 1. `c_channel`
|
||||
/// 2. The channel specified in shorebird.yaml
|
||||
|
||||
Reference in New Issue
Block a user