Skip to content

Add closed_as_duplicate_of to issues API

Sean Arnold requested to merge 352261-add-closed-as-duplicated-of-to-issues into master

What does this MR do and why?

This adds closed_as_duplicate_of to the _links response of the Issue entity in the REST API.

The closed_as_duplicate_of field returns an API URL to the corresponding issue that the requesting issue was closed as a duplicate of, if the user has access to it.

In order to do this we need add a few queries to the request:

  • Another query to get the Issues via Issue#duplicated_to_id,
  • The issue's from above Project features via their Project (for user permission checks)
Queries for Request before
Project Load (1.0ms)  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"."pending_delete" = FALSE AND "projects"."hidden" = FALSE AND "projects"."id" = 40 LIMIT 1 /*application:web,correlation_id:01G532F0BKRV19PY5XSEFFA5FA,endpoint_id:GET /api/:version/projects/:id/issues,db_config_name:main,line:/lib/api/helpers.rb:125:in `find_project'*/
  ↳ lib/api/helpers.rb:125:in `find_project'
  Project Load (1.0ms)  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" = 40 LIMIT 1 /*application:web,correlation_id:01G532F0BKRV19PY5XSEFFA5FA,endpoint_id:GET /api/:version/projects/:id/issues,db_config_name:main,line:/app/finders/issuable_finder/params.rb:108:in `block in project'*/
  ↳ app/finders/issuable_finder/params.rb:108:in `block in project'
  ProjectFeature Load (0.5ms)  SELECT "project_features".* FROM "project_features" WHERE "project_features"."project_id" = 40 LIMIT 1 /*application:web,correlation_id:01G532F0BKRV19PY5XSEFFA5FA,endpoint_id:GET /api/:version/projects/:id/issues,db_config_name:main,line:/app/policies/project_policy.rb:827:in `access_allowed_to?'*/
  ↳ app/policies/project_policy.rb:827:in `access_allowed_to?'
   (0.6ms)  SELECT COUNT(*) FROM (SELECT 1 AS one FROM "issues" WHERE "issues"."project_id" = 40 LIMIT 10001) subquery_for_count /*application:web,correlation_id:01G532F0BKRV19PY5XSEFFA5FA,endpoint_id:GET /api/:version/projects/:id/issues,db_config_name:main,line:/lib/gitlab/pagination/offset_pagination.rb:31:in `paginate_with_limit_optimization'*/
  ↳ config/initializers/kaminari_active_record_relation_methods_with_limit.rb:31:in `total_count_with_limit'
  Issue Load (0.7ms)  SELECT "issues".* FROM "issues" WHERE "issues"."project_id" = 40 ORDER BY "issues"."created_at" DESC, "issues"."id" DESC LIMIT 20 OFFSET 0 /*application:web,correlation_id:01G532F0BKRV19PY5XSEFFA5FA,endpoint_id:GET /api/:version/projects/:id/issues,db_config_name:main,line:/lib/api/issues.rb:224:in `block (2 levels) in <class:Issues>'*/
  ↳ lib/api/issues.rb:224:in `block (2 levels) in <class:Issues>'
  Timelog Load (0.4ms)  SELECT "timelogs".* FROM "timelogs" WHERE "timelogs"."issue_id" IN (1622, 1621) /*application:web,correlation_id:01G532F0BKRV19PY5XSEFFA5FA,endpoint_id:GET /api/:version/projects/:id/issues,db_config_name:main,line:/lib/api/issues.rb:224:in `block (2 levels) in <class:Issues>'*/
  ↳ lib/api/issues.rb:224:in `block (2 levels) in <class:Issues>'
  User Load (0.6ms)  SELECT "users".* FROM "users" WHERE "users"."id" = 1 /*application:web,correlation_id:01G532F0BKRV19PY5XSEFFA5FA,endpoint_id:GET /api/:version/projects/:id/issues,db_config_name:main,line:/lib/api/issues.rb:224:in `block (2 levels) in <class:Issues>'*/
  ↳ lib/api/issues.rb:224:in `block (2 levels) in <class:Issues>'
  IssueAssignee Load (0.4ms)  SELECT "issue_assignees".* FROM "issue_assignees" WHERE "issue_assignees"."issue_id" IN (1622, 1621) /*application:web,correlation_id:01G532F0BKRV19PY5XSEFFA5FA,endpoint_id:GET /api/:version/projects/:id/issues,db_config_name:main,line:/lib/api/issues.rb:224:in `block (2 levels) in <class:Issues>'*/
  ↳ lib/api/issues.rb:224:in `block (2 levels) in <class:Issues>'
  CACHE User Load (0.1ms)  SELECT "users".* FROM "users" WHERE "users"."id" = 1
  ↳ lib/api/issues.rb:224:in `block (2 levels) in <class:Issues>'
  LabelLink Load (0.4ms)  SELECT "label_links".* FROM "label_links" WHERE "label_links"."target_type" = 'Issue' AND "label_links"."target_id" IN (1622, 1621) /*application:web,correlation_id:01G532F0BKRV19PY5XSEFFA5FA,endpoint_id:GET /api/:version/projects/:id/issues,db_config_name:main,line:/lib/api/issues.rb:224:in `block (2 levels) in <class:Issues>'*/
  ↳ lib/api/issues.rb:224:in `block (2 levels) in <class:Issues>'
  IssuableSeverity Load (0.4ms)  SELECT "issuable_severities".* FROM "issuable_severities" WHERE "issuable_severities"."issue_id" IN (1622, 1621) /*application:web,correlation_id:01G532F0BKRV19PY5XSEFFA5FA,endpoint_id:GET /api/:version/projects/:id/issues,db_config_name:main,line:/lib/api/issues.rb:224:in `block (2 levels) in <class:Issues>'*/
  ↳ lib/api/issues.rb:224:in `block (2 levels) in <class:Issues>'
  Project Load (1.0ms)  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" = 40 /*application:web,correlation_id:01G532F0BKRV19PY5XSEFFA5FA,endpoint_id:GET /api/:version/projects/:id/issues,db_config_name:main,line:/lib/api/issues.rb:224:in `block (2 levels) in <class:Issues>'*/
  ↳ lib/api/issues.rb:224:in `block (2 levels) in <class:Issues>'
  Route Load (0.4ms)  SELECT "routes".* FROM "routes" WHERE "routes"."source_type" = 'Project' AND "routes"."source_id" = 40 /*application:web,correlation_id:01G532F0BKRV19PY5XSEFFA5FA,endpoint_id:GET /api/:version/projects/:id/issues,db_config_name:main,line:/lib/api/issues.rb:224:in `block (2 levels) in <class:Issues>'*/
  ↳ lib/api/issues.rb:224:in `block (2 levels) in <class:Issues>'
  Namespace Load (0.7ms)  SELECT "namespaces"."id", "namespaces"."name", "namespaces"."path", "namespaces"."owner_id", "namespaces"."created_at", "namespaces"."updated_at", "namespaces"."type", "namespaces"."description", "namespaces"."avatar", "namespaces"."membership_lock", "namespaces"."share_with_group_lock", "namespaces"."visibility_level", "namespaces"."request_access_enabled", "namespaces"."ldap_sync_status", "namespaces"."ldap_sync_error", "namespaces"."ldap_sync_last_update_at", "namespaces"."ldap_sync_last_successful_update_at", "namespaces"."ldap_sync_last_sync_at", "namespaces"."description_html", "namespaces"."lfs_enabled", "namespaces"."parent_id", "namespaces"."shared_runners_minutes_limit", "namespaces"."repository_size_limit", "namespaces"."require_two_factor_authentication", "namespaces"."two_factor_grace_period", "namespaces"."cached_markdown_version", "namespaces"."project_creation_level", "namespaces"."runners_token", "namespaces"."file_template_project_id", "namespaces"."saml_discovery_token", "namespaces"."runners_token_encrypted", "namespaces"."custom_project_templates_group_id", "namespaces"."auto_devops_enabled", "namespaces"."extra_shared_runners_minutes_limit", "namespaces"."last_ci_minutes_notification_at", "namespaces"."last_ci_minutes_usage_notification_level", "namespaces"."subgroup_creation_level", "namespaces"."emails_disabled", "namespaces"."max_pages_size", "namespaces"."max_artifacts_size", "namespaces"."mentions_disabled", "namespaces"."default_branch_protection", "namespaces"."unlock_membership_to_ldap", "namespaces"."max_personal_access_token_lifetime", "namespaces"."push_rule_id", "namespaces"."shared_runners_enabled", "namespaces"."allow_descendants_override_disabled_shared_runners", "namespaces"."traversal_ids" FROM "namespaces" WHERE "namespaces"."id" = 1 /*application:web,correlation_id:01G532F0BKRV19PY5XSEFFA5FA,endpoint_id:GET /api/:version/projects/:id/issues,db_config_name:main,line:/lib/api/issues.rb:224:in `block (2 levels) in <class:Issues>'*/
  ↳ lib/api/issues.rb:224:in `block (2 levels) in <class:Issues>'
  Route Load (0.4ms)  SELECT "routes".* FROM "routes" WHERE "routes"."source_type" = 'Namespace' AND "routes"."source_id" = 1 /*application:web,correlation_id:01G532F0BKRV19PY5XSEFFA5FA,endpoint_id:GET /api/:version/projects/:id/issues,db_config_name:main,line:/lib/api/issues.rb:224:in `block (2 levels) in <class:Issues>'*/
  ↳ lib/api/issues.rb:224:in `block (2 levels) in <class:Issues>'
  EpicIssue Load (0.3ms)  SELECT "epic_issues".* FROM "epic_issues" WHERE "epic_issues"."issue_id" IN (1622, 1621) /*application:web,correlation_id:01G532F0BKRV19PY5XSEFFA5FA,endpoint_id:GET /api/:version/projects/:id/issues,db_config_name:main,line:/lib/api/issues.rb:224:in `block (2 levels) in <class:Issues>'*/
  ↳ lib/api/issues.rb:224:in `block (2 levels) in <class:Issues>'
  Upload Load (0.5ms)  SELECT "uploads".* FROM "uploads" WHERE "uploads"."uploader" = 'AvatarUploader' AND "uploads"."path" IN ('uploads/-/system/user/avatar/1/avatar.png', 'user/avatar/1/avatar.png') ORDER BY "uploads"."id" ASC LIMIT 1000 /*application:web,correlation_id:01G532F0BKRV19PY5XSEFFA5FA,endpoint_id:GET /api/:version/projects/:id/issues,db_config_name:main,line:/app/models/concerns/avatarable.rb:120:in `block in retrieve_upload_from_batch'*/
  ↳ app/models/concerns/avatarable.rb:120:in `block in retrieve_upload_from_batch'
  AwardEmoji Load (0.5ms)  SELECT "award_emoji"."name", "award_emoji"."awardable_id", COUNT(*) as count FROM "award_emoji" WHERE (name IN ('thumbsdown','thumbsup') AND awardable_type = 'Issue' AND awardable_id IN (1622,1621)) GROUP BY "award_emoji"."name", "award_emoji"."awardable_id" /*application:web,correlation_id:01G532F0BKRV19PY5XSEFFA5FA,endpoint_id:GET /api/:version/projects/:id/issues,db_config_name:main,line:/lib/gitlab/issuable_metadata.rb:38:in `metadata_for_issuable'*/
  ↳ lib/gitlab/issuable_metadata.rb:38:in `metadata_for_issuable'
  Note Load (0.5ms)  SELECT "notes"."noteable_id", COUNT(*) as count FROM "notes" WHERE "notes"."system" = FALSE AND "notes"."noteable_type" = 'Issue' AND "notes"."noteable_id" IN (1622, 1621) GROUP BY "notes"."noteable_id" /*application:web,correlation_id:01G532F0BKRV19PY5XSEFFA5FA,endpoint_id:GET /api/:version/projects/:id/issues,db_config_name:main,line:/lib/gitlab/issuable_metadata.rb:40:in `metadata_for_issuable'*/
  ↳ lib/gitlab/issuable_metadata.rb:40:in `metadata_for_issuable'
   (0.4ms)  SELECT "merge_requests_closing_issues"."issue_id", COUNT(*) as count FROM "merge_requests_closing_issues" WHERE "merge_requests_closing_issues"."issue_id" IN (1622, 1621) GROUP BY "merge_requests_closing_issues"."issue_id" /*application:web,correlation_id:01G532F0BKRV19PY5XSEFFA5FA,endpoint_id:GET /api/:version/projects/:id/issues,db_config_name:main,line:/app/models/merge_requests_closing_issues.rb:28:in `count_for_collection'*/
  ↳ app/models/merge_requests_closing_issues.rb:28:in `count_for_collection'
  IssueLink Load (0.7ms)  SELECT COUNT(CASE WHEN issues.state_id = 1 then 1 else null end), issue_links.source_id AS blocking_issue_id FROM "issue_links" INNER JOIN "issues" ON "issues"."id" = "issue_links"."target_id" WHERE "issue_links"."link_type" = 1 AND "issue_links"."source_id" IN (1622, 1621) GROUP BY blocking_issue_id /*application:web,correlation_id:01G532F0BKRV19PY5XSEFFA5FA,endpoint_id:GET /api/:version/projects/:id/issues,db_config_name:main,line:/ee/lib/ee/gitlab/issuable_metadata.rb:12:in `block in metadata_for_issuable'*/
  ↳ ee/lib/ee/gitlab/issuable_metadata.rb:12:in `block in metadata_for_issuable'
  Route Load (0.5ms)  SELECT "routes".* FROM "routes" WHERE "routes"."source_id" = 40 AND "routes"."source_type" = 'Project' LIMIT 1 /*application:web,correlation_id:01G532F0BKRV19PY5XSEFFA5FA,endpoint_id:GET /api/:version/projects/:id/issues,db_config_name:main,line:/app/models/concerns/routable.rb:118:in `full_path'*/
  ↳ app/models/concerns/routable.rb:118:in `full_path'
Queries for Request after

New queries separated by line breaks 👇

Project Load (0.9ms)  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" = 40 LIMIT 1 /*application:web,correlation_id:01G533ABEV2C62S7323BDDTKAX,endpoint_id:GET /api/:version/projects/:id/issues,db_config_name:main,line:/app/finders/issuable_finder/params.rb:108:in `block in project'*/
  ↳ app/finders/issuable_finder/params.rb:108:in `block in project'
  ProjectFeature Load (0.4ms)  SELECT "project_features".* FROM "project_features" WHERE "project_features"."project_id" = 40 LIMIT 1 /*application:web,correlation_id:01G533ABEV2C62S7323BDDTKAX,endpoint_id:GET /api/:version/projects/:id/issues,db_config_name:main,line:/app/policies/project_policy.rb:827:in `access_allowed_to?'*/
  ↳ app/policies/project_policy.rb:827:in `access_allowed_to?'
  Namespace Load (0.6ms)  SELECT "namespaces"."id", "namespaces"."name", "namespaces"."path", "namespaces"."owner_id", "namespaces"."created_at", "namespaces"."updated_at", "namespaces"."type", "namespaces"."description", "namespaces"."avatar", "namespaces"."membership_lock", "namespaces"."share_with_group_lock", "namespaces"."visibility_level", "namespaces"."request_access_enabled", "namespaces"."ldap_sync_status", "namespaces"."ldap_sync_error", "namespaces"."ldap_sync_last_update_at", "namespaces"."ldap_sync_last_successful_update_at", "namespaces"."ldap_sync_last_sync_at", "namespaces"."description_html", "namespaces"."lfs_enabled", "namespaces"."parent_id", "namespaces"."shared_runners_minutes_limit", "namespaces"."repository_size_limit", "namespaces"."require_two_factor_authentication", "namespaces"."two_factor_grace_period", "namespaces"."cached_markdown_version", "namespaces"."project_creation_level", "namespaces"."runners_token", "namespaces"."file_template_project_id", "namespaces"."saml_discovery_token", "namespaces"."runners_token_encrypted", "namespaces"."custom_project_templates_group_id", "namespaces"."auto_devops_enabled", "namespaces"."extra_shared_runners_minutes_limit", "namespaces"."last_ci_minutes_notification_at", "namespaces"."last_ci_minutes_usage_notification_level", "namespaces"."subgroup_creation_level", "namespaces"."emails_disabled", "namespaces"."max_pages_size", "namespaces"."max_artifacts_size", "namespaces"."mentions_disabled", "namespaces"."default_branch_protection", "namespaces"."unlock_membership_to_ldap", "namespaces"."max_personal_access_token_lifetime", "namespaces"."push_rule_id", "namespaces"."shared_runners_enabled", "namespaces"."allow_descendants_override_disabled_shared_runners", "namespaces"."traversal_ids" FROM "namespaces" WHERE "namespaces"."id" = 1 LIMIT 1 /*application:web,correlation_id:01G533ABEV2C62S7323BDDTKAX,endpoint_id:GET /api/:version/projects/:id/issues,db_config_name:main,line:/app/models/project.rb:1585:in `owner'*/
  ↳ app/models/project.rb:1585:in `owner'
  User Load (0.6ms)  SELECT "users".* FROM "users" WHERE "users"."id" = 1 LIMIT 1 /*application:web,correlation_id:01G533ABEV2C62S7323BDDTKAX,endpoint_id:GET /api/:version/projects/:id/issues,db_config_name:main,line:/app/models/project.rb:1585:in `owner'*/
  ↳ app/models/project.rb:1585:in `owner'
   (1.0ms)  SELECT COUNT(*) FROM (SELECT 1 AS one FROM "issues" WHERE (NOT EXISTS (SELECT 1 FROM "banned_users" WHERE (issues.author_id = banned_users.user_id))) AND (issues.confidential IS NOT TRUE) AND "issues"."project_id" = 40 LIMIT 10001) subquery_for_count /*application:web,correlation_id:01G533ABEV2C62S7323BDDTKAX,endpoint_id:GET /api/:version/projects/:id/issues,db_config_name:main,line:/lib/gitlab/pagination/offset_pagination.rb:31:in `paginate_with_limit_optimization'*/
  ↳ config/initializers/kaminari_active_record_relation_methods_with_limit.rb:31:in `total_count_with_limit'
  Issue Load (0.9ms)  SELECT "issues".* FROM "issues" WHERE (NOT EXISTS (SELECT 1 FROM "banned_users" WHERE (issues.author_id = banned_users.user_id))) AND (issues.confidential IS NOT TRUE) AND "issues"."project_id" = 40 ORDER BY "issues"."created_at" DESC, "issues"."id" DESC LIMIT 20 OFFSET 0 /*application:web,correlation_id:01G533ABEV2C62S7323BDDTKAX,endpoint_id:GET /api/:version/projects/:id/issues,db_config_name:main,line:/lib/api/issues.rb:224:in `block (2 levels) in <class:Issues>'*/
  ↳ lib/api/issues.rb:224:in `block (2 levels) in <class:Issues>'
  Timelog Load (0.3ms)  SELECT "timelogs".* FROM "timelogs" WHERE "timelogs"."issue_id" IN (1623, 1622, 1621) /*application:web,correlation_id:01G533ABEV2C62S7323BDDTKAX,endpoint_id:GET /api/:version/projects/:id/issues,db_config_name:main,line:/lib/api/issues.rb:224:in `block (2 levels) in <class:Issues>'*/
  ↳ lib/api/issues.rb:224:in `block (2 levels) in <class:Issues>'
  User Load (0.6ms)  SELECT "users".* FROM "users" WHERE "users"."id" = 1 /*application:web,correlation_id:01G533ABEV2C62S7323BDDTKAX,endpoint_id:GET /api/:version/projects/:id/issues,db_config_name:main,line:/lib/api/issues.rb:224:in `block (2 levels) in <class:Issues>'*/
  ↳ lib/api/issues.rb:224:in `block (2 levels) in <class:Issues>'
  IssueAssignee Load (0.3ms)  SELECT "issue_assignees".* FROM "issue_assignees" WHERE "issue_assignees"."issue_id" IN (1623, 1622, 1621) /*application:web,correlation_id:01G533ABEV2C62S7323BDDTKAX,endpoint_id:GET /api/:version/projects/:id/issues,db_config_name:main,line:/lib/api/issues.rb:224:in `block (2 levels) in <class:Issues>'*/
  ↳ lib/api/issues.rb:224:in `block (2 levels) in <class:Issues>'
  CACHE User Load (0.1ms)  SELECT "users".* FROM "users" WHERE "users"."id" = 1
  ↳ lib/api/issues.rb:224:in `block (2 levels) in <class:Issues>'
  LabelLink Load (0.4ms)  SELECT "label_links".* FROM "label_links" WHERE "label_links"."target_type" = 'Issue' AND "label_links"."target_id" IN (1623, 1622, 1621) /*application:web,correlation_id:01G533ABEV2C62S7323BDDTKAX,endpoint_id:GET /api/:version/projects/:id/issues,db_config_name:main,line:/lib/api/issues.rb:224:in `block (2 levels) in <class:Issues>'*/
  ↳ lib/api/issues.rb:224:in `block (2 levels) in <class:Issues>'
  IssuableSeverity Load (0.4ms)  SELECT "issuable_severities".* FROM "issuable_severities" WHERE "issuable_severities"."issue_id" IN (1623, 1622, 1621) /*application:web,correlation_id:01G533ABEV2C62S7323BDDTKAX,endpoint_id:GET /api/:version/projects/:id/issues,db_config_name:main,line:/lib/api/issues.rb:224:in `block (2 levels) in <class:Issues>'*/
  ↳ lib/api/issues.rb:224:in `block (2 levels) in <class:Issues>'
  Project Load (0.9ms)  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" = 40 /*application:web,correlation_id:01G533ABEV2C62S7323BDDTKAX,endpoint_id:GET /api/:version/projects/:id/issues,db_config_name:main,line:/lib/api/issues.rb:224:in `block (2 levels) in <class:Issues>'*/
  ↳ lib/api/issues.rb:224:in `block (2 levels) in <class:Issues>'
  Route Load (0.4ms)  SELECT "routes".* FROM "routes" WHERE "routes"."source_type" = 'Project' AND "routes"."source_id" = 40 /*application:web,correlation_id:01G533ABEV2C62S7323BDDTKAX,endpoint_id:GET /api/:version/projects/:id/issues,db_config_name:main,line:/lib/api/issues.rb:224:in `block (2 levels) in <class:Issues>'*/
  ↳ lib/api/issues.rb:224:in `block (2 levels) in <class:Issues>'
  Namespace Load (0.5ms)  SELECT "namespaces"."id", "namespaces"."name", "namespaces"."path", "namespaces"."owner_id", "namespaces"."created_at", "namespaces"."updated_at", "namespaces"."type", "namespaces"."description", "namespaces"."avatar", "namespaces"."membership_lock", "namespaces"."share_with_group_lock", "namespaces"."visibility_level", "namespaces"."request_access_enabled", "namespaces"."ldap_sync_status", "namespaces"."ldap_sync_error", "namespaces"."ldap_sync_last_update_at", "namespaces"."ldap_sync_last_successful_update_at", "namespaces"."ldap_sync_last_sync_at", "namespaces"."description_html", "namespaces"."lfs_enabled", "namespaces"."parent_id", "namespaces"."shared_runners_minutes_limit", "namespaces"."repository_size_limit", "namespaces"."require_two_factor_authentication", "namespaces"."two_factor_grace_period", "namespaces"."cached_markdown_version", "namespaces"."project_creation_level", "namespaces"."runners_token", "namespaces"."file_template_project_id", "namespaces"."saml_discovery_token", "namespaces"."runners_token_encrypted", "namespaces"."custom_project_templates_group_id", "namespaces"."auto_devops_enabled", "namespaces"."extra_shared_runners_minutes_limit", "namespaces"."last_ci_minutes_notification_at", "namespaces"."last_ci_minutes_usage_notification_level", "namespaces"."subgroup_creation_level", "namespaces"."emails_disabled", "namespaces"."max_pages_size", "namespaces"."max_artifacts_size", "namespaces"."mentions_disabled", "namespaces"."default_branch_protection", "namespaces"."unlock_membership_to_ldap", "namespaces"."max_personal_access_token_lifetime", "namespaces"."push_rule_id", "namespaces"."shared_runners_enabled", "namespaces"."allow_descendants_override_disabled_shared_runners", "namespaces"."traversal_ids" FROM "namespaces" WHERE "namespaces"."id" = 1 /*application:web,correlation_id:01G533ABEV2C62S7323BDDTKAX,endpoint_id:GET /api/:version/projects/:id/issues,db_config_name:main,line:/lib/api/issues.rb:224:in `block (2 levels) in <class:Issues>'*/
  ↳ lib/api/issues.rb:224:in `block (2 levels) in <class:Issues>'
  Route Load (0.4ms)  SELECT "routes".* FROM "routes" WHERE "routes"."source_type" = 'Namespace' AND "routes"."source_id" = 1 /*application:web,correlation_id:01G533ABEV2C62S7323BDDTKAX,endpoint_id:GET /api/:version/projects/:id/issues,db_config_name:main,line:/lib/api/issues.rb:224:in `block (2 levels) in <class:Issues>'*/
  ↳ lib/api/issues.rb:224:in `block (2 levels) in <class:Issues>'
  
  Issue Load (0.6ms)  SELECT "issues".* FROM "issues" WHERE "issues"."id" IN (495, 1621) /*application:web,correlation_id:01G533ABEV2C62S7323BDDTKAX,endpoint_id:GET /api/:version/projects/:id/issues,db_config_name:main,line:/lib/api/issues.rb:224:in `block (2 levels) in <class:Issues>'*/
  ↳ lib/api/issues.rb:224:in `block (2 levels) in <class:Issues>'
  Project Load (0.8ms)  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 (23, 40) /*application:web,correlation_id:01G533ABEV2C62S7323BDDTKAX,endpoint_id:GET /api/:version/projects/:id/issues,db_config_name:main,line:/lib/api/issues.rb:224:in `block (2 levels) in <class:Issues>'*/
  ↳ lib/api/issues.rb:224:in `block (2 levels) in <class:Issues>'
  ProjectFeature Load (0.6ms)  SELECT "project_features".* FROM "project_features" WHERE "project_features"."project_id" IN (40, 23) /*application:web,correlation_id:01G533ABEV2C62S7323BDDTKAX,endpoint_id:GET /api/:version/projects/:id/issues,db_config_name:main,line:/lib/api/issues.rb:224:in `block (2 levels) in <class:Issues>'*/
  ↳ lib/api/issues.rb:224:in `block (2 levels) in <class:Issues>'
  
  EpicIssue Load (0.4ms)  SELECT "epic_issues".* FROM "epic_issues" WHERE "epic_issues"."issue_id" IN (1623, 1622, 1621) /*application:web,correlation_id:01G533ABEV2C62S7323BDDTKAX,endpoint_id:GET /api/:version/projects/:id/issues,db_config_name:main,line:/lib/api/issues.rb:224:in `block (2 levels) in <class:Issues>'*/
  ↳ lib/api/issues.rb:224:in `block (2 levels) in <class:Issues>'
  Upload Load (0.6ms)  SELECT "uploads".* FROM "uploads" WHERE "uploads"."uploader" = 'AvatarUploader' AND "uploads"."path" IN ('uploads/-/system/user/avatar/1/avatar.png', 'user/avatar/1/avatar.png') ORDER BY "uploads"."id" ASC LIMIT 1000 /*application:web,correlation_id:01G533ABEV2C62S7323BDDTKAX,endpoint_id:GET /api/:version/projects/:id/issues,db_config_name:main,line:/app/models/concerns/avatarable.rb:120:in `block in retrieve_upload_from_batch'*/
  ↳ app/models/concerns/avatarable.rb:120:in `block in retrieve_upload_from_batch'
  AwardEmoji Load (0.6ms)  SELECT "award_emoji"."name", "award_emoji"."awardable_id", COUNT(*) as count FROM "award_emoji" WHERE (name IN ('thumbsdown','thumbsup') AND awardable_type = 'Issue' AND awardable_id IN (1623,1622,1621)) GROUP BY "award_emoji"."name", "award_emoji"."awardable_id" /*application:web,correlation_id:01G533ABEV2C62S7323BDDTKAX,endpoint_id:GET /api/:version/projects/:id/issues,db_config_name:main,line:/lib/gitlab/issuable_metadata.rb:38:in `metadata_for_issuable'*/
  ↳ lib/gitlab/issuable_metadata.rb:38:in `metadata_for_issuable'
  Note Load (0.5ms)  SELECT "notes"."noteable_id", COUNT(*) as count FROM "notes" WHERE "notes"."system" = FALSE AND "notes"."noteable_type" = 'Issue' AND "notes"."noteable_id" IN (1623, 1622, 1621) GROUP BY "notes"."noteable_id" /*application:web,correlation_id:01G533ABEV2C62S7323BDDTKAX,endpoint_id:GET /api/:version/projects/:id/issues,db_config_name:main,line:/lib/gitlab/issuable_metadata.rb:40:in `metadata_for_issuable'*/
  ↳ lib/gitlab/issuable_metadata.rb:40:in `metadata_for_issuable'
   (1.3ms)  SELECT "merge_requests_closing_issues"."issue_id", COUNT(*) as count FROM "merge_requests_closing_issues" INNER JOIN "merge_requests" ON "merge_requests"."id" = "merge_requests_closing_issues"."merge_request_id" INNER JOIN project_features ON merge_requests.target_project_id = project_features.project_id WHERE "merge_requests_closing_issues"."issue_id" IN (1623, 1622, 1621) AND (project_features.merge_requests_access_level >= 20) GROUP BY "merge_requests_closing_issues"."issue_id" /*application:web,correlation_id:01G533ABEV2C62S7323BDDTKAX,endpoint_id:GET /api/:version/projects/:id/issues,db_config_name:main,line:/app/models/merge_requests_closing_issues.rb:28:in `count_for_collection'*/
  ↳ app/models/merge_requests_closing_issues.rb:28:in `count_for_collection'
  IssueLink Load (0.7ms)  SELECT COUNT(CASE WHEN issues.state_id = 1 then 1 else null end), issue_links.source_id AS blocking_issue_id FROM "issue_links" INNER JOIN "issues" ON "issues"."id" = "issue_links"."target_id" WHERE "issue_links"."link_type" = 1 AND "issue_links"."source_id" IN (1623, 1622, 1621) GROUP BY blocking_issue_id /*application:web,correlation_id:01G533ABEV2C62S7323BDDTKAX,endpoint_id:GET /api/:version/projects/:id/issues,db_config_name:main,line:/ee/lib/ee/gitlab/issuable_metadata.rb:12:in `block in metadata_for_issuable'*/
  ↳ ee/lib/ee/gitlab/issuable_metadata.rb:12:in `block in metadata_for_issuable'
  Route Load (0.5ms)  SELECT "routes".* FROM "routes" WHERE "routes"."source_id" = 40 AND "routes"."source_type" = 'Project' LIMIT 1 /*application:web,correlation_id:01G533ABEV2C62S7323BDDTKAX,endpoint_id:GET /api/:version/projects/:id/issues,db_config_name:main,line:/app/models/concerns/routable.rb:118:in `full_path'*/
  ↳ app/models/concerns/routable.rb:118:in `full_path'

To ensure we roll this out responsibly as this is a high impact API, I've added this behind a Feature flag (closed_as_duplicate_of_issues_api). Rollout issue here: #352123 (closed).

Screenshots or screen recordings

Screen_Shot_2022-06-09_at_2.35.00_PM

How to set up and validate locally

  1. Set up a project
  2. Create a few issues for it
  3. Close an issue as a duplicate. You can use the /duplicate quick action to do this.
  4. Enable Feature flag (closed_as_duplicate_of_issues_api)
  5. Call the API: <host>/api/v4/projects/<project_id>/issues

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 #352261 (closed)

Edited by Sean Arnold

Merge request reports