yext-api 0.1.1 → 0.1.3

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 (54) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +3 -1
  3. data/Gemfile.lock +16 -8
  4. data/PULL_REQUEST_TEMPLATE.md +11 -0
  5. data/README.md +25 -7
  6. data/app/controllers/yext/api/agreements/add_request_controller.rb +78 -0
  7. data/app/controllers/yext/api/application_controller.rb +1 -3
  8. data/config/initializers/memoist.rb +3 -0
  9. data/config/routes.rb +3 -0
  10. data/lib/config/api.yml +95 -230
  11. data/lib/yext/api/administrative_api/account.rb +37 -27
  12. data/lib/yext/api/administrative_api/add_request.rb +87 -3
  13. data/lib/yext/api/administrative_api/service.rb +26 -4
  14. data/lib/yext/api/concerns/account_child.rb +37 -8
  15. data/lib/yext/api/concerns/account_relations.rb +16 -1
  16. data/lib/yext/api/concerns/default_scopes.rb +8 -2
  17. data/lib/yext/api/concerns/faraday_connection.rb +21 -7
  18. data/lib/yext/api/concerns/rate_limits.rb +8 -1
  19. data/lib/yext/api/enumerations/add_request_status.rb +19 -0
  20. data/lib/yext/api/enumerations/location_type.rb +18 -0
  21. data/lib/yext/api/knowledge_api/account_settings/account.rb +0 -2
  22. data/lib/yext/api/knowledge_api/account_settings/role.rb +24 -0
  23. data/lib/yext/api/knowledge_api/account_settings/user.rb +81 -0
  24. data/lib/yext/api/knowledge_api/health_check/health.rb +0 -1
  25. data/lib/yext/api/knowledge_api/knowledge_manager/category.rb +0 -1
  26. data/lib/yext/api/knowledge_api/knowledge_manager/location.rb +18 -6
  27. data/lib/yext/api/live_api/location.rb +0 -4
  28. data/lib/yext/api/utils/api_base.rb +8 -0
  29. data/lib/yext/api/utils/api_finder.rb +100 -0
  30. data/lib/yext/api/utils/configuration.rb +93 -0
  31. data/lib/yext/api/utils/middleware/api_rate_limits.rb +35 -0
  32. data/lib/yext/api/utils/middleware/default_parameters.rb +138 -0
  33. data/lib/yext/api/utils/middleware/response_parser.rb +111 -0
  34. data/lib/yext/api/utils/middleware/uri_cleanup.rb +48 -0
  35. data/lib/yext/api/utils/params.rb +69 -0
  36. data/lib/yext/api/version.rb +1 -1
  37. data/yext-api.gemspec +6 -7
  38. metadata +56 -31
  39. data/.travis.yml +0 -5
  40. data/app/assets/config/yext_api_manifest.js +0 -2
  41. data/app/assets/images/yext/api/.keep +0 -0
  42. data/app/assets/javascripts/yext/api/application.js +0 -13
  43. data/app/assets/stylesheets/yext/api/application.css +0 -15
  44. data/app/docs/notes.txt +0 -45
  45. data/app/helpers/yext/api/application_helper.rb +0 -11
  46. data/app/jobs/yext/api/application_job.rb +0 -8
  47. data/app/mailers/yext/api/application_mailer.rb +0 -11
  48. data/app/models/yext/api/application_record.rb +0 -10
  49. data/app/views/layouts/yext/api/application.html.erb +0 -14
  50. data/lib/tasks/yext/api_tasks.rake +0 -6
  51. data/lib/yext/api/concerns/api_finder.rb +0 -61
  52. data/lib/yext/api/utils/api_rate_limits.rb +0 -36
  53. data/lib/yext/api/utils/default_parameters.rb +0 -57
  54. data/lib/yext/api/utils/response_parser.rb +0 -84
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Yext
4
+ module Api
5
+ module Enumerations
6
+ # An enumeration class to contain the allowable values for the status of an AddRequest
7
+ class AddRequestStatus
8
+ include Yext::Api::Concerns::EnumAll
9
+
10
+ SUBMITTED = "SUBMITTED"
11
+ PROCESSING = "PROCESSING"
12
+ COMPLETE = "COMPLETE"
13
+ CANCELED = "CANCELED"
14
+ REVIEW = "REVIEW"
15
+ FAILED = "FAILED"
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Yext
4
+ module Api
5
+ module Enumerations
6
+ # An enumeration class to contain the allowable values for the status of an AddRequest
7
+ class LocationType
8
+ include Yext::Api::Concerns::EnumAll
9
+
10
+ LOCATION = "LOCATION"
11
+ HEALTHCARE_PROFESSIONAL = "HEALTHCARE_PROFESSIONAL"
12
+ HEALTHCARE_FACILITY = "HEALTHCARE_FACILITY"
13
+ RESTAURANT = "RESTAURANT"
14
+ ATM = "ATM"
15
+ end
16
+ end
17
+ end
18
+ end
@@ -14,7 +14,6 @@ module Yext
14
14
  # :path_regex: v2/accounts
15
15
  # :default_version: 20161012
16
16
  # :documentation: http://developer.yext.com/docs/administrative-api/#operation/listAccounts
17
- # :comment: List all accounts that you have access to. Unless you are in Partner Portal mode, this will only be your own account.
18
17
  # :sandbox_only: false
19
18
  # - :action: :show
20
19
  # :method: :get
@@ -22,7 +21,6 @@ module Yext
22
21
  # :path_regex: v2/accounts/\w+
23
22
  # :default_version: 20161012
24
23
  # :documentation: http://developer.yext.com/docs/administrative-api/#operation/getAccount
25
- # :comment: Get details for an account.
26
24
  # :sandbox_only: false
27
25
  class Account < Yext::Api::Utils::ApiBase
28
26
  include Yext::Api::Concerns::AccountRelations
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Yext
4
+ module Api
5
+ module KnowledgeApi
6
+ module AccountSettings
7
+ # :administrative_api:
8
+ # :account_settings:
9
+ # :role:
10
+ # :actions:
11
+ # - :action: :index
12
+ # :method: :get
13
+ # :endpoint: https://api.yext.com/v2/accounts/{accountId}/roles
14
+ # :path_regex: v2/accounts/[^/]+?/roles
15
+ # :default_version: 20161012
16
+ # :documentation: http://developer.yext.com/docs/api-reference/#operation/getRoles
17
+ # :sandbox_only: false
18
+ class Role < Yext::Api::Utils::ApiBase
19
+ include Yext::Api::Concerns::AccountChild
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Yext
4
+ module Api
5
+ module KnowledgeApi
6
+ module AccountSettings
7
+ # :administrative_api:
8
+ # :account_settings:
9
+ # :user:
10
+ # :actions:
11
+ # - :action: :index
12
+ # :method: :get
13
+ # :endpoint: https://api.yext.com/v2/accounts/{accountId}/users
14
+ # :path_regex: v2/accounts/[^/]+?/users
15
+ # :default_version: 20161012
16
+ # :documentation: http://developer.yext.com/docs/api-reference/#operation/getUsers
17
+ # :sandbox_only: false
18
+ # - :action: :create
19
+ # :method: :post
20
+ # :endpoint: https://api.yext.com/v2/accounts/{accountId}/users
21
+ # :path_regex: v2/accounts/[^/]+?/users
22
+ # :default_version: 20161012
23
+ # :documentation: http://developer.yext.com/docs/api-reference/#operation/createUser
24
+ # :sandbox_only: false
25
+ # - :action: :show
26
+ # :method: :get
27
+ # :endpoint: https://api.yext.com/v2/accounts/{accountId}/users/{userId}
28
+ # :path_regex: v2/accounts/[^/]+?/users/[^/]+?
29
+ # :default_version: 20161012
30
+ # :documentation: http://developer.yext.com/docs/api-reference/#operation/getUser
31
+ # :sandbox_only: false
32
+ # - :action: :update
33
+ # :method: :put
34
+ # :endpoint: https://api.yext.com/v2/accounts/{accountId}/users/{userId}
35
+ # :path_regex: v2/accounts/[^/]+?/users/[^/]+?
36
+ # :default_version: 20161012
37
+ # :documentation: http://developer.yext.com/docs/api-reference/#operation/updateUser
38
+ # :sandbox_only: false
39
+ # - :action: :destroy
40
+ # :method: :delete
41
+ # :endpoint: https://api.yext.com/v2/accounts/{accountId}/users/{userId}
42
+ # :path_regex: v2/accounts/[^/]+?/users/[^/]+?
43
+ # :default_version: 20161012
44
+ # :documentation: http://developer.yext.com/docs/api-reference/#operation/deleteUser
45
+ # :sandbox_only: false
46
+ # - :action: :change_password
47
+ # :method: :put
48
+ # :endpoint: https://api.yext.com/v2/accounts/{accountId}/users/{userId}/password
49
+ # :path_regex: v2/accounts/[^/]+?/users/[^/]+?/password
50
+ # :default_version: 20161012
51
+ # :documentation: http://developer.yext.com/docs/api-reference/#operation/updateUserPassword
52
+ # :sandbox_only: false
53
+ class User < Yext::Api::Utils::ApiBase
54
+ include Yext::Api::Concerns::AccountChild
55
+
56
+ # Helper method to change password without a User object.
57
+ #
58
+ # Some example ways to change a password:
59
+ # Yext::Api::KnowledgeApi::AccountSettings::User.account(account_id).where(id: user_id).change_password!(new_password)
60
+ # Yext::Api::KnowledgeApi::AccountSettings::User.new(id: user_id, account_id: account_id).change_password!(new_password)
61
+ # user = Yext::Api::KnowledgeApi::AccountSettings::User.account(account_id).find(user_id)
62
+ # user.change_password!(new_password)
63
+ scope(:change_password!, lambda do |new_password|
64
+ args = scope_args
65
+
66
+ args[:newPassword] = new_password
67
+
68
+ Yext::Api::KnowledgeApi::AccountSettings::User.
69
+ where(args).
70
+ with(:password).
71
+ put
72
+ end)
73
+
74
+ def change_password!(new_password)
75
+ self.class.where(id: id, account_id: account_id).change_password!(new_password)
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
@@ -14,7 +14,6 @@ module Yext
14
14
  # :path_regex: v2/healthy
