Skip to content

Commit

Permalink
Fixed file sturucture and incorporated feedback
Browse files Browse the repository at this point in the history
  • Loading branch information
jaipise committed Mar 18, 2024
1 parent 961ef03 commit dc00bc2
Show file tree
Hide file tree
Showing 12 changed files with 973 additions and 871 deletions.
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@
"ts-proto": "^1.162.1"
},
"dependencies": {
"@sendgrid/mail": "^8.1.1",
"axios": "^1.6.7",
"bcrypt": "^5.1.1",
"fs-extra": "^11.2.0",
"jsonwebtoken": "^9.0.2"
Expand Down
9 changes: 1 addition & 8 deletions packages/email-service/src/app.controller.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,7 @@
import { Post, Body, Controller } from '@nestjs/common';
import { Controller } from '@nestjs/common';
import { AppService } from './app.service';
import { EmailProto } from 'juno-proto';

@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
@Post('/register-sender')
async registerSender(
@Body() req: EmailProto.RegisterSenderRequest,
): Promise<EmailProto.RegisterSenderResponse> {
return await this.appService.registerSender(req);
}
}
3 changes: 2 additions & 1 deletion packages/email-service/src/app.module.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { EmailModule } from './modules/email/email.module';

@Module({
imports: [],
imports: [EmailModule],
controllers: [AppController],
providers: [AppService],
})
Expand Down
53 changes: 1 addition & 52 deletions packages/email-service/src/app.service.ts
Original file line number Diff line number Diff line change
@@ -1,55 +1,4 @@
import { Injectable } from '@nestjs/common';
import { EmailProto } from 'juno-proto';
import axios from 'axios';
import { RpcException } from '@nestjs/microservices';

@Injectable()
export class AppService {
async registerSender(
req: EmailProto.RegisterSenderRequest,
): Promise<EmailProto.RegisterEmailResponse> {
if (!req.from_email) {
throw new RpcException('Cannot register sender (no email supplied)');
}
if (!req.from_name) {
throw new RpcException('Cannot register sender (no name supplied)');
}
if (!req.reply_to) {
throw new RpcException('Cannot register sender (no reply to specified)');
}

const sendgridApiKey = process.env.SENDGRID_API_KEY;

if (!sendgridApiKey) {
throw new RpcException(
'Cannot register sender (sendgrid API key is missing)',
);
}
const sendgridUrl = 'https://api.sendgrid.com/v3/verified_senders';

try {
const res = await axios.post(
sendgridUrl,
{
from_email: req.from_email,
from_name: req.from_name,
reply_to: req.reply_to,
},
{
headers: {
Authorization: `Bearer ${sendgridApiKey}`,
'Content-Type': 'application/json',
},
},
);

return {
statusCode: res.status,
message: 'Sender registered successfully',
};
} catch (err) {
console.error('error registering sender:', err);
throw new RpcException('Unable to register sender');
}
}
}
export class AppService {}
5 changes: 3 additions & 2 deletions packages/email-service/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { AppModule } from './app.module';
import { MicroserviceOptions, Transport } from '@nestjs/microservices';
import { join } from 'path';
import { ConfigModule } from '@nestjs/config';
import { EmailProtoFile, EmailProto } from 'juno-proto';

async function bootstrap() {
ConfigModule.forRoot({
Expand All @@ -13,8 +14,8 @@ async function bootstrap() {
{
transport: Transport.GRPC,
options: {
package: [],
protoPath: [],
package: [EmailProto.JUNO_EMAIL_PACKAGE_NAME],
protoPath: [EmailProtoFile],
url: process.env.EMAIL_SERVICE_ADDR,
},
},
Expand Down
13 changes: 13 additions & 0 deletions packages/email-service/src/modules/email/email.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { Body, Controller } from '@nestjs/common';
import { EmailService } from './email.service';
import { EmailProto } from 'juno-proto';

@Controller()
export class EmailController {
constructor(private emailService: EmailService) {}
async registerSender(
@Body() req: EmailProto.RegisterSenderRequest,
): Promise<EmailProto.RegisterSenderResponse> {
return await this.emailService.registerSender(req);
}
}
10 changes: 10 additions & 0 deletions packages/email-service/src/modules/email/email.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Module } from '@nestjs/common';
import { EmailController } from './email.controller';
import { EmailService } from './email.service';
import { SendGridService } from '../../sendgrid.service';

@Module({
controllers: [EmailController],
providers: [EmailService, SendGridService],
})
export class EmailModule {}
59 changes: 59 additions & 0 deletions packages/email-service/src/modules/email/email.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { Injectable } from '@nestjs/common';
import { EmailProto } from 'juno-proto';
import axios from 'axios';
import { RpcException } from '@nestjs/microservices';

@Injectable()
export class EmailService {
async registerSender(
req: EmailProto.RegisterSenderRequest,
): Promise<EmailProto.RegisterSenderResponse> {
if (process.env.SENDGRID_API_KEY) {
if (!req.fromEmail) {
throw new RpcException('Cannot register sender (no email supplied)');
}
if (!req.fromName) {
throw new RpcException('Cannot register sender (no name supplied)');
}
if (!req.replyTo) {
throw new RpcException(
'Cannot register sender (no reply to specified)',
);
}

const sendgridApiKey = process.env.SENDGRID_API_KEY;
const sendgridUrl = 'https://api.sendgrid.com/v3/verified_senders';

if (!sendgridApiKey) {
throw new RpcException(
'Cannot register sender (sendgrid API key is missing)',
);
}

try {
const res = await axios.post(
sendgridUrl,
{
fromEmail: req.fromEmail,
fromName: req.fromName,
replyTo: req.replyTo,
},
{
headers: {
Authorization: `Bearer ${sendgridApiKey}`,
'Content-Type': 'application/json',
},
},
);

return {
statusCode: res.status,
message: 'Sender registered successfully',
};
} catch (err) {
console.error('error registering sender:', err);
throw new RpcException('Unable to register sender');
}
}
}
}
14 changes: 14 additions & 0 deletions packages/email-service/src/sendgrid.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { Injectable, OnModuleInit } from '@nestjs/common';
import { MailService as SendGridMailService } from '@sendgrid/mail';

@Injectable()
export class SendGridService
extends SendGridMailService
implements OnModuleInit
{
async onModuleInit() {
if (process.env.SENDGRID_API_KEY) {
this.setApiKey(process.env.SENDGRID_API_KEY);
}
}
}
20 changes: 14 additions & 6 deletions packages/email-service/test/app.e2e-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,20 @@ beforeAll(async () => {

const protoGRPC = GRPC.loadPackageDefinition(proto) as any;

const resetClient = new protoGRPC.juno.reset_db.DatabaseReset(
process.env.DB_SERVICE_ADDR,
GRPC.credentials.createInsecure(),
);

const emailClient = new protoGRPC.juno.emailService(
process.env.DB_SERVICE_ADDR,
GRPC.credentials.createInsecure(),
);

await new Promise((resolve) => {
resetClient.resetDb({}, () => {
resolve(0);
});
emailClient.resetDb({}, () => {
resolve(0);
});
Expand All @@ -59,9 +67,9 @@ it('should successfully register a sender', async () => {
(resolve, reject) => {
emailClient.registerSender(
{
from_email: '[email protected]',
from_name: 'example',
reply_to: '[email protected]',
fromEmail: '[email protected]',
fromName: 'example',
replyTo: '[email protected]',
},
(err: any, response: EmailProto.RegisterSenderResponse) => {
if (err) {
Expand All @@ -83,9 +91,9 @@ it('should fail to register a sender', async () => {
(resolve, reject) => {
emailClient.registerSender(
{
from_email: '',
from_name: '',
reply_to: '',
fromEmail: '',
fromName: '',
replyTo: '',
},
(err: any, response: EmailProto.RegisterSenderResponse) => {
if (err) {
Expand Down
24 changes: 23 additions & 1 deletion packages/proto/src/gen/email.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,25 @@ export interface SendEmailRequestResponse {
success: boolean;
}

export interface RegisterSenderRequest {
fromEmail: string;
fromName: string;
replyTo: string;
}

export interface RegisterSenderResponse {
statusCode: number;
message: string;
}

export const JUNO_EMAIL_PACKAGE_NAME = 'juno.email';

export interface EmailServiceClient {
sendEmail(request: SendEmailRequest): Observable<SendEmailRequestResponse>;

registerSender(
request: RegisterSenderRequest,
): Observable<RegisterSenderResponse>;
}

export interface EmailServiceController {
Expand All @@ -27,11 +42,18 @@ export interface EmailServiceController {
| Promise<SendEmailRequestResponse>
| Observable<SendEmailRequestResponse>
| SendEmailRequestResponse;

registerSender(
request: RegisterSenderRequest,
):
| Promise<RegisterSenderResponse>
| Observable<RegisterSenderResponse>
| RegisterSenderResponse;
}

export function EmailServiceControllerMethods() {
return function (constructor: Function) {
const grpcMethods: string[] = ['sendEmail'];
const grpcMethods: string[] = ['sendEmail', 'registerSender'];
for (const method of grpcMethods) {
const descriptor: any = Reflect.getOwnPropertyDescriptor(
constructor.prototype,
Expand Down
Loading

0 comments on commit dc00bc2

Please sign in to comment.