This commit is contained in:
Sergey Melnikov 2024-01-17 11:41:47 +03:00
commit c034dbdae0
24 changed files with 1774 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
vendor

20
.golangci.yml Normal file
View File

@ -0,0 +1,20 @@
# https://golangci-lint.run/usage/configuration/#config-file
run:
allow-parallel-runners: true
linters:
enable:
- megacheck
- govet
- gocritic
- gocyclo
- lll
- exportloopref
disable-all: false
disable:
- scopelint
presets:
- bugs
- unused
fast: false

36
docker-compose.yml Normal file
View File

@ -0,0 +1,36 @@
version: "3"
services:
cassandra:
container_name: cassandra
environment:
- CASSANDRA_KEYSPACE=userprofileservice
- TABLE_NAME=users
build:
context: .
dockerfile: ./docker/db/Dockerfile
command: /cassandra-init.sh
ports:
- 9042:9042
service:
container_name: service
environment:
- CASSANDRA_HOST=cassandra
- CASSANDRA_PORT=9042
- CASSANDRA_KEYSPACE=userprofileservice
- CASSANDRA_CONSISTANCY=LOCAL_QUORUM
- TABLE_NAME=users
- BIDN_SERVICE=:8080
- AWS_REGION=fake-region
- AWS_ACCESS_KEY_ID=fake-key
- AWS_SECRET_ACCESS_KEY=fake-secret
- BUCKET_NAME=fake-bucket
- AWS_ACL=public-read
build:
context: .
dockerfile: ./docker/api/Dockerfile
ports:
- "8080:8080"
networks:
- default
depends_on:
- cassandra

12
docker/api/Dockerfile Normal file
View File

@ -0,0 +1,12 @@
FROM golang as builder
RUN mkdir -p /build/src
WORKDIR /build
COPY ./src/. /build/src/
COPY ./go.mod /build/
RUN go mod vendor && go test ./src/ && CGO_ENABLED=0 GOOS=linux go build -o apiservice ./src/main.go
FROM alpine
ENV TZ=Europe/Helsinki
RUN apk add --no-cache tzdata && ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
COPY --from=builder /build/apiservice .
ENTRYPOINT [ "./apiservice" ]

3
docker/db/Dockerfile Normal file
View File

@ -0,0 +1,3 @@
FROM cassandra
COPY docker/db/cassandra-init.sh /cassandra-init.sh
RUN chmod +x /cassandra-init.sh

View File

@ -0,0 +1,10 @@
#!/bin/bash
cat >/import.cql <<EOF
CREATE keyspace IF NOT EXISTS ${CASSANDRA_KEYSPACE} with replication = {'class':'SimpleStrategy', 'replication_factor' : 1};
USE ${CASSANDRA_KEYSPACE};
CREATE TABLE IF NOT EXISTS ${TABLE_NAME}(id TIMEUUID, firstname TEXT, lastname TEXT, birthday date, currentlocation TEXT, userpicture TEXT, certificate TEXT, PRIMARY KEY(id));
EOF
bash -c 'sleep 60; cqlsh -f /import.cql;' &
exec /docker-entrypoint.sh "$@";

15
go.mod Normal file
View File

@ -0,0 +1,15 @@
module userprofileservice
go 1.16
require (
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect
github.com/aws/aws-sdk-go v1.38.37
github.com/go-ozzo/ozzo-validation v3.6.0+incompatible
github.com/gocql/gocql v0.0.0-20210504150947-558dfae50b5d
github.com/golang/mock v1.5.0
github.com/gorilla/mux v1.8.0
github.com/swaggo/http-swagger v1.0.0
github.com/swaggo/swag v1.7.0
)

154
go.sum Normal file
View File

