Skip to content

Fix private contributions missing on the calendar if user leaves project

What does this MR do and why?

Fixes private contributions being hidden on the contributions calendar when a user leaves a project (or gets removed), despite being opted-in to include private contributions in the calendar. The contributions are currently only listed in the activity feed, but missing from the contributions calendar.

Screenshots or screen recordings

Before After
before fixed

Please note that the first activity shown in the feed is the Left project X/Y, which doesn't get counted as contribution.

How to set up and validate locally

  1. Opt-in to include private contributions in the calendar
  2. Join a private project and contribute something (e.g. push, create issue)
  3. Leave the project
  4. Open your profile while not being logged-in
  5. Note that your contribution gets included in the contribution calendar

Database

After

For ContributedProjectsFinder.new(contributor).execute(current_user, ignore_visibility: true)
Plan: https://explain.depesz.com/s/Muso

SELECT
    "projects"."id",
    "projects"."name",
    "projects"."path",
    "projects"."description",
    "projects"."created_at",
    "projects"."updated_at",
    "projects"."creator_id",
    "projects"."namespace_id",
    "projects"."last_activity_at",
    "projects"."import_url",
    "projects"."visibility_level",
    "projects"."archived",
    "projects"."avatar",
    "projects"."merge_requests_template",
    "projects"."star_count",
    "projects"."merge_requests_rebase_enabled",
    "projects"."import_type",
    "projects"."import_source",
    "projects"."approvals_before_merge",
    "projects"."reset_approvals_on_push",
    "projects"."merge_requests_ff_only_enabled",
    "projects"."issues_template",
    "projects"."mirror",
    "projects"."mirror_user_id",
    "projects"."shared_runners_enabled",
    "projects"."runners_token",
    "projects"."build_coverage_regex",
    "projects"."build_allow_git_fetch",
    "projects"."build_timeout",
    "projects"."mirror_trigger_builds",
    "projects"."pending_delete",
    "projects"."public_builds",
    "projects"."last_repository_check_failed",
    "projects"."last_repository_check_at",
    "projects"."only_allow_merge_if_pipeline_succeeds",
    "projects"."has_external_issue_tracker",
    "projects"."repository_storage",
    "projects"."repository_read_only",
    "projects"."request_access_enabled",
    "projects"."has_external_wiki",
    "projects"."ci_config_path",
    "projects"."lfs_enabled",
    "projects"."description_html",
    "projects"."only_allow_merge_if_all_discussions_are_resolved",
    "projects"."repository_size_limit",
    "projects"."printing_merge_request_link_enabled",
    "projects"."auto_cancel_pending_pipelines",
    "projects"."service_desk_enabled",
    "projects"."cached_markdown_version",
    "projects"."delete_error",
    "projects"."last_repository_updated_at",
    "projects"."disable_overriding_approvers_per_merge_request",
    "projects"."storage_version",
    "projects"."resolve_outdated_diff_discussions",
    "projects"."remote_mirror_available_overridden",
    "projects"."only_mirror_protected_branches",
    "projects"."pull_mirror_available_overridden",
    "projects"."jobs_cache_index",
    "projects"."external_authorization_classification_label",
    "projects"."mirror_overwrites_diverged_branches",
    "projects"."pages_https_only",
    "projects"."external_webhook_token",
    "projects"."packages_enabled",
    "projects"."merge_requests_author_approval",
    "projects"."pool_repository_id",
    "projects"."runners_token_encrypted",
    "projects"."bfg_object_map",
    "projects"."detected_repository_languages",
    "projects"."merge_requests_disable_committers_approval",
    "projects"."require_password_to_approve",
    "projects"."emails_disabled",
    "projects"."max_pages_size",
    "projects"."max_artifacts_size",
    "projects"."remove_source_branch_after_merge",
    "projects"."marked_for_deletion_at",
    "projects"."marked_for_deletion_by_user_id",
    "projects"."autoclose_referenced_issues",
    "projects"."suggestion_commit_message",
    "projects"."project_namespace_id",
    "projects"."hidden"
FROM
    "projects"
WHERE
    "projects"."id" IN ( SELECT DISTINCT
            "events"."project_id"
        FROM
            "events"
        WHERE (action IN (5, 6)
            OR (target_type IN ('MergeRequest', 'Issue')
                AND action IN (1, 3, 7)))
        AND "events"."author_id" = 19
        AND (created_at > '2021-06-17 09:32:18.502508'))
AND "projects"."marked_for_deletion_at" IS NULL
AND "projects"."pending_delete" = FALSE
ORDER BY
    "projects"."id" DESC
Before

For ContributedProjectsFinder.new(@contributor).execute(@contributor)
Plan: https://explain.depesz.com/s/NI7n

SELECT
    "projects"."id",
    "projects"."name",
    "projects"."path",
    "projects"."description",
    "projects"."created_at",
    "projects"."updated_at",
    "projects"."creator_id",
    "projects"."namespace_id",
    "projects"."last_activity_at",
    "projects"."import_url",
    "projects"."visibility_level",
    "projects"."archived",
    "projects"."avatar",
    "projects"."merge_requests_template",
    "projects"."star_count",
    "projects"."merge_requests_rebase_enabled",
    "projects"."import_type",
    "projects"."import_source",
    "projects"."approvals_before_merge",
    "projects"."reset_approvals_on_push",
    "projects"."merge_requests_ff_only_enabled",
    "projects"."issues_template",
    "projects"."mirror",
    "projects"."mirror_user_id",
    "projects"."shared_runners_enabled",
    "projects"."runners_token",
    "projects"."build_coverage_regex",
    "projects"."build_allow_git_fetch",
    "projects"."build_timeout",
    "projects"."mirror_trigger_builds",
    "projects"."pending_delete",
    "projects"."public_builds",
    "projects"."last_repository_check_failed",
    "projects"."last_repository_check_at",
    "projects"."only_allow_merge_if_pipeline_succeeds",
    "projects"."has_external_issue_tracker",
    "projects"."repository_storage",
    "projects"."repository_read_only",
    "projects"."request_access_enabled",
    "projects"."has_external_wiki",
    "projects"."ci_config_path",
    "projects"."lfs_enabled",
    "projects"."description_html",
    "projects"."only_allow_merge_if_all_discussions_are_resolved",
    "projects"."repository_size_limit",
    "projects"."printing_merge_request_link_enabled",
    "projects"."auto_cancel_pending_pipelines",
    "projects"."service_desk_enabled",
    "projects"."cached_markdown_version",
    "projects"."delete_error",
    "projects"."last_repository_updated_at",
    "projects"."disable_overriding_approvers_per_merge_request",
    "projects"."storage_version",
    "projects"."resolve_outdated_diff_discussions",
    "projects"."remote_mirror_available_overridden",
    "projects"."only_mirror_protected_branches",
    "projects"."pull_mirror_available_overridden",
    "projects"."jobs_cache_index",
    "projects"."external_authorization_classification_label",
    "projects"."mirror_overwrites_diverged_branches",
    "projects"."pages_https_only",
    "projects"."external_webhook_token",
    "projects"."packages_enabled",
    "projects"."merge_requests_author_approval",
    "projects"."pool_repository_id",
    "projects"."runners_token_encrypted",
    "projects"."bfg_object_map",
    "projects"."detected_repository_languages",
    "projects"."merge_requests_disable_committers_approval",
    "projects"."require_password_to_approve",
    "projects"."emails_disabled",
    "projects"."max_pages_size",
    "projects"."max_artifacts_size",
    "projects"."remove_source_branch_after_merge",
    "projects"."marked_for_deletion_at",
    "projects"."marked_for_deletion_by_user_id",
    "projects"."autoclose_referenced_issues",
    "projects"."suggestion_commit_message",
    "projects"."project_namespace_id",
    "projects"."hidden"
