Skip to content
GitLab
Explore
Sign in
Register
Primary navigation
Search or go to…
Project
P
perxis-go
Manage
Activity
Members
Code
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Deploy
Package Registry
Operate
Terraform modules
Analyze
Contributor analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
perxis
perxis-go
Commits
226c80ab
Commit
226c80ab
authored
2 years ago
by
Alena Petraki
Browse files
Options
Downloads
Patches
Plain Diff
Добавлен пакет 'cache'
parent
1f57fd0f
No related branches found
No related tags found
No related merge requests found
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
go.mod
+6
-2
6 additions, 2 deletions
go.mod
go.sum
+30
-3
30 additions, 3 deletions
go.sum
pkg/cache/cache.go
+90
-0
90 additions, 0 deletions
pkg/cache/cache.go
pkg/cache/cache_test.go
+82
-0
82 additions, 0 deletions
pkg/cache/cache_test.go
with
208 additions
and
5 deletions
go.mod
+
6
−
2
View file @
226c80ab
...
...
@@ -6,23 +6,27 @@ require (
github.com/go-kit/kit
v0.12.0
github.com/golang/protobuf
v1.5.2
github.com/hashicorp/go-multierror
v1.1.1
github.com/hashicorp/golang-lru
v0.5.4
github.com/pkg/errors
v0.9.1
github.com/rs/xid
v1.4.0
github.com/stretchr/testify
v1.7.0
go.uber.org/zap
v1.19.1
golang.org/x/net
v0.0.0-20210917221730-978cfadd31cf
google.golang.org/grpc
v1.45.0
google.golang.org/protobuf
v1.28.0
)
require (
github.com/davecgh/go-spew
v1.1.
0
// indirect
github.com/davecgh/go-spew
v1.1.
1
// indirect
github.com/go-kit/log
v0.2.0 // indirect
github.com/go-logfmt/logfmt
v0.5.1 // indirect
github.com/hashicorp/errwrap
v1.0.0 // indirect
github.com/pmezard/go-difflib
v1.0.0 // indirect
github.com/stretchr/objx
v0.1.0 // indirect
go.uber.org/atomic
v1.9.0 // indirect
go.uber.org/multierr
v1.7.0 // indirect
golang.org/x/sys
v0.0.0-20210917161153-d61c044b1678 // indirect
golang.org/x/text
v0.3.7 // indirect
google.golang.org/genproto
v0.0.0-20210917145530-b395a37504d4 // indirect
gopkg.in/yaml.v3
v3.0.0-202
00313102051-9f266ea9e77c
// indirect
gopkg.in/yaml.v3
v3.0.0-202
10107192922-496545a6307b
// indirect
)
This diff is collapsed.
Click to expand it.
go.sum
+
30
−
3
View file @
226c80ab
...
...
@@ -3,6 +3,8 @@ cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT
github.com/BurntSushi/toml
v0.3.1/go.mod h1:
xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/OneOfOne/xxhash
v1.2.2/go.mod h1:
HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/antihax/optional
v1.0.0/go.mod h1:
uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/benbjohnson/clock
v1.1.0 h1:
Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
github.com/benbjohnson/clock
v1.1.0/go.mod h1:
J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/census-instrumentation/opencensus-proto
v0.2.1/go.mod h1:
f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash
v1.1.0/go.mod h1:
XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/cespare/xxhash/v2
v2.1.1/go.mod h1:
VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
...
...
@@ -14,8 +16,9 @@ github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWH
github.com/cncf/xds/go
v0.0.0-20210805033703-aa0b78936158/go.mod h1:
eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go
v0.0.0-20210922020428-25de7278fc84/go.mod h1:
eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go
v0.0.0-20211011173535-cb28da3451f1/go.mod h1:
eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/davecgh/go-spew
v1.1.0 h1:
ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew
v1.1.0/go.mod h1:
J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew
v1.1.1 h1:
vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew
v1.1.1/go.mod h1:
J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/envoyproxy/go-control-plane
v0.9.0/go.mod h1:
YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane
v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:
YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane
v0.9.4/go.mod h1:
6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
...
...
@@ -59,6 +62,14 @@ github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/U
github.com/hashicorp/errwrap
v1.0.0/go.mod h1:
YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-multierror
v1.1.1 h1:
H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
github.com/hashicorp/go-multierror
v1.1.1/go.mod h1:
iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
github.com/hashicorp/golang-lru
v0.5.4 h1:
YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
github.com/hashicorp/golang-lru
v0.5.4/go.mod h1:
iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/kr/pretty
v0.1.0 h1:
L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty
v0.1.0/go.mod h1:
dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty
v1.1.1/go.mod h1:
pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text
v0.1.0 h1:
45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text
v0.1.0/go.mod h1:
4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/pkg/errors
v0.8.1/go.mod h1:
bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors
v0.9.1 h1:
FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors
v0.9.1/go.mod h1:
bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib
v1.0.0 h1:
4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
...
...
@@ -70,11 +81,22 @@ github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/spaolacci/murmur3
v0.0.0-20180118202830-f09979ecbc72/go.mod h1:
JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/stretchr/objx
v0.1.0 h1:
4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
github.com/stretchr/objx
v0.1.0/go.mod h1:
HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify
v1.3.0/go.mod h1:
M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify
v1.5.1/go.mod h1:
5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify
v1.7.0 h1:
nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify
v1.7.0/go.mod h1:
6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/yuin/goldmark
v1.3.5/go.mod h1:
mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
go.opentelemetry.io/proto/otlp
v0.7.0/go.mod h1:
PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
go.uber.org/atomic
v1.7.0/go.mod h1:
fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/atomic
v1.9.0 h1:
ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
go.uber.org/atomic
v1.9.0/go.mod h1:
fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/goleak
v1.1.11-0.20210813005559-691160354723 h1:
sHOAIxRGBp443oHZIPB+HsUGaksVCXVQENPxwTfQdH4=
go.uber.org/goleak
v1.1.11-0.20210813005559-691160354723/go.mod h1:
cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
go.uber.org/multierr
v1.6.0/go.mod h1:
cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/multierr
v1.7.0 h1:
zaiO/rmgFjbmCXdSYJWQcdvOCsthmdaHfr3Gm2Kx4Ec=
go.uber.org/multierr
v1.7.0/go.mod h1:
7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak=
go.uber.org/zap
v1.19.1 h1:
ue41HOKd1vGURxrmeKIgELGb3jPW9DMUDGtsinblHwI=
go.uber.org/zap
v1.19.1/go.mod h1:
j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI=
golang.org/x/crypto
v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:
djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto
v0.0.0-20191011191535-87dc89f01550/go.mod h1:
yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto
v0.0.0-20200622213623-75b288015ac9/go.mod h1:
LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
...
...
@@ -82,6 +104,7 @@ golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL
golang.org/x/lint
v0.0.0-20181026193005-c67002cb31c3/go.mod h1:
UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint
v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:
wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint
v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:
6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint
v0.0.0-20190930215403-16217165b5de/go.mod h1:
6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint
v0.0.0-20210508222113-6edffad5e616/go.mod h1:
3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mod
v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:
QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod
v0.4.2/go.mod h1:
s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
...
...
@@ -162,11 +185,15 @@ google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ
google.golang.org/protobuf
v1.27.1/go.mod h1:
9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf
v1.28.0 h1:
w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=
google.golang.org/protobuf
v1.28.0/go.mod h1:
HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/check.v1
v0.0.0-20161208181325-20d25e280405 h1:
yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1
v0.0.0-20161208181325-20d25e280405/go.mod h1:
Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1
v1.0.0-20180628173108-788fd7840127 h1:
qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1
v1.0.0-20180628173108-788fd7840127/go.mod h1:
Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2
v2.2.2/go.mod h1:
hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2
v2.2.3/go.mod h1:
hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3
v3.0.0-20200313102051-9f266ea9e77c h1:
dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v2
v2.2.8 h1:
obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
gopkg.in/yaml.v2
v2.2.8/go.mod h1:
hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3
v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:
K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3
v3.0.0-20210107192922-496545a6307b h1:
h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
gopkg.in/yaml.v3
v3.0.0-20210107192922-496545a6307b/go.mod h1:
K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools
v0.0.0-20190102054323-c2f93a96b099/go.mod h1:
rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools
v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:
rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
This diff is collapsed.
Click to expand it.
pkg/cache/cache.go
0 → 100644
+
90
−
0
View file @
226c80ab
package
cache
import
(
"errors"
"fmt"
"time"
lru
"github.com/hashicorp/golang-lru"
"go.uber.org/zap"
)
const
(
defaultCacheSize
=
1000
defaultTTL
=
30
*
time
.
Second
)
var
ErrNotFound
=
errors
.
New
(
"not found"
)
type
Cache
struct
{
cache
*
lru
.
Cache
ttl
time
.
Duration
logger
*
zap
.
Logger
}
type
item
struct
{
value
interface
{}
expiredAt
time
.
Time
}
func
NewCache
(
size
int
,
ttl
time
.
Duration
,
opts
...
interface
{})
*
Cache
{
if
size
==
0
{
size
=
defaultCacheSize
}
if
ttl
==
0
{
ttl
=
defaultTTL
}
c
,
err
:=
lru
.
New
(
size
)
if
err
!=
nil
{
panic
(
err
)
}
ch
:=
&
Cache
{
cache
:
c
,
ttl
:
ttl
,
logger
:
zap
.
NewNop
(),
}
for
_
,
o
:=
range
opts
{
switch
p
:=
o
.
(
type
)
{
case
*
zap
.
Logger
:
ch
.
logger
=
p
}
}
ch
.
logger
=
ch
.
logger
.
Named
(
"Cache"
)
return
ch
}
func
(
c
*
Cache
)
Set
(
key
,
value
interface
{})
(
err
error
)
{
c
.
cache
.
Add
(
key
,
&
item
{
value
:
value
,
expiredAt
:
time
.
Now
()
.
Add
(
c
.
ttl
)})
c
.
logger
.
Debug
(
"Set"
,
zap
.
String
(
"key"
,
fmt
.
Sprintf
(
"%v"
,
key
)),
zap
.
String
(
"ptr"
,
fmt
.
Sprintf
(
"%p"
,
value
)))
return
nil
}
func
(
c
*
Cache
)
Get
(
key
interface
{})
(
value
interface
{},
err
error
)
{
val
,
ok
:=
c
.
cache
.
Get
(
key
)
if
ok
{
v
:=
val
.
(
*
item
)
if
v
.
expiredAt
.
Before
(
time
.
Now
())
{
c
.
Remove
(
key
)
c
.
logger
.
Debug
(
"Expired"
,
zap
.
String
(
"key"
,
fmt
.
Sprintf
(
"%v"
,
key
)),
zap
.
String
(
"ptr"
,
fmt
.
Sprintf
(
"%p"
,
v
.
value
)))
return
nil
,
ErrNotFound
}
c
.
logger
.
Debug
(
"Hit"
,
zap
.
String
(
"key"
,
fmt
.
Sprintf
(
"%v"
,
key
)),
zap
.
String
(
"ptr"
,
fmt
.
Sprintf
(
"%p"
,
v
.
value
)))
return
v
.
value
,
nil
}
c
.
logger
.
Debug
(
"Miss"
,
zap
.
String
(
"key"
,
fmt
.
Sprintf
(
"%v"
,
key
)))
return
nil
,
ErrNotFound
}
func
(
c
*
Cache
)
Remove
(
key
interface
{})
(
err
error
)
{
present
:=
c
.
cache
.
Remove
(
key
)
c
.
logger
.
Debug
(
"Remove"
,
zap
.
String
(
"key"
,
fmt
.
Sprintf
(
"%v"
,
key
)))
if
!
present
{
err
=
ErrNotFound
}
return
}
This diff is collapsed.
Click to expand it.
pkg/cache/cache_test.go
0 → 100644
+
82
−
0
View file @
226c80ab
package
cache
import
(
"errors"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func
TestCache
(
t
*
testing
.
T
)
{
t
.
Run
(
"Simple"
,
func
(
t
*
testing
.
T
)
{
c
:=
NewCache
(
10
,
0
)
{
val
,
err
:=
c
.
Get
(
"test_key"
)
require
.
Error
(
t
,
err
)
assert
.
True
(
t
,
errors
.
Is
(
err
,
ErrNotFound
))
assert
.
Nil
(
t
,
val
)
}
{
err
:=
c
.
Set
(
"test_key"
,
"test_val"
)
require
.
NoError
(
t
,
err
)
val
,
err
:=
c
.
Get
(
"test_key"
)
require
.
NoError
(
t
,
err
)
assert
.
Equal
(
t
,
"test_val"
,
val
.
(
string
))
}
{
err
:=
c
.
Remove
(
"test_key"
)
require
.
NoError
(
t
,
err
)
val
,
err
:=
c
.
Get
(
"test_key"
)
assert
.
True
(
t
,
errors
.
Is
(
err
,
ErrNotFound
))
assert
.
Nil
(
t
,
val
)
}
})
t
.
Run
(
"Value Evicted"
,
func
(
t
*
testing
.
T
)
{
c
:=
NewCache
(
1
,
0
)
{
err
:=
c
.
Set
(
"test_key_1"
,
"test_val_1"
)
require
.
NoError
(
t
,
err
)
val
,
err
:=
c
.
Get
(
"test_key_1"
)
require
.
NoError
(
t
,
err
)
assert
.
Equal
(
t
,
"test_val_1"
,
val
.
(
string
))
}
{
err
:=
c
.
Set
(
"test_key_2"
,
"test_val_2"
)
require
.
NoError
(
t
,
err
)
val
,
err
:=
c
.
Get
(
"test_key_1"
)
assert
.
True
(
t
,
errors
.
Is
(
err
,
ErrNotFound
))
assert
.
Nil
(
t
,
val
)
val
,
err
=
c
.
Get
(
"test_key_2"
)
require
.
NoError
(
t
,
err
)
assert
.
Equal
(
t
,
"test_val_2"
,
val
.
(
string
))
}
})
t
.
Run
(
"TTL expired"
,
func
(
t
*
testing
.
T
)
{
c
:=
NewCache
(
10
,
10
*
time
.
Millisecond
)
err
:=
c
.
Set
(
"test_key"
,
"test_val"
)
require
.
NoError
(
t
,
err
)
val
,
err
:=
c
.
Get
(
"test_key"
)
require
.
NoError
(
t
,
err
)
assert
.
Equal
(
t
,
"test_val"
,
val
.
(
string
))
time
.
Sleep
(
15
*
time
.
Millisecond
)
val
,
err
=
c
.
Get
(
"test_key"
)
assert
.
True
(
t
,
errors
.
Is
(
err
,
ErrNotFound
))
assert
.
Nil
(
t
,
val
)
})
}
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment