diff --git a/.rubocop.yml b/.rubocop.yml index 543b1c577..538599c28 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -350,6 +350,9 @@ Style/WhenThen: Metrics/AbcSize: Enabled: false +Metrics/BlockLength: + ExcludedMethods: ['describe', 'context'] + Metrics/ClassLength: Enabled: false diff --git a/Dockerfile b/Dockerfile index c991986ca..f32135e8e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,10 +2,12 @@ FROM ruby:2.4.5 MAINTAINER BioSistemika # additional dependecies +# libSSL-1.0 is required by wkhtmltopdf binary RUN curl -sL https://deb.nodesource.com/setup_8.x | bash - && \ apt-get update -qq && \ apt-get install -y \ libjemalloc1 \ + libssl1.0-dev \ nodejs \ postgresql-client \ default-jre-headless \ diff --git a/Dockerfile.production b/Dockerfile.production index 61efe3707..5344fbec3 100644 --- a/Dockerfile.production +++ b/Dockerfile.production @@ -2,10 +2,12 @@ FROM ruby:2.4.5 MAINTAINER BioSistemika # additional dependecies +# libSSL-1.0 is required by wkhtmltopdf binary RUN curl -sL https://deb.nodesource.com/setup_8.x | bash - && \ apt-get update -qq && \ apt-get install -y \ libjemalloc1 \ + libssl1.0-dev \ nodejs \ groff-base \ awscli \ diff --git a/app/assets/javascripts/application.js.erb b/app/assets/javascripts/application.js.erb index 17d89518d..b04018106 100644 --- a/app/assets/javascripts/application.js.erb +++ b/app/assets/javascripts/application.js.erb @@ -35,6 +35,7 @@ //= require underscore //= require i18n.js //= require i18n/translations +//= require users/settings/teams/invite_users_modal //= require turbolinks diff --git a/app/assets/javascripts/users/settings/teams/invite_users_modal.js b/app/assets/javascripts/users/settings/teams/invite_users_modal.js index 55ebbca28..74e4c654e 100644 --- a/app/assets/javascripts/users/settings/teams/invite_users_modal.js +++ b/app/assets/javascripts/users/settings/teams/invite_users_modal.js @@ -1,6 +1,16 @@ (function() { 'use strict'; + function reloadRecaptchaIfExists() { + if (typeof grecaptcha !== 'undefined') { + grecaptcha.reset(); + } + } + + window.recaptchaCallback = function recaptchaCallback(response) { + $('#recaptcha-invite-modal').val(response); + }; + function initializeModal(modal) { var modalDialog = modal.find('.modal-dialog'); var type = modal.attr('data-type'); @@ -14,6 +24,9 @@ var teamSelectorDropdown = modal.find('[data-role=team-selector-dropdown]'); var teamSelectorDropdown2 = $(); var tagsInput = modal.find('[data-role=tags-input]'); + var recaptchaInput = modal.find('#recaptcha-invite-modal'); + var recaptchaErrorMsgDiv = modal.find('#recaptcha-error-msg'); + var recaptchaErrorText = modal.find('#recaptcha-error-msg>span'); // Set max tags tagsInput.tagsinput({ @@ -85,7 +98,8 @@ // Click action modal.find('[data-action=invite]').off('click').on('click', function() { var data = { - emails: tagsInput.val() + emails: tagsInput.val(), + 'g-recaptcha-response': recaptchaInput.val() }; animateSpinner(modalDialog); @@ -130,16 +144,23 @@ // Add 'data-invited="true"' status to modal element modal.attr('data-invited', 'true'); }, - error: function() { + error: function(jqXHR) { + // ReCaptcha fails + if (jqXHR.status === 422) { + recaptchaErrorMsgDiv.removeClass('hidden'); + recaptchaErrorText.text(jqXHR.responseJSON.recaptcha_error); + } else { + modal.modal('hide'); + alert('Error inviting users.'); + } + reloadRecaptchaIfExists(); animateSpinner(modalDialog, false); - modal.modal('hide'); - alert('Error inviting users.'); } }); }); }).on('shown.bs.modal', function() { tagsInput.tagsinput('focus'); - + recaptchaErrorMsgDiv.addClass('hidden'); // Remove 'data-invited="true"' status modal.removeAttr('data-invited'); }).on('hide.bs.modal', function() { @@ -150,6 +171,7 @@ inviteWithRoleBtn.attr('disabled', 'disabled'); teamSelectorDropdown2.addClass('disabled'); animateSpinner(modalDialog, false); + recaptchaErrorMsgDiv.addClass('hidden'); // Unbind event listeners teamSelectorCheckbox.off('change'); @@ -160,6 +182,8 @@ stepResultsDiv.html(''); stepResults.hide(); stepForm.show(); + reloadRecaptchaIfExists(); + recaptchaInput.val(''); }); } diff --git a/app/assets/stylesheets/themes/scinote.scss b/app/assets/stylesheets/themes/scinote.scss index 3098f6d4c..a6075b605 100644 --- a/app/assets/stylesheets/themes/scinote.scss +++ b/app/assets/stylesheets/themes/scinote.scss @@ -1405,6 +1405,10 @@ table.dataTable { margin: 0; } } + + .g-recaptcha { + margin-top: 20px; + } } // Image preview modal diff --git a/app/controllers/experiments_controller.rb b/app/controllers/experiments_controller.rb index 7c5a51381..1259c893f 100644 --- a/app/controllers/experiments_controller.rb +++ b/app/controllers/experiments_controller.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class ExperimentsController < ApplicationController include SampleActions include TeamsHelper @@ -237,49 +239,23 @@ class ExperimentsController < ApplicationController # POST: move_experiment(id) def move - project = Project.find_by_id(params[:experiment].try(:[], :project_id)) - old_project = @experiment.project - - # Try to move the experiment - success = true - if @experiment.moveable_projects(current_user).include?(project) - success = @experiment.move_to_project(project) - else - success = false - end - - if success - Activity.create( - type_of: :move_experiment, - project: project, - experiment: @experiment, - user: current_user, - message: I18n.t( - 'activities.move_experiment', - user: current_user.full_name, - experiment: @experiment.name, - project_new: project.name, - project_original: old_project.name - ) - ) + service = Experiments::MoveToProjectService + .call(experiment_id: @experiment.id, + project_id: move_experiment_param, + user_id: current_user.id) + if service.succeed? flash[:success] = t('experiments.move.success_flash', experiment: @experiment.name) - respond_to do |format| - format.json do - render json: { path: canvas_experiment_url(@experiment) }, status: :ok - end - end + path = canvas_experiment_url(@experiment) + status = :ok else - respond_to do |format| - format.json do - render json: { message: t('experiments.move.error_flash', - experiment: - escape_input(@experiment.name)) }, - status: :unprocessable_entity - end - end + message = t('experiments.move.error_flash', + experiment: escape_input(@experiment.name)) + status = :unprocessable_entity end + + render json: { message: message, path: path }, status: status end def module_archive @@ -348,6 +324,10 @@ class ExperimentsController < ApplicationController params.require(:experiment).permit(:name, :description, :archived) end + def move_experiment_param + params.require(:experiment).require(:project_id) + end + def load_projects_tree # Switch to correct team current_team_switch(@experiment.project.team) unless @experiment.project.nil? diff --git a/app/controllers/teams_controller.rb b/app/controllers/teams_controller.rb index 74709eb8a..50db7a324 100644 --- a/app/controllers/teams_controller.rb +++ b/app/controllers/teams_controller.rb @@ -229,8 +229,7 @@ class TeamsController < ApplicationController def export_projects unless export_proj_requests_exceeded? - current_user.export_vars['num_of_export_all_last_24_hours'] += 1 - current_user.save + current_user.increase_daily_exports_counter! generate_export_projects_zip diff --git a/app/controllers/users/invitations_controller.rb b/app/controllers/users/invitations_controller.rb index 3158a600b..9d8c4b292 100644 --- a/app/controllers/users/invitations_controller.rb +++ b/app/controllers/users/invitations_controller.rb @@ -5,6 +5,8 @@ module Users prepend_before_action :check_captcha, only: [:update] + prepend_before_action :check_captcha_for_invite, only: [:invite_users] + before_action :check_invite_users_permission, only: :invite_users before_action :update_sanitized_params, only: :update @@ -188,6 +190,15 @@ module Users end end + def check_captcha_for_invite + if Rails.configuration.x.enable_recaptcha + unless verify_recaptcha + render json: { recaptcha_error: t('invite_users.errors.recaptcha') }, + status: :unprocessable_entity + end + end + end + def check_invite_users_permission @user = current_user @emails = params[:emails].map(&:downcase) diff --git a/app/models/experiment.rb b/app/models/experiment.rb index 851372781..ee99a7bb5 100644 --- a/app/models/experiment.rb +++ b/app/models/experiment.rb @@ -255,24 +255,6 @@ class Experiment < ApplicationRecord clone end - def move_to_project(project) - self.project = project - - my_modules.each do |m| - new_tags = [] - m.tags.each do |t| - new_tags << t.deep_clone_to_project(project) - end - m.my_module_tags.destroy_all - - project.tags << new_tags - m.tags << new_tags - end - result = save - touch(:workflowimg_updated_at) if result - result - end - # Get projects where user is either owner or user in the same team # as this experiment def projects_with_role_above_user(current_user) diff --git a/app/models/tag.rb b/app/models/tag.rb index a1b1089ee..4818622de 100644 --- a/app/models/tag.rb +++ b/app/models/tag.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class Tag < ApplicationRecord include SearchableModel @@ -48,7 +50,10 @@ class Tag < ApplicationRecord end end - def deep_clone_to_project(project) + def clone_to_project_or_return_existing(project) + tag = Tag.find_by(project: project, name: name, color: color) + return tag if tag + Tag.create( name: name, color: color, diff --git a/app/models/user.rb b/app/models/user.rb index 614a273e3..31291398b 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -56,7 +56,8 @@ class User < ApplicationRecord default_variables( export_vars: { - num_of_export_all_last_24_hours: 0 + num_of_export_all_last_24_hours: 0, + last_export_timestamp: Date.today.to_time.to_i } ) @@ -505,6 +506,16 @@ class User < ApplicationRecord end end + def increase_daily_exports_counter! + if Time.at(export_vars['last_export_timestamp'] || 0).to_date == Date.today + export_vars['num_of_export_all_last_24_hours'] += 1 + else + export_vars['last_export_timestamp'] = Date.today.to_time.to_i + export_vars['num_of_export_all_last_24_hours'] = 1 + end + save + end + protected def confirmation_required? diff --git a/app/services/experiments/move_to_project_service.rb b/app/services/experiments/move_to_project_service.rb new file mode 100644 index 000000000..088bbc7b0 --- /dev/null +++ b/app/services/experiments/move_to_project_service.rb @@ -0,0 +1,89 @@ +# frozen_string_literal: true + +module Experiments + class MoveToProjectService + extend Service + + attr_reader :errors + + def initialize(experiment_id:, project_id:, user_id:) + @exp = Experiment.find experiment_id + @project = Project.find project_id + @user = User.find user_id + @original_project = @exp&.project + @errors = {} + end + + def call + return self unless valid? + + ActiveRecord::Base.transaction do + @exp.project = @project + + @exp.my_modules.each do |m| + new_tags = m.tags.map do |t| + t.clone_to_project_or_return_existing(@project) + end + m.my_module_tags.delete_all + m.tags = new_tags + end + + raise ActiveRecord::Rollback unless @exp.save + end + + @errors.merge!(@exp.errors.to_hash) unless @exp.valid? + + track_activity if succeed? + self + end + + def succeed? + @errors.none? + end + + private + + def valid? + unless @exp && @project && @user + @errors[:invalid_arguments] = + { 'experiment': @exp, + 'project': @project, + 'user': @user } + .map do |key, value| + "Can't find #{key.capitalize}" if value.nil? + end.compact + return false + end + + e = Experiment.find_by(name: @exp.name, project: @project) + + if e + @errors[:project_with_exp] = + ['Project already contains experiment with this name'] + false + elsif !@exp.moveable_projects(@user).include?(@project) + @errors[:target_project_not_valid] = + ['Experiment cannot be moved to this project'] + false + else + true + end + end + + def track_activity + Activity.create( + type_of: :move_experiment, + project: @project, + experiment: @exp, + user: @user, + message: I18n.t( + 'activities.move_experiment', + user: @user, + experiment: @exp.name, + project_new: @project.name, + project_original: @original_project.name + ) + ) + end + end +end diff --git a/app/views/shared/_invite_users_modal.html.erb b/app/views/shared/_invite_users_modal.html.erb index 4ef8ed571..6b612c91a 100644 --- a/app/views/shared/_invite_users_modal.html.erb +++ b/app/views/shared/_invite_users_modal.html.erb @@ -99,6 +99,16 @@ invite_to_team = type.in?(%w(invite_to_team invite_to_team_with_role)) <% end %> <% end %> + <% if ENV['ENABLE_RECAPTCHA'] %> +
>
+ + + + <% end %>
diff --git a/app/views/users/settings/teams/show.html.erb b/app/views/users/settings/teams/show.html.erb index 0271b5866..cb9c7d660 100644 --- a/app/views/users/settings/teams/show.html.erb +++ b/app/views/users/settings/teams/show.html.erb @@ -137,5 +137,4 @@ <%= render partial: 'users/settings/user_teams/destroy_user_team_modal.html.erb' %> <%= stylesheet_link_tag 'datatables' %> <%= javascript_include_tag 'users/settings/teams/show' %> -<%= javascript_include_tag 'users/settings/teams/invite_users_modal' %> diff --git a/config/locales/en.yml b/config/locales/en.yml index 191d786ea..6c9ceeb3b 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -1831,6 +1831,8 @@ en: invite_guest: "As Guests" invite_user: "As Normal Users" invite_admin: "As Administrators" + errors: + recaptcha: "reCAPTCHA verification failed, please try again" results: heading: "Invitation results:" user_exists: "User is already a member of SciNote." diff --git a/db/migrate/20190117155006_change_indices_from_int_to_bigint.rb b/db/migrate/20190117155006_change_indices_from_int_to_bigint.rb new file mode 100644 index 000000000..b9a67790e --- /dev/null +++ b/db/migrate/20190117155006_change_indices_from_int_to_bigint.rb @@ -0,0 +1,195 @@ +# frozen_string_literal: true + +class ChangeIndicesFromIntToBigint < ActiveRecord::Migration[5.1] + def up + drop_view :datatables_teams + + change_column :activities, :id, :bigint + change_column :assets, :id, :bigint + change_column :asset_text_data, :id, :bigint + change_column :checklist_items, :id, :bigint + change_column :checklists, :id, :bigint + change_column :comments, :id, :bigint + change_column :connections, :id, :bigint + change_column :custom_fields, :id, :bigint + change_column :delayed_jobs, :id, :bigint + change_column :experiments, :id, :bigint + change_column :my_module_groups, :id, :bigint + change_column :my_module_repository_rows, :id, :bigint + change_column :my_modules, :id, :bigint + change_column :my_module_tags, :id, :bigint + change_column :notifications, :id, :bigint + change_column :projects, :id, :bigint + change_column :protocol_keywords, :id, :bigint + change_column :protocol_protocol_keywords, :id, :bigint + change_column :protocols, :id, :bigint + change_column :report_elements, :id, :bigint + change_column :reports, :id, :bigint + change_column :repositories, :id, :bigint + change_column :repository_table_states, :id, :bigint + change_column :result_assets, :id, :bigint + change_column :results, :id, :bigint + change_column :result_tables, :id, :bigint + change_column :result_texts, :id, :bigint + change_column :sample_custom_fields, :id, :bigint + change_column :sample_groups, :id, :bigint + change_column :sample_my_modules, :id, :bigint + change_column :samples, :id, :bigint + change_column :samples_tables, :id, :bigint + change_column :sample_types, :id, :bigint + change_column :settings, :id, :bigint + change_column :step_assets, :id, :bigint + change_column :steps, :id, :bigint + change_column :step_tables, :id, :bigint + change_column :tables, :id, :bigint + change_column :tags, :id, :bigint + change_column :teams, :id, :bigint + change_column :temp_files, :id, :bigint + change_column :tiny_mce_assets, :id, :bigint + change_column :tokens, :id, :bigint + change_column :user_identities, :id, :bigint + change_column :user_my_modules, :id, :bigint + change_column :user_notifications, :id, :bigint + change_column :user_projects, :id, :bigint + change_column :users, :id, :bigint + change_column :user_teams, :id, :bigint + change_column :wopi_actions, :id, :bigint + change_column :wopi_apps, :id, :bigint + change_column :wopi_discoveries, :id, :bigint + change_column :zip_exports, :id, :bigint + change_column :activities, :user_id, :bigint + change_column :activities, :my_module_id, :bigint + change_column :activities, :project_id, :bigint + change_column :activities, :experiment_id, :bigint + change_column :assets, :created_by_id, :bigint + change_column :assets, :last_modified_by_id, :bigint + change_column :asset_text_data, :asset_id, :bigint + change_column :checklist_items, :checklist_id, :bigint + change_column :checklist_items, :last_modified_by_id, :bigint + change_column :checklist_items, :created_by_id, :bigint + change_column :checklists, :step_id, :bigint + change_column :checklists, :created_by_id, :bigint + change_column :checklists, :last_modified_by_id, :bigint + change_column :comments, :last_modified_by_id, :bigint + change_column :comments, :user_id, :bigint + change_column :connections, :output_id, :bigint + change_column :connections, :input_id, :bigint + change_column :custom_fields, :team_id, :bigint + change_column :custom_fields, :user_id, :bigint + change_column :custom_fields, :last_modified_by_id, :bigint + change_column :experiments, :created_by_id, :bigint + change_column :experiments, :restored_by_id, :bigint + change_column :experiments, :archived_by_id, :bigint + change_column :experiments, :last_modified_by_id, :bigint + change_column :my_module_groups, :experiment_id, :bigint + change_column :my_module_groups, :created_by_id, :bigint + change_column :my_module_repository_rows, :assigned_by_id, :bigint + change_column :my_modules, :last_modified_by_id, :bigint + change_column :my_modules, :created_by_id, :bigint + change_column :my_modules, :experiment_id, :bigint + change_column :my_modules, :my_module_group_id, :bigint + change_column :my_modules, :archived_by_id, :bigint + change_column :my_modules, :restored_by_id, :bigint + change_column :my_module_tags, :created_by_id, :bigint + change_column :notifications, :generator_user_id, :bigint + change_column :oauth_access_grants, :resource_owner_id, :bigint + change_column :oauth_access_tokens, :resource_owner_id, :bigint + change_column :projects, :team_id, :bigint + change_column :projects, :archived_by_id, :bigint + change_column :projects, :restored_by_id, :bigint + change_column :projects, :created_by_id, :bigint + change_column :projects, :last_modified_by_id, :bigint + change_column :protocol_keywords, :team_id, :bigint + change_column :protocol_protocol_keywords, :protocol_keyword_id, :bigint + change_column :protocol_protocol_keywords, :protocol_id, :bigint + change_column :protocols, :team_id, :bigint + change_column :protocols, :added_by_id, :bigint + change_column :protocols, :parent_id, :bigint + change_column :protocols, :restored_by_id, :bigint + change_column :protocols, :archived_by_id, :bigint + change_column :protocols, :my_module_id, :bigint + change_column :report_elements, :report_id, :bigint + change_column :report_elements, :experiment_id, :bigint + change_column :report_elements, :table_id, :bigint + change_column :report_elements, :asset_id, :bigint + change_column :report_elements, :checklist_id, :bigint + change_column :report_elements, :result_id, :bigint + change_column :report_elements, :step_id, :bigint + change_column :report_elements, :my_module_id, :bigint + change_column :report_elements, :project_id, :bigint + change_column :reports, :user_id, :bigint + change_column :reports, :last_modified_by_id, :bigint + change_column :reports, :project_id, :bigint + change_column :repositories, :created_by_id, :bigint + change_column :repository_columns, :created_by_id, :bigint + change_column :repository_date_values, :last_modified_by_id, :bigint + change_column :repository_date_values, :created_by_id, :bigint + change_column :repository_rows, :last_modified_by_id, :bigint + change_column :repository_rows, :created_by_id, :bigint + change_column :repository_text_values, :created_by_id, :bigint + change_column :repository_text_values, :last_modified_by_id, :bigint + change_column :result_assets, :result_id, :bigint + change_column :result_assets, :asset_id, :bigint + change_column :results, :archived_by_id, :bigint + change_column :results, :user_id, :bigint + change_column :results, :last_modified_by_id, :bigint + change_column :results, :restored_by_id, :bigint + change_column :results, :my_module_id, :bigint + change_column :result_tables, :result_id, :bigint + change_column :result_tables, :table_id, :bigint + change_column :result_texts, :result_id, :bigint + change_column :sample_custom_fields, :custom_field_id, :bigint + change_column :sample_custom_fields, :sample_id, :bigint + change_column :sample_groups, :team_id, :bigint + change_column :sample_groups, :last_modified_by_id, :bigint + change_column :sample_groups, :created_by_id, :bigint + change_column :sample_my_modules, :my_module_id, :bigint + change_column :sample_my_modules, :sample_id, :bigint + change_column :sample_my_modules, :assigned_by_id, :bigint + change_column :samples, :sample_group_id, :bigint + change_column :samples, :last_modified_by_id, :bigint + change_column :samples, :sample_type_id, :bigint + change_column :samples, :team_id, :bigint + change_column :samples, :user_id, :bigint + change_column :sample_types, :created_by_id, :bigint + change_column :sample_types, :team_id, :bigint + change_column :sample_types, :last_modified_by_id, :bigint + change_column :step_assets, :asset_id, :bigint + change_column :step_assets, :step_id, :bigint + change_column :steps, :last_modified_by_id, :bigint + change_column :steps, :user_id, :bigint + change_column :steps, :protocol_id, :bigint + change_column :step_tables, :step_id, :bigint + change_column :step_tables, :table_id, :bigint + change_column :tables, :created_by_id, :bigint + change_column :tables, :last_modified_by_id, :bigint + change_column :tags, :last_modified_by_id, :bigint + change_column :tags, :created_by_id, :bigint + change_column :tags, :project_id, :bigint + change_column :teams, :last_modified_by_id, :bigint + change_column :teams, :created_by_id, :bigint + change_column :tokens, :user_id, :bigint + change_column :user_my_modules, :my_module_id, :bigint + change_column :user_my_modules, :assigned_by_id, :bigint + change_column :user_my_modules, :user_id, :bigint + change_column :user_notifications, :user_id, :bigint + change_column :user_notifications, :notification_id, :bigint + change_column :user_projects, :assigned_by_id, :bigint + change_column :user_projects, :user_id, :bigint + change_column :user_projects, :project_id, :bigint + change_column :users, :current_team_id, :bigint + change_column :user_teams, :user_id, :bigint + change_column :user_teams, :team_id, :bigint + change_column :user_teams, :assigned_by_id, :bigint + change_column :wopi_actions, :wopi_app_id, :bigint + change_column :wopi_apps, :wopi_discovery_id, :bigint + change_column :zip_exports, :user_id, :bigint + + create_view :datatables_teams + end + + def down + # Bad, bad things can happen if we reverse this migration, so we'll + # simply skip it + end +end diff --git a/db/schema.rb b/db/schema.rb index ab055cf53..d0e6f5f88 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,22 +10,22 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20181212162649) do +ActiveRecord::Schema.define(version: 20190117155006) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" enable_extension "pg_trgm" enable_extension "btree_gist" - create_table "activities", id: :serial, force: :cascade do |t| - t.integer "my_module_id" - t.integer "user_id" + create_table "activities", force: :cascade do |t| + t.bigint "my_module_id" + t.bigint "user_id" t.integer "type_of", null: false t.string "message", null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false - t.integer "project_id", null: false - t.integer "experiment_id" + t.bigint "project_id", null: false + t.bigint "experiment_id" t.index ["created_at"], name: "index_activities_on_created_at" t.index ["experiment_id"], name: "index_activities_on_experiment_id" t.index ["my_module_id"], name: "index_activities_on_my_module_id" @@ -34,9 +34,9 @@ ActiveRecord::Schema.define(version: 20181212162649) do t.index ["user_id"], name: "index_activities_on_user_id" end - create_table "asset_text_data", id: :serial, force: :cascade do |t| + create_table "asset_text_data", force: :cascade do |t| t.text "data", null: false - t.integer "asset_id", null: false + t.bigint "asset_id", null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false t.tsvector "data_vector" @@ -44,15 +44,15 @@ ActiveRecord::Schema.define(version: 20181212162649) do t.index ["data_vector"], name: "index_asset_text_data_on_data_vector", using: :gin end - create_table "assets", id: :serial, force: :cascade do |t| + create_table "assets", force: :cascade do |t| t.datetime "created_at", null: false t.datetime "updated_at", null: false t.string "file_file_name" t.string "file_content_type" t.integer "file_file_size" t.datetime "file_updated_at" - t.integer "created_by_id" - t.integer "last_modified_by_id" + t.bigint "created_by_id" + t.bigint "last_modified_by_id" t.integer "estimated_size", default: 0, null: false t.boolean "file_present", default: false, null: false t.string "lock", limit: 1024 @@ -67,14 +67,14 @@ ActiveRecord::Schema.define(version: 20181212162649) do t.index ["team_id"], name: "index_assets_on_team_id" end - create_table "checklist_items", id: :serial, force: :cascade do |t| + create_table "checklist_items", force: :cascade do |t| t.string "text", null: false t.boolean "checked", default: false, null: false - t.integer "checklist_id", null: false + t.bigint "checklist_id", null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false - t.integer "created_by_id" - t.integer "last_modified_by_id" + t.bigint "created_by_id" + t.bigint "last_modified_by_id" t.integer "position" t.index "trim_html_tags((text)::text) gin_trgm_ops", name: "index_checklist_items_on_text", using: :gin t.index ["checklist_id"], name: "index_checklist_items_on_checklist_id" @@ -82,25 +82,25 @@ ActiveRecord::Schema.define(version: 20181212162649) do t.index ["last_modified_by_id"], name: "index_checklist_items_on_last_modified_by_id" end - create_table "checklists", id: :serial, force: :cascade do |t| + create_table "checklists", force: :cascade do |t| t.string "name", null: false - t.integer "step_id", null: false + t.bigint "step_id", null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false - t.integer "created_by_id" - t.integer "last_modified_by_id" + t.bigint "created_by_id" + t.bigint "last_modified_by_id" t.index "trim_html_tags((name)::text) gin_trgm_ops", name: "index_checklists_on_name", using: :gin t.index ["created_by_id"], name: "index_checklists_on_created_by_id" t.index ["last_modified_by_id"], name: "index_checklists_on_last_modified_by_id" t.index ["step_id"], name: "index_checklists_on_step_id" end - create_table "comments", id: :serial, force: :cascade do |t| + create_table "comments", force: :cascade do |t| t.string "message", null: false - t.integer "user_id", null: false + t.bigint "user_id", null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false - t.integer "last_modified_by_id" + t.bigint "last_modified_by_id" t.string "type" t.integer "associated_id" t.index "trim_html_tags((message)::text) gin_trgm_ops", name: "index_comments_on_message", using: :gin @@ -111,26 +111,26 @@ ActiveRecord::Schema.define(version: 20181212162649) do t.index ["user_id"], name: "index_comments_on_user_id" end - create_table "connections", id: :serial, force: :cascade do |t| - t.integer "input_id", null: false - t.integer "output_id", null: false + create_table "connections", force: :cascade do |t| + t.bigint "input_id", null: false + t.bigint "output_id", null: false t.index ["input_id"], name: "index_connections_on_input_id" t.index ["output_id"], name: "index_connections_on_output_id" end - create_table "custom_fields", id: :serial, force: :cascade do |t| + create_table "custom_fields", force: :cascade do |t| t.string "name", null: false - t.integer "user_id", null: false - t.integer "team_id", null: false + t.bigint "user_id", null: false + t.bigint "team_id", null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false - t.integer "last_modified_by_id" + t.bigint "last_modified_by_id" t.index ["last_modified_by_id"], name: "index_custom_fields_on_last_modified_by_id" t.index ["team_id"], name: "index_custom_fields_on_team_id" t.index ["user_id"], name: "index_custom_fields_on_user_id" end - create_table "delayed_jobs", id: :serial, force: :cascade do |t| + create_table "delayed_jobs", force: :cascade do |t| t.integer "priority", default: 0, null: false t.integer "attempts", default: 0, null: false t.text "handler", null: false @@ -146,16 +146,16 @@ ActiveRecord::Schema.define(version: 20181212162649) do t.index ["queue"], name: "delayed_jobs_queue" end - create_table "experiments", id: :serial, force: :cascade do |t| + create_table "experiments", force: :cascade do |t| t.string "name", null: false t.text "description" t.integer "project_id", null: false - t.integer "created_by_id", null: false - t.integer "last_modified_by_id", null: false + t.bigint "created_by_id", null: false + t.bigint "last_modified_by_id", null: false t.boolean "archived", default: false, null: false - t.integer "archived_by_id" + t.bigint "archived_by_id" t.datetime "archived_on" - t.integer "restored_by_id" + t.bigint "restored_by_id" t.datetime "restored_on" t.datetime "created_at", null: false t.datetime "updated_at", null: false @@ -171,53 +171,53 @@ ActiveRecord::Schema.define(version: 20181212162649) do t.index ["restored_by_id"], name: "index_experiments_on_restored_by_id" end - create_table "my_module_groups", id: :serial, force: :cascade do |t| + create_table "my_module_groups", force: :cascade do |t| t.datetime "created_at", null: false t.datetime "updated_at", null: false - t.integer "created_by_id" - t.integer "experiment_id", default: 0, null: false + t.bigint "created_by_id" + t.bigint "experiment_id", default: 0, null: false t.index ["created_by_id"], name: "index_my_module_groups_on_created_by_id" t.index ["experiment_id"], name: "index_my_module_groups_on_experiment_id" end - create_table "my_module_repository_rows", id: :serial, force: :cascade do |t| + create_table "my_module_repository_rows", force: :cascade do |t| t.bigint "repository_row_id", null: false t.integer "my_module_id" - t.integer "assigned_by_id", null: false + t.bigint "assigned_by_id", null: false t.datetime "created_at" t.datetime "updated_at" t.index ["my_module_id", "repository_row_id"], name: "index_my_module_ids_repository_row_ids" t.index ["repository_row_id"], name: "index_my_module_repository_rows_on_repository_row_id" end - create_table "my_module_tags", id: :serial, force: :cascade do |t| + create_table "my_module_tags", force: :cascade do |t| t.integer "my_module_id" t.integer "tag_id" - t.integer "created_by_id" + t.bigint "created_by_id" t.index ["created_by_id"], name: "index_my_module_tags_on_created_by_id" t.index ["my_module_id"], name: "index_my_module_tags_on_my_module_id" t.index ["tag_id"], name: "index_my_module_tags_on_tag_id" end - create_table "my_modules", id: :serial, force: :cascade do |t| + create_table "my_modules", force: :cascade do |t| t.string "name", null: false t.datetime "due_date" t.string "description" t.integer "x", default: 0, null: false t.integer "y", default: 0, null: false - t.integer "my_module_group_id" + t.bigint "my_module_group_id" t.datetime "created_at", null: false t.datetime "updated_at", null: false t.boolean "archived", default: false, null: false t.datetime "archived_on" - t.integer "created_by_id" - t.integer "last_modified_by_id" - t.integer "archived_by_id" - t.integer "restored_by_id" + t.bigint "created_by_id" + t.bigint "last_modified_by_id" + t.bigint "archived_by_id" + t.bigint "restored_by_id" t.datetime "restored_on" t.integer "nr_of_assigned_samples", default: 0 t.integer "workflow_order", default: -1, null: false - t.integer "experiment_id", default: 0, null: false + t.bigint "experiment_id", default: 0, null: false t.integer "state", limit: 2, default: 0 t.datetime "completed_on" t.index "trim_html_tags((description)::text) gin_trgm_ops", name: "index_my_modules_on_description", using: :gin @@ -230,18 +230,18 @@ ActiveRecord::Schema.define(version: 20181212162649) do t.index ["restored_by_id"], name: "index_my_modules_on_restored_by_id" end - create_table "notifications", id: :serial, force: :cascade do |t| + create_table "notifications", force: :cascade do |t| t.string "title" t.string "message" t.integer "type_of", null: false - t.integer "generator_user_id" + t.bigint "generator_user_id" t.datetime "created_at", null: false t.datetime "updated_at", null: false t.index ["created_at"], name: "index_notifications_on_created_at" end create_table "oauth_access_grants", force: :cascade do |t| - t.integer "resource_owner_id", null: false + t.bigint "resource_owner_id", null: false t.bigint "application_id", null: false t.string "token", null: false t.integer "expires_in", null: false @@ -254,7 +254,7 @@ ActiveRecord::Schema.define(version: 20181212162649) do end create_table "oauth_access_tokens", force: :cascade do |t| - t.integer "resource_owner_id" + t.bigint "resource_owner_id" t.bigint "application_id" t.text "token", null: false t.string "refresh_token" @@ -281,19 +281,19 @@ ActiveRecord::Schema.define(version: 20181212162649) do t.index ["uid"], name: "index_oauth_applications_on_uid", unique: true end - create_table "projects", id: :serial, force: :cascade do |t| + create_table "projects", force: :cascade do |t| t.string "name", null: false t.integer "visibility", default: 0, null: false t.datetime "due_date" - t.integer "team_id", null: false + t.bigint "team_id", null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false t.boolean "archived", default: false, null: false t.datetime "archived_on" - t.integer "created_by_id" - t.integer "last_modified_by_id" - t.integer "archived_by_id" - t.integer "restored_by_id" + t.bigint "created_by_id" + t.bigint "last_modified_by_id" + t.bigint "archived_by_id" + t.bigint "restored_by_id" t.datetime "restored_on" t.string "experiments_order" t.index "trim_html_tags((name)::text) gin_trgm_ops", name: "index_projects_on_name", using: :gin @@ -304,36 +304,36 @@ ActiveRecord::Schema.define(version: 20181212162649) do t.index ["team_id"], name: "index_projects_on_team_id" end - create_table "protocol_keywords", id: :serial, force: :cascade do |t| + create_table "protocol_keywords", force: :cascade do |t| t.string "name" t.datetime "created_at", null: false t.datetime "updated_at", null: false t.integer "nr_of_protocols", default: 0 - t.integer "team_id", null: false + t.bigint "team_id", null: false t.index "trim_html_tags((name)::text) gin_trgm_ops", name: "index_protocol_keywords_on_name", using: :gin t.index ["team_id"], name: "index_protocol_keywords_on_team_id" end - create_table "protocol_protocol_keywords", id: :serial, force: :cascade do |t| - t.integer "protocol_id", null: false - t.integer "protocol_keyword_id", null: false + create_table "protocol_protocol_keywords", force: :cascade do |t| + t.bigint "protocol_id", null: false + t.bigint "protocol_keyword_id", null: false t.index ["protocol_id"], name: "index_protocol_protocol_keywords_on_protocol_id" t.index ["protocol_keyword_id"], name: "index_protocol_protocol_keywords_on_protocol_keyword_id" end - create_table "protocols", id: :serial, force: :cascade do |t| + create_table "protocols", force: :cascade do |t| t.string "name" t.text "authors" t.text "description" - t.integer "added_by_id" - t.integer "my_module_id" - t.integer "team_id", null: false + t.bigint "added_by_id" + t.bigint "my_module_id" + t.bigint "team_id", null: false t.integer "protocol_type", default: 0, null: false - t.integer "parent_id" + t.bigint "parent_id" t.datetime "parent_updated_at" - t.integer "archived_by_id" + t.bigint "archived_by_id" t.datetime "archived_on" - t.integer "restored_by_id" + t.bigint "restored_by_id" t.datetime "restored_on" t.datetime "created_at", null: false t.datetime "updated_at", null: false @@ -351,22 +351,22 @@ ActiveRecord::Schema.define(version: 20181212162649) do t.index ["team_id"], name: "index_protocols_on_team_id" end - create_table "report_elements", id: :serial, force: :cascade do |t| + create_table "report_elements", force: :cascade do |t| t.integer "position", null: false t.integer "type_of", null: false t.integer "sort_order", default: 0 - t.integer "report_id" + t.bigint "report_id" t.integer "parent_id" - t.integer "project_id" - t.integer "my_module_id" - t.integer "step_id" - t.integer "result_id" - t.integer "checklist_id" - t.integer "asset_id" - t.integer "table_id" + t.bigint "project_id" + t.bigint "my_module_id" + t.bigint "step_id" + t.bigint "result_id" + t.bigint "checklist_id" + t.bigint "asset_id" + t.bigint "table_id" t.datetime "created_at", null: false t.datetime "updated_at", null: false - t.integer "experiment_id" + t.bigint "experiment_id" t.integer "repository_id" t.index ["asset_id"], name: "index_report_elements_on_asset_id" t.index ["checklist_id"], name: "index_report_elements_on_checklist_id" @@ -381,14 +381,14 @@ ActiveRecord::Schema.define(version: 20181212162649) do t.index ["table_id"], name: "index_report_elements_on_table_id" end - create_table "reports", id: :serial, force: :cascade do |t| + create_table "reports", force: :cascade do |t| t.string "name", null: false t.string "description" - t.integer "project_id", null: false - t.integer "user_id", null: false + t.bigint "project_id", null: false + t.bigint "user_id", null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false - t.integer "last_modified_by_id" + t.bigint "last_modified_by_id" t.bigint "team_id" t.index "trim_html_tags((description)::text) gin_trgm_ops", name: "index_reports_on_description", using: :gin t.index "trim_html_tags((name)::text) gin_trgm_ops", name: "index_reports_on_name", using: :gin @@ -398,9 +398,9 @@ ActiveRecord::Schema.define(version: 20181212162649) do t.index ["user_id"], name: "index_reports_on_user_id" end - create_table "repositories", id: :serial, force: :cascade do |t| + create_table "repositories", force: :cascade do |t| t.integer "team_id" - t.integer "created_by_id", null: false + t.bigint "created_by_id", null: false t.string "name" t.datetime "created_at" t.datetime "updated_at" @@ -434,7 +434,7 @@ ActiveRecord::Schema.define(version: 20181212162649) do create_table "repository_columns", force: :cascade do |t| t.integer "repository_id" - t.integer "created_by_id", null: false + t.bigint "created_by_id", null: false t.string "name" t.integer "data_type", null: false t.datetime "created_at" @@ -446,8 +446,8 @@ ActiveRecord::Schema.define(version: 20181212162649) do t.datetime "data" t.datetime "created_at" t.datetime "updated_at" - t.integer "created_by_id", null: false - t.integer "last_modified_by_id", null: false + t.bigint "created_by_id", null: false + t.bigint "last_modified_by_id", null: false end create_table "repository_list_items", force: :cascade do |t| @@ -478,8 +478,8 @@ ActiveRecord::Schema.define(version: 20181212162649) do create_table "repository_rows", force: :cascade do |t| t.integer "repository_id" - t.integer "created_by_id", null: false - t.integer "last_modified_by_id", null: false + t.bigint "created_by_id", null: false + t.bigint "last_modified_by_id", null: false t.string "name" t.datetime "created_at" t.datetime "updated_at" @@ -487,7 +487,7 @@ ActiveRecord::Schema.define(version: 20181212162649) do t.index ["repository_id"], name: "index_repository_rows_on_repository_id" end - create_table "repository_table_states", id: :serial, force: :cascade do |t| + create_table "repository_table_states", force: :cascade do |t| t.jsonb "state", null: false t.integer "user_id", null: false t.integer "repository_id", null: false @@ -501,41 +501,41 @@ ActiveRecord::Schema.define(version: 20181212162649) do t.string "data" t.datetime "created_at" t.datetime "updated_at" - t.integer "created_by_id", null: false - t.integer "last_modified_by_id", null: false + t.bigint "created_by_id", null: false + t.bigint "last_modified_by_id", null: false t.index "trim_html_tags((data)::text) gin_trgm_ops", name: "index_repository_text_values_on_data", using: :gin end - create_table "result_assets", id: :serial, force: :cascade do |t| - t.integer "result_id", null: false - t.integer "asset_id", null: false + create_table "result_assets", force: :cascade do |t| + t.bigint "result_id", null: false + t.bigint "asset_id", null: false t.index ["result_id", "asset_id"], name: "index_result_assets_on_result_id_and_asset_id" end - create_table "result_tables", id: :serial, force: :cascade do |t| - t.integer "result_id", null: false - t.integer "table_id", null: false + create_table "result_tables", force: :cascade do |t| + t.bigint "result_id", null: false + t.bigint "table_id", null: false t.index ["result_id", "table_id"], name: "index_result_tables_on_result_id_and_table_id" end - create_table "result_texts", id: :serial, force: :cascade do |t| + create_table "result_texts", force: :cascade do |t| t.string "text", null: false - t.integer "result_id", null: false + t.bigint "result_id", null: false t.index "trim_html_tags((text)::text) gin_trgm_ops", name: "index_result_texts_on_text", using: :gin t.index ["result_id"], name: "index_result_texts_on_result_id" end - create_table "results", id: :serial, force: :cascade do |t| + create_table "results", force: :cascade do |t| t.string "name" - t.integer "my_module_id", null: false - t.integer "user_id", null: false + t.bigint "my_module_id", null: false + t.bigint "user_id", null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false t.boolean "archived", default: false, null: false t.datetime "archived_on" - t.integer "last_modified_by_id" - t.integer "archived_by_id" - t.integer "restored_by_id" + t.bigint "last_modified_by_id" + t.bigint "archived_by_id" + t.bigint "restored_by_id" t.datetime "restored_on" t.index "trim_html_tags((name)::text) gin_trgm_ops", name: "index_results_on_name", using: :gin t.index ["archived_by_id"], name: "index_results_on_archived_by_id" @@ -546,10 +546,10 @@ ActiveRecord::Schema.define(version: 20181212162649) do t.index ["user_id"], name: "index_results_on_user_id" end - create_table "sample_custom_fields", id: :serial, force: :cascade do |t| + create_table "sample_custom_fields", force: :cascade do |t| t.string "value", null: false - t.integer "custom_field_id", null: false - t.integer "sample_id" + t.bigint "custom_field_id", null: false + t.bigint "sample_id" t.datetime "created_at", null: false t.datetime "updated_at", null: false t.index "trim_html_tags((value)::text) gin_trgm_ops", name: "index_sample_custom_fields_on_value", using: :gin @@ -557,52 +557,52 @@ ActiveRecord::Schema.define(version: 20181212162649) do t.index ["sample_id"], name: "index_sample_custom_fields_on_sample_id" end - create_table "sample_groups", id: :serial, force: :cascade do |t| + create_table "sample_groups", force: :cascade do |t| t.string "name", null: false t.string "color", default: "#ff0000", null: false - t.integer "team_id", null: false + t.bigint "team_id", null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false - t.integer "created_by_id" - t.integer "last_modified_by_id" + t.bigint "created_by_id" + t.bigint "last_modified_by_id" t.index "trim_html_tags((name)::text) gin_trgm_ops", name: "index_sample_groups_on_name", using: :gin t.index ["created_by_id"], name: "index_sample_groups_on_created_by_id" t.index ["last_modified_by_id"], name: "index_sample_groups_on_last_modified_by_id" t.index ["team_id"], name: "index_sample_groups_on_team_id" end - create_table "sample_my_modules", id: :serial, force: :cascade do |t| - t.integer "sample_id", null: false - t.integer "my_module_id", null: false - t.integer "assigned_by_id" + create_table "sample_my_modules", force: :cascade do |t| + t.bigint "sample_id", null: false + t.bigint "my_module_id", null: false + t.bigint "assigned_by_id" t.datetime "assigned_on" t.index ["assigned_by_id"], name: "index_sample_my_modules_on_assigned_by_id" t.index ["my_module_id"], name: "index_sample_my_modules_on_my_module_id" t.index ["sample_id", "my_module_id"], name: "index_sample_my_modules_on_sample_id_and_my_module_id" end - create_table "sample_types", id: :serial, force: :cascade do |t| + create_table "sample_types", force: :cascade do |t| t.string "name", null: false - t.integer "team_id", null: false + t.bigint "team_id", null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false - t.integer "created_by_id" - t.integer "last_modified_by_id" + t.bigint "created_by_id" + t.bigint "last_modified_by_id" t.index "trim_html_tags((name)::text) gin_trgm_ops", name: "index_sample_types_on_name", using: :gin t.index ["created_by_id"], name: "index_sample_types_on_created_by_id" t.index ["last_modified_by_id"], name: "index_sample_types_on_last_modified_by_id" t.index ["team_id"], name: "index_sample_types_on_team_id" end - create_table "samples", id: :serial, force: :cascade do |t| + create_table "samples", force: :cascade do |t| t.string "name", null: false - t.integer "user_id", null: false - t.integer "team_id", null: false + t.bigint "user_id", null: false + t.bigint "team_id", null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false - t.integer "sample_group_id" - t.integer "sample_type_id" - t.integer "last_modified_by_id" + t.bigint "sample_group_id" + t.bigint "sample_type_id" + t.bigint "last_modified_by_id" t.integer "nr_of_modules_assigned_to", default: 0 t.index "trim_html_tags((name)::text) gin_trgm_ops", name: "index_samples_on_name", using: :gin t.index ["last_modified_by_id"], name: "index_samples_on_last_modified_by_id" @@ -612,7 +612,7 @@ ActiveRecord::Schema.define(version: 20181212162649) do t.index ["user_id"], name: "index_samples_on_user_id" end - create_table "samples_tables", id: :serial, force: :cascade do |t| + create_table "samples_tables", force: :cascade do |t| t.jsonb "status", default: {"time"=>0, "order"=>[[2, "desc"]], "start"=>0, "length"=>10, "search"=>{"regex"=>false, "smart"=>true, "search"=>"", "caseInsensitive"=>true}, "columns"=>[{"search"=>{"regex"=>false, "smart"=>true, "search"=>"", "caseInsensitive"=>true}, "visible"=>true}, {"search"=>{"regex"=>false, "smart"=>true, "search"=>"", "caseInsensitive"=>true}, "visible"=>true}, {"search"=>{"regex"=>false, "smart"=>true, "search"=>"", "caseInsensitive"=>true}, "visible"=>true}, {"search"=>{"regex"=>false, "smart"=>true, "search"=>"", "caseInsensitive"=>true}, "visible"=>true}, {"search"=>{"regex"=>false, "smart"=>true, "search"=>"", "caseInsensitive"=>true}, "visible"=>true}, {"search"=>{"regex"=>false, "smart"=>true, "search"=>"", "caseInsensitive"=>true}, "visible"=>true}, {"search"=>{"regex"=>false, "smart"=>true, "search"=>"", "caseInsensitive"=>true}, "visible"=>true}], "assigned"=>"all", "ColReorder"=>[0, 1, 2, 3, 4, 5, 6]}, null: false t.integer "user_id", null: false t.integer "team_id", null: false @@ -622,35 +622,35 @@ ActiveRecord::Schema.define(version: 20181212162649) do t.index ["user_id"], name: "index_samples_tables_on_user_id" end - create_table "settings", id: :serial, force: :cascade do |t| + create_table "settings", force: :cascade do |t| t.text "type", null: false t.jsonb "values", default: {}, null: false t.index ["type"], name: "index_settings_on_type", unique: true end - create_table "step_assets", id: :serial, force: :cascade do |t| - t.integer "step_id", null: false - t.integer "asset_id", null: false + create_table "step_assets", force: :cascade do |t| + t.bigint "step_id", null: false + t.bigint "asset_id", null: false t.index ["step_id", "asset_id"], name: "index_step_assets_on_step_id_and_asset_id" end - create_table "step_tables", id: :serial, force: :cascade do |t| - t.integer "step_id", null: false - t.integer "table_id", null: false + create_table "step_tables", force: :cascade do |t| + t.bigint "step_id", null: false + t.bigint "table_id", null: false t.index ["step_id", "table_id"], name: "index_step_tables_on_step_id_and_table_id", unique: true end - create_table "steps", id: :serial, force: :cascade do |t| + create_table "steps", force: :cascade do |t| t.string "name" t.string "description" t.integer "position", null: false t.boolean "completed", null: false t.datetime "completed_on" - t.integer "user_id", null: false + t.bigint "user_id", null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false - t.integer "last_modified_by_id" - t.integer "protocol_id", null: false + t.bigint "last_modified_by_id" + t.bigint "protocol_id", null: false t.index "trim_html_tags((description)::text) gin_trgm_ops", name: "index_steps_on_description", using: :gin t.index "trim_html_tags((name)::text) gin_trgm_ops", name: "index_steps_on_name", using: :gin t.index ["created_at"], name: "index_steps_on_created_at" @@ -660,12 +660,12 @@ ActiveRecord::Schema.define(version: 20181212162649) do t.index ["user_id"], name: "index_steps_on_user_id" end - create_table "tables", id: :serial, force: :cascade do |t| + create_table "tables", force: :cascade do |t| t.binary "contents", null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false - t.integer "created_by_id" - t.integer "last_modified_by_id" + t.bigint "created_by_id" + t.bigint "last_modified_by_id" t.tsvector "data_vector" t.string "name", default: "" t.integer "team_id" @@ -677,26 +677,26 @@ ActiveRecord::Schema.define(version: 20181212162649) do t.index ["team_id"], name: "index_tables_on_team_id" end - create_table "tags", id: :serial, force: :cascade do |t| + create_table "tags", force: :cascade do |t| t.string "name", null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false t.string "color", default: "#ff0000", null: false - t.integer "project_id", null: false - t.integer "created_by_id" - t.integer "last_modified_by_id" + t.bigint "project_id", null: false + t.bigint "created_by_id" + t.bigint "last_modified_by_id" t.index "trim_html_tags((name)::text) gin_trgm_ops", name: "index_tags_on_name", using: :gin t.index ["created_by_id"], name: "index_tags_on_created_by_id" t.index ["last_modified_by_id"], name: "index_tags_on_last_modified_by_id" t.index ["project_id"], name: "index_tags_on_project_id" end - create_table "teams", id: :serial, force: :cascade do |t| + create_table "teams", force: :cascade do |t| t.string "name", null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false - t.integer "created_by_id" - t.integer "last_modified_by_id" + t.bigint "created_by_id" + t.bigint "last_modified_by_id" t.string "description" t.bigint "space_taken", default: 1048576, null: false t.index ["created_by_id"], name: "index_teams_on_created_by_id" @@ -704,7 +704,7 @@ ActiveRecord::Schema.define(version: 20181212162649) do t.index ["name"], name: "index_teams_on_name" end - create_table "temp_files", id: :serial, force: :cascade do |t| + create_table "temp_files", force: :cascade do |t| t.string "session_id", null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false @@ -714,7 +714,7 @@ ActiveRecord::Schema.define(version: 20181212162649) do t.datetime "file_updated_at" end - create_table "tiny_mce_assets", id: :serial, force: :cascade do |t| + create_table "tiny_mce_assets", force: :cascade do |t| t.string "image_file_name" t.string "image_content_type" t.integer "image_file_size" @@ -730,13 +730,13 @@ ActiveRecord::Schema.define(version: 20181212162649) do t.index ["team_id"], name: "index_tiny_mce_assets_on_team_id" end - create_table "tokens", id: :serial, force: :cascade do |t| + create_table "tokens", force: :cascade do |t| t.string "token", null: false t.integer "ttl", null: false - t.integer "user_id", null: false + t.bigint "user_id", null: false end - create_table "user_identities", id: :serial, force: :cascade do |t| + create_table "user_identities", force: :cascade do |t| t.integer "user_id" t.string "provider", null: false t.string "uid", null: false @@ -747,20 +747,20 @@ ActiveRecord::Schema.define(version: 20181212162649) do t.index ["user_id"], name: "index_user_identities_on_user_id" end - create_table "user_my_modules", id: :serial, force: :cascade do |t| - t.integer "user_id", null: false - t.integer "my_module_id", null: false + create_table "user_my_modules", force: :cascade do |t| + t.bigint "user_id", null: false + t.bigint "my_module_id", null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false - t.integer "assigned_by_id" + t.bigint "assigned_by_id" t.index ["assigned_by_id"], name: "index_user_my_modules_on_assigned_by_id" t.index ["my_module_id"], name: "index_user_my_modules_on_my_module_id" t.index ["user_id"], name: "index_user_my_modules_on_user_id" end - create_table "user_notifications", id: :serial, force: :cascade do |t| - t.integer "user_id" - t.integer "notification_id" + create_table "user_notifications", force: :cascade do |t| + t.bigint "user_id" + t.bigint "notification_id" t.boolean "checked", default: false t.datetime "created_at", null: false t.datetime "updated_at", null: false @@ -769,31 +769,31 @@ ActiveRecord::Schema.define(version: 20181212162649) do t.index ["user_id"], name: "index_user_notifications_on_user_id" end - create_table "user_projects", id: :serial, force: :cascade do |t| + create_table "user_projects", force: :cascade do |t| t.integer "role" - t.integer "user_id", null: false - t.integer "project_id", null: false + t.bigint "user_id", null: false + t.bigint "project_id", null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false - t.integer "assigned_by_id" + t.bigint "assigned_by_id" t.index ["assigned_by_id"], name: "index_user_projects_on_assigned_by_id" t.index ["project_id"], name: "index_user_projects_on_project_id" t.index ["user_id"], name: "index_user_projects_on_user_id" end - create_table "user_teams", id: :serial, force: :cascade do |t| + create_table "user_teams", force: :cascade do |t| t.integer "role", default: 1, null: false - t.integer "user_id", null: false - t.integer "team_id", null: false + t.bigint "user_id", null: false + t.bigint "team_id", null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false - t.integer "assigned_by_id" + t.bigint "assigned_by_id" t.index ["assigned_by_id"], name: "index_user_teams_on_assigned_by_id" t.index ["team_id"], name: "index_user_teams_on_team_id" t.index ["user_id"], name: "index_user_teams_on_user_id" end - create_table "users", id: :serial, force: :cascade do |t| + create_table "users", force: :cascade do |t| t.string "full_name", null: false t.string "initials", null: false t.string "email", default: "", null: false @@ -824,7 +824,7 @@ ActiveRecord::Schema.define(version: 20181212162649) do t.string "invited_by_type" t.integer "invited_by_id" t.integer "invitations_count", default: 0 - t.integer "current_team_id" + t.bigint "current_team_id" t.string "authentication_token", limit: 30 t.jsonb "settings", default: {}, null: false t.jsonb "variables", default: {}, null: false @@ -849,21 +849,21 @@ ActiveRecord::Schema.define(version: 20181212162649) do t.index ["viewable_type", "viewable_id"], name: "index_view_states_on_viewable_type_and_viewable_id" end - create_table "wopi_actions", id: :serial, force: :cascade do |t| + create_table "wopi_actions", force: :cascade do |t| t.string "action", null: false t.string "extension", null: false t.string "urlsrc", null: false - t.integer "wopi_app_id", null: false + t.bigint "wopi_app_id", null: false t.index ["extension", "action"], name: "index_wopi_actions_on_extension_and_action" end - create_table "wopi_apps", id: :serial, force: :cascade do |t| + create_table "wopi_apps", force: :cascade do |t| t.string "name", null: false t.string "icon", null: false - t.integer "wopi_discovery_id", null: false + t.bigint "wopi_discovery_id", null: false end - create_table "wopi_discoveries", id: :serial, force: :cascade do |t| + create_table "wopi_discoveries", force: :cascade do |t| t.integer "expires", null: false t.string "proof_key_mod", null: false t.string "proof_key_exp", null: false @@ -871,8 +871,8 @@ ActiveRecord::Schema.define(version: 20181212162649) do t.string "proof_key_old_exp", null: false end - create_table "zip_exports", id: :serial, force: :cascade do |t| - t.integer "user_id" + create_table "zip_exports", force: :cascade do |t| + t.bigint "user_id" t.datetime "created_at", null: false t.datetime "updated_at", null: false t.string "zip_file_file_name" diff --git a/lib/tasks/exportable_items.rake b/lib/tasks/exportable_items.rake index 04f9130f3..2d178eac2 100644 --- a/lib/tasks/exportable_items.rake +++ b/lib/tasks/exportable_items.rake @@ -1,3 +1,5 @@ +# frozen_string_literal: true + namespace :exportable_items do desc 'Removes exportable zip files' task cleanup: :environment do @@ -6,23 +8,4 @@ namespace :exportable_items do puts "All exportable zip files older than " \ "'#{num.days.ago}' have been removed" end - - desc 'Resets export project counter to 0' - task reset_export_projects_counter: :environment do - User.find_each do |user| - User.transaction do - begin - user.export_vars['num_of_export_all_last_24_hours'] = 0 - user.save - rescue ActiveRecord::ActiveRecordError, - ArgumentError, - ActiveRecord::RecordNotSaved => e - puts "Error resetting users num_of_export_all_last_24_hours " \ - "variable to 0, transaction reverted: #{e}" - end - end - end - puts 'Export project counter successfully ' \ - 'reset on all users' - end end diff --git a/spec/controllers/projects_controller_spec.rb b/spec/controllers/projects_controller_spec.rb index 511fce723..1aa7f6690 100644 --- a/spec/controllers/projects_controller_spec.rb +++ b/spec/controllers/projects_controller_spec.rb @@ -54,11 +54,15 @@ describe ProjectsController, type: :controller do end end + # rubocop:disable Security/Eval + # rubocop:disable Style/EvalWithLocation (1..PROJECTS_CNT).each do |i| let!("user_projects_#{i}") do - create :user_project, project: eval("project_#{i}"), user: user + create :user_project, :owner, project: eval("project_#{i}"), user: user end end + # rubocop:enable Security/Eval + # rubocop:enable Style/EvalWithLocation describe '#index' do context 'in JSON format' do diff --git a/spec/factories/experiments.rb b/spec/factories/experiments.rb index 610c12b20..8743772e8 100644 --- a/spec/factories/experiments.rb +++ b/spec/factories/experiments.rb @@ -12,7 +12,7 @@ FactoryBot.define do project { create :project, created_by: user } factory :experiment_with_tasks do after(:create) do |e| - create_list :my_module, 3, experiment: e + create_list :my_module, 3, :with_tag, experiment: e end end end diff --git a/spec/factories/my_modules.rb b/spec/factories/my_modules.rb index 3101a427d..c383d794f 100644 --- a/spec/factories/my_modules.rb +++ b/spec/factories/my_modules.rb @@ -8,5 +8,8 @@ FactoryBot.define do workflow_order { MyModule.where(experiment_id: experiment.id).count + 1 } experiment my_module_group { create :my_module_group, experiment: experiment } + trait :with_tag do + tags { create_list :tag, 3, project: experiment.project } + end end end diff --git a/spec/factories/tags.rb b/spec/factories/tags.rb new file mode 100644 index 000000000..5e5a09f41 --- /dev/null +++ b/spec/factories/tags.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +FactoryBot.define do + factory :tag do + sequence(:name) { |n| "My tag-#{n}" } + project + color { Faker::Color.hex_color } + end +end diff --git a/spec/factories/teams.rb b/spec/factories/teams.rb index 130f67fb7..a94666a66 100644 --- a/spec/factories/teams.rb +++ b/spec/factories/teams.rb @@ -6,5 +6,8 @@ FactoryBot.define do sequence(:name) { |n| "My team-#{n}" } description { Faker::Lorem.sentence } space_taken { 1048576 } + trait :with_members do + users { create_list :user, 3 } + end end end diff --git a/spec/factories/user_project.rb b/spec/factories/user_project.rb deleted file mode 100644 index 18b5d163c..000000000 --- a/spec/factories/user_project.rb +++ /dev/null @@ -1,5 +0,0 @@ -FactoryBot.define do - factory :user_project do - role 'owner' - end -end diff --git a/spec/factories/user_projects.rb b/spec/factories/user_projects.rb new file mode 100644 index 000000000..6ab1aa8e2 --- /dev/null +++ b/spec/factories/user_projects.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +FactoryBot.define do + factory :user_project do + user + project + assigned_by { user } + trait :owner do + role { UserProject.roles[:owner] } + end + trait :normal_user do + role { UserProject.roles[:normal_user] } + end + trait :technician do + role { UserProject.roles[:technician] } + end + trait :viewer do + role { UserProject.roles[:viewer] } + end + end +end diff --git a/spec/models/experiment_spec.rb b/spec/models/experiment_spec.rb index d30877fa4..1f5bcbe14 100644 --- a/spec/models/experiment_spec.rb +++ b/spec/models/experiment_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'rails_helper' describe Experiment, type: :model do diff --git a/spec/models/tag_spec.rb b/spec/models/tag_spec.rb index e95c7abef..ded47e60c 100644 --- a/spec/models/tag_spec.rb +++ b/spec/models/tag_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'rails_helper' describe Tag, type: :model do @@ -23,15 +25,46 @@ describe Tag, type: :model do it { should have_many :my_modules } end - describe 'Should be a valid object' do - it { should validate_presence_of :name } - it { should validate_presence_of :color } - it { should validate_presence_of :project } - it do - should validate_length_of(:name).is_at_most(Constants::NAME_MAX_LENGTH) + describe 'Validations' do + describe '#name' do + it { should validate_presence_of :name } + it do + should validate_length_of(:name) + .is_at_most(Constants::NAME_MAX_LENGTH) + end end - it do - should validate_length_of(:color).is_at_most(Constants::COLOR_MAX_LENGTH) + + describe '#color' do + it { should validate_presence_of :color } + it do + should validate_length_of(:color) + .is_at_most(Constants::COLOR_MAX_LENGTH) + end + end + describe '#projects' do + it { should validate_presence_of :project } + end + end + describe '.clone_to_project_or_return_existing' do + let(:project) { create :project } + let(:tag) { create :tag } + + context 'when tag does not exits' do + it 'does create new tag for project' do + expect do + tag.clone_to_project_or_return_existing(project) + end.to(change { Tag.where(project_id: project.id).count }) + end + end + + context 'when tag already exists' do + it 'return existing tag for project' do + Tag.create(name: tag.name, color: tag.color, project: project) + + expect do + tag.clone_to_project_or_return_existing(project) + end.to_not(change { Tag.where(project_id: project.id).count }) + end end end end diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 167dabb5a..a41f965b3 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -187,7 +187,9 @@ describe User, type: :model do describe '#last_activities' do let!(:user) { create :user } let!(:project) { create :project } - let!(:user_projects) { create :user_project, project: project, user: user } + let!(:user_projects) do + create :user_project, :viewer, project: project, user: user + end let!(:activity_one) { create :activity, user: user, project: project } let!(:activity_two) { create :activity, user: user, project: project } @@ -198,4 +200,62 @@ describe User, type: :model do expect(activities).to include activity_two end end + + describe '#increase_daily_exports_counter!' do + let(:user) { create :user } + context 'when last_export_timestamp is set' do + it 'increases counter by 1' do + expect { user.increase_daily_exports_counter! } + .to change { + user.reload.export_vars['num_of_export_all_last_24_hours'] + }.from(0).to(1) + end + + it 'sets last_export_timestamp on today' do + user.export_vars['last_export_timestamp'] = Date.yesterday.to_time.to_i + user.save + + expect { user.increase_daily_exports_counter! } + .to change { + user.reload.export_vars['last_export_timestamp'] + }.to(Date.today.to_time.to_i) + end + + it 'sets new counter for today' do + user.export_vars = { + 'num_of_export_all_last_24_hours': 3, + 'last_export_timestamp': Date.yesterday.to_time.to_i + } + user.save + + expect { user.increase_daily_exports_counter! } + .to change { + user.reload.export_vars['num_of_export_all_last_24_hours'] + }.from(3).to(1) + end + end + + context 'when last_export_timestamp not exists (existing users)' do + it 'sets last_export_timestamp on today' do + user.export_vars.delete('last_export_timestamp') + user.save + + expect { user.increase_daily_exports_counter! } + .to change { + user.reload.export_vars['last_export_timestamp'] + }.from(nil).to(Date.today.to_time.to_i) + end + + it 'starts count reports with 1' do + user.export_vars.delete('last_export_timestamp') + user.export_vars['num_of_export_all_last_24_hours'] = 2 + user.save + + expect { user.increase_daily_exports_counter! } + .to change { + user.reload.export_vars['num_of_export_all_last_24_hours'] + }.from(2).to(1) + end + end + end end diff --git a/spec/requests/api/v1/users_controller_spec.rb b/spec/requests/api/v1/users_controller_spec.rb index ba705391d..508b471ec 100644 --- a/spec/requests/api/v1/users_controller_spec.rb +++ b/spec/requests/api/v1/users_controller_spec.rb @@ -4,9 +4,9 @@ require 'rails_helper' RSpec.describe 'Api::V1::UsersController', type: :request do before :all do - @user1 = create(:user, email: Faker::Internet.unique.email) - @user2 = create(:user, email: Faker::Internet.unique.email) - @user3 = create(:user, email: Faker::Internet.unique.email) + @user1 = create(:user) + @user2 = create(:user) + @user3 = create(:user) @team1 = create(:team, created_by: @user1) @team2 = create(:team, created_by: @user2) @team3 = create(:team, created_by: @user3) @@ -40,7 +40,7 @@ RSpec.describe 'Api::V1::UsersController', type: :request do it 'When invalid request, non existing user' do hash_body = nil - get api_v1_user_path(id: 123), headers: @valid_headers + get api_v1_user_path(id: -1), headers: @valid_headers expect(response).to have_http_status(403) expect { hash_body = json }.not_to raise_exception expect(hash_body['errors'][0]).to include('status': 403) diff --git a/spec/services/experiments/move_to_project_service_spec.rb b/spec/services/experiments/move_to_project_service_spec.rb new file mode 100644 index 000000000..67d8e33a9 --- /dev/null +++ b/spec/services/experiments/move_to_project_service_spec.rb @@ -0,0 +1,98 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe Experiments::MoveToProjectService do + let(:team) { create :team, :with_members } + let(:project) do + create :project, team: team, user_projects: [] + end + let(:new_project) do + create :project, team: team, user_projects: [user_project2] + end + let(:experiment) do + create :experiment_with_tasks, name: 'MyExp', project: project + end + let(:user) { create :user } + let(:user_project2) { create :user_project, :normal_user, user: user } + let(:service_call) do + Experiments::MoveToProjectService.call(experiment_id: experiment.id, + project_id: new_project.id, + user_id: user.id) + end + + context 'when call with valid params' do + it 'unnasigns experiment from project' do + service_call + + expect(project.experiments.pluck(:id)).to_not include(experiment.id) + end + + it 'assigns experiment to new_project' do + service_call + + expect(new_project.experiments.pluck(:id)).to include(experiment.id) + end + + it 'copies tags to new project' do + expect { service_call }.to(change { new_project.tags.count }) + end + + it 'leaves tags on an old project' do + experiment # explicit call to create tags + expect { service_call }.not_to(change { project.tags.count }) + end + + it 'sets new project tags to modules' do + service_call + new_tags = experiment.my_modules.map { |m| m.tags.map { |t| t } }.flatten + tags_project_id = new_tags.map(&:project_id).uniq.first + + expect(tags_project_id).to be == new_project.id + end + + it 'adds Activity record' do + expect { service_call }.to(change { Activity.all.count }) + end + end + + context 'when call with invalid params' do + it 'returns an error when can\'t find user and project' do + allow(Project).to receive(:find).and_return(nil) + allow(User).to receive(:find).and_return(nil) + + expect(service_call.errors).to have_key(:invalid_arguments) + end + + it 'returns an error on validate' do + FactoryBot.create :experiment, project: new_project, name: 'MyExp' + expect(service_call.succeed?).to be_falsey + end + + it 'returns an error on saving' do + expect_any_instance_of(Experiments::MoveToProjectService) + .to receive(:valid?) + .and_return(true) + FactoryBot.create :experiment, project: new_project, name: 'MyExp' + + expect(service_call.succeed?).to be_falsey + end + + it 'rollbacks cloned tags after unsucessful save' do + expect_any_instance_of(Experiments::MoveToProjectService) + .to receive(:valid?) + .and_return(true) + FactoryBot.create :experiment, project: new_project, name: 'MyExp' + experiment + + expect { service_call }.not_to(change { Tag.count }) + end + + it 'returns error if teams is not the same' do + t = create :team, :with_members + project.update(team: t) + + expect(service_call.errors).to have_key(:target_project_not_valid) + end + end +end