package documentService import ( "bytes" "encoding/json" "errors" "fmt" "github.com/spf13/viper" "go.uber.org/zap" "houston/common/util" "houston/logger" "houston/model" "houston/pkg/rest" "io" "net/http" "time" ) type ActionsImpl struct { BaseURL string CustomerId string DownloadPreSignedURLGeneratorPath string UploadPreSignedURLGeneratorPath string ServiceToken string Client rest.HttpRestClient DefaultTimeout time.Duration } func NewActionsImpl(client rest.HttpRestClient) *ActionsImpl { return &ActionsImpl{ Client: client, BaseURL: viper.GetString("DOCUMENT_SERVICE_BASEURL"), CustomerId: viper.GetString("DOCUMENT_SERVICE_CUSTOMER_ID"), ServiceToken: viper.GetString("DOCUMENT_SERVICE_TOKEN"), DownloadPreSignedURLGeneratorPath: viper.GetString("DOCUMENT_SERVICE_DOWNLOAD_PRE_SIGNED_URL_GENERATOR_PATH"), UploadPreSignedURLGeneratorPath: viper.GetString("DOCUMENT_SERVICE_UPLOAD_PRE_SIGNED_URL_GENERATOR_PATH"), DefaultTimeout: viper.GetDuration("DEFAULT_DOCUMENT_SERVICE_TIMEOUT"), } } func (documentService *ActionsImpl) GenerateFileUploadPreSignedURL(fileType string, flowId string) (*model.FileUploadURLGeneratorResponse, error) { fullURL := documentService.BaseURL + documentService.UploadPreSignedURLGeneratorPath fileUploadURLRequestPayload := model.FileUploadURLRequest{ CustomerId: documentService.CustomerId, FileType: fileType, FlowId: flowId, } payload, err := json.Marshal(fileUploadURLRequestPayload) requestHeaders := map[string]string{ "Authorization": "Basic " + documentService.ServiceToken, "X-Service": "houston", "X-Source": "HOUSTON", "Content-Type": util.ContentTypeJSON, } response, err := documentService.Client.PostWithTimeout(fullURL, *bytes.NewBuffer(payload), requestHeaders, documentService.DefaultTimeout, nil) if err != nil { logger.Error("Error while generating pre-signed url", zap.Error(err)) return nil, err } if response.StatusCode == http.StatusOK { logger.Info("File upload URL generated successfully.") } else { logger.Error("File upload URL generation failed with status code:", zap.Int("status_code", response.StatusCode)) } if response.Body == nil { logger.Error("response body is nil") return nil, errors.New("response body is nil") } responseBody, err := io.ReadAll(response.Body) if err != nil { logger.Error("Error while reading response body", zap.Error(err)) return nil, err } var fileUploadURLGeneratorResponse *model.FileUploadURLGeneratorResponse err = json.Unmarshal(responseBody, &fileUploadURLGeneratorResponse) if err != nil { logger.Error("error while unmarshalling response body", zap.Error(err)) return nil, err } if fileUploadURLGeneratorResponse.Url == "" { logger.Error("file upload URL is empty") return nil, errors.New("file upload URL is empty") } return fileUploadURLGeneratorResponse, nil } func (documentService *ActionsImpl) UploadFileWithPreSignedURL( fileUploadResponse *model.FileUploadURLGeneratorResponse, fileName string, file io.Reader, contentType string) error { formData := map[string]interface{}{ "Policy": fileUploadResponse.FormData.Policy, "X-Amz-Algorithm": fileUploadResponse.FormData.XAmzAlgorithm, "X-Amz-Credential": fileUploadResponse.FormData.XAmzCredential, "X-Amz-Date": fileUploadResponse.FormData.XAmzDate, "X-Amz-Security-Token": fileUploadResponse.FormData.XAmzSecurityToken, "X-Amz-Signature": fileUploadResponse.FormData.XAmzSignature, "bucket": fileUploadResponse.FormData.Bucket, "key": fileUploadResponse.FormData.Key, "Content-type": contentType, "file": file, "filename": fileName, } resp, err := documentService.Client.PostWithTimeout(fileUploadResponse.Url, bytes.Buffer{}, nil, documentService.DefaultTimeout, formData) if err != nil { logger.Error("Error while uploading file", zap.Error(err)) return err } // Check the response status code if resp.StatusCode == http.StatusNoContent { logger.Info("File uploaded successfully.") } else { logger.Error("File upload failed with status code:", zap.Int("status_code", resp.StatusCode)) return errors.New("file upload failed") } return nil } func (documentService *ActionsImpl) GenerateFileDownloadPreSignedURL(fileDownloadPreSignedURLRequest model.FileDownloadPreSignedURLRequest) (string, error) { documentDownloadURL := documentService.BaseURL + documentService.DownloadPreSignedURLGeneratorPath params := map[string]string{ "customerId": documentService.CustomerId, "flowId": fileDownloadPreSignedURLRequest.FlowId, "identifierKey": fileDownloadPreSignedURLRequest.IdentifierKey, } requestHeaders := map[string]string{ "Authorization": "Basic " + documentService.ServiceToken, "X-Service": "houston", "X-Source": "HOUSTON", "Content-Type": util.ContentTypeJSON, } response, err := documentService.Client.GetWithTimeout(documentDownloadURL, params, requestHeaders, documentService.DefaultTimeout, false) if err != nil { logger.Error("Error while generating pre-signed url", zap.Error(err)) return "", err } if response.StatusCode == http.StatusOK { logger.Info("File download URL generated successfully.") } else { logger.Error("File download URL generation failed with status code:", zap.Int("status_code", response.StatusCode)) } responseBody, err := io.ReadAll(response.Body) if err != nil { logger.Error("Error while reading response body", zap.Error(err)) return "", err } var fileDownloadURLResponse *model.FileDownloadURLResponse err = json.Unmarshal(responseBody, &fileDownloadURLResponse) if err != nil { logger.Error("Error while unmarshalling response body", zap.Error(err)) return "", err } if fileDownloadURLResponse.StatusCode != 200 || fileDownloadURLResponse.Data.Url == "" { logger.Error("file download URL generation failed") return "", errors.New("file download URL generation failed") } return fileDownloadURLResponse.Data.Url, nil } func (documentService *ActionsImpl) UploadFileAndGetDownloadPreSignedUrl(file io.Reader, fileType string, fileName string, contentType string, flowId string) (string, string, error) { fileUploadResponse, err := documentService.GenerateFileUploadPreSignedURL(fileType, flowId) if err != nil { logger.Error(fmt.Sprintf("Error generating upload pre-signed url for file name %s", fileName), zap.Error(err)) return "", "", err } err = documentService.UploadFileWithPreSignedURL(fileUploadResponse, fileName, file, contentType) if err != nil { logger.Error(fmt.Sprintf("Error uploading file of file name %s", fileName), zap.Error(err)) return "", "", err } fileDownloadURL, err := documentService.GenerateFileDownloadPreSignedURL(model.FileDownloadPreSignedURLRequest{ FlowId: flowId, IdentifierKey: fileUploadResponse.FormData.Key, }) if err != nil { logger.Error(fmt.Sprintf("Error generating download pre-signed url for file name %s", fileName), zap.Error(err)) return "", "", err } return fileDownloadURL, fileUploadResponse.FormData.Key, nil }