Update user with Admin SDK - google-apps-script

I am trying to update some user data via the admin SDK. I thought this would work
function directoryUpdate(userId, userDept, userLocation, userPhone, userTitle) {
var update = {
organizations:
{
name: "Next Step Living",
title: userTitle,
primary: true,
type: "work",
department: userDept,
location: userLocation
},
phones:
{
value: userPhone,
type: "work",
primary: true,
}
};
update = AdminDirectory.Users.update(update, userId);
Logger.log('User %s updated with result %s.', userId, update)
return true;
}
but it is not updating the organization or phone data on the record. It also does not throw any kind of error.
three questions, what is the proper syntax to do this update, I assume this works like the API update and behaves like an upsert, is that true, and what is the best practice for capturing any errors during the update. I would like to return a false when the update fails and capture that info. Thanks in advance for your help.

Thanks for your question!
This "inspired" me to work out how the update API worked, as I had got as far as retrieving a User object, updating the properties but had not worked out how to persist the data back to Google.
So, here's my prototype code, which appears to work (the objective being to reset the user's password based on entries in a spreadsheet).
It doesn't seem the most elegant code to me, being that there are two round-trips to the Admin API, and we have to post the email address twice, but I guess that is a side-effect of the JSON API.
var emailAddress = userListSheet.getRange(row, 1).getValue();
var password = userListSheet.getRange(row, 2).getValue();
Logger.log('Email: %s, Password: %s', emailAddress, password);
// Reset user's password
var user = AdminDirectory.Users.get(emailAddress);
user.password = password;
if (changePasswordAtNextLogin == 'Yes') {
user.changePasswordAtNextLogin = true;
}
AdminDirectory.Users.update(user, emailAddress);

Figured out the syntax issue. You do need a set of [] around the name value pairs under organization and phones. organizations:[{....}], phones:[{...}]}; and no, at the end of primary: true under phones. Also changed it from an update to a patch but not sure if that was really required;
update = AdminDirectory.Users.patch(update, userId);
And Yes, it did behave like an upsert and modified existing data and added new data just like the API.
Still need to figure out the best way to capture any errors though so if you have any suggestions please post them.
Looks like supplying an invalid email address is a fatal error that can not be caught and dealt with in code. What I did was get all the primary emails out of Google, store them in an array, and validate that the email I was using was in that list prior to running the update. Since everything else is just a string or logical replacement it should not throw any errors so I am confident that the script will not fail. Now all I have to worry about is the time limit.

Related

How to get department and manager's email through google admin sdk?

I am calling google admin directory api to get user through email and then storing the organizations return in org.
var user = AdminDirectory.Users.get(bmail);
org = user.organizations;
Output
[ { customType: '',
title: 'PM',
department: 'BIT',
primary: true,
description: 'Permanent' } ]
How can i get only department in org? I've tried to use get.child(element) to get the department however not successful.
How can i get only manager's email? I've read the Rest Resource Users but there is no information related to manager's email.
Any reference or help will be much appreciated.
Thank you
You already have the User object, so if you want to read the department property you only have to navigate its tree. See the example below:
function myFunction() {
var user = AdminDirectory.Users.get(bmail);
org = user['organizations'][0]['department'];
}
Now to answer your second question you could use the relations field to keep track of the user's managers. Feel free to drop a comment if you need further clarifications.

How can I get UserPool details by IdentityPool/UserPool id (sub)

Is there a way to get user details (profile attributes etc) if I have IdentityPool or UserPool ID (sub) of a user with AWS SDK?
The use case is that I'm saving some information submitted by a user in a DB with a key equal to user ID (sub). So, when I'm reading it from the DB, I want to restore back some user info from my pool for my app UI.
I found a similar question (Getting cognito user pool username from cognito identity pool identityId), but it seems, the answer given is focused around serverless deployment, and still has some gaps.
Thanks in advance
Since you have the user's sub, you can use AdminGetUser. It returns the UserAttributes in the pool.
I think I found a solution, it was on the surface actually.
Having user pool id one can use ListUsers call with filter sub = \"${userId}\". The client to be used is CognitoIdentityProviderClient, if JS is used.
const client = new CognitoIdentityProviderClient({
region: REGION,
credentials: fromCognitoIdentityPool({
client: new CognitoIdentityClient({ region: REGION }),
logins: {
[PROVIDER_ID]: token
},
identityPoolId: ID_POOL_ID
})
});
const filter = `sub = \"${userPoolId}\"`;
const resp = await client.send(new ListUsersCommand({
UserPoolId: USER_POOL_ID,
Filter: filter,
Limit: 1
}));
Of course AdminGetUser can be used as well, as Ulas Keles mentioned above, if it's applicable

MySQL deadlock on account registration

