1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
use super::auth;
use super::sauce_errors;
use super::users;
use std::error::Error;

fn region_tunnel_api(region: &users::Region, tunnel_id: &str, owner: &str) -> std::string::String {
    match region {
        users::Region::US => format!(
            "https://api.us-west-1.saucelabs.com/rest/v1/{}/tunnels/{}",
            owner, tunnel_id
        ),
        users::Region::EU => format!(
            "https://api.eu-central-1.saucelabs.com/rest/v1/{}/tunnels/{}",
            owner, tunnel_id
        ),
    }
}

fn region_job_api(region: &users::Region, job_id: &str) -> std::string::String {
    match region {
        users::Region::US => format!("https://saucelabs.com/rest/v1.1/jobs/{}", job_id),
        users::Region::EU => format!(
            "https://eu-central-1.saucelabs.com/rest/v1.1/jobs/{}",
            job_id
        ),
    }
}

/// `tunnel_raw` requires the Owner of a tunnel + the tunnel id to
/// return data about the tunnel. Creation time, config, and more are returned from the API call.
pub fn tunnel_raw(
    owner: &users::User,
    tunnel_id: &str,
    super_admin: Option<&users::User>,
) -> Result<String, Box<dyn Error>> {
    let api = region_tunnel_api(&owner.region, tunnel_id, &owner.creds.username);
    let client = reqwest::blocking::Client::new();
    let auth: &users::User = match super_admin {
        Some(admin) => admin,
        None => owner,
    };
    let resp = client
        .get(&api)
        .basic_auth(&auth.creds.username, Some(&auth.creds.access_key))
        .send()?;
    if !resp.status().is_success() {
        return Err(format!("{} response during req to {}. Are you looking for a tunnel owned by {}? Or owned by someone else?", resp.status(), api, &owner.creds.username))?;
    }
    return Ok(resp.text()?);
}

/// Returns the JSON info for a Job. `job_info` makes a REST call
/// with given credentials to fetch the details of a single job.
pub fn job_info(
    owner: &users::User,
    job_id: &str,
    super_admin: Option<&users::User>,
) -> Result<String, Box<dyn Error>> {
    let auth: &users::User = match super_admin {
        Some(admin) => admin,
        None => owner,
    };

    let api = region_job_api(&owner.region, job_id);

    let client = reqwest::blocking::Client::new();
    let resp = client
        .get(&api)
        .basic_auth(&auth.creds.username, Some(&auth.creds.access_key))
        .send()?;
    if !resp.status().is_success() {
        return Err(format!(
            "{} response during GET req to {}",
            resp.status(),
            api
        ))?;
    }
    return Ok(resp.text()?);
}

/// Get latest jobs for a user, limit of 500 at a time.  Returns
/// a JSON object with details of each job
pub fn recent_user_jobs(
    owner: &users::User,
    super_admin: Option<&users::User>,
    limit: u64,
) -> Result<String, Box<dyn Error>> {
    if limit > 500 {
        Err(format!(
            "{} is too many jobs.  Limit is 500 for /user/jobs API. See Sauce Labs API docs",
            limit
        ))?;
    }
    let auth: &users::User = match super_admin {
        Some(admin) => admin,
        None => owner,
    };
    let job_info_api = format!(
        "https://saucelabs.com/rest/v1/{}/jobs?limit={}&full=true",
        owner.creds.username, limit
    );

    let text_resp = reqwest::blocking::Client::new()
        .get(&job_info_api)
        .basic_auth(&auth.creds.username, Some(&auth.creds.access_key))
        .send()?
        .text()?;
    return Ok(text_resp);
}

/// `all_jobs` makes an API call to the builds/:id/jobs endpoint
/// to fetch the job details for each job in the build.
pub fn all_jobs(build_id: String, user: users::User) -> Result<serde_json::Value, Box<dyn Error>> {
    let build_api = format!("https://app.saucelabs.com/rest/v1/builds/{}/jobs", build_id);
    let resp: serde_json::Value = reqwest::blocking::Client::new()
        .get(&build_api)
        .basic_auth(&user.creds.username, Some(&user.creds.access_key))
        .send()?
        .json()?;
    if resp["jobs"].is_array() {
        return Ok(resp);
    } else {
        let masked_key = auth::mask_key(user.creds.access_key);
        return Err(Box::new(sauce_errors::build::NoJobs::new(
            &user.creds.username,
            &masked_key,
            &build_api,
            resp,
        )));
    };
}

