mindbody-api 1.0.1.alpha

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.
Files changed (96) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +20 -0
  3. data/.travis.yml +5 -0
  4. data/CONTRIBUTING.md +16 -0
  5. data/Gemfile +12 -0
  6. data/Guardfile +6 -0
  7. data/LICENSE.md +22 -0
  8. data/README.md +79 -0
  9. data/Rakefile +52 -0
  10. data/lib/mindbody-api.rb +35 -0
  11. data/lib/mindbody-api/api_status.rb +109 -0
  12. data/lib/mindbody-api/client.rb +39 -0
  13. data/lib/mindbody-api/ext/savon_ext.rb +53 -0
  14. data/lib/mindbody-api/models.rb +34 -0
  15. data/lib/mindbody-api/models/appointment.rb +22 -0
  16. data/lib/mindbody-api/models/appointment_status.rb +13 -0
  17. data/lib/mindbody-api/models/availability.rb +11 -0
  18. data/lib/mindbody-api/models/class.rb +31 -0
  19. data/lib/mindbody-api/models/class_description.rb +14 -0
  20. data/lib/mindbody-api/models/class_schedule.rb +26 -0
  21. data/lib/mindbody-api/models/client.rb +32 -0
  22. data/lib/mindbody-api/models/client_service.rb +16 -0
  23. data/lib/mindbody-api/models/course.rb +16 -0
  24. data/lib/mindbody-api/models/level.rb +8 -0
  25. data/lib/mindbody-api/models/location.rb +38 -0
  26. data/lib/mindbody-api/models/payment.rb +11 -0
  27. data/lib/mindbody-api/models/program.rb +9 -0
  28. data/lib/mindbody-api/models/resource.rb +8 -0
  29. data/lib/mindbody-api/models/sale.rb +10 -0
  30. data/lib/mindbody-api/models/sale_item.rb +14 -0
  31. data/lib/mindbody-api/models/schedule_type.rb +13 -0
  32. data/lib/mindbody-api/models/semester.rb +9 -0
  33. data/lib/mindbody-api/models/service.rb +13 -0
  34. data/lib/mindbody-api/models/session_type.rb +8 -0
  35. data/lib/mindbody-api/models/site.rb +23 -0
  36. data/lib/mindbody-api/models/staff.rb +32 -0
  37. data/lib/mindbody-api/models/unavailability.rb +7 -0
  38. data/lib/mindbody-api/models/visit.rb +19 -0
  39. data/lib/mindbody-api/response.rb +119 -0
  40. data/lib/mindbody-api/service.rb +39 -0
  41. data/lib/mindbody-api/services/appointment_service.rb +15 -0
  42. data/lib/mindbody-api/services/class_service.rb +12 -0
  43. data/lib/mindbody-api/services/client_service.rb +30 -0
  44. data/lib/mindbody-api/services/finder_service.rb +11 -0
  45. data/lib/mindbody-api/services/sale_service.rb +14 -0
  46. data/lib/mindbody-api/services/site_service.rb +15 -0
  47. data/lib/mindbody-api/services/staff_service.rb +12 -0
  48. data/lib/mindbody-api/version.rb +4 -0
  49. data/mindbody-api.gemspec +29 -0
  50. data/spec/api_status_spec.rb +110 -0
  51. data/spec/client_spec.rb +51 -0
  52. data/spec/fixtures/wsdl/geotrust.wsdl +156 -0
  53. data/spec/mindbody_spec.rb +79 -0
  54. data/spec/model_spec.rb +7 -0
  55. data/spec/models/appointment_spec.rb +18 -0
  56. data/spec/models/appointment_status_spec.rb +17 -0
  57. data/spec/models/availability.rb +8 -0
  58. data/spec/models/class_description_spec.rb +12 -0
  59. data/spec/models/class_schedule_spec.rb +24 -0
  60. data/spec/models/class_spec.rb +39 -0
  61. data/spec/models/client_service_spec.rb +13 -0
  62. data/spec/models/client_spec.rb +33 -0
  63. data/spec/models/course_spec.rb +14 -0
  64. data/spec/models/level_spec.rb +6 -0
  65. data/spec/models/location_spec.rb +34 -0
  66. data/spec/models/payment_spec.rb +9 -0
  67. data/spec/models/program_spec.rb +7 -0
  68. data/spec/models/resource_spec.rb +6 -0
  69. data/spec/models/sale_item_spec.rb +12 -0
  70. data/spec/models/sale_spec.rb +8 -0
  71. data/spec/models/schedule_type_spec.rb +17 -0
  72. data/spec/models/semester_spec.rb +7 -0
  73. data/spec/models/service_spec.rb +11 -0
  74. data/spec/models/session_type_spec.rb +6 -0
  75. data/spec/models/site_spec.rb +31 -0
  76. data/spec/models/staff_spec.rb +29 -0
  77. data/spec/models/unavailability.rb +5 -0
  78. data/spec/models/visit_spec.rb +16 -0
  79. data/spec/response_spec.rb +115 -0
  80. data/spec/service_spec.rb +106 -0
  81. data/spec/services/appointment_service_spec.rb +13 -0
  82. data/spec/services/class_service_spec.rb +10 -0
  83. data/spec/services/client_service_spec.rb +27 -0
  84. data/spec/services/finder_service_spec.rb +9 -0
  85. data/spec/services/sale_service_spec.rb +12 -0
  86. data/spec/services/site_service_spec.rb +13 -0
  87. data/spec/services/staff_service_spec.rb +10 -0
  88. data/spec/spec_helper.rb +18 -0
  89. data/wsdl/AppointmentService.wsdl +1084 -0
  90. data/wsdl/ClassService.wsdl +1656 -0
  91. data/wsdl/ClientService.wsdl +2295 -0
  92. data/wsdl/FinderService.wsdl +1427 -0
  93. data/wsdl/SaleService.wsdl +1678 -0
  94. data/wsdl/SiteService.wsdl +915 -0
  95. data/wsdl/StaffService.wsdl +939 -0
  96. metadata +277 -0
@@ -0,0 +1,7 @@
1
+ module MindBody
2
+ module Models
3
+ class Unavailability < Availability
4
+ attribute :description, String
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,19 @@
1
+ module MindBody
2
+ module Models
3
+ class Visit < Base
4
+ attribute :id, Integer
5
+ attribute :class_id, Integer
6
+ attribute :start_date_time, DateTime
7
+ attribute :end_date_time, DateTime
8
+ attribute :name, String
9
+ attribute :staff, Staff
10
+ attribute :location, Location
11
+ attribute :client, Client
12
+ attribute :web_signup, Boolean
13
+ attribute :signed_in, Boolean
14
+ attribute :make_up, Boolean
15
+ attribute :service, ClientService
16
+ end
17
+ end
18
+ end
19
+
@@ -0,0 +1,119 @@
1
+ module MindBody
2
+ module Services
3
+ class Response
4
+ attr_reader :response, :result, :status, :error_code, :xml_detail,
5
+ :result_count, :current_page_index, :total_page_count,
6
+ :remote_method, :message, :api_status
7
+
8
+ def initialize(res)
9
+ @response = res
10
+ @result = {}
11
+ normalize_response
12
+ @api_status = APIStatus.build_status(error_code, status, message)
13
+ end
14
+
15
+ # Delegate methods to the @response object
16
+ def method_missing(method, *args, &blk)
17
+ if @response.respond_to?(method)
18
+ self.class.send(:define_method, method) do |*args, &blk|
19
+ @response.send(method, *args, &blk)
20
+ end
21
+ self.send(method)
22
+ else
23
+ super
24
+ end
25
+ end
26
+
27
+ def respond_to?(method, include_private = false)
28
+ super || @response.respond_to?(method)
29
+ end
30
+
31
+ private
32
+ def normalize_response
33
+ # All responses come back nested in a CallingMethodResponse
34
+ # and then a CallingMethodResult. Strip these two parents
35
+ # so we can get at the meat of the result. This is a naive
36
+ # strip
37
+ result = @response.to_hash
38
+ result = result.flatten.last.flatten[1]
39
+
40
+ result = normalize_hash(result)
41
+
42
+ # Loop over result. If we respond to a key of the response, consume it,
43
+ # otherwise build the result hash
44
+ @result.clear
45
+ result.each_pair do |key, value|
46
+ if self.respond_to?(key) && key != :class
47
+ self.instance_variable_set("@#{key}", value)
48
+ else
49
+ @result[key] = constantize_key(key, value)
50
+ end
51
+ end
52
+ end
53
+
54
+ # Recursively iterate over the hash and perform
55
+ # a number of optimizations:
56
+ #
57
+ # * Sometimes data comes back like this:
58
+ # things => {
59
+ # thing => [thing1, thing2, thing3]
60
+ # }
61
+ #
62
+ # Map this pattern to be like this:
63
+ # things => [thing1, thing2, thing3, ...
64
+ def normalize_hash(hash)
65
+ hash.each_pair do |key, value|
66
+ if value.is_a? Hash
67
+ hash[key] = normalize_hash(value)
68
+
69
+ key_singular = map_key(key).singularize.to_sym
70
+
71
+ if value.has_key?(key_singular)
72
+ hash[key] = value[key_singular]
73
+ elsif value.has_key?(:string)
74
+ hash[key] = value[:string]
75
+ elsif value.has_key?(:int)
76
+ hash[key] = value[:int]
77
+ end
78
+ elsif value.is_a? Array
79
+ value.map! do |i|
80
+ i.is_a?(Hash) ? normalize_hash(i) : i
81
+ end
82
+ end
83
+ end
84
+
85
+ return hash
86
+ end
87
+
88
+ def constantize_key(key, value)
89
+ begin
90
+ klass_s = map_key(key).classify
91
+ klass = "MindBody::Models::#{klass_s}".constantize
92
+
93
+ # Make sure we return a properly mapped array if needed
94
+ value.is_a?(Array) ? value.map {|v| klass.new(v)} : klass.new(value) unless value.nil?
95
+ rescue
96
+ value
97
+ end
98
+ end
99
+
100
+ # We have some keys that need
101
+ # to be mapped to others so that classify works properly.
102
+ #
103
+ # We cannot use inflector to do this because some words are popular
104
+ # enough to stomp on people's namespaces
105
+ def map_key(key)
106
+ case key
107
+ when :staff_members, :organizer
108
+ 'staff'
109
+ when :purchases
110
+ 'sale_item'
111
+ when :image_url, :mobile_image_url
112
+ 'string'
113
+ else
114
+ key.to_s
115
+ end
116
+ end
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,39 @@
1
+ require 'savon'
2
+ require 'mindbody-api/client'
3
+ require 'mindbody-api/ext/savon_ext'
4
+
5
+ module MindBody
6
+ module Services
7
+ class Service
8
+ extend Savon::Model
9
+
10
+ WSDL_PATH = File.expand_path(File.join(__FILE__, '..', '..', '..', 'wsdl', '%s.wsdl'))
11
+
12
+ class << self
13
+ def service(name)
14
+ @wsdl = WSDL_PATH % name.to_s
15
+ client :wsdl => @wsdl
16
+ end
17
+
18
+ def client(globals = {})
19
+ @client ||= Client.new(globals)
20
+ end
21
+
22
+ def operation(name, *opts)
23
+ define_class_operation(name, *opts)
24
+ define_instance_operation(name, *opts)
25
+ end
26
+
27
+ include MindBody::SavonExt::Service
28
+ end
29
+ end
30
+ end
31
+ end
32
+
33
+ require 'mindbody-api/services/appointment_service'
34
+ require 'mindbody-api/services/class_service'
35
+ require 'mindbody-api/services/client_service'
36
+ require 'mindbody-api/services/finder_service'
37
+ require 'mindbody-api/services/sale_service'
38
+ require 'mindbody-api/services/site_service'
39
+ require 'mindbody-api/services/staff_service'
@@ -0,0 +1,15 @@
1
+ module MindBody
2
+ module Services
3
+ class AppointmentService < Service
4
+ service "AppointmentService"
5
+
6
+ operation :get_staff_appointments, :required => [:staff_credentials]
7
+ operation :get_bookable_items, :required => [:session_type_ids]
8
+ operation :get_schedule_items
9
+ operation :add_or_update_appointments, :required => [:appointments]
10
+ operation :add_or_update_availabilities, :required => [:user_credentials]
11
+ operation :get_active_session_times
12
+ operation :get_appointment_options
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,12 @@
1
+ module MindBody
2
+ module Services
3
+ class ClassService < Service
4
+ service "ClassService"
5
+
6
+ operation :get_classes
7
+ operation :get_class_visits, required:[:class_id]
8
+ operation :get_class_descriptions
9
+ operation :get_class_schedules
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,30 @@
1
+ module MindBody
2
+ module Services
3
+ class ClientService < Service
4
+ service 'ClientService'
5
+
6
+ operation :add_arrival, :required => [:client_id, :location_id], :locals => false
7
+ operation :add_or_update_clients, :required => [:clients]
8
+ operation :get_clients
9
+ operation :get_custom_client_fields, :locals => false
10
+ operation :get_client_indexes, :locals => false
11
+ operation :get_client_contact_logs, :required => [:client_id]
12
+ operation :add_or_update_contact_logs, :required => [:contact_logs]
13
+ operation :get_contact_log_types, :locals => false
14
+ operation :upload_client_document, :required => [:client_id, :filename, :bytes]
15
+ operation :get_client_referral_types, :locals => false
16
+ operation :get_active_client_memberships, :required => [:client_id]
17
+ operation :get_client_contracts, :required => [:client_id]
18
+ operation :get_client_account_balances, :required => [:client_ids]
19
+ operation :get_client_services, :required => [:client_id]
20
+ operation :get_client_visits, :required => [:client_id]
21
+ operation :get_client_purchases, :required => [:client_id]
22
+ operation :get_client_schedule, :required => [:client_id]
23
+ operation :get_required_client_fields, :locals => false
24
+ operation :validate_login, :required => [:username, :password], :locals => false
25
+ operation :update_client_services, :required => [:client_services]
26
+ operation :send_user_new_password, :required => [:user_email, :user_first_name, :user_last_name], :locals => false
27
+ end
28
+ end
29
+ end
30
+
@@ -0,0 +1,11 @@
1
+ module MindBody
2
+ module Services
3
+ class FinderService < Service
4
+ service 'FinderService'
5
+
6
+ operation :get_classes_within_radius, :required => [:search_latitude, :search_longitude, :search_radius]
7
+ operation :get_session_types_within_radius, :required => [:search_latitude, :search_longitude, :search_radius]
8
+ operation :get_business_locations_within_radius, :required => [:search_latitude, :search_longitude, :search_radius]
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,14 @@
1
+ module MindBody
2
+ module Services
3
+ class SaleService < Service
4
+ service "SaleService"
5
+
6
+ operation :get_accepted_card_type, :locals => false
7
+ operation :get_sales
8
+ operation :get_services
9
+ operation :get_packages
10
+ operation :get_products
11
+ operation :get_custom_payment_methods
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,15 @@
1
+ module MindBody
2
+ module Services
3
+ class SiteService < Service
4
+ service 'SiteService'
5
+
6
+ operation :get_sites
7
+ operation :get_locations, :locals => false
8
+ operation :get_activation_code, :locals => false
9
+ operation :get_programs
10
+ operation :get_session_types
11
+ operation :get_resources
12
+ operation :get_relationships, :locals => false
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,12 @@
1
+ module MindBody
2
+ module Services
3
+ class StaffService < Service
4
+ service 'StaffService'
5
+
6
+ operation :get_staff
7
+ operation :get_staff_permissions
8
+ operation :add_or_update_staff, :required => [:staff]
9
+ operation :get_staff_img_url, :required => [:staffid]
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,4 @@
1
+ module MindBody
2
+ VERSION = "1.0.1.alpha"
3
+ API_VERSION = "0.5.2.0"
4
+ end
@@ -0,0 +1,29 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'mindbody-api/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "mindbody-api"
8
+ gem.version = MindBody::VERSION
9
+ gem.authors = ["Stafford Brunk"]
10
+ gem.email = ["stafford.brunk@gmail.com"]
11
+ gem.description = %q{A Ruby interface for the MindBody API}
12
+ gem.summary = %q{This gem provides a Ruby interface for the MindBody API in both a services format and a 'Rails-style' format}
13
+ gem.homepage = "http://wingrunr21.github.io/mindbody-api/"
14
+ gem.license = 'MIT'
15
+
16
+ gem.files = `git ls-files`.split($/)
17
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
18
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
19
+ gem.require_paths = ["lib"]
20
+
21
+ gem.add_dependency 'savon', '~> 2.11.0'
22
+ gem.add_dependency 'virtus', '~> 1.0.0'
23
+ gem.add_dependency 'activesupport', '>= 3.2.11'
24
+
25
+ gem.add_development_dependency 'rspec', '~> 2.14.0'
26
+ gem.add_development_dependency 'simplecov', '~> 0.8.2'
27
+ gem.add_development_dependency 'guard-rspec', '~> 4.2.8'
28
+ gem.add_development_dependency 'rake', '~> 10.0.3'
29
+ end
@@ -0,0 +1,110 @@
1
+ require 'spec_helper'
2
+
3
+ describe MindBody::APIStatus do
4
+ subject { MindBody::APIStatus }
5
+
6
+ describe '#build_status' do
7
+ before do
8
+ @preconstants = MindBody::APIStatus.constants
9
+ end
10
+ after do
11
+ (MindBody::APIStatus.constants - @preconstants).each do |const|
12
+ MindBody::APIStatus.send(:remove_const, const)
13
+ end
14
+ end
15
+
16
+ it 'should return a new instance of the given status' do
17
+ status = subject.build_status(102, 'InvalidCredentials')
18
+ expect(status.error_code).to eq(102)
19
+ expect(status.status).to eq :invalid_credentials
20
+ expect(status.class.to_s).to eq 'MindBody::APIStatus::InvalidCredentials'
21
+ end
22
+
23
+ it 'should normalize oddities in the given status' do
24
+ status = subject.build_status(315, 'invalid.parameters')
25
+ expect(status.status).to eq :invalid_parameters
26
+ expect(status.class.to_s).to eq 'MindBody::APIStatus::InvalidParameters'
27
+ end
28
+
29
+ context "when the status has already been defined" do
30
+ before do
31
+ MindBody::APIStatus.const_set('InvalidCredentials', MindBody::APIStatus::Base)
32
+ end
33
+
34
+ it 'should return the existing constant' do
35
+ expect(MindBody::APIStatus).to receive(:define_api_status_constant).never
36
+
37
+ subject.build_status(102, 'InvalidCredentials')
38
+ end
39
+
40
+ after do
41
+ MindBody::APIStatus.send(:remove_const, 'InvalidCredentials')
42
+ end
43
+ end
44
+
45
+ context "when a new status has been received" do
46
+ before do
47
+ MindBody::APIStatus.stub(:define_api_status_constant)
48
+ .and_return(MindBody::APIStatus::Base)
49
+ end
50
+ it 'should create a new constant' do
51
+ expect(MindBody::APIStatus).to receive(:define_api_status_constant)
52
+ .with('InvalidCredentials', 'invalid_credentials', 102)
53
+
54
+ subject.build_status(102, 'InvalidCredentials')
55
+ end
56
+ end
57
+ end
58
+
59
+ describe '#define_api_status_constant' do
60
+ before :all do
61
+ @klass = MindBody::APIStatus.define_api_status_constant(
62
+ 'InvalidCredentials', :invalid_credentials, 102)
63
+ end
64
+
65
+ after :all do
66
+ MindBody::APIStatus.send(:remove_const, 'InvalidCredentials')
67
+ end
68
+
69
+ it 'should create a new constant that is a subclass of Base' do
70
+ expect(@klass.superclass).to eq MindBody::APIStatus::Base
71
+ end
72
+
73
+ it 'should override status to return the given status' do
74
+ expect(@klass.status).to eq :invalid_credentials
75
+ end
76
+
77
+ it 'should override error_code to return the given code' do
78
+ expect(@klass.error_code).to eq 102
79
+ end
80
+ end
81
+ end
82
+
83
+ describe MindBody::APIStatus::Base do
84
+
85
+ it 'should have a status of :undefined_error' do
86
+ expect(subject.status).to eq :undefined_error
87
+ end
88
+
89
+ it 'should have a status of :undefined_error at the class level' do
90
+ expect(subject.class.status).to eq :undefined_error
91
+ end
92
+
93
+ it 'should delegate status up to the class level' do
94
+ expect(subject.class).to receive(:status)
95
+ subject.status
96
+ end
97
+
98
+ it 'should have an error_code of 9999' do
99
+ expect(subject.error_code).to eq 9999
100
+ end
101
+
102
+ it 'should have a error_code of 9999 at the class level' do
103
+ expect(subject.class.error_code).to eq 9999
104
+ end
105
+
106
+ it 'should delegate status up to the class level' do
107
+ expect(subject.class).to receive(:error_code)
108
+ subject.error_code
109
+ end
110
+ end