diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-02-20 21:12:16 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-02-20 21:12:16 +0300 |
commit | 72c050db64bd61ae767f47c4fa2bc97b94a67592 (patch) | |
tree | 6e3b239ffcc70d9ca88bf3144d8459a86bed7c38 | |
parent | 1d033298a5973bdbd9c56d4eb0f1d29265b153a6 (diff) |
Add latest changes from gitlab-org/gitlab@master
64 files changed, 627 insertions, 918 deletions
diff --git a/app/assets/javascripts/airflow/dags/components/dags.vue b/app/assets/javascripts/airflow/dags/components/dags.vue deleted file mode 100644 index 88eb3fd5aba..00000000000 --- a/app/assets/javascripts/airflow/dags/components/dags.vue +++ /dev/null @@ -1,111 +0,0 @@ -<script> -import { GlTableLite, GlEmptyState, GlPagination, GlTooltipDirective } from '@gitlab/ui'; -import { s__ } from '~/locale'; -import { setUrlParams } from '~/lib/utils/url_utility'; -import { formatDate } from '~/lib/utils/datetime/date_format_utility'; -import TimeAgo from '~/vue_shared/components/time_ago_tooltip.vue'; -import IncubationAlert from '~/vue_shared/components/incubation/incubation_alert.vue'; - -export default { - name: 'AirflowDags', - components: { - GlTableLite, - GlEmptyState, - IncubationAlert, - GlPagination, - TimeAgo, - }, - directives: { - GlTooltip: GlTooltipDirective, - }, - props: { - dags: { - type: Array, - required: true, - }, - pagination: { - type: Object, - required: true, - }, - }, - computed: { - fields() { - return [ - { key: 'dag_name', label: this.$options.i18n.dagLabel }, - { key: 'schedule', label: this.$options.scheduleLabel }, - { key: 'next_run', label: this.$options.nextRunLabel }, - { key: 'is_active', label: this.$options.isActiveLabel }, - { key: 'is_paused', label: this.$options.isPausedLabel }, - { key: 'fileloc', label: this.$options.fileLocLabel }, - ]; - }, - hasPagination() { - return this.dags.length > 0; - }, - prevPage() { - return this.pagination.page > 1 ? this.pagination.page - 1 : null; - }, - nextPage() { - return !this.pagination.isLastPage ? this.pagination.page + 1 : null; - }, - emptyState() { - return { - svgPath: '/assets/illustrations/empty-state/empty-dag-md.svg', - }; - }, - }, - methods: { - generateLink(page) { - return setUrlParams({ page }); - }, - formatDate(dateString) { - return formatDate(new Date(dateString)); - }, - }, - i18n: { - emptyStateLabel: s__('Airflow|There are no DAGs to show'), - emptyStateDescription: s__( - 'Airflow|Either the Airflow instance does not contain DAGs or has yet to be configured', - ), - dagLabel: s__('Airflow|DAG'), - scheduleLabel: s__('Airflow|Schedule'), - nextRunLabel: s__('Airflow|Next run'), - isActiveLabel: s__('Airflow|Is active'), - isPausedLabel: s__('Airflow|Is paused'), - fileLocLabel: s__('Airflow|DAG file location'), - featureName: s__('Airflow|GitLab Airflow integration'), - }, - linkToFeedbackIssue: - 'https://gitlab.com/gitlab-org/incubation-engineering/airflow/meta/-/issues/2', -}; -</script> - -<template> - <div> - <incubation-alert - :feature-name="$options.i18n.featureName" - :link-to-feedback-issue="$options.linkToFeedbackIssue" - /> - <gl-empty-state - v-if="!dags.length" - :title="$options.i18n.emptyStateLabel" - :description="$options.i18n.emptyStateDescription" - :svg-path="emptyState.svgPath" - /> - <gl-table-lite v-else :items="dags" :fields="fields" class="gl-mt-0!"> - <template #cell(next_run)="data"> - <time-ago v-gl-tooltip.hover :time="data.value" :title="formatDate(data.value)" /> - </template> - </gl-table-lite> - <gl-pagination - v-if="hasPagination" - :value="pagination.page" - :prev-page="prevPage" - :next-page="nextPage" - :total-items="pagination.totalItems" - :per-page="pagination.perPage" - :link-gen="generateLink" - align="center" - /> - </div> -</template> diff --git a/app/assets/javascripts/contextual_sidebar.js b/app/assets/javascripts/contextual_sidebar.js index f2ff77daf02..ea444b5c146 100644 --- a/app/assets/javascripts/contextual_sidebar.js +++ b/app/assets/javascripts/contextual_sidebar.js @@ -38,9 +38,6 @@ export default class ContextualSidebar { this.toggleCollapsedSidebar(value, true); } }); - this.$page.on('transitionstart transitionend', () => { - $(document).trigger('content.resize'); - }); $(window).on( 'resize', diff --git a/app/assets/javascripts/contributors/components/contributors.vue b/app/assets/javascripts/contributors/components/contributors.vue index 17e6cc87ff8..ce99d5da3cc 100644 --- a/app/assets/javascripts/contributors/components/contributors.vue +++ b/app/assets/javascripts/contributors/components/contributors.vue @@ -9,7 +9,6 @@ import { getSvgIconPathContent } from '~/lib/utils/icon_utils'; import { __ } from '~/locale'; import RefSelector from '~/ref/components/ref_selector.vue'; import { REF_TYPE_BRANCHES, REF_TYPE_TAGS } from '~/ref/constants'; -import ResizableChartContainer from '~/vue_shared/components/resizable_chart/resizable_chart_container.vue'; import { xAxisLabelFormatter, dateFormatter } from '../utils'; const GRAPHS_PATH_REGEX = /^(.*?)\/-\/graphs/g; @@ -26,7 +25,6 @@ export default { GlAreaChart, GlButton, GlLoadingIcon, - ResizableChartContainer, RefSelector, }, props: { @@ -249,18 +247,15 @@ export default { <div data-testid="contributors-charts"> <h4 class="gl-mb-2 gl-mt-5">{{ __('Commits to') }} {{ branch }}</h4> <span>{{ __('Excluding merge commits. Limited to 6,000 commits.') }}</span> - <resizable-chart-container> - <template #default="{ width }"> - <gl-area-chart - class="gl-mb-5" - :width="width" - :data="masterChartData" - :option="masterChartOptions" - :height="masterChartHeight" - @created="onMasterChartCreated" - /> - </template> - </resizable-chart-container> + <gl-area-chart + class="gl-mb-5" + responsive + width="auto" + :data="masterChartData" + :option="masterChartOptions" + :height="masterChartHeight" + @created="onMasterChartCreated" + /> <div class="row"> <div @@ -272,17 +267,14 @@ export default { <p class="gl-mb-3"> {{ n__('%d commit', '%d commits', contributor.commits) }} ({{ contributor.email }}) </p> - <resizable-chart-container> - <template #default="{ width }"> - <gl-area-chart - :width="width" - :data="contributor.dates" - :option="individualChartOptions" - :height="individualChartHeight" - @created="onIndividualChartCreated" - /> - </template> - </resizable-chart-container> + <gl-area-chart + responsive + width="auto" + :data="contributor.dates" + :option="individualChartOptions" + :height="individualChartHeight" + @created="onIndividualChartCreated" + /> </div> </div> </div> diff --git a/app/assets/javascripts/pages/projects/airflow/dags/index/index.js b/app/assets/javascripts/pages/projects/airflow/dags/index/index.js deleted file mode 100644 index 1d7cf4a5b8e..00000000000 --- a/app/assets/javascripts/pages/projects/airflow/dags/index/index.js +++ /dev/null @@ -1,27 +0,0 @@ -import Vue from 'vue'; -import AirflowDags from '~/airflow/dags/components/dags.vue'; -import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils'; - -const initShowDags = () => { - const element = document.querySelector('#js-show-airflow-dags'); - if (!element) { - return null; - } - - const dags = JSON.parse(element.dataset.dags); - const pagination = convertObjectPropsToCamelCase(JSON.parse(element.dataset.pagination)); - - return new Vue({ - el: element, - render(h) { - return h(AirflowDags, { - props: { - dags, - pagination, - }, - }); - }, - }); -}; - -initShowDags(); diff --git a/app/assets/javascripts/profile/account/components/update_username.vue b/app/assets/javascripts/profile/account/components/update_username.vue index 51e62984715..afe1714db11 100644 --- a/app/assets/javascripts/profile/account/components/update_username.vue +++ b/app/assets/javascripts/profile/account/components/update_username.vue @@ -117,6 +117,7 @@ Please update your Git repository remotes as soon as possible.`), <input :id="$options.inputId" v-model="newUsername" + data-testid="new-username-input" :disabled="isRequestPending" class="form-control" required="required" diff --git a/app/assets/javascripts/vue_shared/components/ci_cd_analytics/ci_cd_analytics_area_chart.vue b/app/assets/javascripts/vue_shared/components/ci_cd_analytics/ci_cd_analytics_area_chart.vue index fb7105bd416..c89e843b660 100644 --- a/app/assets/javascripts/vue_shared/components/ci_cd_analytics/ci_cd_analytics_area_chart.vue +++ b/app/assets/javascripts/vue_shared/components/ci_cd_analytics/ci_cd_analytics_area_chart.vue @@ -1,13 +1,11 @@ <script> import { GlAreaChart } from '@gitlab/ui/dist/charts'; -import ResizableChartContainer from '~/vue_shared/components/resizable_chart/resizable_chart_container.vue'; import { CHART_CONTAINER_HEIGHT } from './constants'; export default { name: 'CiCdAnalyticsAreaChart', components: { GlAreaChart, - ResizableChartContainer, }, props: { chartData: { @@ -27,24 +25,21 @@ export default { <p> <slot></slot> </p> - <resizable-chart-container> - <template #default="{ width }"> - <gl-area-chart - v-bind="$attrs" - :width="width" - :height="$options.chartContainerHeight" - :data="chartData" - :include-legend-avg-max="false" - :option="areaChartOptions" - > - <template #tooltip-title> - <slot name="tooltip-title"></slot> - </template> - <template #tooltip-content> - <slot name="tooltip-content"></slot> - </template> - </gl-area-chart> + <gl-area-chart + v-bind="$attrs" + responsive + width="auto" + :height="$options.chartContainerHeight" + :data="chartData" + :include-legend-avg-max="false" + :option="areaChartOptions" + > + <template #tooltip-title> + <slot name="tooltip-title"></slot> </template> - </resizable-chart-container> + <template #tooltip-content> + <slot name="tooltip-content"></slot> + </template> + </gl-area-chart> </div> </template> diff --git a/app/assets/javascripts/vue_shared/components/resizable_chart/resizable_chart_container.vue b/app/assets/javascripts/vue_shared/components/resizable_chart/resizable_chart_container.vue deleted file mode 100644 index 02cb7785ef4..00000000000 --- a/app/assets/javascripts/vue_shared/components/resizable_chart/resizable_chart_container.vue +++ /dev/null @@ -1,40 +0,0 @@ -<script> -import $ from 'jquery'; -import { debounceByAnimationFrame } from '~/lib/utils/common_utils'; - -export default { - data() { - return { - width: 0, - height: 0, - }; - }, - beforeDestroy() { - this.contentResizeHandler.off('content.resize', this.debouncedResize); - window.removeEventListener('resize', this.debouncedResize); - }, - created() { - this.debouncedResize = debounceByAnimationFrame(this.onResize); - - // Handle when we explicictly trigger a custom resize event - this.contentResizeHandler = $(document).on('content.resize', this.debouncedResize); - - // Handle window resize - window.addEventListener('resize', this.debouncedResize); - }, - methods: { - onResize() { - // Slot dimensions - const { clientWidth, clientHeight } = this.$refs.chartWrapper; - this.width = clientWidth; - this.height = clientHeight; - }, - }, -}; -</script> - -<template> - <div ref="chartWrapper"> - <slot :width="width" :height="height"> </slot> - </div> -</template> diff --git a/app/assets/stylesheets/page_bundles/merge_requests.scss b/app/assets/stylesheets/page_bundles/merge_requests.scss index 4caaeeb647e..704ff5d4ee2 100644 --- a/app/assets/stylesheets/page_bundles/merge_requests.scss +++ b/app/assets/stylesheets/page_bundles/merge_requests.scss @@ -912,7 +912,7 @@ $tabs-holder-z-index: 250; &:not(:last-child)::before { content: ''; - border-left: 2px solid var(--gray-10, $gray-10); + border-left: 2px solid var(--border-color, $border-color); position: absolute; bottom: -17px; left: calc(1rem - 1px); diff --git a/app/controllers/projects/airflow/dags_controller.rb b/app/controllers/projects/airflow/dags_controller.rb deleted file mode 100644 index 9d1f0b0d63b..00000000000 --- a/app/controllers/projects/airflow/dags_controller.rb +++ /dev/null @@ -1,38 +0,0 @@ -# frozen_string_literal: true - -module Projects - module Airflow - class DagsController < ::Projects::ApplicationController - before_action :check_feature_flag - before_action :authorize_read_airflow_dags! - - feature_category :dataops - - MAX_DAGS_PER_PAGE = 15 - def index - page = params[:page].to_i - page = 1 if page <= 0 - - @dags = ::Airflow::Dags.by_project_id(@project.id) - - return unless @dags.any? - - @dags = @dags.page(page).per(MAX_DAGS_PER_PAGE) - return redirect_to(url_for(page: @dags.total_pages)) if @dags.out_of_range? - - @pagination = { - page: page, - is_last_page: @dags.last_page?, - per_page: MAX_DAGS_PER_PAGE, - total_items: @dags.total_count - } - end - - private - - def check_feature_flag - render_404 unless Feature.enabled?(:airflow_dags, @project) - end - end - end -end diff --git a/app/models/airflow.rb b/app/models/airflow.rb deleted file mode 100644 index 2e5642a2639..00000000000 --- a/app/models/airflow.rb +++ /dev/null @@ -1,6 +0,0 @@ -# frozen_string_literal: true -module Airflow - def self.table_name_prefix - 'airflow_' - end -end diff --git a/app/models/airflow/dags.rb b/app/models/airflow/dags.rb deleted file mode 100644 index d17d4a4f3db..00000000000 --- a/app/models/airflow/dags.rb +++ /dev/null @@ -1,14 +0,0 @@ -# frozen_string_literal: true - -module Airflow - class Dags < ApplicationRecord - belongs_to :project - - validates :project, presence: true - validates :dag_name, length: { maximum: 255 }, presence: true - validates :schedule, length: { maximum: 255 } - validates :fileloc, length: { maximum: 255 } - - scope :by_project_id, ->(project_id) { where(project_id: project_id).order(id: :asc) } - end -end diff --git a/app/models/ci/build_metadata.rb b/app/models/ci/build_metadata.rb index 4c723bb7c0c..4b2be446fe3 100644 --- a/app/models/ci/build_metadata.rb +++ b/app/models/ci/build_metadata.rb @@ -10,12 +10,15 @@ module Ci include Presentable include ChronicDurationAttribute include Gitlab::Utils::StrongMemoize + include IgnorableColumns self.table_name = 'p_ci_builds_metadata' self.primary_key = 'id' partitionable scope: :build + ignore_column :runner_machine_id, remove_with: '16.0', remove_after: '2023-04-22' + belongs_to :build, class_name: 'CommitStatus' belongs_to :project diff --git a/app/models/project.rb b/app/models/project.rb index 1909ed8dfdd..b1bb0ff2bbb 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -1314,6 +1314,11 @@ class Project < ApplicationRecord super(value&.delete("\0")) end + # Used by Import/Export to export commit notes + def commit_notes + notes.where(noteable_type: "Commit") + end + def import_url=(value) if Gitlab::UrlSanitizer.valid?(value) import_url = Gitlab::UrlSanitizer.new(value) diff --git a/app/policies/project_policy.rb b/app/policies/project_policy.rb index 875520d24be..2cbe9ad83cc 100644 --- a/app/policies/project_policy.rb +++ b/app/policies/project_policy.rb @@ -477,7 +477,6 @@ class ProjectPolicy < BasePolicy enable :update_escalation_status enable :read_secure_files enable :update_sentry_issue - enable :read_airflow_dags end rule { can?(:developer_access) & user_confirmed? }.policy do diff --git a/app/views/projects/airflow/dags/index.html.haml b/app/views/projects/airflow/dags/index.html.haml deleted file mode 100644 index d631d084db1..00000000000 --- a/app/views/projects/airflow/dags/index.html.haml +++ /dev/null @@ -1,11 +0,0 @@ -- breadcrumb_title s_('Airflow|Airflow DAGs') -- page_title s_('Airflow|Airflow DAGs') - -.page-title-holder - %h1.page-title.gl-font-size-h-display= s_('Airflow|Airflow DAGs') - -#js-show-airflow-dags{ data: { - dags: @dags.to_json, - pagination: @pagination.to_json - } -} diff --git a/config/feature_flags/development/airflow_dags.yml b/config/feature_flags/development/airflow_dags.yml deleted file mode 100644 index e32035e7d2a..00000000000 --- a/config/feature_flags/development/airflow_dags.yml +++ /dev/null @@ -1,8 +0,0 @@ ---- -name: airflow_dags -introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/108900 -rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/387529 -milestone: '15.9' -type: development -group: group::incubation -default_enabled: false diff --git a/config/metrics/counts_28d/20210216180319_action_monthly_active_users_web_ide_edit.yml b/config/metrics/counts_28d/20210216180319_action_monthly_active_users_web_ide_edit.yml index dd66f4a3b47..9d56815712f 100644 --- a/config/metrics/counts_28d/20210216180319_action_monthly_active_users_web_ide_edit.yml +++ b/config/metrics/counts_28d/20210216180319_action_monthly_active_users_web_ide_edit.yml @@ -7,7 +7,9 @@ product_stage: create product_group: editor product_category: web_ide value_type: number -status: active +status: removed +removed_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/111351 +milestone_removed: 15.9 time_frame: 28d data_source: redis_hll distribution: diff --git a/config/routes/project.rb b/config/routes/project.rb index cf19111b2e2..ceb0671c034 100644 --- a/config/routes/project.rb +++ b/config/routes/project.rb @@ -473,10 +473,6 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do resources :experiments, only: [:index, :show], controller: 'experiments' resources :candidates, only: [:show], controller: 'candidates', param: :iid end - - namespace :airflow do - resources :dags, only: [:index, :show], controller: 'dags' - end end # End of the /-/ scope. diff --git a/db/docs/airflow_dags.yml b/db/docs/deleted_tables/airflow_dags.yml index 6ed6c9753a8..4942e71d3bf 100644 --- a/db/docs/airflow_dags.yml +++ b/db/docs/deleted_tables/airflow_dags.yml @@ -8,3 +8,5 @@ description: An Airflow DAG is a record of the current status of a DAG from an A introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/108900 milestone: '15.9' gitlab_schema: gitlab_main +removed_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/112192 +removed_in_milestone: '15.10' diff --git a/db/migrate/20230215124011_add_provisioned_by_group_at_to_user_details.rb b/db/migrate/20230215124011_add_provisioned_by_group_at_to_user_details.rb new file mode 100644 index 00000000000..3ff1cf6c6d4 --- /dev/null +++ b/db/migrate/20230215124011_add_provisioned_by_group_at_to_user_details.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +class AddProvisionedByGroupAtToUserDetails < Gitlab::Database::Migration[2.1] + enable_lock_retries! + + def change + add_column :user_details, :provisioned_by_group_at, :datetime_with_timezone + end +end diff --git a/db/migrate/20230216144719_drop_table_airflow_dags.rb b/db/migrate/20230216144719_drop_table_airflow_dags.rb new file mode 100644 index 00000000000..ed80ca16703 --- /dev/null +++ b/db/migrate/20230216144719_drop_table_airflow_dags.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +class DropTableAirflowDags < Gitlab::Database::Migration[2.1] + def up + # the table is not in use + drop_table :airflow_dags, if_exists: true # rubocop: disable Migration/DropTable + end + + def down + create_table :airflow_dags do |t| + t.references :project, index: true, foreign_key: { on_delete: :cascade }, null: false + t.timestamps_with_timezone null: false + t.datetime_with_timezone :next_run + t.boolean :has_import_errors + t.boolean :is_active + t.boolean :is_paused + t.text :dag_name, null: false, limit: 255 + t.text :schedule, limit: 255 + t.text :fileloc, limit: 255 + end + end +end diff --git a/db/schema_migrations/20230215124011 b/db/schema_migrations/20230215124011 new file mode 100644 index 00000000000..88911b9faaf --- /dev/null +++ b/db/schema_migrations/20230215124011 @@ -0,0 +1 @@ +e8449ee3c54eb3ec1c4f2e302e7fceda950a16eb2bf57b0cd77ed955ad314eee
\ No newline at end of file diff --git a/db/schema_migrations/20230216144719 b/db/schema_migrations/20230216144719 new file mode 100644 index 00000000000..01ad731d72f --- /dev/null +++ b/db/schema_migrations/20230216144719 @@ -0,0 +1 @@ +cc74cddc9851a56cc98df1947ea1ea539358e9959b5c8f79aa1cea44979760b2
\ No newline at end of file diff --git a/db/structure.sql b/db/structure.sql index 83e66e11098..fd115fd89c9 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -10827,32 +10827,6 @@ CREATE SEQUENCE agent_project_authorizations_id_seq ALTER SEQUENCE agent_project_authorizations_id_seq OWNED BY agent_project_authorizations.id; -CREATE TABLE airflow_dags ( - id bigint NOT NULL, - project_id bigint NOT NULL, - created_at timestamp with time zone NOT NULL, - updated_at timestamp with time zone NOT NULL, - next_run timestamp with time zone, - has_import_errors boolean, - is_active boolean, - is_paused boolean, - dag_name text NOT NULL, - schedule text, - fileloc text, - CONSTRAINT check_6999a61016 CHECK ((char_length(schedule) <= 255)), - CONSTRAINT check_6f52bee3a1 CHECK ((char_length(fileloc) <= 255)), - CONSTRAINT check_e10ac15d52 CHECK ((char_length(dag_name) <= 255)) -); - -CREATE SEQUENCE airflow_dags_id_seq - START WITH 1 - INCREMENT BY 1 - NO MINVALUE - NO MAXVALUE - CACHE 1; - -ALTER SEQUENCE airflow_dags_id_seq OWNED BY airflow_dags.id; - CREATE TABLE alert_management_alert_assignees ( id bigint NOT NULL, user_id bigint NOT NULL, @@ -22955,6 +22929,7 @@ CREATE TABLE user_details ( password_last_changed_at timestamp with time zone DEFAULT now() NOT NULL, onboarding_step_url text, discord text DEFAULT ''::text NOT NULL, + provisioned_by_group_at timestamp with time zone, CONSTRAINT check_245664af82 CHECK ((char_length(webauthn_xid) <= 100)), CONSTRAINT check_444573ee52 CHECK ((char_length(skype) <= 500)), CONSTRAINT check_466a25be35 CHECK ((char_length(twitter) <= 500)), @@ -24234,8 +24209,6 @@ ALTER TABLE ONLY agent_group_authorizations ALTER COLUMN id SET DEFAULT nextval( ALTER TABLE ONLY agent_project_authorizations ALTER COLUMN id SET DEFAULT nextval('agent_project_authorizations_id_seq'::regclass); -ALTER TABLE ONLY airflow_dags ALTER COLUMN id SET DEFAULT nextval('airflow_dags_id_seq'::regclass); - ALTER TABLE ONLY alert_management_alert_assignees ALTER COLUMN id SET DEFAULT nextval('alert_management_alert_assignees_id_seq'::regclass); ALTER TABLE ONLY alert_management_alert_metric_images ALTER COLUMN id SET DEFAULT nextval('alert_management_alert_metric_images_id_seq'::regclass); @@ -25915,9 +25888,6 @@ ALTER TABLE ONLY agent_group_authorizations ALTER TABLE ONLY agent_project_authorizations ADD CONSTRAINT agent_project_authorizations_pkey PRIMARY KEY (id); -ALTER TABLE ONLY airflow_dags - ADD CONSTRAINT airflow_dags_pkey PRIMARY KEY (id); - ALTER TABLE ONLY alert_management_alert_assignees ADD CONSTRAINT alert_management_alert_assignees_pkey PRIMARY KEY (id); @@ -29019,8 +28989,6 @@ CREATE UNIQUE INDEX index_agent_project_authorizations_on_agent_id_and_project_i CREATE INDEX index_agent_project_authorizations_on_project_id ON agent_project_authorizations USING btree (project_id); -CREATE INDEX index_airflow_dags_on_project_id ON airflow_dags USING btree (project_id); - CREATE INDEX index_alert_assignees_on_alert_id ON alert_management_alert_assignees USING btree (alert_id); CREATE UNIQUE INDEX index_alert_assignees_on_user_id_and_alert_id ON alert_management_alert_assignees USING btree (user_id, alert_id); @@ -36185,9 +36153,6 @@ ALTER TABLE ONLY gpg_signatures ALTER TABLE ONLY board_group_recent_visits ADD CONSTRAINT fk_rails_ca04c38720 FOREIGN KEY (board_id) REFERENCES boards(id) ON DELETE CASCADE; -ALTER TABLE ONLY airflow_dags - ADD CONSTRAINT fk_rails_ca3ac0d68c FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE; - ALTER TABLE ONLY boards_epic_board_positions ADD CONSTRAINT fk_rails_cb4563dd6e FOREIGN KEY (epic_board_id) REFERENCES boards_epic_boards(id) ON DELETE CASCADE; diff --git a/doc/architecture/blueprints/runner_tokens/index.md b/doc/architecture/blueprints/runner_tokens/index.md index 3b99f8e42c8..fefebf71f3a 100644 --- a/doc/architecture/blueprints/runner_tokens/index.md +++ b/doc/architecture/blueprints/runner_tokens/index.md @@ -373,10 +373,10 @@ scope. | GitLab Rails app | `%15.8` | Create database migration to add `config` column to `ci_runner_machines` table. | | GitLab Runner | `%15.9` | Start sending `system_id` value in `POST /jobs/request` request and other follow-up requests that require identifying the unique system. | | GitLab Rails app | `%15.9` | Create service similar to `StaleGroupRunnersPruneCronWorker` service to clean up `ci_runner_machines` records instead of `ci_runners` records.<br/>Existing service continues to exist but focuses only on legacy runners. | -| GitLab Rails app | `%15.9` | [Feature flag] Rollout of `create_runner_machine`. | +| GitLab Rails app | `%15.9` | Enabled `create_runner_machine` [with a flag](../../../administration/feature_flags.md) named `flag_name`. | | GitLab Rails app | `%15.9` | Create `ci_runner_machines` record in `POST /runners/verify` request if the runner token is prefixed with `glrt-`. | | GitLab Rails app | `%15.9` | Use runner token + `system_id` JSON parameters in `POST /jobs/request` request in the [heartbeat request](https://gitlab.com/gitlab-org/gitlab/blob/c73c96a8ffd515295842d72a3635a8ae873d688c/lib/api/ci/helpers/runner.rb#L14-20) to update the `ci_runner_machines` cache/table. | -| GitLab Rails app | `%15.9` | [Feature flag] Enable runner creation workflow (`create_runner_workflow`). | +| GitLab Rails app | `%15.9` | Enable runner creation workflow (`create_runner_workflow`) [with a flag](../../../administration/feature_flags.md) named `flag_name`. | | GitLab Rails app | `%15.9` | Implement `create_{instance|group|project}_runner` permissions. | | GitLab Rails app | `%15.9` | Replace `ci_builds_metadata.runner_machine_id` with a new join table. | | GitLab Rails app | `%15.9` | Rename `ci_runner_machines.machine_xid` column to `system_xid` to be consistent with `system_id` passed in APIs. | diff --git a/doc/operations/error_tracking.md b/doc/operations/error_tracking.md index 0be2f087c62..cb46b08c279 100644 --- a/doc/operations/error_tracking.md +++ b/doc/operations/error_tracking.md @@ -34,15 +34,15 @@ For error tracking to work, you need two pieces: ## Sentry error tracking -[Sentry](https://sentry.io/) is an open source error tracking system. GitLab allows administrators to connect Sentry to GitLab, to allow users to view a list of Sentry errors in GitLab. +[Sentry](https://sentry.io/) is an open source error tracking system. GitLab allows administrators to connect Sentry to GitLab so users can view a list of Sentry errors in GitLab. ### Deploying Sentry -You can sign up to the cloud hosted [Sentry](https://sentry.io) or deploy your own [on-premise instance](https://github.com/getsentry/onpremise/). +You can sign up to the cloud-hosted [Sentry](https://sentry.io) or deploy your own [on-premise instance](https://github.com/getsentry/onpremise/). ### Enabling Sentry -GitLab provides an easy way to connect Sentry to your project. You need at +GitLab provides a way to connect Sentry to your project. You need at least Maintainer [permissions](../user/permissions.md) to enable the Sentry integration. 1. Sign up to Sentry.io or [deploy your own](#deploying-sentry) Sentry instance. @@ -94,9 +94,9 @@ Here, you can filter errors by title or by status (one of Ignored , Resolved, or ## Error Details -From error list, users can navigate to the error details page by selecting the title of any error. +From error list, users can go to the error details page by selecting the title of any error. -This page has: +This page includes: - A link to the Sentry issue. - A link to the GitLab commit if the Sentry [release ID/version](https://docs.sentry.io/product/releases/?platform=javascript#configure-sdk) on the Sentry Issue's first release matches a commit SHA in your GitLab hosted project. @@ -108,26 +108,26 @@ By default, a **Create issue** button is displayed: ![Error Details without Issue Link](img/error_details_v12_7.png) If you create a GitLab issue from the error, the **Create issue** button changes to a **View issue** -button and a link to the GitLab issue displays within the error detail section. +button and a link to the GitLab issue displays in the error detail section. ## Taking Action on errors -You can take action on Sentry Errors from within the GitLab UI. Marking errors ignored or resolved require at least Developer role. +You can take action on Sentry Errors in the GitLab UI. Marking errors as ignored or resolved requires at least Developer role. ### Ignoring errors > [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/39665) in GitLab 12.7. -From within the [Error Details](#error-details) page you can ignore a Sentry error by selecting the **Ignore** button near the top of the page. +In the [Error Details](#error-details) page you can ignore a Sentry error by selecting **Ignore** near the top of the page. -Ignoring an error prevents it from appearing in the [Error Tracking List](#error-tracking-list), and silences notifications that were set up within Sentry. +Ignoring an error prevents it from appearing in the [Error Tracking List](#error-tracking-list), and silences notifications that were set up in Sentry. ### Resolving errors > [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/39825) in GitLab 12.7. -From within the [Error Details](#error-details) page you can resolve a Sentry error by -selecting the **Resolve** button near the top of the page. +In the [Error Details](#error-details) page you can resolve a Sentry error by +selecting **Resolve** near the top of the page. Marking an error as resolved indicates that the error has stopped firing events. If a GitLab issue is linked to the error, then the issue closes. @@ -142,51 +142,231 @@ If another event occurs, the error reverts to unresolved. NOTE: Available only on GitLab.com. This feature is currently in [Open Beta](https://about.gitlab.com/handbook/product/gitlab-the-product/#open-beta). -Integrated error tracking is a lightweight alternative to Sentry backend. -You still use Sentry SDK with your application. But you don't need to deploy Sentry -or set up for cloud-hosted Sentry. Instead, you use GitLab as a backend for it. +### Known limitations -Sentry backend automatically assigns a Data Source Name (DSN) for every project you create. -GitLab does the same. You should be able to find a DSN for your project in the GitLab error tracking -settings. By using a GitLab-provided DSN, your application connects to GitLab to report an error. -Those errors are stored in the GitLab database and rendered by the GitLab UI, in the same way as -Sentry integration. +Only basic support is provided with `capture_exception` as the holding method. +Additional features requests (see this [issue](https://gitlab.com/gitlab-org/gitlab/-/issues/340178)) will be added on a case-by-case basis. -In GitLab 15.6 and later, the integrated error tracking -uses a new backend based on the ClickHouse database that enables better scalability. -Integrated error tracking remains limited in comparison to the Sentry backend, as only a small subset of the -Sentry API is implemented. +### Debugging issues -Changing the GitLab error UI to use the GitLab Observability UI is tracked in epic [19](https://gitlab.com/groups/gitlab-org/opstrace/-/epics/32). +The majority of languages are supported by Sentry expose `debug` option as part of initialization. +It is also possible to output JSON before it is sent to the API. +See the [Go example](#go) below for a suggested solution. -### Project settings +### Enabling error tracking -You can find the feature configuration at **Settings > Monitor > Error Tracking**. +Regardless of the programming language, you need to enable error tracking for your project. This doc assumes you already have a project for which you want to enable error tracking. +This example uses the `gitlab.com` instance. -#### How to enable +To enable error tracking, follow these steps: -1. Select **GitLab** as the error tracking backend for your project: +1. In your project, go to **Settings >Monitor**. Expand the **Error Tracking** tab: - ![Error Tracking Settings](img/error_tracking_setting_v14_3.png) + ![MonitorTabPreEnable](img/Monitor_tab-pre-enable.png) -1. Select **Save changes**. After page reload you should see a text field with the DSN string. Copy it. +1. Enable Error Tracking with GitLab as backend: - ![Error Tracking Settings DSN](img/error_tracking_setting_dsn_v14_4.png) + ![MonitorTabPostEnable](img/Monitor_tab-post-enable.png) -1. Take the DSN from the previous step and configure your Sentry SDK with it. Errors are now - reported to the GitLab collector and are visible in the [GitLab UI](#error-tracking-list). +1. Select the `Save Changes` button. -#### Managing DSN +1. Copy the DSN string. You will need this later. -When you enable the feature you receive a DSN. It includes a hash used for authentication. This hash -is a client key. GitLab uses client keys to authenticate error tracking requests from your -application to the GitLab backend. +### Listing captured errors -In some situations, you may want to create a new client key and remove an existing one. -You can do so by managing client keys with the [error tracking API](../api/error_tracking.md). +Once your application has emitted errors to the Error Tracking API through the Sentry SDK, they should be available under **Monitor > Error Tracking** tab/section. -#### Limitations +![MonitorListErrors](img/Monitor-list_errors.png) -The Integrated Error Tracking feature was built and tested with Sentry SDK for Ruby on Rails. -Support for other languages and frameworks is not guaranteed. For up-to-date information, see the -[compatibility issue](https://gitlab.com/gitlab-org/gitlab/-/issues/340178). +For more details, refer to the [GitLab error tracking documentation](https://gitlab.com/help/operations/error_tracking#error-tracking-list). + +This process assumes the GDK feature flag `integrated_error_tracking` is enabled. If you are running GDK locally and you do not see the option for error tracking, you can enable it by running the following commands: + +```linux +cd <PATH_TO_GDK> +gdk rails console +Feature.enable(:integrated_error_tracking) +``` + +### Emitting Errors + +#### Supported Sentry types + +According to the [data model](https://develop.sentry.dev/sdk/envelopes/#data-model), the available item types are: + +- [Event](https://develop.sentry.dev/sdk/event-payloads/) +- [Transactions](https://develop.sentry.dev/sdk/event-payloads/transaction/) +- Attachment +- [Session](https://develop.sentry.dev/sdk/sessions/) +- [Sessions](https://develop.sentry.dev/sdk/sessions/) +- [User feedback](https://develop.sentry.dev/sdk/envelopes/#user-feedback) (also known as user report) +- [Client report](https://develop.sentry.dev/sdk/client-reports/) + +Items of various types can be sent to the error tracking app, using either the Store endpoint, the envelope endpoint, or both. The following table lists all event types available through Sentry SDK. It also explains which endpoint can be used for ingestion and whether it is supported by GitLab Observability Backend. + +Event item types can contain various interfaces, such as exception, message, stack trace, and template. You can read more about the core data interfaces in [Sentry documentation](https://develop.sentry.dev/sdk/event-payloads/#core-interfaces). + +| Item type | Interface | Can be sent through the Store endpoint | Can be sent through the Envelope endpoint | Currently Supported | +|---------------|--------------|----------------|----------------|---------------------| +| event | exception | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | +| event | message | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | +| event | stack trace | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | +| event | template | **{check-circle}** Yes | **{check-circle}** Yes | **{dotted-circle}** No | +| transaction | NA | **{dotted-circle}** No | **{check-circle}** Yes | **{dotted-circle}** No | +| attachment | NA | **{dotted-circle}** No | **{check-circle}** Yes | **{dotted-circle}** No | +| session | NA | **{dotted-circle}** No | **{check-circle}** Yes | **{dotted-circle}** No | +| sessions | NA | **{dotted-circle}** No | **{check-circle}** Yes | **{dotted-circle}** No | +| user_report | NA | **{dotted-circle}** No | **{check-circle}** Yes | **{dotted-circle}** No | +| client_report | NA | **{dotted-circle}** No | **{check-circle}** Yes | **{dotted-circle}** No | + +\* NA = Not Applicable + +#### Supported languages + +Each language shows a basic example of how to capture exceptions with the respective SDK. +For more in-depth documentation, see [documentation for Sentry SDK](https://docs.sentry.io/). You can also find information for additional programming languages. + +Only a subset of languages is supported. + +The following table lists them: + +| Sentry SDK | Supported? | +| ----------- | ----------- | +| Ruby | Yes | +| Go | Yes | +| JavaScript | Yes | +| Java | Yes | +| Python | Yes | +| PHP | Yes | +| .NET | Not tested | +| Android | Not tested | +| Apple | Not tested | +| Perl | Not tested | + +A more up-to-date version of [this matrix can be found in this doc](https://gitlab.com/gitlab-org/opstrace/opstrace/-/issues/1737). + +#### Go + +1. `chdir` into folder `docs/guides/user/error_tracking_examples/go/` +1. Install the dependencies with the following command: + + ```shell + go mod tidy + ``` + +1. Run the following command: + + ```shell + export SENTRY_DSN="<DSN string>" + go run main.go <DSN string> + ``` + +After you've run this program, there should be an error visible in the Error tracking tab from `Listing captured errors` section of this document. + +#### Ruby + +1. `chdir` into folder `docs/guides/user/error_tracking_examples/ruby/` +1. Install the dependencies with the following command: + + ```shell + gem install bundler + bundle install + ``` + +1. Execute the example with the following command: + + ```shell + export SENTRY_DSN="<DSN string>" + ruby app.rb + ``` + +After you've run this program, there should be an error visible in the Error tracking tab from `Listing captured errors` section of this document. + +#### PHP + +1. `chdir` into folder `docs/guides/user/error_tracking_examples/php/` + +1. Build and run the Docker container with the following commands: + +```shell +export SENTRY_DSN="<DSN string>" +docker build -t sentry-php . +docker run -e SENTRY_DSN --rm sentry-php +``` + +After you've run this program, there should be an error visible in the Error tracking tab from `Listing captured errors` section of this document. + +#### Python + +1. `chdir` into folder `docs/guides/user/error_tracking_examples/python/` + +1. Install the dependencies with the following commands: + + ```shell + virtualenv env + source env/bin/activate + pip install -r requirements.txt + ``` + +1. Run the following commands: + +```shell +export SENTRY_DSN="<DSN string>" +python send_exception.py +``` + +After you've run this program, there should be an error visible in the Error tracking tab from `Listing captured errors` section of this document. + +#### Java + +1. `chdir` into folder `docs/guides/user/error_tracking_examples/python/` + +1. Run the following command: + +```shell +export SENTRY_DSN="<DSN string>" +./gradlew run +``` + +#### Node.js + +1. `chdir` into folder `docs/guides/user/error_tracking_examples/nodejs/` + +1. Install the dependencies with the following command: + + ```shell + npm install --save @sentry/node @sentry/tracing + ``` + +1. Run the following command: + + ```shell + export SENTRY_DSN="<DSN string>" + node ./test.js + ``` + +After you've run this program, there should be an error visible in the Error tracking tab from `Listing captured errors` section of this document. + +### Rotating Sentry DSN + +The Sentry DSN (client key) is a secret and it should not be exposed to the public. If it's leaked, you can rotate the Sentry DSN with the following steps: + +1. [Create an access token](../user/profile/personal_access_tokens.md#create-a-personal-access-token) by clicking your profile picture in GitLab.com. Then choose Preferences,then Access Token. Make sure you add the API scope. +1. Using the [error tracking API](../api/error_tracking.md), create a new Sentry DSN with the following command: + + ```shell + curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" --header "Content-Type: application/json" \ + "https://gitlab.example.com/api/v4/projects/<your_project_number>/error_tracking/client_keys" + ``` + +1. Get the available client keys (Sentry DSNs). Ensure that the newly created Sentry DSN is in place. Then note down the key ID of the old client key: + + ```shell + curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/<your_project_number>/error_tracking/client_keys" + ``` + +1. Delete the old client key with the following command: + + ```shell + curl --request DELETE --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/<your_project_number>/error_tracking/client_keys/<key_id>" + ``` diff --git a/doc/operations/img/Monitor-list_errors.png b/doc/operations/img/Monitor-list_errors.png Binary files differnew file mode 100644 index 00000000000..06833c08637 --- /dev/null +++ b/doc/operations/img/Monitor-list_errors.png diff --git a/doc/operations/img/Monitor_tab-post-enable.png b/doc/operations/img/Monitor_tab-post-enable.png Binary files differnew file mode 100644 index 00000000000..91f0a0d8d27 --- /dev/null +++ b/doc/operations/img/Monitor_tab-post-enable.png diff --git a/doc/operations/img/Monitor_tab-pre-enable.png b/doc/operations/img/Monitor_tab-pre-enable.png Binary files differnew file mode 100644 index 00000000000..2984110a7d7 --- /dev/null +++ b/doc/operations/img/Monitor_tab-pre-enable.png diff --git a/lib/bulk_imports/projects/pipelines/commit_notes_pipeline.rb b/lib/bulk_imports/projects/pipelines/commit_notes_pipeline.rb new file mode 100644 index 00000000000..092b03cb7b7 --- /dev/null +++ b/lib/bulk_imports/projects/pipelines/commit_notes_pipeline.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module BulkImports + module Projects + module Pipelines + class CommitNotesPipeline + include NdjsonPipeline + + relation_name 'commit_notes' + + extractor ::BulkImports::Common::Extractors::NdjsonExtractor, relation: relation + end + end + end +end diff --git a/lib/bulk_imports/projects/stage.rb b/lib/bulk_imports/projects/stage.rb index 73e102696fa..eecd567f54f 100644 --- a/lib/bulk_imports/projects/stage.rb +++ b/lib/bulk_imports/projects/stage.rb @@ -104,6 +104,11 @@ module BulkImports pipeline: BulkImports::Projects::Pipelines::CiPipelinesPipeline, stage: 5 }, + commit_notes: { + pipeline: BulkImports::Projects::Pipelines::CommitNotesPipeline, + minimum_source_version: '15.10.0', + stage: 5 + }, wiki: { pipeline: BulkImports::Common::Pipelines::WikiPipeline, stage: 5 diff --git a/lib/gitlab/database/async_foreign_keys/foreign_key_validator.rb b/lib/gitlab/database/async_foreign_keys/foreign_key_validator.rb index 5958c56a45a..1eb8588405d 100644 --- a/lib/gitlab/database/async_foreign_keys/foreign_key_validator.rb +++ b/lib/gitlab/database/async_foreign_keys/foreign_key_validator.rb @@ -33,11 +33,7 @@ module Gitlab delegate :connection, :name, :table_name, :connection_db_config, to: :async_validation def foreign_key_exists? - relation = if table_name =~ Gitlab::Database::FULLY_QUALIFIED_IDENTIFIER - Gitlab::Database::PostgresForeignKey.by_constrained_table_identifier(table_name) - else - Gitlab::Database::PostgresForeignKey.by_constrained_table_name(table_name) - end + relation = Gitlab::Database::PostgresForeignKey.by_constrained_table_name_or_identifier(table_name) relation.by_name(name).exists? end diff --git a/lib/gitlab/database/migration_helpers.rb b/lib/gitlab/database/migration_helpers.rb index 9c1cb8e352c..08122875abc 100644 --- a/lib/gitlab/database/migration_helpers.rb +++ b/lib/gitlab/database/migration_helpers.rb @@ -379,7 +379,7 @@ module Gitlab end end - fks = Gitlab::Database::PostgresForeignKey.by_constrained_table_name(source) + fks = Gitlab::Database::PostgresForeignKey.by_constrained_table_name_or_identifier(source) fks = fks.by_referenced_table_name(target) if target fks = fks.by_name(options[:name]) if options[:name] diff --git a/lib/gitlab/database/postgres_foreign_key.rb b/lib/gitlab/database/postgres_foreign_key.rb index 04ef574a451..28044b42f44 100644 --- a/lib/gitlab/database/postgres_foreign_key.rb +++ b/lib/gitlab/database/postgres_foreign_key.rb @@ -38,6 +38,14 @@ module Gitlab scope :by_constrained_table_name, ->(name) { where(constrained_table_name: name) } + scope :by_constrained_table_name_or_identifier, ->(name) do + if name =~ Database::FULLY_QUALIFIED_IDENTIFIER + by_constrained_table_identifier(name) + else + by_constrained_table_name(name) + end + end + scope :not_inherited, -> { where(is_inherited: false) } scope :by_name, ->(name) { where(name: name) } diff --git a/lib/gitlab/import_export/project/import_export.yml b/lib/gitlab/import_export/project/import_export.yml index 0fd390ec785..335096faed6 100644 --- a/lib/gitlab/import_export/project/import_export.yml +++ b/lib/gitlab/import_export/project/import_export.yml @@ -89,11 +89,11 @@ tree: - :milestone - :resource_state_events - :external_pull_requests + - commit_notes: + - :author + - events: + - :push_event_payload - ci_pipelines: - - notes: - - :author - - events: - - :push_event_payload - stages: - :builds - :generic_commit_statuses @@ -132,6 +132,10 @@ tree: import_only_tree: project: - ci_pipelines: + - notes: + - :author + - events: + - :push_event_payload - stages: - :statuses @@ -975,6 +979,9 @@ excluded_attributes: notes: - :noteable_id - :review_id + commit_notes: + - :noteable_id + - :review_id label_links: - :label_id - :target_id @@ -1099,6 +1106,8 @@ methods: - :squash_option notes: - :type + commit_notes: + - :type labels: - :type label: @@ -1120,8 +1129,6 @@ methods: - :type lists: - :list_type - ci_pipelines: - - :notes issues: - :state diff --git a/lib/gitlab/import_export/project/relation_factory.rb b/lib/gitlab/import_export/project/relation_factory.rb index f93731c7aa4..ab95e306abf 100644 --- a/lib/gitlab/import_export/project/relation_factory.rb +++ b/lib/gitlab/import_export/project/relation_factory.rb @@ -5,6 +5,7 @@ module Gitlab module Project class RelationFactory < Base::RelationFactory OVERRIDES = { snippets: :project_snippets, + commit_notes: 'Note', ci_pipelines: 'Ci::Pipeline', pipelines: 'Ci::Pipeline', stages: 'Ci::Stage', @@ -84,7 +85,7 @@ module Gitlab def setup_models case @relation_name when :merge_request_diff_files then setup_diff - when :notes then setup_note + when :notes, :Note then setup_note when :'Ci::Pipeline' then setup_pipeline when *BUILD_MODELS then setup_build when :issues then setup_issue diff --git a/lib/gitlab/usage_data.rb b/lib/gitlab/usage_data.rb index 53794854bd0..3974c5f0170 100644 --- a/lib/gitlab/usage_data.rb +++ b/lib/gitlab/usage_data.rb @@ -587,7 +587,6 @@ module Gitlab date_range = { date_from: time_period[:created_at].first, date_to: time_period[:created_at].last } { - action_monthly_active_users_web_ide_edit: redis_usage_data { counter.count_web_ide_edit_actions(**date_range) }, action_monthly_active_users_sfe_edit: redis_usage_data { counter.count_sfe_edit_actions(**date_range) }, action_monthly_active_users_snippet_editor_edit: redis_usage_data { counter.count_snippet_editor_edit_actions(**date_range) } } diff --git a/lib/sidebars/projects/menus/monitor_menu.rb b/lib/sidebars/projects/menus/monitor_menu.rb index 7d1fa8b8fa7..fea71e4aefd 100644 --- a/lib/sidebars/projects/menus/monitor_menu.rb +++ b/lib/sidebars/projects/menus/monitor_menu.rb @@ -12,7 +12,6 @@ module Sidebars add_item(error_tracking_menu_item) add_item(alert_management_menu_item) add_item(incidents_menu_item) - add_item(airflow_dashboard_menu_item) true end @@ -97,20 +96,6 @@ module Sidebars item_id: :incidents ) end - - def airflow_dashboard_menu_item - unless can?(context.current_user, :read_airflow_dags, context.project) && - Feature.enabled?(:airflow_dags, context.project) - return ::Sidebars::NilMenuItem.new(item_id: :airflow) - end - - ::Sidebars::MenuItem.new( - title: _('Airflow'), - link: project_airflow_dags_path(context.project), - active_routes: { path: 'airflow/dags#show' }, - item_id: :airflow_dags - ) - end end end end diff --git a/locale/gitlab.pot b/locale/gitlab.pot index ea74c2390ac..9d9a472a40c 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -3705,39 +3705,6 @@ msgstr "" msgid "After you've reviewed these contribution guidelines, you'll be all set to" msgstr "" -msgid "Airflow" -msgstr "" - -msgid "Airflow|Airflow DAGs" -msgstr "" - -msgid "Airflow|DAG" -msgstr "" - -msgid "Airflow|DAG file location" -msgstr "" - -msgid "Airflow|Either the Airflow instance does not contain DAGs or has yet to be configured" -msgstr "" - -msgid "Airflow|GitLab Airflow integration" -msgstr "" - -msgid "Airflow|Is active" -msgstr "" - -msgid "Airflow|Is paused" -msgstr "" - -msgid "Airflow|Next run" -msgstr "" - -msgid "Airflow|Schedule" -msgstr "" - -msgid "Airflow|There are no DAGs to show" -msgstr "" - msgid "Akismet" msgstr "" diff --git a/spec/factories/airflow/dags.rb b/spec/factories/airflow/dags.rb deleted file mode 100644 index ca4276e2c8f..00000000000 --- a/spec/factories/airflow/dags.rb +++ /dev/null @@ -1,8 +0,0 @@ -# frozen_string_literal: true -FactoryBot.define do - factory :airflow_dags, class: '::Airflow::Dags' do - sequence(:dag_name) { |n| "dag_name_#{n}" } - - project - end -end diff --git a/spec/fixtures/lib/gitlab/import_export/complex/project.json b/spec/fixtures/lib/gitlab/import_export/complex/project.json index afec1bb2585..a0ac70d7d9c 100644 --- a/spec/fixtures/lib/gitlab/import_export/complex/project.json +++ b/spec/fixtures/lib/gitlab/import_export/complex/project.json @@ -7877,6 +7877,95 @@ } } ], + "commit_notes": [ + { + "note": "Commit note 1", + "noteable_type": "Commit", + "author_id": 1, + "created_at": "2023-01-30T19:27:36.585Z", + "updated_at": "2023-02-10T14:43:01.308Z", + "project_id": 5, + "attachment": { + "url": null + }, + "line_code": null, + "commit_id": "sha-notes", + "system": false, + "st_diff": null, + "updated_by_id": 1, + "type": null, + "position": null, + "original_position": null, + "resolved_at": null, + "resolved_by_id": null, + "discussion_id": "e3fde7d585c6467a7a5147e83617eb6daa61aaf4", + "change_position": null, + "resolved_by_push": null, + "confidential": null, + "last_edited_at": "2023-02-10T14:43:01.306Z", + "author": { + "name": "Administrator" + }, + "events": [ + { + "project_id": 1, + "author_id": 1, + "created_at": "2023-01-30T19:27:36.815Z", + "updated_at": "2023-01-30T19:27:36.815Z", + "action": "commented", + "target_type": "Note", + "fingerprint": null, + "push_event_payload": { + "commit_count": 1, + "action": "pushed", + "ref_type": "branch", + "commit_to": "sha-notes", + "ref": "master" + } + } + ] + }, + { + "note": "Commit note 2", + "noteable_type": "Commit", + "author_id": 1, + "created_at": "2023-02-10T14:44:08.138Z", + "updated_at": "2023-02-10T14:54:42.828Z", + "project_id": 1, + "attachment": { + "url": null + }, + "line_code": null, + "commit_id": "sha-notes", + "system": false, + "st_diff": null, + "updated_by_id": 1, + "type": null, + "position": null, + "original_position": null, + "resolved_at": null, + "resolved_by_id": null, + "discussion_id": "53ca55a01732aff4f17daecdf076853f4ab152eb", + "change_position": null, + "resolved_by_push": null, + "confidential": null, + "last_edited_at": "2023-02-10T14:54:42.827Z", + "author": { + "name": "Administrator" + }, + "events": [ + { + "project_id": 1, + "author_id": 1, + "created_at": "2023-02-10T16:37:16.659Z", + "updated_at": "2023-02-10T16:37:16.659Z", + "action": "commented", + "target_type": "Note", + "fingerprint": null + } + ] + } + ], "pipeline_schedules": [ { "id": 1, diff --git a/spec/fixtures/lib/gitlab/import_export/complex/tree/project/commit_notes.ndjson b/spec/fixtures/lib/gitlab/import_export/complex/tree/project/commit_notes.ndjson new file mode 100644 index 00000000000..b623c388b4f --- /dev/null +++ b/spec/fixtures/lib/gitlab/import_export/complex/tree/project/commit_notes.ndjson @@ -0,0 +1,2 @@ +{"note":"Commit note 1","noteable_type":"Commit","author_id":1,"created_at":"2023-01-30T19:27:36.585Z","updated_at":"2023-02-10T14:43:01.308Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":"sha-notes","system":false,"st_diff":null,"updated_by_id":1,"type":null,"position":null,"original_position":null,"resolved_at":null,"resolved_by_id":null,"discussion_id":"e3fde7d585c6467a7a5147e83617eb6daa61aaf4","change_position":null,"resolved_by_push":null,"confidential":null,"last_edited_at":"2023-02-10T14:43:01.306Z","author":{"name":"Administrator"},"events":[{"project_id":1,"author_id":1,"created_at":"2023-01-30T19:27:36.815Z","updated_at":"2023-01-30T19:27:36.815Z","action":"commented","target_type":"Note","fingerprint":null,"push_event_payload":{"commit_count":1,"action":"pushed","ref_type":"branch","commit_to":"sha-notes","ref":"master"}}]} +{"note":"Commit note 2","noteable_type":"Commit","author_id":1,"created_at":"2023-02-10T14:44:08.138Z","updated_at":"2023-02-10T14:54:42.828Z","project_id":1,"attachment":{"url":null},"line_code":null,"commit_id":"sha-notes","system":false,"st_diff":null,"updated_by_id":1,"type":null,"position":null,"original_position":null,"resolved_at":null,"resolved_by_id":null,"discussion_id":"53ca55a01732aff4f17daecdf076853f4ab152eb","change_position":null,"resolved_by_push":null,"confidential":null,"last_edited_at":"2023-02-10T14:54:42.827Z","author":{"name":"Administrator"},"events":[{"project_id":1,"author_id":1,"created_at":"2023-02-10T16:37:16.659Z","updated_at":"2023-02-10T16:37:16.659Z","action":"commented","target_type":"Note","fingerprint":null}]} diff --git a/spec/frontend/airflow/dags/components/dags_spec.js b/spec/frontend/airflow/dags/components/dags_spec.js deleted file mode 100644 index f9cf4fc87af..00000000000 --- a/spec/frontend/airflow/dags/components/dags_spec.js +++ /dev/null @@ -1,115 +0,0 @@ -import { GlAlert, GlPagination, GlTableLite } from '@gitlab/ui'; -import { mountExtended } from 'helpers/vue_test_utils_helper'; -import { TEST_HOST } from 'helpers/test_constants'; -import AirflowDags from '~/airflow/dags/components/dags.vue'; -import TimeAgo from '~/vue_shared/components/time_ago_tooltip.vue'; -import { mockDags } from './mock_data'; - -describe('AirflowDags', () => { - let wrapper; - - const createWrapper = ( - dags = [], - pagination = { page: 1, isLastPage: false, per_page: 2, totalItems: 0 }, - ) => { - wrapper = mountExtended(AirflowDags, { - propsData: { - dags, - pagination, - }, - }); - }; - - const findAlert = () => wrapper.findComponent(GlAlert); - const findEmptyState = () => wrapper.findByText('There are no DAGs to show'); - const findPagination = () => wrapper.findComponent(GlPagination); - - describe('default (no dags)', () => { - beforeEach(() => { - createWrapper(); - }); - - it('shows incubation warning', () => { - expect(findAlert().exists()).toBe(true); - }); - - it('shows empty state', () => { - expect(findEmptyState().exists()).toBe(true); - }); - - it('does not show pagination', () => { - expect(findPagination().exists()).toBe(false); - }); - }); - - describe('with dags', () => { - const createWrapperWithDags = (pagination = {}) => { - createWrapper(mockDags, { - page: 1, - isLastPage: false, - per_page: 2, - totalItems: 5, - ...pagination, - }); - }; - - const findDagsData = () => { - return wrapper - .findComponent(GlTableLite) - .findAll('tbody tr') - .wrappers.map((tr) => { - return tr.findAll('td').wrappers.map((td) => { - const timeAgo = td.findComponent(TimeAgo); - - if (timeAgo.exists()) { - return { - type: 'time', - value: timeAgo.props('time'), - }; - } - - return { - type: 'text', - value: td.text(), - }; - }); - }); - }; - - it('renders the table of Dags with data', () => { - createWrapperWithDags(); - - expect(findDagsData()).toEqual( - mockDags.map((x) => [ - { type: 'text', value: x.dag_name }, - { type: 'text', value: x.schedule }, - { type: 'time', value: x.next_run }, - { type: 'text', value: String(x.is_active) }, - { type: 'text', value: String(x.is_paused) }, - { type: 'text', value: x.fileloc }, - ]), - ); - }); - - describe('Pagination behaviour', () => { - it.each` - pagination | expected - ${{}} | ${{ value: 1, prevPage: null, nextPage: 2 }} - ${{ page: 2 }} | ${{ value: 2, prevPage: 1, nextPage: 3 }} - ${{ isLastPage: true, page: 2 }} | ${{ value: 2, prevPage: 1, nextPage: null }} - `('with $pagination, sets pagination props', ({ pagination, expected }) => { - createWrapperWithDags({ ...pagination }); - - expect(findPagination().props()).toMatchObject(expected); - }); - - it('generates link for each page', () => { - createWrapperWithDags(); - - const generateLink = findPagination().props('linkGen'); - - expect(generateLink(3)).toBe(`${TEST_HOST}/?page=3`); - }); - }); - }); -}); diff --git a/spec/frontend/airflow/dags/components/mock_data.js b/spec/frontend/airflow/dags/components/mock_data.js deleted file mode 100644 index 9547282517d..00000000000 --- a/spec/frontend/airflow/dags/components/mock_data.js +++ /dev/null @@ -1,67 +0,0 @@ -export const mockDags = [ - { - id: 1, - project_id: 7, - created_at: '2023-01-05T14:07:02.975Z', - updated_at: '2023-01-05T14:07:02.975Z', - has_import_errors: false, - is_active: false, - is_paused: true, - next_run: '2023-01-05T14:07:02.975Z', - dag_name: 'Dag number 1', - schedule: 'Manual', - fileloc: '/opt/dag.py', - }, - { - id: 2, - project_id: 7, - created_at: '2023-01-05T14:07:02.975Z', - updated_at: '2023-01-05T14:07:02.975Z', - has_import_errors: false, - is_active: false, - is_paused: true, - next_run: '2023-01-05T14:07:02.975Z', - dag_name: 'Dag number 2', - schedule: 'Manual', - fileloc: '/opt/dag.py', - }, - { - id: 3, - project_id: 7, - created_at: '2023-01-05T14:07:02.975Z', - updated_at: '2023-01-05T14:07:02.975Z', - has_import_errors: false, - is_active: false, - is_paused: true, - next_run: '2023-01-05T14:07:02.975Z', - dag_name: 'Dag number 3', - schedule: 'Manual', - fileloc: '/opt/dag.py', - }, - { - id: 4, - project_id: 7, - created_at: '2023-01-05T14:07:02.975Z', - updated_at: '2023-01-05T14:07:02.975Z', - has_import_errors: false, - is_active: false, - is_paused: true, - next_run: '2023-01-05T14:07:02.975Z', - dag_name: 'Dag number 4', - schedule: 'Manual', - fileloc: '/opt/dag.py', - }, - { - id: 5, - project_id: 7, - created_at: '2023-01-05T14:07:02.975Z', - updated_at: '2023-01-05T14:07:02.975Z', - has_import_errors: false, - is_active: false, - is_paused: true, - next_run: '2023-01-05T14:07:02.975Z', - dag_name: 'Dag number 5', - schedule: 'Manual', - fileloc: '/opt/dag.py', - }, -]; diff --git a/spec/frontend/contributors/component/__snapshots__/contributors_spec.js.snap b/spec/frontend/contributors/component/__snapshots__/contributors_spec.js.snap index 2f441f0f747..4b7439f6fd2 100644 --- a/spec/frontend/contributors/component/__snapshots__/contributors_spec.js.snap +++ b/spec/frontend/contributors/component/__snapshots__/contributors_spec.js.snap @@ -53,23 +53,22 @@ exports[`Contributors charts should render charts and a RefSelector when loading Excluding merge commits. Limited to 6,000 commits. </span> - <div> - <glareachart-stub - annotations="" - class="gl-mb-5" - data="[object Object]" - height="264" - includelegendavgmax="true" - legendaveragetext="Avg" - legendcurrenttext="Current" - legendlayout="inline" - legendmaxtext="Max" - legendmintext="Min" - option="[object Object]" - thresholds="" - width="0" - /> - </div> + <glareachart-stub + annotations="" + class="gl-mb-5" + data="[object Object]" + height="264" + includelegendavgmax="true" + legendaveragetext="Avg" + legendcurrenttext="Current" + legendlayout="inline" + legendmaxtext="Max" + legendmintext="Min" + option="[object Object]" + responsive="" + thresholds="" + width="auto" + /> <div class="row" @@ -91,22 +90,21 @@ exports[`Contributors charts should render charts and a RefSelector when loading </p> - <div> - <glareachart-stub - annotations="" - data="[object Object]" - height="216" - includelegendavgmax="true" - legendaveragetext="Avg" - legendcurrenttext="Current" - legendlayout="inline" - legendmaxtext="Max" - legendmintext="Min" - option="[object Object]" - thresholds="" - width="0" - /> - </div> + <glareachart-stub + annotations="" + data="[object Object]" + height="216" + includelegendavgmax="true" + legendaveragetext="Avg" + legendcurrenttext="Current" + legendlayout="inline" + legendmaxtext="Max" + legendmintext="Min" + option="[object Object]" + responsive="" + thresholds="" + width="auto" + /> </div> </div> </div> diff --git a/spec/frontend/profile/account/components/update_username_spec.js b/spec/frontend/profile/account/components/update_username_spec.js index fa0e86a7b05..028c30cb291 100644 --- a/spec/frontend/profile/account/components/update_username_spec.js +++ b/spec/frontend/profile/account/components/update_username_spec.js @@ -1,8 +1,8 @@ import { GlModal } from '@gitlab/ui'; -import { shallowMount } from '@vue/test-utils'; import MockAdapter from 'axios-mock-adapter'; import { nextTick } from 'vue'; import { TEST_HOST } from 'helpers/test_constants'; +import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; import { createAlert } from '~/flash'; import axios from '~/lib/utils/axios_utils'; import { HTTP_STATUS_BAD_REQUEST, HTTP_STATUS_OK } from '~/lib/utils/http_status'; @@ -21,8 +21,10 @@ describe('UpdateUsername component', () => { let wrapper; let axiosMock; + const findNewUsernameInput = () => wrapper.findByTestId('new-username-input'); + const createComponent = (props = {}) => { - wrapper = shallowMount(UpdateUsername, { + wrapper = shallowMountExtended(UpdateUsername, { propsData: { ...defaultProps, ...props, @@ -80,11 +82,7 @@ describe('UpdateUsername component', () => { beforeEach(async () => { createComponent(); - // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details - // eslint-disable-next-line no-restricted-syntax - wrapper.setData({ newUsername }); - - await nextTick(); + await findNewUsernameInput().setValue(newUsername); }); it('confirmation modal contains proper header and body', async () => { diff --git a/spec/frontend/projects/pipelines/charts/components/__snapshots__/ci_cd_analytics_area_chart_spec.js.snap b/spec/frontend/projects/pipelines/charts/components/__snapshots__/ci_cd_analytics_area_chart_spec.js.snap index fc51825f15b..1545c52d7cb 100644 --- a/spec/frontend/projects/pipelines/charts/components/__snapshots__/ci_cd_analytics_area_chart_spec.js.snap +++ b/spec/frontend/projects/pipelines/charts/components/__snapshots__/ci_cd_analytics_area_chart_spec.js.snap @@ -8,20 +8,19 @@ exports[`CiCdAnalyticsAreaChart matches the snapshot 1`] = ` Some title </p> - <div> - <glareachart-stub - annotations="" - data="[object Object],[object Object]" - height="300" - legendaveragetext="Avg" - legendcurrenttext="Current" - legendlayout="inline" - legendmaxtext="Max" - legendmintext="Min" - option="[object Object]" - thresholds="" - width="0" - /> - </div> + <glareachart-stub + annotations="" + data="[object Object],[object Object]" + height="300" + legendaveragetext="Avg" + legendcurrenttext="Current" + legendlayout="inline" + legendmaxtext="Max" + legendmintext="Min" + option="[object Object]" + responsive="" + thresholds="" + width="auto" + /> </div> `; diff --git a/spec/frontend/vue_shared/components/resizable_chart/__snapshots__/resizable_chart_container_spec.js.snap b/spec/frontend/vue_shared/components/resizable_chart/__snapshots__/resizable_chart_container_spec.js.snap deleted file mode 100644 index cdfe311acd9..00000000000 --- a/spec/frontend/vue_shared/components/resizable_chart/__snapshots__/resizable_chart_container_spec.js.snap +++ /dev/null @@ -1,23 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Resizable Chart Container renders the component 1`] = ` -<div> - <template> - <div - class="slot" - > - <span - class="width" - > - 0 - </span> - - <span - class="height" - > - 0 - </span> - </div> - </template> -</div> -`; diff --git a/spec/frontend/vue_shared/components/resizable_chart/resizable_chart_container_spec.js b/spec/frontend/vue_shared/components/resizable_chart/resizable_chart_container_spec.js deleted file mode 100644 index 7536df24ac6..00000000000 --- a/spec/frontend/vue_shared/components/resizable_chart/resizable_chart_container_spec.js +++ /dev/null @@ -1,64 +0,0 @@ -import { mount } from '@vue/test-utils'; -import $ from 'jquery'; -import { nextTick } from 'vue'; -import ResizableChartContainer from '~/vue_shared/components/resizable_chart/resizable_chart_container.vue'; - -jest.mock('~/lib/utils/common_utils', () => ({ - debounceByAnimationFrame(callback) { - return jest.spyOn({ callback }, 'callback'); - }, -})); - -describe('Resizable Chart Container', () => { - let wrapper; - - beforeEach(() => { - wrapper = mount(ResizableChartContainer, { - scopedSlots: { - default: ` - <template #default="{ width, height }"> - <div class="slot"> - <span class="width">{{width}}</span> - <span class="height">{{height}}</span> - </div> - </template> - `, - }, - }); - }); - - afterEach(() => { - wrapper.destroy(); - }); - - it('renders the component', () => { - expect(wrapper.element).toMatchSnapshot(); - }); - - it('updates the slot width and height props', async () => { - const width = 1920; - const height = 1080; - - // JSDOM mocks and sets clientWidth/clientHeight to 0 so we set manually - wrapper.vm.$refs.chartWrapper = { clientWidth: width, clientHeight: height }; - - $(document).trigger('content.resize'); - - await nextTick(); - const widthNode = wrapper.find('.slot > .width'); - const heightNode = wrapper.find('.slot > .height'); - - expect(parseInt(widthNode.text(), 10)).toEqual(width); - expect(parseInt(heightNode.text(), 10)).toEqual(height); - }); - - it('calls onResize on manual resize', () => { - $(document).trigger('content.resize'); - expect(wrapper.vm.debouncedResize).toHaveBeenCalled(); - }); - - it('calls onResize on page resize', () => { - window.dispatchEvent(new Event('resize')); - expect(wrapper.vm.debouncedResize).toHaveBeenCalled(); - }); -}); diff --git a/spec/lib/bulk_imports/projects/pipelines/commit_notes_pipeline_spec.rb b/spec/lib/bulk_imports/projects/pipelines/commit_notes_pipeline_spec.rb new file mode 100644 index 00000000000..f5f31c83033 --- /dev/null +++ b/spec/lib/bulk_imports/projects/pipelines/commit_notes_pipeline_spec.rb @@ -0,0 +1,69 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe BulkImports::Projects::Pipelines::CommitNotesPipeline, feature_category: :importers do + let_it_be(:user) { create(:user) } + let_it_be(:group) { create(:group) } + let_it_be(:project) { create(:project, group: group) } + let_it_be(:bulk_import) { create(:bulk_import, user: user) } + let_it_be(:entity) do + create( + :bulk_import_entity, + :project_entity, + project: project, + bulk_import: bulk_import, + source_full_path: 'source/full/path', + destination_slug: 'destination-project', + destination_namespace: group.full_path + ) + end + + let(:ci_pipeline_note) do + { + "note" => "Commit note 1", + "noteable_type" => "Commit", + "author_id" => 1, + "created_at" => "2023-01-30T19:27:36.585Z", + "updated_at" => "2023-02-10T14:43:01.308Z", + "project_id" => 1, + "commit_id" => "sha-notes", + "system" => false, + "updated_by_id" => 1, + "discussion_id" => "e3fde7d585c6467a7a5147e83617eb6daa61aaf4", + "last_edited_at" => "2023-02-10T14:43:01.306Z", + "author" => { + "name" => "Administrator" + }, + "events" => [ + { + "project_id" => 1, + "author_id" => 1, + "action" => "commented", + "target_type" => "Note" + } + ] + } + end + + let_it_be(:tracker) { create(:bulk_import_tracker, entity: entity) } + let_it_be(:context) { BulkImports::Pipeline::Context.new(tracker) } + + subject(:pipeline) { described_class.new(context) } + + describe '#run' do + before do + group.add_owner(user) + + allow_next_instance_of(BulkImports::Common::Extractors::NdjsonExtractor) do |extractor| + allow(extractor).to receive(:extract).and_return( + BulkImports::Pipeline::ExtractedData.new(data: [ci_pipeline_note]) + ) + end + end + + it 'imports ci pipeline notes into destination project' do + expect { pipeline.run }.to change { project.notes.for_commit_id("sha-notes").count }.from(0).to(1) + end + end +end diff --git a/spec/lib/gitlab/database/migration_helpers_spec.rb b/spec/lib/gitlab/database/migration_helpers_spec.rb index 9df23776be8..63a470c82a1 100644 --- a/spec/lib/gitlab/database/migration_helpers_spec.rb +++ b/spec/lib/gitlab/database/migration_helpers_spec.rb @@ -1049,6 +1049,8 @@ RSpec.describe Gitlab::Database::MigrationHelpers do describe '#foreign_key_exists?' do let(:referenced_table_name) { '_test_gitlab_main_referenced' } let(:referencing_table_name) { '_test_gitlab_main_referencing' } + let(:schema) { 'public' } + let(:identifier) { "#{schema}.#{referencing_table_name}" } before do model.connection.execute(<<~SQL) @@ -1085,6 +1087,10 @@ RSpec.describe Gitlab::Database::MigrationHelpers do expect(model.foreign_key_exists?(referencing_table_name, target_table)).to be_truthy end + it 'finds existing foreign_keys by identifier' do + expect(model.foreign_key_exists?(identifier, target_table)).to be_truthy + end + it 'compares by column name if given' do expect(model.foreign_key_exists?(referencing_table_name, target_table, column: :user_id)).to be_falsey end diff --git a/spec/lib/gitlab/database/postgres_foreign_key_spec.rb b/spec/lib/gitlab/database/postgres_foreign_key_spec.rb index ae56f66737d..75f94bf2654 100644 --- a/spec/lib/gitlab/database/postgres_foreign_key_spec.rb +++ b/spec/lib/gitlab/database/postgres_foreign_key_spec.rb @@ -70,13 +70,29 @@ RSpec.describe Gitlab::Database::PostgresForeignKey, type: :model, feature_categ end describe '#by_constrained_table_name' do - it 'finds the foreign keys for the constrained table' do - expected = described_class.where(name: %w[fk_constrained_to_referenced fk_constrained_to_other_referenced]).to_a + let(:expected) { described_class.where(name: %w[fk_constrained_to_referenced fk_constrained_to_other_referenced]).to_a } + it 'finds the foreign keys for the constrained table' do expect(described_class.by_constrained_table_name(table_name("constrained_table"))).to match_array(expected) end end + describe '#by_constrained_table_name_or_identifier' do + let(:expected) { described_class.where(name: %w[fk_constrained_to_referenced fk_constrained_to_other_referenced]).to_a } + + context 'when using table name' do + it 'finds the foreign keys for the constrained table' do + expect(described_class.by_constrained_table_name_or_identifier(table_name("constrained_table"))).to match_array(expected) + end + end + + context 'when using identifier' do + it 'finds the foreign keys for the constrained table' do + expect(described_class.by_constrained_table_name_or_identifier(schema_table_name('constrained_table'))).to match_array(expected) + end + end + end + describe '#by_name' do it 'finds foreign keys by name' do expect(described_class.by_name('fk_constrained_to_referenced').pluck(:name)).to contain_exactly('fk_constrained_to_referenced') diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml index 9607ac18548..63f1e50804c 100644 --- a/spec/lib/gitlab/import_export/all_models.yml +++ b/spec/lib/gitlab/import_export/all_models.yml @@ -92,6 +92,20 @@ notes: - suggestions - diff_note_positions - review +commit_notes: +- award_emoji +- noteable +- author +- updated_by +- last_edited_by +- resolved_by +- todos +- events +- system_note_metadata +- note_diff_file +- suggestions +- diff_note_positions +- review label_links: - target - label diff --git a/spec/lib/gitlab/import_export/fast_hash_serializer_spec.rb b/spec/lib/gitlab/import_export/fast_hash_serializer_spec.rb index 7cc503eb66b..02419267f0e 100644 --- a/spec/lib/gitlab/import_export/fast_hash_serializer_spec.rb +++ b/spec/lib/gitlab/import_export/fast_hash_serializer_spec.rb @@ -141,8 +141,8 @@ RSpec.describe Gitlab::ImportExport::FastHashSerializer, :with_license, feature_ expect(subject['ci_pipelines']).not_to be_empty end - it 'has ci pipeline notes' do - expect(subject['ci_pipelines'].first['notes']).not_to be_empty + it 'has commit notes' do + expect(subject['commit_notes']).not_to be_empty end it 'has labels with no associations' do diff --git a/spec/lib/gitlab/import_export/project/relation_tree_restorer_spec.rb b/spec/lib/gitlab/import_export/project/relation_tree_restorer_spec.rb index 6053df8ba97..75012aa80ec 100644 --- a/spec/lib/gitlab/import_export/project/relation_tree_restorer_spec.rb +++ b/spec/lib/gitlab/import_export/project/relation_tree_restorer_spec.rb @@ -50,6 +50,7 @@ RSpec.describe Gitlab::ImportExport::Project::RelationTreeRestorer, feature_cate expect(project.custom_attributes.count).to eq(2) expect(project.project_badges.count).to eq(2) expect(project.snippets.count).to eq(1) + expect(project.commit_notes.count).to eq(3) end end end diff --git a/spec/lib/gitlab/import_export/project/tree_restorer_spec.rb b/spec/lib/gitlab/import_export/project/tree_restorer_spec.rb index 85d8809e4c5..bb735d6689e 100644 --- a/spec/lib/gitlab/import_export/project/tree_restorer_spec.rb +++ b/spec/lib/gitlab/import_export/project/tree_restorer_spec.rb @@ -582,6 +582,10 @@ RSpec.describe Gitlab::ImportExport::Project::TreeRestorer, feature_category: :i expect(@project.import_failures.size).to eq 0 end end + + it 'restores commit notes' do + expect(@project.commit_notes.count).to eq(3) + end end end diff --git a/spec/lib/gitlab/import_export/project/tree_saver_spec.rb b/spec/lib/gitlab/import_export/project/tree_saver_spec.rb index adb4b0e382a..b87992c4594 100644 --- a/spec/lib/gitlab/import_export/project/tree_saver_spec.rb +++ b/spec/lib/gitlab/import_export/project/tree_saver_spec.rb @@ -240,10 +240,12 @@ RSpec.describe Gitlab::ImportExport::Project::TreeSaver, :with_license, feature_ expect(count).to eq(1) end + end - it 'has ci pipeline notes' do - expect(subject.first['notes']).not_to be_empty - end + context 'with commit_notes' do + let(:relation_name) { :commit_notes } + + it { is_expected.not_to be_empty } end context 'with labels' do @@ -475,6 +477,7 @@ RSpec.describe Gitlab::ImportExport::Project::TreeSaver, :with_license, feature_ end end + # rubocop: disable Metrics/AbcSize def setup_project release = create(:release) @@ -537,4 +540,5 @@ RSpec.describe Gitlab::ImportExport::Project::TreeSaver, :with_license, feature_ project end + # rubocop: enable Metrics/AbcSize end diff --git a/spec/lib/gitlab/import_export/references_configuration_spec.rb b/spec/lib/gitlab/import_export/references_configuration_spec.rb index ad165790b77..84c5b564cb1 100644 --- a/spec/lib/gitlab/import_export/references_configuration_spec.rb +++ b/spec/lib/gitlab/import_export/references_configuration_spec.rb @@ -9,7 +9,7 @@ require 'spec_helper' # or to be blacklisted by using the import_export.yml configuration file. # Likewise, new models added to import_export.yml, will need to be added with their correspondent relations # to this spec. -RSpec.describe 'Import/Export Project configuration' do +RSpec.describe 'Import/Export Project configuration', feature_category: :importers do include ConfigurationHelper where(:relation_path, :relation_name) do diff --git a/spec/lib/gitlab/import_export/safe_model_attributes.yml b/spec/lib/gitlab/import_export/safe_model_attributes.yml index 5c43cfec612..2384baabb6b 100644 --- a/spec/lib/gitlab/import_export/safe_model_attributes.yml +++ b/spec/lib/gitlab/import_export/safe_model_attributes.yml @@ -86,6 +86,7 @@ Note: - original_discussion_id - confidential - last_edited_at +- internal LabelLink: - id - target_type diff --git a/spec/lib/gitlab/usage_data_spec.rb b/spec/lib/gitlab/usage_data_spec.rb index 5325ef5b5dd..a2461545a56 100644 --- a/spec/lib/gitlab/usage_data_spec.rb +++ b/spec/lib/gitlab/usage_data_spec.rb @@ -1097,7 +1097,6 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures, feature_category: :servic it 'returns the distinct count of user actions within the specified time period' do expect(described_class.action_monthly_active_users(time_period)).to eq( { - action_monthly_active_users_web_ide_edit: 2, action_monthly_active_users_sfe_edit: 2, action_monthly_active_users_snippet_editor_edit: 2 } diff --git a/spec/models/airflow/dags_spec.rb b/spec/models/airflow/dags_spec.rb deleted file mode 100644 index ff3c4522779..00000000000 --- a/spec/models/airflow/dags_spec.rb +++ /dev/null @@ -1,17 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe Airflow::Dags, feature_category: :dataops do - describe 'associations' do - it { is_expected.to belong_to(:project) } - end - - describe 'validations' do - it { is_expected.to validate_presence_of(:project) } - it { is_expected.to validate_presence_of(:dag_name) } - it { is_expected.to validate_length_of(:dag_name).is_at_most(255) } - it { is_expected.to validate_length_of(:schedule).is_at_most(255) } - it { is_expected.to validate_length_of(:fileloc).is_at_most(255) } - end -end diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 5304fec506e..9db1dde6294 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -939,6 +939,17 @@ RSpec.describe Project, factory_default: :keep, feature_category: :projects do end end + describe '#commit_notes' do + let_it_be(:project) { create(:project) } + + it "returns project's commit notes" do + note_1 = create(:note_on_commit, project: project, commit_id: 'commit_id_1') + note_2 = create(:note_on_commit, project: project, commit_id: 'commit_id_2') + + expect(project.commit_notes).to match_array([note_1, note_2]) + end + end + describe '#personal_namespace_holder?' do let_it_be(:group) { create(:group) } let_it_be(:namespace_user) { create(:user) } diff --git a/spec/requests/projects/airflow/dags_controller_spec.rb b/spec/requests/projects/airflow/dags_controller_spec.rb deleted file mode 100644 index 2dcedf5f128..00000000000 --- a/spec/requests/projects/airflow/dags_controller_spec.rb +++ /dev/null @@ -1,105 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe Projects::Airflow::DagsController, feature_category: :dataops do - let_it_be(:non_member) { create(:user) } - let_it_be(:user) { create(:user) } - let_it_be(:group) { create(:group).tap { |p| p.add_developer(user) } } - let_it_be(:project) { create(:project, group: group).tap { |p| p.add_developer(user) } } - - let(:current_user) { user } - let(:feature_flag) { true } - - let_it_be(:dags) do - create_list(:airflow_dags, 5, project: project) - end - - let(:params) { { namespace_id: project.namespace.to_param, project_id: project } } - let(:extra_params) { {} } - - before do - sign_in(current_user) if current_user - stub_feature_flags(airflow_dags: false) - stub_feature_flags(airflow_dags: project) if feature_flag - list_dags - end - - shared_examples 'returns a 404 if feature flag disabled' do - context 'when :airflow_dags disabled' do - let(:feature_flag) { false } - - it 'is 404' do - expect(response).to have_gitlab_http_status(:not_found) - end - end - end - - describe 'GET index' do - it 'renders the template' do - expect(response).to render_template('projects/airflow/dags/index') - end - - describe 'pagination' do - before do - stub_const("Projects::Airflow::DagsController::MAX_DAGS_PER_PAGE", 2) - dags - - list_dags - end - - context 'when out of bounds' do - let(:params) { extra_params.merge(page: 10000) } - - it 'redirects to last page' do - last_page = (dags.size + 1) / 2 - expect(response).to redirect_to(project_airflow_dags_path(project, page: last_page)) - end - end - - context 'when bad page' do - let(:params) { extra_params.merge(page: 's') } - - it 'uses first page' do - expect(assigns(:pagination)).to include( - page: 1, - is_last_page: false, - per_page: 2, - total_items: dags.size) - end - end - end - - it 'does not perform N+1 sql queries' do - control_count = ActiveRecord::QueryRecorder.new(skip_cached: false) { list_dags } - - create_list(:airflow_dags, 1, project: project) - - expect { list_dags }.not_to exceed_all_query_limit(control_count) - end - - context 'when user is not logged in' do - let(:current_user) { nil } - - it 'redirects to login' do - expect(response).to redirect_to(new_user_session_path) - end - end - - context 'when user is not a member' do - let(:current_user) { non_member } - - it 'returns a 404' do - expect(response).to have_gitlab_http_status(:not_found) - end - end - - it_behaves_like 'returns a 404 if feature flag disabled' - end - - private - - def list_dags - get project_airflow_dags_path(project), params: params - end -end diff --git a/spec/support/shared_contexts/navbar_structure_context.rb b/spec/support/shared_contexts/navbar_structure_context.rb index b74819d2ac7..08da55162b4 100644 --- a/spec/support/shared_contexts/navbar_structure_context.rb +++ b/spec/support/shared_contexts/navbar_structure_context.rb @@ -85,8 +85,7 @@ RSpec.shared_context 'project navbar structure' do _('Metrics'), _('Error Tracking'), _('Alerts'), - _('Incidents'), - _('Airflow') + _('Incidents') ] }, { |