Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
9769ccf613
commit
9b762f50fe
79 changed files with 1629 additions and 385 deletions
|
@ -203,20 +203,20 @@ ee/lib/ee/gitlab/checks/** @proglottis @toon @zj-gitlab
|
|||
lib/gitlab/checks/** @proglottis @toon @zj-gitlab
|
||||
|
||||
^[Documentation Directories]
|
||||
.markdownlint.yml @marcel.amirault @eread @aqualls @cnorris
|
||||
/doc/.markdownlint @marcel.amirault @eread @aqualls @cnorris
|
||||
.markdownlint.yml @marcel.amirault @eread @aqualls @dianalogan
|
||||
/doc/.markdownlint @marcel.amirault @eread @aqualls @dianalogan
|
||||
/doc/ @gl-docsteam
|
||||
/doc/.vale/ @marcel.amirault @eread @aqualls @cnorris
|
||||
/doc/.vale/ @marcel.amirault @eread @aqualls @dianalogan
|
||||
|
||||
^[Documentation Pages]
|
||||
/doc/administration/application_settings_cache.md @marcel.amirault
|
||||
/doc/administration/application_settings_cache.md @marcia
|
||||
/doc/administration/audit_event_streaming.md @eread
|
||||
/doc/administration/audit_events.md @eread
|
||||
/doc/administration/audit_reports.md @eread
|
||||
/doc/administration/auditor_users.md @axil
|
||||
/doc/administration/auditor_users.md @eread
|
||||
/doc/administration/auth/ @eread
|
||||
/doc/administration/cicd.md @marcel.amirault
|
||||
/doc/administration/clusters/kas.md @sselhorn
|
||||
/doc/administration/clusters/kas.md @marcia
|
||||
/doc/administration/compliance.md @eread
|
||||
/doc/administration/configure.md @axil
|
||||
/doc/administration/consul.md @axil
|
||||
|
@ -240,15 +240,15 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
|
|||
/doc/administration/integration/terminal.md @kpaizee
|
||||
/doc/administration/invalidate_markdown_cache.md @msedlakjakubowski
|
||||
/doc/administration/issue_closing_pattern.md @aqualls
|
||||
/doc/administration/job_artifacts.md @eread
|
||||
/doc/administration/job_artifacts.md @marcel.amirault
|
||||
/doc/administration/job_logs.md @sselhorn
|
||||
/doc/administration/lfs/index.md @aqualls
|
||||
/doc/administration/libravatar.md @axil
|
||||
/doc/administration/load_balancer.md @axil
|
||||
/doc/administration/logs.md @ngaskill
|
||||
/doc/administration/logs.md @msedlakjakubowski
|
||||
/doc/administration/maintenance_mode/index.md @axil
|
||||
/doc/administration/merge_request_diffs.md @aqualls
|
||||
/doc/administration/monitoring/ @ngaskill
|
||||
/doc/administration/monitoring/ @msedlakjakubowski
|
||||
/doc/administration/monitoring/prometheus/index.md @axil
|
||||
/doc/administration/nfs.md @axil
|
||||
/doc/administration/object_storage.md @axil
|
||||
|
@ -256,11 +256,11 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
|
|||
/doc/administration/operations/moving_repositories.md @eread
|
||||
/doc/administration/operations/sidekiq_memory_killer.md @marcel.amirault
|
||||
/doc/administration/package_information/ @axil
|
||||
/doc/administration/packages/ @ngaskill
|
||||
/doc/administration/packages/ @claytoncornell
|
||||
/doc/administration/pages/index.md @aqualls
|
||||
/doc/administration/pages/source.md @aqualls
|
||||
/doc/administration/polling.md @axil
|
||||
/doc/administration/postgresql/ @aqualls
|
||||
/doc/administration/postgresql/ @marcia
|
||||
/doc/administration/pseudonymizer.md @axil
|
||||
/doc/administration/raketasks/ @axil
|
||||
/doc/administration/raketasks/praefect.md @eread
|
||||
|
@ -280,17 +280,18 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
|
|||
/doc/administration/snippets/index.md @aqualls
|
||||
/doc/administration/static_objects_external_storage.md @aqualls
|
||||
/doc/administration/system_hooks.md @kpaizee
|
||||
/doc/administration/terraform_state.md @sselhorn
|
||||
/doc/administration/terraform_state.md @marcia
|
||||
/doc/administration/timezone.md @axil
|
||||
/doc/administration/troubleshooting/ @axil
|
||||
/doc/administration/troubleshooting/elasticsearch.md @rdickenson
|
||||
/doc/administration/troubleshooting/postgresql.md @aqualls
|
||||
/doc/administration/troubleshooting/postgresql.md @marcia
|
||||
/doc/administration/uploads.md @axil
|
||||
/doc/administration/user_settings.md @eread
|
||||
/doc/administration/whats-new.md @kpaizee
|
||||
/doc/administration/wikis/index.md @aqualls
|
||||
/doc/api/access_requests.md @eread
|
||||
/doc/api/admin_sidekiq_queues.md @axil
|
||||
/doc/api/alert_management_alerts.md @msedlakjakubowski
|
||||
/doc/api/api_resources.md @kpaizee
|
||||
/doc/api/appearance.md @eread
|
||||
/doc/api/applications.md @eread
|
||||
|
@ -300,12 +301,13 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
|
|||
/doc/api/boards.md @msedlakjakubowski
|
||||
/doc/api/branches.md @aqualls
|
||||
/doc/api/broadcast_messages.md @kpaizee
|
||||
/doc/api/bulk_imports.md @ngaskill
|
||||
/doc/api/bulk_imports.md @eread
|
||||
/doc/api/cluster_agents.md @marcia
|
||||
/doc/api/commits.md @aqualls
|
||||
/doc/api/container_registry.md @ngaskill
|
||||
/doc/api/container_registry.md @claytoncornell
|
||||
/doc/api/custom_attributes.md @kpaizee
|
||||
/doc/api/dependencies.md @rdickenson
|
||||
/doc/api/dependency_proxy.md @ngaskill
|
||||
/doc/api/dependency_proxy.md @claytoncornell
|
||||
/doc/api/deploy_keys.md @rdickenson
|
||||
/doc/api/deploy_tokens.md @rdickenson
|
||||
/doc/api/deployments.md @rdickenson
|
||||
|
@ -316,12 +318,11 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
|
|||
/doc/api/epic_issues.md @msedlakjakubowski
|
||||
/doc/api/epic_links.md @msedlakjakubowski
|
||||
/doc/api/epics.md @msedlakjakubowski
|
||||
/doc/api/error_tracking.md @ngaskill
|
||||
/doc/api/error_tracking.md @msedlakjakubowski
|
||||
/doc/api/events.md @eread
|
||||
/doc/api/experiments.md @kpaizee
|
||||
/doc/api/feature_flag_specs.md @rdickenson
|
||||
/doc/api/feature_flag_user_lists.md @rdickenson
|
||||
/doc/api/feature_flags_legacy.md @rdickenson
|
||||
/doc/api/feature_flags.md @rdickenson
|
||||
/doc/api/features.md @rdickenson
|
||||
/doc/api/freeze_periods.md @rdickenson
|
||||
|
@ -333,20 +334,21 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
|
|||
/doc/api/group_activity_analytics.md @fneill
|
||||
/doc/api/group_badges.md @fneill
|
||||
/doc/api/group_boards.md @msedlakjakubowski
|
||||
/doc/api/group_clusters.md @sselhorn
|
||||
/doc/api/group_import_export.md @ngaskill
|
||||
/doc/api/group_clusters.md @marcia
|
||||
/doc/api/group_import_export.md @eread
|
||||
/doc/api/group_iterations.md @msedlakjakubowski
|
||||
/doc/api/group_labels.md @msedlakjakubowski
|
||||
/doc/api/group_level_variables.md @marcel.amirault
|
||||
/doc/api/group_milestones.md @msedlakjakubowski
|
||||
/doc/api/group_protected_environments.md @rdickenson
|
||||
/doc/api/group_relations_export.md @ngaskill
|
||||
/doc/api/group_relations_export.md @eread
|
||||
/doc/api/group_releases.md @rdickenson
|
||||
/doc/api/group_repository_storage_moves.md @aqualls
|
||||
/doc/api/group_wikis.md @aqualls
|
||||
/doc/api/groups.md @fneill
|
||||
/doc/api/import.md @ngaskill
|
||||
/doc/api/import.md @eread
|
||||
/doc/api/index.md @kpaizee
|
||||
/doc/api/instance_clusters.md @sselhorn
|
||||
/doc/api/instance_clusters.md @marcia
|
||||
/doc/api/instance_level_ci_variables.md @marcel.amirault
|
||||
/doc/api/integrations.md @kpaizee
|
||||
/doc/api/invitations.md @kpaizee
|
||||
|
@ -354,7 +356,7 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
|
|||
/doc/api/issues_statistics.md @msedlakjakubowski
|
||||
/doc/api/issues.md @msedlakjakubowski
|
||||
/doc/api/iterations.md @msedlakjakubowski
|
||||
/doc/api/job_artifacts.md @eread
|
||||
/doc/api/job_artifacts.md @marcel.amirault
|
||||
/doc/api/jobs.md @marcel.amirault
|
||||
/doc/api/keys.md @aqualls
|
||||
/doc/api/labels.md @msedlakjakubowski
|
||||
|
@ -368,31 +370,30 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
|
|||
/doc/api/merge_request_context_commits.md @aqualls
|
||||
/doc/api/merge_requests.md @aqualls
|
||||
/doc/api/merge_trains.md @marcel.amirault
|
||||
/doc/api/metrics_dashboard_annotations.md @ngaskill
|
||||
/doc/api/metrics_user_starred_dashboards.md @ngaskill
|
||||
/doc/api/metrics_dashboard_annotations.md @msedlakjakubowski
|
||||
/doc/api/metrics_user_starred_dashboards.md @msedlakjakubowski
|
||||
/doc/api/milestones.md @msedlakjakubowski
|
||||
/doc/api/namespaces.md @eread
|
||||
/doc/api/notes.md @msedlakjakubowski
|
||||
/doc/api/notification_settings.md @msedlakjakubowski
|
||||
/doc/api/oauth2.md @eread
|
||||
/doc/api/openapi/openapi_interactive.md @kpaizee
|
||||
/doc/api/packages.md @ngaskill
|
||||
/doc/api/packages/ @ngaskill
|
||||
/doc/api/packages.md @claytoncornell
|
||||
/doc/api/packages/ @claytoncornell
|
||||
/doc/api/pages_domains.md @aqualls
|
||||
/doc/api/pages.md @aqualls
|
||||
/doc/api/personal_access_tokens.md @eread
|
||||
/doc/api/pipeline_schedules.md @marcel.amirault
|
||||
/doc/api/pipeline_triggers.md @marcel.amirault
|
||||
/doc/api/pipelines.md @marcel.amirault
|
||||
/doc/api/scim.md @eread
|
||||
/doc/api/plan_limits.md @eread
|
||||
/doc/api/project_access_tokens.md @eread
|
||||
/doc/api/project_aliases.md @aqualls
|
||||
/doc/api/project_badges.md @aqualls
|
||||
/doc/api/project_clusters.md @sselhorn
|
||||
/doc/api/project_clusters.md @marcia
|
||||
/doc/api/project_import_export.md @aqualls
|
||||
/doc/api/project_level_variables.md @marcel.amirault
|
||||
/doc/api/project_relations_export.md @ngaskill
|
||||
/doc/api/project_relations_export.md @eread
|
||||
/doc/api/project_repository_storage_moves.md @eread
|
||||
/doc/api/project_snippets.md @aqualls
|
||||
/doc/api/project_statistics.md @aqualls
|
||||
|
@ -415,7 +416,7 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
|
|||
/doc/api/resource_state_events.md @msedlakjakubowski
|
||||
/doc/api/resource_weight_events.md @msedlakjakubowski
|
||||
/doc/api/runners.md @sselhorn
|
||||
/doc/api/plan_limits.md @eread
|
||||
/doc/api/scim.md @eread
|
||||
/doc/api/search.md @aqualls
|
||||
/doc/api/secure_files.md @marcel.amirault
|
||||
/doc/api/settings.md @eread
|
||||
|
@ -433,19 +434,19 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
|
|||
/doc/api/templates/licenses.md @rdickenson
|
||||
/doc/api/todos.md @msedlakjakubowski
|
||||
/doc/api/topics.md @fneill
|
||||
/doc/api/usage_data.md @fneill
|
||||
/doc/api/usage_data.md @claytoncornell
|
||||
/doc/api/users.md @eread
|
||||
/doc/api/version.md @kpaizee
|
||||
/doc/api/visual_review_discussions.md @eread
|
||||
/doc/api/vulnerabilities.md @fneill
|
||||
/doc/api/vulnerability_exports.md @fneill
|
||||
/doc/api/vulnerability_findings.md @fneill
|
||||
/doc/api/visual_review_discussions.md @marcel.amirault
|
||||
/doc/api/vulnerabilities.md @claytoncornell
|
||||
/doc/api/vulnerability_exports.md @claytoncornell
|
||||
/doc/api/vulnerability_findings.md @claytoncornell
|
||||
/doc/api/wikis.md @aqualls
|
||||
/doc/architecture/blueprints/container_registry_metadata_database/index.md @ngaskill
|
||||
/doc/architecture/blueprints/database/scalability/patterns/ @aqualls
|
||||
/doc/architecture/blueprints/gitlab_to_kubernetes_communication/index.md @sselhorn
|
||||
/doc/architecture/blueprints/container_registry_metadata_database/index.md @claytoncornell
|
||||
/doc/architecture/blueprints/database/scalability/patterns/ @marcia
|
||||
/doc/architecture/blueprints/gitlab_to_kubernetes_communication/index.md @marcia
|
||||
/doc/ci/caching/index.md @marcel.amirault
|
||||
/doc/ci/chatops/index.md @sselhorn
|
||||
/doc/ci/chatops/index.md @marcia
|
||||
/doc/ci/ci_cd_for_external_repos/ @marcel.amirault
|
||||
/doc/ci/cloud_deployment/ecs/quick_start_guide.md @rdickenson
|
||||
/doc/ci/cloud_deployment/index.md @rdickenson
|
||||
|
@ -460,11 +461,11 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
|
|||
/doc/ci/examples/authenticating-with-hashicorp-vault/index.md @marcel.amirault
|
||||
/doc/ci/examples/deployment/composer-npm-deploy.md @rdickenson
|
||||
/doc/ci/examples/deployment/index.md @rdickenson
|
||||
/doc/ci/examples/end_to_end_testing_webdriverio/index.md @eread
|
||||
/doc/ci/examples/end_to_end_testing_webdriverio/index.md @marcel.amirault
|
||||
/doc/ci/examples/index.md @marcel.amirault
|
||||
/doc/ci/examples/laravel_with_gitlab_and_envoy/index.md @marcel.amirault
|
||||
/doc/ci/examples/php.md @marcel.amirault
|
||||
/doc/ci/examples/semantic-release.md @ngaskill
|
||||
/doc/ci/examples/semantic-release.md @claytoncornell
|
||||
/doc/ci/git_submodules.md @marcel.amirault
|
||||
/doc/ci/index.md @marcel.amirault
|
||||
/doc/ci/interactive_web_terminal/index.md @sselhorn
|
||||
|
@ -474,16 +475,14 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
|
|||
/doc/ci/jobs/job_control.md @marcel.amirault
|
||||
/doc/ci/large_repositories/index.md @sselhorn
|
||||
/doc/ci/lint.md @marcel.amirault
|
||||
/doc/ci/metrics_reports.md @eread
|
||||
/doc/ci/metrics_reports.md @marcel.amirault
|
||||
/doc/ci/migration/circleci.md @marcel.amirault
|
||||
/doc/ci/migration/jenkins.md @marcel.amirault
|
||||
/doc/ci/pipeline_editor/index.md @marcel.amirault
|
||||
/doc/ci/pipelines/ @marcel.amirault
|
||||
/doc/ci/pipelines/job_artifacts.md @eread
|
||||
/doc/ci/pipelines/pipeline_artifacts.md @eread
|
||||
/doc/ci/quick_start/index.md @marcel.amirault
|
||||
/doc/ci/resource_groups/index.md @rdickenson
|
||||
/doc/ci/review_apps/index.md @eread
|
||||
/doc/ci/review_apps/index.md @marcel.amirault
|
||||
/doc/ci/runners/ @sselhorn
|
||||
/doc/ci/secrets/index.md @marcel.amirault
|
||||
/doc/ci/services/ @sselhorn
|
||||
|
@ -491,23 +490,22 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
|
|||
/doc/ci/test_cases/index.md @msedlakjakubowski
|
||||
/doc/ci/triggers/index.md @marcel.amirault
|
||||
/doc/ci/troubleshooting.md @marcel.amirault
|
||||
/doc/ci/unit_test_reports.md @eread
|
||||
/doc/ci/unit_test_reports.md @marcel.amirault
|
||||
/doc/ci/variables/ @marcel.amirault
|
||||
/doc/ci/yaml/ @marcel.amirault
|
||||
/doc/ci/yaml/artifacts_reports.md @eread
|
||||
/doc/development/adding_database_indexes.md @aqualls
|
||||
/doc/development/adding_database_indexes.md @marcia
|
||||
/doc/development/application_limits.md @axil
|
||||
/doc/development/approval_rules.md @aqualls
|
||||
/doc/development/audit_event_guide/index.md @eread
|
||||
/doc/development/auto_devops.md @sselhorn
|
||||
/doc/development/avoiding_downtime_in_migrations.md @aqualls
|
||||
/doc/development/auto_devops.md @marcia
|
||||
/doc/development/backend/create_source_code_be/index.md @aqualls
|
||||
/doc/development/backend/ruby_style_guide.md @marcia
|
||||
/doc/development/background_migrations.md @marcia
|
||||
/doc/development/batched_background_migrations.md @marcia
|
||||
/doc/development/build_test_package.md @axil
|
||||
/doc/development/bulk_import.md @ngaskill
|
||||
/doc/development/bulk_import.md @eread
|
||||
/doc/development/cached_queries.md @marcia
|
||||
/doc/development/cascading_settings.md @eread
|
||||
/doc/development/chatops_on_gitlabcom.md @sselhorn
|
||||
/doc/development/chatops_on_gitlabcom.md @marcia
|
||||
/doc/development/cicd/cicd_reference_documentation_guide.md @marcel.amirault
|
||||
/doc/development/cicd/index.md @marcel.amirault
|
||||
/doc/development/cicd/schema.md @marcel.amirault
|
||||
|
@ -515,27 +513,28 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
|
|||
/doc/development/code_intelligence/index.md @aqualls
|
||||
/doc/development/contributing/ @marcia
|
||||
/doc/development/contributing/merge_request_workflow.md @aqualls
|
||||
/doc/development/creating_enums.md @aqualls
|
||||
/doc/development/database_debugging.md @aqualls
|
||||
/doc/development/database_query_comments.md @aqualls
|
||||
/doc/development/database_review.md @aqualls
|
||||
/doc/development/database/client_side_connection_pool.md @marcia
|
||||
/doc/development/database/ @aqualls
|
||||
/doc/development/creating_enums.md @marcia
|
||||
/doc/development/database_debugging.md @marcia
|
||||
/doc/development/database_query_comments.md @marcia
|
||||
/doc/development/database_review.md @marcia
|
||||
/doc/development/database/ @marcia
|
||||
/doc/development/database/multiple_databases.md @marcia
|
||||
/doc/development/db_dump.md @aqualls
|
||||
/doc/development/db_dump.md @marcia
|
||||
/doc/development/developing_with_solargraph.md @aqualls
|
||||
/doc/development/distributed_tracing.md @ngaskill
|
||||
/doc/development/diffs.md @aqualls
|
||||
/doc/development/distributed_tracing.md @msedlakjakubowski
|
||||
/doc/development/documentation/feature_flags.md @sselhorn
|
||||
/doc/development/documentation/graphql_styleguide.md @sselhorn
|
||||
/doc/development/documentation/index.md @cnorris
|
||||
/doc/development/documentation/redirects.md @cnorris
|
||||
/doc/development/documentation/review_apps.md @cnorris
|
||||
/doc/development/documentation/index.md @dianalogan
|
||||
/doc/development/documentation/redirects.md @dianalogan
|
||||
/doc/development/documentation/review_apps.md @dianalogan
|
||||
/doc/development/documentation/structure.md @sselhorn
|
||||
/doc/development/documentation/styleguide/ @sselhorn
|
||||
/doc/development/documentation/testing.md @cnorris
|
||||
/doc/development/elasticsearch.md @rdickenson
|
||||
/doc/development/experiment_guide/ @kpaizee
|
||||
/doc/development/export_csv.md @ngaskill
|
||||
/doc/development/documentation/testing.md @dianalogan
|
||||
/doc/development/elasticsearch.md @marcia
|
||||
/doc/development/experiment_guide/gitlab_experiment.md @kpaizee
|
||||
/doc/development/experiment_guide/index.md @kpaizee
|
||||
/doc/development/export_csv.md @eread
|
||||
/doc/development/fe_guide/content_editor.md @aqualls
|
||||
/doc/development/fe_guide/dark_mode.md @marcia
|
||||
/doc/development/fe_guide/graphql.md @marcia
|
||||
|
@ -544,70 +543,70 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
|
|||
/doc/development/feature_flags/controls.md @marcia
|
||||
/doc/development/feature_flags/index.md @marcia
|
||||
/doc/development/filtering_by_label.md @msedlakjakubowski
|
||||
/doc/development/foreign_keys.md @aqualls
|
||||
/doc/development/foreign_keys.md @marcia
|
||||
/doc/development/geo.md @axil
|
||||
/doc/development/geo/framework.md @axil
|
||||
/doc/development/git_object_deduplication.md @eread
|
||||
/doc/development/gitaly.md @eread
|
||||
/doc/development/graphql_guide/ @kpaizee
|
||||
/doc/development/graphql_guide/batchloader.md @aqualls
|
||||
/doc/development/hash_indexes.md @aqualls
|
||||
/doc/development/i18n/ @ngaskill
|
||||
/doc/development/image_scaling.md @marcel.amirault
|
||||
/doc/development/import_export.md @ngaskill
|
||||
/doc/development/graphql_guide/batchloader.md @marcia
|
||||
/doc/development/hash_indexes.md @marcia
|
||||
/doc/development/i18n/ @eread
|
||||
/doc/development/image_scaling.md @marcia
|
||||
/doc/development/import_export.md @eread
|
||||
/doc/development/index.md @marcia
|
||||
/doc/development/insert_into_tables_in_batches.md @aqualls
|
||||
/doc/development/insert_into_tables_in_batches.md @marcia
|
||||
/doc/development/integrations/ @kpaizee
|
||||
/doc/development/integrations/codesandbox.md @marcia
|
||||
/doc/development/integrations/jira_connect.md @kpaizee
|
||||
/doc/development/integrations/secure_partner_integration.md @rdickenson
|
||||
/doc/development/integrations/secure.md @ngaskill
|
||||
/doc/development/integrations/secure.md @claytoncornell
|
||||
/doc/development/internal_api/ @aqualls
|
||||
/doc/development/internal_users.md @marcia
|
||||
/doc/development/issuable-like-models.md @msedlakjakubowski
|
||||
/doc/development/issue_types.md @msedlakjakubowski
|
||||
/doc/development/iterating_tables_in_batches.md @aqualls
|
||||
/doc/development/kubernetes.md @sselhorn
|
||||
/doc/development/iterating_tables_in_batches.md @marcia
|
||||
/doc/development/kubernetes.md @marcia
|
||||
/doc/development/lfs.md @aqualls
|
||||
/doc/development/licensed_feature_availability.md @sselhorn
|
||||
/doc/development/logging.md @msedlakjakubowski
|
||||
/doc/development/maintenance_mode.md @axil
|
||||
/doc/development/new_fe_guide/modules/widget_extensions.md @aqualls
|
||||
/doc/development/new_fe_guide/tips.md @marcia
|
||||
/doc/development/omnibus.md @axil
|
||||
/doc/development/ordering_table_columns.md @aqualls
|
||||
/doc/development/packages.md @ngaskill
|
||||
/doc/development/ordering_table_columns.md @marcia
|
||||
/doc/development/packages.md @claytoncornell
|
||||
/doc/development/permissions.md @eread
|
||||
/doc/development/policies.md @eread
|
||||
/doc/development/product_qualified_lead_guide/index.md @kpaizee
|
||||
/doc/development/query_performance.md @aqualls
|
||||
/doc/development/project_templates.md @fneill
|
||||
/doc/development/prometheus_metrics.md @msedlakjakubowski
|
||||
/doc/development/query_performance.md @marcia
|
||||
/doc/development/query_recorder.md @marcia
|
||||
/doc/development/real_time.md @msedlakjakubowski
|
||||
/doc/development/secure_coding_guidelines.md @marcia
|
||||
/doc/development/serializing_data.md @aqualls
|
||||
/doc/development/service_ping/ @fneill
|
||||
/doc/development/snowplow/ @fneill
|
||||
/doc/development/serializing_data.md @marcia
|
||||
/doc/development/service_ping/ @claytoncornell
|
||||
/doc/development/single_table_inheritance.md @marcia
|
||||
/doc/development/snowplow/ @claytoncornell
|
||||
/doc/development/spam_protection_and_captcha/ @eread
|
||||
/doc/development/sql.md @aqualls
|
||||
/doc/development/swapping_tables.md @aqualls
|
||||
/doc/development/sql.md @marcia
|
||||
/doc/development/swapping_tables.md @marcia
|
||||
/doc/development/testing_guide/best_practices.md @marcia
|
||||
/doc/development/testing_guide/end_to_end/best_practices.md @marcia
|
||||
/doc/development/understanding_explain_plans.md @aqualls
|
||||
/doc/development/understanding_explain_plans.md @marcia
|
||||
/doc/development/value_stream_analytics.md @fneill
|
||||
/doc/development/value_stream_analytics/value_stream_analytics_aggregated_backend.md @fneill
|
||||
/doc/development/verifying_database_capabilities.md @aqualls
|
||||
/doc/development/verifying_database_capabilities.md @marcia
|
||||
/doc/development/wikis.md @aqualls
|
||||
/doc/development/work_items.md @msedlakjakubowski
|
||||
/doc/development/work_items_widgets.md @msedlakjakubowski
|
||||
/doc/development/work_items.md @msedlakjakubowski
|
||||
/doc/development/workhorse/ @aqualls
|
||||
/doc/development/workspace/index.md @marcia
|
||||
/doc/downgrade_ee_to_ce/index.md @axil
|
||||
/doc/gitlab-basics/add-file.md @aqualls
|
||||
/doc/gitlab-basics/command-line-commands.md @aqualls
|
||||
/doc/gitlab-basics/create-branch.md @aqualls
|
||||
/doc/gitlab-basics/feature_branch_workflow.md @aqualls
|
||||
/doc/gitlab-basics/index.md @aqualls
|
||||
/doc/gitlab-basics/start-using-git.md @aqualls
|
||||
/doc/gitlab-basics/ @aqualls
|
||||
/doc/install/ @axil
|
||||
/doc/integration/ @kpaizee
|
||||
/doc/integration/elasticsearch.md @rdickenson
|
||||
/doc/integration/elasticsearch.md @marcia
|
||||
/doc/integration/gitpod.md @aqualls
|
||||
/doc/integration/kerberos.md @eread
|
||||
/doc/integration/mattermost/index.md @axil
|
||||
|
@ -615,45 +614,40 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
|
|||
/doc/integration/saml.md @eread
|
||||
/doc/integration/security_partners/index.md @rdickenson
|
||||
/doc/integration/sourcegraph.md @aqualls
|
||||
/doc/integration/vault.md @sselhorn
|
||||
/doc/operations/ @ngaskill
|
||||
/doc/integration/vault.md @marcia
|
||||
/doc/operations/ @msedlakjakubowski
|
||||
/doc/operations/feature_flags.md @rdickenson
|
||||
/doc/operations/product_analytics.md @fneill
|
||||
/doc/operations/product_analytics.md @claytoncornell
|
||||
/doc/policy/ @axil
|
||||
/doc/public_access/public_access.md @fneill
|
||||
/doc/raketasks/ @axil
|
||||
/doc/raketasks/generate_sample_prometheus_data.md @ngaskill
|
||||
/doc/raketasks/generate_sample_prometheus_data.md @msedlakjakubowski
|
||||
/doc/raketasks/migrate_snippets.md @aqualls
|
||||
/doc/raketasks/spdx.md @rdickenson
|
||||
/doc/raketasks/user_management.md @axil
|
||||
/doc/raketasks/web_hooks.md @axil
|
||||
/doc/raketasks/x509_signatures.md @aqualls
|
||||
/doc/security/ @eread
|
||||
/doc/ssh/index.md @eread
|
||||
/doc/subscriptions/ @fneill
|
||||
/doc/system_hooks/system_hooks.md @kpaizee
|
||||
/doc/subscriptions/ @sselhorn
|
||||
/doc/topics/authentication/index.md @eread
|
||||
/doc/topics/autodevops/ @sselhorn
|
||||
/doc/topics/autodevops/ @marcia
|
||||
/doc/topics/git/ @aqualls
|
||||
/doc/topics/gitlab_flow.md @aqualls
|
||||
/doc/topics/offline/ @axil
|
||||
/doc/topics/plan_and_track.md @msedlakjakubowski
|
||||
/doc/update/ @axil
|
||||
/doc/update/mysql_to_postgresql.md @aqualls
|
||||
/doc/update/upgrading_postgresql_using_slony.md @aqualls
|
||||
/doc/update/mysql_to_postgresql.md @marcia
|
||||
/doc/update/upgrading_postgresql_using_slony.md @marcia
|
||||
/doc/user/admin_area/analytics/ @fneill
|
||||
/doc/user/admin_area/broadcast_messages.md @kpaizee
|
||||
/doc/user/admin_area/credentials_inventory.md @eread
|
||||
/doc/user/admin_area/custom_project_templates.md @ngaskill
|
||||
/doc/user/admin_area/custom_project_templates.md @eread
|
||||
/doc/user/admin_area/diff_limits.md @aqualls
|
||||
/doc/user/admin_area/geo_nodes.md @axil
|
||||
/doc/user/admin_area/labels.md @msedlakjakubowski
|
||||
/doc/user/admin_area/license.md @kpaizee
|
||||
/doc/user/admin_area/license_file.md @sselhorn
|
||||
/doc/user/admin_area/license.md @kpaizee
|
||||
/doc/user/admin_area/merge_requests_approvals.md @aqualls
|
||||
/doc/user/admin_area/moderate_users.md @eread
|
||||
/doc/user/admin_area/monitoring/background_migrations.md @aqualls
|
||||
/doc/user/admin_area/monitoring/health_check.md @ngaskill
|
||||
/doc/user/admin_area/monitoring/background_migrations.md @marcia
|
||||
/doc/user/admin_area/monitoring/health_check.md @msedlakjakubowski
|
||||
/doc/user/admin_area/reporting/spamcheck.md @axil
|
||||
/doc/user/admin_area/review_abuse_reports.md @eread
|
||||
/doc/user/admin_area/settings/account_and_limit_settings.md @aqualls
|
||||
|
@ -664,10 +658,10 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
|
|||
/doc/user/admin_area/settings/files_api_rate_limits.md @aqualls
|
||||
/doc/user/admin_area/settings/git_lfs_rate_limits.md @aqualls
|
||||
/doc/user/admin_area/settings/gitaly_timeouts.md @eread
|
||||
/doc/user/admin_area/settings/import_export_rate_limits.md @ngaskill
|
||||
/doc/user/admin_area/settings/import_export_rate_limits.md @eread
|
||||
/doc/user/admin_area/settings/index.md @aqualls
|
||||
/doc/user/admin_area/settings/instance_template_repository.md @aqualls
|
||||
/doc/user/admin_area/settings/package_registry_rate_limits.md @ngaskill
|
||||
/doc/user/admin_area/settings/package_registry_rate_limits.md @claytoncornell
|
||||
/doc/user/admin_area/settings/project_integration_management.md @kpaizee
|
||||
/doc/user/admin_area/settings/push_event_activities_limit.md @aqualls
|
||||
/doc/user/admin_area/settings/rate_limit_on_issues_creation.md @msedlakjakubowski
|
||||
|
@ -678,69 +672,71 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
|
|||
/doc/user/analytics/ @fneill
|
||||
/doc/user/analytics/ci_cd_analytics.md @rdickenson
|
||||
/doc/user/application_security/ @rdickenson
|
||||
/doc/user/application_security/cluster_image_scanning/index.md @ngaskill
|
||||
/doc/user/application_security/container_scanning/index.md @ngaskill
|
||||
/doc/user/application_security/cve_id_request.md @fneill
|
||||
/doc/user/application_security/policies/ @ngaskill
|
||||
/doc/user/application_security/security_dashboard/index.md @fneill
|
||||
/doc/user/application_security/threat_monitoring/index.md @ngaskill
|
||||
/doc/user/application_security/vulnerabilities/ @fneill
|
||||
/doc/user/application_security/vulnerability_report/index.md @fneill
|
||||
/doc/user/application_security/cluster_image_scanning/index.md @claytoncornell
|
||||
/doc/user/application_security/container_scanning/index.md @claytoncornell
|
||||
/doc/user/application_security/coverage_fuzzing/index.md @rdickenson
|
||||
/doc/user/application_security/cve_id_request.md @claytoncornell
|
||||
/doc/user/application_security/policies/ @claytoncornell
|
||||
/doc/user/application_security/security_dashboard/index.md @claytoncornell
|
||||
/doc/user/application_security/threat_monitoring/index.md @claytoncornell
|
||||
/doc/user/application_security/vulnerabilities/index.md @claytoncornell
|
||||
/doc/user/application_security/vulnerabilities/severities.md @claytoncornell
|
||||
/doc/user/application_security/vulnerability_report/index.md @claytoncornell
|
||||
/doc/user/asciidoc.md @aqualls
|
||||
/doc/user/award_emojis.md @msedlakjakubowski
|
||||
/doc/user/clusters/ @sselhorn
|
||||
/doc/user/clusters/ @marcia
|
||||
/doc/user/compliance/compliance_report/index.md @eread
|
||||
/doc/user/compliance/index.md @eread
|
||||
/doc/user/compliance/license_compliance/index.md @rdickenson
|
||||
/doc/user/crm/index.md @msedlakjakubowski
|
||||
/doc/user/discussions/index.md @aqualls
|
||||
/doc/user/feature_flags.md @marcia
|
||||
/doc/user/group/index.md @eread
|
||||
/doc/user/group/clusters/index.md @sselhorn
|
||||
/doc/user/group/clusters/index.md @marcia
|
||||
/doc/user/group/contribution_analytics/index.md @fneill
|
||||
/doc/user/group/custom_project_templates.md @ngaskill
|
||||
/doc/user/group/custom_project_templates.md @eread
|
||||
/doc/user/group/devops_adoption/index.md @fneill
|
||||
/doc/user/group/epics/epic_boards.md @msedlakjakubowski
|
||||
/doc/user/group/epics/index.md @msedlakjakubowski
|
||||
/doc/user/group/epics/linked_epics.md @msedlakjakubowski
|
||||
/doc/user/group/epics/manage_epics.md @msedlakjakubowski
|
||||
/doc/user/group/import/index.md @eread
|
||||
/doc/user/group/index.md @fneill
|
||||
/doc/user/group/import/index.md @ngaskill
|
||||
/doc/user/group/insights/index.md @fneill
|
||||
/doc/user/group/issues_analytics/index.md @msedlakjakubowski
|
||||
/doc/user/group/iterations/index.md @msedlakjakubowski
|
||||
/doc/user/group/planning_hierarchy/index.md @msedlakjakubowski
|
||||
/doc/user/group/repositories_analytics/index.md @eread
|
||||
/doc/user/group/repositories_analytics/index.md @marcel.amirault
|
||||
/doc/user/group/roadmap/index.md @msedlakjakubowski
|
||||
/doc/user/group/saml_sso/group_managed_accounts.md @eread
|
||||
/doc/user/group/saml_sso/index.md @eread
|
||||
/doc/user/group/saml_sso/scim_setup.md @eread
|
||||
/doc/user/group/settings/group_access_tokens.md @eread
|
||||
/doc/user/group/settings/import_export.md @ngaskill
|
||||
/doc/user/group/settings/import_export.md @eread
|
||||
/doc/user/group/subgroups/index.md @fneill
|
||||
/doc/user/group/value_stream_analytics/index.md @fneill
|
||||
/doc/user/infrastructure/clusters/ @sselhorn
|
||||
/doc/user/infrastructure/clusters/manage/management_project_applications/apparmor.md @ngaskill
|
||||
/doc/user/infrastructure/clusters/manage/management_project_applications/cilium.md @ngaskill
|
||||
/doc/user/infrastructure/clusters/manage/management_project_applications/elasticstack.md @ngaskill
|
||||
/doc/user/infrastructure/clusters/manage/management_project_applications/falco.md @ngaskill
|
||||
/doc/user/infrastructure/clusters/manage/management_project_applications/fluentd.md @ngaskill
|
||||
/doc/user/infrastructure/iac/ @sselhorn
|
||||
/doc/user/infrastructure/index.md @sselhorn
|
||||
/doc/user/infrastructure/clusters/ @marcia
|
||||
/doc/user/infrastructure/clusters/manage/management_project_applications/apparmor.md @claytoncornell
|
||||
/doc/user/infrastructure/clusters/manage/management_project_applications/cilium.md @claytoncornell
|
||||
/doc/user/infrastructure/clusters/manage/management_project_applications/elasticstack.md @msedlakjakubowski
|
||||
/doc/user/infrastructure/clusters/manage/management_project_applications/falco.md @claytoncornell
|
||||
/doc/user/infrastructure/clusters/manage/management_project_applications/fluentd.md @claytoncornell
|
||||
/doc/user/infrastructure/clusters/manage/management_project_applications/prometheus.md @msedlakjakubowski
|
||||
/doc/user/infrastructure/clusters/manage/management_project_applications/runner.md @marcia
|
||||
/doc/user/infrastructure/clusters/manage/management_project_applications/sentry.md @msedlakjakubowski
|
||||
/doc/user/infrastructure/iac/ @marcia
|
||||
/doc/user/infrastructure/index.md @marcia
|
||||
/doc/user/markdown.md @aqualls
|
||||
/doc/user/packages/ @ngaskill
|
||||
/doc/user/packages/infrastructure_registry/index.md @sselhorn
|
||||
/doc/user/packages/terraform_module_registry/index.md @sselhorn
|
||||
/doc/user/packages/workflows/project_registry.md @ngaskill
|
||||
/doc/user/packages/workflows/working_with_monorepos.md @ngaskill
|
||||
/doc/user/packages/ @claytoncornell
|
||||
/doc/user/packages/infrastructure_registry/index.md @marcia
|
||||
/doc/user/packages/terraform_module_registry/index.md @marcia
|
||||
/doc/user/permissions.md @eread
|
||||
/doc/user/profile/ @eread
|
||||
/doc/user/profile/notifications.md @msedlakjakubowski
|
||||
/doc/user/project/autocomplete_characters.md @aqualls
|
||||
/doc/user/project/badges.md @aqualls
|
||||
/doc/user/project/clusters/ @sselhorn
|
||||
/doc/user/project/clusters/kubernetes_pod_logs.md @ngaskill
|
||||
/doc/user/project/clusters/protect/ @ngaskill
|
||||
/doc/user/project/clusters/ @marcia
|
||||
/doc/user/project/clusters/kubernetes_pod_logs.md @msedlakjakubowski
|
||||
/doc/user/project/clusters/protect/ @claytoncornell
|
||||
/doc/user/project/code_intelligence.md @aqualls
|
||||
/doc/user/project/code_owners.md @aqualls
|
||||
/doc/user/project/deploy_boards.md @rdickenson
|
||||
|
@ -750,33 +746,31 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
|
|||
/doc/user/project/file_lock.md @aqualls
|
||||
/doc/user/project/git_attributes.md @aqualls
|
||||
/doc/user/project/highlighting.md @aqualls
|
||||
/doc/user/project/import/ @ngaskill
|
||||
/doc/user/project/import/ @eread
|
||||
/doc/user/project/import/jira.md @msedlakjakubowski
|
||||
/doc/user/project/index.md @fneill
|
||||
/doc/user/project/integrations/ @kpaizee
|
||||
/doc/user/project/integrations/prometheus_library/ @ngaskill
|
||||
/doc/user/project/integrations/prometheus.md @ngaskill
|
||||
/doc/user/project/integrations/prometheus_library/ @msedlakjakubowski
|
||||
/doc/user/project/integrations/prometheus.md @msedlakjakubowski
|
||||
/doc/user/project/issue_board.md @msedlakjakubowski
|
||||
/doc/user/project/issues/ @msedlakjakubowski
|
||||
/doc/user/project/issues/csv_import.md @ngaskill
|
||||
/doc/user/project/issues/csv_import.md @eread
|
||||
/doc/user/project/labels.md @msedlakjakubowski
|
||||
/doc/user/project/members/index.md @fneill
|
||||
/doc/user/project/members/share_project_with_groups.md @fneill
|
||||
/doc/user/project/merge_requests/ @aqualls
|
||||
/doc/user/project/merge_requests/accessibility_testing.md @eread
|
||||
/doc/user/project/merge_requests/browser_performance_testing.md @eread
|
||||
/doc/user/project/merge_requests/accessibility_testing.md @marcel.amirault
|
||||
/doc/user/project/merge_requests/browser_performance_testing.md @marcel.amirault
|
||||
/doc/user/project/merge_requests/code_quality.md @rdickenson
|
||||
/doc/user/project/merge_requests/csv_export.md @eread
|
||||
/doc/user/project/merge_requests/fail_fast_testing.md @eread
|
||||
/doc/user/project/merge_requests/load_performance_testing.md @eread
|
||||
/doc/user/project/merge_requests/fail_fast_testing.md @marcel.amirault
|
||||
/doc/user/project/merge_requests/load_performance_testing.md @marcel.amirault
|
||||
/doc/user/project/merge_requests/reviews/index.md @aqualls
|
||||
/doc/user/project/merge_requests/status_checks.md @eread
|
||||
/doc/user/project/merge_requests/test_coverage_visualization.md @eread
|
||||
/doc/user/project/merge_requests/testing_and_reports_in_merge_requests.md @eread
|
||||
/doc/user/project/merge_requests/test_coverage_visualization.md @marcel.amirault
|
||||
/doc/user/project/merge_requests/testing_and_reports_in_merge_requests.md @marcel.amirault
|
||||
/doc/user/project/milestones/ @msedlakjakubowski
|
||||
/doc/user/project/pages/ @aqualls
|
||||
/doc/user/project/protected_branches.md @aqualls
|
||||
/doc/user/project/protected_tags.md @aqualls
|
||||
/doc/user/project/push_options.md @aqualls
|
||||
/doc/user/project/quick_actions.md @msedlakjakubowski
|
||||
/doc/user/project/releases/index.md @rdickenson
|
||||
/doc/user/project/releases/release_cli.md @rdickenson
|
||||
|
@ -784,7 +778,7 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
|
|||
/doc/user/project/repository/reducing_the_repo_size_using_git.md @eread
|
||||
/doc/user/project/requirements/index.md @msedlakjakubowski
|
||||
/doc/user/project/service_desk.md @msedlakjakubowski
|
||||
/doc/user/project/settings/import_export.md @ngaskill
|
||||
/doc/user/project/settings/import_export.md @eread
|
||||
/doc/user/project/settings/index.md @fneill
|
||||
/doc/user/project/settings/project_access_tokens.md @eread
|
||||
/doc/user/project/static_site_editor/index.md @aqualls
|
||||
|
@ -795,13 +789,14 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
|
|||
/doc/user/project/working_with_projects.md @fneill
|
||||
/doc/user/public_access.md @fneill
|
||||
/doc/user/reserved_names.md @fneill
|
||||
/doc/user/search/advanced_search.md @rdickenson
|
||||
/doc/user/search/advanced_search.md @marcia
|
||||
/doc/user/search/index.md @aqualls
|
||||
/doc/user/shortcuts.md @aqualls
|
||||
/doc/user/snippets.md @aqualls
|
||||
/doc/user/ssh.md @eread
|
||||
/doc/user/tasks.md @msedlakjakubowski
|
||||
/doc/user/todos.md @msedlakjakubowski
|
||||
/doc/user/usage_quotas.md @fneill
|
||||
/doc/user/usage_quotas.md @sselhorn
|
||||
/doc/user/workspace/index.md @fneill
|
||||
|
||||
[Authentication and Authorization]
|
||||
|
|
|
@ -1 +1 @@
|
|||
9413ca591ebe30dcb133c86d0ec53f6bc2fc30bb
|
||||
c311109b15c26e1981c855bfa5c87aef02d27560
|
||||
|
|
1
Gemfile
1
Gemfile
|
@ -41,6 +41,7 @@ gem 'omniauth-azure-activedirectory-v2', '~> 1.0'
|
|||
gem 'omniauth-azure-oauth2', '~> 0.0.9' # Deprecated v1 version
|
||||
gem 'omniauth-cas3', '~> 1.1.4'
|
||||
gem 'omniauth-dingtalk-oauth2', '~> 1.0'
|
||||
gem 'omniauth-alicloud', '~> 1.0.1'
|
||||
gem 'omniauth-facebook', '~> 4.0.0'
|
||||
gem 'omniauth-github', '~> 1.4'
|
||||
gem 'omniauth-gitlab', '~> 1.0.2'
|
||||
|
|
|
@ -829,6 +829,8 @@ GEM
|
|||
omniauth (1.9.1)
|
||||
hashie (>= 3.4.6)
|
||||
rack (>= 1.6.2, < 3)
|
||||
omniauth-alicloud (1.0.1)
|
||||
omniauth-oauth2 (~> 1.7.1)
|
||||
omniauth-atlassian-oauth2 (0.2.0)
|
||||
omniauth (>= 1.1.1)
|
||||
omniauth-oauth2 (>= 1.5)
|
||||
|
@ -1570,6 +1572,7 @@ DEPENDENCIES
|
|||
ohai (~> 16.10)
|
||||
oj (~> 3.10.6)
|
||||
omniauth (~> 1.8)
|
||||
omniauth-alicloud (~> 1.0.1)
|
||||
omniauth-atlassian-oauth2 (~> 0.2.0)
|
||||
omniauth-auth0 (~> 2.0.0)
|
||||
omniauth-authentiq (~> 0.3.3)
|
||||
|
|
BIN
app/assets/images/auth_buttons/alicloud_64.png
Normal file
BIN
app/assets/images/auth_buttons/alicloud_64.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.5 KiB |
|
@ -1,32 +0,0 @@
|
|||
<script>
|
||||
import { GlLoadingIcon } from '@gitlab/ui';
|
||||
import { NodeViewWrapper } from '@tiptap/vue-2';
|
||||
|
||||
export default {
|
||||
name: 'ImageWrapper',
|
||||
components: {
|
||||
NodeViewWrapper,
|
||||
GlLoadingIcon,
|
||||
},
|
||||
props: {
|
||||
node: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<node-view-wrapper class="gl-display-inline-block">
|
||||
<span class="gl-relative">
|
||||
<img
|
||||
data-testid="image"
|
||||
class="gl-max-w-full gl-h-auto"
|
||||
:title="node.attrs.title"
|
||||
:class="{ 'gl-opacity-5': node.attrs.uploading }"
|
||||
:src="node.attrs.src"
|
||||
/>
|
||||
<gl-loading-icon v-if="node.attrs.uploading" class="gl-absolute gl-left-50p gl-top-half" />
|
||||
</span>
|
||||
</node-view-wrapper>
|
||||
</template>
|
|
@ -0,0 +1,51 @@
|
|||
<script>
|
||||
import { GlLoadingIcon } from '@gitlab/ui';
|
||||
import { NodeViewWrapper } from '@tiptap/vue-2';
|
||||
|
||||
const tagNameMap = {
|
||||
image: 'img',
|
||||
video: 'video',
|
||||
audio: 'audio',
|
||||
};
|
||||
|
||||
export default {
|
||||
name: 'MediaWrapper',
|
||||
components: {
|
||||
NodeViewWrapper,
|
||||
GlLoadingIcon,
|
||||
},
|
||||
props: {
|
||||
node: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
tagName() {
|
||||
return tagNameMap[this.node.type.name] || 'img';
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<node-view-wrapper class="gl-display-inline-block">
|
||||
<span class="gl-relative" :class="{ [`media-container ${tagName}-container`]: true }">
|
||||
<gl-loading-icon v-if="node.attrs.uploading" class="gl-absolute gl-left-50p gl-top-half" />
|
||||
<component
|
||||
:is="tagName"
|
||||
data-testid="media"
|
||||
:class="{
|
||||
'gl-max-w-full gl-h-auto': tagName !== 'audio',
|
||||
'gl-opacity-5': node.attrs.uploading,
|
||||
}"
|
||||
:title="node.attrs.title || node.attrs.alt"
|
||||
:alt="node.attrs.alt"
|
||||
:src="node.attrs.src"
|
||||
controls="true"
|
||||
/>
|
||||
<a v-if="tagName !== 'img'" :href="node.attrs.canonicalSrc || node.attrs.src" @click.prevent>
|
||||
{{ node.attrs.title || node.attrs.alt }}
|
||||
</a>
|
||||
</span>
|
||||
</node-view-wrapper>
|
||||
</template>
|
|
@ -1,6 +1,6 @@
|
|||
import { Image } from '@tiptap/extension-image';
|
||||
import { VueNodeViewRenderer } from '@tiptap/vue-2';
|
||||
import ImageWrapper from '../components/wrappers/image.vue';
|
||||
import MediaWrapper from '../components/wrappers/media.vue';
|
||||
import { PARSE_HTML_PRIORITY_HIGHEST } from '../constants';
|
||||
|
||||
const resolveImageEl = (element) =>
|
||||
|
@ -78,6 +78,6 @@ export default Image.extend({
|
|||
];
|
||||
},
|
||||
addNodeView() {
|
||||
return VueNodeViewRenderer(ImageWrapper);
|
||||
return VueNodeViewRenderer(MediaWrapper);
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
/* eslint-disable @gitlab/require-i18n-strings */
|
||||
|
||||
import { Node } from '@tiptap/core';
|
||||
import { VueNodeViewRenderer } from '@tiptap/vue-2';
|
||||
import MediaWrapper from '../components/wrappers/media.vue';
|
||||
|
||||
const queryPlayableElement = (element, mediaType) => element.querySelector(mediaType);
|
||||
|
||||
|
@ -11,6 +13,9 @@ export default Node.create({
|
|||
|
||||
addAttributes() {
|
||||
return {
|
||||
uploading: {
|
||||
default: false,
|
||||
},
|
||||
src: {
|
||||
default: null,
|
||||
parseHTML: (element) => {
|
||||
|
@ -60,7 +65,11 @@ export default Node.create({
|
|||
...this.extraElementAttrs,
|
||||
},
|
||||
],
|
||||
['a', { href: node.attrs.src }, node.attrs.alt],
|
||||
['a', { href: node.attrs.src }, node.attrs.title || node.attrs.alt || ''],
|
||||
];
|
||||
},
|
||||
|
||||
addNodeView() {
|
||||
return VueNodeViewRenderer(MediaWrapper);
|
||||
},
|
||||
});
|
||||
|
|
|
@ -5,6 +5,16 @@ import { extractFilename, readFileAsDataURL } from './utils';
|
|||
|
||||
export const acceptedMimes = {
|
||||
image: ['image/jpeg', 'image/png', 'image/gif', 'image/jpg'],
|
||||
audio: [
|
||||
'audio/basic',
|
||||
'audio/mid',
|
||||
'audio/mpeg',
|
||||
'audio/x-aiff',
|
||||
'audio/ogg',
|
||||
'audio/vorbis',
|
||||
'audio/vnd.wav',
|
||||
],
|
||||
video: ['video/mp4', 'video/quicktime'],
|
||||
};
|
||||
|
||||
const extractAttachmentLinkUrl = (html) => {
|
||||
|
@ -50,11 +60,11 @@ export const uploadFile = async ({ uploadsPath, renderMarkdown, file }) => {
|
|||
return extractAttachmentLinkUrl(rendered);
|
||||
};
|
||||
|
||||
const uploadImage = async ({ editor, file, uploadsPath, renderMarkdown, eventHub }) => {
|
||||
const uploadContent = async ({ type, editor, file, uploadsPath, renderMarkdown, eventHub }) => {
|
||||
const encodedSrc = await readFileAsDataURL(file);
|
||||
const { view } = editor;
|
||||
|
||||
editor.commands.setImage({ uploading: true, src: encodedSrc });
|
||||
editor.commands.insertContent({ type, attrs: { uploading: true, src: encodedSrc } });
|
||||
|
||||
const { state } = view;
|
||||
const position = state.selection.from - 1;
|
||||
|
@ -74,7 +84,7 @@ const uploadImage = async ({ editor, file, uploadsPath, renderMarkdown, eventHub
|
|||
} catch (e) {
|
||||
editor.commands.deleteRange({ from: position, to: position + 1 });
|
||||
eventHub.$emit('alert', {
|
||||
message: __('An error occurred while uploading the image. Please try again.'),
|
||||
message: __('An error occurred while uploading the file. Please try again.'),
|
||||
variant: VARIANT_DANGER,
|
||||
});
|
||||
}
|
||||
|
@ -114,10 +124,12 @@ const uploadAttachment = async ({ editor, file, uploadsPath, renderMarkdown, eve
|
|||
export const handleFileEvent = ({ editor, file, uploadsPath, renderMarkdown, eventHub }) => {
|
||||
if (!file) return false;
|
||||
|
||||
if (acceptedMimes.image.includes(file?.type)) {
|
||||
uploadImage({ editor, file, uploadsPath, renderMarkdown, eventHub });
|
||||
for (const [type, mimes] of Object.entries(acceptedMimes)) {
|
||||
if (mimes.includes(file?.type)) {
|
||||
uploadContent({ type, editor, file, uploadsPath, renderMarkdown, eventHub });
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
uploadAttachment({ editor, file, uploadsPath, renderMarkdown, eventHub });
|
||||
|
|
|
@ -308,8 +308,7 @@ module WikiActions
|
|||
end
|
||||
|
||||
def load_content?
|
||||
return false if %w[history destroy diff].include?(params[:action])
|
||||
return false if params[:action] == 'show' && Feature.enabled?(:wiki_async_load, container, default_enabled: :yaml)
|
||||
return false if %w[history destroy diff show].include?(params[:action])
|
||||
|
||||
true
|
||||
end
|
||||
|
|
|
@ -169,15 +169,17 @@ class SearchController < ApplicationController
|
|||
|
||||
search_allowed = case params[:scope]
|
||||
when 'blobs'
|
||||
Feature.enabled?(:global_search_code_tab, current_user, type: :ops, default_enabled: true)
|
||||
Feature.enabled?(:global_search_code_tab, current_user, type: :ops, default_enabled: :yaml)
|
||||
when 'commits'
|
||||
Feature.enabled?(:global_search_commits_tab, current_user, type: :ops, default_enabled: true)
|
||||
Feature.enabled?(:global_search_commits_tab, current_user, type: :ops, default_enabled: :yaml)
|
||||
when 'issues'
|
||||
Feature.enabled?(:global_search_issues_tab, current_user, type: :ops, default_enabled: true)
|
||||
Feature.enabled?(:global_search_issues_tab, current_user, type: :ops, default_enabled: :yaml)
|
||||
when 'merge_requests'
|
||||
Feature.enabled?(:global_search_merge_requests_tab, current_user, type: :ops, default_enabled: true)
|
||||
Feature.enabled?(:global_search_merge_requests_tab, current_user, type: :ops, default_enabled: :yaml)
|
||||
when 'wiki_blobs'
|
||||
Feature.enabled?(:global_search_wiki_tab, current_user, type: :ops, default_enabled: true)
|
||||
Feature.enabled?(:global_search_wiki_tab, current_user, type: :ops, default_enabled: :yaml)
|
||||
when 'users'
|
||||
Feature.enabled?(:global_search_users_tab, current_user, type: :ops, default_enabled: :yaml)
|
||||
else
|
||||
true
|
||||
end
|
||||
|
|
|
@ -14,7 +14,8 @@ class UploadsController < ApplicationController
|
|||
"appearance" => Appearance,
|
||||
"personal_snippet" => PersonalSnippet,
|
||||
"projects/topic" => Projects::Topic,
|
||||
nil => PersonalSnippet
|
||||
'alert_management_metric_image' => ::AlertManagement::MetricImage,
|
||||
nil => PersonalSnippet
|
||||
}.freeze
|
||||
|
||||
rescue_from UnknownUploadModelError, with: :render_404
|
||||
|
@ -56,6 +57,8 @@ class UploadsController < ApplicationController
|
|||
true
|
||||
when Projects::Topic
|
||||
true
|
||||
when ::AlertManagement::MetricImage
|
||||
can?(current_user, :read_alert_management_metric_image, model.alert)
|
||||
else
|
||||
can?(current_user, "read_#{model.class.underscore}".to_sym, model)
|
||||
end
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
module AuthHelper
|
||||
PROVIDERS_WITH_ICONS = %w(
|
||||
alicloud
|
||||
atlassian_oauth2
|
||||
auth0
|
||||
authentiq
|
||||
|
|
|
@ -436,11 +436,11 @@ module SearchHelper
|
|||
end
|
||||
|
||||
def show_user_search_tab?
|
||||
if @project
|
||||
project_search_tabs?(:members)
|
||||
else
|
||||
can?(current_user, :read_users_list)
|
||||
end
|
||||
return project_search_tabs?(:members) if @project
|
||||
return false unless can?(current_user, :read_users_list)
|
||||
return true if @group
|
||||
|
||||
Feature.enabled?(:global_search_users_tab, current_user, type: :ops, default_enabled: :yaml)
|
||||
end
|
||||
|
||||
def issuable_state_to_badge_class(issuable)
|
||||
|
|
|
@ -27,6 +27,7 @@ module AlertManagement
|
|||
has_many :notes, as: :noteable, inverse_of: :noteable, dependent: :delete_all # rubocop:disable Cop/ActiveRecordDependent
|
||||
has_many :ordered_notes, -> { fresh }, as: :noteable, class_name: 'Note'
|
||||
has_many :user_mentions, class_name: 'AlertManagement::AlertUserMention', foreign_key: :alert_management_alert_id
|
||||
has_many :metric_images, class_name: '::AlertManagement::MetricImage'
|
||||
|
||||
has_internal_id :iid, scope: :project
|
||||
|
||||
|
@ -142,6 +143,10 @@ module AlertManagement
|
|||
reference.to_i > 0 && reference.to_i <= Gitlab::Database::MAX_INT_VALUE
|
||||
end
|
||||
|
||||
def metric_images_available?
|
||||
::AlertManagement::MetricImage.available_for?(project)
|
||||
end
|
||||
|
||||
def prometheus?
|
||||
monitoring_tool == Gitlab::AlertManagement::Payload::MONITORING_TOOLS[:prometheus]
|
||||
end
|
||||
|
|
25
app/models/alert_management/metric_image.rb
Normal file
25
app/models/alert_management/metric_image.rb
Normal file
|
@ -0,0 +1,25 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module AlertManagement
|
||||
class MetricImage < ApplicationRecord
|
||||
include MetricImageUploading
|
||||
self.table_name = 'alert_management_alert_metric_images'
|
||||
|
||||
belongs_to :alert, class_name: 'AlertManagement::Alert', foreign_key: 'alert_id', inverse_of: :metric_images
|
||||
|
||||
def self.available_for?(project)
|
||||
true
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def local_path
|
||||
Gitlab::Routing.url_helpers.alert_metric_image_upload_path(
|
||||
filename: file.filename,
|
||||
id: file.upload.model_id,
|
||||
model: model_name.param_key,
|
||||
mounted_as: 'file'
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -621,6 +621,8 @@ class ApplicationSetting < ApplicationRecord
|
|||
attr_encrypted :external_pipeline_validation_service_token, encryption_options_base_32_aes_256_gcm
|
||||
attr_encrypted :mailgun_signing_key, encryption_options_base_32_aes_256_gcm.merge(encode: false)
|
||||
attr_encrypted :database_grafana_api_key, encryption_options_base_32_aes_256_gcm.merge(encode: false, encode_iv: false)
|
||||
attr_encrypted :arkose_labs_public_api_key, encryption_options_base_32_aes_256_gcm.merge(encode: false, encode_iv: false)
|
||||
attr_encrypted :arkose_labs_private_api_key, encryption_options_base_32_aes_256_gcm.merge(encode: false, encode_iv: false)
|
||||
|
||||
validates :disable_feed_token,
|
||||
inclusion: { in: [true, false], message: _('must be a boolean value') }
|
||||
|
|
|
@ -504,7 +504,11 @@ module Ci
|
|||
if metadata&.expanded_environment_name.present?
|
||||
metadata.expanded_environment_name
|
||||
else
|
||||
ExpandVariables.expand(environment, -> { simple_variables })
|
||||
if ::Feature.enabled?(:ci_expand_environment_name_and_url, project, default_enabled: :yaml)
|
||||
ExpandVariables.expand(environment, -> { simple_variables.sort_and_expand_all })
|
||||
else
|
||||
ExpandVariables.expand(environment, -> { simple_variables })
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
54
app/models/concerns/metric_image_uploading.rb
Normal file
54
app/models/concerns/metric_image_uploading.rb
Normal file
|
@ -0,0 +1,54 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module MetricImageUploading
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
MAX_FILE_SIZE = 1.megabyte.freeze
|
||||
|
||||
included do
|
||||
include Gitlab::FileTypeDetection
|
||||
include FileStoreMounter
|
||||
include WithUploads
|
||||
|
||||
validates :file, presence: true
|
||||
validate :validate_file_is_image
|
||||
validates :url, length: { maximum: 255 }, public_url: { allow_blank: true }
|
||||
validates :url_text, length: { maximum: 128 }
|
||||
|
||||
scope :order_created_at_asc, -> { order(created_at: :asc) }
|
||||
|
||||
attribute :file_store, :integer, default: -> { MetricImageUploader.default_store }
|
||||
|
||||
mount_file_store_uploader MetricImageUploader
|
||||
end
|
||||
|
||||
def filename
|
||||
@filename ||= file&.filename
|
||||
end
|
||||
|
||||
def file_path
|
||||
@file_path ||= begin
|
||||
return file&.url unless file&.upload
|
||||
|
||||
# If we're using a CDN, we need to use the full URL
|
||||
asset_host = ActionController::Base.asset_host || Gitlab.config.gitlab.base_url
|
||||
|
||||
Gitlab::Utils.append_path(asset_host, local_path)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def valid_file_extensions
|
||||
Gitlab::FileTypeDetection::SAFE_IMAGE_EXT
|
||||
end
|
||||
|
||||
def validate_file_is_image
|
||||
unless image?
|
||||
message = _('does not have a supported extension. Only %{extension_list} are supported') % {
|
||||
extension_list: valid_file_extensions.to_sentence
|
||||
}
|
||||
errors.add(:file, message)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -282,7 +282,7 @@ class User < ApplicationRecord
|
|||
after_update :username_changed_hook, if: :saved_change_to_username?
|
||||
after_destroy :post_destroy_hook
|
||||
after_destroy :remove_key_cache
|
||||
after_save if: -> { saved_change_to_email? && confirmed? } do
|
||||
after_save if: -> { (saved_change_to_email? || saved_change_to_confirmed_at?) && confirmed? } do
|
||||
email_to_confirm = self.emails.find_by(email: self.email)
|
||||
|
||||
if email_to_confirm.present?
|
||||
|
|
|
@ -3,7 +3,15 @@
|
|||
module AlertManagement
|
||||
class AlertPolicy < ::BasePolicy
|
||||
delegate { @subject.project }
|
||||
|
||||
rule { can?(:read_alert_management_alert) }.policy do
|
||||
enable :read_alert_management_metric_image
|
||||
end
|
||||
|
||||
rule { can?(:update_alert_management_alert) }.policy do
|
||||
enable :upload_alert_management_metric_image
|
||||
enable :update_alert_management_metric_image
|
||||
enable :destroy_alert_management_metric_image
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
AlertManagement::AlertPolicy.prepend_mod
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module AlertManagement
|
||||
module MetricImages
|
||||
class UploadService < BaseService
|
||||
attr_reader :alert, :file, :url, :url_text, :metric
|
||||
|
||||
def initialize(alert, current_user, params = {})
|
||||
super
|
||||
|
||||
@alert = alert
|
||||
@file = params.fetch(:file)
|
||||
@url = params.fetch(:url, nil)
|
||||
@url_text = params.fetch(:url_text, nil)
|
||||
end
|
||||
|
||||
def execute
|
||||
unless can_upload_metrics?
|
||||
return ServiceResponse.error(
|
||||
message: _("You are not authorized to upload metric images"),
|
||||
http_status: :forbidden
|
||||
)
|
||||
end
|
||||
|
||||
metric = AlertManagement::MetricImage.new(
|
||||
alert: alert,
|
||||
file: file,
|
||||
url: url,
|
||||
url_text: url_text
|
||||
)
|
||||
|
||||
if metric.save
|
||||
ServiceResponse.success(payload: { metric: metric, alert: alert })
|
||||
else
|
||||
ServiceResponse.error(message: metric.errors.full_messages.join(', '), http_status: :bad_request)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def can_upload_metrics?
|
||||
alert.metric_images_available? && current_user&.can?(:upload_alert_management_metric_image, alert)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -56,7 +56,13 @@ module Deployments
|
|||
end
|
||||
|
||||
def expanded_environment_url
|
||||
ExpandVariables.expand(environment_url, -> { variables }) if environment_url
|
||||
return unless environment_url
|
||||
|
||||
if ::Feature.enabled?(:ci_expand_environment_name_and_url, deployment.project, default_enabled: :yaml)
|
||||
ExpandVariables.expand(environment_url, -> { variables.sort_and_expand_all })
|
||||
else
|
||||
ExpandVariables.expand(environment_url, -> { variables })
|
||||
end
|
||||
end
|
||||
|
||||
def environment_url
|
||||
|
|
|
@ -105,7 +105,8 @@ module Projects
|
|||
end
|
||||
|
||||
@project.track_project_repository
|
||||
@project.create_project_setting unless @project.project_setting
|
||||
|
||||
create_project_settings
|
||||
|
||||
yield if block_given?
|
||||
|
||||
|
@ -122,6 +123,14 @@ module Projects
|
|||
create_sast_commit if @initialize_with_sast
|
||||
end
|
||||
|
||||
def create_project_settings
|
||||
if Feature.enabled?(:create_project_settings, default_enabled: :yaml)
|
||||
@project.project_setting.save if @project.project_setting.changed?
|
||||
else
|
||||
@project.create_project_setting unless @project.project_setting
|
||||
end
|
||||
end
|
||||
|
||||
# Add an authorization for the current user authorizations inline
|
||||
# (so they can access the project immediately after this request
|
||||
# completes), and any other affected users in the background
|
||||
|
|
20
app/uploaders/metric_image_uploader.rb
Normal file
20
app/uploaders/metric_image_uploader.rb
Normal file
|
@ -0,0 +1,20 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class MetricImageUploader < GitlabUploader # rubocop:disable Gitlab/NamespacedClass
|
||||
include RecordsUploads::Concern
|
||||
include ObjectStorage::Concern
|
||||
prepend ObjectStorage::Extension::RecordsUploads
|
||||
include UploaderHelper
|
||||
|
||||
private
|
||||
|
||||
def dynamic_segment
|
||||
File.join(model.class.underscore, mounted_as.to_s, model.id.to_s)
|
||||
end
|
||||
|
||||
class << self
|
||||
def default_store
|
||||
object_store_enabled? ? ObjectStorage::Store::REMOTE : ObjectStorage::Store::LOCAL
|
||||
end
|
||||
end
|
||||
end
|
|
@ -3,5 +3,4 @@
|
|||
.gl-mb-3.js-email-opt-in.hidden
|
||||
.gl-font-weight-bold.gl-mb-3
|
||||
= _('Email updates (optional)')
|
||||
= f.check_box :email_opted_in
|
||||
= f.label :email_opted_in, _("I'd like to receive updates about GitLab via email"), class: 'gl-font-weight-normal'
|
||||
= f.gitlab_ui_checkbox_component :email_opted_in, _("I'd like to receive updates about GitLab via email")
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
|
||||
.settings-content
|
||||
- if mirror_settings_enabled
|
||||
= form_for @project, url: project_mirror_path(@project), html: { class: 'gl-show-field-errors js-mirror-form', autocomplete: 'new-password', data: mirrors_form_data_attributes } do |f|
|
||||
= gitlab_ui_form_for @project, url: project_mirror_path(@project), html: { class: 'gl-show-field-errors js-mirror-form', autocomplete: 'new-password', data: mirrors_form_data_attributes } do |f|
|
||||
.panel.panel-default
|
||||
.panel-body
|
||||
%div= form_errors(@project)
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
%p.gl-text-center= html_escape(_('%{gitlab_experience_text}. We won\'t share this information with anyone.')) % { gitlab_experience_text: gitlab_experience_text }
|
||||
- else
|
||||
%p.gl-text-center= html_escape(_('%{gitlab_experience_text}. Don\'t worry, this information isn\'t shared outside of your self-managed GitLab instance.')) % { gitlab_experience_text: gitlab_experience_text }
|
||||
= form_for(current_user, url: users_sign_up_welcome_path, html: { class: 'card gl-w-full! gl-p-5', 'aria-live' => 'assertive' }) do |f|
|
||||
= gitlab_ui_form_for(current_user, url: users_sign_up_welcome_path, html: { class: 'card gl-w-full! gl-p-5', 'aria-live' => 'assertive' }) do |f|
|
||||
.devise-errors
|
||||
= render 'devise/shared/error_messages', resource: current_user
|
||||
.row
|
||||
|
|
|
@ -27,9 +27,6 @@
|
|||
- if can?(current_user, :create_wiki, @wiki.container) && @page.latest? && @valid_encoding
|
||||
= link_to sprite_icon('pencil', css_class: 'gl-icon'), wiki_page_path(@wiki, @page, action: :edit), title: 'Edit', role: "button", class: 'btn gl-button btn-icon btn-default js-wiki-edit', data: { qa_selector: 'edit_page_button', testid: 'wiki_edit_button' }
|
||||
|
||||
- if Feature.enabled?(:wiki_async_load, @wiki.container, default_enabled: :yaml)
|
||||
.js-async-wiki-page-content.md.gl-pt-2{ data: { qa_selector: 'wiki_page_content', testid: 'wiki_page_content', tracking_context: wiki_page_tracking_context(@page).to_json, get_wiki_content_url: wiki_page_render_api_endpoint(@page) } }
|
||||
- else
|
||||
= render 'shared/wikis/wiki_content'
|
||||
.js-async-wiki-page-content.md.gl-pt-2{ data: { qa_selector: 'wiki_page_content', testid: 'wiki_page_content', tracking_context: wiki_page_tracking_context(@page).to_json, get_wiki_content_url: wiki_page_render_api_endpoint(@page) } }
|
||||
|
||||
= render 'shared/wikis/sidebar'
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
name: ci_expand_environment_name_and_url
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/84969
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/358831
|
||||
milestone: '14.10'
|
||||
type: development
|
||||
group: group::pipeline authoring
|
||||
default_enabled: false
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
name: create_project_settings
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/84502
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/358136
|
||||
milestone: '14.10'
|
||||
type: development
|
||||
group: group::authentication and authorization
|
||||
default_enabled: false
|
|
@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/323743
|
|||
milestone: '13.10'
|
||||
type: development
|
||||
group: group::project management
|
||||
default_enabled: false
|
||||
default_enabled: true
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
---
|
||||
name: wiki_async_load
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/82394
|
||||
rollout_issue_url:
|
||||
milestone: '14.9'
|
||||
type: development
|
||||
group: group::editor
|
||||
default_enabled: false
|
8
config/feature_flags/ops/global_search_users_tab.yml
Normal file
8
config/feature_flags/ops/global_search_users_tab.yml
Normal file
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
name: global_search_users_tab
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/84186
|
||||
rollout_issue_url:
|
||||
milestone: '14.10'
|
||||
type: ops
|
||||
group: group::global search
|
||||
default_enabled: true
|
|
@ -1035,6 +1035,9 @@ production: &base
|
|||
# arguments, followed by optional 'args' which can be either a hash or an array.
|
||||
# Documentation for this is available at http://doc.gitlab.com/ce/integration/omniauth.html
|
||||
providers:
|
||||
# - { name: 'alicloud',
|
||||
# app_id: 'YOUR_APP_ID',
|
||||
# app_secret: 'YOUR_APP_SECRET' }
|
||||
# See omniauth-cas3 for more configuration details
|
||||
# - { name: 'cas3',
|
||||
# label: 'cas3',
|
||||
|
@ -1562,6 +1565,9 @@ test:
|
|||
external_providers: []
|
||||
|
||||
providers:
|
||||
- { name: 'alicloud',
|
||||
app_id: 'YOUR_APP_ID',
|
||||
app_secret: 'YOUR_APP_SECRET' }
|
||||
- { name: 'cas3',
|
||||
label: 'cas3',
|
||||
args: { url: 'https://sso.example.com',
|
||||
|
|
|
@ -1001,14 +1001,6 @@ Settings['prometheus'] ||= Settingslogic.new({})
|
|||
Settings.prometheus['enabled'] ||= false
|
||||
Settings.prometheus['server_address'] ||= nil
|
||||
|
||||
#
|
||||
# Arkose settings
|
||||
#
|
||||
Settings['arkose'] ||= Settingslogic.new({})
|
||||
Settings.arkose['public_key'] ||= ENV['ARKOSE_LABS_PUBLIC_KEY']
|
||||
Settings.arkose['private_key'] ||= ENV['ARKOSE_LABS_PRIVATE_KEY']
|
||||
Settings.arkose['verify_url'] ||= ENV['ARKOSE_LABS_VERIFY_URL']
|
||||
|
||||
#
|
||||
# Shutdown settings
|
||||
#
|
||||
|
|
|
@ -38,6 +38,12 @@ scope path: :uploads do
|
|||
post ':model/authorize',
|
||||
to: 'uploads#authorize',
|
||||
constraints: { model: /personal_snippet|user/ }
|
||||
|
||||
# Alert Metric Images
|
||||
get "-/system/:model/:mounted_as/:id/:filename",
|
||||
to: "uploads#show",
|
||||
constraints: { model: /alert_management_metric_image/, mounted_as: /file/, filename: %r{[^/]+} },
|
||||
as: 'alert_metric_image_upload'
|
||||
end
|
||||
|
||||
# Redirect old note attachments path to new uploads path.
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddArkoseSettingsToApplicationSettings < Gitlab::Database::Migration[1.0]
|
||||
# rubocop:disable Migration/AddLimitToTextColumns
|
||||
# limit is added in 20220405203843_add_text_limit_to_arkose_verify_url_application_settings.rb
|
||||
def up
|
||||
add_column :application_settings, :encrypted_arkose_labs_public_api_key, :binary
|
||||
add_column :application_settings, :encrypted_arkose_labs_public_api_key_iv, :binary
|
||||
|
||||
add_column :application_settings, :encrypted_arkose_labs_private_api_key, :binary
|
||||
add_column :application_settings, :encrypted_arkose_labs_private_api_key_iv, :binary
|
||||
|
||||
add_column :application_settings, :arkose_labs_verify_api_url, :text
|
||||
end
|
||||
# rubocop:enable Migration/AddLimitToTextColumns
|
||||
|
||||
def down
|
||||
remove_column :application_settings, :encrypted_arkose_labs_public_api_key
|
||||
remove_column :application_settings, :encrypted_arkose_labs_public_api_key_iv
|
||||
|
||||
remove_column :application_settings, :encrypted_arkose_labs_private_api_key
|
||||
remove_column :application_settings, :encrypted_arkose_labs_private_api_key_iv
|
||||
|
||||
remove_column :application_settings, :arkose_labs_verify_api_url
|
||||
end
|
||||
end
|
|
@ -0,0 +1,13 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddTextLimitToArkoseVerifyUrlApplicationSettings < Gitlab::Database::Migration[1.0]
|
||||
disable_ddl_transaction!
|
||||
|
||||
def up
|
||||
add_text_limit :application_settings, :arkose_labs_verify_api_url, 255
|
||||
end
|
||||
|
||||
def down
|
||||
remove_text_limit :application_settings, :arkose_labs_verify_api_url
|
||||
end
|
||||
end
|
1
db/schema_migrations/20220405181814
Normal file
1
db/schema_migrations/20220405181814
Normal file
|
@ -0,0 +1 @@
|
|||
0835eaaf3e355f98783a11098a37b894b581176d98c39cdfd3be44e2447fe232
|
1
db/schema_migrations/20220405203843
Normal file
1
db/schema_migrations/20220405203843
Normal file
|
@ -0,0 +1 @@
|
|||
ac1892c5f2131e41774cadc8799cb5fb2c7d36fe567850fc1251a23c2d454695
|
|
@ -11265,6 +11265,11 @@ CREATE TABLE application_settings (
|
|||
database_grafana_api_url text,
|
||||
database_grafana_tag text,
|
||||
public_runner_releases_url text DEFAULT 'https://gitlab.com/api/v4/projects/gitlab-org%2Fgitlab-runner/releases'::text NOT NULL,
|
||||
encrypted_arkose_labs_public_api_key bytea,
|
||||
encrypted_arkose_labs_public_api_key_iv bytea,
|
||||
encrypted_arkose_labs_private_api_key bytea,
|
||||
encrypted_arkose_labs_private_api_key_iv bytea,
|
||||
arkose_labs_verify_api_url text,
|
||||
delete_inactive_projects boolean DEFAULT false NOT NULL,
|
||||
inactive_projects_delete_after_months integer DEFAULT 2 NOT NULL,
|
||||
inactive_projects_min_size_mb integer DEFAULT 0 NOT NULL,
|
||||
|
@ -11299,7 +11304,8 @@ CREATE TABLE application_settings (
|
|||
CONSTRAINT check_d820146492 CHECK ((char_length(spam_check_endpoint_url) <= 255)),
|
||||
CONSTRAINT check_e5024c8801 CHECK ((char_length(elasticsearch_username) <= 255)),
|
||||
CONSTRAINT check_e5aba18f02 CHECK ((char_length(container_registry_version) <= 255)),
|
||||
CONSTRAINT check_ef6176834f CHECK ((char_length(encrypted_cloud_license_auth_token_iv) <= 255))
|
||||
CONSTRAINT check_ef6176834f CHECK ((char_length(encrypted_cloud_license_auth_token_iv) <= 255)),
|
||||
CONSTRAINT check_f6563bc000 CHECK ((char_length(arkose_labs_verify_api_url) <= 255))
|
||||
);
|
||||
|
||||
COMMENT ON COLUMN application_settings.content_validation_endpoint_url IS 'JiHu-specific column';
|
||||
|
|
|
@ -10023,6 +10023,7 @@ Input type for DastSiteProfile authentication.
|
|||
| <a id="dastsiteprofileauthenabled"></a>`enabled` | [`Boolean`](#boolean) | Indicates whether authentication is enabled. |
|
||||
| <a id="dastsiteprofileauthpassword"></a>`password` | [`String`](#string) | Redacted password to authenticate with on the target website. |
|
||||
| <a id="dastsiteprofileauthpasswordfield"></a>`passwordField` | [`String`](#string) | Name of password field at the sign-in HTML form. |
|
||||
| <a id="dastsiteprofileauthsubmitfield"></a>`submitField` | [`String`](#string) | Name or ID of sign-in submit button at the sign-in HTML form. |
|
||||
| <a id="dastsiteprofileauthurl"></a>`url` | [`String`](#string) | The URL of the page containing the sign-in HTML form on the target website. |
|
||||
| <a id="dastsiteprofileauthusername"></a>`username` | [`String`](#string) | Username to authenticate with on the target website. |
|
||||
| <a id="dastsiteprofileauthusernamefield"></a>`usernameField` | [`String`](#string) | Name of username field at the sign-in HTML form. |
|
||||
|
@ -20608,6 +20609,7 @@ Input type for DastSiteProfile authentication.
|
|||
| <a id="dastsiteprofileauthinputenabled"></a>`enabled` | [`Boolean`](#boolean) | Indicates whether authentication is enabled. |
|
||||
| <a id="dastsiteprofileauthinputpassword"></a>`password` | [`String`](#string) | Password to authenticate with on the target website. |
|
||||
| <a id="dastsiteprofileauthinputpasswordfield"></a>`passwordField` | [`String`](#string) | Name of password field at the sign-in HTML form. |
|
||||
| <a id="dastsiteprofileauthinputsubmitfield"></a>`submitField` | [`String`](#string) | Name or ID of sign-in submit button at the sign-in HTML form. |
|
||||
| <a id="dastsiteprofileauthinputurl"></a>`url` | [`String`](#string) | The URL of the page containing the sign-in HTML form on the target website. |
|
||||
| <a id="dastsiteprofileauthinputusername"></a>`username` | [`String`](#string) | Username to authenticate with on the target website. |
|
||||
| <a id="dastsiteprofileauthinputusernamefield"></a>`usernameField` | [`String`](#string) | Name of username field at the sign-in HTML form. |
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 6.5 KiB |
|
@ -21,6 +21,56 @@ GitLab supports the following security policies:
|
|||
- [Scan Result Policy](scan-result-policies.md)
|
||||
- [Container Network Policy](#container-network-policy) (DEPRECATED)
|
||||
|
||||
## Security policy project
|
||||
|
||||
All security policies are stored as YAML in a separate security policy project that gets linked to
|
||||
the development project. This association can be a one-to-many relationship, allowing one security
|
||||
policy project to apply to multiple development projects. Linked projects are not required to be in
|
||||
the same group as the development projects to which they are linked.
|
||||
|
||||
![Security Policy Project Linking Diagram](img/association_diagram.png)
|
||||
|
||||
Although it is possible to have one project linked to itself and to serve as both the development
|
||||
project and the security policy project, this is not recommended. Keeping the security policy
|
||||
project separate from the development project allows for complete separation of duties between
|
||||
security/compliance teams and development teams.
|
||||
|
||||
All security policies are stored in the `.gitlab/security-policies/policy.yml` YAML file inside the
|
||||
linked security policy project. The format for this YAML is specific to the type of policy that is
|
||||
stored there. Examples and schema information are available for the following policy types:
|
||||
|
||||
- [Scan execution policy](scan-execution-policies.md#example-security-policies-project)
|
||||
- [Scan result policy](scan-result-policies.md#example-security-scan-result-policies-project)
|
||||
|
||||
Policies created in this project are applied through a background job that runs once every 10
|
||||
minutes. Allow up to 10 minutes for any policy changes committed to this project to take effect.
|
||||
|
||||
### Managing the linked security policy project
|
||||
|
||||
NOTE:
|
||||
Only project Owners have the [permissions](../../permissions.md#project-members-permissions)
|
||||
to select, edit, and unlink a security policy project.
|
||||
|
||||
As a project owner, take the following steps to create or edit an association between your current
|
||||
project and a project that you would like to designate as the security policy project:
|
||||
|
||||
1. On the top bar, select **Menu > Projects** and find your project.
|
||||
1. On the left sidebar, select **Security & Compliance > Policies**.
|
||||
1. Select **Edit Policy Project**, and search for and select the
|
||||
project you would like to link from the dropdown menu.
|
||||
1. Select **Save**.
|
||||
|
||||
To unlink a security policy project, follow the same steps but instead select the trash can icon in
|
||||
the modal.
|
||||
|
||||
![Security Policy Project](img/security_policy_project_v14_6.png)
|
||||
|
||||
### Viewing the linked security policy project
|
||||
|
||||
All users who have access to the project policy page and are not project owners will instead view a
|
||||
button linking out to the associated security policy project. If no security policy project has been
|
||||
associated then the linking button does not appear.
|
||||
|
||||
## Policy management
|
||||
|
||||
The Policies page displays deployed
|
||||
|
@ -57,6 +107,7 @@ You can use the policy editor to create, edit, and delete policies:
|
|||
1. On the top bar, select **Menu > Projects** and find your group.
|
||||
1. On the left sidebar, select **Security & Compliance > Policies**.
|
||||
- To create a new policy, select **New policy** which is located in the **Policies** page's header.
|
||||
You can then select which type of policy to create.
|
||||
- To edit an existing policy, select **Edit policy** in the selected policy drawer.
|
||||
|
||||
The policy editor has two modes:
|
||||
|
@ -78,44 +129,12 @@ by the Rule mode, Rule mode is automatically
|
|||
disabled. If the YAML is incorrect, you must use YAML
|
||||
mode to fix your policy before Rule mode is available again.
|
||||
|
||||
## Security Policies project
|
||||
|
||||
NOTE:
|
||||
We recommend using the [Security Policies project](#security-policies-project)
|
||||
exclusively for managing policies for the project. Do not add your application's source code to such
|
||||
projects.
|
||||
|
||||
The Security Policies feature is a repository to store policies. All security policies are stored in
|
||||
the `.gitlab/security-policies/policy.yml` YAML file. The format for this YAML is specific to the type of policy that is being stored there. Examples and schema information are available for the following policy types:
|
||||
|
||||
- [Scan execution policy](scan-execution-policies.md#example-security-policies-project)
|
||||
- [Scan result policy](scan-result-policies.md#example-security-scan-result-policies-project)
|
||||
|
||||
Policies created in this project are applied through a background job that runs once every 10
|
||||
minutes. Allow up to 10 minutes for any policy changes committed to this project to take effect.
|
||||
|
||||
## Security Policy project selection
|
||||
|
||||
NOTE:
|
||||
Only project Owners have the [permissions](../../permissions.md#project-members-permissions)
|
||||
to select Security Policy Project.
|
||||
|
||||
When the Security Policy project is created and policies are created within that repository, you
|
||||
must create an association between that project and the project you want to apply policies to:
|
||||
|
||||
1. On the top bar, select **Menu > Projects** and find your project.
|
||||
1. On the left sidebar, select **Security & Compliance > Policies**.
|
||||
1. Select **Edit Policy Project**, and search for and select the
|
||||
project you would like to link from the dropdown menu.
|
||||
1. Select **Save**.
|
||||
|
||||
![Security Policy Project](img/security_policy_project_v14_6.png)
|
||||
|
||||
### Unlink Security Policy projects
|
||||
|
||||
Project owners can unlink Security Policy projects from development projects. To do this, follow
|
||||
the steps described in [Security Policy project selection](#security-policy-project-selection),
|
||||
but select the trash can icon in the modal.
|
||||
When you finish creating or editing your policy, save and apply it by selecting the
|
||||
**Configure with a merge request** button and then merging the resulting merge request. When you
|
||||
press this button, the policy YAML is validated and any resulting errors are displayed.
|
||||
Additionally, if you are a project owner and a security policy project has not been previously
|
||||
associated with this project, then a new project is created and associated automatically at the same
|
||||
time that the first policy merge request is created.
|
||||
|
||||
## Scan execution policies
|
||||
|
||||
|
|
|
@ -132,8 +132,8 @@ Note the following:
|
|||
|
||||
## Example security policies project
|
||||
|
||||
You can use this example in a `.gitlab/security-policies/policy.yml`, as described in
|
||||
[Security policies project](index.md#security-policies-project).
|
||||
You can use this example in a `.gitlab/security-policies/policy.yml` file stored in a
|
||||
[security policy project](index.md#security-policy-project):
|
||||
|
||||
```yaml
|
||||
---
|
||||
|
|
|
@ -90,8 +90,8 @@ Requirements and limitations:
|
|||
|
||||
## Example security scan result policies project
|
||||
|
||||
You can use this example in a `.gitlab/security-policies/policy.yml`, as described in
|
||||
[Security policies project](index.md#security-policies-project):
|
||||
You can use this example in a `.gitlab/security-policies/policy.yml` file stored in a
|
||||
[security policy project](index.md#security-policy-project):
|
||||
|
||||
```yaml
|
||||
---
|
||||
|
|
|
@ -26,8 +26,8 @@ when searching in:
|
|||
- Comments
|
||||
- Code
|
||||
- Commits
|
||||
- Wiki (except [group wikis](../project/wiki/group.md))
|
||||
- Users
|
||||
- Wiki (except [group wikis](../project/wiki/group.md))
|
||||
|
||||
The Advanced Search can be useful in various scenarios:
|
||||
|
||||
|
@ -76,6 +76,7 @@ its performance:
|
|||
| Commits | `global_search_commits_tab` | When enabled, the global search includes commits as part of the search. |
|
||||
| Issues | `global_search_issues_tab` | When enabled, the global search includes issues as part of the search. |
|
||||
| Merge Requests | `global_search_merge_requests_tab` | When enabled, the global search includes merge requests as part of the search. |
|
||||
| Users | `global_search_users_tab` | When enabled, the global search includes users as part of the search. |
|
||||
| Wiki | `global_search_wiki_tab` | When enabled, the global search includes wiki as part of the search. [Group wikis](../project/wiki/group.md) are not included. |
|
||||
|
||||
## Global Search validation
|
||||
|
|
|
@ -41,6 +41,7 @@ in the search field in the upper right corner:
|
|||
> - Filtering by iterations was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/118742) in GitLab 13.6.
|
||||
> - Filtering by iterations was moved from GitLab Ultimate to GitLab Premium in 13.9.
|
||||
> - Filtering by type was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/322755) in GitLab 13.10 [with a flag](../../administration/feature_flags.md) named `vue_issues_list`. Disabled by default.
|
||||
> - Filtering by type was [enabled on self-managed](https://gitlab.com/gitlab-org/gitlab/-/issues/322755) in GitLab 14.10.
|
||||
> - Filtering by attention request was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/343528) in GitLab 14.10 [with a flag](../../administration/feature_flags.md) named `mr_attention_requests`. Disabled by default.
|
||||
|
||||
Follow these steps to filter the **Issues** and **Merge requests** list pages in projects and
|
||||
|
@ -59,12 +60,6 @@ groups:
|
|||
- My-reaction
|
||||
- Release
|
||||
- Type
|
||||
|
||||
FLAG:
|
||||
On self-managed GitLab, by default filtering by type is not available.
|
||||
To make it available per group, ask an administrator to [enable the feature flag](../../administration/feature_flags.md) named `vue_issues_list`.
|
||||
On GitLab.com, this feature is not available.
|
||||
|
||||
- Weight
|
||||
- Search for this text
|
||||
1. Select or type the operator to use for filtering the attribute. The following operators are
|
||||
|
|
137
lib/api/alert_management_alerts.rb
Normal file
137
lib/api/alert_management_alerts.rb
Normal file
|
@ -0,0 +1,137 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module API
|
||||
class AlertManagementAlerts < ::API::Base
|
||||
feature_category :incident_management
|
||||
|
||||
params do
|
||||
requires :id, type: String, desc: 'The ID of a project'
|
||||
requires :alert_iid, type: Integer, desc: 'The IID of the Alert'
|
||||
end
|
||||
|
||||
resource :projects, requirements: ::API::API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
|
||||
namespace ':id/alert_management_alerts/:alert_iid/metric_images' do
|
||||
post 'authorize' do
|
||||
authorize!(:upload_alert_management_metric_image, find_project_alert(request.params[:alert_iid]))
|
||||
|
||||
require_gitlab_workhorse!
|
||||
::Gitlab::Workhorse.verify_api_request!(request.headers)
|
||||
status 200
|
||||
content_type ::Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE
|
||||
|
||||
params = {
|
||||
has_length: false,
|
||||
maximum_size: ::AlertManagement::MetricImage::MAX_FILE_SIZE.to_i
|
||||
}
|
||||
|
||||
::MetricImageUploader.workhorse_authorize(**params)
|
||||
end
|
||||
|
||||
desc 'Upload a metric image for an alert' do
|
||||
success Entities::MetricImage
|
||||
end
|
||||
params do
|
||||
requires :file, type: ::API::Validations::Types::WorkhorseFile, desc: 'The image file to be uploaded'
|
||||
optional :url, type: String, desc: 'The url to view more metric info'
|
||||
optional :url_text, type: String, desc: 'A description of the image or URL'
|
||||
end
|
||||
post do
|
||||
require_gitlab_workhorse!
|
||||
bad_request!('File is too large') if max_file_size_exceeded?
|
||||
|
||||
alert = find_project_alert(params[:alert_iid])
|
||||
|
||||
authorize!(:upload_alert_management_metric_image, alert)
|
||||
|
||||
upload = ::AlertManagement::MetricImages::UploadService.new(
|
||||
alert,
|
||||
current_user,
|
||||
params.slice(:file, :url, :url_text)
|
||||
).execute
|
||||
|
||||
if upload.success?
|
||||
present upload.payload[:metric],
|
||||
with: Entities::MetricImage,
|
||||
current_user: current_user,
|
||||
project: user_project
|
||||
else
|
||||
render_api_error!(upload.message, upload.http_status)
|
||||
end
|
||||
end
|
||||
|
||||
desc 'Metric Images for alert'
|
||||
get do
|
||||
alert = find_project_alert(params[:alert_iid])
|
||||
|
||||
if can?(current_user, :read_alert_management_metric_image, alert)
|
||||
present alert.metric_images.order_created_at_asc, with: Entities::MetricImage
|
||||
else
|
||||
render_api_error!('Alert not found', 404)
|
||||
end
|
||||
end
|
||||
|
||||
desc 'Update a metric image for an alert' do
|
||||
success Entities::MetricImage
|
||||
end
|
||||
params do
|
||||
requires :metric_image_id, type: Integer, desc: 'The ID of metric image'
|
||||
optional :url, type: String, desc: 'The url to view more metric info'
|
||||
optional :url_text, type: String, desc: 'A description of the image or URL'
|
||||
end
|
||||
put ':metric_image_id' do
|
||||
alert = find_project_alert(params[:alert_iid])
|
||||
|
||||
authorize!(:update_alert_management_metric_image, alert)
|
||||
|
||||
render_api_error!('Feature not available', 403) unless alert.metric_images_available?
|
||||
|
||||
metric_image = alert.metric_images.find_by_id(params[:metric_image_id])
|
||||
|
||||
render_api_error!('Metric image not found', 404) unless metric_image
|
||||
|
||||
if metric_image.update(params.slice(:url, :url_text))
|
||||
present metric_image, with: Entities::MetricImage, current_user: current_user, project: user_project
|
||||
else
|
||||
unprocessable_entity!('Metric image could not be updated')
|
||||
end
|
||||
end
|
||||
|
||||
desc 'Remove a metric image for an alert' do
|
||||
success Entities::MetricImage
|
||||
end
|
||||
params do
|
||||
requires :metric_image_id, type: Integer, desc: 'The ID of metric image'
|
||||
end
|
||||
delete ':metric_image_id' do
|
||||
alert = find_project_alert(params[:alert_iid])
|
||||
|
||||
authorize!(:destroy_alert_management_metric_image, alert)
|
||||
|
||||
render_api_error!('Feature not available', 403) unless alert.metric_images_available?
|
||||
|
||||
metric_image = alert.metric_images.find_by_id(params[:metric_image_id])
|
||||
|
||||
render_api_error!('Metric image not found', 404) unless metric_image
|
||||
|
||||
if metric_image.destroy
|
||||
no_content!
|
||||
else
|
||||
unprocessable_entity!('Metric image could not be deleted')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
helpers do
|
||||
def find_project_alert(iid, project_id = nil)
|
||||
project = project_id ? find_project!(project_id) : user_project
|
||||
|
||||
::AlertManagement::AlertsFinder.new(current_user, project, { iid: [iid] }).execute.first
|
||||
end
|
||||
|
||||
def max_file_size_exceeded?
|
||||
params[:file].size > ::AlertManagement::MetricImage::MAX_FILE_SIZE
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -163,6 +163,7 @@ module API
|
|||
mount ::API::Admin::InstanceClusters
|
||||
mount ::API::Admin::PlanLimits
|
||||
mount ::API::Admin::Sidekiq
|
||||
mount ::API::AlertManagementAlerts
|
||||
mount ::API::Appearance
|
||||
mount ::API::Applications
|
||||
mount ::API::Avatar
|
||||
|
|
9
lib/api/entities/metric_image.rb
Normal file
9
lib/api/entities/metric_image.rb
Normal file
|
@ -0,0 +1,9 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module API
|
||||
module Entities
|
||||
class MetricImage < Grape::Entity
|
||||
expose :id, :created_at, :filename, :file_path, :url, :url_text
|
||||
end
|
||||
end
|
||||
end
|
|
@ -5,6 +5,7 @@ module Gitlab
|
|||
module OAuth
|
||||
class Provider
|
||||
LABELS = {
|
||||
"alicloud" => "AliCloud",
|
||||
"dingtalk" => "DingTalk",
|
||||
"github" => "GitHub",
|
||||
"gitlab" => "GitLab.com",
|
||||
|
|
|
@ -27,12 +27,12 @@ variables:
|
|||
SECURE_ANALYZERS_PREFIX: "registry.gitlab.com/security-products"
|
||||
#
|
||||
FUZZAPI_VERSION: "1"
|
||||
FUZZAPI_VERSION_TAG: ""
|
||||
FUZZAPI_IMAGE_SUFFIX: ""
|
||||
FUZZAPI_IMAGE: api-fuzzing
|
||||
|
||||
apifuzzer_fuzz:
|
||||
stage: fuzz
|
||||
image: $SECURE_ANALYZERS_PREFIX/$FUZZAPI_IMAGE:$FUZZAPI_VERSION$FUZZAPI_VERSION_TAG
|
||||
image: $SECURE_ANALYZERS_PREFIX/$FUZZAPI_IMAGE:$FUZZAPI_VERSION$FUZZAPI_IMAGE_SUFFIX
|
||||
allow_failure: true
|
||||
rules:
|
||||
- if: $API_FUZZING_DISABLED
|
||||
|
@ -43,7 +43,7 @@ apifuzzer_fuzz:
|
|||
- if: $CI_COMMIT_BRANCH &&
|
||||
$CI_GITLAB_FIPS_MODE == "true"
|
||||
variables:
|
||||
FUZZAPI_VERSION_TAG: "-fips"
|
||||
FUZZAPI_IMAGE_SUFFIX: "-fips"
|
||||
- if: $CI_COMMIT_BRANCH
|
||||
script:
|
||||
- /peach/analyzer-fuzz-api
|
||||
|
|
|
@ -27,12 +27,12 @@ variables:
|
|||
SECURE_ANALYZERS_PREFIX: "registry.gitlab.com/security-products"
|
||||
#
|
||||
DAST_API_VERSION: "1"
|
||||
DAST_API_VERSION_TAG: ""
|
||||
DAST_API_IMAGE_SUFFIX: ""
|
||||
DAST_API_IMAGE: api-fuzzing
|
||||
|
||||
dast_api:
|
||||
stage: dast
|
||||
image: $SECURE_ANALYZERS_PREFIX/$DAST_API_IMAGE:$DAST_API_VERSION$DAST_API_VERSION_TAG
|
||||
image: $SECURE_ANALYZERS_PREFIX/$DAST_API_IMAGE:$DAST_API_VERSION$DAST_API_IMAGE_SUFFIX
|
||||
allow_failure: true
|
||||
rules:
|
||||
- if: $DAST_API_DISABLED
|
||||
|
@ -43,7 +43,7 @@ dast_api:
|
|||
- if: $CI_COMMIT_BRANCH &&
|
||||
$CI_GITLAB_FIPS_MODE == "true"
|
||||
variables:
|
||||
DAST_API_VERSION_TAG: "-fips"
|
||||
DAST_API_IMAGE_SUFFIX: "-fips"
|
||||
- if: $CI_COMMIT_BRANCH
|
||||
script:
|
||||
- /peach/analyzer-dast-api
|
||||
|
|
|
@ -22,7 +22,7 @@ module Gitlab
|
|||
'frame_src' => ContentSecurityPolicy::Directives.frame_src,
|
||||
'img_src' => "'self' data: blob: http: https:",
|
||||
'manifest_src' => "'self'",
|
||||
'media_src' => "'self'",
|
||||
'media_src' => "'self' data:",
|
||||
'script_src' => ContentSecurityPolicy::Directives.script_src,
|
||||
'style_src' => "'self' 'unsafe-inline'",
|
||||
'worker_src' => "#{Gitlab::Utils.append_path(Gitlab.config.gitlab.url, 'assets/')} blob: data:",
|
||||
|
|
|
@ -6,6 +6,14 @@ module Gitlab
|
|||
include Gitlab::Utils::StrongMemoize
|
||||
|
||||
class DiffFile < Gitlab::Diff::File
|
||||
RENDERED_TIMEOUT_BACKGROUND = 10.seconds
|
||||
RENDERED_TIMEOUT_FOREGROUND = 1.5.seconds
|
||||
BACKGROUND_EXECUTION = 'background'
|
||||
FOREGROUND_EXECUTION = 'foreground'
|
||||
LOG_IPYNBDIFF_GENERATED = 'IPYNB_DIFF_GENERATED'
|
||||
LOG_IPYNBDIFF_TIMEOUT = 'IPYNB_DIFF_TIMEOUT'
|
||||
LOG_IPYNBDIFF_INVALID = 'IPYNB_DIFF_INVALID'
|
||||
|
||||
attr_reader :source_diff
|
||||
|
||||
delegate :repository, :diff_refs, :fallback_diff_refs, :unfolded, :unique_identifier,
|
||||
|
@ -52,14 +60,17 @@ module Gitlab
|
|||
|
||||
def notebook_diff
|
||||
strong_memoize(:notebook_diff) do
|
||||
Gitlab::AppLogger.info({ message: 'IPYNB_DIFF_GENERATED' })
|
||||
|
||||
IpynbDiff.diff(source_diff.old_blob&.data, source_diff.new_blob&.data,
|
||||
raise_if_invalid_nb: true,
|
||||
diffy_opts: { include_diff_info: true })
|
||||
Timeout.timeout(timeout_time) do
|
||||
IpynbDiff.diff(source_diff.old_blob&.data, source_diff.new_blob&.data,
|
||||
raise_if_invalid_nb: true, diffy_opts: { include_diff_info: true })&.tap do
|
||||
log_event(LOG_IPYNBDIFF_GENERATED)
|
||||
end
|
||||
end
|
||||
rescue Timeout::Error => e
|
||||
rendered_timeout.increment(source: Gitlab::Runtime.sidekiq? ? BACKGROUND_EXECUTION : FOREGROUND_EXECUTION)
|
||||
log_event(LOG_IPYNBDIFF_TIMEOUT, e)
|
||||
rescue IpynbDiff::InvalidNotebookError, IpynbDiff::InvalidTokenError => e
|
||||
Gitlab::ErrorTracking.log_exception(e)
|
||||
nil
|
||||
log_event(LOG_IPYNBDIFF_INVALID, e)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -116,6 +127,23 @@ module Gitlab
|
|||
|
||||
[removals, additions]
|
||||
end
|
||||
|
||||
def rendered_timeout
|
||||
@rendered_timeout ||= Gitlab::Metrics.counter(
|
||||
:ipynb_semantic_diff_timeouts_total,
|
||||
'Counts the times notebook diff rendering timed out'
|
||||
)
|
||||
end
|
||||
|
||||
def timeout_time
|
||||
Gitlab::Runtime.sidekiq? ? RENDERED_TIMEOUT_BACKGROUND : RENDERED_TIMEOUT_FOREGROUND
|
||||
end
|
||||
|
||||
def log_event(message, error = nil)
|
||||
Gitlab::AppLogger.info({ message: message })
|
||||
Gitlab::ErrorTracking.track_exception(error) if error
|
||||
nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4178,9 +4178,6 @@ msgstr ""
|
|||
msgid "An error occurred while uploading the file. Please try again."
|
||||
msgstr ""
|
||||
|
||||
msgid "An error occurred while uploading the image. Please try again."
|
||||
msgstr ""
|
||||
|
||||
msgid "An error occurred while validating group path"
|
||||
msgstr ""
|
||||
|
||||
|
@ -6559,9 +6556,6 @@ msgstr ""
|
|||
msgid "CI variables"
|
||||
msgstr ""
|
||||
|
||||
msgid "CI will run using the credentials assigned above."
|
||||
msgstr ""
|
||||
|
||||
msgid "CI/CD"
|
||||
msgstr ""
|
||||
|
||||
|
@ -18881,7 +18875,7 @@ msgstr ""
|
|||
msgid "If checked, new group memberships and permissions can only be added via LDAP synchronization"
|
||||
msgstr ""
|
||||
|
||||
msgid "If disabled, a diverged local branch will not be automatically updated with commits from its remote counterpart, to prevent local data loss. If the default branch (%{default_branch}) has diverged and cannot be updated, mirroring will fail. Other diverged branches are silently ignored."
|
||||
msgid "If disabled, a diverged local branch will not be automatically updated with commits from its remote counterpart, to prevent local data loss. If the default branch (%{default_branch}) has diverged and cannot be updated, mirroring will fail. Other diverged branches are silently ignored. %{link_start}Learn more.%{link_end}"
|
||||
msgstr ""
|
||||
|
||||
msgid "If disabled, only administrators can configure repository mirroring."
|
||||
|
@ -39907,7 +39901,7 @@ msgstr ""
|
|||
msgid "Trigger pipelines for mirror updates"
|
||||
msgstr ""
|
||||
|
||||
msgid "Trigger pipelines when branches or tags are updated in the upstream repository. Depending on the activity of the upstream repository, this may greatly increase the load on your CI runners. Only enable this if you know they can handle the load."
|
||||
msgid "Trigger pipelines when branches or tags are updated in the upstream repository. Depending on the activity of the upstream repository, this may greatly increase the load on your CI runners. Only enable this if you know they can handle the load. %{strong_start}CI will run using the credentials assigned above.%{strong_end} %{link_start}Learn more.%{link_end}"
|
||||
msgstr ""
|
||||
|
||||
msgid "Trigger removed."
|
||||
|
|
|
@ -31,7 +31,7 @@ RSpec.describe Profiles::AccountsController do
|
|||
end
|
||||
end
|
||||
|
||||
[:twitter, :facebook, :google_oauth2, :gitlab, :github, :bitbucket, :crowd, :auth0, :authentiq, :dingtalk].each do |provider|
|
||||
[:twitter, :facebook, :google_oauth2, :gitlab, :github, :bitbucket, :crowd, :auth0, :authentiq, :dingtalk, :alicloud].each do |provider|
|
||||
describe "#{provider} provider" do
|
||||
let(:user) { create(:omniauth_user, provider: provider.to_s) }
|
||||
|
||||
|
|
|
@ -211,6 +211,7 @@ RSpec.describe SearchController do
|
|||
:global_search_merge_requests_tab | 'merge_requests'
|
||||
:global_search_wiki_tab | 'wiki_blobs'
|
||||
:global_search_commits_tab | 'commits'
|
||||
:global_search_users_tab | 'users'
|
||||
end
|
||||
|
||||
with_them do
|
||||
|
|
|
@ -701,6 +701,24 @@ RSpec.describe UploadsController do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when viewing alert metric images' do
|
||||
let!(:user) { create(:user) }
|
||||
let!(:project) { create(:project) }
|
||||
let(:alert) { create(:alert_management_alert, project: project) }
|
||||
let(:metric_image) { create(:alert_metric_image, alert: alert) }
|
||||
|
||||
before do
|
||||
project.add_developer(user)
|
||||
sign_in(user)
|
||||
end
|
||||
|
||||
it "responds with status 200" do
|
||||
get :show, params: { model: "alert_management_metric_image", mounted_as: 'file', id: metric_image.id, filename: metric_image.filename }
|
||||
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def post_authorize(verified: true)
|
||||
|
|
16
spec/factories/alert_management/metric_images.rb
Normal file
16
spec/factories/alert_management/metric_images.rb
Normal file
|
@ -0,0 +1,16 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
FactoryBot.define do
|
||||
factory :alert_metric_image, class: 'AlertManagement::MetricImage' do
|
||||
association :alert, factory: :alert_management_alert
|
||||
url { generate(:url) }
|
||||
|
||||
trait :local do
|
||||
file_store { ObjectStorage::Store::LOCAL }
|
||||
end
|
||||
|
||||
after(:build) do |image|
|
||||
image.file = fixture_file_upload('spec/fixtures/rails_sample.jpg', 'image/jpg')
|
||||
end
|
||||
end
|
||||
end
|
|
@ -16,7 +16,7 @@ RSpec.describe 'OAuth Login', :js, :allow_forgery_protection do
|
|||
end
|
||||
|
||||
providers = [:github, :twitter, :bitbucket, :gitlab, :google_oauth2,
|
||||
:facebook, :cas3, :auth0, :authentiq, :salesforce, :dingtalk]
|
||||
:facebook, :cas3, :auth0, :authentiq, :salesforce, :dingtalk, :alicloud]
|
||||
|
||||
around do |example|
|
||||
with_omniauth_full_host { example.run }
|
||||
|
|
|
@ -8,26 +8,14 @@ RSpec.describe 'Project wikis', :js do
|
|||
let(:wiki) { create(:project_wiki, user: user, project: project) }
|
||||
let(:project) { create(:project, namespace: user.namespace, creator: user) }
|
||||
|
||||
shared_examples 'wiki feature tests' do
|
||||
it_behaves_like 'User creates wiki page'
|
||||
it_behaves_like 'User deletes wiki page'
|
||||
it_behaves_like 'User previews wiki changes'
|
||||
it_behaves_like 'User updates wiki page'
|
||||
it_behaves_like 'User uses wiki shortcuts'
|
||||
it_behaves_like 'User views AsciiDoc page with includes'
|
||||
it_behaves_like 'User views a wiki page'
|
||||
it_behaves_like 'User views wiki pages'
|
||||
it_behaves_like 'User views wiki sidebar'
|
||||
it_behaves_like 'User views Git access wiki page'
|
||||
end
|
||||
|
||||
it_behaves_like 'wiki feature tests'
|
||||
|
||||
context 'when feature flag :wiki_async_load is disabled' do
|
||||
before do
|
||||
stub_feature_flags(wiki_async_load: false)
|
||||
end
|
||||
|
||||
it_behaves_like 'wiki feature tests'
|
||||
end
|
||||
it_behaves_like 'User creates wiki page'
|
||||
it_behaves_like 'User deletes wiki page'
|
||||
it_behaves_like 'User previews wiki changes'
|
||||
it_behaves_like 'User updates wiki page'
|
||||
it_behaves_like 'User uses wiki shortcuts'
|
||||
it_behaves_like 'User views AsciiDoc page with includes'
|
||||
it_behaves_like 'User views a wiki page'
|
||||
it_behaves_like 'User views wiki pages'
|
||||
it_behaves_like 'User views wiki sidebar'
|
||||
it_behaves_like 'User views Git access wiki page'
|
||||
end
|
||||
|
|
|
@ -1,21 +1,24 @@
|
|||
import { GlLoadingIcon } from '@gitlab/ui';
|
||||
import { NodeViewWrapper } from '@tiptap/vue-2';
|
||||
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
|
||||
import ImageWrapper from '~/content_editor/components/wrappers/image.vue';
|
||||
import MediaWrapper from '~/content_editor/components/wrappers/media.vue';
|
||||
|
||||
describe('content/components/wrappers/image', () => {
|
||||
describe('content/components/wrappers/media', () => {
|
||||
let wrapper;
|
||||
|
||||
const createWrapper = async (nodeAttrs = {}) => {
|
||||
wrapper = shallowMountExtended(ImageWrapper, {
|
||||
wrapper = shallowMountExtended(MediaWrapper, {
|
||||
propsData: {
|
||||
node: {
|
||||
attrs: nodeAttrs,
|
||||
type: {
|
||||
name: 'image',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
const findImage = () => wrapper.findByTestId('image');
|
||||
const findMedia = () => wrapper.findByTestId('media');
|
||||
const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
|
||||
|
||||
afterEach(() => {
|
||||
|
@ -33,7 +36,7 @@ describe('content/components/wrappers/image', () => {
|
|||
|
||||
createWrapper({ src });
|
||||
|
||||
expect(findImage().attributes().src).toBe(src);
|
||||
expect(findMedia().attributes().src).toBe(src);
|
||||
});
|
||||
|
||||
describe('when uploading', () => {
|
||||
|
@ -45,8 +48,8 @@ describe('content/components/wrappers/image', () => {
|
|||
expect(findLoadingIcon().exists()).toBe(true);
|
||||
});
|
||||
|
||||
it('adds gl-opacity-5 class selector to image', () => {
|
||||
expect(findImage().classes()).toContain('gl-opacity-5');
|
||||
it('adds gl-opacity-5 class selector to the media tag', () => {
|
||||
expect(findMedia().classes()).toContain('gl-opacity-5');
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -59,8 +62,8 @@ describe('content/components/wrappers/image', () => {
|
|||
expect(findLoadingIcon().exists()).toBe(false);
|
||||
});
|
||||
|
||||
it('does not add gl-opacity-5 class selector to image', () => {
|
||||
expect(findImage().classes()).not.toContain('gl-opacity-5');
|
||||
it('does not add gl-opacity-5 class selector to the media tag', () => {
|
||||
expect(findMedia().classes()).not.toContain('gl-opacity-5');
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,7 +1,10 @@
|
|||
import axios from 'axios';
|
||||
import MockAdapter from 'axios-mock-adapter';
|
||||
import waitForPromises from 'helpers/wait_for_promises';
|
||||
import Attachment from '~/content_editor/extensions/attachment';
|
||||
import Image from '~/content_editor/extensions/image';
|
||||
import Audio from '~/content_editor/extensions/audio';
|
||||
import Video from '~/content_editor/extensions/video';
|
||||
import Link from '~/content_editor/extensions/link';
|
||||
import Loading from '~/content_editor/extensions/loading';
|
||||
import { VARIANT_DANGER } from '~/flash';
|
||||
|
@ -14,6 +17,23 @@ const PROJECT_WIKI_ATTACHMENT_IMAGE_HTML = `<p data-sourcepos="1:1-1:27" dir="au
|
|||
<img alt="test-file" class="lazy" data-src="/group1/project1/-/wikis/test-file.png" data-canonical-src="test-file.png">
|
||||
</a>
|
||||
</p>`;
|
||||
|
||||
const PROJECT_WIKI_ATTACHMENT_VIDEO_HTML = `<p data-sourcepos="1:1-1:132" dir="auto">
|
||||
<span class="media-container video-container">
|
||||
<video src="/group1/project1/-/wikis/test-file.mp4" controls="true" data-setup="{}" data-title="test-file" width="400" preload="metadata" data-canonical-src="test-file.mp4">
|
||||
</video>
|
||||
<a href="/himkp/test/-/wikis/test-file.mp4" target="_blank" rel="noopener noreferrer" title="Download 'test-file'" data-canonical-src="test-file.mp4">test-file</a>
|
||||
</span>
|
||||
</p>`;
|
||||
|
||||
const PROJECT_WIKI_ATTACHMENT_AUDIO_HTML = `<p data-sourcepos="3:1-3:74" dir="auto">
|
||||
<span class="media-container audio-container">
|
||||
<audio src="/himkp/test/-/wikis/test-file.mp3" controls="true" data-setup="{}" data-title="test-file" data-canonical-src="test-file.mp3">
|
||||
</audio>
|
||||
<a href="/himkp/test/-/wikis/test-file.mp3" target="_blank" rel="noopener noreferrer" title="Download 'test-file'" data-canonical-src="test-file.mp3">test-file</a>
|
||||
</span>
|
||||
</p>`;
|
||||
|
||||
const PROJECT_WIKI_ATTACHMENT_LINK_HTML = `<p data-sourcepos="1:1-1:26" dir="auto">
|
||||
<a href="/group1/project1/-/wikis/test-file.zip" data-canonical-src="test-file.zip">test-file</a>
|
||||
</p>`;
|
||||
|
@ -23,6 +43,8 @@ describe('content_editor/extensions/attachment', () => {
|
|||
let doc;
|
||||
let p;
|
||||
let image;
|
||||
let audio;
|
||||
let video;
|
||||
let loading;
|
||||
let link;
|
||||
let renderMarkdown;
|
||||
|
@ -31,15 +53,18 @@ describe('content_editor/extensions/attachment', () => {
|
|||
|
||||
const uploadsPath = '/uploads/';
|
||||
const imageFile = new File(['foo'], 'test-file.png', { type: 'image/png' });
|
||||
const audioFile = new File(['foo'], 'test-file.mp3', { type: 'audio/mpeg' });
|
||||
const videoFile = new File(['foo'], 'test-file.mp4', { type: 'video/mp4' });
|
||||
const attachmentFile = new File(['foo'], 'test-file.zip', { type: 'application/zip' });
|
||||
|
||||
const expectDocumentAfterTransaction = ({ number, expectedDoc, action }) => {
|
||||
return new Promise((resolve) => {
|
||||
let counter = 1;
|
||||
const handleTransaction = () => {
|
||||
const handleTransaction = async () => {
|
||||
if (counter === number) {
|
||||
expect(tiptapEditor.state.doc.toJSON()).toEqual(expectedDoc.toJSON());
|
||||
tiptapEditor.off('update', handleTransaction);
|
||||
await waitForPromises();
|
||||
resolve();
|
||||
}
|
||||
|
||||
|
@ -60,18 +85,22 @@ describe('content_editor/extensions/attachment', () => {
|
|||
Loading,
|
||||
Link,
|
||||
Image,
|
||||
Audio,
|
||||
Video,
|
||||
Attachment.configure({ renderMarkdown, uploadsPath, eventHub }),
|
||||
],
|
||||
});
|
||||
|
||||
({
|
||||
builders: { doc, p, image, loading, link },
|
||||
builders: { doc, p, image, audio, video, loading, link },
|
||||
} = createDocBuilder({
|
||||
tiptapEditor,
|
||||
names: {
|
||||
loading: { markType: Loading.name },
|
||||
image: { nodeType: Image.name },
|
||||
link: { nodeType: Link.name },
|
||||
audio: { nodeType: Audio.name },
|
||||
video: { nodeType: Video.name },
|
||||
},
|
||||
}));
|
||||
|
||||
|
@ -103,17 +132,22 @@ describe('content_editor/extensions/attachment', () => {
|
|||
tiptapEditor.commands.setContent(initialDoc.toJSON());
|
||||
});
|
||||
|
||||
describe('when the file has image mime type', () => {
|
||||
const base64EncodedFile = 'data:image/png;base64,Zm9v';
|
||||
describe.each`
|
||||
nodeType | mimeType | html | file | mediaType
|
||||
${'image'} | ${'image/png'} | ${PROJECT_WIKI_ATTACHMENT_IMAGE_HTML} | ${imageFile} | ${(attrs) => image(attrs)}
|
||||
${'audio'} | ${'audio/mpeg'} | ${PROJECT_WIKI_ATTACHMENT_AUDIO_HTML} | ${audioFile} | ${(attrs) => audio(attrs)}
|
||||
${'video'} | ${'video/mp4'} | ${PROJECT_WIKI_ATTACHMENT_VIDEO_HTML} | ${videoFile} | ${(attrs) => video(attrs)}
|
||||
`('when the file has $nodeType mime type', ({ mimeType, html, file, mediaType }) => {
|
||||
const base64EncodedFile = `data:${mimeType};base64,Zm9v`;
|
||||
|
||||
beforeEach(() => {
|
||||
renderMarkdown.mockResolvedValue(PROJECT_WIKI_ATTACHMENT_IMAGE_HTML);
|
||||
renderMarkdown.mockResolvedValue(html);
|
||||
});
|
||||
|
||||
describe('when uploading succeeds', () => {
|
||||
const successResponse = {
|
||||
link: {
|
||||
markdown: '![test-file](test-file.png)',
|
||||
markdown: `![test-file](${file.name})`,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -121,21 +155,21 @@ describe('content_editor/extensions/attachment', () => {
|
|||
mock.onPost().reply(httpStatus.OK, successResponse);
|
||||
});
|
||||
|
||||
it('inserts an image with src set to the encoded image file and uploading true', async () => {
|
||||
const expectedDoc = doc(p(image({ uploading: true, src: base64EncodedFile })));
|
||||
it('inserts a media content with src set to the encoded content and uploading true', async () => {
|
||||
const expectedDoc = doc(p(mediaType({ uploading: true, src: base64EncodedFile })));
|
||||
|
||||
await expectDocumentAfterTransaction({
|
||||
number: 1,
|
||||
expectedDoc,
|
||||
action: () => tiptapEditor.commands.uploadAttachment({ file: imageFile }),
|
||||
action: () => tiptapEditor.commands.uploadAttachment({ file }),
|
||||
});
|
||||
});
|
||||
|
||||
it('updates the inserted image with canonicalSrc when upload is successful', async () => {
|
||||
it('updates the inserted content with canonicalSrc when upload is successful', async () => {
|
||||
const expectedDoc = doc(
|
||||
p(
|
||||
image({
|
||||
canonicalSrc: 'test-file.png',
|
||||
mediaType({
|
||||
canonicalSrc: file.name,
|
||||
src: base64EncodedFile,
|
||||
alt: 'test-file',
|
||||
uploading: false,
|
||||
|
@ -146,7 +180,7 @@ describe('content_editor/extensions/attachment', () => {
|
|||
await expectDocumentAfterTransaction({
|
||||
number: 2,
|
||||
expectedDoc,
|
||||
action: () => tiptapEditor.commands.uploadAttachment({ file: imageFile }),
|
||||
action: () => tiptapEditor.commands.uploadAttachment({ file }),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -162,16 +196,16 @@ describe('content_editor/extensions/attachment', () => {
|
|||
await expectDocumentAfterTransaction({
|
||||
number: 2,
|
||||
expectedDoc,
|
||||
action: () => tiptapEditor.commands.uploadAttachment({ file: imageFile }),
|
||||
action: () => tiptapEditor.commands.uploadAttachment({ file }),
|
||||
});
|
||||
});
|
||||
|
||||
it('emits an alert event that includes an error message', (done) => {
|
||||
tiptapEditor.commands.uploadAttachment({ file: imageFile });
|
||||
tiptapEditor.commands.uploadAttachment({ file });
|
||||
|
||||
eventHub.$on('alert', ({ message, variant }) => {
|
||||
expect(variant).toBe(VARIANT_DANGER);
|
||||
expect(message).toBe('An error occurred while uploading the image. Please try again.');
|
||||
expect(message).toBe('An error occurred while uploading the file. Please try again.');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -467,6 +467,12 @@ RSpec.describe SearchHelper do
|
|||
describe '#show_user_search_tab?' do
|
||||
subject { show_user_search_tab? }
|
||||
|
||||
let(:current_user) { build(:user) }
|
||||
|
||||
before do
|
||||
allow(self).to receive(:current_user).and_return(current_user)
|
||||
end
|
||||
|
||||
context 'when project search' do
|
||||
before do
|
||||
@project = :some_project
|
||||
|
@ -481,11 +487,14 @@ RSpec.describe SearchHelper do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when not project search' do
|
||||
context 'when group search' do
|
||||
before do
|
||||
@group = :some_group
|
||||
end
|
||||
|
||||
context 'when current_user can read_users_list' do
|
||||
before do
|
||||
allow(self).to receive(:current_user).and_return(:the_current_user)
|
||||
allow(self).to receive(:can?).with(:the_current_user, :read_users_list).and_return(true)
|
||||
allow(self).to receive(:can?).with(current_user, :read_users_list).and_return(true)
|
||||
end
|
||||
|
||||
it { is_expected.to eq(true) }
|
||||
|
@ -493,8 +502,33 @@ RSpec.describe SearchHelper do
|
|||
|
||||
context 'when current_user cannot read_users_list' do
|
||||
before do
|
||||
allow(self).to receive(:current_user).and_return(:the_current_user)
|
||||
allow(self).to receive(:can?).with(:the_current_user, :read_users_list).and_return(false)
|
||||
allow(self).to receive(:can?).with(current_user, :read_users_list).and_return(false)
|
||||
end
|
||||
|
||||
it { is_expected.to eq(false) }
|
||||
end
|
||||
end
|
||||
|
||||
context 'when global search' do
|
||||
context 'when current_user can read_users_list' do
|
||||
before do
|
||||
allow(self).to receive(:can?).with(current_user, :read_users_list).and_return(true)
|
||||
end
|
||||
|
||||
it { is_expected.to eq(true) }
|
||||
|
||||
context 'when global_search_user_tab feature flag is disabled' do
|
||||
before do
|
||||
stub_feature_flags(global_search_users_tab: false)
|
||||
end
|
||||
|
||||
it { is_expected.to eq(false) }
|
||||
end
|
||||
end
|
||||
|
||||
context 'when current_user cannot read_users_list' do
|
||||
before do
|
||||
allow(self).to receive(:can?).with(current_user, :read_users_list).and_return(false)
|
||||
end
|
||||
|
||||
it { is_expected.to eq(false) }
|
||||
|
|
|
@ -63,6 +63,28 @@ RSpec.describe Gitlab::Diff::Rendered::Notebook::DiffFile do
|
|||
expect(nb_file.diff).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context 'timeout' do
|
||||
it 'utilizes timeout for web' do
|
||||
expect(Timeout).to receive(:timeout).with(described_class::RENDERED_TIMEOUT_FOREGROUND).and_call_original
|
||||
|
||||
nb_file.diff
|
||||
end
|
||||
|
||||
it 'falls back to nil on timeout' do
|
||||
allow(Gitlab::ErrorTracking).to receive(:track_and_raise_for_dev_exception)
|
||||
expect(Timeout).to receive(:timeout).and_raise(Timeout::Error)
|
||||
|
||||
expect(nb_file.diff).to be_nil
|
||||
end
|
||||
|
||||
it 'utilizes longer timeout for sidekiq' do
|
||||
allow(Gitlab::Runtime).to receive(:sidekiq?).and_return(true)
|
||||
expect(Timeout).to receive(:timeout).with(described_class::RENDERED_TIMEOUT_BACKGROUND).and_call_original
|
||||
|
||||
nb_file.diff
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#has_renderable?' do
|
||||
|
|
26
spec/models/alert_management/metric_image_spec.rb
Normal file
26
spec/models/alert_management/metric_image_spec.rb
Normal file
|
@ -0,0 +1,26 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe AlertManagement::MetricImage do
|
||||
subject { build(:alert_metric_image) }
|
||||
|
||||
describe 'associations' do
|
||||
it { is_expected.to belong_to(:alert) }
|
||||
end
|
||||
|
||||
describe 'validations' do
|
||||
it { is_expected.to be_valid }
|
||||
it { is_expected.to validate_presence_of(:file) }
|
||||
it { is_expected.to validate_length_of(:url).is_at_most(255) }
|
||||
it { is_expected.to validate_length_of(:url_text).is_at_most(128) }
|
||||
end
|
||||
|
||||
describe '.available_for?' do
|
||||
subject { described_class.available_for?(issue.project) }
|
||||
|
||||
let_it_be_with_refind(:issue) { create(:issue) }
|
||||
|
||||
it { is_expected.to eq(true) }
|
||||
end
|
||||
end
|
|
@ -1585,6 +1585,31 @@ RSpec.describe Ci::Build do
|
|||
|
||||
it { is_expected.to eq('review/x') }
|
||||
end
|
||||
|
||||
context 'when environment name uses a nested variable' do
|
||||
let(:yaml_variables) do
|
||||
[
|
||||
{ key: 'ENVIRONMENT_NAME', value: '${CI_COMMIT_REF_NAME}' }
|
||||
]
|
||||
end
|
||||
|
||||
let(:build) do
|
||||
create(:ci_build,
|
||||
ref: 'master',
|
||||
yaml_variables: yaml_variables,
|
||||
environment: 'review/$ENVIRONMENT_NAME')
|
||||
end
|
||||
|
||||
it { is_expected.to eq('review/master') }
|
||||
|
||||
context 'when the FF ci_expand_environment_name_and_url is disabled' do
|
||||
before do
|
||||
stub_feature_flags(ci_expand_environment_name_and_url: false)
|
||||
end
|
||||
|
||||
it { is_expected.to eq('review/${CI_COMMIT_REF_NAME}') }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#expanded_kubernetes_namespace' do
|
||||
|
|
|
@ -1560,7 +1560,7 @@ RSpec.describe User do
|
|||
end
|
||||
|
||||
it 'adds the confirmed primary email to emails' do
|
||||
expect(user.emails.confirmed.map(&:email)).not_to include(user.email)
|
||||
expect(user.emails.confirmed.map(&:email)).not_to include(user.unconfirmed_email)
|
||||
|
||||
user.confirm
|
||||
|
||||
|
@ -1619,14 +1619,23 @@ RSpec.describe User do
|
|||
context 'when the email is changed but not confirmed' do
|
||||
let(:user) { create(:user, email: 'primary@example.com') }
|
||||
|
||||
it 'does not add the new email to emails yet' do
|
||||
before do
|
||||
user.update!(email: 'new_primary@example.com')
|
||||
end
|
||||
|
||||
it 'does not add the new email to emails yet' do
|
||||
expect(user.unconfirmed_email).to eq('new_primary@example.com')
|
||||
expect(user.email).to eq('primary@example.com')
|
||||
expect(user).to be_confirmed
|
||||
expect(user.emails.pluck(:email)).not_to include('new_primary@example.com')
|
||||
end
|
||||
|
||||
it 'adds the new email to emails upon confirmation' do
|
||||
user.confirm
|
||||
expect(user.email).to eq('new_primary@example.com')
|
||||
expect(user).to be_confirmed
|
||||
expect(user.emails.pluck(:email)).to include('new_primary@example.com')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the user is created as not confirmed' do
|
||||
|
@ -1636,6 +1645,11 @@ RSpec.describe User do
|
|||
expect(user).not_to be_confirmed
|
||||
expect(user.emails.pluck(:email)).not_to include('primary@example.com')
|
||||
end
|
||||
|
||||
it 'adds the email to emails upon confirmation' do
|
||||
user.confirm
|
||||
expect(user.emails.pluck(:email)).to include('primary@example.com')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the user is created as confirmed' do
|
||||
|
|
|
@ -3,9 +3,10 @@
|
|||
require 'spec_helper'
|
||||
|
||||
RSpec.describe AlertManagement::AlertPolicy, :models do
|
||||
let(:alert) { create(:alert_management_alert) }
|
||||
let(:project) { alert.project }
|
||||
let(:user) { create(:user) }
|
||||
let_it_be(:user) { create(:user) }
|
||||
let_it_be(:project) { create(:project) }
|
||||
let_it_be(:alert) { create(:alert_management_alert, project: project, issue: incident) }
|
||||
let_it_be(:incident) { nil }
|
||||
|
||||
subject(:policy) { described_class.new(user, alert) }
|
||||
|
||||
|
@ -21,5 +22,50 @@ RSpec.describe AlertManagement::AlertPolicy, :models do
|
|||
it { is_expected.to be_allowed :read_alert_management_alert }
|
||||
it { is_expected.to be_allowed :update_alert_management_alert }
|
||||
end
|
||||
|
||||
shared_examples 'does not allow metric image reads' do
|
||||
it { expect(policy).to be_disallowed(:read_alert_management_metric_image) }
|
||||
end
|
||||
|
||||
shared_examples 'does not allow metric image updates' do
|
||||
specify do
|
||||
expect(policy).to be_disallowed(:upload_alert_management_metric_image)
|
||||
expect(policy).to be_disallowed(:destroy_alert_management_metric_image)
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples 'allows metric image reads' do
|
||||
it { expect(policy).to be_allowed(:read_alert_management_metric_image) }
|
||||
end
|
||||
|
||||
shared_examples 'allows metric image updates' do
|
||||
specify do
|
||||
expect(policy).to be_allowed(:upload_alert_management_metric_image)
|
||||
expect(policy).to be_allowed(:destroy_alert_management_metric_image)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when user is not a member' do
|
||||
include_examples 'does not allow metric image reads'
|
||||
include_examples 'does not allow metric image updates'
|
||||
end
|
||||
|
||||
context 'when user is a guest' do
|
||||
before do
|
||||
project.add_guest(user)
|
||||
end
|
||||
|
||||
include_examples 'does not allow metric image reads'
|
||||
include_examples 'does not allow metric image updates'
|
||||
end
|
||||
|
||||
context 'when user is a developer' do
|
||||
before do
|
||||
project.add_developer(user)
|
||||
end
|
||||
|
||||
include_examples 'allows metric image reads'
|
||||
include_examples 'allows metric image updates'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
411
spec/requests/api/alert_management_alerts_spec.rb
Normal file
411
spec/requests/api/alert_management_alerts_spec.rb
Normal file
|
@ -0,0 +1,411 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe API::AlertManagementAlerts do
|
||||
let_it_be(:creator) { create(:user) }
|
||||
let_it_be(:project) do
|
||||
create(:project, :public, creator_id: creator.id, namespace: creator.namespace)
|
||||
end
|
||||
|
||||
let_it_be(:user) { create(:user) }
|
||||
let_it_be(:alert) { create(:alert_management_alert, project: project) }
|
||||
|
||||
describe 'PUT /projects/:id/alert_management_alerts/:alert_iid/metric_images/authorize' do
|
||||
include_context 'workhorse headers'
|
||||
|
||||
before do
|
||||
project.add_developer(user)
|
||||
end
|
||||
|
||||
subject do
|
||||
post api("/projects/#{project.id}/alert_management_alerts/#{alert.iid}/metric_images/authorize", user),
|
||||
headers: workhorse_headers
|
||||
end
|
||||
|
||||
it 'authorizes uploading with workhorse header' do
|
||||
subject
|
||||
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(response.media_type.to_s).to eq(Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE)
|
||||
end
|
||||
|
||||
it 'rejects requests that bypassed gitlab-workhorse' do
|
||||
workhorse_headers.delete(Gitlab::Workhorse::INTERNAL_API_REQUEST_HEADER)
|
||||
|
||||
subject
|
||||
|
||||
expect(response).to have_gitlab_http_status(:forbidden)
|
||||
end
|
||||
|
||||
context 'when using remote storage' do
|
||||
context 'when direct upload is enabled' do
|
||||
before do
|
||||
stub_uploads_object_storage(MetricImageUploader, enabled: true, direct_upload: true)
|
||||
end
|
||||
|
||||
it 'responds with status 200, location of file remote store and object details' do
|
||||
subject
|
||||
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(response.media_type.to_s).to eq(Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE)
|
||||
expect(json_response).not_to have_key('TempPath')
|
||||
expect(json_response['RemoteObject']).to have_key('ID')
|
||||
expect(json_response['RemoteObject']).to have_key('GetURL')
|
||||
expect(json_response['RemoteObject']).to have_key('StoreURL')
|
||||
expect(json_response['RemoteObject']).to have_key('DeleteURL')
|
||||
expect(json_response['RemoteObject']).to have_key('MultipartUpload')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when direct upload is disabled' do
|
||||
before do
|
||||
stub_uploads_object_storage(MetricImageUploader, enabled: true, direct_upload: false)
|
||||
end
|
||||
|
||||
it 'handles as a local file' do
|
||||
subject
|
||||
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(response.media_type.to_s).to eq(Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE)
|
||||
expect(json_response['TempPath']).to eq(MetricImageUploader.workhorse_local_upload_path)
|
||||
expect(json_response['RemoteObject']).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST /projects/:id/alert_management_alerts/:alert_iid/metric_images' do
|
||||
include WorkhorseHelpers
|
||||
using RSpec::Parameterized::TableSyntax
|
||||
|
||||
include_context 'workhorse headers'
|
||||
|
||||
let(:file) { fixture_file_upload('spec/fixtures/rails_sample.jpg', 'image/jpg') }
|
||||
let(:file_name) { 'rails_sample.jpg' }
|
||||
let(:url) { 'http://gitlab.com' }
|
||||
let(:url_text) { 'GitLab' }
|
||||
|
||||
let(:params) { { url: url, url_text: url_text } }
|
||||
|
||||
subject do
|
||||
workhorse_finalize(
|
||||
api("/projects/#{project.id}/alert_management_alerts/#{alert.iid}/metric_images", user),
|
||||
method: :post,
|
||||
file_key: :file,
|
||||
params: params.merge(file: file),
|
||||
headers: workhorse_headers,
|
||||
send_rewritten_field: true
|
||||
)
|
||||
end
|
||||
|
||||
shared_examples 'can_upload_metric_image' do
|
||||
it 'creates a new metric image' do
|
||||
subject
|
||||
|
||||
expect(response).to have_gitlab_http_status(:created)
|
||||
expect(json_response['filename']).to eq(file_name)
|
||||
expect(json_response['url']).to eq(url)
|
||||
expect(json_response['url_text']).to eq(url_text)
|
||||
expect(json_response['created_at']).not_to be_nil
|
||||
expect(json_response['id']).not_to be_nil
|
||||
file_path_regex = %r{/uploads/-/system/alert_management_metric_image/file/\d+/#{file_name}}
|
||||
expect(json_response['file_path']).to match(file_path_regex)
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples 'unauthorized_upload' do
|
||||
it 'disallows the upload' do
|
||||
subject
|
||||
|
||||
expect(response).to have_gitlab_http_status(:forbidden)
|
||||
expect(json_response['message']).to eq('403 Forbidden')
|
||||
end
|
||||
end
|
||||
|
||||
where(:user_role, :expected_status) do
|
||||
:guest | :unauthorized_upload
|
||||
:reporter | :unauthorized_upload
|
||||
:developer | :can_upload_metric_image
|
||||
end
|
||||
|
||||
with_them do
|
||||
before do
|
||||
# Local storage
|
||||
stub_uploads_object_storage(MetricImageUploader, enabled: false)
|
||||
allow_next_instance_of(MetricImageUploader) do |uploader|
|
||||
allow(uploader).to receive(:file_storage?).and_return(true)
|
||||
end
|
||||
|
||||
project.send("add_#{user_role}", user)
|
||||
end
|
||||
|
||||
it_behaves_like "#{params[:expected_status]}"
|
||||
end
|
||||
|
||||
context 'file size too large' do
|
||||
before do
|
||||
allow_next_instance_of(UploadedFile) do |upload_file|
|
||||
allow(upload_file).to receive(:size).and_return(AlertManagement::MetricImage::MAX_FILE_SIZE + 1)
|
||||
end
|
||||
end
|
||||
|
||||
it 'returns an error' do
|
||||
subject
|
||||
|
||||
expect(response).to have_gitlab_http_status(:bad_request)
|
||||
expect(response.body).to match(/File is too large/)
|
||||
end
|
||||
end
|
||||
|
||||
context 'error when saving' do
|
||||
before do
|
||||
project.add_developer(user)
|
||||
|
||||
allow_next_instance_of(::AlertManagement::MetricImages::UploadService) do |service|
|
||||
error = instance_double(ServiceResponse, success?: false, message: 'some error', http_status: :bad_request)
|
||||
allow(service).to receive(:execute).and_return(error)
|
||||
end
|
||||
end
|
||||
|
||||
it 'returns an error' do
|
||||
subject
|
||||
|
||||
expect(response).to have_gitlab_http_status(:bad_request)
|
||||
expect(response.body).to match(/some error/)
|
||||
end
|
||||
end
|
||||
|
||||
context 'object storage enabled' do
|
||||
before do
|
||||
# Object storage
|
||||
stub_uploads_object_storage(MetricImageUploader)
|
||||
|
||||
allow_next_instance_of(MetricImageUploader) do |uploader|
|
||||
allow(uploader).to receive(:file_storage?).and_return(true)
|
||||
end
|
||||
project.add_developer(user)
|
||||
end
|
||||
|
||||
it_behaves_like 'can_upload_metric_image'
|
||||
|
||||
it 'uploads to remote storage' do
|
||||
subject
|
||||
|
||||
last_upload = AlertManagement::MetricImage.last.uploads.last
|
||||
expect(last_upload.store).to eq(::ObjectStorage::Store::REMOTE)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'GET /projects/:id/alert_management_alerts/:alert_iid/metric_images' do
|
||||
using RSpec::Parameterized::TableSyntax
|
||||
|
||||
let!(:image) { create(:alert_metric_image, alert: alert) }
|
||||
|
||||
subject { get api("/projects/#{project.id}/alert_management_alerts/#{alert.iid}/metric_images", user) }
|
||||
|
||||
shared_examples 'can_read_metric_image' do
|
||||
it 'can read the metric images' do
|
||||
subject
|
||||
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(json_response.first).to match(
|
||||
{
|
||||
id: image.id,
|
||||
created_at: image.created_at.strftime('%Y-%m-%dT%H:%M:%S.%LZ'),
|
||||
filename: image.filename,
|
||||
file_path: image.file_path,
|
||||
url: image.url,
|
||||
url_text: nil
|
||||
}.with_indifferent_access
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples 'unauthorized_read' do
|
||||
it 'cannot read the metric images' do
|
||||
subject
|
||||
|
||||
expect(response).to have_gitlab_http_status(:not_found)
|
||||
end
|
||||
end
|
||||
|
||||
where(:user_role, :public_project, :expected_status) do
|
||||
:not_member | false | :unauthorized_read
|
||||
:not_member | true | :unauthorized_read
|
||||
:guest | false | :unauthorized_read
|
||||
:reporter | false | :unauthorized_read
|
||||
:developer | false | :can_read_metric_image
|
||||
end
|
||||
|
||||
with_them do
|
||||
before do
|
||||
project.send("add_#{user_role}", user) unless user_role == :not_member
|
||||
project.update!(visibility_level: Gitlab::VisibilityLevel::PRIVATE) unless public_project
|
||||
end
|
||||
|
||||
it_behaves_like "#{params[:expected_status]}"
|
||||
end
|
||||
end
|
||||
|
||||
describe 'PUT /projects/:id/alert_management_alerts/:alert_iid/metric_images/:metric_image_id' do
|
||||
using RSpec::Parameterized::TableSyntax
|
||||
|
||||
let!(:image) { create(:alert_metric_image, alert: alert) }
|
||||
let(:params) { { url: 'http://test.example.com', url_text: 'Example website 123' } }
|
||||
|
||||
subject do
|
||||
put api("/projects/#{project.id}/alert_management_alerts/#{alert.iid}/metric_images/#{image.id}", user),
|
||||
params: params
|
||||
end
|
||||
|
||||
shared_examples 'can_update_metric_image' do
|
||||
it 'can update the metric images' do
|
||||
subject
|
||||
|
||||
expect(response).to have_gitlab_http_status(:success)
|
||||
expect(json_response['url']).to eq(params[:url])
|
||||
expect(json_response['url_text']).to eq(params[:url_text])
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples 'unauthorized_update' do
|
||||
it 'cannot update the metric image' do
|
||||
subject
|
||||
|
||||
expect(response).to have_gitlab_http_status(:forbidden)
|
||||
expect(image.reload).to eq(image)
|
||||
end
|
||||
end
|
||||
|
||||
where(:user_role, :public_project, :expected_status) do
|
||||
:not_member | false | :unauthorized_update
|
||||
:not_member | true | :unauthorized_update
|
||||
:guest | false | :unauthorized_update
|
||||
:reporter | false | :unauthorized_update
|
||||
:developer | false | :can_update_metric_image
|
||||
end
|
||||
|
||||
with_them do
|
||||
before do
|
||||
project.send("add_#{user_role}", user) unless user_role == :not_member
|
||||
project.update!(visibility_level: Gitlab::VisibilityLevel::PRIVATE) unless public_project
|
||||
end
|
||||
|
||||
it_behaves_like "#{params[:expected_status]}"
|
||||
end
|
||||
|
||||
context 'when user has access' do
|
||||
before do
|
||||
project.add_developer(user)
|
||||
end
|
||||
|
||||
context 'and metric image not found' do
|
||||
subject do
|
||||
put api("/projects/#{project.id}/alert_management_alerts/#{alert.iid}/metric_images/#{non_existing_record_id}", user) # rubocop: disable Layout/LineLength
|
||||
end
|
||||
|
||||
it 'returns an error' do
|
||||
subject
|
||||
|
||||
expect(response).to have_gitlab_http_status(:not_found)
|
||||
expect(json_response['message']).to eq('Metric image not found')
|
||||
end
|
||||
end
|
||||
|
||||
context 'metric image cannot be updated' do
|
||||
let(:params) { { url_text: 'something_long' * 100 } }
|
||||
|
||||
it 'returns an error' do
|
||||
subject
|
||||
|
||||
expect(response).to have_gitlab_http_status(:unprocessable_entity)
|
||||
expect(json_response['message']).to eq('Metric image could not be updated')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'DELETE /projects/:id/alert_management_alerts/:alert_iid/metric_images/:metric_image_id' do
|
||||
using RSpec::Parameterized::TableSyntax
|
||||
|
||||
let!(:image) { create(:alert_metric_image, alert: alert) }
|
||||
|
||||
subject do
|
||||
delete api("/projects/#{project.id}/alert_management_alerts/#{alert.iid}/metric_images/#{image.id}", user)
|
||||
end
|
||||
|
||||
shared_examples 'can delete metric image successfully' do
|
||||
it 'can delete the metric images' do
|
||||
subject
|
||||
|
||||
expect(response).to have_gitlab_http_status(:no_content)
|
||||
expect { image.reload }.to raise_error(ActiveRecord::RecordNotFound)
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples 'unauthorized delete' do
|
||||
it 'cannot delete the metric image' do
|
||||
subject
|
||||
|
||||
expect(response).to have_gitlab_http_status(:forbidden)
|
||||
expect(image.reload).to eq(image)
|
||||
end
|
||||
end
|
||||
|
||||
where(:user_role, :public_project, :expected_status) do
|
||||
:not_member | false | 'unauthorized delete'
|
||||
:not_member | true | 'unauthorized delete'
|
||||
:guest | false | 'unauthorized delete'
|
||||
:reporter | false | 'unauthorized delete'
|
||||
:developer | false | 'can delete metric image successfully'
|
||||
end
|
||||
|
||||
with_them do
|
||||
before do
|
||||
project.send("add_#{user_role}", user) unless user_role == :not_member
|
||||
project.update!(visibility_level: Gitlab::VisibilityLevel::PRIVATE) unless public_project
|
||||
end
|
||||
|
||||
it_behaves_like "#{params[:expected_status]}"
|
||||
end
|
||||
|
||||
context 'when user has access' do
|
||||
before do
|
||||
project.add_developer(user)
|
||||
end
|
||||
|
||||
context 'when metric image not found' do
|
||||
subject do
|
||||
delete api("/projects/#{project.id}/alert_management_alerts/#{alert.iid}/metric_images/#{non_existing_record_id}", user) # rubocop: disable Layout/LineLength
|
||||
end
|
||||
|
||||
it 'returns an error' do
|
||||
subject
|
||||
|
||||
expect(response).to have_gitlab_http_status(:not_found)
|
||||
expect(json_response['message']).to eq('Metric image not found')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when error when deleting' do
|
||||
before do
|
||||
allow_next_instance_of(AlertManagement::AlertsFinder) do |finder|
|
||||
allow(finder).to receive(:execute).and_return([alert])
|
||||
end
|
||||
|
||||
allow(alert).to receive_message_chain('metric_images.find_by_id') { image }
|
||||
allow(image).to receive(:destroy).and_return(false)
|
||||
end
|
||||
|
||||
it 'returns an error' do
|
||||
subject
|
||||
|
||||
expect(response).to have_gitlab_http_status(:unprocessable_entity)
|
||||
expect(json_response['message']).to eq('Metric image could not be deleted')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -47,7 +47,7 @@ RSpec.describe API::ProjectImport, :aggregate_failures do
|
|||
it 'executes a limited number of queries' do
|
||||
control_count = ActiveRecord::QueryRecorder.new { subject }.count
|
||||
|
||||
expect(control_count).to be <= 105
|
||||
expect(control_count).to be <= 108
|
||||
end
|
||||
|
||||
it 'schedules an import using a namespace' do
|
||||
|
|
|
@ -21,6 +21,17 @@ RSpec.describe 'Uploads', 'routing' do
|
|||
)
|
||||
end
|
||||
|
||||
it 'allows fetching alert metric metric images' do
|
||||
expect(get('/uploads/-/system/alert_management_metric_image/file/1/test.jpg')).to route_to(
|
||||
controller: 'uploads',
|
||||
action: 'show',
|
||||
model: 'alert_management_metric_image',
|
||||
id: '1',
|
||||
filename: 'test.jpg',
|
||||
mounted_as: 'file'
|
||||
)
|
||||
end
|
||||
|
||||
it 'does not allow creating uploads for other models' do
|
||||
unroutable_models = UploadsController::MODEL_CLASSES.keys.compact - %w(personal_snippet user)
|
||||
|
||||
|
|
|
@ -0,0 +1,79 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe AlertManagement::MetricImages::UploadService do
|
||||
subject(:service) { described_class.new(alert, current_user, params) }
|
||||
|
||||
let_it_be_with_refind(:project) { create(:project) }
|
||||
let_it_be_with_refind(:alert) { create(:alert_management_alert, project: project) }
|
||||
let_it_be_with_refind(:current_user) { create(:user) }
|
||||
|
||||
let(:params) do
|
||||
{
|
||||
file: fixture_file_upload('spec/fixtures/rails_sample.jpg', 'image/jpg'),
|
||||
url: 'https://www.gitlab.com'
|
||||
}
|
||||
end
|
||||
|
||||
describe '#execute' do
|
||||
subject { service.execute }
|
||||
|
||||
shared_examples 'uploads the metric' do
|
||||
it 'uploads the metric and returns a success' do
|
||||
expect { subject }.to change(AlertManagement::MetricImage, :count).by(1)
|
||||
expect(subject.success?).to eq(true)
|
||||
expect(subject.payload).to match({ metric: instance_of(AlertManagement::MetricImage), alert: alert })
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples 'no metric saved, an error given' do |message|
|
||||
it 'returns an error and does not upload', :aggregate_failures do
|
||||
expect(subject.success?).to eq(false)
|
||||
expect(subject.message).to match(a_string_matching(message))
|
||||
expect(AlertManagement::MetricImage.count).to eq(0)
|
||||
end
|
||||
end
|
||||
|
||||
context 'user does not have permissions' do
|
||||
it_behaves_like 'no metric saved, an error given', 'You are not authorized to upload metric images'
|
||||
end
|
||||
|
||||
context 'user has permissions' do
|
||||
before_all do
|
||||
project.add_developer(current_user)
|
||||
end
|
||||
|
||||
it_behaves_like 'uploads the metric'
|
||||
|
||||
context 'no url given' do
|
||||
let(:params) do
|
||||
{
|
||||
file: fixture_file_upload('spec/fixtures/rails_sample.jpg', 'image/jpg')
|
||||
}
|
||||
end
|
||||
|
||||
it_behaves_like 'uploads the metric'
|
||||
end
|
||||
|
||||
context 'record invalid' do
|
||||
let(:params) do
|
||||
{
|
||||
file: fixture_file_upload('spec/fixtures/doc_sample.txt', 'text/plain'),
|
||||
url: 'https://www.gitlab.com'
|
||||
}
|
||||
end
|
||||
|
||||
it_behaves_like 'no metric saved, an error given', /File does not have a supported extension. Only png, jpg, jpeg, gif, bmp, tiff, ico, and webp are supported/ # rubocop: disable Layout/LineLength
|
||||
end
|
||||
|
||||
context 'user is guest' do
|
||||
before_all do
|
||||
project.add_guest(current_user)
|
||||
end
|
||||
|
||||
it_behaves_like 'no metric saved, an error given', 'You are not authorized to upload metric images'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -286,6 +286,37 @@ RSpec.describe Deployments::UpdateEnvironmentService do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when environment url uses a nested variable' do
|
||||
let(:yaml_variables) do
|
||||
[
|
||||
{ key: 'MAIN_DOMAIN', value: '${STACK_NAME}.example.com' },
|
||||
{ key: 'STACK_NAME', value: 'appname-${ENVIRONMENT_NAME}' },
|
||||
{ key: 'ENVIRONMENT_NAME', value: '${CI_COMMIT_REF_SLUG}' }
|
||||
]
|
||||
end
|
||||
|
||||
let(:job) do
|
||||
create(:ci_build,
|
||||
:with_deployment,
|
||||
pipeline: pipeline,
|
||||
ref: 'master',
|
||||
environment: 'production',
|
||||
project: project,
|
||||
yaml_variables: yaml_variables,
|
||||
options: { environment: { name: 'production', url: 'http://$MAIN_DOMAIN' } })
|
||||
end
|
||||
|
||||
it { is_expected.to eq('http://appname-master.example.com') }
|
||||
|
||||
context 'when the FF ci_expand_environment_name_and_url is disabled' do
|
||||
before do
|
||||
stub_feature_flags(ci_expand_environment_name_and_url: false)
|
||||
end
|
||||
|
||||
it { is_expected.to eq('http://${STACK_NAME}.example.com') }
|
||||
end
|
||||
end
|
||||
|
||||
context 'when yaml environment does not have url' do
|
||||
let(:job) { create(:ci_build, :with_deployment, pipeline: pipeline, environment: 'staging', project: project) }
|
||||
|
||||
|
|
|
@ -135,10 +135,22 @@ RSpec.describe Projects::CreateService, '#execute' do
|
|||
create_project(user, opts)
|
||||
end
|
||||
|
||||
it 'builds associated project settings' do
|
||||
it 'creates associated project settings' do
|
||||
project = create_project(user, opts)
|
||||
|
||||
expect(project.project_setting).to be_new_record
|
||||
expect(project.project_setting).to be_persisted
|
||||
end
|
||||
|
||||
context 'create_project_settings feature flag is disabled' do
|
||||
before do
|
||||
stub_feature_flags(create_project_settings: false)
|
||||
end
|
||||
|
||||
it 'builds associated project settings' do
|
||||
project = create_project(user, opts)
|
||||
|
||||
expect(project.project_setting).to be_new_record
|
||||
end
|
||||
end
|
||||
|
||||
it_behaves_like 'storing arguments in the application context' do
|
||||
|
|
Loading…
Reference in a new issue