Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2020-12-11 15:10:04 +00:00
parent 54f170b699
commit a8704bd33c
47 changed files with 1684 additions and 607 deletions

View File

@ -427,6 +427,7 @@ Scalability/FileUploads:
Graphql/Descriptions:
Enabled: true
AutoCorrect: true
Include:
- 'app/graphql/**/*'
- 'ee/app/graphql/**/*'

View File

@ -671,3 +671,374 @@ RSpec/TimecopTravel:
- 'spec/support/shared_examples/workers/concerns/reenqueuer_shared_examples.rb'
- 'spec/workers/concerns/reenqueuer_spec.rb'
- 'spec/lib/gitlab/analytics/cycle_analytics/median_spec.rb'
Graphql/Descriptions:
Exclude:
- 'app/graphql/mutations/admin/sidekiq_queues/delete_jobs.rb'
- 'app/graphql/mutations/alert_management/base.rb'
- 'app/graphql/mutations/alert_management/http_integration/create.rb'
- 'app/graphql/mutations/alert_management/http_integration/destroy.rb'
- 'app/graphql/mutations/alert_management/http_integration/http_integration_base.rb'
- 'app/graphql/mutations/alert_management/http_integration/reset_token.rb'
- 'app/graphql/mutations/alert_management/http_integration/update.rb'
- 'app/graphql/mutations/alert_management/prometheus_integration/create.rb'
- 'app/graphql/mutations/alert_management/prometheus_integration/prometheus_integration_base.rb'
- 'app/graphql/mutations/alert_management/prometheus_integration/reset_token.rb'
- 'app/graphql/mutations/alert_management/prometheus_integration/update.rb'
- 'app/graphql/mutations/alert_management/update_alert_status.rb'
- 'app/graphql/mutations/award_emojis/base.rb'
- 'app/graphql/mutations/boards/destroy.rb'
- 'app/graphql/mutations/boards/issues/issue_move_list.rb'
- 'app/graphql/mutations/boards/lists/base.rb'
- 'app/graphql/mutations/boards/lists/create.rb'
- 'app/graphql/mutations/boards/lists/update.rb'
- 'app/graphql/mutations/branches/create.rb'
- 'app/graphql/mutations/ci/base.rb'
- 'app/graphql/mutations/ci/pipeline_retry.rb'
- 'app/graphql/mutations/commits/create.rb'
- 'app/graphql/mutations/concerns/mutations/resolves_resource_parent.rb'
- 'app/graphql/mutations/concerns/mutations/resolves_subscription.rb'
- 'app/graphql/mutations/concerns/mutations/spammable_mutation_fields.rb'
- 'app/graphql/mutations/container_expiration_policies/update.rb'
- 'app/graphql/mutations/container_repositories/destroy_tags.rb'
- 'app/graphql/mutations/custom_emoji/create.rb'
- 'app/graphql/mutations/design_management/base.rb'
- 'app/graphql/mutations/design_management/delete.rb'
- 'app/graphql/mutations/design_management/move.rb'
- 'app/graphql/mutations/design_management/upload.rb'
- 'app/graphql/mutations/discussions/toggle_resolve.rb'
- 'app/graphql/mutations/environments/canary_ingress/update.rb'
- 'app/graphql/mutations/issues/base.rb'
- 'app/graphql/mutations/issues/create.rb'
- 'app/graphql/mutations/issues/move.rb'
- 'app/graphql/mutations/issues/set_due_date.rb'
- 'app/graphql/mutations/issues/set_locked.rb'
- 'app/graphql/mutations/issues/update.rb'
- 'app/graphql/mutations/jira_import/import_users.rb'
- 'app/graphql/mutations/jira_import/start.rb'
- 'app/graphql/mutations/labels/create.rb'
- 'app/graphql/mutations/merge_requests/base.rb'
- 'app/graphql/mutations/merge_requests/create.rb'
- 'app/graphql/mutations/metrics/dashboard/annotations/create.rb'
- 'app/graphql/mutations/metrics/dashboard/annotations/delete.rb'
- 'app/graphql/mutations/notes/base.rb'
- 'app/graphql/mutations/notes/create/base.rb'
- 'app/graphql/mutations/notes/create/note.rb'
- 'app/graphql/mutations/notes/destroy.rb'
- 'app/graphql/mutations/notes/reposition_image_diff_note.rb'
- 'app/graphql/mutations/notes/update/base.rb'
- 'app/graphql/mutations/releases/base.rb'
- 'app/graphql/mutations/releases/create.rb'
- 'app/graphql/mutations/releases/update.rb'
- 'app/graphql/mutations/snippets/base.rb'
- 'app/graphql/mutations/snippets/create.rb'
- 'app/graphql/mutations/snippets/destroy.rb'
- 'app/graphql/mutations/snippets/mark_as_spam.rb'
- 'app/graphql/mutations/snippets/update.rb'
- 'app/graphql/mutations/terraform/state/base.rb'
- 'app/graphql/mutations/todos/create.rb'
- 'app/graphql/mutations/todos/mark_all_done.rb'
- 'app/graphql/mutations/todos/mark_done.rb'
- 'app/graphql/mutations/todos/restore.rb'
- 'app/graphql/mutations/todos/restore_many.rb'
- 'app/graphql/resolvers/admin/analytics/instance_statistics/measurements_resolver.rb'
- 'app/graphql/resolvers/alert_management/alert_resolver.rb'
- 'app/graphql/resolvers/alert_management/alert_status_counts_resolver.rb'
- 'app/graphql/resolvers/board_list_issues_resolver.rb'
- 'app/graphql/resolvers/board_lists_resolver.rb'
- 'app/graphql/resolvers/board_resolver.rb'
- 'app/graphql/resolvers/boards_resolver.rb'
- 'app/graphql/resolvers/ci/config_resolver.rb'
- 'app/graphql/resolvers/ci/jobs_resolver.rb'
- 'app/graphql/resolvers/ci/runner_setup_resolver.rb'
- 'app/graphql/resolvers/concerns/issue_resolver_arguments.rb'
- 'app/graphql/resolvers/concerns/resolves_pipelines.rb'
- 'app/graphql/resolvers/concerns/resolves_snippets.rb'
- 'app/graphql/resolvers/concerns/time_frame_arguments.rb'
- 'app/graphql/resolvers/container_repositories_resolver.rb'
- 'app/graphql/resolvers/design_management/design_at_version_resolver.rb'
- 'app/graphql/resolvers/design_management/design_resolver.rb'
- 'app/graphql/resolvers/design_management/designs_resolver.rb'
- 'app/graphql/resolvers/design_management/version/design_at_version_resolver.rb'
- 'app/graphql/resolvers/design_management/version/designs_at_version_resolver.rb'
- 'app/graphql/resolvers/design_management/version_in_collection_resolver.rb'
- 'app/graphql/resolvers/design_management/version_resolver.rb'
- 'app/graphql/resolvers/design_management/versions_resolver.rb'
- 'app/graphql/resolvers/echo_resolver.rb'
- 'app/graphql/resolvers/environments_resolver.rb'
- 'app/graphql/resolvers/error_tracking/sentry_detailed_error_resolver.rb'
- 'app/graphql/resolvers/error_tracking/sentry_error_stack_trace_resolver.rb'
- 'app/graphql/resolvers/error_tracking/sentry_errors_resolver.rb'
- 'app/graphql/resolvers/full_path_resolver.rb'
- 'app/graphql/resolvers/group_members_resolver.rb'
- 'app/graphql/resolvers/group_milestones_resolver.rb'
- 'app/graphql/resolvers/issues_resolver.rb'
- 'app/graphql/resolvers/members_resolver.rb'
- 'app/graphql/resolvers/merge_request_resolver.rb'
- 'app/graphql/resolvers/merge_requests_resolver.rb'
- 'app/graphql/resolvers/metrics/dashboard_resolver.rb'
- 'app/graphql/resolvers/metrics/dashboards/annotation_resolver.rb'
- 'app/graphql/resolvers/milestones_resolver.rb'
- 'app/graphql/resolvers/namespace_projects_resolver.rb'
- 'app/graphql/resolvers/project_members_resolver.rb'
- 'app/graphql/resolvers/project_milestones_resolver.rb'
- 'app/graphql/resolvers/project_pipeline_resolver.rb'
- 'app/graphql/resolvers/projects/jira_projects_resolver.rb'
- 'app/graphql/resolvers/projects/services_resolver.rb'
- 'app/graphql/resolvers/projects_resolver.rb'
- 'app/graphql/resolvers/release_resolver.rb'
- 'app/graphql/resolvers/releases_resolver.rb'
- 'app/graphql/resolvers/snippets/blobs_resolver.rb'
- 'app/graphql/resolvers/snippets_resolver.rb'
- 'app/graphql/resolvers/todo_resolver.rb'
- 'app/graphql/resolvers/tree_resolver.rb'
- 'app/graphql/resolvers/user_resolver.rb'
- 'app/graphql/resolvers/user_starred_projects_resolver.rb'
- 'app/graphql/resolvers/users/snippets_resolver.rb'
- 'app/graphql/resolvers/users_resolver.rb'
- 'app/graphql/types/access_level_type.rb'
- 'app/graphql/types/admin/analytics/instance_statistics/measurement_type.rb'
- 'app/graphql/types/admin/sidekiq_queues/delete_jobs_response_type.rb'
- 'app/graphql/types/alert_management/alert_status_counts_type.rb'
- 'app/graphql/types/alert_management/alert_type.rb'
- 'app/graphql/types/alert_management/integration_type.rb'
- 'app/graphql/types/award_emojis/award_emoji_type.rb'
- 'app/graphql/types/board_list_type.rb'
- 'app/graphql/types/board_type.rb'
- 'app/graphql/types/boards/board_issue_input_base_type.rb'
- 'app/graphql/types/boards/board_issue_input_type.rb'
- 'app/graphql/types/branch_type.rb'
- 'app/graphql/types/ci/analytics_type.rb'
- 'app/graphql/types/ci/config/config_type.rb'
- 'app/graphql/types/ci/config/group_type.rb'
- 'app/graphql/types/ci/config/job_type.rb'
- 'app/graphql/types/ci/config/need_type.rb'
- 'app/graphql/types/ci/config/stage_type.rb'
- 'app/graphql/types/ci/detailed_status_type.rb'
- 'app/graphql/types/ci/group_type.rb'
- 'app/graphql/types/ci/job_artifact_type.rb'
- 'app/graphql/types/ci/job_type.rb'
- 'app/graphql/types/ci/pipeline_type.rb'
- 'app/graphql/types/ci/runner_architecture_type.rb'
- 'app/graphql/types/ci/runner_platform_type.rb'
- 'app/graphql/types/ci/runner_setup_type.rb'
- 'app/graphql/types/ci/stage_type.rb'
- 'app/graphql/types/ci/status_action_type.rb'
- 'app/graphql/types/commit_action_type.rb'
- 'app/graphql/types/commit_type.rb'
- 'app/graphql/types/container_expiration_policy_type.rb'
- 'app/graphql/types/container_repository_details_type.rb'
- 'app/graphql/types/container_repository_type.rb'
- 'app/graphql/types/countable_connection_type.rb'
- 'app/graphql/types/current_user_todos.rb'
- 'app/graphql/types/custom_emoji_type.rb'
- 'app/graphql/types/design_management/design_at_version_type.rb'
- 'app/graphql/types/design_management/design_collection_type.rb'
- 'app/graphql/types/design_management/design_fields.rb'
- 'app/graphql/types/design_management/design_type.rb'
- 'app/graphql/types/design_management/version_type.rb'
- 'app/graphql/types/design_management_type.rb'
- 'app/graphql/types/diff_paths_input_type.rb'
- 'app/graphql/types/diff_refs_type.rb'
- 'app/graphql/types/diff_stats_summary_type.rb'
- 'app/graphql/types/diff_stats_type.rb'
- 'app/graphql/types/environment_type.rb'
- 'app/graphql/types/error_tracking/sentry_detailed_error_type.rb'
- 'app/graphql/types/error_tracking/sentry_error_collection_type.rb'
- 'app/graphql/types/error_tracking/sentry_error_frequency_type.rb'
- 'app/graphql/types/error_tracking/sentry_error_stack_trace_context_type.rb'
- 'app/graphql/types/error_tracking/sentry_error_stack_trace_entry_type.rb'
- 'app/graphql/types/error_tracking/sentry_error_stack_trace_type.rb'
- 'app/graphql/types/error_tracking/sentry_error_tags_type.rb'
- 'app/graphql/types/error_tracking/sentry_error_type.rb'
- 'app/graphql/types/evidence_type.rb'
- 'app/graphql/types/grafana_integration_type.rb'
- 'app/graphql/types/group_invitation_type.rb'
- 'app/graphql/types/group_member_type.rb'
- 'app/graphql/types/group_type.rb'
- 'app/graphql/types/invitation_interface.rb'
- 'app/graphql/types/issue_type.rb'
- 'app/graphql/types/jira_import_type.rb'
- 'app/graphql/types/jira_user_type.rb'
- 'app/graphql/types/jira_users_mapping_input_type.rb'
- 'app/graphql/types/label_type.rb'
- 'app/graphql/types/member_interface.rb'
- 'app/graphql/types/merge_request_connection_type.rb'
- 'app/graphql/types/merge_request_type.rb'
- 'app/graphql/types/metadata_type.rb'
- 'app/graphql/types/metrics/dashboard_type.rb'
- 'app/graphql/types/metrics/dashboards/annotation_type.rb'
- 'app/graphql/types/milestone_stats_type.rb'
- 'app/graphql/types/milestone_type.rb'
- 'app/graphql/types/namespace_type.rb'
- 'app/graphql/types/notes/diff_position_type.rb'
- 'app/graphql/types/notes/discussion_type.rb'
- 'app/graphql/types/notes/note_type.rb'
- 'app/graphql/types/notes/noteable_type.rb'
- 'app/graphql/types/package_type.rb'
- 'app/graphql/types/project_invitation_type.rb'
- 'app/graphql/types/project_member_type.rb'
- 'app/graphql/types/project_statistics_type.rb'
- 'app/graphql/types/project_type.rb'
- 'app/graphql/types/projects/service_type.rb'
- 'app/graphql/types/projects/services/jira_project_type.rb'
- 'app/graphql/types/projects/services/jira_service_type.rb'
- 'app/graphql/types/prometheus_alert_type.rb'
- 'app/graphql/types/query_type.rb'
- 'app/graphql/types/range_input_type.rb'
- 'app/graphql/types/release_asset_link_input_type.rb'
- 'app/graphql/types/release_asset_link_type.rb'
- 'app/graphql/types/release_assets_input_type.rb'
- 'app/graphql/types/release_assets_type.rb'
- 'app/graphql/types/release_links_type.rb'
- 'app/graphql/types/release_source_type.rb'
- 'app/graphql/types/release_type.rb'
- 'app/graphql/types/repository_type.rb'
- 'app/graphql/types/resolvable_interface.rb'
- 'app/graphql/types/root_storage_statistics_type.rb'
- 'app/graphql/types/snippet_type.rb'
- 'app/graphql/types/snippets/blob_action_input_type.rb'
- 'app/graphql/types/snippets/blob_type.rb'
- 'app/graphql/types/snippets/blob_viewer_type.rb'
- 'app/graphql/types/task_completion_status.rb'
- 'app/graphql/types/terraform/state_type.rb'
- 'app/graphql/types/terraform/state_version_type.rb'
- 'app/graphql/types/todo_type.rb'
- 'app/graphql/types/tree/blob_type.rb'
- 'app/graphql/types/tree/entry_type.rb'
- 'app/graphql/types/tree/submodule_type.rb'
- 'app/graphql/types/tree/tree_entry_type.rb'
- 'app/graphql/types/tree/tree_type.rb'
- 'app/graphql/types/user_status_type.rb'
- 'app/graphql/types/user_type.rb'
- 'ee/app/graphql/ee/mutations/boards/issues/issue_move_list.rb'
- 'ee/app/graphql/ee/mutations/boards/lists/create.rb'
- 'ee/app/graphql/ee/mutations/issues/create.rb'
- 'ee/app/graphql/ee/mutations/issues/update.rb'
- 'ee/app/graphql/ee/resolvers/issues_resolver.rb'
- 'ee/app/graphql/ee/resolvers/namespace_projects_resolver.rb'
- 'ee/app/graphql/ee/types/board_list_type.rb'
- 'ee/app/graphql/ee/types/board_type.rb'
- 'ee/app/graphql/ee/types/boards/board_issue_input_base_type.rb'
- 'ee/app/graphql/ee/types/boards/board_issue_input_type.rb'
- 'ee/app/graphql/ee/types/ci/pipeline_type.rb'
- 'ee/app/graphql/ee/types/group_type.rb'
- 'ee/app/graphql/ee/types/issue_connection_type.rb'
- 'ee/app/graphql/ee/types/merge_request_type.rb'
- 'ee/app/graphql/ee/types/namespace_type.rb'
- 'ee/app/graphql/ee/types/project_type.rb'
- 'ee/app/graphql/ee/types/query_type.rb'
- 'ee/app/graphql/mutations/admin/analytics/devops_adoption/segments/delete.rb'
- 'ee/app/graphql/mutations/admin/analytics/devops_adoption/segments/mixins.rb'
- 'ee/app/graphql/mutations/admin/analytics/devops_adoption/segments/update.rb'
- 'ee/app/graphql/mutations/boards/lists/update_limit_metrics.rb'
- 'ee/app/graphql/mutations/boards/update.rb'
- 'ee/app/graphql/mutations/boards/update_epic_user_preferences.rb'
- 'ee/app/graphql/mutations/compliance_management/frameworks/destroy.rb'
- 'ee/app/graphql/mutations/compliance_management/frameworks/update.rb'
- 'ee/app/graphql/mutations/clusters/agent_tokens/create.rb'
- 'ee/app/graphql/mutations/clusters/agent_tokens/delete.rb'
- 'ee/app/graphql/mutations/clusters/agents/create.rb'
- 'ee/app/graphql/mutations/clusters/agents/delete.rb'
- 'ee/app/graphql/mutations/concerns/mutations/shared_epic_arguments.rb'
- 'ee/app/graphql/mutations/epic_tree/reorder.rb'
- 'ee/app/graphql/mutations/epics/add_issue.rb'
- 'ee/app/graphql/mutations/epics/base.rb'
- 'ee/app/graphql/mutations/epics/create.rb'
- 'ee/app/graphql/mutations/epics/set_subscription.rb'
- 'ee/app/graphql/mutations/epics/update.rb'
- 'ee/app/graphql/mutations/incident_management/oncall_schedule/create.rb'
- 'ee/app/graphql/mutations/incident_management/oncall_schedule/destroy.rb'
- 'ee/app/graphql/mutations/incident_management/oncall_schedule/oncall_schedule_base.rb'
- 'ee/app/graphql/mutations/incident_management/oncall_schedule/update.rb'
- 'ee/app/graphql/mutations/instance_security_dashboard/add_project.rb'
- 'ee/app/graphql/mutations/instance_security_dashboard/remove_project.rb'
- 'ee/app/graphql/mutations/issues/common_ee_mutation_arguments.rb'
- 'ee/app/graphql/mutations/issues/promote_to_epic.rb'
- 'ee/app/graphql/mutations/issues/set_weight.rb'
- 'ee/app/graphql/mutations/iterations/create.rb'
- 'ee/app/graphql/mutations/namespaces/base.rb'
- 'ee/app/graphql/mutations/quality_management/test_cases/create.rb'
- 'ee/app/graphql/mutations/requirements_management/base_requirement.rb'
- 'ee/app/graphql/mutations/requirements_management/update_requirement.rb'
- 'ee/app/graphql/mutations/security/ci_configuration/configure_sast.rb'
- 'ee/app/graphql/mutations/vulnerabilities/confirm.rb'
- 'ee/app/graphql/mutations/vulnerabilities/dismiss.rb'
- 'ee/app/graphql/mutations/vulnerabilities/resolve.rb'
- 'ee/app/graphql/mutations/vulnerabilities/revert_to_detected.rb'
- 'ee/app/graphql/resolvers/board_groupings/epics_resolver.rb'
- 'ee/app/graphql/resolvers/boards/epic_boards_resolver.rb'
- 'ee/app/graphql/resolvers/ci/code_coverage_activities_resolver.rb'
- 'ee/app/graphql/resolvers/clusters/agents_resolver.rb'
- 'ee/app/graphql/resolvers/dast_site_profile_resolver.rb'
- 'ee/app/graphql/resolvers/dast_site_validation_resolver.rb'
- 'ee/app/graphql/resolvers/epics_resolver.rb'
- 'ee/app/graphql/resolvers/geo/registries_resolver.rb'
- 'ee/app/graphql/resolvers/requirements_management/requirements_resolver.rb'
- 'ee/app/graphql/resolvers/requirements_management/test_reports_resolver.rb'
- 'ee/app/graphql/resolvers/timelog_resolver.rb'
- 'ee/app/graphql/resolvers/vulnerabilities/issue_links_resolver.rb'
- 'ee/app/graphql/resolvers/vulnerabilities_count_per_day_resolver.rb'
- 'ee/app/graphql/resolvers/vulnerabilities_grade_resolver.rb'
- 'ee/app/graphql/resolvers/vulnerabilities_history_resolver.rb'
- 'ee/app/graphql/resolvers/vulnerabilities_resolver.rb'
- 'ee/app/graphql/resolvers/vulnerability_severities_count_resolver.rb'
- 'ee/app/graphql/types/admin/analytics/devops_adoption/segment_type.rb'
- 'ee/app/graphql/types/admin/analytics/devops_adoption/snapshot_type.rb'
- 'ee/app/graphql/types/boards/board_epic_type.rb'
- 'ee/app/graphql/types/boards/epic_board_type.rb'
- 'ee/app/graphql/types/boards/epic_user_preferences_type.rb'
- 'ee/app/graphql/types/burnup_chart_daily_totals_type.rb'
- 'ee/app/graphql/types/ci_configuration/sast/analyzers_entity_input_type.rb'
- 'ee/app/graphql/types/ci_configuration/sast/analyzers_entity_type.rb'
- 'ee/app/graphql/types/ci_configuration/sast/entity_input_type.rb'
- 'ee/app/graphql/types/ci_configuration/sast/input_type.rb'
- 'ee/app/graphql/types/clusters/agent_token_type.rb'
- 'ee/app/graphql/types/clusters/agent_type.rb'
- 'ee/app/graphql/types/compliance_management/compliance_framework_type.rb'
- 'ee/app/graphql/types/dast_scanner_profile_type.rb'
- 'ee/app/graphql/types/dast_site_profile_type.rb'
- 'ee/app/graphql/types/dast_site_validation_type.rb'
- 'ee/app/graphql/types/epic_descendant_count_type.rb'
- 'ee/app/graphql/types/epic_descendant_weight_sum_type.rb'
- 'ee/app/graphql/types/epic_health_status_type.rb'
- 'ee/app/graphql/types/epic_issue_type.rb'
- 'ee/app/graphql/types/epic_tree/epic_tree_node_input_type.rb'
- 'ee/app/graphql/types/epic_type.rb'
- 'ee/app/graphql/types/external_issue_type.rb'
- 'ee/app/graphql/types/geo/geo_node_type.rb'
- 'ee/app/graphql/types/geo/merge_request_diff_registry_type.rb'
- 'ee/app/graphql/types/geo/package_file_registry_type.rb'
- 'ee/app/graphql/types/geo/snippet_repository_registry_type.rb'
- 'ee/app/graphql/types/geo/terraform_state_version_registry_type.rb'
- 'ee/app/graphql/types/group_stats_type.rb'
- 'ee/app/graphql/types/incident_management/oncall_schedule_type.rb'
- 'ee/app/graphql/types/instance_security_dashboard_type.rb'
- 'ee/app/graphql/types/iteration_type.rb'
- 'ee/app/graphql/types/metric_image_type.rb'
- 'ee/app/graphql/types/requirements_management/requirement_states_count_type.rb'
- 'ee/app/graphql/types/requirements_management/requirement_type.rb'
- 'ee/app/graphql/types/requirements_management/test_report_type.rb'
- 'ee/app/graphql/types/scanned_resource_type.rb'
- 'ee/app/graphql/types/security_report_summary_section_type.rb'
- 'ee/app/graphql/types/time_report_stats_type.rb'
- 'ee/app/graphql/types/timebox_metrics_type.rb'
- 'ee/app/graphql/types/timebox_report_interface.rb'
- 'ee/app/graphql/types/timebox_report_type.rb'
- 'ee/app/graphql/types/timelog_type.rb'
- 'ee/app/graphql/types/vulnerabilities_count_by_day_and_severity_type.rb'
- 'ee/app/graphql/types/vulnerabilities_count_by_day_type.rb'
- 'ee/app/graphql/types/vulnerability/external_issue_link_type.rb'
- 'ee/app/graphql/types/vulnerability/issue_link_type.rb'
- 'ee/app/graphql/types/vulnerability_identifier_type.rb'
- 'ee/app/graphql/types/vulnerability_location/container_scanning_type.rb'
- 'ee/app/graphql/types/vulnerability_location/coverage_fuzzing_type.rb'
- 'ee/app/graphql/types/vulnerability_location/dast_type.rb'
- 'ee/app/graphql/types/vulnerability_location/dependency_scanning_type.rb'
- 'ee/app/graphql/types/vulnerability_location/sast_type.rb'
- 'ee/app/graphql/types/vulnerability_location/secret_detection_type.rb'
- 'ee/app/graphql/types/vulnerability_scanner_type.rb'
- 'ee/app/graphql/types/vulnerability_type.rb'
- 'ee/app/graphql/types/vulnerable_dependency_type.rb'
- 'ee/app/graphql/types/vulnerable_package_type.rb'
- 'ee/app/graphql/types/vulnerable_projects_by_grade_type.rb'

View File

@ -1 +1 @@
13.13.0
13.14.0

View File

@ -1,4 +1,5 @@
import { sortBy } from 'lodash';
import axios from '~/lib/utils/axios_utils';
import { ListType } from './constants';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import boardsStore from '~/boards/stores/boards_store';
@ -108,10 +109,20 @@ export function moveIssueListHelper(issue, fromList, toList) {
return updatedIssue;
}
export function getBoardsPath(endpoint, board) {
const path = `${endpoint}${board.id ? `/${board.id}` : ''}.json`;
if (board.id) {
return axios.put(path, { board });
}
return axios.post(path, { board });
}
export default {
getMilestone,
formatIssue,
formatListIssues,
fullBoardId,
fullLabelId,
getBoardsPath,
};

View File

@ -1,11 +1,14 @@
<script>
import { GlModal } from '@gitlab/ui';
import { pick } from 'lodash';
import { __, s__ } from '~/locale';
import { deprecatedCreateFlash as Flash } from '~/flash';
import { visitUrl } from '~/lib/utils/url_utility';
import boardsStore from '~/boards/stores/boards_store';
import { fullBoardId, getBoardsPath } from '../boards_util';
import BoardConfigurationOptions from './board_configuration_options.vue';
import createBoardMutation from '../graphql/board.mutation.graphql';
const boardDefaults = {
id: false,
@ -81,11 +84,19 @@ export default {
required: false,
default: false,
},
currentBoard: {
type: Object,
required: true,
},
},
inject: {
endpoints: {
default: {},
},
},
data() {
return {
board: { ...boardDefaults, ...this.currentBoard },
currentBoard: boardsStore.state.currentBoard,
currentPage: boardsStore.state.currentPage,
isLoading: false,
};
@ -143,6 +154,15 @@ export default {
text: this.$options.i18n.cancelButtonText,
};
},
boardPayload() {
const { assignee, milestone, labels } = this.board;
return {
...this.board,
assignee_id: assignee?.id,
milestone_id: milestone?.id,
label_ids: labels.length ? labels.map(b => b.id) : [''],
};
},
},
mounted() {
this.resetFormState();
@ -151,6 +171,31 @@ export default {
}
},
methods: {
callBoardMutation(id) {
return this.$apollo.mutate({
mutation: createBoardMutation,
variables: {
...pick(this.boardPayload, ['hideClosedList', 'hideBacklogList']),
id,
},
});
},
async updateBoard() {
const responses = await Promise.all([
// Remove unnecessary REST API call when https://gitlab.com/gitlab-org/gitlab/-/issues/282299#note_462996301 is resolved
getBoardsPath(this.endpoints.boardsEndpoint, this.boardPayload),
this.callBoardMutation(fullBoardId(this.boardPayload.id)),
]);
return responses[0].data;
},
async createBoard() {
// TODO: change this to use `createBoard` mutation https://gitlab.com/gitlab-org/gitlab/-/issues/292466 is resolved
const boardData = await getBoardsPath(this.endpoints.boardsEndpoint, this.boardPayload);
await this.callBoardMutation(fullBoardId(boardData.data.id));
return boardData.data || boardData;
},
submit() {
if (this.board.name.length === 0) return;
this.isLoading = true;
@ -166,21 +211,9 @@ export default {
this.isLoading = false;
});
} else {
boardsStore
.createBoard(this.board)
.then(resp => {
// This handles 2 use cases
// - In create call we only get one parameter, the new board
// - In update call, due to Promise.all, we get REST response in
// array index 0
if (Array.isArray(resp)) {
return resp[0].data;
}
return resp.data ? resp.data : resp;
})
const boardAction = this.boardPayload.id ? this.updateBoard : this.createBoard;
boardAction()
.then(data => {
this.isLoading = false;
visitUrl(data.board_path);
})
.catch(() => {
@ -219,9 +252,11 @@ export default {
@close="cancel"
@hide.prevent
>
<p v-if="isDeleteForm">{{ $options.i18n.deleteConfirmationMessage }}</p>
<form v-else class="js-board-config-modal" @submit.prevent>
<div v-if="!readonly" class="gl-mb-5">
<p v-if="isDeleteForm" data-testid="delete-confirmation-message">
{{ $options.i18n.deleteConfirmationMessage }}
</p>
<form v-else class="js-board-config-modal" data-testid="board-form-wrapper" @submit.prevent>
<div v-if="!readonly" class="gl-mb-5" data-testid="board-form">
<label class="gl-font-weight-bold gl-font-lg" for="board-new-name">
{{ $options.i18n.titleFieldLabel }}
</label>

View File

@ -345,6 +345,7 @@ export default {
:scoped-issue-board-feature-enabled="scopedIssueBoardFeatureEnabled"
:weights="weights"
:enable-scoped-labels="enabledScopedLabels"
:current-board="currentBoard"
/>
</span>
</div>

View File

@ -349,5 +349,8 @@ export default () => {
toggleEpicsSwimlanes();
}
mountMultipleBoardsSwitcher();
mountMultipleBoardsSwitcher({
boardsEndpoint: $boardApp.dataset.boardsEndpoint,
recentBoardsEndpoint: $boardApp.dataset.recentBoardsEndpoint,
});
};

View File

@ -10,7 +10,7 @@ const apolloProvider = new VueApollo({
defaultClient: createDefaultClient(),
});
export default () => {
export default (endpoints = {}) => {
const boardsSwitcherElement = document.getElementById('js-multiple-boards-switcher');
return new Vue({
el: boardsSwitcherElement,
@ -35,6 +35,9 @@ export default () => {
return { boardsSelectorProps };
},
provide: {
endpoints,
},
render(createElement) {
return createElement(BoardsSelector, {
props: this.boardsSelectorProps,

View File

@ -1,7 +1,7 @@
/* eslint-disable no-shadow, no-param-reassign,consistent-return */
/* global List */
/* global ListIssue */
import { sortBy, pick } from 'lodash';
import { sortBy } from 'lodash';
import Vue from 'vue';
import BoardsStoreEE from 'ee_else_ce/boards/stores/boards_store_ee';
import {
@ -21,8 +21,6 @@ import ListLabel from '../models/label';
import ListAssignee from '../models/assignee';
import ListMilestone from '../models/milestone';
import createBoardMutation from '../graphql/board.mutation.graphql';
const PER_PAGE = 20;
export const gqlClient = createDefaultClient();
@ -759,52 +757,6 @@ const boardsStore = {
return axios.get(this.state.endpoints.recentBoardsEndpoint);
},
createBoard(board) {
const boardPayload = { ...board };
boardPayload.label_ids = (board.labels || []).map(b => b.id);
if (boardPayload.label_ids.length === 0) {
boardPayload.label_ids = [''];
}
if (boardPayload.assignee) {
boardPayload.assignee_id = boardPayload.assignee.id;
}
if (boardPayload.milestone) {
boardPayload.milestone_id = boardPayload.milestone.id;
}
if (boardPayload.id) {
const input = {
...pick(boardPayload, ['hideClosedList', 'hideBacklogList']),
id: this.generateBoardGid(boardPayload.id),
};
return Promise.all([
axios.put(this.generateBoardsPath(boardPayload.id), { board: boardPayload }),
gqlClient.mutate({
mutation: createBoardMutation,
variables: input,
}),
]);
}
return axios
.post(this.generateBoardsPath(), { board: boardPayload })
.then(resp => resp.data)
.then(data => {
gqlClient.mutate({
mutation: createBoardMutation,
variables: {
...pick(boardPayload, ['hideClosedList', 'hideBacklogList']),
id: this.generateBoardGid(data.id),
},
});
return data;
});
},
deleteBoard({ id }) {
return axios.delete(this.generateBoardsPath(id));
},

View File

@ -134,15 +134,17 @@ export default {
@after-enter="afterEndTransition"
>
<div v-if="isCompact" ref="compactEl" class="commit-form-compact">
<button
<gl-button
:disabled="!someUncommittedChanges"
type="button"
class="btn btn-primary btn-sm btn-block qa-begin-commit-button"
category="primary"
variant="info"
block
class="qa-begin-commit-button"
data-testid="begin-commit-button"
@click="beginCommit"
>
{{ __('Commit…') }}
</button>
</gl-button>
<p class="text-center bold">{{ overviewText }}</p>
</div>
<form v-else ref="formEl" @submit.prevent.stop="commit">
@ -158,28 +160,21 @@ export default {
<gl-button
:loading="submitCommitLoading"
class="float-left qa-commit-button"
size="small"
category="primary"
variant="success"
@click="commit"
>
{{ __('Commit') }}
</gl-button>
<button
v-if="!discardDraftButtonDisabled"
type="button"
class="btn btn-default btn-sm float-right"
@click="discardDraft"
>
<gl-button v-if="!discardDraftButtonDisabled" class="float-right" @click="discardDraft">
{{ __('Discard draft') }}
</button>
</gl-button>
<gl-button
v-else
type="button"
class="float-right"
category="secondary"
variant="default"
size="small"
@click="toggleIsCompact"
>
{{ __('Collapse') }}

View File

@ -1,15 +1,29 @@
<script>
import { GlSprintf } from '@gitlab/ui';
import { sprintf } from '~/locale';
import TitleArea from '~/vue_shared/components/registry/title_area.vue';
import { DETAILS_PAGE_TITLE } from '../../constants/index';
import MetadataItem from '~/vue_shared/components/registry/metadata_item.vue';
import timeagoMixin from '~/vue_shared/mixins/timeago';
import { DETAILS_PAGE_TITLE, UPDATED_AT } from '../../constants/index';
export default {
components: { GlSprintf, TitleArea },
components: { GlSprintf, TitleArea, MetadataItem },
mixins: [timeagoMixin],
props: {
imageName: {
type: String,
required: false,
default: '',
image: {
type: Object,
required: true,
},
},
computed: {
visibilityIcon() {
return this.image?.project?.visibility === 'public' ? 'eye' : 'eye-slash';
},
timeAgo() {
return this.timeFormatted(this.image.updatedAt);
},
updatedText() {
return sprintf(UPDATED_AT, { time: this.timeAgo });
},
},
i18n: {
@ -23,9 +37,17 @@ export default {
<template #title>
<gl-sprintf :message="$options.i18n.DETAILS_PAGE_TITLE">
<template #imageName>
{{ imageName }}
{{ image.name }}
</template>
</gl-sprintf>
</template>
<template #metadata-updated>
<metadata-item
:icon="visibilityIcon"
:text="updatedText"
size="xl"
data-testid="updated-and-visibility"
/>
</template>
</title-area>
</template>

View File

@ -56,6 +56,8 @@ export const MISSING_MANIFEST_WARNING_TOOLTIP = s__(
'ContainerRegistry|Invalid tag: missing manifest digest',
);
export const UPDATED_AT = s__('ContainerRegistry|Last updated %{time}');
export const NOT_AVAILABLE_TEXT = __('N/A');
export const NOT_AVAILABLE_SIZE = __('0 bytes');
// Parameters

View File

@ -15,6 +15,7 @@ query getContainerRepositoryDetails(
location
canDelete
createdAt
updatedAt
tagsCount
expirationPolicyStartedAt
tags(after: $after, before: $before, first: $first, last: $last) {
@ -33,5 +34,8 @@ query getContainerRepositoryDetails(
...PageInfo
}
}
project {
visibility
}
}
}

View File

@ -183,7 +183,7 @@ export default {
@dismiss="dismissPartialCleanupWarning = true"
/>
<details-header :image-name="image.name" />
<details-header :image="image" />
<tags-loader v-if="isLoading" />
<template v-else>

View File

@ -78,7 +78,7 @@ module Types
attr_reader :feature_flag
def feature_documentation_message(key, description)
"#{description}. Available only when feature flag `#{key}` is enabled"
"#{description} Available only when feature flag `#{key}` is enabled."
end
def check_feature_flag(args)

View File

@ -23,8 +23,8 @@ module GitlabStyleDeprecations
raise ArgumentError, '`milestone` must be a `String`' unless milestone.is_a?(String)
deprecated_in = "Deprecated in #{milestone}"
kwargs[:deprecation_reason] = "#{reason}. #{deprecated_in}"
kwargs[:description] += ". #{deprecated_in}: #{reason}" if kwargs[:description]
kwargs[:deprecation_reason] = "#{reason}. #{deprecated_in}."
kwargs[:description] += " #{deprecated_in}: #{reason}." if kwargs[:description]
kwargs
end

View File

@ -40,6 +40,7 @@
.settings-content
= render 'shared/badges/badge_settings'
= render_if_exists 'groups/compliance_frameworks', expanded: expanded
= render_if_exists 'groups/custom_project_templates_setting'
= render_if_exists 'groups/templates_setting', expanded: expanded

View File

@ -0,0 +1,5 @@
---
title: Add visibility and last updated image repository details
merge_request: 49703
author:
type: changed

View File

@ -0,0 +1,5 @@
---
title: Migrate Bootstrap button to GitLab UI GlButton in IDE
merge_request: 39988
author:
type: changed

View File

@ -0,0 +1,5 @@
---
title: Migrate `createBoard` away from boardStore
merge_request: 49450
author:
type: changed

View File

@ -0,0 +1,5 @@
---
title: Bump gitlab-shell version to v13.14.0
merge_request: 49810
author:
type: other

View File

@ -537,22 +537,22 @@ enum AlertManagementAlertSort {
"""
Created at ascending order
"""
created_asc @deprecated(reason: "Use CREATED_ASC. Deprecated in 13.5")
created_asc @deprecated(reason: "Use CREATED_ASC. Deprecated in 13.5.")
"""
Created at descending order
"""
created_desc @deprecated(reason: "Use CREATED_DESC. Deprecated in 13.5")
created_desc @deprecated(reason: "Use CREATED_DESC. Deprecated in 13.5.")
"""
Updated at ascending order
"""
updated_asc @deprecated(reason: "Use UPDATED_ASC. Deprecated in 13.5")
updated_asc @deprecated(reason: "Use UPDATED_ASC. Deprecated in 13.5.")
"""
Updated at descending order
"""
updated_desc @deprecated(reason: "Use UPDATED_DESC. Deprecated in 13.5")
updated_desc @deprecated(reason: "Use UPDATED_DESC. Deprecated in 13.5.")
}
"""
@ -1435,7 +1435,7 @@ type BoardEpic implements CurrentUserTodos & Noteable {
"""
List items overlapping a time frame defined by startDate..endDate (if one
date is provided, both must be present). Deprecated in 13.5: Use timeframe.end
date is provided, both must be present) Deprecated in 13.5: Use timeframe.end.
"""
endDate: Time
@ -1491,8 +1491,8 @@ type BoardEpic implements CurrentUserTodos & Noteable {
"""
List items overlapping a time frame defined by startDate..endDate (if one
date is provided, both must be present). Deprecated in 13.5: Use
timeframe.start
date is provided, both must be present) Deprecated in 13.5: Use
timeframe.start.
"""
startDate: Time
@ -4925,9 +4925,9 @@ type DastScannerProfile {
editPath: String
"""
ID of the DAST scanner profile. Deprecated in 13.6: Use `id`
ID of the DAST scanner profile Deprecated in 13.6: Use `id`.
"""
globalId: DastScannerProfileID! @deprecated(reason: "Use `id`. Deprecated in 13.6")
globalId: DastScannerProfileID! @deprecated(reason: "Use `id`. Deprecated in 13.6.")
"""
ID of the DAST scanner profile
@ -5049,9 +5049,9 @@ type DastScannerProfileCreatePayload {
errors: [String!]!
"""
ID of the scanner profile.. Deprecated in 13.6: Use `id`
ID of the scanner profile. Deprecated in 13.6: Use `id`.
"""
globalId: DastScannerProfileID @deprecated(reason: "Use `id`. Deprecated in 13.6")
globalId: DastScannerProfileID @deprecated(reason: "Use `id`. Deprecated in 13.6.")
"""
ID of the scanner profile.
@ -7540,7 +7540,7 @@ type Epic implements CurrentUserTodos & Noteable {
"""
List items overlapping a time frame defined by startDate..endDate (if one
date is provided, both must be present). Deprecated in 13.5: Use timeframe.end
date is provided, both must be present) Deprecated in 13.5: Use timeframe.end.
"""
endDate: Time
@ -7596,8 +7596,8 @@ type Epic implements CurrentUserTodos & Noteable {
"""
List items overlapping a time frame defined by startDate..endDate (if one
date is provided, both must be present). Deprecated in 13.5: Use
timeframe.start
date is provided, both must be present) Deprecated in 13.5: Use
timeframe.start.
"""
startDate: Time
@ -9213,8 +9213,8 @@ type Group {
): CodeCoverageActivityConnection
"""
Compliance frameworks available to projects in this namespace. Available only
when feature flag `ff_custom_compliance_frameworks` is enabled
Compliance frameworks available to projects in this namespace Available only
when feature flag `ff_custom_compliance_frameworks` is enabled.
"""
complianceFrameworks(
"""
@ -9279,7 +9279,7 @@ type Group {
containsLockedProjects: Boolean!
"""
Custom emoji within this namespace. Available only when feature flag `custom_emoji` is enabled
Custom emoji within this namespace Available only when feature flag `custom_emoji` is enabled.
"""
customEmoji(
"""
@ -9329,7 +9329,7 @@ type Group {
"""
List items overlapping a time frame defined by startDate..endDate (if one
date is provided, both must be present). Deprecated in 13.5: Use timeframe.end
date is provided, both must be present) Deprecated in 13.5: Use timeframe.end.
"""
endDate: Time
@ -9375,8 +9375,8 @@ type Group {
"""
List items overlapping a time frame defined by startDate..endDate (if one
date is provided, both must be present). Deprecated in 13.5: Use
timeframe.start
date is provided, both must be present) Deprecated in 13.5: Use
timeframe.start.
"""
startDate: Time
@ -9447,7 +9447,7 @@ type Group {
"""
List items overlapping a time frame defined by startDate..endDate (if one
date is provided, both must be present). Deprecated in 13.5: Use timeframe.end
date is provided, both must be present) Deprecated in 13.5: Use timeframe.end.
"""
endDate: Time
@ -9503,8 +9503,8 @@ type Group {
"""
List items overlapping a time frame defined by startDate..endDate (if one
date is provided, both must be present). Deprecated in 13.5: Use
timeframe.start
date is provided, both must be present) Deprecated in 13.5: Use
timeframe.start.
"""
startDate: Time
@ -9730,7 +9730,7 @@ type Group {
"""
List items overlapping a time frame defined by startDate..endDate (if one
date is provided, both must be present). Deprecated in 13.5: Use timeframe.end
date is provided, both must be present) Deprecated in 13.5: Use timeframe.end.
"""
endDate: Time
@ -9761,8 +9761,8 @@ type Group {
"""
List items overlapping a time frame defined by startDate..endDate (if one
date is provided, both must be present). Deprecated in 13.5: Use
timeframe.start
date is provided, both must be present) Deprecated in 13.5: Use
timeframe.start.
"""
startDate: Time
@ -9938,7 +9938,7 @@ type Group {
"""
List items overlapping a time frame defined by startDate..endDate (if one
date is provided, both must be present). Deprecated in 13.5: Use timeframe.end
date is provided, both must be present) Deprecated in 13.5: Use timeframe.end.
"""
endDate: Time
@ -9969,8 +9969,8 @@ type Group {
"""
List items overlapping a time frame defined by startDate..endDate (if one
date is provided, both must be present). Deprecated in 13.5: Use
timeframe.start
date is provided, both must be present) Deprecated in 13.5: Use
timeframe.start.
"""
startDate: Time
@ -10272,7 +10272,7 @@ type Group {
"""
Number of vulnerabilities per severity level, per day, for the projects in the
group and its subgroups. Deprecated in 13.3: Use `vulnerabilitiesCountByDay`
group and its subgroups Deprecated in 13.3: Use `vulnerabilitiesCountByDay`.
"""
vulnerabilitiesCountByDayAndSeverity(
"""
@ -10304,7 +10304,7 @@ type Group {
First day for which to fetch vulnerability history
"""
startDate: ISO8601Date!
): VulnerabilitiesCountByDayAndSeverityConnection @deprecated(reason: "Use `vulnerabilitiesCountByDay`. Deprecated in 13.3")
): VulnerabilitiesCountByDayAndSeverityConnection @deprecated(reason: "Use `vulnerabilitiesCountByDay`. Deprecated in 13.3.")
"""
Represents vulnerable project counts for each grade
@ -12066,22 +12066,22 @@ enum IssueSort {
"""
Created at ascending order
"""
created_asc @deprecated(reason: "Use CREATED_ASC. Deprecated in 13.5")
created_asc @deprecated(reason: "Use CREATED_ASC. Deprecated in 13.5.")
"""
Created at descending order
"""
created_desc @deprecated(reason: "Use CREATED_DESC. Deprecated in 13.5")
created_desc @deprecated(reason: "Use CREATED_DESC. Deprecated in 13.5.")
"""
Updated at ascending order
"""
updated_asc @deprecated(reason: "Use UPDATED_ASC. Deprecated in 13.5")
updated_asc @deprecated(reason: "Use UPDATED_ASC. Deprecated in 13.5.")
"""
Updated at descending order
"""
updated_desc @deprecated(reason: "Use UPDATED_DESC. Deprecated in 13.5")
updated_desc @deprecated(reason: "Use UPDATED_DESC. Deprecated in 13.5.")
}
"""
@ -14124,22 +14124,22 @@ enum MergeRequestSort {
"""
Created at ascending order
"""
created_asc @deprecated(reason: "Use CREATED_ASC. Deprecated in 13.5")
created_asc @deprecated(reason: "Use CREATED_ASC. Deprecated in 13.5.")
"""
Created at descending order
"""
created_desc @deprecated(reason: "Use CREATED_DESC. Deprecated in 13.5")
created_desc @deprecated(reason: "Use CREATED_DESC. Deprecated in 13.5.")
"""
Updated at ascending order
"""
updated_asc @deprecated(reason: "Use UPDATED_ASC. Deprecated in 13.5")
updated_asc @deprecated(reason: "Use UPDATED_ASC. Deprecated in 13.5.")
"""
Updated at descending order
"""
updated_desc @deprecated(reason: "Use UPDATED_DESC. Deprecated in 13.5")
updated_desc @deprecated(reason: "Use UPDATED_DESC. Deprecated in 13.5.")
}
"""
@ -14515,7 +14515,7 @@ enum MoveType {
}
type Mutation {
addAwardEmoji(input: AddAwardEmojiInput!): AddAwardEmojiPayload @deprecated(reason: "Use awardEmojiAdd. Deprecated in 13.2")
addAwardEmoji(input: AddAwardEmojiInput!): AddAwardEmojiPayload @deprecated(reason: "Use awardEmojiAdd. Deprecated in 13.2.")
addProjectToSecurityDashboard(input: AddProjectToSecurityDashboardInput!): AddProjectToSecurityDashboardPayload
adminSidekiqQueuesDeleteJobs(input: AdminSidekiqQueuesDeleteJobsInput!): AdminSidekiqQueuesDeleteJobsPayload
alertSetAssignees(input: AlertSetAssigneesInput!): AlertSetAssigneesPayload
@ -14537,7 +14537,7 @@ type Mutation {
createClusterAgent(input: CreateClusterAgentInput!): CreateClusterAgentPayload
"""
. Available only when feature flag `custom_emoji` is enabled
Available only when feature flag `custom_emoji` is enabled.
"""
createCustomEmoji(input: CreateCustomEmojiInput!): CreateCustomEmojiPayload
createDevopsAdoptionSegment(input: CreateDevopsAdoptionSegmentInput!): CreateDevopsAdoptionSegmentPayload
@ -14576,7 +14576,7 @@ type Mutation {
Toggles the resolved state of a discussion
"""
discussionToggleResolve(input: DiscussionToggleResolveInput!): DiscussionToggleResolvePayload
dismissVulnerability(input: DismissVulnerabilityInput!): DismissVulnerabilityPayload @deprecated(reason: "Use vulnerabilityDismiss. Deprecated in 13.5")
dismissVulnerability(input: DismissVulnerabilityInput!): DismissVulnerabilityPayload @deprecated(reason: "Use vulnerabilityDismiss. Deprecated in 13.5.")
environmentsCanaryIngressUpdate(input: EnvironmentsCanaryIngressUpdateInput!): EnvironmentsCanaryIngressUpdatePayload
epicAddIssue(input: EpicAddIssueInput!): EpicAddIssuePayload
epicSetSubscription(input: EpicSetSubscriptionInput!): EpicSetSubscriptionPayload
@ -14626,15 +14626,15 @@ type Mutation {
releaseCreate(input: ReleaseCreateInput!): ReleaseCreatePayload
releaseDelete(input: ReleaseDeleteInput!): ReleaseDeletePayload
releaseUpdate(input: ReleaseUpdateInput!): ReleaseUpdatePayload
removeAwardEmoji(input: RemoveAwardEmojiInput!): RemoveAwardEmojiPayload @deprecated(reason: "Use awardEmojiRemove. Deprecated in 13.2")
removeAwardEmoji(input: RemoveAwardEmojiInput!): RemoveAwardEmojiPayload @deprecated(reason: "Use awardEmojiRemove. Deprecated in 13.2.")
removeProjectFromSecurityDashboard(input: RemoveProjectFromSecurityDashboardInput!): RemoveProjectFromSecurityDashboardPayload
"""
Repositions a DiffNote on an image (a `Note` where the `position.positionType` is `"image"`)
"""
repositionImageDiffNote(input: RepositionImageDiffNoteInput!): RepositionImageDiffNotePayload
revertVulnerabilityToDetected(input: RevertVulnerabilityToDetectedInput!): RevertVulnerabilityToDetectedPayload @deprecated(reason: "Use vulnerabilityRevertToDetected. Deprecated in 13.5")
runDastScan(input: RunDASTScanInput!): RunDASTScanPayload @deprecated(reason: "Use DastOnDemandScanCreate. Deprecated in 13.4")
revertVulnerabilityToDetected(input: RevertVulnerabilityToDetectedInput!): RevertVulnerabilityToDetectedPayload @deprecated(reason: "Use vulnerabilityRevertToDetected. Deprecated in 13.5.")
runDastScan(input: RunDASTScanInput!): RunDASTScanPayload @deprecated(reason: "Use DastOnDemandScanCreate. Deprecated in 13.4.")
terraformStateDelete(input: TerraformStateDeleteInput!): TerraformStateDeletePayload
terraformStateLock(input: TerraformStateLockInput!): TerraformStateLockPayload
terraformStateUnlock(input: TerraformStateUnlockInput!): TerraformStateUnlockPayload
@ -14643,7 +14643,7 @@ type Mutation {
todoRestore(input: TodoRestoreInput!): TodoRestorePayload
todoRestoreMany(input: TodoRestoreManyInput!): TodoRestoreManyPayload
todosMarkAllDone(input: TodosMarkAllDoneInput!): TodosMarkAllDonePayload
toggleAwardEmoji(input: ToggleAwardEmojiInput!): ToggleAwardEmojiPayload @deprecated(reason: "Use awardEmojiToggle. Deprecated in 13.2")
toggleAwardEmoji(input: ToggleAwardEmojiInput!): ToggleAwardEmojiPayload @deprecated(reason: "Use awardEmojiToggle. Deprecated in 13.2.")
updateAlertStatus(input: UpdateAlertStatusInput!): UpdateAlertStatusPayload
updateBoard(input: UpdateBoardInput!): UpdateBoardPayload
updateBoardEpicUserPreferences(input: UpdateBoardEpicUserPreferencesInput!): UpdateBoardEpicUserPreferencesPayload
@ -14707,8 +14707,8 @@ type Namespace {
additionalPurchasedStorageSize: Float
"""
Compliance frameworks available to projects in this namespace. Available only
when feature flag `ff_custom_compliance_frameworks` is enabled
Compliance frameworks available to projects in this namespace Available only
when feature flag `ff_custom_compliance_frameworks` is enabled.
"""
complianceFrameworks(
"""
@ -16912,7 +16912,7 @@ type Project {
"""
List items overlapping a time frame defined by startDate..endDate (if one
date is provided, both must be present). Deprecated in 13.5: Use timeframe.end
date is provided, both must be present) Deprecated in 13.5: Use timeframe.end.
"""
endDate: Time
@ -16943,8 +16943,8 @@ type Project {
"""
List items overlapping a time frame defined by startDate..endDate (if one
date is provided, both must be present). Deprecated in 13.5: Use
timeframe.start
date is provided, both must be present) Deprecated in 13.5: Use
timeframe.start.
"""
startDate: Time
@ -17177,7 +17177,7 @@ type Project {
"""
List items overlapping a time frame defined by startDate..endDate (if one
date is provided, both must be present). Deprecated in 13.5: Use timeframe.end
date is provided, both must be present) Deprecated in 13.5: Use timeframe.end.
"""
endDate: Time
@ -17208,8 +17208,8 @@ type Project {
"""
List items overlapping a time frame defined by startDate..endDate (if one
date is provided, both must be present). Deprecated in 13.5: Use
timeframe.start
date is provided, both must be present) Deprecated in 13.5: Use
timeframe.start.
"""
startDate: Time
@ -19008,8 +19008,8 @@ type Query {
"""
Number of vulnerabilities per severity level, per day, for the projects on the
current user's instance security dashboard. Deprecated in 13.3: Use
`vulnerabilitiesCountByDay`
current user's instance security dashboard Deprecated in 13.3: Use
`vulnerabilitiesCountByDay`.
"""
vulnerabilitiesCountByDayAndSeverity(
"""
@ -19041,7 +19041,7 @@ type Query {
First day for which to fetch vulnerability history
"""
startDate: ISO8601Date!
): VulnerabilitiesCountByDayAndSeverityConnection @deprecated(reason: "Use `vulnerabilitiesCountByDay`. Deprecated in 13.3")
): VulnerabilitiesCountByDayAndSeverityConnection @deprecated(reason: "Use `vulnerabilitiesCountByDay`. Deprecated in 13.3.")
"""
Find a vulnerability
@ -21529,9 +21529,9 @@ type Snippet implements Noteable {
author: User
"""
Snippet blob. Deprecated in 13.3: Use `blobs`
Snippet blob Deprecated in 13.3: Use `blobs`.
"""
blob: SnippetBlob! @deprecated(reason: "Use `blobs`. Deprecated in 13.3")
blob: SnippetBlob! @deprecated(reason: "Use `blobs`. Deprecated in 13.3.")
"""
Snippet blobs
@ -22038,22 +22038,22 @@ enum Sort {
"""
Created at ascending order
"""
created_asc @deprecated(reason: "Use CREATED_ASC. Deprecated in 13.5")
created_asc @deprecated(reason: "Use CREATED_ASC. Deprecated in 13.5.")
"""
Created at descending order
"""
created_desc @deprecated(reason: "Use CREATED_DESC. Deprecated in 13.5")
created_desc @deprecated(reason: "Use CREATED_DESC. Deprecated in 13.5.")
"""
Updated at ascending order
"""
updated_asc @deprecated(reason: "Use UPDATED_ASC. Deprecated in 13.5")
updated_asc @deprecated(reason: "Use UPDATED_ASC. Deprecated in 13.5.")
"""
Updated at descending order
"""
updated_desc @deprecated(reason: "Use UPDATED_DESC. Deprecated in 13.5")
updated_desc @deprecated(reason: "Use UPDATED_DESC. Deprecated in 13.5.")
}
type StatusAction {
@ -22891,9 +22891,9 @@ type TodoRestoreManyPayload {
todos: [Todo!]!
"""
The IDs of the updated todo items. Deprecated in 13.2: Use todos
The IDs of the updated todo items Deprecated in 13.2: Use todos.
"""
updatedIds: [TodoID!]! @deprecated(reason: "Use todos. Deprecated in 13.2")
updatedIds: [TodoID!]! @deprecated(reason: "Use todos. Deprecated in 13.2.")
}
"""
@ -22988,9 +22988,9 @@ type TodosMarkAllDonePayload {
todos: [Todo!]!
"""
Ids of the updated todos. Deprecated in 13.2: Use todos
Ids of the updated todos Deprecated in 13.2: Use todos.
"""
updatedIds: [TodoID!]! @deprecated(reason: "Use todos. Deprecated in 13.2")
updatedIds: [TodoID!]! @deprecated(reason: "Use todos. Deprecated in 13.2.")
}
"""
@ -24261,12 +24261,12 @@ type User {
avatarUrl: String
"""
User email. Deprecated in 13.7: Use public_email
User email Deprecated in 13.7: Use public_email.
"""
email: String @deprecated(reason: "Use public_email. Deprecated in 13.7")
email: String @deprecated(reason: "Use public_email. Deprecated in 13.7.")
"""
Group count for the user. Available only when feature flag `user_group_counts` is enabled
Group count for the user Available only when feature flag `user_group_counts` is enabled.
"""
groupCount: Int

View File

@ -1252,25 +1252,25 @@
"name": "updated_desc",
"description": "Updated at descending order",
"isDeprecated": true,
"deprecationReason": "Use UPDATED_DESC. Deprecated in 13.5"
"deprecationReason": "Use UPDATED_DESC. Deprecated in 13.5."
},
{
"name": "updated_asc",
"description": "Updated at ascending order",
"isDeprecated": true,
"deprecationReason": "Use UPDATED_ASC. Deprecated in 13.5"
"deprecationReason": "Use UPDATED_ASC. Deprecated in 13.5."
},
{
"name": "created_desc",
"description": "Created at descending order",
"isDeprecated": true,
"deprecationReason": "Use CREATED_DESC. Deprecated in 13.5"
"deprecationReason": "Use CREATED_DESC. Deprecated in 13.5."
},
{
"name": "created_asc",
"description": "Created at ascending order",
"isDeprecated": true,
"deprecationReason": "Use CREATED_ASC. Deprecated in 13.5"
"deprecationReason": "Use CREATED_ASC. Deprecated in 13.5."
},
{
"name": "UPDATED_DESC",
@ -3766,7 +3766,7 @@
"args": [
{
"name": "startDate",
"description": "List items overlapping a time frame defined by startDate..endDate (if one date is provided, both must be present). Deprecated in 13.5: Use timeframe.start",
"description": "List items overlapping a time frame defined by startDate..endDate (if one date is provided, both must be present) Deprecated in 13.5: Use timeframe.start.",
"type": {
"kind": "SCALAR",
"name": "Time",
@ -3776,7 +3776,7 @@
},
{
"name": "endDate",
"description": "List items overlapping a time frame defined by startDate..endDate (if one date is provided, both must be present). Deprecated in 13.5: Use timeframe.end",
"description": "List items overlapping a time frame defined by startDate..endDate (if one date is provided, both must be present) Deprecated in 13.5: Use timeframe.end.",
"type": {
"kind": "SCALAR",
"name": "Time",
@ -13550,7 +13550,7 @@
},
{
"name": "globalId",
"description": "ID of the DAST scanner profile. Deprecated in 13.6: Use `id`",
"description": "ID of the DAST scanner profile Deprecated in 13.6: Use `id`.",
"args": [
],
@ -13564,7 +13564,7 @@
}
},
"isDeprecated": true,
"deprecationReason": "Use `id`. Deprecated in 13.6"
"deprecationReason": "Use `id`. Deprecated in 13.6."
},
{
"name": "id",
@ -13897,7 +13897,7 @@
},
{
"name": "globalId",
"description": "ID of the scanner profile.. Deprecated in 13.6: Use `id`",
"description": "ID of the scanner profile. Deprecated in 13.6: Use `id`.",
"args": [
],
@ -13907,7 +13907,7 @@
"ofType": null
},
"isDeprecated": true,
"deprecationReason": "Use `id`. Deprecated in 13.6"
"deprecationReason": "Use `id`. Deprecated in 13.6."
},
{
"name": "id",
@ -21129,7 +21129,7 @@
"args": [
{
"name": "startDate",
"description": "List items overlapping a time frame defined by startDate..endDate (if one date is provided, both must be present). Deprecated in 13.5: Use timeframe.start",
"description": "List items overlapping a time frame defined by startDate..endDate (if one date is provided, both must be present) Deprecated in 13.5: Use timeframe.start.",
"type": {
"kind": "SCALAR",
"name": "Time",
@ -21139,7 +21139,7 @@
},
{
"name": "endDate",
"description": "List items overlapping a time frame defined by startDate..endDate (if one date is provided, both must be present). Deprecated in 13.5: Use timeframe.end",
"description": "List items overlapping a time frame defined by startDate..endDate (if one date is provided, both must be present) Deprecated in 13.5: Use timeframe.end.",
"type": {
"kind": "SCALAR",
"name": "Time",
@ -25739,7 +25739,7 @@
},
{
"name": "complianceFrameworks",
"description": "Compliance frameworks available to projects in this namespace. Available only when feature flag `ff_custom_compliance_frameworks` is enabled",
"description": "Compliance frameworks available to projects in this namespace Available only when feature flag `ff_custom_compliance_frameworks` is enabled.",
"args": [
{
"name": "after",
@ -25891,7 +25891,7 @@
},
{
"name": "customEmoji",
"description": "Custom emoji within this namespace. Available only when feature flag `custom_emoji` is enabled",
"description": "Custom emoji within this namespace Available only when feature flag `custom_emoji` is enabled.",
"args": [
{
"name": "after",
@ -25990,7 +25990,7 @@
"args": [
{
"name": "startDate",
"description": "List items overlapping a time frame defined by startDate..endDate (if one date is provided, both must be present). Deprecated in 13.5: Use timeframe.start",
"description": "List items overlapping a time frame defined by startDate..endDate (if one date is provided, both must be present) Deprecated in 13.5: Use timeframe.start.",
"type": {
"kind": "SCALAR",
"name": "Time",
@ -26000,7 +26000,7 @@
},
{
"name": "endDate",
"description": "List items overlapping a time frame defined by startDate..endDate (if one date is provided, both must be present). Deprecated in 13.5: Use timeframe.end",
"description": "List items overlapping a time frame defined by startDate..endDate (if one date is provided, both must be present) Deprecated in 13.5: Use timeframe.end.",
"type": {
"kind": "SCALAR",
"name": "Time",
@ -26229,7 +26229,7 @@
"args": [
{
"name": "startDate",
"description": "List items overlapping a time frame defined by startDate..endDate (if one date is provided, both must be present). Deprecated in 13.5: Use timeframe.start",
"description": "List items overlapping a time frame defined by startDate..endDate (if one date is provided, both must be present) Deprecated in 13.5: Use timeframe.start.",
"type": {
"kind": "SCALAR",
"name": "Time",
@ -26239,7 +26239,7 @@
},
{
"name": "endDate",
"description": "List items overlapping a time frame defined by startDate..endDate (if one date is provided, both must be present). Deprecated in 13.5: Use timeframe.end",
"description": "List items overlapping a time frame defined by startDate..endDate (if one date is provided, both must be present) Deprecated in 13.5: Use timeframe.end.",
"type": {
"kind": "SCALAR",
"name": "Time",
@ -26908,7 +26908,7 @@
"args": [
{
"name": "startDate",
"description": "List items overlapping a time frame defined by startDate..endDate (if one date is provided, both must be present). Deprecated in 13.5: Use timeframe.start",
"description": "List items overlapping a time frame defined by startDate..endDate (if one date is provided, both must be present) Deprecated in 13.5: Use timeframe.start.",
"type": {
"kind": "SCALAR",
"name": "Time",
@ -26918,7 +26918,7 @@
},
{
"name": "endDate",
"description": "List items overlapping a time frame defined by startDate..endDate (if one date is provided, both must be present). Deprecated in 13.5: Use timeframe.end",
"description": "List items overlapping a time frame defined by startDate..endDate (if one date is provided, both must be present) Deprecated in 13.5: Use timeframe.end.",
"type": {
"kind": "SCALAR",
"name": "Time",
@ -27364,7 +27364,7 @@
"args": [
{
"name": "startDate",
"description": "List items overlapping a time frame defined by startDate..endDate (if one date is provided, both must be present). Deprecated in 13.5: Use timeframe.start",
"description": "List items overlapping a time frame defined by startDate..endDate (if one date is provided, both must be present) Deprecated in 13.5: Use timeframe.start.",
"type": {
"kind": "SCALAR",
"name": "Time",
@ -27374,7 +27374,7 @@
},
{
"name": "endDate",
"description": "List items overlapping a time frame defined by startDate..endDate (if one date is provided, both must be present). Deprecated in 13.5: Use timeframe.end",
"description": "List items overlapping a time frame defined by startDate..endDate (if one date is provided, both must be present) Deprecated in 13.5: Use timeframe.end.",
"type": {
"kind": "SCALAR",
"name": "Time",
@ -28227,7 +28227,7 @@
},
{
"name": "vulnerabilitiesCountByDayAndSeverity",
"description": "Number of vulnerabilities per severity level, per day, for the projects in the group and its subgroups. Deprecated in 13.3: Use `vulnerabilitiesCountByDay`",
"description": "Number of vulnerabilities per severity level, per day, for the projects in the group and its subgroups Deprecated in 13.3: Use `vulnerabilitiesCountByDay`.",
"args": [
{
"name": "startDate",
@ -28304,7 +28304,7 @@
"ofType": null
},
"isDeprecated": true,
"deprecationReason": "Use `vulnerabilitiesCountByDay`. Deprecated in 13.3"
"deprecationReason": "Use `vulnerabilitiesCountByDay`. Deprecated in 13.3."
},
{
"name": "vulnerabilityGrades",
@ -33085,25 +33085,25 @@
"name": "updated_desc",
"description": "Updated at descending order",
"isDeprecated": true,
"deprecationReason": "Use UPDATED_DESC. Deprecated in 13.5"
"deprecationReason": "Use UPDATED_DESC. Deprecated in 13.5."
},
{
"name": "updated_asc",
"description": "Updated at ascending order",
"isDeprecated": true,
"deprecationReason": "Use UPDATED_ASC. Deprecated in 13.5"
"deprecationReason": "Use UPDATED_ASC. Deprecated in 13.5."
},
{
"name": "created_desc",
"description": "Created at descending order",
"isDeprecated": true,
"deprecationReason": "Use CREATED_DESC. Deprecated in 13.5"
"deprecationReason": "Use CREATED_DESC. Deprecated in 13.5."
},
{
"name": "created_asc",
"description": "Created at ascending order",
"isDeprecated": true,
"deprecationReason": "Use CREATED_ASC. Deprecated in 13.5"
"deprecationReason": "Use CREATED_ASC. Deprecated in 13.5."
},
{
"name": "UPDATED_DESC",
@ -39012,25 +39012,25 @@
"name": "updated_desc",
"description": "Updated at descending order",
"isDeprecated": true,
"deprecationReason": "Use UPDATED_DESC. Deprecated in 13.5"
"deprecationReason": "Use UPDATED_DESC. Deprecated in 13.5."
},
{
"name": "updated_asc",
"description": "Updated at ascending order",
"isDeprecated": true,
"deprecationReason": "Use UPDATED_ASC. Deprecated in 13.5"
"deprecationReason": "Use UPDATED_ASC. Deprecated in 13.5."
},
{
"name": "created_desc",
"description": "Created at descending order",
"isDeprecated": true,
"deprecationReason": "Use CREATED_DESC. Deprecated in 13.5"
"deprecationReason": "Use CREATED_DESC. Deprecated in 13.5."
},
{
"name": "created_asc",
"description": "Created at ascending order",
"isDeprecated": true,
"deprecationReason": "Use CREATED_ASC. Deprecated in 13.5"
"deprecationReason": "Use CREATED_ASC. Deprecated in 13.5."
},
{
"name": "UPDATED_DESC",
@ -40261,7 +40261,7 @@
"ofType": null
},
"isDeprecated": true,
"deprecationReason": "Use awardEmojiAdd. Deprecated in 13.2"
"deprecationReason": "Use awardEmojiAdd. Deprecated in 13.2."
},
{
"name": "addProjectToSecurityDashboard",
@ -40778,7 +40778,7 @@
},
{
"name": "createCustomEmoji",
"description": ". Available only when feature flag `custom_emoji` is enabled",
"description": " Available only when feature flag `custom_emoji` is enabled.",
"args": [
{
"name": "input",
@ -41692,7 +41692,7 @@
"ofType": null
},
"isDeprecated": true,
"deprecationReason": "Use vulnerabilityDismiss. Deprecated in 13.5"
"deprecationReason": "Use vulnerabilityDismiss. Deprecated in 13.5."
},
{
"name": "environmentsCanaryIngressUpdate",
@ -42934,7 +42934,7 @@
"ofType": null
},
"isDeprecated": true,
"deprecationReason": "Use awardEmojiRemove. Deprecated in 13.2"
"deprecationReason": "Use awardEmojiRemove. Deprecated in 13.2."
},
{
"name": "removeProjectFromSecurityDashboard",
@ -43015,7 +43015,7 @@
"ofType": null
},
"isDeprecated": true,
"deprecationReason": "Use vulnerabilityRevertToDetected. Deprecated in 13.5"
"deprecationReason": "Use vulnerabilityRevertToDetected. Deprecated in 13.5."
},
{
"name": "runDastScan",
@ -43042,7 +43042,7 @@
"ofType": null
},
"isDeprecated": true,
"deprecationReason": "Use DastOnDemandScanCreate. Deprecated in 13.4"
"deprecationReason": "Use DastOnDemandScanCreate. Deprecated in 13.4."
},
{
"name": "terraformStateDelete",
@ -43285,7 +43285,7 @@
"ofType": null
},
"isDeprecated": true,
"deprecationReason": "Use awardEmojiToggle. Deprecated in 13.2"
"deprecationReason": "Use awardEmojiToggle. Deprecated in 13.2."
},
{
"name": "updateAlertStatus",
@ -43845,7 +43845,7 @@
},
{
"name": "complianceFrameworks",
"description": "Compliance frameworks available to projects in this namespace. Available only when feature flag `ff_custom_compliance_frameworks` is enabled",
"description": "Compliance frameworks available to projects in this namespace Available only when feature flag `ff_custom_compliance_frameworks` is enabled.",
"args": [
{
"name": "after",
@ -49886,7 +49886,7 @@
"args": [
{
"name": "startDate",
"description": "List items overlapping a time frame defined by startDate..endDate (if one date is provided, both must be present). Deprecated in 13.5: Use timeframe.start",
"description": "List items overlapping a time frame defined by startDate..endDate (if one date is provided, both must be present) Deprecated in 13.5: Use timeframe.start.",
"type": {
"kind": "SCALAR",
"name": "Time",
@ -49896,7 +49896,7 @@
},
{
"name": "endDate",
"description": "List items overlapping a time frame defined by startDate..endDate (if one date is provided, both must be present). Deprecated in 13.5: Use timeframe.end",
"description": "List items overlapping a time frame defined by startDate..endDate (if one date is provided, both must be present) Deprecated in 13.5: Use timeframe.end.",
"type": {
"kind": "SCALAR",
"name": "Time",
@ -50478,7 +50478,7 @@
"args": [
{
"name": "startDate",
"description": "List items overlapping a time frame defined by startDate..endDate (if one date is provided, both must be present). Deprecated in 13.5: Use timeframe.start",
"description": "List items overlapping a time frame defined by startDate..endDate (if one date is provided, both must be present) Deprecated in 13.5: Use timeframe.start.",
"type": {
"kind": "SCALAR",
"name": "Time",
@ -50488,7 +50488,7 @@
},
{
"name": "endDate",
"description": "List items overlapping a time frame defined by startDate..endDate (if one date is provided, both must be present). Deprecated in 13.5: Use timeframe.end",
"description": "List items overlapping a time frame defined by startDate..endDate (if one date is provided, both must be present) Deprecated in 13.5: Use timeframe.end.",
"type": {
"kind": "SCALAR",
"name": "Time",
@ -55455,7 +55455,7 @@
},
{
"name": "vulnerabilitiesCountByDayAndSeverity",
"description": "Number of vulnerabilities per severity level, per day, for the projects on the current user's instance security dashboard. Deprecated in 13.3: Use `vulnerabilitiesCountByDay`",
"description": "Number of vulnerabilities per severity level, per day, for the projects on the current user's instance security dashboard Deprecated in 13.3: Use `vulnerabilitiesCountByDay`.",
"args": [
{
"name": "startDate",
@ -55532,7 +55532,7 @@
"ofType": null
},
"isDeprecated": true,
"deprecationReason": "Use `vulnerabilitiesCountByDay`. Deprecated in 13.3"
"deprecationReason": "Use `vulnerabilitiesCountByDay`. Deprecated in 13.3."
},
{
"name": "vulnerability",
@ -62800,7 +62800,7 @@
},
{
"name": "blob",
"description": "Snippet blob. Deprecated in 13.3: Use `blobs`",
"description": "Snippet blob Deprecated in 13.3: Use `blobs`.",
"args": [
],
@ -62814,7 +62814,7 @@
}
},
"isDeprecated": true,
"deprecationReason": "Use `blobs`. Deprecated in 13.3"
"deprecationReason": "Use `blobs`. Deprecated in 13.3."
},
{
"name": "blobs",
@ -64283,25 +64283,25 @@
"name": "updated_desc",
"description": "Updated at descending order",
"isDeprecated": true,
"deprecationReason": "Use UPDATED_DESC. Deprecated in 13.5"
"deprecationReason": "Use UPDATED_DESC. Deprecated in 13.5."
},
{
"name": "updated_asc",
"description": "Updated at ascending order",
"isDeprecated": true,
"deprecationReason": "Use UPDATED_ASC. Deprecated in 13.5"
"deprecationReason": "Use UPDATED_ASC. Deprecated in 13.5."
},
{
"name": "created_desc",
"description": "Created at descending order",
"isDeprecated": true,
"deprecationReason": "Use CREATED_DESC. Deprecated in 13.5"
"deprecationReason": "Use CREATED_DESC. Deprecated in 13.5."
},
{
"name": "created_asc",
"description": "Created at ascending order",
"isDeprecated": true,
"deprecationReason": "Use CREATED_ASC. Deprecated in 13.5"
"deprecationReason": "Use CREATED_ASC. Deprecated in 13.5."
},
{
"name": "UPDATED_DESC",
@ -66994,7 +66994,7 @@
},
{
"name": "updatedIds",
"description": "The IDs of the updated todo items. Deprecated in 13.2: Use todos",
"description": "The IDs of the updated todo items Deprecated in 13.2: Use todos.",
"args": [
],
@ -67016,7 +67016,7 @@
}
},
"isDeprecated": true,
"deprecationReason": "Use todos. Deprecated in 13.2"
"deprecationReason": "Use todos. Deprecated in 13.2."
}
],
"inputFields": null,
@ -67271,7 +67271,7 @@
},
{
"name": "updatedIds",
"description": "Ids of the updated todos. Deprecated in 13.2: Use todos",
"description": "Ids of the updated todos Deprecated in 13.2: Use todos.",
"args": [
],
@ -67293,7 +67293,7 @@
}
},
"isDeprecated": true,
"deprecationReason": "Use todos. Deprecated in 13.2"
"deprecationReason": "Use todos. Deprecated in 13.2."
}
],
"inputFields": null,
@ -70685,7 +70685,7 @@
},
{
"name": "email",
"description": "User email. Deprecated in 13.7: Use public_email",
"description": "User email Deprecated in 13.7: Use public_email.",
"args": [
],
@ -70695,11 +70695,11 @@
"ofType": null
},
"isDeprecated": true,
"deprecationReason": "Use public_email. Deprecated in 13.7"
"deprecationReason": "Use public_email. Deprecated in 13.7."
},
{
"name": "groupCount",
"description": "Group count for the user. Available only when feature flag `user_group_counts` is enabled",
"description": "Group count for the user Available only when feature flag `user_group_counts` is enabled.",
"args": [
],

View File

@ -831,7 +831,7 @@ Represents a DAST scanner profile.
| Field | Type | Description |
| ----- | ---- | ----------- |
| `editPath` | String | Relative web path to the edit page of a scanner profile |
| `globalId` **{warning-solid}** | DastScannerProfileID! | **Deprecated:** Use `id`. Deprecated in 13.6 |
| `globalId` **{warning-solid}** | DastScannerProfileID! | **Deprecated:** Use `id`. Deprecated in 13.6. |
| `id` | DastScannerProfileID! | ID of the DAST scanner profile |
| `profileName` | String | Name of the DAST scanner profile |
| `scanType` | DastScanTypeEnum | Indicates the type of DAST scan that will run. Either a Passive Scan or an Active Scan. |
@ -848,7 +848,7 @@ Autogenerated return type of DastScannerProfileCreate.
| ----- | ---- | ----------- |
| `clientMutationId` | String | A unique identifier for the client performing the mutation. |
| `errors` | String! => Array | Errors encountered during execution of the mutation. |
| `globalId` **{warning-solid}** | DastScannerProfileID | **Deprecated:** Use `id`. Deprecated in 13.6 |
| `globalId` **{warning-solid}** | DastScannerProfileID | **Deprecated:** Use `id`. Deprecated in 13.6. |
| `id` | DastScannerProfileID | ID of the scanner profile. |
### DastScannerProfileDeletePayload
@ -1554,11 +1554,11 @@ Represents an external issue.
| `board` | Board | A single board of the group |
| `boards` | BoardConnection | Boards of the group |
| `codeCoverageActivities` | CodeCoverageActivityConnection | Represents the code coverage activity for this group |
| `complianceFrameworks` | ComplianceFrameworkConnection | Compliance frameworks available to projects in this namespace. Available only when feature flag `ff_custom_compliance_frameworks` is enabled |
| `complianceFrameworks` | ComplianceFrameworkConnection | Compliance frameworks available to projects in this namespace Available only when feature flag `ff_custom_compliance_frameworks` is enabled. |
| `containerRepositories` | ContainerRepositoryConnection | Container repositories of the group |
| `containerRepositoriesCount` | Int! | Number of container repositories in the group |
| `containsLockedProjects` | Boolean! | Includes at least one project where the repository size exceeds the limit |
| `customEmoji` | CustomEmojiConnection | Custom emoji within this namespace. Available only when feature flag `custom_emoji` is enabled |
| `customEmoji` | CustomEmojiConnection | Custom emoji within this namespace Available only when feature flag `custom_emoji` is enabled. |
| `description` | String | Description of the namespace |
| `descriptionHtml` | String | The GitLab Flavored Markdown rendering of `description` |
| `emailsDisabled` | Boolean | Indicates if a group has email notifications disabled |
@ -1603,7 +1603,7 @@ Represents an external issue.
| `visibility` | String | Visibility of the namespace |
| `vulnerabilities` | VulnerabilityConnection | Vulnerabilities reported on the projects in the group and its subgroups |
| `vulnerabilitiesCountByDay` | VulnerabilitiesCountByDayConnection | Number of vulnerabilities per day for the projects in the group and its subgroups |
| `vulnerabilitiesCountByDayAndSeverity` **{warning-solid}** | VulnerabilitiesCountByDayAndSeverityConnection | **Deprecated:** Use `vulnerabilitiesCountByDay`. Deprecated in 13.3 |
| `vulnerabilitiesCountByDayAndSeverity` **{warning-solid}** | VulnerabilitiesCountByDayAndSeverityConnection | **Deprecated:** Use `vulnerabilitiesCountByDay`. Deprecated in 13.3. |
| `vulnerabilityGrades` | VulnerableProjectsByGrade! => Array | Represents vulnerable project counts for each grade |
| `vulnerabilityScanners` | VulnerabilityScannerConnection | Vulnerability scanners reported on the project vulnerabilties of the group and its subgroups |
| `vulnerabilitySeveritiesCount` | VulnerabilitySeveritiesCount | Counts for each vulnerability severity in the group and its subgroups |
@ -2288,7 +2288,7 @@ Contains statistics about a milestone.
| ----- | ---- | ----------- |
| `actualRepositorySizeLimit` | Float | Size limit for repositories in the namespace in bytes |
| `additionalPurchasedStorageSize` | Float | Additional storage purchased for the root namespace in bytes |
| `complianceFrameworks` | ComplianceFrameworkConnection | Compliance frameworks available to projects in this namespace. Available only when feature flag `ff_custom_compliance_frameworks` is enabled |
| `complianceFrameworks` | ComplianceFrameworkConnection | Compliance frameworks available to projects in this namespace Available only when feature flag `ff_custom_compliance_frameworks` is enabled. |
| `containsLockedProjects` | Boolean! | Includes at least one project where the repository size exceeds the limit |
| `description` | String | Description of the namespace |
| `descriptionHtml` | String | The GitLab Flavored Markdown rendering of `description` |
@ -3198,7 +3198,7 @@ Represents a snippet entry.
| Field | Type | Description |
| ----- | ---- | ----------- |
| `author` | User | The owner of the snippet |
| `blob` **{warning-solid}** | SnippetBlob! | **Deprecated:** Use `blobs`. Deprecated in 13.3 |
| `blob` **{warning-solid}** | SnippetBlob! | **Deprecated:** Use `blobs`. Deprecated in 13.3. |
| `blobs` | SnippetBlobConnection | Snippet blobs |
| `createdAt` | Time! | Timestamp this snippet was created |
| `description` | String | Description of the snippet |
@ -3468,7 +3468,7 @@ Autogenerated return type of TodoRestoreMany.
| `clientMutationId` | String | A unique identifier for the client performing the mutation. |
| `errors` | String! => Array | Errors encountered during execution of the mutation. |
| `todos` | Todo! => Array | Updated todos |
| `updatedIds` **{warning-solid}** | TodoID! => Array | **Deprecated:** Use todos. Deprecated in 13.2 |
| `updatedIds` **{warning-solid}** | TodoID! => Array | **Deprecated:** Use todos. Deprecated in 13.2. |
### TodoRestorePayload
@ -3489,7 +3489,7 @@ Autogenerated return type of TodosMarkAllDone.
| `clientMutationId` | String | A unique identifier for the client performing the mutation. |
| `errors` | String! => Array | Errors encountered during execution of the mutation. |
| `todos` | Todo! => Array | Updated todos |
| `updatedIds` **{warning-solid}** | TodoID! => Array | **Deprecated:** Use todos. Deprecated in 13.2 |
| `updatedIds` **{warning-solid}** | TodoID! => Array | **Deprecated:** Use todos. Deprecated in 13.2. |
### ToggleAwardEmojiPayload
@ -3676,8 +3676,8 @@ Autogenerated return type of UpdateSnippet.
| `assignedMergeRequests` | MergeRequestConnection | Merge Requests assigned to the user |
| `authoredMergeRequests` | MergeRequestConnection | Merge Requests authored by the user |
| `avatarUrl` | String | URL of the user's avatar |
| `email` **{warning-solid}** | String | **Deprecated:** Use public_email. Deprecated in 13.7 |
| `groupCount` | Int | Group count for the user. Available only when feature flag `user_group_counts` is enabled |
| `email` **{warning-solid}** | String | **Deprecated:** Use public_email. Deprecated in 13.7. |
| `groupCount` | Int | Group count for the user Available only when feature flag `user_group_counts` is enabled. |
| `groupMemberships` | GroupMemberConnection | Group memberships of the user |
| `id` | ID! | ID of the user |
| `location` | String | The location of the user. |
@ -4015,10 +4015,10 @@ Values for sorting alerts.
| `UPDATED_DESC` | Updated at descending order |
| `UPDATED_TIME_ASC` | Created time by ascending order |
| `UPDATED_TIME_DESC` | Created time by descending order |
| `created_asc` **{warning-solid}** | **Deprecated:** Use CREATED_ASC. Deprecated in 13.5 |
| `created_desc` **{warning-solid}** | **Deprecated:** Use CREATED_DESC. Deprecated in 13.5 |
| `updated_asc` **{warning-solid}** | **Deprecated:** Use UPDATED_ASC. Deprecated in 13.5 |
| `updated_desc` **{warning-solid}** | **Deprecated:** Use UPDATED_DESC. Deprecated in 13.5 |
| `created_asc` **{warning-solid}** | **Deprecated:** Use CREATED_ASC. Deprecated in 13.5. |
| `created_desc` **{warning-solid}** | **Deprecated:** Use CREATED_DESC. Deprecated in 13.5. |
| `updated_asc` **{warning-solid}** | **Deprecated:** Use UPDATED_ASC. Deprecated in 13.5. |
| `updated_desc` **{warning-solid}** | **Deprecated:** Use UPDATED_DESC. Deprecated in 13.5. |
### AlertManagementDomainFilter
@ -4332,10 +4332,10 @@ Values for sorting issues.
| `UPDATED_DESC` | Updated at descending order |
| `WEIGHT_ASC` | Weight by ascending order |
| `WEIGHT_DESC` | Weight by descending order |
| `created_asc` **{warning-solid}** | **Deprecated:** Use CREATED_ASC. Deprecated in 13.5 |
| `created_desc` **{warning-solid}** | **Deprecated:** Use CREATED_DESC. Deprecated in 13.5 |
| `updated_asc` **{warning-solid}** | **Deprecated:** Use UPDATED_ASC. Deprecated in 13.5 |
| `updated_desc` **{warning-solid}** | **Deprecated:** Use UPDATED_DESC. Deprecated in 13.5 |
| `created_asc` **{warning-solid}** | **Deprecated:** Use CREATED_ASC. Deprecated in 13.5. |
| `created_desc` **{warning-solid}** | **Deprecated:** Use CREATED_DESC. Deprecated in 13.5. |
| `updated_asc` **{warning-solid}** | **Deprecated:** Use UPDATED_ASC. Deprecated in 13.5. |
| `updated_desc` **{warning-solid}** | **Deprecated:** Use UPDATED_DESC. Deprecated in 13.5. |
### IssueState
@ -4466,10 +4466,10 @@ Values for sorting merge requests.
| `PRIORITY_DESC` | Priority by descending order |
| `UPDATED_ASC` | Updated at ascending order |
| `UPDATED_DESC` | Updated at descending order |
| `created_asc` **{warning-solid}** | **Deprecated:** Use CREATED_ASC. Deprecated in 13.5 |
| `created_desc` **{warning-solid}** | **Deprecated:** Use CREATED_DESC. Deprecated in 13.5 |
| `updated_asc` **{warning-solid}** | **Deprecated:** Use UPDATED_ASC. Deprecated in 13.5 |
| `updated_desc` **{warning-solid}** | **Deprecated:** Use UPDATED_DESC. Deprecated in 13.5 |
| `created_asc` **{warning-solid}** | **Deprecated:** Use CREATED_ASC. Deprecated in 13.5. |
| `created_desc` **{warning-solid}** | **Deprecated:** Use CREATED_DESC. Deprecated in 13.5. |
| `updated_asc` **{warning-solid}** | **Deprecated:** Use UPDATED_ASC. Deprecated in 13.5. |
| `updated_desc` **{warning-solid}** | **Deprecated:** Use UPDATED_DESC. Deprecated in 13.5. |
### MergeRequestState
@ -4724,10 +4724,10 @@ Common sort values.
| `CREATED_DESC` | Created at descending order |
| `UPDATED_ASC` | Updated at ascending order |
| `UPDATED_DESC` | Updated at descending order |
| `created_asc` **{warning-solid}** | **Deprecated:** Use CREATED_ASC. Deprecated in 13.5 |
| `created_desc` **{warning-solid}** | **Deprecated:** Use CREATED_DESC. Deprecated in 13.5 |
| `updated_asc` **{warning-solid}** | **Deprecated:** Use UPDATED_ASC. Deprecated in 13.5 |
| `updated_desc` **{warning-solid}** | **Deprecated:** Use UPDATED_DESC. Deprecated in 13.5 |
| `created_asc` **{warning-solid}** | **Deprecated:** Use CREATED_ASC. Deprecated in 13.5. |
| `created_desc` **{warning-solid}** | **Deprecated:** Use CREATED_DESC. Deprecated in 13.5. |
| `updated_asc` **{warning-solid}** | **Deprecated:** Use UPDATED_ASC. Deprecated in 13.5. |
| `updated_desc` **{warning-solid}** | **Deprecated:** Use UPDATED_DESC. Deprecated in 13.5. |
### TestReportState

View File

@ -14,7 +14,7 @@ Notes are comments on:
- Epics **(ULTIMATE)**
This includes system notes, which are notes about changes to the object (for example, when an
assignee changes, there will be a corresponding system note).
assignee changes, GitLab posts a system note).
## Resource events
@ -137,7 +137,7 @@ Parameters:
- `issue_iid` (required) - The IID of an issue
- `body` (required) - The content of a note. Limited to 1,000,000 characters.
- `confidential` (optional) - The confidential flag of a note. Default is false.
- `created_at` (optional) - Date time string, ISO 8601 formatted, e.g. 2016-03-11T03:45:40Z (requires admin or project/group owner rights)
- `created_at` (optional) - Date time string, ISO 8601 formatted. Example: `2016-03-11T03:45:40Z` (requires administrator or project/group owner rights)
```shell
curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/5/issues/11/notes?body=note"
@ -244,8 +244,8 @@ curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/a
### Create new snippet note
Creates a new note for a single snippet. Snippet notes are comments users can post to a snippet.
If you create a note where the body only contains an Award Emoji, you'll receive this object back.
Creates a new note for a single snippet. Snippet notes are user comments on snippets.
If you create a note where the body only contains an Award Emoji, GitLab returns this object.
```plaintext
POST /projects/:id/snippets/:snippet_id/notes
@ -256,7 +256,7 @@ Parameters:
- `id` (required) - The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding)
- `snippet_id` (required) - The ID of a snippet
- `body` (required) - The content of a note. Limited to 1,000,000 characters.
- `created_at` (optional) - Date time string, ISO 8601 formatted, e.g. 2016-03-11T03:45:40Z
- `created_at` (optional) - Date time string, ISO 8601 formatted. Example: `2016-03-11T03:45:40Z` (requires administrator or project/group owner rights)
```shell
curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/5/snippet/11/notes?body=note"
@ -368,8 +368,7 @@ curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/a
### Create new merge request note
Creates a new note for a single merge request.
If you create a note where the body only contains an Award Emoji, you'll receive
this object back.
If you create a note where the body only contains an Award Emoji, GitLab returns this object.
```plaintext
POST /projects/:id/merge_requests/:merge_request_iid/notes
@ -380,7 +379,7 @@ Parameters:
- `id` (required) - The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding)
- `merge_request_iid` (required) - The IID of a merge request
- `body` (required) - The content of a note. Limited to 1,000,000 characters.
- `created_at` (optional) - Date time string, ISO 8601 formatted, e.g. 2016-03-11T03:45:40Z
- `created_at` (optional) - Date time string, ISO 8601 formatted. Example: `2016-03-11T03:45:40Z` (requires administrator or project/group owner rights)
### Modify existing merge request note
@ -486,7 +485,7 @@ curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/a
### Create new epic note
Creates a new note for a single epic. Epic notes are comments users can post to an epic.
If you create a note where the body only contains an Award Emoji, you'll receive this object back.
If you create a note where the body only contains an Award Emoji, GitLab returns this object.
```plaintext
POST /groups/:id/epics/:epic_id/notes

View File

@ -310,7 +310,7 @@ class MergeRequestPermissionsType < BasePermissionType
abilities :admin_merge_request, :update_merge_request, :create_note
ability_field :resolve_note,
description: 'Indicates the user can resolve discussions on the merge request'
description: 'Indicates the user can resolve discussions on the merge request.'
permission_field :push_to_source_branch, method: :can_push_to_source_branch?
end
```
@ -369,7 +369,7 @@ Example:
```ruby
field :test_field, type: GraphQL::STRING_TYPE,
null: true,
description: 'Some test field',
description: 'Some test field.',
feature_flag: :my_feature_flag
```
@ -394,7 +394,7 @@ Example:
field :foo, GraphQL::STRING_TYPE,
null: true,
description: 'Some test field. Will always return `null`' \
'if `my_feature_flag` feature flag is disabled'
'if `my_feature_flag` feature flag is disabled.'
def foo
object.foo if Feature.enabled?(:my_feature_flag, object)
@ -420,7 +420,7 @@ Example:
```ruby
field :token, GraphQL::STRING_TYPE, null: true,
deprecated: { reason: 'Login via token has been removed', milestone: '10.0' },
description: 'Token for login'
description: 'Token for login.'
```
The original `description` of the things being deprecated should be maintained,
@ -441,7 +441,7 @@ Example:
```ruby
field :designs, ::Types::DesignManagement::DesignCollectionType, null: true,
deprecated: { reason: 'Use `designCollection`', milestone: '10.0' },
description: 'The designs associated with this issue',
description: 'The designs associated with this issue.',
```
```ruby
@ -477,9 +477,9 @@ module Types
graphql_name 'TrafficLightState'
description 'State of a traffic light'
value 'RED', description: 'Drivers must stop'
value 'YELLOW', description: 'Drivers must stop when it is safe to'
value 'GREEN', description: 'Drivers can start or keep driving'
value 'RED', description: 'Drivers must stop.'
value 'YELLOW', description: 'Drivers must stop when it is safe to.'
value 'GREEN', description: 'Drivers can start or keep driving.'
end
end
```
@ -498,8 +498,8 @@ module Types
graphql_name 'EpicState'
description 'State of a GitLab epic'
value 'OPENED', value: 'opened', description: 'An open Epic'
value 'CLOSED', value: 'closed', description: 'An closed Epic'
value 'OPENED', value: 'opened', description: 'An open Epic.'
value 'CLOSED', value: 'closed', description: 'A closed Epic.'
end
end
```
@ -523,7 +523,7 @@ module Types
description 'Incident severity'
::IssuableSeverity.severities.keys.each do |severity|
value severity.upcase, value: severity, description: "#{severity.titleize} severity"
value severity.upcase, value: severity, description: "#{severity.titleize} severity."
end
end
end
@ -562,15 +562,15 @@ We can use GraphQL types like this:
```ruby
module Types
class ChartType < BaseObject
field :title, GraphQL::STRING_TYPE, null: true, description: 'Title of the chart'
field :data, [Types::ChartDatumType], null: true, description: 'Data of the chart'
field :title, GraphQL::STRING_TYPE, null: true, description: 'Title of the chart.'
field :data, [Types::ChartDatumType], null: true, description: 'Data of the chart.'
end
end
module Types
class ChartDatumType < BaseObject
field :x, GraphQL::INT_TYPE, null: true, description: 'X-axis value of the chart datum'
field :y, GraphQL::INT_TYPE, null: true, description: 'Y-axis value of the chart datum'
field :x, GraphQL::INT_TYPE, null: true, description: 'X-axis value of the chart datum.'
field :y, GraphQL::INT_TYPE, null: true, description: 'Y-axis value of the chart datum.'
end
end
```
@ -584,7 +584,7 @@ A description of a field or argument is given using the `description:`
keyword. For example:
```ruby
field :id, GraphQL::ID_TYPE, description: 'ID of the resource'
field :id, GraphQL::ID_TYPE, description: 'ID of the resource.'
```
Descriptions of fields and arguments are viewable to users through:
@ -606,14 +606,14 @@ descriptions:
- Always include the word `"timestamp"` when describing an argument or
field of type `Types::TimeType`. This lets the reader know that the
format of the property is `Time`, rather than just `Date`.
- No `.` at end of strings.
- Must end with a period (`.`).
Example:
```ruby
field :id, GraphQL::ID_TYPE, description: 'ID of the issue'
field :confidential, GraphQL::BOOLEAN_TYPE, description: 'Indicates the issue is confidential'
field :closed_at, Types::TimeType, description: 'Timestamp of when the issue was closed'
field :id, GraphQL::ID_TYPE, description: 'ID of the issue.'
field :confidential, GraphQL::BOOLEAN_TYPE, description: 'Indicates the issue is confidential.'
field :closed_at, Types::TimeType, description: 'Timestamp of when the issue was closed.'
```
### `copy_field_description` helper
@ -889,8 +889,8 @@ Then we can use these resolver on fields:
```ruby
# In PipelineType
field :jobs, resolver: JobsResolver, description: 'All jobs'
field :job, resolver: JobsResolver.single, description: 'A single job'
field :jobs, resolver: JobsResolver, description: 'All jobs.'
field :job, resolver: JobsResolver.single, description: 'A single job.'
```
### Correct use of `Resolver#ready?`
@ -965,7 +965,7 @@ to advertise the need for lookahead:
field :my_things, MyThingType.connection_type, null: true,
extras: [:lookahead], # Necessary
resolver: MyThingResolver,
description: 'My things'
description: 'My things.'
```
For an example of real world use, please
@ -1034,7 +1034,7 @@ To find the parent object in your `Presenter` class:
field :computed_field, SomeType, null: true,
method: :my_computing_method,
extras: [:parent], # Necessary
description: 'My field description'
description: 'My field description.'
field :resolver_field, resolver: SomeTypeResolver
@ -1042,7 +1042,7 @@ To find the parent object in your `Presenter` class:
extras [:parent]
type SomeType, null: true
description 'My field description'
description 'My field description.'
```
1. Declare your field's method in your Presenter class and have it accept the `parent` keyword argument.
@ -1161,7 +1161,7 @@ Example:
```ruby
argument :my_arg, GraphQL::STRING_TYPE,
required: true,
description: "A description of the argument"
description: "A description of the argument."
```
Each GraphQL `argument` defined is passed to the `#resolve` method
@ -1186,11 +1186,11 @@ defines these arguments (some
```ruby
argument :project_path, GraphQL::ID_TYPE,
required: true,
description: "The project the merge request to mutate is in"
description: "The project the merge request to mutate is in."
argument :iid, GraphQL::STRING_TYPE,
required: true,
description: "The IID of the merge request to mutate"
description: "The IID of the merge request to mutate."
argument :wip,
GraphQL::BOOLEAN_TYPE,
@ -1242,7 +1242,7 @@ field:
field :merge_request,
Types::MergeRequestType,
null: true,
description: "The merge request after mutation"
description: "The merge request after mutation."
```
This means that the hash returned from `resolve` in this mutation
@ -1527,7 +1527,7 @@ and handles time inputs.
Example:
```ruby
field :created_at, Types::TimeType, null: true, description: 'Timestamp of when the issue was created'
field :created_at, Types::TimeType, null: true, description: 'Timestamp of when the issue was created.'
```
## Testing

View File

@ -127,21 +127,21 @@ To use the Vue performance plugin:
1. Import the plugin:
```javascript
import PerformancePlugin from '~/performance/vue_performance_plugin';
```
```javascript
import PerformancePlugin from '~/performance/vue_performance_plugin';
```
1. Use it before initializing your Vue application:
```javascript
Vue.use(PerformancePlugin, {
components: [
'IdeTreeList',
'FileTree',
'RepoEditor',
]
});
```
```javascript
Vue.use(PerformancePlugin, {
components: [
'IdeTreeList',
'FileTree',
'RepoEditor',
]
});
```
The plugin accepts the list of components, performance of which should be measured. The components
should be specified by their `name` option.
@ -182,7 +182,7 @@ To access stored measurements, you can use either:
performance.getEntriesByType('measure');
```
## Naming convention
### Naming convention
All the marks and measures should be instantiated with the constants from
`app/assets/javascripts/performance/constants.js`. When youre ready to add a new marks or

View File

@ -10,14 +10,16 @@ Please read through the [GitLab Issue Documentation](index.md) for an overview o
## Parts of an Issue
The image below illustrates what an issue may look like. Note that certain parts will
look slightly different or will be absent, depending on the version of GitLab being used
and the permissions of the user viewing the issue.
The image below illustrates what an issue may look like. Certain parts
look slightly different or are absent, depending on the GitLab version
and the user's permissions.
You can find all the information for that issue on one screen.
You can find all of an issue's information on one page.
![Issue view](img/issues_main_view_numbered.png)
The numbers in the image correspond to the following features:
- **1.** [Issue actions](#issue-actions)
- **2.** [To Do](#to-do)
- **3.** [Assignee](#assignee)
@ -47,10 +49,6 @@ You can find all the information for that issue on one screen.
- **25.** [Submit comment, start a thread, or comment and close](#submit-comment-start-a-thread-or-comment-and-close)
- **26.** [Zoom meetings](#zoom-meetings)
An issue starts with its status (open or closed), followed by its author,
and includes many other functionalities, numbered in the image above to
explain what they mean, one by one.
Many of the elements of the issue screen refresh automatically, such as the title and
description, when they are changed by another user. Comments and system notes also
update automatically in response to various actions and content updates.
@ -89,9 +87,9 @@ An issue can be assigned to:
- Another person.
- [Many people](#multiple-assignees). **(STARTER)**
The assignee(s) can be changed as often as needed. The idea is that the assignees are
The assignees can be changed as often as needed. The idea is that the assignees are
responsible for that issue until it's reassigned to someone else to take it from there.
When assigned to someone, it will appear in their assigned issues list.
When assigned to someone, it appears in their assigned issues list.
NOTE:
If a user is not member of that project, it can only be
@ -99,7 +97,7 @@ assigned to them if they created the issue themselves.
#### Multiple Assignees **(STARTER)**
Often multiple people work on the same issue together, which can be especially difficult
Often, multiple people work on the same issue together. This can be difficult
to track in large teams where there is shared ownership of an issue.
In [GitLab Starter](https://about.gitlab.com/pricing/), you can
@ -116,10 +114,10 @@ Select a [milestone](../milestones/index.md) to attribute that issue to.
### Time tracking
Use [GitLab Quick Actions](../quick_actions.md) to [track estimates and time spent on issues](../time_tracking.md).
You can add an [estimate of the time it will take](../time_tracking.md#estimates)
to resolve the issue, and also add [the time spent](../time_tracking.md#time-spent)
on the resolution of the issue.
Use [GitLab Quick Actions](../quick_actions.md) to [track estimates and time
spent on issues](../time_tracking.md). You can add a [time estimate](../time_tracking.md#estimates)
for resolving the issue, and also add [the time spent](../time_tracking.md#time-spent)
to resolve the issue.
### Due date
@ -132,13 +130,12 @@ element. Due dates can be changed as many times as needed.
Categorize issues by giving them [labels](../labels.md). They help to organize workflows,
and they enable you to work with the [GitLab Issue Board](index.md#issue-boards).
Group Labels, which allow you to use the same labels for all projects within the same
group, can be also given to issues. They work exactly the same, but they are immediately
Group Labels, which allow you to use the same labels for all projects in the same
group, can also be given to issues. They work exactly the same, but are immediately
available to all projects in the group.
NOTE:
If a label doesn't exist yet, you can click **Edit**, and it opens a dropdown menu
from which you can select **Create new label**.
If a label doesn't exist yet, you can create one by clicking **Edit**
followed by **Create new label** in the dropdown menu.
### Weight **(STARTER)**
@ -148,9 +145,8 @@ positive values or zero are allowed.
### Confidentiality
You can [set an issue to be confidential](confidential_issues.md). When set, unauthorized
users will not be able to access the issue, and will not see it listed in project
issue boards or the issue list.
You can [set an issue to be confidential](confidential_issues.md). Unauthorized users
cannot access the issue, and it is not listed in the project's issue boards nor list for them.
### Lock issue
@ -165,7 +161,7 @@ or were mentioned in the description or threads.
### Notifications
Click on the icon to enable/disable [notifications](../../profile/notifications.md#issue--epics--merge-request-events)
for the issue. This will automatically enable if you participate in the issue in any way.
for the issue. Notifications are automatically enabled after you participate in the issue in any way.
- **Enable**: If you are not a participant in the discussion on that issue, but
want to receive notifications on each update, subscribe to it.
@ -180,9 +176,9 @@ for the issue. This will automatically enable if you participate in the issue in
### Edit
Clicking this icon opens the issue for editing, and you will have access to all the
same fields as when the issue was created. This icon will not display if the user
does not have permission to edit the issue.
Clicking this icon opens the issue for editing. All the fields which
were shown when the issue was created are displayed for editing.
This icon is only displayed if the user has permission to edit the issue.
### Description
@ -195,17 +191,15 @@ allowing many formatting options.
### Mentions
You can mention a user or a group present in your GitLab instance with `@username` or
`@groupname` and they will be notified via to-dos and email, unless they have disabled
all notifications in their profile settings. This is controlled in the
[notification settings](../../profile/notifications.md).
`@groupname`. All mentioned users are notified via to-do items and emails,
unless they have disabled all notifications in their profile settings.
This is controlled in the [notification settings](../../profile/notifications.md).
Mentions for yourself (the current logged in user), will be highlighted in a different
color, allowing you to easily see which comments involve you, helping you focus on
them quickly.
Mentions for yourself (the current logged in user) are highlighted
in a different color, which allows you to quickly see which comments involve you.
NOTE:
Avoid mentioning `@all` in issues and merge requests, as it sends an email notification
to all the members of that project's group, which can be interpreted as spam.
to all the members of that project's group. This might be interpreted as spam.
### Related Issues
@ -217,18 +211,18 @@ You can also click the `+` to add more related issues.
Merge requests that were mentioned in that issue's description or in the issue thread
are listed as [related merge requests](crosslinking_issues.md#from-merge-requests) here.
Also, if the current issue was mentioned as related in another merge request, that
merge request will be listed here.
merge request is also listed here.
### Award emoji
You can award an emoji to that issue. There are shortcuts to "thumbs_up" and "thumbs_down",
or you can click on the light gray "face" to choose a different reaction from the
dropdown list of available [GitLab Flavored Markdown Emoji](../../markdown.md#emoji).
You can award emojis to issues. You can select the "thumbs up" and "thumbs down",
or the gray "smiley-face" to choose from the list of available
[GitLab Flavored Markdown Emoji](../../markdown.md#emoji).
NOTE:
Posting "+1" as a comment in a thread spams all subscribed participants of that issue,
clutters the threads, and is not recommended. Awarding an emoji is a way
to let them know your reaction without spamming them.
to let them know your reaction without notifying them.
### Show all activity
@ -241,21 +235,20 @@ and selecting either:
Also:
- You can mention a user or a group present in your GitLab instance with
`@username` or `@groupname` and they will be notified via to-do items
and email, unless they have [disabled all notifications](#notifications)
`@username` or `@groupname` and they are notified via to-do items
and emails, unless they have [disabled all notifications](#notifications)
in their profile settings.
- Mentions for yourself (the current logged in user), will be highlighted
in a different color, allowing you to easily see which comments involve you,
helping you focus on them quickly.
- Mentions for yourself (the current logged-in user) are highlighted
in a different color, which allows you to quickly see which comments involve you.
![Show all activity](img/show-all-activity.png)
### Create Merge Request
Create a new branch and [**Draft** merge request](../merge_requests/work_in_progress_merge_requests.md)
in one action. The branch will be named `issuenumber-title` by default, but you can
choose any name, and GitLab will verify that it is not already in use. The merge request
will automatically inherit the milestone and labels of the issue, and will be set to
in one action. The branch is named `issuenumber-title` by default, but you can
choose any name, and GitLab verifies that it is not already in use. The merge request
inherits the milestone and labels of the issue, and is set to automatically
close the issue when it is merged.
![Create MR from issue](img/create_mr_from_issue.png)
@ -288,11 +281,11 @@ supports [GitLab Flavored Markdown](../../markdown.md#gitlab-flavored-markdown-g
### Submit comment, start a thread, or comment and close
Once you write a comment, you can:
After you write a comment, you can:
- Click **Comment** and your comment will be published.
- Click **Comment** and to publish your comment.
- Choose **Start thread** from the dropdown list and start a new [thread](../../discussions/index.md#threaded-discussions)
within that issue's main thread to discuss specific points. This invites other participants
in that issue's main thread to discuss specific points. This invites other participants
to reply directly to your thread, keeping related comments grouped together.
![Comment or thread](img/comment-or-discussion.png)

View File

@ -44,6 +44,24 @@ module Gitlab
def get_config_path(dir)
File.join(dir, 'config.toml')
end
def compile_into(dir)
command = %W[#{make} -C #{Rails.root.join('workhorse')} install PREFIX=#{File.absolute_path(dir)}]
make_out, make_status = Gitlab::Popen.popen(command)
unless make_status == 0
warn make_out
raise 'workhorse make failed'
end
# 'make install' puts the binaries in #{dir}/bin but the init script expects them in dir
FileUtils.mv(Dir["#{dir}/bin/*"], dir)
end
def make
_, which_status = Gitlab::Popen.popen(%w[which gmake])
which_status == 0 ? 'gmake' : 'make'
end
end
end

View File

@ -26,14 +26,7 @@ namespace :gitlab do
args.with_defaults(repo: 'https://gitlab.com/gitlab-org/gitlab-workhorse.git')
checkout_or_clone_version(version: 'workhorse-move-notice', repo: args.repo, target_dir: args.dir, clone_opts: %w[--depth 1])
_, which_status = Gitlab::Popen.popen(%w[which gmake])
make = which_status == 0 ? 'gmake' : 'make'
command = %W[#{make} -C #{Rails.root.join('workhorse')} install PREFIX=#{File.absolute_path(args.dir)}]
run_command!(command)
# 'make install' puts the binaries in #{args.dir}/bin but the init script expects them in args.dir
FileUtils.mv(Dir["#{args.dir}/bin/*"], args.dir)
Gitlab::SetupHelper::Workhorse.compile_into(args.dir)
end
end
end

View File

@ -7131,6 +7131,24 @@ msgstr ""
msgid "ComplianceDashboard|created by:"
msgstr ""
msgid "ComplianceFrameworks|Add framework"
msgstr ""
msgid "ComplianceFrameworks|All"
msgstr ""
msgid "ComplianceFrameworks|Error fetching compliance frameworks data. Please refresh the page"
msgstr ""
msgid "ComplianceFrameworks|Once you have created a compliance framework it will appear here."
msgstr ""
msgid "ComplianceFrameworks|Regulated"
msgstr ""
msgid "ComplianceFrameworks|There are no compliance frameworks set up yet"
msgstr ""
msgid "ComplianceFramework|GDPR"
msgstr ""
@ -7432,6 +7450,9 @@ msgstr ""
msgid "ContainerRegistry|Keep these tags"
msgstr ""
msgid "ContainerRegistry|Last updated %{time}"
msgstr ""
msgid "ContainerRegistry|Login"
msgstr ""
@ -9715,6 +9736,9 @@ msgstr ""
msgid "DevopsAdoption|Segment"
msgstr ""
msgid "DevopsAdoption|Segment data pending until the start of next month"
msgstr ""
msgid "DevopsAdoption|There was an error fetching Groups. Please refresh the page to try again."
msgstr ""
@ -13679,6 +13703,12 @@ msgstr ""
msgid "GroupSettings|Changing group URL can have unintended side effects."
msgstr ""
msgid "GroupSettings|Compliance frameworks"
msgstr ""
msgid "GroupSettings|Configure frameworks to apply enforceable rules to projects."
msgstr ""
msgid "GroupSettings|Custom project templates"
msgstr ""

View File

@ -13,42 +13,74 @@
# argument :some_argument, GraphQL::STRING_TYPE
# end
#
# class UngoodClass
# field :some_argument,
# GraphQL::STRING_TYPE,
# description: "A description that does not end in a period"
# end
#
# # good
# class GreatClass
# argument :some_field,
# GraphQL::STRING_TYPE,
# description: "Well described - a superb description"
# description: "Well described - a superb description."
#
# field :some_field,
# GraphQL::STRING_TYPE,
# description: "A thorough and compelling description"
# description: "A thorough and compelling description."
# end
module RuboCop
module Cop
module Graphql
class Descriptions < RuboCop::Cop::Cop
MSG = 'Please add a `description` property.'
MSG_NO_DESCRIPTION = 'Please add a `description` property.'
MSG_NO_PERIOD = '`description` strings must end with a `.`.'
# ability_field and permission_field set a default description.
def_node_matcher :fields, <<~PATTERN
(send nil? :field $...)
def_node_matcher :field_or_argument?, <<~PATTERN
(send nil? {:field :argument} ...)
PATTERN
def_node_matcher :arguments, <<~PATTERN
(send nil? :argument $...)
PATTERN
def_node_matcher :has_description?, <<~PATTERN
(hash <(pair (sym :description) _) ...>)
def_node_matcher :description, <<~PATTERN
(... (hash <(pair (sym :description) $_) ...>))
PATTERN
def on_send(node)
matches = fields(node) || arguments(node)
return unless field_or_argument?(node)
return if matches.nil?
description = description(node)
add_offense(node, location: :expression) unless has_description?(matches.last)
return add_offense(node, location: :expression, message: MSG_NO_DESCRIPTION) unless description
add_offense(node, location: :expression, message: MSG_NO_PERIOD) if no_period?(description)
end
# Autocorrect missing periods at end of description.
def autocorrect(node)
lambda do |corrector|
description = description(node)
next unless description
corrector.insert_after(before_end_quote(description), '.')
end
end
private
def no_period?(description)
# Test that the description node is a `:str` (as opposed to
# a `#copy_field_description` call) before checking.
description.type == :str && !description.value.strip.end_with?('.')
end
# Returns a Parser::Source::Range that ends just before the final String delimiter.
def before_end_quote(string)
return string.source_range.adjust(end_pos: -1) unless string.heredoc?
heredoc_source = string.location.heredoc_body.source
adjust = heredoc_source.index(/\s+\Z/) - heredoc_source.length
string.location.heredoc_body.adjust(end_pos: adjust)
end
end
end

View File

@ -1,7 +1,7 @@
import AxiosMockAdapter from 'axios-mock-adapter';
import { TEST_HOST } from 'helpers/test_constants';
import axios from '~/lib/utils/axios_utils';
import boardsStore, { gqlClient } from '~/boards/stores/boards_store';
import boardsStore from '~/boards/stores/boards_store';
import eventHub from '~/boards/eventhub';
import { listObj, listObjDuplicate } from './mock_data';
@ -456,118 +456,6 @@ describe('boardsStore', () => {
});
});
describe('createBoard', () => {
const labelIds = ['first label', 'second label'];
const assigneeId = 'as sign ee';
const milestoneId = 'vegetable soup';
const board = {
labels: labelIds.map(id => ({ id })),
assignee: { id: assigneeId },
milestone: { id: milestoneId },
};
describe('for existing board', () => {
const id = 'skate-board';
const url = `${endpoints.boardsEndpoint}/${id}.json`;
const expectedRequest = expect.objectContaining({
data: JSON.stringify({
board: {
...board,
id,
label_ids: labelIds,
assignee_id: assigneeId,
milestone_id: milestoneId,
},
}),
});
let requestSpy;
beforeEach(() => {
requestSpy = jest.fn();
axiosMock.onPut(url).replyOnce(config => requestSpy(config));
jest.spyOn(gqlClient, 'mutate').mockReturnValue(Promise.resolve({}));
});
it('makes a request to update the board', () => {
requestSpy.mockReturnValue([200, dummyResponse]);
const expectedResponse = [
expect.objectContaining({ data: dummyResponse }),
expect.objectContaining({}),
];
return expect(
boardsStore.createBoard({
...board,
id,
}),
)
.resolves.toEqual(expectedResponse)
.then(() => {
expect(requestSpy).toHaveBeenCalledWith(expectedRequest);
});
});
it('fails for error response', () => {
requestSpy.mockReturnValue([500]);
return expect(
boardsStore.createBoard({
...board,
id,
}),
)
.rejects.toThrow()
.then(() => {
expect(requestSpy).toHaveBeenCalledWith(expectedRequest);
});
});
});
describe('for new board', () => {
const url = `${endpoints.boardsEndpoint}.json`;
const expectedRequest = expect.objectContaining({
data: JSON.stringify({
board: {
...board,
label_ids: labelIds,
assignee_id: assigneeId,
milestone_id: milestoneId,
},
}),
});
let requestSpy;
beforeEach(() => {
requestSpy = jest.fn();
axiosMock.onPost(url).replyOnce(config => requestSpy(config));
jest.spyOn(gqlClient, 'mutate').mockReturnValue(Promise.resolve({}));
});
it('makes a request to create a new board', () => {
requestSpy.mockReturnValue([200, dummyResponse]);
const expectedResponse = dummyResponse;
return expect(boardsStore.createBoard(board))
.resolves.toEqual(expectedResponse)
.then(() => {
expect(requestSpy).toHaveBeenCalledWith(expectedRequest);
});
});
it('fails for error response', () => {
requestSpy.mockReturnValue([500]);
return expect(boardsStore.createBoard(board))
.rejects.toThrow()
.then(() => {
expect(requestSpy).toHaveBeenCalledWith(expectedRequest);
});
});
});
});
describe('deleteBoard', () => {
const id = 'capsized';
const url = `${endpoints.boardsEndpoint}/${id}.json`;

View File

@ -1,47 +1,289 @@
import { mount } from '@vue/test-utils';
import { shallowMount } from '@vue/test-utils';
import AxiosMockAdapter from 'axios-mock-adapter';
import { TEST_HOST } from 'jest/helpers/test_constants';
import { GlModal } from '@gitlab/ui';
import waitForPromises from 'helpers/wait_for_promises';
import BoardScope from 'ee_component/boards/components/board_scope.vue';
import axios from '~/lib/utils/axios_utils';
import { visitUrl } from '~/lib/utils/url_utility';
import boardsStore from '~/boards/stores/boards_store';
import boardForm from '~/boards/components/board_form.vue';
import BoardForm from '~/boards/components/board_form.vue';
import BoardConfigurationOptions from '~/boards/components/board_configuration_options.vue';
import createBoardMutation from '~/boards/graphql/board.mutation.graphql';
describe('board_form.vue', () => {
jest.mock('~/lib/utils/url_utility', () => ({
visitUrl: jest.fn().mockName('visitUrlMock'),
}));
const currentBoard = {
id: 1,
name: 'test',
labels: [],
milestone_id: undefined,
assignee: {},
assignee_id: undefined,
weight: null,
hide_backlog_list: false,
hide_closed_list: false,
};
const boardDefaults = {
id: false,
name: '',
labels: [],
milestone_id: undefined,
assignee: {},
assignee_id: undefined,
weight: null,
hide_backlog_list: false,
hide_closed_list: false,
};
const defaultProps = {
canAdminBoard: false,
labelsPath: `${TEST_HOST}/labels/path`,
labelsWebUrl: `${TEST_HOST}/-/labels`,
currentBoard,
};
const endpoints = {
boardsEndpoint: 'test-endpoint',
};
const mutate = jest.fn().mockResolvedValue({});
describe('BoardForm', () => {
let wrapper;
const propsData = {
canAdminBoard: false,
labelsPath: `${TEST_HOST}/labels/path`,
labelsWebUrl: `${TEST_HOST}/-/labels`,
};
let axiosMock;
const findModal = () => wrapper.find(GlModal);
const findModalActionPrimary = () => findModal().props('actionPrimary');
const findForm = () => wrapper.find('[data-testid="board-form"]');
const findFormWrapper = () => wrapper.find('[data-testid="board-form-wrapper"]');
const findDeleteConfirmation = () => wrapper.find('[data-testid="delete-confirmation-message"]');
const findConfigurationOptions = () => wrapper.find(BoardConfigurationOptions);
const findBoardScope = () => wrapper.find(BoardScope);
const findInput = () => wrapper.find('#board-new-name');
const createComponent = (props, data) => {
wrapper = shallowMount(BoardForm, {
propsData: { ...defaultProps, ...props },
data() {
return {
...data,
};
},
provide: {
endpoints,
},
mocks: {
$apollo: {
mutate,
},
},
attachToDocument: true,
});
};
beforeEach(() => {
boardsStore.state.currentPage = 'edit';
wrapper = mount(boardForm, { propsData });
axiosMock = new AxiosMockAdapter(axios);
});
afterEach(() => {
wrapper.destroy();
wrapper = null;
axiosMock.restore();
boardsStore.state.currentPage = null;
});
describe('methods', () => {
describe('cancel', () => {
it('resets currentPage', () => {
wrapper.vm.cancel();
expect(boardsStore.state.currentPage).toBe('');
describe('when user can not admin the board', () => {
beforeEach(() => {
boardsStore.state.currentPage = 'new';
createComponent();
});
it('hides modal footer when user is not a board admin', () => {
expect(findModal().attributes('hide-footer')).toBeDefined();
});
it('displays board scope title', () => {
expect(findModal().attributes('title')).toBe('Board scope');
});
it('does not display a form', () => {
expect(findForm().exists()).toBe(false);
});
});
describe('when user can admin the board', () => {
beforeEach(() => {
boardsStore.state.currentPage = 'new';
createComponent({ canAdminBoard: true });
});
it('shows modal footer when user is a board admin', () => {
expect(findModal().attributes('hide-footer')).toBeUndefined();
});
it('displays a form', () => {
expect(findForm().exists()).toBe(true);
});
it('focuses an input field', async () => {
expect(document.activeElement).toBe(wrapper.vm.$refs.name);
});
});
describe('when creating a new board', () => {
beforeEach(() => {
boardsStore.state.currentPage = 'new';
});
describe('on non-scoped-board', () => {
beforeEach(() => {
createComponent({ canAdminBoard: true });
});
it('clears the form', () => {
expect(findConfigurationOptions().props('board')).toEqual(boardDefaults);
});
it('shows a correct title about creating a board', () => {
expect(findModal().attributes('title')).toBe('Create new board');
});
it('passes correct primary action text and variant', () => {
expect(findModalActionPrimary().text).toBe('Create board');
expect(findModalActionPrimary().attributes[0].variant).toBe('success');
});
it('does not render delete confirmation message', () => {
expect(findDeleteConfirmation().exists()).toBe(false);
});
it('renders form wrapper', () => {
expect(findFormWrapper().exists()).toBe(true);
});
it('passes a true isNewForm prop to BoardConfigurationOptions component', () => {
expect(findConfigurationOptions().props('isNewForm')).toBe(true);
});
});
it('passes a correct collapseScope property to BoardScope component on scoped board', async () => {
createComponent({ canAdminBoard: true, scopedIssueBoardFeatureEnabled: true });
await waitForPromises();
expect(findBoardScope().props('collapseScope')).toBe(true);
});
describe('when submitting a create event', () => {
beforeEach(() => {
const url = `${endpoints.boardsEndpoint}.json`;
axiosMock.onPost(url).reply(200, { id: '2', board_path: 'new path' });
});
it('does not call API if board name is empty', async () => {
createComponent({ canAdminBoard: true });
findInput().trigger('keyup.enter', { metaKey: true });
await waitForPromises();
expect(mutate).not.toHaveBeenCalled();
});
it('calls REST and GraphQL API and redirects to correct page', async () => {
createComponent({ canAdminBoard: true });
findInput().value = 'Test name';
findInput().trigger('input');
findInput().trigger('keyup.enter', { metaKey: true });
await waitForPromises();
expect(axiosMock.history.post[0].data).toBe(
JSON.stringify({ board: { ...boardDefaults, name: 'test', label_ids: [''] } }),
);
expect(mutate).toHaveBeenCalledWith({
mutation: createBoardMutation,
variables: {
id: 'gid://gitlab/Board/2',
},
});
await waitForPromises();
expect(visitUrl).toHaveBeenCalledWith('new path');
});
});
});
describe('buttons', () => {
it('cancel button triggers cancel()', () => {
wrapper.setMethods({ cancel: jest.fn() });
findModal().vm.$emit('cancel');
describe('when editing a board', () => {
beforeEach(() => {
boardsStore.state.currentPage = 'edit';
});
return wrapper.vm.$nextTick().then(() => {
expect(wrapper.vm.cancel).toHaveBeenCalled();
describe('on non-scoped-board', () => {
beforeEach(() => {
createComponent({ canAdminBoard: true });
});
it('clears the form', () => {
expect(findConfigurationOptions().props('board')).toEqual(currentBoard);
});
it('shows a correct title about creating a board', () => {
expect(findModal().attributes('title')).toBe('Edit board');
});
it('passes correct primary action text and variant', () => {
expect(findModalActionPrimary().text).toBe('Save changes');
expect(findModalActionPrimary().attributes[0].variant).toBe('info');
});
it('does not render delete confirmation message', () => {
expect(findDeleteConfirmation().exists()).toBe(false);
});
it('renders form wrapper', () => {
expect(findFormWrapper().exists()).toBe(true);
});
it('passes a false isNewForm prop to BoardConfigurationOptions component', () => {
expect(findConfigurationOptions().props('isNewForm')).toBe(false);
});
});
it('passes a correct collapseScope property to BoardScope component on scoped board', async () => {
createComponent({ canAdminBoard: true, scopedIssueBoardFeatureEnabled: true });
await waitForPromises();
expect(findBoardScope().props('collapseScope')).toBe(false);
});
describe('when submitting an update event', () => {
beforeEach(() => {
const url = endpoints.boardsEndpoint;
axiosMock.onPut(url).reply(200, { board_path: 'new path' });
});
it('calls REST and GraphQL API with correct parameters', async () => {
createComponent({ canAdminBoard: true });
findInput().trigger('keyup.enter', { metaKey: true });
await waitForPromises();
expect(axiosMock.history.put[0].data).toBe(
JSON.stringify({ board: { ...currentBoard, label_ids: [''] } }),
);
expect(mutate).toHaveBeenCalledWith({
mutation: createBoardMutation,
variables: {
id: `gid://gitlab/Board/${currentBoard.id}`,
},
});
});
});
});

View File

@ -7,9 +7,27 @@ import { DETAILS_PAGE_TITLE } from '~/registry/explorer/constants';
describe('Details Header', () => {
let wrapper;
const mountComponent = propsData => {
const defaultImage = {
name: 'foo',
updatedAt: '2020-11-03T13:29:21Z',
project: {
visibility: 'public',
},
};
const findLastUpdatedAndVisibility = () => wrapper.find('[data-testid="updated-and-visibility"]');
const waitForMetadataItems = async () => {
// Metadata items are printed by a loop in the title-area and it takes two ticks for them to be available
await wrapper.vm.$nextTick();
await wrapper.vm.$nextTick();
};
const mountComponent = (image = defaultImage) => {
wrapper = shallowMount(component, {
propsData,
propsData: {
image,
},
stubs: {
GlSprintf,
TitleArea,
@ -23,12 +41,34 @@ describe('Details Header', () => {
});
it('has the correct title ', () => {
mountComponent();
mountComponent({ ...defaultImage, name: '' });
expect(wrapper.text()).toMatchInterpolatedText(DETAILS_PAGE_TITLE);
});
it('shows imageName in the title', () => {
mountComponent({ imageName: 'foo' });
mountComponent();
expect(wrapper.text()).toContain('foo');
});
it('has a metadata item with last updated text', async () => {
mountComponent();
await waitForMetadataItems();
expect(findLastUpdatedAndVisibility().props('text')).toBe('Last updated 1 month ago');
});
describe('visibility icon', () => {
it('shows an eye when the project is public', async () => {
mountComponent();
await waitForMetadataItems();
expect(findLastUpdatedAndVisibility().props('icon')).toBe('eye');
});
it('shows an eye slashed when the project is not public', async () => {
mountComponent({ ...defaultImage, project: { visibility: 'private' } });
await waitForMetadataItems();
expect(findLastUpdatedAndVisibility().props('icon')).toBe('eye-slash');
});
});
});

View File

@ -114,8 +114,13 @@ export const containerRepositoryMock = {
location: 'host.docker.internal:5000/gitlab-org/gitlab-test/rails-12009',
canDelete: true,
createdAt: '2020-11-03T13:29:21Z',
updatedAt: '2020-11-03T13:29:21Z',
tagsCount: 13,
expirationPolicyStartedAt: null,
project: {
visibility: 'public',
__typename: 'Project',
},
};
export const tagsPageInfo = {

View File

@ -353,7 +353,12 @@ describe('Details Page', () => {
mountComponent();
await waitForApolloRequestRender();
expect(findDetailsHeader().props()).toEqual({ imageName: containerRepositoryMock.name });
expect(findDetailsHeader().props('image')).toMatchObject({
name: containerRepositoryMock.name,
project: {
visibility: containerRepositoryMock.project.visibility,
},
});
});
});

View File

@ -145,11 +145,11 @@ RSpec.describe Types::BaseField do
describe '#description' do
context 'feature flag given' do
let(:field) { described_class.new(name: 'test', type: GraphQL::STRING_TYPE, feature_flag: flag, null: false, description: 'Test description') }
let(:field) { described_class.new(name: 'test', type: GraphQL::STRING_TYPE, feature_flag: flag, null: false, description: 'Test description.') }
let(:flag) { :test_flag }
it 'prepends the description' do
expect(field.description). to eq 'Test description. Available only when feature flag `test_flag` is enabled'
expect(field.description). to eq 'Test description. Available only when feature flag `test_flag` is enabled.'
end
context 'falsey feature_flag values' do
@ -164,7 +164,7 @@ RSpec.describe Types::BaseField do
with_them do
it 'returns the correct description' do
expect(field.description).to eq('Test description')
expect(field.description).to eq('Test description.')
end
end
end
@ -181,11 +181,11 @@ RSpec.describe Types::BaseField do
it 'interacts well with the `feature_flag` property' do
field = subject(
deprecated: { milestone: '1.10', reason: 'Deprecation reason' },
description: 'Field description',
description: 'Field description.',
feature_flag: 'foo_flag'
)
expectation = 'Field description. Available only when feature flag `foo_flag` is enabled. Deprecated in 1.10: Deprecation reason'
expectation = 'Field description. Available only when feature flag `foo_flag` is enabled. Deprecated in 1.10: Deprecation reason.'
expect(field.description).to eq(expectation)
end

View File

@ -30,7 +30,7 @@ RSpec.describe Gitlab::Graphql::Docs::Renderer do
Class.new(Types::BaseObject) do
graphql_name 'ArrayTest'
field :foo, [GraphQL::STRING_TYPE], null: false, description: 'A description'
field :foo, [GraphQL::STRING_TYPE], null: false, description: 'A description.'
end
end
@ -40,7 +40,7 @@ RSpec.describe Gitlab::Graphql::Docs::Renderer do
| Field | Type | Description |
| ----- | ---- | ----------- |
| `foo` | String! => Array | A description |
| `foo` | String! => Array | A description. |
DOC
is_expected.to include(expectation)
@ -52,8 +52,8 @@ RSpec.describe Gitlab::Graphql::Docs::Renderer do
Class.new(Types::BaseObject) do
graphql_name 'OrderingTest'
field :foo, GraphQL::STRING_TYPE, null: false, description: 'A description of foo field'
field :bar, GraphQL::STRING_TYPE, null: false, description: 'A description of bar field'
field :foo, GraphQL::STRING_TYPE, null: false, description: 'A description of foo field.'
field :bar, GraphQL::STRING_TYPE, null: false, description: 'A description of bar field.'
end
end
@ -63,8 +63,8 @@ RSpec.describe Gitlab::Graphql::Docs::Renderer do
| Field | Type | Description |
| ----- | ---- | ----------- |
| `bar` | String! | A description of bar field |
| `foo` | String! | A description of foo field |
| `bar` | String! | A description of bar field. |
| `foo` | String! | A description of foo field. |
DOC
is_expected.to include(expectation)
@ -76,7 +76,7 @@ RSpec.describe Gitlab::Graphql::Docs::Renderer do
Class.new(Types::BaseObject) do
graphql_name 'DeprecatedTest'
field :foo, GraphQL::STRING_TYPE, null: false, deprecated: { reason: 'This is deprecated', milestone: '1.10' }, description: 'A description'
field :foo, GraphQL::STRING_TYPE, null: false, deprecated: { reason: 'This is deprecated', milestone: '1.10' }, description: 'A description.'
end
end
@ -86,7 +86,7 @@ RSpec.describe Gitlab::Graphql::Docs::Renderer do
| Field | Type | Description |
| ----- | ---- | ----------- |
| `foo` **{warning-solid}** | String! | **Deprecated:** This is deprecated. Deprecated in 1.10 |
| `foo` **{warning-solid}** | String! | **Deprecated:** This is deprecated. Deprecated in 1.10. |
DOC
is_expected.to include(expectation)
@ -98,14 +98,14 @@ RSpec.describe Gitlab::Graphql::Docs::Renderer do
enum_type = Class.new(Types::BaseEnum) do
graphql_name 'MyEnum'
value 'BAZ', description: 'A description of BAZ'
value 'BAR', description: 'A description of BAR', deprecated: { reason: 'This is deprecated', milestone: '1.10' }
value 'BAZ', description: 'A description of BAZ.'
value 'BAR', description: 'A description of BAR.', deprecated: { reason: 'This is deprecated', milestone: '1.10' }
end
Class.new(Types::BaseObject) do
graphql_name 'EnumTest'
field :foo, enum_type, null: false, description: 'A description of foo field'
field :foo, enum_type, null: false, description: 'A description of foo field.'
end
end
@ -115,8 +115,8 @@ RSpec.describe Gitlab::Graphql::Docs::Renderer do
| Value | Description |
| ----- | ----------- |
| `BAR` **{warning-solid}** | **Deprecated:** This is deprecated. Deprecated in 1.10 |
| `BAZ` | A description of BAZ |
| `BAR` **{warning-solid}** | **Deprecated:** This is deprecated. Deprecated in 1.10. |
| `BAZ` | A description of BAZ. |
DOC
is_expected.to include(expectation)

View File

@ -0,0 +1,25 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::SetupHelper::Workhorse do
describe '.make' do
subject { described_class.make }
context 'when there is a gmake' do
it 'returns gmake' do
expect(Gitlab::Popen).to receive(:popen).with(%w[which gmake]).and_return(['/usr/bin/gmake', 0])
expect(subject).to eq 'gmake'
end
end
context 'when there is no gmake' do
it 'returns make' do
expect(Gitlab::Popen).to receive(:popen).with(%w[which gmake]).and_return(['', 1])
expect(subject).to eq 'make'
end
end
end
end

View File

@ -151,8 +151,9 @@ RSpec.describe 'getting project information' do
)))
end
it 'can lookahead to eliminate N+1 queries', :use_clean_rails_memory_store_caching, :request_store do
expect { run_query(10) }.to issue_same_number_of_queries_as { run_query(1) }.or_fewer.ignoring_cached_queries
it 'can lookahead to eliminate N+1 queries' do
baseline = ActiveRecord::QueryRecorder.new { run_query(1) }
expect { run_query(10) }.not_to exceed_query_limit(baseline)
end
end

View File

@ -10,7 +10,7 @@ RSpec.describe RuboCop::Cop::Graphql::Descriptions, type: :rubocop do
subject(:cop) { described_class.new }
context 'fields' do
it 'adds an offense when there is no field description' do
it 'adds an offense when there is no description' do
inspect_source(<<~TYPE)
module Types
class FakeType < BaseObject
@ -24,24 +24,37 @@ RSpec.describe RuboCop::Cop::Graphql::Descriptions, type: :rubocop do
expect(cop.offenses.size).to eq 1
end
it 'does not add an offense for fields with a description' do
expect_no_offenses(<<~TYPE.strip)
it 'adds an offense when description does not end in a period' do
inspect_source(<<~TYPE)
module Types
class FakeType < BaseObject
graphql_name 'FakeTypeName'
argument :a_thing,
field :a_thing,
GraphQL::STRING_TYPE,
null: false,
description: 'A descriptive description'
end
end
TYPE
expect(cop.offenses.size).to eq 1
end
it 'does not add an offense when description is correct' do
expect_no_offenses(<<~TYPE.strip)
module Types
class FakeType < BaseObject
field :a_thing,
GraphQL::STRING_TYPE,
null: false,
description: 'A descriptive description.'
end
end
TYPE
end
end
context 'arguments' do
it 'adds an offense when there is no argument description' do
it 'adds an offense when there is no description' do
inspect_source(<<~TYPE)
module Types
class FakeType < BaseObject
@ -55,12 +68,10 @@ RSpec.describe RuboCop::Cop::Graphql::Descriptions, type: :rubocop do
expect(cop.offenses.size).to eq 1
end
it 'does not add an offense for arguments with a description' do
expect_no_offenses(<<~TYPE.strip)
it 'adds an offense when description does not end in a period' do
inspect_source(<<~TYPE)
module Types
class FakeType < BaseObject
graphql_name 'FakeTypeName'
argument :a_thing,
GraphQL::STRING_TYPE,
null: false,
@ -68,6 +79,77 @@ RSpec.describe RuboCop::Cop::Graphql::Descriptions, type: :rubocop do
end
end
TYPE
expect(cop.offenses.size).to eq 1
end
it 'does not add an offense when description is correct' do
expect_no_offenses(<<~TYPE.strip)
module Types
class FakeType < BaseObject
argument :a_thing,
GraphQL::STRING_TYPE,
null: false,
description: 'Behold! A description.'
end
end
TYPE
end
end
describe 'autocorrecting descriptions without periods' do
it 'can autocorrect' do
expect_offense(<<~TYPE)
module Types
class FakeType < BaseObject
field :a_thing,
^^^^^^^^^^^^^^^ `description` strings must end with a `.`.
GraphQL::STRING_TYPE,
null: false,
description: 'Behold! A description'
end
end
TYPE
expect_correction(<<~TYPE)
module Types
class FakeType < BaseObject
field :a_thing,
GraphQL::STRING_TYPE,
null: false,
description: 'Behold! A description.'
end
end
TYPE
end
it 'can autocorrect a heredoc' do
expect_offense(<<~TYPE)
module Types
class FakeType < BaseObject
field :a_thing,
^^^^^^^^^^^^^^^ `description` strings must end with a `.`.
GraphQL::STRING_TYPE,
null: false,
description: <<~DESC
Behold! A description
DESC
end
end
TYPE
expect_correction(<<~TYPE)
module Types
class FakeType < BaseObject
field :a_thing,
GraphQL::STRING_TYPE,
null: false,
description: <<~DESC
Behold! A description.
DESC
end
end
TYPE
end
end
end

View File

@ -241,15 +241,14 @@ module TestEnv
end
def setup_workhorse
install_workhorse_args = [workhorse_dir, workhorse_url].compact.join(',')
start = Time.now
puts "\n==> Setting up GitLab Workhorse..."
component_timed_setup(
'GitLab Workhorse',
install_dir: workhorse_dir,
version: Gitlab::Workhorse.version,
task: "gitlab:workhorse:install[#{install_workhorse_args}]") do
Gitlab::SetupHelper::Workhorse.create_configuration(workhorse_dir, nil)
end
FileUtils.rm_rf(workhorse_dir)
Gitlab::SetupHelper::Workhorse.compile_into(workhorse_dir)
Gitlab::SetupHelper::Workhorse.create_configuration(workhorse_dir, nil)
puts " GitLab Workhorse set up in #{Time.now - start} seconds...\n"
end
def workhorse_dir

View File

@ -3,6 +3,13 @@
module ExceedQueryLimitHelpers
MARGINALIA_ANNOTATION_REGEX = %r{\s*\/\*.*\*\/}.freeze
DB_QUERY_RE = Regexp.union([
/^(?<prefix>SELECT .* FROM "?[a-z_]+"?) (?<suffix>.*)$/m,
/^(?<prefix>UPDATE "?[a-z_]+"?) (?<suffix>.*)$/m,
/^(?<prefix>INSERT INTO "[a-z_]+" \((?:"[a-z_]+",?\s?)+\)) (?<suffix>.*)$/m,
/^(?<prefix>DELETE FROM "[a-z_]+") (?<suffix>.*)$/m
]).freeze
def with_threshold(threshold)
@threshold = threshold
self
@ -13,41 +20,129 @@ module ExceedQueryLimitHelpers
self
end
def show_common_queries
@show_common_queries = true
self
end
def ignoring(pattern)
@ignoring_pattern = pattern
self
end
def threshold
@threshold.to_i
end
def expected_count
if expected.is_a?(ActiveRecord::QueryRecorder)
expected.count
query_recorder_count(expected)
else
expected
end
end
def actual_count
@actual_count ||= if @query
recorder.log.select { |recorded| recorded =~ @query }.size
else
recorder.count
end
@actual_count ||= query_recorder_count(recorder)
end
def query_recorder_count(query_recorder)
return query_recorder.count unless @query || @ignoring_pattern
query_log(query_recorder).size
end
def query_log(query_recorder)
filtered = query_recorder.log
filtered = filtered.select { |q| q =~ @query } if @query
filtered = filtered.reject { |q| q =~ @ignoring_pattern } if @ignoring_pattern
filtered
end
def recorder
@recorder ||= ActiveRecord::QueryRecorder.new(skip_cached: skip_cached, &@subject_block)
end
def count_queries(queries)
queries.each_with_object(Hash.new(0)) { |query, counts| counts[query] += 1 }
# Take a query recorder and tabulate the frequencies of suffixes for each prefix.
#
# @return Hash[String, Hash[String, Int]]
#
# Example:
#
# r = ActiveRecord::QueryRecorder.new do
# SomeTable.create(x: 1, y: 2, z: 3)
# SomeOtherTable.where(id: 1).first
# SomeTable.create(x: 4, y: 5, z: 6)
# SomeOtherTable.all
# end
# count_queries(r)
# #=>
# {
# 'INSERT INTO "some_table" VALUES' => {
# '(1,2,3)' => 1,
# '(4,5,6)' => 1
# },
# 'SELECT * FROM "some_other_table"' => {
# 'WHERE id = 1 LIMIT 1' => 1,
# '' => 2
# }
# }
def count_queries(query_recorder)
strip_marginalia_annotations(query_log(query_recorder))
.map { |q| query_group_key(q) }
.group_by { |k| k[:prefix] }
.transform_values { |keys| frequencies(:suffix, keys) }
end
def frequencies(key, things)
things.group_by { |x| x[key] }.transform_values(&:size)
end
def query_group_key(query)
DB_QUERY_RE.match(query) || { prefix: query, suffix: '' }
end
def diff_query_counts(expected, actual)
expected_counts = expected.transform_values do |suffixes|
suffixes.transform_values { |n| [n, 0] }
end
recorded_counts = actual.transform_values do |suffixes|
suffixes.transform_values { |n| [0, n] }
end
combined_counts = expected_counts.merge(recorded_counts) do |_k, exp, got|
exp.merge(got) do |_k, exp_counts, got_counts|
exp_counts.zip(got_counts).map { |a, b| a + b }
end
end
unless @show_common_queries
combined_counts = combined_counts.transform_values do |suffs|
suffs.reject { |_k, counts| counts.first == counts.second }
end
end
combined_counts.reject { |_prefix, suffs| suffs.empty? }
end
def diff_query_group_message(query, suffixes)
suffix_messages = suffixes.map do |s, counts|
"-- (expected: #{counts.first}, got: #{counts.second})\n #{s}"
end
"#{query}...\n#{suffix_messages.join("\n")}"
end
def log_message
if expected.is_a?(ActiveRecord::QueryRecorder)
counts = count_queries(strip_marginalia_annotations(expected.log))
extra_queries = strip_marginalia_annotations(@recorder.log).reject { |query| counts[query] -= 1 unless counts[query] == 0 }
extra_queries_display = count_queries(extra_queries).map { |query, count| "[#{count}] #{query}" }
diff_counts = diff_query_counts(count_queries(expected), count_queries(@recorder))
sections = diff_counts.map { |q, suffixes| diff_query_group_message(q, suffixes) }
(['Extra queries:'] + extra_queries_display).join("\n\n")
<<~MSG
Query Diff:
-----------
#{sections.join("\n\n")}
MSG
else
@recorder.log_message
end

View File

@ -32,16 +32,16 @@ RSpec.shared_examples 'Gitlab-style deprecations' do
it 'adds a formatted `deprecated_reason` to the subject' do
deprecable = subject(deprecated: { milestone: '1.10', reason: 'Deprecation reason' })
expect(deprecable.deprecation_reason).to eq('Deprecation reason. Deprecated in 1.10')
expect(deprecable.deprecation_reason).to eq('Deprecation reason. Deprecated in 1.10.')
end
it 'appends to the description if given' do
deprecable = subject(
deprecated: { milestone: '1.10', reason: 'Deprecation reason' },
description: 'Deprecable description'
description: 'Deprecable description.'
)
expect(deprecable.description).to eq('Deprecable description. Deprecated in 1.10: Deprecation reason')
expect(deprecable.description).to eq('Deprecable description. Deprecated in 1.10: Deprecation reason.')
end
it 'does not append to the description if it is absent' do

View File

@ -22,6 +22,232 @@ RSpec.describe ExceedQueryLimitHelpers do
end
end
describe '#diff_query_group_message' do
it 'prints a group helpfully' do
test_matcher = TestMatcher.new
suffixes = {
'WHERE x = z' => [1, 1],
'WHERE x = y' => [1, 2],
'LIMIT 1' => [1, 0]
}
message = test_matcher.diff_query_group_message('SELECT * FROM foo', suffixes)
expect(message).to eq(<<~MSG.chomp)
SELECT * FROM foo...
-- (expected: 1, got: 1)
WHERE x = z
-- (expected: 1, got: 2)
WHERE x = y
-- (expected: 1, got: 0)
LIMIT 1
MSG
end
end
describe '#diff_query_counts' do
let(:expected) do
ActiveRecord::QueryRecorder.new do
TestQueries.where(version: 'foobar').to_a
TestQueries.where(version: 'also foobar and baz').to_a
TestQueries.count
TestQueries.first
TestQueries.where(version: 'foobar').to_a
TestQueries.where(version: 'x').update_all(version: 'y')
TestQueries.where(version: 'foobar').count
TestQueries.where(version: 'z').delete_all
end
end
let(:actual) do
ActiveRecord::QueryRecorder.new do
TestQueries.where(version: 'foobar').to_a
TestQueries.where(version: 'also foobar and baz').to_a
TestQueries.count
TestQueries.create!(version: 'x')
TestQueries.where(version: 'foobar').to_a
TestQueries.where(version: 'x').update_all(version: 'y')
TestQueries.where(version: 'foobar').count
TestQueries.count
TestQueries.where(version: 'y').update_all(version: 'z')
TestQueries.where(version: 'z').delete_all
end
end
it 'merges two query counts' do
test_matcher = TestMatcher.new
diff = test_matcher.diff_query_counts(
test_matcher.count_queries(expected),
test_matcher.count_queries(actual)
)
expect(diff).to eq({
"SELECT \"schema_migrations\".* FROM \"schema_migrations\"" => {
"ORDER BY \"schema_migrations\".\"version\" ASC LIMIT 1" => [1, 0]
},
"SELECT COUNT(*) FROM \"schema_migrations\"" => { "" => [1, 2] },
"UPDATE \"schema_migrations\"" => {
"SET \"version\" = 'z' WHERE \"schema_migrations\".\"version\" = 'y'" => [0, 1]
},
"SAVEPOINT active_record_1" => { "" => [0, 1] },
"INSERT INTO \"schema_migrations\" (\"version\")" => {
"VALUES ('x') RETURNING \"version\"" => [0, 1]
},
"RELEASE SAVEPOINT active_record_1" => { "" => [0, 1] }
})
end
it 'can show common queries if so desired' do
test_matcher = TestMatcher.new.show_common_queries
diff = test_matcher.diff_query_counts(
test_matcher.count_queries(expected),
test_matcher.count_queries(actual)
)
expect(diff).to eq({
"SELECT \"schema_migrations\".* FROM \"schema_migrations\"" => {
"WHERE \"schema_migrations\".\"version\" = 'foobar'" => [2, 2],
"WHERE \"schema_migrations\".\"version\" = 'also foobar and baz'" => [1, 1],
"ORDER BY \"schema_migrations\".\"version\" ASC LIMIT 1" => [1, 0]
},
"SELECT COUNT(*) FROM \"schema_migrations\"" => {
"" => [1, 2],
"WHERE \"schema_migrations\".\"version\" = 'foobar'" => [1, 1]
},
"UPDATE \"schema_migrations\"" => {
"SET \"version\" = 'y' WHERE \"schema_migrations\".\"version\" = 'x'" => [1, 1],
"SET \"version\" = 'z' WHERE \"schema_migrations\".\"version\" = 'y'" => [0, 1]
},
"DELETE FROM \"schema_migrations\"" => {
"WHERE \"schema_migrations\".\"version\" = 'z'" => [1, 1]
},
"SAVEPOINT active_record_1" => {
"" => [0, 1]
},
"INSERT INTO \"schema_migrations\" (\"version\")" => {
"VALUES ('x') RETURNING \"version\"" => [0, 1]
},
"RELEASE SAVEPOINT active_record_1" => {
"" => [0, 1]
}
})
end
end
describe '#count_queries' do
it 'handles queries with suffixes over multiple lines' do
test_matcher = TestMatcher.new
recorder = ActiveRecord::QueryRecorder.new do
TestQueries.find_by(version: %w(foo bar baz).join("\n"))
TestQueries.find_by(version: %w(foo biz baz).join("\n"))
TestQueries.find_by(version: %w(foo bar baz).join("\n"))
end
recorder.count
expect(test_matcher.count_queries(recorder)).to eq({
'SELECT "schema_migrations".* FROM "schema_migrations"' => {
%Q[WHERE "schema_migrations"."version" = 'foo\nbar\nbaz' LIMIT 1] => 2,
%Q[WHERE "schema_migrations"."version" = 'foo\nbiz\nbaz' LIMIT 1] => 1
}
})
end
it 'can aggregate queries' do
test_matcher = TestMatcher.new
recorder = ActiveRecord::QueryRecorder.new do
TestQueries.where(version: 'foobar').to_a
TestQueries.where(version: 'also foobar and baz').to_a
TestQueries.count
TestQueries.create!(version: 'x')
TestQueries.first
TestQueries.where(version: 'foobar').to_a
TestQueries.where(version: 'x').update_all(version: 'y')
TestQueries.where(version: 'foobar').count
TestQueries.count
TestQueries.where(version: 'y').update_all(version: 'z')
TestQueries.where(version: 'z').delete_all
end
recorder.count
expect(test_matcher.count_queries(recorder)).to eq({
'SELECT "schema_migrations".* FROM "schema_migrations"' => {
%q[WHERE "schema_migrations"."version" = 'foobar'] => 2,
%q[WHERE "schema_migrations"."version" = 'also foobar and baz'] => 1,
%q[ORDER BY "schema_migrations"."version" ASC LIMIT 1] => 1
},
'SELECT COUNT(*) FROM "schema_migrations"' => {
"" => 2,
%q[WHERE "schema_migrations"."version" = 'foobar'] => 1
},
'SAVEPOINT active_record_1' => { "" => 1 },
'INSERT INTO "schema_migrations" ("version")' => {
%q[VALUES ('x') RETURNING "version"] => 1
},
'RELEASE SAVEPOINT active_record_1' => { "" => 1 },
'UPDATE "schema_migrations"' => {
%q[SET "version" = 'y' WHERE "schema_migrations"."version" = 'x'] => 1,
%q[SET "version" = 'z' WHERE "schema_migrations"."version" = 'y'] => 1
},
'DELETE FROM "schema_migrations"' => {
%q[WHERE "schema_migrations"."version" = 'z'] => 1
}
})
end
end
it 'can count queries' do
test_matcher = TestMatcher.new
test_matcher.verify_count do
TestQueries.where(version: 'foobar').to_a
TestQueries.where(version: 'also foobar and baz').to_a
TestQueries.first
TestQueries.count
end
expect(test_matcher.actual_count).to eq(4)
end
it 'can select specific queries' do
test_matcher = TestMatcher.new.for_query(/foobar/)
test_matcher.verify_count do
TestQueries.where(version: 'foobar').to_a
TestQueries.where(version: 'also foobar and baz').to_a
TestQueries.first
TestQueries.count
end
expect(test_matcher.actual_count).to eq(2)
end
it 'can ignore specific queries' do
test_matcher = TestMatcher.new.ignoring(/foobar/)
test_matcher.verify_count do
TestQueries.where(version: 'foobar').to_a
TestQueries.where(version: 'also foobar and baz').to_a
TestQueries.first
end
expect(test_matcher.actual_count).to eq(1)
end
it 'can perform inclusion and exclusion' do
test_matcher = TestMatcher.new.for_query(/foobar/).ignoring(/baz/)
test_matcher.verify_count do
TestQueries.where(version: 'foobar').to_a
TestQueries.where(version: 'also foobar and baz').to_a
TestQueries.first
TestQueries.count
end
expect(test_matcher.actual_count).to eq(1)
end
it 'does not contain marginalia annotations' do
test_matcher = TestMatcher.new
test_matcher.verify_count do

View File

@ -9,9 +9,13 @@ RSpec.describe 'gitlab:workhorse namespace rake task' do
describe 'install' do
let(:repo) { 'https://gitlab.com/gitlab-org/gitlab-workhorse.git' }
let(:clone_path) { Rails.root.join('tmp/tests/gitlab-workhorse').to_s }
let(:clone_path) { Dir.mktmpdir('gitlab:workhorse:install-rake-test') }
let(:workhorse_source) { Rails.root.join('workhorse').to_s }
after do
FileUtils.rm_rf(clone_path)
end
context 'no dir given' do
it 'aborts and display a help message' do
# avoid writing task output to spec progress
@ -20,7 +24,7 @@ RSpec.describe 'gitlab:workhorse namespace rake task' do
end
end
context 'when an underlying Git command fail' do
context 'when an underlying Git command fails' do
it 'aborts and display a help message' do
expect(main_object)
.to receive(:checkout_or_clone_version).and_raise 'Git error'
@ -29,47 +33,26 @@ RSpec.describe 'gitlab:workhorse namespace rake task' do
end
end
describe 'checkout or clone' do
it 'calls checkout_or_clone_version with the right arguments' do
expect(main_object)
.to receive(:checkout_or_clone_version).with(version: 'workhorse-move-notice', repo: repo, target_dir: clone_path, clone_opts: %w[--depth 1])
it 'clones the origin and creates a gitlab-workhorse binary' do
FileUtils.rm_rf(clone_path)
run_rake_task('gitlab:workhorse:install', clone_path)
end
end
describe 'gmake/make' do
before do
FileUtils.mkdir_p(clone_path)
end
context 'gmake is available' do
before do
expect(main_object).to receive(:checkout_or_clone_version)
allow(Object).to receive(:run_command!).with(['gmake']).and_return(true)
Dir.mktmpdir('fake-workhorse-origin') do |workhorse_origin|
[
%W[git init -q #{workhorse_origin}],
%W[git -C #{workhorse_origin} checkout -q -b workhorse-move-notice],
%W[touch #{workhorse_origin}/proof-that-repo-got-cloned],
%W[git -C #{workhorse_origin} add .],
%W[git -C #{workhorse_origin} commit -q -m init],
%W[git -C #{workhorse_origin} checkout -q -b master]
].each do |cmd|
raise "#{cmd.join(' ')} failed" unless system(*cmd)
end
it 'calls gmake in the gitlab-workhorse directory' do
expect(Gitlab::Popen).to receive(:popen).with(%w[which gmake]).and_return(['/usr/bin/gmake', 0])
expect(main_object).to receive(:run_command!).with(["gmake", "-C", workhorse_source, "install", "PREFIX=#{clone_path}"]).and_return(true)
run_rake_task('gitlab:workhorse:install', clone_path)
end
run_rake_task('gitlab:workhorse:install', clone_path, File.join(workhorse_origin, '.git'))
end
context 'gmake is not available' do
before do
expect(main_object).to receive(:checkout_or_clone_version)
allow(main_object).to receive(:run_command!).with(['make']).and_return(true)
end
it 'calls make in the gitlab-workhorse directory' do
expect(Gitlab::Popen).to receive(:popen).with(%w[which gmake]).and_return(['', 42])
expect(main_object).to receive(:run_command!).with(["make", "-C", workhorse_source, "install", "PREFIX=#{clone_path}"]).and_return(true)
run_rake_task('gitlab:workhorse:install', clone_path)
end
end
expect(File.exist?(File.join(clone_path, 'proof-that-repo-got-cloned'))).to be true
expect(File.executable?(File.join(clone_path, 'gitlab-workhorse'))).to be true
end
end
end