diff --git a/Gemfile b/Gemfile index 59b3134..36b2b76 100644 --- a/Gemfile +++ b/Gemfile @@ -4,6 +4,11 @@ source 'https://rubygems.org' # Bundler will treat runtime dependencies like base dependencies, and # development dependencies will be added by default to the :development group. # +git_source(:gitlab) do |repo_name| + repo_name = "#{repo_name}/#{repo_name}" unless repo_name.include?("/") + "git@gitlab.com:#{repo_name}.git" +end + gemspec gem "rails", "~> 5.1" @@ -22,6 +27,8 @@ group :development, :test do gem "select2-rails" gem 'puma' gem 'paperclip', '~> 5.3' + gem 'dunlop-ember', gitlab: 'benja-2/dunlop-ember', branch: 'master' + #gem 'carrierwave' #gem 'fog-aws' gem 'uuidtools' diff --git a/app/controllers/dunlop/user_filters_controller.rb b/app/controllers/dunlop/user_filters_controller.rb new file mode 100644 index 0000000..2a302c7 --- /dev/null +++ b/app/controllers/dunlop/user_filters_controller.rb @@ -0,0 +1,27 @@ +class Dunlop::UserFiltersController < Dunlop::ApplicationController + include Dunlop::Ember::ApiBaseController # Ugly to require a non dependency here, but for now only used in application explicitly including dunlop-ember + before_action :check_current_user + + private + + def serialize_options + {key_transform: :unaltered} + end + + def permitted_params + [:scope_key, :is_default, filters: {}] + end + + def check_current_user + head 404 unless current_user.present? + end + + def record_scope + current_user.user_filters + end + + def index_pagination? + false + end + +end diff --git a/app/models/dunlop/user_filter.rb b/app/models/dunlop/user_filter.rb new file mode 100644 index 0000000..70f8643 --- /dev/null +++ b/app/models/dunlop/user_filter.rb @@ -0,0 +1,14 @@ +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) + end + + before_save do + if changes['is_default'] == [false, true] + self.class.where(scope_key: scope_key).update_all(is_default: false) + end + end + end +end diff --git a/app/models/dunlop/user_model.rb b/app/models/dunlop/user_model.rb index accab5f..b222c24 100644 --- a/app/models/dunlop/user_model.rb +++ b/app/models/dunlop/user_model.rb @@ -1,33 +1,36 @@ -module Dunlop::UserModel - extend ActiveSupport::Concern +module Dunlop + module UserModel + extend ActiveSupport::Concern - included do - serialize :role_names, JSONColumnCoder.new(Array) - serialize :role_names_admin, JSONColumnCoder.new(Array) - serialize :settings_storage, JSONColumnCoder.new(Hash) - has_many :source_files, as: :creator - end - - def settings - @settings ||= Dunlop::UserSettings.new(self, settings_storage) - end - - def all_role_names - role_names + role_names_admin - end - - def presentation_name - email - end - - module ClassMethods - - def current - RequestStore.store[:current_user] + included do + serialize :role_names, JSONColumnCoder.new(Array) + serialize :role_names_admin, JSONColumnCoder.new(Array) + serialize :settings_storage, JSONColumnCoder.new(Hash) + has_many :source_files, as: :creator + has_many :user_filters, class_name: 'Dunlop::UserFilter' end - def current=(user) - RequestStore.store[:current_user] = user + def settings + @settings ||= Dunlop::UserSettings.new(self, settings_storage) + end + + def all_role_names + role_names + role_names_admin + end + + def presentation_name + email + end + + module ClassMethods + + def current + RequestStore.store[:current_user] + end + + def current=(user) + RequestStore.store[:current_user] = user + end end end end diff --git a/app/serializers/dunlop/user_filter_serializer.rb b/app/serializers/dunlop/user_filter_serializer.rb new file mode 100644 index 0000000..ea72e84 --- /dev/null +++ b/app/serializers/dunlop/user_filter_serializer.rb @@ -0,0 +1,9 @@ +module Dunlop + class UserFilterSerializer < ActiveModel::Serializer + # manual key transform to preserve filters lowercases + type 'dunlop/user-filter' + attribute :scope_key, key: 'scope-key' + attribute :filters + attribute :is_default, key: 'is-default' + end +end diff --git a/config/routes.rb b/config/routes.rb index edbae50..29797cd 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -31,6 +31,8 @@ Dunlop::Engine.routes.draw do get "/download_latest/:target_file_type", action: :download_latest, on: :collection end + resources :user_filters + resources :execution_batches resources :workflow_instances do diff --git a/db/migrate/20181114140017_create_dunlop_user_filters.rb b/db/migrate/20181114140017_create_dunlop_user_filters.rb new file mode 100644 index 0000000..7a4ccbb --- /dev/null +++ b/db/migrate/20181114140017_create_dunlop_user_filters.rb @@ -0,0 +1,16 @@ +class CreateDunlopUserFilters < ActiveRecord::Migration[5.2] + def change + create_table :dunlop_user_filters do |t| + t.belongs_to :user + t.string :scope_key + t.text :description + t.boolean :is_default, null: false, default: false + if Rails.application.config.database_configuration[Rails.env]['adapter'] == 'postgesql' + t.jsonb :filters, default: {} + else + t.text :filters + end + t.timestamps + end + end +end diff --git a/spec/controllers/dunlop/user_filters_controller_spec.rb b/spec/controllers/dunlop/user_filters_controller_spec.rb new file mode 100644 index 0000000..321f794 --- /dev/null +++ b/spec/controllers/dunlop/user_filters_controller_spec.rb @@ -0,0 +1,46 @@ +require 'rails_helper' + +RSpec.describe Dunlop::UserFiltersController do + routes { Dunlop::Engine.routes } + let(:model) { Dunlop::UserFilter } + describe 'logged in' do + let(:user) { create :user } + before { sign_in user} + describe '#index' do + it "returns listed serialized" do + model1 = create 'dunlop/user_filter', user: user, scope_key: 'panda/action-list', filters: {key_with_underscores: 17} + model2 = create 'dunlop/user_filter', user: user, scope_key: 'other/action-list', filters: {key_with_underscores: 19} + get :index, params: {q: {scope_key_matches: 'panda/%'}} + + response.body.should start_with '{"data":[' + result = JSON.parse(response.body)['data'] + result.should eq [ + {"id"=>model1.id.to_s, "type"=>"dunlop/user-filter", "attributes"=>{"scope-key"=>"panda/action-list", "filters"=>{"key_with_underscores"=>17}, "is-default"=>false}} + ] + end + end + + describe '#create' do + it 'creates a new filter attached to the current user' do + post :create, params: {'dunlop/user_filter' => {scope_key: 'panda/something', filters: {abc_def: 'g'}, is_default: true}} + record = model.last + record.user.should eq user + record.scope_key.should eq 'panda/something' + record.filters.should eq('abc_def' => 'g') + record.should be_is_default + end + end + + end + + describe 'not logged in' do + describe '#index' do + it "302" do + model = create 'dunlop/user_filter', scope_key: 'panda:action-list', filters: {key_with_underscores: 17} + get :index + response.body.should include 'redirected' + response.body.should include 'users/sign_in' + end + end + end +end diff --git a/spec/dummy/db/schema.rb b/spec/dummy/db/schema.rb index afc92c7..232f2b2 100644 --- a/spec/dummy/db/schema.rb +++ b/spec/dummy/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2018_06_07_212010) do +ActiveRecord::Schema.define(version: 2018_11_14_140017) do create_table "contractor_initializations", force: :cascade do |t| t.integer "workflow_instance_id" @@ -51,6 +51,17 @@ ActiveRecord::Schema.define(version: 2018_06_07_212010) do t.index ["loggable_id", "loggable_type"], name: "index_dunlop_log_entries_on_loggable_id_and_loggable_type" end + create_table "dunlop_user_filters", force: :cascade do |t| + t.integer "user_id" + t.string "scope_key" + t.text "description" + t.boolean "is_default", default: false, null: false + t.text "filters" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["user_id"], name: "index_dunlop_user_filters_on_user_id" + end + create_table "execution_batches", force: :cascade do |t| t.string "state" t.string "sti_type" diff --git a/spec/factories/user_filter.rb b/spec/factories/user_filter.rb new file mode 100644 index 0000000..8ae5f99 --- /dev/null +++ b/spec/factories/user_filter.rb @@ -0,0 +1,3 @@ +FactoryBot.define do + factory 'dunlop/user_filter' +end diff --git a/spec/models/dunlop/user_filter_spec.rb b/spec/models/dunlop/user_filter_spec.rb new file mode 100644 index 0000000..5165277 --- /dev/null +++ b/spec/models/dunlop/user_filter_spec.rb @@ -0,0 +1,22 @@ +require 'rails_helper' + +describe Dunlop::UserFilter do + let(:time) { '1981-03-09T13:33:04Z'.to_time } + let(:model) { User } + before { Timecop.freeze time } + after { Timecop.return } + subject { create :user } + + describe 'is_default' do + it "creates" do + filter1 = create 'dunlop/user_filter', scope_key: 'dunlop:test1', filters: {a: 3, b: 8}, is_default: true + filter2 = create 'dunlop/user_filter', scope_key: 'dunlop:test2', filters: {a: 3, b: 8}, is_default: true + filter3 = create 'dunlop/user_filter', scope_key: 'dunlop:test1', filters: {a: 3, b: 8}, is_default: true + + filter1.reload.is_default.should be false # there can be only one + filter2.reload.is_default.should be true # different scope key + filter3.reload.is_default.should be true # there can only be the last one + end + + end +end diff --git a/spec/models/dunlop/user_model_spec.rb b/spec/models/dunlop/user_model_spec.rb new file mode 100644 index 0000000..d7246a8 --- /dev/null +++ b/spec/models/dunlop/user_model_spec.rb @@ -0,0 +1,25 @@ +require 'rails_helper' + +describe Dunlop::UserModel do + let(:time) { '1981-03-09T13:33:04Z'.to_time } + let(:model) { User } + before { Timecop.freeze time } + after { Timecop.return } + subject { create :user } + describe 'basics' do + it 'exists' do + subject.should be_present + subject.id.should be_present + end + end + + describe 'has_many user_filters' do + it "creates" do + expect { subject.user_filters.create(scope_key: 'dunlop:test1', filters: {a: 3, b: 8}) }.to change { Dunlop::UserFilter.count }.by 1 + filters = Dunlop::UserFilter.last + filters.user.should eq subject + filters.filters.should eq('a' => 3, 'b' => 8) + end + + end +end