@dooer/dooer-test-env 1.14.0 → 1.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/docs/user-management.md +246 -0
- package/lib/cli.js +36 -21
- package/lib/command/partner.js +34 -57
- package/lib/command/remote-environment.js +292 -0
- package/lib/command/target.js +18 -15
- package/lib/command/user.js +53 -77
- package/lib/remote.js +357 -0
- package/lib/users.js +200 -206
- package/package.json +2 -2
- package/readme.md +53 -12
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
# Logins and user management
|
|
2
|
+
|
|
3
|
+
These commands talk to an environment through its **public GraphQL endpoint** — the same one the frontends
|
|
4
|
+
use. No VPN, no kubectl, no cluster credentials: just an ordinary HTTPS call and a session.
|
|
5
|
+
|
|
6
|
+
| environment | endpoint | `--env` |
|
|
7
|
+
| --- | --- | --- |
|
|
8
|
+
| your local stack | `http://localhost:4000/graphql` | `local` (the default) |
|
|
9
|
+
| staging | `https://api.s-e032.com/graphql` | `staging`, or `dooer-staging` / `s-e032` |
|
|
10
|
+
| live | `https://api.dooer.com/graphql` | `production`, or `live` / `dooer-production` |
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## 1. The three kinds of user
|
|
15
|
+
|
|
16
|
+
One person normally has **several user rows** — commonly `you@dooer.com` as a customer and
|
|
17
|
+
`you+hi@dooer.com` as a professional. They are separate accounts with separate passwords, and *which one
|
|
18
|
+
you sign in as* decides what you can see and do. This is the single most important thing on this page: most
|
|
19
|
+
confusing "why can't I…" moments are a session that belongs to the wrong one of your accounts.
|
|
20
|
+
|
|
21
|
+
| | who uses it | what it can see | how to log in |
|
|
22
|
+
| --- | --- | --- | --- |
|
|
23
|
+
| **customer** | end users | only the organizations attached to that user | email **or** BankID |
|
|
24
|
+
| **partner** (`hi`) | accountants | every organization belonging to their partner | email **or** BankID |
|
|
25
|
+
| **admin** | internal staff | everything, and can create users | email **only** |
|
|
26
|
+
|
|
27
|
+
**BankID cannot reach an admin account.** The general BankID route (`loginWithBankIdV2`) is pinned to
|
|
28
|
+
`user_type: 'customer'` in service-accounts, and the partner route resolves your `hi` account — so an admin
|
|
29
|
+
session always comes from an email + password login.
|
|
30
|
+
|
|
31
|
+
### Partners worth knowing
|
|
32
|
+
|
|
33
|
+
A partner is an accounting firm; its `domain` is how it is addressed on the command line.
|
|
34
|
+
|
|
35
|
+
| partner | domain | what it actually is |
|
|
36
|
+
| --- | --- | --- |
|
|
37
|
+
| Dooer | `dooer` | **Voitto** — the in-house accounting firm |
|
|
38
|
+
| Dooer Sales | `dooersales` | **Dooer free customers** |
|
|
39
|
+
|
|
40
|
+
`dooer-test-env partner list` shows the rest for whichever environment you point it at.
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## 2. Logging in
|
|
45
|
+
|
|
46
|
+
The session is stored in your macOS keychain, one per environment, and every other command picks it up.
|
|
47
|
+
|
|
48
|
+
### As a customer user
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
# email + password (you are prompted for the password; it is not echoed)
|
|
52
|
+
dooer-test-env remote-environment login --env staging --email you@dooer.com
|
|
53
|
+
|
|
54
|
+
# BankID — --customer selects the customer route
|
|
55
|
+
dooer-test-env remote-environment login --env staging --bankid --customer
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### As a partner user (accountant)
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
# BankID — this is the default, and is exactly what HQ does
|
|
62
|
+
dooer-test-env remote-environment login --env staging
|
|
63
|
+
|
|
64
|
+
# email + password, using your professional account's address
|
|
65
|
+
dooer-test-env remote-environment login --env staging --email you+hi@dooer.com
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
The BankID flow never asks which partner you are signing in to — like HQ, it submits an empty domain and
|
|
69
|
+
lets the server resolve it from your BankID identity.
|
|
70
|
+
|
|
71
|
+
### As an admin
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
dooer-test-env remote-environment login --env staging --email you+admin@dooer.com
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Email only, for the reason in §1.
|
|
78
|
+
|
|
79
|
+
### What a BankID login looks like
|
|
80
|
+
|
|
81
|
+
An animated QR is drawn in the terminal and refreshed once a second (the payload is time-based, so a frozen
|
|
82
|
+
QR stops scanning). Open the BankID app, scan, confirm — the command prints who you ended up as:
|
|
83
|
+
|
|
84
|
+
```
|
|
85
|
+
logged in to staging (s-e032) as Jimmy Bergman <jimmy.bergman+hi@dooer.com>
|
|
86
|
+
user id 203b9010-abb9-4f7c-9a5e-8bbf6f75ca7b
|
|
87
|
+
user type hi
|
|
88
|
+
session stored in keychain, valid until 2026-09-11T09:31:17.000Z
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Always read the **user type** line. It is the quickest way to catch "I meant to be my other account".
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## 3. Becoming a partner user from an admin session
|
|
96
|
+
|
|
97
|
+
Signed in as an admin, you can assume a partner user without their BankID — the same thing back-office's
|
|
98
|
+
**“Become user in X”** button does:
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
dooer-test-env remote-environment become someone+hi@dooer.com --env staging
|
|
102
|
+
dooer-test-env remote-environment become someone+hi@dooer.com --env staging --partner "Dooer Sales"
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
`--partner` is only needed when they belong to more than one; otherwise it resolves on its own.
|
|
106
|
+
|
|
107
|
+
Becoming replaces the stored session, so the admin token that authorised it is stashed first:
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
dooer-test-env remote-environment become --revert --env staging
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
## 4. Who am I, and where
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
dooer-test-env remote-environment status --env staging
|
|
119
|
+
dooer-test-env remote-environment status --all # every environment at once
|
|
120
|
+
dooer-test-env remote-environment list # endpoints + whether you are logged in
|
|
121
|
+
dooer-test-env remote-environment logout --env staging
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
## 5. Using the token from scripts
|
|
127
|
+
|
|
128
|
+
`remote-environment token` writes **only** the token to stdout — no colour, no label, no trailing text — so
|
|
129
|
+
it drops straight into a variable or a pipe. Errors go to stderr, and stdout stays empty when they do.
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
dooer-test-env remote-environment token # local
|
|
133
|
+
dooer-test-env remote-environment token --env staging
|
|
134
|
+
dooer-test-env remote-environment token --env staging | pbcopy
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Calling the API by hand with it:
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
TOKEN=$(dooer-test-env remote-environment token --env staging)
|
|
141
|
+
|
|
142
|
+
curl -s https://api.s-e032.com/graphql \
|
|
143
|
+
-H "content-type: application/json" \
|
|
144
|
+
-H "authorization: Bearer $TOKEN" \
|
|
145
|
+
-d '{"query":"{ currentUser { id email userType } }"}'
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
The same works against the local stack on `http://localhost:4000/graphql`. The local environment needs no
|
|
149
|
+
login at all — with no stored session it mints a service token from the shared dev keypair, so
|
|
150
|
+
`token --env local` always returns something usable.
|
|
151
|
+
|
|
152
|
+
> The token is a bearer credential for whichever account you logged in as. Treat it like a password: it is
|
|
153
|
+
> fine in a shell variable, not in a commit, a ticket or a chat message.
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
## 6. Managing users
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
dooer-test-env user search # the most recent users
|
|
161
|
+
dooer-test-env user search anna # email, name, personnummer or id
|
|
162
|
+
dooer-test-env user search --role customer # only customers
|
|
163
|
+
dooer-test-env user get anna@dooer.com # details + partner memberships
|
|
164
|
+
dooer-test-env user partners anna@dooer.com # just the memberships, with levels
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
Users are addressable by **id or email** everywhere.
|
|
168
|
+
|
|
169
|
+
### Creating a user
|
|
170
|
+
|
|
171
|
+
```bash
|
|
172
|
+
# password generated and printed once
|
|
173
|
+
dooer-test-env user create --email anna@dooer.com --execute
|
|
174
|
+
|
|
175
|
+
# a customer, with your own password and a personnummer
|
|
176
|
+
dooer-test-env user create --email kim@example.com \
|
|
177
|
+
--role customer --password 'hunter2hunter2' --personnummer 19900101-1239 --execute
|
|
178
|
+
|
|
179
|
+
# a professional placed straight into a partner
|
|
180
|
+
dooer-test-env user create --email anna@dooer.com --partner "Dooer Sales" --level admin --execute
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
This calls `users.create`, the same mutation back-office's `/users/new` uses, so the service hashes the
|
|
184
|
+
password and applies its own validation. Notes:
|
|
185
|
+
|
|
186
|
+
- **Needs an admin session** in any remote environment.
|
|
187
|
+
- `--role` defaults to `hi`; the others are `customer` and `admin`.
|
|
188
|
+
- The password is shown **once** and cannot be read back. It works on the ordinary email+password login.
|
|
189
|
+
- First and last name default to something derived from the address; override with `--first-name` /
|
|
190
|
+
`--last-name`.
|
|
191
|
+
- The personnummer must be a **real** one — the API checks the Luhn digit, so an invented number is
|
|
192
|
+
rejected. The CLI checks it first and tells you which rule failed.
|
|
193
|
+
- Writes are **dry-run until `--execute`**.
|
|
194
|
+
|
|
195
|
+
---
|
|
196
|
+
|
|
197
|
+
## 7. Partner membership
|
|
198
|
+
|
|
199
|
+
```bash
|
|
200
|
+
dooer-test-env partner list # every partner in the environment
|
|
201
|
+
dooer-test-env partner users "Dooer Sales" # its users and their level
|
|
202
|
+
|
|
203
|
+
dooer-test-env partner assign anna@dooer.com dooersales --level admin --execute
|
|
204
|
+
dooer-test-env partner unassign anna@dooer.com dooersales --execute
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
Partners are addressable by **id, name or domain**.
|
|
208
|
+
|
|
209
|
+
`--level` (`member` or `admin`) is the level *within* the partner — not the same axis as the user's
|
|
210
|
+
`--role` at Dooer. Only **`hi`** users can belong to a partner; the CLI applies the same
|
|
211
|
+
`invalid-user-type` rule the API does, so it will not write a row HQ would trip over.
|
|
212
|
+
|
|
213
|
+
---
|
|
214
|
+
|
|
215
|
+
## 8. What each session may do
|
|
216
|
+
|
|
217
|
+
| operation | required session |
|
|
218
|
+
| --- | --- |
|
|
219
|
+
| `user search`, `user get`, `user partners` | any |
|
|
220
|
+
| `partner assign`, `partner unassign`, `partner users` | partner |
|
|
221
|
+
| `partner list` | admin |
|
|
222
|
+
| `user create` | admin |
|
|
223
|
+
|
|
224
|
+
When a session lacks the level, the CLI names the one that is required and who you are currently signed in
|
|
225
|
+
as, instead of surfacing a raw `Forbidden`:
|
|
226
|
+
|
|
227
|
+
```
|
|
228
|
+
creating a user needs an "admin" session. You are signed in to staging as jimmy.bergman+hi@dooer.com (hi).
|
|
229
|
+
|
|
230
|
+
Log in as an account that has it:
|
|
231
|
+
dooer-test-env remote-environment login --env staging --email <your-admin-email>
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
---
|
|
235
|
+
|
|
236
|
+
## Troubleshooting
|
|
237
|
+
|
|
238
|
+
| Symptom | Cause |
|
|
239
|
+
| --- | --- |
|
|
240
|
+
| `not logged in to <env>` | No session for that environment — log in (§2) |
|
|
241
|
+
| `your <env> session expired …` | Sessions are short-lived; log in again |
|
|
242
|
+
| `needs an "admin" session` | Wrong one of your accounts — see §1 and §8 |
|
|
243
|
+
| `only "hi" users can belong to a partner` | You aimed a partner command at a customer/admin account |
|
|
244
|
+
| `"x@y.com" matches N users` | That address exists once per user type; address the user by id instead |
|
|
245
|
+
| `personnummer … has an invalid check digit` | Not a real personnummer — the API validates the Luhn digit |
|
|
246
|
+
| BankID QR never completes on **local** | The local stack lost its BankID certs; `dooer-test-env up` re-injects them |
|
package/lib/cli.js
CHANGED
|
@@ -6,25 +6,40 @@ const pkg = require('../package.json')
|
|
|
6
6
|
// customers), copy/purge customers between environments, and shred — see ENVIRONMENT-PLAN.md.
|
|
7
7
|
// Command groups are wired as modules under ./command.
|
|
8
8
|
module.exports = async function cli() {
|
|
9
|
-
return
|
|
10
|
-
.
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
9
|
+
return (
|
|
10
|
+
yargs(hideBin(process.argv))
|
|
11
|
+
.scriptName('dooer-test-env')
|
|
12
|
+
.usage('$0 <group> <command> [options]')
|
|
13
|
+
.version(pkg.version)
|
|
14
|
+
.command(require('./command/setup')) // prerequisites + registry login + compose generation
|
|
15
|
+
.command(require('./command/env')) // up / down / start / stop / status
|
|
16
|
+
.command(require('./command/service')) // service start|stop|restart|deploy|local|unlocal|version
|
|
17
|
+
.command(require('./command/db')) // db build|pull|snapshot|rollback
|
|
18
|
+
.command(require('./command/customer')) // customer copy|purge
|
|
19
|
+
.command(require('./command/remote-environment')) // login/status/logout/list against a public GraphQL API
|
|
20
|
+
.command(require('./command/user')) // user search|get|create|partners
|
|
21
|
+
.command(require('./command/partner')) // partner list|users|assign|unassign
|
|
22
|
+
.command(require('./command/shred')) // shred (localhost only)
|
|
23
|
+
.command(require('./command/bankid')) // bankid pull|status|clear (staging certs → keychain)
|
|
24
|
+
.command(require('./command/validation')) // validation on|off|status (output-schema checks)
|
|
25
|
+
.command(require('./command/logs')) // search logs across all local services (transactionId, errors, …)
|
|
26
|
+
.command(require('./command/measure')) // resource-usage report for the running env
|
|
27
|
+
.demandCommand(1, 'Pick a command group. Try --help.')
|
|
28
|
+
.strict()
|
|
29
|
+
// yargs prints the full usage block for a HANDLER failure too, which buries the actual message under
|
|
30
|
+
// an option list that has nothing to do with it (a permission error is not a usage error). Print the
|
|
31
|
+
// usage only when yargs itself rejected the arguments; otherwise just the message.
|
|
32
|
+
.fail((msg, err, yargsInstance) => {
|
|
33
|
+
if (!err) {
|
|
34
|
+
console.error(yargsInstance.help())
|
|
35
|
+
console.error(`\n${msg}\n`)
|
|
36
|
+
} else {
|
|
37
|
+
console.error(`\n${err.message}\n`)
|
|
38
|
+
}
|
|
39
|
+
process.exit(1)
|
|
40
|
+
})
|
|
41
|
+
.help()
|
|
42
|
+
.wrap(Math.min(120, yargs().terminalWidth()))
|
|
43
|
+
.parseAsync()
|
|
44
|
+
)
|
|
30
45
|
}
|
package/lib/command/partner.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const chalk = require('chalk')
|
|
2
2
|
const u = require('../users')
|
|
3
|
-
const
|
|
3
|
+
const remote = require('../remote')
|
|
4
|
+
const { envOption, writeOption, printTable } = require('./target')
|
|
4
5
|
|
|
5
6
|
module.exports = {
|
|
6
7
|
command: 'partner <command>',
|
|
@@ -10,17 +11,15 @@ module.exports = {
|
|
|
10
11
|
.command({
|
|
11
12
|
command: 'list',
|
|
12
13
|
describe: 'list every partner in the environment',
|
|
13
|
-
builder:
|
|
14
|
+
builder: envOption,
|
|
14
15
|
handler: async (argv) => {
|
|
15
|
-
const
|
|
16
|
-
const rows = await u.
|
|
17
|
-
console.log(chalk.bold(`\n${rows.length} partner(s) in ${label
|
|
16
|
+
const env = remote.resolveEnv(argv.env)
|
|
17
|
+
const rows = await u.listPartners(env)
|
|
18
|
+
console.log(chalk.bold(`\n${rows.length} partner(s) in ${env.label}\n`))
|
|
18
19
|
printTable(rows, [
|
|
19
20
|
['NAME', (r) => r.name],
|
|
20
21
|
['DOMAIN', (r) => r.domain],
|
|
21
|
-
['USERS', (r) => r.users],
|
|
22
22
|
['STATUS', (r) => r.status],
|
|
23
|
-
['DEFAULT', (r) => (r.isDefaultPartner ? 'yes' : '')],
|
|
24
23
|
['ID', (r) => r.id],
|
|
25
24
|
])
|
|
26
25
|
console.log()
|
|
@@ -30,29 +29,19 @@ module.exports = {
|
|
|
30
29
|
command: 'users <partner>',
|
|
31
30
|
describe: 'list the users of one partner and their level',
|
|
32
31
|
builder: (y2) =>
|
|
33
|
-
|
|
32
|
+
envOption(y2).positional('partner', { type: 'string', describe: 'partner id, name or domain' }),
|
|
34
33
|
handler: async (argv) => {
|
|
35
|
-
const
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
`SELECT us.users_pk AS id, us.email, us.first_name, us.last_name,
|
|
40
|
-
pu.role AS level, pu.status, pu."defaultRepresentative"
|
|
41
|
-
FROM "service_accounts"."partnerUser" pu
|
|
42
|
-
JOIN "service_accounts".users us ON us.users_pk = pu."userId"
|
|
43
|
-
WHERE pu."partnerId" = $1 ORDER BY us.email`,
|
|
44
|
-
[partner.id]
|
|
45
|
-
)
|
|
46
|
-
return { partner, rows }
|
|
47
|
-
})
|
|
48
|
-
console.log(chalk.bold(`\n${partner.name} — ${rows.length} user(s)`) + chalk.gray(` (${label(ns)})\n`))
|
|
34
|
+
const env = remote.resolveEnv(argv.env)
|
|
35
|
+
const partner = await u.findPartner(env, argv.partner)
|
|
36
|
+
const rows = await u.partnerUsers(env, partner.id)
|
|
37
|
+
console.log(chalk.bold(`\n${partner.name} — ${rows.length} user(s)`) + ` (${env.label})\n`)
|
|
49
38
|
printTable(rows, [
|
|
50
|
-
['EMAIL', (r) => r.email],
|
|
51
|
-
['NAME', (r) => `${r.
|
|
52
|
-
['LEVEL', (r) => r.
|
|
39
|
+
['EMAIL', (r) => r.user && r.user.email],
|
|
40
|
+
['NAME', (r) => (r.user ? `${r.user.firstName} ${r.user.lastName}` : null)],
|
|
41
|
+
['LEVEL', (r) => r.role],
|
|
53
42
|
['STATUS', (r) => r.status],
|
|
54
43
|
['DEFAULT REP', (r) => (r.defaultRepresentative ? 'yes' : 'no')],
|
|
55
|
-
['ID', (r) => r.
|
|
44
|
+
['ID', (r) => r.userId],
|
|
56
45
|
])
|
|
57
46
|
console.log()
|
|
58
47
|
},
|
|
@@ -61,31 +50,24 @@ module.exports = {
|
|
|
61
50
|
command: 'assign <user> <partner>',
|
|
62
51
|
describe: 'add a user to a partner (or change their level). Dry-run by default.',
|
|
63
52
|
builder: (y2) =>
|
|
64
|
-
|
|
53
|
+
writeOption(envOption(y2))
|
|
65
54
|
.positional('user', { type: 'string', describe: 'user id or email' })
|
|
66
55
|
.positional('partner', { type: 'string', describe: 'partner id, name or domain' })
|
|
67
56
|
.option('level', { choices: u.PARTNER_LEVELS, default: 'member', describe: 'level within the partner' }),
|
|
68
57
|
handler: async (argv) => {
|
|
69
|
-
const
|
|
70
|
-
u.
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
})
|
|
77
|
-
const what =
|
|
78
|
-
res.previous && res.previous !== argv.level
|
|
79
|
-
? `level ${res.previous} → ${argv.level}`
|
|
80
|
-
: res.previous
|
|
81
|
-
? `already a ${argv.level} (no change)`
|
|
82
|
-
: `added as ${argv.level}`
|
|
58
|
+
const env = remote.resolveEnv(argv.env)
|
|
59
|
+
const user = await u.findUser(env, argv.user)
|
|
60
|
+
const partner = await u.findPartner(env, argv.partner)
|
|
61
|
+
// The API refuses a non-`hi` user (invalid-user-type); say so before spending a round trip.
|
|
62
|
+
if (user.userType !== 'hi') {
|
|
63
|
+
throw new Error(`only "hi" users can belong to a partner — ${user.email} is "${user.userType}"`)
|
|
64
|
+
}
|
|
65
|
+
if (argv.execute) await u.assignPartner(env, { userId: user.id, partnerId: partner.id, level: argv.level })
|
|
83
66
|
console.log(
|
|
84
|
-
(argv.execute ? chalk.green(
|
|
85
|
-
`: ${user.email} @ ${partner.name}`
|
|
86
|
-
chalk.gray(` (${label(ns)})`)
|
|
67
|
+
(argv.execute ? chalk.green(`\nset ${argv.level}`) : chalk.yellow(`\nDRY RUN — would set ${argv.level}`)) +
|
|
68
|
+
`: ${user.email} @ ${partner.name} (${env.label})`
|
|
87
69
|
)
|
|
88
|
-
if (!argv.execute) console.log(
|
|
70
|
+
if (!argv.execute) console.log(' Re-run with --execute to apply.')
|
|
89
71
|
console.log()
|
|
90
72
|
},
|
|
91
73
|
})
|
|
@@ -93,24 +75,19 @@ module.exports = {
|
|
|
93
75
|
command: 'unassign <user> <partner>',
|
|
94
76
|
describe: 'remove a user from a partner. Dry-run by default.',
|
|
95
77
|
builder: (y2) =>
|
|
96
|
-
|
|
78
|
+
writeOption(envOption(y2))
|
|
97
79
|
.positional('user', { type: 'string', describe: 'user id or email' })
|
|
98
80
|
.positional('partner', { type: 'string', describe: 'partner id, name or domain' }),
|
|
99
81
|
handler: async (argv) => {
|
|
100
|
-
const
|
|
101
|
-
u.
|
|
102
|
-
const
|
|
103
|
-
|
|
104
|
-
const partner = await u.findPartner(db, argv.partner)
|
|
105
|
-
const res = await u.unassignPartner(db, { user, partner, execute: argv.execute })
|
|
106
|
-
return { user, partner, res }
|
|
107
|
-
})
|
|
82
|
+
const env = remote.resolveEnv(argv.env)
|
|
83
|
+
const user = await u.findUser(env, argv.user)
|
|
84
|
+
const partner = await u.findPartner(env, argv.partner)
|
|
85
|
+
if (argv.execute) await u.unassignPartner(env, { userId: user.id, partnerId: partner.id })
|
|
108
86
|
console.log(
|
|
109
87
|
(argv.execute ? chalk.green('\nremoved') : chalk.yellow('\nDRY RUN — would remove')) +
|
|
110
|
-
` ${user.email}
|
|
111
|
-
chalk.gray(` (${label(ns)})`)
|
|
88
|
+
` ${user.email} from ${partner.name} (${env.label})`
|
|
112
89
|
)
|
|
113
|
-
if (!argv.execute) console.log(
|
|
90
|
+
if (!argv.execute) console.log(' Re-run with --execute to apply.')
|
|
114
91
|
console.log()
|
|
115
92
|
},
|
|
116
93
|
})
|