From 6d66070b65645f5c820c3a76889431d7759351ec Mon Sep 17 00:00:00 2001 From: Simon Tharby Date: Tue, 17 Dec 2024 11:42:40 +0000 Subject: [PATCH 01/63] [TAN-2538] Remove seeding of initiatives --- .../multi_tenancy/db/seeds/initiatives.rb | 56 ------------------- .../multi_tenancy/db/seeds/runner.rb | 16 +----- 2 files changed, 3 insertions(+), 69 deletions(-) delete mode 100644 back/engines/commercial/multi_tenancy/db/seeds/initiatives.rb diff --git a/back/engines/commercial/multi_tenancy/db/seeds/initiatives.rb b/back/engines/commercial/multi_tenancy/db/seeds/initiatives.rb deleted file mode 100644 index 5cbd84219395..000000000000 --- a/back/engines/commercial/multi_tenancy/db/seeds/initiatives.rb +++ /dev/null @@ -1,56 +0,0 @@ -# frozen_string_literal: true - -require_relative 'base' - -module MultiTenancy - module Seeds - class Initiatives < Base - def run - runner.num_initiatives.times do - created_at = Faker::Date.between(from: Tenant.current.created_at, to: Time.zone.now) - initiative = Initiative.create!( - title_multiloc: runner.create_for_some_locales { Faker::Lorem.sentence[0...80] }, - body_multiloc: runner.rand_description_multiloc, - author: User.offset(rand(User.count)).first, - publication_status: 'published', - published_at: Faker::Date.between(from: created_at, to: Time.zone.now), - created_at: created_at, - location_point: rand(3) == 0 ? nil : "POINT(#{runner.map_center[1] + (((rand * 2) - 1) * runner.map_offset)} #{runner.map_center[0] + (((rand * 2) - 1) * runner.map_offset)})", - location_description: rand(2) == 0 ? nil : Faker::Address.street_address, - header_bg: rand(25) == 0 ? nil : Rails.root.join("spec/fixtures/image#{rand(20)}.png").open, - topics: Array.new(rand(3)) { rand(Topic.count) }.uniq.map { |offset| Topic.offset(offset).first }, - areas: Array.new(rand(3)) { rand(Area.count) }.uniq.map { |offset| Area.offset(offset).first }, - assignee: rand(5) == 0 ? User.admin.sample : nil - ) - - [1, 1, 2, 2, 3][rand(5)].times do |_i| - initiative.initiative_images.create!(image: Rails.root.join("spec/fixtures/image#{rand(20)}.png").open) - end - if rand(5) == 0 - rand(1..3).times do - initiative.initiative_files.create!(runner.generate_file_attributes) - end - end - - User.all.each do |u| - r = rand(5) - if r < 2 - Reaction.create!(reactable: initiative, user: u, mode: 'up', - created_at: Faker::Date.between(from: initiative.published_at, to: Time.zone.now)) - end - end - - rand(5).times do - initiative.official_feedbacks.create!( - body_multiloc: runner.rand_description_multiloc, - author_multiloc: runner.create_for_some_locales { Faker::FunnyName.name }, - user: User.admin.sample - ) - end - - runner.create_comment_tree(initiative, nil) - end - end - end - end -end diff --git a/back/engines/commercial/multi_tenancy/db/seeds/runner.rb b/back/engines/commercial/multi_tenancy/db/seeds/runner.rb index 4ff5ab51c17d..17cc1b61d41a 100644 --- a/back/engines/commercial/multi_tenancy/db/seeds/runner.rb +++ b/back/engines/commercial/multi_tenancy/db/seeds/runner.rb @@ -16,7 +16,6 @@ require_relative 'followers' require_relative 'groups' require_relative 'ideas' -require_relative 'initiatives' require_relative 'internal_comments' require_relative 'invites' require_relative 'permissions' @@ -44,20 +43,17 @@ class Runner small: { num_users: 5, num_projects: 1, - num_ideas: 4, - num_initiatives: 3 + num_ideas: 4 }, medium: { num_users: 10, num_projects: 5, - num_ideas: 15, - num_initiatives: 10 + num_ideas: 15 }, large: { num_users: 50, num_projects: 20, - num_ideas: 100, - num_initiatives: 60 + num_ideas: 100 } }.freeze @@ -111,7 +107,6 @@ def seed_localhost_tenant MultiTenancy::Seeds::ProjectFolders.new(runner: self).run MultiTenancy::Seeds::Projects.new(runner: self).run MultiTenancy::Seeds::Ideas.new(runner: self).run - MultiTenancy::Seeds::Initiatives.new(runner: self).run MultiTenancy::Seeds::InternalComments.new(runner: self).run InitiativeStatusService.new.automated_transitions! @@ -150,11 +145,6 @@ def num_ideas SEED_SIZES.dig(seed_size, :num_ideas) end - # @return [Integer] Number of iniatives to be seeded - def num_initiatives - SEED_SIZES.dig(seed_size, :num_initiatives) - end - # @return [Float] default map center def map_center MAP_CENTER From 3b450c999901879d2f5f3023ea7d687637cefb40 Mon Sep 17 00:00:00 2001 From: Simon Tharby Date: Tue, 17 Dec 2024 11:56:01 +0000 Subject: [PATCH 02/63] [TAN-2538] Remove web_api/v1 controller + routes - see what breaks --- .../web_api/v1/initiatives_controller.rb | 244 ------------------ back/config/routes.rb | 16 -- 2 files changed, 260 deletions(-) delete mode 100644 back/app/controllers/web_api/v1/initiatives_controller.rb diff --git a/back/app/controllers/web_api/v1/initiatives_controller.rb b/back/app/controllers/web_api/v1/initiatives_controller.rb deleted file mode 100644 index 9049aea14cb8..000000000000 --- a/back/app/controllers/web_api/v1/initiatives_controller.rb +++ /dev/null @@ -1,244 +0,0 @@ -# frozen_string_literal: true - -class WebApi::V1::InitiativesController < ApplicationController - include BlockingProfanity - - before_action :set_initiative, only: %i[show update destroy allowed_transitions accept_cosponsorship_invite] - skip_before_action :authenticate_user - skip_after_action :verify_authorized, only: %i[index_xlsx index_initiative_markers filter_counts] - - rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized - - def index - initiatives = InitiativesFinder.new( - params, - current_user: current_user, - scope: policy_scope(Initiative), - includes: %i[author assignee topics areas] - ).find_records - initiatives = paginate SortByParamsService.new.sort_initiatives(initiatives, params, current_user) - render json: linked_json(initiatives, WebApi::V1::InitiativeSerializer, serialization_options_for(initiatives)) - end - - def index_initiative_markers - initiatives = InitiativesFinder.new( - params, - current_user: current_user, - scope: policy_scope(Initiative) - ).find_records - initiatives = paginate SortByParamsService.new.sort_initiatives(initiatives, params, current_user) - render json: linked_json(initiatives, WebApi::V1::PostMarkerSerializer, params: jsonapi_serializer_params) - end - - def index_xlsx - authorize :initiative, :index_xlsx? - initiatives = InitiativesFinder.new( - params, - current_user: current_user, - scope: policy_scope(Initiative).where(publication_status: 'published'), - includes: %i[author cosponsors initiative_status topics areas] - ).find_records - initiatives = SortByParamsService.new.sort_initiatives(initiatives, params, current_user) - - I18n.with_locale(current_user&.locale) do - xlsx = XlsxService.new.generate_initiatives_xlsx( - initiatives, - view_private_attributes: policy(User).view_private_attributes? - ) - - xlsx_content_type = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' - send_data(xlsx, type: xlsx_content_type, filename: 'initiatives.xlsx') - end - end - - def filter_counts - initiatives = policy_scope(Initiative) - search_last_names = !UserDisplayNameService.new(AppConfiguration.instance, current_user).restricted? - initiatives = PostsFilteringService.new.apply_common_initiative_index_filters( - initiatives, params, - search_last_names - ) - counts = { - 'initiative_status_id' => {}, - 'area_id' => {}, - 'topic_id' => {} - } - attributes = %w[initiative_status_id area_id topic_id] - initiatives - .joins('FULL OUTER JOIN initiatives_topics ON initiatives_topics.initiative_id = initiatives.id') - .joins('FULL OUTER JOIN areas_initiatives ON areas_initiatives.initiative_id = initiatives.id') - .joins('FULL OUTER JOIN initiative_initiative_statuses ON initiative_initiative_statuses.initiative_id = initiatives.id') - .select('initiative_initiative_statuses.initiative_status_id, areas_initiatives.area_id, initiatives_topics.topic_id, COUNT(DISTINCT(initiatives.id)) as count') - .reorder(nil) # Avoids SQL error on GROUP BY when a search string is used - .group('GROUPING SETS (initiative_initiative_statuses.initiative_status_id, areas_initiatives.area_id, initiatives_topics.topic_id)') - .each do |record| - attributes.each do |attribute| - id = record.send attribute - counts[attribute][id] = record.count if id - end - end - counts['total'] = initiatives.reorder(nil).distinct.count # reorder(nil) avoids SQL error on SELECT DISTINCT when a search string is used - render json: raw_json(counts) - end - - def show - render json: WebApi::V1::InitiativeSerializer.new( - @initiative, - params: jsonapi_serializer_params, - include: %i[author cosponsors topics areas user_reaction initiative_images] - ).serializable_hash - end - - def by_slug - @initiative = Initiative.find_by!(slug: params[:slug]) - authorize @initiative - show - end - - def create - service = SideFxInitiativeService.new - - @initiative = Initiative.new(permitted_attributes(Initiative)) - @initiative.author ||= current_user - - service.before_create(@initiative, current_user) - - authorize @initiative - if anonymous_not_allowed? - render json: { errors: { base: [{ error: :anonymous_participation_not_allowed }] } }, status: :unprocessable_entity - return - end - verify_profanity @initiative - - save_options = {} - save_options[:context] = :publication if params.dig(:initiative, :publication_status) == 'published' - ActiveRecord::Base.transaction do - if @initiative.save(**save_options) - service.after_create(@initiative, current_user) - render json: WebApi::V1::InitiativeSerializer.new( - @initiative.reload, - params: jsonapi_serializer_params, - include: %i[author cosponsors topics areas user_reaction initiative_images] - ).serializable_hash, status: :created - else - render json: { errors: @initiative.errors.details }, status: :unprocessable_entity - end - end - end - - def update - service = SideFxInitiativeService.new - - cosponsor_ids = @initiative.cosponsors.map(&:id) - initiative_params = permitted_attributes(@initiative) - @initiative.assign_attributes(initiative_params) - remove_image_if_requested!(@initiative, initiative_params, :header_bg) - - authorize @initiative - if anonymous_not_allowed? - render json: { errors: { base: [{ error: :anonymous_participation_not_allowed }] } }, status: :unprocessable_entity - return - end - verify_profanity @initiative - - service.before_update(@initiative, current_user) - - save_options = {} - save_options[:context] = :publication if params.dig(:initiative, :publication_status) == 'published' - saved = nil - ActiveRecord::Base.transaction do - saved = @initiative.save(**save_options) - if saved - service.after_update(@initiative, current_user, cosponsor_ids) - end - end - - # Keeping `render` outside of the transaction is better anyway. - # Additionally, if we wouldn't do it here, we're running into an issue - # where carrierwave is not storing the actual header_bg file on the - # filesystem. The root cause it not exactly clear. - if saved - render json: WebApi::V1::InitiativeSerializer.new( - @initiative.reload, - params: jsonapi_serializer_params, - include: %i[author cosponsors topics areas user_reaction initiative_images] - ).serializable_hash, status: :ok - else - render json: { errors: @initiative.errors.details }, status: :unprocessable_entity - end - end - - def destroy - service = SideFxInitiativeService.new - - initiative = @initiative.destroy - if initiative.destroyed? - service.after_destroy(initiative, current_user) - head :ok - else - head :internal_server_error - end - end - - def accept_cosponsorship_invite - @cosponsors_initiative = @initiative.cosponsors_initiatives.find_by(user_id: current_user.id) - - if @cosponsors_initiative.update(status: 'accepted') - SideFxInitiativeService.new.after_accept_cosponsorship_invite(@cosponsors_initiative, current_user) - - render json: WebApi::V1::InitiativeSerializer.new( - @initiative.reload, - params: jsonapi_serializer_params, - include: %i[author cosponsors topics areas user_reaction initiative_images] - ).serializable_hash, status: :ok - else - render json: { errors: @initiative.errors.details }, status: :unprocessable_entity - end - end - - def allowed_transitions - authorize @initiative - render json: raw_json(InitiativeStatusService.new.allowed_transitions(@initiative)) - end - - private - - # renders errors in the new HookForm format - def render_profanity_blocked(exception) - errors = exception.violating_attributes.index_with { [{ error: :includes_banned_words }] } - render json: { errors: errors }, status: :unprocessable_entity - end - - def set_initiative - @initiative = Initiative.find params[:id] - authorize @initiative - end - - def serialization_options_for(initiatives) - default_params = jsonapi_serializer_params - - if current_user - reactions = current_user.reactions.where( - reactable_id: initiatives.pluck(:id), - reactable_type: 'Initiative' - ).index_by(&:reactable_id) - user_followers = current_user.follows - .where(followable_type: 'Initiative') - .group_by do |follower| - [follower.followable_id, follower.followable_type] - end - user_followers ||= {} - { params: default_params.merge(vbii: reactions, user_followers: user_followers), include: %i[author cosponsors user_reaction initiative_images assignee] } - else - { params: default_params, include: %i[author cosponsors initiative_images] } - end - end - - def display_names_restricted? - UserDisplayNameService.new(Tenant.current, current_user).restricted? - end - - def anonymous_not_allowed? - params.dig('initiative', 'anonymous') && !AppConfiguration.instance.settings.dig('initiatives', 'allow_anonymous_participation') - end -end diff --git a/back/config/routes.rb b/back/config/routes.rb index 6674a5f89cd9..e8d1d8fe4c8a 100644 --- a/back/config/routes.rb +++ b/back/config/routes.rb @@ -73,22 +73,6 @@ get :similarities, on: :member end - resources :initiatives, - concerns: %i[reactable spam_reportable post followable], - defaults: { reactable: 'Initiative', spam_reportable: 'Initiative', post: 'Initiative', followable: 'Initiative' } do - resources :images, defaults: { container_type: 'Initiative' } - resources :files, defaults: { container_type: 'Initiative' } - - resources :initiative_status_changes, shallow: true, except: %i[update destroy] - - get :as_xlsx, on: :collection, action: 'index_xlsx' - get 'by_slug/:slug', on: :collection, to: 'initiatives#by_slug' - get :as_markers, on: :collection, action: 'index_initiative_markers' - get :filter_counts, on: :collection - get :allowed_transitions, on: :member - patch :accept_cosponsorship_invite, on: :member - end - resources :background_jobs, only: %i[index] resources :idea_statuses do From 7952f5508c981eb94a473584d6cf6bb336eaf16b Mon Sep 17 00:00:00 2001 From: Simon Tharby Date: Tue, 17 Dec 2024 12:17:57 +0000 Subject: [PATCH 03/63] [TAN-2538] Remove _status_changes_ & _statuses_ controllers & obviosuly related specs --- .../initiative_status_changes_controller.rb | 83 -- .../v1/initiative_statuses_controller.rb | 22 - .../spec/acceptance/initiatives_spec.rb | 74 -- .../acceptance/initiative_comments_spec.rb | 359 -------- back/spec/acceptance/initiative_files_spec.rb | 76 -- .../spec/acceptance/initiative_images_spec.rb | 87 -- .../initiative_official_feedbacks_spec.rb | 132 --- .../acceptance/initiative_reactions_spec.rb | 148 ---- .../acceptance/initiative_spam_report_spec.rb | 84 -- .../initiative_status_changes_spec.rb | 227 ----- .../acceptance/initiative_statuses_spec.rb | 33 - back/spec/acceptance/initiatives_spec.rb | 796 ------------------ 12 files changed, 2121 deletions(-) delete mode 100644 back/app/controllers/web_api/v1/initiative_status_changes_controller.rb delete mode 100644 back/app/controllers/web_api/v1/initiative_statuses_controller.rb delete mode 100644 back/engines/commercial/flag_inappropriate_content/spec/acceptance/initiatives_spec.rb delete mode 100644 back/spec/acceptance/initiative_comments_spec.rb delete mode 100644 back/spec/acceptance/initiative_files_spec.rb delete mode 100644 back/spec/acceptance/initiative_images_spec.rb delete mode 100644 back/spec/acceptance/initiative_official_feedbacks_spec.rb delete mode 100644 back/spec/acceptance/initiative_reactions_spec.rb delete mode 100644 back/spec/acceptance/initiative_spam_report_spec.rb delete mode 100644 back/spec/acceptance/initiative_status_changes_spec.rb delete mode 100644 back/spec/acceptance/initiative_statuses_spec.rb delete mode 100644 back/spec/acceptance/initiatives_spec.rb diff --git a/back/app/controllers/web_api/v1/initiative_status_changes_controller.rb b/back/app/controllers/web_api/v1/initiative_status_changes_controller.rb deleted file mode 100644 index f67d986c2141..000000000000 --- a/back/app/controllers/web_api/v1/initiative_status_changes_controller.rb +++ /dev/null @@ -1,83 +0,0 @@ -# frozen_string_literal: true - -class WebApi::V1::InitiativeStatusChangesController < ApplicationController - before_action :set_initiative, only: %i[index create] - before_action :set_change, only: %i[show] - skip_before_action :authenticate_user - - def index - @changes = policy_scope(InitiativeStatusChange) - .where(initiative: @initiative) - .order(created_at: :desc) - @changes = paginate @changes - - render json: linked_json(@changes, WebApi::V1::InitiativeStatusChangeSerializer, params: jsonapi_serializer_params) - end - - def show - render json: WebApi::V1::InitiativeStatusChangeSerializer.new( - @change, - params: jsonapi_serializer_params - ).serializable_hash - end - - def create - attributes = status_change_params.to_h - if attributes[:initiative_status_id] == @initiative.initiative_status_id - skip_authorization - render json: { errors: { base: [{ error: 'initiative_status_transition_without_change' }] } }, status: :unprocessable_entity - return - end - if attributes[:official_feedback_attributes].present? # If not nil nor empty hash - attributes[:official_feedback_attributes].merge! post_id: @initiative.id, post_type: 'Initiative' - else - attributes.except! :official_feedback_attributes - end - @change = InitiativeStatusChange.new attributes - @change.initiative = @initiative - @change.user ||= current_user - authorize @change - SideFxInitiativeStatusChangeService.new.before_create @change, current_user - if InitiativeStatusService.new.transition_allowed?( - @initiative, - @initiative.initiative_status, - @change.initiative_status, - with_feedback: attributes[:official_feedback_attributes].present? || attributes[:official_feedback_id] - ) - if @change.save - SideFxInitiativeStatusChangeService.new.after_create @change, current_user - render json: WebApi::V1::InitiativeStatusChangeSerializer.new( - @change, - params: jsonapi_serializer_params - ).serializable_hash, status: :created - else - render json: { errors: @change.errors.details }, status: :unprocessable_entity - end - else - render json: { errors: { base: [{ error: 'initiative_status_transition_not_allowed' }] } }, status: :unprocessable_entity - end - end - - private - - def set_change - @change = InitiativeStatusChange.find params[:id] - authorize @change - end - - def set_initiative - @initiative = Initiative.find params[:initiative_id] - end - - def status_change_params - params.require(:initiative_status_change).permit( - :initiative_status_id, - :user_id, - :official_feedback_id, - official_feedback_attributes: [ - body_multiloc: CL2_SUPPORTED_LOCALES, - author_multiloc: CL2_SUPPORTED_LOCALES - ] - ) - end -end diff --git a/back/app/controllers/web_api/v1/initiative_statuses_controller.rb b/back/app/controllers/web_api/v1/initiative_statuses_controller.rb deleted file mode 100644 index 76398d7154c6..000000000000 --- a/back/app/controllers/web_api/v1/initiative_statuses_controller.rb +++ /dev/null @@ -1,22 +0,0 @@ -# frozen_string_literal: true - -class WebApi::V1::InitiativeStatusesController < ApplicationController - before_action :set_initiative_status, only: :show - skip_before_action :authenticate_user - - def index - @initiative_statuses = policy_scope(InitiativeStatus).order(:ordering) - render json: WebApi::V1::InitiativeStatusSerializer.new(@initiative_statuses, params: jsonapi_serializer_params).serializable_hash - end - - def show - render json: WebApi::V1::InitiativeStatusSerializer.new(@initiative_status, params: jsonapi_serializer_params).serializable_hash - end - - private - - def set_initiative_status - @initiative_status = InitiativeStatus.find(params[:id]) - authorize @initiative_status - end -end diff --git a/back/engines/commercial/flag_inappropriate_content/spec/acceptance/initiatives_spec.rb b/back/engines/commercial/flag_inappropriate_content/spec/acceptance/initiatives_spec.rb deleted file mode 100644 index 428e736a6999..000000000000 --- a/back/engines/commercial/flag_inappropriate_content/spec/acceptance/initiatives_spec.rb +++ /dev/null @@ -1,74 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' -require 'rspec_api_documentation/dsl' - -# TODO: cleanup-after-proposals-migration -resource 'Initiatives' do - before do - header 'Content-Type', 'application/json' - @user = create(:user) - header_token_for @user - - create(:initiative_status, code: 'proposed') - end - - post 'web_api/v1/initiatives' do - with_options scope: :initiative do - parameter :publication_status - parameter :title_multiloc - parameter :body_multiloc - parameter :location_description - end - - let(:initiative) { build(:initiative) } - let(:publication_status) { 'published' } - let(:title_multiloc) { initiative.title_multiloc } - let(:body_multiloc) { initiative.body_multiloc } - - example 'Toxicity detection job is enqueued when creating an initiative', document: false do - SettingsService.new.activate_feature! 'moderation' - SettingsService.new.activate_feature! 'flag_inappropriate_content' - expect do - do_request - end.to have_enqueued_job(ToxicityDetectionJob) - end - end - - patch 'web_api/v1/initiatives/:id' do - before do - SettingsService.new.activate_feature! 'moderation' - SettingsService.new.activate_feature! 'flag_inappropriate_content' - @initiative = create(:initiative, author: @user) - end - - with_options scope: :initiative do - parameter :title_multiloc - parameter :body_multiloc - parameter :location_description - parameter :topic_ids - end - - let(:id) { @initiative.id } - - describe do - let(:location_description) { 'Watkins Road 8' } - - example 'Toxicity detection job is enqueued when updating an initiative\'s title', document: false do - expect do - do_request - end.to have_enqueued_job(ToxicityDetectionJob).with(@initiative, attributes: [:location_description]) - end - end - - describe do - let(:topic_ids) { [create(:topic).id] } - - example 'No toxicity detection job is enqueued when updating initiative attributes without text', document: false do - expect do - do_request - end.not_to have_enqueued_job(ToxicityDetectionJob) - end - end - end -end diff --git a/back/spec/acceptance/initiative_comments_spec.rb b/back/spec/acceptance/initiative_comments_spec.rb deleted file mode 100644 index e883065f17f5..000000000000 --- a/back/spec/acceptance/initiative_comments_spec.rb +++ /dev/null @@ -1,359 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' -require 'rspec_api_documentation/dsl' - -# TODO: cleanup-after-proposals-migration -resource 'Comments' do - explanation 'Comments permit users to have discussions about content (i.e. ideas).' - - before do - header 'Content-Type', 'application/json' - @initiative = create(:initiative) - end - - get 'web_api/v1/initiatives/:initiative_id/comments' do - with_options scope: :page do - parameter :number, 'Page number' - parameter :size, 'Number of top-level comments per page. The response will include 2 to 5 child comments per top-level comment, so expect to receive more' - end - parameter :sort, 'Either new, -new, likes_count or -likes_count. Defaults to -new. Only applies to the top-level comments, children are always returned chronologically.' - - describe do - before do - @c1 = create(:comment, post: @initiative) - @c2 = create(:comment, post: @initiative) - @c1sub1 = create(:comment, parent: @c2, post: @initiative) - @c1sub2 = create(:comment, parent: @c2, post: @initiative) - @c1sub3 = create(:comment, parent: @c2, post: @initiative) - @c1sub4 = create(:comment, parent: @c2, post: @initiative) - @c1sub5 = create(:comment, parent: @c2, post: @initiative) - @c3 = create(:comment, post: @initiative) - @c3sub1 = create(:comment, parent: @c3, post: @initiative) - @c3sub2 = create(:comment, parent: @c3, post: @initiative) - @c3sub3 = create(:comment, parent: @c3, post: @initiative) - @c3sub4 = create(:comment, parent: @c3, post: @initiative) - @c3sub5 = create(:comment, parent: @c3, post: @initiative) - @c3sub6 = create(:comment, parent: @c3, post: @initiative) - @c4 = create(:comment, post: @initiative) - @c4sub1 = create(:comment, parent: @c4, post: @initiative) - end - - let(:initiative_id) { @initiative.id } - let(:size) { 3 } - - example_request 'List the top-level comments of an initiative' do - expect(status).to eq(200) - json_response = json_parse(response_body) - expect(json_response[:data].size).to eq 10 - expect(json_response[:data].pluck(:id)).to eq([ - @c1, - @c2, - @c1sub1, - @c1sub2, - @c1sub3, - @c1sub4, - @c1sub5, - @c3, - @c3sub5, - @c3sub6 - ].map(&:id)) - end - end - - describe do - let(:initiative_id) { @initiative.id } - let(:sort) { '-likes_count' } - - before do - @c1, @c2, @c3 = create_list(:comment, 3, post: @initiative) - create_list(:reaction, 2, reactable: @c3) - create_list(:reaction, 3, reactable: @c2) - @c3sub1, @c3sub2 = create_list(:comment, 2, parent: @c3, post: @initiative) - create(:reaction, reactable: @c3sub2) - end - - example_request 'List the top-level comments of an initiative sorted by descending likes_count' do - expect(status).to eq(200) - json_response = json_parse(response_body) - expect(json_response[:data].size).to eq 5 - expect(json_response[:data].pluck(:id)).to eq([ - @c2, - @c3, - @c3sub1, - @c3sub2, - @c1 - ].map(&:id)) - end - end - end - - get 'web_api/v1/comments/:comment_id/children' do - explanation 'Children are always returned chronologically' - with_options scope: :page do - parameter :number, 'Page number' - parameter :size, 'Number of comments per page' - end - - before do - @c = create(:comment, post: @initiative) - @csub1 = create(:comment, parent: @c, post: @initiative) - @csub2 = create(:comment, parent: @c, post: @initiative) - @csub3 = create(:comment, parent: @c, post: @initiative) - @csub4 = create(:comment, parent: @c, post: @initiative) - @csub5 = create(:comment, parent: @c, post: @initiative) - @csub6 = create(:comment, parent: @c, post: @initiative) - @c2 = create(:comment, post: @initiative) - end - - let(:comment_id) { @c.id } - - example_request 'List the direct child comments of a comment on an initiative' do - expect(status).to eq(200) - json_response = json_parse(response_body) - expect(json_response[:data].size).to eq 6 - expect(json_response[:data].pluck(:id)).to eq([ - @csub1, - @csub2, - @csub3, - @csub4, - @csub5, - @csub6 - ].map(&:id)) - end - end - - get 'web_api/v1/initiatives/comments/as_xlsx' do - parameter :initiatives, 'Filter by a given list of initiative ids', required: false - before do - @user = create(:admin) - header_token_for @user - end - - describe do - before do - @comments = Array.new(3) do |_i| - create(:comment, post: create(:initiative)) - end - end - - example_request 'XLSX export of comments on initiatives' do - expect(status).to eq 200 - worksheet = RubyXL::Parser.parse_buffer(response_body).worksheets[0] - expect(worksheet.count).to eq(@comments.size + 1) - end - end - - describe do - before do - @comments = create_list(:comment, 4, post: create(:initiative)) - end - - let(:initiatives) { @comments.map(&:post_id) } - - example 'XLSX export by initiative ids', document: false do - do_request - expect(status).to eq 200 - worksheet = RubyXL::Parser.parse_buffer(response_body).worksheets[0] - expect(worksheet.count).to eq(initiatives.size + 1) - end - end - - describe 'when resident' do - before { resident_header_token } - - example '[error] XLSX export', document: false do - do_request - expect(status).to eq 401 - end - end - end - - get 'web_api/v1/comments/:id' do - let(:comment) { create(:comment, post: create(:initiative)) } - let(:id) { comment.id } - - example_request 'Get one comment by id' do - expect(status).to eq 200 - expect(response_data[:id]).to eq id - expect(response_data[:attributes]).to include( - dislikes_count: 0, - publication_status: 'published', - is_admin_comment: false, - anonymous: false, - author_hash: comment.author_hash - ) - end - end - - context 'when authenticated' do - before do - @user = create(:user) - header_token_for @user - end - - get 'web_api/v1/initiatives/:initiative_id/comments' do - let(:initiative_id) { @initiative.id } - - example 'List all comments of an initiative includes the user_reaction when authenticated' do - comment = create(:comment, post: @initiative) - reaction = create(:reaction, user: @user, reactable: comment) - do_request - json_response = json_parse(response_body) - expect(json_response[:data].filter_map { |d| d[:relationships][:user_reaction][:data] }.first[:id]).to eq reaction.id - expect(json_response[:included].pluck(:id)).to include reaction.id - end - end - - post 'web_api/v1/initiatives/:initiative_id/comments' do - with_options scope: :comment do - parameter :author_id, 'The user id of the user owning the comment. Signed in user by default', required: false - parameter :body_multiloc, 'Multi-locale field with the comment body', required: true - parameter :parent_id, 'The id of the comment this comment is a response to', required: false - parameter :anonymous, 'Post this comment anonymously - true/false', required: false - end - ValidationErrorHelper.new.error_fields(self, Comment) - response_field :base, "Array containing objects with signature { error: #{Permissions::InitiativePermissionsService::USER_DENIED_REASONS.values.join(' | ')} }", scope: :errors - - let(:initiative_id) { @initiative.id } - let(:comment) { build(:comment) } - let(:body_multiloc) { comment.body_multiloc } - - example_request 'Create a comment on an initiative' do - expect(response_status).to eq 201 - json_response = json_parse(response_body) - expect(json_response.dig(:data, :relationships, :author, :data, :id)).to eq @user.id - expect(json_response.dig(:data, :attributes, :body_multiloc).stringify_keys).to match body_multiloc - expect(json_response.dig(:data, :relationships, :parent, :data)).to be_nil - expect(json_response.dig(:data, :relationships, :post, :data, :id)).to eq initiative_id - expect(@initiative.reload.comments_count).to eq 1 - end - - describe 'anomymous commenting' do - let(:allow_anonymous_participation) { true } - let(:anonymous) { true } - - before do - config = AppConfiguration.instance - config.settings['initiatives']['allow_anonymous_participation'] = allow_anonymous_participation - config.save! - end - - example_request 'Create an anonymous comment on an initiative' do - assert_status 201 - expect(response_data.dig(:relationships, :author, :data, :id)).to be_nil - expect(response_data.dig(:attributes, :anonymous)).to be true - expect(response_data.dig(:attributes, :author_name)).to be_nil - end - - example 'Does not add the author as a follower', document: false do - expect { do_request }.not_to change(Follower, :count) - end - - example 'Does not log activities for the author', document: false do - expect { do_request }.not_to have_enqueued_job(LogActivityJob).with(anything, anything, @user, anything) - end - - describe 'when anonymous posting is not allowed' do - let(:allow_anonymous_participation) { false } - - example_request 'Rejects the anonymous parameter' do - assert_status 422 - json_response = json_parse response_body - expect(json_response).to include_response_error(:base, 'anonymous_participation_not_allowed') - end - end - end - end - - post 'web_api/v1/comments/:id/mark_as_deleted' do - with_options scope: :comment do - parameter :reason_code, "one of #{Notifications::CommentDeletedByAdmin::REASON_CODES}; only required for admins", required: false - parameter :other_reason, "the reason for deleting the comment, if none of the reason codes is applicable, in which case 'other' must be chosen", required: false - end - - let(:comment) { create(:comment, author: @user, post: @initiative) } - let(:id) { comment.id } - - example_request 'Mark a comment on an initiative as deleted' do - expect(response_status).to eq 202 - expect(comment.reload.publication_status).to eq('deleted') - end - end - - patch 'web_api/v1/comments/:id' do - with_options scope: :comment do - parameter :author_id, 'The user id of the user owning the comment. Signed in user by default' - parameter :body_multiloc, 'Multi-locale field with the comment body' - parameter :parent_id, 'The id of the comment this comment is a response to' - parameter :anonymous, 'Change this comment to anonymous - true/false' - end - ValidationErrorHelper.new.error_fields(self, Comment) - response_field :base, "Array containing objects with signature { error: #{Permissions::InitiativePermissionsService::USER_DENIED_REASONS.values.join(' | ')} }", scope: :errors - - let(:comment) { create(:comment, author: @user, post: @initiative) } - let(:id) { comment.id } - let(:body_multiloc) { { 'en' => "His hair is not blond, it's orange. Get your facts straight!" } } - - example_request 'Update a comment on an initiative' do - expect(response_status).to eq 200 - json_response = json_parse(response_body) - expect(json_response.dig(:data, :attributes, :body_multiloc).stringify_keys).to match body_multiloc - expect(@initiative.reload.comments_count).to eq 1 - end - - example 'Admins cannot modify a comment on an initiative', document: false do - admin_header_token - do_request - expect(comment.reload.body_multiloc).not_to eq body_multiloc - end - - describe 'anomymous commenting' do - let(:allow_anonymous_participation) { true } - let(:anonymous) { true } - - before do - config = AppConfiguration.instance - config.settings['initiatives']['allow_anonymous_participation'] = allow_anonymous_participation - config.save! - end - - example_request 'Change an comment on an initiative to anonymous' do - assert_status 200 - expect(response_data.dig(:relationships, :author, :data, :id)).to be_nil - expect(response_data.dig(:attributes, :anonymous)).to be true - expect(response_data.dig(:attributes, :author_name)).to be_nil - end - - example '[Error] Cannot update an anonymous comment' do - comment.update!(anonymous: true) - do_request - assert_status 401 - expect(json_response_body.dig(:errors, :base, 0, :error)).to eq 'Unauthorized!' - end - - example 'Does not log activities for the author and clears the author from past activities', document: false do - clear_activity = create(:activity, item: comment, user: @user) - other_item_activity = create(:activity, item: comment, user: create(:user)) - other_user_activity = create(:activity, user: @user) - - expect { do_request }.not_to have_enqueued_job(LogActivityJob).with(anything, anything, @user, anything) - expect(clear_activity.reload.user_id).to be_nil - expect(other_item_activity.reload.user_id).to be_present - expect(other_user_activity.reload.user_id).to eq @user.id - end - - describe 'when anonymous posting is not allowed' do - let(:allow_anonymous_participation) { false } - - example 'Rejects the anonymous parameter' do - do_request comment: { anonymous: true } - assert_status 422 - json_response = json_parse response_body - expect(json_response).to include_response_error(:base, 'anonymous_participation_not_allowed') - end - end - end - end - end -end diff --git a/back/spec/acceptance/initiative_files_spec.rb b/back/spec/acceptance/initiative_files_spec.rb deleted file mode 100644 index cae6b6ead8bb..000000000000 --- a/back/spec/acceptance/initiative_files_spec.rb +++ /dev/null @@ -1,76 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' -require 'rspec_api_documentation/dsl' - -# TODO: cleanup-after-proposals-migration -resource 'InitiativeFile' do - explanation 'File attachments.' - - before do - header 'Content-Type', 'application/json' - @user = create(:user) - header_token_for @user - @initiative = create(:initiative, author: @user) - create_list(:initiative_file, 2, initiative: @initiative) - end - - get 'web_api/v1/initiatives/:initiative_id/files' do - let(:initiative_id) { @initiative.id } - - example_request 'List all file attachments of an initiative' do - expect(status).to eq(200) - json_response = json_parse(response_body) - expect(json_response[:data].size).to eq 2 - end - end - - get 'web_api/v1/initiatives/:initiative_id/files/:file_id' do - let(:initiative_id) { @initiative.id } - let(:file_id) { InitiativeFile.first.id } - - example_request 'Get one file attachment of an initiative by id' do - expect(status).to eq(200) - json_response = json_parse(response_body) - expect(json_response.dig(:data, :attributes, :file)).to be_present - end - end - - post 'web_api/v1/initiatives/:initiative_id/files' do - with_options scope: :file do - parameter :file, 'The base64 encoded file', required: true - parameter :name, 'The name of the file, including the file extension', required: true - parameter :ordering, 'An integer that is used to order the files within an initiative', required: false - end - ValidationErrorHelper.new.error_fields(self, InitiativeFile) - - let(:initiative_id) { @initiative.id } - let(:file) { encode_file_as_base64('afvalkalender.pdf') } - let(:ordering) { 1 } - let(:name) { 'afvalkalender.pdf' } - - example_request 'Add a file attachment to an initiative' do - expect(response_status).to eq 201 - json_response = json_parse(response_body) - expect(json_response.dig(:data, :attributes, :file)).to be_present - expect(json_response.dig(:data, :attributes, :ordering)).to eq(1) - expect(json_response.dig(:data, :attributes, :name)).to eq(name) - end - end - - delete 'web_api/v1/initiatives/:initiative_id/files/:file_id' do - let(:initiative_id) { @initiative.id } - let(:file_id) { InitiativeFile.first.id } - - example_request 'Delete a file attachment from an initiative' do - expect(response_status).to eq 200 - expect { InitiativeFile.find(file_id) }.to raise_error(ActiveRecord::RecordNotFound) - end - end - - private - - def encode_file_as_base64(filename) - "data:application/pdf;base64,#{Base64.encode64(Rails.root.join('spec', 'fixtures', filename).read)}" - end -end diff --git a/back/spec/acceptance/initiative_images_spec.rb b/back/spec/acceptance/initiative_images_spec.rb deleted file mode 100644 index 82db8a7c4103..000000000000 --- a/back/spec/acceptance/initiative_images_spec.rb +++ /dev/null @@ -1,87 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' -require 'rspec_api_documentation/dsl' - -# TODO: cleanup-after-proposals-migration -resource 'InitiativeImage' do - explanation 'Initiatives can have mutliple images.' - - before do - header 'Content-Type', 'application/json' - @user = create(:user) - header_token_for @user - @initiative = create(:initiative, author: @user) - create_list(:initiative_image, 2, initiative: @initiative) - end - - get 'web_api/v1/initiatives/:initiative_id/images' do - let(:initiative_id) { @initiative.id } - - example_request 'List all images of an initiative' do - expect(status).to eq(200) - json_response = json_parse(response_body) - expect(json_response[:data].size).to eq 2 - end - end - - get 'web_api/v1/initiatives/:initiative_id/images/:image_id' do - let(:initiative_id) { @initiative.id } - let(:image_id) { InitiativeImage.first.id } - - example_request 'Get one image of an initiative by id' do - expect(status).to eq(200) - json_response = json_parse(response_body) - expect(json_response.dig(:data, :attributes, :versions).keys).to match %i[small medium large fb] - end - end - - post 'web_api/v1/initiatives/:initiative_id/images' do - with_options scope: :image do - parameter :image, 'The base64 encoded image', required: true - parameter :ordering, 'An integer that is used to order the images within an initiative', required: false - end - ValidationErrorHelper.new.error_fields(self, InitiativeImage) - - let(:initiative_id) { @initiative.id } - let(:image) { png_image_as_base64 'image13.png' } - let(:ordering) { 1 } - - example_request 'Add an image to an initiative' do - expect(response_status).to eq 201 - json_response = json_parse(response_body) - expect(json_response.dig(:data, :attributes, :versions).keys).to match %i[small medium large fb] - expect(json_response.dig(:data, :attributes, :ordering)).to eq(1) - end - end - - patch 'web_api/v1/initiatives/:initiative_id/images/:image_id' do - with_options scope: :image do - parameter :image, 'The base64 encoded image' - parameter :ordering, 'An integer that is used to order the images within an initiative' - end - ValidationErrorHelper.new.error_fields(self, InitiativeImage) - - let(:initiative_id) { @initiative.id } - let(:image_id) { InitiativeImage.first.id } - let(:image) { png_image_as_base64 'image14.png' } - let(:ordering) { 2 } - - example_request 'Update an image for an initiative' do - expect(response_status).to eq 200 - json_response = json_parse(response_body) - expect(json_response.dig(:data, :attributes, :versions).keys).to match %i[small medium large fb] - expect(json_response.dig(:data, :attributes, :ordering)).to eq(2) - end - end - - delete 'web_api/v1/initiatives/:initiative_id/images/:image_id' do - let(:initiative_id) { @initiative.id } - let(:image_id) { InitiativeImage.first.id } - - example_request 'Delete an image from an initiative' do - expect(response_status).to eq 200 - expect { InitiativeImage.find(image_id) }.to raise_error(ActiveRecord::RecordNotFound) - end - end -end diff --git a/back/spec/acceptance/initiative_official_feedbacks_spec.rb b/back/spec/acceptance/initiative_official_feedbacks_spec.rb deleted file mode 100644 index 2d405bbbd53e..000000000000 --- a/back/spec/acceptance/initiative_official_feedbacks_spec.rb +++ /dev/null @@ -1,132 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' -require 'rspec_api_documentation/dsl' - -# TODO: cleanup-after-proposals-migration -resource 'OfficialFeedback' do - explanation 'Official feedback is input from moderators on content (i.e. ideas), separated from comments.' - - before do - header 'Content-Type', 'application/json' - @initiative = create(:initiative) - @feedbacks = create_list(:official_feedback, 2, post: @initiative) - end - - get 'web_api/v1/initiatives/:initiative_id/official_feedback' do - with_options scope: :page do - parameter :number, 'Page number' - parameter :size, 'Number of official feedback per page' - end - - let(:initiative_id) { @initiative.id } - - example_request 'List all official feedback of an initiative' do - assert_status 200 - json_response = json_parse(response_body) - expect(json_response[:data].size).to eq 2 - expect(json_response.dig(:data, 0, :attributes, :body_multiloc)).to be_present - expect(json_response.dig(:data, 0, :attributes, :author_multiloc)).to be_present - end - end - - get 'web_api/v1/official_feedback/:id' do - let(:id) { @feedbacks.first.id } - - example_request 'Get one official feedback on an initiative by id' do - assert_status 200 - json_response = json_parse(response_body) - expect(json_response.dig(:data, :id)).to eq @feedbacks.first.id - end - end - - context 'when authenticated' do - before do - @user = create(:admin) - header_token_for @user - end - - post 'web_api/v1/initiatives/:initiative_id/official_feedback' do - with_options scope: :official_feedback do - parameter :body_multiloc, 'Multi-locale field with the feedback body', required: true - parameter :author_multiloc, 'Multi-locale field with describing the author', required: true - end - ValidationErrorHelper.new.error_fields(self, OfficialFeedback) - - let(:initiative_id) { @initiative.id } - let(:feedback) { build(:official_feedback) } - let(:body_multiloc) { feedback.body_multiloc } - let(:author_multiloc) { feedback.author_multiloc } - - example_request 'Create an official feedback on an initiative' do - assert_status 201 - json_response = json_parse(response_body) - expect(json_response.dig(:data, :relationships, :user, :data, :id)).to eq @user.id - expect(json_response.dig(:data, :attributes, :body_multiloc).stringify_keys).to match body_multiloc - expect(json_response.dig(:data, :attributes, :author_multiloc).stringify_keys).to match author_multiloc - expect(json_response.dig(:data, :relationships, :post, :data, :id)).to eq initiative_id - expect(@initiative.reload.official_feedbacks_count).to eq 3 - end - - describe do - let(:body_multiloc) { { 'en' => '' } } - - example_request '[error] Create an invalid official feedback on an initiative' do - assert_status 422 - json_response = json_parse response_body - expect(json_response).to include_response_error(:body_multiloc, 'blank') - end - end - end - - patch 'web_api/v1/official_feedback/:id' do - with_options scope: :official_feedback do - parameter :body_multiloc, 'Multi-locale field with the feedback body', required: true - parameter :author_multiloc, 'Multi-locale field with describing the author', required: true - end - ValidationErrorHelper.new.error_fields(self, OfficialFeedback) - - let(:official_feedback) { create(:official_feedback, user: @user, post: @initiative) } - let(:id) { official_feedback.id } - let(:body_multiloc) { { 'en' => "His hair is not blond, it's orange. Get your facts straight!" } } - - example_request 'Update an official feedback for an initiative' do - expect(response_status).to eq 200 - json_response = json_parse(response_body) - expect(json_response.dig(:data, :attributes, :body_multiloc).stringify_keys).to match body_multiloc - expect(@initiative.reload.official_feedbacks_count).to eq 3 - end - end - - delete 'web_api/v1/official_feedback/:id' do - let(:official_feedback) { create(:official_feedback, user: @user, post: @initiative) } - let(:id) { official_feedback.id } - example_request 'Delete an official feedback from an initiative' do - expect(response_status).to eq 200 - expect { OfficialFeedback.find(id) }.to raise_error(ActiveRecord::RecordNotFound) - expect(@initiative.reload.official_feedbacks_count).to eq 2 - end - end - end - - context 'when resident' do - before { resident_header_token } - - post 'web_api/v1/initiatives/:initiative_id/official_feedback' do - with_options scope: :official_feedback do - parameter :body_multiloc, 'Multi-locale field with the feedback body', required: true - parameter :author_multiloc, 'Multi-locale field with describing the author', required: true - end - ValidationErrorHelper.new.error_fields(self, OfficialFeedback) - - let(:initiative_id) { @initiative.id } - let(:feedback) { build(:official_feedback) } - let(:body_multiloc) { feedback.body_multiloc } - let(:author_multiloc) { feedback.author_multiloc } - - example_request '[error] Create an official feedback on an initiative' do - expect(response_status).to eq 401 - end - end - end -end diff --git a/back/spec/acceptance/initiative_reactions_spec.rb b/back/spec/acceptance/initiative_reactions_spec.rb deleted file mode 100644 index c16109130871..000000000000 --- a/back/spec/acceptance/initiative_reactions_spec.rb +++ /dev/null @@ -1,148 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' -require 'rspec_api_documentation/dsl' - -# TODO: cleanup-after-proposals-migration -resource 'Reactions' do - explanation 'Reactions are used to express agreement on content (i.e. ideas). Ideally, the city would accept the most reactiond initiatives.' - - before do - @user = create(:admin) - header_token_for @user - header 'Content-Type', 'application/json' - - @status_proposed = create(:initiative_status_proposed) - @status_expired = create(:initiative_status_expired) - @status_threshold_reached = create(:initiative_status_threshold_reached) - @status_answered = create(:initiative_status_answered) - @status_ineligible = create(:initiative_status_ineligible) - - @initiative = create(:initiative) - - @initiative.initiative_status_changes.create!( - initiative_status: @status_proposed - ) - @reactions = create_list(:reaction, 2, reactable: @initiative, mode: 'up') - end - - get 'web_api/v1/initiatives/:initiative_id/reactions' do - let(:initiative_id) { @initiative.id } - - example_request 'List all reactions of an initiative' do - assert_status 200 - json_response = json_parse(response_body) - expect(json_response[:data].size).to eq 2 - end - end - - get 'web_api/v1/reactions/:id' do - let(:id) { @reactions.first.id } - - example_request 'Get one reaction on an initiative by id' do - assert_status 200 - json_response = json_parse(response_body) - expect(json_response.dig(:data, :id)).to eq @reactions.first.id - end - end - - post 'web_api/v1/initiatives/:initiative_id/reactions' do - with_options scope: :reaction do - parameter :user_id, 'The user id of the user owning the reaction. Signed in user by default', required: false - parameter :mode, 'one of [up, down]', required: true - end - ValidationErrorHelper.new.error_fields(self, Reaction) - - disabled_reasons = Permissions::InitiativePermissionsService::USER_DENIED_REASONS.values - response_field :base, "Array containing objects with signature { error: #{disabled_reasons.join(' | ')} }", scope: :errors - - let(:initiative_id) { @initiative.id } - let(:mode) { 'up' } - - example_request 'Create a reaction to an initiative' do - assert_status 201 - json_response = json_parse(response_body) - expect(json_response.dig(:data, :relationships, :user, :data, :id)).to be_nil - expect(json_response.dig(:data, :attributes, :mode)).to eq 'up' - expect(@initiative.reload.likes_count).to eq 3 - end - - # TODO: cleanup-after-proposals-migration - example 'Reaching the voting threshold immediately triggers status change', document: false do - settings = AppConfiguration.instance.settings - settings['initiatives']['reacting_threshold'] = 3 - AppConfiguration.instance.update! settings: settings - - do_request - assert_status 201 - expect(@initiative.reload.initiative_status).to eq @status_threshold_reached - end - - example 'The first non-author reaction create action will set editing_locked to true', document: false do - expect(@initiative.reload.editing_locked).to be false - do_request - assert_status 201 - expect(@initiative.reload.editing_locked).to be true - end - end - - post 'web_api/v1/initiatives/:initiative_id/reactions/up' do - ValidationErrorHelper.new.error_fields(self, Reaction) - - disabled_reasons = Permissions::InitiativePermissionsService::USER_DENIED_REASONS.values - response_field :base, "Array containing objects with signature { error: #{disabled_reasons.join(' | ')} }", scope: :errors - - let(:initiative_id) { @initiative.id } - - example_request "Like an initiative that doesn't have your reaction yet" do - assert_status 201 - expect(@initiative.reload.likes_count).to eq 3 - expect(@initiative.reload.dislikes_count).to eq 0 - end - - example 'Like an initiative that you disliked before' do - @initiative.reactions.create(user: @user, mode: 'down') - do_request - assert_status 201 - expect(@initiative.reload.likes_count).to eq 3 - expect(@initiative.reload.dislikes_count).to eq 0 - end - - example '[error] Like an initiative that you liked before' do - @initiative.reactions.create(user: @user, mode: 'up') - do_request - assert_status 422 - json_response = json_parse response_body - expect(json_response).to include_response_error(:base, 'already_liked') - expect(@initiative.reload.likes_count).to eq 3 - expect(@initiative.reload.dislikes_count).to eq 0 - end - end - - post 'web_api/v1/initiatives/:initiative_id/reactions/down' do - ValidationErrorHelper.new.error_fields(self, Reaction) - - disabled_reasons = Permissions::InitiativePermissionsService::USER_DENIED_REASONS.values - response_field :base, "Array containing objects with signature { error: #{disabled_reasons.join(' | ')} }", scope: :errors - - let(:initiative_id) { @initiative.id } - - example_request "[error] Dislike an initiative that doesn't have your reaction yet" do - assert_status 401 - json_response = json_parse(response_body) - expect(json_response.dig(:errors, :base)).to include({ error: 'dislikes_not_supported' }) - expect(@initiative.reload.likes_count).to eq 2 - expect(@initiative.reload.dislikes_count).to eq 0 - end - end - - delete 'web_api/v1/reactions/:id' do - let(:reaction) { create(:reaction, user: @user, reactable: @initiative) } - let(:id) { reaction.id } - - example_request 'Delete a reaction from an initiative' do - assert_status 200 - expect { Reaction.find(id) }.to raise_error(ActiveRecord::RecordNotFound) - end - end -end diff --git a/back/spec/acceptance/initiative_spam_report_spec.rb b/back/spec/acceptance/initiative_spam_report_spec.rb deleted file mode 100644 index d19fee268f84..000000000000 --- a/back/spec/acceptance/initiative_spam_report_spec.rb +++ /dev/null @@ -1,84 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' -require 'rspec_api_documentation/dsl' - -# TODO: cleanup-after-proposals-migration -resource 'Spam Reports' do - explanation 'Reporting undesired content (i.e. an initiative).' - - before do - @user = create(:admin) - header_token_for @user - header 'Content-Type', 'application/json' - @initiative = create(:initiative) - @spam_reports = create_list(:spam_report, 2, spam_reportable: @initiative) - end - - get 'web_api/v1/initiatives/:initiative_id/spam_reports' do - let(:initiative_id) { @initiative.id } - - example_request 'List all spam reports of an initiative' do - expect(status).to eq(200) - json_response = json_parse(response_body) - expect(json_response[:data].size).to eq 2 - end - end - - get 'web_api/v1/spam_reports/:id' do - let(:id) { @spam_reports.first.id } - - example_request 'Get one spam report of an initiative by id' do - expect(status).to eq 200 - json_response = json_parse(response_body) - expect(json_response.dig(:data, :id)).to eq @spam_reports.first.id - end - end - - post 'web_api/v1/initiatives/:initiative_id/spam_reports' do - with_options scope: :spam_report do - parameter :user_id, 'the user id of the user owning the spam report. Signed in user by default', required: false - parameter :reason_code, 'one of [wrong_content, inappropriate, other]', required: true - parameter :other_reason, "the reason for the spam report, if none of the reason codes is applicable, in which case 'other' must be chosen", required: false - end - ValidationErrorHelper.new.error_fields(self, SpamReport) - - let(:initiative_id) { @initiative.id } - let(:reason_code) { 'inappropriate' } - - example_request 'Create a spam report for an initiative' do - expect(response_status).to eq 201 - json_response = json_parse(response_body) - expect(json_response.dig(:data, :relationships, :user, :data, :id)).to eq @user.id - expect(json_response.dig(:data, :attributes, :reason_code)).to eq 'inappropriate' - end - end - - patch 'web_api/v1/spam_reports/:id' do - with_options scope: :spam_report do - parameter :reason_code, 'one of [wrong_content, inappropriate, other]', required: true - parameter :other_reason, "the reason for the spam report, if none of the reason codes is applicable, in which case 'other' must be chosen", required: false - end - ValidationErrorHelper.new.error_fields(self, SpamReport) - - let(:spam_report) { create(:spam_report, user: @user, spam_reportable: @initiative, reason_code: 'other', other_reason: 'pagiarism') } - let(:id) { spam_report.id } - let(:reason_code) { 'inappropriate' } - - example_request 'Update a spam report for an initiative' do - expect(status).to be 200 - json_response = json_parse(response_body) - expect(json_response.dig(:data, :attributes, :reason_code)).to eq 'inappropriate' - end - end - - delete 'web_api/v1/spam_reports/:id' do - let(:spam_report) { create(:spam_report, user: @user, spam_reportable: @initiative) } - let(:id) { spam_report.id } - - example_request 'Delete a spam report from an initiative' do - expect(response_status).to eq 200 - expect { SpamReport.find(id) }.to raise_error(ActiveRecord::RecordNotFound) - end - end -end diff --git a/back/spec/acceptance/initiative_status_changes_spec.rb b/back/spec/acceptance/initiative_status_changes_spec.rb deleted file mode 100644 index 94d9819d8764..000000000000 --- a/back/spec/acceptance/initiative_status_changes_spec.rb +++ /dev/null @@ -1,227 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' -require 'rspec_api_documentation/dsl' - -# TODO: cleanup-after-proposals-migration -resource 'InitiativeStatusChange' do - explanation 'Initiative status changes allow admins to apply manual status changes on initiatives.' - - before do - header 'Content-Type', 'application/json' - @initiative = create(:initiative) - @changes = create_list(:initiative_status_change, 2, initiative: @initiative) - end - - get 'web_api/v1/initiatives/:initiative_id/initiative_status_changes' do - with_options scope: :page do - parameter :number, 'Page number' - parameter :size, 'Number of status changes per page' - end - - before { admin_header_token } - - let(:initiative_id) { @initiative.id } - - example_request 'List all status changes of an initiative' do - assert_status 200 - json_response = json_parse(response_body) - expect(json_response[:data].size).to eq 3 - expect(json_response.dig(:data, 0, :attributes, :created_at)).to be_present - end - end - - get 'web_api/v1/initiative_status_changes/:id' do - before { admin_header_token } - - let(:id) { @changes.first.id } - - example_request 'Get one status changes on an initiative by id' do - assert_status 200 - json_response = json_parse(response_body) - expect(json_response.dig(:data, :id)).to eq @changes.first.id - end - end - - context 'when authenticated' do - before do - @user = create(:admin) - header_token_for @user - - @status_review_pending = create(:initiative_status_review_pending) - @status_changes_requested = create(:initiative_status_changes_requested) - @status_proposed = create(:initiative_status_proposed) - @status_expired = create(:initiative_status_expired) - @status_threshold_reached = create(:initiative_status_threshold_reached) - @status_answered = create(:initiative_status_answered) - @status_ineligible = create(:initiative_status_ineligible) - - create( - :initiative_status_change, - initiative: @initiative, initiative_status: @status_threshold_reached - ) - end - - post 'web_api/v1/initiatives/:initiative_id/initiative_status_changes' do - with_options scope: :initiative_status_change do - parameter :initiative_status_id, 'The new initiative status', required: true - parameter :user_id, 'The user who made the status change', required: false - parameter :official_feedback_id, 'An existing official feedback can be used', required: false - end - with_options scope: %i[initiative_status_change official_feedback_attributes] do - parameter :body_multiloc, 'Multi-locale field with the feedback body', required: false - parameter :author_multiloc, 'Multi-locale field with describing the author', required: false - end - ValidationErrorHelper.new.error_fields(self, InitiativeStatusChange) - - let(:initiative_id) { @initiative.id } - let(:initiative_status_id) { @status_answered.id } - let(:feedback) { build(:official_feedback) } - let(:body_multiloc) { feedback.body_multiloc } - let(:author_multiloc) { feedback.author_multiloc } - - example_request 'Create a status change on an initiative with new feedback' do - assert_status 201 - json_response = json_parse(response_body) - expect(json_response.dig(:data, :relationships, :user, :data, :id)).to eq @user.id - expect(@initiative.reload.official_feedbacks_count).to eq 1 - end - - describe do - let(:official_feedback_id) { create(:official_feedback, post: @initiative).id } - let(:body_multiloc) { nil } - let(:author_multiloc) { nil } - - example_request 'Create a status change on an initiative using an existing feedback' do - assert_status 201 - expect(@initiative.reload.official_feedbacks_count).to eq 1 - end - end - - describe do - let(:body_multiloc) { nil } - let(:author_multiloc) { nil } - - example_request '[error] Create a status change on an initiative without feedback' do - assert_status 422 - end - end - - describe do - let(:initiative_status_id) { @status_expired.id } - - example_request '[error] Create a status change through an invalid transition' do - assert_status 422 - json_response = json_parse response_body - expect(json_response).to include_response_error(:base, 'initiative_status_transition_not_allowed') - end - end - - describe do - let(:initiative_status_id) { @status_threshold_reached.id } - - example_request '[error] Create a status change to the same status' do - assert_status 422 - json_response = json_parse response_body - expect(json_response).to include_response_error(:base, 'initiative_status_transition_without_change') - end - end - - # If the review feature is off, the initiative_status_change with code 'proposed' - # is created at the model level when the initiative is published. - # Thus, this POST request can only happen when the review feature is on. - context 'when the the status change is to the proposed status' do - let(:new_initiative) { create(:initiative) } - let!(:_initiative_status_change) do - create( - :initiative_status_change, - initiative: new_initiative, - initiative_status: @status_review_pending - ) - end - - let(:initiative_id) { new_initiative.id } - let(:initiative_status_id) { @status_proposed.id } - - example 'creation of status change record' do - expect(Initiative.find(initiative_id).editing_locked).to be false - - # results in the logging of an Initiative 'proposed' activity - expect { do_request } - .to have_enqueued_job(LogActivityJob) - .with(instance_of(Initiative), 'proposed', @user, instance_of(Integer)) - .exactly(1).times - assert_status 201 - # results in the setting of editing_locked: true - expect(Initiative.find(initiative_id).editing_locked).to be true - end - end - - context 'when the the status change is to the changes_requested status' do - let(:new_initiative) { create(:initiative) } - let!(:_initiative_status_change) do - create( - :initiative_status_change, - initiative: new_initiative, - initiative_status: @status_changes_requested - ) - end - - let(:initiative_id) { new_initiative.id } - let(:initiative_status_id) { @status_review_pending.id } - - example 'creation of status change record' do - expect(Initiative.find(initiative_id).editing_locked).to be false - - # does not result in the logging of an Initiative 'proposed' activity - expect { do_request } - .not_to have_enqueued_job(LogActivityJob) - .with(instance_of(Initiative), 'proposed', anything, anything) - assert_status 201 - # does not result in locked editing - expect(Initiative.find(initiative_id).editing_locked).to be false - end - end - end - end - - context 'when resident' do - before do - resident_header_token - - @status_proposed = create(:initiative_status_proposed) - @status_expired = create(:initiative_status_expired) - @status_threshold_reached = create(:initiative_status_threshold_reached) - @status_answered = create(:initiative_status_answered) - @status_ineligible = create(:initiative_status_ineligible) - - create( - :initiative_status_change, - initiative: @initiative, initiative_status: @status_threshold_reached - ) - end - - post 'web_api/v1/initiatives/:initiative_id/initiative_status_changes' do - with_options scope: :initiative_status_change do - parameter :initiative_status_id, 'The new initiative status', required: true - parameter :user_id, 'The user who made the status change', required: false - parameter :official_feedback_id, 'An existing official feedback can be used', required: false - end - with_options scope: %i[initiative_status_change official_feedback_attributes] do - parameter :body_multiloc, 'Multi-locale field with the feedback body', required: false - parameter :author_multiloc, 'Multi-locale field with describing the author', required: false - end - ValidationErrorHelper.new.error_fields(self, InitiativeStatusChange) - - let(:initiative_id) { @initiative.id } - let(:initiative_status_id) { @status_answered.id } - let(:feedback) { build(:official_feedback) } - let(:body_multiloc) { feedback.body_multiloc } - let(:author_multiloc) { feedback.author_multiloc } - - example_request '[error] Create an official feedback on an initiative' do - expect(response_status).to eq 401 - end - end - end -end diff --git a/back/spec/acceptance/initiative_statuses_spec.rb b/back/spec/acceptance/initiative_statuses_spec.rb deleted file mode 100644 index 1c873c524a4a..000000000000 --- a/back/spec/acceptance/initiative_statuses_spec.rb +++ /dev/null @@ -1,33 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' -require 'rspec_api_documentation/dsl' - -# TODO: cleanup-after-proposals-migration -resource 'InitiativeStatuses' do - explanation 'Initivative statuses reflect the cities attitude towards an initiative.' - - before do - header 'Content-Type', 'application/json' - @statuses = create_list(:initiative_status, 3) - end - - get 'web_api/v1/initiative_statuses' do - example_request 'List all initiative statuses' do - expect(status).to eq(200) - json_response = json_parse(response_body) - expect(json_response[:data].size).to eq 3 - expect(json_response[:data].pluck(:id)).to match_array @statuses.map(&:id) - end - end - - get 'web_api/v1/initiative_statuses/:id' do - let(:id) { @statuses.first.id } - - example_request 'Get one initiative status by id' do - expect(status).to eq 200 - json_response = json_parse(response_body) - expect(json_response.dig(:data, :id)).to eq @statuses.first.id - end - end -end diff --git a/back/spec/acceptance/initiatives_spec.rb b/back/spec/acceptance/initiatives_spec.rb deleted file mode 100644 index 509399a1c964..000000000000 --- a/back/spec/acceptance/initiatives_spec.rb +++ /dev/null @@ -1,796 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' -require 'rspec_api_documentation/dsl' - -resource 'Initiatives' do - explanation 'Proposals from citizens (but more spontaneous than ideas) to the city.' - - before do - header 'Content-Type', 'application/json' - @first_admin = create(:admin) - @initiatives = %w[published published draft published published published].map { |ps| create(:initiative, publication_status: ps, assignee: create(:admin)) } - @user = create(:user) - header_token_for @user - end - - # TODO: cleanup-after-proposals-migration - get 'web_api/v1/initiatives' do - with_options scope: :page do - parameter :number, 'Page number' - parameter :size, 'Number of initiatives per page' - end - parameter :author, 'Filter by author (user id)', required: false - parameter :publication_status, 'Filter by publication status; returns all publlished initiatives by default', required: false - parameter :topics, 'Filter by topics (OR)', required: false - parameter :areas, 'Filter by areas (OR)', required: false - parameter :initiative_status, 'Filter by status (initiative status id)', required: false - parameter :assignee, 'Filter by assignee (user id)', required: false - parameter :search, 'Filter by searching in title and body', required: false - parameter :feedback_needed, 'Filter out initiatives that need feedback', required: false - parameter :sort, "Either 'trending' (default), 'new', '-new', 'author_name', '-author_name', 'likes_count', '-likes_count', 'status', '-status', 'random'", required: false - - example_request 'List all published initiatives (default behaviour)' do - assert_status 200 - json_response = json_parse(response_body) - expect(json_response[:data].size).to eq 5 - expect(json_response[:data].map { |d| d.dig(:attributes, :publication_status) }).to all(eq 'published') - end - - example "Don't list drafts (default behaviour)", document: false do - do_request publication_status: 'draft' - - assert_status 200 - json_response = json_parse(response_body) - expect(json_response[:data].size).to eq 0 - end - - example 'List all initiatives which match one of the given topics', document: false do - t1 = create(:topic) - t2 = create(:topic) - - i1 = @initiatives[0] - i1.topics = [t1] - i1.save - i2 = @initiatives[1] - i2.topics = [t2] - i2.save - - do_request topics: [t1.id, t2.id] - json_response = json_parse(response_body) - expect(json_response[:data].size).to eq 2 - expect(json_response[:data].pluck(:id)).to match_array [i1.id, i2.id] - end - - example 'List all initiatives which match one of the given areas', document: false do - a1 = create(:area) - a2 = create(:area) - - i1 = @initiatives.first - i1.areas = [a1] - i1.save - i2 = @initiatives.second - i2.areas = [a2] - i2.save - - do_request areas: [a1.id, a2.id] - json_response = json_parse(response_body) - expect(json_response[:data].size).to eq 2 - expect(json_response[:data].pluck(:id)).to match_array [i1.id, i2.id] - end - - example 'List all initiatives for a user' do - u = create(:user) - i = create(:initiative, author: u) - - do_request author: u.id - json_response = json_parse(response_body) - expect(json_response[:data].size).to eq 1 - expect(json_response[:data][0][:id]).to eq i.id - end - - example 'List all initiatives for an initiative status', document: false do - status = create(:initiative_status) - i = create(:initiative, initiative_status: status) - - do_request initiative_status: status.id - json_response = json_parse(response_body) - expect(json_response[:data].size).to eq 1 - expect(json_response[:data][0][:id]).to eq i.id - end - - example 'List all initiatives for an assignee', document: false do - a = create(:admin) - i = create(:initiative, assignee: a) - - do_request assignee: a.id - json_response = json_parse(response_body) - expect(json_response[:data].size).to eq 1 - expect(json_response[:data][0][:id]).to eq i.id - end - - example 'List all unassigned initiatives' do - create(:initiative, assignee: create(:admin)) - initiatives = create_list(:initiative, 2, assignee: nil) - - do_request assignee: 'unassigned' - - assert_status 200 - json_response = json_parse response_body - expect(json_response[:data].size).to eq 2 - expect(json_response[:data].pluck(:id)).to match_array initiatives.map(&:id) - end - - example 'List all initiatives that need feedback', document: false do - threshold_reached = create(:initiative_status_threshold_reached) - i = create(:initiative, initiative_status: threshold_reached) - - do_request feedback_needed: true - json_response = json_parse(response_body) - expect(json_response[:data].size).to eq 1 - expect(json_response[:data][0][:id]).to eq i.id - end - - example 'Search for initiatives', document: false do - create(:user) - initiatives = [ - create(:initiative, title_multiloc: { en: 'This initiative is uniqque' }), - create(:initiative, title_multiloc: { en: 'This one origiinal' }) - ] - - do_request search: 'uniqque' - json_response = json_parse(response_body) - expect(json_response[:data].size).to eq 1 - expect(json_response[:data][0][:id]).to eq initiatives[0].id - end - - example 'List all initiatives sorted by new', document: false do - create(:user) - i1 = create(:initiative) - - do_request sort: 'new' - json_response = json_parse(response_body) - expect(json_response[:data].size).to eq 6 - expect(json_response[:data][0][:id]).to eq i1.id - end - - example 'List all initiatives by random ordering', document: false do - create(:user) - create(:initiative) - - do_request sort: 'random' - json_response = json_parse(response_body) - expect(json_response[:data].size).to eq 6 - end - - example 'List all initiatives includes the user_reaction and user_follower', document: false do - initiative = create(:initiative) - reaction = create(:reaction, reactable: initiative, user: @user) - follower = create(:follower, followable: create(:initiative), user: @user) - - do_request - json_response = json_parse(response_body) - expect(json_response[:data].filter_map { |d| d.dig(:relationships, :user_reaction, :data, :id) }.first).to eq reaction.id - expect(json_response[:data].filter_map { |d| d.dig(:relationships, :user_follower, :data, :id) }.first).to eq follower.id - expect(json_response[:included].pluck(:id)).to include reaction.id - end - end - - # TODO: cleanup-after-proposals-migration - get 'web_api/v1/initiatives/as_markers' do - before do - locations = [[51.044039, 3.716964], [50.845552, 4.357355], [50.640255, 5.571848], [50.950772, 4.308304], [51.215929, 4.422602], [50.453848, 3.952217], [-27.148983, -109.424659]] - placenames = ['Ghent', 'Brussels', 'Liège', 'Meise', 'Antwerp', 'Mons', 'Hanga Roa'] - @initiatives.each do |i| - i.location_point_geojson = { 'type' => 'Point', 'coordinates' => locations.pop } - i.title_multiloc['en'] = placenames.pop - i.publication_status = 'published' - i.save! - end - end - - with_options scope: :page do - parameter :number, 'Page number' - parameter :size, 'Number of initiatives per page' - end - parameter :author, 'Filter by author (user id)', required: false - parameter :publication_status, 'Return only initiatives with the specified publication status; returns all pusblished initiatives by default', required: false - parameter :bounding_box, 'Given an [x1,y1,x2,y2] array of doubles (x being latitude and y being longitude), the markers are filtered to only retain those within the (x1,y1)-(x2,y2) box.', required: false - parameter :topics, 'Filter by topics (OR)', required: false - parameter :areas, 'Filter by areas (OR)', required: false - parameter :assignee, 'Filter by assignee (user id)', required: false - parameter :search, 'Filter by searching in title and body', required: false - - example 'List all markers within a bounding box' do - do_request(bounding_box: '[51.208758,3.224363,50.000667,5.715281]') # Bruges-Bastogne - - assert_status 200 - json_response = json_parse(response_body) - expect(json_response[:data].size).to eq 4 - expect(json_response[:data].map { |d| d.dig(:attributes, :title_multiloc, :en) }.sort).to match %w[Brussels Liège Meise Mons].sort - end - end - - # TODO: move-old-proposals-test - get 'web_api/v1/initiatives/as_xlsx' do - before { admin_header_token } - - parameter :initiatives, 'Filter by a given list of initiative ids', required: false - - example_request 'XLSX export' do - assert_status 200 - end - - describe do - before do - @selected_initiatives = @initiatives.select(&:published?).shuffle.take 2 - end - - let(:initiatives) { @selected_initiatives.map(&:id) } - - example_request 'XLSX export by initiative ids', document: false do - assert_status 200 - worksheet = RubyXL::Parser.parse_buffer(response_body).worksheets[0] - expect(worksheet.count).to eq(@selected_initiatives.size + 1) - end - end - - describe 'when resident' do - before { resident_header_token } - - example '[error] XLSX export', document: false do - do_request - assert_status 401 - end - end - end - - # TODO: cleanup-after-proposals-migration - get 'web_api/v1/initiatives/filter_counts' do - before do - Initiative.all.each(&:destroy!) - @t1 = create(:topic) - @t2 = create(:topic) - @a1 = create(:area) - @a2 = create(:area) - @s1 = create(:initiative_status) - @s2 = create(:initiative_status) - @i1 = create(:initiative, topics: [@t1, @t2], areas: [@a1], initiative_status: @s1) - @i2 = create(:initiative, topics: [@t1], areas: [@a1, @a2], initiative_status: @s2) - @i3 = create(:initiative, topics: [@t2], areas: [], initiative_status: @s2) - @i4 = create(:initiative, topics: [], areas: [@a1], initiative_status: @s2) - - # a1 -> 3 - # a2 -> 1 - # t1 -> 2 - # t2 -> 2 - # s1 -> 1 - # s2 -> 3 - end - - parameter :topics, 'Filter by topics (OR)', required: false - parameter :areas, 'Filter by areas (OR)', required: false - parameter :author, 'Filter by author (user id)', required: false - parameter :assignee, 'Filter by assignee (user id)', required: false - parameter :initiative_status, 'Filter by status (initiative status id)', required: false - parameter :search, 'Filter by searching in title and body', required: false - parameter :publication_status, 'Return only initiatives with the specified publication status; returns all pusblished initiatives by default', required: false - - example_request 'List initiative counts per filter option' do - assert_status 200 - json_response = json_parse response_body - expect(json_response.dig(:data, :type)).to eq 'filter_counts' - - expect(json_response.dig(:data, :attributes, :initiative_status_id)[@s1.id.to_sym]).to eq 1 - expect(json_response.dig(:data, :attributes, :initiative_status_id)[@s2.id.to_sym]).to eq 3 - expect(json_response.dig(:data, :attributes, :area_id)[@a1.id.to_sym]).to eq 3 - expect(json_response.dig(:data, :attributes, :area_id)[@a2.id.to_sym]).to eq 1 - expect(json_response.dig(:data, :attributes, :topic_id)[@t1.id.to_sym]).to eq 2 - expect(json_response.dig(:data, :attributes, :topic_id)[@t2.id.to_sym]).to eq 2 - expect(json_response.dig(:data, :attributes, :total)).to eq 4 - end - - example 'List initiative counts per filter option on topic', document: false do - do_request topics: [@t1.id] - assert_status 200 - end - - example 'List initiative counts per filter option on area', document: false do - do_request areas: [@a1.id] - assert_status 200 - end - - example 'List initiative counts when also using search filtering AND sort', document: false do - do_request(search: 'uniqque', sort: 'new') - assert_status 200 - end - end - - get 'web_api/v1/initiatives/:id' do - let(:initiative) { @initiatives.first } - let(:id) { initiative.id } - - example_request 'Get one initiative by id' do - assert_status 200 - expect(response_data[:id]).to eq initiative.id - expect(response_data[:attributes]).to include( - anonymous: false, - author_hash: initiative.author_hash - ) - end - end - - get 'web_api/v1/initiatives/by_slug/:slug' do - let(:slug) { @initiatives.first.slug } - - example_request 'Get one initiative by slug' do - assert_status 200 - json_response = json_parse(response_body) - expect(json_response.dig(:data, :id)).to eq @initiatives.first.id - end - - describe do - let(:slug) { 'unexisting-initiative' } - - example '[error] Get an unexisting initiative', document: false do - do_request - assert_status 404 - end - end - end - - # TODO: cleanup-after-proposals-migration - post 'web_api/v1/initiatives' do - before do - create(:initiative_status, code: 'proposed') - end - - with_options scope: :initiative do - parameter :author_id, 'The user id of the user owning the initiative. This can only be specified by moderators and is inferred from the JWT token for residents.' - parameter :publication_status, 'Publication status', required: true, extra: "One of #{Post::PUBLICATION_STATUSES.join(',')}" - parameter :title_multiloc, 'Multi-locale field with the initiative title', required: true, extra: 'Maximum 100 characters' - parameter :body_multiloc, 'Multi-locale field with the initiative body', extra: 'Required if not draft' - parameter :location_point_geojson, 'A GeoJSON point that situates the location the initiative applies to' - parameter :location_description, 'A human readable description of the location the initiative applies to' - parameter :header_bg, 'Base64 encoded header image' - parameter :topic_ids, 'Array of ids of the associated topics' - parameter :area_ids, 'Array of ids of the associated areas' - parameter :assignee_id, 'The user id of the admin that takes ownership. Set automatically if not provided. Only allowed for admins.' - parameter :anonymous, 'Post this initiative anonymously - true/false' - parameter :cosponsor_ids, 'Array of user ids of the desired cosponsors' - end - ValidationErrorHelper.new.error_fields(self, Initiative) - - let(:initiative) { build(:initiative) } - let(:publication_status) { 'published' } - let(:title_multiloc) { initiative.title_multiloc } - let(:body_multiloc) { initiative.body_multiloc } - let(:location_point_geojson) { { type: 'Point', coordinates: [51.11520776293035, 3.921154106874878] } } - let(:location_description) { 'Stanley Road 4' } - let(:header_bg) { file_as_base64 'header.jpg', 'image/jpeg' } - let(:topic_ids) { create_list(:topic, 2).map(&:id) } - let(:area_ids) { create_list(:area, 2).map(&:id) } - let(:assignee_id) { create(:admin).id } - - describe do - before do - @user.add_role 'admin' - @user.save! - end - - example_request 'Create an initiative' do - assert_status 201 - json_response = json_parse(response_body) - expect(json_response.dig(:data, :attributes, :location_point_geojson)).to eq location_point_geojson - expect(json_response.dig(:data, :attributes, :location_description)).to eq location_description - expect(json_response.dig(:data, :relationships, :topics, :data).pluck(:id)).to match_array topic_ids - expect(json_response.dig(:data, :relationships, :areas, :data).pluck(:id)).to match_array area_ids - expect(json_response.dig(:data, :relationships, :assignee, :data, :id)).to eq assignee_id - end - - example 'Check for the automatic creation of a like by the author when an initiative is created', document: false do - do_request - json_response = json_parse(response_body) - new_initiative = Initiative.find(json_response.dig(:data, :id)) - expect(new_initiative.reactions.size).to eq 1 - expect(new_initiative.reactions[0].mode).to eq 'up' - expect(new_initiative.reactions[0].user.id).to eq @user.id - expect(json_response[:data][:attributes][:likes_count]).to eq 1 - end - - example 'Check for the automatic assignement of the default assignee', document: false do - do_request(initiative: { assignee_id: nil }) - json_response = json_parse(response_body) - expect(json_response.dig(:data, :relationships, :assignee, :data, :id)).to eq @first_admin.id - end - end - - example_group 'with permissions on phase' do - let(:group) { create(:group) } - - before do - Permissions::PermissionsUpdateService.new.update_global_permissions - Permission.find_by(permission_scope: nil, action: 'posting_initiative') - .update!(permitted_by: 'users', groups: [group]) - end - - example '[error] Not authorized to create an initiative', document: false do - do_request - assert_status 401 - end - - example 'Create an initiative (group permission)' do - group.add_member(@user).save! - do_request - assert_status 201 - end - end - - describe do - before { SettingsService.new.activate_feature! 'blocking_profanity' } - - let(:title_multiloc) { { 'nl-BE' => 'Fuck' } } - let(:body_multiloc) { { 'fr-FR' => 'Fuck' } } - - example_request '[error] Create an initiative with blocked words' do - assert_status 422 - json_response = json_parse(response_body) - title_multiloc_error = json_response - .dig(:errors, :title_multiloc)&.select { |err| err[:error] == 'includes_banned_words' } - body_multiloc_error = json_response - .dig(:errors, :body_multiloc)&.select { |err| err[:error] == 'includes_banned_words' } - expect(title_multiloc_error).to be_present - expect(body_multiloc_error).to be_present - end - end - - describe 'anomymous posting of inititatives' do - let(:allow_anonymous_participation) { true } - let(:anonymous) { true } - - before do - config = AppConfiguration.instance - config.settings['initiatives']['allow_anonymous_participation'] = allow_anonymous_participation - config.save! - end - - example_request 'Create an anonymous initiative' do - assert_status 201 - expect(response_data.dig(:relationships, :author, :data, :id)).to be_nil - expect(response_data.dig(:attributes, :anonymous)).to be true - expect(response_data.dig(:attributes, :author_name)).to be_nil - end - - example 'Does not log activities for the author', document: false do - expect { do_request }.not_to have_enqueued_job(LogActivityJob).with(anything, anything, @user, anything) - end - - example 'Does not add the author as a follower of the initiative', document: false do - expect { do_request }.not_to change(Follower, :count) - initiative_id = response_data[:id] - expect(Follower.where(followable_id: initiative_id, user: @user)).not_to exist - end - - describe 'when anonymous posting is not allowed' do - let(:allow_anonymous_participation) { false } - - example_request 'Rejects the anonymous parameter' do - assert_status 422 - json_response = json_parse response_body - expect(json_response).to include_response_error(:base, 'anonymous_participation_not_allowed') - end - end - end - - describe 'cosponsor_ids' do - let(:cosponsor) { create(:user) } - let(:cosponsor_ids) { [cosponsor.id] } - - example 'Update the cosponsors of an initiative' do - expect { do_request } - .to have_enqueued_job(LogActivityJob) - .with(instance_of(CosponsorsInitiative), 'created', @user, instance_of(Integer)) - .exactly(1).times - - assert_status 201 - json_response = json_parse(response_body) - - expect(json_response.dig(:data, :relationships, :cosponsors, :data).pluck(:id)).to match_array cosponsor_ids - end - end - end - - # TODO: cleanup-after-proposals-migration - patch 'web_api/v1/initiatives/:id' do - before do - create(:initiative_status, code: 'proposed') - @initiative = create(:initiative, author: @user) - end - - with_options scope: :initiative do - parameter :author_id, 'The user id of the user owning the initiative. This can only be specified by moderators and is inferred from the JWT token for residents.' - parameter :publication_status, "Either #{Post::PUBLICATION_STATUSES.join(', ')}" - parameter :title_multiloc, 'Multi-locale field with the initiative title', extra: 'Maximum 100 characters' - parameter :body_multiloc, 'Multi-locale field with the initiative body', extra: 'Required if not draft' - parameter :location_point_geojson, 'A GeoJSON point that situates the location the initiative applies to' - parameter :location_description, 'A human readable description of the location the initiative applies to' - parameter :header_bg, 'Base64 encoded header image' - parameter :topic_ids, 'Array of ids of the associated topics' - parameter :area_ids, 'Array of ids of the associated areas' - parameter :assignee_id, 'The user id of the admin that takes ownership. Only allowed for admins.' - parameter :anonymous, 'Post this initiative anonymously - true/false' - parameter :cosponsor_ids, 'Array of user ids of the desired cosponsors' - end - ValidationErrorHelper.new.error_fields(self, Initiative) - - describe 'published initiatives' do - let(:id) { @initiative.id } - let(:location_point_geojson) { { type: 'Point', coordinates: [51.4365635, 3.825930459] } } - let(:location_description) { 'Watkins Road 8' } - let(:header_bg) { file_as_base64 'header.jpg', 'image/jpeg' } - let(:topic_ids) { create_list(:topic, 2).map(&:id) } - let(:area_ids) { create_list(:area, 2).map(&:id) } - - describe do - let(:title_multiloc) { { 'en' => 'Changed title' } } - - example_request 'Update an initiative' do - expect(status).to be 200 - json_response = json_parse(response_body) - expect(json_response.dig(:data, :attributes, :title_multiloc, :en)).to eq 'Changed title' - expect(json_response.dig(:data, :attributes, :location_point_geojson)).to eq location_point_geojson - expect(json_response.dig(:data, :attributes, :location_description)).to eq location_description - expect(json_response.dig(:data, :relationships, :topics, :data).pluck(:id)).to match_array topic_ids - expect(json_response.dig(:data, :relationships, :areas, :data).pluck(:id)).to match_array area_ids - end - end - - describe do - let(:id) { @initiative.id } - let(:header_bg) { file_as_base64 'header.jpg', 'image/jpeg' } - - example 'The header image can be updated and the file is present', document: false do - do_request - expect(@initiative.reload.header_bg_url).to be_present - expect(@initiative.reload.header_bg.file).to be_present - end - end - - describe do - let(:id) { @initiative.id } - - example 'The header image can be removed' do - @initiative.update!(header_bg: Rails.root.join('spec/fixtures/header.jpg').open) - expect(@initiative.reload.header_bg_url).to be_present - do_request initiative: { header_bg: nil } - expect(@initiative.reload.header_bg_url).to be_nil - end - end - - describe do - let(:topic_ids) { [] } - let(:area_ids) { [] } - - example 'Remove the topics/areas', document: false do - @initiative.topics = create_list(:topic, 2) - @initiative.areas = create_list(:area, 2) - do_request - expect(status).to be 200 - json_response = json_parse(response_body) - expect(json_response.dig(:data, :relationships, :topics, :data).pluck(:id)).to match_array topic_ids - expect(json_response.dig(:data, :relationships, :areas, :data).pluck(:id)).to match_array area_ids - end - end - - describe do - let(:assignee_id) { create(:admin).id } - - example 'Changing the assignee as a non-admin does not work', document: false do - do_request - expect(status).to be 200 - json_response = json_parse(response_body) - expect(json_response.dig(:data, :relationships, :assignee)).to be_nil - end - end - - describe 'changing the author of my own initiative' do - let(:author_id) { create(:admin).id } - - example '[Error] Cannot change the author from your own id as a non-admin', document: false do - do_request - expect(@initiative.reload.author_id).not_to eq author_id - end - end - - describe 'changing the author of another authors initiative' do - let(:author_id) { @user.id } - - example '[Error] Cannot update another authors record to your own id as a non-admin', document: false do - @initiative.update!(author: create(:user)) - do_request - assert_status 401 - expect(json_response_body.dig(:errors, :base, 0, :error)).to eq 'Unauthorized!' - end - end - - describe 'updating anomymous initiatives' do - let(:allow_anonymous_participation) { true } - let(:anonymous) { true } - - before do - config = AppConfiguration.instance - config.settings['initiatives']['allow_anonymous_participation'] = allow_anonymous_participation - config.save! - end - - example_request 'Change a published initiative to anonymous' do - assert_status 200 - expect(response_data.dig(:relationships, :author, :data, :id)).to be_nil - expect(response_data.dig(:attributes, :anonymous)).to be true - expect(response_data.dig(:attributes, :author_name)).to be_nil - end - - example '[Error] Cannot update an anonymous initiative as a non-admin' do - @initiative.update!(anonymous: true) - do_request - assert_status 401 - expect(json_response_body.dig(:errors, :base, 0, :error)).to eq 'Unauthorized!' - end - - example 'Does not log activities for the author and clears the author from past activities', document: false do - clear_activity = create(:activity, item: @initiative, user: @user) - other_item_activity = create(:activity, item: @initiative, user: create(:user)) - other_user_activity = create(:activity, user: @user) - - expect { do_request }.not_to have_enqueued_job(LogActivityJob).with(anything, anything, @user, anything) - expect(clear_activity.reload.user_id).to be_nil - expect(other_item_activity.reload.user_id).to be_present - expect(other_user_activity.reload.user_id).to eq @user.id - end - - describe 'when anonymous posting is not allowed' do - let(:allow_anonymous_participation) { false } - - example_request 'Rejects the anonymous parameter' do - assert_status 422 - json_response = json_parse response_body - expect(json_response).to include_response_error(:base, 'anonymous_participation_not_allowed') - end - end - end - end - - describe 'draft initiatives' do - before do - @initiative = create(:initiative, author: @user, publication_status: 'draft') - end - - parameter :publication_status, "Either #{Post::PUBLICATION_STATUSES.join(', ')}", required: true, scope: :initiative - - let(:id) { @initiative.id } - - describe 'updating anomymous initiatives' do - let(:anonymous) { true } - - before do - config = AppConfiguration.instance - config.settings['initiatives']['allow_anonymous_participation'] = true - config.save! - end - - example_request 'Change a draft initiative to anonymous' do - assert_status 200 - expect(response_data.dig(:relationships, :author, :data, :id)).to be_nil - expect(response_data.dig(:attributes, :anonymous)).to be true - expect(response_data.dig(:attributes, :author_name)).to be_nil - end - end - - describe 'publishing an initiative' do - let(:publication_status) { 'published' } - - example_request 'Change the publication status' do - assert_status 200 - expect(response_data.dig(:attributes, :publication_status)).to eq 'published' - end - end - - describe 'changing a draft initiative of another user' do - let(:title_multiloc) { { 'en' => 'Changed title' } } - - example '[Error] Cannot update an anonymous initiative as a non-admin' do - @initiative.update!(author: create(:user)) - do_request - assert_status 401 - expect(json_response_body.dig(:errors, :base, 0, :error)).to eq 'Unauthorized!' - end - end - - describe 'cosponsor_ids' do - let(:id) { @initiative.id } - let(:cosponsor) { create(:user) } - let(:cosponsor_ids) { [cosponsor.id] } - - example 'Update the cosponsors of an initiative' do - expect { do_request } - .to have_enqueued_job(LogActivityJob) - .with(instance_of(CosponsorsInitiative), 'created', @user, instance_of(Integer)) - .exactly(1).times - - assert_status 200 - json_response = json_parse(response_body) - - expect(json_response.dig(:data, :relationships, :cosponsors, :data).pluck(:id)).to match_array cosponsor_ids - end - end - end - end - - # TODO: move-old-proposals-test - patch 'web_api/v1/initiatives/:id/accept_cosponsorship_invite' do - before do - @initiative = create(:initiative) - @cosponsors_initiative = create(:cosponsors_initiative, initiative: @initiative, user: @user) - end - - describe 'for initiative with associated cosponsor' do - let(:id) { @initiative.id } - - example 'cosponsor accepts invitation' do - expect { do_request }.to change { @cosponsors_initiative.reload.status }.from('pending').to('accepted') - assert_status 200 - json_response = json_parse(response_body) - expect(json_response.dig(:data, :attributes, :slug)).to eq @initiative.slug - end - end - end - - # TODO: cleanup-after-proposals-migration - delete 'web_api/v1/initiatives/:id' do - before do - @initiative = create(:initiative_with_topics, author: @user, publication_status: 'published') - end - - let(:id) { @initiative.id } - - example_request 'Delete an initiative' do - assert_status 200 - expect { Initiative.find(id) }.to raise_error(ActiveRecord::RecordNotFound) - end - end - - # TODO: cleanup-after-proposals-migration - get 'web_api/v1/initiatives/:id/allowed_transitions' do - before do - admin_header_token - - @initiative = create(:initiative) - threshold_reached = create(:initiative_status_threshold_reached) - create( - :initiative_status_change, - initiative: @initiative, initiative_status: threshold_reached - ) - end - - let(:id) { @initiative.id } - - example_request 'Allowed transitions' do - assert_status 200 - json_response = json_parse response_body - expect(json_response.dig(:data, :type)).to eq 'allowed_transitions' - expect(json_response).to eq({ - data: { - type: 'allowed_transitions', - attributes: { - **InitiativeStatus.where(code: 'answered').ids.to_h do |id| - [id.to_sym, { feedback_required: true }] - end, - **InitiativeStatus.where(code: 'ineligible').ids.to_h do |id| - [id.to_sym, { feedback_required: true }] - end - } - } - }) - end - end -end From 14c692e547c91e8e08ecba0d5befbcbb409f9a82 Mon Sep 17 00:00:00 2001 From: Simon Tharby Date: Tue, 17 Dec 2024 12:32:39 +0000 Subject: [PATCH 04/63] [TAN-2538] Fix test in rack_attack_spec --- back/spec/requests/rack_attack_spec.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/back/spec/requests/rack_attack_spec.rb b/back/spec/requests/rack_attack_spec.rb index c46ac9a86fc1..d99535eb6693 100644 --- a/back/spec/requests/rack_attack_spec.rb +++ b/back/spec/requests/rack_attack_spec.rb @@ -248,16 +248,16 @@ it 'limits search requests from same IP to 15 in 20 seconds' do freeze_time do 15.times do - get '/web_api/v1/initiatives?search=some-random-search-term' + get '/web_api/v1/ideas?search=some-random-search-term' end expect(status).to eq(200) # OK - get '/web_api/v1/initiatives?search=some-random-search-term' + get '/web_api/v1/ideas?search=some-random-search-term' expect(status).to eq(429) # Too many requests end travel_to(20.seconds.from_now) do - get '/web_api/v1/initiatives?search=some-random-search-term' + get '/web_api/v1/ideas?search=some-random-search-term' expect(status).to eq(200) # OK end end From 2c6c10c2754d8ac9fa8426f55a651e74a1adbe8d Mon Sep 17 00:00:00 2001 From: Simon Tharby Date: Tue, 17 Dec 2024 12:36:24 +0000 Subject: [PATCH 05/63] [TAN-2538] Fix test in followers_spec --- back/spec/acceptance/followers_spec.rb | 6 ------ 1 file changed, 6 deletions(-) diff --git a/back/spec/acceptance/followers_spec.rb b/back/spec/acceptance/followers_spec.rb index 23db18c2f4af..ba4da1cb4a85 100644 --- a/back/spec/acceptance/followers_spec.rb +++ b/back/spec/acceptance/followers_spec.rb @@ -50,7 +50,6 @@ end end - # TODO: cleanup-after-proposals-migration get 'web_api/v1/followers/:id' do let(:id) { create(:follower, user: user).id } @@ -77,11 +76,6 @@ resource: 'ideas', factory: 'idea' }, - { - type: 'initiative', - resource: 'initiatives', - factory: 'initiative' - }, { type: 'topic', resource: 'topics', From f59794f87569c66a02a6ac3f00bf8194771adf87 Mon Sep 17 00:00:00 2001 From: Simon Tharby Date: Tue, 17 Dec 2024 12:38:56 +0000 Subject: [PATCH 06/63] [TAN-2538] Remove tests from internal_comments_spec --- .../spec/acceptance/internal_comments_spec.rb | 273 ------------------ 1 file changed, 273 deletions(-) diff --git a/back/spec/acceptance/internal_comments_spec.rb b/back/spec/acceptance/internal_comments_spec.rb index 38db842df0d6..5387810b3ec4 100644 --- a/back/spec/acceptance/internal_comments_spec.rb +++ b/back/spec/acceptance/internal_comments_spec.rb @@ -294,277 +294,4 @@ end end end - - # TODO: cleanup-after-proposals-migration - context 'when internal comments are on an initiative' do - before { @initiative = create(:initiative) } - - context 'when an authenticated regular user' do - before do - @user = create(:user) - header_token_for @user - end - - post 'web_api/v1/initiatives/:initiative_id/internal_comments' do - with_options scope: :internal_comment do - parameter :body, 'Text field with the comment body', required: true - parameter :parent_id, 'The id of the comment this comment is a response to', required: false - end - - let(:initiative_id) { @initiative.id } - let(:internal_comment) { build(:internal_comment) } - let(:body) { internal_comment.body } - - example_request '[Unauthorized] Create an internal comment on an initiative' do - assert_status 401 - json_response = json_parse(response_body) - - expect(json_response.dig(:errors, :base)[0][:error]).to eq 'Unauthorized!' - expect(@initiative.reload.internal_comments_count).to eq 0 - end - end - end - - context 'when an authenticated admin' do - before do - @user = create(:admin) - header_token_for @user - end - - get 'web_api/v1/internal_comments/:id' do - let(:initiative) { create(:initiative) } - let(:parent) { create(:internal_comment, post: initiative) } - let(:internal_comment) { create(:internal_comment, parent: parent, post: initiative) } - let(:id) { internal_comment.id } - - example_request 'Get one internal comment by id' do - assert_status 200 - json_response = json_parse(response_body) - - expect(json_response.dig(:data, :id)).to eq id - expect(json_response.dig(:data, :type)).to eq 'internal_comment' - expect(json_response.dig(:data, :attributes)).to include(publication_status: 'published') - expect(json_response.dig(:data, :relationships)).to include( - post: { - data: { id: internal_comment.post_id, type: 'initiative' } - }, - author: { - data: { id: internal_comment.author_id, type: 'user' } - }, - parent: { - data: { id: parent.id, type: 'internal_comment' } - } - ) - expect(json_response.dig(:included, 0, :attributes)).to include( - first_name: internal_comment.author.first_name, - locale: internal_comment.author.locale - ) - end - end - - get 'web_api/v1/initiatives/:initiative_id/internal_comments' do - with_options scope: :page do - parameter :number, 'Page number' - parameter :size, 'Number of top-level comments per page. The response will include 2 to 5 child comments per top-level comment, so expect to receive more' - end - parameter :sort, 'Either new or -new. Defaults to -new. Only applies to the top-level comments, children are always returned chronologically.' - - describe do - before do - @c1 = create(:internal_comment, post: @initiative) - @c2 = create(:internal_comment, post: @initiative) - @c1sub1 = create(:internal_comment, parent: @c2, post: @initiative) - @c1sub2 = create(:internal_comment, parent: @c2, post: @initiative) - @c1sub3 = create(:internal_comment, parent: @c2, post: @initiative) - @c1sub4 = create(:internal_comment, parent: @c2, post: @initiative) - @c1sub5 = create(:internal_comment, parent: @c2, post: @initiative) - @c3 = create(:internal_comment, post: @initiative) - @c3sub1 = create(:internal_comment, parent: @c3, post: @initiative) - @c3sub2 = create(:internal_comment, parent: @c3, post: @initiative) - @c3sub3 = create(:internal_comment, parent: @c3, post: @initiative) - @c3sub4 = create(:internal_comment, parent: @c3, post: @initiative) - @c3sub5 = create(:internal_comment, parent: @c3, post: @initiative) - @c3sub6 = create(:internal_comment, parent: @c3, post: @initiative) - @c4 = create(:internal_comment, post: @initiative) - @c4sub1 = create(:internal_comment, parent: @c4, post: @initiative) - end - - let(:initiative_id) { @initiative.id } - let(:size) { 3 } - - example_request 'List the top-level internal comments of an initiative' do - assert_status 200 - json_response = json_parse(response_body) - expect(json_response[:data].size).to eq 10 - expect(json_response[:data].pluck(:id)).to eq([ - @c1, - @c2, - @c1sub1, - @c1sub2, - @c1sub3, - @c1sub4, - @c1sub5, - @c3, - @c3sub5, - @c3sub6 - ].map(&:id)) - expect(json_response[:links][:next]).to be_present - end - end - - describe do - let(:initiative_id) { @initiative.id } - let(:sort) { '-new' } - - before do - @c1 = create(:internal_comment, post: @initiative, created_at: 1.day.ago) - @c2 = create(:internal_comment, post: @initiative, created_at: 2.days.ago) - @c3 = create(:internal_comment, post: @initiative, created_at: 3.days.ago) - @c2sub1, @c2sub2 = create_list(:internal_comment, 2, parent: @c2, post: @initiative) - end - - example_request 'List the top-level internal comments of an initiative sorted by age, with oldest first' do - assert_status 200 - json_response = json_parse(response_body) - expect(json_response[:data].size).to eq 5 - expect(json_response[:data].pluck(:id)).to eq([ - @c3, - @c2, - @c2sub1, - @c2sub2, - @c1 - ].map(&:id)) - end - end - end - - get 'web_api/v1/internal_comments/:internal_comment_id/children' do - explanation 'Children are always returned chronologically' - with_options scope: :page do - parameter :number, 'Page number' - parameter :size, 'Number of internal comments per page' - end - - describe do - before do - @c = create(:internal_comment, post: @initiative) - @csub1 = create(:internal_comment, parent: @c, post: @initiative) - @csub2 = create(:internal_comment, parent: @c, post: @initiative) - @csub3 = create(:internal_comment, parent: @c, post: @initiative) - @csub4 = create(:internal_comment, parent: @c, post: @initiative) - @csub5 = create(:internal_comment, parent: @c, post: @initiative) - @csub6 = create(:internal_comment, parent: @c, post: @initiative) - @c2 = create(:internal_comment, post: @initiative) - end - - let(:internal_comment_id) { @c.id } - - example_request 'List the direct child internal comments of an internal comment on an initiative' do - assert_status 200 - json_response = json_parse(response_body) - expect(json_response[:data].size).to eq 6 - expect(json_response[:data].pluck(:id)).to eq([ - @csub1, - @csub2, - @csub3, - @csub4, - @csub5, - @csub6 - ].map(&:id)) - end - end - end - - post 'web_api/v1/initiatives/:initiative_id/internal_comments' do - with_options scope: :internal_comment do - parameter :body, 'Text field with the comment body', required: true - parameter :parent_id, 'The id of the comment this comment is a response to', required: false - end - - let(:initiative_id) { @initiative.id } - let(:internal_comment) { build(:internal_comment) } - let(:body) { internal_comment.body } - - example_request 'Create an internal comment on an initiative' do - assert_status 201 - json_response = json_parse(response_body) - - expect(json_response.dig(:data, :relationships, :author, :data, :id)).to eq @user.id - expect(json_response.dig(:data, :attributes, :body)).to match body - expect(json_response.dig(:data, :relationships, :parent, :data)).to be_nil - expect(json_response.dig(:data, :relationships, :post, :data, :id)).to eq initiative_id - expect(@initiative.reload.internal_comments_count).to eq 1 - end - - describe do - let(:parent_id) { create(:internal_comment, post: @initiative).id } - - example_request 'Create an internal comment on an internal comment' do - assert_status 201 - json_response = json_parse(response_body) - - expect(json_response.dig(:data, :relationships, :author, :data, :id)).to eq @user.id - expect(json_response.dig(:data, :attributes, :body)).to match body - expect(json_response.dig(:data, :relationships, :parent, :data, :id)).to eq parent_id - expect(json_response.dig(:data, :relationships, :post, :data, :id)).to eq initiative_id - expect(@initiative.reload.internal_comments_count).to eq 2 - end - end - - describe do - let(:body) { '' } - - example_request '[error] Create an invalid internal comment' do - assert_status 422 - json_response = json_parse response_body - expect(json_response).to include_response_error(:body, 'blank') - end - end - end - - patch 'web_api/v1/internal_comments/:id' do - with_options scope: :internal_comment do - parameter :body, 'Text field with the comment body' - parameter :parent_id, 'The id of the internal comment this internal comment is a response to' - end - - let(:internal_comment) { create(:internal_comment, author: @user, post: @initiative) } - let(:id) { internal_comment.id } - let(:body) { "His hair is not blond, it's orange. Get your facts straight!" } - - example_request "Update author's own internal comment on an initiative" do - assert_status 200 - json_response = json_parse(response_body) - expect(json_response.dig(:data, :attributes, :body)).to match body - expect(@initiative.reload.internal_comments_count).to eq 1 - end - - example "[Unauthorized] Update other admin's internal comment on an initiative" do - internal_comment.update!(author: create(:admin)) - - do_request - assert_status 401 - expect(@initiative.reload.internal_comments_count).to eq 1 - end - end - - patch 'web_api/v1/internal_comments/:id/mark_as_deleted' do - let(:internal_comment) { create(:internal_comment, author: @user, post: @initiative) } - let(:id) { internal_comment.id } - - example_request "Author marks their own internal comment as 'deleted'" do - assert_status 204 - expect(internal_comment.reload.publication_status).to eq('deleted') - end - - example "[Unauthorized] Admin (not author) marks an internal comment on an initiative as 'deleted'" do - internal_comment.update!(author: create(:admin)) - - do_request - assert_status 401 - expect(internal_comment.reload.publication_status).to eq('published') - end - end - end - end end From 72b13a26d4d7e2b1da5746926d5221f84e094ddf Mon Sep 17 00:00:00 2001 From: Simon Tharby Date: Tue, 17 Dec 2024 14:44:25 +0000 Subject: [PATCH 07/63] [TAN-2538] Remove serializers & spec --- .../web_api/v1/initiative_serializer.rb | 98 ---------------- .../v1/initiative_status_change_serializer.rb | 10 -- .../v1/initiative_status_serializer.rb | 9 -- .../web_api/v1/initiative_serializer_spec.rb | 105 ------------------ 4 files changed, 222 deletions(-) delete mode 100644 back/app/serializers/web_api/v1/initiative_serializer.rb delete mode 100644 back/app/serializers/web_api/v1/initiative_status_change_serializer.rb delete mode 100644 back/app/serializers/web_api/v1/initiative_status_serializer.rb delete mode 100644 back/spec/serializers/web_api/v1/initiative_serializer_spec.rb diff --git a/back/app/serializers/web_api/v1/initiative_serializer.rb b/back/app/serializers/web_api/v1/initiative_serializer.rb deleted file mode 100644 index e3acfcd4cffb..000000000000 --- a/back/app/serializers/web_api/v1/initiative_serializer.rb +++ /dev/null @@ -1,98 +0,0 @@ -# frozen_string_literal: true - -class WebApi::V1::InitiativeSerializer < WebApi::V1::BaseSerializer - attributes :title_multiloc, - :slug, - :publication_status, - :likes_count, - :comments_count, - :internal_comments_count, - :official_feedbacks_count, - :followers_count, - :location_point_geojson, - :location_description, - :created_at, - :updated_at, - :published_at, - :expires_at, - :reactions_needed, - :anonymous, - :author_hash, - :editing_locked, - :public, - :proposed_at - - attribute :author_name do |object, params| - name_service = UserDisplayNameService.new(AppConfiguration.instance, current_user(params)) - name_service.display_name!(object.author) - end - - attribute :body_multiloc do |object| - TextImageService.new.render_data_images_multiloc object.body_multiloc, field: :body_multiloc, imageable: object - end - - attribute :header_bg do |object| - object.header_bg && object.header_bg.versions.to_h { |k, v| [k.to_s, v.url] } - end - - attribute :internal_comments_count, if: proc { |object, params| - can_moderate?(object, params) - } - - attribute :cosponsorships do |object, params| - name_service = UserDisplayNameService.new(AppConfiguration.instance, current_user(params)) - - object.cosponsors_initiatives.includes(:user).map do |ci| - { user_id: ci.user_id, name: name_service.display_name!(ci.user), status: ci.status } - end - end - - attribute :public do |object| - object.initiative_status ? object.initiative_status.public? : false - end - - has_many :initiative_images, serializer: WebApi::V1::ImageSerializer - has_many :topics - has_many :areas - has_many :cosponsors, record_type: :user, serializer: WebApi::V1::UserSerializer - - belongs_to :author, record_type: :user, serializer: WebApi::V1::UserSerializer - belongs_to :initiative_status - belongs_to :assignee, if: proc { |object, params| - can_moderate? object, params - }, record_type: :user, serializer: WebApi::V1::UserSerializer - - has_one :user_reaction, if: proc { |object, params| - signed_in? object, params - }, record_type: :reaction, serializer: WebApi::V1::ReactionSerializer do |object, params| - cached_user_reaction object, params - end - - has_one :user_follower, record_type: :follower, if: proc { |object, params| - signed_in? object, params - } do |object, params| - user_follower object, params - end - - def self.can_moderate?(_object, params) - current_user(params) && UserRoleService.new.can_moderate_initiatives?(current_user(params)) - end - - def self.cached_user_reaction(object, params) - if params[:vbii] - params.dig(:vbii, object.id) - else - object.reactions.where(user_id: current_user(params)&.id).first - end - end - - def self.user_follower(object, params) - if params[:user_followers] - params.dig(:user_followers, [object.id, 'Initiative'])&.first - else - current_user(params)&.follows&.find do |follow| - follow.followable_id == object.id && follow.followable_type == 'Initiative' - end - end - end -end diff --git a/back/app/serializers/web_api/v1/initiative_status_change_serializer.rb b/back/app/serializers/web_api/v1/initiative_status_change_serializer.rb deleted file mode 100644 index 950140ce95ab..000000000000 --- a/back/app/serializers/web_api/v1/initiative_status_change_serializer.rb +++ /dev/null @@ -1,10 +0,0 @@ -# frozen_string_literal: true - -class WebApi::V1::InitiativeStatusChangeSerializer < WebApi::V1::BaseSerializer - attribute :created_at, :updated_at - - belongs_to :initiative_status - belongs_to :initiative - belongs_to :user - belongs_to :official_feedback -end diff --git a/back/app/serializers/web_api/v1/initiative_status_serializer.rb b/back/app/serializers/web_api/v1/initiative_status_serializer.rb deleted file mode 100644 index 7f32a528a01d..000000000000 --- a/back/app/serializers/web_api/v1/initiative_status_serializer.rb +++ /dev/null @@ -1,9 +0,0 @@ -# frozen_string_literal: true - -class WebApi::V1::InitiativeStatusSerializer < WebApi::V1::BaseSerializer - attributes :title_multiloc, :description_multiloc, :ordering, :code, :color - - attribute :transition_type do |object| - InitiativeStatusService.new.transition_type object - end -end diff --git a/back/spec/serializers/web_api/v1/initiative_serializer_spec.rb b/back/spec/serializers/web_api/v1/initiative_serializer_spec.rb deleted file mode 100644 index 62f5ef698f69..000000000000 --- a/back/spec/serializers/web_api/v1/initiative_serializer_spec.rb +++ /dev/null @@ -1,105 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -describe WebApi::V1::InitiativeSerializer do - context "with 'abbreviated user names' enabled" do - before { SettingsService.new.activate_feature! 'abbreviated_user_names' } - - let(:jane) { create(:user, first_name: 'Jane', last_name: 'Doe') } - let(:john) { create(:user, first_name: 'John', last_name: 'Smith') } - let(:admin) { create(:admin, first_name: 'Thomas', last_name: 'Anderson') } - - it 'should abbreviate the author name' do - jane_initiative = create(:initiative, author: jane) - last_name = described_class - .new(jane_initiative, params: { current_user: john }) - .serializable_hash - .dig(:data, :attributes, :author_name) - expect(last_name).to eq 'Jane D.' - end - - it 'should not abbreviate user names for admins' do - jane_initiative = create(:initiative, author: jane) - last_name = described_class - .new(jane_initiative, params: { current_user: admin }) - .serializable_hash - .dig(:data, :attributes, :author_name) - expect(last_name).to eq 'Jane Doe' - - admin_initiative = create(:initiative, author: admin) - last_name = described_class - .new(admin_initiative, params: { current_user: john }) - .serializable_hash - .dig(:data, :attributes, :author_name) - expect(last_name).to eq 'Thomas Anderson' - end - end - - context 'when serializing internal comments count of initiative' do - let(:initiative) { create(:initiative) } - - before do - create_list(:internal_comment, 2, post: initiative) - initiative.reload - end - - context 'when current user is nil (visitor)' do - it 'should not include internal comments count' do - expect(internal_comments_count_for_current_user(initiative, nil)).to be_nil - end - end - - context 'when current user is regular user' do - it 'should not include internal comments count' do - expect(internal_comments_count_for_current_user(initiative, create(:user))).to be_nil - end - end - - context 'when current user is admin' do - it 'should include internal comments count' do - expect(internal_comments_count_for_current_user(initiative, create(:admin))).to eq 2 - end - end - end - - context 'when cosponsors of initiative exist' do - let(:initiative) { create(:initiative) } - let(:current_user) { create(:user) } - let(:cosponsor) { create(:user) } - let(:name_service) { UserDisplayNameService.new(AppConfiguration.instance, current_user) } - let(:cosponsor_display_name) { name_service.display_name!(cosponsor) } - let!(:_cosponsorship) { create(:cosponsors_initiative, initiative: initiative, user: cosponsor) } - - it 'should include cosponsorships' do - expect(cosponsorships(initiative, current_user).first[:user_id]).to eq cosponsor.id - expect(cosponsorships(initiative, current_user).first[:name]).to eq cosponsor_display_name - end - - it 'should include cosponsors' do - expect(cosponsors(initiative, current_user).size).to eq 1 - expect(cosponsors(initiative, current_user).first[:id]).to eq cosponsor.id - end - end - - def internal_comments_count_for_current_user(initiative, current_user) - described_class - .new(initiative, params: { current_user: current_user }) - .serializable_hash - .dig(:data, :attributes, :internal_comments_count) - end - - def cosponsors(initiative, current_user) - described_class - .new(initiative, params: { current_user: current_user }) - .serializable_hash - .dig(:data, :relationships, :cosponsors, :data) - end - - def cosponsorships(initiative, current_user) - described_class - .new(initiative, params: { current_user: current_user }) - .serializable_hash - .dig(:data, :attributes, :cosponsorships) - end -end From 7b704a2cc3eb323743bfc9d9ac254b0dc190cc82 Mon Sep 17 00:00:00 2001 From: Simon Tharby Date: Tue, 17 Dec 2024 15:06:19 +0000 Subject: [PATCH 08/63] [TAN-2538] Fix tests + remove from machine_translations controller & routes --- .../v1/machine_translations_controller.rb | 4 -- .../machine_translations/config/routes.rb | 3 -- .../acceptance/machine_translations_spec.rb | 49 ------------------- back/spec/acceptance/followers_spec.rb | 5 +- back/spec/acceptance/user_comments_spec.rb | 3 +- 5 files changed, 3 insertions(+), 61 deletions(-) diff --git a/back/engines/commercial/machine_translations/app/controllers/machine_translations/web_api/v1/machine_translations_controller.rb b/back/engines/commercial/machine_translations/app/controllers/machine_translations/web_api/v1/machine_translations_controller.rb index c39099e30117..b724b51e4035 100644 --- a/back/engines/commercial/machine_translations/app/controllers/machine_translations/web_api/v1/machine_translations_controller.rb +++ b/back/engines/commercial/machine_translations/app/controllers/machine_translations/web_api/v1/machine_translations_controller.rb @@ -11,10 +11,6 @@ class MachineTranslationsController < ApplicationController translatable_class: Idea, translatable_id: :idea_id }, - 'Initiative' => { - translatable_class: Initiative, - translatable_id: :initiative_id - }, 'Comment' => { translatable_class: Comment, translatable_id: :comment_id diff --git a/back/engines/commercial/machine_translations/config/routes.rb b/back/engines/commercial/machine_translations/config/routes.rb index e0f7df62a830..e5aa47c5db0f 100644 --- a/back/engines/commercial/machine_translations/config/routes.rb +++ b/back/engines/commercial/machine_translations/config/routes.rb @@ -6,9 +6,6 @@ defaults translatable_type: 'Idea' do get 'ideas/:idea_id/machine_translation', action: :show, controller: 'machine_translations' end - defaults translatable_type: 'Initiative' do - get 'initiatives/:initiative_id/machine_translation', action: :show, controller: 'machine_translations' - end defaults translatable_type: 'Comment' do get 'comments/:comment_id/machine_translation', action: :show, controller: 'machine_translations' end diff --git a/back/engines/commercial/machine_translations/spec/acceptance/machine_translations_spec.rb b/back/engines/commercial/machine_translations/spec/acceptance/machine_translations_spec.rb index 409347b9a1ae..406eb1bf6e19 100644 --- a/back/engines/commercial/machine_translations/spec/acceptance/machine_translations_spec.rb +++ b/back/engines/commercial/machine_translations/spec/acceptance/machine_translations_spec.rb @@ -80,55 +80,6 @@ end end - # TODO: cleanup-after-proposals-migration - get '/web_api/v1/initiatives/:initiative_id/machine_translation' do - with_options scope: :machine_translation do - parameter :attribute_name, 'The name of the attribute to translate (e.g. title_multiloc)' - parameter :locale_to, 'The locale to translate to' - end - - describe 'Get one machine translation:' do - let(:translation) { create(:machine_translation, translatable: create(:initiative, title_multiloc: { 'nl-BE' => 'Fietssnelweg doorheen het stadcentrum' })) } - let(:initiative_id) { translation.translatable_id } - let(:attribute_name) { translation.attribute_name } - let(:locale_to) { translation.locale_to } - - example_request 'Return an up to date translation if it has already been created' do - expect(status).to eq 200 - json_response = json_parse response_body - expect(json_response.dig(:data, :attributes, :translation)).to eq translation.translation - end - - example 'Update the translation if it has already been created but the original text might have changed' do - prev_updated_at = translation.updated_at - translation.translatable.update! title_multiloc: { 'nl-BE' => 'Fietssnelweg doorheen elke deelgemeente' } - do_request - expect(status).to eq 200 - expect(translation.reload.updated_at).to be > prev_updated_at - end - - example '[error] Create a translation for an unknown resource' do - translation.translatable.destroy! - do_request - expect(status).to eq 404 - end - end - - describe do - let(:translation) { nil } - let(:initiative_id) { create(:initiative).id } - let(:attribute_name) { 'title_multiloc' } - let(:locale_to) { 'en' } - - example_request 'A new machine translation is created when the translation was never done before' do - expect(status).to eq 200 - json_response = json_parse response_body - expect(json_response.dig(:data, :attributes, :attribute_name)).to eq attribute_name - expect(json_response.dig(:data, :relationships, :translatable, :data, :id)).to eq initiative_id - end - end - end - get '/web_api/v1/comments/:comment_id/machine_translation' do with_options scope: :machine_translation do parameter :attribute_name, 'The name of the attribute to translate (e.g. title_multiloc)' diff --git a/back/spec/acceptance/followers_spec.rb b/back/spec/acceptance/followers_spec.rb index ba4da1cb4a85..fead47c9d9e6 100644 --- a/back/spec/acceptance/followers_spec.rb +++ b/back/spec/acceptance/followers_spec.rb @@ -25,15 +25,14 @@ let!(:other_follow) { create(:follower, user: create(:user)) } let!(:folder_follows) { create_list(:project_folder, 2).map { |project| create(:follower, user: user, followable: project) } } let!(:idea_follows) { [create(:follower, user: user, followable: create(:idea))] } - let!(:initiative_follows) { [create(:follower, user: user, followable: create(:initiative))] } let!(:topic_follows) { [create(:follower, user: user, followable: create(:topic))] } let!(:area_follows) { [create(:follower, user: user, followable: create(:area))] } example_request 'List all followers' do assert_status 200 json_response = json_parse response_body - expect(json_response[:data].size).to eq 8 - expect(json_response[:data].pluck(:id)).to match_array (project_follows + folder_follows + idea_follows + initiative_follows + topic_follows + area_follows).map(&:id) + expect(json_response[:data].size).to eq 7 + expect(json_response[:data].pluck(:id)).to match_array (project_follows + folder_follows + idea_follows + topic_follows + area_follows).map(&:id) end describe do diff --git a/back/spec/acceptance/user_comments_spec.rb b/back/spec/acceptance/user_comments_spec.rb index 58072113a124..cf099a8e693a 100644 --- a/back/spec/acceptance/user_comments_spec.rb +++ b/back/spec/acceptance/user_comments_spec.rb @@ -21,7 +21,7 @@ describe do before do @i1 = create(:idea, published_at: Time.zone.now) - @i2 = create(:initiative, published_at: 1.day.ago) + @i2 = create(:idea, published_at: 1.day.ago) @i3 = create(:idea, published_at: 3.days.ago) @user = create(:user) @c1 = create(:comment, post: @i2, author: @user, created_at: 1.hour.ago) @@ -35,7 +35,6 @@ let(:user_id) { @user.id } let(:size) { 2 } - # TODO: posting_initiative example_request 'List the comments of a user' do expect(status).to eq(200) json_response = json_parse(response_body) From 512c00b8699554c334b308d9eb90c32823ebfbf9 Mon Sep 17 00:00:00 2001 From: Simon Tharby Date: Tue, 17 Dec 2024 15:16:17 +0000 Subject: [PATCH 09/63] [TAN-2538] Remove 8 initiative related policies & 4 related specs --- .../app/policies/initiative_comment_policy.rb | 58 --- back/app/policies/initiative_file_policy.rb | 25 -- back/app/policies/initiative_image_policy.rb | 25 -- .../initiative_official_feedback_policy.rb | 25 -- back/app/policies/initiative_policy.rb | 92 ---- .../policies/initiative_reaction_policy.rb | 45 -- .../initiative_status_change_policy.rb | 22 - back/app/policies/initiative_status_policy.rb | 43 -- .../initiative_comment_policy_spec.rb | 176 -------- back/spec/policies/initiative_policy_spec.rb | 417 ------------------ .../initiative_reaction_policy_spec.rb | 74 ---- .../policies/initiative_status_policy_spec.rb | 81 ---- 12 files changed, 1083 deletions(-) delete mode 100644 back/app/policies/initiative_comment_policy.rb delete mode 100644 back/app/policies/initiative_file_policy.rb delete mode 100644 back/app/policies/initiative_image_policy.rb delete mode 100644 back/app/policies/initiative_official_feedback_policy.rb delete mode 100644 back/app/policies/initiative_policy.rb delete mode 100644 back/app/policies/initiative_reaction_policy.rb delete mode 100644 back/app/policies/initiative_status_change_policy.rb delete mode 100644 back/app/policies/initiative_status_policy.rb delete mode 100644 back/spec/policies/initiative_comment_policy_spec.rb delete mode 100644 back/spec/policies/initiative_policy_spec.rb delete mode 100644 back/spec/policies/initiative_reaction_policy_spec.rb delete mode 100644 back/spec/policies/initiative_status_policy_spec.rb diff --git a/back/app/policies/initiative_comment_policy.rb b/back/app/policies/initiative_comment_policy.rb deleted file mode 100644 index 5b2a4bec46f2..000000000000 --- a/back/app/policies/initiative_comment_policy.rb +++ /dev/null @@ -1,58 +0,0 @@ -# frozen_string_literal: true - -class InitiativeCommentPolicy < ApplicationPolicy - class Scope < ApplicationPolicy::Scope - def resolve - scope.where(post: scope_for(Initiative)) - end - end - - def index_xlsx? - user&.admin? - end - - def create? - return unless active? - return true if admin? - - owner? && commenting_allowed?(user) - end - - def children? - show? - end - - def show? - policy_for(record.post).show? - end - - def update? - create? - end - - def mark_as_deleted? - update? - end - - def destroy? - false - end - - def permitted_attributes_for_update - attrs = %i[parent_id author_id] - if record.author_id == user&.id - attrs += [body_multiloc: CL2_SUPPORTED_LOCALES] - end - attrs - end - - private - - def commenting_allowed?(user) - !Permissions::InitiativePermissionsService.new(user).denied_reason_for_action 'commenting_initiative' - end - - def owner? - user && (record.author_id == user.id) - end -end diff --git a/back/app/policies/initiative_file_policy.rb b/back/app/policies/initiative_file_policy.rb deleted file mode 100644 index 910b88e275fc..000000000000 --- a/back/app/policies/initiative_file_policy.rb +++ /dev/null @@ -1,25 +0,0 @@ -# frozen_string_literal: true - -class InitiativeFilePolicy < ApplicationPolicy - class Scope < ApplicationPolicy::Scope - def resolve - scope.where(initiative: scope_for(Initiative)) - end - end - - def create? - policy_for(record.initiative).update? - end - - def show? - policy_for(record.initiative).show? - end - - def update? - policy_for(record.initiative).update? - end - - def destroy? - policy_for(record.initiative).update? - end -end diff --git a/back/app/policies/initiative_image_policy.rb b/back/app/policies/initiative_image_policy.rb deleted file mode 100644 index 8cd7e7a670b0..000000000000 --- a/back/app/policies/initiative_image_policy.rb +++ /dev/null @@ -1,25 +0,0 @@ -# frozen_string_literal: true - -class InitiativeImagePolicy < ApplicationPolicy - class Scope < ApplicationPolicy::Scope - def resolve - scope.where(initiative: scope_for(Initiative)) - end - end - - def create? - policy_for(record.initiative).update? - end - - def show? - policy_for(record.initiative).show? - end - - def update? - policy_for(record.initiative).update? - end - - def destroy? - policy_for(record.initiative).update? - end -end diff --git a/back/app/policies/initiative_official_feedback_policy.rb b/back/app/policies/initiative_official_feedback_policy.rb deleted file mode 100644 index 48e888bd605d..000000000000 --- a/back/app/policies/initiative_official_feedback_policy.rb +++ /dev/null @@ -1,25 +0,0 @@ -# frozen_string_literal: true - -class InitiativeOfficialFeedbackPolicy < ApplicationPolicy - class Scope < ApplicationPolicy::Scope - def resolve - scope.where(post: scope_for(Initiative)) - end - end - - def create? - user&.admin? - end - - def show? - policy_for(record.post).show? - end - - def update? - create? - end - - def destroy? - create? - end -end diff --git a/back/app/policies/initiative_policy.rb b/back/app/policies/initiative_policy.rb deleted file mode 100644 index faf4e8add487..000000000000 --- a/back/app/policies/initiative_policy.rb +++ /dev/null @@ -1,92 +0,0 @@ -# frozen_string_literal: true - -class InitiativePolicy < ApplicationPolicy - class Scope < ApplicationPolicy::Scope - def resolve - published = scope.where(publication_status: 'published') - - if UserRoleService.new.can_moderate_initiatives?(user) - published - elsif user&.active? - published.left_outer_joins(:cosponsors_initiatives).with_status_code(InitiativeStatus::NOT_REVIEW_CODES) - .or(published.where(author: user)) - .or(published.where(cosponsors_initiatives: { user: user })) - else - published.with_status_code(InitiativeStatus::NOT_REVIEW_CODES) - end - end - end - - def index_xlsx? - active? && UserRoleService.new.can_moderate_initiatives?(user) - end - - def create? - return true if active? && can_moderate? - - reason = posting_denied_reason user - raise_not_authorized reason if reason - - active? && owner? - end - - def show? - return true if active? && (owner? || cosponsor? || can_moderate?) - return false if record.review_status? - - true - end - - def by_slug? - show? - end - - def update? - return true if active? && can_moderate? - - create? && !record.editing_locked - end - - def destroy? - create? - end - - def accept_cosponsorship_invite? - cosponsor? - end - - def allowed_transitions? - can_moderate? - end - - def permitted_attributes - shared = [ - :publication_status, - :location_description, - :header_bg, - :anonymous, - { cosponsor_ids: [] }, - { location_point_geojson: [:type, { coordinates: [] }], - title_multiloc: CL2_SUPPORTED_LOCALES, - body_multiloc: CL2_SUPPORTED_LOCALES, - topic_ids: [], - area_ids: [] } - ] - - can_moderate? ? [:author_id, :assignee_id, *shared] : shared - end - - private - - def posting_denied_reason(user) - Permissions::InitiativePermissionsService.new(user).denied_reason_for_action 'posting_initiative' - end - - def owner? - user && record.author_id == user.id - end - - def cosponsor? - user && record&.cosponsors&.include?(user) - end -end diff --git a/back/app/policies/initiative_reaction_policy.rb b/back/app/policies/initiative_reaction_policy.rb deleted file mode 100644 index cc61ba356128..000000000000 --- a/back/app/policies/initiative_reaction_policy.rb +++ /dev/null @@ -1,45 +0,0 @@ -# frozen_string_literal: true - -class InitiativeReactionPolicy < ApplicationPolicy - class Scope < ApplicationPolicy::Scope - def resolve - if user&.admin? - scope.all - elsif user - scope.where(user: user) - else - scope.none - end - end - end - - def create? - return if !user&.active? || !owner? - - reason = reacting_denied_reason user - reason ? raise_not_authorized(reason) : true - end - - def destroy? - create? - end - - def up? - create? - end - - def down? - # TODO: JS - should this reason be in a permissions service - raise_not_authorized('dislikes_not_supported') - end - - def show? - active? && (owner? || admin?) - end - - private - - def reacting_denied_reason(user) - Permissions::InitiativePermissionsService.new(user).denied_reason_for_action 'reacting_initiative' - end -end diff --git a/back/app/policies/initiative_status_change_policy.rb b/back/app/policies/initiative_status_change_policy.rb deleted file mode 100644 index 137857fefdcc..000000000000 --- a/back/app/policies/initiative_status_change_policy.rb +++ /dev/null @@ -1,22 +0,0 @@ -# frozen_string_literal: true - -class InitiativeStatusChangePolicy < ApplicationPolicy - class Scope < ApplicationPolicy::Scope - def resolve - # Disabled - if user&.active? && user&.admin? - scope.all - else - scope.none - end - end - end - - def create? - user&.active? && user&.admin? - end - - def show? - user&.active? && user&.admin? - end -end diff --git a/back/app/policies/initiative_status_policy.rb b/back/app/policies/initiative_status_policy.rb deleted file mode 100644 index 0a34855457fc..000000000000 --- a/back/app/policies/initiative_status_policy.rb +++ /dev/null @@ -1,43 +0,0 @@ -# frozen_string_literal: true - -class InitiativeStatusPolicy < ApplicationPolicy - class Scope < ApplicationPolicy::Scope - def resolve - filter_out_review_statuses - end - - private - - # This method handles the case when review was turned on, - # some initiatives were created, and then review was turned off. - # We still want to show statuses of all used initiatives after that. - def filter_out_review_statuses - if UserRoleService.new.can_moderate_initiatives?(user) - if Initiative.review_required? - scope.all - else - review_initiatives = initiatives.with_status_code(InitiativeStatus::REVIEW_CODES) - - if review_initiatives.any? - scope.all # make it possible to change the status of initiatives that were created when review was on - else - scope.where(code: InitiativeStatus::NOT_REVIEW_CODES) - end - end - else - public_codes = InitiativeStatus::NOT_REVIEW_CODES - # We want to show only statuses of initiatives available to the current user. - user_codes = initiatives.joins(:initiative_status).distinct.pluck('initiative_status.code') - scope.where(code: (public_codes + user_codes).uniq) - end - end - - def initiatives - scope_for(Initiative) - end - end - - def show? - true - end -end diff --git a/back/spec/policies/initiative_comment_policy_spec.rb b/back/spec/policies/initiative_comment_policy_spec.rb deleted file mode 100644 index 1181e481d36d..000000000000 --- a/back/spec/policies/initiative_comment_policy_spec.rb +++ /dev/null @@ -1,176 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -describe InitiativeCommentPolicy do - subject { described_class.new(user, comment) } - - let(:scope) { InitiativeCommentPolicy::Scope.new(user, initiative.comments) } - - context 'on comment on initiative with proposed status' do - let(:initiative) { create(:initiative) } - - let!(:_initiative_status_change) do - create( - :initiative_status_change, - initiative: initiative, - initiative_status: create(:initiative_status_proposed) - ) - end - - let!(:comment) { create(:comment, post: initiative) } - - context 'for a visitor' do - let(:user) { nil } - - it { is_expected.to permit(:show) } - it { is_expected.not_to permit(:create) } - it { is_expected.not_to permit(:update) } - it { is_expected.not_to permit(:destroy) } - - it 'indexes the comment' do - expect(scope.resolve.size).to eq 1 - end - end - - context 'for a user who is not the author of the comment' do - let(:user) { create(:user) } - - it { is_expected.to permit(:show) } - it { is_expected.not_to permit(:create) } - it { is_expected.not_to permit(:update) } - it { is_expected.not_to permit(:destroy) } - - it 'indexes the comment' do - expect(scope.resolve.size).to eq 1 - end - end - - context 'for a user who is the author of the comment' do - let(:user) { comment.author } - - it { is_expected.to permit(:show) } - it { is_expected.to permit(:create) } - it { is_expected.to permit(:update) } - it { is_expected.not_to permit(:destroy) } - - it 'indexes the comment' do - expect(scope.resolve.size).to eq 1 - end - end - - context 'for blocked comment author' do - let(:user) { create(:user, block_end_at: 5.days.from_now) } - let(:comment) { create(:comment, author: user, post: initiative) } - - it_behaves_like 'policy for blocked user' - end - - context 'for an admin' do - let(:user) { create(:admin) } - - it { is_expected.to permit(:show) } - it { is_expected.to permit(:create) } - it { is_expected.to permit(:update) } - it { is_expected.not_to permit(:destroy) } - - it 'indexes the comment' do - expect(scope.resolve.size).to eq 1 - end - end - end - - context 'on comment on initiative with review_pending status' do - let(:initiative) { create(:initiative) } - - let!(:_initiative_status_change) do - create( - :initiative_status_change, - initiative: initiative, - initiative_status: create(:initiative_status_review_pending) - ) - end - - let!(:comment) { create(:comment, post: initiative, author: initiative.author) } - - context 'for a visitor' do - let(:user) { nil } - - it { is_expected.not_to permit(:show) } - it { is_expected.not_to permit(:create) } - it { is_expected.not_to permit(:update) } - it { is_expected.not_to permit(:destroy) } - - it 'does not index the comment' do - expect(scope.resolve.size).to eq 0 - end - end - - context 'for a user who is not the author of the initiative or the comment' do - let(:user) { create(:user) } - - it { is_expected.not_to permit(:show) } - it { is_expected.not_to permit(:create) } - it { is_expected.not_to permit(:update) } - it { is_expected.not_to permit(:destroy) } - - it 'does not index the comment' do - expect(scope.resolve.size).to eq 0 - end - end - - context 'for a user who is the author of the initiative and the comment' do - let(:user) { initiative.author } - - it { is_expected.to permit(:show) } - it { is_expected.to permit(:create) } - it { is_expected.to permit(:update) } - it { is_expected.not_to permit(:destroy) } - - it 'indexes the comment' do - expect(scope.resolve.size).to eq 1 - end - end - - context 'for blocked comment author' do - let(:user) { create(:user, block_end_at: 5.days.from_now) } - let(:comment) { create(:comment, author: user, post: initiative) } - - it { is_expected.not_to permit(:show) } - it { is_expected.not_to permit(:create) } - it { is_expected.not_to permit(:update) } - it { is_expected.not_to permit(:destroy) } - - it 'does not index the comment' do - expect(scope.resolve.size).to eq 0 - end - end - - context 'for an admin' do - let(:user) { create(:admin) } - - it { is_expected.to permit(:show) } - it { is_expected.to permit(:create) } - it { is_expected.to permit(:update) } - it { is_expected.not_to permit(:destroy) } - - it 'indexes the comment' do - expect(scope.resolve.size).to eq 1 - end - end - - context 'for a user who is cosponsor of the initiative' do - let(:user) { create(:user) } - let!(:cosponsors_initiative) { create(:cosponsors_initiative, user: user, initiative: initiative) } - - it { is_expected.to permit(:show) } - it { is_expected.not_to permit(:create) } - it { is_expected.not_to permit(:update) } - it { is_expected.not_to permit(:destroy) } - - it 'indexes the initiative' do - expect(scope.resolve.size).to eq 1 - end - end - end -end diff --git a/back/spec/policies/initiative_policy_spec.rb b/back/spec/policies/initiative_policy_spec.rb deleted file mode 100644 index 19c8f38c8814..000000000000 --- a/back/spec/policies/initiative_policy_spec.rb +++ /dev/null @@ -1,417 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -# TODO: cleanup-after-proposals-migration -describe InitiativePolicy do - subject(:policy) { described_class.new(user, initiative) } - - let(:scope) { InitiativePolicy::Scope.new(user, Initiative) } - let(:author) { create(:user) } - - # We need to consider 4 main scenarios: - # 1. When review feature is NOT active, and - # 2. When review feature is active - # a). When initiative has review_pending or review_rejected status - # b). when initiative has status other than review_pending or review_rejected - # 1a + 1b + 2a + 2b = 4 scenarios - # - # We avoid testing initiatives with 'custom' status, as it is not used and not adequately considered in the codebase. - - # ------------------- 1. Review feature is NOT active ------------------- - - context 'when review feature is NOT fully active' do - it 'is not active' do - expect(Initiative.review_required?).to be false - end - - # For statuses with REVIEW_CODES, we only show/index initiatives with review statuses to the author and admins. - context 'for an initiative with status review_pending' do - let!(:initiative) do - create(:initiative, author: author, initiative_status: create(:initiative_status_review_pending)) - end - - context 'for an admin' do - let(:user) { create(:admin) } - - it { is_expected.to permit(:show) } - it { is_expected.to permit(:by_slug) } - it { is_expected.to permit(:create) } - it { is_expected.to permit(:update) } - it { is_expected.to permit(:destroy) } - it { is_expected.not_to permit(:accept_cosponsorship_invite) } - - it 'indexes the initiative' do - expect(scope.resolve.size).to eq 1 - end - end - - context 'for a user who is not author of the initiative' do - let(:user) { create(:user) } - - it { is_expected.not_to permit(:show) } - it { is_expected.not_to permit(:by_slug) } - it { is_expected.not_to permit(:create) } - it { is_expected.not_to permit(:update) } - it { is_expected.not_to permit(:destroy) } - it { is_expected.not_to permit(:accept_cosponsorship_invite) } - - it 'does not index the initiative' do - expect(scope.resolve.size).to eq 0 - end - end - - context 'for a user who is author of the initiative' do - let(:user) { author } - - it { is_expected.to permit(:show) } - it { is_expected.to permit(:by_slug) } - it { is_expected.to permit(:create) } - it { is_expected.to permit(:update) } - it { is_expected.to permit(:destroy) } - it { is_expected.not_to permit(:accept_cosponsorship_invite) } - - it 'indexes the initiative' do - expect(scope.resolve.size).to eq 1 - end - end - - context 'for a user who is cosponsor of the initiative' do - let(:user) { create(:user) } - let!(:cosponsors_initiative) { create(:cosponsors_initiative, user: user, initiative: initiative) } - - it { is_expected.to permit(:show) } - it { is_expected.to permit(:by_slug) } - it { is_expected.not_to permit(:create) } - it { is_expected.not_to permit(:update) } - it { is_expected.not_to permit(:destroy) } - it { is_expected.to permit(:accept_cosponsorship_invite) } - - it 'indexes the initiative' do - expect(scope.resolve.size).to eq 1 - end - end - - context 'for a visitor' do - let(:user) { nil } - - it { is_expected.not_to permit(:show) } - it { is_expected.not_to permit(:by_slug) } - it { expect { policy.create? }.to raise_error(Pundit::NotAuthorizedError) } - it { expect { policy.update? }.to raise_error(Pundit::NotAuthorizedError) } - it { expect { policy.destroy? }.to raise_error(Pundit::NotAuthorizedError) } - it { is_expected.not_to permit(:accept_cosponsorship_invite) } - - it 'does not index the initiative' do - expect(scope.resolve.size).to eq 0 - end - end - end - - context 'for an initiative with proposed status' do - let!(:initiative) do - create(:initiative, author: author, initiative_status: create(:initiative_status_proposed)) - end - - context 'for an admin' do - let(:user) { create(:admin) } - - it { is_expected.to permit(:show) } - it { is_expected.to permit(:by_slug) } - it { is_expected.to permit(:create) } - it { is_expected.to permit(:update) } - it { is_expected.to permit(:destroy) } - it { is_expected.not_to permit(:accept_cosponsorship_invite) } - - it 'indexes the initiative' do - expect(scope.resolve.size).to eq 1 - end - end - - context 'for a user who is not author of the initiative' do - let(:user) { create(:user) } - - it { is_expected.to permit(:show) } - it { is_expected.to permit(:by_slug) } - it { is_expected.not_to permit(:create) } - it { is_expected.not_to permit(:update) } - it { is_expected.not_to permit(:destroy) } - it { is_expected.not_to permit(:accept_cosponsorship_invite) } - - it 'indexes the initiative' do - expect(scope.resolve.size).to eq 1 - end - end - - context 'for a user who is author of the initiative' do - let(:user) { author } - - it { is_expected.to permit(:show) } - it { is_expected.to permit(:by_slug) } - it { is_expected.to permit(:create) } - it { is_expected.to permit(:update) } - it { is_expected.to permit(:destroy) } - it { is_expected.not_to permit(:accept_cosponsorship_invite) } - - it 'indexes the initiative' do - expect(scope.resolve.size).to eq 1 - end - end - - context 'for a user who is cosponsor of the initiative' do - let(:user) { create(:user) } - let!(:cosponsors_initiative) { create(:cosponsors_initiative, user: user, initiative: initiative) } - - it { is_expected.to permit(:show) } - it { is_expected.to permit(:by_slug) } - it { is_expected.not_to permit(:create) } - it { is_expected.not_to permit(:update) } - it { is_expected.not_to permit(:destroy) } - it { is_expected.to permit(:accept_cosponsorship_invite) } - - it 'indexes the initiative' do - expect(scope.resolve.size).to eq 1 - end - end - - context 'for a visitor' do - let(:user) { nil } - - it { is_expected.to permit(:show) } - it { is_expected.to permit(:by_slug) } - it { expect { policy.create? }.to raise_error(Pundit::NotAuthorizedError) } - it { expect { policy.update? }.to raise_error(Pundit::NotAuthorizedError) } - it { expect { policy.destroy? }.to raise_error(Pundit::NotAuthorizedError) } - it { is_expected.not_to permit(:accept_cosponsorship_invite) } - - it 'indexes the initiative' do - expect(scope.resolve.size).to eq 1 - end - end - end - end - - # ------------------- 2. Review feature IS active ---------------------- - - context 'when review feature IS fully active' do - before do - SettingsService.new.activate_feature! 'initiative_review' - - configuration = AppConfiguration.instance - configuration.settings['initiatives']['require_review'] = true - configuration.save! - end - - it 'is active' do - expect(Initiative.review_required?).to be true - end - - # For statuses with REVIEW_CODES, we only show initiatives with review statuses to the author and admins. - context 'for an initiative with review_pending status' do - let!(:initiative) do - create(:initiative, author: author, initiative_status: create(:initiative_status_review_pending)) - end - - context 'for an admin' do - let(:user) { create(:admin) } - - it { is_expected.to permit(:show) } - it { is_expected.to permit(:by_slug) } - it { is_expected.to permit(:create) } - it { is_expected.to permit(:update) } - it { is_expected.to permit(:destroy) } - it { is_expected.not_to permit(:accept_cosponsorship_invite) } - - it 'indexes the initiative' do - expect(scope.resolve.size).to eq 1 - end - end - - context 'for a user who is not author of the initiative' do - let(:user) { create(:user) } - - it { is_expected.not_to permit(:show) } - it { is_expected.not_to permit(:by_slug) } - it { is_expected.not_to permit(:create) } - it { is_expected.not_to permit(:update) } - it { is_expected.not_to permit(:destroy) } - it { is_expected.not_to permit(:accept_cosponsorship_invite) } - - it 'does not index the initiative' do - expect(scope.resolve.size).to eq 0 - end - end - - # Author can still see their own initiative if review feature is active, - # even when status in InitiativeStatus::REVIEW_CODES. - context 'for a user who is author of the initiative' do - let(:user) { author } - - it { is_expected.to permit(:show) } - it { is_expected.to permit(:by_slug) } - it { is_expected.to permit(:create) } - it { is_expected.to permit(:update) } - it { is_expected.to permit(:destroy) } - it { is_expected.not_to permit(:accept_cosponsorship_invite) } - - it 'indexes the initiative' do - expect(scope.resolve.size).to eq 1 - end - end - - # Cosponsor can still see an initiative thaye are a cosponsor of if review feature is active, - # even when status in InitiativeStatus::REVIEW_CODES. - context 'for a user who is cosponsor of the initiative' do - let(:user) { create(:user) } - let!(:cosponsors_initiative) { create(:cosponsors_initiative, user: user, initiative: initiative) } - - it { is_expected.to permit(:show) } - it { is_expected.to permit(:by_slug) } - it { is_expected.not_to permit(:create) } - it { is_expected.not_to permit(:update) } - it { is_expected.not_to permit(:destroy) } - it { is_expected.to permit(:accept_cosponsorship_invite) } - - it 'indexes the initiative' do - expect(scope.resolve.size).to eq 1 - end - end - - context 'for a visitor' do - let(:user) { nil } - - it { is_expected.not_to permit(:show) } - it { is_expected.not_to permit(:by_slug) } - it { expect { policy.create? }.to raise_error(Pundit::NotAuthorizedError) } - it { expect { policy.update? }.to raise_error(Pundit::NotAuthorizedError) } - it { expect { policy.destroy? }.to raise_error(Pundit::NotAuthorizedError) } - it { is_expected.not_to permit(:accept_cosponsorship_invite) } - - it 'does not index the initiative' do - expect(scope.resolve.size).to eq 0 - end - end - end - - context 'for an initiative with proposed status' do - let!(:initiative) do - create(:initiative, author: author, initiative_status: create(:initiative_status_proposed)) - end - - context 'for an admin' do - let(:user) { create(:admin) } - - it { is_expected.to permit(:show) } - it { is_expected.to permit(:by_slug) } - it { is_expected.to permit(:create) } - it { is_expected.to permit(:update) } - it { is_expected.to permit(:destroy) } - it { is_expected.not_to permit(:accept_cosponsorship_invite) } - - it 'indexes the initiative' do - expect(scope.resolve.size).to eq 1 - end - end - - context 'for a user who is not author of the initiative' do - let(:user) { create(:user) } - - it { is_expected.to permit(:show) } - it { is_expected.to permit(:by_slug) } - it { is_expected.not_to permit(:create) } - it { is_expected.not_to permit(:update) } - it { is_expected.not_to permit(:destroy) } - it { is_expected.not_to permit(:accept_cosponsorship_invite) } - - it 'does not index the initiative' do - expect(scope.resolve.size).to eq 1 - end - end - - # Author can see initiative, but not edit, if review feature is active, - # and when status in InitiativeStatus::NOT_REVIEW_CODES - context 'for a user who is author of the initiative' do - let(:user) { author } - - it { is_expected.to permit(:show) } - it { is_expected.to permit(:by_slug) } - it { is_expected.to permit(:create) } - it { is_expected.to permit(:update) } # TODO: This is confusing, as editing_locked should be true when status proposed! - it { is_expected.to permit(:destroy) } - it { is_expected.not_to permit(:accept_cosponsorship_invite) } - - it 'indexes the initiative' do - expect(scope.resolve.size).to eq 1 - end - end - - context 'for a user who is cosponsor of the initiative' do - let(:user) { create(:user) } - let!(:cosponsors_initiative) { create(:cosponsors_initiative, user: user, initiative: initiative) } - - it { is_expected.to permit(:show) } - it { is_expected.to permit(:by_slug) } - it { is_expected.not_to permit(:create) } - it { is_expected.not_to permit(:update) } - it { is_expected.not_to permit(:destroy) } - it { is_expected.to permit(:accept_cosponsorship_invite) } - - it 'indexes the initiative' do - expect(scope.resolve.size).to eq 1 - end - end - - context 'for a visitor' do - let(:user) { nil } - - it { is_expected.to permit(:show) } - it { is_expected.to permit(:by_slug) } - it { expect { policy.create? }.to raise_error(Pundit::NotAuthorizedError) } - it { expect { policy.update? }.to raise_error(Pundit::NotAuthorizedError) } - it { expect { policy.destroy? }.to raise_error(Pundit::NotAuthorizedError) } - it { is_expected.not_to permit(:accept_cosponsorship_invite) } - - it 'does not index the initiative' do - expect(scope.resolve.size).to eq 1 - end - end - end - end - - # Nobody can edit initiative if editing is locked, including admins and authors - context 'when editing is locked for the initiative' do - let!(:initiative) do - create(:initiative, author: author, editing_locked: true, initiative_status: create(:initiative_status_proposed)) - end - - context 'for an admin' do - let(:user) { create(:admin) } - - it { is_expected.to permit(:show) } - it { is_expected.to permit(:by_slug) } - it { is_expected.to permit(:create) } - it { is_expected.to permit(:update) } - it { is_expected.to permit(:destroy) } - it { is_expected.not_to permit(:accept_cosponsorship_invite) } - - it 'indexes the initiative' do - expect(scope.resolve.size).to eq 1 - end - end - - context 'for a user who is author of the initiative' do - let(:user) { author } - - it { is_expected.to permit(:show) } - it { is_expected.to permit(:by_slug) } - it { is_expected.to permit(:create) } - it { is_expected.not_to permit(:update) } - it { is_expected.to permit(:destroy) } - it { is_expected.not_to permit(:accept_cosponsorship_invite) } - - it 'indexes the initiative' do - expect(scope.resolve.size).to eq 1 - end - end - end -end diff --git a/back/spec/policies/initiative_reaction_policy_spec.rb b/back/spec/policies/initiative_reaction_policy_spec.rb deleted file mode 100644 index c6cc7bb1844c..000000000000 --- a/back/spec/policies/initiative_reaction_policy_spec.rb +++ /dev/null @@ -1,74 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -describe InitiativeReactionPolicy do - subject(:policy) { described_class.new(user, reaction) } - - let(:scope) { InitiativeReactionPolicy::Scope.new(user, Reaction) } - let(:reactable) { create(:initiative) } - let!(:reaction) { create(:reaction, reactable: reactable) } - - context 'for a visitor' do - let(:user) { nil } - - it { is_expected.not_to permit(:show) } - it { is_expected.not_to permit(:create) } - it { is_expected.not_to permit(:up) } - it { expect { policy.down? }.to raise_error(Pundit::NotAuthorizedError) } - it { is_expected.not_to permit(:destroy) } - - it 'should not index the reaction' do - expect(scope.resolve.size).to eq 0 - end - end - - context 'for a mortal user on a reaction of another user' do - let(:user) { create(:user) } - - it { is_expected.not_to permit(:show) } - it { is_expected.not_to permit(:create) } - it { is_expected.not_to permit(:up) } - it { expect { policy.down? }.to raise_error(Pundit::NotAuthorizedError) } - it { is_expected.not_to permit(:destroy) } - - it 'should not index the reaction' do - expect(scope.resolve.size).to eq 0 - end - end - - context 'for a mortal user who owns the reaction' do - let(:user) { reaction.user } - - it { is_expected.to permit(:show) } - it { is_expected.to permit(:create) } - it { is_expected.to permit(:up) } - it { expect { policy.down? }.to raise_error(Pundit::NotAuthorizedError) } - it { is_expected.to permit(:destroy) } - - it 'should index the reaction' do - expect(scope.resolve.size).to eq 1 - end - end - - context 'for blocked reaction owner' do - let(:user) { create(:user, block_end_at: 5.days.from_now) } - let(:reaction) { create(:reaction, user: user, reactable: reactable) } - - it_behaves_like 'policy for blocked user reaction', down_authorized: false - end - - context 'for an admin' do - let(:user) { create(:admin) } - - it { is_expected.to permit(:show) } - it { is_expected.not_to permit(:create) } - it { is_expected.not_to permit(:up) } - it { expect { policy.down? }.to raise_error(Pundit::NotAuthorizedError) } - it { is_expected.not_to permit(:destroy) } - - it 'should index the reaction' do - expect(scope.resolve.size).to eq 1 - end - end -end diff --git a/back/spec/policies/initiative_status_policy_spec.rb b/back/spec/policies/initiative_status_policy_spec.rb deleted file mode 100644 index dd63b5b6d68e..000000000000 --- a/back/spec/policies/initiative_status_policy_spec.rb +++ /dev/null @@ -1,81 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -# TODO: cleanup-after-proposals-migration -describe InitiativeStatusPolicy do - describe InitiativeStatusPolicy::Scope do - subject(:scope) { described_class.new(user, InitiativeStatus).resolve } - - def create_initiative_status_change(status, initiative: build(:initiative)) - create(:initiative_status_change, initiative: initiative, initiative_status: status) - end - - context 'with default statuses' do - let!(:status_review_pending) { create(:initiative_status_review_pending) } - let!(:status_changes_requested) { create(:initiative_status_changes_requested) } - let!(:status_proposed) { create(:initiative_status_proposed) } - let!(:status_expired) { create(:initiative_status_expired) } - let!(:status_threshold_reached) { create(:initiative_status_threshold_reached) } - let!(:status_answered) { create(:initiative_status_answered) } - let!(:status_ineligible) { create(:initiative_status_ineligible) } - - let!(:default_codes) { InitiativeStatus::CODES - ['custom'] } - let!(:not_review_codes) { InitiativeStatus::NOT_REVIEW_CODES - ['custom'] } - - context 'when admin' do - let(:user) { create(:admin) } - - context 'when review feature is not fully activated' do - it 'does not return review statuses if no review initiative exists' do - expect(scope.pluck(:code)).to match_array not_review_codes - end - - it 'returns all statuses if at least one review_pending initiative exists' do - create_initiative_status_change(status_review_pending) - expect(scope.pluck(:code)).to match_array default_codes - end - - it 'returns all statuses if at least one changes_requested initiative exists' do - create_initiative_status_change(status_changes_requested) - expect(scope.pluck(:code)).to match_array default_codes - end - end - - context 'when review feature is fully activated' do - before do - SettingsService.new.activate_feature! 'initiative_review' - - configuration = AppConfiguration.instance - configuration.settings['initiatives']['require_review'] = true - configuration.save! - end - - it 'returns all initiative statuses' do - expect(scope.pluck(:code)).to match_array default_codes - end - end - end - - context 'when normal user' do - let(:user) { create(:user) } - - it 'does not return review statuses if user authored no review initiatives' do - create_initiative_status_change(status_review_pending) - create_initiative_status_change(status_changes_requested) - expect(scope.pluck(:code)).to match_array not_review_codes - end - - it 'returns review_pending status if user authored an review_pending initiative' do - create_initiative_status_change(status_review_pending, initiative: build(:initiative, author: user)) - expect(scope.pluck(:code)).to match_array not_review_codes + ['review_pending'] - end - - it 'returns changes_requested status if user authored an changes_requested initiative' do - create_initiative_status_change(status_changes_requested, initiative: build(:initiative, author: user)) - expect(scope.pluck(:code)).to match_array not_review_codes + ['changes_requested'] - end - end - end - end -end From 0c2edf7245d465b45d915c14288428442c2a2d89 Mon Sep 17 00:00:00 2001 From: Simon Tharby Date: Tue, 17 Dec 2024 15:32:29 +0000 Subject: [PATCH 10/63] [TAN-2538] Remove from CONSTANTIZER in imges & files controllers --- back/app/controllers/web_api/v1/files_controller.rb | 7 ------- back/app/controllers/web_api/v1/images_controller.rb | 7 ------- 2 files changed, 14 deletions(-) diff --git a/back/app/controllers/web_api/v1/files_controller.rb b/back/app/controllers/web_api/v1/files_controller.rb index 0d96eea89f0f..8b374627ee0c 100644 --- a/back/app/controllers/web_api/v1/files_controller.rb +++ b/back/app/controllers/web_api/v1/files_controller.rb @@ -13,13 +13,6 @@ class WebApi::V1::FilesController < ApplicationController file_relationship: :idea_files, container_id: :idea_id }, - 'Initiative' => { - container_class: Initiative, - file_class: InitiativeFile, - policy_scope_class: InitiativeFilePolicy::Scope, - file_relationship: :initiative_files, - container_id: :initiative_id - }, 'Project' => { container_class: Project, file_class: ProjectFile, diff --git a/back/app/controllers/web_api/v1/images_controller.rb b/back/app/controllers/web_api/v1/images_controller.rb index 925f64124fb1..339f4598a8e6 100644 --- a/back/app/controllers/web_api/v1/images_controller.rb +++ b/back/app/controllers/web_api/v1/images_controller.rb @@ -18,13 +18,6 @@ class WebApi::V1::ImagesController < ApplicationController image_relationship: :event_images, container_id: :event_id }, - 'Initiative' => { - container_class: Initiative, - image_class: InitiativeImage, - policy_scope_class: InitiativeImagePolicy::Scope, - image_relationship: :initiative_images, - container_id: :initiative_id - }, 'Project' => { container_class: Project, image_class: ProjectImage, From fb6058c4c428eaca9a1efefd30f7c7879512902f Mon Sep 17 00:00:00 2001 From: Simon Tharby Date: Tue, 17 Dec 2024 15:43:27 +0000 Subject: [PATCH 11/63] [TAN-2538] Remove from Folower FOLLOWABLE_TYPES + folllower_policy_spec tests --- back/app/models/follower.rb | 2 +- back/spec/policies/follower_policy_spec.rb | 40 ---------------------- 2 files changed, 1 insertion(+), 41 deletions(-) diff --git a/back/app/models/follower.rb b/back/app/models/follower.rb index 71c7efc4361b..cd316243db8f 100644 --- a/back/app/models/follower.rb +++ b/back/app/models/follower.rb @@ -23,7 +23,7 @@ # fk_rails_... (user_id => users.id) # class Follower < ApplicationRecord - FOLLOWABLE_TYPES = %w[Project ProjectFolders::Folder Idea Initiative Topic Area] + FOLLOWABLE_TYPES = %w[Project ProjectFolders::Folder Idea Topic Area] belongs_to :user belongs_to :followable, polymorphic: true diff --git a/back/spec/policies/follower_policy_spec.rb b/back/spec/policies/follower_policy_spec.rb index e02fc71ade7e..91b1d13c9e7a 100644 --- a/back/spec/policies/follower_policy_spec.rb +++ b/back/spec/policies/follower_policy_spec.rb @@ -196,46 +196,6 @@ end end - context 'for an initiative' do - let(:followable) { create(:initiative) } - - context 'for a resident' do - let(:user) { create(:user) } - - it { is_expected.not_to permit(:show) } - it { is_expected.not_to permit(:create) } - it { is_expected.not_to permit(:destroy) } - - it 'does not index the follower' do - expect(scope.resolve.size).to eq 0 - end - end - - context 'for the following user' do - let(:user) { following_user } - - it { is_expected.to permit(:show) } - it { is_expected.to permit(:create) } - it { is_expected.to permit(:destroy) } - - it 'indexes the follower' do - expect(scope.resolve.size).to eq 1 - end - end - - context 'for an admin' do - let(:user) { create(:admin) } - - it { is_expected.not_to permit(:show) } - it { is_expected.not_to permit(:create) } - it { is_expected.not_to permit(:destroy) } - - it 'does not index the follower' do - expect(scope.resolve.size).to eq 0 - end - end - end - context 'for a topic' do let(:followable) { create(:topic) } From a0f51a42e5705d84c2186b5720f4a1c76911fd63 Mon Sep 17 00:00:00 2001 From: Simon Tharby Date: Tue, 17 Dec 2024 15:48:05 +0000 Subject: [PATCH 12/63] [TAN-2538] Remove from UserCommentsController --- .../app/controllers/web_api/v1/user_comments_controller.rb | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/back/app/controllers/web_api/v1/user_comments_controller.rb b/back/app/controllers/web_api/v1/user_comments_controller.rb index b3e0800a4ec8..d7413325348b 100644 --- a/back/app/controllers/web_api/v1/user_comments_controller.rb +++ b/back/app/controllers/web_api/v1/user_comments_controller.rb @@ -9,22 +9,17 @@ def index comment_allowed_ideas = policy_scope(Comment, policy_scope_class: IdeaCommentPolicy::Scope) .published .where(author_id: params[:user_id]) - comment_allowed_initiatives = policy_scope(Comment, policy_scope_class: InitiativeCommentPolicy::Scope) - .published - .where(author_id: params[:user_id]) # Apply pagination to the posts, using the union_posts # view and ordering by publication date. joined_posts = UnionPost.joins('INNER JOIN comments ON comments.post_id = union_posts.id') paged_posts = joined_posts.where(comments: { id: comment_allowed_ideas }) - .or(joined_posts.where(comments: { id: comment_allowed_initiatives })) .order(published_at: :desc) .group('union_posts.id, union_posts.published_at') # Remove union_post duplicates .select('union_posts.id') paged_posts = paginate paged_posts - # Get the comments, grouped by the corresponding posts - # page. + # Get the comments, grouped by the corresponding posts page. comments = Comment.where(post_id: paged_posts) .where(author_id: params[:user_id]) .includes(:post) From 4b8420e32084cc5b46cd041a61be2beb487dc75c Mon Sep 17 00:00:00 2001 From: Simon Tharby Date: Tue, 17 Dec 2024 15:55:46 +0000 Subject: [PATCH 13/63] [TAN-2538] Comment out back/engines/commercial/admin_api/spec/graphql/initiatives_spec.rb --- .../spec/graphql/initiatives_spec.rb | 125 +++++++++--------- 1 file changed, 65 insertions(+), 60 deletions(-) diff --git a/back/engines/commercial/admin_api/spec/graphql/initiatives_spec.rb b/back/engines/commercial/admin_api/spec/graphql/initiatives_spec.rb index 1451fb27ae6f..9a8f9c0b703f 100644 --- a/back/engines/commercial/admin_api/spec/graphql/initiatives_spec.rb +++ b/back/engines/commercial/admin_api/spec/graphql/initiatives_spec.rb @@ -3,65 +3,70 @@ require 'rails_helper' # TODO: cleanup-after-proposals-migration -RSpec.describe AdminApi::Schema do - let(:context) { {} } - let(:variables) { {} } - let(:result) do - res = described_class.execute( - query_string, - context: context, - variables: variables - ) - if res['errors'] - pp res - end - res - end +# +# Commented out for now, just so I can use CI to see other failures clearly, and because I'm not sure what +# we want to do about the admin_api for Initiatives. Maybe we remove it all, or maybe we keep it and return useful +# errors, like 'initiatives are no longer supported as a separate model, use ideas instead' or something. +# +# RSpec.describe AdminApi::Schema do +# let(:context) { {} } +# let(:variables) { {} } +# let(:result) do +# res = described_class.execute( +# query_string, +# context: context, +# variables: variables +# ) +# if res['errors'] +# pp res +# end +# res +# end - describe 'publicInitiatives' do - let(:query_string) do - %| - query { - publicInitiatives(first: 5) { - edges { - node { - id - href - titleMultiloc { - en - nlBe - frBe - frFr - } - images(first: 1) { - edges { - node { - smallUrl - } - } - } - likesCount - commentsCount - internalCommentsCount - } - } - } - } - | - end +# describe 'publicInitiatives' do +# let(:query_string) do +# %| +# query { +# publicInitiatives(first: 5) { +# edges { +# node { +# id +# href +# titleMultiloc { +# en +# nlBe +# frBe +# frFr +# } +# images(first: 1) { +# edges { +# node { +# smallUrl +# } +# } +# } +# likesCount +# commentsCount +# internalCommentsCount +# } +# } +# } +# } +# | +# end - it 'returns all public initiatives with fields' do - create_list(:initiative, 5) - create(:initiative, publication_status: 'draft') - response = result - edges = response.dig('data', 'publicInitiatives', 'edges') - expect(edges&.size).to eq 5 - expect(edges&.first&.dig('node', 'id')).to be_present - expect(edges&.first&.dig('node', 'href')).to be_present - expect(edges&.first&.dig('node', 'titleMultiloc')&.values&.compact&.size).to be >= 1 - expect(edges&.first&.dig('node', 'likesCount')).to be_present - expect(edges&.first&.dig('node', 'commentsCount')).to be_present - expect(edges&.first&.dig('node', 'internalCommentsCount')).to be_present - end - end -end +# it 'returns all public initiatives with fields' do +# create_list(:initiative, 5) +# create(:initiative, publication_status: 'draft') +# response = result +# edges = response.dig('data', 'publicInitiatives', 'edges') +# expect(edges&.size).to eq 5 +# expect(edges&.first&.dig('node', 'id')).to be_present +# expect(edges&.first&.dig('node', 'href')).to be_present +# expect(edges&.first&.dig('node', 'titleMultiloc')&.values&.compact&.size).to be >= 1 +# expect(edges&.first&.dig('node', 'likesCount')).to be_present +# expect(edges&.first&.dig('node', 'commentsCount')).to be_present +# expect(edges&.first&.dig('node', 'internalCommentsCount')).to be_present +# end +# end +# end From 0910e982abe89516e99264320478406170f7da38 Mon Sep 17 00:00:00 2001 From: Simon Tharby Date: Tue, 17 Dec 2024 16:02:45 +0000 Subject: [PATCH 14/63] [TAN-2538] Remove stats_initiatives routes, controller & spec --- .../v1/stats_initiatives_controller.rb | 16 --- back/config/routes.rb | 3 - .../spec/acceptance/stats_initiatives_spec.rb | 98 ------------------- 3 files changed, 117 deletions(-) delete mode 100644 back/app/controllers/web_api/v1/stats_initiatives_controller.rb delete mode 100644 back/spec/acceptance/stats_initiatives_spec.rb diff --git a/back/app/controllers/web_api/v1/stats_initiatives_controller.rb b/back/app/controllers/web_api/v1/stats_initiatives_controller.rb deleted file mode 100644 index 95fbff5ae713..000000000000 --- a/back/app/controllers/web_api/v1/stats_initiatives_controller.rb +++ /dev/null @@ -1,16 +0,0 @@ -# frozen_string_literal: true - -class WebApi::V1::StatsInitiativesController < WebApi::V1::StatsController - def initiatives_count - initiatives = policy_scope(Initiative.published).where(published_at: @start_at..@end_at) - initiatives = PostsFilteringService.new.apply_common_initiative_index_filters initiatives, params - - render json: raw_json({ count: initiatives.count }) - end - - private - - def do_authorize - authorize :stat_initiative - end -end diff --git a/back/config/routes.rb b/back/config/routes.rb index e8d1d8fe4c8a..9c2cb889a981 100644 --- a/back/config/routes.rb +++ b/back/config/routes.rb @@ -100,7 +100,6 @@ get 'by_slug/:slug', on: :collection, to: 'users#by_slug' get 'by_invite/:token', on: :collection, to: 'users#by_invite' get 'ideas_count', on: :member - get 'initiatives_count', on: :member get 'comments_count', on: :member get 'blocked_count', on: :collection get 'check/:email', on: :collection, to: 'users#check', constraints: { email: /.*/ } @@ -263,8 +262,6 @@ get 'ideas_by_project_as_xlsx' end - get 'initiatives_count', controller: 'stats_initiatives' - with_options controller: 'stats_comments' do get 'comments_count' get 'comments_by_topic' diff --git a/back/spec/acceptance/stats_initiatives_spec.rb b/back/spec/acceptance/stats_initiatives_spec.rb deleted file mode 100644 index 46dff56278da..000000000000 --- a/back/spec/acceptance/stats_initiatives_spec.rb +++ /dev/null @@ -1,98 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' -require 'rspec_api_documentation/dsl' - -def time_boundary_parameters(s) - s.parameter :start_at, 'Date defining from where results should start', required: false - s.parameter :end_at, 'Date defining till when results should go', required: false -end - -def time_series_parameters(s) - time_boundary_parameters s - s.parameter :interval, 'Either day, week, month, year', required: true -end - -def group_filter_parameter(s) - s.parameter :group, 'Group ID. Only count initiatives posted by users in the given group', required: false -end - -def topic_filter_parameter(s) - s.parameter :topic, 'Topic ID. Only count initiatives that have the given topic assigned', required: false -end - -def feedback_needed_filter_parameter(s) - s.parameter :feedback_needed, 'Only count initiatives that need feedback', required: false -end - -# TODO: cleanup-after-proposals-migration -resource 'Stats - Initiatives' do - explanation 'The various stats endpoints can be used to show certain properties of initiatives.' - header 'Content-Type', 'application/json' - - let_it_be(:now) { AppConfiguration.timezone.now } - - before { admin_header_token } - - before_all do - AppConfiguration.instance.update!(created_at: now - 3.years) - - @threshold_reached = create(:initiative_status, code: 'threshold_reached') - @initiatives_with_topics = [] - @initiatives_with_areas = [] - - begin_of_last_year = AppConfiguration.timezone.now.beginning_of_year - 1.year - - travel_to(begin_of_last_year - 1.month) do - i = create(:initiative, initiative_status: @threshold_reached) - create(:official_feedback, post: i) - end - - travel_to(begin_of_last_year + 2.months) do - @initiatives_with_topics += create_list(:initiative_with_topics, 2, initiative_status: @threshold_reached) - @initiatives_with_areas += create_list(:initiative_with_areas, 3, initiative_status: @threshold_reached) - end - - travel_to(begin_of_last_year + 5.months) do - @initiatives_with_topics += create_list(:initiative_with_topics, 3, initiative_status: @threshold_reached) - @initiatives_with_areas += create_list(:initiative_with_areas, 2, initiative_status: @threshold_reached) - create(:initiative, initiative_status: @threshold_reached) - end - end - - get 'web_api/v1/stats/initiatives_count' do - time_boundary_parameters self - group_filter_parameter self - topic_filter_parameter self - feedback_needed_filter_parameter self - - example_request 'Count all initiatives' do - assert_status 200 - json_response = json_parse response_body - expect(json_response.dig(:data, :type)).to eq 'initiatives_count' - expect(json_response.dig(:data, :attributes, :count)).to eq Initiative.published.count - end - - describe 'with feedback_needed filter' do - let(:feedback_needed) { true } - - example_request 'Count all initiatives that need feedback' do - assert_status 200 - json_response = json_parse response_body - expect(json_response.dig(:data, :type)).to eq 'initiatives_count' - expect(json_response.dig(:data, :attributes, :count)).to eq Initiative.published.count - end - - example 'Count all initiatives that need feedback for a specific assignee' do - assignee = create(:admin) - create(:initiative, initiative_status: @threshold_reached, assignee: assignee) - do_request assignee: assignee.id - - assert_status 200 - json_response = json_parse response_body - expect(json_response.dig(:data, :type)).to eq 'initiatives_count' - expect(json_response.dig(:data, :attributes, :count)).to eq 1 - end - end - end -end From 6660cd77897b6017302a95cce025328a198d1614 Mon Sep 17 00:00:00 2001 From: Simon Tharby Date: Tue, 17 Dec 2024 16:13:26 +0000 Subject: [PATCH 15/63] [TAN-2538] Remove test from avatars_spec --- back/spec/acceptance/avatars_spec.rb | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/back/spec/acceptance/avatars_spec.rb b/back/spec/acceptance/avatars_spec.rb index 9c8446bddfb8..a7f8d7d1767e 100644 --- a/back/spec/acceptance/avatars_spec.rb +++ b/back/spec/acceptance/avatars_spec.rb @@ -91,26 +91,6 @@ end end - # TODO: cleanup-after-proposals-migration - describe do - let(:initiative) { create(:initiative) } - let(:context_type) { 'initiative' } - let(:context_id) { initiative.id } - let(:author_id) { initiative.author.id } - let!(:commenter_ids) { Array.new(2) { create(:comment, post: initiative).author.id } } - let(:limit) { 2 } - - example_request 'List random user avatars on an initiative (author and commenters)' do - assert_status 200 - json_response = json_parse(response_body) - expect(json_response[:data].size).to eq 2 - expect(json_response[:data].map { |d| d.dig(:attributes, :avatar).keys }).to all(eq %i[small medium large]) - expect(json_response[:data].flat_map { |d| d.dig(:attributes, :avatar).values }).to all(be_present) - expect(json_response[:data].pluck(:id)).to all(satisfy { |id| (commenter_ids + [author_id]).include?(id) }) - expect(json_response.dig(:meta, :total)).to eq 3 - end - end - context 'as an admin' do before { admin_header_token } From 24c653fad554e4bf2a2f0c5667070373df7e08da Mon Sep 17 00:00:00 2001 From: Simon Tharby Date: Tue, 17 Dec 2024 16:13:51 +0000 Subject: [PATCH 16/63] [TAN-2538] Remove SideFxInitiativesService + spec --- .../services/side_fx_initiative_service.rb | 158 -------------- .../patches/side_fx_initiative_service.rb | 34 --- .../side_fx_initiative_service_spec.rb | 204 ------------------ 3 files changed, 396 deletions(-) delete mode 100644 back/app/services/side_fx_initiative_service.rb delete mode 100644 back/engines/commercial/flag_inappropriate_content/app/services/flag_inappropriate_content/patches/side_fx_initiative_service.rb delete mode 100644 back/spec/services/side_fx_initiative_service_spec.rb diff --git a/back/app/services/side_fx_initiative_service.rb b/back/app/services/side_fx_initiative_service.rb deleted file mode 100644 index ce9199d5c45f..000000000000 --- a/back/app/services/side_fx_initiative_service.rb +++ /dev/null @@ -1,158 +0,0 @@ -# frozen_string_literal: true - -class SideFxInitiativeService - include SideFxHelper - - def initialize - @automatic_assignment = false - end - - def before_create(initiative, user) - before_publish initiative, user if initiative.published? - end - - def after_create(initiative, user) - initiative.update!(body_multiloc: TextImageService.new.swap_data_images_multiloc(initiative.body_multiloc, field: :body_multiloc, imageable: initiative)) - return unless initiative.published? - - after_publish initiative, user - - log_activities_if_cosponsors_added(initiative, user, _old_cosponsor_ids = []) - end - - def before_update(initiative, user) - initiative.body_multiloc = TextImageService.new.swap_data_images_multiloc(initiative.body_multiloc, field: :body_multiloc, imageable: initiative) - return unless initiative.publication_status_change == %w[draft published] - - before_publish initiative, user - end - - def after_update(initiative, user, old_cosponsor_ids) - transition_to_review_pending_if_required(initiative, user) - remove_user_from_past_activities_with_item(initiative, user) if initiative.anonymous_previously_changed?(to: true) - - LogActivityJob.perform_later(initiative, 'changed', user_for_activity_on_anonymizable_item(initiative, user), initiative.updated_at.to_i) - - if initiative.assignee_id_previously_changed? - initiating_user = @automatic_assignment ? nil : user - LogActivityJob.perform_later(initiative, 'changed_assignee', user_for_activity_on_anonymizable_item(initiative, initiating_user), initiative.updated_at.to_i, payload: { change: initiative.assignee_id_previous_change }) - end - - if initiative.title_multiloc_previously_changed? - LogActivityJob.perform_later(initiative, 'changed_title', user_for_activity_on_anonymizable_item(initiative, user), initiative.updated_at.to_i, payload: { change: initiative.title_multiloc_previous_change }) - end - - if initiative.body_multiloc_previously_changed? - LogActivityJob.perform_later(initiative, 'changed_body', user_for_activity_on_anonymizable_item(initiative, user), initiative.updated_at.to_i, payload: { change: initiative.body_multiloc_previous_change }) - end - - log_activities_if_cosponsors_added(initiative, user, old_cosponsor_ids) - end - - def after_accept_cosponsorship_invite(cosponsors_initiative, user) - LogActivityJob.perform_later( - cosponsors_initiative, - 'cosponsorship_accepted', - user, # We don't want anonymized users being cosponsors - cosponsors_initiative.updated_at.to_i, - payload: { change: cosponsors_initiative.status_previous_change } - ) - create_followers cosponsors_initiative.initiative, cosponsors_initiative.user - end - - def before_destroy(initiative, user); end - - def after_destroy(frozen_initiative, user) - serialized_initiative = clean_time_attributes(frozen_initiative.attributes) - serialized_initiative['location_point'] = serialized_initiative['location_point'].to_s - LogActivityJob.perform_later(encode_frozen_resource(frozen_initiative), 'deleted', user, Time.now.to_i, payload: { initiative: serialized_initiative }) - end - - def log_initiative_proposed_activity(initiative, user) - LogActivityJob.perform_later( - initiative, - 'proposed', - user_for_activity_on_anonymizable_item(initiative, user), - initiative.updated_at.to_i - ) - end - - private - - def log_activities_if_cosponsors_added(initiative, user, old_cosponsor_ids) - added_ids = initiative.reload.cosponsors.map(&:id) - old_cosponsor_ids - - if added_ids.present? - new_cosponsors_initiatives = initiative.cosponsors_initiatives.where(user_id: added_ids) - - new_cosponsors_initiatives.each do |cosponsors_initiative| - LogActivityJob.perform_later( - cosponsors_initiative, - 'created', - user, # We don't want anonymized authors when cosponsors feature in use - cosponsors_initiative.created_at.to_i - ) - end - end - end - - def transition_to_review_pending_if_required(initiative, user) - if initiative.initiative_status&.code == 'changes_requested' && user == initiative.author - status_id_to = InitiativeStatus.find_by(code: 'review_pending')&.id - InitiativeStatusService.new.transition!([initiative.id], status_id_to) - end - end - - def before_publish(initiative, _user) - set_assignee initiative - end - - def after_publish(initiative, user) - add_autoreaction(initiative, user) - log_activity_jobs_after_published(initiative, user) - create_followers(initiative, user) unless initiative.anonymous? - end - - def set_assignee(initiative) - default_assignee = User.active.admin.order(:created_at).reject(&:super_admin?).first - return unless !initiative.assignee && default_assignee - - initiative.assignee = default_assignee - @automatic_assignment = true - end - - def add_autoreaction(initiative, user) - reaction = Reaction.new(reactable: initiative, user: user, mode: 'up') - - begin - Pundit.authorize( - user, - reaction, - :create?, - policy_class: InitiativeReactionPolicy - ) - rescue Pundit::NotAuthorizedErrorWithReason - # Do not create the auto-reaction. - else - initiative.reactions.create!(mode: 'up', user: initiative.author) - initiative.reload - end - end - - def log_activity_jobs_after_published(initiative, user) - LogActivityJob.set(wait: 20.seconds).perform_later( - initiative, - 'published', - user_for_activity_on_anonymizable_item(initiative, user), - initiative.published_at.to_i - ) - - log_initiative_proposed_activity(initiative, user) if initiative.initiative_status.code == 'proposed' - end - - def create_followers(initiative, user) - Follower.find_or_create_by(followable: initiative, user: user) - end -end - -SideFxInitiativeService.prepend(FlagInappropriateContent::Patches::SideFxInitiativeService) diff --git a/back/engines/commercial/flag_inappropriate_content/app/services/flag_inappropriate_content/patches/side_fx_initiative_service.rb b/back/engines/commercial/flag_inappropriate_content/app/services/flag_inappropriate_content/patches/side_fx_initiative_service.rb deleted file mode 100644 index 97c0aa60c78e..000000000000 --- a/back/engines/commercial/flag_inappropriate_content/app/services/flag_inappropriate_content/patches/side_fx_initiative_service.rb +++ /dev/null @@ -1,34 +0,0 @@ -# frozen_string_literal: true - -module FlagInappropriateContent - module Patches - module SideFxInitiativeService - SUPPORTED_ATTRS = %i[title_multiloc body_multiloc location_description].freeze - - def after_create(initiative, user) - super - ToxicityDetectionJob.perform_later initiative, attributes: SUPPORTED_ATTRS - end - - def after_update(initiative, user, _old_cosponsor_ids) - # before super to reliably detect attribute changes - atrs = updated_supported_attrs initiative - if atrs.present? - # retry all attributes to consider removing flag - atrs = SUPPORTED_ATTRS if initiative.inappropriate_content_flag - ToxicityDetectionJob.perform_later initiative, attributes: atrs - end - - super - end - - private - - def updated_supported_attrs(initiative) - SUPPORTED_ATTRS.select do |atr| - initiative.saved_change_to_attribute? atr - end - end - end - end -end diff --git a/back/spec/services/side_fx_initiative_service_spec.rb b/back/spec/services/side_fx_initiative_service_spec.rb deleted file mode 100644 index 8efc2568ce1e..000000000000 --- a/back/spec/services/side_fx_initiative_service_spec.rb +++ /dev/null @@ -1,204 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -# TODO: cleanup-after-proposals-migration -describe SideFxInitiativeService do - let(:service) { described_class.new } - let(:user) { create(:user) } - - describe '#after_update' do - it "logs a 'changed' action job when the initiative has changed" do - initiative = create(:initiative) - initiative.update!(title_multiloc: { en: 'something else' }) - expect { service.after_update(initiative, user, _cosponsor_ids = []) } - .to enqueue_job(LogActivityJob).with(initiative, 'changed', any_args).exactly(1).times - end - - it "logs a 'changed_title' action job when the title has changed" do - initiative = create(:initiative) - old_initiative_title = initiative.title_multiloc - initiative.update!(title_multiloc: { en: 'changed' }) - - expect { service.after_update(initiative, user, _cosponsor_ids = []) } - .to enqueue_job(LogActivityJob).with( - initiative, - 'changed_title', - any_args, - payload: { change: [old_initiative_title, initiative.title_multiloc] } - ).exactly(1).times - end - - it "logs a 'changed_body' action job when the body has changed" do - initiative = create(:initiative) - old_initiative_body = initiative.body_multiloc - initiative.update!(body_multiloc: { en: 'changed' }) - - expect { service.after_update(initiative, user, _cosponsor_ids = []) } - .to enqueue_job(LogActivityJob).with( - initiative, - 'changed_body', - any_args, - payload: { change: [old_initiative_body, initiative.body_multiloc] } - ) - end - - context 'when initiative status is changes_requested' do - let(:initiative) do - create(:initiative_status_review_pending) - changes_requested = create(:initiative_status_changes_requested) - create(:initiative, initiative_status: changes_requested, author: user) - end - - it 'changes initiative status to review_pending' do - service.after_update(initiative, user, _cosponsor_ids = []) - expect(initiative.reload.initiative_status.code).to eq 'review_pending' - end - end - - context 'when update results in new cosponsors' do - let(:initiative) { create(:initiative, author: user) } - let(:cosponsor1) { create(:user) } - let(:cosponsor2) { create(:user) } - - it "logs CosponsorsInitiative 'created' activity jobs" do - initiative.update!(cosponsor_ids: [cosponsor1.id, cosponsor2.id]) - cosponsors_initiatives = - CosponsorsInitiative.where(initiative: initiative).where(user_id: [cosponsor1.id, cosponsor2.id]) - - expect { service.after_update(initiative, user, _old_cosponsor_ids = []) } - .to enqueue_job(LogActivityJob) - .with(cosponsors_initiatives[0], 'created', user, cosponsors_initiatives[0].created_at.to_i) - .exactly(1).times - .and enqueue_job(LogActivityJob) - .with(cosponsors_initiatives[1], 'created', user, cosponsors_initiatives[1].created_at.to_i) - .exactly(1).times - end - end - end - - describe '#after_accept_cosponsorship_invite' do - let(:initiative) { create(:initiative) } - let(:cosponsors_initiative) { create(:cosponsors_initiative, status: 'accepted', user: user, initiative: initiative) } - - it 'logs a cosponsorship_accepted activity job' do - expect { service.after_accept_cosponsorship_invite(cosponsors_initiative, user) } - .to enqueue_job(LogActivityJob) - .with( - cosponsors_initiative, - 'cosponsorship_accepted', - user, - cosponsors_initiative.updated_at.to_i, - payload: { change: %w[pending accepted] } - ) - .exactly(1).times - end - - it 'creates a follower' do - expect do - service.after_accept_cosponsorship_invite cosponsors_initiative, user - end.to change(Follower, :count).from(0).to(1) - - expect(user.follows.pluck(:followable_id)).to contain_exactly initiative.id - end - end - - describe 'after_create' do - it "logs a 'published' action job when publication_state is published" do - initiative = create(:initiative, publication_status: 'published', author: user) - - expect { service.after_create(initiative, user) } - .to enqueue_job(LogActivityJob) - .with(initiative, 'published', user, initiative.created_at.to_i) - .exactly(1).times - end - - it "doesn't log a 'published' action job when publication_state is draft" do - initiative = create(:initiative, publication_status: 'draft') - expect { service.after_create(initiative, user) } - .not_to enqueue_job(LogActivityJob) - end - - it 'creates a follower' do - initiative = create(:initiative) - - expect do - service.after_create initiative, user - end.to change(Follower, :count).from(0).to(1) - - expect(user.follows.pluck(:followable_id)).to contain_exactly initiative.id - end - - it 'creates a reaction (vote) for an author who can react (vote)' do - initiative = create(:initiative) - - expect do - service.after_create initiative, user - end.to change(Reaction, :count).from(0).to(1) - - expect(initiative.reactions.pluck(:user_id)).to contain_exactly initiative.author.id - end - - it 'does not create a reaction (vote) for an author who cannot react (vote)' do - initiative = create(:initiative) - - create( - :permission, - permission_scope: nil, - action: 'reacting_initiative', - permitted_by: 'users', - groups: [create(:group)] - ) - - expect do - service.after_create initiative, user - end.not_to change(Reaction, :count) - end - - context "when initiative has status 'proposed'" do - let(:initiative) { create(:initiative) } - let!(:initiative_status_change) do - create( - :initiative_status_change, - initiative: initiative, - initiative_status: create(:initiative_status_proposed) - ) - end - - it 'logs a proposed activity job' do - expect { service.after_create(initiative, user) } - .to enqueue_job(LogActivityJob) - .with(initiative, 'proposed', user, initiative.updated_at.to_i) - .exactly(1).times - end - end - - context "when initiative has status 'review_pending'" do - let(:initiative) { create(:initiative) } - let!(:initiative_status_change) do - create( - :initiative_status_change, - initiative: initiative, - initiative_status: create(:initiative_status_review_pending) - ) - end - - it "doesn't log a proposed activity job" do - expect { service.after_update(initiative, user, _cosponsor_ids = []) } - .not_to enqueue_job(LogActivityJob) - .with(instance_of(Initiative), 'proposed', anything, anything) - end - end - end - - describe 'after_destroy' do - it "logs a 'deleted' action job when the initiative is destroyed" do - initiative = create(:initiative) - freeze_time do - frozen_initiative = initiative.destroy - expect { service.after_destroy(frozen_initiative, user) } - .to enqueue_job(LogActivityJob).exactly(1).times - end - end - end -end From ebbbdba5ff19da52f378ebda74aa31c9a5d4c95c Mon Sep 17 00:00:00 2001 From: Simon Tharby Date: Tue, 17 Dec 2024 16:51:54 +0000 Subject: [PATCH 17/63] [TAN-2538] Comment out problematic test in SideFxCommentService + add explanatory comments --- .../services/side_fx_comment_service_spec.rb | 27 ++++++++++++------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/back/spec/services/side_fx_comment_service_spec.rb b/back/spec/services/side_fx_comment_service_spec.rb index b0d696856d99..0a789612c208 100644 --- a/back/spec/services/side_fx_comment_service_spec.rb +++ b/back/spec/services/side_fx_comment_service_spec.rb @@ -47,15 +47,24 @@ expect(user.follows.pluck(:followable_id)).to contain_exactly idea.id, project.id, folder.id end - it 'does not create a follower if the user already follows the post' do - initiative = create(:initiative) - comment = create(:comment, post: initiative) - create(:follower, followable: initiative, user: user) - - expect do - service.after_create comment, user - end.not_to change(Follower, :count) - end + # TODO: Part of initiatives cleanup + # I have commented this out, for now, as it fails if I change initiative to idea + # This seems to be because the related `after_create` method in fact creates a folllower for the related project, + # and intitiatives never had a related project. + # Furthermore, it appears that the `after_create` method will go on to also create another follower for the + # project's folder, it there is one. + # Thus, this test was not as general (to Post) as it might have seemed + I think we need to check that the cascading + # follower creations (idea -> project -> folder as followable) is intended & necessary. + # + # it 'does not create a follower if the user already follows the post' do + # initiative = create(:initiative) + # comment = create(:comment, post: initiative) + # create(:follower, followable: initiative, user: user) + + # expect do + # service.after_create comment, user + # end.not_to change(Follower, :count) + # end end describe 'after_update' do From 6d9bcb4776e287cb0c60bab11bc8c6a5f2331212 Mon Sep 17 00:00:00 2001 From: Simon Tharby Date: Tue, 17 Dec 2024 16:57:08 +0000 Subject: [PATCH 18/63] [TAN-2538] Remove InitiativesFinder + spec --- back/app/finders/initiatives_finder.rb | 62 ------ back/spec/finders/initiatives_finder_spec.rb | 195 ------------------- 2 files changed, 257 deletions(-) delete mode 100644 back/app/finders/initiatives_finder.rb delete mode 100644 back/spec/finders/initiatives_finder_spec.rb diff --git a/back/app/finders/initiatives_finder.rb b/back/app/finders/initiatives_finder.rb deleted file mode 100644 index 25b9f02d7eb9..000000000000 --- a/back/app/finders/initiatives_finder.rb +++ /dev/null @@ -1,62 +0,0 @@ -# frozen_string_literal: true - -class InitiativesFinder < ApplicationFinder - def find_records - initiatives = super - # We use Initiative.where to avoid duplicates caused by `left_outer_joins(:cosponsors_initiatives)`. - # #distinct fails with `ERROR: for SELECT DISTINCT, ORDER BY expressions must appear in select list ... md5(` - Initiative.where(id: initiatives).includes(@includes) - end - - private - - def topics_condition(topics) - scope(:with_some_topics, topics) - end - - def areas_condition(areas) - scope(:with_some_areas, areas) - end - - def initiative_status_condition(status_id) - records.left_outer_joins(:initiative_initiative_status) - .where(initiative_initiative_statuses: { initiative_status_id: status_id }) - end - - def assignee_condition(assignee_id) - assignee_id = nil if assignee_id == 'unassigned' - where(assignee_id: assignee_id) - end - - def feedback_needed_condition(feedback_needed) - feedback_needed ? scope(:feedback_needed) : scope(:no_feedback_needed) - end - - def author_condition(author_id) - records.includes(:author).where(author_id: author_id) - end - - def search_condition(search_term) - if _search_restricted? - scope(:restricted_search, search_term) - else - scope(:search_by_all, search_term) - end - end - - def publication_status_condition(status) - where(publication_status: status) - end - - def bounding_box_condition(bounding_box) - scope(:with_bounding_box, bounding_box) - end - - def initiatives_condition(initiative_ids) - where(id: initiative_ids) - end - - def _search_restricted? - UserDisplayNameService.new(AppConfiguration.instance, current_user).restricted? - end -end diff --git a/back/spec/finders/initiatives_finder_spec.rb b/back/spec/finders/initiatives_finder_spec.rb deleted file mode 100644 index aa4990b2041e..000000000000 --- a/back/spec/finders/initiatives_finder_spec.rb +++ /dev/null @@ -1,195 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -# TODO: cleanup-after-proposals-migration -describe InitiativesFinder do - subject(:finder) { described_class.new(params, **options) } - - let(:record_ids) { finder.find_records.pluck(:id) } - let(:options) { {} } - let(:params) { {} } - - before { create_list(:initiative, 3, assignee: create(:admin)) } - - context 'without passing params' do - it 'returns all initiatives' do - expect(finder.find_records.count).to eq Initiative.count - end - end - - describe '#find_records' do - let(:normal_user) { create(:user) } - - let(:options) do - { - scope: InitiativePolicy::Scope.new(normal_user, Initiative).resolve - } - end - - before do - users = User.all - Initiative.first.cosponsors << users[0] - Initiative.first.cosponsors << users[1] - end - - it 'does not return duplicate records' do - expect(record_ids).to match_array Initiative.all.pluck(:id).uniq - end - end - - describe '#topics_condition' do - let(:topic_ids) { Topic.limit(3).pluck(:id) } - - before do - params[:topics] = topic_ids - end - - it 'filters by topics' do - expect(record_ids).to match_array Initiative.with_some_topics(topic_ids) - end - end - - describe '#areas_condition' do - let(:area_ids) { Area.limit(3).pluck(:id) } - - before do - params[:areas] = area_ids - end - - it 'filters by areas' do - expect(record_ids).to match_array Initiative.with_some_areas(area_ids) - end - end - - describe '#initiative_status_condition' do - let(:initiative_status_id) { InitiativeStatus.first.id } - - before do - params[:initiative_status] = initiative_status_id - end - - it 'filters by initiative_status' do - filtered_ids = Initiative - .left_outer_joins(:initiative_initiative_status) - .where(initiative_initiative_statuses: { initiative_status_id: initiative_status_id }) - .pluck(:id) - expect(record_ids).to match_array filtered_ids - end - end - - describe '#assignee_condition' do - let(:assignee) { create(:admin) } - let!(:unassigned_initiatives) { create_list(:initiative, 2, assignee: nil) } - let!(:assigned_initiatives) { create_list(:initiative, 3, assignee: assignee) } - - describe 'filtering on an assignee ID' do - let(:params) { { assignee: assignee.id } } - - it 'returns the correct records' do - expect(record_ids).to match_array assigned_initiatives.map(&:id) - end - end - - describe 'filtering on unassigned' do - let(:params) { { assignee: 'unassigned' } } - - it 'returns the correct records' do - expect(record_ids).to match_array unassigned_initiatives.map(&:id) - end - end - end - - describe '#feedback_needed_condition' do - let(:initiative_status) { create(:initiative_status) } - let(:feedback_needed_initiative_status) { create(:initiative_status, code: 'threshold_reached') } - - context 'when true' do - before do - create(:initiative, initiative_status: initiative_status) - create(:initiative, initiative_status: feedback_needed_initiative_status) - params[:feedback_needed] = true - end - - it 'filters by feedback needed' do - expect(record_ids).to match_array Initiative.feedback_needed.pluck(:id) - end - end - - context 'when false' do - before do - create(:initiative, initiative_status: initiative_status) - create(:initiative, initiative_status: feedback_needed_initiative_status) - params[:feedback_needed] = false - end - - it 'filters by feedback not needed' do - expect(record_ids).to match_array Initiative.no_feedback_needed.pluck(:id) - end - end - end - - describe '#author_condition' do - let(:author) { create(:user) } - - before do - create(:initiative, author_id: author.id) - params[:author] = author.id - end - - it 'filters by author' do - expect(record_ids).to match_array Initiative.where(author_id: author.id).pluck(:id) - end - end - - describe '#search_condition' do - let(:slug) { 'slug-1' } - let(:expected_record_ids) { Initiative.search_by_all(slug).pluck(:id) } - - before do - params[:search] = slug - end - - it 'returns the correct records' do - create(:initiative, slug: slug) - expect(record_ids).to match_array expected_record_ids - end - end - - describe '#publication_status_condition' do - let(:publication_status) { :published } - - before do - create(:initiative, publication_status: publication_status) - params[:publication_status] = publication_status - end - - it 'filters by publication_status' do - expect(record_ids).to match_array Initiative.where(publication_status: publication_status).pluck(:id) - end - end - - describe '#bounding_box_condition' do - let(:bounding_box) { '[51.208758, 3.224363, 50.000667, 5.715281]' } - - before do - params[:bounding_box] = bounding_box - end - - it 'filters by bounding_box' do - expect(record_ids).to match_array Initiative.with_bounding_box(bounding_box).pluck(:id) - end - end - - describe '#initiatives_condition' do - let(:ids) { Initiative.limit(2).pluck(:id) } - - before do - params[:initiatives] = ids - end - - it 'filters by initiatives' do - expect(record_ids).to match_array Initiative.where(id: ids).pluck(:id) - end - end -end From af95d9786f6ed212b9efb81a0ac9660fc8d30402 Mon Sep 17 00:00:00 2001 From: Simon Tharby Date: Tue, 17 Dec 2024 17:18:45 +0000 Subject: [PATCH 19/63] [TAN-1084] Remove initiatives from UserController#comments_count + clean up users_spec --- .../web_api/v1/users_controller.rb | 8 ++--- back/spec/acceptance/users_spec.rb | 33 ++----------------- 2 files changed, 5 insertions(+), 36 deletions(-) diff --git a/back/app/controllers/web_api/v1/users_controller.rb b/back/app/controllers/web_api/v1/users_controller.rb index 8f2e579fd60b..b36f70e5ecda 100644 --- a/back/app/controllers/web_api/v1/users_controller.rb +++ b/back/app/controllers/web_api/v1/users_controller.rb @@ -203,18 +203,14 @@ def initiatives_count def comments_count count = 0 published_comments = @user.comments.published + # TODO: cleanup-after-proposals-migration + # This test won't make sense once Comment.post_type is removed if !params[:post_type] || params[:post_type] == 'Idea' count += policy_scope( published_comments.where(post_type: 'Idea'), policy_scope_class: IdeaCommentPolicy::Scope ).count end - if !params[:post_type] || params[:post_type] == 'Initiative' - count += policy_scope( - published_comments.where(post_type: 'Initiative'), - policy_scope_class: InitiativeCommentPolicy::Scope - ).count - end render json: raw_json({ count: count }), status: :ok end diff --git a/back/spec/acceptance/users_spec.rb b/back/spec/acceptance/users_spec.rb index e1a08f72435a..16592f2e9819 100644 --- a/back/spec/acceptance/users_spec.rb +++ b/back/spec/acceptance/users_spec.rb @@ -1439,31 +1439,15 @@ end end - # TODO: cleanup-after-proposals-migration - get 'web_api/v1/users/:id/initiatives_count' do - let(:id) { @user.id } - - example 'Get the number of initiatives published by one user' do - create(:initiative, author: @user) - create(:initiative) - create(:initiative, author: @user, publication_status: 'draft') - do_request - assert_status 200 - json_response = json_parse response_body - expect(json_response.dig(:data, :type)).to eq 'initiatives_count' - expect(json_response.dig(:data, :attributes, :count)).to eq 1 - end - end - get 'web_api/v1/users/:id/comments_count' do parameter :post_type, "Count only comments of one post type. Either 'Idea' or 'Initiative'.", required: false let(:id) { @user.id } example 'Get the number of comments posted by one user' do - create(:comment, author: @user, post: create(:initiative)) create(:comment) create(:comment, author: @user, post: create(:idea)) + create(:comment, author: @user, post: create(:idea)) create(:comment, author: @user, publication_status: 'deleted') do_request expect(status).to eq 200 @@ -1471,6 +1455,8 @@ expect(json_response.dig(:data, :attributes, :count)).to eq 2 end + # TODO: cleanup-after-proposals-migration + # This test won't make sense once Comment.post_type is removed example 'Get the number of comments on ideas posted by one user' do create(:comment, author: @user, post: create(:initiative)) create(:comment, post: create(:initiative)) @@ -1482,19 +1468,6 @@ json_response = json_parse(response_body) expect(json_response.dig(:data, :attributes, :count)).to eq 2 end - - # TODO: cleanup-after-proposals-migration - example 'Get the number of comments on initiatives posted by one user' do - create(:comment, author: @user, post: create(:initiative)) - create(:comment, author: @user, post: create(:initiative)) - create(:comment, post: create(:idea)) - create(:comment, author: @user, post: create(:idea)) - create(:comment, author: @user, publication_status: 'deleted', post: create(:initiative)) - do_request post_type: 'Initiative' - expect(status).to eq 200 - json_response = json_parse(response_body) - expect(json_response.dig(:data, :attributes, :count)).to eq 2 - end end end end From df14123e37e0116db396e19f98f9041dfa28602c Mon Sep 17 00:00:00 2001 From: Simon Tharby Date: Tue, 17 Dec 2024 17:19:46 +0000 Subject: [PATCH 20/63] [TAN-1084] Remove comment_on_initiative_you_follow notification + spec --- .../comment_on_initiative_you_follow.rb | 96 ------------------- .../comment_on_initiative_you_follow_spec.rb | 33 ------- 2 files changed, 129 deletions(-) delete mode 100644 back/app/models/notifications/comment_on_initiative_you_follow.rb delete mode 100644 back/spec/models/notifications/comment_on_initiative_you_follow_spec.rb diff --git a/back/app/models/notifications/comment_on_initiative_you_follow.rb b/back/app/models/notifications/comment_on_initiative_you_follow.rb deleted file mode 100644 index 210a153d1da7..000000000000 --- a/back/app/models/notifications/comment_on_initiative_you_follow.rb +++ /dev/null @@ -1,96 +0,0 @@ -# frozen_string_literal: true - -# == Schema Information -# -# Table name: notifications -# -# id :uuid not null, primary key -# type :string -# read_at :datetime -# recipient_id :uuid -# post_id :uuid -# comment_id :uuid -# project_id :uuid -# created_at :datetime not null -# updated_at :datetime not null -# initiating_user_id :uuid -# spam_report_id :uuid -# invite_id :uuid -# reason_code :string -# other_reason :string -# post_status_id :uuid -# official_feedback_id :uuid -# phase_id :uuid -# post_type :string -# post_status_type :string -# project_folder_id :uuid -# inappropriate_content_flag_id :uuid -# internal_comment_id :uuid -# basket_id :uuid -# cosponsors_initiative_id :uuid -# cosponsorship_id :uuid -# -# Indexes -# -# index_notifications_on_basket_id (basket_id) -# index_notifications_on_cosponsors_initiative_id (cosponsors_initiative_id) -# index_notifications_on_cosponsorship_id (cosponsorship_id) -# index_notifications_on_created_at (created_at) -# index_notifications_on_inappropriate_content_flag_id (inappropriate_content_flag_id) -# index_notifications_on_initiating_user_id (initiating_user_id) -# index_notifications_on_internal_comment_id (internal_comment_id) -# index_notifications_on_invite_id (invite_id) -# index_notifications_on_official_feedback_id (official_feedback_id) -# index_notifications_on_phase_id (phase_id) -# index_notifications_on_post_id_and_post_type (post_id,post_type) -# index_notifications_on_post_status_id (post_status_id) -# index_notifications_on_post_status_id_and_post_status_type (post_status_id,post_status_type) -# index_notifications_on_recipient_id (recipient_id) -# index_notifications_on_recipient_id_and_read_at (recipient_id,read_at) -# index_notifications_on_spam_report_id (spam_report_id) -# -# Foreign Keys -# -# fk_rails_... (basket_id => baskets.id) -# fk_rails_... (comment_id => comments.id) -# fk_rails_... (cosponsors_initiative_id => cosponsors_initiatives.id) -# fk_rails_... (cosponsorship_id => cosponsorships.id) -# fk_rails_... (inappropriate_content_flag_id => flag_inappropriate_content_inappropriate_content_flags.id) -# fk_rails_... (initiating_user_id => users.id) -# fk_rails_... (internal_comment_id => internal_comments.id) -# fk_rails_... (invite_id => invites.id) -# fk_rails_... (official_feedback_id => official_feedbacks.id) -# fk_rails_... (phase_id => phases.id) -# fk_rails_... (project_id => projects.id) -# fk_rails_... (recipient_id => users.id) -# fk_rails_... (spam_report_id => spam_reports.id) -# -module Notifications - class CommentOnInitiativeYouFollow < Notification - validates :comment, :initiating_user, :post, presence: true - validates :post_type, inclusion: { in: ['Initiative'] } - - ACTIVITY_TRIGGERS = { 'Comment' => { 'created' => true } } - EVENT_NAME = 'Comment on initiative you follow' - - def self.make_notifications_on(activity) - return [] unless AppConfiguration.instance.feature_activated? 'follow' - - comment = activity.item - initiator_id = comment&.author_id - - if comment.post_type == 'Initiative' && initiator_id - User.from_follows(comment.post.followers).where.not(id: initiator_id).map do |recipient| - new( - recipient_id: recipient.id, - initiating_user_id: initiator_id, - post: comment.post, - comment: comment - ) - end - else - [] - end - end - end -end diff --git a/back/spec/models/notifications/comment_on_initiative_you_follow_spec.rb b/back/spec/models/notifications/comment_on_initiative_you_follow_spec.rb deleted file mode 100644 index 41152605f43c..000000000000 --- a/back/spec/models/notifications/comment_on_initiative_you_follow_spec.rb +++ /dev/null @@ -1,33 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe Notifications::CommentOnInitiativeYouFollow do - describe 'make_notifications_on' do - it 'makes a notification on created comment activity' do - initiative = create(:initiative) - follower = create(:follower, followable: initiative) - comment = create(:comment, post: initiative) - activity = create(:activity, item: comment, action: 'created') - - notifications = described_class.make_notifications_on activity - expect(notifications.first).to have_attributes( - recipient_id: follower.user_id, - initiating_user_id: comment.author_id, - post_id: initiative.id, - comment_id: comment.id - ) - end - - it 'does not make a notification when follow feature is turned off' do - initiative = create(:initiative) - create(:follower, followable: initiative) - comment = create(:comment, post: initiative) - activity = create(:activity, item: comment, action: 'created') - - SettingsService.new.deactivate_feature! 'follow' - notifications = described_class.make_notifications_on activity - expect(notifications).to be_empty - end - end -end From fa6efd6e6c887804c333b12f05820ccd704963dd Mon Sep 17 00:00:00 2001 From: Simon Tharby Date: Tue, 17 Dec 2024 17:26:35 +0000 Subject: [PATCH 21/63] [TAN-1084] Remove notifications/status_change_on_initiative_you_follow + spec --- .../status_change_on_initiative_you_follow.rb | 93 ------------------- .../analytics/populate_dimensions_service.rb | 6 +- .../populate_dimensions_service_spec.rb | 4 +- ...us_change_on_initiative_you_follow_spec.rb | 39 -------- 4 files changed, 3 insertions(+), 139 deletions(-) delete mode 100644 back/app/models/notifications/status_change_on_initiative_you_follow.rb delete mode 100644 back/spec/models/notifications/status_change_on_initiative_you_follow_spec.rb diff --git a/back/app/models/notifications/status_change_on_initiative_you_follow.rb b/back/app/models/notifications/status_change_on_initiative_you_follow.rb deleted file mode 100644 index 78182563c275..000000000000 --- a/back/app/models/notifications/status_change_on_initiative_you_follow.rb +++ /dev/null @@ -1,93 +0,0 @@ -# frozen_string_literal: true - -# == Schema Information -# -# Table name: notifications -# -# id :uuid not null, primary key -# type :string -# read_at :datetime -# recipient_id :uuid -# post_id :uuid -# comment_id :uuid -# project_id :uuid -# created_at :datetime not null -# updated_at :datetime not null -# initiating_user_id :uuid -# spam_report_id :uuid -# invite_id :uuid -# reason_code :string -# other_reason :string -# post_status_id :uuid -# official_feedback_id :uuid -# phase_id :uuid -# post_type :string -# post_status_type :string -# project_folder_id :uuid -# inappropriate_content_flag_id :uuid -# internal_comment_id :uuid -# basket_id :uuid -# cosponsors_initiative_id :uuid -# cosponsorship_id :uuid -# -# Indexes -# -# index_notifications_on_basket_id (basket_id) -# index_notifications_on_cosponsors_initiative_id (cosponsors_initiative_id) -# index_notifications_on_cosponsorship_id (cosponsorship_id) -# index_notifications_on_created_at (created_at) -# index_notifications_on_inappropriate_content_flag_id (inappropriate_content_flag_id) -# index_notifications_on_initiating_user_id (initiating_user_id) -# index_notifications_on_internal_comment_id (internal_comment_id) -# index_notifications_on_invite_id (invite_id) -# index_notifications_on_official_feedback_id (official_feedback_id) -# index_notifications_on_phase_id (phase_id) -# index_notifications_on_post_id_and_post_type (post_id,post_type) -# index_notifications_on_post_status_id (post_status_id) -# index_notifications_on_post_status_id_and_post_status_type (post_status_id,post_status_type) -# index_notifications_on_recipient_id (recipient_id) -# index_notifications_on_recipient_id_and_read_at (recipient_id,read_at) -# index_notifications_on_spam_report_id (spam_report_id) -# -# Foreign Keys -# -# fk_rails_... (basket_id => baskets.id) -# fk_rails_... (comment_id => comments.id) -# fk_rails_... (cosponsors_initiative_id => cosponsors_initiatives.id) -# fk_rails_... (cosponsorship_id => cosponsorships.id) -# fk_rails_... (inappropriate_content_flag_id => flag_inappropriate_content_inappropriate_content_flags.id) -# fk_rails_... (initiating_user_id => users.id) -# fk_rails_... (internal_comment_id => internal_comments.id) -# fk_rails_... (invite_id => invites.id) -# fk_rails_... (official_feedback_id => official_feedbacks.id) -# fk_rails_... (phase_id => phases.id) -# fk_rails_... (project_id => projects.id) -# fk_rails_... (recipient_id => users.id) -# fk_rails_... (spam_report_id => spam_reports.id) -# -module Notifications - class StatusChangeOnInitiativeYouFollow < Notification - validates :post_status, :post, presence: true - validates :post_type, inclusion: { in: ['Initiative'] } - - ACTIVITY_TRIGGERS = { 'Initiative' => { 'changed_status' => true } } - EVENT_NAME = 'Status change on initiative you follow' - - def self.make_notifications_on(activity) - return [] unless AppConfiguration.instance.feature_activated? 'follow' - - initiator_id = activity.user_id - initiative = activity.item - return [] if !initiative - - User.from_follows(initiative.followers).where.not(id: initiator_id).map do |recipient| - new( - recipient_id: recipient.id, - initiating_user_id: initiator_id, - post: initiative, - post_status: initiative.initiative_status - ) - end - end - end -end diff --git a/back/engines/commercial/analytics/app/services/analytics/populate_dimensions_service.rb b/back/engines/commercial/analytics/app/services/analytics/populate_dimensions_service.rb index 236c1d82c391..7fd955a9571c 100644 --- a/back/engines/commercial/analytics/app/services/analytics/populate_dimensions_service.rb +++ b/back/engines/commercial/analytics/app/services/analytics/populate_dimensions_service.rb @@ -13,12 +13,9 @@ def run def populate_types types = [ { name: 'idea', parent: 'post' }, - { name: 'initiative', parent: 'post' }, { name: 'proposal', parent: 'post' }, - { name: 'comment', parent: 'initiative' }, { name: 'comment', parent: 'idea' }, { name: 'comment', parent: 'proposal' }, - { name: 'reaction', parent: 'initiative' }, { name: 'reaction', parent: 'idea' }, { name: 'reaction', parent: 'comment' }, { name: 'poll', parent: nil }, @@ -43,8 +40,7 @@ def populate_dates tenant_creation = AppConfiguration.instance.created_at first_idea = Idea.order(:created_at).limit(1).pluck(:created_at)[0] - first_initiative = Initiative.order(:created_at).limit(1).pluck(:created_at)[0] - first_activity_date = [tenant_creation, first_idea, first_initiative].compact.min.to_date + first_activity_date = [tenant_creation, first_idea].compact.min.to_date if Analytics::DimensionDate.none? from = first_activity_date diff --git a/back/engines/commercial/analytics/spec/services/analytics/populate_dimensions_service_spec.rb b/back/engines/commercial/analytics/spec/services/analytics/populate_dimensions_service_spec.rb index 0a6544482012..044a7ba01b65 100644 --- a/back/engines/commercial/analytics/spec/services/analytics/populate_dimensions_service_spec.rb +++ b/back/engines/commercial/analytics/spec/services/analytics/populate_dimensions_service_spec.rb @@ -3,7 +3,7 @@ require 'rails_helper' describe Analytics::PopulateDimensionsService do - describe 'run with no ideas or initiatives' do + describe 'run with no ideas' do before_all do described_class.run end @@ -21,7 +21,7 @@ end it 'has 20 dimension types' do - expect(Analytics::DimensionType.count).to eq(20) + expect(Analytics::DimensionType.count).to eq(16) end it 'has 5 referrer types' do diff --git a/back/spec/models/notifications/status_change_on_initiative_you_follow_spec.rb b/back/spec/models/notifications/status_change_on_initiative_you_follow_spec.rb deleted file mode 100644 index 5ff07d281cb0..000000000000 --- a/back/spec/models/notifications/status_change_on_initiative_you_follow_spec.rb +++ /dev/null @@ -1,39 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe Notifications::StatusChangeOnInitiativeYouFollow do - describe 'make_notifications_on' do - it 'generates exactly one notification for each follower of the initiative' do - initiative = create(:initiative) - follower1 = create(:follower, followable: initiative) - create(:follower) - follower3 = create(:follower, followable: initiative) - - activity = create(:activity, item: initiative, action: 'changed_status') - - notifications = described_class.make_notifications_on activity - expect(notifications.map(&:recipient_id)).to contain_exactly follower1.user_id, follower3.user_id - end - - it "doesn't generate notifications for the initiating user" do - initiative = create(:initiative) - follower = create(:follower, followable: initiative) - - activity = create(:activity, item: initiative, action: 'changed_status', user: follower.user) - - notifications = described_class.make_notifications_on(activity) - expect(notifications).to eq [] - end - - it 'does not make a notification when follow feature is turned off' do - initiative = create(:initiative) - create(:follower, followable: initiative) - activity = create(:activity, item: initiative, action: 'changed_status') - - SettingsService.new.deactivate_feature! 'follow' - notifications = described_class.make_notifications_on activity - expect(notifications).to be_empty - end - end -end From c921772f822ae0802fe9d46bfbe289e3f7dec141 Mon Sep 17 00:00:00 2001 From: Simon Tharby Date: Tue, 17 Dec 2024 17:28:05 +0000 Subject: [PATCH 22/63] [TAN-1084] Remove side_fx_initiative_status_change_service + spec --- ...ide_fx_initiative_status_change_service.rb | 21 --------- ...x_initiative_status_change_service_spec.rb | 45 ------------------- 2 files changed, 66 deletions(-) delete mode 100644 back/app/services/side_fx_initiative_status_change_service.rb delete mode 100644 back/spec/services/side_fx_initiative_status_change_service_spec.rb diff --git a/back/app/services/side_fx_initiative_status_change_service.rb b/back/app/services/side_fx_initiative_status_change_service.rb deleted file mode 100644 index 8a53c4ae56cc..000000000000 --- a/back/app/services/side_fx_initiative_status_change_service.rb +++ /dev/null @@ -1,21 +0,0 @@ -# frozen_string_literal: true - -class SideFxInitiativeStatusChangeService - include SideFxHelper - - def before_create(change, user); end - - def after_create(change, user) - InitiativeStatusService.new.log_status_change change, user: user - - # Behaviour we want when proposal review feature is on and initiative_status_change is to 'proposed': - # * Create an Initiative 'proposed' activity type, to trigger the InitiativePublished campaign. - # * Lock (prevent) editing of the initiative. - # This works because, if the review feature is off, the associated initiative_status_change with code 'proposed' - # is created at the model level when the initiative is published, and does not invoke this method. - if change.initiative_status.code == 'proposed' - SideFxInitiativeService.new.log_initiative_proposed_activity(change.initiative, user) - change.initiative.update!(editing_locked: true) - end - end -end diff --git a/back/spec/services/side_fx_initiative_status_change_service_spec.rb b/back/spec/services/side_fx_initiative_status_change_service_spec.rb deleted file mode 100644 index 31509bbf8de0..000000000000 --- a/back/spec/services/side_fx_initiative_status_change_service_spec.rb +++ /dev/null @@ -1,45 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -# TODO: cleanup-after-proposals-migration -describe SideFxInitiativeStatusChangeService do - let(:service) { described_class.new } - let(:user) { create(:user) } - let(:initiative) { create(:initiative, author: user) } - - describe '#after_create' do - context "when status change is to 'proposed'" do - let!(:initiative_status_change) do - create( - :initiative_status_change, - initiative: initiative, - initiative_status: create(:initiative_status_proposed) - ) - end - - it 'logs a proposed activity job' do - expect { service.after_create(initiative_status_change, user) } - .to enqueue_job(LogActivityJob) - .with(initiative_status_change.initiative, 'proposed', user, instance_of(Integer)) - .exactly(1).times - end - end - - context "when status change is to 'review_pending'" do - let!(:initiative_status_change) do - create( - :initiative_status_change, - initiative: initiative, - initiative_status: create(:initiative_status_review_pending) - ) - end - - it "doesn't log a proposed activity job" do - expect { service.after_create(initiative_status_change, user) } - .not_to enqueue_job(LogActivityJob) - .with(instance_of(Initiative), 'proposed', anything, anything) - end - end - end -end From 910021628680c9d02f6930d608dc3e2cca11ce43 Mon Sep 17 00:00:00 2001 From: Simon Tharby Date: Tue, 17 Dec 2024 17:29:29 +0000 Subject: [PATCH 23/63] [TAN-1084] Remove notifications/official_feedback_on_initiative_you_follow + spec --- ...icial_feedback_on_initiative_you_follow.rb | 98 ------------------- ..._feedback_on_initiative_you_follow_spec.rb | 62 ------------ 2 files changed, 160 deletions(-) delete mode 100644 back/app/models/notifications/official_feedback_on_initiative_you_follow.rb delete mode 100644 back/spec/models/notifications/official_feedback_on_initiative_you_follow_spec.rb diff --git a/back/app/models/notifications/official_feedback_on_initiative_you_follow.rb b/back/app/models/notifications/official_feedback_on_initiative_you_follow.rb deleted file mode 100644 index 4a738eff9ec5..000000000000 --- a/back/app/models/notifications/official_feedback_on_initiative_you_follow.rb +++ /dev/null @@ -1,98 +0,0 @@ -# frozen_string_literal: true - -# == Schema Information -# -# Table name: notifications -# -# id :uuid not null, primary key -# type :string -# read_at :datetime -# recipient_id :uuid -# post_id :uuid -# comment_id :uuid -# project_id :uuid -# created_at :datetime not null -# updated_at :datetime not null -# initiating_user_id :uuid -# spam_report_id :uuid -# invite_id :uuid -# reason_code :string -# other_reason :string -# post_status_id :uuid -# official_feedback_id :uuid -# phase_id :uuid -# post_type :string -# post_status_type :string -# project_folder_id :uuid -# inappropriate_content_flag_id :uuid -# internal_comment_id :uuid -# basket_id :uuid -# cosponsors_initiative_id :uuid -# cosponsorship_id :uuid -# -# Indexes -# -# index_notifications_on_basket_id (basket_id) -# index_notifications_on_cosponsors_initiative_id (cosponsors_initiative_id) -# index_notifications_on_cosponsorship_id (cosponsorship_id) -# index_notifications_on_created_at (created_at) -# index_notifications_on_inappropriate_content_flag_id (inappropriate_content_flag_id) -# index_notifications_on_initiating_user_id (initiating_user_id) -# index_notifications_on_internal_comment_id (internal_comment_id) -# index_notifications_on_invite_id (invite_id) -# index_notifications_on_official_feedback_id (official_feedback_id) -# index_notifications_on_phase_id (phase_id) -# index_notifications_on_post_id_and_post_type (post_id,post_type) -# index_notifications_on_post_status_id (post_status_id) -# index_notifications_on_post_status_id_and_post_status_type (post_status_id,post_status_type) -# index_notifications_on_recipient_id (recipient_id) -# index_notifications_on_recipient_id_and_read_at (recipient_id,read_at) -# index_notifications_on_spam_report_id (spam_report_id) -# -# Foreign Keys -# -# fk_rails_... (basket_id => baskets.id) -# fk_rails_... (comment_id => comments.id) -# fk_rails_... (cosponsors_initiative_id => cosponsors_initiatives.id) -# fk_rails_... (cosponsorship_id => cosponsorships.id) -# fk_rails_... (inappropriate_content_flag_id => flag_inappropriate_content_inappropriate_content_flags.id) -# fk_rails_... (initiating_user_id => users.id) -# fk_rails_... (internal_comment_id => internal_comments.id) -# fk_rails_... (invite_id => invites.id) -# fk_rails_... (official_feedback_id => official_feedbacks.id) -# fk_rails_... (phase_id => phases.id) -# fk_rails_... (project_id => projects.id) -# fk_rails_... (recipient_id => users.id) -# fk_rails_... (spam_report_id => spam_reports.id) -# -module Notifications - class OfficialFeedbackOnInitiativeYouFollow < Notification - validates :initiating_user, :post, :official_feedback, presence: true - validates :post_type, inclusion: { in: ['Initiative'] } - - ACTIVITY_TRIGGERS = { 'OfficialFeedback' => { 'created' => true } } - EVENT_NAME = 'Official feedback on initiative you follow' - - def self.make_notifications_on(activity) - return [] unless AppConfiguration.instance.feature_activated? 'follow' - - official_feedback = activity.item - initiator_id = official_feedback.user_id - - if official_feedback.post_type == 'Initiative' && initiator_id - User.from_follows(official_feedback.post.followers).where.not(id: initiator_id).filter_map do |recipient| - next if InitiativeStatusChange.exists?(official_feedback: official_feedback) - - new( - recipient_id: recipient.id, - initiating_user_id: initiator_id, - post: official_feedback.post, - official_feedback: official_feedback - ) - end - else - [] - end - end - end -end diff --git a/back/spec/models/notifications/official_feedback_on_initiative_you_follow_spec.rb b/back/spec/models/notifications/official_feedback_on_initiative_you_follow_spec.rb deleted file mode 100644 index ec9a89ab10d1..000000000000 --- a/back/spec/models/notifications/official_feedback_on_initiative_you_follow_spec.rb +++ /dev/null @@ -1,62 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe Notifications::OfficialFeedbackOnInitiativeYouFollow do - describe 'make_notifications_on' do - it 'generates exactly one notification for each follower of the initiative' do - initiative = create(:initiative) - follower1 = create(:follower, followable: initiative) - create(:follower) - follower3 = create(:follower, followable: initiative) - - official_feedback = create(:official_feedback, post: initiative) - activity = create(:activity, item: official_feedback, action: :created) - - notifications = described_class.make_notifications_on(activity) - expect(notifications.map(&:recipient_id)).to contain_exactly follower1.user_id, follower3.user_id - end - - it "doesn't generate notifications for the initiating user" do - initiative = create(:initiative) - follower = create(:follower, followable: initiative) - - official_feedback = create(:official_feedback, post: initiative, user: follower.user) - activity = create(:activity, item: official_feedback, action: :created) - - notifications = described_class.make_notifications_on(activity) - expect(notifications).to eq [] - end - - it "doesn't generate notifications when there was a status change for the same official feedback" do - initiative = create(:initiative) - create(:follower, followable: initiative) - official_feedback = create(:official_feedback, post: initiative) - create(:initiative_status_change, initiative: initiative, official_feedback: official_feedback) - activity = create(:activity, item: official_feedback, action: :created) - - notifications = described_class.make_notifications_on(activity) - expect(notifications).to eq [] - end - - it "doesn't generate notifications when the post is an idea" do - idea = create(:idea) - official_feedback = create(:official_feedback, post: idea) - activity = create(:activity, item: official_feedback, action: :created) - - notifications = described_class.make_notifications_on(activity) - expect(notifications).to eq [] - end - - it 'does not make a notification when follow feature is turned off' do - initiative = create(:initiative) - create(:follower, followable: initiative) - official_feedback = create(:official_feedback, post: initiative) - activity = create(:activity, item: official_feedback, action: :created) - - SettingsService.new.deactivate_feature! 'follow' - notifications = described_class.make_notifications_on activity - expect(notifications).to be_empty - end - end -end From cf688c94a4ffd40449a176b3ddd94d301dc75e58 Mon Sep 17 00:00:00 2001 From: Simon Tharby Date: Wed, 18 Dec 2024 09:19:31 +0000 Subject: [PATCH 24/63] [TAN-2538] Remove 3 deleted notification classes from NotificationService NOTIFICATION_CLASSES --- back/app/services/notification_service.rb | 3 --- 1 file changed, 3 deletions(-) diff --git a/back/app/services/notification_service.rb b/back/app/services/notification_service.rb index b836c6ce1334..1e72f4b1b590 100644 --- a/back/app/services/notification_service.rb +++ b/back/app/services/notification_service.rb @@ -6,7 +6,6 @@ class NotificationService Notifications::CommentDeletedByAdmin, Notifications::CommentMarkedAsSpam, Notifications::CommentOnIdeaYouFollow, - Notifications::CommentOnInitiativeYouFollow, Notifications::CommentOnYourComment, Notifications::CosponsorOfYourIdea, Notifications::CosponsorOfYourInitiative, @@ -30,7 +29,6 @@ class NotificationService Notifications::MentionInOfficialFeedback, Notifications::NativeSurveyNotSubmitted, Notifications::OfficialFeedbackOnIdeaYouFollow, - Notifications::OfficialFeedbackOnInitiativeYouFollow, Notifications::ProjectFolderModerationRightsReceived, Notifications::ProjectModerationRightsReceived, Notifications::ProjectPhaseStarted, @@ -39,7 +37,6 @@ class NotificationService Notifications::ProjectReviewRequest, Notifications::ProjectReviewStateChange, Notifications::StatusChangeOnIdeaYouFollow, - Notifications::StatusChangeOnInitiativeYouFollow, Notifications::ThresholdReachedForAdmin, Notifications::VotingBasketNotSubmitted, Notifications::VotingBasketSubmitted, From a9014e4a9c2c52206b3c4c9ec118e433828fcd47 Mon Sep 17 00:00:00 2001 From: Simon Tharby Date: Wed, 18 Dec 2024 09:41:30 +0000 Subject: [PATCH 25/63] [TAN-2538] Remove test from notification service --- back/spec/serializers/web_api/v1/notifications_spec.rb | 7 ------- 1 file changed, 7 deletions(-) diff --git a/back/spec/serializers/web_api/v1/notifications_spec.rb b/back/spec/serializers/web_api/v1/notifications_spec.rb index b5a12b4d3c9e..4ccf9d0f2e4e 100644 --- a/back/spec/serializers/web_api/v1/notifications_spec.rb +++ b/back/spec/serializers/web_api/v1/notifications_spec.rb @@ -43,13 +43,6 @@ def expect_serializer_to_hide_name(user1, user2, admin, notification_factory_nam ) end - it 'serializes CommentOnInitiativeYouFollow correctly' do - expect_serializer_to_hide_name( - john, jane, admin, :comment_on_initiative_you_follow, - WebApi::V1::Notifications::CommentOnInitiativeYouFollowSerializer - ) - end - it 'serializes CommentOnIdeaYouFollow correctly' do expect_serializer_to_hide_name( john, jane, admin, :comment_on_idea_you_follow, From 9df49f0d0fda2780ef5ce73d9818589ed8f12fcb Mon Sep 17 00:00:00 2001 From: Simon Tharby Date: Wed, 18 Dec 2024 09:48:46 +0000 Subject: [PATCH 26/63] [TAN-2538] Remove status_change_on_initiative_you_follow mail code & specs --- ...nge_on_initiative_you_follow_serializer.rb | 15 --- ..._change_on_initiative_you_follow_mailer.rb | 29 ------ .../status_change_on_initiative_you_follow.rb | 95 ------------------- .../spec/factories/campaigns.rb | 4 - ...on_initiative_you_follow_mailer_preview.rb | 18 ---- ...ge_on_initiative_you_follow_mailer_spec.rb | 45 --------- ...us_change_on_initiative_you_follow_spec.rb | 38 -------- back/spec/factories/notifications.rb | 8 -- 8 files changed, 252 deletions(-) delete mode 100644 back/app/serializers/web_api/v1/notifications/status_change_on_initiative_you_follow_serializer.rb delete mode 100644 back/engines/free/email_campaigns/app/mailers/email_campaigns/status_change_on_initiative_you_follow_mailer.rb delete mode 100644 back/engines/free/email_campaigns/app/models/email_campaigns/campaigns/status_change_on_initiative_you_follow.rb delete mode 100644 back/engines/free/email_campaigns/spec/mailers/previews/email_campaigns/status_change_on_initiative_you_follow_mailer_preview.rb delete mode 100644 back/engines/free/email_campaigns/spec/mailers/status_change_on_initiative_you_follow_mailer_spec.rb delete mode 100644 back/engines/free/email_campaigns/spec/models/email_campaigns/campaigns/status_change_on_initiative_you_follow_spec.rb diff --git a/back/app/serializers/web_api/v1/notifications/status_change_on_initiative_you_follow_serializer.rb b/back/app/serializers/web_api/v1/notifications/status_change_on_initiative_you_follow_serializer.rb deleted file mode 100644 index a4aa6571309b..000000000000 --- a/back/app/serializers/web_api/v1/notifications/status_change_on_initiative_you_follow_serializer.rb +++ /dev/null @@ -1,15 +0,0 @@ -# frozen_string_literal: true - -class WebApi::V1::Notifications::StatusChangeOnInitiativeYouFollowSerializer < WebApi::V1::Notifications::NotificationSerializer - attribute :post_title_multiloc do |object| - object.post&.title_multiloc - end - - attribute :post_slug do |object| - object.post&.slug - end - - attribute :initiative_status_title_multiloc do |object| - object.post_status&.title_multiloc - end -end diff --git a/back/engines/free/email_campaigns/app/mailers/email_campaigns/status_change_on_initiative_you_follow_mailer.rb b/back/engines/free/email_campaigns/app/mailers/email_campaigns/status_change_on_initiative_you_follow_mailer.rb deleted file mode 100644 index ff92361291e7..000000000000 --- a/back/engines/free/email_campaigns/app/mailers/email_campaigns/status_change_on_initiative_you_follow_mailer.rb +++ /dev/null @@ -1,29 +0,0 @@ -# frozen_string_literal: true - -module EmailCampaigns - class StatusChangeOnInitiativeYouFollowMailer < ApplicationMailer - private - - def preheader - format_message('preheader') - end - - def subject - format_message('subject') - end - - def header_title - format_message('header_title', values: { organizationName: organization_name }) - end - - def header_message - format_message( - 'header_message', - values: { - initiativeTitle: localize_for_recipient(event.post_title_multiloc), - organizationName: organization_name - } - ) - end - end -end diff --git a/back/engines/free/email_campaigns/app/models/email_campaigns/campaigns/status_change_on_initiative_you_follow.rb b/back/engines/free/email_campaigns/app/models/email_campaigns/campaigns/status_change_on_initiative_you_follow.rb deleted file mode 100644 index 514573af0cad..000000000000 --- a/back/engines/free/email_campaigns/app/models/email_campaigns/campaigns/status_change_on_initiative_you_follow.rb +++ /dev/null @@ -1,95 +0,0 @@ -# frozen_string_literal: true - -# == Schema Information -# -# Table name: email_campaigns_campaigns -# -# id :uuid not null, primary key -# type :string not null -# author_id :uuid -# enabled :boolean -# sender :string -# reply_to :string -# schedule :jsonb -# subject_multiloc :jsonb -# body_multiloc :jsonb -# created_at :datetime not null -# updated_at :datetime not null -# deliveries_count :integer default(0), not null -# context_id :uuid -# -# Indexes -# -# index_email_campaigns_campaigns_on_author_id (author_id) -# index_email_campaigns_campaigns_on_context_id (context_id) -# index_email_campaigns_campaigns_on_type (type) -# -# Foreign Keys -# -# fk_rails_... (author_id => users.id) -# -module EmailCampaigns - class Campaigns::StatusChangeOnInitiativeYouFollow < Campaign - include Consentable - include Disableable - include ActivityTriggerable - include RecipientConfigurable - include Trackable - include LifecycleStageRestrictable - allow_lifecycle_stages only: %w[trial active] - - recipient_filter :filter_notification_recipient - - def mailer_class - StatusChangeOnInitiativeYouFollowMailer - end - - def activity_triggers - { 'Notifications::StatusChangeOnInitiativeYouFollow' => { 'created' => true } } - end - - def filter_notification_recipient(users_scope, activity:, time: nil) - users_scope.where(id: activity.item.recipient_id) - end - - def self.recipient_role_multiloc_key - 'email_campaigns.admin_labels.recipient_role.registered_users' - end - - def self.recipient_segment_multiloc_key - 'email_campaigns.admin_labels.recipient_segment.users_who_follow_the_proposal' - end - - def self.content_type_multiloc_key - 'email_campaigns.admin_labels.content_type.proposals' - end - - def self.trigger_multiloc_key - 'email_campaigns.admin_labels.trigger.proposal_status_changes' - end - - def generate_commands(recipient:, activity:) - initiative = activity.item.post - status = initiative.initiative_status - [{ - event_payload: { - post_id: initiative.id, - post_title_multiloc: initiative.title_multiloc, - post_body_multiloc: initiative.body_multiloc, - post_url: Frontend::UrlService.new.model_to_url(initiative, locale: Locale.new(recipient.locale)), - post_images: initiative.initiative_images.map do |image| - { - ordering: image.ordering, - versions: image.image.versions.to_h { |k, v| [k.to_s, v.url] } - } - end, - initiative_status_id: status.id, - initiative_status_title_multiloc: status.title_multiloc, - initiative_status_code: status.code, - initiative_status_color: status.color, - unfollow_url: Frontend::UrlService.new.unfollow_url(Follower.new(followable: initiative, user: recipient)) - } - }] - end - end -end diff --git a/back/engines/free/email_campaigns/spec/factories/campaigns.rb b/back/engines/free/email_campaigns/spec/factories/campaigns.rb index 2f45ee7afadf..83d15041ddb9 100644 --- a/back/engines/free/email_campaigns/spec/factories/campaigns.rb +++ b/back/engines/free/email_campaigns/spec/factories/campaigns.rb @@ -190,10 +190,6 @@ enabled { true } end - factory :status_change_on_initiative_you_follow_campaign, class: EmailCampaigns::Campaigns::StatusChangeOnInitiativeYouFollow do - enabled { true } - end - factory :threshold_reached_for_admin_campaign, class: EmailCampaigns::Campaigns::ThresholdReachedForAdmin do enabled { true } end diff --git a/back/engines/free/email_campaigns/spec/mailers/previews/email_campaigns/status_change_on_initiative_you_follow_mailer_preview.rb b/back/engines/free/email_campaigns/spec/mailers/previews/email_campaigns/status_change_on_initiative_you_follow_mailer_preview.rb deleted file mode 100644 index d864c049650c..000000000000 --- a/back/engines/free/email_campaigns/spec/mailers/previews/email_campaigns/status_change_on_initiative_you_follow_mailer_preview.rb +++ /dev/null @@ -1,18 +0,0 @@ -# frozen_string_literal: true - -module EmailCampaigns - class StatusChangeOnInitiativeYouFollowMailerPreview < ActionMailer::Preview - include EmailCampaigns::MailerPreviewRecipient - - def campaign_mail - campaign = EmailCampaigns::Campaigns::StatusChangeOnInitiativeYouFollow.first - - command = campaign.generate_commands( - recipient: recipient_user, - activity: Activity.new(item: Notification.new(post: Initiative.first)) - ).first.merge({ recipient: recipient_user }) - - campaign.mailer_class.with(campaign: campaign, command: command).campaign_mail - end - end -end diff --git a/back/engines/free/email_campaigns/spec/mailers/status_change_on_initiative_you_follow_mailer_spec.rb b/back/engines/free/email_campaigns/spec/mailers/status_change_on_initiative_you_follow_mailer_spec.rb deleted file mode 100644 index 1624a76761c4..000000000000 --- a/back/engines/free/email_campaigns/spec/mailers/status_change_on_initiative_you_follow_mailer_spec.rb +++ /dev/null @@ -1,45 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe EmailCampaigns::StatusChangeOnInitiativeYouFollowMailer do - describe 'campaign_mail' do - let_it_be(:recipient) { create(:user, locale: 'en') } - let_it_be(:campaign) { EmailCampaigns::Campaigns::StatusChangeOnInitiativeYouFollow.create! } - let_it_be(:initiative) { create(:initiative) } - let_it_be(:command) do - campaign.generate_commands( - recipient: recipient, - activity: Activity.new(item: Notification.new(post: initiative)) - ).first.merge({ recipient: recipient }) - end - - let_it_be(:mail) { described_class.with(command: command, campaign: campaign).campaign_mail.deliver_now } - - before { EmailCampaigns::UnsubscriptionToken.create!(user_id: recipient.id) } - - it 'renders the subject' do - expect(mail.subject).to start_with('The status of a proposal you follow has changed') - end - - it 'renders the sender email' do - expect(mail.from).to all(end_with('@citizenlab.co')) - end - - it 'assigns organisation name' do - expect(mail.body.encoded).to match(AppConfiguration.instance.settings('core', 'organization_name', 'en')) - end - - it 'assigns cta url' do - expect(mail.body.encoded).to match(command.dig(:event_payload, :post_url)) - end - - it 'includes the initiative title' do - expect(mail.body.encoded).to match(initiative.title_multiloc['en']) - end - - it 'includes the unfollow url' do - expect(mail.body.encoded).to match(Frontend::UrlService.new.unfollow_url(Follower.new(followable: initiative, user: recipient))) - end - end -end diff --git a/back/engines/free/email_campaigns/spec/models/email_campaigns/campaigns/status_change_on_initiative_you_follow_spec.rb b/back/engines/free/email_campaigns/spec/models/email_campaigns/campaigns/status_change_on_initiative_you_follow_spec.rb deleted file mode 100644 index ba1d6ce6345b..000000000000 --- a/back/engines/free/email_campaigns/spec/models/email_campaigns/campaigns/status_change_on_initiative_you_follow_spec.rb +++ /dev/null @@ -1,38 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe EmailCampaigns::Campaigns::StatusChangeOnInitiativeYouFollow do - describe 'StatusChangeOnInitiativeYouFollow campaign default factory' do - it 'is valid' do - expect(build(:status_change_on_initiative_you_follow_campaign)).to be_valid - end - end - - describe '#generate_commands' do - let(:campaign) { create(:status_change_on_initiative_you_follow_campaign) } - let(:notification) { create(:status_change_on_initiative_you_follow) } - let(:notification_activity) { create(:activity, item: notification, action: 'created') } - - it 'generates a command with the desired payload and tracked content' do - command = campaign.generate_commands( - recipient: notification_activity.item.recipient, - activity: notification_activity - ).first - - expect(command).to match({ - event_payload: hash_including( - post_id: notification.post.id, - post_title_multiloc: notification.post.title_multiloc, - post_body_multiloc: notification.post.body_multiloc, - post_url: an_instance_of(String), - post_images: an_instance_of(Array), - initiative_status_id: notification.post_status.id, - initiative_status_title_multiloc: notification.post_status.title_multiloc, - initiative_status_code: notification.post_status.code, - initiative_status_color: notification.post_status.color - ) - }) - end - end -end diff --git a/back/spec/factories/notifications.rb b/back/spec/factories/notifications.rb index 8ed70d4f7e65..56c2bad6d803 100644 --- a/back/spec/factories/notifications.rb +++ b/back/spec/factories/notifications.rb @@ -236,14 +236,6 @@ end end - factory :status_change_on_initiative_you_follow, parent: :notification, class: 'Notifications::StatusChangeOnInitiativeYouFollow' do - association :post, factory: :initiative - association :post_status, factory: :initiative_status - before(:create) do |notification| - notification.post.initiative_status_changes.create!(initiative_status: notification.post_status) - end - end - factory :threshold_reached_for_admin, parent: :notification, class: 'Notifications::ThresholdReachedForAdmin' do association :post, factory: :initiative association :post_status, factory: :proposals_status From c05e9fd6ccfcae5f1e8bdf838b32417327cd42b4 Mon Sep 17 00:00:00 2001 From: Simon Tharby Date: Wed, 18 Dec 2024 09:56:06 +0000 Subject: [PATCH 27/63] [TAN-2538] Remove MJML for StatusChangeOnInitiativeYouFollow --- .../email_campaigns/delivery_service.rb | 1 - .../campaign_mail.mjml | 17 ----------------- 2 files changed, 18 deletions(-) delete mode 100644 back/engines/free/email_campaigns/app/views/email_campaigns/status_change_on_initiative_you_follow_mailer/campaign_mail.mjml diff --git a/back/engines/free/email_campaigns/app/services/email_campaigns/delivery_service.rb b/back/engines/free/email_campaigns/app/services/email_campaigns/delivery_service.rb index f1c056601489..f1d727ca38e3 100644 --- a/back/engines/free/email_campaigns/app/services/email_campaigns/delivery_service.rb +++ b/back/engines/free/email_campaigns/app/services/email_campaigns/delivery_service.rb @@ -51,7 +51,6 @@ class DeliveryService Campaigns::ProjectReviewRequest, Campaigns::ProjectReviewStateChange, Campaigns::StatusChangeOnIdeaYouFollow, - Campaigns::StatusChangeOnInitiativeYouFollow, Campaigns::ThresholdReachedForAdmin, Campaigns::UserDigest, Campaigns::VotingBasketNotSubmitted, diff --git a/back/engines/free/email_campaigns/app/views/email_campaigns/status_change_on_initiative_you_follow_mailer/campaign_mail.mjml b/back/engines/free/email_campaigns/app/views/email_campaigns/status_change_on_initiative_you_follow_mailer/campaign_mail.mjml deleted file mode 100644 index c7ef267bd366..000000000000 --- a/back/engines/free/email_campaigns/app/views/email_campaigns/status_change_on_initiative_you_follow_mailer/campaign_mail.mjml +++ /dev/null @@ -1,17 +0,0 @@ - - - - <%= format_message('status_change', values: { - initiativeStatus: localize_for_recipient(event.initiative_status_title_multiloc) - }) %> - - - - -<%= render partial: 'application/cta_button', locals: { href: event.post_url , message: format_message('cta_goto_proposal', component: 'general') } %> - -<% if AppConfiguration.instance.feature_activated?('follow') %> - <%= render 'email_campaigns/follow/card' %> -<% end %> From 5586579828b0a5d4c3975a34894a063eed3849d6 Mon Sep 17 00:00:00 2001 From: Simon Tharby Date: Wed, 18 Dec 2024 10:04:06 +0000 Subject: [PATCH 28/63] [TAN-2538] Fix footer spec --- .../free/email_campaigns/spec/mailers/footer_spec.rb | 8 ++++---- back/spec/jobs/log_activity_job_spec.rb | 3 --- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/back/engines/free/email_campaigns/spec/mailers/footer_spec.rb b/back/engines/free/email_campaigns/spec/mailers/footer_spec.rb index febaf59c6904..888ca058e9b7 100644 --- a/back/engines/free/email_campaigns/spec/mailers/footer_spec.rb +++ b/back/engines/free/email_campaigns/spec/mailers/footer_spec.rb @@ -10,15 +10,15 @@ let(:locale) { 'en' } let(:recipient) { create(:user, locale: locale) } - let(:campaign) { EmailCampaigns::Campaigns::CommentOnInitiativeYouFollow.create! } + let(:campaign) { EmailCampaigns::Campaigns::CommentOnIdeaYouFollow.create! } let(:command) do - create(:comment_on_initiative_you_follow_campaign).generate_commands( - activity: create(:activity, item: create(:comment_on_initiative_you_follow), action: 'created'), + create(:comment_on_idea_you_follow_campaign).generate_commands( + activity: create(:activity, item: create(:comment_on_idea_you_follow), action: 'created'), recipient: recipient ).first.merge({ recipient: recipient }) end - let(:mail) { EmailCampaigns::CommentOnInitiativeYouFollowMailer.with(command: command, campaign: campaign).campaign_mail.deliver_now } + let(:mail) { EmailCampaigns::CommentOnIdeaYouFollowMailer.with(command: command, campaign: campaign).campaign_mail.deliver_now } it 'includes Go Vocal logo' do expect(mail.body.encoded).to have_tag('a', with: { href: 'https://govocal.com/' }) do diff --git a/back/spec/jobs/log_activity_job_spec.rb b/back/spec/jobs/log_activity_job_spec.rb index 476920f7b05f..71c84ea8b6ee 100644 --- a/back/spec/jobs/log_activity_job_spec.rb +++ b/back/spec/jobs/log_activity_job_spec.rb @@ -178,7 +178,6 @@ Notifications::CommentMarkedAsSpam, Notifications::CommentOnYourComment, Notifications::CommentOnIdeaYouFollow, - Notifications::CommentOnInitiativeYouFollow, Notifications::IdeaMarkedAsSpam, Notifications::InitiativeAssignedToYou, Notifications::InitiativeMarkedAsSpam, @@ -187,14 +186,12 @@ Notifications::MentionInComment, Notifications::MentionInOfficialFeedback, Notifications::OfficialFeedbackOnIdeaYouFollow, - Notifications::OfficialFeedbackOnInitiativeYouFollow, Notifications::ProjectFolderModerationRightsReceived, Notifications::ProjectModerationRightsReceived, Notifications::ProjectPhaseStarted, Notifications::ProjectPhaseUpcoming, Notifications::ProjectPublished, Notifications::StatusChangeOnIdeaYouFollow, - Notifications::StatusChangeOnInitiativeYouFollow, Notifications::ThresholdReachedForAdmin, OfficialFeedback, Phase, From cea1e4c402deeb3972143baff5970d32533cda4f Mon Sep 17 00:00:00 2001 From: Simon Tharby Date: Wed, 18 Dec 2024 10:06:24 +0000 Subject: [PATCH 29/63] [TAN-2538] Remove OfficialFeedbackOnInitiativeYouFollow mail files & spec --- ...eedback_on_initiative_you_follow_mailer.rb | 36 -------------- .../campaign_mail.mjml | 7 --- ...ck_on_initiative_you_follow_mailer_spec.rb | 48 ------------------- ...on_initiative_you_follow_mailer_preview.rb | 25 ---------- 4 files changed, 116 deletions(-) delete mode 100644 back/engines/free/email_campaigns/app/mailers/email_campaigns/official_feedback_on_initiative_you_follow_mailer.rb delete mode 100644 back/engines/free/email_campaigns/app/views/email_campaigns/official_feedback_on_initiative_you_follow_mailer/campaign_mail.mjml delete mode 100644 back/engines/free/email_campaigns/spec/mailers/official_feedback_on_initiative_you_follow_mailer_spec.rb delete mode 100644 back/engines/free/email_campaigns/spec/mailers/previews/email_campaigns/official_feedback_on_initiative_you_follow_mailer_preview.rb diff --git a/back/engines/free/email_campaigns/app/mailers/email_campaigns/official_feedback_on_initiative_you_follow_mailer.rb b/back/engines/free/email_campaigns/app/mailers/email_campaigns/official_feedback_on_initiative_you_follow_mailer.rb deleted file mode 100644 index 058398f9070b..000000000000 --- a/back/engines/free/email_campaigns/app/mailers/email_campaigns/official_feedback_on_initiative_you_follow_mailer.rb +++ /dev/null @@ -1,36 +0,0 @@ -# frozen_string_literal: true - -module EmailCampaigns - class OfficialFeedbackOnInitiativeYouFollowMailer < ApplicationMailer - private - - helper_method :author_name - - def author_name - localize_for_recipient(event.official_feedback_author_multiloc) - end - - def preheader - format_message('preheader') - end - - def subject - format_message('subject', values: { organizationName: organization_name }) - end - - def header_title - format_message('header_title', values: { officialName: organization_name }) - end - - def header_message - format_message( - 'header_message', - values: { - initiativeTitle: localize_for_recipient(event.post_title_multiloc), - officialName: localize_for_recipient(event.official_feedback_author_multiloc), - organizationName: organization_name - } - ) - end - end -end diff --git a/back/engines/free/email_campaigns/app/views/email_campaigns/official_feedback_on_initiative_you_follow_mailer/campaign_mail.mjml b/back/engines/free/email_campaigns/app/views/email_campaigns/official_feedback_on_initiative_you_follow_mailer/campaign_mail.mjml deleted file mode 100644 index 27b52c41206d..000000000000 --- a/back/engines/free/email_campaigns/app/views/email_campaigns/official_feedback_on_initiative_you_follow_mailer/campaign_mail.mjml +++ /dev/null @@ -1,7 +0,0 @@ -<%= render 'email_campaigns/official_feedbacks/card' %> - -<%= render partial: 'application/cta_button', locals: { href: event.official_feedback_url , message: format_message('cta_goto_proposal', component: 'general') } %> - -<% if AppConfiguration.instance.feature_activated?('follow') %> - <%= render 'email_campaigns/follow/card' %> -<% end %> diff --git a/back/engines/free/email_campaigns/spec/mailers/official_feedback_on_initiative_you_follow_mailer_spec.rb b/back/engines/free/email_campaigns/spec/mailers/official_feedback_on_initiative_you_follow_mailer_spec.rb deleted file mode 100644 index bd1ace30947c..000000000000 --- a/back/engines/free/email_campaigns/spec/mailers/official_feedback_on_initiative_you_follow_mailer_spec.rb +++ /dev/null @@ -1,48 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe EmailCampaigns::OfficialFeedbackOnInitiativeYouFollowMailer do - describe 'campaign_mail' do - let_it_be(:recipient) { create(:user, locale: 'en') } - let_it_be(:initiative) { create(:initiative, title_multiloc: { 'en' => 'Initiative title' }) } - let_it_be(:feedback) { create(:official_feedback, body_multiloc: { 'en' => 'We appreciate your participation' }, post: initiative) } - let_it_be(:campaign) { EmailCampaigns::Campaigns::OfficialFeedbackOnInitiativeYouFollow.create! } - let_it_be(:notification) { create(:official_feedback_on_initiative_you_follow, recipient: recipient, post: initiative, official_feedback: feedback) } - let_it_be(:command) do - activity = create(:activity, item: notification, action: 'created') - create(:official_feedback_on_initiative_you_follow_campaign).generate_commands( - activity: activity, - recipient: recipient - ).first.merge({ recipient: recipient }) - end - - let_it_be(:mail) { described_class.with(command: command, campaign: campaign).campaign_mail.deliver_now } - - before { EmailCampaigns::UnsubscriptionToken.create!(user_id: recipient.id) } - - it 'renders the subject' do - expect(mail.subject).to start_with('A proposal you follow has received an official update') - end - - it 'renders the sender email' do - expect(mail.from).to all(end_with('@citizenlab.co')) - end - - it 'assigns organisation name' do - expect(mail.body.encoded).to match(AppConfiguration.instance.settings('core', 'organization_name', 'en')) - end - - it 'assigns cta url' do - expect(mail.body.encoded).to match(command.dig(:event_payload, :official_feedback_url)) - end - - it 'includes the initiative title' do - expect(mail.body.encoded).to match('Initiative title') - end - - it 'includes the unfollow url' do - expect(mail.body.encoded).to match(Frontend::UrlService.new.unfollow_url(Follower.new(followable: notification.post, user: recipient))) - end - end -end diff --git a/back/engines/free/email_campaigns/spec/mailers/previews/email_campaigns/official_feedback_on_initiative_you_follow_mailer_preview.rb b/back/engines/free/email_campaigns/spec/mailers/previews/email_campaigns/official_feedback_on_initiative_you_follow_mailer_preview.rb deleted file mode 100644 index 3b92ef49b676..000000000000 --- a/back/engines/free/email_campaigns/spec/mailers/previews/email_campaigns/official_feedback_on_initiative_you_follow_mailer_preview.rb +++ /dev/null @@ -1,25 +0,0 @@ -# frozen_string_literal: true - -module EmailCampaigns - class OfficialFeedbackOnInitiativeYouFollowMailerPreview < ActionMailer::Preview - include EmailCampaigns::MailerPreviewRecipient - - def campaign_mail - campaign = EmailCampaigns::Campaigns::OfficialFeedbackOnInitiativeYouFollow.first - - command = { - recipient: recipient_user, - event_payload: { - official_feedback_author_multiloc: { 'en' => 'City of Plattsburgh Official' }, - official_feedback_body_multiloc: { 'en' => 'Thank you for taking the time to comment on our parks. We value your feedback and will be looking at improving the dog park in the same way the one at the city beach has been improved.' }, - official_feedback_url: 'https://demo.stg.govocal.com', - post_published_at: Time.zone.today.prev_week.iso8601, - post_title_multiloc: { 'en' => 'Fence around the park' }, - post_author_name: 'Julia Langer' - } - } - - campaign.mailer_class.with(campaign: campaign, command: command).campaign_mail - end - end -end From 5a4e47062c7dfcc6c050fa94f28bd789a66ae442 Mon Sep 17 00:00:00 2001 From: Simon Tharby Date: Wed, 18 Dec 2024 10:08:39 +0000 Subject: [PATCH 30/63] [TAN-2538] Remove test from notification_spec --- back/spec/models/notification_spec.rb | 8 -------- 1 file changed, 8 deletions(-) diff --git a/back/spec/models/notification_spec.rb b/back/spec/models/notification_spec.rb index 362fe4225a52..3c94a8a4ba06 100644 --- a/back/spec/models/notification_spec.rb +++ b/back/spec/models/notification_spec.rb @@ -132,14 +132,6 @@ expect(described_class.count).to eq(count - 1) end - it 'deleting an initiative status also deletes notifications requiring that initiative status' do - initiative_status = create(:initiative_status) - create(:status_change_on_initiative_you_follow, post_status: initiative_status) - count = described_class.count - initiative_status.destroy! - expect(described_class.count).to eq(count - 1) - end - it 'deleting an invite also deletes notifications requiring that invite' do invite = create(:invite) create(:invite_accepted, invite: invite) From d56f56e8c450a8066dfd4dff04075ef5eecebf72 Mon Sep 17 00:00:00 2001 From: Simon Tharby Date: Wed, 18 Dec 2024 10:13:16 +0000 Subject: [PATCH 31/63] [TAN-2538] Remove test from fact_participation_spec --- .../spec/models/analytics/fact_participation_spec.rb | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/back/engines/commercial/analytics/spec/models/analytics/fact_participation_spec.rb b/back/engines/commercial/analytics/spec/models/analytics/fact_participation_spec.rb index 5dafc9046703..493ab1fea6bd 100644 --- a/back/engines/commercial/analytics/spec/models/analytics/fact_participation_spec.rb +++ b/back/engines/commercial/analytics/spec/models/analytics/fact_participation_spec.rb @@ -34,16 +34,6 @@ end end - # TODO: cleanup-after-proposals-migration - context 'when an initiative is created' do - let!(:initiative) { create(:initiative) } - - it 'is also available as a participation fact' do - participation = described_class.find(initiative.id) - expect(participation.dimension_type.name).to eq('initiative') - end - end - context 'when a comment is added' do let!(:comment) { create(:comment) } From 68c5c935238c1b1d8abc60b29a186173ff91944f Mon Sep 17 00:00:00 2001 From: Simon Tharby Date: Wed, 18 Dec 2024 10:18:55 +0000 Subject: [PATCH 32/63] [TAN-2538] Remove comment_on_initiative_you_follow mail files & specs --- ...ent_on_initiative_you_follow_serializer.rb | 24 ----- ...comment_on_initiative_you_follow_mailer.rb | 30 ------- .../comment_on_initiative_you_follow.rb | 89 ------------------- .../campaign_mail.mjml | 23 ----- ...nt_on_initiative_you_follow_mailer_spec.rb | 53 ----------- ...on_initiative_you_follow_mailer_preview.rb | 25 ------ .../comment_on_initiative_you_follow_spec.rb | 53 ----------- 7 files changed, 297 deletions(-) delete mode 100644 back/app/serializers/web_api/v1/notifications/comment_on_initiative_you_follow_serializer.rb delete mode 100644 back/engines/free/email_campaigns/app/mailers/email_campaigns/comment_on_initiative_you_follow_mailer.rb delete mode 100644 back/engines/free/email_campaigns/app/models/email_campaigns/campaigns/comment_on_initiative_you_follow.rb delete mode 100644 back/engines/free/email_campaigns/app/views/email_campaigns/comment_on_initiative_you_follow_mailer/campaign_mail.mjml delete mode 100644 back/engines/free/email_campaigns/spec/mailers/comment_on_initiative_you_follow_mailer_spec.rb delete mode 100644 back/engines/free/email_campaigns/spec/mailers/previews/email_campaigns/comment_on_initiative_you_follow_mailer_preview.rb delete mode 100644 back/engines/free/email_campaigns/spec/models/email_campaigns/campaigns/comment_on_initiative_you_follow_spec.rb diff --git a/back/app/serializers/web_api/v1/notifications/comment_on_initiative_you_follow_serializer.rb b/back/app/serializers/web_api/v1/notifications/comment_on_initiative_you_follow_serializer.rb deleted file mode 100644 index 7222480ac0f7..000000000000 --- a/back/app/serializers/web_api/v1/notifications/comment_on_initiative_you_follow_serializer.rb +++ /dev/null @@ -1,24 +0,0 @@ -# frozen_string_literal: true - -class WebApi::V1::Notifications::CommentOnInitiativeYouFollowSerializer < WebApi::V1::Notifications::NotificationSerializer - attribute :initiating_user_first_name do |object| - object.initiating_user&.first_name - end - - attribute :initiating_user_last_name do |object, params| - name_service = UserDisplayNameService.new(AppConfiguration.instance, current_user(params)) - name_service.last_name!(object.initiating_user) - end - - attribute :initiating_user_slug do |object| - object.initiating_user&.slug - end - - attribute :post_title_multiloc do |object| - object.post&.title_multiloc - end - - attribute :post_slug do |object| - object.post&.slug - end -end diff --git a/back/engines/free/email_campaigns/app/mailers/email_campaigns/comment_on_initiative_you_follow_mailer.rb b/back/engines/free/email_campaigns/app/mailers/email_campaigns/comment_on_initiative_you_follow_mailer.rb deleted file mode 100644 index 3888e0ef4fa4..000000000000 --- a/back/engines/free/email_campaigns/app/mailers/email_campaigns/comment_on_initiative_you_follow_mailer.rb +++ /dev/null @@ -1,30 +0,0 @@ -# frozen_string_literal: true - -module EmailCampaigns - class CommentOnInitiativeYouFollowMailer < ApplicationMailer - protected - - def subject - format_message('subject', values: { organizationName: organization_name, authorName: event.comment_author_name }) - end - - def header_title - format_message('main_header', values: { commentAuthor: event.comment_author_name }) - end - - def header_message - format_message( - 'event_description', - values: { - authorNameFull: event.comment_author_name, - authorName: event.initiating_user_first_name, - initiativeTitle: localize_for_recipient(event.post_title_multiloc) - } - ) - end - - def preheader - format_message('preheader', values: { organizationName: organization_name, authorName: event.comment_author_name }) - end - end -end diff --git a/back/engines/free/email_campaigns/app/models/email_campaigns/campaigns/comment_on_initiative_you_follow.rb b/back/engines/free/email_campaigns/app/models/email_campaigns/campaigns/comment_on_initiative_you_follow.rb deleted file mode 100644 index c10b7a3d45c3..000000000000 --- a/back/engines/free/email_campaigns/app/models/email_campaigns/campaigns/comment_on_initiative_you_follow.rb +++ /dev/null @@ -1,89 +0,0 @@ -# frozen_string_literal: true - -# == Schema Information -# -# Table name: email_campaigns_campaigns -# -# id :uuid not null, primary key -# type :string not null -# author_id :uuid -# enabled :boolean -# sender :string -# reply_to :string -# schedule :jsonb -# subject_multiloc :jsonb -# body_multiloc :jsonb -# created_at :datetime not null -# updated_at :datetime not null -# deliveries_count :integer default(0), not null -# context_id :uuid -# -# Indexes -# -# index_email_campaigns_campaigns_on_author_id (author_id) -# index_email_campaigns_campaigns_on_context_id (context_id) -# index_email_campaigns_campaigns_on_type (type) -# -# Foreign Keys -# -# fk_rails_... (author_id => users.id) -# -module EmailCampaigns - class Campaigns::CommentOnInitiativeYouFollow < Campaign - include Consentable - include Disableable - include ActivityTriggerable - include RecipientConfigurable - include LifecycleStageRestrictable - include Trackable - allow_lifecycle_stages only: %w[trial active] - - recipient_filter :filter_notification_recipient - - def mailer_class - CommentOnInitiativeYouFollowMailer - end - - def activity_triggers - { 'Notifications::CommentOnInitiativeYouFollow' => { 'created' => true } } - end - - def filter_notification_recipient(users_scope, activity:, time: nil) - users_scope.where(id: activity.item.recipient.id) - end - - def self.recipient_role_multiloc_key - 'email_campaigns.admin_labels.recipient_role.registered_users' - end - - def self.recipient_segment_multiloc_key - 'email_campaigns.admin_labels.recipient_segment.users_who_follow_the_proposal' - end - - def self.content_type_multiloc_key - 'email_campaigns.admin_labels.content_type.proposals' - end - - def self.trigger_multiloc_key - 'email_campaigns.admin_labels.trigger.user_comments_on_proposal' - end - - def generate_commands(recipient:, activity:, time: nil) - notification = activity.item - name_service = UserDisplayNameService.new(AppConfiguration.instance, recipient) - [{ - event_payload: { - initiating_user_first_name: notification.initiating_user&.first_name, - initiating_user_last_name: name_service.last_name!(notification.initiating_user), - comment_author_name: name_service.display_name!(notification.comment.author), - comment_body_multiloc: notification.comment.body_multiloc, - comment_url: Frontend::UrlService.new.model_to_url(notification.comment, locale: Locale.new(recipient.locale)), - post_published_at: notification.post.published_at.iso8601, - post_title_multiloc: notification.post.title_multiloc, - post_author_name: name_service.display_name!(notification.post.author), - unfollow_url: Frontend::UrlService.new.unfollow_url(Follower.new(followable: notification.post, user: recipient)) - } - }] - end - end -end diff --git a/back/engines/free/email_campaigns/app/views/email_campaigns/comment_on_initiative_you_follow_mailer/campaign_mail.mjml b/back/engines/free/email_campaigns/app/views/email_campaigns/comment_on_initiative_you_follow_mailer/campaign_mail.mjml deleted file mode 100644 index ee1b0af013e5..000000000000 --- a/back/engines/free/email_campaigns/app/views/email_campaigns/comment_on_initiative_you_follow_mailer/campaign_mail.mjml +++ /dev/null @@ -1,23 +0,0 @@ - - - - <%= format_message('author_wrote', component: 'general', values: { - authorName: " - #{event.initiating_user_first_name&.upcase} - " - }).html_safe %> -

- <%= localize_for_recipient_and_truncate(event.comment_body_multiloc, 140) %> -

-
-
-
- - -<%= render partial: 'application/cta_button', locals: { href: event.comment_url, message: format_message('cta_reply_to', values: {commentAuthor: event.initiating_user_first_name&.capitalize}) } %> - -<% if AppConfiguration.instance.feature_activated?('follow') %> - <%= render 'email_campaigns/follow/card' %> -<% end %> diff --git a/back/engines/free/email_campaigns/spec/mailers/comment_on_initiative_you_follow_mailer_spec.rb b/back/engines/free/email_campaigns/spec/mailers/comment_on_initiative_you_follow_mailer_spec.rb deleted file mode 100644 index 2042e403a333..000000000000 --- a/back/engines/free/email_campaigns/spec/mailers/comment_on_initiative_you_follow_mailer_spec.rb +++ /dev/null @@ -1,53 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe EmailCampaigns::CommentOnInitiativeYouFollowMailer do - describe 'CommentOnInitiativeYouFollow' do - let_it_be(:recipient) { create(:user, locale: 'en') } - let_it_be(:campaign) { EmailCampaigns::Campaigns::CommentOnInitiativeYouFollow.create! } - let_it_be(:initiative) { create(:initiative) } - let_it_be(:initiator) { create(:user, first_name: 'Dries') } - let_it_be(:comment) { create(:comment, post: initiative, body_multiloc: { 'en' => 'I agree' }, author: initiator) } - let_it_be(:notification) { create(:comment_on_initiative_you_follow, recipient: recipient, post: initiative, comment: comment) } - let_it_be(:command) do - activity = create(:activity, item: notification, action: 'created') - create(:comment_on_initiative_you_follow_campaign).generate_commands( - activity: activity, - recipient: recipient - ).first.merge({ recipient: recipient }) - end - - let_it_be(:mail) { described_class.with(command: command, campaign: campaign).campaign_mail.deliver_now } - - before { EmailCampaigns::UnsubscriptionToken.create!(user_id: recipient.id) } - - it 'renders the subject' do - expect(mail.subject).to be_present - end - - it 'renders the receiver email' do - expect(mail.to).to eq([recipient.email]) - end - - it 'renders the sender email' do - expect(mail.from).to all(end_with('@citizenlab.co')) - end - - it 'assigns organisation name' do - expect(mail.body.encoded).to match(AppConfiguration.instance.settings('core', 'organization_name')['en']) - end - - it 'includes the comment author name' do - expect(mail.body.encoded).to include('Dries') - end - - it 'includes the comment body' do - expect(mail.body.encoded).to include('I agree') - end - - it 'includes the unfollow url' do - expect(mail.body.encoded).to match(Frontend::UrlService.new.unfollow_url(Follower.new(followable: initiative, user: recipient))) - end - end -end diff --git a/back/engines/free/email_campaigns/spec/mailers/previews/email_campaigns/comment_on_initiative_you_follow_mailer_preview.rb b/back/engines/free/email_campaigns/spec/mailers/previews/email_campaigns/comment_on_initiative_you_follow_mailer_preview.rb deleted file mode 100644 index 96cc5395ce50..000000000000 --- a/back/engines/free/email_campaigns/spec/mailers/previews/email_campaigns/comment_on_initiative_you_follow_mailer_preview.rb +++ /dev/null @@ -1,25 +0,0 @@ -# frozen_string_literal: true - -module EmailCampaigns - class CommentOnInitiativeYouFollowMailerPreview < ActionMailer::Preview - include EmailCampaigns::MailerPreviewRecipient - - def campaign_mail - comment = Comment.where(post_type: 'Initiative').first || Comment.create(post: Initiative.first, author: User.first, body_multiloc: { 'en' => 'I agree' }) - notification = Notifications::CommentOnInitiativeYouFollow.create!( - recipient_id: recipient_user.id, - initiating_user: comment.author, - post: comment.post, - comment: comment - ) - activity = Activity.new(item: notification, action: 'created') - - campaign = EmailCampaigns::Campaigns::CommentOnInitiativeYouFollow.first - command = campaign.generate_commands( - activity: activity, - recipient: recipient_user - ).first.merge({ recipient: recipient_user }) - campaign.mailer_class.with(campaign: campaign, command: command).campaign_mail - end - end -end diff --git a/back/engines/free/email_campaigns/spec/models/email_campaigns/campaigns/comment_on_initiative_you_follow_spec.rb b/back/engines/free/email_campaigns/spec/models/email_campaigns/campaigns/comment_on_initiative_you_follow_spec.rb deleted file mode 100644 index 0965d35bcbc8..000000000000 --- a/back/engines/free/email_campaigns/spec/models/email_campaigns/campaigns/comment_on_initiative_you_follow_spec.rb +++ /dev/null @@ -1,53 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe EmailCampaigns::Campaigns::CommentOnInitiativeYouFollow do - describe 'CommentOnYourInitiative Campaign default factory' do - it 'is valid' do - expect(build(:comment_on_initiative_you_follow_campaign)).to be_valid - end - end - - describe '#generate_commands' do - let(:campaign) { create(:comment_on_initiative_you_follow_campaign) } - let(:notification) { create(:comment_on_initiative_you_follow) } - let(:notification_activity) { create(:activity, item: notification, action: 'created') } - let(:command) do - campaign.generate_commands(recipient: notification_activity.item.recipient, activity: notification_activity).first - end - - it 'generates a command with the desired payload and tracked content' do - expect(command).to match({ - event_payload: hash_including( - initiating_user_first_name: notification.initiating_user.first_name, - initiating_user_last_name: notification.initiating_user.last_name, - comment_author_name: notification.comment.author.full_name, - comment_body_multiloc: notification.comment.body_multiloc, - comment_url: an_instance_of(String), - post_published_at: an_instance_of(String), - post_title_multiloc: notification.post.title_multiloc, - post_author_name: notification.post.author.full_name, - unfollow_url: an_instance_of(String) - ) - }) - - expect( - command.dig(:event_payload, :initiating_user_first_name) - ).to eq(notification.initiating_user.first_name) - expect( - command.dig(:event_payload, :comment_body_multiloc) - ).to eq(notification.comment.body_multiloc) - end - - it 'generates a command with an abbreviated name' do - SettingsService.new.activate_feature! 'abbreviated_user_names' - - expect(notification.recipient.admin?).to be false - expect(notification.initiating_user.admin?).to be false - - initial = "#{notification.initiating_user.last_name[0]}." - expect(command.dig(:event_payload, :initiating_user_last_name)).to eq(initial) - end - end -end From 647aee6cf6ca8f6ede87a8b4879fa5312543df04 Mon Sep 17 00:00:00 2001 From: Simon Tharby Date: Wed, 18 Dec 2024 11:01:28 +0000 Subject: [PATCH 33/63] [TAN-2538] Remove constant value & factory of removed campaign --- .../app/services/email_campaigns/delivery_service.rb | 1 - back/engines/free/email_campaigns/spec/factories/campaigns.rb | 4 ---- 2 files changed, 5 deletions(-) diff --git a/back/engines/free/email_campaigns/app/services/email_campaigns/delivery_service.rb b/back/engines/free/email_campaigns/app/services/email_campaigns/delivery_service.rb index f1d727ca38e3..fe1909a6c761 100644 --- a/back/engines/free/email_campaigns/app/services/email_campaigns/delivery_service.rb +++ b/back/engines/free/email_campaigns/app/services/email_campaigns/delivery_service.rb @@ -9,7 +9,6 @@ class DeliveryService Campaigns::CommentDeletedByAdmin, Campaigns::CommentMarkedAsSpam, Campaigns::CommentOnIdeaYouFollow, - Campaigns::CommentOnInitiativeYouFollow, Campaigns::CommentOnYourComment, Campaigns::CosponsorOfYourInitiative, Campaigns::CosponsorOfYourIdea, diff --git a/back/engines/free/email_campaigns/spec/factories/campaigns.rb b/back/engines/free/email_campaigns/spec/factories/campaigns.rb index 83d15041ddb9..264c1e7e72f0 100644 --- a/back/engines/free/email_campaigns/spec/factories/campaigns.rb +++ b/back/engines/free/email_campaigns/spec/factories/campaigns.rb @@ -42,10 +42,6 @@ enabled { true } end - factory :comment_on_initiative_you_follow_campaign, class: EmailCampaigns::Campaigns::CommentOnInitiativeYouFollow do - enabled { true } - end - factory :cosponsor_of_your_initiative_campaign, class: EmailCampaigns::Campaigns::CosponsorOfYourInitiative do enabled { true } end From f16dbacfab773f90f91bfe67dfe32887d5ae87b3 Mon Sep 17 00:00:00 2001 From: Simon Tharby Date: Wed, 18 Dec 2024 11:17:16 +0000 Subject: [PATCH 34/63] [TAN-2538] Remove OfficialFeedbackOnInitiativeYouFollow campaign files & spec --- ...ack_on_initiative_you_follow_serializer.rb | 15 ---- ...icial_feedback_on_initiative_you_follow.rb | 87 ------------------- ..._feedback_on_initiative_you_follow_spec.rb | 35 -------- 3 files changed, 137 deletions(-) delete mode 100644 back/app/serializers/web_api/v1/notifications/official_feedback_on_initiative_you_follow_serializer.rb delete mode 100644 back/engines/free/email_campaigns/app/models/email_campaigns/campaigns/official_feedback_on_initiative_you_follow.rb delete mode 100644 back/engines/free/email_campaigns/spec/models/email_campaigns/campaigns/official_feedback_on_initiative_you_follow_spec.rb diff --git a/back/app/serializers/web_api/v1/notifications/official_feedback_on_initiative_you_follow_serializer.rb b/back/app/serializers/web_api/v1/notifications/official_feedback_on_initiative_you_follow_serializer.rb deleted file mode 100644 index 869016f45276..000000000000 --- a/back/app/serializers/web_api/v1/notifications/official_feedback_on_initiative_you_follow_serializer.rb +++ /dev/null @@ -1,15 +0,0 @@ -# frozen_string_literal: true - -class WebApi::V1::Notifications::OfficialFeedbackOnInitiativeYouFollowSerializer < WebApi::V1::Notifications::NotificationSerializer - attribute :official_feedback_author do |object| - object.official_feedback&.author_multiloc - end - - attribute :post_title_multiloc do |object| - object.post&.title_multiloc - end - - attribute :post_slug do |object| - object.post&.slug - end -end diff --git a/back/engines/free/email_campaigns/app/models/email_campaigns/campaigns/official_feedback_on_initiative_you_follow.rb b/back/engines/free/email_campaigns/app/models/email_campaigns/campaigns/official_feedback_on_initiative_you_follow.rb deleted file mode 100644 index eeb2878f80ea..000000000000 --- a/back/engines/free/email_campaigns/app/models/email_campaigns/campaigns/official_feedback_on_initiative_you_follow.rb +++ /dev/null @@ -1,87 +0,0 @@ -# frozen_string_literal: true - -# == Schema Information -# -# Table name: email_campaigns_campaigns -# -# id :uuid not null, primary key -# type :string not null -# author_id :uuid -# enabled :boolean -# sender :string -# reply_to :string -# schedule :jsonb -# subject_multiloc :jsonb -# body_multiloc :jsonb -# created_at :datetime not null -# updated_at :datetime not null -# deliveries_count :integer default(0), not null -# context_id :uuid -# -# Indexes -# -# index_email_campaigns_campaigns_on_author_id (author_id) -# index_email_campaigns_campaigns_on_context_id (context_id) -# index_email_campaigns_campaigns_on_type (type) -# -# Foreign Keys -# -# fk_rails_... (author_id => users.id) -# -module EmailCampaigns - class Campaigns::OfficialFeedbackOnInitiativeYouFollow < Campaign - include Consentable - include Disableable - include ActivityTriggerable - include RecipientConfigurable - include Trackable - include LifecycleStageRestrictable - allow_lifecycle_stages only: %w[trial active] - - recipient_filter :filter_notification_recipient - - def mailer_class - OfficialFeedbackOnInitiativeYouFollowMailer - end - - def activity_triggers - { 'Notifications::OfficialFeedbackOnInitiativeYouFollow' => { 'created' => true } } - end - - def filter_notification_recipient(users_scope, activity:, time: nil) - users_scope.where(id: activity.item.recipient.id) - end - - def self.recipient_role_multiloc_key - 'email_campaigns.admin_labels.recipient_role.registered_users' - end - - def self.recipient_segment_multiloc_key - 'email_campaigns.admin_labels.recipient_segment.users_who_follow_the_proposal' - end - - def self.content_type_multiloc_key - 'email_campaigns.admin_labels.content_type.proposals' - end - - def self.trigger_multiloc_key - 'email_campaigns.admin_labels.trigger.proposal_is_updated' - end - - def generate_commands(recipient:, activity:, time: nil) - notification = activity.item - name_service = UserDisplayNameService.new(AppConfiguration.instance, recipient) - [{ - event_payload: { - official_feedback_author_multiloc: notification.official_feedback.author_multiloc, - official_feedback_body_multiloc: notification.official_feedback.body_multiloc, - official_feedback_url: Frontend::UrlService.new.model_to_url(notification.official_feedback, locale: Locale.new(recipient.locale)), - post_published_at: notification.post.published_at.iso8601, - post_title_multiloc: notification.post.title_multiloc, - post_author_name: name_service.display_name!(notification.post.author), - unfollow_url: Frontend::UrlService.new.unfollow_url(Follower.new(followable: notification.post, user: recipient)) - } - }] - end - end -end diff --git a/back/engines/free/email_campaigns/spec/models/email_campaigns/campaigns/official_feedback_on_initiative_you_follow_spec.rb b/back/engines/free/email_campaigns/spec/models/email_campaigns/campaigns/official_feedback_on_initiative_you_follow_spec.rb deleted file mode 100644 index 2520317e1a6e..000000000000 --- a/back/engines/free/email_campaigns/spec/models/email_campaigns/campaigns/official_feedback_on_initiative_you_follow_spec.rb +++ /dev/null @@ -1,35 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe EmailCampaigns::Campaigns::OfficialFeedbackOnInitiativeYouFollow do - describe 'OfficialFeedbackOnInitiativeYouFollow campaign default factory' do - it 'is valid' do - expect(build(:official_feedback_on_initiative_you_follow_campaign)).to be_valid - end - end - - describe '#generate_commands' do - let(:campaign) { create(:official_feedback_on_initiative_you_follow_campaign) } - let(:notification) { create(:official_feedback_on_initiative_you_follow) } - let(:notification_activity) { create(:activity, item: notification, action: 'created') } - - it 'generates a command with the desired payload and tracked content' do - command = campaign.generate_commands( - recipient: notification_activity.item.recipient, - activity: notification_activity - ).first - - expect(command).to match({ - event_payload: hash_including( - official_feedback_author_multiloc: notification.official_feedback.author_multiloc, - official_feedback_body_multiloc: notification.official_feedback.body_multiloc, - official_feedback_url: an_instance_of(String), - post_published_at: an_instance_of(String), - post_title_multiloc: notification.post.title_multiloc, - post_author_name: notification.post.author_name - ) - }) - end - end -end From 4bd4c164c3d1460773492683c1438ac85ff7c9b6 Mon Sep 17 00:00:00 2001 From: Simon Tharby Date: Wed, 18 Dec 2024 11:38:05 +0000 Subject: [PATCH 35/63] [TAN-2538] Remove OfficialFeedbackOnInitiativeYouFollow factory --- back/engines/free/email_campaigns/spec/factories/campaigns.rb | 4 ---- 1 file changed, 4 deletions(-) diff --git a/back/engines/free/email_campaigns/spec/factories/campaigns.rb b/back/engines/free/email_campaigns/spec/factories/campaigns.rb index 264c1e7e72f0..0cce94c7671d 100644 --- a/back/engines/free/email_campaigns/spec/factories/campaigns.rb +++ b/back/engines/free/email_campaigns/spec/factories/campaigns.rb @@ -150,10 +150,6 @@ enabled { true } end - factory :official_feedback_on_initiative_you_follow_campaign, class: EmailCampaigns::Campaigns::OfficialFeedbackOnInitiativeYouFollow do - enabled { true } - end - factory :project_folder_moderation_rights_received_campaign, class: EmailCampaigns::Campaigns::ProjectFolderModerationRightsReceived do enabled { true } end From 5b85c7820d3aea106c7d5eeeb2db889d622c48c0 Mon Sep 17 00:00:00 2001 From: Simon Tharby Date: Wed, 18 Dec 2024 11:40:26 +0000 Subject: [PATCH 36/63] [TAN-2538] Remove OfficialFeedbackOnInitiativeYouFollow notification factory --- .../app/services/email_campaigns/delivery_service.rb | 1 - back/spec/factories/notifications.rb | 6 ------ 2 files changed, 7 deletions(-) diff --git a/back/engines/free/email_campaigns/app/services/email_campaigns/delivery_service.rb b/back/engines/free/email_campaigns/app/services/email_campaigns/delivery_service.rb index fe1909a6c761..00371aca317c 100644 --- a/back/engines/free/email_campaigns/app/services/email_campaigns/delivery_service.rb +++ b/back/engines/free/email_campaigns/app/services/email_campaigns/delivery_service.rb @@ -41,7 +41,6 @@ class DeliveryService Campaigns::NewIdeaForAdmin, Campaigns::NewInitiativeForAdmin, Campaigns::OfficialFeedbackOnIdeaYouFollow, - Campaigns::OfficialFeedbackOnInitiativeYouFollow, Campaigns::ProjectFolderModerationRightsReceived, Campaigns::ProjectModerationRightsReceived, Campaigns::ProjectPhaseStarted, diff --git a/back/spec/factories/notifications.rb b/back/spec/factories/notifications.rb index 56c2bad6d803..cdda0e6bb41e 100644 --- a/back/spec/factories/notifications.rb +++ b/back/spec/factories/notifications.rb @@ -185,12 +185,6 @@ project end - factory :official_feedback_on_initiative_you_follow, parent: :notification, class: 'Notifications::OfficialFeedbackOnInitiativeYouFollow' do - initiating_user - official_feedback - association :post, factory: :initiative - end - factory :project_folder_moderation_rights_received, parent: :notification, class: 'Notifications::ProjectFolderModerationRightsReceived' do initiating_user project_folder From f2f7f65747f5a0eeb240b94c1a77583a8617a06f Mon Sep 17 00:00:00 2001 From: Simon Tharby Date: Wed, 18 Dec 2024 11:57:37 +0000 Subject: [PATCH 37/63] [TAN-2538] Fix campaigns_spec --- .../free/email_campaigns/spec/acceptance/campaigns_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/back/engines/free/email_campaigns/spec/acceptance/campaigns_spec.rb b/back/engines/free/email_campaigns/spec/acceptance/campaigns_spec.rb index 47de9df0ca43..77b3fd4f90e8 100644 --- a/back/engines/free/email_campaigns/spec/acceptance/campaigns_spec.rb +++ b/back/engines/free/email_campaigns/spec/acceptance/campaigns_spec.rb @@ -9,7 +9,7 @@ before do @manual_campaigns = create_list(:manual_campaign, 4) @manual_project_participants_campaign = create(:manual_project_participants_campaign) - @automated_campaigns = create_list(:official_feedback_on_initiative_you_follow_campaign, 2) + @automated_campaigns = create_list(:official_feedback_on_idea_you_follow_campaign, 2) end context 'as an admin' do From bfd31f31a9bc056086b7809c58a7585854a6347b Mon Sep 17 00:00:00 2001 From: Simon Tharby Date: Wed, 18 Dec 2024 12:11:29 +0000 Subject: [PATCH 38/63] [TAN-2538] Fix notifications_spec --- back/spec/serializers/web_api/v1/notifications_spec.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/back/spec/serializers/web_api/v1/notifications_spec.rb b/back/spec/serializers/web_api/v1/notifications_spec.rb index 4ccf9d0f2e4e..7e889d37d62d 100644 --- a/back/spec/serializers/web_api/v1/notifications_spec.rb +++ b/back/spec/serializers/web_api/v1/notifications_spec.rb @@ -50,10 +50,10 @@ def expect_serializer_to_hide_name(user1, user2, admin, notification_factory_nam ) end - it 'serializes InitiativeAssignedToYouSerializer correctly' do + it 'serializes InternalCommentOnIdeaAssignedToYouSerializer correctly' do expect_serializer_to_hide_name( - john, jane, admin, :initiative_assigned_to_you, - WebApi::V1::Notifications::InitiativeAssignedToYouSerializer + john, jane, admin, :internal_comment_on_idea_assigned_to_you, + WebApi::V1::Notifications::InternalCommentOnIdeaAssignedToYouSerializer ) end From 2514774d6a08d6e53b21f4ae1d7e629015938b07 Mon Sep 17 00:00:00 2001 From: Simon Tharby Date: Wed, 18 Dec 2024 12:27:04 +0000 Subject: [PATCH 39/63] [TAN-2538] Improve tests in side_fx-comment_service_spec --- .../services/side_fx_comment_service_spec.rb | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/back/spec/services/side_fx_comment_service_spec.rb b/back/spec/services/side_fx_comment_service_spec.rb index 0a789612c208..6f6a50d83d0b 100644 --- a/back/spec/services/side_fx_comment_service_spec.rb +++ b/back/spec/services/side_fx_comment_service_spec.rb @@ -34,7 +34,7 @@ expectation.to enqueue_job(LogActivityJob).with(comment, 'mentioned', user, created_at, payload: { mentioned_user: u2.id }, project_id: project_id) end - it 'creates a follower' do + it 'creates the expected follower records' do project = create(:project) folder = create(:project_folder, projects: [project]) idea = create(:idea, project: project) @@ -47,24 +47,24 @@ expect(user.follows.pluck(:followable_id)).to contain_exactly idea.id, project.id, folder.id end - # TODO: Part of initiatives cleanup - # I have commented this out, for now, as it fails if I change initiative to idea - # This seems to be because the related `after_create` method in fact creates a folllower for the related project, - # and intitiatives never had a related project. - # Furthermore, it appears that the `after_create` method will go on to also create another follower for the - # project's folder, it there is one. - # Thus, this test was not as general (to Post) as it might have seemed + I think we need to check that the cascading - # follower creations (idea -> project -> folder as followable) is intended & necessary. - # - # it 'does not create a follower if the user already follows the post' do - # initiative = create(:initiative) - # comment = create(:comment, post: initiative) - # create(:follower, followable: initiative, user: user) - - # expect do - # service.after_create comment, user - # end.not_to change(Follower, :count) - # end + it 'does not create new follower records for followable items user already follows' do + project = create(:project) + folder = create(:project_folder, projects: [project]) + idea = create(:idea, project: project) + comment = create(:comment, post: idea) + + create(:follower, followable: idea, user: user) + create(:follower, followable: project, user: user) + create(:follower, followable: folder, user: user) + n_idea_followers = idea.followers.count + n_project_followers = project.followers.count + n_folder_followers = folder.followers.count + + service.after_create comment, user + expect(idea.reload.followers.count).to eq n_idea_followers + expect(project.reload.followers.count).to eq n_project_followers + expect(folder.reload.followers.count).to eq n_folder_followers + end end describe 'after_update' do From 0eaad86af08e00d8dc9f0ed6b5f9d34b89d0b747 Mon Sep 17 00:00:00 2001 From: Simon Tharby Date: Wed, 18 Dec 2024 12:38:14 +0000 Subject: [PATCH 40/63] [TAN-2538] Remove InitiativeAssignedToYou notification & campaign files & specs --- .../initiative_assigned_to_you.rb | 99 ------------------- .../initiative_assigned_to_you_serializer.rb | 24 ----- back/app/services/notification_service.rb | 1 - .../initiative_assigned_to_you_mailer.rb | 25 ----- .../campaigns/initiative_assigned_to_you.rb | 91 ----------------- .../email_campaigns/delivery_service.rb | 1 - .../campaign_mail.mjml | 20 ---- .../spec/factories/campaigns.rb | 4 - .../initiative_assigned_to_you_mailer_spec.rb | 53 ---------- ...itiative_assigned_to_you_mailer_preview.rb | 28 ------ back/spec/factories/notifications.rb | 5 - back/spec/jobs/log_activity_job_spec.rb | 1 - 12 files changed, 352 deletions(-) delete mode 100644 back/app/models/notifications/initiative_assigned_to_you.rb delete mode 100644 back/app/serializers/web_api/v1/notifications/initiative_assigned_to_you_serializer.rb delete mode 100644 back/engines/free/email_campaigns/app/mailers/email_campaigns/initiative_assigned_to_you_mailer.rb delete mode 100644 back/engines/free/email_campaigns/app/models/email_campaigns/campaigns/initiative_assigned_to_you.rb delete mode 100644 back/engines/free/email_campaigns/app/views/email_campaigns/initiative_assigned_to_you_mailer/campaign_mail.mjml delete mode 100644 back/engines/free/email_campaigns/spec/mailers/initiative_assigned_to_you_mailer_spec.rb delete mode 100644 back/engines/free/email_campaigns/spec/mailers/previews/email_campaigns/initiative_assigned_to_you_mailer_preview.rb diff --git a/back/app/models/notifications/initiative_assigned_to_you.rb b/back/app/models/notifications/initiative_assigned_to_you.rb deleted file mode 100644 index 23c2303b4bd1..000000000000 --- a/back/app/models/notifications/initiative_assigned_to_you.rb +++ /dev/null @@ -1,99 +0,0 @@ -# frozen_string_literal: true - -# == Schema Information -# -# Table name: notifications -# -# id :uuid not null, primary key -# type :string -# read_at :datetime -# recipient_id :uuid -# post_id :uuid -# comment_id :uuid -# project_id :uuid -# created_at :datetime not null -# updated_at :datetime not null -# initiating_user_id :uuid -# spam_report_id :uuid -# invite_id :uuid -# reason_code :string -# other_reason :string -# post_status_id :uuid -# official_feedback_id :uuid -# phase_id :uuid -# post_type :string -# post_status_type :string -# project_folder_id :uuid -# inappropriate_content_flag_id :uuid -# internal_comment_id :uuid -# basket_id :uuid -# cosponsors_initiative_id :uuid -# cosponsorship_id :uuid -# project_review_id :uuid -# -# Indexes -# -# index_notifications_on_basket_id (basket_id) -# index_notifications_on_cosponsors_initiative_id (cosponsors_initiative_id) -# index_notifications_on_cosponsorship_id (cosponsorship_id) -# index_notifications_on_created_at (created_at) -# index_notifications_on_inappropriate_content_flag_id (inappropriate_content_flag_id) -# index_notifications_on_initiating_user_id (initiating_user_id) -# index_notifications_on_internal_comment_id (internal_comment_id) -# index_notifications_on_invite_id (invite_id) -# index_notifications_on_official_feedback_id (official_feedback_id) -# index_notifications_on_phase_id (phase_id) -# index_notifications_on_post_id_and_post_type (post_id,post_type) -# index_notifications_on_post_status_id (post_status_id) -# index_notifications_on_post_status_id_and_post_status_type (post_status_id,post_status_type) -# index_notifications_on_project_review_id (project_review_id) -# index_notifications_on_recipient_id (recipient_id) -# index_notifications_on_recipient_id_and_read_at (recipient_id,read_at) -# index_notifications_on_spam_report_id (spam_report_id) -# -# Foreign Keys -# -# fk_rails_... (basket_id => baskets.id) -# fk_rails_... (comment_id => comments.id) -# fk_rails_... (cosponsors_initiative_id => cosponsors_initiatives.id) -# fk_rails_... (cosponsorship_id => cosponsorships.id) -# fk_rails_... (inappropriate_content_flag_id => flag_inappropriate_content_inappropriate_content_flags.id) -# fk_rails_... (initiating_user_id => users.id) -# fk_rails_... (internal_comment_id => internal_comments.id) -# fk_rails_... (invite_id => invites.id) -# fk_rails_... (official_feedback_id => official_feedbacks.id) -# fk_rails_... (phase_id => phases.id) -# fk_rails_... (project_id => projects.id) -# fk_rails_... (project_review_id => project_reviews.id) -# fk_rails_... (recipient_id => users.id) -# fk_rails_... (spam_report_id => spam_reports.id) -# -module Notifications - class InitiativeAssignedToYou < Notification - validates :initiating_user, :post, presence: true - validates :post_type, inclusion: { in: ['Initiative'] } - - ACTIVITY_TRIGGERS = { 'Initiative' => { 'changed_assignee' => true } } - EVENT_NAME = 'Initiative assigned to you' - - def self.make_notifications_on(activity) - initiative = activity.item - recipient_id = initiative.assignee_id - initiator_id = activity.user_id - - # We only notify manual assignments, meaning there needs to be an - # initiator - if recipient_id && initiator_id && recipient_id != initiator_id - [ - new( - recipient_id: recipient_id, - post: initiative, - initiating_user_id: initiator_id - ) - ] - else - [] - end - end - end -end diff --git a/back/app/serializers/web_api/v1/notifications/initiative_assigned_to_you_serializer.rb b/back/app/serializers/web_api/v1/notifications/initiative_assigned_to_you_serializer.rb deleted file mode 100644 index f1e9b5b509d6..000000000000 --- a/back/app/serializers/web_api/v1/notifications/initiative_assigned_to_you_serializer.rb +++ /dev/null @@ -1,24 +0,0 @@ -# frozen_string_literal: true - -class WebApi::V1::Notifications::InitiativeAssignedToYouSerializer < WebApi::V1::Notifications::NotificationSerializer - attribute :initiating_user_first_name do |object| - object.initiating_user&.first_name - end - - attribute :initiating_user_last_name do |object, params| - name_service = UserDisplayNameService.new(AppConfiguration.instance, current_user(params)) - name_service.last_name!(object.initiating_user) - end - - attribute :initiating_user_slug do |object| - object.initiating_user&.slug - end - - attribute :post_title_multiloc do |object| - object.post&.title_multiloc - end - - attribute :post_slug do |object| - object.post&.slug - end -end diff --git a/back/app/services/notification_service.rb b/back/app/services/notification_service.rb index 1e72f4b1b590..4355e829dd11 100644 --- a/back/app/services/notification_service.rb +++ b/back/app/services/notification_service.rb @@ -10,7 +10,6 @@ class NotificationService Notifications::CosponsorOfYourIdea, Notifications::CosponsorOfYourInitiative, Notifications::IdeaMarkedAsSpam, - Notifications::InitiativeAssignedToYou, Notifications::InitiativeMarkedAsSpam, Notifications::InitiativeResubmittedForReview, Notifications::InternalComments::InternalCommentOnIdeaAssignedToYou, diff --git a/back/engines/free/email_campaigns/app/mailers/email_campaigns/initiative_assigned_to_you_mailer.rb b/back/engines/free/email_campaigns/app/mailers/email_campaigns/initiative_assigned_to_you_mailer.rb deleted file mode 100644 index 85b45b1d7e1f..000000000000 --- a/back/engines/free/email_campaigns/app/mailers/email_campaigns/initiative_assigned_to_you_mailer.rb +++ /dev/null @@ -1,25 +0,0 @@ -# frozen_string_literal: true - -module EmailCampaigns - class InitiativeAssignedToYouMailer < ApplicationMailer - protected - - def subject - format_message('subject', values: { organizationName: organization_name }) - end - - private - - def header_title - format_message('main_header', values: { firstName: recipient_first_name }) - end - - def header_message - format_message('event_description_initiative') - end - - def preheader - format_message('preheader_initiative', values: { organizationName: organization_name, authorName: event.post_author_name }) - end - end -end diff --git a/back/engines/free/email_campaigns/app/models/email_campaigns/campaigns/initiative_assigned_to_you.rb b/back/engines/free/email_campaigns/app/models/email_campaigns/campaigns/initiative_assigned_to_you.rb deleted file mode 100644 index 507bb010407e..000000000000 --- a/back/engines/free/email_campaigns/app/models/email_campaigns/campaigns/initiative_assigned_to_you.rb +++ /dev/null @@ -1,91 +0,0 @@ -# frozen_string_literal: true - -# == Schema Information -# -# Table name: email_campaigns_campaigns -# -# id :uuid not null, primary key -# type :string not null -# author_id :uuid -# enabled :boolean -# sender :string -# reply_to :string -# schedule :jsonb -# subject_multiloc :jsonb -# body_multiloc :jsonb -# created_at :datetime not null -# updated_at :datetime not null -# deliveries_count :integer default(0), not null -# context_id :uuid -# -# Indexes -# -# index_email_campaigns_campaigns_on_author_id (author_id) -# index_email_campaigns_campaigns_on_context_id (context_id) -# index_email_campaigns_campaigns_on_type (type) -# -# Foreign Keys -# -# fk_rails_... (author_id => users.id) -# -module EmailCampaigns - class Campaigns::InitiativeAssignedToYou < Campaign - include Consentable - include ActivityTriggerable - include Disableable - include Trackable - include LifecycleStageRestrictable - allow_lifecycle_stages only: %w[trial active] - - recipient_filter :filter_notification_recipient - - def self.consentable_roles - ['admin'] - end - - def mailer_class - InitiativeAssignedToYouMailer - end - - def activity_triggers - { 'Notifications::InitiativeAssignedToYou' => { 'created' => true } } - end - - def filter_notification_recipient(users_scope, activity:, time: nil) - users_scope.where(id: activity.item.recipient.id) - end - - def self.recipient_role_multiloc_key - 'email_campaigns.admin_labels.recipient_role.admins_and_managers' - end - - def self.recipient_segment_multiloc_key - 'email_campaigns.admin_labels.recipient_segment.admins_assigned_to_a_proposal' - end - - def self.content_type_multiloc_key - 'email_campaigns.admin_labels.content_type.proposals' - end - - def self.trigger_multiloc_key - 'email_campaigns.admin_labels.trigger.proposal_is_assigned_to_admin' - end - - def generate_commands(recipient:, activity:, time: nil) - notification = activity.item - name_service = UserDisplayNameService.new(AppConfiguration.instance, recipient) - [{ - event_payload: { - post_title_multiloc: notification.post.title_multiloc, - post_body_multiloc: notification.post.body_multiloc, - post_author_name: name_service.display_name!(notification.post.author), - post_published_at: notification.post.published_at.iso8601, - post_url: Frontend::UrlService.new.model_to_url(notification.post, locale: Locale.new(recipient.locale)), - post_assigned_at: notification.post.assigned_at&.iso8601, - initiative_reactions_needed: notification.post.reactions_needed, - initiative_expires_at: notification.post.expires_at.iso8601 - } - }] - end - end -end diff --git a/back/engines/free/email_campaigns/app/services/email_campaigns/delivery_service.rb b/back/engines/free/email_campaigns/app/services/email_campaigns/delivery_service.rb index 00371aca317c..a8cdf04d2f9d 100644 --- a/back/engines/free/email_campaigns/app/services/email_campaigns/delivery_service.rb +++ b/back/engines/free/email_campaigns/app/services/email_campaigns/delivery_service.rb @@ -15,7 +15,6 @@ class DeliveryService Campaigns::EventRegistrationConfirmation, Campaigns::IdeaMarkedAsSpam, Campaigns::IdeaPublished, - Campaigns::InitiativeAssignedToYou, Campaigns::InitiativeMarkedAsSpam, Campaigns::InitiativePublished, Campaigns::InitiativeResubmittedForReview, diff --git a/back/engines/free/email_campaigns/app/views/email_campaigns/initiative_assigned_to_you_mailer/campaign_mail.mjml b/back/engines/free/email_campaigns/app/views/email_campaigns/initiative_assigned_to_you_mailer/campaign_mail.mjml deleted file mode 100644 index b7eadf7f4740..000000000000 --- a/back/engines/free/email_campaigns/app/views/email_campaigns/initiative_assigned_to_you_mailer/campaign_mail.mjml +++ /dev/null @@ -1,20 +0,0 @@ - - - - -

- <%= localize_for_recipient(event.post_title_multiloc) %> -

- <%= format_message('by_author', component: 'general', values: { authorName: event.post_author_name }) %> -

-

- <%= localize_for_recipient(event.post_body_multiloc).html_safe %> -

-
-
-
- -<%= render partial: 'application/cta_button', locals: { href: url_service.admin_initiatives_url, message: format_message('cta_reply_to', values: { authorName: event.post_author_name }) } %> diff --git a/back/engines/free/email_campaigns/spec/factories/campaigns.rb b/back/engines/free/email_campaigns/spec/factories/campaigns.rb index 0cce94c7671d..4467107b1d47 100644 --- a/back/engines/free/email_campaigns/spec/factories/campaigns.rb +++ b/back/engines/free/email_campaigns/spec/factories/campaigns.rb @@ -70,10 +70,6 @@ enabled { true } end - factory :initiative_assigned_to_you_campaign, class: EmailCampaigns::Campaigns::InitiativeAssignedToYou do - enabled { true } - end - factory :initiative_marked_as_spam_campaign, class: EmailCampaigns::Campaigns::InitiativeMarkedAsSpam do enabled { true } end diff --git a/back/engines/free/email_campaigns/spec/mailers/initiative_assigned_to_you_mailer_spec.rb b/back/engines/free/email_campaigns/spec/mailers/initiative_assigned_to_you_mailer_spec.rb deleted file mode 100644 index cc93cf523fed..000000000000 --- a/back/engines/free/email_campaigns/spec/mailers/initiative_assigned_to_you_mailer_spec.rb +++ /dev/null @@ -1,53 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe EmailCampaigns::InitiativeAssignedToYouMailer do - describe 'campaign_mail' do - let_it_be(:recipient) { create(:user, locale: 'en') } - let_it_be(:campaign) { EmailCampaigns::Campaigns::InitiativeAssignedToYou.create! } - let_it_be(:assigned_at) { Time.zone.now } - let_it_be(:initiative) { create(:initiative, author: recipient, assignee: create(:admin), assigned_at: assigned_at) } - let_it_be(:author_name) { UserDisplayNameService.new(AppConfiguration.instance, recipient).display_name!(initiative.author) } - let_it_be(:command) do - { - recipient: recipient, - event_payload: { - post_title_multiloc: initiative.title_multiloc, - post_body_multiloc: initiative.body_multiloc, - post_author_name: author_name, - post_published_at: initiative.published_at&.iso8601, - post_url: Frontend::UrlService.new.model_to_url(initiative, locale: Locale.new(recipient.locale)), - post_assigned_at: initiative.assigned_at&.iso8601 || Time.now.iso8601, - initiative_reactions_needed: initiative.reactions_needed, - initiative_expires_at: initiative.expires_at.iso8601 - } - } - end - - let_it_be(:mail) { described_class.with(command: command, campaign: campaign).campaign_mail.deliver_now } - - before_all { EmailCampaigns::UnsubscriptionToken.create!(user_id: recipient.id) } - - it 'renders the subject' do - expect(mail.subject).to start_with('You have an assignment on the platform of') - end - - it 'renders the receiver email' do - expect(mail.to).to eq([recipient.email]) - end - - it 'renders the sender email' do - expect(mail.from).to all(end_with('@citizenlab.co')) - end - - it 'assigns initiative author name' do - expect(mail.body.encoded).to match(author_name) - end - - it 'assigns go to initiative CTA' do - admin_initiatives_url = Frontend::UrlService.new.admin_initiatives_url - expect(mail.body.encoded).to match(admin_initiatives_url) - end - end -end diff --git a/back/engines/free/email_campaigns/spec/mailers/previews/email_campaigns/initiative_assigned_to_you_mailer_preview.rb b/back/engines/free/email_campaigns/spec/mailers/previews/email_campaigns/initiative_assigned_to_you_mailer_preview.rb deleted file mode 100644 index 51e22cd563e7..000000000000 --- a/back/engines/free/email_campaigns/spec/mailers/previews/email_campaigns/initiative_assigned_to_you_mailer_preview.rb +++ /dev/null @@ -1,28 +0,0 @@ -# frozen_string_literal: true - -module EmailCampaigns - class InitiativeAssignedToYouMailerPreview < ActionMailer::Preview - include EmailCampaigns::MailerPreviewRecipient - - def campaign_mail - initiative = Initiative.order(created_at: :asc).first - # TODO: generate commands with campaign#generate_commands method - command = { - recipient: recipient_user, - event_payload: { - post_title_multiloc: initiative.title_multiloc, - post_body_multiloc: initiative.body_multiloc, - post_author_name: UserDisplayNameService.new(AppConfiguration.instance, recipient_user).display_name!(initiative.author), - post_published_at: initiative.published_at&.iso8601, - post_url: Frontend::UrlService.new.model_to_url(initiative, locale: Locale.new(recipient_user.locale)), - post_assigned_at: initiative.assigned_at&.iso8601 || Time.now.iso8601, - initiative_reactions_needed: initiative.reactions_needed, - initiative_expires_at: initiative.expires_at.iso8601 - } - } - campaign = EmailCampaigns::Campaigns::InitiativeAssignedToYou.first - - campaign.mailer_class.with(campaign: campaign, command: command).campaign_mail - end - end -end diff --git a/back/spec/factories/notifications.rb b/back/spec/factories/notifications.rb index cdda0e6bb41e..95f8f7629a0a 100644 --- a/back/spec/factories/notifications.rb +++ b/back/spec/factories/notifications.rb @@ -114,11 +114,6 @@ initiating_user end - factory :initiative_assigned_to_you, parent: :notification, class: 'Notifications::InitiativeAssignedToYou' do - initiating_user - association :post, factory: :initiative - end - factory :initiative_marked_as_spam, parent: :notification, class: 'Notifications::InitiativeMarkedAsSpam' do association :post, factory: :initiative spam_report diff --git a/back/spec/jobs/log_activity_job_spec.rb b/back/spec/jobs/log_activity_job_spec.rb index 71c84ea8b6ee..c138677d8d1f 100644 --- a/back/spec/jobs/log_activity_job_spec.rb +++ b/back/spec/jobs/log_activity_job_spec.rb @@ -179,7 +179,6 @@ Notifications::CommentOnYourComment, Notifications::CommentOnIdeaYouFollow, Notifications::IdeaMarkedAsSpam, - Notifications::InitiativeAssignedToYou, Notifications::InitiativeMarkedAsSpam, Notifications::InviteAccepted, Notifications::MarkedAsSpam, From 83a5c8661f174a6e6e97beb35fb0859863f9daa0 Mon Sep 17 00:00:00 2001 From: Simon Tharby Date: Wed, 18 Dec 2024 12:51:25 +0000 Subject: [PATCH 41/63] [TAN-2538] Remove InitiativeMarkedAsSpam notification & campaign files & specs --- .../initiative_marked_as_spam.rb | 96 ------------------- .../initiative_marked_as_spam_serializer.rb | 24 ----- back/app/services/notification_service.rb | 1 - .../initiative_marked_as_spam_mailer.rb | 25 ----- .../campaigns/initiative_marked_as_spam.rb | 93 ------------------ .../email_campaigns/delivery_service.rb | 1 - .../campaign_mail.mjml | 42 -------- .../spec/factories/campaigns.rb | 4 - .../initiative_marked_as_spam_mailer_spec.rb | 58 ----------- ...nitiative_marked_as_spam_mailer_preview.rb | 31 ------ back/spec/factories/notifications.rb | 6 -- back/spec/jobs/log_activity_job_spec.rb | 1 - 12 files changed, 382 deletions(-) delete mode 100644 back/app/models/notifications/initiative_marked_as_spam.rb delete mode 100644 back/app/serializers/web_api/v1/notifications/initiative_marked_as_spam_serializer.rb delete mode 100644 back/engines/free/email_campaigns/app/mailers/email_campaigns/initiative_marked_as_spam_mailer.rb delete mode 100644 back/engines/free/email_campaigns/app/models/email_campaigns/campaigns/initiative_marked_as_spam.rb delete mode 100644 back/engines/free/email_campaigns/app/views/email_campaigns/initiative_marked_as_spam_mailer/campaign_mail.mjml delete mode 100644 back/engines/free/email_campaigns/spec/mailers/initiative_marked_as_spam_mailer_spec.rb delete mode 100644 back/engines/free/email_campaigns/spec/mailers/previews/email_campaigns/initiative_marked_as_spam_mailer_preview.rb diff --git a/back/app/models/notifications/initiative_marked_as_spam.rb b/back/app/models/notifications/initiative_marked_as_spam.rb deleted file mode 100644 index 811ca53d2944..000000000000 --- a/back/app/models/notifications/initiative_marked_as_spam.rb +++ /dev/null @@ -1,96 +0,0 @@ -# frozen_string_literal: true - -# == Schema Information -# -# Table name: notifications -# -# id :uuid not null, primary key -# type :string -# read_at :datetime -# recipient_id :uuid -# post_id :uuid -# comment_id :uuid -# project_id :uuid -# created_at :datetime not null -# updated_at :datetime not null -# initiating_user_id :uuid -# spam_report_id :uuid -# invite_id :uuid -# reason_code :string -# other_reason :string -# post_status_id :uuid -# official_feedback_id :uuid -# phase_id :uuid -# post_type :string -# post_status_type :string -# project_folder_id :uuid -# inappropriate_content_flag_id :uuid -# internal_comment_id :uuid -# basket_id :uuid -# cosponsors_initiative_id :uuid -# cosponsorship_id :uuid -# project_review_id :uuid -# -# Indexes -# -# index_notifications_on_basket_id (basket_id) -# index_notifications_on_cosponsors_initiative_id (cosponsors_initiative_id) -# index_notifications_on_cosponsorship_id (cosponsorship_id) -# index_notifications_on_created_at (created_at) -# index_notifications_on_inappropriate_content_flag_id (inappropriate_content_flag_id) -# index_notifications_on_initiating_user_id (initiating_user_id) -# index_notifications_on_internal_comment_id (internal_comment_id) -# index_notifications_on_invite_id (invite_id) -# index_notifications_on_official_feedback_id (official_feedback_id) -# index_notifications_on_phase_id (phase_id) -# index_notifications_on_post_id_and_post_type (post_id,post_type) -# index_notifications_on_post_status_id (post_status_id) -# index_notifications_on_post_status_id_and_post_status_type (post_status_id,post_status_type) -# index_notifications_on_project_review_id (project_review_id) -# index_notifications_on_recipient_id (recipient_id) -# index_notifications_on_recipient_id_and_read_at (recipient_id,read_at) -# index_notifications_on_spam_report_id (spam_report_id) -# -# Foreign Keys -# -# fk_rails_... (basket_id => baskets.id) -# fk_rails_... (comment_id => comments.id) -# fk_rails_... (cosponsors_initiative_id => cosponsors_initiatives.id) -# fk_rails_... (cosponsorship_id => cosponsorships.id) -# fk_rails_... (inappropriate_content_flag_id => flag_inappropriate_content_inappropriate_content_flags.id) -# fk_rails_... (initiating_user_id => users.id) -# fk_rails_... (internal_comment_id => internal_comments.id) -# fk_rails_... (invite_id => invites.id) -# fk_rails_... (official_feedback_id => official_feedbacks.id) -# fk_rails_... (phase_id => phases.id) -# fk_rails_... (project_id => projects.id) -# fk_rails_... (project_review_id => project_reviews.id) -# fk_rails_... (recipient_id => users.id) -# fk_rails_... (spam_report_id => spam_reports.id) -# -module Notifications - class InitiativeMarkedAsSpam < MarkedAsSpam - validates :post, presence: true - validates :post_type, inclusion: { in: ['Initiative'] } - - ACTIVITY_TRIGGERS = { 'SpamReport' => { 'created' => true } } - EVENT_NAME = 'Initiative marked as spam' - - def self.make_notifications_on(activity) - spam_report = activity.item - if spam_report.spam_reportable_type == 'Initiative' - initiator_id = spam_report.user_id - recipient_ids(initiator_id).map do |recipient_id| - new( - recipient_id: recipient_id, - initiating_user_id: initiator_id, - spam_report: spam_report, - post: spam_report.spam_reportable - ) - end - else - [] - end - end - end -end diff --git a/back/app/serializers/web_api/v1/notifications/initiative_marked_as_spam_serializer.rb b/back/app/serializers/web_api/v1/notifications/initiative_marked_as_spam_serializer.rb deleted file mode 100644 index 0758c665535a..000000000000 --- a/back/app/serializers/web_api/v1/notifications/initiative_marked_as_spam_serializer.rb +++ /dev/null @@ -1,24 +0,0 @@ -# frozen_string_literal: true - -class WebApi::V1::Notifications::InitiativeMarkedAsSpamSerializer < WebApi::V1::Notifications::NotificationSerializer - attribute :initiating_user_first_name do |object| - object.initiating_user&.first_name - end - - attribute :initiating_user_last_name do |object, params| - name_service = UserDisplayNameService.new(AppConfiguration.instance, current_user(params)) - name_service.last_name!(object.initiating_user) - end - - attribute :initiating_user_slug do |object| - object.initiating_user&.slug - end - - attribute :post_title_multiloc do |object| - object.post&.title_multiloc - end - - attribute :post_slug do |object| - object.post&.slug - end -end diff --git a/back/app/services/notification_service.rb b/back/app/services/notification_service.rb index 4355e829dd11..1e3f79b97f46 100644 --- a/back/app/services/notification_service.rb +++ b/back/app/services/notification_service.rb @@ -10,7 +10,6 @@ class NotificationService Notifications::CosponsorOfYourIdea, Notifications::CosponsorOfYourInitiative, Notifications::IdeaMarkedAsSpam, - Notifications::InitiativeMarkedAsSpam, Notifications::InitiativeResubmittedForReview, Notifications::InternalComments::InternalCommentOnIdeaAssignedToYou, Notifications::InternalComments::InternalCommentOnIdeaYouCommentedInternallyOn, diff --git a/back/engines/free/email_campaigns/app/mailers/email_campaigns/initiative_marked_as_spam_mailer.rb b/back/engines/free/email_campaigns/app/mailers/email_campaigns/initiative_marked_as_spam_mailer.rb deleted file mode 100644 index f5184b8460eb..000000000000 --- a/back/engines/free/email_campaigns/app/mailers/email_campaigns/initiative_marked_as_spam_mailer.rb +++ /dev/null @@ -1,25 +0,0 @@ -# frozen_string_literal: true - -module EmailCampaigns - class InitiativeMarkedAsSpamMailer < ApplicationMailer - protected - - def subject - format_message('subject', values: { organizationName: organization_name }) - end - - private - - def header_title - format_message('title_spam_report', values: { firstName: event.initiating_user_first_name, lastName: event.initiating_user_last_name }) - end - - def header_message - false - end - - def preheader - format_message('preheader') - end - end -end diff --git a/back/engines/free/email_campaigns/app/models/email_campaigns/campaigns/initiative_marked_as_spam.rb b/back/engines/free/email_campaigns/app/models/email_campaigns/campaigns/initiative_marked_as_spam.rb deleted file mode 100644 index 9029de537cfd..000000000000 --- a/back/engines/free/email_campaigns/app/models/email_campaigns/campaigns/initiative_marked_as_spam.rb +++ /dev/null @@ -1,93 +0,0 @@ -# frozen_string_literal: true - -# == Schema Information -# -# Table name: email_campaigns_campaigns -# -# id :uuid not null, primary key -# type :string not null -# author_id :uuid -# enabled :boolean -# sender :string -# reply_to :string -# schedule :jsonb -# subject_multiloc :jsonb -# body_multiloc :jsonb -# created_at :datetime not null -# updated_at :datetime not null -# deliveries_count :integer default(0), not null -# context_id :uuid -# -# Indexes -# -# index_email_campaigns_campaigns_on_author_id (author_id) -# index_email_campaigns_campaigns_on_context_id (context_id) -# index_email_campaigns_campaigns_on_type (type) -# -# Foreign Keys -# -# fk_rails_... (author_id => users.id) -# -module EmailCampaigns - class Campaigns::InitiativeMarkedAsSpam < Campaign - include Consentable - include ActivityTriggerable - include RecipientConfigurable - include Disableable - include LifecycleStageRestrictable - include Trackable - allow_lifecycle_stages only: %w[trial active] - - recipient_filter :filter_notification_recipient - - def self.consentable_roles - ['admin'] - end - - def mailer_class - InitiativeMarkedAsSpamMailer - end - - def activity_triggers - { 'Notifications::InitiativeMarkedAsSpam' => { 'created' => true } } - end - - def filter_notification_recipient(users_scope, activity:, time: nil) - users_scope.where(id: activity.item.recipient.id) - end - - def self.recipient_role_multiloc_key - 'email_campaigns.admin_labels.recipient_role.admins_and_managers' - end - - def self.recipient_segment_multiloc_key - 'email_campaigns.admin_labels.recipient_segment.admins' - end - - def self.content_type_multiloc_key - 'email_campaigns.admin_labels.content_type.proposals' - end - - def self.trigger_multiloc_key - 'email_campaigns.admin_labels.trigger.proposal_gets_reported_as_spam' - end - - def generate_commands(recipient:, activity:, time: nil) - notification = activity.item - [{ - event_payload: { - initiating_user_first_name: notification.initiating_user&.first_name, - initiating_user_last_name: notification.initiating_user&.last_name, - post_created_at: notification.post.created_at.iso8601, - post_title_multiloc: notification.post.title_multiloc, - post_author_name: notification.post.author_name, - post_url: Frontend::UrlService.new.model_to_url(notification.post, locale: Locale.new(recipient.locale)), - initiative_reactions_needed: notification.post.reactions_needed, - initiative_expires_at: notification.post.expires_at.iso8601, - spam_report_reason_code: notification.spam_report.reason_code, - spam_report_other_reason: notification.spam_report.other_reason - } - }] - end - end -end diff --git a/back/engines/free/email_campaigns/app/services/email_campaigns/delivery_service.rb b/back/engines/free/email_campaigns/app/services/email_campaigns/delivery_service.rb index a8cdf04d2f9d..03ba809dac66 100644 --- a/back/engines/free/email_campaigns/app/services/email_campaigns/delivery_service.rb +++ b/back/engines/free/email_campaigns/app/services/email_campaigns/delivery_service.rb @@ -15,7 +15,6 @@ class DeliveryService Campaigns::EventRegistrationConfirmation, Campaigns::IdeaMarkedAsSpam, Campaigns::IdeaPublished, - Campaigns::InitiativeMarkedAsSpam, Campaigns::InitiativePublished, Campaigns::InitiativeResubmittedForReview, Campaigns::InternalCommentOnIdeaAssignedToYou, diff --git a/back/engines/free/email_campaigns/app/views/email_campaigns/initiative_marked_as_spam_mailer/campaign_mail.mjml b/back/engines/free/email_campaigns/app/views/email_campaigns/initiative_marked_as_spam_mailer/campaign_mail.mjml deleted file mode 100644 index c0b7d5b69873..000000000000 --- a/back/engines/free/email_campaigns/app/views/email_campaigns/initiative_marked_as_spam_mailer/campaign_mail.mjml +++ /dev/null @@ -1,42 +0,0 @@ - - - - -

- <%= localize_for_recipient(event.post_title_multiloc) %> -

-

- <%= format_message('by_author', component: 'general', values: { authorName: event.post_author_name }) %> -

-
-
-
- - - - - - <%= format_message('reported_this_because', values: { - reporterFirstName: "#{event.initiating_user_first_name}" - }, escape_html: false) %> - - -

- <% case event.spam_report_reason_code %> - <% when 'other' %> - <%= event.spam_report_other_reason %> - <% when 'inappropriate' %> - <%= format_message('report_inappropriate_offensive_content') %> - <% when 'wrong_content' %> - <%= format_message('report_not_an_initiative') %> - <% end %> -

-
-
-
- - -<%= render partial: 'application/cta_button', locals: { href: event.post_url, message: format_message('cta_review_initiative') } %> diff --git a/back/engines/free/email_campaigns/spec/factories/campaigns.rb b/back/engines/free/email_campaigns/spec/factories/campaigns.rb index 4467107b1d47..92813ee09da0 100644 --- a/back/engines/free/email_campaigns/spec/factories/campaigns.rb +++ b/back/engines/free/email_campaigns/spec/factories/campaigns.rb @@ -70,10 +70,6 @@ enabled { true } end - factory :initiative_marked_as_spam_campaign, class: EmailCampaigns::Campaigns::InitiativeMarkedAsSpam do - enabled { true } - end - factory :initiative_published_campaign, class: EmailCampaigns::Campaigns::InitiativePublished do enabled { true } end diff --git a/back/engines/free/email_campaigns/spec/mailers/initiative_marked_as_spam_mailer_spec.rb b/back/engines/free/email_campaigns/spec/mailers/initiative_marked_as_spam_mailer_spec.rb deleted file mode 100644 index 7106b6e2d348..000000000000 --- a/back/engines/free/email_campaigns/spec/mailers/initiative_marked_as_spam_mailer_spec.rb +++ /dev/null @@ -1,58 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe EmailCampaigns::InitiativeMarkedAsSpamMailer do - describe 'campaign_mail' do - let_it_be(:recipient) { create(:user, locale: 'en') } - let_it_be(:campaign) { EmailCampaigns::Campaigns::InitiativeMarkedAsSpam.create! } - let_it_be(:initiating_user) { create(:user) } - let_it_be(:initiative) { create(:initiative, author: recipient) } - let_it_be(:command) do - { - recipient: recipient, - event_payload: { - initiating_user_first_name: initiating_user&.first_name, - initiating_user_last_name: initiating_user&.last_name, - post_created_at: initiative.created_at.iso8601, - post_title_multiloc: initiative.title_multiloc, - post_author_name: initiative.author_name, - post_url: Frontend::UrlService.new.model_to_url(initiative, locale: Locale.new(recipient.locale)), - initiative_reactions_needed: initiative.reactions_needed, - initiative_expires_at: initiative.expires_at.iso8601, - spam_report_reason_code: 'other', - spam_report_other_reason: 'This post is an intrusion to my privacy' - } - } - end - - let_it_be(:mail) { described_class.with(command: command, campaign: campaign).campaign_mail.deliver_now } - - before { EmailCampaigns::UnsubscriptionToken.create!(user_id: recipient.id) } - - it 'renders the subject' do - expect(mail.subject).to start_with('You have a spam report on the platform of') - end - - it 'renders the receiver email' do - expect(mail.to).to eq([recipient.email]) - end - - it 'renders the sender email' do - expect(mail.from).to all(end_with('@citizenlab.co')) - end - - it 'assigns reporter\'s name' do - expect(mail.body.encoded).to match(initiating_user.first_name) - end - - it 'assigns the reason' do - expect(mail.body.encoded).to match('This post is an intrusion to my privacy') - end - - it 'assigns go to initiative CTA' do - initiative_url = Frontend::UrlService.new.model_to_url(initiative, locale: Locale.new(recipient.locale)) - expect(mail.body.encoded).to match(initiative_url) - end - end -end diff --git a/back/engines/free/email_campaigns/spec/mailers/previews/email_campaigns/initiative_marked_as_spam_mailer_preview.rb b/back/engines/free/email_campaigns/spec/mailers/previews/email_campaigns/initiative_marked_as_spam_mailer_preview.rb deleted file mode 100644 index cdeeb07a0016..000000000000 --- a/back/engines/free/email_campaigns/spec/mailers/previews/email_campaigns/initiative_marked_as_spam_mailer_preview.rb +++ /dev/null @@ -1,31 +0,0 @@ -# frozen_string_literal: true - -module EmailCampaigns - class InitiativeMarkedAsSpamMailerPreview < ActionMailer::Preview - include EmailCampaigns::MailerPreviewRecipient - - def campaign_mail - initiative = Initiative.first - initiating_user = User.last - # TODO: generate commands with campaign#generate_commands method - command = { - recipient: recipient_user, - event_payload: { - initiating_user_first_name: initiating_user&.first_name, - initiating_user_last_name: initiating_user&.last_name, - post_created_at: initiative.created_at.iso8601, - post_title_multiloc: initiative.title_multiloc, - post_author_name: initiative.author_name, - post_url: Frontend::UrlService.new.model_to_url(initiative, locale: Locale.new(recipient_user.locale)), - initiative_reactions_needed: initiative.reactions_needed, - initiative_expires_at: initiative.expires_at.iso8601, - spam_report_reason_code: 'other', - spam_report_other_reason: 'This post is an intrusion to my privacy' - } - } - campaign = EmailCampaigns::Campaigns::InitiativeMarkedAsSpam.first - - campaign.mailer_class.with(campaign: campaign, command: command).campaign_mail - end - end -end diff --git a/back/spec/factories/notifications.rb b/back/spec/factories/notifications.rb index 95f8f7629a0a..c1f1a82b51fa 100644 --- a/back/spec/factories/notifications.rb +++ b/back/spec/factories/notifications.rb @@ -114,12 +114,6 @@ initiating_user end - factory :initiative_marked_as_spam, parent: :notification, class: 'Notifications::InitiativeMarkedAsSpam' do - association :post, factory: :initiative - spam_report - initiating_user - end - factory :invite_accepted, parent: :notification, class: 'Notifications::InviteAccepted' do initiating_user invite diff --git a/back/spec/jobs/log_activity_job_spec.rb b/back/spec/jobs/log_activity_job_spec.rb index c138677d8d1f..ada3362cf134 100644 --- a/back/spec/jobs/log_activity_job_spec.rb +++ b/back/spec/jobs/log_activity_job_spec.rb @@ -179,7 +179,6 @@ Notifications::CommentOnYourComment, Notifications::CommentOnIdeaYouFollow, Notifications::IdeaMarkedAsSpam, - Notifications::InitiativeMarkedAsSpam, Notifications::InviteAccepted, Notifications::MarkedAsSpam, Notifications::MentionInComment, From 776db9129462a2e3174e1c0c6114907f3dfd229d Mon Sep 17 00:00:00 2001 From: Simon Tharby Date: Wed, 18 Dec 2024 12:52:58 +0000 Subject: [PATCH 42/63] [TAN-2538] Remove InitiativePublished campaign files & specs --- .../initiative_published_mailer.rb | 25 ---- .../campaigns/initiative_published.rb | 94 --------------- .../email_campaigns/delivery_service.rb | 1 - .../campaign_mail.mjml | 111 ------------------ .../initiative_published_mailer_spec.rb | 58 --------- .../initiative_published_mailer_preview.rb | 35 ------ 6 files changed, 324 deletions(-) delete mode 100644 back/engines/free/email_campaigns/app/mailers/email_campaigns/initiative_published_mailer.rb delete mode 100644 back/engines/free/email_campaigns/app/models/email_campaigns/campaigns/initiative_published.rb delete mode 100644 back/engines/free/email_campaigns/app/views/email_campaigns/initiative_published_mailer/campaign_mail.mjml delete mode 100644 back/engines/free/email_campaigns/spec/mailers/initiative_published_mailer_spec.rb delete mode 100644 back/engines/free/email_campaigns/spec/mailers/previews/email_campaigns/initiative_published_mailer_preview.rb diff --git a/back/engines/free/email_campaigns/app/mailers/email_campaigns/initiative_published_mailer.rb b/back/engines/free/email_campaigns/app/mailers/email_campaigns/initiative_published_mailer.rb deleted file mode 100644 index 88a29be4f472..000000000000 --- a/back/engines/free/email_campaigns/app/mailers/email_campaigns/initiative_published_mailer.rb +++ /dev/null @@ -1,25 +0,0 @@ -# frozen_string_literal: true - -module EmailCampaigns - class InitiativePublishedMailer < ApplicationMailer - protected - - def subject - format_message('subject', values: { organizationName: organization_name }) - end - - private - - def header_title - format_message('main_header') - end - - def header_message - format_message('message_next_steps', values: { userFirstName: recipient_first_name, organizationName: organization_name }) - end - - def preheader - format_message('preheader', values: { initiativeTitle: localize_for_recipient(event.post_title_multiloc), organizationName: organization_name }) - end - end -end diff --git a/back/engines/free/email_campaigns/app/models/email_campaigns/campaigns/initiative_published.rb b/back/engines/free/email_campaigns/app/models/email_campaigns/campaigns/initiative_published.rb deleted file mode 100644 index b2640b2897e5..000000000000 --- a/back/engines/free/email_campaigns/app/models/email_campaigns/campaigns/initiative_published.rb +++ /dev/null @@ -1,94 +0,0 @@ -# frozen_string_literal: true - -# == Schema Information -# -# Table name: email_campaigns_campaigns -# -# id :uuid not null, primary key -# type :string not null -# author_id :uuid -# enabled :boolean -# sender :string -# reply_to :string -# schedule :jsonb -# subject_multiloc :jsonb -# body_multiloc :jsonb -# created_at :datetime not null -# updated_at :datetime not null -# deliveries_count :integer default(0), not null -# context_id :uuid -# -# Indexes -# -# index_email_campaigns_campaigns_on_author_id (author_id) -# index_email_campaigns_campaigns_on_context_id (context_id) -# index_email_campaigns_campaigns_on_type (type) -# -# Foreign Keys -# -# fk_rails_... (author_id => users.id) -# -module EmailCampaigns - class Campaigns::InitiativePublished < Campaign - include Consentable - include ActivityTriggerable - include RecipientConfigurable - include Disableable - include Trackable - include LifecycleStageRestrictable - allow_lifecycle_stages only: %w[trial active] - - recipient_filter :filter_recipient - - def mailer_class - InitiativePublishedMailer - end - - def activity_triggers - { 'Initiative' => { 'proposed' => true } } - end - - def filter_recipient(users_scope, activity:, time: nil) - users_scope.where(id: activity.item.author_id) - end - - def self.recipient_role_multiloc_key - 'email_campaigns.admin_labels.recipient_role.registered_users' - end - - def self.recipient_segment_multiloc_key - 'email_campaigns.admin_labels.recipient_segment.user_who_published_the_proposal' - end - - def self.content_type_multiloc_key - 'email_campaigns.admin_labels.content_type.proposals' - end - - def self.trigger_multiloc_key - 'email_campaigns.admin_labels.trigger.proposal_is_published' - end - - def generate_commands(recipient:, activity:) - initiative = activity.item - [{ - event_payload: { - post_id: initiative.id, - post_title_multiloc: initiative.title_multiloc, - post_body_multiloc: initiative.body_multiloc, - post_url: Frontend::UrlService.new.model_to_url(initiative, locale: Locale.new(recipient.locale)), - post_images: initiative.initiative_images.map do |image| - { - ordering: image.ordering, - versions: image.image.versions.to_h { |k, v| [k.to_s, v.url] } - } - end, - initiative_header_bg: { - versions: initiative.header_bg.versions.to_h { |k, v| [k.to_s, v.url] } - }, - initiative_reactions_needed: initiative.reactions_needed, - initiative_expires_at: initiative.expires_at.iso8601 - } - }] - end - end -end diff --git a/back/engines/free/email_campaigns/app/services/email_campaigns/delivery_service.rb b/back/engines/free/email_campaigns/app/services/email_campaigns/delivery_service.rb index 03ba809dac66..76823d0b9b8a 100644 --- a/back/engines/free/email_campaigns/app/services/email_campaigns/delivery_service.rb +++ b/back/engines/free/email_campaigns/app/services/email_campaigns/delivery_service.rb @@ -15,7 +15,6 @@ class DeliveryService Campaigns::EventRegistrationConfirmation, Campaigns::IdeaMarkedAsSpam, Campaigns::IdeaPublished, - Campaigns::InitiativePublished, Campaigns::InitiativeResubmittedForReview, Campaigns::InternalCommentOnIdeaAssignedToYou, Campaigns::InternalCommentOnIdeaYouCommentedInternallyOn, diff --git a/back/engines/free/email_campaigns/app/views/email_campaigns/initiative_published_mailer/campaign_mail.mjml b/back/engines/free/email_campaigns/app/views/email_campaigns/initiative_published_mailer/campaign_mail.mjml deleted file mode 100644 index b64cecf1ffb9..000000000000 --- a/back/engines/free/email_campaigns/app/views/email_campaigns/initiative_published_mailer/campaign_mail.mjml +++ /dev/null @@ -1,111 +0,0 @@ - - - - -

- <%= format_message("message_get_votes") %> -

-
- - - - - Checked checkmark - - - - <%= format_message('action_published_initiative') %> - - - - - - - - Empty checkmark - - - <%= format_message('action_share_fb', values: { - fbLink: "Facebook" - }, escape_html: false) - %> - - - Facebook icon - - - - - - Empty checkmark - - - <%= format_message('action_share_twitter', values: { - twitterLink: "Twitter" - }, escape_html: false) - %> - - - Twitter icon - - - - - - - Empty checkmark - - - <%= format_message('action_send_email', values: { - sendEmailLink: "#{format_message('send_email')}" - }, escape_html: false) - %> - - - Paper plane - - - - - - - Empty checkmark - - - <%= format_message('action_share_link', values: { - link: "#{format_message('link')}" - }, escape_html: false) - %> - - - Copy - - - - -
-
- - - <%= render partial: 'application/cta_button', locals: { href: event.post_url, message: format_message('cta_goto_proposal', component: 'general') } %> diff --git a/back/engines/free/email_campaigns/spec/mailers/initiative_published_mailer_spec.rb b/back/engines/free/email_campaigns/spec/mailers/initiative_published_mailer_spec.rb deleted file mode 100644 index 13b017a19bbd..000000000000 --- a/back/engines/free/email_campaigns/spec/mailers/initiative_published_mailer_spec.rb +++ /dev/null @@ -1,58 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe EmailCampaigns::InitiativePublishedMailer do - describe 'campaign_mail' do - let_it_be(:recipient) { create(:user, locale: 'en') } - let_it_be(:campaign) { EmailCampaigns::Campaigns::InitiativePublished.create! } - let_it_be(:initiative) { create(:initiative, author: recipient) } - let_it_be(:command) do - { - recipient: recipient, - event_payload: { - post_id: initiative.id, - post_title_multiloc: initiative.title_multiloc, - post_body_multiloc: initiative.body_multiloc, - post_url: Frontend::UrlService.new.model_to_url(initiative, locale: Locale.new(recipient.locale)), - post_images: initiative.initiative_images.map do |image| - { - ordering: image.ordering, - versions: image.image.versions.to_h { |k, v| [k.to_s, v.url] } - } - end, - initiative_header_bg: { - versions: initiative.header_bg.versions.to_h { |k, v| [k.to_s, v.url] } - }, - initiative_reactions_needed: initiative.reactions_needed, - initiative_expires_at: initiative.expires_at.iso8601 - } - } - end - - let_it_be(:mail) { described_class.with(command: command, campaign: campaign).campaign_mail.deliver_now } - - before_all { EmailCampaigns::UnsubscriptionToken.create!(user_id: recipient.id) } - - it 'renders the subject' do - expect(mail.subject).to start_with('Your proposal was published on the platform of') - end - - it 'renders the receiver email' do - expect(mail.to).to eq([recipient.email]) - end - - it 'renders the sender email' do - expect(mail.from).to all(end_with('@citizenlab.co')) - end - - it 'assigns organisation name' do - expect(mail.body.encoded).to match(AppConfiguration.instance.settings('core', 'organization_name', 'en')) - end - - it 'assigns go to initiative CTA' do - initiative_url = Frontend::UrlService.new.model_to_url(initiative, locale: Locale.new(recipient.locale)) - expect(mail.body.encoded).to match(initiative_url) - end - end -end diff --git a/back/engines/free/email_campaigns/spec/mailers/previews/email_campaigns/initiative_published_mailer_preview.rb b/back/engines/free/email_campaigns/spec/mailers/previews/email_campaigns/initiative_published_mailer_preview.rb deleted file mode 100644 index 6b9109be956d..000000000000 --- a/back/engines/free/email_campaigns/spec/mailers/previews/email_campaigns/initiative_published_mailer_preview.rb +++ /dev/null @@ -1,35 +0,0 @@ -# frozen_string_literal: true - -module EmailCampaigns - class InitiativePublishedMailerPreview < ActionMailer::Preview - include EmailCampaigns::MailerPreviewRecipient - - def campaign_mail - initiative = Initiative.first - # TODO: generate commands with campaign#generate_commands method - command = { - recipient: recipient_user, - event_payload: { - post_id: initiative.id, - post_title_multiloc: initiative.title_multiloc, - post_body_multiloc: initiative.body_multiloc, - post_url: Frontend::UrlService.new.model_to_url(initiative, locale: Locale.new(recipient_user.locale)), - post_images: initiative.initiative_images.map do |image| - { - ordering: image.ordering, - versions: image.image.versions.to_h { |k, v| [k.to_s, v.url] } - } - end, - initiative_header_bg: { - versions: initiative.header_bg.versions.to_h { |k, v| [k.to_s, v.url] } - }, - initiative_reactions_needed: initiative.reactions_needed, - initiative_expires_at: initiative.expires_at.iso8601 - } - } - campaign = EmailCampaigns::Campaigns::InitiativePublished.first - - campaign.mailer_class.with(campaign: campaign, command: command).campaign_mail - end - end -end From 6e4eb312b50361a41221267631711764887c783b Mon Sep 17 00:00:00 2001 From: Simon Tharby Date: Wed, 18 Dec 2024 12:55:47 +0000 Subject: [PATCH 43/63] [TAN-2538] Remove InitiativeResubmittedForReview notification & campaign files & specs --- .../initiative_resubmitted_for_review.rb | 104 ------------------ ...ative_resubmitted_for_review_serializer.rb | 11 -- back/app/services/notification_service.rb | 1 - ...nitiative_resubmitted_for_review_mailer.rb | 25 ----- .../initiative_resubmitted_for_review.rb | 84 -------------- .../email_campaigns/delivery_service.rb | 1 - .../campaign_mail.mjml | 20 ---- .../spec/factories/campaigns.rb | 8 -- ...tive_resubmitted_for_review_mailer_spec.rb | 45 -------- ...e_resubmitted_for_review_mailer_preview.rb | 19 ---- back/spec/factories/notifications.rb | 5 - .../initiative_resubmitted_for_review_spec.rb | 21 ---- 12 files changed, 344 deletions(-) delete mode 100644 back/app/models/notifications/initiative_resubmitted_for_review.rb delete mode 100644 back/app/serializers/web_api/v1/notifications/initiative_resubmitted_for_review_serializer.rb delete mode 100644 back/engines/free/email_campaigns/app/mailers/email_campaigns/initiative_resubmitted_for_review_mailer.rb delete mode 100644 back/engines/free/email_campaigns/app/models/email_campaigns/campaigns/initiative_resubmitted_for_review.rb delete mode 100644 back/engines/free/email_campaigns/app/views/email_campaigns/initiative_resubmitted_for_review_mailer/campaign_mail.mjml delete mode 100644 back/engines/free/email_campaigns/spec/mailers/initiative_resubmitted_for_review_mailer_spec.rb delete mode 100644 back/engines/free/email_campaigns/spec/mailers/previews/email_campaigns/initiative_resubmitted_for_review_mailer_preview.rb delete mode 100644 back/spec/models/notifications/initiative_resubmitted_for_review_spec.rb diff --git a/back/app/models/notifications/initiative_resubmitted_for_review.rb b/back/app/models/notifications/initiative_resubmitted_for_review.rb deleted file mode 100644 index c0eaf89d7afe..000000000000 --- a/back/app/models/notifications/initiative_resubmitted_for_review.rb +++ /dev/null @@ -1,104 +0,0 @@ -# frozen_string_literal: true - -# == Schema Information -# -# Table name: notifications -# -# id :uuid not null, primary key -# type :string -# read_at :datetime -# recipient_id :uuid -# post_id :uuid -# comment_id :uuid -# project_id :uuid -# created_at :datetime not null -# updated_at :datetime not null -# initiating_user_id :uuid -# spam_report_id :uuid -# invite_id :uuid -# reason_code :string -# other_reason :string -# post_status_id :uuid -# official_feedback_id :uuid -# phase_id :uuid -# post_type :string -# post_status_type :string -# project_folder_id :uuid -# inappropriate_content_flag_id :uuid -# internal_comment_id :uuid -# basket_id :uuid -# cosponsors_initiative_id :uuid -# cosponsorship_id :uuid -# project_review_id :uuid -# -# Indexes -# -# index_notifications_on_basket_id (basket_id) -# index_notifications_on_cosponsors_initiative_id (cosponsors_initiative_id) -# index_notifications_on_cosponsorship_id (cosponsorship_id) -# index_notifications_on_created_at (created_at) -# index_notifications_on_inappropriate_content_flag_id (inappropriate_content_flag_id) -# index_notifications_on_initiating_user_id (initiating_user_id) -# index_notifications_on_internal_comment_id (internal_comment_id) -# index_notifications_on_invite_id (invite_id) -# index_notifications_on_official_feedback_id (official_feedback_id) -# index_notifications_on_phase_id (phase_id) -# index_notifications_on_post_id_and_post_type (post_id,post_type) -# index_notifications_on_post_status_id (post_status_id) -# index_notifications_on_post_status_id_and_post_status_type (post_status_id,post_status_type) -# index_notifications_on_project_review_id (project_review_id) -# index_notifications_on_recipient_id (recipient_id) -# index_notifications_on_recipient_id_and_read_at (recipient_id,read_at) -# index_notifications_on_spam_report_id (spam_report_id) -# -# Foreign Keys -# -# fk_rails_... (basket_id => baskets.id) -# fk_rails_... (comment_id => comments.id) -# fk_rails_... (cosponsors_initiative_id => cosponsors_initiatives.id) -# fk_rails_... (cosponsorship_id => cosponsorships.id) -# fk_rails_... (inappropriate_content_flag_id => flag_inappropriate_content_inappropriate_content_flags.id) -# fk_rails_... (initiating_user_id => users.id) -# fk_rails_... (internal_comment_id => internal_comments.id) -# fk_rails_... (invite_id => invites.id) -# fk_rails_... (official_feedback_id => official_feedbacks.id) -# fk_rails_... (phase_id => phases.id) -# fk_rails_... (project_id => projects.id) -# fk_rails_... (project_review_id => project_reviews.id) -# fk_rails_... (recipient_id => users.id) -# fk_rails_... (spam_report_id => spam_reports.id) -# -module Notifications - class InitiativeResubmittedForReview < Notification - validates :post_status, :post, presence: true - validates :post_type, inclusion: { in: ['Initiative'] } - - ACTIVITY_TRIGGERS = { 'Initiative' => { 'changed_status' => true } } - EVENT_NAME = 'Initiative resubmitted for review' - - class << self - def make_notifications_on(activity) - initiative = activity.item - recipient_id = initiative&.assignee_id - - if initiative && recipient_id && transitioned_from_rejected_to_pending?(initiative) - [new( - recipient_id: recipient_id, - initiating_user_id: activity.user_id, - post: initiative, - post_status: initiative.initiative_status - )] - else - [] - end - end - - private - - def transitioned_from_rejected_to_pending?(initiative) - previous_change = initiative.initiative_status_changes.order(created_at: :asc)[-2] - previous_change && previous_change.initiative_status.code == 'changes_requested' && initiative.initiative_status.code == 'review_pending' - end - end - end -end diff --git a/back/app/serializers/web_api/v1/notifications/initiative_resubmitted_for_review_serializer.rb b/back/app/serializers/web_api/v1/notifications/initiative_resubmitted_for_review_serializer.rb deleted file mode 100644 index e9f4bd27f94e..000000000000 --- a/back/app/serializers/web_api/v1/notifications/initiative_resubmitted_for_review_serializer.rb +++ /dev/null @@ -1,11 +0,0 @@ -# frozen_string_literal: true - -class WebApi::V1::Notifications::InitiativeResubmittedForReviewSerializer < WebApi::V1::Notifications::NotificationSerializer - attribute :post_title_multiloc do |object| - object.post&.title_multiloc - end - - attribute :post_slug do |object| - object.post&.slug - end -end diff --git a/back/app/services/notification_service.rb b/back/app/services/notification_service.rb index 1e3f79b97f46..702d933276b4 100644 --- a/back/app/services/notification_service.rb +++ b/back/app/services/notification_service.rb @@ -10,7 +10,6 @@ class NotificationService Notifications::CosponsorOfYourIdea, Notifications::CosponsorOfYourInitiative, Notifications::IdeaMarkedAsSpam, - Notifications::InitiativeResubmittedForReview, Notifications::InternalComments::InternalCommentOnIdeaAssignedToYou, Notifications::InternalComments::InternalCommentOnIdeaYouCommentedInternallyOn, Notifications::InternalComments::InternalCommentOnIdeaYouModerate, diff --git a/back/engines/free/email_campaigns/app/mailers/email_campaigns/initiative_resubmitted_for_review_mailer.rb b/back/engines/free/email_campaigns/app/mailers/email_campaigns/initiative_resubmitted_for_review_mailer.rb deleted file mode 100644 index b82ac46ce727..000000000000 --- a/back/engines/free/email_campaigns/app/mailers/email_campaigns/initiative_resubmitted_for_review_mailer.rb +++ /dev/null @@ -1,25 +0,0 @@ -# frozen_string_literal: true - -module EmailCampaigns - class InitiativeResubmittedForReviewMailer < ApplicationMailer - protected - - def subject - format_message('subject', values: { organizationName: organization_name }) - end - - private - - def header_title - format_message('main_header', values: { firstName: recipient_first_name }) - end - - def header_message - format_message('event_description_initiative') - end - - def preheader - format_message('preheader_initiative', values: { organizationName: organization_name, authorName: event.post_author_name }) - end - end -end diff --git a/back/engines/free/email_campaigns/app/models/email_campaigns/campaigns/initiative_resubmitted_for_review.rb b/back/engines/free/email_campaigns/app/models/email_campaigns/campaigns/initiative_resubmitted_for_review.rb deleted file mode 100644 index eb3831d95d36..000000000000 --- a/back/engines/free/email_campaigns/app/models/email_campaigns/campaigns/initiative_resubmitted_for_review.rb +++ /dev/null @@ -1,84 +0,0 @@ -# frozen_string_literal: true - -# == Schema Information -# -# Table name: email_campaigns_campaigns -# -# id :uuid not null, primary key -# type :string not null -# author_id :uuid -# enabled :boolean -# sender :string -# reply_to :string -# schedule :jsonb -# subject_multiloc :jsonb -# body_multiloc :jsonb -# created_at :datetime not null -# updated_at :datetime not null -# deliveries_count :integer default(0), not null -# context_id :uuid -# -# Indexes -# -# index_email_campaigns_campaigns_on_author_id (author_id) -# index_email_campaigns_campaigns_on_context_id (context_id) -# index_email_campaigns_campaigns_on_type (type) -# -# Foreign Keys -# -# fk_rails_... (author_id => users.id) -# -module EmailCampaigns - class Campaigns::InitiativeResubmittedForReview < Campaign - include Consentable - include ActivityTriggerable - include RecipientConfigurable - include Disableable - include Trackable - include LifecycleStageRestrictable - allow_lifecycle_stages only: %w[trial active] - - recipient_filter :filter_notification_recipient - - def mailer_class - InitiativeResubmittedForReviewMailer - end - - def activity_triggers - { 'Notifications::InitiativeResubmittedForReview' => { 'created' => true } } - end - - def filter_notification_recipient(users_scope, activity:, time: nil) - users_scope.where(id: activity.item.recipient.id) - end - - def self.recipient_role_multiloc_key - 'email_campaigns.admin_labels.recipient_role.admins_and_managers' - end - - def self.recipient_segment_multiloc_key - 'email_campaigns.admin_labels.recipient_segment.admins_assigned_to_a_proposal' - end - - def self.content_type_multiloc_key - 'email_campaigns.admin_labels.content_type.proposals' - end - - def self.trigger_multiloc_key - 'email_campaigns.admin_labels.trigger.initiative_resubmitted_for_review' - end - - def generate_commands(recipient:, activity:) - initiative = activity.item.post - name_service = UserDisplayNameService.new(AppConfiguration.instance, recipient) - - [{ - event_payload: { - post_title_multiloc: initiative.title_multiloc, - post_body_multiloc: initiative.body_multiloc, - post_author_name: name_service.display_name!(initiative.author) - } - }] - end - end -end diff --git a/back/engines/free/email_campaigns/app/services/email_campaigns/delivery_service.rb b/back/engines/free/email_campaigns/app/services/email_campaigns/delivery_service.rb index 76823d0b9b8a..2edb826dbe56 100644 --- a/back/engines/free/email_campaigns/app/services/email_campaigns/delivery_service.rb +++ b/back/engines/free/email_campaigns/app/services/email_campaigns/delivery_service.rb @@ -15,7 +15,6 @@ class DeliveryService Campaigns::EventRegistrationConfirmation, Campaigns::IdeaMarkedAsSpam, Campaigns::IdeaPublished, - Campaigns::InitiativeResubmittedForReview, Campaigns::InternalCommentOnIdeaAssignedToYou, Campaigns::InternalCommentOnIdeaYouCommentedInternallyOn, Campaigns::InternalCommentOnIdeaYouModerate, diff --git a/back/engines/free/email_campaigns/app/views/email_campaigns/initiative_resubmitted_for_review_mailer/campaign_mail.mjml b/back/engines/free/email_campaigns/app/views/email_campaigns/initiative_resubmitted_for_review_mailer/campaign_mail.mjml deleted file mode 100644 index 5eb306cae8c8..000000000000 --- a/back/engines/free/email_campaigns/app/views/email_campaigns/initiative_resubmitted_for_review_mailer/campaign_mail.mjml +++ /dev/null @@ -1,20 +0,0 @@ - - - - -

- <%= localize_for_recipient(event.post_title_multiloc) %> -

- <%= format_message('by_author', values: { authorName: event.post_author_name }) %> -

-

- <%= localize_for_recipient(event.post_body_multiloc).html_safe %> -

-
-
-
- -<%= render partial: 'application/cta_button', locals: { href: url_service.admin_initiatives_url, message: format_message('cta_reply_to', values: { authorName: event.post_author_name }) } %> diff --git a/back/engines/free/email_campaigns/spec/factories/campaigns.rb b/back/engines/free/email_campaigns/spec/factories/campaigns.rb index 92813ee09da0..e879db1eea5c 100644 --- a/back/engines/free/email_campaigns/spec/factories/campaigns.rb +++ b/back/engines/free/email_campaigns/spec/factories/campaigns.rb @@ -70,14 +70,6 @@ enabled { true } end - factory :initiative_published_campaign, class: EmailCampaigns::Campaigns::InitiativePublished do - enabled { true } - end - - factory :initiative_resubmitted_for_review_campaign, class: EmailCampaigns::Campaigns::InitiativeResubmittedForReview do - enabled { true } - end - factory :internal_comment_on_idea_assigned_to_you_campaign, class: EmailCampaigns::Campaigns::InternalCommentOnIdeaAssignedToYou do enabled { true } end diff --git a/back/engines/free/email_campaigns/spec/mailers/initiative_resubmitted_for_review_mailer_spec.rb b/back/engines/free/email_campaigns/spec/mailers/initiative_resubmitted_for_review_mailer_spec.rb deleted file mode 100644 index c6f4f7f78a5e..000000000000 --- a/back/engines/free/email_campaigns/spec/mailers/initiative_resubmitted_for_review_mailer_spec.rb +++ /dev/null @@ -1,45 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -# TODO: move-old-proposals-test -RSpec.describe EmailCampaigns::InitiativeResubmittedForReviewMailer do - describe 'campaign_mail' do - let_it_be(:recipient) { create(:user, locale: 'en') } - let_it_be(:author) { create(:user) } - let_it_be(:initiative) { create(:initiative, author: author) } - let_it_be(:campaign) { EmailCampaigns::Campaigns::InitiativeResubmittedForReview.create! } - let_it_be(:author_name) { UserDisplayNameService.new(AppConfiguration.instance, author).display_name!(initiative.author) } - let_it_be(:command) do - item = Notifications::InitiativeResubmittedForReview.new(post: initiative) - activity = Activity.new(item: item) - commands = EmailCampaigns::Campaigns::InitiativeResubmittedForReview.new.generate_commands(recipient: recipient, activity: activity) - commands[0].merge({ recipient: recipient }) - end - - let_it_be(:mail) { described_class.with(command: command, campaign: campaign).campaign_mail.deliver_now } - - before_all { EmailCampaigns::UnsubscriptionToken.create!(user_id: recipient.id) } - - it 'renders the subject' do - expect(mail.subject).to start_with('You have a proposal to review on the platform of') - end - - it 'renders the receiver email' do - expect(mail.to).to eq([recipient.email]) - end - - it 'renders the sender email' do - expect(mail.from).to all(end_with('@citizenlab.co')) - end - - it 'assigns initiative author name' do - expect(mail.body.encoded).to match(author_name) - end - - it 'assigns cta url' do - admin_initiatives_url = Frontend::UrlService.new.admin_initiatives_url - expect(mail.body.encoded).to match(admin_initiatives_url) - end - end -end diff --git a/back/engines/free/email_campaigns/spec/mailers/previews/email_campaigns/initiative_resubmitted_for_review_mailer_preview.rb b/back/engines/free/email_campaigns/spec/mailers/previews/email_campaigns/initiative_resubmitted_for_review_mailer_preview.rb deleted file mode 100644 index 31388f5b3df3..000000000000 --- a/back/engines/free/email_campaigns/spec/mailers/previews/email_campaigns/initiative_resubmitted_for_review_mailer_preview.rb +++ /dev/null @@ -1,19 +0,0 @@ -# frozen_string_literal: true - -module EmailCampaigns - class InitiativeResubmittedForReviewMailerPreview < ActionMailer::Preview - include EmailCampaigns::MailerPreviewRecipient - - def campaign_mail - campaign = EmailCampaigns::Campaigns::InitiativeResubmittedForReview.first - initiative = Initiative.order(created_at: :asc).first - user = User.order(created_at: :asc).first - item = Notifications::InitiativeResubmittedForReview.new(post: initiative) - activity = Activity.new(item: item) - commands = EmailCampaigns::Campaigns::InitiativeResubmittedForReview.new.generate_commands(recipient: user, activity: activity) - command = commands[0].merge({ recipient: user }) - - campaign.mailer_class.with(campaign: campaign, command: command).campaign_mail - end - end -end diff --git a/back/spec/factories/notifications.rb b/back/spec/factories/notifications.rb index c1f1a82b51fa..bb92ef46bac9 100644 --- a/back/spec/factories/notifications.rb +++ b/back/spec/factories/notifications.rb @@ -131,11 +131,6 @@ initiating_user end - factory :initiative_resubmitted_for_review, parent: :notification, class: 'Notifications::InitiativeResubmittedForReview' do - association :post, factory: :initiative - association :post_status, factory: :initiative_status, code: 'review_pending' - end - factory :comment_on_idea_you_follow, parent: :notification, class: 'Notifications::CommentOnIdeaYouFollow' do initiating_user comment diff --git a/back/spec/models/notifications/initiative_resubmitted_for_review_spec.rb b/back/spec/models/notifications/initiative_resubmitted_for_review_spec.rb deleted file mode 100644 index dd930fb9da21..000000000000 --- a/back/spec/models/notifications/initiative_resubmitted_for_review_spec.rb +++ /dev/null @@ -1,21 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe Notifications::InitiativeResubmittedForReview do - describe '.make_notifications_on' do - it 'makes a notification on changed_status activity, when status changes from changes_requested to review_pending' do - assignee = create(:admin) - initiative = create(:initiative, assignee: assignee) - create(:initiative_status_change, initiative: initiative, initiative_status: create(:initiative_status, code: 'changes_requested')) - create(:initiative_status_change, initiative: initiative, initiative_status: create(:initiative_status, code: 'review_pending')) - activity = create(:activity, item: initiative, action: 'changed_status') - - notifications = described_class.make_notifications_on activity - expect(notifications.first).to have_attributes( - recipient_id: assignee.id, - post_id: initiative.id - ) - end - end -end From 533091f3cfe66fcd6c6affd37c71899c02007256 Mon Sep 17 00:00:00 2001 From: Simon Tharby Date: Wed, 18 Dec 2024 12:59:56 +0000 Subject: [PATCH 44/63] [TAN-2538] Remove CosponsorOfYourInitiative notification & campaign files & specs --- .../cosponsor_of_your_initiative.rb | 96 ------------------- ...cosponsor_of_your_initiative_serializer.rb | 24 ----- back/app/services/notification_service.rb | 1 - .../cosponsor_of_your_initiative_mailer.rb | 25 ----- .../campaigns/cosponsor_of_your_initiative.rb | 95 ------------------ .../email_campaigns/delivery_service.rb | 1 - .../campaign_mail.mjml | 2 - .../spec/factories/campaigns.rb | 4 - ...osponsor_of_your_initiative_mailer_spec.rb | 46 --------- ...onsor_of_your_initiative_mailer_preview.rb | 19 ---- back/spec/factories/notifications.rb | 12 --- .../cosponsor_of_your_initiative_spec.rb | 25 ----- 12 files changed, 350 deletions(-) delete mode 100644 back/app/models/notifications/cosponsor_of_your_initiative.rb delete mode 100644 back/app/serializers/web_api/v1/notifications/cosponsor_of_your_initiative_serializer.rb delete mode 100644 back/engines/free/email_campaigns/app/mailers/email_campaigns/cosponsor_of_your_initiative_mailer.rb delete mode 100644 back/engines/free/email_campaigns/app/models/email_campaigns/campaigns/cosponsor_of_your_initiative.rb delete mode 100644 back/engines/free/email_campaigns/app/views/email_campaigns/cosponsor_of_your_initiative_mailer/campaign_mail.mjml delete mode 100644 back/engines/free/email_campaigns/spec/mailers/cosponsor_of_your_initiative_mailer_spec.rb delete mode 100644 back/engines/free/email_campaigns/spec/mailers/previews/email_campaigns/cosponsor_of_your_initiative_mailer_preview.rb delete mode 100644 back/spec/models/notifications/cosponsor_of_your_initiative_spec.rb diff --git a/back/app/models/notifications/cosponsor_of_your_initiative.rb b/back/app/models/notifications/cosponsor_of_your_initiative.rb deleted file mode 100644 index 731528cf6028..000000000000 --- a/back/app/models/notifications/cosponsor_of_your_initiative.rb +++ /dev/null @@ -1,96 +0,0 @@ -# frozen_string_literal: true - -# == Schema Information -# -# Table name: notifications -# -# id :uuid not null, primary key -# type :string -# read_at :datetime -# recipient_id :uuid -# post_id :uuid -# comment_id :uuid -# project_id :uuid -# created_at :datetime not null -# updated_at :datetime not null -# initiating_user_id :uuid -# spam_report_id :uuid -# invite_id :uuid -# reason_code :string -# other_reason :string -# post_status_id :uuid -# official_feedback_id :uuid -# phase_id :uuid -# post_type :string -# post_status_type :string -# project_folder_id :uuid -# inappropriate_content_flag_id :uuid -# internal_comment_id :uuid -# basket_id :uuid -# cosponsors_initiative_id :uuid -# cosponsorship_id :uuid -# project_review_id :uuid -# -# Indexes -# -# index_notifications_on_basket_id (basket_id) -# index_notifications_on_cosponsors_initiative_id (cosponsors_initiative_id) -# index_notifications_on_cosponsorship_id (cosponsorship_id) -# index_notifications_on_created_at (created_at) -# index_notifications_on_inappropriate_content_flag_id (inappropriate_content_flag_id) -# index_notifications_on_initiating_user_id (initiating_user_id) -# index_notifications_on_internal_comment_id (internal_comment_id) -# index_notifications_on_invite_id (invite_id) -# index_notifications_on_official_feedback_id (official_feedback_id) -# index_notifications_on_phase_id (phase_id) -# index_notifications_on_post_id_and_post_type (post_id,post_type) -# index_notifications_on_post_status_id (post_status_id) -# index_notifications_on_post_status_id_and_post_status_type (post_status_id,post_status_type) -# index_notifications_on_project_review_id (project_review_id) -# index_notifications_on_recipient_id (recipient_id) -# index_notifications_on_recipient_id_and_read_at (recipient_id,read_at) -# index_notifications_on_spam_report_id (spam_report_id) -# -# Foreign Keys -# -# fk_rails_... (basket_id => baskets.id) -# fk_rails_... (comment_id => comments.id) -# fk_rails_... (cosponsors_initiative_id => cosponsors_initiatives.id) -# fk_rails_... (cosponsorship_id => cosponsorships.id) -# fk_rails_... (inappropriate_content_flag_id => flag_inappropriate_content_inappropriate_content_flags.id) -# fk_rails_... (initiating_user_id => users.id) -# fk_rails_... (internal_comment_id => internal_comments.id) -# fk_rails_... (invite_id => invites.id) -# fk_rails_... (official_feedback_id => official_feedbacks.id) -# fk_rails_... (phase_id => phases.id) -# fk_rails_... (project_id => projects.id) -# fk_rails_... (project_review_id => project_reviews.id) -# fk_rails_... (recipient_id => users.id) -# fk_rails_... (spam_report_id => spam_reports.id) -# -module Notifications - class CosponsorOfYourInitiative < Notification - validates :initiating_user, :cosponsors_initiative, presence: true - - ACTIVITY_TRIGGERS = { 'CosponsorsInitiative' => { 'cosponsorship_accepted' => true } } - EVENT_NAME = 'Cosponsor has accepted your invitation to cosponsor your initiative' - - def self.make_notifications_on(activity) - cosponsors_initiative = activity&.item - initiating_user = activity&.user - initiative = cosponsors_initiative&.initiative - recipient_id = initiative&.author_id - - if recipient_id && initiating_user && initiative - [new( - recipient_id: recipient_id, - initiating_user: initiating_user, - cosponsors_initiative: cosponsors_initiative, - post: initiative - )] - else - [] - end - end - end -end diff --git a/back/app/serializers/web_api/v1/notifications/cosponsor_of_your_initiative_serializer.rb b/back/app/serializers/web_api/v1/notifications/cosponsor_of_your_initiative_serializer.rb deleted file mode 100644 index 3704d93821ce..000000000000 --- a/back/app/serializers/web_api/v1/notifications/cosponsor_of_your_initiative_serializer.rb +++ /dev/null @@ -1,24 +0,0 @@ -# frozen_string_literal: true - -class WebApi::V1::Notifications::CosponsorOfYourInitiativeSerializer < WebApi::V1::Notifications::NotificationSerializer - attribute :initiating_user_first_name do |object| - object.initiating_user&.first_name - end - - attribute :initiating_user_last_name do |object, params| - name_service = UserDisplayNameService.new(AppConfiguration.instance, current_user(params)) - name_service.last_name!(object.initiating_user) - end - - attribute :initiating_user_slug do |object| - object.initiating_user&.slug - end - - attribute :post_title_multiloc do |object| - object.post&.title_multiloc - end - - attribute :post_slug do |object| - object.post&.slug - end -end diff --git a/back/app/services/notification_service.rb b/back/app/services/notification_service.rb index 702d933276b4..e3584bc80a65 100644 --- a/back/app/services/notification_service.rb +++ b/back/app/services/notification_service.rb @@ -8,7 +8,6 @@ class NotificationService Notifications::CommentOnIdeaYouFollow, Notifications::CommentOnYourComment, Notifications::CosponsorOfYourIdea, - Notifications::CosponsorOfYourInitiative, Notifications::IdeaMarkedAsSpam, Notifications::InternalComments::InternalCommentOnIdeaAssignedToYou, Notifications::InternalComments::InternalCommentOnIdeaYouCommentedInternallyOn, diff --git a/back/engines/free/email_campaigns/app/mailers/email_campaigns/cosponsor_of_your_initiative_mailer.rb b/back/engines/free/email_campaigns/app/mailers/email_campaigns/cosponsor_of_your_initiative_mailer.rb deleted file mode 100644 index fc73e3166a6c..000000000000 --- a/back/engines/free/email_campaigns/app/mailers/email_campaigns/cosponsor_of_your_initiative_mailer.rb +++ /dev/null @@ -1,25 +0,0 @@ -# frozen_string_literal: true - -module EmailCampaigns - class CosponsorOfYourInitiativeMailer < ApplicationMailer - protected - - def subject - format_message('subject', values: { cosponsorName: event.post_cosponsor_name }) - end - - def header_title - format_message('main_header', values: { cosponsorName: event.post_cosponsor_name }) - end - - private - - def header_message - format_message('event_description_initiative', values: { cosponsorName: event.post_cosponsor_name }) - end - - def preheader - format_message('preheader_initiative', values: { cosponsorName: event.post_cosponsor_name }) - end - end -end diff --git a/back/engines/free/email_campaigns/app/models/email_campaigns/campaigns/cosponsor_of_your_initiative.rb b/back/engines/free/email_campaigns/app/models/email_campaigns/campaigns/cosponsor_of_your_initiative.rb deleted file mode 100644 index e70a52021bc0..000000000000 --- a/back/engines/free/email_campaigns/app/models/email_campaigns/campaigns/cosponsor_of_your_initiative.rb +++ /dev/null @@ -1,95 +0,0 @@ -# frozen_string_literal: true - -# == Schema Information -# -# Table name: email_campaigns_campaigns -# -# id :uuid not null, primary key -# type :string not null -# author_id :uuid -# enabled :boolean -# sender :string -# reply_to :string -# schedule :jsonb -# subject_multiloc :jsonb -# body_multiloc :jsonb -# created_at :datetime not null -# updated_at :datetime not null -# deliveries_count :integer default(0), not null -# context_id :uuid -# -# Indexes -# -# index_email_campaigns_campaigns_on_author_id (author_id) -# index_email_campaigns_campaigns_on_context_id (context_id) -# index_email_campaigns_campaigns_on_type (type) -# -# Foreign Keys -# -# fk_rails_... (author_id => users.id) -# -module EmailCampaigns - class Campaigns::CosponsorOfYourInitiative < Campaign - include Consentable - include ActivityTriggerable - include RecipientConfigurable - include Disableable - include Trackable - include LifecycleStageRestrictable - allow_lifecycle_stages only: %w[trial active] - - recipient_filter :filter_notification_recipient - - def mailer_class - CosponsorOfYourInitiativeMailer - end - - def activity_triggers - { 'Notifications::CosponsorOfYourInitiative' => { 'created' => true } } - end - - def filter_notification_recipient(users_scope, activity:, time: nil) - users_scope.where(id: activity.item.recipient.id) - end - - def self.recipient_role_multiloc_key - 'email_campaigns.admin_labels.recipient_role.registered_users' - end - - def self.recipient_segment_multiloc_key - 'email_campaigns.admin_labels.recipient_segment.user_who_published_the_proposal' - end - - def self.content_type_multiloc_key - 'email_campaigns.admin_labels.content_type.proposals' - end - - def self.trigger_multiloc_key - 'email_campaigns.admin_labels.trigger.user_accepts_invitation_to_cosponsor_a_proposal' - end - - def generate_commands(recipient:, activity:) - initiative = activity.item.post - cosponsor = activity.item.initiating_user - name_service = UserDisplayNameService.new(AppConfiguration.instance, recipient) - - [{ - event_payload: { - post_title_multiloc: initiative.title_multiloc, - post_body_multiloc: initiative.body_multiloc, - post_author_name: name_service.display_name!(initiative.author), - post_cosponsor_name: name_service.display_name!(cosponsor), - post_url: Frontend::UrlService.new.model_to_url(initiative, locale: Locale.new(recipient.locale)), - post_image_medium_url: post_image_medium_url(initiative) - } - }] - end - - private - - def post_image_medium_url(initiative) - image = initiative&.initiative_images&.first - image.image.versions[:medium].url if image&.image&.versions - end - end -end diff --git a/back/engines/free/email_campaigns/app/services/email_campaigns/delivery_service.rb b/back/engines/free/email_campaigns/app/services/email_campaigns/delivery_service.rb index 2edb826dbe56..c9fbdb92062c 100644 --- a/back/engines/free/email_campaigns/app/services/email_campaigns/delivery_service.rb +++ b/back/engines/free/email_campaigns/app/services/email_campaigns/delivery_service.rb @@ -10,7 +10,6 @@ class DeliveryService Campaigns::CommentMarkedAsSpam, Campaigns::CommentOnIdeaYouFollow, Campaigns::CommentOnYourComment, - Campaigns::CosponsorOfYourInitiative, Campaigns::CosponsorOfYourIdea, Campaigns::EventRegistrationConfirmation, Campaigns::IdeaMarkedAsSpam, diff --git a/back/engines/free/email_campaigns/app/views/email_campaigns/cosponsor_of_your_initiative_mailer/campaign_mail.mjml b/back/engines/free/email_campaigns/app/views/email_campaigns/cosponsor_of_your_initiative_mailer/campaign_mail.mjml deleted file mode 100644 index 297e57cace57..000000000000 --- a/back/engines/free/email_campaigns/app/views/email_campaigns/cosponsor_of_your_initiative_mailer/campaign_mail.mjml +++ /dev/null @@ -1,2 +0,0 @@ -<%= render 'email_campaigns/posts/post_with_image', post_image_url: event&.post_image_medium_url, post_title_multiloc: event.post_title_multiloc, post_body_multiloc: event.post_body_multiloc %> -<%= render partial: 'application/cta_button', locals: { href: event.post_url, message: format_message('cta_reply_to', values: { authorName: event.post_author_name }) } %> diff --git a/back/engines/free/email_campaigns/spec/factories/campaigns.rb b/back/engines/free/email_campaigns/spec/factories/campaigns.rb index e879db1eea5c..e64f2e697466 100644 --- a/back/engines/free/email_campaigns/spec/factories/campaigns.rb +++ b/back/engines/free/email_campaigns/spec/factories/campaigns.rb @@ -42,10 +42,6 @@ enabled { true } end - factory :cosponsor_of_your_initiative_campaign, class: EmailCampaigns::Campaigns::CosponsorOfYourInitiative do - enabled { true } - end - factory :cosponsor_of_your_idea_campaign, class: EmailCampaigns::Campaigns::CosponsorOfYourIdea do enabled { true } end diff --git a/back/engines/free/email_campaigns/spec/mailers/cosponsor_of_your_initiative_mailer_spec.rb b/back/engines/free/email_campaigns/spec/mailers/cosponsor_of_your_initiative_mailer_spec.rb deleted file mode 100644 index 1ec16b426a74..000000000000 --- a/back/engines/free/email_campaigns/spec/mailers/cosponsor_of_your_initiative_mailer_spec.rb +++ /dev/null @@ -1,46 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -# TODO: move-old-proposals-test -RSpec.describe EmailCampaigns::CosponsorOfYourInitiativeMailer do - describe 'campaign_mail' do - let_it_be(:recipient) { create(:user, locale: 'en') } - let_it_be(:author) { create(:user) } - let_it_be(:cosponsor) { create(:user) } - let_it_be(:initiative) { create(:initiative, author: author) } - let_it_be(:campaign) { EmailCampaigns::Campaigns::CosponsorOfYourInitiative.create! } - let_it_be(:cosponsor_name) { UserDisplayNameService.new(AppConfiguration.instance, cosponsor).display_name!(cosponsor) } - let_it_be(:command) do - item = Notifications::CosponsorOfYourInitiative.new(post: initiative, initiating_user: cosponsor) - activity = Activity.new(item: item, user: author) - commands = EmailCampaigns::Campaigns::CosponsorOfYourInitiative.new.generate_commands(recipient: recipient, activity: activity) - commands[0].merge({ recipient: recipient }) - end - - let_it_be(:mail) { described_class.with(command: command, campaign: campaign).campaign_mail.deliver_now } - - before_all { EmailCampaigns::UnsubscriptionToken.create!(user_id: recipient.id) } - - it 'renders the subject' do - expect(mail.subject).to match('has accepted your invitation to co-sponsor your proposal') - end - - it 'renders the receiver email' do - expect(mail.to).to eq([recipient.email]) - end - - it 'renders the sender email' do - expect(mail.from).to all(end_with('@citizenlab.co')) - end - - it 'assigns initiative cosponsor name' do - expect(mail.body.encoded).to match(cosponsor_name) - end - - it 'assigns cta url' do - post_url = Frontend::UrlService.new.model_to_url(initiative, locale: Locale.new(recipient.locale)) - expect(mail.body.encoded).to match(post_url) - end - end -end diff --git a/back/engines/free/email_campaigns/spec/mailers/previews/email_campaigns/cosponsor_of_your_initiative_mailer_preview.rb b/back/engines/free/email_campaigns/spec/mailers/previews/email_campaigns/cosponsor_of_your_initiative_mailer_preview.rb deleted file mode 100644 index 27dfe1df492f..000000000000 --- a/back/engines/free/email_campaigns/spec/mailers/previews/email_campaigns/cosponsor_of_your_initiative_mailer_preview.rb +++ /dev/null @@ -1,19 +0,0 @@ -# frozen_string_literal: true - -module EmailCampaigns - class CosponsorOfYourInitiativeMailerPreview < ActionMailer::Preview - include EmailCampaigns::MailerPreviewRecipient - - def campaign_mail - campaign = EmailCampaigns::Campaigns::CosponsorOfYourInitiative.first - initiative = Initiative.order(created_at: :asc).first - user = User.order(created_at: :asc).first - item = Notifications::CosponsorOfYourInitiative.new(post: initiative) - activity = Activity.new(item: item, user: user) - commands = EmailCampaigns::Campaigns::CosponsorOfYourInitiative.new.generate_commands(recipient: user, activity: activity) - command = commands[0].merge({ recipient: user }) - - campaign.mailer_class.with(campaign: campaign, command: command).campaign_mail - end - end -end diff --git a/back/spec/factories/notifications.rb b/back/spec/factories/notifications.rb index bb92ef46bac9..1df75c05c39d 100644 --- a/back/spec/factories/notifications.rb +++ b/back/spec/factories/notifications.rb @@ -31,12 +31,6 @@ association :post, factory: :idea end - factory :cosponsor_of_your_initiative, parent: :notification, class: 'Notifications::CosponsorOfYourInitiative' do - association :post, factory: :initiative - cosponsors_initiative - initiating_user - end - factory :cosponsor_of_your_idea, parent: :notification, class: 'Notifications::CosponsorOfYourIdea' do association :post, factory: :idea cosponsorship @@ -138,12 +132,6 @@ project end - factory :comment_on_initiative_you_follow, parent: :notification, class: 'Notifications::CommentOnInitiativeYouFollow' do - initiating_user - comment - association :post, factory: :initiative - end - factory :mention_in_comment, parent: :notification, class: 'Notifications::MentionInComment' do initiating_user comment diff --git a/back/spec/models/notifications/cosponsor_of_your_initiative_spec.rb b/back/spec/models/notifications/cosponsor_of_your_initiative_spec.rb deleted file mode 100644 index 2eedc91b14dc..000000000000 --- a/back/spec/models/notifications/cosponsor_of_your_initiative_spec.rb +++ /dev/null @@ -1,25 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe Notifications::CosponsorOfYourInitiative do - describe 'cosponsor_of_your_initiative' do - it 'makes a notification on cosponsorship_accepted cosponsors_initiative activity' do - initiative = create(:initiative, author: create(:user)) - cosponsors_initiative = create(:cosponsors_initiative, initiative: initiative, status: 'accepted') - activity = create( - :activity, - item: cosponsors_initiative, - user_id: cosponsors_initiative.user_id, - action: 'cosponsorship_accepted' - ) - - notifications = described_class.make_notifications_on activity - expect(notifications.first).to have_attributes( - recipient_id: initiative.author_id, - initiating_user: cosponsors_initiative.user, - post: initiative - ) - end - end -end From d5b4e9513b44fcb74d1ad7e03c95bc7da7d9deef Mon Sep 17 00:00:00 2001 From: Simon Tharby Date: Wed, 18 Dec 2024 13:47:24 +0000 Subject: [PATCH 45/63] [TAN-2538] Remove InternalCommentOnInitiativeAssignedToYou notification & campaign files & specs --- ...l_comment_on_initiative_assigned_to_you.rb | 82 ------------------- ...n_initiative_assigned_to_you_serializer.rb | 4 - back/app/services/notification_service.rb | 1 - ...t_on_initiative_assigned_to_you_builder.rb | 26 ------ ...nt_on_initiative_assigned_to_you_mailer.rb | 6 -- ...l_comment_on_initiative_assigned_to_you.rb | 79 ------------------ .../email_campaigns/delivery_service.rb | 1 - .../campaign_mail.mjml | 1 - .../spec/factories/campaigns.rb | 4 - ..._initiative_assigned_to_you_mailer_spec.rb | 36 -------- ...itiative_assigned_to_you_mailer_preview.rb | 31 ------- ...ment_on_initiative_assigned_to_you_spec.rb | 19 ----- back/spec/factories/notifications.rb | 8 -- ...ment_on_initiative_assigned_to_you_spec.rb | 74 ----------------- ...iative_you_commented_internally_on_spec.rb | 8 -- 15 files changed, 380 deletions(-) delete mode 100644 back/app/models/notifications/internal_comments/internal_comment_on_initiative_assigned_to_you.rb delete mode 100644 back/app/serializers/web_api/v1/notifications/internal_comment_on_initiative_assigned_to_you_serializer.rb delete mode 100644 back/app/services/notifications/internal_comments/internal_comment_on_initiative_assigned_to_you_builder.rb delete mode 100644 back/engines/free/email_campaigns/app/mailers/email_campaigns/internal_comment_on_initiative_assigned_to_you_mailer.rb delete mode 100644 back/engines/free/email_campaigns/app/models/email_campaigns/campaigns/internal_comment_on_initiative_assigned_to_you.rb delete mode 100644 back/engines/free/email_campaigns/app/views/email_campaigns/internal_comment_on_initiative_assigned_to_you_mailer/campaign_mail.mjml delete mode 100644 back/engines/free/email_campaigns/spec/mailers/internal_comment_on_initiative_assigned_to_you_mailer_spec.rb delete mode 100644 back/engines/free/email_campaigns/spec/mailers/previews/email_campaigns/internal_comment_on_initiative_assigned_to_you_mailer_preview.rb delete mode 100644 back/engines/free/email_campaigns/spec/models/email_campaigns/campaigns/internal_comment_on_initiative_assigned_to_you_spec.rb delete mode 100644 back/spec/models/notifications/internal_comments/internal_comment_on_initiative_assigned_to_you_spec.rb diff --git a/back/app/models/notifications/internal_comments/internal_comment_on_initiative_assigned_to_you.rb b/back/app/models/notifications/internal_comments/internal_comment_on_initiative_assigned_to_you.rb deleted file mode 100644 index 9f2c328a172b..000000000000 --- a/back/app/models/notifications/internal_comments/internal_comment_on_initiative_assigned_to_you.rb +++ /dev/null @@ -1,82 +0,0 @@ -# frozen_string_literal: true - -# == Schema Information -# -# Table name: notifications -# -# id :uuid not null, primary key -# type :string -# read_at :datetime -# recipient_id :uuid -# post_id :uuid -# comment_id :uuid -# project_id :uuid -# created_at :datetime not null -# updated_at :datetime not null -# initiating_user_id :uuid -# spam_report_id :uuid -# invite_id :uuid -# reason_code :string -# other_reason :string -# post_status_id :uuid -# official_feedback_id :uuid -# phase_id :uuid -# post_type :string -# post_status_type :string -# project_folder_id :uuid -# inappropriate_content_flag_id :uuid -# internal_comment_id :uuid -# basket_id :uuid -# cosponsors_initiative_id :uuid -# cosponsorship_id :uuid -# project_review_id :uuid -# -# Indexes -# -# index_notifications_on_basket_id (basket_id) -# index_notifications_on_cosponsors_initiative_id (cosponsors_initiative_id) -# index_notifications_on_cosponsorship_id (cosponsorship_id) -# index_notifications_on_created_at (created_at) -# index_notifications_on_inappropriate_content_flag_id (inappropriate_content_flag_id) -# index_notifications_on_initiating_user_id (initiating_user_id) -# index_notifications_on_internal_comment_id (internal_comment_id) -# index_notifications_on_invite_id (invite_id) -# index_notifications_on_official_feedback_id (official_feedback_id) -# index_notifications_on_phase_id (phase_id) -# index_notifications_on_post_id_and_post_type (post_id,post_type) -# index_notifications_on_post_status_id (post_status_id) -# index_notifications_on_post_status_id_and_post_status_type (post_status_id,post_status_type) -# index_notifications_on_project_review_id (project_review_id) -# index_notifications_on_recipient_id (recipient_id) -# index_notifications_on_recipient_id_and_read_at (recipient_id,read_at) -# index_notifications_on_spam_report_id (spam_report_id) -# -# Foreign Keys -# -# fk_rails_... (basket_id => baskets.id) -# fk_rails_... (comment_id => comments.id) -# fk_rails_... (cosponsors_initiative_id => cosponsors_initiatives.id) -# fk_rails_... (cosponsorship_id => cosponsorships.id) -# fk_rails_... (inappropriate_content_flag_id => flag_inappropriate_content_inappropriate_content_flags.id) -# fk_rails_... (initiating_user_id => users.id) -# fk_rails_... (internal_comment_id => internal_comments.id) -# fk_rails_... (invite_id => invites.id) -# fk_rails_... (official_feedback_id => official_feedbacks.id) -# fk_rails_... (phase_id => phases.id) -# fk_rails_... (project_id => projects.id) -# fk_rails_... (project_review_id => project_reviews.id) -# fk_rails_... (recipient_id => users.id) -# fk_rails_... (spam_report_id => spam_reports.id) -# -module Notifications - class InternalComments::InternalCommentOnInitiativeAssignedToYou < Notification - validates :initiating_user, :internal_comment, :post, presence: true - - ACTIVITY_TRIGGERS = { 'InternalComment' => { 'created' => true } } - EVENT_NAME = 'Internal comment on initiative assigned to you' - - def self.make_notifications_on(activity) - InternalComments::InternalCommentOnInitiativeAssignedToYouBuilder.new(activity).build_notifications - end - end -end diff --git a/back/app/serializers/web_api/v1/notifications/internal_comment_on_initiative_assigned_to_you_serializer.rb b/back/app/serializers/web_api/v1/notifications/internal_comment_on_initiative_assigned_to_you_serializer.rb deleted file mode 100644 index 14d31cd81403..000000000000 --- a/back/app/serializers/web_api/v1/notifications/internal_comment_on_initiative_assigned_to_you_serializer.rb +++ /dev/null @@ -1,4 +0,0 @@ -# frozen_string_literal: true - -class WebApi::V1::Notifications::InternalCommentOnInitiativeAssignedToYouSerializer < WebApi::V1::Notifications::InternalCommentNotificationSerializer -end diff --git a/back/app/services/notification_service.rb b/back/app/services/notification_service.rb index e3584bc80a65..5833a50d0267 100644 --- a/back/app/services/notification_service.rb +++ b/back/app/services/notification_service.rb @@ -12,7 +12,6 @@ class NotificationService Notifications::InternalComments::InternalCommentOnIdeaAssignedToYou, Notifications::InternalComments::InternalCommentOnIdeaYouCommentedInternallyOn, Notifications::InternalComments::InternalCommentOnIdeaYouModerate, - Notifications::InternalComments::InternalCommentOnInitiativeAssignedToYou, Notifications::InternalComments::InternalCommentOnInitiativeYouCommentedInternallyOn, Notifications::InternalComments::InternalCommentOnUnassignedInitiative, Notifications::InternalComments::InternalCommentOnUnassignedUnmoderatedIdea, diff --git a/back/app/services/notifications/internal_comments/internal_comment_on_initiative_assigned_to_you_builder.rb b/back/app/services/notifications/internal_comments/internal_comment_on_initiative_assigned_to_you_builder.rb deleted file mode 100644 index c02106ad6009..000000000000 --- a/back/app/services/notifications/internal_comments/internal_comment_on_initiative_assigned_to_you_builder.rb +++ /dev/null @@ -1,26 +0,0 @@ -# frozen_string_literal: true - -module Notifications - class InternalComments::InternalCommentOnInitiativeAssignedToYouBuilder < InternalComments::BaseNotificationBuilder - protected - - def skip_recipient?(recipient) - initiator_id.nil? || - initiator_id == recipient.id || - recipient.id == parent_author_id || - MentionService.new.user_mentioned?(internal_comment.body, recipient) - end - - def recipients - [User.find_by(id: internal_comment&.post&.assignee_id)].compact - end - - def preconditions_met? - post_type == 'Initiative' - end - - def notification_class - Notifications::InternalComments::InternalCommentOnInitiativeAssignedToYou - end - end -end diff --git a/back/engines/free/email_campaigns/app/mailers/email_campaigns/internal_comment_on_initiative_assigned_to_you_mailer.rb b/back/engines/free/email_campaigns/app/mailers/email_campaigns/internal_comment_on_initiative_assigned_to_you_mailer.rb deleted file mode 100644 index 7ee697d6d952..000000000000 --- a/back/engines/free/email_campaigns/app/mailers/email_campaigns/internal_comment_on_initiative_assigned_to_you_mailer.rb +++ /dev/null @@ -1,6 +0,0 @@ -# frozen_string_literal: true - -module EmailCampaigns - class InternalCommentOnInitiativeAssignedToYouMailer < BaseInternalCommentMailer - end -end diff --git a/back/engines/free/email_campaigns/app/models/email_campaigns/campaigns/internal_comment_on_initiative_assigned_to_you.rb b/back/engines/free/email_campaigns/app/models/email_campaigns/campaigns/internal_comment_on_initiative_assigned_to_you.rb deleted file mode 100644 index ba84d3b3fc62..000000000000 --- a/back/engines/free/email_campaigns/app/models/email_campaigns/campaigns/internal_comment_on_initiative_assigned_to_you.rb +++ /dev/null @@ -1,79 +0,0 @@ -# frozen_string_literal: true - -# == Schema Information -# -# Table name: email_campaigns_campaigns -# -# id :uuid not null, primary key -# type :string not null -# author_id :uuid -# enabled :boolean -# sender :string -# reply_to :string -# schedule :jsonb -# subject_multiloc :jsonb -# body_multiloc :jsonb -# created_at :datetime not null -# updated_at :datetime not null -# deliveries_count :integer default(0), not null -# context_id :uuid -# -# Indexes -# -# index_email_campaigns_campaigns_on_author_id (author_id) -# index_email_campaigns_campaigns_on_context_id (context_id) -# index_email_campaigns_campaigns_on_type (type) -# -# Foreign Keys -# -# fk_rails_... (author_id => users.id) -# -module EmailCampaigns - class Campaigns::InternalCommentOnInitiativeAssignedToYou < Campaign - include Consentable - include ActivityTriggerable - include RecipientConfigurable - include Disableable - include LifecycleStageRestrictable - include Trackable - allow_lifecycle_stages only: %w[trial active] - - recipient_filter :filter_notification_recipient - - def activity_triggers - { 'Notifications::InternalComments::InternalCommentOnInitiativeAssignedToYou' => { 'created' => true } } - end - - def filter_notification_recipient(users_scope, activity:, time: nil) - users_scope.where(id: activity.item.recipient.id) - end - - def self.consentable_roles - %w[admin] - end - - def self.recipient_role_multiloc_key - 'email_campaigns.admin_labels.recipient_role.admins' - end - - def self.recipient_segment_multiloc_key - 'email_campaigns.admin_labels.recipient_segment.admins' - end - - def self.content_type_multiloc_key - 'email_campaigns.admin_labels.content_type.internal_comments' - end - - def self.trigger_multiloc_key - 'email_campaigns.admin_labels.trigger.internal_comment_is_posted_on_initiative_assigned_to_user' - end - - def mailer_class - InternalCommentOnInitiativeAssignedToYouMailer - end - - def generate_commands(recipient:, activity:, time: nil) - InternalCommentCampaignCommandsBuilder.new.build_commands(recipient, activity) - end - end -end diff --git a/back/engines/free/email_campaigns/app/services/email_campaigns/delivery_service.rb b/back/engines/free/email_campaigns/app/services/email_campaigns/delivery_service.rb index c9fbdb92062c..f0e0b86ff154 100644 --- a/back/engines/free/email_campaigns/app/services/email_campaigns/delivery_service.rb +++ b/back/engines/free/email_campaigns/app/services/email_campaigns/delivery_service.rb @@ -17,7 +17,6 @@ class DeliveryService Campaigns::InternalCommentOnIdeaAssignedToYou, Campaigns::InternalCommentOnIdeaYouCommentedInternallyOn, Campaigns::InternalCommentOnIdeaYouModerate, - Campaigns::InternalCommentOnInitiativeAssignedToYou, Campaigns::InternalCommentOnInitiativeYouCommentedInternallyOn, Campaigns::InternalCommentOnUnassignedInitiative, Campaigns::InternalCommentOnUnassignedUnmoderatedIdea, diff --git a/back/engines/free/email_campaigns/app/views/email_campaigns/internal_comment_on_initiative_assigned_to_you_mailer/campaign_mail.mjml b/back/engines/free/email_campaigns/app/views/email_campaigns/internal_comment_on_initiative_assigned_to_you_mailer/campaign_mail.mjml deleted file mode 100644 index 328c41c16b0a..000000000000 --- a/back/engines/free/email_campaigns/app/views/email_campaigns/internal_comment_on_initiative_assigned_to_you_mailer/campaign_mail.mjml +++ /dev/null @@ -1 +0,0 @@ -<%= render 'email_campaigns/internal_comments/internal_comment_mail', event: event %> diff --git a/back/engines/free/email_campaigns/spec/factories/campaigns.rb b/back/engines/free/email_campaigns/spec/factories/campaigns.rb index e64f2e697466..33ebb1388368 100644 --- a/back/engines/free/email_campaigns/spec/factories/campaigns.rb +++ b/back/engines/free/email_campaigns/spec/factories/campaigns.rb @@ -78,10 +78,6 @@ enabled { true } end - factory :internal_comment_on_initiative_assigned_to_you_campaign, class: EmailCampaigns::Campaigns::InternalCommentOnInitiativeAssignedToYou do - enabled { true } - end - factory :internal_comment_on_initiative_you_commented_internally_on_campaign, class: EmailCampaigns::Campaigns::InternalCommentOnInitiativeYouCommentedInternallyOn do enabled { true } end diff --git a/back/engines/free/email_campaigns/spec/mailers/internal_comment_on_initiative_assigned_to_you_mailer_spec.rb b/back/engines/free/email_campaigns/spec/mailers/internal_comment_on_initiative_assigned_to_you_mailer_spec.rb deleted file mode 100644 index c1e9e20328bc..000000000000 --- a/back/engines/free/email_campaigns/spec/mailers/internal_comment_on_initiative_assigned_to_you_mailer_spec.rb +++ /dev/null @@ -1,36 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe EmailCampaigns::InternalCommentOnInitiativeAssignedToYouMailer do - describe 'InternalCommentOnInitiativeAssignedToYouMailer' do - let_it_be(:recipient) { create(:user, locale: 'en') } - let_it_be(:post_image) { create(:idea_image) } - let_it_be(:campaign) { EmailCampaigns::Campaigns::InternalCommentOnInitiativeAssignedToYou.create! } - let_it_be(:token) { ResetPasswordService.new.generate_reset_password_token recipient } - let_it_be(:command) do - { - recipient: recipient, - event_payload: { - initiating_user_first_name: 'Matthias', - initiating_user_last_name: 'Geeke', - internal_comment_author_name: 'Matthias Geeke', - internal_comment_body: 'I agree. I really think this proposal is amazing! Wowzers!', - internal_comment_url: 'http://localhost:3000/en/internal_comments/fake-url-comment-does-not-exist', - post_title_multiloc: { en: 'Permit paving of front gardens' }, - post_body_multiloc: { - en: 'There are many advantages to paving your front garden. Less cars on the road and more space for pedestrians.' - }, - post_type: 'Initiative', - post_image_medium_url: post_image.image.versions[:medium].url - } - } - end - - let_it_be(:mail) { described_class.with(command: command, campaign: campaign).campaign_mail.deliver_now } - - before_all { EmailCampaigns::UnsubscriptionToken.create!(user_id: recipient.id) } - - include_examples 'internal_comment_campaign_mailer_examples' - end -end diff --git a/back/engines/free/email_campaigns/spec/mailers/previews/email_campaigns/internal_comment_on_initiative_assigned_to_you_mailer_preview.rb b/back/engines/free/email_campaigns/spec/mailers/previews/email_campaigns/internal_comment_on_initiative_assigned_to_you_mailer_preview.rb deleted file mode 100644 index 3667b9257f29..000000000000 --- a/back/engines/free/email_campaigns/spec/mailers/previews/email_campaigns/internal_comment_on_initiative_assigned_to_you_mailer_preview.rb +++ /dev/null @@ -1,31 +0,0 @@ -# frozen_string_literal: true - -module EmailCampaigns - class InternalCommentOnInitiativeAssignedToYouMailerPreview < ActionMailer::Preview - include EmailCampaigns::MailerPreviewRecipient - - def campaign_mail - # TODO: generate commands with campaign#generate_commands method - command = { - recipient: recipient_user, - event_payload: { - initiating_user_first_name: 'Matthias', - initiating_user_last_name: 'Geeke', - internal_comment_author_name: 'Matthias Geeke', - internal_comment_body: 'I agree. I really think this proposal is amazing!', - internal_comment_url: 'http://localhost:3000/en/internal_comments/fake-url-comment-does-not-exist', - post_title_multiloc: { en: 'Permit paving of front gardens' }, - post_body_multiloc: { - en: 'There are many advantages to paving your front garden. Less cars on the road and more space for pedestrians.' - }, - post_type: 'Initiative', - post_image_medium_url: InitiativeImage.first.image.versions[:medium].url - } - } - - campaign = EmailCampaigns::Campaigns::InternalCommentOnInitiativeAssignedToYou.first - - campaign.mailer_class.with(campaign: campaign, command: command).campaign_mail - end - end -end diff --git a/back/engines/free/email_campaigns/spec/models/email_campaigns/campaigns/internal_comment_on_initiative_assigned_to_you_spec.rb b/back/engines/free/email_campaigns/spec/models/email_campaigns/campaigns/internal_comment_on_initiative_assigned_to_you_spec.rb deleted file mode 100644 index 83ad0da92d81..000000000000 --- a/back/engines/free/email_campaigns/spec/models/email_campaigns/campaigns/internal_comment_on_initiative_assigned_to_you_spec.rb +++ /dev/null @@ -1,19 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe EmailCampaigns::Campaigns::InternalCommentOnInitiativeAssignedToYou do - describe 'InternalCommentOnInitiativeAssignedToYou Campaign default factory' do - it 'is valid' do - expect(build(:internal_comment_on_initiative_assigned_to_you_campaign)).to be_valid - end - end - - describe '#generate_commands' do - let(:campaign) { create(:internal_comment_on_initiative_assigned_to_you_campaign) } - let(:notification) { create(:internal_comment_on_initiative_assigned_to_you) } - let!(:post_image) { create(:initiative_image, initiative: notification.post) } - - include_examples 'internal_comment_campaign_generate_commands' - end -end diff --git a/back/spec/factories/notifications.rb b/back/spec/factories/notifications.rb index 1df75c05c39d..d8f2a7dea065 100644 --- a/back/spec/factories/notifications.rb +++ b/back/spec/factories/notifications.rb @@ -61,14 +61,6 @@ association :post, factory: :idea end - factory :internal_comment_on_initiative_assigned_to_you, - parent: :notification, - class: 'Notifications::InternalComments::InternalCommentOnInitiativeAssignedToYou' do - initiating_user - internal_comment - association :post, factory: :initiative - end - factory :internal_comment_on_initiative_you_commented_internally_on, parent: :notification, class: 'Notifications::InternalComments::InternalCommentOnInitiativeYouCommentedInternallyOn' do diff --git a/back/spec/models/notifications/internal_comments/internal_comment_on_initiative_assigned_to_you_spec.rb b/back/spec/models/notifications/internal_comments/internal_comment_on_initiative_assigned_to_you_spec.rb deleted file mode 100644 index 265a2994e526..000000000000 --- a/back/spec/models/notifications/internal_comments/internal_comment_on_initiative_assigned_to_you_spec.rb +++ /dev/null @@ -1,74 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe Notifications::InternalComments::InternalCommentOnInitiativeAssignedToYou do - describe 'make_notifications_on' do - let(:assignee) { create(:admin) } - let(:initiative) { create(:initiative, assignee: assignee) } - let(:internal_comment) { create(:internal_comment, post: initiative) } - - context 'when an assignee should receive this notification' do - let(:activity) { create(:activity, item: internal_comment, action: 'created') } - - it 'makes a notification on created internal comment activity' do - notifications = described_class.make_notifications_on activity - expect(notifications.first).to have_attributes( - recipient_id: assignee.id, - initiating_user_id: internal_comment.author_id, - post_id: initiative.id, - internal_comment_id: internal_comment.id - ) - end - end - - shared_examples 'no notification created' do - it 'does not make a notification on created internal comment activity' do - notifications_count = described_class.count - activity = create(:activity, item: internal_comment, action: 'created') - notifications = described_class.make_notifications_on activity - - expect(notifications.count).to eq notifications_count - end - end - - context 'when the recipient is the internal comment author' do - let(:internal_comment) { create(:internal_comment, author: assignee, post: initiative) } - - it_behaves_like 'no notification created' - end - - context "when the internal comment is a comment on the assignee's internal comment" do - let(:parent_internal_comment) { create(:internal_comment, post: initiative, author: assignee) } - let(:internal_comment) { create(:internal_comment, parent: parent_internal_comment, post: initiative) } - - # Don't create this notification if the Activity (internal comment created) - # should lead to a InternalCommentOnYourInternalComment notification to the recipient. - it_behaves_like 'no notification created' - end - - context 'when the internal comment contains a mention of the assignee' do - let(:internal_comment) do - create( - :internal_comment, - :with_mentions, - mentioned_users: [assignee], - post: initiative - ) - end - - # Don't create this notification if the Activity (internal comment created) - # should lead to a MentionInInternalComment notification to the recipient. - it_behaves_like 'no notification created' - end - - context 'when the internal comment is on an idea the assignee is assigned to' do - let(:idea) { create(:idea, assignee: assignee) } - let(:internal_comment) { create(:internal_comment, post: idea) } - - # Don't create this notification if the Activity (internal comment created) - # should lead to a InternalCommentOnIdeaAssignedToYou notification to the recipient. - it_behaves_like 'no notification created' - end - end -end diff --git a/back/spec/models/notifications/internal_comments/internal_comment_on_initiative_you_commented_internally_on_spec.rb b/back/spec/models/notifications/internal_comments/internal_comment_on_initiative_you_commented_internally_on_spec.rb index 7019c7dfd4ea..b0fd5026fe0f 100644 --- a/back/spec/models/notifications/internal_comments/internal_comment_on_initiative_you_commented_internally_on_spec.rb +++ b/back/spec/models/notifications/internal_comments/internal_comment_on_initiative_you_commented_internally_on_spec.rb @@ -84,13 +84,5 @@ # should lead to a MentionInInternalComment notification to the recipient. it_behaves_like 'no notification created' end - - context 'when the internal comment is on an initiative the recipient is assigned to' do - let(:initiative) { create(:initiative, assignee: commenter) } - - # Don't create this notification if the Activity (internal comment created) - # should lead to a InternalCommentOnInitiativeAssignedToYou notification to the recipient. - it_behaves_like 'no notification created' - end end end From acc9891e62d6bf3d1e495913a5d5a6737cdd577a Mon Sep 17 00:00:00 2001 From: Simon Tharby Date: Wed, 18 Dec 2024 13:53:01 +0000 Subject: [PATCH 46/63] [TAN-2538] Remove InternalCommentOnInitiativeYouCommentedInternallyOn notification & campaign files & specs --- ..._initiative_you_commented_internally_on.rb | 82 ----------------- ..._you_commented_internally_on_serializer.rb | 4 - back/app/services/notification_service.rb | 1 - ...ive_you_commented_internally_on_builder.rb | 27 ------ ...tive_you_commented_internally_on_mailer.rb | 6 -- ..._initiative_you_commented_internally_on.rb | 79 ----------------- .../email_campaigns/delivery_service.rb | 1 - .../campaign_mail.mjml | 1 - .../spec/factories/campaigns.rb | 4 - ...you_commented_internally_on_mailer_spec.rb | 36 -------- ..._commented_internally_on_mailer_preview.rb | 31 ------- ...iative_you_commented_internally_on_spec.rb | 19 ---- back/spec/factories/notifications.rb | 8 -- ...iative_you_commented_internally_on_spec.rb | 88 ------------------- ...l_comment_on_unassigned_initiative_spec.rb | 79 ----------------- ...ent_on_unassigned_unmoderated_idea_spec.rb | 2 +- 16 files changed, 1 insertion(+), 467 deletions(-) delete mode 100644 back/app/models/notifications/internal_comments/internal_comment_on_initiative_you_commented_internally_on.rb delete mode 100644 back/app/serializers/web_api/v1/notifications/internal_comment_on_initiative_you_commented_internally_on_serializer.rb delete mode 100644 back/app/services/notifications/internal_comments/internal_comment_on_initiative_you_commented_internally_on_builder.rb delete mode 100644 back/engines/free/email_campaigns/app/mailers/email_campaigns/internal_comment_on_initiative_you_commented_internally_on_mailer.rb delete mode 100644 back/engines/free/email_campaigns/app/models/email_campaigns/campaigns/internal_comment_on_initiative_you_commented_internally_on.rb delete mode 100644 back/engines/free/email_campaigns/app/views/email_campaigns/internal_comment_on_initiative_you_commented_internally_on_mailer/campaign_mail.mjml delete mode 100644 back/engines/free/email_campaigns/spec/mailers/internal_comment_on_initiative_you_commented_internally_on_mailer_spec.rb delete mode 100644 back/engines/free/email_campaigns/spec/mailers/previews/email_campaigns/internal_comment_on_initiative_you_commented_internally_on_mailer_preview.rb delete mode 100644 back/engines/free/email_campaigns/spec/models/email_campaigns/campaigns/internal_comment_on_initiative_you_commented_internally_on_spec.rb delete mode 100644 back/spec/models/notifications/internal_comments/internal_comment_on_initiative_you_commented_internally_on_spec.rb delete mode 100644 back/spec/models/notifications/internal_comments/internal_comment_on_unassigned_initiative_spec.rb diff --git a/back/app/models/notifications/internal_comments/internal_comment_on_initiative_you_commented_internally_on.rb b/back/app/models/notifications/internal_comments/internal_comment_on_initiative_you_commented_internally_on.rb deleted file mode 100644 index 21647cfe14b8..000000000000 --- a/back/app/models/notifications/internal_comments/internal_comment_on_initiative_you_commented_internally_on.rb +++ /dev/null @@ -1,82 +0,0 @@ -# frozen_string_literal: true - -# == Schema Information -# -# Table name: notifications -# -# id :uuid not null, primary key -# type :string -# read_at :datetime -# recipient_id :uuid -# post_id :uuid -# comment_id :uuid -# project_id :uuid -# created_at :datetime not null -# updated_at :datetime not null -# initiating_user_id :uuid -# spam_report_id :uuid -# invite_id :uuid -# reason_code :string -# other_reason :string -# post_status_id :uuid -# official_feedback_id :uuid -# phase_id :uuid -# post_type :string -# post_status_type :string -# project_folder_id :uuid -# inappropriate_content_flag_id :uuid -# internal_comment_id :uuid -# basket_id :uuid -# cosponsors_initiative_id :uuid -# cosponsorship_id :uuid -# project_review_id :uuid -# -# Indexes -# -# index_notifications_on_basket_id (basket_id) -# index_notifications_on_cosponsors_initiative_id (cosponsors_initiative_id) -# index_notifications_on_cosponsorship_id (cosponsorship_id) -# index_notifications_on_created_at (created_at) -# index_notifications_on_inappropriate_content_flag_id (inappropriate_content_flag_id) -# index_notifications_on_initiating_user_id (initiating_user_id) -# index_notifications_on_internal_comment_id (internal_comment_id) -# index_notifications_on_invite_id (invite_id) -# index_notifications_on_official_feedback_id (official_feedback_id) -# index_notifications_on_phase_id (phase_id) -# index_notifications_on_post_id_and_post_type (post_id,post_type) -# index_notifications_on_post_status_id (post_status_id) -# index_notifications_on_post_status_id_and_post_status_type (post_status_id,post_status_type) -# index_notifications_on_project_review_id (project_review_id) -# index_notifications_on_recipient_id (recipient_id) -# index_notifications_on_recipient_id_and_read_at (recipient_id,read_at) -# index_notifications_on_spam_report_id (spam_report_id) -# -# Foreign Keys -# -# fk_rails_... (basket_id => baskets.id) -# fk_rails_... (comment_id => comments.id) -# fk_rails_... (cosponsors_initiative_id => cosponsors_initiatives.id) -# fk_rails_... (cosponsorship_id => cosponsorships.id) -# fk_rails_... (inappropriate_content_flag_id => flag_inappropriate_content_inappropriate_content_flags.id) -# fk_rails_... (initiating_user_id => users.id) -# fk_rails_... (internal_comment_id => internal_comments.id) -# fk_rails_... (invite_id => invites.id) -# fk_rails_... (official_feedback_id => official_feedbacks.id) -# fk_rails_... (phase_id => phases.id) -# fk_rails_... (project_id => projects.id) -# fk_rails_... (project_review_id => project_reviews.id) -# fk_rails_... (recipient_id => users.id) -# fk_rails_... (spam_report_id => spam_reports.id) -# -module Notifications - class InternalComments::InternalCommentOnInitiativeYouCommentedInternallyOn < Notification - validates :initiating_user, :internal_comment, :post, presence: true - - ACTIVITY_TRIGGERS = { 'InternalComment' => { 'created' => true } } - EVENT_NAME = 'Internal comment on initiative you commented internally on' - - def self.make_notifications_on(activity) - InternalComments::InternalCommentOnInitiativeYouCommentedInternallyOnBuilder.new(activity).build_notifications - end - end -end diff --git a/back/app/serializers/web_api/v1/notifications/internal_comment_on_initiative_you_commented_internally_on_serializer.rb b/back/app/serializers/web_api/v1/notifications/internal_comment_on_initiative_you_commented_internally_on_serializer.rb deleted file mode 100644 index 870840dc0666..000000000000 --- a/back/app/serializers/web_api/v1/notifications/internal_comment_on_initiative_you_commented_internally_on_serializer.rb +++ /dev/null @@ -1,4 +0,0 @@ -# frozen_string_literal: true - -class WebApi::V1::Notifications::InternalCommentOnInitiativeYouCommentedInternallyOnSerializer < WebApi::V1::Notifications::InternalCommentNotificationSerializer -end diff --git a/back/app/services/notification_service.rb b/back/app/services/notification_service.rb index 5833a50d0267..75fc28945252 100644 --- a/back/app/services/notification_service.rb +++ b/back/app/services/notification_service.rb @@ -12,7 +12,6 @@ class NotificationService Notifications::InternalComments::InternalCommentOnIdeaAssignedToYou, Notifications::InternalComments::InternalCommentOnIdeaYouCommentedInternallyOn, Notifications::InternalComments::InternalCommentOnIdeaYouModerate, - Notifications::InternalComments::InternalCommentOnInitiativeYouCommentedInternallyOn, Notifications::InternalComments::InternalCommentOnUnassignedInitiative, Notifications::InternalComments::InternalCommentOnUnassignedUnmoderatedIdea, Notifications::InternalComments::InternalCommentOnYourInternalComment, diff --git a/back/app/services/notifications/internal_comments/internal_comment_on_initiative_you_commented_internally_on_builder.rb b/back/app/services/notifications/internal_comments/internal_comment_on_initiative_you_commented_internally_on_builder.rb deleted file mode 100644 index 3c71e349d370..000000000000 --- a/back/app/services/notifications/internal_comments/internal_comment_on_initiative_you_commented_internally_on_builder.rb +++ /dev/null @@ -1,27 +0,0 @@ -# frozen_string_literal: true - -module Notifications - class InternalComments::InternalCommentOnInitiativeYouCommentedInternallyOnBuilder < InternalComments::BaseNotificationBuilder - protected - - def skip_recipient?(recipient) - initiator_id.nil? || - initiator_id == recipient.id || - recipient.id == parent_author_id || - recipient.id == assignee_id || - MentionService.new.user_mentioned?(internal_comment.body, recipient) - end - - def recipients - InternalComment.where(post_id: internal_comment.post_id).map(&:author).uniq - end - - def preconditions_met? - post_type == 'Initiative' - end - - def notification_class - Notifications::InternalComments::InternalCommentOnInitiativeYouCommentedInternallyOn - end - end -end diff --git a/back/engines/free/email_campaigns/app/mailers/email_campaigns/internal_comment_on_initiative_you_commented_internally_on_mailer.rb b/back/engines/free/email_campaigns/app/mailers/email_campaigns/internal_comment_on_initiative_you_commented_internally_on_mailer.rb deleted file mode 100644 index 433d8d883d52..000000000000 --- a/back/engines/free/email_campaigns/app/mailers/email_campaigns/internal_comment_on_initiative_you_commented_internally_on_mailer.rb +++ /dev/null @@ -1,6 +0,0 @@ -# frozen_string_literal: true - -module EmailCampaigns - class InternalCommentOnInitiativeYouCommentedInternallyOnMailer < BaseInternalCommentMailer - end -end diff --git a/back/engines/free/email_campaigns/app/models/email_campaigns/campaigns/internal_comment_on_initiative_you_commented_internally_on.rb b/back/engines/free/email_campaigns/app/models/email_campaigns/campaigns/internal_comment_on_initiative_you_commented_internally_on.rb deleted file mode 100644 index 311c18a46a17..000000000000 --- a/back/engines/free/email_campaigns/app/models/email_campaigns/campaigns/internal_comment_on_initiative_you_commented_internally_on.rb +++ /dev/null @@ -1,79 +0,0 @@ -# frozen_string_literal: true - -# == Schema Information -# -# Table name: email_campaigns_campaigns -# -# id :uuid not null, primary key -# type :string not null -# author_id :uuid -# enabled :boolean -# sender :string -# reply_to :string -# schedule :jsonb -# subject_multiloc :jsonb -# body_multiloc :jsonb -# created_at :datetime not null -# updated_at :datetime not null -# deliveries_count :integer default(0), not null -# context_id :uuid -# -# Indexes -# -# index_email_campaigns_campaigns_on_author_id (author_id) -# index_email_campaigns_campaigns_on_context_id (context_id) -# index_email_campaigns_campaigns_on_type (type) -# -# Foreign Keys -# -# fk_rails_... (author_id => users.id) -# -module EmailCampaigns - class Campaigns::InternalCommentOnInitiativeYouCommentedInternallyOn < Campaign - include Consentable - include ActivityTriggerable - include RecipientConfigurable - include Disableable - include LifecycleStageRestrictable - include Trackable - allow_lifecycle_stages only: %w[trial active] - - recipient_filter :filter_notification_recipient - - def activity_triggers - { 'Notifications::InternalComments::InternalCommentOnInitiativeYouCommentedInternallyOn' => { 'created' => true } } - end - - def filter_notification_recipient(users_scope, activity:, time: nil) - users_scope.where(id: activity.item.recipient.id) - end - - def self.consentable_roles - %w[admin] - end - - def self.recipient_role_multiloc_key - 'email_campaigns.admin_labels.recipient_role.admins' - end - - def self.recipient_segment_multiloc_key - 'email_campaigns.admin_labels.recipient_segment.admins' - end - - def self.content_type_multiloc_key - 'email_campaigns.admin_labels.content_type.internal_comments' - end - - def self.trigger_multiloc_key - 'email_campaigns.admin_labels.trigger.internal_comment_is_posted_on_initiative_user_has_commented_internally_on' - end - - def mailer_class - InternalCommentOnInitiativeYouCommentedInternallyOnMailer - end - - def generate_commands(recipient:, activity:, time: nil) - InternalCommentCampaignCommandsBuilder.new.build_commands(recipient, activity) - end - end -end diff --git a/back/engines/free/email_campaigns/app/services/email_campaigns/delivery_service.rb b/back/engines/free/email_campaigns/app/services/email_campaigns/delivery_service.rb index f0e0b86ff154..91122d0d2b58 100644 --- a/back/engines/free/email_campaigns/app/services/email_campaigns/delivery_service.rb +++ b/back/engines/free/email_campaigns/app/services/email_campaigns/delivery_service.rb @@ -17,7 +17,6 @@ class DeliveryService Campaigns::InternalCommentOnIdeaAssignedToYou, Campaigns::InternalCommentOnIdeaYouCommentedInternallyOn, Campaigns::InternalCommentOnIdeaYouModerate, - Campaigns::InternalCommentOnInitiativeYouCommentedInternallyOn, Campaigns::InternalCommentOnUnassignedInitiative, Campaigns::InternalCommentOnUnassignedUnmoderatedIdea, Campaigns::InternalCommentOnYourInternalComment, diff --git a/back/engines/free/email_campaigns/app/views/email_campaigns/internal_comment_on_initiative_you_commented_internally_on_mailer/campaign_mail.mjml b/back/engines/free/email_campaigns/app/views/email_campaigns/internal_comment_on_initiative_you_commented_internally_on_mailer/campaign_mail.mjml deleted file mode 100644 index 328c41c16b0a..000000000000 --- a/back/engines/free/email_campaigns/app/views/email_campaigns/internal_comment_on_initiative_you_commented_internally_on_mailer/campaign_mail.mjml +++ /dev/null @@ -1 +0,0 @@ -<%= render 'email_campaigns/internal_comments/internal_comment_mail', event: event %> diff --git a/back/engines/free/email_campaigns/spec/factories/campaigns.rb b/back/engines/free/email_campaigns/spec/factories/campaigns.rb index 33ebb1388368..6b71c467c45d 100644 --- a/back/engines/free/email_campaigns/spec/factories/campaigns.rb +++ b/back/engines/free/email_campaigns/spec/factories/campaigns.rb @@ -78,10 +78,6 @@ enabled { true } end - factory :internal_comment_on_initiative_you_commented_internally_on_campaign, class: EmailCampaigns::Campaigns::InternalCommentOnInitiativeYouCommentedInternallyOn do - enabled { true } - end - factory :internal_comment_on_unassigned_initiative_campaign, class: EmailCampaigns::Campaigns::InternalCommentOnUnassignedInitiative do enabled { true } end diff --git a/back/engines/free/email_campaigns/spec/mailers/internal_comment_on_initiative_you_commented_internally_on_mailer_spec.rb b/back/engines/free/email_campaigns/spec/mailers/internal_comment_on_initiative_you_commented_internally_on_mailer_spec.rb deleted file mode 100644 index 019256ecf14a..000000000000 --- a/back/engines/free/email_campaigns/spec/mailers/internal_comment_on_initiative_you_commented_internally_on_mailer_spec.rb +++ /dev/null @@ -1,36 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe EmailCampaigns::InternalCommentOnInitiativeYouCommentedInternallyOnMailer do - describe 'InternalCommentOnInitiativeYouCommentedInternallyOnMailer' do - let_it_be(:recipient) { create(:user, locale: 'en') } - let_it_be(:post_image) { create(:idea_image) } - let_it_be(:campaign) { EmailCampaigns::Campaigns::InternalCommentOnInitiativeYouCommentedInternallyOn.create! } - let_it_be(:token) { ResetPasswordService.new.generate_reset_password_token recipient } - let_it_be(:command) do - { - recipient: recipient, - event_payload: { - initiating_user_first_name: 'Matthias', - initiating_user_last_name: 'Geeke', - internal_comment_author_name: 'Matthias Geeke', - internal_comment_body: 'I also think this proposal is amazing! Wowzers!', - internal_comment_url: 'http://localhost:3000/en/internal_comments/fake-url-comment-does-not-exist', - post_title_multiloc: { en: 'Permit paving of front gardens' }, - post_body_multiloc: { - en: 'There are many advantages to paving your front garden. Less cars on the road and more space for pedestrians.' - }, - post_type: 'Initiative', - post_image_medium_url: post_image.image.versions[:medium].url - } - } - end - - let_it_be(:mail) { described_class.with(command: command, campaign: campaign).campaign_mail.deliver_now } - - before_all { EmailCampaigns::UnsubscriptionToken.create!(user_id: recipient.id) } - - include_examples 'internal_comment_campaign_mailer_examples' - end -end diff --git a/back/engines/free/email_campaigns/spec/mailers/previews/email_campaigns/internal_comment_on_initiative_you_commented_internally_on_mailer_preview.rb b/back/engines/free/email_campaigns/spec/mailers/previews/email_campaigns/internal_comment_on_initiative_you_commented_internally_on_mailer_preview.rb deleted file mode 100644 index e74f00c7d510..000000000000 --- a/back/engines/free/email_campaigns/spec/mailers/previews/email_campaigns/internal_comment_on_initiative_you_commented_internally_on_mailer_preview.rb +++ /dev/null @@ -1,31 +0,0 @@ -# frozen_string_literal: true - -module EmailCampaigns - class InternalCommentOnInitiativeYouCommentedInternallyOnMailerPreview < ActionMailer::Preview - include EmailCampaigns::MailerPreviewRecipient - - def campaign_mail - # TODO: generate commands with campaign#generate_commands method - command = { - recipient: recipient_user, - event_payload: { - initiating_user_first_name: 'Matthias', - initiating_user_last_name: 'Geeke', - internal_comment_author_name: 'Matthias Geeke', - internal_comment_body: 'I also think this proposal is amazing!', - internal_comment_url: 'http://localhost:3000/en/internal_comments/fake-url-comment-does-not-exist', - post_title_multiloc: { en: 'Permit paving of front gardens' }, - post_body_multiloc: { - en: 'There are many advantages to paving your front garden. Less cars on the road and more space for pedestrians.' - }, - post_type: 'Initiative', - post_image_medium_url: InitiativeImage.first.image.versions[:medium].url - } - } - - campaign = EmailCampaigns::Campaigns::InternalCommentOnInitiativeYouCommentedInternallyOn.first - - campaign.mailer_class.with(campaign: campaign, command: command).campaign_mail - end - end -end diff --git a/back/engines/free/email_campaigns/spec/models/email_campaigns/campaigns/internal_comment_on_initiative_you_commented_internally_on_spec.rb b/back/engines/free/email_campaigns/spec/models/email_campaigns/campaigns/internal_comment_on_initiative_you_commented_internally_on_spec.rb deleted file mode 100644 index 2db0cb4c3bd2..000000000000 --- a/back/engines/free/email_campaigns/spec/models/email_campaigns/campaigns/internal_comment_on_initiative_you_commented_internally_on_spec.rb +++ /dev/null @@ -1,19 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe EmailCampaigns::Campaigns::InternalCommentOnInitiativeYouCommentedInternallyOn do - describe 'InternalCommentOnInitiativeYouCommentedInternallyOn Campaign default factory' do - it 'is valid' do - expect(build(:internal_comment_on_initiative_you_commented_internally_on_campaign)).to be_valid - end - end - - describe '#generate_commands' do - let(:campaign) { create(:internal_comment_on_initiative_you_commented_internally_on_campaign) } - let(:notification) { create(:internal_comment_on_initiative_you_commented_internally_on) } - let!(:post_image) { create(:initiative_image, initiative: notification.post) } - - include_examples 'internal_comment_campaign_generate_commands' - end -end diff --git a/back/spec/factories/notifications.rb b/back/spec/factories/notifications.rb index d8f2a7dea065..6bbac7cb3571 100644 --- a/back/spec/factories/notifications.rb +++ b/back/spec/factories/notifications.rb @@ -61,14 +61,6 @@ association :post, factory: :idea end - factory :internal_comment_on_initiative_you_commented_internally_on, - parent: :notification, - class: 'Notifications::InternalComments::InternalCommentOnInitiativeYouCommentedInternallyOn' do - initiating_user - internal_comment - association :post, factory: :initiative - end - factory :internal_comment_on_unassigned_initiative, parent: :notification, class: 'Notifications::InternalComments::InternalCommentOnUnassignedInitiative' do diff --git a/back/spec/models/notifications/internal_comments/internal_comment_on_initiative_you_commented_internally_on_spec.rb b/back/spec/models/notifications/internal_comments/internal_comment_on_initiative_you_commented_internally_on_spec.rb deleted file mode 100644 index b0fd5026fe0f..000000000000 --- a/back/spec/models/notifications/internal_comments/internal_comment_on_initiative_you_commented_internally_on_spec.rb +++ /dev/null @@ -1,88 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe Notifications::InternalComments::InternalCommentOnInitiativeYouCommentedInternallyOn do - describe 'make_notifications_on' do - let(:initiative) { create(:initiative) } - let!(:commenter) { create(:admin) } - let!(:_internal_comment2) { create(:internal_comment, post: initiative, author: commenter) } - let(:internal_comment) { create(:internal_comment, post: initiative) } - - context 'when admin has internally commented on initiative and should receive this notification' do - let(:activity) { create(:activity, item: internal_comment, action: 'created') } - - it 'makes a notification on created internal comment activity' do - notifications = described_class.make_notifications_on activity - expect(notifications.first).to have_attributes( - recipient_id: commenter.id, - initiating_user_id: internal_comment.author_id, - internal_comment_id: internal_comment.id, - post_id: initiative.id, - post_type: 'Initiative', - project_id: nil - ) - end - end - - context 'when recipient is moderator and should receive this notification' do - let(:initiative2) { create(:initiative) } - let!(:project_moderator) { create(:project_moderator) } - let(:internal_comment) { create(:internal_comment, post: initiative2) } - let!(:_internal_comment2) { create(:internal_comment, post: initiative2, author: project_moderator) } - let(:activity) { create(:activity, item: internal_comment, action: 'created') } - - it 'makes a notification on created internal comment activity' do - notifications = described_class.make_notifications_on activity - expect(notifications.first).to have_attributes( - recipient_id: project_moderator.id, - initiating_user_id: internal_comment.author_id, - internal_comment_id: internal_comment.id, - post_id: initiative2.id, - post_type: 'Initiative', - project_id: nil - ) - end - end - - shared_examples 'no notification created' do - it 'does not make a notification on created internal comment activity' do - notifications_count = described_class.count - activity = create(:activity, item: internal_comment, action: 'created') - notifications = described_class.make_notifications_on activity - - expect(notifications.count).to eq notifications_count - end - end - - context 'when the recipient is the internal comment author' do - let(:internal_comment) { create(:internal_comment, author: commenter, post: initiative) } - - it_behaves_like 'no notification created' - end - - context "when the internal comment is a comment on the recipient's internal comment" do - let(:parent_internal_comment) { create(:internal_comment, post: initiative, author: commenter) } - let(:internal_comment) { create(:internal_comment, parent: parent_internal_comment, post: initiative) } - - # Don't create this notification if the Activity (internal comment created) - # should lead to a InternalCommentOnYourInternalComment notification to the recipient. - it_behaves_like 'no notification created' - end - - context 'when the internal comment contains a mention of the recipient' do - let(:internal_comment) do - create( - :internal_comment, - :with_mentions, - mentioned_users: [commenter], - post: initiative - ) - end - - # Don't create this notification if the Activity (internal comment created) - # should lead to a MentionInInternalComment notification to the recipient. - it_behaves_like 'no notification created' - end - end -end diff --git a/back/spec/models/notifications/internal_comments/internal_comment_on_unassigned_initiative_spec.rb b/back/spec/models/notifications/internal_comments/internal_comment_on_unassigned_initiative_spec.rb deleted file mode 100644 index fd327838a428..000000000000 --- a/back/spec/models/notifications/internal_comments/internal_comment_on_unassigned_initiative_spec.rb +++ /dev/null @@ -1,79 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe Notifications::InternalComments::InternalCommentOnUnassignedInitiative do - describe 'make_notifications_on' do - let(:initiative) { create(:initiative) } - let!(:admin) { create(:admin) } - let(:internal_comment) { create(:internal_comment, post: initiative) } - - context 'when an admin should receive this notification' do - let(:activity) { create(:activity, item: internal_comment, action: 'created') } - - it 'makes a notification on created internal comment activity' do - notifications = described_class.make_notifications_on activity - expect(notifications.first).to have_attributes( - recipient_id: admin.id, - initiating_user_id: internal_comment.author_id, - post_id: initiative.id, - internal_comment_id: internal_comment.id - ) - end - end - - shared_examples 'no notification created' do - it 'does not make a notification on created internal comment activity' do - notifications_count = described_class.count - activity = create(:activity, item: internal_comment, action: 'created') - notifications = described_class.make_notifications_on activity - - expect(notifications.count).to eq notifications_count - end - end - - context 'when the recipient is the internal comment author' do - let(:internal_comment) { create(:internal_comment, author: admin, post: initiative) } - - it_behaves_like 'no notification created' - end - - context 'when someone is assigned to the initiative' do - let(:initiative) { create(:initiative, assignee: create(:admin)) } - - it_behaves_like 'no notification created' - end - - context "when the internal comment is a comment on the recipient's internal comment" do - let(:parent_internal_comment) { create(:internal_comment, post: initiative, author: admin) } - let(:internal_comment) { create(:internal_comment, parent: parent_internal_comment, post: initiative) } - - # Don't create this notification if the Activity (internal comment created) - # should lead to a InternalCommentOnYourInternalComment notification to the recipient. - it_behaves_like 'no notification created' - end - - context 'when the internal comment contains a mention of the recipient' do - let(:internal_comment) do - create( - :internal_comment, - :with_mentions, - mentioned_users: [admin], - post: initiative - ) - end - - # Don't create this notification if the Activity (internal comment created) - # should lead to a MentionInInternalComment notification to the recipient. - it_behaves_like 'no notification created' - end - - context 'when the recipient has already commented internally on the initiative' do - let!(:_other_internal_comment) { create(:internal_comment, post: initiative, author: admin) } - - # Don't create this notification if the Activity (internal comment created) - # should lead to a InternalCommentOnInitiativeYouCommentedInternallyOn notification to the recipient. - it_behaves_like 'no notification created' - end - end -end diff --git a/back/spec/models/notifications/internal_comments/internal_comment_on_unassigned_unmoderated_idea_spec.rb b/back/spec/models/notifications/internal_comments/internal_comment_on_unassigned_unmoderated_idea_spec.rb index 97ba9401f488..b9507ebb54fc 100644 --- a/back/spec/models/notifications/internal_comments/internal_comment_on_unassigned_unmoderated_idea_spec.rb +++ b/back/spec/models/notifications/internal_comments/internal_comment_on_unassigned_unmoderated_idea_spec.rb @@ -87,7 +87,7 @@ let!(:_other_internal_comment) { create(:internal_comment, post: idea, author: admin) } # Don't create this notification if the Activity (internal comment created) - # should lead to a InternalCommentOnInitiativeYouCommentedInternallyOn notification to the recipient. + # should lead to a InternalCommentOnIdeaYouCommentedInternallyOn notification to the recipient. it_behaves_like 'no notification created' end end From 3346e5e347c656067fc76a8147a86e144b4d35d0 Mon Sep 17 00:00:00 2001 From: Simon Tharby Date: Wed, 18 Dec 2024 13:55:24 +0000 Subject: [PATCH 47/63] [TAN-2538] Remove InternalCommentOnUnassignedInitiative notification & campaign files & specs --- ...ternal_comment_on_unassigned_initiative.rb | 82 ------------------- ...ent_on_unassigned_initiative_serializer.rb | 4 - back/app/services/notification_service.rb | 1 - ...omment_on_unassigned_initiative_builder.rb | 29 ------- ...comment_on_unassigned_initiative_mailer.rb | 6 -- ...ternal_comment_on_unassigned_initiative.rb | 79 ------------------ .../email_campaigns/delivery_service.rb | 1 - .../campaign_mail.mjml | 1 - .../spec/factories/campaigns.rb | 4 - ...nt_on_unassigned_initiative_mailer_spec.rb | 36 -------- ...on_unassigned_initiative_mailer_preview.rb | 31 ------- ...l_comment_on_unassigned_initiative_spec.rb | 19 ----- back/spec/factories/notifications.rb | 8 -- 13 files changed, 301 deletions(-) delete mode 100644 back/app/models/notifications/internal_comments/internal_comment_on_unassigned_initiative.rb delete mode 100644 back/app/serializers/web_api/v1/notifications/internal_comment_on_unassigned_initiative_serializer.rb delete mode 100644 back/app/services/notifications/internal_comments/internal_comment_on_unassigned_initiative_builder.rb delete mode 100644 back/engines/free/email_campaigns/app/mailers/email_campaigns/internal_comment_on_unassigned_initiative_mailer.rb delete mode 100644 back/engines/free/email_campaigns/app/models/email_campaigns/campaigns/internal_comment_on_unassigned_initiative.rb delete mode 100644 back/engines/free/email_campaigns/app/views/email_campaigns/internal_comment_on_unassigned_initiative_mailer/campaign_mail.mjml delete mode 100644 back/engines/free/email_campaigns/spec/mailers/internal_comment_on_unassigned_initiative_mailer_spec.rb delete mode 100644 back/engines/free/email_campaigns/spec/mailers/previews/email_campaigns/internal_comment_on_unassigned_initiative_mailer_preview.rb delete mode 100644 back/engines/free/email_campaigns/spec/models/email_campaigns/campaigns/internal_comment_on_unassigned_initiative_spec.rb diff --git a/back/app/models/notifications/internal_comments/internal_comment_on_unassigned_initiative.rb b/back/app/models/notifications/internal_comments/internal_comment_on_unassigned_initiative.rb deleted file mode 100644 index dec2b97b3c8b..000000000000 --- a/back/app/models/notifications/internal_comments/internal_comment_on_unassigned_initiative.rb +++ /dev/null @@ -1,82 +0,0 @@ -# frozen_string_literal: true - -# == Schema Information -# -# Table name: notifications -# -# id :uuid not null, primary key -# type :string -# read_at :datetime -# recipient_id :uuid -# post_id :uuid -# comment_id :uuid -# project_id :uuid -# created_at :datetime not null -# updated_at :datetime not null -# initiating_user_id :uuid -# spam_report_id :uuid -# invite_id :uuid -# reason_code :string -# other_reason :string -# post_status_id :uuid -# official_feedback_id :uuid -# phase_id :uuid -# post_type :string -# post_status_type :string -# project_folder_id :uuid -# inappropriate_content_flag_id :uuid -# internal_comment_id :uuid -# basket_id :uuid -# cosponsors_initiative_id :uuid -# cosponsorship_id :uuid -# project_review_id :uuid -# -# Indexes -# -# index_notifications_on_basket_id (basket_id) -# index_notifications_on_cosponsors_initiative_id (cosponsors_initiative_id) -# index_notifications_on_cosponsorship_id (cosponsorship_id) -# index_notifications_on_created_at (created_at) -# index_notifications_on_inappropriate_content_flag_id (inappropriate_content_flag_id) -# index_notifications_on_initiating_user_id (initiating_user_id) -# index_notifications_on_internal_comment_id (internal_comment_id) -# index_notifications_on_invite_id (invite_id) -# index_notifications_on_official_feedback_id (official_feedback_id) -# index_notifications_on_phase_id (phase_id) -# index_notifications_on_post_id_and_post_type (post_id,post_type) -# index_notifications_on_post_status_id (post_status_id) -# index_notifications_on_post_status_id_and_post_status_type (post_status_id,post_status_type) -# index_notifications_on_project_review_id (project_review_id) -# index_notifications_on_recipient_id (recipient_id) -# index_notifications_on_recipient_id_and_read_at (recipient_id,read_at) -# index_notifications_on_spam_report_id (spam_report_id) -# -# Foreign Keys -# -# fk_rails_... (basket_id => baskets.id) -# fk_rails_... (comment_id => comments.id) -# fk_rails_... (cosponsors_initiative_id => cosponsors_initiatives.id) -# fk_rails_... (cosponsorship_id => cosponsorships.id) -# fk_rails_... (inappropriate_content_flag_id => flag_inappropriate_content_inappropriate_content_flags.id) -# fk_rails_... (initiating_user_id => users.id) -# fk_rails_... (internal_comment_id => internal_comments.id) -# fk_rails_... (invite_id => invites.id) -# fk_rails_... (official_feedback_id => official_feedbacks.id) -# fk_rails_... (phase_id => phases.id) -# fk_rails_... (project_id => projects.id) -# fk_rails_... (project_review_id => project_reviews.id) -# fk_rails_... (recipient_id => users.id) -# fk_rails_... (spam_report_id => spam_reports.id) -# -module Notifications - class InternalComments::InternalCommentOnUnassignedInitiative < Notification - validates :initiating_user, :internal_comment, :post, presence: true - - ACTIVITY_TRIGGERS = { 'InternalComment' => { 'created' => true } } - EVENT_NAME = 'Internal comment on unassigned initiative' - - def self.make_notifications_on(activity) - InternalComments::InternalCommentOnUnassignedInitiativeBuilder.new(activity).build_notifications - end - end -end diff --git a/back/app/serializers/web_api/v1/notifications/internal_comment_on_unassigned_initiative_serializer.rb b/back/app/serializers/web_api/v1/notifications/internal_comment_on_unassigned_initiative_serializer.rb deleted file mode 100644 index 76470100b128..000000000000 --- a/back/app/serializers/web_api/v1/notifications/internal_comment_on_unassigned_initiative_serializer.rb +++ /dev/null @@ -1,4 +0,0 @@ -# frozen_string_literal: true - -class WebApi::V1::Notifications::InternalCommentOnUnassignedInitiativeSerializer < WebApi::V1::Notifications::InternalCommentNotificationSerializer -end diff --git a/back/app/services/notification_service.rb b/back/app/services/notification_service.rb index 75fc28945252..e18b380e84ad 100644 --- a/back/app/services/notification_service.rb +++ b/back/app/services/notification_service.rb @@ -12,7 +12,6 @@ class NotificationService Notifications::InternalComments::InternalCommentOnIdeaAssignedToYou, Notifications::InternalComments::InternalCommentOnIdeaYouCommentedInternallyOn, Notifications::InternalComments::InternalCommentOnIdeaYouModerate, - Notifications::InternalComments::InternalCommentOnUnassignedInitiative, Notifications::InternalComments::InternalCommentOnUnassignedUnmoderatedIdea, Notifications::InternalComments::InternalCommentOnYourInternalComment, Notifications::InternalComments::MentionInInternalComment, diff --git a/back/app/services/notifications/internal_comments/internal_comment_on_unassigned_initiative_builder.rb b/back/app/services/notifications/internal_comments/internal_comment_on_unassigned_initiative_builder.rb deleted file mode 100644 index 3a4f84233efb..000000000000 --- a/back/app/services/notifications/internal_comments/internal_comment_on_unassigned_initiative_builder.rb +++ /dev/null @@ -1,29 +0,0 @@ -# frozen_string_literal: true - -module Notifications - class InternalComments::InternalCommentOnUnassignedInitiativeBuilder < InternalComments::BaseNotificationBuilder - protected - - def skip_recipient?(recipient) - initiator_id.nil? || - initiator_id == recipient.id || - recipient.id == parent_author_id || - recipient.id == assignee_id || - MentionService.new.user_mentioned?(internal_comment.body, recipient) - end - - def recipients - commenters = InternalComment.where(post_id: internal_comment.post_id).map(&:author).uniq - - User.admin - commenters - end - - def preconditions_met? - post_type == 'Initiative' && assignee_id.nil? - end - - def notification_class - Notifications::InternalComments::InternalCommentOnUnassignedInitiative - end - end -end diff --git a/back/engines/free/email_campaigns/app/mailers/email_campaigns/internal_comment_on_unassigned_initiative_mailer.rb b/back/engines/free/email_campaigns/app/mailers/email_campaigns/internal_comment_on_unassigned_initiative_mailer.rb deleted file mode 100644 index 6e193e272dac..000000000000 --- a/back/engines/free/email_campaigns/app/mailers/email_campaigns/internal_comment_on_unassigned_initiative_mailer.rb +++ /dev/null @@ -1,6 +0,0 @@ -# frozen_string_literal: true - -module EmailCampaigns - class InternalCommentOnUnassignedInitiativeMailer < BaseInternalCommentMailer - end -end diff --git a/back/engines/free/email_campaigns/app/models/email_campaigns/campaigns/internal_comment_on_unassigned_initiative.rb b/back/engines/free/email_campaigns/app/models/email_campaigns/campaigns/internal_comment_on_unassigned_initiative.rb deleted file mode 100644 index c52df10f5ffb..000000000000 --- a/back/engines/free/email_campaigns/app/models/email_campaigns/campaigns/internal_comment_on_unassigned_initiative.rb +++ /dev/null @@ -1,79 +0,0 @@ -# frozen_string_literal: true - -# == Schema Information -# -# Table name: email_campaigns_campaigns -# -# id :uuid not null, primary key -# type :string not null -# author_id :uuid -# enabled :boolean -# sender :string -# reply_to :string -# schedule :jsonb -# subject_multiloc :jsonb -# body_multiloc :jsonb -# created_at :datetime not null -# updated_at :datetime not null -# deliveries_count :integer default(0), not null -# context_id :uuid -# -# Indexes -# -# index_email_campaigns_campaigns_on_author_id (author_id) -# index_email_campaigns_campaigns_on_context_id (context_id) -# index_email_campaigns_campaigns_on_type (type) -# -# Foreign Keys -# -# fk_rails_... (author_id => users.id) -# -module EmailCampaigns - class Campaigns::InternalCommentOnUnassignedInitiative < Campaign - include Consentable - include ActivityTriggerable - include RecipientConfigurable - include Disableable - include LifecycleStageRestrictable - include Trackable - allow_lifecycle_stages only: %w[trial active] - - recipient_filter :filter_notification_recipient - - def activity_triggers - { 'Notifications::InternalComments::InternalCommentOnUnassignedInitiative' => { 'created' => true } } - end - - def filter_notification_recipient(users_scope, activity:, time: nil) - users_scope.where(id: activity.item.recipient.id) - end - - def self.consentable_roles - %w[admin] - end - - def self.recipient_role_multiloc_key - 'email_campaigns.admin_labels.recipient_role.admins' - end - - def self.recipient_segment_multiloc_key - 'email_campaigns.admin_labels.recipient_segment.admins' - end - - def self.content_type_multiloc_key - 'email_campaigns.admin_labels.content_type.internal_comments' - end - - def self.trigger_multiloc_key - 'email_campaigns.admin_labels.trigger.internal_comment_is_posted_on_unassigned_initiative' - end - - def mailer_class - InternalCommentOnUnassignedInitiativeMailer - end - - def generate_commands(recipient:, activity:, time: nil) - InternalCommentCampaignCommandsBuilder.new.build_commands(recipient, activity) - end - end -end diff --git a/back/engines/free/email_campaigns/app/services/email_campaigns/delivery_service.rb b/back/engines/free/email_campaigns/app/services/email_campaigns/delivery_service.rb index 91122d0d2b58..99fa26d1cfc1 100644 --- a/back/engines/free/email_campaigns/app/services/email_campaigns/delivery_service.rb +++ b/back/engines/free/email_campaigns/app/services/email_campaigns/delivery_service.rb @@ -17,7 +17,6 @@ class DeliveryService Campaigns::InternalCommentOnIdeaAssignedToYou, Campaigns::InternalCommentOnIdeaYouCommentedInternallyOn, Campaigns::InternalCommentOnIdeaYouModerate, - Campaigns::InternalCommentOnUnassignedInitiative, Campaigns::InternalCommentOnUnassignedUnmoderatedIdea, Campaigns::InternalCommentOnYourInternalComment, Campaigns::InvitationToCosponsorIdea, diff --git a/back/engines/free/email_campaigns/app/views/email_campaigns/internal_comment_on_unassigned_initiative_mailer/campaign_mail.mjml b/back/engines/free/email_campaigns/app/views/email_campaigns/internal_comment_on_unassigned_initiative_mailer/campaign_mail.mjml deleted file mode 100644 index 328c41c16b0a..000000000000 --- a/back/engines/free/email_campaigns/app/views/email_campaigns/internal_comment_on_unassigned_initiative_mailer/campaign_mail.mjml +++ /dev/null @@ -1 +0,0 @@ -<%= render 'email_campaigns/internal_comments/internal_comment_mail', event: event %> diff --git a/back/engines/free/email_campaigns/spec/factories/campaigns.rb b/back/engines/free/email_campaigns/spec/factories/campaigns.rb index 6b71c467c45d..71f0449a8076 100644 --- a/back/engines/free/email_campaigns/spec/factories/campaigns.rb +++ b/back/engines/free/email_campaigns/spec/factories/campaigns.rb @@ -78,10 +78,6 @@ enabled { true } end - factory :internal_comment_on_unassigned_initiative_campaign, class: EmailCampaigns::Campaigns::InternalCommentOnUnassignedInitiative do - enabled { true } - end - factory :internal_comment_on_unassigned_unmoderated_idea_campaign, class: EmailCampaigns::Campaigns::InternalCommentOnUnassignedUnmoderatedIdea do enabled { true } end diff --git a/back/engines/free/email_campaigns/spec/mailers/internal_comment_on_unassigned_initiative_mailer_spec.rb b/back/engines/free/email_campaigns/spec/mailers/internal_comment_on_unassigned_initiative_mailer_spec.rb deleted file mode 100644 index 06f9c022491d..000000000000 --- a/back/engines/free/email_campaigns/spec/mailers/internal_comment_on_unassigned_initiative_mailer_spec.rb +++ /dev/null @@ -1,36 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe EmailCampaigns::InternalCommentOnUnassignedInitiativeMailer do - describe 'InternalCommentOnUnassignedInitiativeMailer' do - let_it_be(:recipient) { create(:user, locale: 'en') } - let_it_be(:post_image) { create(:idea_image) } - let_it_be(:campaign) { EmailCampaigns::Campaigns::InternalCommentOnUnassignedInitiative.create! } - let_it_be(:token) { ResetPasswordService.new.generate_reset_password_token recipient } - let_it_be(:command) do - { - recipient: recipient, - event_payload: { - initiating_user_first_name: 'Matthias', - initiating_user_last_name: 'Geeke', - internal_comment_author_name: 'Matthias Geeke', - internal_comment_body: 'I think this proposal is amazing! Wowzers!', - internal_comment_url: 'http://localhost:3000/en/internal_comments/fake-url-comment-does-not-exist', - post_title_multiloc: { en: 'Permit paving of front gardens' }, - post_body_multiloc: { - en: 'There are many advantages to paving your front garden. Less cars on the road and more space for pedestrians.' - }, - post_type: 'Initiative', - post_image_medium_url: post_image.image.versions[:medium].url - } - } - end - - let_it_be(:mail) { described_class.with(command: command, campaign: campaign).campaign_mail.deliver_now } - - before_all { EmailCampaigns::UnsubscriptionToken.create!(user_id: recipient.id) } - - include_examples 'internal_comment_campaign_mailer_examples' - end -end diff --git a/back/engines/free/email_campaigns/spec/mailers/previews/email_campaigns/internal_comment_on_unassigned_initiative_mailer_preview.rb b/back/engines/free/email_campaigns/spec/mailers/previews/email_campaigns/internal_comment_on_unassigned_initiative_mailer_preview.rb deleted file mode 100644 index f0ec38d39870..000000000000 --- a/back/engines/free/email_campaigns/spec/mailers/previews/email_campaigns/internal_comment_on_unassigned_initiative_mailer_preview.rb +++ /dev/null @@ -1,31 +0,0 @@ -# frozen_string_literal: true - -module EmailCampaigns - class InternalCommentOnUnassignedInitiativeMailerPreview < ActionMailer::Preview - include EmailCampaigns::MailerPreviewRecipient - - def campaign_mail - # TODO: generate commands with campaign#generate_commands method - command = { - recipient: recipient_user, - event_payload: { - initiating_user_first_name: 'Matthias', - initiating_user_last_name: 'Geeke', - internal_comment_author_name: 'Matthias Geeke', - internal_comment_body: 'I think this proposal is amazing!', - internal_comment_url: 'http://localhost:3000/en/internal_comments/fake-url-comment-does-not-exist', - post_title_multiloc: { en: 'Permit paving of front gardens' }, - post_body_multiloc: { - en: 'There are many advantages to paving your front garden. Less cars on the road and more space for pedestrians.' - }, - post_type: 'Initiative', - post_image_medium_url: InitiativeImage.first.image.versions[:medium].url - } - } - - campaign = EmailCampaigns::Campaigns::InternalCommentOnUnassignedInitiative.first - - campaign.mailer_class.with(campaign: campaign, command: command).campaign_mail - end - end -end diff --git a/back/engines/free/email_campaigns/spec/models/email_campaigns/campaigns/internal_comment_on_unassigned_initiative_spec.rb b/back/engines/free/email_campaigns/spec/models/email_campaigns/campaigns/internal_comment_on_unassigned_initiative_spec.rb deleted file mode 100644 index 78d58f0f4aa8..000000000000 --- a/back/engines/free/email_campaigns/spec/models/email_campaigns/campaigns/internal_comment_on_unassigned_initiative_spec.rb +++ /dev/null @@ -1,19 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe EmailCampaigns::Campaigns::InternalCommentOnUnassignedInitiative do - describe 'InternalCommentOnUnassignedInitiative Campaign default factory' do - it 'is valid' do - expect(build(:internal_comment_on_unassigned_initiative_campaign)).to be_valid - end - end - - describe '#generate_commands' do - let(:campaign) { create(:internal_comment_on_unassigned_initiative_campaign) } - let(:notification) { create(:internal_comment_on_unassigned_initiative) } - let!(:post_image) { create(:initiative_image, initiative: notification.post) } - - include_examples 'internal_comment_campaign_generate_commands' - end -end diff --git a/back/spec/factories/notifications.rb b/back/spec/factories/notifications.rb index 6bbac7cb3571..ba8350bc542f 100644 --- a/back/spec/factories/notifications.rb +++ b/back/spec/factories/notifications.rb @@ -61,14 +61,6 @@ association :post, factory: :idea end - factory :internal_comment_on_unassigned_initiative, - parent: :notification, - class: 'Notifications::InternalComments::InternalCommentOnUnassignedInitiative' do - initiating_user - internal_comment - association :post, factory: :initiative - end - factory :internal_comment_on_unassigned_unmoderated_idea, parent: :notification, class: 'Notifications::InternalComments::InternalCommentOnUnassignedUnmoderatedIdea' do From 7548317acbccc76a852cecb6fccb8516318c858e Mon Sep 17 00:00:00 2001 From: Simon Tharby Date: Wed, 18 Dec 2024 13:57:27 +0000 Subject: [PATCH 48/63] [TAN-2538] Remove InvitationToCosponsorInitiative notification & campaign files & specs --- .../invitation_to_cosponsor_initiative.rb | 96 ------------------- ...tion_to_cosponsor_initiative_serializer.rb | 24 ----- back/app/services/notification_service.rb | 1 - ...vitation_to_cosponsor_initiative_mailer.rb | 25 ----- .../invitation_to_cosponsor_initiative.rb | 93 ------------------ .../email_campaigns/delivery_service.rb | 1 - .../campaign_mail.mjml | 26 ----- .../spec/factories/campaigns.rb | 4 - ...ion_to_cosponsor_initiative_mailer_spec.rb | 45 --------- ..._to_cosponsor_initiative_mailer_preview.rb | 19 ---- back/spec/factories/notifications.rb | 6 -- ...invitation_to_cosponsor_initiative_spec.rb | 20 ---- 12 files changed, 360 deletions(-) delete mode 100644 back/app/models/notifications/invitation_to_cosponsor_initiative.rb delete mode 100644 back/app/serializers/web_api/v1/notifications/invitation_to_cosponsor_initiative_serializer.rb delete mode 100644 back/engines/free/email_campaigns/app/mailers/email_campaigns/invitation_to_cosponsor_initiative_mailer.rb delete mode 100644 back/engines/free/email_campaigns/app/models/email_campaigns/campaigns/invitation_to_cosponsor_initiative.rb delete mode 100644 back/engines/free/email_campaigns/app/views/email_campaigns/invitation_to_cosponsor_initiative_mailer/campaign_mail.mjml delete mode 100644 back/engines/free/email_campaigns/spec/mailers/invitation_to_cosponsor_initiative_mailer_spec.rb delete mode 100644 back/engines/free/email_campaigns/spec/mailers/previews/email_campaigns/invitation_to_cosponsor_initiative_mailer_preview.rb delete mode 100644 back/spec/models/notifications/invitation_to_cosponsor_initiative_spec.rb diff --git a/back/app/models/notifications/invitation_to_cosponsor_initiative.rb b/back/app/models/notifications/invitation_to_cosponsor_initiative.rb deleted file mode 100644 index e3e1be3e092f..000000000000 --- a/back/app/models/notifications/invitation_to_cosponsor_initiative.rb +++ /dev/null @@ -1,96 +0,0 @@ -# frozen_string_literal: true - -# == Schema Information -# -# Table name: notifications -# -# id :uuid not null, primary key -# type :string -# read_at :datetime -# recipient_id :uuid -# post_id :uuid -# comment_id :uuid -# project_id :uuid -# created_at :datetime not null -# updated_at :datetime not null -# initiating_user_id :uuid -# spam_report_id :uuid -# invite_id :uuid -# reason_code :string -# other_reason :string -# post_status_id :uuid -# official_feedback_id :uuid -# phase_id :uuid -# post_type :string -# post_status_type :string -# project_folder_id :uuid -# inappropriate_content_flag_id :uuid -# internal_comment_id :uuid -# basket_id :uuid -# cosponsors_initiative_id :uuid -# cosponsorship_id :uuid -# project_review_id :uuid -# -# Indexes -# -# index_notifications_on_basket_id (basket_id) -# index_notifications_on_cosponsors_initiative_id (cosponsors_initiative_id) -# index_notifications_on_cosponsorship_id (cosponsorship_id) -# index_notifications_on_created_at (created_at) -# index_notifications_on_inappropriate_content_flag_id (inappropriate_content_flag_id) -# index_notifications_on_initiating_user_id (initiating_user_id) -# index_notifications_on_internal_comment_id (internal_comment_id) -# index_notifications_on_invite_id (invite_id) -# index_notifications_on_official_feedback_id (official_feedback_id) -# index_notifications_on_phase_id (phase_id) -# index_notifications_on_post_id_and_post_type (post_id,post_type) -# index_notifications_on_post_status_id (post_status_id) -# index_notifications_on_post_status_id_and_post_status_type (post_status_id,post_status_type) -# index_notifications_on_project_review_id (project_review_id) -# index_notifications_on_recipient_id (recipient_id) -# index_notifications_on_recipient_id_and_read_at (recipient_id,read_at) -# index_notifications_on_spam_report_id (spam_report_id) -# -# Foreign Keys -# -# fk_rails_... (basket_id => baskets.id) -# fk_rails_... (comment_id => comments.id) -# fk_rails_... (cosponsors_initiative_id => cosponsors_initiatives.id) -# fk_rails_... (cosponsorship_id => cosponsorships.id) -# fk_rails_... (inappropriate_content_flag_id => flag_inappropriate_content_inappropriate_content_flags.id) -# fk_rails_... (initiating_user_id => users.id) -# fk_rails_... (internal_comment_id => internal_comments.id) -# fk_rails_... (invite_id => invites.id) -# fk_rails_... (official_feedback_id => official_feedbacks.id) -# fk_rails_... (phase_id => phases.id) -# fk_rails_... (project_id => projects.id) -# fk_rails_... (project_review_id => project_reviews.id) -# fk_rails_... (recipient_id => users.id) -# fk_rails_... (spam_report_id => spam_reports.id) -# -module Notifications - class InvitationToCosponsorInitiative < Notification - validates :initiating_user, :cosponsors_initiative, presence: true - - ACTIVITY_TRIGGERS = { 'CosponsorsInitiative' => { 'created' => true } } - EVENT_NAME = 'Invitation to cosponsor an initiative' - - def self.make_notifications_on(activity) - cosponsors_initiative = activity&.item - initiating_user = activity&.user - recipient_id = cosponsors_initiative&.user_id - initiative = cosponsors_initiative&.initiative - - if recipient_id && initiating_user && initiative - [new( - recipient_id: recipient_id, - initiating_user: initiating_user, - cosponsors_initiative: cosponsors_initiative, - post: initiative - )] - else - [] - end - end - end -end diff --git a/back/app/serializers/web_api/v1/notifications/invitation_to_cosponsor_initiative_serializer.rb b/back/app/serializers/web_api/v1/notifications/invitation_to_cosponsor_initiative_serializer.rb deleted file mode 100644 index 7eb8c49e16a7..000000000000 --- a/back/app/serializers/web_api/v1/notifications/invitation_to_cosponsor_initiative_serializer.rb +++ /dev/null @@ -1,24 +0,0 @@ -# frozen_string_literal: true - -class WebApi::V1::Notifications::InvitationToCosponsorInitiativeSerializer < WebApi::V1::Notifications::NotificationSerializer - attribute :initiating_user_first_name do |object| - object.initiating_user&.first_name - end - - attribute :initiating_user_last_name do |object, params| - name_service = UserDisplayNameService.new(AppConfiguration.instance, current_user(params)) - name_service.last_name!(object.initiating_user) - end - - attribute :initiating_user_slug do |object| - object.initiating_user&.slug - end - - attribute :post_title_multiloc do |object| - object.post&.title_multiloc - end - - attribute :post_slug do |object| - object.post&.slug - end -end diff --git a/back/app/services/notification_service.rb b/back/app/services/notification_service.rb index e18b380e84ad..3300a002d4d8 100644 --- a/back/app/services/notification_service.rb +++ b/back/app/services/notification_service.rb @@ -16,7 +16,6 @@ class NotificationService Notifications::InternalComments::InternalCommentOnYourInternalComment, Notifications::InternalComments::MentionInInternalComment, Notifications::InvitationToCosponsorIdea, - Notifications::InvitationToCosponsorInitiative, Notifications::InviteAccepted, Notifications::MentionInComment, Notifications::MentionInOfficialFeedback, diff --git a/back/engines/free/email_campaigns/app/mailers/email_campaigns/invitation_to_cosponsor_initiative_mailer.rb b/back/engines/free/email_campaigns/app/mailers/email_campaigns/invitation_to_cosponsor_initiative_mailer.rb deleted file mode 100644 index ec1ccb891097..000000000000 --- a/back/engines/free/email_campaigns/app/mailers/email_campaigns/invitation_to_cosponsor_initiative_mailer.rb +++ /dev/null @@ -1,25 +0,0 @@ -# frozen_string_literal: true - -module EmailCampaigns - class InvitationToCosponsorInitiativeMailer < ApplicationMailer - protected - - def subject - format_message('subject') - end - - def header_title - format_message('main_header') - end - - private - - def header_message - format_message('event_description_initiative', values: { authorName: event.post_author_name }) - end - - def preheader - format_message('preheader_initiative', values: { authorName: event.post_author_name }) - end - end -end diff --git a/back/engines/free/email_campaigns/app/models/email_campaigns/campaigns/invitation_to_cosponsor_initiative.rb b/back/engines/free/email_campaigns/app/models/email_campaigns/campaigns/invitation_to_cosponsor_initiative.rb deleted file mode 100644 index c3a94bbb91d7..000000000000 --- a/back/engines/free/email_campaigns/app/models/email_campaigns/campaigns/invitation_to_cosponsor_initiative.rb +++ /dev/null @@ -1,93 +0,0 @@ -# frozen_string_literal: true - -# == Schema Information -# -# Table name: email_campaigns_campaigns -# -# id :uuid not null, primary key -# type :string not null -# author_id :uuid -# enabled :boolean -# sender :string -# reply_to :string -# schedule :jsonb -# subject_multiloc :jsonb -# body_multiloc :jsonb -# created_at :datetime not null -# updated_at :datetime not null -# deliveries_count :integer default(0), not null -# context_id :uuid -# -# Indexes -# -# index_email_campaigns_campaigns_on_author_id (author_id) -# index_email_campaigns_campaigns_on_context_id (context_id) -# index_email_campaigns_campaigns_on_type (type) -# -# Foreign Keys -# -# fk_rails_... (author_id => users.id) -# -module EmailCampaigns - class Campaigns::InvitationToCosponsorInitiative < Campaign - include Consentable - include ActivityTriggerable - include RecipientConfigurable - include Disableable - include Trackable - include LifecycleStageRestrictable - allow_lifecycle_stages only: %w[trial active] - - recipient_filter :filter_notification_recipient - - def mailer_class - InvitationToCosponsorInitiativeMailer - end - - def activity_triggers - { 'Notifications::InvitationToCosponsorInitiative' => { 'created' => true } } - end - - def filter_notification_recipient(users_scope, activity:, time: nil) - users_scope.where(id: activity.item.recipient.id) - end - - def self.recipient_role_multiloc_key - 'email_campaigns.admin_labels.recipient_role.registered_users' - end - - def self.recipient_segment_multiloc_key - 'email_campaigns.admin_labels.recipient_segment.user_who_is_invited_to_cosponsor_a_proposal' - end - - def self.content_type_multiloc_key - 'email_campaigns.admin_labels.content_type.proposals' - end - - def self.trigger_multiloc_key - 'email_campaigns.admin_labels.trigger.user_is_invited_to_cosponsor_a_proposal' - end - - def generate_commands(recipient:, activity:) - initiative = activity.item.post - name_service = UserDisplayNameService.new(AppConfiguration.instance, recipient) - - [{ - event_payload: { - post_title_multiloc: initiative.title_multiloc, - post_body_multiloc: initiative.body_multiloc, - post_author_name: name_service.display_name!(initiative.author), - post_url: Frontend::UrlService.new.model_to_url(initiative, locale: Locale.new(recipient.locale)), - post_image_medium_url: post_image_medium_url(initiative) - } - }] - end - - private - - def post_image_medium_url(initiative) - image = initiative&.initiative_images&.first - image.image.versions[:medium].url if image&.image&.versions - end - end -end diff --git a/back/engines/free/email_campaigns/app/services/email_campaigns/delivery_service.rb b/back/engines/free/email_campaigns/app/services/email_campaigns/delivery_service.rb index 99fa26d1cfc1..4324f9af19a8 100644 --- a/back/engines/free/email_campaigns/app/services/email_campaigns/delivery_service.rb +++ b/back/engines/free/email_campaigns/app/services/email_campaigns/delivery_service.rb @@ -20,7 +20,6 @@ class DeliveryService Campaigns::InternalCommentOnUnassignedUnmoderatedIdea, Campaigns::InternalCommentOnYourInternalComment, Campaigns::InvitationToCosponsorIdea, - Campaigns::InvitationToCosponsorInitiative, Campaigns::InviteReceived, Campaigns::InviteReminder, Campaigns::Manual, diff --git a/back/engines/free/email_campaigns/app/views/email_campaigns/invitation_to_cosponsor_initiative_mailer/campaign_mail.mjml b/back/engines/free/email_campaigns/app/views/email_campaigns/invitation_to_cosponsor_initiative_mailer/campaign_mail.mjml deleted file mode 100644 index f53f543d4270..000000000000 --- a/back/engines/free/email_campaigns/app/views/email_campaigns/invitation_to_cosponsor_initiative_mailer/campaign_mail.mjml +++ /dev/null @@ -1,26 +0,0 @@ - - - -

<%= format_message('event_description_cosponsoring', escape_html: false) %>

-
-
-
- - - - -

<%= format_message('event_description_before_action', escape_html: false) %>

-
-
-
- - - - -

<%= format_message('event_description_action') %>

-
-
-
- -<%= render 'email_campaigns/posts/post_with_image', post_image_url: event&.post_image_medium_url, post_title_multiloc: event.post_title_multiloc, post_body_multiloc: event.post_body_multiloc %> -<%= render partial: 'application/cta_button', locals: { href: event.post_url, message: format_message('cta_reply_to', values: { authorName: event.post_author_name }) } %> diff --git a/back/engines/free/email_campaigns/spec/factories/campaigns.rb b/back/engines/free/email_campaigns/spec/factories/campaigns.rb index 71f0449a8076..371a33bacdc2 100644 --- a/back/engines/free/email_campaigns/spec/factories/campaigns.rb +++ b/back/engines/free/email_campaigns/spec/factories/campaigns.rb @@ -90,10 +90,6 @@ enabled { true } end - factory :invitation_to_cosponsor_initiative_campaign, class: EmailCampaigns::Campaigns::InvitationToCosponsorInitiative do - enabled { true } - end - factory :mention_in_internal_comment_campaign, class: EmailCampaigns::Campaigns::MentionInInternalComment do enabled { true } end diff --git a/back/engines/free/email_campaigns/spec/mailers/invitation_to_cosponsor_initiative_mailer_spec.rb b/back/engines/free/email_campaigns/spec/mailers/invitation_to_cosponsor_initiative_mailer_spec.rb deleted file mode 100644 index c34322c99cf6..000000000000 --- a/back/engines/free/email_campaigns/spec/mailers/invitation_to_cosponsor_initiative_mailer_spec.rb +++ /dev/null @@ -1,45 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -# TODO: move-old-proposals-test -RSpec.describe EmailCampaigns::InvitationToCosponsorInitiativeMailer do - describe 'campaign_mail' do - let_it_be(:recipient) { create(:user, locale: 'en') } - let_it_be(:author) { create(:user) } - let_it_be(:initiative) { create(:initiative, author: author) } - let_it_be(:campaign) { EmailCampaigns::Campaigns::InvitationToCosponsorInitiative.create! } - let_it_be(:author_name) { UserDisplayNameService.new(AppConfiguration.instance, author).display_name!(initiative.author) } - let_it_be(:command) do - item = Notifications::InvitationToCosponsorInitiative.new(post: initiative) - activity = Activity.new(item: item) - commands = EmailCampaigns::Campaigns::InvitationToCosponsorInitiative.new.generate_commands(recipient: recipient, activity: activity) - commands[0].merge({ recipient: recipient }) - end - - let_it_be(:mail) { described_class.with(command: command, campaign: campaign).campaign_mail.deliver_now } - - before_all { EmailCampaigns::UnsubscriptionToken.create!(user_id: recipient.id) } - - it 'renders the subject' do - expect(mail.subject).to start_with('You have been invited to co-sponsor a proposal') - end - - it 'renders the receiver email' do - expect(mail.to).to eq([recipient.email]) - end - - it 'renders the sender email' do - expect(mail.from).to all(end_with('@citizenlab.co')) - end - - it 'assigns initiative author name' do - expect(mail.body.encoded).to match(author_name) - end - - it 'assigns cta url' do - post_url = Frontend::UrlService.new.model_to_url(initiative, locale: Locale.new(recipient.locale)) - expect(mail.body.encoded).to match(post_url) - end - end -end diff --git a/back/engines/free/email_campaigns/spec/mailers/previews/email_campaigns/invitation_to_cosponsor_initiative_mailer_preview.rb b/back/engines/free/email_campaigns/spec/mailers/previews/email_campaigns/invitation_to_cosponsor_initiative_mailer_preview.rb deleted file mode 100644 index 835a4ea94466..000000000000 --- a/back/engines/free/email_campaigns/spec/mailers/previews/email_campaigns/invitation_to_cosponsor_initiative_mailer_preview.rb +++ /dev/null @@ -1,19 +0,0 @@ -# frozen_string_literal: true - -module EmailCampaigns - class InvitationToCosponsorInitiativeMailerPreview < ActionMailer::Preview - include EmailCampaigns::MailerPreviewRecipient - - def campaign_mail - campaign = EmailCampaigns::Campaigns::InvitationToCosponsorInitiative.first - initiative = Initiative.order(created_at: :asc).first - user = User.order(created_at: :asc).first - item = Notifications::InvitationToCosponsorInitiative.new(post: initiative) - activity = Activity.new(item: item) - commands = EmailCampaigns::Campaigns::InvitationToCosponsorInitiative.new.generate_commands(recipient: user, activity: activity) - command = commands[0].merge({ recipient: user }) - - campaign.mailer_class.with(campaign: campaign, command: command).campaign_mail - end - end -end diff --git a/back/spec/factories/notifications.rb b/back/spec/factories/notifications.rb index ba8350bc542f..078f402330f0 100644 --- a/back/spec/factories/notifications.rb +++ b/back/spec/factories/notifications.rb @@ -89,12 +89,6 @@ invite end - factory :invitation_to_cosponsor_initiative, parent: :notification, class: 'Notifications::InvitationToCosponsorInitiative' do - association :post, factory: :initiative - cosponsors_initiative - initiating_user - end - factory :invitation_to_cosponsor_idea, parent: :notification, class: 'Notifications::InvitationToCosponsorIdea' do association :post, factory: :idea cosponsorship diff --git a/back/spec/models/notifications/invitation_to_cosponsor_initiative_spec.rb b/back/spec/models/notifications/invitation_to_cosponsor_initiative_spec.rb deleted file mode 100644 index 7b930878aeae..000000000000 --- a/back/spec/models/notifications/invitation_to_cosponsor_initiative_spec.rb +++ /dev/null @@ -1,20 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe Notifications::InvitationToCosponsorInitiative do - describe 'invitation_to_cosponsor_initiative' do - it 'makes a notification on created cosponsors_initiative activity' do - initiative = create(:initiative, author: create(:user)) - cosponsors_initiative = create(:cosponsors_initiative, initiative: initiative) - activity = create(:activity, item: cosponsors_initiative, user_id: initiative.author.id, action: 'created') - - notifications = described_class.make_notifications_on activity - expect(notifications.first).to have_attributes( - recipient_id: cosponsors_initiative.user_id, - initiating_user: cosponsors_initiative.initiative.author, - post: initiative - ) - end - end -end From b1b2848e2684ee0a7fd03440d5d61f6214826310 Mon Sep 17 00:00:00 2001 From: Simon Tharby Date: Wed, 18 Dec 2024 13:59:08 +0000 Subject: [PATCH 49/63] [TAN-2538] Remove NewInitiativeForAdmin notification & campaign files & specs --- .../new_initiative_for_admin_mailer.rb | 25 ----- .../campaigns/new_initiative_for_admin.rb | 106 ------------------ .../email_campaigns/delivery_service.rb | 1 - .../campaign_mail.mjml | 19 ---- .../spec/factories/campaigns.rb | 4 - .../new_initiative_for_admin_mailer_spec.rb | 49 -------- ...new_initiative_for_admin_mailer_preview.rb | 26 ----- .../new_initiative_for_admin_spec.rb | 35 ------ 8 files changed, 265 deletions(-) delete mode 100644 back/engines/free/email_campaigns/app/mailers/email_campaigns/new_initiative_for_admin_mailer.rb delete mode 100644 back/engines/free/email_campaigns/app/models/email_campaigns/campaigns/new_initiative_for_admin.rb delete mode 100644 back/engines/free/email_campaigns/app/views/email_campaigns/new_initiative_for_admin_mailer/campaign_mail.mjml delete mode 100644 back/engines/free/email_campaigns/spec/mailers/new_initiative_for_admin_mailer_spec.rb delete mode 100644 back/engines/free/email_campaigns/spec/mailers/previews/email_campaigns/new_initiative_for_admin_mailer_preview.rb delete mode 100644 back/engines/free/email_campaigns/spec/models/email_campaigns/campaigns/new_initiative_for_admin_spec.rb diff --git a/back/engines/free/email_campaigns/app/mailers/email_campaigns/new_initiative_for_admin_mailer.rb b/back/engines/free/email_campaigns/app/mailers/email_campaigns/new_initiative_for_admin_mailer.rb deleted file mode 100644 index f84102d7eea8..000000000000 --- a/back/engines/free/email_campaigns/app/mailers/email_campaigns/new_initiative_for_admin_mailer.rb +++ /dev/null @@ -1,25 +0,0 @@ -# frozen_string_literal: true - -module EmailCampaigns - class NewInitiativeForAdminMailer < ApplicationMailer - protected - - def subject - format_message('subject', values: { organizationName: organization_name }) - end - - private - - def header_title - format_message('main_header', values: { firstName: recipient_first_name }) - end - - def header_message - format_message('event_description', values: { authorName: event.post_author_name, organizationName: organization_name }) - end - - def preheader - format_message('preheader', values: { authorName: event.post_author_name, organizationName: organization_name }) - end - end -end diff --git a/back/engines/free/email_campaigns/app/models/email_campaigns/campaigns/new_initiative_for_admin.rb b/back/engines/free/email_campaigns/app/models/email_campaigns/campaigns/new_initiative_for_admin.rb deleted file mode 100644 index 9b539b06d32e..000000000000 --- a/back/engines/free/email_campaigns/app/models/email_campaigns/campaigns/new_initiative_for_admin.rb +++ /dev/null @@ -1,106 +0,0 @@ -# frozen_string_literal: true - -# == Schema Information -# -# Table name: email_campaigns_campaigns -# -# id :uuid not null, primary key -# type :string not null -# author_id :uuid -# enabled :boolean -# sender :string -# reply_to :string -# schedule :jsonb -# subject_multiloc :jsonb -# body_multiloc :jsonb -# created_at :datetime not null -# updated_at :datetime not null -# deliveries_count :integer default(0), not null -# context_id :uuid -# -# Indexes -# -# index_email_campaigns_campaigns_on_author_id (author_id) -# index_email_campaigns_campaigns_on_context_id (context_id) -# index_email_campaigns_campaigns_on_type (type) -# -# Foreign Keys -# -# fk_rails_... (author_id => users.id) -# -module EmailCampaigns - class Campaigns::NewInitiativeForAdmin < Campaign - include Consentable - include ActivityTriggerable - include RecipientConfigurable - include Disableable - include LifecycleStageRestrictable - include Trackable - allow_lifecycle_stages only: %w[trial active] - - recipient_filter :filter_recipient - - def self.consentable_roles - ['admin'] - end - - def mailer_class - NewInitiativeForAdminMailer - end - - def activity_triggers - { 'Initiative' => { 'published' => true } } - end - - def filter_recipient(users_scope, activity:, time: nil) - initiative = activity.item - initiator = initiative.author - - recipient_ids = if initiator && !initiator.admin? - User.admin.ids.reject do |recipient_id| - recipient_id == initiative&.assignee_id - end - else - [] - end - - users_scope.where(id: recipient_ids) - end - - def self.recipient_role_multiloc_key - 'email_campaigns.admin_labels.recipient_role.admins_and_managers' - end - - def self.recipient_segment_multiloc_key - 'email_campaigns.admin_labels.recipient_segment.admins' - end - - def self.content_type_multiloc_key - 'email_campaigns.admin_labels.content_type.proposals' - end - - def self.trigger_multiloc_key - 'email_campaigns.admin_labels.trigger.new_proposal_is_posted' - end - - def generate_commands(recipient:, activity:, time: nil) - initiative = activity.item - [{ - event_payload: { - post_published_at: initiative.published_at.iso8601, - post_title_multiloc: initiative.title_multiloc, - post_author_name: initiative.author_name, - post_url: Frontend::UrlService.new.model_to_url(initiative, locale: Locale.new(recipient.locale)), - initiative_reactions_needed: initiative.reactions_needed, - initiative_expires_at: initiative.expires_at.iso8601 - } - }] - end - - protected - - def set_enabled - self.enabled = false if enabled.nil? - end - end -end diff --git a/back/engines/free/email_campaigns/app/services/email_campaigns/delivery_service.rb b/back/engines/free/email_campaigns/app/services/email_campaigns/delivery_service.rb index 4324f9af19a8..17c44abd2342 100644 --- a/back/engines/free/email_campaigns/app/services/email_campaigns/delivery_service.rb +++ b/back/engines/free/email_campaigns/app/services/email_campaigns/delivery_service.rb @@ -30,7 +30,6 @@ class DeliveryService Campaigns::NativeSurveyNotSubmitted, Campaigns::NewCommentForAdmin, Campaigns::NewIdeaForAdmin, - Campaigns::NewInitiativeForAdmin, Campaigns::OfficialFeedbackOnIdeaYouFollow, Campaigns::ProjectFolderModerationRightsReceived, Campaigns::ProjectModerationRightsReceived, diff --git a/back/engines/free/email_campaigns/app/views/email_campaigns/new_initiative_for_admin_mailer/campaign_mail.mjml b/back/engines/free/email_campaigns/app/views/email_campaigns/new_initiative_for_admin_mailer/campaign_mail.mjml deleted file mode 100644 index f228934cc515..000000000000 --- a/back/engines/free/email_campaigns/app/views/email_campaigns/new_initiative_for_admin_mailer/campaign_mail.mjml +++ /dev/null @@ -1,19 +0,0 @@ - - - - -

- <%= localize_for_recipient(event.post_title_multiloc) %> -

-

- <%= format_message('by_author', component: 'general', values: { authorName: event.post_author_name }) %> -

-
-
-
- - -<%= render partial: 'application/cta_button', locals: { href: event.post_url, message: format_message('cta_reply_to', values: { authorName: event.post_author_name }) } %> diff --git a/back/engines/free/email_campaigns/spec/factories/campaigns.rb b/back/engines/free/email_campaigns/spec/factories/campaigns.rb index 371a33bacdc2..9758bfd3e8e7 100644 --- a/back/engines/free/email_campaigns/spec/factories/campaigns.rb +++ b/back/engines/free/email_campaigns/spec/factories/campaigns.rb @@ -106,10 +106,6 @@ enabled { true } end - factory :new_initiative_for_admin_campaign, class: EmailCampaigns::Campaigns::NewInitiativeForAdmin do - enabled { true } - end - factory :official_feedback_on_idea_you_follow_campaign, class: EmailCampaigns::Campaigns::OfficialFeedbackOnIdeaYouFollow do enabled { true } end diff --git a/back/engines/free/email_campaigns/spec/mailers/new_initiative_for_admin_mailer_spec.rb b/back/engines/free/email_campaigns/spec/mailers/new_initiative_for_admin_mailer_spec.rb deleted file mode 100644 index 97c38455c4d6..000000000000 --- a/back/engines/free/email_campaigns/spec/mailers/new_initiative_for_admin_mailer_spec.rb +++ /dev/null @@ -1,49 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe EmailCampaigns::NewInitiativeForAdminMailer do - describe 'campaign_mail' do - let_it_be(:recipient) { create(:user, locale: 'en') } - let_it_be(:campaign) { EmailCampaigns::Campaigns::NewInitiativeForAdmin.create! } - let_it_be(:initiative) { create(:initiative, author: recipient) } - let_it_be(:command) do - { - recipient: recipient, - event_payload: { - post_published_at: initiative.published_at.iso8601, - post_title_multiloc: initiative.title_multiloc, - post_author_name: initiative.author_name, - post_url: Frontend::UrlService.new.model_to_url(initiative, locale: Locale.new(recipient.locale)), - initiative_reactions_needed: initiative.reactions_needed, - initiative_expires_at: initiative.expires_at.iso8601 - } - } - end - - let_it_be(:mail) { described_class.with(command: command, campaign: campaign).campaign_mail.deliver_now } - - before_all { EmailCampaigns::UnsubscriptionToken.create!(user_id: recipient.id) } - - it 'renders the subject' do - expect(mail.subject).to start_with('Someone published a new proposal on the platform of') - end - - it 'renders the receiver email' do - expect(mail.to).to eq([recipient.email]) - end - - it 'renders the sender email' do - expect(mail.from).to all(end_with('@citizenlab.co')) - end - - it 'assigns author name' do - expect(mail.body.encoded).to match(initiative.author_name) - end - - it 'assigns go to initiative CTA' do - initiative_url = Frontend::UrlService.new.model_to_url(initiative, locale: Locale.new(recipient.locale)) - expect(mail.body.encoded).to match(initiative_url) - end - end -end diff --git a/back/engines/free/email_campaigns/spec/mailers/previews/email_campaigns/new_initiative_for_admin_mailer_preview.rb b/back/engines/free/email_campaigns/spec/mailers/previews/email_campaigns/new_initiative_for_admin_mailer_preview.rb deleted file mode 100644 index 944fc3893a1a..000000000000 --- a/back/engines/free/email_campaigns/spec/mailers/previews/email_campaigns/new_initiative_for_admin_mailer_preview.rb +++ /dev/null @@ -1,26 +0,0 @@ -# frozen_string_literal: true - -module EmailCampaigns - class NewInitiativeForAdminMailerPreview < ActionMailer::Preview - include EmailCampaigns::MailerPreviewRecipient - - def campaign_mail - initiative = Initiative.first - # TODO: generate commands with campaign#generate_commands method - command = { - recipient: recipient_user, - event_payload: { - post_published_at: initiative.published_at.iso8601, - post_title_multiloc: initiative.title_multiloc, - post_author_name: initiative.author_name, - post_url: Frontend::UrlService.new.model_to_url(initiative, locale: Locale.new(recipient_user.locale)), - initiative_reactions_needed: initiative.reactions_needed, - initiative_expires_at: initiative.expires_at.iso8601 - } - } - campaign = EmailCampaigns::Campaigns::NewInitiativeForAdmin.first - - campaign.mailer_class.with(campaign: campaign, command: command).campaign_mail - end - end -end diff --git a/back/engines/free/email_campaigns/spec/models/email_campaigns/campaigns/new_initiative_for_admin_spec.rb b/back/engines/free/email_campaigns/spec/models/email_campaigns/campaigns/new_initiative_for_admin_spec.rb deleted file mode 100644 index 26622ec42e84..000000000000 --- a/back/engines/free/email_campaigns/spec/models/email_campaigns/campaigns/new_initiative_for_admin_spec.rb +++ /dev/null @@ -1,35 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe EmailCampaigns::Campaigns::NewInitiativeForAdmin do - describe 'NewInitiativeForAdmin Campaign default factory' do - it 'is valid' do - expect(build(:new_initiative_for_admin_campaign)).to be_valid - end - end - - describe 'apply_recipient_filters' do - let(:campaign) { build(:new_initiative_for_admin_campaign) } - - it 'filters out normal users' do - initiative = create(:initiative) - create(:user) - admin = create(:admin) - - expect(campaign.apply_recipient_filters(activity: create(:activity, item: initiative, action: 'published')).ids).to match_array([admin.id]) - end - end - - describe 'apply_recipient_filters' do - let(:campaign) { build(:new_initiative_for_admin_campaign) } - - it 'filters out everyone if the author is admin' do - author = create(:admin) - initiative = create(:initiative, author: author) - create(:admin) - - expect(campaign.apply_recipient_filters(activity: create(:activity, item: initiative, action: 'published')).count).to eq 0 - end - end -end From 20d39152fcf97eda48ae85e8fe1c82dc82a04db6 Mon Sep 17 00:00:00 2001 From: Simon Tharby Date: Wed, 18 Dec 2024 14:06:38 +0000 Subject: [PATCH 50/63] [TAN-2538] Use idea factory in threshold_reached_for_admin factory --- back/spec/factories/notifications.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/back/spec/factories/notifications.rb b/back/spec/factories/notifications.rb index 078f402330f0..214142f814c3 100644 --- a/back/spec/factories/notifications.rb +++ b/back/spec/factories/notifications.rb @@ -173,7 +173,7 @@ end factory :threshold_reached_for_admin, parent: :notification, class: 'Notifications::ThresholdReachedForAdmin' do - association :post, factory: :initiative + association :post, factory: :idea association :post_status, factory: :proposals_status end From 5c5a65c456109fe87af4315e9c9631cb3b7796d6 Mon Sep 17 00:00:00 2001 From: Simon Tharby Date: Wed, 18 Dec 2024 14:16:25 +0000 Subject: [PATCH 51/63] [TAN-2538] Comment out failing intitiative_spec test --- back/spec/models/initiative_spec.rb | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/back/spec/models/initiative_spec.rb b/back/spec/models/initiative_spec.rb index 246532cc8fd1..09bd170b0a04 100644 --- a/back/spec/models/initiative_spec.rb +++ b/back/spec/models/initiative_spec.rb @@ -266,13 +266,13 @@ expect(User.find(cosponsor1.id)).to be_present end - it 'removes cosponsors_initiative even when an associated notifcation exists' do - cosponsor2 = create(:user) - create(:invitation_to_cosponsor_initiative, cosponsors_initiative: cosponsors_initiative) - initiative.update!(cosponsor_ids: [cosponsor2.id]) + # it 'removes cosponsors_initiative even when an associated notifcation exists' do + # cosponsor2 = create(:user) + # create(:invitation_to_cosponsor_initiative, cosponsors_initiative: cosponsors_initiative) + # initiative.update!(cosponsor_ids: [cosponsor2.id]) - expect(initiative.reload.cosponsors).to match_array [cosponsor2] - end + # expect(initiative.reload.cosponsors).to match_array [cosponsor2] + # end it 'can add and remove cosponsors_initiatives at the same time' do cosponsor2 = create(:user) From 8b13bb6f1af0ec6f8df107f954be596d8514e2a3 Mon Sep 17 00:00:00 2001 From: Simon Tharby Date: Wed, 18 Dec 2024 14:22:28 +0000 Subject: [PATCH 52/63] [TAN-2538] Remove admin_api controller & spec --- .../admin_api/types/initiative_type.rb | 54 -------------- .../spec/graphql/initiatives_spec.rb | 72 ------------------- 2 files changed, 126 deletions(-) delete mode 100644 back/engines/commercial/admin_api/app/graphql/admin_api/types/initiative_type.rb delete mode 100644 back/engines/commercial/admin_api/spec/graphql/initiatives_spec.rb diff --git a/back/engines/commercial/admin_api/app/graphql/admin_api/types/initiative_type.rb b/back/engines/commercial/admin_api/app/graphql/admin_api/types/initiative_type.rb deleted file mode 100644 index 65bf2ec62b8c..000000000000 --- a/back/engines/commercial/admin_api/app/graphql/admin_api/types/initiative_type.rb +++ /dev/null @@ -1,54 +0,0 @@ -# frozen_string_literal: true - -module AdminApi - class Types::InitiativeType < GraphQL::Schema::Object - description 'Single unit of citizen input' - - class InitiativePublicationStatus < GraphQL::Schema::Enum - Initiative::PUBLICATION_STATUSES.each do |ps| - value ps - end - end - - class InitiativeImage < GraphQL::Schema::Object - description 'An image associates with an initiative' - - field :id, ID, null: false - field :ordering, Integer, null: true - field :small_url, String, null: false - def small_url - object.image.versions[:small].url - end - field :medium_url, String, null: false - def medium_url - object.image.versions[:medium].url - end - field :large_url, String, null: false - def large_url - object.image.versions[:large].url - end - field :updated_at, String, null: false - field :created_at, String, null: false - end - - field :id, ID, null: false - field :title_multiloc, Types::MultilocType, null: false - field :slug, String, null: false - field :publication_status, InitiativePublicationStatus, null: false - field :updated_at, String, null: false - field :created_at, String, null: false - field :published_at, String, null: false - field :href, String, null: true - field :likes_count, Integer, null: false - field :comments_count, Integer, null: false - field :internal_comments_count, Integer, null: false - field :images, InitiativeImage.connection_type, null: true - def images - object.initiative_images - end - - def href - Frontend::UrlService.new.model_to_url(object) - end - end -end diff --git a/back/engines/commercial/admin_api/spec/graphql/initiatives_spec.rb b/back/engines/commercial/admin_api/spec/graphql/initiatives_spec.rb deleted file mode 100644 index 9a8f9c0b703f..000000000000 --- a/back/engines/commercial/admin_api/spec/graphql/initiatives_spec.rb +++ /dev/null @@ -1,72 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -# TODO: cleanup-after-proposals-migration -# -# Commented out for now, just so I can use CI to see other failures clearly, and because I'm not sure what -# we want to do about the admin_api for Initiatives. Maybe we remove it all, or maybe we keep it and return useful -# errors, like 'initiatives are no longer supported as a separate model, use ideas instead' or something. -# -# RSpec.describe AdminApi::Schema do -# let(:context) { {} } -# let(:variables) { {} } -# let(:result) do -# res = described_class.execute( -# query_string, -# context: context, -# variables: variables -# ) -# if res['errors'] -# pp res -# end -# res -# end - -# describe 'publicInitiatives' do -# let(:query_string) do -# %| -# query { -# publicInitiatives(first: 5) { -# edges { -# node { -# id -# href -# titleMultiloc { -# en -# nlBe -# frBe -# frFr -# } -# images(first: 1) { -# edges { -# node { -# smallUrl -# } -# } -# } -# likesCount -# commentsCount -# internalCommentsCount -# } -# } -# } -# } -# | -# end - -# it 'returns all public initiatives with fields' do -# create_list(:initiative, 5) -# create(:initiative, publication_status: 'draft') -# response = result -# edges = response.dig('data', 'publicInitiatives', 'edges') -# expect(edges&.size).to eq 5 -# expect(edges&.first&.dig('node', 'id')).to be_present -# expect(edges&.first&.dig('node', 'href')).to be_present -# expect(edges&.first&.dig('node', 'titleMultiloc')&.values&.compact&.size).to be >= 1 -# expect(edges&.first&.dig('node', 'likesCount')).to be_present -# expect(edges&.first&.dig('node', 'commentsCount')).to be_present -# expect(edges&.first&.dig('node', 'internalCommentsCount')).to be_present -# end -# end -# end From 92308a20b2f7525cc30654d04b6b40cf477cfcae Mon Sep 17 00:00:00 2001 From: Simon Tharby Date: Wed, 18 Dec 2024 14:35:02 +0000 Subject: [PATCH 53/63] [TAN-2538] Remove initiatives from admin_api graphql query_type.rb --- .../web_api/v1/users_controller.rb | 2 +- .../app/graphql/admin_api/types/query_type.rb | 24 ------------------- 2 files changed, 1 insertion(+), 25 deletions(-) diff --git a/back/app/controllers/web_api/v1/users_controller.rb b/back/app/controllers/web_api/v1/users_controller.rb index b36f70e5ecda..d49677b0d44b 100644 --- a/back/app/controllers/web_api/v1/users_controller.rb +++ b/back/app/controllers/web_api/v1/users_controller.rb @@ -204,7 +204,7 @@ def comments_count count = 0 published_comments = @user.comments.published # TODO: cleanup-after-proposals-migration - # This test won't make sense once Comment.post_type is removed + # Simplify when Comment.post_type is removed if !params[:post_type] || params[:post_type] == 'Idea' count += policy_scope( published_comments.where(post_type: 'Idea'), diff --git a/back/engines/commercial/admin_api/app/graphql/admin_api/types/query_type.rb b/back/engines/commercial/admin_api/app/graphql/admin_api/types/query_type.rb index cc8c3de750b9..623fdc355136 100644 --- a/back/engines/commercial/admin_api/app/graphql/admin_api/types/query_type.rb +++ b/back/engines/commercial/admin_api/app/graphql/admin_api/types/query_type.rb @@ -72,21 +72,6 @@ def public_ideas(args = {}) ideas end - field :initiatives, Types::InitiativeType.connection_type, null: false - - def initiatives - Initiative.all - end - - field :public_initiatives, Types::InitiativeType.connection_type, null: false - - def public_initiatives(_args = {}) - ::InitiativePolicy::Scope.new(nil, Initiative) - .resolve - .includes(:initiative_images) - .published - end - field :user, Types::UserType, null: false do argument :id, ID, required: true end @@ -132,15 +117,6 @@ def idea(args) Idea.find(args[:id]) end - field :initiative, Types::InitiativeType, null: false do - argument :id, ID, required: true - description 'Find an initiative by ID' - end - - def initiative(args) - Initiative.find(args[:id]) - end - field :public_pages, Types::PageType.connection_type, null: false def public_pages ::StaticPagePolicy::Scope.new(nil, StaticPage).resolve From 25d46e3526e6a563c3fd8ab2702404708e28f846 Mon Sep 17 00:00:00 2001 From: Simon Tharby Date: Wed, 18 Dec 2024 15:08:27 +0000 Subject: [PATCH 54/63] [TAN-2538] Remove permissions + spec --- back/app/models/permission.rb | 2 +- .../permissions/base_permissions_service.rb | 3 -- .../initiative_permissions_service.rb | 22 ----------- .../acceptance/action_descriptors_spec.rb | 33 ---------------- back/spec/acceptance/permissions_spec.rb | 6 +-- .../initiatives_permissions_service_spec.rb | 38 ------------------- 6 files changed, 3 insertions(+), 101 deletions(-) delete mode 100644 back/app/services/permissions/initiative_permissions_service.rb delete mode 100644 back/spec/services/permissions/initiatives_permissions_service_spec.rb diff --git a/back/app/models/permission.rb b/back/app/models/permission.rb index ef63779294bf..56b96a29a69e 100644 --- a/back/app/models/permission.rb +++ b/back/app/models/permission.rb @@ -24,7 +24,7 @@ class Permission < ApplicationRecord PERMITTED_BIES = %w[everyone everyone_confirmed_email users admins_moderators verified].freeze ACTIONS = { # NOTE: Order of actions in each array is used when using :order_by_action - nil => %w[visiting following posting_initiative commenting_initiative reacting_initiative attending_event], + nil => %w[visiting following attending_event], 'information' => %w[attending_event], 'ideation' => %w[posting_idea commenting_idea reacting_idea attending_event], 'proposals' => %w[posting_idea commenting_idea reacting_idea attending_event], diff --git a/back/app/services/permissions/base_permissions_service.rb b/back/app/services/permissions/base_permissions_service.rb index d1e44d3a8a0e..cee6d2b7b782 100644 --- a/back/app/services/permissions/base_permissions_service.rb +++ b/back/app/services/permissions/base_permissions_service.rb @@ -3,9 +3,6 @@ class BasePermissionsService SUPPORTED_ACTIONS = %w[ following visiting - posting_initiative - commenting_initiative - reacting_initiative posting_idea commenting_idea reacting_idea diff --git a/back/app/services/permissions/initiative_permissions_service.rb b/back/app/services/permissions/initiative_permissions_service.rb deleted file mode 100644 index 13ac58a152c9..000000000000 --- a/back/app/services/permissions/initiative_permissions_service.rb +++ /dev/null @@ -1,22 +0,0 @@ -# frozen_string_literal: true - -module Permissions - class InitiativePermissionsService < BasePermissionsService - def action_descriptors - posting_disabled_reason = denied_reason_for_action 'posting_initiative' - commenting_disabled_reason = denied_reason_for_action 'commenting_initiative' - reacting_disabled_reason = denied_reason_for_action 'reacting_initiative' - - descriptors = { - posting_initiative: { disabled_reason: posting_disabled_reason }, - commenting_initiative: { disabled_reason: commenting_disabled_reason }, - reacting_initiative: { disabled_reason: reacting_disabled_reason } - } - - descriptors.each_value { |desc| desc[:enabled] = !desc[:disabled_reason] } - descriptors[:comment_reacting_initiative] = descriptors[:commenting_initiative] - descriptors[:cancelling_initiative_reactions] = descriptors[:reacting_initiative] - descriptors - end - end -end diff --git a/back/spec/acceptance/action_descriptors_spec.rb b/back/spec/acceptance/action_descriptors_spec.rb index 8648cb7994ce..0f9fc693a41c 100644 --- a/back/spec/acceptance/action_descriptors_spec.rb +++ b/back/spec/acceptance/action_descriptors_spec.rb @@ -28,39 +28,6 @@ Permission.find_by(permission_scope: nil, action: 'commenting_initiative') .update!(permitted_by: 'users', groups: create_list(:group, 2)) end - - # TODO: cleanup-after-proposals-migration - example_request 'Get the global action descriptors for initiatives' do - expect(json_response).to eq( - { - data: { - type: 'initiatives', - attributes: { - posting_initiative: { - enabled: true, - disabled_reason: nil - }, - commenting_initiative: { - enabled: false, - disabled_reason: 'user_not_in_group' - }, - reacting_initiative: { - enabled: true, - disabled_reason: nil - }, - cancelling_initiative_reactions: { - enabled: true, - disabled_reason: nil - }, - comment_reacting_initiative: { - enabled: false, - disabled_reason: 'user_not_in_group' - } - } - } - } - ) - end end end end diff --git a/back/spec/acceptance/permissions_spec.rb b/back/spec/acceptance/permissions_spec.rb index c3689c4e190e..bd1625fda14d 100644 --- a/back/spec/acceptance/permissions_spec.rb +++ b/back/spec/acceptance/permissions_spec.rb @@ -123,9 +123,8 @@ end end - # TODO: posting_initiative get 'web_api/v1/permissions/:action' do - let(:action) { 'posting_initiative' } + let(:action) { 'attending_event' } example_request 'Get one global permission by action' do assert_status 200 @@ -198,7 +197,6 @@ end end - # TODO: posting_initiative patch 'web_api/v1/permissions/:action' do with_options scope: :permission do parameter :permitted_by, "Defines who is granted permission, either #{Permission::PERMITTED_BIES.join(',')}.", required: false @@ -207,7 +205,7 @@ end ValidationErrorHelper.new.error_fields(self, Permission) - let(:action) { 'reacting_initiative' } + let(:action) { 'attending_event' } let(:permitted_by) { 'users' } let(:group_ids) { create_list(:group, 3).map(&:id) } diff --git a/back/spec/services/permissions/initiatives_permissions_service_spec.rb b/back/spec/services/permissions/initiatives_permissions_service_spec.rb deleted file mode 100644 index 0ff55870247f..000000000000 --- a/back/spec/services/permissions/initiatives_permissions_service_spec.rb +++ /dev/null @@ -1,38 +0,0 @@ -require 'rails_helper' - -# TODO: cleanup-after-proposals-migration -describe Permissions::InitiativePermissionsService do - let(:service) { described_class.new(user) } - - before { SettingsService.new.activate_feature! 'user_confirmation' } - - # NOTE: Most of the logic here is tested in the parent class tests - BasePermissionsService - describe '"posting_initiative" denied_reason_for_action' do - let(:action) { 'posting_initiative' } - let(:permission) { Permission.find_by(permission_scope: nil, action: action) } - let(:user) { create(:user) } - - before { Permissions::PermissionsUpdateService.new.update_global_permissions } - - it 'returns nil when action is allowed' do - groups = create_list(:group, 2) - groups.first.add_member(user).save! - permission.update!(permitted_by: 'users', group_ids: groups.map(&:id)) - expect(service.denied_reason_for_action(action)).to be_nil - end - - context 'when the user is not signed in' do - let(:user) { nil } - - it 'returns `user_not_signed_in` when user needs to be signed in' do - permission.update!(permitted_by: 'users') - expect(service.denied_reason_for_action(action)).to eq 'user_not_signed_in' - end - end - - it 'returns `user_not_in_group` when user is not in authorized groups' do - permission.update!(permitted_by: 'users', group_ids: create_list(:group, 2).map(&:id)) - expect(service.denied_reason_for_action(action)).to eq 'user_not_in_group' - end - end -end From f872d6f5a1042efc084012501e9ceec44219a619 Mon Sep 17 00:00:00 2001 From: Simon Tharby Date: Wed, 18 Dec 2024 15:30:53 +0000 Subject: [PATCH 55/63] [TAN-2538] Remove action_descriptors_controller & spec --- .../v1/action_descriptors_controller.rb | 11 ------- .../acceptance/action_descriptors_spec.rb | 33 ------------------- 2 files changed, 44 deletions(-) delete mode 100644 back/app/controllers/web_api/v1/action_descriptors_controller.rb delete mode 100644 back/spec/acceptance/action_descriptors_spec.rb diff --git a/back/app/controllers/web_api/v1/action_descriptors_controller.rb b/back/app/controllers/web_api/v1/action_descriptors_controller.rb deleted file mode 100644 index 18b724bcb092..000000000000 --- a/back/app/controllers/web_api/v1/action_descriptors_controller.rb +++ /dev/null @@ -1,11 +0,0 @@ -# frozen_string_literal: true - -class WebApi::V1::ActionDescriptorsController < ApplicationController - skip_before_action :authenticate_user - skip_after_action :verify_authorized, only: [:initiatives] - - def initiatives - descriptors = Permissions::InitiativePermissionsService.new(current_user).action_descriptors - render(json: raw_json(descriptors)) - end -end diff --git a/back/spec/acceptance/action_descriptors_spec.rb b/back/spec/acceptance/action_descriptors_spec.rb deleted file mode 100644 index 0f9fc693a41c..000000000000 --- a/back/spec/acceptance/action_descriptors_spec.rb +++ /dev/null @@ -1,33 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' -require 'rspec_api_documentation/dsl' - -resource 'ActionDescriptors' do - explanation 'Describe which actions the current user is allowed to take.' - - let(:json_response) { json_parse(response_body) } - - before do - resident_header_token - header 'Content-Type', 'application/json' - end - - get 'web_api/v1/action_descriptors/initiatives' do - example_request 'Get the global action descriptors for initiatives' do - assert_status 200 - expect(json_response.dig(:data, :type)).to eq 'initiatives' - json_attributes = json_response.dig(:data, :attributes) - expect(json_attributes.values.pluck(:enabled).all?).to be true - expect(json_attributes.values.pluck(:disabled_reason).none?).to be true - end - - context 'with permissions on a phase enabled', document: false do - before do - Permissions::PermissionsUpdateService.new.update_all_permissions - Permission.find_by(permission_scope: nil, action: 'commenting_initiative') - .update!(permitted_by: 'users', groups: create_list(:group, 2)) - end - end - end -end From 86472855ed3a371df697aab31107fbaf5816c73c Mon Sep 17 00:00:00 2001 From: Simon Tharby Date: Wed, 18 Dec 2024 15:33:41 +0000 Subject: [PATCH 56/63] [TAN-2538] Fix 2 permissions related tests --- back/spec/acceptance/comment_reactions_spec.rb | 4 ++-- back/spec/acceptance/permissions_spec.rb | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/back/spec/acceptance/comment_reactions_spec.rb b/back/spec/acceptance/comment_reactions_spec.rb index c327431875c0..b229c7dbce85 100644 --- a/back/spec/acceptance/comment_reactions_spec.rb +++ b/back/spec/acceptance/comment_reactions_spec.rb @@ -70,9 +70,9 @@ end describe do - before { @comment.update!(post: create(:initiative)) } + before { @comment.update!(post: create(:idea)) } - example 'Create a reaction on a comment of an initiative', document: false do + example 'Create a reaction on a comment of an idea', document: false do do_request assert_status 201 end diff --git a/back/spec/acceptance/permissions_spec.rb b/back/spec/acceptance/permissions_spec.rb index bd1625fda14d..102d6392885d 100644 --- a/back/spec/acceptance/permissions_spec.rb +++ b/back/spec/acceptance/permissions_spec.rb @@ -91,7 +91,7 @@ example_request 'List all global permissions' do assert_status 200 - expect(response_data.size).to eq 6 + expect(response_data.size).to eq 3 expect(response_data.map { |d| d.dig(:attributes, :action) }).to match_array Permission.available_actions(nil) end end From 2e90958ef05025a07a560d32b3ca8abce0ade8df Mon Sep 17 00:00:00 2001 From: Simon Tharby Date: Wed, 18 Dec 2024 16:01:31 +0000 Subject: [PATCH 57/63] [TAN-2538] Delete a bunch of initiatives related campaigns translations --- .../email_campaigns/config/locales/en.yml | 113 ------------------ .../spec/fixtures/locales/mailers.en.yml | 108 ----------------- 2 files changed, 221 deletions(-) diff --git a/back/engines/free/email_campaigns/config/locales/en.yml b/back/engines/free/email_campaigns/config/locales/en.yml index 34594a924179..a39d0da8b62b 100644 --- a/back/engines/free/email_campaigns/config/locales/en.yml +++ b/back/engines/free/email_campaigns/config/locales/en.yml @@ -8,35 +8,23 @@ en: "comment_marked_as_spam": Comment spam report "comment_on_your_comment": A reply on my comment "comment_on_idea_you_follow": A comment on an idea that you follow - "comment_on_initiative_you_follow": A comment on a proposal that you follow - "cosponsor_of_your_initiative": A user accepts my invitation to co-sponsor my proposal "cosponsor_of_your_idea": A user accepts my invitation to co-sponsor my proposal "event_registration_confirmation": Event registration confirmation "idea_marked_as_spam": Idea spam report "idea_published": Publication of my input - "initiative_assigned_to_you": Assignment of a proposal to me - "initiative_marked_as_spam": Proposal spam report - "initiative_published": Publication of my proposal - "invitation_to_cosponsor_initiative": Invitation to co-sponsor a proposal "invitation_to_cosponsor_idea": Invitation to co-sponsor a proposal - "initiative_resubmitted_for_review": Proposal resubmitted for review "invite_received": Invitation "invite_reminder": Invitation reminder "internal_comment_on_idea_assigned_to_you": Internal comment on input assigned to me "internal_comment_on_idea_you_commented_internally_on": Internal comment on input I commented internally on "internal_comment_on_idea_you_moderate": Internal comment on input in project or folder I manage - "internal_comment_on_initiative_assigned_to_you": Internal comment on proposal assigned to me - "internal_comment_on_initiative_you_commented_internally_on": Internal comment on proposal I commented internally on - "internal_comment_on_unassigned_initiative": Internal comment on unassigned proposal "internal_comment_on_unassigned_unmoderated_idea": Internal comment on unassigned input in unmanaged project "internal_comment_on_your_internal_comment": Internal comment on my internal comment "mention_in_official_feedback": Mention in an update "mention_in_internal_comment": Mention in an internal comment "new_comment_for_admin": New comment in a project I moderate "new_idea_for_admin": New input in a project I moderate - "new_initiative_for_admin": New proposal "official_feedback_on_idea_you_follow": Update on an input that you follow - "official_feedback_on_initiative_you_follow": Update on a proposal that you follow "password_reset": Password reset "project_moderation_rights_received": Project moderation rights received "project_folder_moderation_rights_received": Folder manager rights received @@ -46,7 +34,6 @@ en: "project_review_request": Project review request "project_review_state_change": Project approved "status_change_on_idea_you_follow": Status change of an input that you follow - "status_change_on_initiative_you_follow": Status change of a proposal that you follow "threshold_reached_for_admin": Proposal reached the voting threshold "welcome": After registration "admin_digest": Weekly overview for admins @@ -73,7 +60,6 @@ en: issue: 'Go to this issue' option: 'Go to this option' proposal: 'Go to this proposal' - initiative: 'Go to this initiative' petition: 'Go to this petition' cta_goto_your: idea: 'Go to your idea' @@ -83,7 +69,6 @@ en: issue: 'Go to your issue' option: 'Go to your option' proposal: 'Go to your proposal' - initiative: 'Go to your initiative' petition: 'Go to your petition' cta_goto_proposal: 'Go to this proposal' cta_goto_project: 'Go to this project' @@ -130,7 +115,6 @@ en: new_comments: 'New comments' title_activity_past_week: 'Activity of the past week' title_no_activity_past_week: 'There was no activity in the past week' - title_initiatives_past_week: 'New proposals of the past week' reached_threshold: 'Reached the threshold' yesterday_by_author: 'Yesterday by %{author}' today_by_author: 'Today by %{author}' @@ -168,12 +152,6 @@ en: main_header: '%{cosponsorName} has accepted your invitation to co-sponsor your proposal' subject: '%{cosponsorName} has accepted your invitation to co-sponsor your proposal' preheader: '%{cosponsorName} has accepted your invitation to co-sponsor your proposal' - cosponsor_of_your_initiative: - cta_reply_to: 'View your proposal' - event_description_initiative: 'Congratulations! %{cosponsorName} has accepted your invitation to co-sponsor your proposal.' - main_header: '%{cosponsorName} has accepted your invitation to co-sponsor your proposal' - subject: '%{cosponsorName} has accepted your invitation to co-sponsor your proposal' - preheader_initiative: '%{cosponsorName} has accepted your invitation to co-sponsor your proposal' assignee_digest: subject: 'Inputs requiring your feedback: %{numberIdeas}' preheader: 'Assignee digest of %{organizationName}' @@ -194,20 +172,6 @@ en: preheader: 'Act upon this spam report' reported_this_because: '%{reporterFirstName} reported this because:' title_spam_report: '%{firstName} %{lastName} reported spam' - initiative_marked_as_spam: - cta_review_initiative: 'Review proposal' - report_inappropriate_offensive_content: 'I find this content inappropriate or offensive.' - report_not_an_initiative: 'This content is not a proposal and does not belong here.' - subject: 'You have a spam report on the platform of %{organizationName}' - preheader: 'Act upon this spam report' - reported_this_because: '%{reporterFirstName} reported this because:' - title_spam_report: '%{firstName} %{lastName} reported spam' - initiative_assigned_to_you: - cta_reply_to: 'Give feedback to %{authorName}' - event_description_initiative: 'A proposal has been assigned to you. Give feedback by writing an official update or by changing its status.' - main_header: '%{firstName}, you have a new assignment' - subject: 'You have an assignment on the platform of %{organizationName}' - preheader_initiative: 'The proposal of %{authorName} has been assigned to you' invitation_to_cosponsor_idea: cta_reply_to: 'Co-sponsor this proposal' event_description: '%{authorName} has created a new proposal and would like you to co-sponsor it.' @@ -217,22 +181,6 @@ en: main_header: 'You have been invited to co-sponsor a proposal' subject: 'You have been invited to co-sponsor a proposal' preheader: 'You have been invited to co-sponsor the proposal of %{authorName}' - invitation_to_cosponsor_initiative: - cta_reply_to: 'Co-sponsor this proposal' - event_description_initiative: '%{authorName} has created a new proposal and would like you to co-sponsor it.' - event_description_cosponsoring: 'Co-sponsoring a proposal means that your name will be displayed with the names of other co-sponsors of the proposal.' - event_description_before_action: 'To see the proposal and accept the invitation, you must be logged in to your account.' - event_description_action: 'Click below to read the proposal.' - main_header: 'You have been invited to co-sponsor a proposal' - subject: 'You have been invited to co-sponsor a proposal' - preheader_initiative: 'You have been invited to co-sponsor the proposal of %{authorName}' - initiative_resubmitted_for_review: - by_author: 'by %{authorName}' - cta_reply_to: 'Give feedback to %{authorName}' - event_description_initiative: 'A proposal has been resubmitted for review. Give feedback by writing an official update or by changing its status.' - main_header: '%{firstName}, you have a proposal to review' - subject: 'You have a proposal to review on the platform of %{organizationName}' - preheader_initiative: 'The proposal of %{authorName} has been resubmitted for review' invite_reminder: cta_accept_invitation: 'Accept your invitation' invitation_header: 'Your invitation is pending' @@ -247,20 +195,6 @@ en: invitation_expiry_message: 'This invitation expires in %{expiryDays} days.' preheader: '%{organizationName} sent you an invite to join their participation platform.' subject: 'You are invited to join the platform of %{organizationName}' - initiative_published: - action_add_image: '%{addImageLink} to increase visibility' - action_published_initiative: 'Published proposal' - action_send_email: 'Send your contacts an %{sendEmailLink}' - action_share_fb: 'Let your friends know on %{fbLink}' - action_share_link: 'Share it via any channel by copying the %{link}' - action_share_twitter: 'Inform your followers on %{twitterLink}' - link: link - main_header: 'You posted a proposal! Let''s gather support for it.' - message_next_steps: '%{userFirstName}, your proposal has successfully been published on the participation platform of %{organizationName}. You will be notified when people interact with your proposal. Find out what next steps you can take below.' - message_get_votes: 'Reach the voting threshold:' - preheader: 'Thank you for sharing your proposal %{initiativeTitle} for %{organizationName}. The next step is to get support for your proposal by sharing it across your network.' - send_email: email - subject: 'Your proposal was published on the platform of %{organizationName}' mention_in_comment: cta_reply_to: 'Reply to %{commentAuthor}' event_description: '%{commentAuthorFull} has mentioned you in his comment on the idea ''%{post}‘. Click the link below to enter the conversation with %{commentAuthor}' @@ -309,22 +243,9 @@ en: issue: '%{authorName} commented on an issue you follow' option: '%{authorName} commented on an option you follow' proposal: '%{authorName} commented on a proposal you follow' - initiative: '%{authorName} commented on an initiative you follow' petition: '%{authorName} commented on a petition you follow' subject: 'There''s a new comment on "%{input_title}"' preheader: '%{authorName} left a comment on an idea for %{organizationName}' - comment_on_initiative_you_follow: - cta_reply_to: 'Reply to %{commentAuthor}' - event_description: '%{authorNameFull} placed a reaction on the proposal ''%{initiativeTitle}''. Click the button below to continue the conversation with %{authorName}.' - main_header: '%{commentAuthor} commented on an proposal you follow' - subject: 'There''s a new comment on the proposal you follow' - preheader: '%{authorName} left a comment on an proposal for %{organizationName}' - new_initiative_for_admin: - cta_reply_to: 'Give feedback to %{authorName}' - event_description: '%{authorName} has published a new proposal on your platform. Discover it now, give some feedback or change its status!' - main_header: '%{firstName}, a new proposal has been published on your platform' - subject: 'Someone published a new proposal on the platform of %{organizationName}' - preheader: '%{authorName} published a new proposal on your platform' new_idea_for_admin: main_header_publication: 'A new input has been published on your platform' event_description_publication: '%{authorName} has submitted a new input on your platform. Discover it now, give some feedback or change its status!' @@ -368,24 +289,6 @@ en: subject: 'New internal comment on ''%{post}''' main_header: 'New internal comment on ''%{post}''' preheader: 'New internal comment on ''%{post}''' - internal_comment_on_initiative_assigned_to_you: - cta_reply_to: 'View comment by %{firstName}' - event_description: '%{authorNameFull} commented internally on a proposal assigned to you.' - subject: 'New internal comment on ''%{post}''' - main_header: 'New internal comment on ''%{post}''' - preheader: 'New internal comment on ''%{post}''' - internal_comment_on_initiative_you_commented_internally_on: - cta_reply_to: 'View comment by %{firstName}' - event_description: '%{authorNameFull} commented internally on a proposal you commented internally on.' - subject: 'New internal comment on ''%{post}''' - main_header: 'New internal comment on ''%{post}''' - preheader: 'New internal comment on ''%{post}''' - internal_comment_on_unassigned_initiative: - cta_reply_to: 'View comment by %{firstName}' - event_description: '%{authorNameFull} commented internally on an unassigned proposal.' - subject: 'New internal comment on ''%{post}''' - main_header: 'New internal comment on ''%{post}''' - preheader: 'New internal comment on ''%{post}''' internal_comment_on_unassigned_unmoderated_idea: cta_reply_to: 'View comment by %{firstName}' event_description: '%{authorNameFull} commented internally on an unassigned input in an unmanaged project.' @@ -397,11 +300,6 @@ en: header_title: 'There''s an update on "%{input_title}"' subject: 'Official feedback was posted on "%{input_title}"' preheader: 'There''s an update on an input you follow' - official_feedback_on_initiative_you_follow: - header_message: '%{officialName} gave an update on the proposal ''%{initiativeTitle}''. Click the button below to enter the conversation with %{organizationName}.' - header_title: 'There''s an update on a proposal you follow' - subject: 'A proposal you follow has received an official update on the platform of %{organizationName}' - preheader: 'There''s an update on a proposal you follow' mention_in_official_feedback: cta_reply_to: 'Reply to %{organizationName}' event_description: '%{organizationName} mentioned you in their feedback on the idea ''%{post}‘. Click the link below to enter the conversation with %{organizationName}' @@ -475,12 +373,6 @@ en: header_title: 'An input you follow has a new status' subject: 'The status of "%{input_title}" has changed' preheader: 'An input you follow has a new status' - status_change_on_initiative_you_follow: - status_change: 'The new status of this proposal is ''%{initiativeStatus}''' - header_message: 'The status of the proposal ''%{initiativeTitle}'' has been updated on the digital participation platform of %{organizationName}.' - header_title: 'A proposal you follow has a new status' - subject: 'The status of a proposal you follow has changed' - preheader: 'A proposal you follow has a new status' user_digest: subject: "This week on the participation platform of %{organizationName}" commented: "%{authorFirstName} commented:" @@ -521,7 +413,6 @@ en: issue: 'Your issue has been published' option: 'Your option has been published' proposal: 'Your proposal has been published' - initiative: 'Your initiative has been published' petition: 'Your petition has been published' main_header: 'You posted "%{input_title}"' header_message: 'Let''s make sure it gets read.' @@ -545,7 +436,6 @@ en: issue: 'Your issue is in "%{prescreening_status_title}"' option: 'Your option is in "%{prescreening_status_title}"' proposal: 'Your proposal is in "%{prescreening_status_title}"' - initiative: 'Your initiative is in "%{prescreening_status_title}"' petition: 'Your petition is in "%{prescreening_status_title}"' message: '"%{input_title}" will become visible to others once it has been reviewed and approved.' subject: '"%{input_title}" is almost published' @@ -674,9 +564,6 @@ en: internal_comment_is_posted_on_idea_in_project_or_folder_user_manages: 'Internal comment is posted on input in project or folder user manages' internal_comment_is_posted_on_idea_user_has_commented_internally_on: 'Internal comment is posted on input user commented internally on' internal_comment_is_posted_on_idea_user_moderates: 'Internal comment is posted on input user moderates' - internal_comment_is_posted_on_initiative_assigned_to_user: 'Internal comment is posted on proposal assigned to user' - internal_comment_is_posted_on_initiative_user_has_commented_internally_on: 'Internal comment is posted on proposal user commented internally on' - internal_comment_is_posted_on_unassigned_initiative: 'Internal comment is posted on unassigned proposal' internal_comment_is_posted_on_unassigned_unmoderated_idea: 'Internal comment is posted on unassigned input in unmanaged project' project_review_request: 'Moderator requested a project review' project_review_state_change: 'Reviewer approved the project' diff --git a/back/engines/free/email_campaigns/spec/fixtures/locales/mailers.en.yml b/back/engines/free/email_campaigns/spec/fixtures/locales/mailers.en.yml index 43f6ac448615..1bdc6797a535 100644 --- a/back/engines/free/email_campaigns/spec/fixtures/locales/mailers.en.yml +++ b/back/engines/free/email_campaigns/spec/fixtures/locales/mailers.en.yml @@ -8,31 +8,20 @@ en: "comment_marked_as_spam": Comment spam report "comment_on_your_comment": A reply on my comment "comment_on_idea_you_follow": A comment on an idea that you follow - "comment_on_initiative_you_follow": A comment on a proposal that you follow - "cosponsor_of_your_initiative": A user accepts my invitation to co-sponor my proposal "idea_marked_as_spam": Idea spam report "idea_published": Publication of my input - "initiative_assigned_to_you": Assignment of a proposal to me - "initiative_marked_as_spam": Proposal spam report - "initiative_published": Publication of my proposal "internal_comment_on_idea_assigned_to_you": Internal comment on input assigned to me "internal_comment_on_idea_you_commented_internally_on": Internal comment on input I commented internally on "internal_comment_on_idea_you_moderate": Internal comment on input in project or folder I manage - "internal_comment_on_initiative_assigned_to_you": Internal comment on proposal assigned to me - "internal_comment_on_initiative_you_commented_internally_on": Internal comment on proposal I commented internally on - "internal_comment_on_unassigned_initiative": Internal comment on unassigned proposal "internal_comment_on_unassigned_unmoderated_idea": Internal comment on unassigned input in unmanaged project "internal_comment_on_your_internal_comment": Internal comment on my internal comment "invite_received": Invitation "invite_reminder": Invitation reminder - "invitation_to_cosponsor_initiative": Invitation to co-sponsor a proposal "mention_in_internal_comment": Mention in an internal comment "mention_in_official_feedback": Mention in an update "new_comment_for_admin": New comment in a project I moderate "new_idea_for_admin": New input in a project I moderate - "new_initiative_for_admin": New proposal "official_feedback_on_idea_you_follow": Update on an input that you follow - "official_feedback_on_initiative_you_follow": Update on a proposal that you follow "password_reset": Password reset "project_moderation_rights_received": Project moderation rights received "project_folder_moderation_rights_received": Project folder moderation rights received @@ -40,7 +29,6 @@ en: "project_phase_upcoming": Upcoming new project phase "project_published": Project published "status_change_on_idea_you_follow": Status change of an input that you follow - "status_change_on_initiative_you_follow": Status change of a proposal that you follow "threshold_reached_for_admin": Proposal reached the voting threshold "welcome": After registration "admin_digest": Weekly overview for admins @@ -99,8 +87,6 @@ en: new_comments: 'New comments' title_activity_past_week: 'Activity of the past week' title_no_activity_past_week: 'There was no activity in the past week' - title_initiatives_past_week: 'New proposals of the past week' - title_successful_initiatives_past_week: 'Proposals that reached the threshold in the past week' yesterday_by_author: 'Yesterday by %{author}' today_by_author: 'Today by %{author}' x_days_ago_by_author: '%{x} days ago by %{author}' @@ -131,12 +117,6 @@ en: today: Today wrong_content: 'The comment is not relevant.' yesterday: Yesterday - cosponsor_of_your_initiative: - cta_reply_to: 'View your proposal' - event_description_initiative: 'Congratulations! %{cosponsorName} has accepted your invitation to co-sponsor your proposal.' - main_header: '%{cosponsorName} has accepted your invitation to co-sponsor your proposal' - subject: '%{cosponsorName} has accepted your invitation to co-sponsor your proposal' - preheader_initiative: '%{cosponsorName} has accepted your invitation to co-sponsor your proposal' assignee_digest: subject: 'Ideas requiring your feedback: %{numberIdeas}' preheader: 'Assignee digest of %{organizationName}' @@ -149,9 +129,6 @@ en: today_by_author: 'Today by %{author}' x_days_ago_by_author: '%{x} days ago by %{author}' idea_section: Ideas - initiative_section: Proposals - title_initiatives_past_week: 'New proposals assigned to you' - title_successful_initiatives_past_week: 'Proposals assigned to you that reached the threshold' idea_marked_as_spam: cta_review_idea: 'Review idea' report_inappropriate_offensive_content: 'I find this content inappropriate or offensive.' @@ -160,20 +137,6 @@ en: preheader: 'Act upon this spam report' reported_this_because: '%{reporterFirstName} reported this because:' title_spam_report: '%{firstName} %{lastName} reported spam' - initiative_marked_as_spam: - cta_review_initiative: 'Review proposal' - report_inappropriate_offensive_content: 'I find this content inappropriate or offensive.' - report_not_an_initiative: 'This content is not a proposal and does not belong here.' - subject: 'You have a spam report on the platform of %{organizationName}' - preheader: 'Act upon this spam report' - reported_this_because: '%{reporterFirstName} reported this because:' - title_spam_report: '%{firstName} %{lastName} reported spam' - initiative_assigned_to_you: - cta_reply_to: 'Give feedback to %{authorName}' - event_description_initiative: 'An proposal has been assigned to you. Give feedback by writing an official update or by changing its status.' - main_header: '%{firstName}, you have a new assignment' - subject: 'You have an assignment on the platform of %{organizationName}' - preheader_initiative: 'The proposal of %{authorName} has been assigned to you' invite_reminder: cta_accept_invitation: 'Accept your invitation' invitation_header: 'Your invitation is pending' @@ -197,29 +160,6 @@ en: main_header: 'You have been invited to co-sponsor a proposal' subject: 'You have been invited to co-sponsor a proposal' preheader: 'You have been invited to co-sponsor the proposal of %{authorName}' - invitation_to_cosponsor_initative: - cta_reply_to: 'Co-sponsor this proposal' - event_description_initiative: '%{authorName} has created a new proposal and would like you to co-sponsor it.' - event_description_cosponsoring: 'Co-sponsoring a proposal means that your name will be displayed with the names of other co-sponsors of the proposal' - event_description_before_action: 'To see the proposal and accept the invitation, you must be logged in to your account.' - event_description_action: 'Click below to read the proposal' - main_header: 'You have been invited to co-sponsor a proposal' - subject: 'You have been invited to co-sponsor a proposal' - preheader_initiative: 'You have been invited to co-sponsor the proposal of %{authorName}' - initiative_published: - action_add_image: '%{addImageLink} to increase visibility' - action_published_initiative: 'Published proposal' - action_send_email: 'Send your contacts an %{sendEmailLink}' - action_share_fb: 'Let your friends know on %{fbLink}' - action_share_link: 'Share it via any channel by copying the %{link}' - action_share_twitter: 'Inform your followers on %{twitterLink}' - link: link - main_header: 'You posted a proposal! Let''s gather support for it.' - message_next_steps: '%{userFirstName}, your proposal has successfully been published on the participation platform of %{organizationName}. You will be notified when people interact with your proposal. Find out what next steps you can take below.' - message_get_votes: 'Reach the voting threshold:' - preheader: 'Thank you for sharing your proposal %{initiativeTitle} for %{organizationName}. The next step is to get support for your proposal by sharing it across your network.' - send_email: email - subject: 'Your proposal was published on the platform of %{organizationName}' new_comment_on_commented_idea: cta_reply_to: 'Reply to %{commentAuthor}' event_description: '%{commentAuthorFull} also wrote a comment on the idea ''%{ideaTitle}'' on the participation platform of %{organizationName}. Click the button below to start the conversation with %{commentAuthor}.' @@ -252,13 +192,6 @@ en: yesterday_by_author: 'Yesterday by %{author}' today_by_author: 'Today by %{author}' x_days_ago_by_author: '%{x} days ago by %{author}' - new_comment_on_commented_initiative: - commented: '%{commentAuthor} wrote:' - cta_reply_to: 'Reply to %{commentAuthor}' - event_description: '%{commentAuthorFull} also wrote a comment on the proposal ''%{initiativeTitle}'' on the participation platform of %{organizationName}. Click the button below to start the conversation with %{commentAuthor}.' - main_header: '%{commentAuthor} wrote a comment on the same proposal' - subject: 'There''s another comment on the proposal you commented on' - preheader: '%{commentAuthor} also left a comment on an proposal for %{organizationName}' new_comment_for_admin: commented: '%{authorFirstName} commented:' cta_reply_to: 'View %{commentAuthor}''s comment' @@ -275,18 +208,6 @@ en: main_header: '%{authorName} commented on an idea you follow' subject: 'There''s a new comment on an idea you follow' preheader: '%{authorName} left a comment on an idea for %{organizationName}' - comment_on_initiative_you_follow: - cta_reply_to: 'Reply to %{commentAuthor}' - event_description: '%{authorNameFull} placed a reaction on the initiative ''%{initiativeTitle}''. Click the button below to continue the conversation with %{authorName}.' - main_header: '%{commentAuthor} commented on an initiative you follow' - subject: 'There''s a new comment on the proposal you follow' - preheader: '%{authorName} left a comment on an initiative for %{organizationName}' - new_initiative_for_admin: - cta_reply_to: 'Give feedback to %{authorName}' - event_description: '%{authorName} has published a new proposal on your platform. Discover it now, give some feedback or change its status!' - main_header: '%{firstName}, a new proposal has been published on your platform' - subject: 'Someone published a new proposal on the platform of %{organizationName}' - preheader: '%{authorName} published a new proposal on your platform' new_idea_for_admin: new_idea_for_admin: main_header_publication: '%{firstName}, a new input has been published on your platform' @@ -331,24 +252,6 @@ en: subject: '''%{post}'' has a new internal comment' main_header: '''%{post}'' has a new internal comment' preheader: '''%{post}'' has a new internal comment' - internal_comment_on_initiative_assigned_to_you: - cta_reply_to: 'View comment by %{firstName}' - event_description: '%{authorNameFull} commented internally on a proposal assigned to you.' - subject: '''%{post}'' has a new internal comment' - main_header: '''%{post}'' has a new internal comment' - preheader: '''%{post}'' has a new internal comment' - internal_comment_on_initiative_you_commented_internally_on: - cta_reply_to: 'View comment by %{firstName}' - event_description: '%{authorNameFull} commented internally on a proposal you commented internally on.' - subject: '''%{post}'' has a new internal comment' - main_header: '''%{post}'' has a new internal comment' - preheader: '''%{post}'' has a new internal comment' - internal_comment_on_unassigned_initiative: - cta_reply_to: 'View comment by %{firstName}' - event_description: '%{authorNameFull} commented internally on an unassigned proposal.' - subject: '''%{post}'' has a new internal comment' - main_header: '''%{post}'' has a new internal comment' - preheader: '''%{post}'' has a new internal comment' internal_comment_on_unassigned_unmoderated_idea: cta_reply_to: 'View comment by %{firstName}' event_description: '%{authorNameFull} commented internally on an unassigned input in an unmanaged project.' @@ -360,11 +263,6 @@ en: header_title: 'There''s an update on an input you follow' subject: 'An input you follow has received an official update on the platform of %{organizationName}' preheader: 'There''s an update on an input you follow' - official_feedback_on_initiative_you_follow: - header_message: '%{officialName} gave an update on the proposal ''%{initiativeTitle}''. Click the button below to enter the conversation with %{organizationName}.' - header_title: 'There''s an update on a proposal you follow' - subject: 'A proposal you follow has received an official update on the platform of %{organizationName}' - preheader: 'There''s an update on a proposal you follow' mention_in_official_feedback: cta_reply_to: 'Reply to %{organizationName}' event_description: '%{organizationName} mentioned you in their feedback on the idea ''%{post}‘. Click the link below to enter the conversation with %{organizationName}' @@ -428,12 +326,6 @@ en: header_title: 'An input you follow has a new status' subject: 'The status of "%{input_title}" has changed' preheader: 'An input you follow has a new status' - status_change_on_initiative_you_follow: - status_change: 'The new status of this proposal is ''%{initiativeStatus}''' - header_message: 'The status of the proposal ''%{initiativeTitle}'' has been updated on the digital participation platform of %{organizationName}.' - header_title: 'A proposal you follow has a new status' - subject: 'The status of a proposal you follow has changed' - preheader: 'A proposal you follow has a new status' user_digest: subject: "Your activity on the participation platform of %{organizationName}" commented: "%{authorFirstName} commented:" From 7fa17038e76200581b50650c6ea93282eeb27a71 Mon Sep 17 00:00:00 2001 From: Simon Tharby Date: Thu, 19 Dec 2024 11:15:44 +0000 Subject: [PATCH 58/63] [TAN-2538] Remove public_api controller, route + spec --- .../public_api/v2/initiatives_controller.rb | 25 ------ .../commercial/public_api/config/routes.rb | 1 - .../spec/acceptance/v2/initiatives_spec.rb | 89 ------------------- 3 files changed, 115 deletions(-) delete mode 100644 back/engines/commercial/public_api/app/controllers/public_api/v2/initiatives_controller.rb delete mode 100644 back/engines/commercial/public_api/spec/acceptance/v2/initiatives_spec.rb diff --git a/back/engines/commercial/public_api/app/controllers/public_api/v2/initiatives_controller.rb b/back/engines/commercial/public_api/app/controllers/public_api/v2/initiatives_controller.rb deleted file mode 100644 index 79be12f1a4cf..000000000000 --- a/back/engines/commercial/public_api/app/controllers/public_api/v2/initiatives_controller.rb +++ /dev/null @@ -1,25 +0,0 @@ -# frozen_string_literal: true - -module PublicApi - class V2::InitiativesController < PublicApiController - include DeletedItemsAction - - def index - initiatives = Initiative - .order(created_at: :desc) - .page(params[:page_number]) - .per(num_per_page) - - initiatives = common_date_filters(initiatives) - - render json: initiatives, - each_serializer: V2::InitiativeSerializer, - adapter: :json, - meta: meta_properties(initiatives) - end - - def show - show_item Initiative.find(params[:id]), V2::InitiativeSerializer - end - end -end diff --git a/back/engines/commercial/public_api/config/routes.rb b/back/engines/commercial/public_api/config/routes.rb index 6c6593eedf26..c7e359e4b6af 100644 --- a/back/engines/commercial/public_api/config/routes.rb +++ b/back/engines/commercial/public_api/config/routes.rb @@ -26,7 +26,6 @@ route_mapper.resources :events route_mapper.resources :event_attendances route_mapper.resources :ideas - route_mapper.resources :initiatives route_mapper.resources :phases route_mapper.resources :project_folders route_mapper.resources :reactions, only: %i[index] diff --git a/back/engines/commercial/public_api/spec/acceptance/v2/initiatives_spec.rb b/back/engines/commercial/public_api/spec/acceptance/v2/initiatives_spec.rb deleted file mode 100644 index e97199abb191..000000000000 --- a/back/engines/commercial/public_api/spec/acceptance/v2/initiatives_spec.rb +++ /dev/null @@ -1,89 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' -require 'rspec_api_documentation/dsl' -require './engines/commercial/public_api/spec/acceptance/v2/support/shared' - -# TODO: cleanup-after-proposals-migration -resource 'Posts' do - # NOTE: Same name as in ideas_spec to combine the documentation into the same section - explanation <<~DESC.squish - Posts are written inputs created by citizens. These can be ideas, initiatives - (proposals), or survey responses. - DESC - - include_context 'common_auth' - - let!(:initiatives) { create_list(:initiative, 5, created_at: '2020-01-01') } - - get '/api/v2/initiatives/' do - route_summary 'List initiatives - known as proposals in the platform' - route_description <<~DESC.squish - Retrieve a paginated list of all the initiatives in the platform, with the most - recent ones appearing first. - DESC - - include_context 'common_list_params' - - context 'when the page size is smaller than the total number of initiatives' do - let(:page_size) { 2 } - - example_request 'List only the first initiatives' do - assert_status 200 - expect(json_response_body[:initiatives].size).to eq(page_size) - - total_pages = (initiatives.size.to_f / page_size).ceil - expect(json_response_body[:meta]).to eq({ total_pages: total_pages, current_page: 1 }) - end - end - - include_examples 'filtering_by_date', :initiative, :created_at - include_examples 'filtering_by_date', :initiative, :updated_at - end - - get '/api/v2/initiatives/:id' do - route_summary 'Get an initiative (proposal)' - route_description 'Retrieve a single initiative (proposal) by its ID.' - - include_context 'common_item_params' - - let(:initiative) { initiatives.first } - let(:id) { initiative.id } - - before do - # NOTE: Temp fix until locales of factories and tenants are consistent - # Currently, the tenant locales are ["en", "fr-FR", "nl-NL"], while the factory - # locales are ["en", "nl-BE"]. The following code aligns the two by replacing - # the "nl-BE" locale with "nl-NL" in the initiative. - title = initiative[:title_multiloc] - title['nl-NL'] = title.delete 'nl-BE' - initiative.update!(title_multiloc: title) - end - - example_request 'Return the initiative in the default locale' do - assert_status 200 - expect(json_response_body[:initiative]).to include({ id: id }) - expect(json_response_body.dig(:initiative, :threshold_reached_at)).to be_nil - end - - context 'when requesting the initiative in a specific locale' do - let(:locale) { 'nl-NL' } - - example_request 'Return the initiative in the requested locale', document: false do - assert_status 200 - expect(json_response_body.dig(:initiative, :title)).to eq initiative.title_multiloc['nl-NL'] - end - end - - context 'when initiative is successful' do - let(:initiative) { create(:initiative, initiative_status: create(:initiative_status_threshold_reached)) } - - example_request 'Return the initiative with successful date', document: false do - assert_status 200 - expect(json_response_body.dig(:initiative, :threshold_reached_at)).not_to be_nil - end - end - end - - include_examples '/api/v2/.../deleted', :initiatives -end From 2441e03f44a5e176110b1b84a52ac5ca3f92f98f Mon Sep 17 00:00:00 2001 From: Simon Tharby Date: Fri, 20 Dec 2024 11:12:52 +0000 Subject: [PATCH 59/63] [TAN-2538] Update views & migrate --- ...tiatives_from_analytics_views.analytics.rb | 14 ++ back/db/structure.sql | 190 +++++++----------- .../views/analytics_build_feedbacks_v02.sql | 28 +++ .../analytics_dimension_statuses_v03.sql | 1 + .../analytics_fact_participations_v08.sql | 127 ++++++++++++ back/db/views/analytics_fact_posts_v09.sql | 25 +++ ...remove_initiatives_from_analytics_views.rb | 13 ++ .../views/analytics_build_feedbacks_v02.sql | 28 +++ .../analytics_dimension_statuses_v03.sql | 1 + .../analytics_fact_participations_v08.sql | 127 ++++++++++++ .../db/views/analytics_fact_posts_v09.sql | 25 +++ 11 files changed, 463 insertions(+), 116 deletions(-) create mode 100644 back/db/migrate/20241220103433_remove_initiatives_from_analytics_views.analytics.rb create mode 100644 back/db/views/analytics_build_feedbacks_v02.sql create mode 100644 back/db/views/analytics_dimension_statuses_v03.sql create mode 100644 back/db/views/analytics_fact_participations_v08.sql create mode 100644 back/db/views/analytics_fact_posts_v09.sql create mode 100644 back/engines/commercial/analytics/db/migrate/20241220103251_remove_initiatives_from_analytics_views.rb create mode 100644 back/engines/commercial/analytics/db/views/analytics_build_feedbacks_v02.sql create mode 100644 back/engines/commercial/analytics/db/views/analytics_dimension_statuses_v03.sql create mode 100644 back/engines/commercial/analytics/db/views/analytics_fact_participations_v08.sql create mode 100644 back/engines/commercial/analytics/db/views/analytics_fact_posts_v09.sql diff --git a/back/db/migrate/20241220103433_remove_initiatives_from_analytics_views.analytics.rb b/back/db/migrate/20241220103433_remove_initiatives_from_analytics_views.analytics.rb new file mode 100644 index 000000000000..ac920508698b --- /dev/null +++ b/back/db/migrate/20241220103433_remove_initiatives_from_analytics_views.analytics.rb @@ -0,0 +1,14 @@ +# This migration comes from analytics (originally 20241220103251) +class RemoveInitiativesFromAnalyticsViews < ActiveRecord::Migration[7.0] + def change + # We need to drop analytics_fact_posts, before updating analytics_build_feedbacks + # because analytics_fact_posts depends on analytics_build_feedbacks. + drop_view :analytics_fact_posts, revert_to_version: 8 + update_view :analytics_build_feedbacks, version: 2, revert_to_version: 1 + # We recreate analytics_fact_posts, after updating analytics_build_feedbacks + create_view :analytics_fact_posts, version: 8 + update_view :analytics_dimension_statuses, version: 3, revert_to_version: 2 + update_view :analytics_fact_participations, version: 8, revert_to_version: 7 + update_view :analytics_fact_posts, version: 9, revert_to_version: 8 + end +end diff --git a/back/db/structure.sql b/back/db/structure.sql index 224b08c361c1..fca50116bfbb 100644 --- a/back/db/structure.sql +++ b/back/db/structure.sql @@ -571,6 +571,9 @@ DROP TABLE IF EXISTS public.machine_translations_machine_translations; DROP TABLE IF EXISTS public.internal_comments; DROP TABLE IF EXISTS public.initiatives_topics; DROP VIEW IF EXISTS public.initiative_initiative_statuses; +DROP TABLE IF EXISTS public.initiatives; +DROP TABLE IF EXISTS public.initiative_statuses; +DROP TABLE IF EXISTS public.initiative_status_changes; DROP TABLE IF EXISTS public.initiative_images; DROP TABLE IF EXISTS public.initiative_files; DROP TABLE IF EXISTS public.impact_tracking_salts; @@ -622,14 +625,12 @@ DROP VIEW IF EXISTS public.analytics_fact_registrations; DROP TABLE IF EXISTS public.invites; DROP VIEW IF EXISTS public.analytics_fact_project_statuses; DROP VIEW IF EXISTS public.analytics_fact_posts; -DROP TABLE IF EXISTS public.initiative_status_changes; DROP VIEW IF EXISTS public.analytics_fact_participations; DROP TABLE IF EXISTS public.volunteering_volunteers; DROP TABLE IF EXISTS public.volunteering_causes; DROP TABLE IF EXISTS public.reactions; DROP TABLE IF EXISTS public.polls_responses; DROP TABLE IF EXISTS public.phases; -DROP TABLE IF EXISTS public.initiatives; DROP TABLE IF EXISTS public.ideas; DROP TABLE IF EXISTS public.events_attendances; DROP TABLE IF EXISTS public.comments; @@ -644,7 +645,6 @@ DROP TABLE IF EXISTS public.users; DROP TABLE IF EXISTS public.analytics_fact_visits; DROP TABLE IF EXISTS public.analytics_dimension_types; DROP VIEW IF EXISTS public.analytics_dimension_statuses; -DROP TABLE IF EXISTS public.initiative_statuses; DROP TABLE IF EXISTS public.idea_statuses; DROP TABLE IF EXISTS public.analytics_dimension_referrer_types; DROP TABLE IF EXISTS public.analytics_dimension_projects_fact_visits; @@ -1161,7 +1161,7 @@ CREATE VIEW public.analytics_build_feedbacks AS 0 AS feedback_official, 1 AS feedback_status_change FROM public.activities - WHERE (((activities.action)::text = 'changed_status'::text) AND ((activities.item_type)::text = ANY (ARRAY[('Idea'::character varying)::text, ('Initiative'::character varying)::text]))) + WHERE (((activities.action)::text = 'changed_status'::text) AND ((activities.item_type)::text = 'Idea'::text)) GROUP BY activities.item_id UNION ALL SELECT official_feedbacks.post_id, @@ -1281,38 +1281,16 @@ CREATE TABLE public.idea_statuses ( ); --- --- Name: initiative_statuses; Type: TABLE; Schema: public; Owner: - --- - -CREATE TABLE public.initiative_statuses ( - id uuid DEFAULT shared_extensions.gen_random_uuid() NOT NULL, - title_multiloc jsonb DEFAULT '{}'::jsonb, - description_multiloc jsonb DEFAULT '{}'::jsonb, - ordering integer, - code character varying, - color character varying, - created_at timestamp without time zone NOT NULL, - updated_at timestamp without time zone NOT NULL -); - - -- -- Name: analytics_dimension_statuses; Type: VIEW; Schema: public; Owner: - -- CREATE VIEW public.analytics_dimension_statuses AS - SELECT idea_statuses.id, - idea_statuses.title_multiloc, - idea_statuses.code, - idea_statuses.color - FROM public.idea_statuses -UNION ALL - SELECT initiative_statuses.id, - initiative_statuses.title_multiloc, - initiative_statuses.code, - initiative_statuses.color - FROM public.initiative_statuses; + SELECT id, + title_multiloc, + code, + color + FROM public.idea_statuses; -- @@ -1582,37 +1560,6 @@ CREATE TABLE public.ideas ( ); --- --- Name: initiatives; Type: TABLE; Schema: public; Owner: - --- - -CREATE TABLE public.initiatives ( - id uuid DEFAULT shared_extensions.gen_random_uuid() NOT NULL, - title_multiloc jsonb DEFAULT '{}'::jsonb, - body_multiloc jsonb DEFAULT '{}'::jsonb, - publication_status character varying, - published_at timestamp without time zone, - author_id uuid, - likes_count integer DEFAULT 0 NOT NULL, - dislikes_count integer DEFAULT 0 NOT NULL, - location_point shared_extensions.geography(Point,4326), - location_description character varying, - slug character varying, - comments_count integer DEFAULT 0 NOT NULL, - created_at timestamp without time zone NOT NULL, - updated_at timestamp without time zone NOT NULL, - header_bg character varying, - assignee_id uuid, - official_feedbacks_count integer DEFAULT 0 NOT NULL, - assigned_at timestamp without time zone, - author_hash character varying, - anonymous boolean DEFAULT false NOT NULL, - internal_comments_count integer DEFAULT 0 NOT NULL, - followers_count integer DEFAULT 0 NOT NULL, - editing_locked boolean DEFAULT false NOT NULL -); - - -- -- Name: phases; Type: TABLE; Schema: public; Owner: - -- @@ -1747,18 +1694,6 @@ CREATE VIEW public.analytics_fact_participations AS JOIN public.analytics_dimension_types idea ON (((idea.name)::text = 'idea'::text))) LEFT JOIN public.analytics_dimension_types survey ON (((survey.name)::text = 'survey'::text))) WHERE ((i.publication_status)::text = 'published'::text) -UNION ALL - SELECT i.id, - i.author_id AS dimension_user_id, - COALESCE((i.author_id)::text, (i.author_hash)::text, (i.id)::text) AS participant_id, - NULL::uuid AS dimension_project_id, - adt.id AS dimension_type_id, - (i.created_at)::date AS dimension_date_created_id, - (i.likes_count + i.dislikes_count) AS reactions_count, - i.likes_count, - i.dislikes_count - FROM (public.initiatives i - JOIN public.analytics_dimension_types adt ON (((adt.name)::text = 'initiative'::text))) UNION ALL SELECT c.id, c.author_id AS dimension_user_id, @@ -1848,21 +1783,6 @@ UNION ALL JOIN public.analytics_dimension_types adt ON (((adt.name)::text = 'event_attendance'::text))); --- --- Name: initiative_status_changes; Type: TABLE; Schema: public; Owner: - --- - -CREATE TABLE public.initiative_status_changes ( - id uuid DEFAULT shared_extensions.gen_random_uuid() NOT NULL, - user_id uuid, - initiative_id uuid, - initiative_status_id uuid, - official_feedback_id uuid, - created_at timestamp without time zone NOT NULL, - updated_at timestamp without time zone NOT NULL -); - - -- -- Name: analytics_fact_posts; Type: VIEW; Schema: public; Owner: - -- @@ -1895,32 +1815,7 @@ CREATE VIEW public.analytics_fact_posts AS WHEN ((creation_phase.participation_method)::text = 'proposals'::text) THEN 'proposal'::text ELSE NULL::text END))) - WHERE ((creation_phase.* IS NULL) OR ((creation_phase.participation_method)::text = 'proposals'::text)) -UNION ALL - SELECT i.id, - i.author_id AS user_id, - NULL::uuid AS dimension_project_id, - adt.id AS dimension_type_id, - (i.created_at)::date AS dimension_date_created_id, - (abf.feedback_first_date)::date AS dimension_date_first_feedback_id, - isc.initiative_status_id AS dimension_status_id, - (abf.feedback_first_date - i.created_at) AS feedback_time_taken, - COALESCE(abf.feedback_official, 0) AS feedback_official, - COALESCE(abf.feedback_status_change, 0) AS feedback_status_change, - CASE - WHEN (abf.feedback_first_date IS NULL) THEN 1 - ELSE 0 - END AS feedback_none, - (i.likes_count + i.dislikes_count) AS reactions_count, - i.likes_count, - i.dislikes_count, - i.publication_status - FROM (((public.initiatives i - JOIN public.analytics_dimension_types adt ON (((adt.name)::text = 'initiative'::text))) - LEFT JOIN public.analytics_build_feedbacks abf ON ((abf.post_id = i.id))) - LEFT JOIN public.initiative_status_changes isc ON (((isc.initiative_id = i.id) AND (isc.updated_at = ( SELECT max(isc_.updated_at) AS max - FROM public.initiative_status_changes isc_ - WHERE (isc_.initiative_id = i.id)))))); + WHERE ((creation_phase.* IS NULL) OR ((creation_phase.participation_method)::text = 'proposals'::text)); -- @@ -2683,6 +2578,68 @@ CREATE TABLE public.initiative_images ( ); +-- +-- Name: initiative_status_changes; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.initiative_status_changes ( + id uuid DEFAULT shared_extensions.gen_random_uuid() NOT NULL, + user_id uuid, + initiative_id uuid, + initiative_status_id uuid, + official_feedback_id uuid, + created_at timestamp without time zone NOT NULL, + updated_at timestamp without time zone NOT NULL +); + + +-- +-- Name: initiative_statuses; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.initiative_statuses ( + id uuid DEFAULT shared_extensions.gen_random_uuid() NOT NULL, + title_multiloc jsonb DEFAULT '{}'::jsonb, + description_multiloc jsonb DEFAULT '{}'::jsonb, + ordering integer, + code character varying, + color character varying, + created_at timestamp without time zone NOT NULL, + updated_at timestamp without time zone NOT NULL +); + + +-- +-- Name: initiatives; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.initiatives ( + id uuid DEFAULT shared_extensions.gen_random_uuid() NOT NULL, + title_multiloc jsonb DEFAULT '{}'::jsonb, + body_multiloc jsonb DEFAULT '{}'::jsonb, + publication_status character varying, + published_at timestamp without time zone, + author_id uuid, + likes_count integer DEFAULT 0 NOT NULL, + dislikes_count integer DEFAULT 0 NOT NULL, + location_point shared_extensions.geography(Point,4326), + location_description character varying, + slug character varying, + comments_count integer DEFAULT 0 NOT NULL, + created_at timestamp without time zone NOT NULL, + updated_at timestamp without time zone NOT NULL, + header_bg character varying, + assignee_id uuid, + official_feedbacks_count integer DEFAULT 0 NOT NULL, + assigned_at timestamp without time zone, + author_hash character varying, + anonymous boolean DEFAULT false NOT NULL, + internal_comments_count integer DEFAULT 0 NOT NULL, + followers_count integer DEFAULT 0 NOT NULL, + editing_locked boolean DEFAULT false NOT NULL +); + + -- -- Name: initiative_initiative_statuses; Type: VIEW; Schema: public; Owner: - -- @@ -7805,6 +7762,7 @@ INSERT INTO "schema_migrations" (version) VALUES ('20241127093339'), ('20241203151945'), ('20241204133717'), -('20241204144321'); +('20241204144321'), +('20241220103433'); diff --git a/back/db/views/analytics_build_feedbacks_v02.sql b/back/db/views/analytics_build_feedbacks_v02.sql new file mode 100644 index 000000000000..84d81d6ab248 --- /dev/null +++ b/back/db/views/analytics_build_feedbacks_v02.sql @@ -0,0 +1,28 @@ +SELECT + post_id, + MIN(feedback_first_date) AS feedback_first_date, + MAX(feedback_official) AS feedback_official, + MAX(feedback_status_change) AS feedback_status_change +FROM ( + SELECT + item_id AS post_id, + MIN(created_at) AS feedback_first_date, + 0 AS feedback_official, + 1 AS feedback_status_change + FROM activities + WHERE + action = 'changed_status' AND + item_type = 'Idea' + GROUP BY item_id + + UNION ALL + + SELECT + post_id, + MIN(created_at) AS feedback_first_date, + 1 AS feedback_official, + 0 AS feedback_status_change + FROM official_feedbacks + GROUP BY post_id + ) AS a +GROUP BY post_id; \ No newline at end of file diff --git a/back/db/views/analytics_dimension_statuses_v03.sql b/back/db/views/analytics_dimension_statuses_v03.sql new file mode 100644 index 000000000000..b23507512883 --- /dev/null +++ b/back/db/views/analytics_dimension_statuses_v03.sql @@ -0,0 +1 @@ +SELECT id, title_multiloc, code, color FROM idea_statuses diff --git a/back/db/views/analytics_fact_participations_v08.sql b/back/db/views/analytics_fact_participations_v08.sql new file mode 100644 index 000000000000..73dca99913a6 --- /dev/null +++ b/back/db/views/analytics_fact_participations_v08.sql @@ -0,0 +1,127 @@ +-- Analytics view for all types of participation + +-- Ideas & Native surveys +SELECT + i.id, + i.author_id AS dimension_user_id, + COALESCE(i.author_id::TEXT, i.author_hash, i.id::TEXT) as participant_id, + i.project_id AS dimension_project_id, + CASE + WHEN ph.participation_method = 'native_survey' THEN survey.id + ELSE idea.id + END AS dimension_type_id, + i.created_at::DATE AS dimension_date_created_id, + likes_count + dislikes_count AS reactions_count, + likes_count, + dislikes_count +FROM ideas i +LEFT JOIN projects pr ON pr.id = i.project_id +LEFT JOIN phases ph ON ph.id = i.creation_phase_id +INNER JOIN analytics_dimension_types idea ON idea.name = 'idea' +LEFT JOIN analytics_dimension_types survey ON survey.name = 'survey' +WHERE i.publication_status = 'published' + +UNION ALL + +-- Comments +SELECT + c.id, + c.author_id AS dimension_user_id, + COALESCE(c.author_id::TEXT, c.author_hash, c.id::TEXT) as participant_id, + i.project_id AS dimension_project_id, + adt.id AS dimension_type_id, + c.created_at::DATE AS dimension_date_created_id, + c.likes_count + c.dislikes_count AS reactions_count, + c.likes_count, + c.dislikes_count +FROM comments c +INNER JOIN analytics_dimension_types adt ON adt.name = 'comment' AND adt.parent = LOWER(c.post_type) +LEFT JOIN ideas i ON c.post_id = i.id + +UNION ALL + +-- Reactions +SELECT + r.id, + r.user_id AS dimension_user_id, + COALESCE(r.user_id::TEXT, r.id::TEXT) as participant_id, + COALESCE(i.project_id, ic.project_id) AS dimension_project_id, + adt.id AS dimension_type_id, + r.created_at::DATE AS dimension_date_created_id, + 1 AS reactions_count, + CASE WHEN r.mode = 'up' THEN 1 ELSE 0 END AS likes_count, + CASE WHEN r.mode = 'down' THEN 1 ELSE 0 END AS dislikes_count +FROM reactions r +INNER JOIN analytics_dimension_types adt ON adt.name = 'reaction' AND adt.parent = LOWER(r.reactable_type) +LEFT JOIN ideas i ON i.id = r.reactable_id +LEFT JOIN comments c ON c.id = r.reactable_id +LEFT JOIN ideas ic ON ic.id = c.post_id + +UNION ALL + +-- Poll Response +SELECT + pr.id, + pr.user_id AS dimension_user_id, + COALESCE(pr.user_id::TEXT, pr.id::TEXT) as participant_id, + p.project_id AS dimension_project_id, + adt.id AS dimension_type_id, + pr.created_at::DATE AS dimension_date_created_id, + 0 AS reactions_count, + 0 AS likes_count, + 0 AS dislikes_count +FROM polls_responses pr +LEFT JOIN phases p ON p.id = pr.phase_id +INNER JOIN analytics_dimension_types adt ON adt.name = 'poll' + +UNION ALL + +-- Volunteering +SELECT + vv.id, + vv.user_id AS dimension_user_id, + COALESCE(vv.user_id::TEXT, vv.id::TEXT) as participant_id, + p.project_id AS dimension_project_id, + adt.id AS dimension_type_id, + vv.created_at::DATE AS dimension_date_created_id, + 0 AS reactions_count, + 0 AS likes_count, + 0 AS dislikes_count +FROM volunteering_volunteers vv +LEFT JOIN volunteering_causes vc ON vc.id = vv.cause_id +LEFT JOIN phases p ON p.id = vc.phase_id +INNER JOIN analytics_dimension_types adt ON adt.name = 'volunteer' + +UNION ALL + +--Baskets +SELECT + b.id, + b.user_id AS dimension_user_id, + COALESCE(b.user_id::TEXT, b.id::TEXT) as participant_id, + p.project_id AS dimension_project_id, + adt.id AS dimension_type_id, + b.created_at::DATE AS dimension_date_created_id, + 0 AS reactions_count, + 0 AS likes_count, + 0 AS dislikes_count +FROM baskets b +LEFT JOIN phases p ON p.id = b.phase_id +INNER JOIN analytics_dimension_types adt ON adt.name = 'basket' + +UNION ALL + +--Event attendance +SELECT + ea.id, + ea.attendee_id AS dimension_user_id, + ea.attendee_id::TEXT as participant_id, + e.project_id AS dimension_project_id, + adt.id AS dimension_type_id, + ea.created_at::DATE AS dimension_date_created_id, + 0 AS reactions_count, + 0 AS likes_count, + 0 AS dislikes_count +FROM events_attendances ea +LEFT JOIN events e ON e.id = ea.event_id +INNER JOIN analytics_dimension_types adt on adt.name = 'event_attendance'; \ No newline at end of file diff --git a/back/db/views/analytics_fact_posts_v09.sql b/back/db/views/analytics_fact_posts_v09.sql new file mode 100644 index 000000000000..5ba6a9bd3f34 --- /dev/null +++ b/back/db/views/analytics_fact_posts_v09.sql @@ -0,0 +1,25 @@ +-- Analytics view for posts +SELECT + i.id, + i.author_id AS user_id, + i.project_id AS dimension_project_id, + adt.id as dimension_type_id, + i.created_at::DATE AS dimension_date_created_id, + abf.feedback_first_date::DATE as dimension_date_first_feedback_id, + idea_status_id as dimension_status_id, + (abf.feedback_first_date - i.created_at) as feedback_time_taken, + COALESCE(abf.feedback_official,0) AS feedback_official, + COALESCE(abf.feedback_status_change,0) AS feedback_status_change, + CASE WHEN abf.feedback_first_date IS NULL THEN 1 ELSE 0 END AS feedback_none, + likes_count + dislikes_count as reactions_count, + likes_count, + dislikes_count, + i.publication_status +from ideas i +LEFT JOIN analytics_build_feedbacks AS abf ON abf.post_id = i.id +LEFT JOIN phases AS creation_phase on i.creation_phase_id = creation_phase.id +INNER JOIN analytics_dimension_types adt ON adt.name = CASE + WHEN creation_phase IS NULL THEN 'idea' + WHEN creation_phase.participation_method = 'proposals' THEN 'proposal' + END +WHERE creation_phase IS NULL OR creation_phase.participation_method = 'proposals' diff --git a/back/engines/commercial/analytics/db/migrate/20241220103251_remove_initiatives_from_analytics_views.rb b/back/engines/commercial/analytics/db/migrate/20241220103251_remove_initiatives_from_analytics_views.rb new file mode 100644 index 000000000000..56372a99e9e7 --- /dev/null +++ b/back/engines/commercial/analytics/db/migrate/20241220103251_remove_initiatives_from_analytics_views.rb @@ -0,0 +1,13 @@ +class RemoveInitiativesFromAnalyticsViews < ActiveRecord::Migration[7.0] + def change + # We need to drop analytics_fact_posts, before updating analytics_build_feedbacks + # because analytics_fact_posts depends on analytics_build_feedbacks. + drop_view :analytics_fact_posts, revert_to_version: 8 + update_view :analytics_build_feedbacks, version: 2, revert_to_version: 1 + # We recreate analytics_fact_posts, after updating analytics_build_feedbacks + create_view :analytics_fact_posts, version: 8 + update_view :analytics_dimension_statuses, version: 3, revert_to_version: 2 + update_view :analytics_fact_participations, version: 8, revert_to_version: 7 + update_view :analytics_fact_posts, version: 9, revert_to_version: 8 + end +end diff --git a/back/engines/commercial/analytics/db/views/analytics_build_feedbacks_v02.sql b/back/engines/commercial/analytics/db/views/analytics_build_feedbacks_v02.sql new file mode 100644 index 000000000000..84d81d6ab248 --- /dev/null +++ b/back/engines/commercial/analytics/db/views/analytics_build_feedbacks_v02.sql @@ -0,0 +1,28 @@ +SELECT + post_id, + MIN(feedback_first_date) AS feedback_first_date, + MAX(feedback_official) AS feedback_official, + MAX(feedback_status_change) AS feedback_status_change +FROM ( + SELECT + item_id AS post_id, + MIN(created_at) AS feedback_first_date, + 0 AS feedback_official, + 1 AS feedback_status_change + FROM activities + WHERE + action = 'changed_status' AND + item_type = 'Idea' + GROUP BY item_id + + UNION ALL + + SELECT + post_id, + MIN(created_at) AS feedback_first_date, + 1 AS feedback_official, + 0 AS feedback_status_change + FROM official_feedbacks + GROUP BY post_id + ) AS a +GROUP BY post_id; \ No newline at end of file diff --git a/back/engines/commercial/analytics/db/views/analytics_dimension_statuses_v03.sql b/back/engines/commercial/analytics/db/views/analytics_dimension_statuses_v03.sql new file mode 100644 index 000000000000..b23507512883 --- /dev/null +++ b/back/engines/commercial/analytics/db/views/analytics_dimension_statuses_v03.sql @@ -0,0 +1 @@ +SELECT id, title_multiloc, code, color FROM idea_statuses diff --git a/back/engines/commercial/analytics/db/views/analytics_fact_participations_v08.sql b/back/engines/commercial/analytics/db/views/analytics_fact_participations_v08.sql new file mode 100644 index 000000000000..73dca99913a6 --- /dev/null +++ b/back/engines/commercial/analytics/db/views/analytics_fact_participations_v08.sql @@ -0,0 +1,127 @@ +-- Analytics view for all types of participation + +-- Ideas & Native surveys +SELECT + i.id, + i.author_id AS dimension_user_id, + COALESCE(i.author_id::TEXT, i.author_hash, i.id::TEXT) as participant_id, + i.project_id AS dimension_project_id, + CASE + WHEN ph.participation_method = 'native_survey' THEN survey.id + ELSE idea.id + END AS dimension_type_id, + i.created_at::DATE AS dimension_date_created_id, + likes_count + dislikes_count AS reactions_count, + likes_count, + dislikes_count +FROM ideas i +LEFT JOIN projects pr ON pr.id = i.project_id +LEFT JOIN phases ph ON ph.id = i.creation_phase_id +INNER JOIN analytics_dimension_types idea ON idea.name = 'idea' +LEFT JOIN analytics_dimension_types survey ON survey.name = 'survey' +WHERE i.publication_status = 'published' + +UNION ALL + +-- Comments +SELECT + c.id, + c.author_id AS dimension_user_id, + COALESCE(c.author_id::TEXT, c.author_hash, c.id::TEXT) as participant_id, + i.project_id AS dimension_project_id, + adt.id AS dimension_type_id, + c.created_at::DATE AS dimension_date_created_id, + c.likes_count + c.dislikes_count AS reactions_count, + c.likes_count, + c.dislikes_count +FROM comments c +INNER JOIN analytics_dimension_types adt ON adt.name = 'comment' AND adt.parent = LOWER(c.post_type) +LEFT JOIN ideas i ON c.post_id = i.id + +UNION ALL + +-- Reactions +SELECT + r.id, + r.user_id AS dimension_user_id, + COALESCE(r.user_id::TEXT, r.id::TEXT) as participant_id, + COALESCE(i.project_id, ic.project_id) AS dimension_project_id, + adt.id AS dimension_type_id, + r.created_at::DATE AS dimension_date_created_id, + 1 AS reactions_count, + CASE WHEN r.mode = 'up' THEN 1 ELSE 0 END AS likes_count, + CASE WHEN r.mode = 'down' THEN 1 ELSE 0 END AS dislikes_count +FROM reactions r +INNER JOIN analytics_dimension_types adt ON adt.name = 'reaction' AND adt.parent = LOWER(r.reactable_type) +LEFT JOIN ideas i ON i.id = r.reactable_id +LEFT JOIN comments c ON c.id = r.reactable_id +LEFT JOIN ideas ic ON ic.id = c.post_id + +UNION ALL + +-- Poll Response +SELECT + pr.id, + pr.user_id AS dimension_user_id, + COALESCE(pr.user_id::TEXT, pr.id::TEXT) as participant_id, + p.project_id AS dimension_project_id, + adt.id AS dimension_type_id, + pr.created_at::DATE AS dimension_date_created_id, + 0 AS reactions_count, + 0 AS likes_count, + 0 AS dislikes_count +FROM polls_responses pr +LEFT JOIN phases p ON p.id = pr.phase_id +INNER JOIN analytics_dimension_types adt ON adt.name = 'poll' + +UNION ALL + +-- Volunteering +SELECT + vv.id, + vv.user_id AS dimension_user_id, + COALESCE(vv.user_id::TEXT, vv.id::TEXT) as participant_id, + p.project_id AS dimension_project_id, + adt.id AS dimension_type_id, + vv.created_at::DATE AS dimension_date_created_id, + 0 AS reactions_count, + 0 AS likes_count, + 0 AS dislikes_count +FROM volunteering_volunteers vv +LEFT JOIN volunteering_causes vc ON vc.id = vv.cause_id +LEFT JOIN phases p ON p.id = vc.phase_id +INNER JOIN analytics_dimension_types adt ON adt.name = 'volunteer' + +UNION ALL + +--Baskets +SELECT + b.id, + b.user_id AS dimension_user_id, + COALESCE(b.user_id::TEXT, b.id::TEXT) as participant_id, + p.project_id AS dimension_project_id, + adt.id AS dimension_type_id, + b.created_at::DATE AS dimension_date_created_id, + 0 AS reactions_count, + 0 AS likes_count, + 0 AS dislikes_count +FROM baskets b +LEFT JOIN phases p ON p.id = b.phase_id +INNER JOIN analytics_dimension_types adt ON adt.name = 'basket' + +UNION ALL + +--Event attendance +SELECT + ea.id, + ea.attendee_id AS dimension_user_id, + ea.attendee_id::TEXT as participant_id, + e.project_id AS dimension_project_id, + adt.id AS dimension_type_id, + ea.created_at::DATE AS dimension_date_created_id, + 0 AS reactions_count, + 0 AS likes_count, + 0 AS dislikes_count +FROM events_attendances ea +LEFT JOIN events e ON e.id = ea.event_id +INNER JOIN analytics_dimension_types adt on adt.name = 'event_attendance'; \ No newline at end of file diff --git a/back/engines/commercial/analytics/db/views/analytics_fact_posts_v09.sql b/back/engines/commercial/analytics/db/views/analytics_fact_posts_v09.sql new file mode 100644 index 000000000000..5ba6a9bd3f34 --- /dev/null +++ b/back/engines/commercial/analytics/db/views/analytics_fact_posts_v09.sql @@ -0,0 +1,25 @@ +-- Analytics view for posts +SELECT + i.id, + i.author_id AS user_id, + i.project_id AS dimension_project_id, + adt.id as dimension_type_id, + i.created_at::DATE AS dimension_date_created_id, + abf.feedback_first_date::DATE as dimension_date_first_feedback_id, + idea_status_id as dimension_status_id, + (abf.feedback_first_date - i.created_at) as feedback_time_taken, + COALESCE(abf.feedback_official,0) AS feedback_official, + COALESCE(abf.feedback_status_change,0) AS feedback_status_change, + CASE WHEN abf.feedback_first_date IS NULL THEN 1 ELSE 0 END AS feedback_none, + likes_count + dislikes_count as reactions_count, + likes_count, + dislikes_count, + i.publication_status +from ideas i +LEFT JOIN analytics_build_feedbacks AS abf ON abf.post_id = i.id +LEFT JOIN phases AS creation_phase on i.creation_phase_id = creation_phase.id +INNER JOIN analytics_dimension_types adt ON adt.name = CASE + WHEN creation_phase IS NULL THEN 'idea' + WHEN creation_phase.participation_method = 'proposals' THEN 'proposal' + END +WHERE creation_phase IS NULL OR creation_phase.participation_method = 'proposals' From d34f9960740cc6df9611287f2fdef847005588cb Mon Sep 17 00:00:00 2001 From: Simon Tharby Date: Fri, 20 Dec 2024 11:20:17 +0000 Subject: [PATCH 60/63] [TAN-2538] Add EOF newlines --- back/db/views/analytics_build_feedbacks_v02.sql | 2 +- back/db/views/analytics_fact_participations_v08.sql | 2 +- .../analytics/db/views/analytics_build_feedbacks_v02.sql | 2 +- .../analytics/db/views/analytics_fact_participations_v08.sql | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/back/db/views/analytics_build_feedbacks_v02.sql b/back/db/views/analytics_build_feedbacks_v02.sql index 84d81d6ab248..568d37a2bcff 100644 --- a/back/db/views/analytics_build_feedbacks_v02.sql +++ b/back/db/views/analytics_build_feedbacks_v02.sql @@ -25,4 +25,4 @@ FROM ( FROM official_feedbacks GROUP BY post_id ) AS a -GROUP BY post_id; \ No newline at end of file +GROUP BY post_id; diff --git a/back/db/views/analytics_fact_participations_v08.sql b/back/db/views/analytics_fact_participations_v08.sql index 73dca99913a6..16c9c30f36d5 100644 --- a/back/db/views/analytics_fact_participations_v08.sql +++ b/back/db/views/analytics_fact_participations_v08.sql @@ -124,4 +124,4 @@ SELECT 0 AS dislikes_count FROM events_attendances ea LEFT JOIN events e ON e.id = ea.event_id -INNER JOIN analytics_dimension_types adt on adt.name = 'event_attendance'; \ No newline at end of file +INNER JOIN analytics_dimension_types adt on adt.name = 'event_attendance'; diff --git a/back/engines/commercial/analytics/db/views/analytics_build_feedbacks_v02.sql b/back/engines/commercial/analytics/db/views/analytics_build_feedbacks_v02.sql index 84d81d6ab248..568d37a2bcff 100644 --- a/back/engines/commercial/analytics/db/views/analytics_build_feedbacks_v02.sql +++ b/back/engines/commercial/analytics/db/views/analytics_build_feedbacks_v02.sql @@ -25,4 +25,4 @@ FROM ( FROM official_feedbacks GROUP BY post_id ) AS a -GROUP BY post_id; \ No newline at end of file +GROUP BY post_id; diff --git a/back/engines/commercial/analytics/db/views/analytics_fact_participations_v08.sql b/back/engines/commercial/analytics/db/views/analytics_fact_participations_v08.sql index 73dca99913a6..16c9c30f36d5 100644 --- a/back/engines/commercial/analytics/db/views/analytics_fact_participations_v08.sql +++ b/back/engines/commercial/analytics/db/views/analytics_fact_participations_v08.sql @@ -124,4 +124,4 @@ SELECT 0 AS dislikes_count FROM events_attendances ea LEFT JOIN events e ON e.id = ea.event_id -INNER JOIN analytics_dimension_types adt on adt.name = 'event_attendance'; \ No newline at end of file +INNER JOIN analytics_dimension_types adt on adt.name = 'event_attendance'; From 5a8e3792049eab74d39645838c51e7b1fcd2c4eb Mon Sep 17 00:00:00 2001 From: Simon Tharby Date: Fri, 20 Dec 2024 11:29:17 +0000 Subject: [PATCH 61/63] [TAN-2538] Fix specs --- .../spec/acceptance/analytics_participations_spec.rb | 4 +--- .../spec/models/analytics/dimension_status_spec.rb | 9 --------- .../analytics/spec/models/analytics/fact_post_spec.rb | 10 ---------- .../services/analytics/query_runner_service_spec.rb | 10 ++-------- 4 files changed, 3 insertions(+), 30 deletions(-) diff --git a/back/engines/commercial/analytics/spec/acceptance/analytics_participations_spec.rb b/back/engines/commercial/analytics/spec/acceptance/analytics_participations_spec.rb index b0737e2e01f7..f0d61be3d969 100644 --- a/back/engines/commercial/analytics/spec/acceptance/analytics_participations_spec.rb +++ b/back/engines/commercial/analytics/spec/acceptance/analytics_participations_spec.rb @@ -22,7 +22,6 @@ # Type dimensions [ { name: 'idea', parent: 'post' }, - { name: 'initiative', parent: 'post' }, { name: 'comment', parent: 'idea' }, { name: 'reaction', parent: 'idea' } ].each do |type| @@ -37,7 +36,6 @@ idea = create(:idea, created_at: dates[0], author: male) create(:comment, created_at: dates[2], post: idea, author: female) create(:reaction, created_at: dates[3], user: create(:admin, gender: 'female'), reactable: idea) - create(:initiative, created_at: dates[1], author: unspecified) end example 'group participations by month' do @@ -52,7 +50,7 @@ }) assert_status 200 expect(response_data[:attributes]).to match_array([ - { 'dimension_date_created.month': '2022-09', count: 2 }, + { 'dimension_date_created.month': '2022-09', count: 1 }, { 'dimension_date_created.month': '2022-10', count: 2 } ]) end diff --git a/back/engines/commercial/analytics/spec/models/analytics/dimension_status_spec.rb b/back/engines/commercial/analytics/spec/models/analytics/dimension_status_spec.rb index 515a1c3ffc74..a5eb29dbad1f 100644 --- a/back/engines/commercial/analytics/spec/models/analytics/dimension_status_spec.rb +++ b/back/engines/commercial/analytics/spec/models/analytics/dimension_status_spec.rb @@ -10,13 +10,4 @@ expect { described_class.find(idea_status.id) }.not_to raise_error end end - - # TODO: cleanup-after-proposals-migration - context 'when an initiative status is created' do - let!(:initiative_status) { create(:initiative_status) } - - it 'is also available as a status dimension' do - expect { described_class.find(initiative_status.id) }.not_to raise_error - end - end end diff --git a/back/engines/commercial/analytics/spec/models/analytics/fact_post_spec.rb b/back/engines/commercial/analytics/spec/models/analytics/fact_post_spec.rb index be5bd5ccf3f5..c2f39722dd68 100644 --- a/back/engines/commercial/analytics/spec/models/analytics/fact_post_spec.rb +++ b/back/engines/commercial/analytics/spec/models/analytics/fact_post_spec.rb @@ -11,14 +11,4 @@ expect { described_class.find(idea.id) }.not_to raise_error end end - - # TODO: cleanup-after-proposals-migration - context 'when an initiative is created and there is an initiative type' do - let!(:type_initiative) { create(:dimension_type, name: 'initiative') } - let!(:initiative) { create(:initiative) } - - it 'is also available as a post fact' do - expect { described_class.find(initiative.id) }.not_to raise_error - end - end end diff --git a/back/engines/commercial/analytics/spec/services/analytics/query_runner_service_spec.rb b/back/engines/commercial/analytics/spec/services/analytics/query_runner_service_spec.rb index d968fcaaeb4f..131fd644704d 100644 --- a/back/engines/commercial/analytics/spec/services/analytics/query_runner_service_spec.rb +++ b/back/engines/commercial/analytics/spec/services/analytics/query_runner_service_spec.rb @@ -23,9 +23,7 @@ it 'return groups with aggregations' do idea = create(:idea) - initiative = create(:initiative) create_list(:reaction, 2, reactable: idea) - create_list(:reaction, 1, reactable: initiative) query_param = { fact: 'post', @@ -39,10 +37,7 @@ runner = described_class.new results, * = runner.run(query) - expected_result = [ - { 'dimension_type.name' => 'initiative', 'sum_reactions_count' => 1 }, - { 'dimension_type.name' => 'idea', 'sum_reactions_count' => 2 } - ] + expected_result = [{ 'dimension_type.name' => 'idea', 'sum_reactions_count' => 2 }] expect(results).to match_array expected_result end @@ -68,7 +63,6 @@ it 'return first two sorted posts' do ideas = create_list(:idea, 5) - initiatives = create_list(:initiative, 5) query_param = { fact: 'post', @@ -79,7 +73,7 @@ runner = described_class.new results, * = runner.run(query) - posts = (ideas + initiatives) + posts = ideas .sort_by { |p| p[:id] } .map { |p| { 'id' => p.id } } expect(results).to eq(posts) From 4783d9435fc167c01394864fb1dc0e0a2cffd9b8 Mon Sep 17 00:00:00 2001 From: Simon Tharby Date: Fri, 20 Dec 2024 11:54:47 +0000 Subject: [PATCH 62/63] [TAN-2538] Linting --- .../analytics/spec/acceptance/analytics_participations_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/back/engines/commercial/analytics/spec/acceptance/analytics_participations_spec.rb b/back/engines/commercial/analytics/spec/acceptance/analytics_participations_spec.rb index f0d61be3d969..ae60c686fca8 100644 --- a/back/engines/commercial/analytics/spec/acceptance/analytics_participations_spec.rb +++ b/back/engines/commercial/analytics/spec/acceptance/analytics_participations_spec.rb @@ -30,7 +30,7 @@ male = create(:user, gender: 'male') female = create(:user, gender: 'female') - unspecified = create(:user, gender: 'unspecified') + _unspecified = create(:user, gender: 'unspecified') # Create participations (3 by citizens, 1 by admin) idea = create(:idea, created_at: dates[0], author: male) From a40997fb115959b5cd86f6234774d804105664e5 Mon Sep 17 00:00:00 2001 From: Simon Tharby Date: Fri, 20 Dec 2024 14:10:37 +0000 Subject: [PATCH 63/63] [TAN-2538] Fix ThresholdReachedForAdminMailerPreview --- .../threshold_reached_for_admin_mailer_preview.rb | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/back/engines/free/email_campaigns/spec/mailers/previews/email_campaigns/threshold_reached_for_admin_mailer_preview.rb b/back/engines/free/email_campaigns/spec/mailers/previews/email_campaigns/threshold_reached_for_admin_mailer_preview.rb index 5d5dd4ab80af..ab35caf3be16 100644 --- a/back/engines/free/email_campaigns/spec/mailers/previews/email_campaigns/threshold_reached_for_admin_mailer_preview.rb +++ b/back/engines/free/email_campaigns/spec/mailers/previews/email_campaigns/threshold_reached_for_admin_mailer_preview.rb @@ -6,7 +6,7 @@ class ThresholdReachedForAdminMailerPreview < ActionMailer::Preview def campaign_mail campaign = EmailCampaigns::Campaigns::ThresholdReachedForAdmin.first - post = Initiative.first + post = Idea.first # TODO: generate commands with campaign#generate_commands method command = { @@ -19,15 +19,12 @@ def campaign_mail post_url: 'demo.stg.govocal.com', post_likes_count: 3, post_comments_count: 4, - post_images: post.initiative_images.map do |image| + post_images: post.idea_images.map do |image| { ordering: image.ordering, versions: image.image.versions.to_h { |k, v| [k.to_s, v.url] } } end, - initiative_header_bg: { - versions: post.header_bg.versions.to_h { |k, v| [k.to_s, v.url] } - }, assignee_first_name: 'Lady', assignee_last_name: 'Gaga' }