eventbrite_sdk 3.0.2
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/.gitignore +2 -0
- data/.rspec +2 -0
- data/.travis.yml +7 -0
- data/Gemfile +12 -0
- data/LICENSE.txt +21 -0
- data/README.md +136 -0
- data/Rakefile +6 -0
- data/bin/console +16 -0
- data/bin/setup +6 -0
- data/eventbrite_sdk.gemspec +28 -0
- data/lib/eventbrite_sdk.rb +135 -0
- data/lib/eventbrite_sdk/attendee.rb +26 -0
- data/lib/eventbrite_sdk/category.rb +14 -0
- data/lib/eventbrite_sdk/event.rb +75 -0
- data/lib/eventbrite_sdk/exceptions.rb +42 -0
- data/lib/eventbrite_sdk/lists/owned_event_orders_list.rb +27 -0
- data/lib/eventbrite_sdk/media.rb +77 -0
- data/lib/eventbrite_sdk/order.rb +25 -0
- data/lib/eventbrite_sdk/organizer.rb +20 -0
- data/lib/eventbrite_sdk/report.rb +49 -0
- data/lib/eventbrite_sdk/resource.rb +99 -0
- data/lib/eventbrite_sdk/resource/attributes.rb +158 -0
- data/lib/eventbrite_sdk/resource/null_schema_definition.rb +13 -0
- data/lib/eventbrite_sdk/resource/operations/attribute_schema.rb +57 -0
- data/lib/eventbrite_sdk/resource/operations/endpoint.rb +101 -0
- data/lib/eventbrite_sdk/resource/operations/list.rb +15 -0
- data/lib/eventbrite_sdk/resource/operations/relationships.rb +120 -0
- data/lib/eventbrite_sdk/resource/schema_definition.rb +50 -0
- data/lib/eventbrite_sdk/resource_list.rb +86 -0
- data/lib/eventbrite_sdk/subcategory.rb +12 -0
- data/lib/eventbrite_sdk/ticket_class.rb +60 -0
- data/lib/eventbrite_sdk/user.rb +28 -0
- data/lib/eventbrite_sdk/venue.rb +22 -0
- data/lib/eventbrite_sdk/version.rb +5 -0
- data/lib/eventbrite_sdk/webhook.rb +11 -0
- metadata +167 -0
@@ -0,0 +1,42 @@
|
|
1
|
+
module EventbriteSDK
|
2
|
+
class EventbriteAPIError < RuntimeError
|
3
|
+
attr_reader :message, :response
|
4
|
+
|
5
|
+
def initialize(msg = '', response = :none)
|
6
|
+
@message = msg
|
7
|
+
@response = response
|
8
|
+
end
|
9
|
+
|
10
|
+
# Returns a hash with AT LEAST 'error_description'.
|
11
|
+
# When an error is raised manually there will be no response!
|
12
|
+
# This is handled by using the specified message as the error_description.
|
13
|
+
def parsed_error
|
14
|
+
default = %({"error_description": "#{message}"})
|
15
|
+
value = response_value(:body, fallback: default)
|
16
|
+
|
17
|
+
JSON.parse(value)
|
18
|
+
end
|
19
|
+
|
20
|
+
# Returns the status code of the response, or :none if there is no response.
|
21
|
+
def status_code
|
22
|
+
response_value(:code)
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def response_value(key, fallback: :none)
|
28
|
+
if response.respond_to?(key)
|
29
|
+
response.send(key)
|
30
|
+
else
|
31
|
+
fallback
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
class BadRequest < EventbriteAPIError; end
|
37
|
+
class Forbidden < EventbriteAPIError; end
|
38
|
+
class InvalidAttribute < EventbriteAPIError; end
|
39
|
+
class InternalServerError < EventbriteAPIError; end
|
40
|
+
class ResourceNotFound < EventbriteAPIError; end
|
41
|
+
class Unauthorized < EventbriteAPIError; end
|
42
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module EventbriteSDK
|
2
|
+
module Lists
|
3
|
+
class OwnedEventOrdersList < ResourceList
|
4
|
+
def search(term)
|
5
|
+
send(:"term_provided_#{!!term}", term)
|
6
|
+
|
7
|
+
@pagination = { 'page_count' => 1, 'page_number' => 1 }
|
8
|
+
|
9
|
+
self
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
# Swaps the endpoint out to allow searches
|
15
|
+
def term_provided_true(term)
|
16
|
+
@url_base.sub!('owned_event_orders', 'search_owned_event_orders')
|
17
|
+
@query[:search_term] = term
|
18
|
+
end
|
19
|
+
|
20
|
+
# Swaps the endpoint out owned events, and removes the search_term query
|
21
|
+
def term_provided_false(_term)
|
22
|
+
@url_base.sub!('search_owned_event_orders', 'owned_event_orders')
|
23
|
+
@query.delete(:search_term)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
module EventbriteSDK
|
2
|
+
# This module implements media upload to Eventbrite based on:
|
3
|
+
# https://docs.evbhome.com/apidocs/reference/uploads/?highlight=logo
|
4
|
+
|
5
|
+
class Media < Resource
|
6
|
+
resource_path 'media/:id'
|
7
|
+
|
8
|
+
attr_reader :image_type, :file
|
9
|
+
|
10
|
+
VALID_TYPES = {
|
11
|
+
event_logo: 'image-event-logo',
|
12
|
+
organizer_logo: 'image-organizer-logo',
|
13
|
+
user_photo: 'image-user-photo',
|
14
|
+
event_view_from_seat: 'image-event-view-from-seat',
|
15
|
+
}.freeze
|
16
|
+
|
17
|
+
schema_definition do
|
18
|
+
string 'crop_mask'
|
19
|
+
string 'original'
|
20
|
+
string 'id'
|
21
|
+
string 'url'
|
22
|
+
string 'aspect_ratio'
|
23
|
+
string 'edge_color'
|
24
|
+
string 'edge_color_set'
|
25
|
+
end
|
26
|
+
|
27
|
+
def upload!(image_type, file)
|
28
|
+
# Media uploads through the API involve a multiple step process:
|
29
|
+
|
30
|
+
# 1. Retrieve upload instructions + an upload token from the API
|
31
|
+
instructions = get_instructions(image_type)
|
32
|
+
|
33
|
+
# 2. Upload the file to the endpoint specified in the upload instructions
|
34
|
+
eventbrite_upload(file, instructions)
|
35
|
+
|
36
|
+
# 3. When the upload has finished, notify the API by re-sending the
|
37
|
+
# upload token from step 1
|
38
|
+
notify(instructions['upload_token'])
|
39
|
+
|
40
|
+
true
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def get_instructions(image_type, request = EventbriteSDK)
|
46
|
+
type = VALID_TYPES[image_type]
|
47
|
+
|
48
|
+
unless type
|
49
|
+
raise ArgumentError.new(
|
50
|
+
"image_type needs to be one of #{VALID_TYPES.keys}"
|
51
|
+
)
|
52
|
+
end
|
53
|
+
|
54
|
+
request.get(url: path('upload'), query: { type: type })
|
55
|
+
end
|
56
|
+
|
57
|
+
def eventbrite_upload(file, instructions)
|
58
|
+
RestClient.post(
|
59
|
+
instructions['upload_url'],
|
60
|
+
instructions['upload_data'].merge(file: file),
|
61
|
+
multipart: true,
|
62
|
+
)
|
63
|
+
end
|
64
|
+
|
65
|
+
def notify(upload_token, request = EventbriteSDK)
|
66
|
+
response = request.post(
|
67
|
+
url: path('upload'), payload: { upload_token: upload_token }
|
68
|
+
)
|
69
|
+
|
70
|
+
if payload['crop_mask']
|
71
|
+
response = request.post(url: path(response['id']), payload: payload)
|
72
|
+
end
|
73
|
+
|
74
|
+
reload(response)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module EventbriteSDK
|
2
|
+
class Order < Resource
|
3
|
+
resource_path 'orders/:id'
|
4
|
+
|
5
|
+
# Defines order#resend_confirmation_email and order#refund
|
6
|
+
#
|
7
|
+
# When an event has an id the POST is made, otherwise we return false
|
8
|
+
# POSTS to order/:id/(resend_confirmation_email|refunds)
|
9
|
+
define_api_actions :resend_confirmation_email, { refund: :refunds }
|
10
|
+
|
11
|
+
has_many :attendees, object_class: 'Attendee'
|
12
|
+
belongs_to :event, object_class: 'Event'
|
13
|
+
|
14
|
+
schema_definition do
|
15
|
+
string 'name'
|
16
|
+
string 'first_name'
|
17
|
+
string 'last_name'
|
18
|
+
string 'email'
|
19
|
+
string 'costs'
|
20
|
+
datetime 'created', read_only: true
|
21
|
+
datetime 'changed', read_only: true
|
22
|
+
string 'resource_uri', read_only: true
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module EventbriteSDK
|
2
|
+
class Organizer < Resource
|
3
|
+
resource_path 'organizers/:id'
|
4
|
+
|
5
|
+
attributes_prefix 'organizer'
|
6
|
+
|
7
|
+
has_many :events, object_class: 'Event'
|
8
|
+
|
9
|
+
schema_definition do
|
10
|
+
string 'name'
|
11
|
+
string 'description.html'
|
12
|
+
string 'long_description.html'
|
13
|
+
string 'logo.id'
|
14
|
+
string 'website'
|
15
|
+
string 'twitter'
|
16
|
+
string 'facebook'
|
17
|
+
string 'instagram'
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module EventbriteSDK
|
2
|
+
class Report
|
3
|
+
STRING_KEYS = %i(
|
4
|
+
start_date
|
5
|
+
end_date
|
6
|
+
date_facet
|
7
|
+
event_status
|
8
|
+
timezone
|
9
|
+
group_by
|
10
|
+
).freeze
|
11
|
+
|
12
|
+
VALID_REPORTS = %i(attendees sales).freeze
|
13
|
+
|
14
|
+
def initialize
|
15
|
+
@query = {}
|
16
|
+
end
|
17
|
+
|
18
|
+
def event_ids(*ids)
|
19
|
+
@query[:event_ids] = ids.join(',')
|
20
|
+
|
21
|
+
self
|
22
|
+
end
|
23
|
+
|
24
|
+
def filter_by(filters)
|
25
|
+
@query[:filter_by] = filters.to_json
|
26
|
+
|
27
|
+
self
|
28
|
+
end
|
29
|
+
|
30
|
+
STRING_KEYS.each do |method|
|
31
|
+
define_method(method) do |value|
|
32
|
+
@query[method] = value
|
33
|
+
self
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def query
|
38
|
+
@query.dup # Don't allow mutation
|
39
|
+
end
|
40
|
+
|
41
|
+
def retrieve(type = nil, sdk = EventbriteSDK)
|
42
|
+
unless VALID_REPORTS.include?(type)
|
43
|
+
raise ArgumentError, "`:type` is not of #{VALID_REPORTS}"
|
44
|
+
end
|
45
|
+
|
46
|
+
sdk.get(url: "reports/#{type}", query: query)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
module EventbriteSDK
|
2
|
+
class Resource
|
3
|
+
include Operations::AttributeSchema
|
4
|
+
include Operations::Endpoint
|
5
|
+
include Operations::Relationships
|
6
|
+
|
7
|
+
def self.build(attrs)
|
8
|
+
new.tap do |instance|
|
9
|
+
instance.assign_attributes(attrs)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
# Allows compile time definition of POST methods
|
14
|
+
#
|
15
|
+
# Example:
|
16
|
+
# class Event < Resource
|
17
|
+
# define_api_actions :publish, :unpublish
|
18
|
+
# end
|
19
|
+
#
|
20
|
+
# would defined instance methods like so:
|
21
|
+
#
|
22
|
+
# def publish
|
23
|
+
# !new? && EventbriteSDK.post(url: path('publish'))
|
24
|
+
# end
|
25
|
+
#
|
26
|
+
# def publish
|
27
|
+
# !new? && EventbriteSDK.post(url: path('unpublish'))
|
28
|
+
# end
|
29
|
+
def self.define_api_actions(*actions)
|
30
|
+
req = ->(inst, postfix) do
|
31
|
+
inst.instance_eval { !new? && EventbriteSDK.post(url: path(postfix)) }
|
32
|
+
end
|
33
|
+
|
34
|
+
actions.each do |action|
|
35
|
+
if action.is_a?(Hash)
|
36
|
+
method_name, postfix_path = action.flatten
|
37
|
+
else
|
38
|
+
method_name = postfix_path = action
|
39
|
+
end
|
40
|
+
|
41
|
+
define_method(method_name) { req.call(self, postfix_path) }
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def initialize(hydrated_attrs = {})
|
46
|
+
reload hydrated_attrs
|
47
|
+
end
|
48
|
+
|
49
|
+
def new?
|
50
|
+
!id
|
51
|
+
end
|
52
|
+
|
53
|
+
def refresh!(request = EventbriteSDK)
|
54
|
+
unless new?
|
55
|
+
reload request.get(url: path)
|
56
|
+
else
|
57
|
+
false
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def inspect
|
62
|
+
"#<#{self.class}: #{JSON.pretty_generate(@attrs.to_h)}>"
|
63
|
+
end
|
64
|
+
|
65
|
+
def save(postfixed_path = '', request = EventbriteSDK)
|
66
|
+
if changed? || !postfixed_path.empty?
|
67
|
+
response = request.post(url: path(postfixed_path),
|
68
|
+
payload: attrs.payload(self.class.prefix))
|
69
|
+
|
70
|
+
reload(response)
|
71
|
+
|
72
|
+
true
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def to_json(opts = {})
|
77
|
+
attrs.to_json(opts)
|
78
|
+
end
|
79
|
+
|
80
|
+
def delete(request = EventbriteSDK)
|
81
|
+
response = request.delete(url: path)
|
82
|
+
response['deleted']
|
83
|
+
end
|
84
|
+
|
85
|
+
def read_attribute_for_serialization(attribute)
|
86
|
+
attrs[attribute]
|
87
|
+
end
|
88
|
+
|
89
|
+
private
|
90
|
+
|
91
|
+
def resource_class_from_string(klass)
|
92
|
+
EventbriteSDK.const_get(klass)
|
93
|
+
end
|
94
|
+
|
95
|
+
def reload(hydrated_attrs = {})
|
96
|
+
build_attrs(hydrated_attrs)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,158 @@
|
|
1
|
+
module EventbriteSDK
|
2
|
+
class Resource
|
3
|
+
class Attributes
|
4
|
+
attr_reader :attrs, :changes
|
5
|
+
|
6
|
+
def self.build(attrs, schema)
|
7
|
+
new({}, schema).tap do |instance|
|
8
|
+
instance.assign_attributes(attrs)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize(hydrated_attrs = {}, schema = NullSchemaDefinition.new)
|
13
|
+
@attrs = {}
|
14
|
+
@changes = {}
|
15
|
+
@schema = schema
|
16
|
+
|
17
|
+
# Build out initial hash based on schema's defined keys
|
18
|
+
schema.defined_keys.each { |key| bury_attribute(key, nil) }
|
19
|
+
|
20
|
+
@attrs = attrs.merge(stringify_keys(hydrated_attrs))
|
21
|
+
end
|
22
|
+
|
23
|
+
def [](key)
|
24
|
+
public_send(key)
|
25
|
+
end
|
26
|
+
|
27
|
+
def assign_attributes(new_attrs)
|
28
|
+
stringify_keys(new_attrs).each do |attribute_key, value|
|
29
|
+
assign_value(attribute_key, value) if schema.writeable?(attribute_key)
|
30
|
+
end
|
31
|
+
|
32
|
+
nil
|
33
|
+
end
|
34
|
+
|
35
|
+
def changed?
|
36
|
+
changes.any?
|
37
|
+
end
|
38
|
+
|
39
|
+
def to_h
|
40
|
+
attrs.to_h
|
41
|
+
end
|
42
|
+
|
43
|
+
def to_json(opts = {})
|
44
|
+
to_h.to_json(opts)
|
45
|
+
end
|
46
|
+
|
47
|
+
def inspect
|
48
|
+
"#<#{self.class}: #{JSON.pretty_generate(@attrs.to_h)}>"
|
49
|
+
end
|
50
|
+
|
51
|
+
def reset!
|
52
|
+
changes.each do |attribute_key, (old_value, _current_value)|
|
53
|
+
bury_attribute(attribute_key, old_value)
|
54
|
+
end
|
55
|
+
|
56
|
+
@changes = {}
|
57
|
+
|
58
|
+
true
|
59
|
+
end
|
60
|
+
|
61
|
+
# Provides changeset in a format that can be thrown at an endpoint
|
62
|
+
#
|
63
|
+
# prefix: This is needed due to inconsistencies in the EB API
|
64
|
+
# Sometimes there's a prefix, sometimes there's not,
|
65
|
+
# sometimes it's singular, sometimes it's plural.
|
66
|
+
# Once the API gets a bit more nomalized we can remove this
|
67
|
+
# alltogether and infer a prefix based
|
68
|
+
# on the class name of the resource
|
69
|
+
def payload(prefix = nil)
|
70
|
+
changes.each_with_object({}) do |(attribute_key, (_, value)), payload|
|
71
|
+
key = if prefix
|
72
|
+
"#{prefix}.#{attribute_key}"
|
73
|
+
else
|
74
|
+
attribute_key
|
75
|
+
end
|
76
|
+
|
77
|
+
bury(key, value, payload)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def values
|
82
|
+
attrs.values
|
83
|
+
end
|
84
|
+
|
85
|
+
private
|
86
|
+
|
87
|
+
attr_reader :schema
|
88
|
+
|
89
|
+
def assign_value(attribute_key, value)
|
90
|
+
dirty_check(attribute_key, value)
|
91
|
+
add_rich_value(attribute_key)
|
92
|
+
bury_attribute(attribute_key, value)
|
93
|
+
end
|
94
|
+
|
95
|
+
def dirty_check(attribute_key, value)
|
96
|
+
initial_value = attrs.dig(*attribute_key.split('.'))
|
97
|
+
|
98
|
+
if initial_value != value
|
99
|
+
changes[attribute_key] = [initial_value, value]
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def bury_attribute(attribute_key, value)
|
104
|
+
bury(attribute_key, value, attrs)
|
105
|
+
end
|
106
|
+
|
107
|
+
# Since we use dirty checking to determine what the payload is
|
108
|
+
# you can run into a case where a "rich media" field needs other attrs
|
109
|
+
# Namely timezone, so if a rich date changed, add the tz with it.
|
110
|
+
def add_rich_value(attribute_key)
|
111
|
+
if changes[attribute_key] && attribute_key =~ /\A(.+)\.utc\z/
|
112
|
+
key_prefix = Regexp.last_match(1)
|
113
|
+
tz = attrs.dig(key_prefix, 'timezone')
|
114
|
+
|
115
|
+
changes["#{key_prefix}.timezone"] = [tz, tz]
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def bury(attribute_key, value, hash = {})
|
120
|
+
keys = attribute_key.split '.'
|
121
|
+
|
122
|
+
# Hand rolling #bury
|
123
|
+
# hopefully we get it in the next release of Ruby
|
124
|
+
keys.each_cons(2).reduce(hash) do |prev_attrs, (key, _)|
|
125
|
+
prev_attrs[key] ||= {}
|
126
|
+
end[keys.last] = value
|
127
|
+
|
128
|
+
hash
|
129
|
+
end
|
130
|
+
|
131
|
+
def method_missing(method_name, *_args, &_block)
|
132
|
+
requested_key = method_name.to_s
|
133
|
+
|
134
|
+
if attrs.has_key?(requested_key)
|
135
|
+
handle_requested_attr(attrs[requested_key])
|
136
|
+
else
|
137
|
+
super
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
def respond_to_missing?(method_name, _include_private = false)
|
142
|
+
attrs.has_key?(method_name.to_s) || super
|
143
|
+
end
|
144
|
+
|
145
|
+
def handle_requested_attr(value)
|
146
|
+
if value.is_a?(Hash)
|
147
|
+
self.class.new(value)
|
148
|
+
else
|
149
|
+
value
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
def stringify_keys(params)
|
154
|
+
params.to_h.map { |key, value| [key.to_s, value] }.to_h
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|