Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implementing Attachments #18

Merged
merged 4 commits into from
Jun 17, 2024
Merged

Implementing Attachments #18

merged 4 commits into from
Jun 17, 2024

Conversation

mliao95
Copy link
Contributor

@mliao95 mliao95 commented Jun 6, 2024

Issue #, if available:

Description of changes:

  • Implementing sendAttachment
    • StartAttachmentUpload, PUT request to S3 bucket, CompleteAttachmentUpload
  • Implementing getAttachmentDownloadUrl:
    • Calls GetAttachment ConnectParticipantAPI and returns URL for user to download attachment
  • Implementing downloadAttachment:
    • Calls getAttachmentDownloadUrl to get download link, downloads file to temporary local storage, returns file URL of temporarily stored file
  • Unit testing
  • Moved .gitignore to directory root
  • Minor quality of live improvements to code

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

@mliao95 mliao95 requested a review from mrajatttt June 6, 2024 01:25
@mliao95 mliao95 requested a review from a team as a code owner June 6, 2024 01:25
@mliao95 mliao95 requested review from doreechi and removed request for a team June 6, 2024 01:25
@mliao95 mliao95 marked this pull request as draft June 6, 2024 03:39
@mliao95 mliao95 marked this pull request as ready for review June 11, 2024 03:27
}
}

self.httpClient.putJson((response.uploadMetadata?.url)!, headersToInclude, fileData) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shall we move this entire API calling logic to a separate file? ApiProtocol + ApiClient?
The reason i am thinking is because we are intending chatService to act as a router file and making actual call here might be misleading when we are expanding. We can also put the metrics uploading api there?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Created APIProtocol and APIClient

switch result {
case .success(let response):
if let url = URL(string: response.url!) {
self.downloadFile(url: url, filename: filename) { (localUrl, error) in
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shall we do this or instead let client take care of it?
Reason: They might wanna show progress/notification for downloading large files etc.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would imagine downloading would fall more under business logic than UI logic, so I think the right place for it would still be the SDK. This might be more of a niche case considering attachments are capped at 20MB. We can consider exposing GetAttachment API in addition to this one in the future in case customers want to trigger and manage their own download.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is true, we can do that. My worry was for large files and user not knowing about how is it being downloading after they hit getAttachment. Like these two operations could take significant time based on n/w. Shall we consider a callback for progress update or similar?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added getAttachmentDownloadUrl for customers who want to handle the download themselves

let tempFilePathUrl = tempDirectory.appendingPathComponent(filename)


if FileManager.default.fileExists(atPath: tempFilePathUrl.path) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we requesting any sort of permission to access it? If thats the case then we should let client take care of it imo

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FileManager.default.temporaryDirectory lives within the app's sandbox and doesn't persist across sessions/apps, so no need to request additional permissions.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see.

chatService.downloadAttachment(attachmentId: attachmentId, filename: filename) { result in
switch result {
case .success(let localUrl):
print("Local URL: \(String(describing: localUrl))")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: was it intentional? I am looking out for any app sec flags

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed

@mrajatttt mrajatttt self-requested a review June 12, 2024 17:56
mrajatttt
mrajatttt previously approved these changes Jun 12, 2024
case doc = "application/msword"
case docx = "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
case heic = "image/heic"
case jpg = "image/jpeg"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is there a difference between jpeg and jpg? also do we need to add jfif?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.jpg , .jpeg , .jpe , .jif , .jfif all map to image/jpeg MIME type.

/// - completion: Completion handler to handle the success status or error.
func completeAttachmentUpload(connectionToken: String, attachmentIds: [String], completion: @escaping (Result<AWSConnectParticipantCompleteAttachmentUploadResponse, Error>) -> Void)

func getAttachment(connectionToken: String, attachmentId: String, completion: @escaping (Result<AWSConnectParticipantGetAttachmentResponse, Error>) -> Void)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit - description?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated

return nil
}
let time = CommonUtils().formatTime(innerJson["AbsoluteTime"] as! String)!
switch type {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we need null check for type?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The guard statement above will execute the else block if either typeString or type is nil (null).

guard let typeString = innerJson["Type"] as? String, let type = WebSocketMessageType(rawValue: typeString) else {
                print("Unknown websocket message type: \(String(describing: innerJson["Type"]))")
                return nil
            }

return nil
}

let message = Message(

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what message is this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For this function, if an attachment message comes through the websocket, we translate the data into a Message object for the customer to then parse in the UI.

Pretty much instead of letting the customer parse the JSON themselves, they can access message properties via message.text or message.timeStamp

@@ -7,6 +7,6 @@ import AWSCore
// Temporary using this file untill we figure out how to separate sender and reciever based on thier name or role.

class Config {
let isDevMode = false // TODO: Set up isDevMode
let isDevMode = true // TODO: Set up isDevMode

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit - is this a temp dev change?

}
}

// Print the entire request for debugging

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are we going to remove this in the future? or at least redact sensitive info like connection token??

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah we will remove this after pen-testing concludes. They want all logging enabled for now

Copy link
Contributor

@mrajatttt mrajatttt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good!

@mliao95 mliao95 merged commit 2287910 into main Jun 17, 2024
3 checks passed
@mrajatttt mrajatttt deleted the mikeliao/attachments branch July 21, 2024 05:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants