Skip to content

Commit

Permalink
Refactor create faction (#22)
Browse files Browse the repository at this point in the history
  • Loading branch information
AlexKMarshall committed Oct 7, 2022
2 parents dfa8a6f + 8d04366 commit a15b3ab
Show file tree
Hide file tree
Showing 7 changed files with 102 additions and 82 deletions.
10 changes: 6 additions & 4 deletions 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',
Expand Down
50 changes: 6 additions & 44 deletions 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<string, 'FactionName'>

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<CreateFactionError, CreateFactionEvent[]>
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)
)
}
28 changes: 15 additions & 13 deletions 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,
Expand Down
51 changes: 30 additions & 21 deletions 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<FactionValidationError, ValidatedFaction>

type CheckFactionExists = (name: string) => boolean
) => TE.TaskEither<FactionValidationError, ValidatedFaction>

class FactionNameAlreadyExistsError extends Error {
public _tag: 'FactionNameAlreadyExistsError'
Expand All @@ -40,29 +40,38 @@ const _tag = (name: FactionName.FactionName): UniqueFactionName =>
FactionName.value(name) as UniqueFactionName

export type UniqueFactionName = Opaque<string, 'UniqueFactionName'>

const toUniqueFactionName =
(checkFactionExists: CheckFactionExists) =>
(checkFactionNameExists: CheckFactionNameExists) =>
(
name: FactionName.FactionName
): E.Either<FactionNameAlreadyExistsError, UniqueFactionName> => {
if (checkFactionExists(name)) {
return pipe(name, FactionNameAlreadyExistsError.of, E.left)
}
return pipe(name, _tag, E.right)
): TE.TaskEither<FactionNameAlreadyExistsError, UniqueFactionName> => {
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)),
})
)
}
Expand Down
1 change: 1 addition & 0 deletions apps/domain/src/faction/create/index.ts
@@ -1 +1,2 @@
export * from './createFaction'
export * from './types'
43 changes: 43 additions & 0 deletions 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<boolean>

/*
* 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<CreateFactionError, CreateFactionEvent[]>
1 change: 1 addition & 0 deletions apps/domain/src/gang/foundGang/index.ts
@@ -1 +1,2 @@
export * from './foundGang'
export * from './types'

0 comments on commit a15b3ab

Please sign in to comment.