Commit bd8fb566 authored by GitLab Bot's avatar GitLab Bot

Add latest changes from gitlab-org/gitlab@master

parent 561e1b47
...@@ -53,7 +53,7 @@ class DiffFileEntity < DiffFileBaseEntity ...@@ -53,7 +53,7 @@ class DiffFileEntity < DiffFileBaseEntity
end end
# Used for inline diffs # Used for inline diffs
expose :highlighted_diff_lines, using: DiffLineEntity, if: -> (diff_file, options) { inline_diff_view?(options) && diff_file.text? } do |diff_file| expose :highlighted_diff_lines, using: DiffLineEntity, if: -> (diff_file, options) { inline_diff_view?(options, diff_file) && diff_file.text? } do |diff_file|
diff_file.diff_lines_for_serializer diff_file.diff_lines_for_serializer
end end
...@@ -62,19 +62,19 @@ class DiffFileEntity < DiffFileBaseEntity ...@@ -62,19 +62,19 @@ class DiffFileEntity < DiffFileBaseEntity
end end
# Used for parallel diffs # Used for parallel diffs
expose :parallel_diff_lines, using: DiffLineParallelEntity, if: -> (diff_file, options) { parallel_diff_view?(options) && diff_file.text? } expose :parallel_diff_lines, using: DiffLineParallelEntity, if: -> (diff_file, options) { parallel_diff_view?(options, diff_file) && diff_file.text? }
private private
def parallel_diff_view?(options) def parallel_diff_view?(options, diff_file)
return true unless Feature.enabled?(:single_mr_diff_view) return true unless Feature.enabled?(:single_mr_diff_view, diff_file.repository.project)
# If we're not rendering inline, we must be rendering parallel # If we're not rendering inline, we must be rendering parallel
!inline_diff_view?(options) !inline_diff_view?(options, diff_file)
end end
def inline_diff_view?(options) def inline_diff_view?(options, diff_file)
return true unless Feature.enabled?(:single_mr_diff_view) return true unless Feature.enabled?(:single_mr_diff_view, diff_file.repository.project)
# If nothing is present, inline will be the default. # If nothing is present, inline will be the default.
options.fetch(:diff_view, :inline).to_sym == :inline options.fetch(:diff_view, :inline).to_sym == :inline
......
---
title: Allow default time window on grafana embeds
merge_request: 21884
author:
type: changed
...@@ -17,6 +17,8 @@ The access levels are defined in the `Gitlab::Access` module. Currently, these l ...@@ -17,6 +17,8 @@ The access levels are defined in the `Gitlab::Access` module. Currently, these l
Gets a list of group or project members viewable by the authenticated user. Gets a list of group or project members viewable by the authenticated user.
Returns only direct members and not inherited members through ancestors groups. Returns only direct members and not inherited members through ancestors groups.
This function takes pagination parameters `page` and `per_page` to restrict the list of users.
```plaintext ```plaintext
GET /groups/:id/members GET /groups/:id/members
GET /projects/:id/members GET /projects/:id/members
...@@ -72,6 +74,8 @@ Gets a list of group or project members viewable by the authenticated user, incl ...@@ -72,6 +74,8 @@ Gets a list of group or project members viewable by the authenticated user, incl
When a user is a member of the project/group and of one or more ancestor groups the user is returned only once with the project `access_level` (if exists) When a user is a member of the project/group and of one or more ancestor groups the user is returned only once with the project `access_level` (if exists)
or the `access_level` for the user in the first group which they belong to in the project groups ancestors chain. or the `access_level` for the user in the first group which they belong to in the project groups ancestors chain.
This function takes pagination parameters `page` and `per_page` to restrict the list of users.
```plaintext ```plaintext
GET /groups/:id/members/all GET /groups/:id/members/all
GET /projects/:id/members/all GET /projects/:id/members/all
......
...@@ -195,16 +195,17 @@ module Gitlab ...@@ -195,16 +195,17 @@ module Gitlab
The [current version history](../user/project/settings/import_export.md) also displays the equivalent GitLab version The [current version history](../user/project/settings/import_export.md) also displays the equivalent GitLab version
and it is useful for knowing which versions won't be compatible between them. and it is useful for knowing which versions won't be compatible between them.
| GitLab version | Import/Export version | | Exporting GitLab version | Importing GitLab version |
| ---------------- | --------------------- | | -------------------------- | -------------------------- |
| 11.1 to current | 0.2.4 | | 11.7 to current | 11.7 to current |
| 10.8 | 0.2.3 | | 11.1 to 11.6 | 11.1 to 11.6 |
| 10.4 | 0.2.2 | | 10.8 to 11.0 | 10.8 to 11.0 |
| ... | ... | | 10.4 to 10.7 | 10.4 to 10.7 |
| 8.10.3 | 0.1.3 | | ... | ... |
| 8.10.0 | 0.1.2 | | 8.10.3 to 8.11 | 8.10.3 to 8.11 |
| 8.9.5 | 0.1.1 | | 8.10.0 to 8.10.2 | 8.10.0 to 8.10.2 |
| 8.9.0 | 0.1.0 | | 8.9.5 to 8.9.11 | 8.9.5 to 8.9.11 |
| 8.9.0 to 8.9.4 | 8.9.0 to 8.9.4 |
### When to bump the version up ### When to bump the version up
...@@ -223,28 +224,6 @@ Every time we bump the version, the integration specs will fail and can be fixed ...@@ -223,28 +224,6 @@ Every time we bump the version, the integration specs will fail and can be fixed
bundle exec rake gitlab:import_export:bump_version bundle exec rake gitlab:import_export:bump_version
``` ```
### Renaming columns or models
This is a relatively common occurrence that will require a version bump.
There is also the _RC problem_ - GitLab.com runs an RC, prior to any customers,
meaning that we want to bump the version up in the next version (or patch release).
For example:
1. Add rename to `RelationRenameService` in X.Y
1. Remove it from `RelationRenameService` in X.Y + 1
1. Bump Import/Export version in X.Y + 1
```ruby
module Gitlab
module ImportExport
class RelationRenameService
RENAMES = {
'pipelines' => 'ci_pipelines' # Added in 11.6, remove in 11.7
}.freeze
```
## A quick dive into the code ## A quick dive into the code
### Import/Export configuration (`import_export.yml`) ### Import/Export configuration (`import_export.yml`)
......
...@@ -162,6 +162,9 @@ class CleanupUsersUpdatedAtRename < ActiveRecord::Migration[4.2] ...@@ -162,6 +162,9 @@ class CleanupUsersUpdatedAtRename < ActiveRecord::Migration[4.2]
end end
``` ```
NOTE: **Note:** If you're renaming a large table, please carefully consider the state when the first migration has run but the second cleanup migration hasn't been run yet.
With [Canary](https://about.gitlab.com/handbook/engineering/infrastructure/library/canary/) it is possible that the system runs in this state for a significant amount of time.
## Changing Column Constraints ## Changing Column Constraints
Adding or removing a NOT NULL clause (or another constraint) can typically be Adding or removing a NOT NULL clause (or another constraint) can typically be
......
...@@ -769,7 +769,7 @@ Prerequisites for embedding from a Grafana instance: ...@@ -769,7 +769,7 @@ Prerequisites for embedding from a Grafana instance:
1. In the upper-left corner of the page, select a specific value for each variable required for the queries in the chart. 1. In the upper-left corner of the page, select a specific value for each variable required for the queries in the chart.
![Select Query Variables](img/select_query_variables_v12_5.png) ![Select Query Variables](img/select_query_variables_v12_5.png)
1. In Grafana, click on a panel's title, then click **Share** to open the panel's sharing dialog to the **Link** tab. 1. In Grafana, click on a panel's title, then click **Share** to open the panel's sharing dialog to the **Link** tab.
1. If your Prometheus queries use Grafana's custom template variables, ensure that "Template variables" and "Current time range" options are toggled to **On**. Of Grafana global template variables, only `$__interval`, `$__from`, and `$__to` are currently supported. 1. If your Prometheus queries use Grafana's custom template variables, ensure that "Template variables" option is toggled to **On**. Of Grafana global template variables, only `$__interval`, `$__from`, and `$__to` are currently supported. Toggle **On** the "Current time range" option to specify the time range of the chart. Otherwise, the default range will be the last 8 hours.
![Grafana Sharing Dialog](img/grafana_sharing_dialog_v12_5.png) ![Grafana Sharing Dialog](img/grafana_sharing_dialog_v12_5.png)
1. Click **Copy** to copy the URL to the clipboard. 1. Click **Copy** to copy the URL to the clipboard.
1. In GitLab, paste the URL into a Markdown field and save. The chart will take a few moments to render. 1. In GitLab, paste the URL into a Markdown field and save. The chart will take a few moments to render.
......
...@@ -42,22 +42,23 @@ Note the following: ...@@ -42,22 +42,23 @@ Note the following:
The following table lists updates to Import/Export: The following table lists updates to Import/Export:
| GitLab version | Import/Export schema version | | Exporting GitLab version | Importing GitLab version |
| ---------------- | --------------------- | | -------------------------- | -------------------------- |
| 11.1 to current | 0.2.4 | | 11.7 to current | 11.7 to current |
| 10.8 | 0.2.3 | | 11.1 to 11.6 | 11.1 to 11.6 |
| 10.4 | 0.2.2 | | 10.8 to 11.0 | 10.8 to 11.0 |
| 10.3 | 0.2.1 | | 10.4 to 10.7 | 10.4 to 10.7 |
| 10.0 | 0.2.0 | | 10.3 | 10.3 |
| 9.4.0 | 0.1.8 | | 10.0 to 10.2 | 10.0 to 10.2 |
| 9.2.0 | 0.1.7 | | 9.4 to 9.6 | 9.4 to 9.6 |
| 8.17.0 | 0.1.6 | | 9.2 to 9.3 | 9.2 to 9.3 |
| 8.13.0 | 0.1.5 | | 8.17 to 9.1 | 8.17 to 9.1 |
| 8.12.0 | 0.1.4 | | 8.13 to 8.16 | 8.13 to 8.16 |
| 8.10.3 | 0.1.3 | | 8.12 | 8.12 |
| 8.10.0 | 0.1.2 | | 8.10.3 to 8.11 | 8.10.3 to 8.11 |
| 8.9.5 | 0.1.1 | | 8.10.0 to 8.10.2 | 8.10.0 to 8.10.2 |
| 8.9.0 | 0.1.0 | | 8.9.5 to 8.9.11 | 8.9.5 to 8.9.11 |
| 8.9.0 to 8.9.4 | 8.9.0 to 8.9.4 |
Projects can be exported and imported only between versions of GitLab with matching Import/Export versions. Projects can be exported and imported only between versions of GitLab with matching Import/Export versions.
......
...@@ -13,13 +13,16 @@ module Banzai ...@@ -13,13 +13,16 @@ module Banzai
super super
end end
# @return [Hash<Symbol, String>] with keys :grafana_url, :start, and :end
def embed_params(node) def embed_params(node)
query_params = Gitlab::Metrics::Dashboard::Url.parse_query(node['href']) query_params = Gitlab::Metrics::Dashboard::Url.parse_query(node['href'])
return unless [:panelId, :from, :to].all? do |param|
query_params.include?(param)
end
{ url: node['href'], start: query_params[:from], end: query_params[:to] } return unless query_params.include?(:panelId)
time_window = Grafana::TimeWindow.new(query_params[:from], query_params[:to])
url = url_with_window(node['href'], query_params, time_window.in_milliseconds)
{ grafana_url: url }.merge(time_window.formatted)
end end
# Selects any links with an href contains the configured # Selects any links with an href contains the configured
...@@ -44,18 +47,24 @@ module Banzai ...@@ -44,18 +47,24 @@ module Banzai
Gitlab::Routing.url_helpers.project_grafana_api_metrics_dashboard_url( Gitlab::Routing.url_helpers.project_grafana_api_metrics_dashboard_url(
project, project,
embedded: true, embedded: true,
grafana_url: params[:url], **params
start: format_time(params[:start]),
end: format_time(params[:end])
) )
end end
# Formats a timestamp from Grafana for compatibility with # If the provided url is missing time window parameters,
# parsing in JS via `new Date(timestamp)` # this inserts the default window into the url, allowing
# the embed service to correctly format prometheus
# queries during embed processing.
# #
# @param time [String] Represents miliseconds since epoch # @param url [String]
def format_time(time) # @param query_params [Hash<Symbol, String>]
Time.at(time.to_i / 1000).utc.strftime('%FT%TZ') # @param time_window_params [Hash<Symbol, Integer>]
# @return [String]
def url_with_window(url, query_params, time_window_params)
uri = URI(url)
uri.query = time_window_params.merge(query_params).to_query
uri.to_s
end end
# Fetches a dashboard and caches the result for the # Fetches a dashboard and caches the result for the
......
...@@ -21,8 +21,6 @@ module Gitlab ...@@ -21,8 +21,6 @@ module Gitlab
@tree_hash = read_tree_hash @tree_hash = read_tree_hash
@project_members = @tree_hash.delete('project_members') @project_members = @tree_hash.delete('project_members')
RelationRenameService.rename(@tree_hash)
if relation_tree_restorer.restore if relation_tree_restorer.restore
import_failure_service.with_retry(action: 'set_latest_merge_request_diff_ids!') do import_failure_service.with_retry(action: 'set_latest_merge_request_diff_ids!') do
@project.merge_requests.set_latest_merge_request_diff_ids! @project.merge_requests.set_latest_merge_request_diff_ids!
......
...@@ -35,8 +35,6 @@ module Gitlab ...@@ -35,8 +35,6 @@ module Gitlab
end end
project_tree['project_members'] += group_members_array project_tree['project_members'] += group_members_array
RelationRenameService.add_new_associations(project_tree)
end end
def reader def reader
......
# frozen_string_literal: true
# This class is intended to help with relation renames within Gitlab versions
# and allow compatibility between versions.
# If you have to change one relationship name that is imported/exported,
# you should add it to the RENAMES constant indicating the old name and the
# new one.
# The behavior of these renamed relationships should be transient and it should
# only last one release until you completely remove the renaming from the list.
#
# When importing, this class will check the hash and:
# - if only the old relationship name is found, it will rename it with the new one
# - if only the new relationship name is found, it will do nothing
# - if it finds both, it will use the new relationship data
#
# When exporting, this class will duplicate the keys in the resulting file.
# This way, if we open the file in an old version of the exporter it will work
# and also it will with the newer versions.
module Gitlab
module ImportExport
class RelationRenameService
RENAMES = {
'pipelines' => 'ci_pipelines' # Added in 11.6, remove in 11.7
}.freeze
def self.rename(tree_hash)
return unless tree_hash&.present?
RENAMES.each do |old_name, new_name|
old_entry = tree_hash.delete(old_name)
next if tree_hash[new_name]
next unless old_entry
tree_hash[new_name] = old_entry
end
end
def self.add_new_associations(tree_hash)
RENAMES.each do |old_name, new_name|
next if tree_hash.key?(old_name)
tree_hash[old_name] = tree_hash[new_name]
end
end
end
end
end
# frozen_string_literal: true
module Grafana
# Allows for easy formatting and manipulations of timestamps
# coming from a Grafana url
class TimeWindow
include ::Gitlab::Utils::StrongMemoize
def initialize(from, to)
@from = from
@to = to
end
def formatted
{
start: window[:from].formatted,
end: window[:to].formatted
}
end
def in_milliseconds
window.transform_values(&:to_ms)
end
private
def window
strong_memoize(:window) do
specified_window
rescue Timestamp::Error
default_window
end
end
def specified_window
RangeWithDefaults.new(
from: Timestamp.from_ms_since_epoch(@from),
to: Timestamp.from_ms_since_epoch(@to)
).to_hash
end
def default_window
RangeWithDefaults.new.to_hash
end
end
# For incomplete time ranges, adds default parameters to
# achieve a complete range. If both full range is provided,
# range will be returned.
class RangeWithDefaults
DEFAULT_RANGE = 8.hours
# @param from [Grafana::Timestamp, nil] Start of the expected range
# @param to [Grafana::Timestamp, nil] End of the expected range
def initialize(from: nil, to: nil)
@from = from
@to = to
apply_defaults!
end
def to_hash
{ from: @from, to: @to }.compact
end
private
def apply_defaults!
@to ||= @from ? relative_end : Timestamp.new(Time.now)
@from ||= relative_start
end
def relative_start
Timestamp.new(DEFAULT_RANGE.before(@to.time))
end
def relative_end
Timestamp.new(DEFAULT_RANGE.since(@from.time))
end
end
# Offers a consistent API for timestamps originating from
# Grafana or other sources, allowing for formatting of timestamps
# as consumed by Grafana-related utilities
class Timestamp
Error = Class.new(StandardError)
attr_accessor :time
# @param timestamp [Time]
def initialize(time)
@time = time
end
# Formats a timestamp from Grafana for compatibility with
# parsing in JS via `new Date(timestamp)`
def formatted
time.utc.strftime('%FT%TZ')
end
# Converts to milliseconds since epoch
def to_ms
time.to_i * 1000
end
class << self
# @param time [String] Representing milliseconds since epoch.
# This is what JS "decided" unix is.
def from_ms_since_epoch(time)
return if time.nil?
raise Error.new('Expected milliseconds since epoch') unless ms_since_epoch?(time)
new(cast_ms_to_time(time))
end
private
def cast_ms_to_time(time)
Time.at(time.to_i / 1000.0)
end
def ms_since_epoch?(time)
ms = time.to_i
ms.to_s == time && ms.bit_length < 64
end
end
end
end
...@@ -127,7 +127,7 @@ module QA ...@@ -127,7 +127,7 @@ module QA
click_element(:edit_link_labels) click_element(:edit_link_labels)
labels.each do |label| labels.each do |label|
has_element?(:labels_block, text: label) has_element?(:labels_block, text: label, wait: 0)
end end
refresh refresh
......
...@@ -16,6 +16,14 @@ module Trigger ...@@ -16,6 +16,14 @@ module Trigger
%w[gitlab gitlab-ee].include?(ENV['CI_PROJECT_NAME']) %w[gitlab gitlab-ee].include?(ENV['CI_PROJECT_NAME'])
end end
def self.non_empty_variable_value(variable)
variable_value = ENV[variable]
return if variable_value.nil? || variable_value.empty?
variable_value
end
class Base class Base
def invoke!(post_comment: false, downstream_job_name: nil) def invoke!(post_comment: false, downstream_job_name: nil)
pipeline_variables = variables pipeline_variables = variables
...@@ -84,14 +92,15 @@ module Trigger ...@@ -84,14 +92,15 @@ module Trigger
end end
def base_variables def base_variables
# Use CI_MERGE_REQUEST_SOURCE_BRANCH_SHA for omnibus checkouts due to pipeline for merged results # Use CI_MERGE_REQUEST_SOURCE_BRANCH_SHA for omnibus checkouts due to pipeline for merged results,
# and fallback to CI_COMMIT_SHA for the `detached` pipelines.
{ {
'GITLAB_REF_SLUG' => ENV['CI_COMMIT_TAG'] ? ENV['CI_COMMIT_REF_NAME'] : ENV['CI_COMMIT_REF_SLUG'], 'GITLAB_REF_SLUG' => ENV['CI_COMMIT_TAG'] ? ENV['CI_COMMIT_REF_NAME'] : ENV['CI_COMMIT_REF_SLUG'],
'TRIGGERED_USER' => ENV['TRIGGERED_USER'] || ENV['GITLAB_USER_NAME'], 'TRIGGERED_USER' => ENV['TRIGGERED_USER'] || ENV['GITLAB_USER_NAME'],
'TRIGGER_SOURCE' => ENV['CI_JOB_URL'], 'TRIGGER_SOURCE' => ENV['CI_JOB_URL'],
'TOP_UPSTREAM_SOURCE_PROJECT' => ENV['CI_PROJECT_PATH'], 'TOP_UPSTREAM_SOURCE_PROJECT' => ENV['CI_PROJECT_PATH'],
'TOP_UPSTREAM_SOURCE_JOB' => ENV['CI_JOB_URL'], 'TOP_UPSTREAM_SOURCE_JOB' => ENV['CI_JOB_URL'],
'TOP_UPSTREAM_SOURCE_SHA' => ENV['CI_MERGE_REQUEST_SOURCE_BRANCH_SHA'], 'TOP_UPSTREAM_SOURCE_SHA' => Trigger.non_empty_variable_value('CI_MERGE_REQUEST_SOURCE_BRANCH_SHA') || ENV['CI_COMMIT_SHA'],
'TOP_UPSTREAM_SOURCE_REF' => ENV['CI_COMMIT_REF_NAME'], 'TOP_UPSTREAM_SOURCE_REF' => ENV['CI_COMMIT_REF_NAME'],
'TOP_UPSTREAM_MERGE_REQUEST_PROJECT_ID' => ENV['CI_MERGE_REQUEST_PROJECT_ID'], 'TOP_UPSTREAM_MERGE_REQUEST_PROJECT_ID' => ENV['CI_MERGE_REQUEST_PROJECT_ID'],
'TOP_UPSTREAM_MERGE_REQUEST_IID' => ENV['CI_MERGE_REQUEST_IID'] 'TOP_UPSTREAM_MERGE_REQUEST_IID' => ENV['CI_MERGE_REQUEST_IID']
...@@ -127,8 +136,9 @@ module Trigger ...@@ -127,8 +136,9 @@ module Trigger
def extra_variables def extra_variables
# Use CI_MERGE_REQUEST_SOURCE_BRANCH_SHA for omnibus checkouts due to pipeline for merged results # Use CI_MERGE_REQUEST_SOURCE_BRANCH_SHA for omnibus checkouts due to pipeline for merged results
# and fallback to CI_COMMIT_SHA for the `detached` pipelines.
{ {
'GITLAB_VERSION' => ENV['CI_MERGE_REQUEST_SOURCE_BRANCH_SHA'], 'GITLAB_VERSION' => Trigger.non_empty_variable_value('CI_MERGE_REQUEST_SOURCE_BRANCH_SHA') || ENV['CI_COMMIT_SHA'],
'ALTERNATIVE_SOURCES' => 'true', 'ALTERNATIVE_SOURCES' => 'true',
'ee' => Trigger.ee? ? 'true' : 'false', 'ee' => Trigger.ee? ? 'true' : 'false',
'QA_BRANCH' => ENV['QA_BRANCH'] || 'master' 'QA_BRANCH' => ENV['QA_BRANCH'] || 'master'
...@@ -194,7 +204,7 @@ module Trigger ...@@ -194,7 +204,7 @@ module Trigger
Gitlab.create_commit_comment( Gitlab.create_commit_comment(
ENV['CI_PROJECT_PATH'], ENV['CI_PROJECT_PATH'],
ENV['CI_MERGE_REQUEST_SOURCE_BRANCH_SHA'], Trigger.non_empty_variable_value('CI_MERGE_REQUEST_SOURCE_BRANCH_SHA') || ENV['CI_COMMIT_SHA'],
"The [`#{ENV['CI_JOB_NAME']}`](#{ENV['CI_JOB_URL']}) job from pipeline #{ENV['CI_PIPELINE_URL']} triggered #{downstream_pipeline.web_url} downstream.") "The [`#{ENV['CI_JOB_NAME']}`](#{ENV['CI_JOB_URL']}) job from pipeline #{ENV['CI_PIPELINE_URL']} triggered #{downstream_pipeline.web_url} downstream.")
rescue Gitlab::Error::Error => error rescue Gitlab::Error::Error => error
......
...@@ -20,7 +20,7 @@ describe 'a maintainer edits files on a source-branch of an MR from a fork', :js ...@@ -20,7 +20,7 @@ describe 'a maintainer edits files on a source-branch of an MR from a fork', :js
end end
before do before do
stub_feature_flags(web_ide_default: false, single_mr_diff_view: false, code_navigation: false) stub_feature_flags(web_ide_default: false, single_mr_diff_view: { enabled: false, thing: target_project }, code_navigation: false)
target_project.add_maintainer(user) target_project.add_maintainer(user)
sign_in(user) sign_in(user)
......
...@@ -10,7 +10,7 @@ describe 'Batch diffs', :js do ...@@ -10,7 +10,7 @@ describe 'Batch diffs', :js do
let(:merge_request) { create(:merge_request, source_project: project, source_branch: 'master', target_branch: 'empty-branch') } let(:merge_request) { create(:merge_request, source_project: project, source_branch: 'master', target_branch: 'empty-branch') }
before do before do
stub_feature_flags(single_mr_diff_view: true) stub_feature_flags(single_mr_diff_view: { enabled: true, thing: project })
stub_feature_flags(diffs_batch_load: true) stub_feature_flags(diffs_batch_load: true)
sign_in(project.owner) sign_in(project.owner)
......
...@@ -6318,7 +6318,7 @@ ...@@ -6318,7 +6318,7 @@
] ]
} }
], ],
"pipelines": [ "ci_pipelines": [
{ {
"id": 36, "id": 36,
"project_id": 5, "project_id": 5,
......
...@@ -10,18 +10,20 @@ describe Banzai::Filter::InlineGrafanaMetricsFilter do ...@@ -10,18 +10,20 @@ describe Banzai::Filter::InlineGrafanaMetricsFilter do
let(:input) { %(<a href="#{trigger_url}">example</a>) } let(:input) { %(<a href="#{trigger_url}">example</a>) }
let(:doc) { filter(input) } let(:doc) { filter(input) }
let(:embed_url) { doc.at_css('.js-render-metrics')['data-dashboard-url'] }
let(:dashboard_path) do let(:dashboard_path) do
'/d/XDaNK6amz/gitlab-omnibus-redis' \ '/d/XDaNK6amz/gitlab-omnibus-redis' \
'?from=1570397739557&to=1570484139557' \ '?from=1570397739557&panelId=14' \
'&var-instance=All&panelId=14' '&to=1570484139557&var-instance=All'
end end
let(:trigger_url) { grafana_integration.grafana_url + dashboard_path } let(:trigger_url) { grafana_integration.grafana_url + dashboard_path }
let(:dashboard_url) do let(:dashboard_url) do
urls.project_grafana_api_metrics_dashboard_url( urls.project_grafana_api_metrics_dashboard_url(
project, project,
embedded: true,
grafana_url: trigger_url, grafana_url: trigger_url,
embedded: true,
start: "2019-10-06T21:35:39Z", start: "2019-10-06T21:35:39Z",
end: "2019-10-07T21:35:39Z" end: "2019-10-07T21:35:39Z"
) )
...@@ -29,6 +31,10 @@ describe Banzai::Filter::InlineGrafanaMetricsFilter do ...@@ -29,6 +31,10 @@ describe Banzai::Filter::InlineGrafanaMetricsFilter do
it_behaves_like 'a metrics embed filter' it_behaves_like 'a metrics embed filter'
around do |example|
Timecop.freeze(Time.utc(2019, 3, 17, 13, 10)) { example.run }
end
context 'when grafana is not configured' do context 'when grafana is not configured' do
before do before do
allow(project).to receive(:grafana_integration).and_return(nil) allow(project).to receive(:grafana_integration).and_return(nil)
...@@ -39,7 +45,7 @@ describe Banzai::Filter::InlineGrafanaMetricsFilter do ...@@ -39,7 +45,7 @@ describe Banzai::Filter::InlineGrafanaMetricsFilter do
end end
end end
context 'when parameters are missing' do context 'when "panelId" parameter is missing' do
let(:dashboard_path) { '/d/XDaNK6amz/gitlab-omnibus-redis' } let(:dashboard_path) { '/d/XDaNK6amz/gitlab-omnibus-redis' }
it 'leaves the markdown unchanged' do it 'leaves the markdown unchanged' do
...@@ -47,6 +53,39 @@ describe Banzai::Filter::InlineGrafanaMetricsFilter do ...@@ -47,6 +53,39 @@ describe Banzai::Filter::InlineGrafanaMetricsFilter do
end end
end end
context 'when time window parameters are missing' do
let(:dashboard_path) { '/d/XDaNK6amz/gitlab-omnibus-redis?panelId=16' }
it 'sets the window to the last 8 hrs' do
expect(embed_url).to include(
'from%3D1552799400000', 'to%3D1552828200000',
'start=2019-03-17T05%3A10%3A00Z', 'end=2019-03-17T13%3A10%3A00Z'
)
end
end
context 'when "to" parameter is missing' do
let(:dashboard_path) { '/d/XDaNK6amz/gitlab-omnibus-redis?panelId=16&from=1570397739557' }
it 'sets "to" to 8 hrs after "from"' do
expect(embed_url).to include(
'from%3D1570397739557', 'to%3D1570426539000',
'start=2019-10-06T21%3A35%3A39Z', 'end=2019-10-07T05%3A35%3A39Z'
)
end
end
context 'when "from" parameter is missing' do
let(:dashboard_path) { '/d/XDaNK6amz/gitlab-omnibus-redis?panelId=16&to=1570484139557' }
it 'sets "from" to 8 hrs before "to"' do
expect(embed_url).to include(
'from%3D1570455339000', 'to%3D1570484139557',
'start=2019-10-07T13%3A35%3A39Z', 'end=2019-10-07T21%3A35%3A39Z'
)
end
end
private private
# Nokogiri escapes the URLs, but we don't care about that # Nokogiri escapes the URLs, but we don't care about that
......
...@@ -89,8 +89,6 @@ describe 'Test coverage of the Project Import' do ...@@ -89,8 +89,6 @@ describe 'Test coverage of the Project Import' do
def relations_from_json(json_file) def relations_from_json(json_file)
json = ActiveSupport::JSON.decode(IO.read(json_file)) json = ActiveSupport::JSON.decode(IO.read(json_file))
Gitlab::ImportExport::RelationRenameService.rename(json)
[].tap {|res| gather_relations({ project: json }, res, [])} [].tap {|res| gather_relations({ project: json }, res, [])}
.map {|relation_names| relation_names.join('.')} .map {|relation_names| relation_names.join('.')}
end end
......
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::ImportExport::RelationRenameService do
include ImportExport::CommonUtil
let(:renames) do
{
'example_relation1' => 'new_example_relation1',
'example_relation2' => 'new_example_relation2'
}
end
let(:user) { create(:admin) }
let(:group) { create(:group, :nested) }
let!(:project) { create(:project, :builds_disabled, :issues_disabled, group: group, name: 'project', path: 'project') }
let(:shared) { project.import_export_shared }
before do
stub_const("#{described_class}::RENAMES", renames)
end
context 'when importing' do
let(:project_tree_restorer) { Gitlab::ImportExport::Project::TreeRestorer.new(user: user, shared: shared, project: project) }
let(:file_content) { IO.read(File.join(shared.export_path, 'project.json')) }
let(:json_file) { ActiveSupport::JSON.decode(file_content) }
before do
setup_import_export_config('complex')
allow(ActiveSupport::JSON).to receive(:decode).and_call_original
allow(ActiveSupport::JSON).to receive(:decode).with(file_content).and_return(json_file)
end
context 'when the file has only old relationship names' do
# Configuring the json as an old version exported file, with only
# the previous association with the old name
before do
renames.each do |old_name, _|
json_file[old_name.to_s] = []
end
end
it 'renames old relationships to the new name' do
expect(json_file.keys).to include(*renames.keys)
project_tree_restorer.restore
expect(json_file.keys).to include(*renames.values)
expect(json_file.keys).not_to include(*renames.keys)
end
end
context 'when the file has both the old and new relationships' do
# Configuring the json as the new version exported file, with both
# the old association name and the new one
before do
renames.each do |old_name, new_name|
json_file[old_name.to_s] = [1]
json_file[new_name.to_s] = [2]
end
end
it 'uses the new relationships and removes the old ones from the hash' do
expect(json_file.keys).to include(*renames.keys)
project_tree_restorer.restore
expect(json_file.keys).to include(*renames.values)
expect(json_file.values_at(*renames.values).flatten.uniq.first).to eq 2
expect(json_file.keys).not_to include(*renames.keys)
end
end
context 'when the file has only new relationship names' do
# Configuring the json as the future version exported file, with only
# the new association name
before do
renames.each do |_, new_name|
json_file[new_name.to_s] = []
end
end
it 'uses the new relationships' do
expect(json_file.keys).not_to include(*renames.keys)
project_tree_restorer.restore
expect(json_file.keys).to include(*renames.values)
end
end
end
context 'when exporting' do
let(:export_content_path) { project_tree_saver.full_path }
let(:export_content_hash) { ActiveSupport::JSON.decode(File.read(export_content_path)) }
let(:injected_hash) { renames.values.product([{}]).to_h }
let(:relation_tree_saver) { Gitlab::ImportExport::RelationTreeSaver.new }
let(:project_tree_saver) do
Gitlab::ImportExport::Project::TreeSaver.new(
project: project, current_user: user, shared: shared)
end
before do
allow(project_tree_saver).to receive(:tree_saver).and_return(relation_tree_saver)
end
it 'adds old relationships to the exported file' do
# we inject relations with new names that should be rewritten
expect(relation_tree_saver).to receive(:serialize).and_wrap_original do |method, *args|
method.call(*args).merge(injected_hash)
end
expect(project_tree_saver.save).to eq(true)
expect(export_content_hash.keys).to include(*renames.keys)
expect(export_content_hash.keys).to include(*renames.values)
end
end
end
# frozen_string_literal: true
require 'spec_helper'
describe Grafana::TimeWindow do
let(:from) { '1552799400000' }
let(:to) { '1552828200000' }
around do |example|
Timecop.freeze(Time.utc(2019, 3, 17, 13, 10)) { example.run }
end
describe '#formatted' do
subject { described_class.new(from, to).formatted }
it { is_expected.to eq(start: "2019-03-17T05:10:00Z", end: "2019-03-17T13:10:00Z") }
end
describe '#in_milliseconds' do
subject { described_class.new(from, to).in_milliseconds }
it { is_expected.to eq(from: 1552799400000, to: 1552828200000) }
context 'when non-unix parameters are provided' do
let(:to) { Time.now.to_s }
let(:default_from) { 8.hours.ago.to_i * 1000 }
let(:default_to) { Time.now.to_i * 1000 }
it { is_expected.to eq(from: default_from, to: default_to) }
end
end
end
describe Grafana::RangeWithDefaults do
let(:from) { Grafana::Timestamp.from_ms_since_epoch('1552799400000') }
let(:to) { Grafana::Timestamp.from_ms_since_epoch('1552828200000') }
around do |example|
Timecop.freeze(Time.utc(2019, 3, 17, 13, 10)) { example.run }
end
describe '#to_hash' do
subject { described_class.new(from: from, to: to).to_hash }
it { is_expected.to eq(from: from, to: to) }
context 'when only "to" is provided' do
let(:from) { nil }
it 'has the expected properties' do
expect(subject[:to]).to eq(to)
expect(subject[:from].time).to eq(to.time - 8.hours)
end
end
context 'when only "from" is provided' do
let(:to) { nil }
it 'has the expected properties' do
expect(subject[:to].time).to eq(from.time + 8.hours)
expect(subject[:from]).to eq(from)
end
end
context 'when no parameters are provided' do
let(:to) { nil }
let(:from) { nil }
let(:default_from) { 8.hours.ago }
let(:default_to) { Time.now }
it 'has the expected properties' do
expect(subject[:to].time).to eq(default_to)
expect(subject[:from].time).to eq(default_from)
end
end
end
end
describe Grafana::Timestamp do
let(:timestamp) { Time.at(1552799400) }
around do |example|
Timecop.freeze(Time.utc(2019, 3, 17, 13, 10)) { example.run }
end
describe '#formatted' do
subject { described_class.new(timestamp).formatted }
it { is_expected.to eq "2019-03-17T05:10:00Z" }
end
describe '#to_ms' do
subject { described_class.new(timestamp).to_ms }
it { is_expected.to eq 1552799400000 }
end
describe '.from_ms_since_epoch' do
let(:timestamp) { '1552799400000' }
subject { described_class.from_ms_since_epoch(timestamp) }
it { is_expected.to be_a described_class }
context 'when the input is not a unix-ish timestamp' do
let(:timestamp) { Time.now.to_s }
it 'raises an error' do
expect { subject }.to raise_error(Grafana::Timestamp::Error)
end
end
end
end
...@@ -60,7 +60,7 @@ RSpec.shared_examples 'diff file entity' do ...@@ -60,7 +60,7 @@ RSpec.shared_examples 'diff file entity' do
context 'when the `single_mr_diff_view` feature is disabled' do context 'when the `single_mr_diff_view` feature is disabled' do
before do before do
stub_feature_flags(single_mr_diff_view: false) stub_feature_flags(single_mr_diff_view: { enabled: false, thing: project })
end end
it 'contains both kinds of diffs' do it 'contains both kinds of diffs' do
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment