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
+ ---
2
+ SHA1:
3
+ metadata.gz: 177b54dfe5a26b025296433fc1feac2a0718c3b6
4
+ data.tar.gz: 8fa1c5cd465d3e65925bbb651c2ffe651543c5cd
5
+ SHA512:
6
+ metadata.gz: 0670b56e47509479b718e6381bbd09ee5781b9174b53588d487433acebd0dd11fe290190889f5363d20becc217885265bdffeaef11119d0d81aa30dcddb7bdd2
7
+ data.tar.gz: cfed01706925dcd97a82b9caa2fc19920167e390a75cad18763f48b4da15d6bd0c9c850b3cf807041d298c9b8739fde6cfa81f1b6df764d1829dc454d2783cfd
@@ -0,0 +1,20 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ .rbenv-version
19
+ .ruby-version
20
+ .env
@@ -0,0 +1,5 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - 2.0.0
5
+ - 2.1.0
@@ -0,0 +1,16 @@
1
+ # Contributing
2
+
3
+ ## Guidelines:
4
+
5
+ * All changes should be in a topic branch
6
+ * All changes should have appropriate test coverage
7
+ * 2 spaces (not tabs) for indention
8
+ * Travis builds must be green prior to merging
9
+
10
+ ## Steps:
11
+
12
+ 1. Fork it
13
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
14
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
15
+ 4. Push to the branch (`git push origin my-new-feature`)
16
+ 5. Create new Pull Request
data/Gemfile ADDED
@@ -0,0 +1,12 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in mindbody-api.gemspec
4
+ gemspec
5
+
6
+ # For guard
7
+ gem 'rb-inotify', :require => false
8
+ gem 'rb-fsevent', :require => false
9
+ gem 'rb-fchange', :require => false
10
+
11
+ # To pick up the .env file
12
+ gem 'dotenv'
@@ -0,0 +1,6 @@
1
+ guard 'rspec' do
2
+ watch(%r{^spec/.+_spec\.rb$})
3
+ watch(%r{^lib/mindbody-api/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
4
+ watch('spec/spec_helper.rb') { "spec" }
5
+ end
6
+
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Stafford Brunk
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,79 @@
1
+ # MindBody API
2
+
3
+ Provides a Ruby interface to the [MindBody API](http://www.mindbodyonline.com/api)
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'mindbody-api'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install mindbody-api
18
+
19
+ ## Configuration
20
+
21
+ You must set your MindBody source name, key, and site ids. These values are accessible via the [MindBody Partner Page](https://api.mindbodyonline.com/Home/LogIn)
22
+
23
+ The easiest way to set these is via three environment variables: `MINDBODY_SOURCE_NAME`, `MINDBODY_SOURCE_KEY`, and `MINDBODY_SITE_IDS`. `MINDBODY_SITE_IDS` can have any delimiter.
24
+
25
+ Alternatively, you may set them in an initializer:
26
+
27
+ MindBody.configure do |config|
28
+ config.site_ids = -99
29
+ config.source_key = 'abcd1234'
30
+ config.source_name = 'SuperFoo'
31
+ config.log_level = :info # Savon logging level. Default is :debug, options are [:debug, :info, :warn, :error, :fatal]
32
+ end
33
+
34
+ See http://savonrb.com/version2/globals.html for more information on the logging
35
+ setting.
36
+
37
+ ## Usage
38
+
39
+ `mindbody-api` is laid out into two distinct parts: [services](https://github.com/wingrunr21/mindbody-api/tree/master/lib/mindbody-api/services) and [models](https://github.com/wingrunr21/mindbody-api/tree/master/lib/mindbody-api/models)
40
+
41
+ ### Services
42
+
43
+ The services are direct mirrors of those documented by MindBody in their [API
44
+ documentation](https://api.mindbodyonline.com/Doc). Required data arguments for
45
+ each service call are defined as arguments for the Ruby methods. Optional
46
+ arguments should be passed as a string based hash. For example:
47
+
48
+ client_id = 1234
49
+ response = MindBody::Services::ClientService.get_client_visits(client_id, 'StartDate' => Date.today, 'EndDate' => Date.today)
50
+ result = response.result
51
+ visits = result[:visits]
52
+
53
+ If any of the service calls need to be passed in a list of something (eg `Int32`
54
+ or `Int64`) then the option needs to be specified like this:
55
+
56
+ MindBody::Services::StaffService.get_staff('StaffIDs' => {'long' => [123415123, 123123213]})
57
+
58
+ ### Models
59
+
60
+ When a request comes back from MindBody, the various data types are hydrated
61
+ into models that are defined in the `MindBody::Models` namespace.
62
+
63
+ ## Versioning
64
+
65
+ In theory the gem would track the version of the MindBody API. In practice their
66
+ version number does not increment in a way that would make sense for a rubygem.
67
+ So, the gem itself uses [semver](http://semver.org/) and makes available a
68
+ constant `API_VERSION` that stores the version of the MindBody API.
69
+
70
+ ## Roadmap
71
+
72
+ See the various [issues](https://github.com/wingrunr21/mindbody-api/issues?state=open)
73
+
74
+ ## Credits
75
+
76
+ This gem is written by [Stafford Brunk](https://github.com/wingrunr21)
77
+
78
+ [![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/wingrunr21/mindbody-api/trend.png)](https://bitdeli.com/free "Bitdeli Badge")
79
+
@@ -0,0 +1,52 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ Bundler::GemHelper.install_tasks
4
+ require 'rspec/core/rake_task'
5
+ require 'dotenv/tasks'
6
+
7
+ # Rake tasks from https://github.com/mojombo/rakegem/blob/master/Rakefile
8
+
9
+ # Helper Functions
10
+ def name
11
+ @name ||= Dir['*.gemspec'].first.split('.').first
12
+ end
13
+
14
+ def version
15
+ line = File.read("lib/#{name}/version.rb")[/^\s*VERSION\s*=\s*.*/]
16
+ line.match(/.*VERSION\s*=\s*['"](.*)['"]/)[1]
17
+ end
18
+
19
+ # Standard tasks
20
+ RSpec::Core::RakeTask.new(:spec)
21
+ task :test => :spec
22
+ task :default => :spec
23
+
24
+ require 'rdoc/task'
25
+ Rake::RDocTask.new do |rdoc|
26
+ rdoc.rdoc_dir = 'rdoc'
27
+ rdoc.title = "#{name} #{version}"
28
+ rdoc.rdoc_files.include('README*')
29
+ rdoc.rdoc_files.include('lib/**/*.rb')
30
+ end
31
+
32
+ desc "Open an irb session preloaded with this library"
33
+ task :console => :dotenv do
34
+ sh "pry -Ilib -r #{name}.rb"
35
+ end
36
+
37
+ namespace :wsdl do
38
+ desc "Update the cached MindBody API wsdls"
39
+ task :update do
40
+ require 'open-uri'
41
+
42
+ url = "https://api.mindbodyonline.com/0_5/%sService.asmx?wsdl"
43
+ services = %w{Appointment Class Client Finder Sale Site Staff}
44
+
45
+ services.each do |service|
46
+ wsdl = open(url % service).read
47
+ File.open("wsdl/#{service}Service.wsdl", 'w') do |f|
48
+ f.write wsdl
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,35 @@
1
+ require 'active_support/inflector'
2
+ require 'active_support/core_ext/array/extract_options'
3
+
4
+ require 'mindbody-api/version'
5
+ require 'mindbody-api/models'
6
+ require 'mindbody-api/service'
7
+ require 'mindbody-api/api_status'
8
+
9
+ module MindBody
10
+ class << self
11
+ def configure
12
+ yield(configuration) if block_given?
13
+ end
14
+
15
+ def configuration
16
+ @configuration ||= Config.new
17
+ end
18
+ end
19
+
20
+ class Config
21
+ attr_accessor :log_level, :open_timeout, :read_timeout, :source_name, :source_key, :site_ids
22
+
23
+ def initialize
24
+ @log_level = :debug
25
+ @source_name = ENV['MINDBODY_SOURCE_NAME'] || ''
26
+ @source_key = ENV['MINDBODY_SOURCE_KEY'] || ''
27
+ @site_ids = (ENV['MINDBODY_SITE_IDS'] || '').scan(/-?\d+/).map(&:to_i)
28
+ end
29
+
30
+ # Make sure site_ids is always an Array
31
+ def site_ids=(ids)
32
+ @site_ids = [*ids]
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,109 @@
1
+ module MindBody
2
+ module APIStatus
3
+ class Base
4
+ attr_accessor :message
5
+
6
+ def initialize(message = '')
7
+ @message = message
8
+ end
9
+
10
+ def self.status
11
+ :undefined_error
12
+ end
13
+
14
+ def status
15
+ self.class.status
16
+ end
17
+
18
+ def self.error_code
19
+ 9999
20
+ end
21
+
22
+ def error_code
23
+ self.class.error_code
24
+ end
25
+ end
26
+
27
+ def self.build_status(code, status, message = nil)
28
+ # Double underscores makes sure Foo Bar and FooBar get
29
+ # the same treatment
30
+ underscore_status = status.underscore.parameterize.underscore
31
+ klass_name = underscore_status.camelize
32
+
33
+ klass = if const_defined?(klass_name)
34
+ const_get(klass_name)
35
+ else
36
+ define_api_status_constant(klass_name, underscore_status, code)
37
+ end
38
+
39
+ klass.new(message)
40
+ end
41
+
42
+ def self.define_api_status_constant(name, status, code)
43
+ klass = const_set(name, Class.new(Base))
44
+ klass.send(:define_singleton_method, :status) {status.to_sym}
45
+ klass.send(:define_singleton_method, :error_code) {code}
46
+ klass
47
+ end
48
+
49
+ # MindBody API code defintions
50
+ # Commented out codes have not been verified via the API.
51
+ # MindBody's own code definitions are not accurate and
52
+ # they are not willing to provide the correct mappings
53
+ # publically at this time
54
+ STATUS_CODES = {
55
+ #101 => "Invalid Source Credentials",
56
+ 102 => "InvalidCredentials",
57
+ #103 => "Invalid Staff Credentials",
58
+ #104 => "Invalid User Credentials",
59
+ 200 => "Success",
60
+ #201 => "Something failed in your call",
61
+ #301 => "Client ID doesn't exist",
62
+ #302 => "Invalid SQL Format",
63
+ #303 => "Client Index doesn't exist",
64
+ #304 => "Client Index Value doesn't exist",
65
+ #305 => "Missing Required Client Fields",
66
+ #306 => "Invalid Class ID",
67
+ #308 => "Related Client ID doesn't exist",
68
+ #309 => "Client Custom Field doesn't exist",
69
+ #310 => "Client Custom Field Value has an incorrect data type",
70
+ #311 => "Rep ID doesn't exist",
71
+ #312 => "Staff ID doesn't exist",
72
+ #313 => "Missing required fields",
73
+ #314 => "Session/Appointment Type ID doesn't exist",
74
+ 315 => "InvalidParameters",
75
+ #316 => "Appointment ID doesn't exist",
76
+ #317 => "Availabiliy ID doesn't exist",
77
+ #318 => "Invalid Payment Info",
78
+ #319 => "Staff not available",
79
+ #320 => "Client Service ID not found",
80
+ #321 => "Class Schedule ID doesn't exist",
81
+ #322 => "ContactLog Creation/Update Error",
82
+ #323 => "Unable to find client or found duplicate",
83
+ #324 => "Add/Update Staff error",
84
+ #501 => "Scheduling restrictions failed",
85
+ #502 => "Client is suspended from booking",
86
+ #503 => "Client needs payment",
87
+ #504 => "Client already booked",
88
+ #600 => "Class full",
89
+ #601 => "Class requires payment",
90
+ #602 => "Class is outside of scheduling window",
91
+ #603 => "Client already booked at this time",
92
+ #604 => "Client doesn't meet the prerequisites",
93
+ #605 => "Class capacity exceeded exception",
94
+ #606 => "Client already on waitlist",
95
+ #607 => "Waitlist full exception",
96
+ #700 => "Invalid booking time",
97
+ #800 => "Permission Exception",
98
+ #801 => "Staff member may not receive tips",
99
+ #900 => "RetailObject Exception",
100
+ #901 => "RetailItemNotFound Exception",
101
+ #1000 => "Location ID doesn't exist",
102
+ #1001 => "Setting Not Enabled on site",
103
+ #1002 => "PromotionCode Exception",
104
+ #1003 => "Resource Exception",
105
+ #2000 => "Finder user does not exist",
106
+ #2001 => "Finder user already exists"
107
+ }
108
+ end
109
+ end
@@ -0,0 +1,39 @@
1
+ require 'mindbody-api/response'
2
+
3
+ module MindBody
4
+ module Services
5
+ class Client < Savon::Client
6
+
7
+ def call(operation_name, locals = {}, &block)
8
+ # Inject the auth params into the request and setup the
9
+ # correct request structure
10
+ @globals.open_timeout(MindBody.configuration.open_timeout)
11
+ @globals.read_timeout(MindBody.configuration.read_timeout)
12
+ @globals.log_level(MindBody.configuration.log_level)
13
+ locals = locals.has_key?(:message) ? locals[:message] : locals
14
+ locals = fixup_locals(locals)
15
+ params = {:message => {'Request' => auth_params.merge(locals)}}
16
+
17
+ # Run the request
18
+ response = super(operation_name, params, &block)
19
+ Response.new(response)
20
+ end
21
+
22
+ private
23
+ def auth_params
24
+ {'SourceCredentials'=>{'SourceName'=>MindBody.configuration.source_name,
25
+ 'Password'=>MindBody.configuration.source_key,
26
+ 'SiteIDs'=>{'int'=>MindBody.configuration.site_ids}}}
27
+ end
28
+
29
+ def fixup_locals(locals)
30
+ # TODO this needs fixed to support various list types
31
+ locals.each_pair do |key, value|
32
+ if value.is_a? Array
33
+ locals[key] = {'int' => value}
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,53 @@
1
+ module MindBody
2
+ module SavonExt
3
+ module Service
4
+ private
5
+ # Defines a class-level SOAP operation.
6
+ def define_class_operation(operation, *options)
7
+ options = options.extract_options!
8
+ params = prepare_params(options)
9
+
10
+ class_operation_module.module_eval <<-RUBY_EVAL, __FILE__, __LINE__+1
11
+ def #{operation.to_s.snakecase}(#{params.join(',')})
12
+ req_hash = {
13
+ #{params.select{|p| p != 'locals = {}'}
14
+ .map{|p| "'#{params_key(p)}' => #{p}"}
15
+ .join(',')}
16
+ }
17
+ locals ||= {}
18
+ client.call #{operation.inspect}, :message => locals.merge(req_hash)
19
+ end
20
+ RUBY_EVAL
21
+ end
22
+
23
+ # Defines an instance-level SOAP operation.
24
+ # Defers to the class-level operation
25
+ def define_instance_operation(operation, *options)
26
+ options = options.extract_options!
27
+ params = prepare_params(options)
28
+
29
+ instance_operation_module.module_eval <<-RUBY_EVAL, __FILE__, __LINE__+1
30
+ def #{operation.to_s.snakecase}(#{params.join(',')})
31
+ self.class.#{operation.to_s.snakecase} #{params.join(',').chomp(' = {}')}
32
+ end
33
+ RUBY_EVAL
34
+ end
35
+
36
+ # Builds the array of params the
37
+ # generated method will utilize
38
+ def prepare_params(options)
39
+ params = options[:required] || []
40
+ params = params.map {|p| p.to_s.snakecase}
41
+ params.push 'locals = {}' if options[:locals].nil? || options[:locals]
42
+ params
43
+ end
44
+
45
+ # Maps the snakecase key to the
46
+ # correct MindBody upper case
47
+ def params_key(k)
48
+ key = k.to_s.camelize
49
+ key.gsub('Id', 'ID')
50
+ end
51
+ end
52
+ end
53
+ end