• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

alkem-io / server / #8050

16 Aug 2024 11:21AM UTC coverage: 13.92%. First build
#8050

Pull #4411

travis-ci

Pull Request #4411: Type added to authorization policy entity

80 of 4158 branches covered (1.92%)

Branch coverage included in aggregate %.

61 of 116 new or added lines in 50 files covered. (52.59%)

1945 of 10389 relevant lines covered (18.72%)

3.01 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

12.69
/src/domain/space/account/account.service.ts
1
import { LogContext } from '@common/enums';
2✔
2
import {
2✔
3
  EntityNotFoundException,
4
  EntityNotInitializedException,
5
  RelationshipNotFoundException,
6
  ValidationException,
7
} from '@common/exceptions';
8
import { Inject, Injectable, LoggerService } from '@nestjs/common';
2✔
9
import { InjectRepository } from '@nestjs/typeorm';
2✔
10
import { FindManyOptions, FindOneOptions, Repository } from 'typeorm';
2✔
11
import { WINSTON_MODULE_NEST_PROVIDER } from 'nest-winston';
2✔
12
import { Account } from './account.entity';
2✔
13
import { IAccount } from './account.interface';
14
import { AgentService } from '@domain/agent/agent/agent.service';
2✔
15
import { SpaceService } from '../space/space.service';
16
import { AgentInfo } from '@core/authentication.agent.info/agent.info';
2✔
17
import { ISpace } from '../space/space.interface';
2✔
18
import { CreateVirtualContributorOnAccountInput } from './dto/account.dto.create.virtual.contributor';
19
import { IVirtualContributor } from '@domain/community/virtual-contributor/virtual.contributor.interface';
20
import { VirtualContributorService } from '@domain/community/virtual-contributor/virtual.contributor.service';
2✔
21
import { StorageAggregatorService } from '@domain/storage/storage-aggregator/storage.aggregator.service';
22
import { CreateSpaceOnAccountInput } from './dto/account.dto.create.space';
23
import { CreateInnovationHubOnAccountInput } from './dto/account.dto.create.innovation.hub';
24
import { IInnovationHub } from '@domain/innovation-hub/innovation.hub.interface';
2✔
25
import { InnovationHubService } from '@domain/innovation-hub/innovation.hub.service';
26
import { MAX_SPACE_LEVEL, SpaceLevel } from '@common/enums/space.level';
2✔
27
import { InnovationPackService } from '@library/innovation-pack/innovation.pack.service';
28
import { CreateInnovationPackOnAccountInput } from './dto/account.dto.create.innovation.pack';
29
import { IInnovationPack } from '@library/innovation-pack/innovation.pack.interface';
2✔
30
import { IStorageAggregator } from '@domain/storage/storage-aggregator/storage.aggregator.interface';
31
import { NamingService } from '@services/infrastructure/naming/naming.service';
32
import { InnovationPackAuthorizationService } from '@library/innovation-pack/innovation.pack.service.authorization';
2✔
33
import { InnovationHubAuthorizationService } from '@domain/innovation-hub/innovation.hub.service.authorization';
2✔
34
import { AuthorizationPolicyService } from '@domain/common/authorization-policy/authorization.policy.service';
2✔
35
import { AccountHostService } from '../account.host/account.host.service';
2✔
36
import { IAccountSubscription } from './account.license.subscription.interface';
2✔
37
import { LicensingCredentialBasedCredentialType } from '@common/enums/licensing.credential.based.credential.type';
38
import { LicenseService } from '@domain/common/license/license.service';
2✔
39
import { InstrumentService } from '@src/apm/decorators';
2✔
40
import { AccountType } from '@common/enums/account.type';
41
import { AccountLookupService } from '../account.lookup/account.lookup.service';
42
import { CreateCalloutInput } from '@domain/collaboration/callout/dto/callout.dto.create';
2✔
43
import { PlatformTemplatesService } from '@platform/platform-templates/platform.templates.service';
44
import { TemplateDefaultType } from '@common/enums/template.default.type';
45
import { IRoleSet } from '@domain/access/role-set';
2✔
46
import { IAgent } from '@domain/agent';
2✔
47