@ -0,0 +1,154 @@
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d h1:Byv0BzEl3/e6D5CLfI0j/7hiIEtvGVFPCZ7Ei2oq8iQ=
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
github.com/aws/aws-sdk-go v1.38.37 h1:Eh3/jog9t2NhGcOi086NRfRqVKduRXkaH6svI8yF1Jg=
github.com/aws/aws-sdk-go v1.38.37/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932 h1:mXoPYz/Ul5HYEDvkta6I8/rnYM5gSdSV2tJ6XbZuEtY=
github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932/go.mod h1:NOuUCSz6Q9T7+igc/hlvDOUdtWKryOrtFyIVABv/p7k=
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY=
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
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/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-chi/chi v4.0.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ=
github.com/go-openapi/jsonpointer v0.19.3 h1:gihV7YNZK1iK6Tgwwsxo2rJbD1GTbdm72325Bq8FI3w=
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY=
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
github.com/go-openapi/jsonreference v0.19.4 h1:3Vw+rh13uq2JFNxgnMTGE1rnoieU9FmyE1gvnyylsYg=
github.com/go-openapi/jsonreference v0.19.4/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg=
github.com/go-openapi/jsonreference v0.19.5 h1:1WJP/wi4OjB4iV8KVbH73rQaoialJrqv8gitZLxGLtM=
github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg=
github.com/go-openapi/spec v0.19.14 h1:r4fbYFo6N4ZelmSX8G6p+cv/hZRXzcuqQIADGT1iNKM=
github.com/go-openapi/spec v0.19.14/go.mod h1:gwrgJS15eCUgjLpMjBJmbZezCsw88LmgeEip0M63doA=
github.com/go-openapi/spec v0.20.0 h1:HGLc8AJ7ynOxwv0Lq4TsnwLsWMawHAYiJIFzbcML86I=
github.com/go-openapi/spec v0.20.0/go.mod h1:+81FIL1JwC5P3/Iuuozq3pPE9dXdIEGxFutcFKaVbmU=
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
github.com/go-openapi/swag v0.19.11 h1:RFTu/dlFySpyVvJDfp/7674JY4SDglYWKztbiIGFpmc=
github.com/go-openapi/swag v0.19.11/go.mod h1:Uc0gKkdR+ojzsEpjh39QChyu92vPgIr72POcgHMAgSY=
github.com/go-openapi/swag v0.19.12 h1:Bc0bnY2c3AoF7Gc+IMIAQQsD8fLHjHpc19wXvYuayQI=
github.com/go-openapi/swag v0.19.12/go.mod h1:eFdyEBkTdoAf/9RXBvj4cr1nH7GD8Kzo5HTt47gr72M=
github.com/go-ozzo/ozzo-validation v3.6.0+incompatible h1:msy24VGS42fKO9K1vLz82/GeYW1cILu7Nuuj1N3BBkE=
github.com/go-ozzo/ozzo-validation v3.6.0+incompatible/go.mod h1:gsEKFIVnabGBt6mXmxK0MoFy+cZoTJY6mu5Ll3LVLBU=
github.com/gocql/gocql v0.0.0-20210504150947-558dfae50b5d h1:QbC6FK7LDUrgkZPYdtaKiXGCIw41fdk39H+vrTgHFMs=
github.com/gocql/gocql v0.0.0-20210504150947-558dfae50b5d/go.mod h1:DL0ekTmBSTdlNF25Orwt/JMzqIq3EJ4MVa/J/uK64OY=
github.com/golang/mock v1.5.0 h1:jlYHihg//f7RRwuPfptm04yp4s7O6Kw8EZiVYIGcH0g=
github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
github.com/golang/snappy v0.0.0-20170215233205-553a64147049 h1:K9KHZbXKpGydfDN0aZrsoHpLJlZsBrGMFWbgLDGnPZk=
github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs=
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed h1:5upAirOpQc1Q53c0bnx2ufif5kANL7bfZWcc6VJWJd8=
github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4=
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
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 h1:VkoXIwSboBpnk99O/KFauAEILuNHv5DVFKZMBN/gUgw=
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/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e h1:hB2xlXdHp/pmPZq0y3QnmWAArdw9PqbmotexnWx/FU8=
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA=
github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag=
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
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=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
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 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/swaggo/files v0.0.0-20190704085106-630677cd5c14 h1:PyYN9JH5jY9j6av01SpfRMb+1DWg/i3MbGOKPxJ2wjM=
github.com/swaggo/files v0.0.0-20190704085106-630677cd5c14/go.mod h1:gxQT6pBGRuIGunNf/+tSOB5OHvguWi8Tbt82WOkf35E=
github.com/swaggo/http-swagger v1.0.0 h1:ksYgVBCYmAaxFsGVGojlPROgYfiQQSllETTWMtHJHTo=
github.com/swaggo/http-swagger v1.0.0/go.mod h1:cKIcshBU9yEAnfWv6ZzVKSsEf8h5ozxB8/zHQWyOQ/8=
github.com/swaggo/swag v1.7.0 h1:5bCA/MTLQoIqDXXyHfOpMeDvL9j68OY/udlK4pQoo4E=
github.com/swaggo/swag v1.7.0/go.mod h1:BdPIL73gvS9NBsdi7M1JOxLvlbfvNRaBP8m6WT6Aajo=
github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M=
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
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 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b h1:uwuIcX0g4Yl1NC5XAz37xsr2lTtcqevgzYNVt49waME=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201207224615-747e23833adb h1:xj2oMIbduz83x7tzglytWT7spn6rP+9hvKjTpro6/pM=
golang.org/x/net v0.0.0-20201207224615-747e23833adb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.4 h1:0YWbFKbhXG/wIiuHDSKpS0Iy7FSA+u45VtBMfQcFTTc=
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e h1:FDhOuMEY4JVRztM/gsbk+IKUQ8kj74bxZrgw87eMMVc=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e h1:aZzprAO9/8oim3qStq3wc1Xuxx4QmAGriC4VU4ojemQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20201120155355-20be4ac4bd6e h1:t96dS3DO8DGjawSLJL/HIdz8CycAd2v07XxqB3UPTi0=
golang.org/x/tools v0.0.0-20201120155355-20be4ac4bd6e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201208062317-e652b2f42cc7 h1:2OSu5vYyX4LVqZAtqZXnFEcN26SDKIJYlEVIRl1tj8U=
golang.org/x/tools v0.0.0-20201208062317-e652b2f42cc7/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898 h1:/atklqdjdhuosWIl6AIbOeHJjicWYPqR9bpxqxYG2pA=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
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/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
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.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

21
readme.md Normal file
View File

@ -0,0 +1,21 @@
# INIT SERVICE
docker-compose up --build
# AFTER STARTING WILL BE AVAILABLE POINTS
GET http://localhost:8080/user/e2bb359d-b347-11eb-b01c-0242ac120003 - with parameter will return user info, url parameter [uuid]
POST http://localhost:8080/user - add or change user, request in json format
- {
- "firstname":"John",
- "lastname":"Dou",
- "birthday":"1972-10-06",
- "currentlocation":"Helsinki"
- }
with id UUID in request user will be changed
POST http://localhost:8080/upload/userpicture - add image or document to user record with form-data request, url parameter [userpicture|certificate]
# SWAGGER
http://localhost:8080/swagger/

68
src/aws/aws.go Normal file
View File

@ -0,0 +1,68 @@
package aws
import (
"io"
"userprofileservice/src/utils"
"github.com/aws/aws-sdk-go/aws/session"
)
type AWS struct {
aws_region string
aws_access_key_id string
aws_secret_ascess_key string
bucket_name string
acl string
Session *session.Session
}
// InitDB create ClusterConfig and init DB struct
func InitAWS() *AWS {
aws := &AWS{
aws_region: utils.GetEnv("AWS_REGION", "fake-region"),
aws_access_key_id: utils.GetEnv("AWS_ACCESS_KEY_ID ", "fake-key"),
aws_secret_ascess_key: utils.GetEnv("AWS_SECRET_ACCESS_KEY ", "fake-secret"),
bucket_name: utils.GetEnv("BUCKET_NAME ", "fake-bucket"),
acl: utils.GetEnv("AWS_ACL ", "public-read"),
}
aws.connectAWS()
return aws
}
// connectAWS connect to AWS if not connected
// Comment in for the test and not connect to AWS
func (a *AWS) connectAWS() {
// if a.Session == nil {
// sess, err := session.NewSession(
// &aws.Config{
// Region: aws.String(a.aws_region),
// Credentials: credentials.NewStaticCredentials(
// a.aws_access_key_id,
// a.aws_secret_ascess_key,
// "", // a token will be created when the session it's used.
// ),
// })
// if err != nil {
// panic(err)
// }
// a.Session = sess
// }
}
// uploadFile upload file to AWS and return filename
// comment in for fake upload
func (a *AWS) UploadFile(file io.Reader, filename string) (string, error) {
// uploader := s3manager.NewUploader(a.Session)
// _, err := uploader.Upload(&s3manager.UploadInput{
// Bucket: aws.String(a.bucket_name),
// ACL: aws.String(a.acl),
// Key: aws.String(filename),
// Body: file,
// })
// if err != nil {
// return "", err
// }
filepath := "https://" + a.bucket_name + "." + "s3-" + a.aws_region + ".amazonaws.com/" + filename
return filepath, nil
}

View File

@ -0,0 +1,23 @@
package controller
import (
"userprofileservice/src/model"
"userprofileservice/src/structs"
"github.com/gocql/gocql"
)
//go:generate mockgen -source=controller.go -destination=mocks/mock.go
type IController interface {
CreateUser(user *structs.USER) error
GetUser(id gocql.UUID) (*structs.USER, error)
}
type Controller struct {
IController
}
func NewController(model *model.Model) *Controller {
return &Controller{IController: NewUserController(model)}
}

View File

