Skip to content

Commit

Permalink
feat(commands): add ability to upload attachments for comments
Browse files Browse the repository at this point in the history
  • Loading branch information
joe-prosser committed Jul 25, 2024
1 parent 5c3a3bb commit 4d0b957
Show file tree
Hide file tree
Showing 9 changed files with 285 additions and 18 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
- Add ability to download attachments for comments
- Increase default http timeout to 120s
- Add `--resume-on-error` flag when creating annotations
- Add ability to upload attachment content for comments

# v0.28.0
- Add general fields to `create datasets`
Expand Down
5 changes: 5 additions & 0 deletions api/src/error.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::path::PathBuf;

use reqwest::StatusCode;

pub type Result<T> = std::result::Result<T, Error>;
Expand All @@ -16,6 +18,9 @@ pub enum Error {
#[error("Bad token: {}", token)]
BadToken { token: String },

#[error("File does not exist : {}", path.to_string_lossy())]
FileDoesNotExist { path: PathBuf },

#[error("Expected <owner>/<name> or a source id, got: {}", identifier)]
BadSourceIdentifier { identifier: String },

Expand Down
83 changes: 80 additions & 3 deletions api/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use reqwest::{
IntoUrl, Proxy, Result as ReqwestResult,
};
use resources::{
attachments::UploadAttachmentResponse,
bucket_statistics::GetBucketStatisticsResponse,
comment::{AttachmentReference, CommentTimestampFilter},
dataset::{
Expand All @@ -36,7 +37,13 @@ use resources::{
};
use serde::{Deserialize, Serialize};
use serde_json::json;
use std::{cell::Cell, fmt::Display, io::Read, path::Path, time::Duration};
use std::{
cell::Cell,
fmt::Display,
io::Read,
path::{Path, PathBuf},
time::Duration,
};
use url::Url;

use crate::resources::{
Expand Down Expand Up @@ -622,10 +629,59 @@ impl Client {
Ok(())
}

pub fn upload_comment_attachment(
&self,
source_id: &SourceId,
comment_id: &CommentId,
attachment_index: usize,
attachment: &PathBuf,
) -> Result<UploadAttachmentResponse> {
let url = self
.endpoints
.attachment_upload(source_id, comment_id, attachment_index)?;

if !attachment.is_file() || !attachment.exists() {
return Err(Error::FileDoesNotExist {
path: attachment.clone(),
});
}

let do_request = || {
let form = Form::new()
.file("file", attachment)
.map_err(|source| Error::Unknown {
message: "PUT comment attachment operation failed".to_owned(),
source: source.into(),
})
.unwrap();
let request = self
.http_client
.request(Method::PUT, url.clone())
.multipart(form)
.headers(self.headers.clone());

request.send()
};

let result = self.with_retries(do_request);

let http_response = result.map_err(|source| Error::ReqwestError {
source,
message: "Operation failed.".to_string(),
})?;

let status = http_response.status();

http_response
.json::<Response<UploadAttachmentResponse>>()
.map_err(Error::BadJsonResponse)?
.into_result(status)
}

pub fn get_attachment(&self, reference: &AttachmentReference) -> Result<Vec<u8>> {
let mut response = self.raw_request(
&Method::GET,
&self.endpoints.attachment(reference)?,
&self.endpoints.attachment_reference(reference)?,
&None::<()>,
&None::<()>,
&Retry::Yes,
Expand Down Expand Up @@ -1528,10 +1584,31 @@ impl Endpoints {
construct_endpoint(&self.base, &["api", "_private", "integrations", &name.0])
}

fn attachment(&self, reference: &AttachmentReference) -> Result<Url> {
fn attachment_reference(&self, reference: &AttachmentReference) -> Result<Url> {
construct_endpoint(&self.base, &["api", "v1", "attachments", &reference.0])
}

fn attachment_upload(
&self,
source_id: &SourceId,
comment_id: &CommentId,
attachment_index: usize,
) -> Result<Url> {
construct_endpoint(
&self.base,
&[
"api",
"_private",
"sources",
&format!("id:{}", source_id.0),
"comments",
&comment_id.0,
"attachments",
&attachment_index.to_string(),
],
)
}

fn validation(
&self,
dataset_name: &DatasetFullName,
Expand Down
9 changes: 9 additions & 0 deletions api/src/resources/attachments.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
pub struct ContentHash(pub String);

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct UploadAttachmentResponse {
pub content_hash: ContentHash,
}
5 changes: 2 additions & 3 deletions api/src/resources/comment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ use std::{
str::FromStr,
};

use super::attachments::ContentHash;

#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct Id(pub String);

Expand Down Expand Up @@ -295,9 +297,6 @@ pub enum Sentiment {
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
pub struct AttachmentReference(pub String);

#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
pub struct ContentHash(pub String);

#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
pub struct AttachmentMetadata {
pub name: String,
Expand Down
1 change: 1 addition & 0 deletions api/src/resources/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pub mod attachments;
pub mod audit;
pub mod bucket;
pub mod bucket_statistics;
Expand Down
5 changes: 5 additions & 0 deletions cli/src/commands/create/annotations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,11 @@ pub trait AnnotationStatistic {
fn add_failed_annotation(&self);
}

pub trait AttachmentStatistic {
fn add_attachment(&self);
fn add_failed_attachment(&self);
}

#[allow(clippy::too_many_arguments)]
pub fn upload_batch_of_annotations(
annotations_to_upload: &mut Vec<NewAnnotation>,
Expand Down
Loading

0 comments on commit 4d0b957

Please sign in to comment.