Class: SmilyCli::Context
- Inherits:
-
Object
- Object
- SmilyCli::Context
- Defined in:
- lib/smily_cli/context.rb
Overview
Resolves effective settings for a command from three layers, highest wins:
1. explicit flags (the Thor `options` hash)
2. environment variables (`SMILY_*`, with `BOOKINGSYNC_*` fallbacks)
3. the selected config profile
A Context is the single object a command needs: it exposes the resolved token/base_url/output settings and can build a Client. It never performs I/O beyond reading the config file.
Constant Summary collapse
- DEFAULT_BASE_URL =
"https://www.bookingsync.com"- OUTPUT_FORMATS =
%w[table json yaml csv ndjson jsonl].freeze
- DEFAULT_MAX_PAGES =
1000
Instance Attribute Summary collapse
- #config ⇒ Config readonly
-
#options ⇒ Hash
readonly
The raw flags this context was built from.
Class Method Summary collapse
-
.from(options, config: nil) ⇒ Context
Build a Context from a Thor options hash.
Instance Method Summary collapse
-
#account_id ⇒ String?
Account to scope requests to.
-
#base_url ⇒ String
API base URL (no trailing
/api/v3; the client appends that). - #client ⇒ Client
-
#client_id ⇒ String?
Values used to seed OAuth commands, pulled from flags then profile.
- #client_secret ⇒ String?
-
#color? ⇒ Boolean
Whether ANSI color is active for this run.
-
#fields ⇒ Array<String>?
Field selection shared between sparse fieldsets (sent to the API) and column selection (used by table/csv).
-
#initialize(options, config) ⇒ Context
constructor
A new instance of Context.
-
#max_pages ⇒ Integer
Build an API client from these settings.
-
#mcp_client ⇒ Mcp::Client
An MCP client for the configured MCP server and token.
-
#mcp_token ⇒ String?
The MCP-mode access token (distinct from the REST API token).
- #mcp_token! ⇒ String
-
#mcp_url ⇒ String
The MCP server endpoint.
-
#oauth ⇒ OAuth
An OAuth helper bound to this context's base URL.
-
#output_format ⇒ String
Chosen output format.
-
#profile_name ⇒ String
The profile these settings resolve against.
- #quiet? ⇒ Boolean
- #refresh_token ⇒ String?
-
#requested_output_format ⇒ String?
The output format explicitly requested via
--outputorSMILY_OUTPUT, validated, or nil when none was given. -
#retries ⇒ Integer
Number of times to retry a
429 Too Many Requests(waiting for the rate-limit window each time). -
#token ⇒ String?
The OAuth access token.
-
#token! ⇒ String
Like #token but raises when nothing is configured.
- #verbose? ⇒ Boolean
Constructor Details
#initialize(options, config) ⇒ Context
Returns a new instance of Context.
41 42 43 44 45 |
# File 'lib/smily_cli/context.rb', line 41 def initialize(, config) @options = @config = config apply_color_setting end |
Instance Attribute Details
#config ⇒ Config (readonly)
21 22 23 |
# File 'lib/smily_cli/context.rb', line 21 def config @config end |
#options ⇒ Hash (readonly)
Returns the raw flags this context was built from.
23 24 25 |
# File 'lib/smily_cli/context.rb', line 23 def @options end |
Class Method Details
.from(options, config: nil) ⇒ Context
Build a Context from a Thor options hash.
30 31 32 33 34 35 36 37 |
# File 'lib/smily_cli/context.rb', line 30 def self.from(, config: nil) opts = .each_with_object({}) { |(k, v), h| h[k.to_s] = v } cfg = config || Config.load if (profile = opts["profile"] || ENV.fetch("SMILY_PROFILE", nil)) cfg.current_profile_name = profile end new(opts, cfg) end |
Instance Method Details
#account_id ⇒ String?
Account to scope requests to. Primarily for Client-Credentials-flow tokens
that span multiple accounts; sent as the account_id query parameter (REST)
or account_id tool argument (MCP).
82 83 84 |
# File 'lib/smily_cli/context.rb', line 82 def account_id resolve("account_id", env: %w[SMILY_ACCOUNT_ID BOOKINGSYNC_ACCOUNT_ID]) end |
#base_url ⇒ String
API base URL (no trailing /api/v3; the client appends that).
73 74 75 |
# File 'lib/smily_cli/context.rb', line 73 def base_url resolve("base_url", env: %w[SMILY_BASE_URL BOOKINGSYNC_URL]) || DEFAULT_BASE_URL end |
#client ⇒ Client
190 191 192 193 194 195 196 197 198 199 200 |
# File 'lib/smily_cli/context.rb', line 190 def client warn_insecure_transport(base_url) Client.new( token: token!, base_url: base_url, account_id: account_id, max_pages: max_pages, retries: retries, verbose: verbose? ) end |
#client_id ⇒ String?
Values used to seed OAuth commands, pulled from flags then profile.
163 164 |
# File 'lib/smily_cli/context.rb', line 163 def client_id = resolve("client_id", env: %w[SMILY_CLIENT_ID]) # @return [String, nil] |
#client_secret ⇒ String?
165 166 |
# File 'lib/smily_cli/context.rb', line 165 def client_secret = resolve("client_secret", env: %w[SMILY_CLIENT_SECRET]) # @return [String, nil] |
#color? ⇒ Boolean
Returns whether ANSI color is active for this run.
157 158 159 |
# File 'lib/smily_cli/context.rb', line 157 def color? Color.enabled end |
#fields ⇒ Array<String>?
Field selection shared between sparse fieldsets (sent to the API) and column selection (used by table/csv). Accepts repeated flags and/or comma-separated values.
138 139 140 141 142 143 144 |
# File 'lib/smily_cli/context.rb', line 138 def fields raw = ["fields"] return nil if raw.nil? list = Array(raw).flat_map { |v| v.to_s.split(",") }.map(&:strip).reject(&:empty?) list.empty? ? nil : list end |
#max_pages ⇒ Integer
Build an API client from these settings.
Hard backstop for auto-pagination (--all / --limit), so a server that
keeps advertising a next link can't loop forever.
175 176 177 178 179 |
# File 'lib/smily_cli/context.rb', line 175 def max_pages raw = resolve("max_pages", env: %w[SMILY_MAX_PAGES]) value = raw.to_i value.positive? ? value : DEFAULT_MAX_PAGES end |
#mcp_client ⇒ Mcp::Client
An MCP client for the configured MCP server and token.
213 214 215 216 |
# File 'lib/smily_cli/context.rb', line 213 def mcp_client warn_insecure_transport(mcp_url) Mcp::Client.new(token: mcp_token!, url: mcp_url, verbose: verbose?) end |
#mcp_token ⇒ String?
The MCP-mode access token (distinct from the REST API token).
89 90 91 |
# File 'lib/smily_cli/context.rb', line 89 def mcp_token resolve("mcp_token", env: %w[SMILY_MCP_TOKEN]) end |
#mcp_token! ⇒ String
94 95 96 97 98 99 |
# File 'lib/smily_cli/context.rb', line 94 def mcp_token! mcp_token || raise(ConfigurationError, <<~MSG.strip) No MCP token configured. Provide one with --mcp-token, set SMILY_MCP_TOKEN, or run: smily mcp login --token <MCP_TOKEN> MSG end |
#mcp_url ⇒ String
The MCP server endpoint. Defaults to <base_url>/mcp, which is where a
BookingSync app mounts its MCP server; override for other servers.
105 106 107 108 109 110 |
# File 'lib/smily_cli/context.rb', line 105 def mcp_url explicit = resolve("mcp_url", env: %w[SMILY_MCP_URL]) return explicit if explicit && !explicit.empty? URI.join("#{base_url.chomp('/')}/", "mcp").to_s end |
#oauth ⇒ OAuth
An OAuth helper bound to this context's base URL.
205 206 207 208 |
# File 'lib/smily_cli/context.rb', line 205 def oauth warn_insecure_transport(base_url) OAuth.new(base_url: base_url, verbose: verbose?) end |
#output_format ⇒ String
Chosen output format. Defaults to table on a TTY, json otherwise, so
piping produces machine-readable output without a flag.
129 130 131 |
# File 'lib/smily_cli/context.rb', line 129 def output_format requested_output_format || default_output_format end |
#profile_name ⇒ String
Returns the profile these settings resolve against.
48 49 50 |
# File 'lib/smily_cli/context.rb', line 48 def profile_name config.current_profile_name end |
#quiet? ⇒ Boolean
152 153 154 |
# File 'lib/smily_cli/context.rb', line 152 def quiet? truthy(["quiet"]) end |
#refresh_token ⇒ String?
167 |
# File 'lib/smily_cli/context.rb', line 167 def refresh_token = resolve("refresh_token", env: %w[SMILY_REFRESH_TOKEN]) |
#requested_output_format ⇒ String?
The output format explicitly requested via --output or SMILY_OUTPUT,
validated, or nil when none was given. Commands whose default isn't the
usual table/json (e.g. api, which defaults to the raw envelope) use this
so they still honor an explicit flag/env choice.
118 119 120 121 122 123 |
# File 'lib/smily_cli/context.rb', line 118 def requested_output_format raw = (["output"] || ENV.fetch("SMILY_OUTPUT", nil))&.to_s return nil if raw.nil? || raw.empty? validate_output_format!(raw) end |
#retries ⇒ Integer
Number of times to retry a 429 Too Many Requests (waiting for the
rate-limit window each time). Off by default, like the GitHub CLI.
185 186 187 |
# File 'lib/smily_cli/context.rb', line 185 def retries resolve("retry", env: %w[SMILY_RETRY]).to_i.clamp(0, 10) end |
#token ⇒ String?
The OAuth access token. Resolution order applies.
55 56 57 |
# File 'lib/smily_cli/context.rb', line 55 def token resolve("token", env: %w[SMILY_TOKEN BOOKINGSYNC_TOKEN SMILY_ACCESS_TOKEN]) end |
#token! ⇒ String
Like #token but raises when nothing is configured.
62 63 64 65 66 67 68 |
# File 'lib/smily_cli/context.rb', line 62 def token! token || raise(ConfigurationError, <<~MSG.strip) No API token configured. Provide one with --token, set SMILY_TOKEN, or run: smily config set token <ACCESS_TOKEN> You can obtain a token with `smily auth client-credentials` or `smily auth exchange`. MSG end |
#verbose? ⇒ Boolean
147 148 149 |
# File 'lib/smily_cli/context.rb', line 147 def verbose? truthy(["verbose"]) || truthy(ENV.fetch("SMILY_VERBOSE", nil)) end |