I need to pass extra data from the show action in the controller to the serializer on rendere method.
I have a render like this:
render json: test, serializer: TestSerializer
The json that is returned is something like this:
{
"test": {
"id": 1,
"name": "Name",
"surname": "Surname",
}
}
I need to pass extra data to this serializer in order to have a json like this:
render json: test, serializer: TestSerializer, extradata: extradata
{
"test": {
"id": 1,
"name": "Name",
"surname": "Surname",
"extradata": {
"first": 1,
"second": 12
}
}
}
Can anyone help me?
Thanks in advance.
I have done this to resolve my problem very quickly:
Controller.rb before
render json: test, serializer: TestSerializer
Json before
{
"test": {
"id": 1,
"name": "Name",
"surname": "Surname",
}
}
Controller.rb after
extradata = "test text"
render json: test, serializer: TestSerializer, extradata: extradata
Json after
{
"test": {
"id": 1,
"name": "Name",
"surname": "Surname",
"extradata": "test text"
}
}
Related
I have been trying to integrate an API on a rails app.
It is structured in such a way that I have to write my own POST API to communicate back and forth with it.
For example: POST dividebuy/api/getorderdetails
Example Request:
{
"orderId":"6667",
"retailerStoreCode":"default",
"storeAuthentication":"5LIH1TaW8ewd",
"storeToken":"3aa7Sgt76sz7"
}
Example Response:
{
"order_detail": {
"store_order_id": "64",
"store_order_increment_id": "145006485",
"store_token": ENV['DIVIDEBUY_TOKEN'],
"store_authentication": ENV['DIVIDEBUY_AUTHENTICATION'],
"logo_url": "https://moduleinstalledmagento1.dbuytest.info/media/dividebuy/",
"grand_total": 318.4,
"subtotal": 265.33,
"subtotalInclVat": 318.4,
"discount": 0,
"discountApplied": "beoforeVat",
"shipping": 0,
"shippingInclVat": 0,
"shipping_label": "Free Shipping - Free",
"shipping_method": "freeshipping_freeshipping",
"is_default_shipping": 0,
"is_default_billing": 0,
"vat": 53.07
},
"product_details": [
{
"name": "Some product name",
"sku": "SKU",
"qty": "1.0000",
"price": "249.1700",
"priceInclVat": "299.0000",
"rowTotal": "249.1700",
"rowTotalInclVat": "299.0000",
"discount": "0.0000",
"short_description": "Some Product",
"product_type": "simple",
"product_weight": "35.5000",
"product_visibility": "4",
"DivVat": "20",
"image_url": "some url.jpg"
}
],
"shipping_address": {
"first_name": "name",
"last_name": "name",
"email": "retailer#dividebuy.co.uk",
"street": [
"Address 1",
"Adress 2"
],
"postcode": "DE4 3ED",
"region": "County",
"city": "town"
},
"billing_address": {
"first_name": "name",
"last_name": "name",
"email": "retailer#dividebuy.co.uk",
"street": [
"Address 1", "Adress 2"
],
"postcode": "ST15 8YR",
"region": "County",
"city": "town"
}
}
so the controller I have is structured like this:
class Dividebuy::Api::GetorderdetailsController < ApplicationController
protect_from_forgery with: :null_session
skip_before_action :verify_authenticity_token, only: :create
# GET /product
def index
product = Spree::Product.all
render json: product, status: 200
end
# POST /product
def create
data = {
"order_detail": {
"store_order_id": "64",
"store_order_increment_id": "145006485",
"store_token": ENV['DIVIDEBUY_TOKEN'],
"store_authentication": ENV['DIVIDEBUY_AUTHENTICATION'],
"logo_url": "https://moduleinstalledmagento1.dbuytest.info/media/dividebuy/",
"grand_total": 318.4,
"subtotal": 265.33,
"subtotalInclVat": 318.4,
"discount": 0,
"discountApplied": "beoforeVat",
"shipping": 0,
"shippingInclVat": 0,
"shipping_label": "Free Shipping - Free",
"shipping_method": "freeshipping_freeshipping",
"is_default_shipping": 0,
"is_default_billing": 0,
"vat": 53.07
},
"product_details": [
{
"name": "Some product name",
"sku": "SKU",
"qty": "1.0000",
"price": "249.1700",
"priceInclVat": "299.0000",
"rowTotal": "249.1700",
"rowTotalInclVat": "299.0000",
"discount": "0.0000",
"short_description": "Some Product",
"product_type": "simple",
"product_weight": "35.5000",
"product_visibility": "4",
"DivVat": "20",
"image_url": "some url.jpg"
}
],
"shipping_address": {
"first_name": "name",
"last_name": "name",
"email": "retailer#dividebuy.co.uk",
"street": [
"Address 1",
"Adress 2"
],
"postcode": "DE4 3ED",
"region": "County",
"city": "town"
},
"billing_address": {
"first_name": "name",
"last_name": "name",
"email": "retailer#dividebuy.co.uk",
"street": [
"Address 1", "Adress 2"
],
"postcode": "ST15 8YR",
"region": "County",
"city": "town"
}
}
user = User.where(email: 'test#example.com')
if user.valid_password?(params[:password])
render json: data, status: :created
else
render json: { error: "error:" }, status: 400
end
end
end
Here is what I have on my routes for this API:
namespace :dividebuy, defaults: { format: 'json' } do
namespace :api do
resources :getorderdetails, only: [:index, :create]
end
end
My question is basically from the examples given in the documentation of the request & response as shown above, what is the best way to integrate with this API communication on my rails app?
I just hardcoded some data and passed it as a response, probably there is a better way to do that.
More clarity, to test I use postman:
localhost:3000/dividebuy/api/getorderdetails
Any help or guidance will be much appreciated.
Since I just did something similar myself, I can tell you my approach, which follows my guiding principle of "fat model skinny controller".
All the api-related structures that you show in the GetOrderDetailsController should be moved into a service class. Since I see the word Spree in your question, I'll call it SpreeService. So the first step at refactoring is to change your GetOrderDetailsController:
# app/controllers/dividebuy/api/get_order_details_controller.rb
def create
spree = SpreeService.new(params)
response = spree.create_order
if response.errors
# render the error page
else
# render the success page, with order number etc
end
end
This gives the general idea, specifics depend on your app.
In the SpreeService class, you can further decompose the request into smaller methods. As you app develops, you will find more and more functionality to delegate to the SpreeService class.
# app/domain_models/spree_service.rb
require 'net/http'
class SpreeService
BaseUrl = "api.spree.com"
def initialize(params)
# capture the params of interest
end
def create_order
response = Net::HTTP.request(...)
end
private
def create_order_query
# define the hash of all the date to make new order
end
end
I am using 'active_model_serializers', '~> 0.10.6' for rendering my API response. For my index action I am doing this -
render json: #items, root: 'data', each_serializer: ItemsSerializer
but in my response, I am not getting the root key - data
[
{
"id": 85,
"title": "B",
"source": "manager_added",
"shared": true,
"status": "suggested",
"item_type": "action_item",
"manager": {
"id": 2614,
"full_name": "Calvin H",
"first_name": "Calvin"
},
"reportee": {
"id": 2614,
"full_name": "Calvin H",
"first_name": "Calvin"
}
},
{
"id": 87,
"title": "D",
"source": "manager_added",
"shared": true,
"status": "suggested",
"item_type": "action_item",
"manager": {
"id": 2614,
"full_name": "Calvin H",
"first_name": "Calvin"
},
"reportee": {
"id": 2614,
"full_name": "Calvin H",
"first_name": "Calvin"
}
}
]
What I am doing wrong here?
The hardest part with AMS is finding it's right documentation. Based on the version you have mentioned, here's the documentation link:
https://github.com/rails-api/active_model_serializers/tree/0-10-stable/docs
There are 3 adapters:
:default (There won't be any root, basically root key is useless, even if you add it)
:json (This is what you need, you can add custom root key. https://github.com/rails-api/active_model_serializers/blob/0-10-stable/docs/general/adapters.md#example-output-1)
:json_api (Default root key will be data, but you can customize, maybe you can use this, but it will change the whole structure of your response json into something like: https://github.com/rails-api/active_model_serializers/blob/0-10-stable/docs/general/adapters.md#example-output-2)
Answer:
render json: #items, root: 'data', adapter: :json, each_serializer: ItemsSerializer
or
render json: #items, adapter: :json, each_serializer: ItemsSerializer
Here it's my products.controller
def index
if params[:page]
#product = Product.page(params[:page]).per(5)
else
#product = Product.order('updated_at DESC')
end
render json: {
status: '200',
message: 'OK',
data: ActiveModel::Serializer::CollectionSerializer.new(#product, each_serializer: ProductSerializer)},status: 200
end
def show
render json: {
status: '200',
message: 'OK',
data: ActiveModel::Serializer::CollectionSerializer.new(#product, each_serializer: ProductSerializer)},status: 200
end
Here my product serializer
class ProductSerializer < AplicationSerializer
attributes :id, :product_name, :category, :company, :description, :logo_img, :web_link
belongs_to :category
has_many :images
en
d
If I try to access localhost:3000/products/, it can display all the data that I want, but if I want to try localhost:3000/products/1, it's wrong, so what the wrong about my code?
{
"status": "200",
"message": "OK",
"data": [
{
"id": 1,
"product_name": "MDS",
"category": {
"id": 4,
"category": "Market Place",
"created_at": "2018-04-12T02:58:59.949Z",
"updated_at": "2018-04-12T02:58:59.949Z"
},
"company": "PT Media Data ",
"description": "ISP di Indo",
"logo_img": "mds.jpg",
"web_link": "https://mds.co/",",
"images": [
{
"id": 1,
"link": "http:/mds.com/mds.png"
},
{
"id": 2,
"link": "http:/mds.com/mds.png"
},
{
"id": 3,
"link": "http:/mds.com/mds.png"
},
{
"id": 4,
"link": "http:/mds.com/mds.png"
}
]
}
]
}
in the above is the display I want to display at http://localhost:3000/products/1, but i still got no appear, even though i can display it with same code on def index
Thanks for your help
The only thing that can seem to be missing here is setting the #product
If you have a before_action :show, :set_product this might not be the problem.
The other difference is between an array in the index and a single element in the show. you better use ProductSerializer.new(#product).as_json instead of using the collection serializer
tech context: rails 4.2.2, active_model_serializers 0.10.0.rc2
Given a cart and a list of product when I add a product to the cart I expect to get as response:
{
"data": {
"id": "575",
"type": "carts",
"attributes": {
"name": "cart 1"
},
"relationships": {
"cart_products": {
"data": [
{
"type": "cart_products",
"id": "32",
"attributes": {
"product_id": 456
}
}
]
}
}
}
}
unfortunately ,
the current response is
{
"data": {
"id": "575",
"type": "carts",
"attributes": {
"name": "cart 1"
},
"relationships": {
"cart_products": {
"data": [
{
"type": "cart_products",
"id": "32",
}
]
}
}
}
}
is there a way to have relationship attributes rendered?
The JSON:API Spec explains how the relationship data should be. What you are asking for is actually meant to be nested, or better yet "Included" as per the spec.
I would suggest you read a bit over there http://jsonapi.org/format/#document-compound-documents for more details on the spec regarding included/nested relationships
Also, regarding your question, you need to tell your serializer to render included elements, like so: render #posts, include: ['authors', 'comments']
See here for more info: https://github.com/rails-api/active_model_serializers
According to the manual:
render json: #posts, include: ['author', 'comments', 'comments.author']
# or
render json: #posts, include: 'author,comments,comments.author'
More details:
http://jsonapi.org/format/#document-compound-documents
https://github.com/rails-api/active_model_serializers/blob/a032201a91cbca407211bca0392ba881eef1f7ba/docs/general/adapters.md#included
I just need to generate a JSON string that looks something like:
def self.feedback_request(history_event)
#event = history_event.event
#case_tracking_id = history_event.case_tracking_id
#request_type = "feedback"
return "{
"event":#event
"case_tracking_id":#case_tracking_id
"request_type":#request_type
"event_data":historic_event.to_json
}"
end
Does rails have some way to generate JSON strings?
The right way to do it is using the jbuilder which is part of Rails.
so from the documentation:
# app/views/message/show.json.jbuilder
json.content format_content(#message.content)
json.(#message, :created_at, :updated_at)
json.author do
json.name #message.creator.name.familiar
json.email_address #message.creator.email_address_with_name
json.url url_for(#message.creator, format: :json)
end
if current_user.admin?
json.visitors calculate_visitors(#message)
end
json.comments #message.comments, :content, :created_at
json.attachments #message.attachments do |attachment|
json.filename attachment.filename
json.url url_for(attachment)
end
This will build the following structure:
{
"content": "<p>This is <i>serious</i> monkey business</p>",
"created_at": "2011-10-29T20:45:28-05:00",
"updated_at": "2011-10-29T20:45:28-05:00",
"author": {
"name": "David H.",
"email_address": "'David Heinemeier Hansson' <david#heinemeierhansson.com>",
"url": "http://example.com/users/1-david.json"
},
"visitors": 15,
"comments": [
{ "content": "Hello everyone!", "created_at": "2011-10-29T20:45:28-05:00" },
{ "content": "To you my good sir!", "created_at": "2011-10-29T20:47:28-05:00" }
],
"attachments": [
{ "filename": "forecast.xls", "url": "http://example.com/downloads/forecast.xls" },
{ "filename": "presentation.pdf", "url": "http://example.com/downloads/presentation.pdf" }
]
}
So in you case your code should be something like:
json.(#event, #case_tracking_id, #request_type, historic_event)