Skip to content

Commit

Permalink
chore(docs): clean up exposed symbols and doc comments for Rust SDK
Browse files Browse the repository at this point in the history
Signed-off-by: jlanson <[email protected]>
  • Loading branch information
j-lanson authored and alilleybrinker committed Jan 17, 2025
1 parent 819b4f0 commit 51486aa
Show file tree
Hide file tree
Showing 6 changed files with 134 additions and 71 deletions.
4 changes: 4 additions & 0 deletions sdk/rust/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,7 @@ console = "0.15.10"
macros = ["dep:hipcheck-sdk-macros"]
mock_engine = []
print-timings = []

[package.metadata.docs.rs]
all-features = true
rustdoc-args = ["--cfg", "docsrs"]
58 changes: 52 additions & 6 deletions sdk/rust/src/plugin_engine.rs → sdk/rust/src/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@

use crate::{
error::{Error, Result},
QueryTarget,
JsonValue, Plugin, QueryTarget,
};
use crate::{mock::MockResponses, JsonValue, Plugin};
use futures::Stream;
use hipcheck_common::proto::{
self, InitiateQueryProtocolRequest, InitiateQueryProtocolResponse, Query as PluginQuery,
Expand Down Expand Up @@ -35,21 +34,26 @@ impl From<Status> for Error {

type SessionTracker = HashMap<i32, mpsc::Sender<Option<PluginQuery>>>;

/// The handle that a `Query::run()` function can use to request information from other Hipcheck
/// plugins in order to fulfill a query.
/// Manages a particular query session.
///
/// This struct invokes a `Query` trait object, passing a handle to itself to `Query::run()`. This
/// allows the query logic to request information from other Hipcheck plugins in order to complete.
pub struct PluginEngine {
id: usize,
tx: mpsc::Sender<StdResult<InitiateQueryProtocolResponse, Status>>,
rx: mpsc::Receiver<Option<PluginQuery>>,
concerns: Vec<String>,
// So that we can remove ourselves when we get dropped
drop_tx: mpsc::Sender<i32>,
/// when unit testing, this enables the user to mock plugin responses to various inputs
// When unit testing, this enables the user to mock plugin responses to various inputs
mock_responses: MockResponses,
}

impl PluginEngine {
#[cfg(feature = "mock_engine")]
#[cfg_attr(docsrs, doc(cfg(feature = "mock_engine")))]
/// Constructor for use in unit tests, `query()` function will reference this map instead of
/// trying to connect to Hipcheck core for a response value
pub fn mock(mock_responses: MockResponses) -> Self {
mock_responses.into()
}
Expand Down Expand Up @@ -260,19 +264,22 @@ impl PluginEngine {
}
}

/// Records a string-like concern that will be emitted in the final Hipcheck report. Intended
/// for use within a `Query` trait impl.
pub fn record_concern<S: AsRef<str>>(&mut self, concern: S) {
fn inner(engine: &mut PluginEngine, concern: &str) {
engine.concerns.push(concern.to_owned());
}
inner(self, concern.as_ref())
}

pub fn take_concerns(&mut self) -> Vec<String> {
fn take_concerns(&mut self) -> Vec<String> {
self.concerns.drain(..).collect()
}
}

#[cfg(feature = "mock_engine")]
#[cfg_attr(docsrs, doc(cfg(feature = "mock_engine")))]
impl From<MockResponses> for PluginEngine {
fn from(value: MockResponses) -> Self {
let (tx, _) = mpsc::channel(1);
Expand Down Expand Up @@ -466,3 +473,42 @@ enum HandleAction<'s> {
ForwardMsgToExistingSession(&'s mut mpsc::Sender<Option<PluginQuery>>),
CreateSession,
}

/// A map of query endpoints to mock return values.
///
/// When using the `mock_engine` feature, calling `PluginEngine::query()` will cause this
/// structure to be referenced instead of trying to communicate with Hipcheck core. Allows
/// constructing a `PluginEngine` with which to write unit tests.
#[derive(Default, Debug)]
pub struct MockResponses(pub(crate) HashMap<(QueryTarget, JsonValue), Result<JsonValue>>);

impl MockResponses {
pub fn new() -> Self {
Self(HashMap::new())
}
}

impl MockResponses {
#[cfg(feature = "mock_engine")]
pub fn insert<T, V, W>(
&mut self,
query_target: T,
query_value: V,
query_response: Result<W>,
) -> Result<()>
where
T: TryInto<QueryTarget, Error: Into<crate::Error>>,
V: serde::Serialize,
W: serde::Serialize,
{
let query_target: QueryTarget = query_target.try_into().map_err(|e| e.into())?;
let query_value: JsonValue =
serde_json::to_value(query_value).map_err(crate::Error::InvalidJsonInQueryKey)?;
let query_response = match query_response {
Ok(v) => serde_json::to_value(v).map_err(crate::Error::InvalidJsonInQueryKey),
Err(e) => Err(e),
};
self.0.insert((query_target, query_value), query_response);
Ok(())
}
}
1 change: 1 addition & 0 deletions sdk/rust/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ impl From<Infallible> for Error {
}
}

/// A Result type using `hipcheck_sdk::Error`
pub type Result<T> = StdResult<T, Error>;

/// Errors specific to the execution of `Plugin::set_configuration()` to configure a Hipcheck
Expand Down
102 changes: 76 additions & 26 deletions sdk/rust/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,55 +1,100 @@
// SPDX-License-Identifier: Apache-2.0

use crate::error::Error;
use crate::error::Result;
use error::ConfigError;
use plugin_engine::PluginEngine;
#![allow(unexpected_cfgs)]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![cfg_attr(docsrs, allow(unused_attributes))]

//! Hipcheck Plugin SDK in Rust.
//!
//! ## What is Hipcheck?
//! [Hipcheck][hipcheck] is a command line interface (CLI) tool for analyzing open source software
//! packages and source repositories to understand their software supply chain risk. It analyzes a
//! project's software development practices and detects active supply chain attacks to give you
//! both a long-term and immediate picture of the risk from using a package.
//!
//! Part of Hipcheck's value is its [plugin system][hipcheck_plugins], which allows anyone to write
//! a new data source or analysis component, or build even higher level analyses off of the results
//! of multiple other components.
//!
//! ## The Plugin SDK
//! This crate is a Rust SDK to help developers focus on writing the essential logic of their
//! Hipcheck plugins instead of worrying about session management or communication with Hipcheck
//! core. The essential steps of using this SDK are to implement the `Query` trait for each query
//! endpoint you wish to support, then implement the `Plugin` trait to tie your plugin together and
//! describe things like configuration parameters.
//!
//! For more, see our [detailed guide][web_sdk_docs] on writing plugins using this crate.
//!
//! [hipcheck]: https://hipcheck.mitre.org/
//! [hipcheck_plugins]: https://hipcheck.mitre.org/docs/guide/making-plugins/creating-a-plugin/
//! [web_sdk_docs]: https://hipcheck.mitre.org/docs/guide/making-plugins/rust-sdk/
use crate::error::{ConfigError, Error, Result};
pub use engine::PluginEngine;
use schemars::schema::SchemaObject as JsonSchema;
use serde_json::Value as JsonValue;
pub use server::PluginServer;
use std::result::Result as StdResult;
use std::str::FromStr;

#[cfg(feature = "macros")]
extern crate hipcheck_sdk_macros;
#[cfg_attr(docsrs, doc(cfg(feature = "macros")))]
/// Macros for simplifying `Query` and `Plugin` trait implementations
pub mod macros {
pub use hipcheck_sdk_macros::*;
}

#[cfg(feature = "print-timings")]
mod benchmarking;

mod engine;
pub mod error;
mod mock;
pub mod plugin_engine;
pub mod plugin_server;
mod server;

#[cfg(feature = "mock_engine")]
#[cfg_attr(docsrs, doc(cfg(feature = "mock_engine")))]
/// Tools for unit-testing plugin `Query` implementations
pub mod mock {
pub use crate::engine::MockResponses;
}

/// The definitions of Hipcheck's analysis `Target` object and its sub-types for use in writing
/// query endpoints.
pub mod types;

/// A utility module, users can simply write `use hipcheck_sdk::prelude::*` to import everything
/// they need to write a plugin
/// A utility module containing everything needed to write a plugin, just write `use
/// hipcheck_sdk::prelude::*`.
pub mod prelude {
pub use crate::deps::*;
pub use crate::engine::PluginEngine;
pub use crate::error::{ConfigError, Error, Result};
pub use crate::plugin_engine::PluginEngine;
pub use crate::plugin_server::{PluginServer, QueryResult};
pub use crate::server::{PluginServer, QueryResult};
pub use crate::{DynQuery, NamedQuery, Plugin, Query, QuerySchema, QueryTarget};
// Re-export macros
#[cfg(feature = "macros")]
pub use hipcheck_sdk_macros::{queries, query};
#[cfg_attr(docsrs, doc(cfg(feature = "macros")))]
pub use crate::macros::{queries, query};

#[cfg(feature = "mock_engine")]
pub use crate::mock::MockResponses;
#[cfg_attr(docsrs, doc(cfg(feature = "mock_engine")))]
pub use crate::engine::MockResponses;
}

/// re-export of user-facing third-party dependencies
/// Re-export of user-facing third-party dependencies
pub mod deps {
pub use jiff::{Span, Zoned};
pub use schemars::{schema::SchemaObject as JsonSchema, schema_for};
pub use serde_json::{from_str, from_value, to_value, Value};
pub use tonic::async_trait;
}

/// The target of a Hipcheck query. The `publisher` and `plugin` fields are necessary to identify a
/// plugin process. Plugins may define one or more query endpoints, and may include an unnamed
/// endpoint as the "default", hence why the `query` field is of type Option. QueryTarget
/// implements `FromStr`, taking strings of the format `"publisher/plugin[/query]"` where the
/// bracketed substring is optional.
/// Identifies the target plugin and endpoint of a Hipcheck query.
///
/// The `publisher` and `plugin` fields are necessary from Hipcheck core's perspective to identify
/// a plugin process. Plugins may define one or more query endpoints, and may include an unnamed
/// endpoint as the "default", hence why the `query` field is optional. `QueryTarget` implements
/// `FromStr` so it can be parsed from strings of the format `"publisher/plugin[/query]"`, where
/// the bracketed substring is optional.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct QueryTarget {
pub publisher: String,
Expand Down Expand Up @@ -85,9 +130,10 @@ impl TryInto<QueryTarget> for &str {
}
}

/// Encapsulates the signature of a particular `NamedQuery`. Instances of this type are usually
/// created by the default implementation of `Plugin::schemas()` and would not need to be created
/// by hand unless you are doing something very unorthodox.
/// Descrbies the signature of a particular `NamedQuery`.
///
/// Instances of this type are usually created by the default implementation of `Plugin::schemas()`
/// and would not need to be created by hand unless you are doing something very unorthodox.
pub struct QuerySchema {
/// The name of the query being described.
query_name: &'static str,
Expand All @@ -102,9 +148,12 @@ pub struct QuerySchema {
/// A `Query` trait object.
pub type DynQuery = Box<dyn Query>;

/// Pairs a query endpoint name with a particular `Query` trait implementation.
///
/// Since the `Query` trait needs to be made into a trait object, we can't use a static associated
/// string to store the query's name in the trait itself. This object wraps a `Query` trait object
/// and allows us to associate a name with it.
/// and allows us to associate a name with it so that when the plugin receives a query from
/// Hipcheck core, it can look up the proper behavior to invoke.
pub struct NamedQuery {
/// The name of the query.
pub name: &'static str,
Expand Down Expand Up @@ -136,7 +185,8 @@ pub trait Query: Send {
async fn run(&self, engine: &mut PluginEngine, input: JsonValue) -> Result<JsonValue>;
}

/// The core trait that a plugin author must implement to write a plugin with the Hipcheck SDK.
/// The core trait that a plugin author must implement using the Hipcheck SDK.
///
/// Declares basic information about the plugin and its query endpoints, and accepts a
/// configuration map from Hipcheck core.
pub trait Plugin: Send + Sync + 'static {
Expand All @@ -160,7 +210,7 @@ pub trait Plugin: Send + Sync + 'static {

/// Get all the queries supported by the plugin. Each query endpoint in a plugin will have its
/// own `trait Query` implementation. This function should return an iterator containing one
/// `NamedQuery` instance ofr each `trait Query` implementation defined by the plugin author.
/// `NamedQuery` instance for each `trait Query` implementation defined by the plugin author.
fn queries(&self) -> impl Iterator<Item = NamedQuery>;

/// Get the plugin's default query, if it has one. The default query is a `NamedQuery` with an
Expand Down
38 changes: 0 additions & 38 deletions sdk/rust/src/mock.rs

This file was deleted.

2 changes: 1 addition & 1 deletion sdk/rust/src/plugin_server.rs → sdk/rust/src/server.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
// SPDX-License-Identifier: Apache-2.0

use crate::{
engine::HcSessionSocket,
error::{Error, Result},
plugin_engine::HcSessionSocket,
Plugin, QuerySchema,
};
use hipcheck_common::proto::{
Expand Down

0 comments on commit 51486aa

Please sign in to comment.