For database performance reasons, we are keeping ID (primary key). For our client-end apps, we use UUID for security reasons. The UUID is not a primary key but it is used as a finder to find the single record.
For example, both use cases are valid:
http://janedoe.com/v1/people/6 #=> valid
http://janedoe.com/v1/people/7228ba24-336e-4edb-948c-b0531135aceb #=> also valid
What is the best way to handle that -- the flexibility to take in either an ID (for internal use only, such as our internal CRM webapp) or UUID (for our public webapp)?
@chadwtaylor you can do something like this:
module V1
class PersonResource < JSONAPI::Resource
UUID_REGEXP = /\A[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}\z/f
INT_REGEXP = /\A\d+\z/f
def id
context[:internal] ? @model.id : @model.public_uuid
end
filter :id, verify: :verify_keys, apply: ->(records, values, options) do
col = options[:context][:internal] ? :id : :public_uuid
records.where(col => values)
end
def self.verify_key(key, context = nil)
return if key.nil?
regexp = context[:internal] ? INT_REGEXP : UUID_REGEXP
if regexp =~ key.to_s
key
else
raise JSONAPI::Exceptions::InvalidFieldValue.new(:id, key)
end
end
def self.sortable_fields(context)
fields = super
# why somebody ever need to sort by uuid?)
context[:internal] ? fields : (fields - [:id])
end
end
@senid231 Many thanks! I will give this a spin and keep you posted. I do appreciate you taking the time to share the code here for folks like me.
Here's my final code based on the sample from @senid231 (thank you!) that allows us to be able to interchangeably find resources using id or uuid. I integrated the above sample into BaseResource that can be extended into other resources (eg: PersonResource, ProviderResource) that would benefit from it.
module Api
module V1
class BaseResource < JSONAPI::Resource
UUID_REGEX = /\A[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89aAbB][a-f0-9]{3}-[a-f0-9]{12}\z/
INT_REGEX = /\A\d+\z/
filter :id, :verify => :verify_keys, apply: -> (records, values, options) do
col = values.uuid? ? :uuid : :id # refer to String extension using .uuid?
records.where(col => values)
end
def id
@model.respond_to?(:uuid) ? @model.uuid : @model.id
end
def self.verify_key(key, context = nil)
return if key.nil?
regex = key.uuid? ? UUID_REGEX : INT_REGEX # refer to String extension using .uuid?
return key if regex =~ key.to_s
raise JSONAPI::Exceptions::InvalidFieldValue.new(:id, key)
end
end
end
end
class String
def uuid?
regex = /\A[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89aAbB][a-f0-9]{3}-[a-f0-9]{12}\z/
return regex =~ self
end
end
Here's a slightly more compact regex literal using ruby's \h character class, ftw.
UUID_V4_REGEX = /^\h{8}-\h{4}-4\h{3}-[89AaBb]\h{3}-\h{12}$/
Most helpful comment
Here's my final code based on the sample from @senid231 (thank you!) that allows us to be able to interchangeably find resources using
idoruuid. I integrated the above sample intoBaseResourcethat can be extended into other resources (eg: PersonResource, ProviderResource) that would benefit from it.