Skip to content

Commit

Permalink
TST - S8 convergence
Browse files Browse the repository at this point in the history
  • Loading branch information
juliecoust committed Jun 25, 2024
1 parent b425b17 commit 51165ee
Show file tree
Hide file tree
Showing 19 changed files with 219 additions and 156 deletions.
2 changes: 1 addition & 1 deletion src/domain/entities/project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ export interface PublicProjectUpdateModel {
contact?: MinimalUserModel;
}
export interface PublicProjectRequestCreationModel {
project_id: number;
//project_id: number;
root_folder_path: string;
project_title: string;
project_acronym: string;
Expand Down
3 changes: 2 additions & 1 deletion src/domain/interfaces/repositories/privilege-repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@ import { PrivilegeRequestModel, PublicPrivilege } from "../../entities/privilege
import { ProjectRequestModel } from "../../entities/project";

export interface PrivilegeRepository {
isManager(is_granted_params: PrivilegeRequestModel): unknown;
ensurePrivilegeCoherence(publicPrivilege: PublicPrivilege): void;
getProjectsByMembers(user_ids: number[]): Promise<number[]>;
getProjectsByManagers(user_ids: number[]): Promise<number[]>;
getProjectsByContacts(user_ids: number[]): Promise<number[]>;
getProjectsByUsers(user_ids: number[]): Promise<number[]>;
getProjectsByUser(privilege: PrivilegeRequestModel): Promise<number[]>;
is_granted(privilege: PrivilegeRequestModel): Promise<boolean>;
isGranted(privilege: PrivilegeRequestModel): Promise<boolean>;
createPrivileges(publicPrivilege: PublicPrivilege): Promise<number>;
getPublicPrivileges(project: ProjectRequestModel): Promise<PublicPrivilege>;
deletePrivileges(privilege: PrivilegeRequestModel): Promise<number>
Expand Down
27 changes: 26 additions & 1 deletion src/domain/repositories/privilege-repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ export class PrivilegeRepositoryImpl implements PrivilegeRepository {
return privileges.items.map(privilege => privilege.project_id);
}

async is_granted(privilege: PrivilegeRequestModel): Promise<boolean> {
async isGranted(privilege: PrivilegeRequestModel): Promise<boolean> {
if (!privilege.user_id || !privilege.project_id) {
throw new Error("Please provide a valid user_id and project_id to check if a privilege is granted for a project and a user")
}
Expand All @@ -188,6 +188,31 @@ export class PrivilegeRepositoryImpl implements PrivilegeRepository {
const privileges = await this.privilegeDataSource.getAll(prepare_options)
return privileges.items.length > 0;
}
async isManager(is_granted_params: PrivilegeRequestModel): Promise<boolean> {
if (!is_granted_params.user_id || !is_granted_params.project_id) {
throw new Error("Please provide a valid user_id and project_id to check if a user is a manager for a project")
}
const filter = [
{
field: "user_id",
operator: "=",
value: is_granted_params.user_id
},
{
field: "project_id",
operator: "=",
value: is_granted_params.project_id
},
{
field: "privilege_name",
operator: "=",
value: "manager"
}
]

const privileges = await this.getPrivilegesByFilter(filter)
return privileges.items.length > 0;
}

async getContact(project: ProjectRequestModel): Promise<PrivilegeResponseModel> {
const prepare_options: PreparedSearchOptions = {
Expand Down
2 changes: 1 addition & 1 deletion src/domain/use-cases/project/delete-project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export class DeleteProject implements DeleteProjectUseCase {
// Ensure user is admin or has privilege to delete the project
private async ensureUserCanDelete(current_user: UserUpdateModel, project: ProjectRequestModel): Promise<void> {
const userIsAdmin = await this.userRepository.isAdmin(current_user.user_id);
const userHasPrivilege = await this.privilegeRepository.is_granted({
const userHasPrivilege = await this.privilegeRepository.isGranted({
user_id: current_user.user_id,
project_id: project.project_id
});
Expand Down
9 changes: 8 additions & 1 deletion src/domain/use-cases/project/update-project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,17 @@ export class UpdateProject implements UpdateProjectUseCase {
// Ensure user is admin or has privilege to update the project
private async ensureUserCanUpdate(current_user: UserUpdateModel, project: PublicProjectUpdateModel): Promise<void> {
const is_granted_params: PrivilegeRequestModel = { user_id: current_user.user_id, project_id: project.project_id }
const hasPrivilege = await this.privilegeRepository.is_granted(is_granted_params) || await this.userRepository.isAdmin(current_user.user_id);
const hasPrivilege = await this.privilegeRepository.isGranted(is_granted_params) || await this.userRepository.isAdmin(current_user.user_id);
if (!hasPrivilege) {
throw new Error("Logged user cannot update this property or project");
}
// Only managers or admin can update privileges of a project
if (this.isPrivilegeUpdate(project)) {
const canUpdatePrivilege = await this.privilegeRepository.isManager(is_granted_params) || await this.userRepository.isAdmin(current_user.user_id);
if (!canUpdatePrivilege) {
throw new Error("Logged user cannot update privileges");
}
}
}

private async prepareProjectUpdateModel(public_project_to_update: PublicProjectUpdateModel): Promise<ProjectUpdateModel> {
Expand Down
3 changes: 2 additions & 1 deletion src/presentation/middleware/user-validation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,8 @@ export class MiddlewareUserValidation implements IMiddlewareUserValidation {
throw new Error('Invalid country. Please select from the list.');
}
return true;
}),
})
.bail(),
// Email Validation
check('email').optional()
.trim()
Expand Down
2 changes: 1 addition & 1 deletion src/presentation/routers/auth-router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ export default function AuthRouter(
} catch (err) {
console.log(err)
if (err.message === "User does not exist") res.status(200).send({ message: "Reset password request email sent." })
else if (err.message === "User cannot be used") res.status(403).send({ errors: [err.message] })
else if (err.message === "User cannot be used") res.status(403).send({ errors: ["Cannot reset password"] })
else if (err.message === "User email is not validated") res.status(200).send({ message: "Reset password request email sent." })
else if (err.message === "Cannot set password reset code") res.status(500).send({ errors: ["Cannot reset password"] })
else if (err.message === "Cannot find updated user") res.status(500).send({ errors: ["Cannot reset password"] })
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,6 @@ function cleanProjectDB() {
describe('SQLiteProjectDataSource', () => {
let dataSource: SQLiteProjectDataSource;



beforeAll(() => {
dataSource = initializeProjectDB();
});
Expand Down
8 changes: 4 additions & 4 deletions test/domain/repositories/project-repository.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { ProjectRequestCreationModel, ProjectRequestModel, ProjectResponseModel,
import { SearchResult } from "../../../src/domain/entities/search";
import { ProjectRepository } from "../../../src/domain/interfaces/repositories/project-repository";
import { ProjectRepositoryImpl } from "../../../src/domain/repositories/project-repository";
import { projectRequestCreationModel, projectResponseModel, projectResponseModelArray, projectUpdateModel, projectUpdateModel_withBadData } from "../../entities/project";
import { projectRequestCreationModelForRepository, projectResponseModel, projectResponseModelArray, projectUpdateModel, projectUpdateModel_withBadData } from "../../entities/project";
import { MockProjectDataSource } from "../../mocks/project-mock";

import 'dotenv/config'
Expand All @@ -23,7 +23,7 @@ describe("Project Repository", () => {

describe("CreateProject", () => {
test("Should create a project", async () => {
const project: ProjectRequestCreationModel = projectRequestCreationModel
const project: ProjectRequestCreationModel = projectRequestCreationModelForRepository

jest.spyOn(mockProjectDataSource, 'create').mockResolvedValue(1)

Expand Down Expand Up @@ -51,7 +51,7 @@ describe("Project Repository", () => {

describe("ComputeDefaultDepthOffset", () => {
test("Should compute default depth offset", async () => {
const instrument_model = 1
const instrument_model = "UVP5HD"
const result = projectRepository.computeDefaultDepthOffset(instrument_model)

expect(result).toBe(1.2)
Expand All @@ -63,7 +63,7 @@ describe("Project Repository", () => {
})

test("Should return undefined if instrument is not uvp5", async () => {
const instrument_model = 50
const instrument_model = "not_uvp5"
const result = projectRepository.computeDefaultDepthOffset(instrument_model)

expect(result).toBe(undefined)
Expand Down
2 changes: 1 addition & 1 deletion test/domain/use-cases/user/create-user.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ describe("Create User Use Case", () => {
}
beforeEach(async () => {
jest.clearAllMocks();
mockUserRepository = new MockUserRepository()
mockUserRepository = new MockUserRepository();
mockMailerAdapter = new NodemailerAdapter((config.TEST_BASE_URL + config.TEST_PORT), config.TEST_MAIL_SENDER)
mockTransporter = await mockMailerAdapter.createTransport({
host: config.TEST_MAIL_HOST,
Expand Down
115 changes: 84 additions & 31 deletions test/entities/project.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,36 @@

// define an example of project entities to use in the tests

import { ProjectRequestCreationModel, ProjectRequestModel, ProjectResponseModel, ProjectUpdateModel } from "../../src/domain/entities/project"
import { ProjectRequestCreationModel, ProjectRequestModel, ProjectUpdateModel, PublicProjectRequestCreationModel, PublicProjectResponseModel } from "../../src/domain/entities/project"
import { MinimalUserModel } from "../../src/domain/entities/user"

export const projectRequestCreationModel: ProjectRequestCreationModel = {
export const projectRequestCreationModel: PublicProjectRequestCreationModel = {
root_folder_path: 'root_folder_path',
project_title: 'project_title',
project_acronym: 'project_acronym',
project_description: 'project_description',
project_information: 'project_information',
cruise: 'cruise',
ship: "['ship1', 'ship2']",
data_owner_name: 'data_owner_name',
data_owner_email: '[email protected]',
operator_name: 'operator_name',
operator_email: '[email protected]',
chief_scientist_name: 'chief_scientist_name',
chief_scientist_email: '[email protected]',
override_depth_offset: 1,
enable_descent_filter: true,
privacy_duration: 1,
visible_duration: 1,
public_duration: 1,
instrument_model: "UVP5HD",
serial_number: 'serial_number',
contact: { user_id: 1 } as MinimalUserModel,
managers: [{ user_id: 1 } as MinimalUserModel],
members: [{ user_id: 2 } as MinimalUserModel],

}
export const projectRequestCreationModelForRepository: ProjectRequestCreationModel = {
root_folder_path: 'root_folder_path',
project_title: 'project_title',
project_acronym: 'project_acronym',
Expand All @@ -26,7 +53,7 @@ export const projectRequestCreationModel: ProjectRequestCreationModel = {
serial_number: 'serial_number'
}

export const projectRequestCreationModel_2: ProjectRequestCreationModel = {
export const projectRequestCreationModel_2: PublicProjectRequestCreationModel = {
root_folder_path: 'root_folder_path',
project_title: 'joan project_title',
project_acronym: 'project_acronym',
Expand All @@ -45,10 +72,13 @@ export const projectRequestCreationModel_2: ProjectRequestCreationModel = {
privacy_duration: 1,
visible_duration: 1,
public_duration: 1,
instrument_model: 1,
serial_number: 'serial_number'
instrument_model: "UVP5HD",
serial_number: 'serial_number',
contact: { user_id: 1 } as MinimalUserModel,
managers: [{ user_id: 1 } as MinimalUserModel],
members: [{ user_id: 2 } as MinimalUserModel],
}
export const projectRequestCreationModel_3: ProjectRequestCreationModel = {
export const projectRequestCreationModel_3: PublicProjectRequestCreationModel = {
root_folder_path: 'root_folder_path',
project_title: 'john project_title',
project_acronym: 'project_acronym',
Expand All @@ -67,10 +97,13 @@ export const projectRequestCreationModel_3: ProjectRequestCreationModel = {
privacy_duration: 1,
visible_duration: 1,
public_duration: 1,
instrument_model: 1,
serial_number: 'serial_number'
instrument_model: "UVP5HD",
serial_number: 'serial_number',
contact: { user_id: 1 } as MinimalUserModel,
managers: [{ user_id: 1 } as MinimalUserModel],
members: [{ user_id: 2 } as MinimalUserModel],
}
export const projectRequestCreationModel_4: ProjectRequestCreationModel = {
export const projectRequestCreationModel_4: PublicProjectRequestCreationModel = {
root_folder_path: 'root_folder_path',
project_title: 'alice project_title',
project_acronym: 'project_acronym',
Expand All @@ -89,10 +122,13 @@ export const projectRequestCreationModel_4: ProjectRequestCreationModel = {
privacy_duration: 1,
visible_duration: 1,
public_duration: 1,
instrument_model: 1,
serial_number: 'serial_number'
instrument_model: "UVP5HD",
serial_number: 'serial_number',
contact: { user_id: 1 } as MinimalUserModel,
managers: [{ user_id: 1 } as MinimalUserModel],
members: [{ user_id: 2 } as MinimalUserModel],
}
export const projectRequestCreationModel_5: ProjectRequestCreationModel = {
export const projectRequestCreationModel_5: PublicProjectRequestCreationModel = {
root_folder_path: 'root_folder_path',
project_title: 'marc project_title',
project_acronym: 'project_acronym',
Expand All @@ -111,10 +147,13 @@ export const projectRequestCreationModel_5: ProjectRequestCreationModel = {
privacy_duration: 1,
visible_duration: 1,
public_duration: 1,
instrument_model: 1,
serial_number: 'serial_number'
instrument_model: "UVP5HD",
serial_number: 'serial_number',
contact: { user_id: 1 } as MinimalUserModel,
managers: [{ user_id: 1 } as MinimalUserModel],
members: [{ user_id: 2 } as MinimalUserModel],
}
export const projectRequestCreationModel_6: ProjectRequestCreationModel = {
export const projectRequestCreationModel_6: PublicProjectRequestCreationModel = {
root_folder_path: 'root_folder_path',
project_title: 'julie project_title',
project_acronym: 'project_acronym',
Expand All @@ -133,11 +172,14 @@ export const projectRequestCreationModel_6: ProjectRequestCreationModel = {
privacy_duration: 1,
visible_duration: 1,
public_duration: 1,
instrument_model: 2,
serial_number: 'serial_number'
instrument_model: "UVP5SD",
serial_number: 'serial_number',
contact: { user_id: 1 } as MinimalUserModel,
managers: [{ user_id: 1 } as MinimalUserModel],
members: [{ user_id: 2 } as MinimalUserModel],
}

export const projectRequestCreationModel_withDataToSanitize: ProjectRequestCreationModel = {
export const projectRequestCreationModel_withDataToSanitize: PublicProjectRequestCreationModel = {
root_folder_path: ' root_folder_path',
project_title: ' project_title',
project_acronym: ' project_acronym ',
Expand All @@ -156,11 +198,14 @@ export const projectRequestCreationModel_withDataToSanitize: ProjectRequestCreat
privacy_duration: 1,
visible_duration: 1,
public_duration: 1,
instrument_model: 1,
serial_number: ' serial_number'
instrument_model: "UVP5HD",
serial_number: ' serial_number',
contact: { user_id: 1 } as MinimalUserModel,
managers: [{ user_id: 1 } as MinimalUserModel],
members: [{ user_id: 2 } as MinimalUserModel],
}

export const projectRequestCreationModel_withDataSanitized: ProjectRequestCreationModel = {
export const projectRequestCreationModel_withDataSanitized: PublicProjectRequestCreationModel = {
root_folder_path: 'root_folder_path',
project_title: 'project_title',
project_acronym: 'project_acronym',
Expand All @@ -179,8 +224,11 @@ export const projectRequestCreationModel_withDataSanitized: ProjectRequestCreati
privacy_duration: 1,
visible_duration: 1,
public_duration: 1,
instrument_model: 1,
serial_number: 'serial_number'
instrument_model: "UVP5HD",
serial_number: 'serial_number',
contact: { user_id: 1 } as MinimalUserModel,
managers: [{ user_id: 1 } as MinimalUserModel],
members: [{ user_id: 2 } as MinimalUserModel],
}

export const projectRequestCreationModel_withmissingData = {
Expand All @@ -200,8 +248,10 @@ export const projectRequestCreationModel_withmissingData = {
privacy_duration: 1,
visible_duration: 1,
public_duration: 1,
instrument_model: 1,
serial_number: 'serial_number'
instrument_model: "UVP5HD",
serial_number: 'serial_number',
contact: { user_id: 1 } as MinimalUserModel,
managers: [{ user_id: 1 } as MinimalUserModel]
}

export const projectRequestCreationModel_withmissingOverrideDepthOffset = {
Expand All @@ -222,22 +272,25 @@ export const projectRequestCreationModel_withmissingOverrideDepthOffset = {
privacy_duration: 1,
visible_duration: 1,
public_duration: 1,
instrument_model: 1,
serial_number: 'serial_number'
instrument_model: "UVP5HD",
serial_number: 'serial_number',
contact: { user_id: 1 } as MinimalUserModel,
managers: [{ user_id: 1 } as MinimalUserModel],
members: [{ user_id: 2 } as MinimalUserModel],
}
export const projectResponseModel: ProjectResponseModel = {

export const projectResponseModel: PublicProjectResponseModel = {
...projectRequestCreationModel,
project_id: 1,
project_creation_date: '2024-04-29 15:43:10'
}
export const ProjectResponseModel2: ProjectResponseModel = {
export const projectResponseModel2: PublicProjectResponseModel = {
...projectRequestCreationModel,
project_id: 2,
project_creation_date: '2024-04-30 12:15:11'
}


export const projectResponseModelArray: ProjectResponseModel[] = [projectResponseModel, ProjectResponseModel2]
export const projectResponseModelArray: PublicProjectResponseModel[] = [projectResponseModel, projectResponseModel2]

export const projectRequestModel: ProjectRequestModel = {
project_id: 1
Expand Down
8 changes: 7 additions & 1 deletion test/mocks/project-mock.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
import { ProjectResponseModel } from "../../src/domain/entities/project";
import { ProjectRequestCreationModel, ProjectResponseModel, PublicProjectResponseModel } from "../../src/domain/entities/project";
import { SearchResult } from "../../src/domain/entities/search";
import { ProjectRepository } from "../../src/domain/interfaces/repositories/project-repository"

export class MockProjectRepository implements ProjectRepository {
formatProjectRequestCreationModel(): ProjectRequestCreationModel {
throw new Error("Method not implemented.");
}
toPublicProject(): PublicProjectResponseModel {
throw new Error("Method not implemented.");
}
standardUpdateProject(): Promise<number> {
throw new Error("Method not implemented : standardUpdateProject");
}
Expand Down
Loading

0 comments on commit 51165ee

Please sign in to comment.