9.3 KiB
go-nc-files guide
How to use the library. Symbol signatures and doc comments are in API. Words for the domain are in CONTEXT.md.
Sentinel errors are ErrNotExist, ErrForbidden, and ErrNotDir. IsNotExist and IsForbidden report whether an error wraps the first two. The features below name which calls return them.
Files seam
Purpose
Root opens a folder by a User Files Root–relative path. Folder is flat storage inside that folder: list, read, write, exists, and remove, by basename only.
When to use it
Write product code against Root and Folder. Pass a Local root in tests and a WebDAVRoot against Nextcloud. The rest of the call sequence stays the same.
Call sequence
- Obtain a
Root(LocalorWebDAVRoot). OpenFolderwhen the folder must already exist.EnsureFolderwhen missing parents should be created.- On the
Folder, callList,Read,Write,Exists, orRemovewith a basename.
List returns first-level names. Exists reports a non-directory file. Entry.IsDir is true for a child directory.
Errors
OpenFolder and EnsureFolder return a plain error when the relative path is invalid (CleanRel yields empty). A missing folder from OpenFolder wraps ErrNotExist. A path that exists and is not a directory wraps ErrNotDir. Permission failures wrap ErrForbidden.
Read, Write, Exists, and Remove return an error from RequireBasename when the name is not a single basename. Remove and WebDAV Read wrap ErrNotExist when the child is missing. Exists returns false and a nil error when the child is missing.
Example
folder, err := root.OpenFolder("Zones")
if err != nil {
return err
}
if err := folder.Write("note.txt", []byte("hi")); err != nil {
return err
}
data, err := folder.Read("note.txt")
Local
Purpose
Local is a User Files Root on the local filesystem. RootDir is the absolute directory that corresponds to the user's files root.
When to use it
Use Local for tests and for go run without Nextcloud. Use LocalFolder when the caller already has an absolute directory and only needs a Folder. LocalPath reports that absolute directory when the Folder came from the local adapter.
Call sequence
- Set
Local.RootDir. - Call
OpenFolderorEnsureFolderwith a relative path.EnsureFoldercreates the directory with mode0755. - Or call
LocalFolderwith an absolute directory and skip the root.
OpenLocal is the Visit helper that pairs Local with a FileStore. It is described under Visit.
Errors
OpenFolder wraps ErrNotExist, ErrForbidden, or ErrNotDir, or returns a plain error for an invalid relative path. EnsureFolder wraps ErrForbidden when the process cannot create the directory.
Example
root := goncfiles.Local{RootDir: filesRoot}
folder, err := root.EnsureFolder("myapp")
if err != nil {
return err
}
_ = folder
WebDAVRoot
Purpose
WebDAVRoot is a User Files Root at /remote.php/dav/files/{user}/. Cred supplies the user id and AppAPI auth headers from go-nc-exapp. A nil Client uses http.DefaultClient.
When to use it
Use WebDAVRoot when the Working Folder is the user's Nextcloud files. The same Root and Folder methods apply as with Local.
Call sequence
- Fill
gonexapp.Credentials, includingBaseURLandUserID. - Store them on
WebDAVRoot.Cred. OpenFolderchecks the folder with PROPFIND and does not create it.EnsureFoldercreates missing parents with MKCOL, then opens the folder.Listuses PROPFIND with depth 1.Readuses GET.Writeuses PUT.Removeuses DELETE.Existsuses HEAD and falls back to GET when the status is neither success, not found, nor forbidden.
Errors
An invalid relative path returns a plain error and does not call the server. OpenFolder wraps ErrNotExist on HTTP 404, ErrForbidden on 401 or 403, and ErrNotDir when the resource is not a collection. EnsureFolder wraps ErrForbidden on 401 or 403. Other unexpected statuses are plain errors that include the HTTP status.
Example
root := goncfiles.WebDAVRoot{Cred: cred}
folder, err := root.OpenFolder("Zones")
if err != nil {
if goncfiles.IsNotExist(err) {
return fmt.Errorf("folder missing")
}
return err
}
_ = folder
CleanRel
Purpose
CleanRel normalizes a User Files Root–relative path. The result uses forward slashes and has no leading or trailing slash.
When to use it
The roots call CleanRel themselves. Call it directly when the product must reject a path before opening a folder, or must show the normalized form.
Call sequence
- Pass the raw path from a query parameter or a saved preference.
- Treat an empty result as invalid. Do not open it.
Backslashes are treated as slashes. .., URLs, and a bare numeric Nextcloud file ID are rejected.
Errors
CleanRel does not return an error. An empty string means the path is not usable. Callers that need an error, such as OpenFolder, turn that empty result into invalid Working Folder path.
Example
rel := goncfiles.CleanRel(r.URL.Query().Get("folder"))
if rel == "" {
http.Error(w, "invalid folder", http.StatusBadRequest)
return
}
RequireBasename
Purpose
RequireBasename accepts a single file name inside a Working Folder and rejects a path.
When to use it
Folder methods call it before touching a child. Call it directly when the product checks a name before building a request.
Call sequence
- Pass the child name.
- Continue only when the error is nil.
An empty name, a name containing / or \, a name containing .., or a name that is not path.Base of itself is rejected.
Errors
The error text is name must be a basename in the Working Folder. It does not wrap ErrNotExist or ErrForbidden.
Example
if err := goncfiles.RequireBasename(name); err != nil {
return err
}
DefaultPathStore
Purpose
DefaultPathStore persists the Saved Default Working Folder path, relative to the User Files Root. GetDefault returns that path, or empty when none is stored. SetDefault writes a new path.
When to use it
Visit reads and writes the Saved Default through this interface. FileStore is a JSON file (defaultPath) for the local adapter. Memory is an in-memory store for tests. An ExApp that stores the path in Nextcloud preferences implements the same two methods around gonexapp.AppAPIPreferences. This library does not call OCS.
Call sequence
- Construct a
FileStorewith a file path, aMemory, or a product store. - Pass it to
Open. GetDefaulton a missing file returns empty and a nil error.SetDefaultwrites the path.FileStorecreates parent directories and writes indented JSON.
Visit.SetDefault is the call that also ensures the folder exists. It is described under Visit.
Errors
FileStore.GetDefault returns an error when the file exists and cannot be read or is not JSON. A missing file is not an error. FileStore.SetDefault returns an error when the directory cannot be created or the file cannot be written. Memory does not return an error.
Example
store := goncfiles.FileStore{Path: filepath.Join(dir, "settings.json")}
saved, err := store.GetDefault()
if err != nil {
return err
}
_ = saved
Visit
Purpose
A Visit is one opening of the ExApp: the Working Folder for this request, and the Saved Default. App-icon launch uses the Saved Default and creates the initial folder when it is missing. A Files-view folder is used only for this visit and does not change the Saved Default.
When to use it
Call Open at the start of a request that needs the user's Working Folder. Use OpenLocal when both the files root and the settings file are on local disk.
Call sequence
- Call
Openwith aRoot, aDefaultPathStore, the product's initial folder name, and the visit-relative path. - Pass an empty visit-relative path for an app-icon launch.
Openreads the Saved Default, usesinitialFolderNamewhen nothing is stored, andEnsureFolders that path. - Pass the Files-view folder for a visit-only path.
OpenusesOpenFolderand does not create it and does not fall back to the Saved Default. - Use
Visit.FSas theFolder,WorkingRelas the relative path, andSavedDefaultas the stored default. - Call
SetDefaultwhen the user picks a new default. ItEnsureFolders the path, stores it, and updatesSavedDefault. It does not switchFSto that folder. WorkingDirreturns a local absolute path whenFSis a local folder, andWorkingRelotherwise.
Errors
Open returns the error from GetDefault. An invalid saved path or an invalid visit path is a plain error (invalid Working Folder path or invalid saved Working Folder path). A missing or forbidden visit path fails closed: the error from OpenFolder is wrapped with the path, and the Saved Default is not opened instead.
SetDefault returns a plain error for an invalid path, or the error from EnsureFolder or SetDefault on the store.
Example
visit, err := goncfiles.Open(root, store, "myapp", visitRelative)
if err != nil {
if goncfiles.IsForbidden(err) {
http.Error(w, "forbidden", http.StatusForbidden)
return err
}
return err
}
data, err := visit.FS.Read("note.txt")
_ = visit.WorkingRel
_ = data