Module: KnoxCall::CLI::Common
- Defined in:
- lib/knoxcall/cli/common.rb
Overview
Shared CLI plumbing — token-endpoint POSTs, profile persistence.
Class Method Summary collapse
-
.default_base_url(sandbox) ⇒ Object
Default management base: KNOXCALL_BASE_URL env > (sandbox|production) host.
-
.persist_login(path:, profile:, base_url:, token_body:, fallback_tenant: nil) ⇒ Object
Store a successful token response as a credentials-file profile.
-
.post_form(url, form, timeout: 30) ⇒ Object
POST a urlencoded form; return [status, parsed-JSON-or-empty-hash].
- .token_error_message(status, body) ⇒ Object
Class Method Details
.default_base_url(sandbox) ⇒ Object
Default management base: KNOXCALL_BASE_URL env > (sandbox|production) host. An explicit --base-url beats both (handled by the caller).
89 90 91 92 93 |
# File 'lib/knoxcall/cli/common.rb', line 89 def default_base_url(sandbox) env = ENV["KNOXCALL_BASE_URL"] return env if env && !env.empty? sandbox ? "https://sandbox.#{DEFAULT_CLOUD_HOST}" : DEFAULT_API_BASE end |
.persist_login(path:, profile:, base_url:, token_body:, fallback_tenant: nil) ⇒ Object
Store a successful token response as a credentials-file profile.
Persists the tenant and client_id extension members — refreshes
must use the REAL per-tenant client id, not the knoxcall-cli alias.
The write happens UNDER the cross-process file lock so a login racing
a concurrent refresh never loses a rotation.
63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
# File 'lib/knoxcall/cli/common.rb', line 63 def persist_login(path:, profile:, base_url:, token_body:, fallback_tenant: nil) expires_in = begin Float(token_body["expires_in"] || 3600) rescue ArgumentError, TypeError 3600.0 end record = { "tenant" => CredentialsFile.presence(token_body["tenant"]) || fallback_tenant, "base_url" => base_url, "client_id" => CredentialsFile.presence(token_body["client_id"]) || CLI_CLIENT_ID, "refresh_token" => token_body["refresh_token"], "access_token" => token_body["access_token"], "access_token_expires_at" => CredentialsFile.format_expiry(Time.now + expires_in), "scope" => token_body["scope"] || "" } # The lock file lives next to the target — on a first-ever login the # directory does not exist yet, so create it before acquiring. FileUtils.mkdir_p(File.dirname(path), mode: 0o700) CredentialsFile::Lock.new(path).with_lock do CredentialsFile.write_profile(path, profile, record) end record end |
.post_form(url, form, timeout: 30) ⇒ Object
POST a urlencoded form; return [status, parsed-JSON-or-empty-hash].
Connection failures raise CLI::Error with a human message. HTTP error statuses are returned, not raised — device polling needs the error codes.
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
# File 'lib/knoxcall/cli/common.rb', line 24 def post_form(url, form, timeout: 30) uri = URI.parse(url) req = Net::HTTP::Post.new(uri) req["Content-Type"] = "application/x-www-form-urlencoded" req["Accept"] = "application/json" req["User-Agent"] = SDK_VERSION req.body = URI.encode_www_form(form) resp = begin http = Net::HTTP.new(uri.host, uri.port) http.use_ssl = uri.scheme == "https" http.open_timeout = timeout http.read_timeout = timeout http.start { |h| h.request(req) } rescue Net::OpenTimeout, Net::ReadTimeout, OpenSSL::SSL::SSLError, EOFError, SocketError, SystemCallError, IOError => e raise Error, "could not reach #{url}: #{e.message}" end body = begin JSON.parse(resp.body.to_s) rescue JSON::ParserError nil end [resp.code.to_i, body.is_a?(Hash) ? body : {}] end |
.token_error_message(status, body) ⇒ Object
51 52 53 54 55 |
# File 'lib/knoxcall/cli/common.rb', line 51 def (status, body) detail = CredentialsFile.presence(body["error_description"]) || CredentialsFile.presence(body["error"]) || "HTTP #{status}" "sign-in failed: #{detail}" end |