Browse Source

Implement most of the important foundry endpoints

master
NGnius (Graham) 2 years ago
parent
commit
7d60b6fa15
6 changed files with 517 additions and 56 deletions
  1. +2
    -4
      Cargo.toml
  2. +171
    -11
      src/robocraft2/factory.rs
  3. +115
    -1
      src/robocraft2/factory_json.rs
  4. +2
    -2
      src/robocraft2/mod.rs
  5. +52
    -20
      src/robocraft2/portal.rs
  6. +175
    -18
      tests/robocraft_factory.rs

+ 2
- 4
Cargo.toml View File

@@ -1,6 +1,6 @@
[package]
name = "libfj"
version = "0.6.1"
version = "0.7.0"
authors = ["NGnius (Graham) <ngniusness@gmail.com>"]
edition = "2018"
description = "An unofficial collection of APIs used in FreeJam games and mods"
@@ -21,8 +21,6 @@ serde_json = "^1"
reqwest = { version = "^0.11", features = ["json"], optional = true}
url = "^2.2"
ureq = { version = "^2", features = ["json"], optional = true}
cookie_store = { version = "0.16", optional = true}
cookie = { version = "0.16", optional = true}
async-trait = { version = "0.1", optional = true }
base64 = "^0.13"
num_enum = "^0.5"
@@ -46,4 +44,4 @@ robocraft = ["reqwest", "ureq"]
cardlife = ["reqwest"]
techblox = ["chrono", "highhash", "half", "libfj_parsable_macro_derive"]
convert = ["obj", "genmesh", "cgmath"]
robocraft2 = ["reqwest", "reqwest/cookies", "async-trait"]
robocraft2 = ["reqwest", "async-trait", "chrono"]

+ 171
- 11
src/robocraft2/factory.rs View File

@@ -1,13 +1,49 @@
use std::sync::Mutex;

use reqwest::{Client, Error};
use reqwest::{Client, Error as ReqwestError, Response};
use url::{Url};

use crate::robocraft2::{SearchPayload, SearchResponse, ITokenProvider};
use crate::robocraft2::{ITokenProvider, ErrorPayload};
use crate::robocraft2::{SearchPayload, SearchResponse, CreateRobotPayload, CreateRobotResponse, FactoryInfoResponse, PublishRobotPayload, PublishRobotResponse, MyRobotsResponse, GetRobotResponse};

/// Community Factory Robot 2 root URL
pub const FACTORY_DOMAIN: &str = "https://factory.production.robocraft2.com";

#[derive(Debug)]
pub enum FactoryError {
Protocol(ReqwestError),
Response(ErrorPayload),
ResponseCode(ReqwestError, u16)
}

impl std::fmt::Display for FactoryError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
match self {
Self::Protocol(p) => if let Some(status) = p.status() {
write!(f, "HTTP Error {}: {}", status, p)
} else {
write!(f, "HTTP Error: {}", p)
}
Self::Response(r) => write!(f, "Factory Error #{}: {}", r.error, r.error_message),
Self::ResponseCode(p, s) => write!(f, "HTTP Error {}: {}", s, p)
}
}
}

impl std::error::Error for FactoryError {}

async fn handle_json_response<D: for<'a> serde::Deserialize<'a>>(response: Response) -> Result<D, FactoryError> {
let status_code: u16 = response.status().into();
if status_code > 199 && status_code < 300 {
Ok(response.json::<D>().await.map_err(FactoryError::Protocol)?)
} else {
match response.json::<ErrorPayload>().await {
Ok(err) => Err(FactoryError::Response(err)),
Err(e) => Err(FactoryError::ResponseCode(e, status_code))
}
}
}