15
15
  # :default_version: 20161012
16
16
  # :documentation: http://developer.yext.com/docs/api-reference/#operation/healthCheck
17
- # :comment: The Health Check endpoint allows you to monitor the status of Yext's systems.
18
17
  # :sandbox_only: false
19
18
  class Health < Yext::Api::Utils::ApiBase
20
19
  uri("healthy")
@@ -13,7 +13,6 @@ module Yext
13
13
  # :endpoint: https://api.yext.com/v2/categories
14
14
  # :default_version: 20161012
15
15
  # :documentation: http://developer.yext.com/docs/api-reference/#operation/getBusinessCategories
16
- # :comment: Get available Categories.
17
16
  # :sandbox_only: false
18
17
  #
19
18
  # Class for fetching categories
@@ -14,7 +14,6 @@ module Yext
14
14
  # :path_regex: v2/accounts/[^/]+?/locations
15
15
  # :default_version: 20161012
16
16
  # :documentation: http://developer.yext.com/docs/api-reference/#operation/getLocations
17
- # :comment: Get multiple Locations (primary profiles only).
18
17
  # :sandbox_only: false
19
18
  # - :action: :create
20
19
  # :method: :post
@@ -22,7 +21,6 @@ module Yext
22
21
  # :path_regex: v2/accounts/[^/]+?/locations
