Wrap parameters in AngularJS service for update API request - ruby-on-rails

I'm trying to do update with AngularJS and API
Service: expense.js
angular
.module('timeTrackerApp.services',[])
.service('Expense', ['$resource', function ($resource) {
return $resource('/api/v1/expenses/:id', {id:'#id'}, {
'update': {
method: 'PUT'
},
'destroy': {
method: 'DELETE'
}
})
}])
Controller: expenses_controller.rb
def permitted_params
params.require(:expense).permit(:name, :price)
end
So expected JSON format is { expense: { name: "value", price: value } }
but i'm getting { name: "value", price: value }
So can anyone help me wrap this into root node ( expense ) ?

Rails automatically does wrap parameters when controller name matches a model name. Check doc.
If ever it fails, you can do it manually, in your controller:
wrap_parameters :expense, include: [:name, :price]
so if you receive:
{ name: 'name', price: 'price' }
Controller will give you:
{ name: 'name', price: 'price', expense: { name: 'name', price: 'price' } }
So do this server side since its neat and simple.

Related

Using rswag in Rails - what is the syntax for index responses (arrays)?

Unfortunately the "documentation" for rswag seems pretty lacking and doesn't give an example of how to implement an index action. My "create" spec displays the Schema and Example Value in the Swagger UI, but my "index" method isn't displaying either of those in the UI.
What do I need to change here? I've played around with it based on the limited examples I've found and none of them seem to work.
path '/api/v1/users' do
get('list users') do
tags 'Users'
response(200, 'successful') do
schema type: :array,
properties: {
id: { type: :integer },
title: { type: :string },
created_at: { type: :datetime},
updated_at: { type: :datetime}
}
after do |example|
example.metadata[:response][:content] = {
'application/json' => {
example: JSON.parse(response.body, symbolize_names: true)
}
}
end
run_test!
end
end
post('create user') do
tags 'Users'
consumes 'application/json'
parameter name: :user, in: :body, schema: {
type: :object,
properties: {
title: { type: :string }
},
required: [ 'title', 'description' ]
}
response(200, 'successful') do
after do |example|
example.metadata[:response][:content] = {
'application/json' => {
example: JSON.parse(response.body, symbolize_names: true)
}
}
end
run_test!
end
end
end
I have also tried to format the schema like so, based on another example I found, which didn't do anything either (schema/example just aren't displaying):-
schema type: :object,
properties: {
collection: {
type: :array,
items: {
type: :object,
properties: {
id: { type: :integer },
title: { type: :string },
created_at: { type: :datetime},
updated_at: { type: :datetime}
}
}
}
}
Check your swagger_helper file. If you've followed the documentation, it's probably something like that:
RSpec.configure do |config|
config.swagger_root = Rails.root.to_s + '/swagger'
config.swagger_docs = {
'v1/swagger.json' => {
openapi: '3.0.1',
info: {
title: 'API V1',
version: 'v1',
description: 'This is the first version of my API'
},
servers: [
{
url: 'https://{defaultHost}',
variables: {
defaultHost: {
default: 'www.example.com'
}
}
}
]
}
}
end
Just replace opeanapi: '3.0.1' for swagger: '2.0'. I've faced this same issue and this is the only workaround I've found so far.
What worked for me was using the produces method, like:
get 'Retrieves the lists' do
tags 'Lists'
produces 'application/json'
response '200', 'Lists found' do
schema type: :array,
items: {
type: :object,
properties: {
id: { type: :integer },
title: { type: :string },
created_at: { type: :datetime},
updated_at: { type: :datetime}
}
}
run_test!
end
end
#s89_

Falcor - HTTPDataSource to post Json

Is it possible to post a Json file using the falcor.browser's model? I have used the get method in it. Below is what I require, but it is not working.
<script src="./js/falcor.browser.js"></script>
function registerUser() {
var dataSource = new falcor.HttpDataSource("http://localhost/registerUser.json");
var model = new falcor.Model({
source: dataSource
});
var userJson = {"name":"John","age":"35","email":"john#abc.com"};
model.
set(userJson).
then(function(done){
console.log(done);
});
This is the server.js code:
app.use('/registerUser.json', falcorExpress.dataSourceRoute(function (req, res) {
return new Router([
{
route: "rating",
get: function() {
// Post call to external Api goes here
}
}
]);
}));
A few things:
The Model's set() method takes 1+ pathValues, so reformat your userJson object literal into a set of pathValues. Something like:
model.
set(
{ path: ['users', 'id1', 'name'], value: 'John' },
{ path: ['users', 'id1', 'age'], value: 35 },
{ path: ['users', 'id1', 'email'], value: 'john#abc.com' }
).
then(function(done){
console.log(done);
});
Second, your router must implement set handlers to correspond to the paths you are trying to set. These handlers should also return pathValues:
new Router([
{
route: 'users[{keys:ids}]["name", "age", "email"]',
set: function(jsonGraph) {
// jsonGraph looks like { users: { id1: { name: "John", age: 35, email: "john#abc.com" }
// make request to update name/age/email fields and return updated pathValues, e.g.
return [
{ path: ['users', 'id1', 'name'], value: 'John' },
{ path: ['users', 'id1', 'age'], value: 35 },
{ path: ['users', 'id1', 'email'], value: 'john#abc.com' },
];
}
}
]);
Given that your DB request is likely asynchronous, your route get handler will have to return a promise or observable. But the above should work as a demonstration.
Edit
You can also use route pattern matching on the third path key if the number of fields gets large, as was demonstrated above on the second id key.
{
route: 'users[{keys:ids}][{keys:fields}]',
set: function(jsonGraph) {
/* jsonGraph looks like
{
users: {
id1: { field1: "xxx", field2: "yyy", ... },
id1: { field1: "xxx", field2: "yyy", ... },
...
}
}
*/
}
}

How to send params from nested forms?

I'm making a POST request from a nested form which is written in reactjs such that it is making an ajax request to create method of the products controller.
React Code:
I have an empty object in the getInitialState like this
getInitialState: function() {
return {
products: [{name: '', price: '', quantity: ''}],
count: 1
};
},
When i submit the form,
handleSubmit: function(e) {
e.preventDefault();
var productsArray = this.state.products;
$.ajax({
data: {
product: productsArray
},
url: '',
type: "POST",
dataType: "json",
success: function ( data ) {
console.log(data);
// this.setState({ comments: data });
}.bind(this)
});
},
the object gets populated and the parameter hash becomes like this
Parameters: {"product"=>{"0"=>{"name"=>"", "price"=>"", "quantity"=>""}}, "shop_id"=>"gulshop"}
So i'm getting
ActiveRecord::UnknownAttributeError (unknown attribute '0' for Product.):
How can i get the parameter hash like this:
Parameters: {"product"=>[{"name"=>"", "price"=>"", "quantity"=>""}], "shop_id"=>"gulshop"}
What can be done for it ?
Your original error 'unknown attribute '0' for Product.' is because the Product class does not have an attribute '0'. I'm not sure where the '0' is coming from as you haven't posted your react code that makes the request.
You can easily make a request from your component using jQuery's .ajax method. e.g
$.ajax({
type: 'POST',
url: '/your_url',
data: {
course: {
name: 'Hello World',
price: 120
}
}
});
You would then have something like the following in your controller..
class ProductController < ApplicationController
def create
#product = Product.create(product_params)
end
private
def product_params
params.require(:product).permit(:name, :price)
end
end

Wildcard for nested params

Is there a way I can test for both these params to be true? Some sort of wildcard value for the first key?
params[:book][:return_to]
params[:work][:return_to]
At the moment I'm having to do:
if params[:book] and params[:book][:return_to]
# blah
elsif params[:work] and params[:work][:return_to]
# blah
Hashie is the solution I've used.
https://github.com/intridea/hashie
From their readme:
user = {
name: { first: 'Bob', last: 'Boberts' },
groups: [
{ name: 'Rubyists' },
{ name: 'Open source enthusiasts' }
]
}
user.extend Hashie::Extensions::DeepFind
user.deep_find(:name) #=> { first: 'Bob', last: 'Boberts' }

How to pass a Hash to Grape API method?

I'm having problems with the Grape gem and the parameters validation.
The idea behind this is to create a complex entity using nested attributes through an API service.
I have a method to create a trip, trip have many destinations and i want to pass that destinations using a hash (using the accepts_nested_attributes_for helper).
I have this grape restriction over the parameter:
requires :destinations, type: Hash
And I'm trying to send something like this:
{ destinations => [
{ destination: { name => 'dest1'} },
{ destination: { name => 'dest2'} },
{ destination: { name => 'dest3'} }
]}
In order to build something like the structure below inside the method and get the trip created:
{ trip: {
name: 'Trip1', destinations_attributes: [
{ name: 'dest1' },
{ name: 'dest2' },
{ name: 'dest3' }
]
}}
I'm using POSTMAN chrome extension to call the API method.
Here's a screen capture:
If someone can help me i would be very grateful.
By the looks of what you are trying to send, you need to change the Grape restriction, because destinations is an Array, not a Hash:
requires :destinations, type: Array
You don't need the "destination" hash when sending the request:
{ destinations => [
{ name => 'dest1', other_attribute: 'value', etc... },
{ name => 'dest2', other_attribute: 'value', etc... },
{ name => 'dest3', other_attribute: 'value', etc... }
]}
This creates an Array of hashes.
In order to send this through POSTMAN, you'll need to modify that destinations param your sending and add multiple lines in POSTMAN. Something like:
destinations[][name] 'dest1'
destinations[][other_attribute] 'value1'
destinations[][name] 'dest2'
destinations[][other_attribute] 'value2'
destinations[][name] 'dest3'
destinations[][other_attribute] 'value3'
Hope this answers your questions. Let me know if this is what you were looking for.

Resources