From dc24c43059ba20aeb87089de496ae241340f1d14 Mon Sep 17 00:00:00 2001 From: Sam McVicker Date: Mon, 18 Nov 2024 15:04:18 -0500 Subject: [PATCH] feat(db) added queries and implemented database_sql.rs functions. --- graphql/schema.graphql | 43 +++++++------ graphql/src/query.rs | 14 ++--- types/queries/attach_audio_to_document.sql | 23 +++++++ types/queries/document_contributor_audio.sql | 18 ++++++ .../update_document_audio_visibility.sql | 28 +++++++++ ...y.sql => update_word_audio_visibility.sql} | 0 types/src/database_sql.rs | 63 ++++++++++++++++++- 7 files changed, 157 insertions(+), 32 deletions(-) create mode 100644 types/queries/attach_audio_to_document.sql create mode 100644 types/queries/document_contributor_audio.sql create mode 100644 types/queries/update_document_audio_visibility.sql rename types/queries/{update_audio_visibility.sql => update_word_audio_visibility.sql} (100%) diff --git a/graphql/schema.graphql b/graphql/schema.graphql index dc2911a8..f9b8282d 100644 --- a/graphql/schema.graphql +++ b/graphql/schema.graphql @@ -197,30 +197,30 @@ Element within a spreadsheet before being transformed into a full document. union AnnotatedSeg = AnnotatedForm | LineBreak """ -Request to attach user-recorded audio to a word +Request to attach user-recorded audio to a document """ -input AttachAudioToWordInput { +input AttachAudioToDocumentInput { """ - Word to bind audio to + Document to bind audio to """ - wordId: UUID! + documentId: UUID! """ - A URL to a Cloudfront-proxied user-recorded pronunciation of a word. + A URL to a Cloudfront-proxied user-recorded reading of a document. A new resource will be created to represent the recording if one does not exist already """ contributorAudioUrl: String! } """ -Request to attach user-recorded audio to a document +Request to attach user-recorded audio to a word """ -input AttachAudioToDocumentInput { +input AttachAudioToWordInput { """ - Document to bind audio to + Word to bind audio to """ - documentId: UUID! + wordId: UUID! """ - A URL to a Cloudfront-proxied user-recorded reading of a document. + A URL to a Cloudfront-proxied user-recorded pronunciation of a word. A new resource will be created to represent the recording if one does not exist already """ contributorAudioUrl: String! @@ -433,13 +433,13 @@ type ContributorDetails { } """ -Request to update if a piece of word audio should be included in an edited collection +Request to update if a piece of document audio should be included in an edited collection """ -input CurateWordAudioInput { +input CurateDocumentAudioInput { """ - Word audio is attached to + Document audio is attached to """ - wordId: UUID! + documentId: UUID! """ Audio to include/exclude """ @@ -451,13 +451,13 @@ input CurateWordAudioInput { } """ -Request to update if a piece of document audio should be included in an edited collection +Request to update if a piece of word audio should be included in an edited collection """ -input CurateDocumentAudioInput { +input CurateWordAudioInput { """ - Document audio is attached to + Word audio is attached to """ - documentId: UUID! + wordId: UUID! """ Audio to include/exclude """ @@ -856,19 +856,18 @@ type Mutation { """ removeBookmark(documentId: UUID!): AnnotatedDoc! """ - Decide if a piece word audio should be included in edited collection + Decide if a piece of word audio should be included in edited collection """ curateWordAudio(input: CurateWordAudioInput!): AnnotatedForm! """ - Decide if a piece document audio should be included in edited collection + Decide if a piece of document audio should be included in edited collection """ curateDocumentAudio(input: CurateDocumentAudioInput!): AnnotatedDoc! """ Attach audio that has already been uploaded to S3 to a particular word - Assumes user requesting mutation recorded the audio + Assumes user requesting mutation recoreded the audio """ attachAudioToWord(input: AttachAudioToWordInput!): AnnotatedForm! - updateDocumentMetadata(document: DocumentMetadataUpdate!): UUID! """ Attach audio that has already been uploaded to S3 to a particular document Assumes user requesting mutation recorded the audio diff --git a/graphql/src/query.rs b/graphql/src/query.rs index a32d3a44..7f89d570 100644 --- a/graphql/src/query.rs +++ b/graphql/src/query.rs @@ -572,7 +572,7 @@ impl Mutation { let word_id = context .data::>()? .loader() - .update_audio_visibility( + .update_word_audio_visibility( &input.word_id, &input.audio_slice_id, input.include_in_edited_collection, @@ -608,9 +608,10 @@ impl Mutation { .await?; Ok(context .data::>()? - .loader() - .load_one(&document_id.ok_or_else(|| anyhow::format_err!("Document audio not found"))?) - .await?) + .load_one(dailp::DocumentId( + document_id.ok_or_else(|| anyhow::format_err!("Document not found"))? + )) + .await?.ok_or_else(|| anyhow::format_err!("Document not found"))?) } /// Attach audio that has already been uploaded to S3 to a particular word @@ -655,9 +656,8 @@ impl Mutation { .await?; Ok(context .data::>()? - .loader() - .load_one(&input.document_id) - .await?) + .load_one(dailp::DocumentId(input.document_id)) + .await?.ok_or_else(|| anyhow::format_err!("Document not found"))?) } #[graphql(guard = "GroupGuard::new(UserGroup::Editors)")] diff --git a/types/queries/attach_audio_to_document.sql b/types/queries/attach_audio_to_document.sql new file mode 100644 index 00000000..7618fd8b --- /dev/null +++ b/types/queries/attach_audio_to_document.sql @@ -0,0 +1,23 @@ +-- Binds: user_id, resource_url, start, end, document_id + +with upserted_audio_resource as ( + insert into media_resource (url, recorded_at, recorded_by) + select $2::text, now(), $1 + -- we do this no-op update to ensure an id is returned + on conflict (url) do update set url=excluded.url + returning id +), + +inserted_audio_slice as ( + insert into media_slice (resource_id, time_range) + select upserted_audio_resource.id, int8range($3, $4) + from upserted_audio_resource + returning id +) + +insert into document_user_media (document_id, media_slice_id) + select $5, inserted_audio_slice.id + from inserted_audio_slice + join document on document.id = $5 + on conflict (media_slice_id, document_id) do nothing -- document already associated + returning media_slice_id \ No newline at end of file diff --git a/types/queries/document_contributor_audio.sql b/types/queries/document_contributor_audio.sql new file mode 100644 index 00000000..3c04b8a2 --- /dev/null +++ b/types/queries/document_contributor_audio.sql @@ -0,0 +1,18 @@ +select + media_slice.id as "id", + media_slice.time_range as "range?", + media_resource.url as "resource_url", + document_user_media.include_in_edited_collection as "include_in_edited_collection", + media_resource.recorded_at as "recorded_at?", + contributor.id as "recorded_by?", + contributor.display_name as "recorded_by_name?", + editor.id as "edited_by?", + editor.display_name as "edited_by_name?" +from document_user_media + left join media_slice on media_slice.id = document_user_media.media_slice_id + left join media_resource on media_resource.id = media_slice.resource_id + left join dailp_user contributor on contributor.id = media_resource.recorded_by + left join dailp_user editor on editor.id = document_user_media.edited_by +where + document_id = $1 +order by media_resource.recorded_at desc diff --git a/types/queries/update_document_audio_visibility.sql b/types/queries/update_document_audio_visibility.sql new file mode 100644 index 00000000..4e3d7c09 --- /dev/null +++ b/types/queries/update_document_audio_visibility.sql @@ -0,0 +1,28 @@ +-- Binds: document_id, slice_id, include_in_edited_collection, editor_id + +with document_update as ( + -- update ingested audio, if the slice_id was ingested audio + update document + set include_audio_in_edited_collection=$3, + audio_edited_by=$4 + where document.id = $1 + -- if the slice_id given doesn't match, we won't update + and document.audio_slice_id = $2 + returning document.id as document_id +), +document_user_media_update as ( +-- update user contributed audio, if the slice id is user contributed audio tied to the document + update document_user_media + set include_in_edited_collection=$3, + edited_by=$4 + where document_id = $1 + and media_slice_id = $2 + returning document_id +) + +select distinct t.document_id +from ( + select document_id from document_update + union + select document_id from document_user_media_update +) as t \ No newline at end of file diff --git a/types/queries/update_audio_visibility.sql b/types/queries/update_word_audio_visibility.sql similarity index 100% rename from types/queries/update_audio_visibility.sql rename to types/queries/update_word_audio_visibility.sql diff --git a/types/src/database_sql.rs b/types/src/database_sql.rs index a235cd8b..54b92706 100644 --- a/types/src/database_sql.rs +++ b/types/src/database_sql.rs @@ -223,6 +223,20 @@ impl Database { .collect()) } + pub async fn document_contributor_audio(&self, document_id: &Uuid) -> Result> { + let contributor_audio = query_file_as!( + BasicAudioSlice, + "queries/document_contributor_audio.sql", + document_id + ) + .fetch_all(&self.client) + .await?; + Ok(contributor_audio + .into_iter() + .map(AudioSlice::from) + .collect()) + } + pub async fn words_by_doc( &self, document_id: Option, @@ -666,9 +680,31 @@ impl Database { Ok(media_slice_id) } - /// Update if a piece of audio will be shown to readers + /// As above in `attach_audio_to_word`: + /// Creates media slice if doesn't yet exist for provided audio recording. + /// Adds join table entry attaching media slice to document. + /// Returns `id` of upserted media slice. + pub async fn attach_audio_to_document( + &self, + upload: &AttachAudioToDocumentInput, + contributor_id: &Uuid, + ) -> Result { + let media_slice_id = query_file_scalar!( + "queries/attach_audio_to_document.sql", + contributor_id, + upload.contributor_audio_url as _, + 0, + 0, + upload.document_id + ) + .fetch_one(&self.client) + .await?; + Ok(media_slice_id) + } + + /// Update if a piece of word audio will be shown to readers /// Will return None if the word and audio assocation could not be found, otherwise word id. - pub async fn update_audio_visibility( + pub async fn update_word_audio_visibility( &self, word_id: &Uuid, audio_slice_id: &Uuid, @@ -676,7 +712,7 @@ impl Database { editor_id: &Uuid, ) -> Result> { let _word_id = query_file_scalar!( - "queries/update_audio_visibility.sql", + "queries/update_word_audio_visibility.sql", word_id, audio_slice_id, include_in_edited_collection, @@ -687,6 +723,27 @@ impl Database { Ok(_word_id) } + /// Update if a piece of document audio will be shown to readers + /// Will return None if the document and audio assocation could not be found, otherwise document id. + pub async fn update_document_audio_visibility( + &self, + document_id: &Uuid, + audio_slice_id: &Uuid, + include_in_edited_collection: bool, + editor_id: &Uuid, + ) -> Result> { + let _document_id = query_file_scalar!( + "queries/update_document_audio_visibility.sql", + document_id, + audio_slice_id, + include_in_edited_collection, + editor_id + ) + .fetch_one(&self.client) + .await?; + Ok(_document_id) + } + pub async fn update_document_metadata(&self, document: DocumentMetadataUpdate) -> Result { let title = document.title.into_vec(); let written_at: Option = document.written_at.value().map(Into::into);