api_version.rb 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. # frozen_string_literal: true
  2. module ShopifyAPI
  3. class ApiVersion
  4. class UnknownVersion < StandardError; end
  5. class ApiVersionNotSetError < StandardError; end
  6. include Comparable
  7. HANDLE_FORMAT = /^\d{4}-\d{2}$/.freeze
  8. UNSTABLE_HANDLE = 'unstable'
  9. UNSTABLE_AS_DATE = Time.utc(3000, 1, 1)
  10. API_PREFIX = '/admin/api/'
  11. LOOKUP_MODES = [:raise_on_unknown, :define_on_unknown].freeze
  12. class << self
  13. attr_reader :versions
  14. def version_lookup_mode
  15. @version_lookup_mode ||= :define_on_unknown
  16. end
  17. def version_lookup_mode=(mode)
  18. raise ArgumentError, "Mode must be one of #{LOOKUP_MODES}" unless LOOKUP_MODES.include?(mode)
  19. sanitize_known_versions if mode == :raise_on_unknown
  20. @version_lookup_mode = mode
  21. end
  22. def find_version(version_or_handle)
  23. return version_or_handle if version_or_handle.is_a?(ApiVersion)
  24. handle = version_or_handle.to_s
  25. @versions ||= {}
  26. @versions.fetch(handle) do
  27. if @version_lookup_mode == :raise_on_unknown
  28. raise UnknownVersion, unknown_version_error_message(handle)
  29. else
  30. add_to_known_versions(ApiVersion.new(handle: handle))
  31. end
  32. end
  33. end
  34. def coerce_to_version(version_or_handle)
  35. warn(
  36. '[DEPRECATED] ShopifyAPI::ApiVersion.coerce_to_version be removed in a future version. ' \
  37. 'Use `find_version` instead.'
  38. )
  39. find_version(version_or_handle)
  40. end
  41. def fetch_known_versions
  42. @versions = Meta.admin_versions.map do |version|
  43. [version.handle, ApiVersion.new(version.attributes.merge(verified: version.persisted?))]
  44. end.to_h
  45. end
  46. def define_known_versions
  47. warn(
  48. '[DEPRECATED] ShopifyAPI::ApiVersion.define_known_versions is deprecated and will be removed in a future version. ' \
  49. 'Use `fetch_known_versions` instead.'
  50. )
  51. fetch_known_versions
  52. end
  53. def add_to_known_versions(version)
  54. @versions[version.handle] = version
  55. end
  56. def clear_known_versions
  57. @versions = {}
  58. end
  59. def clear_defined_versions
  60. warn(
  61. '[DEPRECATED] ShopifyAPI::ApiVersion.clear_defined_versions is deprecated and will be removed in a future version. ' \
  62. 'Use `clear_known_versions` instead.'
  63. )
  64. clear_known_versions
  65. end
  66. def latest_stable_version
  67. warn(
  68. '[DEPRECATED] ShopifyAPI::ApiVersion.latest_stable_version is deprecated and will be removed in a future version.'
  69. )
  70. versions.values.find(&:latest_supported?)
  71. end
  72. private
  73. def sanitize_known_versions
  74. return if @versions.nil?
  75. @versions = @versions.keys.map do |handle|
  76. next unless @versions[handle].verified?
  77. [handle, @versions[handle]]
  78. end.compact.to_h
  79. end
  80. def unknown_version_error_message(handle)
  81. msg = "ApiVersion.version_lookup_mode is set to `:raise_on_unknown`. \n"
  82. return msg + "No versions defined. You must call `ApiVersion.fetch_known_versions` first." if @versions.empty?
  83. msg + "`#{handle}` is not in the defined version set. Available versions: #{@versions.keys}"
  84. end
  85. end
  86. attr_reader :handle, :display_name, :supported, :latest_supported, :verified
  87. def initialize(attributes)
  88. attributes = ActiveSupport::HashWithIndifferentAccess.new(attributes)
  89. @handle = attributes[:handle].to_s
  90. @display_name = attributes.fetch(:display_name, attributes[:handle].to_s)
  91. @supported = attributes.fetch(:supported, false)
  92. @latest_supported = attributes.fetch(:latest_supported, false)
  93. @verified = attributes.fetch(:verified, false)
  94. end
  95. def to_s
  96. handle
  97. end
  98. def latest_supported?
  99. latest_supported
  100. end
  101. def supported?
  102. supported
  103. end
  104. def verified?
  105. verified
  106. end
  107. def <=>(other)
  108. handle_as_date <=> other.handle_as_date
  109. end
  110. def ==(other)
  111. other.class == self.class && handle == other.handle
  112. end
  113. def hash
  114. handle.hash
  115. end
  116. def construct_api_path(path)
  117. "#{API_PREFIX}#{handle}/#{path}"
  118. end
  119. def construct_graphql_path
  120. construct_api_path('graphql.json')
  121. end
  122. def name
  123. warn(
  124. '[DEPRECATED] ShopifyAPI::ApiVersion#name is deprecated and will be removed in a future version. ' \
  125. 'Use `handle` instead.'
  126. )
  127. handle
  128. end
  129. def stable?
  130. warn(
  131. '[DEPRECATED] ShopifyAPI::ApiVersion#stable? is deprecated and will be removed in a future version. ' \
  132. 'Use `supported?` instead.'
  133. )
  134. supported?
  135. end
  136. def unstable?
  137. handle == UNSTABLE_HANDLE
  138. end
  139. def handle_as_date
  140. return UNSTABLE_AS_DATE if unstable?
  141. year, month, day = handle.split('-')
  142. Time.utc(year, month, day)
  143. end
  144. class NullVersion
  145. class << self
  146. def raise_not_set_error(*_args)
  147. raise ApiVersionNotSetError, "You must set ShopifyAPI::Base.api_version before making a request."
  148. end
  149. alias_method :stable?, :raise_not_set_error
  150. alias_method :construct_api_path, :raise_not_set_error
  151. alias_method :construct_graphql_path, :raise_not_set_error
  152. alias_method :latest_supported?, :raise_not_set_error
  153. alias_method :supported?, :raise_not_set_error
  154. alias_method :verified?, :raise_not_set_error
  155. alias_method :unstable?, :raise_not_set_error
  156. alias_method :handle, :raise_not_set_error
  157. alias_method :display_name, :raise_not_set_error
  158. alias_method :supported, :raise_not_set_error
  159. alias_method :verified, :raise_not_set_error
  160. alias_method :latest_supported, :raise_not_set_error
  161. alias_method :name, :raise_not_set_error
  162. end
  163. end
  164. end
  165. end