diff --git a/cmd/server/main.go b/cmd/server/main.go index 0659738ef..7b8620e0f 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -40,13 +40,13 @@ func main() { log := logger.NewLogrusLogger() log.Infof("Server starting") - vault, err := vault.New(cfg) + v, err := vault.New(cfg) if err != nil { log.Error(err, "failed to init vault") } - if vault != nil { - cfg = config.Generate(vault) + if v != nil { + cfg = config.Generate(v) } s := store.New() diff --git a/go.mod b/go.mod index 8c6bc9683..5659ae5fe 100644 --- a/go.mod +++ b/go.mod @@ -19,12 +19,13 @@ require ( github.com/golang-jwt/jwt/v4 v4.4.3 github.com/golang/mock v1.4.4 github.com/google/go-cmp v0.5.9 + github.com/google/go-github/v52 v52.0.0 github.com/hashicorp/vault/api v1.8.2 github.com/issyl0/go-improvmx v0.17.0 github.com/jackc/pgtype v1.12.0 github.com/jinzhu/now v1.1.5 github.com/joho/godotenv v1.4.0 - github.com/k0kubun/pp v3.0.1+incompatible + github.com/k0kubun/pp/v3 v3.2.0 github.com/lib/pq v1.10.6 github.com/matoous/go-nanoid v1.5.0 github.com/patrickmn/go-cache v2.1.0+incompatible @@ -38,9 +39,9 @@ require ( github.com/swaggo/gin-swagger v1.5.3 github.com/swaggo/swag v1.8.7 github.com/thoas/go-funk v0.9.3 - golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa + golang.org/x/crypto v0.7.0 golang.org/x/exp v0.0.0-20221217163422-3c43f8badb15 - golang.org/x/oauth2 v0.5.0 + golang.org/x/oauth2 v0.7.0 google.golang.org/api v0.110.0 gorm.io/datatypes v1.0.7 gorm.io/driver/postgres v1.4.5 @@ -53,6 +54,7 @@ require ( cloud.google.com/go/compute/metadata v0.2.3 // indirect cloud.google.com/go/iam v0.12.0 // indirect github.com/KyleBanks/depth v1.2.1 // indirect + github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8 // indirect github.com/PuerkitoBio/purell v1.1.1 // indirect github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect github.com/andybalholm/brotli v1.0.4 // indirect @@ -60,6 +62,7 @@ require ( github.com/armon/go-metrics v0.3.10 // indirect github.com/armon/go-radix v1.0.0 // indirect github.com/cenkalti/backoff/v3 v3.0.0 // indirect + github.com/cloudflare/circl v1.1.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/fatih/color v1.13.0 // indirect github.com/fsnotify/fsnotify v1.5.4 // indirect @@ -75,6 +78,7 @@ require ( github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.2 // indirect github.com/golang/snappy v0.0.4 // indirect + github.com/google/go-querystring v1.1.0 // indirect github.com/google/uuid v1.3.0 // indirect github.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect github.com/googleapis/gax-go/v2 v2.7.0 // indirect @@ -108,7 +112,6 @@ require ( github.com/jinzhu/inflection v1.0.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88 // indirect github.com/leodido/go-urn v1.2.1 // indirect github.com/magiconair/properties v1.8.6 // indirect github.com/mailru/easyjson v0.7.6 // indirect @@ -137,11 +140,11 @@ require ( github.com/ugorji/go/codec v1.2.7 // indirect go.opencensus.io v0.24.0 // indirect go.uber.org/atomic v1.9.0 // indirect - golang.org/x/net v0.7.0 // indirect - golang.org/x/sys v0.5.0 // indirect - golang.org/x/text v0.7.0 // indirect + golang.org/x/net v0.9.0 // indirect + golang.org/x/sys v0.7.0 // indirect + golang.org/x/text v0.9.0 // indirect golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1 // indirect - golang.org/x/tools v0.2.0 // indirect + golang.org/x/tools v0.6.0 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto v0.0.0-20230216225411-c8e22ba71e44 // indirect diff --git a/go.sum b/go.sum index 2743589fc..b9086d029 100644 --- a/go.sum +++ b/go.sum @@ -61,6 +61,8 @@ github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6Xge github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc= github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8 h1:wPbRQzjjwFc0ih8puEVAOFGELsn1zoIIYdxvML7mDxA= +github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8/go.mod h1:I0gYDMZ6Z5GRU7l58bNFSkPTFN6Yl12dsUlAZ8xy98g= 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= @@ -92,6 +94,7 @@ github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+Ce github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= +github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= github.com/bwmarrin/discordgo v0.27.0 h1:4ZK9KN+rGIxZ0fdGTmgdCcliQeW8Zhu6MnlFI92nf0Q= github.com/bwmarrin/discordgo v0.27.0/go.mod h1:NJZpH+1AfhIcyQsPeuBKsUtYrRnjkyu0kIVMCHkZtRY= github.com/cenkalti/backoff/v3 v3.0.0 h1:ske+9nBpD9qZsTBoF41nW5L+AIuFBKMeze18XQ3eG1c= @@ -105,6 +108,8 @@ github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMn github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cloudflare/circl v1.1.0 h1:bZgT/A+cikZnKIwn7xL2OBj012Bmvho/o6RpRvv3GKY= +github.com/cloudflare/circl v1.1.0/go.mod h1:prBCrKB9DV4poKZY1l9zBXg2QJY7mvgRvtMxxK7fi4I= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= @@ -252,6 +257,10 @@ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-github/v52 v52.0.0 h1:uyGWOY+jMQ8GVGSX8dkSwCzlehU3WfdxQ7GweO/JP7M= +github.com/google/go-github/v52 v52.0.0/go.mod h1:WJV6VEEUPuMo5pXqqa2ZCZEdbQqua4zAk2MZTIo+m+4= +github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= +github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= @@ -422,10 +431,8 @@ github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1 github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88 h1:uC1QfSlInpQF+M0ao65imhwqKnz3Q2z/d8PWZRMQvDM= -github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k= -github.com/k0kubun/pp v3.0.1+incompatible h1:3tqvf7QgUnZ5tXO6pNAZlrvHgl6DvifjDrd9g2S9Z40= -github.com/k0kubun/pp v3.0.1+incompatible/go.mod h1:GWse8YhT0p8pT4ir3ZgBbfZild3tgzSScAn6HmfYukg= +github.com/k0kubun/pp/v3 v3.2.0 h1:h33hNTZ9nVFNP3u2Fsgz8JXiF5JINoZfFq4SvKJwNcs= +github.com/k0kubun/pp/v3 v3.2.0/go.mod h1:ODtJQbQcIRfAD3N+theGCV1m/CBxweERz2dapdz1EwA= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= @@ -687,8 +694,9 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa h1:zuSxTR4o9y82ebqCUJYNGJbGPo6sKVl54f/TVDObg1c= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A= +golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -726,7 +734,7 @@ golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.6.0 h1:b9gGHsz9/HhJ3HF5DHQytPpuwocVTChQJK3AvoLRD5I= +golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -773,8 +781,8 @@ golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= -golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= -golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM= +golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -784,8 +792,8 @@ golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.5.0 h1:HuArIo48skDwlrvM3sEdHXElYslAMsf3KwRkkW4MC4s= -golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I= +golang.org/x/oauth2 v0.7.0 h1:qe6s0zUXlPX80/dITx3440hWZ7GwMwgDDyrSGTPJG/g= +golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -849,6 +857,7 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -856,8 +865,8 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= +golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -871,8 +880,8 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -935,8 +944,8 @@ golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.2.0 h1:G6AHpWxTMGY1KyEYoAQ5WTtIekUUvDNjan3ugu60JvE= -golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA= +golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/pkg/config/config.go b/pkg/config/config.go index 93e440aee..2ce8a2aa6 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -29,6 +29,7 @@ type Config struct { Invoice Invoice Sendgrid Sendgrid + Github Github APIKey string Debug bool @@ -61,6 +62,7 @@ type Google struct { GCSCredentials string GCPProjectID string AccountingGoogleRefreshToken string + AdminGoogleRefreshToken string AccountingEmailID string TeamGoogleRefreshToken string TeamEmailID string @@ -95,6 +97,10 @@ type Notion struct { Databases NotionDatabase } +type Github struct { + Token string +} + type NotionDatabase struct { AuditCycle string AuditActionItem string @@ -170,19 +176,22 @@ func Generate(v ENV) *Config { Pass: v.GetString("DB_PASS"), SSLMode: v.GetString("DB_SSL_MODE"), }, - + Github: Github{ + Token: v.GetString("GITHUB_ACCESS_TOKEN"), + }, Google: Google{ - ClientSecret: v.GetString("GOOGLE_API_CLIENT_SECRET"), - ClientID: v.GetString("GOOGLE_API_CLIENT_ID"), + AccountingEmailID: v.GetString("ACCOUNTING_EMAIL_ID"), + AccountingGoogleRefreshToken: v.GetString("ACCOUNTING_GOOGLE_REFRESH_TOKEN"), + AdminGoogleRefreshToken: v.GetString("ADMIN_GOOGLE_REFRESH_TOKEN"), AppName: v.GetString("GOOGLE_API_APP_NAME"), + ClientID: v.GetString("GOOGLE_API_CLIENT_ID"), + ClientSecret: v.GetString("GOOGLE_API_CLIENT_SECRET"), GCPProjectID: v.GetString("GCP_PROJECT_ID"), GCSBucketName: v.GetString("GCS_BUCKET_NAME"), - GCSProjectID: v.GetString("GCS_PROJECT_ID"), GCSCredentials: v.GetString("GCS_CREDENTIALS"), - AccountingGoogleRefreshToken: v.GetString("ACCOUNTING_GOOGLE_REFRESH_TOKEN"), - AccountingEmailID: v.GetString("ACCOUNTING_EMAIL_ID"), - TeamGoogleRefreshToken: v.GetString("TEAM_GOOGLE_REFRESH_TOKEN"), + GCSProjectID: v.GetString("GCS_PROJECT_ID"), TeamEmailID: v.GetString("TEAM_EMAIL_ID"), + TeamGoogleRefreshToken: v.GetString("TEAM_GOOGLE_REFRESH_TOKEN"), }, Wise: Wise{ @@ -193,7 +202,6 @@ func Generate(v ENV) *Config { CurrencyLayer: CurrencyLayer{ APIKey: v.GetString("CURRENCY_LAYER_API_KEY"), }, - Vault: Vault{ Address: v.GetString("VAULT_ADDR"), Token: v.GetString("VAULT_TOKEN"), diff --git a/pkg/controller/employee/create.go b/pkg/controller/employee/create.go index 6e8e064e5..cf97bb1a4 100644 --- a/pkg/controller/employee/create.go +++ b/pkg/controller/employee/create.go @@ -33,6 +33,7 @@ func (r *controller) Create(userID string, input CreateEmployeeInput) (*model.Em "controller": "employee", "method": "Create", }) + loggedInUser, err := r.store.Employee.One(r.repo.DB(), userID, false) if errors.Is(err, gorm.ErrRecordNotFound) { return nil, ErrEmployeeNotFound diff --git a/pkg/controller/employee/getLineManagers.go b/pkg/controller/employee/get_line_managers.go similarity index 100% rename from pkg/controller/employee/getLineManagers.go rename to pkg/controller/employee/get_line_managers.go diff --git a/pkg/controller/employee/updateEmployeeStatus.go b/pkg/controller/employee/updateEmployeeStatus.go deleted file mode 100644 index 3dbae6706..000000000 --- a/pkg/controller/employee/updateEmployeeStatus.go +++ /dev/null @@ -1,39 +0,0 @@ -package employee - -import ( - "errors" - "time" - - "gorm.io/gorm" - - "github.com/dwarvesf/fortress-api/pkg/model" -) - -type UpdateWorkingStatusInput struct { - EmployeeStatus model.WorkingStatus -} - -func (r *controller) UpdateEmployeeStatus(employeeID string, body UpdateWorkingStatusInput) (*model.Employee, error) { - emp, err := r.store.Employee.One(r.repo.DB(), employeeID, true) - if err != nil { - if errors.Is(err, gorm.ErrRecordNotFound) { - return nil, ErrEmployeeNotFound - } - return nil, err - } - - emp.WorkingStatus = body.EmployeeStatus - emp.LeftDate = nil - - now := time.Now() - if body.EmployeeStatus == model.WorkingStatusLeft { - emp.LeftDate = &now - } - - _, err = r.store.Employee.UpdateSelectedFieldsByID(r.repo.DB(), employeeID, *emp, "working_status", "left_date") - if err != nil { - return nil, err - } - - return emp, err -} diff --git a/pkg/controller/employee/updateBaseSalary.go b/pkg/controller/employee/update_base_salary.go similarity index 99% rename from pkg/controller/employee/updateBaseSalary.go rename to pkg/controller/employee/update_base_salary.go index 0cf240db5..d63e2de9a 100644 --- a/pkg/controller/employee/updateBaseSalary.go +++ b/pkg/controller/employee/update_base_salary.go @@ -2,10 +2,12 @@ package employee import ( "errors" + "time" + + "gorm.io/gorm" + "github.com/dwarvesf/fortress-api/pkg/logger" "github.com/dwarvesf/fortress-api/pkg/model" - "gorm.io/gorm" - "time" ) type UpdateBaseSalaryInput struct { diff --git a/pkg/controller/employee/update_employee_status.go b/pkg/controller/employee/update_employee_status.go new file mode 100644 index 000000000..1d22fa0a8 --- /dev/null +++ b/pkg/controller/employee/update_employee_status.go @@ -0,0 +1,193 @@ +package employee + +import ( + "context" + "errors" + "time" + + "gorm.io/gorm" + "gorm.io/gorm/utils" + + "github.com/dwarvesf/fortress-api/pkg/logger" + "github.com/dwarvesf/fortress-api/pkg/model" +) + +type UpdateWorkingStatusInput struct { + EmployeeStatus model.WorkingStatus +} + +func (r *controller) UpdateEmployeeStatus(employeeID string, body UpdateWorkingStatusInput) (*model.Employee, error) { + l := r.logger.Fields(logger.Fields{ + "controller": "employee", + "method": "UpdateEmployeeStatus", + }) + + now := time.Now() + e, err := r.store.Employee.One(r.repo.DB(), employeeID, true) + if err != nil { + l.Errorf(err, "failed to get Employee ", employeeID) + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, ErrEmployeeNotFound + } + return nil, err + } + + e.WorkingStatus = body.EmployeeStatus + e.LeftDate = &now + + if body.EmployeeStatus != model.WorkingStatusLeft { + e.LeftDate = nil + } + + tx, done := r.repo.NewTransaction() + defer func() { + _ = done(nil) + }() + + _, err = r.store.Employee.UpdateSelectedFieldsByID(tx.DB(), employeeID, *e, "working_status", "left_date") + if err != nil { + return nil, done(err) + } + + //If employee working status is left, do off-boarding flow + if body.EmployeeStatus == model.WorkingStatusLeft { + err = r.store.ProjectMember.UpdateMemberToInActiveByID(tx.DB(), employeeID, &now) + if err != nil { + return nil, done(err) + } + + // Do Off-boarding process + r.processOffBoardingEmployee(l, e) + } + + return e, err +} + +func (r *controller) processOffBoardingEmployee(l logger.Logger, e *model.Employee) { + discordInfo := model.SocialAccounts(e.SocialAccounts).GetDiscord() + if discordInfo != nil { + err := r.updateDiscordRoles(discordInfo.AccountID) + if err != nil { + l.Errorf(err, "failed to update discord roles", "employeeID", e.ID.String(), "discordID", discordInfo.AccountID) + } + } + + err := r.removeBasecampAccess(e.BasecampID) + if err != nil { + l.Errorf(err, "failed to remove basecamp access", "employeeID", e.ID.String(), "basecampID", e.BasecampID) + } + + err = r.removeTeamEmailForward(e.TeamEmail) + if err != nil { + l.Errorf(err, "failed to remove team email forward", "employeeID", e.ID.String(), "email", e.TeamEmail) + } + + err = r.removeTeamEmail(e.TeamEmail) + if err != nil { + l.Errorf(err, "failed to delete google account", "employeeID", e.ID.String(), "email", e.TeamEmail) + } + + err = r.removeGithubFromOrganization(e) + if err != nil { + l.Errorf(err, "failed to remove github user from organization", "employeeID", e.ID.String()) + } +} + +func (r *controller) updateDiscordRoles(discordUserID string) error { + if r.config.Env != "prod" { + return nil + } + + if discordUserID == "" { + return nil + } + + roles, err := r.service.Discord.GetRoles() + if err != nil { + return err + } + + dfRoles := roles.DwarvesRoles() + + discordMember, err := r.service.Discord.GetMember(discordUserID) + if err != nil { + return err + } + + for _, role := range dfRoles { + if utils.Contains(discordMember.Roles, role.ID) { + err = r.service.Discord.RemoveRole(discordUserID, role.ID) + if err != nil { + return err + } + } + } + + // Assign alumni role + alumniRole := roles.ByCode("alumni") + err = r.service.Discord.AddRole(discordUserID, alumniRole.ID) + if err != nil { + return err + } + + return nil +} + +func (r *controller) removeBasecampAccess(baseCampID int) error { + if r.config.Env != "prod" { + return nil + } + + err := r.service.Basecamp.People.Remove(int64(baseCampID)) + if err != nil { + return err + } + + return nil +} + +func (r *controller) removeTeamEmailForward(teamEmail string) error { + if r.config.Env != "prod" { + return nil + } + + err := r.service.ImprovMX.DeleteAccount(teamEmail) + if err != nil { + return err + } + + return nil +} + +func (r *controller) removeTeamEmail(teamEmail string) error { + if r.config.Env != "prod" { + return nil + } + + err := r.service.GoogleAdmin.DeleteAccount(teamEmail) + if err != nil { + return err + } + + return nil +} + +func (r *controller) removeGithubFromOrganization(e *model.Employee) error { + if r.config.Env != "prod" { + return nil + } + + githubSA := model.SocialAccounts(e.SocialAccounts).GetGithub() + if githubSA != nil { + if githubSA.AccountID == "" { + return nil + } + + err := r.service.Github.RemoveFromOrganizationByUsername(context.Background(), githubSA.AccountID) + if err != nil { + return err + } + } + + return nil +} diff --git a/pkg/controller/employee/updateGeneralInfo.go b/pkg/controller/employee/update_general_info.go similarity index 100% rename from pkg/controller/employee/updateGeneralInfo.go rename to pkg/controller/employee/update_general_info.go diff --git a/pkg/controller/employee/updatePersonalInfo.go b/pkg/controller/employee/update_personal_info.go similarity index 100% rename from pkg/controller/employee/updatePersonalInfo.go rename to pkg/controller/employee/update_personal_info.go diff --git a/pkg/controller/employee/updateRole.go b/pkg/controller/employee/update_role.go similarity index 100% rename from pkg/controller/employee/updateRole.go rename to pkg/controller/employee/update_role.go diff --git a/pkg/controller/employee/updateSkills.go b/pkg/controller/employee/update_skills.go similarity index 100% rename from pkg/controller/employee/updateSkills.go rename to pkg/controller/employee/update_skills.go diff --git a/pkg/controller/employee/uploadAvatar.go b/pkg/controller/employee/upload_avatar.go similarity index 100% rename from pkg/controller/employee/uploadAvatar.go rename to pkg/controller/employee/upload_avatar.go diff --git a/pkg/handler/employee/employee.go b/pkg/handler/employee/employee.go index 954f22afe..b736eb2cc 100644 --- a/pkg/handler/employee/employee.go +++ b/pkg/handler/employee/employee.go @@ -238,7 +238,7 @@ func (h *handler) UpdateEmployeeStatus(c *gin.Context) { EmployeeStatus: body.EmployeeStatus, }) if err != nil { - l.Error(err, "failed to get detail employees") + l.Error(err, "failed to update employee status") errs.ConvertControllerErr(c, err) return } diff --git a/pkg/handler/payroll/payroll.go b/pkg/handler/payroll/payroll.go index a4396af79..1728535bb 100644 --- a/pkg/handler/payroll/payroll.go +++ b/pkg/handler/payroll/payroll.go @@ -23,8 +23,8 @@ import ( "github.com/dwarvesf/fortress-api/pkg/service" "github.com/dwarvesf/fortress-api/pkg/service/currency" "github.com/dwarvesf/fortress-api/pkg/store" - commissionStore "github.com/dwarvesf/fortress-api/pkg/store/commission" "github.com/dwarvesf/fortress-api/pkg/store/employee" + "github.com/dwarvesf/fortress-api/pkg/store/employeecommission" "github.com/dwarvesf/fortress-api/pkg/store/payroll" "github.com/dwarvesf/fortress-api/pkg/utils" "github.com/dwarvesf/fortress-api/pkg/utils/timeutil" @@ -255,13 +255,13 @@ func GetPayrollDetailHandler(h *handler, month, year, batch int, email string) ( // !isForecast if batchDate.Month() <= time.Now().Month() || (batchDate.Month() == 12 && time.Now().Month() == 1) { toDate := batchDate.AddDate(0, 1, 0) - commissionQuery := commissionStore.Query{ + commissionQuery := employeecommission.Query{ EmployeeID: payrolls[i].Employee.ID.String(), IsPaid: isPaid, FromDate: &batchDate, ToDate: &toDate, } - userCommissions, err := h.store.Commission.Get(h.repo.DB(), commissionQuery) + userCommissions, err := h.store.EmployeeCommission.Get(h.repo.DB(), commissionQuery) if err != nil { return nil, err } @@ -279,7 +279,7 @@ func GetPayrollDetailHandler(h *handler, month, year, batch int, email string) ( notes = append(notes, fmt.Sprintf("%v (%v)", j, utils.FormatCurrencyAmount(duplicate[j]))) } - bonusExplain := []model.CommissionExplain{} + var bonusExplain []model.CommissionExplain if len(payrolls[i].ProjectBonusExplain) > 0 { if err := json.Unmarshal(payrolls[i].ProjectBonusExplain, &bonusExplain); err != nil { return nil, errs.ErrCannotReadProjectBonusExplain @@ -485,7 +485,7 @@ func getCommissionExplains(h *handler, p *model.Payroll, markPaid bool) ([]model } if markPaid { for i := range commissionExplains { - err := h.store.Commission.MarkPaid(h.repo.DB(), commissionExplains[i].ID) + err := h.store.EmployeeCommission.MarkPaid(h.repo.DB(), commissionExplains[i].ID) if err != nil { return nil, err } @@ -781,7 +781,7 @@ func (h *handler) commitPayrollHandler(month, year, batch int, email string) err } func markBonusAsDone(h *handler, p *model.Payroll) error { - projectBonusExplains := []model.ProjectBonusExplain{} + var projectBonusExplains []model.ProjectBonusExplain err := json.Unmarshal( p.ProjectBonusExplain, &projectBonusExplains, diff --git a/pkg/handler/payroll/payroll_calculator.go b/pkg/handler/payroll/payroll_calculator.go index 440cfc94a..8ec4dd5d6 100644 --- a/pkg/handler/payroll/payroll_calculator.go +++ b/pkg/handler/payroll/payroll_calculator.go @@ -10,7 +10,7 @@ import ( "github.com/dwarvesf/fortress-api/pkg/model" bcModel "github.com/dwarvesf/fortress-api/pkg/service/basecamp/model" "github.com/dwarvesf/fortress-api/pkg/service/currency" - commissionStore "github.com/dwarvesf/fortress-api/pkg/store/commission" + commissionStore "github.com/dwarvesf/fortress-api/pkg/store/employeecommission" "github.com/dwarvesf/fortress-api/pkg/utils/timeutil" ) @@ -41,12 +41,12 @@ func calculatePayrolls(h *handler, users []*model.Employee, batchDate time.Time) opsExpenseID = consts.OpsExpenseTodoID } - opsTodolists, err := h.service.Basecamp.Todo.GetAllInList(opsExpenseID, opsID) + opsTodoLists, err := h.service.Basecamp.Todo.GetAllInList(opsExpenseID, opsID) if err != nil { h.logger.Error(err, "can't get ops expense todo") return nil, err } - for _, exps := range opsTodolists { + for _, exps := range opsTodoLists { isApproved := false cmts, err := h.service.Basecamp.Comment.Gets(opsID, exps.ID) if err != nil { @@ -122,7 +122,7 @@ func calculatePayrolls(h *handler, users []*model.Employee, batchDate time.Time) // TODO... // try to calculate if user start/end after/before the payroll // fallback to default - baseSalary, contract, _ = tryParticalCalculation(batchDate, dueDate, *u.JoinedDate, u.LeftDate, users[i].BaseSalary.PersonalAccountAmount, users[i].BaseSalary.CompanyAccountAmount) + baseSalary, contract, _ = tryPartialCalculation(batchDate, dueDate, *u.JoinedDate, u.LeftDate, users[i].BaseSalary.PersonalAccountAmount, users[i].BaseSalary.CompanyAccountAmount) var bonus, commission, reimbursementAmount model.VietnamDong var bonusExplains, commissionExplains []model.CommissionExplain @@ -194,7 +194,7 @@ func calculatePayrolls(h *handler, users []*model.Employee, batchDate time.Time) if dueDate.Sub(*candidates[i].OfferStartDate).Hours()/24 < 15 { continue } - baseSalary, contract, _ := tryParticalCalculation(batchDate, dueDate, *candidates[i].OfferStartDate, nil, int64(candidates[i].OfferSalary), 0) + baseSalary, contract, _ := tryPartialCalculation(batchDate, dueDate, *candidates[i].OfferStartDate, nil, int64(candidates[i].OfferSalary), 0) total := model.NewVietnamDong(baseSalary) p := model.Payroll{ @@ -290,7 +290,7 @@ func getBonus( EmployeeID: u.ID.String(), IsPaid: false, } - userCommissions, err := h.store.Commission.Get(h.repo.DB(), commissionQuery) + userCommissions, err := h.store.EmployeeCommission.Get(h.repo.DB(), commissionQuery) if err != nil { return } @@ -317,7 +317,7 @@ func getBonus( return } -func tryParticalCalculation( +func tryPartialCalculation( batchDate, dueDate, startDate time.Time, leftDate *time.Time, baseSalary, contract int64, diff --git a/pkg/handler/profile/profile.go b/pkg/handler/profile/profile.go index bd0e95ac8..85f965e59 100644 --- a/pkg/handler/profile/profile.go +++ b/pkg/handler/profile/profile.go @@ -877,10 +877,11 @@ func (h *handler) assignDiscordRole(discordName string) error { discordNameParts := strings.Split(discordName, "#") - guildMembers, err := h.service.Discord.GetMemberByName(discordNameParts[0]) + guildMembers, err := h.service.Discord.SearchMember(discordNameParts[0]) if err != nil { return err } + var discordMember *discordgo.Member for _, m := range guildMembers { if len(discordNameParts) == 1 { @@ -918,7 +919,7 @@ func (h *handler) assignDiscordRole(discordName string) error { return nil } - err := h.service.Discord.AssignRole(discordMember.User.ID, peepsRoleID) + err := h.service.Discord.AddRole(discordMember.User.ID, peepsRoleID) if err != nil { return err } diff --git a/pkg/handler/project/project.go b/pkg/handler/project/project.go index a50325bcd..3eacf6561 100644 --- a/pkg/handler/project/project.go +++ b/pkg/handler/project/project.go @@ -9,6 +9,7 @@ import ( "time" "github.com/gin-gonic/gin" + "github.com/k0kubun/pp/v3" "github.com/shopspring/decimal" "gorm.io/gorm" @@ -24,7 +25,6 @@ import ( "github.com/dwarvesf/fortress-api/pkg/utils" "github.com/dwarvesf/fortress-api/pkg/utils/authutils" "github.com/dwarvesf/fortress-api/pkg/view" - "github.com/k0kubun/pp" ) type handler struct { @@ -1036,7 +1036,6 @@ func (h *handler) UpdateMember(c *gin.Context) { // // --- end --- func (h *handler) updateProjectMember(db *gorm.DB, p *model.Project, slotID string, projectID string, input request.UpdateMemberInput, userInfo *model.CurrentLoggedUserInfo) (*model.ProjectMember, error) { - pp.Println("updateProjectMember") var member *model.ProjectMember var err error @@ -1075,8 +1074,6 @@ func (h *handler) updateProjectMember(db *gorm.DB, p *model.Project, slotID stri updateEndDate := false inputEndDate := input.GetEndDate() - pp.Println(inputEndDate) - pp.Println(member.EndDate) if member.EndDate != nil && inputEndDate == nil { member.EndDate = nil updateEndDate = true @@ -3061,5 +3058,6 @@ func (h *handler) SyncProjectMemberStatus(c *gin.Context) { return } + pp.Println("synced project member status") c.JSON(http.StatusOK, view.CreateResponse[any](nil, nil, nil, nil, "ok")) } diff --git a/pkg/handler/webhook/basecamp.go b/pkg/handler/webhook/basecamp.go index adecae65e..576aa2ca1 100644 --- a/pkg/handler/webhook/basecamp.go +++ b/pkg/handler/webhook/basecamp.go @@ -35,7 +35,7 @@ func (h *handler) BasecampExpenseValidate(c *gin.Context) { c.JSON(http.StatusOK, view.CreateResponse[any](nil, nil, nil, nil, "")) } -// // BasecampExpense runs expense process in basecamp +// BasecampExpense runs expense process in basecamp func (h *handler) BasecampExpense(c *gin.Context) { msg, err := basecampWebhookMessageFromCtx(c) if err != nil { diff --git a/pkg/handler/webhook/basecamp_expense.go b/pkg/handler/webhook/basecamp_expense.go index b45b7c746..d07905bde 100644 --- a/pkg/handler/webhook/basecamp_expense.go +++ b/pkg/handler/webhook/basecamp_expense.go @@ -137,7 +137,7 @@ func (h *handler) UncheckBasecampExpenseHandler(msg model.BasecampWebhookMessage return nil } -// h.ExtractExpenseData takes a webhook message and parse it into BasecampExpenseData structure +// ExtractExpenseData takes a webhook message and parse it into BasecampExpenseData structure func (h *handler) ExtractExpenseData(msg model.BasecampWebhookMessage) (*bc.BasecampExpenseData, error) { res := &bc.BasecampExpenseData{BasecampID: msg.Recording.ID} @@ -161,7 +161,7 @@ func (h *handler) ExtractExpenseData(msg model.BasecampWebhookMessage) (*bc.Base res.Amount = amount // extract currency type - t := strings.ToLower(strings.TrimSpace((parts[2]))) + t := strings.ToLower(strings.TrimSpace(parts[2])) if t != "vnd" && t != "usd" { return nil, errors.New("invalid format in currency type section (VND or USD) of expense format") } diff --git a/pkg/model/social_account.go b/pkg/model/social_account.go index 1271a2286..6d5e83d00 100644 --- a/pkg/model/social_account.go +++ b/pkg/model/social_account.go @@ -42,3 +42,23 @@ func (e SocialAccountType) IsValid() bool { func (e SocialAccountType) String() string { return string(e) } + +type SocialAccounts []SocialAccount + +func (e SocialAccounts) GetDiscord() *SocialAccount { + for _, account := range e { + if account.Type == SocialAccountTypeDiscord { + return &account + } + } + return nil +} + +func (e SocialAccounts) GetGithub() *SocialAccount { + for _, account := range e { + if account.Type == SocialAccountTypeGitHub { + return &account + } + } + return nil +} diff --git a/pkg/service/basecamp/basecamp.go b/pkg/service/basecamp/basecamp.go index aaf5b8a6b..88fa862d2 100644 --- a/pkg/service/basecamp/basecamp.go +++ b/pkg/service/basecamp/basecamp.go @@ -48,7 +48,7 @@ type Service struct { Wise wise.IService } -func NewService(store *store.Store, repo store.DBRepo, cfg *config.Config, bc *model.Basecamp, logger logger.Logger) *Service { +func New(store *store.Store, repo store.DBRepo, cfg *config.Config, bc *model.Basecamp, logger logger.Logger) *Service { c, err := client.NewClient(bc, cfg) if err != nil { logger.Error(err, "init basecamp service") diff --git a/pkg/service/discord/discord.go b/pkg/service/discord/discord.go index 0623fc3b3..e106dec3a 100644 --- a/pkg/service/discord/discord.go +++ b/pkg/service/discord/discord.go @@ -142,6 +142,27 @@ func (d *discordClient) SendMessage(msg, webhookUrl string) (*model.DiscordMessa return &discordMsg, nil } +func (d *discordClient) SearchMember(discordName string) ([]*discordgo.Member, error) { + members := make([]*discordgo.Member, 0) + guildMembers, err := d.session.GuildMembersSearch(d.cfg.Discord.IDs.DwarvesGuild, discordName, 1000) + if err != nil { + return nil, err + } + + members = append(members, guildMembers...) + + return members, nil +} + +func (d *discordClient) GetMember(userID string) (*discordgo.Member, error) { + member, err := d.session.GuildMember(d.cfg.Discord.IDs.DwarvesGuild, userID) + if err != nil { + return nil, err + } + + return member, nil +} + func (d *discordClient) GetMemberByName(discordName string) ([]*discordgo.Member, error) { members := make([]*discordgo.Member, 0) guildMembers, err := d.session.GuildMembersSearch(d.cfg.Discord.IDs.DwarvesGuild, discordName, 1000) @@ -154,7 +175,7 @@ func (d *discordClient) GetMemberByName(discordName string) ([]*discordgo.Member return members, nil } -func (d *discordClient) GetRoles() ([]*discordgo.Role, error) { +func (d *discordClient) GetRoles() (Roles, error) { roles, err := d.session.GuildRoles(d.cfg.Discord.IDs.DwarvesGuild) if err != nil { return nil, err @@ -163,6 +184,64 @@ func (d *discordClient) GetRoles() ([]*discordgo.Role, error) { return roles, nil } -func (d *discordClient) AssignRole(roleID, userID string) error { +func (d *discordClient) AddRole(userID, roleID string) error { return d.session.GuildMemberRoleAdd(d.cfg.Discord.IDs.DwarvesGuild, userID, roleID) } + +func (d *discordClient) RemoveRole(userID string, roleID string) error { + return d.session.GuildMemberRoleRemove(d.cfg.Discord.IDs.DwarvesGuild, userID, roleID) +} + +type Roles discordgo.Roles + +func (r Roles) DwarvesRoles() []*discordgo.Role { + roleMap := getDwarvesRoleMap() + + dwarvesRoles := make([]*discordgo.Role, 0) + for _, dRole := range r { + _, ok := roleMap[dRole.Name] + if ok { + dwarvesRoles = append(dwarvesRoles, dRole) + } + } + + return dwarvesRoles +} + +func (r Roles) ByCode(code string) *discordgo.Role { + for _, dRole := range r { + if dRole.Name == code { + return dRole + } + } + + return nil +} + +func getDwarvesRoleMap() map[string]bool { + roleMap := make(map[string]bool) + var dwarvesRoleCodes = []string{ + "moderator", + "dwarf", + "booster", + "apprentice", + "crafter", + "specialist", + "principal", + "peeps", + "learning", + "engagement", + "delivery", + "labs", + "baby dwarf", + "ladies", + "sers", + "consultant", + "chad", + } + for _, code := range dwarvesRoleCodes { + roleMap[code] = true + } + + return roleMap +} diff --git a/pkg/service/discord/service.go b/pkg/service/discord/service.go index fbf21615f..ebe230d3f 100644 --- a/pkg/service/discord/service.go +++ b/pkg/service/discord/service.go @@ -9,15 +9,17 @@ import ( type IService interface { PostBirthdayMsg(msg string) (model.DiscordMessage, error) GetMembers() ([]*discordgo.Member, error) - GetMemberByName(discordName string) ([]*discordgo.Member, error) - GetRoles() ([]*discordgo.Role, error) - AssignRole(roleID, userID string) error + GetMember(userID string) (*discordgo.Member, error) + SearchMember(discordName string) ([]*discordgo.Member, error) + GetRoles() (Roles, error) + AddRole(userID, roleID string) error + RemoveRole(userID string, roleID string) error // CreateEvent create a discord event CreateEvent(event *model.Schedule) (scheduledEvent *discordgo.GuildScheduledEvent, err error) UpdateEvent(event *model.Schedule) (scheduledEvent *discordgo.GuildScheduledEvent, err error) DeleteEvent(event *model.Schedule) error - // Log discord + // SendMessage logs a message to a Discord channel SendMessage(msg, webhookUrl string) (*model.DiscordMessage, error) } diff --git a/pkg/service/github/errors.go b/pkg/service/github/errors.go new file mode 100644 index 000000000..1161eb91e --- /dev/null +++ b/pkg/service/github/errors.go @@ -0,0 +1,8 @@ +package github + +import "errors" + +var ( + ErrFailedToGetGithubAccount = errors.New("failed to get github account") + ErrFoundOneMoreGithubAccount = errors.New("failed to get github account due to more than 1 github account found") +) diff --git a/pkg/service/github/github.go b/pkg/service/github/github.go new file mode 100644 index 000000000..135209fb2 --- /dev/null +++ b/pkg/service/github/github.go @@ -0,0 +1,111 @@ +package github + +import ( + "context" + + "github.com/google/go-github/v52/github" + "golang.org/x/oauth2" + + "github.com/dwarvesf/fortress-api/pkg/config" + "github.com/dwarvesf/fortress-api/pkg/logger" + "github.com/dwarvesf/fortress-api/pkg/model" +) + +type githubService struct { + Client *github.Client + log logger.Logger +} + +var ( + defaultRole = "direct_member" + dwarvesFoundationOrg = "dwarvesf" +) + +func New(cfg *config.Config, l logger.Logger) IService { + if cfg.Github.Token == "" { + return &githubService{} + } + return &githubService{ + Client: github.NewClient( + oauth2.NewClient(context.Background(), oauth2.StaticTokenSource( + &oauth2.Token{ + AccessToken: cfg.Github.Token, + }, + ))), + log: l, + } +} + +func (s githubService) SendInvitationByEmail(ctx context.Context, e *model.Employee) error { + if s.Client == nil { + s.log.Warn("[SendInvitationByEmail] github token is empty") + return nil + } + + role := defaultRole + opt := github.CreateOrgInvitationOptions{ + Email: &e.PersonalEmail, + Role: &role, + TeamID: []int64{}, + } + + s.log.Infof("[SendInvitationByEmail] Send invitation to user", "email", e.PersonalEmail) + _, _, err := s.Client.Organizations.CreateOrgInvitation(ctx, dwarvesFoundationOrg, &opt) + if err != nil { + s.log.Errorf(err, "[SendInvitationByEmail] Fail to send invitation", "email", e.PersonalEmail) + return err + } + + return nil +} + +func (s githubService) RemoveFromOrganizationByEmail(ctx context.Context, email string) error { + if s.Client == nil { + return nil + } + + sOpts := github.SearchOptions{} + + result, _, err := s.Client.Search.Users(ctx, email, &sOpts) + if err != nil { + s.log.Errorf(err, "[RemoveFromOrganizationByEmail] fail to search user by email", "email", email) + return err + } + + switch { + case len(result.Users) > 1: + s.log.Errorf(ErrFoundOneMoreGithubAccount, "[RemoveFromOrganizationByEmail] more than 1 result return", "email", email) + return ErrFoundOneMoreGithubAccount + case len(result.Users) == 0: + s.log.Errorf(ErrFailedToGetGithubAccount, "[RemoveFromOrganizationByEmail] can not found github account from user email", "email", email) + return ErrFailedToGetGithubAccount + } + + s.log.Infof("[RemoveFromOrganizationByEmail] Remove github member out of organization", "username", result.Users[0].GetLogin()) + _, err = s.Client.Organizations.RemoveMember(ctx, dwarvesFoundationOrg, result.Users[0].GetLogin()) + if err != nil { + return err + } + + return nil +} + +func (s githubService) RemoveFromOrganizationByUsername(ctx context.Context, username string) error { + if s.Client == nil { + return nil + } + + result, _, err := s.Client.Users.Get(ctx, username) + if err != nil { + s.log.Errorf(err, "[RemoveFromOrganizationByUsername] fail to search user by email", "username", username) + return err + } + + s.log.Infof("[RemoveFromOrganizationByUsername] remove github member out of organization", "username", result.GetLogin()) + _, err = s.Client.Organizations.RemoveMember(ctx, dwarvesFoundationOrg, result.GetLogin()) + if err != nil { + return err + } + + return nil +} diff --git a/pkg/service/github/service.go b/pkg/service/github/service.go new file mode 100644 index 000000000..b208ccd02 --- /dev/null +++ b/pkg/service/github/service.go @@ -0,0 +1,13 @@ +package github + +import ( + "context" + + "github.com/dwarvesf/fortress-api/pkg/model" +) + +type IService interface { + RemoveFromOrganizationByEmail(ctx context.Context, email string) error + RemoveFromOrganizationByUsername(ctx context.Context, username string) error + SendInvitationByEmail(ctx context.Context, e *model.Employee) error +} diff --git a/pkg/service/googleadmin/google_admin.go b/pkg/service/googleadmin/google_admin.go new file mode 100644 index 000000000..2fca4a546 --- /dev/null +++ b/pkg/service/googleadmin/google_admin.go @@ -0,0 +1,94 @@ +package googleadmin + +import ( + "context" + "errors" + "fmt" + "google.golang.org/api/option" + + "golang.org/x/oauth2" + admin "google.golang.org/api/admin/directory/v1" + + "github.com/dwarvesf/fortress-api/pkg/config" +) + +type googleService struct { + config *oauth2.Config + token *oauth2.Token + service *admin.Service + appConfig *config.Config +} + +// New function return Google service +func New(config *oauth2.Config, appConfig *config.Config) IService { + return &googleService{ + config: config, + appConfig: appConfig, + } +} + +func (g *googleService) DeleteAccount(mail string) error { + if err := g.ensureToken(g.appConfig.Google.AdminGoogleRefreshToken); err != nil { + return err + } + + if err := g.prepareService(); err != nil { + return err + } + + err := g.service.Users.Delete(mail).Do() + return err +} + +func (g *googleService) GetGroupMemberEmails(groupEmail string) ([]string, error) { + if err := g.ensureToken(g.appConfig.Google.AccountingGoogleRefreshToken); err != nil { + return nil, err + } + + if err := g.prepareService(); err != nil { + return nil, err + } + + var memberEmails []string + + members, err := g.service.Members.List(groupEmail).Do() + if err != nil { + return nil, err + } + + if members == nil { + return nil, fmt.Errorf("No member in group %v", groupEmail) + } + + for _, m := range members.Members { + memberEmails = append(memberEmails, m.Email) + } + + return memberEmails, nil +} + +func (g *googleService) ensureToken(rToken string) error { + token := &oauth2.Token{ + RefreshToken: rToken, + } + + if !g.token.Valid() { + tks := g.config.TokenSource(context.Background(), token) + tok, err := tks.Token() + if err != nil { + return err + } + g.token = tok + } + return nil +} + +func (g *googleService) prepareService() error { + client := g.config.Client(context.Background(), g.token) + service, err := admin.NewService(context.Background(), option.WithHTTPClient(client)) + if err != nil { + return errors.New("failed to prepare google admin service " + err.Error()) + } + g.service = service + return nil +} diff --git a/pkg/service/googleadmin/interface.go b/pkg/service/googleadmin/interface.go new file mode 100644 index 000000000..a1acb7078 --- /dev/null +++ b/pkg/service/googleadmin/interface.go @@ -0,0 +1,7 @@ +package googleadmin + +// IService interface contain related google calendar method +type IService interface { + GetGroupMemberEmails(groupEmail string) ([]string, error) + DeleteAccount(mail string) error +} diff --git a/pkg/service/googledrive/google_drive.go b/pkg/service/googledrive/google_drive.go index d1b394fce..0e31bb85b 100644 --- a/pkg/service/googledrive/google_drive.go +++ b/pkg/service/googledrive/google_drive.go @@ -26,7 +26,7 @@ type googleService struct { } // New function return Google service -func New(config *oauth2.Config, appConfig *config.Config) Service { +func New(config *oauth2.Config, appConfig *config.Config) IService { return &googleService{ config: config, appConfig: appConfig, diff --git a/pkg/service/googledrive/interface.go b/pkg/service/googledrive/interface.go index 7b40745e2..30ae4b046 100644 --- a/pkg/service/googledrive/interface.go +++ b/pkg/service/googledrive/interface.go @@ -10,7 +10,7 @@ var ( ErrInvoicePDFNotFound = errors.New("invoice pdf not found") ) -type Service interface { +type IService interface { UploadInvoicePDF(invoice *model.Invoice, dirName string) error MoveInvoicePDF(invoice *model.Invoice, fromDirName, toDirName string) error DownloadInvoicePDF(invoice *model.Invoice, dirName string) ([]byte, error) diff --git a/pkg/service/mochi/mochi.go b/pkg/service/mochi/mochi.go index 8718b2339..2efa61c89 100644 --- a/pkg/service/mochi/mochi.go +++ b/pkg/service/mochi/mochi.go @@ -15,19 +15,19 @@ type IService interface { GetVaultTransaction(req *model.VaultTransactionRequest) (*model.VaultTransactionResponse, error) } -type mochiClient struct { +type client struct { cfg *config.Config l logger.Logger } func New(cfg *config.Config, l logger.Logger) IService { - return &mochiClient{ + return &client{ cfg: cfg, l: l, } } -func (m *mochiClient) GetVaultTransaction(req *model.VaultTransactionRequest) (*model.VaultTransactionResponse, error) { +func (m *client) GetVaultTransaction(req *model.VaultTransactionRequest) (*model.VaultTransactionResponse, error) { var client = &http.Client{} request, err := http.NewRequest("GET", fmt.Sprintf("%s/api/v1/vault/%s/transaction?start_time=%s&end_time=%s", m.cfg.Mochi.BaseURL, req.VaultId, req.StartTime, req.EndTime), nil) if err != nil { diff --git a/pkg/service/sendgrid/sendgrid.go b/pkg/service/sendgrid/sendgrid.go index e686525a1..53e693afc 100644 --- a/pkg/service/sendgrid/sendgrid.go +++ b/pkg/service/sendgrid/sendgrid.go @@ -10,7 +10,7 @@ import ( "github.com/sendgrid/sendgrid-go/helpers/mail" ) -type Service interface { +type IService interface { SendEmail(*model.Email) error } @@ -20,7 +20,7 @@ type sendgridClient struct { l logger.Logger } -func New(key string, cfg *config.Config, l logger.Logger) Service { +func New(key string, cfg *config.Config, l logger.Logger) IService { client := sendgrid.NewSendClient(key) return &sendgridClient{ client: client, diff --git a/pkg/service/service.go b/pkg/service/service.go index 6bb91f3e0..7b5c63de1 100644 --- a/pkg/service/service.go +++ b/pkg/service/service.go @@ -3,10 +3,11 @@ package service import ( "time" - cache "github.com/patrickmn/go-cache" + "github.com/patrickmn/go-cache" "golang.org/x/oauth2" "golang.org/x/oauth2/google" - gmail "google.golang.org/api/gmail/v1" + admin "google.golang.org/api/admin/directory/v1" + "google.golang.org/api/gmail/v1" "github.com/dwarvesf/fortress-api/pkg/config" "github.com/dwarvesf/fortress-api/pkg/logger" @@ -14,7 +15,9 @@ import ( "github.com/dwarvesf/fortress-api/pkg/service/basecamp/model" "github.com/dwarvesf/fortress-api/pkg/service/currency" "github.com/dwarvesf/fortress-api/pkg/service/discord" + "github.com/dwarvesf/fortress-api/pkg/service/github" googleauth "github.com/dwarvesf/fortress-api/pkg/service/google" + "github.com/dwarvesf/fortress-api/pkg/service/googleadmin" "github.com/dwarvesf/fortress-api/pkg/service/googledrive" "github.com/dwarvesf/fortress-api/pkg/service/googlemail" "github.com/dwarvesf/fortress-api/pkg/service/improvmx" @@ -30,13 +33,15 @@ type Service struct { Cache *cache.Cache Currency currency.IService Discord discord.IService + Github github.IService Google googleauth.IService - GoogleDrive googledrive.Service + GoogleDrive googledrive.IService GoogleMail googlemail.IService + GoogleAdmin googleadmin.IService ImprovMX improvmx.IService Mochi mochi.IService Notion notion.IService - Sendgrid sendgrid.Service + Sendgrid sendgrid.IService Wise wise.IService } @@ -69,6 +74,16 @@ func New(cfg *config.Config, store *store.Store, repo store.DBRepo) *Service { googleDriveSvc := googledrive.New(driveConfig, cfg) + googleAdminConfig := &oauth2.Config{ + ClientID: cfg.Google.ClientID, + ClientSecret: cfg.Google.ClientSecret, + Endpoint: google.Endpoint, + Scopes: []string{admin.AdminDirectoryUserScope, + admin.AdminDirectoryGroupScope, + }, + } + googleAdminSvc := googleadmin.New(googleAdminConfig, cfg) + mailConfig := &oauth2.Config{ ClientID: cfg.Google.ClientID, ClientSecret: cfg.Google.ClientSecret, @@ -76,7 +91,7 @@ func New(cfg *config.Config, store *store.Store, repo store.DBRepo) *Service { Scopes: []string{gmail.MailGoogleComScope}, } - googleMailService := googlemail.New( + googleMailSvc := googlemail.New( mailConfig, cfg, ) @@ -88,13 +103,15 @@ func New(cfg *config.Config, store *store.Store, repo store.DBRepo) *Service { Currency := currency.New(cfg) return &Service{ - Basecamp: basecamp.NewService(store, repo, cfg, &bc, logger.L), + Basecamp: basecamp.New(store, repo, cfg, &bc, logger.L), Cache: cch, Currency: Currency, Discord: discord.New(cfg), + Github: github.New(cfg, logger.L), Google: googleSvc, + GoogleAdmin: googleAdminSvc, GoogleDrive: googleDriveSvc, - GoogleMail: googleMailService, + GoogleMail: googleMailSvc, ImprovMX: improvmx.New(cfg.ImprovMX.Token), Mochi: mochi.New(cfg, logger.L), Notion: notion.New(cfg.Notion.Secret, cfg.Notion.Databases.Project, logger.L), diff --git a/pkg/store/accounting/pg.go b/pkg/store/accounting/accounting.go similarity index 100% rename from pkg/store/accounting/pg.go rename to pkg/store/accounting/accounting.go diff --git a/pkg/store/accounting/store.go b/pkg/store/accounting/interface.go similarity index 100% rename from pkg/store/accounting/store.go rename to pkg/store/accounting/interface.go diff --git a/pkg/store/cachedpayroll/pg.go b/pkg/store/cachedpayroll/cached_payroll.go similarity index 100% rename from pkg/store/cachedpayroll/pg.go rename to pkg/store/cachedpayroll/cached_payroll.go diff --git a/pkg/store/commission/interface.go b/pkg/store/commission/interface.go deleted file mode 100644 index 56bcae0ce..000000000 --- a/pkg/store/commission/interface.go +++ /dev/null @@ -1,26 +0,0 @@ -package commission - -import ( - "time" - - "gorm.io/gorm" - - "github.com/dwarvesf/fortress-api/pkg/model" -) - -// IStore is an interface that abstract database method for commission -type IStore interface { - // Create new row for table user_commissions, save the commission - // for an user for specific invoice - Create(db *gorm.DB, us []model.EmployeeCommission) error - - Get(db *gorm.DB, q Query) ([]model.EmployeeCommission, error) - MarkPaid(db *gorm.DB, ids model.UUID) error -} - -type Query struct { - EmployeeID string - FromDate *time.Time - ToDate *time.Time - IsPaid bool -} diff --git a/pkg/store/commission/pg.go b/pkg/store/commission/pg.go deleted file mode 100644 index 9fc43fd72..000000000 --- a/pkg/store/commission/pg.go +++ /dev/null @@ -1,49 +0,0 @@ -package commission - -import ( - "time" - - "gorm.io/gorm" - - "github.com/dwarvesf/fortress-api/pkg/model" -) - -type store struct{} - -// New initilize new store for commission -func New() IStore { - return &store{} -} - -func (s *store) Create(db *gorm.DB, us []model.EmployeeCommission) error { - for i := range us { - err := db.Create(&us[i]).Error - if err != nil { - return err - } - } - return nil -} - -func (s *store) Get(db *gorm.DB, q Query) ([]model.EmployeeCommission, error) { - var res []model.EmployeeCommission - if q.EmployeeID != "" { - db = db.Where("employee_id = ?", q.EmployeeID) - } - if q.FromDate != nil { - db = db.Where("created_at > ?", q.FromDate) - } - if q.ToDate != nil { - db = db.Where("created_at < ?", q.ToDate) - } - return res, db.Preload("Invoice").Where("is_paid = ?", q.IsPaid).Find(&res).Error -} - -func (s *store) MarkPaid(db *gorm.DB, id model.UUID) error { - var cms model.EmployeeCommission - err := db.Where("id = ?", id).Find(&cms).Error - if err != nil { - return err - } - return db.Model(&cms).Updates(map[string]interface{}{"is_paid": true, "paid_at": time.Now()}).Error -} diff --git a/pkg/store/currency/pg.go b/pkg/store/currency/currency.go similarity index 100% rename from pkg/store/currency/pg.go rename to pkg/store/currency/currency.go diff --git a/pkg/store/currency/store.go b/pkg/store/currency/interface.go similarity index 100% rename from pkg/store/currency/store.go rename to pkg/store/currency/interface.go diff --git a/pkg/store/bonus/pg.go b/pkg/store/employeebonus/employee_bonus.go similarity index 85% rename from pkg/store/bonus/pg.go rename to pkg/store/employeebonus/employee_bonus.go index da2a1238a..09d79552c 100644 --- a/pkg/store/bonus/pg.go +++ b/pkg/store/employeebonus/employee_bonus.go @@ -1,4 +1,4 @@ -package bonus +package employeebonus import ( "gorm.io/gorm" @@ -8,7 +8,7 @@ import ( type store struct{} -// New initilize new store for bonus +// New initialize new store for bonus func New() IStore { return &store{} } diff --git a/pkg/store/bonus/interface.go b/pkg/store/employeebonus/interface.go similarity index 66% rename from pkg/store/bonus/interface.go rename to pkg/store/employeebonus/interface.go index 76eaa5c89..7a0081e6b 100644 --- a/pkg/store/bonus/interface.go +++ b/pkg/store/employeebonus/interface.go @@ -1,4 +1,4 @@ -package bonus +package employeebonus import ( "gorm.io/gorm" @@ -6,7 +6,7 @@ import ( "github.com/dwarvesf/fortress-api/pkg/model" ) -// Store is an interface that abstract database method for bonus +// IStore is an interface that abstract database method for bonus type IStore interface { GetByUserID(db *gorm.DB, id model.UUID) ([]model.EmployeeBonus, error) } diff --git a/pkg/store/employeecommission/employee_commission.go b/pkg/store/employeecommission/employee_commission.go index 57600e139..d6cf3a03c 100644 --- a/pkg/store/employeecommission/employee_commission.go +++ b/pkg/store/employeecommission/employee_commission.go @@ -1,6 +1,8 @@ package employeecommission import ( + "time" + "gorm.io/gorm" "github.com/dwarvesf/fortress-api/pkg/model" @@ -8,11 +10,34 @@ import ( type store struct{} +// New initialize new store for commission func New() IStore { return &store{} } -// Create make new one by id func (s *store) Create(db *gorm.DB, employeeCommissions []model.EmployeeCommission) ([]model.EmployeeCommission, error) { return employeeCommissions, db.Create(&employeeCommissions).Error } + +func (s *store) Get(db *gorm.DB, q Query) ([]model.EmployeeCommission, error) { + var res []model.EmployeeCommission + if q.EmployeeID != "" { + db = db.Where("employee_id = ?", q.EmployeeID) + } + if q.FromDate != nil { + db = db.Where("created_at > ?", q.FromDate) + } + if q.ToDate != nil { + db = db.Where("created_at < ?", q.ToDate) + } + return res, db.Preload("Invoice").Where("is_paid = ?", q.IsPaid).Find(&res).Error +} + +func (s *store) MarkPaid(db *gorm.DB, id model.UUID) error { + var cms model.EmployeeCommission + err := db.Where("id = ?", id).Find(&cms).Error + if err != nil { + return err + } + return db.Model(&cms).Updates(map[string]interface{}{"is_paid": true, "paid_at": time.Now()}).Error +} diff --git a/pkg/store/commission/pg_test.go b/pkg/store/employeecommission/employee_commission_test.go similarity index 99% rename from pkg/store/commission/pg_test.go rename to pkg/store/employeecommission/employee_commission_test.go index 577d47517..489fa0971 100644 --- a/pkg/store/commission/pg_test.go +++ b/pkg/store/employeecommission/employee_commission_test.go @@ -1,4 +1,4 @@ -package commission +package employeecommission // func TestCreate(t *testing.T) { // t.Parallel() diff --git a/pkg/store/employeecommission/interface.go b/pkg/store/employeecommission/interface.go index 787865760..4444fabd0 100644 --- a/pkg/store/employeecommission/interface.go +++ b/pkg/store/employeecommission/interface.go @@ -1,11 +1,25 @@ package employeecommission import ( + "time" + "gorm.io/gorm" "github.com/dwarvesf/fortress-api/pkg/model" ) +// IStore is an interface that abstract database method for commission type IStore interface { + // Create new row for table user_commissions, save the commission + // for an user for specific invoice Create(db *gorm.DB, employeeCommissions []model.EmployeeCommission) ([]model.EmployeeCommission, error) + Get(db *gorm.DB, q Query) ([]model.EmployeeCommission, error) + MarkPaid(db *gorm.DB, ids model.UUID) error +} + +type Query struct { + EmployeeID string + FromDate *time.Time + ToDate *time.Time + IsPaid bool } diff --git a/pkg/store/expense/pg.go b/pkg/store/expense/expense.go similarity index 51% rename from pkg/store/expense/pg.go rename to pkg/store/expense/expense.go index 33a0e94a2..f8ac74c85 100644 --- a/pkg/store/expense/pg.go +++ b/pkg/store/expense/expense.go @@ -6,31 +6,31 @@ import ( "github.com/dwarvesf/fortress-api/pkg/model" ) -type pgService struct { +type store struct { } func New() IStore { - return &pgService{} + return &store{} } -func (s *pgService) Create(db *gorm.DB, e *model.Expense) (*model.Expense, error) { +func (s *store) Create(db *gorm.DB, e *model.Expense) (*model.Expense, error) { return e, db.Create(&e).Error } -func (s *pgService) Delete(db *gorm.DB, e *model.Expense) (*model.Expense, error) { +func (s *store) Delete(db *gorm.DB, e *model.Expense) (*model.Expense, error) { return e, db.Delete(&e).Error } -func (s *pgService) Update(db *gorm.DB, e *model.Expense) (*model.Expense, error) { +func (s *store) Update(db *gorm.DB, e *model.Expense) (*model.Expense, error) { return e, db.Model(&model.Expense{}).Updates(&e).Error } -func (s *pgService) GetValuation(db *gorm.DB, y int) (*model.CurrencyView, error) { +func (s *store) GetValuation(db *gorm.DB, y int) (*model.CurrencyView, error) { res := &model.CurrencyView{} return res, db.Raw("select * from vw_expense where year = ?", y).Find(&res).Error } -func (s *pgService) GetByQuery(db *gorm.DB, q *ExpenseQuery) (*model.Expense, error) { +func (s *store) GetByQuery(db *gorm.DB, q *ExpenseQuery) (*model.Expense, error) { e := &model.Expense{} if q.BasecampID != 0 { db = db.Where("basecamp_id = ?", q.BasecampID) diff --git a/pkg/store/expense/store.go b/pkg/store/expense/interface.go similarity index 100% rename from pkg/store/expense/store.go rename to pkg/store/expense/interface.go diff --git a/pkg/store/payroll/interface.go b/pkg/store/payroll/interface.go index a56fef63f..3450c32da 100644 --- a/pkg/store/payroll/interface.go +++ b/pkg/store/payroll/interface.go @@ -10,7 +10,7 @@ import ( // IStore implement operation method type IStore interface { - // TODO: rename to GetList + // GetSalary TODO: rename to GetList GetSalary(db *gorm.DB, year int, month int) ([]model.Employee, error) Get(db *gorm.DB, userId string, year, month int) (*model.Payroll, error) Create(db *gorm.DB, p *model.Payroll) error diff --git a/pkg/store/payroll/pg.go b/pkg/store/payroll/payroll.go similarity index 99% rename from pkg/store/payroll/pg.go rename to pkg/store/payroll/payroll.go index 5d025e397..e6348ebb5 100644 --- a/pkg/store/payroll/pg.go +++ b/pkg/store/payroll/payroll.go @@ -18,7 +18,7 @@ func New() IStore { } func (s *store) GetSalary(db *gorm.DB, year int, month int) ([]model.Employee, error) { - employees := []model.Employee{} + var employees []model.Employee return employees, db.Table("employee"). Where("status = <> ?", model.WorkingStatusLeft). diff --git a/pkg/store/payroll/pg_test.go b/pkg/store/payroll/payroll_test.go similarity index 100% rename from pkg/store/payroll/pg_test.go rename to pkg/store/payroll/payroll_test.go diff --git a/pkg/store/projectmember/interface.go b/pkg/store/projectmember/interface.go index 1d172adeb..044c1ed50 100644 --- a/pkg/store/projectmember/interface.go +++ b/pkg/store/projectmember/interface.go @@ -1,8 +1,11 @@ package projectmember import ( - "github.com/dwarvesf/fortress-api/pkg/model" + "time" + "gorm.io/gorm" + + "github.com/dwarvesf/fortress-api/pkg/model" ) type IStore interface { @@ -20,6 +23,7 @@ type IStore interface { UpdateEndDateOverdueMemberToInActive(db *gorm.DB) error UpdateLeftMemberToInActive(db *gorm.DB) error UpdateMemberInClosedProjectToInActive(db *gorm.DB) error + UpdateMemberToInActiveByID(db *gorm.DB, id string, endDate *time.Time) error UpdateSelectedFieldByProjectID(db *gorm.DB, projectID string, updateModel model.ProjectMember, updatedField string) error UpdateSelectedFieldsByID(db *gorm.DB, id string, updateModel model.ProjectMember, updatedFields ...string) (*model.ProjectMember, error) } diff --git a/pkg/store/projectmember/project_member.go b/pkg/store/projectmember/project_member.go index 8e5898f1a..57bc3b126 100644 --- a/pkg/store/projectmember/project_member.go +++ b/pkg/store/projectmember/project_member.go @@ -185,3 +185,17 @@ func (s *store) UpdateLeftMemberToInActive(db *gorm.DB) error { ` return db.Exec(sql).Error } + +func (s *store) UpdateMemberToInActiveByID(db *gorm.DB, id string, endDate *time.Time) error { + sql := ` + UPDATE project_members pm + SET + status = 'inactive', + end_date = ? + WHERE + employee_id = ? + AND (status <> 'inactive' OR end_date IS NULL OR end_date > NOW()) + + ` + return db.Exec(sql, endDate, id).Error +} diff --git a/pkg/store/recruitment/store.go b/pkg/store/recruitment/interface.go similarity index 100% rename from pkg/store/recruitment/store.go rename to pkg/store/recruitment/interface.go diff --git a/pkg/store/recruitment/pg.go b/pkg/store/recruitment/recruitment.go similarity index 100% rename from pkg/store/recruitment/pg.go rename to pkg/store/recruitment/recruitment.go diff --git a/pkg/store/store.go b/pkg/store/store.go index a85cfbf33..8b1318e0b 100644 --- a/pkg/store/store.go +++ b/pkg/store/store.go @@ -13,18 +13,17 @@ import ( "github.com/dwarvesf/fortress-api/pkg/store/auditparticipant" "github.com/dwarvesf/fortress-api/pkg/store/bankaccount" "github.com/dwarvesf/fortress-api/pkg/store/basesalary" - "github.com/dwarvesf/fortress-api/pkg/store/bonus" "github.com/dwarvesf/fortress-api/pkg/store/cachedpayroll" "github.com/dwarvesf/fortress-api/pkg/store/chapter" "github.com/dwarvesf/fortress-api/pkg/store/client" "github.com/dwarvesf/fortress-api/pkg/store/clientcontact" - "github.com/dwarvesf/fortress-api/pkg/store/commission" "github.com/dwarvesf/fortress-api/pkg/store/content" "github.com/dwarvesf/fortress-api/pkg/store/country" "github.com/dwarvesf/fortress-api/pkg/store/currency" "github.com/dwarvesf/fortress-api/pkg/store/dashboard" "github.com/dwarvesf/fortress-api/pkg/store/discordtemplate" "github.com/dwarvesf/fortress-api/pkg/store/employee" + "github.com/dwarvesf/fortress-api/pkg/store/employeebonus" "github.com/dwarvesf/fortress-api/pkg/store/employeechapter" "github.com/dwarvesf/fortress-api/pkg/store/employeecommission" "github.com/dwarvesf/fortress-api/pkg/store/employeeeventquestion" @@ -80,19 +79,18 @@ type Store struct { AuditParticipant auditparticipant.IStore BankAccount bankaccount.IStore BaseSalary basesalary.IStore - Bonus bonus.IStore + Bonus employeebonus.IStore CachedPayroll cachedpayroll.IStore Chapter chapter.IStore Client client.IStore ClientContact clientcontact.IStore - Commission commission.IStore + EmployeeCommission employeecommission.IStore Content content.IStore Country country.IStore Currency currency.IStore Dashboard dashboard.IStore Employee employee.IStore EmployeeChapter employeechapter.IStore - EmployeeCommission employeecommission.IStore EmployeeEventQuestion employeeeventquestion.IStore EmployeeEventReviewer employeeeventreviewer.IStore EmployeeEventTopic employeeeventtopic.IStore @@ -148,12 +146,11 @@ func New() *Store { AuditParticipant: auditparticipant.New(), BankAccount: bankaccount.New(), BaseSalary: basesalary.New(), - Bonus: bonus.New(), + Bonus: employeebonus.New(), CachedPayroll: cachedpayroll.New(), Chapter: chapter.New(), Client: client.New(), ClientContact: clientcontact.New(), - Commission: commission.New(), Content: content.New(), Country: country.New(), Currency: currency.New(), diff --git a/pkg/utils/currency.go b/pkg/utils/currency.go index 2d73ac553..518e75411 100644 --- a/pkg/utils/currency.go +++ b/pkg/utils/currency.go @@ -7,7 +7,7 @@ import ( func FormatCurrencyAmount(n int) string { milPart := n / 1000000 thouPart := (n % 1000000) / 1000 - hunPart := (n % 1000) + hunPart := n % 1000 var res string if milPart != 0 { res = fmt.Sprintf("%d", milPart)