Roles entity has many-many relation with entitlement entity as shown below:
#ManyToMany(() => Entitlement)
#JoinTable({
name: 'role-entitlements',
joinColumn: {
name: 'role_id',
referencedColumnName: 'id',
},
inverseJoinColumn: {
name: 'entitlement_id',
referencedColumnName: 'id',
},
})
entitlements: Entitlement[];
Below given is the function to add multiple entitlements to a Role:
async linkEntitlement(id: string, linkEntitlementDto: LinkEntitlementDto) {
const role: Role = await this.findOne(id);
if (!role) {
throw new CustomException('role not found');
}
const entitlement: Entitlement = await this.entitlementService.findOne(
linkEntitlementDto.entitlementId,
);
if (!entitlement) {
throw new CustomException('entitlement not found');
}
const entitlements: Entitlement[] = role.entitlements;
role.entitlements = entitlements.concat(entitlement);
await this.rolesRepository.save(role);
}
Below given is the json content I am trying to post via Postman:
{
"entitlements":[
{"id":"12d7b37e-1464-4ffa-b9af-779ab298afb9"}
]
}
I am getting the error as mentioned above
What I am trying to do here is there's an Role entity and an entitlement entity. I
created many- many relationship between role and entitlement. I created a function
LinkEntitlement where I can map role & entitlement. I am posting the data via
postman here but I am facing such an error. What is it that I can change in this
code?below is the postman image
entitlements is undefined because you didn't fetch the relationship with Entitlement and it is lazily loaded by default.
To do so:
const role = await this.findOne(id, { relations: ['entitlements'] });
btw I advise you to mark that field entitlements: Entitlement[] as optional. Then you won't get confused on why TypeScript is telling you that something is not undefined when it is at runtime.
Related
I return the following JSON after confirming credentials:
{username: 'foo', name: 'bar', type: 123}
However, NextAuth does not allow me to store all the fields due to model limitations, so what it returns in JWT to client is:
{name: 'bar', email: null, image: null}
My [...nextauth].js setup is very basic:
providers: [
Providers.Credentials({
async authorize(credentials) {
const res = await fetch('http://localhost:3000/api/user', {
method: 'GET',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'credentials': JSON.stringify(credentials)
}
})
const user = await res.json()
if (user && !user.message) {
return user
} else {
return null
}
}
})
],
The only solution I came up with is to fake email field with JSON string in which I can store everything I need:
{name: 'bar', email: "{username: 'foo', type: 123}", image: null}
How can I do it properly? I tried looking into custom models (https://next-auth.js.org/tutorials/typeorm-custom-models), but it seems to be only about databases, which is not my case since I use JWT for session storage.
Also what drawbacks I can encounter if I continue with my solution?
You will need to persist the additional info through callbacks, at first through JWT's callback and then Session's callback:
callbacks: {
async jwt(token, user, account, profile, isNewUser) {
// Since you are using Credentials' provider, the data you're persisting
// _should_ reside in the user here (as far as can I see, since I've just tested it out).
// This gets called whenever a JSON Web Token is created (once) or updated
if (user?.type) {
token.status = user.type
}
if (user?.username) {
token.username = user.username;
}
return token
},
async session(session, token) {
session.type = token.type;
session.username = token.username;
return session
}
}
I want to update the newly created User's data. The returned JSON is:
{
"user":{
"uid":"test123",
"displayName":null,
"photoURL":null,
"email":"test12#test.com",
"emailVerified":false,
"phoneNumber":null,
"isAnonymous":false,
"tenantId":null,
"providerData":[
{
"uid":"test12#test.com",
"displayName":null,
"photoURL":null,
"email":"test12#test.com",
"phoneNumber":null,
"providerId":"password"
}
],
"apiKey":"test123",
"appName":"[DEFAULT]",
"authDomain":"test123.firebaseapp.com",
"stsTokenManager":{
"apiKey":"test123",
"refreshToken":"test123",
"accessToken":"test123",
"expirationTime":1571238989357
},
"redirectEventId":null,
"lastLoginAt":"1571235389108",
"createdAt":"1571235389108"
},
"credential":null,
"additionalUserInfo":{
"providerId":"password",
"isNewUser":true
},
"operationType":"signIn"
}
This is my callout and update:
createUser = async (userData) => {
return await firebase.auth().createUserWithEmailAndPassword(userData.get('userName'), userData.get('password'))
.then((authData) => {
firebase.database().ref('users/' + authData.user.uid + '/').set({
fullName: userData.get('fullName'),
pictures: userData.get('pictures'),
phoneNumber: userData.get('phoneNumber')
});
})
};
Is it possible to add to the User table custom fields?
A few things are happening. It appears that userData can not be seen in the .then statement. So to solve this I attempted to pass in the userData JSON as a param. This did not work. I then broke out each value out of userData, saved it into a const and passed that value. This did not work.
I can see that userData has values in it before the .then statement. I am able to successfully create a new user with the right userName and password. This means to me either:
A - I am not passing the userData JSON correctly or
B - I am not allowed to pass data to firebase like I am doing
My end goal is to sign up a user and then take all of the data they input from a registration form (aka userData) and update the user table with it.
Articles I am using are:
https://firebase.google.com/docs/auth/web/manage-users
https://medium.com/mindorks/firebase-realtime-database-with-react-native-5f357c6ee13b
Main class that calls the createUser function:
const signUp = (dispatch) => {
return async (userData)=>{
try{
const response = await config.createUser(userData);
console.log('sign up resonse1: ' + response); //coming back as undefined
//todo:: figure out how to parse out the apikey out of response
await AsyncStorage.setItem('token', '123mockToken');
dispatch({type: 'sign_up', payload: '123mockToken'});
navigate('mainFlow');
} catch(e){
dispatch({type: 'add_error', payload: '' + e}); //we call dispatch anytime we want to update our state
}
}
};
I understand that the parameter userData holds all the data you want to use for creating the user ("all of the data they input from a registration form").
The following should work:
createUser = async userData => {
try {
const userCredential = await firebase
.auth()
.createUserWithEmailAndPassword(
userData.get('userName'),
userData.get('password')
);
const userId = userCredential.user.uid;
await firebase
.database()
.ref('users/' + userId + '/')
.set({
fullName: userData.get('fullName'),
pictures: userData.get('pictures'),
phoneNumber: userData.get('phoneNumber')
});
return userId; //As per your comment below
} catch (error) {
return error;
}
};
The createUserWithEmailAndPassword() method returns a UserCredential which contains a User.
hello i want to display the data that i got from a mongodb using a backend api (nodejs)
this is the code for event model
const mongoose = require('mongoose');
const config = require('../config/database');
// Events Schema
const EventSchema = mongoose.Schema({
eventname: {
type: String,
required: true
},
eventstartdate: {
type: String,
required: true
},
eventenddate: {
type: String,
required: true
},
eventcategorie: {
type: String
},
eventdescription: {
type: String
},
eventimage: {
type: String
}
});
const Event = module.exports = mongoose.model('Event', EventSchema);
this is the code from the router
const express = require('express');
const router = express.Router();
const passport = require('passport');
const jwt = require('jsonwebtoken');
const config = require ('../config/database');
const User = require('../models/user');
const Event = require('../models/event');
//get event by id
router.get('/event/:eventid', (req,res) => {
Event.findById(req.params.eventid, (err, event) =>{
if (err){
return res.status(500).send({message:err.message});
}
if(!event){
return res.status(400).send({message:'Event not found'});
}
res.json({
event: {
id: event._id,
eventname: event.eventname,
eventstartdate: event.eventstartdate,
eventenddate: event.eventenddate,
eventcategorie: event.eventcategorie,
eventdescription: event.eventdescription,
eventimage: event.eventimage
}
});
});
});
and this is the code from the service in the angular
// GET an event by ID
displayEvent$(id: string) {
return this.http.get(`http://localhost:3000/users/event/${id}`)
.map(response => response.json());
}
then i created a simple method that is triggered by a button
and i passed an id of an event that i konw is in the database just to test it out
onclickeventpage(){
this.authService.displayEvent$('5ae0c8e96b40a71cd3b772cc').subscribe(event => {
console.log(event)
});
}
this gives me back at the console the event i need with every aribute
but whene i change this
console.log(event)
to this so i can get evey atribute separetly and then i an put them in the html
console.log(event.eventname)
i get undefined
i just want to know how to get every event atribute so i can display them in my html page
First you dont have to call .json() witn angular5
displayEvent$(id: string) {
return this.http.get(`http://localhost:3000/users/event/${id}`)
.map(response => response.json());
}
also you need to access
console.log(event.event.eventname);
HttpModule is deprecated and the new HttpClientModule by default formats the response to JSON so we no longer need to parse it using response.json():
I just want to know how to get every event attribute so that I can
display them on my HTML page
You can tell HttpClient the type of the response to make consuming the output easier and more obvious.
Typechecking of response can be done by using type parameter
export interface Ievent {
id:string
eventname: string
eventstartdate: string
eventenddate: string
eventcategorie: string
eventdescription: string
eventimage: string
}
Http returns an observable and We can tell the HttpClient.get to return response as Ievent type When we use http.get<Ievent>(...) then it returns the instance of Observable<Ievent> type.
In your service
import { HttpClient } from '#angular/common/http';
import { Observable } from 'rxjs/Observable';
import {Ievent} from './eventModel'
#Injectable()
export class authService()
{
constructor(private http:HttpClient){}
displayEvent$(id: string)Observable<Ievent> {
return this.http.get<Ievent>(`http://localhost:3000/users/event/${id}`);
}
}
In your component subscribe to Observable<Ievent> to get instance of Ievent
onclickeventpage(){
this.authService.displayEvent$('5ae0c8e96b40a71cd3b772cc').subscribe(event => {
console.log(event);
console.log(event.eventname)});
}
I have a problem with my application in angular2. I need to connect to api to retrieve records to my Persons [] class. I want to use the second method to get people with individual id. I do the same way as in the tutorial on the Angular site but unfortunately I still have a fault
ERROR TypeError: Cannot read property 'Name' of undefined
This is my service
getPersons(): Promise<Person[]> {
var currentUser = JSON.parse(localStorage.getItem('currentUser'));
var token = currentUser.token;
let headers = new Headers({ 'Authorization': 'Bearer ' + token })
let options = new RequestOptions({ headers: headers });
return this.http.get(this.QuestionsUrl, options)
.toPromise()
.then(response => response.json() as Person[]);
}
getPerson(id: number): Promise<Person> {
return this.getPersons().then(persons => persons.find(person => person.Id === id));
}
My component:
export class PersonsComponent implements OnInit {
activePerson: any = {};
model: any = {};
constructor(private personService: PersonService, private route: ActivatedRoute, private location: Location) {
}
ngOnInit(): void {
this.route.paramMap.switchMap((params: ParamMap) => this.personService.getPerson(+params.get('Id')))
.subscribe(selected => this.activePerson = selected);
}
}
And html:
<body>
{{activePerson.Name}}
Try using
{{activePerson?.Name}}
With a question mark.
The issue is that the template attempts to display the value before the data is retrieved. At that point, activePerson is undefined. Hence the error message.
The "?" is called a safe navigation operator. It prevents navigating to the "dot" property (name in this example) unless the object to the left of the question mark has a value.
use
{{activePerson |json}}
to know if you are receiving any data
How do you write query resolvers in GraphQL that perform well against a relational database?
Using the example schema from this tutorial, let's say I have a simple database with users and stories. Users can author multiple stories but stories only have one user as their author (for simplicity).
When querying for a user, one might also want to get a list of all stories authored by that user. One possible definition a GraphQL query to handle that (stolen from the above linked tutorial):
const Query = new GraphQLObjectType({
name: 'Query',
fields: () => ({
user: {
type: User,
args: {
id: {
type: new GraphQLNonNull(GraphQLID)
}
},
resolve(parent, {id}, {db}) {
return db.get(`
SELECT * FROM User WHERE id = $id
`, {$id: id});
}
},
})
});
const User = new GraphQLObjectType({
name: 'User',
fields: () => ({
id: {
type: GraphQLID
},
name: {
type: GraphQLString
},
stories: {
type: new GraphQLList(Story),
resolve(parent, args, {db}) {
return db.all(`
SELECT * FROM Story WHERE author = $user
`, {$user: parent.id});
}
}
})
});
This will work as expected; if I query a specific user, I'll be able to get that user's stories as well if needed. However, this does not perform ideally. It requires two trips to the database, when a single query with a JOIN would have sufficed. The problem is amplified if I query multiple users -- every additional user will result in an additional database query. The problem gets worse exponentially the deeper I traverse my object relationships.
Has this problem been solved? Is there a way to write a query resolver that won't result in inefficient SQL queries being generated?
There are two approaches to this kind of problem.
One approach, that is used by Facebook, is to enqueue requests happening in one tick and combine them together before sending. This way instead of doing a request for each user, you can do one request to retrieve information about several users. Dan Schafer wrote a good comment explaining this approach. Facebook released Dataloader, which is an example implementation of this technique.
// Pass this to graphql-js context
const storyLoader = new DataLoader((authorIds) => {
return db.all(
`SELECT * FROM Story WHERE author IN (${authorIds.join(',')})`
).then((rows) => {
// Order rows so they match orde of authorIds
const result = {};
for (const row of rows) {
const existing = result[row.author] || [];
existing.push(row);
result[row.author] = existing;
}
const array = [];
for (const author of authorIds) {
array.push(result[author] || []);
}
return array;
});
});
// Then use dataloader in your type
const User = new GraphQLObjectType({
name: 'User',
fields: () => ({
id: {
type: GraphQLID
},
name: {
type: GraphQLString
},
stories: {
type: new GraphQLList(Story),
resolve(parent, args, {rootValue: {storyLoader}}) {
return storyLoader.load(parent.id);
}
}
})
});
While this doesn't resolve to efficient SQL, it still might be good enough for many use cases and will make stuff run faster. It's also a good approach for non-relational databases that don't allow JOINs.
Another approach is to use the information about requested fields in the resolve function to use JOIN when it is relevant. Resolve context has fieldASTs field which has parsed AST of the currently resolved query part. By looking through the children of that AST (selectionSet), we can predict whether we need a join. A very simplified and clunky example:
const User = new GraphQLObjectType({
name: 'User',
fields: () => ({
id: {
type: GraphQLID
},
name: {
type: GraphQLString
},
stories: {
type: new GraphQLList(Story),
resolve(parent, args, {rootValue: {storyLoader}}) {
// if stories were pre-fetched use that
if (parent.stories) {
return parent.stories;
} else {
// otherwise request them normally
return db.all(`
SELECT * FROM Story WHERE author = $user
`, {$user: parent.id});
}
}
}
})
});
const Query = new GraphQLObjectType({
name: 'Query',
fields: () => ({
user: {
type: User,
args: {
id: {
type: new GraphQLNonNull(GraphQLID)
}
},
resolve(parent, {id}, {rootValue: {db}, fieldASTs}) {
// find names of all child fields
const childFields = fieldASTs[0].selectionSet.selections.map(
(set) => set.name.value
);
if (childFields.includes('stories')) {
// use join to optimize
return db.all(`
SELECT * FROM User INNER JOIN Story ON User.id = Story.author WHERE User.id = $id
`, {$id: id}).then((rows) => {
if (rows.length > 0) {
return {
id: rows[0].author,
name: rows[0].name,
stories: rows
};
} else {
return db.get(`
SELECT * FROM User WHERE id = $id
`, {$id: id}
);
}
});
} else {
return db.get(`
SELECT * FROM User WHERE id = $id
`, {$id: id}
);
}
}
},
})
});
Note that this could have problem with, eg, fragments. However one can handle them too, it's just a matter of inspecting the selection set in more detail.
There is currently a PR in graphql-js repository, which will allow writing more complex logic for query optimization, by providing a 'resolve plan' in the context.