From 20424b511b90e779194ac4d207b5eed7beb04c9f Mon Sep 17 00:00:00 2001 From: Kshitija Kadam Date: Thu, 14 Sep 2023 19:26:34 +0530 Subject: [PATCH 1/5] chore : minor changes in student creation --- src/adapters/hasura/altStudent.adapter.ts | 26 ++++++++++++++--------- src/altStudent/altStudent.controller.ts | 5 +---- src/altStudent/dto/alt-student.dto.ts | 10 ++++----- 3 files changed, 22 insertions(+), 19 deletions(-) diff --git a/src/adapters/hasura/altStudent.adapter.ts b/src/adapters/hasura/altStudent.adapter.ts index 36ba81e..2d60551 100644 --- a/src/adapters/hasura/altStudent.adapter.ts +++ b/src/adapters/hasura/altStudent.adapter.ts @@ -5,12 +5,7 @@ import { SuccessResponse } from "src/success-response"; import { StudentDto } from "src/altStudent/dto/alt-student.dto"; import { ErrorResponse } from "src/error-response"; import { HasuraUserService } from "./user.adapter"; -import { - getUserGroup, - getUserRole, - getToken, - createUserInKeyCloak, -} from "./adapter.utils"; +import { getUserRole } from "./adapter.utils"; @Injectable() export class ALTStudentService { @@ -103,6 +98,10 @@ export class ALTStudentService { const creatorUserId = decoded["https://hasura.io/jwt/claims"]["x-hasura-user-id"]; + studentDto.createdBy = creatorUserId; + studentDto.updatedBy = creatorUserId; + studentDto.role = "student"; + if (altUserRoles.includes("systemAdmin")) { const createdUser: any = await this.userService.createUser( request, @@ -111,9 +110,7 @@ export class ALTStudentService { if (createdUser.statusCode === 200) { studentDto.userId = createdUser.data.userId; - studentDto.createdBy = creatorUserId; - studentDto.updatedBy = creatorUserId; - const studentSchema = new StudentDto(studentDto,false); + const studentSchema = new StudentDto(studentDto, false); let query = ""; Object.keys(studentDto).forEach((e) => { @@ -139,6 +136,9 @@ export class ALTStudentService { insert_Students_one(object: {${query}}) { studentId userId + user { + username + } } } `, @@ -161,6 +161,7 @@ export class ALTStudentService { const response = await this.axios(config); if (response?.data?.errors) { + console.log(response.data.errors); return new ErrorResponse({ errorCode: response.data.errors[0].extensions, errorMessage: response.data.errors[0].message, @@ -174,6 +175,11 @@ export class ALTStudentService { data: result, }); } + } else { + return new ErrorResponse({ + errorCode: "500", + errorMessage: createdUser?.errorMessage, + }); } } else { return new ErrorResponse({ @@ -215,7 +221,7 @@ export class ALTStudentService { // createdAt: item?.created ? `${item.created}` : "", // updatedAt: item?.updated ? `${item.updated}` : "", }; - return new StudentDto(studentMapping,true); + return new StudentDto(studentMapping, true); }); return studentResponse; diff --git a/src/altStudent/altStudent.controller.ts b/src/altStudent/altStudent.controller.ts index b2ca667..748db53 100644 --- a/src/altStudent/altStudent.controller.ts +++ b/src/altStudent/altStudent.controller.ts @@ -57,10 +57,7 @@ export class ALTStudentController { @Req() request: Request, @Body() studentDto: StudentDto ) { - // return - const val = this.altStudentService.createStudent(request, studentDto); - console.log(val); - return val; + return this.altStudentService.createStudent(request, studentDto); } // @Put("/:id") diff --git a/src/altStudent/dto/alt-student.dto.ts b/src/altStudent/dto/alt-student.dto.ts index 36947c2..a763f8d 100644 --- a/src/altStudent/dto/alt-student.dto.ts +++ b/src/altStudent/dto/alt-student.dto.ts @@ -22,7 +22,7 @@ export class StudentDto { @ApiProperty({ type: String, description: "username", - }) + }) // Auto Generated if not provided @Expose() username: string; @@ -55,10 +55,10 @@ export class StudentDto { @Expose() dateOfBirth: string; - @ApiProperty({ - type: String, - description: "role of user", - }) + // @ApiProperty({ + // type: String, + // description: "role of user", + // }) @Expose() role: string; From 390ccb5e750f5cf4146119d2bff61743cdbdcce9 Mon Sep 17 00:00:00 2001 From: Kshitija Kadam Date: Thu, 14 Sep 2023 19:27:42 +0530 Subject: [PATCH 2/5] chore : removing old user api --- src/adapters/hasura/user.adapter.ts | 8 ++++---- src/adapters/userservicelocator.ts | 3 +-- src/user/user.controller.ts | 6 +----- 3 files changed, 6 insertions(+), 11 deletions(-) diff --git a/src/adapters/hasura/user.adapter.ts b/src/adapters/hasura/user.adapter.ts index 233c0f9..bb62880 100644 --- a/src/adapters/hasura/user.adapter.ts +++ b/src/adapters/hasura/user.adapter.ts @@ -531,11 +531,11 @@ export class HasuraUserService implements IServicelocator { } } - /* - public async teacherSegment( + public async teacherSegment( schoolId: string, templateId: string, request: any - ) {} - */ + ) { + throw new Error("Method not implemented."); + } } diff --git a/src/adapters/userservicelocator.ts b/src/adapters/userservicelocator.ts index f48ffb6..dab27a1 100644 --- a/src/adapters/userservicelocator.ts +++ b/src/adapters/userservicelocator.ts @@ -8,6 +8,5 @@ export interface IServicelocator { createUser(request: any, userDto: UserDto); updateUser(id: string, request: any, userDto: UserUpdateDto); searchUser(request: any, userSearchDto: UserSearchDto); - // Not required for ALT - // teacherSegment(schoolId: string, templateId: string, request: any); + teacherSegment(schoolId: string, templateId: string, request: any); } diff --git a/src/user/user.controller.ts b/src/user/user.controller.ts index af91c89..3cf303e 100644 --- a/src/user/user.controller.ts +++ b/src/user/user.controller.ts @@ -129,9 +129,7 @@ export class UserController { reqBody.newPassword ); } - - /* - Not required for ALT + @Get("teachersegment/:schoolId") // @ApiBasicAuth("access-token") @ApiCreatedResponse({ description: "User list." }) @@ -147,6 +145,4 @@ export class UserController { .buildUserAdapter() .teacherSegment(schoolId, templateId, request); } - */ - } From ed578224d3658d5f38c72928d5b068f7ebac59ef Mon Sep 17 00:00:00 2001 From: Kshitija Kadam Date: Thu, 14 Sep 2023 19:30:54 +0530 Subject: [PATCH 3/5] chore : adding function for username generation --- src/adapters/hasura/adapter.utils.ts | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/adapters/hasura/adapter.utils.ts b/src/adapters/hasura/adapter.utils.ts index e7c3fc3..4ff829d 100644 --- a/src/adapters/hasura/adapter.utils.ts +++ b/src/adapters/hasura/adapter.utils.ts @@ -19,6 +19,13 @@ function getUserGroup(role: string) { } } +function getUsername(obj: any) { + return ( + obj.name.toLowerCase().replace(/ /g, "") + + obj.dateOfBirth.replace(/\-/g, "") + ); +} + async function getToken() { const axios = require("axios"); const qs = require("qs"); @@ -92,7 +99,8 @@ async function createUserInKeyCloak(query, token) { try { userResponse = await axios(config); } catch (e) { - console.log(e); + console.log(e, "Keycloak Creation error"); + return e; } const userString = userResponse.headers.location; @@ -102,4 +110,10 @@ async function createUserInKeyCloak(query, token) { return result; } -export { getUserGroup, getUserRole, getToken, createUserInKeyCloak }; +export { + getUserGroup, + getUserRole, + getToken, + createUserInKeyCloak, + getUsername, +}; From a5d3d2125b0c2e5c24c3479f575b5b165d812b43 Mon Sep 17 00:00:00 2001 From: Kshitija Kadam Date: Thu, 14 Sep 2023 19:31:24 +0530 Subject: [PATCH 4/5] feat : added new APIs for user --- src/adapters/hasura/altUser.adapter.ts | 522 +++++++++++++++++++++++++ src/altUser/altUser.controller.ts | 123 ++++++ src/altUser/altUser.module.ts | 18 + src/altUser/dto/alt-user-search.dto.ts | 34 ++ src/altUser/dto/alt-user-update.dto.ts | 70 ++++ src/altUser/dto/alt-user.dto.ts | 220 +++++++++++ src/app.module.ts | 8 +- 7 files changed, 992 insertions(+), 3 deletions(-) create mode 100644 src/adapters/hasura/altUser.adapter.ts create mode 100644 src/altUser/altUser.controller.ts create mode 100644 src/altUser/altUser.module.ts create mode 100644 src/altUser/dto/alt-user-search.dto.ts create mode 100644 src/altUser/dto/alt-user-update.dto.ts create mode 100644 src/altUser/dto/alt-user.dto.ts diff --git a/src/adapters/hasura/altUser.adapter.ts b/src/adapters/hasura/altUser.adapter.ts new file mode 100644 index 0000000..6d2eeb1 --- /dev/null +++ b/src/adapters/hasura/altUser.adapter.ts @@ -0,0 +1,522 @@ +import { Injectable } from "@nestjs/common"; +import { HttpService } from "@nestjs/axios"; +import { SuccessResponse } from "src/success-response"; +import { IServicelocator } from "../userservicelocator"; +import { UserDto } from "src/altUser/dto/alt-user.dto"; +import jwt_decode from "jwt-decode"; +import { UserSearchDto } from "src/user/dto/user-search.dto"; +import { ErrorResponse } from "src/error-response"; +import { UserUpdateDto } from "src/user/dto/user-update.dto"; +import { + getUserRole, + getToken, + createUserInKeyCloak, + getUsername, +} from "./adapter.utils"; +import { ALTUserUpdateDto } from "src/altUser/dto/alt-user-update.dto"; + +@Injectable() +export class ALTHasuraUserService { + axios = require("axios"); + + constructor(private httpService: HttpService) {} + + public async getUser(userId: string, request: any) { + const decoded: any = jwt_decode(request.headers.authorization); + const altUserRoles = + decoded["https://hasura.io/jwt/claims"]["x-hasura-allowed-roles"]; + + const data = { + query: `query GetUser($userId:uuid!) { + Users_by_pk(userId: $userId) { + userId + name + username + email + mobile + gender + dateOfBirth + role + status + createdAt + updatedAt + createdBy + updatedBy + } + } + `, + variables: { userId: userId }, + }; + + const config = { + method: "post", + url: process.env.ALTHASURA, + headers: { + Authorization: request.headers.authorization, + "x-hasura-role": getUserRole(altUserRoles), + "Content-Type": "application/json", + }, + data: data, + }; + + const response = await this.axios(config); + + if (response?.data?.errors) { + return new ErrorResponse({ + errorCode: response.data.errors[0].extensions, + errorMessage: response.data.errors[0].message, + }); + } else { + const result = [response.data.data.Users_by_pk]; + + const userData = await this.mappedResponse(result); + return new SuccessResponse({ + statusCode: response.status, + message: "Ok.", + data: userData[0], + }); + } + } + + public async createUser(request: any, userDto: UserDto) { + const decoded: any = jwt_decode(request.headers.authorization); + const altUserRoles = + decoded["https://hasura.io/jwt/claims"]["x-hasura-allowed-roles"]; + + const userId = decoded["https://hasura.io/jwt/claims"]["x-hasura-user-id"]; + userDto.createdBy = userId; + userDto.updatedBy = userId; + if (!userDto.username) { + userDto.username = getUsername(userDto); + } + if (!userDto.email) { + userDto.email = userDto.username + "@yopmail.com"; + } + const userSchema = new UserDto(userDto, true); + + let query = ""; + let errKeycloak = ""; + let resKeycloak = ""; + + if (altUserRoles.includes("systemAdmin")) { + const response = await getToken(); + const token = response.data.access_token; + resKeycloak = await createUserInKeyCloak(userSchema, token).catch( + (error) => { + errKeycloak = error.response?.data.errorMessage; + console.log(errKeycloak, "Keycloak error"); + return new ErrorResponse({ + errorCode: "500", + errorMessage: "Someting went wrong", + }); + } + ); + } else { + return new ErrorResponse({ + errorCode: "401", + errorMessage: "Unauthorized", + }); + } + + Object.keys(userDto).forEach((e) => { + if ( + userDto[e] && + userDto[e] !== "" && + e != "password" && + Object.keys(userSchema).includes(e) + ) { + if (e === "role") { + query += `${e}: ${userDto[e]},`; + } else if (Array.isArray(userDto[e])) { + query += `${e}: ${JSON.stringify(userDto[e])}, `; + } else { + query += `${e}: ${JSON.stringify(userDto[e])}, `; + } + } + }); + + // Add userId created in keycloak as user Id of ALT user + query += `userId: "${resKeycloak}"`; + const data = { + query: `mutation CreateUser { + insert_Users_one(object: {${query}}) { + userId + } + } + `, + variables: {}, + }; + + const headers = { + Authorization: request.headers.authorization, + "x-hasura-role": getUserRole(altUserRoles), + "Content-Type": "application/json", + }; + + const config = { + method: "post", + url: process.env.REGISTRYHASURA, + headers: headers, + data: data, + }; + + const response = await this.axios(config); + + if (response?.data?.errors || resKeycloak == undefined) { + return new ErrorResponse({ + errorCode: response.data.errors[0].extensions, + errorMessage: response.data.errors[0].message + errKeycloak, + }); + } else { + const result = response.data.data.insert_Users_one; + + return new SuccessResponse({ + statusCode: 200, + message: "Ok.", + data: result, + }); + } + } + + public async updateUser( + userId: string, + request: any, + userUpdateDto: ALTUserUpdateDto + ) { + const decoded: any = jwt_decode(request.headers.authorization); + const altUserRoles = + decoded["https://hasura.io/jwt/claims"]["x-hasura-allowed-roles"]; + + const userSchema = new UserUpdateDto(userUpdateDto); + let userUpdate = ""; + Object.keys(userUpdateDto).forEach((e) => { + if ( + userUpdateDto[e] && + userUpdateDto[e] != "" && + Object.keys(userSchema).includes(e) + ) { + if (e === "role") { + userUpdate += `${e}: ${userUpdateDto[e]},`; + } else if (Array.isArray(userUpdateDto[e])) { + userUpdate += `${e}: ${JSON.stringify(userUpdateDto[e])}, `; + } else { + userUpdate += `${e}: ${JSON.stringify(userUpdateDto[e])}, `; + } + } + }); + + const data = { + query: `mutation UpdateUser ($userId:uuid){ + update_Users(where: {userId: {_eq: $userId}}, _set: {${userUpdate}}) { + affected_rows + } + }`, + variables: { + userId: userId, + }, + }; + + const config = { + method: "post", + url: process.env.ALTHASURA, + headers: { + Authorization: request.headers.authorization, + "x-hasura-role": getUserRole(altUserRoles), + "Content-Type": "application/json", + }, + data: data, + }; + + const response = await this.axios(config); + + if (response?.data?.errors) { + return new ErrorResponse({ + errorCode: response.data.errors[0].extensions, + errorMessage: response.data.errors[0].message, + }); + } else { + const result = response.data.data.update_Users; + return new SuccessResponse({ + statusCode: 200, + message: "Ok.", + data: result, + }); + } + } + + public async searchUser(request: any, userSearchDto: UserSearchDto) { + const decoded: any = jwt_decode(request.headers.authorization); + const altUserRoles = + decoded["https://hasura.io/jwt/claims"]["x-hasura-allowed-roles"]; + + let offset = 0; + if (userSearchDto.page > 1) { + offset = userSearchDto.limit * (userSearchDto.page - 1); + } + + let query = ""; + Object.keys(userSearchDto.filters).forEach((e) => { + if (userSearchDto.filters[e] && userSearchDto.filters[e] != "") { + if (e === "name" || e === "username") { + query += `${e}:{_ilike: "%${userSearchDto.filters[e]}%"}`; + } else { + query += `${e}:{_eq:"${userSearchDto.filters[e]}"}`; + } + } + }); + + const data = { + query: `query SearchUser($limit:Int, $offset:Int) { + Users_aggregate { + aggregate { + count + } + } + Users(where:{${query}}, limit: $limit, offset: $offset,) { + userId + name + username + email + mobile + gender + dateOfBirth + role + status + createdAt + updatedAt + createdBy + updatedBy + } + }`, + variables: { + limit: userSearchDto.limit, + offset: offset, + }, + }; + + const config = { + method: "post", + url: process.env.ALTHASURA, + headers: { + Authorization: request.headers.authorization, + "x-hasura-role": getUserRole(altUserRoles), + "Content-Type": "application/json", + }, + data: data, + }; + + const response = await this.axios(config); + + if (response?.data?.errors) { + return new ErrorResponse({ + errorCode: response.data.errors[0].extensions, + errorMessage: response.data.errors[0].message, + }); + } else { + const result = response.data.data.Users; + const userData = await this.mappedResponse(result); + const count = response?.data?.data?.user_aggregate?.aggregate?.count; + + return new SuccessResponse({ + statusCode: 200, + message: "Ok.", + data: userData, + }); + } + } + + public async mappedResponse(result: any) { + const userResponse = result.map((item: any) => { + const userMapping = { + userId: item?.userId ? `${item.userId}` : "", + name: item?.name ? `${item.name}` : "", + username: item?.username ? `${item.username}` : "", + schoolUdise: item?.schoolUdise ? `${item.schoolUdise}` : "", + email: item?.email ? `${item.email}` : "", + mobile: item?.mobile ? item.mobile : "", + gender: item?.gender ? `${item.gender}` : "", + dateOfBirth: item?.dateOfBirth ? `${item.dateOfBirth}` : "", + status: item?.status ? `${item.status}` : "", + role: item?.role ? `${item.role}` : "", + createdAt: item?.createdAt ? `${item.createdAt}` : "", + updatedAt: item?.updatedAt ? `${item.updatedAt}` : "", + createdBy: item?.createdBy ? `${item.createdBy}` : "", + updatedBy: item?.updatedBy ? `${item.updatedBy}` : "", + }; + return new UserDto(userMapping, false); + }); + + return userResponse; + } + + public async getUserByAuth(request: any) { + const authToken = request.headers.authorization; + const decoded: any = jwt_decode(authToken); + const altUserRoles = + decoded["https://hasura.io/jwt/claims"]["x-hasura-allowed-roles"]; + const username = decoded.preferred_username; + + const data = { + query: `query searchUser($username:String) { + Users(where: {username: {_eq: $username}}) { + userId + name + username + email + mobile + gender + dateOfBirth + role + status + createdAt + updatedAt + createdBy + updatedBy + } + }`, + variables: { username: username }, + }; + + const config = { + method: "post", + url: process.env.ALTHASURA, + headers: { + Authorization: request.headers.authorization, + "x-hasura-role": getUserRole(altUserRoles), + "Content-Type": "application/json", + }, + data: data, + }; + + const response = await this.axios(config); + + if (response?.data?.errors) { + return new ErrorResponse({ + errorCode: response.data.errors[0].extensions, + errorMessage: response.data.errors[0].message, + }); + } else { + const result = response.data.data.Users; + const userData = await this.mappedResponse(result); + return new SuccessResponse({ + statusCode: 200, + message: "Ok.", + data: userData, + }); + } + } + + public async resetUserPassword( + request: any, + username: string, + newPassword: string + ) { + const userData: any = await this.getUserByUsername(username, request); + let userId; + + if (userData?.data?.userId) { + userId = userData.data.userId; + } else { + return new ErrorResponse({ + errorCode: `404`, + errorMessage: "User with given username not found", + }); + } + + const data = JSON.stringify({ + temporary: "false", + type: "password", + value: newPassword, + }); + + const response = await getToken(); + const res = response.data.access_token; + let apiResponse; + + const config = { + method: "put", + url: + "https://alt-shiksha.uniteframework.io/auth/admin/realms/hasura/users/" + + userId + + "/reset-password", + headers: { + "Content-Type": "application/json", + Authorization: "Bearer " + res, + }, + data: data, + }; + + try { + apiResponse = await this.axios(config); + } catch (e) { + return new ErrorResponse({ + errorCode: `${e.response.status}`, + errorMessage: e.response.data.error, + }); + } + + if (apiResponse.status === 204) { + return new SuccessResponse({ + statusCode: apiResponse.status, + message: apiResponse.statusText, + data: { msg: "Password reset successful!" }, + }); + } else { + return new ErrorResponse({ + errorCode: "400", + errorMessage: apiResponse.errors, + }); + } + } + + public async getUserByUsername(username: string, request: any) { + const data = { + query: `query GetUserByUsername($username:String) { + Users(where: {username: {_eq: $username}}){ + userId + name + username + email + mobile + gender + dateOfBirth + role + status + createdAt + updatedAt + createdBy + updatedBy + } + } + `, + variables: { username: username }, + }; + + const config = { + method: "post", + url: process.env.REGISTRYHASURA, + headers: { + "x-hasura-admin-secret": process.env.REGISTRYHASURAADMINSECRET, + "Content-Type": "application/json", + }, + data: data, + }; + + const response = await this.axios(config); + + if (response?.data?.errors) { + return new ErrorResponse({ + errorCode: response.data.errors[0].extensions, + errorMessage: response.data.errors[0].message, + }); + } else { + const result = response.data.data.Users; + const userData = await this.mappedResponse(result); + return new SuccessResponse({ + statusCode: response.status, + message: "Ok.", + data: userData[0], + }); + } + } +} diff --git a/src/altUser/altUser.controller.ts b/src/altUser/altUser.controller.ts new file mode 100644 index 0000000..fc75e64 --- /dev/null +++ b/src/altUser/altUser.controller.ts @@ -0,0 +1,123 @@ +import { + Controller, + Get, + Post, + Body, + Put, + Param, + UseInterceptors, + ClassSerializerInterceptor, + SerializeOptions, + Req, + CacheInterceptor, + Inject, + Query, +} from "@nestjs/common"; +import { + SunbirdUserToken, + UserService, +} from "../adapters/sunbirdrc/user.adapter"; +import { Request } from "@nestjs/common"; +import { + ApiTags, + ApiBody, + ApiOkResponse, + ApiForbiddenResponse, + ApiCreatedResponse, + ApiBasicAuth, + ApiQuery, +} from "@nestjs/swagger"; + +import { ALTHasuraUserService } from "src/adapters/hasura/altUser.adapter"; +import { UserDto } from "./dto/alt-user.dto"; +import { ALTUserUpdateDto } from "./dto/alt-user-update.dto"; +import { ALTUserSearchDto } from "./dto/alt-user-search.dto"; +@ApiTags("User") +@Controller("user") +export class ALTUserController { + constructor(private hasuraUserService: ALTHasuraUserService) {} + + @Get("/:id") + @UseInterceptors(ClassSerializerInterceptor, CacheInterceptor) + @ApiBasicAuth("access-token") + @ApiOkResponse({ description: "User detail." }) + @ApiForbiddenResponse({ description: "Forbidden" }) + @SerializeOptions({ + strategy: "excludeAll", + }) + public async getUser(@Param("id") id: string, @Req() request: Request) { + return this.hasuraUserService.getUser(id, request); + } + + @Get() + @UseInterceptors(ClassSerializerInterceptor, CacheInterceptor) + @ApiBasicAuth("access-token") + @ApiOkResponse({ description: "User detail." }) + @ApiForbiddenResponse({ description: "Forbidden" }) + @SerializeOptions({ + strategy: "excludeAll", + }) + public async getUserByAuth(@Req() request: Request) { + return this.hasuraUserService.getUserByAuth(request); + } + + @Post() + @ApiBasicAuth("access-token") + @ApiCreatedResponse({ description: "User has been created successfully." }) + @ApiBody({ type: UserDto }) + @ApiForbiddenResponse({ description: "Forbidden" }) + @UseInterceptors(ClassSerializerInterceptor) + public async createUser(@Req() request: Request, @Body() userDto: UserDto) { + return this.hasuraUserService.createUser(request, userDto); + } + + @Put("/:id") + @ApiBasicAuth("access-token") + @ApiCreatedResponse({ description: "User has been updated successfully." }) + @ApiForbiddenResponse({ description: "Forbidden" }) + @UseInterceptors(ClassSerializerInterceptor) + public async updateUser( + @Param("id") id: string, + @Req() request: Request, + @Body() userUpdateDto: ALTUserUpdateDto + ) { + return await this.hasuraUserService.updateUser(id, request, userUpdateDto); + } + + @Post("/search") + @ApiBasicAuth("access-token") + @ApiCreatedResponse({ description: "User list." }) + @ApiBody({ type: ALTUserSearchDto }) + @ApiForbiddenResponse({ description: "Forbidden" }) + @UseInterceptors(ClassSerializerInterceptor) + @SerializeOptions({ + strategy: "excludeAll", + }) + public async searchUser( + @Req() request: Request, + @Body() userSearchDto: ALTUserSearchDto + ) { + return await this.hasuraUserService.searchUser(request, userSearchDto); + } + + @Post("/reset-password") + @ApiBasicAuth("access-token") + @ApiOkResponse({ description: "Password reset successfully." }) + @ApiForbiddenResponse({ description: "Forbidden" }) + @ApiBody({ type: Object }) + @UseInterceptors(ClassSerializerInterceptor) + public async resetUserPassword( + @Req() request: Request, + @Body() + reqBody: { + userName: string; + newPassword: string; + } + ) { + return this.hasuraUserService.resetUserPassword( + request, + reqBody.userName, + reqBody.newPassword + ); + } +} diff --git a/src/altUser/altUser.module.ts b/src/altUser/altUser.module.ts new file mode 100644 index 0000000..5c25ed4 --- /dev/null +++ b/src/altUser/altUser.module.ts @@ -0,0 +1,18 @@ +import { CacheModule, Module } from "@nestjs/common"; +import { HttpModule } from "@nestjs/axios"; +import { ALTUserController } from "./altUser.controller"; +import { ALTHasuraUserService } from "src/adapters/hasura/altUser.adapter"; + +const ttl = process.env.TTL as never; + +@Module({ + imports: [ + HttpModule, + CacheModule.register({ + ttl: ttl, + }), + ], + controllers: [ALTUserController], + providers: [ALTHasuraUserService], +}) +export class ALTUserModule {} diff --git a/src/altUser/dto/alt-user-search.dto.ts b/src/altUser/dto/alt-user-search.dto.ts new file mode 100644 index 0000000..f7c2eba --- /dev/null +++ b/src/altUser/dto/alt-user-search.dto.ts @@ -0,0 +1,34 @@ +import { Exclude, Expose } from "class-transformer"; +import { + MaxLength, + IsNotEmpty, + IsEmail, + IsString, + IsNumber, +} from "class-validator"; +import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger"; + +export class ALTUserSearchDto { + @ApiProperty({ + type: Number, + description: "Limit", + }) + limit: number; + + @ApiProperty({ + type: Object, + description: "Filters", + }) + @ApiPropertyOptional() + filters: object; + + @ApiProperty({ + type: Number, + description: "Page", + }) + page: number; + + constructor(partial: Partial) { + Object.assign(this, partial); + } +} diff --git a/src/altUser/dto/alt-user-update.dto.ts b/src/altUser/dto/alt-user-update.dto.ts new file mode 100644 index 0000000..561746d --- /dev/null +++ b/src/altUser/dto/alt-user-update.dto.ts @@ -0,0 +1,70 @@ +import { Exclude, Expose } from "class-transformer"; +import { + MaxLength, + IsNotEmpty, + IsEmail, + IsString, + IsNumber, +} from "class-validator"; +import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger"; + +export class ALTUserUpdateDto { + @ApiProperty({ + type: String, + description: "The full name of the user", + }) + @Expose() + name: string; + + @ApiProperty({ + type: String, + description: "The contact number of the user", + }) + @Expose() + mobile: string; + + @ApiProperty({ + type: String, + description: "The email of the user", + }) + @Expose() + @IsEmail() + email: string; + + @ApiProperty({ + type: String, + description: "The gender of the user", + }) + @Expose() + gender: string; + + @ApiProperty({ + type: String, + description: "The birthDate of the user", + }) + @Expose() + dateOfBirth: string; + + @ApiProperty({ + type: Boolean, + description: "Status", + }) + @Expose() + status: boolean; + + @Expose() + createdAt: string; + + @Expose() + updatedAt: string; + + @Expose() + createdBy: string; + + @Expose() + updatedBy: string; + + constructor(obj: any) { + Object.assign(this, obj); + } +} diff --git a/src/altUser/dto/alt-user.dto.ts b/src/altUser/dto/alt-user.dto.ts new file mode 100644 index 0000000..84656dd --- /dev/null +++ b/src/altUser/dto/alt-user.dto.ts @@ -0,0 +1,220 @@ +import { Exclude, Expose } from "class-transformer"; +import { + MaxLength, + IsNotEmpty, + IsEmail, + IsString, + IsNumber, +} from "class-validator"; +import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger"; + +export class UserDto { + @Expose() + userId: string; + + @ApiProperty({ + type: String, + description: "The full name of the user", + }) + @Expose() + name: string; + + @ApiProperty({ + type: String, + description: "username", + }) // Auto Generated if not provided + @Expose() + username: string; + + @ApiProperty({ + type: String, + description: "The email of the user", + }) + @Expose() + @IsEmail() + email: string; + + @ApiProperty({ + type: String, + description: "The contact number of the user", + }) + @Expose() + mobile: string; + + @ApiProperty({ + type: String, + description: "The gender of the user", + }) + @Expose() + gender: string; + + @ApiProperty({ + type: String, + description: "The birthDate of the user", + }) + @Expose() + dateOfBirth: string; + + @ApiProperty({ + type: String, + description: "role of user", + }) + @Expose() + role: string; + + // @ApiProperty({ + // type: String, + // description: "The school of the user", + // }) + // @Expose() + // schoolUdise: string; + + @ApiProperty({ + type: String, + description: "Password", + }) + @Expose() + password: string; + + @ApiProperty({ + type: Boolean, + description: "Status", + }) + @Expose() + status: boolean; + + @Expose() + createdAt: string; + + @Expose() + updatedAt: string; + + @Expose() + createdBy: string; + + @Expose() + updatedBy: string; + + constructor(obj: any, includePassword) { + this.userId = obj?.userId ? `${obj.userId}` : ""; + this.name = obj?.name ? `${obj.name}` : ""; + this.username = obj?.username ? `${obj.username}` : ""; + this.email = obj?.email ? `${obj.email}` : ""; + this.mobile = obj?.mobile ? obj.mobile : ""; + this.gender = obj?.gender ? `${obj.gender}` : ""; + this.dateOfBirth = obj?.dateOfBirth ? `${obj.dateOfBirth}` : ""; + this.status = obj?.status ? obj.status : false; + this.role = obj?.role ? `${obj.role}` : ""; + // this.createdAt = obj?.createdAt ? `${obj.createdAt}` : ""; + // this.updatedAt = obj?.updatedAt ? `${obj.updatedAt}` : ""; + this.createdBy = obj?.createdBy ? `${obj.createdBy}` : ""; + this.updatedBy = obj?.updatedBy ? `${obj.updatedBy}` : ""; + if (includePassword) { + this.password = obj?.password ? `${obj.password}` : ""; + } + } +} + +/* + @ApiProperty({ + type: String, + description: "the user medium", + }) + @Expose() + medium: string; + + @ApiPropertyOptional() + @Expose() + grade: string; + + @ApiProperty({ + type: String, + description: "The father's name of the user", + }) + @Expose() + father: string; + + @ApiProperty({ + type: String, + description: "The father's name of the user", + }) + @Expose() + mother: string; + + @ApiProperty({ + type: String, + description: "user udise Id", + }) + @Expose() + uniqueId: string; + + @ApiProperty({ + type: String, + description: "user udise Id", + }) + @Expose() + udise: string; + + @ApiProperty({ + type: String, + description: "user Serial Id", + }) + @Expose() + serialNo: string; + + @ApiProperty({ + type: String, + description: "The school of the user", + }) + @Expose() + school: string; + + + @ApiProperty({ + type: String, + description: "State", + }) + @Expose() + state: string; + + @ApiProperty({ + type: String, + description: "District", + }) + @Expose() + district: string; + + @ApiProperty({ + type: String, + description: "Student grade section", + }) + @Expose() + section: string; + + @ApiProperty() + @Expose() + block: string; + + @ApiProperty({ + type: String, + description: "The bloodGroup of the user", + }) + @Expose() + bloodGroup: string; + + @ApiProperty({ + type: String, + description: "The status of the user", + }) + @Expose() + status: string; + + @ApiProperty({ + type: String, + description: "The image of the user", + }) + @Expose() + image: string; + + + */ diff --git a/src/app.module.ts b/src/app.module.ts index 5c65f60..51f4207 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -25,7 +25,7 @@ import { AppService } from "./app.service"; // import { RoleModule } from "./role/role.module"; // import { WorkHistoryModule } from "./workHistory/workHistory.module"; // import { StudentModule } from "./student/student.module"; -import { UserModule } from "./user/user.module"; +// import { UserModule } from "./user/user.module"; import { SchoolModule } from "./school/school.module"; import { GroupModule } from "./group/group.module"; import { GroupMembershipModule } from "./groupMembership/groupMembership.module"; @@ -39,6 +39,7 @@ import { ALTCurrentPhaseModule } from "./altCurrentPhase/altCurrentPhase.module" import { ALTAssessmentExportModule } from "./altAssessmentExport/altAssessmentExport.module"; import { ALTTimeSpentExportModule } from "./altTimeSpentExport/altTimeSpentExport.module"; import { ALTStudentModule } from "./altStudent/altStudent.module"; +import { ALTUserModule } from "./altUser/altUser.module"; @Module({ imports: [ ConfigModule.forRoot(), @@ -67,8 +68,8 @@ import { ALTStudentModule } from "./altStudent/altStudent.module"; AnnouncementsModule, WorkHistoryModule, StudentModule, - */ UserModule, + */ SchoolModule, GroupModule, GroupMembershipModule, @@ -81,7 +82,8 @@ import { ALTStudentModule } from "./altStudent/altStudent.module"; ALTCurrentPhaseModule, ALTAssessmentExportModule, ALTTimeSpentExportModule, - ALTStudentModule + ALTStudentModule, + ALTUserModule ], controllers: [AppController], providers: [AppService], From 3d2909d1d6250140266a9c9711e005b7ee9e60f4 Mon Sep 17 00:00:00 2001 From: Kshitija Kadam Date: Thu, 14 Sep 2023 19:47:37 +0530 Subject: [PATCH 5/5] fix: minor fixes --- src/adapters/hasura/altStudent.adapter.ts | 4 ++-- src/adapters/hasura/altUser.adapter.ts | 13 ++----------- src/altStudent/altStudent.module.ts | 4 ++-- 3 files changed, 6 insertions(+), 15 deletions(-) diff --git a/src/adapters/hasura/altStudent.adapter.ts b/src/adapters/hasura/altStudent.adapter.ts index 2d60551..0e7fd12 100644 --- a/src/adapters/hasura/altStudent.adapter.ts +++ b/src/adapters/hasura/altStudent.adapter.ts @@ -4,14 +4,14 @@ import jwt_decode from "jwt-decode"; import { SuccessResponse } from "src/success-response"; import { StudentDto } from "src/altStudent/dto/alt-student.dto"; import { ErrorResponse } from "src/error-response"; -import { HasuraUserService } from "./user.adapter"; import { getUserRole } from "./adapter.utils"; +import { ALTHasuraUserService } from "./altUser.adapter"; @Injectable() export class ALTStudentService { constructor( private httpService: HttpService, - private userService: HasuraUserService + private userService: ALTHasuraUserService ) {} baseURL = process.env.ALTHASURA; diff --git a/src/adapters/hasura/altUser.adapter.ts b/src/adapters/hasura/altUser.adapter.ts index 6d2eeb1..21374b4 100644 --- a/src/adapters/hasura/altUser.adapter.ts +++ b/src/adapters/hasura/altUser.adapter.ts @@ -119,12 +119,7 @@ export class ALTHasuraUserService { } Object.keys(userDto).forEach((e) => { - if ( - userDto[e] && - userDto[e] !== "" && - e != "password" && - Object.keys(userSchema).includes(e) - ) { + if (e != "password" && Object.keys(userSchema).includes(e)) { if (e === "role") { query += `${e}: ${userDto[e]},`; } else if (Array.isArray(userDto[e])) { @@ -190,11 +185,7 @@ export class ALTHasuraUserService { const userSchema = new UserUpdateDto(userUpdateDto); let userUpdate = ""; Object.keys(userUpdateDto).forEach((e) => { - if ( - userUpdateDto[e] && - userUpdateDto[e] != "" && - Object.keys(userSchema).includes(e) - ) { + if (Object.keys(userSchema).includes(e)) { if (e === "role") { userUpdate += `${e}: ${userUpdateDto[e]},`; } else if (Array.isArray(userUpdateDto[e])) { diff --git a/src/altStudent/altStudent.module.ts b/src/altStudent/altStudent.module.ts index 6f71ec5..102711c 100644 --- a/src/altStudent/altStudent.module.ts +++ b/src/altStudent/altStudent.module.ts @@ -2,7 +2,7 @@ import { CacheModule, Module } from "@nestjs/common"; import { HttpModule } from "@nestjs/axios"; import { ALTStudentController } from "./altStudent.controller"; import { ALTStudentService } from "src/adapters/hasura/altStudent.adapter"; -import { HasuraUserService } from "src/adapters/hasura/user.adapter"; +import { ALTHasuraUserService } from "src/adapters/hasura/altUser.adapter"; const ttl = process.env.TTL as never; @@ -14,6 +14,6 @@ const ttl = process.env.TTL as never; }), ], controllers: [ALTStudentController], - providers: [ALTStudentService, HasuraUserService], + providers: [ALTStudentService, ALTHasuraUserService], }) export class ALTStudentModule {}