ruby-on-railsgrape-api

Grape API: Create endpoint programmatically


I have the very basic CRUD endpoints that are repeated over and over across my resources.

Currently I do have a Rails generator that helps me generating my API endpoints more quickly.

The obvious downside is that whenever I make a change to the template I need to update a bunch of files.

Since there is a very clear pattern for some of my endpoints, I was wondering if there was a way to generate them dynamically.

something like calling a method that calls desc, params etc...:

def delete(target)
  klass = target.camelize.constantize
  entity = V1::Entities.const_defined?(target.camelize) ? V1::Entities.get_const(target.camelize) : nil
  desc "Delete a #{target}", {
   http_codes: [
    { code: 204, message: "#{target} deleted successfully", model: entity},
    { code: 401, message: 'Unauthorized', model: Entities::ApiError },
    { code: 403, message: 'Forbidden', model: Entities::ApiError },
    { code: 404, message: 'Not Found', model: Entities::ApiError },
   ]
  }
  params do
   requires :id, type: Integer, documentation: entity.documentation[:id]
  end
  route_param :id do
   delete do 
    resource = klass.find_by(id: params[:id])
    must_have_record!(resource)
    authenticate! and authorize!("#{target.upcase}_DELETE".to_sym, resource)
    if resource.destroy
     present resource, with: entity
    else 
     destroy_error!
    end
   end
  end
 end

I know the above doesn't work and doesn't make sense. It was only an example to give the idea of what I have in mind.


Solution

  • I was able to create an end point programmatically by adding the following static method to my Base < Grape:API. It is important not to call the static method with already existing Grape::API class (such as get, post, delete, patch, put, ...):

    #######################################################################################
    ### AUTOMAGICALLY GENERATES AN API TO DESTROY A RESOURCE
    #######################################################################################
    def self.destroy(model)
      model_entity = model.const_defined?("Entity") ? model::Entity : nil
      resource model.to_s.underscore.pluralize do
        desc "Delete a #{model.to_s.downcase}", {
          http_codes: [
            { code: 204, message: "#{model.to_s.downcase} deleted successfully", model: model_entity },
            { code: 401, message: "Unauthorized", model: Entities::ApiError  },
            { code: 403, message: "Forbidden", model: Entities::ApiError },
            { code: 404, message: "Not Found", model: Entities::ApiError },
          ]
        }
        params do
          requires :id, type: Integer, documentation: model_entity && model_entity.documentation[:id] ? model_entity.documentation[:id] : { type: "Integer",  desc: "" }
        end
        route_param :id do
          delete do 
            resource = model.find_by(id: params[:id])
            must_have_record!(resource)
            authenticate! and authorize!(:"#{model.to_s.upcase}_DELETE", resource)
            if resource.destroy
              present resource, with: model_entity
            else 
              destroy_error!
            end
          end
        end
      end
    end