From 06fd404d44b44a53733f889aabd4da63f21e2f36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karol=20S=C3=B3jko?= Date: Mon, 5 Dec 2022 10:22:59 +0100 Subject: [PATCH] feat(domain-core): distinguish between username and email --- .../src/Domain/Entity/AnalyticsEntity.ts | 2 +- .../SubscriptionCancelledEventHandler.ts | 4 +- .../SubscriptionExpiredEventHandler.ts | 4 +- .../SubscriptionPurchasedEventHandler.ts | 4 +- .../SubscriptionRefundedEventHandler.ts | 4 +- .../SubscriptionRenewedEventHandler.ts | 4 +- .../Handler/UserRegisteredEventHandler.ts | 2 +- .../src/Domain/Map/RevenueModificationMap.ts | 6 +- .../Revenue/RevenueModification.spec.ts | 4 +- .../GetUserAnalyticsId.spec.ts | 2 +- .../GetUserAnalyticsId/GetUserAnalyticsId.ts | 4 +- .../GetUserAnalyticsIdResponse.ts | 4 +- .../SaveRevenueModification.spec.ts | 20 ++--- .../SaveRevenueModification.ts | 2 +- .../SaveRevenueModificationDTO.ts | 4 +- .../analytics/src/Domain/User/User.spec.ts | 4 +- .../analytics/src/Domain/User/UserProps.ts | 4 +- .../TypeORM/TypeORMRevenueModification.ts | 2 +- .../src/Domain/Common/Email.spec.ts | 2 +- .../domain-core/src/Domain/Common/Email.ts | 10 ++- .../src/Domain/Common/RoleName.spec.ts | 10 ++- .../domain-core/src/Domain/Common/RoleName.ts | 2 +- .../Domain/Common/RoleNameCollection.spec.ts | 85 +++++++++++++------ .../src/Domain/Common/Username.spec.ts | 16 ++++ .../domain-core/src/Domain/Common/Username.ts | 22 +++++ .../src/Domain/Common/UsernameProps.ts | 3 + .../src/Domain/Core/Validator.spec.ts | 49 +++++++++++ .../domain-core/src/Domain/Core/Validator.ts | 23 +++++ packages/domain-core/src/Domain/index.ts | 2 + 29 files changed, 229 insertions(+), 75 deletions(-) create mode 100644 packages/domain-core/src/Domain/Common/Username.spec.ts create mode 100644 packages/domain-core/src/Domain/Common/Username.ts create mode 100644 packages/domain-core/src/Domain/Common/UsernameProps.ts diff --git a/packages/analytics/src/Domain/Entity/AnalyticsEntity.ts b/packages/analytics/src/Domain/Entity/AnalyticsEntity.ts index ed168cebf..00300bbb3 100644 --- a/packages/analytics/src/Domain/Entity/AnalyticsEntity.ts +++ b/packages/analytics/src/Domain/Entity/AnalyticsEntity.ts @@ -18,5 +18,5 @@ export class AnalyticsEntity { nullable: true, }) @Index('email') - declare userEmail: string + declare username: string } diff --git a/packages/analytics/src/Domain/Handler/SubscriptionCancelledEventHandler.ts b/packages/analytics/src/Domain/Handler/SubscriptionCancelledEventHandler.ts index 5aa1a969f..4d912b070 100644 --- a/packages/analytics/src/Domain/Handler/SubscriptionCancelledEventHandler.ts +++ b/packages/analytics/src/Domain/Handler/SubscriptionCancelledEventHandler.ts @@ -1,7 +1,7 @@ import { DomainEventHandlerInterface, SubscriptionCancelledEvent } from '@standardnotes/domain-events' import { inject, injectable } from 'inversify' import { Logger } from 'winston' -import { Email } from '@standardnotes/domain-core' +import { Username } from '@standardnotes/domain-core' import TYPES from '../../Bootstrap/Types' import { AnalyticsActivity } from '../Analytics/AnalyticsActivity' @@ -41,7 +41,7 @@ export class SubscriptionCancelledEventHandler implements DomainEventHandlerInte payedAmount: event.payload.payAmount, planName: SubscriptionPlanName.create(event.payload.subscriptionName).getValue(), subscriptionId: event.payload.subscriptionId, - userEmail: Email.create(event.payload.userEmail).getValue(), + username: Username.create(event.payload.userEmail).getValue(), userUuid, }) diff --git a/packages/analytics/src/Domain/Handler/SubscriptionExpiredEventHandler.ts b/packages/analytics/src/Domain/Handler/SubscriptionExpiredEventHandler.ts index 9bf4b02db..6b35eddf3 100644 --- a/packages/analytics/src/Domain/Handler/SubscriptionExpiredEventHandler.ts +++ b/packages/analytics/src/Domain/Handler/SubscriptionExpiredEventHandler.ts @@ -1,7 +1,7 @@ +import { Username } from '@standardnotes/domain-core' import { DomainEventHandlerInterface, SubscriptionExpiredEvent } from '@standardnotes/domain-events' import { inject, injectable } from 'inversify' import { Logger } from 'winston' -import { Email } from '@standardnotes/domain-core' import TYPES from '../../Bootstrap/Types' import { AnalyticsActivity } from '../Analytics/AnalyticsActivity' @@ -45,7 +45,7 @@ export class SubscriptionExpiredEventHandler implements DomainEventHandlerInterf payedAmount: event.payload.payAmount, planName: SubscriptionPlanName.create(event.payload.subscriptionName).getValue(), subscriptionId: event.payload.subscriptionId, - userEmail: Email.create(event.payload.userEmail).getValue(), + username: Username.create(event.payload.userEmail).getValue(), userUuid, }) diff --git a/packages/analytics/src/Domain/Handler/SubscriptionPurchasedEventHandler.ts b/packages/analytics/src/Domain/Handler/SubscriptionPurchasedEventHandler.ts index abe194c93..0366cea89 100644 --- a/packages/analytics/src/Domain/Handler/SubscriptionPurchasedEventHandler.ts +++ b/packages/analytics/src/Domain/Handler/SubscriptionPurchasedEventHandler.ts @@ -1,7 +1,7 @@ +import { Username } from '@standardnotes/domain-core' import { DomainEventHandlerInterface, SubscriptionPurchasedEvent } from '@standardnotes/domain-events' import { inject, injectable } from 'inversify' import { Logger } from 'winston' -import { Email } from '@standardnotes/domain-core' import TYPES from '../../Bootstrap/Types' import { AnalyticsActivity } from '../Analytics/AnalyticsActivity' @@ -69,7 +69,7 @@ export class SubscriptionPurchasedEventHandler implements DomainEventHandlerInte payedAmount: event.payload.payAmount, planName: SubscriptionPlanName.create(event.payload.subscriptionName).getValue(), subscriptionId: event.payload.subscriptionId, - userEmail: Email.create(event.payload.userEmail).getValue(), + username: Username.create(event.payload.userEmail).getValue(), userUuid, }) diff --git a/packages/analytics/src/Domain/Handler/SubscriptionRefundedEventHandler.ts b/packages/analytics/src/Domain/Handler/SubscriptionRefundedEventHandler.ts index 968114240..b8e7f59cd 100644 --- a/packages/analytics/src/Domain/Handler/SubscriptionRefundedEventHandler.ts +++ b/packages/analytics/src/Domain/Handler/SubscriptionRefundedEventHandler.ts @@ -1,7 +1,7 @@ +import { Username } from '@standardnotes/domain-core' import { DomainEventHandlerInterface, SubscriptionRefundedEvent } from '@standardnotes/domain-events' import { inject, injectable } from 'inversify' import { Logger } from 'winston' -import { Email } from '@standardnotes/domain-core' import TYPES from '../../Bootstrap/Types' import { AnalyticsActivity } from '../Analytics/AnalyticsActivity' @@ -41,7 +41,7 @@ export class SubscriptionRefundedEventHandler implements DomainEventHandlerInter payedAmount: event.payload.payAmount, planName: SubscriptionPlanName.create(event.payload.subscriptionName).getValue(), subscriptionId: event.payload.subscriptionId, - userEmail: Email.create(event.payload.userEmail).getValue(), + username: Username.create(event.payload.userEmail).getValue(), userUuid, }) diff --git a/packages/analytics/src/Domain/Handler/SubscriptionRenewedEventHandler.ts b/packages/analytics/src/Domain/Handler/SubscriptionRenewedEventHandler.ts index 5fab9a39b..6550827b1 100644 --- a/packages/analytics/src/Domain/Handler/SubscriptionRenewedEventHandler.ts +++ b/packages/analytics/src/Domain/Handler/SubscriptionRenewedEventHandler.ts @@ -1,6 +1,6 @@ import { DomainEventHandlerInterface, SubscriptionRenewedEvent } from '@standardnotes/domain-events' import { inject, injectable } from 'inversify' -import { Email } from '@standardnotes/domain-core' +import { Username } from '@standardnotes/domain-core' import TYPES from '../../Bootstrap/Types' import { GetUserAnalyticsId } from '../UseCase/GetUserAnalyticsId/GetUserAnalyticsId' @@ -41,7 +41,7 @@ export class SubscriptionRenewedEventHandler implements DomainEventHandlerInterf payedAmount: event.payload.payAmount, planName: SubscriptionPlanName.create(event.payload.subscriptionName).getValue(), subscriptionId: event.payload.subscriptionId, - userEmail: Email.create(event.payload.userEmail).getValue(), + username: Username.create(event.payload.userEmail).getValue(), userUuid, }) diff --git a/packages/analytics/src/Domain/Handler/UserRegisteredEventHandler.ts b/packages/analytics/src/Domain/Handler/UserRegisteredEventHandler.ts index 3d4a96589..5b74b3af1 100644 --- a/packages/analytics/src/Domain/Handler/UserRegisteredEventHandler.ts +++ b/packages/analytics/src/Domain/Handler/UserRegisteredEventHandler.ts @@ -18,7 +18,7 @@ export class UserRegisteredEventHandler implements DomainEventHandlerInterface { async handle(event: UserRegisteredEvent): Promise { let analyticsEntity = new AnalyticsEntity() analyticsEntity.userUuid = event.payload.userUuid - analyticsEntity.userEmail = event.payload.email + analyticsEntity.username = event.payload.email analyticsEntity = await this.analyticsEntityRepository.save(analyticsEntity) await this.analyticsStore.markActivity([AnalyticsActivity.Register], analyticsEntity.id, [ diff --git a/packages/analytics/src/Domain/Map/RevenueModificationMap.ts b/packages/analytics/src/Domain/Map/RevenueModificationMap.ts index 963a7b9a5..ebce2afed 100644 --- a/packages/analytics/src/Domain/Map/RevenueModificationMap.ts +++ b/packages/analytics/src/Domain/Map/RevenueModificationMap.ts @@ -1,5 +1,5 @@ import { injectable } from 'inversify' -import { Email, MapperInterface, UniqueEntityId } from '@standardnotes/domain-core' +import { MapperInterface, UniqueEntityId, Username } from '@standardnotes/domain-core' import { TypeORMRevenueModification } from '../../Infra/TypeORM/TypeORMRevenueModification' import { MonthlyRevenue } from '../Revenue/MonthlyRevenue' @@ -14,7 +14,7 @@ export class RevenueModificationMap implements MapperInterface { planName: SubscriptionPlanName.create('PRO_PLAN').getValue(), }).getValue() user = User.create({ - email: Email.create('test@test.te').getValue(), + username: Username.create('test@test.te').getValue(), }).getValue() }) diff --git a/packages/analytics/src/Domain/UseCase/GetUserAnalyticsId/GetUserAnalyticsId.spec.ts b/packages/analytics/src/Domain/UseCase/GetUserAnalyticsId/GetUserAnalyticsId.spec.ts index e43d5c216..285a8ca65 100644 --- a/packages/analytics/src/Domain/UseCase/GetUserAnalyticsId/GetUserAnalyticsId.spec.ts +++ b/packages/analytics/src/Domain/UseCase/GetUserAnalyticsId/GetUserAnalyticsId.spec.ts @@ -15,7 +15,7 @@ describe('GetUserAnalyticsId', () => { analyticsEntity = { id: 123, userUuid: '84c0f8e8-544a-4c7e-9adf-26209303bc1d', - userEmail: 'test@test.te', + username: 'test@test.te', } as jest.Mocked analyticsEntityRepository = {} as jest.Mocked diff --git a/packages/analytics/src/Domain/UseCase/GetUserAnalyticsId/GetUserAnalyticsId.ts b/packages/analytics/src/Domain/UseCase/GetUserAnalyticsId/GetUserAnalyticsId.ts index c944979dd..5ffc8a24a 100644 --- a/packages/analytics/src/Domain/UseCase/GetUserAnalyticsId/GetUserAnalyticsId.ts +++ b/packages/analytics/src/Domain/UseCase/GetUserAnalyticsId/GetUserAnalyticsId.ts @@ -1,5 +1,5 @@ import { inject, injectable } from 'inversify' -import { Email, Uuid } from '@standardnotes/domain-core' +import { Username, Uuid } from '@standardnotes/domain-core' import TYPES from '../../../Bootstrap/Types' import { AnalyticsEntityRepositoryInterface } from '../../Entity/AnalyticsEntityRepositoryInterface' @@ -28,7 +28,7 @@ export class GetUserAnalyticsId implements UseCaseInterface { return { analyticsId: analyticsEntity.id, userUuid: Uuid.create(analyticsEntity.userUuid).getValue(), - userEmail: Email.create(analyticsEntity.userEmail).getValue(), + username: Username.create(analyticsEntity.username).getValue(), } } } diff --git a/packages/analytics/src/Domain/UseCase/GetUserAnalyticsId/GetUserAnalyticsIdResponse.ts b/packages/analytics/src/Domain/UseCase/GetUserAnalyticsId/GetUserAnalyticsIdResponse.ts index af3873927..260d09f18 100644 --- a/packages/analytics/src/Domain/UseCase/GetUserAnalyticsId/GetUserAnalyticsIdResponse.ts +++ b/packages/analytics/src/Domain/UseCase/GetUserAnalyticsId/GetUserAnalyticsIdResponse.ts @@ -1,7 +1,7 @@ -import { Email, Uuid } from '@standardnotes/domain-core' +import { Username, Uuid } from '@standardnotes/domain-core' export type GetUserAnalyticsIdResponse = { analyticsId: number - userEmail: Email + username: Username userUuid: Uuid } diff --git a/packages/analytics/src/Domain/UseCase/SaveRevenueModification/SaveRevenueModification.spec.ts b/packages/analytics/src/Domain/UseCase/SaveRevenueModification/SaveRevenueModification.spec.ts index 03e1cc7c2..a67a46ad3 100644 --- a/packages/analytics/src/Domain/UseCase/SaveRevenueModification/SaveRevenueModification.spec.ts +++ b/packages/analytics/src/Domain/UseCase/SaveRevenueModification/SaveRevenueModification.spec.ts @@ -1,7 +1,7 @@ import 'reflect-metadata' import { TimerInterface } from '@standardnotes/time' -import { Email, Result, Uuid } from '@standardnotes/domain-core' +import { Result, Username, Uuid } from '@standardnotes/domain-core' import { MonthlyRevenue } from '../../Revenue/MonthlyRevenue' @@ -45,7 +45,7 @@ describe('SaveRevenueModification', () => { payedAmount: 12.99, planName: SubscriptionPlanName.create('PRO_PLAN').getValue(), subscriptionId: 1234, - userEmail: Email.create('test@test.te').getValue(), + username: Username.create('test@test.te').getValue(), userUuid: Uuid.create('84c0f8e8-544a-4c7e-9adf-26209303bc1d').getValue(), }) @@ -63,7 +63,7 @@ describe('SaveRevenueModification', () => { payedAmount: 12.99, planName: SubscriptionPlanName.create('PRO_PLAN').getValue(), subscriptionId: 1234, - userEmail: Email.create('test@test.te').getValue(), + username: Username.create('test@test.te').getValue(), userUuid: Uuid.create('84c0f8e8-544a-4c7e-9adf-26209303bc1d').getValue(), }) @@ -81,7 +81,7 @@ describe('SaveRevenueModification', () => { payedAmount: 2, planName: SubscriptionPlanName.create('PRO_PLAN').getValue(), subscriptionId: 1234, - userEmail: Email.create('test@test.te').getValue(), + username: Username.create('test@test.te').getValue(), userUuid: Uuid.create('84c0f8e8-544a-4c7e-9adf-26209303bc1d').getValue(), }) @@ -101,7 +101,7 @@ describe('SaveRevenueModification', () => { payedAmount: 12.99, planName: SubscriptionPlanName.create('PRO_PLAN').getValue(), subscriptionId: 1234, - userEmail: Email.create('test@test.te').getValue(), + username: Username.create('test@test.te').getValue(), userUuid: Uuid.create('84c0f8e8-544a-4c7e-9adf-26209303bc1d').getValue(), }) @@ -122,7 +122,7 @@ describe('SaveRevenueModification', () => { payedAmount: 12.99, planName: SubscriptionPlanName.create('PRO_PLAN').getValue(), subscriptionId: 1234, - userEmail: Email.create('test@test.te').getValue(), + username: Username.create('test@test.te').getValue(), userUuid: Uuid.create('84c0f8e8-544a-4c7e-9adf-26209303bc1d').getValue(), }) @@ -142,7 +142,7 @@ describe('SaveRevenueModification', () => { payedAmount: 12.99, planName: SubscriptionPlanName.create('PRO_PLAN').getValue(), subscriptionId: 1234, - userEmail: Email.create('test@test.te').getValue(), + username: Username.create('test@test.te').getValue(), userUuid: Uuid.create('84c0f8e8-544a-4c7e-9adf-26209303bc1d').getValue(), }) @@ -162,7 +162,7 @@ describe('SaveRevenueModification', () => { payedAmount: 12.99, planName: SubscriptionPlanName.create('PRO_PLAN').getValue(), subscriptionId: 1234, - userEmail: Email.create('test@test.te').getValue(), + username: Username.create('test@test.te').getValue(), userUuid: Uuid.create('84c0f8e8-544a-4c7e-9adf-26209303bc1d').getValue(), }) @@ -182,7 +182,7 @@ describe('SaveRevenueModification', () => { payedAmount: 12.99, planName: SubscriptionPlanName.create('PRO_PLAN').getValue(), subscriptionId: 1234, - userEmail: Email.create('test@test.te').getValue(), + username: Username.create('test@test.te').getValue(), userUuid: Uuid.create('84c0f8e8-544a-4c7e-9adf-26209303bc1d').getValue(), }) @@ -202,7 +202,7 @@ describe('SaveRevenueModification', () => { payedAmount: 12.99, planName: SubscriptionPlanName.create('PRO_PLAN').getValue(), subscriptionId: 1234, - userEmail: Email.create('test@test.te').getValue(), + username: Username.create('test@test.te').getValue(), userUuid: Uuid.create('84c0f8e8-544a-4c7e-9adf-26209303bc1d').getValue(), }) diff --git a/packages/analytics/src/Domain/UseCase/SaveRevenueModification/SaveRevenueModification.ts b/packages/analytics/src/Domain/UseCase/SaveRevenueModification/SaveRevenueModification.ts index 2899e4937..db3643223 100644 --- a/packages/analytics/src/Domain/UseCase/SaveRevenueModification/SaveRevenueModification.ts +++ b/packages/analytics/src/Domain/UseCase/SaveRevenueModification/SaveRevenueModification.ts @@ -23,7 +23,7 @@ export class SaveRevenueModification implements DomainUseCaseInterface> { const userOrError = User.create( { - email: dto.userEmail, + username: dto.username, }, new UniqueEntityId(dto.userUuid.value), ) diff --git a/packages/analytics/src/Domain/UseCase/SaveRevenueModification/SaveRevenueModificationDTO.ts b/packages/analytics/src/Domain/UseCase/SaveRevenueModification/SaveRevenueModificationDTO.ts index 914633a51..cad49872f 100644 --- a/packages/analytics/src/Domain/UseCase/SaveRevenueModification/SaveRevenueModificationDTO.ts +++ b/packages/analytics/src/Domain/UseCase/SaveRevenueModification/SaveRevenueModificationDTO.ts @@ -1,4 +1,4 @@ -import { Email, Uuid } from '@standardnotes/domain-core' +import { Username, Uuid } from '@standardnotes/domain-core' import { SubscriptionEventType } from '../../Subscription/SubscriptionEventType' import { SubscriptionPlanName } from '../../Subscription/SubscriptionPlanName' @@ -9,7 +9,7 @@ export interface SaveRevenueModificationDTO { planName: SubscriptionPlanName newSubscriber: boolean userUuid: Uuid - userEmail: Email + username: Username subscriptionId: number billingFrequency: number } diff --git a/packages/analytics/src/Domain/User/User.spec.ts b/packages/analytics/src/Domain/User/User.spec.ts index 7845883fd..e2e4b2667 100644 --- a/packages/analytics/src/Domain/User/User.spec.ts +++ b/packages/analytics/src/Domain/User/User.spec.ts @@ -1,11 +1,11 @@ -import { Email } from '@standardnotes/domain-core' +import { Username } from '@standardnotes/domain-core' import { User } from './User' describe('User', () => { it('should create an entity', () => { const user = User.create({ - email: Email.create('test@test.te').getValue(), + username: Username.create('test@test.te').getValue(), }).getValue() expect(user.id.toString()).toHaveLength(36) diff --git a/packages/analytics/src/Domain/User/UserProps.ts b/packages/analytics/src/Domain/User/UserProps.ts index 18c137a08..fa569710b 100644 --- a/packages/analytics/src/Domain/User/UserProps.ts +++ b/packages/analytics/src/Domain/User/UserProps.ts @@ -1,5 +1,5 @@ -import { Email } from '@standardnotes/domain-core' +import { Username } from '@standardnotes/domain-core' export interface UserProps { - email: Email + username: Username } diff --git a/packages/analytics/src/Infra/TypeORM/TypeORMRevenueModification.ts b/packages/analytics/src/Infra/TypeORM/TypeORMRevenueModification.ts index 43cc30f7e..aa1b25e1a 100644 --- a/packages/analytics/src/Infra/TypeORM/TypeORMRevenueModification.ts +++ b/packages/analytics/src/Infra/TypeORM/TypeORMRevenueModification.ts @@ -18,7 +18,7 @@ export class TypeORMRevenueModification { length: 255, }) @Index('email') - declare userEmail: string + declare username: string @Column({ name: 'user_uuid', diff --git a/packages/domain-core/src/Domain/Common/Email.spec.ts b/packages/domain-core/src/Domain/Common/Email.spec.ts index ddcecdf54..c7c92b87f 100644 --- a/packages/domain-core/src/Domain/Common/Email.spec.ts +++ b/packages/domain-core/src/Domain/Common/Email.spec.ts @@ -9,7 +9,7 @@ describe('Email', () => { }) it('should not create an invalid value object', () => { - const valueOrError = Email.create('') + const valueOrError = Email.create('foobar') expect(valueOrError.isFailed()).toBeTruthy() }) diff --git a/packages/domain-core/src/Domain/Common/Email.ts b/packages/domain-core/src/Domain/Common/Email.ts index 577dc5202..6cfa6e722 100644 --- a/packages/domain-core/src/Domain/Common/Email.ts +++ b/packages/domain-core/src/Domain/Common/Email.ts @@ -1,6 +1,7 @@ import { ValueObject } from '../Core/ValueObject' import { Result } from '../Core/Result' import { EmailProps } from './EmailProps' +import { Validator } from '../Core/Validator' export class Email extends ValueObject { get value(): string { @@ -12,10 +13,11 @@ export class Email extends ValueObject { } static create(email: string): Result { - if (!!email === false || email.length === 0) { - return Result.fail('Email cannot be empty') - } else { - return Result.ok(new Email({ value: email })) + const emailValidation = Validator.isValidEmail(email) + if (emailValidation.isFailed()) { + return Result.fail(emailValidation.getError()) } + + return Result.ok(new Email({ value: email })) } } diff --git a/packages/domain-core/src/Domain/Common/RoleName.spec.ts b/packages/domain-core/src/Domain/Common/RoleName.spec.ts index 51e2c37dc..1abe2761c 100644 --- a/packages/domain-core/src/Domain/Common/RoleName.spec.ts +++ b/packages/domain-core/src/Domain/Common/RoleName.spec.ts @@ -2,7 +2,7 @@ import { RoleName } from './RoleName' describe('RoleName', () => { it('should create a value object', () => { - const valueOrError = RoleName.create('PRO_USER') + const valueOrError = RoleName.create(RoleName.NAMES.ProUser) expect(valueOrError.isFailed()).toBeFalsy() expect(valueOrError.getValue().value).toEqual('PRO_USER') @@ -17,9 +17,9 @@ describe('RoleName', () => { }) it('should say if a role has more power or equal power to another role', () => { - const proUserRole = RoleName.create('PRO_USER').getValue() - const plusUserRole = RoleName.create('PLUS_USER').getValue() - const coreUser = RoleName.create('CORE_USER').getValue() + const proUserRole = RoleName.create(RoleName.NAMES.ProUser).getValue() + const plusUserRole = RoleName.create(RoleName.NAMES.PlusUser).getValue() + const coreUser = RoleName.create(RoleName.NAMES.CoreUser).getValue() expect(proUserRole.hasMoreOrEqualPowerTo(proUserRole)).toBeTruthy() expect(proUserRole.hasMoreOrEqualPowerTo(plusUserRole)).toBeTruthy() @@ -32,5 +32,7 @@ describe('RoleName', () => { expect(coreUser.hasMoreOrEqualPowerTo(proUserRole)).toBeFalsy() expect(coreUser.hasMoreOrEqualPowerTo(plusUserRole)).toBeFalsy() expect(coreUser.hasMoreOrEqualPowerTo(coreUser)).toBeTruthy() + + expect(RoleName.create(RoleName.NAMES.FilesBetaUser).getValue().hasMoreOrEqualPowerTo(coreUser)).toBeFalsy() }) }) diff --git a/packages/domain-core/src/Domain/Common/RoleName.ts b/packages/domain-core/src/Domain/Common/RoleName.ts index 7c14ec030..75fc5ba0c 100644 --- a/packages/domain-core/src/Domain/Common/RoleName.ts +++ b/packages/domain-core/src/Domain/Common/RoleName.ts @@ -3,7 +3,7 @@ import { Result } from '../Core/Result' import { RoleNameProps } from './RoleNameProps' export class RoleName extends ValueObject { - private static readonly NAMES = { + static readonly NAMES = { CoreUser: 'CORE_USER', PlusUser: 'PLUS_USER', ProUser: 'PRO_USER', diff --git a/packages/domain-core/src/Domain/Common/RoleNameCollection.spec.ts b/packages/domain-core/src/Domain/Common/RoleNameCollection.spec.ts index 8b9b16765..484a5dfef 100644 --- a/packages/domain-core/src/Domain/Common/RoleNameCollection.spec.ts +++ b/packages/domain-core/src/Domain/Common/RoleNameCollection.spec.ts @@ -3,7 +3,7 @@ import { RoleNameCollection } from './RoleNameCollection' describe('RoleNameCollection', () => { it('should create a value object', () => { - const role1 = RoleName.create('PRO_USER').getValue() + const role1 = RoleName.create(RoleName.NAMES.ProUser).getValue() const valueOrError = RoleNameCollection.create([role1]) @@ -12,23 +12,38 @@ describe('RoleNameCollection', () => { }) it('should tell if collections are not equal', () => { - const roles1 = [RoleName.create('PRO_USER').getValue(), RoleName.create('PLUS_USER').getValue()] + const roles1 = [ + RoleName.create(RoleName.NAMES.ProUser).getValue(), + RoleName.create(RoleName.NAMES.PlusUser).getValue(), + ] - const roles2 = RoleNameCollection.create([ - RoleName.create('PRO_USER').getValue(), - RoleName.create('CORE_USER').getValue(), + let roles2 = RoleNameCollection.create([ + RoleName.create(RoleName.NAMES.ProUser).getValue(), + RoleName.create(RoleName.NAMES.CoreUser).getValue(), ]).getValue() - const valueOrError = RoleNameCollection.create(roles1) + let valueOrError = RoleNameCollection.create(roles1) + expect(valueOrError.getValue().equals(roles2)).toBeFalsy() + + roles2 = RoleNameCollection.create([ + RoleName.create(RoleName.NAMES.ProUser).getValue(), + RoleName.create(RoleName.NAMES.PlusUser).getValue(), + RoleName.create(RoleName.NAMES.CoreUser).getValue(), + ]).getValue() + + valueOrError = RoleNameCollection.create(roles1) expect(valueOrError.getValue().equals(roles2)).toBeFalsy() }) it('should tell if collections are equal', () => { - const roles1 = [RoleName.create('PRO_USER').getValue(), RoleName.create('PLUS_USER').getValue()] + const roles1 = [ + RoleName.create(RoleName.NAMES.ProUser).getValue(), + RoleName.create(RoleName.NAMES.PlusUser).getValue(), + ] const roles2 = RoleNameCollection.create([ - RoleName.create('PRO_USER').getValue(), - RoleName.create('PLUS_USER').getValue(), + RoleName.create(RoleName.NAMES.ProUser).getValue(), + RoleName.create(RoleName.NAMES.PlusUser).getValue(), ]).getValue() const valueOrError = RoleNameCollection.create(roles1) @@ -36,42 +51,62 @@ describe('RoleNameCollection', () => { }) it('should tell if collection includes element', () => { - const roles1 = [RoleName.create('PRO_USER').getValue(), RoleName.create('PLUS_USER').getValue()] + const roles1 = [ + RoleName.create(RoleName.NAMES.ProUser).getValue(), + RoleName.create(RoleName.NAMES.PlusUser).getValue(), + ] const valueOrError = RoleNameCollection.create(roles1) - expect(valueOrError.getValue().includes(RoleName.create('PRO_USER').getValue())).toBeTruthy() + expect(valueOrError.getValue().includes(RoleName.create(RoleName.NAMES.ProUser).getValue())).toBeTruthy() }) it('should tell if collection does not includes element', () => { - const roles1 = [RoleName.create('PRO_USER').getValue(), RoleName.create('PLUS_USER').getValue()] + const roles1 = [ + RoleName.create(RoleName.NAMES.ProUser).getValue(), + RoleName.create(RoleName.NAMES.PlusUser).getValue(), + ] const valueOrError = RoleNameCollection.create(roles1) - expect(valueOrError.getValue().includes(RoleName.create('CORE_USER').getValue())).toBeFalsy() + expect(valueOrError.getValue().includes(RoleName.create(RoleName.NAMES.CoreUser).getValue())).toBeFalsy() }) it('should tell if collection has a role with more or equal power to', () => { - let roles = [RoleName.create('CORE_USER').getValue()] + let roles = [RoleName.create(RoleName.NAMES.CoreUser).getValue()] let valueOrError = RoleNameCollection.create(roles) let roleNames = valueOrError.getValue() - expect(roleNames.hasARoleNameWithMoreOrEqualPowerTo(RoleName.create('PLUS_USER').getValue())).toBeFalsy() - expect(roleNames.hasARoleNameWithMoreOrEqualPowerTo(RoleName.create('PRO_USER').getValue())).toBeFalsy() - expect(roleNames.hasARoleNameWithMoreOrEqualPowerTo(RoleName.create('CORE_USER').getValue())).toBeTruthy() + expect( + roleNames.hasARoleNameWithMoreOrEqualPowerTo(RoleName.create(RoleName.NAMES.PlusUser).getValue()), + ).toBeFalsy() + expect(roleNames.hasARoleNameWithMoreOrEqualPowerTo(RoleName.create(RoleName.NAMES.ProUser).getValue())).toBeFalsy() + expect( + roleNames.hasARoleNameWithMoreOrEqualPowerTo(RoleName.create(RoleName.NAMES.CoreUser).getValue()), + ).toBeTruthy() - roles = [RoleName.create('CORE_USER').getValue(), RoleName.create('PLUS_USER').getValue()] + roles = [RoleName.create(RoleName.NAMES.CoreUser).getValue(), RoleName.create(RoleName.NAMES.PlusUser).getValue()] valueOrError = RoleNameCollection.create(roles) roleNames = valueOrError.getValue() - expect(roleNames.hasARoleNameWithMoreOrEqualPowerTo(RoleName.create('PLUS_USER').getValue())).toBeTruthy() - expect(roleNames.hasARoleNameWithMoreOrEqualPowerTo(RoleName.create('PRO_USER').getValue())).toBeFalsy() - expect(roleNames.hasARoleNameWithMoreOrEqualPowerTo(RoleName.create('CORE_USER').getValue())).toBeTruthy() + expect( + roleNames.hasARoleNameWithMoreOrEqualPowerTo(RoleName.create(RoleName.NAMES.PlusUser).getValue()), + ).toBeTruthy() + expect(roleNames.hasARoleNameWithMoreOrEqualPowerTo(RoleName.create(RoleName.NAMES.ProUser).getValue())).toBeFalsy() + expect( + roleNames.hasARoleNameWithMoreOrEqualPowerTo(RoleName.create(RoleName.NAMES.CoreUser).getValue()), + ).toBeTruthy() - roles = [RoleName.create('PRO_USER').getValue(), RoleName.create('PLUS_USER').getValue()] + roles = [RoleName.create(RoleName.NAMES.ProUser).getValue(), RoleName.create(RoleName.NAMES.PlusUser).getValue()] valueOrError = RoleNameCollection.create(roles) roleNames = valueOrError.getValue() - expect(roleNames.hasARoleNameWithMoreOrEqualPowerTo(RoleName.create('PLUS_USER').getValue())).toBeTruthy() - expect(roleNames.hasARoleNameWithMoreOrEqualPowerTo(RoleName.create('PRO_USER').getValue())).toBeTruthy() - expect(roleNames.hasARoleNameWithMoreOrEqualPowerTo(RoleName.create('CORE_USER').getValue())).toBeTruthy() + expect( + roleNames.hasARoleNameWithMoreOrEqualPowerTo(RoleName.create(RoleName.NAMES.PlusUser).getValue()), + ).toBeTruthy() + expect( + roleNames.hasARoleNameWithMoreOrEqualPowerTo(RoleName.create(RoleName.NAMES.ProUser).getValue()), + ).toBeTruthy() + expect( + roleNames.hasARoleNameWithMoreOrEqualPowerTo(RoleName.create(RoleName.NAMES.CoreUser).getValue()), + ).toBeTruthy() }) }) diff --git a/packages/domain-core/src/Domain/Common/Username.spec.ts b/packages/domain-core/src/Domain/Common/Username.spec.ts new file mode 100644 index 000000000..53475b255 --- /dev/null +++ b/packages/domain-core/src/Domain/Common/Username.spec.ts @@ -0,0 +1,16 @@ +import { Username } from './Username' + +describe('Username', () => { + it('should create a value object', () => { + const valueOrError = Username.create('test@test.te') + + expect(valueOrError.isFailed()).toBeFalsy() + expect(valueOrError.getValue().value).toEqual('test@test.te') + }) + + it('should not create an invalid value object', () => { + const valueOrError = Username.create('') + + expect(valueOrError.isFailed()).toBeTruthy() + }) +}) diff --git a/packages/domain-core/src/Domain/Common/Username.ts b/packages/domain-core/src/Domain/Common/Username.ts new file mode 100644 index 000000000..6272fe95a --- /dev/null +++ b/packages/domain-core/src/Domain/Common/Username.ts @@ -0,0 +1,22 @@ +import { ValueObject } from '../Core/ValueObject' +import { Result } from '../Core/Result' +import { UsernameProps } from './UsernameProps' +import { Validator } from '../Core/Validator' + +export class Username extends ValueObject { + get value(): string { + return this.props.value + } + + private constructor(props: UsernameProps) { + super(props) + } + + static create(username: string): Result { + if (Validator.isNotEmpty(username).isFailed()) { + return Result.fail('Username cannot be empty') + } + + return Result.ok(new Username({ value: username })) + } +} diff --git a/packages/domain-core/src/Domain/Common/UsernameProps.ts b/packages/domain-core/src/Domain/Common/UsernameProps.ts new file mode 100644 index 000000000..4f5347c4a --- /dev/null +++ b/packages/domain-core/src/Domain/Common/UsernameProps.ts @@ -0,0 +1,3 @@ +export interface UsernameProps { + value: string +} diff --git a/packages/domain-core/src/Domain/Core/Validator.spec.ts b/packages/domain-core/src/Domain/Core/Validator.spec.ts index 46d4ebd01..4a99bfcb0 100644 --- a/packages/domain-core/src/Domain/Core/Validator.spec.ts +++ b/packages/domain-core/src/Domain/Core/Validator.spec.ts @@ -18,6 +18,31 @@ describe('Validator', () => { '../../escaped.sh', ] + const validEmails = [ + 'something@something.com', + 'someone@localhost.localdomain', + 'a/b@domain.com', + '{}@domain.com', + 'karol+test@standardnotes.com', + "m*'!%@something.sa", + 'tu!!7n7.ad##0!!!@company.ca', + '%@com.com', + "!#$%&'*+/=?^_`{|}~.-@com.com", + 'someone@do-ma-in.com', + '""testlah""@example.com', + ] + + const invalidEmails = [ + 'someone@127.0.0.1', + 'a@b.b', + '', + null, + '.wooly@example.com', + 'wo..oly@example.com', + 'somebody@example', + 'a @p.com', + ] + it('should validate proper uuids', () => { for (const validUuid of validUuids) { expect(Validator.isValidUuid(validUuid).isFailed()).toBeFalsy() @@ -29,4 +54,28 @@ describe('Validator', () => { expect(Validator.isValidUuid(invalidUuid as string).isFailed()).toBeTruthy() } }) + + it('should validate proper emails', () => { + for (const validEmail of validEmails) { + expect(Validator.isValidEmail(validEmail).isFailed()).toBeFalsy() + } + }) + + it('should not validate invalid emails', () => { + for (const invalidEmail of invalidEmails) { + expect(Validator.isValidEmail(invalidEmail as string).isFailed()).toBeTruthy() + } + }) + + it('should validate value if not empty', () => { + for (const value of [1, 'foobar', {}, 0]) { + expect(Validator.isNotEmpty(value).isFailed()).toBeFalsy() + } + }) + + it('should not validate value if empty', () => { + for (const value of [null, undefined, '', []]) { + expect(Validator.isNotEmpty(value).isFailed()).toBeTruthy() + } + }) }) diff --git a/packages/domain-core/src/Domain/Core/Validator.ts b/packages/domain-core/src/Domain/Core/Validator.ts index 9594c5ffe..7f6cf34d5 100644 --- a/packages/domain-core/src/Domain/Core/Validator.ts +++ b/packages/domain-core/src/Domain/Core/Validator.ts @@ -2,6 +2,8 @@ import { Result } from './Result' export class Validator { private static readonly UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$/i + private static readonly EMAIL_REGEX = + /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/ static isValidUuid(value: string): Result { const matchesUuidRegex = String(value).toLowerCase().match(Validator.UUID_REGEX) !== null @@ -11,4 +13,25 @@ export class Validator { return Result.fail(`Given value is not a valid uuid: ${value}`) } + + static isValidEmail(value: string): Result { + const matchesUuidRegex = String(value).toLowerCase().match(Validator.EMAIL_REGEX) !== null + if (matchesUuidRegex) { + return Result.ok() + } + + return Result.fail(`Given value is not a valid email address: ${value}`) + } + + static isNotEmpty(value: unknown): Result { + if (value instanceof Array && value.length === 0) { + return Result.fail(`Given value is empty: ${value}`) + } + + if (value === null || value === undefined || value === '') { + return Result.fail(`Given value is empty: ${value}`) + } + + return Result.ok() + } } diff --git a/packages/domain-core/src/Domain/index.ts b/packages/domain-core/src/Domain/index.ts index 165e9987d..42f547e6b 100644 --- a/packages/domain-core/src/Domain/index.ts +++ b/packages/domain-core/src/Domain/index.ts @@ -6,6 +6,8 @@ export * from './Common/RoleName' export * from './Common/RoleNameProps' export * from './Common/RoleNameCollection' export * from './Common/RoleNameCollectionProps' +export * from './Common/Username' +export * from './Common/UsernameProps' export * from './Common/Uuid' export * from './Common/UuidProps'