Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
1
Merge Requests
1
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
gitlab-ce
Commits
729e3765
Commit
729e3765
authored
Mar 24, 2020
by
GitLab Bot
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add latest changes from gitlab-org/gitlab@master
parent
6f7881ee
Changes
88
Hide whitespace changes
Inline
Side-by-side
Showing
88 changed files
with
1616 additions
and
418 deletions
+1616
-418
.rubocop.yml
.rubocop.yml
+0
-2
app/assets/javascripts/notes/components/discussion_counter.vue
...ssets/javascripts/notes/components/discussion_counter.vue
+30
-7
app/assets/javascripts/notes/stores/actions.js
app/assets/javascripts/notes/stores/actions.js
+5
-0
app/assets/javascripts/notes/stores/mutation_types.js
app/assets/javascripts/notes/stores/mutation_types.js
+1
-0
app/assets/javascripts/notes/stores/mutations.js
app/assets/javascripts/notes/stores/mutations.js
+9
-0
app/assets/javascripts/releases/components/evidence_block.vue
...assets/javascripts/releases/components/evidence_block.vue
+65
-37
app/assets/javascripts/releases/components/release_block.vue
app/assets/javascripts/releases/components/release_block.vue
+1
-1
app/assets/stylesheets/framework/gitlab_theme.scss
app/assets/stylesheets/framework/gitlab_theme.scss
+17
-0
app/assets/stylesheets/framework/header.scss
app/assets/stylesheets/framework/header.scss
+8
-0
app/assets/stylesheets/pages/notes.scss
app/assets/stylesheets/pages/notes.scss
+10
-10
app/controllers/projects/releases/evidences_controller.rb
app/controllers/projects/releases/evidences_controller.rb
+38
-0
app/controllers/projects/releases_controller.rb
app/controllers/projects/releases_controller.rb
+0
-14
app/finders/events_finder.rb
app/finders/events_finder.rb
+8
-1
app/graphql/resolvers/issues_resolver.rb
app/graphql/resolvers/issues_resolver.rb
+8
-3
app/graphql/types/group_type.rb
app/graphql/types/group_type.rb
+6
-0
app/helpers/nav_helper.rb
app/helpers/nav_helper.rb
+4
-0
app/models/event.rb
app/models/event.rb
+7
-2
app/models/event_collection.rb
app/models/event_collection.rb
+8
-1
app/models/issue.rb
app/models/issue.rb
+0
-2
app/models/merge_request.rb
app/models/merge_request.rb
+0
-2
app/models/release.rb
app/models/release.rb
+10
-10
app/models/releases/evidence.rb
app/models/releases/evidence.rb
+4
-2
app/policies/release_policy.rb
app/policies/release_policy.rb
+0
-27
app/policies/releases/evidence_policy.rb
app/policies/releases/evidence_policy.rb
+34
-0
app/presenters/release_presenter.rb
app/presenters/release_presenter.rb
+3
-2
app/presenters/releases/evidence_presenter.rb
app/presenters/releases/evidence_presenter.rb
+16
-0
app/services/event_create_service.rb
app/services/event_create_service.rb
+15
-0
app/services/wiki_pages/base_service.rb
app/services/wiki_pages/base_service.rb
+47
-5
app/services/wiki_pages/create_service.rb
app/services/wiki_pages/create_service.rb
+13
-1
app/services/wiki_pages/destroy_service.rb
app/services/wiki_pages/destroy_service.rb
+13
-1
app/services/wiki_pages/update_service.rb
app/services/wiki_pages/update_service.rb
+20
-1
app/views/layouts/header/_default.html.haml
app/views/layouts/header/_default.html.haml
+2
-0
app/workers/create_evidence_worker.rb
app/workers/create_evidence_worker.rb
+1
-1
changelogs/unreleased/197227-graphql-group-milestones.yml
changelogs/unreleased/197227-graphql-group-milestones.yml
+5
-0
changelogs/unreleased/199065-support-on-demand-release-evidence.yml
.../unreleased/199065-support-on-demand-release-evidence.yml
+5
-0
changelogs/unreleased/209854-cache-es-check.yml
changelogs/unreleased/209854-cache-es-check.yml
+5
-0
changelogs/unreleased/expose-created-at-in-groups-api.yml
changelogs/unreleased/expose-created-at-in-groups-api.yml
+5
-0
changelogs/unreleased/feat-add-toggle-all-discussions-button.yml
...ogs/unreleased/feat-add-toggle-all-discussions-button.yml
+5
-0
changelogs/unreleased/sast-no-env-file.yml
changelogs/unreleased/sast-no-env-file.yml
+5
-0
config/routes/project.rb
config/routes/project.rb
+3
-1
doc/api/graphql/reference/gitlab_schema.graphql
doc/api/graphql/reference/gitlab_schema.graphql
+100
-0
doc/api/graphql/reference/gitlab_schema.json
doc/api/graphql/reference/gitlab_schema.json
+219
-0
doc/api/groups.md
doc/api/groups.md
+7
-2
doc/integration/elasticsearch.md
doc/integration/elasticsearch.md
+9
-0
lib/api/entities/event.rb
lib/api/entities/event.rb
+1
-0
lib/api/entities/group.rb
lib/api/entities/group.rb
+1
-0
lib/api/entities/release.rb
lib/api/entities/release.rb
+2
-0
lib/api/entities/releases/evidence.rb
lib/api/entities/releases/evidence.rb
+15
-0
lib/api/helpers/presentable.rb
lib/api/helpers/presentable.rb
+2
-1
lib/event_filter.rb
lib/event_filter.rb
+18
-1
lib/gitlab/ci/templates/Security/SAST.gitlab-ci.yml
lib/gitlab/ci/templates/Security/SAST.gitlab-ci.yml
+2
-2
lib/gitlab/experimentation.rb
lib/gitlab/experimentation.rb
+6
-0
locale/gitlab.pot
locale/gitlab.pot
+7
-1
scripts/trigger-build-docs
scripts/trigger-build-docs
+7
-5
spec/controllers/projects/releases/evidences_controller_spec.rb
...ontrollers/projects/releases/evidences_controller_spec.rb
+165
-0
spec/controllers/projects/releases_controller_spec.rb
spec/controllers/projects/releases_controller_spec.rb
+5
-140
spec/factories/events.rb
spec/factories/events.rb
+2
-2
spec/factories/evidences.rb
spec/factories/evidences.rb
+1
-1
spec/finders/events_finder_spec.rb
spec/finders/events_finder_spec.rb
+28
-2
spec/fixtures/api/schemas/public_api/v4/release.json
spec/fixtures/api/schemas/public_api/v4/release.json
+4
-0
spec/fixtures/api/schemas/public_api/v4/release/evidence.json
.../fixtures/api/schemas/public_api/v4/release/evidence.json
+14
-0
spec/frontend/locale/index_spec.js
spec/frontend/locale/index_spec.js
+4
-8
spec/frontend/notes/components/discussion_counter_spec.js
spec/frontend/notes/components/discussion_counter_spec.js
+54
-5
spec/frontend/notes/stores/mutation_spec.js
spec/frontend/notes/stores/mutation_spec.js
+46
-0
spec/frontend/releases/components/evidence_block_spec.js
spec/frontend/releases/components/evidence_block_spec.js
+7
-8
spec/frontend/releases/mock_data.js
spec/frontend/releases/mock_data.js
+20
-3
spec/graphql/resolvers/issues_resolver_spec.rb
spec/graphql/resolvers/issues_resolver_spec.rb
+27
-8
spec/helpers/nav_helper_spec.rb
spec/helpers/nav_helper_spec.rb
+20
-0
spec/lib/api/entities/release_spec.rb
spec/lib/api/entities/release_spec.rb
+18
-16
spec/lib/event_filter_spec.rb
spec/lib/event_filter_spec.rb
+50
-0
spec/lib/gitlab/import_export/all_models.yml
spec/lib/gitlab/import_export/all_models.yml
+1
-1
spec/lib/gitlab/import_export/safe_model_attributes.yml
spec/lib/gitlab/import_export/safe_model_attributes.yml
+1
-1
spec/models/event_collection_spec.rb
spec/models/event_collection_spec.rb
+70
-11
spec/models/event_spec.rb
spec/models/event_spec.rb
+17
-4
spec/models/release_spec.rb
spec/models/release_spec.rb
+5
-5
spec/models/releases/evidence_spec.rb
spec/models/releases/evidence_spec.rb
+1
-1
spec/presenters/release_presenter_spec.rb
spec/presenters/release_presenter_spec.rb
+0
-24
spec/requests/api/events_spec.rb
spec/requests/api/events_spec.rb
+20
-0
spec/requests/api/graphql/group_query_spec.rb
spec/requests/api/graphql/group_query_spec.rb
+3
-0
spec/requests/api/groups_spec.rb
spec/requests/api/groups_spec.rb
+22
-0
spec/requests/api/releases_spec.rb
spec/requests/api/releases_spec.rb
+18
-3
spec/services/event_create_service_spec.rb
spec/services/event_create_service_spec.rb
+40
-0
spec/services/wiki_pages/base_service_spec.rb
spec/services/wiki_pages/base_service_spec.rb
+11
-9
spec/services/wiki_pages/create_service_spec.rb
spec/services/wiki_pages/create_service_spec.rb
+41
-8
spec/services/wiki_pages/destroy_service_spec.rb
spec/services/wiki_pages/destroy_service_spec.rb
+20
-2
spec/services/wiki_pages/update_service_spec.rb
spec/services/wiki_pages/update_service_spec.rb
+43
-9
spec/support/helpers/stub_experiments.rb
spec/support/helpers/stub_experiments.rb
+4
-0
spec/workers/create_evidence_worker_spec.rb
spec/workers/create_evidence_worker_spec.rb
+2
-2
No files found.
.rubocop.yml
View file @
729e3765
...
...
@@ -202,7 +202,6 @@ GitlabSecurity/PublicSend:
Gitlab/DuplicateSpecLocation
:
Exclude
:
-
ee/spec/controllers/groups_controller_spec.rb
-
ee/spec/controllers/projects/jobs_controller_spec.rb
-
ee/spec/helpers/auth_helper_spec.rb
-
ee/spec/lib/gitlab/gl_repository_spec.rb
...
...
@@ -215,7 +214,6 @@ Gitlab/DuplicateSpecLocation:
-
ee/spec/services/merge_requests/refresh_service_spec.rb
-
ee/spec/services/merge_requests/update_service_spec.rb
-
ee/spec/services/system_hooks_service_spec.rb
-
ee/spec/controllers/ee/groups_controller_spec.rb
-
ee/spec/controllers/ee/projects/jobs_controller_spec.rb
-
ee/spec/helpers/ee/auth_helper_spec.rb
-
ee/spec/lib/ee/gitlab/gl_repository_spec.rb
...
...
app/assets/javascripts/notes/components/discussion_counter.vue
View file @
729e3765
<
script
>
import
{
mapGetters
}
from
'
vuex
'
;
import
{
mapGetters
,
mapActions
}
from
'
vuex
'
;
import
{
GlTooltipDirective
}
from
'
@gitlab/ui
'
;
import
Icon
from
'
~/vue_shared/components/icon.vue
'
;
import
discussionNavigation
from
'
../mixins/discussion_navigation
'
;
...
...
@@ -18,13 +18,11 @@ export default {
'
getNoteableData
'
,
'
resolvableDiscussionsCount
'
,
'
unresolvedDiscussionsCount
'
,
'
discussions
'
,
]),
isLoggedIn
()
{
return
this
.
getUserData
.
id
;
},
hasNextButton
()
{
return
this
.
isLoggedIn
&&
!
this
.
allResolved
;
},
allResolved
()
{
return
this
.
unresolvedDiscussionsCount
===
0
;
},
...
...
@@ -34,6 +32,21 @@ export default {
resolvedDiscussionsCount
()
{
return
this
.
resolvableDiscussionsCount
-
this
.
unresolvedDiscussionsCount
;
},
toggeableDiscussions
()
{
return
this
.
discussions
.
filter
(
discussion
=>
!
discussion
.
individual_note
);
},
allExpanded
()
{
return
this
.
toggeableDiscussions
.
every
(
discussion
=>
discussion
.
expanded
);
},
},
methods
:
{
...
mapActions
([
'
setExpandDiscussions
'
]),
handleExpandDiscussions
()
{
this
.
setExpandDiscussions
({
discussionIds
:
this
.
toggeableDiscussions
.
map
(
discussion
=>
discussion
.
id
),
expanded
:
!
this
.
allExpanded
,
});
},
},
};
</
script
>
...
...
@@ -44,8 +57,8 @@ export default {
ref=
"discussionCounter"
class=
"line-resolve-all-container full-width-mobile"
>
<div
class=
"full-width-mobile d-flex d-sm-
block
"
>
<div
:class=
"
{ 'has-next-btn': hasNextButton }"
class="line-resolve-all">
<div
class=
"full-width-mobile d-flex d-sm-
flex
"
>
<div
class=
"line-resolve-all"
>
<span
:class=
"
{ 'is-active': allResolved }"
class="line-resolve-btn is-disabled"
...
...
@@ -75,7 +88,7 @@ export default {
<div
v-if=
"isLoggedIn && !allResolved"
class=
"btn-group btn-group-sm"
role=
"group"
>
<button
v-gl-tooltip
title=
"Jump to next unresolved thread
"
:title=
"__('Jump to next unresolved thread')
"
class=
"btn btn-default discussion-next-btn"
data-track-event=
"click_button"
data-track-label=
"mr_next_unresolved_thread"
...
...
@@ -85,6 +98,16 @@ export default {
<icon
name=
"comment-next"
/>
</button>
</div>
<div
v-if=
"isLoggedIn"
class=
"btn-group btn-group-sm"
role=
"group"
>
<button
v-gl-tooltip
:title=
"__('Toggle all threads')"
class=
"btn btn-default toggle-all-discussions-btn"
@
click=
"handleExpandDiscussions"
>
<icon
:name=
"allExpanded ? 'angle-up' : 'angle-down'"
/>
</button>
</div>
</div>
</div>
</
template
>
app/assets/javascripts/notes/stores/actions.js
View file @
729e3765
...
...
@@ -46,6 +46,10 @@ export const setNotesFetchedState = ({ commit }, state) =>
export
const
toggleDiscussion
=
({
commit
},
data
)
=>
commit
(
types
.
TOGGLE_DISCUSSION
,
data
);
export
const
setExpandDiscussions
=
({
commit
},
{
discussionIds
,
expanded
})
=>
{
commit
(
types
.
SET_EXPAND_DISCUSSIONS
,
{
discussionIds
,
expanded
});
};
export
const
fetchDiscussions
=
({
commit
,
dispatch
},
{
path
,
filter
,
persistFilter
})
=>
{
const
config
=
filter
!==
undefined
...
...
@@ -54,6 +58,7 @@ export const fetchDiscussions = ({ commit, dispatch }, { path, filter, persistFi
return
axios
.
get
(
path
,
config
).
then
(({
data
})
=>
{
commit
(
types
.
SET_INITIAL_DISCUSSIONS
,
data
);
dispatch
(
'
updateResolvableDiscussionsCounts
'
);
});
};
...
...
app/assets/javascripts/notes/stores/mutation_types.js
View file @
729e3765
...
...
@@ -24,6 +24,7 @@ export const REMOVE_CONVERTED_DISCUSSION = 'REMOVE_CONVERTED_DISCUSSION';
export
const
COLLAPSE_DISCUSSION
=
'
COLLAPSE_DISCUSSION
'
;
export
const
EXPAND_DISCUSSION
=
'
EXPAND_DISCUSSION
'
;
export
const
TOGGLE_DISCUSSION
=
'
TOGGLE_DISCUSSION
'
;
export
const
SET_EXPAND_DISCUSSIONS
=
'
SET_EXPAND_DISCUSSIONS
'
;
export
const
UPDATE_RESOLVABLE_DISCUSSIONS_COUNTS
=
'
UPDATE_RESOLVABLE_DISCUSSIONS_COUNTS
'
;
export
const
SET_CURRENT_DISCUSSION_ID
=
'
SET_CURRENT_DISCUSSION_ID
'
;
...
...
app/assets/javascripts/notes/stores/mutations.js
View file @
729e3765
...
...
@@ -190,6 +190,15 @@ export default {
});
},
[
types
.
SET_EXPAND_DISCUSSIONS
](
state
,
{
discussionIds
,
expanded
})
{
if
(
discussionIds
?.
length
)
{
discussionIds
.
forEach
(
discussionId
=>
{
const
discussion
=
utils
.
findNoteObjectById
(
state
.
discussions
,
discussionId
);
Object
.
assign
(
discussion
,
{
expanded
});
});
}
},
[
types
.
UPDATE_NOTE
](
state
,
note
)
{
const
noteObj
=
utils
.
findNoteObjectById
(
state
.
discussions
,
note
.
discussion_id
);
...
...
app/assets/javascripts/releases/components/evidence_block.vue
View file @
729e3765
<
script
>
import
{
GlLink
,
GlTooltipDirective
}
from
'
@gitlab/ui
'
;
import
dateFormat
from
'
dateformat
'
;
import
{
GlLink
,
GlTooltipDirective
,
GlIcon
}
from
'
@gitlab/ui
'
;
import
{
__
,
sprintf
}
from
'
~/locale
'
;
import
{
truncateSha
}
from
'
~/lib/utils/text_utility
'
;
import
Icon
from
'
~/vue_shared/components/icon.vue
'
;
import
{
getTimeago
}
from
'
~/lib/utils/datetime_utility
'
;
import
ClipboardButton
from
'
~/vue_shared/components/clipboard_button.vue
'
;
import
ExpandButton
from
'
~/vue_shared/components/expand_button.vue
'
;
...
...
@@ -12,7 +13,7 @@ export default {
ClipboardButton
,
ExpandButton
,
GlLink
,
Icon
,
Gl
Icon
,
},
directives
:
{
GlTooltip
:
GlTooltipDirective
,
...
...
@@ -24,17 +25,33 @@ export default {
},
},
computed
:
{
evidence
Title
()
{
return
sprintf
(
__
(
'
%{tag}-evidence.json
'
),
{
tag
:
this
.
release
.
tagName
})
;
evidence
s
()
{
return
this
.
release
.
evidences
;
},
evidenceUrl
()
{
return
this
.
release
.
assets
&&
this
.
release
.
assets
.
evidenceFilePath
;
},
methods
:
{
evidenceTitle
(
index
)
{
const
[
tag
,
evidence
,
filename
]
=
this
.
release
.
evidences
[
index
].
filepath
.
split
(
'
/
'
).
slice
(
-
3
);
return
sprintf
(
__
(
'
%{tag}-%{evidence}-%{filename}
'
),
{
tag
,
evidence
,
filename
});
},
evidenceUrl
(
index
)
{
return
this
.
release
.
evidences
[
index
].
filepath
;
},
sha
(
index
)
{
return
this
.
release
.
evidences
[
index
].
sha
;
},
shortSha
()
{
return
truncateSha
(
this
.
sha
);
shortSha
(
index
)
{
return
truncateSha
(
this
.
release
.
evidences
[
index
].
sha
);
},
sha
()
{
return
this
.
release
.
evidenceSha
;
collectedAt
(
index
)
{
return
dateFormat
(
this
.
release
.
evidences
[
index
].
collectedAt
,
'
mmmm dS, yyyy, h:MM TT
'
);
},
timeSummary
(
index
)
{
const
{
format
}
=
getTimeago
();
const
summary
=
sprintf
(
__
(
'
Collected %{time}
'
),
{
time
:
format
(
this
.
release
.
evidences
[
index
].
collectedAt
),
});
return
summary
;
},
},
};
...
...
@@ -43,34 +60,45 @@ export default {
<
template
>
<div>
<div
class=
"card-text prepend-top-default"
>
<b>
{{
__
(
'
Evidence collection
'
)
}}
</b>
<b>
{{
__
(
'
Evidence collection
'
)
}}
</b>
</div>
<div
class=
"d-flex align-items-baseline"
>
<gl-link
v-gl-tooltip
class=
"monospace"
:title=
"__('Download evidence JSON')"
:download=
"evidenceTitle"
:href=
"evidenceUrl"
>
<icon
name=
"review-list"
class=
"align-top append-right-4"
/><span>
{{
evidenceTitle
}}
</span>
</gl-link>
<div
v-for=
"(evidence, index) in evidences"
:key=
"evidenceTitle(index)"
class=
"mb-2"
>
<div
class=
"d-flex align-items-center"
>
<gl-link
v-gl-tooltip
class=
"d-flex align-items-center monospace"
:title=
"__('Download evidence JSON')"
:download=
"evidenceTitle(index)"
:href=
"evidenceUrl(index)"
>
<gl-icon
name=
"review-list"
class=
"align-middle append-right-8"
/>
<span>
{{
evidenceTitle
(
index
)
}}
</span>
</gl-link>
<expand-button>
<template
slot=
"short"
>
<span
class=
"js-short monospace"
>
{{
shortSha
(
index
)
}}
</span>
</
template
>
<
template
slot=
"expanded"
>
<span
class=
"js-expanded monospace gl-pl-1"
>
{{
sha
(
index
)
}}
</span>
</
template
>
</expand-button>
<clipboard-button
:title=
"__('Copy evidence SHA')"
:text=
"sha(index)"
css-class=
"btn-default btn-transparent btn-clipboard"
/>
</div>
<expand-button>
<template
slot=
"short"
>
<span
class=
"js-short monospace"
>
{{
shortSha
}}
</span>
</
template
>
<
template
slot=
"expanded"
>
<span
class=
"js-expanded monospace gl-pl-1"
>
{{
sha
}}
</span>
</
template
>
</expand-button>
<clipboard-button
:title=
"__('Copy evidence SHA')"
:text=
"sha"
css-class=
"btn-default btn-transparent btn-clipboard"
/>
<div
class=
"d-flex align-items-center text-muted"
>
<gl-icon
v-gl-tooltip
name=
"clock"
class=
"align-middle append-right-8"
:title=
"collectedAt(index)"
/>
<span>
{{ timeSummary(index) }}
</span>
</div>
</div>
</div>
</template>
app/assets/javascripts/releases/components/release_block.vue
View file @
729e3765
...
...
@@ -44,7 +44,7 @@ export default {
return
this
.
release
.
assets
||
{};
},
hasEvidence
()
{
return
Boolean
(
this
.
release
.
evidence
Sha
);
return
Boolean
(
this
.
release
.
evidence
s
&&
this
.
release
.
evidences
.
length
);
},
milestones
()
{
return
this
.
release
.
milestones
||
[];
...
...
app/assets/stylesheets/framework/gitlab_theme.scss
View file @
729e3765
...
...
@@ -68,6 +68,23 @@
.header-user-avatar
{
border-color
:
$search-and-nav-links
;
}
.header-user-notification-dot
{
border
:
2px
solid
$nav-svg-color
;
}
}
&
:focus:hover
,
&
:focus
{
&
.header-user-dropdown-toggle
.header-user-notification-dot
{
border-color
:
$white-light
;
}
}
&
:hover
{
&
.header-user-dropdown-toggle
.header-user-notification-dot
{
border-color
:
$nav-svg-color
+
33
;
}
}
&
:hover
,
...
...
app/assets/stylesheets/framework/header.scss
View file @
729e3765
...
...
@@ -567,6 +567,14 @@
border
:
1px
solid
$gray-normal
;
}
.header-user-notification-dot
{
background-color
:
$orange-500
;
height
:
10px
;
width
:
10px
;
right
:
8px
;
top
:
-8px
;
}
.with-performance-bar
.navbar-gitlab
{
top
:
$performance-bar-height
;
}
...
...
app/assets/stylesheets/pages/notes.scss
View file @
729e3765
...
...
@@ -842,11 +842,11 @@ $note-form-margin-left: 72px;
white-space
:
nowrap
;
}
.
btn-group
{
margin-left
:
-4px
;
.
discussion-next-btn
{
border-radius
:
0
;
}
.
discussion-next
-btn
{
.
toggle-all-discussions
-btn
{
border-top-left-radius
:
0
;
border-bottom-left-radius
:
0
;
}
...
...
@@ -859,7 +859,6 @@ $note-form-margin-left: 72px;
}
&
.discussion-create-issue-btn
{
margin-left
:
-4px
;
border-radius
:
0
;
border-right
:
0
;
...
...
@@ -873,6 +872,10 @@ $note-form-margin-left: 72px;
}
}
}
&
.discussion-next-btn
{
border-right
:
0
;
}
}
}
...
...
@@ -884,12 +887,9 @@ $note-form-margin-left: 72px;
border
:
1px
solid
$border-color
;
border-radius
:
$border-radius-default
;
font-size
:
$gl-btn-small-font-size
;
&
.has-next-btn
{
border-top-right-radius
:
0
;
border-bottom-right-radius
:
0
;
border-right
:
0
;
}
border-top-right-radius
:
0
;
border-bottom-right-radius
:
0
;
border-right
:
0
;
.line-resolve-btn
{
margin-right
:
5px
;
...
...
app/controllers/projects/releases/evidences_controller.rb
0 → 100644
View file @
729e3765
# frozen_string_literal: true
module
Projects
module
Releases
class
EvidencesController
<
Projects
::
ApplicationController
before_action
:require_non_empty_project
before_action
:release
before_action
:authorize_read_release_evidence!
def
show
respond_to
do
|
format
|
format
.
json
do
render
json:
evidence
.
summary
end
end
end
private
def
authorize_read_release_evidence!
access_denied!
unless
Feature
.
enabled?
(
:release_evidence
,
project
,
default_enabled:
true
)
access_denied!
unless
can?
(
current_user
,
:read_release_evidence
,
evidence
)
end
def
release
@release
||=
project
.
releases
.
find_by_tag!
(
sanitized_tag_name
)
end
def
evidence
release
.
evidences
.
find
(
params
[
:id
])
end
def
sanitized_tag_name
CGI
.
unescape
(
params
[
:tag
])
end
end
end
end
app/controllers/projects/releases_controller.rb
View file @
729e3765
...
...
@@ -11,7 +11,6 @@ class Projects::ReleasesController < Projects::ApplicationController
push_frontend_feature_flag
(
:release_show_page
,
project
,
default_enabled:
true
)
end
before_action
:authorize_update_release!
,
only:
%i[edit update]
before_action
:authorize_read_release_evidence!
,
only:
[
:evidence
]
def
index
respond_to
do
|
format
|
...
...
@@ -22,14 +21,6 @@ class Projects::ReleasesController < Projects::ApplicationController
end
end
def
evidence
respond_to
do
|
format
|
format
.
json
do
render
json:
release
.
evidence_summary
end
end
end
def
show
return
render_404
unless
Feature
.
enabled?
(
:release_show_page
,
project
,
default_enabled:
true
)
...
...
@@ -64,11 +55,6 @@ class Projects::ReleasesController < Projects::ApplicationController
access_denied!
unless
can?
(
current_user
,
:update_release
,
release
)
end
def
authorize_read_release_evidence!
access_denied!
unless
Feature
.
enabled?
(
:release_evidence
,
project
,
default_enabled:
true
)
access_denied!
unless
can?
(
current_user
,
:read_release_evidence
,
release
)
end
def
release
@release
||=
project
.
releases
.
find_by_tag!
(
sanitized_tag_name
)
end
...
...
app/finders/events_finder.rb
View file @
729e3765
...
...
@@ -52,10 +52,17 @@ class EventsFinder
if
current_user
&&
scope
==
'all'
EventCollection
.
new
(
current_user
.
authorized_projects
).
all_project_events
else
source
.
events
# EventCollection is responsible for applying the feature flag
apply_feature_flags
(
source
.
events
)
end
end
def
apply_feature_flags
(
events
)
return
events
if
::
Feature
.
enabled?
(
:wiki_events
)
events
.
not_wiki_page
end
# rubocop: disable CodeReuse/ActiveRecord
def
by_current_user_access
(
events
)
events
.
merge
(
Project
.
public_or_visible_to_user
(
current_user
))
...
...
app/graphql/resolvers/issues_resolver.rb
View file @
729e3765
...
...
@@ -56,12 +56,17 @@ module Resolvers
# The project could have been loaded in batch by `BatchLoader`.
# At this point we need the `id` of the project to query for issues, so
# make sure it's loaded and not `nil` before continuing.
project
=
object
.
respond_to?
(
:sync
)
?
object
.
sync
:
object
return
Issue
.
none
if
project
.
nil?
parent
=
object
.
respond_to?
(
:sync
)
?
object
.
sync
:
object
return
Issue
.
none
if
parent
.
nil?
if
parent
.
is_a?
(
Group
)
args
[
:group_id
]
=
parent
.
id
else
args
[
:project_id
]
=
parent
.
id
end
# Will need to be be made group & namespace aware with
# https://gitlab.com/gitlab-org/gitlab-foss/issues/54520
args
[
:project_id
]
=
project
.
id
args
[
:iids
]
||=
[
args
[
:iid
]].
compact
args
[
:attempt_project_search_optimizations
]
=
args
[
:search
].
present?
...
...
app/graphql/types/group_type.rb
View file @
729e3765
...
...
@@ -43,6 +43,12 @@ module Types
description:
'Parent group'
,
resolve:
->
(
obj
,
_args
,
_ctx
)
{
Gitlab
::
Graphql
::
Loaders
::
BatchModelLoader
.
new
(
Group
,
obj
.
parent_id
).
find
}
field
:issues
,
Types
::
IssueType
.
connection_type
,
null:
true
,
description:
'Issues of the group'
,
resolver:
Resolvers
::
IssuesResolver
field
:milestones
,
Types
::
MilestoneType
.
connection_type
,
null:
true
,
description:
'Find milestones'
,
resolver:
Resolvers
::
MilestoneResolver
...
...
app/helpers/nav_helper.rb
View file @
729e3765
...
...
@@ -65,6 +65,10 @@ module NavHelper
%w(groups#issues labels#index milestones#index boards#index boards#show)
end
def
show_user_notification_dot?
experiment_enabled?
(
:ci_notification_dot
)
end
private
def
get_header_links
...
...
app/models/event.rb
View file @
729e3765
...
...
@@ -36,6 +36,8 @@ class Event < ApplicationRecord
expired:
EXPIRED
).
freeze
WIKI_ACTIONS
=
[
CREATED
,
UPDATED
,
DESTROYED
].
freeze
TARGET_TYPES
=
HashWithIndifferentAccess
.
new
(
issue:
Issue
,
milestone:
Milestone
,
...
...
@@ -81,7 +83,10 @@ class Event < ApplicationRecord
scope
:recent
,
->
{
reorder
(
id: :desc
)
}
scope
:code_push
,
->
{
where
(
action:
PUSHED
)
}
scope
:merged
,
->
{
where
(
action:
MERGED
)
}
scope
:for_wiki_page
,
->
{
where
(
target_type:
WikiPage
::
Meta
.
name
)
}
scope
:for_wiki_page
,
->
{
where
(
target_type:
'WikiPage::Meta'
)
}
# Needed to implement feature flag: can be removed when feature flag is removed
scope
:not_wiki_page
,
->
{
where
(
'target_type IS NULL or target_type <> ?'
,
'WikiPage::Meta'
)
}
scope
:with_associations
,
->
do
# We're using preload for "push_event_payload" as otherwise the association
...
...
@@ -229,7 +234,7 @@ class Event < ApplicationRecord
end
def
wiki_page?
target_type
==
WikiPage
::
Meta
.
name
target_type
==
'WikiPage::Meta'
end
def
milestone
...
...
app/models/event_collection.rb
View file @
729e3765
...
...
@@ -33,16 +33,23 @@ class EventCollection
project_events
end
relation
=
apply_feature_flags
(
relation
)
relation
=
paginate_events
(
relation
)
relation
.
with_associations
.
to_a
end
def
all_project_events
Event
.
from_union
([
project_events
]).
recent
apply_feature_flags
(
Event
.
from_union
([
project_events
]).
recent
)
end
private
def
apply_feature_flags
(
events
)
return
events
if
::
Feature
.
enabled?
(
:wiki_events
)
events
.
not_wiki_page
end
def
project_events
relation_with_join_lateral
(
'project_id'
,
projects
)
end
...
...
app/models/issue.rb
View file @
729e3765
...
...
@@ -78,8 +78,6 @@ class Issue < ApplicationRecord
scope
:counts_by_state
,
->
{
reorder
(
nil
).
group
(
:state_id
).
count
}
ignore_column
:state
,
remove_with:
'12.10'
,
remove_after:
'2020-03-22'
after_commit
:expire_etag_cache
,
unless: :importing?
after_save
:ensure_metrics
,
unless: :importing?
...
...
app/models/merge_request.rb
View file @
729e3765
...
...
@@ -261,8 +261,6 @@ class MergeRequest < ApplicationRecord
includes
(
:metrics
)
end
ignore_column
:state
,
remove_with:
'12.10'
,
remove_after:
'2020-03-22'
after_save
:keep_around_commit
,
unless: :importing?
alias_attribute
:project
,
:target_project
...
...
app/models/release.rb
View file @
729e3765
...
...
@@ -16,7 +16,7 @@ class Release < ApplicationRecord
has_many
:milestone_releases
has_many
:milestones
,
through: :milestone_releases
has_
one
:evidence
has_
many
:evidences
,
inverse_of: :release
,
class_name:
'Releases::Evidence'
default_value_for
:released_at
,
allows_nil:
false
do
Time
.
zone
.
now
...
...
@@ -28,7 +28,7 @@ class Release < ApplicationRecord
validates_associated
:milestone_releases
,
message:
->
(
_
,
obj
)
{
obj
[
:value
].
map
(
&
:errors
).
map
(
&
:full_messages
).
join
(
","
)
}
scope
:sorted
,
->
{
order
(
released_at: :desc
)
}
scope
:preloaded
,
->
{
includes
(
project: :namespace
)
}
scope
:preloaded
,
->
{
includes
(
:evidences
,
:milestones
,
project:
[
:project_feature
,
:route
,
{
namespace: :route
}]
)
}
scope
:with_project_and_namespace
,
->
{
includes
(
project: :namespace
)
}
scope
:recent
,
->
{
sorted
.
limit
(
MAX_NUMBER_TO_DISPLAY
)
}
...
...
@@ -66,27 +66,27 @@ class Release < ApplicationRecord
end
def
upcoming_release?
released_at
.
present?
&&
released_at
>
Time
.
zone
.
now
released_at
.
present?
&&
released_at
.
to_i
>
Time
.
zone
.
now
.
to_i
end
def
historical_release?
released_at
.
present?
&&
released_at
<
created_at
released_at
.
present?
&&
released_at
.
to_i
<
created_at
.
to_i
end
def
name
self
.
read_attribute
(
:name
)
||
tag
end
def
evidence_sha
evidence
&
.
summary_sha
def
milestone_titles
self
.
milestones
.
map
{
|
m
|
m
.
title
}.
sort
.
join
(
", "
)
end
def
evidence_s
ummary
evidence
&
.
summary
||
{}
def
evidence_s
ha
evidence
s
.
first
&
.
summary_sha
end
def
milestone_titles
self
.
milestones
.
map
{
|
m
|
m
.
title
}.
sort
.
join
(
", "
)
def
evidence_summary
evidences
.
first
&
.
summary
||
{}
end
private
...
...
app/models/evidence.rb
→
app/models/
releases/
evidence.rb
View file @
729e3765
# frozen_string_literal: true
class
Evidence
<
ApplicationRecord
class
Releases::
Evidence
<
ApplicationRecord
include
ShaAttribute
include
Presentable
belongs_to
:release
belongs_to
:release
,
inverse_of: :evidences
before_validation
:generate_summary_and_sha
default_scope
{
order
(
created_at: :asc
)
}
sha_attribute
:summary_sha
alias_attribute
:collected_at
,
:created_at
def
milestones
@milestones
||=
release
.
milestones
.
includes
(
:issues
)
...
...
app/policies/release_policy.rb
View file @
729e3765
...
...
@@ -2,31 +2,4 @@
class
ReleasePolicy
<
BasePolicy
delegate
{
@subject
.
project
}
rule
{
allowed_to_read_evidence
&
external_authorization_service_disabled
}.
policy
do
enable
:read_release_evidence
end
##
# evidence.summary includes the following entities:
# - Release
# - git-tag (Repository)
# - Project
# - Milestones
# - Issues
condition
(
:allowed_to_read_evidence
)
do
can?
(
:read_release
)
&&
can?
(
:download_code
)
&&
can?
(
:read_project
)
&&
can?
(
:read_milestone
)
&&
can?
(
:read_issue
)
end
##
# Currently, we don't support release evidence for the GitLab instances
# that enables external authorization services.
# See https://gitlab.com/gitlab-org/gitlab/issues/121930.
condition
(
:external_authorization_service_disabled
)
do
!
Gitlab
::
ExternalAuthorization
::
Config
.
enabled?
end
end
app/policies/releases/evidence_policy.rb
0 → 100644
View file @
729e3765
# frozen_string_literal: true
module
Releases
class
EvidencePolicy
<
BasePolicy
delegate
{
@subject
.
release
.
project
}
rule
{
allowed_to_read_evidence
&
external_authorization_service_disabled
}.
policy
do
enable
:read_release_evidence
end
##
# evidence.summary includes the following entities:
# - Release
# - git-tag (Repository)
# - Project
# - Milestones
# - Issues
condition
(
:allowed_to_read_evidence
)
do
can?
(
:read_release
)
&&
can?
(
:download_code
)
&&
can?
(
:read_project
)
&&
can?
(
:read_milestone
)
&&
can?
(
:read_issue
)
end
##
# Currently, we don't support release evidence for the GitLab instances
# that enables external authorization services.
# See https://gitlab.com/gitlab-org/gitlab/issues/121930.
condition
(
:external_authorization_service_disabled
)
do
!
Gitlab
::
ExternalAuthorization
::
Config
.
enabled?
end
end
end
app/presenters/release_presenter.rb
View file @
729e3765
...
...
@@ -44,9 +44,10 @@ class ReleasePresenter < Gitlab::View::Presenter::Delegated
end
def
evidence_file_path
return
unless
release
.
evidence
.
present?
evidence
=
release
.
evidences
.
first
return
unless
evidence
evidence_project_release_url
(
project
,
release
.
to_param
,
format: :json
)
project_evidence_url
(
project
,
release
,
evidence
,
format: :json
)
end
private
...
...
app/presenters/releases/evidence_presenter.rb
0 → 100644
View file @
729e3765
# frozen_string_literal: true
module
Releases
class
EvidencePresenter
<
Gitlab
::
View
::
Presenter
::
Delegated
include
ActionView
::
Helpers
::
UrlHelper
presents
:evidence
def
filepath
release
=
evidence
.
release
project
=
release
.
project
project_evidence_url
(
project
,
release
,
evidence
,
format: :json
)
end
end
end
app/services/event_create_service.rb
View file @
729e3765
...
...
@@ -8,6 +8,8 @@
# EventCreateService.new.new_issue(issue, current_user)
#
class
EventCreateService
IllegalActionError
=
Class
.
new
(
StandardError
)
def
open_issue
(
issue
,
current_user
)
create_record_event
(
issue
,
current_user
,
Event
::
CREATED
)
end
...
...
@@ -80,6 +82,19 @@ class EventCreateService
create_push_event
(
BulkPushEventPayloadService
,
project
,
current_user
,
push_data
)
end
# Create a new wiki page event
#
# @param [WikiPage::Meta] wiki_page_meta The event target
# @param [User] current_user The event author
# @param [Integer] action One of the Event::WIKI_ACTIONS
def
wiki_event
(
wiki_page_meta
,
current_user
,
action
)
return
unless
Feature
.
enabled?
(
:wiki_events
)
raise
IllegalActionError
,
action
unless
Event
::
WIKI_ACTIONS
.
include?
(
action
)
create_record_event
(
wiki_page_meta
,
current_user
,
action
)
end
private
def
create_record_event
(
record
,
current_user
,
status
)
...
...
app/services/wiki_pages/base_service.rb
View file @
729e3765
# frozen_string_literal: true
module
WikiPages
# There are 3 notions of 'action' that inheriting classes must implement:
#
# - external_action: the action we report to external clients with webhooks
# - usage_counter_action: the action that we count in out internal counters
# - event_action: what we record as the value of `Event#action`
class
BaseService
<
::
BaseService
private
def
execute_hooks
(
page
,
action
=
'create'
)
page_data
=
Gitlab
::
DataBuilder
::
WikiPage
.
build
(
page
,
current_user
,
action
)
def
execute_hooks
(
page
)
page_data
=
payload
(
page
)
@project
.
execute_hooks
(
page_data
,
:wiki_page_hooks
)
@project
.
execute_services
(
page_data
,
:wiki_page_hooks
)
increment_usage
(
action
)
increment_usage
create_wiki_event
(
page
)
end
# Passed to web-hooks, and send to external consumers.
def
external_action
raise
NotImplementedError
end
# Passed to the WikiPageCounter to count events.
# Must be one of WikiPageCounter::KNOWN_EVENTS
def
usage_counter_action
raise
NotImplementedError
end
# Used to create `Event` records.
# Must be a valid value for `Event#action`
def
event_action
raise
NotImplementedError
end
def
payload
(
page
)
Gitlab
::
DataBuilder
::
WikiPage
.
build
(
page
,
current_user
,
external_action
)
end
# This method throws an error if the action is an unanticipated value.
def
increment_usage
(
action
)
Gitlab
::
UsageDataCounters
::
WikiPageCounter
.
count
(
action
)
def
increment_usage
Gitlab
::
UsageDataCounters
::
WikiPageCounter
.
count
(
usage_counter_action
)
end
def
create_wiki_event
(
page
)
return
unless
::
Feature
.
enabled?
(
:wiki_events
)
slug
=
slug_for_page
(
page
)
Event
.
transaction
do
wiki_page_meta
=
WikiPage
::
Meta
.
find_or_create
(
slug
,
page
)
EventCreateService
.
new
.
wiki_event
(
wiki_page_meta
,
current_user
,
event_action
)
end
end
def
slug_for_page
(
page
)
page
.
slug
end
end
end
...
...
app/services/wiki_pages/create_service.rb
View file @
729e3765
...
...
@@ -7,10 +7,22 @@ module WikiPages
page
=
WikiPage
.
new
(
project_wiki
)
if
page
.
create
(
@params
)
execute_hooks
(
page
,
'create'
)
execute_hooks
(
page
)
end
page
end
def
usage_counter_action
:create
end
def
external_action
'create'
end
def
event_action
Event
::
CREATED
end
end
end
app/services/wiki_pages/destroy_service.rb
View file @
729e3765
...
...
@@ -4,10 +4,22 @@ module WikiPages
class
DestroyService
<
WikiPages
::
BaseService
def
execute
(
page
)
if
page
&
.
delete
execute_hooks
(
page
,
'delete'
)
execute_hooks
(
page
)
end
page
end
def
usage_counter_action
:delete
end
def
external_action
'delete'
end
def
event_action
Event
::
DESTROYED
end
end
end
app/services/wiki_pages/update_service.rb
View file @
729e3765
...
...
@@ -3,11 +3,30 @@
module
WikiPages
class
UpdateService
<
WikiPages
::
BaseService
def
execute
(
page
)
# this class is not thread safe!
@old_slug
=
page
.
slug
if
page
.
update
(
@params
)
execute_hooks
(
page
,
'update'
)
execute_hooks
(
page
)
end
page
end
def
usage_counter_action
:update
end
def
external_action
'update'
end
def
event_action
Event
::
UPDATED
end
def
slug_for_page
(
page
)
@old_slug
.
presence
||
super
end
end
end
app/views/layouts/header/_default.html.haml
View file @
729e3765
...
...
@@ -68,6 +68,8 @@
%li
.nav-item.header-user.dropdown
{
data:
{
track_label:
"profile_dropdown"
,
track_event:
"click_dropdown"
,
track_value:
""
,
qa_selector:
'user_menu'
},
class:
(
'mr-0'
if
has_impersonation_link
)
}
=
link_to
current_user
,
class:
user_dropdown_class
,
data:
{
toggle:
"dropdown"
}
do
=
image_tag
avatar_icon_for_user
(
current_user
,
23
),
width:
23
,
height:
23
,
class:
"header-user-avatar qa-user-avatar"
-
if
show_user_notification_dot?
%span
.header-user-notification-dot.rounded-circle.position-relative
=
sprite_icon
(
'angle-down'
,
css_class:
'caret-down'
)
.dropdown-menu.dropdown-menu-right
=
render
'layouts/header/current_user_dropdown'
...
...
app/workers/create_evidence_worker.rb
View file @
729e3765
...
...
@@ -10,6 +10,6 @@ class CreateEvidenceWorker # rubocop:disable Scalability/IdempotentWorker
release
=
Release
.
find_by_id
(
release_id
)
return
unless
release
Evidence
.
create!
(
release:
release
)
Releases
::
Evidence
.
create!
(
release:
release
)
end
end
changelogs/unreleased/197227-graphql-group-milestones.yml
0 → 100644
View file @
729e3765
---
title
:
Add issues to graphQL group endpoint
merge_request
:
27789
author
:
type
:
added
changelogs/unreleased/199065-support-on-demand-release-evidence.yml
0 → 100644
View file @
729e3765
---
title
:
Support multiple Evidences for a Release
merge_request
:
26509
author
:
type
:
changed
changelogs/unreleased/209854-cache-es-check.yml
0 → 100644
View file @
729e3765
---
title
:
Cache ES enabled namespaces and projects
merge_request
:
27348
author
:
type
:
performance
changelogs/unreleased/expose-created-at-in-groups-api.yml
0 → 100644
View file @
729e3765
---
title
:
Expose created_at property in Groups API
merge_request
:
27824
author
:
type
:
added
changelogs/unreleased/feat-add-toggle-all-discussions-button.yml
0 → 100644
View file @
729e3765
---
title
:
Add toggle all discussions button to MRs
merge_request
:
24670
author
:
Martin Hobert & Diego Louzán
type
:
added
changelogs/unreleased/sast-no-env-file.yml
0 → 100644
View file @
729e3765
---
title
:
"
Run
SAST
using
awk
to
pass
env
variables
directly
to
docker
without
creating
.env
file"
merge_request
:
21174
author
:
Florian Gaultier
type
:
fixed
config/routes/project.rb
View file @
729e3765
...
...
@@ -170,8 +170,10 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
resources
:releases
,
only:
[
:index
,
:show
,
:edit
],
param: :tag
,
constraints:
{
tag:
%r{[^/]+}
}
do
member
do
get
:evidence
get
:downloads
,
path:
'downloads/*filepath'
,
format:
false
scope
module: :releases
do
resources
:evidences
,
only:
[
:show
]
end
end
end
...
...
doc/api/graphql/reference/gitlab_schema.graphql
View file @
729e3765
...
...
@@ -3219,6 +3219,106 @@ type Group {
"""
id
:
ID
!
"""
Issues
of
the
group
"""
issues
(
"""
Returns
the
elements
in
the
list
that
come
after
the
specified
cursor
.
"""
after
:
String
"""
ID
of
a
user
assigned
to
the
issues
,
"
none
"
and
"
any
"
values
supported
"""
assigneeId
:
String
"""
Username
of
a
user
assigned
to
the
issues
"""
assigneeUsername
:
String
"""
Returns
the
elements
in
the
list
that
come
before
the
specified
cursor
.
"""
before
:
String
"""
Issues
closed
after
this
date
"""
closedAfter
:
Time
"""
Issues
closed
before
this
date
"""
closedBefore
:
Time
"""
Issues
created
after
this
date
"""
createdAfter
:
Time
"""
Issues
created
before
this
date
"""
createdBefore
:
Time
"""
Returns
the
first
_n_
elements
from
the
list
.
"""
first
:
Int
"""
IID
of
the
issue
.
For
example
,
"1"
"""
iid
:
String
"""
List
of
IIDs
of
issues
.
For
example
,
[1
,
2]
"""
iids
:
[
String
!]
"""
Labels
applied
to
this
issue
"""
labelName
:
[
String
]
"""
Returns
the
last
_n_
elements
from
the
list
.
"""
last
:
Int
"""
Milestones
applied
to
this
issue
"""
milestoneTitle
:
[
String
]
"""
Search
query
for
finding
issues
by
title
or
description
"""
search
:
String
"""
Sort
issues
by
this
criteria
"""
sort
:
IssueSort
=
created_desc
"""
Current
state
of
this
issue
"""
state
:
IssuableState
"""
Issues
updated
after
this
date
"""
updatedAfter
:
Time
"""
Issues
updated
before
this
date
"""
updatedBefore
:
Time
):
IssueConnection
"""
Indicates
if
Large
File
Storage
(
LFS
)
is
enabled
for
namespace
"""
...
...
doc/api/graphql/reference/gitlab_schema.json
View file @
729e3765
...
...
@@ -9242,6 +9242,225 @@
"isDeprecated"
:
false
,
"deprecationReason"
:
null
},
{
"name"
:
"issues"
,
"description"
:
"Issues of the group"
,
"args"
:
[
{
"name"
:
"iid"
,
"description"
:
"IID of the issue. For example,
\"
1
\"
"
,
"type"
:
{
"kind"
:
"SCALAR"
,
"name"
:
"String"
,
"ofType"
:
null
},
"defaultValue"
:
null
},
{
"name"
:
"iids"
,
"description"
:
"List of IIDs of issues. For example, [1, 2]"
,
"type"
:
{
"kind"
:
"LIST"
,
"name"
:
null
,
"ofType"
:
{
"kind"
:
"NON_NULL"
,
"name"
:
null
,
"ofType"
:
{
"kind"
:
"SCALAR"
,
"name"
:
"String"
,
"ofType"
:
null
}
}
},
"defaultValue"
:
null
},
{
"name"
:
"state"
,
"description"
:
"Current state of this issue"
,
"type"
:
{
"kind"
:
"ENUM"
,
"name"
:
"IssuableState"
,
"ofType"
:
null
},
"defaultValue"
:
null
},
{
"name"
:
"labelName"
,
"description"
:
"Labels applied to this issue"
,
"type"
:
{
"kind"
:
"LIST"
,
"name"
:
null
,
"ofType"
:
{
"kind"
:
"SCALAR"
,
"name"
:
"String"
,
"ofType"
:
null
}
},
"defaultValue"
:
null
},
{
"name"
:
"milestoneTitle"
,
"description"
:
"Milestones applied to this issue"
,
"type"
:
{
"kind"
:
"LIST"
,
"name"
:
null
,
"ofType"
:
{
"kind"
:
"SCALAR"
,
"name"
:
"String"
,
"ofType"
:
null
}
},
"defaultValue"
:
null
},
{
"name"
:
"assigneeUsername"
,
"description"
:
"Username of a user assigned to the issues"
,
"type"
:
{
"kind"
:
"SCALAR"
,
"name"
:
"String"
,
"ofType"
:
null
},
"defaultValue"
:
null
},
{
"name"
:
"assigneeId"
,
"description"
:
"ID of a user assigned to the issues,
\"
none
\"
and
\"
any
\"
values supported"
,
"type"
:
{
"kind"
:
"SCALAR"
,
"name"
:
"String"
,
"ofType"
:
null
},
"defaultValue"
:
null
},
{
"name"
:
"createdBefore"
,
"description"
:
"Issues created before this date"
,
"type"
:
{
"kind"
:
"SCALAR"
,
"name"
:
"Time"
,
"ofType"
:
null
},
"defaultValue"
:
null
},
{
"name"
:
"createdAfter"
,
"description"
:
"Issues created after this date"
,
"type"
:
{
"kind"
:
"SCALAR"
,
"name"
:
"Time"
,
"ofType"
:
null
},
"defaultValue"
:
null
},
{
"name"
:
"updatedBefore"
,
"description"
:
"Issues updated before this date"
,
"type"
:
{
"kind"
:
"SCALAR"
,
"name"
:
"Time"
,
"ofType"
:
null
},
"defaultValue"
:
null
},
{
"name"
:
"updatedAfter"
,
"description"
:
"Issues updated after this date"
,
"type"
:
{
"kind"
:
"SCALAR"
,
"name"
:
"Time"
,
"ofType"
:
null
},
"defaultValue"
:
null
},
{
"name"
:
"closedBefore"
,
"description"
:
"Issues closed before this date"
,
"type"
:
{
"kind"
:
"SCALAR"
,
"name"
:
"Time"
,
"ofType"
:
null
},
"defaultValue"
:
null
},
{
"name"
:
"closedAfter"
,
"description"
:
"Issues closed after this date"
,
"type"
:
{
"kind"
:
"SCALAR"
,
"name"
:
"Time"
,
"ofType"
:
null
},
"defaultValue"
:
null
},
{
"name"
:
"search"
,
"description"
:
"Search query for finding issues by title or description"
,
"type"
:
{
"kind"
:
"SCALAR"
,
"name"
:
"String"
,
"ofType"
:
null
},
"defaultValue"
:
null
},
{
"name"
:
"sort"
,
"description"
:
"Sort issues by this criteria"
,
"type"
:
{
"kind"
:
"ENUM"
,
"name"
:
"IssueSort"
,
"ofType"
:
null
},
"defaultValue"
:
"created_desc"
},
{
"name"
:
"after"
,
"description"
:
"Returns the elements in the list that come after the specified cursor."
,
"type"
:
{
"kind"
:
"SCALAR"
,
"name"
:
"String"
,
"ofType"
:
null
},
"defaultValue"
:
null
},
{
"name"
:
"before"
,
"description"
:
"Returns the elements in the list that come before the specified cursor."
,
"type"
:
{
"kind"
:
"SCALAR"
,
"name"
:
"String"
,
"ofType"
:
null
},
"defaultValue"
:
null
},
{
"name"
:
"first"
,
"description"
:
"Returns the first _n_ elements from the list."
,
"type"
:
{
"kind"
:
"SCALAR"
,
"name"
:
"Int"
,
"ofType"
:
null
},
"defaultValue"
:
null
},
{
"name"
:
"last"
,
"description"
:
"Returns the last _n_ elements from the list."
,
"type"
:
{
"kind"
:
"SCALAR"
,
"name"
:
"Int"
,
"ofType"
:
null
},
"defaultValue"
:
null
}
],
"type"
:
{
"kind"
:
"OBJECT"
,
"name"
:
"IssueConnection"
,
"ofType"
:
null
},
"isDeprecated"
:
false
,
"deprecationReason"
:
null
},
{
"name"
:
"lfsEnabled"
,
"description"
:
"Indicates if Large File Storage (LFS) is enabled for namespace"
,
...
...
doc/api/groups.md
View file @
729e3765
...
...
@@ -49,7 +49,8 @@ GET /groups
"full_name"
:
"Foobar Group"
,
"full_path"
:
"foo-bar"
,
"file_template_project_id"
:
1
,
"parent_id"
:
null
"parent_id"
:
null
,
"created_at"
:
"2020-01-15T12:36:29.590Z"
}
]
```
...
...
@@ -85,6 +86,7 @@ GET /groups?statistics=true
"full_path"
:
"foo-bar"
,
"file_template_project_id"
:
1
,
"parent_id"
:
null
,
"created_at"
:
"2020-01-15T12:36:29.590Z"
,
"statistics"
:
{
"storage_size"
:
212
,
"repository_size"
:
33
,
...
...
@@ -157,7 +159,8 @@ GET /groups/:id/subgroups
"full_name"
:
"Foobar Group"
,
"full_path"
:
"foo-bar"
,
"file_template_project_id"
:
1
,
"parent_id"
:
123
"parent_id"
:
123
,
"created_at"
:
"2020-01-15T12:36:29.590Z"
}
]
```
...
...
@@ -282,6 +285,7 @@ Example response:
"runners_token"
:
"ba324ca7b1c77fc20bb9"
,
"file_template_project_id"
:
1
,
"parent_id"
:
null
,
"created_at"
:
"2020-01-15T12:36:29.590Z"
,
"projects"
:
[
{
"id"
:
7
,
...
...
@@ -591,6 +595,7 @@ Example response:
"full_path"
:
"foo-bar"
,
"file_template_project_id"
:
1
,
"parent_id"
:
null
,
"created_at"
:
"2020-01-15T12:36:29.590Z"
,
"projects"
:
[
{
"id"
:
9
,
...
...
doc/integration/elasticsearch.md
View file @
729e3765
...
...
@@ -426,6 +426,15 @@ There are several rake tasks available to you via the command line:
-
Performs an Elasticsearch import that indexes the snippets data.
-
[
`sudo gitlab-rake gitlab:elastic:projects_not_indexed`
](
https://gitlab.com/gitlab-org/gitlab/blob/master/ee/lib/tasks/gitlab/elastic.rake
)
-
Displays which projects are not indexed.
-
[
`sudo gitlab-rake gitlab:elastic:reindex_to_another_cluster[<SOURCE_CLUSTER_URL>,<DESTINATION_CLUSTER_URL>]`
](
https://gitlab.com/gitlab-org/gitlab/blob/master/ee/lib/tasks/gitlab/elastic.rake
)
-
Creates a new index in the destination cluster and triggers a
[
reindex from
remote
](
https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-reindex.html#reindex-from-remote
)
such that the index is fully copied from the source index. This can be
useful when you wish to perform a migration to a new cluster as this
reindexing should be quicker than reindexing via GitLab. Note that remote
reindex requires your source cluster to be whitelisted in your destination
cluster in Elasticsearch settings as per
[
the
documentation
](
https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-reindex.html#reindex-from-remote
)
.
### Environment Variables
...
...
lib/api/entities/event.rb
View file @
729e3765
...
...
@@ -9,6 +9,7 @@ module API
expose
:created_at
expose
:note
,
using:
Entities
::
Note
,
if:
->
(
event
,
options
)
{
event
.
note?
}
expose
:author
,
using:
Entities
::
UserBasic
,
if:
->
(
event
,
options
)
{
event
.
author
}
expose
:wiki_page
,
using:
Entities
::
WikiPageBasic
,
if:
->
(
event
,
_options
)
{
event
.
wiki_page?
}
expose
:push_event_payload
,
as: :push_data
,
...
...
lib/api/entities/group.rb
View file @
729e3765
...
...
@@ -19,6 +19,7 @@ module API
end
expose
:request_access_enabled
expose
:full_name
,
:full_path
expose
:created_at
expose
:parent_id
expose
:custom_attributes
,
using:
'API::Entities::CustomAttribute'
,
if: :with_custom_attributes
...
...
lib/api/entities/release.rb
View file @
729e3765
...
...
@@ -22,6 +22,7 @@ module API
expose
:commit_path
,
expose_nil:
false
expose
:tag_path
,
expose_nil:
false
expose
:evidence_sha
,
expose_nil:
false
,
if:
->
(
_
,
_
)
{
can_download_code?
}
expose
:assets
do
expose
:assets_count
,
as: :count
do
|
release
,
_
|
assets_to_exclude
=
can_download_code?
?
[]
:
[
:sources
]
...
...
@@ -33,6 +34,7 @@ module API
end
expose
:evidence_file_path
,
expose_nil:
false
,
if:
->
(
_
,
_
)
{
can_download_code?
}
end
expose
:evidences
,
using:
Entities
::
Releases
::
Evidence
,
expose_nil:
false
,
if:
->
(
_
,
_
)
{
can_download_code?
}
expose
:_links
do
expose
:self_url
,
as: :self
,
expose_nil:
false
expose
:merge_requests_url
,
expose_nil:
false
...
...
lib/api/entities/releases/evidence.rb
0 → 100644
View file @
729e3765
# frozen_string_literal: true
module
API
module
Entities
module
Releases
class
Evidence
<
Grape
::
Entity
include
::
API
::
Helpers
::
Presentable
expose
:summary_sha
,
as: :sha
expose
:filepath
expose
:collected_at
end
end
end
end
lib/api/helpers/presentable.rb
View file @
729e3765
...
...
@@ -4,7 +4,7 @@ module API
module
Helpers
##
# This module makes it possible to use `app/presenters` with
# Grape Entities. It instantiates model presenter and passes
# Grape Entities. It instantiates
the
model presenter and passes
# options defined in the API endpoint to the presenter itself.
#
# present object, with: Entities::Something,
...
...
@@ -22,6 +22,7 @@ module API
extend
ActiveSupport
::
Concern
def
initialize
(
object
,
options
=
{})
options
=
options
.
opts_hash
if
options
.
is_a?
(
Grape
::
Entity
::
Options
)
super
(
object
.
present
(
options
),
options
)
end
end
...
...
lib/event_filter.rb
View file @
729e3765
...
...
@@ -9,6 +9,7 @@ class EventFilter
ISSUE
=
'issue'
COMMENTS
=
'comments'
TEAM
=
'team'
WIKI
=
'wiki'
def
initialize
(
filter
)
# Split using comma to maintain backward compatibility Ex/ "filter1,filter2"
...
...
@@ -22,6 +23,8 @@ class EventFilter
# rubocop: disable CodeReuse/ActiveRecord
def
apply_filter
(
events
)
events
=
apply_feature_flags
(
events
)
case
filter
when
PUSH
events
.
where
(
action:
Event
::
PUSHED
)
...
...
@@ -33,6 +36,8 @@ class EventFilter
events
.
where
(
action:
[
Event
::
JOINED
,
Event
::
LEFT
,
Event
::
EXPIRED
])
when
ISSUE
events
.
where
(
action:
[
Event
::
CREATED
,
Event
::
UPDATED
,
Event
::
CLOSED
,
Event
::
REOPENED
],
target_type:
'Issue'
)
when
WIKI
wiki_events
(
events
)
else
events
end
...
...
@@ -41,8 +46,20 @@ class EventFilter
private
def
apply_feature_flags
(
events
)
return
events
.
not_wiki_page
unless
Feature
.
enabled?
(
:wiki_events
)
events
end
def
wiki_events
(
events
)
return
events
unless
Feature
.
enabled?
(
:wiki_events
)
events
.
for_wiki_page
end
def
filters
[
ALL
,
PUSH
,
MERGED
,
ISSUE
,
COMMENTS
,
TEAM
]
[
ALL
,
PUSH
,
MERGED
,
ISSUE
,
COMMENTS
,
TEAM
,
WIKI
]
end
end
...
...
lib/gitlab/ci/templates/Security/SAST.gitlab-ci.yml
View file @
729e3765
...
...
@@ -36,9 +36,9 @@ sast:
export DOCKER_HOST='tcp://localhost:2375'
fi
fi
-
ENVS=`printenv | grep -vE '^(DOCKER_|CI|GITLAB_|FF_|HOME|PWD|OLDPWD|PATH|SHLVL|HOSTNAME)' | sed -n '/^[^\t]/s/=.*//p' | sed '/^$/d' | sed 's/^/-e /g' | tr '\n' ' '`
-
|
docker run $ENVS \
docker run \
$(awk 'BEGIN{for(v in ENVIRON) print v}' | grep -v -E '^(DOCKER_|CI|GITLAB_|FF_|HOME|PWD|OLDPWD|PATH|SHLVL|HOSTNAME)' | awk '{printf " -e %s", $0}') \
--volume "$PWD:/code" \
--volume /var/run/docker.sock:/var/run/docker.sock \
"registry.gitlab.com/gitlab-org/security-products/sast:$SAST_VERSION" /app/bin/run /code
...
...
lib/gitlab/experimentation.rb
View file @
729e3765
...
...
@@ -28,6 +28,12 @@ module Gitlab
environment:
::
Gitlab
.
dev_env_or_com?
,
enabled_ratio:
0.1
,
tracking_category:
'Growth::Expansion::Experiment::SuggestPipeline'
},
ci_notification_dot:
{
feature_toggle: :ci_notification_dot
,
environment:
::
Gitlab
.
dev_env_or_com?
,
enabled_ratio:
0.1
,
tracking_category:
'Growth::Expansion::Experiment::CiNotificationDot'
}
}.
freeze
...
...
locale/gitlab.pot
View file @
729e3765
...
...
@@ -22,6 +22,9 @@ msgstr ""
msgid " (from %{timeoutSource})"
msgstr ""
msgid " Collected %{time}"
msgstr ""
msgid " Please sign in."
msgstr ""
...
...
@@ -475,7 +478,7 @@ msgstr ""
msgid "%{tags} tags per image name"
msgstr ""
msgid "%{tag}-
evidence.json
"
msgid "%{tag}-
%{evidence}-%{filename}
"
msgstr ""
msgid "%{template_project_id} is unknown or invalid"
...
...
@@ -21006,6 +21009,9 @@ msgstr ""
msgid "Toggle Sidebar"
msgstr ""
msgid "Toggle all threads"
msgstr ""
msgid "Toggle backtrace"
msgstr ""
...
...
scripts/trigger-build-docs
View file @
729e3765
...
...
@@ -72,15 +72,17 @@ end
# Define suffix in review app URL based on project
#
def
slug
case
ENV
[
"CI_PROJECT_
NAME
"
]
when
'gitlab-foss'
case
ENV
[
"CI_PROJECT_
PATH
"
]
when
'gitlab-
org/gitlab-
foss'
'ce'
when
'gitlab'
when
'gitlab
-org/gitlab
'
'ee'
when
'gitlab-runner'
when
'gitlab-
org/gitlab-
runner'
'runner'
when
'omnibus-gitlab'
when
'
gitlab-org/
omnibus-gitlab'
'omnibus'
when
'gitlab-org/charts/gitlab'
'charts'
end
end
...
...
spec/controllers/projects/releases/evidences_controller_spec.rb
0 → 100644
View file @
729e3765
# frozen_string_literal: true
require
'spec_helper'
describe
Projects
::
Releases
::
EvidencesController
do
let!
(
:project
)
{
create
(
:project
,
:repository
,
:public
)
}
let_it_be
(
:private_project
)
{
create
(
:project
,
:repository
,
:private
)
}
let_it_be
(
:developer
)
{
create
(
:user
)
}
let_it_be
(
:reporter
)
{
create
(
:user
)
}
let
(
:user
)
{
developer
}
before
do
project
.
add_developer
(
developer
)
project
.
add_reporter
(
reporter
)
end
shared_examples_for
'successful request'
do
it
'renders a 200'
do
subject
expect
(
response
).
to
have_gitlab_http_status
(
:success
)
end
end
shared_examples_for
'not found'
do
it
'renders 404'
do
subject
expect
(
response
).
to
have_gitlab_http_status
(
:not_found
)
end
end
describe
'GET #show'
do
let_it_be
(
:tag_name
)
{
"v1.1.0-evidence"
}
let!
(
:release
)
{
create
(
:release
,
:with_evidence
,
project:
project
,
tag:
tag_name
)
}
let
(
:evidence
)
{
release
.
evidences
.
first
}
let
(
:tag
)
{
CGI
.
escape
(
release
.
tag
)
}
let
(
:format
)
{
:json
}
subject
do
get
:show
,
params:
{
namespace_id:
project
.
namespace
.
to_param
,
project_id:
project
,
tag:
tag
,
id:
evidence
.
id
,
format:
format
}
end
before
do
sign_in
(
user
)
end
context
'when the user is a developer'
do
it
'returns the correct evidence summary as a json'
do
subject
expect
(
json_response
).
to
eq
(
evidence
.
summary
)
end
context
'when the release was created before evidence existed'
do
before
do
evidence
.
destroy
end
it_behaves_like
'not found'
end
end
context
'when the user is a guest for the project'
do
before
do
project
.
add_guest
(
user
)
end
context
'when the project is private'
do
let
(
:project
)
{
private_project
}
it_behaves_like
'not found'
end
context
'when the project is public'
do
it_behaves_like
'successful request'
end
end
context
'when release is associated to a milestone which includes an issue'
do
let_it_be
(
:project
)
{
create
(
:project
,
:repository
,
:public
)
}
let_it_be
(
:issue
)
{
create
(
:issue
,
project:
project
)
}
let_it_be
(
:milestone
)
{
create
(
:milestone
,
project:
project
,
issues:
[
issue
])
}
let_it_be
(
:release
)
{
create
(
:release
,
project:
project
,
tag:
tag_name
,
milestones:
[
milestone
])
}
before
do
create
(
:evidence
,
release:
release
)
end
shared_examples_for
'does not show the issue in evidence'
do
it
do
subject
expect
(
response
).
to
have_gitlab_http_status
(
:ok
)
expect
(
json_response
[
'release'
][
'milestones'
]
.
all?
{
|
milestone
|
milestone
[
'issues'
].
nil?
}).
to
eq
(
true
)
end
end
shared_examples_for
'evidence not found'
do
it
do
subject
expect
(
response
).
to
have_gitlab_http_status
(
:not_found
)
end
end
shared_examples_for
'safely expose evidence'
do
it_behaves_like
'does not show the issue in evidence'
context
'when the issue is confidential'
do
let
(
:issue
)
{
create
(
:issue
,
:confidential
,
project:
project
)
}
it_behaves_like
'does not show the issue in evidence'
end
context
'when the user is the author of the confidential issue'
do
let
(
:issue
)
{
create
(
:issue
,
:confidential
,
project:
project
,
author:
user
)
}
it_behaves_like
'does not show the issue in evidence'
end
context
'when project is private'
do
let!
(
:project
)
{
create
(
:project
,
:repository
,
:private
)
}
it_behaves_like
'evidence not found'
end
context
'when project restricts the visibility of issues to project members only'
do
let!
(
:project
)
{
create
(
:project
,
:repository
,
:issues_private
)
}
it_behaves_like
'evidence not found'
end
end
context
'when user is non-project member'
do
let
(
:user
)
{
create
(
:user
)
}
it_behaves_like
'safely expose evidence'
end
context
'when user is auditor'
,
if:
Gitlab
.
ee?
do
let
(
:user
)
{
create
(
:user
,
:auditor
)
}
it_behaves_like
'safely expose evidence'
end
context
'when external authorization control is enabled'
do
let
(
:user
)
{
create
(
:user
)
}
before
do
stub_application_setting
(
external_authorization_service_enabled:
true
)
end
it_behaves_like
'evidence not found'
end
end
end
end
spec/controllers/projects/releases_controller_spec.rb
View file @
729e3765
...
...
@@ -3,11 +3,11 @@
require
'spec_helper'
describe
Projects
::
ReleasesController
do
let!
(
:project
)
{
create
(
:project
,
:repository
,
:public
)
}
let
!
(
:private_project
)
{
create
(
:project
,
:repository
,
:private
)
}
let
(
:user
)
{
developer
}
let
(
:developer
)
{
create
(
:user
)
}
let
(
:reporter
)
{
create
(
:user
)
}
let!
(
:project
)
{
create
(
:project
,
:repository
,
:public
)
}
let
_it_be
(
:private_project
)
{
create
(
:project
,
:repository
,
:private
)
}
let
_it_be
(
:developer
)
{
create
(
:user
)
}
let
_it_be
(
:reporter
)
{
create
(
:user
)
}
let
_it_be
(
:user
)
{
developer
}
let!
(
:release_1
)
{
create
(
:release
,
project:
project
,
released_at:
Time
.
zone
.
parse
(
'2018-10-18'
))
}
let!
(
:release_2
)
{
create
(
:release
,
project:
project
,
released_at:
Time
.
zone
.
parse
(
'2019-10-19'
))
}
...
...
@@ -295,141 +295,6 @@ describe Projects::ReleasesController do
end
end
describe
'GET #evidence'
do
let_it_be
(
:tag_name
)
{
"v1.1.0-evidence"
}
let!
(
:release
)
{
create
(
:release
,
:with_evidence
,
project:
project
,
tag:
tag_name
)
}
let
(
:tag
)
{
CGI
.
escape
(
release
.
tag
)
}
let
(
:format
)
{
:json
}
subject
do
get
:evidence
,
params:
{
namespace_id:
project
.
namespace
,
project_id:
project
,
tag:
tag
,
format:
format
}
end
before
do
sign_in
(
user
)
end
context
'when the user is a developer'
do
it
'returns the correct evidence summary as a json'
do
subject
expect
(
json_response
).
to
eq
(
release
.
evidence
.
summary
)
end
context
'when the release was created before evidence existed'
do
before
do
release
.
evidence
.
destroy
end
it
'returns an empty json'
do
subject
expect
(
json_response
).
to
eq
({})
end
end
end
context
'when the user is a guest for the project'
do
before
do
project
.
add_guest
(
user
)
end
context
'when the project is private'
do
let
(
:project
)
{
private_project
}
it_behaves_like
'not found'
end
context
'when the project is public'
do
it_behaves_like
'successful request'
end
end
context
'when release is associated to a milestone which includes an issue'
do
let_it_be
(
:project
)
{
create
(
:project
,
:repository
,
:public
)
}
let_it_be
(
:issue
)
{
create
(
:issue
,
project:
project
)
}
let_it_be
(
:milestone
)
{
create
(
:milestone
,
project:
project
,
issues:
[
issue
])
}
let_it_be
(
:release
)
{
create
(
:release
,
project:
project
,
tag:
tag_name
,
milestones:
[
milestone
])
}
before
do
create
(
:evidence
,
release:
release
)
end
shared_examples_for
'does not show the issue in evidence'
do
it
do
subject
expect
(
response
).
to
have_gitlab_http_status
(
:ok
)
expect
(
json_response
[
'release'
][
'milestones'
]
.
all?
{
|
milestone
|
milestone
[
'issues'
].
nil?
}).
to
eq
(
true
)
end
end
shared_examples_for
'evidence not found'
do
it
do
subject
expect
(
response
).
to
have_gitlab_http_status
(
:not_found
)
end
end
shared_examples_for
'safely expose evidence'
do
it_behaves_like
'does not show the issue in evidence'
context
'when the issue is confidential'
do
let
(
:issue
)
{
create
(
:issue
,
:confidential
,
project:
project
)
}
it_behaves_like
'does not show the issue in evidence'
end
context
'when the user is the author of the confidential issue'
do
let
(
:issue
)
{
create
(
:issue
,
:confidential
,
project:
project
,
author:
user
)
}
it_behaves_like
'does not show the issue in evidence'
end
context
'when project is private'
do
let!
(
:project
)
{
create
(
:project
,
:repository
,
:private
)
}
it_behaves_like
'evidence not found'
end
context
'when project restricts the visibility of issues to project members only'
do
let!
(
:project
)
{
create
(
:project
,
:repository
,
:issues_private
)
}
it_behaves_like
'evidence not found'
end
end
context
'when user is non-project member'
do
let
(
:user
)
{
create
(
:user
)
}
it_behaves_like
'safely expose evidence'
end
context
'when user is auditor'
,
if:
Gitlab
.
ee?
do
let
(
:user
)
{
create
(
:user
,
:auditor
)
}
it_behaves_like
'safely expose evidence'
end
context
'when external authorization control is enabled'
do
let
(
:user
)
{
create
(
:user
)
}
before
do
stub_application_setting
(
external_authorization_service_enabled:
true
)
end
it_behaves_like
'evidence not found'
end
end
end
private
def
get_index
...
...
spec/factories/events.rb
View file @
729e3765
...
...
@@ -25,12 +25,12 @@ FactoryBot.define do
factory
:wiki_page_event
do
action
{
Event
::
CREATED
}
project
{
@overrides
[
:wiki_page
]
&
.
project
||
create
(
:project
,
:wiki_repo
)
}
target
{
create
(
:wiki_page_meta
,
:for_wiki_page
,
wiki_page:
wiki_page
)
}
transient
do
wiki_page
{
create
(
:wiki_page
,
project:
project
)
}
end
target
{
create
(
:wiki_page_meta
,
:for_wiki_page
,
wiki_page:
wiki_page
)
}
end
end
...
...
spec/factories/evidences.rb
View file @
729e3765
# frozen_string_literal: true
FactoryBot
.
define
do
factory
:evidence
do
factory
:evidence
,
class:
'Releases::Evidence'
do
release
end
end
spec/finders/events_finder_spec.rb
View file @
729e3765
...
...
@@ -3,7 +3,7 @@
require
'spec_helper'
describe
EventsFinder
do
let
(
:user
)
{
create
(
:user
)
}
let
_it_be
(
:user
)
{
create
(
:user
)
}
let
(
:other_user
)
{
create
(
:user
)
}
let
(
:project1
)
{
create
(
:project
,
:private
,
creator_id:
user
.
id
,
namespace:
user
.
namespace
)
}
...
...
@@ -20,7 +20,7 @@ describe EventsFinder do
let
(
:opened_merge_request3
)
{
create
(
:merge_request
,
source_project:
project1
,
author:
other_user
)
}
let!
(
:other_developer_event
)
{
create
(
:event
,
project:
project1
,
author:
other_user
,
target:
opened_merge_request3
,
action:
Event
::
CREATED
)
}
let
(
:public_project
)
{
create
(
:project
,
:public
,
creator_id:
user
.
id
,
namespace:
user
.
namespace
)
}
let
_it_be
(
:public_project
)
{
create
(
:project
,
:public
,
creator_id:
user
.
id
,
namespace:
user
.
namespace
)
}
let
(
:confidential_issue
)
{
create
(
:closed_issue
,
confidential:
true
,
project:
public_project
,
author:
user
)
}
let!
(
:confidential_event
)
{
create
(
:event
,
project:
public_project
,
author:
user
,
target:
confidential_issue
,
action:
Event
::
CLOSED
)
}
...
...
@@ -59,6 +59,32 @@ describe EventsFinder do
end
end
describe
'wiki events feature flag'
do
let_it_be
(
:events
)
{
create_list
(
:wiki_page_event
,
3
,
project:
public_project
)
}
subject
(
:finder
)
{
described_class
.
new
(
source:
public_project
,
target_type:
'wiki'
,
current_user:
user
)
}
context
'the wiki_events feature flag is disabled'
do
before
do
stub_feature_flags
(
wiki_events:
false
)
end
it
'omits the wiki page events'
do
expect
(
finder
.
execute
).
to
be_empty
end
end
context
'the wiki_events feature flag is enabled'
do
before
do
stub_feature_flags
(
wiki_events:
true
)
end
it
'can find the wiki events'
do
expect
(
finder
.
execute
).
to
match_array
(
events
)
end
end
end
context
'dashboard events'
do
before
do
project1
.
add_developer
(
other_user
)
...
...
spec/fixtures/api/schemas/public_api/v4/release.json
View file @
729e3765
...
...
@@ -22,6 +22,10 @@
"commit_path"
:
{
"type"
:
"string"
},
"tag_path"
:
{
"type"
:
"string"
},
"name"
:
{
"type"
:
"string"
},
"evidences"
:
{
"type"
:
"array"
,
"items"
:
{
"$ref"
:
"release/evidence.json"
}
},
"assets"
:
{
"required"
:
[
"count"
,
"links"
,
"sources"
],
"properties"
:
{
...
...
spec/fixtures/api/schemas/public_api/v4/release/evidence.json
0 → 100644
View file @
729e3765
{
"type"
:
"object"
,
"required"
:
[
"sha"
,
"filepath"
,
"collected_at"
],
"properties"
:
{
"sha"
:
{
"type"
:
"string"
},
"filepath"
:
{
"type"
:
"string"
},
"collected_at"
:
{
"type"
:
"date"
}
},
"additionalProperties"
:
false
}
spec/
javascripts
/locale/index_spec.js
→
spec/
frontend
/locale/index_spec.js
View file @
729e3765
import
{
createDateTimeFormat
,
languageCode
}
from
'
~/locale
'
;
import
{
setLanguage
}
from
'
../
helpers/locale_helper
'
;
import
{
setLanguage
}
from
'
helpers/locale_helper
'
;
describe
(
'
locale
'
,
()
=>
{
afterEach
(()
=>
{
setLanguage
(
null
);
});
afterEach
(()
=>
setLanguage
(
null
));
describe
(
'
languageCode
'
,
()
=>
{
it
(
'
parses the lang attribute
'
,
()
=>
{
...
...
@@ -22,14 +20,12 @@ describe('locale', () => {
});
describe
(
'
createDateTimeFormat
'
,
()
=>
{
beforeEach
(()
=>
{
setLanguage
(
'
de
'
);
});
beforeEach
(()
=>
setLanguage
(
'
en
'
));
it
(
'
creates an instance of Intl.DateTimeFormat
'
,
()
=>
{
const
dateFormat
=
createDateTimeFormat
({
year
:
'
numeric
'
,
month
:
'
long
'
,
day
:
'
numeric
'
});
expect
(
dateFormat
.
format
(
new
Date
(
2015
,
6
,
3
))).
toBe
(
'
3. Juli
2015
'
);
expect
(
dateFormat
.
format
(
new
Date
(
2015
,
6
,
3
))).
toBe
(
'
July 3,
2015
'
);
});
});
});
spec/frontend/notes/components/discussion_counter_spec.js
View file @
729e3765
...
...
@@ -75,17 +75,66 @@ describe('DiscussionCounter component', () => {
});
it
.
each
`
title
| resolved | hasNextBtn
| isActive | icon | groupLength
${
'
hasNextButton
'
}
|
${
false
}
|
${
true
}
|
${
false
}
|
${
'
check-circle
'
}
|
${
2
}
${
'
allResolved
'
}
|
${
true
}
|
${
false
}
|
${
true
}
|
${
'
check-circle-filled
'
}
|
${
0
}
`
(
'
renders correctly if $title
'
,
({
resolved
,
hasNextBtn
,
isActive
,
icon
,
groupLength
})
=>
{
title
| resolved
| isActive | icon | groupLength
${
'
not allResolved
'
}
|
${
false
}
|
${
false
}
|
${
'
check-circle
'
}
|
${
3
}
${
'
allResolved
'
}
|
${
true
}
|
${
true
}
|
${
'
check-circle-filled
'
}
|
${
1
}
`
(
'
renders correctly if $title
'
,
({
resolved
,
isActive
,
icon
,
groupLength
})
=>
{
updateStore
({
resolvable
:
true
,
resolved
});
wrapper
=
shallowMount
(
DiscussionCounter
,
{
store
,
localVue
});
expect
(
wrapper
.
find
(
`.has-next-btn`
).
exists
()).
toBe
(
hasNextBtn
);
expect
(
wrapper
.
find
(
`.is-active`
).
exists
()).
toBe
(
isActive
);
expect
(
wrapper
.
find
({
name
:
icon
}).
exists
()).
toBe
(
true
);
expect
(
wrapper
.
findAll
(
'
[role="group"
'
).
length
).
toBe
(
groupLength
);
});
});
describe
(
'
toggle all threads button
'
,
()
=>
{
let
toggleAllButton
;
const
updateStoreWithExpanded
=
expanded
=>
{
const
discussion
=
{
...
discussionMock
,
expanded
};
store
.
commit
(
types
.
SET_INITIAL_DISCUSSIONS
,
[
discussion
]);
store
.
dispatch
(
'
updateResolvableDiscussionsCounts
'
);
wrapper
=
shallowMount
(
DiscussionCounter
,
{
store
,
localVue
});
toggleAllButton
=
wrapper
.
find
(
'
.toggle-all-discussions-btn
'
);
};
afterEach
(()
=>
wrapper
.
destroy
());
it
(
'
calls button handler when clicked
'
,
()
=>
{
updateStoreWithExpanded
(
true
);
wrapper
.
setMethods
({
handleExpandDiscussions
:
jest
.
fn
()
});
toggleAllButton
.
trigger
(
'
click
'
);
expect
(
wrapper
.
vm
.
handleExpandDiscussions
).
toHaveBeenCalledTimes
(
1
);
});
it
(
'
collapses all discussions if expanded
'
,
()
=>
{
updateStoreWithExpanded
(
true
);
expect
(
wrapper
.
vm
.
allExpanded
).
toBe
(
true
);
expect
(
toggleAllButton
.
find
({
name
:
'
angle-up
'
}).
exists
()).
toBe
(
true
);
toggleAllButton
.
trigger
(
'
click
'
);
return
wrapper
.
vm
.
$nextTick
().
then
(()
=>
{
expect
(
wrapper
.
vm
.
allExpanded
).
toBe
(
false
);
expect
(
toggleAllButton
.
find
({
name
:
'
angle-down
'
}).
exists
()).
toBe
(
true
);
});
});
it
(
'
expands all discussions if collapsed
'
,
()
=>
{
updateStoreWithExpanded
(
false
);
expect
(
wrapper
.
vm
.
allExpanded
).
toBe
(
false
);
expect
(
toggleAllButton
.
find
({
name
:
'
angle-down
'
}).
exists
()).
toBe
(
true
);
toggleAllButton
.
trigger
(
'
click
'
);
return
wrapper
.
vm
.
$nextTick
().
then
(()
=>
{
expect
(
wrapper
.
vm
.
allExpanded
).
toBe
(
true
);
expect
(
toggleAllButton
.
find
({
name
:
'
angle-up
'
}).
exists
()).
toBe
(
true
);
});
});
});
});
spec/frontend/notes/stores/mutation_spec.js
View file @
729e3765
...
...
@@ -329,6 +329,52 @@ describe('Notes Store mutations', () => {
});
});
describe
(
'
SET_EXPAND_DISCUSSIONS
'
,
()
=>
{
it
(
'
should succeed when discussions are null
'
,
()
=>
{
const
state
=
{};
mutations
.
SET_EXPAND_DISCUSSIONS
(
state
,
{
discussionIds
:
null
,
expanded
:
true
});
expect
(
state
).
toEqual
({});
});
it
(
'
should succeed when discussions are empty
'
,
()
=>
{
const
state
=
{};
mutations
.
SET_EXPAND_DISCUSSIONS
(
state
,
{
discussionIds
:
[],
expanded
:
true
});
expect
(
state
).
toEqual
({});
});
it
(
'
should open all closed discussions
'
,
()
=>
{
const
discussion1
=
Object
.
assign
({},
discussionMock
,
{
id
:
0
,
expanded
:
false
});
const
discussion2
=
Object
.
assign
({},
discussionMock
,
{
id
:
1
,
expanded
:
true
});
const
discussionIds
=
[
discussion1
.
id
,
discussion2
.
id
];
const
state
=
{
discussions
:
[
discussion1
,
discussion2
]
};
mutations
.
SET_EXPAND_DISCUSSIONS
(
state
,
{
discussionIds
,
expanded
:
true
});
state
.
discussions
.
forEach
(
discussion
=>
{
expect
(
discussion
.
expanded
).
toEqual
(
true
);
});
});
it
(
'
should close all opened discussions
'
,
()
=>
{
const
discussion1
=
Object
.
assign
({},
discussionMock
,
{
id
:
0
,
expanded
:
false
});
const
discussion2
=
Object
.
assign
({},
discussionMock
,
{
id
:
1
,
expanded
:
true
});
const
discussionIds
=
[
discussion1
.
id
,
discussion2
.
id
];
const
state
=
{
discussions
:
[
discussion1
,
discussion2
]
};
mutations
.
SET_EXPAND_DISCUSSIONS
(
state
,
{
discussionIds
,
expanded
:
false
});
state
.
discussions
.
forEach
(
discussion
=>
{
expect
(
discussion
.
expanded
).
toEqual
(
false
);
});
});
});
describe
(
'
UPDATE_NOTE
'
,
()
=>
{
it
(
'
should update a note
'
,
()
=>
{
const
state
=
{
...
...
spec/frontend/releases/components/evidence_block_spec.js
View file @
729e3765
import
{
mount
}
from
'
@vue/test-utils
'
;
import
{
GlLink
}
from
'
@gitlab/ui
'
;
import
{
GlLink
,
GlIcon
}
from
'
@gitlab/ui
'
;
import
{
truncateSha
}
from
'
~/lib/utils/text_utility
'
;
import
Icon
from
'
~/vue_shared/components/icon.vue
'
;
import
{
release
as
originalRelease
}
from
'
../mock_data
'
;
import
EvidenceBlock
from
'
~/releases/components/evidence_block.vue
'
;
import
ClipboardButton
from
'
~/vue_shared/components/clipboard_button.vue
'
;
...
...
@@ -32,11 +31,11 @@ describe('Evidence Block', () => {
});
it
(
'
renders the evidence icon
'
,
()
=>
{
expect
(
wrapper
.
find
(
Icon
).
props
(
'
name
'
)).
toBe
(
'
review-list
'
);
expect
(
wrapper
.
find
(
Gl
Icon
).
props
(
'
name
'
)).
toBe
(
'
review-list
'
);
});
it
(
'
renders the title for the dowload link
'
,
()
=>
{
expect
(
wrapper
.
find
(
GlLink
).
text
()).
toBe
(
`
${
release
.
tagName
}
-evidence.json`
);
expect
(
wrapper
.
find
(
GlLink
).
text
()).
toBe
(
'
v1.1.2-evidences-1.json
'
);
});
it
(
'
renders the correct hover text for the download
'
,
()
=>
{
...
...
@@ -44,19 +43,19 @@ describe('Evidence Block', () => {
});
it
(
'
renders the correct file link for download
'
,
()
=>
{
expect
(
wrapper
.
find
(
GlLink
).
attributes
().
download
).
toBe
(
`
${
release
.
tagName
}
-evidence.json`
);
expect
(
wrapper
.
find
(
GlLink
).
attributes
().
download
).
toBe
(
'
v1.1.2-evidences-1.json
'
);
});
describe
(
'
sha text
'
,
()
=>
{
it
(
'
renders the short sha initially
'
,
()
=>
{
expect
(
wrapper
.
find
(
'
.js-short
'
).
text
()).
toBe
(
truncateSha
(
release
.
evidence
S
ha
));
expect
(
wrapper
.
find
(
'
.js-short
'
).
text
()).
toBe
(
truncateSha
(
release
.
evidence
s
[
0
].
s
ha
));
});
it
(
'
renders the long sha after expansion
'
,
()
=>
{
wrapper
.
find
(
'
.js-text-expander-prepend
'
).
trigger
(
'
click
'
);
return
wrapper
.
vm
.
$nextTick
().
then
(()
=>
{
expect
(
wrapper
.
find
(
'
.js-expanded
'
).
text
()).
toBe
(
release
.
evidence
S
ha
);
expect
(
wrapper
.
find
(
'
.js-expanded
'
).
text
()).
toBe
(
release
.
evidence
s
[
0
].
s
ha
);
});
});
});
...
...
@@ -72,7 +71,7 @@ describe('Evidence Block', () => {
it
(
'
copies the sha
'
,
()
=>
{
expect
(
wrapper
.
find
(
ClipboardButton
).
attributes
(
'
data-clipboard-text
'
)).
toBe
(
release
.
evidence
S
ha
,
release
.
evidence
s
[
0
].
s
ha
,
);
});
});
...
...
spec/frontend/releases/mock_data.js
View file @
729e3765
...
...
@@ -43,7 +43,6 @@ export const release = {
description_html
:
'
<p data-sourcepos="1:1-1:21" dir="auto">A super nice release!</p>
'
,
created_at
:
'
2019-08-26T17:54:04.952Z
'
,
released_at
:
'
2019-08-26T17:54:04.807Z
'
,
evidence_sha
:
'
fb3a125fd69a0e5048ebfb0ba43eb32ce4911520dd8d
'
,
author
:
{
id
:
1
,
name
:
'
Administrator
'
,
...
...
@@ -69,10 +68,28 @@ export const release = {
commit_path
:
'
/root/release-test/commit/c22b0728d1b465f82898c884d32b01aa642f96c1
'
,
upcoming_release
:
false
,
milestones
,
evidences
:
[
{
filepath
:
'
https://20592.qa-tunnel.gitlab.info/root/test-deployments/-/releases/v1.1.2/evidences/1.json
'
,
sha
:
'
fb3a125fd69a0e5048ebfb0ba43eb32ce4911520dd8d
'
,
collected_at
:
'
2018-10-19 15:43:20 +0200
'
,
},
{
filepath
:
'
https://20592.qa-tunnel.gitlab.info/root/test-deployments/-/releases/v1.1.2/evidences/2.json
'
,
sha
:
'
6ebd17a66e6a861175735416e49cf677678029805712dd71bb805c609e2d9108
'
,
collected_at
:
'
2018-10-19 15:43:20 +0200
'
,
},
{
filepath
:
'
https://20592.qa-tunnel.gitlab.info/root/test-deployments/-/releases/v1.1.2/evidences/3.json
'
,
sha
:
'
2f65beaf275c3cb4b4e24fb01d481cc475d69c957830833f15338384816b5cba
'
,
collected_at
:
'
2018-10-19 15:43:20 +0200
'
,
},
],
assets
:
{
count
:
5
,
evidence_file_path
:
'
https://20592.qa-tunnel.gitlab.info/root/test-deployments/-/releases/v1.1.2/evidence.json
'
,
sources
:
[
{
format
:
'
zip
'
,
...
...
spec/graphql/resolvers/issues_resolver_spec.rb
View file @
729e3765
...
...
@@ -7,15 +7,20 @@ describe Resolvers::IssuesResolver do
let
(
:current_user
)
{
create
(
:user
)
}
context
"with a project"
do
let_it_be
(
:project
)
{
create
(
:project
)
}
let_it_be
(
:milestone
)
{
create
(
:milestone
,
project:
project
)
}
let_it_be
(
:assignee
)
{
create
(
:user
)
}
let_it_be
(
:issue1
)
{
create
(
:issue
,
project:
project
,
state: :opened
,
created_at:
3
.
hours
.
ago
,
updated_at:
3
.
hours
.
ago
,
milestone:
milestone
)
}
let_it_be
(
:issue2
)
{
create
(
:issue
,
project:
project
,
state: :closed
,
title:
'foo'
,
created_at:
1
.
hour
.
ago
,
updated_at:
1
.
hour
.
ago
,
closed_at:
1
.
hour
.
ago
,
assignees:
[
assignee
])
}
let_it_be
(
:label1
)
{
create
(
:label
,
project:
project
)
}
let_it_be
(
:label2
)
{
create
(
:label
,
project:
project
)
}
let_it_be
(
:group
)
{
create
(
:group
)
}
let_it_be
(
:project
)
{
create
(
:project
,
group:
group
)
}
let_it_be
(
:other_project
)
{
create
(
:project
,
group:
group
)
}
let_it_be
(
:milestone
)
{
create
(
:milestone
,
project:
project
)
}
let_it_be
(
:assignee
)
{
create
(
:user
)
}
let_it_be
(
:issue1
)
{
create
(
:issue
,
project:
project
,
state: :opened
,
created_at:
3
.
hours
.
ago
,
updated_at:
3
.
hours
.
ago
,
milestone:
milestone
)
}
let_it_be
(
:issue2
)
{
create
(
:issue
,
project:
project
,
state: :closed
,
title:
'foo'
,
created_at:
1
.
hour
.
ago
,
updated_at:
1
.
hour
.
ago
,
closed_at:
1
.
hour
.
ago
,
assignees:
[
assignee
])
}
let_it_be
(
:issue3
)
{
create
(
:issue
,
project:
other_project
,
state: :closed
,
title:
'foo'
,
created_at:
1
.
hour
.
ago
,
updated_at:
1
.
hour
.
ago
,
closed_at:
1
.
hour
.
ago
,
assignees:
[
assignee
])
}
let_it_be
(
:issue4
)
{
create
(
:issue
)
}
let_it_be
(
:label1
)
{
create
(
:label
,
project:
project
)
}
let_it_be
(
:label2
)
{
create
(
:label
,
project:
project
)
}
context
"with a project"
do
before
do
project
.
add_developer
(
current_user
)
create
(
:label_link
,
label:
label1
,
target:
issue1
)
...
...
@@ -184,6 +189,20 @@ describe Resolvers::IssuesResolver do
end
end
context
"with a group"
do
before
do
group
.
add_developer
(
current_user
)
end
describe
'#resolve'
do
it
'finds all group issues'
do
result
=
resolve
(
described_class
,
obj:
group
,
ctx:
{
current_user:
current_user
})
expect
(
result
).
to
contain_exactly
(
issue1
,
issue2
,
issue3
)
end
end
end
context
"when passing a non existent, batch loaded project"
do
let
(
:project
)
do
BatchLoader
::
GraphQL
.
for
(
"non-existent-path"
).
batch
do
|
_fake_paths
,
loader
,
_
|
...
...
spec/helpers/nav_helper_spec.rb
View file @
729e3765
...
...
@@ -117,4 +117,24 @@ describe NavHelper, :do_not_mock_admin_mode do
it
{
is_expected
.
to
all
(
be_a
(
String
))
}
end
describe
'.show_user_notification_dot?'
do
subject
{
helper
.
show_user_notification_dot?
}
context
'when experiment is disabled'
do
before
do
allow
(
helper
).
to
receive
(
:experiment_enabled?
).
with
(
:ci_notification_dot
).
and_return
(
false
)
end
it
{
is_expected
.
to
be_falsey
}
end
context
'when experiment is enabled'
do
before
do
allow
(
helper
).
to
receive
(
:experiment_enabled?
).
with
(
:ci_notification_dot
).
and_return
(
true
)
end
it
{
is_expected
.
to
be_truthy
}
end
end
end
spec/lib/api/entities/release_spec.rb
View file @
729e3765
...
...
@@ -4,26 +4,29 @@ require 'spec_helper'
describe
API
::
Entities
::
Release
do
let_it_be
(
:project
)
{
create
(
:project
)
}
let_it_be
(
:user
)
{
create
(
:user
)
}
let
(
:entity
)
{
described_class
.
new
(
release
,
current_user:
user
)
}
describe
'evidence'
do
let
(
:release
)
{
create
(
:release
,
:with_evidence
,
project:
project
)
}
subject
{
entity
.
as_json
}
let_it_be
(
:release
)
{
create
(
:release
,
:with_evidence
,
project:
project
)
}
let
(
:evidence
)
{
release
.
evidences
.
first
}
let
(
:user
)
{
create
(
:user
)
}
let
(
:entity
)
{
described_class
.
new
(
release
,
current_user:
user
).
as_json
}
describe
'evidences'
do
context
'when the current user can download code'
do
let
(
:entity_evidence
)
{
entity
[
:evidences
].
first
}
it
'exposes the evidence sha and the json path'
do
allow
(
Ability
).
to
receive
(
:allowed?
).
and_call_original
allow
(
Ability
).
to
receive
(
:allowed?
)
.
with
(
user
,
:download_code
,
project
).
and_return
(
true
)
expect
(
subject
[
:evidence_sha
]).
to
eq
(
release
.
evidence_sha
)
expect
(
subject
[
:assets
][
:evidence_file_path
]).
to
eq
(
Gitlab
::
Routing
.
url_helpers
.
evidence_project_release_url
(
project
,
release
.
tag
,
format: :json
)
)
expect
(
entity_evidence
[
:sha
]).
to
eq
(
evidence
.
summary_sha
)
expect
(
entity_evidence
[
:collected_at
]).
to
eq
(
evidence
.
collected_at
)
expect
(
entity_evidence
[
:filepath
]).
to
eq
(
Gitlab
::
Routing
.
url_helpers
.
namespace_project_evidence_url
(
namespace_id:
project
.
namespace
,
project_id:
project
,
tag:
release
,
id:
evidence
.
id
,
format: :json
))
end
end
...
...
@@ -33,8 +36,7 @@ describe API::Entities::Release do
allow
(
Ability
).
to
receive
(
:allowed?
)
.
with
(
user
,
:download_code
,
project
).
and_return
(
false
)
expect
(
subject
.
keys
).
not_to
include
(
:evidence_sha
)
expect
(
subject
[
:assets
].
keys
).
not_to
include
(
:evidence_file_path
)
expect
(
entity
.
keys
).
not_to
include
(
:evidences
)
end
end
end
...
...
@@ -45,7 +47,7 @@ describe API::Entities::Release do
let
(
:issue_title
)
{
'title="%s"'
%
issue
.
title
}
let
(
:release
)
{
create
(
:release
,
project:
project
,
description:
"Now shipping
#{
issue
.
to_reference
}
"
)
}
subject
(
:description_html
)
{
entity
.
as_json
[
:description_html
]
}
subject
(
:description_html
)
{
entity
.
as_json
[
'description_html'
]
}
it
'renders special references if current user has access'
do
project
.
add_reporter
(
user
)
...
...
spec/lib/event_filter_spec.rb
View file @
729e3765
...
...
@@ -28,6 +28,8 @@ describe EventFilter do
let_it_be
(
:comments_event
)
{
create
(
:event
,
:commented
,
project:
public_project
,
target:
public_project
)
}
let_it_be
(
:joined_event
)
{
create
(
:event
,
:joined
,
project:
public_project
,
target:
public_project
)
}
let_it_be
(
:left_event
)
{
create
(
:event
,
:left
,
project:
public_project
,
target:
public_project
)
}
let_it_be
(
:wiki_page_event
)
{
create
(
:wiki_page_event
)
}
let_it_be
(
:wiki_page_update_event
)
{
create
(
:wiki_page_event
,
:updated
)
}
let
(
:filtered_events
)
{
described_class
.
new
(
filter
).
apply_filter
(
Event
.
all
)
}
...
...
@@ -77,6 +79,34 @@ describe EventFilter do
it
'returns all events'
do
expect
(
filtered_events
).
to
eq
(
Event
.
all
)
end
context
'the :wiki_events filter is disabled'
do
before
do
stub_feature_flags
(
wiki_events:
false
)
end
it
'does not return wiki events'
do
expect
(
filtered_events
).
to
eq
(
Event
.
not_wiki_page
)
end
end
end
context
'with the "wiki" filter'
do
let
(
:filter
)
{
described_class
::
WIKI
}
it
'returns only wiki page events'
do
expect
(
filtered_events
).
to
contain_exactly
(
wiki_page_event
,
wiki_page_update_event
)
end
context
'the :wiki_events filter is disabled'
do
before
do
stub_feature_flags
(
wiki_events:
false
)
end
it
'does not return wiki events'
do
expect
(
filtered_events
).
not_to
include
(
wiki_page_event
,
wiki_page_update_event
)
end
end
end
context
'with an unknown filter'
do
...
...
@@ -85,6 +115,16 @@ describe EventFilter do
it
'returns all events'
do
expect
(
filtered_events
).
to
eq
(
Event
.
all
)
end
context
'the :wiki_events filter is disabled'
do
before
do
stub_feature_flags
(
wiki_events:
false
)
end
it
'does not return wiki events'
do
expect
(
filtered_events
).
to
eq
(
Event
.
not_wiki_page
)
end
end
end
context
'with a nil filter'
do
...
...
@@ -93,6 +133,16 @@ describe EventFilter do
it
'returns all events'
do
expect
(
filtered_events
).
to
eq
(
Event
.
all
)
end
context
'the :wiki_events filter is disabled'
do
before
do
stub_feature_flags
(
wiki_events:
false
)
end
it
'does not return wiki events'
do
expect
(
filtered_events
).
to
eq
(
Event
.
not_wiki_page
)
end
end
end
end
...
...
spec/lib/gitlab/import_export/all_models.yml
View file @
729e3765
...
...
@@ -94,7 +94,7 @@ releases:
-
links
-
milestone_releases
-
milestones
-
evidence
-
evidence
s
links
:
-
release
project_members
:
...
...
spec/lib/gitlab/import_export/safe_model_attributes.yml
View file @
729e3765
...
...
@@ -134,7 +134,7 @@ Release:
-
created_at
-
updated_at
-
released_at
Evidence
:
Releases::
Evidence:
-
id
-
summary
-
created_at
...
...
spec/models/event_collection_spec.rb
View file @
729e3765
...
...
@@ -8,22 +8,68 @@ describe EventCollection do
let_it_be
(
:project
)
{
create
(
:project_empty_repo
,
group:
group
)
}
let_it_be
(
:projects
)
{
Project
.
where
(
id:
project
.
id
)
}
let_it_be
(
:user
)
{
create
(
:user
)
}
let_it_be
(
:merge_request
)
{
create
(
:merge_request
)
}
context
'with project events'
do
before
do
20
.
times
do
event
=
create
(
:push_event
,
project:
project
,
author:
user
)
create
(
:push_event_payload
,
event:
event
)
let_it_be
(
:push_event_payloads
)
do
Array
.
new
(
9
)
do
create
(
:push_event_payload
,
event:
create
(
:push_event
,
project:
project
,
author:
user
))
end
create
(
:closed_issue_event
,
project:
project
,
author:
user
)
end
it
'returns an Array of events'
do
let_it_be
(
:merge_request_events
)
{
create_list
(
:event
,
10
,
:commented
,
project:
project
,
target:
merge_request
)
}
let_it_be
(
:closed_issue_event
)
{
create
(
:closed_issue_event
,
project:
project
,
author:
user
)
}
let_it_be
(
:wiki_page_event
)
{
create
(
:wiki_page_event
,
project:
project
)
}
let
(
:push_events
)
{
push_event_payloads
.
map
(
&
:event
)
}
it
'returns an Array of events'
,
:aggregate_failures
do
most_recent_20_events
=
[
wiki_page_event
,
closed_issue_event
,
*
push_events
,
*
merge_request_events
].
sort_by
(
&
:id
).
reverse
.
take
(
20
)
events
=
described_class
.
new
(
projects
).
to_a
expect
(
events
).
to
be_an_instance_of
(
Array
)
expect
(
events
).
to
match_array
(
most_recent_20_events
)
end
context
'the wiki_events feature flag is disabled'
do
before
do
stub_feature_flags
(
wiki_events:
false
)
end
it
'omits the wiki page events when using to_a'
do
events
=
described_class
.
new
(
projects
).
to_a
expect
(
events
).
not_to
include
(
wiki_page_event
)
end
it
'omits the wiki page events when using all_project_events'
do
events
=
described_class
.
new
(
projects
).
all_project_events
expect
(
events
).
not_to
include
(
wiki_page_event
)
end
end
context
'the wiki_events feature flag is enabled'
do
before
do
stub_feature_flags
(
wiki_events:
true
)
end
it
'includes the wiki page events when using to_a'
do
events
=
described_class
.
new
(
projects
).
to_a
expect
(
events
).
to
include
(
wiki_page_event
)
end
it
'includes the wiki page events when using all_project_events'
do
events
=
described_class
.
new
(
projects
).
all_project_events
expect
(
events
).
to
include
(
wiki_page_event
)
end
end
it
'applies a limit to the number of events'
do
...
...
@@ -44,12 +90,25 @@ describe EventCollection do
expect
(
events
).
to
be_empty
end
it
'allows filtering of events using an EventFilter'
do
it
'allows filtering of events using an EventFilter
, returning single item
'
do
filter
=
EventFilter
.
new
(
EventFilter
::
ISSUE
)
events
=
described_class
.
new
(
projects
,
filter:
filter
).
to_a
expect
(
events
.
length
).
to
eq
(
1
)
expect
(
events
[
0
].
action
).
to
eq
(
Event
::
CLOSED
)
expect
(
events
).
to
contain_exactly
(
closed_issue_event
)
end
it
'allows filtering of events using an EventFilter, returning several items'
do
filter
=
EventFilter
.
new
(
EventFilter
::
COMMENTS
)
events
=
described_class
.
new
(
projects
,
filter:
filter
).
to_a
expect
(
events
).
to
match_array
(
merge_request_events
)
end
it
'allows filtering of events using an EventFilter, returning pushes'
do
filter
=
EventFilter
.
new
(
EventFilter
::
PUSH
)
events
=
described_class
.
new
(
projects
,
filter:
filter
).
to_a
expect
(
events
).
to
match_array
(
push_events
)
end
end
...
...
spec/models/event_spec.rb
View file @
729e3765
...
...
@@ -454,9 +454,10 @@ describe Event do
end
end
describe
'
.for_wiki_page
'
do
describe
'
wiki_page predicate scopes
'
do
let_it_be
(
:events
)
do
[
create
(
:push_event
),
create
(
:closed_issue_event
),
create
(
:wiki_page_event
),
create
(
:closed_issue_event
),
...
...
@@ -465,10 +466,22 @@ describe Event do
]
end
it
'only contains the wiki page events'
do
wiki_events
=
events
.
select
(
&
:wiki_page?
)
describe
'.for_wiki_page'
do
it
'only contains the wiki page events'
do
wiki_events
=
events
.
select
(
&
:wiki_page?
)
expect
(
described_class
.
for_wiki_page
).
to
match_array
(
wiki_events
)
expect
(
events
).
not_to
match_array
(
wiki_events
)
expect
(
described_class
.
for_wiki_page
).
to
match_array
(
wiki_events
)
end
end
describe
'.not_wiki_page'
do
it
'does not contain the wiki page events'
do
non_wiki_events
=
events
.
reject
(
&
:wiki_page?
)
expect
(
events
).
not_to
match_array
(
non_wiki_events
)
expect
(
described_class
.
not_wiki_page
).
to
match_array
(
non_wiki_events
)
end
end
end
...
...
spec/models/release_spec.rb
View file @
729e3765
...
...
@@ -15,7 +15,7 @@ RSpec.describe Release do
it
{
is_expected
.
to
have_many
(
:links
).
class_name
(
'Releases::Link'
)
}
it
{
is_expected
.
to
have_many
(
:milestones
)
}
it
{
is_expected
.
to
have_many
(
:milestone_releases
)
}
it
{
is_expected
.
to
have_
one
(
:evidence
)
}
it
{
is_expected
.
to
have_
many
(
:evidences
).
class_name
(
'Releases::Evidence'
)
}
end
describe
'validation'
do
...
...
@@ -97,7 +97,7 @@ RSpec.describe Release do
describe
'#create_evidence!'
do
context
'when a release is created'
do
it
'creates one Evidence object too'
do
expect
{
release_with_evidence
}.
to
change
(
Evidence
,
:count
).
by
(
1
)
expect
{
release_with_evidence
}.
to
change
(
Releases
::
Evidence
,
:count
).
by
(
1
)
end
end
end
...
...
@@ -106,7 +106,7 @@ RSpec.describe Release do
it
'also deletes the associated evidence'
do
release_with_evidence
expect
{
release_with_evidence
.
destroy
}.
to
change
(
Evidence
,
:count
).
by
(
-
1
)
expect
{
release_with_evidence
.
destroy
}.
to
change
(
Releases
::
Evidence
,
:count
).
by
(
-
1
)
end
end
end
...
...
@@ -155,7 +155,7 @@ RSpec.describe Release do
context
'when a release was created with evidence collection'
do
let!
(
:release
)
{
create
(
:release
,
:with_evidence
)
}
it
{
is_expected
.
to
eq
(
release
.
evidence
.
summary_sha
)
}
it
{
is_expected
.
to
eq
(
release
.
evidence
s
.
first
.
summary_sha
)
}
end
end
...
...
@@ -171,7 +171,7 @@ RSpec.describe Release do
context
'when a release was created with evidence collection'
do
let!
(
:release
)
{
create
(
:release
,
:with_evidence
)
}
it
{
is_expected
.
to
eq
(
release
.
evidence
.
summary
)
}
it
{
is_expected
.
to
eq
(
release
.
evidence
s
.
first
.
summary
)
}
end
end
...
...
spec/models/evidence_spec.rb
→
spec/models/
releases/
evidence_spec.rb
View file @
729e3765
...
...
@@ -2,7 +2,7 @@
require
'spec_helper'
describe
Evidence
do
describe
Releases
::
Evidence
do
let_it_be
(
:project
)
{
create
(
:project
)
}
let
(
:release
)
{
create
(
:release
,
project:
project
)
}
let
(
:schema_file
)
{
'evidences/evidence'
}
...
...
spec/presenters/release_presenter_spec.rb
View file @
729e3765
...
...
@@ -112,28 +112,4 @@ describe ReleasePresenter do
it
{
is_expected
.
to
be_nil
}
end
end
describe
'#evidence_file_path'
do
subject
{
presenter
.
evidence_file_path
}
context
'without evidence'
do
it
{
is_expected
.
to
be_falsy
}
end
context
'with evidence'
do
let
(
:release
)
{
create
:release
,
:with_evidence
,
project:
project
}
specify
do
is_expected
.
to
match
/
#{
evidence_project_release_url
(
project
,
release
.
tag
,
format: :json
)
}
/
end
end
context
'when a tag contains a slash'
do
let
(
:release
)
{
create
:release
,
:with_evidence
,
project:
project
,
tag:
'debian/2.4.0-1'
}
specify
do
is_expected
.
to
match
/
#{
evidence_project_release_url
(
project
,
CGI
.
escape
(
release
.
tag
),
format: :json
)
}
/
end
end
end
end
spec/requests/api/events_spec.rb
View file @
729e3765
...
...
@@ -114,6 +114,26 @@ describe API::Events do
expect
(
json_response
.
size
).
to
eq
(
1
)
end
context
'when the list of events includes wiki page events'
do
it
'returns information about the wiki event'
,
:aggregate_failures
do
page
=
create
(
:wiki_page
,
project:
private_project
)
[
Event
::
CREATED
,
Event
::
UPDATED
,
Event
::
DESTROYED
].
each
do
|
action
|
create
(
:wiki_page_event
,
wiki_page:
page
,
action:
action
,
author:
user
)
end
get
api
(
"/users/
#{
user
.
id
}
/events"
,
user
)
wiki_events
=
json_response
.
select
{
|
e
|
e
[
'target_type'
]
==
'WikiPage::Meta'
}
action_names
=
wiki_events
.
map
{
|
e
|
e
[
'action_name'
]
}
titles
=
wiki_events
.
map
{
|
e
|
e
[
'target_title'
]
}
slugs
=
wiki_events
.
map
{
|
e
|
e
.
dig
(
'wiki_page'
,
'slug'
)
}
expect
(
action_names
).
to
contain_exactly
(
'created'
,
'updated'
,
'destroyed'
)
expect
(
titles
).
to
all
(
eq
(
page
.
title
))
expect
(
slugs
).
to
all
(
eq
(
page
.
slug
))
end
end
context
'when the list of events includes push events'
do
let
(
:event
)
do
create
(
:push_event
,
author:
user
,
project:
private_project
)
...
...
spec/requests/api/graphql/group_query_spec.rb
View file @
729e3765
...
...
@@ -51,6 +51,7 @@ describe 'getting group information', :do_not_mock_admin_mode do
it
"returns one of user1's groups"
do
project
=
create
(
:project
,
namespace:
group2
,
path:
'Foo'
)
issue
=
create
(
:issue
,
project:
create
(
:project
,
group:
group1
))
create
(
:project_group_link
,
project:
project
,
group:
group1
)
post_graphql
(
group_query
(
group1
),
current_user:
user1
)
...
...
@@ -67,6 +68,8 @@ describe 'getting group information', :do_not_mock_admin_mode do
expect
(
graphql_data
[
'group'
][
'fullName'
]).
to
eq
(
group1
.
full_name
)
expect
(
graphql_data
[
'group'
][
'fullPath'
]).
to
eq
(
group1
.
full_path
)
expect
(
graphql_data
[
'group'
][
'parentId'
]).
to
eq
(
group1
.
parent_id
)
expect
(
graphql_data
[
'group'
][
'issues'
][
'nodes'
].
count
).
to
eq
(
1
)
expect
(
graphql_data
[
'group'
][
'issues'
][
'nodes'
][
0
][
'iid'
]).
to
eq
(
issue
.
iid
.
to_s
)
end
it
"does not return a non existing group"
do
...
...
spec/requests/api/groups_spec.rb
View file @
729e3765
...
...
@@ -71,6 +71,7 @@ describe API::Groups do
expect
(
response
).
to
include_pagination_headers
expect
(
json_response
).
to
be_an
Array
expect
(
json_response
.
length
).
to
eq
(
1
)
expect
(
json_response
.
first
[
'created_at'
]).
to
be_present
expect
(
json_response
)
.
to
satisfy_one
{
|
group
|
group
[
'name'
]
==
group1
.
name
}
end
...
...
@@ -121,6 +122,15 @@ describe API::Groups do
expect
(
json_response
).
to
be_an
Array
expect
(
json_response
.
first
).
not_to
include
'statistics'
end
it
"includes a created_at timestamp"
do
get
api
(
"/groups"
,
user1
)
expect
(
response
).
to
have_gitlab_http_status
(
:ok
)
expect
(
response
).
to
include_pagination_headers
expect
(
json_response
).
to
be_an
Array
expect
(
json_response
.
first
[
'created_at'
]).
to
be_present
end
end
context
"when authenticated as admin"
do
...
...
@@ -152,6 +162,15 @@ describe API::Groups do
expect
(
json_response
.
first
).
not_to
include
(
'statistics'
)
end
it
"includes a created_at timestamp"
do
get
api
(
"/groups"
,
admin
)
expect
(
response
).
to
have_gitlab_http_status
(
:ok
)
expect
(
response
).
to
include_pagination_headers
expect
(
json_response
).
to
be_an
Array
expect
(
json_response
.
first
[
'created_at'
]).
to
be_present
end
it
"includes statistics if requested"
do
attributes
=
{
storage_size:
1158
,
...
...
@@ -357,6 +376,7 @@ describe API::Groups do
expect
(
response
).
to
have_gitlab_http_status
(
:ok
)
expect
(
json_response
).
not_to
include
(
'runners_token'
)
expect
(
json_response
).
to
include
(
'created_at'
)
end
it
'returns only public projects in the group'
do
...
...
@@ -407,6 +427,7 @@ describe API::Groups do
expect
(
json_response
[
'full_name'
]).
to
eq
(
group1
.
full_name
)
expect
(
json_response
[
'full_path'
]).
to
eq
(
group1
.
full_path
)
expect
(
json_response
[
'parent_id'
]).
to
eq
(
group1
.
parent_id
)
expect
(
json_response
[
'created_at'
]).
to
be_present
expect
(
json_response
[
'projects'
]).
to
be_an
Array
expect
(
json_response
[
'projects'
].
length
).
to
eq
(
2
)
expect
(
json_response
[
'shared_projects'
]).
to
be_an
Array
...
...
@@ -613,6 +634,7 @@ describe API::Groups do
expect
(
json_response
[
'subgroup_creation_level'
]).
to
eq
(
"maintainer"
)
expect
(
json_response
[
'request_access_enabled'
]).
to
eq
(
true
)
expect
(
json_response
[
'parent_id'
]).
to
eq
(
nil
)
expect
(
json_response
[
'created_at'
]).
to
be_present
expect
(
json_response
[
'projects'
]).
to
be_an
Array
expect
(
json_response
[
'projects'
].
length
).
to
eq
(
2
)
expect
(
json_response
[
'shared_projects'
]).
to
be_an
Array
...
...
spec/requests/api/releases_spec.rb
View file @
729e3765
...
...
@@ -104,6 +104,21 @@ describe API::Releases do
expect
(
json_response
.
first
[
'upcoming_release'
]).
to
eq
(
false
)
end
it
'avoids N+1 queries'
do
create
(
:release
,
:with_evidence
,
project:
project
,
tag:
'v0.1'
,
author:
maintainer
)
control_count
=
ActiveRecord
::
QueryRecorder
.
new
do
get
api
(
"/projects/
#{
project
.
id
}
/releases"
,
maintainer
)
end
.
count
create
(
:release
,
:with_evidence
,
project:
project
,
tag:
'v0.1'
,
author:
maintainer
)
create
(
:release
,
:with_evidence
,
project:
project
,
tag:
'v0.1'
,
author:
maintainer
)
expect
do
get
api
(
"/projects/
#{
project
.
id
}
/releases"
,
maintainer
)
end
.
not_to
exceed_query_limit
(
control_count
)
end
context
'when tag does not exist in git repository'
do
let!
(
:release
)
{
create
(
:release
,
project:
project
,
tag:
'v1.1.5'
)
}
...
...
@@ -725,7 +740,7 @@ describe API::Releases do
end
it
'does not create an Evidence object'
,
:sidekiq_inline
do
expect
{
subject
}.
not_to
change
(
Evidence
,
:count
)
expect
{
subject
}.
not_to
change
(
Releases
::
Evidence
,
:count
)
end
it
'is a historical release'
do
...
...
@@ -755,7 +770,7 @@ describe API::Releases do
end
it
'creates Evidence'
,
:sidekiq_inline
do
expect
{
subject
}.
to
change
(
Evidence
,
:count
).
by
(
1
)
expect
{
subject
}.
to
change
(
Releases
::
Evidence
,
:count
).
by
(
1
)
end
it
'is not a historical release'
do
...
...
@@ -785,7 +800,7 @@ describe API::Releases do
end
it
'creates Evidence'
,
:sidekiq_inline
do
expect
{
subject
}.
to
change
(
Evidence
,
:count
).
by
(
1
)
expect
{
subject
}.
to
change
(
Releases
::
Evidence
,
:count
).
by
(
1
)
end
it
'is not a historical release'
do
...
...
spec/services/event_create_service_spec.rb
View file @
729e3765
...
...
@@ -153,6 +153,46 @@ describe EventCreateService do
end
end
describe
'#wiki_event'
do
let_it_be
(
:user
)
{
create
(
:user
)
}
let_it_be
(
:wiki_page
)
{
create
(
:wiki_page
)
}
let_it_be
(
:meta
)
{
create
(
:wiki_page_meta
,
:for_wiki_page
,
wiki_page:
wiki_page
)
}
Event
::
WIKI_ACTIONS
.
each
do
|
action
|
context
"The action is
#{
action
}
"
do
let
(
:event
)
{
service
.
wiki_event
(
meta
,
user
,
action
)
}
it
'creates the event'
do
expect
(
event
).
to
have_attributes
(
wiki_page?:
true
,
valid?:
true
,
persisted?:
true
,
action:
action
,
wiki_page:
wiki_page
)
end
context
'the feature is disabled'
do
before
do
stub_feature_flags
(
wiki_events:
false
)
end
it
'does not create the event'
do
expect
{
event
}.
not_to
change
(
Event
,
:count
)
end
end
end
end
(
Event
::
ACTIONS
.
values
-
Event
::
WIKI_ACTIONS
).
each
do
|
bad_action
|
context
"The action is
#{
bad_action
}
"
do
it
'raises an error'
do
expect
{
service
.
wiki_event
(
meta
,
user
,
bad_action
)
}.
to
raise_error
(
described_class
::
IllegalActionError
)
end
end
end
end
describe
'#push'
,
:clean_gitlab_redis_shared_state
do
let
(
:project
)
{
create
(
:project
)
}
let
(
:user
)
{
create
(
:user
)
}
...
...
spec/services/wiki_pages/base_service_spec.rb
View file @
729e3765
...
...
@@ -6,22 +6,24 @@ describe WikiPages::BaseService do
let
(
:project
)
{
double
(
'project'
)
}
let
(
:user
)
{
double
(
'user'
)
}
subject
(
:service
)
{
described_class
.
new
(
project
,
user
,
{})
}
describe
'#increment_usage'
do
counter
=
Gitlab
::
UsageDataCounters
::
WikiPageCounter
error
=
counter
::
UnknownEvent
it
'raises an error on unknown events'
do
expect
{
subject
.
send
(
:increment_usage
,
:bad_event
)
}.
to
raise_error
error
end
let
(
:subject
)
{
bad_service_class
.
new
(
project
,
user
,
{})
}
context
'the event is valid'
do
counter
::
KNOWN_EVENTS
.
each
do
|
e
|
it
"updates the
#{
e
}
counter"
do
expect
{
subject
.
send
(
:increment_usage
,
e
)
}.
to
change
{
counter
.
read
(
e
)
}
context
'the class implements usage_counter_action incorrectly'
do
let
(
:bad_service_class
)
do
Class
.
new
(
described_class
)
do
def
usage_counter_action
:bad_event
end
end
end
it
'raises an error on unknown events'
do
expect
{
subject
.
send
(
:increment_usage
)
}.
to
raise_error
(
error
)
end
end
end
end
spec/services/wiki_pages/create_service_spec.rb
View file @
729e3765
...
...
@@ -5,19 +5,16 @@ require 'spec_helper'
describe
WikiPages
::
CreateService
do
let
(
:project
)
{
create
(
:project
,
:wiki_repo
)
}
let
(
:user
)
{
create
(
:user
)
}
let
(
:page_title
)
{
'Title'
}
let
(
:opts
)
do
{
title:
'Title'
,
title:
page_title
,
content:
'Content for wiki page'
,
format:
'markdown'
}
end
let
(
:bad_opts
)
do
{
title:
''
}
end
subject
(
:service
)
{
described_class
.
new
(
project
,
user
,
opts
)
}
before
do
...
...
@@ -35,8 +32,7 @@ describe WikiPages::CreateService do
end
it
'executes webhooks'
do
expect
(
service
).
to
receive
(
:execute_hooks
).
once
.
with
(
instance_of
(
WikiPage
),
'create'
)
expect
(
service
).
to
receive
(
:execute_hooks
).
once
.
with
(
WikiPage
)
service
.
execute
end
...
...
@@ -47,8 +43,41 @@ describe WikiPages::CreateService do
expect
{
service
.
execute
}.
to
change
{
counter
.
read
(
:create
)
}.
by
1
end
shared_examples
'correct event created'
do
it
'creates appropriate events'
do
expect
{
service
.
execute
}.
to
change
{
Event
.
count
}.
by
1
expect
(
Event
.
recent
.
first
).
to
have_attributes
(
action:
Event
::
CREATED
,
target:
have_attributes
(
canonical_slug:
page_title
)
)
end
end
context
'the new page is at the top level'
do
let
(
:page_title
)
{
'root-level-page'
}
include_examples
'correct event created'
end
context
'the new page is in a subsection'
do
let
(
:page_title
)
{
'subsection/page'
}
include_examples
'correct event created'
end
context
'the feature is disabled'
do
before
do
stub_feature_flags
(
wiki_events:
false
)
end
it
'does not record the activity'
do
expect
{
service
.
execute
}.
not_to
change
(
Event
,
:count
)
end
end
context
'when the options are bad'
do
subject
(
:service
)
{
described_class
.
new
(
project
,
user
,
bad_opts
)
}
let
(
:page_title
)
{
''
}
it
'does not count a creation event'
do
counter
=
Gitlab
::
UsageDataCounters
::
WikiPageCounter
...
...
@@ -56,6 +85,10 @@ describe WikiPages::CreateService do
expect
{
service
.
execute
}.
not_to
change
{
counter
.
read
(
:create
)
}
end
it
'does not record the activity'
do
expect
{
service
.
execute
}.
not_to
change
(
Event
,
:count
)
end
it
'reports the error'
do
expect
(
service
.
execute
).
to
be_invalid
.
and
have_attributes
(
errors:
be_present
)
...
...
spec/services/wiki_pages/destroy_service_spec.rb
View file @
729e3765
...
...
@@ -15,8 +15,7 @@ describe WikiPages::DestroyService do
describe
'#execute'
do
it
'executes webhooks'
do
expect
(
service
).
to
receive
(
:execute_hooks
).
once
.
with
(
instance_of
(
WikiPage
),
'delete'
)
expect
(
service
).
to
receive
(
:execute_hooks
).
once
.
with
(
page
)
service
.
execute
(
page
)
end
...
...
@@ -27,10 +26,29 @@ describe WikiPages::DestroyService do
expect
{
service
.
execute
(
page
)
}.
to
change
{
counter
.
read
(
:delete
)
}.
by
1
end
it
'creates a new wiki page deletion event'
do
expect
{
service
.
execute
(
page
)
}.
to
change
{
Event
.
count
}.
by
1
expect
(
Event
.
recent
.
first
).
to
have_attributes
(
action:
Event
::
DESTROYED
,
target:
have_attributes
(
canonical_slug:
page
.
slug
)
)
end
it
'does not increment the delete count if the deletion failed'
do
counter
=
Gitlab
::
UsageDataCounters
::
WikiPageCounter
expect
{
service
.
execute
(
nil
)
}.
not_to
change
{
counter
.
read
(
:delete
)
}
end
end
context
'the feature is disabled'
do
before
do
stub_feature_flags
(
wiki_events:
false
)
end
it
'does not record the activity'
do
expect
{
service
.
execute
(
page
)
}.
not_to
change
(
Event
,
:count
)
end
end
end
spec/services/wiki_pages/update_service_spec.rb
View file @
729e3765
...
...
@@ -6,20 +6,17 @@ describe WikiPages::UpdateService do
let
(
:project
)
{
create
(
:project
)
}
let
(
:user
)
{
create
(
:user
)
}
let
(
:page
)
{
create
(
:wiki_page
)
}
let
(
:page_title
)
{
'New Title'
}
let
(
:opts
)
do
{
content:
'New content for wiki page'
,
format:
'markdown'
,
message:
'New wiki message'
,
title:
'New Title'
title:
page_title
}
end
let
(
:bad_opts
)
do
{
title:
''
}
end
subject
(
:service
)
{
described_class
.
new
(
project
,
user
,
opts
)
}
before
do
...
...
@@ -34,12 +31,11 @@ describe WikiPages::UpdateService do
expect
(
updated_page
.
message
).
to
eq
(
opts
[
:message
])
expect
(
updated_page
.
content
).
to
eq
(
opts
[
:content
])
expect
(
updated_page
.
format
).
to
eq
(
opts
[
:format
].
to_sym
)
expect
(
updated_page
.
title
).
to
eq
(
opts
[
:title
]
)
expect
(
updated_page
.
title
).
to
eq
(
page_title
)
end
it
'executes webhooks'
do
expect
(
service
).
to
receive
(
:execute_hooks
).
once
.
with
(
instance_of
(
WikiPage
),
'update'
)
expect
(
service
).
to
receive
(
:execute_hooks
).
once
.
with
(
WikiPage
)
service
.
execute
(
page
)
end
...
...
@@ -50,8 +46,42 @@ describe WikiPages::UpdateService do
expect
{
service
.
execute
page
}.
to
change
{
counter
.
read
(
:update
)
}.
by
1
end
shared_examples
'adds activity event'
do
it
'adds a new wiki page activity event'
do
expect
{
service
.
execute
(
page
)
}.
to
change
{
Event
.
count
}.
by
1
expect
(
Event
.
recent
.
first
).
to
have_attributes
(
action:
Event
::
UPDATED
,
wiki_page:
page
,
target_title:
page
.
title
)
end
end
context
'the page is at the top level'
do
let
(
:page_title
)
{
'Top level page'
}
include_examples
'adds activity event'
end
context
'the page is in a subsection'
do
let
(
:page_title
)
{
'Subsection / secondary page'
}
include_examples
'adds activity event'
end
context
'the feature is disabled'
do
before
do
stub_feature_flags
(
wiki_events:
false
)
end
it
'does not record the activity'
do
expect
{
service
.
execute
(
page
)
}.
not_to
change
(
Event
,
:count
)
end
end
context
'when the options are bad'
do
subject
(
:service
)
{
described_class
.
new
(
project
,
user
,
bad_opts
)
}
let
(
:page_title
)
{
''
}
it
'does not count an edit event'
do
counter
=
Gitlab
::
UsageDataCounters
::
WikiPageCounter
...
...
@@ -59,6 +89,10 @@ describe WikiPages::UpdateService do
expect
{
service
.
execute
page
}.
not_to
change
{
counter
.
read
(
:update
)
}
end
it
'does not record the activity'
do
expect
{
service
.
execute
page
}.
not_to
change
(
Event
,
:count
)
end
it
'reports the error'
do
expect
(
service
.
execute
page
).
to
be_invalid
.
and
have_attributes
(
errors:
be_present
)
...
...
spec/support/helpers/stub_experiments.rb
View file @
729e3765
...
...
@@ -8,6 +8,8 @@ module StubExperiments
# Examples
# - `stub_experiment(signup_flow: false)` ... Disable `signup_flow` experiment globally.
def
stub_experiment
(
experiments
)
allow
(
Gitlab
::
Experimentation
).
to
receive
(
:enabled?
).
and_call_original
experiments
.
each
do
|
experiment_key
,
enabled
|
allow
(
Gitlab
::
Experimentation
).
to
receive
(
:enabled?
).
with
(
experiment_key
)
{
enabled
}
end
...
...
@@ -20,6 +22,8 @@ module StubExperiments
# Examples
# - `stub_experiment_for_user(signup_flow: false)` ... Disable `signup_flow` experiment for user.
def
stub_experiment_for_user
(
experiments
)
allow
(
Gitlab
::
Experimentation
).
to
receive
(
:enabled_for_user?
).
and_call_original
experiments
.
each
do
|
experiment_key
,
enabled
|
allow
(
Gitlab
::
Experimentation
).
to
receive
(
:enabled_for_user?
).
with
(
experiment_key
,
anything
)
{
enabled
}
end
...
...
spec/workers/create_evidence_worker_spec.rb
View file @
729e3765
...
...
@@ -5,7 +5,7 @@ require 'spec_helper'
describe
CreateEvidenceWorker
do
let!
(
:release
)
{
create
(
:release
)
}
it
'creates a new Evidence'
do
expect
{
described_class
.
new
.
perform
(
release
.
id
)
}.
to
change
(
Evidence
,
:count
).
by
(
1
)
it
'creates a new Evidence
record
'
do
expect
{
described_class
.
new
.
perform
(
release
.
id
)
}.
to
change
(
Releases
::
Evidence
,
:count
).
by
(
1
)
end
end
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment