Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 11 additions & 9 deletions AGENTS.md

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
[![Tests](https://github.com/i2h3/rainmaker/actions/workflows/test.yml/badge.svg)](https://github.com/i2h3/rainmaker/actions/workflows/test.yml)
[![REUSE](https://api.reuse.software/badge/github.com/i2h3/rainmaker)](https://api.reuse.software/info/github.com/i2h3/rainmaker)

A simple Swift library and CLI to access [Nextcloud](https://www.nextcloud.com) programmatically: files, notifications, activities, notes (including incremental and chunked synchronization, summaries without their text, conflict detection, attachments, and helpers which predict a note's title and category and reference attachments from its content the way the notes and Text apps do), collectives, Talk conversations and the current user.
A simple Swift library and CLI to access [Nextcloud](https://www.nextcloud.com) programmatically: files, notifications, activities, notes (including incremental and chunked synchronization, summaries without their text, conflict detection, attachments, and helpers which predict a note's title and category and reference attachments from its content the way the notes and Text apps do), collectives, Talk conversations (listing them, starting one-to-one conversations with users found by a search, and sending messages and files into them) and the current user.
The library keeps no state between calls and needs no synchronization engine, so it suits a single action such as one of Shortcuts as well as a client keeping its own copy of an account's data.
For further information, see [the documentation which is built from the source code and deployed to GitHub pages](https://i2h3.github.io/rainmaker/).

Expand All @@ -19,6 +19,7 @@ For further information, see [the documentation which is built from the source c
- iOS 15, macOS 12, tvOS 15, visionOS 1 or watchOS 8 or newer.
- A Nextcloud server. Every release is verified against the versions listed in `ServerVersion` in `Sources/RainmakerTestServerTags/`, currently Nextcloud 31, 32, 33 and 34.
- For the notes features, the notes app serving its API in version 1.4 or newer, which it does since release 4.12.3. Deleting attachments and keeping them in a folder per note require release 6.1.0 or newer.
- For sending files into a Talk conversation, Talk 24 or newer with its folders per conversation enabled, which `Talk.storesAttachmentsPerConversation` tells.

## Testing

Expand Down
24 changes: 24 additions & 0 deletions Sources/Rainmaker/Documentation.docc/ActivityStream.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Activity Stream

Retrieve what the server recorded about an account: files being created, changed and shared, calendar events being scheduled, and whatever else an installed app contributes.

## Overview

Whether the activity app is installed is advertised through the ``Activity`` capability.

## Topics

### Retrieving Activities

- ``Server/activities(filter:since:limit:sort:previews:objectType:objectId:)``
- ``Server/activityFilters()``
- ``ActivityFilter``
- ``ActivitySort``

### Activities

- ``ActivityPage``
- ``ActivityItem``
- ``ActivityRichText``
- ``ActivityRichObject``
- ``ActivityPreview``
22 changes: 22 additions & 0 deletions Sources/Rainmaker/Documentation.docc/Authenticating.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Authentication

Obtain an app password through the server's login flow and revoke it again once it is no longer needed.

## Overview

``Server/login()`` begins a login flow, which the user completes in a browser, while the client polls for its result.
``Server/poll(_:)`` returns `nil` while the user has not completed the flow and throws only for real failures, so a client polling it can tell an unreachable server from a pending flow, while the deprecated ``Server/poll(_:token:)`` throws in both cases.

## Topics

### Logging In

- ``Server/login()``
- ``Server/poll(_:)``
- ``Server/poll(_:token:)``
- ``LoginFlow``
- ``LoginResult``

### Logging Out

- ``Server/deleteAppPassword()``
18 changes: 18 additions & 0 deletions Sources/Rainmaker/Documentation.docc/Collectives.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Collectives

Retrieve the collectives of an account and the pages within one of them.

## Overview

The Collectives app is not part of a Nextcloud installation, and unlike every other app covered here it advertises no capability at all, so whether it is available is answered by looking for the entry with the identifier `collectives` in ``Server/navigation()`` rather than through ``Server/capabilities()``.
Pages are returned flat and in the server's order; their hierarchy is reconstructed from ``CollectivePage/parentId``.

## Topics

### Retrieving Collectives

- ``Server/collectives()``
- ``Server/pages(inCollective:)``
- ``Collective``
- ``CollectivePage``
- ``MembershipLevel``
186 changes: 8 additions & 178 deletions Sources/Rainmaker/Documentation.docc/CommandLineInterface.md
Original file line number Diff line number Diff line change
@@ -1,190 +1,20 @@
# Command Line Interface

This package also provides a convenient executable to leverage the features of the library in a terminal environment.
Use the features of the library in a terminal through the executable this package also provides.

## Overview

You can get an overview by running the command line interface itself or any subcommand with the `--help` option in the package root directory like this, shown here and below without the line wrapping a terminal applies:

```plaintext
$ swift run rainmaker-cli --help
USAGE: rainmaker <subcommand>

OPTIONS:
-h, --help Show help information.

SUBCOMMANDS:
activities List one page of the activity stream of the authenticated user. Requires authentication and the server's activity app.
activity-filters List the filters the server offers to narrow the activity stream down with. Requires authentication and the server's activity app.
capabilities Fetch the capabilities advertised by a server. Authentication is optional.
collectives List the collectives of the authenticated user and their pages. Requires authentication and the server's collectives app.
conversations List the Talk conversations of the authenticated user and retrieve their images. Requires authentication and the server's Talk app.
create-directory Create a directory on the server.
current-user Show the identifier and the display name of the authenticated account. Requires authentication.
delete Delete a file or directory from the server.
delete-app-password Delete the app password currently used to authenticate, ending the session on the server side.
download Download a file or directory from the server.
info Show information about a remote file or directory.
list List the content of a directory on the server by the given path.
login Fetch the login flow information from a server.
move Move or rename a remote file or directory on the server.
navigation List the apps navigation entries advertised by a server. Requires authentication.
notes List, retrieve, create, change and delete the notes of the authenticated user and their attachments. Requires authentication and the server's notes app.
notes-settings Show or change where and how the notes app stores the notes of the authenticated user. Requires authentication and the server's notes app.
notifications List the notifications queued for the authenticated user. Requires authentication and the server's notifications app.
poll Poll the status of a previously initiated login flow.
trash Manage the server trash bin.
upload Upload a file or directory to a folder on the server.
watch Observe server-side changes over notify_push (or polling when unavailable or asked to) and print each event. Runs until interrupted.
record-fixtures Record test fixtures by deploying Nextcloud containers and running the test suite against them.

See 'rainmaker help <subcommand>' for detailed help.
```

## Notes

The `notes` command is a group of subcommands, of which `list` is the default, so `notes --changed-since` keeps working as before.
Attachments are managed by the nested `notes attachment` group, and the settings of the notes app by the separate `notes-settings` command.
In the plain output, a listing of changes ends with a `#cursor` line while more chunks follow, an `#etag` line and a `#last-modified` line in whole seconds since the Unix epoch, which are what `--cursor`, `--if-none-match` and `--changed-since` take for the next listing, while `notes get` writes the note's `#etag` line to standard error so that standard output stays the text of the note.

```plaintext
$ swift run rainmaker-cli notes --help
OVERVIEW: List, retrieve, create, change and delete the notes of the authenticated user and their attachments. Requires authentication and the server's notes app.

USAGE: rainmaker notes <subcommand>

OPTIONS:
-h, --help Show help information.

SUBCOMMANDS:
list (default) List the notes of the authenticated user, all of them, those changed since a moment, or one chunk of those.
get Retrieve a single note of the authenticated user by its identifier.
create Create a note for the authenticated user and print its identifier, entity tag and title.
update Change a note of the authenticated user and print its identifier, entity tag and title.
delete Delete a note of the authenticated user.
attachment Retrieve, add and delete the files attached to a note of the authenticated user.

See 'rainmaker help notes <subcommand>' for detailed help.
```

```plaintext
$ swift run rainmaker-cli notes attachment --help
OVERVIEW: Retrieve, add and delete the files attached to a note of the authenticated user.

USAGE: rainmaker notes attachment <subcommand>

OPTIONS:
-h, --help Show help information.

SUBCOMMANDS:
get Retrieve a file attached to a note of the authenticated user.
add Attach a local file to a note of the authenticated user and print the path the server stored it at.
delete Delete a file attached to a note of the authenticated user. Requires the notes app 6.1.0 or newer.

See 'rainmaker help notes attachment <subcommand>' for detailed help.
```

```plaintext
$ swift run rainmaker-cli notes-settings --help
OVERVIEW: Show or change where and how the notes app stores the notes of the authenticated user. Requires authentication and the server's notes app.

USAGE: rainmaker notes-settings [--user <user>] [--password <password>] [--output-format <output-format>] [--host <host>] [--notes-path <notes-path>] [--file-suffix <file-suffix>] [--note-mode <note-mode>] [--show-hidden-files <show-hidden-files>] [--load-recent-note-on-start-up <load-recent-note-on-start-up>]

OPTIONS:
-u, --user <user> The user account name to authenticate with. Can also be set via RAINMAKER_USER environment variable.
-p, --password <password>
The password to authenticate with. Can also be set via RAINMAKER_PASSWORD environment variable.
--output-format <output-format>
In what form to render the output. (default: plain)
-h, --host <host> The Nextcloud instance to connect to. Can also be set via RAINMAKER_HOST environment variable.
--notes-path <notes-path>
Store the notes in this folder, relative to the account's files, with an empty string for the root folder. Existing notes are not moved.
--file-suffix <file-suffix>
Give the notes created from now on this file extension, such as '.md' or '.txt'.
--note-mode <note-mode> Open notes in the web interface in this mode. (values: rich, edit, preview)
--show-hidden-files <show-hidden-files>
Whether files and folders whose names start with a dot are listed as notes and categories, 'true' or 'false'. Requires notes app 6.1.0 or newer.
--load-recent-note-on-start-up <load-recent-note-on-start-up>
Whether the web interface opens the most recently edited note when it starts, 'true' or 'false'. Requires notes app 6.1.0 or newer.
-h, --help Show help information.
```

## Current User

```plaintext
$ swift run rainmaker-cli current-user --help
OVERVIEW: Show the identifier and the display name of the authenticated account. Requires authentication.

USAGE: rainmaker current-user [--user <user>] [--password <password>] [--output-format <output-format>] [--host <host>]

OPTIONS:
-u, --user <user> The user account name to authenticate with. Can also be set via RAINMAKER_USER environment variable.
-p, --password <password>
The password to authenticate with. Can also be set via RAINMAKER_PASSWORD environment variable.
--output-format <output-format>
In what form to render the output. (default: plain)
-h, --host <host> The Nextcloud instance to connect to. Can also be set via RAINMAKER_HOST environment variable.
-h, --help Show help information.
```

## Login

`login` starts a login flow and prints the login page to open in a browser together with the poll address and the poll token, which `poll` takes to wait for the app password.
While the flow is pending, `poll` tries again every second, and any real failure, such as an unreachable server or an address which is not a login flow endpoint, ends it with that error.

```plaintext
$ swift run rainmaker-cli poll --help
OVERVIEW: Poll the status of a previously initiated login flow.

USAGE: rainmaker poll [--output-format <output-format>] <endpoint> <token> [--tries <tries>]

ARGUMENTS:
<endpoint> The address which to poll for login flow status.
<token> The token to identify the login flow.

OPTIONS:
--output-format <output-format>
In what form to render the output. (default: plain)
--tries <tries> How many times the endpoint should be polled before failing. (default: 300)
-h, --help Show help information.
```

## Observing Changes

```plaintext
$ swift run rainmaker-cli watch --help
OVERVIEW: Observe server-side changes over notify_push (or polling when unavailable or asked to) and print each event. Runs until interrupted.

USAGE: rainmaker watch [--user <user>] [--password <password>] [--host <host>] [--poll-interval <poll-interval>] [--transport <transport>] [--listen-file-ids]

OPTIONS:
-u, --user <user> The user account name to authenticate with. Can also be set via RAINMAKER_USER environment variable.
-p, --password <password>
The password to authenticate with. Can also be set via RAINMAKER_PASSWORD environment variable.
-h, --host <host> The Nextcloud instance to connect to. Can also be set via RAINMAKER_HOST environment variable.
--poll-interval <poll-interval>
The polling interval in seconds used when notify_push is unavailable or not used. (default: 30.0)
--transport <transport> How to learn about changes: 'automatic' prefers notify_push and polls when it is unavailable, 'polling' only polls. (values: automatic, polling; default: automatic)
--listen-file-ids Request per-file identifiers via notify_file_id.
-h, --help Show help information.
```

## Authentication

You can provide credentials directly as command-line options:
The package provides the executable product `rainmaker-cli`, which offers the features of the library as subcommands.
Run it from the package root directory through the Swift Package Manager:

```bash
$ swift run rainmaker-cli list --host "http://localhost:8080" --user "myuser" --password "mypassword"
swift run rainmaker-cli <subcommand>
```

To avoid password leakage and enable a default account pattern, you can also set credentials via environment variables:
The command line interface describes itself, so ask it for help rather than relying on a copy of its help in this documentation.
The `--help` option lists the subcommands, and the `help` subcommand describes a single one, including the options it takes and how they are set:

```bash
export RAINMAKER_HOST="http://localhost:8080"
export RAINMAKER_USER="myuser"
export RAINMAKER_PASSWORD="mypassword"

$ swift run rainmaker-cli list
swift run rainmaker-cli --help
swift run rainmaker-cli help <subcommand>
```

Environment variables can be mixed with command-line options. Command-line options take precedence over environment variables.
28 changes: 28 additions & 0 deletions Sources/Rainmaker/Documentation.docc/CustomRequests.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Custom Requests and Transport

Build requests for endpoints Rainmaker does not cover, and supply your own transport to intercept, record or replay requests.

## Overview

If the built in features of Rainmaker do not suffice for your use case, the request factories of ``Server`` build your own requests on top, for example for the API of a third-party Nextcloud server app.
The names and values of query items are passed as they are and percent-encoded by the factories, including `+`, `&`, `=` and `#`, so a value such as `a+b` reaches the server as `a+b` rather than as `a b`.

The factories return a plain `URLRequest`, and a ``Server`` performs it through the abstractions below rather than through `URLSession` directly.
Supplying your own implementations is how requests are intercepted, recorded or replayed, which is exactly what this package's own test suite does.
The HTTP and the WebSocket sides are separate because a `URLSession` typed as ``Requesting`` does not expose its WebSocket features.

## Topics

### Building Requests

- ``Server/makeAppRequest(for:method:queryItems:)``
- ``Server/makeOCSRequest(for:method:queryItems:)``
- ``Server/makeWebDAVRequest(for:method:)``
- ``Method``

### Customizing Transport

- ``Requesting``
- ``WebSocketConnecting``
- ``WebSocketChannel``
- ``WebSocketFrame``
33 changes: 33 additions & 0 deletions Sources/Rainmaker/Documentation.docc/DiscoveringServerFeatures.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Capabilities and Navigation

Find out what a server offers before relying on it, and which apps it advertises to the authenticated user.

## Overview

``Server/capabilities()`` returns what the server and its apps advertise, from which a client looks up the ``Capability`` it is interested in, such as ``Notes`` before using the notes features or ``Talk`` before sending into a conversation.
A downstream project can declare a ``Capability`` of its own for anything this library does not model.

``Server/navigation()`` lists the server apps, such as Files, Photos and Activity, which the server advertises to the authenticated user so that a client can surface them in its own navigation.
It is also how a client finds out whether an app without a capability of its own, such as Collectives, is available.

## Topics

### Capabilities

- ``Server/capabilities()``
- ``CapabilitySet``
- ``Capability``
- ``Activity``
- ``ChunkedUpload``
- ``Notes``
- ``Notifications``
- ``PushNotifications``
- ``Talk``
- ``Theming``
- ``Trashing``
- ``Version``

### Navigation

- ``Server/navigation()``
- ``NavigationItem``
Loading
Loading