holivia 0.12.0 → 0.14.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +20 -0
- data/README.md +36 -8
- data/SKILL.md +69 -8
- data/exe/holivia +4 -1
- data/lib/holivia/auth.rb +31 -19
- data/lib/holivia/browser_login.rb +125 -0
- data/lib/holivia/cli/help.rb +19 -1
- data/lib/holivia/cli.rb +4 -0
- data/lib/holivia/client.rb +14 -1
- data/lib/holivia/commands/auth.rb +5 -23
- data/lib/holivia/commands/contract.rb +41 -0
- data/lib/holivia/commands/employee.rb +48 -0
- data/lib/holivia/commands/enterprise.rb +16 -0
- data/lib/holivia/version.rb +1 -1
- data/lib/holivia.rb +1 -0
- metadata +4 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: fab1251135002a03f719e93cb2f2d1fcb52842e2f5c5fecc3fa8c47c279b8747
|
|
4
|
+
data.tar.gz: 8e2dd66d735bf875c123d42d90c25ab7d853024c5378dc8445d2f8c4eeb233aa
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a0c502ac53052598fff122e0b5bafc5028c7cd2dee25ebc6629879f0a77d17516314608012493d41ee8571cb79802d5a276c43e64d03236f2e86d8e7a4c68e77
|
|
7
|
+
data.tar.gz: 5454072e3f4e4d046b1a8de6e60a9a9641db2a5d16eaf85441de7340e065572f4606c83a603bc4f615ef0c86b02f4a03babf28fa267559375c9fa0602fac3c56
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [0.14.0] - 2026-10-23
|
|
4
|
+
|
|
5
|
+
- Add `holivia enterprise show <id>` for enterprise details: settings, headquarter, CSM, countries, employees count, and the current contract summary for contract viewers and managers
|
|
6
|
+
- Add `holivia enterprise employee-stats <id>` for aggregate employee counts (total, left, current, current dependents and HR managers), excluding demo employees
|
|
7
|
+
- Add read-only `holivia employee index` and `holivia employee show` commands with enterprise, query, left, HR manager, dependent status, demo, and pagination filters; demo employees are excluded unless `--demo` is given
|
|
8
|
+
- Add read-only `holivia contract index` and `holivia contract show` commands with enterprise, status (`active`, `expiring_soon`, `low_remaining`), and pagination filters
|
|
9
|
+
- Employee responses never include Holivia account data (user id, sign-up, or care status); SKILL.md treats employee data as personal data
|
|
10
|
+
- Employee commands require an employee viewer or manager role; contract commands require a contract viewer or manager role
|
|
11
|
+
- Requires the matching Holivia backoffice API deployment
|
|
12
|
+
|
|
13
|
+
## [0.13.0] - 2026-09-30
|
|
14
|
+
|
|
15
|
+
- Replace terminal password login with browser OAuth authorization using PKCE and existing password/company SSO sign-in.
|
|
16
|
+
- Require CLI upgrade and a new login; remove `--email`/`--password` and legacy `/api/v1/auth/*` endpoints.
|
|
17
|
+
- Use two-hour access tokens, rotating refresh tokens without a fixed expiry, and server revocation for logout.
|
|
18
|
+
- Store per-environment credentials atomically with owner-only permissions and omit credentials from debug logs.
|
|
19
|
+
- Keep browser and CLI sessions independent; existing API permissions continue to apply.
|
|
20
|
+
- Redirect browser authorization callbacks to the localized Holivia completion page while the CLI exchanges tokens separately.
|
|
21
|
+
- Requires the matching Holivia OAuth server deployment and provisioning of the public CLI application.
|
|
22
|
+
|
|
3
23
|
## [0.12.0] - 2026-09-16
|
|
4
24
|
|
|
5
25
|
- Add `holivia tag` commands for catalogue CRUD and incremental topic-link management through repeatable `--topic-tag-json` flags or stdin JSON
|
data/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Ruby client and command-line interface for the Holivia API.
|
|
4
4
|
|
|
5
|
-
The `holivia` executable is primarily used to manage Holivia backoffice data from the terminal: environments, authentication, selfcare content, objectives, enterprises, communication library items, HR intervention catalog items, HR home highlights, crises, collective interventions, and appointment diagnostics.
|
|
5
|
+
The `holivia` executable is primarily used to manage Holivia backoffice data from the terminal: environments, authentication, selfcare content, objectives, enterprises, employees, contracts, communication library items, HR intervention catalog items, HR home highlights, crises, collective interventions, and appointment diagnostics.
|
|
6
6
|
|
|
7
7
|
Coding agents can use [`SKILL.md`](SKILL.md) as a downloadable usage guide for operating this CLI safely.
|
|
8
8
|
|
|
@@ -73,21 +73,23 @@ holivia env debug --false
|
|
|
73
73
|
|
|
74
74
|
## Authentication
|
|
75
75
|
|
|
76
|
-
Log in
|
|
76
|
+
Log in through your browser using your existing password or company SSO:
|
|
77
77
|
|
|
78
78
|
```bash
|
|
79
79
|
holivia login
|
|
80
80
|
```
|
|
81
81
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
```bash
|
|
85
|
-
holivia login --email user@example.com --password secret
|
|
86
|
-
```
|
|
82
|
+
The CLI opens a browser and prints a URL you can open manually on the same computer. A temporary loopback listener receives the callback. Browserless login is not supported.
|
|
87
83
|
|
|
88
84
|
Credentials are stored per environment in `~/.holivia/credentials.<env>.json`. Access tokens are sent as bearer tokens, and the CLI automatically refreshes credentials after a `401` response when a refresh token is available.
|
|
89
85
|
|
|
90
|
-
|
|
86
|
+
Access tokens expire after two hours. Refresh tokens have no fixed expiry and rotate when used. Credential files are replaced atomically with owner-only permissions. Pundit permissions still determine which API operations the user may perform.
|
|
87
|
+
|
|
88
|
+
Existing users must upgrade the CLI and run `holivia login` again. The old `/api/v1/auth/login`, `/api/v1/auth/refresh`, and `/api/v1/auth/logout` endpoints and `--email`/`--password` flags have been removed.
|
|
89
|
+
|
|
90
|
+
The database migration drops the legacy `auth_tokens` table. Rolling it back restores the table structure only; deleted or revoked credentials are not restored. Users must log in again.
|
|
91
|
+
|
|
92
|
+
Logout revokes this CLI session and removes credentials for the current environment. Other CLI sessions and browser sessions remain signed in. If the server cannot be reached, credentials are retained so logout can be retried:
|
|
91
93
|
|
|
92
94
|
```bash
|
|
93
95
|
holivia logout
|
|
@@ -184,6 +186,32 @@ Find an enterprise id:
|
|
|
184
186
|
holivia enterprise lookup --query acme
|
|
185
187
|
```
|
|
186
188
|
|
|
189
|
+
Show details for an enterprise:
|
|
190
|
+
|
|
191
|
+
```bash
|
|
192
|
+
holivia enterprise show 123
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
Show employee counts for an enterprise:
|
|
196
|
+
|
|
197
|
+
```bash
|
|
198
|
+
holivia enterprise employee-stats 123
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
List and inspect employees of an enterprise:
|
|
202
|
+
|
|
203
|
+
```bash
|
|
204
|
+
holivia employee index --enterprise-id 123 --query durand --left false
|
|
205
|
+
holivia employee show 42
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
List and inspect contracts of an enterprise:
|
|
209
|
+
|
|
210
|
+
```bash
|
|
211
|
+
holivia contract index --enterprise-id 123 --status active
|
|
212
|
+
holivia contract show 7
|
|
213
|
+
```
|
|
214
|
+
|
|
187
215
|
List crises for an enterprise:
|
|
188
216
|
|
|
189
217
|
```bash
|
data/SKILL.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Use this skill when you need to operate the Holivia API through the `holivia` command-line tool.
|
|
4
4
|
|
|
5
|
-
The CLI manages Holivia backoffice data: environments, authentication, selfcare contents, objectives, selfcare-objective associations, enterprises, HR intervention catalog items, HR home highlights, crises, collective interventions, and appointment video diagnostics.
|
|
5
|
+
The CLI manages Holivia backoffice data: environments, authentication, selfcare contents, objectives, selfcare-objective associations, enterprises, employees, contracts, HR intervention catalog items, HR home highlights, crises, collective interventions, and appointment video diagnostics.
|
|
6
6
|
|
|
7
7
|
## Preconditions
|
|
8
8
|
|
|
@@ -16,11 +16,12 @@ Use `holivia` in examples below. If working from a checkout before installation,
|
|
|
16
16
|
|
|
17
17
|
- Never invent API URLs, credentials, IDs, enum values, or content model fields.
|
|
18
18
|
- Never print, store, or commit passwords, access tokens, refresh tokens, or files under `~/.holivia/credentials.*.json`.
|
|
19
|
-
-
|
|
19
|
+
- Use `holivia login` and let the user authenticate in their browser. Terminal email/password flags are no longer supported.
|
|
20
20
|
- Before creating or updating content, run `holivia selfcare schema` and use its returned enum values and permitted item params as the source of truth.
|
|
21
21
|
- Before creating or updating intervention catalog items, run `holivia intervention schema` and use its returned enum values and locales as the source of truth.
|
|
22
22
|
- Before creating or updating HR home highlights, run `holivia highlight schema` and use its returned enum values and locales as the source of truth.
|
|
23
23
|
- For destructive actions such as `holivia env remove`, `holivia selfcare objective delete`, `holivia crisis delete`, or `holivia collective-intervention delete`, confirm the target environment and ID first unless the user explicitly requested that exact action.
|
|
24
|
+
- Employee data is personal data. Query only the employees a task needs, do not copy employee lists into unrelated output, and never try to infer whether an employee has a Holivia account.
|
|
24
25
|
- Treat all command output as JSON except informational commands such as `holivia env`, `holivia env list`, `holivia login`, `holivia logout`, `holivia version`, and `holivia --help`.
|
|
25
26
|
|
|
26
27
|
## Installation
|
|
@@ -90,19 +91,19 @@ Debug logging may expose request details. Do not enable it in shared logs unless
|
|
|
90
91
|
|
|
91
92
|
Credentials are stored per environment at `~/.holivia/credentials.<env>.json`. The CLI sends the access token as a bearer token and refreshes credentials automatically after a `401` response when possible.
|
|
92
93
|
|
|
93
|
-
|
|
94
|
+
Browser login using your existing password or company SSO:
|
|
94
95
|
|
|
95
96
|
```bash
|
|
96
97
|
holivia login
|
|
97
98
|
```
|
|
98
99
|
|
|
99
|
-
|
|
100
|
+
A browser on the same computer is required. If it does not open automatically, open the printed URL manually. Do not ask for terminal credentials; browserless login is not supported.
|
|
100
101
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
102
|
+
Access tokens expire after two hours. Refresh tokens have no fixed expiry and rotate when used. Credential files are replaced atomically with owner-only permissions. Pundit permissions still determine which API operations the user may perform.
|
|
103
|
+
|
|
104
|
+
Existing users must upgrade the CLI and run `holivia login` again. The old `/api/v1/auth/login`, `/api/v1/auth/refresh`, and `/api/v1/auth/logout` endpoints and `--email`/`--password` flags have been removed.
|
|
104
105
|
|
|
105
|
-
Logout for the current environment:
|
|
106
|
+
Logout revokes this CLI session and removes credentials for the current environment. Other CLI sessions and browser sessions remain signed in. If the server cannot be reached, credentials are retained so logout can be retried:
|
|
106
107
|
|
|
107
108
|
```bash
|
|
108
109
|
holivia logout
|
|
@@ -367,6 +368,66 @@ Filter enterprise lookup:
|
|
|
367
368
|
holivia enterprise index --headquarter-id 10 --user-id 20 --page 1 --per-page 25
|
|
368
369
|
```
|
|
369
370
|
|
|
371
|
+
Show details for one enterprise (settings, headquarter, CSM, countries, employees count; the current contract is included only for contract viewers and managers):
|
|
372
|
+
|
|
373
|
+
```bash
|
|
374
|
+
holivia enterprise show 123
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
Show aggregate employee counts for one enterprise (excludes demo employees; never includes account sign-up data):
|
|
378
|
+
|
|
379
|
+
```bash
|
|
380
|
+
holivia enterprise employee-stats 123
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
## Employees
|
|
384
|
+
|
|
385
|
+
Requires an employee viewer or manager role. Responses never include Holivia account data (no user id, sign-up, or care status). `employee index` excludes demo employees unless `--demo true` (demo employees only) or `--demo all` (everyone) is given.
|
|
386
|
+
|
|
387
|
+
List employees of an enterprise:
|
|
388
|
+
|
|
389
|
+
```bash
|
|
390
|
+
holivia employee index --enterprise-id 123 --page 1 --per-page 25
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
Search and filter employees:
|
|
394
|
+
|
|
395
|
+
```bash
|
|
396
|
+
holivia employee index --enterprise-id 123 --query durand --left false --hr-manager true
|
|
397
|
+
holivia employee index --enterprise-id 123 --dependent-status dependent
|
|
398
|
+
holivia employee index --enterprise-id 123 --demo true
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
Show one employee:
|
|
402
|
+
|
|
403
|
+
```bash
|
|
404
|
+
holivia employee show 42
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
## Contracts
|
|
408
|
+
|
|
409
|
+
Requires a contract viewer or manager role.
|
|
410
|
+
|
|
411
|
+
List contracts of an enterprise, most recent first:
|
|
412
|
+
|
|
413
|
+
```bash
|
|
414
|
+
holivia contract index --enterprise-id 123
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
Find active contracts, contracts expiring within three months, or contracts with low remaining sessions:
|
|
418
|
+
|
|
419
|
+
```bash
|
|
420
|
+
holivia contract index --status active
|
|
421
|
+
holivia contract index --status expiring_soon
|
|
422
|
+
holivia contract index --status low_remaining
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
Show every setting of one contract, including notes:
|
|
426
|
+
|
|
427
|
+
```bash
|
|
428
|
+
holivia contract show 7
|
|
429
|
+
```
|
|
430
|
+
|
|
370
431
|
## Crises
|
|
371
432
|
|
|
372
433
|
List crises:
|
data/exe/holivia
CHANGED
|
@@ -9,7 +9,10 @@ rescue Holivia::ApiError => e
|
|
|
9
9
|
warn "Error (#{e.status}): #{e.message}"
|
|
10
10
|
warn JSON.pretty_generate(e.details) if e.details.is_a?(Hash)
|
|
11
11
|
exit 1
|
|
12
|
-
rescue Holivia::ConfigError => e
|
|
12
|
+
rescue Holivia::ConfigError, Holivia::BrowserLogin::Error, OptionParser::ParseError, Faraday::Error => e
|
|
13
13
|
warn "Error: #{e.message}"
|
|
14
14
|
exit 1
|
|
15
|
+
rescue Interrupt
|
|
16
|
+
warn "Cancelled."
|
|
17
|
+
exit 130
|
|
15
18
|
end
|
data/lib/holivia/auth.rb
CHANGED
|
@@ -2,15 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|
require "json"
|
|
4
4
|
require "fileutils"
|
|
5
|
+
require "tempfile"
|
|
5
6
|
|
|
6
7
|
module Holivia
|
|
7
8
|
class Auth
|
|
8
|
-
def initialize(client:
|
|
9
|
-
@client = client
|
|
9
|
+
def initialize(client: nil, configuration: Holivia.configuration)
|
|
10
|
+
@client = client || Client.new(base_url: configuration.base_url, auth: nil)
|
|
11
|
+
@credentials_path = configuration.credentials_path.dup.freeze
|
|
10
12
|
end
|
|
11
13
|
|
|
12
|
-
def login(
|
|
13
|
-
response = client.
|
|
14
|
+
def login(code:, code_verifier:, redirect_uri:)
|
|
15
|
+
response = client.post_form("/oauth/token", body: {
|
|
16
|
+
grant_type: "authorization_code", client_id: BrowserLogin::CLIENT_ID,
|
|
17
|
+
code: code, code_verifier: code_verifier, redirect_uri: redirect_uri
|
|
18
|
+
})
|
|
14
19
|
store_tokens(response)
|
|
15
20
|
response
|
|
16
21
|
end
|
|
@@ -18,15 +23,25 @@ module Holivia
|
|
|
18
23
|
def refresh
|
|
19
24
|
raise ApiError.new("No refresh token. Run `holivia login`.", status: 401) unless refresh_token
|
|
20
25
|
|
|
21
|
-
response = client.
|
|
26
|
+
response = client.post_form("/oauth/token", body: {
|
|
27
|
+
grant_type: "refresh_token", client_id: BrowserLogin::CLIENT_ID,
|
|
28
|
+
refresh_token: refresh_token
|
|
29
|
+
})
|
|
22
30
|
store_tokens(response)
|
|
23
31
|
response
|
|
32
|
+
rescue ApiError => e
|
|
33
|
+
raise unless e.details.is_a?(Hash) && e.details["error"] == "invalid_grant"
|
|
34
|
+
|
|
35
|
+
raise ApiError.new("Session expired or revoked. Run `holivia login`.", status: 401)
|
|
24
36
|
end
|
|
25
37
|
|
|
26
38
|
def logout
|
|
27
|
-
raise ApiError.new("Already logged out.", status: 401) unless
|
|
39
|
+
raise ApiError.new("Already logged out.", status: 401) unless refresh_token
|
|
28
40
|
|
|
29
|
-
response = client.
|
|
41
|
+
response = client.post_form("/oauth/revoke", body: {
|
|
42
|
+
client_id: BrowserLogin::CLIENT_ID, token: refresh_token,
|
|
43
|
+
token_type_hint: "refresh_token"
|
|
44
|
+
})
|
|
30
45
|
FileUtils.rm_f(credentials_path)
|
|
31
46
|
response
|
|
32
47
|
end
|
|
@@ -41,7 +56,7 @@ module Holivia
|
|
|
41
56
|
|
|
42
57
|
private
|
|
43
58
|
|
|
44
|
-
attr_reader :client
|
|
59
|
+
attr_reader :client, :credentials_path
|
|
45
60
|
|
|
46
61
|
def credentials
|
|
47
62
|
return nil unless File.exist?(credentials_path)
|
|
@@ -50,17 +65,14 @@ module Holivia
|
|
|
50
65
|
end
|
|
51
66
|
|
|
52
67
|
def store_tokens(data)
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
def credentials_path
|
|
63
|
-
Holivia.configuration.credentials_path
|
|
68
|
+
directory = File.dirname(credentials_path)
|
|
69
|
+
FileUtils.mkdir_p(directory, mode: 0o700)
|
|
70
|
+
Tempfile.create(["credentials", ".json"], directory) do |file|
|
|
71
|
+
file.write(JSON.pretty_generate(data.slice("access_token", "refresh_token")))
|
|
72
|
+
file.flush
|
|
73
|
+
file.fsync
|
|
74
|
+
File.rename(file.path, credentials_path)
|
|
75
|
+
end
|
|
64
76
|
end
|
|
65
77
|
end
|
|
66
78
|
end
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "digest"
|
|
4
|
+
require "securerandom"
|
|
5
|
+
require "socket"
|
|
6
|
+
require "timeout"
|
|
7
|
+
require "uri"
|
|
8
|
+
|
|
9
|
+
module Holivia
|
|
10
|
+
class BrowserLogin
|
|
11
|
+
class Error < StandardError; end
|
|
12
|
+
|
|
13
|
+
CLIENT_ID = "holivia-cli"
|
|
14
|
+
|
|
15
|
+
def initialize(base_url: Holivia.configuration.base_url, output: $stderr, timeout: 180, browser_launcher: nil)
|
|
16
|
+
@base_url = base_url.dup.freeze
|
|
17
|
+
@output = output
|
|
18
|
+
@timeout = timeout
|
|
19
|
+
@browser_launcher = browser_launcher || method(:launch_browser)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def call
|
|
23
|
+
verifier = SecureRandom.urlsafe_base64(32)
|
|
24
|
+
state = SecureRandom.urlsafe_base64(32)
|
|
25
|
+
server = TCPServer.new("127.0.0.1", 0)
|
|
26
|
+
redirect_uri = "http://127.0.0.1:#{server.addr[1]}/callback"
|
|
27
|
+
|
|
28
|
+
Timeout.timeout(@timeout, Error, "Browser login timed out. Run holivia login to try again.") do
|
|
29
|
+
open_browser(authorization_url(verifier, state, redirect_uri))
|
|
30
|
+
code = receive_code(server, state)
|
|
31
|
+
{ code: code, code_verifier: verifier, redirect_uri: redirect_uri }
|
|
32
|
+
end
|
|
33
|
+
ensure
|
|
34
|
+
server&.close
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
|
|
39
|
+
def authorization_url(verifier, state, redirect_uri)
|
|
40
|
+
url = URI.join(@base_url, "/oauth/authorize")
|
|
41
|
+
url.query = URI.encode_www_form(
|
|
42
|
+
client_id: CLIENT_ID, response_type: "code", redirect_uri: redirect_uri,
|
|
43
|
+
code_challenge: [Digest::SHA256.digest(verifier)].pack("m0").tr("+/", "-_").delete("="),
|
|
44
|
+
code_challenge_method: "S256", state: state
|
|
45
|
+
)
|
|
46
|
+
url.to_s
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def open_browser(url)
|
|
50
|
+
@output.puts "Open this URL in a browser on this computer to log in:\n#{url}"
|
|
51
|
+
@browser_launcher.call(url)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def launch_browser(url)
|
|
55
|
+
command = RUBY_PLATFORM.include?("darwin") ? "open" : "xdg-open"
|
|
56
|
+
system(command, url, out: File::NULL, err: File::NULL)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def receive_code(server, state)
|
|
60
|
+
loop do
|
|
61
|
+
code = handle_callback(server.accept, state)
|
|
62
|
+
return code if code
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def handle_callback(socket, state)
|
|
67
|
+
params = callback_params(socket.gets("\r\n\r\n", 8192))
|
|
68
|
+
unless valid_callback?(params, state)
|
|
69
|
+
respond(socket, "400 Bad Request", "Invalid login callback.")
|
|
70
|
+
return nil
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
complete_callback(socket, params)
|
|
74
|
+
rescue SystemCallError, IOError
|
|
75
|
+
nil # A disconnected browser must not prevent a later valid callback.
|
|
76
|
+
ensure
|
|
77
|
+
socket.close
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def callback_params(request)
|
|
81
|
+
return {} unless request&.end_with?("\r\n\r\n")
|
|
82
|
+
|
|
83
|
+
match = request.lines.first.match(%r{\AGET /callback\?([^\s]+) HTTP/1\.[01]\r\n\z})
|
|
84
|
+
return {} unless match
|
|
85
|
+
|
|
86
|
+
pairs = URI.decode_www_form(match[1])
|
|
87
|
+
return {} unless pairs.map(&:first).uniq.length == pairs.length
|
|
88
|
+
|
|
89
|
+
pairs.to_h
|
|
90
|
+
rescue ArgumentError
|
|
91
|
+
{}
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def valid_callback?(params, state)
|
|
95
|
+
return false unless params["state"] == state
|
|
96
|
+
|
|
97
|
+
code = params["code"]
|
|
98
|
+
error = params["error"]
|
|
99
|
+
(code && !code.empty? && error.nil?) || (error && !error.empty? && code.nil?)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def complete_callback(socket, params)
|
|
103
|
+
if params["error"]
|
|
104
|
+
respond(socket, "200 OK", "Authorization was not granted. Return to the terminal.")
|
|
105
|
+
raise Error, "Authorization was not granted. Run holivia login to try again."
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
redirect_to_completion(socket)
|
|
109
|
+
params.fetch("code")
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def redirect_to_completion(socket)
|
|
113
|
+
url = URI.join(@base_url, "/oauth/complete")
|
|
114
|
+
socket.write "HTTP/1.1 303 See Other\r\nLocation: #{url}\r\n" \
|
|
115
|
+
"Referrer-Policy: no-referrer\r\nCache-Control: no-store\r\n" \
|
|
116
|
+
"Content-Length: 0\r\nConnection: close\r\n\r\n"
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def respond(socket, status, message)
|
|
120
|
+
socket.write "HTTP/1.1 #{status}\r\nContent-Type: text/plain; charset=utf-8\r\n" \
|
|
121
|
+
"Content-Length: #{message.bytesize}\r\nCache-Control: no-store\r\n" \
|
|
122
|
+
"Connection: close\r\n\r\n#{message}"
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|
data/lib/holivia/cli/help.rb
CHANGED
|
@@ -17,7 +17,7 @@ module Holivia
|
|
|
17
17
|
Config stored at ~/.holivia/config.yml. Defaults to staging if no config exists.
|
|
18
18
|
|
|
19
19
|
Authentication:
|
|
20
|
-
holivia login
|
|
20
|
+
holivia login Log in through your browser (password or company SSO)
|
|
21
21
|
holivia logout Log out and clear stored credentials
|
|
22
22
|
Tokens are stored at ~/.holivia/credentials.<env>.json and refreshed automatically on 401.
|
|
23
23
|
|
|
@@ -77,6 +77,24 @@ module Holivia
|
|
|
77
77
|
holivia enterprise index [--query TEXT --headquarter-id ID --user-id ID --page N --per-page N]
|
|
78
78
|
Find enterprises and their enterprise_id values
|
|
79
79
|
holivia enterprise lookup [options] Alias for enterprise index
|
|
80
|
+
holivia enterprise show <id> Show enterprise details: settings, headquarter, CSM,
|
|
81
|
+
countries, employees count, and the current contract
|
|
82
|
+
for contract viewers
|
|
83
|
+
holivia enterprise employee-stats <id> Show aggregate employee counts (total, left, current,
|
|
84
|
+
current dependents, current HR managers)
|
|
85
|
+
|
|
86
|
+
Employees:
|
|
87
|
+
holivia employee index [--enterprise-id ID --query TEXT --left true|false --hr-manager true|false
|
|
88
|
+
--dependent-status creator|dependent|not_dependent --demo true|false|all
|
|
89
|
+
--page N --per-page N]
|
|
90
|
+
List employees; never includes Holivia account data.
|
|
91
|
+
Demo employees are excluded unless --demo is given
|
|
92
|
+
holivia employee show <id> Show one employee with its enterprise
|
|
93
|
+
|
|
94
|
+
Contracts:
|
|
95
|
+
holivia contract index [--enterprise-id ID --status active|expiring_soon|low_remaining --page N --per-page N]
|
|
96
|
+
List contracts, most recent first
|
|
97
|
+
holivia contract show <id> Show every setting of one contract with its enterprise
|
|
80
98
|
|
|
81
99
|
Communication Items:
|
|
82
100
|
holivia communication index [--page N --per-page N --query TEXT --locale LOCALE --document-type TYPE]
|
data/lib/holivia/cli.rb
CHANGED
|
@@ -16,6 +16,8 @@ require_relative "commands/communication_item"
|
|
|
16
16
|
require_relative "commands/intervention_item"
|
|
17
17
|
require_relative "commands/hr_home_highlight"
|
|
18
18
|
require_relative "commands/enterprise"
|
|
19
|
+
require_relative "commands/employee"
|
|
20
|
+
require_relative "commands/contract"
|
|
19
21
|
require_relative "commands/crisis"
|
|
20
22
|
require_relative "commands/collective_intervention"
|
|
21
23
|
require_relative "commands/support_phone_number"
|
|
@@ -37,6 +39,8 @@ module Holivia
|
|
|
37
39
|
when "intervention" then Commands::InterventionItem.route(args)
|
|
38
40
|
when "highlight" then Commands::HrHomeHighlight.route(args)
|
|
39
41
|
when "enterprise" then Commands::Enterprise.route(args)
|
|
42
|
+
when "employee" then Commands::Employee.route(args)
|
|
43
|
+
when "contract" then Commands::Contract.route(args)
|
|
40
44
|
when "crisis" then Commands::Crisis.route(args)
|
|
41
45
|
when "collective-intervention", "collective_intervention", "collective" then Commands::CollectiveIntervention.route(args)
|
|
42
46
|
when "support-phone-numbers" then Commands::SupportPhoneNumber.route(args)
|
data/lib/holivia/client.rb
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require "faraday"
|
|
4
4
|
require "faraday/multipart"
|
|
5
|
+
require "uri"
|
|
5
6
|
|
|
6
7
|
module Holivia
|
|
7
8
|
class Client
|
|
@@ -15,6 +16,16 @@ module Holivia
|
|
|
15
16
|
request(:post, path, body:, headers:)
|
|
16
17
|
end
|
|
17
18
|
|
|
19
|
+
def post_form(path, body: {})
|
|
20
|
+
response = connection.post(path) do |req|
|
|
21
|
+
req.headers["Accept"] = "application/json"
|
|
22
|
+
req.headers["Content-Type"] = "application/x-www-form-urlencoded"
|
|
23
|
+
req.body = URI.encode_www_form(body)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
handle_response(response)
|
|
27
|
+
end
|
|
28
|
+
|
|
18
29
|
def get(path, params: {}, headers: {})
|
|
19
30
|
request(:get, path, params:, headers:)
|
|
20
31
|
end
|
|
@@ -59,7 +70,7 @@ module Holivia
|
|
|
59
70
|
@connection ||= http.new(url: base_url) do |f|
|
|
60
71
|
f.request :multipart
|
|
61
72
|
f.request :json
|
|
62
|
-
f.response :logger if Holivia.configuration.debug?
|
|
73
|
+
f.response :logger, nil, headers: false, bodies: false if Holivia.configuration.debug?
|
|
63
74
|
f.response :json
|
|
64
75
|
f.adapter Faraday.default_adapter
|
|
65
76
|
end
|
|
@@ -77,6 +88,8 @@ module Holivia
|
|
|
77
88
|
return "Request failed" unless body.is_a?(Hash)
|
|
78
89
|
|
|
79
90
|
error = body["error"]
|
|
91
|
+
return [error, body["error_description"]].compact.join(": ") if error.is_a?(String)
|
|
92
|
+
|
|
80
93
|
error.is_a?(Hash) ? (error["message"] || "Request failed") : (error || "Request failed")
|
|
81
94
|
end
|
|
82
95
|
|
|
@@ -1,23 +1,19 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "io/console"
|
|
4
3
|
require "optparse"
|
|
5
4
|
|
|
6
5
|
module Holivia
|
|
7
6
|
module Commands
|
|
8
7
|
class Auth
|
|
9
8
|
def login(args = [])
|
|
10
|
-
options = {}
|
|
11
9
|
OptionParser.new do |opts|
|
|
12
|
-
opts.banner = "Usage: holivia login
|
|
13
|
-
opts.on("--email EMAIL") { |v| options[:email] = v }
|
|
14
|
-
opts.on("--password PASSWORD") { |v| options[:password] = v }
|
|
10
|
+
opts.banner = "Usage: holivia login"
|
|
15
11
|
end.parse!(args)
|
|
12
|
+
raise OptionParser::InvalidArgument, args.join(" ") unless args.empty?
|
|
16
13
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
Holivia::Auth.new.login(email: email, password: password)
|
|
14
|
+
auth = Holivia::Auth.new
|
|
15
|
+
exchange = BrowserLogin.new.call
|
|
16
|
+
auth.login(**exchange)
|
|
21
17
|
puts "Logged in successfully."
|
|
22
18
|
end
|
|
23
19
|
|
|
@@ -25,20 +21,6 @@ module Holivia
|
|
|
25
21
|
Holivia::Auth.new.logout
|
|
26
22
|
puts "Logged out successfully."
|
|
27
23
|
end
|
|
28
|
-
|
|
29
|
-
private
|
|
30
|
-
|
|
31
|
-
def prompt(label)
|
|
32
|
-
print label
|
|
33
|
-
$stdin.gets.chomp
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
def prompt_secret(label)
|
|
37
|
-
print label
|
|
38
|
-
result = $stdin.noecho(&:gets).chomp
|
|
39
|
-
puts
|
|
40
|
-
result
|
|
41
|
-
end
|
|
42
24
|
end
|
|
43
25
|
end
|
|
44
26
|
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "base"
|
|
4
|
+
|
|
5
|
+
module Holivia
|
|
6
|
+
module Commands
|
|
7
|
+
class Contract < Base
|
|
8
|
+
BASE_PATH = "/api/v1/backoffice/contracts"
|
|
9
|
+
|
|
10
|
+
def self.route(args)
|
|
11
|
+
subcommand = args.shift
|
|
12
|
+
case subcommand
|
|
13
|
+
when "index" then new.index(args)
|
|
14
|
+
when "show" then new.show(args)
|
|
15
|
+
else warn "Unknown contract command: #{subcommand}"
|
|
16
|
+
exit 1
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def index(args = [])
|
|
21
|
+
params = {}
|
|
22
|
+
OptionParser.new do |opts|
|
|
23
|
+
opts.banner = "Usage: holivia contract index [options]"
|
|
24
|
+
opts.on("--enterprise-id ID", Integer) { |v| params[:enterprise_id] = v }
|
|
25
|
+
opts.on("--status STATUS") { |v| params[:status] = v }
|
|
26
|
+
opts.on("--page N", Integer) { |v| params[:page] = v }
|
|
27
|
+
opts.on("--per-page N", Integer) { |v| params[:per_page] = v }
|
|
28
|
+
end.parse!(args)
|
|
29
|
+
|
|
30
|
+
output(client.get(BASE_PATH, params: params))
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def show(args = [])
|
|
34
|
+
id = args.shift
|
|
35
|
+
abort "Usage: holivia contract show <id>" unless id
|
|
36
|
+
|
|
37
|
+
output(client.get("#{BASE_PATH}/#{id}"))
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "base"
|
|
4
|
+
|
|
5
|
+
module Holivia
|
|
6
|
+
module Commands
|
|
7
|
+
class Employee < Base
|
|
8
|
+
BASE_PATH = "/api/v1/backoffice/employees"
|
|
9
|
+
BOOLEAN_VALUES = %w[true false].freeze
|
|
10
|
+
DEPENDENT_STATUSES = %w[creator dependent not_dependent].freeze
|
|
11
|
+
DEMO_VALUES = %w[true false all].freeze
|
|
12
|
+
|
|
13
|
+
def self.route(args)
|
|
14
|
+
subcommand = args.shift
|
|
15
|
+
case subcommand
|
|
16
|
+
when "index" then new.index(args)
|
|
17
|
+
when "show" then new.show(args)
|
|
18
|
+
else warn "Unknown employee command: #{subcommand}"
|
|
19
|
+
exit 1
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def index(args = []) # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
|
|
24
|
+
params = {}
|
|
25
|
+
OptionParser.new do |opts|
|
|
26
|
+
opts.banner = "Usage: holivia employee index [options]"
|
|
27
|
+
opts.on("--enterprise-id ID", Integer) { |v| params[:enterprise_id] = v }
|
|
28
|
+
opts.on("--query TEXT") { |v| params[:query] = v }
|
|
29
|
+
opts.on("--left BOOL", BOOLEAN_VALUES) { |v| params[:left] = v }
|
|
30
|
+
opts.on("--hr-manager BOOL", BOOLEAN_VALUES) { |v| params[:hr_manager] = v }
|
|
31
|
+
opts.on("--dependent-status STATUS", DEPENDENT_STATUSES) { |v| params[:dependent_status] = v }
|
|
32
|
+
opts.on("--demo VALUE", DEMO_VALUES) { |v| params[:demo] = v }
|
|
33
|
+
opts.on("--page N", Integer) { |v| params[:page] = v }
|
|
34
|
+
opts.on("--per-page N", Integer) { |v| params[:per_page] = v }
|
|
35
|
+
end.parse!(args)
|
|
36
|
+
|
|
37
|
+
output(client.get(BASE_PATH, params: params))
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def show(args = [])
|
|
41
|
+
id = args.shift
|
|
42
|
+
abort "Usage: holivia employee show <id>" unless id
|
|
43
|
+
|
|
44
|
+
output(client.get("#{BASE_PATH}/#{id}"))
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -11,6 +11,8 @@ module Holivia
|
|
|
11
11
|
subcommand = args.shift
|
|
12
12
|
case subcommand
|
|
13
13
|
when "index", "lookup", "search" then new.index(args)
|
|
14
|
+
when "show" then new.show(args)
|
|
15
|
+
when "employee-stats" then new.employee_stats(args)
|
|
14
16
|
else warn "Unknown enterprise command: #{subcommand}"
|
|
15
17
|
exit 1
|
|
16
18
|
end
|
|
@@ -29,6 +31,20 @@ module Holivia
|
|
|
29
31
|
|
|
30
32
|
output(client.get(BASE_PATH, params: params))
|
|
31
33
|
end
|
|
34
|
+
|
|
35
|
+
def show(args = [])
|
|
36
|
+
id = args.shift
|
|
37
|
+
abort "Usage: holivia enterprise show <id>" unless id
|
|
38
|
+
|
|
39
|
+
output(client.get("#{BASE_PATH}/#{id}"))
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def employee_stats(args = [])
|
|
43
|
+
id = args.shift
|
|
44
|
+
abort "Usage: holivia enterprise employee-stats <id>" unless id
|
|
45
|
+
|
|
46
|
+
output(client.get("#{BASE_PATH}/#{id}/employee_stats"))
|
|
47
|
+
end
|
|
32
48
|
end
|
|
33
49
|
end
|
|
34
50
|
end
|
data/lib/holivia/version.rb
CHANGED
data/lib/holivia.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: holivia
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.14.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Holivia
|
|
@@ -57,6 +57,7 @@ files:
|
|
|
57
57
|
- lib/holivia.rb
|
|
58
58
|
- lib/holivia/api_error.rb
|
|
59
59
|
- lib/holivia/auth.rb
|
|
60
|
+
- lib/holivia/browser_login.rb
|
|
60
61
|
- lib/holivia/cli.rb
|
|
61
62
|
- lib/holivia/cli/help.rb
|
|
62
63
|
- lib/holivia/client.rb
|
|
@@ -65,7 +66,9 @@ files:
|
|
|
65
66
|
- lib/holivia/commands/base.rb
|
|
66
67
|
- lib/holivia/commands/collective_intervention.rb
|
|
67
68
|
- lib/holivia/commands/communication_item.rb
|
|
69
|
+
- lib/holivia/commands/contract.rb
|
|
68
70
|
- lib/holivia/commands/crisis.rb
|
|
71
|
+
- lib/holivia/commands/employee.rb
|
|
69
72
|
- lib/holivia/commands/enterprise.rb
|
|
70
73
|
- lib/holivia/commands/env.rb
|
|
71
74
|
- lib/holivia/commands/format.rb
|