/// CRF API implementation
pub struct FactoryAPI {
client: Client,
@@ -32,13 +68,12 @@ impl FactoryAPI {
}

/// Retrieve CRF robots on the main page.
///
/// For searching, use `list_builder()` instead.
pub async fn list(&self) -> Result<SearchResponse, Error> {
pub async fn list(&self) -> Result<SearchResponse, FactoryError> {
self.search(SearchPayload::default()).await
}

pub async fn search(&self, params: SearchPayload) -> Result<SearchResponse, Error> {
/// Search for robots on the CRF which meet the provided parameters
pub async fn search(&self, params: SearchPayload) -> Result<SearchResponse, FactoryError> {
let mut url = Url::parse(FACTORY_DOMAIN)
.unwrap()
.join("/v1/foundry/search")
@@ -87,11 +122,136 @@ impl FactoryAPI {
}
url.query_pairs_mut().append_pair("sortBy", &params.sort_by);
url.query_pairs_mut().append_pair("orderBy", &params.order_by);
let mut request_builder = self.client.get(url);
if let Ok(token) = self.token.lock().unwrap().token().await {
request_builder = request_builder.header("Authorization", "Bearer ".to_owned() + &token);
let token = self.token.lock().unwrap().token().await.map_err(FactoryError::Protocol)?;
let result = self.client.get(url)
.header("Authorization", "Bearer ".to_owned() + &token)
.send().await
.map_err(FactoryError::Protocol)?;
handle_json_response::<SearchResponse>(result).await
}

pub async fn create_robot(&self, robot: CreateRobotPayload) -> Result<CreateRobotResponse, FactoryError> {
let url = Url::parse(FACTORY_DOMAIN)
.unwrap()
.join("/v1/foundry/garage")
.unwrap();
let token = self.token.lock().unwrap().token().await.map_err(FactoryError::Protocol)?;
let result = self.client.post(url)
.header("Authorization", "Bearer ".to_owned() + &token)
.header("Content-Type", "application/json")
.json(&robot)
.send().await
.map_err(FactoryError::Protocol)?;
handle_json_response::<CreateRobotResponse>(result).await
}

pub async fn publish_robot(&self, robot: PublishRobotPayload, id: String) -> Result<PublishRobotResponse, FactoryError> {
let url = Url::parse(FACTORY_DOMAIN)
.unwrap()
.join(&format!("/v1/foundry/vehicles/{}/publish", id))
.unwrap();
let token = self.token.lock().unwrap().token().await.map_err(FactoryError::Protocol)?;
let result = self.client.post(url)
.header("Authorization", "Bearer ".to_owned() + &token)
.header("Content-Type", "application/json")
.json(&robot)
.send().await
.map_err(FactoryError::Protocol)?;
handle_json_response(result).await
}

pub async fn unpublish_bot(&self, id: String) -> Result<(), FactoryError> {
let url = Url::parse(FACTORY_DOMAIN)
.unwrap()
.join(&format!("/v1/foundry/vehicles/{}/unpublish", id))
.unwrap();
let token = self.token.lock().unwrap().token().await.map_err(FactoryError::Protocol)?;
let result = self.client.post(url)
.header("Authorization", "Bearer ".to_owned() + &token)
.send().await
.map_err(FactoryError::Protocol)?;
let status_code = result.status().as_u16();
if status_code > 199 && status_code < 300 {
Ok(())
} else {
match result.json::<ErrorPayload>().await {
Ok(err) => Err(FactoryError::Response(err)),
Err(e) => Err(FactoryError::ResponseCode(e, status_code))
}
}
}

pub async fn delete_robot(&self, id: String) -> Result<(), FactoryError> {
let url = Url::parse(FACTORY_DOMAIN)
.unwrap()
.join(&format!("/v1/foundry/vehicles/{}", id))
.unwrap();
let token = self.token.lock().unwrap().token().await.map_err(FactoryError::Protocol)?;
let result = self.client.delete(url)
.header("Authorization", "Bearer ".to_owned() + &token)
.header("Content-Type", "application/json")
.send().await
.map_err(FactoryError::Protocol)?;
let status_code = result.status().as_u16();
if status_code > 199 && status_code < 300 {
Ok(())
} else {
match result.json::<ErrorPayload>().await {
Ok(err) => Err(FactoryError::Response(err)),
Err(e) => Err(FactoryError::ResponseCode(e, status_code))
}
}
let result = request_builder.send().await?;
result.json::<SearchResponse>().await
}

pub async fn factory_info(&self) -> Result<FactoryInfoResponse, FactoryError> {
let url = Url::parse(FACTORY_DOMAIN)
.unwrap()
.join("/v1/foundry/info")
.unwrap();
let token = self.token.lock().unwrap().token().await.map_err(FactoryError::Protocol)?;
let result = self.client.get(url)
.header("Authorization", "Bearer ".to_owned() + &token)
.send().await
.map_err(FactoryError::Protocol)?;
handle_json_response::<FactoryInfoResponse>(result).await
}

pub async fn my_robots(&self) -> Result<MyRobotsResponse, FactoryError> {
let url = Url::parse(FACTORY_DOMAIN)
.unwrap()
.join("/v1/foundry/garage")
.unwrap();
let token = self.token.lock().unwrap().token().await.map_err(FactoryError::Protocol)?;
let result = self.client.get(url)
.header("Authorization", "Bearer ".to_owned() + &token)
.send().await
.map_err(FactoryError::Protocol)?;
handle_json_response::<MyRobotsResponse>(result).await
}

pub async fn my_published_robots(&self) -> Result<MyRobotsResponse, FactoryError> {
let url = Url::parse(FACTORY_DOMAIN)
.unwrap()
.join("/v1/foundry/published")
.unwrap();
let token = self.token.lock().unwrap().token().await.map_err(FactoryError::Protocol)?;
let result = self.client.get(url)
.header("Authorization", "Bearer ".to_owned() + &token)
.send().await
.map_err(FactoryError::Protocol)?;
handle_json_response::<MyRobotsResponse>(result).await
}

pub async fn get(&self, id: String) -> Result<GetRobotResponse, FactoryError> {
let url = Url::parse(FACTORY_DOMAIN)
.unwrap()
.join(&format!("/v1/foundry/vehicles/{}", id))
.unwrap();
let token = self.token.lock().unwrap().token().await.map_err(FactoryError::Protocol)?;
let result = self.client.get(url)
.header("Authorization", "Bearer ".to_owned() + &token)
.send().await
.map_err(FactoryError::Protocol)?;
handle_json_response::<GetRobotResponse>(result).await
}
}

+ 115
- 1
src/robocraft2/factory_json.rs View File

@@ -1,5 +1,13 @@
use serde::{Deserialize, Serialize};

#[derive(Deserialize, Serialize, Debug, Clone)]
pub struct ErrorPayload {
#[serde(rename = "error")]
pub error: isize,
#[serde(rename = "errorMessage")]
pub error_message: String,
}

// search endpoint

#[derive(Deserialize, Serialize, Clone)]
@@ -119,7 +127,7 @@ pub struct RobotInfo {

impl std::string::ToString for RobotInfo {
fn to_string(&self) -> String {
format!("{} by {} ({})", &self.name, &self.creator_name, &self.id)
format!("{} ({}) by {} ({})", &self.name, &self.id, &self.creator_name, &self.creator_id)
}
}

@@ -130,3 +138,109 @@ pub struct RobotPrice {
#[serde(rename = "amount")]
pub amount: isize,
}

// create robot endpoint

#[derive(Deserialize, Serialize, Clone, Debug)]
pub struct CreateRobotPayload {
#[serde(rename = "name")]
pub name: String,
#[serde(rename = "data")]
pub data: String, // base64
#[serde(rename = "image")]
pub image: String, // base64??
#[serde(rename = "baseCpu")]
pub base_cpu: isize,
#[serde(rename = "weaponCpu")]
pub weapon_cpu: isize,
#[serde(rename = "cosmeticCpu")]
pub cosmetic_cpu: isize,
#[serde(rename = "clusterCount")]
pub cluster_count: isize,
#[serde(rename = "blockCounts")]
pub block_counts: std::collections::HashMap<usize, usize>,
#[serde(rename = "materialsUsed")]
pub materials_used: std::collections::HashSet<isize>,
#[serde(rename = "minimumOffsetX")]
pub minimum_offset_x: f64,
#[serde(rename = "minimumOffsetY")]
pub minimum_offset_y: f64,
#[serde(rename = "minimumOffsetZ")]
pub minimum_offset_z: f64,
#[serde(rename = "maximumOffsetX")]
pub maximum_offset_x: f64,
#[serde(rename = "maximumOffsetY")]
pub maximum_offset_y: f64,
#[serde(rename = "maximumOffsetZ")]
pub maximum_offset_z: f64,
}

#[derive(Deserialize, Serialize, Clone, Debug)]
pub struct CreateRobotResponse {
#[serde(rename = "header")]
pub header: RobotInfo,
}

// factory info endpoint

// (no payload -- this is a GET request)

#[derive(Deserialize, Serialize, Clone, Debug)]
pub struct FactoryInfoResponse {
#[serde(rename = "robotCount")]
pub robot_count: isize,
#[serde(rename = "robotLimit")]
pub robot_limit: isize,
#[serde(rename = "publishedRobotCount")]
pub published_robot_count: isize,
#[serde(rename = "publishedRobotLimit")]
pub published_robot_limit: isize,
}

// publish robot endpoint

#[derive(Deserialize, Serialize, Clone, Debug)]
pub struct PublishRobotPayload {
#[serde(rename = "name")]
pub name: String,
#[serde(rename = "description")]
pub description: String,
#[serde(rename = "techPoints")]
pub techpoints: isize,
#[serde(rename = "bloxCoin")]
pub bloxcoin: isize,
}

#[derive(Deserialize, Serialize, Clone, Debug)]
pub struct PublishRobotResponse {
#[serde(rename = "header")]
pub header: RobotInfo,
#[serde(rename = "data")]
pub data: String, // base64
}

// get my robots endpoint

// (no payload -- this is a GET request)

#[derive(Deserialize, Serialize, Clone, Debug)]
pub struct MyRobotsResponse {
#[serde(rename = "vehicles")]
pub vehicles: Vec<RobotInfo>,
}

// get robot endpoint

// (no payload -- this is a GET request)

#[derive(Deserialize, Serialize, Clone, Debug)]
pub struct GetRobotResponse {
#[serde(rename = "header")]
pub header: RobotInfo,
#[serde(rename = "data")]
pub data: String, // base64
#[serde(rename = "description")]
pub description: String,
#[serde(rename = "created")]
pub created: String, // date
}

+ 2
- 2
src/robocraft2/mod.rs View File

@@ -2,10 +2,10 @@
//! Subject to change and breakages as RC2 is still in an early development stage.

mod factory;
pub use factory::FactoryAPI;
pub use factory::{FactoryAPI, FactoryError};

mod factory_json;
pub use factory_json::{SearchPayload, SearchResponse, SearchResponseItem, RobotInfo, RobotPrice};
pub use factory_json::{ErrorPayload, SearchPayload, SearchResponse, SearchResponseItem, RobotInfo, RobotPrice, CreateRobotPayload, CreateRobotResponse, FactoryInfoResponse, PublishRobotPayload, PublishRobotResponse, MyRobotsResponse, GetRobotResponse};

mod portal;
pub use self::portal::{PortalTokenProvider, AccountInfo, PortalCheckResponse, ITokenProvider};

+ 52
- 20
src/robocraft2/portal.rs View File

@@ -4,12 +4,13 @@ use reqwest::{Client, Error};
//use cookie_store::CookieStore;
//use url::{Url};
use serde_json::from_slice;
use chrono::{DateTime, naive::NaiveDateTime, Utc};

/// Token generator for authenticated API endpoints
#[async_trait::async_trait]
pub trait ITokenProvider {
/// Retrieve the token to use
async fn token(&mut self) -> Result<String, ()>;
async fn token(&mut self) -> Result<String, Error>;
}

/// Token provider for an existing Freejam account, authenticated through the web browser portal.
@@ -22,6 +23,8 @@ pub struct PortalTokenProvider {
jwt: PortalCheckResponse,
/// Ureq HTTP client
client: Client,
/// target game
target: String,
}

impl PortalTokenProvider {
@@ -34,7 +37,7 @@ impl PortalTokenProvider {
pub async fn target(value: String) -> Result<Self, Error> {
let client = Client::new();
let payload = PortalStartPayload {
target: value,
target: value.clone(),
};
let start_response = client.post("https://account.freejamgames.com/api/authenticate/portal/start")
.header("Content-Type", "application/json")
@@ -62,7 +65,7 @@ impl PortalTokenProvider {
let check_res = check_response.json::<PortalCheckResponse>().await?;

// login with token we just got
Self::login_internal(check_res, client).await
Self::login_internal(check_res, client, value).await
}

pub async fn with_email(email: &str, password: &str) -> Result<Self, Error> {
@@ -96,7 +99,7 @@ impl PortalTokenProvider {
/// Automatically validate portal
async fn auto_portal(client: Client, value: String, token: String) -> Result<Self, Error> {
let payload = PortalStartPayload {
target: value,
target: value.clone(),
};
let start_response = client.post("https://account.freejamgames.com/api/authenticate/portal/start")
.header("Content-Type", "application/json")
@@ -121,10 +124,20 @@ impl PortalTokenProvider {
let check_res = check_response.json::<PortalCheckResponse>().await?;

// login with token we just got
Self::login_internal(check_res, client).await
Self::login_internal(check_res, client, value).await
}

async fn login_internal(token_data: PortalCheckResponse, client: Client) -> Result<Self, Error> {
async fn login_internal(token_data: PortalCheckResponse, client: Client, target: String) -> Result<Self, Error> {
let progress_res = Self::login_step(&token_data, &client).await?;
Ok(Self {
token: progress_res,
jwt: token_data,
client: client,
target: target,
})
}

async fn login_step(token_data: &PortalCheckResponse, client: &Client) -> Result<ProgressionLoginResponse, Error> {
let payload = ProgressionLoginPayload {
token: token_data.token.clone(),
};
@@ -132,17 +145,12 @@ impl PortalTokenProvider {
.header("Content-Type", "application/json")
.json(&payload)
.send().await?;
let progress_res = progress_response.json::<ProgressionLoginResponse>().await?;
Ok(Self {
token: progress_res,
jwt: token_data,
client: client,
})
progress_response.json::<ProgressionLoginResponse>().await
}

/// Login using the portal token data from a previous portal authentication
pub async fn login(token_data: PortalCheckResponse) -> Result<Self, Error> {
Self::login_internal(token_data, Client::new()).await
pub async fn login(token_data: PortalCheckResponse, target: String) -> Result<Self, Error> {
Self::login_internal(token_data, Client::new(), target).await
}

pub fn get_account_info(&self) -> Result<AccountInfo, Error> {
@@ -156,13 +164,27 @@ impl PortalTokenProvider {

#[async_trait::async_trait]
impl ITokenProvider for PortalTokenProvider {
async fn token(&mut self) -> Result<String, ()> {
// TODO re-authenticate when expired
if let Some(token) = self.token.token.clone() {
Ok(token)
} else {
Err(())
async fn token(&mut self) -> Result<String, Error> {
let decoded_jwt = self.jwt.decode_jwt_data();
let expiry = DateTime::<Utc>::from_utc(NaiveDateTime::from_timestamp(decoded_jwt.exp as i64, 0), Utc);
let now = Utc::now();
if now >= expiry || self.token.token.is_none() {
// refresh token when expired
// TODO make sure refresh token isn't also expired
// (it would be a bit concerning if you decide to run libfj for 1+ month, though)
let payload = RefreshTokenPayload {
target: self.target.clone(),
refresh_token: self.jwt.refresh_token.clone(),
public_id: decoded_jwt.public_id,
};
let refresh_response = self.client.post("https://account.freejamgames.com/api/authenticate/token/refresh")
.header("Content-Type", "application/json")
.json(&payload)
.send().await?;
self.jwt = refresh_response.json::<PortalCheckResponse>().await?;
self.token = Self::login_step(&self.jwt, &self.client).await?;
}
Ok(self.token.token.clone().unwrap())
}
}

@@ -249,6 +271,16 @@ pub(crate) struct ProgressionLoginResponse {
pub server_token: Option<String>,
}

#[derive(Deserialize, Serialize, Clone, Debug)]
pub(crate) struct RefreshTokenPayload {
#[serde(rename = "Target")]
pub target: String, // "Techblox"
#[serde(rename = "RefreshToken")]
pub refresh_token: String,
#[serde(rename = "PublicId")]
pub public_id: String,
}

/// Robocraft2 account information.
#[derive(Deserialize, Serialize, Clone)]
pub struct AccountInfo {


+ 175
- 18
tests/robocraft_factory.rs View File

@@ -31,24 +31,6 @@ async fn robocraft_factory_default_query() -> Result<(), ()> {
Ok(())
}

#[cfg(feature = "robocraft2")]
#[tokio::test]
async fn robocraft2_factory_default_query() -> Result<(), ()> {
let api = robocraft2::FactoryAPI::with_auth(Box::new(robocraft2::PortalTokenProvider::with_username("FJAPIC00L", "P4$$w0rd").await.unwrap()));
let result = api.list().await;
assert!(result.is_ok());
let robo_info = result.unwrap();
assert_ne!(robo_info.results.len(), 0);
for robot in &robo_info.results {
assert_ne!(robot.robot.name, "");
assert_ne!(robot.robot.creator_id, "");
assert_ne!(robot.robot.creator_id, "");
assert_ne!(robot.robot.image, "");
//println!("FactoryRobotListInfo.to_string() -> `{}`", robot.to_string());
}
Ok(())
}

#[cfg(feature = "robocraft")]
fn builder() -> robocraft::FactorySearchBuilder {
robocraft::FactoryAPI::new().list_builder()
@@ -139,3 +121,178 @@ async fn robocraft_factory_robot_cubes() -> Result<(), ()> {
assert_eq!(colour_str, bot_info.response.colour_data);
Ok(())
}

#[cfg(feature = "robocraft2")]
//#[tokio::test]
async fn robocraft2_factory_default_query() -> Result<(), ()> {
let api = robocraft2::FactoryAPI::with_auth(Box::new(robocraft2::PortalTokenProvider::with_username("FJAPIC00L", "P4$$w0rd").await.unwrap()));
let result = api.list().await;
assert!(result.is_ok());
let robo_info = unwrap_factory2(result);
assert_ne!(robo_info.results.len(), 0);
for robot in &robo_info.results {
assert_ne!(robot.robot.name, "");
assert_ne!(robot.robot.creator_id, "");
assert_ne!(robot.robot.creator_id, "");
assert_ne!(robot.robot.image, "");
//println!("RobotInfo.to_string() -> `{}`", robot.robot.to_string());
println!("SearchResponseItem {}", serde_json::to_string_pretty(&robot).unwrap());
}
Ok(())
}


#[cfg(feature = "robocraft2")]
#[tokio::test]
async fn robocraft2_factory_info() -> Result<(), ()> {
let api = robocraft2::FactoryAPI::with_auth(Box::new(robocraft2::PortalTokenProvider::with_username("FJAPIC00L", "P4$$w0rd").await.unwrap()));
let result = api.factory_info().await;
assert!(result.is_ok());
let crf_info = unwrap_factory2(result);
println!("FactoryInfo {:?}", crf_info);
Ok(())
}

#[cfg(feature = "robocraft2")]
//#[tokio::test]
async fn robocraft2_factory_upload() -> Result<(), ()> {
let api = robocraft2::FactoryAPI::with_auth(Box::new(robocraft2::PortalTokenProvider::with_username("FJAPIC00L", "P4$$w0rd").await.unwrap()));

// copy default bot
let result = api.get("08dab2c9-7a72-4ec4-843c-154fe8768e91".to_owned()).await;
assert!(result.is_ok());
let robot = unwrap_factory2(result);

let result = api.create_robot(
robocraft2::CreateRobotPayload {
name: "API is easy".to_owned(),
data: robot.data, // base64
image: "".to_owned(), // base64
base_cpu: 42,
weapon_cpu: 1,
cosmetic_cpu: 6,
cluster_count: 1,
block_counts: vec![
(42, 3),
(3, 6)
].drain(..).collect(),
materials_used: vec![
8,
4
].drain(..).collect(),
minimum_offset_x: 0.0,
minimum_offset_y: 0.0,
minimum_offset_z: 0.0,
maximum_offset_x: 0.0,
maximum_offset_y: 0.0,
maximum_offset_z: 0.0,
}
).await;
//assert!(result.is_ok());
let robot_info = unwrap_factory2(result);
println!("CreateRobotInfo {:?}", robot_info);
let result = api.publish_robot(
robocraft2::PublishRobotPayload {
name: "CRF API oh my".to_owned(),
description: "There once was a person named NGnius, who simply wasn't that bright.\nBut he had a misleading name, and wasn't not quite unhated\nSo he thought it alright to put up a fight\nand get banned for reverse engineering".to_owned(),
techpoints: -42,
bloxcoin: 123
}, robot_info.header.id.clone()).await;
//assert!(result.is_ok());
let _publish_info = unwrap_factory2(result);

// clean up
let result = api.unpublish_bot(robot_info.header.id.clone()).await;
//assert!(result.is_ok());
let _robot = unwrap_factory2(result);

let result = api.delete_robot(robot_info.header.id).await;
//assert!(result.is_ok());
let _robot = unwrap_factory2(result);
Ok(())
}

#[cfg(feature = "robocraft2")]
#[tokio::test]
async fn robocraft2_factory_my_bots() -> Result<(), ()> {
let api = robocraft2::FactoryAPI::with_auth(Box::new(robocraft2::PortalTokenProvider::with_username("FJAPIC00L", "P4$$w0rd").await.unwrap()));
let result = api.my_robots().await;
assert!(result.is_ok());
let robo_info = unwrap_factory2(result);
assert_ne!(robo_info.vehicles.len(), 0);
for robot in &robo_info.vehicles {
assert_ne!(robot.name, "");
assert_ne!(robot.creator_id, "");
assert_ne!(robot.creator_id, "");
assert_ne!(robot.image, "");
println!("My bot `{}`", robot.to_string());
//println!("my vehicle {}", serde_json::to_string_pretty(&robot).unwrap());
}
Ok(())
}

#[cfg(feature = "robocraft2")]
#[tokio::test]
async fn robocraft2_factory_my_published_bots() -> Result<(), ()> {
let api = robocraft2::FactoryAPI::with_auth(Box::new(robocraft2::PortalTokenProvider::with_username("FJAPIC00L", "P4$$w0rd").await.unwrap()));
let result = api.my_published_robots().await;
assert!(result.is_ok());
let robo_info = unwrap_factory2(result);
//assert_ne!(robo_info.vehicles.len(), 0);
for robot in &robo_info.vehicles {
assert_ne!(robot.name, "");
assert_ne!(robot.creator_id, "");
assert_ne!(robot.creator_id, "");
assert_ne!(robot.image, "");
println!("My pub bot `{}`", robot.to_string());
//println!("pub vehicle {}", serde_json::to_string_pretty(&robot).unwrap());
}
Ok(())
}

#[cfg(feature = "robocraft2")]
//#[tokio::test]
async fn robocraft2_factory_bot() -> Result<(), ()> {
let api = robocraft2::FactoryAPI::with_auth(Box::new(robocraft2::PortalTokenProvider::with_username("FJAPIC00L", "P4$$w0rd").await.unwrap()));
let result = api.get("08dab2c9-7a72-4ec4-843c-154fe8768e91".to_owned()).await;
//assert!(result.is_ok());
let robot = unwrap_factory2(result);
assert_ne!(robot.header.name, "");
assert_ne!(robot.header.creator_id, "");
assert_ne!(robot.header.creator_id, "");
//assert_ne!(robot.header.image, "");
//assert_ne!(robot.description, "");
assert_ne!(robot.data, "");
println!("robot {}", serde_json::to_string_pretty(&robot).unwrap());
Ok(())
}

#[cfg(feature = "robocraft2")]
//#[tokio::test]
async fn robocraft2_factory_delete_bot() -> Result<(), ()> {
let api = robocraft2::FactoryAPI::with_auth(Box::new(robocraft2::PortalTokenProvider::with_username("FJAPIC00L", "P4$$w0rd").await.unwrap()));
let result = api.delete_robot("08dab2d2-dcae-4f52-8a77-bbce7cf10124".to_owned()).await;
//assert!(result.is_ok());
let _robot = unwrap_factory2(result);
Ok(())
}

#[cfg(feature = "robocraft2")]
//#[tokio::test]
async fn robocraft2_factory_unpublish_bot() -> Result<(), ()> {
let api = robocraft2::FactoryAPI::with_auth(Box::new(robocraft2::PortalTokenProvider::with_username("FJAPIC00L", "P4$$w0rd").await.unwrap()));
let result = api.unpublish_bot("08dab2d3-2a68-48c6-8fd6-a59a663336ca".to_owned()).await;
//assert!(result.is_ok());
let _robot = unwrap_factory2(result);
Ok(())
}

fn unwrap_factory2<T>(result: Result<T, robocraft2::FactoryError>) -> T {
match result {
Ok(t) => t,
Err(e) => {
//println!("FactoryError: {}", e);
panic!("CRF2 Error: {}", e);
}
}
}

Loading…
Cancel
Save