@forklaunch/implementation-iam-base 0.2.0 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,9 @@
1
1
  import { IdDto, InstanceTypeRecord } from '@forklaunch/common';
2
2
  import {
3
+ evaluateTelemetryOptions,
3
4
  MetricsDefinition,
4
- OpenTelemetryCollector
5
+ OpenTelemetryCollector,
6
+ TelemetryOptions
5
7
  } from '@forklaunch/core/http';
6
8
  import {
7
9
  InternalDtoMapper,
@@ -66,6 +68,11 @@ export class BaseOrganizationService<
66
68
  Entities,
67
69
  Dto
68
70
  >;
71
+ private evaluatedTelemetryOptions: {
72
+ logging?: boolean;
73
+ metrics?: boolean;
74
+ tracing?: boolean;
75
+ };
69
76
 
70
77
  constructor(
71
78
  public em: EntityManager,
@@ -87,23 +94,43 @@ export class BaseOrganizationService<
87
94
  Dto['UpdateOrganizationDtoMapper'],
88
95
  Entities['UpdateOrganizationDtoMapper']
89
96
  >;
97
+ },
98
+ options?: {
99
+ telemetry?: TelemetryOptions;
90
100
  }
91
101
  ) {
92
102
  this.#mappers = transformIntoInternalDtoMapper(mappers, schemaValidator);
103
+ this.evaluatedTelemetryOptions = options?.telemetry
104
+ ? evaluateTelemetryOptions(options.telemetry).enabled
105
+ : {
106
+ logging: false,
107
+ metrics: false,
108
+ tracing: false
109
+ };
93
110
  }
94
111
 
95
112
  async createOrganization(
96
113
  organizationDto: Dto['CreateOrganizationDtoMapper'],
97
114
  em?: EntityManager
98
115
  ): Promise<Dto['OrganizationDtoMapper']> {
99
- this.openTelemetryCollector.log('info', 'Creating organization');
116
+ if (this.evaluatedTelemetryOptions.logging) {
117
+ this.openTelemetryCollector.info(
118
+ 'Creating organization',
119
+ organizationDto
120
+ );
121
+ }
122
+
100
123
  const organization =
101
124
  await this.#mappers.CreateOrganizationDtoMapper.deserializeDtoToEntity(
102
- organizationDto
125
+ organizationDto,
126
+ em ?? this.em
103
127
  );
104
- await (em ?? this.em).transactional(async (innerEm) => {
105
- await innerEm.persist(organization);
106
- });
128
+
129
+ if (em) {
130
+ await em.persist(organization);
131
+ } else {
132
+ await this.em.persistAndFlush(organization);
133
+ }
107
134
 
108
135
  return this.#mappers.OrganizationDtoMapper.serializeEntityToDto(
109
136
  organization
@@ -114,10 +141,15 @@ export class BaseOrganizationService<
114
141
  idDto: IdDto,
115
142
  em?: EntityManager
116
143
  ): Promise<Dto['OrganizationDtoMapper']> {
144
+ if (this.evaluatedTelemetryOptions.logging) {
145
+ this.openTelemetryCollector.info('Getting organization', idDto);
146
+ }
147
+
117
148
  const organization = await (em ?? this.em).findOneOrFail(
118
149
  'Organization',
119
150
  idDto
120
151
  );
152
+
121
153
  return this.#mappers.OrganizationDtoMapper.serializeEntityToDto(
122
154
  organization as Entities['OrganizationDtoMapper']
123
155
  );
@@ -127,17 +159,39 @@ export class BaseOrganizationService<
127
159
  organizationDto: Dto['UpdateOrganizationDtoMapper'],
128
160
  em?: EntityManager
129
161
  ): Promise<Dto['OrganizationDtoMapper']> {
162
+ if (this.evaluatedTelemetryOptions.logging) {
163
+ this.openTelemetryCollector.info(
164
+ 'Updating organization',
165
+ organizationDto
166
+ );
167
+ }
168
+
130
169
  const updatedOrganization =
131
170
  await this.#mappers.UpdateOrganizationDtoMapper.deserializeDtoToEntity(
132
- organizationDto
171
+ organizationDto,
172
+ em ?? this.em
133
173
  );
134
- await (em ?? this.em).upsert(updatedOrganization);
174
+
175
+ if (em) {
176
+ await em.persist(updatedOrganization);
177
+ } else {
178
+ await this.em.persistAndFlush(updatedOrganization);
179
+ }
180
+
135
181
  return this.#mappers.OrganizationDtoMapper.serializeEntityToDto(
136
182
  updatedOrganization
137
183
  );
138
184
  }
139
185
 
140
186
  async deleteOrganization(idDto: IdDto, em?: EntityManager): Promise<void> {
141
- await (em ?? this.em).nativeDelete('Organization', idDto);
187
+ if (this.evaluatedTelemetryOptions.logging) {
188
+ this.openTelemetryCollector.info('Deleting organization', idDto);
189
+ }
190
+
191
+ if (em) {
192
+ await em.nativeDelete('Organization', idDto);
193
+ } else {
194
+ await this.em.nativeDelete('Organization', idDto);
195
+ }
142
196
  }
143
197
  }
@@ -5,8 +5,10 @@ import {
5
5
 
6
6
  import { IdDto, IdsDto, InstanceTypeRecord } from '@forklaunch/common';
7
7
  import {
8
+ evaluateTelemetryOptions,
8
9
  MetricsDefinition,
9
- OpenTelemetryCollector
10
+ OpenTelemetryCollector,
11
+ TelemetryOptions
10
12
  } from '@forklaunch/core/http';
11
13
  import {
12
14
  InternalDtoMapper,
@@ -18,8 +20,8 @@ import { MapNestedDtoArraysToCollections } from '@forklaunch/core/services';
18
20
  import {
19
21
  CreatePermissionDto,
20
22
  PermissionDto,
21
- RoleDto,
22
- UpdatePermissionDto
23
+ UpdatePermissionDto,
24
+ UpdateRoleDto
23
25
  } from '@forklaunch/interfaces-iam/types';
24
26
  import { AnySchemaValidator } from '@forklaunch/validator';
25
27
  import { EntityManager } from '@mikro-orm/core';
@@ -31,23 +33,29 @@ export class BasePermissionService<
31
33
  PermissionDtoMapper: PermissionDto;
32
34
  CreatePermissionDtoMapper: CreatePermissionDto;
33
35
  UpdatePermissionDtoMapper: UpdatePermissionDto;
34
- RoleDtoMapper: RoleDto;
36
+ RoleEntityMapper: UpdateRoleDto;
35
37
  } = {
36
38
  PermissionDtoMapper: PermissionDto;
37
39
  CreatePermissionDtoMapper: CreatePermissionDto;
38
40
  UpdatePermissionDtoMapper: UpdatePermissionDto;
39
- RoleDtoMapper: RoleDto;
41
+ RoleEntityMapper: UpdateRoleDto;
40
42
  },
41
43
  Entities extends {
42
44
  PermissionDtoMapper: PermissionDto;
43
45
  CreatePermissionDtoMapper: PermissionDto;
44
46
  UpdatePermissionDtoMapper: PermissionDto;
45
- RoleDtoMapper: MapNestedDtoArraysToCollections<RoleDto, 'permissions'>;
47
+ RoleEntityMapper: MapNestedDtoArraysToCollections<
48
+ UpdateRoleDto,
49
+ 'permissions'
50
+ >;
46
51
  } = {
47
52
  PermissionDtoMapper: PermissionDto;
48
53
  CreatePermissionDtoMapper: PermissionDto;
49
54
  UpdatePermissionDtoMapper: PermissionDto;
50
- RoleDtoMapper: MapNestedDtoArraysToCollections<RoleDto, 'permissions'>;
55
+ RoleEntityMapper: MapNestedDtoArraysToCollections<
56
+ UpdateRoleDto,
57
+ 'permissions'
58
+ >;
51
59
  }
52
60
  > implements PermissionService
53
61
  {
@@ -56,6 +64,11 @@ export class BasePermissionService<
56
64
  Entities,
57
65
  Dto
58
66
  >;
67
+ private evaluatedTelemetryOptions: {
68
+ logging?: boolean;
69
+ metrics?: boolean;
70
+ tracing?: boolean;
71
+ };
59
72
 
60
73
  constructor(
61
74
  public em: EntityManager,
@@ -78,21 +91,31 @@ export class BasePermissionService<
78
91
  Dto['UpdatePermissionDtoMapper'],
79
92
  Entities['UpdatePermissionDtoMapper']
80
93
  >;
81
- RoleDtoMapper: RequestDtoMapperConstructor<
94
+ RoleEntityMapper: RequestDtoMapperConstructor<
82
95
  SchemaValidator,
83
- Dto['RoleDtoMapper'],
84
- Entities['RoleDtoMapper']
96
+ Dto['RoleEntityMapper'],
97
+ Entities['RoleEntityMapper']
85
98
  >;
99
+ },
100
+ options?: {
101
+ telemetry?: TelemetryOptions;
86
102
  }
87
103
  ) {
88
104
  this.#mappers = transformIntoInternalDtoMapper(mappers, schemaValidator);
105
+ this.evaluatedTelemetryOptions = options?.telemetry
106
+ ? evaluateTelemetryOptions(options.telemetry).enabled
107
+ : {
108
+ logging: false,
109
+ metrics: false,
110
+ tracing: false
111
+ };
89
112
  }
90
113
 
91
114
  // start: global helper functions
92
115
  private async updateRolesWithPermissions(
93
- roles: Entities['RoleDtoMapper'][],
116
+ roles: Entities['RoleEntityMapper'][],
94
117
  permissions: Entities['PermissionDtoMapper'][]
95
- ): Promise<Entities['RoleDtoMapper'][]> {
118
+ ): Promise<Entities['RoleEntityMapper'][]> {
96
119
  return Promise.all(
97
120
  roles.map(async (role) => {
98
121
  permissions.forEach((permission) => role.permissions.add(permission));
@@ -102,9 +125,9 @@ export class BasePermissionService<
102
125
  }
103
126
 
104
127
  private async removePermissionsFromRoles(
105
- roles: Entities['RoleDtoMapper'][],
128
+ roles: Entities['RoleEntityMapper'][],
106
129
  permissions: Entities['PermissionDtoMapper'][]
107
- ): Promise<Entities['RoleDtoMapper'][]> {
130
+ ): Promise<Entities['RoleEntityMapper'][]> {
108
131
  return Promise.all(
109
132
  roles.map(async (role) => {
110
133
  permissions.forEach((permission) =>
@@ -118,13 +141,16 @@ export class BasePermissionService<
118
141
  private async getBatchRoles(
119
142
  roleIds?: IdsDto,
120
143
  em?: EntityManager
121
- ): Promise<Entities['RoleDtoMapper'][]> {
144
+ ): Promise<Entities['RoleEntityMapper'][]> {
122
145
  return roleIds
123
146
  ? await Promise.all(
124
147
  (await this.roleServiceFactory().getBatchRoles(roleIds, em)).map(
125
148
  async (role) => {
126
149
  return (em ?? this.em).merge(
127
- await this.#mappers.RoleDtoMapper.deserializeDtoToEntity(role)
150
+ await this.#mappers.RoleEntityMapper.deserializeDtoToEntity(
151
+ role,
152
+ em ?? this.em
153
+ )
128
154
  );
129
155
  }
130
156
  )
@@ -139,12 +165,12 @@ export class BasePermissionService<
139
165
  addToRoles
140
166
  }: {
141
167
  permission: Entities['PermissionDtoMapper'];
142
- addToRoles: Entities['RoleDtoMapper'][];
168
+ addToRoles: Entities['RoleEntityMapper'][];
143
169
  }): Promise<{
144
170
  permission: Entities['PermissionDtoMapper'];
145
- roles: Entities['RoleDtoMapper'][];
171
+ roles: Entities['RoleEntityMapper'][];
146
172
  }> {
147
- let roles: Entities['RoleDtoMapper'][] = [];
173
+ let roles: Entities['RoleEntityMapper'][] = [];
148
174
  if (addToRoles) {
149
175
  roles = await this.updateRolesWithPermissions(addToRoles, [permission]);
150
176
  }
@@ -157,12 +183,13 @@ export class BasePermissionService<
157
183
  em?: EntityManager
158
184
  ): Promise<{
159
185
  permission: Entities['PermissionDtoMapper'];
160
- addToRoles: Entities['RoleDtoMapper'][];
186
+ addToRoles: Entities['RoleEntityMapper'][];
161
187
  }> {
162
188
  return {
163
189
  permission: (em ?? this.em).merge(
164
190
  await this.#mappers.CreatePermissionDtoMapper.deserializeDtoToEntity(
165
- permissionDto
191
+ permissionDto,
192
+ em ?? this.em
166
193
  )
167
194
  ),
168
195
  addToRoles: permissionDto.addToRolesIds
@@ -176,12 +203,22 @@ export class BasePermissionService<
176
203
  createPermissionDto: Dto['CreatePermissionDtoMapper'],
177
204
  em?: EntityManager
178
205
  ): Promise<Dto['PermissionDtoMapper']> {
206
+ if (this.evaluatedTelemetryOptions.logging) {
207
+ this.openTelemetryCollector.info(
208
+ 'Creating permission',
209
+ createPermissionDto
210
+ );
211
+ }
179
212
  const { permission, roles } = await this.createPermissionDto(
180
213
  await this.extractCreatePermissionDtoToEntityData(createPermissionDto, em)
181
214
  );
182
- await (em ?? this.em).transactional(async (innerEm) => {
183
- await innerEm.persist([permission, ...roles]);
184
- });
215
+
216
+ if (em) {
217
+ await em.persist([permission, ...roles]);
218
+ } else {
219
+ await this.em.persistAndFlush([permission, ...roles]);
220
+ }
221
+
185
222
  return this.#mappers.PermissionDtoMapper.serializeEntityToDto(permission);
186
223
  }
187
224
 
@@ -189,37 +226,44 @@ export class BasePermissionService<
189
226
  permissionDtos: Dto['CreatePermissionDtoMapper'][],
190
227
  em?: EntityManager
191
228
  ): Promise<Dto['PermissionDtoMapper'][]> {
192
- const rolesCache: Record<string, Entities['RoleDtoMapper']> = {};
229
+ if (this.evaluatedTelemetryOptions.logging) {
230
+ this.openTelemetryCollector.info(
231
+ 'Creating batch permissions',
232
+ permissionDtos
233
+ );
234
+ }
235
+ const rolesCache: Record<string, Entities['RoleEntityMapper']> = {};
193
236
  const permissions: Entities['PermissionDtoMapper'][] = [];
194
- await (em ?? this.em).transactional(async (em) => {
195
- permissionDtos.map(async (createPermissionDto) => {
196
- const { permission, roles } = await this.createPermissionDto(
197
- await this.extractCreatePermissionDtoToEntityData(
198
- createPermissionDto,
199
- em
200
- )
201
- );
202
- roles.forEach((role) => {
203
- if (
204
- rolesCache[role.id] &&
205
- role.permissions !== rolesCache[role.id].permissions
206
- ) {
207
- role.permissions.getItems().forEach((permission) => {
208
- if (!rolesCache[role.id].permissions.contains(permission)) {
209
- rolesCache[role.id].permissions.add(permission);
210
- }
211
- });
212
- } else {
213
- rolesCache[role.id] = role;
214
- }
215
- });
216
- permissions.push(permission);
237
+ permissionDtos.map(async (createPermissionDto) => {
238
+ const { permission, roles } = await this.createPermissionDto(
239
+ await this.extractCreatePermissionDtoToEntityData(
240
+ createPermissionDto,
241
+ em
242
+ )
243
+ );
244
+ roles.forEach((role) => {
245
+ if (
246
+ rolesCache[role.id] &&
247
+ role.permissions !== rolesCache[role.id].permissions
248
+ ) {
249
+ role.permissions.getItems().forEach((permission) => {
250
+ if (!rolesCache[role.id].permissions.contains(permission)) {
251
+ rolesCache[role.id].permissions.add(permission);
252
+ }
253
+ });
254
+ } else {
255
+ rolesCache[role.id] = role;
256
+ }
217
257
  });
218
- await (em ?? this.em).persist([
219
- ...permissions,
220
- ...Object.values(rolesCache)
221
- ]);
258
+ permissions.push(permission);
222
259
  });
260
+ const entities = [...permissions, ...Object.values(rolesCache)];
261
+
262
+ if (em) {
263
+ await em.persist(entities);
264
+ } else {
265
+ await this.em.persistAndFlush(entities);
266
+ }
223
267
 
224
268
  return Promise.all(
225
269
  permissions.map(async (permission) =>
@@ -232,6 +276,9 @@ export class BasePermissionService<
232
276
  idDto: IdDto,
233
277
  em?: EntityManager
234
278
  ): Promise<Dto['PermissionDtoMapper']> {
279
+ if (this.evaluatedTelemetryOptions.logging) {
280
+ this.openTelemetryCollector.info('Getting permission', idDto);
281
+ }
235
282
  const permission = await (em ?? this.em).findOneOrFail('Permission', idDto);
236
283
  return this.#mappers.PermissionDtoMapper.serializeEntityToDto(
237
284
  permission as Entities['PermissionDtoMapper']
@@ -242,6 +289,9 @@ export class BasePermissionService<
242
289
  idsDto: IdsDto,
243
290
  em?: EntityManager
244
291
  ): Promise<Dto['PermissionDtoMapper'][]> {
292
+ if (this.evaluatedTelemetryOptions.logging) {
293
+ this.openTelemetryCollector.info('Getting batch permissions', idsDto);
294
+ }
245
295
  return Promise.all(
246
296
  (await (em ?? this.em).find('Permission', idsDto)).map((permission) =>
247
297
  this.#mappers.PermissionDtoMapper.serializeEntityToDto(
@@ -257,11 +307,12 @@ export class BasePermissionService<
257
307
  em?: EntityManager
258
308
  ): Promise<{
259
309
  permission: Entities['PermissionDtoMapper'];
260
- roles: Entities['RoleDtoMapper'][];
310
+ roles: Entities['RoleEntityMapper'][];
261
311
  }> => {
262
312
  const permission =
263
313
  await this.#mappers.UpdatePermissionDtoMapper.deserializeDtoToEntity(
264
- permissionDto
314
+ permissionDto,
315
+ em ?? this.em
265
316
  );
266
317
  const addToRoles = permissionDto.addToRolesIds
267
318
  ? await this.getBatchRoles({ ids: permissionDto.addToRolesIds }, em)
@@ -270,7 +321,7 @@ export class BasePermissionService<
270
321
  ? await this.getBatchRoles({ ids: permissionDto.removeFromRolesIds }, em)
271
322
  : [];
272
323
 
273
- let roles: Entities['RoleDtoMapper'][] = [];
324
+ let roles: Entities['RoleEntityMapper'][] = [];
274
325
 
275
326
  roles = roles.concat(
276
327
  await this.updateRolesWithPermissions(addToRoles, [permission])
@@ -290,11 +341,18 @@ export class BasePermissionService<
290
341
  permissionDto: Dto['UpdatePermissionDtoMapper'],
291
342
  em?: EntityManager
292
343
  ): Promise<Dto['PermissionDtoMapper']> {
344
+ if (this.evaluatedTelemetryOptions.logging) {
345
+ this.openTelemetryCollector.info('Updating permission', permissionDto);
346
+ }
293
347
  const { permission, roles } = await this.updatePermissionDto(permissionDto);
294
- await (em ?? this.em).upsertMany([permission, ...roles]);
295
- if (!em) {
296
- this.em.flush();
348
+ const entities = await (em ?? this.em).upsertMany([permission, ...roles]);
349
+
350
+ if (em) {
351
+ await em.persist(entities);
352
+ } else {
353
+ await this.em.persistAndFlush(entities);
297
354
  }
355
+
298
356
  return this.#mappers.PermissionDtoMapper.serializeEntityToDto(permission);
299
357
  }
300
358
 
@@ -302,7 +360,13 @@ export class BasePermissionService<
302
360
  permissionDtos: Dto['UpdatePermissionDtoMapper'][],
303
361
  em?: EntityManager
304
362
  ): Promise<Dto['PermissionDtoMapper'][]> {
305
- const rolesCache: Record<string, Entities['RoleDtoMapper']> = {};
363
+ if (this.evaluatedTelemetryOptions.logging) {
364
+ this.openTelemetryCollector.info(
365
+ 'Updating batch permissions',
366
+ permissionDtos
367
+ );
368
+ }
369
+ const rolesCache: Record<string, Entities['RoleEntityMapper']> = {};
306
370
  const permissions: Entities['PermissionDtoMapper'][] = [];
307
371
  await (em ?? this.em).transactional(async (em) => {
308
372
  permissionDtos.map(async (updatePermissionDto) => {
@@ -324,10 +388,13 @@ export class BasePermissionService<
324
388
  });
325
389
  permissions.push(permission);
326
390
  });
327
- await (em ?? this.em).persist([
328
- ...permissions,
329
- ...Object.values(rolesCache)
330
- ]);
391
+ const entities = [...permissions, ...Object.values(rolesCache)];
392
+
393
+ if (em) {
394
+ await em.persist(entities);
395
+ } else {
396
+ await this.em.persistAndFlush(entities);
397
+ }
331
398
  });
332
399
 
333
400
  return Promise.all(
@@ -338,6 +405,9 @@ export class BasePermissionService<
338
405
  }
339
406
 
340
407
  async deletePermission(idDto: IdDto, em?: EntityManager): Promise<void> {
408
+ if (this.evaluatedTelemetryOptions.logging) {
409
+ this.openTelemetryCollector.info('Deleting permission', idDto);
410
+ }
341
411
  await (em ?? this.em).nativeDelete('Permission', idDto);
342
412
  }
343
413
 
@@ -345,6 +415,9 @@ export class BasePermissionService<
345
415
  idsDto: IdsDto,
346
416
  em?: EntityManager
347
417
  ): Promise<void> {
418
+ if (this.evaluatedTelemetryOptions.logging) {
419
+ this.openTelemetryCollector.info('Deleting batch permissions', idsDto);
420
+ }
348
421
  await (em ?? this.em).nativeDelete('Permission', {
349
422
  id: { $in: idsDto.ids }
350
423
  });