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
feafff8d
Commit
feafff8d
authored
May 07, 2020
by
Sean Arnold
Committed by
Shinya Maeda
May 07, 2020
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
WIP add sorting to alerts resolver
- Add alert resolver & enum - Adopt sort in alerts finder
parent
7f2274b5
Changes
10
Show whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
513 additions
and
7 deletions
+513
-7
app/finders/alert_management/alerts_finder.rb
app/finders/alert_management/alerts_finder.rb
+6
-1
app/graphql/resolvers/alert_management_alert_resolver.rb
app/graphql/resolvers/alert_management_alert_resolver.rb
+4
-0
app/graphql/types/alert_management/alert_sort_enum.rb
app/graphql/types/alert_management/alert_sort_enum.rb
+25
-0
app/models/alert_management/alert.rb
app/models/alert_management/alert.rb
+24
-0
changelogs/unreleased/215598-alert-management-graphql-sort-backend.yml
...released/215598-alert-management-graphql-sort-backend.yml
+5
-0
doc/api/graphql/reference/gitlab_schema.graphql
doc/api/graphql/reference/gitlab_schema.graphql
+105
-0
doc/api/graphql/reference/gitlab_schema.json
doc/api/graphql/reference/gitlab_schema.json
+139
-0
spec/finders/alert_management/alerts_finder_spec.rb
spec/finders/alert_management/alerts_finder_spec.rb
+157
-2
spec/graphql/resolvers/alert_management_alert_resolver_spec.rb
...graphql/resolvers/alert_management_alert_resolver_spec.rb
+18
-2
spec/requests/api/graphql/project/alert_management/alerts_spec.rb
...uests/api/graphql/project/alert_management/alerts_spec.rb
+30
-2
No files found.
app/finders/alert_management/alerts_finder.rb
View file @
feafff8d
...
...
@@ -12,7 +12,8 @@ module AlertManagement
return
AlertManagement
::
Alert
.
none
unless
authorized?
collection
=
project
.
alert_management_alerts
by_iid
(
collection
)
collection
=
by_iid
(
collection
)
sort
(
collection
)
end
private
...
...
@@ -25,6 +26,10 @@ module AlertManagement
collection
.
for_iid
(
params
[
:iid
])
end
def
sort
(
collection
)
params
[
:sort
]
?
collection
.
sort_by_attribute
(
params
[
:sort
])
:
collection
end
def
authorized?
Ability
.
allowed?
(
current_user
,
:read_alert_management_alerts
,
project
)
end
...
...
app/graphql/resolvers/alert_management_alert_resolver.rb
View file @
feafff8d
...
...
@@ -6,6 +6,10 @@ module Resolvers
required:
false
,
description:
'IID of the alert. For example, "1"'
argument
:sort
,
Types
::
AlertManagement
::
AlertSortEnum
,
description:
'Sort alerts by this criteria'
,
required:
false
type
Types
::
AlertManagement
::
AlertType
,
null:
true
def
resolve
(
**
args
)
...
...
app/graphql/types/alert_management/alert_sort_enum.rb
0 → 100644
View file @
feafff8d
# frozen_string_literal: true
module
Types
module
AlertManagement
class
AlertSortEnum
<
SortEnum
graphql_name
'AlertManagementAlertSort'
description
'Values for sorting alerts'
value
'START_TIME_ASC'
,
'Start time by ascending order'
,
value: :start_time_asc
value
'START_TIME_DESC'
,
'Start time by descending order'
,
value: :start_time_desc
value
'END_TIME_ASC'
,
'End time by ascending order'
,
value: :end_time_asc
value
'END_TIME_DESC'
,
'End time by descending order'
,
value: :end_time_desc
value
'CREATED_TIME_ASC'
,
'Created time by ascending order'
,
value: :created_at_asc
value
'CREATED_TIME_DESC'
,
'Created time by ascending order'
,
value: :created_at_desc
value
'UPDATED_TIME_ASC'
,
'Created time by ascending order'
,
value: :updated_at_desc
value
'UPDATED_TIME_DESC'
,
'Created time by ascending order'
,
value: :updated_at_desc
value
'EVENTS_COUNT_ASC'
,
'Events count by ascending order'
,
value: :events_count_asc
value
'EVENTS_COUNT_DESC'
,
'Events count by descending order'
,
value: :events_count_desc
value
'SEVERITY_ASC'
,
'Severity by ascending order'
,
value: :severity_asc
value
'SEVERITY_DESC'
,
'Severity by descending order'
,
value: :severity_desc
value
'STATUS_ASC'
,
'Status by ascending order'
,
value: :status_asc
value
'STATUS_DESC'
,
'Status by descending order'
,
value: :status_desc
end
end
end
app/models/alert_management/alert.rb
View file @
feafff8d
...
...
@@ -4,6 +4,7 @@ module AlertManagement
class
Alert
<
ApplicationRecord
include
AtomicInternalId
include
ShaAttribute
include
Sortable
belongs_to
:project
belongs_to
:issue
,
optional:
true
...
...
@@ -45,6 +46,29 @@ module AlertManagement
scope
:for_iid
,
->
(
iid
)
{
where
(
iid:
iid
)
}
scope
:order_start_time
,
->
(
sort_order
)
{
order
(
started_at:
sort_order
)
}
scope
:order_end_time
,
->
(
sort_order
)
{
order
(
ended_at:
sort_order
)
}
scope
:order_events_count
,
->
(
sort_order
)
{
order
(
events:
sort_order
)
}
scope
:order_severity
,
->
(
sort_order
)
{
order
(
severity:
sort_order
)
}
scope
:order_status
,
->
(
sort_order
)
{
order
(
status:
sort_order
)
}
def
self
.
sort_by_attribute
(
method
)
case
method
.
to_s
when
'start_time_asc'
then
order_start_time
(
:asc
)
when
'start_time_desc'
then
order_start_time
(
:desc
)
when
'end_time_asc'
then
order_end_time
(
:asc
)
when
'end_time_desc'
then
order_end_time
(
:desc
)
when
'events_count_asc'
then
order_events_count
(
:asc
)
when
'events_count_desc'
then
order_events_count
(
:desc
)
when
'severity_asc'
then
order_severity
(
:asc
)
when
'severity_desc'
then
order_severity
(
:desc
)
when
'status_asc'
then
order_status
(
:asc
)
when
'status_desc'
then
order_status
(
:desc
)
else
order_by
(
method
)
end
end
def
fingerprint
=
(
value
)
if
value
.
blank?
super
(
nil
)
...
...
changelogs/unreleased/215598-alert-management-graphql-sort-backend.yml
0 → 100644
View file @
feafff8d
---
title
:
Add sorting to AlertManagement Alert Graphql
merge_request
:
30964
author
:
type
:
added
doc/api/graphql/reference/gitlab_schema.graphql
View file @
feafff8d
...
...
@@ -248,6 +248,101 @@ type AlertManagementAlertEdge {
node
:
AlertManagementAlert
}
"""
Values for sorting alerts
"""
enum
AlertManagementAlertSort
{
"""
Created
time
by
ascending
order
"""
CREATED_TIME_ASC
"""
Created
time
by
ascending
order
"""
CREATED_TIME_DESC
"""
End
time
by
ascending
order
"""
END_TIME_ASC
"""
End
time
by
descending
order
"""
END_TIME_DESC
"""
Events
count
by
ascending
order
"""
EVENTS_COUNT_ASC
"""
Events
count
by
descending
order
"""
EVENTS_COUNT_DESC
"""
Severity
by
ascending
order
"""
SEVERITY_ASC
"""
Severity
by
descending
order
"""
SEVERITY_DESC
"""
Start
time
by
ascending
order
"""
START_TIME_ASC
"""
Start
time
by
descending
order
"""
START_TIME_DESC
"""
Status
by
ascending
order
"""
STATUS_ASC
"""
Status
by
descending
order
"""
STATUS_DESC
"""
Created
time
by
ascending
order
"""
UPDATED_TIME_ASC
"""
Created
time
by
ascending
order
"""
UPDATED_TIME_DESC
"""
Created
at
ascending
order
"""
created_asc
"""
Created
at
descending
order
"""
created_desc
"""
Updated
at
ascending
order
"""
updated_asc
"""
Updated
at
descending
order
"""
updated_desc
}
"""
Alert severity values
"""
...
...
@@ -6705,6 +6800,11 @@ type Project {
IID
of
the
alert
.
For
example
,
"1"
"""
iid
:
String
"""
Sort
alerts
by
this
criteria
"""
sort
:
AlertManagementAlertSort
):
AlertManagementAlert
"""
...
...
@@ -6735,6 +6835,11 @@ type Project {
Returns
the
last
_n_
elements
from
the
list
.
"""
last
:
Int
"""
Sort
alerts
by
this
criteria
"""
sort
:
AlertManagementAlertSort
):
AlertManagementAlertConnection
"""
...
...
doc/api/graphql/reference/gitlab_schema.json
View file @
feafff8d
...
...
@@ -722,6 +722,125 @@
"enumValues"
:
null
,
"possibleTypes"
:
null
},
{
"kind"
:
"ENUM"
,
"name"
:
"AlertManagementAlertSort"
,
"description"
:
"Values for sorting alerts"
,
"fields"
:
null
,
"inputFields"
:
null
,
"interfaces"
:
null
,
"enumValues"
:
[
{
"name"
:
"updated_desc"
,
"description"
:
"Updated at descending order"
,
"isDeprecated"
:
false
,
"deprecationReason"
:
null
},
{
"name"
:
"updated_asc"
,
"description"
:
"Updated at ascending order"
,
"isDeprecated"
:
false
,
"deprecationReason"
:
null
},
{
"name"
:
"created_desc"
,
"description"
:
"Created at descending order"
,
"isDeprecated"
:
false
,
"deprecationReason"
:
null
},
{
"name"
:
"created_asc"
,
"description"
:
"Created at ascending order"
,
"isDeprecated"
:
false
,
"deprecationReason"
:
null
},
{
"name"
:
"START_TIME_ASC"
,
"description"
:
"Start time by ascending order"
,
"isDeprecated"
:
false
,
"deprecationReason"
:
null
},
{
"name"
:
"START_TIME_DESC"
,
"description"
:
"Start time by descending order"
,
"isDeprecated"
:
false
,
"deprecationReason"
:
null
},
{
"name"
:
"END_TIME_ASC"
,
"description"
:
"End time by ascending order"
,
"isDeprecated"
:
false
,
"deprecationReason"
:
null
},
{
"name"
:
"END_TIME_DESC"
,
"description"
:
"End time by descending order"
,
"isDeprecated"
:
false
,
"deprecationReason"
:
null
},
{
"name"
:
"CREATED_TIME_ASC"
,
"description"
:
"Created time by ascending order"
,
"isDeprecated"
:
false
,
"deprecationReason"
:
null
},
{
"name"
:
"CREATED_TIME_DESC"
,
"description"
:
"Created time by ascending order"
,
"isDeprecated"
:
false
,
"deprecationReason"
:
null
},
{
"name"
:
"UPDATED_TIME_ASC"
,
"description"
:
"Created time by ascending order"
,
"isDeprecated"
:
false
,
"deprecationReason"
:
null
},
{
"name"
:
"UPDATED_TIME_DESC"
,
"description"
:
"Created time by ascending order"
,
"isDeprecated"
:
false
,
"deprecationReason"
:
null
},
{
"name"
:
"EVENTS_COUNT_ASC"
,
"description"
:
"Events count by ascending order"
,
"isDeprecated"
:
false
,
"deprecationReason"
:
null
},
{
"name"
:
"EVENTS_COUNT_DESC"
,
"description"
:
"Events count by descending order"
,
"isDeprecated"
:
false
,
"deprecationReason"
:
null
},
{
"name"
:
"SEVERITY_ASC"
,
"description"
:
"Severity by ascending order"
,
"isDeprecated"
:
false
,
"deprecationReason"
:
null
},
{
"name"
:
"SEVERITY_DESC"
,
"description"
:
"Severity by descending order"
,
"isDeprecated"
:
false
,
"deprecationReason"
:
null
},
{
"name"
:
"STATUS_ASC"
,
"description"
:
"Status by ascending order"
,
"isDeprecated"
:
false
,
"deprecationReason"
:
null
},
{
"name"
:
"STATUS_DESC"
,
"description"
:
"Status by descending order"
,
"isDeprecated"
:
false
,
"deprecationReason"
:
null
}
],
"possibleTypes"
:
null
},
{
"kind"
:
"ENUM"
,
"name"
:
"AlertManagementSeverity"
,
...
...
@@ -20262,6 +20381,16 @@
"ofType"
:
null
},
"defaultValue"
:
null
},
{
"name"
:
"sort"
,
"description"
:
"Sort alerts by this criteria"
,
"type"
:
{
"kind"
:
"ENUM"
,
"name"
:
"AlertManagementAlertSort"
,
"ofType"
:
null
},
"defaultValue"
:
null
}
],
"type"
:
{
...
...
@@ -20286,6 +20415,16 @@
},
"defaultValue"
:
null
},
{
"name"
:
"sort"
,
"description"
:
"Sort alerts by this criteria"
,
"type"
:
{
"kind"
:
"ENUM"
,
"name"
:
"AlertManagementAlertSort"
,
"ofType"
:
null
},
"defaultValue"
:
null
},
{
"name"
:
"after"
,
"description"
:
"Returns the elements in the list that come after the specified cursor."
,
...
...
spec/finders/alert_management/alerts_finder_spec.rb
View file @
feafff8d
...
...
@@ -5,8 +5,8 @@ require 'spec_helper'
describe
AlertManagement
::
AlertsFinder
,
'#execute'
do
let_it_be
(
:current_user
)
{
create
(
:user
)
}
let_it_be
(
:project
)
{
create
(
:project
)
}
let_it_be
(
:alert_1
)
{
create
(
:alert_management_alert
,
project:
project
)
}
let_it_be
(
:alert_2
)
{
create
(
:alert_management_alert
,
project:
project
)
}
let_it_be
(
:alert_1
)
{
create
(
:alert_management_alert
,
project:
project
,
ended_at:
1
.
year
.
ago
,
events:
2
,
severity: :high
,
status: :resolved
)
}
let_it_be
(
:alert_2
)
{
create
(
:alert_management_alert
,
project:
project
,
events:
1
,
severity: :critical
,
status: :ignored
)
}
let_it_be
(
:alert_3
)
{
create
(
:alert_management_alert
)
}
let
(
:params
)
{
{}
}
...
...
@@ -36,5 +36,160 @@ describe AlertManagement::AlertsFinder, '#execute' do
it
{
is_expected
.
to
be_empty
}
end
end
describe
'sorting'
do
context
'when sorting by created'
do
context
'sorts alerts ascending'
do
let
(
:params
)
{
{
sort:
'created_asc'
}
}
it
{
is_expected
.
to
eq
[
alert_1
,
alert_2
]
}
end
context
'sorts alerts descending'
do
let
(
:params
)
{
{
sort:
'created_desc'
}
}
it
{
is_expected
.
to
eq
[
alert_2
,
alert_1
]
}
end
end
context
'when sorting by updated'
do
context
'sorts alerts ascending'
do
let
(
:params
)
{
{
sort:
'updated_asc'
}
}
it
{
is_expected
.
to
eq
[
alert_1
,
alert_2
]
}
end
context
'sorts alerts descending'
do
let
(
:params
)
{
{
sort:
'updated_desc'
}
}
it
{
is_expected
.
to
eq
[
alert_2
,
alert_1
]
}
end
end
context
'when sorting by start time'
do
context
'sorts alerts ascending'
do
let
(
:params
)
{
{
sort:
'start_time_asc'
}
}
it
{
is_expected
.
to
eq
[
alert_1
,
alert_2
]
}
end
context
'sorts alerts descending'
do
let
(
:params
)
{
{
sort:
'start_time_desc'
}
}
it
{
is_expected
.
to
eq
[
alert_2
,
alert_1
]
}
end
end
context
'when sorting by end time'
do
context
'sorts alerts ascending'
do
let
(
:params
)
{
{
sort:
'end_time_asc'
}
}
it
{
is_expected
.
to
eq
[
alert_1
,
alert_2
]
}
end
context
'sorts alerts descending'
do
let
(
:params
)
{
{
sort:
'end_time_desc'
}
}
it
{
is_expected
.
to
eq
[
alert_2
,
alert_1
]
}
end
end
context
'when sorting by events count'
do
let_it_be
(
:alert_count_6
)
{
create
(
:alert_management_alert
,
project:
project
,
events:
6
)
}
let_it_be
(
:alert_count_3
)
{
create
(
:alert_management_alert
,
project:
project
,
events:
3
)
}
context
'sorts alerts ascending'
do
let
(
:params
)
{
{
sort:
'events_count_asc'
}
}
it
{
is_expected
.
to
eq
[
alert_2
,
alert_1
,
alert_count_3
,
alert_count_6
]
}
end
context
'sorts alerts descending'
do
let
(
:params
)
{
{
sort:
'events_count_desc'
}
}
it
{
is_expected
.
to
eq
[
alert_count_6
,
alert_count_3
,
alert_1
,
alert_2
]
}
end
end
context
'when sorting by severity'
do
let_it_be
(
:alert_critical
)
{
create
(
:alert_management_alert
,
project:
project
,
severity: :critical
)
}
let_it_be
(
:alert_high
)
{
create
(
:alert_management_alert
,
project:
project
,
severity: :high
)
}
let_it_be
(
:alert_medium
)
{
create
(
:alert_management_alert
,
project:
project
,
severity: :medium
)
}
let_it_be
(
:alert_low
)
{
create
(
:alert_management_alert
,
project:
project
,
severity: :low
)
}
let_it_be
(
:alert_info
)
{
create
(
:alert_management_alert
,
project:
project
,
severity: :info
)
}
let_it_be
(
:alert_unknown
)
{
create
(
:alert_management_alert
,
project:
project
,
severity: :unknown
)
}
context
'sorts alerts ascending'
do
let
(
:params
)
{
{
sort:
'severity_asc'
}
}
it
do
is_expected
.
to
eq
[
alert_2
,
alert_critical
,
alert_1
,
alert_high
,
alert_medium
,
alert_low
,
alert_info
,
alert_unknown
]
end
end
context
'sorts alerts descending'
do
let
(
:params
)
{
{
sort:
'severity_desc'
}
}
it
do
is_expected
.
to
eq
[
alert_unknown
,
alert_info
,
alert_low
,
alert_medium
,
alert_1
,
alert_high
,
alert_critical
,
alert_2
]
end
end
end
context
'when sorting by status'
do
let_it_be
(
:alert_triggered
)
{
create
(
:alert_management_alert
,
project:
project
,
status: :triggered
)
}
let_it_be
(
:alert_acknowledged
)
{
create
(
:alert_management_alert
,
project:
project
,
status: :acknowledged
)
}
let_it_be
(
:alert_resolved
)
{
create
(
:alert_management_alert
,
project:
project
,
status: :resolved
)
}
let_it_be
(
:alert_ignored
)
{
create
(
:alert_management_alert
,
project:
project
,
status: :ignored
)
}
context
'sorts alerts ascending'
do
let
(
:params
)
{
{
sort:
'status_asc'
}
}
it
do
is_expected
.
to
eq
[
alert_triggered
,
alert_acknowledged
,
alert_1
,
alert_resolved
,
alert_2
,
alert_ignored
]
end
end
context
'sorts alerts descending'
do
let
(
:params
)
{
{
sort:
'status_desc'
}
}
it
do
is_expected
.
to
eq
[
alert_2
,
alert_ignored
,
alert_1
,
alert_resolved
,
alert_acknowledged
,
alert_triggered
]
end
end
end
end
end
end
spec/graphql/resolvers/alert_management_alert_resolver_spec.rb
View file @
feafff8d
...
...
@@ -7,8 +7,8 @@ describe Resolvers::AlertManagementAlertResolver do
let_it_be
(
:current_user
)
{
create
(
:user
)
}
let_it_be
(
:project
)
{
create
(
:project
)
}
let_it_be
(
:alert_1
)
{
create
(
:alert_management_alert
,
project:
project
)
}
let_it_be
(
:alert_2
)
{
create
(
:alert_management_alert
,
project:
project
)
}
let_it_be
(
:alert_1
)
{
create
(
:alert_management_alert
,
project:
project
,
ended_at:
1
.
year
.
ago
,
events:
2
,
severity: :high
,
status: :resolved
)
}
let_it_be
(
:alert_2
)
{
create
(
:alert_management_alert
,
project:
project
,
events:
1
,
severity: :critical
,
status: :ignored
)
}
let_it_be
(
:alert_other_proj
)
{
create
(
:alert_management_alert
)
}
let
(
:args
)
{
{}
}
...
...
@@ -31,6 +31,22 @@ describe Resolvers::AlertManagementAlertResolver do
it
{
is_expected
.
to
contain_exactly
(
alert_1
)
}
end
describe
'sorting'
do
# Other sorting examples in spec/finders/alert_management/alerts_finder_spec.rb
context
'when sorting by events count'
do
let_it_be
(
:alert_count_6
)
{
create
(
:alert_management_alert
,
project:
project
,
events:
6
)
}
let_it_be
(
:alert_count_3
)
{
create
(
:alert_management_alert
,
project:
project
,
events:
3
)
}
it
'sorts alerts ascending'
do
expect
(
resolve_alerts
(
sort: :events_count_asc
)).
to
eq
[
alert_2
,
alert_1
,
alert_count_3
,
alert_count_6
]
end
it
'sorts alerts descending'
do
expect
(
resolve_alerts
(
sort: :events_count_desc
)).
to
eq
[
alert_count_6
,
alert_count_3
,
alert_1
,
alert_2
]
end
end
end
end
private
...
...
spec/requests/api/graphql/project/alert_management/alerts_spec.rb
View file @
feafff8d
...
...
@@ -7,8 +7,9 @@ describe 'getting Alert Management Alerts' do
let_it_be
(
:payload
)
{
{
'custom'
=>
{
'alert'
=>
'payload'
}
}
}
let_it_be
(
:project
)
{
create
(
:project
,
:repository
)
}
let_it_be
(
:current_user
)
{
create
(
:user
)
}
let_it_be
(
:alert_1
)
{
create
(
:alert_management_alert
,
:all_fields
,
project:
project
)
}
let_it_be
(
:alert_2
)
{
create
(
:alert_management_alert
,
:all_fields
,
project:
project
,
payload:
payload
)
}
let_it_be
(
:alert_1
)
{
create
(
:alert_management_alert
,
:all_fields
,
project:
project
,
severity: :low
)
}
let_it_be
(
:alert_2
)
{
create
(
:alert_management_alert
,
:all_fields
,
project:
project
,
severity: :critical
,
payload:
payload
)
}
let_it_be
(
:other_project_alert
)
{
create
(
:alert_management_alert
,
:all_fields
)
}
let
(
:fields
)
do
<<~
QUERY
...
...
@@ -85,6 +86,33 @@ describe 'getting Alert Management Alerts' do
it
{
expect
(
alerts
.
size
).
to
eq
(
1
)
}
it
{
expect
(
first_alert
[
'iid'
]).
to
eq
(
alert_1
.
iid
.
to_s
)
}
end
context
'sorting data given'
do
let
(
:query
)
do
graphql_query_for
(
'project'
,
{
'fullPath'
=>
project
.
full_path
},
query_graphql_field
(
'alertManagementAlerts'
,
params
,
fields
)
)
end
let
(
:params
)
{
'sort: SEVERITY_DESC'
}
let
(
:iids
)
{
alerts
.
map
{
|
a
|
a
[
'iid'
]
}
}
it_behaves_like
'a working graphql query'
it
'sorts in the correct order'
do
expect
(
iids
).
to
eq
[
alert_1
.
iid
.
to_s
,
alert_2
.
iid
.
to_s
]
end
context
'ascending order'
do
let
(
:params
)
{
'sort: SEVERITY_ASC'
}
it
'sorts in the correct order'
do
expect
(
iids
).
to
eq
[
alert_2
.
iid
.
to_s
,
alert_1
.
iid
.
to_s
]
end
end
end
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