Skip to content

Commit

Permalink
feat: rename port; add initial character support
Browse files Browse the repository at this point in the history
  • Loading branch information
SomethingSexy committed Sep 26, 2024
1 parent e7bcb7e commit d0f902c
Show file tree
Hide file tree
Showing 22 changed files with 179 additions and 51 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ Run all migrations on a new database
```shell
atlas migrate apply \
--url "postgres://postgres:postgres@db:5432/chronicle?sslmode=disable" \
--dir "file://migrations"
--dir "file://migrations"
```

Migrate from a baseline, the baseline will be ignored
Expand Down
36 changes: 36 additions & 0 deletions internal/chronicle/adapter/http/character/character_request.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package character

import (
"errors"
"net/http"

"github.com/SomethingSexy/chronicle/internal/chronicle/core/domain"
"github.com/google/uuid"
)

type CharacterRequest struct {
ID string `jsonapi:"primary,characters"`
CharacterId string `jsonapi:"attr,characterId"`
Name string `jsonapi:"attr,name"`
Description string `jsonapi:"attr,description"`
}

func (c *CharacterRequest) Bind(r *http.Request) error {
if c.Name == "" {
return errors.New("missing required name fields")
}

if _, err := uuid.Parse(c.CharacterId); err != nil {
return errors.New("characterId must be valid UUID")
}

return nil
}

func (c *CharacterRequest) ToDomain() domain.Character {
return domain.Character{
CharacterId: uuid.MustParse(c.CharacterId),
Name: c.Name,
Description: c.Description,
}
}
47 changes: 47 additions & 0 deletions internal/chronicle/adapter/http/character/server.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package character

import (
"net/http"

corePort "github.com/SomethingSexy/chronicle/internal/chronicle/core/port"
"github.com/SomethingSexy/chronicle/internal/chronicle/port"
"github.com/SomethingSexy/chronicle/internal/common"
"github.com/go-chi/chi/v5"
"github.com/go-chi/render"
)

func NewCharacterHttpServer(commands port.CharacterCommands, queries port.CharacterQueries) CharacterHttpServer {
return CharacterHttpServer{
commands: commands,
queries: queries,
}
}

type CharacterHttpServer struct {
commands port.CharacterCommands
queries port.CharacterQueries
}

func (h CharacterHttpServer) Routes() chi.Router {
r := chi.NewRouter()
r.Post("/", h.CreateCharacter)

return r
}

func (h CharacterHttpServer) CreateCharacter(w http.ResponseWriter, r *http.Request) {
data := &CharacterRequest{}
if err := render.Bind(r, data); err != nil {
render.Render(w, r, common.ErrInvalidRequest(err))
return
}

if err := h.commands.CreateCharacter.Handle(r.Context(), corePort.CreateCharacter{
Character: data.ToDomain(),
}); err != nil {
render.Render(w, r, common.ErrInvalidRequest(err))
return
}

render.Status(r, http.StatusCreated)
}
4 changes: 2 additions & 2 deletions internal/chronicle/adapter/http/game/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,15 @@ import (
"github.com/google/uuid"
)

func NewGameHttpServer(commands port.ChronicleCommands, queries port.GameQueries) GameHttpServer {
func NewGameHttpServer(commands port.GameCommands, queries port.GameQueries) GameHttpServer {
return GameHttpServer{
commands: commands,
queries: queries,
}
}

type GameHttpServer struct {
commands port.ChronicleCommands
commands port.GameCommands
queries port.GameQueries
}

Expand Down
5 changes: 4 additions & 1 deletion internal/chronicle/adapter/http/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ type HttpServer struct {
app common.Service
}

// TODO: Does it make more sense that we pass in the core interfaces here
// and keep the route logic internal to this package, instead of passing it in?
func NewHttpServer(application common.Service) HttpServer {
return HttpServer{
app: application,
Expand All @@ -30,7 +32,8 @@ func (h HttpServer) Start() {
render.Decode = DefaultDecoder

// TODO: Given the application, this should mount all of the route handlers
r.Mount("/games", h.app.Routes()[0])
r.Mount("/games", h.app.Routes()["Games"][0])
r.Mount("/characters", h.app.Routes()["Characters"][0])

http.ListenAndServe(":3000", r)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ CREATE TABLE "public"."character" (
"character_id" uuid NOT NULL,
"name" text NOT NULL,
"description" text NULL,
"created_at" timestamptz NOT NULL DEFAULT now(),
"updated_at" timestamptz NOT NULL DEFAULT now(),
PRIMARY KEY ("id"),
CONSTRAINT "character_character_id_key" UNIQUE ("character_id")
);
Expand All @@ -15,6 +17,8 @@ CREATE TABLE "public"."game" (
"game_id" uuid NOT NULL,
"name" text NOT NULL,
"type" text NOT NULL,
"created_at" timestamptz NOT NULL DEFAULT now(),
"updated_at" timestamptz NOT NULL DEFAULT now(),
PRIMARY KEY ("id"),
CONSTRAINT "game_game_id_key" UNIQUE ("game_id")
);
Expand All @@ -24,6 +28,8 @@ CREATE TABLE "public"."world" (
"world_id" uuid NOT NULL,
"game_id" bigserial NOT NULL,
"name" text NOT NULL,
"created_at" timestamptz NOT NULL DEFAULT now(),
"updated_at" timestamptz NOT NULL DEFAULT now(),
PRIMARY KEY ("id"),
CONSTRAINT "world_world_id_key" UNIQUE ("world_id"),
CONSTRAINT "world_game_id_fkey" FOREIGN KEY ("game_id") REFERENCES "public"."game" ("id") ON UPDATE NO ACTION ON DELETE NO ACTION
Expand All @@ -39,6 +45,8 @@ CREATE TABLE "public"."location" (
"type" text NOT NULL,
"name" text NOT NULL,
"path" public.ltree NULL,
"created_at" timestamptz NOT NULL DEFAULT now(),
"updated_at" timestamptz NOT NULL DEFAULT now(),
PRIMARY KEY ("id"),
CONSTRAINT "location_location_id_key" UNIQUE ("location_id"),
CONSTRAINT "location_game_id_fkey" FOREIGN KEY ("game_id") REFERENCES "public"."game" ("id") ON UPDATE NO ACTION ON DELETE NO ACTION,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
h1:DljSNBalsjIkrSZ+WOGYFkwLIstGT0W6kK3Zalo3/9Q=
20240924154059_initial.sql h1:YA7/O03b3X56utueibLwOYeFGKNHYWsGZ+3nyM+YWq8=
h1:TdZuQJmsSrzILr2eodHh12uuIpXTkEIV6zGSwf9SSnk=
20240926193418_initial.sql h1:JA1YcfhJ3X2c51i2AuEQdTXOHrWn+Mfmc1OidtC7oSo=
14 changes: 10 additions & 4 deletions internal/chronicle/core/application/application.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,28 @@ import (
)

func NewApplication(persistence port.Persistence) port.ChronicleApplication {
commands := port.ChronicleCommands{
gameCommands := port.GameCommands{
CreateGame: command.NewCreateGameCommand(persistence.Game),
CreateWorld: command.NewCreateWorldCommand(persistence.Game),
CreateLocation: command.NewCreateLocationCommand(persistence.Game),
}

queries := port.GameQueries{
characterCommands := port.CharacterCommands{
CreateCharacter: command.NewCreateCharacterCommand(persistence.Character),
}

gameQueries := port.GameQueries{
ListGames: query.NewListGamesHandler(persistence.Game),
GetGame: query.NewGetGameHandler(persistence.Game),
ListLocations: query.NewListLocationsHandler(persistence.Game),
GetWorld: query.NewGetWorldHandler(persistence.Game),
}

characterQueries := port.CharacterQueries{}

return port.ChronicleApplication{
Commands: commands,
Queries: queries,
Commands: port.ChronicleCommands{GameCommands: gameCommands, CharacterCommands: characterCommands},
Queries: port.ChronicleQueries{GameQueries: gameQueries, CharacterQueries: characterQueries},
Persistence: persistence,
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,14 @@ import (
"github.com/SomethingSexy/chronicle/internal/common"
)

func NewCreateCharacterCommand(persistence port.CharacterQueries) common.CommandHandler[corePort.CreateCharacter] {
func NewCreateCharacterCommand(persistence port.CharacterPersistence) common.CommandHandler[corePort.CreateCharacter] {
return createCharacterHandler{
Persistence: persistence,
}
}

type createCharacterHandler struct {
Persistence port.CharacterQueries
Persistence port.CharacterPersistence
}

func (c createCharacterHandler) Handle(ctx context.Context, cmd corePort.CreateCharacter) error {
Expand Down
4 changes: 2 additions & 2 deletions internal/chronicle/core/application/command/create_game.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,14 @@ import (
"github.com/SomethingSexy/chronicle/internal/common"
)

func NewCreateGameCommand(persistence port.ChronicleQueries) common.CommandHandler[gamePort.CreateGame] {
func NewCreateGameCommand(persistence port.GamePersistence) common.CommandHandler[gamePort.CreateGame] {
return createGameHandler{
Persistence: persistence,
}
}

type createGameHandler struct {
Persistence port.ChronicleQueries
Persistence port.GamePersistence
}

func (c createGameHandler) Handle(ctx context.Context, cmd gamePort.CreateGame) error {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,14 @@ import (
"github.com/SomethingSexy/chronicle/internal/common"
)

func NewCreateLocationCommand(persistence port.ChronicleQueries) common.CommandHandler[gamePort.CreateLocation] {
func NewCreateLocationCommand(persistence port.GamePersistence) common.CommandHandler[gamePort.CreateLocation] {
return createLocationHandler{
Persistence: persistence,
}
}

type createLocationHandler struct {
Persistence port.ChronicleQueries
Persistence port.GamePersistence
}

func (c createLocationHandler) Handle(ctx context.Context, cmd gamePort.CreateLocation) error {
Expand Down
4 changes: 2 additions & 2 deletions internal/chronicle/core/application/command/create_world.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,14 @@ import (
"github.com/SomethingSexy/chronicle/internal/common"
)

func NewCreateWorldCommand(persistence port.ChronicleQueries) common.CommandHandler[gamePort.CreateWorld] {
func NewCreateWorldCommand(persistence port.GamePersistence) common.CommandHandler[gamePort.CreateWorld] {
return createWorldHandler{
Persistence: persistence,
}
}

type createWorldHandler struct {
Persistence port.ChronicleQueries
Persistence port.GamePersistence
}

func (c createWorldHandler) Handle(ctx context.Context, cmd gamePort.CreateWorld) error {
Expand Down
4 changes: 2 additions & 2 deletions internal/chronicle/core/application/query/get_game.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@ import (
"github.com/SomethingSexy/chronicle/internal/chronicle/port"
)

func NewGetGameHandler(persistence port.ChronicleQueries) gamePort.GetGameHandler {
func NewGetGameHandler(persistence port.GamePersistence) gamePort.GetGameHandler {
return getGameHandler{
Persistence: persistence,
}
}

type getGameHandler struct {
Persistence port.ChronicleQueries
Persistence port.GamePersistence
}

func (h getGameHandler) Handle(ctx context.Context, q gamePort.GetGameQuery) (domain.Game, error) {
Expand Down
4 changes: 2 additions & 2 deletions internal/chronicle/core/application/query/get_world.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@ import (
"github.com/SomethingSexy/chronicle/internal/chronicle/port"
)

func NewGetWorldHandler(persistence port.ChronicleQueries) worldPort.GetWorldHandler {
func NewGetWorldHandler(persistence port.GamePersistence) worldPort.GetWorldHandler {
return getWorldHandler{
Persistence: persistence,
}
}

type getWorldHandler struct {
Persistence port.ChronicleQueries
Persistence port.GamePersistence
}

func (h getWorldHandler) Handle(ctx context.Context, q worldPort.GetWorldQuery) (domain.World, error) {
Expand Down
4 changes: 2 additions & 2 deletions internal/chronicle/core/application/query/list_game.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@ import (
"github.com/SomethingSexy/chronicle/internal/chronicle/port"
)

func NewListGamesHandler(persistence port.ChronicleQueries) gamePort.ListGamesHandler {
func NewListGamesHandler(persistence port.GamePersistence) gamePort.ListGamesHandler {
return listGamesHandler{
Persistence: persistence,
}
}

type listGamesHandler struct {
Persistence port.ChronicleQueries
Persistence port.GamePersistence
}

func (h listGamesHandler) Handle(ctx context.Context, _ gamePort.AllGamesQuery) ([]domain.Game, error) {
Expand Down
4 changes: 2 additions & 2 deletions internal/chronicle/core/application/query/list_location.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@ import (
"github.com/SomethingSexy/chronicle/internal/chronicle/port"
)

func NewListLocationsHandler(persistence port.ChronicleQueries) gamePort.ListLocationsHandler {
func NewListLocationsHandler(persistence port.GamePersistence) gamePort.ListLocationsHandler {
return listLocationsHandler{
Persistence: persistence,
}
}

type listLocationsHandler struct {
Persistence port.ChronicleQueries
Persistence port.GamePersistence
}

func (h listLocationsHandler) Handle(ctx context.Context, query gamePort.LocationsQuery) ([]domain.Location, error) {
Expand Down
2 changes: 1 addition & 1 deletion internal/chronicle/core/port/character.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,4 @@ type CreateCharacter struct {
Character domain.Character
}

type CreateCharacterHander common.CommandHandler[CreateCharacter]
type CreateCharacterHandler common.CommandHandler[CreateCharacter]
9 changes: 4 additions & 5 deletions internal/chronicle/port/persistence.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,11 @@ import (
// Port for the rest of the application to interface
// with the persistence layer
type Persistence struct {
Game ChronicleQueries
Character CharacterQueries
Game GamePersistence
Character CharacterPersistence
}

// TODO: Probably a better name for this
type ChronicleQueries interface {
type GamePersistence interface {
CreateGame(ctx context.Context, game domain.Game) (domain.Game, error)
ListGames(ctx context.Context) ([]domain.Game, error)
GetGame(ctx context.Context, id uuid.UUID) (domain.Game, error)
Expand All @@ -28,6 +27,6 @@ type ChronicleQueries interface {
ListLocations(ctx context.Context, gameId uuid.UUID, worldId uuid.UUID) ([]domain.Location, error)
}

type CharacterQueries interface {
type CharacterPersistence interface {
CreateCharacter(ctx context.Context, character domain.Character) (domain.Character, error)
}
Loading

0 comments on commit d0f902c

Please sign in to comment.