github.com/Files-com/files-sdk-go/v3@v3.1.81/file/client.go (about) 1 package file 2 3 import ( 4 "io" 5 "io/fs" 6 "net/http" 7 "net/url" 8 "time" 9 10 "github.com/Files-com/files-sdk-go/v3/downloadurl" 11 "github.com/Files-com/files-sdk-go/v3/file/manager" 12 "github.com/Files-com/files-sdk-go/v3/folder" 13 14 files_sdk "github.com/Files-com/files-sdk-go/v3" 15 lib "github.com/Files-com/files-sdk-go/v3/lib" 16 ) 17 18 type Client struct { 19 files_sdk.Config 20 } 21 22 func (c *Client) Get(Path string, opts ...files_sdk.RequestResponseOption) (files_sdk.File, error) { 23 file := files_sdk.File{} 24 path, err := lib.BuildPath("/files/{path}", map[string]string{"path": Path}) 25 if err != nil { 26 return file, err 27 } 28 data, _, err := files_sdk.Call("GET", c.Config, path, lib.Params{Params: lib.Interface()}, opts...) 29 if err != nil { 30 return file, err 31 } 32 if err := file.UnmarshalJSON(*data); err != nil { 33 return file, err 34 } 35 36 return file, nil 37 } 38 39 func Get(Path string, opts ...files_sdk.RequestResponseOption) (files_sdk.File, error) { 40 client := Client{} 41 return client.Get(Path, opts...) 42 } 43 44 // File{}.Size and File{}.Mtime are not always up to date. This calls HEAD on File{}.DownloadUri to get the latest info. 45 // Some Download URLs won't support HEAD. In this case the size is reported as UntrustedSizeValue. The size can be known post download 46 // using Client{}.DownloadRequestStatus. This applies to the remote mount types FTP, SFTP, and WebDAV. 47 func (c *Client) FileStats(file files_sdk.File, opts ...files_sdk.RequestResponseOption) (files_sdk.File, error) { 48 var err error 49 var size int64 50 file, err = c.Download( 51 files_sdk.FileDownloadParams{File: file}, 52 append(opts, 53 files_sdk.RequestOption(func(req *http.Request) error { 54 if req.URL.Host != "s3.amazonaws.com" { 55 req.Method = "HEAD" 56 } 57 return nil 58 }), 59 files_sdk.ResponseOption(func(response *http.Response) error { 60 if response.StatusCode == 422 { 61 size = int64(UntrustedSizeValue) 62 return nil 63 } 64 if err := lib.ResponseErrors(response, lib.NonOkError); err != nil { 65 return err 66 } 67 size = response.ContentLength 68 if response.Header.Get("Last-Modified") != "" { 69 mtime, err := time.Parse(time.RFC1123, response.Header.Get("Last-Modified")) 70 if err == nil { 71 file.Mtime = &mtime 72 } 73 } 74 return response.Body.Close() 75 }), 76 )..., 77 ) 78 if err == nil { 79 file.Size = size 80 } 81 return file, err 82 } 83 84 func (c *Client) DownloadRequestStatus(fileDownloadUrl string, downloadRequestId string, opts ...files_sdk.RequestResponseOption) (files_sdk.ResponseError, error) { 85 re := files_sdk.ResponseError{} 86 uri, err := url.Parse(fileDownloadUrl) 87 if err != nil { 88 return re, err 89 } 90 91 uri = uri.JoinPath(downloadRequestId) 92 93 request, err := http.NewRequest("GET", uri.String(), nil) 94 if err != nil { 95 return re, err 96 } 97 98 c.SetHeaders(&request.Header) 99 100 resp, err := files_sdk.WrapRequestOptions(c.Config, request, opts...) 101 if err != nil { 102 return re, err 103 } 104 data, err := io.ReadAll(resp.Body) 105 if err != nil { 106 return re, err 107 } 108 if lib.IsJSON(resp) { 109 err = re.UnmarshalJSON(data) 110 if err != nil { 111 return re, err 112 } 113 if re.Type == "" && !re.IsNil() { 114 re.Type = "download request status" 115 } 116 } 117 return re, err 118 } 119 120 func (c *Client) DownloadUri(params files_sdk.FileDownloadParams, opts ...files_sdk.RequestResponseOption) (files_sdk.File, error) { 121 var err error 122 if params.Path == "" { 123 params.Path = params.File.Path 124 } 125 126 if params.File.DownloadUri == "" { 127 err = files_sdk.Resource(c.Config, lib.Resource{Method: "GET", Path: "/files/{path}", Params: params, Entity: ¶ms.File}, opts...) 128 return params.File, err 129 } else { 130 url, parseErr := downloadurl.New(params.File.DownloadUri) 131 remaining, valid := url.Valid(time.Millisecond * 100) 132 if parseErr == nil { 133 if !valid { 134 err = files_sdk.Resource(c.Config, lib.Resource{Method: "GET", Path: "/files/{path}", Params: params, Entity: ¶ms.File}) 135 if params.File.DownloadUri == url.URL.String() { 136 c.LogPath(params.Path, map[string]interface{}{"message": "URL was expired. Fetched a new URL but it didn't change", "Remaining": remaining, "Time": url.Time}) 137 } else { 138 c.LogPath(params.Path, map[string]interface{}{"message": "URL was expired. Fetched a new URL", "Remaining": remaining, "Time": url.Time}) 139 } 140 } 141 } 142 } 143 144 return params.File, err 145 } 146 147 func (c *Client) Download(params files_sdk.FileDownloadParams, opts ...files_sdk.RequestResponseOption) (files_sdk.File, error) { 148 if params.Path == "" { 149 params.Path = params.File.Path 150 } 151 var err error 152 153 params.File, err = c.DownloadUri(params, files_sdk.WithContext(files_sdk.ContextOption(opts))) 154 if err != nil { 155 return params.File, err 156 } 157 request, err := http.NewRequest("GET", params.File.DownloadUri, nil) 158 if err != nil { 159 return params.File, err 160 } 161 162 c.SetHeaders(&request.Header) 163 164 _, err = files_sdk.WrapRequestOptions(c.Config, request, opts...) 165 166 return params.File, err 167 } 168 169 func Download(params files_sdk.FileDownloadParams, opts ...files_sdk.RequestResponseOption) (files_sdk.File, error) { 170 client := Client{} 171 return client.Download(params, opts...) 172 } 173 174 func (c *Client) Create(params files_sdk.FileCreateParams, opts ...files_sdk.RequestResponseOption) (file files_sdk.File, err error) { 175 err = files_sdk.Resource(c.Config, lib.Resource{Method: "POST", Path: "/files/{path}", Params: params, Entity: &file}, opts...) 176 return 177 } 178 179 func Create(params files_sdk.FileCreateParams, opts ...files_sdk.RequestResponseOption) (file files_sdk.File, err error) { 180 return (&Client{}).Create(params, opts...) 181 } 182 183 func (c *Client) Update(params files_sdk.FileUpdateParams, opts ...files_sdk.RequestResponseOption) (file files_sdk.File, err error) { 184 err = files_sdk.Resource(c.Config, lib.Resource{Method: "PATCH", Path: "/files/{path}", Params: params, Entity: &file}, opts...) 185 return 186 } 187 188 func Update(params files_sdk.FileUpdateParams, opts ...files_sdk.RequestResponseOption) (file files_sdk.File, err error) { 189 return (&Client{}).Update(params, opts...) 190 } 191 192 func (c *Client) UpdateWithMap(params map[string]interface{}, opts ...files_sdk.RequestResponseOption) (file files_sdk.File, err error) { 193 err = files_sdk.Resource(c.Config, lib.Resource{Method: "PATCH", Path: "/files/{path}", Params: params, Entity: &file}, opts...) 194 return 195 } 196 197 func UpdateWithMap(params map[string]interface{}, opts ...files_sdk.RequestResponseOption) (file files_sdk.File, err error) { 198 return (&Client{}).UpdateWithMap(params, opts...) 199 } 200 201 func (c *Client) Delete(params files_sdk.FileDeleteParams, opts ...files_sdk.RequestResponseOption) (err error) { 202 err = files_sdk.Resource(c.Config, lib.Resource{Method: "DELETE", Path: "/files/{path}", Params: params, Entity: nil}, opts...) 203 return 204 } 205 206 func Delete(params files_sdk.FileDeleteParams, opts ...files_sdk.RequestResponseOption) (err error) { 207 return (&Client{}).Delete(params, opts...) 208 } 209 210 func (c *Client) Find(params files_sdk.FileFindParams, opts ...files_sdk.RequestResponseOption) (file files_sdk.File, err error) { 211 err = files_sdk.Resource(c.Config, lib.Resource{Method: "GET", Path: "/file_actions/metadata/{path}", Params: params, Entity: &file}, opts...) 212 return 213 } 214 215 func Find(params files_sdk.FileFindParams, opts ...files_sdk.RequestResponseOption) (file files_sdk.File, err error) { 216 return (&Client{}).Find(params, opts...) 217 } 218 219 func (c *Client) Copy(params files_sdk.FileCopyParams, opts ...files_sdk.RequestResponseOption) (fileAction files_sdk.FileAction, err error) { 220 err = files_sdk.Resource(c.Config, lib.Resource{Method: "POST", Path: "/file_actions/copy/{path}", Params: params, Entity: &fileAction}, opts...) 221 return 222 } 223 224 func Copy(params files_sdk.FileCopyParams, opts ...files_sdk.RequestResponseOption) (fileAction files_sdk.FileAction, err error) { 225 return (&Client{}).Copy(params, opts...) 226 } 227 228 func (c *Client) Move(params files_sdk.FileMoveParams, opts ...files_sdk.RequestResponseOption) (fileAction files_sdk.FileAction, err error) { 229 err = files_sdk.Resource(c.Config, lib.Resource{Method: "POST", Path: "/file_actions/move/{path}", Params: params, Entity: &fileAction}, opts...) 230 return 231 } 232 233 func Move(params files_sdk.FileMoveParams, opts ...files_sdk.RequestResponseOption) (fileAction files_sdk.FileAction, err error) { 234 return (&Client{}).Move(params, opts...) 235 } 236 237 func (c *Client) BeginUpload(params files_sdk.FileBeginUploadParams, opts ...files_sdk.RequestResponseOption) (fileUploadPartCollection files_sdk.FileUploadPartCollection, err error) { 238 err = files_sdk.Resource(c.Config, lib.Resource{Method: "POST", Path: "/file_actions/begin_upload/{path}", Params: params, Entity: &fileUploadPartCollection}, opts...) 239 return 240 } 241 242 func BeginUpload(params files_sdk.FileBeginUploadParams, opts ...files_sdk.RequestResponseOption) (fileUploadPartCollection files_sdk.FileUploadPartCollection, err error) { 243 return (&Client{}).BeginUpload(params, opts...) 244 } 245 246 type Iter struct { 247 *folder.Iter 248 } 249 250 var _ files_sdk.ResourceIterator = Iter{} 251 var _ files_sdk.ResourceLoader = Iter{} 252 253 func (i Iter) LoadResource(identifier interface{}, opts ...files_sdk.RequestResponseOption) (interface{}, error) { 254 params := files_sdk.FileFindParams{} 255 if path, ok := identifier.(string); ok { 256 params.Path = path 257 } 258 259 return (&Client{Config: i.Config}).Find(params, opts...) 260 } 261 262 func (i Iter) Iterate(identifier interface{}, opts ...files_sdk.RequestResponseOption) (files_sdk.IterI, error) { 263 it, err := i.Iter.Iterate(identifier, opts...) 264 return Iter{Iter: it.(*folder.Iter)}, err 265 } 266 267 func (c *Client) ListFor(params files_sdk.FolderListForParams, opts ...files_sdk.RequestResponseOption) (Iter, error) { 268 it, err := (&folder.Client{Config: c.Config}).ListFor(params, opts...) 269 return Iter{Iter: it}, err 270 } 271 272 type RecursiveItem struct { 273 files_sdk.File 274 error `json:"error"` 275 } 276 277 func (r RecursiveItem) Err() error { 278 return r.error 279 } 280 281 func (c *Client) ListForRecursive(params files_sdk.FolderListForParams, opts ...files_sdk.RequestResponseOption) (files_sdk.TypedIterI[RecursiveItem], error) { 282 if params.ConcurrencyManager == nil { 283 params.ConcurrencyManager = manager.Default().FilePartsManager 284 } 285 // Find the path first for recursive operations 286 fsi := (&FS{}).Init(c.Config, true).WithContext(files_sdk.ContextOption(opts)).(*FS) 287 fStat, err := fs.Stat(fsi, params.Path) 288 if err != nil { 289 return nil, err 290 } 291 // The path is a directory and the literal strings do not match use our returned path 292 if fStat.Sys().(files_sdk.File).Type == "directory" && fStat.Sys().(files_sdk.File).Path != params.Path { 293 params.Path = fStat.Sys().(files_sdk.File).Path 294 } 295 296 return (&lib.Walk[RecursiveItem]{ 297 FS: fsi, 298 Root: lib.UrlJoinNoEscape(params.Path), 299 ConcurrencyManager: params.ConcurrencyManager, 300 ListDirectories: true, 301 WalkFile: func(d fs.DirEntry, path string, err error) (RecursiveItem, error) { 302 info, infoErr := d.Info() 303 if infoErr == nil { 304 return RecursiveItem{info.Sys().(files_sdk.File), err}, nil 305 } else if err != nil { 306 return RecursiveItem{}, err 307 } else { 308 return RecursiveItem{}, infoErr 309 } 310 }, 311 }).Walk(files_sdk.ContextOption(opts)), nil 312 }