autoforce / APIcasso

An abstract API design as a Rails-based mountable engine
MIT License
32 stars 1 forks source link

Problems with nested resources and custom queries #89

Open AlejandroVirlan opened 4 years ago

AlejandroVirlan commented 4 years ago

I've been performing queries from the endpoint without nested_resources and it worked fine, but when I've tried to perform them with nested_resources I couldn't make it work. I got this error:

NoMethodError in Apicasso::CrudController#nested_index

undefined method `incidence_trackings' for #<Incidence:0x007efe1810bea0> Did you mean? incidence_tracking incidence_tracking= incidence_tracking_ids incidence_tracking_ids=

This is the endpoint that I tested:

http://localhost:3000/api/v1/incidences/1/incidence_trackings

I need to perform a custom query so the API returns the expected results; I've been reading your documentation and there is a section called "Extending base API actions", which I've tried and couldn't make it work. In this case that didn't work I got the BadRequest error (Bad hacker, stop bullying or I'll tell your mom!).

It seems that I need an id and a nested_resource.

I've tried using any id from my resource, even if I don't need it, and it doesn't work either. This is the endpoint that I tested:

http://localhost:3000/api/v1/incidences/1/custom

Processing by Apicasso::CrudController#nested_index as */*
  Parameters: {"resource"=>"incidences", "id"=>"2", "nested"=>"custom"}
Completed 400 Bad Request in 1ms (ActiveRecord: 0.0ms)

Can I make an endpoint which doesn't require an id with a custom query.

This is my code:

routes.rb

Rails.application.routes.draw do

  match '/:resource/:id/custom' => 'apicustom#custom', via: :get
  mount Apicasso::Engine, at: "/api/v1"

end

api_controller.rb

class ApiController < ApplicationController
  Apicasso::Key.create(scope:
                           { read:
                                 {
                                   incidence: true,
                                   phone_identifier: true,
                                   interest_point: true,
                                   incidence_type: true,
                                   incidence_tracking: true
                                 },
                             create:
                                 {
                                   incidence: true,
                                   phone_identifier: true
                                 }
                           })
  end

apicustom_controller.rb

class ApicustomController < Apicasso::CrudController

  def custom
    render json: Incidence.find_by_sql("SELECT inc.id, inc.image_url, inc.description, it.status
                                        FROM incidences inc, (SELECT id, status, date, incidence_id
                                                              FROM incidence_trackings it1
                                                              WHERE date = (SELECT MAX(date)
                                                                            FROM incidence_trackings it2
                                                                            WHERE it1.incidence_id = it2.incidence_id)) it
                                        WHERE inc.id = it.incidence_id;")

  end

end

Thanks for your time.

ErvalhouS commented 4 years ago

Hello @AlejandroVirlan could you please attach at least the code of how those models relate? Seems to me you have a has_many declaration with a singularized name. You could always do a i18n inference if you need it to respond with that name.

Either way, declaring a route like this:

match '/:resource/:id/custom' => 'apicustom#custom', via: :get

Yields a URL like this

http://localhost:3000/incidences/1/custom

Which is different from what you have posted that you tried consuming:

http://localhost:3000/api/v1/incidences/1/custom

This makes the API think you are consuming a regular nested resource with a model name of custom on the nested index action, and you probably don't have a model with that name 😂

You could edit the route to reflect the URL that you are consuming with:

match '/api/v1/:resource/:id/custom' => 'apicustom#custom', via: :get