-
-
Notifications
You must be signed in to change notification settings - Fork 19
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add example content to monorepo template (#34)
- Loading branch information
Showing
17 changed files
with
277 additions
and
22 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
import { Args, Command, Options } from "@effect/cli" | ||
import { TodosClient } from "./TodosClient.js" | ||
|
||
const todoArg = Args.text({ name: "todo" }).pipe( | ||
Args.withDescription("The message associated with a todo") | ||
) | ||
|
||
const todoId = Options.integer("id").pipe( | ||
Options.withDescription("The identifier of the todo") | ||
) | ||
|
||
const add = Command.make("add", { todo: todoArg }).pipe( | ||
Command.withDescription("Add a new todo"), | ||
Command.withHandler(({ todo }) => TodosClient.create(todo)) | ||
) | ||
|
||
const done = Command.make("done", { id: todoId }).pipe( | ||
Command.withDescription("Mark a todo as done"), | ||
Command.withHandler(({ id }) => TodosClient.complete(id)) | ||
) | ||
|
||
const list = Command.make("list").pipe( | ||
Command.withDescription("List all todos"), | ||
Command.withHandler(() => TodosClient.list) | ||
) | ||
|
||
const remove = Command.make("remove", { id: todoId }).pipe( | ||
Command.withDescription("Remove a todo"), | ||
Command.withHandler(({ id }) => TodosClient.remove(id)) | ||
) | ||
|
||
const command = Command.make("todo").pipe( | ||
Command.withSubcommands([add, done, list, remove]) | ||
) | ||
|
||
export const cli = Command.run(command, { | ||
name: "Todo CLI", | ||
version: "0.0.0" | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
import { HttpApiClient } from "@effect/platform" | ||
import { TodosApi } from "@template/domain/TodosApi" | ||
import { Effect, Layer } from "effect" | ||
|
||
export const make = Effect.gen(function*() { | ||
const client = yield* HttpApiClient.make(TodosApi, { | ||
baseUrl: "http://localhost:3000" | ||
}) | ||
|
||
function create(text: string) { | ||
return client.todos.createTodo({ payload: { text } }).pipe( | ||
Effect.flatMap((todo) => Effect.logInfo("Created todo: ", todo)) | ||
) | ||
} | ||
|
||
const list = client.todos.getAllTodos().pipe( | ||
Effect.flatMap((todos) => Effect.logInfo(todos)) | ||
) | ||
|
||
function complete(id: number) { | ||
return client.todos.completeTodo({ path: { id } }).pipe( | ||
Effect.flatMap((todo) => Effect.logInfo("Marked todo completed: ", todo)), | ||
Effect.catchTag("TodoNotFound", () => Effect.logError(`Failed to find todo with id: ${id}`)) | ||
) | ||
} | ||
|
||
function remove(id: number) { | ||
return client.todos.removeTodo({ path: { id } }).pipe( | ||
Effect.flatMap(() => Effect.logInfo(`Deleted todo with id: ${id}`)), | ||
Effect.catchTag("TodoNotFound", () => Effect.logError(`Failed to find todo with id: ${id}`)) | ||
) | ||
} | ||
|
||
return { | ||
create, | ||
list, | ||
complete, | ||
remove | ||
} as const | ||
}) | ||
|
||
export class TodosClient extends Effect.Tag("cli/TodosClient")< | ||
TodosClient, | ||
Effect.Effect.Success<typeof make> | ||
>() { | ||
static Live = Layer.effect(this, make) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
#!/usr/bin/env node | ||
|
||
import { NodeContext, NodeHttpClient, NodeRuntime } from "@effect/platform-node" | ||
import { Effect, Layer } from "effect" | ||
import { cli } from "./Cli.js" | ||
import { TodosClient } from "./TodosClient.js" | ||
|
||
const MainLive = TodosClient.Live.pipe( | ||
Layer.provide(NodeHttpClient.layerUndici), | ||
Layer.merge(NodeContext.layer) | ||
) | ||
|
||
cli(process.argv).pipe( | ||
Effect.provide(MainLive), | ||
NodeRuntime.runMain | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
import { HttpApi, HttpApiEndpoint, HttpApiGroup } from "@effect/platform" | ||
import { Schema } from "@effect/schema" | ||
|
||
export const TodoId = Schema.Number.pipe(Schema.brand("TodoId")) | ||
export type TodoId = typeof TodoId.Type | ||
|
||
export const TodoIdFromString = Schema.NumberFromString.pipe( | ||
Schema.compose(TodoId) | ||
) | ||
|
||
export class Todo extends Schema.Class<Todo>("Todo")({ | ||
id: TodoId, | ||
text: Schema.NonEmptyTrimmedString, | ||
done: Schema.Boolean | ||
}) {} | ||
|
||
export class TodoNotFound extends Schema.TaggedError<TodoNotFound>()("TodoNotFound", { | ||
id: Schema.Number | ||
}) {} | ||
|
||
export class TodosApiGroup extends HttpApiGroup.make("todos").pipe( | ||
HttpApiGroup.add( | ||
HttpApiEndpoint.get("getAllTodos", "/todos").pipe( | ||
HttpApiEndpoint.setSuccess(Schema.Array(Todo)) | ||
) | ||
), | ||
HttpApiGroup.add( | ||
HttpApiEndpoint.get("getTodoById", "/todos/:id").pipe( | ||
HttpApiEndpoint.setSuccess(Todo), | ||
HttpApiEndpoint.addError(TodoNotFound, { status: 404 }), | ||
HttpApiEndpoint.setPath(Schema.Struct({ id: Schema.NumberFromString })) | ||
) | ||
), | ||
HttpApiGroup.add( | ||
HttpApiEndpoint.post("createTodo", "/todos").pipe( | ||
HttpApiEndpoint.setSuccess(Todo), | ||
HttpApiEndpoint.setPayload(Schema.Struct({ text: Schema.NonEmptyTrimmedString })) | ||
) | ||
), | ||
HttpApiGroup.add( | ||
HttpApiEndpoint.patch("completeTodo", "/todos/:id").pipe( | ||
HttpApiEndpoint.setSuccess(Todo), | ||
HttpApiEndpoint.addError(TodoNotFound, { status: 404 }), | ||
HttpApiEndpoint.setPath(Schema.Struct({ id: Schema.NumberFromString })) | ||
) | ||
), | ||
HttpApiGroup.add( | ||
HttpApiEndpoint.del("removeTodo", "/todos/:id").pipe( | ||
HttpApiEndpoint.setSuccess(Schema.Void), | ||
HttpApiEndpoint.addError(TodoNotFound, { status: 404 }), | ||
HttpApiEndpoint.setPath(Schema.Struct({ id: Schema.NumberFromString })) | ||
) | ||
) | ||
) {} | ||
|
||
export class TodosApi extends HttpApi.empty.pipe(HttpApi.addGroup(TodosApiGroup)) {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
import { HttpApiBuilder } from "@effect/platform" | ||
import { TodosApi } from "@template/domain/TodosApi" | ||
import { Effect, Layer } from "effect" | ||
import { TodosRepository } from "./TodosRepository.js" | ||
|
||
const TodosApiLive = HttpApiBuilder.group(TodosApi, "todos", (handlers) => | ||
Effect.gen(function*() { | ||
const todos = yield* TodosRepository | ||
return handlers.pipe( | ||
HttpApiBuilder.handle("getAllTodos", () => todos.getAll), | ||
HttpApiBuilder.handle("getTodoById", ({ path: { id } }) => todos.getById(id)), | ||
HttpApiBuilder.handle("createTodo", ({ payload: { text } }) => todos.create(text)), | ||
HttpApiBuilder.handle("completeTodo", ({ path: { id } }) => todos.complete(id)), | ||
HttpApiBuilder.handle("removeTodo", ({ path: { id } }) => todos.remove(id)) | ||
) | ||
})) | ||
|
||
export const ApiLive = HttpApiBuilder.api(TodosApi).pipe( | ||
Layer.provide(TodosApiLive) | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
import { Todo, TodoId, TodoNotFound } from "@template/domain/TodosApi" | ||
import { Context, Effect, HashMap, Layer, Ref } from "effect" | ||
|
||
const make = Effect.gen(function*() { | ||
const todos = yield* Ref.make(HashMap.empty<TodoId, Todo>()) | ||
|
||
const getAll = Ref.get(todos).pipe( | ||
Effect.map((todos) => Array.from(HashMap.values(todos))) | ||
) | ||
|
||
function getById(id: number): Effect.Effect<Todo, TodoNotFound> { | ||
return Ref.get(todos).pipe( | ||
Effect.flatMap(HashMap.get(id)), | ||
Effect.catchTag("NoSuchElementException", () => new TodoNotFound({ id })) | ||
) | ||
} | ||
|
||
function create(text: string): Effect.Effect<Todo> { | ||
return Ref.modify(todos, (map) => { | ||
const id = TodoId.make(HashMap.reduce(map, 0, (max, todo) => todo.id > max ? todo.id : max)) | ||
const todo = new Todo({ id, text, done: false }) | ||
return [todo, HashMap.set(map, id, todo)] | ||
}) | ||
} | ||
|
||
function complete(id: number): Effect.Effect<Todo, TodoNotFound> { | ||
return getById(id).pipe( | ||
Effect.map((todo) => new Todo({ ...todo, done: true })), | ||
Effect.tap((todo) => Ref.update(todos, HashMap.set(todo.id, todo))) | ||
) | ||
} | ||
|
||
function remove(id: number): Effect.Effect<void, TodoNotFound> { | ||
return getById(id).pipe( | ||
Effect.flatMap((todo) => Ref.update(todos, HashMap.remove(todo.id))) | ||
) | ||
} | ||
|
||
return { | ||
getAll, | ||
getById, | ||
create, | ||
complete, | ||
remove | ||
} as const | ||
}) | ||
|
||
export class TodosRepository extends Context.Tag("api/TodosRepository")< | ||
TodosRepository, | ||
Effect.Effect.Success<typeof make> | ||
>() { | ||
static Live = Layer.effect(this, make) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import { HttpApiBuilder, HttpMiddleware } from "@effect/platform" | ||
import { NodeHttpServer, NodeRuntime } from "@effect/platform-node" | ||
import { Layer } from "effect" | ||
import { createServer } from "node:http" | ||
import { ApiLive } from "./Api.js" | ||
import { TodosRepository } from "./TodosRepository.js" | ||
|
||
const HttpLive = HttpApiBuilder.serve(HttpMiddleware.logger).pipe( | ||
Layer.provide(ApiLive), | ||
Layer.provide(TodosRepository.Live), | ||
Layer.provide(NodeHttpServer.layer(createServer, { port: 3000 })) | ||
) | ||
|
||
Layer.launch(HttpLive).pipe( | ||
NodeRuntime.runMain | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters