Skip to main content

ytcli/cli/
guidance.rs

1//! Where to get credentials.
2//!
3//! Printed when someone is stuck rather than on every run: a first login, a
4//! rejected token, an organisation that answers 403. Getting a Tracker token
5//! means creating an OAuth application and hand-assembling an authorize URL,
6//! which is not something anyone guesses, and the organisation id lives behind a
7//! page most people have never opened.
8//!
9//! The blocks are written as markdown and rendered for a terminal, for the same
10//! reason issue descriptions are: a numbered procedure with a table in it reads
11//! as a procedure, not as punctuation. Where nobody is watching — a pipe, a log,
12//! a CI run — the markdown source goes out unrendered, because reflowed text
13//! with escape codes in it is worse to read in a log than the source was.
14
15use std::io::IsTerminal;
16
17/// How to obtain an OAuth token.
18pub const TOKEN: &str = "\
19## Get an OAuth token
20
211. Create an application at https://oauth.yandex.ru/client/new — pick
22   **For API access or debugging**, and grant `tracker:write` for full access
23   or `tracker:read` to stay read-only.
242. Copy the application's **ClientID** from its page.
253. Open `https://oauth.yandex.ru/authorize?response_type=token&client_id=<ClientID>`
26   and sign in. The token comes back in the address bar you land on.
27
28**Stay on one domain for both steps.** `oauth.yandex.com` works too, but it is a
29separate origin with its own cookies, so your browser may be signed in there as
30somebody else — or as nobody. An application created under one account and
31authorised under another produces a token for the wrong person, and the first
32sign of it is `auth status` naming a stranger.
33
34It looks like `y0__xAbc...`, roughly 60-90 characters.
35
36Docs: https://yandex.ru/support/tracker/en/api-ref/access
37";
38
39/// How to find the organisation id, and which flavour it is.
40pub const ORG: &str = "\
41## Find your organisation id
42
43Open https://tracker.yandex.ru/admin/orgs — it lists every organisation you are
44in, with its id, and says which kind each one is.
45
46| Kind | Id looks like | Flag |
47|:-|:-|:-|
48| Yandex 360 for Business | `1234567` | `--org-kind yandex360` |
49| Yandex Cloud Organization | `bpfaidqca8vd0m5jl3fp` | `--org-kind cloud` |
50
51Not sure which you have? Omit `--org-kind`: login tries both and reports which
52one answered. The two use different headers, and the wrong one returns **403** —
53which reads like a rights problem rather than a configuration mistake.
54
55Docs: https://yandex.ru/support/tracker/en/api-ref/access
56";
57
58/// What `auth login` is, above the two blocks.
59const INTRO: &str = "\
60Store a token for an account, and set up a profile to use it with.
61
62```
63ytcli auth login
64ytcli auth login --account work --org-id 1234567 --profile work
65ytcli auth login --account work --org-id 1234567 --dry-run
66```
67
68Run it with no arguments in a terminal and it walks you through each step,
69taking the token as a hidden password. Pass whatever you already know as flags
70and only the rest is asked for. Outside a terminal the flags are all there is,
71and the token is read from stdin.
72";
73
74/// Render one block for whoever is reading it.
75///
76/// Rendered only for a terminal, and to the terminal's own width: markdown
77/// wrapped to 80 columns in a 200-column window looks like a mistake, and
78/// escape codes in a captured log are one.
79#[must_use]
80pub fn block(markdown: &str) -> String {
81    if !std::io::stderr().is_terminal() {
82        return markdown.to_owned();
83    }
84    crate::render::markdown::render(markdown, width())
85}
86
87/// Both blocks, for a first run.
88#[must_use]
89pub fn full() -> String {
90    block(&format!("{TOKEN}\n{ORG}"))
91}
92
93/// The long help of `auth login`: what the command is, then the same two
94/// blocks, so `--help` answers the question without anyone having to fail first.
95#[must_use]
96pub fn login_help() -> String {
97    crate::cli::help::md(&format!("{INTRO}\n{TOKEN}\n{ORG}"))
98}
99
100/// The window, narrowed to a width prose is readable at.
101///
102/// A procedure wrapped to 200 columns is one long line with a number in front
103/// of it; nobody reads that as steps.
104fn width() -> usize {
105    crate::cli::terminal_width().clamp(40, 92)
106}