I'm trying to implement a feature that detects whether or not an account has already signed up. Now when doing parallel requests I'm getting a deadlock. I think I understand why it is happening but I'm unsure on how to solve it.
Here's a simplified version of what I'm doing;
START TRANSACTION;
-- check if user has already signed up (returned rows > 0, throw error if so)
SELECT * FROM users WHERE email = 'example#site.com' FOR UPDATE;
-- user has not signed up yet.. create the account.
INSERT INTO users SET ...;
COMMIT;
Now this in itself works fine. However when two parallel request happen, a deadlock is made because the transaction will both create a FOR UPDATE lock, which is allowed because initially when there is no account signed up yet there are no rows to lock. Atleast, that's what I think is happening.. correct me if I'm wrong.
I'm curious on how I were to fix this, I still want to check whether not an account has registered already so I can show the user a message. Of course the email has a unique constraint but I do not want to rely on that because the auto increment index will increment, even when it violates the constraint.
Also I'm using typeorm, a sample of my code;
public async registerUser(email: string, password: string, displayName?: string) {
const connection = await getConnection();
connection.transaction(async (manager) => {
// First we need to make sure that this email isn't already registered. If
// it has been registered we can throw a simple UserError which will be
// caught by our error handler.
const hasAlreadyRegistered = await this.findUser(email, manager);
if (hasAlreadyRegistered) throw new UserError('Email has already been registered.');
// At last we can create the user, linking him to the previously created
// authentication strategy.
const user = new User();
user.email = email;
user.displayName = displayName || randomBytes(8).toString('hex');
user.strategies = [authentication];
await manager.save(user);
logger.silly('> Created user row.');
return user;
});
}
I have solved this by just checking for the constraint error in the end (per suggestion of #Shadow). It saves me a lot of hassle.
Code
try {
await manager.save(user);
} catch (err: any) {
// Check whether or not this entry violates an unique constraint.
if (err.code === 'ER_DUP_ENTRY') {
throw new UserError('Email has already been registered.');
} else throw err;
}

How to change the field delete instead of id in loopback?

I want to the delete a few rows in MySQL table using loopabck. But I don't want to use the id to delete the record. I'm trying it in Angular SDK. My code is:
ModelName.destroyById({ fieldName : myValue })
.$promise
.then(function() {
});
I'd be very thankful for ideas.
Seems like you need to use the destroyAll method, which does what you want, but you need to set up remoting for it first, since it's disabled by default:
ModelName.remoteMethod('destroyAll', {
isStatic: true,
description: 'Delete all matching records',
accessType: 'WRITE',
accepts: {arg: 'where', type: 'object', description: 'filter.where object'},
http: {verb: 'del', path: '/'}
});
Then after that you regenerate your Angular client and you can use it like this in the client side:
ModelName.destroyAll({ filter: { fieldName : myValue }}).$promise.then(...)
Note that using destroyAll is risky as if you have any mistakes in your code you will end up losing data. Enabling it to client side might be security concern.
Another way to do this is to write your own remote method which uses destroyAll in turn with proper validation of the inputs.

Database update issue using node-orm2 for mysql backend

Problem:
I am working on an Android app which interacts with nodejs REST server using node orm for mysql backend. On my server, I have a functionality of authenticating users based on email verification. Once verification is successful, node orm fetches the user object, changes the verified column value and saves it back.
But, the change is not reflecting in the db after execution. Only if we run the same code another time, it is reflecting in the database
Code
exports.activateEmail = function(email, callback) {
log.info('In verifyEmailDao.js, activateEmail module');
var db = connectionObj.getConnection();
var Candidate = db.models.jobseeker_id;
Candidate.find({email : email}, function(err,candidate){
if(err){
log.info('cannot find account with email to activate', email);
callback(false, null);
}
else {
candidate[0].verified = true;
log.info('candidate email now activated.! status is', candidate[0].verified);
candidate[0].save(function(error){
log.info('Email verified any errors?', error);
callback(true, candidate[0].id);
});
}
});
}
Edit 1:
jobseeker_id.js (node-orm model)
var orm = require('orm');
module.exports = function(db){
console.log('coming inside candidateId.js');
var JobSeekerId = db.define('jobseeker_id', {
id : {type:'serial' , key:true},
first_name : String,
last_name : String,
email : String,
password : String,
verified : Boolean
},{
validations : {
email : orm.enforce.unique("Already registered")
}
});
}
Server log:
{"name":"test-app" "msg":"In verifyEmailDao.js, activateEmail module"}
{"name":"test-app","msg":"candidate email now activated.! status is true"}
{"name":"test-app","msg":"Email verified any errors? null"}
{"name":"test-app","msg":"Email sucessfully activated. Now deleting the entry from verify email link table for candidate id 30}
{"name":"test-app","msg":"In verifyEmailDao.js, deleteRandomLink module"}
{"name":"test-app","msg":"error is---> null"}
{"name":"test-app","msg":"Entry deleted from verify email table as email is activated"}
There will no be no changes in the log when I execute the code for second time, but the change in the db will be reflected!
After 2 days of hell I finally fixed the issue by adding a statement db.settings.set('instance.cache', false) to the db config file. Though I did'nt clearly understand how db update issue was resolved by setting the cache to false, this did the trick!