diff --git a/.dockerignore b/.dockerignore index e3cd043e9..58a818464 100644 --- a/.dockerignore +++ b/.dockerignore @@ -6,9 +6,12 @@ # Ignore bundler config. /.bundle -# Ignore all environment files (except templates). +# Ignore all environment files /.env* -!/.env*.erb + +# Ignore Kamal files. +/config/deploy*.yml +/.kamal # Ignore all default key files. /config/master.key @@ -45,3 +48,50 @@ # Ignore Docker-related files /.dockerignore /Dockerfile* + +# SAPI +/coverage +/rdoc +/private +/public/system +/public/downloads/*.pdf +#LaTeX +/public/latex/*.aux +/public/latex/*.out +/public/latex/*.log +/public/latex/*.gz +/public/latex/index.pdf +/public/latex/history.pdf +/public/sitemap* + +#checklist downloads +/public/downloads/checklist/*.* + +#exports csvs +/public/downloads/documents/*.csv +/public/downloads/quotas/*.csv +/public/downloads/cites_listings/*.csv +/public/downloads/cites_suspensions/*.csv +/public/downloads/eu_listings/*.csv +/public/downloads/eu_decisions/*.csv +/public/downloads/cms_listings/*.csv +/public/downloads/checklist/*.pdf +/public/downloads/checklist/*.csv +/public/downloads/checklist/*.json +/public/downloads/taxon_concepts_names/*.csv +/public/downloads/synonyms_and_trade_names/*.csv +/public/downloads/taxon_concepts_distributions/*.csv +/public/downloads/shipments/*.csv +/public/downloads/comptab/*.csv +/public/downloads/gross_exports/*.csv +/public/downloads/gross_imports/*.csv +/public/downloads/net_exports/*.csv +/public/downloads/net_imports/*.csv +/public/downloads/trade_download_stats/*.csv +/public/downloads/species_reference_output/*.csv +/public/downloads/standard_reference_output/*.csv +/public/downloads/common_names/*.csv +/public/downloads/iucn_mappings/*.csv +/public/downloads/cms_mappings/*.csv +/public/downloads/orphaned_taxon_concepts/*.csv +/public/uploads/* diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 000000000..1a7a1d3ba --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,158 @@ +name: Deploy SAPI Rails API +on: + push: + branches: + - staging + workflow_dispatch: + +jobs: + deploy: + runs-on: self-hosted + environment: staging + steps: + - name: Set workflow start time + run: | + echo "START_TIME=$(date -u +"%Y-%m-%dT%H:%M:%SZ")" >> $GITHUB_ENV + + - uses: actions/checkout@v4 + + - name: Notify deployment start + id: notify-start + uses: unepwcmc/devops-actions/.github/actions/slack-notify@v1 + with: + slack-bot-token: ${{ secrets.SLACK_BOT_TOKEN }} + slack-channel-id: ${{ secrets.SLACK_CHANNEL_ID }} + notification-type: started + action-type: Deploy + environment: staging + repository: ${{ github.repository }} + repository-url: ${{ github.server_url }}/${{ github.repository }} + action-run-url: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} + actor: ${{ github.actor }} + actor-url: ${{ github.server_url }}/${{ github.actor }} + workflow-name: ${{ github.workflow }} + run-id: ${{ github.run_id }} + commit-message: ${{ github.event.head_commit.message || 'Manual workflow trigger' }} + runner-name: ${{ runner.name }} + start-time: ${{ env.START_TIME }} + + - name: Export and validate kamal secrets + shell: bash + env: + SECRETS_JSON: ${{ toJSON(secrets) }} + run: | + if [[ ! -f ".kamal/secrets-common" ]]; then + echo "Error: .kamal/secrets-common file not found." + exit 1 + fi + + temp_secrets="$(mktemp)" + trap 'rm -f "$temp_secrets"' EXIT + printf '%s' "$SECRETS_JSON" > "$temp_secrets" + + needed_vars=() + while IFS= read -r line; do + trimmed="$(echo "$line" | tr -d '\r' | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')" + [[ -z "$trimmed" || "$trimmed" == \#* ]] && continue + [[ "$trimmed" != *=* ]] && continue + + value="${trimmed#*=}" + value="${value#"${value%%[![:space:]]*}"}" + value="${value%"${value##*[![:space:]]}"}" + + [[ "$value" != \$* ]] && continue + [[ "$value" == '$('* ]] && continue + + if [[ "$value" == \$\{* ]]; then + if [[ "$value" =~ ^\$\{([A-Z0-9_]+) ]]; then + needed_vars+=("${BASH_REMATCH[1]}") + fi + elif [[ "$value" =~ ^\$([A-Z0-9_]+) ]]; then + needed_vars+=("${BASH_REMATCH[1]}") + fi + done < ".kamal/secrets-common" + + IFS=$'\n' read -r -d '' -a unique_vars < <(printf '%s\n' "${needed_vars[@]}" | sort -u && printf '\0') + + for var in "${unique_vars[@]}"; do + value=$(jq -r --arg key "$var" '.[$key] // empty' "$temp_secrets") + if [[ -z "$value" ]]; then + echo "Missing environment variable required in .kamal/secrets-common: ${var}" >&2 + exit 1 + fi + + { + echo "${var}<> "$GITHUB_ENV" + done + + echo "✅ All required kamal secrets validated" + + - name: Deploy with Kamal v2 + uses: unepwcmc/devops-actions/.github/actions/kamal-v2.x-deploy@v1 + with: + environment: staging + working-directory: . + env: + SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }} + + - name: Set workflow end time and calculate duration + if: always() + run: | + echo "END_TIME=$(date -u +"%Y-%m-%dT%H:%M:%SZ")" >> $GITHUB_ENV + if [[ -n "$START_TIME" ]]; then + start_timestamp=$(date -d "$START_TIME" +%s) + end_timestamp=$(date -d "$(date -u +"%Y-%m-%dT%H:%M:%SZ")" +%s) + duration=$((end_timestamp - start_timestamp)) + echo "DEPLOYMENT_DURATION=${duration}" >> $GITHUB_ENV + else + echo "DEPLOYMENT_DURATION=0" >> $GITHUB_ENV + fi + + - name: Notify deployment success + if: success() + uses: unepwcmc/devops-actions/.github/actions/slack-notify@v1 + with: + slack-bot-token: ${{ secrets.SLACK_BOT_TOKEN }} + slack-channel-id: ${{ secrets.SLACK_CHANNEL_ID }} + notification-type: success + action-type: Deploy + environment: staging + repository: ${{ github.repository }} + repository-url: ${{ github.server_url }}/${{ github.repository }} + action-run-url: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} + actor: ${{ github.actor }} + actor-url: ${{ github.server_url }}/${{ github.actor }} + workflow-name: ${{ github.workflow }} + run-id: ${{ github.run_id }} + commit-message: ${{ github.event.head_commit.message || 'Manual workflow trigger' }} + runner-name: ${{ runner.name }} + start-time: ${{ env.START_TIME }} + end-time: ${{ env.END_TIME }} + deployment-duration: ${{ env.DEPLOYMENT_DURATION }} + update-message-ts: ${{ steps.notify-start.outputs.message-ts }} + + - name: Notify deployment failure + if: failure() + uses: unepwcmc/devops-actions/.github/actions/slack-notify@v1 + with: + slack-bot-token: ${{ secrets.SLACK_BOT_TOKEN }} + slack-channel-id: ${{ secrets.SLACK_CHANNEL_ID }} + notification-type: failure + action-type: Deploy + environment: staging + repository: ${{ github.repository }} + repository-url: ${{ github.server_url }}/${{ github.repository }} + action-run-url: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} + actor: ${{ github.actor }} + actor-url: ${{ github.server_url }}/${{ github.actor }} + workflow-name: ${{ github.workflow }} + run-id: ${{ github.run_id }} + commit-message: ${{ github.event.head_commit.message || 'Manual workflow trigger' }} + runner-name: ${{ runner.name }} + start-time: ${{ env.START_TIME }} + end-time: ${{ env.END_TIME }} + deployment-duration: ${{ env.DEPLOYMENT_DURATION }} + update-message-ts: ${{ steps.notify-start.outputs.message-ts }} diff --git a/.github/workflows/kamal-setup.yml b/.github/workflows/kamal-setup.yml new file mode 100644 index 000000000..ad607227a --- /dev/null +++ b/.github/workflows/kamal-setup.yml @@ -0,0 +1,162 @@ +name: Kamal v2 SAPI Setup (One-time per Environment) +on: + workflow_dispatch: + inputs: + environment: + description: 'Environment to setup' + required: true + type: choice + options: + - staging + confirm: + description: 'Type "CONFIRM" to proceed with setup' + required: true + type: string + +jobs: + validate-input: + runs-on: self-hosted + steps: + - name: Validate confirmation + run: | + if [[ "${{ github.event.inputs.confirm }}" != "CONFIRM" ]]; then + echo "❌ Setup cancelled. You must type 'CONFIRM' to proceed." + echo "⚠️ This is a one-time setup operation that will:" + echo " • Initialize Kamal v2 configuration for ${{ github.event.inputs.environment }}" + echo " • Set up Docker containers and services" + echo " • Configure the deployment infrastructure" + exit 1 + fi + echo "✅ Setup confirmed for ${{ github.event.inputs.environment }} environment" + + setup: + needs: validate-input + runs-on: self-hosted + environment: ${{ github.event.inputs.environment }} + env: + RAILS_MASTER_KEY: ${{ secrets.RAILS_MASTER_KEY }} + SAPI_DATABASE_HOST: ${{ secrets.SAPI_DATABASE_HOST }} + SAPI_DATABASE_NAME: ${{ secrets.SAPI_DATABASE_NAME }} + SAPI_DATABASE_USERNAME: ${{ secrets.SAPI_DATABASE_USERNAME }} + SAPI_DATABASE_PASSWORD: ${{ secrets.SAPI_DATABASE_PASSWORD }} + SAPI_DATABASE_PORT: ${{ secrets.SAPI_DATABASE_PORT }} + CAPTIVE_BREEDING_DATABASE_HOST: ${{ secrets.CAPTIVE_BREEDING_DATABASE_HOST }} + CAPTIVE_BREEDING_DATABASE_NAME: ${{ secrets.CAPTIVE_BREEDING_DATABASE_NAME }} + CAPTIVE_BREEDING_DATABASE_USERNAME: ${{ secrets.CAPTIVE_BREEDING_DATABASE_USERNAME }} + CAPTIVE_BREEDING_DATABASE_PASSWORD: ${{ secrets.CAPTIVE_BREEDING_DATABASE_PASSWORD }} + CAPTIVE_BREEDING_DATABASE_PORT: ${{ secrets.CAPTIVE_BREEDING_DATABASE_PORT }} + MAIL_USERNAME: ${{ secrets.MAIL_USERNAME }} + MAIL_PASSWORD: ${{ secrets.MAIL_PASSWORD }} + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + AWS_REGION: ${{ secrets.AWS_REGION }} + SAPI_SIDEKIQ_REDIS_URL: ${{ secrets.SAPI_SIDEKIQ_REDIS_URL }} + SAPI_SIDEKIQ_REDIS_CACHE_URL: ${{ secrets.SAPI_SIDEKIQ_REDIS_CACHE_URL }} + CERTIFICATE_PEM: ${{ secrets.CERTIFICATE_PEM }} + PRIVATE_KEY_PEM: ${{ secrets.PRIVATE_KEY_PEM }} + steps: + - name: Set workflow start time + run: | + echo "START_TIME=$(date -u +"%Y-%m-%dT%H:%M:%SZ")" >> $GITHUB_ENV + + - uses: actions/checkout@v4 + + - name: Notify setup start + id: notify-start + uses: unepwcmc/devops-actions/.github/actions/slack-notify@v1 + with: + slack-bot-token: ${{ secrets.SLACK_BOT_TOKEN }} + slack-channel-id: ${{ secrets.SLACK_CHANNEL_ID }} + notification-type: started + action-type: Setup + environment: ${{ github.event.inputs.environment }} + repository: ${{ github.repository }} + repository-url: ${{ github.server_url }}/${{ github.repository }} + action-run-url: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} + actor: ${{ github.actor }} + actor-url: ${{ github.server_url }}/${{ github.actor }} + workflow-name: ${{ github.workflow }} + run-id: ${{ github.run_id }} + commit-message: ${{ github.event.head_commit.message || 'Manual workflow trigger' }} + runner-name: ${{ runner.name }} + start-time: ${{ env.START_TIME }} + + - name: Set up SSH agent + uses: webfactory/ssh-agent@v0.9.0 + with: + ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }} + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + working-directory: deploy + bundler-cache: false + + - name: Install Kamal + working-directory: deploy + run: bundle install --quiet + + - name: Run Kamal setup + run: BUNDLE_GEMFILE=deploy/Gemfile bundle exec kamal setup -d ${{ github.event.inputs.environment }} + + - name: Set workflow end time and calculate duration + if: always() + run: | + echo "END_TIME=$(date -u +"%Y-%m-%dT%H:%M:%SZ")" >> $GITHUB_ENV + if [[ -n "$START_TIME" ]]; then + start_timestamp=$(date -d "$START_TIME" +%s) + end_timestamp=$(date -d "$(date -u +"%Y-%m-%dT%H:%M:%SZ")" +%s) + duration=$((end_timestamp - start_timestamp)) + echo "DEPLOYMENT_DURATION=${duration}" >> $GITHUB_ENV + else + echo "DEPLOYMENT_DURATION=0" >> $GITHUB_ENV + fi + + - name: Notify setup success + if: success() + uses: unepwcmc/devops-actions/.github/actions/slack-notify@v1 + with: + slack-bot-token: ${{ secrets.SLACK_BOT_TOKEN }} + slack-channel-id: ${{ secrets.SLACK_CHANNEL_ID }} + notification-type: success + action-type: Setup + environment: ${{ github.event.inputs.environment }} + repository: ${{ github.repository }} + repository-url: ${{ github.server_url }}/${{ github.repository }} + action-run-url: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} + actor: ${{ github.actor }} + actor-url: ${{ github.server_url }}/${{ github.actor }} + workflow-name: ${{ github.workflow }} + run-id: ${{ github.run_id }} + commit-message: ${{ github.event.head_commit.message || 'Manual workflow trigger' }} + runner-name: ${{ runner.name }} + start-time: ${{ env.START_TIME }} + end-time: ${{ env.END_TIME }} + deployment-duration: ${{ env.DEPLOYMENT_DURATION }} + update-message-ts: ${{ steps.notify-start.outputs.message-ts }} + + - name: Notify setup failure + if: failure() + uses: unepwcmc/devops-actions/.github/actions/slack-notify@v1 + with: + slack-bot-token: ${{ secrets.SLACK_BOT_TOKEN }} + slack-channel-id: ${{ secrets.SLACK_CHANNEL_ID }} + notification-type: failure + action-type: Setup + environment: ${{ github.event.inputs.environment }} + repository: ${{ github.repository }} + repository-url: ${{ github.server_url }}/${{ github.repository }} + action-run-url: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} + actor: ${{ github.actor }} + actor-url: ${{ github.server_url }}/${{ github.actor }} + workflow-name: ${{ github.workflow }} + run-id: ${{ github.run_id }} + commit-message: ${{ github.event.head_commit.message || 'Manual workflow trigger' }} + runner-name: ${{ runner.name }} + start-time: ${{ env.START_TIME }} + end-time: ${{ env.END_TIME }} + deployment-duration: ${{ env.DEPLOYMENT_DURATION }} + update-message-ts: ${{ steps.notify-start.outputs.message-ts }} diff --git a/.gitignore b/.gitignore index f856c4e89..bf1f2d413 100644 --- a/.gitignore +++ b/.gitignore @@ -7,9 +7,8 @@ # Ignore bundler config /.bundle -# Ignore all environment files (except templates). +# Ignore all environment files /.env* -!/.env*.erb # Ignore all logfiles and tempfiles. /log/* @@ -30,8 +29,8 @@ !/public/assets/.keep .byebug_history -# Ignore master key for decrypting credentials and more. -/config/master.key +# Ignore key files for decrypting credentials and more. +/config/*.key /config/credentials/*.key /coverage diff --git a/.kamal/hooks/docker-setup.sample b/.kamal/hooks/docker-setup.sample new file mode 100755 index 000000000..2fb07d7d7 --- /dev/null +++ b/.kamal/hooks/docker-setup.sample @@ -0,0 +1,3 @@ +#!/bin/sh + +echo "Docker set up on $KAMAL_HOSTS..." diff --git a/.kamal/hooks/post-app-boot.sample b/.kamal/hooks/post-app-boot.sample new file mode 100755 index 000000000..70f9c4bc9 --- /dev/null +++ b/.kamal/hooks/post-app-boot.sample @@ -0,0 +1,3 @@ +#!/bin/sh + +echo "Booted app version $KAMAL_VERSION on $KAMAL_HOSTS..." diff --git a/.kamal/hooks/post-deploy.sample b/.kamal/hooks/post-deploy.sample new file mode 100755 index 000000000..fd364c2a7 --- /dev/null +++ b/.kamal/hooks/post-deploy.sample @@ -0,0 +1,14 @@ +#!/bin/sh + +# A sample post-deploy hook +# +# These environment variables are available: +# KAMAL_RECORDED_AT +# KAMAL_PERFORMER +# KAMAL_VERSION +# KAMAL_HOSTS +# KAMAL_ROLES (if set) +# KAMAL_DESTINATION (if set) +# KAMAL_RUNTIME + +echo "$KAMAL_PERFORMER deployed $KAMAL_VERSION to $KAMAL_DESTINATION in $KAMAL_RUNTIME seconds" diff --git a/.kamal/hooks/post-proxy-reboot.sample b/.kamal/hooks/post-proxy-reboot.sample new file mode 100755 index 000000000..1435a677f --- /dev/null +++ b/.kamal/hooks/post-proxy-reboot.sample @@ -0,0 +1,3 @@ +#!/bin/sh + +echo "Rebooted kamal-proxy on $KAMAL_HOSTS" diff --git a/.kamal/hooks/pre-app-boot.sample b/.kamal/hooks/pre-app-boot.sample new file mode 100755 index 000000000..45f735504 --- /dev/null +++ b/.kamal/hooks/pre-app-boot.sample @@ -0,0 +1,3 @@ +#!/bin/sh + +echo "Booting app version $KAMAL_VERSION on $KAMAL_HOSTS..." diff --git a/.kamal/hooks/pre-build b/.kamal/hooks/pre-build new file mode 100755 index 000000000..c5a55678b --- /dev/null +++ b/.kamal/hooks/pre-build @@ -0,0 +1,51 @@ +#!/bin/sh + +# A sample pre-build hook +# +# Checks: +# 1. We have a clean checkout +# 2. A remote is configured +# 3. The branch has been pushed to the remote +# 4. The version we are deploying matches the remote +# +# These environment variables are available: +# KAMAL_RECORDED_AT +# KAMAL_PERFORMER +# KAMAL_VERSION +# KAMAL_HOSTS +# KAMAL_ROLES (if set) +# KAMAL_DESTINATION (if set) + +if [ -n "$(git status --porcelain)" ]; then + echo "Git checkout is not clean, aborting..." >&2 + git status --porcelain >&2 + exit 1 +fi + +first_remote=$(git remote) + +if [ -z "$first_remote" ]; then + echo "No git remote set, aborting..." >&2 + exit 1 +fi + +current_branch=$(git branch --show-current) + +if [ -z "$current_branch" ]; then + echo "Not on a git branch, aborting..." >&2 + exit 1 +fi + +remote_head=$(git ls-remote $first_remote --tags $current_branch | cut -f1) + +if [ -z "$remote_head" ]; then + echo "Branch not pushed to remote, aborting..." >&2 + exit 1 +fi + +if [ "$KAMAL_VERSION" != "$remote_head" ]; then + echo "Version ($KAMAL_VERSION) does not match remote HEAD ($remote_head), aborting..." >&2 + exit 1 +fi + +exit 0 diff --git a/.kamal/hooks/pre-connect.sample b/.kamal/hooks/pre-connect.sample new file mode 100755 index 000000000..77744bdca --- /dev/null +++ b/.kamal/hooks/pre-connect.sample @@ -0,0 +1,47 @@ +#!/usr/bin/env ruby + +# A sample pre-connect check +# +# Warms DNS before connecting to hosts in parallel +# +# These environment variables are available: +# KAMAL_RECORDED_AT +# KAMAL_PERFORMER +# KAMAL_VERSION +# KAMAL_HOSTS +# KAMAL_ROLES (if set) +# KAMAL_DESTINATION (if set) +# KAMAL_RUNTIME + +hosts = ENV["KAMAL_HOSTS"].split(",") +results = nil +max = 3 + +elapsed = Benchmark.realtime do + results = hosts.map do |host| + Thread.new do + tries = 1 + + begin + Socket.getaddrinfo(host, 0, Socket::AF_UNSPEC, Socket::SOCK_STREAM, nil, Socket::AI_CANONNAME) + rescue SocketError + if tries < max + puts "Retrying DNS warmup: #{host}" + tries += 1 + sleep rand + retry + else + puts "DNS warmup failed: #{host}" + host + end + end + + tries + end + end.map(&:value) +end + +retries = results.sum - hosts.size +nopes = results.count { |r| r == max } + +puts "Prewarmed %d DNS lookups in %.2f sec: %d retries, %d failures" % [ hosts.size, elapsed, retries, nopes ] diff --git a/.kamal/hooks/pre-deploy.sample b/.kamal/hooks/pre-deploy.sample new file mode 100755 index 000000000..05b3055b7 --- /dev/null +++ b/.kamal/hooks/pre-deploy.sample @@ -0,0 +1,122 @@ +#!/usr/bin/env ruby + +# A sample pre-deploy hook +# +# Checks the Github status of the build, waiting for a pending build to complete for up to 720 seconds. +# +# Fails unless the combined status is "success" +# +# These environment variables are available: +# KAMAL_RECORDED_AT +# KAMAL_PERFORMER +# KAMAL_VERSION +# KAMAL_HOSTS +# KAMAL_COMMAND +# KAMAL_SUBCOMMAND +# KAMAL_ROLES (if set) +# KAMAL_DESTINATION (if set) + +# Only check the build status for production deployments +if ENV["KAMAL_COMMAND"] == "rollback" || ENV["KAMAL_DESTINATION"] != "production" + exit 0 +end + +require "bundler/inline" + +# true = install gems so this is fast on repeat invocations +gemfile(true, quiet: true) do + source "https://rubygems.org" + + gem "octokit" + gem "faraday-retry" +end + +MAX_ATTEMPTS = 72 +ATTEMPTS_GAP = 10 + +def exit_with_error(message) + $stderr.puts message + exit 1 +end + +class GithubStatusChecks + attr_reader :remote_url, :git_sha, :github_client, :combined_status + + def initialize + @remote_url = github_repo_from_remote_url + @git_sha = `git rev-parse HEAD`.strip + @github_client = Octokit::Client.new(access_token: ENV["GITHUB_TOKEN"]) + refresh! + end + + def refresh! + @combined_status = github_client.combined_status(remote_url, git_sha) + end + + def state + combined_status[:state] + end + + def first_status_url + first_status = combined_status[:statuses].find { |status| status[:state] == state } + first_status && first_status[:target_url] + end + + def complete_count + combined_status[:statuses].count { |status| status[:state] != "pending"} + end + + def total_count + combined_status[:statuses].count + end + + def current_status + if total_count > 0 + "Completed #{complete_count}/#{total_count} checks, see #{first_status_url} ..." + else + "Build not started..." + end + end + + private + def github_repo_from_remote_url + url = `git config --get remote.origin.url`.strip.delete_suffix(".git") + if url.start_with?("https://github.com/") + url.delete_prefix("https://github.com/") + elsif url.start_with?("git@github.com:") + url.delete_prefix("git@github.com:") + else + url + end + end +end + + +$stdout.sync = true + +begin + puts "Checking build status..." + + attempts = 0 + checks = GithubStatusChecks.new + + loop do + case checks.state + when "success" + puts "Checks passed, see #{checks.first_status_url}" + exit 0 + when "failure" + exit_with_error "Checks failed, see #{checks.first_status_url}" + when "pending" + attempts += 1 + end + + exit_with_error "Checks are still pending, gave up after #{MAX_ATTEMPTS * ATTEMPTS_GAP} seconds" if attempts == MAX_ATTEMPTS + + puts checks.current_status + sleep(ATTEMPTS_GAP) + checks.refresh! + end +rescue Octokit::NotFound + exit_with_error "Build status could not be found" +end diff --git a/.kamal/hooks/pre-proxy-reboot.sample b/.kamal/hooks/pre-proxy-reboot.sample new file mode 100755 index 000000000..061f8059e --- /dev/null +++ b/.kamal/hooks/pre-proxy-reboot.sample @@ -0,0 +1,3 @@ +#!/bin/sh + +echo "Rebooting kamal-proxy on $KAMAL_HOSTS..." diff --git a/.kamal/secrets-common b/.kamal/secrets-common new file mode 100644 index 000000000..70635feb0 --- /dev/null +++ b/.kamal/secrets-common @@ -0,0 +1,40 @@ +# Minimal Secrets Template - Backend Rails API Kamal Deployment +# Secrets defined here are available for reference under registry/password, env/secret, builder/secrets, +# and accessories/*/env/secret in config/deploy.yml. All secrets should be pulled from either +# password manager, ENV, or a file. DO NOT ENTER RAW CREDENTIALS HERE! This file needs to be safe for git. + +# Rails Configuration (REQUIRED) +RAILS_MASTER_KEY=$RAILS_MASTER_KEY + +# SAPI Database Configuration +SAPI_DATABASE_HOST=$SAPI_DATABASE_HOST +SAPI_DATABASE_NAME=$SAPI_DATABASE_NAME +SAPI_DATABASE_USERNAME=$SAPI_DATABASE_USERNAME +SAPI_DATABASE_PASSWORD=$SAPI_DATABASE_PASSWORD +SAPI_DATABASE_PORT=$SAPI_DATABASE_PORT + +# Captive Breeding Database Configuration +CAPTIVE_BREEDING_DATABASE_HOST=$CAPTIVE_BREEDING_DATABASE_HOST +CAPTIVE_BREEDING_DATABASE_NAME=$CAPTIVE_BREEDING_DATABASE_NAME +CAPTIVE_BREEDING_DATABASE_USERNAME=$CAPTIVE_BREEDING_DATABASE_USERNAME +CAPTIVE_BREEDING_DATABASE_PASSWORD=$CAPTIVE_BREEDING_DATABASE_PASSWORD +CAPTIVE_BREEDING_DATABASE_PORT=$CAPTIVE_BREEDING_DATABASE_PORT + +# Mail Configuration +MAIL_USERNAME=$MAIL_USERNAME +MAIL_PASSWORD=$MAIL_PASSWORD + +# AWS Configuration +AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID +AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY +AWS_REGION=$AWS_REGION + +# Redis for Sidekiq +SAPI_SIDEKIQ_REDIS_URL=$SAPI_SIDEKIQ_REDIS_URL + +# Redis for cache +SAPI_SIDEKIQ_REDIS_CACHE_URL=$SAPI_SIDEKIQ_REDIS_CACHE_URL + +# SSL certificates +CERTIFICATE_PEM=$CERTIFICATE_PEM +PRIVATE_KEY_PEM=$PRIVATE_KEY_PEM diff --git a/.node-version b/.node-version new file mode 100644 index 000000000..08b7109d0 --- /dev/null +++ b/.node-version @@ -0,0 +1 @@ +18.20.8 diff --git a/.rubocop.yml b/.rubocop.yml index 0d729d038..04854f23f 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -12,8 +12,8 @@ require: Rails: Enabled: true AllCops: - TargetRubyVersion: 3.2.5 - TargetRailsVersion: 7.1 + TargetRubyVersion: 3.4 + TargetRailsVersion: 8.1 Style/StringLiterals: EnforcedStyle: single_quotes @@ -175,6 +175,11 @@ Layout/AssignmentIndentation: Metrics/MethodLength: Enabled: false + CountAsOne: + - array + - hash + - heredoc + - method_call Naming/VariableNumber: Enabled: false @@ -186,6 +191,15 @@ Layout/HashAlignment: Rails/NotNullColumn: Enabled: false +Rails/ExampleLength: + Enabled: true + Max: 10 + CountAsOne: + - array + - hash + - heredoc + - method_call + Rails/UnknownEnv: Enabled: true Environments: diff --git a/.ruby-version b/.ruby-version index 5ae69bd5f..7bcbb3808 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -3.2.5 +3.4.9 diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c88f2f20..1fb1ee0ed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,25 @@ +### 1.22.0 + +**Rails 7.2 Upgrade** + +The primary goal of this release is to upgrade the Rails version without causing +any breaking changes to functionality. + +* Upgrades Rails from 7.1.3.4 to 8.0.5 +* Upgrades Ruby from 3.2.5 to 3.4.9 +* Some dependency updates/changes consequently allowed or required, in particular: + * Upgraded `acts-as-taggable-on` from 10.0.0 to 13.0.0 + * Upgraded `devise` from 4.9.3 to 5.0.3 + * Upgraded `papertrail` from 15.1.0 to 17.0.0 + * Upgraded `pg` from 1.5.4 to 1.6.3 + * Upgraded `puma` from 5.6.8 to 8.0.0 + * Upgraded `rubyzip` from 2.3.3 to 3.2.2 + * Upgraded `sidekiq` from 6.5.12 to 7.3.10 + * Upgraded `sprockets` from 3.7.2 to 4.0.3 +* We now target Postgres 17 (was 10) and Redis 7 (was 4.2). +* Moving to `params.expect` over `require`/`permit` pattern. + + ### 1.21.2 **Species+** diff --git a/Dockerfile b/Dockerfile index 5b18e7dcc..0ca4e9c24 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,39 +1,227 @@ -# WARNING: -# This file is for development, not for production. -# For production, please refer to https://railsdiff.org/7.0.8.4/7.1.3.4#diff-466a28a0e93935ce250159682062e5a94698a3d8 -# or SUS-ORS project. - -# Dockerfile -FROM ruby:3.2.5 - -# Rails and SAPI has some additional dependencies, e.g. rake requires a JS -# runtime, so attempt to get these from apt, where possible -# socat is just for binding ports within docker, not needed for the application -RUN apt-get update && apt-get install -y --force-yes \ - libsodium-dev libgmp3-dev libssl-dev \ - libpq-dev postgresql-client \ - nodejs \ - socat \ - texlive-latex-base texlive-fonts-recommended texlive-fonts-extra texlive-latex-extra \ - ; -# NB: Postgres client from Debian is 9.4 - not sure if this is acceptable - -RUN mkdir /SAPI -WORKDIR /SAPI - -# -# Don't need to do these, as we have done this with Docker bindings -# COPY Gemfile /SAPI/Gemfile -# COPY Gemfile.lock /SAPI/Gemfile.lock -RUN gem install bundler -v 2.5.17 +ARG RUBY_VERSION=3.4.9 + +FROM ruby:$RUBY_VERSION-slim AS base + ARG NODE_VERSION=18.20.8 + ARG POSTGRES_CLIENT_MAJOR=17 + ARG TARGETARCH=amd64 + ARG DEBIAN_FRONTEND=noninteractive + + WORKDIR /rails + + ## + # Rails and SAPI has some additional dependencies, e.g. rake requires a JS + # runtime, so attempt to get these from apt, where possible + RUN apt-get update -qq \ + && apt-get install --no-install-recommends -y \ + # Needed to install Node.js from official distribution archives. + curl ca-certificates xz-utils gnupg \ + \ + # Needed by psych native extension (`yaml.h`) when bundling on slim images. + libyaml-dev \ + \ + # Use jemalloc for long-running Rails/Sidekiq processes to reduce allocator fragmentation and lower RSS during + # memory-heavy jobs such as questionnaire publish loop expansion. + libjemalloc2 \ + \ + # Needed for variout library building activities + build-essential pkg-config \ + \ + # Do not install Postgres libraries from Debian; needs a specific version: + # postgresql-client libpq + # Do not install Node.js from Debian; needs a specific version: + # nodejs + \ + ## + # Keep PostgreSQL client major version aligned with DB server major version + # to avoid the generation of SQL which is inconsistent with the DB server, e.g. + # unpinned `postgresql-client` can upgrade to 17 and generate `structure.sql` + # that includes `transaction_timeout`, which fails to load on PostgreSQL 15. + # pg_dump requires that the client library >= the server (major) version + # `postgresql-client-${POSTGRES_CLIENT_MAJOR}` requires PostgreSQL `apt` + # repository. + && install -d /usr/share/postgresql-common/pgdg \ + && curl -fsSL https://www.postgresql.org/media/keys/ACCC4CF8.asc \ + | gpg --dearmor -o /usr/share/postgresql-common/pgdg/apt.postgresql.org.gpg \ + && . /etc/os-release \ + && echo "deb [signed-by=/usr/share/postgresql-common/pgdg/apt.postgresql.org.gpg] http://apt.postgresql.org/pub/repos/apt ${VERSION_CODENAME}-pgdg main" \ + > /etc/apt/sources.list.d/pgdg.list \ + \ + && apt-get install --no-install-recommends -y --force-yes \ + # Install libvips for Active Storage preview support + libvips \ + \ + # Zip for exports + zip \ + # + libsodium-dev libgmp3-dev libssl-dev \ + \ + # socat is just for binding ports within docker, not needed for the application + socat \ + \ + # TeX is used for the generation of CITES Checklist PDFs. + texlive-latex-base texlive-fonts-recommended texlive-fonts-extra texlive-latex-extra \ + && rm -rf /var/lib/apt/lists /var/cache/apt/archives \ + ; + + RUN case $TARGETARCH \ + in \ + amd64) NODE_ARCH=x64 ;; \ + arm64) NODE_ARCH=arm64 ;; \ + *) echo "Unsupported architecture: '$TARGETARCH'"; exit 1 ;; \ + esac \ + && echo https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION-linux-$NODE_ARCH.tar.xz \ + && curl -fsSL https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION-linux-$NODE_ARCH.tar.xz \ + | tar -xJ -C /usr/local --strip-components=1 \ + ; + + # Debian installs jemalloc in an architecture-specific library directory. Resolve the actual path at build time and + # expose one stable preload path so both the Rails web process and Sidekiq use the same allocator automatically. + RUN jemalloc_path="$(find /usr/lib -name 'libjemalloc.so.2' | head -n 1)" \ + && test -n "${jemalloc_path}" \ + && ln -sf "${jemalloc_path}" /usr/local/lib/libjemalloc.so.2 \ + ; + + ENV LD_PRELOAD=/usr/local/lib/libjemalloc.so.2 + +FROM base AS build + ARG DEBIAN_FRONTEND=noninteractive + RUN apt-get update -qq \ + && apt-get install --no-install-recommends -y \ + build-essential git libyaml-dev pkg-config \ + && rm -rf /var/lib/apt/lists /var/cache/apt/archives \ + ; ## -# This happens in the entrypoint -# RUN bundle install -# -# This is done via docker bindings -# COPY . /SAPI - -ENTRYPOINT ["/SAPI/bin/docker-entrypoint-develop"] -EXPOSE 3000 -CMD ["bundle", "exec", "rails", "server", "-b", "0.0.0.0"] +# For local development, we run `bundler` during the entrypoint +FROM build AS build-develop + ## + # BUNDLE_DEPLOYMENT prevents changes to Gemfile.lock + ENV RAILS_ENV="development" \ + NODE_ENV="production" \ + BUNDLE_DEPLOYMENT="1" \ + BUNDLE_PATH="/usr/local/bundle" + + ## + # Install the same version of bundler as the one used to create the lockfile + RUN --mount=type=bind,source=Gemfile.lock,target=Gemfile.lock \ + grep -A1 '^BUNDLED WITH$' Gemfile.lock | tail -n1 | tr -d ' ' \ + | xargs -I _BUNDLER_VERSION_ gem install bundler -v _BUNDLER_VERSION_ \ + ; + +FROM build-develop AS built-develop + ## + # You may wish to do the following: + # + # export LOCAL_UID=$(id -u) + # export LOCAL_GID=$(id -g) + ARG LOCAL_UID=1000 + ARG LOCAL_GID=1000 + ARG DEFAULT_GROUPNAME=railsgroup + ARG DEFAULT_USERNAME=railsuser + + ## + # Docker UID/GID Mapping to Host: reuse or create group, then reuse or create + # user, and give them passwordless sudo. + RUN if getent group $LOCAL_GID > /dev/null; then \ + grp=$(getent group $LOCAL_GID | cut -d: -f1); \ + else \ + groupadd -g $LOCAL_GID $DEFAULT_GROUPNAME; \ + grp=$DEFAULT_GROUPNAME; \ + fi \ + && if getent passwd $LOCAL_UID > /dev/null; then \ + user=$(getent passwd $LOCAL_UID | cut -d: -f1); \ + else \ + useradd -m -u $LOCAL_UID -g "$grp" $DEFAULT_USERNAME; \ + user=$DEFAULT_USERNAME; \ + fi \ + && mkdir -p /etc/sudoers.d/ \ + && echo "${user} ALL=(ALL) NOPASSWD:ALL" > "/etc/sudoers.d/${user}"\ + && chmod 0440 "/etc/sudoers.d/${user}" \ + ; + + ## + # Ensure writable dirs for the non-root user + RUN mkdir -p /usr/local/bundle \ + ./ ./db ./log ./storage ./tmp \ + && chown -R $LOCAL_UID:$LOCAL_GID \ + /usr/local/bundle \ + ./ ./db ./log ./storage ./tmp \ + ; + + ## + # Run as the numeric UID:GID so it works regardless of whether we created + # a user + USER ${LOCAL_UID}:${LOCAL_GID} + +## +# The build step for staging +FROM build AS build-staging + ENV NODE_ENV="production" \ + RAILS_ENV="staging" \ + BUNDLE_DEPLOYMENT="1" \ + BUNDLE_PATH="/usr/local/bundle" \ + BUNDLE_WITHOUT="development" + + COPY Gemfile Gemfile.lock ./ + + RUN grep -A1 '^BUNDLED WITH$' Gemfile.lock | tail -n1 | tr -d ' ' \ + | xargs -I _BUNDLER_VERSION_ \ + gem install bundler -v _BUNDLER_VERSION_ \ + ; + + RUN bundle install \ + && rm -rf \ + ~/.bundle/ \ + "${BUNDLE_PATH}"/ruby/*/cache \ + "${BUNDLE_PATH}"/ruby/*/bundler/gems/*/.git \ + && bundle exec bootsnap precompile --gemfile \ + ; + + COPY . . + + RUN bundle exec bootsnap precompile app/ lib/ + + RUN SECRET_KEY_BASE_DUMMY=1 ./bin/rails assets:precompile + +FROM base AS built-staging + ARG DEFAULT_GROUPNAME=railsgroup + ARG DEFAULT_USERNAME=railsuser + ENV BUNDLE_PATH="/usr/local/bundle" + + # Run and own only the runtime files as a non-root user for security + RUN groupadd $DEFAULT_GROUPNAME \ + --gid 1000 --system \ + && useradd $DEFAULT_USERNAME \ + --uid 1000 --gid 1000 --create-home --shell /bin/bash \ + ; + + USER 1000:1000 + + COPY --from="build-staging" --chown=1000:1000 $BUNDLE_PATH $BUNDLE_PATH + COPY --from="build-staging" --chown=1000:1000 /rails/ /rails/ + +FROM built-staging AS deploy-staging + ENV RAILS_ENV="staging" \ + BUNDLE_WITHOUT="development" + + CMD ["tail", "-f", "/dev/null"] + +FROM built-staging AS exec-staging + ENV RAILS_ENV="staging" \ + BUNDLE_WITHOUT="development" + + ENTRYPOINT ["/rails/bin/docker-entrypoint"] + + EXPOSE 80 + + CMD ["./bin/rails", "server", "-b", "0.0.0.0"] + +FROM built-develop AS deploy-develop + CMD ["tail", "-f", "/dev/null"] + +FROM built-develop AS exec-develop + ENTRYPOINT ["/rails/bin/docker-entrypoint-develop"] + + EXPOSE 80 + + CMD ["./bin/rails", "server", "-b", "0.0.0.0"] diff --git a/Dockerfile.cap-deploy b/Dockerfile.cap-deploy deleted file mode 100644 index 377c091fc..000000000 --- a/Dockerfile.cap-deploy +++ /dev/null @@ -1,50 +0,0 @@ -# Dockerfile -FROM ruby:3.2.5 - -ENV DEBIAN_FRONTEND=noninteractive -# Rails and SAPI has some additional dependencies, e.g. rake requires a JS -# runtime, so attempt to get these from apt, where possible -RUN apt-get update && apt-get install -y --force-yes \ - # For ruby? - libsodium-dev libgmp3-dev \ - # For RVM - gnupg procps curl libssl-dev \ - # For assets local_precompile (cap deploy) - rsync nodejs \ - # gems - libpq-dev postgresql-client - -RUN mkdir /SAPI -WORKDIR /SAPI - -# https://stackoverflow.com/questions/43612927/how-to-correctly-install-rvm-in-docker -RUN gpg --keyserver keyserver.ubuntu.com --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 7D2BAF1CF37B13E2069D6956105BD0E739499BDB -RUN curl -sSL https://get.rvm.io | bash -s -RUN /bin/bash -l -c ". /etc/profile.d/rvm.sh && rvm install 3.2.5" -# RVM installed in multi-user mode. However cap assume rvm is installed in single user mode. -# Create a soft link to fake it. -RUN mkdir -p ~/.rvm/bin && ln -s /usr/local/rvm/bin/rvm ~/.rvm/bin/rvm - -COPY Gemfile /SAPI/Gemfile -COPY Gemfile.lock /SAPI/Gemfile.lock - -ENV BUNDLE_SILENCE_ROOT_WARNING=1 -RUN /bin/bash -c "source /etc/profile.d/rvm.sh \ - && gem install bundler:2.5.17 \ - && bundle" - -ENTRYPOINT ["/bin/bash", "-l"] - -########################################## -## Run the following in container -########################################## -# /bin/bash --rcfile cap-deploy-shell.sh -# [Enter your passphrase] -# root@commit:/SAPI$ CAP_BRANCH= bundle exec cap staging deploy -# -########################################## -# Alternatively, from outside docker -########################################## -# docker exec -it sapi-cap-deploy /bin/bash --rcfile cap-deploy-shell.sh -# [Enter your passphrase] -# root@commit:/SAPI# CAP_BRANCH= bundle exec cap staging deploy diff --git a/Gemfile b/Gemfile index 100a8b7da..c2959a7e6 100644 --- a/Gemfile +++ b/Gemfile @@ -1,9 +1,9 @@ source 'https://rubygems.org' -ruby '3.2.5' +ruby '3.4.9' # Bundle edge Rails instead: gem 'rails', github: 'rails/rails' -gem 'rails', '7.1.3.4' +gem 'rails', '8.1.3' # Configure Cross-Origin resource sharing gem 'rack-cors' @@ -11,22 +11,17 @@ gem 'rack-cors' # Use sqlite3 as the database for Active Record # gem 'sqlite3' # Use Puma as the app server -gem 'puma', '~> 5.0' +gem 'puma', '~> 8.0' # Use SCSS for stylesheets -# TODO: Can't upgrade sass-rails to 6.0, it raise the following error when running `RAILS_ENV=staging rake assets:precompile`. -# SassC::SyntaxError: Error: Invalid CSS after "...in-bottom:-3px;": expected "}", was ".margin-bottom:-3px" -# on line 3712:5063 of stdin -# >> ction=135,Strength=3)";_margin-bottom:-3px;.margin-bottom:-3px;}/*!Add round -# gem 'sass-rails', '>= 6' -gem 'sass-rails', '~> 5.0' +gem 'sass-rails', '~> 6' # https://stackoverflow.com/questions/55213868/rails-6-how-to-disable-webpack-and-use-sprockets-instead -gem 'sprockets', '3.7.2' +gem 'sprockets', '~> 4' gem 'sprockets-rails', require: 'sprockets/railtie' # Use Terser as compressor for JavaScript assets -gem 'terser', '~> 1.2.3' +gem 'terser', '~> 1.2.7' # Use CoffeeScript for .coffee assets and views gem 'coffee-rails', '~> 5.0' @@ -34,43 +29,44 @@ gem 'coffee-rails', '~> 5.0' # gem 'mini_racer', platforms: :ruby gem 'active_model_serializers', '0.8.4' # Deprecated -gem "active_storage_validations", "~> 2.0" +gem 'active_storage_validations', '~> 2.0' # Use redis for caching -gem "redis", "~> 4.8" +gem 'redis', '~> 4.8' # Use PostgreSQL database -gem 'pg', '~> 1.5', '>= 1.5.4' +gem 'pg', '~> 1.6', '>= 1.6.3' gem 'pg_array_parser', '~> 0.0.9' gem 'nested-hstore', '~> 0.1.2' -gem 'pg_search', '~> 2.3', '>= 2.3.6' +gem 'pg_search', '~> 2.3', '>= 2.3.7' -gem 'oj', '~> 3.16', '>= 3.16.3' # optimised JSON (picked by multi_json) +gem 'oj', '~> 3.16', '>= 3.16.17' # optimised JSON (picked by multi_json) gem 'inherited_resources', '~> 1.14' # Deprecated (https://github.com/activeadmin/inherited_resources#notice) -gem 'nokogiri', '~> 1.18', force_ruby_platform: true -gem 'mobility', '~> 1.2', '>= 1.2.9' -gem 'devise', '~> 4.9', '>= 4.9.3' +gem 'nokogiri', '~> 1.19.2', force_ruby_platform: true +gem 'mobility', '~> 1.3', '>= 1.3.2' +gem 'devise', '~> 5', '>= 5.0.3' gem 'cancancan', '~> 3.5' gem 'ahoy_matey', '~> 5.0', '>= 5.0.2' gem 'uuidtools', '~> 2.2' # For Ahoy. (https://github.com/ankane/ahoy/blob/v2.2.1/docs/Ahoy-2-Upgrade.md#activerecordstore) +gem 'csv', '~> 3.3.5' # no longer a default gem from Ruby 3.4.0 onwards gem 'wicked', '2.0.0' -gem 'groupdate', '~> 6.4' +gem 'groupdate', '~> 6.8.0' -gem 'rubyzip', '~> 2.3', '>= 2.3.2' -gem 'responders', '~> 3.1', '>= 3.1.1' # https://guides.rubyonrails.org/v4.2/upgrading_ruby_on_rails.html#responders +gem 'rubyzip', '~> 3.2.2' +gem 'responders', '~> 3.2.0' # https://guides.rubyonrails.org/v4.2/upgrading_ruby_on_rails.html#responders -gem 'sidekiq', '< 7' # TODO, latest is 7, which required Redis 6.2+, but our servers running Redis 4.0.9. -gem 'sidekiq-status', '~> 3.0', '>= 3.0.3' -gem 'sidekiq-unique-jobs', '7.1.33' # TODO: can upgrade to latest when sidekiq upgrade to 7 -gem 'sidekiq-cron', '~> 1.12' +gem 'sidekiq', '< 9' +gem 'sidekiq-status', '~> 4.0' +gem 'sidekiq-unique-jobs', '~> 8', '>= 8.1.0' +gem 'sidekiq-cron', '~> 2.3.1' -gem 'httparty', '~> 0.21.0' +gem 'httparty', '~> 0.24.2' gem 'kaminari', '~> 1.2', '>= 1.2.2' # TODO: Suggest migrate to pagy gem. -gem 'acts-as-taggable-on', '~> 10.0' # TODO: refuses to install against Rails 7.2 +gem 'acts-as-taggable-on', '~> 13.0' gem 'carrierwave', '~> 3.0', '>= 3.0.5' # PDF @@ -84,7 +80,7 @@ gem 'aws-sdk-s3', '~> 1.143', require: false # See https://github.com/sstephenson/execjs#readme for more supported runtimes # gem 'therubyracer', :platforms => :ruby -gem 'strong_migrations', '~> 1.7' +gem 'strong_migrations', '~> 1.7' # v2 drops support for pg<12 # Use Active Model has_secure_password # gem 'bcrypt', '~> 3.1.7' @@ -93,22 +89,27 @@ gem 'strong_migrations', '~> 1.7' # gem 'mini_magick', '~> 4.8' # Reduces boot times through caching; required in config/boot.rb -gem 'bootsnap', '>= 1.4.4', require: false +gem 'bootsnap', '>= 1.23.0', require: false # To use Jbuilder templates for JSON # gem 'jbuilder', '~> 2.7' +gem 'erb', '~> 6.0.2' + group :development do + ## # Adds comments at the top of models describing table column # (replaces annotate) - gem 'annotaterb', '~> 4.10.2' + gem 'annotaterb', '~> 4.22.0' + ## # Access an interactive console on exception pages or by calling 'console' anywhere in the code. - gem 'web-console', '>= 4.1.0' + gem 'web-console' + # Display performance information such as SQL time and flame graphs for each request in your browser. # Can be configured to work on production as well see: https://github.com/MiniProfiler/rack-mini-profiler/blob/master/README.md - gem 'rack-mini-profiler', '~> 2.0' - gem 'listen', '~> 3.3' + gem 'rack-mini-profiler', '~> 4.0.1' + gem 'listen', '~> 3.10.0' # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring gem 'spring' @@ -145,11 +146,15 @@ group :development do gem 'bcrypt_pbkdf', '1.1.0' gem 'ed25519', '1.2.4' - # @TODO: bring back when ruby updated to > 2.6 # gem 'net-ssh', '7.0.0.beta1' # openssl 3.0 compatibility @see https://stackoverflow.com/q/72068406/1090438 + ## + # Static analysis for security vulnerabilities [https://brakemanscanner.org/] + gem 'brakeman', require: false + + gem 'net-ssh', '7.3.2' end group :test, :development do - gem 'rspec-rails', '~> 6.1', '>= 6.1.1' + gem 'rspec-rails', '~> 8.0' gem 'rspec-collection_matchers', '~> 1.2', '>= 1.2.1' gem 'json_spec', '~> 1.1', '>= 1.1.5' gem 'database_cleaner', '~> 2.0', '>= 2.0.2' @@ -161,26 +166,26 @@ end group :test do # Adds support for Capybara system testing and selenium driver gem 'capybara', '>= 3.26' - gem 'selenium-webdriver', '>= 4.0.0.rc1' + gem 'selenium-webdriver', '~> 4.41' # Easy installation and use of web drivers to run system tests with browsers gem 'webdrivers' gem 'rails-controller-testing' - gem 'factory_bot_rails', '5.2.0' + gem 'factory_bot_rails', '~> 6.5.1' gem 'simplecov', '~> 0.22.0', require: false gem 'coveralls_reborn', '~> 0.28.0', require: false end gem 'geoip', '1.3.5' # TODO: no change logs, no idea if safe to update. Latest version is 1.6.4 @ 2018 -gem 'request_store', '~> 1.5', '>= 1.5.1' -gem 'paper_trail', '15.1.0' +gem 'request_store', '~> 1.7', '>= 1.7.0' +gem 'paper_trail', '~> 17.0.0' -gem 'dotenv-rails', '2.0.1' +gem 'dotenv-rails', '3.2.0' gem 'sitemap_generator', '~> 6.3' -gem 'appsignal', '~> 3.13.1' +gem 'appsignal', '~> 4' ### GEM for frontend ### # Remove the `jquery-rails` gem to eliminate any dependency issues that may block the upgrade process. @@ -228,6 +233,6 @@ gem 'handlebars-source', '1.0.12' # TODO: just a wrapwrapper. Any update will ch # # It might be possible to fix this if we had an nginx version which supported # the config: `passenger_preload_bundler on;` -gem 'base64', '0.1.1' - +gem 'base64', '0.2.0' +gem "benchmark", "~> 0.5.0" diff --git a/Gemfile.lock b/Gemfile.lock index d9acef088..9e2b96299 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -2,51 +2,48 @@ GEM remote: https://rubygems.org/ specs: Ascii85 (1.0.3) - actioncable (7.1.3.4) - actionpack (= 7.1.3.4) - activesupport (= 7.1.3.4) + action_text-trix (2.1.18) + railties + actioncable (8.1.3) + actionpack (= 8.1.3) + activesupport (= 8.1.3) nio4r (~> 2.0) websocket-driver (>= 0.6.1) zeitwerk (~> 2.6) - actionmailbox (7.1.3.4) - actionpack (= 7.1.3.4) - activejob (= 7.1.3.4) - activerecord (= 7.1.3.4) - activestorage (= 7.1.3.4) - activesupport (= 7.1.3.4) - mail (>= 2.7.1) - net-imap - net-pop - net-smtp - actionmailer (7.1.3.4) - actionpack (= 7.1.3.4) - actionview (= 7.1.3.4) - activejob (= 7.1.3.4) - activesupport (= 7.1.3.4) - mail (~> 2.5, >= 2.5.4) - net-imap - net-pop - net-smtp + actionmailbox (8.1.3) + actionpack (= 8.1.3) + activejob (= 8.1.3) + activerecord (= 8.1.3) + activestorage (= 8.1.3) + activesupport (= 8.1.3) + mail (>= 2.8.0) + actionmailer (8.1.3) + actionpack (= 8.1.3) + actionview (= 8.1.3) + activejob (= 8.1.3) + activesupport (= 8.1.3) + mail (>= 2.8.0) rails-dom-testing (~> 2.2) - actionpack (7.1.3.4) - actionview (= 7.1.3.4) - activesupport (= 7.1.3.4) + actionpack (8.1.3) + actionview (= 8.1.3) + activesupport (= 8.1.3) nokogiri (>= 1.8.5) - racc rack (>= 2.2.4) rack-session (>= 1.0.1) rack-test (>= 0.6.3) rails-dom-testing (~> 2.2) rails-html-sanitizer (~> 1.6) - actiontext (7.1.3.4) - actionpack (= 7.1.3.4) - activerecord (= 7.1.3.4) - activestorage (= 7.1.3.4) - activesupport (= 7.1.3.4) + useragent (~> 0.16) + actiontext (8.1.3) + action_text-trix (~> 2.1.15) + actionpack (= 8.1.3) + activerecord (= 8.1.3) + activestorage (= 8.1.3) + activesupport (= 8.1.3) globalid (>= 0.6.0) nokogiri (>= 1.8.5) - actionview (7.1.3.4) - activesupport (= 7.1.3.4) + actionview (8.1.3) + activesupport (= 8.1.3) builder (~> 3.1) erubi (~> 1.11) rails-dom-testing (~> 2.2) @@ -55,67 +52,78 @@ GEM ember-data-source (>= 1.13, < 3.0) active_model_serializers (0.8.4) activemodel (>= 3.0) - active_storage_validations (2.0.3) + active_storage_validations (2.0.4) activejob (>= 6.1.4) activemodel (>= 6.1.4) activestorage (>= 6.1.4) activesupport (>= 6.1.4) marcel (>= 1.0.3) - activejob (7.1.3.4) - activesupport (= 7.1.3.4) + activejob (8.1.3) + activesupport (= 8.1.3) globalid (>= 0.3.6) - activemodel (7.1.3.4) - activesupport (= 7.1.3.4) - activerecord (7.1.3.4) - activemodel (= 7.1.3.4) - activesupport (= 7.1.3.4) + activemodel (8.1.3) + activesupport (= 8.1.3) + activerecord (8.1.3) + activemodel (= 8.1.3) + activesupport (= 8.1.3) timeout (>= 0.4.0) - activestorage (7.1.3.4) - actionpack (= 7.1.3.4) - activejob (= 7.1.3.4) - activerecord (= 7.1.3.4) - activesupport (= 7.1.3.4) + activestorage (8.1.3) + actionpack (= 8.1.3) + activejob (= 8.1.3) + activerecord (= 8.1.3) + activesupport (= 8.1.3) marcel (~> 1.0) - activesupport (7.1.3.4) + activesupport (8.1.3) base64 bigdecimal - concurrent-ruby (~> 1.0, >= 1.0.2) + concurrent-ruby (~> 1.0, >= 1.3.1) connection_pool (>= 2.2.5) drb i18n (>= 1.6, < 2) + json + logger (>= 1.4.2) minitest (>= 5.1) - mutex_m - tzinfo (~> 2.0) - acts-as-taggable-on (10.0.0) - activerecord (>= 6.1, < 7.2) - addressable (2.8.7) - public_suffix (>= 2.0.2, < 7.0) + securerandom (>= 0.3) + tzinfo (~> 2.0, >= 2.0.5) + uri (>= 0.13.1) + acts-as-taggable-on (13.0.0) + activerecord (>= 7.1, < 8.2) + zeitwerk (>= 2.4, < 3.0) + addressable (2.9.0) + public_suffix (>= 2.0.2, < 8.0) afm (0.2.2) - ahoy_matey (5.1.0) - activesupport (>= 6.1) + ahoy_matey (5.5.0) + activesupport (>= 7.2) + cgi device_detector (>= 1) safely_block (>= 0.4) - airbrussh (1.5.2) + airbrussh (1.6.1) sshkit (>= 1.6.1, != 1.7.0) - annotaterb (4.10.2) - appsignal (3.13.1) - rack - ast (2.4.2) - aws-eventstream (1.3.0) - aws-partitions (1.961.0) - aws-sdk-core (3.201.3) + annotaterb (4.22.0) + activerecord (>= 6.0.0) + activesupport (>= 6.0.0) + appsignal (4.8.4) + logger + rack (>= 2.0.0) + ast (2.4.3) + aws-eventstream (1.4.0) + aws-partitions (1.1237.0) + aws-sdk-core (3.244.0) aws-eventstream (~> 1, >= 1.3.0) - aws-partitions (~> 1, >= 1.651.0) - aws-sigv4 (~> 1.8) + aws-partitions (~> 1, >= 1.992.0) + aws-sigv4 (~> 1.9) + base64 + bigdecimal jmespath (~> 1, >= 1.6.1) - aws-sdk-kms (1.88.0) - aws-sdk-core (~> 3, >= 3.201.0) + logger + aws-sdk-kms (1.123.0) + aws-sdk-core (~> 3, >= 3.244.0) aws-sigv4 (~> 1.5) - aws-sdk-s3 (1.157.0) - aws-sdk-core (~> 3, >= 3.201.0) + aws-sdk-s3 (1.219.0) + aws-sdk-core (~> 3, >= 3.244.0) aws-sdk-kms (~> 1) aws-sigv4 (~> 1.5) - aws-sigv4 (1.9.1) + aws-sigv4 (1.12.1) aws-eventstream (~> 1, >= 1.0.2) babel-source (5.8.35) babel-transpiler (0.7.0) @@ -124,20 +132,21 @@ GEM barber (0.12.2) ember-source (>= 1.0, < 3.1) execjs (>= 1.2, < 3) - base64 (0.1.1) - bcrypt (3.1.20) + base64 (0.2.0) + bcrypt (3.1.22) bcrypt_pbkdf (1.1.0) - bigdecimal (3.1.8) + benchmark (0.5.0) + bigdecimal (4.1.1) bindex (0.8.1) - bootsnap (1.18.3) + bootsnap (1.23.0) msgpack (~> 1.2) bootstrap-sass (2.3.2.2) sass (~> 3.2) - brpoplpush-redis_script (0.1.3) - concurrent-ruby (~> 1.0, >= 1.0.5) - redis (>= 1.0, < 6) + brakeman (8.0.4) + racc builder (3.3.0) - byebug (11.1.3) + byebug (13.0.0) + reline (>= 0.6.0) cancancan (3.6.1) capistrano (3.18.0) airbrussh (>= 1.0.0) @@ -171,14 +180,15 @@ GEM rack-test (>= 0.6.3) regexp_parser (>= 1.5, < 3.0) xpath (~> 3.2) - carrierwave (3.0.7) + carrierwave (3.1.2) activemodel (>= 6.0.0) activesupport (>= 6.0.0) addressable (~> 2.6) image_processing (~> 1.1) marcel (~> 1.0.0) ssrf_filter (~> 1.0) - chartkick (5.0.7) + cgi (0.5.1) + chartkick (5.2.1) chronic_duration (0.10.6) numerizer (~> 0.1.1) coffee-rails (5.0.0) @@ -188,34 +198,39 @@ GEM coffee-script-source execjs coffee-script-source (1.12.2) - concurrent-ruby (1.3.5) - connection_pool (2.5.0) + concurrent-ruby (1.3.6) + connection_pool (2.5.5) coveralls_reborn (0.28.0) simplecov (~> 0.22.0) term-ansicolor (~> 1.7) thor (~> 1.2) tins (~> 1.32) crass (1.0.6) - database_cleaner (2.0.2) + cronex (0.15.0) + tzinfo + unicode (>= 0.4.4.5) + csv (3.3.5) + database_cleaner (2.1.0) database_cleaner-active_record (>= 2, < 3) - database_cleaner-active_record (2.2.0) + database_cleaner-active_record (2.2.2) activerecord (>= 5.a) - database_cleaner-core (~> 2.0.0) + database_cleaner-core (~> 2.0) database_cleaner-core (2.0.1) - date (3.4.1) + date (3.5.1) device_detector (1.1.3) - devise (4.9.4) + devise (5.0.3) bcrypt (~> 3.0) orm_adapter (~> 0.1) - railties (>= 4.1.0) + railties (>= 7.0) responders warden (~> 1.2.3) - diff-lcs (1.5.1) + diff-lcs (1.6.2) docile (1.4.1) - dotenv (2.0.1) - dotenv-rails (2.0.1) - dotenv (= 2.0.1) - drb (2.2.1) + dotenv (3.2.0) + dotenv-rails (3.2.0) + dotenv (= 3.2.0) + railties (>= 6.1) + drb (2.2.3) ed25519 (1.2.4) ember-cli-assets (0.0.37) ember-data-source (1.13.0) @@ -238,59 +253,63 @@ GEM railties (>= 4.2) ember-source (1.8.0) handlebars-source (~> 1.0) - erubi (1.13.0) - et-orbi (1.2.11) + erb (6.0.2) + erubi (1.13.1) + et-orbi (1.4.0) tzinfo - execjs (2.9.1) - factory_bot (5.2.0) - activesupport (>= 4.2.0) - factory_bot_rails (5.2.0) - factory_bot (~> 5.2.0) - railties (>= 4.2.0) - ffi (1.17.0) + execjs (2.10.1) + factory_bot (6.5.6) + activesupport (>= 6.1.0) + factory_bot_rails (6.5.1) + factory_bot (~> 6.5) + railties (>= 6.1.0) + ffi (1.17.4-x86_64-linux-gnu) file_exists (0.2.0) - fugit (1.11.0) - et-orbi (~> 1, >= 1.2.11) + fugit (1.12.1) + et-orbi (~> 1.4) raabro (~> 1.4) geoip (1.3.5) - globalid (1.2.1) + globalid (1.3.0) activesupport (>= 6.1) - gon (6.4.0) + gon (6.6.0) actionpack (>= 3.0.20) i18n (>= 0.7) multi_json request_store (>= 1.0) - groupdate (6.4.0) - activesupport (>= 6.1) + groupdate (6.8.0) + activesupport (>= 7.2) handlebars-source (1.0.12) - has_scope (0.8.2) - actionpack (>= 5.2) - activesupport (>= 5.2) + has_scope (0.9.0) + actionpack (>= 7.0) + activesupport (>= 7.0) hashery (2.1.2) - httparty (0.21.0) + httparty (0.24.2) + csv mini_mime (>= 1.0.0) multi_xml (>= 0.5.2) - i18n (1.14.5) + i18n (1.14.8) concurrent-ruby (~> 1.0) - image_processing (1.13.0) - mini_magick (>= 4.9.5, < 5) + image_processing (1.14.0) + mini_magick (>= 4.9.5, < 6) ruby-vips (>= 2.0.17, < 3) inherited_resources (1.14.0) actionpack (>= 6.0) has_scope (>= 0.6) railties (>= 6.0) responders (>= 2) - io-console (0.7.2) - irb (1.14.0) + io-console (0.8.2) + irb (1.17.0) + pp (>= 0.6.0) + prism (>= 1.3.0) rdoc (>= 4.0.0) reline (>= 0.4.2) jmespath (1.6.2) - jquery-rails (4.6.0) + jquery-rails (4.6.1) rails-dom-testing (>= 1, < 3) railties (>= 4.2.0) thor (>= 0.14, < 2.0) jslint_on_rails (1.1.1) - json (2.7.2) + json (2.19.3) json_spec (1.1.5) multi_json (~> 1.0) rspec (>= 2.0, < 4.0) @@ -306,65 +325,74 @@ GEM activerecord kaminari-core (= 1.2.2) kaminari-core (1.2.2) - language_server-protocol (3.17.0.3) + language_server-protocol (3.17.0.5) launchy (2.4.3) addressable (~> 2.3) - listen (3.9.0) + lint_roller (1.1.0) + listen (3.10.0) + logger rb-fsevent (~> 0.10, >= 0.10.3) rb-inotify (~> 0.9, >= 0.9.10) - logger (1.6.0) - loofah (2.24.0) + logger (1.7.0) + loofah (2.25.1) crass (~> 1.0.2) nokogiri (>= 1.12.0) - mail (2.8.1) + mail (2.9.0) + logger mini_mime (>= 0.1.1) net-imap net-pop net-smtp marcel (1.0.4) - matrix (0.4.2) - mini_magick (4.13.2) + matrix (0.4.3) + mini_magick (5.3.1) + logger mini_mime (1.1.5) - mini_portile2 (2.8.8) - minitest (5.24.1) - mobility (1.2.9) + mini_portile2 (2.8.9) + minitest (6.0.3) + drb (~> 2.0) + prism (~> 1.5) + mize (0.6.1) + mobility (1.3.2) i18n (>= 0.6.10, < 2) request_store (~> 1.0) - msgpack (1.7.2) - multi_json (1.15.0) - multi_xml (0.6.0) - mutex_m (0.2.0) + msgpack (1.8.0) + multi_json (1.20.0) + multi_xml (0.8.1) + bigdecimal (>= 3.1, < 5) nested-hstore (0.1.2) activerecord activesupport nested_form (0.3.2) - net-imap (0.4.20) + net-imap (0.6.3) date net-protocol net-pop (0.1.2) net-protocol net-protocol (0.2.2) timeout - net-scp (4.0.0) + net-scp (4.1.0) net-ssh (>= 2.6.5, < 8.0.0) net-sftp (4.0.0) net-ssh (>= 5.0.0, < 8.0.0) - net-smtp (0.5.0) + net-smtp (0.5.1) net-protocol - net-ssh (7.2.3) - nio4r (2.7.3) - nokogiri (1.18.8) + net-ssh (7.3.2) + nio4r (2.7.5) + nokogiri (1.19.2) mini_portile2 (~> 2.8.2) racc (~> 1.4) numerizer (0.1.1) - oj (3.16.4) + oj (3.16.17) bigdecimal (>= 3.0) + ostruct (>= 0.2) orm_adapter (0.5.0) - paper_trail (15.1.0) - activerecord (>= 6.1) + ostruct (0.6.3) + paper_trail (17.0.0) + activerecord (>= 7.1) request_store (~> 1.4) - parallel (1.25.1) - parser (3.3.4.0) + parallel (2.0.1) + parser (3.3.11.1) ast (~> 2.4.1) racc pdf-reader (1.4.1) @@ -374,69 +402,75 @@ GEM ruby-rc4 ttfunk pdfkit (0.8.7.3) - pg (1.5.7) + pg (1.6.3-x86_64-linux) pg_array_parser (0.0.9) - pg_search (2.3.6) - activerecord (>= 5.2) - activesupport (>= 5.2) + pg_search (2.3.7) + activerecord (>= 6.1) + activesupport (>= 6.1) + pp (0.6.3) + prettyprint prawn (0.13.2) pdf-reader (~> 1.2) ruby-rc4 ttfunk (~> 1.0.3) - psych (5.1.2) + prettyprint (0.2.0) + prism (1.9.0) + psych (5.3.1) + date stringio - public_suffix (6.0.1) - puma (5.6.8) + public_suffix (7.0.5) + puma (8.0.0) nio4r (~> 2.0) raabro (1.4.0) racc (1.8.1) - rack (2.2.14) + rack (2.2.23) rack-cors (2.0.2) rack (>= 2.0.0) - rack-mini-profiler (2.3.4) + rack-mini-profiler (4.0.1) rack (>= 1.2.0) rack-session (1.0.2) rack (< 3) - rack-test (2.1.0) + rack-test (2.2.0) rack (>= 1.3) - rackup (1.0.0) + rackup (1.0.1) rack (< 3) webrick - rails (7.1.3.4) - actioncable (= 7.1.3.4) - actionmailbox (= 7.1.3.4) - actionmailer (= 7.1.3.4) - actionpack (= 7.1.3.4) - actiontext (= 7.1.3.4) - actionview (= 7.1.3.4) - activejob (= 7.1.3.4) - activemodel (= 7.1.3.4) - activerecord (= 7.1.3.4) - activestorage (= 7.1.3.4) - activesupport (= 7.1.3.4) + rails (8.1.3) + actioncable (= 8.1.3) + actionmailbox (= 8.1.3) + actionmailer (= 8.1.3) + actionpack (= 8.1.3) + actiontext (= 8.1.3) + actionview (= 8.1.3) + activejob (= 8.1.3) + activemodel (= 8.1.3) + activerecord (= 8.1.3) + activestorage (= 8.1.3) + activesupport (= 8.1.3) bundler (>= 1.15.0) - railties (= 7.1.3.4) + railties (= 8.1.3) rails-controller-testing (1.0.5) actionpack (>= 5.0.1.rc1) actionview (>= 5.0.1.rc1) activesupport (>= 5.0.1.rc1) - rails-dom-testing (2.2.0) + rails-dom-testing (2.3.0) activesupport (>= 5.0.0) minitest nokogiri (>= 1.6) - rails-html-sanitizer (1.6.1) - loofah (~> 2.21) + rails-html-sanitizer (1.7.0) + loofah (~> 2.25) nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0) - railties (7.1.3.4) - actionpack (= 7.1.3.4) - activesupport (= 7.1.3.4) - irb + railties (8.1.3) + actionpack (= 8.1.3) + activesupport (= 8.1.3) + irb (~> 1.13) rackup (>= 1.0.0) rake (>= 12.2) thor (~> 1.0, >= 1.2.2) + tsort (>= 0.2) zeitwerk (~> 2.6) rainbow (3.1.1) - rake (13.2.1) + rake (13.3.1) rb-fsevent (0.11.2) rb-inotify (0.11.1) ffi (~> 1.0) @@ -444,198 +478,229 @@ GEM ffi rbnacl-libsodium (1.0.16) rbnacl (>= 3.0.1) - rdoc (6.7.0) + rdoc (7.2.0) + erb psych (>= 4.0.0) + tsort + readline (0.0.4) + reline redis (4.8.1) - regexp_parser (2.9.2) - reline (0.5.9) + redis-client (0.28.0) + connection_pool + regexp_parser (2.12.0) + reline (0.6.3) io-console (~> 0.5) request_store (1.7.0) rack (>= 1.4) - responders (3.1.1) - actionpack (>= 5.2) - railties (>= 5.2) - rexml (3.3.9) - rspec (3.13.0) + responders (3.2.0) + actionpack (>= 7.0) + railties (>= 7.0) + rexml (3.4.4) + rspec (3.13.2) rspec-core (~> 3.13.0) rspec-expectations (~> 3.13.0) rspec-mocks (~> 3.13.0) rspec-collection_matchers (1.2.1) rspec-expectations (>= 2.99.0.beta1) - rspec-core (3.13.0) + rspec-core (3.13.6) rspec-support (~> 3.13.0) - rspec-expectations (3.13.1) + rspec-expectations (3.13.5) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.13.0) - rspec-mocks (3.13.1) + rspec-mocks (3.13.8) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.13.0) - rspec-rails (6.1.3) - actionpack (>= 6.1) - activesupport (>= 6.1) - railties (>= 6.1) - rspec-core (~> 3.13) - rspec-expectations (~> 3.13) - rspec-mocks (~> 3.13) - rspec-support (~> 3.13) - rspec-support (3.13.1) - rubocop (1.65.1) + rspec-rails (8.0.4) + actionpack (>= 7.2) + activesupport (>= 7.2) + railties (>= 7.2) + rspec-core (>= 3.13.0, < 5.0.0) + rspec-expectations (>= 3.13.0, < 5.0.0) + rspec-mocks (>= 3.13.0, < 5.0.0) + rspec-support (>= 3.13.0, < 5.0.0) + rspec-support (3.13.7) + rubocop (1.86.1) json (~> 2.3) - language_server-protocol (>= 3.17.0) - parallel (~> 1.10) + language_server-protocol (~> 3.17.0.2) + lint_roller (~> 1.1.0) + parallel (>= 1.10) parser (>= 3.3.0.2) rainbow (>= 2.2.2, < 4.0) - regexp_parser (>= 2.4, < 3.0) - rexml (>= 3.2.5, < 4.0) - rubocop-ast (>= 1.31.1, < 2.0) + regexp_parser (>= 2.9.3, < 3.0) + rubocop-ast (>= 1.49.0, < 2.0) ruby-progressbar (~> 1.7) - unicode-display_width (>= 2.4.0, < 3.0) - rubocop-ast (1.31.3) - parser (>= 3.3.1.0) - rubocop-capybara (2.21.0) - rubocop (~> 1.41) - rubocop-factory_bot (2.26.1) - rubocop (~> 1.61) - rubocop-minitest (0.35.1) - rubocop (>= 1.61, < 2.0) - rubocop-ast (>= 1.31.1, < 2.0) - rubocop-performance (1.21.1) - rubocop (>= 1.48.1, < 2.0) - rubocop-ast (>= 1.31.1, < 2.0) - rubocop-rails (2.25.1) + unicode-display_width (>= 2.4.0, < 4.0) + rubocop-ast (1.49.1) + parser (>= 3.3.7.2) + prism (~> 1.7) + rubocop-capybara (2.22.1) + lint_roller (~> 1.1) + rubocop (~> 1.72, >= 1.72.1) + rubocop-factory_bot (2.28.0) + lint_roller (~> 1.1) + rubocop (~> 1.72, >= 1.72.1) + rubocop-performance (1.26.1) + lint_roller (~> 1.1) + rubocop (>= 1.75.0, < 2.0) + rubocop-ast (>= 1.47.1, < 2.0) + rubocop-rails (2.34.3) activesupport (>= 4.2.0) + lint_roller (~> 1.1) rack (>= 1.1) - rubocop (>= 1.33.0, < 2.0) - rubocop-ast (>= 1.31.1, < 2.0) - rubocop-rails-omakase (1.0.0) - rubocop - rubocop-minitest - rubocop-performance - rubocop-rails - rubocop-rspec (3.0.3) - rubocop (~> 1.61) - rubocop-rspec_rails (2.30.0) - rubocop (~> 1.61) - rubocop-rspec (~> 3, >= 3.0.1) + rubocop (>= 1.75.0, < 2.0) + rubocop-ast (>= 1.44.0, < 2.0) + rubocop-rails-omakase (1.1.0) + rubocop (>= 1.72) + rubocop-performance (>= 1.24) + rubocop-rails (>= 2.30) + rubocop-rspec (3.9.0) + lint_roller (~> 1.1) + rubocop (~> 1.81) + rubocop-rspec_rails (2.32.0) + lint_roller (~> 1.1) + rubocop (~> 1.72, >= 1.72.1) + rubocop-rspec (~> 3.5) ruby-progressbar (1.13.0) ruby-rc4 (0.1.5) - ruby-vips (2.2.2) + ruby-vips (2.3.0) ffi (~> 1.12) logger - rubyzip (2.3.2) - safely_block (0.4.0) + rubyzip (3.2.2) + safely_block (1.0.0) sass (3.4.25) - sass-rails (5.1.0) - railties (>= 5.2.0) - sass (~> 3.1) - sprockets (>= 2.8, < 4.0) - sprockets-rails (>= 2.0, < 4.0) - tilt (>= 1.1, < 3) - selenium-webdriver (4.16.0) + sass-rails (6.0.0) + sassc-rails (~> 2.1, >= 2.1.1) + sassc (2.4.0) + ffi (~> 1.9) + sassc-rails (2.1.2) + railties (>= 4.0.0) + sassc (>= 2.0) + sprockets (> 3.0) + sprockets-rails + tilt + securerandom (0.4.1) + selenium-webdriver (4.43.0) + base64 (~> 0.2) + logger (~> 1.4) rexml (~> 3.2, >= 3.2.5) - rubyzip (>= 1.2.2, < 3.0) + rubyzip (>= 1.2.2, < 4.0) websocket (~> 1.0) - sidekiq (6.5.12) - connection_pool (>= 2.2.5, < 3) - rack (~> 2.0) - redis (>= 4.5.0, < 5) - sidekiq-cron (1.12.0) - fugit (~> 1.8) + sidekiq (7.3.10) + base64 + connection_pool (>= 2.3.0, < 3) + logger + rack (>= 2.2.4, < 3.3) + redis-client (>= 0.23.0, < 1) + sidekiq-cron (2.3.1) + cronex (>= 0.13.0) + fugit (~> 1.8, >= 1.11.1) globalid (>= 1.0.1) - sidekiq (>= 6) - sidekiq-status (3.0.3) + sidekiq (>= 6.5.0) + sidekiq-status (4.0.0) + base64 chronic_duration - sidekiq (>= 6.0, < 8) - sidekiq-unique-jobs (7.1.33) - brpoplpush-redis_script (> 0.1.1, <= 2.0.0) + logger + sidekiq (>= 7, < 9) + sidekiq-unique-jobs (8.1.0) concurrent-ruby (~> 1.0, >= 1.0.5) - redis (< 5.0) - sidekiq (>= 5.0, < 7.0) - thor (>= 0.20, < 3.0) + sidekiq (>= 7.0.0, < 9.0.0) + thor (>= 1.0, < 3.0) simplecov (0.22.0) docile (~> 1.1) simplecov-html (~> 0.11) simplecov_json_formatter (~> 0.1) - simplecov-html (0.12.3) + simplecov-html (0.13.2) simplecov_json_formatter (0.1.4) sitemap_generator (6.3.0) builder (~> 3.0) slackistrano (0.1.9) capistrano (>= 3.0.1) json - spring (4.2.1) - sprockets (3.7.2) + spring (4.4.2) + sprockets (4.0.3) concurrent-ruby (~> 1.0) rack (> 1, < 3) sprockets-rails (3.5.2) actionpack (>= 6.1) activesupport (>= 6.1) sprockets (>= 3.0.0) - sshkit (1.23.0) + sshkit (1.25.0) base64 + logger net-scp (>= 1.1.2) net-sftp (>= 2.1.2) net-ssh (>= 2.8.0) - ssrf_filter (1.1.2) - stringio (3.1.1) + ostruct + ssrf_filter (1.5.0) + stringio (3.2.0) strong_migrations (1.8.0) activerecord (>= 5.2) susy (2.2.14) sass (>= 3.3.0, < 3.5) sync (0.5.0) - term-ansicolor (1.11.1) - tins (~> 1.0) - terser (1.2.3) + term-ansicolor (1.11.3) + tins (~> 1) + terser (1.2.7) execjs (>= 0.3.0, < 3) - thor (1.3.2) - tilt (2.4.0) - timeout (0.4.3) - tins (1.33.0) + thor (1.5.0) + tilt (2.7.0) + timeout (0.6.1) + tins (1.52.0) bigdecimal + mize (~> 0.6) + readline sync + tsort (0.2.0) ttfunk (1.0.3) tzinfo (2.0.6) concurrent-ruby (~> 1.0) - unicode-display_width (2.5.0) + unicode (0.4.4.5) + unicode-display_width (3.2.0) + unicode-emoji (~> 4.1) + unicode-emoji (4.2.0) + uri (1.1.1) + useragent (0.16.11) uuidtools (2.2.0) warden (1.2.9) rack (>= 2.0.9) - web-console (4.2.1) - actionview (>= 6.0.0) - activemodel (>= 6.0.0) + web-console (4.3.0) + actionview (>= 8.0.0) bindex (>= 0.4.0) - railties (>= 6.0.0) + railties (>= 8.0.0) webdrivers (5.2.0) nokogiri (~> 1.6) rubyzip (>= 1.3.0) selenium-webdriver (~> 4.0) - webrick (1.8.2) + webrick (1.9.2) websocket (1.2.11) - websocket-driver (0.7.6) + websocket-driver (0.8.0) + base64 websocket-extensions (>= 0.1.0) websocket-extensions (0.1.5) wicked (2.0.0) railties (>= 3.0.7) - wkhtmltopdf-binary (0.12.6.7) + wkhtmltopdf-binary (0.12.6.10) xpath (3.2.0) nokogiri (~> 1.8) - zeitwerk (2.6.17) + zeitwerk (2.7.5) PLATFORMS - ruby + x86_64-linux-gnu DEPENDENCIES active_model_serializers (= 0.8.4) active_storage_validations (~> 2.0) - acts-as-taggable-on (~> 10.0) + acts-as-taggable-on (~> 13.0) ahoy_matey (~> 5.0, >= 5.0.2) - annotaterb (~> 4.10.2) - appsignal (~> 3.13.1) + annotaterb (~> 4.22.0) + appsignal (~> 4) aws-sdk-s3 (~> 1.143) - base64 (= 0.1.1) + base64 (= 0.2.0) bcrypt_pbkdf (= 1.1.0) - bootsnap (>= 1.4.4) + benchmark (~> 0.5.0) + bootsnap (>= 1.23.0) bootstrap-sass (= 2.3.2.2) + brakeman byebug cancancan (~> 3.5) capistrano (= 3.18.0) @@ -651,49 +716,52 @@ DEPENDENCIES chartkick (~> 5.0, >= 5.0.5) coffee-rails (~> 5.0) coveralls_reborn (~> 0.28.0) + csv (~> 3.3.5) database_cleaner (~> 2.0, >= 2.0.2) - devise (~> 4.9, >= 4.9.3) - dotenv-rails (= 2.0.1) + devise (~> 5, >= 5.0.3) + dotenv-rails (= 3.2.0) ed25519 (= 1.2.4) ember-data-source (= 1.13.0) ember-rails (~> 0.21.0) ember-source (= 1.8.0) - factory_bot_rails (= 5.2.0) + erb (~> 6.0.2) + factory_bot_rails (~> 6.5.1) file_exists (~> 0.2.0) geoip (= 1.3.5) gon (~> 6.4) - groupdate (~> 6.4) + groupdate (~> 6.8.0) handlebars-source (= 1.0.12) - httparty (~> 0.21.0) + httparty (~> 0.24.2) inherited_resources (~> 1.14) jslint_on_rails (= 1.1.1) json_spec (~> 1.1, >= 1.1.5) kaminari (~> 1.2, >= 1.2.2) launchy (= 2.4.3) - listen (~> 3.3) - mobility (~> 1.2, >= 1.2.9) + listen (~> 3.10.0) + mobility (~> 1.3, >= 1.3.2) nested-hstore (~> 0.1.2) nested_form (~> 0.3.2) - nokogiri (~> 1.18) - oj (~> 3.16, >= 3.16.3) - paper_trail (= 15.1.0) + net-ssh (= 7.3.2) + nokogiri (~> 1.19.2) + oj (~> 3.16, >= 3.16.17) + paper_trail (~> 17.0.0) pdfkit (~> 0.8.7.3) - pg (~> 1.5, >= 1.5.4) + pg (~> 1.6, >= 1.6.3) pg_array_parser (~> 0.0.9) - pg_search (~> 2.3, >= 2.3.6) + pg_search (~> 2.3, >= 2.3.7) prawn (= 0.13.2) - puma (~> 5.0) + puma (~> 8.0) rack-cors - rack-mini-profiler (~> 2.0) - rails (= 7.1.3.4) + rack-mini-profiler (~> 4.0.1) + rails (= 8.1.3) rails-controller-testing rbnacl (= 4.0.2) rbnacl-libsodium (= 1.0.16) redis (~> 4.8) - request_store (~> 1.5, >= 1.5.1) - responders (~> 3.1, >= 3.1.1) + request_store (~> 1.7, >= 1.7.0) + responders (~> 3.2.0) rspec-collection_matchers (~> 1.2, >= 1.2.1) - rspec-rails (~> 6.1, >= 6.1.1) + rspec-rails (~> 8.0) rubocop rubocop-capybara rubocop-factory_bot @@ -701,30 +769,30 @@ DEPENDENCIES rubocop-rails-omakase rubocop-rspec rubocop-rspec_rails - rubyzip (~> 2.3, >= 2.3.2) - sass-rails (~> 5.0) - selenium-webdriver (>= 4.0.0.rc1) - sidekiq (< 7) - sidekiq-cron (~> 1.12) - sidekiq-status (~> 3.0, >= 3.0.3) - sidekiq-unique-jobs (= 7.1.33) + rubyzip (~> 3.2.2) + sass-rails (~> 6) + selenium-webdriver (~> 4.41) + sidekiq (< 9) + sidekiq-cron (~> 2.3.1) + sidekiq-status (~> 4.0) + sidekiq-unique-jobs (~> 8, >= 8.1.0) simplecov (~> 0.22.0) sitemap_generator (~> 6.3) slackistrano (= 0.1.9) spring - sprockets (= 3.7.2) + sprockets (~> 4) sprockets-rails strong_migrations (~> 1.7) susy (~> 2.2, >= 2.2.14) - terser (~> 1.2.3) + terser (~> 1.2.7) uuidtools (~> 2.2) - web-console (>= 4.1.0) + web-console webdrivers wicked (= 2.0.0) wkhtmltopdf-binary (~> 0.12.6.6) RUBY VERSION - ruby 3.2.5p208 + ruby 3.4.9p82 BUNDLED WITH - 2.5.17 + 4.0.10 diff --git a/app/assets/stylesheets/mobile/mobile.scss b/app/assets/stylesheets/mobile/mobile.scss index b4a7ddb10..efff1051d 100644 --- a/app/assets/stylesheets/mobile/mobile.scss +++ b/app/assets/stylesheets/mobile/mobile.scss @@ -1,6 +1,4 @@ // Settings (mobile-first) -@import url('https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;600;700&display=swap'); - $navy: #253848; $black: #2D2D2D; $medium-grey: #dddddd; diff --git a/app/assets/stylesheets/species/all.scss b/app/assets/stylesheets/species/all.scss index b28ec7f5e..a18e844fb 100755 --- a/app/assets/stylesheets/species/all.scss +++ b/app/assets/stylesheets/species/all.scss @@ -3,7 +3,6 @@ License: none (public domain) */ -@import url(https://fonts.googleapis.com/css?family=Open+Sans:400italic,700italic,400,600,700); @import './variables'; html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, canvas, details, embed, figure, figcaption, footer, header, hgroup, menu, nav, output, ruby, section, summary, time, mark, audio, video { diff --git a/app/channels/application_cable/channel.rb b/app/channels/application_cable/channel.rb deleted file mode 100644 index d67269728..000000000 --- a/app/channels/application_cable/channel.rb +++ /dev/null @@ -1,4 +0,0 @@ -module ApplicationCable - class Channel < ActionCable::Channel::Base - end -end diff --git a/app/channels/application_cable/connection.rb b/app/channels/application_cable/connection.rb deleted file mode 100644 index 0ff5442f4..000000000 --- a/app/channels/application_cable/connection.rb +++ /dev/null @@ -1,4 +0,0 @@ -module ApplicationCable - class Connection < ActionCable::Connection::Base - end -end diff --git a/app/controllers/admin/change_types_controller.rb b/app/controllers/admin/change_types_controller.rb index 908262b51..41312061b 100644 --- a/app/controllers/admin/change_types_controller.rb +++ b/app/controllers/admin/change_types_controller.rb @@ -20,9 +20,11 @@ def load_associations private def change_type_params - params.require(:change_type).permit( - # attributes were in model `attr_accessible`. - :name, :display_name_en, :display_name_es, :display_name_fr, :designation_id + params.expect( + change_type: [ + :name, :designation_id, + :display_name_en, :display_name_es, :display_name_fr + ] ) end end diff --git a/app/controllers/admin/cites_acs_controller.rb b/app/controllers/admin/cites_acs_controller.rb index 7a12267be..8051bf366 100644 --- a/app/controllers/admin/cites_acs_controller.rb +++ b/app/controllers/admin/cites_acs_controller.rb @@ -21,11 +21,14 @@ def collection private def cites_ac_params - params.require(:cites_ac).permit( - # attributes were in model `attr_accessible`. - :is_current, :name, :designation_id, :description, :extended_description, - :url, :private_url, :multilingual_url, :published_at, :effective_at, :is_current, :end_date, - :created_by_id, :updated_by_id + params.expect( + cites_ac: [ + :is_current, :name, :designation_id, + :description, :extended_description, + :url, :private_url, :multilingual_url, + :published_at, :effective_at, :is_current, :end_date, + :created_by_id, :updated_by_id + ] ) end end diff --git a/app/controllers/admin/cites_captivity_processes_controller.rb b/app/controllers/admin/cites_captivity_processes_controller.rb index 512650bb2..37d237884 100644 --- a/app/controllers/admin/cites_captivity_processes_controller.rb +++ b/app/controllers/admin/cites_captivity_processes_controller.rb @@ -62,11 +62,13 @@ def collection private def cites_captivity_process_params - params.require(:cites_captivity_process).permit( - # attributes were in model `attr_accessible`. - :start_event_id, :geo_entity_id, :resolution, :start_date, - :taxon_concept_id, :notes, :status, :document, :document_title, - :created_by_id, :updated_by_id + params.expect( + cites_captivity_process: [ + :start_event_id, :geo_entity_id, :resolution, :start_date, + :taxon_concept_id, :notes, :status, + :document, :document_title, + :created_by_id, :updated_by_id + ] ) end end diff --git a/app/controllers/admin/cites_cops_controller.rb b/app/controllers/admin/cites_cops_controller.rb index af100aec7..c011a1a51 100644 --- a/app/controllers/admin/cites_cops_controller.rb +++ b/app/controllers/admin/cites_cops_controller.rb @@ -20,11 +20,14 @@ def collection private def cites_cop_params - params.require(:cites_cop).permit( - # attributes were in model `attr_accessible`. - :is_current, :name, :designation_id, :description, :extended_description, - :url, :private_url, :multilingual_url, :published_at, :effective_at, :is_current, :end_date, - :created_by_id, :updated_by_id + params.expect( + cites_cop: [ + :is_current, :name, :designation_id, + :description, :extended_description, + :url, :private_url, :multilingual_url, :published_at, + :effective_at, :is_current, :end_date, + :created_by_id, :updated_by_id + ] ) end end diff --git a/app/controllers/admin/cites_extraordinary_meetings_controller.rb b/app/controllers/admin/cites_extraordinary_meetings_controller.rb index a9145e9f1..5e1318740 100644 --- a/app/controllers/admin/cites_extraordinary_meetings_controller.rb +++ b/app/controllers/admin/cites_extraordinary_meetings_controller.rb @@ -21,11 +21,14 @@ def collection private def cites_extraordinary_meeting_params - params.require(:cites_extraordinary_meeting).permit( - # attributes were in model `attr_accessible`. - :is_current, :name, :designation_id, :description, :extended_description, - :url, :private_url, :multilingual_url, :published_at, :effective_at, :is_current, :end_date, - :created_by_id, :updated_by_id + params.expect( + cites_extraordinary_meeting: [ + :is_current, :name, :designation_id, + :description, :extended_description, + :url, :private_url, :multilingual_url, + :published_at, :effective_at, :is_current, :end_date, + :created_by_id, :updated_by_id + ] ) end end diff --git a/app/controllers/admin/cites_hash_annotations_controller.rb b/app/controllers/admin/cites_hash_annotations_controller.rb index 01e3b0863..967019d6b 100644 --- a/app/controllers/admin/cites_hash_annotations_controller.rb +++ b/app/controllers/admin/cites_hash_annotations_controller.rb @@ -12,11 +12,14 @@ def load_associations private def cites_hash_annotation_params - params.require(:annotation).permit( - # attributes were in model `attr_accessible`. - :listing_change_id, :symbol, :parent_symbol, :short_note_en, - :full_note_en, :short_note_fr, :full_note_fr, :short_note_es, :full_note_es, - :display_in_index, :display_in_footnote, :event_id + params.expect( + annotation: [ + :listing_change_id, :symbol, :parent_symbol, + :short_note_en, :full_note_en, + :short_note_fr, :full_note_fr, + :short_note_es, :full_note_es, + :display_in_index, :display_in_footnote, :event_id + ] ) end end diff --git a/app/controllers/admin/cites_pcs_controller.rb b/app/controllers/admin/cites_pcs_controller.rb index 9cd047f88..4f39e2cec 100644 --- a/app/controllers/admin/cites_pcs_controller.rb +++ b/app/controllers/admin/cites_pcs_controller.rb @@ -21,11 +21,14 @@ def collection private def cites_pc_params - params.require(:cites_pc).permit( - # attributes were in model `attr_accessible`. - :is_current, :name, :designation_id, :description, :extended_description, - :url, :private_url, :multilingual_url, :published_at, :effective_at, :is_current, :end_date, - :created_by_id, :updated_by_id + params.expect( + cites_pc: [ + :is_current, :name, :designation_id, + :description, :extended_description, + :url, :private_url, :multilingual_url, + :published_at, :effective_at, :is_current, :end_date, + :created_by_id, :updated_by_id + ] ) end end diff --git a/app/controllers/admin/cites_suspension_notifications_controller.rb b/app/controllers/admin/cites_suspension_notifications_controller.rb index 45b7d570b..50d12ff63 100644 --- a/app/controllers/admin/cites_suspension_notifications_controller.rb +++ b/app/controllers/admin/cites_suspension_notifications_controller.rb @@ -20,11 +20,14 @@ def collection private def cites_suspension_notification_params - params.require(:cites_suspension_notification).permit( - # attributes were in model `attr_accessible`. - :subtype, :new_subtype, :end_date, :name, :designation_id, :description, :extended_description, - :url, :private_url, :multilingual_url, :published_at, :effective_at, :is_current, :end_date, - :created_by_id, :updated_by_id + params.expect( + cites_suspension_notification: [ + :subtype, :new_subtype, :end_date, :name, :designation_id, + :description, :extended_description, + :url, :private_url, :multilingual_url, + :published_at, :effective_at, :is_current, :end_date, + :created_by_id, :updated_by_id + ] ) end end diff --git a/app/controllers/admin/cites_suspensions_controller.rb b/app/controllers/admin/cites_suspensions_controller.rb index 31fad98a3..514c86517 100644 --- a/app/controllers/admin/cites_suspensions_controller.rb +++ b/app/controllers/admin/cites_suspensions_controller.rb @@ -67,21 +67,22 @@ def collection private def cites_suspension_params - params.require(:cites_suspension).permit( - # attributes were in model `attr_accessible`. - :start_notification_id, :end_notification_id, - :applies_to_import, :end_date, :geo_entity_id, :is_current, - :notes, :publication_date, :quota, :type, - :start_date, :unit_id, :internal_notes, - :nomenclature_note_en, :nomenclature_note_es, :nomenclature_note_fr, - :created_by_id, :updated_by_id, :url, - :taxon_concept_id, - cites_suspension_confirmations_attributes: [ - :id, :cites_suspension_notification_id, :_destroy - ], - purpose_ids: [], - term_ids: [], - source_ids: [] + params.expect( + cites_suspension: [ + :start_notification_id, :end_notification_id, + :applies_to_import, :end_date, :geo_entity_id, :is_current, + :notes, :publication_date, :quota, :type, + :start_date, :unit_id, :internal_notes, + :nomenclature_note_en, :nomenclature_note_es, :nomenclature_note_fr, + :created_by_id, :updated_by_id, :url, + :taxon_concept_id, + cites_suspension_confirmations_attributes: [ + [ :id, :_destroy, :cites_suspension_notification_id ] + ], + purpose_ids: [], + term_ids: [], + source_ids: [] + ] ) end end diff --git a/app/controllers/admin/cites_tcs_controller.rb b/app/controllers/admin/cites_tcs_controller.rb index f15f37e2c..334c16a4f 100644 --- a/app/controllers/admin/cites_tcs_controller.rb +++ b/app/controllers/admin/cites_tcs_controller.rb @@ -21,11 +21,14 @@ def collection private def cites_tc_params - params.require(:cites_tc).permit( - # attributes were in model `attr_accessible`. - :is_current, :name, :designation_id, :description, :extended_description, - :url, :private_url, :multilingual_url, :published_at, :effective_at, :is_current, :end_date, - :created_by_id, :updated_by_id + params.expect( + cites_tc: [ + :is_current, :name, :designation_id, + :description, :extended_description, + :url, :private_url, :multilingual_url, + :published_at, :effective_at, :is_current, :end_date, + :created_by_id, :updated_by_id + ] ) end end diff --git a/app/controllers/admin/designations_controller.rb b/app/controllers/admin/designations_controller.rb index f978c4e1f..fdad4aa55 100644 --- a/app/controllers/admin/designations_controller.rb +++ b/app/controllers/admin/designations_controller.rb @@ -31,9 +31,6 @@ def load_associations private def designation_params - params.require(:designation).permit( - # attributes were in model `attr_accessible`. - :name, :taxonomy_id - ) + params.expect(designation: [ :name, :taxonomy_id ]) end end diff --git a/app/controllers/admin/distributions_controller.rb b/app/controllers/admin/distributions_controller.rb index fead5828b..0248e51d6 100644 --- a/app/controllers/admin/distributions_controller.rb +++ b/app/controllers/admin/distributions_controller.rb @@ -80,11 +80,15 @@ def load_distributions private def distribution_params - params.require(:distribution).permit( - # attributes were in model `attr_accessible`. - :geo_entity_id, :taxon_concept_id, :internal_notes, :created_by_id, :updated_by_id, - references_attributes: [ :citation, :created_by_id, :updated_by_id, :id, :_destroy ], - tag_list: [] + params.expect( + distribution: [ + :geo_entity_id, :taxon_concept_id, :internal_notes, + :created_by_id, :updated_by_id, + references_attributes: [ + [ :id, :_destroy, :citation, :created_by_id, :updated_by_id ] + ], + tag_list: [] + ] ) end end diff --git a/app/controllers/admin/document_batches_controller.rb b/app/controllers/admin/document_batches_controller.rb index f8af456a2..e87759ac4 100644 --- a/app/controllers/admin/document_batches_controller.rb +++ b/app/controllers/admin/document_batches_controller.rb @@ -15,6 +15,7 @@ def new def create @event = Event.find(params[:event_id]) if params[:event_id] @document_batch = DocumentBatch.new(document_batch_params) + if @document_batch.save if @event redirect_to admin_event_documents_url(@event) @@ -42,11 +43,24 @@ def load_associations end def document_batch_params - params.require(:document_batch).permit( - :event_id, :date, :language_id, :is_public, - documents_attributes: [ - :type - ], files: [] + params.expect( + document_batch: [ + :event_id, + :date, + :language_id, + :is_public, + documents_attributes: [ [ :type ] ], + files: [] + ] ) + + # TODO: for some reason the following won't work + # params.expect( + # document_batch: [ + # :event_id, :date, :language_id, :is_public, + # files: [ [] ], + # documents_attributes: [ [ :type ] ], + # ] + # ) end end diff --git a/app/controllers/admin/documents_controller.rb b/app/controllers/admin/documents_controller.rb index 639ecb04d..e9b996cf8 100644 --- a/app/controllers/admin/documents_controller.rb +++ b/app/controllers/admin/documents_controller.rb @@ -163,23 +163,29 @@ def redirect_url private def document_params - params.require(:document).permit( - # attributes were in model `attr_accessible`. - :event_id, :file, :date, :type, :title, :is_public, - :language_id, - :sort_index, :discussion_id, :discussion_sort_index, - :primary_language_document_id, - :designation_id, - citations_attributes: [ - :id, :_destroy, :document_id, :stringy_taxon_concept_ids, - geo_entity_ids: [] - ], - proposal_details_attributes: [ - :id, :_destroy, :document_id, :proposal_nature, :proposal_outcome_id, - :representation, :proposal_number - ], - review_details_attributes: [ - :id, :_destroy, :document_id, :review_phase_id, :process_stage_id, :recommended_category + params.expect( + document: [ + :event_id, :file, :date, :type, :title, :is_public, + :language_id, + :sort_index, :discussion_id, :discussion_sort_index, + :primary_language_document_id, + :designation_id, + citations_attributes: [ + [ + :id, :_destroy, :document_id, + :stringy_taxon_concept_ids, + geo_entity_ids: [] + ] + ], + proposal_details_attributes: [ + :id, :_destroy, :document_id, + :proposal_nature, :proposal_outcome_id, + :representation, :proposal_number + ], + review_details_attributes: [ + :id, :_destroy, :document_id, + :review_phase_id, :process_stage_id, :recommended_category + ] ] ) end diff --git a/app/controllers/admin/ec_srgs_controller.rb b/app/controllers/admin/ec_srgs_controller.rb index c37d005f4..e2490110f 100644 --- a/app/controllers/admin/ec_srgs_controller.rb +++ b/app/controllers/admin/ec_srgs_controller.rb @@ -19,11 +19,14 @@ def collection private def ec_srg_params - params.require(:ec_srg).permit( - # attributes were in model `attr_accessible`. - :is_current, :name, :designation_id, :description, :extended_description, - :url, :private_url, :multilingual_url, :published_at, :effective_at, :is_current, :end_date, - :created_by_id, :updated_by_id + params.expect( + ec_srg: [ + :is_current, :name, :designation_id, + :description, :extended_description, + :url, :private_url, :multilingual_url, + :published_at, :effective_at, :is_current, :end_date, + :created_by_id, :updated_by_id + ] ) end end diff --git a/app/controllers/admin/eu_council_regulations_controller.rb b/app/controllers/admin/eu_council_regulations_controller.rb index d20f5e25c..3500210a1 100644 --- a/app/controllers/admin/eu_council_regulations_controller.rb +++ b/app/controllers/admin/eu_council_regulations_controller.rb @@ -22,11 +22,13 @@ def list_template private def eu_council_regulation_params - params.require(:eu_council_regulation).permit( - # attributes were in model `attr_accessible`. - :name, :designation_id, :description, :extended_description, - :url, :private_url, :multilingual_url, :published_at, :effective_at, :is_current, :end_date, - :created_by_id, :updated_by_id + params.expect( + eu_council_regulation: [ + :name, :designation_id, :description, :extended_description, + :url, :private_url, :multilingual_url, + :published_at, :effective_at, :is_current, :end_date, + :created_by_id, :updated_by_id + ] ) end end diff --git a/app/controllers/admin/eu_decision_types_controller.rb b/app/controllers/admin/eu_decision_types_controller.rb index 7d9d60754..564801aca 100644 --- a/app/controllers/admin/eu_decision_types_controller.rb +++ b/app/controllers/admin/eu_decision_types_controller.rb @@ -22,9 +22,10 @@ def collection private def eu_decision_type_params - params.require(:eu_decision_type).permit( - # attributes were in model `attr_accessible`. - :name, :tooltip, :decision_type + params.expect( + eu_decision_type: [ + :name, :tooltip, :decision_type + ] ) end end diff --git a/app/controllers/admin/eu_hash_annotations_controller.rb b/app/controllers/admin/eu_hash_annotations_controller.rb index bd763c8dd..f9a06e797 100644 --- a/app/controllers/admin/eu_hash_annotations_controller.rb +++ b/app/controllers/admin/eu_hash_annotations_controller.rb @@ -12,11 +12,12 @@ def load_associations private def eu_hash_annotation_params - params.require(:annotation).permit( - # attributes were in model `attr_accessible`. - :listing_change_id, :symbol, :parent_symbol, :short_note_en, - :full_note_en, :short_note_fr, :full_note_fr, :short_note_es, :full_note_es, - :display_in_index, :display_in_footnote, :event_id + params.expect( + annotation: [ + :listing_change_id, :symbol, :parent_symbol, :short_note_en, + :full_note_en, :short_note_fr, :full_note_fr, :short_note_es, :full_note_es, + :display_in_index, :display_in_footnote, :event_id + ] ) end end diff --git a/app/controllers/admin/eu_implementing_regulations_controller.rb b/app/controllers/admin/eu_implementing_regulations_controller.rb index 0adad6b9f..cd458e24e 100644 --- a/app/controllers/admin/eu_implementing_regulations_controller.rb +++ b/app/controllers/admin/eu_implementing_regulations_controller.rb @@ -22,11 +22,13 @@ def list_template private def eu_implementing_regulation_params - params.require(:eu_implementing_regulation).permit( - # attributes were in model `attr_accessible`. - :name, :designation_id, :description, :extended_description, - :url, :private_url, :multilingual_url, :published_at, :effective_at, :is_current, :end_date, - :created_by_id, :updated_by_id + params.expect( + eu_implementing_regulation: [ + :name, :designation_id, :description, :extended_description, + :url, :private_url, :multilingual_url, :published_at, + :effective_at, :is_current, :end_date, + :created_by_id, :updated_by_id + ] ) end end diff --git a/app/controllers/admin/eu_opinions_controller.rb b/app/controllers/admin/eu_opinions_controller.rb index 589283fc6..380112ab1 100644 --- a/app/controllers/admin/eu_opinions_controller.rb +++ b/app/controllers/admin/eu_opinions_controller.rb @@ -73,13 +73,14 @@ def collection private def eu_opinion_params - params.require(:eu_opinion).permit( - # attributes were in model `attr_accessible`. - :document_id, :end_date, :end_event_id, :geo_entity_id, :internal_notes, - :is_current, :notes, :start_date, :start_event_id, :eu_decision_type_id, - :taxon_concept_id, :type, :conditions_apply, :term_id, :source_id, - :nomenclature_note_en, :nomenclature_note_es, :nomenclature_note_fr, - :created_by_id, :updated_by_id, :srg_history_id + params.expect( + eu_opinion: [ + :document_id, :end_date, :end_event_id, :geo_entity_id, :internal_notes, + :is_current, :notes, :start_date, :start_event_id, :eu_decision_type_id, + :taxon_concept_id, :type, :conditions_apply, :term_id, :source_id, + :nomenclature_note_en, :nomenclature_note_es, :nomenclature_note_fr, + :created_by_id, :updated_by_id, :srg_history_id + ] ) end end diff --git a/app/controllers/admin/eu_regulations_controller.rb b/app/controllers/admin/eu_regulations_controller.rb index fc59951dc..8d9869d85 100644 --- a/app/controllers/admin/eu_regulations_controller.rb +++ b/app/controllers/admin/eu_regulations_controller.rb @@ -35,11 +35,14 @@ def load_associations private def eu_regulation_params - params.require(:eu_regulation).permit( - # attributes were in model `attr_accessible`. - :listing_changes_event_id, :name, :designation_id, :description, :extended_description, - :url, :private_url, :multilingual_url, :published_at, :effective_at, :is_current, :end_date, - :created_by_id, :updated_by_id + params.expect( + eu_regulation: [ + :listing_changes_event_id, :name, :designation_id, + :description, :extended_description, + :url, :private_url, :multilingual_url, :published_at, + :effective_at, :is_current, :end_date, + :created_by_id, :updated_by_id + ] ) end end diff --git a/app/controllers/admin/eu_suspension_regulations_controller.rb b/app/controllers/admin/eu_suspension_regulations_controller.rb index 329687a3d..5f9cc3a78 100644 --- a/app/controllers/admin/eu_suspension_regulations_controller.rb +++ b/app/controllers/admin/eu_suspension_regulations_controller.rb @@ -42,11 +42,14 @@ def load_associations private def eu_suspension_regulation_params - params.require(:eu_suspension_regulation).permit( - # attributes were in model `attr_accessible`. - :eu_suspensions_event_id, :name, :designation_id, :description, :extended_description, - :url, :private_url, :multilingual_url, :published_at, :effective_at, :is_current, :end_date, - :created_by_id, :updated_by_id + params.expect( + eu_suspension_regulation: [ + :eu_suspensions_event_id, :name, :designation_id, + :description, :extended_description, + :url, :private_url, :multilingual_url, + :published_at, :effective_at, :is_current, :end_date, + :created_by_id, :updated_by_id + ] ) end end diff --git a/app/controllers/admin/events_controller.rb b/app/controllers/admin/events_controller.rb index c71853f82..6d6ddd5f4 100644 --- a/app/controllers/admin/events_controller.rb +++ b/app/controllers/admin/events_controller.rb @@ -54,11 +54,13 @@ def load_associations private def event_params - params.require(:event).permit( - # attributes were in model `attr_accessible`. - :name, :designation_id, :description, :extended_description, - :url, :private_url, :multilingual_url, :published_at, :effective_at, :is_current, :end_date, - :created_by_id, :updated_by_id + params.expect( + event: [ + :name, :designation_id, :description, :extended_description, + :url, :private_url, :multilingual_url, + :published_at, :effective_at, :is_current, :end_date, + :created_by_id, :updated_by_id + ] ) end end diff --git a/app/controllers/admin/geo_entities_controller.rb b/app/controllers/admin/geo_entities_controller.rb index 8bbe75d7e..2abced7e8 100644 --- a/app/controllers/admin/geo_entities_controller.rb +++ b/app/controllers/admin/geo_entities_controller.rb @@ -37,11 +37,12 @@ def collection private def geo_entity_params - params.require(:geo_entity).permit( - # attributes were in model `attr_accessible`. - :geo_entity_type_id, :iso_code2, :iso_code3, - :legacy_id, :legacy_type, :long_name, :name_en, :name_es, :name_fr, - :is_current + params.expect( + geo_entity: [ + :geo_entity_type_id, :iso_code2, :iso_code3, + :legacy_id, :legacy_type, :long_name, :name_en, :name_es, :name_fr, + :is_current + ] ) end end diff --git a/app/controllers/admin/geo_relationships_controller.rb b/app/controllers/admin/geo_relationships_controller.rb index 8b7b50954..9383a3559 100644 --- a/app/controllers/admin/geo_relationships_controller.rb +++ b/app/controllers/admin/geo_relationships_controller.rb @@ -47,9 +47,10 @@ def collection private def geo_relationship_params - params.require(:geo_relationship).permit( - # attributes were in model `attr_accessible`. - :geo_entity_id, :geo_relationship_type_id, :other_geo_entity_id + params.expect( + geo_relationship: [ + :geo_entity_id, :geo_relationship_type_id, :other_geo_entity_id + ] ) end end diff --git a/app/controllers/admin/hybrid_relationships_controller.rb b/app/controllers/admin/hybrid_relationships_controller.rb index 6dfec3073..ed8f75697 100644 --- a/app/controllers/admin/hybrid_relationships_controller.rb +++ b/app/controllers/admin/hybrid_relationships_controller.rb @@ -66,10 +66,11 @@ def load_hybrid_relationship_type private def hybrid_relationship_params - params.require(:taxon_relationship).permit( - # attributes were in model `attr_accessible`. - :taxon_concept_id, :other_taxon_concept_id, :taxon_relationship_type_id, - :created_by_id, :updated_by_id + params.expect( + taxon_relationship: [ + :taxon_concept_id, :other_taxon_concept_id, :taxon_relationship_type_id, + :created_by_id, :updated_by_id + ] ) end end diff --git a/app/controllers/admin/instruments_controller.rb b/app/controllers/admin/instruments_controller.rb index cb19f5fc8..3eb36c278 100644 --- a/app/controllers/admin/instruments_controller.rb +++ b/app/controllers/admin/instruments_controller.rb @@ -30,9 +30,6 @@ def load_associations private def instrument_params - params.require(:instrument).permit( - # attributes were in model `attr_accessible`. - :designation_id, :name - ) + params.expect(instrument: [ :designation_id, :name ]) end end diff --git a/app/controllers/admin/languages_controller.rb b/app/controllers/admin/languages_controller.rb index f7a26fe4f..0308e857a 100644 --- a/app/controllers/admin/languages_controller.rb +++ b/app/controllers/admin/languages_controller.rb @@ -14,9 +14,8 @@ def collection private def language_params - params.require(:language).permit( - # attributes were in model `attr_accessible`. - :iso_code1, :iso_code3, :name_en, :name_fr, :name_es + params.expect( + language: [ :iso_code1, :iso_code3, :name_en, :name_fr, :name_es ] ) end end diff --git a/app/controllers/admin/nomenclature_changes/build_controller.rb b/app/controllers/admin/nomenclature_changes/build_controller.rb index 95941c87a..7cfb85ec4 100644 --- a/app/controllers/admin/nomenclature_changes/build_controller.rb +++ b/app/controllers/admin/nomenclature_changes/build_controller.rb @@ -12,6 +12,7 @@ def finish_wizard_path def show raise NoMethodError end + def create @nomenclature_change = klass.new() @nomenclature_change.status = NomenclatureChange::NEW @@ -31,6 +32,64 @@ def destroy raise NoMethodError end +protected + def common_nomenclature_change_attribute_names + input_attribute_names = [ + :id, :_destroy, + :nomenclature_change_id, :taxon_concept_id, + :note_en, :note_es, :note_fr, :internal_note + ] + + output_attribute_names = [ + :id, :_destroy, + :nomenclature_change_id, :taxon_concept_id, + :new_taxon_concept_id, :rank_id, :new_scientific_name, :new_author_year, + :new_name_status, :new_parent_id, :new_rank_id, :taxonomy_id, + :note_en, :note_es, :note_fr, :internal_note, :is_primary_output, + :output_type, :created_by_id, :updated_by_id, + tag_list: [] + ] + + reassignment_attribute_names = [ + :id, :_destroy, + :type, :reassignable_id, :reassignable_type, + :nomenclature_change_input_id, :nomenclature_change_output_id, + :note_en, :note_es, :note_fr, :internal_note + ] + + reassignment_target_attribute_names = [ + :id, :_destroy, + :nomenclature_change_output_id, + :nomenclature_change_reassignment_id, + :note + ] + + parent_reassignments_attribute_names = [ + *reassignment_attribute_names, + reassignment_target_attributes: [ reassignment_target_attribute_names ] + ] + + output_parent_reassignment_attribute_names = [ + *parent_reassignments_attribute_names, + output_ids: [] + ] + + output_reassignment_attribute_names = [ + *reassignment_attribute_names, + output_ids: [] + ] + + { + input_attribute_names:, + output_attribute_names:, + output_parent_reassignment_attribute_names:, + output_reassignment_attribute_names:, + parent_reassignments_attribute_names:, + reassignment_attribute_names:, + reassignment_target_attribute_names: + } + end + private def set_nomenclature_change @@ -64,8 +123,6 @@ def authorise_finish end end -private - def klass NomenclatureChange end diff --git a/app/controllers/admin/nomenclature_changes/lump_controller.rb b/app/controllers/admin/nomenclature_changes/lump_controller.rb index 5a88fef91..a922da79e 100644 --- a/app/controllers/admin/nomenclature_changes/lump_controller.rb +++ b/app/controllers/admin/nomenclature_changes/lump_controller.rb @@ -1,5 +1,5 @@ class Admin::NomenclatureChanges::LumpController < Admin::NomenclatureChanges::BuildController - steps *NomenclatureChange::Lump::STEPS + steps(*NomenclatureChange::Lump::STEPS) def show builder = NomenclatureChange::Lump::Constructor.new(@nomenclature_change) @@ -22,6 +22,7 @@ def show skip_or_previous_step if @nomenclature_change.inputs.map(&:legislation_reassignments).flatten.empty? when :summary builder.build_document_reassignments + processor = NomenclatureChange::Lump::Processor.new(@nomenclature_change) @summary = processor.summary end @@ -30,13 +31,20 @@ def show def update @nomenclature_change.assign_attributes( - (nomenclature_change_lump_params || {}).merge( + begin + nomenclature_change_lump_params + rescue ActionController::ParameterMissing + # TODO: explain why we allow this to fail silently + {} + end.merge( { status: (step == steps.last ? NomenclatureChange::SUBMITTED : step.to_s) } ) ) + success = @nomenclature_change.valid? + case step when :inputs, :outputs unless success @@ -50,6 +58,7 @@ def update set_ranks end end + render_wizard @nomenclature_change end @@ -60,63 +69,36 @@ def klass end def nomenclature_change_lump_params - params.require(:nomenclature_change_lump).permit( - :event_id, :status, - inputs_attributes: [ - :id, :_destroy, - :nomenclature_change_id, :taxon_concept_id, - :note_en, :note_es, :note_fr, :internal_note, - parent_reassignments_attributes: [ - :id, :_destroy, - :type, :reassignable_id, :reassignable_type, - :nomenclature_change_input_id, :nomenclature_change_output_id, - :note_en, :note_es, :note_fr, :internal_note, - reassignment_target_attributes: [ - :id, :_destroy, - :nomenclature_change_output_id, - :nomenclature_change_reassignment_id, :note - ], - output_ids: [] - ], - name_reassignments_attributes: [ - :id, :_destroy, - :type, :reassignable_id, :reassignable_type, - :nomenclature_change_input_id, :nomenclature_change_output_id, - :note_en, :note_es, :note_fr, :internal_note, - output_ids: [] - ], - distribution_reassignments_attributes: [ - :id, :_destroy, - :type, :reassignable_id, :reassignable_type, - :nomenclature_change_input_id, :nomenclature_change_output_id, - :note_en, :note_es, :note_fr, :internal_note, - output_ids: [] + ( + input_attribute_names, + output_attribute_names, + output_parent_reassignment_attribute_names, + output_reassignment_attribute_names, + ) = common_nomenclature_change_attribute_names.values_at( + :input_attribute_names, + :output_attribute_names, + :output_parent_reassignment_attribute_names, + :output_reassignment_attribute_names, + ) + + params.expect( + nomenclature_change_lump: [ + :event_id, :status, + ## + # Note: `inputs` is plural, because lump is many -> one + inputs_attributes: [ + [ + *input_attribute_names, + parent_reassignments_attributes: [ output_parent_reassignment_attribute_names ], + name_reassignments_attributes: [ output_reassignment_attribute_names ], + distribution_reassignments_attributes: [ output_reassignment_attribute_names ], + legislation_reassignments_attributes: [ output_reassignment_attribute_names ] + ] ], - legislation_reassignments_attributes: [ - :id, :_destroy, - :type, :reassignable_id, :reassignable_type, - :nomenclature_change_input_id, :nomenclature_change_output_id, - :note_en, :note_es, :note_fr, :internal_note, - output_ids: [] - ] - ], - output_attributes: [ - :id, :_destroy, - :nomenclature_change_id, :taxon_concept_id, - :new_taxon_concept_id, :rank_id, :new_scientific_name, :new_author_year, - :new_name_status, :new_parent_id, :new_rank_id, :taxonomy_id, - :note_en, :note_es, :note_fr, :internal_note, :is_primary_output, - :output_type, :created_by_id, :updated_by_id, - tag_list: [] - # app/models/nomenclature_change/output.rb does not have `accepts_nested_attributes_for`, so - # xxx_attributes suppose not in-use. - # :parent_reassignments_attributes, - # :name_reassignments_attributes, - # :distribution_reassignments_attributes, - # :legislation_reassignments_attributes, + ## + # Note: `outputs` is singular, because lump is many -> one + output_attributes: output_attribute_names ] ) - rescue ActionController::ParameterMissing - nil end end diff --git a/app/controllers/admin/nomenclature_changes/split_controller.rb b/app/controllers/admin/nomenclature_changes/split_controller.rb index d16f82b3c..907f67bb7 100644 --- a/app/controllers/admin/nomenclature_changes/split_controller.rb +++ b/app/controllers/admin/nomenclature_changes/split_controller.rb @@ -29,6 +29,7 @@ def show skip_or_previous_step if @nomenclature_change.input.legislation_reassignments.empty? when :summary builder.build_document_reassignments + processor = NomenclatureChange::Split::Processor.new(@nomenclature_change) @summary = processor.summary end @@ -38,12 +39,18 @@ def show def update @nomenclature_change.assign_attributes( - (nomenclature_change_split_params || {}).merge( + begin + nomenclature_change_split_params + rescue ActionController::ParameterMissing + # TODO: explain why we allow this to fail silently + {} + end.merge( { status: (step == steps.last ? NomenclatureChange::SUBMITTED : step.to_s) } ) ) + success = @nomenclature_change.valid? case step @@ -65,67 +72,34 @@ def klass end def nomenclature_change_split_params - params.require(:nomenclature_change_split).permit( - :event_id, :status, - input_attributes: [ - :id, :_destroy, - :nomenclature_change_id, :taxon_concept_id, - :note_en, :note_es, :note_fr, :internal_note, - parent_reassignments_attributes: [ - :id, :_destroy, - :type, :reassignable_id, :reassignable_type, - :nomenclature_change_input_id, :nomenclature_change_output_id, - :note_en, :note_es, :note_fr, :internal_note, - :output_ids, - output_ids: [], - reassignment_target_attributes: [ - :id, :_destroy, - :nomenclature_change_output_id, - :nomenclature_change_reassignment_id, :note - ] - ], - name_reassignments_attributes: [ - :id, :_destroy, - :type, :reassignable_id, :reassignable_type, - :nomenclature_change_input_id, :nomenclature_change_output_id, - :note_en, :note_es, :note_fr, :internal_note, - :output_ids, - output_ids: [] - ], - distribution_reassignments_attributes: [ - :id, :_destroy, - :type, :reassignable_id, :reassignable_type, - :nomenclature_change_input_id, :nomenclature_change_output_id, - :note_en, :note_es, :note_fr, :internal_note, - :output_ids, - output_ids: [] + ( + input_attribute_names, + output_attribute_names, + output_parent_reassignment_attribute_names, + output_reassignment_attribute_names, + ) = common_nomenclature_change_attribute_names.values_at( + :input_attribute_names, + :output_attribute_names, + :output_parent_reassignment_attribute_names, + :output_reassignment_attribute_names, + ) + + params.expect( + nomenclature_change_split: [ + :event_id, :status, + ## + # Note: `input` is singular, because split is one -> many + input_attributes: [ + *input_attribute_names, + parent_reassignments_attributes: [ output_parent_reassignment_attribute_names ], + name_reassignments_attributes: [ output_reassignment_attribute_names ], + distribution_reassignments_attributes: [ output_reassignment_attribute_names ], + legislation_reassignments_attributes: [ output_reassignment_attribute_names ] ], - legislation_reassignments_attributes: [ - :id, :_destroy, - :type, :reassignable_id, :reassignable_type, - :nomenclature_change_input_id, :nomenclature_change_output_id, - :note_en, :note_es, :note_fr, :internal_note, - :output_ids, - output_ids: [] - ] - ], - outputs_attributes: [ - :id, :_destroy, - :nomenclature_change_id, :taxon_concept_id, - :new_taxon_concept_id, :rank_id, :new_scientific_name, :new_author_year, - :new_name_status, :new_parent_id, :new_rank_id, :taxonomy_id, - :note_en, :note_es, :note_fr, :internal_note, :is_primary_output, - :output_type, :created_by_id, :updated_by_id, - tag_list: [] - # app/models/nomenclature_change/output.rb does not have `accepts_nested_attributes_for`, so - # xxx_attributes suppose not in-use. - # :parent_reassignments_attributes, - # :name_reassignments_attributes, - # :distribution_reassignments_attributes, - # :legislation_reassignments_attributes, + ## + # Note: `outputs` is plural, because split is one -> many + outputs_attributes: [ output_attribute_names ] ] ) - rescue ActionController::ParameterMissing - nil end end diff --git a/app/controllers/admin/nomenclature_changes/status_swap_controller.rb b/app/controllers/admin/nomenclature_changes/status_swap_controller.rb index 59c104112..63ddb81c8 100644 --- a/app/controllers/admin/nomenclature_changes/status_swap_controller.rb +++ b/app/controllers/admin/nomenclature_changes/status_swap_controller.rb @@ -1,8 +1,9 @@ class Admin::NomenclatureChanges::StatusSwapController < Admin::NomenclatureChanges::BuildController - steps *NomenclatureChange::StatusSwap::STEPS + steps(*NomenclatureChange::StatusSwap::STEPS) def show builder = klass::Constructor.new(@nomenclature_change) + case step when :primary_output set_events @@ -21,19 +22,28 @@ def show processor = klass::Processor.new(@nomenclature_change) @summary = processor.summary end + render_wizard end def update @nomenclature_change.assign_attributes( - (nomenclature_change_status_swap_params || {}).merge( + begin + nomenclature_change_status_swap_params + rescue ActionController::ParameterMissing + # TODO: explain why we allow this to fail silently + {} + end.merge( { status: (step == steps.last ? NomenclatureChange::SUBMITTED : step.to_s) } ) ) + success = @nomenclature_change.valid? + case step + when :primary_output unless success set_events @@ -45,6 +55,7 @@ def update set_ranks end end + render_wizard @nomenclature_change end @@ -55,78 +66,31 @@ def klass end def nomenclature_change_status_swap_params - params.require(:nomenclature_change_status_swap).permit( - :event_id, :status, - primary_output_attributes: [ - :id, :_destroy, - :nomenclature_change_id, :taxon_concept_id, - :new_taxon_concept_id, :rank_id, :new_scientific_name, :new_author_year, - :new_name_status, :new_parent_id, :new_rank_id, :taxonomy_id, - :note_en, :note_es, :note_fr, :internal_note, :is_primary_output, - :output_type, :created_by_id, :updated_by_id, - tag_list: [] - # app/models/nomenclature_change/output.rb does not have `accepts_nested_attributes_for`, so - # xxx_attributes suppose not in-use. - # :parent_reassignments_attributes, - # :name_reassignments_attributes, - # :distribution_reassignments_attributes, - # :legislation_reassignments_attributes, - ], - secondary_output_attributes: [ - :id, :_destroy, - :nomenclature_change_id, :taxon_concept_id, - :new_taxon_concept_id, :rank_id, :new_scientific_name, :new_author_year, - :new_name_status, :new_parent_id, :new_rank_id, :taxonomy_id, - :note_en, :note_es, :note_fr, :internal_note, :is_primary_output, - :output_type, :created_by_id, :updated_by_id, - tag_list: [] - # app/models/nomenclature_change/output.rb does not have `accepts_nested_attributes_for`, so - # xxx_attributes suppose not in-use. - # :parent_reassignments_attributes, - # :name_reassignments_attributes, - # :distribution_reassignments_attributes, - # :legislation_reassignments_attributes, - ], - input_attributes: [ - :id, :_destroy, - :nomenclature_change_id, :taxon_concept_id, - :note_en, :note_es, :note_fr, :internal_note, - parent_reassignments_attributes: [ - :id, :_destroy, - :type, :reassignable_id, :reassignable_type, - :nomenclature_change_input_id, :nomenclature_change_output_id, - :note_en, :note_es, :note_fr, :internal_note, - output_ids: [], - reassignment_target_attributes: [ - :id, :_destroy, - :nomenclature_change_output_id, - :nomenclature_change_reassignment_id, :note - ] - ], - name_reassignments_attributes: [ - :id, :_destroy, - :type, :reassignable_id, :reassignable_type, - :nomenclature_change_input_id, :nomenclature_change_output_id, - :note_en, :note_es, :note_fr, :internal_note, - output_ids: [] - ], - distribution_reassignments_attributes: [ - :id, :_destroy, - :type, :reassignable_id, :reassignable_type, - :nomenclature_change_input_id, :nomenclature_change_output_id, - :note_en, :note_es, :note_fr, :internal_note, - output_ids: [] - ], - legislation_reassignments_attributes: [ - :id, :_destroy, - :type, :reassignable_id, :reassignable_type, - :nomenclature_change_input_id, :nomenclature_change_output_id, - :note_en, :note_es, :note_fr, :internal_note, - output_ids: [] + ( + input_attribute_names, + output_attribute_names, + output_reassignment_attribute_names, + output_parent_reassignments_attribute_names, + ) = common_nomenclature_change_attribute_names.values_at( + :input_attribute_names, + :output_attribute_names, + :output_reassignment_attribute_names, + :output_parent_reassignments_attribute_names, + ) + + params.expect( + nomenclature_change_status_swap: [ + :event_id, :status, + primary_output_attributes: output_attribute_names, + secondary_output_attributes: output_attribute_names, + input_attributes: [ + *input_attribute_names, + parent_reassignments_attributes: [ output_parent_reassignments_attribute_names ], + name_reassignments_attributes: [ output_reassignment_attribute_names ], + distribution_reassignments_attributes: [ output_reassignment_attribute_names ], + legislation_reassignments_attributes: [ output_reassignment_attribute_names ] ] ] ) - rescue ActionController::ParameterMissing - nil end end diff --git a/app/controllers/admin/nomenclature_changes/status_to_accepted_controller.rb b/app/controllers/admin/nomenclature_changes/status_to_accepted_controller.rb index 212a9ee32..460b52d65 100644 --- a/app/controllers/admin/nomenclature_changes/status_to_accepted_controller.rb +++ b/app/controllers/admin/nomenclature_changes/status_to_accepted_controller.rb @@ -1,5 +1,5 @@ class Admin::NomenclatureChanges::StatusToAcceptedController < Admin::NomenclatureChanges::BuildController - steps *NomenclatureChange::StatusToAccepted::STEPS + steps(*NomenclatureChange::StatusToAccepted::STEPS) def show builder = klass::Constructor.new(@nomenclature_change) @@ -18,13 +18,20 @@ def show def update @nomenclature_change.assign_attributes( - (nomenclature_change_status_to_accepted_params || {}).merge( + begin + nomenclature_change_status_to_accepted_params + rescue ActionController::ParameterMissing + # TODO: explain why we allow this to fail silently + {} + end.merge( { status: (step == steps.last ? NomenclatureChange::SUBMITTED : step.to_s) } ) ) + success = @nomenclature_change.valid? + case step when :primary_output unless success @@ -33,6 +40,7 @@ def update set_ranks end end + render_wizard @nomenclature_change end @@ -43,78 +51,31 @@ def klass end def nomenclature_change_status_to_accepted_params - params.require(:nomenclature_change_status_to_accepted).permit( - :created_by_id, :updated_by_id, :event_id, :status, - primary_output_attributes: [ - :id, :_destroy, - :nomenclature_change_id, :taxon_concept_id, - :new_taxon_concept_id, :rank_id, :new_scientific_name, :new_author_year, - :new_name_status, :new_parent_id, :new_rank_id, :taxonomy_id, - :note_en, :note_es, :note_fr, :internal_note, :is_primary_output, - :output_type, :created_by_id, :updated_by_id, - tag_list: [] - # app/models/nomenclature_change/output.rb does not have `accepts_nested_attributes_for`, so - # xxx_attributes suppose not in-use. - # :parent_reassignments_attributes, - # :name_reassignments_attributes, - # :distribution_reassignments_attributes, - # :legislation_reassignments_attributes, - ], - secondary_output_attributes: [ - :id, :_destroy, - :nomenclature_change_id, :taxon_concept_id, - :new_taxon_concept_id, :rank_id, :new_scientific_name, :new_author_year, - :new_name_status, :new_parent_id, :new_rank_id, :taxonomy_id, - :note_en, :note_es, :note_fr, :internal_note, :is_primary_output, - :output_type, :created_by_id, :updated_by_id, - tag_list: [] - # app/models/nomenclature_change/output.rb does not have `accepts_nested_attributes_for`, so - # xxx_attributes suppose not in-use. - # :parent_reassignments_attributes, - # :name_reassignments_attributes, - # :distribution_reassignments_attributes, - # :legislation_reassignments_attributes, - ], - input_attributes: [ - :id, :_destroy, - :nomenclature_change_id, :taxon_concept_id, - :note_en, :note_es, :note_fr, :internal_note, - parent_reassignments_attributes: [ - :id, :_destroy, - :type, :reassignable_id, :reassignable_type, - :nomenclature_change_input_id, :nomenclature_change_output_id, - :note_en, :note_es, :note_fr, :internal_note, - output_ids: [], - reassignment_target_attributes: [ - :id, :_destroy, - :nomenclature_change_output_id, - :nomenclature_change_reassignment_id, :note - ] - ], - name_reassignments_attributes: [ - :id, :_destroy, - :type, :reassignable_id, :reassignable_type, - :nomenclature_change_input_id, :nomenclature_change_output_id, - :note_en, :note_es, :note_fr, :internal_note, - output_ids: [] - ], - distribution_reassignments_attributes: [ - :id, :_destroy, - :type, :reassignable_id, :reassignable_type, - :nomenclature_change_input_id, :nomenclature_change_output_id, - :note_en, :note_es, :note_fr, :internal_note, - output_ids: [] - ], - legislation_reassignments_attributes: [ - :id, :_destroy, - :type, :reassignable_id, :reassignable_type, - :nomenclature_change_input_id, :nomenclature_change_output_id, - :note_en, :note_es, :note_fr, :internal_note, - output_ids: [] + ( + input_attribute_names, + output_attribute_names, + output_reassignment_attribute_names, + parent_reassignments_attribute_names, + ) = common_nomenclature_change_attribute_names.values_at( + :input_attribute_names, + :output_attribute_names, + :output_reassignment_attribute_names, + :parent_reassignments_attribute_names, + ) + + params.expect( + nomenclature_change_status_to_accepted: [ + :created_by_id, :updated_by_id, :event_id, :status, + primary_output_attributes: output_attribute_names, + secondary_output_attributes: output_attribute_names, + input_attributes: [ + *input_attribute_names, + parent_reassignments_attributes: [ parent_reassignments_attribute_names ], + name_reassignments_attributes: [ output_reassignment_attribute_names ], + distribution_reassignments_attributes: [ output_reassignment_attribute_names ], + legislation_reassignments_attributes: [ output_reassignment_attribute_names ] ] ] ) - rescue ActionController::ParameterMissing - nil end end diff --git a/app/controllers/admin/nomenclature_changes/status_to_synonym_controller.rb b/app/controllers/admin/nomenclature_changes/status_to_synonym_controller.rb index aeee04fea..c683d36ca 100644 --- a/app/controllers/admin/nomenclature_changes/status_to_synonym_controller.rb +++ b/app/controllers/admin/nomenclature_changes/status_to_synonym_controller.rb @@ -1,8 +1,9 @@ class Admin::NomenclatureChanges::StatusToSynonymController < Admin::NomenclatureChanges::BuildController - steps *NomenclatureChange::StatusToSynonym::STEPS + steps(*NomenclatureChange::StatusToSynonym::STEPS) def show builder = klass::Constructor.new(@nomenclature_change) + case step when :primary_output set_events @@ -23,18 +24,26 @@ def show processor = klass::Processor.new(@nomenclature_change) @summary = processor.summary end + render_wizard end def update @nomenclature_change.assign_attributes( - (nomenclature_change_status_to_synonym_params || {}).merge( + begin + nomenclature_change_status_to_synonym_params + rescue ActionController::ParameterMissing + # TODO: explain why we allow this to fail silently + {} + end.merge( { status: (step == steps.last ? NomenclatureChange::SUBMITTED : step.to_s) } ) ) + success = @nomenclature_change.valid? + case step when :primary_output unless success @@ -44,6 +53,7 @@ def update when :relay, :accepted_name set_taxonomy unless success end + render_wizard @nomenclature_change end @@ -54,78 +64,31 @@ def klass end def nomenclature_change_status_to_synonym_params - params.require(:nomenclature_change_status_to_synonym).permit( - :event_id, :status, - primary_output_attributes: [ - :id, :_destroy, - :nomenclature_change_id, :taxon_concept_id, - :new_taxon_concept_id, :rank_id, :new_scientific_name, :new_author_year, - :new_name_status, :new_parent_id, :new_rank_id, :taxonomy_id, - :note_en, :note_es, :note_fr, :internal_note, :is_primary_output, - :output_type, :created_by_id, :updated_by_id, - tag_list: [] - # app/models/nomenclature_change/output.rb does not have `accepts_nested_attributes_for`, so - # xxx_attributes suppose not in-use. - # :parent_reassignments_attributes, - # :name_reassignments_attributes, - # :distribution_reassignments_attributes, - # :legislation_reassignments_attributes, - ], - secondary_output_attributes: [ - :id, :_destroy, - :nomenclature_change_id, :taxon_concept_id, - :new_taxon_concept_id, :rank_id, :new_scientific_name, :new_author_year, - :new_name_status, :new_parent_id, :new_rank_id, :taxonomy_id, - :note_en, :note_es, :note_fr, :internal_note, :is_primary_output, - :output_type, :created_by_id, :updated_by_id, - tag_list: [] - # app/models/nomenclature_change/output.rb does not have `accepts_nested_attributes_for`, so - # xxx_attributes suppose not in-use. - # :parent_reassignments_attributes, - # :name_reassignments_attributes, - # :distribution_reassignments_attributes, - # :legislation_reassignments_attributes, - ], - input_attributes: [ - :id, :_destroy, - :nomenclature_change_id, :taxon_concept_id, - :note_en, :note_es, :note_fr, :internal_note, - parent_reassignments_attributes: [ - :id, :_destroy, - :type, :reassignable_id, :reassignable_type, - :nomenclature_change_input_id, :nomenclature_change_output_id, - :note_en, :note_es, :note_fr, :internal_note, - output_ids: [], - reassignment_target_attributes: [ - :id, :_destroy, - :nomenclature_change_output_id, - :nomenclature_change_reassignment_id, :note - ] - ], - name_reassignments_attributes: [ - :id, :_destroy, - :type, :reassignable_id, :reassignable_type, - :nomenclature_change_input_id, :nomenclature_change_output_id, - :note_en, :note_es, :note_fr, :internal_note, - output_ids: [] - ], - distribution_reassignments_attributes: [ - :id, :_destroy, - :type, :reassignable_id, :reassignable_type, - :nomenclature_change_input_id, :nomenclature_change_output_id, - :note_en, :note_es, :note_fr, :internal_note, - output_ids: [] + ( + input_attribute_names, + output_attribute_names, + output_reassignment_attribute_names, + parent_reassignments_attribute_names, + ) = common_nomenclature_change_attribute_names.values_at( + :input_attribute_names, + :output_attribute_names, + :output_reassignment_attribute_names, + :parent_reassignments_attribute_names, + ) + + params.expect( + nomenclature_change_status_to_synonym: [ + :event_id, :status, + primary_output_attributes: output_attribute_names, + secondary_output_attributes: output_attribute_names, + input_attributes: [ + *input_attribute_names, + parent_reassignments_attributes: [ parent_reassignments_attribute_names ], + name_reassignments_attributes: [ output_reassignment_attribute_names ], + distribution_reassignments_attributes: [ output_reassignment_attribute_names ], + legislation_reassignments_attributes: [ output_reassignment_attribute_names ] ], - legislation_reassignments_attributes: [ - :id, :_destroy, - :type, :reassignable_id, :reassignable_type, - :nomenclature_change_input_id, :nomenclature_change_output_id, - :note_en, :note_es, :note_fr, :internal_note, - output_ids: [] - ] ] ) - rescue ActionController::ParameterMissing - nil end end diff --git a/app/controllers/admin/purposes_controller.rb b/app/controllers/admin/purposes_controller.rb index 14f9fa392..0ca268dd7 100644 --- a/app/controllers/admin/purposes_controller.rb +++ b/app/controllers/admin/purposes_controller.rb @@ -29,9 +29,6 @@ def collection private def purpose_params - params.require(:purpose).permit( - # attributes were in model `attr_accessible`. - :code, :type, :name_en, :name_es, :name_fr - ) + params.expect(purpose: [ :code, :type, :name_en, :name_es, :name_fr ]) end end diff --git a/app/controllers/admin/quotas_controller.rb b/app/controllers/admin/quotas_controller.rb index 5c1e003b5..e8375e4e8 100644 --- a/app/controllers/admin/quotas_controller.rb +++ b/app/controllers/admin/quotas_controller.rb @@ -18,7 +18,7 @@ def duplication def duplicate quota_params = params[:quotas].merge(current_user_id: current_user.id) - QuotasCopyWorker.perform_async(quota_params.permit!.to_hash) + QuotasCopyWorker.perform_async(quota_params.permit!.as_json) redirect_to admin_quotas_path({ year: params[:quotas][:start_date].split('/')[2] }), notice: "Your quotas are being duplicated in the background. diff --git a/app/controllers/admin/ranks_controller.rb b/app/controllers/admin/ranks_controller.rb index 9099880fc..2e075f472 100644 --- a/app/controllers/admin/ranks_controller.rb +++ b/app/controllers/admin/ranks_controller.rb @@ -14,9 +14,11 @@ def collection private def rank_params - params.require(:rank).permit( - # attributes were in model `attr_accessible`. - :name, :display_name_en, :display_name_es, :display_name_fr, :taxonomic_position, :fixed_order + params.expect( + rank: [ + :name, :display_name_en, :display_name_es, :display_name_fr, + :taxonomic_position, :fixed_order + ] ) end end diff --git a/app/controllers/admin/references_controller.rb b/app/controllers/admin/references_controller.rb index 2e92b6ca2..40f707e23 100644 --- a/app/controllers/admin/references_controller.rb +++ b/app/controllers/admin/references_controller.rb @@ -46,9 +46,6 @@ def collection private def reference_params - params.require(:reference).permit( - # attributes were in model `attr_accessible`. - :citation, :created_by_id, :updated_by_id - ) + params.expect(reference: [ :citation, :created_by_id, :updated_by_id ]) end end diff --git a/app/controllers/admin/sources_controller.rb b/app/controllers/admin/sources_controller.rb index a46f423b2..e4c2e56f8 100644 --- a/app/controllers/admin/sources_controller.rb +++ b/app/controllers/admin/sources_controller.rb @@ -29,9 +29,6 @@ def collection private def source_params - params.require(:source).permit( - # attributes were in model `attr_accessible`. - :code, :type, :name_en, :name_es, :name_fr - ) + params.expect(source: [ :code, :type, :name_en, :name_es, :name_fr ]) end end diff --git a/app/controllers/admin/species_listings_controller.rb b/app/controllers/admin/species_listings_controller.rb index 1649206b7..d50e4e48d 100644 --- a/app/controllers/admin/species_listings_controller.rb +++ b/app/controllers/admin/species_listings_controller.rb @@ -20,9 +20,6 @@ def load_associations private def species_listing_params - params.require(:species_listing).permit( - # attributes were in model `attr_accessible`. - :designation_id, :name, :abbreviation - ) + params.expect(species_listing: [ :designation_id, :name, :abbreviation ]) end end diff --git a/app/controllers/admin/srg_histories_controller.rb b/app/controllers/admin/srg_histories_controller.rb index 93e7ce5f3..344eaa12f 100644 --- a/app/controllers/admin/srg_histories_controller.rb +++ b/app/controllers/admin/srg_histories_controller.rb @@ -14,9 +14,6 @@ def collection private def srg_history_params - params.require(:srg_history).permit( - # attributes were in model `attr_accessible`. - :name, :tooltip - ) + params.expect(srg_history: [ :name, :tooltip ]) end end diff --git a/app/controllers/admin/synonym_relationships_controller.rb b/app/controllers/admin/synonym_relationships_controller.rb index a067137e9..2e1bfb3c1 100644 --- a/app/controllers/admin/synonym_relationships_controller.rb +++ b/app/controllers/admin/synonym_relationships_controller.rb @@ -66,10 +66,11 @@ def load_synonym_relationship_type private def synonym_relationship_params - params.require(:taxon_relationship).permit( - # attributes were in model `attr_accessible`. - :taxon_concept_id, :other_taxon_concept_id, :taxon_relationship_type_id, - :created_by_id, :updated_by_id + params.expect( + taxon_relationship: [ + :taxon_concept_id, :other_taxon_concept_id, :taxon_relationship_type_id, + :created_by_id, :updated_by_id + ] ) end end diff --git a/app/controllers/admin/tags_controller.rb b/app/controllers/admin/tags_controller.rb index a20589052..d869410d3 100644 --- a/app/controllers/admin/tags_controller.rb +++ b/app/controllers/admin/tags_controller.rb @@ -18,9 +18,6 @@ def collection private def tag_params - params.require(:tag).permit( - # attributes were in model `attr_accessible`. - :model, :name - ) + params.expect(tag: [ :model, :name ]) end end diff --git a/app/controllers/admin/taxon_cites_suspensions_controller.rb b/app/controllers/admin/taxon_cites_suspensions_controller.rb index 01804f30d..1870fb243 100644 --- a/app/controllers/admin/taxon_cites_suspensions_controller.rb +++ b/app/controllers/admin/taxon_cites_suspensions_controller.rb @@ -72,21 +72,22 @@ def collection private def cites_suspension_params - params.require(:cites_suspension).permit( - # attributes were in model `attr_accessible`. - :start_notification_id, :end_notification_id, - :applies_to_import, :end_date, :geo_entity_id, :is_current, - :notes, :publication_date, :quota, :type, - :start_date, :unit_id, :internal_notes, - :nomenclature_note_en, :nomenclature_note_es, :nomenclature_note_fr, - :created_by_id, :updated_by_id, :url, - :taxon_concept_id, - cites_suspension_confirmations_attributes: [ - :cites_suspension_notification_id, :id, :_destroy - ], - purpose_ids: [], - term_ids: [], - source_ids: [] + params.expect( + cites_suspension: [ + :start_notification_id, :end_notification_id, + :applies_to_import, :end_date, :geo_entity_id, :is_current, + :notes, :publication_date, :quota, :type, + :start_date, :unit_id, :internal_notes, + :nomenclature_note_en, :nomenclature_note_es, :nomenclature_note_fr, + :created_by_id, :updated_by_id, :url, + :taxon_concept_id, + cites_suspension_confirmations_attributes: [ + [ :cites_suspension_notification_id, :id, :_destroy ] + ], + purpose_ids: [], + term_ids: [], + source_ids: [] + ] ) end end diff --git a/app/controllers/admin/taxon_commons_controller.rb b/app/controllers/admin/taxon_commons_controller.rb index 62e7f739d..ab3591040 100644 --- a/app/controllers/admin/taxon_commons_controller.rb +++ b/app/controllers/admin/taxon_commons_controller.rb @@ -17,6 +17,7 @@ def edit format.js { render 'new' } end end + def create create! do |success, failure| success.js do @@ -65,10 +66,11 @@ def load_associations private def taxon_common_params - params.require(:taxon_common).permit( - # attributes were in model `attr_accessible`. - :common_name_id, :taxon_concept_id, :created_by_id, - :updated_by_id, :name, :language_id + params.expect( + taxon_common: [ + :common_name_id, :taxon_concept_id, :created_by_id, + :updated_by_id, :name, :language_id + ] ) end end diff --git a/app/controllers/admin/taxon_concept_comments_controller.rb b/app/controllers/admin/taxon_concept_comments_controller.rb index 0c30925a0..2af3b9f96 100644 --- a/app/controllers/admin/taxon_concept_comments_controller.rb +++ b/app/controllers/admin/taxon_concept_comments_controller.rb @@ -31,10 +31,11 @@ def update private def comment_params - params.require(:comment).permit( - # attributes were in model `attr_accessible`. - :comment_type, :commentable_id, :commentable_type, :note, - :created_by_id, :updated_by_id + params.expect( + comment: [ + :comment_type, :commentable_id, :commentable_type, :note, + :created_by_id, :updated_by_id + ] ) end end diff --git a/app/controllers/admin/taxon_concept_references_controller.rb b/app/controllers/admin/taxon_concept_references_controller.rb index 6540e6f42..1a98de50b 100644 --- a/app/controllers/admin/taxon_concept_references_controller.rb +++ b/app/controllers/admin/taxon_concept_references_controller.rb @@ -74,12 +74,16 @@ def destroy private def taxon_concept_reference_params - params.require(:taxon_concept_reference).permit( - # attributes were in model `attr_accessible`. - :reference_id, :taxon_concept_id, :is_standard, :is_cascaded, - :created_by_id, :updated_by_id, - :excluded_taxon_concepts_ids, # String - reference_attributes: [ :citation, :created_by_id, :updated_by_id, :id, :_destroy ] + params.expect( + taxon_concept_reference: [ + :reference_id, :taxon_concept_id, :is_standard, :is_cascaded, + :created_by_id, :updated_by_id, + :excluded_taxon_concepts_ids, # String + reference_attributes: [ + # Expect a single object, not an array + :citation, :created_by_id, :updated_by_id, :id, :_destroy + ] + ] ) end end diff --git a/app/controllers/admin/taxon_concept_term_pairs_controller.rb b/app/controllers/admin/taxon_concept_term_pairs_controller.rb index c15066107..c6a9e451e 100644 --- a/app/controllers/admin/taxon_concept_term_pairs_controller.rb +++ b/app/controllers/admin/taxon_concept_term_pairs_controller.rb @@ -25,9 +25,6 @@ def collection private def taxon_concept_term_pair_params - params.require(:taxon_concept_term_pair).permit( - # attributes were in model `attr_accessible`. - :taxon_concept_id, :term_id - ) + params.expect(taxon_concept_term_pair: [ :taxon_concept_id, :term_id ]) end end diff --git a/app/controllers/admin/taxon_concepts_controller.rb b/app/controllers/admin/taxon_concepts_controller.rb index 423f6d856..3f6884fe6 100644 --- a/app/controllers/admin/taxon_concepts_controller.rb +++ b/app/controllers/admin/taxon_concepts_controller.rb @@ -154,18 +154,19 @@ def render_new_by_name_status private def taxon_concept_params - params.require(:taxon_concept).permit( - # attributes were in model `attr_accessible`. - :parent_id, :taxonomy_id, :rank_id, - :parent_id, :author_year, :taxon_name_id, :taxonomic_position, - :legacy_id, :legacy_type, :scientific_name, :name_status, - :legacy_trade_code, - :nomenclature_note_en, :nomenclature_note_es, :nomenclature_note_fr, - :created_by_id, :updated_by_id, :dependents_updated_at, :kew_id, - hybrid_parents_ids: [], # Coerced to int array by split_stringified_ids_lists - accepted_names_ids: [], # Coerced to int array by split_stringified_ids_lists - accepted_names_for_trade_name_ids: [], # Coerced to int array by split_stringified_ids_lists - tag_list: [] + params.expect( + taxon_concept: [ + :parent_id, :taxonomy_id, :rank_id, + :parent_id, :author_year, :taxon_name_id, :taxonomic_position, + :legacy_id, :legacy_type, :scientific_name, :name_status, + :legacy_trade_code, + :nomenclature_note_en, :nomenclature_note_es, :nomenclature_note_fr, + :created_by_id, :updated_by_id, :dependents_updated_at, :kew_id, + hybrid_parents_ids: [], # Coerced to int array by split_stringified_ids_lists + accepted_names_ids: [], # Coerced to int array by split_stringified_ids_lists + accepted_names_for_trade_name_ids: [], # Coerced to int array by split_stringified_ids_lists + tag_list: [] + ] ) end end diff --git a/app/controllers/admin/taxon_eu_suspensions_controller.rb b/app/controllers/admin/taxon_eu_suspensions_controller.rb index be81bacb1..e75dd7f85 100644 --- a/app/controllers/admin/taxon_eu_suspensions_controller.rb +++ b/app/controllers/admin/taxon_eu_suspensions_controller.rb @@ -92,13 +92,14 @@ def collection private def eu_suspension_params - params.require(:eu_suspension).permit( - # attributes were in model `attr_accessible`. - :end_date, :end_event_id, :geo_entity_id, :internal_notes, - :is_current, :notes, :start_date, :start_event_id, :eu_decision_type_id, - :taxon_concept_id, :type, :conditions_apply, :term_id, :source_id, - :nomenclature_note_en, :nomenclature_note_es, :nomenclature_note_fr, - :created_by_id, :updated_by_id, :srg_history_id + params.expect( + eu_suspension: [ + :end_date, :end_event_id, :geo_entity_id, :internal_notes, + :is_current, :notes, :start_date, :start_event_id, :eu_decision_type_id, + :taxon_concept_id, :type, :conditions_apply, :term_id, :source_id, + :nomenclature_note_en, :nomenclature_note_es, :nomenclature_note_fr, + :created_by_id, :updated_by_id, :srg_history_id + ] ) end end diff --git a/app/controllers/admin/taxon_instruments_controller.rb b/app/controllers/admin/taxon_instruments_controller.rb index 5a57d0b3d..5b307af53 100644 --- a/app/controllers/admin/taxon_instruments_controller.rb +++ b/app/controllers/admin/taxon_instruments_controller.rb @@ -67,9 +67,10 @@ def load_taxon_instruments private def taxon_instrument_params - params.require(:taxon_instrument).permit( - # attributes were in model `attr_accessible`. - :effective_from, :instrument_id, :taxon_concept_id + params.expect( + taxon_instrument: [ + :effective_from, :instrument_id, :taxon_concept_id + ] ) end end diff --git a/app/controllers/admin/taxon_listing_changes_controller.rb b/app/controllers/admin/taxon_listing_changes_controller.rb index 2d21b9eee..7fcfe020f 100644 --- a/app/controllers/admin/taxon_listing_changes_controller.rb +++ b/app/controllers/admin/taxon_listing_changes_controller.rb @@ -136,24 +136,26 @@ def load_listing_changes private def listing_change_params - params.require(:listing_change).permit( - :taxon_concept_id, :species_listing_id, :change_type_id, - :effective_at, :is_current, :parent_id, - :inclusion_taxon_concept_id, :hash_annotation_id, :event_id, - :internal_notes, - :excluded_taxon_concepts_ids, # String - :nomenclature_note_en, :nomenclature_note_es, :nomenclature_note_fr, - :created_by_id, :updated_by_id, - annotation_attributes: [ - :listing_change_id, :symbol, :parent_symbol, :short_note_en, - :full_note_en, :short_note_fr, :full_note_fr, :short_note_es, :full_note_es, - :display_in_index, :display_in_footnote, :event_id, :id, :_destroy - ], - party_listing_distribution_attributes: [ - :id, :_destroy, :geo_entity_id, :listing_change_id, :is_party - ], - geo_entity_ids: [], - excluded_geo_entities_ids: [] + params.expect( + listing_change: [ + :taxon_concept_id, :species_listing_id, :change_type_id, + :effective_at, :is_current, :parent_id, + :inclusion_taxon_concept_id, :hash_annotation_id, :event_id, + :internal_notes, + :excluded_taxon_concepts_ids, # String + :nomenclature_note_en, :nomenclature_note_es, :nomenclature_note_fr, + :created_by_id, :updated_by_id, + annotation_attributes: [ + :listing_change_id, :symbol, :parent_symbol, :short_note_en, + :full_note_en, :short_note_fr, :full_note_fr, :short_note_es, :full_note_es, + :display_in_index, :display_in_footnote, :event_id, :id, :_destroy + ], + party_listing_distribution_attributes: [ + :id, :_destroy, :geo_entity_id, :listing_change_id, :is_party + ], + geo_entity_ids: [ [] ], + excluded_geo_entities_ids: [ [] ] + ] ) end end diff --git a/app/controllers/admin/taxon_quotas_controller.rb b/app/controllers/admin/taxon_quotas_controller.rb index 8e0c3d738..2902559f5 100644 --- a/app/controllers/admin/taxon_quotas_controller.rb +++ b/app/controllers/admin/taxon_quotas_controller.rb @@ -79,16 +79,18 @@ def collection private def quota_params - params.require(:quota).permit( - :public_display, :end_date, :geo_entity_id, :is_current, - :notes, :publication_date, :quota, :type, - :start_date, :unit_id, :internal_notes, - :nomenclature_note_en, :nomenclature_note_es, :nomenclature_note_fr, - :created_by_id, :updated_by_id, :url, - :taxon_concept_id, - term_ids: [], - source_ids: [], - purpose_ids: [] + params.expect( + quota: [ + :public_display, :end_date, :geo_entity_id, :is_current, + :notes, :publication_date, :quota, :type, + :start_date, :unit_id, :internal_notes, + :nomenclature_note_en, :nomenclature_note_es, :nomenclature_note_fr, + :created_by_id, :updated_by_id, :url, + :taxon_concept_id, + term_ids: [ [] ], + source_ids: [ [] ], + purpose_ids: [ [] ] + ] ) end end diff --git a/app/controllers/admin/taxon_relationships_controller.rb b/app/controllers/admin/taxon_relationships_controller.rb index a554f72f3..e66211690 100644 --- a/app/controllers/admin/taxon_relationships_controller.rb +++ b/app/controllers/admin/taxon_relationships_controller.rb @@ -89,9 +89,11 @@ def collection private def taxon_relationship_params - params.require(:taxon_relationship).permit( - :taxon_concept_id, :other_taxon_concept_id, :taxon_relationship_type_id, - :created_by_id, :updated_by_id + params.expect( + taxon_relationship: [ + :taxon_concept_id, :other_taxon_concept_id, :taxon_relationship_type_id, + :created_by_id, :updated_by_id + ] ) end end diff --git a/app/controllers/admin/taxonomies_controller.rb b/app/controllers/admin/taxonomies_controller.rb index beffda5d6..25264f34e 100644 --- a/app/controllers/admin/taxonomies_controller.rb +++ b/app/controllers/admin/taxonomies_controller.rb @@ -25,6 +25,6 @@ def collection private def taxonomy_params - params.require(:taxonomy).permit(:name) + params.expect(taxonomy: [ :name ]) end end diff --git a/app/controllers/admin/term_trade_codes_pairs_controller.rb b/app/controllers/admin/term_trade_codes_pairs_controller.rb index 8da60e1c9..9758b327e 100644 --- a/app/controllers/admin/term_trade_codes_pairs_controller.rb +++ b/app/controllers/admin/term_trade_codes_pairs_controller.rb @@ -57,8 +57,8 @@ def collection private def term_trade_codes_pair_params - params.require(:term_trade_codes_pair).permit( - :trade_code_id, :trade_code_type, :term_id + params.expect( + term_trade_codes_pair: [ :trade_code_id, :trade_code_type, :term_id ] ) end end diff --git a/app/controllers/admin/terms_controller.rb b/app/controllers/admin/terms_controller.rb index 7fd3883ce..e98ccefff 100644 --- a/app/controllers/admin/terms_controller.rb +++ b/app/controllers/admin/terms_controller.rb @@ -29,6 +29,6 @@ def collection private def term_params - params.require(:term).permit(:code, :type, :name_en, :name_es, :name_fr) + params.expect(term: [ :code, :type, :name_en, :name_es, :name_fr ]) end end diff --git a/app/controllers/admin/trade_name_relationships_controller.rb b/app/controllers/admin/trade_name_relationships_controller.rb index 36574eea8..8858cdc7c 100644 --- a/app/controllers/admin/trade_name_relationships_controller.rb +++ b/app/controllers/admin/trade_name_relationships_controller.rb @@ -66,9 +66,11 @@ def load_trade_name_relationship_type private def trade_name_relationship_params - params.require(:taxon_relationship).permit( - :taxon_concept_id, :other_taxon_concept_id, :taxon_relationship_type_id, - :created_by_id, :updated_by_id + params.expect( + taxon_relationship: [ + :taxon_concept_id, :other_taxon_concept_id, :taxon_relationship_type_id, + :created_by_id, :updated_by_id + ] ) end end diff --git a/app/controllers/admin/units_controller.rb b/app/controllers/admin/units_controller.rb index d7ae131a3..677e91c93 100644 --- a/app/controllers/admin/units_controller.rb +++ b/app/controllers/admin/units_controller.rb @@ -29,8 +29,6 @@ def collection private def unit_params - params.require(:unit).permit( - :code, :type, :name_en, :name_es, :name_fr - ) + params.expect(unit: [ :code, :type, :name_en, :name_es, :name_fr ]) end end diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb index cc355af3b..0d310040f 100644 --- a/app/controllers/admin/users_controller.rb +++ b/app/controllers/admin/users_controller.rb @@ -59,10 +59,12 @@ def load_associations private def user_params - params.require(:user).permit( - :email, :name, :password, :password_confirmation, - :remember_me, :role, :terms_and_conditions, :is_cites_authority, - :organisation, :geo_entity_id, :is_active + params.expect( + user: [ + :email, :name, :password, :password_confirmation, + :remember_me, :role, :terms_and_conditions, :is_cites_authority, + :organisation, :geo_entity_id, :is_active + ] ) end end diff --git a/app/controllers/api/v1/eu_decisions_controller.rb b/app/controllers/api/v1/eu_decisions_controller.rb index 153fb126b..8bebd348a 100644 --- a/app/controllers/api/v1/eu_decisions_controller.rb +++ b/app/controllers/api/v1/eu_decisions_controller.rb @@ -4,6 +4,7 @@ class Api::V1::EuDecisionsController < ApplicationController def index @eu_decisions = eu_decision_search(sanitized_params) + render json: @eu_decisions, each_serializer: CaptiveBreeding::EuDecisionSerializer end diff --git a/app/controllers/api/v1/shipments_controller.rb b/app/controllers/api/v1/shipments_controller.rb index f03beab1d..77aadc0d6 100644 --- a/app/controllers/api/v1/shipments_controller.rb +++ b/app/controllers/api/v1/shipments_controller.rb @@ -10,7 +10,7 @@ class Api::V1::ShipmentsController < ApplicationController def chart_query @chart_data = Rails.cache.fetch( - [ 'chart_data', permit_params ], + [ 'chart_data', params_unsafely_permitted ], expires_in: 1.week ) do @grouping_class.new([ 'issue_type', 'year' ]). @@ -77,29 +77,29 @@ def country_query # Compliance tool search & full list action def search_query - query = @grouping_class.new(sanitized_attributes, permit_params) + query = @grouping_class.new(sanitized_attributes, params_unsafely_permitted) data = query.run @search_data = Rails.cache.fetch( - [ 'search_data', permit_params ], + [ 'search_data', params_unsafely_permitted ], expires_in: 1.week ) do - query.build_hash(data, permit_params) + query.build_hash(data, params_unsafely_permitted) end - @filtered_data = query.filter(@search_data, permit_params) + @filtered_data = query.filter(@search_data, params_unsafely_permitted) render json: Kaminari.paginate_array(@filtered_data).page(params[:page]).per(params[:per_page]), - meta: metadata(@filtered_data, permit_params) + meta: metadata(@filtered_data, params_unsafely_permitted) end def over_time_query # TODO Remember to implement permitted parameters here - query = @grouping_class.new(sanitized_attributes, permit_params) + query = @grouping_class.new(sanitized_attributes, params_unsafely_permitted) @over_time_data = Rails.cache.fetch( - [ 'over_time_data', permit_params ], expires_in: 1.week + [ 'over_time_data', params_unsafely_permitted ], expires_in: 1.week ) do query.over_time_data end @@ -110,11 +110,11 @@ def over_time_query # TODO refactor to merge this method and the over_time one above together def aggregated_over_time_query # TODO Remember to implement permitted parameters here - query = @grouping_class.new(sanitized_attributes, permit_params) + query = @grouping_class.new(sanitized_attributes, params_unsafely_permitted) @aggregated_over_time_data = Rails.cache.fetch( - [ 'aggregated_over_time_data', permit_params ], + [ 'aggregated_over_time_data', params_unsafely_permitted ], expires_in: 1.week ) do query.aggregated_over_time_data @@ -126,7 +126,7 @@ def aggregated_over_time_query def download_data @download_data = Rails.cache.fetch( - [ 'download_data', permit_params ], expires_in: 1.week + [ 'download_data', params_unsafely_permitted ], expires_in: 1.week ) do Trade::DownloadDataRetriever.dashboard_download(download_params).to_a end @@ -136,7 +136,7 @@ def download_data def search_download_data @download_data = Rails.cache.fetch( - [ 'search_download_data', permit_params ], expires_in: 1.week + [ 'search_download_data', params_unsafely_permitted ], expires_in: 1.week ) do Trade::DownloadDataRetriever.search_download(download_params).to_a end @@ -145,15 +145,15 @@ def search_download_data end def search_download_all_data - query = @grouping_class.new(sanitized_attributes, permit_params) + query = @grouping_class.new(sanitized_attributes, params_unsafely_permitted) data = query.run @search_download_all_data = Rails.cache.fetch( - [ 'search_download_all_data', permit_params ], expires_in: 1.week + [ 'search_download_all_data', params_unsafely_permitted ], expires_in: 1.week ) do - search_data = query.build_hash(data, permit_params) - filtered_data = query.filter(search_data, permit_params) - data_ids = query.filter_download_data(filtered_data, permit_params) + search_data = query.build_hash(data, params_unsafely_permitted) + filtered_data = query.filter(search_data, params_unsafely_permitted) + data_ids = query.filter_download_data(filtered_data, params_unsafely_permitted) hash_params = params_hash_builder(data_ids, download_params) Trade::DownloadDataRetriever.search_download(hash_params).to_a @@ -164,7 +164,7 @@ def search_download_all_data private - def permit_params + def params_unsafely_permitted params.permit! end diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 815be2559..dbb08db6e 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -1,4 +1,7 @@ class ApplicationController < ActionController::Base + # Only allow modern browsers supporting webp images, web push, badges, import maps, CSS nesting, and CSS :has. + allow_browser versions: :modern + before_action :track_who_does_it_current_user before_action :set_locale before_action :configure_permitted_parameters, if: :devise_controller? diff --git a/app/controllers/checklist/downloads_controller.rb b/app/controllers/checklist/downloads_controller.rb index ebcea1317..6b790a06f 100644 --- a/app/controllers/checklist/downloads_controller.rb +++ b/app/controllers/checklist/downloads_controller.rb @@ -30,10 +30,11 @@ def show # POST downloads/ def create @download = Download.create(download_params) + if download_params[:doc_type] == 'citesidmanual' - ManualDownloadWorker.perform_async(@download.id, params.dup.permit!.to_h) + ManualDownloadWorker.perform_async(@download.id, params.dup.permit!.as_json) else - DownloadWorker.perform_async(@download.id, params.dup.permit!.to_h) + DownloadWorker.perform_async(@download.id, params.dup.permit!.as_json) end @download = @download.attributes.except('filename', 'path') @@ -103,11 +104,12 @@ def not_found end def download_params - params.require(:download).permit( - # attributes used in this controller. - :doc_type, - # other attributes were in model `attr_accessible`. - :format + params.expect( + download: [ + # attributes used in this controller. + :doc_type, + :format + ] ) end diff --git a/app/controllers/registrations_controller.rb b/app/controllers/registrations_controller.rb index 1ffd40f89..5bcf961c1 100644 --- a/app/controllers/registrations_controller.rb +++ b/app/controllers/registrations_controller.rb @@ -37,12 +37,13 @@ def needs_password? end def user_params - params.require(:user).permit( - # attributes needed by Devise and/or used in this controller. - :name, :email, :password, :password_confirmation, :current_password, - # other attributes were in model `attr_accessible`. - :remember_me, :role, :terms_and_conditions, :is_cites_authority, - :organisation, :geo_entity_id, :is_active + params.expect( + user: [ + # attributes needed by Devise and/or used in this controller. + :name, :email, :password, :password_confirmation, :current_password, + :remember_me, :role, :terms_and_conditions, :is_cites_authority, + :organisation, :geo_entity_id, :is_active + ] ) end end diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb index 488e67417..7609d2a8d 100644 --- a/app/controllers/sessions_controller.rb +++ b/app/controllers/sessions_controller.rb @@ -38,6 +38,6 @@ def invalid_login_attempt private def user_params - params.require(:user).permit(:email, :password) + params.expect(user: [ :email, :password ]) end end diff --git a/app/controllers/trade/annual_report_uploads_controller.rb b/app/controllers/trade/annual_report_uploads_controller.rb index 56b93ff5a..4e160ed7f 100644 --- a/app/controllers/trade/annual_report_uploads_controller.rb +++ b/app/controllers/trade/annual_report_uploads_controller.rb @@ -55,25 +55,31 @@ def destroy private def annual_report_upload_params - params.require(:annual_report_upload).permit( - :csv_source_file, :trading_country_id, :point_of_view, - sandbox_shipments: [ - :id, - :appendix, - :taxon_name, - :term_code, - :quantity, - :unit_code, - :trading_partner, - :country_of_origin, - :import_permit, - :export_permit, - :origin_permit, - :ifs_permit, - :purpose_code, - :source_code, - :year, - :_destroyed + params.expect( + annual_report_upload: [ + :csv_source_file, + :trading_country_id, + :point_of_view, + sandbox_shipments: [ + [ + :id, + :appendix, + :taxon_name, + :term_code, + :quantity, + :unit_code, + :trading_partner, + :country_of_origin, + :import_permit, + :export_permit, + :origin_permit, + :ifs_permit, + :purpose_code, + :source_code, + :year, + :_destroyed + ] + ] ] ) end diff --git a/app/controllers/trade/sandbox_shipments_controller.rb b/app/controllers/trade/sandbox_shipments_controller.rb index 739212c4c..ca12cbc75 100644 --- a/app/controllers/trade/sandbox_shipments_controller.rb +++ b/app/controllers/trade/sandbox_shipments_controller.rb @@ -39,15 +39,15 @@ def update_batch sandbox_klass = Trade::SandboxTemplate.ar_klass(aru.sandbox.table_name) sandbox_klass.update_batch( - update_batch_params[:updates], ve, aru + update_batch_params, ve, aru ) head :no_content end def destroy_batch - aru = Trade::AnnualReportUpload.find(params[:annual_report_upload_id]) - ve = Trade::ValidationError.find(params[:validation_error_id]) + aru = Trade::AnnualReportUpload.find(destroy_batch_params[:annual_report_upload_id]) + ve = Trade::ValidationError.find(destroy_batch_params[:validation_error_id]) sandbox_klass = Trade::SandboxTemplate.ar_klass(aru.sandbox.table_name) sandbox_klass.destroy_batch(ve, aru) @@ -59,22 +59,23 @@ def destroy_batch private def sandbox_shipment_params - params.require(:sandbox_shipment).permit(*sandbox_shipment_attributes) + params.expect(sandbox_shipment: sandbox_shipment_attribute_names) end def destroy_batch_params - params.permit(:annual_report_upload_id, :validation_error_id) + params.permit( + :annual_report_upload_id, + :validation_error_id + ) end def update_batch_params - params.permit( - :annual_report_upload_id, - :validation_error_id, - updates: sandbox_shipment_attributes + params.expect( + updates: sandbox_shipment_attribute_names ) end - def sandbox_shipment_attributes + def sandbox_shipment_attribute_names [ :appendix, :taxon_concept_id, diff --git a/app/controllers/trade/shipments_controller.rb b/app/controllers/trade/shipments_controller.rb index 9759c68bd..ecfef1832 100644 --- a/app/controllers/trade/shipments_controller.rb +++ b/app/controllers/trade/shipments_controller.rb @@ -84,11 +84,7 @@ def shipment_attributes end def shipment_params - params.require(:shipment).permit( - *(shipment_attributes + [ - :ignore_warnings - ]) - ) + params.expect(shipment: shipment_attributes + [ :ignore_warnings ]) end def batch_update_params diff --git a/app/controllers/trade/validation_errors_controller.rb b/app/controllers/trade/validation_errors_controller.rb index 7c3dee610..ac773ceb2 100644 --- a/app/controllers/trade/validation_errors_controller.rb +++ b/app/controllers/trade/validation_errors_controller.rb @@ -19,8 +19,6 @@ def update private def validation_error_params - params.require(:validation_error).permit( - :is_ignored - ) + params.expect(validation_error: [ :is_ignored ]) end end diff --git a/app/models/ahoy/visit.rb b/app/models/ahoy/visit.rb index 648a62e0e..fb3bfdeca 100644 --- a/app/models/ahoy/visit.rb +++ b/app/models/ahoy/visit.rb @@ -48,6 +48,8 @@ class Visit < ApplicationRecord # (https://github.com/ankane/ahoy/blob/v1.0.1/lib/generators/ahoy/stores/templates/active_record_visits_migration.rb) # However it has changed since version 1.4.0, from `id` to `visit_token`, and from `visitor_id` to `visitor_token`. # (https://github.com/ankane/ahoy/blob/v1.4.0/lib/generators/ahoy/stores/templates/active_record_visits_migration.rb) + # Note that this will bypass custom methods on the original attribute, which thankfully we don't have: + # (https://guides.rubyonrails.org/upgrading_ruby_on_rails.html#alias-attribute-now-bypasses-custom-methods-on-the-original-attribute) alias_attribute :visit_token, :id alias_attribute :visitor_token, :visitor_id end diff --git a/app/models/analytics_event.rb b/app/models/analytics_event.rb index b4439e36e..09482aa58 100644 --- a/app/models/analytics_event.rb +++ b/app/models/analytics_event.rb @@ -17,5 +17,4 @@ class AnalyticsEvent < ApplicationRecord validates :event_type, inclusion: { in: EVENT_TYPES }, presence: true # Used by `download_db` in app/controllers/cites_trade/home_controller.rb - # attr_accessible :event_name, :event_type end diff --git a/app/models/annotation.rb b/app/models/annotation.rb index 6eb7885b9..e1c257e93 100644 --- a/app/models/annotation.rb +++ b/app/models/annotation.rb @@ -41,11 +41,6 @@ class Annotation < ApplicationRecord extend Mobility include TrackWhoDoesIt - # Migrated to controller (Strong Parameters) - # attr_accessible :listing_change_id, :symbol, :parent_symbol, :short_note_en, - # :full_note_en, :short_note_fr, :full_note_fr, :short_note_es, :full_note_es, - # :display_in_index, :display_in_footnote, :event_id - has_many :listing_changes, dependent: :nullify diff --git a/app/models/change_type.rb b/app/models/change_type.rb index 16a8b3083..6ec612b94 100644 --- a/app/models/change_type.rb +++ b/app/models/change_type.rb @@ -27,9 +27,6 @@ class ChangeType < ApplicationRecord include Deletable extend Mobility - # Migrated to controller (Strong Parameters) - # attr_accessible :name, :display_name_en, :display_name_es, :display_name_fr, - # :designation_id include Dictionary build_dictionary :addition, :deletion, :reservation, :reservation_withdrawal, :exception diff --git a/app/models/cites_process.rb b/app/models/cites_process.rb index 1c722081a..b2f0f7a32 100644 --- a/app/models/cites_process.rb +++ b/app/models/cites_process.rb @@ -25,10 +25,6 @@ # class CitesProcess < ApplicationRecord include TrackWhoDoesIt - # Migrated to controller (Strong Parameters) - # attr_accessible :start_event_id, :geo_entity_id, :resolution, :start_date, - # :taxon_concept_id, :notes, :status, :document, :document_title, - # :created_by_id, :updated_by_id belongs_to :taxon_concept belongs_to :geo_entity diff --git a/app/models/cites_processes/cites_rst_process.rb b/app/models/cites_processes/cites_rst_process.rb index df47db665..53de70db4 100644 --- a/app/models/cites_processes/cites_rst_process.rb +++ b/app/models/cites_processes/cites_rst_process.rb @@ -25,7 +25,6 @@ # class CitesRstProcess < CitesProcess # Used by lib/modules/import/rst/importer.rb - # attr_accessible :case_id STATUS = [ 'Initiated', 'Ongoing', 'Retained', 'Trade Suspension', 'Closed' ] diff --git a/app/models/cites_suspension_confirmation.rb b/app/models/cites_suspension_confirmation.rb index ec7722c4e..df7be0c3f 100644 --- a/app/models/cites_suspension_confirmation.rb +++ b/app/models/cites_suspension_confirmation.rb @@ -20,8 +20,6 @@ # class CitesSuspensionConfirmation < ApplicationRecord - # Migrated to controller (Strong Parameters) - # attr_accessible :cites_suspension_notification_id belongs_to :confirmation_notification, class_name: 'CitesSuspensionNotification', foreign_key: :cites_suspension_notification_id belongs_to :confirmed_suspension, class_name: 'CitesSuspension', diff --git a/app/models/cms_mapping.rb b/app/models/cms_mapping.rb index 7e9af5305..cddd9bef8 100644 --- a/app/models/cms_mapping.rb +++ b/app/models/cms_mapping.rb @@ -14,9 +14,6 @@ # class CmsMapping < ApplicationRecord - # Migrated to controller (Strong Parameters) - # attr_accessible :accepted_name_id, :cms_author, :cms_taxon_name, :cms_uuid, :details, :taxon_concept_id - # serialize :details, coder: ActiveRecord::Coders::Hstore belongs_to :taxon_concept, optional: true belongs_to :accepted_name, class_name: 'TaxonConcept', optional: true diff --git a/app/models/comment.rb b/app/models/comment.rb index 22e649e71..01c993c49 100644 --- a/app/models/comment.rb +++ b/app/models/comment.rb @@ -26,9 +26,6 @@ class Comment < ApplicationRecord include TrackWhoDoesIt - # Migrated to controller (Strong Parameters) - # attr_accessible :comment_type, :commentable_id, :commentable_type, :note, - # :created_by_id, :updated_by_id belongs_to :commentable, polymorphic: true end diff --git a/app/models/common_name.rb b/app/models/common_name.rb index de9c33908..e6d3ec38a 100644 --- a/app/models/common_name.rb +++ b/app/models/common_name.rb @@ -26,9 +26,6 @@ class CommonName < ApplicationRecord include TrackWhoDoesIt - # Used by app/models/taxon_common.rb - # attr_accessible :language_id, :name, - # :created_by_id, :updated_by_id belongs_to :language diff --git a/app/models/designation.rb b/app/models/designation.rb index 0b176c263..e82e12d30 100644 --- a/app/models/designation.rb +++ b/app/models/designation.rb @@ -19,10 +19,9 @@ # class Designation < ApplicationRecord - # Migrated to controller (Strong Parameters) - # attr_accessible :name, :taxonomy_id include Deletable include Dictionary + build_dictionary :cites, :eu, :cms validates :name, presence: true, uniqueness: true diff --git a/app/models/designation_geo_entity.rb b/app/models/designation_geo_entity.rb index c95dd43c4..e02fe2186 100644 --- a/app/models/designation_geo_entity.rb +++ b/app/models/designation_geo_entity.rb @@ -21,7 +21,6 @@ class DesignationGeoEntity < ApplicationRecord # Relationship table between Designation and GeoEntity. - # attr_accessible :designation_id, :geo_entity_id belongs_to :designation belongs_to :geo_entity end diff --git a/app/models/distribution.rb b/app/models/distribution.rb index 20c0ccb36..180a9fbf3 100644 --- a/app/models/distribution.rb +++ b/app/models/distribution.rb @@ -31,9 +31,6 @@ class Distribution < ApplicationRecord include Changeable include TrackWhoDoesIt - # Migrated to controller (Strong Parameters) - # attr_accessible :geo_entity_id, :taxon_concept_id, :tag_list, - # :references_attributes, :internal_notes, :created_by_id, :updated_by_id acts_as_taggable diff --git a/app/models/distribution_reference.rb b/app/models/distribution_reference.rb index 2a422699f..3fce8efbe 100644 --- a/app/models/distribution_reference.rb +++ b/app/models/distribution_reference.rb @@ -29,8 +29,6 @@ class DistributionReference < ApplicationRecord include TrackWhoDoesIt # Used by app/models/cms_mapping_manager.rb - # attr_accessible :reference_id, :distribution_id, :created_by_id, - # :updated_by_id belongs_to :reference belongs_to :distribution, touch: true diff --git a/app/models/document.rb b/app/models/document.rb index 5ef7d16cc..90158b3a5 100644 --- a/app/models/document.rb +++ b/app/models/document.rb @@ -56,27 +56,27 @@ class Document < ApplicationRecord include PgSearch::Model ACCEPTED_CONTENT_TYPES = [ - "image/jpeg", # jpg - "image/jpeg", # jpeg - "image/gif", # gif - "image/png", # png - "image/bmp", # bmp - "image/tiff", # tif - "image/tiff", # tiff - "application/vnd.ms-powerpoint", # ppt - "application/vnd.openxmlformats-officedocument.presentationml.presentation", # pptx - "application/vnd.ms-excel", # xls - "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", # xlsx - "application/rtf", # rtf - "text/plain", # txt - "application/msword", # doc - "application/vnd.openxmlformats-officedocument.wordprocessingml.document", # docx - "application/pdf", # pdf - "text/csv", # csv - "text/tab-separated-values", # tsv - "application/vnd.oasis.opendocument.text", # odt - "application/vnd.oasis.opendocument.spreadsheet", # ods - "application/vnd.oasis.opendocument.presentation" # odp + 'image/jpeg', # jpg + 'image/jpeg', # jpeg + 'image/gif', # gif + 'image/png', # png + 'image/bmp', # bmp + 'image/tiff', # tif + 'image/tiff', # tiff + 'application/vnd.ms-powerpoint', # ppt + 'application/vnd.openxmlformats-officedocument.presentationml.presentation', # pptx + 'application/vnd.ms-excel', # xls + 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', # xlsx + 'application/rtf', # rtf + 'text/plain', # txt + 'application/msword', # doc + 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', # docx + 'application/pdf', # pdf + 'text/csv', # csv + 'text/tab-separated-values', # tsv + 'application/vnd.oasis.opendocument.text', # odt + 'application/vnd.oasis.opendocument.spreadsheet', # ods + 'application/vnd.oasis.opendocument.presentation' # odp ].freeze pg_search_scope :search_by_title, against: :title, @@ -85,12 +85,6 @@ class Document < ApplicationRecord include TrackWhoDoesIt - # Migrated to controller (Strong Parameters) - # attr_accessible :event_id, :file, :date, :type, :title, :is_public, - # :language_id, :citations_attributes, - # :sort_index, :discussion_id, :discussion_sort_index, - # :primary_language_document_id, - # :designation_id belongs_to :designation, optional: true belongs_to :event, optional: true belongs_to :language, optional: true diff --git a/app/models/document/proposal.rb b/app/models/document/proposal.rb index 4c73ad029..55c58560d 100644 --- a/app/models/document/proposal.rb +++ b/app/models/document/proposal.rb @@ -55,7 +55,6 @@ def self.display_name end # Used in Document Controller - # attr_accessible :proposal_details_attributes has_one :proposal_details, class_name: 'ProposalDetails', diff --git a/app/models/document/review_of_significant_trade.rb b/app/models/document/review_of_significant_trade.rb index c4123133b..879a6cf89 100644 --- a/app/models/document/review_of_significant_trade.rb +++ b/app/models/document/review_of_significant_trade.rb @@ -55,11 +55,11 @@ def self.display_name end # Used by DocumentsController. - # attr_accessible :review_details_attributes has_one :review_details, class_name: 'ReviewDetails', foreign_key: 'document_id', dependent: :destroy + accepts_nested_attributes_for :review_details, allow_destroy: true end diff --git a/app/models/document_citation.rb b/app/models/document_citation.rb index e5b3d478f..5ad812cd9 100644 --- a/app/models/document_citation.rb +++ b/app/models/document_citation.rb @@ -25,8 +25,6 @@ class DocumentCitation < ApplicationRecord include TrackWhoDoesIt - # Migrated to controller (Strong Parameters) - # attr_accessible :document_id, :stringy_taxon_concept_ids, :geo_entity_ids has_many :document_citation_taxon_concepts, dependent: :destroy, autosave: true has_many :taxon_concepts, through: :document_citation_taxon_concepts diff --git a/app/models/document_citation_geo_entity.rb b/app/models/document_citation_geo_entity.rb index db6e832a3..f50ec852f 100644 --- a/app/models/document_citation_geo_entity.rb +++ b/app/models/document_citation_geo_entity.rb @@ -29,7 +29,6 @@ class DocumentCitationGeoEntity < ApplicationRecord include TrackWhoDoesIt # Used by app/models/nomenclature_change/reassignment_copy_processor.rb and lib/tasks/elibrary/identification_docs_distributions_importer.rb - # attr_accessible :created_by_id, :document_citation_id, :geo_entity_id, :updated_by_id belongs_to :geo_entity belongs_to :document_citation, touch: true diff --git a/app/models/document_citation_taxon_concept.rb b/app/models/document_citation_taxon_concept.rb index e81729703..868573fc3 100644 --- a/app/models/document_citation_taxon_concept.rb +++ b/app/models/document_citation_taxon_concept.rb @@ -29,8 +29,6 @@ class DocumentCitationTaxonConcept < ApplicationRecord include TrackWhoDoesIt # Used by other models, not controllers. - # attr_accessible :created_by_id, :document_citation_id, :taxon_concept_id, :updated_by_id, - # :updated_at belongs_to :taxon_concept belongs_to :document_citation, touch: true diff --git a/app/models/document_tag.rb b/app/models/document_tag.rb index 3d7d74b89..82edd8dc6 100644 --- a/app/models/document_tag.rb +++ b/app/models/document_tag.rb @@ -11,7 +11,6 @@ class DocumentTag < ApplicationRecord # Only created by seed and rake task. - # attr_accessible :name has_and_belongs_to_many :documents diff --git a/app/models/download.rb b/app/models/download.rb index a979889a9..27a617d76 100644 --- a/app/models/download.rb +++ b/app/models/download.rb @@ -14,9 +14,6 @@ # class Download < ApplicationRecord - # Migrated to controller (Strong Parameters) - # attr_accessible :format, :doc_type - validates :format, presence: true, inclusion: { in: %w[pdf csv json zip] } validates :doc_type, presence: true, inclusion: { in: %w[history index citesidmanual] } diff --git a/app/models/eu_country_date.rb b/app/models/eu_country_date.rb index ddb7ab293..b21bf7844 100644 --- a/app/models/eu_country_date.rb +++ b/app/models/eu_country_date.rb @@ -11,7 +11,6 @@ # class EuCountryDate < ApplicationRecord # Used by rake task. - # attr_accessible :eu_accession_year, :eu_exit_year, :geo_entity belongs_to :geo_entity validates :eu_accession_year, presence: true diff --git a/app/models/eu_decision.rb b/app/models/eu_decision.rb index 75a02317e..885ccabef 100644 --- a/app/models/eu_decision.rb +++ b/app/models/eu_decision.rb @@ -61,12 +61,6 @@ class EuDecision < ApplicationRecord include Changeable extend Mobility include TrackWhoDoesIt - # Migrated to controller (Strong Parameters) - # attr_accessible :end_date, :end_event_id, :geo_entity_id, :internal_notes, - # :is_current, :notes, :start_date, :start_event_id, :eu_decision_type_id, - # :taxon_concept_id, :type, :conditions_apply, :term_id, :source_id, - # :nomenclature_note_en, :nomenclature_note_es, :nomenclature_note_fr, - # :created_by_id, :updated_by_id, :srg_history_id belongs_to :taxon_concept belongs_to :m_taxon_concept, foreign_key: :taxon_concept_id, optional: true diff --git a/app/models/eu_decision_confirmation.rb b/app/models/eu_decision_confirmation.rb index 935d96213..d4e1577bf 100644 --- a/app/models/eu_decision_confirmation.rb +++ b/app/models/eu_decision_confirmation.rb @@ -21,7 +21,6 @@ class EuDecisionConfirmation < ApplicationRecord # Relationship table between Event and EuDecision - # attr_accessible :eu_decision_id, :event_id belongs_to :eu_decision belongs_to :event diff --git a/app/models/eu_decision_type.rb b/app/models/eu_decision_type.rb index 7e15fa08a..db1bbc0e9 100644 --- a/app/models/eu_decision_type.rb +++ b/app/models/eu_decision_type.rb @@ -16,10 +16,8 @@ class EuDecisionType < ApplicationRecord include Deletable - - # Migrated to controller (Strong Parameters) - # attr_accessible :name, :tooltip, :decision_type include Dictionary + build_dictionary :negative_opinion, :positive_opinion, :no_opinion, :suspension, :srg_referral diff --git a/app/models/eu_decisions/eu_opinion.rb b/app/models/eu_decisions/eu_opinion.rb index da567d566..c4a1fabb1 100644 --- a/app/models/eu_decisions/eu_opinion.rb +++ b/app/models/eu_decisions/eu_opinion.rb @@ -56,9 +56,6 @@ # class EuOpinion < EuDecision - # Migrated to controller (Strong Parameters) - # attr_accessible :document_id - belongs_to :document, optional: true validates :start_date, presence: true diff --git a/app/models/event.rb b/app/models/event.rb index 2c086ae72..4c8014e8c 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -42,11 +42,6 @@ class Event < ApplicationRecord include TrackWhoDoesIt - # Migrated to controller (Strong Parameters) - # attr_accessible :name, :designation_id, :description, :extended_description, - # :url, :private_url, :multilingual_url, :published_at, :effective_at, :is_current, :end_date, - # :created_by_id, :updated_by_id - attr_reader :effective_at_formatted belongs_to :designation, optional: true diff --git a/app/models/events/cites_ac.rb b/app/models/events/cites_ac.rb index 566db8ff9..200c1a887 100644 --- a/app/models/events/cites_ac.rb +++ b/app/models/events/cites_ac.rb @@ -42,9 +42,6 @@ # Cites Animal Committee class CitesAc < Event - # Migrated to controller (Strong Parameters) - # attr_accessible :is_current - validates :effective_at, presence: true def self.elibrary_document_types diff --git a/app/models/events/cites_cop.rb b/app/models/events/cites_cop.rb index 736cc32df..c510c8c19 100644 --- a/app/models/events/cites_cop.rb +++ b/app/models/events/cites_cop.rb @@ -42,9 +42,6 @@ class CitesCop < Event include Deletable - # Migrated to controller (Strong Parameters) - # attr_accessible :is_current - ## # The only time we would delete a CoP/EU regulation is just after we've # created it by mistake, but we don't want to be able to delete the CoP diff --git a/app/models/events/cites_extraordinary_meeting.rb b/app/models/events/cites_extraordinary_meeting.rb index 35be0fe72..a743c1f76 100644 --- a/app/models/events/cites_extraordinary_meeting.rb +++ b/app/models/events/cites_extraordinary_meeting.rb @@ -40,9 +40,6 @@ # class CitesExtraordinaryMeeting < Event - # Migrated to controller (Strong Parameters) - # attr_accessible :is_current - validates :effective_at, presence: true def self.elibrary_document_types diff --git a/app/models/events/cites_pc.rb b/app/models/events/cites_pc.rb index 036a098b5..9133902e1 100644 --- a/app/models/events/cites_pc.rb +++ b/app/models/events/cites_pc.rb @@ -42,9 +42,6 @@ # Cites Plant Committee class CitesPc < Event - # Migrated to controller (Strong Parameters) - # attr_accessible :is_current - validates :effective_at, presence: true def self.elibrary_document_types diff --git a/app/models/events/cites_suspension_notification.rb b/app/models/events/cites_suspension_notification.rb index f92099122..f8aaf0376 100644 --- a/app/models/events/cites_suspension_notification.rb +++ b/app/models/events/cites_suspension_notification.rb @@ -41,9 +41,9 @@ class CitesSuspensionNotification < Event include Deletable - # Migrated to controller (Strong Parameters) - # attr_accessible :subtype, :new_subtype, :end_date + attr_accessor :new_subtype + has_many :started_suspensions, foreign_key: :start_notification_id, class_name: 'CitesSuspension' has_many :ended_suspensions, foreign_key: :end_notification_id, class_name: 'CitesSuspension' has_many :cites_suspension_confirmations, dependent: :destroy diff --git a/app/models/events/cites_tc.rb b/app/models/events/cites_tc.rb index dc234d293..c2c9cc62f 100644 --- a/app/models/events/cites_tc.rb +++ b/app/models/events/cites_tc.rb @@ -42,9 +42,6 @@ # Cites Tech Committee class CitesTc < Event - # Migrated to controller (Strong Parameters) - # attr_accessible :is_current - validates :effective_at, presence: true before_destroy :check_for_documents diff --git a/app/models/events/ec_srg.rb b/app/models/events/ec_srg.rb index b713eceef..e148f668f 100644 --- a/app/models/events/ec_srg.rb +++ b/app/models/events/ec_srg.rb @@ -42,9 +42,6 @@ # European Commission Scientific Review Group class EcSrg < Event - # Migrated to controller (Strong Parameters) - # attr_accessible :is_current - has_many :eu_opinions, foreign_key: :start_event_id, dependent: :nullify diff --git a/app/models/events/eu_regulation.rb b/app/models/events/eu_regulation.rb index d99c6a744..aa4e2a28b 100644 --- a/app/models/events/eu_regulation.rb +++ b/app/models/events/eu_regulation.rb @@ -42,8 +42,6 @@ class EuRegulation < EuEvent include Deletable - # Migrated to controller (Strong Parameters) - # attr_accessible :listing_changes_event_id, :end_date attr_accessor :listing_changes_event_id ## diff --git a/app/models/events/eu_suspension_regulation.rb b/app/models/events/eu_suspension_regulation.rb index 4c2dd1ab2..6c821ae62 100644 --- a/app/models/events/eu_suspension_regulation.rb +++ b/app/models/events/eu_suspension_regulation.rb @@ -42,8 +42,6 @@ class EuSuspensionRegulation < EuEvent include Deletable - # Migrated to controller (Strong Parameters) - # attr_accessible :eu_suspensions_event_id attr_accessor :eu_suspensions_event_id # Because EuSuspensionRegulation events are created with many suspensions diff --git a/app/models/geo_entity.rb b/app/models/geo_entity.rb index 5578895e0..11cd63e47 100644 --- a/app/models/geo_entity.rb +++ b/app/models/geo_entity.rb @@ -31,10 +31,6 @@ class GeoEntity < ApplicationRecord include Changeable include Deletable extend Mobility - # Migrated to controller (Strong Parameters) - # attr_accessible :geo_entity_type_id, :iso_code2, :iso_code3, - # :legacy_id, :legacy_type, :long_name, :name_en, :name_es, :name_fr, - # :is_current translates :name belongs_to :geo_entity_type has_many :geo_relationships diff --git a/app/models/geo_entity_type.rb b/app/models/geo_entity_type.rb index 7f4a186d6..81c98452a 100644 --- a/app/models/geo_entity_type.rb +++ b/app/models/geo_entity_type.rb @@ -10,7 +10,6 @@ class GeoEntityType < ApplicationRecord # Look like the only place create GeoEntityType is lib/tasks/import_trade_shipments.rake - # attr_accessible :name include Dictionary build_dictionary :country, :cites_region, :region, :territory, diff --git a/app/models/geo_relationship.rb b/app/models/geo_relationship.rb index 28a141902..60594bf60 100644 --- a/app/models/geo_relationship.rb +++ b/app/models/geo_relationship.rb @@ -23,8 +23,6 @@ # class GeoRelationship < ApplicationRecord - # Migrated to controller (Strong Parameters) - # attr_accessible :geo_entity_id, :geo_relationship_type_id, :other_geo_entity_id belongs_to :geo_relationship_type belongs_to :geo_entity belongs_to :related_geo_entity, class_name: 'GeoEntity', foreign_key: :other_geo_entity_id diff --git a/app/models/geo_relationship_type.rb b/app/models/geo_relationship_type.rb index 6a78f96a4..eadb44e74 100644 --- a/app/models/geo_relationship_type.rb +++ b/app/models/geo_relationship_type.rb @@ -10,7 +10,6 @@ class GeoRelationshipType < ApplicationRecord # Used by seed only. - # attr_accessible :name include Dictionary build_dictionary :contains, :intersects diff --git a/app/models/instrument.rb b/app/models/instrument.rb index 8e5053c56..4dcd41a4a 100644 --- a/app/models/instrument.rb +++ b/app/models/instrument.rb @@ -21,8 +21,6 @@ class Instrument < ApplicationRecord include Deletable - # Migrated to controller (Strong Parameters) - # attr_accessible :designation_id, :name validates :name, presence: true, uniqueness: { scope: :designation_id } diff --git a/app/models/iucn_mapping.rb b/app/models/iucn_mapping.rb index 4df57339d..e8cc3e13a 100644 --- a/app/models/iucn_mapping.rb +++ b/app/models/iucn_mapping.rb @@ -16,8 +16,6 @@ class IucnMapping < ApplicationRecord # Used by IucnMappingManager - # attr_accessible :iucn_author, :iucn_category, :iucn_taxon_id, - # :iucn_taxon_name, :taxon_concept_id, :details, :accepted_name_id # serialize :details, coder: ActiveRecord::Coders::Hstore belongs_to :taxon_concept diff --git a/app/models/language.rb b/app/models/language.rb index 87f48a102..d0c0c08e2 100644 --- a/app/models/language.rb +++ b/app/models/language.rb @@ -21,8 +21,7 @@ class Language < ApplicationRecord include Changeable include Deletable extend Mobility - # Migrated to controller (Strong Parameters) - # attr_accessible :iso_code1, :iso_code3, :name_en, :name_fr, :name_es + translates :name has_many :common_names diff --git a/app/models/listing_change.rb b/app/models/listing_change.rb index 8bc47e2c2..70bb9d0ce 100644 --- a/app/models/listing_change.rb +++ b/app/models/listing_change.rb @@ -61,14 +61,6 @@ class ListingChange < ApplicationRecord include Changeable extend Mobility include TrackWhoDoesIt - # Migrated to controller (Strong Parameters) - # attr_accessible :taxon_concept_id, :species_listing_id, :change_type_id, - # :effective_at, :is_current, :parent_id, :geo_entity_ids, - # :party_listing_distribution_attributes, :inclusion_taxon_concept_id, - # :annotation_attributes, :hash_annotation_id, :event_id, - # :excluded_geo_entities_ids, :excluded_taxon_concepts_ids, :internal_notes, - # :nomenclature_note_en, :nomenclature_note_es, :nomenclature_note_fr, - # :created_by_id, :updated_by_id attr_accessor :excluded_geo_entities_ids, # Array :excluded_taxon_concepts_ids # String diff --git a/app/models/listing_distribution.rb b/app/models/listing_distribution.rb index 05a466d53..baa585d19 100644 --- a/app/models/listing_distribution.rb +++ b/app/models/listing_distribution.rb @@ -32,8 +32,6 @@ class ListingDistribution < ApplicationRecord include TrackWhoDoesIt - # Migrated to controller (Strong Parameters) - # attr_accessible :geo_entity_id, :listing_change_id, :is_party belongs_to :geo_entity belongs_to :listing_change, inverse_of: :listing_distributions diff --git a/app/models/nomenclature_change.rb b/app/models/nomenclature_change.rb index 3045e7e54..a54fc7525 100644 --- a/app/models/nomenclature_change.rb +++ b/app/models/nomenclature_change.rb @@ -32,9 +32,6 @@ class NomenclatureChange < ApplicationRecord include TrackWhoDoesIt - # Migrated to controller (Strong Parameters) - # attr_accessible :event_id, :status - belongs_to :event, optional: true validates :status, presence: true @@ -69,16 +66,20 @@ def submit end def cannot_update_when_locked - if status_was == NomenclatureChange::CLOSED || - (status_was == NomenclatureChange::SUBMITTED && - status != NomenclatureChange::CLOSED) + if status_was == NomenclatureChange::CLOSED || ( + status_was == NomenclatureChange::SUBMITTED && + status != NomenclatureChange::CLOSED + ) + errors.add(:base, 'Nomenclature change is locked for updates') + false end end def next_step steps = self.class::STEPS + return nil if steps.empty? if status == NomenclatureChange::NEW diff --git a/app/models/nomenclature_change/input.rb b/app/models/nomenclature_change/input.rb index d8a216a5f..f15d55a9e 100644 --- a/app/models/nomenclature_change/input.rb +++ b/app/models/nomenclature_change/input.rb @@ -33,13 +33,6 @@ # Inputs are required to be existing taxon concepts. class NomenclatureChange::Input < ApplicationRecord include TrackWhoDoesIt - # Migrated to controller (Strong Parameters) - # attr_accessible :nomenclature_change_id, :taxon_concept_id, - # :note_en, :note_es, :note_fr, :internal_note, - # :parent_reassignments_attributes, - # :name_reassignments_attributes, - # :distribution_reassignments_attributes, - # :legislation_reassignments_attributes belongs_to :nomenclature_change belongs_to :taxon_concept diff --git a/app/models/nomenclature_change/lump.rb b/app/models/nomenclature_change/lump.rb index e83e56529..24a178486 100644 --- a/app/models/nomenclature_change/lump.rb +++ b/app/models/nomenclature_change/lump.rb @@ -26,8 +26,7 @@ class NomenclatureChange::Lump < NomenclatureChange build_steps(:inputs, :outputs, :notes, :legislation, :summary) - # Migrated to controller (Strong Parameters) - # attr_accessible :inputs_attributes, :output_attributes + has_many :inputs, inverse_of: :nomenclature_change, class_name: 'NomenclatureChange::Input', foreign_key: :nomenclature_change_id, diff --git a/app/models/nomenclature_change/output.rb b/app/models/nomenclature_change/output.rb index 0a7648e5e..58ed4f14f 100644 --- a/app/models/nomenclature_change/output.rb +++ b/app/models/nomenclature_change/output.rb @@ -55,14 +55,6 @@ class NomenclatureChange::Output < ApplicationRecord include TrackWhoDoesIt attr_accessor :output_type # New taxon, Existing subspecies, Existing taxon - # Migrated to controller (Strong Parameters) - # attr_accessible :nomenclature_change_id, :taxon_concept_id, - # :new_taxon_concept_id, :rank_id, :new_scientific_name, :new_author_year, - # :new_name_status, :new_parent_id, :new_rank_id, :taxonomy_id, - # :note_en, :note_es, :note_fr, :internal_note, :is_primary_output, - # :parent_reassignments_attributes, :name_reassignments_attributes, - # :distribution_reassignments_attributes, :legislation_reassignments_attributes, - # :output_type, :tag_list, :created_by_id, :updated_by_id belongs_to :nomenclature_change belongs_to :taxon_concept, optional: true @@ -171,6 +163,7 @@ def tmp_taxon_concept else display_full_name end + taxon_concept_attrs = { parent_id: new_parent_id || parent_id, rank_id: new_rank_id || rank_id, @@ -186,10 +179,11 @@ def tmp_taxon_concept else Taxonomy.find_by(name: Taxonomy::CITES_EU) end + TaxonConcept.new( taxon_concept_attrs.merge( { - taxonomy_id: taxonomy.id, + taxonomy_id: taxonomy&.id, tag_list: tag_list } ) @@ -204,14 +198,17 @@ def tmp_taxon_concept def validate_tmp_taxon_concept @tmp_taxon_concept = tmp_taxon_concept + unless @tmp_taxon_concept errors.add(:new_taxon_concept, "can't be blank") end + return true if @tmp_taxon_concept.valid? @tmp_taxon_concept.errors.each do |error| attribute = error.attribute message = error.message + if [ :parent_id, :rank, :name_status, :author_year, :full_name ]. include?(attribute) errors.add(:"new_#{attribute}", message) diff --git a/app/models/nomenclature_change/parent_reassignment.rb b/app/models/nomenclature_change/parent_reassignment.rb index 1db897110..e01d26b77 100644 --- a/app/models/nomenclature_change/parent_reassignment.rb +++ b/app/models/nomenclature_change/parent_reassignment.rb @@ -31,8 +31,6 @@ # Reassignable is a taxon concept that is reassigned to a new parent class NomenclatureChange::ParentReassignment < NomenclatureChange::Reassignment - # Migrated to controller (Strong Parameters) - # attr_accessible :reassignment_target_attributes has_one :reassignment_target, inverse_of: :reassignment, class_name: 'NomenclatureChange::ReassignmentTarget', foreign_key: :nomenclature_change_reassignment_id diff --git a/app/models/nomenclature_change/reassignment_helpers.rb b/app/models/nomenclature_change/reassignment_helpers.rb index 900e5b6af..ba56f76f9 100644 --- a/app/models/nomenclature_change/reassignment_helpers.rb +++ b/app/models/nomenclature_change/reassignment_helpers.rb @@ -2,11 +2,6 @@ module NomenclatureChange::ReassignmentHelpers def self.included(base) base.class_eval do include TrackWhoDoesIt - - # Migrated to controller (Strong Parameters) - # attr_accessible :type, :reassignable_id, :reassignable_type, - # :nomenclature_change_input_id, :nomenclature_change_output_id, - # :note_en, :note_es, :note_fr, :internal_note, :output_ids belongs_to :reassignable, polymorphic: true, optional: true has_many :reassignment_targets, inverse_of: :reassignment, diff --git a/app/models/nomenclature_change/reassignment_target.rb b/app/models/nomenclature_change/reassignment_target.rb index 2bd31148a..3836609a1 100644 --- a/app/models/nomenclature_change/reassignment_target.rb +++ b/app/models/nomenclature_change/reassignment_target.rb @@ -27,9 +27,9 @@ class NomenclatureChange::ReassignmentTarget < ApplicationRecord include TrackWhoDoesIt + # Relationship table - # attr_accessible :nomenclature_change_output_id, - # :nomenclature_change_reassignment_id, :note + belongs_to :output, class_name: 'NomenclatureChange::Output', foreign_key: :nomenclature_change_output_id belongs_to :reassignment, diff --git a/app/models/nomenclature_change/split.rb b/app/models/nomenclature_change/split.rb index e54b48f64..1a1639ff8 100644 --- a/app/models/nomenclature_change/split.rb +++ b/app/models/nomenclature_change/split.rb @@ -29,8 +29,7 @@ class NomenclatureChange::Split < NomenclatureChange :inputs, :outputs, :notes, :children, :names, :distribution, :legislation, :summary ) - # Migrated to controller (Strong Parameters) - # attr_accessible :input_attributes, :outputs_attributes + has_one :input, inverse_of: :nomenclature_change, class_name: 'NomenclatureChange::Input', foreign_key: :nomenclature_change_id, diff --git a/app/models/nomenclature_change/status_change_helpers.rb b/app/models/nomenclature_change/status_change_helpers.rb index a6291a95e..c35573b05 100644 --- a/app/models/nomenclature_change/status_change_helpers.rb +++ b/app/models/nomenclature_change/status_change_helpers.rb @@ -1,9 +1,6 @@ module NomenclatureChange::StatusChangeHelpers def self.included(base) base.class_eval do - # Migrated to controller (Strong Parameters) - # attr_accessible :primary_output_attributes, :secondary_output_attributes, - # :input_attributes has_one :input, inverse_of: :nomenclature_change, class_name: 'NomenclatureChange::Input', foreign_key: :nomenclature_change_id, diff --git a/app/models/nomenclature_change/status_to_accepted.rb b/app/models/nomenclature_change/status_to_accepted.rb index 1b4ed779d..2c9afd955 100644 --- a/app/models/nomenclature_change/status_to_accepted.rb +++ b/app/models/nomenclature_change/status_to_accepted.rb @@ -27,15 +27,14 @@ # A status change to A needs to have one output. class NomenclatureChange::StatusToAccepted < NomenclatureChange include NomenclatureChange::StatusChangeHelpers - # Migrated to controller (Strong Parameters) - # attr_accessible :created_by_id, :updated_by_id - build_steps( - :primary_output, :summary - ) + + build_steps :primary_output, :summary + validates :status, inclusion: { in: self.status_dict, message: '%{value} is not a valid status' } + validate :required_primary_output_name_status, if: :primary_output_or_submitting? before_validation :set_output_name_status, if: :primary_output_or_submitting? before_validation :set_output_rank_id, if: :primary_output_or_submitting? @@ -46,6 +45,7 @@ def required_primary_output_name_status errors.add(:primary_output, 'Must be N or T taxon') return false end + true end diff --git a/app/models/preset_tag.rb b/app/models/preset_tag.rb index 64a67b084..5a558b6c6 100644 --- a/app/models/preset_tag.rb +++ b/app/models/preset_tag.rb @@ -15,8 +15,6 @@ class PresetTag < ApplicationRecord include Deletable - # Migrated to controller (Strong Parameters) - # attr_accessible :model, :name TYPES = { Distribution: 'Distribution', diff --git a/app/models/proposal_details.rb b/app/models/proposal_details.rb index 876944df3..a2f6a413b 100644 --- a/app/models/proposal_details.rb +++ b/app/models/proposal_details.rb @@ -24,8 +24,7 @@ class ProposalDetails < ApplicationRecord # Used in DocumentsController - # attr_accessible :document_id, :proposal_nature, :proposal_outcome_id, - # :representation, :proposal_number + self.table_name = 'proposal_details' belongs_to :document, touch: true end diff --git a/app/models/rank.rb b/app/models/rank.rb index 4089c3532..afcadefac 100644 --- a/app/models/rank.rb +++ b/app/models/rank.rb @@ -23,9 +23,6 @@ class Rank < ApplicationRecord include Deletable extend Mobility - # Migrated to controller (Strong Parameters) - # attr_accessible :name, :display_name_en, :display_name_es, :display_name_fr, - # :taxonomic_position, :fixed_order include Dictionary build_dictionary :kingdom, :phylum, :class, :order, :family, :subfamily, :genus, :species, :subspecies, :variety diff --git a/app/models/reference.rb b/app/models/reference.rb index c2f2e2aea..7472198be 100644 --- a/app/models/reference.rb +++ b/app/models/reference.rb @@ -30,8 +30,6 @@ class Reference < ApplicationRecord include Deletable include TrackWhoDoesIt - # Migrated to controller (Strong Parameters) - # attr_accessible :citation, :created_by_id, :updated_by_id validates :citation, presence: true, uniqueness: true has_many :taxon_concept_references diff --git a/app/models/review_details.rb b/app/models/review_details.rb index 1c7561781..66a21cab1 100644 --- a/app/models/review_details.rb +++ b/app/models/review_details.rb @@ -25,7 +25,6 @@ class ReviewDetails < ApplicationRecord # Used by DocumentController. - # attr_accessible :document_id, :review_phase_id, :process_stage_id, :recommended_category self.table_name = 'review_details' belongs_to :document, touch: true diff --git a/app/models/species_listing.rb b/app/models/species_listing.rb index 87677b9e3..6f3be9a9a 100644 --- a/app/models/species_listing.rb +++ b/app/models/species_listing.rb @@ -24,8 +24,6 @@ class SpeciesListing < ApplicationRecord include Deletable - # Migrated to controller (Strong Parameters) - # attr_accessible :designation_id, :name, :abbreviation belongs_to :designation has_many :listing_changes diff --git a/app/models/srg_history.rb b/app/models/srg_history.rb index b2eb72889..6dd244307 100644 --- a/app/models/srg_history.rb +++ b/app/models/srg_history.rb @@ -13,9 +13,6 @@ # index_srg_histories_on_name (name) UNIQUE # class SrgHistory < ApplicationRecord - # Migrated to controller (Strong Parameters) - # attr_accessible :name, :tooltip - has_many :eu_decisions validates :name, presence: true, uniqueness: true diff --git a/app/models/taxon_common.rb b/app/models/taxon_common.rb index 0bb4a7fc6..37d6f4e1c 100644 --- a/app/models/taxon_common.rb +++ b/app/models/taxon_common.rb @@ -28,9 +28,6 @@ class TaxonCommon < ApplicationRecord include Changeable include TrackWhoDoesIt - # Migrated to controller (Strong Parameters) - # attr_accessible :common_name_id, :taxon_concept_id, :created_by_id, - # :updated_by_id, :name, :language_id attr_accessor :name, :language_id diff --git a/app/models/taxon_concept.rb b/app/models/taxon_concept.rb index 4b53f0296..61a51521d 100644 --- a/app/models/taxon_concept.rb +++ b/app/models/taxon_concept.rb @@ -84,15 +84,6 @@ class TaxonConcept < ApplicationRecord rank_name: :rank_name } - # Migrated to controller (Strong Parameters) - # attr_accessible :parent_id, :taxonomy_id, :rank_id, - # :parent_id, :author_year, :taxon_name_id, :taxonomic_position, - # :legacy_id, :legacy_type, :scientific_name, :name_status, - # :tag_list, :legacy_trade_code, :hybrid_parents_ids, - # :accepted_names_ids, :accepted_names_for_trade_name_ids, - # :nomenclature_note_en, :nomenclature_note_es, :nomenclature_note_fr, - # :created_by_id, :updated_by_id, :dependents_updated_at, :kew_id - attr_writer :accepted_names_ids, :accepted_names_for_trade_name_ids, :hybrid_parents_ids diff --git a/app/models/taxon_concept_reference.rb b/app/models/taxon_concept_reference.rb index 3a0e5fe76..02716c9ee 100644 --- a/app/models/taxon_concept_reference.rb +++ b/app/models/taxon_concept_reference.rb @@ -34,10 +34,6 @@ class TaxonConceptReference < ApplicationRecord include Changeable include TrackWhoDoesIt - # Migrated to controller (Strong Parameters) - # attr_accessible :reference_id, :taxon_concept_id, :is_standard, :is_cascaded, - # :excluded_taxon_concepts_ids, :reference_attributes, - # :created_by_id, :updated_by_id belongs_to :reference belongs_to :taxon_concept diff --git a/app/models/taxon_concept_version.rb b/app/models/taxon_concept_version.rb index 143db29f4..1155fa61e 100644 --- a/app/models/taxon_concept_version.rb +++ b/app/models/taxon_concept_version.rb @@ -24,13 +24,6 @@ # index_taxon_concept_versions_on_taxonomy_name_and_created_at (taxonomy_name,created_at) # class TaxonConceptVersion < PaperTrail::Version - # Migrated to Strong Parameters - # attr_accessible :taxon_concept_id, - # :taxonomy_name, - # :full_name, - # :author_year, - # :name_status, - # :rank_name self.table_name = :taxon_concept_versions self.sequence_name = :taxon_concept_versions_id_seq end diff --git a/app/models/taxon_instrument.rb b/app/models/taxon_instrument.rb index 8f1f10f42..5da70d940 100644 --- a/app/models/taxon_instrument.rb +++ b/app/models/taxon_instrument.rb @@ -29,8 +29,6 @@ class TaxonInstrument < ApplicationRecord include Changeable include TrackWhoDoesIt - # Migrated to controller (Strong Parameters) - # attr_accessible :effective_from, :instrument_id, :taxon_concept_id belongs_to :instrument belongs_to :taxon_concept diff --git a/app/models/taxon_name.rb b/app/models/taxon_name.rb index 612878521..06a547f03 100644 --- a/app/models/taxon_name.rb +++ b/app/models/taxon_name.rb @@ -10,7 +10,6 @@ class TaxonName < ApplicationRecord # Used by seed and rake task. - # attr_accessible :basionym_id, :scientific_name has_many :taxon_concepts, dependent: :destroy diff --git a/app/models/taxon_relationship.rb b/app/models/taxon_relationship.rb index ce55bb3df..48d060d93 100644 --- a/app/models/taxon_relationship.rb +++ b/app/models/taxon_relationship.rb @@ -31,9 +31,6 @@ class TaxonRelationship < ApplicationRecord include Changeable include TrackWhoDoesIt - # Migrated to controller (Strong Parameters) - # attr_accessible :taxon_concept_id, :other_taxon_concept_id, :taxon_relationship_type_id, - # :created_by_id, :updated_by_id belongs_to :taxon_relationship_type belongs_to :taxon_concept diff --git a/app/models/taxon_relationship_type.rb b/app/models/taxon_relationship_type.rb index c9e61c6d5..9c785bf37 100644 --- a/app/models/taxon_relationship_type.rb +++ b/app/models/taxon_relationship_type.rb @@ -12,7 +12,6 @@ class TaxonRelationshipType < ApplicationRecord # Used by seed and rake task. - # attr_accessible :name, :is_intertaxonomic, :is_bidirectional include Dictionary build_dictionary :equal_to, :includes, :overlaps, :disjunct, :has_synonym, diff --git a/app/models/taxonomy.rb b/app/models/taxonomy.rb index 531806138..6a9f3e6f5 100644 --- a/app/models/taxonomy.rb +++ b/app/models/taxonomy.rb @@ -15,10 +15,9 @@ class Taxonomy < ApplicationRecord include Deletable include Dictionary + build_dictionary :cites_eu, :cms - # Migrated to controller (Strong Parameters) - # attr_accessible :name has_many :designations has_many :taxon_concepts diff --git a/app/models/term_trade_codes_pair.rb b/app/models/term_trade_codes_pair.rb index fdcfbce51..fa51a9223 100644 --- a/app/models/term_trade_codes_pair.rb +++ b/app/models/term_trade_codes_pair.rb @@ -24,9 +24,6 @@ # class TermTradeCodesPair < ApplicationRecord - # Migrated to controller (Strong Parameters) - # attr_accessible :trade_code_id, :trade_code_type, :term_id - belongs_to :term, class_name: 'TradeCode' belongs_to :trade_code, optional: true diff --git a/app/models/trade/annual_report_upload.rb b/app/models/trade/annual_report_upload.rb index 8ed02bd4f..e5aafc408 100644 --- a/app/models/trade/annual_report_upload.rb +++ b/app/models/trade/annual_report_upload.rb @@ -56,9 +56,6 @@ class Trade::AnnualReportUpload < ApplicationRecord include TrackWhoDoesIt # Suppose use in controller, but controller using strong parameters... - # attr_accessible :csv_source_file, :trading_country_id, :point_of_view, - # :submitted_at, :submitted_by_id, :number_of_rows, - # :number_of_records_submitted, :aws_storage_path mount_uploader :csv_source_file, Trade::CsvSourceFileUploader belongs_to :trading_country, class_name: 'GeoEntity' diff --git a/app/models/trade/format_validation_rule.rb b/app/models/trade/format_validation_rule.rb index 59a95efed..b2f778f0b 100644 --- a/app/models/trade/format_validation_rule.rb +++ b/app/models/trade/format_validation_rule.rb @@ -17,7 +17,6 @@ class Trade::FormatValidationRule < Trade::ValidationRule # Only created by seed. - # attr_accessible :format_re def error_message column_names.join(', ') + ' must be formatted as ' + format_re diff --git a/app/models/trade/inclusion_validation_rule.rb b/app/models/trade/inclusion_validation_rule.rb index 346193fb2..a204e97ea 100644 --- a/app/models/trade/inclusion_validation_rule.rb +++ b/app/models/trade/inclusion_validation_rule.rb @@ -17,7 +17,6 @@ class Trade::InclusionValidationRule < Trade::ValidationRule # Only created by seed. - # attr_accessible :valid_values_view def matching_records_for_aru_and_error(annual_report_upload, validation_error) # The format of validation_error.matching_criteria seems to vary - sometimes diff --git a/app/models/trade/permit.rb b/app/models/trade/permit.rb index 66c8eb760..a409db913 100644 --- a/app/models/trade/permit.rb +++ b/app/models/trade/permit.rb @@ -15,5 +15,4 @@ class Trade::Permit < ApplicationRecord # app/models/trade/shipment.rb is the only place create this record. - # attr_accessible :number end diff --git a/app/models/trade/sandbox_template.rb b/app/models/trade/sandbox_template.rb index 573e23c15..2a61b6111 100644 --- a/app/models/trade/sandbox_template.rb +++ b/app/models/trade/sandbox_template.rb @@ -60,8 +60,8 @@ def self.ar_klass(table_name) has_paper_trail include ActiveModel::ForbiddenAttributesProtection - # Too dynamic, hard to trace where using it. - # attr_accessible :appendix, + # Previously, `attr_accesssible` was used here for: + # :appendix, # :taxon_name, # :term_code, # :quantity, diff --git a/app/models/trade/shipment.rb b/app/models/trade/shipment.rb index 081475049..325b54d4a 100644 --- a/app/models/trade/shipment.rb +++ b/app/models/trade/shipment.rb @@ -80,15 +80,6 @@ class Trade::Shipment < ApplicationRecord include TrackWhoDoesIt - # Not sure where using this. - # attr_accessible :annual_report_upload_id, :appendix, - # :country_of_origin_id, :origin_permit_id, - # :exporter_id, :import_permit_id, :importer_id, :purpose_id, - # :quantity, :reporter_type, :reported_by_exporter, - # :source_id, :taxon_concept_id, :reported_taxon_concept_id, - # :term_id, :unit_id, :year, - # :import_permit_number, :export_permit_number, :origin_permit_number, - # :ignore_warnings, :created_by_id, :updated_by_id attr_accessor :reporter_type, :warnings, :ignore_warnings diff --git a/app/models/trade/taxon_concept_term_pair.rb b/app/models/trade/taxon_concept_term_pair.rb index e923ed29e..dfda331d0 100644 --- a/app/models/trade/taxon_concept_term_pair.rb +++ b/app/models/trade/taxon_concept_term_pair.rb @@ -20,8 +20,6 @@ # class Trade::TaxonConceptTermPair < ApplicationRecord - # Migrated to controller (Strong Parameters) - # attr_accessible :taxon_concept_id, :term_id validates :taxon_concept_id, uniqueness: { scope: :term_id } belongs_to :taxon_concept diff --git a/app/models/trade/trade_data_download.rb b/app/models/trade/trade_data_download.rb index 3a9ca7695..e6d90defa 100644 --- a/app/models/trade/trade_data_download.rb +++ b/app/models/trade/trade_data_download.rb @@ -26,9 +26,6 @@ class Trade::TradeDataDownload < ApplicationRecord # Used by app/models/trade/trade_data_download_logger.rb - # attr_accessible :user_ip, :report_type, :year_from, :year_to, :taxon, - # :appendix, :importer, :exporter, :origin, :term, :unit, :source, :purpose, - # :number_of_rows, :city, :country, :organization after_commit :async_downloads_cache_cleanup, on: [ :create, :update ] diff --git a/app/models/trade/validation_error.rb b/app/models/trade/validation_error.rb index 7f45f3561..0f52424a2 100644 --- a/app/models/trade/validation_error.rb +++ b/app/models/trade/validation_error.rb @@ -28,6 +28,8 @@ class Trade::ValidationError < ApplicationRecord include ActiveModel::Validations + # Used by app/models/trade/validation_rule.rb + validates_each :matching_criteria, allow_blank: true do |record, attr, value| record.errors.add attr, "must be a Hash, got a #{value.class.name}" if !value.is_a? Hash @@ -42,13 +44,4 @@ class Trade::ValidationError < ApplicationRecord belongs_to :annual_report_upload, class_name: 'Trade::AnnualReportUpload' belongs_to :validation_rule, class_name: 'Trade::ValidationRule' - - # Used by app/models/trade/validation_rule.rb - # attr_accessible :annual_report_upload_id, - # :validation_rule_id, - # :matching_criteria, - # :is_ignored, - # :is_primary, - # :error_message, - # :error_count end diff --git a/app/models/trade/validation_rule.rb b/app/models/trade/validation_rule.rb index 8e7f86178..edeb0ec18 100644 --- a/app/models/trade/validation_rule.rb +++ b/app/models/trade/validation_rule.rb @@ -17,7 +17,6 @@ class Trade::ValidationRule < ApplicationRecord # Used by seed. - # attr_accessible :column_names, :run_order, :is_primary, :scope, :is_strict serialize :scope, coder: ActiveRecord::Coders::NestedHstore has_many :validation_errors, class_name: 'Trade::ValidationError' diff --git a/app/models/trade_code.rb b/app/models/trade_code.rb index bb0ec878a..a956d9247 100644 --- a/app/models/trade_code.rb +++ b/app/models/trade_code.rb @@ -18,8 +18,7 @@ class TradeCode < ApplicationRecord extend Mobility - # Migrated to controller (Strong Parameters) - # attr_accessible :code, :type, :name_en, :name_es, :name_fr + translates :name has_many :taxon_concept_term_pairs, diff --git a/app/models/trade_restriction.rb b/app/models/trade_restriction.rb index 2c0af552a..dff1495ba 100644 --- a/app/models/trade_restriction.rb +++ b/app/models/trade_restriction.rb @@ -58,13 +58,6 @@ class TradeRestriction < ApplicationRecord extend Mobility include TrackWhoDoesIt - # Migrated to controller (Strong Parameters) - # attr_accessible :end_date, :geo_entity_id, :is_current, - # :notes, :publication_date, :purpose_ids, :quota, :type, - # :source_ids, :start_date, :term_ids, :unit_id, :internal_notes, - # :nomenclature_note_en, :nomenclature_note_es, :nomenclature_note_fr, - # :created_by_id, :updated_by_id, :url, - # :taxon_concept_id belongs_to :taxon_concept, optional: true belongs_to :m_taxon_concept, foreign_key: :taxon_concept_id, optional: true diff --git a/app/models/trade_restriction_purpose.rb b/app/models/trade_restriction_purpose.rb index a8e40fcf7..3b4604a32 100644 --- a/app/models/trade_restriction_purpose.rb +++ b/app/models/trade_restriction_purpose.rb @@ -28,7 +28,6 @@ class TradeRestrictionPurpose < ApplicationRecord include TrackWhoDoesIt # Relationship model between TradeCode(purpose) and TradeRestriction - # attr_accessible :purpose_id, :trade_restriction_id belongs_to :trade_restriction belongs_to :purpose, class_name: 'TradeCode' diff --git a/app/models/trade_restriction_source.rb b/app/models/trade_restriction_source.rb index eaf84468f..3c28f876d 100644 --- a/app/models/trade_restriction_source.rb +++ b/app/models/trade_restriction_source.rb @@ -28,7 +28,6 @@ class TradeRestrictionSource < ApplicationRecord include TrackWhoDoesIt # Relationship model between TradeCode(source) and TradeRestriction - # attr_accessible :source_id, :trade_restriction_id belongs_to :trade_restriction belongs_to :source, class_name: 'TradeCode' diff --git a/app/models/trade_restriction_term.rb b/app/models/trade_restriction_term.rb index 26e982e2c..7cfcbbbcb 100644 --- a/app/models/trade_restriction_term.rb +++ b/app/models/trade_restriction_term.rb @@ -28,7 +28,6 @@ class TradeRestrictionTerm < ApplicationRecord include TrackWhoDoesIt # Relationship model between TradeCode(term) and TradeRestriction - # attr_accessible :term_id, :trade_restriction_id belongs_to :trade_restriction belongs_to :term, class_name: 'TradeCode' end diff --git a/app/models/trade_restrictions/cites_suspension.rb b/app/models/trade_restrictions/cites_suspension.rb index 3df4f0691..76e4df775 100644 --- a/app/models/trade_restrictions/cites_suspension.rb +++ b/app/models/trade_restrictions/cites_suspension.rb @@ -54,10 +54,7 @@ class CitesSuspension < TradeRestriction include Changeable - # Migrated to controller (Strong Parameters) - # attr_accessible :start_notification_id, :end_notification_id, - # :cites_suspension_confirmations_attributes, - # :applies_to_import + belongs_to :taxon_concept, optional: true belongs_to :start_notification, class_name: 'CitesSuspensionNotification' belongs_to :end_notification, class_name: 'CitesSuspensionNotification', optional: true diff --git a/app/models/trade_restrictions/quota.rb b/app/models/trade_restrictions/quota.rb index d51750deb..8346bf489 100644 --- a/app/models/trade_restrictions/quota.rb +++ b/app/models/trade_restrictions/quota.rb @@ -54,8 +54,6 @@ class Quota < TradeRestriction include Changeable - # Migrated to controller (Strong Parameters) - # attr_accessible :public_display validates :quota, presence: true validates :quota, numericality: { greater_than_or_equal_to: -1.0 } @@ -98,10 +96,8 @@ def self.count_matching(params) SQL ), year: params[:year].to_i, - excluded_geo_entities: params[:excluded_geo_entities_ids].present? ? - params[:excluded_geo_entities_ids].map(&:to_i) : nil, - included_geo_entities: params[:included_geo_entities_ids].present? ? - params[:included_geo_entities_ids].map(&:to_i) : nil, + excluded_geo_entities: (params[:excluded_geo_entities_ids].presence&.map(&:to_i)), + included_geo_entities: (params[:included_geo_entities_ids].presence&.map(&:to_i)), excluded_taxon_concepts: params[:excluded_taxon_concepts_ids].present? ? params[:excluded_taxon_concepts_ids].split(',').map(&:to_i) : nil, included_taxon_concepts: params[:included_taxon_concepts_ids].present? ? diff --git a/app/models/user.rb b/app/models/user.rb index 66fcf5e2c..bd3469f0f 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -35,10 +35,6 @@ class User < ApplicationRecord devise :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable, :validatable - # Migrated to controller (Strong Parameters) - # attr_accessible :email, :name, :password, :password_confirmation, - # :remember_me, :role, :terms_and_conditions, :is_cites_authority, - # :organisation, :geo_entity_id, :is_active MANAGER = 'admin' CONTRIBUTOR = 'default' # nonsense diff --git a/app/models/year_annual_reports_by_country.rb b/app/models/year_annual_reports_by_country.rb index 0467974f1..c4b6f3c69 100644 --- a/app/models/year_annual_reports_by_country.rb +++ b/app/models/year_annual_reports_by_country.rb @@ -11,5 +11,4 @@ class YearAnnualReportsByCountry < ApplicationRecord # No idea where using this. - # attr_accessible :no, :name_en, :year, :reporter_type, :year_created end diff --git a/app/services/document_batch.rb b/app/services/document_batch.rb index 03bc24734..8b00d4bf2 100644 --- a/app/services/document_batch.rb +++ b/app/services/document_batch.rb @@ -1,8 +1,11 @@ class DocumentBatch extend ActiveModel::Naming + include ActiveModel::Conversion include ActiveModel::Validations + attr_reader :event_id, :language_id, :date, :is_public, :documents + validates :date, presence: true validates :documents, presence: true, allow_blank: false @@ -11,6 +14,7 @@ def initialize(attributes = {}) @language_id = attributes[:language_id] @date = attributes[:date] @is_public = attributes[:is_public] + initialize_documents(attributes[:documents_attributes], attributes[:files]) end @@ -18,14 +22,17 @@ def save return false unless valid? success = true + Document.transaction do @documents.each do |d| unless d.save success = false end end + raise ActiveRecord::Rollback unless success end + success end @@ -37,20 +44,28 @@ def persisted? def initialize_documents(documents_attributes, files) @documents = [] + if documents_attributes && files + unless documents_attributes.try(:length) == files.try(:length) + raise StandardError( + 'documents_attributes and files should have the same number of elements' + ) + end + for idx in 0..(files.length - 1) do document_params = if files[idx].respond_to?(:read) # Filter out invalid params for ActiveStorage. { - type: documents_attributes[idx.to_s][:type], + type: documents_attributes[idx][:type], file: files[idx], title: document_title(files[idx]) } else { - type: documents_attributes[idx.to_s][:type], + type: documents_attributes[idx][:type] } end + @documents.push(Document.new(common_attributes.merge(document_params))) end end @@ -71,6 +86,7 @@ def common_attributes def document_title(file) original_filename = file.is_a?(Hash) ? file[:filename].original_filename : file.original_filename + File.basename(original_filename, File.extname(original_filename)) end end diff --git a/app/services/m_taxon_concept_filter_by_scientific_name_with_descendants.rb b/app/services/m_taxon_concept_filter_by_scientific_name_with_descendants.rb index b05cdb790..624fafedf 100644 --- a/app/services/m_taxon_concept_filter_by_scientific_name_with_descendants.rb +++ b/app/services/m_taxon_concept_filter_by_scientific_name_with_descendants.rb @@ -1,7 +1,7 @@ class MTaxonConceptFilterByScientificNameWithDescendants def initialize(relation, scientific_name, match_options = {}) @relation = relation || MTaxonConcept.all - @scientific_name = scientific_name.mb_chars.upcase.strip + @scientific_name = scientific_name.upcase.strip @match_synonyms = match_options[:synonyms] || true @match_common_names = match_options[:common_names] || false @match_subspecies = match_options[:subspecies] || false diff --git a/app/services/trade/filter.rb b/app/services/trade/filter.rb index 74f3a5d46..fa43ad1b5 100644 --- a/app/services/trade/filter.rb +++ b/app/services/trade/filter.rb @@ -87,15 +87,15 @@ def initialize_query draft_query = draft_query.where(term_id: @terms_ids) end - unless @importers_ids.empty? + unless importers_ids.empty? draft_query = draft_query.where( - importer_id: resolve_eu(@importers_ids) + importer_id: resolve_eu(importers_ids) ) end - unless @exporters_ids.empty? + unless exporters_ids.empty? draft_query = draft_query.where( - exporter_id: resolve_eu(@exporters_ids) + exporter_id: resolve_eu(exporters_ids) ) end @@ -144,11 +144,13 @@ def initialize_query if importer_eu_country_ids.present? sub_query = eu_country_date_query(@time_range_start, @time_range_end, 'importer', importer_eu_country_ids) - draft_query = draft_query.where.not(sub_query) if date_query.present? + + draft_query = draft_query.where.not(sub_query) if sub_query.present? end if exporter_eu_country_ids.present? sub_query = eu_country_date_query(@time_range_start, @time_range_end, 'exporter', exporter_eu_country_ids) + draft_query = draft_query.where.not(sub_query) if sub_query.present? end @@ -167,12 +169,20 @@ def eu_country_ids EuCountryDate.pluck(:geo_entity_id) end + def importers_ids + @importers_ids&.presence&.map(&:to_i) || [] + end + + def exporters_ids + @exporters_ids&.presence&.map(&:to_i) || [] + end + # this is to collect only eu country IDs to apply EU rules query to # e.g. EU + Austria we don't have to apply EU rules to Austria def importer_eu_country_ids @importer_eu_country_ids ||= - if @importer_ids&.presence&.include?(eu_id) - eu_country_ids - @importer_ids + if importers_ids.include?(eu_id) + eu_country_ids - exporters_ids else [] end @@ -180,8 +190,8 @@ def importer_eu_country_ids def exporter_eu_country_ids @exporter_eu_country_ids ||= - if @exporter_ids&.presence&.include?(eu_id) - eu_country_ids - @exporter_ids + if exporters_ids.include?(eu_id) + eu_country_ids - exporters_ids else [] end diff --git a/app/services/trade/shipments_export.rb b/app/services/trade/shipments_export.rb index 326b59744..9cafe287c 100644 --- a/app/services/trade/shipments_export.rb +++ b/app/services/trade/shipments_export.rb @@ -1,9 +1,12 @@ # Implements "raw" shipments export class Trade::ShipmentsExport < Species::CsvCopyExport include Trade::ShipmentReportQueries + PUBLIC_CSV_LIMIT = 1000000 PUBLIC_WEB_LIMIT = 50000 + include ActiveModel::SerializerSupport + delegate :report_type, to: :@search delegate :page, to: :@search delegate :per_page, to: :@search @@ -11,6 +14,7 @@ class Trade::ShipmentsExport < Species::CsvCopyExport def initialize(filters) @search = Trade::Filter.new(filters) @filters = @search.options.merge(csv_separator: filters['csv_separator']) + initialize_csv_separator(filters[:csv_separator]) initialize_file_name end @@ -19,12 +23,15 @@ def export unless csv_cached? to_csv end + unless csv_created? Rails.logger.error('Unable to generate output') return false end + ctime = File.ctime(@file_name).strftime('%Y-%m-%d %H:%M') @public_file_name = "#{resource_name}_#{ctime}_#{@csv_separator}_separated.csv" + [ @file_name, { filename: public_file_name, type: 'text/csv' } diff --git a/app/views/layouts/mobile.html.erb b/app/views/layouts/mobile.html.erb index f37c2e414..eef38bf48 100644 --- a/app/views/layouts/mobile.html.erb +++ b/app/views/layouts/mobile.html.erb @@ -7,6 +7,10 @@ + + <%= stylesheet_link_tag "mobile" %> <%= javascript_include_tag "application" %> <%= csrf_meta_tags %> diff --git a/app/views/layouts/species.html.erb b/app/views/layouts/species.html.erb index 054f63979..10a4f6590 100644 --- a/app/views/layouts/species.html.erb +++ b/app/views/layouts/species.html.erb @@ -8,7 +8,11 @@ - <%= stylesheet_link_tag "species" %> + + + <%= stylesheet_link_tag "species" %> diff --git a/bin/brakeman b/bin/brakeman new file mode 100755 index 000000000..ace1c9ba0 --- /dev/null +++ b/bin/brakeman @@ -0,0 +1,7 @@ +#!/usr/bin/env ruby +require "rubygems" +require "bundler/setup" + +ARGV.unshift("--ensure-latest") + +load Gem.bin_path("brakeman", "brakeman") diff --git a/bin/dev b/bin/dev new file mode 100755 index 000000000..5f91c2054 --- /dev/null +++ b/bin/dev @@ -0,0 +1,2 @@ +#!/usr/bin/env ruby +exec "./bin/rails", "server", *ARGV diff --git a/bin/docker-entrypoint b/bin/docker-entrypoint index cd970544f..8e1b5440c 100755 --- a/bin/docker-entrypoint +++ b/bin/docker-entrypoint @@ -1,10 +1,11 @@ #!/bin/bash -e -if [[ "${@}" =~ "rails server" ]]; then - rm -f ./tmp/pids/server.pid; -fi - -bundle install +# Removed per https://railsdiff.org/8.0.5/8.1.3 +# Enable jemalloc for reduced memory usage and latency. +# if [ -z "${LD_PRELOAD+x}" ]; then +# LD_PRELOAD=$(find /usr/lib -name libjemalloc.so.2 -print -quit) +# export LD_PRELOAD +# fi mkdir -p {./,spec/}public/downloads/checklist mkdir -p {./,spec/}public/downloads/cites_listings @@ -31,9 +32,13 @@ mkdir -p {./,spec/}public/downloads/taxon_concepts_distributions mkdir -p {./,spec/}public/downloads/taxon_concepts_names # If running the rails server then create or migrate existing database -# if [ "${1}" == "./bin/rails" ] && [ "${2}" == "server" ]; then +# if [ "${@: -2:1}" == "./bin/rails" ] && [ "${@: -1:1}" == "server" ]; then # ./bin/rails db:prepare # fi +# Remove stale PID file — survives Docker restarts on the same writable layer +# and causes Rails to refuse to start (finds a PID it thinks is already running) +rm -f /rails/tmp/pids/server.pid + # This is the main rails/sidekiq command exec "${@}" diff --git a/bin/docker-sidekiq-healthcheck b/bin/docker-sidekiq-healthcheck new file mode 100755 index 000000000..975e7c159 --- /dev/null +++ b/bin/docker-sidekiq-healthcheck @@ -0,0 +1,11 @@ +#!/bin/bash + +# Cannot use `sidekiqmon`, because it rely on REDIS_URL env. +# We do not store redis_url in REDIS_URL: +# - it has password, is serect, should be in rails credentials; +# - there maybe more than one redis (e.g. One for sidekiq [maxmemory-policy=noeviction]; one for cache [maxmemory-policy=lfu]) +if [ $(ps aux | grep 'sidekiq ' | grep -v grep | wc -l) -gt 0 ]; then + exit 0 +else + exit 1 +fi diff --git a/bin/rubocop b/bin/rubocop new file mode 100755 index 000000000..5a2050471 --- /dev/null +++ b/bin/rubocop @@ -0,0 +1,8 @@ +#!/usr/bin/env ruby +require "rubygems" +require "bundler/setup" + +# Explicit RuboCop config increases performance slightly while avoiding config confusion. +ARGV.unshift("--config", File.expand_path("../.rubocop.yml", __dir__)) + +load Gem.bin_path("rubocop", "rubocop") diff --git a/bin/setup b/bin/setup index 3cd5a9d78..d9b575d79 100755 --- a/bin/setup +++ b/bin/setup @@ -3,6 +3,7 @@ require "fileutils" # path to your application root. APP_ROOT = File.expand_path("..", __dir__) +APP_NAME = "sapi" def system!(*args) system(*args, exception: true) @@ -14,7 +15,6 @@ FileUtils.chdir APP_ROOT do # Add necessary setup steps to this file. puts "== Installing dependencies ==" - system! "gem install bundler --conservative" system("bundle check") || system!("bundle install") # puts "\n== Copying sample files ==" @@ -24,10 +24,14 @@ FileUtils.chdir APP_ROOT do puts "\n== Preparing database ==" system! "bin/rails db:prepare" + system! "bin/rails db:reset" if ARGV.include?("--reset") puts "\n== Removing old logs and tempfiles ==" system! "bin/rails log:clear tmp:clear" - puts "\n== Restarting application server ==" - system! "bin/rails restart" + unless ARGV.include?("--skip-server") + puts "\n== Starting development server ==" + STDOUT.flush # flush the output before exec(2) so that it displays + exec "bin/dev" + end end diff --git a/config/application.rb b/config/application.rb index d08af56ef..1d5184a55 100644 --- a/config/application.rb +++ b/config/application.rb @@ -11,7 +11,7 @@ module SAPI class Application < Rails::Application # Initialize configuration defaults for originally generated Rails version. - config.load_defaults 7.1 + config.load_defaults 8.1 # Since Rails 5, on submit, submission buttons in `form_for` are disabled. # However, if errors are thrown and the whole form is not rerendered, then diff --git a/config/credentials/production.yml.enc b/config/credentials/production.yml.enc index bd1ef26c3..0f3b56fc5 100644 --- a/config/credentials/production.yml.enc +++ b/config/credentials/production.yml.enc @@ -1 +1 @@ -EmP9XcW3ZHaYNRAHhMVJ62joCE3qCkq7U9nmYLgqlYhARoIiRq5Di5U60M37pRIFE4s1jk45FvQS6alf/N1qU3o9X3BRngvBjwXYR+4lNK4BuPQsEbdpQIjzByNDPk8m1mDsgshQJhsb4wZQkhNbYMlXgXAP81cCW26KHL74l9CoPG3p+1xraPTwI1jrWyRgwbK4vcb5yWdLOZ4CGKL29I/QdLc7VwW/MVfYm15mM/k55umGJweaNvAJvBTY3J3a09xoUy1FWsIqnlcf0uKswKrWNzK2cp0HtEVQTF9Xk/Pi3pB9AKZddMYYMAr79iI+qO/WxJk8qSNhAcaEv4K88vC2ZgJ6K3B++aH9vN9Z8fwIxgJ1lWbiraoeMBUsUAd5jLt3VO3vAK3SZvDUco5WDikA0ghuM4kRoO7IJCZYhWlsYrswzx4OpGTvOzp4MXydqOzblGspS9lSLOFuluvEaMCKagqwjol8ZTzw4T/mx1+sYEAXE1Ht9oP4nrbX8wtTh8tbuNsxhRyDcwqCT4S4SykHiy1T/1dyZo8n12YrNb6g0ENuADaXPFRpwd8h4BLVIFyz/TKeGogMmd9UNRPV/JUcNef5BgaTmXjNniQJtasTiqo3wjdb0CPz8oOBEjxXflU2heDlWg60Rr7ucK5AuYHcuHqfVNvET+vK0W3IPfSoEa0M+EUsI5YyBFGbsNNEpENUf6YXMDWxCfNax42g4qVTrUHJaY5qKy9mqM+lKzLec3UTlbN7Zf/JMtfQlhF5wixZpQerotLKng3K1nBGmzYPNdikxm7aA45KNXC5Fu28BqJtlrn3qEwUPfHvie5w8/kbdZL23XrsznIQjlvLxjZDqV30Nw88LbwkeOKbnms4JZONtj4U62QBr3N5LijZzptCGYaBeWdx2VMfxCCd/Ju30hNni/+WNe0tLoaS9+FQ01VcNbJMTkbAhANIs+V2zGyC3Ik+G79N05TcahFlwiuA2N5B40P7TBjEoOtuYfMf2wBaQkSCDDrrjHz1GCwrDuhHGXwYU4NGJjrmBuV2zOzF/KBMa454bfeReMkUG4PtGc9TxHCE7QnABYiou3Vxt2HTtJiRC8/syE2e53Oj4oAQRnkGlIClDrb0dQNVNEguvvoH0PZu8wIoVCT7fqXqMOZI4rciOifNS9CC2xw7P952fY0LhCQv+ZWjYzq9F4TT84kUArUobo3V0h9zoafazIcFFAq/6HaKgXmPrC/hJ5eEgCSuwN6LASkLEuTpJ4KvRem6wRZ7V35tcsgGSUl52gKuCdhEq/6PHis2B1/DkhcICj2iOU9yQWgHtOTeCPwzcDRDe9UKaQQGkER973S5drB3yBuCWHuPcV2J/61ffbHs3YP9E7mdzzUgI7p1P/zEqkPoix3lRltN7lW3mAPVQclF082RAb82WgqgV/TkcqSPFHbwV3vIaMVcMurd7OXmnvFOcBvDcuEn2g4XwepxeAq960cYk6CoU+e6xkQ2qRwyXMvxBsbcD+/59LVuU6Ml0xAzjhZFUfZyoP9tRG+qHE6xwSP7TMUxdvgIdTby09CbdHx4A/i8zgabXjK5asg+J7eE2mC1RFvHgZajdKosioWhTeQ8+vWAPTODtzSpDC1XkAZtPj5Nz8NRYYkL24Aj+CBG3Rkmi2Ng0ryqwb5UdOIVKX4zoaNM1A7v1mC8i9tXXYoRz9FDwbZc7mf8lAsoOx+uUAv2SevwjWTSHjSEcnB9+OsQhrq5j6tDcM3O6OspW0r8dK4I9FJ/cED56F5Oo2Lh0CvqlyOcQD06uv+ZRMQaxaIV4eQLMZTZKdhjNSLvMaGMTnoG3ncMoikhkcHWC7h/qevUC9BrIcwZS2lDI3La/nZroPwXqa5lTsiTzFa148Jr3zAT3fnlgDiABUYBJ4EyyrOEcJb1k11pXe063A7WKT47Z13pO2PZlhMnPz46XoZtUQzv4sQfXqXpZyqJSmgxp2D1BQkYBHnEleJo703ndvRCIJw45gkrZqGbZDCDsJUuvKcWe2hAtQq1KSL4HHY7r/Wd/Ak9KQPCjTxkgJFt775aeCNmkPpkNFdmmoOOC2PFylok21pJpoHOexBjwWQinlDhXzilrWP7CUThgJ3pCU+3qkt5gowjG9mLKV/bRgB1e9Zmva2sG60QHydknxG/xj3vnkeF3yJeUsOqy6QwAJt4id2giwyajhkGM5IdikYAUo98uewU97B5zuPm4sSwAXSJSBsPTuDCA6wSpqHotWNPv0fIv/OWC4MwgFOqrjksbIaV8Vzi6vgRbDRb+NXX8e3cZxAacp2Rq4aE0whzmkRdpY1rei8Wyx+KX02MgNTlPME7o0OAv2bARLEVtUILrbOMZSfuc7VxzgTECJhB4I2y6RH1JPwl1lXGNVjgfiUPKaUPJCmhiZ+nifhcZbADn33/lR2N0XR8vpHkJjUBSW12A7EW5eIFRWjZpyRFjhVBlhU7Ow3KfXTLayj1gTO0lj9fubDL4X3RwNjYoc/X2Cm1LIDwrK0HRZqZzaCzxGogSw4edx8msaC6Vx6Ip8wRmn0cAz72bmq+CAg/00IoBQX5toQ4a9BGimsvwqGT3jsiYFUjs6oAUBL5bzDpbYy5/BmYkN7PdITbxGQ9Kese+7Kxv3qjgzlSv9HeoP0TCfUioCzTulYezzICXFu6+aA9ortFgAhatu0PcDBEWIy1QUYHIcM6UvsvzmCj7LLgeCeIHUuBd7yF5gaNpXmSIpODrGQ6dpqKONUTQt568Wa6YEzmmTi7pN3xdhu3yZQ5k+BxDg0xyQyybeTIg0lnSBSMP7x3LMxkCSCzvIYjXsznZns91hjwuL8SpPqlYs3qGpWcusqGdso5JkddoECKCvdYu95Hbt3v7sIsjBHBzo5O+an6kXcbZgecBU2DbePrZA0seh79+N0/zpKfS2GgqPm1Hrg+8rrmZ7pV+uDJwYSN/Ic0krtUwHQxlzwbplEi0AkHFJNLh3jdQqxJfY9xNugHYMgAcxltHnpwBroH/PtZi6V6PU3O6h9mAxwsw/xcZbZnouF3/iJ58vFcTW/ucFy4e2jGNUlNdeauFW5wvJiz4txUerAQciph31OS+kBpQjyMePk24jx5WQvgaa7+Aa4YfcKO9YS98NjgD+q9ervWcogYdafsTd9bBKGMPWYo/GxyrF1MxWt0RFqWDPa+ViRo+v6KThk9k8USgYZtGyzdZFGxB50pVB1+pZdPE9Hy1y7vUVkOf34s7138+/m2pv6Veb2XsNlq7ekycRJ04KXLU0WCOMMlC/yGbbNydV5xbW/06Y3Dfs9WptKWosRv2FJ+chWMkdsJN4AfEJG8KddmMUN2x866/J6gtGncQAyhGG40VQ==--4qJJGNVpgQ3/1fEB--8mjgJXG+SInWCclUQkgTqA== \ No newline at end of file +eyhl7vURXPhdpXsR910hbXELz258rNgpMSpfDA2QJZLo3PKQbXVJJffzTMQFq92RPbZgPR1/6k9F6JIeM3Z6wYtw+T5j2dI2W/zFPTe/a9sFXGcgh12G877qGX+Cv0EcK3Nfsuwbbs7+bVgSKDM72gs2HNEsmtH2BPWPQCeDhW0yv0CQT8ORvFSpijzd9FIKQKSNrYXeJjxfCX1YcymmXvTmuuqPjK+Mho4xcHNj1SQ2o9eQZKaz7ff7CrSNDJFJH23WBq92M+rQdRN/nJJoLyUhm+hupDyyeQZSaU2b/uBY7adhPQVNz+TS9CFL4YZ8IHG7yZXLRe1Jn2G+XBLXJGQs9V8cUx3gttR2EM9+hadpVpdsUeqrNWQNpqfdppRqsezilzYPxF6duQIWGAb0JJa4lUr72rWwTVfgngK9nRoQ1gFR84gSpOmgvxIeXy6q27kdckFySbktQOnRWCLs0V+U6Kp2Pvn4/dhp795DulfkOgZ8d5yjTsH0m3a3JySOYBhKuQ089GzutK9xgLIvrN3AgVsg1PjJgnIPvwUtfOQu8MFKvdfzNFyD5mJqXVneNoSxWnCP2+LY5eORa+HWLPZ6iiDrJjX9fXG1ISRgs4ZJP/+C9TrVTJ4RA8lZjZAbdfIrXs119IIY8Bpw99p3Fo8fymvCPIjjkkHYtTSs+NlfgsRxHTJLgmReuAoBZ+3sVXMD+6m/Qb+VVQ6ANkx9iBP0IPR827gzRQo8MclCO7DwKZZnZu5WIFqrV1W1tIdMQnAnVK5GuUynzPRzoUU7XsbblFOqp02b92aD7c2D/3MeHsCG4OQMdjGWpQgltVekW+PAkmre69OYxmra7ATLxPPGwrlSB9c5+PTZQZPalfxfBIRVH49kzEPAAORIb/MgmvC29zibPlvQ5DacvHxiydG9v3h8i5p7vYEaPihjnQTVz5IGTX6j0TI8ugI/7KKg7r0UohoSqKcvsZK5ko1mEDqhH2t19ZWpxNDPeckMW6kfC7ahbDm1wUceb13CbbIrHQzSqtfrWdNbAvwYDyVNmzUzdqnbtlSAcK1Rv8ip9pu+OXRWV9qjt7ZT2HNnfysnnXoZBXTzLy8bZScVCs4KrrHTihOk8D19iHBo1CPbxKyHmxOW1yfWaSexKqKFLtj6u4i31Qoo0uAMAGccFY0ejpXWSBkoGCnG+qSKv3ymK9quWObBVIVfMf5c87Pjdz4XTOLL0KnuQvaF0OGq8TsiDCwhSrmHfxqHhMWwU4B3RWb8E2dqHUP39EO/Ry0XtqWkUJU1A+jFdmtVimpQJwUOJzurBQBDZHE64VfcN3i9zthRWCZri9g2le22Tfkgur+Ty8eLnR55R/eXLq+hmeevr//OqXHvLfct16w3qJNEJztJ3EILHpJ2EJ9ULiPmn8IduTIKND9UO14tYJZ5YSMJ6UM6HkztNpRbIQEqmkqKkdl4wDNTuzTQeP7fMcXN+/yS7sQEKTf0fzjOHLzEml0M8gVRsoOW0yKzYHz8FmYqpZ1Me5wM0BOuNPdlxTNM7WyZG8KwCQusGMB8tU+FtM9AS/gDX8eL3ESAPRQBOg/SUkKSLSLrk7w/tGGM22FDv0fCquO+3j7jPHJIcx1x5DABl0TF7G+Vy1RpfOzsd/7FnhDX1+QCOGcXUMcpOJRpD4tn3xF759EWgfqtSXczbDVnHH0thEEBX0QOLwZu25NsKDdDQxeDZW/iF71jkrKhJYtfoUJKMpAuar32TRNM7vdipH5V17b1b0yRVSgr1H1XROI7kzib2kE3tLGK+yVMtQWADBq+pFutbuhC46sNZX7eXxJeEVpypQz8g0QGZsdY7h6viMp5YBflPoTkHqOvHEydUTYjPzr29cSXC7qXgbJxUNBK4NpjCJ9SbQoK2euQXAHsQlRH5SmRopHXdNcwPamV1BBuGfrRQdF/W8yI3S2zAZEI83dOxo2IqKlMbYjQQiGoqQl273sPE3cL5unhecd+m8ZWG5ekqiZXVrvJW1mEe/Mf41EOHPTW8d7te4KySp1c/c/Hz292EU5HvQ+cflozACS30fp5OIWrlEDr6+RpwQp38LsNWk9SLs9iXJl9boT1aZeHjNQr8cN6Y+oBYdlgYF3Avyb/Rw/YWKeQ8+FWNtdNLrRRUslhVB0EK34OQEWE6B/gFojAcM7qmmng5AtDZ+JQyz84Iik6mXsvHuRG4Ue9e5e3a6Pla/9lTOy1aFd4rcbYbSIbMoq3afu7uMJaHzcH7E1FWqfsTO9PhU4L61t16+028FWDfLsnjrBwmz1cNdr0B3/zMtKMAlfUwN7eWUM6Ty8VpfxyB5mxu/crfL1zO1AQs+oHoGk1qlLzdWSK0H/Kt1S/GSBhbezNtAVdmQVZo0uRjuJB6lDdox/RpjZzpx3tRxOwHO1pJBo9NU77WEGty0CNEUDZXS1iBHjEhixjyTEo85j6YTRsIyI1NGAQZKqv0rLzET212p/WAE53ph+4VelJXBLE2Zz7fMPYzUFqkd18HSdReeHI6GZMnyZFFQCR0xcDCZgb15N10+A6FgLmij60Mk/74HXKNmqzE8rGaznojv5uppRBoOkZHaaFu1q04J6TdE9IGkO3XRtwsDFcWb0XY0fpB8ujudKikdjcsf3vk4HYtpQXGlI3sPwP9LtEWNKgBONeDn4hR/ERPVJ6bUR5+cVcYmNdgPFygi6S0vhWBUqWLW4s5uxm+7QW2al3dLV+oaRTqFaDAW2XBDflEKqXr3+z7LiKDKMGy9WM59kvcKivYm6J84b56jYqeQmABUmMtIII1AsM7l6lF0nO/lZnqmoS9Rn1swbSyhNr0i/we1lOQLfuH/S5xVtjm9Cqc3uqgcERGCAuawvDfJ/KIC5l2TP7AJaL81+z/xFLz6X/CFYLoZMZ/eXrCGTgUwWGzAiB7PqNOkMlvYVcWSlCYkU3EwztgN3WmwgYYIJn/ubdqGyF3ITn4Ku8MxnhDlM3zqxNTbCe0Y2+7UoMwDXYXsemA6Sm4gjiS4Hm2SEBJTDwPEKt+OIhQDGFH5EwGgva6usnvL377qyom1D+z/9qAR/jczLVsb4TQMNCIhNKJXA1hmxAdxLvkkaeEfb6IPdkCuMxhU67KJ1H67MvFygMd7TKCH/mhk92nKyJJGtTWtvx2a8F6KQWusgxpr2PkM5AuMqoWr8OaEBb4Qd9r9BRyhTPtNURrW89slO9apw9SGQlNrF7I4GeAlnUi5YVW6VbfoDucc8+4YnDLhxDzjDFs0IdcgdQOaDzm55EPIEJaz8rdemLp/qRJvPwGbYYy+xmm+2lYv7zhiEOQBYTEvYdE8uyViaQP0bg7mEYP/SpMcHK97cKVVYbijvlvDXlJe5hIky4ga+x209EIMc1XksfRypNNhAX55d2/0LHKQkiY964mlqTdlK2iYho+5mUq0g6taCZzqBtgAF8xdKmk34XT9jOf7an+3ozkPibXBvaYJtZlVmWcd77XFlqtwRIXibERaxhN/qQLGVKZ5i5dGSpdDTvuWo4z0bSjhFsnD3dGK5M4WZgPBBj7UO44dfYQN9hPKklgqxJH18rJMnI7L+xWY07ez/T3SzoXl6uNyhxF3rBOOAiMzttMW45WLE=--fG9jdDXr7yO2OUx1--oDin1EisdWlIhUFEcduqNA== \ No newline at end of file diff --git a/config/credentials/staging.yml.enc b/config/credentials/staging.yml.enc index 76ab6ed9a..4d3a37c71 100644 --- a/config/credentials/staging.yml.enc +++ b/config/credentials/staging.yml.enc @@ -1 +1 @@ -df+arMqTyLOBZFcuLABSJ3b2ZnwAnquwmFkbJgMONfqpRcYFybT3R6rkdV0rmDtaIJOoeRUGLflzqAiyy9YuDuPb0z9iy4GbqmuaCTaJq8nEmbfXzceyFePLg82L4u6qnEYh1Rh7FyXRB1kj1houEw2N+ZGHzJT4JJRJkv++LnVwItDn5HoelScK0iz9EunuMOf77MDBnV49MfGwIVT4F1gZi141T3Idg7P+v53fkA9mMYrAXouz3W8iLELn3aO9F+9bbbcC5SU5gJkzo+q0V3dal4RObT0rLRtuD41OuK70sVaz5pyqgykoT6HNkht4fnQEojTk9aA+5lsyjm17geZiIR6NIScMIPVDVCsbfJ8e7hi9ylTbwacQUMQHe/SenjTSlZbvwl9BjmJedB29wAmlA20HmCSRZZlh8XourkbTouRPwDPcmeAnsKDwHXtOUJXeugOer9UcG+52xvy0IEwWzMVqSCqWB0P0TsJ6vBL2vbrzP35nPczL2LyYzXlyVw2rj/HGIetfigTCJ/K0qlAMY/Pb114Pc92kwbn9O0H7a4MqlF3p3YRnjayKmpCLz7IRW91cfRL24M2XfjF59kBKnuGIhQdcjF/ErQCaEw9GkGMNeFBphUZs3s8E7WtyyAtyVU7FOqViaLVAgm/RtFZrlgnCdsYGlWoUJUYeeje9+KFfanNK8s9mlLFDyCpNNSDl4Mtk1watsA/uJqeuf+4E+aiQkk7P7+x9Oi2YeDbpS6rjJ6/ruS/t6qu2NiIP/Qhq7TXyJLIK8sFRW5n867y1HkOAs89sabRRmCj2/y8IQrHXAkXplrwCLfkK60WuNg7XZS13QkoitVmtyBB/nv9uDUu80KBFnDsuDBRnH23Z7STM+d7eFBqvVSLCrDj5rpwN4/Qk41+C/Sj86TKQgQsY5XMiX5TW5icTk1fP75norpZKdXvd5dYtNfHPF2yOD8W33RMb+f/DOtfLwmS7t/4QBW1RTqx3ZNb7MJfKIi8PImOiPpii0sGByObphydnTPrMgB64H1LVSHzKaqzqu4zyqG7qzzNCmjJJ1pUci2rOalM/v3+EJUfOaITGumNVZXc3uA/Xd9bzd0VqxklVFwJcjhGYZ9VQh+fPInU6wwI/m56pGESPaJ4ZalyZ3t3mMrAGBFt/8UIIZjZgb89vpvjC8D3XmSp425R2K2Nts8DSzDqccObhKYROFzVlb6EbcY/u8SUpFyTPBMBv1rXwEmzCfZyIfMcnP6AZhtl7zM7ehCi6oS3ZbYIES/ZE9mr+1fvzH3L5K6oO4FpzBe36BHYsbejf+tl9qGyYBWdQw13gLJbZcMEThqJGGNgwUGj+ZhE+F7AaieCRX4Zr6nMIqADje8GtNK4L3W6dGE9rpUmSO2GZlWXm8pq+pgSJ+6Bk4F3wrISLgBhIo2kG0+EfgS+LHDv1CCXgf9YrHP17ViA2Kn1HgNqGWVOQqPY6Po2P2zAcyRFZ/EFp9VG0e8rArP2AzqgkiVQ7T7rzszEN3Rm/YawK2EToRICPIQBovCdjoQjhQ8/KYQKoW9aO/KU6/aewlNDSbE56R5vdTzRKSfDeMjym7piAmlMMmfKZFmEo1GzlNm9iH38tzErYE2pDzZi8vIES6gJ9vbYjKyIz+ayACFjB4aa0g5vw7NAria+RusfmsDY6aYPSMDftsh1woPQl0jbNK3oM97L0S4aoi7xrFX7cd30ICbxad7tIUMHa+Upl/LwWA0mHbrUFA+1lcjeDxMGurb+GnHgbhBUZjrMhPJm9FzHg64TrvlQv4kRB+azcPq58fL8wJsh1N3VRgAFVRYAQLXBHIzifZ7VpDOE+GGgvh8gaDC/oyMQ0CYhBd98Xnk+A6dwRf6wHQHOub9SIJee9kOhIt92F/WAYYJPQpCt6gLWCghvZr+xy3ZuEvnglrGLOwjxru+A0FKVXObTUCcuzv3wq3DfuT2FBcoZeIHG0y22szqNLxiA774BpG/vzQEf8oprvyw5dKtQiHW9S0WgNJ9SReNXEobad0ONulu826aDZGkdSuD2SOwm8NVMzLGRYjBMAyFgHjnJKj/ENsJ8S13NWJU8I3lNjpL7fNTqUvM4iBxkeelcaVZIa258kMtBj0DP3z+1NBJxjkBi7jPeU411tB9vUI52bEcNR7DQPUQV9FQD9/8Y+S6TB1UVbHiN1Y13Kt0Kr7JoyVGMrLQRZr75NWhi844H4dE8lRNxMQ33bANUiNOfIRfmGoAr7bT2Phw7JM4y3JbpBUdnOzdVVQP8kz52hS8Wv3fuJpIQg3h+WX1MpgSPKYmB7MdHB/o1w+j9CgWa6y9Z8NkRiaNeMwwLMZly4InGX8mwEisWRfTE29e4RlOnHkdcbxpD/Ha2k++FpzutFaDRDfZeEnRmPsXUkOnACseov5CfK6FKhK2oJrhqnBGZXQlj+006euj7BwLS7kGp6bA2Bt2SgzzQPeyJ7XsgukIctsiwDMG24Bhbsx/SHX4PwQ6riYUNsI901tumKTRKi/9/kMoK3mHRmBOU0/7ak21goQbCPt9spzKMqb1ck1I7UYcUZqIN7XMdPOLlSs9Iriuu6wjRwwTujz0lx7QT9USui/TC0FsJr6rkpN3SmE+AZWSmdVAduglmkndmq1BYZd5JfBIY2Ln7YRwqha8/7YDInRYCz1ygsgRyKY7IuxPkiYROUJT1yYD9rIXilKwYDy3cuK/85hToqALfiO6asYokpFsY7muI+QlCs1SpULehV5QtnhB6Dpk+q9P8400qPdvaGIh6/F6bX0iWGbd7ist3fRleZzc3fRnc2UPf6hFzCox1ZOE3EqjzVmOQC812fOB0NCXTYcnppxBFWm+pwpdoec2AXGO5dH5n1RwjBlaMNA8mVgOhk6YuIifXrns9J+UteE0KHyCfFJFeD7joVKGXNu4VAhmobYsxYgBc59ZgLUA+Y0Y5OVCvoQoZsmmvj3mfmoF1c1pyGd5Q7K30Y/SlHfgkW+YmXSOSzYwXCiAbiBLd7KCdc5QGgli/CUBAkmxtZ/jbX/HpLdF90ucVBXMDSNJvO4wvcURNZdcIAec3VDNOkQh6CKcpcCHD1KZAlB7Cu+pKvXV9K0Uvow5UyK/SASqgdZyUpRPR7mA88j+K81sZZvHkVidZYeO2K2SUKnTrrjKPaiNJLDur8z2zGY2sMhAkqtihMoXH6C7Sx0VkCRbxuWCLbfFIUNZHC5bOvOokM17ojEg2f+tHtpkwGCJJXeujle3QOay7lk4a4ncNVKPXBCrAwSI4MfMAayHo=--m9FT/eiK2duMeoPd--TPeu8jM4DG29Rd1MfvgePw== \ No newline at end of file +rihx2kxKh5UVI9e9H3Jyw2FmZAHnmZfSgMkBxAlYWkA7nSrBJSZmWUa8Pq9iHydgXf1Laj8PQw5vQtqVNseD8FJ1ini29Gq6fJVAYKbuwXmWlIFPI8AdIfT9HeTwVzmCz1wMlczJILX3Xh/2sr7qfSbw4YTH2/KFt8rcebEkx9ZvgNnc7xZ3VtaPbeIJ25RLyLHFo/l3h7efIR3S+X4zbg3IltPkKESH4z5uAksTGYZ8YwcuF0RJbYKnnTgPfEsu6qLw3VJmCknAsUYS8FILRCpM8aihtS0ktcEYgDJAe4QFPq1z7D0sVZWdzbRdHcSJp4iktOnhKJl3tCrtZTOS4p5tNTWZqt7wYIgrHf/TUz2aVVIcLyJVNiTsT9Y4KHbfq0eIDpdbRF7quWfhTV+2mEneCViORp6bVn7X8tkvFT1w2aj5Ka3kVA7pfZxLgwMvgeGPJdlAbrdjPMp1Q4cK5KG2fBQJFRIbKB1Y8AjatJyqIqSGHDQ61CcC5qwSRwmbPfquDxGKZPcMuPGR5cUX5ccDGF+BAqqZ3RnukKm0G4m06Bb6NMd6scfb/w+t1kg+2VHbWLnKsyfvx1F0YXv7ACVTOztAcDPuFKg+gnJIIUOVU0xDQEIVH7PfHhHrjDXE/t/+aPsctsSzB3xqfy+k9IFiksL8JjoDhbAgE2mbuLChuslPAnRHP92JqTQ6vx0IQqJmmmWmBSc0iZLGd5Kmsi+gZifH2Y9OZ0Dz4KW4SLKeM53V2nW7mPvpaHaE8ijOWmzgHGyhTCARvcctCk+6McpN63z7zwL2mPbiUJCwMtJptFHaXOLnZ8DudLzqaPChBJy1tXm8lxJrSKZAbC+YspWe4Y16HJn6XARpy5/ta4eXnQoJJ3N9JdOm5NJFXQ6jaOodIGV+7RHNPcV4mDHpZJPBsvB9lm3NEPBHAU41Huc3NHCKCf9OaE1nsKJSvoA6Ei1HQTCzdMfnLY/PWixCEIGMVyh7At5ADGn47WD7YkU9sbOiyAfcUgJ96tE51APzj79slrDtYNJfI2UwF0WY44OKifgXnr8sH4DLlWz+FfpZxYFLx2ab0cbgmTBrS2JV6xYPZXTFjNo5m8zIETUHOb8ty5jUCBR6X+t34iJcEzYH5fveT/2KF2LcnkXIRUFY3pPJA4YWl/kNPhc7FOe3ELzrW/X3F2ef4yD2Nq/ZN4Bk3L6DreltVMA9AKVUt951JhF957Np6pndu85zxHDzvzG6xLd1D2XhVKfoUewY8i2PdicHG6eb10twKj4GpoJYQFxQ1ITfZ4fD/dIs3MnUMiyzIR0o2PDtrZ7hoHMuPRCzH61TGuxo92w2KJSMRQHSSyNW69Du3Hob/i4gbSTmfrI322N5BzYx4FCO2nZ0rJpeNEa/Qt7fKmqNWw8MQeIITu2+5RYRA8VbuTtj0E5a7WhlHV75KgaU+GnfDRD1xDpjO9VvWvaA5CRB5q2Ab/GsG207ywKunPDJ8B0bkeQAq2QoRP4HBVndroli0Q2dJWcQj++sH9nR1DO3l/CVT3CFkGjYYd2LtlfWgafyZhSB+1F/FCjvomj9VZJwen2TCFw3XlvbM0JQxv4GsdBANLJaOa+vROY4l2pIZVo/w2Mnr+qZ4dh0GlV0MT1tMjUpqQN8/SyFO7fiGmTLgR8e0xuzEuxlIkiRZK068PBe8IZZrPTgwqJYqn55nbTq0vJLcTu81DgPwlnKCnP0IOgWp9IrDN/LkzJqh3lBM1Fqh29o/dkfbmpPb1aujoBo3/cIcGVT1GhQouKGStrhtCleuRuzLzZfS9GhKl4I9jH+NqeN3qblm1+AMEuELDUWNLXBQ42A7SjQ4UEOI5EbvOwsSHMfYUV0xA7uMKfLX/yc7VdGKQ0C/PIAyDoWfsNC/CNaSHRVUsu10h5EtaXMC42r0ovaGhJgUZZIgnk80mu0MpyE2N5i2zmQpx1iH4JzeOzgyIGjPCpxSHjTJSUuS5E8177vC1zOohsGEts1diE27WfyUM88VQ45lfFmVPwzWmdxLebzQN+Q1XfIswE47VAuB20zOhBCajayA+gd4/WUpUyNzdRp6AkcZgF9g6mH6p0ulzABMR1Kt0J6wmFNuR2rx3WfXW9+TSQ36OzESkQ2WzLM6LFLXUz0NbP4cY1K/0lWMUrq/RBBFFY6dke2v8L4zZcX0klfvjssV9c2UnSzgzvWpG6vUXMHR6bVr+TUwMSp3Qjame5vAd7QFVv8yuOPllo+QQZyRS1LeQnvRma5B7B/da8sdAZCPioKLdVjATKD9vm52y6vSXjmHmvIr7c1usLJylI4JcKDSnCjShgxrobF6pel+uTIHpzcGyGnGEKl/q7RVaZmC2/d+RKxU7WbKbykVBMEFTLCNhk5qXWY61TmYfL/q2g2z6VM/8iAi/5Xw8Nt3yaq1pvgz7OplAkf9LattQZEMHbiIOm8EbseHi27Vr9hOJmwhU1NGM/qawF+N13omsRbirimN5bTuXQveLMRZdeBeiic6fUzeuqBMDJaDp1NfoLopm0MS/BqvWC9+940xIwWzWFVT3QJhfxWMrSqD7Ri2bRCWELGBw4UQwMTxKmwtLX3m5u8HcWi0AtsStNB5Eqeg8GgxLuUJ6K0lUVK9x27zY/yX4j886u0iQd8oeqVgv+P/ln0HYQq1rkfnRNTbHWIYKe0vEZkExAq3QUoFmS7e+IvmjBJfTGfE0SlPc0prwZq98oPWQAtqCQfhbaS11CaHuW4XvMJSus7IXcTHCwY4oWlP60zdTA35LH/VIGWI99xOUXyM12354goElC0m8wzissQdCkbFTY6MdqhRT0k0bnbRDV0RdG5uF8x6ZFLlD1iCETKMO6Zeucu82OisVBwBq0Do4BgsfexGujfVStbf55c0p+8FwnHzOHfgYLHuV2HPdlrPTSDbe2Y1I3e+qIN5wtJj5YbZ1v485ZN6mPZXysVSjevf3z4A0laCmx06Be4r9APkWrUQRx0cNsvBcC6BsEEcP1Bj4o7LO7+L59GJMR4hQgjzaQEoMTu0rBDbQeQJCei2xxNY6p986Y9PWJnzISa13JsxZmmiMukISADGv4jf9Cglqqqpq0dGzwKR26tzFFthUc+1xtXMLAEUpM6bN2Iy13T7Y7o80gw1hK5G9BQV82n+h7FbHcy0B5oKO0/aeCGv+pTdYGu83NuBsFhsGiVKiN4aTRLAnfwAxavwh3/EFP4Pdfs8AMAC706NXRUglFB4iOARZ3xaHERlfmnNMpFZBdMyBelmzCHXLB5qE1z+KfleBP2on6l/AKkX0KKgdngzvJtiIvPytJMz882CmTJp29k1KEnR2nRWAisU0TDhkr1j1cGsoFe791catt5n+ckOvresRZwd1t5aTOBzCX+SIrNVx2jOREiS2LdmkmhUUFMOW/GJMzItIj11bDDjqROA1sFH0UOxxle7D6RrzV5hutHxiJSHW4aIy66Npqa4ict/Dxb7Mmy9BCEEcMsBudh--a32vZ5YpTU0ayqVe--nJ3rwMYkjBcDjZKdW3wyew== \ No newline at end of file diff --git a/config/database.yml b/config/database.yml index 3b51f642b..26026bfbb 100644 --- a/config/database.yml +++ b/config/database.yml @@ -6,7 +6,7 @@ default: &default host: <%= ENV.fetch("SAPI_DATABASE_HOST", Rails.application.credentials.dig(:db, :host)) %> username: <%= ENV.fetch("SAPI_DATABASE_USERNAME", Rails.application.credentials.dig(:db, :username)) %> password: <%= ENV.fetch("SAPI_DATABASE_PASSWORD", Rails.application.credentials.dig(:db, :password)) %> - port: <%= ENV.fetch("SAPI_DATABASE_PORT", Rails.application.credentials.dig(:db, :port)) %> + port: <%= ENV.fetch("SAPI_DATABASE_PORT", Rails.application.credentials.dig(:db, :port) || '5432') %> database: <%= ENV.fetch("SAPI_DATABASE_NAME", "sapi_#{Rails.env}") %> variables: # It is important that ordinary queries do not hang while waiting for a @@ -24,7 +24,7 @@ default: &default username: <%= ENV.fetch("CAPTIVE_BREEDING_DATABASE_USERNAME", Rails.application.credentials.dig(:captive_breeding_db, :username) || 'postgres') %> password: <%= ENV.fetch("CAPTIVE_BREEDING_DATABASE_PASSWORD", Rails.application.credentials.dig(:captive_breeding_db, :password)) %> port: <%= ENV.fetch("CAPTIVE_BREEDING_DATABASE_PORT", Rails.application.credentials.dig(:captive_breeding_db, :port) || '5432') %> - database: <%= ENV.fetch("CAPTIVE_BREEDING_DATABASE", Rails.application.credentials.dig(:captive_breeding_db, :database) || "captive_breeding_database_#{Rails.env}") %> + database: <%= ENV.fetch("CAPTIVE_BREEDING_DATABASE_NAME", Rails.application.credentials.dig(:captive_breeding_db, :database) || "captive_breeding_database_#{Rails.env}") %> database_tasks: false development: diff --git a/config/database.yml.sample b/config/database.yml.sample deleted file mode 100644 index 64521c841..000000000 --- a/config/database.yml.sample +++ /dev/null @@ -1,29 +0,0 @@ - -default: &default - host: <%= ENV.fetch("SAPI_DATABASE_HOST", 'localhost') %> - adapter: postgresql - encoding: unicode - # For details on connection pooling, see Rails configuration guide - # https://guides.rubyonrails.org/configuring.html#database-pooling - pool: <%= ENV.fetch("SAPI_RAILS_MAX_THREADS") { 5 } %> - username: <%= ENV.fetch("SAPI_DATABASE_USERNAME", 'postgres') %> - port: <%= ENV.fetch("SAPI_DATABASE_PORT", 5432) %> - -development: - <<: *default - database: sapi_development - timeout: 5000 - -test: - <<: *default - database: sapi_test - timeout: 5000 - -staging: - <<: *default - database: sapi_development - port: 5432 - -production: - <<: *default - database: sapi_development diff --git a/config/deploy.production.yml b/config/deploy.production.yml new file mode 100644 index 000000000..a687b2def --- /dev/null +++ b/config/deploy.production.yml @@ -0,0 +1,35 @@ +# Name of the container image +image: ghcr.io/unepwcmc/sapi/rails-production + +servers: + web: + hosts: + - example.com # TODO: Ruan + proxy: + hosts: + - www.speciesplus.net # Public-facing domain + # TODO: Ruan, do we need to add `speciesplus.net` (without www)? + ssl: true # Proxy terminates SSL + forward_headers: true # Forward headers like X-Forwarded-Proto + healthcheck: + path: /up + logging: + request_headers: + - Cache-Control + - User-Agent + - X-Forwarded-Proto # Critical for Rails to detect HTTPS + response_headers: + - X-Request-ID + job: + hosts: + - example.com # TODO: Ruan + options: + add-host: host.docker.internal:host-gateway + cmd: bundle exec sidekiq -C config/sidekiq.yml + healthcheck: + cmd: /rails/bin/docker-sidekiq-healthcheck + +# Environment variables +env: + clear: + RAILS_ENV: production diff --git a/config/deploy.rb b/config/deploy.rb index ce0f0d011..f0a1295f9 100644 --- a/config/deploy.rb +++ b/config/deploy.rb @@ -20,7 +20,7 @@ # set :format, :pretty set :rvm_type, :user -set :rvm_ruby_version, '3.2.5' +set :rvm_ruby_version, '3.4.9' # Sidekiq config set :sidekiq_service_unit_user, :system diff --git a/config/deploy.staging.yml b/config/deploy.staging.yml new file mode 100644 index 000000000..3009feb1d --- /dev/null +++ b/config/deploy.staging.yml @@ -0,0 +1,42 @@ +# Name of the container image +image: unepwcmc/sapi/rails-staging + +servers: + web: + hosts: + - 172.20.0.146 # sapi-web-staging-01.internal.unep-wcmc.org + options: + add-host: host.docker.internal:host-gateway # Required to reach host-bound Redis + proxy: + hosts: + - sapi-web-staging-01.internal.unep-wcmc.org + ssl: + certificate_pem: CERTIFICATE_PEM + private_key_pem: PRIVATE_KEY_PEM + forward_headers: true # Forward headers like X-Forwarded-Proto + healthcheck: + path: /up + logging: + request_headers: + - Cache-Control + - User-Agent + - X-Forwarded-Proto # Critical for Rails to detect HTTPS + response_headers: + - X-Request-ID + job: + hosts: + - 172.20.0.146 # sapi-web-staging-01.internal.unep-wcmc.org + options: + add-host: host.docker.internal:host-gateway # Required to reach host-bound Redis + cmd: bundle exec sidekiq -C config/sidekiq.yml + +# Use staging-specific Dockerfile that starts puma +# (uses tail -f /dev/null) +builder: + dockerfile: Dockerfile + target: exec-staging + +# Environment variables +env: + clear: + RAILS_ENV: staging diff --git a/config/deploy.yml b/config/deploy.yml new file mode 100644 index 000000000..c212863a4 --- /dev/null +++ b/config/deploy.yml @@ -0,0 +1,49 @@ +# Name of your application. Used to uniquely configure containers. +service: sapi + +# Credentials for your image host. +registry: + server: localhost:5555 + +# Inject ENV variables into containers (secrets come from .kamal/secrets.{environment}) +env: + clear: + RAILS_LOG_LEVEL: warn # @see https://github.com/heartcombo/devise#password-reset-tokens-and-rails-logs + PORT: 80 + RAILS_SERVE_STATIC_FILES: 1 + RAILS_LOG_TO_STDOUT: 1 # Need this before upgrade to Rails 7.1, which then default is STDOUT. + secret: + - RAILS_MASTER_KEY + - SAPI_DATABASE_HOST + - SAPI_DATABASE_NAME + - SAPI_DATABASE_USERNAME + - SAPI_DATABASE_PASSWORD + - SAPI_DATABASE_PORT + - CAPTIVE_BREEDING_DATABASE_HOST + - CAPTIVE_BREEDING_DATABASE_NAME + - CAPTIVE_BREEDING_DATABASE_USERNAME + - CAPTIVE_BREEDING_DATABASE_PASSWORD + - CAPTIVE_BREEDING_DATABASE_PORT + - MAIL_USERNAME + - MAIL_PASSWORD + - AWS_ACCESS_KEY_ID + - AWS_SECRET_ACCESS_KEY + - AWS_REGION + - SAPI_SIDEKIQ_REDIS_URL + - SAPI_SIDEKIQ_REDIS_CACHE_URL + +# Use a different ssh user than root +ssh: + user: wcmc + +# Configure builder setup — builds remotely on the server so localhost:5555 registry works +builder: + arch: amd64 + args: + RUBY_VERSION: 3.4.9 + dockerfile: Dockerfile + target: exec-staging + # Use the version in the dockerfile + # args: + # RUBY_VERSION: 3.4.9 + remote: ssh://wcmc@172.20.0.146 diff --git a/config/deploy/staging.rb b/config/deploy/staging.rb index cbec89947..5e5dc6593 100644 --- a/config/deploy/staging.rb +++ b/config/deploy/staging.rb @@ -1,9 +1,9 @@ set :stage, :staging set :branch, ENV['CAP_BRANCH'] || 'develop' -server 'sapi-staging.linode.unep-wcmc.org', user: 'wcmc', roles: %w[app web db] +server 'sapi-web-staging-01.internal.unep-wcmc.org', user: 'wcmc', roles: %w[app web db] -set :domain, 'sapi-staging.linode.unep-wcmc.org' +set :domain, 'sapi-web-staging-01.internal.unep-wcmc.org' set :application, 'sapi' diff --git a/config/environments/development.rb b/config/environments/development.rb index f994d2d70..572e47ce5 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -3,9 +3,7 @@ Rails.application.configure do # Settings specified here will take precedence over those in config/application.rb. - # In the development environment your application's code is reloaded any time - # it changes. This slows down response time but is perfect for development - # since you don't have to restart the web server when you make code changes. + # Make code changes take effect immediately without server restart. config.enable_reloading = true # Do not eager load code on boot. @@ -24,7 +22,7 @@ config.action_controller.enable_fragment_cache_logging = true # Use a redis instance as a cache store in local development. - config.cache_store = :redis_cache_store, { url: ENV.fetch('SAPI_SIDEKIQ_REDIS_CACHE_URL') } + config.cache_store = :redis_cache_store, { url: ENV.fetch('SAPI_SIDEKIQ_REDIS_CACHE_URL', '') } config.public_file_server.headers = { 'Cache-Control' => "public, max-age=#{2.days.to_i}" } @@ -40,6 +38,8 @@ # Don't care if the mailer can't send. config.action_mailer.raise_delivery_errors = false + # Disable caching for Action Mailer templates even if Action Controller + # caching is enabled. config.action_mailer.perform_caching = false # Print deprecation notices to the Rails logger. @@ -61,9 +61,15 @@ # Lets keep this for now, its development mode only. config.assets.debug = true + # Append comments with runtime information tags to SQL queries in logs. + config.active_record.query_log_tags_enabled = true + # Highlight code that enqueued background job in logs. config.active_job.verbose_enqueue_logs = true + # Highlight code that triggered redirect in logs. + config.action_dispatch.verbose_redirect_logs = true + # Suppress logger output for asset requests. config.assets.quiet = true @@ -80,12 +86,12 @@ # config.i18n.raise_on_missing_translations = true # Annotate rendered view with file names. - # config.action_view.annotate_rendered_view_with_filenames = true + config.action_view.annotate_rendered_view_with_filenames = true # Uncomment if you wish to allow Action Cable access from any origin. # config.action_cable.disable_request_forgery_protection = true - # Raise error when a before_action's only/except options reference missing actions + # Raise error when a before_action's only/except options reference missing actions. config.action_controller.raise_on_missing_callback_actions = true ### diff --git a/config/environments/production.rb b/config/environments/production.rb index d93986aa5..dec3831ef 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -6,22 +6,17 @@ # Code is not reloaded between requests. config.enable_reloading = false - # Eager load code on boot. This eager loads most of Rails and - # your application in memory, allowing both threaded web servers - # and those relying on copy on write to perform better. - # Rake tasks automatically ignore this option for performance. + # Eager load code on boot for better performance and memory savings (ignored by Rake tasks). config.eager_load = true - # Full error reports are disabled and caching is turned on. + # Full error reports are disabled. config.consider_all_requests_local = false - config.action_controller.perform_caching = true - # Ensures that a master key has been made available in ENV["RAILS_MASTER_KEY"], config/master.key, or an environment - # key such as config/credentials/production.key. This key is used to decrypt credentials (and other encrypted files). - # config.require_master_key = true + # Turn on fragment caching in view templates. + config.action_controller.perform_caching = true # Disable serving static files from `public/`, relying on NGINX/Apache to do so instead. - config.public_file_server.enabled = false + config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present? # Compress JavaScripts and CSS. config.assets.js_compressor = :terser @@ -29,8 +24,10 @@ # Compress CSS using a preprocessor. # config.assets.css_compressor = :sass - # Do not fall back to assets pipeline if a precompiled asset is missed. - config.assets.compile = false + # Cache assets for far-future expiry since they are all digest stamped. + config.public_file_server.headers = { + 'cache-control' => "public, max-age=#{1.year.to_i}" + } # Enable serving of images, stylesheets, and JavaScripts from an asset server. # config.asset_host = "http://assets.example.com" @@ -48,11 +45,13 @@ # config.action_cable.allowed_request_origins = [ "http://example.com", /http:\/\/example.*/ ] # Assume all access to the app is happening through a SSL-terminating reverse proxy. - # Can be used together with config.force_ssl for Strict-Transport-Security and secure cookies. # config.assume_ssl = true # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. - # config.force_ssl = true # TODO: should we enable it? Rails enable it by default since 7.1 + # config.force_ssl = true + + # Skip http-to-https redirect for the default health check endpoint. + # config.ssl_options = { redirect: { exclude: ->(request) { request.path == "/up" } } } # TODO: Since Rails 7.1, the log no longer output to file, but STDOUT, which is the better approach, specially suitable # for docker. However this project still deploying use cap, we may need to change it back until we ready? @@ -77,18 +76,22 @@ # Prepend all log lines with the following tags. config.log_tags = [ :request_id ] - # "info" includes generic and useful information about system operation, but avoids logging too much - # information to avoid inadvertent exposure of personally identifiable information (PII). If you - # want to log everything, set the level to "debug". + # Change to "debug" to log everything (including potentially personally-identifiable information!) # config.log_level = ENV.fetch("RAILS_LOG_LEVEL", "info") # Leonardo: override default behaviour and hard-code warn. # @see https://github.com/heartcombo/devise#password-reset-tokens-and-rails-logs config.log_level = 'warn' + # Prevent health checks from clogging up the logs. + config.silence_healthcheck_path = '/up' + + # Don't log any deprecations. + config.active_support.report_deprecations = false + # Use a redis instance as a cache store on production. config.cache_store = :redis_cache_store, { url: ENV.fetch('SAPI_SIDEKIQ_REDIS_CACHE_URL', Rails.application.credentials.dig(:redis_cache, :url)) } - # Use a real queuing backend for Active Job (and separate queues per environment). + # Replace the default in-process and non-durable queuing backend for Active Job. # config.active_job.queue_adapter = :resque # config.active_job.queue_name_prefix = "sapi_production" @@ -103,9 +106,6 @@ # config.i18n.fallbacks = true config.i18n.fallbacks = false - # Don't log any deprecations. - config.active_support.report_deprecations = false - # Do not dump schema after migrations. config.active_record.dump_schema_after_migration = false @@ -114,9 +114,9 @@ # "example.com", # Allow requests from example.com # /.*\.example\.com/ # Allow requests from subdomains like `www.example.com` # ] + # # Skip DNS rebinding protection for the default health check endpoint. # config.host_authorization = { exclude: ->(request) { request.path == "/up" } } - config.hosts += ENV['ALLOWED_HOSTS'].split(',') if ENV['ALLOWED_HOSTS'].present? ### # Everything below are WCMC custom settings. @@ -125,31 +125,23 @@ config.ember.variant = :production # Custom email settings - mailer_credentials = Rails.application.credentials[:mailer] - config.action_mailer.delivery_method = :smtp config.action_mailer.smtp_settings = { - address: mailer_credentials[:address], - port: mailer_credentials[:port], - domain: mailer_credentials[:domain], - user_name: mailer_credentials[:username], - password: mailer_credentials[:password], + address: "smtp.sendgrid.net", + port: 587, + domain: "unep-wcmc.org", + user_name: ENV['MAIL_USERNAME'], + password: ENV['MAIL_PASSWORD'], authentication: :login, enable_starttls_auto: true } config.action_mailer.default_url_options = { - host: mailer_credentials[:host] + host: "www.speciesplus.net" } - # fix for current version of mail gem: https://github.com/mikel/mail/issues/1538 - # config.action_mailer.delivery_method = :sendmail - # config.action_mailer.sendmail_settings = { - # location: '/usr/sbin/sendmail', arguments: ['-i'] - # } - config.action_mailer.default_options = { - from: mailer_credentials[:from], - reply_to: mailer_credentials[:from] + from: "no-reply@unep-wcmc.org", + reply_to: "no-reply@unep-wcmc.org" } end diff --git a/config/environments/staging.rb b/config/environments/staging.rb index 6b20dc228..10c1e7ab4 100644 --- a/config/environments/staging.rb +++ b/config/environments/staging.rb @@ -108,8 +108,6 @@ # ] # Skip DNS rebinding protection for the default health check endpoint. # config.host_authorization = { exclude: ->(request) { request.path == "/up" } } - config.hosts += ENV['ALLOWED_HOSTS'].split(',') if ENV['ALLOWED_HOSTS'].present? - # Inserts middleware to perform automatic connection switching. # The `database_selector` hash is used to pass options to the DatabaseSelector @@ -136,31 +134,23 @@ config.ember.variant = :production # Custom email settings - mailer_credentials = Rails.application.credentials[:mailer] - config.action_mailer.delivery_method = :smtp config.action_mailer.smtp_settings = { - address: mailer_credentials[:address], - port: mailer_credentials[:port], - domain: mailer_credentials[:domain], - user_name: mailer_credentials[:username], - password: mailer_credentials[:password], + address: 'smtp.sendgrid.net', + port: 587, + domain: 'unep-wcmc.org', + user_name: ENV.fetch('MAIL_USERNAME', nil), + password: ENV.fetch('MAIL_PASSWORD', nil), authentication: :login, enable_starttls_auto: true } config.action_mailer.default_url_options = { - host: mailer_credentials[:host] + host: 'sapi.sapi-staging.linode.unep-wcmc.org' } - # fix for current version of mail gem: https://github.com/mikel/mail/issues/1538 - # config.action_mailer.delivery_method = :sendmail - # config.action_mailer.sendmail_settings = { - # location: '/usr/sbin/sendmail', arguments: ['-i'] - # } - config.action_mailer.default_options = { - from: mailer_credentials[:from], - reply_to: mailer_credentials[:from] + from: 'no-reply@unep-wcmc.org', + reply_to: 'no-reply@unep-wcmc.org' } end diff --git a/config/environments/test.rb b/config/environments/test.rb index 0ecd0a654..3d9eb940e 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -1,5 +1,3 @@ -require 'active_support/core_ext/integer/time' - # The test environment is used exclusively to run your application's # test suite. You never need to work with it otherwise. Remember that # your test database is "scratch space" for the test suite and is wiped @@ -29,6 +27,9 @@ config.action_controller.perform_caching = false config.cache_store = :null_store + # https://guides.rubyonrails.org/upgrading_ruby_on_rails.html#all-tests-now-respect-the-active-job-queue-adapter-config + config.active_job.queue_adapter = :test + # Render exception templates for rescuable exceptions and raise for other exceptions. config.action_dispatch.show_exceptions = :rescuable @@ -38,6 +39,8 @@ # Store uploaded files on the local file system in a temporary directory. config.active_storage.service = :test + # Disable caching for Action Mailer templates even if Action Controller + # caching is enabled. config.action_mailer.perform_caching = false # Tell Action Mailer not to deliver emails to the real world. @@ -60,7 +63,7 @@ # Annotate rendered view with file names. # config.action_view.annotate_rendered_view_with_filenames = true - # Raise error when a before_action's only/except options reference missing actions + # Raise error when a before_action's only/except options reference missing actions. config.action_controller.raise_on_missing_callback_actions = true ### diff --git a/config/initializers/content_security_policy.rb b/config/initializers/content_security_policy.rb index b3076b38f..d51d71397 100644 --- a/config/initializers/content_security_policy.rb +++ b/config/initializers/content_security_policy.rb @@ -20,6 +20,10 @@ # config.content_security_policy_nonce_generator = ->(request) { request.session.id.to_s } # config.content_security_policy_nonce_directives = %w(script-src style-src) # +# # Automatically add `nonce` to `javascript_tag`, `javascript_include_tag`, and `stylesheet_link_tag` +# # if the corresponding directives are specified in `content_security_policy_nonce_directives`. +# # config.content_security_policy_nonce_auto = true +# # # Report violations without enforcing the policy. # # config.content_security_policy_report_only = true # end diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb index 66d3f7fe7..afd5a0394 100644 --- a/config/initializers/devise.rb +++ b/config/initializers/devise.rb @@ -4,7 +4,7 @@ # The secret key used by Devise. Devise uses this key to generate # random tokens. Changing this key will render invalid all existing # confirmation, reset password and unlock tokens in the database. - config.secret_key = Rails.application.credentials.secret_key_base! + config.secret_key = Rails.application.credentials.secret_key_base # ==> Mailer Configuration # Configure the e-mail address which will be shown in Devise::Mailer, diff --git a/config/initializers/filter_parameter_logging.rb b/config/initializers/filter_parameter_logging.rb index c2d89e28a..c0b717f7e 100644 --- a/config/initializers/filter_parameter_logging.rb +++ b/config/initializers/filter_parameter_logging.rb @@ -4,5 +4,5 @@ # Use this to limit dissemination of sensitive information. # See the ActiveSupport::ParameterFilter documentation for supported notations and behaviors. Rails.application.config.filter_parameters += [ - :passw, :secret, :token, :_key, :crypt, :salt, :certificate, :otp, :ssn + :passw, :email, :secret, :token, :_key, :crypt, :salt, :certificate, :otp, :ssn, :cvv, :cvc ] diff --git a/config/initializers/schema_dump_options.rb b/config/initializers/schema_dump_options.rb index 37c23a120..4a5323ef6 100644 --- a/config/initializers/schema_dump_options.rb +++ b/config/initializers/schema_dump_options.rb @@ -1,2 +1,4 @@ -ActiveRecord::SchemaDumper.ignore_tables << - ActiveRecord::Base.connection.data_sources.grep(/^trade_sandbox_\d+/) +if ENV['SECRET_KEY_BASE_DUMMY'].blank? + ActiveRecord::SchemaDumper.ignore_tables << + ActiveRecord::Base.connection.data_sources.grep(/^trade_sandbox_\d+/) +end diff --git a/config/initializers/sidekiq.rb b/config/initializers/sidekiq.rb index 9fce3c14b..7add07486 100644 --- a/config/initializers/sidekiq.rb +++ b/config/initializers/sidekiq.rb @@ -17,13 +17,15 @@ Sidekiq.configure_server do |config| config.server_middleware do |chain| - chain.add Sidekiq::Status::ServerMiddleware, expiration: 30.minutes + chain.add Sidekiq::Status::ServerMiddleware, expiration: 30.minutes.to_i chain.add SidekiqUniqueJobs::Middleware::Server end + config.client_middleware do |chain| chain.add Sidekiq::Status::ClientMiddleware unless Rails.env.test? chain.add SidekiqUniqueJobs::Middleware::Client end + config.redis = { url: ENV.fetch( 'SAPI_SIDEKIQ_REDIS_URL', diff --git a/config/puma.rb b/config/puma.rb index 58e1c205b..5021f2e38 100644 --- a/config/puma.rb +++ b/config/puma.rb @@ -1,35 +1,42 @@ # This configuration file will be evaluated by Puma. The top-level methods that # are invoked here are part of Puma's configuration DSL. For more information # about methods provided by the DSL, see https://puma.io/puma/Puma/DSL.html. - -# Puma can serve each request in a thread from an internal thread pool. -# The `threads` method setting takes two numbers: a minimum and maximum. -# Any libraries that use thread pools should be configured to match -# the maximum value specified for Puma. Default is set to 5 threads for minimum -# and maximum; this matches the default thread size of Active Record. -max_threads_count = ENV.fetch('RAILS_MAX_THREADS') { 5 } -min_threads_count = ENV.fetch('RAILS_MIN_THREADS') { max_threads_count } -threads min_threads_count, max_threads_count - -# Specifies that the worker count should equal the number of processors in production. -if ENV['RAILS_ENV'] == 'production' - require 'concurrent-ruby' - worker_count = Integer(ENV.fetch('WEB_CONCURRENCY') { Concurrent.physical_processor_count }) - workers worker_count if worker_count > 1 -end - -# Specifies the `worker_timeout` threshold that Puma will use to wait before -# terminating a worker in development environments. -worker_timeout 3600 if ENV.fetch('RAILS_ENV', 'development') == 'development' +# +# Puma starts a configurable number of processes (workers) and each process +# serves each request in a thread from an internal thread pool. +# +# You can control the number of workers using ENV["WEB_CONCURRENCY"]. You +# should only set this value when you want to run 2 or more workers. The +# default is already 1. You can set it to `auto` to automatically start a worker +# for each available processor. +# +# The ideal number of threads per worker depends both on how much time the +# application spends waiting for IO operations and on how much you wish to +# prioritize throughput over latency. +# +# As a rule of thumb, increasing the number of threads will increase how much +# traffic a given process can handle (throughput), but due to CRuby's +# Global VM Lock (GVL) it has diminishing returns and will degrade the +# response time (latency) of the application. +# +# The default is set to 3 threads as it's deemed a decent compromise between +# throughput and latency for the average Rails application. +# +# Any libraries that use a connection pool or another resource pool should +# be configured to provide at least as many connections as the number of +# threads. This includes Active Record's `pool` parameter in `database.yml`. +threads_count = ENV.fetch('RAILS_MAX_THREADS', 3) +threads threads_count, threads_count # Specifies the `port` that Puma will listen on to receive requests; default is 3000. -port ENV.fetch('PORT') { 3000 } +port ENV.fetch('PORT', 3000) -# Specifies the `environment` that Puma will run in. -environment ENV.fetch('RAILS_ENV') { 'development' } +# Allow puma to be restarted by `bin/rails restart` command. +plugin :tmp_restart -# Specifies the `pidfile` that Puma will use. -pidfile ENV.fetch('PIDFILE') { 'tmp/pids/server.pid' } +# Run the Solid Queue supervisor inside of Puma for single-server deployments. +plugin :solid_queue if ENV['SOLID_QUEUE_IN_PUMA'] -# Allow puma to be restarted by `rails restart` command. -plugin :tmp_restart +# Specify the PID file. Defaults to tmp/pids/server.pid in development. +# In other environments, only set the PID file if requested. +pidfile ENV['PIDFILE'] if ENV['PIDFILE'] diff --git a/config/routes.rb b/config/routes.rb index ce44a452b..481f14a42 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,6 +1,22 @@ require 'sidekiq/web' require 'sidekiq/cron/web' +if %w[test development].exclude?(Rails.env) + Sidekiq::Web.use Rack::Auth::Basic do |username, password| + # Protect against timing attacks: + # - See https://codahale.com/a-lesson-in-timing-attacks/ + # - See https://thisdata.com/blog/timing-attacks-against-string-comparison/ + # - Use & (do not use &&) so that it doesn't short circuit. + # - Use digests to stop length information leaking (see also ActiveSupport::SecurityUtils.variable_size_secure_compare) + sidekiq_username = Rails.application.credentials.sidekiq.username! + sidekiq_password = Rails.application.credentials.sidekiq.password! + ActiveSupport::SecurityUtils.secure_compare(Digest::SHA256.hexdigest(username), + Digest::SHA256.hexdigest(sidekiq_username)) & + ActiveSupport::SecurityUtils.secure_compare(Digest::SHA256.hexdigest(password), + Digest::SHA256.hexdigest(sidekiq_password)) + end +end + Rails.application.routes.draw do # Reveal health status on /up that returns 200 if the app boots with no exceptions, otherwise 500. # Can be used by load balancers and uptime monitors to verify that the app is live. diff --git a/config/storage.yml b/config/storage.yml index 90da9d2bd..34b151869 100644 --- a/config/storage.yml +++ b/config/storage.yml @@ -14,9 +14,9 @@ local: # secret_access_key: xXXxxXXXx0Xx/XXxx000xxXX0XxX/xxx00X0xx0X amazon: &amazon service: S3 - access_key_id: <%= ENV.fetch("AWS_S3_ACCESS_KEY_ID") { Rails.application.credentials.dig(:storage, :aws, :access_key_id) } %> - secret_access_key: <%= ENV.fetch("AWS_S3_SECRET_ACCESS_KEY") { Rails.application.credentials.dig(:storage, :aws, :secret_access_key) } %> - region: <%= ENV.fetch("AWS_S3_REGION") { Rails.application.credentials.dig(:storage, :aws, :region) || 'eu-west-2' } %> + access_key_id: <%= ENV.fetch("AWS_ACCESS_KEY_ID") { Rails.application.credentials.dig(:storage, :aws, :access_key_id) } %> + secret_access_key: <%= ENV.fetch("AWS_SECRET_ACCESS_KEY") { Rails.application.credentials.dig(:storage, :aws, :secret_access_key) } %> + region: <%= ENV.fetch("AWS_REGION") { Rails.application.credentials.dig(:storage, :aws, :region) || 'eu-west-2' } %> bucket: <%= ENV.fetch("AWS_S3_BUCKET") { Rails.application.credentials.dig(:storage, :aws, :bucket) || "species-plus-#{Rails.env}" } %> local_s3: diff --git a/deploy/.ruby-version b/deploy/.ruby-version new file mode 100644 index 000000000..7bcbb3808 --- /dev/null +++ b/deploy/.ruby-version @@ -0,0 +1 @@ +3.4.9 diff --git a/deploy/Gemfile b/deploy/Gemfile new file mode 100644 index 000000000..091be5259 --- /dev/null +++ b/deploy/Gemfile @@ -0,0 +1,4 @@ +source 'https://rubygems.org' +ruby '3.4.9' + +gem "kamal", "~> 2.10" diff --git a/deploy/Gemfile.lock b/deploy/Gemfile.lock new file mode 100644 index 000000000..e53430700 --- /dev/null +++ b/deploy/Gemfile.lock @@ -0,0 +1,75 @@ +GEM + remote: https://rubygems.org/ + specs: + activesupport (8.1.2) + base64 + bigdecimal + concurrent-ruby (~> 1.0, >= 1.3.1) + connection_pool (>= 2.2.5) + drb + i18n (>= 1.6, < 2) + json + logger (>= 1.4.2) + minitest (>= 5.1) + securerandom (>= 0.3) + tzinfo (~> 2.0, >= 2.0.5) + uri (>= 0.13.1) + base64 (0.3.0) + bcrypt_pbkdf (1.1.2) + bigdecimal (4.0.1) + concurrent-ruby (1.3.6) + connection_pool (3.0.2) + dotenv (3.2.0) + drb (2.2.3) + ed25519 (1.4.0) + i18n (1.14.8) + concurrent-ruby (~> 1.0) + json (2.19.1) + kamal (2.10.1) + activesupport (>= 7.0) + base64 (~> 0.2) + bcrypt_pbkdf (~> 1.0) + concurrent-ruby (~> 1.2) + dotenv (~> 3.1) + ed25519 (~> 1.4) + net-ssh (~> 7.3) + sshkit (>= 1.23.0, < 2.0) + thor (~> 1.3) + zeitwerk (>= 2.6.18, < 3.0) + logger (1.7.0) + minitest (6.0.2) + drb (~> 2.0) + prism (~> 1.5) + net-scp (4.1.0) + net-ssh (>= 2.6.5, < 8.0.0) + net-sftp (4.0.0) + net-ssh (>= 5.0.0, < 8.0.0) + net-ssh (7.3.1) + ostruct (0.6.3) + prism (1.9.0) + securerandom (0.4.1) + sshkit (1.25.0) + base64 + logger + net-scp (>= 1.1.2) + net-sftp (>= 2.1.2) + net-ssh (>= 2.8.0) + ostruct + thor (1.5.0) + tzinfo (2.0.6) + concurrent-ruby (~> 1.0) + uri (1.1.1) + zeitwerk (2.7.5) + +PLATFORMS + arm64-darwin-24 + ruby + +DEPENDENCIES + kamal (~> 2.10) + +RUBY VERSION + ruby 3.4.9p82 + +BUNDLED WITH + 2.6.9 diff --git a/docker-compose.yml b/docker-compose.yml index 3e64fcb05..01dbfa024 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -45,8 +45,8 @@ services: MINIO_ROOT_USER: minioadmin MINIO_ROOT_PASSWORD: minioadmin - db: - container_name: sapi-db + db-pg10: + container_name: sapi-db-pg10 image: postgres:10 command: postgres -c max_wal_size=2GB healthcheck: @@ -66,43 +66,65 @@ services: POSTGRES_HOST_AUTH_METHOD: "trust" POSTGRES_DB: "sapi_development" + db-pg17: + container_name: sapi-db-pg17 + image: postgres:17 + command: postgres -c max_wal_size=2GB + healthcheck: + test: ["CMD", "pg_isready", "-U", "postgres"] + timeout: 45s + interval: 10s + retries: 10 + volumes: + - ./db_init:/docker-entrypoint-initdb.d + - ./db/structure.sql:/docker-entrypoint-initdb.d/sapi_schema.sql + - 'pg17data:/var/lib/postgresql/data' + ports: + - "${SAPI_CONTAINER_DB_PORT:-5417}:5432" + networks: + - sapi + environment: + POSTGRES_HOST_AUTH_METHOD: "trust" + POSTGRES_DB: "sapi_development" + rails: container_name: sapi-rails build: context: ./ dockerfile: Dockerfile - command: bundle exec rails server -p 3000 -b '0.0.0.0' + target: exec-develop + command: /bin/bash -l -c "bundle install && rm -rf /SAPI/tmp/pids/server.pid && bundle exec rails server -p 3000 -b '0.0.0.0'" volumes: &rails_volumes # Used for both rails and sidekiq # The following paths are commonly written by the application at runtime - - 'app_tmp:/SAPI/tmp' - - 'app_log:/SAPI/log' - - 'app_storage:/SAPI/storage' - - 'app_public_uploads:/SAPI/public/uploads' - - 'app_public_downloads:/SAPI/public/downloads' - - 'app_private_elibrary:/SAPI/private/elibrary' - - 'app_spec_public:/SAPI/spec/public/' + # - 'app_tmp:/rails/tmp' + # - 'app_log:/rails/log' + # - 'app_storage:/rails/storage' + # - 'app_public_uploads:/rails/public/uploads' + # - 'app_public_downloads:/rails/public/downloads' + # - 'app_private_elibrary:/rails/private/elibrary' + # - 'app_spec_public:/rails/spec/public/' # The following paths are writable during build/development - - './Gemfile.lock:/SAPI/Gemfile.lock' + - './Gemfile.lock:/rails/Gemfile.lock' # Most rest of the application should not be writable, except to run # specific tasks like db migrations - - './app:/SAPI/app:${SAPI_CONTAINER_FS_MODE:-ro}' - - './bin:/SAPI/bin:${SAPI_CONTAINER_FS_MODE:-ro}' - - './config:/SAPI/config:${SAPI_CONTAINER_FS_MODE:-ro}' - - './db:/SAPI/db:${SAPI_CONTAINER_FS_MODE:-ro}' - - './db_init:/SAPI/db_init:${SAPI_CONTAINER_FS_MODE:-ro}' - - './doc:/SAPI/doc:${SAPI_CONTAINER_FS_MODE:-ro}' - - './lib:/SAPI/lib:${SAPI_CONTAINER_FS_MODE:-ro}' - - './script:/SAPI/script:${SAPI_CONTAINER_FS_MODE:-ro}' - - './spec:/SAPI/spec:${SAPI_CONTAINER_FS_MODE:-ro}' - - './vendor:/SAPI/vendor:${SAPI_CONTAINER_FS_MODE:-ro}' + - './app:/rails/app:${SAPI_CONTAINER_FS_MODE:-ro}' + - './bin:/rails/bin:${SAPI_CONTAINER_FS_MODE:-ro}' + - './config:/rails/config:${SAPI_CONTAINER_FS_MODE:-ro}' + - './db:/rails/db:${SAPI_CONTAINER_FS_MODE:-ro}' + - './db_init:/rails/db_init:${SAPI_CONTAINER_FS_MODE:-ro}' + - './doc:/rails/doc:${SAPI_CONTAINER_FS_MODE:-ro}' + - './lib:/rails/lib:${SAPI_CONTAINER_FS_MODE:-ro}' + - './script:/rails/script:${SAPI_CONTAINER_FS_MODE:-ro}' + - './spec:/rails/spec:${SAPI_CONTAINER_FS_MODE:-ro}' + - './vendor:/rails/vendor:${SAPI_CONTAINER_FS_MODE:-ro}' # During deploy public/assets is removed and re-added - - './public:/SAPI/public' - - '.:/SAPI' + - './public:/rails/public' + - '.:/rails' # Keep the cache of bundler gems for sharing across install commands - bundler_gems:/usr/local/bundle @@ -115,45 +137,46 @@ services: environment: ALLOWED_HOSTS: sapi-rails MAILER_ADDRESS: sapi-mailcatcher - SAPI_DATABASE_HOST: sapi-db + SAPI_DATABASE_HOST: sapi-db-pg17 SAPI_DATABASE_USERNAME: postgres SAPI_DATABASE_PORT: 5432 SAPI_SIDEKIQ_REDIS_URL: redis://sapi-redis:6379/0 SAPI_SIDEKIQ_REDIS_CACHE_URL: redis://sapi-redis-cache:6380/0 - CAPTIVE_BREEDING_DATABASE_HOST: sapi-db + CAPTIVE_BREEDING_DATABASE_HOST: sapi-db-pg17 SAPI_S3_PORT: '${SAPI_CONTAINER_S3_PORT:-9000}' depends_on: - redis - redis_cache - - db + - db-pg10 + - db-pg17 - mailcatcher - minio # Sometimes the db is not ready when we attempt to start restart: on-failure:10 - deploy: - container_name: sapi-cap-deploy - build: - context: ./ - dockerfile: Dockerfile.cap-deploy - volumes: *rails_volumes - networks: - - sapi - stdin_open: true - tty: true - environment: - SAPI_DATABASE_HOST: sapi-db - SAPI_DATABASE_USERNAME: postgres - SAPI_DATABASE_NAME: sapi_development - SAPI_DATABASE_PORT: 5432 - SAPI_SIDEKIQ_REDIS_URL: redis://sapi-redis:6379/0 - SAPI_SIDEKIQ_REDIS_CACHE_URL: redis://sapi-redis-cache:6380/0 - # Defaults to blank; used by AppSignal: - USER: "$USER" - USERNAME: "$USERNAME" - secrets: - - host_ssh_key - - host_ssh_config + # deploy: + # container_name: sapi-cap-deploy + # build: + # context: ./ + # dockerfile: Dockerfile.cap-deploy + # volumes: *rails_volumes + # networks: + # - sapi + # stdin_open: true + # tty: true + # environment: + # SAPI_DATABASE_HOST: sapi-db-pg17 + # SAPI_DATABASE_USERNAME: postgres + # SAPI_DATABASE_NAME: sapi_development + # SAPI_DATABASE_PORT: 5432 + # SAPI_SIDEKIQ_REDIS_URL: redis://sapi-redis:6379/0 + # SAPI_SIDEKIQ_REDIS_CACHE_URL: redis://sapi-redis-cache:6380/0 + # # Defaults to blank; used by AppSignal: + # USER: "$USER" + # USERNAME: "$USERNAME" + # secrets: + # - host_ssh_key + # - host_ssh_config redis: container_name: sapi-redis @@ -169,6 +192,8 @@ services: redis_cache: container_name: sapi-redis-cache image: redis:7.2.0 + ports: + - "${SAPI_CONTAINER_REDIS_PORT:-6380}:6380" networks: - sapi command: redis-server /usr/local/etc/redis/redis.conf @@ -178,13 +203,12 @@ services: sidekiq: container_name: sapi-sidekiq - build: - context: . - dockerfile: Dockerfile + build: . networks: - sapi depends_on: - - db + - db-pg10 + - db-pg17 - redis - redis_cache - mailcatcher @@ -193,13 +217,13 @@ services: volumes: *rails_volumes environment: MAILER_ADDRESS: sapi-mailcatcher - SAPI_DATABASE_HOST: sapi-db + SAPI_DATABASE_HOST: sapi-db-pg17 SAPI_DATABASE_USERNAME: postgres SAPI_DATABASE_PORT: 5432 - SAPI_SIDEKIQ_REDIS_URL: redis://sapi-redis:6379/0 SAPI_S3_PORT: '${SAPI_CONTAINER_S3_PORT:-9000}' + SAPI_SIDEKIQ_REDIS_URL: redis://sapi-redis:6379/0 SAPI_SIDEKIQ_REDIS_CACHE_URL: redis://sapi-redis-cache:6380/0 - CAPTIVE_BREEDING_DATABASE_HOST: sapi-db + CAPTIVE_BREEDING_DATABASE_HOST: sapi-db-pg17 # Sometimes the db is not ready when we attempt to start restart: on-failure:10 @@ -213,6 +237,7 @@ services: volumes: pgdata: + pg17data: bundler_gems: redis_data: minio-data: diff --git a/lib/modules/search_param_sanitiser.rb b/lib/modules/search_param_sanitiser.rb index eb24c7a69..58f3ad6f1 100644 --- a/lib/modules/search_param_sanitiser.rb +++ b/lib/modules/search_param_sanitiser.rb @@ -5,7 +5,7 @@ def sanitise_string(s) end def sanitise_upcase_string(s) - s && s.strip.mb_chars.upcase + s && s.strip.upcase end def sanitise_symbol(s, default = nil) diff --git a/lib/modules/trade/grouping/base.rb b/lib/modules/trade/grouping/base.rb index 13751bc46..1107286c6 100644 --- a/lib/modules/trade/grouping/base.rb +++ b/lib/modules/trade/grouping/base.rb @@ -3,7 +3,7 @@ class Trade::Grouping::Base TAXONOMIC_GROUPING = 'lib/data/group_conversions.csv'.freeze - YEARS = (2012..Date.today.year - 1).to_a + YEARS = (2012..(Date.today.year - 1)).to_a # Example usage # Group by year considering compliance types: diff --git a/public/400.html b/public/400.html new file mode 100644 index 000000000..640de0339 --- /dev/null +++ b/public/400.html @@ -0,0 +1,135 @@ + + + + + + + The server cannot process the request due to a client error (400 Bad Request) + + + + + + + + + + + + + +
+
+ +
+
+

The server cannot process the request due to a client error. Please check the request and try again. If you're the application owner check the logs for more information.

+
+
+ + + + diff --git a/public/404.html b/public/404.html index f028a6e83..d7f0f1422 100644 --- a/public/404.html +++ b/public/404.html @@ -1,69 +1,135 @@ - - - - - The page you were looking for doesn't exist (404) - - - - - - -
-
-

The page you were looking for doesn't exist.

-

You may have mistyped the address or the page may have moved.

-
-

If you are the application owner check the logs for more information.

-
- + + + + + + + The page you were looking for doesn't exist (404 Not found) + + + + + + + + + + + + + +
+
+ +
+
+

The page you were looking for doesn't exist. You may have mistyped the address or the page may have moved. If you're the application owner check the logs for more information.

+
+
+ + diff --git a/public/406-unsupported-browser.html b/public/406-unsupported-browser.html new file mode 100644 index 000000000..43d2811e8 --- /dev/null +++ b/public/406-unsupported-browser.html @@ -0,0 +1,135 @@ + + + + + + + Your browser is not supported (406 Not Acceptable) + + + + + + + + + + + + + +
+
+ +
+
+

Your browser is not supported.
Please upgrade your browser to continue.

+
+
+ + + + diff --git a/public/422.html b/public/422.html index 33dda348e..f12fb4aa1 100644 --- a/public/422.html +++ b/public/422.html @@ -1,69 +1,135 @@ - - - - - The change you wanted was rejected (422) - - - - - - -
-
-

The change you wanted was rejected.

-

Maybe you tried to change something you didn't have access to.

-
-

If you are the application owner check the logs for more information.

-
- + + + + + + + The change you wanted was rejected (422 Unprocessable Entity) + + + + + + + + + + + + + +
+
+ +
+
+

The change you wanted was rejected. Maybe you tried to change something you didn't have access to. If you're the application owner check the logs for more information.

+
+
+ + diff --git a/public/500.html b/public/500.html index 90beade75..e4eb18a75 100644 --- a/public/500.html +++ b/public/500.html @@ -1,68 +1,135 @@ - - - - - We're sorry, but something went wrong (500) - - - - - - -
-
-

We're sorry, but something went wrong.

-
-

If you are the application owner check the logs for more information.

-
- + + + + + + + We're sorry, but something went wrong (500 Internal Server Error) + + + + + + + + + + + + + +
+
+ +
+
+

We're sorry, but something went wrong.
If you're the application owner check the logs for more information.

+
+
+ + diff --git a/public/latex/history.tex b/public/latex/history.tex index 1303156ba..b4bab6cbe 100644 --- a/public/latex/history.tex +++ b/public/latex/history.tex @@ -8,7 +8,7 @@ \usepackage{attachfile} \usepackage[T1]{fontenc} \usepackage[utf8]{inputenc} -\usepackage{lmodern} +\usepackage{mlmodern} \usepackage{longtable} \usepackage{hyperref} \usepackage{fancyhdr} diff --git a/spec/controllers/admin/ahoy_events_controller_spec.rb b/spec/controllers/admin/ahoy_events_controller_spec.rb index 6da244541..0f1499b79 100644 --- a/spec/controllers/admin/ahoy_events_controller_spec.rb +++ b/spec/controllers/admin/ahoy_events_controller_spec.rb @@ -5,16 +5,18 @@ login_admin describe 'index' do - let!(:ahoy_event1) do - create(:ahoy_event, name: 'Search') + let!(:ahoy_event_older) do + create(:ahoy_event, name: 'Search', time: 3.days.ago) end - let!(:ahoy_event2) do - create(:ahoy_event, name: 'Taxon Concept') + + let!(:ahoy_event_newer) do + create(:ahoy_event, name: 'Taxon Concept', time: 2.days.ago) end describe 'GET index' do it 'assigns to @ahoy_events sorted by time DESC' do - events = [ ahoy_event1, ahoy_event2 ] + events = [ ahoy_event_newer, ahoy_event_older ] + get :index expect(assigns(:ahoy_events)).to eq(events) end diff --git a/spec/controllers/admin/change_types_controller_spec.rb b/spec/controllers/admin/change_types_controller_spec.rb index 775a0f8b4..c4910d326 100644 --- a/spec/controllers/admin/change_types_controller_spec.rb +++ b/spec/controllers/admin/change_types_controller_spec.rb @@ -10,11 +10,15 @@ change_type2_1 = create(:change_type, designation: designation2, name: 'ADD') change_type2_2 = create(:change_type, designation: designation2, name: 'DEL') change_type1 = create(:change_type, designation: designation1, name: 'ADD') + get :index + expect(assigns(:change_types)).to eq([ change_type1, change_type2_1, change_type2_2 ]) end + it 'renders the index template' do get :index + expect(response).to render_template('index') end end @@ -22,30 +26,39 @@ describe 'XHR POST create' do it 'renders create when successful' do post :create, params: { change_type: build_attributes(:change_type) }, xhr: true + expect(response).to render_template('create') end + it 'renders new when not successful' do - post :create, params: { change_type: { dummy: 'test' } }, xhr: true + post :create, params: { change_type: { designation_id: 0 } }, xhr: true + expect(response).to render_template('new') end end describe 'XHR PUT update' do let(:change_type) { create(:change_type) } + it 'responds with 200 when successful' do put :update, format: 'json', params: { id: change_type.id, change_type: { name: 'ZZ' } }, xhr: true + expect(response).to be_successful end + it 'responds with json when not successful' do put :update, format: 'json', params: { id: change_type.id, change_type: { name: nil } }, xhr: true + expect(response.parsed_body).to include('errors') end end describe 'DELETE destroy' do let(:change_type) { create(:change_type) } + it 'redirects after delete' do delete :destroy, params: { id: change_type.id } + expect(response).to redirect_to(admin_change_types_url) end end diff --git a/spec/controllers/admin/cites_cops_controller_spec.rb b/spec/controllers/admin/cites_cops_controller_spec.rb index 5c59dcd27..e98266aba 100644 --- a/spec/controllers/admin/cites_cops_controller_spec.rb +++ b/spec/controllers/admin/cites_cops_controller_spec.rb @@ -12,10 +12,13 @@ describe 'GET index' do it 'assigns @cites_cops sorted by name' do get :index + expect(assigns(:cites_cops)).to eq([ @cites_cop2, @cites_cop1 ]) end + it 'renders the index template' do get :index + expect(response).to render_template('index') end end diff --git a/spec/controllers/admin/cites_suspensions_controller_spec.rb b/spec/controllers/admin/cites_suspensions_controller_spec.rb index 7cf7cf317..d3c6da2df 100644 --- a/spec/controllers/admin/cites_suspensions_controller_spec.rb +++ b/spec/controllers/admin/cites_suspensions_controller_spec.rb @@ -46,7 +46,8 @@ end end it 'renders new when not successful' do - post :create, params: { cites_suspension: { dummy: 'test' } } + post :create, params: { cites_suspension: { notes: 'DUMMY' } } + expect(response).to render_template('new') end end diff --git a/spec/controllers/admin/designations_controller_spec.rb b/spec/controllers/admin/designations_controller_spec.rb index 4322d8584..16db14429 100644 --- a/spec/controllers/admin/designations_controller_spec.rb +++ b/spec/controllers/admin/designations_controller_spec.rb @@ -8,19 +8,23 @@ @designation1 = create(:designation, name: 'BB', taxonomy: create(:taxonomy)) @designation2 = create(:designation, name: 'AA', taxonomy: create(:taxonomy)) end + describe 'GET index' do it 'assigns @designations sorted by name' do get :index expect(assigns(:designations)).to eq([ @designation2, @designation1 ]) end + it 'renders the index template' do get :index expect(response).to render_template('index') end end + describe 'XHR GET index JSON' do it 'renders json for dropdown' do get :index, format: 'json', xhr: true + expect(response.body).to have_json_size(2) expect(parse_json(response.body, '0/text')).to eq('AA') end @@ -30,30 +34,39 @@ describe 'XHR POST create' do it 'renders create when successful' do post :create, params: { designation: build_attributes(:designation) }, xhr: true + expect(response).to render_template('create') end + it 'renders new when not successful' do - post :create, params: { designation: { dummy: 'test' } }, xhr: true + post :create, params: { designation: { name: nil } }, xhr: true + expect(response).to render_template('new') end end describe 'XHR PUT update' do let(:designation) { create(:designation) } + it 'responds with 200 when successful' do put :update, format: 'json', params: { id: designation.id, designation: { name: 'ZZ' } }, xhr: true + expect(response).to be_successful end + it 'responds with json when not successful' do put :update, format: 'json', params: { id: designation.id, designation: { name: nil } }, xhr: true + expect(response.parsed_body).to include('errors') end end describe 'DELETE destroy' do let(:designation) { create(:designation) } + it 'redirects after delete' do delete :destroy, params: { id: designation.id } + expect(response).to redirect_to(admin_designations_url) end end diff --git a/spec/controllers/admin/document_batches_controller_spec.rb b/spec/controllers/admin/document_batches_controller_spec.rb index a24db8b0e..b23517c82 100644 --- a/spec/controllers/admin/document_batches_controller_spec.rb +++ b/spec/controllers/admin/document_batches_controller_spec.rb @@ -3,20 +3,26 @@ describe Admin::DocumentBatchesController, sidekiq: :inline do login_admin let(:event) { create(:event) } - before(:each) { create(:language, iso_code1: 'EN') } + + before { create(:language, iso_code1: 'EN') } describe 'GET new' do context 'when no event' do let(:document) { create(:document) } + it 'renders the new template' do get :new + expect(response).to render_template('new') end end + context 'when event' do let(:document) { create(:document, event_id: event.id) } + it 'renders the new template' do get :new + expect(response).to render_template('new') end end @@ -24,10 +30,17 @@ describe 'POST create' do let(:document_attrs) do - { 'type' => 'Document::Proposal' } + { type: 'Document::Proposal' } end + let(:files) do - [ Rack::Test::UploadedFile.new(Rails.root.join('spec/support/annual_report_upload_exporter.csv').to_s) ] + [ + Rack::Test::UploadedFile.new( + Rails.root.join( + 'spec/support/annual_report_upload_exporter.csv' + ).to_s + ) + ] end context 'when no event' do @@ -35,39 +48,53 @@ it 'creates a new Document' do expect do - post :create, params: { - document_batch: { - date: Date.today, documents_attributes: { '0' => document_attrs }, files: files + post :create, + params: { + document_batch: { + date: Date.today, + documents_attributes: [ document_attrs ], + files: files + } } - } end.to change(Document, :count).by(1) end it 'redirects to index when successful' do - post :create, params: { - document_batch: { - date: Date.today, documents_attributes: { '0' => document_attrs }, files: files + post :create, + params: { + document_batch: { + date: Date.today, + documents_attributes: [ document_attrs ], + files: files + } } - } + expect(response).to redirect_to(admin_documents_url) end - it 'does not create a new Document' do + it 'does not create a new Document without a date' do expect do - post :create, params: { - document_batch: { - date: nil, documents_attributes: { '0' => document_attrs }, files: files + post :create, + params: { + document_batch: { + date: nil, + documents_attributes: [ document_attrs ], + files: files + } } - } - end.to change(Document, :count).by(0) + end.not_to change(Document, :count) end it 'renders new when not successful' do - post :create, params: { - document_batch: { - date: nil, documents_attributes: { '0' => document_attrs }, files: files + post :create, + params: { + document_batch: { + date: nil, + documents_attributes: [ document_attrs ], + files: files + } } - } + expect(response).to render_template('new') end end @@ -76,20 +103,28 @@ let(:document) { create(:document, event_id: event.id) } it 'redirects to index when successful' do - post :create, params: { - event_id: event.id, document_batch: { - date: Date.today, documents_attributes: { '0' => document_attrs }, files: files + post :create, + params: { + event_id: event.id, document_batch: { + date: Date.today, + documents_attributes: [ document_attrs ], + files: files + } } - } + expect(response).to redirect_to(admin_event_documents_url(event)) end it 'renders new when not successful' do - post :create, params: { - event_id: event.id, document_batch: { - date: nil, documents_attributes: { '0' => document_attrs }, files: files + post :create, + params: { + event_id: event.id, document_batch: { + date: nil, + documents_attributes: [ document_attrs ], + files: files + } } - } + expect(response).to render_template('new') end end diff --git a/spec/controllers/admin/documents_controller_spec.rb b/spec/controllers/admin/documents_controller_spec.rb index 454369c35..ed8676c02 100644 --- a/spec/controllers/admin/documents_controller_spec.rb +++ b/spec/controllers/admin/documents_controller_spec.rb @@ -11,9 +11,10 @@ let(:process_stage) { create(:process_stage) } describe 'index' do - before(:each) do + before do @document1 = create(:document, title: 'BB hello world', event: event) @document2 = create(:document, title: 'AA goodbye world', event: event) + @public_document = create(:document, title: 'DD public document', event: event, is_public: true) create(:document_citation, document_id: @document1.id, taxon_concepts: [ taxon_concept ]) create(:document_citation, document_id: @document2.id, geo_entities: [ geo_entity ]) @@ -22,8 +23,10 @@ describe 'GET index' do login_admin - before(:each) do + + before do @document3 = create(:document, title: 'CC no event!', date: DateTime.new(2014, 01, 01)) + DocumentSearch.refresh_citations_and_documents end @@ -37,59 +40,86 @@ context 'search' do it 'runs a full text search on title' do get :index, params: { 'title_query' => 'good' } + expect(assigns(:documents).map(&:id)).to eq([ @document2 ].map(&:id)) end + it 'retrieves documents inclusive of the given start date' do get :index, params: { 'document_date_start' => '25/12/2014' } + expect(assigns(:documents).map(&:id)).to eq([ @public_document, @document2, @document1 ].map(&:id)) end + it 'retrieves documents inclusive of the given end date' do get :index, params: { 'document_date_end' => '01/01/2014' } + expect(assigns(:documents).map(&:id)).to eq([ @document3 ].map(&:id)) end + it 'retrieves documents after the given date' do get :index, params: { 'document_date_start' => '10/01/2014' } + expect(assigns(:documents).map(&:id)).to eq([ @public_document, @document2, @document1 ].map(&:id)) end + it 'retrieves documents before the given date' do get :index, params: { 'document_date_end' => '10/01/2014' } + expect(assigns(:documents).map(&:id)).to eq([ @document3 ].map(&:id)) end + it 'ignores invalid dates' do get :index, params: { 'document_date_start' => '34/24/12', 'document_date_end' => '34/24/12' } + expect(assigns(:documents).map(&:id)).to eq([ @document3, @public_document, @document2, @document1 ].map(&:id)) end + it 'retrieves documents for taxon concept' do get :index, params: { 'taxon_concepts_ids' => taxon_concept.id } + expect(assigns(:documents).map(&:id)).to eq([ @public_document, @document1 ].map(&:id)) end + it 'retrieves documents for geo entity' do get :index, params: { 'geo_entities_ids' => [ geo_entity.id ] } + expect(assigns(:documents).map(&:id)).to eq([ @document2 ].map(&:id)) end + context 'by proposal outcome' do - before(:each) do + before do @document3 = create(:proposal, event: create_cites_cop(published_at: DateTime.new(2014, 01, 01))) + create(:proposal_details, document_id: @document3.id, proposal_outcome_id: proposal_outcome.id) + DocumentSearch.refresh_citations_and_documents end + it 'retrieves documents for tag' do get :index, params: { 'document_tags_ids' => [ proposal_outcome.id ] } + expect(assigns(:documents).map(&:id)).to eq([ @document3 ].map(&:id)) end end + context 'by document tags' do - before(:each) do + before do @document3 = create(:review_of_significant_trade, event: create_ec_srg(published_at: DateTime.new(2014, 01, 01))) + create(:review_details, document_id: @document3.id, review_phase_id: review_phase.id, process_stage_id: process_stage.id) + DocumentSearch.refresh_citations_and_documents end + it 'retrieves documents for review_phase tag' do get :index, params: { 'document_tags_ids' => [ review_phase.id ] } + expect(assigns(:documents).map(&:id)).to eq([ @document3 ].map(&:id)) end + it 'retrieves documents for process_stage tag' do get :index, params: { 'document_tags_ids' => [ process_stage.id ] } + expect(assigns(:documents).map(&:id)).to eq([ @document3 ].map(&:id)) end end @@ -98,6 +128,7 @@ context 'when no event' do it 'renders the index template' do get :index + expect(response).to render_template('index') end end @@ -105,17 +136,24 @@ context 'when event' do it 'renders the event/documents/index template' do get :index, params: { events_ids: [ event.id ] } + expect(response).to render_template('admin/event_documents/index') end + it 'assigns @documents for event, sorted by title' do @document3 = create(:document, title: 'CC hello world', event: event2) + DocumentSearch.refresh_citations_and_documents + get :index, params: { events_ids: [ event.id, event2.id ] } + expect(assigns(:documents).map(&:id)).to eq([ @document3, @document2, @document1, @public_document ].map(&:id)) end end + context 'when secretariat is logged in' do login_secretariat_user + it 'returns only public documents' do get :index expect(assigns(:documents).map(&:id)).to eq([ @public_document ].map(&:id)) @@ -126,6 +164,7 @@ describe 'GET edit' do login_admin + let(:document_tags) { [ create(:document_tag) ] } let(:document) { create(:document, tags: document_tags) } @@ -137,28 +176,37 @@ describe 'PUT update' do login_admin + context 'when no event' do let(:document) { create(:document) } + it 'redirects to index when successful' do put :update, params: { id: document.id, document: { date: Date.today } } + expect(response).to redirect_to(admin_documents_url) expect(flash[:notice]).not_to be_nil end + it 'renders new when not successful' do - put :update, params: { id: document.id, document: { date: nil } } + put :update, params: { id: document.id, document: { date: 'Not a valid date' } } + expect(response).to render_template('new') end end context 'when event' do let(:document) { create(:document, event_id: event.id) } + it 'redirects to index when successful' do put :update, params: { id: document.id, event_id: event.id, document: { date: Date.today } } + expect(response).to redirect_to(admin_event_documents_url(event)) expect(flash[:notice]).not_to be_nil end + it 'renders new when not successful' do - put :update, params: { id: document.id, event_id: event.id, document: { date: nil } } + put :update, params: { id: document.id, event_id: event.id, document: { date: 'Not a valid date' } } + expect(response).to render_template('new') end end @@ -170,35 +218,42 @@ let(:recommended_category) { 'A wonderful category' } it 'assign review phase to Review' do - put :update, params: { - id: document.id, document: { - date: Date.today, review_details_attributes: { review_phase_id: review_phase.id } + put :update, + params: { + id: document.id, + document: { + date: Date.today, + review_details_attributes: { review_phase_id: review_phase.id } + } } - } - expect(response).to redirect_to(admin_documents_url) + expect(response).to redirect_to(admin_documents_url) expect(document.reload.review_details.review_phase_id).to eq(review_phase.id) end it 'assign process stage to Review' do - put :update, params: { - id: document.id, document: { - date: Date.today, review_details_attributes: { process_stage_id: process_stage.id } + put :update, + params: { + id: document.id, + document: { + date: Date.today, + review_details_attributes: { process_stage_id: process_stage.id } + } } - } - expect(response).to redirect_to(admin_documents_url) + expect(response).to redirect_to(admin_documents_url) expect(document.reload.review_details.process_stage_id).to eq(process_stage.id) end it 'assign recommended category to Review' do put :update, params: { id: document.id, document: { - date: Date.today, review_details_attributes: { recommended_category: recommended_category } + date: Date.today, + review_details_attributes: { recommended_category: recommended_category } } } - expect(response).to redirect_to(admin_documents_url) + expect(response).to redirect_to(admin_documents_url) expect(document.reload.review_details.recommended_category).to eq(recommended_category) end end @@ -208,13 +263,15 @@ let(:proposal_outcome) { create(:document_tag, type: 'DocumentTag::ProposalOutcome') } it 'assign outcome to Proposal' do - put :update, params: { - id: document.id, document: { - date: Date.today, proposal_details_attributes: { proposal_outcome_id: proposal_outcome.id } + put :update, + params: { + id: document.id, document: { + date: Date.today, + proposal_details_attributes: { proposal_outcome_id: proposal_outcome.id } + } } - } - expect(response).to redirect_to(admin_documents_url) + expect(response).to redirect_to(admin_documents_url) expect(document.reload.proposal_details.proposal_outcome_id).to eq(proposal_outcome.id) end end @@ -224,17 +281,18 @@ let(:proposal_outcome) { create(:document_tag, type: 'DocumentTag::ProposalOutcome') } it 'assigns a taxon' do - put :update, params: { - id: document.id, - document: { - date: Date.today, - citations_attributes: [ - { - stringy_taxon_concept_ids: taxon_concept.id - } - ] + put :update, + params: { + id: document.id, + document: { + date: Date.today, + citations_attributes: [ + { + stringy_taxon_concept_ids: taxon_concept.id + } + ] + } } - } expect(document.reload.citations.length).to eq(1) expect(document.reload.citations[0].taxon_concepts.length).to eq(1) @@ -242,17 +300,18 @@ end it 'assigns multiple taxa' do - put :update, params: { - id: document.id, - document: { - date: Date.today, - citations_attributes: [ - { - stringy_taxon_concept_ids: "#{taxon_concept.id},#{taxon_concept2.id}" - } - ] + put :update, + params: { + id: document.id, + document: { + date: Date.today, + citations_attributes: [ + { + stringy_taxon_concept_ids: "#{taxon_concept.id},#{taxon_concept2.id}" + } + ] + } } - } expect(document.reload.citations.length).to eq(1) expect(document.reload.citations[0].taxon_concepts.length).to eq(2) @@ -270,17 +329,18 @@ end it 'assigns a geo_entity_id' do - put :update, params: { - id: document.id, - document: { - date: Date.today, - citations_attributes: [ - { - geo_entity_ids: [ geo_entity.id ] - } - ] + put :update, + params: { + id: document.id, + document: { + date: Date.today, + citations_attributes: [ + { + geo_entity_ids: [ geo_entity.id ] + } + ] + } } - } expect(document.reload.citations.length).to eq(1) expect(document.reload.citations[0].geo_entities.length).to eq(1) @@ -298,11 +358,13 @@ geo_entity_type: country_geo_entity_type ) end + let(:document) do document = create(:document) document.citations << DocumentCitation.new(geo_entity_ids: [ poland.id ]) document end + it 'redirects after delete' do delete :destroy, params: { id: document.id } expect(response).to redirect_to(admin_documents_url) @@ -311,6 +373,7 @@ describe 'XHR GET JSON autocomplete' do login_admin + let!(:document) do create( :document, @@ -318,11 +381,13 @@ event_id: event.id ) end + let!(:document2) { create(:document, title: 'Title2') } context 'When no event specified' do it 'returns properly formatted json' do get :autocomplete, format: 'json', params: { title: 'tit' }, xhr: true + expect(response.body).to have_json_size(2) expect(parse_json(response.body, '0/title')).to eq('Title') expect(parse_json(response.body, '1/title')).to eq('Title2') @@ -332,6 +397,7 @@ context 'When an event is specified' do it 'returns properly formatted json' do get :autocomplete, format: 'json', params: { title: 'tit', event_id: event.id }, xhr: true + expect(response.body).to have_json_size(1) expect(parse_json(response.body, '0/title')).to eq('Title') end diff --git a/spec/controllers/admin/eu_opinions_controller_spec.rb b/spec/controllers/admin/eu_opinions_controller_spec.rb index 313d8e189..7799eb543 100644 --- a/spec/controllers/admin/eu_opinions_controller_spec.rb +++ b/spec/controllers/admin/eu_opinions_controller_spec.rb @@ -11,10 +11,13 @@ describe 'GET index' do it 'renders the index template' do get :index, params: { taxon_concept_id: @taxon_concept.id } + expect(response).to render_template('index') end + it 'renders the taxon_concepts_layout' do get :index, params: { taxon_concept_id: @taxon_concept.id } + expect(response).to render_template('layouts/taxon_concepts') end end @@ -22,12 +25,16 @@ describe 'GET new' do it 'renders the new template' do get :new, params: { taxon_concept_id: @taxon_concept.id } + expect(response).to render_template('new') end + it 'assigns @geo_entities (country and territory) with two objects' do create(:geo_entity, geo_entity_type_id: territory_geo_entity_type.id) create(:geo_entity) + get :new, params: { taxon_concept_id: @taxon_concept.id } + expect(assigns(:geo_entities).size).to eq(2) end end @@ -38,72 +45,100 @@ @eu_decision_type = create(:eu_decision_type) @srg_history = create(:srg_history) end + context 'when intersessional document is present' do before do @document = create(:commission_note) end + it 'redirects to the EU Opinions index' do - post :create, params: { - eu_opinion: { - eu_decision_type_id: @eu_decision_type.id, - srg_history_id: @srg_history.id, - start_date: Date.today, - document_id: @document.id, - geo_entity_id: create( - :geo_entity, geo_entity_type_id: country_geo_entity_type.id - ) - }, taxon_concept_id: @taxon_concept.id - } - expect(response).to redirect_to(admin_taxon_concept_eu_opinions_url(@taxon_concept)) + post :create, + params: { + eu_opinion: { + eu_decision_type_id: @eu_decision_type.id, + srg_history_id: @srg_history.id, + start_date: Date.today, + document_id: @document.id, + geo_entity_id: create( + :geo_entity, geo_entity_type_id: country_geo_entity_type.id + ) + }, taxon_concept_id: @taxon_concept.id + } + + expect(response).to redirect_to( + admin_taxon_concept_eu_opinions_url(@taxon_concept) + ) end end + context 'when event is present' do it 'redirects to the EU Opinions index' do - post :create, params: { - eu_opinion: { - eu_decision_type_id: @eu_decision_type.id, - srg_history_id: @srg_history.id, - start_date: Date.today, - start_event_id: @eu_regulation.id, - geo_entity_id: create( - :geo_entity, geo_entity_type_id: country_geo_entity_type.id - ) - }, taxon_concept_id: @taxon_concept.id - } - expect(response).to redirect_to(admin_taxon_concept_eu_opinions_url(@taxon_concept)) + post :create, + params: { + eu_opinion: { + eu_decision_type_id: @eu_decision_type.id, + srg_history_id: @srg_history.id, + start_date: Date.today, + start_event_id: @eu_regulation.id, + geo_entity_id: create( + :geo_entity, geo_entity_type_id: country_geo_entity_type.id + ) + }, taxon_concept_id: @taxon_concept.id + } + + expect(response).to redirect_to( + admin_taxon_concept_eu_opinions_url(@taxon_concept) + ) end end end context 'when not successful' do it 'renders new' do - post :create, params: { eu_opinion: { dummy: 'test' }, taxon_concept_id: @taxon_concept.id } + post :create, params: { + eu_opinion: { geo_entity_id: 0 }, + taxon_concept_id: @taxon_concept.id + } + expect(response).to render_template('new') end end end describe 'GET edit' do - before(:each) do + before do @eu_opinion = create( :eu_opinion, taxon_concept_id: @taxon_concept.id, start_event_id: @eu_regulation.id ) end + it 'renders the edit template' do - get :edit, params: { id: @eu_opinion.id, taxon_concept_id: @taxon_concept.id, start_event_id: @eu_regulation.id } + get :edit, params: { + id: @eu_opinion.id, + taxon_concept_id: @taxon_concept.id, + start_event_id: @eu_regulation.id + } + expect(response).to render_template('edit') end + it 'assigns @geo_entities' do territory = create(:geo_entity, geo_entity_type_id: territory_geo_entity_type.id) - get :edit, params: { id: @eu_opinion.id, taxon_concept_id: @taxon_concept.id, start_event_id: @eu_regulation.id } + + get :edit, params: { + id: @eu_opinion.id, + taxon_concept_id: @taxon_concept.id, + start_event_id: @eu_regulation.id + } + expect(assigns(:geo_entities)).to include(territory) end end describe 'PUT update' do - before(:each) do + before do @eu_opinion = create( :eu_opinion, taxon_concept_id: @taxon_concept.id, @@ -117,12 +152,14 @@ context 'when successful' do context 'when eu decision type is present' do it 'renders taxon_concepts EU Opinions page' do - put :update, params: { - eu_opinion: { - eu_decision_type_id: @eu_decision_type.id, - start_event_id: @eu_regulation.id - }, id: @eu_opinion.id, taxon_concept_id: @taxon_concept.id - } + put :update, + params: { + eu_opinion: { + eu_decision_type_id: @eu_decision_type.id, + start_event_id: @eu_regulation.id + }, id: @eu_opinion.id, taxon_concept_id: @taxon_concept.id + } + expect(response).to redirect_to( admin_taxon_concept_eu_opinions_url(@taxon_concept) ) @@ -131,13 +168,17 @@ context 'when eu decision type is not present' do it 'renders taxon_concepts EU Opinions page' do - put :update, params: { - eu_opinion: { - eu_decision_type_id: nil, - srg_history_id: @srg_history.id, - start_event_id: @eu_regulation.id - }, id: @eu_opinion.id, taxon_concept_id: @taxon_concept.id - } + put :update, + params: { + eu_opinion: { + eu_decision_type_id: nil, + srg_history_id: @srg_history.id, + start_event_id: @eu_regulation.id + }, + id: @eu_opinion.id, + taxon_concept_id: @taxon_concept.id + } + expect(response).to redirect_to( admin_taxon_concept_eu_opinions_url(@taxon_concept) ) @@ -146,12 +187,14 @@ context 'when event is present' do it 'renders taxon_concepts EU Opinions page' do - put :update, params: { - eu_opinion: { - eu_decision_type_id: @eu_decision_type.id, - start_event_id: @eu_regulation.id - }, id: @eu_opinion.id, taxon_concept_id: @taxon_concept.id - } + put :update, + params: { + eu_opinion: { + eu_decision_type_id: @eu_decision_type.id, + start_event_id: @eu_regulation.id + }, id: @eu_opinion.id, taxon_concept_id: @taxon_concept.id + } + expect(response).to redirect_to( admin_taxon_concept_eu_opinions_url(@taxon_concept) ) @@ -160,13 +203,17 @@ context 'when event is not present' do it 'renders taxon_concepts EU Opinions page' do - put :update, params: { - eu_opinion: { - eu_decision_type_id: @eu_decision_type.id, - start_event_id: nil, - document_id: @document.id - }, id: @eu_opinion.id, taxon_concept_id: @taxon_concept.id - } + put :update, + params: { + eu_opinion: { + eu_decision_type_id: @eu_decision_type.id, + start_event_id: nil, + document_id: @document.id + }, + id: @eu_opinion.id, + taxon_concept_id: @taxon_concept.id + } + expect(response).to redirect_to( admin_taxon_concept_eu_opinions_url(@taxon_concept) ) @@ -177,27 +224,35 @@ context 'when not successful' do context 'when eu decision type is present' do it 'renders new' do - put :update, params: { - eu_opinion: { - eu_decision_type_id: @eu_decision_type.id, - start_event_id: @eu_regulation.id, - start_date: nil - }, id: @eu_opinion.id, taxon_concept_id: @taxon_concept.id - } + put :update, + params: { + eu_opinion: { + eu_decision_type_id: @eu_decision_type.id, + start_event_id: @eu_regulation.id, + start_date: nil + }, + id: @eu_opinion.id, + taxon_concept_id: @taxon_concept.id + } + expect(response).to render_template('new') end end context 'when eu decision type is not present' do it 'renders new' do - put :update, params: { - eu_opinion: { - eu_decision_type_id: nil, - srg_history_id: @srg_history.id, - start_event_id: @eu_regulation.id, - start_date: nil - }, id: @eu_opinion.id, taxon_concept_id: @taxon_concept.id - } + put :update, + params: { + eu_opinion: { + eu_decision_type_id: nil, + srg_history_id: @srg_history.id, + start_event_id: @eu_regulation.id, + start_date: nil + }, + id: @eu_opinion.id, + taxon_concept_id: @taxon_concept.id + } + expect(response).to render_template('new') end end @@ -205,83 +260,109 @@ context 'when both eu_decision_type and srg_history are empty' do it 'renders new' do - put :update, params: { - eu_opinion: { - eu_decision_type_id: nil, - srg_history_id: nil, - start_date: nil - }, id: @eu_opinion.id, taxon_concept_id: @taxon_concept.id - } + put :update, + params: { + eu_opinion: { + eu_decision_type_id: nil, + srg_history_id: nil, + start_date: nil + }, + id: @eu_opinion.id, + taxon_concept_id: @taxon_concept.id + } + expect(response).to render_template('new') end end context 'when event is present' do it 'renders new' do - put :update, params: { - eu_opinion: { - eu_decision_type_id: @eu_decision_type.id, - start_event_id: @eu_regulation.id, - start_date: nil - }, id: @eu_opinion.id, taxon_concept_id: @taxon_concept.id - } + put :update, + params: { + eu_opinion: { + eu_decision_type_id: @eu_decision_type.id, + start_event_id: @eu_regulation.id, + start_date: nil + }, id: @eu_opinion.id, + taxon_concept_id: @taxon_concept.id + } + expect(response).to render_template('new') end end context 'when event is not present' do it 'renders new' do - put :update, params: { - eu_opinion: { - eu_decision_type_id: @eu_decision_type.id, - start_event_id: nil, - document_id: @document.id, - start_date: nil - }, id: @eu_opinion.id, taxon_concept_id: @taxon_concept.id - } + put :update, + params: { + eu_opinion: { + eu_decision_type_id: @eu_decision_type.id, + start_event_id: nil, + document_id: @document.id, + start_date: nil + }, + id: @eu_opinion.id, + taxon_concept_id: @taxon_concept.id + } + expect(response).to render_template('new') end end context 'when both event and intersessional doc are empty' do it 'renders new' do - put :update, params: { - eu_opinion: { - eu_decision_type_id: @eu_decision_type.id, - start_event_id: nil, - document_id: nil, - start_date: nil - }, id: @eu_opinion.id, taxon_concept_id: @taxon_concept.id - } + put :update, + params: { + eu_opinion: { + eu_decision_type_id: @eu_decision_type.id, + start_event_id: nil, + document_id: nil, + start_date: nil + }, + id: @eu_opinion.id, + taxon_concept_id: @taxon_concept.id + } + expect(response).to render_template('new') end end context 'when both event and intersessional doc are present' do it 'renders new' do - put :update, params: { - eu_opinion: { - eu_decision_type_id: @eu_decision_type.id, - start_event_id: @eu_regulation.id, - document_id: @document.id, - start_date: nil - }, id: @eu_opinion.id, taxon_concept_id: @taxon_concept.id - } + put :update, + params: { + eu_opinion: { + eu_decision_type_id: @eu_decision_type.id, + start_event_id: @eu_regulation.id, + document_id: @document.id, + start_date: nil + }, + id: @eu_opinion.id, + taxon_concept_id: @taxon_concept.id + } + expect(response).to render_template('new') end end end describe 'DELETE destroy' do - before(:each) do + before do @eu_opinion = create( :eu_opinion, taxon_concept_id: @taxon_concept.id, start_event_id: @eu_regulation.id ) end + it 'redirects after delete' do - delete :destroy, params: { id: @eu_opinion.id, taxon_concept_id: @taxon_concept.id, start_event_id: @eu_regulation.id } + delete :destroy, + params: { + id: @eu_opinion.id, + taxon_concept_id: @taxon_concept.id, + start_event_id: @eu_regulation.id + } + expect(response).to redirect_to( admin_taxon_concept_eu_opinions_url(@taxon_concept) ) diff --git a/spec/controllers/admin/instruments_controller_spec.rb b/spec/controllers/admin/instruments_controller_spec.rb index 08c160dfe..d1c38fa7f 100644 --- a/spec/controllers/admin/instruments_controller_spec.rb +++ b/spec/controllers/admin/instruments_controller_spec.rb @@ -8,19 +8,25 @@ @instrument1 = create(:instrument, name: 'BB', designation: create(:designation)) @instrument2 = create(:instrument, name: 'AA', designation: create(:designation)) end + describe 'GET index' do it 'assigns @instruments sorted by name' do get :index + expect(assigns(:instruments)).to eq([ @instrument2, @instrument1 ]) end + it 'renders the index template' do get :index + expect(response).to render_template('index') end end + describe 'XHR GET index JSON' do it 'renders json for dropdown' do get :index, format: 'json', xhr: true + expect(response.body).to have_json_size(2) expect(parse_json(response.body, '0/text')).to eq('AA') end @@ -30,38 +36,48 @@ describe 'XHR POST create' do it 'renders create when successful' do post :create, params: { instrument: build_attributes(:instrument) }, xhr: true + expect(response).to render_template('create') end it 'renders new when not successful' do - post :create, params: { instrument: { dummy: 'test' } }, xhr: true + post :create, params: { instrument: { name: nil } }, xhr: true + expect(response).to render_template('new') end end describe 'XHR PUT update' do let(:instrument) { create(:instrument) } + it 'responds with 200 when successful' do put :update, format: 'json', params: { id: instrument.id, instrument: { name: 'ZZ' } }, xhr: true expect(response).to be_successful end + it 'responds with json when not successful' do put :update, format: 'json', params: { id: instrument.id, instrument: { name: nil } }, xhr: true + expect(response.parsed_body).to include('errors') end end describe 'DELETE destroy' do let(:instrument) { create(:instrument) } + it 'redirects after delete' do delete :destroy, params: { id: instrument.id } + expect(flash[:notice]).not_to be_nil expect(flash[:alert]).to be_nil expect(response).to redirect_to(admin_instruments_url) end + let(:instrument2) { create(:instrument) } let!(:taxon_instrument) { create(:taxon_instrument, instrument_id: instrument2.id) } + it 'fails to delete instrument because there are dependent objects' do delete :destroy, params: { id: instrument2.id } + expect(flash[:notice]).to be_nil expect(flash[:alert]).not_to be_nil expect(instrument2.reload).not_to be_nil diff --git a/spec/controllers/admin/languages_controller_spec.rb b/spec/controllers/admin/languages_controller_spec.rb index 2bd6ba74b..ea2f5dee3 100644 --- a/spec/controllers/admin/languages_controller_spec.rb +++ b/spec/controllers/admin/languages_controller_spec.rb @@ -7,42 +7,71 @@ it 'assigns @languages sorted by iso_code1' do language1 = create(:language, iso_code1: 'BB', iso_code3: 'BBB') language2 = create(:language, iso_code1: 'AA', iso_code3: 'AAA') + get :index + expect(assigns(:languages)).to eq([ language2, language1 ]) end + it 'renders the index template' do get :index + expect(response).to render_template('index') end end describe 'XHR POST create' do it 'renders create when successful' do - post :create, params: { language: attributes_for(:language) }, xhr: true + post :create, + params: { language: attributes_for(:language) }, + xhr: true + expect(response).to render_template('create') end + it 'renders new when not successful' do - post :create, params: { language: { dummy: 'test' } }, xhr: true + post :create, + params: { language: { iso_code1: '' } }, + xhr: true + expect(response).to render_template('new') end end describe 'XHR PUT update' do let(:language) { create(:language) } + it 'responds with 200 when successful' do - put :update, format: 'json', params: { id: language.id, language: { iso_code1: 'ZZ' } }, xhr: true + put :update, + format: 'json', + params: { + id: language.id, + language: { iso_code1: 'ZZ' } + }, + xhr: true + expect(response).to be_successful end + it 'responds with json when not successful' do - put :update, format: 'json', params: { id: language.id, language: { iso_code1: 'zzz' } }, xhr: true + put :update, + format: 'json', + params: { + id: language.id, + language: { iso_code1: 'zzz' } + }, + xhr: true + expect(response.parsed_body).to include('errors') end end describe 'DELETE destroy' do let(:language) { create(:language) } + it 'redirects after delete' do delete :destroy, params: { id: language.id } + expect(response).to redirect_to(admin_languages_url) end end diff --git a/spec/controllers/admin/nomenclature_changes/lump_controller_spec.rb b/spec/controllers/admin/nomenclature_changes/lump_controller_spec.rb index 8fb8783ff..aaffb334e 100644 --- a/spec/controllers/admin/nomenclature_changes/lump_controller_spec.rb +++ b/spec/controllers/admin/nomenclature_changes/lump_controller_spec.rb @@ -5,47 +5,62 @@ describe 'GET show' do context 'inputs' do - before(:each) do + before do @lump = create(:nomenclature_change_lump) end + it 'renders the inputs template' do get :show, params: { id: :inputs, nomenclature_change_id: @lump.id } + expect(response).to render_template('inputs') end end + context 'outputs' do - before(:each) do + before do @lump = create(:nomenclature_change_lump) + create(:nomenclature_change_input, nomenclature_change: @lump) end + it 'renders the outputs template' do get :show, params: { id: :outputs, nomenclature_change_id: @lump.id } + expect(response).to render_template('outputs') end end + context 'reassignments' do - before(:each) do + before do @input_species = create_cites_eu_species @lump = create(:nomenclature_change_lump) + create(:nomenclature_change_input, nomenclature_change: @lump, taxon_concept: @input_species) create(:nomenclature_change_output, nomenclature_change: @lump) end + it 'renders the notes template' do get :show, params: { id: :notes, nomenclature_change_id: @lump.id } + expect(response).to render_template('notes') end + context 'when legislation present' do - before(:each) do + before do create_cites_I_addition(taxon_concept: @input_species) end + it 'renders the legislation template' do get :show, params: { id: :legislation, nomenclature_change_id: @lump.id } + expect(response).to render_template('legislation') end end + context 'when no legislation' do it 'redirects to next step' do get :show, params: { id: :legislation, nomenclature_change_id: @lump.id } + expect(response).to redirect_to( admin_nomenclature_change_lump_url( nomenclature_change_id: assigns(:nomenclature_change).id, id: 'summary' @@ -53,8 +68,10 @@ ) end end + it 'renders the summary template' do get :show, params: { id: :summary, nomenclature_change_id: @lump.id } + expect(response).to render_template('summary') end end @@ -63,6 +80,7 @@ describe 'POST create' do it 'redirects to lump wizard' do post :create, params: { nomenclature_change_id: 'new' } + expect(response).to redirect_to( admin_nomenclature_change_lump_url( nomenclature_change_id: assigns(:nomenclature_change).id, id: 'inputs' @@ -72,9 +90,10 @@ end describe 'PUT update' do - before(:each) do + before do @lump = create(:nomenclature_change_lump) end + context 'when successful' do it 'redirects to next step' do put :update, params: { @@ -85,6 +104,7 @@ } }, nomenclature_change_id: @lump.id, id: 'inputs' } + expect(response).to redirect_to( admin_nomenclature_change_lump_url( nomenclature_change_id: assigns(:nomenclature_change).id, id: 'outputs' @@ -92,6 +112,7 @@ ) end end + context 'when unsuccessful' do it 're-renders step' do put :update, params: { @@ -101,21 +122,31 @@ } }, nomenclature_change_id: @lump.id, id: 'inputs' } + expect(response).to render_template('inputs') end end + context 'when last step' do context 'when user is secretariat' do login_secretariat_user it 'redirects to admin root path' do put :update, params: { nomenclature_change_id: @lump.id, id: 'summary' } + expect(response).to redirect_to admin_root_path end end + context 'when user is manager' do it 'redirects to nomenclature changes path' do pending('Strange render mismatch after upgrading to Rails 4') - put :update, params: { nomenclature_change_id: @lump.id, id: 'summary', nomenclature_change_lump: { dummy: 'test' } } + put :update, + params: { + nomenclature_change_id: @lump.id, + id: 'summary', + nomenclature_change_lump: { event_id: 0 } + } + expect(response).to be_successful expect(response).to render_template('nomenclature_changes') end @@ -124,33 +155,43 @@ end describe 'Previous button' do - before(:each) do + before do @input_species = create_cites_eu_species @lump = create(:nomenclature_change_lump) + create(:nomenclature_change_input, nomenclature_change: @lump, taxon_concept: @input_species) create(:nomenclature_change_output, nomenclature_change: @lump) end + context 'when step is legislation' do it 'renders notes step' do get :show, params: { id: :notes, nomenclature_change_id: @lump.id, back: true } + expect(response).to render_template('notes') end end + context 'when step is summary' do context 'when legislation' do - before(:each) do + before do create_cites_I_addition(taxon_concept: @input_species) end + it 'renders legislation step' do get :show, params: { id: :legislation, nomenclature_change_id: @lump.id, back: true } + expect(response).to render_template('legislation') end end + context 'when no legislation' do it 'redirects to notes step' do get :show, params: { id: :legislation, nomenclature_change_id: @lump.id, back: true } + expect(response).to redirect_to action: :show, id: :notes + get :show, params: { id: :notes, nomenclature_change_id: @lump.id } + expect(response).to render_template('notes') end end diff --git a/spec/controllers/admin/nomenclature_changes/split_controller_spec.rb b/spec/controllers/admin/nomenclature_changes/split_controller_spec.rb index abfd0254f..cd62f35ca 100644 --- a/spec/controllers/admin/nomenclature_changes/split_controller_spec.rb +++ b/spec/controllers/admin/nomenclature_changes/split_controller_spec.rb @@ -6,43 +6,56 @@ describe 'GET show' do context 'inputs' do - before(:each) do + before do @split = create(:nomenclature_change_split) end + it 'renders the inputs template' do get :show, params: { id: :inputs, nomenclature_change_id: @split.id } + expect(response).to render_template('inputs') end end + context 'outputs' do - before(:each) do + before do @split = split_with_input end + it 'renders the outputs template' do get :show, params: { id: :outputs, nomenclature_change_id: @split.id } + expect(response).to render_template('outputs') end end + context 'reassignments' do - before(:each) do + before do @split = split_with_input_and_output end + it 'renders the notes template' do get :show, params: { id: :notes, nomenclature_change_id: @split.id } + expect(response).to render_template('notes') end + context 'when children present' do - before(:each) do + before do create_cites_eu_subspecies(parent: input_species) end + it 'renders the children template' do get :show, params: { id: :children, nomenclature_change_id: @split.id } + expect(response).to render_template('children') end end + context 'when no children' do it 'redirects to next step' do get :show, params: { id: :children, nomenclature_change_id: @split.id } + expect(response).to redirect_to( admin_nomenclature_change_split_url( nomenclature_change_id: assigns(:nomenclature_change).id, id: 'names' @@ -50,8 +63,9 @@ ) end end + context 'when names present' do - before(:each) do + before do create( :taxon_relationship, taxon_concept: input_species, @@ -59,33 +73,43 @@ taxon_relationship_type: synonym_relationship_type ) end + it 'renders the names template' do get :show, params: { id: :names, nomenclature_change_id: @split.id } + expect(response).to render_template('names') end end + context 'when no names' do it 'redirects to next step' do get :show, params: { id: :names, nomenclature_change_id: @split.id } + expect(response).to redirect_to( admin_nomenclature_change_split_url( - nomenclature_change_id: assigns(:nomenclature_change).id, id: 'distribution' + nomenclature_change_id: assigns(:nomenclature_change).id, + id: 'distribution' ) ) end end + context 'when distribution present' do - before(:each) do + before do create(:distribution, taxon_concept: input_species) end + it 'renders the distribution template' do get :show, params: { id: :distribution, nomenclature_change_id: @split.id } + expect(response).to render_template('distribution') end end + context 'when no distribution' do it 'redirects to next step' do get :show, params: { id: :distribution, nomenclature_change_id: @split.id } + expect(response).to redirect_to( admin_nomenclature_change_split_url( nomenclature_change_id: assigns(:nomenclature_change).id, id: 'legislation' @@ -93,27 +117,42 @@ ) end end + context 'when legislation present' do - before(:each) do + before do create_cites_I_addition(taxon_concept: input_species) end + it 'renders the legislation template' do - get :show, params: { id: :legislation, nomenclature_change_id: @split.id } + get :show, params: { + id: :legislation, + nomenclature_change_id: @split.id + } + expect(response).to render_template('legislation') end end + context 'when no legislation' do it 'redirects to next step' do get :show, params: { id: :legislation, nomenclature_change_id: @split.id } + expect(response).to redirect_to( admin_nomenclature_change_split_url( - nomenclature_change_id: assigns(:nomenclature_change).id, id: 'summary' + nomenclature_change_id: assigns(:nomenclature_change).id, + id: 'summary' ) ) end end + it 'renders the summary template' do - get :show, params: { id: :summary, nomenclature_change_id: @split.id } + get :show, + params: { + id: :summary, + nomenclature_change_id: @split.id + } + expect(response).to render_template('summary') end end @@ -122,25 +161,32 @@ describe 'POST create' do it 'redirects to split wizard' do post :create, params: { nomenclature_change_id: 'new' } + expect(response).to redirect_to( admin_nomenclature_change_split_url( - nomenclature_change_id: assigns(:nomenclature_change).id, id: 'inputs' + nomenclature_change_id: assigns(:nomenclature_change).id, + id: 'inputs' ) ) end end describe 'PUT update' do - before(:each) do + before do @split = create(:nomenclature_change_split) end + context 'when successful' do it 'redirects to next step' do - put :update, params: { - nomenclature_change_split: { - input_attributes: { taxon_concept_id: create_cites_eu_species.id } - }, nomenclature_change_id: @split.id, id: 'inputs' - } + put :update, + params: { + nomenclature_change_split: { + input_attributes: { taxon_concept_id: create_cites_eu_species.id } + }, + nomenclature_change_id: @split.id, + id: 'inputs' + } + expect(response).to redirect_to( admin_nomenclature_change_split_url( nomenclature_change_id: assigns(:nomenclature_change).id, id: 'outputs' @@ -148,28 +194,43 @@ ) end end + context 'when unsuccessful' do it 're-renders step' do - put :update, params: { - nomenclature_change_split: { - input_attributes: { taxon_concept_id: nil } - }, nomenclature_change_id: @split.id, id: 'inputs' - } + put :update, + params: { + nomenclature_change_split: { + input_attributes: { taxon_concept_id: nil } + }, + nomenclature_change_id: @split.id, + id: 'inputs' + } + expect(response).to render_template('inputs') end end + context 'when last step' do context 'when user is secretariat' do login_secretariat_user it 'redirects to admin root path' do put :update, params: { nomenclature_change_id: @split.id, id: 'summary' } + expect(response).to redirect_to admin_root_path end end + context 'when user is manager' do it 'redirects to nomenclature changes path' do pending('Strange render mismatch after upgrading to Rails 4') - put :update, params: { nomenclature_change_id: @split.id, id: 'summary', nomenclature_change_split: { dummy: 'test' } } + + put :update, + params: { + nomenclature_change_id: @split.id, + id: 'summary', + nomenclature_change_split: { event_id: 0 } + } + expect(response).to be_successful expect(response).to render_template('nomenclature_changes') end @@ -178,31 +239,39 @@ end describe 'Previous button' do - before(:each) do + before do @split = split_with_input_and_output end + context 'when step is names' do context 'when children' do - before(:each) do + before do create_cites_eu_subspecies(parent: input_species) end + it 'renders children template' do get :show, params: { id: :children, nomenclature_change_id: @split.id, back: true } + expect(response).to render_template('children') end end + context 'when no children' do it 'redirects to notes step' do get :show, params: { id: :children, nomenclature_change_id: @split.id, back: true } + expect(response).to redirect_to action: :show, id: :notes + get :show, params: { id: :notes, nomenclature_change_id: @split.id } + expect(response).to render_template('notes') end end end + context 'when step is distribution' do context 'when names' do - before(:each) do + before do create( :taxon_relationship, taxon_concept: input_species, @@ -210,54 +279,74 @@ taxon_relationship_type: synonym_relationship_type ) end + it 'renders names template' do get :show, params: { id: :names, nomenclature_change_id: @split.id, back: :true } + expect(response).to render_template('names') end end + context 'when no names and no children' do it 'redirects to notes step' do get :show, params: { id: :names, nomenclature_change_id: @split.id, back: true } + expect(response).to redirect_to action: :show, id: :children + get :show, params: { id: :children, nomenclature_change_id: @split.id } + expect(response).to redirect_to action: :show, id: :notes end end end + context 'when step is legislation' do context 'when distribution' do - before(:each) do + before do create(:distribution, taxon_concept: input_species) end + it 'renders distribution template' do get :show, params: { id: :distribution, nomenclature_change_id: @split.id, back: true } + expect(response).to render_template('distribution') end end + context 'when no distribution and no names' do it 'redirects to children step' do get :show, params: { id: :distribution, nomenclature_change_id: @split.id, back: true } + expect(response).to redirect_to action: :show, id: :names + get :show, params: { id: :names, nomenclature_change_id: @split.id } + expect(response).to redirect_to action: :show, id: :children end end end + context 'when step is summary' do context 'when legislation' do - before(:each) do + before do create_cites_I_addition(taxon_concept: input_species) end + it 'renders legislation template' do get :show, params: { id: :legislation, nomenclature_change_id: @split.id, back: true } + expect(response).to render_template('legislation') end end + context 'when no legislation and no distribution' do it 'redirects to names step' do get :show, params: { id: :legislation, nomenclature_change_id: @split.id, back: true } + expect(response).to redirect_to action: :show, id: :distribution + get :show, params: { id: :distribution, nomenclature_change_id: @split.id } + expect(response).to redirect_to action: :show, id: :names end end diff --git a/spec/controllers/admin/nomenclature_changes/status_swap_controller_spec.rb b/spec/controllers/admin/nomenclature_changes/status_swap_controller_spec.rb index 1b271ac15..a61328437 100644 --- a/spec/controllers/admin/nomenclature_changes/status_swap_controller_spec.rb +++ b/spec/controllers/admin/nomenclature_changes/status_swap_controller_spec.rb @@ -6,53 +6,88 @@ describe 'GET show' do context 'primary_output' do - before(:each) do + before do @status_change = create(:nomenclature_change_status_swap) end + it 'renders the primary_output template' do - get :show, params: { id: :primary_output, nomenclature_change_id: @status_change.id } + get :show, + params: { + id: :primary_output, + nomenclature_change_id: @status_change.id + } + expect(response).to render_template('primary_output') end end + context 'swap' do - before(:each) do + before do @status_change = a_to_s_with_swap end + it 'renders the swap template' do - get :show, params: { id: :secondary_output, nomenclature_change_id: @status_change.id } + get :show, + params: { + id: :secondary_output, + nomenclature_change_id: @status_change.id + } + expect(response).to render_template('secondary_output') end end + context 'reassignments' do - before(:each) do + before do @status_change = a_to_s_with_swap end + context 'when legislation present' do - before(:each) do + before do create_cites_I_addition(taxon_concept: input_species) end + it 'renders the legislation template' do - get :show, params: { id: :legislation, nomenclature_change_id: @status_change.id } + get :show, + params: { + id: :legislation, + nomenclature_change_id: @status_change.id + } + expect(response).to render_template('legislation') end end + context 'when no legislation' do it 'redirects to next step' do - get :show, params: { id: :legislation, nomenclature_change_id: @status_change.id } + get :show, + params: { + id: :legislation, + nomenclature_change_id: @status_change.id + } + expect(response).to redirect_to( admin_nomenclature_change_status_swap_url( - nomenclature_change_id: assigns(:nomenclature_change).id, id: 'summary' + id: 'summary', + nomenclature_change_id: assigns(:nomenclature_change).id ) ) end end end + context 'summary' do - before(:each) do + before do @status_change = a_to_s_with_swap end + it 'renders the summary template' do - get :show, params: { id: :summary, nomenclature_change_id: @status_change.id } + get :show, + params: { + id: :summary, + nomenclature_change_id: @status_change.id + } + expect(response).to render_template('summary') end end @@ -61,53 +96,80 @@ describe 'POST create' do it 'redirects to status_change wizard' do post :create, params: { nomenclature_change_id: 'new' } + expect(response).to redirect_to( admin_nomenclature_change_status_swap_url( - nomenclature_change_id: assigns(:nomenclature_change).id, id: 'primary_output' + id: 'primary_output', + nomenclature_change_id: assigns(:nomenclature_change).id ) ) end end describe 'PUT update' do - before(:each) do + before do @status_change = create(:nomenclature_change_status_swap) end + context 'when successful' do it 'redirects to next step' do - put :update, params: { - nomenclature_change_status_swap: { - primary_output_attributes: { - taxon_concept_id: create_cites_eu_species.id, - new_name_status: 'S' - } - }, nomenclature_change_id: @status_change.id, id: 'primary_output' - } + put :update, + params: { + nomenclature_change_status_swap: { + primary_output_attributes: { + taxon_concept_id: create_cites_eu_species.id, + new_name_status: 'S' + } + }, + nomenclature_change_id: @status_change.id, + id: 'primary_output' + } + expect(response).to redirect_to( admin_nomenclature_change_status_swap_url( - nomenclature_change_id: assigns(:nomenclature_change).id, id: 'secondary_output' + nomenclature_change_id: assigns(:nomenclature_change).id, + id: 'secondary_output' ) ) end end + context 'when unsuccessful' do it 're-renders step' do - put :update, params: { nomenclature_change_status_swap: { dummy: 'test' }, nomenclature_change_id: @status_change.id, id: 'primary_output' } + put :update, + params: { + nomenclature_change_status_swap: { event_id: 0 }, + nomenclature_change_id: @status_change.id, id: 'primary_output' + } + expect(response).to render_template('primary_output') end end + context 'when last step' do context 'when user is secretariat' do login_secretariat_user it 'redirects to admin root path' do - put :update, params: { nomenclature_change_id: @status_change.id, id: 'summary' } + put :update, + params: { + nomenclature_change_id: @status_change.id, + id: 'summary' + } + expect(response).to redirect_to admin_root_path end end + context 'when user is manager' do it 'redirects to nomenclature changes path' do pending('Strange render mismatch after upgrading to Rails 4') - put :update, params: { nomenclature_change_id: @status_change.id, id: 'summary', nomenclature_change_status_swap: { dummy: 'test' } } + put :update, + params: { + nomenclature_change_id: @status_change.id, + id: 'summary', + nomenclature_change_status_swap: {} + } + expect(response).to be_successful expect(response).to render_template('nomenclature_changes') end diff --git a/spec/controllers/admin/nomenclature_changes/status_to_accepted_controller_spec.rb b/spec/controllers/admin/nomenclature_changes/status_to_accepted_controller_spec.rb index fdcfeb2d3..16add018e 100644 --- a/spec/controllers/admin/nomenclature_changes/status_to_accepted_controller_spec.rb +++ b/spec/controllers/admin/nomenclature_changes/status_to_accepted_controller_spec.rb @@ -6,20 +6,25 @@ describe 'GET show' do context 'primary_output' do - before(:each) do + before do @status_change = create(:nomenclature_change_status_to_accepted) end + it 'renders the primary_output template' do get :show, params: { id: :primary_output, nomenclature_change_id: @status_change.id } + expect(response).to render_template('primary_output') end end + context 'summary' do - before(:each) do + before do @status_change = t_to_a_with_input end + it 'renders the summary template' do get :show, params: { id: :summary, nomenclature_change_id: @status_change.id } + expect(response).to render_template('summary') end end @@ -28,59 +33,92 @@ describe 'POST create' do it 'redirects to status_change wizard' do post :create, params: { nomenclature_change_id: 'new' } + expect(response).to redirect_to( admin_nomenclature_change_status_to_accepted_url( - nomenclature_change_id: assigns(:nomenclature_change).id, id: 'primary_output' + nomenclature_change_id: assigns(:nomenclature_change).id, + id: 'primary_output' ) ) end end describe 'PUT update' do - before(:each) do + before do @status_change = create(:nomenclature_change_status_to_accepted) end + context 'when successful' do it 'redirects to next step' do - put :update, params: { - nomenclature_change_status_to_accepted: { - primary_output_attributes: { - taxon_concept_id: create_cites_eu_species( - name_status: 'T', - taxon_name: create(:taxon_name, scientific_name: 'Patagonus miserabilis') - ).id, - new_parent_id: create_cites_eu_genus( - taxon_name: create(:taxon_name, scientific_name: 'Patagonus') - ).id, - new_name_status: 'A' - } - }, nomenclature_change_id: @status_change.id, id: 'primary_output' - } + put :update, + params: { + nomenclature_change_status_to_accepted: { + primary_output_attributes: { + taxon_concept_id: create_cites_eu_species( + name_status: 'T', + taxon_name: create(:taxon_name, scientific_name: 'Patagonus miserabilis') + ).id, + new_parent_id: create_cites_eu_genus( + taxon_name: create(:taxon_name, scientific_name: 'Patagonus') + ).id, + new_name_status: 'A' + } + }, + nomenclature_change_id: @status_change.id, + id: 'primary_output' + } + expect(response).to redirect_to( admin_nomenclature_change_status_to_accepted_url( - nomenclature_change_id: assigns(:nomenclature_change).id, id: 'summary' + nomenclature_change_id: assigns(:nomenclature_change).id, + id: 'summary' ) ) end end + context 'when unsuccessful' do it 're-renders step' do - put :update, params: { nomenclature_change_status_to_accepted: { dummy: 'test' }, nomenclature_change_id: @status_change.id, id: 'primary_output' } + put :update, + params: { + nomenclature_change_status_to_accepted: { + primary_output_attributes: { + new_name_status: '£' + } + }, + nomenclature_change_id: @status_change.id, + id: 'primary_output' + } + expect(response).to render_template('primary_output') end end + context 'when last step' do context 'when user is secretariat' do login_secretariat_user + it 'redirects to admin root path' do - put :update, params: { nomenclature_change_id: @status_change.id, id: 'summary' } + put :update, + params: { + nomenclature_change_id: @status_change.id, + id: 'summary' + } + expect(response).to redirect_to admin_root_path end end + context 'when user is manager' do it 'redirects to nomenclature changes path' do pending('Strange render mismatch after upgrading to Rails 4') - put :update, params: { nomenclature_change_id: @status_change.id, id: 'summary', nomenclature_change_status_to_accepted: { dummy: 'test' } } + + put :update, + params: { + nomenclature_change_id: @status_change.id, + id: 'summary' + } + expect(response).to be_successful expect(response).to render_template('nomenclature_changes') end diff --git a/spec/controllers/admin/nomenclature_changes/status_to_synonym_controller_spec.rb b/spec/controllers/admin/nomenclature_changes/status_to_synonym_controller_spec.rb index 2f5688d2f..4c8f8e73a 100644 --- a/spec/controllers/admin/nomenclature_changes/status_to_synonym_controller_spec.rb +++ b/spec/controllers/admin/nomenclature_changes/status_to_synonym_controller_spec.rb @@ -2,23 +2,28 @@ describe Admin::NomenclatureChanges::StatusToSynonymController do login_admin + include_context 'status_change_definitions' + let(:input_species) { create_cites_eu_species(name_status: 'N') } describe 'GET show' do context 'primary_output' do - before(:each) do + before do @status_change = create(:nomenclature_change_status_to_synonym) end + it 'renders the primary_output template' do get :show, params: { id: :primary_output, nomenclature_change_id: @status_change.id } expect(response).to render_template('primary_output') end end + context 'relay' do - before(:each) do + before do @status_change = n_to_s_with_primary_output end + it 'renders the relay template' do get :show, params: { id: :relay, nomenclature_change_id: @status_change.id } expect(response).to render_template('relay') @@ -26,9 +31,10 @@ end context 'summary' do - before(:each) do + before do @status_change = n_to_s_with_input_and_secondary_output end + it 'renders the summary template' do get :show, params: { id: :summary, nomenclature_change_id: @status_change.id } expect(response).to render_template('summary') @@ -39,6 +45,7 @@ describe 'POST create' do it 'redirects to status_change wizard' do post :create, params: { nomenclature_change_id: 'new' } + expect(response).to redirect_to( admin_nomenclature_change_status_to_synonym_url( nomenclature_change_id: assigns(:nomenclature_change).id, id: 'primary_output' @@ -48,9 +55,10 @@ end describe 'PUT update' do - before(:each) do + before do @status_change = create(:nomenclature_change_status_to_synonym) end + context 'when successful' do it 'redirects to next step' do put :update, params: { @@ -59,8 +67,11 @@ taxon_concept_id: create_cites_eu_species(name_status: 'N').id, new_name_status: 'S' } - }, nomenclature_change_id: @status_change.id, id: 'primary_output' + }, + nomenclature_change_id: @status_change.id, + id: 'primary_output' } + expect(response).to redirect_to( admin_nomenclature_change_status_to_synonym_url( nomenclature_change_id: assigns(:nomenclature_change).id, id: 'relay' @@ -68,24 +79,50 @@ ) end end + context 'when unsuccessful' do it 're-renders step' do - put :update, params: { nomenclature_change_status_to_synonym: { dummy: 'test' }, nomenclature_change_id: @status_change.id, id: 'primary_output' } + put :update, + params: { + nomenclature_change_status_to_synonym: { + primary_output_attributes: { + new_name_status: '£' + } + }, + nomenclature_change_id: @status_change.id, + id: 'primary_output' + } + expect(response).to render_template('primary_output') end end + context 'when last step' do context 'when user is secretariat' do login_secretariat_user + it 'redirects to admin root path' do - put :update, params: { nomenclature_change_id: @status_change.id, id: 'summary' } + put :update, + params: { + nomenclature_change_id: @status_change.id, + nomenclature_change: {}, + id: 'summary' + } + expect(response).to redirect_to admin_root_path end end + context 'when user is manager' do it 'redirects to nomenclature changes path' do pending('Strange render mismatch after upgrading to Rails 4') - put :update, params: { nomenclature_change_id: @status_change.id, id: 'summary', nomenclature_change_status_to_synonym: { dummy: 'test' } } + put :update, + params: { + nomenclature_change_id: @status_change.id, + nomenclature_change: {}, + id: 'summary' + } + expect(response).to be_successful expect(response).to render_template('nomenclature_changes') end diff --git a/spec/controllers/admin/ranks_controller_spec.rb b/spec/controllers/admin/ranks_controller_spec.rb index c0835221e..dfb309faa 100644 --- a/spec/controllers/admin/ranks_controller_spec.rb +++ b/spec/controllers/admin/ranks_controller_spec.rb @@ -7,11 +7,15 @@ it 'assigns @ranks sorted by taxonomic position' do rank2 = create(:rank, name: Rank::PHYLUM, taxonomic_position: '2') rank1 = create(:rank, name: Rank::KINGDOM, taxonomic_position: '1') + get :index + expect(assigns(:ranks)).to eq([ rank1, rank2 ]) end + it 'renders the index template' do get :index + expect(response).to render_template('index') end end @@ -19,30 +23,39 @@ describe 'XHR POST create' do it 'renders create when successful' do post :create, params: { rank: build_attributes(:rank) }, xhr: true + expect(response).to render_template('create') end + it 'renders new when not successful' do - post :create, params: { rank: { dummy: 'test' } }, xhr: true + post :create, params: { rank: { taxonomic_position: '0' } }, xhr: true + expect(response).to render_template('new') end end describe 'XHR PUT update' do let(:rank) { create(:rank) } + it 'responds with 200 when successful' do put :update, format: 'json', params: { id: rank.id, rank: { name: 'ZZ' } }, xhr: true + expect(response).to be_successful end + it 'responds with json when not successful' do put :update, format: 'json', params: { id: rank.id, rank: { name: nil } }, xhr: true + expect(response.parsed_body).to include('errors') end end describe 'DELETE destroy' do let(:rank) { create(:rank) } + it 'redirects after delete' do delete :destroy, params: { id: rank.id } + expect(response).to redirect_to(admin_ranks_url) end end diff --git a/spec/controllers/admin/species_listings_controller_spec.rb b/spec/controllers/admin/species_listings_controller_spec.rb index cb8150581..a1589385d 100644 --- a/spec/controllers/admin/species_listings_controller_spec.rb +++ b/spec/controllers/admin/species_listings_controller_spec.rb @@ -10,11 +10,15 @@ species_listing2_1 = create(:species_listing, designation: designation2, name: 'I') species_listing2_2 = create(:species_listing, designation: designation2, name: 'II') species_listing1 = create(:species_listing, designation: designation1, name: 'I') + get :index + expect(assigns(:species_listings)).to eq([ species_listing1, species_listing2_1, species_listing2_2 ]) end + it 'renders the index template' do get :index + expect(response).to render_template('index') end end @@ -22,30 +26,45 @@ describe 'XHR POST create' do it 'renders create when successful' do post :create, params: { species_listing: build_attributes(:species_listing) }, xhr: true + expect(response).to render_template('create') end + it 'renders new when not successful' do - post :create, params: { species_listing: { dummy: 'test' } }, xhr: true + post :create, params: { species_listing: { name: 'NOT ENOUGH' } }, xhr: true + expect(response).to render_template('new') end end describe 'XHR PUT update' do let(:species_listing) { create(:species_listing) } + it 'responds with 200 when successful' do - put :update, format: 'json', params: { id: species_listing.id, species_listing: { name: 'ZZ' } }, xhr: true + put :update, + format: 'json', + params: { id: species_listing.id, species_listing: { name: 'ZZ' } }, + xhr: true + expect(response).to be_successful end + it 'responds with json when not successful' do - put :update, format: 'json', params: { id: species_listing.id, species_listing: { name: nil } }, xhr: true + put :update, + format: 'json', + params: { id: species_listing.id, species_listing: { name: '' } }, + xhr: true + expect(response.parsed_body).to include('errors') end end describe 'DELETE destroy' do let(:species_listing) { create(:species_listing) } + it 'redirects after delete' do delete :destroy, params: { id: species_listing.id } + expect(response).to redirect_to(admin_species_listings_url) end end diff --git a/spec/controllers/admin/srg_histories_controller_spec.rb b/spec/controllers/admin/srg_histories_controller_spec.rb index 9c9e62cc3..76effe86a 100644 --- a/spec/controllers/admin/srg_histories_controller_spec.rb +++ b/spec/controllers/admin/srg_histories_controller_spec.rb @@ -6,6 +6,7 @@ describe 'GET index' do it 'renders the index template' do get :index + expect(response).to render_template('index') end end @@ -25,7 +26,7 @@ context 'when not successful' do it 'renders new' do - post :create, params: { srg_history: { dummy: 'test' }, format: :js } + post :create, params: { srg_history: { name: '' }, format: :js } expect(response).to render_template('new') end @@ -33,13 +34,13 @@ end describe 'PUT update' do - before(:each) do + before do @srg_history = create(:srg_history) end context 'when successful' do it 'renders the create js template' do - put :update, params: { id: @srg_history.id, srg_history: { dummy: 'test' }, format: :js } + put :update, params: { id: @srg_history.id, srg_history: { name: 'Test SRG' }, format: :js } expect(response).to render_template('create') end @@ -55,7 +56,7 @@ end describe 'DELETE destroy' do - before(:each) do + before do @srg_history = create(:srg_history) end diff --git a/spec/controllers/admin/tags_controller_spec.rb b/spec/controllers/admin/tags_controller_spec.rb index 3d9491e5e..ea9aba8ef 100644 --- a/spec/controllers/admin/tags_controller_spec.rb +++ b/spec/controllers/admin/tags_controller_spec.rb @@ -6,6 +6,7 @@ describe 'GET index' do it 'renders the index template' do get :index + expect(response).to render_template('index') expect(response).to render_template('layouts/admin') end @@ -13,24 +14,52 @@ describe 'XHR POST create' do it 'renders create when successful' do - post :create, params: { tag: { name: 'Test Tag', model: 'TaxonConcept' } }, xhr: true + post :create, + params: { + tag: { + name: 'Test Tag', + model: 'TaxonConcept' + } + }, + xhr: true + expect(response).to render_template('create') end + it 'renders new when not successful' do - post :create, params: { tag: { dummy: 'test' } }, xhr: true + post :create, + params: { tag: { name: nil } }, + xhr: true + expect(response).to render_template('new') end end describe 'XHR PUT update' do let(:preset_tag) { create(:preset_tag) } + context 'when JSON' do it 'responds with 200 when successful' do - put :update, format: 'json', params: { id: preset_tag.id, tag: { dummy: 'test' } }, xhr: true + put :update, + format: 'json', + params: { + id: preset_tag.id, + tag: { name: 'Tag for testing' } + }, + xhr: true + expect(response).to be_successful end + it 'responds with json error when not successful' do - put :update, format: 'json', params: { id: preset_tag.id, tag: { model: 'FakeCategory' } }, xhr: true + put :update, + format: 'json', + params: { + id: preset_tag.id, + tag: { model: 'FakeCategory' } + }, + xhr: true + expect(response.parsed_body).to include('errors') end end @@ -38,8 +67,10 @@ describe 'DELETE destroy' do let(:preset_tag) { create(:preset_tag) } + it 'redirects after delete' do delete :destroy, params: { id: preset_tag.id } + expect(response).to redirect_to(admin_tags_url) end end diff --git a/spec/controllers/admin/taxon_cites_suspensions_controller_spec.rb b/spec/controllers/admin/taxon_cites_suspensions_controller_spec.rb index 35eac94d5..54a128bcb 100644 --- a/spec/controllers/admin/taxon_cites_suspensions_controller_spec.rb +++ b/spec/controllers/admin/taxon_cites_suspensions_controller_spec.rb @@ -15,10 +15,13 @@ describe 'GET index' do it 'renders the index template' do get :index, params: { taxon_concept_id: @taxon_concept.id } + expect(response).to render_template('index') end + it 'renders the taxon_concepts_layout' do get :index, params: { taxon_concept_id: @taxon_concept.id } + expect(response).to render_template('layouts/taxon_concepts') end end @@ -26,6 +29,7 @@ describe 'GET new' do it 'renders the new template' do get :new, params: { taxon_concept_id: @taxon_concept.id } + expect(response).to render_template('new') end end @@ -33,18 +37,26 @@ describe 'POST create' do context 'when successful' do it 'renders index' do - post :create, params: { - cites_suspension: { - start_notification_id: create_cites_suspension_notification.id - }, taxon_concept_id: @taxon_concept.id - } + post :create, + params: { + cites_suspension: { + start_notification_id: create_cites_suspension_notification.id + }, taxon_concept_id: @taxon_concept.id + } + expect(response).to redirect_to( admin_taxon_concept_cites_suspensions_url(@taxon_concept) ) end end + it 'renders new when not successful' do - post :create, params: { cites_suspension: { dummy: 'test' }, taxon_concept_id: @taxon_concept.id } + post :create, + params: { + cites_suspension: { start_notification_id: 0 }, + taxon_concept_id: @taxon_concept.id + } + expect(response).to render_template('new') end end @@ -52,6 +64,7 @@ describe 'GET edit' do it 'renders the edit template' do get :edit, params: { id: @cites_suspension.id, taxon_concept_id: @taxon_concept.id } + expect(response).to render_template('edit') end end @@ -59,11 +72,13 @@ describe 'PUT update' do context 'when successful' do it 'renders taxon_concepts cites suspensions page' do - put :update, params: { - cites_suspension: { - publication_date: 1.week.ago - }, id: @cites_suspension.id, taxon_concept_id: @taxon_concept.id - } + put :update, + params: { + cites_suspension: { + publication_date: 1.week.ago + }, id: @cites_suspension.id, taxon_concept_id: @taxon_concept.id + } + expect(response).to redirect_to( admin_taxon_concept_cites_suspensions_url(@taxon_concept) ) @@ -71,11 +86,13 @@ end it 'renders edit when not successful' do - put :update, params: { - cites_suspension: { - start_notification_id: nil - }, id: @cites_suspension.id, taxon_concept_id: @taxon_concept.id - } + put :update, + params: { + cites_suspension: { + start_notification_id: nil + }, id: @cites_suspension.id, taxon_concept_id: @taxon_concept.id + } + expect(response).to render_template('edit') end end @@ -83,6 +100,7 @@ describe 'DELETE destroy' do it 'redirects after delete' do delete :destroy, params: { id: @cites_suspension.id, taxon_concept_id: @taxon_concept.id } + expect(response).to redirect_to( admin_taxon_concept_cites_suspensions_url(@taxon_concept) ) @@ -95,17 +113,23 @@ describe 'GET index' do it 'renders the index template' do get :index, params: { taxon_concept_id: @taxon_concept.id } + expect(response).to render_template('index') end + it 'renders the taxon_concepts_layout' do get :index, params: { taxon_concept_id: @taxon_concept.id } + expect(response).to render_template('layouts/taxon_concepts') end end + describe 'DELETE destroy' do it 'fails to delete and redirects' do @request.env['HTTP_REFERER'] = admin_taxon_concept_cites_suspensions_url(@taxon_concept) + delete :destroy, params: { id: @cites_suspension.id, taxon_concept_id: @taxon_concept.id } + expect(response).to redirect_to( admin_taxon_concept_cites_suspensions_url(@taxon_concept) ) diff --git a/spec/controllers/admin/taxon_commons_controller_spec.rb b/spec/controllers/admin/taxon_commons_controller_spec.rb index de6f53993..29c4cd8bf 100644 --- a/spec/controllers/admin/taxon_commons_controller_spec.rb +++ b/spec/controllers/admin/taxon_commons_controller_spec.rb @@ -11,6 +11,7 @@ describe "XHR GET 'new'" do it 'returns http success and renders the new template' do get :new, params: { taxon_concept_id: @taxon_concept.id, format: 'js' }, xhr: true + expect(response).to be_successful expect(response).to render_template('new') end @@ -18,7 +19,8 @@ describe 'XHR POST create' do it 'renders create when successful' do - post :create, xhr: true, + post :create, + xhr: true, params: { taxon_concept_id: @taxon_concept.id, taxon_common: { @@ -26,14 +28,18 @@ language_id: @common_name.language_id } } + expect(response).to render_template('create') end + it 'renders new when not successful' do - post :create, xhr: true, + post :create, + xhr: true, params: { taxon_concept_id: @taxon_concept.id, - taxon_common: { dummy: 'test' } + taxon_common: { name: nil } } + expect(response).to render_template('new') end end @@ -46,12 +52,16 @@ taxon_concept_id: @taxon_concept.id ) end + it 'renders the edit template' do get :edit, params: { taxon_concept_id: @taxon_concept.id, id: @taxon_common.id }, xhr: true + expect(response).to render_template('new') end + it 'assigns the taxon common variable' do get :edit, params: { taxon_concept_id: @taxon_concept.id, id: @taxon_common.id }, xhr: true + expect(assigns(:taxon_common)).not_to be_nil end end @@ -64,8 +74,11 @@ taxon_concept_id: @taxon_concept.id ) end + it 'renders create when successful' do - put :update, format: 'js', xhr: true, + put :update, + format: 'js', + xhr: true, params: { taxon_concept_id: @taxon_concept.id, id: @taxon_common.id, @@ -74,10 +87,13 @@ language_id: @common_name.language_id } } + expect(response).to render_template('create') end it 'renders new when not successful' do - put :update, format: 'js', xhr: true, + put :update, + format: 'js', + xhr: true, params: { taxon_concept_id: @taxon_concept.id, id: @taxon_common.id, @@ -85,6 +101,7 @@ common_name_id: nil } } + expect(response).to render_template('new') end end @@ -97,8 +114,10 @@ common_name: @common_name ) end + it 'redirects after delete' do delete :destroy, params: { taxon_concept_id: @taxon_concept.id, id: taxon_common.id } + expect(response).to redirect_to( admin_taxon_concept_names_url(@taxon_concept) ) @@ -124,7 +143,9 @@ expect(@taxon_concept.reload.dependents_updated_at).not_to be_nil old_date = @taxon_concept.dependents_updated_at - put :update, format: 'js', xhr: true, + put :update, + format: 'js', + xhr: true, params: { taxon_concept_id: @taxon_concept.id, id: @taxon_common.id, @@ -155,6 +176,7 @@ describe 'Authorization for contributors' do login_contributor + let(:taxon_common) do create( :taxon_common, @@ -162,10 +184,13 @@ common_name: @common_name ) end + describe 'DELETE destroy' do it 'fails to delete and redirects' do @request.env['HTTP_REFERER'] = admin_taxon_concept_names_url(@taxon_concept) + delete :destroy, params: { id: taxon_common.id, taxon_concept_id: @taxon_concept.id } + expect(response).to redirect_to( admin_taxon_concept_names_url(@taxon_concept) ) diff --git a/spec/controllers/admin/taxon_concept_references_controller_spec.rb b/spec/controllers/admin/taxon_concept_references_controller_spec.rb index a39f169b0..4f57323a9 100644 --- a/spec/controllers/admin/taxon_concept_references_controller_spec.rb +++ b/spec/controllers/admin/taxon_concept_references_controller_spec.rb @@ -10,23 +10,30 @@ describe 'XHR POST create' do it 'renders create when successful' do - post :create, xhr: true, params: { - taxon_concept_id: @taxon_concept.id, - taxon_concept_reference: { - reference_attributes: { citation: 'My nice literature' } + post :create, + xhr: true, + params: { + taxon_concept_id: @taxon_concept.id, + taxon_concept_reference: { + reference_attributes: { citation: 'My nice literature' } + } } - } + expect(response).to render_template('create') end + it 'renders new when not successful' do - post :create, xhr: true, params: { - taxon_concept_id: @taxon_concept.id, - taxon_concept_reference: { - reference_attributes: { - dummy: 'test' + post :create, + xhr: true, + params: { + taxon_concept_id: @taxon_concept.id, + taxon_concept_reference: { + reference_attributes: { + dummy: 'test' + } } } - } + expect(response).to render_template('new') end end @@ -39,12 +46,16 @@ taxon_concept_id: @taxon_concept.id ) end + it 'renders the edit template' do get :edit, params: { taxon_concept_id: @taxon_concept.id, id: @taxon_concept_reference.id }, xhr: true + expect(response).to render_template('new') end + it 'assigns the taxon concept reference variable' do get :edit, params: { taxon_concept_id: @taxon_concept.id, id: @taxon_concept_reference.id }, xhr: true + expect(assigns(:taxon_concept_reference)).not_to be_nil end end @@ -57,8 +68,11 @@ taxon_concept_id: @taxon_concept.id ) end + it 'renders create when successful' do - put :update, format: 'js', xhr: true, + put :update, + format: 'js', + xhr: true, params: { taxon_concept_id: @taxon_concept.id, id: @taxon_concept_reference.id, @@ -66,19 +80,25 @@ reference_attributes: { citation: 'My nice literature' } } } + expect(response).to render_template('create') end + it 'renders new when not successful' do - put :update, format: 'js', xhr: true, + put :update, + format: 'js', + xhr: true, params: { taxon_concept_id: @taxon_concept.id, id: @taxon_concept_reference.id, taxon_concept_reference: { + reference_id: 'not a valid integer', reference_attributes: { - dummy: 'test' + citation: nil } } } + expect(response).to render_template('new') end end @@ -86,6 +106,7 @@ describe "XHR GET 'new'" do it 'returns http success and renders the new template' do get :new, params: { taxon_concept_id: @taxon_concept.id }, xhr: true, format: 'js' + expect(response).to be_successful expect(response).to render_template('new') end @@ -93,8 +114,10 @@ describe 'DELETE destroy' do let(:taxon_concept_reference) { create(:taxon_concept_reference, taxon_concept_id: @taxon_concept.id, reference_id: @reference.id) } + it 'redirects after delete' do delete :destroy, params: { taxon_concept_id: @taxon_concept.id, id: taxon_concept_reference.id } + expect(response).to redirect_to( admin_taxon_concept_taxon_concept_references_url(taxon_concept_reference.taxon_concept) ) diff --git a/spec/controllers/admin/taxon_concepts_controller_spec.rb b/spec/controllers/admin/taxon_concepts_controller_spec.rb index a4dcf51c1..eaadc24f7 100644 --- a/spec/controllers/admin/taxon_concepts_controller_spec.rb +++ b/spec/controllers/admin/taxon_concepts_controller_spec.rb @@ -4,7 +4,7 @@ login_admin describe 'GET index' do - before(:each) do + before do @taxon = create_cites_eu_species( taxon_name: create(:taxon_name, scientific_name: 'indefinitus'), taxonomic_position: '1.1.2', @@ -14,32 +14,41 @@ ) ) end + it 'renders the index template' do get :index + expect(response).to render_template('index') expect(response).to render_template('layouts/admin') end + it 'redirects if 1 result' do - get :index, params: { - search_params: { - taxonomy: { id: cites_eu.id }, scientific_name: 'Foobarus i' + get :index, + params: { + search_params: { + taxonomy: { id: cites_eu.id }, scientific_name: 'Foobarus i' + } } - } + expect(response).to redirect_to(admin_taxon_concept_names_path(@taxon)) end + it 'assigns taxa in taxonomic order' do - get :index, params: { - search_params: { - taxonomy: { id: cites_eu.id }, scientific_name: 'Foobarus' + get :index, + params: { + search_params: { + taxonomy: { id: cites_eu.id }, scientific_name: 'Foobarus' + } } - } + expect(assigns(:taxon_concepts)).to eq([ @taxon.parent, @taxon ]) end end describe 'XHR POST create' do it 'renders create when successful' do - post :create, xhr: true, + post :create, + xhr: true, params: { taxon_concept: { name_status: 'A', @@ -49,53 +58,82 @@ parent_id: create_cites_eu_family } } + expect(response).to render_template('create') end + it 'renders new when not successful' do - post :create, params: { taxon_concept: { dummy: 'test' } }, xhr: true + post :create, params: { taxon_concept: { name_status: '' } }, xhr: true + expect(response).to render_template('new') end + it 'renders new_synonym when not successful S' do post :create, params: { taxon_concept: { name_status: 'S' } }, xhr: true + expect(response).to render_template('new_synonym') end + it 'renders new_hybrid when not successful H' do post :create, params: { taxon_concept: { name_status: 'H' } }, xhr: true + expect(response).to render_template('new_hybrid') end + it 'renders new_synonym when not successful N' do post :create, params: { taxon_concept: { name_status: 'N' } }, xhr: true + expect(response).to render_template('new_n_name') end end describe 'XHR PUT update' do let(:taxon_concept) { create(:taxon_concept) } + context 'when JSON' do it 'responds with 200 when successful' do - put :update, format: 'json', params: { - id: taxon_concept.id, - taxon_concept: { dummy: 'test' } - }, xhr: true + put :update, + format: 'json', + params: { + id: taxon_concept.id, + taxon_concept: { taxonomic_position: '1.1.3' } + }, + xhr: true expect(response).to be_successful end + it 'responds with json error when not successful' do - put :update, format: 'json', params: { - id: taxon_concept.id, - taxon_concept: { taxonomy_id: nil } - }, xhr: true + put :update, + format: 'json', + params: { + id: taxon_concept.id, + taxon_concept: { taxonomy_id: nil } + }, + xhr: true expect(response.parsed_body).to include('errors') end end + context 'when HTML' do it 'redirects to edit when successful' do - put :update, params: { id: taxon_concept.id, taxon_concept: { dummy: 'test' } } + put :update, + params: { + id: taxon_concept.id, + taxon_concept: { taxonomic_position: '1.1.3' } + } + expect(response).to redirect_to(edit_admin_taxon_concept_url(taxon_concept)) end + it 'renders edit when not successful' do - put :update, params: { id: taxon_concept.id, taxon_concept: { taxonomy_id: nil } } + put :update, + params: { + id: taxon_concept.id, + taxon_concept: { taxonomy_id: nil } + } + expect(response).to render_template('edit') end end @@ -103,18 +141,22 @@ describe 'DELETE destroy' do let(:taxon_concept) { create(:taxon_concept) } + it 'redirects after delete' do delete :destroy, params: { id: taxon_concept.id } + expect(response).to redirect_to(admin_taxon_concepts_url) end end describe "DELETE destroy doesn't work for non managers" do login_contributor + let(:taxon_concept) { create(:taxon_concept) } it "redirects to admin root path and doesn't delete" do delete :destroy, params: { id: taxon_concept.id } + expect(response).to redirect_to(admin_root_path) expect(TaxonConcept.where(id: taxon_concept.id).size).to eq(1) end @@ -126,11 +168,13 @@ it 'redirects to root path' do get :index + expect(response).to redirect_to(root_path) end it "redirects to root path and doesn't delete" do delete :destroy, params: { id: taxon_concept.id } + expect(response).to redirect_to(root_path) expect(TaxonConcept.where(id: taxon_concept.id).size).to eq(1) end @@ -142,9 +186,14 @@ taxon_name: create(:taxon_name, scientific_name: 'AAA') ) end + it 'returns properly formatted json' do - get :autocomplete, format: 'json', - params: { search_params: { scientific_name: 'AAA' } }, xhr: true + get :autocomplete, + format: 'json', + params: { + search_params: { scientific_name: 'AAA' } + }, xhr: true + expect(response.body).to have_json_size(1) expect(parse_json(response.body, '0/full_name')).to eq('Aaa') end diff --git a/spec/controllers/admin/taxon_eu_suspensions_controller_spec.rb b/spec/controllers/admin/taxon_eu_suspensions_controller_spec.rb index c2d6ec5f4..6c38809c9 100644 --- a/spec/controllers/admin/taxon_eu_suspensions_controller_spec.rb +++ b/spec/controllers/admin/taxon_eu_suspensions_controller_spec.rb @@ -10,10 +10,13 @@ describe 'GET index' do it 'renders the index template' do get :index, params: { taxon_concept_id: @taxon_concept.id } + expect(response).to render_template('index') end + it 'renders the taxon_concepts_layout' do get :index, params: { taxon_concept_id: @taxon_concept.id } + expect(response).to render_template('layouts/taxon_concepts') end end @@ -21,12 +24,16 @@ describe 'GET new' do it 'renders the new template' do get :new, params: { taxon_concept_id: @taxon_concept.id } + expect(response).to render_template('new') end + it 'assigns @geo_entities (country and territory) with two objects' do - territory = create(:geo_entity, geo_entity_type_id: territory_geo_entity_type.id) - country = create(:geo_entity) + create(:geo_entity, geo_entity_type_id: territory_geo_entity_type.id) + create(:geo_entity) + get :new, params: { taxon_concept_id: @taxon_concept.id } + expect(assigns(:geo_entities).size).to eq(2) end end @@ -36,82 +43,101 @@ before do @eu_decision_type = create(:eu_decision_type) end + it 'redirects to the EU suspensions index' do - post :create, params: { - eu_suspension: { - eu_decision_type_id: @eu_decision_type.id, - start_date: Date.new(2013, 1, 1), - geo_entity_id: create( - :geo_entity, geo_entity_type_id: country_geo_entity_type.id - ) - }, taxon_concept_id: @taxon_concept.id - } + post :create, + params: { + eu_suspension: { + eu_decision_type_id: @eu_decision_type.id, + start_date: Date.new(2013, 1, 1), + geo_entity_id: create( + :geo_entity, geo_entity_type_id: country_geo_entity_type.id + ) + }, taxon_concept_id: @taxon_concept.id + } + expect(response).to redirect_to(admin_taxon_concept_eu_suspensions_url(@taxon_concept.id)) end end context 'when not successful' do it 'renders new' do - post :create, params: { eu_suspension: { dummy: 'test' }, taxon_concept_id: @taxon_concept.id } + post :create, + params: { + eu_suspension: { start_date: nil }, + taxon_concept_id: @taxon_concept.id + } + expect(response).to render_template('new') end end end describe 'GET edit' do - before(:each) do + before do @eu_suspension = create( :eu_suspension, taxon_concept_id: @taxon_concept.id ) end + it 'renders the edit template' do get :edit, params: { id: @eu_suspension.id, taxon_concept_id: @taxon_concept.id } + expect(response).to render_template('edit') end + it 'assigns @geo_entities' do territory = create(:geo_entity, geo_entity_type_id: territory_geo_entity_type.id) + get :edit, params: { id: @eu_suspension.id, taxon_concept_id: @taxon_concept.id } + expect(assigns(:geo_entities)).to include(territory) end end describe 'PUT update' do - before(:each) do + before do @eu_suspension = create( :eu_suspension, taxon_concept_id: @taxon_concept.id ) + @srg_history = create(:srg_history) end context 'when successful' do context 'when eu_decision_type is present' do it 'renders taxon_concepts EU suspensions page' do - put :update, params: { - eu_suspension: { - eu_decision_type_id: create(:eu_decision_type), - geo_entity_id: create( - :geo_entity, geo_entity_type_id: country_geo_entity_type.id - ) - }, id: @eu_suspension.id, taxon_concept_id: @taxon_concept.id - } + put :update, + params: { + eu_suspension: { + eu_decision_type_id: create(:eu_decision_type), + geo_entity_id: create( + :geo_entity, geo_entity_type_id: country_geo_entity_type.id + ) + }, id: @eu_suspension.id, taxon_concept_id: @taxon_concept.id + } + expect(response).to redirect_to( admin_taxon_concept_eu_suspensions_url(@taxon_concept) ) end end + context 'when eu_decision_type is not present' do it 'renders taxon_concepts EU suspensions page' do - put :update, params: { - eu_suspension: { - eu_decision_type_id: nil, - srg_history_id: @srg_history.id, - geo_entity_id: create( - :geo_entity, geo_entity_type_id: country_geo_entity_type.id - ) - }, id: @eu_suspension.id, taxon_concept_id: @taxon_concept.id - } + put :update, + params: { + eu_suspension: { + eu_decision_type_id: nil, + srg_history_id: @srg_history.id, + geo_entity_id: create( + :geo_entity, geo_entity_type_id: country_geo_entity_type.id + ) + }, id: @eu_suspension.id, taxon_concept_id: @taxon_concept.id + } + expect(response).to redirect_to( admin_taxon_concept_eu_suspensions_url(@taxon_concept) ) @@ -122,24 +148,31 @@ context 'when not successful' do context 'when eu_decision_type is present' do it 'renders new' do - put :update, params: { - eu_suspension: { - eu_decision_type_id: create(:eu_decision_type), - geo_entity_id: nil - }, id: @eu_suspension.id, taxon_concept_id: @taxon_concept.id - } + put :update, + params: { + eu_suspension: { + eu_decision_type_id: create(:eu_decision_type), + geo_entity_id: nil + }, id: @eu_suspension.id, taxon_concept_id: @taxon_concept.id + } + expect(response).to render_template('new') end end + context 'when eu_decision_type is not present' do it 'renders new' do - put :update, params: { - eu_suspension: { - eu_decision_type_id: nil, - srg_history_id: @srg_history.id, - geo_entity_id: nil - }, id: @eu_suspension.id, taxon_concept_id: @taxon_concept.id - } + put :update, + params: { + eu_suspension: { + eu_decision_type_id: nil, + srg_history_id: @srg_history.id, + geo_entity_id: nil + }, + id: @eu_suspension.id, + taxon_concept_id: @taxon_concept.id + } + expect(response).to render_template('new') end end @@ -147,25 +180,30 @@ context 'when both eu_decision_type and srg_history are empty' do it 'renders new' do - put :update, params: { - eu_suspension: { - eu_decision_type_id: nil, - srg_history_id: nil, - start_date: nil - }, id: @eu_suspension.id, taxon_concept_id: @taxon_concept.id - } + put :update, + params: { + eu_suspension: { + eu_decision_type_id: nil, + srg_history_id: nil, + start_date: nil + }, + id: @eu_suspension.id, + taxon_concept_id: @taxon_concept.id + } + expect(response).to render_template('new') end end end describe 'DELETE destroy' do - before(:each) do + before do @eu_suspension = create( :eu_suspension, taxon_concept_id: @taxon_concept.id ) end + it 'redirects after delete' do delete :destroy, params: { id: @eu_suspension.id, taxon_concept_id: @taxon_concept.id } expect(response).to redirect_to( @@ -182,16 +220,19 @@ taxon_concept_id: @taxon_concept.id ) end + describe 'GET index' do it 'renders the index template' do get :index, params: { taxon_concept_id: @taxon_concept.id } expect(response).to render_template('index') end + it 'renders the taxon_concepts_layout' do get :index, params: { taxon_concept_id: @taxon_concept.id } expect(response).to render_template('layouts/taxon_concepts') end end + describe 'DELETE destroy' do it 'fails to delete and redirects' do @request.env['HTTP_REFERER'] = admin_taxon_concept_eu_suspensions_url(@taxon_concept) diff --git a/spec/controllers/admin/taxon_listing_changes_controller_spec.rb b/spec/controllers/admin/taxon_listing_changes_controller_spec.rb index d33d1f623..c747d02a8 100644 --- a/spec/controllers/admin/taxon_listing_changes_controller_spec.rb +++ b/spec/controllers/admin/taxon_listing_changes_controller_spec.rb @@ -33,6 +33,7 @@ change_type_id: @addition.id, effective_at: 2.weeks.ago ) + listing_change2 = create( :listing_change, species_listing: @appendix, @@ -40,16 +41,23 @@ change_type_id: @addition.id, effective_at: 1.week.ago ) + get :index, params: { taxon_concept_id: @taxon_concept.id, designation_id: @designation.id } + expect(assigns(:listing_changes)).to eq([ listing_change2, listing_change1 ]) + expect(assigns(:taxon_concept)).to eq @taxon_concept end + it 'renders the index template' do get :index, params: { taxon_concept_id: @taxon_concept.id, designation_id: @designation.id } + expect(response).to render_template('index') end + it 'renders the taxon_concepts_layout' do get :index, params: { taxon_concept_id: @taxon_concept.id, designation_id: @designation.id } + expect(response).to render_template('layouts/taxon_concepts') end end @@ -57,10 +65,13 @@ describe 'GET new' do it 'renders the new template' do get :new, params: { taxon_concept_id: @taxon_concept.id, designation_id: @designation.id } + expect(response).to render_template('new') end + it 'assigns @listing_change' do get :new, params: { taxon_concept_id: @taxon_concept.id, designation_id: @designation.id } + expect(assigns(:listing_change)).not_to be_nil end end @@ -68,21 +79,33 @@ describe 'POST create' do context 'when successful' do it 'redirects to taxon_concept listing_changes page' do - post :create, params: { - listing_change: { - change_type_id: @addition.id, - species_listing_id: @appendix.id, - effective_at: 1.week.ago - }, taxon_concept_id: @taxon_concept.id, designation_id: @designation.id - } + post :create, + params: { + listing_change: { + change_type_id: @addition.id, + species_listing_id: @appendix.id, + effective_at: 1.week.ago + }, + taxon_concept_id: @taxon_concept.id, + designation_id: @designation.id + } + expect(response).to redirect_to( admin_taxon_concept_designation_listing_changes_url(@taxon_concept, @designation) ) end end + it 'renders new when not successful' do taxon_concept = create(:taxon_concept) - post :create, params: { listing_change: { dummy: 'test' }, taxon_concept_id: @taxon_concept.id, designation_id: @designation.id } + + post :create, + params: { + listing_change: { change_type_id: 0 }, + taxon_concept_id: @taxon_concept.id, + designation_id: @designation.id + } + expect(response).to render_template('new') end end @@ -97,12 +120,16 @@ effective_at: 1.week.ago ) end + it 'renders the edit template' do get :edit, params: { id: @listing_change.id, taxon_concept_id: @taxon_concept.id, designation_id: @designation.id } + expect(response).to render_template('edit') end + it 'assigns the listing_change variable' do get :edit, params: { id: @listing_change.id, taxon_concept_id: @taxon_concept.id, designation_id: @designation.id } + expect(assigns(:listing_change)).not_to be_nil end end @@ -119,19 +146,26 @@ annotation_id: @annotation.id ) end + context 'when successful' do it 'redirects to taxon_concept listing_changes page' do - put :update, params: { - listing_change: { - change_type_id: @addition.id, - species_listing_id: @appendix.id, - effective_at: 1.week.ago - }, id: @listing_change.id, taxon_concept_id: @taxon_concept.id, designation_id: @designation.id - } + put :update, + params: { + listing_change: { + change_type_id: @addition.id, + species_listing_id: @appendix.id, + effective_at: 1.week.ago + }, + id: @listing_change.id, + taxon_concept_id: @taxon_concept.id, + designation_id: @designation.id + } + expect(response).to redirect_to( admin_taxon_concept_designation_listing_changes_url(@taxon_concept, @designation) ) end + it 'redirects to eu regulation listing changes page when param is set' do taxon_concept = create(:taxon_concept) eu_designation = create( @@ -158,26 +192,44 @@ effective_at: 1.week.ago, event_id: eu_regulation.id ) - put :update, params: { - listing_change: { - change_type_id: addition.id, - species_listing_id: annex.id, - effective_at: 1.week.ago - }, id: listing_change2.id, taxon_concept_id: taxon_concept.id, designation_id: eu_designation.id, redirect_to_eu_reg: '1' - } + + put :update, + params: { + listing_change: { + change_type_id: addition.id, + species_listing_id: annex.id, + effective_at: 1.week.ago + }, + id: listing_change2.id, + taxon_concept_id: taxon_concept.id, + designation_id: eu_designation.id, + redirect_to_eu_reg: '1' + } + expect(response).to redirect_to( admin_eu_regulation_listing_changes_url(eu_regulation) ) end end + it 'renders edit when not successful' do - put :update, params: { listing_change: { effective_at: nil }, id: @listing_change.id, taxon_concept_id: @taxon_concept.id, designation_id: @designation.id } + put :update, + params: { + listing_change: { effective_at: nil }, + id: @listing_change.id, + taxon_concept_id: @taxon_concept.id, + designation_id: @designation.id + } + expect(response).to render_template('edit') end it 'redirects to index page and removes annotation when fields cleared' do put :update, params: { - id: @listing_change.id, taxon_concept_id: @taxon_concept.id, designation_id: @designation.id, listing_change: { + id: @listing_change.id, + taxon_concept_id: @taxon_concept.id, + designation_id: @designation.id, + listing_change: { annotation_attributes: { 'short_note_en' => '', 'short_note_es' => '', 'short_note_fr' => '', 'full_note_en' => '', @@ -186,11 +238,13 @@ } } } + expect(response).to redirect_to( admin_taxon_concept_designation_listing_changes_url( @taxon_concept, @designation ) ) + expect(@listing_change.reload.annotation).to be_nil end end @@ -205,8 +259,15 @@ effective_at: 1.week.ago ) end + it 'redirects after delete' do - delete :destroy, params: { id: @listing_change.id, taxon_concept_id: @taxon_concept.id, designation_id: @designation.id } + delete :destroy, + params: { + id: @listing_change.id, + taxon_concept_id: @taxon_concept.id, + designation_id: @designation.id + } + expect(response).to redirect_to( admin_taxon_concept_designation_listing_changes_url(@taxon_concept, @designation) ) @@ -214,6 +275,7 @@ end describe 'Authorization for contributors' do login_contributor + let!(:listing_change) do create( :listing_change, @@ -223,23 +285,36 @@ effective_at: 1.week.ago ) end + describe 'GET index' do it 'renders the index template' do get :index, params: { taxon_concept_id: @taxon_concept.id, designation_id: @designation.id } + expect(response).to render_template('index') end + it 'renders the taxon_concepts_layout' do get :index, params: { taxon_concept_id: @taxon_concept.id, designation_id: @designation.id } + expect(response).to render_template('layouts/taxon_concepts') end end describe 'DELETE destroy' do it 'fails to delete and redirects' do - @request.env['HTTP_REFERER'] = admin_taxon_concept_designation_listing_changes_url(@taxon_concept, @designation) - delete :destroy, params: { id: listing_change.id, taxon_concept_id: @taxon_concept.id, designation_id: @designation.id } + @request.env['HTTP_REFERER'] = + admin_taxon_concept_designation_listing_changes_url(@taxon_concept, @designation) + + delete :destroy, + params: { + id: listing_change.id, + taxon_concept_id: @taxon_concept.id, + designation_id: @designation.id + } + expect(response).to redirect_to( admin_taxon_concept_designation_listing_changes_url(@taxon_concept, @designation) ) + expect(ListingChange.find(listing_change.id)).not_to be_nil end end diff --git a/spec/controllers/admin/taxon_quotas_controller_spec.rb b/spec/controllers/admin/taxon_quotas_controller_spec.rb index 7259a4f0a..5390877bd 100644 --- a/spec/controllers/admin/taxon_quotas_controller_spec.rb +++ b/spec/controllers/admin/taxon_quotas_controller_spec.rb @@ -12,10 +12,13 @@ describe 'GET index' do it 'renders the index template' do get :index, params: { taxon_concept_id: @taxon_concept.id } + expect(response).to render_template('index') end + it 'renders the taxon_concepts_layout' do get :index, params: { taxon_concept_id: @taxon_concept.id } + expect(response).to render_template('layouts/taxon_concepts') end end @@ -23,12 +26,16 @@ describe 'GET new' do it 'renders the new template' do get :new, params: { taxon_concept_id: @taxon_concept.id } + expect(response).to render_template('new') end + it 'assigns @geo_entities (country and territory) with two objects' do geo_entity_type_t = create(:geo_entity_type, name: 'TERRITORY') territory = create(:geo_entity, geo_entity_type_id: geo_entity_type_t.id) + get :new, params: { taxon_concept_id: @taxon_concept.id } + expect(assigns(:geo_entities).size).to eq(2) end end @@ -36,27 +43,39 @@ describe 'POST create' do context 'when successful' do it 'renders index' do - post :create, params: { - quota: { - quota: 1, - unit_id: @unit.id, - publication_date: 1.week.ago, - geo_entity_id: @geo_entity.id - }, taxon_concept_id: @taxon_concept.id - } + post :create, + params: { + quota: { + quota: 1, + unit_id: @unit.id, + publication_date: 1.week.ago, + geo_entity_id: @geo_entity.id + }, taxon_concept_id: @taxon_concept.id + } + expect(response).to redirect_to( admin_taxon_concept_quotas_url(@taxon_concept) ) end end + it 'renders new when not successful' do - post :create, params: { quota: { dummy: 'test' }, taxon_concept_id: @taxon_concept.id } + post :create, + params: { + quota: { + unit_id: 0, + publication_date: 1.week.ago, + geo_entity_id: @geo_entity.id + }, + taxon_concept_id: @taxon_concept.id + } + expect(response).to render_template('new') end end describe 'GET edit' do - before(:each) do + before do @quota = create( :quota, unit_id: @unit.id, @@ -64,20 +83,24 @@ geo_entity_id: @geo_entity.id ) end + it 'renders the edit template' do get :edit, params: { id: @quota.id, taxon_concept_id: @taxon_concept.id } expect(response).to render_template('edit') end + it 'assigns @geo_entities (country and territory) with two objects' do geo_entity_type_t = create(:geo_entity_type, name: 'TERRITORY') territory = create(:geo_entity, geo_entity_type_id: geo_entity_type_t.id) + get :edit, params: { id: @quota.id, taxon_concept_id: @taxon_concept.id } + expect(assigns(:geo_entities).size).to eq(2) end end describe 'PUT update' do - before(:each) do + before do @quota = create( :quota, unit_id: @unit.id, @@ -88,11 +111,13 @@ context 'when successful' do it 'renders taxon_concepts quotas page' do - put :update, params: { - quota: { - publication_date: 1.week.ago - }, id: @quota.id, taxon_concept_id: @taxon_concept.id - } + put :update, + params: { + quota: { publication_date: 1.week.ago }, + id: @quota.id, + taxon_concept_id: @taxon_concept.id + } + expect(response).to redirect_to( admin_taxon_concept_quotas_url(@taxon_concept) ) @@ -100,17 +125,19 @@ end it 'renders new when not successful' do - put :update, params: { - quota: { - publication_date: nil - }, id: @quota.id, taxon_concept_id: @taxon_concept.id - } + put :update, + params: { + quota: { publication_date: 'not a date' }, + id: @quota.id, + taxon_concept_id: @taxon_concept.id + } + expect(response).to render_template('new') end end describe 'DELETE destroy' do - before(:each) do + before do @quota = create( :quota, unit_id: @unit.id, @@ -118,8 +145,10 @@ geo_entity_id: @geo_entity.id ) end + it 'redirects after delete' do delete :destroy, params: { id: @quota.id, taxon_concept_id: @taxon_concept.id } + expect(response).to redirect_to( admin_taxon_concept_quotas_url(@taxon_concept) ) @@ -128,6 +157,7 @@ describe 'Authorization for contributors' do login_contributor + let!(:quota) do create( :quota, @@ -136,23 +166,31 @@ geo_entity_id: @geo_entity.id ) end + describe 'GET index' do it 'renders the index template' do get :index, params: { taxon_concept_id: @taxon_concept.id } + expect(response).to render_template('index') end + it 'renders the taxon_concepts_layout' do get :index, params: { taxon_concept_id: @taxon_concept.id } + expect(response).to render_template('layouts/taxon_concepts') end end + describe 'DELETE destroy' do it 'fails to delete and redirects' do @request.env['HTTP_REFERER'] = admin_taxon_concept_quotas_url(@taxon_concept) + delete :destroy, params: { id: quota.id, taxon_concept_id: @taxon_concept.id } + expect(response).to redirect_to( admin_taxon_concept_quotas_url(@taxon_concept) ) + expect(Quota.find(quota.id)).not_to be_nil end end diff --git a/spec/controllers/trade/sandbox_shipments_controller_spec.rb b/spec/controllers/trade/sandbox_shipments_controller_spec.rb index fe1c9fd06..25ad9e22b 100644 --- a/spec/controllers/trade/sandbox_shipments_controller_spec.rb +++ b/spec/controllers/trade/sandbox_shipments_controller_spec.rb @@ -6,20 +6,26 @@ let(:annual_report_upload) do aru = build(:annual_report_upload) aru.save(validate: false) + aru end + let(:sandbox_klass) do Trade::SandboxTemplate.ar_klass(annual_report_upload.sandbox.table_name) end - before(:each) do + + before do @genus = create_cites_eu_genus( taxon_name: create(:taxon_name, scientific_name: 'Acipenser') ) + @species = create_cites_eu_species( taxon_name: create(:taxon_name, scientific_name: 'baerii'), parent_id: @genus.id ) + @shipment = sandbox_klass.create(taxon_name: 'Acipenser baerii', appendix: 'I', year: 2016) + @validation_error = create( :validation_error, annual_report_upload_id: annual_report_upload.id, @@ -31,27 +37,61 @@ error_count: 1 ) end + describe 'PUT update' do - it 'should return success when taxon_name not set' do - put :update, params: { annual_report_upload_id: annual_report_upload.id, id: @shipment.id, sandbox_shipment: { taxon_name: nil, accepted_taxon_name: nil }, format: :json } + it 'returns success when taxon_name not set' do + put :update, + params: { + annual_report_upload_id: annual_report_upload.id, + id: @shipment.id, + sandbox_shipment: { taxon_name: nil, accepted_taxon_name: nil }, + format: :json + } + + expect(response).to be_successful expect(response.body).to be_blank end - it 'should return success when taxon_name does not exist' do - put :update, params: { annual_report_upload_id: annual_report_upload.id, id: @shipment.id, sandbox_shipment: { taxon_name: 'Acipenser foobarus' }, format: :json } + + it 'returns success when taxon_name does not exist' do + put :update, + params: { + annual_report_upload_id: + annual_report_upload.id, + id: @shipment.id, + sandbox_shipment: { taxon_name: 'Acipenser foobarus' }, + format: :json + } + + expect(response).to be_successful expect(response.body).to be_blank end end describe 'DELETE destroy' do - it 'should return success' do - delete :destroy, params: { annual_report_upload_id: annual_report_upload.id, id: @shipment.id, format: :json } + it 'returns success' do + delete :destroy, + params: { + annual_report_upload_id: annual_report_upload.id, + id: @shipment.id, + format: :json + } + + expect(response).to be_successful expect(response.body).to be_blank end end describe 'POST update_batch' do - it 'should return success' do - post :update_batch, params: { annual_report_upload_id: annual_report_upload.id, validation_error_id: @validation_error.id, updates: { appendix: 'II' }, format: :json } + it 'returns success' do + post :update_batch, + params: { + annual_report_upload_id: annual_report_upload.id, + validation_error_id: @validation_error.id, + updates: { appendix: 'II' }, + format: :json + } + + expect(response).to be_successful expect(response.body).to be_blank expect(sandbox_klass.where(taxon_name: @species.full_name, appendix: 'I').count(true)).to eq(0) expect(sandbox_klass.where(taxon_name: @species.full_name, appendix: 'II').count(true)).to eq(1) @@ -59,8 +99,14 @@ end describe 'POST destroy_batch' do - it 'should return success' do - post :destroy_batch, params: { annual_report_upload_id: annual_report_upload.id, validation_error_id: @validation_error.id, format: :json } + it 'returns success' do + post :destroy_batch, + params: { + annual_report_upload_id: annual_report_upload.id, + validation_error_id: @validation_error.id, + format: :json + } + expect(response.body).to be_blank expect(sandbox_klass.where(taxon_concept_id: @species.id).count(true)).to eq(0) end diff --git a/spec/public/downloads/cites_listings/.gitkeep b/spec/public/downloads/cites_listings/.gitkeep deleted file mode 100644 index e69de29bb..000000000 diff --git a/spec/public/downloads/common_names/.gitkeep b/spec/public/downloads/common_names/.gitkeep deleted file mode 100644 index e69de29bb..000000000 diff --git a/spec/public/downloads/documents/.gitkeep b/spec/public/downloads/documents/.gitkeep deleted file mode 100644 index e69de29bb..000000000 diff --git a/spec/public/downloads/orphaned_taxon_concepts/.gitkeep b/spec/public/downloads/orphaned_taxon_concepts/.gitkeep deleted file mode 100644 index e69de29bb..000000000 diff --git a/spec/public/downloads/species_reference_output/.gitkeep b/spec/public/downloads/species_reference_output/.gitkeep deleted file mode 100644 index e69de29bb..000000000 diff --git a/spec/public/downloads/standard_reference_output/.gitkeep b/spec/public/downloads/standard_reference_output/.gitkeep deleted file mode 100644 index e69de29bb..000000000 diff --git a/spec/public/downloads/synonyms_and_trade_names/.gitkeep b/spec/public/downloads/synonyms_and_trade_names/.gitkeep deleted file mode 100644 index e69de29bb..000000000 diff --git a/spec/public/downloads/taxon_concepts_names/.gitkeep b/spec/public/downloads/taxon_concepts_names/.gitkeep deleted file mode 100644 index e69de29bb..000000000 diff --git a/spec/services/document_batch_spec.rb b/spec/services/document_batch_spec.rb index f5de0e267..30127253f 100644 --- a/spec/services/document_batch_spec.rb +++ b/spec/services/document_batch_spec.rb @@ -5,31 +5,42 @@ context 'when invalid' do subject do DocumentBatch.new( - documents_attributes: { - '0' => { type: 'Document' } - }, + documents_attributes: [ + { type: 'Document' } + ], files: [ - filename: Rack::Test::UploadedFile.new(Rails.root.join('spec/support/annual_report_upload_exporter.csv').to_s) + filename: Rack::Test::UploadedFile.new( + Rails.root.join( + 'spec/support/annual_report_upload_exporter.csv' + ).to_s + ) ] ) end + specify { expect(subject.save).to be_falsey } - specify { expect { subject.save }.not_to change { Document.count } } + specify { expect { subject.save }.not_to change(Document, :count) } end + context 'when valid' do subject do DocumentBatch.new( date: Date.today, - documents_attributes: { - '0' => { type: 'Document' } - }, + documents_attributes: [ + { type: 'Document' } + ], files: [ - Rack::Test::UploadedFile.new(Rails.root.join('spec/support/annual_report_upload_exporter.csv').to_s) + Rack::Test::UploadedFile.new( + Rails.root.join( + 'spec/support/annual_report_upload_exporter.csv' + ).to_s + ) ] ) end + specify { expect(subject.save).to be_truthy } - specify { expect { subject.save }.to change { Document.count }.by(1) } + specify { expect { subject.save }.to change(Document, :count).by(1) } end end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 7c3aa80af..3565585a4 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,3 +1,5 @@ +# This file is copied to spec/ when you run 'rails generate rspec:install' + require 'simplecov' require 'coveralls' @@ -14,8 +16,9 @@ add_group 'Serializers', 'app/serializers' end -# This file is copied to spec/ when you run 'rails generate rspec:install' -ENV['RAILS_ENV'] ||= 'test' +# CHANGED +ENV['RAILS_ENV'] = 'test' + require File.expand_path('../../config/environment', __FILE__) require 'rspec/rails' require 'sidekiq/testing'