23
22
  # :default_version: 20161012
24
23
  # :documentation: http://developer.yext.com/docs/api-reference/#operation/createLocation
25
- # :comment: Create a new Location.
26
24
  # :sandbox_only: false
27
25
  # - :action: :search
28
26
  # :method: :get
@@ -30,7 +28,6 @@ module Yext
30
28
  # :path_regex: v2/accounts/[^/]+?/locationsearch
31
29
  # :default_version: 20161012
32
30
  # :documentation: http://developer.yext.com/docs/api-reference/#operation/searchLocations
33
- # :comment: Get multiple Locations (primary profiles only) that match provided filters.
34
31
  # :sandbox_only: false
35
32
  # - :action: :show
36
33
  # :method: :get
@@ -38,7 +35,6 @@ module Yext
38
35
  # :path_regex: v2/accounts/[^/]+?/locations/[^/]+??
39
36
  # :default_version: 20161012
40
37
  # :documentation: http://developer.yext.com/docs/api-reference/#operation/getLocation
41
- # :comment: Gets the primary profile for a single Location.
42
38
  # :sandbox_only: false
43
39
  # - :action: :update
44
40
  # :method: :put
@@ -46,12 +42,28 @@ module Yext
46
42
  # :path_regex: v2/accounts/[^/]+?/locations/[^/]+??
47
43
  # :default_version: 20161012
48
44
  # :documentation: http://developer.yext.com/docs/api-reference/#operation/updateLocation
49
- # :comment: Updates the primary profile for a Location.
50
45
  # :sandbox_only: false
51
46
  class Location < Yext::Api::Utils::ApiBase
52
47
  include Yext::Api::Concerns::AccountChild
53
48
 
54
- # TODO: Custom endpoints
49
+ # http://developer.yext.com/docs/administrative-api/#operation/createExistingLocationAddRequest
50
+ def add_services!(skus:, agreement_id: nil, force_review: false)
51
+ args = { existingAccountId: account_id, existingLocationId: id }
52
+ args[:skus] = skus if skus.present?
53
+ args[:agreementId] = agreement_id if agreement_id.present?
54
+ args[:forceReview] = force_review if force_review.present?
55
+
56
+ Yext::Api::AdministrativeApi::AddRequest.add_services!(args)
57
+ end
58
+
59
+ # http://developer.yext.com/docs/administrative-api/#operation/cancelServices
60
+ def cancel_services!(agreement_id: nil, skus: nil)
61
+ args = { locationId: id }
62
+ args[:agreementId] = agreement_id if agreement_id.present?
63
+ args[:skus] = skus if skus.present?
64
+
65
+ Yext::Api::AdministrativeApi::Service.cancel_services!(args)
66
+ end
55
67
  end
56
68
  end
57
69
  end
@@ -15,9 +15,6 @@ module Yext
15
15
  # :path_regex: https://liveapi.yext.com/v2/accounts/[^/]+?/locations
16
16
  # :default_version: 20161012
17
17
  # :documentation: http://developer.yext.com/docs/live-api/#operation/locationsList
18
- # :comment: >
19
- # Get multiple Locations (primary profile only). Filters are evaluated against all language
20
- # profiles as well as the primary profile.
21
18
  # :sandbox_only: false
22
19
  # - :action: :show
23
20
  # :method: :get
@@ -25,7 +22,6 @@ module Yext
25
22
  # :path_regex: https://liveapi.yext.com/v2/accounts/[^/]+?/locations/[^/]+??
26
23
  # :default_version: 20161012
27
24
  # :documentation: http://developer.yext.com/docs/live-api/#operation/getLocation
28
- # :comment: Gets the primary profile for a single Location.
29
25
  # :sandbox_only: false
30
26
  class Location < Yext::Api::Utils::ApiBase
31
27
  include Yext::Api::Concerns::AccountChild
@@ -7,6 +7,14 @@ module Yext
7
7
  class ApiBase < Spyke::Base
8
8
  include Yext::Api::Concerns::FaradayConnection
9
9
  include Yext::Api::Concerns::DefaultScopes
10
+
11
+ before_save :ensure_create_id
12
+
13
+ private
14
+
15
+ def ensure_create_id
16
+ attributes[:create_id] ||= attributes[:id]
17
+ end
10
18
  end
11
19
  end
12
20
  end
@@ -0,0 +1,100 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Yext
4
+ module Api
5
+ module Utils
6
+ # This module add the `find_api` method to a class to allow the searching of the base
7
+ # api category module for a particular route.
8
+ class ApiFinder
9
+ class << self
10
+ def api_hash
11
+ @api_hash ||= YAML.load_file(Yext::Api::Engine.root.join("lib/config/api.yml"))
12
+ end
13
+ end
14
+
15
+ def initialize(uri_path, uri_method)
16
+ uri = URI.parse(uri_path.to_s)
17
+ uri.query = nil
18
+
19
+ @uri_path = uri.to_s
20
+ @uri_method = uri_method.to_s
21
+ end
22
+
23
+ def find_api
24
+ api = find_class_name.to_s.split("::")
25
+
26
+ return nil if api.length < 3
27
+
28
+ "Yext::Api::#{api[2]}".constantize
29
+ end
30
+
31
+ def find_class_name
32
+ api_hash = Yext::Api::Utils::ApiFinder.api_hash
33
+
34
+ return build_class_name("Yext::Api::LiveApi", api_hash[:live_api]) if uri_path.include?("//liveapi.")
35
+
36
+ api_hash.each do |api_class, module_details|
37
+ next if api_class == :webhooks
38
+
39
+ found_class = build_class_name("Yext::Api::#{api_class.to_s.classify}", module_details)
40
+
41
+ return found_class if found_class.present?
42
+ end
43
+
44
+ nil
45
+ end
46
+
47
+ private
48
+
49
+ attr_accessor :uri_path,
50
+ :uri_method
51
+
52
+ def build_class_name(built_name, module_details)
53
+ module_details.each do |sub_detail_name, sub_detail|
54
+ found_class = find_class_in_module(built_name, sub_detail_name, sub_detail)
55
+
56
+ return found_class unless found_class.nil?
57
+ end
58
+
59
+ nil
60
+ end
61
+
62
+ def find_class_in_module(built_name, sub_detail_name, sub_detail)
63
+ possible_name = construct_build_name(built_name, sub_detail_name, sub_detail)
64
+
65
+ if sub_detail_name == :objects
66
+ sub_detail.each do |class_name, class_actions|
67
+ return "#{possible_name}::#{class_name.to_s.classify}" if action_found?(class_actions[:actions])
68
+ end
69
+
70
+ nil
71
+ elsif sub_detail.is_a?(Hash)
72
+ build_class_name(possible_name, sub_detail)
73
+ end
74
+ end
75
+
76
+ def construct_build_name(built_name, sub_detail_name, sub_detail)
77
+ return built_name if %i[documentation objects].include?(sub_detail_name)
78
+
79
+ name = sub_detail_name.to_s.classify
80
+ name = sub_detail[:module_name] if sub_detail.is_a?(Hash) && sub_detail[:module_name].present?
81
+
82
+ "#{built_name}::#{name}"
83
+ end
84
+
85
+ def action_found?(actions)
86
+ return false if actions.blank?
87
+
88
+ actions.each do |action|
89
+ next if action[:path_regex].blank?
90
+ next if action[:method].to_s != uri_method
91
+
92
+ return true if Regexp.new("#{action[:path_regex]}$").match(uri_path)
93
+ end
94
+
95
+ false
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
@@ -55,6 +55,23 @@ module Yext
55
55
  #
