[Bug]: total value inaccurate when adding and removing users
What happened?
This is a weird one. It occurs when standing up an instance locally using Docker but not when using the sandbox.
Replication steps (assuming you have node installed 22.15.1):
- create a new directory
- run npm install @fusionauth/typescript-client
- set up these files:
import FusionAuthClient from '@fusionauth/typescript-client';
// Configuration constants
const FUSIONAUTH_URL = 'http://localhost:9011';
const API_KEY = 'fwZMPKz9E8SXhujVidgimpPT_p9Vz40ZSo94f26SnOrxwaJC1EwzNDlp';
const APPLICATION_ID = '3c219e58-ed0e-4b18-ad48-f4f92793ae32';
const ADMIN_ROLE_ID = 'admin'; // This should match your role ID in FusionAuth
// Initialize FusionAuth client
const client = new FusionAuthClient(API_KEY, FUSIONAUTH_URL);
async function countUsersWithAdminRole(): Promise<number> {
console.log('Searching for users with admin role...');
// Query for users with the admin role
const searchResponse = await client.searchUsersByQuery({
search: {
queryString: `registrations.applicationId:${APPLICATION_ID} AND registrations.roles:${ADMIN_ROLE_ID}`,
}
});
if (!searchResponse.wasSuccessful()) {
throw new Error(`Failed to search users: ${JSON.stringify(searchResponse.exception)}`);
}
const count = searchResponse.response.total || 0
console.log(`Found ${count} users with admin role`);
return count;
}
async function main() {
try {
// Step 1: Find initial count of users with admin role
console.log('\n=== Step 1: Initial count ===');
const initialCount = await countUsersWithAdminRole();
// Step 2: Create a new user with admin role
console.log('\n=== Step 2: Creating new user ===');
const newUserId = crypto.randomUUID();
const newUser = {
user: {
email: `test-admin-${Date.now()}@example.com`,
username: `testadmin${Date.now()}`,
password: 'TempPassword123!',
firstName: 'Test',
lastName: 'Admin'
},
registration: {
applicationId: APPLICATION_ID,
roles: [ADMIN_ROLE_ID]
}
};
const createResponse = await client.register(newUserId, newUser);
if (!createResponse.wasSuccessful()) {
throw new Error(`Failed to create user: ${JSON.stringify(createResponse.exception)}`);
}
const createdUserId = createResponse.response.user?.id;
console.log(`User created successfully with ID: ${createdUserId}`);
// Step 3: Verify the count increased
//console.log('\n=== Step 3: Count after creation ===');
//const countAfterCreation = await countUsersWithAdminRole();
//console.log(`Count increased by: ${countAfterCreation - initialCount}`);
// Step 4: Delete the user
console.log('\n=== Step 4: Deleting user ===');
const deleteResponse = await client.deleteUser(createdUserId!);
if (!deleteResponse.wasSuccessful()) {
throw new Error(`Failed to delete user: ${JSON.stringify(deleteResponse.exception)}`);
}
console.log(`User ${createdUserId} deleted successfully`);
// Step 5: Verify the count returned to original
console.log('\n=== Step 5: Final count ===');
const finalCount = await countUsersWithAdminRole();
console.log(`Final count matches initial count: ${finalCount === initialCount}`);
console.log('\n=== Summary ===');
console.log(`Initial count: ${initialCount}`);
//console.log(`After creation: ${countAfterCreation}`);
console.log(`Final count: ${finalCount}`);
} catch (error) {
console.error('Error occurred:', error);
process.exit(1);
}
}
// Run the script
main();
in script.ts
and
{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"lib": ["ES2020"],
"moduleResolution": "node",
"esModuleInterop": true,
"skipLibCheck": true,
"strict": true,
"resolveJsonModule": true,
"outDir": "./dist"
},
"include": ["*.ts"],
"exclude": ["node_modules"]
}
in tsconfig.json
- install the default Docker install for FusionAuth as documented here: https://fusionauth.io/docs/get-started/download-and-install/docker
- complete the setup wizard and create a user
- create an API key with the value
fwZMPKz9E8SXhujVidgimpPT_p9Vz40ZSo94f26SnOrxwaJC1EwzNDlp - run the script:
npx ts-node script; you see the value 0 - run it again, you see the value 1
- run it again, you see the value 2
If you reindex or use searchResponse.response.users?.length instead of searchResponse.response.total or wait for a while, the value appears to be correct.
So I don't know if our default docker configuration of opensearch needs to change or something.
But it was weird and confusing for me.
Version
1.61.2
Affects Versions
No response
Alternatives / Workarounds
If you reindex or use searchResponse.response.users?.length instead of searchResponse.response.total or wait for a while, the value appears to be correct.
using the accurateTotal doesn't help.
I wonder if the issue is related to https://github.com/FusionAuth/fusionauth-issues/issues/3270 because I'm seeing that in the docker error output when I run the script.