From 6106bd9b1886f60c92501f42d3d104f096e9d23e Mon Sep 17 00:00:00 2001 From: Emilia Pavlova Date: Thu, 20 Jun 2024 11:17:36 +0300 Subject: [PATCH 01/17] wip --- .../notification-adapter/notification.adapter.ts | 12 ++++++++++++ .../notification.payload.builder.ts | 1 + 2 files changed, 13 insertions(+) diff --git a/src/services/adapters/notification-adapter/notification.adapter.ts b/src/services/adapters/notification-adapter/notification.adapter.ts index 37f9e86981..e8c047269c 100644 --- a/src/services/adapters/notification-adapter/notification.adapter.ts +++ b/src/services/adapters/notification-adapter/notification.adapter.ts @@ -337,6 +337,18 @@ export class NotificationAdapter { this.notificationsClient.emit(event, payload); } + public async spaceCreated( + eventData: NotificationInputSpaceCreated + ): Promise { + const event = NotificationEventType.SPACE_CREATED; + this.logEventTriggered(eventData, event); + + const payload = + await this.notificationPayloadBuilder.buildSpaceCreatedPayload(eventData); + + this.notificationsClient.emit(event, payload); + } + public async communityNewMember( eventData: NotificationInputCommunityNewMember ): Promise { diff --git a/src/services/adapters/notification-adapter/notification.payload.builder.ts b/src/services/adapters/notification-adapter/notification.payload.builder.ts index 31c6fb4606..cc871bf158 100644 --- a/src/services/adapters/notification-adapter/notification.payload.builder.ts +++ b/src/services/adapters/notification-adapter/notification.payload.builder.ts @@ -34,6 +34,7 @@ import { SpaceBaseEventPayload, PlatformGlobalRoleChangeEventPayload, RoleChangeType, + SpaceCreatedEventPayload, } from '@alkemio/notifications-lib'; import { ICallout } from '@domain/collaboration/callout/callout.interface'; import { CommunityResolverService } from '@services/infrastructure/entity-resolver/community.resolver.service'; From 8f73d02a3eb4cd6870dc98d3ea4022de9cc8db00 Mon Sep 17 00:00:00 2001 From: Emilia Pavlova Date: Thu, 27 Jun 2024 11:27:12 +0300 Subject: [PATCH 02/17] wip --- src/common/enums/activity.event.type.ts | 1 + .../community/community.service.events.ts | 14 ++++++++++++++ .../space/account/account.resolver.fields.ts | 1 + .../account/account.resolver.mutations.ts | 14 +++++++++++++- .../notification.dto.input.space.created.ts | 8 ++++++++ .../notification.adapter.ts | 7 ++++++- .../notification.payload.builder.ts | 18 +++++++++++++++++- 7 files changed, 60 insertions(+), 3 deletions(-) create mode 100644 src/services/adapters/notification-adapter/dto/notification.dto.input.space.created.ts diff --git a/src/common/enums/activity.event.type.ts b/src/common/enums/activity.event.type.ts index 110b24fed7..74cb5eb1ec 100644 --- a/src/common/enums/activity.event.type.ts +++ b/src/common/enums/activity.event.type.ts @@ -13,6 +13,7 @@ export enum ActivityEventType { CHALLENGE_CREATED = 'challenge-created', OPPORTUNITY_CREATED = 'opportunity-created', CALENDAR_EVENT_CREATED = 'calendar-event-created', + SPACE_CREATED = 'space-created', } registerEnumType(ActivityEventType, { diff --git a/src/domain/community/community/community.service.events.ts b/src/domain/community/community/community.service.events.ts index d4f9d07778..3051813c70 100644 --- a/src/domain/community/community/community.service.events.ts +++ b/src/domain/community/community/community.service.events.ts @@ -8,6 +8,7 @@ import { ActivityInputMemberJoined } from '@services/adapters/activity-adapter/d import { IUser } from '../user/user.interface'; import { ActivityAdapter } from '@services/adapters/activity-adapter/activity.adapter'; import { SpaceType } from '@common/enums/space.type'; +import { NotificationInputSpaceCreated } from '@services/adapters/notification-adapter/dto/notification.dto.input.space.created'; @Injectable() export class CommunityEventsService { @@ -76,4 +77,17 @@ export class CommunityEventsService { break; } } + + public async createCommunityNewSpaceActivity( + community: ICommunity, + // newMember: IUser, + agentInfo: AgentInfo + ) { + const activityLogInput: NotificationInputSpaceCreated = { + triggeredBy: agentInfo.userID, + community: community, + // user: newMember, + }; + await this.activityAdapter.memberJoined(activityLogInput); + } } diff --git a/src/domain/space/account/account.resolver.fields.ts b/src/domain/space/account/account.resolver.fields.ts index ebb36d00de..2a7cd08cfe 100644 --- a/src/domain/space/account/account.resolver.fields.ts +++ b/src/domain/space/account/account.resolver.fields.ts @@ -164,6 +164,7 @@ export class AccountResolverFields { description: 'The "highest" subscription active for this Account.', }) async activeSubscription(@Parent() account: Account) { + // TODO: Extract as a method in account service const licensingFramework = await this.licensingService.getDefaultLicensingOrFail(); diff --git a/src/domain/space/account/account.resolver.mutations.ts b/src/domain/space/account/account.resolver.mutations.ts index 036d37053b..87fa9cfb74 100644 --- a/src/domain/space/account/account.resolver.mutations.ts +++ b/src/domain/space/account/account.resolver.mutations.ts @@ -34,6 +34,8 @@ import { IngestSpace } from '@services/infrastructure/event-bus/commands'; import { CommunityContributorType } from '@common/enums/community.contributor.type'; import { CommunityRole } from '@common/enums/community.role'; import { AccountHostService } from './account.host.service'; +import { NotificationAdapter } from '@services/adapters/notification-adapter/notification.adapter'; +import { NotificationInputSpaceCreated } from '@services/adapters/notification-adapter/dto/notification.dto.input.space.created'; @Resolver() export class AccountResolverMutations { @@ -49,7 +51,8 @@ export class AccountResolverMutations { private spaceDefaultsService: SpaceDefaultsService, private namingReporter: NameReporterService, private spaceService: SpaceService, - private eventBus: EventBus + private eventBus: EventBus, + private notificationAdapter: NotificationAdapter ) {} @UseGuards(GraphqlGuard) @@ -82,6 +85,15 @@ export class AccountResolverMutations { space.id, space.profile.displayName ); + + // notification + const notificationInput: NotificationInputSpaceCreated = { + triggeredBy: agentInfo.userID, + community: community, + account: invitedContributor.account, + }; + await this.notificationAdapter.spaceCreated(notificationInput); + return accountUpdated; } diff --git a/src/services/adapters/notification-adapter/dto/notification.dto.input.space.created.ts b/src/services/adapters/notification-adapter/dto/notification.dto.input.space.created.ts new file mode 100644 index 0000000000..5d7d88f440 --- /dev/null +++ b/src/services/adapters/notification-adapter/dto/notification.dto.input.space.created.ts @@ -0,0 +1,8 @@ +import { ICommunity } from '@domain/community/community'; +import { NotificationInputBase } from './notification.dto.input.base'; +import { IAccount } from '@domain/space/account/account.interface'; + +export interface NotificationInputSpaceCreated extends NotificationInputBase { + community: ICommunity; + account: IAccount; +} diff --git a/src/services/adapters/notification-adapter/notification.adapter.ts b/src/services/adapters/notification-adapter/notification.adapter.ts index 68ff23f28a..3e731771eb 100644 --- a/src/services/adapters/notification-adapter/notification.adapter.ts +++ b/src/services/adapters/notification-adapter/notification.adapter.ts @@ -30,6 +30,7 @@ import { NotificationInputCommentReply } from './dto/notification.dto.input.comm import { NotificationInputCommunityInvitationExternal } from './dto/notification.dto.input.community.invitation.external'; import { NotificationInputPlatformGlobalRoleChange } from './dto/notification.dto.input.platform.global.role.change'; import { NotificationInputCommunityVirtualContributorInvitation } from './dto/notification.dto.input.community.vc.invitation'; +import { NotificationInputSpaceCreated } from './dto/notification.dto.input.space.created'; @Injectable() export class NotificationAdapter { @@ -362,7 +363,11 @@ export class NotificationAdapter { this.logEventTriggered(eventData, event); const payload = - await this.notificationPayloadBuilder.buildSpaceCreatedPayload(eventData); + await this.notificationPayloadBuilder.buildSpaceCreatedPayload( + eventData.triggeredBy, + eventData.account, + eventData.community + ); this.notificationsClient.emit(event, payload); } diff --git a/src/services/adapters/notification-adapter/notification.payload.builder.ts b/src/services/adapters/notification-adapter/notification.payload.builder.ts index 11a68a3568..ec0063cd50 100644 --- a/src/services/adapters/notification-adapter/notification.payload.builder.ts +++ b/src/services/adapters/notification-adapter/notification.payload.builder.ts @@ -33,7 +33,7 @@ import { SpaceBaseEventPayload, PlatformGlobalRoleChangeEventPayload, RoleChangeType, - SpaceCreatedEventPayload, + // SpaceCreatedEventPayload, } from '@alkemio/notifications-lib'; import { ICallout } from '@domain/collaboration/callout/callout.interface'; import { CommunityResolverService } from '@services/infrastructure/entity-resolver/community.resolver.service'; @@ -434,6 +434,22 @@ export class NotificationPayloadBuilder { return payload; } + async buildSpaceCreatedPayload( + triggeredBy: string, + account: IAccount, + community: ICommunity + // ): Promise { + ): Promise { + const spacePayload = await this.buildSpacePayload(community, triggeredBy); + + const host = await this.accountHostService.getHostOrFail(account); + const hostPayload = await this.getContributorPayloadOrFail(host.id); + return { + host: hostPayload, + ...spacePayload, + }; + } + async buildCommunicationUpdateSentNotificationPayload( updateCreatorId: string, updates: IRoom From b83cb0ed434f26632dc0b1d370e7b941f8924d0e Mon Sep 17 00:00:00 2001 From: Emilia Pavlova Date: Mon, 1 Jul 2024 10:43:30 +0300 Subject: [PATCH 03/17] wip --- src/common/enums/activity.event.type.ts | 1 - .../community/community.service.events.ts | 13 --------- .../space/account.host/account.host.module.ts | 6 +++-- .../account/account.resolver.mutations.ts | 27 ++++++++++++++----- .../notification.adapter.module.ts | 7 ++++- .../notification.adapter.ts | 1 + .../notification.payload.builder.ts | 5 +++- 7 files changed, 35 insertions(+), 25 deletions(-) diff --git a/src/common/enums/activity.event.type.ts b/src/common/enums/activity.event.type.ts index 74cb5eb1ec..110b24fed7 100644 --- a/src/common/enums/activity.event.type.ts +++ b/src/common/enums/activity.event.type.ts @@ -13,7 +13,6 @@ export enum ActivityEventType { CHALLENGE_CREATED = 'challenge-created', OPPORTUNITY_CREATED = 'opportunity-created', CALENDAR_EVENT_CREATED = 'calendar-event-created', - SPACE_CREATED = 'space-created', } registerEnumType(ActivityEventType, { diff --git a/src/domain/community/community/community.service.events.ts b/src/domain/community/community/community.service.events.ts index 4dcc5a4792..162e13b209 100644 --- a/src/domain/community/community/community.service.events.ts +++ b/src/domain/community/community/community.service.events.ts @@ -77,17 +77,4 @@ export class CommunityEventsService { break; } } - - public async createCommunityNewSpaceActivity( - community: ICommunity, - // newMember: IUser, - agentInfo: AgentInfo - ) { - const activityLogInput: NotificationInputSpaceCreated = { - triggeredBy: agentInfo.userID, - community: community, - // user: newMember, - }; - await this.activityAdapter.memberJoined(activityLogInput); - } } diff --git a/src/domain/space/account.host/account.host.module.ts b/src/domain/space/account.host/account.host.module.ts index d5790db318..3a6479b010 100644 --- a/src/domain/space/account.host/account.host.module.ts +++ b/src/domain/space/account.host/account.host.module.ts @@ -2,10 +2,12 @@ import { ContributorModule } from '@domain/community/contributor/contributor.mod import { Module } from '@nestjs/common'; import { AccountHostService } from './account.host.service'; import { AgentModule } from '@domain/agent/agent/agent.module'; +import { ContributorService } from '@domain/community/contributor/contributor.service'; +import { ContributorLookupModule } from '@services/infrastructure/contributor-lookup/contributor.lookup.module'; @Module({ - imports: [ContributorModule, AgentModule], - providers: [AccountHostService], + imports: [ContributorModule, AgentModule, ContributorLookupModule], + providers: [AccountHostService, ContributorService], exports: [AccountHostService], }) export class AccountHostModule {} diff --git a/src/domain/space/account/account.resolver.mutations.ts b/src/domain/space/account/account.resolver.mutations.ts index e884088736..3d11fedf7b 100644 --- a/src/domain/space/account/account.resolver.mutations.ts +++ b/src/domain/space/account/account.resolver.mutations.ts @@ -33,6 +33,7 @@ import { CommunityRole } from '@common/enums/community.role'; import { NotificationAdapter } from '@services/adapters/notification-adapter/notification.adapter'; import { NotificationInputSpaceCreated } from '@services/adapters/notification-adapter/dto/notification.dto.input.space.created'; import { CreateSpaceOnAccountInput } from './dto/account.dto.create.space'; +import { CommunityService } from '@domain/community/community/community.service'; @Resolver() export class AccountResolverMutations { @@ -47,7 +48,8 @@ export class AccountResolverMutations { private spaceDefaultsService: SpaceDefaultsService, private namingReporter: NameReporterService, private spaceService: SpaceService, - private notificationAdapter: NotificationAdapter + private notificationAdapter: NotificationAdapter, + private communityService: CommunityService ) {} @UseGuards(GraphqlGuard) @@ -96,12 +98,23 @@ export class AccountResolverMutations { ); // notification - const notificationInput: NotificationInputSpaceCreated = { - triggeredBy: agentInfo.userID, - community: community, - account: invitedContributor.account, - }; - await this.notificationAdapter.spaceCreated(notificationInput); + // const community = await this.communityService.getCommunityOrFail( + // space.community?.id, + // { + // relations: { + // parentCommunity: { + // authorization: true, + // }, + // }, + // } + // ); + // const notificationInput: NotificationInputSpaceCreated = { + // triggeredBy: agentInfo.userID, + // community: community, + // account: space.account, + // }; + // console.log(notificationInput); + // await this.notificationAdapter.spaceCreated(notificationInput); return account; } diff --git a/src/services/adapters/notification-adapter/notification.adapter.module.ts b/src/services/adapters/notification-adapter/notification.adapter.module.ts index 534ce62b32..46eb568008 100644 --- a/src/services/adapters/notification-adapter/notification.adapter.module.ts +++ b/src/services/adapters/notification-adapter/notification.adapter.module.ts @@ -9,6 +9,7 @@ import { NotificationAdapter } from './notification.adapter'; import { NotificationPayloadBuilder } from './notification.payload.builder'; import { UrlGeneratorModule } from '@services/infrastructure/url-generator/url.generator.module'; import { ContributorLookupModule } from '@services/infrastructure/contributor-lookup/contributor.lookup.module'; +import { AccountHostService } from '@domain/space/account.host/account.host.service'; @Module({ imports: [ @@ -18,7 +19,11 @@ import { ContributorLookupModule } from '@services/infrastructure/contributor-lo TypeOrmModule.forFeature([Post, Whiteboard, Community]), ContributorLookupModule, ], - providers: [NotificationAdapter, NotificationPayloadBuilder], + providers: [ + NotificationAdapter, + NotificationPayloadBuilder, + AccountHostService, + ], exports: [NotificationAdapter], }) export class NotificationAdapterModule {} diff --git a/src/services/adapters/notification-adapter/notification.adapter.ts b/src/services/adapters/notification-adapter/notification.adapter.ts index de798a4611..e2eb03f78b 100644 --- a/src/services/adapters/notification-adapter/notification.adapter.ts +++ b/src/services/adapters/notification-adapter/notification.adapter.ts @@ -370,6 +370,7 @@ export class NotificationAdapter { eventData.community ); + console.log(payload); this.notificationsClient.emit(event, payload); } diff --git a/src/services/adapters/notification-adapter/notification.payload.builder.ts b/src/services/adapters/notification-adapter/notification.payload.builder.ts index f467d75991..f920ec7146 100644 --- a/src/services/adapters/notification-adapter/notification.payload.builder.ts +++ b/src/services/adapters/notification-adapter/notification.payload.builder.ts @@ -53,6 +53,8 @@ import { UrlGeneratorService } from '@services/infrastructure/url-generator/url. import { IDiscussion } from '@platform/forum-discussion/discussion.interface'; import { ContributorLookupService } from '@services/infrastructure/contributor-lookup/contributor.lookup.service'; import { IContributor } from '@domain/community/contributor/contributor.interface'; +import { IAccount } from '@domain/space/account/account.interface'; +import { AccountHostService } from '@domain/space/account.host/account.host.service'; @Injectable() export class NotificationPayloadBuilder { @@ -67,7 +69,8 @@ export class NotificationPayloadBuilder { private readonly logger: LoggerService, private configService: ConfigService, private contributionResolverService: ContributionResolverService, - private urlGeneratorService: UrlGeneratorService + private urlGeneratorService: UrlGeneratorService, + private accountHostService: AccountHostService ) {} async buildApplicationCreatedNotificationPayload( From 90c1ecf0584ad3b57be3f8aa8012aae589e05979 Mon Sep 17 00:00:00 2001 From: Neil Smyth <30729240+techsmyth@users.noreply.github.com> Date: Tue, 2 Jul 2024 17:07:01 +0200 Subject: [PATCH 04/17] updated credentials to accept invitations to include host creds when org (#4204) --- .../invitation.service.authorization.ts | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/src/domain/community/invitation/invitation.service.authorization.ts b/src/domain/community/invitation/invitation.service.authorization.ts index 685153aa97..acedaa3060 100644 --- a/src/domain/community/invitation/invitation.service.authorization.ts +++ b/src/domain/community/invitation/invitation.service.authorization.ts @@ -67,19 +67,11 @@ export class InvitationAuthorizationService { }); break; case CommunityContributorType.VIRTUAL: - const vcWithHost = - await this.virtualContributorService.getVirtualContributorOrFail( - contributor.id, - { - relations: { - account: true, - }, - } + const vcHostCriterias = + await this.virtualContributorService.getAccountHostCredentials( + contributor.id ); - criterias.push({ - type: AuthorizationCredential.ACCOUNT_HOST, - resourceID: vcWithHost.account.id, - }); + criterias.push(...vcHostCriterias); break; } From a7fc1f5af257c9790661394bba33ea576b6d5a3e Mon Sep 17 00:00:00 2001 From: Emilia Pavlova Date: Wed, 3 Jul 2024 09:46:42 +0300 Subject: [PATCH 05/17] Sent notification to license managers when a new space is created --- package.json | 2 +- .../community/community.service.events.ts | 1 - src/domain/space/account/account.module.ts | 4 ++ .../space/account/account.resolver.fields.ts | 29 ++---------- .../account/account.resolver.mutations.ts | 46 +++++++++++-------- src/domain/space/account/account.service.ts | 33 ++++++++++++- .../notification.adapter.module.ts | 7 +-- .../notification.adapter.ts | 2 - .../notification.payload.builder.ts | 23 +++++----- 9 files changed, 80 insertions(+), 67 deletions(-) diff --git a/package.json b/package.json index ac2a2dda9a..cfed573462 100644 --- a/package.json +++ b/package.json @@ -52,7 +52,7 @@ }, "dependencies": { "@alkemio/matrix-adapter-lib": "^0.3.6", - "@alkemio/notifications-lib": "^0.9.3", + "@alkemio/notifications-lib": "^0.9.4", "@apollo/server": "^4.10.2", "@elastic/elasticsearch": "^8.4.0", "@golevelup/nestjs-rabbitmq": "^5.3.0", diff --git a/src/domain/community/community/community.service.events.ts b/src/domain/community/community/community.service.events.ts index 162e13b209..5e67d82c40 100644 --- a/src/domain/community/community/community.service.events.ts +++ b/src/domain/community/community/community.service.events.ts @@ -8,7 +8,6 @@ import { ActivityInputMemberJoined } from '@services/adapters/activity-adapter/d import { ActivityAdapter } from '@services/adapters/activity-adapter/activity.adapter'; import { SpaceType } from '@common/enums/space.type'; import { IContributor } from '../contributor/contributor.interface'; -import { NotificationInputSpaceCreated } from '@services/adapters/notification-adapter/dto/notification.dto.input.space.created'; @Injectable() export class CommunityEventsService { diff --git a/src/domain/space/account/account.module.ts b/src/domain/space/account/account.module.ts index 1b1521732d..8405ba2b08 100644 --- a/src/domain/space/account/account.module.ts +++ b/src/domain/space/account/account.module.ts @@ -24,6 +24,8 @@ import { AccountHostModule } from '../account.host/account.host.module'; import { LicenseEngineModule } from '@core/license-engine/license.engine.module'; import { StorageAggregatorModule } from '@domain/storage/storage-aggregator/storage.aggregator.module'; import { CommunityPolicyModule } from '@domain/community/community-policy/community.policy.module'; +import { NotificationAdapterModule } from '@services/adapters/notification-adapter/notification.adapter.module'; +import { CommunityModule } from '@domain/community/community/community.module'; @Module({ imports: [ @@ -46,6 +48,8 @@ import { CommunityPolicyModule } from '@domain/community/community-policy/commun NameReporterModule, CommunityPolicyModule, TypeOrmModule.forFeature([Account]), + NotificationAdapterModule, + CommunityModule, ], providers: [ AccountService, diff --git a/src/domain/space/account/account.resolver.fields.ts b/src/domain/space/account/account.resolver.fields.ts index d418700d4d..2b83d45210 100644 --- a/src/domain/space/account/account.resolver.fields.ts +++ b/src/domain/space/account/account.resolver.fields.ts @@ -35,7 +35,6 @@ import { } from '@domain/community/virtual-contributor'; import { AccountHostService } from '../account.host/account.host.service'; import { LicensePrivilege } from '@common/enums/license.privilege'; -import { LicensePlanType } from '@common/enums/license.plan.type'; @Resolver(() => IAccount) export class AccountResolverFields { @@ -163,37 +162,15 @@ export class AccountResolverFields { nullable: true, description: 'The "highest" subscription active for this Account.', }) - async activeSubscription(@Parent() account: Account) { - // TODO: Extract as a method in account service - const licensingFramework = - await this.licensingService.getDefaultLicensingOrFail(); - - const today = new Date(); - const plans = await this.licensingService.getLicensePlans( - licensingFramework.id - ); - - return (await this.accountService.getSubscriptions(account)) - .filter( - subscription => !subscription.expires || subscription.expires > today - ) - .map(subscription => { - return { - subscription, - plan: plans.find( - plan => plan.licenseCredential === subscription.name - ), - }; - }) - .filter(item => item.plan?.type === LicensePlanType.SPACE_PLAN) - .sort((a, b) => b.plan!.sortOrder - a.plan!.sortOrder)?.[0].subscription; + async activeSubscription(@Parent() account: IAccount) { + return this.accountService.activeSubscription(account); } @ResolveField('subscriptions', () => [IAccountSubscription], { nullable: false, description: 'The subscriptions active for this Account.', }) - async subscriptions(@Parent() account: Account) { + async subscriptions(@Parent() account: IAccount) { return await this.accountService.getSubscriptions(account); } diff --git a/src/domain/space/account/account.resolver.mutations.ts b/src/domain/space/account/account.resolver.mutations.ts index c2f573c9f6..549dfd245c 100644 --- a/src/domain/space/account/account.resolver.mutations.ts +++ b/src/domain/space/account/account.resolver.mutations.ts @@ -90,31 +90,39 @@ export class AccountResolverMutations { ); account = await this.accountService.save(account); - const rootSpace = await this.accountService.getRootSpace(account); + const rootSpace = await this.accountService.getRootSpace(account, { + relations: { + community: true, + }, + }); await this.namingReporter.createOrUpdateName( rootSpace.id, rootSpace.profile.displayName ); - // notification - // const community = await this.communityService.getCommunityOrFail( - // space.community?.id, - // { - // relations: { - // parentCommunity: { - // authorization: true, - // }, - // }, - // } - // ); - // const notificationInput: NotificationInputSpaceCreated = { - // triggeredBy: agentInfo.userID, - // community: community, - // account: space.account, - // }; - // console.log(notificationInput); - // await this.notificationAdapter.spaceCreated(notificationInput); + if (!rootSpace.community?.id) { + throw new RelationshipNotFoundException( + `Unable to find community with id ${rootSpace.community?.id} `, + LogContext.ACCOUNT + ); + } + const community = await this.communityService.getCommunityOrFail( + rootSpace.community?.id, + { + relations: { + parentCommunity: { + authorization: true, + }, + }, + } + ); + const notificationInput: NotificationInputSpaceCreated = { + triggeredBy: agentInfo.userID, + community: community, + account: account, + }; + await this.notificationAdapter.spaceCreated(notificationInput); return account; } diff --git a/src/domain/space/account/account.service.ts b/src/domain/space/account/account.service.ts index ef6b2b1d2f..3805d9876c 100644 --- a/src/domain/space/account/account.service.ts +++ b/src/domain/space/account/account.service.ts @@ -42,6 +42,8 @@ import { LicensePrivilege } from '@common/enums/license.privilege'; import { LicenseEngineService } from '@core/license-engine/license.engine.service'; import { StorageAggregatorService } from '@domain/storage/storage-aggregator/storage.aggregator.service'; import { CreateSpaceOnAccountInput } from './dto/account.dto.create.space'; +import { Space } from '../space/space.entity'; +import { LicensePlanType } from '@common/enums/license.plan.type'; @Injectable() export class AccountService { @@ -391,7 +393,10 @@ export class AccountService { return license; } - async getRootSpace(accountInput: IAccount): Promise { + async getRootSpace( + accountInput: IAccount, + options?: FindOneOptions + ): Promise { if (accountInput.space && accountInput.space.profile) { return accountInput.space; } @@ -399,6 +404,7 @@ export class AccountService { relations: { space: { profile: true, + ...options?.relations, }, }, }); @@ -466,4 +472,29 @@ export class AccountService { return vc; } + + public async activeSubscription(account: IAccount) { + const licensingFramework = + await this.licensingService.getDefaultLicensingOrFail(); + + const today = new Date(); + const plans = await this.licensingService.getLicensePlans( + licensingFramework.id + ); + + return (await this.getSubscriptions(account)) + .filter( + subscription => !subscription.expires || subscription.expires > today + ) + .map(subscription => { + return { + subscription, + plan: plans.find( + plan => plan.licenseCredential === subscription.name + ), + }; + }) + .filter(item => item.plan?.type === LicensePlanType.SPACE_PLAN) + .sort((a, b) => b.plan!.sortOrder - a.plan!.sortOrder)?.[0].subscription; + } } diff --git a/src/services/adapters/notification-adapter/notification.adapter.module.ts b/src/services/adapters/notification-adapter/notification.adapter.module.ts index 46eb568008..534ce62b32 100644 --- a/src/services/adapters/notification-adapter/notification.adapter.module.ts +++ b/src/services/adapters/notification-adapter/notification.adapter.module.ts @@ -9,7 +9,6 @@ import { NotificationAdapter } from './notification.adapter'; import { NotificationPayloadBuilder } from './notification.payload.builder'; import { UrlGeneratorModule } from '@services/infrastructure/url-generator/url.generator.module'; import { ContributorLookupModule } from '@services/infrastructure/contributor-lookup/contributor.lookup.module'; -import { AccountHostService } from '@domain/space/account.host/account.host.service'; @Module({ imports: [ @@ -19,11 +18,7 @@ import { AccountHostService } from '@domain/space/account.host/account.host.serv TypeOrmModule.forFeature([Post, Whiteboard, Community]), ContributorLookupModule, ], - providers: [ - NotificationAdapter, - NotificationPayloadBuilder, - AccountHostService, - ], + providers: [NotificationAdapter, NotificationPayloadBuilder], exports: [NotificationAdapter], }) export class NotificationAdapterModule {} diff --git a/src/services/adapters/notification-adapter/notification.adapter.ts b/src/services/adapters/notification-adapter/notification.adapter.ts index e2eb03f78b..eecbd0a3b6 100644 --- a/src/services/adapters/notification-adapter/notification.adapter.ts +++ b/src/services/adapters/notification-adapter/notification.adapter.ts @@ -369,8 +369,6 @@ export class NotificationAdapter { eventData.account, eventData.community ); - - console.log(payload); this.notificationsClient.emit(event, payload); } diff --git a/src/services/adapters/notification-adapter/notification.payload.builder.ts b/src/services/adapters/notification-adapter/notification.payload.builder.ts index f920ec7146..231ef3f71e 100644 --- a/src/services/adapters/notification-adapter/notification.payload.builder.ts +++ b/src/services/adapters/notification-adapter/notification.payload.builder.ts @@ -34,7 +34,7 @@ import { RoleChangeType, CommunityPlatformInvitationCreatedEventPayload, CommunityInvitationVirtualContributorCreatedEventPayload, - // SpaceCreatedEventPayload, + SpaceCreatedEventPayload, } from '@alkemio/notifications-lib'; import { ICallout } from '@domain/collaboration/callout/callout.interface'; import { CommunityResolverService } from '@services/infrastructure/entity-resolver/community.resolver.service'; @@ -54,7 +54,7 @@ import { IDiscussion } from '@platform/forum-discussion/discussion.interface'; import { ContributorLookupService } from '@services/infrastructure/contributor-lookup/contributor.lookup.service'; import { IContributor } from '@domain/community/contributor/contributor.interface'; import { IAccount } from '@domain/space/account/account.interface'; -import { AccountHostService } from '@domain/space/account.host/account.host.service'; +import { Space } from '@domain/space/space/space.entity'; @Injectable() export class NotificationPayloadBuilder { @@ -69,8 +69,7 @@ export class NotificationPayloadBuilder { private readonly logger: LoggerService, private configService: ConfigService, private contributionResolverService: ContributionResolverService, - private urlGeneratorService: UrlGeneratorService, - private accountHostService: AccountHostService + private urlGeneratorService: UrlGeneratorService // private accountService: AccountService ) {} async buildApplicationCreatedNotificationPayload( @@ -427,14 +426,16 @@ export class NotificationPayloadBuilder { triggeredBy: string, account: IAccount, community: ICommunity - // ): Promise { - ): Promise { + ): Promise { const spacePayload = await this.buildSpacePayload(community, triggeredBy); + const sender = await this.getContributorPayloadOrFail(triggeredBy); - const host = await this.accountHostService.getHostOrFail(account); - const hostPayload = await this.getContributorPayloadOrFail(host.id); return { - host: hostPayload, + sender: { + name: sender?.profile.displayName, + url: sender?.profile.url, + }, + created: Date.now(), ...spacePayload, }; } @@ -727,9 +728,9 @@ export class NotificationPayloadBuilder { ): Promise { const basePayload = this.buildBaseEventPayload(triggeredBy); const space = - await this.communityResolverService.getSpaceForCommunityOrFail( + (await this.communityResolverService.getSpaceForCommunityOrFail( community.id - ); + )) as Space; const url = await this.urlGeneratorService.generateUrlForProfile( space.profile ); From 8fda209df0d737af7398e64810dc627da603d7b7 Mon Sep 17 00:00:00 2001 From: Emilia Pavlova Date: Wed, 3 Jul 2024 09:58:53 +0300 Subject: [PATCH 06/17] Clear comment --- .../notification-adapter/notification.payload.builder.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/adapters/notification-adapter/notification.payload.builder.ts b/src/services/adapters/notification-adapter/notification.payload.builder.ts index 231ef3f71e..a43169b257 100644 --- a/src/services/adapters/notification-adapter/notification.payload.builder.ts +++ b/src/services/adapters/notification-adapter/notification.payload.builder.ts @@ -69,7 +69,7 @@ export class NotificationPayloadBuilder { private readonly logger: LoggerService, private configService: ConfigService, private contributionResolverService: ContributionResolverService, - private urlGeneratorService: UrlGeneratorService // private accountService: AccountService + private urlGeneratorService: UrlGeneratorService ) {} async buildApplicationCreatedNotificationPayload( From 1b4088a629c094efc89539ec7c9f60a1b3d29072 Mon Sep 17 00:00:00 2001 From: Emilia Pavlova Date: Wed, 3 Jul 2024 12:48:40 +0300 Subject: [PATCH 07/17] update lib version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index cfed573462..442ba88bbd 100644 --- a/package.json +++ b/package.json @@ -52,7 +52,7 @@ }, "dependencies": { "@alkemio/matrix-adapter-lib": "^0.3.6", - "@alkemio/notifications-lib": "^0.9.4", + "@alkemio/notifications-lib": "^0.9.5", "@apollo/server": "^4.10.2", "@elastic/elasticsearch": "^8.4.0", "@golevelup/nestjs-rabbitmq": "^5.3.0", From e114c94999b67afcb63188837bfab406314fad54 Mon Sep 17 00:00:00 2001 From: Svetoslav Date: Wed, 3 Jul 2024 13:55:48 +0300 Subject: [PATCH 08/17] version update --- package-lock.json | 14 +++++++------- package.json | 2 +- .../notification.payload.builder.ts | 4 ++-- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/package-lock.json b/package-lock.json index dc42dcdc0d..38bc8e4454 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,7 @@ "license": "EUPL-1.2", "dependencies": { "@alkemio/matrix-adapter-lib": "^0.3.6", - "@alkemio/notifications-lib": "^0.9.3", + "@alkemio/notifications-lib": "^0.9.6", "@apollo/server": "^4.10.2", "@elastic/elasticsearch": "^8.4.0", "@golevelup/nestjs-rabbitmq": "^5.3.0", @@ -229,9 +229,9 @@ } }, "node_modules/@alkemio/notifications-lib": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/@alkemio/notifications-lib/-/notifications-lib-0.9.3.tgz", - "integrity": "sha512-yqCGNbfT0CYwY0a0H1HZIw6KYiGMfpxch2SMe6BdFUeKaN4AZSiGsG1EuIDCV5/BM4EzYgPMe2O8yUpsAWLz6Q==", + "version": "0.9.6", + "resolved": "https://registry.npmjs.org/@alkemio/notifications-lib/-/notifications-lib-0.9.6.tgz", + "integrity": "sha512-xIJ4JOmvo8grfGSeSM7GVfvuXMuIGZoDdcF37hBj/hKguQxzt3fpUMvR7pnWUbC6g5dbXZzoefZblcxreVd3tw==", "dependencies": { "@alkemio/client-lib": "^0.32.0" }, @@ -14280,9 +14280,9 @@ "version": "0.3.6" }, "@alkemio/notifications-lib": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/@alkemio/notifications-lib/-/notifications-lib-0.9.3.tgz", - "integrity": "sha512-yqCGNbfT0CYwY0a0H1HZIw6KYiGMfpxch2SMe6BdFUeKaN4AZSiGsG1EuIDCV5/BM4EzYgPMe2O8yUpsAWLz6Q==", + "version": "0.9.6", + "resolved": "https://registry.npmjs.org/@alkemio/notifications-lib/-/notifications-lib-0.9.6.tgz", + "integrity": "sha512-xIJ4JOmvo8grfGSeSM7GVfvuXMuIGZoDdcF37hBj/hKguQxzt3fpUMvR7pnWUbC6g5dbXZzoefZblcxreVd3tw==", "requires": { "@alkemio/client-lib": "^0.32.0" } diff --git a/package.json b/package.json index 442ba88bbd..2f35351333 100644 --- a/package.json +++ b/package.json @@ -52,7 +52,7 @@ }, "dependencies": { "@alkemio/matrix-adapter-lib": "^0.3.6", - "@alkemio/notifications-lib": "^0.9.5", + "@alkemio/notifications-lib": "^0.9.6", "@apollo/server": "^4.10.2", "@elastic/elasticsearch": "^8.4.0", "@golevelup/nestjs-rabbitmq": "^5.3.0", diff --git a/src/services/adapters/notification-adapter/notification.payload.builder.ts b/src/services/adapters/notification-adapter/notification.payload.builder.ts index a43169b257..fd9a6ee3cd 100644 --- a/src/services/adapters/notification-adapter/notification.payload.builder.ts +++ b/src/services/adapters/notification-adapter/notification.payload.builder.ts @@ -432,8 +432,8 @@ export class NotificationPayloadBuilder { return { sender: { - name: sender?.profile.displayName, - url: sender?.profile.url, + name: sender.profile.displayName, + url: sender.profile.url, }, created: Date.now(), ...spacePayload, From fb3b184dbfbbca135b0f0f94ec6e32c9702dd341 Mon Sep 17 00:00:00 2001 From: Neil Smyth Date: Wed, 3 Jul 2024 15:34:04 +0200 Subject: [PATCH 09/17] patch that was applied to master also for develop --- .../registration/registration.resolver.mutations.ts | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/services/api/registration/registration.resolver.mutations.ts b/src/services/api/registration/registration.resolver.mutations.ts index 3cb44ff718..59deb1f5e4 100644 --- a/src/services/api/registration/registration.resolver.mutations.ts +++ b/src/services/api/registration/registration.resolver.mutations.ts @@ -35,21 +35,20 @@ export class RegistrationResolverMutations { async createUserNewRegistration( @CurrentUser() agentInfo: AgentInfo ): Promise { - const user = await this.registrationService.registerNewUser(agentInfo); + let user = await this.registrationService.registerNewUser(agentInfo); - const savedUser = - await this.userAuthorizationService.applyAuthorizationPolicy(user); + user = await this.userAuthorizationService.applyAuthorizationPolicy(user); await this.registrationService.processPendingInvitations(user); // Send the notification const notificationInput: NotificationInputUserRegistered = { triggeredBy: agentInfo.userID, - userID: savedUser.id, + userID: user.id, }; - await this.notificationAdapter.userRegistered(notificationInput); + this.notificationAdapter.userRegistered(notificationInput); - return savedUser; + return user; } @UseGuards(GraphqlGuard) From 895b7cb28b784aa42cc232dda92d188f76e8f302 Mon Sep 17 00:00:00 2001 From: Carlos Cano Date: Thu, 4 Jul 2024 09:29:16 +0200 Subject: [PATCH 10/17] Remove unneded space --- src/domain/space/account/account.resolver.mutations.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/domain/space/account/account.resolver.mutations.ts b/src/domain/space/account/account.resolver.mutations.ts index 549dfd245c..dd83acb666 100644 --- a/src/domain/space/account/account.resolver.mutations.ts +++ b/src/domain/space/account/account.resolver.mutations.ts @@ -103,7 +103,7 @@ export class AccountResolverMutations { if (!rootSpace.community?.id) { throw new RelationshipNotFoundException( - `Unable to find community with id ${rootSpace.community?.id} `, + `Unable to find community with id ${rootSpace.community?.id}`, LogContext.ACCOUNT ); } @@ -251,7 +251,7 @@ export class AccountResolverMutations { const spaceDefaults = space.account.defaults; if (!spaceDefaults) { throw new RelationshipNotFoundException( - `Unable to load defaults for space ${spaceDefaultsData.spaceID} `, + `Unable to load defaults for space ${spaceDefaultsData.spaceID}`, LogContext.ACCOUNT ); } From da560f423f2721683e261708cb13ab75ae3686e4 Mon Sep 17 00:00:00 2001 From: Emilia Pavlova Date: Thu, 4 Jul 2024 10:46:48 +0300 Subject: [PATCH 11/17] Clear casting --- .../notification-adapter/notification.payload.builder.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/services/adapters/notification-adapter/notification.payload.builder.ts b/src/services/adapters/notification-adapter/notification.payload.builder.ts index a43169b257..35b6a1e367 100644 --- a/src/services/adapters/notification-adapter/notification.payload.builder.ts +++ b/src/services/adapters/notification-adapter/notification.payload.builder.ts @@ -54,7 +54,6 @@ import { IDiscussion } from '@platform/forum-discussion/discussion.interface'; import { ContributorLookupService } from '@services/infrastructure/contributor-lookup/contributor.lookup.service'; import { IContributor } from '@domain/community/contributor/contributor.interface'; import { IAccount } from '@domain/space/account/account.interface'; -import { Space } from '@domain/space/space/space.entity'; @Injectable() export class NotificationPayloadBuilder { @@ -728,9 +727,9 @@ export class NotificationPayloadBuilder { ): Promise { const basePayload = this.buildBaseEventPayload(triggeredBy); const space = - (await this.communityResolverService.getSpaceForCommunityOrFail( + await this.communityResolverService.getSpaceForCommunityOrFail( community.id - )) as Space; + ); const url = await this.urlGeneratorService.generateUrlForProfile( space.profile ); From 1f98bfced0441c9352134c48290fa3caa8cad4f4 Mon Sep 17 00:00:00 2001 From: Neil Smyth <30729240+techsmyth@users.noreply.github.com> Date: Thu, 4 Jul 2024 16:00:16 +0200 Subject: [PATCH 12/17] remove type on community (#4197) * removed logic for working with cmmunity type * added migration to drop the column --------- --- .../community/community/community.entity.ts | 14 +----- .../community/community.interface.ts | 2 - .../community/community.service.events.ts | 8 +++- .../community/community/community.service.ts | 4 +- .../community/dto/community.dto.create.ts | 2 - src/domain/space/space/space.service.ts | 1 - src/migrations/1719859107990-communityType.ts | 11 +++++ .../activity-adapter/activity.adapter.ts | 48 ++++++++----------- .../notification.payload.builder.ts | 2 +- .../activity.log.builder.service.ts | 1 - ...y.log.dto.entry.member.joined.interface.ts | 6 --- 11 files changed, 43 insertions(+), 56 deletions(-) create mode 100644 src/migrations/1719859107990-communityType.ts diff --git a/src/domain/community/community/community.entity.ts b/src/domain/community/community/community.entity.ts index aa10091293..bf4e490de2 100644 --- a/src/domain/community/community/community.entity.ts +++ b/src/domain/community/community/community.entity.ts @@ -12,15 +12,11 @@ import { ICommunity } from '@domain/community/community/community.interface'; import { AuthorizableEntity } from '@domain/common/entity/authorizable-entity'; import { Application } from '@domain/community/application/application.entity'; import { Communication } from '@domain/communication/communication/communication.entity'; -import { - TINY_TEXT_LENGTH, - UUID_LENGTH, -} from '@src/common/constants/entity.field.length.constants'; +import { UUID_LENGTH } from '@src/common/constants/entity.field.length.constants'; import { CommunityPolicy } from '../community-policy/community.policy.entity'; import { Form } from '@domain/common/form/form.entity'; import { Invitation } from '../invitation/invitation.entity'; import { CommunityGuidelines } from '../community-guidelines/community.guidelines.entity'; -import { SpaceType } from '@common/enums/space.type'; import { PlatformInvitation } from '@platform/invitation'; @Entity() @@ -96,19 +92,13 @@ export class Community }) parentCommunity?: Community; - @Column({ - length: TINY_TEXT_LENGTH, - }) - type!: SpaceType; - @Column({ length: UUID_LENGTH, }) parentID!: string; - constructor(type: SpaceType) { + constructor() { super(); - this.type = type; this.parentID = ''; } } diff --git a/src/domain/community/community/community.interface.ts b/src/domain/community/community/community.interface.ts index 5df591a950..ec05be2c72 100644 --- a/src/domain/community/community/community.interface.ts +++ b/src/domain/community/community/community.interface.ts @@ -8,7 +8,6 @@ import { ICommunityPolicy } from '../community-policy/community.policy.interface import { IForm } from '@domain/common/form/form.interface'; import { IInvitation } from '../invitation/invitation.interface'; import { ICommunityGuidelines } from '../community-guidelines/community.guidelines.interface'; -import { SpaceType } from '@common/enums/space.type'; import { IPlatformInvitation } from '@platform/invitation'; @ObjectType('Community', { @@ -29,7 +28,6 @@ export abstract class ICommunity extends IAuthorizable { guidelines?: ICommunityGuidelines; communication?: ICommunication; - type!: SpaceType; parentID!: string; } diff --git a/src/domain/community/community/community.service.events.ts b/src/domain/community/community/community.service.events.ts index 5e67d82c40..c1f1af21ca 100644 --- a/src/domain/community/community/community.service.events.ts +++ b/src/domain/community/community/community.service.events.ts @@ -8,13 +8,15 @@ import { ActivityInputMemberJoined } from '@services/adapters/activity-adapter/d import { ActivityAdapter } from '@services/adapters/activity-adapter/activity.adapter'; import { SpaceType } from '@common/enums/space.type'; import { IContributor } from '../contributor/contributor.interface'; +import { CommunityResolverService } from '@services/infrastructure/entity-resolver/community.resolver.service'; @Injectable() export class CommunityEventsService { constructor( private contributionReporter: ContributionReporterService, private notificationAdapter: NotificationAdapter, - private activityAdapter: ActivityAdapter + private activityAdapter: ActivityAdapter, + private communityResolverService: CommunityResolverService ) {} public async registerCommunityNewMemberActivity( @@ -47,7 +49,9 @@ export class CommunityEventsService { await this.notificationAdapter.communityNewMember(notificationInput); // Record the contribution events - switch (community.type) { + const space = + await this.communityResolverService.getSpaceForCommunityOrFail(spaceID); + switch (space.type) { case SpaceType.SPACE: this.contributionReporter.spaceJoined( { diff --git a/src/domain/community/community/community.service.ts b/src/domain/community/community/community.service.ts index 6f99d04fbe..dd6a92323b 100644 --- a/src/domain/community/community/community.service.ts +++ b/src/domain/community/community/community.service.ts @@ -95,7 +95,7 @@ export class CommunityService { communityData: CreateCommunityInput, storageAggregator: IStorageAggregator ): Promise { - const community: ICommunity = new Community(communityData.type); + const community: ICommunity = new Community(); community.authorization = new AuthorizationPolicy(); const policy = communityData.policy as ICommunityPolicyDefinition; community.policy = await this.communityPolicyService.createCommunityPolicy( @@ -189,7 +189,7 @@ export class CommunityService { throw new EntityNotFoundException( `Unable to find group with ID: '${groupID}'`, LogContext.COMMUNITY, - { communityId: community.id, communityType: community.type } + { communityId: community.id } ); } return result; diff --git a/src/domain/community/community/dto/community.dto.create.ts b/src/domain/community/community/dto/community.dto.create.ts index 8d185d5535..21bf668d6e 100644 --- a/src/domain/community/community/dto/community.dto.create.ts +++ b/src/domain/community/community/dto/community.dto.create.ts @@ -1,4 +1,3 @@ -import { SpaceType } from '@common/enums/space.type'; import { CreateFormInput } from '@domain/common/form/dto/form.dto.create'; import { CreateCommunityGuidelinesInput } from '@domain/community/community-guidelines/dto/community.guidelines.dto.create'; import { ICommunityPolicyDefinition } from '@domain/community/community-policy/community.policy.definition'; @@ -7,7 +6,6 @@ export class CreateCommunityInput { guidelines!: CreateCommunityGuidelinesInput; name!: string; - type!: SpaceType; policy!: ICommunityPolicyDefinition; applicationForm!: CreateFormInput; } diff --git a/src/domain/space/space/space.service.ts b/src/domain/space/space/space.service.ts index ce37d9df6b..0e484a0d9b 100644 --- a/src/domain/space/space/space.service.ts +++ b/src/domain/space/space/space.service.ts @@ -144,7 +144,6 @@ export class SpaceService { const communityData: CreateCommunityInput = { name: spaceData.profileData.displayName, - type: spaceData.type as SpaceType, policy: communityPolicy, applicationForm: applicationFormData, guidelines: { diff --git a/src/migrations/1719859107990-communityType.ts b/src/migrations/1719859107990-communityType.ts new file mode 100644 index 0000000000..2d86a9444e --- /dev/null +++ b/src/migrations/1719859107990-communityType.ts @@ -0,0 +1,11 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class communityType1719859107990 implements MigrationInterface { + name = 'communityType1719859107990'; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE \`community\` DROP COLUMN \`type\``); + } + + public async down(queryRunner: QueryRunner): Promise {} +} diff --git a/src/services/adapters/activity-adapter/activity.adapter.ts b/src/services/adapters/activity-adapter/activity.adapter.ts index 2b3a72f9bd..ee6a27cb47 100644 --- a/src/services/adapters/activity-adapter/activity.adapter.ts +++ b/src/services/adapters/activity-adapter/activity.adapter.ts @@ -325,7 +325,7 @@ export class ActivityAdapter { const collaborationID = await this.getCollaborationIdFromCommunity( community.id ); - const description = `[${community.type}] '${eventData.contributor.nameID}'`; + const description = `${eventData.contributor.nameID}`; const activity = await this.activityService.createActivity({ triggeredBy: eventData.triggeredBy, collaborationID, @@ -340,6 +340,26 @@ export class ActivityAdapter { return true; } + private async getCollaborationIdFromCommunity(communityID: string) { + const space = await this.entityManager.findOne(Space, { + where: { + community: { + id: communityID, + }, + }, + relations: { + collaboration: true, + }, + }); + if (!space || !space.collaboration) { + throw new EntityNotFoundException( + `Unable to find Collaboration for Community with ID: ${communityID}`, + LogContext.ACTIVITY + ); + } + return space.collaboration.id; + } + public async messageRemoved( eventData: ActivityInputMessageRemoved ): Promise { @@ -536,32 +556,6 @@ export class ActivityAdapter { return contributionResult; } - private async getCollaborationIdFromCommunity(communityId: string) { - const [result]: { - collaborationId: string; - }[] = await this.entityManager.connection.query( - ` - SELECT collaborationId from \`space\` - WHERE \`space\`.\`communityId\` = '${communityId}' UNION - - SELECT collaborationId from \`challenge\` - WHERE \`challenge\`.\`communityId\` = '${communityId}' UNION - - SELECT collaborationId from \`opportunity\` - WHERE \`opportunity\`.\`communityId\` = '${communityId}'; - ` - ); - if (!result) { - this.logger.error( - `Unable to identify Collaboration for provided communityID: ${communityId}`, - undefined, - LogContext.COMMUNITY - ); - return ''; - } - return result.collaborationId; - } - private async getCommunityIdFromUpdates(updatesID: string) { const community = await this.communityRepository .createQueryBuilder('community') diff --git a/src/services/adapters/notification-adapter/notification.payload.builder.ts b/src/services/adapters/notification-adapter/notification.payload.builder.ts index d960a73975..b32107bf99 100644 --- a/src/services/adapters/notification-adapter/notification.payload.builder.ts +++ b/src/services/adapters/notification-adapter/notification.payload.builder.ts @@ -719,7 +719,7 @@ export class NotificationPayloadBuilder { space: { id: space.id, nameID: space.nameID, - type: community.type, + type: space.type, profile: { displayName: space.profile.displayName, url: url, diff --git a/src/services/api/activity-log/activity.log.builder.service.ts b/src/services/api/activity-log/activity.log.builder.service.ts index c2af57f60f..afd09ff242 100644 --- a/src/services/api/activity-log/activity.log.builder.service.ts +++ b/src/services/api/activity-log/activity.log.builder.service.ts @@ -65,7 +65,6 @@ export default class ActivityLogBuilderService implements IActivityLogBuilder { community: community, contributor: contributorJoining, contributorType: contributorType, - communityType: `${community.type}`, }; return activityMemberJoined; } diff --git a/src/services/api/activity-log/dto/activity.log.dto.entry.member.joined.interface.ts b/src/services/api/activity-log/dto/activity.log.dto.entry.member.joined.interface.ts index 27756a2b09..1ba1efd686 100644 --- a/src/services/api/activity-log/dto/activity.log.dto.entry.member.joined.interface.ts +++ b/src/services/api/activity-log/dto/activity.log.dto.entry.member.joined.interface.ts @@ -24,12 +24,6 @@ export abstract class IActivityLogEntryMemberJoined }) contributorType!: CommunityContributorType; - @Field(() => String, { - nullable: false, - description: 'The type of the the Community.', - }) - communityType!: string; - @Field(() => ICommunity, { nullable: false, description: 'The community that was joined.', From 2aac5559b1f2168efce3a954e13b5fcc9b24ff18 Mon Sep 17 00:00:00 2001 From: Neil Smyth <30729240+techsmyth@users.noreply.github.com> Date: Thu, 4 Jul 2024 17:34:28 +0200 Subject: [PATCH 13/17] Matrix adapter api (#4209) * pdated start services for latest synapse + matrix adapter * updated to work with new matrix adapter api * tidied up homeserver * updated package versions * service version update --------- Co-authored-by: Svetoslav --- .build/synapse/homeserver.yaml | 3 +- package-lock.json | 15 ++- package.json | 4 +- quickstart-services.yml | 4 +- .../admin.communication.resolver.mutations.ts | 19 ++-- .../admin.communication.service.ts | 16 +-- ...min.communication.dto.update.room.state.ts | 13 +++ ...communication.dto.update.rooms.joinrule.ts | 7 -- .../communication.adapter.ts | 104 ++++++++---------- 9 files changed, 89 insertions(+), 96 deletions(-) create mode 100644 src/platform/admin/communication/dto/admin.communication.dto.update.room.state.ts delete mode 100644 src/platform/admin/communication/dto/admin.communication.dto.update.rooms.joinrule.ts diff --git a/.build/synapse/homeserver.yaml b/.build/synapse/homeserver.yaml index 7afff2e6be..3269169753 100755 --- a/.build/synapse/homeserver.yaml +++ b/.build/synapse/homeserver.yaml @@ -385,8 +385,7 @@ limit_remote_rooms: #complexity_error: "This room is too complex." # allow server admins to join complex rooms. Default is false. - # - #admins_can_join: true + # admins_can_join: true # Whether to require a user to be in the room to add an alias to it. # Defaults to 'true'. diff --git a/package-lock.json b/package-lock.json index dc42dcdc0d..3e341cc2a0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,15 +1,15 @@ { "name": "alkemio-server", - "version": "0.82.9", + "version": "0.82.10", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "alkemio-server", - "version": "0.82.9", + "version": "0.82.10", "license": "EUPL-1.2", "dependencies": { - "@alkemio/matrix-adapter-lib": "^0.3.6", + "@alkemio/matrix-adapter-lib": "^0.4.1", "@alkemio/notifications-lib": "^0.9.3", "@apollo/server": "^4.10.2", "@elastic/elasticsearch": "^8.4.0", @@ -221,8 +221,9 @@ } }, "node_modules/@alkemio/matrix-adapter-lib": { - "version": "0.3.6", - "license": "EUPL-1.2", + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@alkemio/matrix-adapter-lib/-/matrix-adapter-lib-0.4.1.tgz", + "integrity": "sha512-9EW0tOIeDPPaExF4aBpjZ839+NvU0VwGTuFHJu6PGZxVP6ncIcNtVcgi6T7fsONFMeYoZtcS38ZiH8FesCpI5Q==", "engines": { "node": ">=16.15.0", "npm": ">=8.5.5" @@ -14277,7 +14278,9 @@ } }, "@alkemio/matrix-adapter-lib": { - "version": "0.3.6" + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@alkemio/matrix-adapter-lib/-/matrix-adapter-lib-0.4.1.tgz", + "integrity": "sha512-9EW0tOIeDPPaExF4aBpjZ839+NvU0VwGTuFHJu6PGZxVP6ncIcNtVcgi6T7fsONFMeYoZtcS38ZiH8FesCpI5Q==" }, "@alkemio/notifications-lib": { "version": "0.9.3", diff --git a/package.json b/package.json index ac2a2dda9a..0e160a4d8f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "alkemio-server", - "version": "0.82.9", + "version": "0.82.10", "description": "Alkemio server, responsible for managing the shared Alkemio platform", "author": "Alkemio Foundation", "private": false, @@ -51,7 +51,7 @@ } }, "dependencies": { - "@alkemio/matrix-adapter-lib": "^0.3.6", + "@alkemio/matrix-adapter-lib": "^0.4.1", "@alkemio/notifications-lib": "^0.9.3", "@apollo/server": "^4.10.2", "@elastic/elasticsearch": "^8.4.0", diff --git a/quickstart-services.yml b/quickstart-services.yml index fcd35876cf..e455743c89 100644 --- a/quickstart-services.yml +++ b/quickstart-services.yml @@ -126,7 +126,7 @@ services: synapse: container_name: alkemio_dev_synapse - image: matrixdotorg/synapse:v1.82.0 + image: matrixdotorg/synapse:v1.98.0 depends_on: - postgres restart: always @@ -234,7 +234,7 @@ services: - 'host.docker.internal:host-gateway' container_name: alkemio_dev_matrix_adapter hostname: matrix-adapter - image: alkemio/matrix-adapter:v0.3.1 + image: alkemio/matrix-adapter:v0.4.3 platform: linux/x86_64 environment: - RABBITMQ_HOST diff --git a/src/platform/admin/communication/admin.communication.resolver.mutations.ts b/src/platform/admin/communication/admin.communication.resolver.mutations.ts index 55dcbf049b..301f3b2982 100644 --- a/src/platform/admin/communication/admin.communication.resolver.mutations.ts +++ b/src/platform/admin/communication/admin.communication.resolver.mutations.ts @@ -10,8 +10,9 @@ import { CommunicationAdminEnsureAccessInput } from './dto/admin.communication.d import { AuthorizationService } from '@core/authorization/authorization.service'; import { AdminCommunicationService } from './admin.communication.service'; import { CommunicationAdminRemoveOrphanedRoomInput } from './dto/admin.communication.dto.remove.orphaned.room'; -import { CommunicationAdminUpdateRoomsJoinRuleInput } from './dto/admin.communication.dto.update.rooms.joinrule'; +import { CommunicationAdminUpdateRoomStateInput } from './dto/admin.communication.dto.update.room.state'; import { GLOBAL_POLICY_ADMIN_COMMUNICATION_GRANT } from '@common/constants/authorization/global.policy.constants'; +import { CommunicationRoomResult } from '@services/adapters/communication-adapter/dto/communication.dto.room.result'; @Resolver() export class AdminCommunicationResolverMutations { @@ -75,22 +76,24 @@ export class AdminCommunicationResolverMutations { @UseGuards(GraphqlGuard) @Mutation(() => Boolean, { - description: 'Allow updating the rule for joining rooms: public or invite.', + description: 'Allow updating the state flags of a particular rule.', }) @Profiling.api - async adminCommunicationUpdateRoomsJoinRule( - @Args('changeRoomAccessData') - changeRoomAccessData: CommunicationAdminUpdateRoomsJoinRuleInput, + async adminCommunicationUpdateRoomState( + @Args('roomStateData') + roomStateData: CommunicationAdminUpdateRoomStateInput, @CurrentUser() agentInfo: AgentInfo - ): Promise { + ): Promise { await this.authorizationService.grantAccessOrFail( agentInfo, this.communicationGlobalAdminPolicy, AuthorizationPrivilege.GRANT, `communications admin update join rule on all rooms: ${agentInfo.email}` ); - return await this.adminCommunicationService.setMatrixRoomsJoinRule( - changeRoomAccessData.isPublic + return await this.adminCommunicationService.updateMatrixRoomState( + roomStateData.roomID, + roomStateData.isWorldVisible, + roomStateData.isPublic ); } } diff --git a/src/platform/admin/communication/admin.communication.service.ts b/src/platform/admin/communication/admin.communication.service.ts index 19856fdea0..e9ab4c7356 100644 --- a/src/platform/admin/communication/admin.communication.service.ts +++ b/src/platform/admin/communication/admin.communication.service.ts @@ -90,10 +90,6 @@ export class AdminCommunicationService { } } - // Obtain the access mode for the room - result.joinRule = await this.communicationAdapter.getRoomJoinRule( - room.externalRoomID - ); return result; } @@ -123,10 +119,14 @@ export class AdminCommunicationService { return true; } - async setMatrixRoomsJoinRule(isPublic: boolean) { - const roomsUsed = await this.getRoomsUsed(); - return await this.communicationAdapter.setMatrixRoomsGuestAccess( - roomsUsed, + async updateMatrixRoomState( + roomID: string, + isPublic: boolean, + isWorldVisible: boolean + ) { + return await this.communicationAdapter.updateMatrixRoomState( + roomID, + isWorldVisible, isPublic ); } diff --git a/src/platform/admin/communication/dto/admin.communication.dto.update.room.state.ts b/src/platform/admin/communication/dto/admin.communication.dto.update.room.state.ts new file mode 100644 index 0000000000..46d9f96ec7 --- /dev/null +++ b/src/platform/admin/communication/dto/admin.communication.dto.update.room.state.ts @@ -0,0 +1,13 @@ +import { Field, InputType } from '@nestjs/graphql'; + +@InputType() +export class CommunicationAdminUpdateRoomStateInput { + @Field(() => Boolean, { nullable: false }) + isPublic!: boolean; + + @Field(() => Boolean, { nullable: false }) + isWorldVisible!: boolean; + + @Field(() => String, { nullable: false }) + roomID!: string; +} diff --git a/src/platform/admin/communication/dto/admin.communication.dto.update.rooms.joinrule.ts b/src/platform/admin/communication/dto/admin.communication.dto.update.rooms.joinrule.ts deleted file mode 100644 index 5c9f88b368..0000000000 --- a/src/platform/admin/communication/dto/admin.communication.dto.update.rooms.joinrule.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { Field, InputType } from '@nestjs/graphql'; - -@InputType() -export class CommunicationAdminUpdateRoomsJoinRuleInput { - @Field(() => Boolean, { nullable: false }) - isPublic!: boolean; -} diff --git a/src/services/adapters/communication-adapter/communication.adapter.ts b/src/services/adapters/communication-adapter/communication.adapter.ts index f9e6fa852b..5be1e20471 100644 --- a/src/services/adapters/communication-adapter/communication.adapter.ts +++ b/src/services/adapters/communication-adapter/communication.adapter.ts @@ -19,6 +19,7 @@ import { RoomAddMessageReactionPayload, RoomRemoveMessageReactionPayload, RoomAddMessageReactionResponsePayload, + UpdateRoomStatePayload, } from '@alkemio/matrix-adapter-lib'; import { RoomDetailsPayload } from '@alkemio/matrix-adapter-lib'; import { RoomDetailsResponsePayload } from '@alkemio/matrix-adapter-lib'; @@ -45,10 +46,6 @@ import { RemoveRoomPayload } from '@alkemio/matrix-adapter-lib'; import { RemoveRoomResponsePayload } from '@alkemio/matrix-adapter-lib'; import { RoomMembersPayload } from '@alkemio/matrix-adapter-lib'; import { RoomMembersResponsePayload } from '@alkemio/matrix-adapter-lib'; -import { RoomJoinRulePayload } from '@alkemio/matrix-adapter-lib'; -import { RoomJoinRuleResponsePayload } from '@alkemio/matrix-adapter-lib'; -import { UpdateRoomsGuestAccessPayload } from '@alkemio/matrix-adapter-lib'; -import { UpdateRoomsGuestAccessResponsePayload } from '@alkemio/matrix-adapter-lib'; import { SendMessageToUserPayload } from '@alkemio/matrix-adapter-lib'; import { SendMessageToUserResponsePayload } from '@alkemio/matrix-adapter-lib'; import { RoomsUserDirectPayload } from '@alkemio/matrix-adapter-lib'; @@ -285,21 +282,9 @@ export class CommunicationAdapter { response ); this.logResponsePayload(eventType, responseData, eventID); - return { - ...responseData.room, - messages: responseData.room.messages.map(message => { - return { - ...message, - senderType: 'user', - reactions: message.reactions.map(reaction => { - return { - ...reaction, - senderType: 'user', - }; - }), - }; - }), - }; + return this.convertRoomDetailsResponseToCommunicationRoomResult( + responseData + ); } catch (err: any) { this.logInteractionError(eventType, err, eventID); throw new MatrixEntityNotFoundException( @@ -309,6 +294,26 @@ export class CommunicationAdapter { } } + private convertRoomDetailsResponseToCommunicationRoomResult( + roomDetailsResponse: RoomDetailsResponsePayload + ): CommunicationRoomResult { + return { + ...roomDetailsResponse.room, + messages: roomDetailsResponse.room.messages.map(message => { + return { + ...message, + senderType: 'user', + reactions: message.reactions.map(reaction => { + return { + ...reaction, + senderType: 'user', + }; + }), + }; + }), + }; + } + async deleteMessage( deleteMessageData: CommunicationDeleteMessageInput ): Promise { @@ -334,7 +339,7 @@ export class CommunicationAdapter { } catch (err: any) { this.logInteractionError(eventType, err, eventID); throw new MatrixEntityNotFoundException( - `Failed to delete message from room: ${err}`, + 'Failed to delete message from room', LogContext.COMMUNICATION ); } @@ -823,11 +828,17 @@ export class CommunicationAdapter { return userIDs; } - async getRoomJoinRule(roomID: string): Promise { - const eventType = MatrixAdapterEventType.ROOM_JOIN_RULE; - const inputPayload: RoomJoinRulePayload = { + async updateMatrixRoomState( + roomID: string, + worldVisible = true, + allowGuests = true + ): Promise { + const eventType = MatrixAdapterEventType.UPDATE_ROOM_STATE; + const inputPayload: UpdateRoomStatePayload = { triggeredBy: '', - roomID: roomID, + roomID, + historyWorldVisibile: worldVisible, + allowJoining: allowGuests, }; const eventID = this.logInputPayload(eventType, inputPayload); const response = this.matrixAdapterClient.send( @@ -836,49 +847,20 @@ export class CommunicationAdapter { ); try { - const responseData = await firstValueFrom( + const responseData = await firstValueFrom( response ); this.logResponsePayload(eventType, responseData, eventID); - return responseData.rule; - } catch (err: any) { - this.logInteractionError(eventType, err, eventID); - this.logger.verbose?.( - `Unable to get room join rule (${roomID}): ${err}`, - LogContext.COMMUNICATION + return this.convertRoomDetailsResponseToCommunicationRoomResult( + responseData ); - throw err; - } - } - - async setMatrixRoomsGuestAccess(roomIDs: string[], allowGuests = true) { - const eventType = MatrixAdapterEventType.UPDATE_ROOMS_GUEST_ACCESS; - const inputPayload: UpdateRoomsGuestAccessPayload = { - triggeredBy: '', - roomIDs, - allowGuests, - }; - const eventID = this.logInputPayload(eventType, inputPayload); - const response = this.matrixAdapterClient.send( - { cmd: eventType }, - inputPayload - ); - - try { - const responseData = - await firstValueFrom(response); - this.logResponsePayload(eventType, responseData, eventID); - return responseData.success; } catch (err: any) { this.logInteractionError(eventType, err, eventID); - this.logger.error( - `Unable to change guest access for rooms to (${ - allowGuests ? 'Public' : 'Private' - }): ${err}`, - err?.stack, - LogContext.COMMUNICATION - ); - return false; + const message = `Unable to change guest access for rooms to (${ + allowGuests ? 'Public' : 'Private' + }): ${err}`; + this.logger.error(message, err?.stack, LogContext.COMMUNICATION); + throw err; } } From 7840ee3e34609dc86167568c1fa8a04a0ebdd26a Mon Sep 17 00:00:00 2001 From: Neil Smyth Date: Fri, 5 Jul 2024 07:35:09 +0200 Subject: [PATCH 14/17] updated matrix adapter lib version which was somehow lost --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 63c6070194..06a78e4acf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "0.82.10", "license": "EUPL-1.2", "dependencies": { - "@alkemio/matrix-adapter-lib": "^0.3.6", + "@alkemio/matrix-adapter-lib": "^0.4.1", "@alkemio/notifications-lib": "^0.9.6", "@apollo/server": "^4.10.2", "@elastic/elasticsearch": "^8.4.0", diff --git a/package.json b/package.json index cf59573d0e..b45cee8e25 100644 --- a/package.json +++ b/package.json @@ -51,7 +51,7 @@ } }, "dependencies": { - "@alkemio/matrix-adapter-lib": "^0.3.6", + "@alkemio/matrix-adapter-lib": "^0.4.1", "@alkemio/notifications-lib": "^0.9.6", "@apollo/server": "^4.10.2", "@elastic/elasticsearch": "^8.4.0", From a51c40ff551aafb4ae7a665dd600d68f5e694cef Mon Sep 17 00:00:00 2001 From: Svetoslav Petkov Date: Fri, 5 Jul 2024 12:45:01 +0300 Subject: [PATCH 15/17] Search: fixes and improvements (#4196) * whiteboards, callouts, posts with the correct root space id * proper loading of ALL entities with the correct data * search in whiteboards displayed as callouts --------- Co-authored-by: Valentin Yanakiev Co-authored-by: Neil Smyth <30729240+techsmyth@users.noreply.github.com> Co-authored-by: Carlos Cano --- src/common/enums/search.result.type.ts | 1 + .../admin.search.ingest.resolver.mutations.ts | 15 +- .../dto/search.result.entry.interface.ts | 2 + .../v1/search.result.builder.interface.ts | 1 + .../v1/search.result.builder.service.ts | 7 +- .../v2/extract/search.extract.service.ts | 7 + .../search/v2/ingest/search.ingest.service.ts | 218 +++++++++++------- .../search/v2/result/search.result.service.ts | 49 +++- 8 files changed, 205 insertions(+), 95 deletions(-) diff --git a/src/common/enums/search.result.type.ts b/src/common/enums/search.result.type.ts index 71ff8937b6..6504781852 100644 --- a/src/common/enums/search.result.type.ts +++ b/src/common/enums/search.result.type.ts @@ -9,6 +9,7 @@ export enum SearchResultType { USERGROUP = 'usergroup', POST = 'post', CALLOUT = 'callout', + WHITEBOARD = 'whiteboard', } registerEnumType(SearchResultType, { diff --git a/src/platform/admin/search/admin.search.ingest.resolver.mutations.ts b/src/platform/admin/search/admin.search.ingest.resolver.mutations.ts index 884471782d..819302dd1a 100644 --- a/src/platform/admin/search/admin.search.ingest.resolver.mutations.ts +++ b/src/platform/admin/search/admin.search.ingest.resolver.mutations.ts @@ -2,7 +2,7 @@ import { Inject, LoggerService, UseGuards } from '@nestjs/common'; import { Mutation, Resolver } from '@nestjs/graphql'; import { CurrentUser, Profiling } from '@src/common/decorators'; import { GraphqlGuard } from '@core/authorization/graphql.guard'; -import { AuthorizationPrivilege } from '@common/enums'; +import { AuthorizationPrivilege, LogContext } from '@common/enums'; import { PlatformAuthorizationPolicyService } from '@platform/authorization/platform.authorization.policy.service'; import { AgentInfo } from '@core/authentication.agent.info/agent.info'; import { AuthorizationService } from '@core/authorization/authorization.service'; @@ -61,9 +61,20 @@ export class AdminSearchIngestResolverMutations { this.taskService.updateTaskResults(task.id, 'Indices recreated'); return this.searchIngestService.ingest(task); }) - .then(() => this.taskService.complete(task.id)) + .then(() => { + this.taskService.complete(task.id); + this.logger.verbose?.( + 'Search ingest from scratch completed', + LogContext.SEARCH_INGEST + ); + }) .catch(async e => { await this.taskService.updateTaskErrors(task.id, e?.message); + this.logger.error?.( + `Search ingest from scratch completed with error: ${e?.message}`, + e?.stack, + LogContext.SEARCH_INGEST + ); return this.taskService.complete(task.id, TaskStatus.ERRORED); }); diff --git a/src/services/api/search/dto/search.result.entry.interface.ts b/src/services/api/search/dto/search.result.entry.interface.ts index 6a77d94bf9..95504cbd03 100644 --- a/src/services/api/search/dto/search.result.entry.interface.ts +++ b/src/services/api/search/dto/search.result.entry.interface.ts @@ -29,6 +29,8 @@ import { ISearchResultCallout } from './search.result.dto.entry.callout'; return ISearchResultUserGroup; case SearchResultType.CALLOUT: return ISearchResultCallout; + case SearchResultType.WHITEBOARD: + return ISearchResultCallout; } throw new RelationshipNotFoundException( diff --git a/src/services/api/search/v1/search.result.builder.interface.ts b/src/services/api/search/v1/search.result.builder.interface.ts index ce446dba3f..5e71a3c81a 100644 --- a/src/services/api/search/v1/search.result.builder.interface.ts +++ b/src/services/api/search/v1/search.result.builder.interface.ts @@ -22,4 +22,5 @@ export interface ISearchResultBuilder { [SearchResultType.USERGROUP]: SearchResultBuilderFunction; [SearchResultType.POST]: SearchResultBuilderFunction; [SearchResultType.CALLOUT]: SearchResultBuilderFunction; + [SearchResultType.WHITEBOARD]: SearchResultBuilderFunction; } diff --git a/src/services/api/search/v1/search.result.builder.service.ts b/src/services/api/search/v1/search.result.builder.service.ts index cddeb1139d..60fd0b9776 100644 --- a/src/services/api/search/v1/search.result.builder.service.ts +++ b/src/services/api/search/v1/search.result.builder.service.ts @@ -47,7 +47,6 @@ export default class SearchResultBuilderService private readonly calloutService: CalloutService, private readonly entityManager: EntityManager ) {} - async [SearchResultType.SPACE](rawSearchResult: ISearchResult) { const space = await this.spaceService.getSpaceOrFail( rawSearchResult.result.id @@ -245,4 +244,10 @@ export default class SearchResultBuilderService }; return searchResultCallout; } + + async [SearchResultType.WHITEBOARD]( + _rawSearchResult: ISearchResult + ): Promise { + throw new Error('Method not implemented.'); + } } diff --git a/src/services/api/search/v2/extract/search.extract.service.ts b/src/services/api/search/v2/extract/search.extract.service.ts index a485567933..d5900e9bca 100644 --- a/src/services/api/search/v2/extract/search.extract.service.ts +++ b/src/services/api/search/v2/extract/search.extract.service.ts @@ -176,6 +176,13 @@ export class SearchExtractService { const filteredIndices = entityTypesFilter.map( type => TYPE_TO_INDEX(this.indexPattern)[type as SearchEntityTypes] ); + // todo: remove this when whiteboard is a separate search result + // include the whiteboards, if the callout is included + if (entityTypesFilter.includes(SearchEntityTypes.CALLOUT)) { + filteredIndices.push( + TYPE_TO_INDEX(this.indexPattern)[SearchEntityTypes.WHITEBOARD] + ); + } if (onlyPublicResults) { const publicIndices = Object.values( diff --git a/src/services/api/search/v2/ingest/search.ingest.service.ts b/src/services/api/search/v2/ingest/search.ingest.service.ts index 25a0681a37..5a00a03da4 100644 --- a/src/services/api/search/v2/ingest/search.ingest.service.ts +++ b/src/services/api/search/v2/ingest/search.ingest.service.ts @@ -222,51 +222,60 @@ export class SearchIngestService { { index: `${this.indexPattern}spaces`, fetchFn: this.fetchSpacesLevel0.bind(this), + countFn: this.fetchSpacesLevel0Count.bind(this), batchSize: 100, }, { index: `${this.indexPattern}subspaces`, fetchFn: this.fetchSpacesLevel1.bind(this), + countFn: this.fetchSpacesLevel1Count.bind(this), batchSize: 100, }, { index: `${this.indexPattern}subspaces`, fetchFn: this.fetchSpacesLevel2.bind(this), + countFn: this.fetchSpacesLevel2Count.bind(this), batchSize: 100, }, { index: `${this.indexPattern}organizations`, - fetchFn: this.fetchOrganization.bind(this), + fetchFn: this.fetchOrganizations.bind(this), + countFn: this.fetchOrganizationsCount.bind(this), batchSize: 100, }, { index: `${this.indexPattern}users`, fetchFn: this.fetchUsers.bind(this), + countFn: this.fetchUsersCount.bind(this), batchSize: 100, }, { - index: `${this.indexPattern}posts`, - fetchFn: this.fetchPosts.bind(this), + index: `${this.indexPattern}callouts`, + fetchFn: this.fetchCallout.bind(this), + countFn: this.fetchCalloutCount.bind(this), batchSize: 20, }, { - index: `${this.indexPattern}callouts`, - fetchFn: this.fetchCallout.bind(this), + index: `${this.indexPattern}posts`, + fetchFn: this.fetchPosts.bind(this), + countFn: this.fetchPostsCount.bind(this), batchSize: 20, }, { index: `${this.indexPattern}whiteboards`, fetchFn: this.fetchWhiteboard.bind(this), - batchSize: 10, + countFn: this.fetchWhiteboardCount.bind(this), + batchSize: 20, }, ]; return asyncReduceSequential( params, - async (acc, { index, fetchFn, batchSize }) => { + async (acc, { index, fetchFn, countFn, batchSize }) => { const batches = await this.fetchAndIngest( index, fetchFn, + countFn, batchSize, task ); @@ -285,26 +294,20 @@ export class SearchIngestService { private async fetchAndIngest( index: string, fetchFn: (start: number, limit: number) => Promise, + countFn: () => Promise, batchSize: number, task: Task ): Promise { let start = 0; const results: IngestBatchResultType[] = []; - while (true) { - const fetched = await fetchFn(start, batchSize); - // if there are no results fetched, we have reached the end - if (!fetched.length) { - break; - } + const total = await countFn(); - const result = await this.ingestBulk(fetched, index, task); - results.push(result); - // some statement are not directly querying a table, but instead parent entities - // so the total count is not predictable; in that case an extra query has to be made - // to ensure there is no more data - if (!fetched.length) { - break; + while (start <= total) { + const fetched = await fetchFn(start, batchSize); + if (fetched.length) { + const result = await this.ingestBulk(fetched, index, task); + results.push(result); } start += batchSize; @@ -387,6 +390,14 @@ export class SearchIngestService { } } // TODO: validate the loaded data for missing relations - https://github.com/alkem-io/server/issues/3699 + private fetchSpacesLevel0Count() { + return this.entityManager.count(Space, { + where: { + account: { license: { visibility: Not(SpaceVisibility.ARCHIVED) } }, + level: SpaceLevel.SPACE, + }, + }); + } private fetchSpacesLevel0(start: number, limit: number) { return this.entityManager .find(Space, { @@ -422,6 +433,14 @@ export class SearchIngestService { }); } + private fetchSpacesLevel1Count() { + return this.entityManager.count(Space, { + where: { + account: { license: { visibility: Not(SpaceVisibility.ARCHIVED) } }, + level: SpaceLevel.CHALLENGE, + }, + }); + } private fetchSpacesLevel1(start: number, limit: number) { return this.entityManager .find(Space, { @@ -460,6 +479,14 @@ export class SearchIngestService { }); } + private fetchSpacesLevel2Count() { + return this.entityManager.count(Space, { + where: { + account: { license: { visibility: Not(SpaceVisibility.ARCHIVED) } }, + level: SpaceLevel.OPPORTUNITY, + }, + }); + } private fetchSpacesLevel2(start: number, limit: number) { return this.entityManager .find(Space, { @@ -498,7 +525,10 @@ export class SearchIngestService { }); } - private fetchOrganization(start: number, limit: number) { + private fetchOrganizationsCount() { + return this.entityManager.count(Organization); + } + private fetchOrganizations(start: number, limit: number) { return this.entityManager .find(Organization, { loadEagerRelations: false, @@ -524,6 +554,11 @@ export class SearchIngestService { }); } + private fetchUsersCount() { + return this.entityManager.count(User, { + where: { serviceProfile: false }, + }); + } private fetchUsers(start: number, limit: number) { return this.entityManager .find(User, { @@ -557,6 +592,16 @@ export class SearchIngestService { ); } + private fetchCalloutCount() { + return this.entityManager.count(Space, { + loadEagerRelations: false, + where: { + account: { + license: { visibility: Not(SpaceVisibility.ARCHIVED) }, + }, + }, + }); + } private fetchCallout(start: number, limit: number) { return this.entityManager .find(Space, { @@ -568,6 +613,9 @@ export class SearchIngestService { }, relations: { account: { license: true }, + parentSpace: { + parentSpace: true, + }, collaboration: { callouts: { framing: { @@ -579,6 +627,7 @@ export class SearchIngestService { select: { id: true, account: { id: true, license: { visibility: true } }, + parentSpace: { id: true, parentSpace: { id: true } }, collaboration: { id: true, callouts: { @@ -605,7 +654,10 @@ export class SearchIngestService { license: { visibility: space?.account?.license?.visibility ?? EMPTY_VALUE, }, - spaceID: space.id, + spaceID: + space.parentSpace?.parentSpace?.id ?? + space.parentSpace?.id ?? + space.id, collaborationID: space?.collaboration?.id ?? EMPTY_VALUE, profile: { ...callout.framing.profile, @@ -617,6 +669,16 @@ export class SearchIngestService { ); } + private fetchWhiteboardCount() { + return this.entityManager.count(Space, { + loadEagerRelations: false, + where: { + account: { + license: { visibility: Not(SpaceVisibility.ARCHIVED) }, + }, + }, + }); + } private fetchWhiteboard(start: number, limit: number) { return this.entityManager .find(Space, { @@ -642,6 +704,9 @@ export class SearchIngestService { }, }, }, + parentSpace: { + parentSpace: true, + }, }, select: { id: true, @@ -671,13 +736,20 @@ export class SearchIngestService { }, }, }, + parentSpace: { + id: true, + parentSpace: { + id: true, + }, + }, }, skip: start, take: limit, }) .then(spaces => { return spaces.flatMap(space => { - return space.collaboration?.callouts + const callouts = space.collaboration?.callouts; + return callouts ?.flatMap(callout => { // a callout can have whiteboard in the framing // AND whiteboards in the contributions @@ -699,7 +771,10 @@ export class SearchIngestService { visibility: space?.account?.license?.visibility ?? EMPTY_VALUE, }, - spaceID: space.id, + spaceID: + space?.parentSpace?.parentSpace?.id ?? + space?.parentSpace?.id ?? + space.id, calloutID: callout.id, collaborationID: space?.collaboration?.id ?? EMPTY_VALUE, profile: { @@ -735,7 +810,10 @@ export class SearchIngestService { visibility: space?.account?.license?.visibility ?? EMPTY_VALUE, }, - spaceID: space.id, + spaceID: + space?.parentSpace?.parentSpace?.id ?? + space?.parentSpace?.id ?? + space.id, calloutID: callout.id, collaborationID: space?.collaboration?.id ?? EMPTY_VALUE, profile: { @@ -755,6 +833,16 @@ export class SearchIngestService { }); } + private fetchPostsCount() { + return this.entityManager.count(Space, { + loadEagerRelations: false, + where: { + account: { + license: { visibility: Not(SpaceVisibility.ARCHIVED) }, + }, + }, + }); + } private fetchPosts(start: number, limit: number) { return this.entityManager .find(Space, { @@ -775,27 +863,8 @@ export class SearchIngestService { }, }, }, - subspaces: { - collaboration: { - callouts: { - contributions: { - post: { - profile: profileRelationOptions, - }, - }, - }, - }, - subspaces: { - collaboration: { - callouts: { - contributions: { - post: { - profile: profileRelationOptions, - }, - }, - }, - }, - }, + parentSpace: { + parentSpace: true, }, }, select: { @@ -817,42 +886,10 @@ export class SearchIngestService { }, }, }, - subspaces: { + parentSpace: { id: true, - collaboration: { + parentSpace: { id: true, - callouts: { - id: true, - contributions: { - id: true, - post: { - id: true, - createdBy: true, - createdDate: true, - nameID: true, - profile: profileSelectOptions, - }, - }, - }, - }, - subspaces: { - id: true, - collaboration: { - id: true, - callouts: { - id: true, - contributions: { - id: true, - post: { - id: true, - createdBy: true, - createdDate: true, - nameID: true, - profile: profileSelectOptions, - }, - }, - }, - }, }, }, }, @@ -861,9 +898,11 @@ export class SearchIngestService { }) .then(spaces => { const posts: any[] = []; - spaces.forEach(space => - space?.collaboration?.callouts?.forEach(callout => - callout?.contributions?.forEach(contribution => { + spaces.forEach(space => { + const callouts = space?.collaboration?.callouts; + callouts?.forEach(callout => { + const contributions = callout?.contributions; + contributions?.forEach(contribution => { if (!contribution.post) { return; } @@ -874,7 +913,10 @@ export class SearchIngestService { visibility: space?.account?.license?.visibility ?? EMPTY_VALUE, }, - spaceID: space.id, + spaceID: + space.parentSpace?.parentSpace?.id ?? + space.parentSpace?.id ?? + space.id, calloutID: callout.id, collaborationID: space?.collaboration?.id ?? EMPTY_VALUE, profile: { @@ -883,9 +925,9 @@ export class SearchIngestService { tagsets: undefined, }, }); - }) - ) - ); + }); + }); + }); return posts; }); @@ -895,7 +937,7 @@ export class SearchIngestService { const processTagsets = (tagsets: Tagset[] | undefined) => { return tagsets?.flatMap(tagset => tagset.tags).join(' '); }; -// todo: maybe look for text in the shapes also + const extractTextFromWhiteboardContent = (content: string): string => { if (!content) { return ''; diff --git a/src/services/api/search/v2/result/search.result.service.ts b/src/services/api/search/v2/result/search.result.service.ts index febecb83f9..9b0a0b9ff7 100644 --- a/src/services/api/search/v2/result/search.result.service.ts +++ b/src/services/api/search/v2/result/search.result.service.ts @@ -395,8 +395,42 @@ export class SearchResultService { const whiteboardIds = rawSearchResults.map(hit => hit.result.id); - const callouts = await this.entityManager.findBy(Callout, { - id: In(whiteboardIds), + const callouts = await this.entityManager.find(Callout, { + where: [ + { + framing: { + whiteboard: { + id: In(whiteboardIds), + }, + }, + }, + { + contributions: { + whiteboard: { + id: In(whiteboardIds), + }, + }, + }, + ], + relations: { + framing: { whiteboard: true }, + contributions: { whiteboard: true }, + }, + select: { + framing: { + id: true, + whiteboard: { + id: true, + }, + }, + contributions: { + id: true, + whiteboard: { + id: true, + }, + post: { id: true }, + }, + }, }); // usually the authorization is last but here it might be more expensive than usual // find the authorized post first, then get the parents, and map the results @@ -413,12 +447,16 @@ export class SearchResultService { return parents .map(parent => { const rawSearchResult = rawSearchResults.find( - hit => hit.result.id === parent.callout.id + hit => + hit.result.id === parent.callout.framing.whiteboard?.id || + parent.callout.contributions?.some( + contribution => hit.result.id === contribution.whiteboard?.id + ) ); if (!rawSearchResult) { this.logger.error( - `Unable to find raw search result for Callout: ${parent.callout.id}`, + `Unable to find raw search result for Whiteboard: ${parent.callout.id}`, undefined, LogContext.SEARCH ); @@ -427,6 +465,9 @@ export class SearchResultService { return { ...rawSearchResult, + // todo remove when whiteboard is a separate search result + // patch this so it displays the search result as a callout + type: SearchEntityTypes.CALLOUT, callout: parent.callout, space: parent.space, }; From 24950e26b054c2cc4c375b3b36d13152c5abcec5 Mon Sep 17 00:00:00 2001 From: Carlos Cano Date: Fri, 5 Jul 2024 14:10:23 +0200 Subject: [PATCH 16/17] Fix storage query (#4205) * Fix #4194 * simplification --------- Co-authored-by: Neil Smyth <30729240+techsmyth@users.noreply.github.com> Co-authored-by: Svetoslav --- .../storage/storage-bucket/storage.bucket.service.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/domain/storage/storage-bucket/storage.bucket.service.ts b/src/domain/storage/storage-bucket/storage.bucket.service.ts index 677474bef3..0cad665e79 100644 --- a/src/domain/storage/storage-bucket/storage.bucket.service.ts +++ b/src/domain/storage/storage-bucket/storage.bucket.service.ts @@ -289,11 +289,10 @@ export class StorageBucketService { .where('document.storageBucketId = :storageBucketId', { storageBucketId: storage.id, }) - .addSelect('SUM(size)', 'totalSize') - .getRawOne(); + .select('SUM(size)', 'totalSize') + .getRawOne<{ totalSize: number }>(); - if (!documentsSize || !documentsSize.totalSize) return 0; - return documentsSize.totalSize; + return documentsSize?.totalSize ?? 0; } public async getFilteredDocuments( From e981c92364c0d8397fbedacc6b94df94ae2eda29 Mon Sep 17 00:00:00 2001 From: Svetoslav Petkov Date: Fri, 5 Jul 2024 15:19:42 +0300 Subject: [PATCH 17/17] version bump (#4212) --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 06a78e4acf..df53af6e52 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "alkemio-server", - "version": "0.82.10", + "version": "0.83.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "alkemio-server", - "version": "0.82.10", + "version": "0.83.0", "license": "EUPL-1.2", "dependencies": { "@alkemio/matrix-adapter-lib": "^0.4.1", diff --git a/package.json b/package.json index b45cee8e25..1a0aabfed6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "alkemio-server", - "version": "0.82.10", + "version": "0.83.0", "description": "Alkemio server, responsible for managing the shared Alkemio platform", "author": "Alkemio Foundation", "private": false,