Skip to content

Commit

Permalink
Merge pull request #69 from Giveth/migrating-from-autopilot-to-ortto
Browse files Browse the repository at this point in the history
Migrating from Autopilot to Ortto
  • Loading branch information
mohammadranjbarz authored Feb 8, 2024
2 parents 5578c54 + 57367e8 commit 1fd9abe
Show file tree
Hide file tree
Showing 13 changed files with 302 additions and 22 deletions.
6 changes: 6 additions & 0 deletions config/example.env
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,15 @@ REDIS_PORT=6490
REDIS_PASSWORD=

SEGMENT_API_KEY=FAKE_API_KEY
ORTTO_API_KEY=FAKE_API_KEY
ORTTO_ACTIVITY_API=https://api-us.ortto.app/v1/activities/create

#JWT_AUTHORIZATION_ADAPTER=siweMicroservice
JWT_AUTHORIZATION_ADAPTER=mock
AUTH_MICROSERVICE_AUTHORIZATION_URL=

HOSTNAME_WHITELIST=next.giveth.io,www.next.giveth.io,staging.giveth.io,www.staging.giveth.io,localhost,giveth.io,vercel.app,www.giveth.io


EMAIL_ADAPTER=mock
#EMAIL_ADAPTER=ortto
3 changes: 2 additions & 1 deletion config/test.env
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ REDIS_HOST=127.0.0.1
REDIS_PORT=6490
REDIS_PASSWORD=


SEGMENT_API_KEY=FAKE_API_KEY

JWT_AUTHORIZATION_ADAPTER=mock
Expand All @@ -26,3 +25,5 @@ GIV_ECONOMY_THIRD_PARTY_MICRO_SERVICE=giveconomy-notification-service

# OPTIONAL - force logging to stdout when the value is true
LOG_STDOUT=false

EMAIL_ADAPTER=mock
16 changes: 16 additions & 0 deletions src/adapters/adapterFactory.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { SiweAuthenticationMicroserviceAdapter } from './jwtAuthentication/siweAuthenticationMicroserviceAdapter';
import { MockJwtAdapter } from './jwtAuthentication/mockJwtAdapter';
import { errorMessages } from '../utils/errorMessages';
import {OrttoAdapter} from "./emailAdapter/orttoAdapter";
import {OrttoMockAdapter} from "./emailAdapter/orttoMockAdapter";

const siweAuthenricationAdapter = new SiweAuthenticationMicroserviceAdapter();
const jwtMockAdapter = new MockJwtAdapter();
Expand All @@ -15,3 +17,17 @@ export const getJwtAuthenticationAdapter = () => {
throw new Error(errorMessages.SPECIFY_JWT_AUTHENTICATION_ADAPTER);
}
};


const orttoEmailAdapter = new OrttoAdapter()
const emailMockAdapter = new OrttoMockAdapter()
export const getEmailAdapter = () => {
switch (process.env.EMAIL_ADAPTER) {
case 'ortto':
return orttoEmailAdapter;
case 'mock':
return emailMockAdapter;
default:
throw new Error(errorMessages.SPECIFY_Email_ADAPTER);
}
};
32 changes: 32 additions & 0 deletions src/adapters/emailAdapter/orttoAdapter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { logger } from '../../utils/logger';
import axios from 'axios';
import {OrttoAdapterInterface} from "./orttoAdapterInterface";


export class OrttoAdapter implements OrttoAdapterInterface{
async callOrttoActivity(data: any): Promise<void> {
try {
if (!data){
throw new Error('callOrttoActivity input data is empty')
}
const config = {
method: 'post',
maxBodyLength: Infinity,
url: process.env.ORTTO_ACTIVITY_API,
headers: {
'X-Api-Key': process.env.ORTTO_API_KEY as string,
'Content-Type': 'application/json'
},
data
};
data.activities.map((a: any) => logger.debug('orttoActivityCall', a));
await axios.request(config);
} catch (e) {
logger.error('orttoActivityCall error', {
error: e,
data
});
}
}

}
5 changes: 5 additions & 0 deletions src/adapters/emailAdapter/orttoAdapterInterface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import internal from "stream";

export interface OrttoAdapterInterface {
callOrttoActivity (data: any): Promise<void>
}
11 changes: 11 additions & 0 deletions src/adapters/emailAdapter/orttoMockAdapter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { logger } from '../../utils/logger';
import axios from 'axios';
import {OrttoAdapterInterface} from "./orttoAdapterInterface";


export class OrttoMockAdapter implements OrttoAdapterInterface{
async callOrttoActivity(data: any): Promise<void> {
logger.debug('OrttoMockAdapter has been called', data)
}

}
1 change: 0 additions & 1 deletion src/controllers/v1/notificationsController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ import {
markNotificationsAsRead,
} from '../../repositories/notificationRepository';
import { UserAddress } from '../../entities/userAddress';
import { Notification } from '../../entities/notification';
import { createNewUserAddressIfNotExists } from '../../repositories/userAddressRepository';
import { sendNotification } from '../../services/notificationService';
import { StandardError } from '../../types/StandardError';
Expand Down
10 changes: 0 additions & 10 deletions src/routes/v1/notificationRouter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -355,8 +355,6 @@ function sendNotificationTestCases() {
sendEmail: true,
sendSegment: true,
segment: {
analyticsUserId: 'givethId-255',
anonymousId: 'givethId-255',
payload: {
title: 'Test verify and reject form emails',
lastName: 'Ranjbar',
Expand Down Expand Up @@ -1385,8 +1383,6 @@ function sendNotificationTestCases() {
projectLink,
},
segment: {
analyticsUserId: 'givethId-255',
anonymousId: 'givethId-255',
payload: {
email: '[email protected]',
title: 'How many photos is too many photos?',
Expand Down Expand Up @@ -1428,8 +1424,6 @@ function sendNotificationTestCases() {
projectLink,
},
segment: {
analyticsUserId: 'givethId-255',
anonymousId: 'givethId-255',
payload: {
email: '[email protected]',
title: 'How many photos is too many photos?',
Expand Down Expand Up @@ -1499,8 +1493,6 @@ function sendNotificationTestCases() {
projectLink,
},
segment: {
analyticsUserId: 'givethId-255',
anonymousId: 'givethId-255',
payload: {
email: '[email protected]',
title: 'How many photos is too many photos?',
Expand Down Expand Up @@ -1542,8 +1534,6 @@ function sendNotificationTestCases() {
projectLink,
},
segment: {
analyticsUserId: 'givethId-255',
anonymousId: 'givethId-255',
payload: {
email: '[email protected]',
title: 'How many photos is too many photos?',
Expand Down
178 changes: 168 additions & 10 deletions src/services/notificationService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,171 @@ import { logger } from '../utils/logger';
import { EMAIL_STATUSES, Notification } from '../entities/notification';
import { SEGMENT_METADATA_SCHEMA_VALIDATOR } from '../utils/validators/segmentAndMetadataValidators';
import { validateWithJoiSchema } from '../validators/schemaValidators';
import { SegmentAnalyticsSingleton } from './segment/segmentAnalyticsSingleton';
import { SendNotificationRequest } from '../types/requestResponses';
import { StandardError } from '../types/StandardError';
import { NOTIFICATIONS_EVENT_NAMES, ORTTO_EVENT_NAMES } from '../types/notifications';
import {getEmailAdapter} from "../adapters/adapterFactory";

const activityCreator = (payload: any, orttoEventName: NOTIFICATIONS_EVENT_NAMES) : any=> {
switch (orttoEventName) {
case NOTIFICATIONS_EVENT_NAMES.DONATION_RECEIVED:
return {
"activities": [
{
"activity_id": `act:cm:${ORTTO_EVENT_NAMES[orttoEventName]}`,
"attributes": {
"str:cm:projecttitle": payload.title,
"int:cm:donationamount": payload.amount,
"str:cm:donationtoken": payload.token,
"str:cm:email": payload.email,
"str:cm:projectlink": payload.projectLink,
"bol:cm:verified": payload.verified,
"str:cm:transactionlink": payload.transactionLink,
},
"fields": {
"str::email": payload.email
}
}
]
};
case NOTIFICATIONS_EVENT_NAMES.DRAFTED_PROJECT_ACTIVATED:
return {
"activities": [
{
"activity_id": `act:cm:${ORTTO_EVENT_NAMES[orttoEventName]}`,
"attributes": {
"str:cm:projecttitle": payload.title,
"str:cm:email": payload.email,
"str:cm:projectlink": payload.projectLink,
"str:cm:firstname": payload.firstName,
"str:cm:lastname": payload.lastName,
},
"fields": {
"str::email": payload.email
}
}
]
};
case NOTIFICATIONS_EVENT_NAMES.PROJECT_LISTED:
return {
"activities": [
{
"activity_id": `act:cm:${ORTTO_EVENT_NAMES[orttoEventName]}`,
"attributes": {
"str:cm:projecttitle": payload.title,
"str:cm:email": payload.email,
"str:cm:projectlink": payload.projectLink,
},
"fields": {
"str::email": payload.email
}
}
]
};
case NOTIFICATIONS_EVENT_NAMES.PROJECT_UNLISTED:
return {
"activities": [
{
"activity_id": `act:cm:${ORTTO_EVENT_NAMES[orttoEventName]}`,
"attributes": {
"str:cm:projecttitle": payload.title,
"str:cm:email": payload.email,
"str:cm:projectlink": payload.projectLink,
},
"fields": {
"str::email": payload.email
}
}
]
};
case NOTIFICATIONS_EVENT_NAMES.PROJECT_CANCELLED:
return {
"activities": [
{
"activity_id": `act:cm:${ORTTO_EVENT_NAMES[orttoEventName]}`,
"attributes": {
"str:cm:projecttitle": payload.title,
"str:cm:email": payload.email,
"str:cm:projectlink": payload.projectLink,
},
"fields": {
"str::email": payload.email
}
}
]
};
case NOTIFICATIONS_EVENT_NAMES.PROJECT_UPDATE_ADDED_OWNER:
return {
"activities": [
{
"activity_id": `act:cm:${ORTTO_EVENT_NAMES[orttoEventName]}`,
"attributes": {
"str:cm:projecttitle": payload.title,
"str:cm:email": payload.email,
"str:cm:projectupdatelink": payload.projectLink + '?tab=updates',
},
"fields": {
"str::email": payload.email
}
}
]
};
case NOTIFICATIONS_EVENT_NAMES.PROJECT_VERIFIED:
return {
"activities": [
{
"activity_id": `act:cm:${ORTTO_EVENT_NAMES[orttoEventName]}`,
"attributes": {
"str:cm:projecttitle": payload.title,
"str:cm:email": payload.email,
"str:cm:projectlink": payload.projectLink,
"str:cm:verified-status": 'verified',
},
"fields": {
"str::email": payload.email
}
}
]
};
case NOTIFICATIONS_EVENT_NAMES.PROJECT_UNVERIFIED:
return {
"activities": [
{
"activity_id": `act:cm:${ORTTO_EVENT_NAMES[orttoEventName]}`,
"attributes": {
"str:cm:projecttitle": payload.title,
"str:cm:email": payload.email,
"str:cm:projectlink": payload.projectLink,
"str:cm:verified-status": 'rejected',
},
"fields": {
"str::email": payload.email
}
}
]
};
case NOTIFICATIONS_EVENT_NAMES.PROJECT_BADGE_REVOKED:
return {
"activities": [
{
"activity_id": `act:cm:${ORTTO_EVENT_NAMES[orttoEventName]}`,
"attributes": {
"str:cm:projecttitle": payload.title,
"str:cm:email": payload.email,
"str:cm:projectlink": payload.projectLink,
"str:cm:verified-status": 'revoked',
},
"fields": {
"str::email": payload.email
}
}
]
};
default:
logger.debug('activityCreator() invalid event name', orttoEventName)
return undefined
}
}

export const sendNotification = async (
body: SendNotificationRequest,
Expand Down Expand Up @@ -78,14 +240,10 @@ export const sendNotification = async (
if (shouldSendEmail && body.sendSegment && segmentValidator) {
//TODO Currently sending email and segment event are tightly coupled, we can't send segment event without sending email
// And it's not good, we should find another solution to separate sending segment and email
const segmentData = body.segment?.payload;
validateWithJoiSchema(segmentData, segmentValidator);
await SegmentAnalyticsSingleton.getInstance().track({
eventName: notificationType.emailNotificationId as string,
anonymousId: body?.segment?.anonymousId,
properties: segmentData,
analyticsUserId: body?.segment?.analyticsUserId,
});
const emailData = body.segment?.payload;
validateWithJoiSchema(emailData, segmentValidator);
const data = activityCreator(emailData, body.eventName as NOTIFICATIONS_EVENT_NAMES);
await getEmailAdapter().callOrttoActivity(data);
emailStatus = EMAIL_STATUSES.SENT;
}

Expand All @@ -99,7 +257,7 @@ export const sendNotification = async (
}

if (!notificationSetting?.allowDappPushNotification) {
//TODO In future we can add a create notification but with disabledNotification:true
//TODO In future we can add a create notification but with disabledNotification:true
// So we can exclude them in list of notifications
return {
success: true,
Expand Down
Loading

0 comments on commit 1fd9abe

Please sign in to comment.