Initializing an angular resource object instance without calling the server - caching

In Backbone.js, one can instantiate a model with pre-existing JSON data by using
var user = new Backbone.Model({ name: "John Doe", age: 30 });
And then perform updates with a single call
user.save({ age: 31 }, { success: successCallback, error: errorCallback });
This comes handy in all sorts of scenarios, such as when that data is already available on the client through caching or pre-populated templates (like using <%= raw #user.to_json %> in rails erb templates).
I'm curious to know how this is done when using $resource with Angular.js. Right now, I have a situation where my data is cached on the client before the $resource is created, and I'd like to update it without making an extra GET call to populate the object:
app.factory('User', ['$resource', function($resource) {
return $resource('/users/:user_id.json', { user_id: '#id' }, {
get: { method: 'GET' },
save: { method: 'PUT' },
create: { method: 'POST' },
destroy: { method: 'DELETE' }
});
}]);
And somewhere in my controller I'd like to just update an existing user without fetching it from the server:
app.controller('UsersController', ['User', function(User) {
// somehow initialize a user from an existing
// object, say { name: "John Doe", age: 30 }
user.age = 31
user.$save()
// instead of doing this
User.get({ user_id: 100 }, function(user, respHeaders) {
user.age = 31
user.$save()
});
}]);
I'm probably still in the Backbone mindset, but I'm really curious if I'm approaching this all wrong. Thanks.

$resource gives the option to create a new user like you would with any object:
var user = new User({name : 'John Doe', age: 30});
user.age = 32;
user.$save();
Resource Docs

Related

Strapi update username from custom controller

I am trying to create a custom controller to update the user profile.
I created the routing file and the corresponding controller.
Routing file: server/src/api/profile/routes/profile.js
module.exports = {
routes: [
{
method: 'GET',
path: '/profile',
handler: 'profile.getProfile',
},
{
method: 'PUT',
path: '/profile',
handler: 'profile.updateProfile',
},
]
}
Controller: src/api/profile/controllers/profile.js
async updateProfile(ctx) {
try {
const { id } = ctx.state?.user;
const user = strapi.query('admin::user').update({
where: { id },
data: {
username: "testUsername"
}
})
ctx.body = "User updated"
} catch(error) {
ctx.badRequest("Something went wrong", { error })
}
},
The above code returns "User updated", but the username does not update. I am executing the PUT call with a correct Bearer authorisation token and the user permissions for that user are set to enable "updateProfile".
Oddly enough, the same code, when changed to update a different API item, works perfectly fine:
async updateArticle(ctx) {
try {
const { id } = ctx.state?.user;
const article = strapi.query('api::article.article').update({
where: { author: id },
data: {
title: "New title"
}
})
ctx.body = article
} catch(error) {
ctx.badRequest("Something went wrong", { error })
}
},
I am also confused by different syntaxes appearing in the official Strapi documentation, for example some docs mention:
strapi.query('admin::user').update({ id }, data)
But in other places in the documentation its:
strapi.plugins['users-permissions'].services.user.update({ id });
And then elsewhere:
strapi.query('user', 'users-permissions').update(params, values);
Another question is: do I need to sanitise the input / output in any way? If yes, how? Importing sanitizeEntity from "Strapi-utils" doesn't work, but it's mentioned in several places on the internet.
Additionally, I cannot find a list of all ctx properties. Where can I read what is the difference between ctx.body and ctx.send?
The lack of good documentation is really hindering my development. Any help with this will be greatly appreciated.

What is the best method of getting objects from a class that belong to the current user

I'm just starting out with Parse (REST API) and would like to know the best method of getting private objects from a class that belong to the current user. As far as I see it, there are two options:
1/ Pass the user id in the query string - I thought this was bad practise though.
2/ Each object would have an ACL set - could I just hit the class endpoint and let the ACL's take care of things and just return the objects that belong to the user?
Thanks.
You potentially could just let the ACL handle things, using the user's session token. This requires public find access to be disabled, though.
It's also not ideal, as it can not utilize database indexes. Your database is going to find all objects that match your query and parse-server will filter out the ones that don't pass a CLP / ACL check. At scale, this could damage performance significantly. Do objects only belong to a single user? Consider storing the user's ID on the object, then you can query for all objects that match that ID, and you can index your database by that id for fast results.
Jake's answer is spot on, but I couldn't resist trying it out using rest, so in case its helpful to anyone.....
const request = require('request-promise');
const baseUrl = 'http://localhost:8378/1'
let sessionToken;
let userId;
let userPointer;
/**
* This test will setup a user, get its sessionToken.
* Create three objects. Two with both a pointer to
* our test user and an acl that only gives our user permission
* to read them. The third test object has no acl so anyone can
* read it.
*
* We then test this setup with two tests:
*
* The first test does a rest query with our test user's
* session token and a where clause to only get our test
* user's objects.
*
* The second test makes an anonymous query to get all
* of the test object. We then verify that we only get
* the one object that is not restricted to our test user.
*/
fdescribe('Get own objects', () => {
beforeEach((done) => {
// create a user to test with
request.post({
uri: `${baseUrl}/users`,
headers: {
'X-Parse-Application-Id': 'test',
'X-Parse-REST-API-Key': 'rest',
},
json: true,
body: {
username: 'tester',
password: 'test'
}
})
.then((r) => {
sessionToken = r.sessionToken;
userId = r.objectId;
userPointer = {
__type: 'Pointer',
className: '_User',
objectId: userId
};
// making the acl in two steps so a prop can
// be set from a variable. The user id was
// returned in the response under 'objectId'
const ACL = {};
ACL[userId] = { read: true, write: true };
return request.post({
uri: `${baseUrl}/batch`,
headers: {
'X-Parse-Application-Id': 'test',
'X-Parse-REST-API-Key': 'rest',
// add our test users sessionToken so the request is
// made with her permissions.
'X-Parse-Session-Token': sessionToken,
},
json: true,
body: {
requests: [
{
method: "POST",
path: "/1/classes/Stuff",
body: {
user: userPointer,
ACL // <-- shorthand notation for ACL: ACL
}
},
{
method: "POST",
path: "/1/classes/Stuff",
body: {
user: userPointer,
ACL
},
},
{
method: "POST",
path: "/1/classes/Stuff",
body: {
// just an empty one so without ACL so we can make sure our where query is working.
}
},
]
}
})
})
.then(done)
.catch(done.fail)
});
it('should be able to fetch our own objects', (done) => {
// make sure our test user can get her own objects
const where = {
user: userPointer
}
request.post({
uri: `${baseUrl}/Classes/Stuff`,
json: {
where,
_method: 'GET'
},
headers: {
'X-Parse-Application-Id': 'test',
'X-Parse-REST-API-Key': 'rest',
'X-Parse-Session-Token': sessionToken,
}
})
.then((response) => expect(response.results.length).toBe(2))
.then(done)
.catch(done.fail);
});
it('should fail to fetch objects that aren\'t ours', (done) => {
// make sure a request not made by our test user doesn't return
// our test user's objects.
request.get({
uri: `${baseUrl}/Classes/Stuff`,
headers: {
'X-Parse-Application-Id': 'test',
'X-Parse-REST-API-Key': 'rest',
},
json: true
})
.then((response) => expect(response.results.length).toBe(1))
.then(done)
.catch(done.fail);
});
});

Need more fields than 'className' '_objCount' and 'id' when making a find() query against Parse.com

When making a find() request against Parse.com, each of the results I get back only contain the following fields: 'className' '_objCount' and 'id'.
How to get all fields ? (I have more than 3 columns in the database browser)
How to get specific fields ?
UPDATE 1
1) Screenshot of the headers of my class, named "Code"
2) Query:
var Code = Parse.Object.extend('Code');
var query = new Parse.Query(Code);
query.equalTo('codeSet', codeSet);
query.find()
.then(function(codes) {
console.log(codes);
}
3) Results:
[ { className: 'Code', _objCount: 1, id: '6cMkEQxE6A' },
{ className: 'Code', _objCount: 2, id: 'uOU7osLvOo' },
{ className: 'Code', _objCount: 3, id: 'Vks4QEpBlh' },
{ className: 'Code', _objCount: 4, id: 'dWtukZhsHZ' } ]
UPDATE 2
The code is hosted in a heroku-hosted node.js instance
Use PFObject get method. like user.get('nickname');
https://parse.com/docs/js/api/classes/Parse.Object.html
Just incase anyone comes accross this problem, I'm going to post an answer as it took me several hours to find the solution, which is to use toJSON().
Example:
var Parse = require('parse/node');
var User = Parse.Object.extend("User");
const query = new Parse.Query(User);
await query.get("object-id-here")
.then((user) => {
// The object was retrieved successfully.
console.log("The User Object", user.toJSON())
}, (error) => {
// The object was not retrieved successfully.
console.log("Error:", error)
});

ExtJS 5.0.1: Unable to use anonymous models in a Session

I'm getting this error:
[E] Ext.data.Session.checkModelType(): Unable to use anonymous models
in a Session
when trying to use a Session when binding a Grid with a Store via ViewModel:
ViewModel:
Ext.define('App.view.gridViewModel', {
extend: 'Ext.app.ViewModel',
alias: 'viewmodel.gridview',
stores:{
gridstore: {
model: 'gridView',
autoLoad: true,
//This triggers the Exception:
session: true,
listeners: {
beforeload: function(store, operation, eOpts) {
var oProxy = this.getProxy();
oProxy.setExtraParams({
tableName: 'dbo.SomeTable'
, identityKey: "id"
, primaryKey: ["id"]
, mode: "readonly"
, lang: "es"
, output: 'json'
});
}
}
}
}
});
View:
Ext.define('App.view.gridView', {
extend: 'Ext.form.Panel',
//...
viewModel: {
type: 'gridview'
},
controller: 'gridview',
// Create a session for this view
session:true,
items: [{
xtype: 'grid',
reference: 'myGrid',
bind: '{gridstore}',
columns: [
//...
]
}]
//...
});
Model's data is fetch through a Proxy:
Model:
Ext.define("App.model.gridView", {
extend: 'Ext.data.Model',
schema: {
namespace: 'App.model'
},
proxy: {
//proxy remote api stuff......
}.
idProperty: 'id'.
primaryKeys: 'id'.
fields: [
//fields
]
});
I have no idea what an anonymous model is and I haven't found anything related in the web, any ideas?
Thanks in advance!
The reason seems to be that in my Server's response I have a JSON Object called "metaData", which collides with the one available in JSON Reader:
Response MetaData
The server can return metadata in its response, in addition to the
record data, that describe attributes of the data set itself or are
used to reconfigure the Reader. To pass metadata in the response you
simply add a metaData attribute to the root of the response data. The
metaData attribute can contain anything, but supports a specific set
of properties that are handled by the Reader if they are present:
http://docs.sencha.com/extjs/5.0/apidocs/#!/api/Ext.data.reader.Json
The curious thing is that I don't use any of the available metaData options for JSON Reader, nothing related to any anonymous model, therefore this might be considered a bug

why I can't get JSON from server in ExtJs4?

I wanna get JSON from the server.And here is the django server view function:
def showChart(request):
data = [{"id":1, "name":"Tom", "email":"a#a.com"}, {"id":2, "name":"Bosh", "email":"c#c.com"}]
return HttpResponse(json.dumps(data), mimetype="application/json");
Obviously, showChart() will return a json.
My frontend extjs4 code:
Ext.onReady(function() {
Ext.define('ProductionInfo', {
extend: 'Ext.data.Model',
fields: ['email', 'id', 'name']
});
var store_new = Ext.create('Ext.data.Store', {
model: 'ProductionInfo',
proxy: {
type: 'ajax',
url : 'http://localhost:8000/production_data',
reader: {
type: 'json'
}
}
});
store_new.load();
alert(store_new.getCount());
});
But the alert dialog shows '0', while the right answer is '2'. So why I can't get JSON from server? (I can get the right JSON through a GET request in Chorme and Firefox)
You could check my sample: https://github.com/lucassus/extjs4-account-manager
It's rails application but json mechanism is very similar.
Basically you have to define a model with valid proxy: https://github.com/lucassus/extjs4-account-manager/blob/master/app/assets/javascripts/app/model/User.js
and a store which will have model attribute: https://github.com/lucassus/extjs4-account-manager/blob/master/app/assets/javascripts/app/store/Users.js

Resources