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
df9f198f
Commit
df9f198f
authored
Jul 07, 2021
by
GitLab Bot
Browse files
Options
Browse Files
Download
Plain Diff
Automatic merge of gitlab-org/gitlab master
parents
152741c4
f82da155
Changes
16
Hide whitespace changes
Inline
Side-by-side
Showing
16 changed files
with
274 additions
and
55 deletions
+274
-55
app/graphql/types/query_complexity_type.rb
app/graphql/types/query_complexity_type.rb
+30
-0
app/graphql/types/query_type.rb
app/graphql/types/query_type.rb
+8
-0
app/models/integrations/jira.rb
app/models/integrations/jira.rb
+1
-1
app/models/wiki.rb
app/models/wiki.rb
+0
-2
config/feature_flags/development/wiki_uses_default_branch.yml
...ig/feature_flags/development/wiki_uses_default_branch.yml
+0
-8
doc/api/graphql/getting_started.md
doc/api/graphql/getting_started.md
+18
-0
doc/api/graphql/index.md
doc/api/graphql/index.md
+1
-1
doc/api/graphql/reference/index.md
doc/api/graphql/reference/index.md
+15
-0
doc/development/api_graphql_styleguide.md
doc/development/api_graphql_styleguide.md
+1
-3
ee/app/assets/javascripts/roadmap/mixins/filtered_search_mixin.js
...ssets/javascripts/roadmap/mixins/filtered_search_mixin.js
+1
-1
ee/spec/features/epics/epics_list_spec.rb
ee/spec/features/epics/epics_list_spec.rb
+53
-25
ee/spec/features/groups/group_roadmap_spec.rb
ee/spec/features/groups/group_roadmap_spec.rb
+41
-2
ee/spec/support/shared_examples/features/epics_filtered_search_shared_examples.rb
...xamples/features/epics_filtered_search_shared_examples.rb
+40
-0
spec/graphql/types/query_complexity_type_spec.rb
spec/graphql/types/query_complexity_type_spec.rb
+35
-0
spec/models/integrations/jira_spec.rb
spec/models/integrations/jira_spec.rb
+30
-0
spec/support/shared_examples/models/wiki_shared_examples.rb
spec/support/shared_examples/models/wiki_shared_examples.rb
+0
-12
No files found.
app/graphql/types/query_complexity_type.rb
0 → 100644
View file @
df9f198f
# frozen_string_literal: true
module
Types
# rubocop: disable Graphql/AuthorizeTypes
class
QueryComplexityType
<
::
Types
::
BaseObject
ANALYZER
=
GraphQL
::
Analysis
::
QueryComplexity
.
new
{
|
_query
,
complexity
|
complexity
}
graphql_name
'QueryComplexity'
alias_method
:query
,
:object
field
:limit
,
GraphQL
::
INT_TYPE
,
null:
true
,
method: :max_complexity
,
see:
{
'GitLab documentation on this limit'
=>
'https://docs.gitlab.com/ee/api/graphql/index.html#max-query-complexity'
},
description:
'GraphQL query complexity limit.'
field
:score
,
GraphQL
::
INT_TYPE
,
null:
true
,
description:
'GraphQL query complexity score.'
def
score
::
GraphQL
::
Analysis
.
analyze_query
(
query
,
[
ANALYZER
]).
first
end
end
# rubocop: enable Graphql/AuthorizeTypes
end
app/graphql/types/query_type.rb
View file @
df9f198f
...
...
@@ -36,6 +36,10 @@ module Types
resolver:
Resolvers
::
MetadataResolver
,
description:
'Metadata about GitLab.'
field
:query_complexity
,
Types
::
QueryComplexityType
,
null:
true
,
description:
'Information about the complexity of the GraphQL query.'
field
:snippets
,
Types
::
SnippetType
.
connection_type
,
null:
true
,
...
...
@@ -170,6 +174,10 @@ module Types
def
application_settings
Gitlab
::
CurrentSettings
.
current_application_settings
end
def
query_complexity
context
.
query
end
end
end
...
...
app/models/integrations/jira.rb
View file @
df9f198f
...
...
@@ -155,7 +155,7 @@ module Integrations
end
def
web_url
(
path
=
nil
,
**
params
)
return
unless
url
.
present?
return
''
unless
url
.
present?
if
Gitlab
.
com?
params
.
merge!
(
ATLASSIAN_REFERRER_GITLAB_COM
)
unless
Gitlab
.
staging?
...
...
app/models/wiki.rb
View file @
df9f198f
...
...
@@ -249,8 +249,6 @@ class Wiki
override
:default_branch
def
default_branch
return
'master'
if
Feature
.
disabled?
(
:wiki_uses_default_branch
,
user
,
default_enabled: :yaml
)
super
||
wiki
.
class
.
default_ref
(
container
)
end
...
...
config/feature_flags/development/wiki_uses_default_branch.yml
deleted
100644 → 0
View file @
152741c4
---
name
:
wiki_uses_default_branch
introduced_by_url
:
https://gitlab.com/gitlab-org/gitlab/-/merge_requests/64891
rollout_issue_url
:
https://gitlab.com/gitlab-org/gitlab/-/issues/334754
milestone
:
'
14.1'
type
:
development
group
:
group::editor
default_enabled
:
false
doc/api/graphql/getting_started.md
View file @
df9f198f
...
...
@@ -298,6 +298,24 @@ query IssueTypes {
More about introspection:
[
GraphQL documentation
](
https://graphql.org/learn/introspection/
)
### Query complexity
The calculated
[
complexity score and limit
](
index.md#max-query-complexity
)
for a query can be revealed to clients by
querying for
`queryComplexity`
.
```
graphql
query
{
queryComplexity
{
score
limit
}
project
(
fullPath
:
"gitlab-org/graphql-sandbox"
)
{
name
}
}
```
## Sorting
Some of the GitLab GraphQL endpoints allow you to specify how to sort a
...
...
doc/api/graphql/index.md
View file @
df9f198f
...
...
@@ -166,7 +166,7 @@ The complexity of a single query is limited to a maximum of:
-
`200`
for unauthenticated requests.
-
`250`
for authenticated requests.
The
re is no way to discover the complexity of a query except by exceeding the limit
.
The
complexity score of a query and limit for the request
[
can be queried for
](
getting_started.md#query-complexity
)
.
If a query exceeds the complexity limit an error message response will
be returned.
...
...
doc/api/graphql/reference/index.md
View file @
df9f198f
...
...
@@ -286,6 +286,12 @@ four standard [pagination arguments](#connection-pagination-arguments):
|
<a
id=
"queryprojectssort"
></a>
`sort`
|
[
`String`
](
#string
)
| Sort order of results. |
|
<a
id=
"queryprojectstopics"
></a>
`topics`
|
[
`[String!]`
](
#string
)
| Filters projects by topics. |
### `Query.queryComplexity`
Information about the complexity of the GraphQL query.
Returns
[
`QueryComplexity`
](
#querycomplexity
)
.
### `Query.runner`
Find a runner. Available only when feature flag
`runner_graphql_query`
is enabled.
...
...
@@ -12335,6 +12341,15 @@ Pypi metadata.
|
<a
id=
"pypimetadataid"
></a>
`id`
|
[
`PackagesPypiMetadatumID!`
](
#packagespypimetadatumid
)
| ID of the metadatum. |
|
<a
id=
"pypimetadatarequiredpython"
></a>
`requiredPython`
|
[
`String`
](
#string
)
| Required Python version of the Pypi package. |
### `QueryComplexity`
#### Fields
| Name | Type | Description |
| ---- | ---- | ----------- |
|
<a
id=
"querycomplexitylimit"
></a>
`limit`
|
[
`Int`
](
#int
)
| GraphQL query complexity limit. See
[
GitLab documentation on this limit
](
https://docs.gitlab.com/ee/api/graphql/index.html#max-query-complexity
)
. |
|
<a
id=
"querycomplexityscore"
></a>
`score`
|
[
`Int`
](
#int
)
| GraphQL query complexity score. |
### `RecentFailures`
Recent failure history of a test case.
...
...
doc/development/api_graphql_styleguide.md
View file @
df9f198f
...
...
@@ -68,9 +68,7 @@ Complexity is explained [on our client-facing API page](../api/graphql/index.md#
Fields default to adding
`1`
to a query's complexity score, but developers can
[
specify a custom complexity
](
#field-complexity
)
when defining a field.
To estimate the complexity of a query, you can run the
[
`gitlab:graphql:analyze`
](
rake_tasks.md#analyze-graphql-queries
)
Rake task.
The complexity score of a query
[
can itself be queried for
](
../api/graphql/getting_started.md#query-complexity
)
.
### Request timeout
...
...
ee/app/assets/javascripts/roadmap/mixins/filtered_search_mixin.js
View file @
df9f198f
...
...
@@ -101,7 +101,7 @@ export default {
params
.
search
=
search
;
}
return
Api
.
groupLabels
(
this
.
groupFullPath
,
{
return
Api
.
groupLabels
(
encodeURIComponent
(
this
.
groupFullPath
)
,
{
params
,
});
},
...
...
ee/spec/features/epics/epics_list_spec.rb
View file @
df9f198f
...
...
@@ -5,6 +5,9 @@ require 'spec_helper'
RSpec
.
describe
'epics list'
,
:js
do
let
(
:group
)
{
create
(
:group
,
:public
)
}
let
(
:user
)
{
create
(
:user
)
}
let
(
:user_dev
)
{
create
(
:user
)
}
let!
(
:bug_label
)
{
create
(
:group_label
,
group:
group
,
title:
'Bug'
)
}
let!
(
:critical_label
)
{
create
(
:group_label
,
group:
group
,
title:
'Critical'
)
}
before
do
stub_licensed_features
(
epics:
true
)
...
...
@@ -215,43 +218,68 @@ RSpec.describe 'epics list', :js do
end
context
'vue epics list'
do
let!
(
:epic1
)
{
create
(
:epic
,
group:
group
,
start_date:
'2020-12-15'
,
end_date:
'2021-1-15'
)
}
let!
(
:epic2
)
{
create
(
:epic
,
group:
group
,
start_date:
'2020-12-15'
)
}
let!
(
:epic3
)
{
create
(
:epic
,
group:
group
,
end_date:
'2021-1-15'
)
}
available_tokens
=
%w[Author Label My-Reaction]
before
do
stub_feature_flags
(
vue_epics_list:
true
)
group
.
add_developer
(
user
)
end
visit
group_epics_path
(
group
)
describe
'within a group'
do
let!
(
:epic1
)
{
create
(
:epic
,
group:
group
,
start_date:
'2020-12-15'
,
end_date:
'2021-1-15'
)
}
let!
(
:epic2
)
{
create
(
:epic
,
group:
group
,
start_date:
'2020-12-15'
)
}
let!
(
:epic3
)
{
create
(
:epic
,
group:
group
,
end_date:
'2021-1-15'
)
}
let!
(
:award_emoji_star
)
{
create
(
:award_emoji
,
name:
'star'
,
user:
user
,
awardable:
epic1
)
}
before
do
group
.
add_developer
(
user
)
group
.
add_developer
(
user_dev
)
visit
group_epics_path
(
group
)
wait_for_requests
end
wait_for_requests
end
it
'renders epics list'
,
:aggregate_failures
do
page
.
within
(
'.issuable-list-container'
)
do
expect
(
page
).
to
have_selector
(
'.gl-tabs'
)
expect
(
page
).
to
have_link
(
'New epic'
)
expect
(
page
).
to
have_selector
(
'.vue-filtered-search-bar-container'
)
expect
(
page
.
find
(
'.issuable-list'
)).
to
have_selector
(
'li.issue'
,
count:
3
)
end
end
it
'renders epics list'
do
page
.
within
(
'.issuable-list-container'
)
do
expect
(
page
).
to
have_selector
(
'.gl-tabs'
)
expect
(
page
).
to
have_link
(
'New epic'
)
expect
(
page
).
to
have_selector
(
'.vue-filtered-search-bar-container'
)
expect
(
page
.
find
(
'.issuable-list'
)).
to
have_selector
(
'li.issue'
,
count:
3
)
it
'renders epics item with metadata'
,
:aggregate_failures
do
page
.
within
(
'.issuable-list-container .issuable-list'
)
do
expect
(
page
.
all
(
'.issuable-info-container'
)[
0
].
find
(
'.issue-title'
)).
to
have_content
(
epic2
.
title
)
expect
(
page
.
all
(
'.issuable-info-container'
)[
0
].
find
(
'.issuable-reference'
)).
to
have_content
(
"&
#{
epic2
.
iid
}
"
)
expect
(
page
.
all
(
'.issuable-info-container'
)[
0
].
find
(
'.issuable-authored'
)).
to
have_content
(
'created'
)
expect
(
page
.
all
(
'.issuable-info-container'
)[
0
].
find
(
'.issuable-authored'
)).
to
have_content
(
"by
#{
epic2
.
author
.
name
}
"
)
end
end
end
it
'renders epics item with metadata'
do
page
.
within
(
'.issuable-list-container .issuable-list'
)
do
expect
(
page
.
all
(
'.issuable-info-container'
)[
0
].
find
(
'.issue-title'
)).
to
have_content
(
epic2
.
title
)
expect
(
page
.
all
(
'.issuable-info-container'
)[
0
].
find
(
'.issuable-reference'
)).
to
have_content
(
"&
#{
epic2
.
iid
}
"
)
expect
(
page
.
all
(
'.issuable-info-container'
)[
0
].
find
(
'.issuable-authored'
)).
to
have_content
(
'created
'
)
e
xpect
(
page
.
all
(
'.issuable-info-container'
)[
0
].
find
(
'.issuable-authored'
)).
to
have_content
(
"by
#{
epic2
.
author
.
name
}
"
)
it
'renders epic item timeframe'
,
:aggregate_failures
do
page
.
within
(
'.issuable-list-container .issuable-list'
)
do
expect
(
page
.
all
(
'.issuable-info-container'
)[
0
].
find
(
'.issuable-info'
)).
to
have_content
(
'Dec 15, 2020 – No due date'
)
expect
(
page
.
all
(
'.issuable-info-container'
)[
1
].
find
(
'.issuable-info'
)).
to
have_content
(
'Dec 15, 2020 – Jan 15, 2021'
)
expect
(
page
.
all
(
'.issuable-info-container'
)[
2
].
find
(
'.issuable-info'
)).
to
have_content
(
'No start date – Jan 15, 2021
'
)
e
nd
end
it_behaves_like
'filtered search bar'
,
available_tokens
end
it
'renders epic item timeframe'
do
page
.
within
(
'.issuable-list-container .issuable-list'
)
do
expect
(
page
.
all
(
'.issuable-info-container'
)[
0
].
find
(
'.issuable-info'
)).
to
have_content
(
'Dec 15, 2020 – No due date'
)
expect
(
page
.
all
(
'.issuable-info-container'
)[
1
].
find
(
'.issuable-info'
)).
to
have_content
(
'Dec 15, 2020 – Jan 15, 2021'
)
expect
(
page
.
all
(
'.issuable-info-container'
)[
2
].
find
(
'.issuable-info'
)).
to
have_content
(
'No start date – Jan 15, 2021'
)
describe
'within a sub-group group'
do
let!
(
:subgroup
)
{
create
(
:group
,
parent:
group
,
name:
'subgroup'
)
}
let!
(
:sub_epic1
)
{
create
(
:epic
,
group:
subgroup
,
start_date:
'2020-12-15'
,
end_date:
'2021-1-15'
)
}
let!
(
:sub_epic2
)
{
create
(
:epic
,
group:
subgroup
,
start_date:
'2020-12-15'
)
}
let!
(
:award_emoji_star
)
{
create
(
:award_emoji
,
name:
'star'
,
user:
user
,
awardable:
sub_epic1
)
}
before
do
subgroup
.
add_developer
(
user
)
subgroup
.
add_developer
(
user_dev
)
visit
group_epics_path
(
subgroup
)
wait_for_requests
end
it_behaves_like
'filtered search bar'
,
available_tokens
end
end
end
ee/spec/features/groups/group_roadmap_spec.rb
View file @
df9f198f
...
...
@@ -7,14 +7,16 @@ RSpec.describe 'group epic roadmap', :js do
include
MobileHelpers
let
(
:user
)
{
create
(
:user
)
}
let
(
:user_dev
)
{
create
(
:user
)
}
let
(
:group
)
{
create
(
:group
)
}
let
(
:milestone
)
{
create
(
:milestone
,
group:
group
)
}
let
(
:filtered_search
)
{
find
(
'.filtered-search'
)
}
let
(
:js_dropdown_label
)
{
'#js-dropdown-label'
}
let
(
:filter_dropdown
)
{
find
(
"
#{
js_dropdown_label
}
.filter-dropdown"
)
}
let
(
:state_dropdown
)
{
find
(
'.dropdown-epics-state'
)
}
let
(
:bug_label
)
{
create
(
:group_label
,
group:
group
,
title:
'Bug'
)
}
let
(
:critical_label
)
{
create
(
:group_label
,
group:
group
,
title:
'Critical'
)
}
let
!
(
:bug_label
)
{
create
(
:group_label
,
group:
group
,
title:
'Bug'
)
}
let
!
(
:critical_label
)
{
create
(
:group_label
,
group:
group
,
title:
'Critical'
)
}
def
search_for_label
(
label
)
init_label_search
...
...
@@ -190,4 +192,41 @@ RSpec.describe 'group epic roadmap', :js do
end
end
end
context
'async filtered search'
do
available_tokens
=
%w[Author Label Milestone Epic My-Reaction]
before
do
stub_feature_flags
(
async_filtering:
true
)
end
describe
'within a group'
do
let!
(
:epic1
)
{
create
(
:epic
,
group:
group
,
end_date:
10
.
days
.
ago
)
}
let!
(
:epic2
)
{
create
(
:epic
,
group:
group
,
start_date:
2
.
days
.
ago
)
}
let!
(
:award_emoji_star
)
{
create
(
:award_emoji
,
name:
'star'
,
user:
user
,
awardable:
epic1
)
}
before
do
group
.
add_developer
(
user_dev
)
visit
group_roadmap_path
(
group
)
wait_for_requests
end
it_behaves_like
'filtered search bar'
,
available_tokens
end
describe
'within a sub-group group'
do
let!
(
:subgroup
)
{
create
(
:group
,
parent:
group
,
name:
'subgroup'
)
}
let!
(
:sub_epic1
)
{
create
(
:epic
,
group:
subgroup
,
end_date:
10
.
days
.
ago
)
}
let!
(
:sub_epic2
)
{
create
(
:epic
,
group:
subgroup
,
start_date:
2
.
days
.
ago
)
}
let!
(
:award_emoji_star
)
{
create
(
:award_emoji
,
name:
'star'
,
user:
user
,
awardable:
sub_epic1
)
}
before
do
subgroup
.
add_developer
(
user_dev
)
visit
group_roadmap_path
(
subgroup
)
wait_for_requests
end
it_behaves_like
'filtered search bar'
,
available_tokens
end
end
end
ee/spec/support/shared_examples/features/epics_filtered_search_shared_examples.rb
0 → 100644
View file @
df9f198f
# frozen_string_literal: true
require
'spec_helper'
RSpec
.
shared_examples
'filtered search bar'
do
|
tokens
|
minimum_values_for_token
=
{
# Count must be at least 3 as `Any` & current user are available by default
"Author"
=>
3
,
# Count must be at least 3 as `None` & `Any` are available by default
"Label"
=>
3
,
# Count must be at least 5 as `None`, `Any`, `Upcoming` & `Started` are available by default
"Milestone"
=>
5
,
# Count must be at least 1
"Epic"
=>
1
,
# Count must be at least 3 as `None` & `Any` are available by default
"My-Reaction"
=>
3
}
def
select_token
(
token_name
)
page
.
find
(
'input.gl-filtered-search-term-input'
).
click
click_link
token_name
page
.
first
(
'.gl-filtered-search-suggestion'
).
click
end
tokens
.
each
do
|
token
|
it
"renders values for token '
#{
token
}
' correctly"
do
page
.
within
(
'.vue-filtered-search-bar-container .gl-search-box-by-click'
)
do
select_token
(
token
)
wait_for_requests
expect
(
page
.
find
(
'.gl-filtered-search-suggestion-list'
)).
to
have_selector
(
'li.gl-filtered-search-suggestion'
,
minimum:
minimum_values_for_token
[
token
])
end
end
end
end
spec/graphql/types/query_complexity_type_spec.rb
0 → 100644
View file @
df9f198f
# frozen_string_literal: true
require
'spec_helper'
RSpec
.
describe
GitlabSchema
.
types
[
'QueryComplexity'
]
do
include
GraphqlHelpers
specify
do
expect
(
described_class
).
to
have_graphql_fields
(
:limit
,
:score
).
only
end
it
'works when executed'
do
query
=
<<-
GQL
query {
queryComplexity {
score
limit
}
currentUser {
name
}
}
GQL
query_result
=
run_with_clean_state
(
query
).
to_h
data
=
graphql_dig_at
(
query_result
,
:data
,
:queryComplexity
)
expect
(
data
).
to
include
(
'score'
=>
be
>
0
,
'limit'
=>
GitlabSchema
::
DEFAULT_MAX_COMPLEXITY
)
end
end
spec/models/integrations/jira_spec.rb
View file @
df9f198f
...
...
@@ -1024,6 +1024,12 @@ RSpec.describe Integrations::Jira do
expect
(
integration
.
web_url
(
'subpath'
,
bar:
'baz baz'
)).
to
eq
(
'http://jira.test.com/path/subpath?bar=baz%20baz&foo=bar%20bar&nosso'
)
end
it
'returns an empty string if URL is not set'
do
integration
.
url
=
nil
expect
(
integration
.
web_url
).
to
eq
(
''
)
end
it
'includes Atlassian referrer for gitlab.com'
do
allow
(
Gitlab
).
to
receive
(
:com?
).
and_return
(
true
)
...
...
@@ -1041,16 +1047,40 @@ RSpec.describe Integrations::Jira do
end
end
describe
'#project_url'
do
it
'returns the correct URL'
do
expect
(
integration
.
project_url
).
to
eq
(
'http://jira.test.com/path'
)
end
it
'returns an empty string if URL is not set'
do
integration
.
url
=
nil
expect
(
integration
.
project_url
).
to
eq
(
''
)
end
end
describe
'#issues_url'
do
it
'returns the correct URL'
do
expect
(
integration
.
issues_url
).
to
eq
(
'http://jira.test.com/path/browse/:id'
)
end
it
'returns an empty string if URL is not set'
do
integration
.
url
=
nil
expect
(
integration
.
issues_url
).
to
eq
(
''
)
end
end
describe
'#new_issue_url'
do
it
'returns the correct URL'
do
expect
(
integration
.
new_issue_url
).
to
eq
(
'http://jira.test.com/path/secure/CreateIssue!default.jspa'
)
end
it
'returns an empty string if URL is not set'
do
integration
.
url
=
nil
expect
(
integration
.
new_issue_url
).
to
eq
(
''
)
end
end
end
...
...
spec/support/shared_examples/models/wiki_shared_examples.rb
View file @
df9f198f
...
...
@@ -545,22 +545,12 @@ RSpec.shared_examples 'wiki model' do
allow
(
Gitlab
::
DefaultBranch
).
to
receive
(
:value
).
and_return
(
'main'
)
end
shared_examples
'feature flag wiki_uses_default_branch is disabled'
do
it
'returns "master"'
do
stub_feature_flags
(
wiki_uses_default_branch:
false
)
expect
(
subject
).
to
eq
'master'
end
end
context
'when repository is not created'
do
let
(
:wiki_container
)
{
wiki_container_without_repo
}
it
'returns the instance default branch'
do
expect
(
subject
).
to
eq
'main'
end
it_behaves_like
'feature flag wiki_uses_default_branch is disabled'
end
context
'when repository is empty'
do
...
...
@@ -573,8 +563,6 @@ RSpec.shared_examples 'wiki model' do
it
'returns the instance default branch'
do
expect
(
subject
).
to
eq
'main'
end
it_behaves_like
'feature flag wiki_uses_default_branch is disabled'
end
context
'when repository is not empty'
do
...
...
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