/// `build_info` uses the v1/builds/:id endpoint to fetch
/// all the build meta data as a json object
pub fn build_info(build_id: &str, user: users::User) -> Result<serde_json::Value, Box<dyn Error>> {
    let build_api = format!("https://app.saucelabs.com/rest/v1/builds/{}", build_id);
    let resp: serde_json::Value = reqwest::blocking::Client::new()
        .get(&build_api)
        .basic_auth(&user.creds.username, Some(&user.creds.access_key))
        .send()?
        .json()?;
    return Ok(resp);
}

#[test]
/// use the recent_user_jobs api call and confirm
/// we only get the requested number of jobs as raw json
fn json_user_last_3_jobs() {
    let real_user_env_vars = super::users::User::new(None, None, None);
    let jobs_json = super::api::recent_user_jobs(&real_user_env_vars, None, 3).unwrap();
    let last_3_jobs: serde_json::Value = serde_json::from_str(&jobs_json).unwrap();
    println!(
        "{}\nLength of jobs_json: {}",
        last_3_jobs,
        last_3_jobs.as_array().unwrap().len()
    );
    assert_eq!(last_3_jobs.as_array().unwrap().len(), 3);
}

#[test]
fn over_500_limit() {
    let real_user_env_vars = super::users::User::new(None, None, None);
    // let _jobs_json = super::jobs::recent_user_jobs(&real_user_env_vars, None, 505).unwrap();
    match super::api::recent_user_jobs(&real_user_env_vars, None, 505) {
        Ok(_) => println!("Shouldn't be here"),
        Err(e) => {
            println!("{:?}", e);
            assert_eq!(
                "505 is too many jobs.  Limit is 500 for /user/jobs API. See Sauce Labs API docs",
                e.to_string()
            )
        }
    }
}

#[test]
#[should_panic]
fn all_jobs_bad_input() {
    let fake_user = super::users::User::new(
        Some("bad.user12b1581b".to_string()),
        Some("1285-fake-b128b519".to_string()),
        None,
    );
    match super::api::all_jobs("91ee45d589ce4177981bf22f911f22c5".to_string(), fake_user) {
        Ok(resp) => assert_eq!(resp["jobs"].as_array().unwrap().len(), 32),
        Err(e) => assert_eq!(e.to_string(), ""),
    }
}

#[test]
fn get_build_data() {
    let real_user = super::users::User::new(None, None, None);
    let resp = match super::api::build_info("91ee45d589ce4177981bf22f911f22c5", real_user) {
        Ok(resp) => resp,
        Err(e) => panic!("{}", e),
    };
    assert_eq!(resp["jobs"]["finished"], 32);
    println!(
        "Build 91ee45d589ce4177981bf22f911f22c5 has data ---------> {:?}",
        resp
    );
}

#[test]
fn create_new_build_object() {
    let real_user = super::users::User::new(None, None, None);
    let mybuild = match super::builds::Build::new("91ee45d589ce4177981bf22f911f22c5", real_user) {
        Ok(b) => b,
        Err(e) => panic!("{}", e),
    };
    println!("my build ----->{:?}", mybuild);
    assert_eq!(
        mybuild.name,
        Some("generic build: grey Small Fresh Computer 6.0.4".to_string())
    )
}

#[test]
fn get_tunnel_raw() {
    let real_user_env_vars = super::users::User::new(None, None, None);
    let tunnel_deets = super::api::tunnel_raw(
        &real_user_env_vars,
        "20073ff17a234bec951b7a51a1bce2ad",
        Some(&real_user_env_vars),
    )
    .unwrap();
    println!("{}", tunnel_deets)
}