-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Calendar synchronisation #125
Merged
Merged
Changes from 1 commit
Commits
Show all changes
31 commits
Select commit
Hold shift + click to select a range
f3f059e
Drop `update_calendar` and associated code.
6a4a0a4
Rename Workers::Synchronisation to Workers::PropertySynchronisation.
982a829
Introduce background workers, configurable per supplier.
4dd4ee3
Ability to filter workers for a given supplier.
61c05a8
Include flows for creating suppliers and associated workers.
afb4826
Drop the contradicting on_delete: set null option on foreign keys.
5438de3
Flexible supplier sync workers configuration via suppliers.yml.
74dff28
Drops `next_run_at` column from hosts.
76dd403
Rename supplier creation flow to host creation flow.
7393342
Ability to query for hosts by identifier and supplier.
cdbc667
Query for backround workers associated with a host instead of supplier.
85d8939
Create background workers associated with hosts.
e046e38
Revert suppliers rake task to simplicity.
e004485
Run host creation in a database transaction.
1e66a17
Include rake task to update workers definition for all hosts.
e04b467
Support querying for pending background workers.
12c3cae
Schedule workers rather than hosts.
df7a08b
Process background workers instead of sync operations.
f985e48
Background worker coordination by the queue processor.
a3a4294
Update AtLeisure event name according to recent changes.
5041bf7
Introduce Roomorama::Calendar to specify rates and availabilities.
45be50a
Add new UpdateCalendar Roomorama API operation.
9e39461
Include type of synchronisation worker when sync process starts.
5050890
Add stats and type to sync processes.
d7831ce
Migrate to stats field on property synchronisation.
0d1d969
Validate calendars when required.
d230af3
Operation runner for calendar updates.
bd1ff84
Introduce a CalendarSynchronisation helper class.
267b475
Permanently delete property counters from sync_processes.
9600f7b
Update change log.
cc681fb
Documentation fixes.
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Include flows for creating suppliers and associated workers.
- Loading branch information
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
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,121 @@ | ||
module Concierge::Flows | ||
|
||
# +Concierge::Flows::BackgroundWorkerCreation+ | ||
# | ||
# This class encapsulates the flow of creating a background worker for a given | ||
# supplier, validating the parameters and interpreting human-readable interval | ||
# times, such as "1d" for one day, and generating the resulting number of seconds | ||
# to be stored in the database. | ||
class BackgroundWorkerCreation | ||
include Hanami::Validations | ||
|
||
INTERVAL_FORMAT = /^\s*(?<amount>\d+)\s*(?<unit>[smhd])\s*$/ | ||
|
||
attribute :supplier_id, presence: true | ||
attribute :interval, presence: true, format: INTERVAL_FORMAT | ||
attribute :type, presence: true, inclusion: BackgroundWorker::TYPES | ||
attribute :status, presence: true, inclusion: BackgroundWorker::STATUSES | ||
|
||
def perform | ||
if valid? | ||
worker = find_existing || build_new | ||
|
||
# set the values passed to make sure that changes in the parameters update | ||
# existing records. | ||
worker.interval = interpret_interval(interval) | ||
worker.type = type | ||
worker.status = status | ||
|
||
worker = BackgroundWorkerRepository.persist(worker) | ||
Result.new(worker) | ||
else | ||
Result.error(:invalid_parameters) | ||
end | ||
end | ||
|
||
private | ||
|
||
def find_existing | ||
workers = BackgroundWorkerRepository.for_supplier(supplier).to_a | ||
workers.find { |worker| worker.type == type } | ||
end | ||
|
||
def build_new | ||
BackgroundWorker.new(supplier_id: supplier_id) | ||
end | ||
|
||
def interpret_interval(interval) | ||
match = INTERVAL_FORMAT.match(interval) | ||
absolute = match[:amount].to_i | ||
|
||
case match[:unit] | ||
when "s" | ||
absolute | ||
when "m" | ||
absolute * 60 | ||
when "h" | ||
absolute * 60 * 60 | ||
when "d" | ||
absolute * 60 * 60 * 24 | ||
end | ||
end | ||
|
||
def supplier | ||
@supplier ||= SupplierRepository.find(supplier_id) | ||
end | ||
|
||
def attributes | ||
to_h | ||
end | ||
end | ||
|
||
# +Concierge::Flows::SupplierCreation+ | ||
# | ||
# This class encapsulates the creation of supplier, a including associated | ||
# background workers. Suppleirs are composed of a +name+ only. A definition of | ||
# the workers is also expected by this class. | ||
class SupplierCreation | ||
include Hanami::Validations | ||
|
||
attribute :name, presence: true | ||
attribute :workers, presence: true | ||
|
||
# creates database records for the supplier and background workers. | ||
# Returns a +Result+ instance wrapping the resulting +Supplier+ instance, | ||
# or an error in case the parameters are not valid. | ||
def perform | ||
if valid? | ||
name = attributes[:name] | ||
supplier = SupplierRepository.named(name) || create_supplier(attributes[:name]) | ||
|
||
attributes[:workers].each do |type, data| | ||
result = BackgroundWorkerCreation.new( | ||
supplier_id: supplier.id, | ||
interval: data[:every], | ||
type: type.to_s, | ||
status: "idle" | ||
).perform | ||
|
||
return result unless result.success? | ||
end | ||
|
||
Result.new(supplier) | ||
else | ||
Result.error(:invalid_parameters) | ||
end | ||
end | ||
|
||
private | ||
|
||
def create_supplier(name) | ||
SupplierRepository.create( | ||
Supplier.new(name: attributes[:name]) | ||
) | ||
end | ||
|
||
def attributes | ||
to_h | ||
end | ||
end | ||
|
||
end |
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,140 @@ | ||
require "spec_helper" | ||
|
||
RSpec.describe Concierge::Flows::SupplierCreation do | ||
let(:parameters) { | ||
{ | ||
name: "Supplier X", | ||
workers: { | ||
metadata: { | ||
every: "1d" | ||
}, | ||
availabilities: { | ||
every: "2h" | ||
} | ||
} | ||
} | ||
} | ||
|
||
subject { described_class.new(parameters) } | ||
|
||
describe "#perform" do | ||
it "returns an unsuccessful result without a valid name" do | ||
[nil, ""].each do |invalid_name| | ||
parameters[:name] = invalid_name | ||
|
||
result = subject.perform | ||
expect(result).to be_a Result | ||
expect(result).not_to be_success | ||
expect(result.error.code).to eq :invalid_parameters | ||
end | ||
end | ||
|
||
it "returns an unsuccessful result without a workers definition" do | ||
parameters.delete(:workers) | ||
|
||
result = subject.perform | ||
expect(result).to be_a Result | ||
expect(result).not_to be_success | ||
expect(result.error.code).to eq :invalid_parameters | ||
end | ||
|
||
it "returns an unsuccessful result if parameters for the workers are missing" do | ||
parameters[:workers][:metadata].delete(:every) | ||
|
||
result = subject.perform | ||
expect(result).to be_a Result | ||
expect(result).not_to be_success | ||
expect(result.error.code).to eq :invalid_parameters | ||
end | ||
|
||
it "returns an unsuccessful result if the worker type is unknown" do | ||
parameters[:workers][:invalid] = parameters[:workers].delete(:metadata) | ||
|
||
result = subject.perform | ||
expect(result).to be_a Result | ||
expect(result).not_to be_success | ||
expect(result.error.code).to eq :invalid_parameters | ||
end | ||
|
||
it "returns an unsuccessful result if the interval specified is not recognised" do | ||
parameters[:workers][:metadata][:every] = "invalid" | ||
|
||
result = subject.perform | ||
expect(result).to be_a Result | ||
expect(result).not_to be_success | ||
expect(result.error.code).to eq :invalid_parameters | ||
end | ||
|
||
it "creates supplier and associated workers" do | ||
expect { | ||
expect { | ||
expect(subject.perform).to be_success | ||
}.to change { SupplierRepository.count }.by(1) | ||
}.to change { BackgroundWorkerRepository.count }.by(2) | ||
|
||
supplier = SupplierRepository.last | ||
workers = BackgroundWorkerRepository.for_supplier(supplier).to_a | ||
|
||
expect(supplier.name).to eq "Supplier X" | ||
expect(workers.size).to eq 2 | ||
|
||
worker = workers.first | ||
expect(worker.type).to eq "metadata" | ||
expect(worker.next_run_at).to be_nil | ||
expect(worker.interval).to eq 24 * 60 * 60 # one day | ||
expect(worker.status).to eq "idle" | ||
|
||
worker = workers.last | ||
expect(worker.type).to eq "availabilities" | ||
expect(worker.next_run_at).to be_nil | ||
expect(worker.interval).to eq 2 * 60 * 60 # two hours | ||
expect(worker.status).to eq "idle" | ||
end | ||
|
||
it "updates changed data on consecutive runs" do | ||
subject.perform | ||
|
||
supplier = SupplierRepository.named("Supplier X") | ||
workers = BackgroundWorkerRepository.for_supplier(supplier).to_a | ||
metadata_worker = workers.find { |w| w.type == "metadata" } | ||
|
||
expect(metadata_worker.interval).to eq 24 * 60 * 60 | ||
|
||
# updates metadata worker interval to every 2 days | ||
parameters[:workers][:metadata][:every] = "2d" | ||
|
||
expect { | ||
subject.perform | ||
}.not_to change { SupplierRepository.count } | ||
|
||
metadata_worker = BackgroundWorkerRepository.find(metadata_worker.id) | ||
expect(metadata_worker.interval).to eq 2 * 24 * 60 * 60 # 2 days | ||
end | ||
|
||
context "interval parsing" do | ||
it "understands seconds notation" do | ||
parameters[:workers][:availabilities][:every] = "10s" | ||
subject.perform | ||
|
||
supplier = SupplierRepository.named("Supplier X") | ||
worker = BackgroundWorkerRepository. | ||
for_supplier(supplier). | ||
find { |w| w.type == "availabilities" } | ||
|
||
expect(worker.interval).to eq 10 | ||
end | ||
|
||
it "understands minutes notation" do | ||
parameters[:workers][:availabilities][:every] = "10m" | ||
subject.perform | ||
|
||
supplier = SupplierRepository.named("Supplier X") | ||
worker = BackgroundWorkerRepository. | ||
for_supplier(supplier). | ||
find { |w| w.type == "availabilities" } | ||
|
||
expect(worker.interval).to eq 10 * 60 | ||
end | ||
end | ||
end | ||
end |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👀