pub(crate) mod completions;
pub(crate) mod execute_anonymous;
pub(crate) mod run_tests;
pub use completions::{CompletionsResult, CompletionsType};
pub use execute_anonymous::ExecuteAnonymousResult;
pub use run_tests::{
CodeCoverageResult, RunTestsRequest, RunTestsResult, TestFailure, TestItem, TestSuccess,
};
use crate::api::rest_operation::RestOperation;
use std::sync::Arc;
#[derive(Debug)]
pub struct ToolingHandler<A: crate::auth::Authenticator> {
inner: Arc<crate::session::Session<A>>,
}
impl<A: crate::auth::Authenticator> Clone for ToolingHandler<A> {
fn clone(&self) -> Self {
Self {
inner: Arc::clone(&self.inner),
}
}
}
impl<A: crate::auth::Authenticator> ToolingHandler<A> {
#[must_use]
pub(crate) fn new(inner: Arc<crate::session::Session<A>>) -> Self {
Self { inner }
}
}
impl<A: crate::auth::Authenticator> RestOperation<A> for ToolingHandler<A> {
fn session(&self) -> &Arc<crate::session::Session<A>> {
&self.inner
}
fn path_prefix(&self) -> &'static str {
"tooling"
}
}
#[cfg(test)]
mod tests {
use crate::api::rest_operation::RestOperation;
use crate::client::{ForceClient, builder};
use crate::test_support::{MockAuthenticator, Must, MustMsg};
use crate::types::SalesforceId;
use serde_json::json;
use wiremock::matchers::{header, method, path, query_param};
use wiremock::{Mock, MockServer, ResponseTemplate};
async fn create_test_client() -> ForceClient<MockAuthenticator> {
let auth = MockAuthenticator::new("test_token", "https://test.salesforce.com");
builder()
.authenticate(auth)
.build()
.await
.must_msg("failed to create test client")
}
#[tokio::test]
async fn test_tooling_handler_construction() {
let client = create_test_client().await;
let _handler = client.tooling();
}
#[tokio::test]
async fn test_tooling_handler_is_cloneable() {
let client = create_test_client().await;
let h1 = client.tooling();
let _h2 = Clone::clone(&h1);
}
#[tokio::test]
async fn test_tooling_path_prefix() {
let client = create_test_client().await;
let handler = client.tooling();
assert_eq!(handler.path_prefix(), "tooling");
}
#[tokio::test]
async fn test_tooling_handler_debug() {
let client = create_test_client().await;
let handler = client.tooling();
let debug = format!("{:?}", handler);
assert!(!debug.is_empty());
}
#[tokio::test]
async fn test_tooling_resolve_api_path_sobjects() {
let client = create_test_client().await;
let handler = client.tooling();
assert_eq!(
handler.resolve_api_path("sobjects/ApexClass"),
"tooling/sobjects/ApexClass"
);
}
#[tokio::test]
async fn test_tooling_resolve_api_path_query() {
let client = create_test_client().await;
let handler = client.tooling();
assert_eq!(handler.resolve_api_path("query"), "tooling/query");
}
#[tokio::test]
async fn test_tooling_resolve_api_path_describe() {
let client = create_test_client().await;
let handler = client.tooling();
assert_eq!(handler.resolve_api_path("sobjects"), "tooling/sobjects");
}
#[tokio::test]
async fn test_tooling_create_hits_tooling_url() {
let mock_server = MockServer::start().await;
let auth = MockAuthenticator::new("test_token", &mock_server.uri());
let client = builder().authenticate(auth).build().await.must();
Mock::given(method("POST"))
.and(path("/services/data/v60.0/tooling/sobjects/ApexClass"))
.and(header("Authorization", "Bearer test_token"))
.respond_with(ResponseTemplate::new(201).set_body_json(json!({
"id": "01p000000000001AAA",
"success": true,
"errors": []
})))
.expect(1)
.mount(&mock_server)
.await;
let tooling = client.tooling();
let response = tooling
.create("ApexClass", &json!({"Body": "public class MyClass {}"}))
.await
.must();
assert!(response.is_success());
assert_eq!(response.id.must().as_str(), "01p000000000001AAA");
}
#[tokio::test]
async fn test_tooling_get_hits_tooling_url() {
let mock_server = MockServer::start().await;
let auth = MockAuthenticator::new("test_token", &mock_server.uri());
let client = builder().authenticate(auth).build().await.must();
Mock::given(method("GET"))
.and(path(
"/services/data/v60.0/tooling/sobjects/ApexClass/01p000000000001AAA",
))
.and(header("Authorization", "Bearer test_token"))
.respond_with(ResponseTemplate::new(200).set_body_json(json!({
"Id": "01p000000000001AAA",
"Name": "MyClass"
})))
.expect(1)
.mount(&mock_server)
.await;
let tooling = client.tooling();
let id = SalesforceId::new("01p000000000001AAA").must();
let record = tooling.get("ApexClass", &id).await.must();
assert_eq!(record["Id"], "01p000000000001AAA");
assert_eq!(record["Name"], "MyClass");
}
#[tokio::test]
async fn test_tooling_query_hits_tooling_url() {
let mock_server = MockServer::start().await;
let auth = MockAuthenticator::new("test_token", &mock_server.uri());
let client = builder().authenticate(auth).build().await.must();
Mock::given(method("GET"))
.and(path("/services/data/v60.0/tooling/query"))
.and(query_param("q", "SELECT Id FROM ApexClass"))
.and(header("Authorization", "Bearer test_token"))
.respond_with(ResponseTemplate::new(200).set_body_json(json!({
"totalSize": 1,
"done": true,
"records": [{"Id": "01p000000000001AAA"}]
})))
.expect(1)
.mount(&mock_server)
.await;
let tooling = client.tooling();
let result = tooling
.query::<serde_json::Value>("SELECT Id FROM ApexClass")
.await
.must();
assert_eq!(result.total_size, 1);
assert!(result.done);
assert_eq!(result.records.len(), 1);
}
#[tokio::test]
async fn test_tooling_describe_hits_tooling_url() {
let mock_server = MockServer::start().await;
let auth = MockAuthenticator::new("test_token", &mock_server.uri());
let client = builder().authenticate(auth).build().await.must();
Mock::given(method("GET"))
.and(path(
"/services/data/v60.0/tooling/sobjects/ApexClass/describe",
))
.and(header("Authorization", "Bearer test_token"))
.respond_with(ResponseTemplate::new(200).set_body_json(json!({
"activateable": false,
"createable": true,
"custom": false,
"customSetting": false,
"deletable": true,
"deprecatedAndHidden": false,
"feedEnabled": false,
"hasSubtypes": false,
"isSubtype": false,
"keyPrefix": "01p",
"label": "Apex Class",
"labelPlural": "Apex Classes",
"layoutable": false,
"mergeable": false,
"mruEnabled": false,
"name": "ApexClass",
"queryable": true,
"replicateable": false,
"retrieveable": true,
"searchable": true,
"triggerable": false,
"undeletable": true,
"updateable": true,
"urls": {
"sobject": "/services/data/v60.0/tooling/sobjects/ApexClass"
},
"fields": [],
"childRelationships": [],
"recordTypeInfos": []
})))
.expect(1)
.mount(&mock_server)
.await;
let tooling = client.tooling();
let describe = tooling.describe("ApexClass").await.must();
assert_eq!(describe.name, "ApexClass");
assert_eq!(describe.label, "Apex Class");
assert_eq!(describe.key_prefix, Some("01p".to_string()));
}
#[tokio::test]
async fn test_tooling_describe_global_hits_tooling_url() {
let mock_server = MockServer::start().await;
let auth = MockAuthenticator::new("test_token", &mock_server.uri());
let client = builder().authenticate(auth).build().await.must();
Mock::given(method("GET"))
.and(path("/services/data/v60.0/tooling/sobjects"))
.and(header("Authorization", "Bearer test_token"))
.respond_with(ResponseTemplate::new(200).set_body_json(json!({
"encoding": "UTF-8",
"maxBatchSize": 200,
"sobjects": []
})))
.expect(1)
.mount(&mock_server)
.await;
let tooling = client.tooling();
let global = tooling.describe_global().await.must();
assert_eq!(global.encoding, "UTF-8");
assert_eq!(global.max_batch_size, 200);
assert!(global.sobjects.is_empty());
}
}