Compare commits

...

10 Commits

25 changed files with 184 additions and 174 deletions
+2 -1
View File
@@ -11,7 +11,7 @@ end
gemspec
gem "rails", "~> 5.1"
gem "rails", "~> 8.0"
group :development, :test do
gem 'bootsnap', require: false
@@ -28,6 +28,7 @@ group :development, :test do
gem 'puma'
gem 'paperclip', '~> 5.3'
gem 'dunlop-ember', gitlab: 'benja-2/dunlop-ember', branch: 'master'
gem 'record_collection' #, github: 'bterkuile/record_collection', branch: 'master'
#gem 'carrierwave'
#gem 'fog-aws'
+1
View File
@@ -4,6 +4,7 @@ dunlop
Installation
-----------------------
test change for git
```
gem 'dunlop-core', github: 'mozolutions/dunlop-core', branch: :master
gem 'devise'
+1 -1
View File
@@ -73,7 +73,7 @@ module Dunlop::ExecutionBatchModel
def execute
execute!
end
deprecate execute: "Please use execute! in stead of execute. This will be removed in dunlop v0.2.0"
# deprecate execute: "Please use execute! in stead of execute. This will be removed in dunlop v0.2.0"
def cleanup!(older_than = 1.month.ago)
older_than = older_than.ago if older_than.is_a?(ActiveSupport::Duration)
+1 -1
View File
@@ -2,7 +2,7 @@ module Dunlop
class UserFilter < ApplicationRecord
belongs_to :user
if Rails.application.config.database_configuration[Rails.env]['adapter'] != 'postgesql' # postgesql uses jsonb
serialize :filters, JSONColumnCoder.new(Hash)
serialize :filters, coder: JSONColumnCoder.new(Hash)
end
before_save do
+3 -3
View File
@@ -3,9 +3,9 @@ module Dunlop
extend ActiveSupport::Concern
included do
serialize :role_names, JSONColumnCoder.new(Array)
serialize :role_names_admin, JSONColumnCoder.new(Array)
serialize :settings_storage, JSONColumnCoder.new(Hash)
serialize :role_names, coder: JSONColumnCoder.new(Array)
serialize :role_names_admin, coder: JSONColumnCoder.new(Array)
serialize :settings_storage, coder: JSONColumnCoder.new(Hash)
has_many :source_files, as: :creator
has_many :user_filters, class_name: 'Dunlop::UserFilter'
end
+1 -1
View File
@@ -6,7 +6,7 @@ module Dunlop::Loggable
end
def with_nested_logger_and_catch_failed(scope=nil, options={})
scope ||= caller[0][/`.*'/][1..-2]
scope ||= caller[0][/'.*'/][1..-2]
options[:exception_notifier] = true unless options.has_key?(:exception_notifier)
backtrace = nil
::Dunlop::NestedLogger.scope('empty sandbox', benchmark: false) do
+94 -92
View File
@@ -1,124 +1,126 @@
module Dunlop::OverdueHandling
@registry = {}
@final_states = %w[overdue completed rejected]
module Dunlop
module OverdueHandling
@registry = {}
@final_states = %w[overdue completed rejected]
def self.registry
@registry
end
def self.registry
@registry
end
def self.final_states
@final_states
end
def self.final_states
@final_states
end
def self.final_states=(states)
@final_states = states
end
def self.final_states=(states)
@final_states = states
end
def self.define(&block)
@registry = {} # empty the registry
definition_proxy = DefinitionProxy.new
definition_proxy.instance_eval(&block)
end
def self.define(&block)
@registry = {} # empty the registry
definition_proxy = DefinitionProxy.new
definition_proxy.instance_eval(&block)
end
def self.handle_overdue!
registry.each do |workflow_step_name, config|
base_scope = WorkflowInstance.all #where.not(state: final_states)
base_scope = base_scope.where(sti_type: config.scenarios.map{|s| "WorkflowInstance::#{s.to_s.classify}"}) if config.scenarios.any?
base_scope = base_scope.joins(workflow_step_name).where.not("#{workflow_step_name.to_s.pluralize}.state" => final_states) #.includes(workflow_step_name)
if config.from.is_nested?
base_scope = base_scope.joins(config.from.key) if config.from.key != workflow_step_name
end
base_scope = base_scope.where("#{config.date_target_class.table_name}.#{config.date_target_column} <= ?", Date.current - config.time)
def self.handle_overdue!
registry.each do |workflow_step_name, config|
base_scope = WorkflowInstance.all #where.not(state: final_states)
base_scope = base_scope.where(sti_type: config.scenarios.map{|s| "WorkflowInstance::#{s.to_s.classify}"}) if config.scenarios.any?
base_scope = base_scope.joins(workflow_step_name).where.not("#{workflow_step_name.to_s.pluralize}.state" => final_states) #.includes(workflow_step_name)
if config.from.is_nested?
base_scope = base_scope.joins(config.from.key) if config.from.key != workflow_step_name
end
base_scope = base_scope.where("#{config.date_target_class.table_name}.#{config.date_target_column} <= ?", Date.current - config.time)
base_scope.find_each do |workflow_instance|
next unless workflow_step = workflow_instance.public_send(workflow_step_name)
workflow_step.is_overdue!
base_scope.find_each do |workflow_instance|
next unless workflow_step = workflow_instance.public_send(workflow_step_name)
workflow_step.is_overdue!
end
end
end
end
class DefinitionProxy
def when_workflow_step(workflow_step, options = {}, &block)
raise "Workflow step #{workflow_step} does not exist" unless WorkflowInstance.possible_workflow_step_names.include?(workflow_step)
workflow_step_proxy = WorkflowStepProxy.new(workflow_step, options)
workflow_step_proxy.instance_eval(&block) if block
workflow_step_proxy.register_callbacks!
::Dunlop::OverdueHandling.registry[workflow_step] = workflow_step_proxy
class DefinitionProxy
def when_workflow_step(workflow_step, options = {}, &block)
raise "Workflow step #{workflow_step} does not exist" unless WorkflowInstance.possible_workflow_step_names.include?(workflow_step)
workflow_step_proxy = WorkflowStepProxy.new(workflow_step, options)
workflow_step_proxy.instance_eval(&block) if block
workflow_step_proxy.register_callbacks!
::Dunlop::OverdueHandling.registry[workflow_step] = workflow_step_proxy
end
def the_final_states_are(states)
::Dunlop::OverdueHandling.final_states = states
end
end
def the_final_states_are(states)
::Dunlop::OverdueHandling.final_states = states
end
end
class WorkflowStepProxy
attr_reader :workflow_step, :options, :overdue_target_class
def initialize(workflow_step, options)
@workflow_step, @options = workflow_step, options
@overdue_target_class = workflow_step.to_s.classify.constantize
end
class WorkflowStepProxy
attr_reader :workflow_step, :options, :overdue_target_class
def initialize(workflow_step, options)
@workflow_step, @options = workflow_step, options
@overdue_target_class = workflow_step.to_s.classify.constantize
end
def date_target_class
@date_target_class ||= from.is_nested? ? from.key.to_s.classify.constantize : WorkflowInstance
end
def date_target_class
@date_target_class ||= from.is_nested? ? from.key.to_s.classify.constantize : WorkflowInstance
end
def date_target_column
@date_target_column ||= from.field
end
def date_target_column
@date_target_column ||= from.field
end
def scenarios
options[:scenarios] || []
end
def scenarios
options[:scenarios] || []
end
def from
WorkflowStepBaseDate.new(workflow_step, options[:from])
end
def from
WorkflowStepBaseDate.new(workflow_step, options[:from])
end
def time
options[:is]
end
def time
options[:is]
end
#TODO: find a simple way to do this!!!
def register_callbacks!
date_target_classes = [date_target_class] #TODO: only implement on specific scenarios if given as argument
date_column = date_target_column
workflow_step_name = workflow_step # make local scope for after_save definition
overdue_time = time
date_target_classes.each do |target_class|
target_class.after_save do |record|
if saved_changes[date_column]
target_record = record.workflow_instance.public_send(workflow_step_name)
if date = record.public_send(date_column)
if date <= Date.current - overdue_time
target_record.is_overdue! if target_record.pending? or target_record.processing?
#TODO: find a simple way to do this!!!
def register_callbacks!
date_target_classes = [date_target_class] #TODO: only implement on specific scenarios if given as argument
date_column = date_target_column
workflow_step_name = workflow_step # make local scope for after_save definition
overdue_time = time
date_target_classes.each do |target_class|
target_class.after_save do |record|
if saved_changes[date_column]
target_record = record.workflow_instance.public_send(workflow_step_name)
if date = record.public_send(date_column)
if date <= Date.current - overdue_time
target_record.is_overdue! if target_record.pending? or target_record.processing?
else
target_record.process! if target_record.overdue?
end
else
target_record.process! if target_record.overdue?
end
else
target_record.process! if target_record.overdue?
end
end
end
end
end
end
class WorkflowStepBaseDate
attr_reader :workflow_step, :config
def initialize(workflow_step, config)
@workflow_step, @config = workflow_step, config
end
class WorkflowStepBaseDate
attr_reader :workflow_step, :config
def initialize(workflow_step, config)
@workflow_step, @config = workflow_step, config
end
def is_nested?
config.is_a?(Hash)
end
def is_nested?
config.is_a?(Hash)
end
def key
is_nested? ? config.keys.first : config
end
def key
is_nested? ? config.keys.first : config
end
def field
is_nested? ? config.values.first : config
def field
is_nested? ? config.values.first : config
end
end
end
end
+1
View File
@@ -3,6 +3,7 @@
ENGINE_ROOT = File.expand_path('../..', __FILE__)
ENGINE_PATH = File.expand_path('../../lib/dunlop/engine', __FILE__)
APP_PATH = File.expand_path("../spec/dummy/config/application", __dir__)
# Set up gems listed in the Gemfile.
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
+2 -1
View File
@@ -18,7 +18,7 @@ Gem::Specification.new do |s|
s.add_dependency "rails", ">= 4.2.6"
s.add_dependency 'slim-rails'
#s.add_dependency 'record_collection', '>= 0.10.4'
#s.add_dependency 'record_collection', '>= 0.10.4' # Only in Gemfile to make not requried
s.add_dependency 'request_store'
s.add_dependency 'ransack'
s.add_dependency 'responders'
@@ -31,6 +31,7 @@ Gem::Specification.new do |s|
s.add_dependency 'facets'
s.add_dependency 'memoist'
s.add_dependency 'cancancan'
s.add_dependency 'csv'
# assets
#s.add_dependency 'momentjs-rails'
+1
View File
@@ -0,0 +1 @@
require "dunlop"
+1 -1
View File
@@ -45,7 +45,7 @@ module Dunlop::CsvBuilder
return resource.to_csv if self.class == CsvBuilder and resource.present? and resource.respond_to?(:to_csv) and !resource.is_a?(Array)
# try iteration otherwise
str = CSV.generate csv_options do |csv|
str = CSV.generate **csv_options do |csv|
csv << headers if headers.present?
for_each_serialized_row do |serialized_row|
csv << serialized_row
+5 -11
View File
@@ -2,14 +2,14 @@
# Asset groups
if (Rails.groups.map(&:to_s) & %w[development assets test]).any?
require "sass-rails"
require "js-routes"
require "momentjs-rails"
#require "js-routes"
#require "momentjs-rails"
require 'coffee-rails'
require "pickadate-rails"
#require "pickadate-rails"
end
require "slim-rails"
require 'record_collection'
#require 'record_collection'
require 'request_store'
require 'ransack'
require 'devise'
@@ -36,13 +36,6 @@ module Dunlop
class Engine < ::Rails::Engine
isolate_namespace Dunlop
#TODO this generates noise and pollutes API based implementations (Like Panda)
initializer 'dunlop.action_controller' do |app|
ActiveSupport.on_load :action_controller do
helper ::Dunlop::ApplicationHelper if respond_to?(:helper)
end
end
initializer "dunlop.ability", group: 'dunlop' do |app|
if ability_class = "Ability".safe_constantize and ability_class.respond_to?(:add_dunlop_allowed_authorization_classes!)
ability_class.add_dunlop_allowed_authorization_classes!
@@ -59,6 +52,7 @@ module Dunlop
end
config.after_initialize do
# Auto generate target-file's ::Job classes having purpose triggering .generate on the class
::Dunlop.target_file_models.each do |target_file_base_model|
## Generte a job for all target files
@@ -36,8 +36,8 @@ class Dunlop::Install::BaseGenerator < Rails::Generators::Base
config.application_name = "#{Rails.application.class.name.deconstantize}"
config.git_version = Rails.env
config.git_version = File.read('REVISION').strip if File.exists?('REVISION')
config.git_version = File.read('OFFLINE_REVISION').strip if File.exists?('OFFLINE_REVISION')
config.git_version = File.read('REVISION').strip if File.exist?('REVISION')
config.git_version = File.read('OFFLINE_REVISION').strip if File.exist?('OFFLINE_REVISION')
RUBY
end
+3
View File
@@ -0,0 +1,3 @@
//= link_tree ../images
//= link_directory ../javascripts .js
//= link_directory ../stylesheets .css
@@ -1,8 +1,8 @@
class OwnerInitialization < ApplicationRecord
include WorkflowStepModel
serialize :window_from, DayTimeMinutes
serialize :window_to, DayTimeMinutes
serialize :window_from, coder: DayTimeMinutes
serialize :window_to, coder: DayTimeMinutes
activate_dunlop!
end
+2 -2
View File
@@ -22,8 +22,8 @@ module Dummy
config.application_name = 'Dunlop'
config.git_version = nil
config.git_version = File.read('REVISION').strip if File.exists?('REVISION')
config.git_version = File.read('OFFLINE_REVISION').strip if File.exists?('OFFLINE_REVISION')
config.git_version = File.read('REVISION').strip if File.exist?('REVISION')
config.git_version = File.read('OFFLINE_REVISION').strip if File.exist?('OFFLINE_REVISION')
config.file_storage_path = Rails.root.join('files') # source file storage
config.working_path = '/tmp' # source file processing directory
@@ -1,3 +1,5 @@
Dunlop::OverdueHandling.define do
when_workflow_step :contractor_initialization, is: 2.days, from: {owner_initialization: :plan_date}
Rails.application.config.after_initialize do
Dunlop::OverdueHandling.define do
when_workflow_step :contractor_initialization, is: 2.days, from: {owner_initialization: :plan_date}
end
end
@@ -1,3 +1,5 @@
Dunlop::Workflow.configure do |config|
config.max_number_of_records_in_display_badge = 72
Rails.application.config.after_initialize do
Dunlop::Workflow.configure do |config|
config.max_number_of_records_in_display_badge = 72
end
end
@@ -1,28 +1,30 @@
Dunlop::WorkflowStepsInBatchFinished.define do
the_final_states_are %w[rejected completed]
Rails.application.config.after_initialize do
Dunlop::WorkflowStepsInBatchFinished.define do
the_final_states_are %w[rejected completed]
on_finishing_of :owner_initialization do
description "Send notification mail to Owner" do |workflow_instance_batch|
OwnerMailer.owner_initialization_finished(workflow_instance_batch, self).deliver_later
on_finishing_of :owner_initialization do
description "Send notification mail to Owner" do |workflow_instance_batch|
OwnerMailer.owner_initialization_finished(workflow_instance_batch, self).deliver_later
end
end
#on_finishing_of :contractor_service_window1_execution do
# description <<-DESCRIPTION
# Check ${attributes.owner_service_window2_preparation.migration_file_for_bvt_ready}
# for ${models.owner_service_window2_preparation} workflow steps in the batch
# DESCRIPTION
# action do |workflow_instance_batch|
# workflow_steps_in_batch_scope(:owner_service_window2_preparation).update_all migration_file_for_bvt_ready: true
# end
# description "Send an email to Core Delivery informing them of batch completion" do |workflow_instance_batch|
# KpnCoreDeliveryMailer.contractor_service_window1_handled_for_batch(workflow_instance_batch.id).deliver_later
# end
# description "Send an email to Schuuring informing them of batch completion" do |workflow_instance_batch|
# SchuuringMailer.workflow_instance_batch_workflow_step_finished(self.class.name, workflow_instance_batch.id).deliver_later
# end
#end
end
#on_finishing_of :contractor_service_window1_execution do
# description <<-DESCRIPTION
# Check ${attributes.owner_service_window2_preparation.migration_file_for_bvt_ready}
# for ${models.owner_service_window2_preparation} workflow steps in the batch
# DESCRIPTION
# action do |workflow_instance_batch|
# workflow_steps_in_batch_scope(:owner_service_window2_preparation).update_all migration_file_for_bvt_ready: true
# end
# description "Send an email to Core Delivery informing them of batch completion" do |workflow_instance_batch|
# KpnCoreDeliveryMailer.contractor_service_window1_handled_for_batch(workflow_instance_batch.id).deliver_later
# end
# description "Send an email to Schuuring informing them of batch completion" do |workflow_instance_batch|
# SchuuringMailer.workflow_instance_batch_workflow_step_finished(self.class.name, workflow_instance_batch.id).deliver_later
# end
#end
end
+1 -1
View File
@@ -3,7 +3,7 @@ FactoryBot.define do
original_file { Tempfile.new("target_file_factory") }
trait :failed do
after(:build) { |record| record.log_entries.build(body: 'ERROR - some reason to fail') }
state 'failed'
state { 'failed' }
end
end
+5 -5
View File
@@ -6,14 +6,14 @@ FactoryBot.define do
factory :user do
email
password 'password'
role_names %w[read-class-WorkflowInstance]
password { 'password' }
role_names { %w[read-class-WorkflowInstance] }
end
factory :admin, class: User do
email 'admin@example.com'
password 'password'
admin true
email { 'admin@example.com' }
password { 'password' }
admin { true }
end
end
+8 -8
View File
@@ -1,29 +1,29 @@
FactoryBot.define do
factory :workflow_instance do
after(:build){|instance| instance.setup_workflow_steps }
state "unplanned" # default for Vula
state { "unplanned" } # default for Vula
trait :uncategorized do
state 'uncategorized'
state { 'uncategorized' }
end
trait :unplanned do
state 'unplanned'
state { 'unplanned' }
end
trait :planned do
state 'planned'
state { 'planned' }
end
trait :active do
state 'active'
state { 'active' }
after :build do |instance|
instance.workflow_steps.first.state = 'processing'
end
end
trait :overdue do
state 'overdue'
state { 'overdue' }
after :build do |instance|
instance.owner_initialization.plan_date = 1.year.ago
instance.contractor_initialization.state = 'overdue'
@@ -31,14 +31,14 @@ FactoryBot.define do
end
trait :all_completed do
state 'all_completed'
state { 'all_completed' }
after :build do |instance|
instance.workflow_steps.each{|workflow_step| workflow_step.state = 'completed' }
end
end
trait :any_rejected do
state 'any_rejected'
state { 'any_rejected' }
after :build do |instance|
instance.workflow_steps.first.state = 'rejected'
end
@@ -1,6 +1,6 @@
require 'rails_helper'
describe Dunlop::ExecutionBatch do
describe ::ExecutionBatch do
let(:time) { '1981-03-09T13:33:04Z'.to_time }
let(:model) { ExecutionBatch::MyBatch }
before { Timecop.freeze time }
+1 -1
View File
@@ -43,7 +43,7 @@ RSpec::Support::ObjectFormatter.default_instance.max_formatted_output_length = 1
RSpec.configure do |config|
# Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
config.fixture_path = Dunlop::Engine.root.join("spec/fixtures")
config.fixture_paths = [Dunlop::Engine.root.join("spec/fixtures")]
config.expect_with(:rspec) { |c| c.syntax = [:should, :expect] }
+12 -12
View File
@@ -20,10 +20,10 @@ RSpec.describe Dunlop::FileZip do
context "mime-type: application/zip" do
it "unzips the file to the provided filename" do
expect(File.exists?(destination_filename)).to be_falsey
expect(File.exist?(destination_filename)).to be_falsey
subject.unzip(zipped_filename, destination_filename)
expect(File.exists?(destination_filename)).to be_truthy
expect(File.exists?(zipped_filename)).to be_truthy
expect(File.exist?(destination_filename)).to be_truthy
expect(File.exist?(zipped_filename)).to be_truthy
expect(File.read(destination_filename)).to eq File.read(unzipped_filename)
end
@@ -34,10 +34,10 @@ RSpec.describe Dunlop::FileZip do
context "mime-type: application/x-gzip" do
it "unzips the file to the provided filename" do
expect(File.exists?(destination_filename)).to be_falsey
expect(File.exist?(destination_filename)).to be_falsey
subject.unzip(gzipped_filename, destination_filename)
expect(File.exists?(destination_filename)).to be_truthy
expect(File.exists?(gzipped_filename)).to be_truthy
expect(File.exist?(destination_filename)).to be_truthy
expect(File.exist?(gzipped_filename)).to be_truthy
expect(File.read(destination_filename)).to eq File.read(unzipped_filename)
end
@@ -46,10 +46,10 @@ RSpec.describe Dunlop::FileZip do
context "mime-type: text/plain" do
it "copies the file to the provided filename" do
expect(File.exists?(destination_filename)).to be_falsey
expect(File.exist?(destination_filename)).to be_falsey
subject.unzip(unzipped_filename, destination_filename)
expect(File.exists?(destination_filename)).to be_truthy
expect(File.exists?(unzipped_filename)).to be_truthy
expect(File.exist?(destination_filename)).to be_truthy
expect(File.exist?(unzipped_filename)).to be_truthy
expect(File.read(destination_filename)).to eq File.read(unzipped_filename)
end
@@ -69,10 +69,10 @@ RSpec.describe Dunlop::FileZip do
end
it "gzips the file to the provided filename" do
expect(File.exists?(gzipped_filename)).to be_falsey
expect(File.exist?(gzipped_filename)).to be_falsey
subject.gzip(unzipped_filename, gzipped_filename)
expect(File.exists?(gzipped_filename)).to be_truthy
expect(File.exists?(unzipped_filename)).to be_truthy
expect(File.exist?(gzipped_filename)).to be_truthy
expect(File.exist?(unzipped_filename)).to be_truthy
expect(File.size(unzipped_filename)).to eq 305
expect(File.size(gzipped_filename)).to eq 153