Files
go-nc-files/docs/guide.md
T

9.3 KiB
Raw Blame History

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

  1. Obtain a Root (Local or WebDAVRoot).
  2. OpenFolder when the folder must already exist. EnsureFolder when missing parents should be created.
  3. On the Folder, call List, Read, Write, Exists, or Remove with 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

  1. Set Local.RootDir.
  2. Call OpenFolder or EnsureFolder with a relative path. EnsureFolder creates the directory with mode 0755.
  3. Or call LocalFolder with 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

  1. Fill gonexapp.Credentials, including BaseURL and UserID.
  2. Store them on WebDAVRoot.Cred.
  3. OpenFolder checks the folder with PROPFIND and does not create it.
  4. EnsureFolder creates missing parents with MKCOL, then opens the folder.
  5. List uses PROPFIND with depth 1. Read uses GET. Write uses PUT. Remove uses DELETE. Exists uses 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

  1. Pass the raw path from a query parameter or a saved preference.
  2. 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

  1. Pass the child name.
  2. 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

  1. Construct a FileStore with a file path, a Memory, or a product store.
  2. Pass it to Open.
  3. GetDefault on a missing file returns empty and a nil error.
  4. SetDefault writes the path. FileStore creates 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

  1. Call Open with a Root, a DefaultPathStore, the product's initial folder name, and the visit-relative path.
  2. Pass an empty visit-relative path for an app-icon launch. Open reads the Saved Default, uses initialFolderName when nothing is stored, and EnsureFolders that path.
  3. Pass the Files-view folder for a visit-only path. Open uses OpenFolder and does not create it and does not fall back to the Saved Default.
  4. Use Visit.FS as the Folder, WorkingRel as the relative path, and SavedDefault as the stored default.
  5. Call SetDefault when the user picks a new default. It EnsureFolders the path, stores it, and updates SavedDefault. It does not switch FS to that folder.
  6. WorkingDir returns a local absolute path when FS is a local folder, and WorkingRel otherwise.

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