diff --git a/go.mod b/go.mod index 9d7d057..1df4a7a 100644 --- a/go.mod +++ b/go.mod @@ -3,5 +3,24 @@ module github.com/valek177/platform-common go 1.23 require ( + github.com/georgysavva/scany v1.2.2 + github.com/gojuno/minimock/v3 v3.4.1 + github.com/jackc/pgconn v1.14.3 + github.com/jackc/pgx/v4 v4.18.3 + github.com/joho/godotenv v1.5.1 github.com/pkg/errors v0.9.1 ) + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/jackc/chunkreader/v2 v2.0.1 // indirect + github.com/jackc/pgio v1.0.0 // indirect + github.com/jackc/pgpassfile v1.0.0 // indirect + github.com/jackc/pgproto3/v2 v2.3.3 // indirect + github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect + github.com/jackc/pgtype v1.14.0 // indirect + github.com/jackc/puddle v1.3.0 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + golang.org/x/crypto v0.20.0 // indirect + golang.org/x/text v0.14.0 // indirect +) diff --git a/go.sum b/go.sum index e69de29..8b54171 100644 --- a/go.sum +++ b/go.sum @@ -0,0 +1,220 @@ +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= +github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= +github.com/cockroachdb/cockroach-go/v2 v2.2.0/go.mod h1:u3MiKYGupPPjkn3ozknpMUpxPaNLTFWAya419/zv6eI= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/georgysavva/scany v1.2.2 h1:ckhXrq3HuM+myrLaYg9fEbA/gUFysUz8NSWq12DjoGU= +github.com/georgysavva/scany v1.2.2/go.mod h1:vGBpL5XRLOocMFFa55pj0P04DrL3I7qKVRL49K6Eu5o= +github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= +github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= +github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= +github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/gojuno/minimock/v3 v3.4.1 h1:Flf735K7TT45TKCUMG4fz1vwadW/cW0Q0wH8x7eJKos= +github.com/gojuno/minimock/v3 v3.4.1/go.mod h1:mpNkl275+w8a6CYjeCHIRfN8QzN2R7ejT6jEDUdweuo= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/jackc/chunkreader v1.0.0 h1:4s39bBR8ByfqH+DKm8rQA3E1LHZWB9XWcrz8fqaZbe0= +github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo= +github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= +github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8= +github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= +github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA= +github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE= +github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s= +github.com/jackc/pgconn v1.4.0/go.mod h1:Y2O3ZDF0q4mMacyWV3AstPJpeHXWGEetiFttmq5lahk= +github.com/jackc/pgconn v1.5.0/go.mod h1:QeD3lBfpTFe8WUnPZWN5KY/mB8FGMIYRdd8P8Jr0fAI= +github.com/jackc/pgconn v1.5.1-0.20200601181101-fa742c524853/go.mod h1:QeD3lBfpTFe8WUnPZWN5KY/mB8FGMIYRdd8P8Jr0fAI= +github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o= +github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY= +github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI= +github.com/jackc/pgconn v1.14.3 h1:bVoTr12EGANZz66nZPkMInAV/KHD2TxH9npjXXgiB3w= +github.com/jackc/pgconn v1.14.3/go.mod h1:RZbme4uasqzybK2RK5c65VsHxoyaml09lx3tXOcO/VM= +github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE= +github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8= +github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE= +github.com/jackc/pgmock v0.0.0-20201204152224-4fe30f7445fd/go.mod h1:hrBW0Enj2AZTNpt/7Y5rr2xe/9Mn757Wtb2xeBzPv2c= +github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65/go.mod h1:5R2h2EEX+qri8jOWMbJCtaPWkrrNc7OHwsp2TCqp7ak= +github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= +github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= +github.com/jackc/pgproto3 v1.1.0 h1:FYYE4yRw+AgI8wXIinMlNjBbp/UitDJwfj5LqqewP1A= +github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78= +github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA= +github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg= +github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= +github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= +github.com/jackc/pgproto3/v2 v2.0.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= +github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= +github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= +github.com/jackc/pgproto3/v2 v2.3.3 h1:1HLSx5H+tXR9pW3in3zaztoEwQYRC9SQaYUHjTSUOag= +github.com/jackc/pgproto3/v2 v2.3.3/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= +github.com/jackc/pgservicefile v0.0.0-20200307190119-3430c5407db8/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= +github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= +github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk= +github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg= +github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc= +github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw= +github.com/jackc/pgtype v1.2.0/go.mod h1:5m2OfMh1wTK7x+Fk952IDmI4nw3nPrvtQdM0ZT4WpC0= +github.com/jackc/pgtype v1.3.1-0.20200510190516-8cd94a14c75a/go.mod h1:vaogEUkALtxZMCH411K+tKzNpwzCKU+AnPzBKZ+I+Po= +github.com/jackc/pgtype v1.3.1-0.20200606141011-f6355165a91c/go.mod h1:cvk9Bgu/VzJ9/lxTO5R5sf80p0DiucVtN7ZxvaC4GmQ= +github.com/jackc/pgtype v1.6.2/go.mod h1:JCULISAZBFGrHaOXIIFiyfzW5VY0GRitRr8NeJsrdig= +github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM= +github.com/jackc/pgtype v1.14.0 h1:y+xUdabmyMkJLyApYuPj38mW+aAIqCe5uuBB51rH3Vw= +github.com/jackc/pgtype v1.14.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4= +github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y= +github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM= +github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc= +github.com/jackc/pgx/v4 v4.5.0/go.mod h1:EpAKPLdnTorwmPUUsqrPxy5fphV18j9q3wrfRXgo+kA= +github.com/jackc/pgx/v4 v4.6.1-0.20200510190926-94ba730bb1e9/go.mod h1:t3/cdRQl6fOLDxqtlyhe9UWgfIi9R8+8v8GKV5TRA/o= +github.com/jackc/pgx/v4 v4.6.1-0.20200606145419-4e5062306904/go.mod h1:ZDaNWkt9sW1JMiNn0kdYBaLelIhw7Pg4qd+Vk6tw7Hg= +github.com/jackc/pgx/v4 v4.10.1/go.mod h1:QlrWebbs3kqEZPHCTGyxecvzG6tvIsYu+A5b1raylkA= +github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs= +github.com/jackc/pgx/v4 v4.18.3 h1:dE2/TrEsGX3RBprb3qryqSV9Y60iZN1C6i8IrmW9/BA= +github.com/jackc/pgx/v4 v4.18.3/go.mod h1:Ey4Oru5tH5sB6tV7hDmfWFahwF15Eb7DNXlRKx2CkVw= +github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jackc/puddle v1.1.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jackc/puddle v1.1.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jackc/puddle v1.3.0 h1:eHK/5clGOatcjX3oWGBO/MpxpbHzSwud5EWTSCI+MX0= +github.com/jackc/puddle v1.3.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= +github.com/jinzhu/now v1.1.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +github.com/jmoiron/sqlx v1.3.1/go.mod h1:2BljVx/86SuTyjE+aPYlHCTNvZrnJXghYGpNiXLBMCQ= +github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +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= +github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.10.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= +github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= +github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= +github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= +github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= +github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= +github.com/shopspring/decimal v0.0.0-20200227202807-02e2044944cc/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= +github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= +github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= +go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= +go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= +go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= +go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.20.0 h1:jmAMJJZXr5KiCw05dfYK9QnqaqKLYXijU23lsEdcQqg= +golang.org/x/crypto v0.20.0/go.mod h1:Xwo95rrVNIoSMx9wa1JroENMToLWn3RNVrTBpLHgZPQ= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +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-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/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/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +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= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/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/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +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= +gorm.io/driver/postgres v1.0.8/go.mod h1:4eOzrI1MUfm6ObJU/UcmbXyiHSs8jSwH95G5P5dxcAg= +gorm.io/gorm v1.20.12/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw= +gorm.io/gorm v1.21.4/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= diff --git a/pkg/client/db/db.go b/pkg/client/db/db.go new file mode 100644 index 0000000..86cfddc --- /dev/null +++ b/pkg/client/db/db.go @@ -0,0 +1,66 @@ +package db + +import ( + "context" + + "github.com/jackc/pgconn" + "github.com/jackc/pgx/v4" +) + +// Handler - функция, которая выполняется в транзакции +type Handler func(ctx context.Context) error + +// Client клиент для работы с БД +type Client interface { + DB() DB + Close() error +} + +// TxManager менеджер транзакций, который выполняет указанный пользователем обработчик в транзакции +type TxManager interface { + ReadCommitted(ctx context.Context, f Handler) error +} + +// Query обертка над запросом, хранящая имя запроса и сам запрос +// Имя запроса используется для логирования и потенциально может использоваться еще где-то, например, для трейсинга +type Query struct { + Name string + QueryRaw string +} + +// Transactor интерфейс для работы с транзакциями +type Transactor interface { + BeginTx(ctx context.Context, txOptions pgx.TxOptions) (pgx.Tx, error) +} + +// SQLExecer комбинирует NamedExecer и QueryExecer +type SQLExecer interface { + NamedExecer + QueryExecer +} + +// NamedExecer интерфейс для работы с именованными запросами с помощью тегов в структурах +type NamedExecer interface { + ScanOneContext(ctx context.Context, dest interface{}, q Query, args ...interface{}) error + ScanAllContext(ctx context.Context, dest interface{}, q Query, args ...interface{}) error +} + +// QueryExecer интерфейс для работы с обычными запросами +type QueryExecer interface { + ExecContext(ctx context.Context, q Query, args ...interface{}) (pgconn.CommandTag, error) + QueryContext(ctx context.Context, q Query, args ...interface{}) (pgx.Rows, error) + QueryRowContext(ctx context.Context, q Query, args ...interface{}) pgx.Row +} + +// Pinger интерфейс для проверки соединения с БД +type Pinger interface { + Ping(ctx context.Context) error +} + +// DB интерфейс для работы с БД +type DB interface { + SQLExecer + Transactor + Pinger + Close() +} diff --git a/pkg/client/db/generate.go b/pkg/client/db/generate.go new file mode 100644 index 0000000..b9793e9 --- /dev/null +++ b/pkg/client/db/generate.go @@ -0,0 +1,4 @@ +package db + +//go:generate sh -c "rm -rf mocks && mkdir -p mocks" +//go:generate minimock -i TxManager -o ./mocks/ -s "_minimock.go" diff --git a/pkg/client/db/mocks/tx_manager_minimock.go b/pkg/client/db/mocks/tx_manager_minimock.go new file mode 100644 index 0000000..51f7c2d --- /dev/null +++ b/pkg/client/db/mocks/tx_manager_minimock.go @@ -0,0 +1,417 @@ +// Code generated by http://github.com/gojuno/minimock (v3.4.1). DO NOT EDIT. + +package mocks + +//go:generate minimock -i github.com/valek177/platform-common/pkg/client/db.TxManager -o tx_manager_minimock.go -n TxManagerMock -p mocks + +import ( + "context" + "sync" + mm_atomic "sync/atomic" + mm_time "time" + + "github.com/gojuno/minimock/v3" + mm_db "github.com/valek177/platform-common/pkg/client/db" +) + +// TxManagerMock implements mm_db.TxManager +type TxManagerMock struct { + t minimock.Tester + finishOnce sync.Once + + funcReadCommitted func(ctx context.Context, f mm_db.Handler) (err error) + funcReadCommittedOrigin string + inspectFuncReadCommitted func(ctx context.Context, f mm_db.Handler) + afterReadCommittedCounter uint64 + beforeReadCommittedCounter uint64 + ReadCommittedMock mTxManagerMockReadCommitted +} + +// NewTxManagerMock returns a mock for mm_db.TxManager +func NewTxManagerMock(t minimock.Tester) *TxManagerMock { + m := &TxManagerMock{t: t} + + if controller, ok := t.(minimock.MockController); ok { + controller.RegisterMocker(m) + } + + m.ReadCommittedMock = mTxManagerMockReadCommitted{mock: m} + m.ReadCommittedMock.callArgs = []*TxManagerMockReadCommittedParams{} + + t.Cleanup(m.MinimockFinish) + + return m +} + +type mTxManagerMockReadCommitted struct { + optional bool + mock *TxManagerMock + defaultExpectation *TxManagerMockReadCommittedExpectation + expectations []*TxManagerMockReadCommittedExpectation + + callArgs []*TxManagerMockReadCommittedParams + mutex sync.RWMutex + + expectedInvocations uint64 + expectedInvocationsOrigin string +} + +// TxManagerMockReadCommittedExpectation specifies expectation struct of the TxManager.ReadCommitted +type TxManagerMockReadCommittedExpectation struct { + mock *TxManagerMock + params *TxManagerMockReadCommittedParams + paramPtrs *TxManagerMockReadCommittedParamPtrs + expectationOrigins TxManagerMockReadCommittedExpectationOrigins + results *TxManagerMockReadCommittedResults + returnOrigin string + Counter uint64 +} + +// TxManagerMockReadCommittedParams contains parameters of the TxManager.ReadCommitted +type TxManagerMockReadCommittedParams struct { + ctx context.Context + f mm_db.Handler +} + +// TxManagerMockReadCommittedParamPtrs contains pointers to parameters of the TxManager.ReadCommitted +type TxManagerMockReadCommittedParamPtrs struct { + ctx *context.Context + f *mm_db.Handler +} + +// TxManagerMockReadCommittedResults contains results of the TxManager.ReadCommitted +type TxManagerMockReadCommittedResults struct { + err error +} + +// TxManagerMockReadCommittedOrigins contains origins of expectations of the TxManager.ReadCommitted +type TxManagerMockReadCommittedExpectationOrigins struct { + origin string + originCtx string + originF string +} + +// Marks this method to be optional. The default behavior of any method with Return() is '1 or more', meaning +// the test will fail minimock's automatic final call check if the mocked method was not called at least once. +// Optional() makes method check to work in '0 or more' mode. +// It is NOT RECOMMENDED to use this option unless you really need it, as default behaviour helps to +// catch the problems when the expected method call is totally skipped during test run. +func (mmReadCommitted *mTxManagerMockReadCommitted) Optional() *mTxManagerMockReadCommitted { + mmReadCommitted.optional = true + return mmReadCommitted +} + +// Expect sets up expected params for TxManager.ReadCommitted +func (mmReadCommitted *mTxManagerMockReadCommitted) Expect(ctx context.Context, f mm_db.Handler) *mTxManagerMockReadCommitted { + if mmReadCommitted.mock.funcReadCommitted != nil { + mmReadCommitted.mock.t.Fatalf("TxManagerMock.ReadCommitted mock is already set by Set") + } + + if mmReadCommitted.defaultExpectation == nil { + mmReadCommitted.defaultExpectation = &TxManagerMockReadCommittedExpectation{} + } + + if mmReadCommitted.defaultExpectation.paramPtrs != nil { + mmReadCommitted.mock.t.Fatalf("TxManagerMock.ReadCommitted mock is already set by ExpectParams functions") + } + + mmReadCommitted.defaultExpectation.params = &TxManagerMockReadCommittedParams{ctx, f} + mmReadCommitted.defaultExpectation.expectationOrigins.origin = minimock.CallerInfo(1) + for _, e := range mmReadCommitted.expectations { + if minimock.Equal(e.params, mmReadCommitted.defaultExpectation.params) { + mmReadCommitted.mock.t.Fatalf("Expectation set by When has same params: %#v", *mmReadCommitted.defaultExpectation.params) + } + } + + return mmReadCommitted +} + +// ExpectCtxParam1 sets up expected param ctx for TxManager.ReadCommitted +func (mmReadCommitted *mTxManagerMockReadCommitted) ExpectCtxParam1(ctx context.Context) *mTxManagerMockReadCommitted { + if mmReadCommitted.mock.funcReadCommitted != nil { + mmReadCommitted.mock.t.Fatalf("TxManagerMock.ReadCommitted mock is already set by Set") + } + + if mmReadCommitted.defaultExpectation == nil { + mmReadCommitted.defaultExpectation = &TxManagerMockReadCommittedExpectation{} + } + + if mmReadCommitted.defaultExpectation.params != nil { + mmReadCommitted.mock.t.Fatalf("TxManagerMock.ReadCommitted mock is already set by Expect") + } + + if mmReadCommitted.defaultExpectation.paramPtrs == nil { + mmReadCommitted.defaultExpectation.paramPtrs = &TxManagerMockReadCommittedParamPtrs{} + } + mmReadCommitted.defaultExpectation.paramPtrs.ctx = &ctx + mmReadCommitted.defaultExpectation.expectationOrigins.originCtx = minimock.CallerInfo(1) + + return mmReadCommitted +} + +// ExpectFParam2 sets up expected param f for TxManager.ReadCommitted +func (mmReadCommitted *mTxManagerMockReadCommitted) ExpectFParam2(f mm_db.Handler) *mTxManagerMockReadCommitted { + if mmReadCommitted.mock.funcReadCommitted != nil { + mmReadCommitted.mock.t.Fatalf("TxManagerMock.ReadCommitted mock is already set by Set") + } + + if mmReadCommitted.defaultExpectation == nil { + mmReadCommitted.defaultExpectation = &TxManagerMockReadCommittedExpectation{} + } + + if mmReadCommitted.defaultExpectation.params != nil { + mmReadCommitted.mock.t.Fatalf("TxManagerMock.ReadCommitted mock is already set by Expect") + } + + if mmReadCommitted.defaultExpectation.paramPtrs == nil { + mmReadCommitted.defaultExpectation.paramPtrs = &TxManagerMockReadCommittedParamPtrs{} + } + mmReadCommitted.defaultExpectation.paramPtrs.f = &f + mmReadCommitted.defaultExpectation.expectationOrigins.originF = minimock.CallerInfo(1) + + return mmReadCommitted +} + +// Inspect accepts an inspector function that has same arguments as the TxManager.ReadCommitted +func (mmReadCommitted *mTxManagerMockReadCommitted) Inspect(f func(ctx context.Context, f mm_db.Handler)) *mTxManagerMockReadCommitted { + if mmReadCommitted.mock.inspectFuncReadCommitted != nil { + mmReadCommitted.mock.t.Fatalf("Inspect function is already set for TxManagerMock.ReadCommitted") + } + + mmReadCommitted.mock.inspectFuncReadCommitted = f + + return mmReadCommitted +} + +// Return sets up results that will be returned by TxManager.ReadCommitted +func (mmReadCommitted *mTxManagerMockReadCommitted) Return(err error) *TxManagerMock { + if mmReadCommitted.mock.funcReadCommitted != nil { + mmReadCommitted.mock.t.Fatalf("TxManagerMock.ReadCommitted mock is already set by Set") + } + + if mmReadCommitted.defaultExpectation == nil { + mmReadCommitted.defaultExpectation = &TxManagerMockReadCommittedExpectation{mock: mmReadCommitted.mock} + } + mmReadCommitted.defaultExpectation.results = &TxManagerMockReadCommittedResults{err} + mmReadCommitted.defaultExpectation.returnOrigin = minimock.CallerInfo(1) + return mmReadCommitted.mock +} + +// Set uses given function f to mock the TxManager.ReadCommitted method +func (mmReadCommitted *mTxManagerMockReadCommitted) Set(f func(ctx context.Context, f mm_db.Handler) (err error)) *TxManagerMock { + if mmReadCommitted.defaultExpectation != nil { + mmReadCommitted.mock.t.Fatalf("Default expectation is already set for the TxManager.ReadCommitted method") + } + + if len(mmReadCommitted.expectations) > 0 { + mmReadCommitted.mock.t.Fatalf("Some expectations are already set for the TxManager.ReadCommitted method") + } + + mmReadCommitted.mock.funcReadCommitted = f + mmReadCommitted.mock.funcReadCommittedOrigin = minimock.CallerInfo(1) + return mmReadCommitted.mock +} + +// When sets expectation for the TxManager.ReadCommitted which will trigger the result defined by the following +// Then helper +func (mmReadCommitted *mTxManagerMockReadCommitted) When(ctx context.Context, f mm_db.Handler) *TxManagerMockReadCommittedExpectation { + if mmReadCommitted.mock.funcReadCommitted != nil { + mmReadCommitted.mock.t.Fatalf("TxManagerMock.ReadCommitted mock is already set by Set") + } + + expectation := &TxManagerMockReadCommittedExpectation{ + mock: mmReadCommitted.mock, + params: &TxManagerMockReadCommittedParams{ctx, f}, + expectationOrigins: TxManagerMockReadCommittedExpectationOrigins{origin: minimock.CallerInfo(1)}, + } + mmReadCommitted.expectations = append(mmReadCommitted.expectations, expectation) + return expectation +} + +// Then sets up TxManager.ReadCommitted return parameters for the expectation previously defined by the When method +func (e *TxManagerMockReadCommittedExpectation) Then(err error) *TxManagerMock { + e.results = &TxManagerMockReadCommittedResults{err} + return e.mock +} + +// Times sets number of times TxManager.ReadCommitted should be invoked +func (mmReadCommitted *mTxManagerMockReadCommitted) Times(n uint64) *mTxManagerMockReadCommitted { + if n == 0 { + mmReadCommitted.mock.t.Fatalf("Times of TxManagerMock.ReadCommitted mock can not be zero") + } + mm_atomic.StoreUint64(&mmReadCommitted.expectedInvocations, n) + mmReadCommitted.expectedInvocationsOrigin = minimock.CallerInfo(1) + return mmReadCommitted +} + +func (mmReadCommitted *mTxManagerMockReadCommitted) invocationsDone() bool { + if len(mmReadCommitted.expectations) == 0 && mmReadCommitted.defaultExpectation == nil && mmReadCommitted.mock.funcReadCommitted == nil { + return true + } + + totalInvocations := mm_atomic.LoadUint64(&mmReadCommitted.mock.afterReadCommittedCounter) + expectedInvocations := mm_atomic.LoadUint64(&mmReadCommitted.expectedInvocations) + + return totalInvocations > 0 && (expectedInvocations == 0 || expectedInvocations == totalInvocations) +} + +// ReadCommitted implements mm_db.TxManager +func (mmReadCommitted *TxManagerMock) ReadCommitted(ctx context.Context, f mm_db.Handler) (err error) { + mm_atomic.AddUint64(&mmReadCommitted.beforeReadCommittedCounter, 1) + defer mm_atomic.AddUint64(&mmReadCommitted.afterReadCommittedCounter, 1) + + mmReadCommitted.t.Helper() + + if mmReadCommitted.inspectFuncReadCommitted != nil { + mmReadCommitted.inspectFuncReadCommitted(ctx, f) + } + + mm_params := TxManagerMockReadCommittedParams{ctx, f} + + // Record call args + mmReadCommitted.ReadCommittedMock.mutex.Lock() + mmReadCommitted.ReadCommittedMock.callArgs = append(mmReadCommitted.ReadCommittedMock.callArgs, &mm_params) + mmReadCommitted.ReadCommittedMock.mutex.Unlock() + + for _, e := range mmReadCommitted.ReadCommittedMock.expectations { + if minimock.Equal(*e.params, mm_params) { + mm_atomic.AddUint64(&e.Counter, 1) + return e.results.err + } + } + + if mmReadCommitted.ReadCommittedMock.defaultExpectation != nil { + mm_atomic.AddUint64(&mmReadCommitted.ReadCommittedMock.defaultExpectation.Counter, 1) + mm_want := mmReadCommitted.ReadCommittedMock.defaultExpectation.params + mm_want_ptrs := mmReadCommitted.ReadCommittedMock.defaultExpectation.paramPtrs + + mm_got := TxManagerMockReadCommittedParams{ctx, f} + + if mm_want_ptrs != nil { + + if mm_want_ptrs.ctx != nil && !minimock.Equal(*mm_want_ptrs.ctx, mm_got.ctx) { + mmReadCommitted.t.Errorf("TxManagerMock.ReadCommitted got unexpected parameter ctx, expected at\n%s:\nwant: %#v\n got: %#v%s\n", + mmReadCommitted.ReadCommittedMock.defaultExpectation.expectationOrigins.originCtx, *mm_want_ptrs.ctx, mm_got.ctx, minimock.Diff(*mm_want_ptrs.ctx, mm_got.ctx)) + } + + if mm_want_ptrs.f != nil && !minimock.Equal(*mm_want_ptrs.f, mm_got.f) { + mmReadCommitted.t.Errorf("TxManagerMock.ReadCommitted got unexpected parameter f, expected at\n%s:\nwant: %#v\n got: %#v%s\n", + mmReadCommitted.ReadCommittedMock.defaultExpectation.expectationOrigins.originF, *mm_want_ptrs.f, mm_got.f, minimock.Diff(*mm_want_ptrs.f, mm_got.f)) + } + + } else if mm_want != nil && !minimock.Equal(*mm_want, mm_got) { + mmReadCommitted.t.Errorf("TxManagerMock.ReadCommitted got unexpected parameters, expected at\n%s:\nwant: %#v\n got: %#v%s\n", + mmReadCommitted.ReadCommittedMock.defaultExpectation.expectationOrigins.origin, *mm_want, mm_got, minimock.Diff(*mm_want, mm_got)) + } + + mm_results := mmReadCommitted.ReadCommittedMock.defaultExpectation.results + if mm_results == nil { + mmReadCommitted.t.Fatal("No results are set for the TxManagerMock.ReadCommitted") + } + return (*mm_results).err + } + if mmReadCommitted.funcReadCommitted != nil { + return mmReadCommitted.funcReadCommitted(ctx, f) + } + mmReadCommitted.t.Fatalf("Unexpected call to TxManagerMock.ReadCommitted. %v %v", ctx, f) + return +} + +// ReadCommittedAfterCounter returns a count of finished TxManagerMock.ReadCommitted invocations +func (mmReadCommitted *TxManagerMock) ReadCommittedAfterCounter() uint64 { + return mm_atomic.LoadUint64(&mmReadCommitted.afterReadCommittedCounter) +} + +// ReadCommittedBeforeCounter returns a count of TxManagerMock.ReadCommitted invocations +func (mmReadCommitted *TxManagerMock) ReadCommittedBeforeCounter() uint64 { + return mm_atomic.LoadUint64(&mmReadCommitted.beforeReadCommittedCounter) +} + +// Calls returns a list of arguments used in each call to TxManagerMock.ReadCommitted. +// The list is in the same order as the calls were made (i.e. recent calls have a higher index) +func (mmReadCommitted *mTxManagerMockReadCommitted) Calls() []*TxManagerMockReadCommittedParams { + mmReadCommitted.mutex.RLock() + + argCopy := make([]*TxManagerMockReadCommittedParams, len(mmReadCommitted.callArgs)) + copy(argCopy, mmReadCommitted.callArgs) + + mmReadCommitted.mutex.RUnlock() + + return argCopy +} + +// MinimockReadCommittedDone returns true if the count of the ReadCommitted invocations corresponds +// the number of defined expectations +func (m *TxManagerMock) MinimockReadCommittedDone() bool { + if m.ReadCommittedMock.optional { + // Optional methods provide '0 or more' call count restriction. + return true + } + + for _, e := range m.ReadCommittedMock.expectations { + if mm_atomic.LoadUint64(&e.Counter) < 1 { + return false + } + } + + return m.ReadCommittedMock.invocationsDone() +} + +// MinimockReadCommittedInspect logs each unmet expectation +func (m *TxManagerMock) MinimockReadCommittedInspect() { + for _, e := range m.ReadCommittedMock.expectations { + if mm_atomic.LoadUint64(&e.Counter) < 1 { + m.t.Errorf("Expected call to TxManagerMock.ReadCommitted at\n%s with params: %#v", e.expectationOrigins.origin, *e.params) + } + } + + afterReadCommittedCounter := mm_atomic.LoadUint64(&m.afterReadCommittedCounter) + // if default expectation was set then invocations count should be greater than zero + if m.ReadCommittedMock.defaultExpectation != nil && afterReadCommittedCounter < 1 { + if m.ReadCommittedMock.defaultExpectation.params == nil { + m.t.Errorf("Expected call to TxManagerMock.ReadCommitted at\n%s", m.ReadCommittedMock.defaultExpectation.returnOrigin) + } else { + m.t.Errorf("Expected call to TxManagerMock.ReadCommitted at\n%s with params: %#v", m.ReadCommittedMock.defaultExpectation.expectationOrigins.origin, *m.ReadCommittedMock.defaultExpectation.params) + } + } + // if func was set then invocations count should be greater than zero + if m.funcReadCommitted != nil && afterReadCommittedCounter < 1 { + m.t.Errorf("Expected call to TxManagerMock.ReadCommitted at\n%s", m.funcReadCommittedOrigin) + } + + if !m.ReadCommittedMock.invocationsDone() && afterReadCommittedCounter > 0 { + m.t.Errorf("Expected %d calls to TxManagerMock.ReadCommitted at\n%s but found %d calls", + mm_atomic.LoadUint64(&m.ReadCommittedMock.expectedInvocations), m.ReadCommittedMock.expectedInvocationsOrigin, afterReadCommittedCounter) + } +} + +// MinimockFinish checks that all mocked methods have been called the expected number of times +func (m *TxManagerMock) MinimockFinish() { + m.finishOnce.Do(func() { + if !m.minimockDone() { + m.MinimockReadCommittedInspect() + } + }) +} + +// MinimockWait waits for all mocked methods to be called the expected number of times +func (m *TxManagerMock) MinimockWait(timeout mm_time.Duration) { + timeoutCh := mm_time.After(timeout) + for { + if m.minimockDone() { + return + } + select { + case <-timeoutCh: + m.MinimockFinish() + return + case <-mm_time.After(10 * mm_time.Millisecond): + } + } +} + +func (m *TxManagerMock) minimockDone() bool { + done := true + return done && + m.MinimockReadCommittedDone() +} diff --git a/pkg/client/db/pg/client.go b/pkg/client/db/pg/client.go new file mode 100644 index 0000000..aa049cd --- /dev/null +++ b/pkg/client/db/pg/client.go @@ -0,0 +1,40 @@ +package pg + +import ( + "context" + + "github.com/jackc/pgx/v4/pgxpool" + "github.com/pkg/errors" + + "github.com/valek177/platform-common/pkg/client/db" +) + +type pgClient struct { + masterDBC db.DB +} + +// New returns new db Client +func New(ctx context.Context, dsn string) (db.Client, error) { + dbc, err := pgxpool.Connect(ctx, dsn) + if err != nil { + return nil, errors.Errorf("failed to connect to db: %v", err) + } + + return &pgClient{ + masterDBC: &pg{dbc: dbc}, + }, nil +} + +// DB returns DB object +func (c *pgClient) DB() db.DB { + return c.masterDBC +} + +// Close closes pgClient connection with DB +func (c *pgClient) Close() error { + if c.masterDBC != nil { + c.masterDBC.Close() + } + + return nil +} diff --git a/pkg/client/db/pg/pg.go b/pkg/client/db/pg/pg.go new file mode 100644 index 0000000..f56e435 --- /dev/null +++ b/pkg/client/db/pg/pg.go @@ -0,0 +1,122 @@ +package pg + +import ( + "context" + "fmt" + "log" + + "github.com/georgysavva/scany/pgxscan" + "github.com/jackc/pgconn" + "github.com/jackc/pgx/v4" + "github.com/jackc/pgx/v4/pgxpool" + + "github.com/valek177/platform-common/pkg/client/db" + "github.com/valek177/platform-common/pkg/client/db/prettier" +) + +type key string + +const ( + // TxKey name + TxKey key = "tx" +) + +type pg struct { + dbc *pgxpool.Pool +} + +// NewDB returns DB object +func NewDB(dbc *pgxpool.Pool) db.DB { + return &pg{ + dbc: dbc, + } +} + +// ScanOneContext executes QueryContext with ScanOne +func (p *pg) ScanOneContext(ctx context.Context, dest interface{}, q db.Query, args ...interface{}) error { + logQuery(ctx, q, args...) + + row, err := p.QueryContext(ctx, q, args...) + if err != nil { + return err + } + + return pgxscan.ScanOne(dest, row) +} + +// ScanAllContext executes QueryContext with ScanAll +func (p *pg) ScanAllContext(ctx context.Context, dest interface{}, q db.Query, args ...interface{}) error { + logQuery(ctx, q, args...) + + rows, err := p.QueryContext(ctx, q, args...) + if err != nil { + return err + } + + return pgxscan.ScanAll(dest, rows) +} + +// ExecContext executes Exec +func (p *pg) ExecContext(ctx context.Context, q db.Query, args ...interface{}) (pgconn.CommandTag, error) { + logQuery(ctx, q, args...) + + tx, ok := ctx.Value(TxKey).(pgx.Tx) + if ok { + return tx.Exec(ctx, q.QueryRaw, args...) + } + + return p.dbc.Exec(ctx, q.QueryRaw, args...) +} + +// QueryContext executes Query +func (p *pg) QueryContext(ctx context.Context, q db.Query, args ...interface{}) (pgx.Rows, error) { + logQuery(ctx, q, args...) + + tx, ok := ctx.Value(TxKey).(pgx.Tx) + if ok { + return tx.Query(ctx, q.QueryRaw, args...) + } + + return p.dbc.Query(ctx, q.QueryRaw, args...) +} + +// QueryRowContext executes QueryRow +func (p *pg) QueryRowContext(ctx context.Context, q db.Query, args ...interface{}) pgx.Row { + logQuery(ctx, q, args...) + + tx, ok := ctx.Value(TxKey).(pgx.Tx) + if ok { + return tx.QueryRow(ctx, q.QueryRaw, args...) + } + + return p.dbc.QueryRow(ctx, q.QueryRaw, args...) +} + +// BeginTx executes pgx BeginTx +func (p *pg) BeginTx(ctx context.Context, txOptions pgx.TxOptions) (pgx.Tx, error) { + return p.dbc.BeginTx(ctx, txOptions) +} + +// Ping executes ping connection +func (p *pg) Ping(ctx context.Context) error { + return p.dbc.Ping(ctx) +} + +// Close closes database client connection +func (p *pg) Close() { + p.dbc.Close() +} + +// MakeContextTx return context with tx settings +func MakeContextTx(ctx context.Context, tx pgx.Tx) context.Context { + return context.WithValue(ctx, TxKey, tx) +} + +func logQuery(ctx context.Context, q db.Query, args ...interface{}) { + prettyQuery := prettier.Pretty(q.QueryRaw, prettier.PlaceholderDollar, args...) + log.Println( + ctx, + fmt.Sprintf("sql: %s", q.Name), + fmt.Sprintf("query: %s", prettyQuery), + ) +} diff --git a/pkg/client/db/prettier/query_prettier.go b/pkg/client/db/prettier/query_prettier.go new file mode 100644 index 0000000..150d2be --- /dev/null +++ b/pkg/client/db/prettier/query_prettier.go @@ -0,0 +1,36 @@ +package prettier + +import ( + "fmt" + "strconv" + "strings" +) + +const ( + // PlaceholderDollar dollar + PlaceholderDollar = "$" + // PlaceholderQuestion question + PlaceholderQuestion = "?" +) + +// Pretty returns string with formatted query +func Pretty(query string, placeholder string, args ...any) string { + for i, param := range args { + var value string + switch v := param.(type) { + case string: + value = fmt.Sprintf("%q", v) + case []byte: + value = fmt.Sprintf("%q", string(v)) + default: + value = fmt.Sprintf("%v", v) + } + + query = strings.Replace(query, fmt.Sprintf("%s%s", placeholder, strconv.Itoa(i+1)), value, -1) + } + + query = strings.ReplaceAll(query, "\t", "") + query = strings.ReplaceAll(query, "\n", " ") + + return strings.TrimSpace(query) +} diff --git a/pkg/client/db/transaction/transaction.go b/pkg/client/db/transaction/transaction.go new file mode 100644 index 0000000..129c2ba --- /dev/null +++ b/pkg/client/db/transaction/transaction.go @@ -0,0 +1,80 @@ +package transaction + +import ( + "context" + + "github.com/jackc/pgx/v4" + "github.com/pkg/errors" + + "github.com/valek177/platform-common/pkg/client/db" + "github.com/valek177/platform-common/pkg/client/db/pg" +) + +type manager struct { + db db.Transactor +} + +// NewTransactionManager создает новый менеджер транзакций, который удовлетворяет интерфейсу db.TxManager +func NewTransactionManager(db db.Transactor) db.TxManager { + return &manager{ + db: db, + } +} + +// transaction основная функция, которая выполняет указанный пользователем обработчик в транзакции +func (m *manager) transaction(ctx context.Context, opts pgx.TxOptions, fn db.Handler) (err error) { + // Если это вложенная транзакция, пропускаем инициацию новой транзакции и выполняем обработчик. + tx, ok := ctx.Value(pg.TxKey).(pgx.Tx) + if ok { + return fn(ctx) + } + + // Стартуем новую транзакцию. + tx, err = m.db.BeginTx(ctx, opts) + if err != nil { + return errors.Wrap(err, "can't begin transaction") + } + + // Кладем транзакцию в контекст. + ctx = pg.MakeContextTx(ctx, tx) + + // Настраиваем функцию отсрочки для отката или коммита транзакции. + defer func() { + // восстанавливаемся после паники + if r := recover(); r != nil { + err = errors.Errorf("panic recovered: %v", r) + } + + // откатываем транзакцию, если произошла ошибка + if err != nil { + if errRollback := tx.Rollback(ctx); errRollback != nil { + err = errors.Wrapf(err, "errRollback: %v", errRollback) + } + + return + } + + // если ошибок не было, коммитим транзакцию + if nil == err { + err = tx.Commit(ctx) + if err != nil { + err = errors.Wrap(err, "tx commit failed") + } + } + }() + + // Выполните код внутри транзакции. + // Если функция терпит неудачу, возвращаем ошибку, и функция отсрочки выполняет откат + // или в противном случае транзакция коммитится. + if err = fn(ctx); err != nil { + err = errors.Wrap(err, "failed executing code inside transaction") + } + + return err +} + +// ReadCommitted executes transaction with parameters +func (m *manager) ReadCommitted(ctx context.Context, f db.Handler) error { + txOpts := pgx.TxOptions{IsoLevel: pgx.ReadCommitted} + return m.transaction(ctx, txOpts, f) +} diff --git a/pkg/closer/closer.go b/pkg/closer/closer.go new file mode 100644 index 0000000..25a1195 --- /dev/null +++ b/pkg/closer/closer.go @@ -0,0 +1,86 @@ +package closer + +import ( + "log" + "os" + "os/signal" + "sync" +) + +var globalCloser = New() + +// Add adds `func() error` callback to the globalCloser +func Add(f ...func() error) { + globalCloser.Add(f...) +} + +// Wait waits global closer before closing +func Wait() { + globalCloser.Wait() +} + +// CloseAll closes all connections +func CloseAll() { + globalCloser.CloseAll() +} + +// Closer is a struct for closing connections +type Closer struct { + mu sync.Mutex + once sync.Once + done chan struct{} + funcs []func() error +} + +// New returns new Closer, if []os.Signal is specified Closer will automatically call CloseAll when one of signals is received from OS +func New(sig ...os.Signal) *Closer { + c := &Closer{done: make(chan struct{})} + if len(sig) > 0 { + go func() { + ch := make(chan os.Signal, 1) + signal.Notify(ch, sig...) + <-ch + signal.Stop(ch) + c.CloseAll() + }() + } + return c +} + +// Add func to closer +func (c *Closer) Add(f ...func() error) { + c.mu.Lock() + c.funcs = append(c.funcs, f...) + c.mu.Unlock() +} + +// Wait blocks until all closer functions are done +func (c *Closer) Wait() { + <-c.done +} + +// CloseAll calls all closer functions +func (c *Closer) CloseAll() { + c.once.Do(func() { + defer close(c.done) + + c.mu.Lock() + funcs := c.funcs + c.funcs = nil + c.mu.Unlock() + + // call all Closer funcs async + errs := make(chan error, len(funcs)) + for _, f := range funcs { + go func(f func() error) { + errs <- f() + }(f) + } + + for i := 0; i < cap(errs); i++ { + if err := <-errs; err != nil { + log.Println("error returned from Closer") + } + } + }) +}