Skip to content

Commit a11bb7c

Browse files
authored
Merge pull request #10 from fasenderos/remove-userId-base-entity
chore(api-gateway): remove userId from base entity
2 parents cdaccc7 + 5913d17 commit a11bb7c

File tree

11 files changed

+54
-66
lines changed

11 files changed

+54
-66
lines changed

packages/api-gateway/src/activities/entities/activity.entity.ts

+3
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ import { Collections } from '../../common/constants';
55
@Entity({ name: Collections.ACTIVITIES })
66
@Index(`index_${Collections.ACTIVITIES}_on_userId`, ['userId'])
77
export class Activity extends BaseEntity {
8+
@Column({ type: 'uuid' })
9+
userId!: string;
10+
811
@Column()
912
userIP!: string;
1013

packages/api-gateway/src/api-keys/api-keys.service.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { Injectable } from '@nestjs/common';
22
import { BaseService } from '../base/base.service';
33
import { ApiKey, ApiKeyType } from './entities/api-key.entity';
4-
import { Repository } from 'typeorm';
4+
import { Repository, UpdateResult } from 'typeorm';
55
import { InjectRepository } from '@nestjs/typeorm';
66
import { CreateApiKeyDto } from './dto/create-api-key.dto';
77
import { UpdateApiKeyDto } from './dto/update-api-key.dto';
@@ -45,16 +45,16 @@ export class ApiKeysService extends BaseService<
4545
return apikey;
4646
}
4747

48-
override async updateById(
48+
override updateById(
4949
id: string,
5050
data: UpdateApiKeyDto,
5151
userId?: string,
52-
): Promise<void> {
52+
): Promise<UpdateResult> {
5353
const update: QueryDeepPartialEntity<ApiKey> = { ...data };
5454
// If userIps is `null`, means that user have
5555
// removed the IPs, so we have to update the apikey expiration
5656
if (data.userIps === null) this.setExpiration(update);
57-
await this.repo.update(
57+
return this.repo.update(
5858
{
5959
id,
6060
...(userId ? { userId: userId } : /* istanbul ignore next */ {}),

packages/api-gateway/src/api-keys/entities/api-key.entity.ts

+3
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ export enum ApiKeyType {
1111
@Entity({ name: Collections.APIKEYS })
1212
@Index(`index_${Collections.APIKEYS}_on_userId`, ['userId'])
1313
export class ApiKey extends BaseEntity {
14+
@Column({ type: 'uuid' })
15+
userId!: string;
16+
1417
@Column({ unique: true })
1518
public!: string;
1619

packages/api-gateway/src/base/base.controller.ts

+6-19
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ import { SanitizeTrimPipe } from '../common/pipes/sanitize-trim.pipe';
3434
* updated and delete an entity protected by the Role Base ACL and the api documentaion.
3535
*/
3636
export function ControllerFactory<
37-
Entity extends BaseEntity,
37+
Entity extends BaseEntity & { userId?: string },
3838
CreateDTO extends DeepPartial<Entity>,
3939
UpdateDTO extends QueryDeepPartialEntity<Entity>,
4040
>(
@@ -64,7 +64,7 @@ export function ControllerFactory<
6464
);
6565

6666
class BaseController<
67-
Entity extends BaseEntity,
67+
Entity extends BaseEntity & { userId?: string },
6868
CreateDTO extends DeepPartial<Entity>,
6969
UpdateDTO extends QueryDeepPartialEntity<Entity>,
7070
> implements IBaseController<Entity, CreateDTO, UpdateDTO>
@@ -149,8 +149,6 @@ export function ControllerFactory<
149149
id,
150150
userId: user.id,
151151
} as unknown as FindOptionsWhere<Entity>);
152-
// We don't wont to give much info, so always return not found
153-
// even if the user is trying to get a resource of another user
154152
if (!entity) throw new NotFoundException();
155153

156154
return entity;
@@ -224,14 +222,9 @@ export function ControllerFactory<
224222
@Body() dto: UpdateDTO,
225223
@CurrentUser() user: User,
226224
): Promise<void> {
227-
const entity = await this.service.findById(id);
228-
// We don't wont to give much info, so always return not found
229-
// even if the user is trying to update a resource of another user
230-
if (!entity || (entity.userId && entity.userId !== user.id))
231-
throw new NotFoundException();
232-
233225
// Update owned resource
234-
await this.service.updateById(id, dto, user.id);
226+
const result = await this.service.updateById(id, dto, user.id);
227+
if (result.affected === 0) throw new NotFoundException();
235228
}
236229

237230
/**
@@ -266,15 +259,9 @@ export function ControllerFactory<
266259
@Param('id', ParseUUIDPipe) id: string,
267260
@CurrentUser() user: User,
268261
): Promise<void> {
269-
const entity = await this.service.findById(id);
270-
271-
// We don't wont to give much info, so always return not found
272-
// even if the user is trying to delete a resource of another user
273-
if (!entity || (entity.userId && entity.userId !== user.id))
274-
throw new NotFoundException();
275-
276262
// Delete owned resource
277-
await this.service.deleteById(id, user.id);
263+
const result = await this.service.deleteById(id, user.id);
264+
if (result.affected === 0) throw new NotFoundException();
278265
}
279266
}
280267

packages/api-gateway/src/base/base.entity.ts

-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import {
2-
Column,
32
CreateDateColumn,
43
DeleteDateColumn,
54
PrimaryGeneratedColumn,
@@ -10,9 +9,6 @@ export abstract class BaseEntity {
109
@PrimaryGeneratedColumn('uuid')
1110
id!: string;
1211

13-
@Column({ type: 'uuid' })
14-
userId!: string;
15-
1612
@CreateDateColumn({ type: 'timestamptz', default: () => 'CURRENT_TIMESTAMP' })
1713
createdAt!: Date;
1814

packages/api-gateway/src/base/base.service.ts

+12-10
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import {
22
DeepPartial,
3+
DeleteResult,
34
FindManyOptions,
45
FindOptionsWhere,
56
Repository,
7+
UpdateResult,
68
} from 'typeorm';
79
import { QueryDeepPartialEntity } from 'typeorm/query-builder/QueryPartialEntity';
810
import { BaseEntity } from './base.entity';
@@ -82,15 +84,15 @@ export abstract class BaseService<
8284
* @param {FindOptionsWhere<Entity>} filter The matching conditions for updating
8385
* @param {UpdateDTO} data The payload to update the entity
8486
*/
85-
async update(
87+
update(
8688
filter: FindOptionsWhere<Entity>,
8789
data: UpdateDTO,
88-
): Promise<void> {
90+
): Promise<UpdateResult> {
8991
// @ts-expect-error Dto should not have userId, but we check anyway at runtime
9092
if (data.userId)
9193
throw new UnprocessableEntityException('Ownership can not be changed');
9294

93-
await this.repo.update(filter, data);
95+
return this.repo.update(filter, data);
9496
}
9597

9698
/**
@@ -100,16 +102,16 @@ export abstract class BaseService<
100102
* @param {UpdateDTO} data The payload to update the entity
101103
* @param {string} userId The userId of the user owner of the resource
102104
*/
103-
async updateById(
105+
updateById(
104106
id: string,
105107
data: UpdateDTO,
106108
userId?: string,
107-
): Promise<void> {
109+
): Promise<UpdateResult> {
108110
// @ts-expect-error Dto should not have userId, but we check anyway at runtime
109111
if (data.userId)
110112
throw new UnprocessableEntityException('Ownership can not be changed');
111113

112-
await this.repo.update(
114+
return this.repo.update(
113115
{
114116
id,
115117
...(userId ? { userId: userId } : {}),
@@ -125,8 +127,8 @@ export abstract class BaseService<
125127
* @param {FindOptionsWhere<Entity>} filter The matching conditions for updating
126128
* @param {boolean} soft When true a soft delete is performed otherwise a real delete.
127129
*/
128-
async delete(filter: FindOptionsWhere<Entity>, soft = true): Promise<void> {
129-
await this.repo[soft ? 'softDelete' : 'delete'](filter);
130+
delete(filter: FindOptionsWhere<Entity>, soft = true): Promise<DeleteResult> {
131+
return this.repo[soft ? 'softDelete' : 'delete'](filter);
130132
}
131133

132134
/**
@@ -137,8 +139,8 @@ export abstract class BaseService<
137139
* @param {string} userId The userId of the user owner of the resource
138140
* @param {boolean} soft When true a soft delete is performed otherwise a real delete.
139141
*/
140-
async deleteById(id: string, userId?: string, soft = true): Promise<void> {
141-
await this.repo[soft ? 'softDelete' : 'delete']({
142+
deleteById(id: string, userId?: string, soft = true): Promise<DeleteResult> {
143+
return this.repo[soft ? 'softDelete' : 'delete']({
142144
id,
143145
...(userId ? { userId: userId } : {}),
144146
} as FindOptionsWhere<Entity>);

packages/api-gateway/src/base/interfaces/base-controller.interface.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { QueryDeepPartialEntity } from 'typeorm/query-builder/QueryPartialEntity
44
import { User } from '../../users/entities/user.entity';
55

66
export interface IBaseController<
7-
T extends BaseEntity,
7+
T extends BaseEntity & { userId?: string },
88
C extends DeepPartial<T>,
99
U extends QueryDeepPartialEntity<T>,
1010
> {
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,15 @@
1-
import { DeepPartial, FindManyOptions, FindOptionsWhere } from 'typeorm';
1+
import {
2+
DeepPartial,
3+
DeleteResult,
4+
FindManyOptions,
5+
FindOptionsWhere,
6+
UpdateResult,
7+
} from 'typeorm';
28
import { BaseEntity } from '../base.entity';
39
import { QueryDeepPartialEntity } from 'typeorm/query-builder/QueryPartialEntity';
410

511
export interface IBaseService<
6-
T extends BaseEntity,
12+
T extends BaseEntity & { userId?: string },
713
C extends DeepPartial<T>,
814
U extends QueryDeepPartialEntity<T>,
915
> {
@@ -12,8 +18,12 @@ export interface IBaseService<
1218
find(options?: FindManyOptions<T>): Promise<T[]>;
1319
findOne(filter: FindOptionsWhere<T>, unselected?: boolean): Promise<T | null>;
1420
findById(id: string, unselected?: boolean): Promise<T | null>;
15-
update(filter: FindOptionsWhere<T>, data: U): Promise<void>;
16-
updateById(id: string, data: U, userId?: string): Promise<void>;
17-
delete(filter: FindOptionsWhere<T>, soft?: boolean): Promise<void>;
18-
deleteById(id: string, userId?: string, soft?: boolean): Promise<void>;
21+
update(filter: FindOptionsWhere<T>, data: U): Promise<UpdateResult>;
22+
updateById(id: string, data: U, userId?: string): Promise<UpdateResult>;
23+
delete(filter: FindOptionsWhere<T>, soft?: boolean): Promise<DeleteResult>;
24+
deleteById(
25+
id: string,
26+
userId?: string,
27+
soft?: boolean,
28+
): Promise<DeleteResult>;
1929
}

packages/api-gateway/src/profiles/entities/profile.entity.ts

+3
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ import { Collections } from '../../common/constants';
55
@Entity({ name: Collections.PROFILES })
66
@Index(`index_${Collections.PROFILES}_on_userId`, ['userId'])
77
export class Profile extends BaseEntity {
8+
@Column({ type: 'uuid' })
9+
userId!: string;
10+
811
@Column('varchar', { nullable: true })
912
firstName!: string | null;
1013

packages/api-gateway/src/recovery-tokens/entities/recovery-token.entity.ts

+3
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ import { Collections } from '../../common/constants';
55
@Entity({ name: Collections.RECOVERY_TOKENS })
66
@Index(`index_${Collections.RECOVERY_TOKENS}_on_userId`, ['userId'])
77
export class RecoveryToken extends BaseEntity {
8+
@Column({ type: 'uuid' })
9+
userId!: string;
10+
811
@Column()
912
token!: string;
1013

packages/api-gateway/src/users/entities/user.entity.ts

+3-22
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,10 @@
1-
import {
2-
Column,
3-
CreateDateColumn,
4-
DeleteDateColumn,
5-
Entity,
6-
Index,
7-
PrimaryGeneratedColumn,
8-
UpdateDateColumn,
9-
} from 'typeorm';
1+
import { Column, Entity, Index } from 'typeorm';
102
import { Collections, UserState } from '../../common/constants';
113
import { UserRole } from '../../app.roles';
4+
import { BaseEntity } from '../../base/base.entity';
125

136
@Entity({ name: Collections.USERS })
14-
export class User {
15-
@PrimaryGeneratedColumn('uuid')
16-
id!: string;
17-
7+
export class User extends BaseEntity {
188
@Index('index_users_on_email', { unique: true })
199
@Column()
2010
email!: string;
@@ -58,13 +48,4 @@ export class User {
5848

5949
@Column('timestamptz', { nullable: true, select: false })
6050
verifyExpire!: Date | null;
61-
62-
@CreateDateColumn({ type: 'timestamptz', default: () => 'CURRENT_TIMESTAMP' })
63-
createdAt!: Date;
64-
65-
@UpdateDateColumn({ type: 'timestamptz', default: () => 'CURRENT_TIMESTAMP' })
66-
updatedAt!: Date;
67-
68-
@DeleteDateColumn({ type: 'timestamptz', nullable: true })
69-
deletedAt!: Date | null;
7051
}

0 commit comments

Comments
 (0)