I am trying to POST(create) an event using EventBrite's API.
I have the auth key but am having trouble formatting the request.
Here is what I have.
def self.syndicate_event
event = Event.first
body_string =
"{
'event': {
'name': {
'html': #{event.name}
},
'description': {
'html': #{event.description}
},
'start': {
'utc': #{event.start},
'timezone': #{event.start_timezone},
},
'end': {
'utc': #{event.end},
'timezone': #{event.end_timezone},
},
'currency':#{event.currency}
}
}"
json_body = body_string.to_json
respo = HTTP.auth("Bearer mytoken")
.post("https://www.eventbriteapi.com/v3/events/",
params: json_body
)
end
It gives me : 'Can't convert String into Array.' error. Any ideas what is going on ? And if someone has used the API for EventBrite, is there a better way other than formatting my string like that and then making it into JSON.
Thanks
I'm not familiar with the EventBrite API, but it looks like the json_body string is malformed. You can verify that your json is valid by doing JSON.parse(json_body) and that should return a hash representation of your json. If it's malformed, it will raise an error.
I would opt to use the to_json method on a hash instance to guarantee the json will not be malformed.
Something like:
body_object =
{ event: {
name: {
html: event.name
},
description: {
html: event.description
}...,
currency: event.currency }
}
json_body = body_object.to_json
Related
I know there are many questions about that already on stackoverflow but none of them has been useful for me. Here is my ajax code:
function update_schedule($task){
var taskId = $task.data("taskId"),
startHour, endHour,
userId = $('#users').val();
if( !$task.hasClass('daily-task')){
startHour = getStartHour($task),
endHour = startHour + (+$task.data('time'));
}
console.log(startHour, endHour)
$.ajax({
url: '/heimdall/update_scheduled_task',
method: 'POST',
data: { "task_id": taskId, "start_hour": startHour, "end_hour": endHour, "user_id": userId },
success: function (){
console.log("SUCESS!!", data['head'])
},
error: function () {
console.log("FAILURE");
},
async: true
});
}
The controller code:
def update_scheduled_task
scheduled_task = ScheduledTask.find_or_create_by(task_id: params[:task_id])
scheduled_task.update_attributes(start_hour: params[:start_hour], end_hour: params[:end_hour], task_id: params[:task_id], user_id: params[:user_id])
end
I want to return the id of found or created task. I don't know how to send/receive this information. I've read about respond to but still I don't know how to use it in this case.
You may do render json: ... to return the required info to ajax in JSON format:
def update_scheduled_task
scheduled_task = ScheduledTask.find_or_create_by(task_id: params[:task_id])
scheduled_task.update_attributes(start_hour: params[:start_hour], end_hour: params[:end_hour], task_id: params[:task_id], user_id: params[:user_id])
render json: {scheduled_task_id: scheduled_task.id}
end
And in the ajax function's success, use it like:
success: function (data){
var data = JSON.parse(data);
console.log(data["scheduled_task_id"]);
},
First, you need to improve your controller code structure (You can rely on the default scaffold generator methods). Then, you must indicate that your method will respond to json format only, with the answer you want to return, something like this:
def update_scheduled_task
scheduled_task = ScheduledTask.find_or_create_by(task_id: params[:task_id])
if (scheduled_task && scheduled_task.update_attributes(start_hour: params[:start_hour], end_hour: params[:end_hour], task_id: params[:task_id], user_id: params[:user_id]))
render json: { scheduled_task_id: scheduled_task.id }
else
render json: { error: l18n.t("error.messages.request_error") }
end
end
Then, you must modify the success and failure response methods of the jquery ajax request. Here's an example of how it might look:
$.ajax({
url: "/heimdall/update_scheduled_task",
method: "post",
data: { task_id: taskId, start_hour: startHour, end_hour: endHour, user_id: userId },
success: function(result) {
if (result["scheduled_task_id"] != null) {
console.log("Schedule record => " + result["scheduled_task_id"])
} else {
console.log("Error: " + result["error"])
}
},
error: function() {
console.log("Ajax request error");
},
// async: true => By default JQuery set true to async param.
});
Do not forget that you need to add a rule to access this method in the file config/ruotes.rb, something like this:
post update_scheduled_task, :defaults => { :format => 'json' }
I hope this helped you, regards!
jQuery's success and error callbacks rely on the status code returned by the controller. Make sure that you return the right status code when you are unable to create/read/update the object. On success, simply render a JSON with the id property set. I beleive update_attributes returns true on success:
if scheduled_task && scheduled_task.update_attributes(...)
render json: { id: scheduled_task.id }
else
head :unprocessable_entity
end
I am trying to create a new issue utilizing the JIRA REST API and whenever I try, I get back the following generic error:
{ errorMessages: [ 'Internal server error' ], errors: {} }
I can successfully GET from the API, and the credentials I'm connecting with have full Admin access to JIRA (so it's not an Auth issue), but I get this error every time with POST. Below is a snippet of the JSON data I'm sending. Am I missing anything obvious?
Below is my JavaScript code. Note I'm using jira-connector from npm. (Real domain replaced with mydomain for this sample code)
const JiraClient = require('jira-connector');
const dotenv = require('dotenv').config();
function createNewIssue(fields) {
const encoded = process.env.JIRA_ENCODED_PW;
const jira = new JiraClient({
host: 'mydomain.atlassian.net',
basic_auth: {
base64: encoded
}
});
return new Promise((resolve, reject) => {
jira.issue.createIssue(fields, (error, issue) => {
if (error) {
console.log(error);
reject(error);
} else {
console.log(issue);
resolve(encoded);
}
});
})
}
Below is the JSON that's being passed into fields in the JS above. Note customfield_17300 is a radio button, and customfield_17300 is a multi-select box. For both cases, I've tried using the "id" and also the actual string "name" value. All IDs below were taken straight from a API GET of the same issue in question:
{
"fields": {
"project": {
"id": "13400"
},
"summary": "TEST API TICKET - 01",
"issuetype": {
"id": "11701"
},
"customfield_14804": { "id": "13716" },
"customfield_14607": "Hardware",
"customfield_17300": [
{
"id": "18322"
}
] ,
"customfield_16301": "Customer PO",
"customfield_14800": "LA, California",
"customfield_16302": "FEDEX 234982347g"
}
}
sigh I figured it out... other posts that said this cryptic error was due to a malformed JSON were correct.
In my route, I passed fields as coming from req.body.fields which actually dove into the fields values instead of passing it straight through. This made it so that when the JSON was sent to JIRA the fields outer wrapper was missing. I changed my route to pass along req.body instead of req.body.fields and all was well.
...that was a fun 4 hours...
I have the following formBuilder in angular2:
constructor(
private formBuilder: FormBuilder) {
this.form = formBuilder.group({
id: [],
title: ['', Validators.required],
dates: formBuilder.group({
start_date: ['', Validators.required],
end_date: ['', Validators.required]
}, {validator: this.checkDates})
});
}
dates is in a separate group, this is for validation purposes. onSubmit calls this service method:
update(academicTerm: AcademicTerm): Observable<AcademicTerm> {
let headers = new Headers();
headers.append('Content-Type', 'application/json');
return this.http
.patch(this.endpointUrl + academicTerm.id, JSON.stringify(academicTerm), {headers})
.map(this.extractData)
.catch(this.handleError);
}
When I check the backend (Rails5 API server) I can see this param set:
Parameters: {"id"=>"3", "title"=>"Term Title", "dates"=>{"start_date"=>"2016-11-27", "end_date"=>"2016-12-01"}, "academic_term"=>{"id"=>"3", "title"=>"Term CL"}}
Note in the academic_term hash that start_date and end_date are not present.
On the Rails side of things I have strong params set up like this:
def academic_term_params
params.require(:academic_term).permit(:id, :title, :start_date, :end_date)
end
I have tried setting the nested dates object in strong params:
def academic_term_params
params.require(:academic_term).permit(:id, :title, :dates => [:start_date, :end_date])
end
Which has no affect (dates is not an associated attribute?). So while I can update title I cannot update the dates.
Is there a way to flatten the params sent from angular to be something like this:
Parameters: {"id"=>"3", "title"=>"Term Title", "start_date"=>"2016-11-27", "end_date"=>"2016-12-01"}
Or is there a way to fix it on the Rails side?
You can flatten the object before sending the request to the server.
update(academicTerm: AcademicTerm): Observable<AcademicTerm> {
let headers = new Headers();
headers.append('Content-Type', 'application/json');
academicTerm['start_date'] = academicTerm.dates.start_date;
academicTerm['end_date'] = academicTerm.dates.end_date;
// delete academicTerm.dates; // optional
return this.http
.patch(this.endpointUrl + academicTerm.id, JSON.stringify(academicTerm), {headers})
.map(this.extractData)
.catch(this.handleError);
}
Looks like a bug in RSpec but maybe I'm missing something.
I have a request spec where I post a JSON that contains an array of hashes:
spec/requests/dummy_request_spec.rb:
post "http://my.server.com/some/route", {
format: :json,
data: [
{
details: {
param1: 1
},
},
{
details: {
param2: 1
}
}
]
}
For some odd reason, RSpec merges the hashes into one element and then sends them to server.
print out of params received in controller:
data: [
{
details: {
param1: 1,
param2: 2
},
},
]
versions:
rspec-2.13.0
rails-3.2.10
Very strange!!
Thanks
Got it! array of hashes is not supported for form-data
RSpec by default posts it as form-data. Solution:
post '...', {...}.to_json, {'CONTENT_TYPE' => "application/json", 'ACCEPT' => 'application/json'}
Also, be aware that you have an extra comma:
data: [
{
details: {
param1: 1
}**,**
},
{
details: {
param2: 1
}
}
]
I faced the same problem reported in the question post while using following versions
ruby 2.3.2
rails (5.0.0.1)
rspec-rails (3.5.2)
Searching for the problem on web I found a related issue at https://github.com/rails/rails/issues/26069 and the solution suggested by it is to pass as: :json option to the post, get etc methods while using them in the controller tests (refer the PR referenced in comment https://github.com/rails/rails/issues/26069#issuecomment-240916233 for more details). Using that solution didn't solved the params mingling issue I was encountering. Following were the results found for different types of data I used with the recommended solution:
In my controller spec I have following
before(:each) do
request.accept = "application/json"
end
and in the test logs I do see that the request is being served
Processing by Api::V1::MyController#my_action as JSON
Data-1
data = [
{
param_1: "param_1_value",
},
{
param_2: "param_2_value",
}
]
params.merge!(my_data: data)
post :my_action, params: params, as: :json
That ends-up in request params as following
{ "my_data"=> [ {"param_1"=>"param_1_value", "param_2"=>"param_2_value"} ] }
which is wrong.
Data-2
data = [
{
param_1: "param_1_value",
something_else: ""
},
{
param_2: "param_2_value",
another_thing: ""
}
]
params.merge!(my_data: data)
post :my_action, params: params, as: :json
That ends-up in request params as following
{ "my_data"=> [ {"param_1"=>"param_1_value", "something_else"=>"", "another_thing"=>"", "param_2"=>"param_2_value"} ] }
which is wrong.
Data-3
data = [
{
param_1: "param_1_value",
param_2: ""
},
{
param_1: ""
param_2: "param_2_value",
}
]
params.merge!(my_data: data)
post :my_action, params: params, as: :json
That ends-up in request params as following
{ "my_data"=>[ {"param_1"=>"param_1_value", "param_2"=>""}, {"param_1"=>"", "param_2"=>"param_2_value"} ] }
which is correct.
It should be noted that for Data-3 without the as: :json option also I receive the correct data in request params.
One more thing: In comment https://github.com/rails/rails/issues/26069#issuecomment-240358290 an alternate solution suggested to deal with the problem narrated above is following
another fix would be to specify nested attributes not as array but as
hash:
params = {
id: #book.id,
book: {
title: 'Cool',
pages_params: {
"0" => { id: #page.id, content: 'another content' },
"1" => { id: #delete_page.id, _destroy: 1 },
"2" => { content: 'another new page' }
}
},
format: :json
}
Unfortunately this was removed from the documentation of nested
attributes so I don't know if this is going to stay valid.
http://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html
But this solution has a drawback is that we need to manually sanitize the data on controller-end to bring it back to expected structure i.e. Array of Hashes.
Finally I am sharing below what worked for me
spec/shared_contexts.rb
RSpec.shared_context "common helpers" do
def set_request_header(request_obj:, request_header_name:, request_header_value:)
request_obj.headers[request_header_name] = request_header_value
end
def set_request_header_content_type_as_json(request_obj:)
set_request_header(request_obj: request_obj, request_header_name: 'CONTENT_TYPE', request_header_value: 'application/json')
end
end
Then in my spec file
require 'shared_contexts'
RSpec.describe Api::V1::MyController, :type => :controller do
include_context "common helpers"
context "POST #my_action" do
it "my example" do
data = [
{
param_1: "param_1_value",
},
{
param_2: "param_2_value",
}
]
params.merge!(my_data: data)
set_request_header_content_type_as_json(request_obj: request)
post :my_action, params: params
end
end
end
As can be seen setting the request header CONTENT_TYPE was what was missing to make the request params to be received in expected structure.
I tried to use model.save() to POST a new user. But I check request payload and found that it not only sent the data, but also sent other parts of the model. That makes my server cannot parse the payload.
The request payload generated :
{"phantom":true,"internalId":"ext-record-58","raw":{},"data":{"userId":0,"userName":"Amy"},"modified":{"userName":""},"hasListeners":{},"events":{},"stores":[],"dirty":true,"id":"AM.model.User-ext-record-58"}
But the desired request payload should be :
{"userId":0,"userName":"Amy"}
And I am aware that the "phantom" of my model is false before I call model.save(). But it becomes true in the request payload. Is it a clue?
Model:
Ext.define('AM.model.User',{
extend: 'Ext.data.Model',
fields: [
{ name: 'userId', type: 'int' },
{ name: 'userName', type: 'string' },
{ name: 'createdTime', type: 'string' },
],
idProperty: 'userId',
associations: [
{
type: 'hasOne',
model: 'AM.model.ModelA',
name:'modelA',
associationKey:'modelA',
getterName:'modelA'
},
{
type: 'hasOne',
model: 'AM.model.ModelB',
name:'modelB',
associationKey:'modelB',
getterName:'modelB'
}
],
proxy: {
type: 'rest',
success:true,
url:'../restful/users',
writer:{
type:'json',
getRecordData:function(record){ //parse createdTime to the format Y-m-d
record.set('createdTime', Ext.Date.format(new Date(record.get('createdTime')), "Y-m-d"));
return record;
}
},
reader: {
type: 'json'
}
}
});
This is the view which has the data to be posted. The view will fill the data to the model:
Ext.define('AM.view.UserRegisterForm',{
extend:'Ext.form.Panel.',
alias:'widget.userRegisterForm',
fields:new Array(), //I want to render the fields in xtemplate, so instead of adding the fields to items, I use an array to manage them.
retrieveData(model){
model.set('userName', this.fields[0].getValue());
model.set('createdTime',this.fields[1].getValue());
}
}
The function in the controller, which sends the POST request:
postUser:function(){
var userRegisterForm= this.getUserRegisterForm();
var userModel = this.getUserModel();
var user= new userModel();
var me = this;
userRegisterForm.retrieveFieldData(user);
console.log(user); //the data in console looks fine!
user.save({
success: function(response) {
//do something...
},failure:function(response) {
alert('fail');
}
});
}
You are returning the full record when you override getRecordData Where as you are just meant to return the records data. record.getData()
Some extra advice. Don't override getRecordData to set the models creation date. Use the models defaultValue property to give assign it a new Date if one doesn't exist.