Skip to content

Commit

Permalink
Merge pull request #33 from vishnuvinay89/all-saas-0.2-dev
Browse files Browse the repository at this point in the history
TaskId #236323 task: Bulk Upload User API
  • Loading branch information
gouravmore authored Mar 3, 2025
2 parents e4608c8 + 631d127 commit 20c5f01
Show file tree
Hide file tree
Showing 3 changed files with 157 additions and 0 deletions.
132 changes: 132 additions & 0 deletions src/adapters/postgres/user-adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import { JwtUtil } from '@utils/jwt-token';
import { ConfigService } from '@nestjs/config';
import { formatTime } from '@utils/formatTimeConversion';
import { API_RESPONSES } from '@utils/response.messages';
import { parse } from 'csv-parse/sync';


@Injectable()
Expand Down Expand Up @@ -952,6 +953,137 @@ export class PostgresUserService implements IServicelocator {
return APIResponse.error(response, apiId, "Internal Server Error", errorMessage, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
async bulkCreateUser(request: any, userCreateDto: UserCreateDto, response: Response) {
// check and validate all fields
let validatedRoles = await this.validateRequestBody(userCreateDto)

if (
validatedRoles &&
Array.isArray(validatedRoles) &&
validatedRoles.length > 0
) {
const errorMessage = validatedRoles.join("; ");
throw new Error(errorMessage);
} else if (validatedRoles) {
throw new Error("Validation Error");
}

userCreateDto.username = userCreateDto.username.toLocaleLowerCase();
const userSchema = new UserCreateDto(userCreateDto);

let errKeycloak = "";
let resKeycloak = "";

const keycloakResponse = await getKeycloakAdminToken();
const token = keycloakResponse.data.access_token;
let checkUserinKeyCloakandDb = await this.checkUserinKeyCloakandDb(userCreateDto)
// let checkUserinDb = await this.checkUserinKeyCloakandDb(userCreateDto.username);
if (checkUserinKeyCloakandDb) {
throw new Error('User Already Exist')
}
resKeycloak = await createUserInKeyCloak(userSchema, token).catch(
(error) => {
errKeycloak = error.response?.data.errorMessage;
throw new Error(errKeycloak);
}
);

userCreateDto.userId = resKeycloak;

let result = await this.createUserInDatabase(request, userCreateDto, response);
}

async bulkUploadUsers(request: any,csvFile :any, response: Response) {
const apiId = APIID.USER_CREATE_BULK;
const decoded: any = jwt_decode(request.headers.authorization);

try {
if (csvFile == undefined) {
return APIResponse.error(
response,
apiId,
"BAD_REQUEST",
"CSV file is required",
HttpStatus.BAD_REQUEST
);
}
const csvData = csvFile.buffer.toString('utf8');

// Parse CSV data
const records = parse(csvData, {
columns: true,
skip_empty_lines: true,
trim: true
});

const results = {
success: 0,
failed: 0,
failedDetails: []
};

// Process each user record
for (const record of records) {
// Validate required fields
if (!record.username || !record.name || !record.password) {
results.failed++;
results.failedDetails.push({
record: record.username || 'Unknown',
error: 'Missing required fields (username, name, or password)'
});
continue;
}

// Create UserCreateDto from CSV record
let userCreateDto =
{
username :record.username.toLocaleLowerCase(),
name : record.name,
password : record.password,
createdBy : decoded.sub,
updatedBy : decoded.sub,
tenantCohortRoleMapping : [
{
roleId: request.body.roleId,
tenantId: request.body.tenantId,
cohortId: [request.body.cohortId]
}
]
}
const userSchema = new UserCreateDto(userCreateDto);
try {
await this.bulkCreateUser(request, userSchema, response);
results.success++;
}
catch (error) {
results.failed++;
results.failedDetails.push({
record: record.username || 'Unknown',
error : error.message
});
}
}

// Return overall results
return APIResponse.success(
response,
apiId,
results,
HttpStatus.OK,
`Bulk upload completed. Success: ${results.success}, Failed: ${results.failed}`
);

} catch (e) {
const errorMessage = e.message || 'Internal server error';
return APIResponse.error(
response,
apiId,
"Internal Server Error",
errorMessage,
HttpStatus.INTERNAL_SERVER_ERROR
);
}
}


async validateRequestBody(userCreateDto) {
Expand Down
1 change: 1 addition & 0 deletions src/common/utils/api-id.config.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export const APIID = {
USER_GET: "api.user.get",
USER_CREATE: "api.user.create",
USER_CREATE_BULK : "api.user.bulkCreate",
USER_UPDATE: "api.user.update",
USER_LIST: "api.user.list",
USER_RESET_PASSWORD: "api.user.resetPassword",
Expand Down
24 changes: 24 additions & 0 deletions src/user/user.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import {
Delete,
ParseUUIDPipe,
UseFilters,
UploadedFile,
UseInterceptors
} from "@nestjs/common";

import {
Expand All @@ -31,6 +33,7 @@ import {
ApiInternalServerErrorResponse,
ApiBadRequestResponse,
ApiConflictResponse,
ApiConsumes,
} from "@nestjs/swagger";

import { UserSearchDto } from "./dto/user-search.dto";
Expand All @@ -43,6 +46,7 @@ import { AllExceptionsFilter } from "src/common/filters/exception.filter";
import { APIID } from "src/common/utils/api-id.config";
import { ForgotPasswordDto, ResetUserPasswordDto, SendPasswordResetLinkDto,learnerForgotPasswordDto } from "./dto/passwordReset.dto";
import { PostgresUserService } from "src/adapters/postgres/user-adapter";
import { FileInterceptor } from '@nestjs/platform-express';

export interface UserData {
context: string;
Expand Down Expand Up @@ -115,6 +119,26 @@ export class UserController {

}

// bulk create of users
@UseInterceptors(FileInterceptor('csvFile'))
@UseFilters(new AllExceptionsFilter(APIID.USER_CREATE_BULK))
@Post("/bulk-create")
@UseGuards(JwtAuthGuard)
@ApiBasicAuth("access-token")
@ApiConsumes('multipart/form-data')
@ApiCreatedResponse({ description: "Users have been created successfully." })
@ApiForbiddenResponse({ description: "Forbidden" })
@ApiHeader({
name: "tenantid",
})
public async bulkCreateUsers(
@Req() request: Request,
@UploadedFile() csvFile: Express.Multer.File,
@Res() response: Response
) {
return await this.postgresUserService.bulkUploadUsers(request, csvFile, response)
}

@UseFilters(new AllExceptionsFilter(APIID.USER_UPDATE))
@Patch("update/:userid")
@UsePipes(new ValidationPipe())
Expand Down

0 comments on commit 20c5f01

Please sign in to comment.