ember multi level has many serialization issue - serialization

I am facing some issues with my serialization of model. Below is a detailed code which will show how my current model and corresponding serializers look.
User model
export default Model.extend({
name: attr('string'),
accounts: hasMany('account', {async: false})
});
Account model
export default Model.extend({
type: attr('string'),
transactions: hasMany('transaction')
})
Transaction model
export default Model.extend({
date: attr('string'),
amount: attr('number')
})
So basically its a hasMany within another hasMany.
Serializers looks like this:
base serializer:
export default BaseSerializer.extend({
keyForAttribute(key) {
return key.underscore();
},
keyForRelationship(key, relationship) {
return key.underscore();
}
});
User serializer:
export default BaseSerializer.extend(DS.EmbeddedRecordsMixin, {
attrs: {
account: { embedded: 'always' }
}
});
Account serializer:
export default BaseSerializer.extend(DS.EmbeddedRecordsMixin, {
attrs: {
transaction: { embedded: 'always' }
}
});
Now when I am invoking api call which gives me a json response where user model has a property named as accounts: which internally have another property called as transactions, I am expecting them to serialize but somehow its not working. have I done anything wrong here? Please advise as I am new to ember and still learning it.
Base serializer is:
export default DS.JSONSerializer.extend(DS.EmbeddedRecordsMixin, {
keyForRelationship(key, relationship) {
return key.underscore();
}
})
Serialized json
I dont have it but from the console logs, seems like only user model is getting serialized as when I tried to print console.log(user.get('accounts').get('firstObject').get('type') then i saw undefined there.
What I want is:
{
name: "bhavya"
accounts: [
{
type : 'savings',
transactions: [
{
amount: 500
}
]
}
]
}

Related

How to read user from request in a Mongoose midddleware hook or a service in NestJS

Assume a user is logged in and there is a Post document, I want to save the user who created and updated the post.
export interface Post extends Document {
readonly title: string;
readonly content: string;
readonly createdAt?: Date;
readonly updatedAt?: Date;
readonly createdBy?: User;
readonly updatedBy?: User;
}
export const PostSchema = new Schema({
title: SchemaTypes.String,
content: SchemaTypes.String,
createdAt: { type: SchemaTypes.Date, required: false },
updatedAt: { type: SchemaTypes.Date, required: false },
createdBy: { type: SchemaTypes.ObjectId, ref: 'User', required: false },
updatedBy: { type: SchemaTypes.ObjectId, ref: 'User', required: false },
});
But I have no idea how to read the user from the request in a service component or Mongoose document schema.
It really depends on how you authenticate your users, but you should be able to intercept whichever access token, cookies (...) that the frontend uses to authentifies itself.
You can intercept it in the controller and get the corresponding user (or userId) which you can then pass to your service.
In case you're using nestjs passport, your example could look like this:
import { Injectable, Request, Body } from '#nestjs/common';
import { AuthGuard } from '#nestjs/passport';
#Injectable()
export class JwtAuthGuard extends AuthGuard('jwt') {}
import { Controller, Bind, Request, Post, UseGuards } from '#nestjs/common';
import { AuthGuard } from '#nestjs/passport';
import { JwtAuthGuard } from './auth/jwt-auth.guard';
#Controller()
export class PostController {
#UseGuards(JwtAuthGuard)
#Put('post')
addPost(#Request() request, #Body() body) {
const user = request.user._id;
// We only need the id to add it to mongo but you could also pass the whole user
return this.createPost(body, user._id)
}
}
#Injectable()
class PostService
//...
async createPost(body, userId) {
this.postModel.create({
...body,
createdBy: userId
})
}
//...
If you want more information about how to use nestjs passport and implement auth guards, see here nestjs docs

Apollo query that depends on another query's result

I’m trying to create an Apollo query (in Vue/Nuxt) that depends on the result of another query.
sessions needs person in order to use this.person.id. How can I ensure that person exists before getting data for sessions?
My script and queries are below. Both queries work fine in GraphiQL. Thank you!
JS
import gql from 'graphql-tag'
import Photo from '~/components/Photo.vue'
import Session from '~/components/Session.vue'
import {Route} from 'vue-router'
import { currentPerson, currentPersonSessions } from '~/apollo/queries.js'
export default {
name: 'Speaker',
props: ['slug', 'id', 'person'],
data() {
return {
title: 'Speaker',
routeParam: this.$route.params.slug
}
},
apollo: {
person: {
query: currentPerson,
loadingKey: 'loading',
variables() {
return {
slug: this.routeParam
}
}
},
sessions: {
query: currentPersonSessions,
loadingKey: 'loading',
variables() {
return {
itemId: this.person.id
}
}
}
},
components: {
Photo,
Session
}
}
Queries
export const currentPerson = gql `query ($slug: String!) {
person(filter: { slug: { eq: $slug }}) {
name
bio
affiliation
id
photo {
url
}
}
}`
export const currentPersonSessions = gql `query($itemId: ItemId!) {
allSessions (filter: { speakers: { anyIn: [$itemId] }}) {
title
slug
start
end
}
}`
I managed to solve this by breaking out the part the sessions into a separate component.
Is that the appropriate way to handle a case like this, or is there a better way by chaining the requests here? If there's a way to do this, I still wouldn't mind hearing other answers.
I think the best way is to have a proper graph of your data model like :
type Person {
name: String!
bio: String
affiliation: String
id: ID!
photo: Photo
sessions(filter: SessionFilter): SessionList!
}
With GraphQL, you model your business domain as a graph
Graphs are powerful tools for modeling many real-world phenomena because they resemble our natural mental models and verbal descriptions of the underlying process. With GraphQL, you model your business domain as a graph by defining a schema; within your schema, you define different types of nodes and how they connect/relate to one another. On the client, this creates a pattern similar to Object-Oriented Programming: types that reference other types
You can read more in the official docucmentation

VueJS,vue-form-generator Accessing $store from field callback

I am using the vue-form-generator component. I am attempting to update a variable in the store with the response from a call back. What I believe the relevant code is (inside the schema:fields property):
{
type: 'submit',
buttonText: 'Save Updates',
styleClasses: ['custom-submit', 'custom-submit-primary', 'custom-submit-full'],
disabled () {
return this.errors.length > 0
},
validateBeforeSubmit: true,
onSubmit: function (model, schema) {
Vue.CustomSubmit('UserUpdate', model).then((response) => {
this.$store.user = response
})
}
}
From within the Vue.CustomSubmit().then((response)) => { ... }
I don't seem to be able to access the store.
It works slightly higher in scope, such as:
data () {
return {
avatar: this.$store.getters.user.avatar,
...
}
You cannot modify the $store directly. That's the most important part of the vuex concept. You need to use a mutation and commit it after you got the data.
Here it is described https://vuex.vuejs.org/en/mutations.html

Ember, Ember Data - Updating hasMany relation

I'm trying to update a hasMany relation in Ember but I'm having a bit of trouble getting the data to send correctly.
I have the following Ember models:
// models/post.js
export default DS.Model.extend({
title: DS.attr('string'),
tags: DS.hasMany('tag', { async: true })
});
// models/tag.js
export default DS.Model.extend({
title: DS.attr('string'),
post: DS.belongsTo('post', { async: true })
});
And then I have the following action in my route to create a new tag and update the post:
addTag: function() {
var post = this.get('currentModel');
var newTag = this.store.createRecord('tag', {
title: 'Lorem Ipsum',
post: post
});
newTag.save().then(function() {
post.get('tags').pushObject(newTag);
post.save();
});
}
The new tag is created successfully and saved by my Express api but the post doesn't get saved correctly. It's received by the api but the request payload made by Ember never contains the tag IDs (it does send the tag title though). What am I doing wrong? Would really appreciate any help!
Edit
It turns out the RESTSerializer by default doesn't serialize and include the related IDs for a hasMany relationship. It only includes them for the belongsTo side as it expects the API to take care of saving it where needed. I will probably change my API to fit this behaviour as it's more efficient but in case any one else comes across this, it is possible to make the serializer include the IDs by extending the serializer and using the DS.EmbeddedRecordsMixin mixin - http://emberjs.com/api/data/classes/DS.EmbeddedRecordsMixin.html - Which would look something like this:
export default DS.RESTSerializer.extend(DS.EmbeddedRecordsMixin, {
attrs: {
tags: { serialize: 'ids' }
}
});
You don't need to call .save() on post. When you call createRecord to create a tag, your backend receives id of post and should persist dependencies accordingly.
addTag: function() {
var post = this.get('currentModel');
this.store.createRecord('tag', {
title: 'Lorem Ipsum',
post: post})
.save()
.then(function(tag) {
post.get('tags').pushObject(tag);
});
Meet the same problem.
Currently I solve it by serializeHasMany hook.
// app/serializers/application
import {ActiveModelSerializer} from 'active-model-adapter';
export default ActiveModelSerializer.extend({
serializeHasMany: function(snapshot, json, relationship){
this._super.apply this, arguments
if (!json[relationship.type + '_ids']){
var ids = []
snapshot.record.get(relationship.key).forEach(function(item){
ids.push item.get 'id'
});
json[relationship.type + '_ids'] = ids
}
}
})

where is the best place to adjust how ember-data talks to sails

I am trying to implement an ember-cli (ember-data) to sails.js connection. I have installed ember-data-sails and have a simple model and am using the socketAdapter...
adapters/application.js
import SailsSocketAdapter from 'ember-data-sails/adapters/sails-socket';
export default SailsSocketAdapter.extend({
useCSRF: true,
coalesceFindRequests: true,
namespace: 'api/v1',
//defaultSerializer: '-rest',
});
Example Model:
RequestType.js
module.exports = {
attributes: {
name : { type: 'string' },
desc : { type: 'string' },
requestSubType : { type: 'hasMany' }
}
};
So far it is mostly working, I can get a list of RequestTypes ok. However if i try and nest a route to display/create the hasMany relationship, then i have an issue.
request-sub-types/new/routes.js
This file, requests the Parent model requestType first, and then creates a new record, inserting the Parent in the belongsTo attribute.
import Ember from 'ember';
export default Ember.Route.extend({
model: function() {
var requestType = this.modelFor('requestTypes/view');
return requestType.get('requestSubTypes').createRecord({
requestType: requestType
});
}
});
request-types/view/route.js
This file is the model I think that is being requested above. The issue here is the params object that get fed through is:
{ requestType_id: 6 }
but if this is passed to the store.query call then nothing gets returned. If however I change the params object to:
{ id: 6 }
then I do see a record come back from the sails api.
import Ember from 'ember';
export default Ember.Route.extend({
model: function(params) {
console.log('RT: View Route MODEL', params);
var query = {};
if(params.requestType_id) {
query.id= params.requestType_id;
}
return this.store.query('requestType', query);
}
});
So My question is what do i need to edit to make the Primary Key definitions match up. so that Ember-data and Sails can talk to each other correctly? Is it in a serializer somewhere, the Sails API or in each model call?
thanks... and sorry if the above doesn't make any sense! :)