diff options
Diffstat (limited to 'fatcat-cli/src/api.rs')
-rw-r--r-- | fatcat-cli/src/api.rs | 440 |
1 files changed, 440 insertions, 0 deletions
diff --git a/fatcat-cli/src/api.rs b/fatcat-cli/src/api.rs new file mode 100644 index 0000000..2463aab --- /dev/null +++ b/fatcat-cli/src/api.rs @@ -0,0 +1,440 @@ +use crate::{parse_macaroon_editor_id, EntityType, Specifier}; +use anyhow::{anyhow, Context, Result}; +use fatcat_openapi::models; +use fatcat_openapi::{ApiNoContext, ContextWrapperExt}; +use swagger::{auth, AuthData, ContextBuilder, EmptyContext, Push, XSpanIdString}; + +type FatcatApiContextType = swagger::make_context_ty!( + ContextBuilder, + EmptyContext, + Option<AuthData>, + XSpanIdString +); + +pub struct FatcatApiClient { + pub api: Box<dyn ApiNoContext<FatcatApiContextType>>, + pub rt: tokio::runtime::Runtime, + pub api_token: Option<String>, + pub api_host: String, + pub editor_id: Option<String>, +} + +impl FatcatApiClient { + pub fn new(api_host: String, api_token: Option<String>) -> Result<Self> { + let auth_data = match api_token { + Some(ref token) => Some(AuthData::Bearer(auth::Bearer { + token: token.clone(), + })), + None => None, + }; + //info!("{:?}", auth_data); + let context: FatcatApiContextType = swagger::make_context!( + ContextBuilder, + EmptyContext, + auth_data, + XSpanIdString::default() + ); + + //let wrapped_client: swagger::ContextWrapper< + let client = fatcat_openapi::client::Client::try_new(&api_host) + .context("failed to create HTTP(S) client")?; + let wrapped_client = Box::new(client.with_context(context)); + let rt: tokio::runtime::Runtime = + tokio::runtime::Runtime::new().expect("create tokio runtime"); + + let editor_id = match api_token { + Some(ref token) => { + Some(parse_macaroon_editor_id(token).context("parse API auth token")?) + } + None => None, + }; + + Ok(FatcatApiClient { + api: wrapped_client, + rt, + api_token, + editor_id, + api_host, + }) + } + + pub fn update_editgroup_submit( + &mut self, + editgroup_id: String, + submit: bool, + ) -> Result<models::Editgroup> { + let result = self + .rt + .block_on(self.api.get_editgroup(editgroup_id.clone())) + .context("fetch editgroups")?; + let eg = match result { + fatcat_openapi::GetEditgroupResponse::Found(eg) => eg, + other => { + return Err(anyhow!("{:?}", other)) + .with_context(|| format!("failed to fetch editgroup {}", editgroup_id)) + } + }; + let result = self + .rt + .block_on( + self.api + .update_editgroup(editgroup_id.clone(), eg, Some(submit)), + ) + .context("submit editgroup")?; + match result { + fatcat_openapi::UpdateEditgroupResponse::UpdatedEditgroup(eg) => Ok(eg), + other => Err(anyhow!("{:?}", other)) + .with_context(|| format!("failed to submit editgroup {}", editgroup_id)), + } + } + + pub fn delete_entity( + &mut self, + specifier: Specifier, + editgroup_id: String, + ) -> Result<models::EntityEdit> { + use Specifier::*; + let specifier = specifier.into_entity_specifier(self)?; + match specifier.clone() { + Release(fcid) => match self + .rt + .block_on(self.api.delete_release(editgroup_id, fcid))? + { + fatcat_openapi::DeleteReleaseResponse::DeletedEntity(ee) => Ok(ee), + other => Err(anyhow!("{:?}", other)), + }, + Work(fcid) => match self.rt.block_on(self.api.delete_work(editgroup_id, fcid))? { + fatcat_openapi::DeleteWorkResponse::DeletedEntity(ee) => Ok(ee), + other => Err(anyhow!("{:?}", other)), + }, + Container(fcid) => match self + .rt + .block_on(self.api.delete_container(editgroup_id, fcid))? + { + fatcat_openapi::DeleteContainerResponse::DeletedEntity(ee) => Ok(ee), + other => Err(anyhow!("{:?}", other)), + }, + Creator(fcid) => match self + .rt + .block_on(self.api.delete_creator(editgroup_id, fcid))? + { + fatcat_openapi::DeleteCreatorResponse::DeletedEntity(ee) => Ok(ee), + other => Err(anyhow!("{:?}", other)), + }, + File(fcid) => match self.rt.block_on(self.api.delete_file(editgroup_id, fcid))? { + fatcat_openapi::DeleteFileResponse::DeletedEntity(ee) => Ok(ee), + other => Err(anyhow!("{:?}", other)), + }, + FileSet(fcid) => match self + .rt + .block_on(self.api.delete_fileset(editgroup_id, fcid))? + { + fatcat_openapi::DeleteFilesetResponse::DeletedEntity(ee) => Ok(ee), + other => Err(anyhow!("{:?}", other)), + }, + WebCapture(fcid) => match self + .rt + .block_on(self.api.delete_webcapture(editgroup_id, fcid))? + { + fatcat_openapi::DeleteWebcaptureResponse::DeletedEntity(ee) => Ok(ee), + other => Err(anyhow!("{:?}", other)), + }, + Editgroup(..) | Editor(..) => unimplemented!("deletion for this entity type"), + Changelog(..) => return Err(anyhow!("mutating this entity type doesn't make sense")), + EditorUsername(..) | ReleaseLookup(..) | ContainerLookup(..) | FileLookup(..) + | CreatorLookup(..) => return Err(anyhow!("into_entity_specifier() didn't work?")), + } + .with_context(|| format!("failed to delete {:?}", specifier)) + } + + pub fn create_entity_from_json( + &mut self, + entity_type: EntityType, + json_str: &str, + editgroup_id: String, + ) -> Result<models::EntityEdit> { + match entity_type { + EntityType::Release => { + match self.rt.block_on( + self.api + .create_release(editgroup_id, serde_json::from_str(&json_str)?), + )? { + fatcat_openapi::CreateReleaseResponse::CreatedEntity(ee) => Ok(ee), + other => Err(anyhow!("{:?}", other)), + } + } + EntityType::Work => { + match self.rt.block_on( + self.api + .create_work(editgroup_id, serde_json::from_str(&json_str)?), + )? { + fatcat_openapi::CreateWorkResponse::CreatedEntity(ee) => Ok(ee), + other => Err(anyhow!("{:?}", other)), + } + } + EntityType::Creator => { + match self.rt.block_on( + self.api + .create_creator(editgroup_id, serde_json::from_str(&json_str)?), + )? { + fatcat_openapi::CreateCreatorResponse::CreatedEntity(ee) => Ok(ee), + other => Err(anyhow!("{:?}", other)), + } + } + EntityType::Container => { + match self.rt.block_on( + self.api + .create_container(editgroup_id, serde_json::from_str(&json_str)?), + )? { + fatcat_openapi::CreateContainerResponse::CreatedEntity(ee) => Ok(ee), + other => Err(anyhow!("{:?}", other)), + } + } + EntityType::File => { + match self.rt.block_on( + self.api + .create_file(editgroup_id, serde_json::from_str(&json_str)?), + )? { + fatcat_openapi::CreateFileResponse::CreatedEntity(ee) => Ok(ee), + other => Err(anyhow!("{:?}", other)), + } + } + EntityType::FileSet => { + match self.rt.block_on( + self.api + .create_fileset(editgroup_id, serde_json::from_str(&json_str)?), + )? { + fatcat_openapi::CreateFilesetResponse::CreatedEntity(ee) => Ok(ee), + other => Err(anyhow!("{:?}", other)), + } + } + EntityType::WebCapture => { + match self.rt.block_on( + self.api + .create_webcapture(editgroup_id, serde_json::from_str(&json_str)?), + )? { + fatcat_openapi::CreateWebcaptureResponse::CreatedEntity(ee) => Ok(ee), + other => Err(anyhow!("{:?}", other)), + } + } + } + .with_context(|| format!("parsing and creating {:?} entity", entity_type)) + } + + pub fn existing_edit_in_editgroup( + &mut self, + editgroup: &models::Editgroup, + specifier: &Specifier, + ) -> Option<models::EntityEdit> { + use Specifier::*; + let (fcid, edit_list) = match specifier.clone() { + Release(fcid) => (fcid, editgroup.edits.as_ref().unwrap().releases.clone()), + Work(fcid) => (fcid, editgroup.edits.as_ref().unwrap().works.clone()), + Container(fcid) => (fcid, editgroup.edits.as_ref().unwrap().containers.clone()), + Creator(fcid) => (fcid, editgroup.edits.as_ref().unwrap().creators.clone()), + File(fcid) => (fcid, editgroup.edits.as_ref().unwrap().files.clone()), + FileSet(fcid) => (fcid, editgroup.edits.as_ref().unwrap().filesets.clone()), + WebCapture(fcid) => (fcid, editgroup.edits.as_ref().unwrap().webcaptures.clone()), + EditorUsername(..) | ReleaseLookup(..) | ContainerLookup(..) | FileLookup(..) + | CreatorLookup(..) | Editgroup(..) | Editor(..) | Changelog(..) => { + panic!("this entity type doesn't exist in editgroups") + } + }; + for entity_edit in edit_list.unwrap() { + if entity_edit.ident == fcid { + return Some(entity_edit); + } + } + None + } + + pub fn delete_editgroup_edit( + &mut self, + editgroup: &models::Editgroup, + specifier: &Specifier, + edit: &models::EntityEdit, + ) -> Result<()> { + use Specifier::*; + let editgroup_id = editgroup.editgroup_id.clone().unwrap(); + let edit_id = edit.edit_id.clone(); + match specifier.clone() { + Release(..) => match self + .rt + .block_on(self.api.delete_release_edit(editgroup_id, edit_id))? + { + fatcat_openapi::DeleteReleaseEditResponse::DeletedEdit(..) => Ok(()), + other => Err(anyhow!("{:?}", other)), + }, + Work(..) => match self + .rt + .block_on(self.api.delete_work_edit(editgroup_id, edit_id))? + { + fatcat_openapi::DeleteWorkEditResponse::DeletedEdit(..) => Ok(()), + other => Err(anyhow!("{:?}", other)), + }, + Container(..) => match self + .rt + .block_on(self.api.delete_container_edit(editgroup_id, edit_id))? + { + fatcat_openapi::DeleteContainerEditResponse::DeletedEdit(..) => Ok(()), + other => Err(anyhow!("{:?}", other)), + }, + Creator(..) => match self + .rt + .block_on(self.api.delete_creator_edit(editgroup_id, edit_id))? + { + fatcat_openapi::DeleteCreatorEditResponse::DeletedEdit(..) => Ok(()), + other => Err(anyhow!("{:?}", other)), + }, + File(..) => match self + .rt + .block_on(self.api.delete_file_edit(editgroup_id, edit_id))? + { + fatcat_openapi::DeleteFileEditResponse::DeletedEdit(..) => Ok(()), + other => Err(anyhow!("{:?}", other)), + }, + FileSet(..) => match self + .rt + .block_on(self.api.delete_fileset_edit(editgroup_id, edit_id))? + { + fatcat_openapi::DeleteFilesetEditResponse::DeletedEdit(..) => Ok(()), + other => Err(anyhow!("{:?}", other)), + }, + WebCapture(..) => match self + .rt + .block_on(self.api.delete_webcapture_edit(editgroup_id, edit_id))? + { + fatcat_openapi::DeleteWebcaptureEditResponse::DeletedEdit(..) => Ok(()), + other => Err(anyhow!("{:?}", other)), + }, + EditorUsername(..) | ReleaseLookup(..) | ContainerLookup(..) | FileLookup(..) + | CreatorLookup(..) | Editgroup(..) | Editor(..) | Changelog(..) => { + panic!("this entity type doesn't exist in editgroups") + } + } + } + + pub fn update_entity_from_json( + &mut self, + specifier: Specifier, + json_str: &str, + editgroup_id: String, + ) -> Result<models::EntityEdit> { + use Specifier::*; + let specifier = specifier.into_entity_specifier(self)?; + let eg = match self + .rt + .block_on(self.api.get_editgroup(editgroup_id.clone()))? + { + fatcat_openapi::GetEditgroupResponse::Found(model) => Ok(model), + fatcat_openapi::GetEditgroupResponse::BadRequest(err) => { + Err(anyhow!("Bad Request ({}): {}", err.error, err.message)) + } + fatcat_openapi::GetEditgroupResponse::NotFound(err) => { + Err(anyhow!("Not Found: {}", err.message)) + } + resp => Err(anyhow!("{:?}", resp)) + .with_context(|| format!("API GET failed: editgroup_{:?}", editgroup_id)), + }?; + if let Some(entity_edit) = self.existing_edit_in_editgroup(&eg, &specifier) { + self.delete_editgroup_edit(&eg, &specifier, &entity_edit)?; + }; + match specifier.clone() { + Release(fcid) => match self.rt.block_on(self.api.update_release( + editgroup_id, + fcid, + serde_json::from_str(&json_str)?, + ))? { + fatcat_openapi::UpdateReleaseResponse::UpdatedEntity(ee) => Ok(ee), + other => Err(anyhow!("{:?}", other)), + }, + Work(fcid) => match self.rt.block_on(self.api.update_work( + editgroup_id, + fcid, + serde_json::from_str(&json_str)?, + ))? { + fatcat_openapi::UpdateWorkResponse::UpdatedEntity(ee) => Ok(ee), + other => Err(anyhow!("{:?}", other)), + }, + Container(fcid) => match self.rt.block_on(self.api.update_container( + editgroup_id, + fcid, + serde_json::from_str(&json_str)?, + ))? { + fatcat_openapi::UpdateContainerResponse::UpdatedEntity(ee) => Ok(ee), + other => Err(anyhow!("{:?}", other)), + }, + Creator(fcid) => match self.rt.block_on(self.api.update_creator( + editgroup_id, + fcid, + serde_json::from_str(&json_str)?, + ))? { + fatcat_openapi::UpdateCreatorResponse::UpdatedEntity(ee) => Ok(ee), + other => Err(anyhow!("{:?}", other)), + }, + File(fcid) => match self.rt.block_on(self.api.update_file( + editgroup_id, + fcid, + serde_json::from_str(&json_str)?, + ))? { + fatcat_openapi::UpdateFileResponse::UpdatedEntity(ee) => Ok(ee), + other => Err(anyhow!("{:?}", other)), + }, + FileSet(fcid) => match self.rt.block_on(self.api.update_fileset( + editgroup_id, + fcid, + serde_json::from_str(&json_str)?, + ))? { + fatcat_openapi::UpdateFilesetResponse::UpdatedEntity(ee) => Ok(ee), + other => Err(anyhow!("{:?}", other)), + }, + WebCapture(fcid) => match self.rt.block_on(self.api.update_webcapture( + editgroup_id, + fcid, + serde_json::from_str(&json_str)?, + ))? { + fatcat_openapi::UpdateWebcaptureResponse::UpdatedEntity(ee) => Ok(ee), + other => Err(anyhow!("{:?}", other)), + }, + Editgroup(..) | Editor(..) => unimplemented!("updates for this entity type"), + Changelog(..) => return Err(anyhow!("deleting this entity type doesn't make sense")), + EditorUsername(..) | ReleaseLookup(..) | ContainerLookup(..) | FileLookup(..) + | CreatorLookup(..) => return Err(anyhow!("into_entity_specifier() didn't work?")), + } + .with_context(|| format!("failed to update {:?}", specifier)) + } + + pub fn create_editgroup(&mut self, description: Option<String>) -> Result<models::Editgroup> { + let mut eg = models::Editgroup::new(); + eg.description = description; + eg.extra = Some({ + let mut extra = std::collections::HashMap::new(); + extra.insert( + "agent".to_string(), + serde_json::Value::String("fatcat-cli".to_string()), + // TODO: version? + ); + extra + }); + let result = self.rt.block_on(self.api.create_editgroup(eg))?; + match result { + fatcat_openapi::CreateEditgroupResponse::SuccessfullyCreated(eg) => Ok(eg), + other => Err(anyhow!("{:?}", other)).context("failed to create editgroup"), + } + } + + pub fn accept_editgroup(&mut self, editgroup_id: String) -> Result<models::Success> { + let result = self + .rt + .block_on(self.api.accept_editgroup(editgroup_id.clone())) + .context("accept editgroup")?; + match result { + fatcat_openapi::AcceptEditgroupResponse::MergedSuccessfully(msg) => Ok(msg), + other => Err(anyhow!( + "failed to accept editgroup {}: {:?}", + editgroup_id, + other + )), + } + } +} |