Commit 5083eeec authored by Luke Duncalfe's avatar Luke Duncalfe

Add copy states to DesignCollection

The copying of designs from one issue to another happens async, so the
state of the copy is tracked through the new `copy_state` state machine.

The state is persisted in redis as the DesignCollection model is a PORO
and not persisted in PostgreSQL.

https://gitlab.com/gitlab-org/gitlab/-/issues/13426
parent f0985664
...@@ -6,8 +6,34 @@ module DesignManagement ...@@ -6,8 +6,34 @@ module DesignManagement
delegate :designs, :project, to: :issue delegate :designs, :project, to: :issue
state_machine :copy_state, initial: :ready, namespace: :copy do
after_transition any => any, do: :update_stored_copy_state!
event :start do
transition ready: :in_progress
end
event :end do
transition in_progress: :ready
end
event :error do
transition in_progress: :error
end
event :reset do
transition any => :ready
end
end
def initialize(issue) def initialize(issue)
super() # Necessary to initialize state_machine
@issue = issue @issue = issue
if stored_copy_state = get_stored_copy_state
@copy_state = stored_copy_state
end
end end
def ==(other) def ==(other)
...@@ -30,5 +56,39 @@ module DesignManagement ...@@ -30,5 +56,39 @@ module DesignManagement
def designs_by_filename(filenames) def designs_by_filename(filenames)
designs.current.where(filename: filenames) designs.current.where(filename: filenames)
end end
private
def update_stored_copy_state!
# As "ready" is the initial copy state we can clear the cached value
# rather than persist it.
if copy_ready?
unset_store_copy_state!
else
set_stored_copy_state!
end
end
def copy_state_cache_key
"DesignCollection/copy_state/issue=#{issue.id}"
end
def get_stored_copy_state
Gitlab::Redis::SharedState.with do |redis|
redis.get(copy_state_cache_key)
end
end
def set_stored_copy_state!
Gitlab::Redis::SharedState.with do |redis|
redis.set(copy_state_cache_key, copy_state)
end
end
def unset_store_copy_state!
Gitlab::Redis::SharedState.with do |redis|
redis.del(copy_state_cache_key)
end
end
end end
end end
...@@ -3,8 +3,9 @@ require 'spec_helper' ...@@ -3,8 +3,9 @@ require 'spec_helper'
RSpec.describe DesignManagement::DesignCollection do RSpec.describe DesignManagement::DesignCollection do
include DesignManagementTestHelpers include DesignManagementTestHelpers
using RSpec::Parameterized::TableSyntax
let_it_be(:issue, reload: true) { create(:issue) } let_it_be(:issue, refind: true) { create(:issue) }
subject(:collection) { described_class.new(issue) } subject(:collection) { described_class.new(issue) }
...@@ -45,6 +46,61 @@ RSpec.describe DesignManagement::DesignCollection do ...@@ -45,6 +46,61 @@ RSpec.describe DesignManagement::DesignCollection do
end end
end end
describe "#copy_state", :clean_gitlab_redis_shared_state do
it "defaults to ready" do
expect(collection).to be_copy_ready
end
it "persists its state changes between initializations" do
collection.start_copy!
expect(described_class.new(issue)).to be_copy_in_progress
end
where(:state, :can_start, :can_end, :can_error, :can_reset) do
"ready" | true | false | true | true
"in_progress" | false | true | true | true
"error" | false | false | false | true
end
with_them do
it "maintains state machine transition rules", :aggregate_failures do
collection.copy_state = state
expect(collection.can_start_copy?).to eq(can_start)
expect(collection.can_end_copy?).to eq(can_end)
end
end
describe "clearing the redis cached state when state changes back to ready" do
def redis_copy_state
Gitlab::Redis::SharedState.with do |redis|
redis.get(collection.send(:copy_state_cache_key))
end
end
def fire_state_events(*events)
events.each do |event|
collection.fire_copy_state_event(event)
end
end
it "clears the cached state on end_copy!", :aggregate_failures do
fire_state_events(:start)
expect { collection.end_copy! }.to change { redis_copy_state }.from("in_progress").to(nil)
expect(collection).to be_copy_ready
end
it "clears the cached state on reset_copy!", :aggregate_failures do
fire_state_events(:start, :error)
expect { collection.reset_copy! }.to change { redis_copy_state }.from("error").to(nil)
expect(collection).to be_copy_ready
end
end
end
describe "#versions" do describe "#versions" do
it "includes versions for all designs" do it "includes versions for all designs" do
version_1 = create(:design_version) version_1 = create(:design_version)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment