Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 96 additions & 0 deletions packages/api-gateway/src/models/email.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -536,6 +536,102 @@ export class SendGridDNSResponse {
}
}

export class VerifiedSenderResponse {
@ApiProperty({ type: 'number' })
id: number;

@ApiProperty({ type: 'string' })
nickname: string;

@ApiProperty({ type: 'string' })
fromEmail: string;

@ApiProperty({ type: 'string' })
fromName: string;

@ApiProperty({ type: 'string' })
replyTo: string;

@ApiProperty({ type: 'string' })
address: string;

@ApiProperty({ type: 'string' })
city: string;

@ApiProperty({ type: 'string' })
state: string;

@ApiProperty({ type: 'string' })
country: string;

@ApiProperty({ type: 'string' })
zip: string;

@ApiProperty({ type: 'boolean' })
verified: boolean;

@ApiProperty({ type: 'boolean' })
locked: boolean;

constructor(sender: EmailProto.VerifiedSender) {
this.id = Number(sender.id);
this.nickname = sender.nickname;
this.fromEmail = sender.fromEmail;
this.fromName = sender.fromName;
this.replyTo = sender.replyTo;
this.address = sender.address;
this.city = sender.city;
this.state = sender.state;
this.country = sender.country;
this.zip = sender.zip;
this.verified = sender.verified;
this.locked = sender.locked;
}
}

export class GetSendersResponse {
@ApiProperty({ type: [VerifiedSenderResponse] })
senders: VerifiedSenderResponse[];

constructor(res: EmailProto.GetSendersResponse) {
this.senders = (res.senders ?? []).map(
(s) => new VerifiedSenderResponse(s),
);
}
}

export class AuthenticatedDomainResponse {
@ApiProperty({ type: 'number' })
id: number;

@ApiProperty({ type: 'string' })
domain: string;

@ApiPropertyOptional({ type: 'string' })
subdomain?: string;

@ApiProperty({ type: 'boolean' })
valid: boolean;

constructor(domain: EmailProto.AuthenticatedDomain) {
this.id = Number(domain.id);
this.domain = domain.domain;
this.subdomain = domain.subdomain;
this.valid = domain.valid;
}
}

export class GetDomainsResponse {
@ApiProperty({ type: [AuthenticatedDomainResponse] })
domains: AuthenticatedDomainResponse[];

constructor(res: EmailProto.GetDomainsResponse) {
this.domains = (res.domains ?? []).map(
(d) => new AuthenticatedDomainResponse(d),
);
}
}

export class RegisterDomainResponse {
@ApiProperty({ type: 'number' })
id: number;
Expand Down
48 changes: 48 additions & 0 deletions packages/api-gateway/src/modules/email/email.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ import {
VerifyDomainModel,
AggregationInterval,
SendEmailStatisticsResponses,
GetSendersResponse,
GetDomainsResponse,
} from 'src/models/email.dto';

import {
Expand Down Expand Up @@ -352,4 +354,50 @@ export class EmailController implements OnModuleInit {
),
);
}

@ApiOperation({ summary: 'Gets all verified senders from SendGrid' })
@ApiOkResponse({
description: 'Returned verified senders',
type: GetSendersResponse,
})
@ApiUnauthorizedResponse({ description: 'Unauthorized' })
@ApiBadRequestResponse({ description: 'Bad Request' })
@Get('/senders')
async getSenders(
@ApiKey() apiKey: AuthCommonProto.ApiKey,
@ProjectId() projectId: number,
): Promise<GetSendersResponse> {
return new GetSendersResponse(
await lastValueFrom(
this.emailService.getSenders({
configId: projectId,
configEnvironment: apiKey.environment,
}),
),
);
}

@ApiOperation({
summary: 'Gets all authenticated domains from SendGrid',
})
@ApiOkResponse({
description: 'Returned authenticated domains',
type: GetDomainsResponse,
})
@ApiUnauthorizedResponse({ description: 'Unauthorized' })
@ApiBadRequestResponse({ description: 'Bad Request' })
@Get('/domains')
async getDomains(
@ApiKey() apiKey: AuthCommonProto.ApiKey,
@ProjectId() projectId: number,
): Promise<GetDomainsResponse> {
return new GetDomainsResponse(
await lastValueFrom(
this.emailService.getDomains({
configId: projectId,
configEnvironment: apiKey.environment,
}),
),
);
}
}
48 changes: 48 additions & 0 deletions packages/api-gateway/test/email.e2e-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -617,6 +617,54 @@ describe('Domain Registration Routes', () => {
});
});

describe('Email Senders Routes', () => {
it('Successfully gets senders', async () => {
return request(app.getHttpServer())
.get('/email/senders')
.set('Authorization', 'Bearer ' + apiKey)
.expect(200)
.then((response) => {
expect(response.body).toHaveProperty('senders');
expect(Array.isArray(response.body.senders)).toBe(true);
});
});

it('Fails without an Authorization header', async () => {
return request(app.getHttpServer()).get('/email/senders').expect(401);
});

it('Fails with an invalid API Key', async () => {
return request(app.getHttpServer())
.get('/email/senders')
.set('Authorization', 'Bearer invalid.api.key')
.expect(401);
});
});

describe('Email Domains Routes', () => {
it('Successfully gets domains', async () => {
return request(app.getHttpServer())
.get('/email/domains')
.set('Authorization', 'Bearer ' + apiKey)
.expect(200)
.then((response) => {
expect(response.body).toHaveProperty('domains');
expect(Array.isArray(response.body.domains)).toBe(true);
});
});

it('Fails without an Authorization header', async () => {
return request(app.getHttpServer()).get('/email/domains').expect(401);
});

it('Fails with an invalid API Key', async () => {
return request(app.getHttpServer())
.get('/email/domains')
.set('Authorization', 'Bearer invalid.api.key')
.expect(401);
});
});

describe('Domain Verification Routes', () => {
it('Verifies a domain without a domain parameter', () => {
return request(app.getHttpServer())
Expand Down
2 changes: 1 addition & 1 deletion packages/db-service/src/modules/email/email.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ export class EmailController implements EmailDbServiceController {
const emailDomain = await this.emailService.createEmailDomain({
domain: request.domain,
subdomain: request.subdomain,
sendgridId: request.sendgridId,
sendgridId: Number(request.sendgridId),
attachedConfigs: {
connectOrCreate: {
create: {
Expand Down
12 changes: 12 additions & 0 deletions packages/email-service/src/modules/email/email.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,4 +108,16 @@ export class EmailController implements EmailProto.EmailServiceController {
): Promise<EmailProto.StatisticResponses> {
return await this.emailService.getStatistics(request);
}

async getSenders(
request: EmailProto.GetSendersRequest,
): Promise<EmailProto.GetSendersResponse> {
return await this.emailService.getSenders(request);
}

async getDomains(
request: EmailProto.GetDomainsRequest,
): Promise<EmailProto.GetDomainsResponse> {
return await this.emailService.getDomains(request);
}
}
Loading
Loading