Skip to content

Commit

Permalink
[Booster] Milestone 5: Family and occupation risk assessment
Browse files Browse the repository at this point in the history
# Conflicts:
#	kyc-booster/src/entities/profile.ts
#	kyc-booster/src/read-models/ProfileReadModel.ts
  • Loading branch information
javiertoledo committed May 4, 2023
1 parent 26d30f1 commit f963d88
Show file tree
Hide file tree
Showing 8 changed files with 192 additions and 30 deletions.
25 changes: 25 additions & 0 deletions kyc-booster/src/commands/add-profile-occupation-data.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { Command } from '@boostercloud/framework-core'
import { Register, UUID } from '@boostercloud/framework-types'
import { ProfileOcupationDataAdded } from '../events/profile-ocupation-data-added'
import { IncomeSource } from '../common/types'

@Command({
authorize: 'all',
})
export class AddProfileOccupationData {
public constructor(
readonly profileId: UUID,
readonly occupation: string,
readonly employer: string,
readonly sourceOfIncome: IncomeSource,
) {}

public static async handle(command: AddProfileOccupationData , register: Register): Promise<void> {
register.events(new ProfileOcupationDataAdded(
command.profileId,
command.occupation,
command.employer,
command.sourceOfIncome,
))
}
}
29 changes: 29 additions & 0 deletions kyc-booster/src/commands/create-relative.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { Command } from '@boostercloud/framework-core'
import { Register, UUID } from '@boostercloud/framework-types'
import { RelativeCreated } from '../events/relative-created'

@Command({
authorize: 'all',
})
export class CreateRelative {
public constructor(
readonly firstName: string,
readonly lastName: string,
readonly relationship: string,
readonly politicalInfluence: boolean,
readonly profileId: UUID,
) {}

public static async handle(command: CreateRelative , register: Register): Promise<{ id: UUID }> {
const relativeId = UUID.generate()
register.events(new RelativeCreated(
relativeId,
command.firstName,
command.lastName,
command.relationship,
command.politicalInfluence,
command.profileId,
))
return { id: relativeId }
}
}
13 changes: 12 additions & 1 deletion kyc-booster/src/common/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,15 @@ export type KYCStatus =
| 'KYCAddressRejected'
| 'KYCBackgroundCheckPassed'
| 'KYCBackgroundCheckRequiresManualReview'
| 'KYCBackgroundCheckRejected';
| 'KYCBackgroundCheckRejected';

export type IncomeSource =
| 'Salary'
| 'Dividends'
| 'BusinessIncome'
| 'Freelance'
| 'RentalIncome'
| 'Royalties'
| 'Investments'
| 'Pensions'
| 'SocialSecurity'
18 changes: 17 additions & 1 deletion kyc-booster/src/entities/profile.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import { UUID } from '@boostercloud/framework-types'
import { Entity, Reduces } from '@boostercloud/framework-core'
import { ProfileCreated } from '../events/profile-created'
import { KYCStatus } from '../common/types'
import { KYCStatus, IncomeSource } from '../common/types'
import { IDVerificationSuccess } from '../events/id-verification-success'
import { IDVerificationRejected } from '../events/id-verification-rejected'
import { AddressVerificationSuccess } from '../events/address-verification-success'
import { AddressVerificationRejected } from '../events/address-verification-rejected'
import { BackgroundCheckPassed } from '../events/background-check-passed';
import { BackgroundCheckRejected } from '../events/background-check-rejected'
import { BackgroundCheckManualReviewRequired } from '../events/background-check-manual-review-required';
import { ProfileOcupationDataAdded } from '../events/profile-ocupation-data-added'

@Entity
export class Profile {
Expand Down Expand Up @@ -38,6 +39,9 @@ export class Profile {
readonly backgroundCheckTriedAt?: string,
readonly backgroundCheckValidatorId?: UUID | 'auto',
readonly backgroundCheckRejectedAt?: string,
readonly occupation?: string,
readonly employer?: string,
readonly sourceOfIncome?: IncomeSource,
) {}

@Reduces(ProfileCreated)
Expand Down Expand Up @@ -107,6 +111,15 @@ export class Profile {
}, currentProfile)
}

