state.sqlserver: Add support for Azure AD auth (+ metadata.yaml) (#2790)
Signed-off-by: ItalyPaleAle <43508+ItalyPaleAle@users.noreply.github.com> Signed-off-by: Alessandro (Ale) Segala <43508+ItalyPaleAle@users.noreply.github.com> Co-authored-by: Deepanshu Agarwal <deepanshu.agarwal1984@gmail.com>
This commit is contained in:
parent
289c784a6a
commit
6b0dedf772
2
go.mod
2
go.mod
|
@ -52,7 +52,6 @@ require (
|
|||
github.com/cyphar/filepath-securejoin v0.2.3
|
||||
github.com/dancannon/gorethink v4.0.0+incompatible
|
||||
github.com/dapr/kit v0.0.5-0.20230401092230-30d122f67bdc
|
||||
github.com/denisenkom/go-mssqldb v0.12.3
|
||||
github.com/didip/tollbooth/v7 v7.0.1
|
||||
github.com/eclipse/paho.mqtt.golang v1.4.2
|
||||
github.com/fasthttp-contrib/sessions v0.0.0-20160905201309-74f6ac73d5d5
|
||||
|
@ -83,6 +82,7 @@ require (
|
|||
github.com/lestrrat-go/jwx/v2 v2.0.9
|
||||
github.com/machinebox/graphql v0.2.2
|
||||
github.com/matoous/go-nanoid/v2 v2.0.0
|
||||
github.com/microsoft/go-mssqldb v0.21.0
|
||||
github.com/mitchellh/mapstructure v1.5.1-0.20220423185008-bf980b35cac4
|
||||
github.com/mrz1836/postmark v1.4.0
|
||||
github.com/nacos-group/nacos-sdk-go/v2 v2.1.3
|
||||
|
|
26
go.sum
26
go.sum
|
@ -418,10 +418,11 @@ github.com/AthenZ/athenz v1.10.39 h1:mtwHTF/v62ewY2Z5KWhuZgVXftBej1/Tn80zx4DcawY
|
|||
github.com/AthenZ/athenz v1.10.39/go.mod h1:3Tg8HLsiQZp81BJY58JBeU2BR6B/H4/0MQGfCwhHNEA=
|
||||
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible h1:fcYLmCpyNYRnvJbPerq7U0hS+6+I79yEDJBqVNcqUzU=
|
||||
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v0.19.0/go.mod h1:h6H6c8enJmmocHUbLiiGY6sx7f9i+X3m1CHdd5c6Rdw=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.0.0/go.mod h1:uGG2W01BaETf0Ozp+QxxKJdMBNRWPdstHG0Fmdwn1/U=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.1.2/go.mod h1:uGG2W01BaETf0Ozp+QxxKJdMBNRWPdstHG0Fmdwn1/U=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.5.0-beta.1 h1:yLM4ZIC+NRvzwFGpXjUbf5FhPBVxJgmYXkjePgNAx64=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.5.0-beta.1/go.mod h1:ON4tFdPTwRcgWEaVDrN3584Ef+b7GgSJaXxe5fW9t4M=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v0.11.0/go.mod h1:HcM1YX14R7CJcghJGOYCgdezslRSVzqwLf/q+4Y2r/0=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.1/go.mod h1:gLa1CL2RNE4s7M3yopJ/p0iq5DdY6Yv5ZUt9MTRZOQM=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0-beta.4 h1:jpSh2461XzXBEw1MJwvVRJwZS0CAgqS0h6jBdoIFtLk=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0-beta.4/go.mod h1:oWa/ZXP08smIi12UyWVbVikBxoZHZCyxijZamTK1i8Q=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/data/azappconfig v0.5.0 h1:OrKZybbyagpgJiREiIVzH5mV/z9oS4rXqdX7i31DSF0=
|
||||
|
@ -430,7 +431,7 @@ github.com/Azure/azure-sdk-for-go/sdk/data/azcosmos v0.3.3 h1:x1shk+tVZ6kLwIQMn4
|
|||
github.com/Azure/azure-sdk-for-go/sdk/data/azcosmos v0.3.3/go.mod h1:Fy3bbChFm4cZn6oIxYYqKB2FG3rBDxk3NZDLDJCHl+Q=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/data/aztables v1.0.1 h1:bFa9IcjvrCber6gGgDAUZ+I2bO8J7s8JxXmu9fhi2ss=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/data/aztables v1.0.1/go.mod h1:l3wvZkG9oW07GLBW5Cd0WwG5asOfJ8aqE8raUvNzLpk=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v0.7.0/go.mod h1:yqy467j36fJxcRV2TzfVZ1pCb5vxm4BtZPUdYWe/Xo8=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.0/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.1.2 h1:+5VZ72z0Qan5Bog5C+ZkgSqUbeVUd9wgtHOrIKuc5b8=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.1.2/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/keyvault/azkeys v0.9.0 h1:TOFrNxfjslms5nLLIMjW7N0+zSALX4KiGsptmpb16AA=
|
||||
|
@ -453,6 +454,7 @@ github.com/Azure/azure-sdk-for-go/sdk/storage/azqueue v0.1.0 h1:TOtQFiO403wClfrZ
|
|||
github.com/Azure/azure-sdk-for-go/sdk/storage/azqueue v0.1.0/go.mod h1:U0IH4deB/maBcagR9SiNeIfgZ1BY/zYCq8SOiQ4vfRc=
|
||||
github.com/Azure/go-amqp v0.18.1 h1:D5Ca+uijuTcj5g76sF+zT4OQZcFFY397+IGf/5Ip5Sc=
|
||||
github.com/Azure/go-amqp v0.18.1/go.mod h1:+bg0x3ce5+Q3ahCEXnCsGG3ETpDQe3MEVnOuT2ywPwc=
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v0.8.1/go.mod h1:4qFor3D/HDsvBME35Xy9rwW9DecL+M2sNw1ybjPtwA0=
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v0.9.0 h1:UE9n9rkJF62ArLb1F3DEjRt8O3jLwMWdSoypKV4f3MU=
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v0.9.0/go.mod h1:kgDmCTgBzIEPFElEF+FK0SdjAor06dRq2Go927dnQ6o=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
|
@ -790,8 +792,6 @@ github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0/go.mod h1:DZGJHZMqrU4JJqFAWUS2U
|
|||
github.com/deepmap/oapi-codegen v1.3.6/go.mod h1:aBozjEveG+33xPiP55Iw/XbVkhtZHEGLq3nxlX0+hfU=
|
||||
github.com/deepmap/oapi-codegen v1.11.0 h1:f/X2NdIkaBKsSdpeuwLnY/vDI0AtPUrmB5LMgc7YD+A=
|
||||
github.com/deepmap/oapi-codegen v1.11.0/go.mod h1:k+ujhoQGxmQYBZBbxhOZNZf4j08qv5mC+OH+fFTnKxM=
|
||||
github.com/denisenkom/go-mssqldb v0.12.3 h1:pBSGx9Tq67pBOTLmxNuirNTeB8Vjmf886Kx+8Y+8shw=
|
||||
github.com/denisenkom/go-mssqldb v0.12.3/go.mod h1:k0mtMFOnU+AihqFxPMiF05rtiDrorD1Vrm1KEz5hxDo=
|
||||
github.com/dgraph-io/badger/v3 v3.2103.5 h1:ylPa6qzbjYRQMU6jokoj4wzcaweHylt//CH0AKt0akg=
|
||||
github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
|
@ -804,6 +804,7 @@ github.com/didip/tollbooth/v7 v7.0.1 h1:TkT4sBKoQoHQFPf7blQ54iHrZiTDnr8TceU+MulV
|
|||
github.com/didip/tollbooth/v7 v7.0.1/go.mod h1:VZhDSGl5bDSPj4wPsih3PFa4Uh9Ghv8hgacaTm5PRT4=
|
||||
github.com/dimfeld/httptreemux v5.0.1+incompatible h1:Qj3gVcDNoOthBAqftuD596rm4wg/adLLz5xh5CmpiCA=
|
||||
github.com/dimfeld/httptreemux v5.0.1+incompatible/go.mod h1:rbUlSV+CCpv/SuqUTP/8Bk2O3LyUV436/yaRGkhP6Z0=
|
||||
github.com/dnaeon/go-vcr v1.1.0/go.mod h1:M7tiix8f0r6mKKJ3Yq/kqU1OYf3MnfmBWVbPx/yU9ko=
|
||||
github.com/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI=
|
||||
github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ=
|
||||
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
|
||||
|
@ -1011,6 +1012,7 @@ github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptG
|
|||
github.com/golang-jwt/jwt v3.2.1+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
|
||||
github.com/golang-jwt/jwt/v4 v4.4.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
||||
github.com/golang-jwt/jwt/v4 v4.4.3 h1:Hxl6lhQFj4AnOX6MLrsCb/+7tCj7DxP7VA+2rDIq5AU=
|
||||
github.com/golang-jwt/jwt/v4 v4.4.3/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
||||
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY=
|
||||
|
@ -1319,10 +1321,12 @@ github.com/jcmturner/aescts/v2 v2.0.0 h1:9YKLH6ey7H4eDBXW8khjYslgyqG2xZikXP0EQFK
|
|||
github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs=
|
||||
github.com/jcmturner/dnsutils/v2 v2.0.0 h1:lltnkeZGL0wILNvrNiVCR6Ro5PGU/SeBvVO/8c/iPbo=
|
||||
github.com/jcmturner/dnsutils/v2 v2.0.0/go.mod h1:b0TnjGOvI/n42bZa+hmXL+kFJZsFT7G4t3HTlQ184QM=
|
||||
github.com/jcmturner/gofork v1.0.0/go.mod h1:MK8+TM0La+2rjBD4jE12Kj1pCCxK7d2LK/UM3ncEo0o=
|
||||
github.com/jcmturner/gofork v1.7.6 h1:QH0l3hzAU1tfT3rZCnW5zXl+orbkNMMRGJfdJjHVETg=
|
||||
github.com/jcmturner/gofork v1.7.6/go.mod h1:1622LH6i/EZqLloHfE7IeZ0uEJwMSUyQ/nDd82IeqRo=
|
||||
github.com/jcmturner/goidentity/v6 v6.0.1 h1:VKnZd2oEIMorCTsFBnJWbExfNN7yZr3EhJAxwOkZg6o=
|
||||
github.com/jcmturner/goidentity/v6 v6.0.1/go.mod h1:X1YW3bgtvwAXju7V3LCIMpY0Gbxyjn/mY9zx4tFonSg=
|
||||
github.com/jcmturner/gokrb5/v8 v8.4.2/go.mod h1:sb+Xq/fTY5yktf/VxLsE3wlfPqQjp0aWNYyvBVK62bc=
|
||||
github.com/jcmturner/gokrb5/v8 v8.4.3 h1:iTonLeSJOn7MVUtyMT+arAn5AKAPrkilzhGw8wE/Tq8=
|
||||
github.com/jcmturner/gokrb5/v8 v8.4.3/go.mod h1:dqRwJGXznQrzw6cWmyo6kH+E7jksEQG/CyVWsJEsJO0=
|
||||
github.com/jcmturner/rpc/v2 v2.0.3 h1:7FXXj8Ti1IaVFpSAziCZWNzbNuZmnvw/i6CqLNdWfZY=
|
||||
|
@ -1502,6 +1506,8 @@ github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zk
|
|||
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
|
||||
github.com/microcosm-cc/bluemonday v1.0.21 h1:dNH3e4PSyE4vNX+KlRGHT5KrSvjeUkoNPwEORjffHJg=
|
||||
github.com/microcosm-cc/bluemonday v1.0.21/go.mod h1:ytNkv4RrDrLJ2pqlsSI46O6IVXmZOBBD4SaJyDwwTkM=
|
||||
github.com/microsoft/go-mssqldb v0.21.0 h1:p2rpHIL7TlSv1QrbXJUAcbyRKnIT0C9rRkH2E4OjLn8=
|
||||
github.com/microsoft/go-mssqldb v0.21.0/go.mod h1:+4wZTUnz/SV6nffv+RRRB/ss8jPng5Sho2SmM1l2ts4=
|
||||
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||
github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
|
||||
github.com/miekg/dns v1.1.27/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
|
||||
|
@ -1662,7 +1668,7 @@ github.com/pierrec/lz4 v2.6.0+incompatible h1:Ix9yFKn1nSPBLFl/yZknTp8TU5G4Ps0JDm
|
|||
github.com/pierrec/lz4 v2.6.0+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
|
||||
github.com/pierrec/lz4/v4 v4.1.17 h1:kV4Ip+/hUBC+8T6+2EgburRtkE9ef4nbY3f4dFhGjMc=
|
||||
github.com/pierrec/lz4/v4 v4.1.17/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||
github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA=
|
||||
github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4/go.mod h1:N6UoU20jOqggOuDwUaBQpluzLNDqif3kq9z2wpdYEfQ=
|
||||
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU=
|
||||
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
|
@ -2093,7 +2099,7 @@ golang.org/x/crypto v0.0.0-20191219195013-becbf705a915/go.mod h1:LzIPMQfyMNhhGPh
|
|||
golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201112155050-0c6587e931a9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
|
@ -2103,6 +2109,7 @@ 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-20220427172511-eb4f295cb31f/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20220511200225-c6db032c6c88/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20220513210258-46612604a0f9/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
|
@ -2211,6 +2218,7 @@ golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/
|
|||
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201016165138-7b1cca2348c0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
|
@ -2226,7 +2234,6 @@ golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy
|
|||
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
||||
golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
|
@ -2418,6 +2425,7 @@ golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||
golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220204135822-1c1b9b1eba6a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220224120231-95c6836cb0e7/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
|
@ -2888,6 +2896,8 @@ gopkg.in/kataras/go-serializer.v0 v0.0.4 h1:mVy3gjU4zZZBe+8JbZDRTMPJdrB0lzBNsLLR
|
|||
gopkg.in/kataras/go-serializer.v0 v0.0.4/go.mod h1:v2jHg/3Wp7uncDNzenTsX75PRDxhzlxoo/qDvM4ZGxk=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
|
||||
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce h1:+JknDZhAj8YMt7GC73Ei8pv4MzjDUNPHgQWJdtMAaDU=
|
||||
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c=
|
||||
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
||||
gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
|
||||
gopkg.in/square/go-jose.v2 v2.4.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
|
||||
|
|
|
@ -151,10 +151,7 @@ func (s EnvironmentSettings) GetTokenCredential() (azcore.TokenCredential, error
|
|||
if file, ok := os.LookupEnv(azureFederatedTokenFile); ok {
|
||||
if _, ok := os.LookupEnv(azureAuthorityHost); ok {
|
||||
if tenantID, ok := os.LookupEnv(azureTenantID); ok {
|
||||
workloadCred, err := azidentity.NewWorkloadIdentityCredential(tenantID, clientID, file, &azidentity.WorkloadIdentityCredentialOptions{
|
||||
ClientOptions: policy.ClientOptions{},
|
||||
},
|
||||
)
|
||||
workloadCred, err := azidentity.NewWorkloadIdentityCredential(tenantID, clientID, file, nil)
|
||||
if err == nil {
|
||||
creds = append(creds, workloadCred)
|
||||
} else {
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
Copyright 2021 The Dapr Authors
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package azure
|
||||
|
||||
import (
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/cloud"
|
||||
)
|
||||
|
||||
const (
|
||||
// Service configuration for Azure SQL. Namespaced with dapr.io
|
||||
ServiceAzureSQL cloud.ServiceName = "dapr.io/azuresql"
|
||||
)
|
||||
|
||||
func init() {
|
||||
// Register additional services in the SDK's clouds
|
||||
cloud.AzureChina.Services[ServiceAzureSQL] = cloud.ServiceConfiguration{
|
||||
Audience: "https://database.chinacloudapi.cn",
|
||||
}
|
||||
cloud.AzureGovernment.Services[ServiceAzureSQL] = cloud.ServiceConfiguration{
|
||||
Audience: "https://database.usgovcloudapi.net",
|
||||
}
|
||||
cloud.AzurePublic.Services[ServiceAzureSQL] = cloud.ServiceConfiguration{
|
||||
Audience: "https://database.windows.net",
|
||||
}
|
||||
}
|
|
@ -0,0 +1,298 @@
|
|||
/*
|
||||
Copyright 2023 The Dapr Authors
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package sqlserver
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
"unicode"
|
||||
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
|
||||
mssql "github.com/microsoft/go-mssqldb"
|
||||
"github.com/microsoft/go-mssqldb/msdsn"
|
||||
|
||||
"github.com/dapr/components-contrib/internal/authentication/azure"
|
||||
"github.com/dapr/components-contrib/metadata"
|
||||
"github.com/dapr/kit/ptr"
|
||||
)
|
||||
|
||||
const (
|
||||
connectionStringKey = "connectionString"
|
||||
tableNameKey = "tableName"
|
||||
metadataTableNameKey = "metadataTableName"
|
||||
schemaKey = "schema"
|
||||
keyTypeKey = "keyType"
|
||||
keyLengthKey = "keyLength"
|
||||
indexedPropertiesKey = "indexedProperties"
|
||||
keyColumnName = "Key"
|
||||
rowVersionColumnName = "RowVersion"
|
||||
databaseNameKey = "databaseName"
|
||||
cleanupIntervalKey = "cleanupIntervalInSeconds"
|
||||
|
||||
defaultKeyLength = 200
|
||||
defaultSchema = "dbo"
|
||||
defaultDatabase = "dapr"
|
||||
defaultTable = "state"
|
||||
defaultMetaTable = "dapr_metadata"
|
||||
defaultCleanupInterval = time.Hour
|
||||
)
|
||||
|
||||
type sqlServerMetadata struct {
|
||||
ConnectionString string
|
||||
DatabaseName string
|
||||
TableName string
|
||||
MetadataTableName string
|
||||
Schema string
|
||||
KeyType string
|
||||
KeyLength int
|
||||
IndexedProperties string
|
||||
CleanupInterval *time.Duration `mapstructure:"cleanupIntervalInSeconds"`
|
||||
UseAzureAD bool `mapstructure:"useAzureAD"`
|
||||
|
||||
// Internal properties
|
||||
keyTypeParsed KeyType
|
||||
keyLengthParsed int
|
||||
indexedPropertiesParsed []IndexedProperty
|
||||
azureEnv azure.EnvironmentSettings
|
||||
}
|
||||
|
||||
func newMetadata() sqlServerMetadata {
|
||||
return sqlServerMetadata{
|
||||
TableName: defaultTable,
|
||||
Schema: defaultSchema,
|
||||
DatabaseName: defaultDatabase,
|
||||
KeyLength: defaultKeyLength,
|
||||
MetadataTableName: defaultMetaTable,
|
||||
CleanupInterval: ptr.Of(defaultCleanupInterval),
|
||||
}
|
||||
}
|
||||
|
||||
func (m *sqlServerMetadata) Parse(meta map[string]string) error {
|
||||
err := metadata.DecodeMetadata(meta, &m)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if m.ConnectionString == "" {
|
||||
return errors.New("missing connection string")
|
||||
}
|
||||
|
||||
if !isValidSQLName(m.TableName) {
|
||||
return fmt.Errorf("invalid table name, accepted characters are (A-Z, a-z, 0-9, _)")
|
||||
}
|
||||
|
||||
if !isValidSQLName(m.MetadataTableName) {
|
||||
return fmt.Errorf("invalid metadata table name, accepted characters are (A-Z, a-z, 0-9, _)")
|
||||
}
|
||||
|
||||
if !isValidSQLName(m.DatabaseName) {
|
||||
return fmt.Errorf("invalid database name, accepted characters are (A-Z, a-z, 0-9, _)")
|
||||
}
|
||||
|
||||
err = m.setKeyType()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !isValidSQLName(m.Schema) {
|
||||
return fmt.Errorf("invalid schema name, accepted characters are (A-Z, a-z, 0-9, _)")
|
||||
}
|
||||
|
||||
err = m.setIndexedProperties()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Cleanup interval
|
||||
if m.CleanupInterval != nil {
|
||||
// Non-positive value from meta means disable auto cleanup.
|
||||
if *m.CleanupInterval <= 0 {
|
||||
if meta[cleanupIntervalKey] == "" {
|
||||
// Unfortunately the mapstructure decoder decodes an empty string to 0, a missing key would be nil however
|
||||
m.CleanupInterval = ptr.Of(defaultCleanupInterval)
|
||||
} else {
|
||||
m.CleanupInterval = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If using Azure AD
|
||||
if m.UseAzureAD {
|
||||
m.azureEnv, err = azure.NewEnvironmentSettings(meta)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetConnector returns the connector from the connection string or Azure AD.
|
||||
// The returned connector can be used with sql.OpenDB.
|
||||
func (m *sqlServerMetadata) GetConnector(setDatabase bool) (*mssql.Connector, bool, error) {
|
||||
// Parse the connection string
|
||||
config, err := msdsn.Parse(m.ConnectionString)
|
||||
if err != nil {
|
||||
return nil, false, fmt.Errorf("failed to parse connection string: %w", err)
|
||||
}
|
||||
|
||||
// If setDatabase is true and the configuration (i.e. the connection string) does not contain a database, add it
|
||||
// This is for backwards-compatibility reasons
|
||||
if setDatabase && config.Database == "" {
|
||||
config.Database = m.DatabaseName
|
||||
}
|
||||
|
||||
// We need to check if the configuration has a database because the migrator needs it
|
||||
hasDatabase := config.Database != ""
|
||||
|
||||
// Configure Azure AD authentication if needed
|
||||
if m.UseAzureAD {
|
||||
tokenCred, errToken := m.azureEnv.GetTokenCredential()
|
||||
if errToken != nil {
|
||||
return nil, false, errToken
|
||||
}
|
||||
|
||||
conn, err := mssql.NewSecurityTokenConnector(config, func(ctx context.Context) (string, error) {
|
||||
at, err := tokenCred.GetToken(ctx, policy.TokenRequestOptions{
|
||||
Scopes: []string{
|
||||
m.azureEnv.Cloud.Services[azure.ServiceAzureSQL].Audience,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return at.Token, nil
|
||||
})
|
||||
return conn, hasDatabase, err
|
||||
}
|
||||
|
||||
conn := mssql.NewConnectorConfig(config)
|
||||
return conn, hasDatabase, nil
|
||||
}
|
||||
|
||||
// Validates and returns the key type.
|
||||
func (m *sqlServerMetadata) setKeyType() error {
|
||||
if m.KeyType != "" {
|
||||
kt, err := KeyTypeFromString(m.KeyType)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
m.keyTypeParsed = kt
|
||||
} else {
|
||||
m.keyTypeParsed = StringKeyType
|
||||
}
|
||||
|
||||
if m.keyTypeParsed != StringKeyType {
|
||||
return nil
|
||||
}
|
||||
|
||||
if m.KeyLength <= 0 {
|
||||
return fmt.Errorf("invalid key length value of %d", m.KeyLength)
|
||||
} else {
|
||||
m.keyLengthParsed = m.KeyLength
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Sets the validated index properties.
|
||||
func (m *sqlServerMetadata) setIndexedProperties() error {
|
||||
if m.IndexedProperties == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
var indexedProperties []IndexedProperty
|
||||
err := json.Unmarshal([]byte(m.IndexedProperties), &indexedProperties)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = m.validateIndexedProperties(indexedProperties)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
m.indexedPropertiesParsed = indexedProperties
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validates that all the mandator index properties are supplied and that the
|
||||
// values are valid.
|
||||
func (m *sqlServerMetadata) validateIndexedProperties(indexedProperties []IndexedProperty) error {
|
||||
for _, p := range indexedProperties {
|
||||
if p.ColumnName == "" {
|
||||
return errors.New("indexed property column cannot be empty")
|
||||
}
|
||||
|
||||
if p.Property == "" {
|
||||
return errors.New("indexed property name cannot be empty")
|
||||
}
|
||||
|
||||
if p.Type == "" {
|
||||
return errors.New("indexed property type cannot be empty")
|
||||
}
|
||||
|
||||
if !isValidSQLName(p.ColumnName) {
|
||||
return fmt.Errorf("invalid indexed property column name, accepted characters are (A-Z, a-z, 0-9, _)")
|
||||
}
|
||||
|
||||
if !isValidIndexedPropertyName(p.Property) {
|
||||
return fmt.Errorf("invalid indexed property name, accepted characters are (A-Z, a-z, 0-9, _, ., [, ])")
|
||||
}
|
||||
|
||||
if !isValidIndexedPropertyType(p.Type) {
|
||||
return fmt.Errorf("invalid indexed property type, accepted characters are (A-Z, a-z, 0-9, _, (, ))")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func isLetterOrNumber(c rune) bool {
|
||||
return unicode.IsNumber(c) || unicode.IsLetter(c)
|
||||
}
|
||||
|
||||
func isValidSQLName(s string) bool {
|
||||
for _, c := range s {
|
||||
if !(isLetterOrNumber(c) || (c == '_')) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func isValidIndexedPropertyName(s string) bool {
|
||||
for _, c := range s {
|
||||
if !(isLetterOrNumber(c) || (c == '_') || (c == '.') || (c == '[') || (c == ']')) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func isValidIndexedPropertyType(s string) bool {
|
||||
for _, c := range s {
|
||||
if !(isLetterOrNumber(c) || (c == '(') || (c == ')')) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
|
@ -0,0 +1,101 @@
|
|||
# yaml-language-server: $schema=../../../component-metadata-schema.json
|
||||
schemaVersion: "v1"
|
||||
type: "state"
|
||||
name: "sqlserver"
|
||||
version: "v1"
|
||||
status: "stable"
|
||||
title: "SQL Server"
|
||||
urls:
|
||||
- title: "Reference"
|
||||
url: "https://docs.dapr.io/reference/components-reference/supported-state-stores/setup-sqlserver/"
|
||||
capabilities:
|
||||
# If actorStateStore is present, the metadata key actorStateStore can be used
|
||||
- "actorStateStore"
|
||||
- "crud"
|
||||
- "transactional"
|
||||
- "etag"
|
||||
authenticationProfiles:
|
||||
- title: "Connection string"
|
||||
description: |
|
||||
Authenticates using a connection string.
|
||||
metadata:
|
||||
- name: connectionString
|
||||
required: true
|
||||
sensitive: true
|
||||
description: |
|
||||
The connection string used to connect.
|
||||
If the connection string contains the database, it must already exist. Otherwise, if the database is omitted, a default database named "Dapr" is created.
|
||||
example: |
|
||||
"Server=myServerName\myInstanceName;Database=myDataBase;User Id=myUsername;Password=myPassword;"
|
||||
builtinAuthenticationProfiles:
|
||||
- name: "azuread"
|
||||
metadata:
|
||||
- name: useAzureAD
|
||||
required: true
|
||||
type: bool
|
||||
description: |
|
||||
Must be set to `true` to enable the component to retrieve access tokens from Azure AD.
|
||||
This authentication method only works with Azure SQL databases.
|
||||
- name: connectionString
|
||||
required: true
|
||||
sensitive: true
|
||||
description: |
|
||||
The connection string or URL of the Azure SQL database, without credentials.
|
||||
If the connection string contains the database, it must already exist. Otherwise, if the database is omitted, a default database named "Dapr" is created.
|
||||
example: |
|
||||
"sqlserver://myServerName.database.windows.net:1433?database=myDataBase"
|
||||
metadata:
|
||||
- name: tableName
|
||||
description: |
|
||||
The name of the table to use. Alpha-numeric with underscores.
|
||||
example: |
|
||||
"table_name"
|
||||
default: |
|
||||
"state"
|
||||
- name: metadataTableName
|
||||
description: |
|
||||
Name of the table Dapr uses to store metadata properties.
|
||||
example: |
|
||||
"dapr_metadata"
|
||||
default: |
|
||||
"dapr_metadata"
|
||||
- name: schema
|
||||
description: |
|
||||
The schema to use.
|
||||
example: |
|
||||
"dapr"
|
||||
default: |
|
||||
"dbo"
|
||||
- name: keyType
|
||||
description: |
|
||||
The type of key used
|
||||
allowedValues:
|
||||
- "string"
|
||||
- "uuid"
|
||||
- "integer"
|
||||
default: |
|
||||
"string"
|
||||
example: |
|
||||
"string"
|
||||
- name: keyLength
|
||||
type: number
|
||||
description: |
|
||||
The max length of key. Ignored if "keyType" is not `string`.
|
||||
example: |
|
||||
200
|
||||
default: |
|
||||
200
|
||||
- name: indexedProperties
|
||||
description: |
|
||||
List of indexed properties, as a string containing a JSON document.
|
||||
example: |
|
||||
'[{"column": "transactionid", "property": "id", "type": "int"}, {"column": "customerid", "property": "customer", "type": "nvarchar(100)"}]'
|
||||
- name: cleanupIntervalInSeconds
|
||||
type: number
|
||||
description: |
|
||||
Interval, in seconds, to clean up rows with an expired TTL. Default: 3600 (i.e. 1 hour).
|
||||
Setting this to values <=0 disables the periodic cleanup.
|
||||
default: |
|
||||
"3600"
|
||||
example: |
|
||||
"1800", "-1"
|
|
@ -17,7 +17,6 @@ import (
|
|||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
type migrator interface {
|
||||
|
@ -25,7 +24,7 @@ type migrator interface {
|
|||
}
|
||||
|
||||
type migration struct {
|
||||
store *SQLServer
|
||||
metadata *sqlServerMetadata
|
||||
}
|
||||
|
||||
type migrationResult struct {
|
||||
|
@ -40,29 +39,29 @@ type migrationResult struct {
|
|||
deleteWithoutETagCommand string
|
||||
}
|
||||
|
||||
func newMigration(store *SQLServer) migrator {
|
||||
func newMigration(metadata *sqlServerMetadata) migrator {
|
||||
return &migration{
|
||||
store: store,
|
||||
metadata: metadata,
|
||||
}
|
||||
}
|
||||
|
||||
func (m *migration) newMigrationResult() migrationResult {
|
||||
r := migrationResult{
|
||||
bulkDeleteProcName: fmt.Sprintf("sp_BulkDelete_%s", m.store.tableName),
|
||||
itemRefTableTypeName: fmt.Sprintf("[%s].%s_Table", m.store.schema, m.store.tableName),
|
||||
upsertProcName: fmt.Sprintf("sp_Upsert_v3_%s", m.store.tableName),
|
||||
getCommand: fmt.Sprintf("SELECT [Data], [RowVersion] FROM [%s].[%s] WHERE [Key] = @Key AND ([ExpireDate] IS NULL OR [ExpireDate] > GETDATE())", m.store.schema, m.store.tableName),
|
||||
deleteWithETagCommand: fmt.Sprintf(`DELETE [%s].[%s] WHERE [Key]=@Key AND [RowVersion]=@RowVersion`, m.store.schema, m.store.tableName),
|
||||
deleteWithoutETagCommand: fmt.Sprintf(`DELETE [%s].[%s] WHERE [Key]=@Key`, m.store.schema, m.store.tableName),
|
||||
bulkDeleteProcName: fmt.Sprintf("sp_BulkDelete_%s", m.metadata.TableName),
|
||||
itemRefTableTypeName: fmt.Sprintf("[%s].%s_Table", m.metadata.Schema, m.metadata.TableName),
|
||||
upsertProcName: fmt.Sprintf("sp_Upsert_v3_%s", m.metadata.TableName),
|
||||
getCommand: fmt.Sprintf("SELECT [Data], [RowVersion] FROM [%s].[%s] WHERE [Key] = @Key AND ([ExpireDate] IS NULL OR [ExpireDate] > GETDATE())", m.metadata.Schema, m.metadata.TableName),
|
||||
deleteWithETagCommand: fmt.Sprintf(`DELETE [%s].[%s] WHERE [Key]=@Key AND [RowVersion]=@RowVersion`, m.metadata.Schema, m.metadata.TableName),
|
||||
deleteWithoutETagCommand: fmt.Sprintf(`DELETE [%s].[%s] WHERE [Key]=@Key`, m.metadata.Schema, m.metadata.TableName),
|
||||
}
|
||||
|
||||
r.bulkDeleteProcFullName = fmt.Sprintf("[%s].%s", m.store.schema, r.bulkDeleteProcName)
|
||||
r.upsertProcFullName = fmt.Sprintf("[%s].%s", m.store.schema, r.upsertProcName)
|
||||
r.bulkDeleteProcFullName = fmt.Sprintf("[%s].%s", m.metadata.Schema, r.bulkDeleteProcName)
|
||||
r.upsertProcFullName = fmt.Sprintf("[%s].%s", m.metadata.Schema, r.upsertProcName)
|
||||
|
||||
//nolint:exhaustive
|
||||
switch m.store.keyType {
|
||||
switch m.metadata.keyTypeParsed {
|
||||
case StringKeyType:
|
||||
r.pkColumnType = fmt.Sprintf("NVARCHAR(%d)", m.store.keyLength)
|
||||
r.pkColumnType = fmt.Sprintf("NVARCHAR(%d)", m.metadata.keyLengthParsed)
|
||||
|
||||
case UUIDKeyType:
|
||||
r.pkColumnType = "uniqueidentifier"
|
||||
|
@ -78,15 +77,16 @@ func (m *migration) newMigrationResult() migrationResult {
|
|||
func (m *migration) executeMigrations(ctx context.Context) (migrationResult, error) {
|
||||
r := m.newMigrationResult()
|
||||
|
||||
db, err := sql.Open("sqlserver", m.store.connectionString)
|
||||
conn, hasDatabase, err := m.metadata.GetConnector(false)
|
||||
if err != nil {
|
||||
return r, err
|
||||
}
|
||||
db := sql.OpenDB(conn)
|
||||
|
||||
// If the user provides a database in the connection string to not attempt
|
||||
// If the user provides a database in the connection string do not attempt
|
||||
// to create the database. This work as the component did before adding the
|
||||
// support to create the db.
|
||||
if connStringContainsDatabase(m.store.connectionString) {
|
||||
if hasDatabase {
|
||||
// Schedule close of connection
|
||||
defer db.Close()
|
||||
} else {
|
||||
|
@ -98,12 +98,12 @@ func (m *migration) executeMigrations(ctx context.Context) (migrationResult, err
|
|||
// Close the existing connection
|
||||
db.Close()
|
||||
|
||||
// Re connect with a database specific connection
|
||||
m.store.connectionString = fmt.Sprintf("%s;database=%s;", m.store.connectionString, m.store.databaseName)
|
||||
db, err = sql.Open("sqlserver", m.store.connectionString)
|
||||
// Re connect with a database-specific connection
|
||||
conn, _, err = m.metadata.GetConnector(true)
|
||||
if err != nil {
|
||||
return r, err
|
||||
}
|
||||
db = sql.OpenDB(conn)
|
||||
|
||||
// Schedule close of new connection
|
||||
defer db.Close()
|
||||
|
@ -124,7 +124,7 @@ func (m *migration) executeMigrations(ctx context.Context) (migrationResult, err
|
|||
return r, fmt.Errorf("failed to create stored procedures: %w", err)
|
||||
}
|
||||
|
||||
for _, ix := range m.store.indexedProperties {
|
||||
for _, ix := range m.metadata.indexedPropertiesParsed {
|
||||
err = m.ensureIndexedPropertyExists(ctx, db, ix)
|
||||
if err != nil {
|
||||
return r, err
|
||||
|
@ -134,12 +134,6 @@ func (m *migration) executeMigrations(ctx context.Context) (migrationResult, err
|
|||
return r, nil
|
||||
}
|
||||
|
||||
func connStringContainsDatabase(connStr string) bool {
|
||||
// This method is only going to be called once (or at least once per component), so we are not pre-compiling the regex to avoid keeping that as a global variable
|
||||
return regexp.MustCompile(`(?i)(^|;)database=.+`).
|
||||
MatchString(connStr)
|
||||
}
|
||||
|
||||
func runCommand(ctx context.Context, db *sql.DB, tsql string) error {
|
||||
if _, err := db.ExecContext(ctx, tsql); err != nil {
|
||||
return err
|
||||
|
@ -158,12 +152,12 @@ func (m *migration) ensureIndexedPropertyExists(ctx context.Context, db *sql.DB,
|
|||
WHERE object_id = OBJECT_ID('[%s].%s')
|
||||
AND name='%s'))
|
||||
CREATE INDEX %s ON [%s].[%s]([%s])`,
|
||||
m.store.schema,
|
||||
m.store.tableName,
|
||||
m.metadata.Schema,
|
||||
m.metadata.TableName,
|
||||
indexName,
|
||||
indexName,
|
||||
m.store.schema,
|
||||
m.store.tableName,
|
||||
m.metadata.Schema,
|
||||
m.metadata.TableName,
|
||||
ix.ColumnName)
|
||||
|
||||
return runCommand(ctx, db, tsql)
|
||||
|
@ -174,7 +168,7 @@ func (m *migration) ensureDatabaseExists(ctx context.Context, db *sql.DB) error
|
|||
tsql := fmt.Sprintf(`
|
||||
IF NOT EXISTS (SELECT * FROM sys.databases WHERE name = N'%s')
|
||||
CREATE DATABASE [%s]`,
|
||||
m.store.databaseName, m.store.databaseName)
|
||||
m.metadata.DatabaseName, m.metadata.DatabaseName)
|
||||
|
||||
return runCommand(ctx, db, tsql)
|
||||
}
|
||||
|
@ -184,7 +178,7 @@ func (m *migration) ensureSchemaExists(ctx context.Context, db *sql.DB) error {
|
|||
tsql := fmt.Sprintf(`
|
||||
IF NOT EXISTS(SELECT * FROM sys.schemas WHERE name = N'%s')
|
||||
EXEC('CREATE SCHEMA [%s]')`,
|
||||
m.store.schema, m.store.schema)
|
||||
m.metadata.Schema, m.metadata.Schema)
|
||||
|
||||
return runCommand(ctx, db, tsql)
|
||||
}
|
||||
|
@ -199,15 +193,13 @@ func (m *migration) ensureTableExists(ctx context.Context, db *sql.DB, r migrati
|
|||
[InsertDate] DateTime2 NOT NULL DEFAULT(GETDATE()),
|
||||
[UpdateDate] DateTime2 NULL,
|
||||
[ExpireDate] DateTime2 NULL,`,
|
||||
m.store.schema, m.store.tableName, m.store.schema, m.store.tableName, r.pkColumnType, m.store.tableName)
|
||||
m.metadata.Schema, m.metadata.TableName, m.metadata.Schema, m.metadata.TableName, r.pkColumnType, m.metadata.TableName)
|
||||
|
||||
if m.store.indexedProperties != nil {
|
||||
for _, prop := range m.store.indexedProperties {
|
||||
if prop.Type != "" {
|
||||
tsql += fmt.Sprintf("\n [%s] AS CONVERT(%s, JSON_VALUE(Data, '$.%s')) PERSISTED,", prop.ColumnName, prop.Type, prop.Property)
|
||||
} else {
|
||||
tsql += fmt.Sprintf("\n [%s] AS JSON_VALUE(Data, '$.%s') PERSISTED,", prop.ColumnName, prop.Property)
|
||||
}
|
||||
for _, prop := range m.metadata.indexedPropertiesParsed {
|
||||
if prop.Type != "" {
|
||||
tsql += fmt.Sprintf("\n [%s] AS CONVERT(%s, JSON_VALUE(Data, '$.%s')) PERSISTED,", prop.ColumnName, prop.Type, prop.Property)
|
||||
} else {
|
||||
tsql += fmt.Sprintf("\n [%s] AS JSON_VALUE(Data, '$.%s') PERSISTED,", prop.ColumnName, prop.Property)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -224,7 +216,7 @@ func (m *migration) ensureTableExists(ctx context.Context, db *sql.DB, r migrati
|
|||
FROM INFORMATION_SCHEMA.COLUMNS
|
||||
WHERE TABLE_SCHEMA = '%[1]s' AND TABLE_NAME = '%[2]s'
|
||||
AND COLUMN_NAME = 'ExpireDate')
|
||||
ALTER TABLE [%[1]s].[%[2]s] ADD [ExpireDate] DateTime2 NULL`, m.store.schema, m.store.tableName)
|
||||
ALTER TABLE [%[1]s].[%[2]s] ADD [ExpireDate] DateTime2 NULL`, m.metadata.Schema, m.metadata.TableName)
|
||||
if err := runCommand(ctx, db, tsql); err != nil {
|
||||
return fmt.Errorf("failed to ensure ExpireDate column: %w", err)
|
||||
}
|
||||
|
@ -234,7 +226,7 @@ func (m *migration) ensureTableExists(ctx context.Context, db *sql.DB, r migrati
|
|||
CREATE TABLE [%[1]s].[%[2]s] (
|
||||
[Key] %[3]s CONSTRAINT PK_%[4]s PRIMARY KEY,
|
||||
[Value] NVARCHAR(MAX) NOT NULL
|
||||
)`, m.store.schema, m.store.metaTableName, r.pkColumnType, m.store.metaTableName)
|
||||
)`, m.metadata.Schema, m.metadata.MetadataTableName, r.pkColumnType, m.metadata.MetadataTableName)
|
||||
if err := runCommand(ctx, db, tsql); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -251,7 +243,7 @@ func (m *migration) ensureTypeExists(ctx context.Context, db *sql.DB, mr migrati
|
|||
[Key] %s NOT NULL,
|
||||
[RowVersion] BINARY(8)
|
||||
)
|
||||
`, m.store.schema, m.store.tableName, m.store.schema, m.store.tableName, mr.pkColumnType)
|
||||
`, m.metadata.Schema, m.metadata.TableName, m.metadata.Schema, m.metadata.TableName, mr.pkColumnType)
|
||||
|
||||
return runCommand(ctx, db, tsql)
|
||||
}
|
||||
|
@ -267,10 +259,10 @@ func (m *migration) ensureBulkDeleteStoredProcedureExists(ctx context.Context, d
|
|||
JOIN @itemsToDelete i ON i.[Key] = x.[Key] AND (i.[RowVersion] IS NULL OR i.[RowVersion] = x.[RowVersion])`,
|
||||
mr.bulkDeleteProcFullName,
|
||||
mr.itemRefTableTypeName,
|
||||
m.store.schema,
|
||||
m.store.tableName,
|
||||
m.store.schema,
|
||||
m.store.tableName)
|
||||
m.metadata.Schema,
|
||||
m.metadata.TableName,
|
||||
m.metadata.Schema,
|
||||
m.metadata.TableName)
|
||||
|
||||
return m.createStoredProcedureIfNotExists(ctx, db, mr.bulkDeleteProcName, tsql)
|
||||
}
|
||||
|
@ -301,7 +293,7 @@ func (m *migration) createStoredProcedureIfNotExists(ctx context.Context, db *sq
|
|||
BEGIN
|
||||
execute ('%s')
|
||||
END`,
|
||||
m.store.schema,
|
||||
m.metadata.Schema,
|
||||
name,
|
||||
escapedDefinition)
|
||||
|
||||
|
@ -385,7 +377,7 @@ func (m *migration) ensureUpsertStoredProcedureExists(ctx context.Context, db *s
|
|||
`,
|
||||
mr.upsertProcFullName,
|
||||
mr.pkColumnType,
|
||||
m.store.tableName,
|
||||
m.metadata.TableName,
|
||||
)
|
||||
|
||||
return m.createStoredProcedureIfNotExists(ctx, db, mr.upsertProcName, tsql)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2021 The Dapr Authors
|
||||
Copyright 2023 The Dapr Authors
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
@ -20,14 +20,11 @@ import (
|
|||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
"unicode"
|
||||
|
||||
mssql "github.com/denisenkom/go-mssqldb"
|
||||
mssql "github.com/microsoft/go-mssqldb"
|
||||
|
||||
internalsql "github.com/dapr/components-contrib/internal/component/sql"
|
||||
"github.com/dapr/components-contrib/metadata"
|
||||
"github.com/dapr/components-contrib/state"
|
||||
"github.com/dapr/components-contrib/state/utils"
|
||||
"github.com/dapr/kit/logger"
|
||||
|
@ -65,27 +62,6 @@ const (
|
|||
InvalidKeyType KeyType = "invalid"
|
||||
)
|
||||
|
||||
const (
|
||||
connectionStringKey = "connectionString"
|
||||
tableNameKey = "tableName"
|
||||
metadataTableNameKey = "metadataTableName"
|
||||
schemaKey = "schema"
|
||||
keyTypeKey = "keyType"
|
||||
keyLengthKey = "keyLength"
|
||||
indexedPropertiesKey = "indexedProperties"
|
||||
keyColumnName = "Key"
|
||||
rowVersionColumnName = "RowVersion"
|
||||
databaseNameKey = "databaseName"
|
||||
cleanupIntervalKey = "cleanupIntervalInSeconds"
|
||||
|
||||
defaultKeyLength = 200
|
||||
defaultSchema = "dbo"
|
||||
defaultDatabase = "dapr"
|
||||
defaultTable = "state"
|
||||
defaultMetaTable = "dapr_metadata"
|
||||
defaultCleanupInterval = time.Hour
|
||||
)
|
||||
|
||||
// New creates a new instance of a SQL Server transaction store.
|
||||
func New(logger logger.Logger) state.Store {
|
||||
s := &SQLServer{
|
||||
|
@ -108,17 +84,9 @@ type IndexedProperty struct {
|
|||
type SQLServer struct {
|
||||
state.BulkStore
|
||||
|
||||
connectionString string
|
||||
databaseName string
|
||||
tableName string
|
||||
metaTableName string
|
||||
schema string
|
||||
keyType KeyType
|
||||
keyLength int
|
||||
indexedProperties []IndexedProperty
|
||||
migratorFactory func(*SQLServer) migrator
|
||||
metadata sqlServerMetadata
|
||||
|
||||
cleanupInterval *time.Duration
|
||||
migratorFactory func(*sqlServerMetadata) migrator
|
||||
|
||||
bulkDeleteCommand string
|
||||
itemRefTableTypeName string
|
||||
|
@ -133,59 +101,15 @@ type SQLServer struct {
|
|||
gc internalsql.GarbageCollector
|
||||
}
|
||||
|
||||
type sqlServerMetadata struct {
|
||||
ConnectionString string
|
||||
DatabaseName string
|
||||
TableName string
|
||||
MetadataTableName string
|
||||
Schema string
|
||||
KeyType string
|
||||
KeyLength int
|
||||
IndexedProperties string
|
||||
}
|
||||
|
||||
func isLetterOrNumber(c rune) bool {
|
||||
return unicode.IsNumber(c) || unicode.IsLetter(c)
|
||||
}
|
||||
|
||||
func isValidSQLName(s string) bool {
|
||||
for _, c := range s {
|
||||
if !(isLetterOrNumber(c) || (c == '_')) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func isValidIndexedPropertyName(s string) bool {
|
||||
for _, c := range s {
|
||||
if !(isLetterOrNumber(c) || (c == '_') || (c == '.') || (c == '[') || (c == ']')) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func isValidIndexedPropertyType(s string) bool {
|
||||
for _, c := range s {
|
||||
if !(isLetterOrNumber(c) || (c == '(') || (c == ')')) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// Init initializes the SQL server state store.
|
||||
func (s *SQLServer) Init(ctx context.Context, metadata state.Metadata) error {
|
||||
err := s.parseMetadata(metadata.Properties)
|
||||
s.metadata = newMetadata()
|
||||
err := s.metadata.Parse(metadata.Properties)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
migration := s.migratorFactory(s)
|
||||
migration := s.migratorFactory(&s.metadata)
|
||||
mr, err := migration.executeMigrations(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -198,216 +122,45 @@ func (s *SQLServer) Init(ctx context.Context, metadata state.Metadata) error {
|
|||
s.deleteWithETagCommand = mr.deleteWithETagCommand
|
||||
s.deleteWithoutETagCommand = mr.deleteWithoutETagCommand
|
||||
|
||||
s.db, err = sql.Open("sqlserver", s.connectionString)
|
||||
conn, _, err := s.metadata.GetConnector(true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.db = sql.OpenDB(conn)
|
||||
|
||||
if s.cleanupInterval != nil {
|
||||
gc, err := internalsql.ScheduleGarbageCollector(internalsql.GCOptions{
|
||||
Logger: s.logger,
|
||||
UpdateLastCleanupQuery: fmt.Sprintf(`BEGIN TRANSACTION;
|
||||
if s.metadata.CleanupInterval != nil {
|
||||
err = s.startGC()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SQLServer) startGC() error {
|
||||
gc, err := internalsql.ScheduleGarbageCollector(internalsql.GCOptions{
|
||||
Logger: s.logger,
|
||||
UpdateLastCleanupQuery: fmt.Sprintf(`BEGIN TRANSACTION;
|
||||
BEGIN TRY
|
||||
INSERT INTO [%[1]s].[%[2]s] ([Key], [Value]) VALUES ('last-cleanup', CONVERT(nvarchar(MAX), GETDATE(), 21));
|
||||
INSERT INTO [%[1]s].[%[2]s] ([Key], [Value]) VALUES ('last-cleanup', CONVERT(nvarchar(MAX), GETDATE(), 21));
|
||||
END TRY
|
||||
BEGIN CATCH
|
||||
UPDATE [%[1]s].[%[2]s] SET [Value] = CONVERT(nvarchar(MAX), GETDATE(), 21) WHERE [Key] = 'last-cleanup' AND Datediff_big(MS, [Value], GETUTCDATE()) > @Interval
|
||||
UPDATE [%[1]s].[%[2]s] SET [Value] = CONVERT(nvarchar(MAX), GETDATE(), 21) WHERE [Key] = 'last-cleanup' AND Datediff_big(MS, [Value], GETUTCDATE()) > @Interval
|
||||
END CATCH
|
||||
COMMIT TRANSACTION;`, s.schema, s.metaTableName),
|
||||
UpdateLastCleanupQueryParameterName: "Interval",
|
||||
DeleteExpiredValuesQuery: fmt.Sprintf(
|
||||
`DELETE FROM [%s].[%s] WHERE [ExpireDate] IS NOT NULL AND [ExpireDate] < GETDATE()`,
|
||||
s.schema, s.tableName,
|
||||
),
|
||||
CleanupInterval: *s.cleanupInterval,
|
||||
DBSql: s.db,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.gc = gc
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SQLServer) parseMetadata(meta map[string]string) error {
|
||||
m := sqlServerMetadata{
|
||||
TableName: defaultTable,
|
||||
Schema: defaultSchema,
|
||||
DatabaseName: defaultDatabase,
|
||||
KeyLength: defaultKeyLength,
|
||||
MetadataTableName: defaultMetaTable,
|
||||
}
|
||||
err := metadata.DecodeMetadata(meta, &m)
|
||||
COMMIT TRANSACTION;`, s.metadata.Schema, s.metadata.MetadataTableName),
|
||||
UpdateLastCleanupQueryParameterName: "Interval",
|
||||
DeleteExpiredValuesQuery: fmt.Sprintf(
|
||||
`DELETE FROM [%s].[%s] WHERE [ExpireDate] IS NOT NULL AND [ExpireDate] < GETDATE()`,
|
||||
s.metadata.Schema, s.metadata.TableName,
|
||||
),
|
||||
CleanupInterval: *s.metadata.CleanupInterval,
|
||||
DBSql: s.db,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if m.ConnectionString == "" {
|
||||
return fmt.Errorf("missing connection string")
|
||||
}
|
||||
s.connectionString = m.ConnectionString
|
||||
|
||||
if err := s.setTable(m.TableName); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := s.setMetadataTable(m.MetadataTableName); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := s.setDatabase(m.DatabaseName); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := s.setKeyType(m.KeyType, m.KeyLength); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := s.setSchema(m.Schema); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := s.setIndexedProperties(m.IndexedProperties); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Cleanup interval
|
||||
if v := meta[cleanupIntervalKey]; v != "" {
|
||||
cleanupIntervalInSec, err := strconv.ParseInt(v, 10, 0)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid value for '%s': %s", cleanupIntervalKey, v)
|
||||
}
|
||||
|
||||
// Non-positive value from meta means disable auto cleanup.
|
||||
if cleanupIntervalInSec > 0 {
|
||||
s.cleanupInterval = ptr.Of(time.Duration(cleanupIntervalInSec) * time.Second)
|
||||
} else {
|
||||
s.cleanupInterval = nil
|
||||
}
|
||||
} else {
|
||||
s.cleanupInterval = ptr.Of(defaultCleanupInterval)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Returns validated index properties.
|
||||
func (s *SQLServer) setIndexedProperties(indexedPropertiesString string) error {
|
||||
if indexedPropertiesString != "" {
|
||||
var indexedProperties []IndexedProperty
|
||||
err := json.Unmarshal([]byte(indexedPropertiesString), &indexedProperties)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = s.validateIndexedProperties(indexedProperties)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.indexedProperties = indexedProperties
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validates that all the mandator index properties are supplied and that the
|
||||
// values are valid.
|
||||
func (s *SQLServer) validateIndexedProperties(indexedProperties []IndexedProperty) error {
|
||||
for _, p := range indexedProperties {
|
||||
if p.ColumnName == "" {
|
||||
return errors.New("indexed property column cannot be empty")
|
||||
}
|
||||
|
||||
if p.Property == "" {
|
||||
return errors.New("indexed property name cannot be empty")
|
||||
}
|
||||
|
||||
if p.Type == "" {
|
||||
return errors.New("indexed property type cannot be empty")
|
||||
}
|
||||
|
||||
if !isValidSQLName(p.ColumnName) {
|
||||
return fmt.Errorf("invalid indexed property column name, accepted characters are (A-Z, a-z, 0-9, _)")
|
||||
}
|
||||
|
||||
if !isValidIndexedPropertyName(p.Property) {
|
||||
return fmt.Errorf("invalid indexed property name, accepted characters are (A-Z, a-z, 0-9, _, ., [, ])")
|
||||
}
|
||||
|
||||
if !isValidIndexedPropertyType(p.Type) {
|
||||
return fmt.Errorf("invalid indexed property type, accepted characters are (A-Z, a-z, 0-9, _, (, ))")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validates and returns the key type.
|
||||
func (s *SQLServer) setKeyType(keyType string, keyLength int) error {
|
||||
if keyType != "" {
|
||||
kt, err := KeyTypeFromString(keyType)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.keyType = kt
|
||||
} else {
|
||||
s.keyType = StringKeyType
|
||||
}
|
||||
|
||||
if s.keyType != StringKeyType {
|
||||
return nil
|
||||
}
|
||||
|
||||
if keyLength <= 0 {
|
||||
return fmt.Errorf("invalid key length value of %d", keyLength)
|
||||
} else {
|
||||
s.keyLength = keyLength
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Returns the schema name if set or the default value otherwise.
|
||||
func (s *SQLServer) setSchema(schemaName string) error {
|
||||
if !isValidSQLName(schemaName) {
|
||||
return fmt.Errorf("invalid schema name, accepted characters are (A-Z, a-z, 0-9, _)")
|
||||
}
|
||||
s.schema = schemaName
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Returns the database name if set or the default value otherwise.
|
||||
func (s *SQLServer) setDatabase(databaseName string) error {
|
||||
if !isValidSQLName(databaseName) {
|
||||
return fmt.Errorf("invalid database name, accepted characters are (A-Z, a-z, 0-9, _)")
|
||||
}
|
||||
|
||||
s.databaseName = databaseName
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Returns the table name if set or the default value otherwise.
|
||||
func (s *SQLServer) setTable(tableName string) error {
|
||||
if !isValidSQLName(tableName) {
|
||||
return fmt.Errorf("invalid table name, accepted characters are (A-Z, a-z, 0-9, _)")
|
||||
}
|
||||
|
||||
s.tableName = tableName
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SQLServer) setMetadataTable(tableName string) error {
|
||||
if !isValidSQLName(tableName) {
|
||||
return fmt.Errorf("invalid metadata table name, accepted characters are (A-Z, a-z, 0-9, _)")
|
||||
}
|
||||
|
||||
s.metaTableName = tableName
|
||||
s.gc = gc
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -679,7 +432,7 @@ func (s *SQLServer) Close() error {
|
|||
// GetCleanupInterval returns the cleanupInterval property.
|
||||
// This is primarily used for tests.
|
||||
func (s *SQLServer) GetCleanupInterval() *time.Duration {
|
||||
return s.cleanupInterval
|
||||
return s.metadata.CleanupInterval
|
||||
}
|
||||
|
||||
func (s *SQLServer) CleanupExpired() error {
|
||||
|
|
|
@ -128,21 +128,21 @@ func getTestStoreWithKeyType(t *testing.T, kt KeyType, indexedProperties string)
|
|||
logger: logger.NewLogger("test"),
|
||||
}
|
||||
err := store.Init(context.Background(), metadata)
|
||||
assert.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
return store
|
||||
}
|
||||
|
||||
func assertUserExists(t *testing.T, store *SQLServer, key string) (user, string) {
|
||||
getRes, err := store.Get(context.Background(), &state.GetRequest{Key: key})
|
||||
assert.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.NotNil(t, getRes)
|
||||
assert.NotNil(t, getRes.Data, "No data was returned")
|
||||
require.NotNil(t, getRes.ETag)
|
||||
|
||||
var loaded user
|
||||
err = json.Unmarshal(getRes.Data, &loaded)
|
||||
assert.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
return loaded, *getRes.ETag
|
||||
}
|
||||
|
@ -158,16 +158,16 @@ func assertLoadedUserIsEqual(t *testing.T, store *SQLServer, key string, expecte
|
|||
|
||||
func assertUserDoesNotExist(t *testing.T, store *SQLServer, key string) {
|
||||
_, err := store.Get(context.Background(), &state.GetRequest{Key: key})
|
||||
assert.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func assertDBQuery(t *testing.T, store *SQLServer, query string, assertReader func(t *testing.T, rows *sql.Rows)) {
|
||||
db, err := sql.Open("sqlserver", store.connectionString)
|
||||
assert.Nil(t, err)
|
||||
db, err := sql.Open("sqlserver", store.metadata.ConnectionString)
|
||||
require.NoError(t, err)
|
||||
defer db.Close()
|
||||
|
||||
rows, err := db.Query(query)
|
||||
assert.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Nil(t, rows.Err())
|
||||
|
||||
defer rows.Close()
|
||||
|
@ -176,12 +176,12 @@ func assertDBQuery(t *testing.T, store *SQLServer, query string, assertReader fu
|
|||
|
||||
/* #nosec. */
|
||||
func assertUserCountIsEqualTo(t *testing.T, store *SQLServer, expected int) {
|
||||
tsql := fmt.Sprintf("SELECT count(*) FROM [%s].[%s]", store.schema, store.tableName)
|
||||
tsql := fmt.Sprintf("SELECT count(*) FROM [%s].[%s]", store.metadata.Schema, store.metadata.TableName)
|
||||
assertDBQuery(t, store, tsql, func(t *testing.T, rows *sql.Rows) {
|
||||
assert.True(t, rows.Next())
|
||||
var actual int
|
||||
err := rows.Scan(&actual)
|
||||
assert.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, expected, actual)
|
||||
})
|
||||
}
|
||||
|
@ -229,14 +229,14 @@ func testSingleOperations(t *testing.T) {
|
|||
|
||||
// Save and read
|
||||
err := store.Set(context.Background(), &state.SetRequest{Key: john.ID, Value: john})
|
||||
assert.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
johnV1, etagFromInsert := assertLoadedUserIsEqual(t, store, john.ID, john)
|
||||
|
||||
// Update with ETAG
|
||||
waterJohn := johnV1
|
||||
waterJohn.FavoriteBeverage = "Water"
|
||||
err = store.Set(context.Background(), &state.SetRequest{Key: waterJohn.ID, Value: waterJohn, ETag: &etagFromInsert})
|
||||
assert.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Get updated
|
||||
johnV2, _ := assertLoadedUserIsEqual(t, store, waterJohn.ID, waterJohn)
|
||||
|
@ -245,7 +245,7 @@ func testSingleOperations(t *testing.T) {
|
|||
noEtagJohn := johnV2
|
||||
noEtagJohn.FavoriteBeverage = "No Etag John"
|
||||
err = store.Set(context.Background(), &state.SetRequest{Key: noEtagJohn.ID, Value: noEtagJohn})
|
||||
assert.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
// 7. Get updated
|
||||
johnV3, _ := assertLoadedUserIsEqual(t, store, noEtagJohn.ID, noEtagJohn)
|
||||
|
@ -264,7 +264,7 @@ func testSingleOperations(t *testing.T) {
|
|||
|
||||
// 10. Delete with valid ETAG
|
||||
err = store.Delete(context.Background(), &state.DeleteRequest{Key: johnV2.ID, ETag: &etag})
|
||||
assert.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
assertUserDoesNotExist(t, store, johnV2.ID)
|
||||
})
|
||||
|
@ -292,10 +292,10 @@ func testIndexedProperties(t *testing.T) {
|
|||
{Key: "4", Value: userWithPets{user{"4", "Maria", "Wine"}, 100}},
|
||||
})
|
||||
|
||||
assert.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Check the database for computed columns
|
||||
assertDBQuery(t, store, fmt.Sprintf("SELECT count(*) from [%s].[%s] WHERE PetsCount < 3", store.schema, usersTableName), func(t *testing.T, rows *sql.Rows) {
|
||||
assertDBQuery(t, store, fmt.Sprintf("SELECT count(*) from [%s].[%s] WHERE PetsCount < 3", store.metadata.Schema, usersTableName), func(t *testing.T, rows *sql.Rows) {
|
||||
assert.True(t, rows.Next())
|
||||
|
||||
var c int
|
||||
|
@ -304,7 +304,7 @@ func testIndexedProperties(t *testing.T) {
|
|||
})
|
||||
|
||||
// Ensure we can get by beverage
|
||||
assertDBQuery(t, store, fmt.Sprintf("SELECT count(*) from [%s].[%s] WHERE FavoriteBeverage = '%s'", store.schema, usersTableName, "Coffee"), func(t *testing.T, rows *sql.Rows) {
|
||||
assertDBQuery(t, store, fmt.Sprintf("SELECT count(*) from [%s].[%s] WHERE FavoriteBeverage = '%s'", store.metadata.Schema, usersTableName, "Coffee"), func(t *testing.T, rows *sql.Rows) {
|
||||
assert.True(t, rows.Next())
|
||||
|
||||
var c int
|
||||
|
@ -348,7 +348,7 @@ func testMultiOperations(t *testing.T) {
|
|||
}
|
||||
|
||||
err := store.BulkSet(context.Background(), bulkSet)
|
||||
assert.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
assertUserCountIsEqualTo(t, store, len(initialUsers))
|
||||
|
||||
// Ensure initial users are correctly stored
|
||||
|
@ -397,7 +397,7 @@ func testMultiOperations(t *testing.T) {
|
|||
state.SetRequest{Key: toInsert.ID, Value: toInsert},
|
||||
},
|
||||
})
|
||||
assert.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
assertLoadedUserIsEqual(t, store, modified.ID, modified)
|
||||
assertLoadedUserIsEqual(t, store, toInsert.ID, toInsert)
|
||||
assertUserDoesNotExist(t, store, toDelete.ID)
|
||||
|
@ -420,7 +420,7 @@ func testMultiOperations(t *testing.T) {
|
|||
state.SetRequest{Key: modified.ID, Value: modified, ETag: &toModify.etag},
|
||||
},
|
||||
})
|
||||
assert.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
assertLoadedUserIsEqual(t, store, modified.ID, modified)
|
||||
assertUserDoesNotExist(t, store, toDelete.ID)
|
||||
|
||||
|
@ -525,7 +525,7 @@ func testBulkSet(t *testing.T) {
|
|||
}
|
||||
|
||||
err := store.BulkSet(context.Background(), sets)
|
||||
assert.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
totalUsers = len(sets)
|
||||
assertUserCountIsEqualTo(t, store, totalUsers)
|
||||
})
|
||||
|
@ -540,7 +540,7 @@ func testBulkSet(t *testing.T) {
|
|||
{Key: modified.ID, Value: modified, ETag: &toModifyETag},
|
||||
{Key: toInsert.ID, Value: toInsert},
|
||||
})
|
||||
assert.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
assertLoadedUserIsEqual(t, store, modified.ID, modified)
|
||||
assertLoadedUserIsEqual(t, store, toInsert.ID, toInsert)
|
||||
totalUsers++
|
||||
|
@ -559,7 +559,7 @@ func testBulkSet(t *testing.T) {
|
|||
{Key: modified.ID, Value: modified},
|
||||
{Key: toInsert.ID, Value: toInsert},
|
||||
})
|
||||
assert.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
assertLoadedUserIsEqual(t, store, modified.ID, modified)
|
||||
assertLoadedUserIsEqual(t, store, toInsert.ID, toInsert)
|
||||
totalUsers++
|
||||
|
@ -626,7 +626,7 @@ func testBulkDelete(t *testing.T) {
|
|||
sets[i] = state.SetRequest{Key: u.ID, Value: u}
|
||||
}
|
||||
err := store.BulkSet(context.Background(), sets)
|
||||
assert.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
totalUsers := len(initialUsers)
|
||||
assertUserCountIsEqualTo(t, store, totalUsers)
|
||||
|
||||
|
@ -639,7 +639,7 @@ func testBulkDelete(t *testing.T) {
|
|||
{Key: deleted1},
|
||||
{Key: deleted2},
|
||||
})
|
||||
assert.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
totalUsers -= 2
|
||||
assertUserCountIsEqualTo(t, store, totalUsers)
|
||||
assertUserDoesNotExist(t, store, deleted1)
|
||||
|
@ -656,7 +656,7 @@ func testBulkDelete(t *testing.T) {
|
|||
{Key: deleted1.ID, ETag: &deleted1Etag},
|
||||
{Key: deleted2.ID, ETag: &deleted2Etag},
|
||||
})
|
||||
assert.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
totalUsers -= 2
|
||||
assertUserCountIsEqualTo(t, store, totalUsers)
|
||||
assertUserDoesNotExist(t, store, deleted1.ID)
|
||||
|
@ -673,7 +673,7 @@ func testBulkDelete(t *testing.T) {
|
|||
{Key: deleted1.ID, ETag: &deleted1Etag},
|
||||
{Key: deleted2.ID},
|
||||
})
|
||||
assert.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
totalUsers -= 2
|
||||
assertUserCountIsEqualTo(t, store, totalUsers)
|
||||
assertUserDoesNotExist(t, store, deleted1.ID)
|
||||
|
@ -708,10 +708,10 @@ func testInsertAndUpdateSetRecordDates(t *testing.T) {
|
|||
|
||||
u := user{"1", "John", "Coffee"}
|
||||
err := store.Set(context.Background(), &state.SetRequest{Key: u.ID, Value: u})
|
||||
assert.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
var originalInsertTime time.Time
|
||||
getUserTsql := fmt.Sprintf("SELECT [InsertDate], [UpdateDate] from [%s].[%s] WHERE [Key]='%s'", store.schema, store.tableName, u.ID)
|
||||
getUserTsql := fmt.Sprintf("SELECT [InsertDate], [UpdateDate] from [%s].[%s] WHERE [Key]='%s'", store.metadata.Schema, store.metadata.TableName, u.ID)
|
||||
assertDBQuery(t, store, getUserTsql, func(t *testing.T, rows *sql.Rows) {
|
||||
assert.True(t, rows.Next())
|
||||
|
||||
|
@ -730,13 +730,13 @@ func testInsertAndUpdateSetRecordDates(t *testing.T) {
|
|||
modified := u
|
||||
modified.FavoriteBeverage = beverageTea
|
||||
err = store.Set(context.Background(), &state.SetRequest{Key: modified.ID, Value: modified})
|
||||
assert.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
assertDBQuery(t, store, getUserTsql, func(t *testing.T, rows *sql.Rows) {
|
||||
assert.True(t, rows.Next())
|
||||
|
||||
var insertDate, updateDate sql.NullTime
|
||||
err := rows.Scan(&insertDate, &updateDate)
|
||||
assert.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.True(t, insertDate.Valid)
|
||||
assert.Equal(t, originalInsertTime, insertDate.Time)
|
||||
|
@ -754,7 +754,7 @@ func testConcurrentSets(t *testing.T) {
|
|||
|
||||
u := user{"1", "John", "Coffee"}
|
||||
err := store.Set(context.Background(), &state.SetRequest{Key: u.ID, Value: u})
|
||||
assert.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, etag := assertLoadedUserIsEqual(t, store, u.ID, u)
|
||||
|
||||
|
@ -805,7 +805,7 @@ func testMultipleInitializations(t *testing.T) {
|
|||
store2 := &SQLServer{
|
||||
logger: logger.NewLogger("test"),
|
||||
}
|
||||
err := store2.Init(context.Background(), createMetadata(store.schema, test.kt, test.indexedProperties))
|
||||
err := store2.Init(context.Background(), createMetadata(store.metadata.Schema, test.kt, test.indexedProperties))
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/dapr/components-contrib/metadata"
|
||||
"github.com/dapr/components-contrib/state"
|
||||
|
@ -55,140 +56,162 @@ func TestValidConfiguration(t *testing.T) {
|
|||
"No schema": {
|
||||
props: map[string]string{connectionStringKey: sampleConnectionString, tableNameKey: sampleUserTableName},
|
||||
expected: SQLServer{
|
||||
connectionString: sampleConnectionString,
|
||||
tableName: sampleUserTableName,
|
||||
schema: defaultSchema,
|
||||
keyType: StringKeyType,
|
||||
keyLength: defaultKeyLength,
|
||||
databaseName: defaultDatabase,
|
||||
metaTableName: defaultMetaTable,
|
||||
metadata: sqlServerMetadata{
|
||||
ConnectionString: sampleConnectionString,
|
||||
TableName: sampleUserTableName,
|
||||
Schema: defaultSchema,
|
||||
keyTypeParsed: StringKeyType,
|
||||
keyLengthParsed: defaultKeyLength,
|
||||
DatabaseName: defaultDatabase,
|
||||
MetadataTableName: defaultMetaTable,
|
||||
},
|
||||
},
|
||||
},
|
||||
"Custom schema": {
|
||||
props: map[string]string{connectionStringKey: sampleConnectionString, tableNameKey: sampleUserTableName, schemaKey: "mytest"},
|
||||
expected: SQLServer{
|
||||
connectionString: sampleConnectionString,
|
||||
tableName: sampleUserTableName,
|
||||
schema: "mytest",
|
||||
keyType: StringKeyType,
|
||||
keyLength: defaultKeyLength,
|
||||
databaseName: defaultDatabase,
|
||||
metaTableName: defaultMetaTable,
|
||||
metadata: sqlServerMetadata{
|
||||
ConnectionString: sampleConnectionString,
|
||||
TableName: sampleUserTableName,
|
||||
Schema: "mytest",
|
||||
keyTypeParsed: StringKeyType,
|
||||
keyLengthParsed: defaultKeyLength,
|
||||
DatabaseName: defaultDatabase,
|
||||
MetadataTableName: defaultMetaTable,
|
||||
},
|
||||
},
|
||||
},
|
||||
"String key type": {
|
||||
props: map[string]string{connectionStringKey: sampleConnectionString, tableNameKey: sampleUserTableName, keyTypeKey: "string"},
|
||||
expected: SQLServer{
|
||||
connectionString: sampleConnectionString,
|
||||
schema: defaultSchema,
|
||||
tableName: sampleUserTableName,
|
||||
keyType: StringKeyType,
|
||||
keyLength: defaultKeyLength,
|
||||
databaseName: defaultDatabase,
|
||||
metaTableName: defaultMetaTable,
|
||||
metadata: sqlServerMetadata{
|
||||
ConnectionString: sampleConnectionString,
|
||||
Schema: defaultSchema,
|
||||
TableName: sampleUserTableName,
|
||||
keyTypeParsed: StringKeyType,
|
||||
keyLengthParsed: defaultKeyLength,
|
||||
DatabaseName: defaultDatabase,
|
||||
MetadataTableName: defaultMetaTable,
|
||||
},
|
||||
},
|
||||
},
|
||||
"Unique identifier key type": {
|
||||
props: map[string]string{connectionStringKey: sampleConnectionString, tableNameKey: sampleUserTableName, keyTypeKey: "uuid"},
|
||||
expected: SQLServer{
|
||||
connectionString: sampleConnectionString,
|
||||
schema: defaultSchema,
|
||||
tableName: sampleUserTableName,
|
||||
keyType: UUIDKeyType,
|
||||
keyLength: 0,
|
||||
databaseName: defaultDatabase,
|
||||
metaTableName: defaultMetaTable,
|
||||
metadata: sqlServerMetadata{
|
||||
ConnectionString: sampleConnectionString,
|
||||
Schema: defaultSchema,
|
||||
TableName: sampleUserTableName,
|
||||
keyTypeParsed: UUIDKeyType,
|
||||
keyLengthParsed: 0,
|
||||
DatabaseName: defaultDatabase,
|
||||
MetadataTableName: defaultMetaTable,
|
||||
},
|
||||
},
|
||||
},
|
||||
"Integer identifier key type": {
|
||||
props: map[string]string{connectionStringKey: sampleConnectionString, tableNameKey: sampleUserTableName, keyTypeKey: "integer"},
|
||||
expected: SQLServer{
|
||||
connectionString: sampleConnectionString,
|
||||
schema: defaultSchema,
|
||||
tableName: sampleUserTableName,
|
||||
keyType: IntegerKeyType,
|
||||
keyLength: 0,
|
||||
databaseName: defaultDatabase,
|
||||
metaTableName: defaultMetaTable,
|
||||
metadata: sqlServerMetadata{
|
||||
ConnectionString: sampleConnectionString,
|
||||
Schema: defaultSchema,
|
||||
TableName: sampleUserTableName,
|
||||
keyTypeParsed: IntegerKeyType,
|
||||
keyLengthParsed: 0,
|
||||
DatabaseName: defaultDatabase,
|
||||
MetadataTableName: defaultMetaTable,
|
||||
},
|
||||
},
|
||||
},
|
||||
"Custom key length": {
|
||||
props: map[string]string{connectionStringKey: sampleConnectionString, tableNameKey: sampleUserTableName, keyLengthKey: "100"},
|
||||
expected: SQLServer{
|
||||
connectionString: sampleConnectionString,
|
||||
schema: defaultSchema,
|
||||
tableName: sampleUserTableName,
|
||||
keyType: StringKeyType,
|
||||
keyLength: 100,
|
||||
databaseName: defaultDatabase,
|
||||
metaTableName: defaultMetaTable,
|
||||
metadata: sqlServerMetadata{
|
||||
ConnectionString: sampleConnectionString,
|
||||
Schema: defaultSchema,
|
||||
TableName: sampleUserTableName,
|
||||
keyTypeParsed: StringKeyType,
|
||||
keyLengthParsed: 100,
|
||||
DatabaseName: defaultDatabase,
|
||||
MetadataTableName: defaultMetaTable,
|
||||
},
|
||||
},
|
||||
},
|
||||
"Single indexed property": {
|
||||
props: map[string]string{connectionStringKey: sampleConnectionString, tableNameKey: sampleUserTableName, indexedPropertiesKey: `[{"column": "Age","property":"age", "type":"int"}]`},
|
||||
expected: SQLServer{
|
||||
connectionString: sampleConnectionString,
|
||||
schema: defaultSchema,
|
||||
tableName: sampleUserTableName,
|
||||
keyType: StringKeyType,
|
||||
keyLength: defaultKeyLength,
|
||||
indexedProperties: []IndexedProperty{
|
||||
{ColumnName: "Age", Property: "age", Type: "int"},
|
||||
metadata: sqlServerMetadata{
|
||||
ConnectionString: sampleConnectionString,
|
||||
Schema: defaultSchema,
|
||||
TableName: sampleUserTableName,
|
||||
keyTypeParsed: StringKeyType,
|
||||
keyLengthParsed: defaultKeyLength,
|
||||
indexedPropertiesParsed: []IndexedProperty{
|
||||
{ColumnName: "Age", Property: "age", Type: "int"},
|
||||
},
|
||||
DatabaseName: defaultDatabase,
|
||||
MetadataTableName: defaultMetaTable,
|
||||
},
|
||||
databaseName: defaultDatabase,
|
||||
metaTableName: defaultMetaTable,
|
||||
},
|
||||
},
|
||||
"Multiple indexed properties": {
|
||||
props: map[string]string{connectionStringKey: sampleConnectionString, tableNameKey: sampleUserTableName, indexedPropertiesKey: `[{"column": "Age","property":"age", "type":"int"}, {"column": "Name","property":"name", "type":"nvarchar(100)"}]`},
|
||||
expected: SQLServer{
|
||||
connectionString: sampleConnectionString,
|
||||
schema: defaultSchema,
|
||||
tableName: sampleUserTableName,
|
||||
keyType: StringKeyType,
|
||||
keyLength: defaultKeyLength,
|
||||
indexedProperties: []IndexedProperty{
|
||||
{ColumnName: "Age", Property: "age", Type: "int"},
|
||||
{ColumnName: "Name", Property: "name", Type: "nvarchar(100)"},
|
||||
metadata: sqlServerMetadata{
|
||||
ConnectionString: sampleConnectionString,
|
||||
Schema: defaultSchema,
|
||||
TableName: sampleUserTableName,
|
||||
keyTypeParsed: StringKeyType,
|
||||
keyLengthParsed: defaultKeyLength,
|
||||
indexedPropertiesParsed: []IndexedProperty{
|
||||
{ColumnName: "Age", Property: "age", Type: "int"},
|
||||
{ColumnName: "Name", Property: "name", Type: "nvarchar(100)"},
|
||||
},
|
||||
DatabaseName: defaultDatabase,
|
||||
MetadataTableName: defaultMetaTable,
|
||||
},
|
||||
databaseName: defaultDatabase,
|
||||
metaTableName: defaultMetaTable,
|
||||
},
|
||||
},
|
||||
"Custom database": {
|
||||
props: map[string]string{connectionStringKey: sampleConnectionString, tableNameKey: sampleUserTableName, databaseNameKey: "dapr_test_table"},
|
||||
expected: SQLServer{
|
||||
connectionString: sampleConnectionString,
|
||||
schema: defaultSchema,
|
||||
tableName: sampleUserTableName,
|
||||
keyType: StringKeyType,
|
||||
keyLength: defaultKeyLength,
|
||||
databaseName: "dapr_test_table",
|
||||
metaTableName: defaultMetaTable,
|
||||
metadata: sqlServerMetadata{
|
||||
ConnectionString: sampleConnectionString,
|
||||
Schema: defaultSchema,
|
||||
TableName: sampleUserTableName,
|
||||
keyTypeParsed: StringKeyType,
|
||||
keyLengthParsed: defaultKeyLength,
|
||||
DatabaseName: "dapr_test_table",
|
||||
MetadataTableName: defaultMetaTable,
|
||||
},
|
||||
},
|
||||
},
|
||||
"No table": {
|
||||
props: map[string]string{connectionStringKey: sampleConnectionString},
|
||||
expected: SQLServer{
|
||||
connectionString: sampleConnectionString,
|
||||
tableName: defaultTable,
|
||||
schema: defaultSchema,
|
||||
keyType: StringKeyType,
|
||||
keyLength: defaultKeyLength,
|
||||
databaseName: defaultDatabase,
|
||||
metaTableName: defaultMetaTable,
|
||||
metadata: sqlServerMetadata{
|
||||
ConnectionString: sampleConnectionString,
|
||||
TableName: defaultTable,
|
||||
Schema: defaultSchema,
|
||||
keyTypeParsed: StringKeyType,
|
||||
keyLengthParsed: defaultKeyLength,
|
||||
DatabaseName: defaultDatabase,
|
||||
MetadataTableName: defaultMetaTable,
|
||||
},
|
||||
},
|
||||
},
|
||||
"Custom meta table": {
|
||||
props: map[string]string{connectionStringKey: sampleConnectionString, "metadataTableName": "dapr_test_meta_table"},
|
||||
expected: SQLServer{
|
||||
connectionString: sampleConnectionString,
|
||||
tableName: defaultTable,
|
||||
schema: defaultSchema,
|
||||
keyType: StringKeyType,
|
||||
keyLength: defaultKeyLength,
|
||||
databaseName: defaultDatabase,
|
||||
metaTableName: "dapr_test_meta_table",
|
||||
metadata: sqlServerMetadata{
|
||||
ConnectionString: sampleConnectionString,
|
||||
TableName: defaultTable,
|
||||
Schema: defaultSchema,
|
||||
keyTypeParsed: StringKeyType,
|
||||
keyLengthParsed: defaultKeyLength,
|
||||
DatabaseName: defaultDatabase,
|
||||
MetadataTableName: "dapr_test_meta_table",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -197,7 +220,7 @@ func TestValidConfiguration(t *testing.T) {
|
|||
t.Run(name, func(t *testing.T) {
|
||||
sqlStore := &SQLServer{
|
||||
logger: logger.NewLogger("test"),
|
||||
migratorFactory: func(s *SQLServer) migrator {
|
||||
migratorFactory: func(*sqlServerMetadata) migrator {
|
||||
return &mockMigrator{}
|
||||
},
|
||||
}
|
||||
|
@ -208,20 +231,20 @@ func TestValidConfiguration(t *testing.T) {
|
|||
|
||||
err := sqlStore.Init(context.Background(), metadata)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, tt.expected.connectionString, sqlStore.connectionString)
|
||||
assert.Equal(t, tt.expected.tableName, sqlStore.tableName)
|
||||
assert.Equal(t, tt.expected.schema, sqlStore.schema)
|
||||
assert.Equal(t, tt.expected.keyType, sqlStore.keyType)
|
||||
assert.Equal(t, tt.expected.keyLength, sqlStore.keyLength)
|
||||
assert.Equal(t, tt.expected.databaseName, sqlStore.databaseName)
|
||||
assert.Equal(t, tt.expected.metaTableName, sqlStore.metaTableName)
|
||||
assert.Equal(t, tt.expected.metadata.ConnectionString, sqlStore.metadata.ConnectionString)
|
||||
assert.Equal(t, tt.expected.metadata.TableName, sqlStore.metadata.TableName)
|
||||
assert.Equal(t, tt.expected.metadata.Schema, sqlStore.metadata.Schema)
|
||||
assert.Equal(t, tt.expected.metadata.keyTypeParsed, sqlStore.metadata.keyTypeParsed)
|
||||
assert.Equal(t, tt.expected.metadata.keyLengthParsed, sqlStore.metadata.keyLengthParsed)
|
||||
assert.Equal(t, tt.expected.metadata.DatabaseName, sqlStore.metadata.DatabaseName)
|
||||
assert.Equal(t, tt.expected.metadata.MetadataTableName, sqlStore.metadata.MetadataTableName)
|
||||
|
||||
assert.Equal(t, len(tt.expected.indexedProperties), len(sqlStore.indexedProperties))
|
||||
if len(tt.expected.indexedProperties) > 0 && len(tt.expected.indexedProperties) == len(sqlStore.indexedProperties) {
|
||||
for i, e := range tt.expected.indexedProperties {
|
||||
assert.Equal(t, e.ColumnName, sqlStore.indexedProperties[i].ColumnName)
|
||||
assert.Equal(t, e.Property, sqlStore.indexedProperties[i].Property)
|
||||
assert.Equal(t, e.Type, sqlStore.indexedProperties[i].Type)
|
||||
assert.Equal(t, len(tt.expected.metadata.indexedPropertiesParsed), len(sqlStore.metadata.indexedPropertiesParsed))
|
||||
if len(tt.expected.metadata.indexedPropertiesParsed) > 0 && len(tt.expected.metadata.indexedPropertiesParsed) == len(sqlStore.metadata.indexedPropertiesParsed) {
|
||||
for i, e := range tt.expected.metadata.indexedPropertiesParsed {
|
||||
assert.Equal(t, e.ColumnName, sqlStore.metadata.indexedPropertiesParsed[i].ColumnName)
|
||||
assert.Equal(t, e.Property, sqlStore.metadata.indexedPropertiesParsed[i].Property)
|
||||
assert.Equal(t, e.Type, sqlStore.metadata.indexedPropertiesParsed[i].Type)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -334,10 +357,10 @@ func TestInvalidConfiguration(t *testing.T) {
|
|||
}
|
||||
|
||||
err := sqlStore.Init(context.Background(), metadata)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
|
||||
if tt.expectedErr != "" {
|
||||
assert.Contains(t, err.Error(), tt.expectedErr)
|
||||
require.ErrorContains(t, err, tt.expectedErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -347,7 +370,7 @@ func TestInvalidConfiguration(t *testing.T) {
|
|||
func TestExecuteMigrationFails(t *testing.T) {
|
||||
sqlStore := &SQLServer{
|
||||
logger: logger.NewLogger("test"),
|
||||
migratorFactory: func(s *SQLServer) migrator {
|
||||
migratorFactory: func(*sqlServerMetadata) migrator {
|
||||
return &mockFailingMigrator{}
|
||||
},
|
||||
}
|
||||
|
@ -371,17 +394,3 @@ func TestSupportedFeatures(t *testing.T) {
|
|||
assert.Equal(t, state.FeatureETag, actual[0])
|
||||
assert.Equal(t, state.FeatureTransactional, actual[1])
|
||||
}
|
||||
|
||||
func TestConnStringContainsDatabase(t *testing.T) {
|
||||
// Regular test - present
|
||||
assert.True(t, connStringContainsDatabase(sampleConnectionString))
|
||||
|
||||
// Regular test - not present
|
||||
assert.False(t, connStringContainsDatabase("server=localhost;user id=sa;password=Pass@Word1;port=1433;"))
|
||||
|
||||
// Case-insensitive test
|
||||
assert.True(t, connStringContainsDatabase("server=localhost;user id=sa;password=Pass@Word1;port=1433;Database=sample;"))
|
||||
|
||||
// Beginning of string
|
||||
assert.True(t, connStringContainsDatabase("Database=sample;server=localhost;user id=sa;password=Pass@Word1;port=1433;"))
|
||||
}
|
||||
|
|
|
@ -95,7 +95,6 @@ require (
|
|||
github.com/danieljoos/wincred v1.1.2 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 // indirect
|
||||
github.com/denisenkom/go-mssqldb v0.12.3 // indirect
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||
github.com/didip/tollbooth/v7 v7.0.1 // indirect
|
||||
github.com/dubbogo/gost v1.13.1 // indirect
|
||||
|
@ -193,6 +192,7 @@ require (
|
|||
github.com/mattn/go-isatty v0.0.18 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
|
||||
github.com/microsoft/durabletask-go v0.1.4 // indirect
|
||||
github.com/microsoft/go-mssqldb v0.21.0 // indirect
|
||||
github.com/miekg/dns v1.1.50 // indirect
|
||||
github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1 // indirect
|
||||
github.com/mitchellh/copystructure v1.2.0 // indirect
|
||||
|
|
|
@ -54,17 +54,18 @@ github.com/AthenZ/athenz v1.10.39 h1:mtwHTF/v62ewY2Z5KWhuZgVXftBej1/Tn80zx4DcawY
|
|||
github.com/AthenZ/athenz v1.10.39/go.mod h1:3Tg8HLsiQZp81BJY58JBeU2BR6B/H4/0MQGfCwhHNEA=
|
||||
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible h1:fcYLmCpyNYRnvJbPerq7U0hS+6+I79yEDJBqVNcqUzU=
|
||||
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v0.19.0/go.mod h1:h6H6c8enJmmocHUbLiiGY6sx7f9i+X3m1CHdd5c6Rdw=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.0.0/go.mod h1:uGG2W01BaETf0Ozp+QxxKJdMBNRWPdstHG0Fmdwn1/U=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.1.2/go.mod h1:uGG2W01BaETf0Ozp+QxxKJdMBNRWPdstHG0Fmdwn1/U=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.5.0-beta.1 h1:yLM4ZIC+NRvzwFGpXjUbf5FhPBVxJgmYXkjePgNAx64=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.5.0-beta.1/go.mod h1:ON4tFdPTwRcgWEaVDrN3584Ef+b7GgSJaXxe5fW9t4M=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v0.11.0/go.mod h1:HcM1YX14R7CJcghJGOYCgdezslRSVzqwLf/q+4Y2r/0=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.1/go.mod h1:gLa1CL2RNE4s7M3yopJ/p0iq5DdY6Yv5ZUt9MTRZOQM=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0-beta.4 h1:jpSh2461XzXBEw1MJwvVRJwZS0CAgqS0h6jBdoIFtLk=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0-beta.4/go.mod h1:oWa/ZXP08smIi12UyWVbVikBxoZHZCyxijZamTK1i8Q=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/data/azcosmos v0.3.3 h1:x1shk+tVZ6kLwIQMn4r+pdz8szo3mA0jd8STmgh+aRk=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/data/azcosmos v0.3.3/go.mod h1:Fy3bbChFm4cZn6oIxYYqKB2FG3rBDxk3NZDLDJCHl+Q=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/data/aztables v1.0.1 h1:bFa9IcjvrCber6gGgDAUZ+I2bO8J7s8JxXmu9fhi2ss=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/data/aztables v1.0.1/go.mod h1:l3wvZkG9oW07GLBW5Cd0WwG5asOfJ8aqE8raUvNzLpk=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v0.7.0/go.mod h1:yqy467j36fJxcRV2TzfVZ1pCb5vxm4BtZPUdYWe/Xo8=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.0/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.1.2 h1:+5VZ72z0Qan5Bog5C+ZkgSqUbeVUd9wgtHOrIKuc5b8=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.1.2/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/keyvault/azsecrets v0.11.0 h1:82w8tzLcOwDP/Q35j/wEBPt0n0kVC3cjtPdD62G8UAk=
|
||||
|
@ -83,6 +84,7 @@ github.com/Azure/azure-sdk-for-go/sdk/storage/azqueue v0.1.0 h1:TOtQFiO403wClfrZ
|
|||
github.com/Azure/azure-sdk-for-go/sdk/storage/azqueue v0.1.0/go.mod h1:U0IH4deB/maBcagR9SiNeIfgZ1BY/zYCq8SOiQ4vfRc=
|
||||
github.com/Azure/go-amqp v0.18.1 h1:D5Ca+uijuTcj5g76sF+zT4OQZcFFY397+IGf/5Ip5Sc=
|
||||
github.com/Azure/go-amqp v0.18.1/go.mod h1:+bg0x3ce5+Q3ahCEXnCsGG3ETpDQe3MEVnOuT2ywPwc=
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v0.8.1/go.mod h1:4qFor3D/HDsvBME35Xy9rwW9DecL+M2sNw1ybjPtwA0=
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v0.9.0 h1:UE9n9rkJF62ArLb1F3DEjRt8O3jLwMWdSoypKV4f3MU=
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v0.9.0/go.mod h1:kgDmCTgBzIEPFElEF+FK0SdjAor06dRq2Go927dnQ6o=
|
||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||
|
@ -334,8 +336,6 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
|
|||
github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc=
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 h1:HbphB4TFFXpv7MNrT52FGrrgVXF1owhMVTHFZIlnvd4=
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0/go.mod h1:DZGJHZMqrU4JJqFAWUS2UO1+lbSKsdiOoYi9Zzey7Fc=
|
||||
github.com/denisenkom/go-mssqldb v0.12.3 h1:pBSGx9Tq67pBOTLmxNuirNTeB8Vjmf886Kx+8Y+8shw=
|
||||
github.com/denisenkom/go-mssqldb v0.12.3/go.mod h1:k0mtMFOnU+AihqFxPMiF05rtiDrorD1Vrm1KEz5hxDo=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
||||
|
@ -344,6 +344,7 @@ github.com/didip/tollbooth/v7 v7.0.1 h1:TkT4sBKoQoHQFPf7blQ54iHrZiTDnr8TceU+MulV
|
|||
github.com/didip/tollbooth/v7 v7.0.1/go.mod h1:VZhDSGl5bDSPj4wPsih3PFa4Uh9Ghv8hgacaTm5PRT4=
|
||||
github.com/dimfeld/httptreemux v5.0.1+incompatible h1:Qj3gVcDNoOthBAqftuD596rm4wg/adLLz5xh5CmpiCA=
|
||||
github.com/dimfeld/httptreemux v5.0.1+incompatible/go.mod h1:rbUlSV+CCpv/SuqUTP/8Bk2O3LyUV436/yaRGkhP6Z0=
|
||||
github.com/dnaeon/go-vcr v1.1.0/go.mod h1:M7tiix8f0r6mKKJ3Yq/kqU1OYf3MnfmBWVbPx/yU9ko=
|
||||
github.com/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI=
|
||||
github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ=
|
||||
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
|
||||
|
@ -515,6 +516,7 @@ github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptG
|
|||
github.com/golang-jwt/jwt v3.2.1+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
|
||||
github.com/golang-jwt/jwt/v4 v4.4.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
||||
github.com/golang-jwt/jwt/v4 v4.4.3 h1:Hxl6lhQFj4AnOX6MLrsCb/+7tCj7DxP7VA+2rDIq5AU=
|
||||
github.com/golang-jwt/jwt/v4 v4.4.3/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
||||
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY=
|
||||
|
@ -767,10 +769,12 @@ github.com/jcmturner/aescts/v2 v2.0.0 h1:9YKLH6ey7H4eDBXW8khjYslgyqG2xZikXP0EQFK
|
|||
github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs=
|
||||
github.com/jcmturner/dnsutils/v2 v2.0.0 h1:lltnkeZGL0wILNvrNiVCR6Ro5PGU/SeBvVO/8c/iPbo=
|
||||
github.com/jcmturner/dnsutils/v2 v2.0.0/go.mod h1:b0TnjGOvI/n42bZa+hmXL+kFJZsFT7G4t3HTlQ184QM=
|
||||
github.com/jcmturner/gofork v1.0.0/go.mod h1:MK8+TM0La+2rjBD4jE12Kj1pCCxK7d2LK/UM3ncEo0o=
|
||||
github.com/jcmturner/gofork v1.7.6 h1:QH0l3hzAU1tfT3rZCnW5zXl+orbkNMMRGJfdJjHVETg=
|
||||
github.com/jcmturner/gofork v1.7.6/go.mod h1:1622LH6i/EZqLloHfE7IeZ0uEJwMSUyQ/nDd82IeqRo=
|
||||
github.com/jcmturner/goidentity/v6 v6.0.1 h1:VKnZd2oEIMorCTsFBnJWbExfNN7yZr3EhJAxwOkZg6o=
|
||||
github.com/jcmturner/goidentity/v6 v6.0.1/go.mod h1:X1YW3bgtvwAXju7V3LCIMpY0Gbxyjn/mY9zx4tFonSg=
|
||||
github.com/jcmturner/gokrb5/v8 v8.4.2/go.mod h1:sb+Xq/fTY5yktf/VxLsE3wlfPqQjp0aWNYyvBVK62bc=
|
||||
github.com/jcmturner/gokrb5/v8 v8.4.3 h1:iTonLeSJOn7MVUtyMT+arAn5AKAPrkilzhGw8wE/Tq8=
|
||||
github.com/jcmturner/gokrb5/v8 v8.4.3/go.mod h1:dqRwJGXznQrzw6cWmyo6kH+E7jksEQG/CyVWsJEsJO0=
|
||||
github.com/jcmturner/rpc/v2 v2.0.3 h1:7FXXj8Ti1IaVFpSAziCZWNzbNuZmnvw/i6CqLNdWfZY=
|
||||
|
@ -920,6 +924,8 @@ github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zk
|
|||
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
|
||||
github.com/microsoft/durabletask-go v0.1.4 h1:sHVFysoJQvG2Sp4wh2tblZBd4DDJmXrBdYCyUlo38MM=
|
||||
github.com/microsoft/durabletask-go v0.1.4/go.mod h1:UOgcE09cx6SzBS31p4darJ7ve9P5pSVIStPShxDnvPo=
|
||||
github.com/microsoft/go-mssqldb v0.21.0 h1:p2rpHIL7TlSv1QrbXJUAcbyRKnIT0C9rRkH2E4OjLn8=
|
||||
github.com/microsoft/go-mssqldb v0.21.0/go.mod h1:+4wZTUnz/SV6nffv+RRRB/ss8jPng5Sho2SmM1l2ts4=
|
||||
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||
github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
|
||||
github.com/miekg/dns v1.1.27/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
|
||||
|
@ -1049,7 +1055,7 @@ github.com/pierrec/lz4 v2.6.0+incompatible h1:Ix9yFKn1nSPBLFl/yZknTp8TU5G4Ps0JDm
|
|||
github.com/pierrec/lz4 v2.6.0+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
|
||||
github.com/pierrec/lz4/v4 v4.1.17 h1:kV4Ip+/hUBC+8T6+2EgburRtkE9ef4nbY3f4dFhGjMc=
|
||||
github.com/pierrec/lz4/v4 v4.1.17/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||
github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA=
|
||||
github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4/go.mod h1:N6UoU20jOqggOuDwUaBQpluzLNDqif3kq9z2wpdYEfQ=
|
||||
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU=
|
||||
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
|
@ -1410,11 +1416,12 @@ golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3
|
|||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201112155050-0c6587e931a9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
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.0.0-20210921155107-089bfa567519/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-20220511200225-c6db032c6c88/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A=
|
||||
|
@ -1517,6 +1524,7 @@ golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/
|
|||
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
|
@ -1529,7 +1537,6 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b
|
|||
golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8=
|
||||
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
||||
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
|
@ -1540,6 +1547,7 @@ golang.org/x/net v0.0.0-20211105192438-b53810dc28af/go.mod h1:9nx3DQGgdP8bBQD5qx
|
|||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
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.0.0-20220725212005-46097bf591d3/go.mod h1:AaygXjzTFtRAg2ttMY5RMuhpJ3cNnI0XpyFJD1iQRSM=
|
||||
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
|
||||
|
@ -1674,6 +1682,7 @@ golang.org/x/sys v0.0.0-20220111092808-5a964db01320/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220204135822-1c1b9b1eba6a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220224120231-95c6836cb0e7/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/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-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
|
@ -1975,6 +1984,8 @@ gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
|||
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
|
||||
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce h1:+JknDZhAj8YMt7GC73Ei8pv4MzjDUNPHgQWJdtMAaDU=
|
||||
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c=
|
||||
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
||||
gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
|
||||
gopkg.in/square/go-jose.v2 v2.4.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
|
||||
|
|
Loading…
Reference in New Issue