Hi! In order to make some tests, I put a message at the very first line of the
stdAccountCreate and stdAccountUpdate commands. It is simply a
throw new ConnectorError('Create Account input: ’ + JSON.stringify(input));
and
throw new ConnectorError('Update Account input: ’ + JSON.stringify(input));
Then, I requeted 25 entitlements for an identity that does not have account in the system yet (the maximum allowed by request center).
What I am watching is that at every call (obviously failed), it is a createAccount operation, each with 1 entitlement inside. Why it simply does a create account with all entitlements? Or an empty account create followed by 25 account updates?
What happens if there is a successful account creation?
Part of what’s returned back after the account create is the account object itself, so if there is no valid account returned, it’s going to send another account create request
As I can see when an account is created, next requests calls the updateUser command. But my doubt is in the first request for some user (for example, an identity that already has no account on target system). If I request 25 entitlements, IDN just send an createUser command, with an attribute entitlements with 25 string elements? Or just sends a creatUser command, followed by 25 updateUser commands?
What should be the expected behaviour?
I am trying to solve a problem, because when I request 4/5 entitlements all works fine, but when the first request has a lot of entitlements, most of them returns a ConnectorError with a timeout that does not come from the target system, but from the connector itself (I tried to send res.keepAlive() calls on every point I could, but this behaviour remains).
I would have to test this on one of my connectors to confirm, but it appears the behavior is
Create account request comes first
Update account request comes next, with one or many requested updates
I say that because the way I have my account update function structured is to expect multiple requests at the same time (input.changes)
.stdAccountUpdate(async (context: Context, input: StdAccountUpdateInput, res: Response<StdAccountUpdateOutput>) => {
logger.info(`${input}`)
for (let change of input.changes) {
const values = [].concat(change.value)
for (let value of values) {
const account = await readAccount(input.identity)
switch (change.op) {
case AttributeChangeOp.Add:
logger.info(`Sending provisioning request for ${account.attributes.full_name} to group id ${value}`)
await assignUserGroup(account, value)
break
case AttributeChangeOp.Remove:
logger.info(`Sending deprovisioning request for ${account.attributes.full_name} from group id ${value}`)
await removeUserGroup(account, value)
break
default:
throw new ConnectorError(`Operation not supported: ${change.op}`)
}
}
}
const account = await readAccount(input.identity)
logger.info(account)
res.send(account)
})
Hi @mcheek thanks for the research. I do not know why, but I was inspecting behaviour with the sail conn logs command, and it showed that when I request many entitlements for some identity (it happens when I request more than 4 or 5, this is random too), I receive a std:account:create comand for each entitlement. First one is created as expected, but the rest fails because of course, account already exists.
This happens when going to Request Center, select an identity, then mark several entitlements and click on submit.
On each row I receive a ConnectorError exception / timeout.
Strange is that if I request only 2 or 3 entitlements at once, it works as expected. Now I have a workaround (check in the create command if account exists, and if it does calls the add entitlment operation), but will continue researching why this is happening. I think is not code problem, because it happens before connector’s code is reached.
I would make sure that you are returning a valid account object back to IdN from your stdAccountCreate function.
I had originally added a check to mine as well
.stdAccountCreate(
async (context: Context, input: StdAccountCreateInput, res: Response<StdAccountCreateOutput>) => {
logger.info(`${input}`)
let account
//Checks to see if account already exists, in case it was created outside of IdN in between aggregations
const account_query = await httpClient.queryAccount(input.attributes.email_address)
if (account_query.data.header.dataRows > 0) {
account = await readAccount(account_query.data.user_id)
logger.info(`A new account for ${input.attributes.full_name} will not be created because
an existing account was found with the same email address - account id is ${account.attributes.user_id}`)
}
else {
const user = { ...input.attributes, ...{ groups: undefined } }
const account_response = await httpClient.createAccount(user)
account = await readAccount(account_response.data.user_id)
logger.info(`New Account Created for ${input.attributes.full_name} - account id is ${account.identity}`)
}
res.send(account)
}
)
The reason I put it there is that it’s possible for an account to be created directly in the system by another admin, so I wanted it to check first in case.
I found the issue was because I was returning the account Id back to IdN as an integer and not a string. Maybe that’s your issue?