@Reduces(ProfileOcupationDataAdded)
public static onProfileOcupationDataAdded(event: ProfileOcupationDataAdded, currentProfile?: Profile): Profile {
return Profile.nextProfile({
occupation: event.occupation,
employer: event.employer,
sourceOfIncome: event.sourceOfIncome,
}, currentProfile)
}

private static nextProfile(fields: Partial<Profile>, currentProfile?: Profile): Profile {
if (!currentProfile) {
throw new Error('Cannot reduce an event over a non-existing profile')
Expand Down Expand Up @@ -137,6 +150,9 @@ export class Profile {
fields.backgroundCheckTriedAt || currentProfile.backgroundCheckTriedAt,
fields.backgroundCheckValidatorId || currentProfile.backgroundCheckValidatorId,
fields.backgroundCheckRejectedAt || currentProfile.backgroundCheckRejectedAt,
fields.occupation || currentProfile.occupation,
fields.employer || currentProfile.employer,
fields.sourceOfIncome || currentProfile.sourceOfIncome,
)
}
}
20 changes: 20 additions & 0 deletions kyc-booster/src/entities/relative.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { Entity, Reduces } from '@boostercloud/framework-core'
import { UUID } from '@boostercloud/framework-types'
import { RelativeCreated } from '../events/relative-created'

@Entity
export class Relative {
public constructor(
public id: UUID,
readonly firstName: string,
readonly lastName: string,
readonly relationship: string,
readonly politicalInfluence: boolean,
readonly profileId: UUID,
) {}

@Reduces(RelativeCreated)
public static reduceRelativeCreated(event: RelativeCreated, currentRelative?: Relative): Relative {
return new Relative(event.relativeId, event.firstName, event.lastName, event.relationship, event.politicalInfluence, event.profileId)
}
}
17 changes: 17 additions & 0 deletions kyc-booster/src/events/profile-ocupation-data-added.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { Event } from '@boostercloud/framework-core'
import { UUID } from '@boostercloud/framework-types'
import { IncomeSource } from '../common/types'

@Event
export class ProfileOcupationDataAdded {
public constructor(
readonly profileId: UUID,
readonly occupation: string,
readonly employer: string,
readonly sourceOfIncome: IncomeSource,
) {}

public entityID(): UUID {
return this.profileId
}
}
18 changes: 18 additions & 0 deletions kyc-booster/src/events/relative-created.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Event } from '@boostercloud/framework-core'
import { UUID } from '@boostercloud/framework-types'

@Event
export class RelativeCreated {
public constructor(
readonly relativeId: UUID,
readonly firstName: string,
readonly lastName: string,
readonly relationship: string,
readonly politicalInfluence: boolean,
readonly profileId: UUID,
) {}

public entityID(): UUID {
return this.relativeId
}
}
82 changes: 54 additions & 28 deletions kyc-booster/src/read-models/ProfileReadModel.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { Projects, ReadModel } from '@boostercloud/framework-core'
import { ProjectionResult, UUID } from '@boostercloud/framework-types'
import { ProjectionResult, ReadModelAction, UUID } from '@boostercloud/framework-types'
import { Profile } from '../entities/profile'
import { KYCStatus } from '../common/types'
import { Relative } from '../entities/relative'
import { IncomeSource, KYCStatus } from '../common/types'

