diff --git a/apps/domain/src/faction/create/createFaction.test.ts b/apps/domain/src/faction/create/createFaction.test.ts index 046b5db..fc64cf8 100644 --- a/apps/domain/src/faction/create/createFaction.test.ts +++ b/apps/domain/src/faction/create/createFaction.test.ts @@ -1,15 +1,17 @@ +import * as T from 'fp-ts/Task' import { describe, it, expect } from 'vitest' -import { createFaction, UnvalidatedFaction } from './createFaction' +import { createFaction } from './createFaction' +import { UnvalidatedFaction } from './types' describe('createFaction', () => { - it('should return a list of events', () => { - const checkFactionExists = () => false + it('should return a list of events', async () => { + const checkFactionNameExists = () => T.of(false) const unvalidatedFaction: UnvalidatedFaction = { name: 'Van Saar', } expect( - createFaction({ checkFactionExists })(unvalidatedFaction) + await createFaction({ checkFactionNameExists })(unvalidatedFaction)() ).toStrictEqualRight([ { event: 'factionCreated', diff --git a/apps/domain/src/faction/create/createFaction.ts b/apps/domain/src/faction/create/createFaction.ts index 04a78c3..d7fb237 100644 --- a/apps/domain/src/faction/create/createFaction.ts +++ b/apps/domain/src/faction/create/createFaction.ts @@ -1,52 +1,14 @@ import { pipe } from 'fp-ts/lib/function' -import { Opaque } from 'type-fest' -import { FactionId } from './factionId' -import { - createEvents, - FactionValidationError, - UniqueFactionName, - validateFaction, -} from './implementation' -import * as E from 'fp-ts/Either' - -export type UnvalidatedFaction = { - name: string -} - -export type FactionName = Opaque - -export type ValidatedFaction = { - name: UniqueFactionName - id: FactionId -} - -export type FactionCreated = { - event: 'factionCreated' - details: ValidatedFaction -} - -export type CreateFactionEvent = FactionCreated - -export type CheckFactionExists = (name: string) => boolean - -export type CreateFactionDependencies = { - checkFactionExists: CheckFactionExists -} - -export type CreateFactionError = FactionValidationError - -export type CreateFaction = ( - dependencies: CreateFactionDependencies -) => ( - unvalidatedFaction: UnvalidatedFaction -) => E.Either +import { createEvents, validateFaction } from './implementation' +import * as TE from 'fp-ts/TaskEither' +import { CreateFaction } from './types' export const createFaction: CreateFaction = - ({ checkFactionExists }) => + ({ checkFactionNameExists }) => (unvalidatedFaction) => { return pipe( unvalidatedFaction, - validateFaction(checkFactionExists), - E.map(createEvents) + validateFaction(checkFactionNameExists), + TE.map(createEvents) ) } diff --git a/apps/domain/src/faction/create/implementation.test.ts b/apps/domain/src/faction/create/implementation.test.ts index fbb16f4..a6b44fe 100644 --- a/apps/domain/src/faction/create/implementation.test.ts +++ b/apps/domain/src/faction/create/implementation.test.ts @@ -1,30 +1,32 @@ import { describe, it, expect } from 'vitest' -import { UnvalidatedFaction } from './createFaction' -import { toValidFactionName, validateFaction } from './implementation' +import { toValidFactionNameT, validateFaction } from './implementation' +import { UnvalidatedFaction } from './types' +import * as T from 'fp-ts/Task' describe('toValidFactionName', () => { - it('should pass a name that does not exist already', () => { - const checkFactionExists = () => false + it('should pass a name that does not exist already', async () => { + const checkFactionExists = () => T.of(false) const factionName = 'Goliath' - expect( - toValidFactionName(checkFactionExists)(factionName) - ).toStrictEqualRight(factionName) + const actual = await toValidFactionNameT(checkFactionExists)(factionName)() + expect(actual).toStrictEqualRight(factionName) }) - it('should reject pre-existing faction', () => { - const checkFactionExists = () => true + it('should reject pre-existing faction', async () => { + const checkFactionExists = () => T.of(true) const factionName = 'Escher' - expect(toValidFactionName(checkFactionExists)(factionName)).toBeLeft() + expect( + await toValidFactionNameT(checkFactionExists)(factionName)() + ).toBeLeft() }) }) describe('validateFaction', () => { - it('should pass a valid faction', () => { + it('should pass a valid faction', async () => { const unvalidatedFaction: UnvalidatedFaction = { name: 'Orlock', } - const checkFactionExists = () => false + const checkFactionExists = () => T.of(false) expect( - validateFaction(checkFactionExists)(unvalidatedFaction) + await validateFaction(checkFactionExists)(unvalidatedFaction)() ).toStrictEqualRight({ id: expect.any(String), name: unvalidatedFaction.name, diff --git a/apps/domain/src/faction/create/implementation.ts b/apps/domain/src/faction/create/implementation.ts index 201e8b0..b310ba7 100644 --- a/apps/domain/src/faction/create/implementation.ts +++ b/apps/domain/src/faction/create/implementation.ts @@ -1,28 +1,28 @@ import { sequenceS } from 'fp-ts/lib/Apply' import { flow, pipe } from 'fp-ts/lib/function' import * as E from 'fp-ts/Either' -import * as String50 from '../../common/string50' -import { - CreateFactionEvent, - UnvalidatedFaction, - ValidatedFaction, -} from './createFaction' import * as FactionId from './factionId' import { ConstrainedStringError } from '../../common/constrained' import { Opaque } from 'type-fest' import * as FactionName from './name' +import { + UnvalidatedFaction, + ValidatedFaction, + CreateFactionEvent, + CheckFactionNameExists, +} from './types' +import * as TE from 'fp-ts/TaskEither' +import * as T from 'fp-ts/Task' export type FactionValidationError = | ConstrainedStringError | FactionNameAlreadyExistsError type ValidateFaction = ( - checkFactionExists: CheckFactionExists + checkFactionNameExists: CheckFactionNameExists ) => ( unvalidatedFaction: UnvalidatedFaction -) => E.Either - -type CheckFactionExists = (name: string) => boolean +) => TE.TaskEither class FactionNameAlreadyExistsError extends Error { public _tag: 'FactionNameAlreadyExistsError' @@ -40,29 +40,38 @@ const _tag = (name: FactionName.FactionName): UniqueFactionName => FactionName.value(name) as UniqueFactionName export type UniqueFactionName = Opaque + const toUniqueFactionName = - (checkFactionExists: CheckFactionExists) => + (checkFactionNameExists: CheckFactionNameExists) => ( name: FactionName.FactionName - ): E.Either => { - if (checkFactionExists(name)) { - return pipe(name, FactionNameAlreadyExistsError.of, E.left) - } - return pipe(name, _tag, E.right) + ): TE.TaskEither => { + return pipe( + name, + checkFactionNameExists, + T.map((exists) => + exists + ? pipe(name, FactionNameAlreadyExistsError.of, E.left) + : pipe(name, _tag, E.right) + ) + ) } -export const toValidFactionName = (checkFactionExists: CheckFactionExists) => +export const toValidFactionNameT = ( + checkFactionNameExists: CheckFactionNameExists +) => flow( FactionName.parse('name'), - E.chainW(toUniqueFactionName(checkFactionExists)) + TE.fromEither, + TE.chainW(toUniqueFactionName(checkFactionNameExists)) ) export const validateFaction: ValidateFaction = (checkFactionExists) => (unvalidatedFaction) => { return pipe(unvalidatedFaction, ({ name }) => - sequenceS(E.Apply)({ - id: pipe(FactionId.create(), E.right), - name: pipe(name, toValidFactionName(checkFactionExists)), + sequenceS(TE.ApplySeq)({ + id: pipe(FactionId.create(), TE.right), + name: pipe(name, toValidFactionNameT(checkFactionExists)), }) ) } diff --git a/apps/domain/src/faction/create/index.ts b/apps/domain/src/faction/create/index.ts index 570d92e..c6b7062 100644 --- a/apps/domain/src/faction/create/index.ts +++ b/apps/domain/src/faction/create/index.ts @@ -1 +1,2 @@ export * from './createFaction' +export * from './types' diff --git a/apps/domain/src/faction/create/types.ts b/apps/domain/src/faction/create/types.ts new file mode 100644 index 0000000..a793fb9 --- /dev/null +++ b/apps/domain/src/faction/create/types.ts @@ -0,0 +1,43 @@ +import { FactionId } from './factionId' +import { FactionValidationError, UniqueFactionName } from './implementation' +import * as E from 'fp-ts/Either' +import * as T from 'fp-ts/Task' +import * as TE from 'fp-ts/TaskEither' + +/* + * External dependencies / DB Functions + */ + +export type CheckFactionNameExists = (name: string) => T.Task + +/* + * Public API + */ + +export type UnvalidatedFaction = { + name: string +} + +export type ValidatedFaction = { + name: UniqueFactionName + id: FactionId +} + +export type FactionCreated = { + event: 'factionCreated' + details: ValidatedFaction +} + +export type CreateFactionEvent = FactionCreated + +export type CreateFactionError = FactionValidationError + +export type CreateFactionDependencies = { + checkFactionNameExists: CheckFactionNameExists +} + +export type CreateFaction = ( + dependencies: CreateFactionDependencies +) => ( + unvalidatedFaction: UnvalidatedFaction +) => TE.TaskEither diff --git a/apps/domain/src/gang/foundGang/index.ts b/apps/domain/src/gang/foundGang/index.ts index 3c77285..af48a4b 100644 --- a/apps/domain/src/gang/foundGang/index.ts +++ b/apps/domain/src/gang/foundGang/index.ts @@ -1 +1,2 @@ export * from './foundGang' +export * from './types'