FROM ((
        SELECT
            "projects"."id",
            "projects"."name",
            "projects"."path",
            "projects"."description",
            "projects"."created_at",
            "projects"."updated_at",
            "projects"."creator_id",
            "projects"."namespace_id",
            "projects"."last_activity_at",
            "projects"."import_url",
            "projects"."visibility_level",
            "projects"."archived",
            "projects"."avatar",
            "projects"."merge_requests_template",
            "projects"."star_count",
            "projects"."merge_requests_rebase_enabled",
            "projects"."import_type",
            "projects"."import_source",
            "projects"."approvals_before_merge",
            "projects"."reset_approvals_on_push",
            "projects"."merge_requests_ff_only_enabled",
            "projects"."issues_template",
            "projects"."mirror",
            "projects"."mirror_user_id",
            "projects"."shared_runners_enabled",
            "projects"."runners_token",
            "projects"."build_coverage_regex",
            "projects"."build_allow_git_fetch",
            "projects"."build_timeout",
            "projects"."mirror_trigger_builds",
            "projects"."pending_delete",
            "projects"."public_builds",
            "projects"."last_repository_check_failed",
            "projects"."last_repository_check_at",
            "projects"."only_allow_merge_if_pipeline_succeeds",
            "projects"."has_external_issue_tracker",
            "projects"."repository_storage",
            "projects"."repository_read_only",
            "projects"."request_access_enabled",
            "projects"."has_external_wiki",
            "projects"."ci_config_path",
            "projects"."lfs_enabled",
            "projects"."description_html",
            "projects"."only_allow_merge_if_all_discussions_are_resolved",
            "projects"."repository_size_limit",
            "projects"."printing_merge_request_link_enabled",
            "projects"."auto_cancel_pending_pipelines",
            "projects"."service_desk_enabled",
            "projects"."cached_markdown_version",
            "projects"."delete_error",
            "projects"."last_repository_updated_at",
            "projects"."disable_overriding_approvers_per_merge_request",
            "projects"."storage_version",
            "projects"."resolve_outdated_diff_discussions",
            "projects"."remote_mirror_available_overridden",
            "projects"."only_mirror_protected_branches",
            "projects"."pull_mirror_available_overridden",
            "projects"."jobs_cache_index",
            "projects"."external_authorization_classification_label",
            "projects"."mirror_overwrites_diverged_branches",
            "projects"."pages_https_only",
            "projects"."external_webhook_token",
            "projects"."packages_enabled",
            "projects"."merge_requests_author_approval",
            "projects"."pool_repository_id",
            "projects"."runners_token_encrypted",
            "projects"."bfg_object_map",
            "projects"."detected_repository_languages",
            "projects"."merge_requests_disable_committers_approval",
            "projects"."require_password_to_approve",
            "projects"."emails_disabled",
            "projects"."max_pages_size",
            "projects"."max_artifacts_size",
            "projects"."remove_source_branch_after_merge",
            "projects"."marked_for_deletion_at",
            "projects"."marked_for_deletion_by_user_id",
            "projects"."autoclose_referenced_issues",
            "projects"."suggestion_commit_message",
            "projects"."project_namespace_id",
            "projects"."hidden"
        FROM
            "projects"
        WHERE
            "projects"."id" IN ( SELECT DISTINCT
                    "events"."project_id"
                FROM
                    "events"
                WHERE (action IN (5, 6)
                    OR (target_type IN ('MergeRequest', 'Issue')
                        AND action IN (1, 3, 7)))
                AND "events"."author_id" = 19
                AND (created_at > '2021-06-17 09:33:45.539431'))
            AND "projects"."marked_for_deletion_at" IS NULL
            AND "projects"."pending_delete" = FALSE
            AND "projects"."id" IN (
                SELECT
                    "projects"."id"
                FROM
                    "projects"
                    INNER JOIN "project_authorizations" ON "projects"."id" = "project_authorizations"."project_id"
                WHERE
                    "project_authorizations"."user_id" = 19))
        UNION (
            SELECT
                "projects"."id",
                "projects"."name",
                "projects"."path",
                "projects"."description",
                "projects"."created_at",
                "projects"."updated_at",
                "projects"."creator_id",
                "projects"."namespace_id",
                "projects"."last_activity_at",
                "projects"."import_url",
                "projects"."visibility_level",
                "projects"."archived",
                "projects"."avatar",
                "projects"."merge_requests_template",
                "projects"."star_count",
                "projects"."merge_requests_rebase_enabled",
                "projects"."import_type",
                "projects"."import_source",
                "projects"."approvals_before_merge",
                "projects"."reset_approvals_on_push",
                "projects"."merge_requests_ff_only_enabled",
                "projects"."issues_template",
                "projects"."mirror",
                "projects"."mirror_user_id",
                "projects"."shared_runners_enabled",
                "projects"."runners_token",
                "projects"."build_coverage_regex",
                "projects"."build_allow_git_fetch",
                "projects"."build_timeout",
                "projects"."mirror_trigger_builds",
                "projects"."pending_delete",
                "projects"."public_builds",
                "projects"."last_repository_check_failed",
                "projects"."last_repository_check_at",
                "projects"."only_allow_merge_if_pipeline_succeeds",
                "projects"."has_external_issue_tracker",
                "projects"."repository_storage",
                "projects"."repository_read_only",
                "projects"."request_access_enabled",
                "projects"."has_external_wiki",
                "projects"."ci_config_path",
                "projects"."lfs_enabled",
                "projects"."description_html",
                "projects"."only_allow_merge_if_all_discussions_are_resolved",
                "projects"."repository_size_limit",
                "projects"."printing_merge_request_link_enabled",
                "projects"."auto_cancel_pending_pipelines",
                "projects"."service_desk_enabled",
                "projects"."cached_markdown_version",
                "projects"."delete_error",
                "projects"."last_repository_updated_at",
                "projects"."disable_overriding_approvers_per_merge_request",
                "projects"."storage_version",
                "projects"."resolve_outdated_diff_discussions",
                "projects"."remote_mirror_available_overridden",
                "projects"."only_mirror_protected_branches",
                "projects"."pull_mirror_available_overridden",
                "projects"."jobs_cache_index",
                "projects"."external_authorization_classification_label",
                "projects"."mirror_overwrites_diverged_branches",
                "projects"."pages_https_only",
                "projects"."external_webhook_token",
                "projects"."packages_enabled",
                "projects"."merge_requests_author_approval",
                "projects"."pool_repository_id",
                "projects"."runners_token_encrypted",
                "projects"."bfg_object_map",
                "projects"."detected_repository_languages",
                "projects"."merge_requests_disable_committers_approval",
                "projects"."require_password_to_approve",
                "projects"."emails_disabled",
                "projects"."max_pages_size",
                "projects"."max_artifacts_size",
                "projects"."remove_source_branch_after_merge",
                "projects"."marked_for_deletion_at",
                "projects"."marked_for_deletion_by_user_id",
                "projects"."autoclose_referenced_issues",
                "projects"."suggestion_commit_message",
                "projects"."project_namespace_id",
                "projects"."hidden"
            FROM
                "projects"
            WHERE
                "projects"."id" IN ( SELECT DISTINCT
                        "events"."project_id"
                    FROM
                        "events"
                    WHERE (action IN (5, 6)
                        OR (target_type IN ('MergeRequest', 'Issue')
                            AND action IN (1, 3, 7)))
                    AND "events"."author_id" = 19
                    AND (created_at > '2021-06-17 09:33:45.540297'))
                AND "projects"."marked_for_deletion_at" IS NULL
                AND "projects"."pending_delete" = FALSE
                AND "projects"."visibility_level" IN (10, 20))) projects
ORDER BY
    "projects"."id" DESC

MR acceptance checklist

This checklist encourages us to confirm any changes have been analyzed to reduce risks in quality, performance, reliability, security, and maintainability.

Related to #365446 (closed)

Edited by Dustin Eckhardt

Merge request reports