mirror of
https://github.com/Growstuff/growstuff.git
synced 2026-11-01 10:49:12 -05:00
Tidy API resources: read_only and owned macros, private hook helpers
Declare read-only resources and the owner relationship once each, make the resource hook helpers private, simplify token auth, and cache the generated API docs for an hour.
This commit is contained in:
1 parent
086140205d
commit
1135c763de
12 files changed
+52
-49
No files matched your search
@@ -4,6 +4,7 @@ module Api
|
||||
# The API's own description, for Swagger UI at /api-docs.
|
||||
class DocsController < ActionController::API
|
||||
def show
|
||||
expires_in 1.hour, public: true
|
||||
render json: ApiDocs.openapi
|
||||
end
|
||||
end
|
||||
|
||||
@@ -39,7 +39,7 @@ module Api
|
||||
|
||||
def update
|
||||
resource = resource_class.find(params)
|
||||
# As it is now; the resource hook then checks what it would become.
|
||||
# The record as it is; the resource then checks what the changes would make of it.
|
||||
authorize! :update, resource.data
|
||||
render_write resource, resource.update_attributes
|
||||
end
|
||||
@@ -89,12 +89,7 @@ module Api
|
||||
|
||||
def authenticate_member_from_token!
|
||||
authenticate_with_http_token do |token, _options|
|
||||
auth = Authentication.find_by(token: token, provider: 'api')
|
||||
if auth.present?
|
||||
@current_user = auth.member
|
||||
|
||||
return true
|
||||
end
|
||||
@current_user = Authentication.find_by(token: token, provider: 'api')&.member
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -31,19 +31,6 @@ class ApplicationResource < Graphiti::Resource
|
||||
context.authorize!(:destroy, record) if written?(record)
|
||||
end
|
||||
|
||||
def written?(record)
|
||||
record.instance_of?(context.resource_class.model)
|
||||
end
|
||||
|
||||
# A foreign key that points at nothing is a request for something that is not there, not a server error.
|
||||
def linked_records_exist!(record)
|
||||
record.class.reflect_on_all_associations(:belongs_to).reject(&:polymorphic?).each do |association|
|
||||
next if record[association.foreign_key].blank? || record.association(association.name).reader
|
||||
|
||||
raise ActiveRecord::RecordNotFound, "Couldn't find #{association.klass.name} with id #{record[association.foreign_key]}"
|
||||
end
|
||||
end
|
||||
|
||||
# Photos are attached to plantings, gardens and harvests through the polymorphic photo_associations table.
|
||||
# This is the `photos` relationship of one of those; filter[photo_id] on that resource is how photos link back.
|
||||
def self.photos_of(type)
|
||||
@@ -81,4 +68,30 @@ class ApplicationResource < Graphiti::Resource
|
||||
def self.filter_only(name, type)
|
||||
attribute name, type, readable: false, writable: false
|
||||
end
|
||||
|
||||
# Read only: it can be listed and shown, and nothing else.
|
||||
def self.read_only
|
||||
primary_endpoint "/#{type}", %i(index show)
|
||||
end
|
||||
|
||||
# The member a record belongs to. It is the member holding the token that wrote it, so it is never set by a request.
|
||||
def self.owned
|
||||
filter_only :owner_id, :integer
|
||||
belongs_to :owner, resource: MemberResource
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def written?(record)
|
||||
record.instance_of?(context.resource_class.model)
|
||||
end
|
||||
|
||||
# A foreign key that points at nothing is a request for something that is not there, not a server error.
|
||||
def linked_records_exist!(record)
|
||||
record.class.reflect_on_all_associations(:belongs_to).reject(&:polymorphic?).each do |association|
|
||||
next if record[association.foreign_key].blank? || record.association(association.name).reader
|
||||
|
||||
raise ActiveRecord::RecordNotFound, "Couldn't find #{association.klass.name} with id #{record[association.foreign_key]}"
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -2,15 +2,15 @@
|
||||
|
||||
class CropResource < ApplicationResource
|
||||
self.model = Crop
|
||||
primary_endpoint '/crops', %i(index show)
|
||||
read_only
|
||||
secondary_endpoint '/crops/search', [:search]
|
||||
|
||||
attribute :name, :string, writable: false
|
||||
attribute :en_wikipedia_url, :string, writable: false
|
||||
attribute :perennial, :boolean, writable: false
|
||||
attribute :median_lifespan, :integer, writable: false
|
||||
attribute :median_days_to_first_harvest, :integer, writable: false
|
||||
attribute :median_days_to_last_harvest, :integer, writable: false
|
||||
attribute :name, :string
|
||||
attribute :en_wikipedia_url, :string
|
||||
attribute :perennial, :boolean
|
||||
attribute :median_lifespan, :integer
|
||||
attribute :median_days_to_first_harvest, :integer
|
||||
attribute :median_days_to_last_harvest, :integer
|
||||
filter_only :approval_status, :string
|
||||
|
||||
default_filter :approval_status do |scope|
|
||||
|
||||
@@ -7,10 +7,9 @@ class GardenResource < ApplicationResource
|
||||
filter_only :slug, :string
|
||||
filter_only :active, :boolean
|
||||
filter_only :location, :string
|
||||
filter_only :owner_id, :integer
|
||||
filter_only :garden_type_id, :integer
|
||||
|
||||
belongs_to :owner, resource: MemberResource
|
||||
owned
|
||||
has_many :plantings
|
||||
photos_of 'Garden'
|
||||
end
|
||||
@@ -12,12 +12,11 @@ class HarvestResource < ApplicationResource
|
||||
|
||||
attribute :crop_id, :integer, readable: false
|
||||
attribute :planting_id, :integer, readable: false
|
||||
filter_only :owner_id, :integer
|
||||
filter_only :plant_part_id, :integer
|
||||
|
||||
belongs_to :crop
|
||||
belongs_to :planting
|
||||
belongs_to :owner, resource: MemberResource
|
||||
owned
|
||||
photos_of 'Harvest'
|
||||
|
||||
before_save do |harvest|
|
||||
|
||||
@@ -12,8 +12,7 @@ class JobResource < ApplicationResource
|
||||
attribute :action, :string
|
||||
|
||||
attribute :garden_id, :integer, readable: false
|
||||
filter_only :owner_id, :integer
|
||||
|
||||
belongs_to :garden
|
||||
belongs_to :owner, resource: MemberResource
|
||||
owned
|
||||
end
|
||||
@@ -2,10 +2,10 @@
|
||||
|
||||
class MemberResource < ApplicationResource
|
||||
self.model = Member
|
||||
primary_endpoint '/members', %i(index show)
|
||||
read_only
|
||||
|
||||
attribute :login_name, :string, writable: false
|
||||
attribute :slug, :string, writable: false
|
||||
attribute :login_name, :string
|
||||
attribute :slug, :string
|
||||
|
||||
has_many :gardens, foreign_key: :owner_id
|
||||
has_many :plantings, foreign_key: :owner_id
|
||||
|
||||
@@ -2,19 +2,18 @@
|
||||
|
||||
class PhotoResource < ApplicationResource
|
||||
self.model = Photo
|
||||
primary_endpoint '/photos', %i(index show)
|
||||
read_only
|
||||
|
||||
attribute :thumbnail_url, :string, writable: false
|
||||
attribute :fullsize_url, :string, writable: false
|
||||
attribute :license_name, :string, writable: false
|
||||
attribute :link_url, :string, writable: false
|
||||
attribute :title, :string, writable: false
|
||||
filter_only :owner_id, :integer
|
||||
attribute :thumbnail_url, :string
|
||||
attribute :fullsize_url, :string
|
||||
attribute :license_name, :string
|
||||
attribute :link_url, :string
|
||||
attribute :title, :string
|
||||
|
||||
filter :crop_id, :integer do
|
||||
eq { |scope, ids| scope.where(id: PhotoAssociation.where(crop_id: ids).select(:photo_id)) }
|
||||
end
|
||||
|
||||
belongs_to :owner, resource: MemberResource
|
||||
owned
|
||||
%w(Planting Garden Harvest).each { |type| photographed type }
|
||||
end
|
||||
@@ -31,7 +31,6 @@ class PlantingResource < ApplicationResource
|
||||
|
||||
attribute :crop_id, :integer, readable: false
|
||||
attribute :garden_id, :integer, readable: false
|
||||
filter_only :owner_id, :integer
|
||||
|
||||
filter :active, :boolean do
|
||||
eq { |scope, value| value ? scope.active : scope }
|
||||
@@ -42,7 +41,7 @@ class PlantingResource < ApplicationResource
|
||||
|
||||
belongs_to :garden
|
||||
belongs_to :crop
|
||||
belongs_to :owner, resource: MemberResource
|
||||
owned
|
||||
has_many :harvests
|
||||
photos_of 'Planting'
|
||||
end
|
||||
@@ -14,8 +14,7 @@ class SeedResource < ApplicationResource
|
||||
attribute :heirloom, :string
|
||||
|
||||
attribute :crop_id, :integer, readable: false
|
||||
filter_only :owner_id, :integer
|
||||
|
||||
belongs_to :crop
|
||||
belongs_to :owner, resource: MemberResource
|
||||
owned
|
||||
end
|
||||
@@ -98,7 +98,7 @@ Rails.application.configure do
|
||||
ActionMailer::Base.delivery_method = :smtp
|
||||
|
||||
config.host = ENV.fetch('HOST', nil)
|
||||
config.x.api_base_url = "https://#{config.host}" if config.host.present? && ENV['API_BASE_URL'].blank?
|
||||
config.x.api_base_url = ENV.fetch('API_BASE_URL') { "https://#{config.host}" }
|
||||
config.analytics_code = <<-GET_CLICKY_SCRIPT
|
||||
<script src="//static.getclicky.com/js" type="text/javascript"></script>
|
||||
<script type="text/javascript">try{ clicky.init(100594260); }catch(e){}</script>
|
||||
|
||||
Reference in new issue
Block a user