Files

213 lines
5.4 KiB
Go

package onspring
import (
"bytes"
"context"
"fmt"
"io"
"mime"
"mime/multipart"
"net/http"
"strconv"
)
const (
filesPath = "/files"
)
// FilesEndpoint provides access to files in an Onspring instance.
type FilesEndpoint struct {
client *Client
}
// FileInfo represents the metadata of a file in Onspring.
type FileInfo struct {
Type string `json:"type"`
ContentType string `json:"contentType"`
Name string `json:"name"`
CreatedDate string `json:"createdDate"`
ModifiedDate string `json:"modifiedDate"`
Owner string `json:"owner"`
Notes string `json:"notes"`
FileHref string `json:"fileHref"`
}
// FileContent represents the content of a downloaded file.
type FileContent struct {
FileName string
ContentType string
Data []byte
}
// SaveFileRequest represents a request to upload a file.
type SaveFileRequest struct {
RecordId int
FieldId int
Notes string
ModifiedDate string
FileName string
FileContents io.Reader
}
// SaveFileResponse represents the response for saving a file.
type SaveFileResponse struct {
Id int `json:"id"`
}
func fileBasePath(recordId, fieldId, fileId int) string {
return fmt.Sprintf("%s/recordId/%d/fieldId/%d/fileId/%d", filesPath, recordId, fieldId, fileId)
}
// GetInfo retrieves a file's metadata from the Onspring API.
//
// Parameters:
// - ctx: The context for the request
// - recordId: The id of the record
// - fieldId: The id of the field
// - fileId: The id of the file
//
// Returns:
// - FileInfo: The file's metadata
// - error: An error if the request fails
func (f *FilesEndpoint) GetInfo(ctx context.Context, recordId, fieldId, fileId int) (FileInfo, error) {
path := fileBasePath(recordId, fieldId, fileId)
req, requestCreationErr := f.client.newRequest(ctx, http.MethodGet, path, nil, nil)
var fileInfo FileInfo
if requestCreationErr != nil {
return fileInfo, requestCreationErr
}
responseErr := f.client.doWithJsonResponse(req, &fileInfo)
if responseErr != nil {
return fileInfo, responseErr
}
return fileInfo, nil
}
// GetContent retrieves a file's content from the Onspring API.
//
// Parameters:
// - ctx: The context for the request
// - recordId: The id of the record
// - fieldId: The id of the field
// - fileId: The id of the file
//
// Returns:
// - FileContent: The file's content including name, content type, and data
// - error: An error if the request fails
func (f *FilesEndpoint) GetContent(ctx context.Context, recordId, fieldId, fileId int) (FileContent, error) {
path := fmt.Sprintf("%s/file", fileBasePath(recordId, fieldId, fileId))
req, requestCreationErr := f.client.newRequest(ctx, http.MethodGet, path, nil, nil)
var fileContent FileContent
if requestCreationErr != nil {
return fileContent, requestCreationErr
}
data, headers, responseErr := f.client.doWithBytesResponse(req)
if responseErr != nil {
return fileContent, responseErr
}
fileContent.Data = data
fileContent.ContentType = headers.Get("Content-Type")
contentDisposition := headers.Get("Content-Disposition")
if contentDisposition != "" {
_, params, err := mime.ParseMediaType(contentDisposition)
if err == nil {
fileContent.FileName = params["filename"]
}
}
return fileContent, nil
}
// Delete removes a file from the Onspring API.
//
// Parameters:
// - ctx: The context for the request
// - recordId: The id of the record
// - fieldId: The id of the field
// - fileId: The id of the file
//
// Returns:
// - error: An error if the request fails
func (f *FilesEndpoint) Delete(ctx context.Context, recordId, fieldId, fileId int) error {
path := fileBasePath(recordId, fieldId, fileId)
req, requestCreationErr := f.client.newRequest(ctx, http.MethodDelete, path, nil, nil)
if requestCreationErr != nil {
return requestCreationErr
}
return f.client.do(req)
}
// Save uploads a file to the Onspring API.
//
// Parameters:
// - ctx: The context for the request
// - saveReq: The file upload request containing record id, field id, file name, contents, and optional metadata
//
// Returns:
// - SaveFileResponse: The response containing the saved file's id
// - error: An error if the request fails
func (f *FilesEndpoint) Save(ctx context.Context, saveReq SaveFileRequest) (SaveFileResponse, error) {
var response SaveFileResponse
body := &bytes.Buffer{}
writer := multipart.NewWriter(body)
_ = writer.WriteField("RecordId", strconv.Itoa(saveReq.RecordId))
_ = writer.WriteField("FieldId", strconv.Itoa(saveReq.FieldId))
if saveReq.Notes != "" {
_ = writer.WriteField("Notes", saveReq.Notes)
}
if saveReq.ModifiedDate != "" {
_ = writer.WriteField("ModifiedDate", saveReq.ModifiedDate)
}
filePart, err := writer.CreateFormFile("File", saveReq.FileName)
if err != nil {
return response, fmt.Errorf("failed to create form file: %w", err)
}
_, err = io.Copy(filePart, saveReq.FileContents)
if err != nil {
return response, fmt.Errorf("failed to copy file contents: %w", err)
}
err = writer.Close()
if err != nil {
return response, fmt.Errorf("failed to close multipart writer: %w", err)
}
req, requestCreationErr := f.client.newMultipartRequest(ctx, filesPath, body, writer.FormDataContentType())
if requestCreationErr != nil {
return response, requestCreationErr
}
responseErr := f.client.doWithJsonResponse(req, &response)
if responseErr != nil {
return response, responseErr
}
return response, nil
}