-
Notifications
You must be signed in to change notification settings - Fork 5
/
api.rb
124 lines (97 loc) · 2.34 KB
/
api.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
require "json"
require "sinatra"
require_relative "./config"
class API < Sinatra::Base
set :server, %w[puma]
set :show_exceptions, false
post "/rides" do
params = validate_params(request)
DB.transaction(isolation: :serializable) do
ride = Ride.create(
distance: params["distance"]
)
StagedLogRecord.insert(
action: ACTION_CREATE,
object: OBJECT_RIDE,
data: Sequel.pg_jsonb({
id: ride.id,
distance: ride.distance,
})
)
[201, JSON.generate(wrap_ok(
Messages.ok(distance: params["distance"].round(1))
))]
end
end
end
#
# constants
#
ACTION_CREATE = "create"
ACTION_UPDATE = "update"
ACTION_DELETE = "delete"
OBJECT_RIDE = "ride"
#
# models
#
class ConsumerState < Sequel::Model
end
class Ride < Sequel::Model
end
class Checkpoint < Sequel::Model
end
class StagedLogRecord < Sequel::Model
end
#
# other modules/classes
#
module Messages
def self.error_require_float(key:)
"Parameter '#{key}' must be a floating-point number."
end
def self.error_require_param(key:)
"Please specify parameter '#{key}'."
end
def self.ok(distance:)
"Payment accepted. Your pilot is on their way! distance=#{distance}m"
end
end
#
# helpers
#
def validate_params(request)
{
"distance" => validate_params_float(request, "distance"),
}
end
def validate_params_float(request, key)
val = validate_params_present(request, key)
# Float as opposed to to_f because it's more strict about what it'll take.
begin
Float(val)
rescue ArgumentError
halt 422, JSON.generate(wrap_error(Messages.error_require_float(key: key)))
end
end
def validate_params_present(request, key)
val = request.POST[key]
return val if !val.nil? && !val.empty?
halt 422, JSON.generate(wrap_error(Messages.error_require_param(key: key)))
end
# Wraps a message in the standard structure that we send back for error
# responses from the API. Still needs to be JSON-encoded before transmission.
def wrap_error(message)
{ error: message }
end
# Wraps a message in the standard structure that we send back for success
# responses from the API. Still needs to be JSON-encoded before transmission.
def wrap_ok(message)
{ message: message }
end
#
# run
#
if __FILE__ == $0
port = ENV["API_PORT"] || abort("need API_PORT")
API.run!(port: port)
end