summaryrefslogtreecommitdiffstats
path: root/fatcat-cli/src/api.rs
diff options
context:
space:
mode:
Diffstat (limited to 'fatcat-cli/src/api.rs')
-rw-r--r--fatcat-cli/src/api.rs440
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
+ )),
+ }
+ }
+}