diff --git a/packages/server/src/Services.ts b/packages/server/src/Services.ts index 51821ae..9190799 100644 --- a/packages/server/src/Services.ts +++ b/packages/server/src/Services.ts @@ -1,9 +1,9 @@ import { Layer } from "effect" -import { TodoRepositoryLive } from "./TodoRepository" +import { TodoRepository } from "./TodoRepository" export const ServicesLive = Layer.mergeAll( - TodoRepositoryLive + TodoRepository.Live ) export interface Services extends Layer.Layer.Success {} diff --git a/packages/server/src/TodoRepository.ts b/packages/server/src/TodoRepository.ts index ea6fc66..c2284bc 100644 --- a/packages/server/src/TodoRepository.ts +++ b/packages/server/src/TodoRepository.ts @@ -5,11 +5,13 @@ import crypto from "node:crypto" export class TodoRepository extends Context.Tag("TodoRepository")() {} -export const TodoRepositoryLive = Layer.effect(TodoRepository, - SubscriptionRef.make(Array.empty }>()).pipe( - Effect.map(ref => new TodoRepositoryService(ref)) +export module TodoRepository { + export const Live = Layer.effect(TodoRepository, + SubscriptionRef.make(Array.empty }>()).pipe( + Effect.map(ref => new TodoRepositoryService(ref)) + ) ) -) +} export class TodoRepositoryService { @@ -17,13 +19,13 @@ export class TodoRepositoryService { readonly todos: SubscriptionRef.SubscriptionRef<(Todo & { id: Option.Some })[]> ) {} - get(id: string) { + getByID(id: string) { return this.todos.get.pipe( Effect.map(Array.findFirst(todo => Equal.equals(todo.id.value, id))) ) } - getIndex(id: string) { + getIndexByID(id: string) { return this.todos.get.pipe( Effect.map(Array.findFirstIndex(todo => Equal.equals(todo.id.value, id))) ) @@ -34,12 +36,14 @@ export class TodoRepositoryService { if (Option.isSome(todo.id)) return yield* Effect.fail(new TodoHasID({ todo })) - yield* Ref.update(this.todos, todos => - Array.append(todos, new Todo({ - ...todo, - id: Option.some(crypto.randomUUID()), - }) as Todo & { id: Option.Some }) - ) + const id: string = crypto.randomUUID() + + yield* Ref.update(this.todos, Array.append(new Todo({ + ...todo, + id: Option.some(id), + }) as Todo & { id: Option.Some })) + + return id }) } @@ -49,7 +53,7 @@ export class TodoRepositoryService { return yield* Effect.fail(new TodoHasNoID({ todo })) yield* Ref.update(this.todos, Array.replace( - yield* yield* this.getIndex(todo.id.value), + yield* yield* this.getIndexByID(todo.id.value), todo as Todo & { id: Option.Some }, )) }) @@ -61,7 +65,7 @@ export class TodoRepositoryService { return yield* Effect.fail(new TodoHasNoID({ todo })) yield* Ref.update(this.todos, Array.remove( - yield* yield* this.getIndex(todo.id.value) + yield* yield* this.getIndexByID(todo.id.value) )) }) } diff --git a/packages/server/src/index.ts b/packages/server/src/index.ts index 8c5bc2a..0054429 100644 --- a/packages/server/src/index.ts +++ b/packages/server/src/index.ts @@ -1,7 +1,7 @@ import { BunRuntime } from "@effect/platform-bun" import { Todo } from "@todo-tests/common/data" import { Identifiable } from "@todo-tests/common/traits" -import { Array, Duration, Effect, Layer, Option, Stream } from "effect" +import { Array, Effect, Layer, Option, Stream } from "effect" import { ServicesLive } from "./Services" import { TodoRepository, createDefaultTodos } from "./TodoRepository" import { ExpressApp } from "./express/ExpressApp" @@ -41,10 +41,10 @@ const watchTodoChanges = Effect.gen(function*() { }) const main = Effect.gen(function*() { - const watcher = yield* Effect.fork(watchTodoChanges) + // const watcher = yield* Effect.fork(watchTodoChanges) yield* createDefaultTodos - const todos = yield* TodoRepository + // const todos = yield* TodoRepository // const secondTodo = yield* yield* todos.todos.get.pipe( // Effect.map(Array.get(1)) @@ -55,17 +55,17 @@ const main = Effect.gen(function*() { // }) // yield* todos.update(secondTodoModified) - yield* todos.add(new Todo({ - id: Option.none(), - title: "Put the dishes in the dishwasher", - content: "Lorem ipsum", - due: Option.none(), - completed: false, - createdAt: new Date(), - updatedAt: new Date(), - })).pipe( - Effect.delay(Duration.seconds(1)) - ) + // yield* todos.add(new Todo({ + // id: Option.none(), + // title: "Put the dishes in the dishwasher", + // content: "Lorem ipsum", + // due: Option.none(), + // completed: false, + // createdAt: new Date(), + // updatedAt: new Date(), + // })).pipe( + // Effect.delay(Duration.seconds(1)) + // ) // yield* Fiber.join(watcher) yield* Layer.launch(ServerLive) diff --git a/packages/server/src/rpc/routers/todos.ts b/packages/server/src/rpc/routers/todos.ts index 017aa55..d1dac18 100644 --- a/packages/server/src/rpc/routers/todos.ts +++ b/packages/server/src/rpc/routers/todos.ts @@ -11,24 +11,41 @@ export const todosRouter = Effect.gen(function*() { const procedure = yield* RPCProcedureBuilder return t.router({ - all: procedure.query(({ ctx }) => ctx.run(Effect.gen(function*() { - const todos = yield* TodoRepository + all: procedure + .query(({ ctx }) => ctx.run(Effect.gen(function*() { + const todos = yield* TodoRepository - return yield* S.encode(S.Array(JsonifiableTodo))( - yield* todos.todos.get - ) - }))), + return yield* S.encode(S.Array(JsonifiableTodo))( + yield* todos.todos.get + ) + }))), - get: procedure - .input(S.decodeUnknownSync( - S.Struct({ id: S.String }) - )) + getByID: procedure + .input(S.decodeUnknownPromise(S.String)) .query(({ ctx, input }) => ctx.run(Effect.gen(function*() { const todos = yield* TodoRepository return yield* S.encode(S.OptionFromNullOr(JsonifiableTodo))( - yield* todos.get(input.id) + yield* todos.getByID(input) ) }))), + + add: procedure + .input(S.decodeUnknownPromise(JsonifiableTodo)) + .mutation(({ ctx, input }) => ctx.run(TodoRepository.pipe( + Effect.flatMap(todos => todos.add(input)) + ))), + + update: procedure + .input(S.decodeUnknownPromise(JsonifiableTodo)) + .mutation(({ ctx, input }) => ctx.run(TodoRepository.pipe( + Effect.flatMap(todos => todos.update(input)) + ))), + + remove: procedure + .input(S.decodeUnknownPromise(JsonifiableTodo)) + .mutation(({ ctx, input }) => ctx.run(TodoRepository.pipe( + Effect.flatMap(todos => todos.remove(input)) + ))), }) })