-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathproject-service.ts
More file actions
165 lines (142 loc) · 4.39 KB
/
project-service.ts
File metadata and controls
165 lines (142 loc) · 4.39 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
import { NotFoundError } from "routing-controllers";
import { Inject, Service, Token } from "typedi";
import { Repository } from "typeorm";
import { IService } from ".";
import { DatabaseServiceToken, IDatabaseService } from "./database-service";
import { Project } from "../entities/project";
import { Settings } from "../entities/settings";
import { Team } from "../entities/team";
import { User } from "../entities/user";
import { UserRole } from "../entities/user-role";
/**
* An interface describing user handling.
*/
export interface IProjectService extends IService {
/**
* Get all projects
*/
getAllProjects(user: User): Promise<readonly Project[]>;
/**
* Create new project
*/
createProject(project: Project): Promise<Project>;
/**
* Update project
*/
updateProject(project: Project, user: User): Promise<Project>;
/**
* Get project by id
*/
getProjectByID(id: number): Promise<Project | undefined>;
/**
* Delete single project by id
*/
deleteProjectByID(id: number, currentUserId: User): Promise<void>;
}
/**
* A token used to inject a concrete user service.
*/
export const ProjectServiceToken = new Token<IProjectService>();
/**
* A service to handle users.
*/
@Service(ProjectServiceToken)
export class ProjectService implements IProjectService {
private _projects!: Repository<Project>;
private _teams!: Repository<Team>;
private _settings!: Repository<Settings>;
public constructor(
@Inject(DatabaseServiceToken) private readonly _database: IDatabaseService,
) {}
/**
* Sets up the project service.
*/
public async bootstrap(): Promise<void> {
this._projects = this._database.getRepository(Project);
this._teams = this._database.getRepository(Team);
this._settings = this._database.getRepository(Settings);
}
/**
* Gets all projects that the user may see.
*/
public async getAllProjects(user: User): Promise<readonly Project[]> {
const teams = await this._teams.find();
const teamIds = teams
.filter((team) => team.users.includes(user.id.toString()))
.map((team) => team.id);
const [settings] = await this._settings.find();
const allowRatingProjects = settings.project.allowRatingProjects;
const isAdmin = user.role === UserRole.Root;
const projects = await this._projects.find();
return projects.filter((project) => {
return (
isAdmin ||
(project.allowRating && allowRatingProjects) ||
teamIds.includes(project.team.id)
);
});
}
/**
* Updates a project.
* @param project The project to update
*/
public async updateProject(project: Project, user: User): Promise<Project> {
const existing = await this._projects.findOneBy({ id: project.id });
if (!existing) {
throw new NotFoundError();
}
await this.checkPermission(existing, user);
existing.title = project.title;
existing.description = project.description;
existing.image = project.image;
// Only admins may change allowRating
if (user.role === UserRole.Root) {
existing.allowRating = project.allowRating;
}
return this._projects.save(existing);
}
/**
* Creates a project.
* @param project The project to create
*/
public async createProject(project: Project): Promise<Project> {
return this._projects.save(project);
}
/**
* Gets a project by its id.
* @param id The id of the project
*/
public async getProjectByID(id: number): Promise<Project | undefined> {
const project = await this._projects.findOneBy({ id });
return project || undefined;
}
/**
* Deletes a project by its id.
* @param id The id of the project
*/
public async deleteProjectByID(
id: number,
currentUserId: User,
): Promise<void> {
const project = await this._projects.findOneBy({ id });
if (!project) {
throw new NotFoundError();
}
await this.checkPermission(project, currentUserId);
await this._projects.delete(id);
return Promise.resolve();
}
/**
* Throw errors if the user is not allowed to modify/access the project.
*/
private async checkPermission(project: Project, user: User): Promise<void> {
if (user.role === UserRole.Root) {
return;
}
const team = project.team;
if (!team || !team.users.includes(user.id.toString())) {
// Tried to access a project belonging to a different team, forbidden
throw new NotFoundError();
}
}
}