Skip to content

Latest commit

 

History

16 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

vapix

Client for AXIS Communications devices' VAPIX API. Bullet points:

  • Async
  • Cross platform
  • #![forbid(unsafe_code)]

Features:

  • vapix::Client monitors and controls devices running AXIS firmware >= 5.00
  • vapix::Transport decouples the library from any http implementation

Optional features:

  • goblin: sniff vapix::application::Architecture from executable files
  • hyper: HTTP via vapix::HyperTransport (enabled by default)

Basic use

// Instantiate a Device using `hyper` to communicate
let uri = http::Uri::from_static("http://user:pass@1.2.3.4");
let client = vapix::Client::new(axis::HyperTransport::default(), uri);

// Probe for VAPIX APIs supported by this device
let services = client.services().await?;

// If it supports the basic device info service...
if let Some(basic_device_info) = services.basic_device_info.as_ref() {
    // ...ask for those properties
    let properties = basic_device_info.properties().await?;
    println!("product_full_name: {:?}", properties.product_full_name);
    println!("    serial_number: {:?}", properties.serial_number);
} else {
    // If not, assume it supports the legacy parameters API, and retrieve those parameters
    let parameters = client.parameters().list(None).await?;
    println!("product_full_name: {:?}", parameters["root.Brand.ProdFullName"]);
    println!("    serial_number: {:?}", parameters["root.Properties.System.Soc"]);
}

Development

Use cargo test, cargo check, cargo clippy, rustfmt as usual. Open issues with issues, open PRs with changesets.

Many API tests use crate::mock_client(), bound to a testing Transport which goes to a block instead of to the network. If you want to ensure that a function sends the right request or does the right thing with a certain response, mock up a test which covers exactly that.

#[tokio::test]
async fn update() {
    let client = crate::mock_client(|req| {
        assert_eq!(req.method(), http::Method::GET);
        assert_eq!(
            req.uri().path_and_query().map(|pq| pq.as_str()),
            Some("/axis-cgi/param.cgi?action=update&foo.bar=baz+quxx")
        );

        http::Response::builder()
            .status(http::StatusCode::OK)
            .header(http::header::CONTENT_TYPE, "text/plain")
            .body(vec![b"OK".to_vec()])
    });

    let response = client
        .parameters()
        .update(vec![("foo.bar", "baz quxx")])
        .await;
    match response {
        Ok(()) => {}
        Err(e) => panic!("update should succeed: {}", e),
    };
}

Tests covering safe-to-call APIs use create::test_with_devices() to test against recordings of actual devices. test_with_devices() takes an async block which gets called repeatedly with various TestDevices, containing metadata and a Client. Tests are free to fail with Error::UnsupportedFeature, but assertion failures or other errors will cause the containing test to fail.

#[test]
fn test_something() {
    crate::test_with_devices(|test_device| async move {
        // This block is called with various test_devices. If the block fails for any device, the
        // test will fail. If the test depends on particular device features, the test must probe
        // for those features, and it must succeed if the feature is missing.
        // 
        // test_device.device_info.{model, firmware_version, ..} describe the test device
        // test_device.device is a crate::Device<_> and can make arbitrary calls

        let parameters = test_device.client.parameters();
        let all_params = parameters.list_definitions(None).await?;

        // assert!() things which should always be true on every device ever
        // If behavior depends on firmware version, inquire about test_device's metadata
    });
}

Recordings are produced using the test suite itself. When RECORD_DEVICE_URI is set, each test_with_devices() block is additionally run against the live device, and a fixture is produced.

$ RECORD_DEVICE_URI=http://user:pass@1.2.3.4 cargo test

$ git status
On branch master

Untracked files:
  (use "git add <file>..." to include in what will be committed)
	fixtures/recordings/ACCC8EF7DE6B v9.80.2.2.json
$

It is desirable to collect recordings from devices in the field, so test_with_devices() blocks must avoid modifying the device state.

About

Client for AXIS Communications devices' VAPIX API (Rust)

Topics

Resources

Stars

10 stars

Watchers

2 watching

Forks

Releases

Packages

Contributors

Languages