Rails4 + Json API : Increase detail of response - ruby

I'm a newbie on RoR (and Ruby). I need a little help about a json response (with Grape).
This is the sample:
{
events: [
{
'some data':'some data',
place_id: 1
}
]
}
Now this is the result of Events.all in Rails, but I want to make for each event a query for the place, to have more data instead only id. I'm sure that new lambda function can help me, but for now I have no idea about to make it. I'm trying without success...
Thanks in advance
UPDATE
Desired result
{
events: [
{
'some data':'some data',
place : {
id: 1,
name: 'Blablabla'
}
]
}

Consider using ActiveModelSerializers which allows you to define how your models should be serialized in a manner similar to ActiveRecord DSL (e.g. your problem would be solved by defining that event has_one :place)

:events => events.as_json(include: :place)
This is a useful for my problem. After add belongs_to, obviously.
from http://api.rubyonrails.org/classes/ActiveModel/Serializers/JSON.html

Related

Using map on returned graphql query is making known members undefined

I'm using Gatsbyjs to build a blog and I can't use the onCreatePage API to pass data from my graphql query into page templates.
My query grabs data from Kentico Cloud and it looks like this.
{
allKenticoCloudTypeBlogPost{
edges{
node{
contentItems{
elements{
url_slug{
value
}
}
}
}
}
}
}
This is a valid query and it returns data that looks like this.
The problem comes in my gatsby-node.js file where I want to utilize this query to build out pages using my predefined template.
Specifically in the createPage method which looks like this.
result.data.allKenticoCloudTypeBlogPost.edges.map(({node}) => {
createPage({
path: `${node.contentItems.elements.url_slug.value}`,
component: path.resolve(`./src/templates/blog-post.js`),
context: {
slug: node.contentItems.elements.url_slug.value,
}
})
});
The error that displays is the following.
TypeError: Cannot read property 'url_slug' of undefined
gatsby-node.js:31 result.data.allKenticoCloudTypeBlogPost.edges.map
C:/Users/xxxx/Desktop/Marketing Repos/xxxx/gatsby-node.js:31:57
I decided to investigate doing a console.table on node.contentItems, as it appears as though the elements part is where it gets tripped up.
The result of console.table(node.contentItems) just before the createPage method is this.
It appears that node.contentItems has a member called url_slug rather than the elements member that I expected.
I thought I could then solve my problem by updating my createPage method call like so.
result.data.allKenticoCloudTypeBlogPost.edges.map(({node}) => {
console.table(node.contentItems);
createPage({
path: `${node.contentItems.url_slug.value}`,
component: path.resolve(`./src/templates/blog-post.js`),
context: {
slug: node.contentItems.url_slug.value,
}
})
});
But then I get an error saying
TypeError: Cannot read property 'value' of undefined.
I truly don't understand how I can do a table log and see the url_slug member, but then when I try to access it, it says that it's undefined. All while I know that my query is correct because I can run it in graphiQL and get back the exact data I expect.
Any help would be appreciated. Thank you.
In your query result, node.contentItems is an array, even though you're trying to access it as if it's an object:
path: `${node.contentItems.elements.url_slug.value}`,
^^^^^^^^
console.log(contentItems) // [ { elements: {...} }, { elements: {...} }, ... ]
I think your confusion probably stems from the way console.table display data. It's confusing if you don't already know the shape of your data. Your screenshot says, this object has 4 properties with index 0 -> 3 (so likely an array), each has one property called elements (listed on table header), which is an object with the only property url_slug.
I'm not familiar with KenticoCloud, but maybe your posts are nested in contentItems, in which case you should loop over it:
result.data.allKenticoCloudTypeBlogPost.edges.map(({node}) => {
node.contentItems.forEach(({ elements }) => {
createPage({
path: elements.url_slug.value,
context: { slug: elements.url_slug.value },
component: ...
})
})
});
Is there a reason you are wrapping node with curly brackets in your map argument?
You might have already tried this, but my first intuition would be to do this instead:
result.data.allKenticoCloudTypeBlogPost.edges.map(node => {
console.log(node.contentItems)
createPage({
path: `${node.contentItems.elements.url_slug.value}`,
component: path.resolve(`./src/templates/blog-post.js`),
context: {
slug: node.contentItems.elements.url_slug.value,
}
})
});

Why is an Array in my payload being flattened in Sinatra / Rack::Test?

I'm trying to test a small Sinatra app using rspec. I want to pass a rather complex payload and am running into issues i do not understand: my payload contains an array of hashes. When I run the actual application this will work as expected, yet when I use the post helper to run my tests, the array will contain a merged hash:
post(
"/#{bot}/webhook",
sessionId: "test-session-#{session_counter}",
result: {
contexts: [
{ some: 'fixture' },
{ name: 'generic', parameters: { facebook_sender_id: 'zuck-so-cool' } }
]
}
)
In the sinatra handler I use params to access this payload:
post '/:bot/webhook' do |bot|
do_something_with(params)
end
When I now look at the structure of params when running the test suite, I will see the following structure:
[{"some" => "fixture", "name" => "generic", "parameters" => {"facebook_sender_id" => "zuck-so-cool"}}]
which I do not really understand. Is this a syntax issue (me being a ruby noob), am I using params wrong, or is this a bug?
EDIT: So i found out this is an "issue" with the way that Rack::Test will serialize the given payload when not specifying how to (i.e. as form data). If I pass JSON and pass the correct headers it will do what I expect it to do:
post(
"/#{bot}/webhook",
{
sessionId: "test-session-#{session_counter}",
result: {
contexts: [
{ some: 'fixture' },
{ name: 'generic', parameters: { facebook_sender_id: 'zuck-so-cool' } }
]
}
}.to_json,
{ 'HTTP_ACCEPT' => 'application/json', 'CONTENT_TYPE' => 'application/json' }
)
Still I am unsure of this is an issue with the passed data structure not being possible to be serialized into form data or if this is a bug in the way that Rack::Test serializes data.
Looking at the relevant portion of the specs it looks like this is is expected behavior.

Graphql relay array field

I am starting to develop for relay coming from Apollo.
I have a dumb server running on a SQLITE3 database just for testing while I am refactoring.
Using graphql-relay on the backen.
Currently I have something like this:
{
root: {
allFoo: [FooType]
}
}
I was wondering how I would add a new FooType item to the allFoo list.
On the getConfigs the RANGE_ADD only acts upon connections.
So do I need to make my allFoo type a connection instead of a GraphqlList(FooType) ? Or can I use FIELD_CHANGE somehow?
Take a look at this example:
https://github.com/bfwg/relay-gallery/blob/master/frontend/src/app/mutation/AddImageMutation.js#L47
The below example is a demo on how to add an image to the image list.
getConfigs() {
return [{
type: 'RANGE_ADD',
parentName: 'User',
parentID: this.props.images.id,
connectionName: 'images',
edgeName: 'newImageEdge',
rangeBehaviors: {
'': 'prepend',
},
}];
}
Hope this helps!

Ruby finding duplicates in MongoDB

I am struggling to get this working efficiently I think map reduce is the answer but can't getting anything working, I know it is probably a simple answer hopefully someone can help
Entry Model looks like this:
field :var_name, type: String
field :var_data, type: String
field :var_date, type: DateTime
field :external_id, type: Integer
If the external data source malfunctions we get duplicate data. One way to stop this was when consuming the results we check if a record with the same external_id already exists, as one we have already consumed. However this is slowing down the process a lot. The plan now is to check for duplicates once a day. So we are looking get a list of Entries with the same external_id. Which we can then sort and delete those no longer needed.
I have tried adapting the snippet from here https://coderwall.com/p/96dp8g/find-duplicate-documents-in-mongoid-with-map-reduce as shown below but get
failed with error 0: "exception: assertion src/mongo/db/commands/mr.cpp:480"
def find_duplicates
map = %Q{
function() {
emit(this.external_id, 1);
}
}
reduce = %Q{
function(key, values) {
return Array.sum(values);
}
}
Entry.all.map_reduce(map, reduce).out(inline: true).each do |entry|
puts entry["_id"] if entry["value"] != 1
end
end
Am I way off? Could anyone suggest a solution? I am using Mongiod, Rails 4.1.6 and Ruby 2.1
I got it working using the suggestion in the comments of the question by Stennie using the Aggregation framework. It looks like this:
results = Entry.collection.aggregate([
{ "$group" => {
_id: { "external_id" => "$external_id"},
recordIds: {"$addToSet" => "$_id" },
count: { "$sum" => 1 }
}},
{ "$match" => {
count: { "$gt" => 1 }
}}
])
I then loop through the results and delete any unnecessary entries.

Mongoid Complex Query Including Embedded Docs

I have a model with several embedded models. I need to query for a record to see if it exists. the issue is that I will have to include reference to multiple embedded documents my query would have to include the following params:
{
"first_name"=>"Steve",
"last_name"=>"Grove",
"email_addresses"=>[
{"type"=>"other", "value"=>"steve#stevegrove.com", "primary"=>"true"}
],
"phone_numbers"=>[
{"type"=>"work_fax", "value"=>"(720) 555-0631"},
{"type"=>"home", "value"=>"(303) 555-1978"}
],
"addresses"=>[
{"type"=>"work", "street_address"=>"6390 N Main Street", "city"=>"Elbert", "state"=>"CO"}
],
}
How can I query for all the embedded docs even though some fields are missing such as _id and associations?
A few things to think about.
Are you sure the query HAS to contain all these parameters? Is there not a subset of this information that uniquely identifies the record? Say (first_name, last_name, and an email_addresses.value). It would be silly to query all the conditions if you could accomplish the same thing in less work.
In Mongoid the where criteria allows you to use straight javascript, so if you know how to write the javascript criteria you could just pass a string of javascript to where.
Else you're left writing a really awkward where criteria statement, thankfully you can use the dot notation.
Something like:
UserProfile.where(first_name: "Steve",
last_name: "Grove",
:email_addresses.matches => {type: "other",
value: "steve#stevegrove.com",
primary: "true"},
..., ...)
in response to the request for embedded js:
query = %{
function () {
var email_match = false;
for(var i = 0; i < this.email_addresses.length && !email_match; i++){
email_match = this.email_addresses[i].value === "steve#stevegrove.com";
}
return this.first_name === "Steve" &&
this.last_name === "Grove" &&
email_match;
}
}
UserProfile.where(query).first
It's not pretty, but it works
With Mongoid 3 you could use elem_match http://mongoid.org/en/origin/docs/selection.html#symbol
UserProfile.where(:email_addresses.elem_match => {value: 'steve#stevegrove.com', primary: true})
This assumes
class UserProfile
include Mongoid::Document
embeds_many :email_addresses
end
Now if you needed to include every one of these fields, I would recommend using the UserProfile.collection.aggregate(query). In this case you could build a giant hash with all the fields.
query = { '$match' => {
'$or' => [
{:email_addresses.elem_match => {value: 'steve#stevegrove.com', primary: true}}
]
} }
it starts to get a little crazy, but hopefully that will give you some insight into what your options might be. https://coderwall.com/p/dtvvha for another example.

Resources