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
4584eb0e
Commit
4584eb0e
authored
Sep 18, 2019
by
GitLab Bot
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add latest changes from gitlab-org/gitlab@master
parent
bdbded58
Changes
43
Hide whitespace changes
Inline
Side-by-side
Showing
43 changed files
with
485 additions
and
31 deletions
+485
-31
app/assets/javascripts/import_projects/components/import_projects_table.vue
...ipts/import_projects/components/import_projects_table.vue
+39
-6
app/assets/javascripts/import_projects/index.js
app/assets/javascripts/import_projects/index.js
+1
-1
app/assets/javascripts/import_projects/store/actions.js
app/assets/javascripts/import_projects/store/actions.js
+17
-5
app/assets/javascripts/import_projects/store/getters.js
app/assets/javascripts/import_projects/store/getters.js
+5
-0
app/assets/javascripts/import_projects/store/mutation_types.js
...ssets/javascripts/import_projects/store/mutation_types.js
+2
-0
app/assets/javascripts/import_projects/store/mutations.js
app/assets/javascripts/import_projects/store/mutations.js
+4
-0
app/assets/javascripts/import_projects/store/state.js
app/assets/javascripts/import_projects/store/state.js
+1
-0
app/controllers/import/github_controller.rb
app/controllers/import/github_controller.rb
+18
-3
changelogs/unreleased/georgekoltsov-add-github-importer-filtering.yml
...nreleased/georgekoltsov-add-github-importer-filtering.yml
+5
-0
doc/user/project/import/gitea.md
doc/user/project/import/gitea.md
+7
-3
doc/user/project/import/github.md
doc/user/project/import/github.md
+4
-1
doc/user/project/import/img/import_projects_from_gitea_importer_v12_3.png
.../import/img/import_projects_from_gitea_importer_v12_3.png
+0
-0
doc/user/project/import/img/import_projects_from_github_importer.png
...oject/import/img/import_projects_from_github_importer.png
+0
-0
doc/user/project/import/img/import_projects_from_github_importer_v12_3.png
...import/img/import_projects_from_github_importer_v12_3.png
+0
-0
lib/gitlab/analytics/cycle_analytics/stage_events.rb
lib/gitlab/analytics/cycle_analytics/stage_events.rb
+4
-2
lib/gitlab/analytics/cycle_analytics/stage_events/code_stage_start.rb
...nalytics/cycle_analytics/stage_events/code_stage_start.rb
+15
-0
lib/gitlab/analytics/cycle_analytics/stage_events/issue_created.rb
...b/analytics/cycle_analytics/stage_events/issue_created.rb
+4
-0
lib/gitlab/analytics/cycle_analytics/stage_events/issue_first_mentioned_in_commit.rb
...analytics/stage_events/issue_first_mentioned_in_commit.rb
+10
-0
lib/gitlab/analytics/cycle_analytics/stage_events/issue_stage_end.rb
...analytics/cycle_analytics/stage_events/issue_stage_end.rb
+13
-0
lib/gitlab/analytics/cycle_analytics/stage_events/merge_request_created.rb
...ics/cycle_analytics/stage_events/merge_request_created.rb
+4
-0
lib/gitlab/analytics/cycle_analytics/stage_events/merge_request_first_deployed_to_production.rb
...tage_events/merge_request_first_deployed_to_production.rb
+10
-0
lib/gitlab/analytics/cycle_analytics/stage_events/merge_request_last_build_finished.rb
...alytics/stage_events/merge_request_last_build_finished.rb
+10
-0
lib/gitlab/analytics/cycle_analytics/stage_events/merge_request_last_build_started.rb
...nalytics/stage_events/merge_request_last_build_started.rb
+10
-0
lib/gitlab/analytics/cycle_analytics/stage_events/merge_request_merged.rb
...tics/cycle_analytics/stage_events/merge_request_merged.rb
+10
-0
lib/gitlab/analytics/cycle_analytics/stage_events/plan_stage_start.rb
...nalytics/cycle_analytics/stage_events/plan_stage_start.rb
+16
-0
lib/gitlab/analytics/cycle_analytics/stage_events/production_stage_end.rb
...tics/cycle_analytics/stage_events/production_stage_end.rb
+33
-0
lib/gitlab/analytics/cycle_analytics/stage_events/stage_event.rb
...lab/analytics/cycle_analytics/stage_events/stage_event.rb
+17
-0
locale/gitlab.pot
locale/gitlab.pot
+2
-2
spec/frontend/import_projects/components/import_projects_table_spec.js
.../import_projects/components/import_projects_table_spec.js
+7
-1
spec/frontend/import_projects/store/actions_spec.js
spec/frontend/import_projects/store/actions_spec.js
+59
-5
spec/lib/gitlab/analytics/cycle_analytics/stage_events/code_stage_start_spec.rb
...ics/cycle_analytics/stage_events/code_stage_start_spec.rb
+22
-0
spec/lib/gitlab/analytics/cycle_analytics/stage_events/issue_created_spec.rb
...lytics/cycle_analytics/stage_events/issue_created_spec.rb
+7
-0
spec/lib/gitlab/analytics/cycle_analytics/stage_events/issue_first_mentioned_in_commit_spec.rb
...tics/stage_events/issue_first_mentioned_in_commit_spec.rb
+7
-0
spec/lib/gitlab/analytics/cycle_analytics/stage_events/issue_stage_end_spec.rb
...tics/cycle_analytics/stage_events/issue_stage_end_spec.rb
+7
-0
spec/lib/gitlab/analytics/cycle_analytics/stage_events/merge_request_created_spec.rb
...ycle_analytics/stage_events/merge_request_created_spec.rb
+7
-0
spec/lib/gitlab/analytics/cycle_analytics/stage_events/merge_request_first_deployed_to_production_spec.rb
...events/merge_request_first_deployed_to_production_spec.rb
+7
-0
spec/lib/gitlab/analytics/cycle_analytics/stage_events/merge_request_last_build_finished_spec.rb
...cs/stage_events/merge_request_last_build_finished_spec.rb
+7
-0
spec/lib/gitlab/analytics/cycle_analytics/stage_events/merge_request_last_build_started_spec.rb
...ics/stage_events/merge_request_last_build_started_spec.rb
+7
-0
spec/lib/gitlab/analytics/cycle_analytics/stage_events/merge_request_merged_spec.rb
...cycle_analytics/stage_events/merge_request_merged_spec.rb
+7
-0
spec/lib/gitlab/analytics/cycle_analytics/stage_events/plan_stage_start_spec.rb
...ics/cycle_analytics/stage_events/plan_stage_start_spec.rb
+24
-0
spec/lib/gitlab/analytics/cycle_analytics/stage_events/stage_event_spec.rb
...nalytics/cycle_analytics/stage_events/stage_event_spec.rb
+5
-2
spec/support/controllers/githubish_import_controller_shared_examples.rb
...ontrollers/githubish_import_controller_shared_examples.rb
+32
-0
spec/support/shared_examples/cycle_analytics_event_shared_examples.rb
.../shared_examples/cycle_analytics_event_shared_examples.rb
+19
-0
No files found.
app/assets/javascripts/import_projects/components/import_projects_table.vue
View file @
4584eb0e
<
script
>
import
_
from
'
underscore
'
;
import
{
mapActions
,
mapState
,
mapGetters
}
from
'
vuex
'
;
import
{
GlLoadingIcon
}
from
'
@gitlab/ui
'
;
import
LoadingButton
from
'
~/vue_shared/components/loading_button.vue
'
;
...
...
@@ -7,6 +8,8 @@ import ImportedProjectTableRow from './imported_project_table_row.vue';
import
ProviderRepoTableRow
from
'
./provider_repo_table_row.vue
'
;
import
eventHub
from
'
../event_hub
'
;
const
reposFetchThrottleDelay
=
1000
;
export
default
{
name
:
'
ImportProjectsTable
'
,
components
:
{
...
...
@@ -23,11 +26,11 @@ export default {
},
computed
:
{
...
mapState
([
'
importedProjects
'
,
'
providerRepos
'
,
'
isLoadingRepos
'
]),
...
mapState
([
'
importedProjects
'
,
'
providerRepos
'
,
'
isLoadingRepos
'
,
'
filter
'
]),
...
mapGetters
([
'
isImportingAnyRepo
'
,
'
hasProviderRepos
'
,
'
hasImportedProjects
'
]),
emptyStateText
()
{
return
sprintf
(
__
(
'
No %{providerTitle} repositories
available to import
'
),
{
return
sprintf
(
__
(
'
No %{providerTitle} repositories
found
'
),
{
providerTitle
:
this
.
providerTitle
,
});
},
...
...
@@ -47,21 +50,38 @@ export default {
},
methods
:
{
...
mapActions
([
'
fetchRepos
'
,
'
fetchJobs
'
,
'
stopJobsPolling
'
,
'
clearJobsEtagPoll
'
]),
...
mapActions
([
'
fetchRepos
'
,
'
fetchReposFiltered
'
,
'
fetchJobs
'
,
'
stopJobsPolling
'
,
'
clearJobsEtagPoll
'
,
'
setFilter
'
,
]),
importAll
()
{
eventHub
.
$emit
(
'
importAll
'
);
},
handleFilterInput
({
target
})
{
this
.
setFilter
(
target
.
value
);
},
throttledFetchRepos
:
_
.
throttle
(
function
fetch
()
{
eventHub
.
$off
(
'
importAll
'
);
this
.
fetchRepos
();
},
reposFetchThrottleDelay
),
},
};
</
script
>
<
template
>
<div>
<p
class=
"light text-nowrap mt-2"
>
{{
s__
(
'
ImportProjects|Select the projects you want to import
'
)
}}
</p>
<div
class=
"d-flex justify-content-between align-items-end flex-wrap mb-3"
>
<p
class=
"light text-nowrap mt-2 my-sm-0"
>
{{
s__
(
'
ImportProjects|Select the projects you want to import
'
)
}}
</p>
<loading-button
container-class=
"btn btn-success js-import-all"
:loading=
"isImportingAnyRepo"
...
...
@@ -70,6 +90,19 @@ export default {
type=
"button"
@
click=
"importAll"
/>
<form
novalidate
@
submit
.
prevent
>
<input
:value=
"filter"
data-qa-selector=
"githubish_import_filter_field"
class=
"form-control"
name=
"filter"
:placeholder=
"__('Filter your projects by name')"
autofocus
size=
"40"
@
input=
"handleFilterInput($event)"
@
keyup.enter=
"throttledFetchRepos"
/>
</form>
</div>
<gl-loading-icon
v-if=
"isLoadingRepos"
...
...
app/assets/javascripts/import_projects/index.js
View file @
4584eb0e
...
...
@@ -38,7 +38,7 @@ export default function mountImportProjectsTable(mountElement) {
},
methods
:
{
...
mapActions
([
'
setInitialData
'
]),
...
mapActions
([
'
setInitialData
'
,
'
setFilter
'
]),
},
render
(
createElement
)
{
...
...
app/assets/javascripts/import_projects/store/actions.js
View file @
4584eb0e
...
...
@@ -5,6 +5,7 @@ import Poll from '~/lib/utils/poll';
import
createFlash
from
'
~/flash
'
;
import
{
s__
,
sprintf
}
from
'
~/locale
'
;
import
axios
from
'
~/lib/utils/axios_utils
'
;
import
{
jobsPathWithFilter
,
reposPathWithFilter
}
from
'
./getters
'
;
let
eTagPoll
;
...
...
@@ -19,16 +20,20 @@ export const restartJobsPolling = () => {
};
export
const
setInitialData
=
({
commit
},
data
)
=>
commit
(
types
.
SET_INITIAL_DATA
,
data
);
export
const
setFilter
=
({
commit
},
filter
)
=>
commit
(
types
.
SET_FILTER
,
filter
);
export
const
requestRepos
=
({
commit
},
repos
)
=>
commit
(
types
.
REQUEST_REPOS
,
repos
);
export
const
receiveReposSuccess
=
({
commit
},
repos
)
=>
commit
(
types
.
RECEIVE_REPOS_SUCCESS
,
repos
);
export
const
receiveReposError
=
({
commit
})
=>
commit
(
types
.
RECEIVE_REPOS_ERROR
);
export
const
fetchRepos
=
({
state
,
dispatch
})
=>
{
dispatch
(
'
stopJobsPolling
'
);
dispatch
(
'
requestRepos
'
);
const
{
provider
}
=
state
;
return
axios
.
get
(
state
.
reposPath
)
.
get
(
reposPathWithFilter
(
state
)
)
.
then
(({
data
})
=>
dispatch
(
'
receiveReposSuccess
'
,
convertObjectPropsToCamelCase
(
data
,
{
deep
:
true
})),
)
...
...
@@ -36,7 +41,7 @@ export const fetchRepos = ({ state, dispatch }) => {
.
catch
(()
=>
{
createFlash
(
sprintf
(
s__
(
'
ImportProjects|Requesting your %{provider} repositories failed
'
),
{
provider
:
state
.
provider
,
provider
,
}),
);
...
...
@@ -77,16 +82,23 @@ export const fetchImport = ({ state, dispatch }, { newName, targetNamespace, rep
export
const
receiveJobsSuccess
=
({
commit
},
updatedProjects
)
=>
commit
(
types
.
RECEIVE_JOBS_SUCCESS
,
updatedProjects
);
export
const
fetchJobs
=
({
state
,
dispatch
})
=>
{
if
(
eTagPoll
)
return
;
const
{
filter
}
=
state
;
if
(
eTagPoll
)
{
stopJobsPolling
();
clearJobsEtagPoll
();
}
eTagPoll
=
new
Poll
({
resource
:
{
fetchJobs
:
()
=>
axios
.
get
(
state
.
jobsPath
),
fetchJobs
:
()
=>
axios
.
get
(
jobsPathWithFilter
(
state
)
),
},
method
:
'
fetchJobs
'
,
successCallback
:
({
data
})
=>
dispatch
(
'
receiveJobsSuccess
'
,
convertObjectPropsToCamelCase
(
data
,
{
deep
:
true
})),
errorCallback
:
()
=>
createFlash
(
s__
(
'
ImportProjects|Updating the imported projects failed
'
)),
errorCallback
:
()
=>
createFlash
(
s__
(
'
ImportProjects|Update of imported projects with realtime changes failed
'
)),
data
:
{
filter
},
});
if
(
!
Visibility
.
hidden
())
{
...
...
app/assets/javascripts/import_projects/store/getters.js
View file @
4584eb0e
...
...
@@ -20,3 +20,8 @@ export const isImportingAnyRepo = state => state.reposBeingImported.length > 0;
export
const
hasProviderRepos
=
state
=>
state
.
providerRepos
.
length
>
0
;
export
const
hasImportedProjects
=
state
=>
state
.
importedProjects
.
length
>
0
;
export
const
reposPathWithFilter
=
({
reposPath
,
filter
=
''
})
=>
filter
?
`
${
reposPath
}
?filter=
${
filter
}
`
:
reposPath
;
export
const
jobsPathWithFilter
=
({
jobsPath
,
filter
=
''
})
=>
filter
?
`
${
jobsPath
}
?filter=
${
filter
}
`
:
jobsPath
;
app/assets/javascripts/import_projects/store/mutation_types.js
View file @
4584eb0e
...
...
@@ -9,3 +9,5 @@ export const RECEIVE_IMPORT_SUCCESS = 'RECEIVE_IMPORT_SUCCESS';
export
const
RECEIVE_IMPORT_ERROR
=
'
RECEIVE_IMPORT_ERROR
'
;
export
const
RECEIVE_JOBS_SUCCESS
=
'
RECEIVE_JOBS_SUCCESS
'
;
export
const
SET_FILTER
=
'
SET_FILTER
'
;
app/assets/javascripts/import_projects/store/mutations.js
View file @
4584eb0e
...
...
@@ -6,6 +6,10 @@ export default {
Object
.
assign
(
state
,
data
);
},
[
types
.
SET_FILTER
](
state
,
filter
)
{
state
.
filter
=
filter
;
},
[
types
.
REQUEST_REPOS
](
state
)
{
state
.
isLoadingRepos
=
true
;
},
...
...
app/assets/javascripts/import_projects/store/state.js
View file @
4584eb0e
...
...
@@ -12,4 +12,5 @@ export default () => ({
isLoadingRepos
:
false
,
canSelectNamespace
:
false
,
ciCdOnly
:
false
,
filter
:
''
,
});
app/controllers/import/github_controller.rb
View file @
4584eb0e
...
...
@@ -2,6 +2,7 @@
class
Import::GithubController
<
Import
::
BaseController
include
ImportHelper
include
ActionView
::
Helpers
::
SanitizeHelper
before_action
:verify_import_enabled
before_action
:provider_auth
,
only:
[
:status
,
:realtime_changes
,
:create
]
...
...
@@ -55,7 +56,7 @@ class Import::GithubController < Import::BaseController
def
realtime_changes
Gitlab
::
PollingInterval
.
set_header
(
response
,
interval:
3_000
)
render
json:
find_jobs
(
provider
)
render
json:
already_added_projects
.
to_json
(
only:
[
:id
],
methods:
[
:import_status
]
)
end
private
...
...
@@ -82,7 +83,7 @@ class Import::GithubController < Import::BaseController
end
def
already_added_projects
@already_added_projects
||=
fi
nd_already_added_projects
(
provider
)
@already_added_projects
||=
fi
ltered
(
find_already_added_projects
(
provider
)
)
end
def
already_added_project_names
...
...
@@ -104,7 +105,7 @@ class Import::GithubController < Import::BaseController
end
def
client_repos
@client_repos
||=
client
.
repos
@client_repos
||=
filtered
(
client
.
repos
)
end
def
verify_import_enabled
...
...
@@ -185,6 +186,20 @@ class Import::GithubController < Import::BaseController
def
extra_import_params
{}
end
def
sanitized_filter_param
@filter
||=
sanitize
(
params
[
:filter
])
end
def
filter_attribute
:name
end
def
filtered
(
collection
)
return
collection
unless
sanitized_filter_param
collection
.
select
{
|
item
|
item
[
filter_attribute
].
include?
(
sanitized_filter_param
)
}
end
end
Import
::
GithubController
.
prepend_if_ee
(
'EE::Import::GithubController'
)
changelogs/unreleased/georgekoltsov-add-github-importer-filtering.yml
0 → 100644
View file @
4584eb0e
---
title
:
Add GitHub & Gitea importers project filtering
merge_request
:
16823
author
:
type
:
added
doc/user/project/import/gitea.md
View file @
4584eb0e
...
...
@@ -66,10 +66,14 @@ From there, you can see the import statuses of your Gitea repositories.
-
whereas those that are not yet imported will have an
**Import**
button on the
right side of the table.
If you want, you can import all your Gitea projects in one go by hitting
**Import all projects**
in the upper left corner.
You also can:
![
Gitea importer page
](
img/import_projects_from_github_importer.png
)
-
Import all your Gitea projects in one go by hitting
**Import all projects**
in
the upper left corner
-
Filter projects by name. If filter is applied, hitting
**Import all projects**
will only import matched projects
![
Gitea importer page
](
img/import_projects_from_gitea_importer_v12_3.png
)
---
...
...
doc/user/project/import/github.md
View file @
4584eb0e
...
...
@@ -115,11 +115,14 @@ your GitHub repositories are listed.
1.
By default, the proposed repository namespaces match the names as they exist in GitHub, but based on your permissions,
you can choose to edit these names before you proceed to import any of them.
1.
Select the
**Import**
button next to any number of repositories, or select
**Import all repositories**
.
1.
Select the
**Import**
button next to any number of repositories, or select
**Import all repositories**
. Additionally,
you can filter projects by name. If filter is applied,
**Import all repositories**
only imports matched repositories.
1.
The
**Status**
column shows the import status of each repository. You can choose to leave the page open and it will
update in realtime or you can return to it later.
1.
Once a repository has been imported, click its GitLab path to open its GitLab URL.
![
Github importer page
](
img/import_projects_from_github_importer_v12_3.png
)
## Mirroring and pipeline status sharing
Depending your GitLab tier,
[
project mirroring
](
../../../workflow/repository_mirroring.md
)
can be set up to keep
...
...
doc/user/project/import/img/import_projects_from_gitea_importer_v12_3.png
0 → 100644
View file @
4584eb0e
49.5 KB
doc/user/project/import/img/import_projects_from_github_importer.png
deleted
100644 → 0
View file @
bdbded58
17.5 KB
doc/user/project/import/img/import_projects_from_github_importer_v12_3.png
0 → 100644
View file @
4584eb0e
52.2 KB
lib/gitlab/analytics/cycle_analytics/stage_events.rb
View file @
4584eb0e
...
...
@@ -18,7 +18,8 @@ module Gitlab
StageEvents
::
MergeRequestMerged
=>
104
,
StageEvents
::
CodeStageStart
=>
1_000
,
StageEvents
::
IssueStageEnd
=>
1_001
,
StageEvents
::
PlanStageStart
=>
1_002
StageEvents
::
PlanStageStart
=>
1_002
,
StageEvents
::
ProductionStageEnd
=>
1_003
}.
freeze
EVENTS
=
ENUM_MAPPING
.
keys
.
freeze
...
...
@@ -32,7 +33,8 @@ module Gitlab
StageEvents
::
MergeRequestCreated
],
StageEvents
::
IssueCreated
=>
[
StageEvents
::
IssueStageEnd
StageEvents
::
IssueStageEnd
,
StageEvents
::
ProductionStageEnd
],
StageEvents
::
MergeRequestCreated
=>
[
StageEvents
::
MergeRequestMerged
...
...
lib/gitlab/analytics/cycle_analytics/stage_events/code_stage_start.rb
View file @
4584eb0e
...
...
@@ -16,6 +16,21 @@ module Gitlab
def
object_type
MergeRequest
end
def
timestamp_projection
issue_metrics_table
[
:first_mentioned_in_commit_at
]
end
# rubocop: disable CodeReuse/ActiveRecord
def
apply_query_customization
(
query
)
issue_metrics_join
=
mr_closing_issues_table
.
join
(
issue_metrics_table
)
.
on
(
mr_closing_issues_table
[
:issue_id
].
eq
(
issue_metrics_table
[
:issue_id
]))
.
join_sources
query
.
joins
(
:merge_requests_closing_issues
).
joins
(
issue_metrics_join
)
end
# rubocop: enable CodeReuse/ActiveRecord
end
end
end
...
...
lib/gitlab/analytics/cycle_analytics/stage_events/issue_created.rb
View file @
4584eb0e
...
...
@@ -16,6 +16,10 @@ module Gitlab
def
object_type
Issue
end
def
timestamp_projection
issue_table
[
:created_at
]
end
end
end
end
...
...
lib/gitlab/analytics/cycle_analytics/stage_events/issue_first_mentioned_in_commit.rb
View file @
4584eb0e
...
...
@@ -16,6 +16,16 @@ module Gitlab
def
object_type
Issue
end
def
timestamp_projection
issue_metrics_table
[
:first_mentioned_in_commit_at
]
end
# rubocop: disable CodeReuse/ActiveRecord
def
apply_query_customization
(
query
)
query
.
joins
(
:metrics
)
end
# rubocop: enable CodeReuse/ActiveRecord
end
end
end
...
...
lib/gitlab/analytics/cycle_analytics/stage_events/issue_stage_end.rb
View file @
4584eb0e
...
...
@@ -16,6 +16,19 @@ module Gitlab
def
object_type
Issue
end
def
timestamp_projection
Arel
::
Nodes
::
NamedFunction
.
new
(
'COALESCE'
,
[
issue_metrics_table
[
:first_associated_with_milestone_at
],
issue_metrics_table
[
:first_added_to_board_at
]
])
end
# rubocop: disable CodeReuse/ActiveRecord
def
apply_query_customization
(
query
)
query
.
joins
(
:metrics
).
where
(
issue_metrics_table
[
:first_added_to_board_at
].
not_eq
(
nil
).
or
(
issue_metrics_table
[
:first_associated_with_milestone_at
].
not_eq
(
nil
)))
end
# rubocop: enable CodeReuse/ActiveRecord
end
end
end
...
...
lib/gitlab/analytics/cycle_analytics/stage_events/merge_request_created.rb
View file @
4584eb0e
...
...
@@ -16,6 +16,10 @@ module Gitlab
def
object_type
MergeRequest
end
def
timestamp_projection
mr_table
[
:created_at
]
end
end
end
end
...
...
lib/gitlab/analytics/cycle_analytics/stage_events/merge_request_first_deployed_to_production.rb
View file @
4584eb0e
...
...
@@ -16,6 +16,16 @@ module Gitlab
def
object_type
MergeRequest
end
def
timestamp_projection
mr_metrics_table
[
:first_deployed_to_production_at
]
end
# rubocop: disable CodeReuse/ActiveRecord
def
apply_query_customization
(
query
)
query
.
joins
(
:metrics
).
where
(
timestamp_projection
.
gteq
(
mr_table
[
:created_at
]))
end
# rubocop: enable CodeReuse/ActiveRecord
end
end
end
...
...
lib/gitlab/analytics/cycle_analytics/stage_events/merge_request_last_build_finished.rb
View file @
4584eb0e
...
...
@@ -16,6 +16,16 @@ module Gitlab
def
object_type
MergeRequest
end
def
timestamp_projection
mr_metrics_table
[
:latest_build_finished_at
]
end
# rubocop: disable CodeReuse/ActiveRecord
def
apply_query_customization
(
query
)
query
.
joins
(
:metrics
)
end
# rubocop: enable CodeReuse/ActiveRecord
end
end
end
...
...
lib/gitlab/analytics/cycle_analytics/stage_events/merge_request_last_build_started.rb
View file @
4584eb0e
...
...
@@ -16,6 +16,16 @@ module Gitlab
def
object_type
MergeRequest
end
def
timestamp_projection
mr_metrics_table
[
:latest_build_started_at
]
end
# rubocop: disable CodeReuse/ActiveRecord
def
apply_query_customization
(
query
)
query
.
joins
(
:metrics
)
end
# rubocop: enable CodeReuse/ActiveRecord
end
end
end
...
...
lib/gitlab/analytics/cycle_analytics/stage_events/merge_request_merged.rb
View file @
4584eb0e
...
...
@@ -16,6 +16,16 @@ module Gitlab
def
object_type
MergeRequest
end
def
timestamp_projection
mr_metrics_table
[
:merged_at
]
end
# rubocop: disable CodeReuse/ActiveRecord
def
apply_query_customization
(
query
)
query
.
joins
(
:metrics
)
end
# rubocop: enable CodeReuse/ActiveRecord
end
end
end
...
...
lib/gitlab/analytics/cycle_analytics/stage_events/plan_stage_start.rb
View file @
4584eb0e
...
...
@@ -16,6 +16,22 @@ module Gitlab
def
object_type
Issue
end
def
timestamp_projection
Arel
::
Nodes
::
NamedFunction
.
new
(
'COALESCE'
,
[
issue_metrics_table
[
:first_associated_with_milestone_at
],
issue_metrics_table
[
:first_added_to_board_at
]
])
end
# rubocop: disable CodeReuse/ActiveRecord
def
apply_query_customization
(
query
)
query
.
joins
(
:metrics
)
.
where
(
issue_metrics_table
[
:first_added_to_board_at
].
not_eq
(
nil
).
or
(
issue_metrics_table
[
:first_associated_with_milestone_at
].
not_eq
(
nil
)))
.
where
(
issue_metrics_table
[
:first_mentioned_in_commit_at
].
not_eq
(
nil
))
end
# rubocop: enable CodeReuse/ActiveRecord
end
end
end
...
...
lib/gitlab/analytics/cycle_analytics/stage_events/production_stage_end.rb
0 → 100644
View file @
4584eb0e
# frozen_string_literal: true
module
Gitlab
module
Analytics
module
CycleAnalytics
module
StageEvents
class
ProductionStageEnd
<
SimpleStageEvent
def
self
.
name
PlanStageStart
.
name
end
def
self
.
identifier
:production_stage_end
end
def
object_type
Issue
end
def
timestamp_projection
mr_metrics_table
[
:first_deployed_to_production_at
]
end
# rubocop: disable CodeReuse/ActiveRecord
def
apply_query_customization
(
query
)
query
.
joins
(
merge_requests_closing_issues:
{
merge_request:
[
:metrics
]
}).
where
(
mr_metrics_table
[
:first_deployed_to_production_at
].
gteq
(
mr_table
[
:created_at
]))
end
# rubocop: enable CodeReuse/ActiveRecord
end
end
end
end
end
lib/gitlab/analytics/cycle_analytics/stage_events/stage_event.rb
View file @
4584eb0e
...
...
@@ -6,6 +6,8 @@ module Gitlab
module
StageEvents
# Base class for expressing an event that can be used for a stage.
class
StageEvent
include
Gitlab
::
CycleAnalytics
::
MetricsTables
def
initialize
(
params
)
@params
=
params
end
...
...
@@ -21,6 +23,21 @@ module Gitlab
def
object_type
raise
NotImplementedError
end
# Each StageEvent must expose a timestamp or a timestamp like expression in order to build a range query.
# Example: get me all the Issue records between start event end end event
def
timestamp_projection
raise
NotImplementedError
end
# Optionally a StageEvent may apply additional filtering or join other tables on the base query.
def
apply_query_customization
(
query
)
query
end
private
attr_reader
:params
end
end
end
...
...
locale/gitlab.pot
View file @
4584eb0e
...
...
@@ -8261,7 +8261,7 @@ msgstr ""
msgid "ImportProjects|The repository could not be created."
msgstr ""
msgid "ImportProjects|Updat
ing the imported project
s failed"
msgid "ImportProjects|Updat
e of imported projects with realtime change
s failed"
msgstr ""
msgid "Improve Issue boards"
...
...
@@ -10205,7 +10205,7 @@ msgstr ""
msgid "No %{header} for this request."
msgstr ""
msgid "No %{providerTitle} repositories
available to import
"
msgid "No %{providerTitle} repositories
found
"
msgstr ""
msgid "No Epic"
...
...
spec/frontend/import_projects/components/import_projects_table_spec.js
View file @
4584eb0e
...
...
@@ -93,7 +93,7 @@ describe('ImportProjectsTable', () => {
return
vm
.
$nextTick
().
then
(()
=>
{
expect
(
vm
.
$el
.
querySelector
(
'
.js-loading-button-icon
'
)).
toBeNull
();
expect
(
vm
.
$el
.
querySelector
(
'
.table
'
)).
toBeNull
();
expect
(
vm
.
$el
.
innerText
).
toMatch
(
`No
${
providerTitle
}
repositories
available to import
`
);
expect
(
vm
.
$el
.
innerText
).
toMatch
(
`No
${
providerTitle
}
repositories
found
`
);
});
});
...
...
@@ -182,4 +182,10 @@ describe('ImportProjectsTable', () => {
expect
(
vm
.
$el
.
querySelector
(
`.ic-status_
${
statusObject
.
icon
}
`
)).
not
.
toBeNull
();
});
});
it
(
'
renders filtering input field
'
,
()
=>
{
expect
(
vm
.
$el
.
querySelector
(
'
input[data-qa-selector="githubish_import_filter_field"]
'
),
).
not
.
toBeNull
();
});
});
spec/frontend/import_projects/store/actions_spec.js
View file @
4584eb0e
...
...
@@ -97,6 +97,7 @@ describe('import_projects store actions', () => {
describe
(
'
fetchRepos
'
,
()
=>
{
let
mock
;
const
payload
=
{
imported_projects
:
[{}],
provider_repos
:
[{}],
namespaces
:
[{}]
};
beforeEach
(()
=>
{
localState
.
reposPath
=
`
${
TEST_HOST
}
/endpoint.json`
;
...
...
@@ -105,8 +106,7 @@ describe('import_projects store actions', () => {
afterEach
(()
=>
mock
.
restore
());
it
(
'
dispatches requestRepos and receiveReposSuccess actions on a successful request
'
,
done
=>
{
const
payload
=
{
imported_projects
:
[{}],
provider_repos
:
[{}],
namespaces
:
[{}]
};
it
(
'
dispatches stopJobsPolling, requestRepos and receiveReposSuccess actions on a successful request
'
,
done
=>
{
mock
.
onGet
(
`
${
TEST_HOST
}
/endpoint.json`
).
reply
(
200
,
payload
);
testAction
(
...
...
@@ -115,6 +115,7 @@ describe('import_projects store actions', () => {
localState
,
[],
[
{
type
:
'
stopJobsPolling
'
},
{
type
:
'
requestRepos
'
},
{
type
:
'
receiveReposSuccess
'
,
...
...
@@ -128,7 +129,7 @@ describe('import_projects store actions', () => {
);
});
it
(
'
dispatches
requestRepos and receiveReposSuccess
actions on an unsuccessful request
'
,
done
=>
{
it
(
'
dispatches
stopJobsPolling, requestRepos and receiveReposError
actions on an unsuccessful request
'
,
done
=>
{
mock
.
onGet
(
`
${
TEST_HOST
}
/endpoint.json`
).
reply
(
500
);
testAction
(
...
...
@@ -136,10 +137,39 @@ describe('import_projects store actions', () => {
null
,
localState
,
[],
[{
type
:
'
requestRepos
'
},
{
type
:
'
receiveReposError
'
}],
[{
type
:
'
stopJobsPolling
'
},
{
type
:
'
requestRepos
'
},
{
type
:
'
receiveReposError
'
}],
done
,
);
});
describe
(
'
when filtered
'
,
()
=>
{
beforeEach
(()
=>
{
localState
.
filter
=
'
filter
'
;
});
it
(
'
fetches repos with filter applied
'
,
done
=>
{
mock
.
onGet
(
`
${
TEST_HOST
}
/endpoint.json?filter=filter`
).
reply
(
200
,
payload
);
testAction
(
fetchRepos
,
null
,
localState
,
[],
[
{
type
:
'
stopJobsPolling
'
},
{
type
:
'
requestRepos
'
},
{
type
:
'
receiveReposSuccess
'
,
payload
:
convertObjectPropsToCamelCase
(
payload
,
{
deep
:
true
}),
},
{
type
:
'
fetchJobs
'
,
},
],
done
,
);
});
});
});
describe
(
'
requestImport
'
,
()
=>
{
...
...
@@ -249,6 +279,7 @@ describe('import_projects store actions', () => {
describe
(
'
fetchJobs
'
,
()
=>
{
let
mock
;
const
updatedProjects
=
[{
name
:
'
imported/project
'
},
{
name
:
'
provider/repo
'
}];
beforeEach
(()
=>
{
localState
.
jobsPath
=
`
${
TEST_HOST
}
/endpoint.json`
;
...
...
@@ -263,7 +294,6 @@ describe('import_projects store actions', () => {
afterEach
(()
=>
mock
.
restore
());
it
(
'
dispatches requestJobs and receiveJobsSuccess actions on a successful request
'
,
done
=>
{
const
updatedProjects
=
[{
name
:
'
imported/project
'
},
{
name
:
'
provider/repo
'
}];
mock
.
onGet
(
`
${
TEST_HOST
}
/endpoint.json`
).
reply
(
200
,
updatedProjects
);
testAction
(
...
...
@@ -280,5 +310,29 @@ describe('import_projects store actions', () => {
done
,
);
});
describe
(
'
when filtered
'
,
()
=>
{
beforeEach
(()
=>
{
localState
.
filter
=
'
filter
'
;
});
it
(
'
fetches realtime changes with filter applied
'
,
done
=>
{
mock
.
onGet
(
`
${
TEST_HOST
}
/endpoint.json?filter=filter`
).
reply
(
200
,
updatedProjects
);
testAction
(
fetchJobs
,
null
,
localState
,
[],
[
{
type
:
'
receiveJobsSuccess
'
,
payload
:
convertObjectPropsToCamelCase
(
updatedProjects
,
{
deep
:
true
}),
},
],
done
,
);
});
});
});
});
spec/lib/gitlab/analytics/cycle_analytics/stage_events/code_stage_start_spec.rb
0 → 100644
View file @
4584eb0e
# frozen_string_literal: true
require
'spec_helper'
describe
Gitlab
::
Analytics
::
CycleAnalytics
::
StageEvents
::
CodeStageStart
do
let
(
:subject
)
{
described_class
.
new
({})
}
let
(
:project
)
{
create
(
:project
)
}
it_behaves_like
'cycle analytics event'
it
'needs connection with an issue via merge_requests_closing_issues table'
do
issue
=
create
(
:issue
,
project:
project
)
merge_request
=
create
(
:merge_request
,
source_project:
project
)
create
(
:merge_requests_closing_issues
,
issue:
issue
,
merge_request:
merge_request
)
other_merge_request
=
create
(
:merge_request
,
source_project:
project
,
source_branch:
'a'
,
target_branch:
'master'
)
records
=
subject
.
apply_query_customization
(
MergeRequest
.
all
)
expect
(
records
).
to
eq
([
merge_request
])
expect
(
records
).
not_to
include
(
other_merge_request
)
end
end
spec/lib/gitlab/analytics/cycle_analytics/stage_events/issue_created_spec.rb
0 → 100644
View file @
4584eb0e
# frozen_string_literal: true
require
'spec_helper'
describe
Gitlab
::
Analytics
::
CycleAnalytics
::
StageEvents
::
IssueCreated
do
it_behaves_like
'cycle analytics event'
end
spec/lib/gitlab/analytics/cycle_analytics/stage_events/issue_first_mentioned_in_commit_spec.rb
0 → 100644
View file @
4584eb0e
# frozen_string_literal: true
require
'spec_helper'
describe
Gitlab
::
Analytics
::
CycleAnalytics
::
StageEvents
::
IssueFirstMentionedInCommit
do
it_behaves_like
'cycle analytics event'
end
spec/lib/gitlab/analytics/cycle_analytics/stage_events/issue_stage_end_spec.rb
0 → 100644
View file @
4584eb0e
# frozen_string_literal: true
require
'spec_helper'
describe
Gitlab
::
Analytics
::
CycleAnalytics
::
StageEvents
::
IssueStageEnd
do
it_behaves_like
'cycle analytics event'
end
spec/lib/gitlab/analytics/cycle_analytics/stage_events/merge_request_created_spec.rb
0 → 100644
View file @
4584eb0e
# frozen_string_literal: true
require
'spec_helper'
describe
Gitlab
::
Analytics
::
CycleAnalytics
::
StageEvents
::
MergeRequestCreated
do
it_behaves_like
'cycle analytics event'
end
spec/lib/gitlab/analytics/cycle_analytics/stage_events/merge_request_first_deployed_to_production_spec.rb
0 → 100644
View file @
4584eb0e
# frozen_string_literal: true
require
'spec_helper'
describe
Gitlab
::
Analytics
::
CycleAnalytics
::
StageEvents
::
MergeRequestFirstDeployedToProduction
do
it_behaves_like
'cycle analytics event'
end
spec/lib/gitlab/analytics/cycle_analytics/stage_events/merge_request_last_build_finished_spec.rb
0 → 100644
View file @
4584eb0e
# frozen_string_literal: true
require
'spec_helper'
describe
Gitlab
::
Analytics
::
CycleAnalytics
::
StageEvents
::
MergeRequestLastBuildFinished
do
it_behaves_like
'cycle analytics event'
end
spec/lib/gitlab/analytics/cycle_analytics/stage_events/merge_request_last_build_started_spec.rb
0 → 100644
View file @
4584eb0e
# frozen_string_literal: true
require
'spec_helper'
describe
Gitlab
::
Analytics
::
CycleAnalytics
::
StageEvents
::
MergeRequestLastBuildStarted
do
it_behaves_like
'cycle analytics event'
end
spec/lib/gitlab/analytics/cycle_analytics/stage_events/merge_request_merged_spec.rb
0 → 100644
View file @
4584eb0e
# frozen_string_literal: true
require
'spec_helper'
describe
Gitlab
::
Analytics
::
CycleAnalytics
::
StageEvents
::
MergeRequestMerged
do
it_behaves_like
'cycle analytics event'
end
spec/lib/gitlab/analytics/cycle_analytics/stage_events/plan_stage_start_spec.rb
0 → 100644
View file @
4584eb0e
# frozen_string_literal: true
require
'spec_helper'
describe
Gitlab
::
Analytics
::
CycleAnalytics
::
StageEvents
::
PlanStageStart
do
let
(
:subject
)
{
described_class
.
new
({})
}
let
(
:project
)
{
create
(
:project
)
}
it_behaves_like
'cycle analytics event'
it
'filters issues where first_associated_with_milestone_at or first_added_to_board_at is filled'
do
issue1
=
create
(
:issue
,
project:
project
)
issue1
.
metrics
.
update!
(
first_added_to_board_at:
1
.
month
.
ago
,
first_mentioned_in_commit_at:
2
.
months
.
ago
)
issue2
=
create
(
:issue
,
project:
project
)
issue2
.
metrics
.
update!
(
first_associated_with_milestone_at:
1
.
month
.
ago
,
first_mentioned_in_commit_at:
2
.
months
.
ago
)
issue_without_metrics
=
create
(
:issue
,
project:
project
)
records
=
subject
.
apply_query_customization
(
Issue
.
all
)
expect
(
records
).
to
match_array
([
issue1
,
issue2
])
expect
(
records
).
not_to
include
(
issue_without_metrics
)
end
end
spec/lib/gitlab/analytics/cycle_analytics/stage_events/stage_event_spec.rb
View file @
4584eb0e
...
...
@@ -3,8 +3,11 @@
require
'spec_helper'
describe
Gitlab
::
Analytics
::
CycleAnalytics
::
StageEvents
::
StageEvent
do
let
(
:instance
)
{
described_class
.
new
({})
}
it
{
expect
(
described_class
).
to
respond_to
(
:name
)
}
it
{
expect
(
described_class
).
to
respond_to
(
:identifier
)
}
it
{
expect
(
described_class
.
new
({})).
to
respond_to
(
:object_type
)
}
it
{
expect
(
instance
).
to
respond_to
(
:object_type
)
}
it
{
expect
(
instance
).
to
respond_to
(
:timestamp_projection
)
}
it
{
expect
(
instance
).
to
respond_to
(
:apply_query_customization
)
}
end
spec/support/controllers/githubish_import_controller_shared_examples.rb
View file @
4584eb0e
...
...
@@ -139,6 +139,38 @@ shared_examples 'a GitHub-ish import controller: GET status' do
expect
{
get
:status
,
format: :json
}
.
not_to
exceed_all_query_limit
(
control_count
)
end
context
'when filtering'
do
let
(
:repo_2
)
{
OpenStruct
.
new
(
login:
'emacs'
,
full_name:
'asd/emacs'
,
name:
'emacs'
,
owner:
{
login:
'owner'
})
}
let
(
:project
)
{
create
(
:project
,
import_type:
provider
,
namespace:
user
.
namespace
,
import_status: :finished
,
import_source:
'example/repo'
)
}
let
(
:group
)
{
create
(
:group
)
}
before
do
group
.
add_owner
(
user
)
stub_client
(
repos:
[
repo
,
repo_2
,
org_repo
],
orgs:
[
org
],
org_repos:
[
org_repo
])
end
it
'filters list of repositories by name'
do
get
:status
,
params:
{
filter:
'emacs'
},
format: :json
expect
(
response
).
to
have_gitlab_http_status
(
200
)
expect
(
json_response
.
dig
(
"imported_projects"
).
count
).
to
eq
(
0
)
expect
(
json_response
.
dig
(
"provider_repos"
).
count
).
to
eq
(
1
)
expect
(
json_response
.
dig
(
"provider_repos"
,
0
,
"id"
)).
to
eq
(
repo_2
.
id
)
expect
(
json_response
.
dig
(
"namespaces"
,
0
,
"id"
)).
to
eq
(
group
.
id
)
end
context
'when user input contains html'
do
let
(
:expected_filter
)
{
'test'
}
let
(
:filter
)
{
"<html>
#{
expected_filter
}
</html>"
}
it
'sanitizes user input'
do
get
:status
,
params:
{
filter:
filter
},
format: :json
expect
(
assigns
(
:filter
)).
to
eq
(
expected_filter
)
end
end
end
end
shared_examples
'a GitHub-ish import controller: POST create'
do
...
...
spec/support/shared_examples/cycle_analytics_event_shared_examples.rb
0 → 100644
View file @
4584eb0e
# frozen_string_literal: true
shared_examples_for
'cycle analytics event'
do
let
(
:instance
)
{
described_class
.
new
({})
}
it
{
expect
(
described_class
.
name
).
to
be_a_kind_of
(
String
)
}
it
{
expect
(
described_class
.
identifier
).
to
be_a_kind_of
(
Symbol
)
}
it
{
expect
(
instance
.
object_type
.
ancestors
).
to
include
(
ApplicationRecord
)
}
it
{
expect
(
instance
).
to
respond_to
(
:timestamp_projection
)
}
describe
'#apply_query_customization'
do
it
'expects an ActiveRecord::Relation object as argument and returns a modified version of it'
do
input_query
=
instance
.
object_type
.
all
output_query
=
instance
.
apply_query_customization
(
input_query
)
expect
(
output_query
).
to
be_a_kind_of
(
ActiveRecord
::
Relation
)
end
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