Skip to content
GitLab
Next
Menu
Projects
Groups
Snippets
/
Help
What's new
7
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
GitLab.org
GitLab FOSS
Commits
8d9963a8
Commit
8d9963a8
authored
May 13, 2022
by
🤖 GitLab Bot 🤖
Browse files
Add latest changes from gitlab-org/gitlab@master
parent
9adada11
Changes
59
Pipelines
1
Expand all
Hide whitespace changes
Inline
Side-by-side
.rubocop_todo.yml
View file @
8d9963a8
...
...
@@ -11,13 +11,6 @@ Gitlab/PolicyRuleBoolean:
Exclude
:
-
'
ee/app/policies/ee/identity_provider_policy.rb'
# Offense count: 2352
# Cop supports --auto-correct.
# Configuration parameters: Strict, EnforcedStyle, AllowedExplicitMatchers.
# SupportedStyles: inflected, explicit
RSpec/PredicateMatcher
:
Enabled
:
false
# Offense count: 118
RSpec/RepeatedExampleGroupBody
:
Enabled
:
false
...
...
.rubocop_todo/rspec/predicate_matcher.yml
0 → 100644
View file @
8d9963a8
This diff is collapsed.
Click to expand it.
app/assets/javascripts/tracking/tracker.js
0 → 100644
View file @
8d9963a8
import
{
LOAD_ACTION_ATTR_SELECTOR
}
from
'
./constants
'
;
import
{
dispatchSnowplowEvent
}
from
'
./dispatch_snowplow_event
'
;
import
getStandardContext
from
'
./get_standard_context
'
;
import
{
getEventHandlers
,
createEventPayload
,
renameKey
,
getReferrersCache
,
addReferrersCacheEntry
,
}
from
'
./utils
'
;
export
const
Tracker
=
{
nonInitializedQueue
:
[],
initialized
:
false
,
definitionsLoaded
:
false
,
definitionsManifest
:
{},
definitionsEventsQueue
:
[],
definitions
:
[],
ALLOWED_URL_HASHES
:
[
'
#diff
'
,
'
#note
'
],
/**
* (Legacy) Determines if tracking is enabled at the user level.
* https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/DNT.
*
* @returns {Boolean}
*/
trackable
()
{
return
!
[
'
1
'
,
'
yes
'
].
includes
(
window
.
doNotTrack
||
navigator
.
doNotTrack
||
navigator
.
msDoNotTrack
,
);
},
/**
* Determines if Snowplow is available/enabled.
*
* @returns {Boolean}
*/
enabled
()
{
return
typeof
window
.
snowplow
===
'
function
'
&&
Tracker
.
trackable
();
},
/**
* Dispatches a structured event per our taxonomy:
* https://docs.gitlab.com/ee/development/snowplow/index.html#structured-event-taxonomy.
*
* If the library is not initialized and events are trying to be
* dispatched (data-attributes, load-events), they will be added
* to a queue to be flushed afterwards.
*
* If there is an error when using the library, it will return ´false´
* and ´true´ otherwise.
*
* @param {...any} eventData defined event taxonomy
* @returns {Boolean}
*/
event
(...
eventData
)
{
if
(
!
Tracker
.
enabled
())
{
return
false
;
}
if
(
!
Tracker
.
initialized
)
{
Tracker
.
nonInitializedQueue
.
push
(
eventData
);
return
false
;
}
return
dispatchSnowplowEvent
(...
eventData
);
},
/**
* Preloads event definitions.
*
* @returns {undefined}
*/
loadDefinitions
()
{
// TODO: fetch definitions from the server and flush the queue
// See https://gitlab.com/gitlab-org/gitlab/-/issues/358256
Tracker
.
definitionsLoaded
=
true
;
while
(
Tracker
.
definitionsEventsQueue
.
length
)
{
Tracker
.
dispatchFromDefinition
(...
Tracker
.
definitionsEventsQueue
.
shift
());
}
},
/**
* Dispatches a structured event with data from its event definition.
*
* @param {String} basename
* @param {Object} eventData
* @returns {Boolean}
*/
definition
(
basename
,
eventData
=
{})
{
if
(
!
Tracker
.
enabled
())
{
return
false
;
}
if
(
!
(
basename
in
Tracker
.
definitionsManifest
))
{
throw
new
Error
(
`Missing Snowplow event definition "
${
basename
}
"`
);
}
return
Tracker
.
dispatchFromDefinition
(
basename
,
eventData
);
},
/**
* Builds an event with data from a valid definition and sends it to
* Snowplow. If the definitions are not loaded, it pushes the data to a queue.
*
* @param {String} basename
* @param {Object} eventData
* @returns {Boolean}
*/
dispatchFromDefinition
(
basename
,
eventData
)
{
if
(
!
Tracker
.
definitionsLoaded
)
{
Tracker
.
definitionsEventsQueue
.
push
([
basename
,
eventData
]);
return
false
;
}
const
eventDefinition
=
Tracker
.
definitions
.
find
((
definition
)
=>
definition
.
key
===
basename
);
return
Tracker
.
event
(
eventData
.
category
??
eventDefinition
.
category
,
eventData
.
action
??
eventDefinition
.
action
,
eventData
,
);
},
/**
* Dispatches any event emitted before initialization.
*
* @returns {undefined}
*/
flushPendingEvents
()
{
Tracker
.
initialized
=
true
;
while
(
Tracker
.
nonInitializedQueue
.
length
)
{
dispatchSnowplowEvent
(...
Tracker
.
nonInitializedQueue
.
shift
());
}
},
/**
* Attaches event handlers for data-attributes powered events.
*
* @param {String} category - the default category for all events
* @param {HTMLElement} parent - element containing data-attributes
* @returns {Array}
*/
bindDocument
(
category
=
document
.
body
.
dataset
.
page
,
parent
=
document
)
{
if
(
!
Tracker
.
enabled
()
||
parent
.
trackingBound
)
{
return
[];
}
// eslint-disable-next-line no-param-reassign
parent
.
trackingBound
=
true
;
const
handlers
=
getEventHandlers
(
category
,
(...
args
)
=>
Tracker
.
event
(...
args
));
handlers
.
forEach
((
event
)
=>
parent
.
addEventListener
(
event
.
name
,
event
.
func
));
return
handlers
;
},
/**
* Attaches event handlers for load-events (on render).
*
* @param {String} category - the default category for all events
* @param {HTMLElement} parent - element containing event targets
* @returns {Array}
*/
trackLoadEvents
(
category
=
document
.
body
.
dataset
.
page
,
parent
=
document
)
{
if
(
!
Tracker
.
enabled
())
{
return
[];
}
const
loadEvents
=
parent
.
querySelectorAll
(
LOAD_ACTION_ATTR_SELECTOR
);
loadEvents
.
forEach
((
element
)
=>
{
const
{
action
,
data
}
=
createEventPayload
(
element
);
Tracker
.
event
(
category
,
action
,
data
);
});
return
loadEvents
;
},
/**
* Enable Snowplow automatic form tracking.
* The config param requires at least one array of either forms
* class names, or field name attributes.
* https://docs.gitlab.com/ee/development/snowplow/index.html#form-tracking.
*
* @param {Object} config
* @param {Array} contexts
* @returns {undefined}
*/
enableFormTracking
(
config
,
contexts
=
[])
{
if
(
!
Tracker
.
enabled
())
{
return
;
}
if
(
!
Array
.
isArray
(
config
?.
forms
?.
allow
)
&&
!
Array
.
isArray
(
config
?.
fields
?.
allow
))
{
// eslint-disable-next-line @gitlab/require-i18n-strings
throw
new
Error
(
'
Unable to enable form event tracking without allow rules.
'
);
}
// Ignore default/standard schema
const
standardContext
=
getStandardContext
();
const
userProvidedContexts
=
contexts
.
filter
(
(
context
)
=>
context
.
schema
!==
standardContext
.
schema
,
);
const
mappedConfig
=
{};
if
(
config
.
forms
)
{
mappedConfig
.
forms
=
renameKey
(
config
.
forms
,
'
allow
'
,
'
whitelist
'
);
}
if
(
config
.
fields
)
{
mappedConfig
.
fields
=
renameKey
(
config
.
fields
,
'
allow
'
,
'
whitelist
'
);
}
const
enabler
=
()
=>
window
.
snowplow
(
'
enableFormTracking
'
,
mappedConfig
,
userProvidedContexts
);
if
(
document
.
readyState
===
'
complete
'
)
{
enabler
();
}
else
{
document
.
addEventListener
(
'
readystatechange
'
,
()
=>
{
if
(
document
.
readyState
===
'
complete
'
)
{
enabler
();
}
});
}
},
/**
* Replaces the URL and referrer for the default web context
* if the replacements are available.
*
* @returns {undefined}
*/
setAnonymousUrls
()
{
const
{
snowplowPseudonymizedPageUrl
:
pageUrl
}
=
window
.
gl
;
if
(
!
pageUrl
)
{
return
;
}
const
referrers
=
getReferrersCache
();
const
pageLinks
=
Object
.
seal
({
url
:
pageUrl
,
referrer
:
''
,
originalUrl
:
window
.
location
.
href
,
});
const
appendHash
=
Tracker
.
ALLOWED_URL_HASHES
.
some
((
prefix
)
=>
window
.
location
.
hash
.
startsWith
(
prefix
),
);
const
customUrl
=
`
${
pageUrl
}${
appendHash
?
window
.
location
.
hash
:
''
}
`
;
window
.
snowplow
(
'
setCustomUrl
'
,
customUrl
);
if
(
document
.
referrer
)
{
const
node
=
referrers
.
find
((
links
)
=>
links
.
originalUrl
===
document
.
referrer
);
if
(
node
)
{
pageLinks
.
referrer
=
node
.
url
;
window
.
snowplow
(
'
setReferrerUrl
'
,
pageLinks
.
referrer
);
}
}
addReferrersCacheEntry
(
referrers
,
pageLinks
);
},
};
app/assets/javascripts/tracking/tracking.js
View file @
8d9963a8
import
{
LOAD_ACTION_ATTR_SELECTOR
}
from
'
./constants
'
;
import
{
dispatchSnowplowEvent
}
from
'
./dispatch_snowplow_event
'
;
import
getStandardContext
from
'
./get_standard_context
'
;
import
{
getEventHandlers
,
createEventPayload
,
renameKey
,
addExperimentContext
,
getReferrersCache
,
addReferrersCacheEntry
,
}
from
'
./utils
'
;
const
ALLOWED_URL_HASHES
=
[
'
#diff
'
,
'
#note
'
];
export
default
class
Tracking
{
static
nonInitializedQueue
=
[];
static
initialized
=
false
;
static
definitionsLoaded
=
false
;
static
definitionsManifest
=
{};
static
definitionsEventsQueue
=
[];
static
definitions
=
[];
/**
* (Legacy) Determines if tracking is enabled at the user level.
* https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/DNT.
*
* @returns {Boolean}
*/
static
trackable
()
{
return
!
[
'
1
'
,
'
yes
'
].
includes
(
window
.
doNotTrack
||
navigator
.
doNotTrack
||
navigator
.
msDoNotTrack
,
);
}
/**
* Determines if Snowplow is available/enabled.
*
* @returns {Boolean}
*/
static
enabled
()
{
return
typeof
window
.
snowplow
===
'
function
'
&&
this
.
trackable
();
}
/**
* Dispatches a structured event per our taxonomy:
* https://docs.gitlab.com/ee/development/snowplow/index.html#structured-event-taxonomy.
*
* If the library is not initialized and events are trying to be
* dispatched (data-attributes, load-events), they will be added
* to a queue to be flushed afterwards.
*
* If there is an error when using the library, it will return ´false´
* and ´true´ otherwise.
*
* @param {...any} eventData defined event taxonomy
* @returns {Boolean}
*/
static
event
(...
eventData
)
{
if
(
!
this
.
enabled
())
{
return
false
;
}
if
(
!
this
.
initialized
)
{
this
.
nonInitializedQueue
.
push
(
eventData
);
return
false
;
}
return
dispatchSnowplowEvent
(...
eventData
);
}
/**
* Preloads event definitions.
*
* @returns {undefined}
*/
static
loadDefinitions
()
{
// TODO: fetch definitions from the server and flush the queue
// See https://gitlab.com/gitlab-org/gitlab/-/issues/358256
this
.
definitionsLoaded
=
true
;
while
(
this
.
definitionsEventsQueue
.
length
)
{
this
.
dispatchFromDefinition
(...
this
.
definitionsEventsQueue
.
shift
());
}
}
/**
* Dispatches a structured event with data from its event definition.
*
* @param {String} basename
* @param {Object} eventData
* @returns {Boolean}
*/
static
definition
(
basename
,
eventData
=
{})
{
if
(
!
this
.
enabled
())
{
return
false
;
}
if
(
!
(
basename
in
this
.
definitionsManifest
))
{
throw
new
Error
(
`Missing Snowplow event definition "
${
basename
}
"`
);
}
return
this
.
dispatchFromDefinition
(
basename
,
eventData
);
}
/**
* Builds an event with data from a valid definition and sends it to
* Snowplow. If the definitions are not loaded, it pushes the data to a queue.
*
* @param {String} basename
* @param {Object} eventData
* @returns {Boolean}
*/
static
dispatchFromDefinition
(
basename
,
eventData
)
{
if
(
!
this
.
definitionsLoaded
)
{
this
.
definitionsEventsQueue
.
push
([
basename
,
eventData
]);
return
false
;
}
const
eventDefinition
=
this
.
definitions
.
find
((
definition
)
=>
definition
.
key
===
basename
);
return
this
.
event
(
eventData
.
category
??
eventDefinition
.
category
,
eventData
.
action
??
eventDefinition
.
action
,
eventData
,
);
}
/**
* Dispatches any event emitted before initialization.
*
* @returns {undefined}
*/
static
flushPendingEvents
()
{
this
.
initialized
=
true
;
while
(
this
.
nonInitializedQueue
.
length
)
{
dispatchSnowplowEvent
(...
this
.
nonInitializedQueue
.
shift
());
}
}
/**
* Attaches event handlers for data-attributes powered events.
*
* @param {String} category - the default category for all events
* @param {HTMLElement} parent - element containing data-attributes
* @returns {Array}
*/
static
bindDocument
(
category
=
document
.
body
.
dataset
.
page
,
parent
=
document
)
{
if
(
!
this
.
enabled
()
||
parent
.
trackingBound
)
{
return
[];
}
// eslint-disable-next-line no-param-reassign
parent
.
trackingBound
=
true
;
const
handlers
=
getEventHandlers
(
category
,
(...
args
)
=>
this
.
event
(...
args
));
handlers
.
forEach
((
event
)
=>
parent
.
addEventListener
(
event
.
name
,
event
.
func
));
return
handlers
;
}
/**
* Attaches event handlers for load-events (on render).
*
* @param {String} category - the default category for all events
* @param {HTMLElement} parent - element containing event targets
* @returns {Array}
*/
static
trackLoadEvents
(
category
=
document
.
body
.
dataset
.
page
,
parent
=
document
)
{
if
(
!
this
.
enabled
())
{
return
[];
}
const
loadEvents
=
parent
.
querySelectorAll
(
LOAD_ACTION_ATTR_SELECTOR
);
loadEvents
.
forEach
((
element
)
=>
{
const
{
action
,
data
}
=
createEventPayload
(
element
);
this
.
event
(
category
,
action
,
data
);
});
return
loadEvents
;
}
/**
* Enable Snowplow automatic form tracking.
* The config param requires at least one array of either forms
* class names, or field name attributes.
* https://docs.gitlab.com/ee/development/snowplow/index.html#form-tracking.
*
* @param {Object} config
* @param {Array} contexts
* @returns {undefined}
*/
static
enableFormTracking
(
config
,
contexts
=
[])
{
if
(
!
this
.
enabled
())
{
return
;
}
if
(
!
Array
.
isArray
(
config
?.
forms
?.
allow
)
&&
!
Array
.
isArray
(
config
?.
fields
?.
allow
))
{
// eslint-disable-next-line @gitlab/require-i18n-strings
throw
new
Error
(
'
Unable to enable form event tracking without allow rules.
'
);
}
// Ignore default/standard schema
const
standardContext
=
getStandardContext
();
const
userProvidedContexts
=
contexts
.
filter
(
(
context
)
=>
context
.
schema
!==
standardContext
.
schema
,
);
const
mappedConfig
=
{};
if
(
config
.
forms
)
{
mappedConfig
.
forms
=
renameKey
(
config
.
forms
,
'
allow
'
,
'
whitelist
'
);
}
if
(
config
.
fields
)
{
mappedConfig
.
fields
=
renameKey
(
config
.
fields
,
'
allow
'
,
'
whitelist
'
);
}
const
enabler
=
()
=>
window
.
snowplow
(
'
enableFormTracking
'
,
mappedConfig
,
userProvidedContexts
);
if
(
document
.
readyState
===
'
complete
'
)
{
enabler
();
}
else
{
document
.
addEventListener
(
'
readystatechange
'
,
()
=>
{
if
(
document
.
readyState
===
'
complete
'
)
{
enabler
();
}
});
}
}
/**
* Replaces the URL and referrer for the default web context
* if the replacements are available.
*
* @returns {undefined}
*/
static
setAnonymousUrls
()
{
const
{
snowplowPseudonymizedPageUrl
:
pageUrl
}
=
window
.
gl
;
if
(
!
pageUrl
)
{
return
;
}
const
referrers
=
getReferrersCache
();
const
pageLinks
=
Object
.
seal
({
url
:
pageUrl
,
referrer
:
''
,
originalUrl
:
window
.
location
.
href
,
});
const
appendHash
=
ALLOWED_URL_HASHES
.
some
((
prefix
)
=>
window
.
location
.
hash
.
startsWith
(
prefix
));
const
customUrl
=
`
${
pageUrl
}${
appendHash
?
window
.
location
.
hash
:
''
}
`
;
window
.
snowplow
(
'
setCustomUrl
'
,
customUrl
);
if
(
document
.
referrer
)
{
const
node
=
referrers
.
find
((
links
)
=>
links
.
originalUrl
===
document
.
referrer
);
if
(
node
)
{
pageLinks
.
referrer
=
node
.
url
;
window
.
snowplow
(
'
setReferrerUrl
'
,
pageLinks
.
referrer
);
}
}
addReferrersCacheEntry
(
referrers
,
pageLinks
);
}
import
{
Tracker
}
from
'
jh_else_ce/tracking/tracker
'
;
import
{
addExperimentContext
}
from
'
./utils
'
;
const
Tracking
=
Object
.
assign
(
Tracker
,
{
/**
* Returns an implementation of this class in the form of
* a Vue mixin.
...
...
@@ -273,7 +9,7 @@ export default class Tracking {
* @param {Object} opts - default options for all events
* @returns {Object}
*/
static
mixin
(
opts
=
{})
{
mixin
(
opts
=
{})
{
return
{
computed
:
{
trackingCategory
()
{
...
...
@@ -297,5 +33,7 @@ export default class Tracking {
},
},
};
}
}
},
});
export
default
Tracking
;
app/controllers/projects/tracings_controller.rb
View file @
8d9963a8
...
...
@@ -15,6 +15,7 @@ class TracingsController < Projects::ApplicationController
feature_category
:tracing
def
show
render_404
unless
Feature
.
enabled?
(
:monitor_tracing
,
@project
)
end
private
...
...
app/helpers/ci/secure_files_helper.rb
0 → 100644
View file @
8d9963a8
# frozen_string_literal: true
module
Ci
module
SecureFilesHelper
def
show_secure_files_setting
(
project
,
user
)
return
false
if
user
.
nil?
Feature
.
enabled?
(
:ci_secure_files
,
project
)
&&
user
.
can?
(
:read_secure_files
,
project
)
end
end
end
app/models/application_setting.rb
View file @
8d9963a8
...
...
@@ -647,6 +647,7 @@ def self.kroki_formats_attributes
reset_memoized_terms
end