Ember.js data records shows content: null in relationships inspector - node.js

I have the following code in my 'user.js' model in ember-data:
export default DS.Model.extend({
organization: DS.belongsTo('organization'),
//other stuff
});
The CRUD for the website is working as expected, and in MongoDB I can see the following for the organization field of User:
"organization" : ObjectId("571974742ce868d575b79d6a"),
BUT, and I'm not sure if this is an error in my code or me not understanding how Ember-data works, I cannot access that ID from a model hook like so:
model(){
return this.store.findRecord("user", this.get("session.currentUser.id"))
.then(user => this.store.findRecord("location", {organization: user.organization}));
}
And if I go to the Ember inspector to observe the belongsTo attribute of the User object, I see:
organization: <(subclass of Ember.ObjectProxy):ember956>
But clicking through I see content: null
What am I doing wrong? Could it be a server-side error?
Edit including JSON response from server for the above findRecord("user") call:
{
"links":{
"self":"/users/5719749a2ce868d575b79d6b"
},
"included":[
{
"type":"organizations",
"id":"571974742ce868d575b79d6a",
"links":{
"self":"/organizations/571974742ce868d575b79d6a"
},
"attributes":{
"creation-date":"2016-04-22T00:46:44.779Z"
}
}
],
"jsonapi":{
"version":"1.0"
},
"data":{
"type":"users",
"id":"5719749a2ce868d575b79d6b",
"links":{
"self":"/users/5719749a2ce868d575b79d6b"
},
"attributes":{
"email":"danthwa#gmail.com",
"first-name":"Daniel",
"last-name":"Thompson",
"registration-date":"2016-04-22T00:47:22.534Z"
},
"relationships":{
"organization":{
"type":"organizations",
"id":"571974742ce868d575b79d6a"
}
}
}
}

Confirmed. As stated by Kingpin2k,
the relationships isn't being built up correctly, I think the type and id inside of organization need to be within a data object.
This applies to Ember sites expecting a JSON API spec payload, meaning they have been configured to use JSONAPISerializer for incoming payloads.

Related

Apollo Cache ignoring Field Alias as key (MockedProvider)

I am seeing some differences in behaviour between ApolloProvider and MockedProvider and it's throwing an error in testing.
Assuming I have the following query:
query {
Author {
id: authorID
name
}
}
In ApolloProvider this query creates entries in the Apollo Cache using the field alias as the key, each Author in the cache has an id. Therefore, Apollo can automatically merge entities.
When using MockedProvider, this is not the case. When I mock the following response:
const mockResponse = {
data: {
Author: {
id: 'test!!',
name: 'test'
},
},
}
I get the following error:
console.warn
Cache data may be lost when replacing the Author field of a Query object.
To address this problem (which is not a bug in Apollo Client), define a custom merge function for the Query.Author field, so InMemoryCache can safely merge these objects:
existing: {"authorID":"test!!"...
So the exact same query in ApolloProvider uses id (field alias) as the key and in MockedProvider it just adds authorID as another field entry. It ignores the field alias and has no key.
Obviously now nothing is able to merge. My first guess is that it's because the MockedProvider does not have access to the schema so it doesn't know that authorID is of type ID? Or am I way off?
One thing that's really weird to me is that my mockResponse doesn't even provide an authorID. My mockResponse is { id: "test!!" } but the cache shows an entry for {"authorID":"test!!"}, so it's somehow 'unaliased' itself.
I'm really struggling to understand what is happening here. Any insight at all would be enormously useful.

Loopback 3 crashes when trying to validate a model attached to User base model

I'm stuck at the validation process of my API. I want to validate multiple objects before storing them into the DB. I've created a function that I can call in my remote method in order to handle the validation.
The function below is working as excepted only the Account validation is causing trouble. My Loopback API crashed if the account object is not valid. Very weird because I do exactly the same with the other 2 models?
I've tried many things but nothing is working. Hopefully someone can point me in the right direction.
async function validate() {
vehicle.isValid(function(valid) {
if (!valid)
throw (vehicle.errors);
});
repairJob.isValid(function(valid) {
if (!valid){
console.log(repairJob.errors);
throw (repairJob.errors);
}
});
account.isValid(function(valid) {
if (!valid){
console.log(account.errors);
throw (account.errors);
}
});
}
Output from console:
Browse your REST API at http://localhost:3000/explorer
Errors {
password: [ 'can\'t be blank' ],
email: [ 'can\'t be blank', 'can\'t be blank' ] }
/Users/xxx/Development/xxx/xxx-api-v2/common/models/job.js:105
throw (account.errors);
^
[object Object]
[nodemon] app crashed - waiting for file changes before starting...
In your account.json, you might have mentioned "base": "User", you inherited account model from user model.As you can see below is the user.json where email and password properities are made required:true and so the loopback throws the error.You can change this in account.json by making email and password required:false if you dont want these two fields to be mandatory.Orelse check your request whether these two fileds have value.
According to documentation (https://loopback.io/doc/en/lb3/Validating-model-data.html) you can use isValid method
To invoke the validation constraints explicitly
The same loopback will do automatically each time model instance is created or updated.
As I understand the account model has set "base": "User" and you can't have a user without a password or email.
Can you add how account look like before calling isValid function?

LoopBack Remote Methods and Access to Model Data

I've been working on this for hours and I'm completely lost, because the loopback documentation is not helpful.
I'm trying to write application logic into a model. The documentation for that is here. Unfortunately, the example doesn't demonstrate anything useful other than passing an external value into the remote method and returning it again. I'd like to understand how to run a query in this context and access model data, but I have searched for hours and not been able to find documentation on even these simple tasks. Maybe I'm just looking in the wrong places. Can anyone help?
Typically, you can accomplish most things you'd want to do such as querying and accessing model data (CRUD operations) through the built-in methods that all models get; see http://docs.strongloop.com/display/LB/Working+with+data. Defining a remote method (custom REST endpoint) for these would be redundant.
You access the standard model CRUD Node APIs (e.g. myModel.create(), myModel.find(), myModel.updateAll() ) in the remote method code if you want to.
You may also find further related examples in https://github.com/strongloop/loopback-example-app-logic
Here's an example using the Getting Started app https://github.com/strongloop/loopback-getting-started app. It defines a remote method that takes a number arg and prints the name of the coffeeshop with that ID to the console:
This code is in common/models/coffeeshop.js:
module.exports = function(CoffeeShop) {
...
// Return Coffee Shop name given an ID.
CoffeeShop.getName = function(shopId, cb) {
CoffeeShop.findById( shopId, function (err, instance) {
response = "Name of coffee shop is " + instance.name;
cb(null, response);
console.log(response);
});
}
...
CoffeeShop.remoteMethod (
'getName',
{
http: {path: '/getname', verb: 'get'},
accepts: {arg: 'id', type: 'number', http: { source: 'query' } },
returns: {arg: 'name', type: 'string'}
}
);
};
You can use the API Explorer to load http://0.0.0.0:3000/explorer/#!/CoffeeShops/getName then enter a number (there are only three coffee shops in the app initially) as the query param and hit "Try It Out!"
Or just GET a URL like http://0.0.0.0:3000/api/CoffeeShops/getid?id=1
Rand
I finally discovered my problem. Object properties must be loaded in a callback of the function calling the CRUD operation. The following syntax worked for me:
module.exports = function (TestModel) {
TestModel.testRemoteMethod = function (id, name, cb) {
TestModel.findOne({where: {id: id}}, function(err, modelInstance) {
//modelInstance has properties here and can be returned to
//the API call using the callback, for example:
cb(null, {"name": modelInstance.name});
}
}
TestModel.remoteMethod('testRemoteMethod',
//..rest of config

Merge two endpoint using a different name and schema with Eve

This is a simplified version of my settings.py:
accounts_schema = {
'username': {
},
'password': {
}
}
accounts = {
'schema': accounts_schema,
}
After a user is created with a POST request to the /accounts endpoint, the user's info can be retrived with a GET to /accounts/<id_of_user>.
I would like to know if it possible to "merge" two endpoints that are using a different schema so
POST /update_accounts/<id_of_user>
will point to
/accounts/<id_of_user>
but update_accounts must have, for example, this schema:
update_accounts_schema = {
'token': {
},
'validity': {
}
}
From the documentation:
Multiple API endpoints can target the same database collection. For example you can set both admins and /users to read and write from the same people collection on the database.
So you can have update_accounts and accounts both targeting the same datasource, and each one with its own user privileges/allowed methods, etc.

SailsJs/Express removing nested array `res.send()`

I'm using SailsJs (which is Express based) to send an JSON object with an array. For some reason, when I load the API in my browser, the array is not sent.
The code that sends the object is here:
exports.RESTifySend = function(res, objects) {
return RESTService.RESTify(objects).then(function(RESTedObjects) {
console.log("SENDING: ", RESTedObjects);
return res.json(RESTedObjects, 200); // I've also tried res.send()
}, function() {
res.send(500);
});
};
The logging statement SENDING: ... outputs:
SENDING: {
id: 'IKIlrgXhp6',
messages: [{
user: null,
text: 'trest',
sentAt: undefined
}]
}
The RESTifyService is just a small framework I built to remove object attributes that shouldn't be exposed in the API (passwords, emails, etc.).
Somewhere in the framework I built, I replaced toObject(); with lodash.clone([object]), which solved all the problems. For others experiencing a similar issue, I suggest trying the same thing in your toJSON method. The toObject(); method of waterline objects apparently has some odd side effects when you true to populate or edit an attribute that matches the name of an association.
Also ran into this issue when serializing a model with a collection attribute (like something with many Comments).
In my case I call .toJSON on the record object with the collection during serialization, which has a model definition like:
attributes: {
...
toJSON: function() {
var self = this.toObject()
// could pick a subset of attrs here
return _.pick(self, _.keys(self))
}
}
Which leaves me free to then populate the comments attribute and not have surprises about the data on the wire.

Resources