Sheetshow.Store.WebDAV (Sheetshow v0.1.0)

Copy Markdown View Source

A spreadsheet file on a WebDAV server: Nextcloud, ownCloud, or anything else that speaks it.

This is the store that can do what a local file cannot: refuse a write that would clobber somebody. A read brings back the file's ETag, a write sends it back as If-Match, and the server compares the two before it writes anything. A file that changed in between gets 412 Precondition Failed, which arrives here as %Sheetshow.Error{reason: :conflict}, and because nothing was written, the work can be re-read, re-planned and tried again.

store = Sheetshow.Store.nextcloud("https://cloud.example.com", "user", "budget/costs.xlsx",
          username: "user", password: System.fetch_env!("NEXTCLOUD_APP_PASSWORD"))

{:ok, workbook} = Sheetshow.Workbook.xlsx(store) |> Sheetshow.connect()

App passwords

Nextcloud takes an ordinary Basic auth header, but an account with two-factor authentication or an external identity provider will not accept the login password over WebDAV. It wants an app password, made under Personal settings → Security → Devices & sessions. That is the usual reason a set of credentials that works in a browser gets a 401 here.

Weak entity tags, and the 412 that never goes away

If-Match uses strong comparison: RFC 7232 says both tags must be non-weak and match exactly, so an entity tag marked weak (W/"…") can never satisfy it. Servers hand one back legitimately whenever the response body was compressed (nginx with gzip on, Cloudflare, Traefik, PHP's zlib.output_compression), and a client that sends it back anyway gets 412 forever, no matter how many times it re-reads.

Two things here about that. Reads ask for Accept-Encoding: identity, so the server has no reason to compress and every reason to give a strong tag. And a write holding a weak one is refused here, with an error that says what to do, rather than sent off to fail in a way that looks like a conflict and is not one.