pike13-cli 0.1.4
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 +7 -0
- data/CHANGELOG.md +196 -0
- data/CONTRIBUTING.md +96 -0
- data/LICENSE.txt +21 -0
- data/README.md +896 -0
- data/bin/pike13 +6 -0
- data/completions/_pike13 +229 -0
- data/completions/pike13.bash +112 -0
- data/lib/pike13/cli/commands/account/business.rb +22 -0
- data/lib/pike13/cli/commands/account/confirmation.rb +21 -0
- data/lib/pike13/cli/commands/account/password.rb +21 -0
- data/lib/pike13/cli/commands/account/person.rb +22 -0
- data/lib/pike13/cli/commands/account.rb +33 -0
- data/lib/pike13/cli/commands/base.rb +350 -0
- data/lib/pike13/cli/commands/concerns/formattable_command.rb +48 -0
- data/lib/pike13/cli/commands/concerns/reporting_query.rb +59 -0
- data/lib/pike13/cli/commands/desk/appointment.rb +58 -0
- data/lib/pike13/cli/commands/desk/booking.rb +56 -0
- data/lib/pike13/cli/commands/desk/business.rb +20 -0
- data/lib/pike13/cli/commands/desk/custom_field.rb +27 -0
- data/lib/pike13/cli/commands/desk/event.rb +32 -0
- data/lib/pike13/cli/commands/desk/event_occurrence.rb +64 -0
- data/lib/pike13/cli/commands/desk/event_occurrence_note.rb +82 -0
- data/lib/pike13/cli/commands/desk/event_occurrence_visit.rb +26 -0
- data/lib/pike13/cli/commands/desk/event_occurrence_waitlist_entry.rb +26 -0
- data/lib/pike13/cli/commands/desk/form_of_payment.rb +75 -0
- data/lib/pike13/cli/commands/desk/invoice.rb +29 -0
- data/lib/pike13/cli/commands/desk/location.rb +29 -0
- data/lib/pike13/cli/commands/desk/make_up.rb +51 -0
- data/lib/pike13/cli/commands/desk/note.rb +71 -0
- data/lib/pike13/cli/commands/desk/pack.rb +20 -0
- data/lib/pike13/cli/commands/desk/pack_product.rb +101 -0
- data/lib/pike13/cli/commands/desk/payment.rb +44 -0
- data/lib/pike13/cli/commands/desk/person.rb +98 -0
- data/lib/pike13/cli/commands/desk/person_plan.rb +27 -0
- data/lib/pike13/cli/commands/desk/person_visit.rb +27 -0
- data/lib/pike13/cli/commands/desk/person_waitlist_entry.rb +27 -0
- data/lib/pike13/cli/commands/desk/person_waiver.rb +27 -0
- data/lib/pike13/cli/commands/desk/plan.rb +29 -0
- data/lib/pike13/cli/commands/desk/plan_product.rb +35 -0
- data/lib/pike13/cli/commands/desk/punch.rb +20 -0
- data/lib/pike13/cli/commands/desk/refund.rb +31 -0
- data/lib/pike13/cli/commands/desk/revenue_category.rb +36 -0
- data/lib/pike13/cli/commands/desk/sales_tax.rb +36 -0
- data/lib/pike13/cli/commands/desk/service.rb +29 -0
- data/lib/pike13/cli/commands/desk/staff_member.rb +35 -0
- data/lib/pike13/cli/commands/desk/visit.rb +36 -0
- data/lib/pike13/cli/commands/desk/waitlist_entry.rb +69 -0
- data/lib/pike13/cli/commands/desk.rb +172 -0
- data/lib/pike13/cli/commands/front/appointment.rb +31 -0
- data/lib/pike13/cli/commands/front/booking.rb +56 -0
- data/lib/pike13/cli/commands/front/branding.rb +17 -0
- data/lib/pike13/cli/commands/front/business.rb +17 -0
- data/lib/pike13/cli/commands/front/event.rb +22 -0
- data/lib/pike13/cli/commands/front/event_occurrence.rb +35 -0
- data/lib/pike13/cli/commands/front/event_occurrence_note.rb +35 -0
- data/lib/pike13/cli/commands/front/event_occurrence_waitlist_eligibility.rb +27 -0
- data/lib/pike13/cli/commands/front/form_of_payment.rb +83 -0
- data/lib/pike13/cli/commands/front/location.rb +22 -0
- data/lib/pike13/cli/commands/front/person.rb +17 -0
- data/lib/pike13/cli/commands/front/person_plan.rb +26 -0
- data/lib/pike13/cli/commands/front/person_visit.rb +26 -0
- data/lib/pike13/cli/commands/front/person_waitlist_entry.rb +26 -0
- data/lib/pike13/cli/commands/front/person_waiver.rb +26 -0
- data/lib/pike13/cli/commands/front/plan_product.rb +31 -0
- data/lib/pike13/cli/commands/front/plan_terms.rb +42 -0
- data/lib/pike13/cli/commands/front/service.rb +22 -0
- data/lib/pike13/cli/commands/front.rb +113 -0
- data/lib/pike13/cli/commands/report/clients.rb +27 -0
- data/lib/pike13/cli/commands/report/enrollments.rb +27 -0
- data/lib/pike13/cli/commands/report/event_occurrence_staff_members.rb +31 -0
- data/lib/pike13/cli/commands/report/event_occurrences.rb +31 -0
- data/lib/pike13/cli/commands/report/invoice_item_transactions.rb +31 -0
- data/lib/pike13/cli/commands/report/invoice_items.rb +31 -0
- data/lib/pike13/cli/commands/report/invoices.rb +27 -0
- data/lib/pike13/cli/commands/report/monthly_business_metrics.rb +31 -0
- data/lib/pike13/cli/commands/report/pays.rb +27 -0
- data/lib/pike13/cli/commands/report/person_plans.rb +31 -0
- data/lib/pike13/cli/commands/report/staff_members.rb +31 -0
- data/lib/pike13/cli/commands/report/transactions.rb +27 -0
- data/lib/pike13/cli/commands/report.rb +77 -0
- data/lib/pike13/cli/config.rb +26 -0
- data/lib/pike13/cli/formatter.rb +142 -0
- data/lib/pike13/cli/progress.rb +85 -0
- data/lib/pike13/cli/thor_nested_subcommand.rb +37 -0
- data/lib/pike13/cli/version.rb +7 -0
- data/lib/pike13/cli.rb +70 -0
- metadata +187 -0
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../thor_nested_subcommand"
|
|
4
|
+
|
|
5
|
+
module Pike13
|
|
6
|
+
module CLI
|
|
7
|
+
module Commands
|
|
8
|
+
class Base < Thor
|
|
9
|
+
include ThorNestedSubcommand
|
|
10
|
+
|
|
11
|
+
# Inherit verbose and quiet options from parent Runner
|
|
12
|
+
class_option :verbose, type: :boolean, aliases: "-v", desc: "Verbose output"
|
|
13
|
+
class_option :quiet, type: :boolean, aliases: "-q", desc: "Quiet mode (errors only)"
|
|
14
|
+
|
|
15
|
+
# Auto-generate base_usage from class name
|
|
16
|
+
def self.base_usage
|
|
17
|
+
# Convert class name like Pike13::CLI::Commands::Report::Clients
|
|
18
|
+
# to "report clients"
|
|
19
|
+
namespace_parts = name.split("::")[3..] # Skip Pike13::CLI::Commands
|
|
20
|
+
if namespace_parts && namespace_parts.length > 1
|
|
21
|
+
# For nested classes like Report::Clients
|
|
22
|
+
namespace_parts.map(&:downcase).join(" ")
|
|
23
|
+
elsif namespace_parts&.length == 1
|
|
24
|
+
# For top-level namespace classes
|
|
25
|
+
namespace_parts.first.downcase
|
|
26
|
+
else
|
|
27
|
+
""
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Helper to add format options to commands
|
|
32
|
+
def self.format_options
|
|
33
|
+
option :format, type: :string, default: "json", desc: "Output format (json, table, csv)"
|
|
34
|
+
option :compact, type: :boolean, default: false, desc: "Compact JSON output"
|
|
35
|
+
option :color, type: :boolean, default: false, desc: "Colorize table output"
|
|
36
|
+
option :progress, type: :boolean, default: false, desc: "Show progress indicator"
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
private
|
|
40
|
+
|
|
41
|
+
def output(data)
|
|
42
|
+
return if options[:quiet]
|
|
43
|
+
|
|
44
|
+
Formatter.output(
|
|
45
|
+
data,
|
|
46
|
+
format: options[:format],
|
|
47
|
+
compact: options[:compact],
|
|
48
|
+
color: options[:color]
|
|
49
|
+
)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def handle_error
|
|
53
|
+
setup_verbose_mode if options && options[:verbose]
|
|
54
|
+
yield
|
|
55
|
+
rescue Pike13::AuthenticationError => e
|
|
56
|
+
handle_authentication_error(e)
|
|
57
|
+
rescue Pike13::NotFoundError => e
|
|
58
|
+
handle_not_found_error(e)
|
|
59
|
+
rescue Pike13::ValidationError => e
|
|
60
|
+
handle_validation_error(e)
|
|
61
|
+
rescue Pike13::RateLimitError => e
|
|
62
|
+
handle_rate_limit_error(e)
|
|
63
|
+
rescue Pike13::ServerError => e
|
|
64
|
+
handle_server_error(e)
|
|
65
|
+
rescue Pike13::BadRequestError => e
|
|
66
|
+
handle_bad_request_error(e)
|
|
67
|
+
rescue Pike13::ConnectionError => e
|
|
68
|
+
handle_connection_error(e)
|
|
69
|
+
rescue Pike13::APIError => e
|
|
70
|
+
handle_generic_api_error(e)
|
|
71
|
+
rescue StandardError => e
|
|
72
|
+
handle_generic_error(e)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def with_progress(message = "Loading", &block)
|
|
76
|
+
require_relative "../progress"
|
|
77
|
+
Progress.run(message, enabled: options[:progress], &block)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def warn_message(msg)
|
|
81
|
+
if $stdout.tty?
|
|
82
|
+
require "colorize"
|
|
83
|
+
puts msg.red.bold
|
|
84
|
+
else
|
|
85
|
+
puts msg
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def success_message(msg)
|
|
90
|
+
return if options[:quiet]
|
|
91
|
+
|
|
92
|
+
if $stdout.tty?
|
|
93
|
+
require "colorize"
|
|
94
|
+
puts msg.green
|
|
95
|
+
else
|
|
96
|
+
puts msg
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def debug_message(msg)
|
|
101
|
+
return unless options[:verbose]
|
|
102
|
+
|
|
103
|
+
if $stderr.tty?
|
|
104
|
+
require "colorize"
|
|
105
|
+
warn "[DEBUG] #{msg}".light_black
|
|
106
|
+
else
|
|
107
|
+
warn "[DEBUG] #{msg}"
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def setup_verbose_mode
|
|
112
|
+
return unless options[:verbose]
|
|
113
|
+
|
|
114
|
+
# Enable HTTParty debugging
|
|
115
|
+
begin
|
|
116
|
+
Pike13::HTTPClient.debug_output($stderr) if Pike13::HTTPClient.respond_to?(:debug_output)
|
|
117
|
+
rescue StandardError
|
|
118
|
+
# Silently ignore if debug_output is not available
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
debug_message "Verbose mode enabled - detailed logging active"
|
|
122
|
+
debug_message "Pike13 Base URL: #{ENV.fetch('PIKE13_BASE_URL', nil)}"
|
|
123
|
+
debug_message "Access Token: #{ENV['PIKE13_ACCESS_TOKEN'] ? '[SET]' : '[NOT SET]'}"
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
# Validate that a required option is present
|
|
127
|
+
def validate_required(option_name)
|
|
128
|
+
unless options[option_name]
|
|
129
|
+
warn_message "Error: --#{option_name.to_s.gsub('_', '-')} is required"
|
|
130
|
+
exit 1
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# Validate date format (YYYY-MM-DD)
|
|
135
|
+
def validate_date_format(date_string, field_name = "date")
|
|
136
|
+
return if date_string.nil?
|
|
137
|
+
|
|
138
|
+
return if date_string =~ /^\d{4}-\d{2}-\d{2}$/
|
|
139
|
+
|
|
140
|
+
warn_message "Error: #{field_name} must be in YYYY-MM-DD format (got: #{date_string})"
|
|
141
|
+
exit 1
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
# Validate that an ID is numeric
|
|
145
|
+
def validate_numeric_id(id, resource_name = "resource")
|
|
146
|
+
return if id.to_s =~ /^\d+$/ || id.to_s =~ /^[a-f0-9-]{36}$/ # Allow UUIDs too
|
|
147
|
+
|
|
148
|
+
warn_message "Error: #{resource_name} ID must be numeric or a valid UUID (got: #{id})"
|
|
149
|
+
exit 1
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
# Error handlers with helpful messages
|
|
153
|
+
def handle_authentication_error(error)
|
|
154
|
+
warn_message "Authentication Error: Invalid or expired access token"
|
|
155
|
+
warn_message ""
|
|
156
|
+
warn_message "Suggestions:"
|
|
157
|
+
warn_message " 1. Verify your PIKE13_ACCESS_TOKEN environment variable is set correctly"
|
|
158
|
+
warn_message " 2. Check if your access token has expired"
|
|
159
|
+
warn_message " 3. Generate a new access token from your Pike13 account settings"
|
|
160
|
+
warn_message ""
|
|
161
|
+
warn_message "Current token: #{ENV['PIKE13_ACCESS_TOKEN'] ? "#{ENV['PIKE13_ACCESS_TOKEN'][0..10]}..." : '[NOT SET]'}"
|
|
162
|
+
show_verbose_error_details(error) if options && options[:verbose]
|
|
163
|
+
exit 1
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def handle_not_found_error(error)
|
|
167
|
+
warn_message "Not Found: The requested resource does not exist"
|
|
168
|
+
warn_message ""
|
|
169
|
+
warn_message "Suggestions:"
|
|
170
|
+
warn_message " 1. Verify the resource ID is correct"
|
|
171
|
+
warn_message " 2. Check if the resource has been deleted"
|
|
172
|
+
warn_message " 3. Ensure you have permission to access this resource"
|
|
173
|
+
show_verbose_error_details(error) if options && options[:verbose]
|
|
174
|
+
exit 1
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def handle_validation_error(error)
|
|
178
|
+
warn_message "Validation Error: The request contains invalid data"
|
|
179
|
+
warn_message ""
|
|
180
|
+
|
|
181
|
+
# Show main error message if available
|
|
182
|
+
if error.message && !error.message.empty?
|
|
183
|
+
formatted_message = format_error_message(error.message)
|
|
184
|
+
warn_message "Error details: #{formatted_message}"
|
|
185
|
+
warn_message ""
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
# Show structured response body errors if available
|
|
189
|
+
if error.response_body
|
|
190
|
+
details = format_validation_errors(error.response_body)
|
|
191
|
+
if details && !details.empty?
|
|
192
|
+
warn_message "API response: #{details}"
|
|
193
|
+
warn_message ""
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
# Show specific suggestions for common validation errors
|
|
198
|
+
suggestions = get_validation_error_suggestions(error.message)
|
|
199
|
+
warn_message "Suggestions:"
|
|
200
|
+
suggestions.each { |suggestion| warn_message " #{suggestion}" }
|
|
201
|
+
|
|
202
|
+
show_verbose_error_details(error) if options && options[:verbose]
|
|
203
|
+
exit 1
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
def handle_rate_limit_error(error)
|
|
207
|
+
warn_message "Rate Limit Exceeded: Too many requests"
|
|
208
|
+
warn_message ""
|
|
209
|
+
if error.respond_to?(:rate_limit_reset) && error.rate_limit_reset
|
|
210
|
+
warn_message "Rate limit will reset at: #{error.rate_limit_reset}"
|
|
211
|
+
warn_message ""
|
|
212
|
+
end
|
|
213
|
+
warn_message "Suggestions:"
|
|
214
|
+
warn_message " 1. Wait a few minutes before retrying"
|
|
215
|
+
warn_message " 2. Reduce the frequency of your requests"
|
|
216
|
+
warn_message " 3. Consider batching operations when possible"
|
|
217
|
+
show_verbose_error_details(error) if options && options[:verbose]
|
|
218
|
+
exit 1
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
def handle_server_error(error)
|
|
222
|
+
warn_message "Server Error: Pike13 API is experiencing issues"
|
|
223
|
+
warn_message ""
|
|
224
|
+
warn_message "HTTP Status: #{error.http_status}" if error.http_status
|
|
225
|
+
warn_message ""
|
|
226
|
+
warn_message "Suggestions:"
|
|
227
|
+
warn_message " 1. Wait a few minutes and try again"
|
|
228
|
+
warn_message " 2. Check Pike13 status page for service updates"
|
|
229
|
+
warn_message " 3. Contact Pike13 support if the issue persists"
|
|
230
|
+
show_verbose_error_details(error) if options && options[:verbose]
|
|
231
|
+
exit 1
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
def handle_bad_request_error(error)
|
|
235
|
+
warn_message "Bad Request: The request is malformed or invalid"
|
|
236
|
+
warn_message ""
|
|
237
|
+
if error.response_body
|
|
238
|
+
warn_message "Details: #{error.response_body}"
|
|
239
|
+
warn_message ""
|
|
240
|
+
end
|
|
241
|
+
warn_message "Suggestions:"
|
|
242
|
+
warn_message " 1. Check command syntax and options"
|
|
243
|
+
warn_message " 2. Verify all required parameters are provided"
|
|
244
|
+
warn_message " 3. Run with --verbose flag for more details"
|
|
245
|
+
show_verbose_error_details(error) if options && options[:verbose]
|
|
246
|
+
exit 1
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
def handle_connection_error(error)
|
|
250
|
+
warn_message "Connection Error: Unable to connect to Pike13 API"
|
|
251
|
+
warn_message ""
|
|
252
|
+
warn_message "Error: #{error.message}"
|
|
253
|
+
warn_message ""
|
|
254
|
+
warn_message "Suggestions:"
|
|
255
|
+
warn_message " 1. Check your internet connection"
|
|
256
|
+
warn_message " 2. Verify PIKE13_BASE_URL is set correctly: #{ENV.fetch('PIKE13_BASE_URL', nil)}"
|
|
257
|
+
warn_message " 3. Check if a firewall is blocking the connection"
|
|
258
|
+
warn_message " 4. Verify the Pike13 service is available"
|
|
259
|
+
show_verbose_error_details(error) if options && options[:verbose]
|
|
260
|
+
exit 1
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
def handle_generic_api_error(error)
|
|
264
|
+
warn_message "API Error: #{error.message}"
|
|
265
|
+
warn_message ""
|
|
266
|
+
warn_message "HTTP Status: #{error.http_status}" if error.http_status
|
|
267
|
+
warn_message ""
|
|
268
|
+
warn_message "Run with --verbose flag for more details"
|
|
269
|
+
show_verbose_error_details(error) if options && options[:verbose]
|
|
270
|
+
exit 1
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
def handle_generic_error(error)
|
|
274
|
+
warn_message "Error: #{error.message}"
|
|
275
|
+
warn_message ""
|
|
276
|
+
warn_message "Run with --verbose flag for more details"
|
|
277
|
+
show_verbose_error_details(error) if options && options[:verbose]
|
|
278
|
+
exit 1
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
def show_verbose_error_details(error)
|
|
282
|
+
warn_message ""
|
|
283
|
+
warn_message "=== Verbose Error Details ==="
|
|
284
|
+
warn_message "Error Class: #{error.class}"
|
|
285
|
+
warn_message "HTTP Status: #{error.http_status}" if error.respond_to?(:http_status)
|
|
286
|
+
if error.respond_to?(:response_body) && error.response_body
|
|
287
|
+
warn_message "Response Body: #{error.response_body}"
|
|
288
|
+
end
|
|
289
|
+
if error.backtrace
|
|
290
|
+
warn_message "Backtrace:"
|
|
291
|
+
error.backtrace.first(10).each { |line| warn_message " #{line}" }
|
|
292
|
+
end
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
def format_validation_errors(response_body)
|
|
296
|
+
case response_body
|
|
297
|
+
when Hash
|
|
298
|
+
if response_body["errors"]
|
|
299
|
+
response_body["errors"].is_a?(Array) ? response_body["errors"].join(", ") : response_body["errors"]
|
|
300
|
+
else
|
|
301
|
+
response_body.to_s
|
|
302
|
+
end
|
|
303
|
+
when String
|
|
304
|
+
response_body
|
|
305
|
+
else
|
|
306
|
+
response_body.to_s
|
|
307
|
+
end
|
|
308
|
+
end
|
|
309
|
+
|
|
310
|
+
def get_validation_error_suggestions(error_message)
|
|
311
|
+
suggestions = [
|
|
312
|
+
"Check that all required fields are provided",
|
|
313
|
+
"Verify field formats (dates, emails, etc.)",
|
|
314
|
+
"Review the Pike13 API documentation for field requirements"
|
|
315
|
+
]
|
|
316
|
+
|
|
317
|
+
# Add specific suggestions based on error message content
|
|
318
|
+
if error_message
|
|
319
|
+
if error_message.include?("active plans")
|
|
320
|
+
suggestions.unshift("Cancel or end all active plans before deleting this person")
|
|
321
|
+
suggestions.unshift("Use 'pike13 desk person_plans list --person-id=ID' to check for active plans")
|
|
322
|
+
elsif error_message.include?("bookings")
|
|
323
|
+
suggestions.unshift("Cancel or complete all active bookings before deletion")
|
|
324
|
+
elsif error_message.include?("payments")
|
|
325
|
+
suggestions.unshift("Ensure there are no pending payment transactions")
|
|
326
|
+
elsif error_message.include?("dependencies")
|
|
327
|
+
suggestions.unshift("Remove or resolve all dependencies before deletion")
|
|
328
|
+
end
|
|
329
|
+
end
|
|
330
|
+
|
|
331
|
+
suggestions
|
|
332
|
+
end
|
|
333
|
+
|
|
334
|
+
def format_error_message(message)
|
|
335
|
+
return nil if message.nil?
|
|
336
|
+
|
|
337
|
+
# Handle common error message formats
|
|
338
|
+
if message.include?("=>") && message.include?('"base"')
|
|
339
|
+
# Parse hash-like error messages: {"base" => ["Error message"]}
|
|
340
|
+
match = message.match(/\{"base"\s*=>\s*\[(.*?)\]\}/)
|
|
341
|
+
return match[1].gsub('"', "").strip if match
|
|
342
|
+
end
|
|
343
|
+
|
|
344
|
+
# Clean up common formatting issues
|
|
345
|
+
message.gsub(/^"|"$/, "").strip
|
|
346
|
+
end
|
|
347
|
+
end
|
|
348
|
+
end
|
|
349
|
+
end
|
|
350
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pike13
|
|
4
|
+
module CLI
|
|
5
|
+
module Commands
|
|
6
|
+
module Concerns
|
|
7
|
+
# Module to add formatting capabilities to Thor commands
|
|
8
|
+
# Include this in command classes and call `add_format_options` on methods
|
|
9
|
+
module FormattableCommand
|
|
10
|
+
def self.included(base)
|
|
11
|
+
base.extend(ClassMethods)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
module ClassMethods
|
|
15
|
+
# Adds format and compact options to a command
|
|
16
|
+
# Usage: add_format_options before defining the command method
|
|
17
|
+
def add_format_options
|
|
18
|
+
option :format, type: :string, default: "json", desc: "Output format (json, table, csv)"
|
|
19
|
+
option :compact, type: :boolean, default: false, desc: "Compact JSON output"
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
# Output data using the formatter with current options
|
|
26
|
+
def output(data)
|
|
27
|
+
Pike13::CLI::Formatter.output(
|
|
28
|
+
data,
|
|
29
|
+
format: options[:format],
|
|
30
|
+
compact: options[:compact]
|
|
31
|
+
)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Error handling wrapper
|
|
35
|
+
def handle_error
|
|
36
|
+
yield
|
|
37
|
+
rescue Pike13::APIError => e
|
|
38
|
+
puts "API Error: #{e.message}"
|
|
39
|
+
exit 1
|
|
40
|
+
rescue StandardError => e
|
|
41
|
+
puts "Error: #{e.message}"
|
|
42
|
+
exit 1
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pike13
|
|
4
|
+
module CLI
|
|
5
|
+
module Commands
|
|
6
|
+
module Concerns
|
|
7
|
+
module ReportingQuery
|
|
8
|
+
def self.included(base)
|
|
9
|
+
base.class_eval do
|
|
10
|
+
format_options
|
|
11
|
+
option :fields, type: :array, desc: "Fields to include (defaults to basic fields)"
|
|
12
|
+
option :filter, type: :hash, desc: "Filter criteria"
|
|
13
|
+
option :group, type: :string, desc: "Group by field"
|
|
14
|
+
option :sort, type: :array, desc: "Sort fields"
|
|
15
|
+
option :page, type: :hash, desc: "Pagination options"
|
|
16
|
+
option :total_count, type: :boolean, desc: "Include total count in response"
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
# rubocop:disable Metrics/AbcSize
|
|
23
|
+
def build_query_params
|
|
24
|
+
params = {}
|
|
25
|
+
|
|
26
|
+
# Handle fields - support comma-separated strings and arrays
|
|
27
|
+
if options[:fields]
|
|
28
|
+
params[:fields] = parse_array_option(options[:fields])
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
params[:filter] = options[:filter] if options[:filter]
|
|
32
|
+
params[:group] = options[:group] if options[:group]
|
|
33
|
+
|
|
34
|
+
# Handle sort - support comma-separated strings and arrays
|
|
35
|
+
if options[:sort]
|
|
36
|
+
params[:sort] = parse_array_option(options[:sort])
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
params[:page] = options[:page] if options[:page]
|
|
40
|
+
params[:total_count] = options[:total_count] if options[:total_count]
|
|
41
|
+
params
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Parse option that can be either a comma-separated string or an array
|
|
45
|
+
def parse_array_option(option)
|
|
46
|
+
if option.is_a?(Array) && option.size == 1 && option.first.include?(",")
|
|
47
|
+
option.first.split(",").map(&:strip)
|
|
48
|
+
elsif option.is_a?(String) && option.include?(",")
|
|
49
|
+
option.split(",").map(&:strip)
|
|
50
|
+
else
|
|
51
|
+
option
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
# rubocop:enable Metrics/AbcSize
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pike13
|
|
4
|
+
module CLI
|
|
5
|
+
module Commands
|
|
6
|
+
class Desk < Base
|
|
7
|
+
class Appointment < Base
|
|
8
|
+
desc "available SERVICE_ID", "Find available appointment slots for a service"
|
|
9
|
+
format_options
|
|
10
|
+
option :date, type: :string, required: true, desc: "Date to check (YYYY-MM-DD)"
|
|
11
|
+
option :location_ids, type: :array, desc: "Location IDs to filter by"
|
|
12
|
+
option :staff_member_ids, type: :array, desc: "Staff member IDs to filter by"
|
|
13
|
+
def available(service_id)
|
|
14
|
+
# Validate date format
|
|
15
|
+
validate_date_format(options[:date], "date")
|
|
16
|
+
|
|
17
|
+
handle_error do
|
|
18
|
+
params = { date: options[:date] }
|
|
19
|
+
params[:location_ids] = options[:location_ids] if options[:location_ids]
|
|
20
|
+
params[:staff_member_ids] = options[:staff_member_ids] if options[:staff_member_ids]
|
|
21
|
+
|
|
22
|
+
result = with_progress("Fetching available slots") do
|
|
23
|
+
Pike13::Desk::Appointment.find_available_slots(service_id: service_id, **params)
|
|
24
|
+
end
|
|
25
|
+
output(result)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
desc "summary SERVICE_ID", "Get appointment availability summary for a service"
|
|
30
|
+
format_options
|
|
31
|
+
option :from, type: :string, required: true, desc: "Start date (YYYY-MM-DD)"
|
|
32
|
+
option :to, type: :string, required: true, desc: "End date (YYYY-MM-DD)"
|
|
33
|
+
option :location_ids, type: :array, desc: "Location IDs to filter by"
|
|
34
|
+
option :staff_member_ids, type: :array, desc: "Staff member IDs to filter by"
|
|
35
|
+
def summary(service_id)
|
|
36
|
+
# Validate date formats
|
|
37
|
+
validate_date_format(options[:from], "from")
|
|
38
|
+
validate_date_format(options[:to], "to")
|
|
39
|
+
|
|
40
|
+
handle_error do
|
|
41
|
+
params = {
|
|
42
|
+
from: options[:from],
|
|
43
|
+
to: options[:to]
|
|
44
|
+
}
|
|
45
|
+
params[:location_ids] = options[:location_ids] if options[:location_ids]
|
|
46
|
+
params[:staff_member_ids] = options[:staff_member_ids] if options[:staff_member_ids]
|
|
47
|
+
|
|
48
|
+
result = with_progress("Fetching availability summary") do
|
|
49
|
+
Pike13::Desk::Appointment.available_slots_summary(service_id: service_id, **params)
|
|
50
|
+
end
|
|
51
|
+
output(result)
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pike13
|
|
4
|
+
module CLI
|
|
5
|
+
module Commands
|
|
6
|
+
class Desk < Base
|
|
7
|
+
class Booking < Base
|
|
8
|
+
desc "get ID", "Get a booking by ID"
|
|
9
|
+
format_options
|
|
10
|
+
def get(id)
|
|
11
|
+
handle_error do
|
|
12
|
+
result = Pike13::Desk::Booking.find(id)
|
|
13
|
+
output(result)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
desc "create", "Create a booking"
|
|
18
|
+
format_options
|
|
19
|
+
option :event_occurrence_id, type: :numeric, required: true
|
|
20
|
+
option :person_id, type: :numeric, required: true
|
|
21
|
+
def create
|
|
22
|
+
handle_error do
|
|
23
|
+
require "securerandom"
|
|
24
|
+
result = Pike13::Desk::Booking.create(
|
|
25
|
+
event_occurrence_id: options[:event_occurrence_id],
|
|
26
|
+
person_id: options[:person_id],
|
|
27
|
+
idempotency_token: SecureRandom.uuid
|
|
28
|
+
)
|
|
29
|
+
output(result)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
desc "update ID", "Update a booking"
|
|
34
|
+
format_options
|
|
35
|
+
option :state, type: :string, desc: "Booking state"
|
|
36
|
+
def update(id)
|
|
37
|
+
handle_error do
|
|
38
|
+
attributes = {}
|
|
39
|
+
attributes[:state] = options[:state] if options[:state]
|
|
40
|
+
result = Pike13::Desk::Booking.update(id, attributes)
|
|
41
|
+
output(result)
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
desc "delete ID", "Delete a booking"
|
|
46
|
+
def delete(id)
|
|
47
|
+
handle_error do
|
|
48
|
+
Pike13::Desk::Booking.destroy(id)
|
|
49
|
+
success_message "Booking #{id} deleted successfully"
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pike13
|
|
4
|
+
module CLI
|
|
5
|
+
module Commands
|
|
6
|
+
class Desk < Base
|
|
7
|
+
class Business < Base
|
|
8
|
+
desc "find", "Get business details"
|
|
9
|
+
format_options
|
|
10
|
+
def find
|
|
11
|
+
handle_error do
|
|
12
|
+
result = Pike13::Desk::Business.find
|
|
13
|
+
output(result)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pike13
|
|
4
|
+
module CLI
|
|
5
|
+
module Commands
|
|
6
|
+
class Desk < Base
|
|
7
|
+
class CustomField < Base
|
|
8
|
+
# Override base_usage to match the actual subcommand registration
|
|
9
|
+
def self.base_usage
|
|
10
|
+
"desk custom_fields"
|
|
11
|
+
end
|
|
12
|
+
desc "list", "List all custom fields"
|
|
13
|
+
map "ls" => :list
|
|
14
|
+
format_options
|
|
15
|
+
def list
|
|
16
|
+
handle_error do
|
|
17
|
+
result = with_progress("Fetching custom fields") do
|
|
18
|
+
Pike13::Desk::CustomField.all
|
|
19
|
+
end
|
|
20
|
+
output(result)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pike13
|
|
4
|
+
module CLI
|
|
5
|
+
module Commands
|
|
6
|
+
class Desk < Base
|
|
7
|
+
class Event < Base
|
|
8
|
+
desc "list", "List all events"
|
|
9
|
+
map "ls" => :list
|
|
10
|
+
format_options
|
|
11
|
+
def list
|
|
12
|
+
handle_error do
|
|
13
|
+
result = with_progress("Fetching events") do
|
|
14
|
+
Pike13::Desk::Event.all
|
|
15
|
+
end
|
|
16
|
+
output(result)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
desc "get ID", "Get an event by ID"
|
|
21
|
+
format_options
|
|
22
|
+
def get(id)
|
|
23
|
+
handle_error do
|
|
24
|
+
result = Pike13::Desk::Event.find(id)
|
|
25
|
+
output(result)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|