2929 fetch emails from backend and display them in the UI (#3092)
* sending mock data from the resolver * add sql raw query to the resolver * improve query * fix email component css * fix query * css adjustments * create hard limit for mail display * fix display name ellipsis * add service * fetching email on company page is working * graphql generate * move queries into separate files * add types * renaming * add early return * modified according to comments * graphql data generate * fix bug after renaming * fix issue with mock data
This commit is contained in:
@ -7,6 +7,7 @@ import { AuthModule } from 'src/core/auth/auth.module';
|
||||
import { ApiRestModule } from 'src/core/api-rest/api-rest.module';
|
||||
import { FeatureFlagModule } from 'src/core/feature-flag/feature-flag.module';
|
||||
import { OpenApiModule } from 'src/core/open-api/open-api.module';
|
||||
import { TimelineMessagingModule } from 'src/core/messaging/timeline-messaging.module';
|
||||
|
||||
import { AnalyticsModule } from './analytics/analytics.module';
|
||||
import { FileModule } from './file/file.module';
|
||||
@ -24,6 +25,7 @@ import { ClientConfigModule } from './client-config/client-config.module';
|
||||
ApiRestModule,
|
||||
OpenApiModule,
|
||||
FeatureFlagModule,
|
||||
TimelineMessagingModule,
|
||||
],
|
||||
exports: [
|
||||
AuthModule,
|
||||
@ -31,6 +33,7 @@ import { ClientConfigModule } from './client-config/client-config.module';
|
||||
UserModule,
|
||||
AnalyticsModule,
|
||||
FeatureFlagModule,
|
||||
TimelineMessagingModule,
|
||||
],
|
||||
})
|
||||
export class CoreModule {}
|
||||
|
||||
@ -0,0 +1,13 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
|
||||
import { TimelineMessagingResolver } from 'src/core/messaging/timeline-messaging.resolver';
|
||||
import { TimelineMessagingService } from 'src/core/messaging/timeline-messaging.service';
|
||||
import { TypeORMModule } from 'src/database/typeorm/typeorm.module';
|
||||
import { DataSourceModule } from 'src/metadata/data-source/data-source.module';
|
||||
|
||||
@Module({
|
||||
imports: [DataSourceModule, TypeORMModule],
|
||||
exports: [],
|
||||
providers: [TimelineMessagingResolver, TimelineMessagingService],
|
||||
})
|
||||
export class TimelineMessagingModule {}
|
||||
@ -0,0 +1,77 @@
|
||||
import { Args, Query, Field, Resolver, ObjectType } from '@nestjs/graphql';
|
||||
import { UseGuards } from '@nestjs/common';
|
||||
|
||||
import { Column, Entity } from 'typeorm';
|
||||
|
||||
import { JwtAuthGuard } from 'src/guards/jwt.auth.guard';
|
||||
import { Workspace } from 'src/core/workspace/workspace.entity';
|
||||
import { AuthWorkspace } from 'src/decorators/auth-workspace.decorator';
|
||||
import { TimelineMessagingService } from 'src/core/messaging/timeline-messaging.service';
|
||||
|
||||
@Entity({ name: 'timelineThread', schema: 'core' })
|
||||
@ObjectType('TimelineThread')
|
||||
class TimelineThread {
|
||||
@Field()
|
||||
@Column()
|
||||
read: boolean;
|
||||
|
||||
@Field()
|
||||
@Column()
|
||||
senderName: string;
|
||||
|
||||
@Field()
|
||||
@Column()
|
||||
senderPictureUrl: string;
|
||||
|
||||
@Field()
|
||||
@Column()
|
||||
numberOfMessagesInThread: number;
|
||||
|
||||
@Field()
|
||||
@Column()
|
||||
subject: string;
|
||||
|
||||
@Field()
|
||||
@Column()
|
||||
body: string;
|
||||
|
||||
@Field()
|
||||
@Column()
|
||||
receivedAt: Date;
|
||||
}
|
||||
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@Resolver(() => [TimelineThread])
|
||||
export class TimelineMessagingResolver {
|
||||
constructor(
|
||||
private readonly timelineMessagingService: TimelineMessagingService,
|
||||
) {}
|
||||
|
||||
@Query(() => [TimelineThread])
|
||||
async getTimelineThreadsFromPersonId(
|
||||
@AuthWorkspace() { id: workspaceId }: Workspace,
|
||||
@Args('personId') personId: string,
|
||||
) {
|
||||
const timelineThreads =
|
||||
await this.timelineMessagingService.getMessagesFromPersonIds(
|
||||
workspaceId,
|
||||
[personId],
|
||||
);
|
||||
|
||||
return timelineThreads;
|
||||
}
|
||||
|
||||
@Query(() => [TimelineThread])
|
||||
async getTimelineThreadsFromCompanyId(
|
||||
@AuthWorkspace() { id: workspaceId }: Workspace,
|
||||
@Args('companyId') companyId: string,
|
||||
) {
|
||||
const timelineThreads =
|
||||
await this.timelineMessagingService.getMessagesFromCompanyId(
|
||||
workspaceId,
|
||||
companyId,
|
||||
);
|
||||
|
||||
return timelineThreads;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,112 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { TypeORMService } from 'src/database/typeorm/typeorm.service';
|
||||
import { DataSourceService } from 'src/metadata/data-source/data-source.service';
|
||||
|
||||
@Injectable()
|
||||
export class TimelineMessagingService {
|
||||
constructor(
|
||||
private readonly dataSourceService: DataSourceService,
|
||||
private readonly typeORMService: TypeORMService,
|
||||
) {}
|
||||
|
||||
async getMessagesFromPersonIds(workspaceId: string, personIds: string[]) {
|
||||
const dataSourceMetadata =
|
||||
await this.dataSourceService.getLastDataSourceMetadataFromWorkspaceIdOrFail(
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
const workspaceDataSource = await this.typeORMService.connectToDataSource(
|
||||
dataSourceMetadata,
|
||||
);
|
||||
|
||||
// 10 first threads This hard limit is just for the POC, we will implement pagination later
|
||||
const messageThreads = await workspaceDataSource?.query(
|
||||
`
|
||||
SELECT
|
||||
subquery.*,
|
||||
message_count,
|
||||
last_message_subject,
|
||||
last_message_body,
|
||||
last_message_date,
|
||||
last_message_recipient_handle,
|
||||
last_message_recipient_displayName
|
||||
FROM (
|
||||
SELECT
|
||||
mt.*,
|
||||
COUNT(m."id") OVER (PARTITION BY mt."id") AS message_count,
|
||||
FIRST_VALUE(m."subject") OVER (PARTITION BY mt."id" ORDER BY m."date" DESC) AS last_message_subject,
|
||||
FIRST_VALUE(m."body") OVER (PARTITION BY mt."id" ORDER BY m."date" DESC) AS last_message_body,
|
||||
FIRST_VALUE(m."date") OVER (PARTITION BY mt."id" ORDER BY m."date" DESC) AS last_message_date,
|
||||
FIRST_VALUE(mr."handle") OVER (PARTITION BY mt."id" ORDER BY m."date" DESC) AS last_message_recipient_handle,
|
||||
FIRST_VALUE(mr."displayName") OVER (PARTITION BY mt."id" ORDER BY m."date" DESC) AS last_message_recipient_displayName,
|
||||
ROW_NUMBER() OVER (PARTITION BY mt."id" ORDER BY m."date" DESC) AS rn
|
||||
FROM
|
||||
${dataSourceMetadata.schema}."messageThread" mt
|
||||
LEFT JOIN
|
||||
${dataSourceMetadata.schema}."message" m ON mt."id" = m."messageThreadId"
|
||||
LEFT JOIN
|
||||
${dataSourceMetadata.schema}."messageRecipient" mr ON m."id" = mr."messageId"
|
||||
WHERE
|
||||
mr."personId" IN (SELECT unnest($1::uuid[]))
|
||||
) AS subquery
|
||||
WHERE
|
||||
subquery.rn = 1
|
||||
ORDER BY
|
||||
subquery.last_message_date DESC
|
||||
LIMIT 10;
|
||||
`,
|
||||
[personIds],
|
||||
);
|
||||
|
||||
const formattedMessageThreads = messageThreads.map((messageThread) => {
|
||||
return {
|
||||
read: true,
|
||||
senderName: messageThread.last_message_recipient_handle,
|
||||
senderPictureUrl: '',
|
||||
numberOfMessagesInThread: messageThread.message_count,
|
||||
subject: messageThread.last_message_subject,
|
||||
body: messageThread.last_message_body,
|
||||
receivedAt: messageThread.last_message_date,
|
||||
};
|
||||
});
|
||||
|
||||
return formattedMessageThreads;
|
||||
}
|
||||
|
||||
async getMessagesFromCompanyId(workspaceId: string, companyId: string) {
|
||||
const dataSourceMetadata =
|
||||
await this.dataSourceService.getLastDataSourceMetadataFromWorkspaceIdOrFail(
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
const workspaceDataSource = await this.typeORMService.connectToDataSource(
|
||||
dataSourceMetadata,
|
||||
);
|
||||
|
||||
const personIds = await workspaceDataSource?.query(
|
||||
`
|
||||
SELECT
|
||||
p."id"
|
||||
FROM
|
||||
${dataSourceMetadata.schema}."person" p
|
||||
WHERE
|
||||
p."companyId" = $1
|
||||
`,
|
||||
[companyId],
|
||||
);
|
||||
|
||||
if (!personIds) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const formattedPersonIds = personIds.map((personId) => personId.id);
|
||||
|
||||
const messageThreads = await this.getMessagesFromPersonIds(
|
||||
workspaceId,
|
||||
formattedPersonIds,
|
||||
);
|
||||
|
||||
return messageThreads;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user