Skip to content

Commit

Permalink
Merge pull request #131 from Sourav-Tekdi/field_creation
Browse files Browse the repository at this point in the history
PS-3323: Adding New Fields from the backend
  • Loading branch information
Shubham4026 authored Jan 16, 2025
2 parents 92f168d + 3177f36 commit 312ec7e
Show file tree
Hide file tree
Showing 7 changed files with 175 additions and 76 deletions.
92 changes: 56 additions & 36 deletions src/adapters/postgres/user-adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,6 @@ export class PostgresUserService implements IServicelocator {
private tenantsRepository: Repository<Tenants>,
@InjectRepository(UserRoleMapping)
private userRoleMappingRepository: Repository<UserRoleMapping>,
@InjectRepository(Cohort)
private cohortRepository: Repository<Cohort>,
@InjectRepository(Role)
private roleRepository: Repository<Role>,
private fieldsService: PostgresFieldsService,
Expand Down Expand Up @@ -404,7 +402,7 @@ export class PostgresUserService implements IServicelocator {
whereCondition += ` AND `;
}
if (userKeys.includes(key)) {
if (key === "name") {
if (key === "firstName") {
whereCondition += ` U."${key}" ILIKE '%${value}%'`;
} else {
if (key === "status" || key === "email") {
Expand Down Expand Up @@ -498,7 +496,7 @@ export class PostgresUserService implements IServicelocator {
}

//Get user core fields data
const query = `SELECT U."userId", U."username",U."email", U."name", R."name" AS role, U."mobile", U."createdBy",U."updatedBy", U."createdAt", U."updatedAt", U.status, COUNT(*) OVER() AS total_count
const query = `SELECT U."userId", U."username",U."email", U."firstName", U."middleName", U."lastName", U."gender", U."dob", R."name" AS role, U."mobile", U."createdBy",U."updatedBy", U."createdAt", U."updatedAt", U.status, COUNT(*) OVER() AS total_count
FROM public."Users" U
LEFT JOIN public."CohortMembers" CM
ON CM."userId" = U."userId"
Expand Down Expand Up @@ -688,7 +686,9 @@ export class PostgresUserService implements IServicelocator {
select: [
"userId",
"username",
"name",
"firstName",
"middleName",
"lastName",
"mobile",
"email",
"temporaryPassword",
Expand Down Expand Up @@ -900,17 +900,31 @@ export class PostgresUserService implements IServicelocator {
return deviceIds;
}

async updateBasicUserDetails(userId, userData: Partial<User>): Promise<User> {
const user = await this.usersRepository.findOne({
where: { userId: userId },
});
if (!user) {
return null;
async updateBasicUserDetails(userId: string, userData: Partial<User>): Promise<User | null> {
try {
// Fetch the user by ID
const user = await this.usersRepository.findOne({ where: { userId } });

if (!user) {
// If the user is not found, return null
return null;
}

// Update the user's details
await this.usersRepository.update(userId, userData);

// Fetch and return the updated user
const updatedUser = await this.usersRepository.findOne({ where: { userId } });

return updatedUser;
} catch (error) {
// Re-throw or handle the error as needed
throw new Error('An error occurred while updating user details');
}
Object.assign(user, userData);
return this.usersRepository.save(user);
}



async createUser(
request: any,
userCreateDto: UserCreateDto,
Expand Down Expand Up @@ -970,7 +984,7 @@ export class PostgresUserService implements IServicelocator {
const userSchema = new UserCreateDto(userCreateDto);

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

const keycloakResponse = await getKeycloakAdminToken();
const token = keycloakResponse.data.access_token;
Expand All @@ -988,28 +1002,36 @@ export class PostgresUserService implements IServicelocator {
);
}

resKeycloak = await createUserInKeyCloak(userSchema, token).catch(
(error) => {
LoggerUtil.error(
`${API_RESPONSES.SERVER_ERROR}: ${request.url}`,
`KeyCloak Error: ${error.message}`,
apiId
);
resKeycloak = await createUserInKeyCloak(userSchema, token)

errKeycloak = error.response?.data.errorMessage;

if(resKeycloak.statusCode !== 201 ){
if (resKeycloak.statusCode === 409) {
LoggerUtil.log(API_RESPONSES.EMAIL_EXIST, apiId);

return APIResponse.error(
response,
apiId,
API_RESPONSES.EMAIL_EXIST,
`${resKeycloak.message} ${resKeycloak.email}`,
HttpStatus.CONFLICT
);
}else{
LoggerUtil.log(API_RESPONSES.SERVER_ERROR, apiId);
return APIResponse.error(
response,
apiId,
API_RESPONSES.SERVER_ERROR,
`${errKeycloak}`,
`${resKeycloak.message}`,
HttpStatus.INTERNAL_SERVER_ERROR
);
}
);
}

LoggerUtil.log(API_RESPONSES.USER_CREATE_KEYCLOAK, apiId);

userCreateDto.userId = resKeycloak;

userCreateDto.userId = resKeycloak.userId;

// if cohort given then check for academic year

Expand Down Expand Up @@ -1294,17 +1316,15 @@ export class PostgresUserService implements IServicelocator {
response: Response
) {
const user = new User();
(user.username = userCreateDto?.username),
(user.name = userCreateDto?.name),
(user.email = userCreateDto?.email),
(user.mobile = Number(userCreateDto?.mobile) || null),
(user.createdBy = userCreateDto?.createdBy || userCreateDto?.userId),
(user.updatedBy = userCreateDto?.updatedBy || userCreateDto?.userId),
(user.userId = userCreateDto?.userId),
(user.state = userCreateDto?.state),
(user.district = userCreateDto?.district),
(user.address = userCreateDto?.address),
(user.pincode = userCreateDto?.pincode);
user.userId = userCreateDto?.userId,
user.username = userCreateDto?.username,
user.firstName = userCreateDto?.firstName,
user.middleName = userCreateDto?.middleName,
user.lastName = userCreateDto?.lastName,
user.gender = userCreateDto?.gender,
user.email = userCreateDto?.email,
user.mobile = Number(userCreateDto?.mobile) || null,
user.createdBy = userCreateDto?.createdBy || userCreateDto?.createdBy;

if (userCreateDto?.dob) {
user.dob = new Date(userCreateDto.dob);
Expand Down
80 changes: 48 additions & 32 deletions src/common/utils/keycloak.adapter.util.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { API_RESPONSES } from "./response.messages";
import { LoggerUtil } from "src/common/logger/LoggerUtil";
const axios = require("axios");

function getUserRole(userRoles: string[]) {
if (userRoles.includes("systemAdmin")) {
return "systemAdmin";
Expand Down Expand Up @@ -53,65 +55,79 @@ async function getKeycloakAdminToken() {
return res;
}


async function createUserInKeyCloak(query, token) {
const axios = require("axios");
const name = query.name;
const nameParts = name.split(" ");
let lname = "";

if (nameParts[2]) {
lname = nameParts[2];
} else if (nameParts[1]) {
lname = nameParts[1];
}
if (!query.password) {
return "User cannot be created, Password missing";
}

const data = JSON.stringify({
firstName: nameParts[0],
lastName: lname,
enabled: "true",
firstName: query.firstName,
lastName: query.lastName,
email: query.email || null, // Use `||` for simpler null/undefined handling
username: query.username,
// groups: [getUserGroup(query.role)],
enabled: true, // Changed "true" (string) to true (boolean)
credentials: [
{
temporary: "false",
temporary: false, // Changed "false" (string) to false (boolean)
type: "password",
value: query.password,
},
],
});

console.log("Payload for Keycloak:", data);

const config = {
method: "post",
url: process.env.KEYCLOAK + process.env.KEYCLOAK_ADMIN,
url: `${process.env.KEYCLOAK}${process.env.KEYCLOAK_ADMIN}`,
headers: {
"Content-Type": "application/json",
Authorization: "Bearer " + token,
Authorization: `Bearer ${token}`,
},
data: data,
data,
};
let userResponse;
// try {
// userResponse = await axios(config);
// } catch (e) {
// return e;
// }

// const userString = userResponse.headers.location;
// const index = userString.lastIndexOf("/");
// const userId = userString.substring(index + 1);

// return userId;
try {
const userResponse = await axios(config);
return userResponse.headers.location.split("/").pop();
// Make the request and wait for the response
const response = await axios(config);

// Log and return the created user's ID
console.log("User created successfully:", response.data);
const userId = response.headers.location.split("/").pop(); // Extract user ID from the location header
console.log("Created User ID:", userId);
return { statusCode: response.status, message: "User created successfully", userId : userId };
} catch (error) {
return "Error creating user: " + error.response.data.error;
// Handle errors and log relevant details
if (error.response) {
console.error("Error Response Status:", error.response.status);
console.error("Error Response Data:", error.response.data);
console.error("Error Response Headers:", error.response.headers);

return {
statusCode: error.response.status,
message: error.response.data.errorMessage || "Error occurred during user creation",
email: query.email || "No email provided",
};
} else if (error.request) {
console.error("No response received:", error.request);
return {
statusCode: 500,
message: "No response received from Keycloak",
email: query.email || "No email provided",
};
} else {
console.error("Error setting up request:", error.message);
return {
statusCode: 500,
message: `Error setting up request: ${error.message}`,
email: query.email || "No email provided",
};
}
}
}


async function checkIfEmailExistsInKeycloak(email, token) {
const axios = require("axios");
const config = {
Expand Down
1 change: 1 addition & 0 deletions src/common/utils/response.messages.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export const API_RESPONSES = {
USERNAME_NOT_FOUND: "Username does not exist",
EMAIL_EXIST: "Email already exists",
USER_NOT_FOUND: "User does not exist",
FORGOT_PASSWORD_SUCCESS: "Forgot password Reset successfully",
EMAIL_NOT_FOUND_FOR_RESET:
Expand Down
31 changes: 29 additions & 2 deletions src/user/dto/user-create.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import {
IsUUID,
ValidateNested,
IsOptional,
Length,
IsEnum,
} from "class-validator";
import { User } from "../entities/user-entity";
import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger";
Expand Down Expand Up @@ -70,9 +72,34 @@ export class UserCreateDto {
@IsNotEmpty()
username: string;

@ApiProperty({ type: () => String })
@ApiProperty({ type: String, description: 'First name of the user', maxLength: 50 })
@Expose()
@IsString()
@Length(1, 50)
firstName: string;

@ApiPropertyOptional({ type: String, description: 'Middle name of the user (optional)', maxLength: 50, required: false })
@Expose()
@IsOptional()
@IsString()
@Length(0, 50)
middleName?: string;

@ApiProperty({ type: String, description: 'Last name of the user', maxLength: 50 })
@Expose()
name: string;
@IsString()
@Length(1, 50)
lastName: string;

@ApiProperty({
type: String,
description: 'Gender of the user',
enum: ['male', 'female', 'transgender']
})
@Expose()
@IsEnum(['male', 'female', 'transgender'])
gender: string;


@ApiPropertyOptional({
type: String,
Expand Down
31 changes: 29 additions & 2 deletions src/user/dto/user-update.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
IsNotEmpty,
IsEnum,
ValidateIf,
Length,
} from "class-validator";
import { Expose, Type } from "class-transformer";
import { UserStatus } from "../entities/user-entity";
Expand All @@ -22,10 +23,36 @@ class UserDataDTO {
@IsOptional()
username: string;

@ApiProperty({ type: () => String })
@ApiProperty({ type: String, description: 'First name of the user', maxLength: 50 })
@Expose()
@IsOptional()
@IsString()
@Length(1, 50)
firstName?: string;

@ApiProperty({ type: String, description: 'Middle name of the user (optional)', maxLength: 50, required: false })
@Expose()
@IsOptional()
@IsString()
@Length(0, 50)
middleName?: string;

@ApiProperty({ type: String, description: 'Last name of the user', maxLength: 50 })
@Expose()
@IsOptional()
@IsString()
@Length(1, 50)
lastName?: string;

@ApiProperty({
type: String,
description: 'Gender of the user',
enum: ['male', 'female', 'transgender']
})
@Expose()
@IsEnum(['male', 'female', 'transgender'])
@IsOptional()
name: string;
gender?: string;

@ApiProperty({ type: () => String })
@IsString()
Expand Down
Loading

0 comments on commit 312ec7e

Please sign in to comment.