---
title: "Managing Connections"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Managing Connections}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r, include = FALSE}
knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>",
  eval = FALSE
)
```

Efficiently managing connections is the foundation of a robust data pipeline.
`ctoclient` is designed to handle both simple single-server scripts and the
multi-server setups common in large-scale research projects.

## 1. Authentication strategies

**Security first.** Never store passwords directly in your R scripts. If you
share your code or push it to GitHub, your credentials go with it.

### The .Renviron approach

The most common way to manage credentials is the `.Renviron` file.

1. Call `usethis::edit_r_environ()`.
2. Add your credentials:

```
CTO_SERVER=myorg
CTO_USER=admin@example.com
CTO_PASS=mypassword123
```

3. Restart R for the change to take effect.
4. Connect:

```{r}
library(ctoclient)

cto_connect(
  server   = Sys.getenv("CTO_SERVER"),
  username = Sys.getenv("CTO_USER"),
  password = Sys.getenv("CTO_PASS")
)
```

Note that `.Renviron` entries are plain `NAME=value` lines, not R code, and
that values containing spaces or `#` should be quoted.

### Interactive mode

If you are working locally and have not set up environment variables, omit the
password and you will be prompted for it securely:

```{r}
cto_connect(server = "myorg", username = "admin@example.com")
```

### The system keyring

For the strongest option on a personal machine, keep the password in your
operating system's credential store with the
[keyring](https://keyring.r-lib.org/) package. The secret never touches a file
in your project:

```{r}
# Once, interactively:
keyring::key_set("ctoclient", username = "admin@example.com")

# In every script from then on:
cto_connect(
  server   = "myorg",
  username = "admin@example.com",
  password = keyring::key_get("ctoclient", username = "admin@example.com")
)
```

## 2. What a session is

`cto_connect()` authenticates, verifies the credentials, and stores the
resulting request object inside the package. Every other function picks it up
on its own, which is why you never pass a connection around:

```{r}
cto_connect("myorg", "admin@example.com")

# No connection argument anywhere
cto_form_ids()
cto_form_data("baseline_survey")
cto_dataset_list()
```

`cto_is_connected()` reports whether a session currently exists. It tells you
that `cto_connect()` has been called, not that the server is reachable right
now:

```{r}
cto_is_connected()
#> [1] TRUE
```

### Cookies

By default `cto_connect()` preserves cookies and handles the CSRF token for
you. Keep it that way. A number of functions reach console endpoints rather
than REST endpoints, and they abort on a session that carries no cookies:

- `cto_form_data()`
- `cto_form_metadata()` and `cto_form_definition()`
- `cto_form_languages()`, `cto_form_printable()`,
  `cto_form_stata_template()` and `cto_form_mail_template()`
- `cto_metadata()`

`cto_form_dofile()` and `cto_form_docx()` are in the same position
indirectly, since both download the form definition first. In other words,
`cookies = FALSE` leaves you with the dataset functions, the attachment
downloads and the listing functions, and little else.

## 3. Working with more than one server

Some projects move data between servers, or aggregate across organizations.
`cto_connect()` returns the session invisibly, so you can capture each one and
switch between them with `cto_set_connection()`.

```{r}
conn_staging <- cto_connect("org-staging", "admin@example.com")
conn_prod    <- cto_connect("org-prod", "admin@example.com")

# The most recent connect() is the active one, so this reads from prod
data_prod <- cto_form_data("baseline_survey")

# Switch to staging and write the same data to a dataset there
cto_set_connection(conn_staging)
cto_dataset_upload("aggregated_data", file = "data/baseline.csv")
```

Because the active session is global, a script that switches servers should
switch deliberately and close to the call that depends on it. Interleaving
reads from two servers without an intervening `cto_set_connection()` is the
easiest way to fetch from the wrong one.

`cto_dataset_upload()` takes a path to a CSV in its `file` argument. To upload
a data frame you have in memory, write it out first:

```{r}
tmp <- tempfile(fileext = ".csv")
readr::write_csv(data_prod, tmp)
cto_dataset_upload("aggregated_data", file = tmp)
```

## 4. Customizing the underlying request

Every session is an `httr2` request object, so anything `httr2` can do to a
request you can do to a connection before handing it back to the package.

```{r}
library(httr2)

custom <- conn_prod |>
  req_user_agent("MyResearchBot/1.0") |>
  req_retry(max_tries = 5) |>
  req_timeout(120)

cto_set_connection(custom)
cto_form_data("my_form")
```

This is the supported way to change retry behaviour, timeouts, proxies or
headers. Two things to keep in mind: the package sets its own throttle
(30 requests per minute) which you should only raise if you know your server
tolerates it, and SurveyCTO rejects parallel requests from the same account
with a 409, so a retry policy is more useful to you than concurrency.

## 5. Verbosity

Most functions report progress through the console. To silence them — in a
scheduled job, say — set the option:

```{r}
options(ctoclient.verbose = FALSE)
```

Set it back to `TRUE`, or unset it, to get the messages again.

## See also

- [Working with form data](form-data.html) for what happens to submissions
  after they are downloaded.
- [Automating a pipeline](https://guturago.github.io/ctoclient/articles/automation.html) for credentials in CI and
  scheduled jobs.