@ -0,0 +1,65 @@
// Code generated by MockGen. DO NOT EDIT.
// Source: controller.go
// Package mock_controller is a generated GoMock package.
package mock_controller
import (
reflect "reflect"
structs "userprofileservice/src/structs"
gocql "github.com/gocql/gocql"
gomock "github.com/golang/mock/gomock"
)
// MockIController is a mock of IController interface.
type MockIController struct {
ctrl *gomock.Controller
recorder *MockIControllerMockRecorder
}
// MockIControllerMockRecorder is the mock recorder for MockIController.
type MockIControllerMockRecorder struct {
mock *MockIController
}
// NewMockIController creates a new mock instance.
func NewMockIController(ctrl *gomock.Controller) *MockIController {
mock := &MockIController{ctrl: ctrl}
mock.recorder = &MockIControllerMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockIController) EXPECT() *MockIControllerMockRecorder {
return m.recorder
}
// CreateUser mocks base method.
func (m *MockIController) CreateUser(user *structs.USER) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "CreateUser", user)
ret0, _ := ret[0].(error)
return ret0
}
// CreateUser indicates an expected call of CreateUser.
func (mr *MockIControllerMockRecorder) CreateUser(user interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateUser", reflect.TypeOf((*MockIController)(nil).CreateUser), user)
}
// GetUser mocks base method.
func (m *MockIController) GetUser(id gocql.UUID) (*structs.USER, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetUser", id)
ret0, _ := ret[0].(*structs.USER)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// GetUser indicates an expected call of GetUser.
func (mr *MockIControllerMockRecorder) GetUser(id interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUser", reflect.TypeOf((*MockIController)(nil).GetUser), id)
}

View File

@ -0,0 +1,24 @@
package controller
import (
"userprofileservice/src/model"
"userprofileservice/src/structs"
"github.com/gocql/gocql"
)
type UserController struct {
model *model.Model
}
func NewUserController(model *model.Model) *UserController {
return &UserController{model: model}
}
func (u *UserController) CreateUser(user *structs.USER) error {
return u.model.CreateUser(user)
}
func (u *UserController) GetUser(id gocql.UUID) (*structs.USER, error) {
return u.model.GetUser(id)
}

314
src/docs/docs.go Normal file
View File

@ -0,0 +1,314 @@
// GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
// This file was generated by swaggo/swag
package docs
import (
"bytes"
"encoding/json"
"strings"
"github.com/alecthomas/template"
"github.com/swaggo/swag"
)
var doc = `{
"schemes": {{ marshal .Schemes }},
"swagger": "2.0",
"info": {
"description": "{{.Description}}",
"title": "{{.Title}}",
"contact": {},
"version": "{{.Version}}"
},
"host": "{{.Host}}",
"basePath": "{{.BasePath}}",
"paths": {
"/upload/{field}": {
"post": {
"description": "upload file for user, one by one",
"consumes": [
"multipart/form-data"
],
"produces": [
"application/json"
],
"tags": [
"uploadfile"
],
"summary": "Upload File",
"operationId": "upload-file",
"parameters": [
{
"type": "string",
"description": "User struct field",
"name": "field",
"in": "path",
"required": true
},
{
"type": "string",
"description": "UUID",
"name": "id",
"in": "formData",
"required": true
},
{
"type": "string",
"description": "File name",
"name": "file_name",
"in": "formData",
"required": true
},
{
"type": "file",
"description": "File data",
"name": "input",
"in": "formData",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/structs.USER"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/structs.ANSWERAPI"
}
},
"404": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/structs.ANSWERAPI"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/structs.ANSWERAPI"
}
},
"default": {
"description": "",
"schema": {
"$ref": "#/definitions/structs.ANSWERAPI"
}
}
}
}
},
"/user": {
"post": {
"description": "create or update user",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"createuser"
],
"summary": "Create User",
"operationId": "create-user",
"parameters": [
{
"description": "User data",
"name": "input",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/structs.USER"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/structs.USER"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/structs.ANSWERAPI"
}
},
"404": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/structs.ANSWERAPI"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/structs.ANSWERAPI"
}
},
"default": {
"description": "",
"schema": {
"$ref": "#/definitions/structs.ANSWERAPI"
}
}
}
}
},
"/user/{id}": {
"get": {
"description": "get user by uuid",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"getuser"
],
"summary": "Get User by UID",
"operationId": "get-user-by-uid",
"parameters": [
{
"type": "string",
"description": "User ID",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/structs.USER"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/structs.ANSWERAPI"
}
},
"404": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/structs.ANSWERAPI"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/structs.ANSWERAPI"
}
},
"default": {
"description": "",
"schema": {
"$ref": "#/definitions/structs.ANSWERAPI"
}
}
}
}
}
},
"definitions": {
"structs.ANSWERAPI": {
"type": "object",
"properties": {
"additional": {
"type": "string"
},
"data": {
"type": "object"
},
"error": {
"type": "string"
},
"status": {
"type": "boolean"
}
}
},
"structs.USER": {
"type": "object",
"properties": {
"birthday": {
"type": "string"
},
"certificate": {
"type": "string"
},
"currentlocation": {
"type": "string"
},
"firstname": {
"type": "string"
},
"id": {
"type": "string"
},
"lastname": {
"type": "string"
},
"userpicture": {
"type": "string"
}
}
}
}
}`
type swaggerInfo struct {
Version string
Host string
BasePath string
Schemes []string
Title string
Description string
}
// SwaggerInfo holds exported Swagger Info so clients can modify it
var SwaggerInfo = swaggerInfo{
Version: "0.1",
Host: "localhost:8080",
BasePath: "/",
Schemes: []string{},
Title: "User Profile Service API",
Description: "API Server for User Profile Service",
}
type s struct{}
func (s *s) ReadDoc() string {
sInfo := SwaggerInfo
sInfo.Description = strings.Replace(sInfo.Description, "\n", "\\n", -1)
t, err := template.New("swagger_info").Funcs(template.FuncMap{
"marshal": func(v interface{}) string {
a, _ := json.Marshal(v)
return string(a)
},
}).Parse(doc)
if err != nil {
return doc
}
var tpl bytes.Buffer
if err := t.Execute(&tpl, sInfo); err != nil {
return doc
}
return tpl.String()
}
func init() {
swag.Register(swag.Name, &s{})
}

252
src/docs/swagger.json Normal file
View File

@ -0,0 +1,252 @@
{
"swagger": "2.0",
"info": {
"description": "API Server for User Profile Service",
"title": "User Profile Service API",
"contact": {},
"version": "0.1"
},
"host": "localhost:8080",
"basePath": "/",
"paths": {
"/upload/{field}": {
"post": {
"description": "upload file for user, one by one",
"consumes": [
"multipart/form-data"
],
"produces": [
"application/json"
],
"tags": [
"uploadfile"
],
"summary": "Upload File",
"operationId": "upload-file",
"parameters": [
{
"type": "string",
"description": "User struct field",
"name": "field",
"in": "path",
"required": true
},
{
"type": "string",
"description": "UUID",
"name": "id",
"in": "formData",
"required": true
},
{
"type": "string",
"description": "File name",
"name": "file_name",
"in": "formData",
"required": true
},
{
"type": "file",
"description": "File data",
"name": "input",
"in": "formData",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/structs.USER"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/structs.ANSWERAPI"
}
},
"404": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/structs.ANSWERAPI"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/structs.ANSWERAPI"
}
},
"default": {
"description": "",
"schema": {
"$ref": "#/definitions/structs.ANSWERAPI"
}
}
}
}
},
"/user": {
"post": {
"description": "create or update user",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"createuser"
],
"summary": "Create User",
"operationId": "create-user",
"parameters": [
{
"description": "User data",
"name": "input",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/structs.USER"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/structs.USER"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/structs.ANSWERAPI"
}
},
"404": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/structs.ANSWERAPI"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/structs.ANSWERAPI"
}
},
"default": {
"description": "",
"schema": {
"$ref": "#/definitions/structs.ANSWERAPI"
}
}
}
}
},
"/user/{id}": {
"get": {
"description": "get user by uuid",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"getuser"
],
"summary": "Get User by UID",
"operationId": "get-user-by-uid",
"parameters": [
{
"type": "string",
"description": "User ID",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/structs.USER"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/structs.ANSWERAPI"
}
},
"404": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/structs.ANSWERAPI"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/structs.ANSWERAPI"
}
},
"default": {
"description": "",
"schema": {
"$ref": "#/definitions/structs.ANSWERAPI"
}
}
}
}
}
},
"definitions": {
"structs.ANSWERAPI": {
"type": "object",
"properties": {
"additional": {
"type": "string"
},
"data": {
"type": "object"
},
"error": {
"type": "string"
},
"status": {
"type": "boolean"
}
}
},
"structs.USER": {
"type": "object",
"properties": {
"birthday": {
"type": "string"
},
"certificate": {
"type": "string"
},
"currentlocation": {
"type": "string"
},
"firstname": {
"type": "string"
},
"id": {
"type": "string"
},
"lastname": {
"type": "string"
},
"userpicture": {
"type": "string"
}
}
}
}
}