56
56
  # sandbox
57
57
  # Boolean that indicates if the gem should use the production or sandbox URL.
58
+ #
59
+ # default_callback_processor
60
+ # The default class to be used to process webhook messages that are called.
61
+ # The code will warn about any missing webhook messages in a default processor.
62
+ # set_callback_processor(callback_function_name, processor)
63
+ # The class to be used to process webhook messages for a specific callback.
64
+ # If the method callback_function_name is not a public instance method for processor, an error
65
+ # will be raised.
66
+ #
67
+ # The processor class must be able to be instanciated with the following arguments:
68
+ # * meta - a hash of metadata for the webhook
69
+ # * object - a Spyke::Base object that represents the object that ws updated or changed
70
+ # to cause the hook event.
71
+ # * language_profiles - (optional) An array of objects.
72
+ #
73
+ # The functions that can be called for the webhooks are:
74
+ # * add_request_changed
58
75
  class Configuration
59
76
  include Singleton
60
77
 
@@ -79,8 +96,84 @@ module Yext
79
96
  @validation_level = value
80
97
  end
81
98
 
99
+ def param_account_id
100
+ account_id || "me"
101
+ end
102
+
103
+ def default_callback_processor=(value)
104
+ callback_names.each do |callback_name|
105
+ verify_method(value, callback_name)
106
+ end
107
+
108
+ callback_processors[:default] = value
109
+ end
110
+
111
+ def default_callback_processor
112
+ callback_processors[:default]
113
+ end
114
+
115
+ def set_callback_processor(callback_function_name, processor)
116
+ callback_function_name = callback_function_name.downcase.to_sym
117
+
118
+ validate_arguments(callback_function_name, processor)
119
+
120
+ if processor.present?
121
+ callback_processors[callback_function_name.downcase.to_sym] = processor
122
+ else
123
+ callback_processors.delete(callback_function_name.downcase.to_sym)
124
+ end
125
+ end
126
+
127
+ def get_callback_processor(callback_function_name)
128
+ callback_function_name = callback_function_name.downcase.to_sym
129
+
130
+ callback_processors.fetch(callback_function_name, callback_processors[:default])
131
+ end
132
+
82
133
  private
83
134
 
135
+ def validate_arguments(callback_function_name, processor)
136
+ raise ArgumentError, "invalid callback function #{callback_function_name}" unless callback_names.include?(callback_function_name)
137
+
138
+ return if verify_method(processor, callback_function_name)
139
+
140
+ raise ArgumentError, "#{processor.name} does not have a valid #{callback_function_name} function"
141
+ end
142
+
143
+ def callback_processors
144
+ @callback_processors ||= {}
145
+ end
146
+
147
+ def callback_names
148
+ %i[add_request_changed].dup
149
+ end
150
+
151
+ def verify_method(callback_class, method_name)
152
+ return true if callback_class.blank?
153
+ return true if valid_method_definition?(callback_class, method_name)
154
+
155
+ warning_messsage = "The callback_processor does not include a valid #{method_name} method."
156
+ if Object.const_defined?("Rails")
157
+ Rails.logger.warn warning_messsage
158
+ else
159
+ # :nocov:
160
+ puts warning_messsage
161
+ # :nocov:
162
+ end
163
+
164
+ false
165
+ end
166
+
167
+ def valid_method_definition?(callback_class, method_name)
168
+ return false if callback_class.public_instance_methods.grep(method_name).blank?
169
+
170
+ method = callback_class.instance_method(method_name)
171
+
172
+ return true if method.parameters.length.zero?
173
+
174
+ method.parameters.count { |param_details| %i[keyreq req].include?(param_details.first) }.zero?
175
+ end
176
+
84
177
  def read_from_environment_variables
85
178
  @sandbox = !Rails.env.production?
86
179