@ReadModel({
authorize: 'all', // You can specify roles to restrict access to this read model
Expand Down Expand Up @@ -33,36 +34,61 @@ export class ProfileReadModel {
readonly backgroundCheckTriedAt?: string,
readonly backgroundCheckValidatorId?: UUID | 'auto',
readonly backgroundCheckRejectedAt?: string,
readonly occupation?: string,
readonly employer?: string,
readonly sourceOfIncome?: IncomeSource,
readonly relatives: Relative[] = []
) {}

@Projects(Profile, 'id')
public static projectProfile(entity: Profile): ProjectionResult<ProfileReadModel> {
public static projectProfile(entity: Profile, currentProfileReadModel?: ProfileReadModel): ProjectionResult<ProfileReadModel> {
if (currentProfileReadModel) {
return ProfileReadModel.build(currentProfileReadModel, { ...entity })
} else {
return ProfileReadModel.build({ ...entity, relatives: [] })
}
}

@Projects(Relative, 'profileId')
public static projectRelative(entity: Relative, currentProfileReadModel?: ProfileReadModel): ProjectionResult<ProfileReadModel> {
if (currentProfileReadModel) {
return ProfileReadModel.build(currentProfileReadModel, { relatives: [...currentProfileReadModel.relatives, entity] })
} else {
return ReadModelAction.Nothing
}
}

private static build(currentProfileReadModel: ProfileReadModel, fields?: Partial<ProfileReadModel>): ProfileReadModel {
return new ProfileReadModel(
entity.id,
entity.firstName,
entity.lastName,
entity.address,
entity.city,
entity.state,
entity.zipCode,
entity.country,
entity.dateOfBirth,
entity.phoneNumber,
entity.nationality,
entity.email,
entity.kycStatus,
entity.ssn,
entity.tin,
entity.idVerificationId,
entity.idVerifiedAt,
entity.idRejectedAt,
entity.addressVerificationId,
entity.addressVerifiedAt,
entity.addressRejectedAt,
entity.backgroundCheckPassedAt,
entity.backgroundCheckTriedAt,
entity.backgroundCheckValidatorId,
entity.backgroundCheckRejectedAt,
fields?.id ?? currentProfileReadModel.id,
fields?.firstName ?? currentProfileReadModel.firstName,
fields?.lastName ?? currentProfileReadModel.lastName,
fields?.address ?? currentProfileReadModel.address,
fields?.city ?? currentProfileReadModel.city,
fields?.state ?? currentProfileReadModel.state,
fields?.zipCode ?? currentProfileReadModel.zipCode,
fields?.country ?? currentProfileReadModel.country,
fields?.dateOfBirth ?? currentProfileReadModel.dateOfBirth,
fields?.phoneNumber ?? currentProfileReadModel.phoneNumber,
fields?.nationality ?? currentProfileReadModel.nationality,
fields?.email ?? currentProfileReadModel.email,
fields?.kycStatus ?? currentProfileReadModel.kycStatus,
fields?.ssn ?? currentProfileReadModel.ssn,
fields?.tin ?? currentProfileReadModel.tin,
fields?.idVerificationId ?? currentProfileReadModel.idVerificationId,
fields?.idVerifiedAt ?? currentProfileReadModel.idVerifiedAt,
fields?.idRejectedAt ?? currentProfileReadModel.idRejectedAt,
fields?.addressVerificationId ?? currentProfileReadModel.addressVerificationId,
fields?.addressVerifiedAt ?? currentProfileReadModel.addressVerifiedAt,
fields?.addressRejectedAt ?? currentProfileReadModel.addressRejectedAt,
fields?.backgroundCheckPassedAt ?? currentProfileReadModel.backgroundCheckPassedAt,
fields?.backgroundCheckTriedAt ?? currentProfileReadModel.backgroundCheckTriedAt,
fields?.backgroundCheckValidatorId ?? currentProfileReadModel.backgroundCheckValidatorId,
fields?.backgroundCheckRejectedAt ?? currentProfileReadModel.backgroundCheckRejectedAt,
fields?.occupation ?? currentProfileReadModel.occupation,
fields?.employer ?? currentProfileReadModel.employer,
fields?.sourceOfIncome ?? currentProfileReadModel.sourceOfIncome,
fields?.relatives ?? currentProfileReadModel.relatives
)
}
}

0 comments on commit f963d88

Please sign in to comment.