square_sandbox_simulator 0.1.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 +7 -0
- data/LICENSE.txt +21 -0
- data/README.md +176 -0
- data/bin/simulate +388 -0
- data/lib/square_sandbox_simulator/configuration.rb +193 -0
- data/lib/square_sandbox_simulator/data/cafe_bakery/categories.json +54 -0
- data/lib/square_sandbox_simulator/data/cafe_bakery/combos.json +33 -0
- data/lib/square_sandbox_simulator/data/cafe_bakery/coupon_codes.json +133 -0
- data/lib/square_sandbox_simulator/data/cafe_bakery/discounts.json +113 -0
- data/lib/square_sandbox_simulator/data/cafe_bakery/items.json +55 -0
- data/lib/square_sandbox_simulator/data/cafe_bakery/modifiers.json +73 -0
- data/lib/square_sandbox_simulator/data/cafe_bakery/tax_rates.json +26 -0
- data/lib/square_sandbox_simulator/data/cafe_bakery/tenders.json +41 -0
- data/lib/square_sandbox_simulator/data/restaurant/categories.json +54 -0
- data/lib/square_sandbox_simulator/data/restaurant/combos.json +265 -0
- data/lib/square_sandbox_simulator/data/restaurant/coupon_codes.json +266 -0
- data/lib/square_sandbox_simulator/data/restaurant/discounts.json +198 -0
- data/lib/square_sandbox_simulator/data/restaurant/gift_cards.json +82 -0
- data/lib/square_sandbox_simulator/data/restaurant/items.json +388 -0
- data/lib/square_sandbox_simulator/data/restaurant/modifiers.json +62 -0
- data/lib/square_sandbox_simulator/data/restaurant/tax_rates.json +38 -0
- data/lib/square_sandbox_simulator/data/restaurant/tenders.json +41 -0
- data/lib/square_sandbox_simulator/data/salon_spa/categories.json +24 -0
- data/lib/square_sandbox_simulator/data/salon_spa/combos.json +88 -0
- data/lib/square_sandbox_simulator/data/salon_spa/coupon_codes.json +96 -0
- data/lib/square_sandbox_simulator/data/salon_spa/discounts.json +93 -0
- data/lib/square_sandbox_simulator/data/salon_spa/gift_cards.json +47 -0
- data/lib/square_sandbox_simulator/data/salon_spa/items.json +100 -0
- data/lib/square_sandbox_simulator/data/salon_spa/modifiers.json +49 -0
- data/lib/square_sandbox_simulator/data/salon_spa/tax_rates.json +17 -0
- data/lib/square_sandbox_simulator/data/salon_spa/tenders.json +41 -0
- data/lib/square_sandbox_simulator/database.rb +224 -0
- data/lib/square_sandbox_simulator/db/factories/api_requests.rb +95 -0
- data/lib/square_sandbox_simulator/db/factories/business_types.rb +178 -0
- data/lib/square_sandbox_simulator/db/factories/categories.rb +379 -0
- data/lib/square_sandbox_simulator/db/factories/daily_summaries.rb +56 -0
- data/lib/square_sandbox_simulator/db/factories/items.rb +1526 -0
- data/lib/square_sandbox_simulator/db/factories/simulated_orders.rb +112 -0
- data/lib/square_sandbox_simulator/db/factories/simulated_payments.rb +61 -0
- data/lib/square_sandbox_simulator/db/migrate/20260312000000_enable_pgcrypto.rb +7 -0
- data/lib/square_sandbox_simulator/db/migrate/20260312000001_create_business_types.rb +18 -0
- data/lib/square_sandbox_simulator/db/migrate/20260312000002_create_categories.rb +18 -0
- data/lib/square_sandbox_simulator/db/migrate/20260312000003_create_items.rb +23 -0
- data/lib/square_sandbox_simulator/db/migrate/20260312000004_create_simulated_orders.rb +36 -0
- data/lib/square_sandbox_simulator/db/migrate/20260312000005_create_simulated_payments.rb +26 -0
- data/lib/square_sandbox_simulator/db/migrate/20260312000006_create_api_requests.rb +27 -0
- data/lib/square_sandbox_simulator/db/migrate/20260312000007_create_daily_summaries.rb +24 -0
- data/lib/square_sandbox_simulator/generators/data_loader.rb +202 -0
- data/lib/square_sandbox_simulator/generators/entity_generator.rb +248 -0
- data/lib/square_sandbox_simulator/generators/order_generator.rb +632 -0
- data/lib/square_sandbox_simulator/models/api_request.rb +43 -0
- data/lib/square_sandbox_simulator/models/business_type.rb +25 -0
- data/lib/square_sandbox_simulator/models/category.rb +18 -0
- data/lib/square_sandbox_simulator/models/daily_summary.rb +68 -0
- data/lib/square_sandbox_simulator/models/item.rb +33 -0
- data/lib/square_sandbox_simulator/models/record.rb +16 -0
- data/lib/square_sandbox_simulator/models/simulated_order.rb +42 -0
- data/lib/square_sandbox_simulator/models/simulated_payment.rb +28 -0
- data/lib/square_sandbox_simulator/seeder.rb +242 -0
- data/lib/square_sandbox_simulator/services/base_service.rb +253 -0
- data/lib/square_sandbox_simulator/services/square/catalog_service.rb +203 -0
- data/lib/square_sandbox_simulator/services/square/customer_service.rb +130 -0
- data/lib/square_sandbox_simulator/services/square/order_service.rb +121 -0
- data/lib/square_sandbox_simulator/services/square/payment_service.rb +136 -0
- data/lib/square_sandbox_simulator/services/square/services_manager.rb +68 -0
- data/lib/square_sandbox_simulator/services/square/team_service.rb +108 -0
- data/lib/square_sandbox_simulator/version.rb +5 -0
- data/lib/square_sandbox_simulator.rb +47 -0
- metadata +348 -0
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SquareSandboxSimulator
|
|
4
|
+
module Services
|
|
5
|
+
module Square
|
|
6
|
+
# Manages Square orders
|
|
7
|
+
class OrderService < BaseService
|
|
8
|
+
# Create an order
|
|
9
|
+
# @param line_items [Array<Hash>] Array of line item hashes
|
|
10
|
+
# @param discounts [Array<Hash>] Array of discount hashes
|
|
11
|
+
# @param taxes [Array<Hash>] Array of tax hashes
|
|
12
|
+
# @param customer_id [String, nil] Customer ID to associate
|
|
13
|
+
# @return [Hash, nil] Order response
|
|
14
|
+
def create_order(line_items:, discounts: [], taxes: [], customer_id: nil)
|
|
15
|
+
logger.info "Creating order with #{line_items.size} line items..."
|
|
16
|
+
|
|
17
|
+
order_data = {
|
|
18
|
+
"location_id" => config.location_id,
|
|
19
|
+
"line_items" => line_items,
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
order_data["discounts"] = discounts if discounts.any?
|
|
23
|
+
order_data["taxes"] = taxes if taxes.any?
|
|
24
|
+
order_data["customer_id"] = customer_id if customer_id
|
|
25
|
+
|
|
26
|
+
payload = { "order" => order_data }
|
|
27
|
+
|
|
28
|
+
response = request(:post, "orders",
|
|
29
|
+
payload: payload,
|
|
30
|
+
resource_type: "Order")
|
|
31
|
+
|
|
32
|
+
order = response&.dig("order")
|
|
33
|
+
logger.info "Order created: #{order["id"]}" if order
|
|
34
|
+
order
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Get a single order (Square uses batch-retrieve for single orders)
|
|
38
|
+
# @param order_id [String] The order ID
|
|
39
|
+
# @return [Hash, nil] Order response
|
|
40
|
+
def get_order(order_id)
|
|
41
|
+
logger.info "Fetching order: #{order_id}"
|
|
42
|
+
|
|
43
|
+
payload = {
|
|
44
|
+
"order_ids" => [order_id],
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
response = request(:post, "orders/batch-retrieve",
|
|
48
|
+
payload: payload,
|
|
49
|
+
resource_type: "Order",
|
|
50
|
+
resource_id: order_id)
|
|
51
|
+
|
|
52
|
+
orders = response&.dig("orders") || []
|
|
53
|
+
orders.first
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Search orders with filters
|
|
57
|
+
# @param filters [Hash] Search filters
|
|
58
|
+
# @return [Array<Hash>] Matching orders
|
|
59
|
+
def search_orders(filters: {})
|
|
60
|
+
logger.info "Searching orders..."
|
|
61
|
+
|
|
62
|
+
payload = {
|
|
63
|
+
"location_ids" => [config.location_id],
|
|
64
|
+
}
|
|
65
|
+
payload["query"] = { "filter" => filters } if filters.any?
|
|
66
|
+
|
|
67
|
+
response = request(:post, "orders/search",
|
|
68
|
+
payload: payload,
|
|
69
|
+
resource_type: "Order")
|
|
70
|
+
|
|
71
|
+
orders = response&.dig("orders") || []
|
|
72
|
+
logger.info "Found #{orders.size} orders"
|
|
73
|
+
orders
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Update an order
|
|
77
|
+
# @param order_id [String] The order ID
|
|
78
|
+
# @param fields [Hash] Fields to update on the order object
|
|
79
|
+
# @param version [Integer] Current order version for optimistic concurrency
|
|
80
|
+
# @return [Hash, nil] Updated order
|
|
81
|
+
def update_order(order_id, fields:, version:)
|
|
82
|
+
logger.info "Updating order: #{order_id}"
|
|
83
|
+
|
|
84
|
+
order_data = fields.merge(
|
|
85
|
+
"location_id" => config.location_id,
|
|
86
|
+
"version" => version,
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
payload = { "order" => order_data }
|
|
90
|
+
|
|
91
|
+
response = request(:put, "orders/#{order_id}",
|
|
92
|
+
payload: payload,
|
|
93
|
+
resource_type: "Order",
|
|
94
|
+
resource_id: order_id)
|
|
95
|
+
|
|
96
|
+
response&.dig("order")
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# Calculate order totals without creating
|
|
100
|
+
# @param line_items [Array<Hash>] Array of line item hashes
|
|
101
|
+
# @return [Hash, nil] Calculated order
|
|
102
|
+
def calculate_order(line_items:)
|
|
103
|
+
logger.info "Calculating order..."
|
|
104
|
+
|
|
105
|
+
payload = {
|
|
106
|
+
"order" => {
|
|
107
|
+
"location_id" => config.location_id,
|
|
108
|
+
"line_items" => line_items,
|
|
109
|
+
},
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
response = request(:post, "orders/calculate",
|
|
113
|
+
payload: payload,
|
|
114
|
+
resource_type: "Order")
|
|
115
|
+
|
|
116
|
+
response&.dig("order")
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
end
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SquareSandboxSimulator
|
|
4
|
+
module Services
|
|
5
|
+
module Square
|
|
6
|
+
# Manages Square payments and refunds
|
|
7
|
+
class PaymentService < BaseService
|
|
8
|
+
# Create a payment
|
|
9
|
+
# @param order_id [String] The order ID
|
|
10
|
+
# @param amount [Integer] Amount in cents
|
|
11
|
+
# @param source_id [String] Payment source (default: sandbox card nonce)
|
|
12
|
+
# @param tip_amount [Integer] Tip amount in cents
|
|
13
|
+
# @return [Hash, nil] Payment response
|
|
14
|
+
def create_payment(order_id:, amount:, source_id: "cnon:card-nonce-ok", tip_amount: 0)
|
|
15
|
+
logger.info "Creating payment for order #{order_id}: $#{amount / 100.0}"
|
|
16
|
+
|
|
17
|
+
payload = {
|
|
18
|
+
"source_id" => source_id,
|
|
19
|
+
"amount_money" => {
|
|
20
|
+
"amount" => amount,
|
|
21
|
+
"currency" => "USD",
|
|
22
|
+
},
|
|
23
|
+
"order_id" => order_id,
|
|
24
|
+
"location_id" => config.location_id,
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if tip_amount.positive?
|
|
28
|
+
payload["tip_money"] = {
|
|
29
|
+
"amount" => tip_amount,
|
|
30
|
+
"currency" => "USD",
|
|
31
|
+
}
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
response = request(:post, "payments",
|
|
35
|
+
payload: payload,
|
|
36
|
+
resource_type: "Payment")
|
|
37
|
+
|
|
38
|
+
payment = response&.dig("payment")
|
|
39
|
+
logger.info "Payment created: #{payment["id"]} (status: #{payment["status"]})" if payment
|
|
40
|
+
|
|
41
|
+
payment
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Create a cash payment using external payment source
|
|
45
|
+
# @param order_id [String] The order ID
|
|
46
|
+
# @param amount [Integer] Amount in cents
|
|
47
|
+
# @return [Hash, nil] Payment response
|
|
48
|
+
def create_cash_payment(order_id:, amount:)
|
|
49
|
+
logger.info "Creating cash payment for order #{order_id}: $#{amount / 100.0}"
|
|
50
|
+
|
|
51
|
+
payload = {
|
|
52
|
+
"source_id" => "EXTERNAL",
|
|
53
|
+
"external_details" => {
|
|
54
|
+
"type" => "CASH",
|
|
55
|
+
"source" => "Cash Register",
|
|
56
|
+
},
|
|
57
|
+
"amount_money" => {
|
|
58
|
+
"amount" => amount,
|
|
59
|
+
"currency" => "USD",
|
|
60
|
+
},
|
|
61
|
+
"order_id" => order_id,
|
|
62
|
+
"location_id" => config.location_id,
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
response = request(:post, "payments",
|
|
66
|
+
payload: payload,
|
|
67
|
+
resource_type: "Payment")
|
|
68
|
+
|
|
69
|
+
payment = response&.dig("payment")
|
|
70
|
+
logger.info "Cash payment created: #{payment["id"]}" if payment
|
|
71
|
+
|
|
72
|
+
payment
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Get a single payment
|
|
76
|
+
# @param payment_id [String] The payment ID
|
|
77
|
+
# @return [Hash, nil] Payment response
|
|
78
|
+
def get_payment(payment_id)
|
|
79
|
+
logger.info "Fetching payment: #{payment_id}"
|
|
80
|
+
response = request(:get, "payments/#{payment_id}",
|
|
81
|
+
resource_type: "Payment",
|
|
82
|
+
resource_id: payment_id)
|
|
83
|
+
response&.dig("payment")
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# List payments
|
|
87
|
+
# @return [Array<Hash>] Array of payments
|
|
88
|
+
def list_payments
|
|
89
|
+
logger.info "Listing payments..."
|
|
90
|
+
response = request(:get, "payments")
|
|
91
|
+
payments = response&.dig("payments") || []
|
|
92
|
+
logger.info "Found #{payments.size} payments"
|
|
93
|
+
payments
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# Refund a payment
|
|
97
|
+
# @param payment_id [String] The payment ID to refund
|
|
98
|
+
# @param amount [Integer] Amount to refund in cents
|
|
99
|
+
# @param reason [String] Reason for refund
|
|
100
|
+
# @return [Hash, nil] Refund response
|
|
101
|
+
def refund_payment(payment_id:, amount:, reason:)
|
|
102
|
+
logger.info "Refunding payment #{payment_id}: $#{amount / 100.0} (#{reason})"
|
|
103
|
+
|
|
104
|
+
payload = {
|
|
105
|
+
"payment_id" => payment_id,
|
|
106
|
+
"amount_money" => {
|
|
107
|
+
"amount" => amount,
|
|
108
|
+
"currency" => "USD",
|
|
109
|
+
},
|
|
110
|
+
"reason" => reason,
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
response = request(:post, "refunds",
|
|
114
|
+
payload: payload,
|
|
115
|
+
resource_type: "Refund")
|
|
116
|
+
|
|
117
|
+
refund = response&.dig("refund")
|
|
118
|
+
logger.info "Refund created: #{refund["id"]} (status: #{refund["status"]})" if refund
|
|
119
|
+
|
|
120
|
+
refund
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# Get a single refund
|
|
124
|
+
# @param refund_id [String] The refund ID
|
|
125
|
+
# @return [Hash, nil] Refund response
|
|
126
|
+
def get_refund(refund_id)
|
|
127
|
+
logger.info "Fetching refund: #{refund_id}"
|
|
128
|
+
response = request(:get, "refunds/#{refund_id}",
|
|
129
|
+
resource_type: "Refund",
|
|
130
|
+
resource_id: refund_id)
|
|
131
|
+
response&.dig("refund")
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
end
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SquareSandboxSimulator
|
|
4
|
+
module Services
|
|
5
|
+
module Square
|
|
6
|
+
# Central manager for all Square services
|
|
7
|
+
# Provides thread-safe, lazy-loaded access to all service classes
|
|
8
|
+
class ServicesManager
|
|
9
|
+
attr_reader :config
|
|
10
|
+
|
|
11
|
+
def initialize(config: nil)
|
|
12
|
+
@config = config || SquareSandboxSimulator.configuration
|
|
13
|
+
@mutex = Mutex.new
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def catalog
|
|
17
|
+
thread_safe_memoize(:@catalog) { CatalogService.new(config: config) }
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def order
|
|
21
|
+
thread_safe_memoize(:@order) { OrderService.new(config: config) }
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def payment
|
|
25
|
+
thread_safe_memoize(:@payment) { PaymentService.new(config: config) }
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def customer
|
|
29
|
+
thread_safe_memoize(:@customer) { CustomerService.new(config: config) }
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def team
|
|
33
|
+
thread_safe_memoize(:@team) { TeamService.new(config: config) }
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Clear all cached service instances
|
|
37
|
+
def clear_services
|
|
38
|
+
@mutex.synchronize do
|
|
39
|
+
instance_variables.each do |var|
|
|
40
|
+
next if %i[@config @mutex].include?(var)
|
|
41
|
+
|
|
42
|
+
instance_variable_set(var, nil)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
private
|
|
48
|
+
|
|
49
|
+
# Thread-safe memoization pattern
|
|
50
|
+
def thread_safe_memoize(ivar_name)
|
|
51
|
+
# Fast path: return if already set
|
|
52
|
+
value = instance_variable_get(ivar_name)
|
|
53
|
+
return value if value
|
|
54
|
+
|
|
55
|
+
# Slow path: synchronize and check again
|
|
56
|
+
@mutex.synchronize do
|
|
57
|
+
value = instance_variable_get(ivar_name)
|
|
58
|
+
return value if value
|
|
59
|
+
|
|
60
|
+
value = yield
|
|
61
|
+
instance_variable_set(ivar_name, value)
|
|
62
|
+
value
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "faker"
|
|
4
|
+
|
|
5
|
+
module SquareSandboxSimulator
|
|
6
|
+
module Services
|
|
7
|
+
module Square
|
|
8
|
+
# Manages Square team members
|
|
9
|
+
class TeamService < BaseService
|
|
10
|
+
# Create a team member
|
|
11
|
+
# @param given_name [String] First name
|
|
12
|
+
# @param family_name [String] Last name
|
|
13
|
+
# @param email [String, nil] Email address
|
|
14
|
+
# @return [Hash, nil] Team member response
|
|
15
|
+
def create_team_member(given_name:, family_name:, email: nil)
|
|
16
|
+
logger.info "Creating team member: #{given_name} #{family_name}"
|
|
17
|
+
|
|
18
|
+
team_member_data = {
|
|
19
|
+
"given_name" => given_name,
|
|
20
|
+
"family_name" => family_name,
|
|
21
|
+
}
|
|
22
|
+
team_member_data["email_address"] = email if email
|
|
23
|
+
|
|
24
|
+
# Assign to the configured location
|
|
25
|
+
payload = {
|
|
26
|
+
"team_member" => team_member_data,
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
response = request(:post, "team-members",
|
|
30
|
+
payload: payload,
|
|
31
|
+
resource_type: "TeamMember")
|
|
32
|
+
|
|
33
|
+
member = response&.dig("team_member")
|
|
34
|
+
logger.info "Team member created: #{member["id"]}" if member
|
|
35
|
+
member
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Search team members
|
|
39
|
+
# @return [Array<Hash>] Array of team members
|
|
40
|
+
def search_team_members
|
|
41
|
+
logger.info "Searching team members..."
|
|
42
|
+
|
|
43
|
+
payload = {
|
|
44
|
+
"query" => {
|
|
45
|
+
"filter" => {
|
|
46
|
+
"location_ids" => [config.location_id],
|
|
47
|
+
"status" => "ACTIVE",
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
response = request(:post, "team-members/search",
|
|
53
|
+
payload: payload,
|
|
54
|
+
resource_type: "TeamMember")
|
|
55
|
+
|
|
56
|
+
members = response&.dig("team_members") || []
|
|
57
|
+
logger.info "Found #{members.size} team members"
|
|
58
|
+
members
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Default team member data for deterministic setup
|
|
62
|
+
DEFAULT_TEAM_MEMBERS = [
|
|
63
|
+
{ given: "Alex", family: "Manager" },
|
|
64
|
+
{ given: "Jordan", family: "Server" },
|
|
65
|
+
{ given: "Casey", family: "Cook" },
|
|
66
|
+
{ given: "Riley", family: "Host" },
|
|
67
|
+
{ given: "Morgan", family: "Bartender" },
|
|
68
|
+
].freeze
|
|
69
|
+
|
|
70
|
+
# Create sample team members if needed (idempotent)
|
|
71
|
+
# @param count [Integer] Minimum number of team members to ensure exist
|
|
72
|
+
# @return [Array<Hash>] All team members
|
|
73
|
+
def ensure_team_members(count: 5)
|
|
74
|
+
existing = search_team_members
|
|
75
|
+
return existing if existing.size >= count
|
|
76
|
+
|
|
77
|
+
needed = count - existing.size
|
|
78
|
+
logger.info "Creating #{needed} sample team members..."
|
|
79
|
+
|
|
80
|
+
new_members = []
|
|
81
|
+
needed.times do |i|
|
|
82
|
+
idx = existing.size + i
|
|
83
|
+
if idx < DEFAULT_TEAM_MEMBERS.size
|
|
84
|
+
member_data = DEFAULT_TEAM_MEMBERS[idx]
|
|
85
|
+
given = member_data[:given]
|
|
86
|
+
family = member_data[:family]
|
|
87
|
+
else
|
|
88
|
+
given = Faker::Name.first_name
|
|
89
|
+
family = Faker::Name.last_name
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
safe_given = given.downcase.gsub(/[^a-z0-9]/, "")
|
|
93
|
+
safe_family = family.downcase.gsub(/[^a-z0-9]/, "")
|
|
94
|
+
|
|
95
|
+
member = create_team_member(
|
|
96
|
+
given_name: given,
|
|
97
|
+
family_name: family,
|
|
98
|
+
email: "#{safe_given}.#{safe_family}@example.com",
|
|
99
|
+
)
|
|
100
|
+
new_members << member if member
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
existing + new_members
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "zeitwerk"
|
|
4
|
+
require "logger"
|
|
5
|
+
require "json"
|
|
6
|
+
require "rest-client"
|
|
7
|
+
require "dotenv"
|
|
8
|
+
|
|
9
|
+
# Load environment variables
|
|
10
|
+
Dotenv.load
|
|
11
|
+
|
|
12
|
+
module SquareSandboxSimulator
|
|
13
|
+
VERSION = "0.1.0"
|
|
14
|
+
|
|
15
|
+
class Error < StandardError; end
|
|
16
|
+
class ConfigurationError < Error; end
|
|
17
|
+
class ApiError < Error; end
|
|
18
|
+
|
|
19
|
+
class << self
|
|
20
|
+
attr_writer :configuration
|
|
21
|
+
|
|
22
|
+
def configuration
|
|
23
|
+
@configuration ||= Configuration.new
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def configure
|
|
27
|
+
yield(configuration)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def logger
|
|
31
|
+
configuration.logger
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def root
|
|
35
|
+
File.expand_path("..", __dir__)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Set up Zeitwerk autoloader
|
|
41
|
+
loader = Zeitwerk::Loader.for_gem
|
|
42
|
+
# Migrations and factories follow ActiveRecord conventions, not Zeitwerk naming
|
|
43
|
+
loader.ignore(File.expand_path("square_sandbox_simulator/db", __dir__))
|
|
44
|
+
loader.setup
|
|
45
|
+
|
|
46
|
+
# Eager load all autoloaded constants
|
|
47
|
+
loader.eager_load
|