From 29d8bf24d221def461b73478a8555caa787db012 Mon Sep 17 00:00:00 2001 From: Moya Mauthoor Date: Wed, 12 Oct 2022 14:16:47 +1100 Subject: [PATCH 1/3] Built out aggregate and command handler for moveup command --- .ruby-version | 2 +- README.md | 3 +++ app/aggregates/todo.rb | 10 +++++++++ app/commands/todo/moveup.rb | 41 +++++++++++++++++++++++++++++++++++++ app/events/todo_moveup.rb | 1 + app/web/server.rb | 7 +++++++ config/environment.rb | 1 + scripts/request | 17 +++++++++++++++ 8 files changed, 81 insertions(+), 1 deletion(-) create mode 100644 app/commands/todo/moveup.rb create mode 100644 app/events/todo_moveup.rb diff --git a/.ruby-version b/.ruby-version index a4dd9db..a603bb5 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -2.7.4 +2.7.5 diff --git a/README.md b/README.md index 53b6675..1bad882 100644 --- a/README.md +++ b/README.md @@ -59,6 +59,9 @@ $ ./scripts/request complete -i aac35923-39b4-4c39-ad5d-f79d67bb2fb2 -D 2017-01- # Abandon $ ./scripts/request abandon -i aac35923-39b4-4c39-ad5d-f79d67bb2fb2 -D 2017-01-01 +# Move up +$ ./scripts/request moveup -i aac35923-39b4-4c39-ad5d-f79d67bb2fb2 + # List $ ./scripts/request list -l outstanding $ ./scripts/request list -l scheduled diff --git a/app/aggregates/todo.rb b/app/aggregates/todo.rb index 89a4315..f4189eb 100644 --- a/app/aggregates/todo.rb +++ b/app/aggregates/todo.rb @@ -28,6 +28,9 @@ class Todo apply StakeholderNotifiedOfTodoCompletion do |event| end + apply TodoMoveup do |event| + end + def add(payload) raise UnprocessableEntity, "Todo #{id.inspect} already exists" if added? @@ -73,6 +76,13 @@ def abandon(payload) ) end + def moveup(payload) + apply_event(TodoMoveup, + aggregate_id: id, + body: payload, + ) + end + private def added? diff --git a/app/commands/todo/moveup.rb b/app/commands/todo/moveup.rb new file mode 100644 index 0000000..93bde1f --- /dev/null +++ b/app/commands/todo/moveup.rb @@ -0,0 +1,41 @@ +require 'app/aggregates/todo' + +module EventSourceryTodoApp + module Commands + module Todo + module Moveup + class Command + attr_reader :payload, :aggregate_id + + def self.build(**args) + new(**args).tap(&:validate) + end + + def initialize(params) + @payload = params.slice(:todo_id) + @aggregate_id = payload.delete(:todo_id) + end + + def validate + end + end + + class CommandHandler + def initialize(repository: EventSourceryTodoApp.repository) + @repository = repository + end + + def handle(command) + aggregate = repository.load(Aggregates::Todo, command.aggregate_id) + aggregate.moveup(command.payload) + repository.save(aggregate) + end + + private + + attr_reader :repository + end + end + end + end +end \ No newline at end of file diff --git a/app/events/todo_moveup.rb b/app/events/todo_moveup.rb new file mode 100644 index 0000000..f9e2872 --- /dev/null +++ b/app/events/todo_moveup.rb @@ -0,0 +1 @@ +TodoMoveup = Class.new(EventSourcery::Event) \ No newline at end of file diff --git a/app/web/server.rb b/app/web/server.rb index 966088f..c069378 100644 --- a/app/web/server.rb +++ b/app/web/server.rb @@ -5,6 +5,7 @@ require 'app/commands/todo/add' require 'app/commands/todo/amend' require 'app/commands/todo/complete' +require 'app/commands/todo/moveup' require 'app/projections/completed_todos/query' require 'app/projections/outstanding_todos/query' require 'app/projections/scheduled_todos/query' @@ -70,6 +71,12 @@ def json_params status 200 end + put '/todo/:todo_id/moveup' do + command = Commands::Todo::Moveup::Command.build(json_params) + Commands::Todo::Moveup::CommandHandler.new.handle(command) + status 200 + end + # Queries get '/todos/outstanding' do diff --git a/config/environment.rb b/config/environment.rb index bc8405d..d112e0f 100644 --- a/config/environment.rb +++ b/config/environment.rb @@ -6,6 +6,7 @@ require 'app/events/todo_added' require 'app/events/todo_amended' require 'app/events/todo_completed' +require 'app/events/todo_moveup' require 'app/events/stakeholder_notified_of_todo_completion' require 'app/errors' require 'app/projections/completed_todos/projector' diff --git a/scripts/request b/scripts/request index 040114b..c09cab5 100755 --- a/scripts/request +++ b/scripts/request @@ -136,6 +136,23 @@ command :complete do |c| end end +command :moveup do |c| + c.syntax = "#{script_name} Move up [options]" + c.summary = 'Move up a Todo item via the Todo web API' + c.option '-i ID', '--id ID', 'Todo ID' + c.action do |args, options| + todo_id = options.id + unless todo_id + $stderr.puts "Error: you must specify a Todo ID for the new Todo" + $stderr.puts "You can generate one using `#{script_name} uuid`." + exit 1 + end + payload = slice_and_compact(options.default) + puts "Moving up todo [#{todo_id}]: #{payload}" + Request.call(:put, "/todo/#{todo_id}/moveup", payload) + end +end + command :list do |c| LISTS = [ 'outstanding', From 110ffc0bacbb860cbd499e52afcb9f6f0a1ba573 Mon Sep 17 00:00:00 2001 From: Moya Mauthoor Date: Thu, 13 Oct 2022 16:18:11 +1100 Subject: [PATCH 2/3] Add moveup projector but unsure how to add corresponding DB table --- README.md | 1 + Rakefile | 4 +++ app/projections/movedup_todos/projector.rb | 42 ++++++++++++++++++++++ app/projections/movedup_todos/query.rb | 12 +++++++ app/web/server.rb | 9 +++++ config/environment.rb | 1 + scripts/request | 1 + 7 files changed, 70 insertions(+) create mode 100644 app/projections/movedup_todos/projector.rb create mode 100644 app/projections/movedup_todos/query.rb diff --git a/README.md b/README.md index 1bad882..f953b12 100644 --- a/README.md +++ b/README.md @@ -66,6 +66,7 @@ $ ./scripts/request moveup -i aac35923-39b4-4c39-ad5d-f79d67bb2fb2 $ ./scripts/request list -l outstanding $ ./scripts/request list -l scheduled $ ./scripts/request list -l completed +$ ./scripts/request list -l movedup ``` ## Application Structure diff --git a/Rakefile b/Rakefile index 4584a44..7b9f210 100644 --- a/Rakefile +++ b/Rakefile @@ -18,6 +18,10 @@ def processors(db_connection, tracker) EventSourceryTodoApp::Reactors::TodoCompletedNotifier.new( tracker: tracker, db_connection: db_connection, + ), + EventSourceryTodoApp::Reactors::MovedupTodos::Projector.new( + tracker: tracker, + db_connection: db_connection, ) ] end diff --git a/app/projections/movedup_todos/projector.rb b/app/projections/movedup_todos/projector.rb new file mode 100644 index 0000000..82f4a17 --- /dev/null +++ b/app/projections/movedup_todos/projector.rb @@ -0,0 +1,42 @@ +module EventSourceryTodoApp + module Projections + module MovedupTodos + class Projector + include EventSourcery::Postgres::Projector + + projector_name :movedup_todos + + table :query_movedup_todos do + column :todo_id, 'UUID NOT NULL' + column :title, :text + column :description, :text + column :due_date, DateTime + column :stakeholder_email, :text + end + + project TodoMoveup do |event| + table.insert( + todo_id: event.aggregate_id, + title: event.body['title'], + description: event.body['description'], + due_date: event.body['due_date'], + stakeholder_email: event.body['stakeholder_email'], + ) + end + + project TodoAmended do |event| + table.where( + todo_id: event.aggregate_id, + ).update( + event.body.slice('title', 'description', 'due_date', 'stakeholder_email') + ) + end + + project TodoCompleted, TodoAbandoned do |event| + table.where(todo_id: event.aggregate_id).delete + end + + end + end + end +end \ No newline at end of file diff --git a/app/projections/movedup_todos/query.rb b/app/projections/movedup_todos/query.rb new file mode 100644 index 0000000..e60ec8c --- /dev/null +++ b/app/projections/movedup_todos/query.rb @@ -0,0 +1,12 @@ +module EventSourceryTodoApp + module Projections + module MovedupTodos + # Query handler that queries the projection table. + class Query + def self.handle + EventSourceryTodoApp.projections_database[:query_movedup_todos].all + end + end + end + end +end diff --git a/app/web/server.rb b/app/web/server.rb index c069378..ef0af23 100644 --- a/app/web/server.rb +++ b/app/web/server.rb @@ -9,6 +9,8 @@ require 'app/projections/completed_todos/query' require 'app/projections/outstanding_todos/query' require 'app/projections/scheduled_todos/query' +require 'app/projections/movedup_todos/query' + module EventSourceryTodoApp class Server < Sinatra::Base @@ -99,5 +101,12 @@ def json_params ) status 200 end + + get '/todos/movedup' do + body JSON.pretty_generate( + EventSourceryTodoApp::Projections::MovedupTodos::Query.handle + ) + status 200 + end end end diff --git a/config/environment.rb b/config/environment.rb index d112e0f..3e6550c 100644 --- a/config/environment.rb +++ b/config/environment.rb @@ -12,6 +12,7 @@ require 'app/projections/completed_todos/projector' require 'app/projections/outstanding_todos/projector' require 'app/projections/scheduled_todos/projector' +require 'app/projections/movedup_todos/projector' require 'app/reactors/todo_completed_notifier' # Configure EventSourcery and our Postgres event store. diff --git a/scripts/request b/scripts/request index c09cab5..0b41b84 100755 --- a/scripts/request +++ b/scripts/request @@ -158,6 +158,7 @@ command :list do |c| 'outstanding', 'scheduled', 'completed', + 'movedup' ] c.syntax = "#{script_name} list [options]" From 41fbcad29173178f5b28e8fe9da02bf32f26dce0 Mon Sep 17 00:00:00 2001 From: Moya Mauthoor Date: Fri, 14 Oct 2022 10:40:21 +1100 Subject: [PATCH 3/3] Update projection to read from event stream --- Rakefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Rakefile b/Rakefile index 7b9f210..b8763a6 100644 --- a/Rakefile +++ b/Rakefile @@ -19,7 +19,7 @@ def processors(db_connection, tracker) tracker: tracker, db_connection: db_connection, ), - EventSourceryTodoApp::Reactors::MovedupTodos::Projector.new( + EventSourceryTodoApp::Projections::MovedupTodos::Projector.new( tracker: tracker, db_connection: db_connection, )