From 759cd6c2985088d187ed519f2a881c2c690b34ec Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Thu, 26 Sep 2019 09:06:04 +0000 Subject: Add latest changes from gitlab-org/gitlab@master --- .eslintrc.yml | 1 - .rubocop_todo.yml | 13 --- app/assets/javascripts/jobs/store/utils.js | 31 ++++++ app/models/audit_event.rb | 9 ++ app/models/ci/pipeline.rb | 6 +- app/models/concerns/mentionable.rb | 2 +- app/models/project_services/hipchat_service.rb | 2 +- app/models/project_services/irker_service.rb | 4 +- app/models/project_services/jira_service.rb | 2 +- app/models/project_services/packagist_service.rb | 2 +- app/models/wiki_page.rb | 6 +- app/views/projects/diffs/_stats.html.haml | 4 +- ...and-timestamps-to-design-management-version.yml | 5 + .../unreleased/29284-video-preview-not-working.yml | 5 + .../31118-do-not-use-blob-for-file-path.yml | 5 + ...d-data-to-environments-and-deployments-apis.yml | 5 + ...and_timestamps_to_design_management_versions.rb | 16 +++ ...d_user_indexes_to_design_management_versions.rb | 19 ++++ db/schema.rb | 6 +- doc/api/api_resources.md | 1 + doc/api/audit_events.md | 115 +++++++++++++++++++++ doc/api/environments.md | 14 ++- lib/api/entities.rb | 2 + lib/banzai/filter/video_link_filter.rb | 22 +--- lib/gitlab/diff/file.rb | 2 +- .../github_import/importer/releases_importer.rb | 6 +- .../projects/commits/user_browses_commits_spec.rb | 4 +- spec/fixtures/api/schemas/deployment.json | 3 +- .../api/schemas/public_api/v4/deployment.json | 3 +- .../api/schemas/public_api/v4/environment.json | 3 +- spec/frontend/jobs/store/utils_spec.js | 86 +++++++++++++++ spec/lib/banzai/filter/video_link_filter_spec.rb | 35 ++++--- 32 files changed, 359 insertions(+), 80 deletions(-) create mode 100644 changelogs/unreleased/13426-add-user-and-timestamps-to-design-management-version.yml create mode 100644 changelogs/unreleased/29284-video-preview-not-working.yml create mode 100644 changelogs/unreleased/31118-do-not-use-blob-for-file-path.yml create mode 100644 changelogs/unreleased/5582-add-missing-actions-and-data-to-environments-and-deployments-apis.yml create mode 100644 db/migrate/20190918025618_add_user_and_timestamps_to_design_management_versions.rb create mode 100644 db/migrate/20190926041216_add_user_indexes_to_design_management_versions.rb create mode 100644 doc/api/audit_events.md diff --git a/.eslintrc.yml b/.eslintrc.yml index 37499eff807..8000bcd4361 100644 --- a/.eslintrc.yml +++ b/.eslintrc.yml @@ -46,7 +46,6 @@ rules: promise/always-return: off promise/no-callback-in-promise: off promise/no-nesting: off - promise/valid-params: off overrides: files: - '**/spec/**/*' diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 48b840b4af2..81bb28dacab 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -442,19 +442,6 @@ Rails/LinkToBlank: - 'ee/app/helpers/ee/user_callouts_helper.rb' - 'ee/app/helpers/license_helper.rb' -# Offense count: 11 -# Cop supports --auto-correct. -Rails/Presence: - Exclude: - - 'app/models/ci/pipeline.rb' - - 'app/models/concerns/mentionable.rb' - - 'app/models/project_services/hipchat_service.rb' - - 'app/models/project_services/irker_service.rb' - - 'app/models/project_services/jira_service.rb' - - 'app/models/project_services/packagist_service.rb' - - 'app/models/wiki_page.rb' - - 'lib/gitlab/github_import/importer/releases_importer.rb' - # Offense count: 1 # Cop supports --auto-correct. # Configuration parameters: Include. diff --git a/app/assets/javascripts/jobs/store/utils.js b/app/assets/javascripts/jobs/store/utils.js index 3838c759354..fd2af0e421b 100644 --- a/app/assets/javascripts/jobs/store/utils.js +++ b/app/assets/javascripts/jobs/store/utils.js @@ -55,6 +55,37 @@ export const logLinesParser = (lines = [], lineNumberStart) => return acc; }, []); +/** + * Finds the repeated offset, removes the old one + * + * Returns a new array with the updated log without + * the repeated offset + * + * @param Array newLog + * @param Array oldParsed + * @returns Array + * + */ +export const findOffsetAndRemove = (newLog, oldParsed) => { + const cloneOldLog = [...oldParsed]; + const lastIndex = cloneOldLog.length - 1; + const last = cloneOldLog[lastIndex]; + + const firstNew = newLog[0]; + + if (last.offset === firstNew.offset || (last.line && last.line.offset === firstNew.offset)) { + cloneOldLog.splice(lastIndex); + } else if (last.lines && last.lines.length) { + const lastNestedIndex = last.lines.length - 1; + const lastNested = last.lines[lastNestedIndex]; + if (lastNested.offset === firstNew.offset) { + last.lines.splice(lastNestedIndex); + } + } + + return cloneOldLog; +}; + /** * When the trace is not complete, backend may send the last received line * in the new response. diff --git a/app/models/audit_event.rb b/app/models/audit_event.rb index c2eef500fb0..06a607b75a4 100644 --- a/app/models/audit_event.rb +++ b/app/models/audit_event.rb @@ -1,6 +1,8 @@ # frozen_string_literal: true class AuditEvent < ApplicationRecord + include CreatedAtFilterable + serialize :details, Hash # rubocop:disable Cop/ActiveRecordSerialize belongs_to :user, foreign_key: :author_id @@ -9,6 +11,9 @@ class AuditEvent < ApplicationRecord validates :entity_id, presence: true validates :entity_type, presence: true + scope :by_entity_type, -> (entity_type) { where(entity_type: entity_type) } + scope :by_entity_id, -> (entity_id) { where(entity_id: entity_id) } + after_initialize :initialize_details def initialize_details @@ -18,6 +23,10 @@ class AuditEvent < ApplicationRecord def author_name self.user.name end + + def formatted_details + details.merge(details.slice(:from, :to).transform_values(&:to_s)) + end end AuditEvent.prepend_if_ee('EE::AuditEvent') diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb index 49656a32c03..12295c0457d 100644 --- a/app/models/ci/pipeline.rb +++ b/app/models/ci/pipeline.rb @@ -584,11 +584,7 @@ module Ci def ci_yaml_file_path return unless repository_source? || unknown_source? - if project.ci_config_path.blank? - '.gitlab-ci.yml' - else - project.ci_config_path - end + project.ci_config_path.presence || '.gitlab-ci.yml' end def ci_yaml_file diff --git a/app/models/concerns/mentionable.rb b/app/models/concerns/mentionable.rb index 377600ef6e5..9b6c57261d8 100644 --- a/app/models/concerns/mentionable.rb +++ b/app/models/concerns/mentionable.rb @@ -150,7 +150,7 @@ module Mentionable # # Returns a Hash. def detect_mentionable_changes - source = (changes.present? ? changes : previous_changes).dup + source = (changes.presence || previous_changes).dup mentionable = self.class.mentionable_attrs.map { |attr, options| attr } diff --git a/app/models/project_services/hipchat_service.rb b/app/models/project_services/hipchat_service.rb index 3320405e9e9..1d0b37abf72 100644 --- a/app/models/project_services/hipchat_service.rb +++ b/app/models/project_services/hipchat_service.rb @@ -73,7 +73,7 @@ class HipchatService < Service private def gate - options = { api_version: api_version.present? ? api_version : 'v2' } + options = { api_version: api_version.presence || 'v2' } options[:server_url] = server unless server.blank? @gate ||= HipChat::Client.new(token, options) end diff --git a/app/models/project_services/irker_service.rb b/app/models/project_services/irker_service.rb index fb76bc89c98..4a6c8339625 100644 --- a/app/models/project_services/irker_service.rb +++ b/app/models/project_services/irker_service.rb @@ -36,8 +36,8 @@ class IrkerService < Service def settings { - server_host: server_host.present? ? server_host : 'localhost', - server_port: server_port.present? ? server_port : 6659 + server_host: server_host.presence || 'localhost', + server_port: server_port.presence || 6659 } end diff --git a/app/models/project_services/jira_service.rb b/app/models/project_services/jira_service.rb index 4a02da975f2..cfdf55b5155 100644 --- a/app/models/project_services/jira_service.rb +++ b/app/models/project_services/jira_service.rb @@ -337,7 +337,7 @@ class JiraService < IssueTrackerService end def client_url - api_url.present? ? api_url : url + api_url.presence || url end def reset_password? diff --git a/app/models/project_services/packagist_service.rb b/app/models/project_services/packagist_service.rb index 003884bb7ac..35dbedd1341 100644 --- a/app/models/project_services/packagist_service.rb +++ b/app/models/project_services/packagist_service.rb @@ -59,7 +59,7 @@ class PackagistService < Service end def hook_url - base_url = server.present? ? server : 'https://packagist.org' + base_url = server.presence || 'https://packagist.org' "#{base_url}/api/update-package?username=#{username}&apiToken=#{token}" end end diff --git a/app/models/wiki_page.rb b/app/models/wiki_page.rb index cd4c7895587..1fa29e5b933 100644 --- a/app/models/wiki_page.rb +++ b/app/models/wiki_page.rb @@ -77,11 +77,7 @@ class WikiPage # The escaped URL path of this page. def slug - if @attributes[:slug].present? - @attributes[:slug] - else - wiki.wiki.preview_slug(title, format) - end + @attributes[:slug].presence || wiki.wiki.preview_slug(title, format) end alias_method :to_param, :slug diff --git a/app/views/projects/diffs/_stats.html.haml b/app/views/projects/diffs/_stats.html.haml index c9057f385da..86e6e732610 100644 --- a/app/views/projects/diffs/_stats.html.haml +++ b/app/views/projects/diffs/_stats.html.haml @@ -24,9 +24,9 @@ %a.diff-changed-file{ href: "##{hexdigest(diff_file.file_path)}", title: diff_file.new_path } = sprite_icon(diff_file_changed_icon(diff_file), size: 16, css_class: "#{diff_file_changed_icon_color(diff_file)} diff-file-changed-icon append-right-8") %span.diff-changed-file-content.append-right-8 - - if diff_file.blob&.name + - if diff_file.file_path %strong.diff-changed-file-name - = diff_file.blob.name + = diff_file.file_path - else %strong.diff-changed-blank-file-name = s_('Diffs|No file name available') diff --git a/changelogs/unreleased/13426-add-user-and-timestamps-to-design-management-version.yml b/changelogs/unreleased/13426-add-user-and-timestamps-to-design-management-version.yml new file mode 100644 index 00000000000..2f56de32b2d --- /dev/null +++ b/changelogs/unreleased/13426-add-user-and-timestamps-to-design-management-version.yml @@ -0,0 +1,5 @@ +--- +title: Add user_id and created_at columns to design_management_versions table +merge_request: 17316 +author: +type: added diff --git a/changelogs/unreleased/29284-video-preview-not-working.yml b/changelogs/unreleased/29284-video-preview-not-working.yml new file mode 100644 index 00000000000..304700dea30 --- /dev/null +++ b/changelogs/unreleased/29284-video-preview-not-working.yml @@ -0,0 +1,5 @@ +--- +title: Fix inline rendering of videos for uploads with uppercase file extensions +merge_request: 17581 +author: +type: fixed diff --git a/changelogs/unreleased/31118-do-not-use-blob-for-file-path.yml b/changelogs/unreleased/31118-do-not-use-blob-for-file-path.yml new file mode 100644 index 00000000000..454ad15ee46 --- /dev/null +++ b/changelogs/unreleased/31118-do-not-use-blob-for-file-path.yml @@ -0,0 +1,5 @@ +--- +title: Reduce Gitaly calls when viewing a commit +merge_request: 17095 +author: +type: performance diff --git a/changelogs/unreleased/5582-add-missing-actions-and-data-to-environments-and-deployments-apis.yml b/changelogs/unreleased/5582-add-missing-actions-and-data-to-environments-and-deployments-apis.yml new file mode 100644 index 00000000000..7c90834385f --- /dev/null +++ b/changelogs/unreleased/5582-add-missing-actions-and-data-to-environments-and-deployments-apis.yml @@ -0,0 +1,5 @@ +--- +title: Add status to deployments and state to environments in API responses +merge_request: 16242 +author: +type: changed diff --git a/db/migrate/20190918025618_add_user_and_timestamps_to_design_management_versions.rb b/db/migrate/20190918025618_add_user_and_timestamps_to_design_management_versions.rb new file mode 100644 index 00000000000..3daca8a8e97 --- /dev/null +++ b/db/migrate/20190918025618_add_user_and_timestamps_to_design_management_versions.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +class AddUserAndTimestampsToDesignManagementVersions < ActiveRecord::Migration[5.2] + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + + def up + add_column :design_management_versions, :user_id, :integer + add_column :design_management_versions, :created_at, :datetime_with_timezone + end + + def down + remove_columns :design_management_versions, :user_id, :created_at + end +end diff --git a/db/migrate/20190926041216_add_user_indexes_to_design_management_versions.rb b/db/migrate/20190926041216_add_user_indexes_to_design_management_versions.rb new file mode 100644 index 00000000000..6eb9fdbbcaa --- /dev/null +++ b/db/migrate/20190926041216_add_user_indexes_to_design_management_versions.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +class AddUserIndexesToDesignManagementVersions < ActiveRecord::Migration[5.2] + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + + disable_ddl_transaction! + + def up + add_concurrent_foreign_key :design_management_versions, :users, column: :user_id, on_delete: :nullify + add_concurrent_index :design_management_versions, :user_id, where: 'user_id IS NOT NULL' + end + + def down + remove_concurrent_index :design_management_versions, :user_id + remove_foreign_key :design_management_versions, column: :user_id + end +end diff --git a/db/schema.rb b/db/schema.rb index 6674e00950a..aff672919c1 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2019_09_19_162036) do +ActiveRecord::Schema.define(version: 2019_09_26_041216) do # These are extensions that must be enabled in order to support this database enable_extension "pg_trgm" @@ -1232,8 +1232,11 @@ ActiveRecord::Schema.define(version: 2019_09_19_162036) do create_table "design_management_versions", force: :cascade do |t| t.binary "sha", null: false t.bigint "issue_id" + t.integer "user_id" + t.datetime_with_timezone "created_at" t.index ["issue_id"], name: "index_design_management_versions_on_issue_id" t.index ["sha", "issue_id"], name: "index_design_management_versions_on_sha_and_issue_id", unique: true + t.index ["user_id"], name: "index_design_management_versions_on_user_id", where: "(user_id IS NOT NULL)" end create_table "draft_notes", force: :cascade do |t| @@ -3930,6 +3933,7 @@ ActiveRecord::Schema.define(version: 2019_09_19_162036) do add_foreign_key "design_management_designs_versions", "design_management_designs", column: "design_id", name: "fk_03c671965c", on_delete: :cascade add_foreign_key "design_management_designs_versions", "design_management_versions", column: "version_id", name: "fk_f4d25ba00c", on_delete: :cascade add_foreign_key "design_management_versions", "issues", on_delete: :cascade + add_foreign_key "design_management_versions", "users", name: "fk_ee16b939e5", on_delete: :nullify add_foreign_key "draft_notes", "merge_requests", on_delete: :cascade add_foreign_key "draft_notes", "users", column: "author_id", on_delete: :cascade add_foreign_key "elasticsearch_indexed_namespaces", "namespaces", on_delete: :cascade diff --git a/doc/api/api_resources.md b/doc/api/api_resources.md index e2ddc2cbc18..232a9825691 100644 --- a/doc/api/api_resources.md +++ b/doc/api/api_resources.md @@ -104,6 +104,7 @@ The following API resources are available outside of project and group contexts | Resource | Available endpoints | |:--------------------------------------------------|:------------------------------------------------------------------------| | [Applications](applications.md) | `/applications` | +| [Audit Events](audit_events.md) **(PREMIUM ONLY)** | `/audit_events` | | [Avatar](avatar.md) | `/avatar` | | [Broadcast messages](broadcast_messages.md) | `/broadcast_messages` | | [Code snippets](snippets.md) | `/snippets` | diff --git a/doc/api/audit_events.md b/doc/api/audit_events.md new file mode 100644 index 00000000000..aca221cf990 --- /dev/null +++ b/doc/api/audit_events.md @@ -0,0 +1,115 @@ +# Audit Events API **(PREMIUM ONLY)** + +The Audit Events API allows you to retrieve [instance audit events](../administration/audit_events.md#instance-events-premium-only). + +To retrieve audit events using the API, you must [authenticate yourself](README.html#authentication) as an Administrator. + +## Retrieve all instance audit events + +``` +GET /audit_events +``` + +| Attribute | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `created_after` | string | no | Return audit events created on or after the given time. Format: ISO 8601 YYYY-MM-DDTHH:MM:SSZ | +| `created_before` | string | no | Return audit events created on or before the given time. Format: ISO 8601 YYYY-MM-DDTHH:MM:SSZ | +| `entity_type` | string | no | Return audit events for the given entity type. Valid values are: `User`, `Group`, or `Project`. | +| `entity_id` | boolean | no | Return audit events for the given entity ID. Requires `entity_type` attribute to be present. | + +By default, `GET` requests return 20 results at a time because the API results +are paginated. + +Read more on [pagination](README.md#pagination). + +```bash +curl --header "PRIVATE-TOKEN: " https://primary.example.com/api/v4/audit_events +``` + +Example response: + +```json +[ + { + "id": 1, + "author_id": 1, + "entity_id": 6, + "entity_type": "Project", + "details": { + "custom_message": "Project archived", + "author_name": "Administrator", + "target_id": "flightjs/flight", + "target_type": "Project", + "target_details": "flightjs/flight", + "ip_address": "127.0.0.1", + "entity_path": "flightjs/flight" + }, + "created_at": "2019-08-30T07:00:41.885Z" + }, + { + "id": 2, + "author_id": 1, + "entity_id": 60, + "entity_type": "Group", + "details": { + "add": "group", + "author_name": "Administrator", + "target_id": "flightjs", + "target_type": "Group", + "target_details": "flightjs", + "ip_address": "127.0.0.1", + "entity_path": "flightjs" + }, + "created_at": "2019-08-27T18:36:44.162Z" + }, + { + "id": 3, + "author_id": 51, + "entity_id": 51, + "entity_type": "User", + "details": { + "change": "email address", + "from": "hello@flightjs.com", + "to": "maintainer@flightjs.com", + "author_name": "Andreas", + "target_id": 51, + "target_type": "User", + "target_details": "Andreas", + "ip_address": null, + "entity_path": "Andreas" + }, + "created_at": "2019-08-22T16:34:25.639Z" + } +] +``` + +## Retrieve single instance audit event + +``` +GET /audit_events/:id +``` + +```bash +curl --header "PRIVATE-TOKEN: " https://primary.example.com/api/v4/audit_events/1 +``` + +Example response: + +```json +{ + "id": 1, + "author_id": 1, + "entity_id": 6, + "entity_type": "Project", + "details": { + "custom_message": "Project archived", + "author_name": "Administrator", + "target_id": "flightjs/flight", + "target_type": "Project", + "target_details": "flightjs/flight", + "ip_address": "127.0.0.1", + "entity_path": "flightjs/flight" + }, + "created_at": "2019-08-30T07:00:41.885Z" +} +``` diff --git a/doc/api/environments.md b/doc/api/environments.md index 26ee075c921..3f46c11ed69 100644 --- a/doc/api/environments.md +++ b/doc/api/environments.md @@ -26,7 +26,8 @@ Example response: "id": 1, "name": "review/fix-foo", "slug": "review-fix-foo-dfjre3", - "external_url": "https://review-fix-foo-dfjre3.example.gitlab.com" + "external_url": "https://review-fix-foo-dfjre3.example.gitlab.com", + "state": "available" } ] ``` @@ -54,12 +55,14 @@ Example of response "name": "review/fix-foo", "slug": "review-fix-foo-dfjre3", "external_url": "https://review-fix-foo-dfjre3.example.gitlab.com" + "state": "available", "last_deployment": { "id": 100, "iid": 34, "ref": "fdroid", "sha": "416d8ea11849050d3d1f5104cf8cf51053e790ab", "created_at": "2019-03-25T18:55:13.252Z", + "status": "success", "user": { "id": 1, "name": "Administrator", @@ -163,7 +166,8 @@ Example response: "id": 1, "name": "deploy", "slug": "deploy", - "external_url": "https://deploy.example.gitlab.com" + "external_url": "https://deploy.example.gitlab.com", + "state": "available" } ``` @@ -195,7 +199,8 @@ Example response: "id": 1, "name": "staging", "slug": "staging", - "external_url": "https://staging.example.gitlab.com" + "external_url": "https://staging.example.gitlab.com", + "state": "available" } ``` @@ -240,6 +245,7 @@ Example response: "id": 1, "name": "deploy", "slug": "deploy", - "external_url": "https://deploy.example.gitlab.com" + "external_url": "https://deploy.example.gitlab.com", + "state": "stopped" } ``` diff --git a/lib/api/entities.rb b/lib/api/entities.rb index 9d393f0217b..72bbba8065e 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -1470,11 +1470,13 @@ module API expose :user, using: Entities::UserBasic expose :environment, using: Entities::EnvironmentBasic expose :deployable, using: Entities::Job + expose :status end class Environment < EnvironmentBasic expose :project, using: Entities::BasicProjectDetails expose :last_deployment, using: Entities::Deployment, if: { last_deployment: true } + expose :state end class LicenseBasic < Grape::Entity diff --git a/lib/banzai/filter/video_link_filter.rb b/lib/banzai/filter/video_link_filter.rb index 58006cc6c13..bdd257a5e67 100644 --- a/lib/banzai/filter/video_link_filter.rb +++ b/lib/banzai/filter/video_link_filter.rb @@ -8,8 +8,8 @@ module Banzai # a "Download" link in the case the video cannot be played. class VideoLinkFilter < HTML::Pipeline::Filter def call - doc.xpath(query).each do |el| - el.replace(video_node(doc, el)) + doc.xpath('descendant-or-self::img[not(ancestor::a)]').each do |el| + el.replace(video_node(doc, el)) if has_video_extension?(el) end doc @@ -17,22 +17,10 @@ module Banzai private - def query - @query ||= begin - src_query = UploaderHelper::SAFE_VIDEO_EXT.map do |ext| - "'.#{ext}' = substring(@src, string-length(@src) - #{ext.size})" - end + def has_video_extension?(element) + src_attr = context[:asset_proxy_enabled] ? 'data-canonical-src' : 'src' - if context[:asset_proxy_enabled].present? - src_query.concat( - UploaderHelper::SAFE_VIDEO_EXT.map do |ext| - "'.#{ext}' = substring(@data-canonical-src, string-length(@data-canonical-src) - #{ext.size})" - end - ) - end - - "descendant-or-self::img[not(ancestor::a) and (#{src_query.join(' or ')})]" - end + element.attr(src_attr).downcase.end_with?(*UploaderHelper::SAFE_VIDEO_EXT) end def video_node(doc, element) diff --git a/lib/gitlab/diff/file.rb b/lib/gitlab/diff/file.rb index c46087e65de..30fe7440148 100644 --- a/lib/gitlab/diff/file.rb +++ b/lib/gitlab/diff/file.rb @@ -428,8 +428,8 @@ module Gitlab def viewer_class_from(classes) return unless diffable? - return if different_type? || external_storage_error? return unless new_file? || deleted_file? || content_changed? + return if different_type? || external_storage_error? verify_binary = !stored_externally? diff --git a/lib/gitlab/github_import/importer/releases_importer.rb b/lib/gitlab/github_import/importer/releases_importer.rb index fe3a8d4aea5..1881f715c99 100644 --- a/lib/gitlab/github_import/importer/releases_importer.rb +++ b/lib/gitlab/github_import/importer/releases_importer.rb @@ -47,11 +47,7 @@ module Gitlab end def description_for(release) - if release.body.present? - release.body - else - "Release for tag #{release.tag_name}" - end + release.body.presence || "Release for tag #{release.tag_name}" end end end diff --git a/spec/features/projects/commits/user_browses_commits_spec.rb b/spec/features/projects/commits/user_browses_commits_spec.rb index 085d8d63d52..131d9097f48 100644 --- a/spec/features/projects/commits/user_browses_commits_spec.rb +++ b/spec/features/projects/commits/user_browses_commits_spec.rb @@ -93,13 +93,13 @@ describe 'User browses commits' do context 'when the blob does not exist' do let(:commit) { create(:commit, project: project) } - it 'shows a blank label' do + it 'renders successfully' do allow_any_instance_of(Gitlab::Diff::File).to receive(:blob).and_return(nil) allow_any_instance_of(Gitlab::Diff::File).to receive(:binary?).and_return(true) visit(project_commit_path(project, commit)) - expect(find('.diff-file-changes', visible: false)).to have_content('No file name available') + expect(find('.diff-file-changes', visible: false)).to have_content('files/ruby/popen.rb') end end diff --git a/spec/fixtures/api/schemas/deployment.json b/spec/fixtures/api/schemas/deployment.json index 81c2d1ef5ab..b1e3c000ddf 100644 --- a/spec/fixtures/api/schemas/deployment.json +++ b/spec/fixtures/api/schemas/deployment.json @@ -60,7 +60,8 @@ "scheduled_actions": { "type": "array", "items": { "$ref": "job/job.json" } - } + }, + "status": { "type": "string" } }, "additionalProperties": false } diff --git a/spec/fixtures/api/schemas/public_api/v4/deployment.json b/spec/fixtures/api/schemas/public_api/v4/deployment.json index 3af2dc27d55..13b10eac625 100644 --- a/spec/fixtures/api/schemas/public_api/v4/deployment.json +++ b/spec/fixtures/api/schemas/public_api/v4/deployment.json @@ -26,7 +26,8 @@ { "type": "null" }, { "$ref": "job.json" } ] - } + }, + "status": { "type": "string" } }, "additionalProperties": false } diff --git a/spec/fixtures/api/schemas/public_api/v4/environment.json b/spec/fixtures/api/schemas/public_api/v4/environment.json index 242e90fb7ac..57352017f03 100644 --- a/spec/fixtures/api/schemas/public_api/v4/environment.json +++ b/spec/fixtures/api/schemas/public_api/v4/environment.json @@ -17,7 +17,8 @@ { "type": "null" }, { "$ref": "deployment.json" } ] - } + }, + "state": { "type": "string" } }, "additionalProperties": false } diff --git a/spec/frontend/jobs/store/utils_spec.js b/spec/frontend/jobs/store/utils_spec.js index 1e885b6b788..bb45fcb7435 100644 --- a/spec/frontend/jobs/store/utils_spec.js +++ b/spec/frontend/jobs/store/utils_spec.js @@ -3,6 +3,7 @@ import { updateIncrementalTrace, parseHeaderLine, parseLine, + findOffsetAndRemove, } from '~/jobs/store/utils'; import { utilsMockData, @@ -83,6 +84,91 @@ describe('Jobs Store Utils', () => { }); }); + describe('findOffsetAndRemove', () => { + describe('when last item is header', () => { + const existingLog = [ + { + isHeader: true, + isClosed: true, + line: { content: [{ text: 'bar' }], offset: 10, lineNumber: 1 }, + }, + ]; + + describe('and matches the offset', () => { + it('returns an array with the item removed', () => { + const newData = [{ offset: 10, content: [{ text: 'foobar' }] }]; + const result = findOffsetAndRemove(newData, existingLog); + + expect(result).toEqual([]); + }); + }); + + describe('and does not match the offset', () => { + it('returns the provided existing log', () => { + const newData = [{ offset: 110, content: [{ text: 'foobar' }] }]; + const result = findOffsetAndRemove(newData, existingLog); + + expect(result).toEqual(existingLog); + }); + }); + }); + + describe('when last item is a regular line', () => { + const existingLog = [{ content: [{ text: 'bar' }], offset: 10, lineNumber: 1 }]; + + describe('and matches the offset', () => { + it('returns an array with the item removed', () => { + const newData = [{ offset: 10, content: [{ text: 'foobar' }] }]; + const result = findOffsetAndRemove(newData, existingLog); + + expect(result).toEqual([]); + }); + }); + + describe('and does not match the fofset', () => { + it('returns the provided old log', () => { + const newData = [{ offset: 101, content: [{ text: 'foobar' }] }]; + const result = findOffsetAndRemove(newData, existingLog); + + expect(result).toEqual(existingLog); + }); + }); + }); + + describe('when last item is nested', () => { + const existingLog = [ + { + isHeader: true, + isClosed: true, + lines: [{ offset: 101, content: [{ text: 'foobar' }], lineNumber: 2 }], + line: { + offset: 10, + lineNumber: 1, + section_duration: '10:00', + }, + }, + ]; + + describe('and matches the offset', () => { + it('returns an array with the last nested line item removed', () => { + const newData = [{ offset: 101, content: [{ text: 'foobar' }] }]; + + const result = findOffsetAndRemove(newData, existingLog); + expect(result[0].lines).toEqual([]); + }); + }); + + describe('and does not match the offset', () => { + it('returns the provided old log', () => { + const newData = [{ offset: 120, content: [{ text: 'foobar' }] }]; + + const result = findOffsetAndRemove(newData, existingLog); + expect(result).toEqual(existingLog); + }); + }); + }); + }); + describe('updateIncrementalTrace', () => { describe('without repeated section', () => { it('concats and parses both arrays', () => { diff --git a/spec/lib/banzai/filter/video_link_filter_spec.rb b/spec/lib/banzai/filter/video_link_filter_spec.rb index b5be204d680..afcc846ba05 100644 --- a/spec/lib/banzai/filter/video_link_filter_spec.rb +++ b/spec/lib/banzai/filter/video_link_filter_spec.rb @@ -17,27 +17,32 @@ describe Banzai::Filter::VideoLinkFilter do let(:project) { create(:project, :repository) } - context 'when the element src has a video extension' do - UploaderHelper::SAFE_VIDEO_EXT.each do |ext| - it "replaces the image tag 'path/video.#{ext}' with a video tag" do - container = filter(link_to_image("/path/video.#{ext}")).children.first + shared_examples 'replaces the image tag with a video tag' do |ext| + it "replaces the image tag 'path/video.#{ext}' with a video tag" do + container = filter(link_to_image("/path/video.#{ext}")).children.first - expect(container.name).to eq 'div' - expect(container['class']).to eq 'video-container' + expect(container.name).to eq 'div' + expect(container['class']).to eq 'video-container' + + video, paragraph = container.children - video, paragraph = container.children + expect(video.name).to eq 'video' + expect(video['src']).to eq "/path/video.#{ext}" - expect(video.name).to eq 'video' - expect(video['src']).to eq "/path/video.#{ext}" + expect(paragraph.name).to eq 'p' - expect(paragraph.name).to eq 'p' + link = paragraph.children.first - link = paragraph.children.first + expect(link.name).to eq 'a' + expect(link['href']).to eq "/path/video.#{ext}" + expect(link['target']).to eq '_blank' + end + end - expect(link.name).to eq 'a' - expect(link['href']).to eq "/path/video.#{ext}" - expect(link['target']).to eq '_blank' - end + context 'when the element src has a video extension' do + UploaderHelper::SAFE_VIDEO_EXT.each do |ext| + it_behaves_like 'replaces the image tag with a video tag', ext + it_behaves_like 'replaces the image tag with a video tag', ext.upcase end end -- cgit v1.2.3