How to update a couchdb document with Ruby - ruby-on-rails

I have a Rails application with MYSQL backend. Upon saving data in MYSQL, it pushes some data to Couchdb using curl commands. However, I am unable to update that very same document. I dont know how query from couchdb and use _rev id to update. Here is how am creating and pushing a couchdb document.
require 'net/http'
require 'uri'
require 'json'
def push_to_couchdb
url = URI.parse("http://127.0.0.1:5984/m5fcash/100")
request = Net::HTTP::Put.new(url)
request.body = JSON.dump({
"fname" => "fname",
"lanme" => "lname",
"type" => "demo",
"facility" => "spectre"
})
req_options = {
use_ssl: url.scheme == "https",
}
response = Net::HTTP.start(url.hostname, url.port, req_options) do |http|
http.request(request)
end
end
This is how am trying to query the document and extract its _rev id
def get_couch_doc
uri = URI.parse("http://127.0.0.1:5984/m5fcash/100")
response = Net::HTTP.get_response(uri)
raise response.inspect
end
I expect raise response.inspect to gove a couchdb document from which I will exctract a _rev id. But all I get is #. How can I query a document in Ruby using curl??

It's not entirely clear to me from your code what you're trying to do, so I'll try to explain in general terms.
In order to read a specific document revsion, you need to use the rev=... query parameter. This is probably not what you want. In your get_couch_doc you can't have a request body. It looks like you're trying to fetch a document based on specific data in the body -- for this you need to create a secondary index, known as a view in CouchDB.
If you want to be able to query based on the lanme (maybe that should be lname?) field, you'd make a view where the map could look something like
function(doc) {
if (doc.lname) {
emit(doc.lname, null);
}
}
If you want to update a document, you need to do a PUT database/doc_id with the rev=.... query parameter on the URL.
CouchDB docs for the document API, and for views.

Related

How do I search emails sent from the authorized account with the Office365 API v2?

I'm using faraday and have a setup for grabbing messages from the authorized account. I have no trouble grabbing incoming messages, but when I go to grab sent messages I get an empty response when I know that the account has sent messages.
Basically, what I need is to retrieve a list emails that the user has sent.
Here's what I'm doing:
from_conn = Faraday.new(:url => "https://outlook.office.com") do |faraday|
faraday.response :logger
faraday.adapter Faraday.default_adapter
end
# Then from:
from_response = from_conn.get do |request|
request.url "/api/v2.0/Me/Messages?$search=%22from:#{user_email}%22&$select=SentDateTime,ToRecipients,From,Subject,Body"
request.headers['Authorization'] = "Bearer #{token['token']}"
request.headers['Accept'] = 'application/json'
request.headers['X-AnchorMailbox'] = user_email
end
And here is the parsed response body:
[
[0] {
"#odata.context" => "https://outlook.office.com/api/v2.0/$metadata#Me/Messages(SentDateTime,ToRecipients,From,Subject,Body)",
"value" => []
}
]
It's generating a request URI of https://outlook.office.com/api/v2.0/Me/Messages?%24search=%22from%3Adr_dickdorkins%40outlook.com%22&%24select=SentDateTime%2CToRecipients%2CFrom%2CSubject%2CBody.
I've tried searching in the SentItems folder like so:
request.url "/api/v2.0/Me/Folders/SentItems$search=%22sender:#{user_email}%22"
But any permutation that I thought of to search folders yields this error:
[0] {
"error" => {
"code" => "RequestBroker-ParseUri",
"message" => "Resource not found for the segment 'Folders'."
}
}
I'm not really sure what else to try--any help is appreciated!
From the docs, that doesn't look like the proper URL format to specify the folder in your last attempt. Excerpt from https://msdn.microsoft.com/en-us/office/office365/api/mail-rest-operations#get-a-message-collection-rest :
GET https://outlook.office.com/api/v2.0/me/MailFolders/{folder_id}/messages
folder_id - string - The folder ID, or the Inbox, Drafts, SentItems, or DeletedItems well-known folder name, if you're getting messages from a specific folder. Specifying AllItems would return all messages from the entire mailbox
You should try changing this url to:
https://outlook.office.com/api/v2.0/me/MailFolders/SentItems/messages/?$select=SentDateTime,ToRecipients,From,Subject,Body
(It looks as if it's expecting MailFolders resource name instead of Folders) Also, before ruling it out a problem with the request built by faraday , you should check what the api is returning with something like advanced rest client, curl or some other REST client that lets you set the headers, then bring those settings to the rails side of things.

Getting data out of a JSON Response in Rails 3

So I am trying to pull tweets off of Twitter at put them into a rails app (Note because this is an assignment I can't use the Twitter Gem) and I am pretty confused.
I can get the tweets I need in the form of a JSON string but I'm not sure where to go from there. I know that the Twitter API call I'm making returns a JSON array with a bunch of Tweet objects but I don't know how to get at the tweet objects. I tried JSON.parse but was still unable to get the required data out (I'm not sure what that returns). Here's the code I have so far, I've made it pretty clear with comments/strings what I'm trying for. I'm super new to Rails, so this may be way off for what I'm trying to do.
def get_tweets
require 'net/http'
uri = URI("http://search.twitter.com/search.json?q=%23bieber&src=typd")
http = Net::HTTP.new(uri.host, uri.port)
request = Net::HTTP::Get.new(uri.request_uri)
response = http.request(request)
case response
when Net::HTTPSuccess then #to get: text -> "text", date: "created_at", tweeted by: "from_user", profile img url: "profile_img_url"
JSON.parse(response.body)
# Here I need to loop through the JSON array and make n tweet objects with the indicated fields
t = Tweet.new(:name => "JSON array item i with field from_user", :text "JSON array item i with field text", :date => "as before" )
t.save
when Net::HTTPRedirection then
location = response['location']
warn "redirected to #{location}"
fetch(location, limit - 1)
else
response.value
end
end
Thanks!
The JSON.parse method returns a ruby hash or array representing the json object.
In your case, the Json is parsed as a hash, with the "results" key (inside that you have your tweets), and some meta data: "max_id", "since_id", "refresh_url", etc. Refer to twitter documentation for a description on the fields returned.
So again with your example it would be:
parsed_response = JSON.parse(response.body)
parsed_response["results"].each do |tweet|
t = Tweet.new(:name => tweet["from_user_name"], :text => tweet["text"], :date => tweet["created_at"])
t.save
end

Rails + CouchDb binary file upload to Database

Simple-Stored is based on top of CouchPotato for handling CouchDb in rails. While trying to upload files to the couchdb we have tryied using base64, json post and nothing seems to work quite right; we are trying to upload to the _attachments propertie of a document already stored.
Having the model like this :
class Patient
include SimplyStored::Couch
end
and in the controller while receiving the file trough the update action
def update
#patient = Patient.find(params[:id])
if params[:patient][:_attachments]
attachement = params[:patient][:_attachments]
filedata = attachement.tempfile.read
data = Base64.encode64(filedata).gsub(/\n/, '')
type = attachement.content_type
or_name = attachement.original_filename
#patient._attachments = {or_name => {'data' => data, 'content_type' => type}}
#patient.save
return render :json => #patient._attachments
end
end
Now the fun part is that I can see that #patient._acttachments has the file itself and that is what is returning in the render after the .save; but it is not actually saving it on the couchdb database.
Any ideas why is not doing the save or should I try to just push the _attachment to the couchdb database. ? (which by the way always returns a 500 error :( )
the solution it's very simple, based on the couchpotato website, you actually don't need to convert it to base64 here is the example of code working
if params[:patient][:_attachments]
attachement = params[:patient][:_attachments]
data = attachement.tempfile.read
type = attachement.content_type
or_name = attachement.original_filename
params[:patient][:_attachments] = {or_name => {'data' => data, 'content_type' => type}}
end
if #patient.update_attributes(params[:patient]) #blah blah blah
since the values are in the [:patient][:_attachments] params, you just need to pass it as another param tested and working.
Also you need to define your patients model as
property :_attachments
dunno if that is required but I did it.
I know I should not ask for money but since I WORK FOUR YOU its only 100 pesos/hour.. see you at the office
cheers
lols
I donno about the Ruby and couchpotato, but I don't think you need to Base64 your attachment. Just read the binary info and write it to request.
my 2cents. :)

Problems with MailChimp API in Ruby Error Code: -90

I am using the following code in my MailChimp Controller to submit simple newsletter data. When It is sent I receive the following error as a "Method is not exported by this server -90" I have attached my controller code below. I am using this controller for a simple newsletter signup form. (Name, Email)
class MailchimpController < ApplicationController
require "net/http"
require "uri"
def subscribe
if request.post?
mailchimp = {}
mailchimp['apikey'] = 'f72328d1de9cc76092casdfsd425e467b6641-us2'
mailchimp['id'] = '8037342dd1874'
mailchimp['email_address'] = "email#gmail.com"
mailchimp['merge_vars[FNAME]'] = "FirstName"
mailchimp['output'] = 'json'
uri = URI.parse("http://us2.api.mailchimp.com/1.3/?method=listSubscribe")
response = Net::HTTP.post_form(uri, mailchimp)
mailchimp = ActiveSupport::JSON.decode(response.body)
if mailchimp['error']
render :text => mailchimp['error'] + "code:" + mailchimp['code'].to_s
elsif mailchimp == 'true'
render :text => 'ok'
else
render :text => 'error'
end
end
end
end
I highly recommend the Hominid gem: https://github.com/tatemae-consultancy/hominid
The problem is that Net::HTTP.post_form is not passing the "method" GET parameter. Not being a big ruby user, I'm not certain what the actual proper way to do that with Net::HTTP is, but this works:
require "net/http"
data="apikey=blahblahblah"
response = nil
Net::HTTP.start('us2.api.mailchimp.com', 80) {|http|
response = http.post('/1.3/?method=lists', data)
}
p response.body
That's the lists() method (for simplicity) and you'd have to build up (and urlencode your values!) your the full POST params rather than simply providing the hash.
Did you take a look at the many gems already available for ruby?
http://apidocs.mailchimp.com/downloads/#ruby
The bigger problem, and main reason I'm replying to this, is that your API Key is not obfuscated nearly well enough. Granted I'm used to working with them, but I was able to guess it very quickly. I would suggest immediately going and disabling that key in your account and then editing the post to actually have completely bogus data rather than anything close to the correct key. The list id on the other hand, doesn't matter at all.
You'll be able to use your hash if you convert it to json before passing it to Net::HTTP. The combined code would look something like:
mailchimp = {}
mailchimp['apikey'] = 'APIKEYAPIKEYAPIKEYAPIKEY'
mailchimp['id'] = '8037342dd1874'
mailchimp['email_address'] = "email#gmail.com"
mailchimp['merge_vars[FNAME]'] = "FirstName"
mailchimp['output'] = 'json'
response = nil
Net::HTTP.start('us2.api.mailchimp.com', 80) {|http|
response = http.post('/1.3/?method=listSubscribe', mailchimp.to_json)
}

Disabled/Custom params_parser per action

I have a create action that handles XML requests. Rather than using the built in params hash, I use Nokogiri to validate the XML against an XML schema. If this validation passes, the raw XML is stored for later processing.
As far as I understand, the XML is parsed twice: First the Rails creates the params hash, then the Nokogiri parsing happens. I've been looking for ways to disable the params parsing to speed things up but have found nothing.
ActionController::Base.param_parsers[Mime::XML] = lambda do |body|
# something
end
I know it's possible to customize the XML params parsing in general using something like the above, but I depend on the default behaviour in other controllers.
Is it possible to bypass the params parsing on a per-action basis? What options do I have?
Thank you for your help!
I've managed to solve the problem using Rails Metal. The relevant part looks something like this:
class ReportMetal
def self.call(env)
if env["PATH_INFO"] =~ /^\/reports/
request = Rack::Request.new(env)
if request.post?
report = Report.new(:raw_xml => request.body.string)
if report.save # this triggers the nokogiri validation on raw_xml
return [201, { 'Content-Type' => 'application/xml' }, report.to_xml]
else
return [422, { 'Content-Type' => 'application/xml' }, report.errors.to_xml]
end
end
end
[404, { "Content-Type" => "text/html" }, "Not Found."]
ensure
ActiveRecord::Base.clear_active_connections!
end
end
Thanks!
PS: Naive benchmarking with Apache Bench in development shows 22.62 Requests per second for standard Rails vs. 57.60 Requests per second for the Metal version.

Resources