Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
it-bott-integrasjoner
cim-client
Commits
1a6ccb14
Commit
1a6ccb14
authored
Apr 26, 2021
by
Trond Aasan
Browse files
CIM-58 Pluralise endpoint names to show that they take lists
parent
8b2909fb
Changes
5
Show whitespace changes
Inline
Side-by-side
README.md
View file @
1a6ccb14
...
@@ -60,20 +60,20 @@ from cim_client import CimClient, CimEndpoints
...
@@ -60,20 +60,20 @@ from cim_client import CimClient, CimEndpoints
from
cim_client.models
import
PersonBase
,
PersonList
from
cim_client.models
import
PersonBase
,
PersonList
c
=
CimClient
(
c
=
CimClient
(
CimEndpoints
(
url
=
'https://example.com'
,
CimEndpoints
(
url
=
'https://example.com'
,
upsert_person_url
=
'/_webservices/?ws=contacts/upsert/1.0'
,
upsert_person
s
_url
=
'/_webservices/?ws=contacts/upsert/1.0'
,
delete_person_url
=
'/_webservices/?ws=contacts/delete/1.0'
),
delete_person
s
_url
=
'/_webservices/?ws=contacts/delete/1.0'
),
tokens
=
{
'api_key'
:
{
'X-Gravitee-API-Key'
:
'c-d-a-b'
},})
tokens
=
{
'api_key'
:
{
'X-Gravitee-API-Key'
:
'c-d-a-b'
},})
upd_schema
=
c
.
get_upsert_person_schema
()
upd_schema
=
c
.
get_upsert_person
s
_schema
()
del_schema
=
c
.
get_delete_person_schema
()
del_schema
=
c
.
get_delete_person
s
_schema
()
person
=
PersonBase
.
from_dict
({
'username'
:
'foo'
,
'firstname'
:
'John'
,
'lastname'
:
'Doe'
})
person
=
PersonBase
.
from_dict
({
'username'
:
'foo'
,
'firstname'
:
'John'
,
'lastname'
:
'Doe'
})
response1
=
c
.
upsert_person
(
person
)
response1
=
c
.
upsert_person
s
(
person
)
person2
=
PersonBase
.
from_dict
({
'username'
:
'bar'
,
'firstname'
:
'Petter'
,
'lastname'
:
'Smart'
})
person2
=
PersonBase
.
from_dict
({
'username'
:
'bar'
,
'firstname'
:
'Petter'
,
'lastname'
:
'Smart'
})
persons
=
[
person
,
person2
]
persons
=
[
person
,
person2
]
personlist
=
PersonList
(
persons
=
persons
)
personlist
=
PersonList
(
persons
=
persons
)
# Note that delete_person supports both PersonList and [Person, Person, ...]
# Note that delete_person supports both PersonList and [Person, Person, ...]
response2
=
c
.
upsert_person
(
personlist
)
response2
=
c
.
upsert_person
s
(
personlist
)
response3
=
c
.
delete_person
(
person
.
username
)
response3
=
c
.
delete_person
s
(
person
.
username
)
```
```
cim_client/client.py
View file @
1a6ccb14
...
@@ -69,14 +69,14 @@ class CimEndpoints:
...
@@ -69,14 +69,14 @@ class CimEndpoints:
def
__init__
(
def
__init__
(
self
,
self
,
url
:
str
,
url
:
str
,
upsert_person_url
:
Optional
[
str
]
=
None
,
upsert_person
s
_url
:
Optional
[
str
]
=
None
,
delete_person_url
:
Optional
[
str
]
=
None
,
delete_person
s
_url
:
Optional
[
str
]
=
None
,
import_organisations_url
:
Optional
[
str
]
=
None
,
import_organisations_url
:
Optional
[
str
]
=
None
,
):
):
""" Get endpoints relative to the CIM API URL. """
""" Get endpoints relative to the CIM API URL. """
self
.
baseurl
=
url
self
.
baseurl
=
url
self
.
upsert_person_url
=
upsert_person_url
or
"?ws=contacts/upsert/1.0"
self
.
upsert_person
s
_url
=
upsert_person
s
_url
or
"?ws=contacts/upsert/1.0"
self
.
delete_person_url
=
delete_person_url
or
"?ws=contacts/delete/1.0"
self
.
delete_person
s
_url
=
delete_person
s
_url
or
"?ws=contacts/delete/1.0"
self
.
import_organisations_url
=
(
self
.
import_organisations_url
=
(
import_organisations_url
or
"?ws=contacts/organisations/1.0"
import_organisations_url
or
"?ws=contacts/organisations/1.0"
)
)
...
@@ -84,17 +84,17 @@ class CimEndpoints:
...
@@ -84,17 +84,17 @@ class CimEndpoints:
def
__repr__
(
self
)
->
str
:
def
__repr__
(
self
)
->
str
:
return
"{cls.__name__}({url!r})"
.
format
(
cls
=
type
(
self
),
url
=
self
.
baseurl
)
return
"{cls.__name__}({url!r})"
.
format
(
cls
=
type
(
self
),
url
=
self
.
baseurl
)
def
upsert_person
(
self
)
->
str
:
def
upsert_person
s
(
self
)
->
str
:
return
urllib
.
parse
.
urljoin
(
self
.
baseurl
,
self
.
upsert_person_url
)
return
urllib
.
parse
.
urljoin
(
self
.
baseurl
,
self
.
upsert_person
s
_url
)
def
delete_person
(
self
)
->
str
:
def
delete_person
s
(
self
)
->
str
:
return
urllib
.
parse
.
urljoin
(
self
.
baseurl
,
self
.
delete_person_url
)
return
urllib
.
parse
.
urljoin
(
self
.
baseurl
,
self
.
delete_person
s
_url
)
def
get_upsert_person_schema
(
self
)
->
str
:
def
get_upsert_person
s
_schema
(
self
)
->
str
:
return
urllib
.
parse
.
urljoin
(
self
.
baseurl
,
self
.
upsert_person_url
)
return
urllib
.
parse
.
urljoin
(
self
.
baseurl
,
self
.
upsert_person
s
_url
)
def
get_delete_person_schema
(
self
)
->
str
:
def
get_delete_person
s
_schema
(
self
)
->
str
:
return
urllib
.
parse
.
urljoin
(
self
.
baseurl
,
self
.
delete_person_url
)
return
urllib
.
parse
.
urljoin
(
self
.
baseurl
,
self
.
delete_person
s
_url
)
def
import_organisations
(
self
)
->
str
:
def
import_organisations
(
self
)
->
str
:
return
urllib
.
parse
.
urljoin
(
self
.
baseurl
,
self
.
import_organisations_url
)
return
urllib
.
parse
.
urljoin
(
self
.
baseurl
,
self
.
import_organisations_url
)
...
@@ -192,7 +192,7 @@ class CimClient(object):
...
@@ -192,7 +192,7 @@ class CimClient(object):
return
data
return
data
return
cls
.
from_dict
(
data
)
return
cls
.
from_dict
(
data
)
def
get_upsert_person_schema
(
self
)
->
JsonType
:
def
get_upsert_person
s
_schema
(
self
)
->
JsonType
:
"""GETs the current schema from CIM
"""GETs the current schema from CIM
The schema can change depending on the installation.
The schema can change depending on the installation.
...
@@ -201,7 +201,7 @@ class CimClient(object):
...
@@ -201,7 +201,7 @@ class CimClient(object):
checks whether the schema has changed lately so you don't update
checks whether the schema has changed lately so you don't update
using an outdated schema.
using an outdated schema.
"""
"""
url
=
self
.
endpoints
.
get_upsert_person_schema
()
url
=
self
.
endpoints
.
get_upsert_person
s
_schema
()
response
=
self
.
get
(
response
=
self
.
get
(
url
,
url
,
auth
=
self
.
auth
,
auth
=
self
.
auth
,
...
@@ -209,7 +209,7 @@ class CimClient(object):
...
@@ -209,7 +209,7 @@ class CimClient(object):
)
)
return
response
return
response
def
get_delete_person_schema
(
self
)
->
JsonType
:
def
get_delete_person
s
_schema
(
self
)
->
JsonType
:
"""GETs the current schema from CIM
"""GETs the current schema from CIM
The schema can change depending on the installation.
The schema can change depending on the installation.
...
@@ -218,7 +218,7 @@ class CimClient(object):
...
@@ -218,7 +218,7 @@ class CimClient(object):
checks whether the schema has changed lately so you don't update
checks whether the schema has changed lately so you don't update
using an outdated schema.
using an outdated schema.
"""
"""
url
=
self
.
endpoints
.
get_delete_person_schema
()
url
=
self
.
endpoints
.
get_delete_person
s
_schema
()
response
=
self
.
get
(
response
=
self
.
get
(
url
,
url
,
auth
=
self
.
auth
,
auth
=
self
.
auth
,
...
@@ -226,7 +226,7 @@ class CimClient(object):
...
@@ -226,7 +226,7 @@ class CimClient(object):
)
)
return
response
return
response
def
upsert_person
(
def
upsert_person
s
(
self
,
self
,
persondata
:
Union
[
List
[
PersonBase
],
PersonBase
,
PersonList
],
persondata
:
Union
[
List
[
PersonBase
],
PersonBase
,
PersonList
],
)
->
Optional
[
Tuple
[
str
,
bytes
]]:
)
->
Optional
[
Tuple
[
str
,
bytes
]]:
...
@@ -239,7 +239,7 @@ class CimClient(object):
...
@@ -239,7 +239,7 @@ class CimClient(object):
Note also that the response will not contain info about failing to find
Note also that the response will not contain info about failing to find
someone. You will have to compare with what you posted.
someone. You will have to compare with what you posted.
"""
"""
url
=
self
.
endpoints
.
upsert_person
()
url
=
self
.
endpoints
.
upsert_person
s
()
if
isinstance
(
persondata
,
PersonList
):
if
isinstance
(
persondata
,
PersonList
):
persondata
=
persondata
.
persons
persondata
=
persondata
.
persons
...
@@ -257,7 +257,7 @@ class CimClient(object):
...
@@ -257,7 +257,7 @@ class CimClient(object):
response
.
raise_for_status
()
response
.
raise_for_status
()
return
None
return
None
def
delete_person
(
def
delete_person
s
(
self
,
self
,
person_ids
:
List
[
str
],
person_ids
:
List
[
str
],
)
->
Union
[
Tuple
[
str
,
bytes
],
None
]:
)
->
Union
[
Tuple
[
str
,
bytes
],
None
]:
...
@@ -271,7 +271,7 @@ class CimClient(object):
...
@@ -271,7 +271,7 @@ class CimClient(object):
:param person_ids: The person or people we want to delete from CIM
:param person_ids: The person or people we want to delete from CIM
:return: String describing status or None if not found
:return: String describing status or None if not found
"""
"""
url
=
self
.
endpoints
.
delete_person
()
url
=
self
.
endpoints
.
delete_person
s
()
headers
=
{
"Content-Type"
:
"application/json"
}
headers
=
{
"Content-Type"
:
"application/json"
}
response
=
self
.
post
(
url
,
json
=
person_ids
,
auth
=
self
.
auth
,
headers
=
headers
)
response
=
self
.
post
(
url
,
json
=
person_ids
,
auth
=
self
.
auth
,
headers
=
headers
)
...
...
example-config.json
View file @
1a6ccb14
...
@@ -3,8 +3,8 @@
...
@@ -3,8 +3,8 @@
"client"
:
{
"client"
:
{
"endpoints"
:
{
"endpoints"
:
{
"url"
:
"https://example.com"
,
"url"
:
"https://example.com"
,
"upsert_person_url"
:
"up
date
_person_url"
,
"upsert_person
s
_url"
:
"up
sert
_person
s
_url"
,
"delete_person_url"
:
"delete_person_url"
,
"delete_person
s
_url"
:
"delete_person
s
_url"
,
"import_organisations_url"
:
"import_organisations_url"
"import_organisations_url"
:
"import_organisations_url"
},
},
"headers"
:
{
"headers"
:
{
...
...
tests/test_client.py
View file @
1a6ccb14
...
@@ -70,53 +70,53 @@ def test_init_from_dict(config_example):
...
@@ -70,53 +70,53 @@ def test_init_from_dict(config_example):
e
=
client
.
endpoints
e
=
client
.
endpoints
assert
isinstance
(
e
,
CimEndpoints
)
assert
isinstance
(
e
,
CimEndpoints
)
assert
e
.
baseurl
==
config_example
[
"endpoints"
][
"url"
]
assert
e
.
baseurl
==
config_example
[
"endpoints"
][
"url"
]
assert
e
.
upsert_person_url
==
"up
date
_person_url"
assert
e
.
upsert_person
s
_url
==
"up
sert
_person
s
_url"
assert
e
.
delete_person_url
==
"delete_person_url"
assert
e
.
delete_person
s
_url
==
"delete_person
s
_url"
assert
e
.
import_organisations_url
==
"import_organisations_url"
assert
e
.
import_organisations_url
==
"import_organisations_url"
assert
e
.
baseurl
==
"https://example.com"
assert
e
.
baseurl
==
"https://example.com"
def
test_get_upsert_person_schema
(
client
,
requests_mock
):
def
test_get_upsert_person
s
_schema
(
client
,
requests_mock
):
"""Ensure getting upsert schema works"""
"""Ensure getting upsert schema works"""
requests_mock
.
get
(
requests_mock
.
get
(
"https://localhost/_webservices/?ws=contacts/upsert/1.0"
,
json
=
{
"foo"
:
"bar"
}
"https://localhost/_webservices/?ws=contacts/upsert/1.0"
,
json
=
{
"foo"
:
"bar"
}
)
)
response
=
client
.
get_upsert_person_schema
()
response
=
client
.
get_upsert_person
s
_schema
()
assert
response
==
{
"foo"
:
"bar"
}
assert
response
==
{
"foo"
:
"bar"
}
def
test_get_delete_person_schema
(
client
,
requests_mock
):
def
test_get_delete_person
s
_schema
(
client
,
requests_mock
):
"""Ensure getting delete schema works"""
"""Ensure getting delete schema works"""
requests_mock
.
get
(
requests_mock
.
get
(
"https://localhost/_webservices/?ws=contacts/delete/1.0"
,
json
=
{
"foo"
:
"bar"
}
"https://localhost/_webservices/?ws=contacts/delete/1.0"
,
json
=
{
"foo"
:
"bar"
}
)
)
response
=
client
.
get_delete_person_schema
()
response
=
client
.
get_delete_person
s
_schema
()
assert
response
==
{
"foo"
:
"bar"
}
assert
response
==
{
"foo"
:
"bar"
}
def
test_delete_person_success_200
(
client
,
john_doe
,
requests_mock
):
def
test_delete_person
s
_success_200
(
client
,
john_doe
,
requests_mock
):
"""Ensure 404 returns None"""
"""Ensure 404 returns None"""
requests_mock
.
post
(
requests_mock
.
post
(
"https://localhost/_webservices/?ws=contacts/delete/1.0"
,
"https://localhost/_webservices/?ws=contacts/delete/1.0"
,
status_code
=
200
,
status_code
=
200
,
json
=
[
"Delete person: johndoe(9999)"
],
json
=
[
"Delete person: johndoe(9999)"
],
)
)
response
=
client
.
delete_person
(
Person
.
from_dict
(
john_doe
).
username
)
response
=
client
.
delete_person
s
(
Person
.
from_dict
(
john_doe
).
username
)
assert
response
==
(
"Delete success"
,
b
'["Delete person: johndoe(9999)"]'
)
assert
response
==
(
"Delete success"
,
b
'["Delete person: johndoe(9999)"]'
)
def
test_delete_person_success_404
(
client
,
john_doe
,
requests_mock
):
def
test_delete_person
s
_success_404
(
client
,
john_doe
,
requests_mock
):
"""Ensure 404 returns None when response text is `No person found: No deletion`"""
"""Ensure 404 returns None when response text is `No person found: No deletion`"""
requests_mock
.
post
(
requests_mock
.
post
(
"https://localhost/_webservices/?ws=contacts/delete/1.0"
,
"https://localhost/_webservices/?ws=contacts/delete/1.0"
,
status_code
=
404
,
status_code
=
404
,
text
=
"No person found: No deletion"
,
text
=
"No person found: No deletion"
,
)
)
response
=
client
.
delete_person
(
Person
.
from_dict
(
john_doe
).
username
)
response
=
client
.
delete_person
s
(
Person
.
from_dict
(
john_doe
).
username
)
assert
response
is
None
assert
response
is
None
def
test_delete_person_failure_404
(
client
,
john_doe
,
requests_mock
):
def
test_delete_person
s
_failure_404
(
client
,
john_doe
,
requests_mock
):
"""Ensure 404 fails when response text is not `No person found: No deletion`"""
"""Ensure 404 fails when response text is not `No person found: No deletion`"""
requests_mock
.
post
(
requests_mock
.
post
(
"https://localhost/_webservices/?ws=contacts/delete/1.0"
,
"https://localhost/_webservices/?ws=contacts/delete/1.0"
,
...
@@ -124,13 +124,13 @@ def test_delete_person_failure_404(client, john_doe, requests_mock):
...
@@ -124,13 +124,13 @@ def test_delete_person_failure_404(client, john_doe, requests_mock):
text
=
'["Something, something, something dark side"]'
,
text
=
'["Something, something, something dark side"]'
,
)
)
with
pytest
.
raises
(
HTTPError
):
with
pytest
.
raises
(
HTTPError
):
client
.
delete_person
(
Person
.
from_dict
(
john_doe
).
username
)
client
.
delete_person
s
(
Person
.
from_dict
(
john_doe
).
username
)
def
test_upsert_persons_one
(
client
,
john_doe
,
requests_mock
):
def
test_upsert_persons_one
(
client
,
john_doe
,
requests_mock
):
"""Ensure upserting one works"""
"""Ensure upserting one works"""
requests_mock
.
post
(
"https://localhost/_webservices/?ws=contacts/upsert/1.0"
)
requests_mock
.
post
(
"https://localhost/_webservices/?ws=contacts/upsert/1.0"
)
response
=
client
.
upsert_person
(
Person
.
from_dict
(
john_doe
))
response
=
client
.
upsert_person
s
(
Person
.
from_dict
(
john_doe
))
assert
response
==
(
"Import success"
,
b
""
)
assert
response
==
(
"Import success"
,
b
""
)
...
@@ -139,7 +139,7 @@ def test_upsert_persons_many(client, john_doe, jane_doe, requests_mock):
...
@@ -139,7 +139,7 @@ def test_upsert_persons_many(client, john_doe, jane_doe, requests_mock):
personlist
=
[
Person
.
from_dict
(
i
)
for
i
in
(
john_doe
,
jane_doe
)]
personlist
=
[
Person
.
from_dict
(
i
)
for
i
in
(
john_doe
,
jane_doe
)]
requests_mock
.
post
(
"https://localhost/_webservices/?ws=contacts/upsert/1.0"
)
requests_mock
.
post
(
"https://localhost/_webservices/?ws=contacts/upsert/1.0"
)
response
=
client
.
upsert_person
(
personlist
)
response
=
client
.
upsert_person
s
(
personlist
)
assert
response
==
(
"Import success"
,
b
""
)
assert
response
==
(
"Import success"
,
b
""
)
...
@@ -149,7 +149,7 @@ def test_upsert_persons_failure_400(client, john_doe, requests_mock):
...
@@ -149,7 +149,7 @@ def test_upsert_persons_failure_400(client, john_doe, requests_mock):
"https://localhost/_webservices/?ws=contacts/upsert/1.0"
,
status_code
=
400
"https://localhost/_webservices/?ws=contacts/upsert/1.0"
,
status_code
=
400
)
)
with
pytest
.
raises
(
HTTPError
):
with
pytest
.
raises
(
HTTPError
):
client
.
upsert_person
(
Person
.
from_dict
(
john_doe
))
client
.
upsert_person
s
(
Person
.
from_dict
(
john_doe
))
def
test_upsert_persons_failure_500
(
client
,
john_doe
,
requests_mock
):
def
test_upsert_persons_failure_500
(
client
,
john_doe
,
requests_mock
):
...
@@ -158,7 +158,7 @@ def test_upsert_persons_failure_500(client, john_doe, requests_mock):
...
@@ -158,7 +158,7 @@ def test_upsert_persons_failure_500(client, john_doe, requests_mock):
"https://localhost/_webservices/?ws=contacts/upsert/1.0"
,
status_code
=
500
"https://localhost/_webservices/?ws=contacts/upsert/1.0"
,
status_code
=
500
)
)
with
pytest
.
raises
(
HTTPError
):
with
pytest
.
raises
(
HTTPError
):
client
.
upsert_person
(
Person
.
from_dict
(
john_doe
))
client
.
upsert_person
s
(
Person
.
from_dict
(
john_doe
))
def
test_get_import_organisations_schema
(
client
,
requests_mock
):
def
test_get_import_organisations_schema
(
client
,
requests_mock
):
...
...
tests/test_endpoints.py
View file @
1a6ccb14
...
@@ -6,47 +6,47 @@ def test_init(baseurl):
...
@@ -6,47 +6,47 @@ def test_init(baseurl):
assert
endpoints
.
baseurl
==
baseurl
assert
endpoints
.
baseurl
==
baseurl
def
test_upsert_person
(
baseurl
,
endpoints
):
def
test_upsert_person
s
(
baseurl
,
endpoints
):
url
=
endpoints
.
upsert_person
()
url
=
endpoints
.
upsert_person
s
()
assert
url
==
baseurl
+
"?ws=contacts/upsert/1.0"
assert
url
==
baseurl
+
"?ws=contacts/upsert/1.0"
def
test_delete_person
(
baseurl
,
endpoints
):
def
test_delete_person
s
(
baseurl
,
endpoints
):
url
=
endpoints
.
delete_person
()
url
=
endpoints
.
delete_person
s
()
assert
url
==
baseurl
+
"?ws=contacts/delete/1.0"
assert
url
==
baseurl
+
"?ws=contacts/delete/1.0"
def
test_get_upsert_person_schema
(
baseurl
,
endpoints
):
def
test_get_upsert_person
s
_schema
(
baseurl
,
endpoints
):
url
=
endpoints
.
get_upsert_person_schema
()
url
=
endpoints
.
get_upsert_person
s
_schema
()
assert
url
==
baseurl
+
"?ws=contacts/upsert/1.0"
assert
url
==
baseurl
+
"?ws=contacts/upsert/1.0"
def
test_get_delete_person_schema
(
baseurl
,
endpoints
):
def
test_get_delete_person
s
_schema
(
baseurl
,
endpoints
):
url
=
endpoints
.
get_delete_person_schema
()
url
=
endpoints
.
get_delete_person
s
_schema
()
assert
url
==
baseurl
+
"?ws=contacts/delete/1.0"
assert
url
==
baseurl
+
"?ws=contacts/delete/1.0"
def
test_custom_upsert_person
(
custom_endpoints
,
baseurl
):
def
test_custom_upsert_person
s
(
custom_endpoints
,
baseurl
):
assert
(
assert
(
custom_endpoints
.
upsert_person
()
==
baseurl
+
"custom/?ws=contacts/upsert/1.0"
custom_endpoints
.
upsert_person
s
()
==
baseurl
+
"custom/?ws=contacts/upsert/1.0"
)
)
def
test_custom_delete_person
(
custom_endpoints
,
baseurl
):
def
test_custom_delete_person
s
(
custom_endpoints
,
baseurl
):
assert
(
assert
(
custom_endpoints
.
delete_person
()
==
baseurl
+
"custom/?ws=contacts/delete/1.0"
custom_endpoints
.
delete_person
s
()
==
baseurl
+
"custom/?ws=contacts/delete/1.0"
)
)
def
test_custom_get_upsert_person_schema
(
custom_endpoints
,
baseurl
):
def
test_custom_get_upsert_person
s
_schema
(
custom_endpoints
,
baseurl
):
assert
(
assert
(
custom_endpoints
.
get_upsert_person_schema
()
custom_endpoints
.
get_upsert_person
s
_schema
()
==
baseurl
+
"custom/?ws=contacts/upsert/1.0"
==
baseurl
+
"custom/?ws=contacts/upsert/1.0"
)
)
def
test_custom_get_delete_person_schema
(
custom_endpoints
,
baseurl
):
def
test_custom_get_delete_person
s
_schema
(
custom_endpoints
,
baseurl
):
assert
(
assert
(
custom_endpoints
.
get_delete_person_schema
()
custom_endpoints
.
get_delete_person
s
_schema
()
==
baseurl
+
"custom/?ws=contacts/delete/1.0"
==
baseurl
+
"custom/?ws=contacts/delete/1.0"
)
)
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