I wrote simple update handler:
{
"handler": "function (doc, req) { if (req.userCtx.roles.indexOf('editor') < 0) return [req.userCtx, 'access error']; if (!doc) { return [doc, 'nothing']; } doc.date = new Date(); doc.edited_by = req.userCtx.name; return [doc, toJSON(doc)]; }"
}
When I'm trying to query from Mozilla HttpRequester http://192.168.0.34:5984/records/_design/records/_update/handler, i'm getting
with that's data
{"title":"testtest","content":"test"}
And getting
{"error":"bad_request","reason":"Document id must not be empty"}
I've added _id to JSON
{"_id":"testid","title":"testtest","content":"test"}
No luck.
Update: and running that query in browser returns nothing, which may indicate that there's no document sent, but that's not true. What i'm doing wrong?
In CouchDB update handler _id never been set automatically, so you must do it manually. The simplest solution is using uuids, which available at req.uuid.
One important thing about update handlers. Their syntax someplace strange: you can't take data and return doc, no. Your update handler taking doc and req objects, where first is a changing document (check document existence using !doc, if it returns true — you can create new) and the second is request data, including access info, uuid and body. We're getting data from server not in req, but in req.body, which is raw string. Is must be parsed first using JSON.parse(req.body). After doing some checks, we can return — but return also not easy. If we have a doc — we're changing it, if not — we're creating new object. Return must be done by array [data_to_be_written, return_data], where first item is document for database and the second is a string (and only string, use toJSON(object) to return objects).
In addition there's my update handler function example, there's a lot of dirty debug code and it can be wrong, but it working on creation (update still unchecked).
function (doc, req) {
if (req['userCtx']['roles'].indexOf('editor') < 0) {
return [null, 'Access denied'];
}
var rcv = JSON.parse(req.body);
if (!doc) {
if (!rcv['_id'] && !!rcv['title'] && !!rcv['content']) {
return [{ '_id':req.uuid,'title': rcv['title'], 'content': rcv['content'], 'date': new Date(), 'edited_by': req['userCtx']['name'] }, 'Created' + toJSON(req)];
}
return [null, 'Empty' + toJSON({ 'no_id': !rcv['_id'], 'title': !!rcv['title'], 'content': !!rcv['content'], 'body':req.body })];
}
doc['date'] = new Date();
doc['edited_by'] = req['userCtx']['name'];
return [doc, 'Edited' + toJSON(doc)];
}
P.S> and remember, that even update handler working over validation func, so if your data can't pass validation, it will not be written anyway.
Related
I am currently consistently getting 500 errors from Express.js that I believe hinge on a failure to obtain a string key that the request hinges on.
On the client side, I have several requests hitting the same point (/restore), all of which are intended to make "key" a field in a jQuery.ajax() call's data dictionary, included in turn in the main dictionary. On the client-side, I have the following, which includes localStorage fallback that I don't think is particularly relevant:
var restore = function(key, default_value, state, callback)
{
populate_state(state, default_value);
state.initialized = false;
var complete = function(jqxhr)
{
if (jqxhr.responseText === 'undefined')
{
}
else
{
populate_state(state, JSON.parse(jqxhr.responseText));
}
callback();
state.initialized = true;
}
jQuery.ajax('/restore',
{
'complete': complete,
'data':
{
'key': key,
'userid': userid
},
'method': 'POST',
});
if (Modernizr.localstorage)
{
if (localStorage[key] === null || localStorage[key]
=== undefined)
{
return default_value;
}
else
{
return JSON.parse(localStorage[key]);
}
}
else
{
return default_value;
}
}
restore('Calendar', default_value,
default_value, function()
{
jQuery('#submit-calendar').prop('disabled', false);
});
restore('Scratchpad', '', result, function()
{
for(var instance in CKEDITOR.instances)
{
if (CKEDITOR.instances[instance])
{
CKEDITOR.instances[instance].setReadOnly(false);
}
}
});
return restore('Todo', {
'items': [],
'text': ''
},
{
'items': [],
'text': ''
},
function()
{
jQuery('#add-activity-button').prop('disabled', false);
jQuery('#todo-new-entries').prop('disabled', false);
});
return restore('YouPick', {
start_time: new Date().getTime()
},
{
start_time: new Date().getTime()
},
function()
{
});
Note that each call to restore() explicitly specifies a nonempty, unique, alphabetic string for the key as the first argument.
On the server side, Express's routes/index.js has a view that is servicing the request:
router.post('/restore', function(request, response, next)
{
console.log('router.post /restore');
console.log('Query: ' + request.query);
console.log('href: ' + sanitize(request.user.href));
console.log('key: ' + sanitize(request.query.key));
var result = look_up_key(request.user.href, request.query.key);
console.log(result);
response.type('application/json');
response.send(result);
});
The sanitize function wipes out characters that are not alphanumeric or an explicitly enumerated punctuation character. It should have no request on the purely alphabetic key.
This, with the multiple calls, has an output for /bin/www of:
router.post /restore
Query: [object Object]
href: httpsapi.stormpath.comv1accounts**********************
POST /restore 500 39.516 ms - 1210
router.post /restore
Query: [object Object]
href: httpsapi.stormpath.comv1accounts**********************
POST /restore 500 5.000 ms - 1210
router.post /restore
Query: [object Object]
href: httpsapi.stormpath.comv1accounts**********************
POST /restore 500 5.842 ms - 1210
It looks like there is something there for the query, but where do I access it? http://expressjs.com/api.html seems like I should be able to treat it as a dictionary, but among the server-side console.log() calls, console.log('key: ' + sanitize(request.query.key)); does not appear to be producing any output, even an empty or corrupt key. It appears to crash there, apparently sending a 500 from there.
I could probably, or at least possibly, circumvent the issue by encoding and decoding data as JSON, and while I think that's generally a winning solution, I would like to understand why this is not working.
I also don't think that key is someone's reserved word; a global, hand-inspected search and replace from key to identifier seemed not to observably alter the behavior.
So two questions, in order of preference:
1: How can I send and receive variables that will be interpreted as putting things into a GET or POST query string, or taking them out of the same? And what is the [Object object] represented by request.query?
2: If that's not the route to take, and I should just use JSON, what (if anything) should I know about JSON encoding in this exact context? Is it as simple as JSON is normally, or are there things I should be advised of?
Thanks,
With POST requests, data is usually passed in the request body, which means that you need to use req.body instead of req.query (the latter is used to access the data passed in the query string of a URL). Before req.body works, you need to include the body-parser middleware.
As for why [object Object] is being logged: this has to do with stringification. Your log command uses + to "add" a string and an object (request.query), which will use the string representation of an object and that happens to be [object Object].
Instead, you should pass objects are separate arguments to console.log():
console.log('Query: ', request.query); // comma instead of plus
That will show a more elaborate string representation of the object. Alternatively, you can even have it print out as JSON:
console.log('Query: %j', request.query)
The ultimate goal is to detect changes between an existing Parse object and the incoming update using the beforeSave function in Cloud Code.
From the Cloud Code log available through parse.com, one can see the input to beforeSave contains a field called original and another one called update.
Cloud Code log:
Input: {"original": { ... }, "update":{...}
I wonder if, and how, we can access the original field in order to detect changing fields before saving.
Note that I've already tried several approaches for solving this without success:
using (object).changedAttributes()
using (object).previousAttributes()
fetching the existing object, before updating it with the new data
Note on request.object.changedAttributes():
returns false when using in beforeSave and afterSave -- see below for more details:
Log for before_save -- summarised for readability:
Input: { original: {units: '10'}, update: {units: '11'} }
Result: Update changed to { units: '11' }
[timestamp] false <--- console.log(request.object.changedAttributes())
Log for corresponding after_save:
[timestamp] false <--- console.log(request.object.changedAttributes())
There is a problem with changedAttributes(). It seems to answer false all the time -- or at least in beforeSave, where it would reasonably be needed. (See here, as well as other similar posts)
Here's a general purpose work-around to do what changedAttributes ought to do.
// use underscore for _.map() since its great to have underscore anyway
// or use JS map if you prefer...
var _ = require('underscore');
function changesOn(object, klass) {
var query = new Parse.Query(klass);
return query.get(object.id).then(function(savedObject) {
return _.map(object.dirtyKeys(), function(key) {
return { oldValue: savedObject.get(key), newValue: object.get(key) }
});
});
}
// my mre beforeSave looks like this
Parse.Cloud.beforeSave("Dummy", function(request, response) {
var object = request.object;
var changedAttributes = object.changedAttributes();
console.log("changed attributes = " + JSON.stringify(changedAttributes)); // null indeed!
changesOn(object, "Dummy").then(function(changes) {
console.log("DIY changed attributes = " + JSON.stringify(changes));
response.success();
}, function(error) {
response.error(error);
});
});
When I change someAttribute (a number column on a Dummy instance) from 32 to 1222 via client code or data browser, the log shows this:
I2015-06-30T20:22:39.886Z]changed attributes = false
I2015-06-30T20:22:39.988Z]DIY changed attributes =
[{"oldValue":32,"newValue":1222}]
I am working on a Meteor application and one of the features I'm building is a form that inserts a new document into an array (inserts a shipping address to a user's profile where a user can have multiple addresses). The error I keep getting is:
Exception while invoking method 'addAddress' Error: When the modifier option is true, validation object must have at least one operator
I have been unsuccessfully trying to figure out the answer on Stackoverflow, Github, etc. but could not find a solution. I now want to take the approach of understanding exactly what the error means - so my question is what exactly are modifier options and operators in MongoDB? From what I understand, modifiers provide constraints on what type of data is returned from a query, and operators are used to modify data. Are these definitions correct?
Does anyone know what the error I'm getting might mean? Here is my sample code:
My click event to capture data on a form and call a method to add an address:
Template.editAddress.events({
'click .addAddress': function(e, tmpl) {
e.preventDefault();
var currentUserId = Meteor.userId();
console.log(currentUserId);
var addressDetails = {
address: {
streetAddress: $('#streetAddress').val()
}
};
console.log(addressDetails);
Meteor.call('addAddress', addressDetails, currentUserId, function(error) {
if (error) {
alert(error.reason);
} else {
console.log('success!');
Router.go('Admin');
}
});
}
});
My method to insert the address:
Meteor.methods({
'addAddress': function(addressDetails, currUserId) {
var currentUserId = currUserId;
console.log('user to add address to is ' + currUserId);
Meteor.users.update(currentUserId, {$addToSet:
{
'address.streetAddress': addressDetails.streetAddress
}
});
}
});
Note that when I type that query in the console, it works:
db.users.update({_id: 'Qdf89k3fd93jfdk'}, {$addToSet: {'address.streetAddress': '12345 fake st'}});
Thank you in advance!
Your addressDetails object doesn't have a field streetAddress, so addressDetails.streetAddress returns undefined. Use addressDetails.address.streetAddress instead in the update. And also, like Joshua pointed out, use an object as selector with { _id: currUserId }. So the whole function should be:
Meteor.users.update( { _id: currentUserId }, {$addToSet:
{
'address.streetAddress': addressDetails.address.streetAddress
}
});
}
One more thing, you should not pass the userId from the client. Any method you define is callable from the client and like that, I would be able to call your method 'addAddress' from the browser console with any userId to update their address. Instead, use the this.userId object in method calls (see here) and check that it is not null, i.e. user is logged in.
if (! this.userId)
throw new Meteor.Error(401, "You must be logged in!");
var currentUserId = this.userId;
It looks like you're passing in the document id directly into the MongoDB query method, rather than constructing an object with an _id property with a value of said document id.
i.e try
var currentUserId = { _id: currUserId };
I know that there is a question named : Ember data: what is difference between find and findById?. However, correct if I'm wrong, but I think that it relates to an older version of Ember data since I can't find this method in the embet-data doc.
I was trying to insert a new category in my catalog. This wouldn't work:
newRecord: function() {
catalog = this.store.find('catalog', 1);
record = this.store.createRecord( 'category', {category_name_fr_sh: 'Nouvelle categorie'});
catalog.get('catalog_categories_ids').pushObject(record);
this.set('content', record);
},
But this work :
newRecord: function() {
catalog = this.store.getById('catalog', 1);
record = this.store.createRecord( 'category', {category_name_fr_sh: 'Nouvelle categorie'});
catalog.get('catalog_categories_ids').pushObject(record);
this.set('content', record);
},
The doc says
Get a record by a given type and ID without triggering a fetch.
This method will synchronously return the record if it's available. Otherwise, it will return null.
I really don't understand why "trggering the fetch" wouldn't work. I tought that the find() first look if it's in the store cache and only fetch if it doesn't find it. Can someone enlighten me?
this.store.find('catalog', 1); doesn't return the record, it return a DS.PromiseObject. Because, if your record is not present in the record cache, a request to the server is needed. If the record is already loaded, you still have the promise object, to keep the same method behavior, but no request is sent to the server.
this.store.getById('catalog', 1); return the object from the record cache if present. Probably this work because you already loaded the catalogs using this.store.find('catalog'); or this.store.find('catalog', 1);
You can get the catalog record from DS.PromiseObject using then method:
newRecord: function() {
var self = this;
var catalogPromise = this.store.find('catalog', 1);
catalogPromise.then(function(catalog) {
var record = this.store.createRecord( 'category', {category_name_fr_sh: 'Nouvelle categorie'});
catalog.get('catalog_categories_ids').pushObject(record);
self.set('content', record);
})
},
We are having a little problem with a functional test with casper.js.
We request the same resource twice, first with the GET and then with POST method.
Now when waiting for the second resource (POST) it matches the first resource and directly goes to the "then" function.
We would like to be able to check for the HTTP method in the "test" function, that way we can identify the resource properly. For now we use the status code (res.status), but that doesn't solve our problem fully, we really need the http method.
// create new email
this.click(xPath('//div[#id="tab-content"]//a[#class="button create"]'));
// GET
this.waitForResource('/some/resource',
function then() {
this.test.assertExists(xPath('//form[#id="email_edit_form"]'), 'Email edit form is there');
this.fill('form#email_edit_form', {
'email_entity[email]': 'test.bruce#im.com',
'email_entity[isMain]': 1
}, true);
// POST
this.waitForResource(
function test(res) {
return res.url.search('/some/resource') !== -1 && res.status === 201;
},
function then() {
this.test.assert(true, 'Email creation worked.');
},
function timeout() {
this.test.fail('Email creation did not work.');
}
);
},
function timeout() {
this.test.fail('Email adress creation form has not been loaded');
});
Or maybe there is a better way to test this scenario? Although since this is a functional test we need to keep all those steps in one test.
You can try to alter the form action url to add some query string, therefore generating a new resource appended to the stack. Could be done this way:
casper.thenEvaluate(function() {
var form = __utils__.findOne('#email_edit_form');
form.setAttribute('action', form.getAttribute('action') + '?plop');
});
That's a hack though, and functional testing should never be achieved that way. Let's hope more information will be added to the response objects in the future.
The res parameter that is passed to the test function has an ID. I created a helper that tests against this ID and blacklists it, so the same resource won't get accepted a second time.
var blackListedResourceIds = [],
testUniqueResource = function (resourceUrl, statusCode) {
return function (res) {
// check if resource was already loaded
var resourceFound = res.url.search(resourceUrl) !== -1;
// check statuscode
if (statusCode !== undefined) {
resourceFound = resourceFound && res.status === statusCode;
}
// check blacklisting
if (!resourceFound || blackListedResourceIds[res.id] !== undefined) {
return false;
} else {
blackListedResourceIds[res.id] = true;
return true;
}
};
};