Sequelize callback before save() is finished - promise

here is my code:
//Models definition
var User = sequelize.define('user', ...);
var Address = sequelize.define('address', ...);
var Club = sequelize.define('club', ...);
//Club scopes
scopes: {
withUser: {
include: [
{ model: User }
]
},
//Models associations
User.hasOne(Address);
Club.hasOne(User);
Club.hasOne(Address);
//Main
//Create address
var addressToCreate = {};
if(body.address) addressToCreate.address = body.address;
if(body.city) addressToCreate.city = body.city;
if(body.zipCode) addressToCreate.zipCode = body.zipCode;
//Get user from db
var user = await User.findByPk(body.user);
var clubToCreate = { name: body.name, phone: body.phone };
//Persist Address in db
return Address.create(addressToCreate)
.then(address => {
//Persist club in db
return Club.create(clubToCreate)
.then(club => {
//Associate User and Address to Club
club.setAddress(address);
club.setUser(user);
//Save club with associated models
return club.save()
})
.then(club => Club.scope('withUser').findByPk(club.id))
.then(club => { console.log(club); return club; })
})
In my db, table address contains userId and clubId and table user contains clubId;
This code seems to work to create and associate models. But the final club displayed by console.log shows user: null
However, in db there is the good row in table user with the good foreign key who reference the club id
My logs show that the request select (from Club.findByPk) is done before the update (from club.save). Like .then is executed before promise is resolved
Sry for my bad english, hope someone can help

You are using async/await but mixing it up with the older promise style based on your code that fetches Users. Below is a copy of your code in async/await style, although you may not need to refetch the club object, etc, so continue to debug.
const address = await Address.create(addressToCreate);
let club = await Club.create(clubToCreate);
console.log('created club', club);
club.setAddress(address);
club.setUser(user);
await club.save();
console.log('saved club', club);
club = await Club.scope('withUser').findByPk(club.id);
console.log('reloaded club', club);
return club;

Related

Parse Cloud Code Setting Relation On User Class

I have an issue with not getting some cloud code to run in Parse. Currently, I pass an objectId of another user who I want to set a review for. In client side, I am saving the review, and capturing that object. I pass that as well up to cloud code. Both show up there, but I cannot figure out the right way to set the relation on this user and apply that review that was created. Code snip is below. Any help would be appreciated before I bang my head against a wall.
Parse.Cloud.define("addReview", async (request, response) => {
let { userObjId, reviewObj } = request.params;
const query = new Parse.Query(Parse.User);
//get the user object to post review of
try{
let object = await query.get(userObjId, {useMasterKey: true});
console.log(object);
console.log("running");
object.set('Reviews', new Parse.Object(reviewObj));
object.save(null, { useMasterKey: true })
return ("saved relation and updated reviews");
} catch (err){
throw new Error(err.message)
}
});
I assume
reviewObj is the review object, the user recently created.
and in your User class, you got a to-many-relation to Review class, named 'Reviews'.
First I'd suggest to name your user object user instead of object, to make it clearer what you're dealing with and make your code easier to understand.
Since you said, the review object is already successfully saved on server side, I'd recommend to transmit only the reviewId in your request.
To add an object to a relation, you first need to get the relation property of your user object:
const reviewsRelation = user.relation('Reviews');
Then to add the object to the relation just call:
reviewsRelation.add(user);
In total, your snippet should look like this:
Parse.Cloud.define("addReview", async (request, response) => {
let { userObjId, reviewObjId } = request.params;
const userQuery = new Parse.Query(Parse.User);
const reviewQuery = new Parse.Query(Parse.Review);
//get the user object to post review of
try{
let user = await userQuery.get(userObjId, {useMasterKey: true});
let review = await reviewQuery.get(reviewObjId, {useMasterKey: true});
console.log(user);
console.log("running");
const reviewsRelation = user.relation('Reviews');
reviewsRelation.add(review);
user.save(null, { useMasterKey: true })
return ("saved relation and updated reviews");
} catch (err){
throw new Error(err.message)
}
});

Cannot insert two one-to-one entities using two EF repositories in one transaction (method hangs)

I have two entities bound as one-to-one via foreignkey: CreateTenantDto and SaasTenantCreateDto.
I need to use TWO repositories (_abpTenantRepository is an instance of 3rd party repository from ABP Framework) to insert those entities into DB. I am trying to use ABP UnitOfWork implementation for this. After SaasTenantCreateDto entity is inserted, I am trying to insert CreateTenantDto entry which depends on it. If I use OnCompleted event to insert a CreateTenantDto record - the method does not enter OnCompleted before returning newTenantDto and the latter is returned as a null (the records are inserted finally, but I want to return the inserted entity if it's inserted successfully). If I don't use OnCompleted at all - the method hangs (looks like DB lock). If I use two nested UnitOfWork objects - the method hangs as well. If I use the scope for working with two repositories -
using (var scope = ServiceProvider.CreateScope())
{
var unitOfWorkManager = scope.ServiceProvider.GetRequiredService<IUnitOfWorkManager>();
using (var tenantUow = unitOfWorkManager.Begin(new AbpUnitOfWorkOptions { IsTransactional = true }))
{ ... }
}
it hangs also... It is definitely the lock and it has to do with accessing the id from the newly created newAbpTenant: I can see that in SQL Developer Sessions
enq: TX - row lock contention
and guilty session is another my HttpApi host session. Probably, the reason is as Oracle doc says: "INSERT, UPDATE, and DELETE statements on the child table do not acquire any locks on the parent table, although INSERT and UPDATE statements wait for a row-lock on the index of the parent table to clear." - SaveChangesAsync causes new record row lock?
How to resolve this issue?
//OnModelCreatingBinding
builder.Entity<Tenant>()
.HasOne(x => x.AbpTenant)
.WithOne()
.HasPrincipalKey<Volo.Saas.Tenant>(x => x.Id)
.HasForeignKey<Tenant>(x => x.AbpId);
...
b.Property(x => x.AbpId).HasColumnName("C_ABP_TENANT").IsRequired();
//Mapping ignoration to avoid problems with 'bound' entities, since using separate repositories for Insert / Update
CreateMap<CreateTenantDto, Tenant>().ForMember(x => x.AbpTenant, opt => opt.Ignore());
CreateMap<UpdateTenantDto, Tenant>().ForMember(x => x.AbpTenant, opt => opt.Ignore());
public class CreateTenantDto
{
[Required]
public int Id { get; set; }
...
public Guid? AbpId { get; set; }
public SaasTenantCreateDto AbpTenant { get; set; }
}
public async Task<TenantDto> CreateAsync(CreateTenantDto input)
{
try
{
TenantDto newTenantDto = null;
using (var uow = _unitOfWorkManager.Begin(new AbpUnitOfWorkOptions { IsTransactional = true, IsolationLevel = System.Data.IsolationLevel.Serializable }))
{
var abpTenant = await _abpTenantManager.CreateAsync(input.AbpTenant.Name, input.AbpTenant.EditionId);
input.AbpTenant.MapExtraPropertiesTo(abpTenant);
var newAbpTenant = await _abpTenantRepository.InsertAsync(abpTenant);
await uow.SaveChangesAsync();
var tenant = ObjectMapper.Map<CreateTenantDto, Tenant>(input);
tenant.AbpId = newAbpTenant.Id;
var newTenant = await _tenantRepository.InsertAsync(tenant);
newTenantDto = ObjectMapper.Map<Tenant, TenantDto>(newTenant);
await uow.CompleteAsync();
}
return newTenantDto;
}
//Implementation by ABP Framework
public virtual async Task CompleteAsync(CancellationToken cancellationToken = default)
{
if (_isRolledback)
{
return;
}
PreventMultipleComplete();
try
{
_isCompleting = true;
await SaveChangesAsync(cancellationToken);
await CommitTransactionsAsync();
IsCompleted = true;
await OnCompletedAsync();
}
catch (Exception ex)
{
_exception = ex;
throw;
}
}
I have finally resolved the problem using the following approach (but it is not using TWO repositories which seems to be impossible to implement, since we need to manipulate DbContext directly):
Application service layer:
//requiresNew: true - to be able to use TransactionScope
//isTransactional: false, otherwise it won't be possible to use TransactionScope, since we would have active ambient transaction
using var uow = _unitOfWorkManager.Begin(requiresNew: true);
var abpTenant = await _abpTenantManager.CreateAsync(input.AbpTenant.Name, input.AbpTenant.EditionId);
input.AbpTenant.MapExtraPropertiesTo(abpTenant);
var tenant = ObjectMapper.Map<CreateTenantDto, Tenant>(input);
var newTenant = await _tenantRepository.InsertAsync(tenant, abpTenant);
await uow.CompleteAsync();
return ObjectMapper.Map<Tenant, TenantDto>(newTenant);
Handmade InsertAsync method on Repository (EntityFrameworkCore) layer:
using (new TransactionScope(asyncFlowOption: TransactionScopeAsyncFlowOption.Enabled))
{
var newAbpTenant = DbContext.AbpTenants.Add(abpTenant).Entity;
tenant.AbpId = newAbpTenant.Id;
var newTenant = DbContext.Tenants.Add(tenant).Entity;
if (autoSave)
{
await DbContext.SaveChangesAsync(GetCancellationToken(cancellationToken));
}
return newTenant;
}

Get user relation users in parse query ionic4

I have the following parse query:
loadAllFriends(params: any = {}): Promise<User[]> {
const page = params.page || 0;
const limit = params.limit || 20;
const query = new Parse.Query(User);
const user = Parse.User.current();
const relation = user.relation('likes');
query.equalTo('likes', user);
query.descending('updatedAt');
query.skip(page * limit);
query.limit(limit);
return query.find();
}
...and i load the data in page.ts like:
const users = await this.userService.loadAllFriends(this.params);
console.log(users);
for (let user of users) {
this.users.push(user);
}
... So this code it queries const user = Parse.User.current(); in likes relation column, checks if user is there and returns the results.
I need exact the opposite, with the anterior query i get the friends, but i need to get the followers. The followers are stored in user -> likes relation column:
How can i query user like:
const user = Parse.User.current();
query.equalTo('objectId', user.id);
... and here get the likes relation data from this user?
I don't think that you can do what you're trying to with a relation. I think that you'll need to model these relationships with a table:
{ fromUser, toUser, status }

Parse Cloud Right Query to retrieve Friendslist and not (like Instagram)

I have the class "Users" the default of Parse Dashboad. I have also the class "Friends", where I store the friendships between users like this:
There are two columns, "toUser" and "fromUser", which are showing who of the two users sent the friend request. These two columns are Pointers(Pointer<_User>) to the Users Class.
My concept is to retrieve two lists:
1. Friendlist for the logged in user
2. Userlist of users (who are not friends with the logged in user)
What would be the appropriate queries for that?
The first one logically should not scan all the class Users because it may slow down the perfomance of the app which is using Ionic 3 and Angular 4
Thank you
you don't need another class to do so all you need is a relation column in the default user class
to add new friend you just need the following code
var friendtoadd
//this is the friend object from parse user class you want to add you can get it by anyway you want
var currentUser = Parse.User.current();
var relation = currentUser.relation("friends");
relation.add(friendtoadd)
currentUser.save();
to retrieve array of friends of logged in user you can use the following code
var currentUser = Parse.User.current();
var friendRelation = currentUser.relation('friends');
var env = this
friendRelation.query().find({
success: function(users) {
for (var i = 0; i< users.length; i++) {
var object = users[i];
env.friends.push(object)
console.log(env.friends)
}
}
});
// you should define friends = []; in the class
if I understood your question right you want to find the friend requests you sent, or the ones you received. because I don't see where you made a relation between the user and his friends.
this is the code if you want to do this using cloud code:
First I validated the parameters of the friendRequest being saved :
Parse.Cloud.beforeSave("Friends", function(request, response){
var friendRequest = request.object;
if (!friendRequest.has("toUser") || !friendRequest.has("fromUser")) {
response.error("Invalid parameters");
return;
}
response.success();
});
then I created two cloud functions, one for retrieving the sentRequests:
Parse.Cloud.define("getSentRequests", function(request, response){
var query = new Parse.Query("Friends");
query.equalTo("fromUser", request.user);
if (!request.master) {
query.find({ sessionToken: request.user.getSessionToken() }).then(function(friends){
response.success(friends);
});
}else{
query.find({useMasterKey:true}).then(function(friends){
response.success(friends);
});
}
});
and you can call this either from a logged in user or using the masterKey if you want, and the other query is for the recievedRequests:
Parse.Cloud.define("getRecievedRequests", function(request, response){
var query = new Parse.Query("Friends");
query.equalTo("toUser", request.user);
if (!request.master) {
query.find({ sessionToken: request.user.getSessionToken() }).then(function(users){
response.success(users);
});
}else{
query.find({useMasterKey:true}).then(function(users){
response.success(users);
});
}
});

Find objects not in a relation

I have a custom object Team in Parse with a relation field for the default User object. What I would like to do is retrieve all User objects which are not related to any Team object. Can anyone point me in the right direction on how to do this using the JavaScript SDK? I've been going over the documentation for the Query object but I can't find anything.
Perhaps another type of relation, or placing the relation at another place is a better solution. What I want to accomplish is the following: Each user is allowed to be in one team and one team only. In addition I need to be able to query the following information from Parse:
I want to retrieve the User objects of all the users assigned to a team
I want to retrieve the User objects of all the users who are not assigned to any team
I have tried using a join table with both the user and team object ids. Then I tried to following query to get all users not assigned to a team:
var teammember = Parse.Object.extend('TeamMember'),
query = new Parse.Query("User");
var innerQuery = new Parse.Query("TeamMember");
query.doesNotMatchQuery('user', innerQuery);
query.find({
success: function(results) {
response.success(results);
},
error : function(error) {
response.error(error);
}
})
But this just gets me the following response: error: "{"code":102,"message":"bad type for $notInQuery"}".
I like the Relation type as I can add or remove multiple members at once with a single call to the REST API. I also have no problems retrieving the information on team members when using the Relation type to connect the users to the teams. It is just getting the users which are not assigned to any team that is giving me problems.
It doesn't sound like you need a relation at all. Instead, add a Pointer column to User that points to Team. It ensures that a User can only belong to one team, and your other requirements can be captured as follows.
// All users assigned to a team
query = new Parse.Query('User');
query.exists('team');
// All users assigned to a specific team
query = new Parse.Query('User');
query.equalTo('team', specificTeam);
// All unassigned users
query = new Parse.Query('User');
query.doesNotExist('team');
Update: If you need to support multiple teams per User in the future, then I would suggest creating a Parse table called Membership with two columns: a Pointer to User and a Pointer to Team. This essentially gives you more control than relying on Parse relations, but it gets a little more complicated.
_ = require('underscore'); // Or lodash
// All users assigned to a team
query = new Parse.Query('Membership');
query.find().then(function (results) {
// http://underscorejs.org/#uniq
users = _.uniq(results, false, function (user) { return user.id; });
});
// All users assigned to a specific team
query = new Parse.Query('Membership');
query.equalTo('team', specificTeam);
// All unassigned users
var assignedUsers = []
var unassignedUsers = []
memberQuery = new Parse.Query('Membership');
userQuery = new Parse.Query('User');
memberQuery.find().then(function (memberResults) {
// http://underscorejs.org/#map
var ids = _.map(memberResults, function (user) { return user.id; });
// http://underscore.js.org/#uniq
assignedUsers = _.uniq(ids);
userQuery.find();
}).then(function (userResults) {
var users = _.map(userResults, function (user) { return user.id; });
// http://underscorejs.org/#difference
unassignedUsers = _.difference(users, assignedUsers);
});
To add and remove Users to/from Teams, you would create Membership objects and save API calls with Parse.Object.saveAll() and Parse.Object.destroyAll().
I ran into trouble with the answer provided by Seth. When retrieving the users not assigned to a team the difference between the two arrays would be incorrect. I am assuming this is due to the assignedUsers having object of type Membership and userResults being of type User. This would make it impossible for underscore to make a proper match.
I would up using this as my Cloud Code:
Parse.Cloud.define("getTeamlessUsers", function(request, response) {
var _ = require("underscore"),
assignedUsers = [],
companyUsers = [],
memberQuery = new Parse.Query("TeamMembers"),
userQuery = new Parse.Query("User"),
index,
ubound;
memberQuery.find().then(function(memberResults) {
// Make sure each User ID will appear just once
memberResults = _.unique(memberResults, false, function(item) { return item.get('user').id; });
// Loop over the unique team members and push the User ID into the array
for (index = 0, ubound = memberResults.length; index < ubound; index++) {
var user = memberResults[index].get("user");
assignedUsers.push(user.id);
}
// Get al the users
return userQuery.find();
}).then(function(userResults) {
// Loop over all the users and push the ID into the array
for (index = 0, ubound = userResults.length; index < ubound; index++) {
companyUsers.push(userResults[index].id);
}
// Create an array of user IDs which are not present in the assignedUsers array
var result = _.difference(companyUsers, assignedUsers);
// Return the IDs of user not assigned to any team
response.success(result);
}).fail(function(error) {
response.error(error);
});
});

Resources