168
src/docs/swagger.yaml Normal file
View File

@ -0,0 +1,168 @@
basePath: /
definitions:
structs.ANSWERAPI:
properties:
additional:
type: string
data:
type: object
error:
type: string
status:
type: boolean
type: object
structs.USER:
properties:
birthday:
type: string
certificate:
type: string
currentlocation:
type: string
firstname:
type: string
id:
type: string
lastname:
type: string
userpicture:
type: string
type: object
host: localhost:8080
info:
contact: {}
description: API Server for User Profile Service
title: User Profile Service API
version: "0.1"
paths:
/upload/{field}:
post:
consumes:
- multipart/form-data
description: upload file for user, one by one
operationId: upload-file
parameters:
- description: User struct field
in: path
name: field
required: true
type: string
- description: UUID
in: formData
name: id
required: true
type: string
- description: File name
in: formData
name: file_name
required: true
type: string
- description: File data
in: formData
name: input
required: true
type: file
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/structs.USER'
"400":
description: Bad Request
schema:
$ref: '#/definitions/structs.ANSWERAPI'
"404":
description: Bad Request
schema:
$ref: '#/definitions/structs.ANSWERAPI'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/structs.ANSWERAPI'
default:
description: ""
schema:
$ref: '#/definitions/structs.ANSWERAPI'
summary: Upload File
tags:
- uploadfile
/user:
post:
consumes:
- application/json
description: create or update user
operationId: create-user
parameters:
- description: User data
in: body
name: input
required: true
schema:
$ref: '#/definitions/structs.USER'
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/structs.USER'
"400":
description: Bad Request
schema:
$ref: '#/definitions/structs.ANSWERAPI'
"404":
description: Bad Request
schema:
$ref: '#/definitions/structs.ANSWERAPI'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/structs.ANSWERAPI'
default:
description: ""
schema:
$ref: '#/definitions/structs.ANSWERAPI'
summary: Create User
tags:
- createuser
/user/{id}:
get:
consumes:
- application/json
description: get user by uuid
operationId: get-user-by-uid
parameters:
- description: User ID
in: path
name: id
required: true
type: string
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/structs.USER'
"400":
description: Bad Request
schema:
$ref: '#/definitions/structs.ANSWERAPI'
"404":
description: Bad Request
schema:
$ref: '#/definitions/structs.ANSWERAPI'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/structs.ANSWERAPI'
default:
description: ""
schema:
$ref: '#/definitions/structs.ANSWERAPI'
summary: Get User by UID
tags:
- getuser
swagger: "2.0"

30
src/main.go Normal file
View File

@ -0,0 +1,30 @@
package main
import (
"log"
"net/http"
"userprofileservice/src/controller"
"userprofileservice/src/model"
"userprofileservice/src/utils"
"userprofileservice/src/view"
)
func main() {
// catch panic
defer func() {
if r := recover(); r != nil {
log.Println("Recovered in f ", r)
}
}()
db := model.NewCassandra()
model := model.NewModel(db)
controller := controller.NewController(model)
view := view.NewView(controller)
bind := utils.GetEnv("BIDN_SERVICE", ":8080")
log.Printf("start service on port %s", bind)
if err := http.ListenAndServe(bind, view.InitRouting()); err != nil {
log.Panic(err)
}
}

61
src/main_test.go Normal file
View File

@ -0,0 +1,61 @@
package main
import (
"log"
"testing"
"userprofileservice/src/controller"
mock_controller "userprofileservice/src/controller/mocks"
"userprofileservice/src/structs"
"github.com/gocql/gocql"
"github.com/golang/mock/gomock"
)
var (
uid = gocql.TimeUUID()
user = &structs.USER{
Firstname: "Sergey",
Lastname: "Melnikov",
Birthday: "1972-10-06",
}
)
func TestCreateUser(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
mocklUserController := mock_controller.NewMockIController(ctrl)
mocklUserController.
EXPECT().
CreateUser(user).
Return(nil)
testCreateUser(mocklUserController)
}
func testCreateUser(c controller.IController) {
err := c.CreateUser(user)
if err != nil {
log.Fatalf("Wrong added user %v", err)
}
log.Printf("User added %v", user)
}
func TestGetUser(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
mocklUserController := mock_controller.NewMockIController(ctrl)
mocklUserController.
EXPECT().
GetUser(uid).
Return(user, nil)
testGetUser(mocklUserController)
}
func testGetUser(c controller.IController) {
u, err := c.GetUser(uid)
if err != nil {
log.Fatalf("Wrong get user %v", err)
}
log.Printf("Get user %v", u)
}

42
src/model/cassandra.go Normal file
View File

@ -0,0 +1,42 @@
package model
import (
"log"
"time"
"userprofileservice/src/utils"
"github.com/gocql/gocql"
)
func NewCassandra() *gocql.Session {
conf := gocql.NewCluster(utils.GetEnv("CASSANDRA_HOST", "cassandra"))
conf.Port = utils.ParsePort(utils.GetEnv("CASSANDRA_PORT", "9042"))
conf.Keyspace = utils.GetEnv("CASSANDRA_KEYSPACE", "userprofileservice")
conf.Consistency = gocql.ParseConsistency(utils.GetEnv("CASSANDRA_CONSISTANCY", "LOCAL_QUORUM"))
sess, err := conf.CreateSession()
if err != nil {
log.Printf("waiting for cassandra session, %s", err.Error())
// added ticker for long cassandra initializing
ticker := time.NewTicker(time.Second * 10)
defer ticker.Stop()
timeout := time.Second * 120
timeoutExceeded := time.After(timeout)
for {
select {
case <-timeoutExceeded:
log.Panicf("cassandra connection failed after %s timeout", timeout)
case <-ticker.C:
sess, err = conf.CreateSession()
if err != nil {
log.Printf("waiting for cassandra session, %s", err.Error())
} else {
log.Printf("cassandra session init")
return sess
}
}
}
}
return sess
}

22
src/model/model.go Normal file
View File

@ -0,0 +1,22 @@
package model
import (
"userprofileservice/src/structs"
"github.com/gocql/gocql"
)
type IModel interface {
CreateUser(user *structs.USER) error
GetUser(id gocql.UUID) (*structs.USER, error)
}
type Model struct {
IModel
}
func NewModel(db *gocql.Session) *Model {
return &Model{
IModel: NewUserModel(db),
}
}

89
src/model/user_model.go Normal file
View File

@ -0,0 +1,89 @@
package model
import (
"userprofileservice/src/structs"
"userprofileservice/src/utils"
"github.com/gocql/gocql"
)
type ModelCassandra struct {
db *gocql.Session
TableName string
}
func NewUserModel(db *gocql.Session) *ModelCassandra {
return &ModelCassandra{
db: db,
TableName: utils.GetEnv("TABLE_NAME", "users"),
}
}
// CreateUser create or update user
func (c *ModelCassandra) CreateUser(user *structs.USER) error {
var query *gocql.Query
// if user ID empty - create new user, else update user fields
if user.Id.Clock() == 0 {
user.Id = gocql.TimeUUID()
query = c.db.Query(`
INSERT INTO `+c.TableName+` (
id,
firstname,
lastname,
birthday,
currentlocation,
userpicture,
certificate
)
VALUES (?, ?, ?, ?, ?, ?, ?)
`,
user.Id,
user.Firstname,
user.Lastname,
user.Birthday,
user.Currentlocation,
user.Userpicture,
user.Certificate)
} else {
query = c.db.Query(`
UPDATE `+c.TableName+` SET
firstname = ?,
lastname = ?,
birthday = ?,
currentlocation = ?,
userpicture = ?,
certificate = ?
WHERE id = ?
IF EXISTS
`,
user.Firstname,
user.Lastname,
user.Birthday,
user.Currentlocation,
user.Userpicture,
user.Certificate,
user.Id)
}
err := query.Exec()
return err
}
// GetUser return user data by id
func (c *ModelCassandra) GetUser(id gocql.UUID) (*structs.USER, error) {
q := `SELECT id, firstname, lastname, currentlocation, birthday, userpicture, certificate
FROM ` + c.TableName + ` WHERE id = ? LIMIT 1`
user := &structs.USER{}
err := c.db.Query(q, id).Consistency(gocql.One).Scan(
&user.Id,
&user.Firstname,
&user.Lastname,
&user.Currentlocation,
&user.Birthday,
&user.Userpicture,
&user.Certificate,
)
return user, err
}

44
src/structs/structs.go Normal file
View File

@ -0,0 +1,44 @@
package structs
import (
"strings"
valid "github.com/go-ozzo/ozzo-validation"
"github.com/gocql/gocql"
)
// User struct for user
type USER struct {
Id gocql.UUID `cql:"id" json:"id,omitempty"`
Firstname string `cql:"firstname" json:"firstname"`
Lastname string `cql:"lastname" json:"lastname"`
Birthday string `cql:"birthday" json:"birthday"`
Currentlocation string `cql:"currentlocation" json:"currentlocation"`
Userpicture string `cql:"userpicture" json:"userpicture"`
Certificate string `cql:"certificate" json:"certificate"`
}
// Validate check Birthday for format yyyy-mm-dd
func (u *USER) Validate() error {
var fieldRules []*valid.FieldRules
fieldRules = append(fieldRules, valid.Field(&u.Birthday, valid.Date("2006-01-02")))
return valid.ValidateStruct(u, fieldRules...)
}
// ANSWERAPI struct for api answer
type ANSWERAPI struct {
STATUS bool `json:"status"`
DATA interface{} `json:"data"`
ERROR string `json:"error"`
ADDITIONAL string `json:"additional"`
}
// Prepare to bool and uppercase errors
func (a *ANSWERAPI) Prepare() {
if a.ERROR != "" {
a.STATUS = false
} else {
a.STATUS = true
}
a.ERROR = strings.ToUpper(a.ERROR)
}

45
src/utils/utils.go Normal file
View File

@ -0,0 +1,45 @@
package utils
import (
"os"
"reflect"
"strconv"
"strings"
"userprofileservice/src/structs"
)
// GetEnv look up ENV settings
func GetEnv(key, fallback string) string {
if value, ok := os.LookupEnv(key); ok {
return value
}
return fallback
}
// ParsePort return port for cassandra
func ParsePort(p string) int {
i, err := strconv.Atoi(p)
if err != nil {
return 9042
}
return i
}
// AvailableField check field struct and add value
func AvailableAndAddValue(v *structs.USER, field, link string) bool {
s := reflect.ValueOf(v).Elem()
typeOfT := s.Type()
for i := 0; i < s.NumField(); i++ {
name := typeOfT.Field(i).Name
if strings.ToLower(name) == field {
if link != "" {
reflect.ValueOf(v).Elem().FieldByName(name).SetString(link)
}
return true
}
}
return false
}

255
src/view/api.go Normal file
View File

@ -0,0 +1,255 @@
package view
import (
"encoding/json"
"errors"
"net/http"
"userprofileservice/src/aws"
"userprofileservice/src/controller"
"userprofileservice/src/structs"
"userprofileservice/src/utils"
_ "userprofileservice/src/docs"
"github.com/gocql/gocql"
"github.com/gorilla/mux"
httpSwagger "github.com/swaggo/http-swagger"
)
type API struct {
services *controller.Controller
aws *aws.AWS
}
func NewView(controller *controller.Controller) *API {
a := &API{
services: controller,
aws: aws.InitAWS(),
}
return a
}
func (a *API) InitRouting() *mux.Router {
routing := mux.NewRouter()
routing.PathPrefix("/swagger").Handler(httpSwagger.WrapHandler)
routing.HandleFunc("/", a.handleRoot()).Methods("GET")
routing.HandleFunc("/user/{id:[0-9a-z]{8}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{12}}",
a.getUser()).Methods("GET")
routing.HandleFunc("/user", a.createUser()).Methods("POST")
routing.HandleFunc("/upload/{field:\\w+}", a.uploadFiles()).Methods("POST")
routing.NotFoundHandler = http.HandlerFunc(a.notFound)
routing.MethodNotAllowedHandler = http.HandlerFunc(a.notAllowed)
return routing
}
// swag init -g src/view/api.go -o src/docs/
// @title User Profile Service API
// @version 0.1
// @description API Server for User Profile Service
// @host localhost:8080
// @BasePath /
// @Summary Get User by UID
// @Tags getuser
// @Description get user by uuid
// @ID get-user-by-uid
// @Accept json
// @Produce json
// @Param id path string true "User ID"
// @Success 200 {object} structs.USER
// @Failure 400,404 {object} structs.ANSWERAPI
// @Failure 500 {object} structs.ANSWERAPI
// @Failure default {object} structs.ANSWERAPI
// @Router /user/{id} [get]
// getUser return one user data in json
func (a *API) getUser() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
uid, err := gocql.ParseUUID(vars["id"])
if err != nil {
a.respond(w, r, http.StatusBadRequest, nil, err, "")
return
}
user, err := a.services.GetUser(uid)
if err != nil {
a.respond(w, r, http.StatusInternalServerError, nil, err, "")
return
}
a.respond(w, r, http.StatusOK, user, nil, "")
}
}
// @Summary Create User
// @Tags createuser
// @Description create or update user
// @ID create-user
// @Accept json
// @Produce json
// @Param input body structs.USER true "User data"
// @Success 200 {object} structs.USER
// @Failure 400,404 {object} structs.ANSWERAPI
// @Failure 500 {object} structs.ANSWERAPI
// @Failure default {object} structs.ANSWERAPI
// @Router /user [post]
// writeUser gets json request and write to nosql, without id - create new user
// {
// "firstname":"username",
// "lastname":"username",
// "birthday":"1972-06-10",
// "currentlocation":"Location"
// }
func (a *API) createUser() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var user *structs.USER
err := json.NewDecoder(r.Body).Decode(&user)
if err != nil {
a.respond(w, r, http.StatusBadRequest, nil, err, "")
return
}
err = user.Validate()
if err != nil {
a.respond(w, r, http.StatusBadRequest, nil, err, "")
return
}
err = a.services.CreateUser(user)
if err != nil {
a.respond(w, r, http.StatusInternalServerError, nil, err, "")
return
}
a.respond(w, r, http.StatusOK, user, nil, "")
}
}
// @Summary Upload File
// @Tags uploadfile
// @Description upload file for user, one by one
// @ID upload-file
// @Accept mpfd
// @Produce json
// @Param field path string true "User struct field"
// @Param id formData string true "UUID"
// @Param file_name formData string true "File name"
// @Param input formData file true "File data"
// @Success 200 {object} structs.USER
// @Failure 400,404 {object} structs.ANSWERAPI
// @Failure 500 {object} structs.ANSWERAPI
// @Failure default {object} structs.ANSWERAPI
// @Router /upload/{field} [post]
// uploadFiles get form-data request
// params:
// url /upload/<field of user struct> e.g. /upload/userpicture
// uid - User UUID
// file - file data
// file_name - e.g. 1234.jpg
func (a *API) uploadFiles() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
file_field := vars["field"]
// get uuid from request
uid, err := gocql.ParseUUID(r.FormValue("id"))
if err != nil {
a.respond(w, r, http.StatusBadRequest, nil, err, "")
return
}
// find user by uuid
user, err := a.services.GetUser(uid)
if err != nil {
a.respond(w, r, http.StatusNotFound, nil, err, "")
return
}
// check available field in struct
if !utils.AvailableAndAddValue(user, file_field, "") {
a.respond(w, r, http.StatusBadRequest, nil, errors.New("wrong url parameter"), "")
return
}
file_name := r.FormValue("file_name")
fileUrl, err := a.fileHandler(r, file_name)
if err != nil {
a.respond(w, r, http.StatusInternalServerError, nil, err, file_name)
return
}
// add aws url to struct user
utils.AvailableAndAddValue(user, file_field, fileUrl)
err = a.services.CreateUser(user)
if err != nil {
a.respond(w, r, http.StatusInternalServerError, nil, err, "")
return
}
a.respond(w, r, http.StatusOK, user, nil, "")
}
}
// fileHandler get data from MultipartForm and send to AWS S3
func (a *API) fileHandler(r *http.Request, filename string) (string, error) {
// total of maxMemory bytes
err := r.ParseMultipartForm(10 << 20)
if err != nil {
return "", err
}
// file upload as form-data
m := r.MultipartForm
fileUrl := ""
for _, v := range m.File {
for _, f := range v {
file, err := f.Open()
if err != nil {
return fileUrl, err
}
defer file.Close()
// upload file to aws
fileUrl, err = a.aws.UploadFile(file, filename)
if err != nil {
return fileUrl, err
}
}
}
return fileUrl, nil
}
// handleRoot paga page
func (a *API) handleRoot() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
a.respond(w, r, http.StatusOK, nil, nil, "")
}
}
// notFound page not found
func (a *API) notFound(w http.ResponseWriter, r *http.Request) {
a.respond(w, r, http.StatusNotFound, nil, errors.New("page not found"), "")
}
// notAllowed return custom method method not allowed
func (a *API) notAllowed(w http.ResponseWriter, r *http.Request) {
a.respond(w, r, http.StatusMethodNotAllowed, nil, errors.New("method not allowed"), "")
}
// setAnswer set api's answer
func setAnswer(data interface{}, e error, add string) *structs.ANSWERAPI {
err := ""
if e != nil {
err = e.Error()
}
a := &structs.ANSWERAPI{
DATA: data,
ERROR: err,
ADDITIONAL: add,
}
a.Prepare()
return a
}
// respond generate http response for api
func (a *API) respond(w http.ResponseWriter, r *http.Request, code int, data interface{}, e error, add string) {
w.Header().Set("Content-Type", "application/json")
w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS")
w.WriteHeader(code)
if r.Method == "OPTIONS" {
return
}
_ = json.NewEncoder(w).Encode(setAnswer(data, e, add))
}