2✔
48
@InstrumentService()
49
@Injectable()
50
export class AccountService {
51
  constructor(
2✔
52
    private accountHostService: AccountHostService,
2✔
53
    private accountLookupService: AccountLookupService,
2✔
54
    private authorizationPolicyService: AuthorizationPolicyService,
2✔
55
    private spaceService: SpaceService,
2✔
56
    private agentService: AgentService,
2✔
57
    private storageAggregatorService: StorageAggregatorService,
2✔
58
    private virtualContributorService: VirtualContributorService,
59
    private innovationHubService: InnovationHubService,
60
    private innovationHubAuthorizationService: InnovationHubAuthorizationService,
2✔
61
    private innovationPackService: InnovationPackService,
62
    private innovationPackAuthorizationService: InnovationPackAuthorizationService,
×
63
    private namingService: NamingService,
×
64
    private licenseService: LicenseService,
×
65
    private platformTemplatesService: PlatformTemplatesService,
×
66
    @InjectRepository(Account)
×
67
    private accountRepository: Repository<Account>,
×
68
    @Inject(WINSTON_MODULE_NEST_PROVIDER) private readonly logger: LoggerService
×
69
  ) {}
×
70

×
71
  async createSpaceOnAccount(
×
72
    spaceData: CreateSpaceOnAccountInput,
×
73
    agentInfo: AgentInfo
×
74
  ): Promise<ISpace> {
×
75
    const account = await this.getAccountOrFail(spaceData.accountID, {
×
76
      relations: {
×
77
        spaces: true,
×
78
        storageAggregator: true,
79
      },
×
80
    });
×
81
    if (!account.storageAggregator) {
82
      throw new RelationshipNotFoundException(
83
        `Unable to find storage aggregator on account for creating space ${account.id} `,
84
        LogContext.ACCOUNT
85
      );
×
86
    }
87

×
NEW
88
    const reservedNameIDs =
×
89
      await this.namingService.getReservedNameIDsLevelZeroSpaces();
90
    if (!spaceData.nameID) {
91
      spaceData.nameID = this.namingService.createNameIdAvoidingReservedNameIDs(
×
92
        spaceData.about.profileData.displayName,
93
        reservedNameIDs
94
      );
95
    } else {
×
96
      if (reservedNameIDs.includes(spaceData.nameID)) {
×
97
        throw new ValidationException(
98
          `Unable to create entity: the provided nameID is already taken or restricted: ${spaceData.nameID}`,
99
          LogContext.SPACES
×
100
        );
101
      }
102
    }
103

104
    // Set data for the root space
×
105
    spaceData.level = SpaceLevel.L0;
×
106
    spaceData.storageAggregatorParent = account.storageAggregator;
107
    // will be set properly after saving to its own ID
108
    spaceData.levelZeroSpaceID = '';
109

×
110
    let space = await this.spaceService.createRootSpaceAndSubspaces(
111
      spaceData,
112
      agentInfo
113
    );
×
114
    space.account = account;
115
    space = await this.spaceService.save(space);
116

117
    space = await this.spaceService.getSpaceOrFail(space.id, {
×
118
      relations: {
119
        community: {
×
120
          roleSet: true,
×
121
        },
122
        subspaces: {
123
          community: {
×
124
            roleSet: true,
×
125
          },
×
126
          subspaces: {
×
127
            community: {
×
128
              roleSet: true,
129
            },
130
          },
×
131
        },
132
        agent: true,
133
      },
134
    });
×
135
    if (!space.agent || !space.community || !space.community.roleSet) {
×
136
      throw new EntityNotInitializedException(
137
        `Unable to load space ${space.id} with required entities for creating space`,
×
138
        LogContext.SPACES
×
139
      );
140
    }
141
    const spaceAgent = space.agent;
142

143
    const roleSets = this.findNestedRoleSets(space);
144

145
    if (!agentInfo.isAnonymous) {
×
146
      for (const roleSet of roleSets) {
147
        await this.spaceService.assignUserToRoles(roleSet, agentInfo);
×
148
      }
149
    }
150

151
    // Add in org as member + lead if applicable
152
    if (account.type === AccountType.ORGANIZATION) {
153
      const host = await this.accountLookupService.getHostOrFail(account);
154
      const organizationID = host.id;
155
      const rootRoleSet = space.community.roleSet;
×
156
      await this.spaceService.assignOrganizationToMemberLeadRoles(
×
157
        rootRoleSet,
158
        organizationID
159
      );
160
    }
161

×
162
    space.agent = await this.accountHostService.assignLicensePlansToSpace(
163
      spaceAgent,
164
      space.id,
×
165
      account.type,
×
166
      spaceData.licensePlanID
×
167
    );
168
    return await this.spaceService.getSpaceOrFail(space.id, {
169
      relations: {
170
        agent: true,
171
      },
×
172
    });
×
173
  }
174

175
  private findNestedRoleSets = (
176
    space: ISpace,
177
    spaceLevel: SpaceLevel = SpaceLevel.L0
178
  ): IRoleSet[] => {
179
    if (spaceLevel > MAX_SPACE_LEVEL) {
180
      return [];
×
181
    }
×
182
    const roleSets: IRoleSet[] = [];
183
    if (space.community?.roleSet) {
×
184
      roleSets.push(space.community.roleSet);
185
    }
186
    if (space.subspaces) {
187
      for (const subspace of space.subspaces) {
188
        roleSets.push(...this.findNestedRoleSets(subspace, spaceLevel + 1));
×
189
      }
190
    }
×
191
    return roleSets;
×
192
  };
193

194
  async save(account: IAccount): Promise<IAccount> {
195
    return await this.accountRepository.save(account);
×
196
  }
197

198
  async deleteAccountOrFail(accountInput: IAccount): Promise<IAccount | never> {
199
    const accountID = accountInput.id;
200
    const account = await this.getAccountOrFail(accountID, {
201
      relations: {
×
202
        agent: true,
203
        spaces: true,
204
        virtualContributors: true,
205
        innovationPacks: true,
206
        storageAggregator: true,
207
        innovationHubs: true,
208
        license: true,
209
      },
×
210
    });
×
211

212
    if (
213
      !account.agent ||
214
      !account.spaces ||
×
215
      !account.virtualContributors ||
216
      !account.storageAggregator ||
217
      !account.innovationHubs ||
218
      !account.innovationPacks ||
219
      !account.license
220
    ) {
221
      throw new RelationshipNotFoundException(
×
222
        `Unable to load all entities for deletion of account ${account.id} `,
×
223
        LogContext.ACCOUNT
224
      );
×
225
    }
×
226

227
    await this.agentService.deleteAgent(account.agent.id);
228

229
    await this.storageAggregatorService.delete(account.storageAggregator.id);
230

231
    await this.licenseService.removeLicenseOrFail(account.license.id);
×
232

233
    for (const vc of account.virtualContributors) {
234
      await this.virtualContributorService.deleteVirtualContributor(vc.id);
235
    }
236
    for (const ip of account.innovationPacks) {
237
      await this.innovationPackService.deleteInnovationPack({ ID: ip.id });
238
    }
239

240
    for (const hub of account.innovationHubs) {
×
241
      await this.innovationHubService.delete(hub.id);
242
    }
243

244
    for (const space of account.spaces) {
×
245
      await this.spaceService.deleteSpaceOrFail({ ID: space.id });
×
246
    }
247

248
    const result = await this.accountRepository.remove(account as Account);
×
249
    result.id = accountID;
250
    return result;
251
  }
252

×
253
  public updateExternalSubscriptionId(
×
254
    accountID: string,
255
    externalSubscriptionID: string
256
  ) {
257
    return this.accountRepository.update(accountID, { externalSubscriptionID });
258
  }
259

260
  async getAccountOrFail(
261
    accountID: string,
262
    options?: FindOneOptions<Account>
263
  ): Promise<IAccount | never> {
264
    const account = await this.getAccount(accountID, options);
265
    if (!account)
266
      throw new EntityNotFoundException(
×
267
        `Unable to find Account with ID: ${accountID}`,
×
268
        LogContext.ACCOUNT
269
      );
270
    return account;
271
  }
272

273
  async getAccount(
274
    accountID: string,
275
    options?: FindOneOptions<Account>
276
  ): Promise<IAccount | null> {
×
277
    return await this.accountRepository.findOne({
278
      where: { id: accountID },
279
      ...options,
280
    });
281
  }
282

×
283
  public async getAccountAndDetails(accountID: string) {
×
284
    const [account] = await this.accountRepository.query(
285
      `
286
        SELECT
287
          account.id as accountId, account.externalSubscriptionID as externalSubscriptionID,
×
288
          organization.id as orgId, organization.contactEmail as orgContactEmail, organization.legalEntityName as orgLegalName, organization.nameID as orgNameID,
289
          profile.displayName as orgDisplayName,
×
290
          user.id as userId, user.email as userEmail, CONCAT(user.firstName, ' ', user.lastName) as userName
291
        FROM account
×
292
        LEFT JOIN user on account.id = user.accountID
×
293
        LEFT JOIN organization on account.id = organization.accountID
294
        left join profile on organization.profileId = profile.id
295
        WHERE account.id = ?
×
296
    `,
297
      [accountID]
298
    );
299

300
    if (!account) {
×
301
      return undefined;
×
302
    }
303

×
304
    return {
×
305
      accountID,
306
      externalSubscriptionID: account.externalSubscriptionID,
307
      user: account.userId
×
308
        ? {
×
309
            id: account.userId,
310
            email: account.userEmail,
311
            name: account.userName,
×
312
          }
×
313
        : undefined,
×
314
      organization: account.orgId
315
        ? {
316
            id: account.orgId,
317
            email: account.orgContactEmail,
318
            legalName: account.orgLegalName,
319
            orgLegalName: account.orgLegalName,
320
            displayName: account.orgDisplayName,
×
321
            nameID: account.orgNameID,
×
322
          }
×
323
        : undefined,
324
    };
325
  }
326

×
327
  async getAccounts(options?: FindManyOptions<Account>): Promise<IAccount[]> {
328
    const accounts = await this.accountRepository.find({
329
      ...options,
330
    });
331

332
    if (!accounts) return [];
333

×
334
    return accounts;
×
335
  }
336

337
  public async getAgentOrFail(accountID: string): Promise<IAgent> {
338
    const account = await this.getAccountOrFail(accountID, {
339
      relations: {
×
340
        agent: true,
341
      },
342
    });
×
343

344
    if (!account.agent) {
345
      throw new EntityNotInitializedException(
346
        'Unable to load Agent for Account',
×
347
        LogContext.ACCOUNT,
348
        { accountId: accountID }
×
349
      );
350
    }
351

×
352
    return account.agent;
×
353
  }
×
354

355
  public async createVirtualContributorOnAccount(
356
    vcData: CreateVirtualContributorOnAccountInput,
357
    agentInfo?: AgentInfo
358
  ): Promise<IVirtualContributor> {
359
    const accountID = vcData.accountID;
360
    const account = await this.getAccountOrFail(accountID, {
×
361
      relations: {
362
        virtualContributors: true,
×
363
        storageAggregator: true,
×
364
      },
365
    });
366

367
    if (!account.virtualContributors || !account.storageAggregator) {
368
      throw new RelationshipNotFoundException(
369
        `Unable to load Account with required entities for creating VC: ${account.id} by user ${agentInfo?.userID}`,
×
370
        LogContext.ACCOUNT
×
371
      );
372
    }
373

374
    const knowledgeBaseCalloutDefaults: CreateCalloutInput[] =
×
375
      await this.platformTemplatesService.getCreateCalloutInputsFromTemplate(
376
        TemplateDefaultType.PLATFORM_SUBSPACE_KNOWLEDGE
377
      );
378
    const vc = await this.virtualContributorService.createVirtualContributor(
379
      vcData,
380
      knowledgeBaseCalloutDefaults,
381
      account.storageAggregator,
×
382
      agentInfo
383
    );
×
384
    vc.account = account;
×
385
    return await this.virtualContributorService.save(vc);
386
  }
387

388
  public async createInnovationHubOnAccount(
389
    innovationHubData: CreateInnovationHubOnAccountInput
390
  ): Promise<IInnovationHub> {
×
391
    const accountID = innovationHubData.accountID;
392
    const account = await this.getAccountOrFail(accountID, {
393
      relations: { storageAggregator: true },
394
    });
395

396
    if (!account.storageAggregator) {
397
      throw new RelationshipNotFoundException(
×
398
        `Unable to load Account with required entities for creating an InnovationHub: ${account.id} `,
×
399
        LogContext.ACCOUNT
400
      );
×
401
    }
402
    let hub = await this.innovationHubService.createInnovationHub(
403
      innovationHubData,
404
      account
×
405
    );
406
    hub.account = account;
407
    hub = await this.innovationHubService.save(hub);
408
    const authorizations =
×
409
      await this.innovationHubAuthorizationService.applyAuthorizationPolicy(
410
        hub,
411
        account.authorization
412
      );
413
    await this.authorizationPolicyService.saveAll(authorizations);
414
    return hub;
×
415
  }
416

417
  public async createInnovationPackOnAccount(
418
    ipData: CreateInnovationPackOnAccountInput
419
  ): Promise<IInnovationPack> {
420
    const accountID = ipData.accountID;
421
    const account = await this.getAccountOrFail(accountID, {
×
422
      relations: { storageAggregator: true },
×
423
    });
424

425
    if (!account.storageAggregator) {
426
      throw new RelationshipNotFoundException(
427
        `Unable to load Account with required entities for creating Innovation Pack: ${account.id} `,
×
428
        LogContext.ACCOUNT
×
429
      );
×
430
    }
431
    let ip = await this.innovationPackService.createInnovationPack(
432
      ipData,
433
      account.storageAggregator
434
    );
×
435
    ip.account = account;
436
    ip = await this.innovationPackService.save(ip);
437
    const authorizations =
438
      await this.innovationPackAuthorizationService.applyAuthorizationPolicy(
439
        ip,
440
        account.authorization
×
441
      );
442
    await this.authorizationPolicyService.saveAll(authorizations);
443
    return ip;
444
  }
445

446
  public async getStorageAggregatorOrFail(
×
447
    accountID: string
×
448
  ): Promise<IStorageAggregator> {
449
    const space = await this.getAccountOrFail(accountID, {
450
      relations: {
451
        storageAggregator: true,
452
      },
453
    });
454
    const storageAggregator = space.storageAggregator;
×
455
    if (!storageAggregator)
×
456
      throw new RelationshipNotFoundException(
457
        `Unable to load storage aggregator for account ${accountID} `,
458
        LogContext.ACCOUNT
459
      );
460
    return storageAggregator;
461
  }
×
462

463
  async getSubscriptions(
464
    accountInput: IAccount
465
  ): Promise<IAccountSubscription[]> {
×
466
    const account = await this.getAccountOrFail(accountInput.id, {
×
467
      relations: {
468
        agent: {
469
          credentials: true,
470
        },
471
      },
472
    });
×
473

×
474
    if (!account.agent || !account.agent.credentials) {
475
      throw new EntityNotFoundException(
476
        `Unable to find agent with credentials for the account: ${accountInput.id}`,
477
        LogContext.ACCOUNT
×
478
      );
×
479
    }
480

481
    const subscriptions: IAccountSubscription[] = [];
482
    for (const credential of account.agent.credentials) {
483
      if (
×
484
        Object.values(LicensingCredentialBasedCredentialType).includes(
485
          credential.type as LicensingCredentialBasedCredentialType
486
        )
487
      ) {
×
488
        subscriptions.push({
×
489
          name: credential.type as LicensingCredentialBasedCredentialType,
490
          expires: credential.expires,
×
491
        });
492
      }
493
    }
494
    return subscriptions;
×
495
  }
×
496
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc