Vendor Update: go-gitlab v0.22.1 -> v0.31.0 (#11136)
* vendor update: go-gitlab to v0.31.0 * migrate client init to v0.31.0 * refactor
This commit is contained in:
parent
5c092eb0ef
commit
82dbb34c9c
9
go.mod
9
go.mod
|
@ -53,9 +53,10 @@ require (
|
||||||
github.com/gobwas/glob v0.2.3
|
github.com/gobwas/glob v0.2.3
|
||||||
github.com/gogs/chardet v0.0.0-20191104214054-4b6791f73a28
|
github.com/gogs/chardet v0.0.0-20191104214054-4b6791f73a28
|
||||||
github.com/gogs/cron v0.0.0-20171120032916-9f6c956d3e14
|
github.com/gogs/cron v0.0.0-20171120032916-9f6c956d3e14
|
||||||
github.com/golang/protobuf v1.3.4 // indirect
|
github.com/golang/protobuf v1.4.0 // indirect
|
||||||
github.com/google/go-github/v24 v24.0.1
|
github.com/google/go-github/v24 v24.0.1
|
||||||
github.com/gorilla/context v1.1.1
|
github.com/gorilla/context v1.1.1
|
||||||
|
github.com/hashicorp/go-retryablehttp v0.6.6 // indirect
|
||||||
github.com/huandu/xstrings v1.3.0
|
github.com/huandu/xstrings v1.3.0
|
||||||
github.com/issue9/assert v1.3.2 // indirect
|
github.com/issue9/assert v1.3.2 // indirect
|
||||||
github.com/issue9/identicon v0.0.0-20160320065130-d36b54562f4c
|
github.com/issue9/identicon v0.0.0-20160320065130-d36b54562f4c
|
||||||
|
@ -104,17 +105,19 @@ require (
|
||||||
github.com/unknwon/i18n v0.0.0-20190805065654-5c6446a380b6
|
github.com/unknwon/i18n v0.0.0-20190805065654-5c6446a380b6
|
||||||
github.com/unknwon/paginater v0.0.0-20151104151617-7748a72e0141
|
github.com/unknwon/paginater v0.0.0-20151104151617-7748a72e0141
|
||||||
github.com/urfave/cli v1.20.0
|
github.com/urfave/cli v1.20.0
|
||||||
github.com/xanzy/go-gitlab v0.22.1
|
github.com/xanzy/go-gitlab v0.31.0
|
||||||
github.com/yohcop/openid-go v0.0.0-20160914080427-2c050d2dae53
|
github.com/yohcop/openid-go v0.0.0-20160914080427-2c050d2dae53
|
||||||
github.com/yuin/goldmark v1.1.25
|
github.com/yuin/goldmark v1.1.25
|
||||||
github.com/yuin/goldmark-meta v0.0.0-20191126180153-f0638e958b60
|
github.com/yuin/goldmark-meta v0.0.0-20191126180153-f0638e958b60
|
||||||
go.etcd.io/bbolt v1.3.3 // indirect
|
go.etcd.io/bbolt v1.3.3 // indirect
|
||||||
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073
|
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073
|
||||||
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e
|
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e
|
||||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45
|
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d
|
||||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd
|
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd
|
||||||
golang.org/x/text v0.3.2
|
golang.org/x/text v0.3.2
|
||||||
|
golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1 // indirect
|
||||||
golang.org/x/tools v0.0.0-20200325010219-a49f79bcc224
|
golang.org/x/tools v0.0.0-20200325010219-a49f79bcc224
|
||||||
|
google.golang.org/appengine v1.6.5 // indirect
|
||||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
|
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
|
||||||
gopkg.in/asn1-ber.v1 v1.0.0-20150924051756-4e86f4367175 // indirect
|
gopkg.in/asn1-ber.v1 v1.0.0-20150924051756-4e86f4367175 // indirect
|
||||||
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
|
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
|
||||||
|
|
36
go.sum
36
go.sum
|
@ -286,8 +286,12 @@ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y
|
||||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
|
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
|
||||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
github.com/golang/protobuf v1.3.4 h1:87PNWwrRvUSnqS4dlcBU/ftvOIBep4sYuBLlh6rX2wk=
|
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||||
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||||
|
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||||
|
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||||
|
github.com/golang/protobuf v1.4.0 h1:oOuy+ugB+P/kBdUnG5QaMXSIyJ1q38wWSojYCb3z5VQ=
|
||||||
|
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||||
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
|
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
|
||||||
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||||
|
@ -297,6 +301,8 @@ github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5a
|
||||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg=
|
github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg=
|
||||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
|
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
|
||||||
|
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY=
|
github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY=
|
||||||
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
|
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
|
||||||
github.com/google/go-github/v24 v24.0.1 h1:KCt1LjMJEey1qvPXxa9SjaWxwTsCWSq6p2Ju57UR4Q4=
|
github.com/google/go-github/v24 v24.0.1 h1:KCt1LjMJEey1qvPXxa9SjaWxwTsCWSq6p2Ju57UR4Q4=
|
||||||
|
@ -337,6 +343,14 @@ github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoA
|
||||||
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
||||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
|
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
|
||||||
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||||
|
github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM=
|
||||||
|
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||||
|
github.com/hashicorp/go-hclog v0.9.2 h1:CG6TE5H9/JXsFWJCfoIVpKFIkFe6ysEuHirp4DxCsHI=
|
||||||
|
github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
|
||||||
|
github.com/hashicorp/go-retryablehttp v0.6.4 h1:BbgctKO892xEyOXnGiaAwIoSq1QZ/SS4AhjoAh9DnfY=
|
||||||
|
github.com/hashicorp/go-retryablehttp v0.6.4/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
|
||||||
|
github.com/hashicorp/go-retryablehttp v0.6.6 h1:HJunrbHTDDbBb/ay4kxa1n+dLmttUlnP3V9oNE4hmsM=
|
||||||
|
github.com/hashicorp/go-retryablehttp v0.6.6/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
|
||||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||||
|
@ -608,8 +622,8 @@ github.com/urfave/cli v1.20.0 h1:fDqGv3UG/4jbVl/QkFwEdddtEDjh/5Ov6X+0B/3bPaw=
|
||||||
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
||||||
github.com/willf/bitset v1.1.10 h1:NotGKqX0KwQ72NUzqrjZq5ipPNDQex9lo3WpaS8L2sc=
|
github.com/willf/bitset v1.1.10 h1:NotGKqX0KwQ72NUzqrjZq5ipPNDQex9lo3WpaS8L2sc=
|
||||||
github.com/willf/bitset v1.1.10/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
|
github.com/willf/bitset v1.1.10/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
|
||||||
github.com/xanzy/go-gitlab v0.22.1 h1:TVxgHmoa35jQL+9FCkG0nwPDxU9dQZXknBTDtGaSFno=
|
github.com/xanzy/go-gitlab v0.31.0 h1:+nHztQuCXGSMluKe5Q9IRaPdz6tO8O0gMkQ0vqGpiBk=
|
||||||
github.com/xanzy/go-gitlab v0.22.1/go.mod h1:t4Bmvnxj7k37S4Y17lfLx+nLqkf/oQwT2HagfWKv5Og=
|
github.com/xanzy/go-gitlab v0.31.0/go.mod h1:sPLojNBn68fMUWSxIJtdVVIP8uSBYqesTfDUseX11Ug=
|
||||||
github.com/xanzy/ssh-agent v0.2.1 h1:TCbipTQL2JiiCprBWx9frJ2eJlCYT00NmctrHxVAr70=
|
github.com/xanzy/ssh-agent v0.2.1 h1:TCbipTQL2JiiCprBWx9frJ2eJlCYT00NmctrHxVAr70=
|
||||||
github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4=
|
github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4=
|
||||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||||
|
@ -699,6 +713,8 @@ golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAG
|
||||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0=
|
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0=
|
||||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
@ -741,6 +757,10 @@ golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
|
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs=
|
||||||
|
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
|
golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1 h1:NusfzzA6yGQ+ua51ck7E3omNUX/JuqbFSaRGqU8CcLI=
|
||||||
|
golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
@ -782,6 +802,8 @@ google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww
|
||||||
google.golang.org/appengine v1.6.2/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
google.golang.org/appengine v1.6.2/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||||
google.golang.org/appengine v1.6.4 h1:WiKh4+/eMB2HaY7QhCfW/R7MuRAoA8QMCSJA6jP5/fo=
|
google.golang.org/appengine v1.6.4 h1:WiKh4+/eMB2HaY7QhCfW/R7MuRAoA8QMCSJA6jP5/fo=
|
||||||
google.golang.org/appengine v1.6.4/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
google.golang.org/appengine v1.6.4/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||||
|
google.golang.org/appengine v1.6.5 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpCM=
|
||||||
|
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
|
@ -795,6 +817,12 @@ google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZi
|
||||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||||
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||||
|
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||||
|
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||||
|
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||||
|
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||||
|
google.golang.org/protobuf v1.21.0 h1:qdOKuR/EIArgaWNjetjgTzgVTAZ+S/WXVrq9HW9zimw=
|
||||||
|
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk=
|
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk=
|
||||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk=
|
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk=
|
||||||
|
|
|
@ -8,7 +8,6 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
@ -87,15 +86,13 @@ type GitlabDownloader struct {
|
||||||
// Use either a username/password, personal token entered into the username field, or anonymous/public access
|
// Use either a username/password, personal token entered into the username field, or anonymous/public access
|
||||||
// Note: Public access only allows very basic access
|
// Note: Public access only allows very basic access
|
||||||
func NewGitlabDownloader(baseURL, repoPath, username, password string) *GitlabDownloader {
|
func NewGitlabDownloader(baseURL, repoPath, username, password string) *GitlabDownloader {
|
||||||
var client *http.Client
|
|
||||||
var gitlabClient *gitlab.Client
|
var gitlabClient *gitlab.Client
|
||||||
var err error
|
var err error
|
||||||
if username != "" {
|
if username != "" {
|
||||||
if password == "" {
|
if password == "" {
|
||||||
gitlabClient = gitlab.NewClient(client, username)
|
gitlabClient, err = gitlab.NewClient(username)
|
||||||
} else {
|
} else {
|
||||||
gitlabClient, err = gitlab.NewBasicAuthClient(client, baseURL, username, password)
|
gitlabClient, err = gitlab.NewBasicAuthClient(username, password, gitlab.WithBaseURL(baseURL))
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,324 @@
|
||||||
|
// Copyright 2019 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package proto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"google.golang.org/protobuf/encoding/prototext"
|
||||||
|
"google.golang.org/protobuf/encoding/protowire"
|
||||||
|
"google.golang.org/protobuf/runtime/protoimpl"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
WireVarint = 0
|
||||||
|
WireFixed32 = 5
|
||||||
|
WireFixed64 = 1
|
||||||
|
WireBytes = 2
|
||||||
|
WireStartGroup = 3
|
||||||
|
WireEndGroup = 4
|
||||||
|
)
|
||||||
|
|
||||||
|
// EncodeVarint returns the varint encoded bytes of v.
|
||||||
|
func EncodeVarint(v uint64) []byte {
|
||||||
|
return protowire.AppendVarint(nil, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SizeVarint returns the length of the varint encoded bytes of v.
|
||||||
|
// This is equal to len(EncodeVarint(v)).
|
||||||
|
func SizeVarint(v uint64) int {
|
||||||
|
return protowire.SizeVarint(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeVarint parses a varint encoded integer from b, returning the
|
||||||
|
// integer value and the length of the varint.
|
||||||
|
// It returns (0, 0) if there is a parse error.
|
||||||
|
func DecodeVarint(b []byte) (uint64, int) {
|
||||||
|
v, n := protowire.ConsumeVarint(b)
|
||||||
|
if n < 0 {
|
||||||
|
return 0, 0
|
||||||
|
}
|
||||||
|
return v, n
|
||||||
|
}
|
||||||
|
|
||||||
|
// Buffer is a buffer for encoding and decoding the protobuf wire format.
|
||||||
|
// It may be reused between invocations to reduce memory usage.
|
||||||
|
type Buffer struct {
|
||||||
|
buf []byte
|
||||||
|
idx int
|
||||||
|
deterministic bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewBuffer allocates a new Buffer initialized with buf,
|
||||||
|
// where the contents of buf are considered the unread portion of the buffer.
|
||||||
|
func NewBuffer(buf []byte) *Buffer {
|
||||||
|
return &Buffer{buf: buf}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetDeterministic specifies whether to use deterministic serialization.
|
||||||
|
//
|
||||||
|
// Deterministic serialization guarantees that for a given binary, equal
|
||||||
|
// messages will always be serialized to the same bytes. This implies:
|
||||||
|
//
|
||||||
|
// - Repeated serialization of a message will return the same bytes.
|
||||||
|
// - Different processes of the same binary (which may be executing on
|
||||||
|
// different machines) will serialize equal messages to the same bytes.
|
||||||
|
//
|
||||||
|
// Note that the deterministic serialization is NOT canonical across
|
||||||
|
// languages. It is not guaranteed to remain stable over time. It is unstable
|
||||||
|
// across different builds with schema changes due to unknown fields.
|
||||||
|
// Users who need canonical serialization (e.g., persistent storage in a
|
||||||
|
// canonical form, fingerprinting, etc.) should define their own
|
||||||
|
// canonicalization specification and implement their own serializer rather
|
||||||
|
// than relying on this API.
|
||||||
|
//
|
||||||
|
// If deterministic serialization is requested, map entries will be sorted
|
||||||
|
// by keys in lexographical order. This is an implementation detail and
|
||||||
|
// subject to change.
|
||||||
|
func (b *Buffer) SetDeterministic(deterministic bool) {
|
||||||
|
b.deterministic = deterministic
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetBuf sets buf as the internal buffer,
|
||||||
|
// where the contents of buf are considered the unread portion of the buffer.
|
||||||
|
func (b *Buffer) SetBuf(buf []byte) {
|
||||||
|
b.buf = buf
|
||||||
|
b.idx = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset clears the internal buffer of all written and unread data.
|
||||||
|
func (b *Buffer) Reset() {
|
||||||
|
b.buf = b.buf[:0]
|
||||||
|
b.idx = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bytes returns the internal buffer.
|
||||||
|
func (b *Buffer) Bytes() []byte {
|
||||||
|
return b.buf
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unread returns the unread portion of the buffer.
|
||||||
|
func (b *Buffer) Unread() []byte {
|
||||||
|
return b.buf[b.idx:]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Marshal appends the wire-format encoding of m to the buffer.
|
||||||
|
func (b *Buffer) Marshal(m Message) error {
|
||||||
|
var err error
|
||||||
|
b.buf, err = marshalAppend(b.buf, m, b.deterministic)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal parses the wire-format message in the buffer and places the decoded results in m.
|
||||||
|
//
|
||||||
|
// Unlike proto.Unmarshal, this does not reset the message before starting to unmarshal.
|
||||||
|
func (b *Buffer) Unmarshal(m Message) error {
|
||||||
|
err := UnmarshalMerge(b.Unread(), m)
|
||||||
|
b.idx = len(b.buf)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
type unknownFields struct{ XXX_unrecognized protoimpl.UnknownFields }
|
||||||
|
|
||||||
|
func (m *unknownFields) String() string { panic("not implemented") }
|
||||||
|
func (m *unknownFields) Reset() { panic("not implemented") }
|
||||||
|
func (m *unknownFields) ProtoMessage() { panic("not implemented") }
|
||||||
|
|
||||||
|
// DebugPrint dumps the encoded bytes of b with a header and footer including s
|
||||||
|
// to stdout. This is only intended for debugging.
|
||||||
|
func (*Buffer) DebugPrint(s string, b []byte) {
|
||||||
|
m := MessageReflect(new(unknownFields))
|
||||||
|
m.SetUnknown(b)
|
||||||
|
b, _ = prototext.MarshalOptions{AllowPartial: true, Indent: "\t"}.Marshal(m.Interface())
|
||||||
|
fmt.Printf("==== %s ====\n%s==== %s ====\n", s, b, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// EncodeVarint appends an unsigned varint encoding to the buffer.
|
||||||
|
func (b *Buffer) EncodeVarint(v uint64) error {
|
||||||
|
b.buf = protowire.AppendVarint(b.buf, v)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// EncodeZigzag32 appends a 32-bit zig-zag varint encoding to the buffer.
|
||||||
|
func (b *Buffer) EncodeZigzag32(v uint64) error {
|
||||||
|
return b.EncodeVarint(uint64((uint32(v) << 1) ^ uint32((int32(v) >> 31))))
|
||||||
|
}
|
||||||
|
|
||||||
|
// EncodeZigzag64 appends a 64-bit zig-zag varint encoding to the buffer.
|
||||||
|
func (b *Buffer) EncodeZigzag64(v uint64) error {
|
||||||
|
return b.EncodeVarint(uint64((uint64(v) << 1) ^ uint64((int64(v) >> 63))))
|
||||||
|
}
|
||||||
|
|
||||||
|
// EncodeFixed32 appends a 32-bit little-endian integer to the buffer.
|
||||||
|
func (b *Buffer) EncodeFixed32(v uint64) error {
|
||||||
|
b.buf = protowire.AppendFixed32(b.buf, uint32(v))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// EncodeFixed64 appends a 64-bit little-endian integer to the buffer.
|
||||||
|
func (b *Buffer) EncodeFixed64(v uint64) error {
|
||||||
|
b.buf = protowire.AppendFixed64(b.buf, uint64(v))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// EncodeRawBytes appends a length-prefixed raw bytes to the buffer.
|
||||||
|
func (b *Buffer) EncodeRawBytes(v []byte) error {
|
||||||
|
b.buf = protowire.AppendBytes(b.buf, v)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// EncodeStringBytes appends a length-prefixed raw bytes to the buffer.
|
||||||
|
// It does not validate whether v contains valid UTF-8.
|
||||||
|
func (b *Buffer) EncodeStringBytes(v string) error {
|
||||||
|
b.buf = protowire.AppendString(b.buf, v)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// EncodeMessage appends a length-prefixed encoded message to the buffer.
|
||||||
|
func (b *Buffer) EncodeMessage(m Message) error {
|
||||||
|
var err error
|
||||||
|
b.buf = protowire.AppendVarint(b.buf, uint64(Size(m)))
|
||||||
|
b.buf, err = marshalAppend(b.buf, m, b.deterministic)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeVarint consumes an encoded unsigned varint from the buffer.
|
||||||
|
func (b *Buffer) DecodeVarint() (uint64, error) {
|
||||||
|
v, n := protowire.ConsumeVarint(b.buf[b.idx:])
|
||||||
|
if n < 0 {
|
||||||
|
return 0, protowire.ParseError(n)
|
||||||
|
}
|
||||||
|
b.idx += n
|
||||||
|
return uint64(v), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeZigzag32 consumes an encoded 32-bit zig-zag varint from the buffer.
|
||||||
|
func (b *Buffer) DecodeZigzag32() (uint64, error) {
|
||||||
|
v, err := b.DecodeVarint()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return uint64((uint32(v) >> 1) ^ uint32((int32(v&1)<<31)>>31)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeZigzag64 consumes an encoded 64-bit zig-zag varint from the buffer.
|
||||||
|
func (b *Buffer) DecodeZigzag64() (uint64, error) {
|
||||||
|
v, err := b.DecodeVarint()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return uint64((uint64(v) >> 1) ^ uint64((int64(v&1)<<63)>>63)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeFixed32 consumes a 32-bit little-endian integer from the buffer.
|
||||||
|
func (b *Buffer) DecodeFixed32() (uint64, error) {
|
||||||
|
v, n := protowire.ConsumeFixed32(b.buf[b.idx:])
|
||||||
|
if n < 0 {
|
||||||
|
return 0, protowire.ParseError(n)
|
||||||
|
}
|
||||||
|
b.idx += n
|
||||||
|
return uint64(v), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeFixed64 consumes a 64-bit little-endian integer from the buffer.
|
||||||
|
func (b *Buffer) DecodeFixed64() (uint64, error) {
|
||||||
|
v, n := protowire.ConsumeFixed64(b.buf[b.idx:])
|
||||||
|
if n < 0 {
|
||||||
|
return 0, protowire.ParseError(n)
|
||||||
|
}
|
||||||
|
b.idx += n
|
||||||
|
return uint64(v), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeRawBytes consumes a length-prefixed raw bytes from the buffer.
|
||||||
|
// If alloc is specified, it returns a copy the raw bytes
|
||||||
|
// rather than a sub-slice of the buffer.
|
||||||
|
func (b *Buffer) DecodeRawBytes(alloc bool) ([]byte, error) {
|
||||||
|
v, n := protowire.ConsumeBytes(b.buf[b.idx:])
|
||||||
|
if n < 0 {
|
||||||
|
return nil, protowire.ParseError(n)
|
||||||
|
}
|
||||||
|
b.idx += n
|
||||||
|
if alloc {
|
||||||
|
v = append([]byte(nil), v...)
|
||||||
|
}
|
||||||
|
return v, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeStringBytes consumes a length-prefixed raw bytes from the buffer.
|
||||||
|
// It does not validate whether the raw bytes contain valid UTF-8.
|
||||||
|
func (b *Buffer) DecodeStringBytes() (string, error) {
|
||||||
|
v, n := protowire.ConsumeString(b.buf[b.idx:])
|
||||||
|
if n < 0 {
|
||||||
|
return "", protowire.ParseError(n)
|
||||||
|
}
|
||||||
|
b.idx += n
|
||||||
|
return v, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeMessage consumes a length-prefixed message from the buffer.
|
||||||
|
// It does not reset m.
|
||||||
|
func (b *Buffer) DecodeMessage(m Message) error {
|
||||||
|
v, err := b.DecodeRawBytes(false)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return UnmarshalMerge(v, m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeGroup consumes a message group from the buffer.
|
||||||
|
// It assumes that the start group marker has already been consumed and
|
||||||
|
// consumes all bytes until (and including the end group marker).
|
||||||
|
// It does not reset m.
|
||||||
|
func (b *Buffer) DecodeGroup(m Message) error {
|
||||||
|
v, n, err := consumeGroup(b.buf[b.idx:])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
b.idx += n
|
||||||
|
return UnmarshalMerge(v, m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// consumeGroup parses b until it finds an end group marker, returning
|
||||||
|
// the raw bytes of the message (excluding the end group marker) and the
|
||||||
|
// the total length of the message (including the end group marker).
|
||||||
|
func consumeGroup(b []byte) ([]byte, int, error) {
|
||||||
|
b0 := b
|
||||||
|
depth := 1 // assume this follows a start group marker
|
||||||
|
for {
|
||||||
|
_, wtyp, tagLen := protowire.ConsumeTag(b)
|
||||||
|
if tagLen < 0 {
|
||||||
|
return nil, 0, protowire.ParseError(tagLen)
|
||||||
|
}
|
||||||
|
b = b[tagLen:]
|
||||||
|
|
||||||
|
var valLen int
|
||||||
|
switch wtyp {
|
||||||
|
case protowire.VarintType:
|
||||||
|
_, valLen = protowire.ConsumeVarint(b)
|
||||||
|
case protowire.Fixed32Type:
|
||||||
|
_, valLen = protowire.ConsumeFixed32(b)
|
||||||
|
case protowire.Fixed64Type:
|
||||||
|
_, valLen = protowire.ConsumeFixed64(b)
|
||||||
|
case protowire.BytesType:
|
||||||
|
_, valLen = protowire.ConsumeBytes(b)
|
||||||
|
case protowire.StartGroupType:
|
||||||
|
depth++
|
||||||
|
case protowire.EndGroupType:
|
||||||
|
depth--
|
||||||
|
default:
|
||||||
|
return nil, 0, errors.New("proto: cannot parse reserved wire type")
|
||||||
|
}
|
||||||
|
if valLen < 0 {
|
||||||
|
return nil, 0, protowire.ParseError(valLen)
|
||||||
|
}
|
||||||
|
b = b[valLen:]
|
||||||
|
|
||||||
|
if depth == 0 {
|
||||||
|
return b0[:len(b0)-len(b)-tagLen], len(b0) - len(b), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,253 +0,0 @@
|
||||||
// Go support for Protocol Buffers - Google's data interchange format
|
|
||||||
//
|
|
||||||
// Copyright 2011 The Go Authors. All rights reserved.
|
|
||||||
// https://github.com/golang/protobuf
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without
|
|
||||||
// modification, are permitted provided that the following conditions are
|
|
||||||
// met:
|
|
||||||
//
|
|
||||||
// * Redistributions of source code must retain the above copyright
|
|
||||||
// notice, this list of conditions and the following disclaimer.
|
|
||||||
// * Redistributions in binary form must reproduce the above
|
|
||||||
// copyright notice, this list of conditions and the following disclaimer
|
|
||||||
// in the documentation and/or other materials provided with the
|
|
||||||
// distribution.
|
|
||||||
// * Neither the name of Google Inc. nor the names of its
|
|
||||||
// contributors may be used to endorse or promote products derived from
|
|
||||||
// this software without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
// Protocol buffer deep copy and merge.
|
|
||||||
// TODO: RawMessage.
|
|
||||||
|
|
||||||
package proto
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"reflect"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Clone returns a deep copy of a protocol buffer.
|
|
||||||
func Clone(src Message) Message {
|
|
||||||
in := reflect.ValueOf(src)
|
|
||||||
if in.IsNil() {
|
|
||||||
return src
|
|
||||||
}
|
|
||||||
out := reflect.New(in.Type().Elem())
|
|
||||||
dst := out.Interface().(Message)
|
|
||||||
Merge(dst, src)
|
|
||||||
return dst
|
|
||||||
}
|
|
||||||
|
|
||||||
// Merger is the interface representing objects that can merge messages of the same type.
|
|
||||||
type Merger interface {
|
|
||||||
// Merge merges src into this message.
|
|
||||||
// Required and optional fields that are set in src will be set to that value in dst.
|
|
||||||
// Elements of repeated fields will be appended.
|
|
||||||
//
|
|
||||||
// Merge may panic if called with a different argument type than the receiver.
|
|
||||||
Merge(src Message)
|
|
||||||
}
|
|
||||||
|
|
||||||
// generatedMerger is the custom merge method that generated protos will have.
|
|
||||||
// We must add this method since a generate Merge method will conflict with
|
|
||||||
// many existing protos that have a Merge data field already defined.
|
|
||||||
type generatedMerger interface {
|
|
||||||
XXX_Merge(src Message)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Merge merges src into dst.
|
|
||||||
// Required and optional fields that are set in src will be set to that value in dst.
|
|
||||||
// Elements of repeated fields will be appended.
|
|
||||||
// Merge panics if src and dst are not the same type, or if dst is nil.
|
|
||||||
func Merge(dst, src Message) {
|
|
||||||
if m, ok := dst.(Merger); ok {
|
|
||||||
m.Merge(src)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
in := reflect.ValueOf(src)
|
|
||||||
out := reflect.ValueOf(dst)
|
|
||||||
if out.IsNil() {
|
|
||||||
panic("proto: nil destination")
|
|
||||||
}
|
|
||||||
if in.Type() != out.Type() {
|
|
||||||
panic(fmt.Sprintf("proto.Merge(%T, %T) type mismatch", dst, src))
|
|
||||||
}
|
|
||||||
if in.IsNil() {
|
|
||||||
return // Merge from nil src is a noop
|
|
||||||
}
|
|
||||||
if m, ok := dst.(generatedMerger); ok {
|
|
||||||
m.XXX_Merge(src)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
mergeStruct(out.Elem(), in.Elem())
|
|
||||||
}
|
|
||||||
|
|
||||||
func mergeStruct(out, in reflect.Value) {
|
|
||||||
sprop := GetProperties(in.Type())
|
|
||||||
for i := 0; i < in.NumField(); i++ {
|
|
||||||
f := in.Type().Field(i)
|
|
||||||
if strings.HasPrefix(f.Name, "XXX_") {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
mergeAny(out.Field(i), in.Field(i), false, sprop.Prop[i])
|
|
||||||
}
|
|
||||||
|
|
||||||
if emIn, err := extendable(in.Addr().Interface()); err == nil {
|
|
||||||
emOut, _ := extendable(out.Addr().Interface())
|
|
||||||
mIn, muIn := emIn.extensionsRead()
|
|
||||||
if mIn != nil {
|
|
||||||
mOut := emOut.extensionsWrite()
|
|
||||||
muIn.Lock()
|
|
||||||
mergeExtension(mOut, mIn)
|
|
||||||
muIn.Unlock()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uf := in.FieldByName("XXX_unrecognized")
|
|
||||||
if !uf.IsValid() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
uin := uf.Bytes()
|
|
||||||
if len(uin) > 0 {
|
|
||||||
out.FieldByName("XXX_unrecognized").SetBytes(append([]byte(nil), uin...))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// mergeAny performs a merge between two values of the same type.
|
|
||||||
// viaPtr indicates whether the values were indirected through a pointer (implying proto2).
|
|
||||||
// prop is set if this is a struct field (it may be nil).
|
|
||||||
func mergeAny(out, in reflect.Value, viaPtr bool, prop *Properties) {
|
|
||||||
if in.Type() == protoMessageType {
|
|
||||||
if !in.IsNil() {
|
|
||||||
if out.IsNil() {
|
|
||||||
out.Set(reflect.ValueOf(Clone(in.Interface().(Message))))
|
|
||||||
} else {
|
|
||||||
Merge(out.Interface().(Message), in.Interface().(Message))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
switch in.Kind() {
|
|
||||||
case reflect.Bool, reflect.Float32, reflect.Float64, reflect.Int32, reflect.Int64,
|
|
||||||
reflect.String, reflect.Uint32, reflect.Uint64:
|
|
||||||
if !viaPtr && isProto3Zero(in) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
out.Set(in)
|
|
||||||
case reflect.Interface:
|
|
||||||
// Probably a oneof field; copy non-nil values.
|
|
||||||
if in.IsNil() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// Allocate destination if it is not set, or set to a different type.
|
|
||||||
// Otherwise we will merge as normal.
|
|
||||||
if out.IsNil() || out.Elem().Type() != in.Elem().Type() {
|
|
||||||
out.Set(reflect.New(in.Elem().Elem().Type())) // interface -> *T -> T -> new(T)
|
|
||||||
}
|
|
||||||
mergeAny(out.Elem(), in.Elem(), false, nil)
|
|
||||||
case reflect.Map:
|
|
||||||
if in.Len() == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if out.IsNil() {
|
|
||||||
out.Set(reflect.MakeMap(in.Type()))
|
|
||||||
}
|
|
||||||
// For maps with value types of *T or []byte we need to deep copy each value.
|
|
||||||
elemKind := in.Type().Elem().Kind()
|
|
||||||
for _, key := range in.MapKeys() {
|
|
||||||
var val reflect.Value
|
|
||||||
switch elemKind {
|
|
||||||
case reflect.Ptr:
|
|
||||||
val = reflect.New(in.Type().Elem().Elem())
|
|
||||||
mergeAny(val, in.MapIndex(key), false, nil)
|
|
||||||
case reflect.Slice:
|
|
||||||
val = in.MapIndex(key)
|
|
||||||
val = reflect.ValueOf(append([]byte{}, val.Bytes()...))
|
|
||||||
default:
|
|
||||||
val = in.MapIndex(key)
|
|
||||||
}
|
|
||||||
out.SetMapIndex(key, val)
|
|
||||||
}
|
|
||||||
case reflect.Ptr:
|
|
||||||
if in.IsNil() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if out.IsNil() {
|
|
||||||
out.Set(reflect.New(in.Elem().Type()))
|
|
||||||
}
|
|
||||||
mergeAny(out.Elem(), in.Elem(), true, nil)
|
|
||||||
case reflect.Slice:
|
|
||||||
if in.IsNil() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if in.Type().Elem().Kind() == reflect.Uint8 {
|
|
||||||
// []byte is a scalar bytes field, not a repeated field.
|
|
||||||
|
|
||||||
// Edge case: if this is in a proto3 message, a zero length
|
|
||||||
// bytes field is considered the zero value, and should not
|
|
||||||
// be merged.
|
|
||||||
if prop != nil && prop.proto3 && in.Len() == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make a deep copy.
|
|
||||||
// Append to []byte{} instead of []byte(nil) so that we never end up
|
|
||||||
// with a nil result.
|
|
||||||
out.SetBytes(append([]byte{}, in.Bytes()...))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
n := in.Len()
|
|
||||||
if out.IsNil() {
|
|
||||||
out.Set(reflect.MakeSlice(in.Type(), 0, n))
|
|
||||||
}
|
|
||||||
switch in.Type().Elem().Kind() {
|
|
||||||
case reflect.Bool, reflect.Float32, reflect.Float64, reflect.Int32, reflect.Int64,
|
|
||||||
reflect.String, reflect.Uint32, reflect.Uint64:
|
|
||||||
out.Set(reflect.AppendSlice(out, in))
|
|
||||||
default:
|
|
||||||
for i := 0; i < n; i++ {
|
|
||||||
x := reflect.Indirect(reflect.New(in.Type().Elem()))
|
|
||||||
mergeAny(x, in.Index(i), false, nil)
|
|
||||||
out.Set(reflect.Append(out, x))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case reflect.Struct:
|
|
||||||
mergeStruct(out, in)
|
|
||||||
default:
|
|
||||||
// unknown type, so not a protocol buffer
|
|
||||||
log.Printf("proto: don't know how to copy %v", in)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func mergeExtension(out, in map[int32]Extension) {
|
|
||||||
for extNum, eIn := range in {
|
|
||||||
eOut := Extension{desc: eIn.desc}
|
|
||||||
if eIn.value != nil {
|
|
||||||
v := reflect.New(reflect.TypeOf(eIn.value)).Elem()
|
|
||||||
mergeAny(v, reflect.ValueOf(eIn.value), false, nil)
|
|
||||||
eOut.value = v.Interface()
|
|
||||||
}
|
|
||||||
if eIn.enc != nil {
|
|
||||||
eOut.enc = make([]byte, len(eIn.enc))
|
|
||||||
copy(eOut.enc, eIn.enc)
|
|
||||||
}
|
|
||||||
|
|
||||||
out[extNum] = eOut
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,427 +0,0 @@
|
||||||
// Go support for Protocol Buffers - Google's data interchange format
|
|
||||||
//
|
|
||||||
// Copyright 2010 The Go Authors. All rights reserved.
|
|
||||||
// https://github.com/golang/protobuf
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without
|
|
||||||
// modification, are permitted provided that the following conditions are
|
|
||||||
// met:
|
|
||||||
//
|
|
||||||
// * Redistributions of source code must retain the above copyright
|
|
||||||
// notice, this list of conditions and the following disclaimer.
|
|
||||||
// * Redistributions in binary form must reproduce the above
|
|
||||||
// copyright notice, this list of conditions and the following disclaimer
|
|
||||||
// in the documentation and/or other materials provided with the
|
|
||||||
// distribution.
|
|
||||||
// * Neither the name of Google Inc. nor the names of its
|
|
||||||
// contributors may be used to endorse or promote products derived from
|
|
||||||
// this software without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
package proto
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Routines for decoding protocol buffer data to construct in-memory representations.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
)
|
|
||||||
|
|
||||||
// errOverflow is returned when an integer is too large to be represented.
|
|
||||||
var errOverflow = errors.New("proto: integer overflow")
|
|
||||||
|
|
||||||
// ErrInternalBadWireType is returned by generated code when an incorrect
|
|
||||||
// wire type is encountered. It does not get returned to user code.
|
|
||||||
var ErrInternalBadWireType = errors.New("proto: internal error: bad wiretype for oneof")
|
|
||||||
|
|
||||||
// DecodeVarint reads a varint-encoded integer from the slice.
|
|
||||||
// It returns the integer and the number of bytes consumed, or
|
|
||||||
// zero if there is not enough.
|
|
||||||
// This is the format for the
|
|
||||||
// int32, int64, uint32, uint64, bool, and enum
|
|
||||||
// protocol buffer types.
|
|
||||||
func DecodeVarint(buf []byte) (x uint64, n int) {
|
|
||||||
for shift := uint(0); shift < 64; shift += 7 {
|
|
||||||
if n >= len(buf) {
|
|
||||||
return 0, 0
|
|
||||||
}
|
|
||||||
b := uint64(buf[n])
|
|
||||||
n++
|
|
||||||
x |= (b & 0x7F) << shift
|
|
||||||
if (b & 0x80) == 0 {
|
|
||||||
return x, n
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// The number is too large to represent in a 64-bit value.
|
|
||||||
return 0, 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Buffer) decodeVarintSlow() (x uint64, err error) {
|
|
||||||
i := p.index
|
|
||||||
l := len(p.buf)
|
|
||||||
|
|
||||||
for shift := uint(0); shift < 64; shift += 7 {
|
|
||||||
if i >= l {
|
|
||||||
err = io.ErrUnexpectedEOF
|
|
||||||
return
|
|
||||||
}
|
|
||||||
b := p.buf[i]
|
|
||||||
i++
|
|
||||||
x |= (uint64(b) & 0x7F) << shift
|
|
||||||
if b < 0x80 {
|
|
||||||
p.index = i
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// The number is too large to represent in a 64-bit value.
|
|
||||||
err = errOverflow
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// DecodeVarint reads a varint-encoded integer from the Buffer.
|
|
||||||
// This is the format for the
|
|
||||||
// int32, int64, uint32, uint64, bool, and enum
|
|
||||||
// protocol buffer types.
|
|
||||||
func (p *Buffer) DecodeVarint() (x uint64, err error) {
|
|
||||||
i := p.index
|
|
||||||
buf := p.buf
|
|
||||||
|
|
||||||
if i >= len(buf) {
|
|
||||||
return 0, io.ErrUnexpectedEOF
|
|
||||||
} else if buf[i] < 0x80 {
|
|
||||||
p.index++
|
|
||||||
return uint64(buf[i]), nil
|
|
||||||
} else if len(buf)-i < 10 {
|
|
||||||
return p.decodeVarintSlow()
|
|
||||||
}
|
|
||||||
|
|
||||||
var b uint64
|
|
||||||
// we already checked the first byte
|
|
||||||
x = uint64(buf[i]) - 0x80
|
|
||||||
i++
|
|
||||||
|
|
||||||
b = uint64(buf[i])
|
|
||||||
i++
|
|
||||||
x += b << 7
|
|
||||||
if b&0x80 == 0 {
|
|
||||||
goto done
|
|
||||||
}
|
|
||||||
x -= 0x80 << 7
|
|
||||||
|
|
||||||
b = uint64(buf[i])
|
|
||||||
i++
|
|
||||||
x += b << 14
|
|
||||||
if b&0x80 == 0 {
|
|
||||||
goto done
|
|
||||||
}
|
|
||||||
x -= 0x80 << 14
|
|
||||||
|
|
||||||
b = uint64(buf[i])
|
|
||||||
i++
|
|
||||||
x += b << 21
|
|
||||||
if b&0x80 == 0 {
|
|
||||||
goto done
|
|
||||||
}
|
|
||||||
x -= 0x80 << 21
|
|
||||||
|
|
||||||
b = uint64(buf[i])
|
|
||||||
i++
|
|
||||||
x += b << 28
|
|
||||||
if b&0x80 == 0 {
|
|
||||||
goto done
|
|
||||||
}
|
|
||||||
x -= 0x80 << 28
|
|
||||||
|
|
||||||
b = uint64(buf[i])
|
|
||||||
i++
|
|
||||||
x += b << 35
|
|
||||||
if b&0x80 == 0 {
|
|
||||||
goto done
|
|
||||||
}
|
|
||||||
x -= 0x80 << 35
|
|
||||||
|
|
||||||
b = uint64(buf[i])
|
|
||||||
i++
|
|
||||||
x += b << 42
|
|
||||||
if b&0x80 == 0 {
|
|
||||||
goto done
|
|
||||||
}
|
|
||||||
x -= 0x80 << 42
|
|
||||||
|
|
||||||
b = uint64(buf[i])
|
|
||||||
i++
|
|
||||||
x += b << 49
|
|
||||||
if b&0x80 == 0 {
|
|
||||||
goto done
|
|
||||||
}
|
|
||||||
x -= 0x80 << 49
|
|
||||||
|
|
||||||
b = uint64(buf[i])
|
|
||||||
i++
|
|
||||||
x += b << 56
|
|
||||||
if b&0x80 == 0 {
|
|
||||||
goto done
|
|
||||||
}
|
|
||||||
x -= 0x80 << 56
|
|
||||||
|
|
||||||
b = uint64(buf[i])
|
|
||||||
i++
|
|
||||||
x += b << 63
|
|
||||||
if b&0x80 == 0 {
|
|
||||||
goto done
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0, errOverflow
|
|
||||||
|
|
||||||
done:
|
|
||||||
p.index = i
|
|
||||||
return x, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// DecodeFixed64 reads a 64-bit integer from the Buffer.
|
|
||||||
// This is the format for the
|
|
||||||
// fixed64, sfixed64, and double protocol buffer types.
|
|
||||||
func (p *Buffer) DecodeFixed64() (x uint64, err error) {
|
|
||||||
// x, err already 0
|
|
||||||
i := p.index + 8
|
|
||||||
if i < 0 || i > len(p.buf) {
|
|
||||||
err = io.ErrUnexpectedEOF
|
|
||||||
return
|
|
||||||
}
|
|
||||||
p.index = i
|
|
||||||
|
|
||||||
x = uint64(p.buf[i-8])
|
|
||||||
x |= uint64(p.buf[i-7]) << 8
|
|
||||||
x |= uint64(p.buf[i-6]) << 16
|
|
||||||
x |= uint64(p.buf[i-5]) << 24
|
|
||||||
x |= uint64(p.buf[i-4]) << 32
|
|
||||||
x |= uint64(p.buf[i-3]) << 40
|
|
||||||
x |= uint64(p.buf[i-2]) << 48
|
|
||||||
x |= uint64(p.buf[i-1]) << 56
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// DecodeFixed32 reads a 32-bit integer from the Buffer.
|
|
||||||
// This is the format for the
|
|
||||||
// fixed32, sfixed32, and float protocol buffer types.
|
|
||||||
func (p *Buffer) DecodeFixed32() (x uint64, err error) {
|
|
||||||
// x, err already 0
|
|
||||||
i := p.index + 4
|
|
||||||
if i < 0 || i > len(p.buf) {
|
|
||||||
err = io.ErrUnexpectedEOF
|
|
||||||
return
|
|
||||||
}
|
|
||||||
p.index = i
|
|
||||||
|
|
||||||
x = uint64(p.buf[i-4])
|
|
||||||
x |= uint64(p.buf[i-3]) << 8
|
|
||||||
x |= uint64(p.buf[i-2]) << 16
|
|
||||||
x |= uint64(p.buf[i-1]) << 24
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// DecodeZigzag64 reads a zigzag-encoded 64-bit integer
|
|
||||||
// from the Buffer.
|
|
||||||
// This is the format used for the sint64 protocol buffer type.
|
|
||||||
func (p *Buffer) DecodeZigzag64() (x uint64, err error) {
|
|
||||||
x, err = p.DecodeVarint()
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
x = (x >> 1) ^ uint64((int64(x&1)<<63)>>63)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// DecodeZigzag32 reads a zigzag-encoded 32-bit integer
|
|
||||||
// from the Buffer.
|
|
||||||
// This is the format used for the sint32 protocol buffer type.
|
|
||||||
func (p *Buffer) DecodeZigzag32() (x uint64, err error) {
|
|
||||||
x, err = p.DecodeVarint()
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
x = uint64((uint32(x) >> 1) ^ uint32((int32(x&1)<<31)>>31))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// DecodeRawBytes reads a count-delimited byte buffer from the Buffer.
|
|
||||||
// This is the format used for the bytes protocol buffer
|
|
||||||
// type and for embedded messages.
|
|
||||||
func (p *Buffer) DecodeRawBytes(alloc bool) (buf []byte, err error) {
|
|
||||||
n, err := p.DecodeVarint()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
nb := int(n)
|
|
||||||
if nb < 0 {
|
|
||||||
return nil, fmt.Errorf("proto: bad byte length %d", nb)
|
|
||||||
}
|
|
||||||
end := p.index + nb
|
|
||||||
if end < p.index || end > len(p.buf) {
|
|
||||||
return nil, io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
|
|
||||||
if !alloc {
|
|
||||||
// todo: check if can get more uses of alloc=false
|
|
||||||
buf = p.buf[p.index:end]
|
|
||||||
p.index += nb
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
buf = make([]byte, nb)
|
|
||||||
copy(buf, p.buf[p.index:])
|
|
||||||
p.index += nb
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// DecodeStringBytes reads an encoded string from the Buffer.
|
|
||||||
// This is the format used for the proto2 string type.
|
|
||||||
func (p *Buffer) DecodeStringBytes() (s string, err error) {
|
|
||||||
buf, err := p.DecodeRawBytes(false)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return string(buf), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unmarshaler is the interface representing objects that can
|
|
||||||
// unmarshal themselves. The argument points to data that may be
|
|
||||||
// overwritten, so implementations should not keep references to the
|
|
||||||
// buffer.
|
|
||||||
// Unmarshal implementations should not clear the receiver.
|
|
||||||
// Any unmarshaled data should be merged into the receiver.
|
|
||||||
// Callers of Unmarshal that do not want to retain existing data
|
|
||||||
// should Reset the receiver before calling Unmarshal.
|
|
||||||
type Unmarshaler interface {
|
|
||||||
Unmarshal([]byte) error
|
|
||||||
}
|
|
||||||
|
|
||||||
// newUnmarshaler is the interface representing objects that can
|
|
||||||
// unmarshal themselves. The semantics are identical to Unmarshaler.
|
|
||||||
//
|
|
||||||
// This exists to support protoc-gen-go generated messages.
|
|
||||||
// The proto package will stop type-asserting to this interface in the future.
|
|
||||||
//
|
|
||||||
// DO NOT DEPEND ON THIS.
|
|
||||||
type newUnmarshaler interface {
|
|
||||||
XXX_Unmarshal([]byte) error
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unmarshal parses the protocol buffer representation in buf and places the
|
|
||||||
// decoded result in pb. If the struct underlying pb does not match
|
|
||||||
// the data in buf, the results can be unpredictable.
|
|
||||||
//
|
|
||||||
// Unmarshal resets pb before starting to unmarshal, so any
|
|
||||||
// existing data in pb is always removed. Use UnmarshalMerge
|
|
||||||
// to preserve and append to existing data.
|
|
||||||
func Unmarshal(buf []byte, pb Message) error {
|
|
||||||
pb.Reset()
|
|
||||||
if u, ok := pb.(newUnmarshaler); ok {
|
|
||||||
return u.XXX_Unmarshal(buf)
|
|
||||||
}
|
|
||||||
if u, ok := pb.(Unmarshaler); ok {
|
|
||||||
return u.Unmarshal(buf)
|
|
||||||
}
|
|
||||||
return NewBuffer(buf).Unmarshal(pb)
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalMerge parses the protocol buffer representation in buf and
|
|
||||||
// writes the decoded result to pb. If the struct underlying pb does not match
|
|
||||||
// the data in buf, the results can be unpredictable.
|
|
||||||
//
|
|
||||||
// UnmarshalMerge merges into existing data in pb.
|
|
||||||
// Most code should use Unmarshal instead.
|
|
||||||
func UnmarshalMerge(buf []byte, pb Message) error {
|
|
||||||
if u, ok := pb.(newUnmarshaler); ok {
|
|
||||||
return u.XXX_Unmarshal(buf)
|
|
||||||
}
|
|
||||||
if u, ok := pb.(Unmarshaler); ok {
|
|
||||||
// NOTE: The history of proto have unfortunately been inconsistent
|
|
||||||
// whether Unmarshaler should or should not implicitly clear itself.
|
|
||||||
// Some implementations do, most do not.
|
|
||||||
// Thus, calling this here may or may not do what people want.
|
|
||||||
//
|
|
||||||
// See https://github.com/golang/protobuf/issues/424
|
|
||||||
return u.Unmarshal(buf)
|
|
||||||
}
|
|
||||||
return NewBuffer(buf).Unmarshal(pb)
|
|
||||||
}
|
|
||||||
|
|
||||||
// DecodeMessage reads a count-delimited message from the Buffer.
|
|
||||||
func (p *Buffer) DecodeMessage(pb Message) error {
|
|
||||||
enc, err := p.DecodeRawBytes(false)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return NewBuffer(enc).Unmarshal(pb)
|
|
||||||
}
|
|
||||||
|
|
||||||
// DecodeGroup reads a tag-delimited group from the Buffer.
|
|
||||||
// StartGroup tag is already consumed. This function consumes
|
|
||||||
// EndGroup tag.
|
|
||||||
func (p *Buffer) DecodeGroup(pb Message) error {
|
|
||||||
b := p.buf[p.index:]
|
|
||||||
x, y := findEndGroup(b)
|
|
||||||
if x < 0 {
|
|
||||||
return io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
err := Unmarshal(b[:x], pb)
|
|
||||||
p.index += y
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unmarshal parses the protocol buffer representation in the
|
|
||||||
// Buffer and places the decoded result in pb. If the struct
|
|
||||||
// underlying pb does not match the data in the buffer, the results can be
|
|
||||||
// unpredictable.
|
|
||||||
//
|
|
||||||
// Unlike proto.Unmarshal, this does not reset pb before starting to unmarshal.
|
|
||||||
func (p *Buffer) Unmarshal(pb Message) error {
|
|
||||||
// If the object can unmarshal itself, let it.
|
|
||||||
if u, ok := pb.(newUnmarshaler); ok {
|
|
||||||
err := u.XXX_Unmarshal(p.buf[p.index:])
|
|
||||||
p.index = len(p.buf)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if u, ok := pb.(Unmarshaler); ok {
|
|
||||||
// NOTE: The history of proto have unfortunately been inconsistent
|
|
||||||
// whether Unmarshaler should or should not implicitly clear itself.
|
|
||||||
// Some implementations do, most do not.
|
|
||||||
// Thus, calling this here may or may not do what people want.
|
|
||||||
//
|
|
||||||
// See https://github.com/golang/protobuf/issues/424
|
|
||||||
err := u.Unmarshal(p.buf[p.index:])
|
|
||||||
p.index = len(p.buf)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Slow workaround for messages that aren't Unmarshalers.
|
|
||||||
// This includes some hand-coded .pb.go files and
|
|
||||||
// bootstrap protos.
|
|
||||||
// TODO: fix all of those and then add Unmarshal to
|
|
||||||
// the Message interface. Then:
|
|
||||||
// The cast above and code below can be deleted.
|
|
||||||
// The old unmarshaler can be deleted.
|
|
||||||
// Clients can call Unmarshal directly (can already do that, actually).
|
|
||||||
var info InternalMessageInfo
|
|
||||||
err := info.Unmarshal(pb, p.buf[p.index:])
|
|
||||||
p.index = len(p.buf)
|
|
||||||
return err
|
|
||||||
}
|
|
|
@ -0,0 +1,63 @@
|
||||||
|
// Copyright 2019 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package proto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"google.golang.org/protobuf/reflect/protoreflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SetDefaults sets unpopulated scalar fields to their default values.
|
||||||
|
// Fields within a oneof are not set even if they have a default value.
|
||||||
|
// SetDefaults is recursively called upon any populated message fields.
|
||||||
|
func SetDefaults(m Message) {
|
||||||
|
if m != nil {
|
||||||
|
setDefaults(MessageReflect(m))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func setDefaults(m protoreflect.Message) {
|
||||||
|
fds := m.Descriptor().Fields()
|
||||||
|
for i := 0; i < fds.Len(); i++ {
|
||||||
|
fd := fds.Get(i)
|
||||||
|
if !m.Has(fd) {
|
||||||
|
if fd.HasDefault() && fd.ContainingOneof() == nil {
|
||||||
|
v := fd.Default()
|
||||||
|
if fd.Kind() == protoreflect.BytesKind {
|
||||||
|
v = protoreflect.ValueOf(append([]byte(nil), v.Bytes()...)) // copy the default bytes
|
||||||
|
}
|
||||||
|
m.Set(fd, v)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m.Range(func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool {
|
||||||
|
switch {
|
||||||
|
// Handle singular message.
|
||||||
|
case fd.Cardinality() != protoreflect.Repeated:
|
||||||
|
if fd.Message() != nil {
|
||||||
|
setDefaults(m.Get(fd).Message())
|
||||||
|
}
|
||||||
|
// Handle list of messages.
|
||||||
|
case fd.IsList():
|
||||||
|
if fd.Message() != nil {
|
||||||
|
ls := m.Get(fd).List()
|
||||||
|
for i := 0; i < ls.Len(); i++ {
|
||||||
|
setDefaults(ls.Get(i).Message())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Handle map of messages.
|
||||||
|
case fd.IsMap():
|
||||||
|
if fd.MapValue().Message() != nil {
|
||||||
|
ms := m.Get(fd).Map()
|
||||||
|
ms.Range(func(_ protoreflect.MapKey, v protoreflect.Value) bool {
|
||||||
|
setDefaults(v.Message())
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
}
|
|
@ -1,63 +1,92 @@
|
||||||
// Go support for Protocol Buffers - Google's data interchange format
|
|
||||||
//
|
|
||||||
// Copyright 2018 The Go Authors. All rights reserved.
|
// Copyright 2018 The Go Authors. All rights reserved.
|
||||||
// https://github.com/golang/protobuf
|
// Use of this source code is governed by a BSD-style
|
||||||
//
|
// license that can be found in the LICENSE file.
|
||||||
// Redistribution and use in source and binary forms, with or without
|
|
||||||
// modification, are permitted provided that the following conditions are
|
|
||||||
// met:
|
|
||||||
//
|
|
||||||
// * Redistributions of source code must retain the above copyright
|
|
||||||
// notice, this list of conditions and the following disclaimer.
|
|
||||||
// * Redistributions in binary form must reproduce the above
|
|
||||||
// copyright notice, this list of conditions and the following disclaimer
|
|
||||||
// in the documentation and/or other materials provided with the
|
|
||||||
// distribution.
|
|
||||||
// * Neither the name of Google Inc. nor the names of its
|
|
||||||
// contributors may be used to endorse or promote products derived from
|
|
||||||
// this software without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
package proto
|
package proto
|
||||||
|
|
||||||
import "errors"
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
// Deprecated: do not use.
|
var (
|
||||||
|
// Deprecated: No longer returned.
|
||||||
|
ErrNil = errors.New("proto: Marshal called with nil")
|
||||||
|
|
||||||
|
// Deprecated: No longer returned.
|
||||||
|
ErrTooLarge = errors.New("proto: message encodes to over 2 GB")
|
||||||
|
|
||||||
|
// Deprecated: No longer returned.
|
||||||
|
ErrInternalBadWireType = errors.New("proto: internal error: bad wiretype for oneof")
|
||||||
|
)
|
||||||
|
|
||||||
|
// Deprecated: Do not use.
|
||||||
type Stats struct{ Emalloc, Dmalloc, Encode, Decode, Chit, Cmiss, Size uint64 }
|
type Stats struct{ Emalloc, Dmalloc, Encode, Decode, Chit, Cmiss, Size uint64 }
|
||||||
|
|
||||||
// Deprecated: do not use.
|
// Deprecated: Do not use.
|
||||||
func GetStats() Stats { return Stats{} }
|
func GetStats() Stats { return Stats{} }
|
||||||
|
|
||||||
// Deprecated: do not use.
|
// Deprecated: Do not use.
|
||||||
func MarshalMessageSet(interface{}) ([]byte, error) {
|
func MarshalMessageSet(interface{}) ([]byte, error) {
|
||||||
return nil, errors.New("proto: not implemented")
|
return nil, errors.New("proto: not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deprecated: do not use.
|
// Deprecated: Do not use.
|
||||||
func UnmarshalMessageSet([]byte, interface{}) error {
|
func UnmarshalMessageSet([]byte, interface{}) error {
|
||||||
return errors.New("proto: not implemented")
|
return errors.New("proto: not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deprecated: do not use.
|
// Deprecated: Do not use.
|
||||||
func MarshalMessageSetJSON(interface{}) ([]byte, error) {
|
func MarshalMessageSetJSON(interface{}) ([]byte, error) {
|
||||||
return nil, errors.New("proto: not implemented")
|
return nil, errors.New("proto: not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deprecated: do not use.
|
// Deprecated: Do not use.
|
||||||
func UnmarshalMessageSetJSON([]byte, interface{}) error {
|
func UnmarshalMessageSetJSON([]byte, interface{}) error {
|
||||||
return errors.New("proto: not implemented")
|
return errors.New("proto: not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deprecated: do not use.
|
// Deprecated: Do not use.
|
||||||
func RegisterMessageSetType(Message, int32, string) {}
|
func RegisterMessageSetType(Message, int32, string) {}
|
||||||
|
|
||||||
|
// Deprecated: Do not use.
|
||||||
|
func EnumName(m map[int32]string, v int32) string {
|
||||||
|
s, ok := m[v]
|
||||||
|
if ok {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
return strconv.Itoa(int(v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Do not use.
|
||||||
|
func UnmarshalJSONEnum(m map[string]int32, data []byte, enumName string) (int32, error) {
|
||||||
|
if data[0] == '"' {
|
||||||
|
// New style: enums are strings.
|
||||||
|
var repr string
|
||||||
|
if err := json.Unmarshal(data, &repr); err != nil {
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
val, ok := m[repr]
|
||||||
|
if !ok {
|
||||||
|
return 0, fmt.Errorf("unrecognized enum %s value %q", enumName, repr)
|
||||||
|
}
|
||||||
|
return val, nil
|
||||||
|
}
|
||||||
|
// Old style: enums are ints.
|
||||||
|
var val int32
|
||||||
|
if err := json.Unmarshal(data, &val); err != nil {
|
||||||
|
return 0, fmt.Errorf("cannot unmarshal %#q into enum %s", data, enumName)
|
||||||
|
}
|
||||||
|
return val, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Do not use.
|
||||||
|
type InternalMessageInfo struct{}
|
||||||
|
|
||||||
|
func (*InternalMessageInfo) DiscardUnknown(Message) { panic("not implemented") }
|
||||||
|
func (*InternalMessageInfo) Marshal([]byte, Message, bool) ([]byte, error) { panic("not implemented") }
|
||||||
|
func (*InternalMessageInfo) Merge(Message, Message) { panic("not implemented") }
|
||||||
|
func (*InternalMessageInfo) Size(Message) int { panic("not implemented") }
|
||||||
|
func (*InternalMessageInfo) Unmarshal(Message, []byte) error { panic("not implemented") }
|
||||||
|
|
|
@ -1,48 +1,13 @@
|
||||||
// Go support for Protocol Buffers - Google's data interchange format
|
// Copyright 2019 The Go Authors. All rights reserved.
|
||||||
//
|
// Use of this source code is governed by a BSD-style
|
||||||
// Copyright 2017 The Go Authors. All rights reserved.
|
// license that can be found in the LICENSE file.
|
||||||
// https://github.com/golang/protobuf
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without
|
|
||||||
// modification, are permitted provided that the following conditions are
|
|
||||||
// met:
|
|
||||||
//
|
|
||||||
// * Redistributions of source code must retain the above copyright
|
|
||||||
// notice, this list of conditions and the following disclaimer.
|
|
||||||
// * Redistributions in binary form must reproduce the above
|
|
||||||
// copyright notice, this list of conditions and the following disclaimer
|
|
||||||
// in the documentation and/or other materials provided with the
|
|
||||||
// distribution.
|
|
||||||
// * Neither the name of Google Inc. nor the names of its
|
|
||||||
// contributors may be used to endorse or promote products derived from
|
|
||||||
// this software without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
package proto
|
package proto
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"google.golang.org/protobuf/reflect/protoreflect"
|
||||||
"reflect"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
"sync/atomic"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type generatedDiscarder interface {
|
|
||||||
XXX_DiscardUnknown()
|
|
||||||
}
|
|
||||||
|
|
||||||
// DiscardUnknown recursively discards all unknown fields from this message
|
// DiscardUnknown recursively discards all unknown fields from this message
|
||||||
// and all embedded messages.
|
// and all embedded messages.
|
||||||
//
|
//
|
||||||
|
@ -51,300 +16,43 @@ type generatedDiscarder interface {
|
||||||
// marshal to be able to produce a message that continues to have those
|
// marshal to be able to produce a message that continues to have those
|
||||||
// unrecognized fields. To avoid this, DiscardUnknown is used to
|
// unrecognized fields. To avoid this, DiscardUnknown is used to
|
||||||
// explicitly clear the unknown fields after unmarshaling.
|
// explicitly clear the unknown fields after unmarshaling.
|
||||||
//
|
|
||||||
// For proto2 messages, the unknown fields of message extensions are only
|
|
||||||
// discarded from messages that have been accessed via GetExtension.
|
|
||||||
func DiscardUnknown(m Message) {
|
func DiscardUnknown(m Message) {
|
||||||
if m, ok := m.(generatedDiscarder); ok {
|
if m != nil {
|
||||||
m.XXX_DiscardUnknown()
|
discardUnknown(MessageReflect(m))
|
||||||
return
|
|
||||||
}
|
|
||||||
// TODO: Dynamically populate a InternalMessageInfo for legacy messages,
|
|
||||||
// but the master branch has no implementation for InternalMessageInfo,
|
|
||||||
// so it would be more work to replicate that approach.
|
|
||||||
discardLegacy(m)
|
|
||||||
}
|
|
||||||
|
|
||||||
// DiscardUnknown recursively discards all unknown fields.
|
|
||||||
func (a *InternalMessageInfo) DiscardUnknown(m Message) {
|
|
||||||
di := atomicLoadDiscardInfo(&a.discard)
|
|
||||||
if di == nil {
|
|
||||||
di = getDiscardInfo(reflect.TypeOf(m).Elem())
|
|
||||||
atomicStoreDiscardInfo(&a.discard, di)
|
|
||||||
}
|
|
||||||
di.discard(toPointer(&m))
|
|
||||||
}
|
|
||||||
|
|
||||||
type discardInfo struct {
|
|
||||||
typ reflect.Type
|
|
||||||
|
|
||||||
initialized int32 // 0: only typ is valid, 1: everything is valid
|
|
||||||
lock sync.Mutex
|
|
||||||
|
|
||||||
fields []discardFieldInfo
|
|
||||||
unrecognized field
|
|
||||||
}
|
|
||||||
|
|
||||||
type discardFieldInfo struct {
|
|
||||||
field field // Offset of field, guaranteed to be valid
|
|
||||||
discard func(src pointer)
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
discardInfoMap = map[reflect.Type]*discardInfo{}
|
|
||||||
discardInfoLock sync.Mutex
|
|
||||||
)
|
|
||||||
|
|
||||||
func getDiscardInfo(t reflect.Type) *discardInfo {
|
|
||||||
discardInfoLock.Lock()
|
|
||||||
defer discardInfoLock.Unlock()
|
|
||||||
di := discardInfoMap[t]
|
|
||||||
if di == nil {
|
|
||||||
di = &discardInfo{typ: t}
|
|
||||||
discardInfoMap[t] = di
|
|
||||||
}
|
|
||||||
return di
|
|
||||||
}
|
|
||||||
|
|
||||||
func (di *discardInfo) discard(src pointer) {
|
|
||||||
if src.isNil() {
|
|
||||||
return // Nothing to do.
|
|
||||||
}
|
|
||||||
|
|
||||||
if atomic.LoadInt32(&di.initialized) == 0 {
|
|
||||||
di.computeDiscardInfo()
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, fi := range di.fields {
|
|
||||||
sfp := src.offset(fi.field)
|
|
||||||
fi.discard(sfp)
|
|
||||||
}
|
|
||||||
|
|
||||||
// For proto2 messages, only discard unknown fields in message extensions
|
|
||||||
// that have been accessed via GetExtension.
|
|
||||||
if em, err := extendable(src.asPointerTo(di.typ).Interface()); err == nil {
|
|
||||||
// Ignore lock since DiscardUnknown is not concurrency safe.
|
|
||||||
emm, _ := em.extensionsRead()
|
|
||||||
for _, mx := range emm {
|
|
||||||
if m, ok := mx.value.(Message); ok {
|
|
||||||
DiscardUnknown(m)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if di.unrecognized.IsValid() {
|
|
||||||
*src.offset(di.unrecognized).toBytes() = nil
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (di *discardInfo) computeDiscardInfo() {
|
func discardUnknown(m protoreflect.Message) {
|
||||||
di.lock.Lock()
|
m.Range(func(fd protoreflect.FieldDescriptor, val protoreflect.Value) bool {
|
||||||
defer di.lock.Unlock()
|
|
||||||
if di.initialized != 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
t := di.typ
|
|
||||||
n := t.NumField()
|
|
||||||
|
|
||||||
for i := 0; i < n; i++ {
|
|
||||||
f := t.Field(i)
|
|
||||||
if strings.HasPrefix(f.Name, "XXX_") {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
dfi := discardFieldInfo{field: toField(&f)}
|
|
||||||
tf := f.Type
|
|
||||||
|
|
||||||
// Unwrap tf to get its most basic type.
|
|
||||||
var isPointer, isSlice bool
|
|
||||||
if tf.Kind() == reflect.Slice && tf.Elem().Kind() != reflect.Uint8 {
|
|
||||||
isSlice = true
|
|
||||||
tf = tf.Elem()
|
|
||||||
}
|
|
||||||
if tf.Kind() == reflect.Ptr {
|
|
||||||
isPointer = true
|
|
||||||
tf = tf.Elem()
|
|
||||||
}
|
|
||||||
if isPointer && isSlice && tf.Kind() != reflect.Struct {
|
|
||||||
panic(fmt.Sprintf("%v.%s cannot be a slice of pointers to primitive types", t, f.Name))
|
|
||||||
}
|
|
||||||
|
|
||||||
switch tf.Kind() {
|
|
||||||
case reflect.Struct:
|
|
||||||
switch {
|
switch {
|
||||||
case !isPointer:
|
// Handle singular message.
|
||||||
panic(fmt.Sprintf("%v.%s cannot be a direct struct value", t, f.Name))
|
case fd.Cardinality() != protoreflect.Repeated:
|
||||||
case isSlice: // E.g., []*pb.T
|
if fd.Message() != nil {
|
||||||
di := getDiscardInfo(tf)
|
discardUnknown(m.Get(fd).Message())
|
||||||
dfi.discard = func(src pointer) {
|
}
|
||||||
sps := src.getPointerSlice()
|
// Handle list of messages.
|
||||||
for _, sp := range sps {
|
case fd.IsList():
|
||||||
if !sp.isNil() {
|
if fd.Message() != nil {
|
||||||
di.discard(sp)
|
ls := m.Get(fd).List()
|
||||||
|
for i := 0; i < ls.Len(); i++ {
|
||||||
|
discardUnknown(ls.Get(i).Message())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
// Handle map of messages.
|
||||||
default: // E.g., *pb.T
|
case fd.IsMap():
|
||||||
di := getDiscardInfo(tf)
|
if fd.MapValue().Message() != nil {
|
||||||
dfi.discard = func(src pointer) {
|
ms := m.Get(fd).Map()
|
||||||
sp := src.getPointer()
|
ms.Range(func(_ protoreflect.MapKey, v protoreflect.Value) bool {
|
||||||
if !sp.isNil() {
|
discardUnknown(v.Message())
|
||||||
di.discard(sp)
|
return true
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
return true
|
||||||
case reflect.Map:
|
})
|
||||||
switch {
|
|
||||||
case isPointer || isSlice:
|
|
||||||
panic(fmt.Sprintf("%v.%s cannot be a pointer to a map or a slice of map values", t, f.Name))
|
|
||||||
default: // E.g., map[K]V
|
|
||||||
if tf.Elem().Kind() == reflect.Ptr { // Proto struct (e.g., *T)
|
|
||||||
dfi.discard = func(src pointer) {
|
|
||||||
sm := src.asPointerTo(tf).Elem()
|
|
||||||
if sm.Len() == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for _, key := range sm.MapKeys() {
|
|
||||||
val := sm.MapIndex(key)
|
|
||||||
DiscardUnknown(val.Interface().(Message))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
dfi.discard = func(pointer) {} // Noop
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case reflect.Interface:
|
|
||||||
// Must be oneof field.
|
|
||||||
switch {
|
|
||||||
case isPointer || isSlice:
|
|
||||||
panic(fmt.Sprintf("%v.%s cannot be a pointer to a interface or a slice of interface values", t, f.Name))
|
|
||||||
default: // E.g., interface{}
|
|
||||||
// TODO: Make this faster?
|
|
||||||
dfi.discard = func(src pointer) {
|
|
||||||
su := src.asPointerTo(tf).Elem()
|
|
||||||
if !su.IsNil() {
|
|
||||||
sv := su.Elem().Elem().Field(0)
|
|
||||||
if sv.Kind() == reflect.Ptr && sv.IsNil() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
switch sv.Type().Kind() {
|
|
||||||
case reflect.Ptr: // Proto struct (e.g., *T)
|
|
||||||
DiscardUnknown(sv.Interface().(Message))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
di.fields = append(di.fields, dfi)
|
|
||||||
}
|
|
||||||
|
|
||||||
di.unrecognized = invalidField
|
// Discard unknown fields.
|
||||||
if f, ok := t.FieldByName("XXX_unrecognized"); ok {
|
if len(m.GetUnknown()) > 0 {
|
||||||
if f.Type != reflect.TypeOf([]byte{}) {
|
m.SetUnknown(nil)
|
||||||
panic("expected XXX_unrecognized to be of type []byte")
|
|
||||||
}
|
|
||||||
di.unrecognized = toField(&f)
|
|
||||||
}
|
|
||||||
|
|
||||||
atomic.StoreInt32(&di.initialized, 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func discardLegacy(m Message) {
|
|
||||||
v := reflect.ValueOf(m)
|
|
||||||
if v.Kind() != reflect.Ptr || v.IsNil() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
v = v.Elem()
|
|
||||||
if v.Kind() != reflect.Struct {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
t := v.Type()
|
|
||||||
|
|
||||||
for i := 0; i < v.NumField(); i++ {
|
|
||||||
f := t.Field(i)
|
|
||||||
if strings.HasPrefix(f.Name, "XXX_") {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
vf := v.Field(i)
|
|
||||||
tf := f.Type
|
|
||||||
|
|
||||||
// Unwrap tf to get its most basic type.
|
|
||||||
var isPointer, isSlice bool
|
|
||||||
if tf.Kind() == reflect.Slice && tf.Elem().Kind() != reflect.Uint8 {
|
|
||||||
isSlice = true
|
|
||||||
tf = tf.Elem()
|
|
||||||
}
|
|
||||||
if tf.Kind() == reflect.Ptr {
|
|
||||||
isPointer = true
|
|
||||||
tf = tf.Elem()
|
|
||||||
}
|
|
||||||
if isPointer && isSlice && tf.Kind() != reflect.Struct {
|
|
||||||
panic(fmt.Sprintf("%T.%s cannot be a slice of pointers to primitive types", m, f.Name))
|
|
||||||
}
|
|
||||||
|
|
||||||
switch tf.Kind() {
|
|
||||||
case reflect.Struct:
|
|
||||||
switch {
|
|
||||||
case !isPointer:
|
|
||||||
panic(fmt.Sprintf("%T.%s cannot be a direct struct value", m, f.Name))
|
|
||||||
case isSlice: // E.g., []*pb.T
|
|
||||||
for j := 0; j < vf.Len(); j++ {
|
|
||||||
discardLegacy(vf.Index(j).Interface().(Message))
|
|
||||||
}
|
|
||||||
default: // E.g., *pb.T
|
|
||||||
discardLegacy(vf.Interface().(Message))
|
|
||||||
}
|
|
||||||
case reflect.Map:
|
|
||||||
switch {
|
|
||||||
case isPointer || isSlice:
|
|
||||||
panic(fmt.Sprintf("%T.%s cannot be a pointer to a map or a slice of map values", m, f.Name))
|
|
||||||
default: // E.g., map[K]V
|
|
||||||
tv := vf.Type().Elem()
|
|
||||||
if tv.Kind() == reflect.Ptr && tv.Implements(protoMessageType) { // Proto struct (e.g., *T)
|
|
||||||
for _, key := range vf.MapKeys() {
|
|
||||||
val := vf.MapIndex(key)
|
|
||||||
discardLegacy(val.Interface().(Message))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case reflect.Interface:
|
|
||||||
// Must be oneof field.
|
|
||||||
switch {
|
|
||||||
case isPointer || isSlice:
|
|
||||||
panic(fmt.Sprintf("%T.%s cannot be a pointer to a interface or a slice of interface values", m, f.Name))
|
|
||||||
default: // E.g., test_proto.isCommunique_Union interface
|
|
||||||
if !vf.IsNil() && f.Tag.Get("protobuf_oneof") != "" {
|
|
||||||
vf = vf.Elem() // E.g., *test_proto.Communique_Msg
|
|
||||||
if !vf.IsNil() {
|
|
||||||
vf = vf.Elem() // E.g., test_proto.Communique_Msg
|
|
||||||
vf = vf.Field(0) // E.g., Proto struct (e.g., *T) or primitive value
|
|
||||||
if vf.Kind() == reflect.Ptr {
|
|
||||||
discardLegacy(vf.Interface().(Message))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if vf := v.FieldByName("XXX_unrecognized"); vf.IsValid() {
|
|
||||||
if vf.Type() != reflect.TypeOf([]byte{}) {
|
|
||||||
panic("expected XXX_unrecognized to be of type []byte")
|
|
||||||
}
|
|
||||||
vf.Set(reflect.ValueOf([]byte(nil)))
|
|
||||||
}
|
|
||||||
|
|
||||||
// For proto2 messages, only discard unknown fields in message extensions
|
|
||||||
// that have been accessed via GetExtension.
|
|
||||||
if em, err := extendable(m); err == nil {
|
|
||||||
// Ignore lock since discardLegacy is not concurrency safe.
|
|
||||||
emm, _ := em.extensionsRead()
|
|
||||||
for _, mx := range emm {
|
|
||||||
if m, ok := mx.value.(Message); ok {
|
|
||||||
discardLegacy(m)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,203 +0,0 @@
|
||||||
// Go support for Protocol Buffers - Google's data interchange format
|
|
||||||
//
|
|
||||||
// Copyright 2010 The Go Authors. All rights reserved.
|
|
||||||
// https://github.com/golang/protobuf
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without
|
|
||||||
// modification, are permitted provided that the following conditions are
|
|
||||||
// met:
|
|
||||||
//
|
|
||||||
// * Redistributions of source code must retain the above copyright
|
|
||||||
// notice, this list of conditions and the following disclaimer.
|
|
||||||
// * Redistributions in binary form must reproduce the above
|
|
||||||
// copyright notice, this list of conditions and the following disclaimer
|
|
||||||
// in the documentation and/or other materials provided with the
|
|
||||||
// distribution.
|
|
||||||
// * Neither the name of Google Inc. nor the names of its
|
|
||||||
// contributors may be used to endorse or promote products derived from
|
|
||||||
// this software without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
package proto
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Routines for encoding data into the wire format for protocol buffers.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"reflect"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
// errRepeatedHasNil is the error returned if Marshal is called with
|
|
||||||
// a struct with a repeated field containing a nil element.
|
|
||||||
errRepeatedHasNil = errors.New("proto: repeated field has nil element")
|
|
||||||
|
|
||||||
// errOneofHasNil is the error returned if Marshal is called with
|
|
||||||
// a struct with a oneof field containing a nil element.
|
|
||||||
errOneofHasNil = errors.New("proto: oneof field has nil value")
|
|
||||||
|
|
||||||
// ErrNil is the error returned if Marshal is called with nil.
|
|
||||||
ErrNil = errors.New("proto: Marshal called with nil")
|
|
||||||
|
|
||||||
// ErrTooLarge is the error returned if Marshal is called with a
|
|
||||||
// message that encodes to >2GB.
|
|
||||||
ErrTooLarge = errors.New("proto: message encodes to over 2 GB")
|
|
||||||
)
|
|
||||||
|
|
||||||
// The fundamental encoders that put bytes on the wire.
|
|
||||||
// Those that take integer types all accept uint64 and are
|
|
||||||
// therefore of type valueEncoder.
|
|
||||||
|
|
||||||
const maxVarintBytes = 10 // maximum length of a varint
|
|
||||||
|
|
||||||
// EncodeVarint returns the varint encoding of x.
|
|
||||||
// This is the format for the
|
|
||||||
// int32, int64, uint32, uint64, bool, and enum
|
|
||||||
// protocol buffer types.
|
|
||||||
// Not used by the package itself, but helpful to clients
|
|
||||||
// wishing to use the same encoding.
|
|
||||||
func EncodeVarint(x uint64) []byte {
|
|
||||||
var buf [maxVarintBytes]byte
|
|
||||||
var n int
|
|
||||||
for n = 0; x > 127; n++ {
|
|
||||||
buf[n] = 0x80 | uint8(x&0x7F)
|
|
||||||
x >>= 7
|
|
||||||
}
|
|
||||||
buf[n] = uint8(x)
|
|
||||||
n++
|
|
||||||
return buf[0:n]
|
|
||||||
}
|
|
||||||
|
|
||||||
// EncodeVarint writes a varint-encoded integer to the Buffer.
|
|
||||||
// This is the format for the
|
|
||||||
// int32, int64, uint32, uint64, bool, and enum
|
|
||||||
// protocol buffer types.
|
|
||||||
func (p *Buffer) EncodeVarint(x uint64) error {
|
|
||||||
for x >= 1<<7 {
|
|
||||||
p.buf = append(p.buf, uint8(x&0x7f|0x80))
|
|
||||||
x >>= 7
|
|
||||||
}
|
|
||||||
p.buf = append(p.buf, uint8(x))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SizeVarint returns the varint encoding size of an integer.
|
|
||||||
func SizeVarint(x uint64) int {
|
|
||||||
switch {
|
|
||||||
case x < 1<<7:
|
|
||||||
return 1
|
|
||||||
case x < 1<<14:
|
|
||||||
return 2
|
|
||||||
case x < 1<<21:
|
|
||||||
return 3
|
|
||||||
case x < 1<<28:
|
|
||||||
return 4
|
|
||||||
case x < 1<<35:
|
|
||||||
return 5
|
|
||||||
case x < 1<<42:
|
|
||||||
return 6
|
|
||||||
case x < 1<<49:
|
|
||||||
return 7
|
|
||||||
case x < 1<<56:
|
|
||||||
return 8
|
|
||||||
case x < 1<<63:
|
|
||||||
return 9
|
|
||||||
}
|
|
||||||
return 10
|
|
||||||
}
|
|
||||||
|
|
||||||
// EncodeFixed64 writes a 64-bit integer to the Buffer.
|
|
||||||
// This is the format for the
|
|
||||||
// fixed64, sfixed64, and double protocol buffer types.
|
|
||||||
func (p *Buffer) EncodeFixed64(x uint64) error {
|
|
||||||
p.buf = append(p.buf,
|
|
||||||
uint8(x),
|
|
||||||
uint8(x>>8),
|
|
||||||
uint8(x>>16),
|
|
||||||
uint8(x>>24),
|
|
||||||
uint8(x>>32),
|
|
||||||
uint8(x>>40),
|
|
||||||
uint8(x>>48),
|
|
||||||
uint8(x>>56))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// EncodeFixed32 writes a 32-bit integer to the Buffer.
|
|
||||||
// This is the format for the
|
|
||||||
// fixed32, sfixed32, and float protocol buffer types.
|
|
||||||
func (p *Buffer) EncodeFixed32(x uint64) error {
|
|
||||||
p.buf = append(p.buf,
|
|
||||||
uint8(x),
|
|
||||||
uint8(x>>8),
|
|
||||||
uint8(x>>16),
|
|
||||||
uint8(x>>24))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// EncodeZigzag64 writes a zigzag-encoded 64-bit integer
|
|
||||||
// to the Buffer.
|
|
||||||
// This is the format used for the sint64 protocol buffer type.
|
|
||||||
func (p *Buffer) EncodeZigzag64(x uint64) error {
|
|
||||||
// use signed number to get arithmetic right shift.
|
|
||||||
return p.EncodeVarint(uint64((x << 1) ^ uint64((int64(x) >> 63))))
|
|
||||||
}
|
|
||||||
|
|
||||||
// EncodeZigzag32 writes a zigzag-encoded 32-bit integer
|
|
||||||
// to the Buffer.
|
|
||||||
// This is the format used for the sint32 protocol buffer type.
|
|
||||||
func (p *Buffer) EncodeZigzag32(x uint64) error {
|
|
||||||
// use signed number to get arithmetic right shift.
|
|
||||||
return p.EncodeVarint(uint64((uint32(x) << 1) ^ uint32((int32(x) >> 31))))
|
|
||||||
}
|
|
||||||
|
|
||||||
// EncodeRawBytes writes a count-delimited byte buffer to the Buffer.
|
|
||||||
// This is the format used for the bytes protocol buffer
|
|
||||||
// type and for embedded messages.
|
|
||||||
func (p *Buffer) EncodeRawBytes(b []byte) error {
|
|
||||||
p.EncodeVarint(uint64(len(b)))
|
|
||||||
p.buf = append(p.buf, b...)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// EncodeStringBytes writes an encoded string to the Buffer.
|
|
||||||
// This is the format used for the proto2 string type.
|
|
||||||
func (p *Buffer) EncodeStringBytes(s string) error {
|
|
||||||
p.EncodeVarint(uint64(len(s)))
|
|
||||||
p.buf = append(p.buf, s...)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Marshaler is the interface representing objects that can marshal themselves.
|
|
||||||
type Marshaler interface {
|
|
||||||
Marshal() ([]byte, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// EncodeMessage writes the protocol buffer to the Buffer,
|
|
||||||
// prefixed by a varint-encoded length.
|
|
||||||
func (p *Buffer) EncodeMessage(pb Message) error {
|
|
||||||
siz := Size(pb)
|
|
||||||
p.EncodeVarint(uint64(siz))
|
|
||||||
return p.Marshal(pb)
|
|
||||||
}
|
|
||||||
|
|
||||||
// All protocol buffer fields are nillable, but be careful.
|
|
||||||
func isNil(v reflect.Value) bool {
|
|
||||||
switch v.Kind() {
|
|
||||||
case reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
|
|
||||||
return v.IsNil()
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
|
@ -1,301 +0,0 @@
|
||||||
// Go support for Protocol Buffers - Google's data interchange format
|
|
||||||
//
|
|
||||||
// Copyright 2011 The Go Authors. All rights reserved.
|
|
||||||
// https://github.com/golang/protobuf
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without
|
|
||||||
// modification, are permitted provided that the following conditions are
|
|
||||||
// met:
|
|
||||||
//
|
|
||||||
// * Redistributions of source code must retain the above copyright
|
|
||||||
// notice, this list of conditions and the following disclaimer.
|
|
||||||
// * Redistributions in binary form must reproduce the above
|
|
||||||
// copyright notice, this list of conditions and the following disclaimer
|
|
||||||
// in the documentation and/or other materials provided with the
|
|
||||||
// distribution.
|
|
||||||
// * Neither the name of Google Inc. nor the names of its
|
|
||||||
// contributors may be used to endorse or promote products derived from
|
|
||||||
// this software without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
// Protocol buffer comparison.
|
|
||||||
|
|
||||||
package proto
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"log"
|
|
||||||
"reflect"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
/*
|
|
||||||
Equal returns true iff protocol buffers a and b are equal.
|
|
||||||
The arguments must both be pointers to protocol buffer structs.
|
|
||||||
|
|
||||||
Equality is defined in this way:
|
|
||||||
- Two messages are equal iff they are the same type,
|
|
||||||
corresponding fields are equal, unknown field sets
|
|
||||||
are equal, and extensions sets are equal.
|
|
||||||
- Two set scalar fields are equal iff their values are equal.
|
|
||||||
If the fields are of a floating-point type, remember that
|
|
||||||
NaN != x for all x, including NaN. If the message is defined
|
|
||||||
in a proto3 .proto file, fields are not "set"; specifically,
|
|
||||||
zero length proto3 "bytes" fields are equal (nil == {}).
|
|
||||||
- Two repeated fields are equal iff their lengths are the same,
|
|
||||||
and their corresponding elements are equal. Note a "bytes" field,
|
|
||||||
although represented by []byte, is not a repeated field and the
|
|
||||||
rule for the scalar fields described above applies.
|
|
||||||
- Two unset fields are equal.
|
|
||||||
- Two unknown field sets are equal if their current
|
|
||||||
encoded state is equal.
|
|
||||||
- Two extension sets are equal iff they have corresponding
|
|
||||||
elements that are pairwise equal.
|
|
||||||
- Two map fields are equal iff their lengths are the same,
|
|
||||||
and they contain the same set of elements. Zero-length map
|
|
||||||
fields are equal.
|
|
||||||
- Every other combination of things are not equal.
|
|
||||||
|
|
||||||
The return value is undefined if a and b are not protocol buffers.
|
|
||||||
*/
|
|
||||||
func Equal(a, b Message) bool {
|
|
||||||
if a == nil || b == nil {
|
|
||||||
return a == b
|
|
||||||
}
|
|
||||||
v1, v2 := reflect.ValueOf(a), reflect.ValueOf(b)
|
|
||||||
if v1.Type() != v2.Type() {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if v1.Kind() == reflect.Ptr {
|
|
||||||
if v1.IsNil() {
|
|
||||||
return v2.IsNil()
|
|
||||||
}
|
|
||||||
if v2.IsNil() {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
v1, v2 = v1.Elem(), v2.Elem()
|
|
||||||
}
|
|
||||||
if v1.Kind() != reflect.Struct {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return equalStruct(v1, v2)
|
|
||||||
}
|
|
||||||
|
|
||||||
// v1 and v2 are known to have the same type.
|
|
||||||
func equalStruct(v1, v2 reflect.Value) bool {
|
|
||||||
sprop := GetProperties(v1.Type())
|
|
||||||
for i := 0; i < v1.NumField(); i++ {
|
|
||||||
f := v1.Type().Field(i)
|
|
||||||
if strings.HasPrefix(f.Name, "XXX_") {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
f1, f2 := v1.Field(i), v2.Field(i)
|
|
||||||
if f.Type.Kind() == reflect.Ptr {
|
|
||||||
if n1, n2 := f1.IsNil(), f2.IsNil(); n1 && n2 {
|
|
||||||
// both unset
|
|
||||||
continue
|
|
||||||
} else if n1 != n2 {
|
|
||||||
// set/unset mismatch
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
f1, f2 = f1.Elem(), f2.Elem()
|
|
||||||
}
|
|
||||||
if !equalAny(f1, f2, sprop.Prop[i]) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if em1 := v1.FieldByName("XXX_InternalExtensions"); em1.IsValid() {
|
|
||||||
em2 := v2.FieldByName("XXX_InternalExtensions")
|
|
||||||
if !equalExtensions(v1.Type(), em1.Interface().(XXX_InternalExtensions), em2.Interface().(XXX_InternalExtensions)) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if em1 := v1.FieldByName("XXX_extensions"); em1.IsValid() {
|
|
||||||
em2 := v2.FieldByName("XXX_extensions")
|
|
||||||
if !equalExtMap(v1.Type(), em1.Interface().(map[int32]Extension), em2.Interface().(map[int32]Extension)) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uf := v1.FieldByName("XXX_unrecognized")
|
|
||||||
if !uf.IsValid() {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
u1 := uf.Bytes()
|
|
||||||
u2 := v2.FieldByName("XXX_unrecognized").Bytes()
|
|
||||||
return bytes.Equal(u1, u2)
|
|
||||||
}
|
|
||||||
|
|
||||||
// v1 and v2 are known to have the same type.
|
|
||||||
// prop may be nil.
|
|
||||||
func equalAny(v1, v2 reflect.Value, prop *Properties) bool {
|
|
||||||
if v1.Type() == protoMessageType {
|
|
||||||
m1, _ := v1.Interface().(Message)
|
|
||||||
m2, _ := v2.Interface().(Message)
|
|
||||||
return Equal(m1, m2)
|
|
||||||
}
|
|
||||||
switch v1.Kind() {
|
|
||||||
case reflect.Bool:
|
|
||||||
return v1.Bool() == v2.Bool()
|
|
||||||
case reflect.Float32, reflect.Float64:
|
|
||||||
return v1.Float() == v2.Float()
|
|
||||||
case reflect.Int32, reflect.Int64:
|
|
||||||
return v1.Int() == v2.Int()
|
|
||||||
case reflect.Interface:
|
|
||||||
// Probably a oneof field; compare the inner values.
|
|
||||||
n1, n2 := v1.IsNil(), v2.IsNil()
|
|
||||||
if n1 || n2 {
|
|
||||||
return n1 == n2
|
|
||||||
}
|
|
||||||
e1, e2 := v1.Elem(), v2.Elem()
|
|
||||||
if e1.Type() != e2.Type() {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return equalAny(e1, e2, nil)
|
|
||||||
case reflect.Map:
|
|
||||||
if v1.Len() != v2.Len() {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
for _, key := range v1.MapKeys() {
|
|
||||||
val2 := v2.MapIndex(key)
|
|
||||||
if !val2.IsValid() {
|
|
||||||
// This key was not found in the second map.
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if !equalAny(v1.MapIndex(key), val2, nil) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
case reflect.Ptr:
|
|
||||||
// Maps may have nil values in them, so check for nil.
|
|
||||||
if v1.IsNil() && v2.IsNil() {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if v1.IsNil() != v2.IsNil() {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return equalAny(v1.Elem(), v2.Elem(), prop)
|
|
||||||
case reflect.Slice:
|
|
||||||
if v1.Type().Elem().Kind() == reflect.Uint8 {
|
|
||||||
// short circuit: []byte
|
|
||||||
|
|
||||||
// Edge case: if this is in a proto3 message, a zero length
|
|
||||||
// bytes field is considered the zero value.
|
|
||||||
if prop != nil && prop.proto3 && v1.Len() == 0 && v2.Len() == 0 {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if v1.IsNil() != v2.IsNil() {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return bytes.Equal(v1.Interface().([]byte), v2.Interface().([]byte))
|
|
||||||
}
|
|
||||||
|
|
||||||
if v1.Len() != v2.Len() {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
for i := 0; i < v1.Len(); i++ {
|
|
||||||
if !equalAny(v1.Index(i), v2.Index(i), prop) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
case reflect.String:
|
|
||||||
return v1.Interface().(string) == v2.Interface().(string)
|
|
||||||
case reflect.Struct:
|
|
||||||
return equalStruct(v1, v2)
|
|
||||||
case reflect.Uint32, reflect.Uint64:
|
|
||||||
return v1.Uint() == v2.Uint()
|
|
||||||
}
|
|
||||||
|
|
||||||
// unknown type, so not a protocol buffer
|
|
||||||
log.Printf("proto: don't know how to compare %v", v1)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// base is the struct type that the extensions are based on.
|
|
||||||
// x1 and x2 are InternalExtensions.
|
|
||||||
func equalExtensions(base reflect.Type, x1, x2 XXX_InternalExtensions) bool {
|
|
||||||
em1, _ := x1.extensionsRead()
|
|
||||||
em2, _ := x2.extensionsRead()
|
|
||||||
return equalExtMap(base, em1, em2)
|
|
||||||
}
|
|
||||||
|
|
||||||
func equalExtMap(base reflect.Type, em1, em2 map[int32]Extension) bool {
|
|
||||||
if len(em1) != len(em2) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
for extNum, e1 := range em1 {
|
|
||||||
e2, ok := em2[extNum]
|
|
||||||
if !ok {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
m1 := extensionAsLegacyType(e1.value)
|
|
||||||
m2 := extensionAsLegacyType(e2.value)
|
|
||||||
|
|
||||||
if m1 == nil && m2 == nil {
|
|
||||||
// Both have only encoded form.
|
|
||||||
if bytes.Equal(e1.enc, e2.enc) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// The bytes are different, but the extensions might still be
|
|
||||||
// equal. We need to decode them to compare.
|
|
||||||
}
|
|
||||||
|
|
||||||
if m1 != nil && m2 != nil {
|
|
||||||
// Both are unencoded.
|
|
||||||
if !equalAny(reflect.ValueOf(m1), reflect.ValueOf(m2), nil) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// At least one is encoded. To do a semantically correct comparison
|
|
||||||
// we need to unmarshal them first.
|
|
||||||
var desc *ExtensionDesc
|
|
||||||
if m := extensionMaps[base]; m != nil {
|
|
||||||
desc = m[extNum]
|
|
||||||
}
|
|
||||||
if desc == nil {
|
|
||||||
// If both have only encoded form and the bytes are the same,
|
|
||||||
// it is handled above. We get here when the bytes are different.
|
|
||||||
// We don't know how to decode it, so just compare them as byte
|
|
||||||
// slices.
|
|
||||||
log.Printf("proto: don't know how to compare extension %d of %v", extNum, base)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
var err error
|
|
||||||
if m1 == nil {
|
|
||||||
m1, err = decodeExtension(e1.enc, desc)
|
|
||||||
}
|
|
||||||
if m2 == nil && err == nil {
|
|
||||||
m2, err = decodeExtension(e2.enc, desc)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
// The encoded form is invalid.
|
|
||||||
log.Printf("proto: badly encoded extension %d of %v: %v", extNum, base, err)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if !equalAny(reflect.ValueOf(m1), reflect.ValueOf(m2), nil) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
|
@ -1,310 +1,111 @@
|
||||||
// Go support for Protocol Buffers - Google's data interchange format
|
|
||||||
//
|
|
||||||
// Copyright 2010 The Go Authors. All rights reserved.
|
// Copyright 2010 The Go Authors. All rights reserved.
|
||||||
// https://github.com/golang/protobuf
|
// Use of this source code is governed by a BSD-style
|
||||||
//
|
// license that can be found in the LICENSE file.
|
||||||
// Redistribution and use in source and binary forms, with or without
|
|
||||||
// modification, are permitted provided that the following conditions are
|
|
||||||
// met:
|
|
||||||
//
|
|
||||||
// * Redistributions of source code must retain the above copyright
|
|
||||||
// notice, this list of conditions and the following disclaimer.
|
|
||||||
// * Redistributions in binary form must reproduce the above
|
|
||||||
// copyright notice, this list of conditions and the following disclaimer
|
|
||||||
// in the documentation and/or other materials provided with the
|
|
||||||
// distribution.
|
|
||||||
// * Neither the name of Google Inc. nor the names of its
|
|
||||||
// contributors may be used to endorse or promote products derived from
|
|
||||||
// this software without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
package proto
|
package proto
|
||||||
|
|
||||||
/*
|
|
||||||
* Types and routines for supporting protocol buffer extensions.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"reflect"
|
"reflect"
|
||||||
"strconv"
|
|
||||||
"sync"
|
"google.golang.org/protobuf/encoding/protowire"
|
||||||
|
"google.golang.org/protobuf/proto"
|
||||||
|
"google.golang.org/protobuf/reflect/protoreflect"
|
||||||
|
"google.golang.org/protobuf/reflect/protoregistry"
|
||||||
|
"google.golang.org/protobuf/runtime/protoiface"
|
||||||
|
"google.golang.org/protobuf/runtime/protoimpl"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ErrMissingExtension is the error returned by GetExtension if the named extension is not in the message.
|
type (
|
||||||
|
// ExtensionDesc represents an extension descriptor and
|
||||||
|
// is used to interact with an extension field in a message.
|
||||||
|
//
|
||||||
|
// Variables of this type are generated in code by protoc-gen-go.
|
||||||
|
ExtensionDesc = protoimpl.ExtensionInfo
|
||||||
|
|
||||||
|
// ExtensionRange represents a range of message extensions.
|
||||||
|
// Used in code generated by protoc-gen-go.
|
||||||
|
ExtensionRange = protoiface.ExtensionRangeV1
|
||||||
|
|
||||||
|
// Deprecated: Do not use; this is an internal type.
|
||||||
|
Extension = protoimpl.ExtensionFieldV1
|
||||||
|
|
||||||
|
// Deprecated: Do not use; this is an internal type.
|
||||||
|
XXX_InternalExtensions = protoimpl.ExtensionFields
|
||||||
|
)
|
||||||
|
|
||||||
|
// ErrMissingExtension reports whether the extension was not present.
|
||||||
var ErrMissingExtension = errors.New("proto: missing extension")
|
var ErrMissingExtension = errors.New("proto: missing extension")
|
||||||
|
|
||||||
// ExtensionRange represents a range of message extensions for a protocol buffer.
|
|
||||||
// Used in code generated by the protocol compiler.
|
|
||||||
type ExtensionRange struct {
|
|
||||||
Start, End int32 // both inclusive
|
|
||||||
}
|
|
||||||
|
|
||||||
// extendableProto is an interface implemented by any protocol buffer generated by the current
|
|
||||||
// proto compiler that may be extended.
|
|
||||||
type extendableProto interface {
|
|
||||||
Message
|
|
||||||
ExtensionRangeArray() []ExtensionRange
|
|
||||||
extensionsWrite() map[int32]Extension
|
|
||||||
extensionsRead() (map[int32]Extension, sync.Locker)
|
|
||||||
}
|
|
||||||
|
|
||||||
// extendableProtoV1 is an interface implemented by a protocol buffer generated by the previous
|
|
||||||
// version of the proto compiler that may be extended.
|
|
||||||
type extendableProtoV1 interface {
|
|
||||||
Message
|
|
||||||
ExtensionRangeArray() []ExtensionRange
|
|
||||||
ExtensionMap() map[int32]Extension
|
|
||||||
}
|
|
||||||
|
|
||||||
// extensionAdapter is a wrapper around extendableProtoV1 that implements extendableProto.
|
|
||||||
type extensionAdapter struct {
|
|
||||||
extendableProtoV1
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e extensionAdapter) extensionsWrite() map[int32]Extension {
|
|
||||||
return e.ExtensionMap()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e extensionAdapter) extensionsRead() (map[int32]Extension, sync.Locker) {
|
|
||||||
return e.ExtensionMap(), notLocker{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// notLocker is a sync.Locker whose Lock and Unlock methods are nops.
|
|
||||||
type notLocker struct{}
|
|
||||||
|
|
||||||
func (n notLocker) Lock() {}
|
|
||||||
func (n notLocker) Unlock() {}
|
|
||||||
|
|
||||||
// extendable returns the extendableProto interface for the given generated proto message.
|
|
||||||
// If the proto message has the old extension format, it returns a wrapper that implements
|
|
||||||
// the extendableProto interface.
|
|
||||||
func extendable(p interface{}) (extendableProto, error) {
|
|
||||||
switch p := p.(type) {
|
|
||||||
case extendableProto:
|
|
||||||
if isNilPtr(p) {
|
|
||||||
return nil, fmt.Errorf("proto: nil %T is not extendable", p)
|
|
||||||
}
|
|
||||||
return p, nil
|
|
||||||
case extendableProtoV1:
|
|
||||||
if isNilPtr(p) {
|
|
||||||
return nil, fmt.Errorf("proto: nil %T is not extendable", p)
|
|
||||||
}
|
|
||||||
return extensionAdapter{p}, nil
|
|
||||||
}
|
|
||||||
// Don't allocate a specific error containing %T:
|
|
||||||
// this is the hot path for Clone and MarshalText.
|
|
||||||
return nil, errNotExtendable
|
|
||||||
}
|
|
||||||
|
|
||||||
var errNotExtendable = errors.New("proto: not an extendable proto.Message")
|
var errNotExtendable = errors.New("proto: not an extendable proto.Message")
|
||||||
|
|
||||||
func isNilPtr(x interface{}) bool {
|
// HasExtension reports whether the extension field is present in m
|
||||||
v := reflect.ValueOf(x)
|
// either as an explicitly populated field or as an unknown field.
|
||||||
return v.Kind() == reflect.Ptr && v.IsNil()
|
func HasExtension(m Message, xt *ExtensionDesc) (has bool) {
|
||||||
}
|
mr := MessageReflect(m)
|
||||||
|
if mr == nil || !mr.IsValid() {
|
||||||
// XXX_InternalExtensions is an internal representation of proto extensions.
|
return false
|
||||||
//
|
|
||||||
// Each generated message struct type embeds an anonymous XXX_InternalExtensions field,
|
|
||||||
// thus gaining the unexported 'extensions' method, which can be called only from the proto package.
|
|
||||||
//
|
|
||||||
// The methods of XXX_InternalExtensions are not concurrency safe in general,
|
|
||||||
// but calls to logically read-only methods such as has and get may be executed concurrently.
|
|
||||||
type XXX_InternalExtensions struct {
|
|
||||||
// The struct must be indirect so that if a user inadvertently copies a
|
|
||||||
// generated message and its embedded XXX_InternalExtensions, they
|
|
||||||
// avoid the mayhem of a copied mutex.
|
|
||||||
//
|
|
||||||
// The mutex serializes all logically read-only operations to p.extensionMap.
|
|
||||||
// It is up to the client to ensure that write operations to p.extensionMap are
|
|
||||||
// mutually exclusive with other accesses.
|
|
||||||
p *struct {
|
|
||||||
mu sync.Mutex
|
|
||||||
extensionMap map[int32]Extension
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// extensionsWrite returns the extension map, creating it on first use.
|
// Check whether any populated known field matches the field number.
|
||||||
func (e *XXX_InternalExtensions) extensionsWrite() map[int32]Extension {
|
xtd := xt.TypeDescriptor()
|
||||||
if e.p == nil {
|
if isValidExtension(mr.Descriptor(), xtd) {
|
||||||
e.p = new(struct {
|
has = mr.Has(xtd)
|
||||||
mu sync.Mutex
|
} else {
|
||||||
extensionMap map[int32]Extension
|
mr.Range(func(fd protoreflect.FieldDescriptor, _ protoreflect.Value) bool {
|
||||||
|
has = int32(fd.Number()) == xt.Field
|
||||||
|
return !has
|
||||||
})
|
})
|
||||||
e.p.extensionMap = make(map[int32]Extension)
|
|
||||||
}
|
}
|
||||||
return e.p.extensionMap
|
|
||||||
}
|
|
||||||
|
|
||||||
// extensionsRead returns the extensions map for read-only use. It may be nil.
|
// Check whether any unknown field matches the field number.
|
||||||
// The caller must hold the returned mutex's lock when accessing Elements within the map.
|
for b := mr.GetUnknown(); !has && len(b) > 0; {
|
||||||
func (e *XXX_InternalExtensions) extensionsRead() (map[int32]Extension, sync.Locker) {
|
num, _, n := protowire.ConsumeField(b)
|
||||||
if e.p == nil {
|
has = int32(num) == xt.Field
|
||||||
return nil, nil
|
b = b[n:]
|
||||||
}
|
}
|
||||||
return e.p.extensionMap, &e.p.mu
|
return has
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExtensionDesc represents an extension specification.
|
// ClearExtension removes the the exntesion field from m
|
||||||
// Used in generated code from the protocol compiler.
|
// either as an explicitly populated field or as an unknown field.
|
||||||
type ExtensionDesc struct {
|
func ClearExtension(m Message, xt *ExtensionDesc) {
|
||||||
ExtendedType Message // nil pointer to the type that is being extended
|
mr := MessageReflect(m)
|
||||||
ExtensionType interface{} // nil pointer to the extension type
|
if mr == nil || !mr.IsValid() {
|
||||||
Field int32 // field number
|
|
||||||
Name string // fully-qualified name of extension, for text formatting
|
|
||||||
Tag string // protobuf tag style
|
|
||||||
Filename string // name of the file in which the extension is defined
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ed *ExtensionDesc) repeated() bool {
|
|
||||||
t := reflect.TypeOf(ed.ExtensionType)
|
|
||||||
return t.Kind() == reflect.Slice && t.Elem().Kind() != reflect.Uint8
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extension represents an extension in a message.
|
|
||||||
type Extension struct {
|
|
||||||
// When an extension is stored in a message using SetExtension
|
|
||||||
// only desc and value are set. When the message is marshaled
|
|
||||||
// enc will be set to the encoded form of the message.
|
|
||||||
//
|
|
||||||
// When a message is unmarshaled and contains extensions, each
|
|
||||||
// extension will have only enc set. When such an extension is
|
|
||||||
// accessed using GetExtension (or GetExtensions) desc and value
|
|
||||||
// will be set.
|
|
||||||
desc *ExtensionDesc
|
|
||||||
|
|
||||||
// value is a concrete value for the extension field. Let the type of
|
|
||||||
// desc.ExtensionType be the "API type" and the type of Extension.value
|
|
||||||
// be the "storage type". The API type and storage type are the same except:
|
|
||||||
// * For scalars (except []byte), the API type uses *T,
|
|
||||||
// while the storage type uses T.
|
|
||||||
// * For repeated fields, the API type uses []T, while the storage type
|
|
||||||
// uses *[]T.
|
|
||||||
//
|
|
||||||
// The reason for the divergence is so that the storage type more naturally
|
|
||||||
// matches what is expected of when retrieving the values through the
|
|
||||||
// protobuf reflection APIs.
|
|
||||||
//
|
|
||||||
// The value may only be populated if desc is also populated.
|
|
||||||
value interface{}
|
|
||||||
|
|
||||||
// enc is the raw bytes for the extension field.
|
|
||||||
enc []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetRawExtension is for testing only.
|
|
||||||
func SetRawExtension(base Message, id int32, b []byte) {
|
|
||||||
epb, err := extendable(base)
|
|
||||||
if err != nil {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
extmap := epb.extensionsWrite()
|
|
||||||
extmap[id] = Extension{enc: b}
|
|
||||||
}
|
|
||||||
|
|
||||||
// isExtensionField returns true iff the given field number is in an extension range.
|
xtd := xt.TypeDescriptor()
|
||||||
func isExtensionField(pb extendableProto, field int32) bool {
|
if isValidExtension(mr.Descriptor(), xtd) {
|
||||||
for _, er := range pb.ExtensionRangeArray() {
|
mr.Clear(xtd)
|
||||||
if er.Start <= field && field <= er.End {
|
} else {
|
||||||
|
mr.Range(func(fd protoreflect.FieldDescriptor, _ protoreflect.Value) bool {
|
||||||
|
if int32(fd.Number()) == xt.Field {
|
||||||
|
mr.Clear(fd)
|
||||||
|
return false
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
clearUnknown(mr, fieldNum(xt.Field))
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// checkExtensionTypes checks that the given extension is valid for pb.
|
// ClearAllExtensions clears all extensions from m.
|
||||||
func checkExtensionTypes(pb extendableProto, extension *ExtensionDesc) error {
|
// This includes populated fields and unknown fields in the extension range.
|
||||||
var pbi interface{} = pb
|
func ClearAllExtensions(m Message) {
|
||||||
// Check the extended type.
|
mr := MessageReflect(m)
|
||||||
if ea, ok := pbi.(extensionAdapter); ok {
|
if mr == nil || !mr.IsValid() {
|
||||||
pbi = ea.extendableProtoV1
|
|
||||||
}
|
|
||||||
if a, b := reflect.TypeOf(pbi), reflect.TypeOf(extension.ExtendedType); a != b {
|
|
||||||
return fmt.Errorf("proto: bad extended type; %v does not extend %v", b, a)
|
|
||||||
}
|
|
||||||
// Check the range.
|
|
||||||
if !isExtensionField(pb, extension.Field) {
|
|
||||||
return errors.New("proto: bad extension number; not in declared ranges")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// extPropKey is sufficient to uniquely identify an extension.
|
|
||||||
type extPropKey struct {
|
|
||||||
base reflect.Type
|
|
||||||
field int32
|
|
||||||
}
|
|
||||||
|
|
||||||
var extProp = struct {
|
|
||||||
sync.RWMutex
|
|
||||||
m map[extPropKey]*Properties
|
|
||||||
}{
|
|
||||||
m: make(map[extPropKey]*Properties),
|
|
||||||
}
|
|
||||||
|
|
||||||
func extensionProperties(ed *ExtensionDesc) *Properties {
|
|
||||||
key := extPropKey{base: reflect.TypeOf(ed.ExtendedType), field: ed.Field}
|
|
||||||
|
|
||||||
extProp.RLock()
|
|
||||||
if prop, ok := extProp.m[key]; ok {
|
|
||||||
extProp.RUnlock()
|
|
||||||
return prop
|
|
||||||
}
|
|
||||||
extProp.RUnlock()
|
|
||||||
|
|
||||||
extProp.Lock()
|
|
||||||
defer extProp.Unlock()
|
|
||||||
// Check again.
|
|
||||||
if prop, ok := extProp.m[key]; ok {
|
|
||||||
return prop
|
|
||||||
}
|
|
||||||
|
|
||||||
prop := new(Properties)
|
|
||||||
prop.Init(reflect.TypeOf(ed.ExtensionType), "unknown_name", ed.Tag, nil)
|
|
||||||
extProp.m[key] = prop
|
|
||||||
return prop
|
|
||||||
}
|
|
||||||
|
|
||||||
// HasExtension returns whether the given extension is present in pb.
|
|
||||||
func HasExtension(pb Message, extension *ExtensionDesc) bool {
|
|
||||||
// TODO: Check types, field numbers, etc.?
|
|
||||||
epb, err := extendable(pb)
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
extmap, mu := epb.extensionsRead()
|
|
||||||
if extmap == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
mu.Lock()
|
|
||||||
_, ok := extmap[extension.Field]
|
|
||||||
mu.Unlock()
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
// ClearExtension removes the given extension from pb.
|
|
||||||
func ClearExtension(pb Message, extension *ExtensionDesc) {
|
|
||||||
epb, err := extendable(pb)
|
|
||||||
if err != nil {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// TODO: Check types, field numbers, etc.?
|
|
||||||
extmap := epb.extensionsWrite()
|
mr.Range(func(fd protoreflect.FieldDescriptor, _ protoreflect.Value) bool {
|
||||||
delete(extmap, extension.Field)
|
if fd.IsExtension() {
|
||||||
|
mr.Clear(fd)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
clearUnknown(mr, mr.Descriptor().ExtensionRanges())
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetExtension retrieves a proto2 extended field from pb.
|
// GetExtension retrieves a proto2 extended field from pb.
|
||||||
|
@ -314,294 +115,242 @@ func ClearExtension(pb Message, extension *ExtensionDesc) {
|
||||||
// If the field is not present, then the default value is returned (if one is specified),
|
// If the field is not present, then the default value is returned (if one is specified),
|
||||||
// otherwise ErrMissingExtension is reported.
|
// otherwise ErrMissingExtension is reported.
|
||||||
//
|
//
|
||||||
// If the descriptor is not type complete (i.e., ExtensionDesc.ExtensionType is nil),
|
// If the descriptor is type incomplete (i.e., ExtensionDesc.ExtensionType is nil),
|
||||||
// then GetExtension returns the raw encoded bytes of the field extension.
|
// then GetExtension returns the raw encoded bytes for the extension field.
|
||||||
func GetExtension(pb Message, extension *ExtensionDesc) (interface{}, error) {
|
func GetExtension(m Message, xt *ExtensionDesc) (interface{}, error) {
|
||||||
epb, err := extendable(pb)
|
mr := MessageReflect(m)
|
||||||
if err != nil {
|
if mr == nil || !mr.IsValid() || mr.Descriptor().ExtensionRanges().Len() == 0 {
|
||||||
|
return nil, errNotExtendable
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieve the unknown fields for this extension field.
|
||||||
|
var bo protoreflect.RawFields
|
||||||
|
for bi := mr.GetUnknown(); len(bi) > 0; {
|
||||||
|
num, _, n := protowire.ConsumeField(bi)
|
||||||
|
if int32(num) == xt.Field {
|
||||||
|
bo = append(bo, bi[:n]...)
|
||||||
|
}
|
||||||
|
bi = bi[n:]
|
||||||
|
}
|
||||||
|
|
||||||
|
// For type incomplete descriptors, only retrieve the unknown fields.
|
||||||
|
if xt.ExtensionType == nil {
|
||||||
|
return []byte(bo), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the extension field only exists as unknown fields, unmarshal it.
|
||||||
|
// This is rarely done since proto.Unmarshal eagerly unmarshals extensions.
|
||||||
|
xtd := xt.TypeDescriptor()
|
||||||
|
if !isValidExtension(mr.Descriptor(), xtd) {
|
||||||
|
return nil, fmt.Errorf("proto: bad extended type; %T does not extend %T", xt.ExtendedType, m)
|
||||||
|
}
|
||||||
|
if !mr.Has(xtd) && len(bo) > 0 {
|
||||||
|
m2 := mr.New()
|
||||||
|
if err := (proto.UnmarshalOptions{
|
||||||
|
Resolver: extensionResolver{xt},
|
||||||
|
}.Unmarshal(bo, m2.Interface())); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if m2.Has(xtd) {
|
||||||
if extension.ExtendedType != nil {
|
mr.Set(xtd, m2.Get(xtd))
|
||||||
// can only check type if this is a complete descriptor
|
clearUnknown(mr, fieldNum(xt.Field))
|
||||||
if err := checkExtensionTypes(epb, extension); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
emap, mu := epb.extensionsRead()
|
// Check whether the message has the extension field set or a default.
|
||||||
if emap == nil {
|
var pv protoreflect.Value
|
||||||
return defaultExtensionValue(extension)
|
switch {
|
||||||
}
|
case mr.Has(xtd):
|
||||||
mu.Lock()
|
pv = mr.Get(xtd)
|
||||||
defer mu.Unlock()
|
case xtd.HasDefault():
|
||||||
e, ok := emap[extension.Field]
|
pv = xtd.Default()
|
||||||
if !ok {
|
default:
|
||||||
// defaultExtensionValue returns the default value or
|
|
||||||
// ErrMissingExtension if there is no default.
|
|
||||||
return defaultExtensionValue(extension)
|
|
||||||
}
|
|
||||||
|
|
||||||
if e.value != nil {
|
|
||||||
// Already decoded. Check the descriptor, though.
|
|
||||||
if e.desc != extension {
|
|
||||||
// This shouldn't happen. If it does, it means that
|
|
||||||
// GetExtension was called twice with two different
|
|
||||||
// descriptors with the same field number.
|
|
||||||
return nil, errors.New("proto: descriptor conflict")
|
|
||||||
}
|
|
||||||
return extensionAsLegacyType(e.value), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if extension.ExtensionType == nil {
|
|
||||||
// incomplete descriptor
|
|
||||||
return e.enc, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
v, err := decodeExtension(e.enc, extension)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remember the decoded version and drop the encoded version.
|
|
||||||
// That way it is safe to mutate what we return.
|
|
||||||
e.value = extensionAsStorageType(v)
|
|
||||||
e.desc = extension
|
|
||||||
e.enc = nil
|
|
||||||
emap[extension.Field] = e
|
|
||||||
return extensionAsLegacyType(e.value), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// defaultExtensionValue returns the default value for extension.
|
|
||||||
// If no default for an extension is defined ErrMissingExtension is returned.
|
|
||||||
func defaultExtensionValue(extension *ExtensionDesc) (interface{}, error) {
|
|
||||||
if extension.ExtensionType == nil {
|
|
||||||
// incomplete descriptor, so no default
|
|
||||||
return nil, ErrMissingExtension
|
return nil, ErrMissingExtension
|
||||||
}
|
}
|
||||||
|
|
||||||
t := reflect.TypeOf(extension.ExtensionType)
|
v := xt.InterfaceOf(pv)
|
||||||
props := extensionProperties(extension)
|
rv := reflect.ValueOf(v)
|
||||||
|
if isScalarKind(rv.Kind()) {
|
||||||
sf, _, err := fieldDefault(t, props)
|
rv2 := reflect.New(rv.Type())
|
||||||
if err != nil {
|
rv2.Elem().Set(rv)
|
||||||
return nil, err
|
v = rv2.Interface()
|
||||||
}
|
}
|
||||||
|
return v, nil
|
||||||
if sf == nil || sf.value == nil {
|
|
||||||
// There is no default value.
|
|
||||||
return nil, ErrMissingExtension
|
|
||||||
}
|
|
||||||
|
|
||||||
if t.Kind() != reflect.Ptr {
|
|
||||||
// We do not need to return a Ptr, we can directly return sf.value.
|
|
||||||
return sf.value, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// We need to return an interface{} that is a pointer to sf.value.
|
|
||||||
value := reflect.New(t).Elem()
|
|
||||||
value.Set(reflect.New(value.Type().Elem()))
|
|
||||||
if sf.kind == reflect.Int32 {
|
|
||||||
// We may have an int32 or an enum, but the underlying data is int32.
|
|
||||||
// Since we can't set an int32 into a non int32 reflect.value directly
|
|
||||||
// set it as a int32.
|
|
||||||
value.Elem().SetInt(int64(sf.value.(int32)))
|
|
||||||
} else {
|
|
||||||
value.Elem().Set(reflect.ValueOf(sf.value))
|
|
||||||
}
|
|
||||||
return value.Interface(), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// decodeExtension decodes an extension encoded in b.
|
// extensionResolver is a custom extension resolver that stores a single
|
||||||
func decodeExtension(b []byte, extension *ExtensionDesc) (interface{}, error) {
|
// extension type that takes precedence over the global registry.
|
||||||
t := reflect.TypeOf(extension.ExtensionType)
|
type extensionResolver struct{ xt protoreflect.ExtensionType }
|
||||||
unmarshal := typeUnmarshaler(t, extension.Tag)
|
|
||||||
|
|
||||||
// t is a pointer to a struct, pointer to basic type or a slice.
|
func (r extensionResolver) FindExtensionByName(field protoreflect.FullName) (protoreflect.ExtensionType, error) {
|
||||||
// Allocate space to store the pointer/slice.
|
if xtd := r.xt.TypeDescriptor(); xtd.FullName() == field {
|
||||||
value := reflect.New(t).Elem()
|
return r.xt, nil
|
||||||
|
|
||||||
var err error
|
|
||||||
for {
|
|
||||||
x, n := decodeVarint(b)
|
|
||||||
if n == 0 {
|
|
||||||
return nil, io.ErrUnexpectedEOF
|
|
||||||
}
|
}
|
||||||
b = b[n:]
|
return protoregistry.GlobalTypes.FindExtensionByName(field)
|
||||||
wire := int(x) & 7
|
|
||||||
|
|
||||||
b, err = unmarshal(b, valToPointer(value.Addr()), wire)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(b) == 0 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return value.Interface(), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetExtensions returns a slice of the extensions present in pb that are also listed in es.
|
func (r extensionResolver) FindExtensionByNumber(message protoreflect.FullName, field protoreflect.FieldNumber) (protoreflect.ExtensionType, error) {
|
||||||
// The returned slice has the same length as es; missing extensions will appear as nil elements.
|
if xtd := r.xt.TypeDescriptor(); xtd.ContainingMessage().FullName() == message && xtd.Number() == field {
|
||||||
func GetExtensions(pb Message, es []*ExtensionDesc) (extensions []interface{}, err error) {
|
return r.xt, nil
|
||||||
epb, err := extendable(pb)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
extensions = make([]interface{}, len(es))
|
return protoregistry.GlobalTypes.FindExtensionByNumber(message, field)
|
||||||
for i, e := range es {
|
}
|
||||||
extensions[i], err = GetExtension(epb, e)
|
|
||||||
|
// GetExtensions returns a list of the extensions values present in m,
|
||||||
|
// corresponding with the provided list of extension descriptors, xts.
|
||||||
|
// If an extension is missing in m, the corresponding value is nil.
|
||||||
|
func GetExtensions(m Message, xts []*ExtensionDesc) ([]interface{}, error) {
|
||||||
|
mr := MessageReflect(m)
|
||||||
|
if mr == nil || !mr.IsValid() {
|
||||||
|
return nil, errNotExtendable
|
||||||
|
}
|
||||||
|
|
||||||
|
vs := make([]interface{}, len(xts))
|
||||||
|
for i, xt := range xts {
|
||||||
|
v, err := GetExtension(m, xt)
|
||||||
|
if err != nil {
|
||||||
if err == ErrMissingExtension {
|
if err == ErrMissingExtension {
|
||||||
err = nil
|
continue
|
||||||
}
|
}
|
||||||
if err != nil {
|
return vs, err
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
vs[i] = v
|
||||||
}
|
}
|
||||||
return
|
return vs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExtensionDescs returns a new slice containing pb's extension descriptors, in undefined order.
|
// SetExtension sets an extension field in m to the provided value.
|
||||||
// For non-registered extensions, ExtensionDescs returns an incomplete descriptor containing
|
func SetExtension(m Message, xt *ExtensionDesc, v interface{}) error {
|
||||||
// just the Field field, which defines the extension's field number.
|
mr := MessageReflect(m)
|
||||||
func ExtensionDescs(pb Message) ([]*ExtensionDesc, error) {
|
if mr == nil || !mr.IsValid() || mr.Descriptor().ExtensionRanges().Len() == 0 {
|
||||||
epb, err := extendable(pb)
|
return errNotExtendable
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
registeredExtensions := RegisteredExtensions(pb)
|
|
||||||
|
|
||||||
emap, mu := epb.extensionsRead()
|
rv := reflect.ValueOf(v)
|
||||||
if emap == nil {
|
if reflect.TypeOf(v) != reflect.TypeOf(xt.ExtensionType) {
|
||||||
return nil, nil
|
return fmt.Errorf("proto: bad extension value type. got: %T, want: %T", v, xt.ExtensionType)
|
||||||
}
|
}
|
||||||
mu.Lock()
|
if rv.Kind() == reflect.Ptr {
|
||||||
defer mu.Unlock()
|
if rv.IsNil() {
|
||||||
extensions := make([]*ExtensionDesc, 0, len(emap))
|
return fmt.Errorf("proto: SetExtension called with nil value of type %T", v)
|
||||||
for extid, e := range emap {
|
}
|
||||||
desc := e.desc
|
if isScalarKind(rv.Elem().Kind()) {
|
||||||
if desc == nil {
|
v = rv.Elem().Interface()
|
||||||
desc = registeredExtensions[extid]
|
|
||||||
if desc == nil {
|
|
||||||
desc = &ExtensionDesc{Field: extid}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extensions = append(extensions, desc)
|
xtd := xt.TypeDescriptor()
|
||||||
|
if !isValidExtension(mr.Descriptor(), xtd) {
|
||||||
|
return fmt.Errorf("proto: bad extended type; %T does not extend %T", xt.ExtendedType, m)
|
||||||
}
|
}
|
||||||
return extensions, nil
|
mr.Set(xtd, xt.ValueOf(v))
|
||||||
}
|
clearUnknown(mr, fieldNum(xt.Field))
|
||||||
|
|
||||||
// SetExtension sets the specified extension of pb to the specified value.
|
|
||||||
func SetExtension(pb Message, extension *ExtensionDesc, value interface{}) error {
|
|
||||||
epb, err := extendable(pb)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := checkExtensionTypes(epb, extension); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
typ := reflect.TypeOf(extension.ExtensionType)
|
|
||||||
if typ != reflect.TypeOf(value) {
|
|
||||||
return fmt.Errorf("proto: bad extension value type. got: %T, want: %T", value, extension.ExtensionType)
|
|
||||||
}
|
|
||||||
// nil extension values need to be caught early, because the
|
|
||||||
// encoder can't distinguish an ErrNil due to a nil extension
|
|
||||||
// from an ErrNil due to a missing field. Extensions are
|
|
||||||
// always optional, so the encoder would just swallow the error
|
|
||||||
// and drop all the extensions from the encoded message.
|
|
||||||
if reflect.ValueOf(value).IsNil() {
|
|
||||||
return fmt.Errorf("proto: SetExtension called with nil value of type %T", value)
|
|
||||||
}
|
|
||||||
|
|
||||||
extmap := epb.extensionsWrite()
|
|
||||||
extmap[extension.Field] = Extension{desc: extension, value: extensionAsStorageType(value)}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ClearAllExtensions clears all extensions from pb.
|
// SetRawExtension inserts b into the unknown fields of m.
|
||||||
func ClearAllExtensions(pb Message) {
|
//
|
||||||
epb, err := extendable(pb)
|
// Deprecated: Use Message.ProtoReflect.SetUnknown instead.
|
||||||
if err != nil {
|
func SetRawExtension(m Message, fnum int32, b []byte) {
|
||||||
|
mr := MessageReflect(m)
|
||||||
|
if mr == nil || !mr.IsValid() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
m := epb.extensionsWrite()
|
|
||||||
for k := range m {
|
// Verify that the raw field is valid.
|
||||||
delete(m, k)
|
for b0 := b; len(b0) > 0; {
|
||||||
|
num, _, n := protowire.ConsumeField(b0)
|
||||||
|
if int32(num) != fnum {
|
||||||
|
panic(fmt.Sprintf("mismatching field number: got %d, want %d", num, fnum))
|
||||||
}
|
}
|
||||||
|
b0 = b0[n:]
|
||||||
|
}
|
||||||
|
|
||||||
|
ClearExtension(m, &ExtensionDesc{Field: fnum})
|
||||||
|
mr.SetUnknown(append(mr.GetUnknown(), b...))
|
||||||
}
|
}
|
||||||
|
|
||||||
// A global registry of extensions.
|
// ExtensionDescs returns a list of extension descriptors found in m,
|
||||||
// The generated code will register the generated descriptors by calling RegisterExtension.
|
// containing descriptors for both populated extension fields in m and
|
||||||
|
// also unknown fields of m that are in the extension range.
|
||||||
var extensionMaps = make(map[reflect.Type]map[int32]*ExtensionDesc)
|
// For the later case, an type incomplete descriptor is provided where only
|
||||||
|
// the ExtensionDesc.Field field is populated.
|
||||||
// RegisterExtension is called from the generated code.
|
// The order of the extension descriptors is undefined.
|
||||||
func RegisterExtension(desc *ExtensionDesc) {
|
func ExtensionDescs(m Message) ([]*ExtensionDesc, error) {
|
||||||
st := reflect.TypeOf(desc.ExtendedType).Elem()
|
mr := MessageReflect(m)
|
||||||
m := extensionMaps[st]
|
if mr == nil || !mr.IsValid() || mr.Descriptor().ExtensionRanges().Len() == 0 {
|
||||||
if m == nil {
|
return nil, errNotExtendable
|
||||||
m = make(map[int32]*ExtensionDesc)
|
|
||||||
extensionMaps[st] = m
|
|
||||||
}
|
}
|
||||||
if _, ok := m[desc.Field]; ok {
|
|
||||||
panic("proto: duplicate extension registered: " + st.String() + " " + strconv.Itoa(int(desc.Field)))
|
// Collect a set of known extension descriptors.
|
||||||
|
extDescs := make(map[protoreflect.FieldNumber]*ExtensionDesc)
|
||||||
|
mr.Range(func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool {
|
||||||
|
if fd.IsExtension() {
|
||||||
|
xt := fd.(protoreflect.ExtensionTypeDescriptor)
|
||||||
|
if xd, ok := xt.Type().(*ExtensionDesc); ok {
|
||||||
|
extDescs[fd.Number()] = xd
|
||||||
}
|
}
|
||||||
m[desc.Field] = desc
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
|
||||||
|
// Collect a set of unknown extension descriptors.
|
||||||
|
extRanges := mr.Descriptor().ExtensionRanges()
|
||||||
|
for b := mr.GetUnknown(); len(b) > 0; {
|
||||||
|
num, _, n := protowire.ConsumeField(b)
|
||||||
|
if extRanges.Has(num) && extDescs[num] == nil {
|
||||||
|
extDescs[num] = nil
|
||||||
|
}
|
||||||
|
b = b[n:]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transpose the set of descriptors into a list.
|
||||||
|
var xts []*ExtensionDesc
|
||||||
|
for num, xt := range extDescs {
|
||||||
|
if xt == nil {
|
||||||
|
xt = &ExtensionDesc{Field: int32(num)}
|
||||||
|
}
|
||||||
|
xts = append(xts, xt)
|
||||||
|
}
|
||||||
|
return xts, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// RegisteredExtensions returns a map of the registered extensions of a
|
// isValidExtension reports whether xtd is a valid extension descriptor for md.
|
||||||
// protocol buffer struct, indexed by the extension number.
|
func isValidExtension(md protoreflect.MessageDescriptor, xtd protoreflect.ExtensionTypeDescriptor) bool {
|
||||||
// The argument pb should be a nil pointer to the struct type.
|
return xtd.ContainingMessage() == md && md.ExtensionRanges().Has(xtd.Number())
|
||||||
func RegisteredExtensions(pb Message) map[int32]*ExtensionDesc {
|
|
||||||
return extensionMaps[reflect.TypeOf(pb).Elem()]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// extensionAsLegacyType converts an value in the storage type as the API type.
|
// isScalarKind reports whether k is a protobuf scalar kind (except bytes).
|
||||||
// See Extension.value.
|
// This function exists for historical reasons since the representation of
|
||||||
func extensionAsLegacyType(v interface{}) interface{} {
|
// scalars differs between v1 and v2, where v1 uses *T and v2 uses T.
|
||||||
switch rv := reflect.ValueOf(v); rv.Kind() {
|
func isScalarKind(k reflect.Kind) bool {
|
||||||
|
switch k {
|
||||||
case reflect.Bool, reflect.Int32, reflect.Int64, reflect.Uint32, reflect.Uint64, reflect.Float32, reflect.Float64, reflect.String:
|
case reflect.Bool, reflect.Int32, reflect.Int64, reflect.Uint32, reflect.Uint64, reflect.Float32, reflect.Float64, reflect.String:
|
||||||
// Represent primitive types as a pointer to the value.
|
return true
|
||||||
rv2 := reflect.New(rv.Type())
|
default:
|
||||||
rv2.Elem().Set(rv)
|
return false
|
||||||
v = rv2.Interface()
|
|
||||||
case reflect.Ptr:
|
|
||||||
// Represent slice types as the value itself.
|
|
||||||
switch rv.Type().Elem().Kind() {
|
|
||||||
case reflect.Slice:
|
|
||||||
if rv.IsNil() {
|
|
||||||
v = reflect.Zero(rv.Type().Elem()).Interface()
|
|
||||||
} else {
|
|
||||||
v = rv.Elem().Interface()
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
return v
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// extensionAsStorageType converts an value in the API type as the storage type.
|
// clearUnknown removes unknown fields from m where remover.Has reports true.
|
||||||
// See Extension.value.
|
func clearUnknown(m protoreflect.Message, remover interface {
|
||||||
func extensionAsStorageType(v interface{}) interface{} {
|
Has(protoreflect.FieldNumber) bool
|
||||||
switch rv := reflect.ValueOf(v); rv.Kind() {
|
}) {
|
||||||
case reflect.Ptr:
|
var bo protoreflect.RawFields
|
||||||
// Represent slice types as the value itself.
|
for bi := m.GetUnknown(); len(bi) > 0; {
|
||||||
switch rv.Type().Elem().Kind() {
|
num, _, n := protowire.ConsumeField(bi)
|
||||||
case reflect.Bool, reflect.Int32, reflect.Int64, reflect.Uint32, reflect.Uint64, reflect.Float32, reflect.Float64, reflect.String:
|
if !remover.Has(num) {
|
||||||
if rv.IsNil() {
|
bo = append(bo, bi[:n]...)
|
||||||
v = reflect.Zero(rv.Type().Elem()).Interface()
|
|
||||||
} else {
|
|
||||||
v = rv.Elem().Interface()
|
|
||||||
}
|
}
|
||||||
|
bi = bi[n:]
|
||||||
}
|
}
|
||||||
case reflect.Slice:
|
if bi := m.GetUnknown(); len(bi) != len(bo) {
|
||||||
// Represent slice types as a pointer to the value.
|
m.SetUnknown(bo)
|
||||||
if rv.Type().Elem().Kind() != reflect.Uint8 {
|
|
||||||
rv2 := reflect.New(rv.Type())
|
|
||||||
rv2.Elem().Set(rv)
|
|
||||||
v = rv2.Interface()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return v
|
|
||||||
|
type fieldNum protoreflect.FieldNumber
|
||||||
|
|
||||||
|
func (n1 fieldNum) Has(n2 protoreflect.FieldNumber) bool {
|
||||||
|
return protoreflect.FieldNumber(n1) == n2
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,965 +0,0 @@
|
||||||
// Go support for Protocol Buffers - Google's data interchange format
|
|
||||||
//
|
|
||||||
// Copyright 2010 The Go Authors. All rights reserved.
|
|
||||||
// https://github.com/golang/protobuf
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without
|
|
||||||
// modification, are permitted provided that the following conditions are
|
|
||||||
// met:
|
|
||||||
//
|
|
||||||
// * Redistributions of source code must retain the above copyright
|
|
||||||
// notice, this list of conditions and the following disclaimer.
|
|
||||||
// * Redistributions in binary form must reproduce the above
|
|
||||||
// copyright notice, this list of conditions and the following disclaimer
|
|
||||||
// in the documentation and/or other materials provided with the
|
|
||||||
// distribution.
|
|
||||||
// * Neither the name of Google Inc. nor the names of its
|
|
||||||
// contributors may be used to endorse or promote products derived from
|
|
||||||
// this software without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
/*
|
|
||||||
Package proto converts data structures to and from the wire format of
|
|
||||||
protocol buffers. It works in concert with the Go source code generated
|
|
||||||
for .proto files by the protocol compiler.
|
|
||||||
|
|
||||||
A summary of the properties of the protocol buffer interface
|
|
||||||
for a protocol buffer variable v:
|
|
||||||
|
|
||||||
- Names are turned from camel_case to CamelCase for export.
|
|
||||||
- There are no methods on v to set fields; just treat
|
|
||||||
them as structure fields.
|
|
||||||
- There are getters that return a field's value if set,
|
|
||||||
and return the field's default value if unset.
|
|
||||||
The getters work even if the receiver is a nil message.
|
|
||||||
- The zero value for a struct is its correct initialization state.
|
|
||||||
All desired fields must be set before marshaling.
|
|
||||||
- A Reset() method will restore a protobuf struct to its zero state.
|
|
||||||
- Non-repeated fields are pointers to the values; nil means unset.
|
|
||||||
That is, optional or required field int32 f becomes F *int32.
|
|
||||||
- Repeated fields are slices.
|
|
||||||
- Helper functions are available to aid the setting of fields.
|
|
||||||
msg.Foo = proto.String("hello") // set field
|
|
||||||
- Constants are defined to hold the default values of all fields that
|
|
||||||
have them. They have the form Default_StructName_FieldName.
|
|
||||||
Because the getter methods handle defaulted values,
|
|
||||||
direct use of these constants should be rare.
|
|
||||||
- Enums are given type names and maps from names to values.
|
|
||||||
Enum values are prefixed by the enclosing message's name, or by the
|
|
||||||
enum's type name if it is a top-level enum. Enum types have a String
|
|
||||||
method, and a Enum method to assist in message construction.
|
|
||||||
- Nested messages, groups and enums have type names prefixed with the name of
|
|
||||||
the surrounding message type.
|
|
||||||
- Extensions are given descriptor names that start with E_,
|
|
||||||
followed by an underscore-delimited list of the nested messages
|
|
||||||
that contain it (if any) followed by the CamelCased name of the
|
|
||||||
extension field itself. HasExtension, ClearExtension, GetExtension
|
|
||||||
and SetExtension are functions for manipulating extensions.
|
|
||||||
- Oneof field sets are given a single field in their message,
|
|
||||||
with distinguished wrapper types for each possible field value.
|
|
||||||
- Marshal and Unmarshal are functions to encode and decode the wire format.
|
|
||||||
|
|
||||||
When the .proto file specifies `syntax="proto3"`, there are some differences:
|
|
||||||
|
|
||||||
- Non-repeated fields of non-message type are values instead of pointers.
|
|
||||||
- Enum types do not get an Enum method.
|
|
||||||
|
|
||||||
The simplest way to describe this is to see an example.
|
|
||||||
Given file test.proto, containing
|
|
||||||
|
|
||||||
package example;
|
|
||||||
|
|
||||||
enum FOO { X = 17; }
|
|
||||||
|
|
||||||
message Test {
|
|
||||||
required string label = 1;
|
|
||||||
optional int32 type = 2 [default=77];
|
|
||||||
repeated int64 reps = 3;
|
|
||||||
optional group OptionalGroup = 4 {
|
|
||||||
required string RequiredField = 5;
|
|
||||||
}
|
|
||||||
oneof union {
|
|
||||||
int32 number = 6;
|
|
||||||
string name = 7;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
The resulting file, test.pb.go, is:
|
|
||||||
|
|
||||||
package example
|
|
||||||
|
|
||||||
import proto "github.com/golang/protobuf/proto"
|
|
||||||
import math "math"
|
|
||||||
|
|
||||||
type FOO int32
|
|
||||||
const (
|
|
||||||
FOO_X FOO = 17
|
|
||||||
)
|
|
||||||
var FOO_name = map[int32]string{
|
|
||||||
17: "X",
|
|
||||||
}
|
|
||||||
var FOO_value = map[string]int32{
|
|
||||||
"X": 17,
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x FOO) Enum() *FOO {
|
|
||||||
p := new(FOO)
|
|
||||||
*p = x
|
|
||||||
return p
|
|
||||||
}
|
|
||||||
func (x FOO) String() string {
|
|
||||||
return proto.EnumName(FOO_name, int32(x))
|
|
||||||
}
|
|
||||||
func (x *FOO) UnmarshalJSON(data []byte) error {
|
|
||||||
value, err := proto.UnmarshalJSONEnum(FOO_value, data)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
*x = FOO(value)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type Test struct {
|
|
||||||
Label *string `protobuf:"bytes,1,req,name=label" json:"label,omitempty"`
|
|
||||||
Type *int32 `protobuf:"varint,2,opt,name=type,def=77" json:"type,omitempty"`
|
|
||||||
Reps []int64 `protobuf:"varint,3,rep,name=reps" json:"reps,omitempty"`
|
|
||||||
Optionalgroup *Test_OptionalGroup `protobuf:"group,4,opt,name=OptionalGroup" json:"optionalgroup,omitempty"`
|
|
||||||
// Types that are valid to be assigned to Union:
|
|
||||||
// *Test_Number
|
|
||||||
// *Test_Name
|
|
||||||
Union isTest_Union `protobuf_oneof:"union"`
|
|
||||||
XXX_unrecognized []byte `json:"-"`
|
|
||||||
}
|
|
||||||
func (m *Test) Reset() { *m = Test{} }
|
|
||||||
func (m *Test) String() string { return proto.CompactTextString(m) }
|
|
||||||
func (*Test) ProtoMessage() {}
|
|
||||||
|
|
||||||
type isTest_Union interface {
|
|
||||||
isTest_Union()
|
|
||||||
}
|
|
||||||
|
|
||||||
type Test_Number struct {
|
|
||||||
Number int32 `protobuf:"varint,6,opt,name=number"`
|
|
||||||
}
|
|
||||||
type Test_Name struct {
|
|
||||||
Name string `protobuf:"bytes,7,opt,name=name"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*Test_Number) isTest_Union() {}
|
|
||||||
func (*Test_Name) isTest_Union() {}
|
|
||||||
|
|
||||||
func (m *Test) GetUnion() isTest_Union {
|
|
||||||
if m != nil {
|
|
||||||
return m.Union
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
const Default_Test_Type int32 = 77
|
|
||||||
|
|
||||||
func (m *Test) GetLabel() string {
|
|
||||||
if m != nil && m.Label != nil {
|
|
||||||
return *m.Label
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Test) GetType() int32 {
|
|
||||||
if m != nil && m.Type != nil {
|
|
||||||
return *m.Type
|
|
||||||
}
|
|
||||||
return Default_Test_Type
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Test) GetOptionalgroup() *Test_OptionalGroup {
|
|
||||||
if m != nil {
|
|
||||||
return m.Optionalgroup
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type Test_OptionalGroup struct {
|
|
||||||
RequiredField *string `protobuf:"bytes,5,req" json:"RequiredField,omitempty"`
|
|
||||||
}
|
|
||||||
func (m *Test_OptionalGroup) Reset() { *m = Test_OptionalGroup{} }
|
|
||||||
func (m *Test_OptionalGroup) String() string { return proto.CompactTextString(m) }
|
|
||||||
|
|
||||||
func (m *Test_OptionalGroup) GetRequiredField() string {
|
|
||||||
if m != nil && m.RequiredField != nil {
|
|
||||||
return *m.RequiredField
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Test) GetNumber() int32 {
|
|
||||||
if x, ok := m.GetUnion().(*Test_Number); ok {
|
|
||||||
return x.Number
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Test) GetName() string {
|
|
||||||
if x, ok := m.GetUnion().(*Test_Name); ok {
|
|
||||||
return x.Name
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
proto.RegisterEnum("example.FOO", FOO_name, FOO_value)
|
|
||||||
}
|
|
||||||
|
|
||||||
To create and play with a Test object:
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"log"
|
|
||||||
|
|
||||||
"github.com/golang/protobuf/proto"
|
|
||||||
pb "./example.pb"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
test := &pb.Test{
|
|
||||||
Label: proto.String("hello"),
|
|
||||||
Type: proto.Int32(17),
|
|
||||||
Reps: []int64{1, 2, 3},
|
|
||||||
Optionalgroup: &pb.Test_OptionalGroup{
|
|
||||||
RequiredField: proto.String("good bye"),
|
|
||||||
},
|
|
||||||
Union: &pb.Test_Name{"fred"},
|
|
||||||
}
|
|
||||||
data, err := proto.Marshal(test)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("marshaling error: ", err)
|
|
||||||
}
|
|
||||||
newTest := &pb.Test{}
|
|
||||||
err = proto.Unmarshal(data, newTest)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("unmarshaling error: ", err)
|
|
||||||
}
|
|
||||||
// Now test and newTest contain the same data.
|
|
||||||
if test.GetLabel() != newTest.GetLabel() {
|
|
||||||
log.Fatalf("data mismatch %q != %q", test.GetLabel(), newTest.GetLabel())
|
|
||||||
}
|
|
||||||
// Use a type switch to determine which oneof was set.
|
|
||||||
switch u := test.Union.(type) {
|
|
||||||
case *pb.Test_Number: // u.Number contains the number.
|
|
||||||
case *pb.Test_Name: // u.Name contains the string.
|
|
||||||
}
|
|
||||||
// etc.
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
package proto
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"reflect"
|
|
||||||
"sort"
|
|
||||||
"strconv"
|
|
||||||
"sync"
|
|
||||||
)
|
|
||||||
|
|
||||||
// RequiredNotSetError is an error type returned by either Marshal or Unmarshal.
|
|
||||||
// Marshal reports this when a required field is not initialized.
|
|
||||||
// Unmarshal reports this when a required field is missing from the wire data.
|
|
||||||
type RequiredNotSetError struct{ field string }
|
|
||||||
|
|
||||||
func (e *RequiredNotSetError) Error() string {
|
|
||||||
if e.field == "" {
|
|
||||||
return fmt.Sprintf("proto: required field not set")
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("proto: required field %q not set", e.field)
|
|
||||||
}
|
|
||||||
func (e *RequiredNotSetError) RequiredNotSet() bool {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
type invalidUTF8Error struct{ field string }
|
|
||||||
|
|
||||||
func (e *invalidUTF8Error) Error() string {
|
|
||||||
if e.field == "" {
|
|
||||||
return "proto: invalid UTF-8 detected"
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("proto: field %q contains invalid UTF-8", e.field)
|
|
||||||
}
|
|
||||||
func (e *invalidUTF8Error) InvalidUTF8() bool {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// errInvalidUTF8 is a sentinel error to identify fields with invalid UTF-8.
|
|
||||||
// This error should not be exposed to the external API as such errors should
|
|
||||||
// be recreated with the field information.
|
|
||||||
var errInvalidUTF8 = &invalidUTF8Error{}
|
|
||||||
|
|
||||||
// isNonFatal reports whether the error is either a RequiredNotSet error
|
|
||||||
// or a InvalidUTF8 error.
|
|
||||||
func isNonFatal(err error) bool {
|
|
||||||
if re, ok := err.(interface{ RequiredNotSet() bool }); ok && re.RequiredNotSet() {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if re, ok := err.(interface{ InvalidUTF8() bool }); ok && re.InvalidUTF8() {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
type nonFatal struct{ E error }
|
|
||||||
|
|
||||||
// Merge merges err into nf and reports whether it was successful.
|
|
||||||
// Otherwise it returns false for any fatal non-nil errors.
|
|
||||||
func (nf *nonFatal) Merge(err error) (ok bool) {
|
|
||||||
if err == nil {
|
|
||||||
return true // not an error
|
|
||||||
}
|
|
||||||
if !isNonFatal(err) {
|
|
||||||
return false // fatal error
|
|
||||||
}
|
|
||||||
if nf.E == nil {
|
|
||||||
nf.E = err // store first instance of non-fatal error
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Message is implemented by generated protocol buffer messages.
|
|
||||||
type Message interface {
|
|
||||||
Reset()
|
|
||||||
String() string
|
|
||||||
ProtoMessage()
|
|
||||||
}
|
|
||||||
|
|
||||||
// A Buffer is a buffer manager for marshaling and unmarshaling
|
|
||||||
// protocol buffers. It may be reused between invocations to
|
|
||||||
// reduce memory usage. It is not necessary to use a Buffer;
|
|
||||||
// the global functions Marshal and Unmarshal create a
|
|
||||||
// temporary Buffer and are fine for most applications.
|
|
||||||
type Buffer struct {
|
|
||||||
buf []byte // encode/decode byte stream
|
|
||||||
index int // read point
|
|
||||||
|
|
||||||
deterministic bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewBuffer allocates a new Buffer and initializes its internal data to
|
|
||||||
// the contents of the argument slice.
|
|
||||||
func NewBuffer(e []byte) *Buffer {
|
|
||||||
return &Buffer{buf: e}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reset resets the Buffer, ready for marshaling a new protocol buffer.
|
|
||||||
func (p *Buffer) Reset() {
|
|
||||||
p.buf = p.buf[0:0] // for reading/writing
|
|
||||||
p.index = 0 // for reading
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetBuf replaces the internal buffer with the slice,
|
|
||||||
// ready for unmarshaling the contents of the slice.
|
|
||||||
func (p *Buffer) SetBuf(s []byte) {
|
|
||||||
p.buf = s
|
|
||||||
p.index = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bytes returns the contents of the Buffer.
|
|
||||||
func (p *Buffer) Bytes() []byte { return p.buf }
|
|
||||||
|
|
||||||
// SetDeterministic sets whether to use deterministic serialization.
|
|
||||||
//
|
|
||||||
// Deterministic serialization guarantees that for a given binary, equal
|
|
||||||
// messages will always be serialized to the same bytes. This implies:
|
|
||||||
//
|
|
||||||
// - Repeated serialization of a message will return the same bytes.
|
|
||||||
// - Different processes of the same binary (which may be executing on
|
|
||||||
// different machines) will serialize equal messages to the same bytes.
|
|
||||||
//
|
|
||||||
// Note that the deterministic serialization is NOT canonical across
|
|
||||||
// languages. It is not guaranteed to remain stable over time. It is unstable
|
|
||||||
// across different builds with schema changes due to unknown fields.
|
|
||||||
// Users who need canonical serialization (e.g., persistent storage in a
|
|
||||||
// canonical form, fingerprinting, etc.) should define their own
|
|
||||||
// canonicalization specification and implement their own serializer rather
|
|
||||||
// than relying on this API.
|
|
||||||
//
|
|
||||||
// If deterministic serialization is requested, map entries will be sorted
|
|
||||||
// by keys in lexicographical order. This is an implementation detail and
|
|
||||||
// subject to change.
|
|
||||||
func (p *Buffer) SetDeterministic(deterministic bool) {
|
|
||||||
p.deterministic = deterministic
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Helper routines for simplifying the creation of optional fields of basic type.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Bool is a helper routine that allocates a new bool value
|
|
||||||
// to store v and returns a pointer to it.
|
|
||||||
func Bool(v bool) *bool {
|
|
||||||
return &v
|
|
||||||
}
|
|
||||||
|
|
||||||
// Int32 is a helper routine that allocates a new int32 value
|
|
||||||
// to store v and returns a pointer to it.
|
|
||||||
func Int32(v int32) *int32 {
|
|
||||||
return &v
|
|
||||||
}
|
|
||||||
|
|
||||||
// Int is a helper routine that allocates a new int32 value
|
|
||||||
// to store v and returns a pointer to it, but unlike Int32
|
|
||||||
// its argument value is an int.
|
|
||||||
func Int(v int) *int32 {
|
|
||||||
p := new(int32)
|
|
||||||
*p = int32(v)
|
|
||||||
return p
|
|
||||||
}
|
|
||||||
|
|
||||||
// Int64 is a helper routine that allocates a new int64 value
|
|
||||||
// to store v and returns a pointer to it.
|
|
||||||
func Int64(v int64) *int64 {
|
|
||||||
return &v
|
|
||||||
}
|
|
||||||
|
|
||||||
// Float32 is a helper routine that allocates a new float32 value
|
|
||||||
// to store v and returns a pointer to it.
|
|
||||||
func Float32(v float32) *float32 {
|
|
||||||
return &v
|
|
||||||
}
|
|
||||||
|
|
||||||
// Float64 is a helper routine that allocates a new float64 value
|
|
||||||
// to store v and returns a pointer to it.
|
|
||||||
func Float64(v float64) *float64 {
|
|
||||||
return &v
|
|
||||||
}
|
|
||||||
|
|
||||||
// Uint32 is a helper routine that allocates a new uint32 value
|
|
||||||
// to store v and returns a pointer to it.
|
|
||||||
func Uint32(v uint32) *uint32 {
|
|
||||||
return &v
|
|
||||||
}
|
|
||||||
|
|
||||||
// Uint64 is a helper routine that allocates a new uint64 value
|
|
||||||
// to store v and returns a pointer to it.
|
|
||||||
func Uint64(v uint64) *uint64 {
|
|
||||||
return &v
|
|
||||||
}
|
|
||||||
|
|
||||||
// String is a helper routine that allocates a new string value
|
|
||||||
// to store v and returns a pointer to it.
|
|
||||||
func String(v string) *string {
|
|
||||||
return &v
|
|
||||||
}
|
|
||||||
|
|
||||||
// EnumName is a helper function to simplify printing protocol buffer enums
|
|
||||||
// by name. Given an enum map and a value, it returns a useful string.
|
|
||||||
func EnumName(m map[int32]string, v int32) string {
|
|
||||||
s, ok := m[v]
|
|
||||||
if ok {
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
return strconv.Itoa(int(v))
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalJSONEnum is a helper function to simplify recovering enum int values
|
|
||||||
// from their JSON-encoded representation. Given a map from the enum's symbolic
|
|
||||||
// names to its int values, and a byte buffer containing the JSON-encoded
|
|
||||||
// value, it returns an int32 that can be cast to the enum type by the caller.
|
|
||||||
//
|
|
||||||
// The function can deal with both JSON representations, numeric and symbolic.
|
|
||||||
func UnmarshalJSONEnum(m map[string]int32, data []byte, enumName string) (int32, error) {
|
|
||||||
if data[0] == '"' {
|
|
||||||
// New style: enums are strings.
|
|
||||||
var repr string
|
|
||||||
if err := json.Unmarshal(data, &repr); err != nil {
|
|
||||||
return -1, err
|
|
||||||
}
|
|
||||||
val, ok := m[repr]
|
|
||||||
if !ok {
|
|
||||||
return 0, fmt.Errorf("unrecognized enum %s value %q", enumName, repr)
|
|
||||||
}
|
|
||||||
return val, nil
|
|
||||||
}
|
|
||||||
// Old style: enums are ints.
|
|
||||||
var val int32
|
|
||||||
if err := json.Unmarshal(data, &val); err != nil {
|
|
||||||
return 0, fmt.Errorf("cannot unmarshal %#q into enum %s", data, enumName)
|
|
||||||
}
|
|
||||||
return val, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// DebugPrint dumps the encoded data in b in a debugging format with a header
|
|
||||||
// including the string s. Used in testing but made available for general debugging.
|
|
||||||
func (p *Buffer) DebugPrint(s string, b []byte) {
|
|
||||||
var u uint64
|
|
||||||
|
|
||||||
obuf := p.buf
|
|
||||||
index := p.index
|
|
||||||
p.buf = b
|
|
||||||
p.index = 0
|
|
||||||
depth := 0
|
|
||||||
|
|
||||||
fmt.Printf("\n--- %s ---\n", s)
|
|
||||||
|
|
||||||
out:
|
|
||||||
for {
|
|
||||||
for i := 0; i < depth; i++ {
|
|
||||||
fmt.Print(" ")
|
|
||||||
}
|
|
||||||
|
|
||||||
index := p.index
|
|
||||||
if index == len(p.buf) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
op, err := p.DecodeVarint()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Printf("%3d: fetching op err %v\n", index, err)
|
|
||||||
break out
|
|
||||||
}
|
|
||||||
tag := op >> 3
|
|
||||||
wire := op & 7
|
|
||||||
|
|
||||||
switch wire {
|
|
||||||
default:
|
|
||||||
fmt.Printf("%3d: t=%3d unknown wire=%d\n",
|
|
||||||
index, tag, wire)
|
|
||||||
break out
|
|
||||||
|
|
||||||
case WireBytes:
|
|
||||||
var r []byte
|
|
||||||
|
|
||||||
r, err = p.DecodeRawBytes(false)
|
|
||||||
if err != nil {
|
|
||||||
break out
|
|
||||||
}
|
|
||||||
fmt.Printf("%3d: t=%3d bytes [%d]", index, tag, len(r))
|
|
||||||
if len(r) <= 6 {
|
|
||||||
for i := 0; i < len(r); i++ {
|
|
||||||
fmt.Printf(" %.2x", r[i])
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for i := 0; i < 3; i++ {
|
|
||||||
fmt.Printf(" %.2x", r[i])
|
|
||||||
}
|
|
||||||
fmt.Printf(" ..")
|
|
||||||
for i := len(r) - 3; i < len(r); i++ {
|
|
||||||
fmt.Printf(" %.2x", r[i])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fmt.Printf("\n")
|
|
||||||
|
|
||||||
case WireFixed32:
|
|
||||||
u, err = p.DecodeFixed32()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Printf("%3d: t=%3d fix32 err %v\n", index, tag, err)
|
|
||||||
break out
|
|
||||||
}
|
|
||||||
fmt.Printf("%3d: t=%3d fix32 %d\n", index, tag, u)
|
|
||||||
|
|
||||||
case WireFixed64:
|
|
||||||
u, err = p.DecodeFixed64()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Printf("%3d: t=%3d fix64 err %v\n", index, tag, err)
|
|
||||||
break out
|
|
||||||
}
|
|
||||||
fmt.Printf("%3d: t=%3d fix64 %d\n", index, tag, u)
|
|
||||||
|
|
||||||
case WireVarint:
|
|
||||||
u, err = p.DecodeVarint()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Printf("%3d: t=%3d varint err %v\n", index, tag, err)
|
|
||||||
break out
|
|
||||||
}
|
|
||||||
fmt.Printf("%3d: t=%3d varint %d\n", index, tag, u)
|
|
||||||
|
|
||||||
case WireStartGroup:
|
|
||||||
fmt.Printf("%3d: t=%3d start\n", index, tag)
|
|
||||||
depth++
|
|
||||||
|
|
||||||
case WireEndGroup:
|
|
||||||
depth--
|
|
||||||
fmt.Printf("%3d: t=%3d end\n", index, tag)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if depth != 0 {
|
|
||||||
fmt.Printf("%3d: start-end not balanced %d\n", p.index, depth)
|
|
||||||
}
|
|
||||||
fmt.Printf("\n")
|
|
||||||
|
|
||||||
p.buf = obuf
|
|
||||||
p.index = index
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetDefaults sets unset protocol buffer fields to their default values.
|
|
||||||
// It only modifies fields that are both unset and have defined defaults.
|
|
||||||
// It recursively sets default values in any non-nil sub-messages.
|
|
||||||
func SetDefaults(pb Message) {
|
|
||||||
setDefaults(reflect.ValueOf(pb), true, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
// v is a pointer to a struct.
|
|
||||||
func setDefaults(v reflect.Value, recur, zeros bool) {
|
|
||||||
v = v.Elem()
|
|
||||||
|
|
||||||
defaultMu.RLock()
|
|
||||||
dm, ok := defaults[v.Type()]
|
|
||||||
defaultMu.RUnlock()
|
|
||||||
if !ok {
|
|
||||||
dm = buildDefaultMessage(v.Type())
|
|
||||||
defaultMu.Lock()
|
|
||||||
defaults[v.Type()] = dm
|
|
||||||
defaultMu.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, sf := range dm.scalars {
|
|
||||||
f := v.Field(sf.index)
|
|
||||||
if !f.IsNil() {
|
|
||||||
// field already set
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
dv := sf.value
|
|
||||||
if dv == nil && !zeros {
|
|
||||||
// no explicit default, and don't want to set zeros
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
fptr := f.Addr().Interface() // **T
|
|
||||||
// TODO: Consider batching the allocations we do here.
|
|
||||||
switch sf.kind {
|
|
||||||
case reflect.Bool:
|
|
||||||
b := new(bool)
|
|
||||||
if dv != nil {
|
|
||||||
*b = dv.(bool)
|
|
||||||
}
|
|
||||||
*(fptr.(**bool)) = b
|
|
||||||
case reflect.Float32:
|
|
||||||
f := new(float32)
|
|
||||||
if dv != nil {
|
|
||||||
*f = dv.(float32)
|
|
||||||
}
|
|
||||||
*(fptr.(**float32)) = f
|
|
||||||
case reflect.Float64:
|
|
||||||
f := new(float64)
|
|
||||||
if dv != nil {
|
|
||||||
*f = dv.(float64)
|
|
||||||
}
|
|
||||||
*(fptr.(**float64)) = f
|
|
||||||
case reflect.Int32:
|
|
||||||
// might be an enum
|
|
||||||
if ft := f.Type(); ft != int32PtrType {
|
|
||||||
// enum
|
|
||||||
f.Set(reflect.New(ft.Elem()))
|
|
||||||
if dv != nil {
|
|
||||||
f.Elem().SetInt(int64(dv.(int32)))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// int32 field
|
|
||||||
i := new(int32)
|
|
||||||
if dv != nil {
|
|
||||||
*i = dv.(int32)
|
|
||||||
}
|
|
||||||
*(fptr.(**int32)) = i
|
|
||||||
}
|
|
||||||
case reflect.Int64:
|
|
||||||
i := new(int64)
|
|
||||||
if dv != nil {
|
|
||||||
*i = dv.(int64)
|
|
||||||
}
|
|
||||||
*(fptr.(**int64)) = i
|
|
||||||
case reflect.String:
|
|
||||||
s := new(string)
|
|
||||||
if dv != nil {
|
|
||||||
*s = dv.(string)
|
|
||||||
}
|
|
||||||
*(fptr.(**string)) = s
|
|
||||||
case reflect.Uint8:
|
|
||||||
// exceptional case: []byte
|
|
||||||
var b []byte
|
|
||||||
if dv != nil {
|
|
||||||
db := dv.([]byte)
|
|
||||||
b = make([]byte, len(db))
|
|
||||||
copy(b, db)
|
|
||||||
} else {
|
|
||||||
b = []byte{}
|
|
||||||
}
|
|
||||||
*(fptr.(*[]byte)) = b
|
|
||||||
case reflect.Uint32:
|
|
||||||
u := new(uint32)
|
|
||||||
if dv != nil {
|
|
||||||
*u = dv.(uint32)
|
|
||||||
}
|
|
||||||
*(fptr.(**uint32)) = u
|
|
||||||
case reflect.Uint64:
|
|
||||||
u := new(uint64)
|
|
||||||
if dv != nil {
|
|
||||||
*u = dv.(uint64)
|
|
||||||
}
|
|
||||||
*(fptr.(**uint64)) = u
|
|
||||||
default:
|
|
||||||
log.Printf("proto: can't set default for field %v (sf.kind=%v)", f, sf.kind)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, ni := range dm.nested {
|
|
||||||
f := v.Field(ni)
|
|
||||||
// f is *T or []*T or map[T]*T
|
|
||||||
switch f.Kind() {
|
|
||||||
case reflect.Ptr:
|
|
||||||
if f.IsNil() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
setDefaults(f, recur, zeros)
|
|
||||||
|
|
||||||
case reflect.Slice:
|
|
||||||
for i := 0; i < f.Len(); i++ {
|
|
||||||
e := f.Index(i)
|
|
||||||
if e.IsNil() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
setDefaults(e, recur, zeros)
|
|
||||||
}
|
|
||||||
|
|
||||||
case reflect.Map:
|
|
||||||
for _, k := range f.MapKeys() {
|
|
||||||
e := f.MapIndex(k)
|
|
||||||
if e.IsNil() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
setDefaults(e, recur, zeros)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
// defaults maps a protocol buffer struct type to a slice of the fields,
|
|
||||||
// with its scalar fields set to their proto-declared non-zero default values.
|
|
||||||
defaultMu sync.RWMutex
|
|
||||||
defaults = make(map[reflect.Type]defaultMessage)
|
|
||||||
|
|
||||||
int32PtrType = reflect.TypeOf((*int32)(nil))
|
|
||||||
)
|
|
||||||
|
|
||||||
// defaultMessage represents information about the default values of a message.
|
|
||||||
type defaultMessage struct {
|
|
||||||
scalars []scalarField
|
|
||||||
nested []int // struct field index of nested messages
|
|
||||||
}
|
|
||||||
|
|
||||||
type scalarField struct {
|
|
||||||
index int // struct field index
|
|
||||||
kind reflect.Kind // element type (the T in *T or []T)
|
|
||||||
value interface{} // the proto-declared default value, or nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// t is a struct type.
|
|
||||||
func buildDefaultMessage(t reflect.Type) (dm defaultMessage) {
|
|
||||||
sprop := GetProperties(t)
|
|
||||||
for _, prop := range sprop.Prop {
|
|
||||||
fi, ok := sprop.decoderTags.get(prop.Tag)
|
|
||||||
if !ok {
|
|
||||||
// XXX_unrecognized
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
ft := t.Field(fi).Type
|
|
||||||
|
|
||||||
sf, nested, err := fieldDefault(ft, prop)
|
|
||||||
switch {
|
|
||||||
case err != nil:
|
|
||||||
log.Print(err)
|
|
||||||
case nested:
|
|
||||||
dm.nested = append(dm.nested, fi)
|
|
||||||
case sf != nil:
|
|
||||||
sf.index = fi
|
|
||||||
dm.scalars = append(dm.scalars, *sf)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return dm
|
|
||||||
}
|
|
||||||
|
|
||||||
// fieldDefault returns the scalarField for field type ft.
|
|
||||||
// sf will be nil if the field can not have a default.
|
|
||||||
// nestedMessage will be true if this is a nested message.
|
|
||||||
// Note that sf.index is not set on return.
|
|
||||||
func fieldDefault(ft reflect.Type, prop *Properties) (sf *scalarField, nestedMessage bool, err error) {
|
|
||||||
var canHaveDefault bool
|
|
||||||
switch ft.Kind() {
|
|
||||||
case reflect.Ptr:
|
|
||||||
if ft.Elem().Kind() == reflect.Struct {
|
|
||||||
nestedMessage = true
|
|
||||||
} else {
|
|
||||||
canHaveDefault = true // proto2 scalar field
|
|
||||||
}
|
|
||||||
|
|
||||||
case reflect.Slice:
|
|
||||||
switch ft.Elem().Kind() {
|
|
||||||
case reflect.Ptr:
|
|
||||||
nestedMessage = true // repeated message
|
|
||||||
case reflect.Uint8:
|
|
||||||
canHaveDefault = true // bytes field
|
|
||||||
}
|
|
||||||
|
|
||||||
case reflect.Map:
|
|
||||||
if ft.Elem().Kind() == reflect.Ptr {
|
|
||||||
nestedMessage = true // map with message values
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !canHaveDefault {
|
|
||||||
if nestedMessage {
|
|
||||||
return nil, true, nil
|
|
||||||
}
|
|
||||||
return nil, false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// We now know that ft is a pointer or slice.
|
|
||||||
sf = &scalarField{kind: ft.Elem().Kind()}
|
|
||||||
|
|
||||||
// scalar fields without defaults
|
|
||||||
if !prop.HasDefault {
|
|
||||||
return sf, false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// a scalar field: either *T or []byte
|
|
||||||
switch ft.Elem().Kind() {
|
|
||||||
case reflect.Bool:
|
|
||||||
x, err := strconv.ParseBool(prop.Default)
|
|
||||||
if err != nil {
|
|
||||||
return nil, false, fmt.Errorf("proto: bad default bool %q: %v", prop.Default, err)
|
|
||||||
}
|
|
||||||
sf.value = x
|
|
||||||
case reflect.Float32:
|
|
||||||
x, err := strconv.ParseFloat(prop.Default, 32)
|
|
||||||
if err != nil {
|
|
||||||
return nil, false, fmt.Errorf("proto: bad default float32 %q: %v", prop.Default, err)
|
|
||||||
}
|
|
||||||
sf.value = float32(x)
|
|
||||||
case reflect.Float64:
|
|
||||||
x, err := strconv.ParseFloat(prop.Default, 64)
|
|
||||||
if err != nil {
|
|
||||||
return nil, false, fmt.Errorf("proto: bad default float64 %q: %v", prop.Default, err)
|
|
||||||
}
|
|
||||||
sf.value = x
|
|
||||||
case reflect.Int32:
|
|
||||||
x, err := strconv.ParseInt(prop.Default, 10, 32)
|
|
||||||
if err != nil {
|
|
||||||
return nil, false, fmt.Errorf("proto: bad default int32 %q: %v", prop.Default, err)
|
|
||||||
}
|
|
||||||
sf.value = int32(x)
|
|
||||||
case reflect.Int64:
|
|
||||||
x, err := strconv.ParseInt(prop.Default, 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
return nil, false, fmt.Errorf("proto: bad default int64 %q: %v", prop.Default, err)
|
|
||||||
}
|
|
||||||
sf.value = x
|
|
||||||
case reflect.String:
|
|
||||||
sf.value = prop.Default
|
|
||||||
case reflect.Uint8:
|
|
||||||
// []byte (not *uint8)
|
|
||||||
sf.value = []byte(prop.Default)
|
|
||||||
case reflect.Uint32:
|
|
||||||
x, err := strconv.ParseUint(prop.Default, 10, 32)
|
|
||||||
if err != nil {
|
|
||||||
return nil, false, fmt.Errorf("proto: bad default uint32 %q: %v", prop.Default, err)
|
|
||||||
}
|
|
||||||
sf.value = uint32(x)
|
|
||||||
case reflect.Uint64:
|
|
||||||
x, err := strconv.ParseUint(prop.Default, 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
return nil, false, fmt.Errorf("proto: bad default uint64 %q: %v", prop.Default, err)
|
|
||||||
}
|
|
||||||
sf.value = x
|
|
||||||
default:
|
|
||||||
return nil, false, fmt.Errorf("proto: unhandled def kind %v", ft.Elem().Kind())
|
|
||||||
}
|
|
||||||
|
|
||||||
return sf, false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// mapKeys returns a sort.Interface to be used for sorting the map keys.
|
|
||||||
// Map fields may have key types of non-float scalars, strings and enums.
|
|
||||||
func mapKeys(vs []reflect.Value) sort.Interface {
|
|
||||||
s := mapKeySorter{vs: vs}
|
|
||||||
|
|
||||||
// Type specialization per https://developers.google.com/protocol-buffers/docs/proto#maps.
|
|
||||||
if len(vs) == 0 {
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
switch vs[0].Kind() {
|
|
||||||
case reflect.Int32, reflect.Int64:
|
|
||||||
s.less = func(a, b reflect.Value) bool { return a.Int() < b.Int() }
|
|
||||||
case reflect.Uint32, reflect.Uint64:
|
|
||||||
s.less = func(a, b reflect.Value) bool { return a.Uint() < b.Uint() }
|
|
||||||
case reflect.Bool:
|
|
||||||
s.less = func(a, b reflect.Value) bool { return !a.Bool() && b.Bool() } // false < true
|
|
||||||
case reflect.String:
|
|
||||||
s.less = func(a, b reflect.Value) bool { return a.String() < b.String() }
|
|
||||||
default:
|
|
||||||
panic(fmt.Sprintf("unsupported map key type: %v", vs[0].Kind()))
|
|
||||||
}
|
|
||||||
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
type mapKeySorter struct {
|
|
||||||
vs []reflect.Value
|
|
||||||
less func(a, b reflect.Value) bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s mapKeySorter) Len() int { return len(s.vs) }
|
|
||||||
func (s mapKeySorter) Swap(i, j int) { s.vs[i], s.vs[j] = s.vs[j], s.vs[i] }
|
|
||||||
func (s mapKeySorter) Less(i, j int) bool {
|
|
||||||
return s.less(s.vs[i], s.vs[j])
|
|
||||||
}
|
|
||||||
|
|
||||||
// isProto3Zero reports whether v is a zero proto3 value.
|
|
||||||
func isProto3Zero(v reflect.Value) bool {
|
|
||||||
switch v.Kind() {
|
|
||||||
case reflect.Bool:
|
|
||||||
return !v.Bool()
|
|
||||||
case reflect.Int32, reflect.Int64:
|
|
||||||
return v.Int() == 0
|
|
||||||
case reflect.Uint32, reflect.Uint64:
|
|
||||||
return v.Uint() == 0
|
|
||||||
case reflect.Float32, reflect.Float64:
|
|
||||||
return v.Float() == 0
|
|
||||||
case reflect.String:
|
|
||||||
return v.String() == ""
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
// ProtoPackageIsVersion3 is referenced from generated protocol buffer files
|
|
||||||
// to assert that that code is compatible with this version of the proto package.
|
|
||||||
ProtoPackageIsVersion3 = true
|
|
||||||
|
|
||||||
// ProtoPackageIsVersion2 is referenced from generated protocol buffer files
|
|
||||||
// to assert that that code is compatible with this version of the proto package.
|
|
||||||
ProtoPackageIsVersion2 = true
|
|
||||||
|
|
||||||
// ProtoPackageIsVersion1 is referenced from generated protocol buffer files
|
|
||||||
// to assert that that code is compatible with this version of the proto package.
|
|
||||||
ProtoPackageIsVersion1 = true
|
|
||||||
)
|
|
||||||
|
|
||||||
// InternalMessageInfo is a type used internally by generated .pb.go files.
|
|
||||||
// This type is not intended to be used by non-generated code.
|
|
||||||
// This type is not subject to any compatibility guarantee.
|
|
||||||
type InternalMessageInfo struct {
|
|
||||||
marshal *marshalInfo
|
|
||||||
unmarshal *unmarshalInfo
|
|
||||||
merge *mergeInfo
|
|
||||||
discard *discardInfo
|
|
||||||
}
|
|
|
@ -1,181 +0,0 @@
|
||||||
// Go support for Protocol Buffers - Google's data interchange format
|
|
||||||
//
|
|
||||||
// Copyright 2010 The Go Authors. All rights reserved.
|
|
||||||
// https://github.com/golang/protobuf
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without
|
|
||||||
// modification, are permitted provided that the following conditions are
|
|
||||||
// met:
|
|
||||||
//
|
|
||||||
// * Redistributions of source code must retain the above copyright
|
|
||||||
// notice, this list of conditions and the following disclaimer.
|
|
||||||
// * Redistributions in binary form must reproduce the above
|
|
||||||
// copyright notice, this list of conditions and the following disclaimer
|
|
||||||
// in the documentation and/or other materials provided with the
|
|
||||||
// distribution.
|
|
||||||
// * Neither the name of Google Inc. nor the names of its
|
|
||||||
// contributors may be used to endorse or promote products derived from
|
|
||||||
// this software without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
package proto
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Support for message sets.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
)
|
|
||||||
|
|
||||||
// errNoMessageTypeID occurs when a protocol buffer does not have a message type ID.
|
|
||||||
// A message type ID is required for storing a protocol buffer in a message set.
|
|
||||||
var errNoMessageTypeID = errors.New("proto does not have a message type ID")
|
|
||||||
|
|
||||||
// The first two types (_MessageSet_Item and messageSet)
|
|
||||||
// model what the protocol compiler produces for the following protocol message:
|
|
||||||
// message MessageSet {
|
|
||||||
// repeated group Item = 1 {
|
|
||||||
// required int32 type_id = 2;
|
|
||||||
// required string message = 3;
|
|
||||||
// };
|
|
||||||
// }
|
|
||||||
// That is the MessageSet wire format. We can't use a proto to generate these
|
|
||||||
// because that would introduce a circular dependency between it and this package.
|
|
||||||
|
|
||||||
type _MessageSet_Item struct {
|
|
||||||
TypeId *int32 `protobuf:"varint,2,req,name=type_id"`
|
|
||||||
Message []byte `protobuf:"bytes,3,req,name=message"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type messageSet struct {
|
|
||||||
Item []*_MessageSet_Item `protobuf:"group,1,rep"`
|
|
||||||
XXX_unrecognized []byte
|
|
||||||
// TODO: caching?
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure messageSet is a Message.
|
|
||||||
var _ Message = (*messageSet)(nil)
|
|
||||||
|
|
||||||
// messageTypeIder is an interface satisfied by a protocol buffer type
|
|
||||||
// that may be stored in a MessageSet.
|
|
||||||
type messageTypeIder interface {
|
|
||||||
MessageTypeId() int32
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ms *messageSet) find(pb Message) *_MessageSet_Item {
|
|
||||||
mti, ok := pb.(messageTypeIder)
|
|
||||||
if !ok {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
id := mti.MessageTypeId()
|
|
||||||
for _, item := range ms.Item {
|
|
||||||
if *item.TypeId == id {
|
|
||||||
return item
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ms *messageSet) Has(pb Message) bool {
|
|
||||||
return ms.find(pb) != nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ms *messageSet) Unmarshal(pb Message) error {
|
|
||||||
if item := ms.find(pb); item != nil {
|
|
||||||
return Unmarshal(item.Message, pb)
|
|
||||||
}
|
|
||||||
if _, ok := pb.(messageTypeIder); !ok {
|
|
||||||
return errNoMessageTypeID
|
|
||||||
}
|
|
||||||
return nil // TODO: return error instead?
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ms *messageSet) Marshal(pb Message) error {
|
|
||||||
msg, err := Marshal(pb)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if item := ms.find(pb); item != nil {
|
|
||||||
// reuse existing item
|
|
||||||
item.Message = msg
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
mti, ok := pb.(messageTypeIder)
|
|
||||||
if !ok {
|
|
||||||
return errNoMessageTypeID
|
|
||||||
}
|
|
||||||
|
|
||||||
mtid := mti.MessageTypeId()
|
|
||||||
ms.Item = append(ms.Item, &_MessageSet_Item{
|
|
||||||
TypeId: &mtid,
|
|
||||||
Message: msg,
|
|
||||||
})
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ms *messageSet) Reset() { *ms = messageSet{} }
|
|
||||||
func (ms *messageSet) String() string { return CompactTextString(ms) }
|
|
||||||
func (*messageSet) ProtoMessage() {}
|
|
||||||
|
|
||||||
// Support for the message_set_wire_format message option.
|
|
||||||
|
|
||||||
func skipVarint(buf []byte) []byte {
|
|
||||||
i := 0
|
|
||||||
for ; buf[i]&0x80 != 0; i++ {
|
|
||||||
}
|
|
||||||
return buf[i+1:]
|
|
||||||
}
|
|
||||||
|
|
||||||
// unmarshalMessageSet decodes the extension map encoded in buf in the message set wire format.
|
|
||||||
// It is called by Unmarshal methods on protocol buffer messages with the message_set_wire_format option.
|
|
||||||
func unmarshalMessageSet(buf []byte, exts interface{}) error {
|
|
||||||
var m map[int32]Extension
|
|
||||||
switch exts := exts.(type) {
|
|
||||||
case *XXX_InternalExtensions:
|
|
||||||
m = exts.extensionsWrite()
|
|
||||||
case map[int32]Extension:
|
|
||||||
m = exts
|
|
||||||
default:
|
|
||||||
return errors.New("proto: not an extension map")
|
|
||||||
}
|
|
||||||
|
|
||||||
ms := new(messageSet)
|
|
||||||
if err := Unmarshal(buf, ms); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
for _, item := range ms.Item {
|
|
||||||
id := *item.TypeId
|
|
||||||
msg := item.Message
|
|
||||||
|
|
||||||
// Restore wire type and field number varint, plus length varint.
|
|
||||||
// Be careful to preserve duplicate items.
|
|
||||||
b := EncodeVarint(uint64(id)<<3 | WireBytes)
|
|
||||||
if ext, ok := m[id]; ok {
|
|
||||||
// Existing data; rip off the tag and length varint
|
|
||||||
// so we join the new data correctly.
|
|
||||||
// We can assume that ext.enc is set because we are unmarshaling.
|
|
||||||
o := ext.enc[len(b):] // skip wire type and field number
|
|
||||||
_, n := DecodeVarint(o) // calculate length of length varint
|
|
||||||
o = o[n:] // skip length varint
|
|
||||||
msg = append(o, msg...) // join old data and new data
|
|
||||||
}
|
|
||||||
b = append(b, EncodeVarint(uint64(len(msg)))...)
|
|
||||||
b = append(b, msg...)
|
|
||||||
|
|
||||||
m[id] = Extension{enc: b}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -1,360 +0,0 @@
|
||||||
// Go support for Protocol Buffers - Google's data interchange format
|
|
||||||
//
|
|
||||||
// Copyright 2012 The Go Authors. All rights reserved.
|
|
||||||
// https://github.com/golang/protobuf
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without
|
|
||||||
// modification, are permitted provided that the following conditions are
|
|
||||||
// met:
|
|
||||||
//
|
|
||||||
// * Redistributions of source code must retain the above copyright
|
|
||||||
// notice, this list of conditions and the following disclaimer.
|
|
||||||
// * Redistributions in binary form must reproduce the above
|
|
||||||
// copyright notice, this list of conditions and the following disclaimer
|
|
||||||
// in the documentation and/or other materials provided with the
|
|
||||||
// distribution.
|
|
||||||
// * Neither the name of Google Inc. nor the names of its
|
|
||||||
// contributors may be used to endorse or promote products derived from
|
|
||||||
// this software without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
// +build purego appengine js
|
|
||||||
|
|
||||||
// This file contains an implementation of proto field accesses using package reflect.
|
|
||||||
// It is slower than the code in pointer_unsafe.go but it avoids package unsafe and can
|
|
||||||
// be used on App Engine.
|
|
||||||
|
|
||||||
package proto
|
|
||||||
|
|
||||||
import (
|
|
||||||
"reflect"
|
|
||||||
"sync"
|
|
||||||
)
|
|
||||||
|
|
||||||
const unsafeAllowed = false
|
|
||||||
|
|
||||||
// A field identifies a field in a struct, accessible from a pointer.
|
|
||||||
// In this implementation, a field is identified by the sequence of field indices
|
|
||||||
// passed to reflect's FieldByIndex.
|
|
||||||
type field []int
|
|
||||||
|
|
||||||
// toField returns a field equivalent to the given reflect field.
|
|
||||||
func toField(f *reflect.StructField) field {
|
|
||||||
return f.Index
|
|
||||||
}
|
|
||||||
|
|
||||||
// invalidField is an invalid field identifier.
|
|
||||||
var invalidField = field(nil)
|
|
||||||
|
|
||||||
// zeroField is a noop when calling pointer.offset.
|
|
||||||
var zeroField = field([]int{})
|
|
||||||
|
|
||||||
// IsValid reports whether the field identifier is valid.
|
|
||||||
func (f field) IsValid() bool { return f != nil }
|
|
||||||
|
|
||||||
// The pointer type is for the table-driven decoder.
|
|
||||||
// The implementation here uses a reflect.Value of pointer type to
|
|
||||||
// create a generic pointer. In pointer_unsafe.go we use unsafe
|
|
||||||
// instead of reflect to implement the same (but faster) interface.
|
|
||||||
type pointer struct {
|
|
||||||
v reflect.Value
|
|
||||||
}
|
|
||||||
|
|
||||||
// toPointer converts an interface of pointer type to a pointer
|
|
||||||
// that points to the same target.
|
|
||||||
func toPointer(i *Message) pointer {
|
|
||||||
return pointer{v: reflect.ValueOf(*i)}
|
|
||||||
}
|
|
||||||
|
|
||||||
// toAddrPointer converts an interface to a pointer that points to
|
|
||||||
// the interface data.
|
|
||||||
func toAddrPointer(i *interface{}, isptr, deref bool) pointer {
|
|
||||||
v := reflect.ValueOf(*i)
|
|
||||||
u := reflect.New(v.Type())
|
|
||||||
u.Elem().Set(v)
|
|
||||||
if deref {
|
|
||||||
u = u.Elem()
|
|
||||||
}
|
|
||||||
return pointer{v: u}
|
|
||||||
}
|
|
||||||
|
|
||||||
// valToPointer converts v to a pointer. v must be of pointer type.
|
|
||||||
func valToPointer(v reflect.Value) pointer {
|
|
||||||
return pointer{v: v}
|
|
||||||
}
|
|
||||||
|
|
||||||
// offset converts from a pointer to a structure to a pointer to
|
|
||||||
// one of its fields.
|
|
||||||
func (p pointer) offset(f field) pointer {
|
|
||||||
return pointer{v: p.v.Elem().FieldByIndex(f).Addr()}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p pointer) isNil() bool {
|
|
||||||
return p.v.IsNil()
|
|
||||||
}
|
|
||||||
|
|
||||||
// grow updates the slice s in place to make it one element longer.
|
|
||||||
// s must be addressable.
|
|
||||||
// Returns the (addressable) new element.
|
|
||||||
func grow(s reflect.Value) reflect.Value {
|
|
||||||
n, m := s.Len(), s.Cap()
|
|
||||||
if n < m {
|
|
||||||
s.SetLen(n + 1)
|
|
||||||
} else {
|
|
||||||
s.Set(reflect.Append(s, reflect.Zero(s.Type().Elem())))
|
|
||||||
}
|
|
||||||
return s.Index(n)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p pointer) toInt64() *int64 {
|
|
||||||
return p.v.Interface().(*int64)
|
|
||||||
}
|
|
||||||
func (p pointer) toInt64Ptr() **int64 {
|
|
||||||
return p.v.Interface().(**int64)
|
|
||||||
}
|
|
||||||
func (p pointer) toInt64Slice() *[]int64 {
|
|
||||||
return p.v.Interface().(*[]int64)
|
|
||||||
}
|
|
||||||
|
|
||||||
var int32ptr = reflect.TypeOf((*int32)(nil))
|
|
||||||
|
|
||||||
func (p pointer) toInt32() *int32 {
|
|
||||||
return p.v.Convert(int32ptr).Interface().(*int32)
|
|
||||||
}
|
|
||||||
|
|
||||||
// The toInt32Ptr/Slice methods don't work because of enums.
|
|
||||||
// Instead, we must use set/get methods for the int32ptr/slice case.
|
|
||||||
/*
|
|
||||||
func (p pointer) toInt32Ptr() **int32 {
|
|
||||||
return p.v.Interface().(**int32)
|
|
||||||
}
|
|
||||||
func (p pointer) toInt32Slice() *[]int32 {
|
|
||||||
return p.v.Interface().(*[]int32)
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
func (p pointer) getInt32Ptr() *int32 {
|
|
||||||
if p.v.Type().Elem().Elem() == reflect.TypeOf(int32(0)) {
|
|
||||||
// raw int32 type
|
|
||||||
return p.v.Elem().Interface().(*int32)
|
|
||||||
}
|
|
||||||
// an enum
|
|
||||||
return p.v.Elem().Convert(int32PtrType).Interface().(*int32)
|
|
||||||
}
|
|
||||||
func (p pointer) setInt32Ptr(v int32) {
|
|
||||||
// Allocate value in a *int32. Possibly convert that to a *enum.
|
|
||||||
// Then assign it to a **int32 or **enum.
|
|
||||||
// Note: we can convert *int32 to *enum, but we can't convert
|
|
||||||
// **int32 to **enum!
|
|
||||||
p.v.Elem().Set(reflect.ValueOf(&v).Convert(p.v.Type().Elem()))
|
|
||||||
}
|
|
||||||
|
|
||||||
// getInt32Slice copies []int32 from p as a new slice.
|
|
||||||
// This behavior differs from the implementation in pointer_unsafe.go.
|
|
||||||
func (p pointer) getInt32Slice() []int32 {
|
|
||||||
if p.v.Type().Elem().Elem() == reflect.TypeOf(int32(0)) {
|
|
||||||
// raw int32 type
|
|
||||||
return p.v.Elem().Interface().([]int32)
|
|
||||||
}
|
|
||||||
// an enum
|
|
||||||
// Allocate a []int32, then assign []enum's values into it.
|
|
||||||
// Note: we can't convert []enum to []int32.
|
|
||||||
slice := p.v.Elem()
|
|
||||||
s := make([]int32, slice.Len())
|
|
||||||
for i := 0; i < slice.Len(); i++ {
|
|
||||||
s[i] = int32(slice.Index(i).Int())
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
// setInt32Slice copies []int32 into p as a new slice.
|
|
||||||
// This behavior differs from the implementation in pointer_unsafe.go.
|
|
||||||
func (p pointer) setInt32Slice(v []int32) {
|
|
||||||
if p.v.Type().Elem().Elem() == reflect.TypeOf(int32(0)) {
|
|
||||||
// raw int32 type
|
|
||||||
p.v.Elem().Set(reflect.ValueOf(v))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// an enum
|
|
||||||
// Allocate a []enum, then assign []int32's values into it.
|
|
||||||
// Note: we can't convert []enum to []int32.
|
|
||||||
slice := reflect.MakeSlice(p.v.Type().Elem(), len(v), cap(v))
|
|
||||||
for i, x := range v {
|
|
||||||
slice.Index(i).SetInt(int64(x))
|
|
||||||
}
|
|
||||||
p.v.Elem().Set(slice)
|
|
||||||
}
|
|
||||||
func (p pointer) appendInt32Slice(v int32) {
|
|
||||||
grow(p.v.Elem()).SetInt(int64(v))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p pointer) toUint64() *uint64 {
|
|
||||||
return p.v.Interface().(*uint64)
|
|
||||||
}
|
|
||||||
func (p pointer) toUint64Ptr() **uint64 {
|
|
||||||
return p.v.Interface().(**uint64)
|
|
||||||
}
|
|
||||||
func (p pointer) toUint64Slice() *[]uint64 {
|
|
||||||
return p.v.Interface().(*[]uint64)
|
|
||||||
}
|
|
||||||
func (p pointer) toUint32() *uint32 {
|
|
||||||
return p.v.Interface().(*uint32)
|
|
||||||
}
|
|
||||||
func (p pointer) toUint32Ptr() **uint32 {
|
|
||||||
return p.v.Interface().(**uint32)
|
|
||||||
}
|
|
||||||
func (p pointer) toUint32Slice() *[]uint32 {
|
|
||||||
return p.v.Interface().(*[]uint32)
|
|
||||||
}
|
|
||||||
func (p pointer) toBool() *bool {
|
|
||||||
return p.v.Interface().(*bool)
|
|
||||||
}
|
|
||||||
func (p pointer) toBoolPtr() **bool {
|
|
||||||
return p.v.Interface().(**bool)
|
|
||||||
}
|
|
||||||
func (p pointer) toBoolSlice() *[]bool {
|
|
||||||
return p.v.Interface().(*[]bool)
|
|
||||||
}
|
|
||||||
func (p pointer) toFloat64() *float64 {
|
|
||||||
return p.v.Interface().(*float64)
|
|
||||||
}
|
|
||||||
func (p pointer) toFloat64Ptr() **float64 {
|
|
||||||
return p.v.Interface().(**float64)
|
|
||||||
}
|
|
||||||
func (p pointer) toFloat64Slice() *[]float64 {
|
|
||||||
return p.v.Interface().(*[]float64)
|
|
||||||
}
|
|
||||||
func (p pointer) toFloat32() *float32 {
|
|
||||||
return p.v.Interface().(*float32)
|
|
||||||
}
|
|
||||||
func (p pointer) toFloat32Ptr() **float32 {
|
|
||||||
return p.v.Interface().(**float32)
|
|
||||||
}
|
|
||||||
func (p pointer) toFloat32Slice() *[]float32 {
|
|
||||||
return p.v.Interface().(*[]float32)
|
|
||||||
}
|
|
||||||
func (p pointer) toString() *string {
|
|
||||||
return p.v.Interface().(*string)
|
|
||||||
}
|
|
||||||
func (p pointer) toStringPtr() **string {
|
|
||||||
return p.v.Interface().(**string)
|
|
||||||
}
|
|
||||||
func (p pointer) toStringSlice() *[]string {
|
|
||||||
return p.v.Interface().(*[]string)
|
|
||||||
}
|
|
||||||
func (p pointer) toBytes() *[]byte {
|
|
||||||
return p.v.Interface().(*[]byte)
|
|
||||||
}
|
|
||||||
func (p pointer) toBytesSlice() *[][]byte {
|
|
||||||
return p.v.Interface().(*[][]byte)
|
|
||||||
}
|
|
||||||
func (p pointer) toExtensions() *XXX_InternalExtensions {
|
|
||||||
return p.v.Interface().(*XXX_InternalExtensions)
|
|
||||||
}
|
|
||||||
func (p pointer) toOldExtensions() *map[int32]Extension {
|
|
||||||
return p.v.Interface().(*map[int32]Extension)
|
|
||||||
}
|
|
||||||
func (p pointer) getPointer() pointer {
|
|
||||||
return pointer{v: p.v.Elem()}
|
|
||||||
}
|
|
||||||
func (p pointer) setPointer(q pointer) {
|
|
||||||
p.v.Elem().Set(q.v)
|
|
||||||
}
|
|
||||||
func (p pointer) appendPointer(q pointer) {
|
|
||||||
grow(p.v.Elem()).Set(q.v)
|
|
||||||
}
|
|
||||||
|
|
||||||
// getPointerSlice copies []*T from p as a new []pointer.
|
|
||||||
// This behavior differs from the implementation in pointer_unsafe.go.
|
|
||||||
func (p pointer) getPointerSlice() []pointer {
|
|
||||||
if p.v.IsNil() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
n := p.v.Elem().Len()
|
|
||||||
s := make([]pointer, n)
|
|
||||||
for i := 0; i < n; i++ {
|
|
||||||
s[i] = pointer{v: p.v.Elem().Index(i)}
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
// setPointerSlice copies []pointer into p as a new []*T.
|
|
||||||
// This behavior differs from the implementation in pointer_unsafe.go.
|
|
||||||
func (p pointer) setPointerSlice(v []pointer) {
|
|
||||||
if v == nil {
|
|
||||||
p.v.Elem().Set(reflect.New(p.v.Elem().Type()).Elem())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
s := reflect.MakeSlice(p.v.Elem().Type(), 0, len(v))
|
|
||||||
for _, p := range v {
|
|
||||||
s = reflect.Append(s, p.v)
|
|
||||||
}
|
|
||||||
p.v.Elem().Set(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
// getInterfacePointer returns a pointer that points to the
|
|
||||||
// interface data of the interface pointed by p.
|
|
||||||
func (p pointer) getInterfacePointer() pointer {
|
|
||||||
if p.v.Elem().IsNil() {
|
|
||||||
return pointer{v: p.v.Elem()}
|
|
||||||
}
|
|
||||||
return pointer{v: p.v.Elem().Elem().Elem().Field(0).Addr()} // *interface -> interface -> *struct -> struct
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p pointer) asPointerTo(t reflect.Type) reflect.Value {
|
|
||||||
// TODO: check that p.v.Type().Elem() == t?
|
|
||||||
return p.v
|
|
||||||
}
|
|
||||||
|
|
||||||
func atomicLoadUnmarshalInfo(p **unmarshalInfo) *unmarshalInfo {
|
|
||||||
atomicLock.Lock()
|
|
||||||
defer atomicLock.Unlock()
|
|
||||||
return *p
|
|
||||||
}
|
|
||||||
func atomicStoreUnmarshalInfo(p **unmarshalInfo, v *unmarshalInfo) {
|
|
||||||
atomicLock.Lock()
|
|
||||||
defer atomicLock.Unlock()
|
|
||||||
*p = v
|
|
||||||
}
|
|
||||||
func atomicLoadMarshalInfo(p **marshalInfo) *marshalInfo {
|
|
||||||
atomicLock.Lock()
|
|
||||||
defer atomicLock.Unlock()
|
|
||||||
return *p
|
|
||||||
}
|
|
||||||
func atomicStoreMarshalInfo(p **marshalInfo, v *marshalInfo) {
|
|
||||||
atomicLock.Lock()
|
|
||||||
defer atomicLock.Unlock()
|
|
||||||
*p = v
|
|
||||||
}
|
|
||||||
func atomicLoadMergeInfo(p **mergeInfo) *mergeInfo {
|
|
||||||
atomicLock.Lock()
|
|
||||||
defer atomicLock.Unlock()
|
|
||||||
return *p
|
|
||||||
}
|
|
||||||
func atomicStoreMergeInfo(p **mergeInfo, v *mergeInfo) {
|
|
||||||
atomicLock.Lock()
|
|
||||||
defer atomicLock.Unlock()
|
|
||||||
*p = v
|
|
||||||
}
|
|
||||||
func atomicLoadDiscardInfo(p **discardInfo) *discardInfo {
|
|
||||||
atomicLock.Lock()
|
|
||||||
defer atomicLock.Unlock()
|
|
||||||
return *p
|
|
||||||
}
|
|
||||||
func atomicStoreDiscardInfo(p **discardInfo, v *discardInfo) {
|
|
||||||
atomicLock.Lock()
|
|
||||||
defer atomicLock.Unlock()
|
|
||||||
*p = v
|
|
||||||
}
|
|
||||||
|
|
||||||
var atomicLock sync.Mutex
|
|
|
@ -1,313 +0,0 @@
|
||||||
// Go support for Protocol Buffers - Google's data interchange format
|
|
||||||
//
|
|
||||||
// Copyright 2012 The Go Authors. All rights reserved.
|
|
||||||
// https://github.com/golang/protobuf
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without
|
|
||||||
// modification, are permitted provided that the following conditions are
|
|
||||||
// met:
|
|
||||||
//
|
|
||||||
// * Redistributions of source code must retain the above copyright
|
|
||||||
// notice, this list of conditions and the following disclaimer.
|
|
||||||
// * Redistributions in binary form must reproduce the above
|
|
||||||
// copyright notice, this list of conditions and the following disclaimer
|
|
||||||
// in the documentation and/or other materials provided with the
|
|
||||||
// distribution.
|
|
||||||
// * Neither the name of Google Inc. nor the names of its
|
|
||||||
// contributors may be used to endorse or promote products derived from
|
|
||||||
// this software without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
// +build !purego,!appengine,!js
|
|
||||||
|
|
||||||
// This file contains the implementation of the proto field accesses using package unsafe.
|
|
||||||
|
|
||||||
package proto
|
|
||||||
|
|
||||||
import (
|
|
||||||
"reflect"
|
|
||||||
"sync/atomic"
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
const unsafeAllowed = true
|
|
||||||
|
|
||||||
// A field identifies a field in a struct, accessible from a pointer.
|
|
||||||
// In this implementation, a field is identified by its byte offset from the start of the struct.
|
|
||||||
type field uintptr
|
|
||||||
|
|
||||||
// toField returns a field equivalent to the given reflect field.
|
|
||||||
func toField(f *reflect.StructField) field {
|
|
||||||
return field(f.Offset)
|
|
||||||
}
|
|
||||||
|
|
||||||
// invalidField is an invalid field identifier.
|
|
||||||
const invalidField = ^field(0)
|
|
||||||
|
|
||||||
// zeroField is a noop when calling pointer.offset.
|
|
||||||
const zeroField = field(0)
|
|
||||||
|
|
||||||
// IsValid reports whether the field identifier is valid.
|
|
||||||
func (f field) IsValid() bool {
|
|
||||||
return f != invalidField
|
|
||||||
}
|
|
||||||
|
|
||||||
// The pointer type below is for the new table-driven encoder/decoder.
|
|
||||||
// The implementation here uses unsafe.Pointer to create a generic pointer.
|
|
||||||
// In pointer_reflect.go we use reflect instead of unsafe to implement
|
|
||||||
// the same (but slower) interface.
|
|
||||||
type pointer struct {
|
|
||||||
p unsafe.Pointer
|
|
||||||
}
|
|
||||||
|
|
||||||
// size of pointer
|
|
||||||
var ptrSize = unsafe.Sizeof(uintptr(0))
|
|
||||||
|
|
||||||
// toPointer converts an interface of pointer type to a pointer
|
|
||||||
// that points to the same target.
|
|
||||||
func toPointer(i *Message) pointer {
|
|
||||||
// Super-tricky - read pointer out of data word of interface value.
|
|
||||||
// Saves ~25ns over the equivalent:
|
|
||||||
// return valToPointer(reflect.ValueOf(*i))
|
|
||||||
return pointer{p: (*[2]unsafe.Pointer)(unsafe.Pointer(i))[1]}
|
|
||||||
}
|
|
||||||
|
|
||||||
// toAddrPointer converts an interface to a pointer that points to
|
|
||||||
// the interface data.
|
|
||||||
func toAddrPointer(i *interface{}, isptr, deref bool) (p pointer) {
|
|
||||||
// Super-tricky - read or get the address of data word of interface value.
|
|
||||||
if isptr {
|
|
||||||
// The interface is of pointer type, thus it is a direct interface.
|
|
||||||
// The data word is the pointer data itself. We take its address.
|
|
||||||
p = pointer{p: unsafe.Pointer(uintptr(unsafe.Pointer(i)) + ptrSize)}
|
|
||||||
} else {
|
|
||||||
// The interface is not of pointer type. The data word is the pointer
|
|
||||||
// to the data.
|
|
||||||
p = pointer{p: (*[2]unsafe.Pointer)(unsafe.Pointer(i))[1]}
|
|
||||||
}
|
|
||||||
if deref {
|
|
||||||
p.p = *(*unsafe.Pointer)(p.p)
|
|
||||||
}
|
|
||||||
return p
|
|
||||||
}
|
|
||||||
|
|
||||||
// valToPointer converts v to a pointer. v must be of pointer type.
|
|
||||||
func valToPointer(v reflect.Value) pointer {
|
|
||||||
return pointer{p: unsafe.Pointer(v.Pointer())}
|
|
||||||
}
|
|
||||||
|
|
||||||
// offset converts from a pointer to a structure to a pointer to
|
|
||||||
// one of its fields.
|
|
||||||
func (p pointer) offset(f field) pointer {
|
|
||||||
// For safety, we should panic if !f.IsValid, however calling panic causes
|
|
||||||
// this to no longer be inlineable, which is a serious performance cost.
|
|
||||||
/*
|
|
||||||
if !f.IsValid() {
|
|
||||||
panic("invalid field")
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
return pointer{p: unsafe.Pointer(uintptr(p.p) + uintptr(f))}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p pointer) isNil() bool {
|
|
||||||
return p.p == nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p pointer) toInt64() *int64 {
|
|
||||||
return (*int64)(p.p)
|
|
||||||
}
|
|
||||||
func (p pointer) toInt64Ptr() **int64 {
|
|
||||||
return (**int64)(p.p)
|
|
||||||
}
|
|
||||||
func (p pointer) toInt64Slice() *[]int64 {
|
|
||||||
return (*[]int64)(p.p)
|
|
||||||
}
|
|
||||||
func (p pointer) toInt32() *int32 {
|
|
||||||
return (*int32)(p.p)
|
|
||||||
}
|
|
||||||
|
|
||||||
// See pointer_reflect.go for why toInt32Ptr/Slice doesn't exist.
|
|
||||||
/*
|
|
||||||
func (p pointer) toInt32Ptr() **int32 {
|
|
||||||
return (**int32)(p.p)
|
|
||||||
}
|
|
||||||
func (p pointer) toInt32Slice() *[]int32 {
|
|
||||||
return (*[]int32)(p.p)
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
func (p pointer) getInt32Ptr() *int32 {
|
|
||||||
return *(**int32)(p.p)
|
|
||||||
}
|
|
||||||
func (p pointer) setInt32Ptr(v int32) {
|
|
||||||
*(**int32)(p.p) = &v
|
|
||||||
}
|
|
||||||
|
|
||||||
// getInt32Slice loads a []int32 from p.
|
|
||||||
// The value returned is aliased with the original slice.
|
|
||||||
// This behavior differs from the implementation in pointer_reflect.go.
|
|
||||||
func (p pointer) getInt32Slice() []int32 {
|
|
||||||
return *(*[]int32)(p.p)
|
|
||||||
}
|
|
||||||
|
|
||||||
// setInt32Slice stores a []int32 to p.
|
|
||||||
// The value set is aliased with the input slice.
|
|
||||||
// This behavior differs from the implementation in pointer_reflect.go.
|
|
||||||
func (p pointer) setInt32Slice(v []int32) {
|
|
||||||
*(*[]int32)(p.p) = v
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Can we get rid of appendInt32Slice and use setInt32Slice instead?
|
|
||||||
func (p pointer) appendInt32Slice(v int32) {
|
|
||||||
s := (*[]int32)(p.p)
|
|
||||||
*s = append(*s, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p pointer) toUint64() *uint64 {
|
|
||||||
return (*uint64)(p.p)
|
|
||||||
}
|
|
||||||
func (p pointer) toUint64Ptr() **uint64 {
|
|
||||||
return (**uint64)(p.p)
|
|
||||||
}
|
|
||||||
func (p pointer) toUint64Slice() *[]uint64 {
|
|
||||||
return (*[]uint64)(p.p)
|
|
||||||
}
|
|
||||||
func (p pointer) toUint32() *uint32 {
|
|
||||||
return (*uint32)(p.p)
|
|
||||||
}
|
|
||||||
func (p pointer) toUint32Ptr() **uint32 {
|
|
||||||
return (**uint32)(p.p)
|
|
||||||
}
|
|
||||||
func (p pointer) toUint32Slice() *[]uint32 {
|
|
||||||
return (*[]uint32)(p.p)
|
|
||||||
}
|
|
||||||
func (p pointer) toBool() *bool {
|
|
||||||
return (*bool)(p.p)
|
|
||||||
}
|
|
||||||
func (p pointer) toBoolPtr() **bool {
|
|
||||||
return (**bool)(p.p)
|
|
||||||
}
|
|
||||||
func (p pointer) toBoolSlice() *[]bool {
|
|
||||||
return (*[]bool)(p.p)
|
|
||||||
}
|
|
||||||
func (p pointer) toFloat64() *float64 {
|
|
||||||
return (*float64)(p.p)
|
|
||||||
}
|
|
||||||
func (p pointer) toFloat64Ptr() **float64 {
|
|
||||||
return (**float64)(p.p)
|
|
||||||
}
|
|
||||||
func (p pointer) toFloat64Slice() *[]float64 {
|
|
||||||
return (*[]float64)(p.p)
|
|
||||||
}
|
|
||||||
func (p pointer) toFloat32() *float32 {
|
|
||||||
return (*float32)(p.p)
|
|
||||||
}
|
|
||||||
func (p pointer) toFloat32Ptr() **float32 {
|
|
||||||
return (**float32)(p.p)
|
|
||||||
}
|
|
||||||
func (p pointer) toFloat32Slice() *[]float32 {
|
|
||||||
return (*[]float32)(p.p)
|
|
||||||
}
|
|
||||||
func (p pointer) toString() *string {
|
|
||||||
return (*string)(p.p)
|
|
||||||
}
|
|
||||||
func (p pointer) toStringPtr() **string {
|
|
||||||
return (**string)(p.p)
|
|
||||||
}
|
|
||||||
func (p pointer) toStringSlice() *[]string {
|
|
||||||
return (*[]string)(p.p)
|
|
||||||
}
|
|
||||||
func (p pointer) toBytes() *[]byte {
|
|
||||||
return (*[]byte)(p.p)
|
|
||||||
}
|
|
||||||
func (p pointer) toBytesSlice() *[][]byte {
|
|
||||||
return (*[][]byte)(p.p)
|
|
||||||
}
|
|
||||||
func (p pointer) toExtensions() *XXX_InternalExtensions {
|
|
||||||
return (*XXX_InternalExtensions)(p.p)
|
|
||||||
}
|
|
||||||
func (p pointer) toOldExtensions() *map[int32]Extension {
|
|
||||||
return (*map[int32]Extension)(p.p)
|
|
||||||
}
|
|
||||||
|
|
||||||
// getPointerSlice loads []*T from p as a []pointer.
|
|
||||||
// The value returned is aliased with the original slice.
|
|
||||||
// This behavior differs from the implementation in pointer_reflect.go.
|
|
||||||
func (p pointer) getPointerSlice() []pointer {
|
|
||||||
// Super-tricky - p should point to a []*T where T is a
|
|
||||||
// message type. We load it as []pointer.
|
|
||||||
return *(*[]pointer)(p.p)
|
|
||||||
}
|
|
||||||
|
|
||||||
// setPointerSlice stores []pointer into p as a []*T.
|
|
||||||
// The value set is aliased with the input slice.
|
|
||||||
// This behavior differs from the implementation in pointer_reflect.go.
|
|
||||||
func (p pointer) setPointerSlice(v []pointer) {
|
|
||||||
// Super-tricky - p should point to a []*T where T is a
|
|
||||||
// message type. We store it as []pointer.
|
|
||||||
*(*[]pointer)(p.p) = v
|
|
||||||
}
|
|
||||||
|
|
||||||
// getPointer loads the pointer at p and returns it.
|
|
||||||
func (p pointer) getPointer() pointer {
|
|
||||||
return pointer{p: *(*unsafe.Pointer)(p.p)}
|
|
||||||
}
|
|
||||||
|
|
||||||
// setPointer stores the pointer q at p.
|
|
||||||
func (p pointer) setPointer(q pointer) {
|
|
||||||
*(*unsafe.Pointer)(p.p) = q.p
|
|
||||||
}
|
|
||||||
|
|
||||||
// append q to the slice pointed to by p.
|
|
||||||
func (p pointer) appendPointer(q pointer) {
|
|
||||||
s := (*[]unsafe.Pointer)(p.p)
|
|
||||||
*s = append(*s, q.p)
|
|
||||||
}
|
|
||||||
|
|
||||||
// getInterfacePointer returns a pointer that points to the
|
|
||||||
// interface data of the interface pointed by p.
|
|
||||||
func (p pointer) getInterfacePointer() pointer {
|
|
||||||
// Super-tricky - read pointer out of data word of interface value.
|
|
||||||
return pointer{p: (*(*[2]unsafe.Pointer)(p.p))[1]}
|
|
||||||
}
|
|
||||||
|
|
||||||
// asPointerTo returns a reflect.Value that is a pointer to an
|
|
||||||
// object of type t stored at p.
|
|
||||||
func (p pointer) asPointerTo(t reflect.Type) reflect.Value {
|
|
||||||
return reflect.NewAt(t, p.p)
|
|
||||||
}
|
|
||||||
|
|
||||||
func atomicLoadUnmarshalInfo(p **unmarshalInfo) *unmarshalInfo {
|
|
||||||
return (*unmarshalInfo)(atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(p))))
|
|
||||||
}
|
|
||||||
func atomicStoreUnmarshalInfo(p **unmarshalInfo, v *unmarshalInfo) {
|
|
||||||
atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(p)), unsafe.Pointer(v))
|
|
||||||
}
|
|
||||||
func atomicLoadMarshalInfo(p **marshalInfo) *marshalInfo {
|
|
||||||
return (*marshalInfo)(atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(p))))
|
|
||||||
}
|
|
||||||
func atomicStoreMarshalInfo(p **marshalInfo, v *marshalInfo) {
|
|
||||||
atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(p)), unsafe.Pointer(v))
|
|
||||||
}
|
|
||||||
func atomicLoadMergeInfo(p **mergeInfo) *mergeInfo {
|
|
||||||
return (*mergeInfo)(atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(p))))
|
|
||||||
}
|
|
||||||
func atomicStoreMergeInfo(p **mergeInfo, v *mergeInfo) {
|
|
||||||
atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(p)), unsafe.Pointer(v))
|
|
||||||
}
|
|
||||||
func atomicLoadDiscardInfo(p **discardInfo) *discardInfo {
|
|
||||||
return (*discardInfo)(atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(p))))
|
|
||||||
}
|
|
||||||
func atomicStoreDiscardInfo(p **discardInfo, v *discardInfo) {
|
|
||||||
atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(p)), unsafe.Pointer(v))
|
|
||||||
}
|
|
|
@ -1,162 +1,104 @@
|
||||||
// Go support for Protocol Buffers - Google's data interchange format
|
|
||||||
//
|
|
||||||
// Copyright 2010 The Go Authors. All rights reserved.
|
// Copyright 2010 The Go Authors. All rights reserved.
|
||||||
// https://github.com/golang/protobuf
|
// Use of this source code is governed by a BSD-style
|
||||||
//
|
// license that can be found in the LICENSE file.
|
||||||
// Redistribution and use in source and binary forms, with or without
|
|
||||||
// modification, are permitted provided that the following conditions are
|
|
||||||
// met:
|
|
||||||
//
|
|
||||||
// * Redistributions of source code must retain the above copyright
|
|
||||||
// notice, this list of conditions and the following disclaimer.
|
|
||||||
// * Redistributions in binary form must reproduce the above
|
|
||||||
// copyright notice, this list of conditions and the following disclaimer
|
|
||||||
// in the documentation and/or other materials provided with the
|
|
||||||
// distribution.
|
|
||||||
// * Neither the name of Google Inc. nor the names of its
|
|
||||||
// contributors may be used to endorse or promote products derived from
|
|
||||||
// this software without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
package proto
|
package proto
|
||||||
|
|
||||||
/*
|
|
||||||
* Routines for encoding data into the wire format for protocol buffers.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
|
||||||
"reflect"
|
"reflect"
|
||||||
"sort"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"google.golang.org/protobuf/reflect/protoreflect"
|
||||||
|
"google.golang.org/protobuf/runtime/protoimpl"
|
||||||
)
|
)
|
||||||
|
|
||||||
const debug bool = false
|
// StructProperties represents protocol buffer type information for a
|
||||||
|
// generated protobuf message in the open-struct API.
|
||||||
// Constants that identify the encoding of a value on the wire.
|
//
|
||||||
const (
|
// Deprecated: Do not use.
|
||||||
WireVarint = 0
|
|
||||||
WireFixed64 = 1
|
|
||||||
WireBytes = 2
|
|
||||||
WireStartGroup = 3
|
|
||||||
WireEndGroup = 4
|
|
||||||
WireFixed32 = 5
|
|
||||||
)
|
|
||||||
|
|
||||||
// tagMap is an optimization over map[int]int for typical protocol buffer
|
|
||||||
// use-cases. Encoded protocol buffers are often in tag order with small tag
|
|
||||||
// numbers.
|
|
||||||
type tagMap struct {
|
|
||||||
fastTags []int
|
|
||||||
slowTags map[int]int
|
|
||||||
}
|
|
||||||
|
|
||||||
// tagMapFastLimit is the upper bound on the tag number that will be stored in
|
|
||||||
// the tagMap slice rather than its map.
|
|
||||||
const tagMapFastLimit = 1024
|
|
||||||
|
|
||||||
func (p *tagMap) get(t int) (int, bool) {
|
|
||||||
if t > 0 && t < tagMapFastLimit {
|
|
||||||
if t >= len(p.fastTags) {
|
|
||||||
return 0, false
|
|
||||||
}
|
|
||||||
fi := p.fastTags[t]
|
|
||||||
return fi, fi >= 0
|
|
||||||
}
|
|
||||||
fi, ok := p.slowTags[t]
|
|
||||||
return fi, ok
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *tagMap) put(t int, fi int) {
|
|
||||||
if t > 0 && t < tagMapFastLimit {
|
|
||||||
for len(p.fastTags) < t+1 {
|
|
||||||
p.fastTags = append(p.fastTags, -1)
|
|
||||||
}
|
|
||||||
p.fastTags[t] = fi
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if p.slowTags == nil {
|
|
||||||
p.slowTags = make(map[int]int)
|
|
||||||
}
|
|
||||||
p.slowTags[t] = fi
|
|
||||||
}
|
|
||||||
|
|
||||||
// StructProperties represents properties for all the fields of a struct.
|
|
||||||
// decoderTags and decoderOrigNames should only be used by the decoder.
|
|
||||||
type StructProperties struct {
|
type StructProperties struct {
|
||||||
Prop []*Properties // properties for each field
|
// Prop are the properties for each field.
|
||||||
reqCount int // required count
|
//
|
||||||
decoderTags tagMap // map from proto tag to struct field number
|
// Fields belonging to a oneof are stored in OneofTypes instead, with a
|
||||||
decoderOrigNames map[string]int // map from original name to struct field number
|
// single Properties representing the parent oneof held here.
|
||||||
order []int // list of struct field numbers in tag order
|
//
|
||||||
|
// The order of Prop matches the order of fields in the Go struct.
|
||||||
|
// Struct fields that are not related to protobufs have a "XXX_" prefix
|
||||||
|
// in the Properties.Name and must be ignored by the user.
|
||||||
|
Prop []*Properties
|
||||||
|
|
||||||
// OneofTypes contains information about the oneof fields in this message.
|
// OneofTypes contains information about the oneof fields in this message.
|
||||||
// It is keyed by the original name of a field.
|
// It is keyed by the protobuf field name.
|
||||||
OneofTypes map[string]*OneofProperties
|
OneofTypes map[string]*OneofProperties
|
||||||
}
|
}
|
||||||
|
|
||||||
// OneofProperties represents information about a specific field in a oneof.
|
// Properties represents the type information for a protobuf message field.
|
||||||
type OneofProperties struct {
|
//
|
||||||
Type reflect.Type // pointer to generated struct type for this oneof field
|
// Deprecated: Do not use.
|
||||||
Field int // struct field number of the containing oneof in the message
|
|
||||||
Prop *Properties
|
|
||||||
}
|
|
||||||
|
|
||||||
// Implement the sorting interface so we can sort the fields in tag order, as recommended by the spec.
|
|
||||||
// See encode.go, (*Buffer).enc_struct.
|
|
||||||
|
|
||||||
func (sp *StructProperties) Len() int { return len(sp.order) }
|
|
||||||
func (sp *StructProperties) Less(i, j int) bool {
|
|
||||||
return sp.Prop[sp.order[i]].Tag < sp.Prop[sp.order[j]].Tag
|
|
||||||
}
|
|
||||||
func (sp *StructProperties) Swap(i, j int) { sp.order[i], sp.order[j] = sp.order[j], sp.order[i] }
|
|
||||||
|
|
||||||
// Properties represents the protocol-specific behavior of a single struct field.
|
|
||||||
type Properties struct {
|
type Properties struct {
|
||||||
Name string // name of the field, for error messages
|
// Name is a placeholder name with little meaningful semantic value.
|
||||||
OrigName string // original name before protocol compiler (always set)
|
// If the name has an "XXX_" prefix, the entire Properties must be ignored.
|
||||||
JSONName string // name to use for JSON; determined by protoc
|
Name string
|
||||||
|
// OrigName is the protobuf field name or oneof name.
|
||||||
|
OrigName string
|
||||||
|
// JSONName is the JSON name for the protobuf field.
|
||||||
|
JSONName string
|
||||||
|
// Enum is a placeholder name for enums.
|
||||||
|
// For historical reasons, this is neither the Go name for the enum,
|
||||||
|
// nor the protobuf name for the enum.
|
||||||
|
Enum string // Deprecated: Do not use.
|
||||||
|
// Weak contains the full name of the weakly referenced message.
|
||||||
|
Weak string
|
||||||
|
// Wire is a string representation of the wire type.
|
||||||
Wire string
|
Wire string
|
||||||
|
// WireType is the protobuf wire type for the field.
|
||||||
WireType int
|
WireType int
|
||||||
|
// Tag is the protobuf field number.
|
||||||
Tag int
|
Tag int
|
||||||
|
// Required reports whether this is a required field.
|
||||||
Required bool
|
Required bool
|
||||||
|
// Optional reports whether this is a optional field.
|
||||||
Optional bool
|
Optional bool
|
||||||
|
// Repeated reports whether this is a repeated field.
|
||||||
Repeated bool
|
Repeated bool
|
||||||
Packed bool // relevant for repeated primitives only
|
// Packed reports whether this is a packed repeated field of scalars.
|
||||||
Enum string // set for enum types only
|
Packed bool
|
||||||
proto3 bool // whether this is known to be a proto3 field
|
// Proto3 reports whether this field operates under the proto3 syntax.
|
||||||
oneof bool // whether this is a oneof field
|
Proto3 bool
|
||||||
|
// Oneof reports whether this field belongs within a oneof.
|
||||||
|
Oneof bool
|
||||||
|
|
||||||
Default string // default value
|
// Default is the default value in string form.
|
||||||
HasDefault bool // whether an explicit default was provided
|
Default string
|
||||||
|
// HasDefault reports whether the field has a default value.
|
||||||
|
HasDefault bool
|
||||||
|
|
||||||
stype reflect.Type // set for struct types only
|
// MapKeyProp is the properties for the key field for a map field.
|
||||||
sprop *StructProperties // set for struct types only
|
MapKeyProp *Properties
|
||||||
|
// MapValProp is the properties for the value field for a map field.
|
||||||
|
MapValProp *Properties
|
||||||
|
}
|
||||||
|
|
||||||
mtype reflect.Type // set for map types only
|
// OneofProperties represents the type information for a protobuf oneof.
|
||||||
MapKeyProp *Properties // set for map types only
|
//
|
||||||
MapValProp *Properties // set for map types only
|
// Deprecated: Do not use.
|
||||||
|
type OneofProperties struct {
|
||||||
|
// Type is a pointer to the generated wrapper type for the field value.
|
||||||
|
// This is nil for messages that are not in the open-struct API.
|
||||||
|
Type reflect.Type
|
||||||
|
// Field is the index into StructProperties.Prop for the containing oneof.
|
||||||
|
Field int
|
||||||
|
// Prop is the properties for the field.
|
||||||
|
Prop *Properties
|
||||||
}
|
}
|
||||||
|
|
||||||
// String formats the properties in the protobuf struct field tag style.
|
// String formats the properties in the protobuf struct field tag style.
|
||||||
func (p *Properties) String() string {
|
func (p *Properties) String() string {
|
||||||
s := p.Wire
|
s := p.Wire
|
||||||
s += ","
|
s += "," + strconv.Itoa(p.Tag)
|
||||||
s += strconv.Itoa(p.Tag)
|
|
||||||
if p.Required {
|
if p.Required {
|
||||||
s += ",req"
|
s += ",req"
|
||||||
}
|
}
|
||||||
|
@ -170,18 +112,21 @@ func (p *Properties) String() string {
|
||||||
s += ",packed"
|
s += ",packed"
|
||||||
}
|
}
|
||||||
s += ",name=" + p.OrigName
|
s += ",name=" + p.OrigName
|
||||||
if p.JSONName != p.OrigName {
|
if p.JSONName != "" {
|
||||||
s += ",json=" + p.JSONName
|
s += ",json=" + p.JSONName
|
||||||
}
|
}
|
||||||
if p.proto3 {
|
|
||||||
s += ",proto3"
|
|
||||||
}
|
|
||||||
if p.oneof {
|
|
||||||
s += ",oneof"
|
|
||||||
}
|
|
||||||
if len(p.Enum) > 0 {
|
if len(p.Enum) > 0 {
|
||||||
s += ",enum=" + p.Enum
|
s += ",enum=" + p.Enum
|
||||||
}
|
}
|
||||||
|
if len(p.Weak) > 0 {
|
||||||
|
s += ",weak=" + p.Weak
|
||||||
|
}
|
||||||
|
if p.Proto3 {
|
||||||
|
s += ",proto3"
|
||||||
|
}
|
||||||
|
if p.Oneof {
|
||||||
|
s += ",oneof"
|
||||||
|
}
|
||||||
if p.HasDefault {
|
if p.HasDefault {
|
||||||
s += ",def=" + p.Default
|
s += ",def=" + p.Default
|
||||||
}
|
}
|
||||||
|
@ -189,356 +134,173 @@ func (p *Properties) String() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse populates p by parsing a string in the protobuf struct field tag style.
|
// Parse populates p by parsing a string in the protobuf struct field tag style.
|
||||||
func (p *Properties) Parse(s string) {
|
func (p *Properties) Parse(tag string) {
|
||||||
// "bytes,49,opt,name=foo,def=hello!"
|
// For example: "bytes,49,opt,name=foo,def=hello!"
|
||||||
fields := strings.Split(s, ",") // breaks def=, but handled below.
|
for len(tag) > 0 {
|
||||||
if len(fields) < 2 {
|
i := strings.IndexByte(tag, ',')
|
||||||
log.Printf("proto: tag has too few fields: %q", s)
|
if i < 0 {
|
||||||
return
|
i = len(tag)
|
||||||
}
|
}
|
||||||
|
switch s := tag[:i]; {
|
||||||
p.Wire = fields[0]
|
case strings.HasPrefix(s, "name="):
|
||||||
switch p.Wire {
|
p.OrigName = s[len("name="):]
|
||||||
case "varint":
|
case strings.HasPrefix(s, "json="):
|
||||||
p.WireType = WireVarint
|
p.JSONName = s[len("json="):]
|
||||||
case "fixed32":
|
case strings.HasPrefix(s, "enum="):
|
||||||
p.WireType = WireFixed32
|
p.Enum = s[len("enum="):]
|
||||||
case "fixed64":
|
case strings.HasPrefix(s, "weak="):
|
||||||
p.WireType = WireFixed64
|
p.Weak = s[len("weak="):]
|
||||||
case "zigzag32":
|
case strings.Trim(s, "0123456789") == "":
|
||||||
p.WireType = WireVarint
|
n, _ := strconv.ParseUint(s, 10, 32)
|
||||||
case "zigzag64":
|
p.Tag = int(n)
|
||||||
p.WireType = WireVarint
|
case s == "opt":
|
||||||
case "bytes", "group":
|
|
||||||
p.WireType = WireBytes
|
|
||||||
// no numeric converter for non-numeric types
|
|
||||||
default:
|
|
||||||
log.Printf("proto: tag has unknown wire type: %q", s)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var err error
|
|
||||||
p.Tag, err = strconv.Atoi(fields[1])
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
outer:
|
|
||||||
for i := 2; i < len(fields); i++ {
|
|
||||||
f := fields[i]
|
|
||||||
switch {
|
|
||||||
case f == "req":
|
|
||||||
p.Required = true
|
|
||||||
case f == "opt":
|
|
||||||
p.Optional = true
|
p.Optional = true
|
||||||
case f == "rep":
|
case s == "req":
|
||||||
|
p.Required = true
|
||||||
|
case s == "rep":
|
||||||
p.Repeated = true
|
p.Repeated = true
|
||||||
case f == "packed":
|
case s == "varint" || s == "zigzag32" || s == "zigzag64":
|
||||||
|
p.Wire = s
|
||||||
|
p.WireType = WireVarint
|
||||||
|
case s == "fixed32":
|
||||||
|
p.Wire = s
|
||||||
|
p.WireType = WireFixed32
|
||||||
|
case s == "fixed64":
|
||||||
|
p.Wire = s
|
||||||
|
p.WireType = WireFixed64
|
||||||
|
case s == "bytes":
|
||||||
|
p.Wire = s
|
||||||
|
p.WireType = WireBytes
|
||||||
|
case s == "group":
|
||||||
|
p.Wire = s
|
||||||
|
p.WireType = WireStartGroup
|
||||||
|
case s == "packed":
|
||||||
p.Packed = true
|
p.Packed = true
|
||||||
case strings.HasPrefix(f, "name="):
|
case s == "proto3":
|
||||||
p.OrigName = f[5:]
|
p.Proto3 = true
|
||||||
case strings.HasPrefix(f, "json="):
|
case s == "oneof":
|
||||||
p.JSONName = f[5:]
|
p.Oneof = true
|
||||||
case strings.HasPrefix(f, "enum="):
|
case strings.HasPrefix(s, "def="):
|
||||||
p.Enum = f[5:]
|
// The default tag is special in that everything afterwards is the
|
||||||
case f == "proto3":
|
// default regardless of the presence of commas.
|
||||||
p.proto3 = true
|
|
||||||
case f == "oneof":
|
|
||||||
p.oneof = true
|
|
||||||
case strings.HasPrefix(f, "def="):
|
|
||||||
p.HasDefault = true
|
p.HasDefault = true
|
||||||
p.Default = f[4:] // rest of string
|
p.Default, i = tag[len("def="):], len(tag)
|
||||||
if i+1 < len(fields) {
|
|
||||||
// Commas aren't escaped, and def is always last.
|
|
||||||
p.Default += "," + strings.Join(fields[i+1:], ",")
|
|
||||||
break outer
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
tag = strings.TrimPrefix(tag[i:], ",")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var protoMessageType = reflect.TypeOf((*Message)(nil)).Elem()
|
|
||||||
|
|
||||||
// setFieldProps initializes the field properties for submessages and maps.
|
|
||||||
func (p *Properties) setFieldProps(typ reflect.Type, f *reflect.StructField, lockGetProp bool) {
|
|
||||||
switch t1 := typ; t1.Kind() {
|
|
||||||
case reflect.Ptr:
|
|
||||||
if t1.Elem().Kind() == reflect.Struct {
|
|
||||||
p.stype = t1.Elem()
|
|
||||||
}
|
|
||||||
|
|
||||||
case reflect.Slice:
|
|
||||||
if t2 := t1.Elem(); t2.Kind() == reflect.Ptr && t2.Elem().Kind() == reflect.Struct {
|
|
||||||
p.stype = t2.Elem()
|
|
||||||
}
|
|
||||||
|
|
||||||
case reflect.Map:
|
|
||||||
p.mtype = t1
|
|
||||||
p.MapKeyProp = &Properties{}
|
|
||||||
p.MapKeyProp.init(reflect.PtrTo(p.mtype.Key()), "Key", f.Tag.Get("protobuf_key"), nil, lockGetProp)
|
|
||||||
p.MapValProp = &Properties{}
|
|
||||||
vtype := p.mtype.Elem()
|
|
||||||
if vtype.Kind() != reflect.Ptr && vtype.Kind() != reflect.Slice {
|
|
||||||
// The value type is not a message (*T) or bytes ([]byte),
|
|
||||||
// so we need encoders for the pointer to this type.
|
|
||||||
vtype = reflect.PtrTo(vtype)
|
|
||||||
}
|
|
||||||
p.MapValProp.init(vtype, "Value", f.Tag.Get("protobuf_val"), nil, lockGetProp)
|
|
||||||
}
|
|
||||||
|
|
||||||
if p.stype != nil {
|
|
||||||
if lockGetProp {
|
|
||||||
p.sprop = GetProperties(p.stype)
|
|
||||||
} else {
|
|
||||||
p.sprop = getPropertiesLocked(p.stype)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
marshalerType = reflect.TypeOf((*Marshaler)(nil)).Elem()
|
|
||||||
)
|
|
||||||
|
|
||||||
// Init populates the properties from a protocol buffer struct tag.
|
// Init populates the properties from a protocol buffer struct tag.
|
||||||
|
//
|
||||||
|
// Deprecated: Do not use.
|
||||||
func (p *Properties) Init(typ reflect.Type, name, tag string, f *reflect.StructField) {
|
func (p *Properties) Init(typ reflect.Type, name, tag string, f *reflect.StructField) {
|
||||||
p.init(typ, name, tag, f, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Properties) init(typ reflect.Type, name, tag string, f *reflect.StructField, lockGetProp bool) {
|
|
||||||
// "bytes,49,opt,def=hello!"
|
|
||||||
p.Name = name
|
p.Name = name
|
||||||
p.OrigName = name
|
p.OrigName = name
|
||||||
if tag == "" {
|
if tag == "" {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
p.Parse(tag)
|
p.Parse(tag)
|
||||||
p.setFieldProps(typ, f, lockGetProp)
|
|
||||||
|
if typ != nil && typ.Kind() == reflect.Map {
|
||||||
|
p.MapKeyProp = new(Properties)
|
||||||
|
p.MapKeyProp.Init(nil, "Key", f.Tag.Get("protobuf_key"), nil)
|
||||||
|
p.MapValProp = new(Properties)
|
||||||
|
p.MapValProp.Init(nil, "Value", f.Tag.Get("protobuf_val"), nil)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var propertiesCache sync.Map // map[reflect.Type]*StructProperties
|
||||||
propertiesMu sync.RWMutex
|
|
||||||
propertiesMap = make(map[reflect.Type]*StructProperties)
|
|
||||||
)
|
|
||||||
|
|
||||||
// GetProperties returns the list of properties for the type represented by t.
|
// GetProperties returns the list of properties for the type represented by t,
|
||||||
// t must represent a generated struct type of a protocol message.
|
// which must be a generated protocol buffer message in the open-struct API,
|
||||||
|
// where protobuf message fields are represented by exported Go struct fields.
|
||||||
|
//
|
||||||
|
// Deprecated: Use protobuf reflection instead.
|
||||||
func GetProperties(t reflect.Type) *StructProperties {
|
func GetProperties(t reflect.Type) *StructProperties {
|
||||||
if t.Kind() != reflect.Struct {
|
if p, ok := propertiesCache.Load(t); ok {
|
||||||
panic("proto: type must have kind struct")
|
return p.(*StructProperties)
|
||||||
}
|
}
|
||||||
|
p, _ := propertiesCache.LoadOrStore(t, newProperties(t))
|
||||||
// Most calls to GetProperties in a long-running program will be
|
return p.(*StructProperties)
|
||||||
// retrieving details for types we have seen before.
|
|
||||||
propertiesMu.RLock()
|
|
||||||
sprop, ok := propertiesMap[t]
|
|
||||||
propertiesMu.RUnlock()
|
|
||||||
if ok {
|
|
||||||
return sprop
|
|
||||||
}
|
|
||||||
|
|
||||||
propertiesMu.Lock()
|
|
||||||
sprop = getPropertiesLocked(t)
|
|
||||||
propertiesMu.Unlock()
|
|
||||||
return sprop
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type (
|
func newProperties(t reflect.Type) *StructProperties {
|
||||||
oneofFuncsIface interface {
|
if t.Kind() != reflect.Struct {
|
||||||
XXX_OneofFuncs() (func(Message, *Buffer) error, func(Message, int, int, *Buffer) (bool, error), func(Message) int, []interface{})
|
panic(fmt.Sprintf("%v is not a generated message in the open-struct API", t))
|
||||||
}
|
|
||||||
oneofWrappersIface interface {
|
|
||||||
XXX_OneofWrappers() []interface{}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
// getPropertiesLocked requires that propertiesMu is held.
|
|
||||||
func getPropertiesLocked(t reflect.Type) *StructProperties {
|
|
||||||
if prop, ok := propertiesMap[t]; ok {
|
|
||||||
return prop
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var hasOneof bool
|
||||||
prop := new(StructProperties)
|
prop := new(StructProperties)
|
||||||
// in case of recursive protos, fill this in now.
|
|
||||||
propertiesMap[t] = prop
|
|
||||||
|
|
||||||
// build properties
|
|
||||||
prop.Prop = make([]*Properties, t.NumField())
|
|
||||||
prop.order = make([]int, t.NumField())
|
|
||||||
|
|
||||||
|
// Construct a list of properties for each field in the struct.
|
||||||
for i := 0; i < t.NumField(); i++ {
|
for i := 0; i < t.NumField(); i++ {
|
||||||
f := t.Field(i)
|
|
||||||
p := new(Properties)
|
p := new(Properties)
|
||||||
name := f.Name
|
f := t.Field(i)
|
||||||
p.init(f.Type, name, f.Tag.Get("protobuf"), &f, false)
|
tagField := f.Tag.Get("protobuf")
|
||||||
|
p.Init(f.Type, f.Name, tagField, &f)
|
||||||
|
|
||||||
oneof := f.Tag.Get("protobuf_oneof") // special case
|
tagOneof := f.Tag.Get("protobuf_oneof")
|
||||||
if oneof != "" {
|
if tagOneof != "" {
|
||||||
// Oneof fields don't use the traditional protobuf tag.
|
hasOneof = true
|
||||||
p.OrigName = oneof
|
p.OrigName = tagOneof
|
||||||
}
|
}
|
||||||
prop.Prop[i] = p
|
|
||||||
prop.order[i] = i
|
// Rename unrelated struct fields with the "XXX_" prefix since so much
|
||||||
if debug {
|
// user code simply checks for this to exclude special fields.
|
||||||
print(i, " ", f.Name, " ", t.String(), " ")
|
if tagField == "" && tagOneof == "" && !strings.HasPrefix(p.Name, "XXX_") {
|
||||||
if p.Tag > 0 {
|
p.Name = "XXX_" + p.Name
|
||||||
print(p.String())
|
p.OrigName = "XXX_" + p.OrigName
|
||||||
|
} else if p.Weak != "" {
|
||||||
|
p.Name = p.OrigName // avoid possible "XXX_" prefix on weak field
|
||||||
}
|
}
|
||||||
print("\n")
|
|
||||||
|
prop.Prop = append(prop.Prop, p)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Construct a mapping of oneof field names to properties.
|
||||||
|
if hasOneof {
|
||||||
|
var oneofWrappers []interface{}
|
||||||
|
if fn, ok := reflect.PtrTo(t).MethodByName("XXX_OneofFuncs"); ok {
|
||||||
|
oneofWrappers = fn.Func.Call([]reflect.Value{reflect.Zero(fn.Type.In(0))})[3].Interface().([]interface{})
|
||||||
|
}
|
||||||
|
if fn, ok := reflect.PtrTo(t).MethodByName("XXX_OneofWrappers"); ok {
|
||||||
|
oneofWrappers = fn.Func.Call([]reflect.Value{reflect.Zero(fn.Type.In(0))})[0].Interface().([]interface{})
|
||||||
|
}
|
||||||
|
if m, ok := reflect.Zero(reflect.PtrTo(t)).Interface().(protoreflect.ProtoMessage); ok {
|
||||||
|
if m, ok := m.ProtoReflect().(interface{ ProtoMessageInfo() *protoimpl.MessageInfo }); ok {
|
||||||
|
oneofWrappers = m.ProtoMessageInfo().OneofWrappers
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Re-order prop.order.
|
|
||||||
sort.Sort(prop)
|
|
||||||
|
|
||||||
var oots []interface{}
|
|
||||||
switch m := reflect.Zero(reflect.PtrTo(t)).Interface().(type) {
|
|
||||||
case oneofFuncsIface:
|
|
||||||
_, _, _, oots = m.XXX_OneofFuncs()
|
|
||||||
case oneofWrappersIface:
|
|
||||||
oots = m.XXX_OneofWrappers()
|
|
||||||
}
|
|
||||||
if len(oots) > 0 {
|
|
||||||
// Interpret oneof metadata.
|
|
||||||
prop.OneofTypes = make(map[string]*OneofProperties)
|
prop.OneofTypes = make(map[string]*OneofProperties)
|
||||||
for _, oot := range oots {
|
for _, wrapper := range oneofWrappers {
|
||||||
oop := &OneofProperties{
|
p := &OneofProperties{
|
||||||
Type: reflect.ValueOf(oot).Type(), // *T
|
Type: reflect.ValueOf(wrapper).Type(), // *T
|
||||||
Prop: new(Properties),
|
Prop: new(Properties),
|
||||||
}
|
}
|
||||||
sft := oop.Type.Elem().Field(0)
|
f := p.Type.Elem().Field(0)
|
||||||
oop.Prop.Name = sft.Name
|
p.Prop.Name = f.Name
|
||||||
oop.Prop.Parse(sft.Tag.Get("protobuf"))
|
p.Prop.Parse(f.Tag.Get("protobuf"))
|
||||||
// There will be exactly one interface field that
|
|
||||||
// this new value is assignable to.
|
|
||||||
for i := 0; i < t.NumField(); i++ {
|
|
||||||
f := t.Field(i)
|
|
||||||
if f.Type.Kind() != reflect.Interface {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if !oop.Type.AssignableTo(f.Type) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
oop.Field = i
|
|
||||||
break
|
|
||||||
}
|
|
||||||
prop.OneofTypes[oop.Prop.OrigName] = oop
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// build required counts
|
// Determine the struct field that contains this oneof.
|
||||||
// build tags
|
// Each wrapper is assignable to exactly one parent field.
|
||||||
reqCount := 0
|
var foundOneof bool
|
||||||
prop.decoderOrigNames = make(map[string]int)
|
for i := 0; i < t.NumField() && !foundOneof; i++ {
|
||||||
for i, p := range prop.Prop {
|
if p.Type.AssignableTo(t.Field(i).Type) {
|
||||||
if strings.HasPrefix(p.Name, "XXX_") {
|
p.Field = i
|
||||||
// Internal fields should not appear in tags/origNames maps.
|
foundOneof = true
|
||||||
// They are handled specially when encoding and decoding.
|
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
if p.Required {
|
|
||||||
reqCount++
|
|
||||||
}
|
}
|
||||||
prop.decoderTags.put(p.Tag, i)
|
if !foundOneof {
|
||||||
prop.decoderOrigNames[p.OrigName] = i
|
panic(fmt.Sprintf("%v is not a generated message in the open-struct API", t))
|
||||||
|
}
|
||||||
|
prop.OneofTypes[p.Prop.OrigName] = p
|
||||||
|
}
|
||||||
}
|
}
|
||||||
prop.reqCount = reqCount
|
|
||||||
|
|
||||||
return prop
|
return prop
|
||||||
}
|
}
|
||||||
|
|
||||||
// A global registry of enum types.
|
func (sp *StructProperties) Len() int { return len(sp.Prop) }
|
||||||
// The generated code will register the generated maps by calling RegisterEnum.
|
func (sp *StructProperties) Less(i, j int) bool { return false }
|
||||||
|
func (sp *StructProperties) Swap(i, j int) { return }
|
||||||
var enumValueMaps = make(map[string]map[string]int32)
|
|
||||||
|
|
||||||
// RegisterEnum is called from the generated code to install the enum descriptor
|
|
||||||
// maps into the global table to aid parsing text format protocol buffers.
|
|
||||||
func RegisterEnum(typeName string, unusedNameMap map[int32]string, valueMap map[string]int32) {
|
|
||||||
if _, ok := enumValueMaps[typeName]; ok {
|
|
||||||
panic("proto: duplicate enum registered: " + typeName)
|
|
||||||
}
|
|
||||||
enumValueMaps[typeName] = valueMap
|
|
||||||
}
|
|
||||||
|
|
||||||
// EnumValueMap returns the mapping from names to integers of the
|
|
||||||
// enum type enumType, or a nil if not found.
|
|
||||||
func EnumValueMap(enumType string) map[string]int32 {
|
|
||||||
return enumValueMaps[enumType]
|
|
||||||
}
|
|
||||||
|
|
||||||
// A registry of all linked message types.
|
|
||||||
// The string is a fully-qualified proto name ("pkg.Message").
|
|
||||||
var (
|
|
||||||
protoTypedNils = make(map[string]Message) // a map from proto names to typed nil pointers
|
|
||||||
protoMapTypes = make(map[string]reflect.Type) // a map from proto names to map types
|
|
||||||
revProtoTypes = make(map[reflect.Type]string)
|
|
||||||
)
|
|
||||||
|
|
||||||
// RegisterType is called from generated code and maps from the fully qualified
|
|
||||||
// proto name to the type (pointer to struct) of the protocol buffer.
|
|
||||||
func RegisterType(x Message, name string) {
|
|
||||||
if _, ok := protoTypedNils[name]; ok {
|
|
||||||
// TODO: Some day, make this a panic.
|
|
||||||
log.Printf("proto: duplicate proto type registered: %s", name)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
t := reflect.TypeOf(x)
|
|
||||||
if v := reflect.ValueOf(x); v.Kind() == reflect.Ptr && v.Pointer() == 0 {
|
|
||||||
// Generated code always calls RegisterType with nil x.
|
|
||||||
// This check is just for extra safety.
|
|
||||||
protoTypedNils[name] = x
|
|
||||||
} else {
|
|
||||||
protoTypedNils[name] = reflect.Zero(t).Interface().(Message)
|
|
||||||
}
|
|
||||||
revProtoTypes[t] = name
|
|
||||||
}
|
|
||||||
|
|
||||||
// RegisterMapType is called from generated code and maps from the fully qualified
|
|
||||||
// proto name to the native map type of the proto map definition.
|
|
||||||
func RegisterMapType(x interface{}, name string) {
|
|
||||||
if reflect.TypeOf(x).Kind() != reflect.Map {
|
|
||||||
panic(fmt.Sprintf("RegisterMapType(%T, %q); want map", x, name))
|
|
||||||
}
|
|
||||||
if _, ok := protoMapTypes[name]; ok {
|
|
||||||
log.Printf("proto: duplicate proto type registered: %s", name)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
t := reflect.TypeOf(x)
|
|
||||||
protoMapTypes[name] = t
|
|
||||||
revProtoTypes[t] = name
|
|
||||||
}
|
|
||||||
|
|
||||||
// MessageName returns the fully-qualified proto name for the given message type.
|
|
||||||
func MessageName(x Message) string {
|
|
||||||
type xname interface {
|
|
||||||
XXX_MessageName() string
|
|
||||||
}
|
|
||||||
if m, ok := x.(xname); ok {
|
|
||||||
return m.XXX_MessageName()
|
|
||||||
}
|
|
||||||
return revProtoTypes[reflect.TypeOf(x)]
|
|
||||||
}
|
|
||||||
|
|
||||||
// MessageType returns the message type (pointer to struct) for a named message.
|
|
||||||
// The type is not guaranteed to implement proto.Message if the name refers to a
|
|
||||||
// map entry.
|
|
||||||
func MessageType(name string) reflect.Type {
|
|
||||||
if t, ok := protoTypedNils[name]; ok {
|
|
||||||
return reflect.TypeOf(t)
|
|
||||||
}
|
|
||||||
return protoMapTypes[name]
|
|
||||||
}
|
|
||||||
|
|
||||||
// A registry of all linked proto files.
|
|
||||||
var (
|
|
||||||
protoFiles = make(map[string][]byte) // file name => fileDescriptor
|
|
||||||
)
|
|
||||||
|
|
||||||
// RegisterFile is called from generated code and maps from the
|
|
||||||
// full file name of a .proto file to its compressed FileDescriptorProto.
|
|
||||||
func RegisterFile(filename string, fileDescriptor []byte) {
|
|
||||||
protoFiles[filename] = fileDescriptor
|
|
||||||
}
|
|
||||||
|
|
||||||
// FileDescriptor returns the compressed FileDescriptorProto for a .proto file.
|
|
||||||
func FileDescriptor(filename string) []byte { return protoFiles[filename] }
|
|
||||||
|
|
|
@ -0,0 +1,167 @@
|
||||||
|
// Copyright 2019 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Package proto provides functionality for handling protocol buffer messages.
|
||||||
|
// In particular, it provides marshaling and unmarshaling between a protobuf
|
||||||
|
// message and the binary wire format.
|
||||||
|
//
|
||||||
|
// See https://developers.google.com/protocol-buffers/docs/gotutorial for
|
||||||
|
// more information.
|
||||||
|
//
|
||||||
|
// Deprecated: Use the "google.golang.org/protobuf/proto" package instead.
|
||||||
|
package proto
|
||||||
|
|
||||||
|
import (
|
||||||
|
protoV2 "google.golang.org/protobuf/proto"
|
||||||
|
"google.golang.org/protobuf/reflect/protoreflect"
|
||||||
|
"google.golang.org/protobuf/runtime/protoiface"
|
||||||
|
"google.golang.org/protobuf/runtime/protoimpl"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
ProtoPackageIsVersion1 = true
|
||||||
|
ProtoPackageIsVersion2 = true
|
||||||
|
ProtoPackageIsVersion3 = true
|
||||||
|
ProtoPackageIsVersion4 = true
|
||||||
|
)
|
||||||
|
|
||||||
|
// GeneratedEnum is any enum type generated by protoc-gen-go
|
||||||
|
// which is a named int32 kind.
|
||||||
|
// This type exists for documentation purposes.
|
||||||
|
type GeneratedEnum interface{}
|
||||||
|
|
||||||
|
// GeneratedMessage is any message type generated by protoc-gen-go
|
||||||
|
// which is a pointer to a named struct kind.
|
||||||
|
// This type exists for documentation purposes.
|
||||||
|
type GeneratedMessage interface{}
|
||||||
|
|
||||||
|
// Message is a protocol buffer message.
|
||||||
|
//
|
||||||
|
// This is the v1 version of the message interface and is marginally better
|
||||||
|
// than an empty interface as it lacks any method to programatically interact
|
||||||
|
// with the contents of the message.
|
||||||
|
//
|
||||||
|
// A v2 message is declared in "google.golang.org/protobuf/proto".Message and
|
||||||
|
// exposes protobuf reflection as a first-class feature of the interface.
|
||||||
|
//
|
||||||
|
// To convert a v1 message to a v2 message, use the MessageV2 function.
|
||||||
|
// To convert a v2 message to a v1 message, use the MessageV1 function.
|
||||||
|
type Message = protoiface.MessageV1
|
||||||
|
|
||||||
|
// MessageV1 converts either a v1 or v2 message to a v1 message.
|
||||||
|
// It returns nil if m is nil.
|
||||||
|
func MessageV1(m GeneratedMessage) protoiface.MessageV1 {
|
||||||
|
return protoimpl.X.ProtoMessageV1Of(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MessageV2 converts either a v1 or v2 message to a v2 message.
|
||||||
|
// It returns nil if m is nil.
|
||||||
|
func MessageV2(m GeneratedMessage) protoV2.Message {
|
||||||
|
return protoimpl.X.ProtoMessageV2Of(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MessageReflect returns a reflective view for a message.
|
||||||
|
// It returns nil if m is nil.
|
||||||
|
func MessageReflect(m Message) protoreflect.Message {
|
||||||
|
return protoimpl.X.MessageOf(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Marshaler is implemented by messages that can marshal themselves.
|
||||||
|
// This interface is used by the following functions: Size, Marshal,
|
||||||
|
// Buffer.Marshal, and Buffer.EncodeMessage.
|
||||||
|
//
|
||||||
|
// Deprecated: Do not implement.
|
||||||
|
type Marshaler interface {
|
||||||
|
// Marshal formats the encoded bytes of the message.
|
||||||
|
// It should be deterministic and emit valid protobuf wire data.
|
||||||
|
// The caller takes ownership of the returned buffer.
|
||||||
|
Marshal() ([]byte, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshaler is implemented by messages that can unmarshal themselves.
|
||||||
|
// This interface is used by the following functions: Unmarshal, UnmarshalMerge,
|
||||||
|
// Buffer.Unmarshal, Buffer.DecodeMessage, and Buffer.DecodeGroup.
|
||||||
|
//
|
||||||
|
// Deprecated: Do not implement.
|
||||||
|
type Unmarshaler interface {
|
||||||
|
// Unmarshal parses the encoded bytes of the protobuf wire input.
|
||||||
|
// The provided buffer is only valid for during method call.
|
||||||
|
// It should not reset the receiver message.
|
||||||
|
Unmarshal([]byte) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Merger is implemented by messages that can merge themselves.
|
||||||
|
// This interface is used by the following functions: Clone and Merge.
|
||||||
|
//
|
||||||
|
// Deprecated: Do not implement.
|
||||||
|
type Merger interface {
|
||||||
|
// Merge merges the contents of src into the receiver message.
|
||||||
|
// It clones all data structures in src such that it aliases no mutable
|
||||||
|
// memory referenced by src.
|
||||||
|
Merge(src Message)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RequiredNotSetError is an error type returned when
|
||||||
|
// marshaling or unmarshaling a message with missing required fields.
|
||||||
|
type RequiredNotSetError struct {
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *RequiredNotSetError) Error() string {
|
||||||
|
if e.err != nil {
|
||||||
|
return e.err.Error()
|
||||||
|
}
|
||||||
|
return "proto: required field not set"
|
||||||
|
}
|
||||||
|
func (e *RequiredNotSetError) RequiredNotSet() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkRequiredNotSet(m protoV2.Message) error {
|
||||||
|
if err := protoV2.CheckInitialized(m); err != nil {
|
||||||
|
return &RequiredNotSetError{err: err}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clone returns a deep copy of src.
|
||||||
|
func Clone(src Message) Message {
|
||||||
|
return MessageV1(protoV2.Clone(MessageV2(src)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Merge merges src into dst, which must be messages of the same type.
|
||||||
|
//
|
||||||
|
// Populated scalar fields in src are copied to dst, while populated
|
||||||
|
// singular messages in src are merged into dst by recursively calling Merge.
|
||||||
|
// The elements of every list field in src is appended to the corresponded
|
||||||
|
// list fields in dst. The entries of every map field in src is copied into
|
||||||
|
// the corresponding map field in dst, possibly replacing existing entries.
|
||||||
|
// The unknown fields of src are appended to the unknown fields of dst.
|
||||||
|
func Merge(dst, src Message) {
|
||||||
|
protoV2.Merge(MessageV2(dst), MessageV2(src))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equal reports whether two messages are equal.
|
||||||
|
// If two messages marshal to the same bytes under deterministic serialization,
|
||||||
|
// then Equal is guaranteed to report true.
|
||||||
|
//
|
||||||
|
// Two messages are equal if they are the same protobuf message type,
|
||||||
|
// have the same set of populated known and extension field values,
|
||||||
|
// and the same set of unknown fields values.
|
||||||
|
//
|
||||||
|
// Scalar values are compared with the equivalent of the == operator in Go,
|
||||||
|
// except bytes values which are compared using bytes.Equal and
|
||||||
|
// floating point values which specially treat NaNs as equal.
|
||||||
|
// Message values are compared by recursively calling Equal.
|
||||||
|
// Lists are equal if each element value is also equal.
|
||||||
|
// Maps are equal if they have the same set of keys, where the pair of values
|
||||||
|
// for each key is also equal.
|
||||||
|
func Equal(x, y Message) bool {
|
||||||
|
return protoV2.Equal(MessageV2(x), MessageV2(y))
|
||||||
|
}
|
||||||
|
|
||||||
|
func isMessageSet(md protoreflect.MessageDescriptor) bool {
|
||||||
|
ms, ok := md.(interface{ IsMessageSet() bool })
|
||||||
|
return ok && ms.IsMessageSet()
|
||||||
|
}
|
|
@ -0,0 +1,323 @@
|
||||||
|
// Copyright 2019 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package proto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"compress/gzip"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"google.golang.org/protobuf/reflect/protoreflect"
|
||||||
|
"google.golang.org/protobuf/reflect/protoregistry"
|
||||||
|
"google.golang.org/protobuf/runtime/protoimpl"
|
||||||
|
)
|
||||||
|
|
||||||
|
// filePath is the path to the proto source file.
|
||||||
|
type filePath = string // e.g., "google/protobuf/descriptor.proto"
|
||||||
|
|
||||||
|
// fileDescGZIP is the compressed contents of the encoded FileDescriptorProto.
|
||||||
|
type fileDescGZIP = []byte
|
||||||
|
|
||||||
|
var fileCache sync.Map // map[filePath]fileDescGZIP
|
||||||
|
|
||||||
|
// RegisterFile is called from generated code to register the compressed
|
||||||
|
// FileDescriptorProto with the file path for a proto source file.
|
||||||
|
//
|
||||||
|
// Deprecated: Use protoregistry.GlobalFiles.Register instead.
|
||||||
|
func RegisterFile(s filePath, d fileDescGZIP) {
|
||||||
|
// Decompress the descriptor.
|
||||||
|
zr, err := gzip.NewReader(bytes.NewReader(d))
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("proto: invalid compressed file descriptor: %v", err))
|
||||||
|
}
|
||||||
|
b, err := ioutil.ReadAll(zr)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("proto: invalid compressed file descriptor: %v", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Construct a protoreflect.FileDescriptor from the raw descriptor.
|
||||||
|
// Note that DescBuilder.Build automatically registers the constructed
|
||||||
|
// file descriptor with the v2 registry.
|
||||||
|
protoimpl.DescBuilder{RawDescriptor: b}.Build()
|
||||||
|
|
||||||
|
// Locally cache the raw descriptor form for the file.
|
||||||
|
fileCache.Store(s, d)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FileDescriptor returns the compressed FileDescriptorProto given the file path
|
||||||
|
// for a proto source file. It returns nil if not found.
|
||||||
|
//
|
||||||
|
// Deprecated: Use protoregistry.GlobalFiles.RangeFilesByPath instead.
|
||||||
|
func FileDescriptor(s filePath) fileDescGZIP {
|
||||||
|
if v, ok := fileCache.Load(s); ok {
|
||||||
|
return v.(fileDescGZIP)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the descriptor in the v2 registry.
|
||||||
|
var b []byte
|
||||||
|
if fd, _ := protoregistry.GlobalFiles.FindFileByPath(s); fd != nil {
|
||||||
|
if fd, ok := fd.(interface{ ProtoLegacyRawDesc() []byte }); ok {
|
||||||
|
b = fd.ProtoLegacyRawDesc()
|
||||||
|
} else {
|
||||||
|
// TODO: Use protodesc.ToFileDescriptorProto to construct
|
||||||
|
// a descriptorpb.FileDescriptorProto and marshal it.
|
||||||
|
// However, doing so causes the proto package to have a dependency
|
||||||
|
// on descriptorpb, leading to cyclic dependency issues.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Locally cache the raw descriptor form for the file.
|
||||||
|
if len(b) > 0 {
|
||||||
|
v, _ := fileCache.LoadOrStore(s, protoimpl.X.CompressGZIP(b))
|
||||||
|
return v.(fileDescGZIP)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// enumName is the name of an enum. For historical reasons, the enum name is
|
||||||
|
// neither the full Go name nor the full protobuf name of the enum.
|
||||||
|
// The name is the dot-separated combination of just the proto package that the
|
||||||
|
// enum is declared within followed by the Go type name of the generated enum.
|
||||||
|
type enumName = string // e.g., "my.proto.package.GoMessage_GoEnum"
|
||||||
|
|
||||||
|
// enumsByName maps enum values by name to their numeric counterpart.
|
||||||
|
type enumsByName = map[string]int32
|
||||||
|
|
||||||
|
// enumsByNumber maps enum values by number to their name counterpart.
|
||||||
|
type enumsByNumber = map[int32]string
|
||||||
|
|
||||||
|
var enumCache sync.Map // map[enumName]enumsByName
|
||||||
|
var numFilesCache sync.Map // map[protoreflect.FullName]int
|
||||||
|
|
||||||
|
// RegisterEnum is called from the generated code to register the mapping of
|
||||||
|
// enum value names to enum numbers for the enum identified by s.
|
||||||
|
//
|
||||||
|
// Deprecated: Use protoregistry.GlobalTypes.Register instead.
|
||||||
|
func RegisterEnum(s enumName, _ enumsByNumber, m enumsByName) {
|
||||||
|
if _, ok := enumCache.Load(s); ok {
|
||||||
|
panic("proto: duplicate enum registered: " + s)
|
||||||
|
}
|
||||||
|
enumCache.Store(s, m)
|
||||||
|
|
||||||
|
// This does not forward registration to the v2 registry since this API
|
||||||
|
// lacks sufficient information to construct a complete v2 enum descriptor.
|
||||||
|
}
|
||||||
|
|
||||||
|
// EnumValueMap returns the mapping from enum value names to enum numbers for
|
||||||
|
// the enum of the given name. It returns nil if not found.
|
||||||
|
//
|
||||||
|
// Deprecated: Use protoregistry.GlobalTypes.FindEnumByName instead.
|
||||||
|
func EnumValueMap(s enumName) enumsByName {
|
||||||
|
if v, ok := enumCache.Load(s); ok {
|
||||||
|
return v.(enumsByName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check whether the cache is stale. If the number of files in the current
|
||||||
|
// package differs, then it means that some enums may have been recently
|
||||||
|
// registered upstream that we do not know about.
|
||||||
|
var protoPkg protoreflect.FullName
|
||||||
|
if i := strings.LastIndexByte(s, '.'); i >= 0 {
|
||||||
|
protoPkg = protoreflect.FullName(s[:i])
|
||||||
|
}
|
||||||
|
v, _ := numFilesCache.Load(protoPkg)
|
||||||
|
numFiles, _ := v.(int)
|
||||||
|
if protoregistry.GlobalFiles.NumFilesByPackage(protoPkg) == numFiles {
|
||||||
|
return nil // cache is up-to-date; was not found earlier
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the enum cache for all enums declared in the given proto package.
|
||||||
|
numFiles = 0
|
||||||
|
protoregistry.GlobalFiles.RangeFilesByPackage(protoPkg, func(fd protoreflect.FileDescriptor) bool {
|
||||||
|
walkEnums(fd, func(ed protoreflect.EnumDescriptor) {
|
||||||
|
name := protoimpl.X.LegacyEnumName(ed)
|
||||||
|
if _, ok := enumCache.Load(name); !ok {
|
||||||
|
m := make(enumsByName)
|
||||||
|
evs := ed.Values()
|
||||||
|
for i := evs.Len() - 1; i >= 0; i-- {
|
||||||
|
ev := evs.Get(i)
|
||||||
|
m[string(ev.Name())] = int32(ev.Number())
|
||||||
|
}
|
||||||
|
enumCache.LoadOrStore(name, m)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
numFiles++
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
numFilesCache.Store(protoPkg, numFiles)
|
||||||
|
|
||||||
|
// Check cache again for enum map.
|
||||||
|
if v, ok := enumCache.Load(s); ok {
|
||||||
|
return v.(enumsByName)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// walkEnums recursively walks all enums declared in d.
|
||||||
|
func walkEnums(d interface {
|
||||||
|
Enums() protoreflect.EnumDescriptors
|
||||||
|
Messages() protoreflect.MessageDescriptors
|
||||||
|
}, f func(protoreflect.EnumDescriptor)) {
|
||||||
|
eds := d.Enums()
|
||||||
|
for i := eds.Len() - 1; i >= 0; i-- {
|
||||||
|
f(eds.Get(i))
|
||||||
|
}
|
||||||
|
mds := d.Messages()
|
||||||
|
for i := mds.Len() - 1; i >= 0; i-- {
|
||||||
|
walkEnums(mds.Get(i), f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// messageName is the full name of protobuf message.
|
||||||
|
type messageName = string
|
||||||
|
|
||||||
|
var messageTypeCache sync.Map // map[messageName]reflect.Type
|
||||||
|
|
||||||
|
// RegisterType is called from generated code to register the message Go type
|
||||||
|
// for a message of the given name.
|
||||||
|
//
|
||||||
|
// Deprecated: Use protoregistry.GlobalTypes.Register instead.
|
||||||
|
func RegisterType(m Message, s messageName) {
|
||||||
|
mt := protoimpl.X.LegacyMessageTypeOf(m, protoreflect.FullName(s))
|
||||||
|
if err := protoregistry.GlobalTypes.RegisterMessage(mt); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
messageTypeCache.Store(s, reflect.TypeOf(m))
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegisterMapType is called from generated code to register the Go map type
|
||||||
|
// for a protobuf message representing a map entry.
|
||||||
|
//
|
||||||
|
// Deprecated: Do not use.
|
||||||
|
func RegisterMapType(m interface{}, s messageName) {
|
||||||
|
t := reflect.TypeOf(m)
|
||||||
|
if t.Kind() != reflect.Map {
|
||||||
|
panic(fmt.Sprintf("invalid map kind: %v", t))
|
||||||
|
}
|
||||||
|
if _, ok := messageTypeCache.Load(s); ok {
|
||||||
|
panic(fmt.Errorf("proto: duplicate proto message registered: %s", s))
|
||||||
|
}
|
||||||
|
messageTypeCache.Store(s, t)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MessageType returns the message type for a named message.
|
||||||
|
// It returns nil if not found.
|
||||||
|
//
|
||||||
|
// Deprecated: Use protoregistry.GlobalTypes.FindMessageByName instead.
|
||||||
|
func MessageType(s messageName) reflect.Type {
|
||||||
|
if v, ok := messageTypeCache.Load(s); ok {
|
||||||
|
return v.(reflect.Type)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Derive the message type from the v2 registry.
|
||||||
|
var t reflect.Type
|
||||||
|
if mt, _ := protoregistry.GlobalTypes.FindMessageByName(protoreflect.FullName(s)); mt != nil {
|
||||||
|
t = messageGoType(mt)
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we could not get a concrete type, it is possible that it is a
|
||||||
|
// pseudo-message for a map entry.
|
||||||
|
if t == nil {
|
||||||
|
d, _ := protoregistry.GlobalFiles.FindDescriptorByName(protoreflect.FullName(s))
|
||||||
|
if md, _ := d.(protoreflect.MessageDescriptor); md != nil && md.IsMapEntry() {
|
||||||
|
kt := goTypeForField(md.Fields().ByNumber(1))
|
||||||
|
vt := goTypeForField(md.Fields().ByNumber(2))
|
||||||
|
t = reflect.MapOf(kt, vt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Locally cache the message type for the given name.
|
||||||
|
if t != nil {
|
||||||
|
v, _ := messageTypeCache.LoadOrStore(s, t)
|
||||||
|
return v.(reflect.Type)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func goTypeForField(fd protoreflect.FieldDescriptor) reflect.Type {
|
||||||
|
switch k := fd.Kind(); k {
|
||||||
|
case protoreflect.EnumKind:
|
||||||
|
if et, _ := protoregistry.GlobalTypes.FindEnumByName(fd.Enum().FullName()); et != nil {
|
||||||
|
return enumGoType(et)
|
||||||
|
}
|
||||||
|
return reflect.TypeOf(protoreflect.EnumNumber(0))
|
||||||
|
case protoreflect.MessageKind, protoreflect.GroupKind:
|
||||||
|
if mt, _ := protoregistry.GlobalTypes.FindMessageByName(fd.Message().FullName()); mt != nil {
|
||||||
|
return messageGoType(mt)
|
||||||
|
}
|
||||||
|
return reflect.TypeOf((*protoreflect.Message)(nil)).Elem()
|
||||||
|
default:
|
||||||
|
return reflect.TypeOf(fd.Default().Interface())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func enumGoType(et protoreflect.EnumType) reflect.Type {
|
||||||
|
return reflect.TypeOf(et.New(0))
|
||||||
|
}
|
||||||
|
|
||||||
|
func messageGoType(mt protoreflect.MessageType) reflect.Type {
|
||||||
|
return reflect.TypeOf(MessageV1(mt.Zero().Interface()))
|
||||||
|
}
|
||||||
|
|
||||||
|
// MessageName returns the full protobuf name for the given message type.
|
||||||
|
//
|
||||||
|
// Deprecated: Use protoreflect.MessageDescriptor.FullName instead.
|
||||||
|
func MessageName(m Message) messageName {
|
||||||
|
if m == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
if m, ok := m.(interface{ XXX_MessageName() messageName }); ok {
|
||||||
|
return m.XXX_MessageName()
|
||||||
|
}
|
||||||
|
return messageName(protoimpl.X.MessageDescriptorOf(m).FullName())
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegisterExtension is called from the generated code to register
|
||||||
|
// the extension descriptor.
|
||||||
|
//
|
||||||
|
// Deprecated: Use protoregistry.GlobalTypes.Register instead.
|
||||||
|
func RegisterExtension(d *ExtensionDesc) {
|
||||||
|
if err := protoregistry.GlobalTypes.RegisterExtension(d); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type extensionsByNumber = map[int32]*ExtensionDesc
|
||||||
|
|
||||||
|
var extensionCache sync.Map // map[messageName]extensionsByNumber
|
||||||
|
|
||||||
|
// RegisteredExtensions returns a map of the registered extensions for the
|
||||||
|
// provided protobuf message, indexed by the extension field number.
|
||||||
|
//
|
||||||
|
// Deprecated: Use protoregistry.GlobalTypes.RangeExtensionsByMessage instead.
|
||||||
|
func RegisteredExtensions(m Message) extensionsByNumber {
|
||||||
|
// Check whether the cache is stale. If the number of extensions for
|
||||||
|
// the given message differs, then it means that some extensions were
|
||||||
|
// recently registered upstream that we do not know about.
|
||||||
|
s := MessageName(m)
|
||||||
|
v, _ := extensionCache.Load(s)
|
||||||
|
xs, _ := v.(extensionsByNumber)
|
||||||
|
if protoregistry.GlobalTypes.NumExtensionsByMessage(protoreflect.FullName(s)) == len(xs) {
|
||||||
|
return xs // cache is up-to-date
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cache is stale, re-compute the extensions map.
|
||||||
|
xs = make(extensionsByNumber)
|
||||||
|
protoregistry.GlobalTypes.RangeExtensionsByMessage(protoreflect.FullName(s), func(xt protoreflect.ExtensionType) bool {
|
||||||
|
if xd, ok := xt.(*ExtensionDesc); ok {
|
||||||
|
xs[int32(xt.TypeDescriptor().Number())] = xd
|
||||||
|
} else {
|
||||||
|
// TODO: This implies that the protoreflect.ExtensionType is a
|
||||||
|
// custom type not generated by protoc-gen-go. We could try and
|
||||||
|
// convert the type to an ExtensionDesc.
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
extensionCache.Store(s, xs)
|
||||||
|
return xs
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -1,654 +0,0 @@
|
||||||
// Go support for Protocol Buffers - Google's data interchange format
|
|
||||||
//
|
|
||||||
// Copyright 2016 The Go Authors. All rights reserved.
|
|
||||||
// https://github.com/golang/protobuf
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without
|
|
||||||
// modification, are permitted provided that the following conditions are
|
|
||||||
// met:
|
|
||||||
//
|
|
||||||
// * Redistributions of source code must retain the above copyright
|
|
||||||
// notice, this list of conditions and the following disclaimer.
|
|
||||||
// * Redistributions in binary form must reproduce the above
|
|
||||||
// copyright notice, this list of conditions and the following disclaimer
|
|
||||||
// in the documentation and/or other materials provided with the
|
|
||||||
// distribution.
|
|
||||||
// * Neither the name of Google Inc. nor the names of its
|
|
||||||
// contributors may be used to endorse or promote products derived from
|
|
||||||
// this software without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
package proto
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"reflect"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
"sync/atomic"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Merge merges the src message into dst.
|
|
||||||
// This assumes that dst and src of the same type and are non-nil.
|
|
||||||
func (a *InternalMessageInfo) Merge(dst, src Message) {
|
|
||||||
mi := atomicLoadMergeInfo(&a.merge)
|
|
||||||
if mi == nil {
|
|
||||||
mi = getMergeInfo(reflect.TypeOf(dst).Elem())
|
|
||||||
atomicStoreMergeInfo(&a.merge, mi)
|
|
||||||
}
|
|
||||||
mi.merge(toPointer(&dst), toPointer(&src))
|
|
||||||
}
|
|
||||||
|
|
||||||
type mergeInfo struct {
|
|
||||||
typ reflect.Type
|
|
||||||
|
|
||||||
initialized int32 // 0: only typ is valid, 1: everything is valid
|
|
||||||
lock sync.Mutex
|
|
||||||
|
|
||||||
fields []mergeFieldInfo
|
|
||||||
unrecognized field // Offset of XXX_unrecognized
|
|
||||||
}
|
|
||||||
|
|
||||||
type mergeFieldInfo struct {
|
|
||||||
field field // Offset of field, guaranteed to be valid
|
|
||||||
|
|
||||||
// isPointer reports whether the value in the field is a pointer.
|
|
||||||
// This is true for the following situations:
|
|
||||||
// * Pointer to struct
|
|
||||||
// * Pointer to basic type (proto2 only)
|
|
||||||
// * Slice (first value in slice header is a pointer)
|
|
||||||
// * String (first value in string header is a pointer)
|
|
||||||
isPointer bool
|
|
||||||
|
|
||||||
// basicWidth reports the width of the field assuming that it is directly
|
|
||||||
// embedded in the struct (as is the case for basic types in proto3).
|
|
||||||
// The possible values are:
|
|
||||||
// 0: invalid
|
|
||||||
// 1: bool
|
|
||||||
// 4: int32, uint32, float32
|
|
||||||
// 8: int64, uint64, float64
|
|
||||||
basicWidth int
|
|
||||||
|
|
||||||
// Where dst and src are pointers to the types being merged.
|
|
||||||
merge func(dst, src pointer)
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
mergeInfoMap = map[reflect.Type]*mergeInfo{}
|
|
||||||
mergeInfoLock sync.Mutex
|
|
||||||
)
|
|
||||||
|
|
||||||
func getMergeInfo(t reflect.Type) *mergeInfo {
|
|
||||||
mergeInfoLock.Lock()
|
|
||||||
defer mergeInfoLock.Unlock()
|
|
||||||
mi := mergeInfoMap[t]
|
|
||||||
if mi == nil {
|
|
||||||
mi = &mergeInfo{typ: t}
|
|
||||||
mergeInfoMap[t] = mi
|
|
||||||
}
|
|
||||||
return mi
|
|
||||||
}
|
|
||||||
|
|
||||||
// merge merges src into dst assuming they are both of type *mi.typ.
|
|
||||||
func (mi *mergeInfo) merge(dst, src pointer) {
|
|
||||||
if dst.isNil() {
|
|
||||||
panic("proto: nil destination")
|
|
||||||
}
|
|
||||||
if src.isNil() {
|
|
||||||
return // Nothing to do.
|
|
||||||
}
|
|
||||||
|
|
||||||
if atomic.LoadInt32(&mi.initialized) == 0 {
|
|
||||||
mi.computeMergeInfo()
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, fi := range mi.fields {
|
|
||||||
sfp := src.offset(fi.field)
|
|
||||||
|
|
||||||
// As an optimization, we can avoid the merge function call cost
|
|
||||||
// if we know for sure that the source will have no effect
|
|
||||||
// by checking if it is the zero value.
|
|
||||||
if unsafeAllowed {
|
|
||||||
if fi.isPointer && sfp.getPointer().isNil() { // Could be slice or string
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if fi.basicWidth > 0 {
|
|
||||||
switch {
|
|
||||||
case fi.basicWidth == 1 && !*sfp.toBool():
|
|
||||||
continue
|
|
||||||
case fi.basicWidth == 4 && *sfp.toUint32() == 0:
|
|
||||||
continue
|
|
||||||
case fi.basicWidth == 8 && *sfp.toUint64() == 0:
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dfp := dst.offset(fi.field)
|
|
||||||
fi.merge(dfp, sfp)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Make this faster?
|
|
||||||
out := dst.asPointerTo(mi.typ).Elem()
|
|
||||||
in := src.asPointerTo(mi.typ).Elem()
|
|
||||||
if emIn, err := extendable(in.Addr().Interface()); err == nil {
|
|
||||||
emOut, _ := extendable(out.Addr().Interface())
|
|
||||||
mIn, muIn := emIn.extensionsRead()
|
|
||||||
if mIn != nil {
|
|
||||||
mOut := emOut.extensionsWrite()
|
|
||||||
muIn.Lock()
|
|
||||||
mergeExtension(mOut, mIn)
|
|
||||||
muIn.Unlock()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if mi.unrecognized.IsValid() {
|
|
||||||
if b := *src.offset(mi.unrecognized).toBytes(); len(b) > 0 {
|
|
||||||
*dst.offset(mi.unrecognized).toBytes() = append([]byte(nil), b...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mi *mergeInfo) computeMergeInfo() {
|
|
||||||
mi.lock.Lock()
|
|
||||||
defer mi.lock.Unlock()
|
|
||||||
if mi.initialized != 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
t := mi.typ
|
|
||||||
n := t.NumField()
|
|
||||||
|
|
||||||
props := GetProperties(t)
|
|
||||||
for i := 0; i < n; i++ {
|
|
||||||
f := t.Field(i)
|
|
||||||
if strings.HasPrefix(f.Name, "XXX_") {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
mfi := mergeFieldInfo{field: toField(&f)}
|
|
||||||
tf := f.Type
|
|
||||||
|
|
||||||
// As an optimization, we can avoid the merge function call cost
|
|
||||||
// if we know for sure that the source will have no effect
|
|
||||||
// by checking if it is the zero value.
|
|
||||||
if unsafeAllowed {
|
|
||||||
switch tf.Kind() {
|
|
||||||
case reflect.Ptr, reflect.Slice, reflect.String:
|
|
||||||
// As a special case, we assume slices and strings are pointers
|
|
||||||
// since we know that the first field in the SliceSlice or
|
|
||||||
// StringHeader is a data pointer.
|
|
||||||
mfi.isPointer = true
|
|
||||||
case reflect.Bool:
|
|
||||||
mfi.basicWidth = 1
|
|
||||||
case reflect.Int32, reflect.Uint32, reflect.Float32:
|
|
||||||
mfi.basicWidth = 4
|
|
||||||
case reflect.Int64, reflect.Uint64, reflect.Float64:
|
|
||||||
mfi.basicWidth = 8
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unwrap tf to get at its most basic type.
|
|
||||||
var isPointer, isSlice bool
|
|
||||||
if tf.Kind() == reflect.Slice && tf.Elem().Kind() != reflect.Uint8 {
|
|
||||||
isSlice = true
|
|
||||||
tf = tf.Elem()
|
|
||||||
}
|
|
||||||
if tf.Kind() == reflect.Ptr {
|
|
||||||
isPointer = true
|
|
||||||
tf = tf.Elem()
|
|
||||||
}
|
|
||||||
if isPointer && isSlice && tf.Kind() != reflect.Struct {
|
|
||||||
panic("both pointer and slice for basic type in " + tf.Name())
|
|
||||||
}
|
|
||||||
|
|
||||||
switch tf.Kind() {
|
|
||||||
case reflect.Int32:
|
|
||||||
switch {
|
|
||||||
case isSlice: // E.g., []int32
|
|
||||||
mfi.merge = func(dst, src pointer) {
|
|
||||||
// NOTE: toInt32Slice is not defined (see pointer_reflect.go).
|
|
||||||
/*
|
|
||||||
sfsp := src.toInt32Slice()
|
|
||||||
if *sfsp != nil {
|
|
||||||
dfsp := dst.toInt32Slice()
|
|
||||||
*dfsp = append(*dfsp, *sfsp...)
|
|
||||||
if *dfsp == nil {
|
|
||||||
*dfsp = []int64{}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
sfs := src.getInt32Slice()
|
|
||||||
if sfs != nil {
|
|
||||||
dfs := dst.getInt32Slice()
|
|
||||||
dfs = append(dfs, sfs...)
|
|
||||||
if dfs == nil {
|
|
||||||
dfs = []int32{}
|
|
||||||
}
|
|
||||||
dst.setInt32Slice(dfs)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case isPointer: // E.g., *int32
|
|
||||||
mfi.merge = func(dst, src pointer) {
|
|
||||||
// NOTE: toInt32Ptr is not defined (see pointer_reflect.go).
|
|
||||||
/*
|
|
||||||
sfpp := src.toInt32Ptr()
|
|
||||||
if *sfpp != nil {
|
|
||||||
dfpp := dst.toInt32Ptr()
|
|
||||||
if *dfpp == nil {
|
|
||||||
*dfpp = Int32(**sfpp)
|
|
||||||
} else {
|
|
||||||
**dfpp = **sfpp
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
sfp := src.getInt32Ptr()
|
|
||||||
if sfp != nil {
|
|
||||||
dfp := dst.getInt32Ptr()
|
|
||||||
if dfp == nil {
|
|
||||||
dst.setInt32Ptr(*sfp)
|
|
||||||
} else {
|
|
||||||
*dfp = *sfp
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
default: // E.g., int32
|
|
||||||
mfi.merge = func(dst, src pointer) {
|
|
||||||
if v := *src.toInt32(); v != 0 {
|
|
||||||
*dst.toInt32() = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case reflect.Int64:
|
|
||||||
switch {
|
|
||||||
case isSlice: // E.g., []int64
|
|
||||||
mfi.merge = func(dst, src pointer) {
|
|
||||||
sfsp := src.toInt64Slice()
|
|
||||||
if *sfsp != nil {
|
|
||||||
dfsp := dst.toInt64Slice()
|
|
||||||
*dfsp = append(*dfsp, *sfsp...)
|
|
||||||
if *dfsp == nil {
|
|
||||||
*dfsp = []int64{}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case isPointer: // E.g., *int64
|
|
||||||
mfi.merge = func(dst, src pointer) {
|
|
||||||
sfpp := src.toInt64Ptr()
|
|
||||||
if *sfpp != nil {
|
|
||||||
dfpp := dst.toInt64Ptr()
|
|
||||||
if *dfpp == nil {
|
|
||||||
*dfpp = Int64(**sfpp)
|
|
||||||
} else {
|
|
||||||
**dfpp = **sfpp
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
default: // E.g., int64
|
|
||||||
mfi.merge = func(dst, src pointer) {
|
|
||||||
if v := *src.toInt64(); v != 0 {
|
|
||||||
*dst.toInt64() = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case reflect.Uint32:
|
|
||||||
switch {
|
|
||||||
case isSlice: // E.g., []uint32
|
|
||||||
mfi.merge = func(dst, src pointer) {
|
|
||||||
sfsp := src.toUint32Slice()
|
|
||||||
if *sfsp != nil {
|
|
||||||
dfsp := dst.toUint32Slice()
|
|
||||||
*dfsp = append(*dfsp, *sfsp...)
|
|
||||||
if *dfsp == nil {
|
|
||||||
*dfsp = []uint32{}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case isPointer: // E.g., *uint32
|
|
||||||
mfi.merge = func(dst, src pointer) {
|
|
||||||
sfpp := src.toUint32Ptr()
|
|
||||||
if *sfpp != nil {
|
|
||||||
dfpp := dst.toUint32Ptr()
|
|
||||||
if *dfpp == nil {
|
|
||||||
*dfpp = Uint32(**sfpp)
|
|
||||||
} else {
|
|
||||||
**dfpp = **sfpp
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
default: // E.g., uint32
|
|
||||||
mfi.merge = func(dst, src pointer) {
|
|
||||||
if v := *src.toUint32(); v != 0 {
|
|
||||||
*dst.toUint32() = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case reflect.Uint64:
|
|
||||||
switch {
|
|
||||||
case isSlice: // E.g., []uint64
|
|
||||||
mfi.merge = func(dst, src pointer) {
|
|
||||||
sfsp := src.toUint64Slice()
|
|
||||||
if *sfsp != nil {
|
|
||||||
dfsp := dst.toUint64Slice()
|
|
||||||
*dfsp = append(*dfsp, *sfsp...)
|
|
||||||
if *dfsp == nil {
|
|
||||||
*dfsp = []uint64{}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case isPointer: // E.g., *uint64
|
|
||||||
mfi.merge = func(dst, src pointer) {
|
|
||||||
sfpp := src.toUint64Ptr()
|
|
||||||
if *sfpp != nil {
|
|
||||||
dfpp := dst.toUint64Ptr()
|
|
||||||
if *dfpp == nil {
|
|
||||||
*dfpp = Uint64(**sfpp)
|
|
||||||
} else {
|
|
||||||
**dfpp = **sfpp
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
default: // E.g., uint64
|
|
||||||
mfi.merge = func(dst, src pointer) {
|
|
||||||
if v := *src.toUint64(); v != 0 {
|
|
||||||
*dst.toUint64() = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case reflect.Float32:
|
|
||||||
switch {
|
|
||||||
case isSlice: // E.g., []float32
|
|
||||||
mfi.merge = func(dst, src pointer) {
|
|
||||||
sfsp := src.toFloat32Slice()
|
|
||||||
if *sfsp != nil {
|
|
||||||
dfsp := dst.toFloat32Slice()
|
|
||||||
*dfsp = append(*dfsp, *sfsp...)
|
|
||||||
if *dfsp == nil {
|
|
||||||
*dfsp = []float32{}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case isPointer: // E.g., *float32
|
|
||||||
mfi.merge = func(dst, src pointer) {
|
|
||||||
sfpp := src.toFloat32Ptr()
|
|
||||||
if *sfpp != nil {
|
|
||||||
dfpp := dst.toFloat32Ptr()
|
|
||||||
if *dfpp == nil {
|
|
||||||
*dfpp = Float32(**sfpp)
|
|
||||||
} else {
|
|
||||||
**dfpp = **sfpp
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
default: // E.g., float32
|
|
||||||
mfi.merge = func(dst, src pointer) {
|
|
||||||
if v := *src.toFloat32(); v != 0 {
|
|
||||||
*dst.toFloat32() = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case reflect.Float64:
|
|
||||||
switch {
|
|
||||||
case isSlice: // E.g., []float64
|
|
||||||
mfi.merge = func(dst, src pointer) {
|
|
||||||
sfsp := src.toFloat64Slice()
|
|
||||||
if *sfsp != nil {
|
|
||||||
dfsp := dst.toFloat64Slice()
|
|
||||||
*dfsp = append(*dfsp, *sfsp...)
|
|
||||||
if *dfsp == nil {
|
|
||||||
*dfsp = []float64{}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case isPointer: // E.g., *float64
|
|
||||||
mfi.merge = func(dst, src pointer) {
|
|
||||||
sfpp := src.toFloat64Ptr()
|
|
||||||
if *sfpp != nil {
|
|
||||||
dfpp := dst.toFloat64Ptr()
|
|
||||||
if *dfpp == nil {
|
|
||||||
*dfpp = Float64(**sfpp)
|
|
||||||
} else {
|
|
||||||
**dfpp = **sfpp
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
default: // E.g., float64
|
|
||||||
mfi.merge = func(dst, src pointer) {
|
|
||||||
if v := *src.toFloat64(); v != 0 {
|
|
||||||
*dst.toFloat64() = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case reflect.Bool:
|
|
||||||
switch {
|
|
||||||
case isSlice: // E.g., []bool
|
|
||||||
mfi.merge = func(dst, src pointer) {
|
|
||||||
sfsp := src.toBoolSlice()
|
|
||||||
if *sfsp != nil {
|
|
||||||
dfsp := dst.toBoolSlice()
|
|
||||||
*dfsp = append(*dfsp, *sfsp...)
|
|
||||||
if *dfsp == nil {
|
|
||||||
*dfsp = []bool{}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case isPointer: // E.g., *bool
|
|
||||||
mfi.merge = func(dst, src pointer) {
|
|
||||||
sfpp := src.toBoolPtr()
|
|
||||||
if *sfpp != nil {
|
|
||||||
dfpp := dst.toBoolPtr()
|
|
||||||
if *dfpp == nil {
|
|
||||||
*dfpp = Bool(**sfpp)
|
|
||||||
} else {
|
|
||||||
**dfpp = **sfpp
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
default: // E.g., bool
|
|
||||||
mfi.merge = func(dst, src pointer) {
|
|
||||||
if v := *src.toBool(); v {
|
|
||||||
*dst.toBool() = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case reflect.String:
|
|
||||||
switch {
|
|
||||||
case isSlice: // E.g., []string
|
|
||||||
mfi.merge = func(dst, src pointer) {
|
|
||||||
sfsp := src.toStringSlice()
|
|
||||||
if *sfsp != nil {
|
|
||||||
dfsp := dst.toStringSlice()
|
|
||||||
*dfsp = append(*dfsp, *sfsp...)
|
|
||||||
if *dfsp == nil {
|
|
||||||
*dfsp = []string{}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case isPointer: // E.g., *string
|
|
||||||
mfi.merge = func(dst, src pointer) {
|
|
||||||
sfpp := src.toStringPtr()
|
|
||||||
if *sfpp != nil {
|
|
||||||
dfpp := dst.toStringPtr()
|
|
||||||
if *dfpp == nil {
|
|
||||||
*dfpp = String(**sfpp)
|
|
||||||
} else {
|
|
||||||
**dfpp = **sfpp
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
default: // E.g., string
|
|
||||||
mfi.merge = func(dst, src pointer) {
|
|
||||||
if v := *src.toString(); v != "" {
|
|
||||||
*dst.toString() = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case reflect.Slice:
|
|
||||||
isProto3 := props.Prop[i].proto3
|
|
||||||
switch {
|
|
||||||
case isPointer:
|
|
||||||
panic("bad pointer in byte slice case in " + tf.Name())
|
|
||||||
case tf.Elem().Kind() != reflect.Uint8:
|
|
||||||
panic("bad element kind in byte slice case in " + tf.Name())
|
|
||||||
case isSlice: // E.g., [][]byte
|
|
||||||
mfi.merge = func(dst, src pointer) {
|
|
||||||
sbsp := src.toBytesSlice()
|
|
||||||
if *sbsp != nil {
|
|
||||||
dbsp := dst.toBytesSlice()
|
|
||||||
for _, sb := range *sbsp {
|
|
||||||
if sb == nil {
|
|
||||||
*dbsp = append(*dbsp, nil)
|
|
||||||
} else {
|
|
||||||
*dbsp = append(*dbsp, append([]byte{}, sb...))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if *dbsp == nil {
|
|
||||||
*dbsp = [][]byte{}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
default: // E.g., []byte
|
|
||||||
mfi.merge = func(dst, src pointer) {
|
|
||||||
sbp := src.toBytes()
|
|
||||||
if *sbp != nil {
|
|
||||||
dbp := dst.toBytes()
|
|
||||||
if !isProto3 || len(*sbp) > 0 {
|
|
||||||
*dbp = append([]byte{}, *sbp...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case reflect.Struct:
|
|
||||||
switch {
|
|
||||||
case !isPointer:
|
|
||||||
panic(fmt.Sprintf("message field %s without pointer", tf))
|
|
||||||
case isSlice: // E.g., []*pb.T
|
|
||||||
mi := getMergeInfo(tf)
|
|
||||||
mfi.merge = func(dst, src pointer) {
|
|
||||||
sps := src.getPointerSlice()
|
|
||||||
if sps != nil {
|
|
||||||
dps := dst.getPointerSlice()
|
|
||||||
for _, sp := range sps {
|
|
||||||
var dp pointer
|
|
||||||
if !sp.isNil() {
|
|
||||||
dp = valToPointer(reflect.New(tf))
|
|
||||||
mi.merge(dp, sp)
|
|
||||||
}
|
|
||||||
dps = append(dps, dp)
|
|
||||||
}
|
|
||||||
if dps == nil {
|
|
||||||
dps = []pointer{}
|
|
||||||
}
|
|
||||||
dst.setPointerSlice(dps)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
default: // E.g., *pb.T
|
|
||||||
mi := getMergeInfo(tf)
|
|
||||||
mfi.merge = func(dst, src pointer) {
|
|
||||||
sp := src.getPointer()
|
|
||||||
if !sp.isNil() {
|
|
||||||
dp := dst.getPointer()
|
|
||||||
if dp.isNil() {
|
|
||||||
dp = valToPointer(reflect.New(tf))
|
|
||||||
dst.setPointer(dp)
|
|
||||||
}
|
|
||||||
mi.merge(dp, sp)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case reflect.Map:
|
|
||||||
switch {
|
|
||||||
case isPointer || isSlice:
|
|
||||||
panic("bad pointer or slice in map case in " + tf.Name())
|
|
||||||
default: // E.g., map[K]V
|
|
||||||
mfi.merge = func(dst, src pointer) {
|
|
||||||
sm := src.asPointerTo(tf).Elem()
|
|
||||||
if sm.Len() == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
dm := dst.asPointerTo(tf).Elem()
|
|
||||||
if dm.IsNil() {
|
|
||||||
dm.Set(reflect.MakeMap(tf))
|
|
||||||
}
|
|
||||||
|
|
||||||
switch tf.Elem().Kind() {
|
|
||||||
case reflect.Ptr: // Proto struct (e.g., *T)
|
|
||||||
for _, key := range sm.MapKeys() {
|
|
||||||
val := sm.MapIndex(key)
|
|
||||||
val = reflect.ValueOf(Clone(val.Interface().(Message)))
|
|
||||||
dm.SetMapIndex(key, val)
|
|
||||||
}
|
|
||||||
case reflect.Slice: // E.g. Bytes type (e.g., []byte)
|
|
||||||
for _, key := range sm.MapKeys() {
|
|
||||||
val := sm.MapIndex(key)
|
|
||||||
val = reflect.ValueOf(append([]byte{}, val.Bytes()...))
|
|
||||||
dm.SetMapIndex(key, val)
|
|
||||||
}
|
|
||||||
default: // Basic type (e.g., string)
|
|
||||||
for _, key := range sm.MapKeys() {
|
|
||||||
val := sm.MapIndex(key)
|
|
||||||
dm.SetMapIndex(key, val)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case reflect.Interface:
|
|
||||||
// Must be oneof field.
|
|
||||||
switch {
|
|
||||||
case isPointer || isSlice:
|
|
||||||
panic("bad pointer or slice in interface case in " + tf.Name())
|
|
||||||
default: // E.g., interface{}
|
|
||||||
// TODO: Make this faster?
|
|
||||||
mfi.merge = func(dst, src pointer) {
|
|
||||||
su := src.asPointerTo(tf).Elem()
|
|
||||||
if !su.IsNil() {
|
|
||||||
du := dst.asPointerTo(tf).Elem()
|
|
||||||
typ := su.Elem().Type()
|
|
||||||
if du.IsNil() || du.Elem().Type() != typ {
|
|
||||||
du.Set(reflect.New(typ.Elem())) // Initialize interface if empty
|
|
||||||
}
|
|
||||||
sv := su.Elem().Elem().Field(0)
|
|
||||||
if sv.Kind() == reflect.Ptr && sv.IsNil() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
dv := du.Elem().Elem().Field(0)
|
|
||||||
if dv.Kind() == reflect.Ptr && dv.IsNil() {
|
|
||||||
dv.Set(reflect.New(sv.Type().Elem())) // Initialize proto message if empty
|
|
||||||
}
|
|
||||||
switch sv.Type().Kind() {
|
|
||||||
case reflect.Ptr: // Proto struct (e.g., *T)
|
|
||||||
Merge(dv.Interface().(Message), sv.Interface().(Message))
|
|
||||||
case reflect.Slice: // E.g. Bytes type (e.g., []byte)
|
|
||||||
dv.Set(reflect.ValueOf(append([]byte{}, sv.Bytes()...)))
|
|
||||||
default: // Basic type (e.g., string)
|
|
||||||
dv.Set(sv)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
panic(fmt.Sprintf("merger not found for type:%s", tf))
|
|
||||||
}
|
|
||||||
mi.fields = append(mi.fields, mfi)
|
|
||||||
}
|
|
||||||
|
|
||||||
mi.unrecognized = invalidField
|
|
||||||
if f, ok := t.FieldByName("XXX_unrecognized"); ok {
|
|
||||||
if f.Type != reflect.TypeOf([]byte{}) {
|
|
||||||
panic("expected XXX_unrecognized to be of type []byte")
|
|
||||||
}
|
|
||||||
mi.unrecognized = toField(&f)
|
|
||||||
}
|
|
||||||
|
|
||||||
atomic.StoreInt32(&mi.initialized, 1)
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,845 +0,0 @@
|
||||||
// Go support for Protocol Buffers - Google's data interchange format
|
|
||||||
//
|
|
||||||
// Copyright 2010 The Go Authors. All rights reserved.
|
|
||||||
// https://github.com/golang/protobuf
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without
|
|
||||||
// modification, are permitted provided that the following conditions are
|
|
||||||
// met:
|
|
||||||
//
|
|
||||||
// * Redistributions of source code must retain the above copyright
|
|
||||||
// notice, this list of conditions and the following disclaimer.
|
|
||||||
// * Redistributions in binary form must reproduce the above
|
|
||||||
// copyright notice, this list of conditions and the following disclaimer
|
|
||||||
// in the documentation and/or other materials provided with the
|
|
||||||
// distribution.
|
|
||||||
// * Neither the name of Google Inc. nor the names of its
|
|
||||||
// contributors may be used to endorse or promote products derived from
|
|
||||||
// this software without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
package proto
|
|
||||||
|
|
||||||
// Functions for writing the text protocol buffer format.
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"bytes"
|
|
||||||
"encoding"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"log"
|
|
||||||
"math"
|
|
||||||
"reflect"
|
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
newline = []byte("\n")
|
|
||||||
spaces = []byte(" ")
|
|
||||||
endBraceNewline = []byte("}\n")
|
|
||||||
backslashN = []byte{'\\', 'n'}
|
|
||||||
backslashR = []byte{'\\', 'r'}
|
|
||||||
backslashT = []byte{'\\', 't'}
|
|
||||||
backslashDQ = []byte{'\\', '"'}
|
|
||||||
backslashBS = []byte{'\\', '\\'}
|
|
||||||
posInf = []byte("inf")
|
|
||||||
negInf = []byte("-inf")
|
|
||||||
nan = []byte("nan")
|
|
||||||
)
|
|
||||||
|
|
||||||
type writer interface {
|
|
||||||
io.Writer
|
|
||||||
WriteByte(byte) error
|
|
||||||
}
|
|
||||||
|
|
||||||
// textWriter is an io.Writer that tracks its indentation level.
|
|
||||||
type textWriter struct {
|
|
||||||
ind int
|
|
||||||
complete bool // if the current position is a complete line
|
|
||||||
compact bool // whether to write out as a one-liner
|
|
||||||
w writer
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *textWriter) WriteString(s string) (n int, err error) {
|
|
||||||
if !strings.Contains(s, "\n") {
|
|
||||||
if !w.compact && w.complete {
|
|
||||||
w.writeIndent()
|
|
||||||
}
|
|
||||||
w.complete = false
|
|
||||||
return io.WriteString(w.w, s)
|
|
||||||
}
|
|
||||||
// WriteString is typically called without newlines, so this
|
|
||||||
// codepath and its copy are rare. We copy to avoid
|
|
||||||
// duplicating all of Write's logic here.
|
|
||||||
return w.Write([]byte(s))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *textWriter) Write(p []byte) (n int, err error) {
|
|
||||||
newlines := bytes.Count(p, newline)
|
|
||||||
if newlines == 0 {
|
|
||||||
if !w.compact && w.complete {
|
|
||||||
w.writeIndent()
|
|
||||||
}
|
|
||||||
n, err = w.w.Write(p)
|
|
||||||
w.complete = false
|
|
||||||
return n, err
|
|
||||||
}
|
|
||||||
|
|
||||||
frags := bytes.SplitN(p, newline, newlines+1)
|
|
||||||
if w.compact {
|
|
||||||
for i, frag := range frags {
|
|
||||||
if i > 0 {
|
|
||||||
if err := w.w.WriteByte(' '); err != nil {
|
|
||||||
return n, err
|
|
||||||
}
|
|
||||||
n++
|
|
||||||
}
|
|
||||||
nn, err := w.w.Write(frag)
|
|
||||||
n += nn
|
|
||||||
if err != nil {
|
|
||||||
return n, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return n, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, frag := range frags {
|
|
||||||
if w.complete {
|
|
||||||
w.writeIndent()
|
|
||||||
}
|
|
||||||
nn, err := w.w.Write(frag)
|
|
||||||
n += nn
|
|
||||||
if err != nil {
|
|
||||||
return n, err
|
|
||||||
}
|
|
||||||
if i+1 < len(frags) {
|
|
||||||
if err := w.w.WriteByte('\n'); err != nil {
|
|
||||||
return n, err
|
|
||||||
}
|
|
||||||
n++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
w.complete = len(frags[len(frags)-1]) == 0
|
|
||||||
return n, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *textWriter) WriteByte(c byte) error {
|
|
||||||
if w.compact && c == '\n' {
|
|
||||||
c = ' '
|
|
||||||
}
|
|
||||||
if !w.compact && w.complete {
|
|
||||||
w.writeIndent()
|
|
||||||
}
|
|
||||||
err := w.w.WriteByte(c)
|
|
||||||
w.complete = c == '\n'
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *textWriter) indent() { w.ind++ }
|
|
||||||
|
|
||||||
func (w *textWriter) unindent() {
|
|
||||||
if w.ind == 0 {
|
|
||||||
log.Print("proto: textWriter unindented too far")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
w.ind--
|
|
||||||
}
|
|
||||||
|
|
||||||
func writeName(w *textWriter, props *Properties) error {
|
|
||||||
if _, err := w.WriteString(props.OrigName); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if props.Wire != "group" {
|
|
||||||
return w.WriteByte(':')
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func requiresQuotes(u string) bool {
|
|
||||||
// When type URL contains any characters except [0-9A-Za-z./\-]*, it must be quoted.
|
|
||||||
for _, ch := range u {
|
|
||||||
switch {
|
|
||||||
case ch == '.' || ch == '/' || ch == '_':
|
|
||||||
continue
|
|
||||||
case '0' <= ch && ch <= '9':
|
|
||||||
continue
|
|
||||||
case 'A' <= ch && ch <= 'Z':
|
|
||||||
continue
|
|
||||||
case 'a' <= ch && ch <= 'z':
|
|
||||||
continue
|
|
||||||
default:
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// isAny reports whether sv is a google.protobuf.Any message
|
|
||||||
func isAny(sv reflect.Value) bool {
|
|
||||||
type wkt interface {
|
|
||||||
XXX_WellKnownType() string
|
|
||||||
}
|
|
||||||
t, ok := sv.Addr().Interface().(wkt)
|
|
||||||
return ok && t.XXX_WellKnownType() == "Any"
|
|
||||||
}
|
|
||||||
|
|
||||||
// writeProto3Any writes an expanded google.protobuf.Any message.
|
|
||||||
//
|
|
||||||
// It returns (false, nil) if sv value can't be unmarshaled (e.g. because
|
|
||||||
// required messages are not linked in).
|
|
||||||
//
|
|
||||||
// It returns (true, error) when sv was written in expanded format or an error
|
|
||||||
// was encountered.
|
|
||||||
func (tm *TextMarshaler) writeProto3Any(w *textWriter, sv reflect.Value) (bool, error) {
|
|
||||||
turl := sv.FieldByName("TypeUrl")
|
|
||||||
val := sv.FieldByName("Value")
|
|
||||||
if !turl.IsValid() || !val.IsValid() {
|
|
||||||
return true, errors.New("proto: invalid google.protobuf.Any message")
|
|
||||||
}
|
|
||||||
|
|
||||||
b, ok := val.Interface().([]byte)
|
|
||||||
if !ok {
|
|
||||||
return true, errors.New("proto: invalid google.protobuf.Any message")
|
|
||||||
}
|
|
||||||
|
|
||||||
parts := strings.Split(turl.String(), "/")
|
|
||||||
mt := MessageType(parts[len(parts)-1])
|
|
||||||
if mt == nil {
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
m := reflect.New(mt.Elem())
|
|
||||||
if err := Unmarshal(b, m.Interface().(Message)); err != nil {
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
w.Write([]byte("["))
|
|
||||||
u := turl.String()
|
|
||||||
if requiresQuotes(u) {
|
|
||||||
writeString(w, u)
|
|
||||||
} else {
|
|
||||||
w.Write([]byte(u))
|
|
||||||
}
|
|
||||||
if w.compact {
|
|
||||||
w.Write([]byte("]:<"))
|
|
||||||
} else {
|
|
||||||
w.Write([]byte("]: <\n"))
|
|
||||||
w.ind++
|
|
||||||
}
|
|
||||||
if err := tm.writeStruct(w, m.Elem()); err != nil {
|
|
||||||
return true, err
|
|
||||||
}
|
|
||||||
if w.compact {
|
|
||||||
w.Write([]byte("> "))
|
|
||||||
} else {
|
|
||||||
w.ind--
|
|
||||||
w.Write([]byte(">\n"))
|
|
||||||
}
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tm *TextMarshaler) writeStruct(w *textWriter, sv reflect.Value) error {
|
|
||||||
if tm.ExpandAny && isAny(sv) {
|
|
||||||
if canExpand, err := tm.writeProto3Any(w, sv); canExpand {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
st := sv.Type()
|
|
||||||
sprops := GetProperties(st)
|
|
||||||
for i := 0; i < sv.NumField(); i++ {
|
|
||||||
fv := sv.Field(i)
|
|
||||||
props := sprops.Prop[i]
|
|
||||||
name := st.Field(i).Name
|
|
||||||
|
|
||||||
if name == "XXX_NoUnkeyedLiteral" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if strings.HasPrefix(name, "XXX_") {
|
|
||||||
// There are two XXX_ fields:
|
|
||||||
// XXX_unrecognized []byte
|
|
||||||
// XXX_extensions map[int32]proto.Extension
|
|
||||||
// The first is handled here;
|
|
||||||
// the second is handled at the bottom of this function.
|
|
||||||
if name == "XXX_unrecognized" && !fv.IsNil() {
|
|
||||||
if err := writeUnknownStruct(w, fv.Interface().([]byte)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if fv.Kind() == reflect.Ptr && fv.IsNil() {
|
|
||||||
// Field not filled in. This could be an optional field or
|
|
||||||
// a required field that wasn't filled in. Either way, there
|
|
||||||
// isn't anything we can show for it.
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if fv.Kind() == reflect.Slice && fv.IsNil() {
|
|
||||||
// Repeated field that is empty, or a bytes field that is unused.
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if props.Repeated && fv.Kind() == reflect.Slice {
|
|
||||||
// Repeated field.
|
|
||||||
for j := 0; j < fv.Len(); j++ {
|
|
||||||
if err := writeName(w, props); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if !w.compact {
|
|
||||||
if err := w.WriteByte(' '); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
v := fv.Index(j)
|
|
||||||
if v.Kind() == reflect.Ptr && v.IsNil() {
|
|
||||||
// A nil message in a repeated field is not valid,
|
|
||||||
// but we can handle that more gracefully than panicking.
|
|
||||||
if _, err := w.Write([]byte("<nil>\n")); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if err := tm.writeAny(w, v, props); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := w.WriteByte('\n'); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if fv.Kind() == reflect.Map {
|
|
||||||
// Map fields are rendered as a repeated struct with key/value fields.
|
|
||||||
keys := fv.MapKeys()
|
|
||||||
sort.Sort(mapKeys(keys))
|
|
||||||
for _, key := range keys {
|
|
||||||
val := fv.MapIndex(key)
|
|
||||||
if err := writeName(w, props); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if !w.compact {
|
|
||||||
if err := w.WriteByte(' '); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// open struct
|
|
||||||
if err := w.WriteByte('<'); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if !w.compact {
|
|
||||||
if err := w.WriteByte('\n'); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
w.indent()
|
|
||||||
// key
|
|
||||||
if _, err := w.WriteString("key:"); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if !w.compact {
|
|
||||||
if err := w.WriteByte(' '); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err := tm.writeAny(w, key, props.MapKeyProp); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := w.WriteByte('\n'); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// nil values aren't legal, but we can avoid panicking because of them.
|
|
||||||
if val.Kind() != reflect.Ptr || !val.IsNil() {
|
|
||||||
// value
|
|
||||||
if _, err := w.WriteString("value:"); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if !w.compact {
|
|
||||||
if err := w.WriteByte(' '); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err := tm.writeAny(w, val, props.MapValProp); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := w.WriteByte('\n'); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// close struct
|
|
||||||
w.unindent()
|
|
||||||
if err := w.WriteByte('>'); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := w.WriteByte('\n'); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if props.proto3 && fv.Kind() == reflect.Slice && fv.Len() == 0 {
|
|
||||||
// empty bytes field
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if fv.Kind() != reflect.Ptr && fv.Kind() != reflect.Slice {
|
|
||||||
// proto3 non-repeated scalar field; skip if zero value
|
|
||||||
if isProto3Zero(fv) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if fv.Kind() == reflect.Interface {
|
|
||||||
// Check if it is a oneof.
|
|
||||||
if st.Field(i).Tag.Get("protobuf_oneof") != "" {
|
|
||||||
// fv is nil, or holds a pointer to generated struct.
|
|
||||||
// That generated struct has exactly one field,
|
|
||||||
// which has a protobuf struct tag.
|
|
||||||
if fv.IsNil() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
inner := fv.Elem().Elem() // interface -> *T -> T
|
|
||||||
tag := inner.Type().Field(0).Tag.Get("protobuf")
|
|
||||||
props = new(Properties) // Overwrite the outer props var, but not its pointee.
|
|
||||||
props.Parse(tag)
|
|
||||||
// Write the value in the oneof, not the oneof itself.
|
|
||||||
fv = inner.Field(0)
|
|
||||||
|
|
||||||
// Special case to cope with malformed messages gracefully:
|
|
||||||
// If the value in the oneof is a nil pointer, don't panic
|
|
||||||
// in writeAny.
|
|
||||||
if fv.Kind() == reflect.Ptr && fv.IsNil() {
|
|
||||||
// Use errors.New so writeAny won't render quotes.
|
|
||||||
msg := errors.New("/* nil */")
|
|
||||||
fv = reflect.ValueOf(&msg).Elem()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := writeName(w, props); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if !w.compact {
|
|
||||||
if err := w.WriteByte(' '); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Enums have a String method, so writeAny will work fine.
|
|
||||||
if err := tm.writeAny(w, fv, props); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := w.WriteByte('\n'); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extensions (the XXX_extensions field).
|
|
||||||
pv := sv.Addr()
|
|
||||||
if _, err := extendable(pv.Interface()); err == nil {
|
|
||||||
if err := tm.writeExtensions(w, pv); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var textMarshalerType = reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem()
|
|
||||||
|
|
||||||
// writeAny writes an arbitrary field.
|
|
||||||
func (tm *TextMarshaler) writeAny(w *textWriter, v reflect.Value, props *Properties) error {
|
|
||||||
v = reflect.Indirect(v)
|
|
||||||
|
|
||||||
// Floats have special cases.
|
|
||||||
if v.Kind() == reflect.Float32 || v.Kind() == reflect.Float64 {
|
|
||||||
x := v.Float()
|
|
||||||
var b []byte
|
|
||||||
switch {
|
|
||||||
case math.IsInf(x, 1):
|
|
||||||
b = posInf
|
|
||||||
case math.IsInf(x, -1):
|
|
||||||
b = negInf
|
|
||||||
case math.IsNaN(x):
|
|
||||||
b = nan
|
|
||||||
}
|
|
||||||
if b != nil {
|
|
||||||
_, err := w.Write(b)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// Other values are handled below.
|
|
||||||
}
|
|
||||||
|
|
||||||
// We don't attempt to serialise every possible value type; only those
|
|
||||||
// that can occur in protocol buffers.
|
|
||||||
switch v.Kind() {
|
|
||||||
case reflect.Slice:
|
|
||||||
// Should only be a []byte; repeated fields are handled in writeStruct.
|
|
||||||
if err := writeString(w, string(v.Bytes())); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
case reflect.String:
|
|
||||||
if err := writeString(w, v.String()); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
case reflect.Struct:
|
|
||||||
// Required/optional group/message.
|
|
||||||
var bra, ket byte = '<', '>'
|
|
||||||
if props != nil && props.Wire == "group" {
|
|
||||||
bra, ket = '{', '}'
|
|
||||||
}
|
|
||||||
if err := w.WriteByte(bra); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if !w.compact {
|
|
||||||
if err := w.WriteByte('\n'); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
w.indent()
|
|
||||||
if v.CanAddr() {
|
|
||||||
// Calling v.Interface on a struct causes the reflect package to
|
|
||||||
// copy the entire struct. This is racy with the new Marshaler
|
|
||||||
// since we atomically update the XXX_sizecache.
|
|
||||||
//
|
|
||||||
// Thus, we retrieve a pointer to the struct if possible to avoid
|
|
||||||
// a race since v.Interface on the pointer doesn't copy the struct.
|
|
||||||
//
|
|
||||||
// If v is not addressable, then we are not worried about a race
|
|
||||||
// since it implies that the binary Marshaler cannot possibly be
|
|
||||||
// mutating this value.
|
|
||||||
v = v.Addr()
|
|
||||||
}
|
|
||||||
if v.Type().Implements(textMarshalerType) {
|
|
||||||
text, err := v.Interface().(encoding.TextMarshaler).MarshalText()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if _, err = w.Write(text); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if v.Kind() == reflect.Ptr {
|
|
||||||
v = v.Elem()
|
|
||||||
}
|
|
||||||
if err := tm.writeStruct(w, v); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
w.unindent()
|
|
||||||
if err := w.WriteByte(ket); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
_, err := fmt.Fprint(w, v.Interface())
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// equivalent to C's isprint.
|
|
||||||
func isprint(c byte) bool {
|
|
||||||
return c >= 0x20 && c < 0x7f
|
|
||||||
}
|
|
||||||
|
|
||||||
// writeString writes a string in the protocol buffer text format.
|
|
||||||
// It is similar to strconv.Quote except we don't use Go escape sequences,
|
|
||||||
// we treat the string as a byte sequence, and we use octal escapes.
|
|
||||||
// These differences are to maintain interoperability with the other
|
|
||||||
// languages' implementations of the text format.
|
|
||||||
func writeString(w *textWriter, s string) error {
|
|
||||||
// use WriteByte here to get any needed indent
|
|
||||||
if err := w.WriteByte('"'); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// Loop over the bytes, not the runes.
|
|
||||||
for i := 0; i < len(s); i++ {
|
|
||||||
var err error
|
|
||||||
// Divergence from C++: we don't escape apostrophes.
|
|
||||||
// There's no need to escape them, and the C++ parser
|
|
||||||
// copes with a naked apostrophe.
|
|
||||||
switch c := s[i]; c {
|
|
||||||
case '\n':
|
|
||||||
_, err = w.w.Write(backslashN)
|
|
||||||
case '\r':
|
|
||||||
_, err = w.w.Write(backslashR)
|
|
||||||
case '\t':
|
|
||||||
_, err = w.w.Write(backslashT)
|
|
||||||
case '"':
|
|
||||||
_, err = w.w.Write(backslashDQ)
|
|
||||||
case '\\':
|
|
||||||
_, err = w.w.Write(backslashBS)
|
|
||||||
default:
|
|
||||||
if isprint(c) {
|
|
||||||
err = w.w.WriteByte(c)
|
|
||||||
} else {
|
|
||||||
_, err = fmt.Fprintf(w.w, "\\%03o", c)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return w.WriteByte('"')
|
|
||||||
}
|
|
||||||
|
|
||||||
func writeUnknownStruct(w *textWriter, data []byte) (err error) {
|
|
||||||
if !w.compact {
|
|
||||||
if _, err := fmt.Fprintf(w, "/* %d unknown bytes */\n", len(data)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
b := NewBuffer(data)
|
|
||||||
for b.index < len(b.buf) {
|
|
||||||
x, err := b.DecodeVarint()
|
|
||||||
if err != nil {
|
|
||||||
_, err := fmt.Fprintf(w, "/* %v */\n", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
wire, tag := x&7, x>>3
|
|
||||||
if wire == WireEndGroup {
|
|
||||||
w.unindent()
|
|
||||||
if _, err := w.Write(endBraceNewline); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if _, err := fmt.Fprint(w, tag); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if wire != WireStartGroup {
|
|
||||||
if err := w.WriteByte(':'); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !w.compact || wire == WireStartGroup {
|
|
||||||
if err := w.WriteByte(' '); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
switch wire {
|
|
||||||
case WireBytes:
|
|
||||||
buf, e := b.DecodeRawBytes(false)
|
|
||||||
if e == nil {
|
|
||||||
_, err = fmt.Fprintf(w, "%q", buf)
|
|
||||||
} else {
|
|
||||||
_, err = fmt.Fprintf(w, "/* %v */", e)
|
|
||||||
}
|
|
||||||
case WireFixed32:
|
|
||||||
x, err = b.DecodeFixed32()
|
|
||||||
err = writeUnknownInt(w, x, err)
|
|
||||||
case WireFixed64:
|
|
||||||
x, err = b.DecodeFixed64()
|
|
||||||
err = writeUnknownInt(w, x, err)
|
|
||||||
case WireStartGroup:
|
|
||||||
err = w.WriteByte('{')
|
|
||||||
w.indent()
|
|
||||||
case WireVarint:
|
|
||||||
x, err = b.DecodeVarint()
|
|
||||||
err = writeUnknownInt(w, x, err)
|
|
||||||
default:
|
|
||||||
_, err = fmt.Fprintf(w, "/* unknown wire type %d */", wire)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err = w.WriteByte('\n'); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func writeUnknownInt(w *textWriter, x uint64, err error) error {
|
|
||||||
if err == nil {
|
|
||||||
_, err = fmt.Fprint(w, x)
|
|
||||||
} else {
|
|
||||||
_, err = fmt.Fprintf(w, "/* %v */", err)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
type int32Slice []int32
|
|
||||||
|
|
||||||
func (s int32Slice) Len() int { return len(s) }
|
|
||||||
func (s int32Slice) Less(i, j int) bool { return s[i] < s[j] }
|
|
||||||
func (s int32Slice) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
|
||||||
|
|
||||||
// writeExtensions writes all the extensions in pv.
|
|
||||||
// pv is assumed to be a pointer to a protocol message struct that is extendable.
|
|
||||||
func (tm *TextMarshaler) writeExtensions(w *textWriter, pv reflect.Value) error {
|
|
||||||
emap := extensionMaps[pv.Type().Elem()]
|
|
||||||
ep, _ := extendable(pv.Interface())
|
|
||||||
|
|
||||||
// Order the extensions by ID.
|
|
||||||
// This isn't strictly necessary, but it will give us
|
|
||||||
// canonical output, which will also make testing easier.
|
|
||||||
m, mu := ep.extensionsRead()
|
|
||||||
if m == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
mu.Lock()
|
|
||||||
ids := make([]int32, 0, len(m))
|
|
||||||
for id := range m {
|
|
||||||
ids = append(ids, id)
|
|
||||||
}
|
|
||||||
sort.Sort(int32Slice(ids))
|
|
||||||
mu.Unlock()
|
|
||||||
|
|
||||||
for _, extNum := range ids {
|
|
||||||
ext := m[extNum]
|
|
||||||
var desc *ExtensionDesc
|
|
||||||
if emap != nil {
|
|
||||||
desc = emap[extNum]
|
|
||||||
}
|
|
||||||
if desc == nil {
|
|
||||||
// Unknown extension.
|
|
||||||
if err := writeUnknownStruct(w, ext.enc); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
pb, err := GetExtension(ep, desc)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed getting extension: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Repeated extensions will appear as a slice.
|
|
||||||
if !desc.repeated() {
|
|
||||||
if err := tm.writeExtension(w, desc.Name, pb); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
v := reflect.ValueOf(pb)
|
|
||||||
for i := 0; i < v.Len(); i++ {
|
|
||||||
if err := tm.writeExtension(w, desc.Name, v.Index(i).Interface()); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tm *TextMarshaler) writeExtension(w *textWriter, name string, pb interface{}) error {
|
|
||||||
if _, err := fmt.Fprintf(w, "[%s]:", name); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if !w.compact {
|
|
||||||
if err := w.WriteByte(' '); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err := tm.writeAny(w, reflect.ValueOf(pb), nil); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := w.WriteByte('\n'); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *textWriter) writeIndent() {
|
|
||||||
if !w.complete {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
remain := w.ind * 2
|
|
||||||
for remain > 0 {
|
|
||||||
n := remain
|
|
||||||
if n > len(spaces) {
|
|
||||||
n = len(spaces)
|
|
||||||
}
|
|
||||||
w.w.Write(spaces[:n])
|
|
||||||
remain -= n
|
|
||||||
}
|
|
||||||
w.complete = false
|
|
||||||
}
|
|
||||||
|
|
||||||
// TextMarshaler is a configurable text format marshaler.
|
|
||||||
type TextMarshaler struct {
|
|
||||||
Compact bool // use compact text format (one line).
|
|
||||||
ExpandAny bool // expand google.protobuf.Any messages of known types
|
|
||||||
}
|
|
||||||
|
|
||||||
// Marshal writes a given protocol buffer in text format.
|
|
||||||
// The only errors returned are from w.
|
|
||||||
func (tm *TextMarshaler) Marshal(w io.Writer, pb Message) error {
|
|
||||||
val := reflect.ValueOf(pb)
|
|
||||||
if pb == nil || val.IsNil() {
|
|
||||||
w.Write([]byte("<nil>"))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
var bw *bufio.Writer
|
|
||||||
ww, ok := w.(writer)
|
|
||||||
if !ok {
|
|
||||||
bw = bufio.NewWriter(w)
|
|
||||||
ww = bw
|
|
||||||
}
|
|
||||||
aw := &textWriter{
|
|
||||||
w: ww,
|
|
||||||
complete: true,
|
|
||||||
compact: tm.Compact,
|
|
||||||
}
|
|
||||||
|
|
||||||
if etm, ok := pb.(encoding.TextMarshaler); ok {
|
|
||||||
text, err := etm.MarshalText()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if _, err = aw.Write(text); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if bw != nil {
|
|
||||||
return bw.Flush()
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
// Dereference the received pointer so we don't have outer < and >.
|
|
||||||
v := reflect.Indirect(val)
|
|
||||||
if err := tm.writeStruct(aw, v); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if bw != nil {
|
|
||||||
return bw.Flush()
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Text is the same as Marshal, but returns the string directly.
|
|
||||||
func (tm *TextMarshaler) Text(pb Message) string {
|
|
||||||
var buf bytes.Buffer
|
|
||||||
tm.Marshal(&buf, pb)
|
|
||||||
return buf.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
defaultTextMarshaler = TextMarshaler{}
|
|
||||||
compactTextMarshaler = TextMarshaler{Compact: true}
|
|
||||||
)
|
|
||||||
|
|
||||||
// TODO: consider removing some of the Marshal functions below.
|
|
||||||
|
|
||||||
// MarshalText writes a given protocol buffer in text format.
|
|
||||||
// The only errors returned are from w.
|
|
||||||
func MarshalText(w io.Writer, pb Message) error { return defaultTextMarshaler.Marshal(w, pb) }
|
|
||||||
|
|
||||||
// MarshalTextString is the same as MarshalText, but returns the string directly.
|
|
||||||
func MarshalTextString(pb Message) string { return defaultTextMarshaler.Text(pb) }
|
|
||||||
|
|
||||||
// CompactText writes a given protocol buffer in compact text format (one line).
|
|
||||||
func CompactText(w io.Writer, pb Message) error { return compactTextMarshaler.Marshal(w, pb) }
|
|
||||||
|
|
||||||
// CompactTextString is the same as CompactText, but returns the string directly.
|
|
||||||
func CompactTextString(pb Message) string { return compactTextMarshaler.Text(pb) }
|
|
|
@ -0,0 +1,801 @@
|
||||||
|
// Copyright 2010 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package proto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"unicode/utf8"
|
||||||
|
|
||||||
|
"google.golang.org/protobuf/encoding/prototext"
|
||||||
|
protoV2 "google.golang.org/protobuf/proto"
|
||||||
|
"google.golang.org/protobuf/reflect/protoreflect"
|
||||||
|
"google.golang.org/protobuf/reflect/protoregistry"
|
||||||
|
)
|
||||||
|
|
||||||
|
const wrapTextUnmarshalV2 = false
|
||||||
|
|
||||||
|
// ParseError is returned by UnmarshalText.
|
||||||
|
type ParseError struct {
|
||||||
|
Message string
|
||||||
|
|
||||||
|
// Deprecated: Do not use.
|
||||||
|
Line, Offset int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ParseError) Error() string {
|
||||||
|
if wrapTextUnmarshalV2 {
|
||||||
|
return e.Message
|
||||||
|
}
|
||||||
|
if e.Line == 1 {
|
||||||
|
return fmt.Sprintf("line 1.%d: %v", e.Offset, e.Message)
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("line %d: %v", e.Line, e.Message)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalText parses a proto text formatted string into m.
|
||||||
|
func UnmarshalText(s string, m Message) error {
|
||||||
|
if u, ok := m.(encoding.TextUnmarshaler); ok {
|
||||||
|
return u.UnmarshalText([]byte(s))
|
||||||
|
}
|
||||||
|
|
||||||
|
m.Reset()
|
||||||
|
mi := MessageV2(m)
|
||||||
|
|
||||||
|
if wrapTextUnmarshalV2 {
|
||||||
|
err := prototext.UnmarshalOptions{
|
||||||
|
AllowPartial: true,
|
||||||
|
}.Unmarshal([]byte(s), mi)
|
||||||
|
if err != nil {
|
||||||
|
return &ParseError{Message: err.Error()}
|
||||||
|
}
|
||||||
|
return checkRequiredNotSet(mi)
|
||||||
|
} else {
|
||||||
|
if err := newTextParser(s).unmarshalMessage(mi.ProtoReflect(), ""); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return checkRequiredNotSet(mi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type textParser struct {
|
||||||
|
s string // remaining input
|
||||||
|
done bool // whether the parsing is finished (success or error)
|
||||||
|
backed bool // whether back() was called
|
||||||
|
offset, line int
|
||||||
|
cur token
|
||||||
|
}
|
||||||
|
|
||||||
|
type token struct {
|
||||||
|
value string
|
||||||
|
err *ParseError
|
||||||
|
line int // line number
|
||||||
|
offset int // byte number from start of input, not start of line
|
||||||
|
unquoted string // the unquoted version of value, if it was a quoted string
|
||||||
|
}
|
||||||
|
|
||||||
|
func newTextParser(s string) *textParser {
|
||||||
|
p := new(textParser)
|
||||||
|
p.s = s
|
||||||
|
p.line = 1
|
||||||
|
p.cur.line = 1
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *textParser) unmarshalMessage(m protoreflect.Message, terminator string) (err error) {
|
||||||
|
md := m.Descriptor()
|
||||||
|
fds := md.Fields()
|
||||||
|
|
||||||
|
// A struct is a sequence of "name: value", terminated by one of
|
||||||
|
// '>' or '}', or the end of the input. A name may also be
|
||||||
|
// "[extension]" or "[type/url]".
|
||||||
|
//
|
||||||
|
// The whole struct can also be an expanded Any message, like:
|
||||||
|
// [type/url] < ... struct contents ... >
|
||||||
|
seen := make(map[protoreflect.FieldNumber]bool)
|
||||||
|
for {
|
||||||
|
tok := p.next()
|
||||||
|
if tok.err != nil {
|
||||||
|
return tok.err
|
||||||
|
}
|
||||||
|
if tok.value == terminator {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if tok.value == "[" {
|
||||||
|
if err := p.unmarshalExtensionOrAny(m, seen); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is a normal, non-extension field.
|
||||||
|
name := protoreflect.Name(tok.value)
|
||||||
|
fd := fds.ByName(name)
|
||||||
|
switch {
|
||||||
|
case fd == nil:
|
||||||
|
gd := fds.ByName(protoreflect.Name(strings.ToLower(string(name))))
|
||||||
|
if gd != nil && gd.Kind() == protoreflect.GroupKind && gd.Message().Name() == name {
|
||||||
|
fd = gd
|
||||||
|
}
|
||||||
|
case fd.Kind() == protoreflect.GroupKind && fd.Message().Name() != name:
|
||||||
|
fd = nil
|
||||||
|
case fd.IsWeak() && fd.Message().IsPlaceholder():
|
||||||
|
fd = nil
|
||||||
|
}
|
||||||
|
if fd == nil {
|
||||||
|
typeName := string(md.FullName())
|
||||||
|
if m, ok := m.Interface().(Message); ok {
|
||||||
|
t := reflect.TypeOf(m)
|
||||||
|
if t.Kind() == reflect.Ptr {
|
||||||
|
typeName = t.Elem().String()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return p.errorf("unknown field name %q in %v", name, typeName)
|
||||||
|
}
|
||||||
|
if od := fd.ContainingOneof(); od != nil && m.WhichOneof(od) != nil {
|
||||||
|
return p.errorf("field '%s' would overwrite already parsed oneof '%s'", name, od.Name())
|
||||||
|
}
|
||||||
|
if fd.Cardinality() != protoreflect.Repeated && seen[fd.Number()] {
|
||||||
|
return p.errorf("non-repeated field %q was repeated", fd.Name())
|
||||||
|
}
|
||||||
|
seen[fd.Number()] = true
|
||||||
|
|
||||||
|
// Consume any colon.
|
||||||
|
if err := p.checkForColon(fd); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse into the field.
|
||||||
|
v := m.Get(fd)
|
||||||
|
if !m.Has(fd) && (fd.IsList() || fd.IsMap() || fd.Message() != nil) {
|
||||||
|
v = m.Mutable(fd)
|
||||||
|
}
|
||||||
|
if v, err = p.unmarshalValue(v, fd); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
m.Set(fd, v)
|
||||||
|
|
||||||
|
if err := p.consumeOptionalSeparator(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *textParser) unmarshalExtensionOrAny(m protoreflect.Message, seen map[protoreflect.FieldNumber]bool) error {
|
||||||
|
name, err := p.consumeExtensionOrAnyName()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// If it contains a slash, it's an Any type URL.
|
||||||
|
if slashIdx := strings.LastIndex(name, "/"); slashIdx >= 0 {
|
||||||
|
tok := p.next()
|
||||||
|
if tok.err != nil {
|
||||||
|
return tok.err
|
||||||
|
}
|
||||||
|
// consume an optional colon
|
||||||
|
if tok.value == ":" {
|
||||||
|
tok = p.next()
|
||||||
|
if tok.err != nil {
|
||||||
|
return tok.err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var terminator string
|
||||||
|
switch tok.value {
|
||||||
|
case "<":
|
||||||
|
terminator = ">"
|
||||||
|
case "{":
|
||||||
|
terminator = "}"
|
||||||
|
default:
|
||||||
|
return p.errorf("expected '{' or '<', found %q", tok.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
mt, err := protoregistry.GlobalTypes.FindMessageByURL(name)
|
||||||
|
if err != nil {
|
||||||
|
return p.errorf("unrecognized message %q in google.protobuf.Any", name[slashIdx+len("/"):])
|
||||||
|
}
|
||||||
|
m2 := mt.New()
|
||||||
|
if err := p.unmarshalMessage(m2, terminator); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
b, err := protoV2.Marshal(m2.Interface())
|
||||||
|
if err != nil {
|
||||||
|
return p.errorf("failed to marshal message of type %q: %v", name[slashIdx+len("/"):], err)
|
||||||
|
}
|
||||||
|
|
||||||
|
urlFD := m.Descriptor().Fields().ByName("type_url")
|
||||||
|
valFD := m.Descriptor().Fields().ByName("value")
|
||||||
|
if seen[urlFD.Number()] {
|
||||||
|
return p.errorf("Any message unpacked multiple times, or %q already set", urlFD.Name())
|
||||||
|
}
|
||||||
|
if seen[valFD.Number()] {
|
||||||
|
return p.errorf("Any message unpacked multiple times, or %q already set", valFD.Name())
|
||||||
|
}
|
||||||
|
m.Set(urlFD, protoreflect.ValueOfString(name))
|
||||||
|
m.Set(valFD, protoreflect.ValueOfBytes(b))
|
||||||
|
seen[urlFD.Number()] = true
|
||||||
|
seen[valFD.Number()] = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
xname := protoreflect.FullName(name)
|
||||||
|
xt, _ := protoregistry.GlobalTypes.FindExtensionByName(xname)
|
||||||
|
if xt == nil && isMessageSet(m.Descriptor()) {
|
||||||
|
xt, _ = protoregistry.GlobalTypes.FindExtensionByName(xname.Append("message_set_extension"))
|
||||||
|
}
|
||||||
|
if xt == nil {
|
||||||
|
return p.errorf("unrecognized extension %q", name)
|
||||||
|
}
|
||||||
|
fd := xt.TypeDescriptor()
|
||||||
|
if fd.ContainingMessage().FullName() != m.Descriptor().FullName() {
|
||||||
|
return p.errorf("extension field %q does not extend message %q", name, m.Descriptor().FullName())
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := p.checkForColon(fd); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
v := m.Get(fd)
|
||||||
|
if !m.Has(fd) && (fd.IsList() || fd.IsMap() || fd.Message() != nil) {
|
||||||
|
v = m.Mutable(fd)
|
||||||
|
}
|
||||||
|
v, err = p.unmarshalValue(v, fd)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
m.Set(fd, v)
|
||||||
|
return p.consumeOptionalSeparator()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *textParser) unmarshalValue(v protoreflect.Value, fd protoreflect.FieldDescriptor) (protoreflect.Value, error) {
|
||||||
|
tok := p.next()
|
||||||
|
if tok.err != nil {
|
||||||
|
return v, tok.err
|
||||||
|
}
|
||||||
|
if tok.value == "" {
|
||||||
|
return v, p.errorf("unexpected EOF")
|
||||||
|
}
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case fd.IsList():
|
||||||
|
lv := v.List()
|
||||||
|
var err error
|
||||||
|
if tok.value == "[" {
|
||||||
|
// Repeated field with list notation, like [1,2,3].
|
||||||
|
for {
|
||||||
|
vv := lv.NewElement()
|
||||||
|
vv, err = p.unmarshalSingularValue(vv, fd)
|
||||||
|
if err != nil {
|
||||||
|
return v, err
|
||||||
|
}
|
||||||
|
lv.Append(vv)
|
||||||
|
|
||||||
|
tok := p.next()
|
||||||
|
if tok.err != nil {
|
||||||
|
return v, tok.err
|
||||||
|
}
|
||||||
|
if tok.value == "]" {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if tok.value != "," {
|
||||||
|
return v, p.errorf("Expected ']' or ',' found %q", tok.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return v, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// One value of the repeated field.
|
||||||
|
p.back()
|
||||||
|
vv := lv.NewElement()
|
||||||
|
vv, err = p.unmarshalSingularValue(vv, fd)
|
||||||
|
if err != nil {
|
||||||
|
return v, err
|
||||||
|
}
|
||||||
|
lv.Append(vv)
|
||||||
|
return v, nil
|
||||||
|
case fd.IsMap():
|
||||||
|
// The map entry should be this sequence of tokens:
|
||||||
|
// < key : KEY value : VALUE >
|
||||||
|
// However, implementations may omit key or value, and technically
|
||||||
|
// we should support them in any order.
|
||||||
|
var terminator string
|
||||||
|
switch tok.value {
|
||||||
|
case "<":
|
||||||
|
terminator = ">"
|
||||||
|
case "{":
|
||||||
|
terminator = "}"
|
||||||
|
default:
|
||||||
|
return v, p.errorf("expected '{' or '<', found %q", tok.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
keyFD := fd.MapKey()
|
||||||
|
valFD := fd.MapValue()
|
||||||
|
|
||||||
|
mv := v.Map()
|
||||||
|
kv := keyFD.Default()
|
||||||
|
vv := mv.NewValue()
|
||||||
|
for {
|
||||||
|
tok := p.next()
|
||||||
|
if tok.err != nil {
|
||||||
|
return v, tok.err
|
||||||
|
}
|
||||||
|
if tok.value == terminator {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
var err error
|
||||||
|
switch tok.value {
|
||||||
|
case "key":
|
||||||
|
if err := p.consumeToken(":"); err != nil {
|
||||||
|
return v, err
|
||||||
|
}
|
||||||
|
if kv, err = p.unmarshalSingularValue(kv, keyFD); err != nil {
|
||||||
|
return v, err
|
||||||
|
}
|
||||||
|
if err := p.consumeOptionalSeparator(); err != nil {
|
||||||
|
return v, err
|
||||||
|
}
|
||||||
|
case "value":
|
||||||
|
if err := p.checkForColon(valFD); err != nil {
|
||||||
|
return v, err
|
||||||
|
}
|
||||||
|
if vv, err = p.unmarshalSingularValue(vv, valFD); err != nil {
|
||||||
|
return v, err
|
||||||
|
}
|
||||||
|
if err := p.consumeOptionalSeparator(); err != nil {
|
||||||
|
return v, err
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
p.back()
|
||||||
|
return v, p.errorf(`expected "key", "value", or %q, found %q`, terminator, tok.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mv.Set(kv.MapKey(), vv)
|
||||||
|
return v, nil
|
||||||
|
default:
|
||||||
|
p.back()
|
||||||
|
return p.unmarshalSingularValue(v, fd)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *textParser) unmarshalSingularValue(v protoreflect.Value, fd protoreflect.FieldDescriptor) (protoreflect.Value, error) {
|
||||||
|
tok := p.next()
|
||||||
|
if tok.err != nil {
|
||||||
|
return v, tok.err
|
||||||
|
}
|
||||||
|
if tok.value == "" {
|
||||||
|
return v, p.errorf("unexpected EOF")
|
||||||
|
}
|
||||||
|
|
||||||
|
switch fd.Kind() {
|
||||||
|
case protoreflect.BoolKind:
|
||||||
|
switch tok.value {
|
||||||
|
case "true", "1", "t", "True":
|
||||||
|
return protoreflect.ValueOfBool(true), nil
|
||||||
|
case "false", "0", "f", "False":
|
||||||
|
return protoreflect.ValueOfBool(false), nil
|
||||||
|
}
|
||||||
|
case protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Sfixed32Kind:
|
||||||
|
if x, err := strconv.ParseInt(tok.value, 0, 32); err == nil {
|
||||||
|
return protoreflect.ValueOfInt32(int32(x)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// The C++ parser accepts large positive hex numbers that uses
|
||||||
|
// two's complement arithmetic to represent negative numbers.
|
||||||
|
// This feature is here for backwards compatibility with C++.
|
||||||
|
if strings.HasPrefix(tok.value, "0x") {
|
||||||
|
if x, err := strconv.ParseUint(tok.value, 0, 32); err == nil {
|
||||||
|
return protoreflect.ValueOfInt32(int32(-(int64(^x) + 1))), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Sfixed64Kind:
|
||||||
|
if x, err := strconv.ParseInt(tok.value, 0, 64); err == nil {
|
||||||
|
return protoreflect.ValueOfInt64(int64(x)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// The C++ parser accepts large positive hex numbers that uses
|
||||||
|
// two's complement arithmetic to represent negative numbers.
|
||||||
|
// This feature is here for backwards compatibility with C++.
|
||||||
|
if strings.HasPrefix(tok.value, "0x") {
|
||||||
|
if x, err := strconv.ParseUint(tok.value, 0, 64); err == nil {
|
||||||
|
return protoreflect.ValueOfInt64(int64(-(int64(^x) + 1))), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case protoreflect.Uint32Kind, protoreflect.Fixed32Kind:
|
||||||
|
if x, err := strconv.ParseUint(tok.value, 0, 32); err == nil {
|
||||||
|
return protoreflect.ValueOfUint32(uint32(x)), nil
|
||||||
|
}
|
||||||
|
case protoreflect.Uint64Kind, protoreflect.Fixed64Kind:
|
||||||
|
if x, err := strconv.ParseUint(tok.value, 0, 64); err == nil {
|
||||||
|
return protoreflect.ValueOfUint64(uint64(x)), nil
|
||||||
|
}
|
||||||
|
case protoreflect.FloatKind:
|
||||||
|
// Ignore 'f' for compatibility with output generated by C++,
|
||||||
|
// but don't remove 'f' when the value is "-inf" or "inf".
|
||||||
|
v := tok.value
|
||||||
|
if strings.HasSuffix(v, "f") && v != "-inf" && v != "inf" {
|
||||||
|
v = v[:len(v)-len("f")]
|
||||||
|
}
|
||||||
|
if x, err := strconv.ParseFloat(v, 32); err == nil {
|
||||||
|
return protoreflect.ValueOfFloat32(float32(x)), nil
|
||||||
|
}
|
||||||
|
case protoreflect.DoubleKind:
|
||||||
|
// Ignore 'f' for compatibility with output generated by C++,
|
||||||
|
// but don't remove 'f' when the value is "-inf" or "inf".
|
||||||
|
v := tok.value
|
||||||
|
if strings.HasSuffix(v, "f") && v != "-inf" && v != "inf" {
|
||||||
|
v = v[:len(v)-len("f")]
|
||||||
|
}
|
||||||
|
if x, err := strconv.ParseFloat(v, 64); err == nil {
|
||||||
|
return protoreflect.ValueOfFloat64(float64(x)), nil
|
||||||
|
}
|
||||||
|
case protoreflect.StringKind:
|
||||||
|
if isQuote(tok.value[0]) {
|
||||||
|
return protoreflect.ValueOfString(tok.unquoted), nil
|
||||||
|
}
|
||||||
|
case protoreflect.BytesKind:
|
||||||
|
if isQuote(tok.value[0]) {
|
||||||
|
return protoreflect.ValueOfBytes([]byte(tok.unquoted)), nil
|
||||||
|
}
|
||||||
|
case protoreflect.EnumKind:
|
||||||
|
if x, err := strconv.ParseInt(tok.value, 0, 32); err == nil {
|
||||||
|
return protoreflect.ValueOfEnum(protoreflect.EnumNumber(x)), nil
|
||||||
|
}
|
||||||
|
vd := fd.Enum().Values().ByName(protoreflect.Name(tok.value))
|
||||||
|
if vd != nil {
|
||||||
|
return protoreflect.ValueOfEnum(vd.Number()), nil
|
||||||
|
}
|
||||||
|
case protoreflect.MessageKind, protoreflect.GroupKind:
|
||||||
|
var terminator string
|
||||||
|
switch tok.value {
|
||||||
|
case "{":
|
||||||
|
terminator = "}"
|
||||||
|
case "<":
|
||||||
|
terminator = ">"
|
||||||
|
default:
|
||||||
|
return v, p.errorf("expected '{' or '<', found %q", tok.value)
|
||||||
|
}
|
||||||
|
err := p.unmarshalMessage(v.Message(), terminator)
|
||||||
|
return v, err
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("invalid kind %v", fd.Kind()))
|
||||||
|
}
|
||||||
|
return v, p.errorf("invalid %v: %v", fd.Kind(), tok.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Consume a ':' from the input stream (if the next token is a colon),
|
||||||
|
// returning an error if a colon is needed but not present.
|
||||||
|
func (p *textParser) checkForColon(fd protoreflect.FieldDescriptor) *ParseError {
|
||||||
|
tok := p.next()
|
||||||
|
if tok.err != nil {
|
||||||
|
return tok.err
|
||||||
|
}
|
||||||
|
if tok.value != ":" {
|
||||||
|
if fd.Message() == nil {
|
||||||
|
return p.errorf("expected ':', found %q", tok.value)
|
||||||
|
}
|
||||||
|
p.back()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// consumeExtensionOrAnyName consumes an extension name or an Any type URL and
|
||||||
|
// the following ']'. It returns the name or URL consumed.
|
||||||
|
func (p *textParser) consumeExtensionOrAnyName() (string, error) {
|
||||||
|
tok := p.next()
|
||||||
|
if tok.err != nil {
|
||||||
|
return "", tok.err
|
||||||
|
}
|
||||||
|
|
||||||
|
// If extension name or type url is quoted, it's a single token.
|
||||||
|
if len(tok.value) > 2 && isQuote(tok.value[0]) && tok.value[len(tok.value)-1] == tok.value[0] {
|
||||||
|
name, err := unquoteC(tok.value[1:len(tok.value)-1], rune(tok.value[0]))
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return name, p.consumeToken("]")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Consume everything up to "]"
|
||||||
|
var parts []string
|
||||||
|
for tok.value != "]" {
|
||||||
|
parts = append(parts, tok.value)
|
||||||
|
tok = p.next()
|
||||||
|
if tok.err != nil {
|
||||||
|
return "", p.errorf("unrecognized type_url or extension name: %s", tok.err)
|
||||||
|
}
|
||||||
|
if p.done && tok.value != "]" {
|
||||||
|
return "", p.errorf("unclosed type_url or extension name")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return strings.Join(parts, ""), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// consumeOptionalSeparator consumes an optional semicolon or comma.
|
||||||
|
// It is used in unmarshalMessage to provide backward compatibility.
|
||||||
|
func (p *textParser) consumeOptionalSeparator() error {
|
||||||
|
tok := p.next()
|
||||||
|
if tok.err != nil {
|
||||||
|
return tok.err
|
||||||
|
}
|
||||||
|
if tok.value != ";" && tok.value != "," {
|
||||||
|
p.back()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *textParser) errorf(format string, a ...interface{}) *ParseError {
|
||||||
|
pe := &ParseError{fmt.Sprintf(format, a...), p.cur.line, p.cur.offset}
|
||||||
|
p.cur.err = pe
|
||||||
|
p.done = true
|
||||||
|
return pe
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *textParser) skipWhitespace() {
|
||||||
|
i := 0
|
||||||
|
for i < len(p.s) && (isWhitespace(p.s[i]) || p.s[i] == '#') {
|
||||||
|
if p.s[i] == '#' {
|
||||||
|
// comment; skip to end of line or input
|
||||||
|
for i < len(p.s) && p.s[i] != '\n' {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
if i == len(p.s) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if p.s[i] == '\n' {
|
||||||
|
p.line++
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
p.offset += i
|
||||||
|
p.s = p.s[i:len(p.s)]
|
||||||
|
if len(p.s) == 0 {
|
||||||
|
p.done = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *textParser) advance() {
|
||||||
|
// Skip whitespace
|
||||||
|
p.skipWhitespace()
|
||||||
|
if p.done {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start of non-whitespace
|
||||||
|
p.cur.err = nil
|
||||||
|
p.cur.offset, p.cur.line = p.offset, p.line
|
||||||
|
p.cur.unquoted = ""
|
||||||
|
switch p.s[0] {
|
||||||
|
case '<', '>', '{', '}', ':', '[', ']', ';', ',', '/':
|
||||||
|
// Single symbol
|
||||||
|
p.cur.value, p.s = p.s[0:1], p.s[1:len(p.s)]
|
||||||
|
case '"', '\'':
|
||||||
|
// Quoted string
|
||||||
|
i := 1
|
||||||
|
for i < len(p.s) && p.s[i] != p.s[0] && p.s[i] != '\n' {
|
||||||
|
if p.s[i] == '\\' && i+1 < len(p.s) {
|
||||||
|
// skip escaped char
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
if i >= len(p.s) || p.s[i] != p.s[0] {
|
||||||
|
p.errorf("unmatched quote")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
unq, err := unquoteC(p.s[1:i], rune(p.s[0]))
|
||||||
|
if err != nil {
|
||||||
|
p.errorf("invalid quoted string %s: %v", p.s[0:i+1], err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
p.cur.value, p.s = p.s[0:i+1], p.s[i+1:len(p.s)]
|
||||||
|
p.cur.unquoted = unq
|
||||||
|
default:
|
||||||
|
i := 0
|
||||||
|
for i < len(p.s) && isIdentOrNumberChar(p.s[i]) {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
if i == 0 {
|
||||||
|
p.errorf("unexpected byte %#x", p.s[0])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
p.cur.value, p.s = p.s[0:i], p.s[i:len(p.s)]
|
||||||
|
}
|
||||||
|
p.offset += len(p.cur.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Back off the parser by one token. Can only be done between calls to next().
|
||||||
|
// It makes the next advance() a no-op.
|
||||||
|
func (p *textParser) back() { p.backed = true }
|
||||||
|
|
||||||
|
// Advances the parser and returns the new current token.
|
||||||
|
func (p *textParser) next() *token {
|
||||||
|
if p.backed || p.done {
|
||||||
|
p.backed = false
|
||||||
|
return &p.cur
|
||||||
|
}
|
||||||
|
p.advance()
|
||||||
|
if p.done {
|
||||||
|
p.cur.value = ""
|
||||||
|
} else if len(p.cur.value) > 0 && isQuote(p.cur.value[0]) {
|
||||||
|
// Look for multiple quoted strings separated by whitespace,
|
||||||
|
// and concatenate them.
|
||||||
|
cat := p.cur
|
||||||
|
for {
|
||||||
|
p.skipWhitespace()
|
||||||
|
if p.done || !isQuote(p.s[0]) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
p.advance()
|
||||||
|
if p.cur.err != nil {
|
||||||
|
return &p.cur
|
||||||
|
}
|
||||||
|
cat.value += " " + p.cur.value
|
||||||
|
cat.unquoted += p.cur.unquoted
|
||||||
|
}
|
||||||
|
p.done = false // parser may have seen EOF, but we want to return cat
|
||||||
|
p.cur = cat
|
||||||
|
}
|
||||||
|
return &p.cur
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *textParser) consumeToken(s string) error {
|
||||||
|
tok := p.next()
|
||||||
|
if tok.err != nil {
|
||||||
|
return tok.err
|
||||||
|
}
|
||||||
|
if tok.value != s {
|
||||||
|
p.back()
|
||||||
|
return p.errorf("expected %q, found %q", s, tok.value)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var errBadUTF8 = errors.New("proto: bad UTF-8")
|
||||||
|
|
||||||
|
func unquoteC(s string, quote rune) (string, error) {
|
||||||
|
// This is based on C++'s tokenizer.cc.
|
||||||
|
// Despite its name, this is *not* parsing C syntax.
|
||||||
|
// For instance, "\0" is an invalid quoted string.
|
||||||
|
|
||||||
|
// Avoid allocation in trivial cases.
|
||||||
|
simple := true
|
||||||
|
for _, r := range s {
|
||||||
|
if r == '\\' || r == quote {
|
||||||
|
simple = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if simple {
|
||||||
|
return s, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := make([]byte, 0, 3*len(s)/2)
|
||||||
|
for len(s) > 0 {
|
||||||
|
r, n := utf8.DecodeRuneInString(s)
|
||||||
|
if r == utf8.RuneError && n == 1 {
|
||||||
|
return "", errBadUTF8
|
||||||
|
}
|
||||||
|
s = s[n:]
|
||||||
|
if r != '\\' {
|
||||||
|
if r < utf8.RuneSelf {
|
||||||
|
buf = append(buf, byte(r))
|
||||||
|
} else {
|
||||||
|
buf = append(buf, string(r)...)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
ch, tail, err := unescape(s)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
buf = append(buf, ch...)
|
||||||
|
s = tail
|
||||||
|
}
|
||||||
|
return string(buf), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func unescape(s string) (ch string, tail string, err error) {
|
||||||
|
r, n := utf8.DecodeRuneInString(s)
|
||||||
|
if r == utf8.RuneError && n == 1 {
|
||||||
|
return "", "", errBadUTF8
|
||||||
|
}
|
||||||
|
s = s[n:]
|
||||||
|
switch r {
|
||||||
|
case 'a':
|
||||||
|
return "\a", s, nil
|
||||||
|
case 'b':
|
||||||
|
return "\b", s, nil
|
||||||
|
case 'f':
|
||||||
|
return "\f", s, nil
|
||||||
|
case 'n':
|
||||||
|
return "\n", s, nil
|
||||||
|
case 'r':
|
||||||
|
return "\r", s, nil
|
||||||
|
case 't':
|
||||||
|
return "\t", s, nil
|
||||||
|
case 'v':
|
||||||
|
return "\v", s, nil
|
||||||
|
case '?':
|
||||||
|
return "?", s, nil // trigraph workaround
|
||||||
|
case '\'', '"', '\\':
|
||||||
|
return string(r), s, nil
|
||||||
|
case '0', '1', '2', '3', '4', '5', '6', '7':
|
||||||
|
if len(s) < 2 {
|
||||||
|
return "", "", fmt.Errorf(`\%c requires 2 following digits`, r)
|
||||||
|
}
|
||||||
|
ss := string(r) + s[:2]
|
||||||
|
s = s[2:]
|
||||||
|
i, err := strconv.ParseUint(ss, 8, 8)
|
||||||
|
if err != nil {
|
||||||
|
return "", "", fmt.Errorf(`\%s contains non-octal digits`, ss)
|
||||||
|
}
|
||||||
|
return string([]byte{byte(i)}), s, nil
|
||||||
|
case 'x', 'X', 'u', 'U':
|
||||||
|
var n int
|
||||||
|
switch r {
|
||||||
|
case 'x', 'X':
|
||||||
|
n = 2
|
||||||
|
case 'u':
|
||||||
|
n = 4
|
||||||
|
case 'U':
|
||||||
|
n = 8
|
||||||
|
}
|
||||||
|
if len(s) < n {
|
||||||
|
return "", "", fmt.Errorf(`\%c requires %d following digits`, r, n)
|
||||||
|
}
|
||||||
|
ss := s[:n]
|
||||||
|
s = s[n:]
|
||||||
|
i, err := strconv.ParseUint(ss, 16, 64)
|
||||||
|
if err != nil {
|
||||||
|
return "", "", fmt.Errorf(`\%c%s contains non-hexadecimal digits`, r, ss)
|
||||||
|
}
|
||||||
|
if r == 'x' || r == 'X' {
|
||||||
|
return string([]byte{byte(i)}), s, nil
|
||||||
|
}
|
||||||
|
if i > utf8.MaxRune {
|
||||||
|
return "", "", fmt.Errorf(`\%c%s is not a valid Unicode code point`, r, ss)
|
||||||
|
}
|
||||||
|
return string(i), s, nil
|
||||||
|
}
|
||||||
|
return "", "", fmt.Errorf(`unknown escape \%c`, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func isIdentOrNumberChar(c byte) bool {
|
||||||
|
switch {
|
||||||
|
case 'A' <= c && c <= 'Z', 'a' <= c && c <= 'z':
|
||||||
|
return true
|
||||||
|
case '0' <= c && c <= '9':
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
switch c {
|
||||||
|
case '-', '+', '.', '_':
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func isWhitespace(c byte) bool {
|
||||||
|
switch c {
|
||||||
|
case ' ', '\t', '\n', '\r':
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func isQuote(c byte) bool {
|
||||||
|
switch c {
|
||||||
|
case '"', '\'':
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
|
@ -0,0 +1,560 @@
|
||||||
|
// Copyright 2010 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package proto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"math"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"google.golang.org/protobuf/encoding/prototext"
|
||||||
|
"google.golang.org/protobuf/encoding/protowire"
|
||||||
|
"google.golang.org/protobuf/proto"
|
||||||
|
"google.golang.org/protobuf/reflect/protoreflect"
|
||||||
|
"google.golang.org/protobuf/reflect/protoregistry"
|
||||||
|
)
|
||||||
|
|
||||||
|
const wrapTextMarshalV2 = false
|
||||||
|
|
||||||
|
// TextMarshaler is a configurable text format marshaler.
|
||||||
|
type TextMarshaler struct {
|
||||||
|
Compact bool // use compact text format (one line)
|
||||||
|
ExpandAny bool // expand google.protobuf.Any messages of known types
|
||||||
|
}
|
||||||
|
|
||||||
|
// Marshal writes the proto text format of m to w.
|
||||||
|
func (tm *TextMarshaler) Marshal(w io.Writer, m Message) error {
|
||||||
|
b, err := tm.marshal(m)
|
||||||
|
if len(b) > 0 {
|
||||||
|
if _, err := w.Write(b); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Text returns a proto text formatted string of m.
|
||||||
|
func (tm *TextMarshaler) Text(m Message) string {
|
||||||
|
b, _ := tm.marshal(m)
|
||||||
|
return string(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tm *TextMarshaler) marshal(m Message) ([]byte, error) {
|
||||||
|
mr := MessageReflect(m)
|
||||||
|
if mr == nil || !mr.IsValid() {
|
||||||
|
return []byte("<nil>"), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if wrapTextMarshalV2 {
|
||||||
|
if m, ok := m.(encoding.TextMarshaler); ok {
|
||||||
|
return m.MarshalText()
|
||||||
|
}
|
||||||
|
|
||||||
|
opts := prototext.MarshalOptions{
|
||||||
|
AllowPartial: true,
|
||||||
|
EmitUnknown: true,
|
||||||
|
}
|
||||||
|
if !tm.Compact {
|
||||||
|
opts.Indent = " "
|
||||||
|
}
|
||||||
|
if !tm.ExpandAny {
|
||||||
|
opts.Resolver = (*protoregistry.Types)(nil)
|
||||||
|
}
|
||||||
|
return opts.Marshal(mr.Interface())
|
||||||
|
} else {
|
||||||
|
w := &textWriter{
|
||||||
|
compact: tm.Compact,
|
||||||
|
expandAny: tm.ExpandAny,
|
||||||
|
complete: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
if m, ok := m.(encoding.TextMarshaler); ok {
|
||||||
|
b, err := m.MarshalText()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
w.Write(b)
|
||||||
|
return w.buf, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
err := w.writeMessage(mr)
|
||||||
|
return w.buf, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
defaultTextMarshaler = TextMarshaler{}
|
||||||
|
compactTextMarshaler = TextMarshaler{Compact: true}
|
||||||
|
)
|
||||||
|
|
||||||
|
// MarshalText writes the proto text format of m to w.
|
||||||
|
func MarshalText(w io.Writer, pb Message) error { return defaultTextMarshaler.Marshal(w, pb) }
|
||||||
|
|
||||||
|
// MarshalTextString returns a proto text formatted string of m.
|
||||||
|
func MarshalTextString(pb Message) string { return defaultTextMarshaler.Text(pb) }
|
||||||
|
|
||||||
|
// CompactText writes the compact proto text format of m to w.
|
||||||
|
func CompactText(w io.Writer, pb Message) error { return compactTextMarshaler.Marshal(w, pb) }
|
||||||
|
|
||||||
|
// CompactTextString returns a compact proto text formatted string of m.
|
||||||
|
func CompactTextString(pb Message) string { return compactTextMarshaler.Text(pb) }
|
||||||
|
|
||||||
|
var (
|
||||||
|
newline = []byte("\n")
|
||||||
|
endBraceNewline = []byte("}\n")
|
||||||
|
posInf = []byte("inf")
|
||||||
|
negInf = []byte("-inf")
|
||||||
|
nan = []byte("nan")
|
||||||
|
)
|
||||||
|
|
||||||
|
// textWriter is an io.Writer that tracks its indentation level.
|
||||||
|
type textWriter struct {
|
||||||
|
compact bool // same as TextMarshaler.Compact
|
||||||
|
expandAny bool // same as TextMarshaler.ExpandAny
|
||||||
|
complete bool // whether the current position is a complete line
|
||||||
|
indent int // indentation level; never negative
|
||||||
|
buf []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *textWriter) Write(p []byte) (n int, _ error) {
|
||||||
|
newlines := bytes.Count(p, newline)
|
||||||
|
if newlines == 0 {
|
||||||
|
if !w.compact && w.complete {
|
||||||
|
w.writeIndent()
|
||||||
|
}
|
||||||
|
w.buf = append(w.buf, p...)
|
||||||
|
w.complete = false
|
||||||
|
return len(p), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
frags := bytes.SplitN(p, newline, newlines+1)
|
||||||
|
if w.compact {
|
||||||
|
for i, frag := range frags {
|
||||||
|
if i > 0 {
|
||||||
|
w.buf = append(w.buf, ' ')
|
||||||
|
n++
|
||||||
|
}
|
||||||
|
w.buf = append(w.buf, frag...)
|
||||||
|
n += len(frag)
|
||||||
|
}
|
||||||
|
return n, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, frag := range frags {
|
||||||
|
if w.complete {
|
||||||
|
w.writeIndent()
|
||||||
|
}
|
||||||
|
w.buf = append(w.buf, frag...)
|
||||||
|
n += len(frag)
|
||||||
|
if i+1 < len(frags) {
|
||||||
|
w.buf = append(w.buf, '\n')
|
||||||
|
n++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
w.complete = len(frags[len(frags)-1]) == 0
|
||||||
|
return n, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *textWriter) WriteByte(c byte) error {
|
||||||
|
if w.compact && c == '\n' {
|
||||||
|
c = ' '
|
||||||
|
}
|
||||||
|
if !w.compact && w.complete {
|
||||||
|
w.writeIndent()
|
||||||
|
}
|
||||||
|
w.buf = append(w.buf, c)
|
||||||
|
w.complete = c == '\n'
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *textWriter) writeName(fd protoreflect.FieldDescriptor) {
|
||||||
|
if !w.compact && w.complete {
|
||||||
|
w.writeIndent()
|
||||||
|
}
|
||||||
|
w.complete = false
|
||||||
|
|
||||||
|
if fd.Kind() != protoreflect.GroupKind {
|
||||||
|
w.buf = append(w.buf, fd.Name()...)
|
||||||
|
w.WriteByte(':')
|
||||||
|
} else {
|
||||||
|
// Use message type name for group field name.
|
||||||
|
w.buf = append(w.buf, fd.Message().Name()...)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !w.compact {
|
||||||
|
w.WriteByte(' ')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func requiresQuotes(u string) bool {
|
||||||
|
// When type URL contains any characters except [0-9A-Za-z./\-]*, it must be quoted.
|
||||||
|
for _, ch := range u {
|
||||||
|
switch {
|
||||||
|
case ch == '.' || ch == '/' || ch == '_':
|
||||||
|
continue
|
||||||
|
case '0' <= ch && ch <= '9':
|
||||||
|
continue
|
||||||
|
case 'A' <= ch && ch <= 'Z':
|
||||||
|
continue
|
||||||
|
case 'a' <= ch && ch <= 'z':
|
||||||
|
continue
|
||||||
|
default:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// writeProto3Any writes an expanded google.protobuf.Any message.
|
||||||
|
//
|
||||||
|
// It returns (false, nil) if sv value can't be unmarshaled (e.g. because
|
||||||
|
// required messages are not linked in).
|
||||||
|
//
|
||||||
|
// It returns (true, error) when sv was written in expanded format or an error
|
||||||
|
// was encountered.
|
||||||
|
func (w *textWriter) writeProto3Any(m protoreflect.Message) (bool, error) {
|
||||||
|
md := m.Descriptor()
|
||||||
|
fdURL := md.Fields().ByName("type_url")
|
||||||
|
fdVal := md.Fields().ByName("value")
|
||||||
|
|
||||||
|
url := m.Get(fdURL).String()
|
||||||
|
mt, err := protoregistry.GlobalTypes.FindMessageByURL(url)
|
||||||
|
if err != nil {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
b := m.Get(fdVal).Bytes()
|
||||||
|
m2 := mt.New()
|
||||||
|
if err := proto.Unmarshal(b, m2.Interface()); err != nil {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
w.Write([]byte("["))
|
||||||
|
if requiresQuotes(url) {
|
||||||
|
w.writeQuotedString(url)
|
||||||
|
} else {
|
||||||
|
w.Write([]byte(url))
|
||||||
|
}
|
||||||
|
if w.compact {
|
||||||
|
w.Write([]byte("]:<"))
|
||||||
|
} else {
|
||||||
|
w.Write([]byte("]: <\n"))
|
||||||
|
w.indent++
|
||||||
|
}
|
||||||
|
if err := w.writeMessage(m2); err != nil {
|
||||||
|
return true, err
|
||||||
|
}
|
||||||
|
if w.compact {
|
||||||
|
w.Write([]byte("> "))
|
||||||
|
} else {
|
||||||
|
w.indent--
|
||||||
|
w.Write([]byte(">\n"))
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *textWriter) writeMessage(m protoreflect.Message) error {
|
||||||
|
md := m.Descriptor()
|
||||||
|
if w.expandAny && md.FullName() == "google.protobuf.Any" {
|
||||||
|
if canExpand, err := w.writeProto3Any(m); canExpand {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fds := md.Fields()
|
||||||
|
for i := 0; i < fds.Len(); {
|
||||||
|
fd := fds.Get(i)
|
||||||
|
if od := fd.ContainingOneof(); od != nil {
|
||||||
|
fd = m.WhichOneof(od)
|
||||||
|
i += od.Fields().Len()
|
||||||
|
} else {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
if fd == nil || !m.Has(fd) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case fd.IsList():
|
||||||
|
lv := m.Get(fd).List()
|
||||||
|
for j := 0; j < lv.Len(); j++ {
|
||||||
|
w.writeName(fd)
|
||||||
|
v := lv.Get(j)
|
||||||
|
if err := w.writeSingularValue(v, fd); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
w.WriteByte('\n')
|
||||||
|
}
|
||||||
|
case fd.IsMap():
|
||||||
|
kfd := fd.MapKey()
|
||||||
|
vfd := fd.MapValue()
|
||||||
|
mv := m.Get(fd).Map()
|
||||||
|
|
||||||
|
type entry struct{ key, val protoreflect.Value }
|
||||||
|
var entries []entry
|
||||||
|
mv.Range(func(k protoreflect.MapKey, v protoreflect.Value) bool {
|
||||||
|
entries = append(entries, entry{k.Value(), v})
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
sort.Slice(entries, func(i, j int) bool {
|
||||||
|
switch kfd.Kind() {
|
||||||
|
case protoreflect.BoolKind:
|
||||||
|
return !entries[i].key.Bool() && entries[j].key.Bool()
|
||||||
|
case protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Sfixed32Kind, protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Sfixed64Kind:
|
||||||
|
return entries[i].key.Int() < entries[j].key.Int()
|
||||||
|
case protoreflect.Uint32Kind, protoreflect.Fixed32Kind, protoreflect.Uint64Kind, protoreflect.Fixed64Kind:
|
||||||
|
return entries[i].key.Uint() < entries[j].key.Uint()
|
||||||
|
case protoreflect.StringKind:
|
||||||
|
return entries[i].key.String() < entries[j].key.String()
|
||||||
|
default:
|
||||||
|
panic("invalid kind")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
for _, entry := range entries {
|
||||||
|
w.writeName(fd)
|
||||||
|
w.WriteByte('<')
|
||||||
|
if !w.compact {
|
||||||
|
w.WriteByte('\n')
|
||||||
|
}
|
||||||
|
w.indent++
|
||||||
|
w.writeName(kfd)
|
||||||
|
if err := w.writeSingularValue(entry.key, kfd); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
w.WriteByte('\n')
|
||||||
|
w.writeName(vfd)
|
||||||
|
if err := w.writeSingularValue(entry.val, vfd); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
w.WriteByte('\n')
|
||||||
|
w.indent--
|
||||||
|
w.WriteByte('>')
|
||||||
|
w.WriteByte('\n')
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
w.writeName(fd)
|
||||||
|
if err := w.writeSingularValue(m.Get(fd), fd); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
w.WriteByte('\n')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if b := m.GetUnknown(); len(b) > 0 {
|
||||||
|
w.writeUnknownFields(b)
|
||||||
|
}
|
||||||
|
return w.writeExtensions(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *textWriter) writeSingularValue(v protoreflect.Value, fd protoreflect.FieldDescriptor) error {
|
||||||
|
switch fd.Kind() {
|
||||||
|
case protoreflect.FloatKind, protoreflect.DoubleKind:
|
||||||
|
switch vf := v.Float(); {
|
||||||
|
case math.IsInf(vf, +1):
|
||||||
|
w.Write(posInf)
|
||||||
|
case math.IsInf(vf, -1):
|
||||||
|
w.Write(negInf)
|
||||||
|
case math.IsNaN(vf):
|
||||||
|
w.Write(nan)
|
||||||
|
default:
|
||||||
|
fmt.Fprint(w, v.Interface())
|
||||||
|
}
|
||||||
|
case protoreflect.StringKind:
|
||||||
|
// NOTE: This does not validate UTF-8 for historical reasons.
|
||||||
|
w.writeQuotedString(string(v.String()))
|
||||||
|
case protoreflect.BytesKind:
|
||||||
|
w.writeQuotedString(string(v.Bytes()))
|
||||||
|
case protoreflect.MessageKind, protoreflect.GroupKind:
|
||||||
|
var bra, ket byte = '<', '>'
|
||||||
|
if fd.Kind() == protoreflect.GroupKind {
|
||||||
|
bra, ket = '{', '}'
|
||||||
|
}
|
||||||
|
w.WriteByte(bra)
|
||||||
|
if !w.compact {
|
||||||
|
w.WriteByte('\n')
|
||||||
|
}
|
||||||
|
w.indent++
|
||||||
|
m := v.Message()
|
||||||
|
if m2, ok := m.Interface().(encoding.TextMarshaler); ok {
|
||||||
|
b, err := m2.MarshalText()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
w.Write(b)
|
||||||
|
} else {
|
||||||
|
w.writeMessage(m)
|
||||||
|
}
|
||||||
|
w.indent--
|
||||||
|
w.WriteByte(ket)
|
||||||
|
case protoreflect.EnumKind:
|
||||||
|
if ev := fd.Enum().Values().ByNumber(v.Enum()); ev != nil {
|
||||||
|
fmt.Fprint(w, ev.Name())
|
||||||
|
} else {
|
||||||
|
fmt.Fprint(w, v.Enum())
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
fmt.Fprint(w, v.Interface())
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// writeQuotedString writes a quoted string in the protocol buffer text format.
|
||||||
|
func (w *textWriter) writeQuotedString(s string) {
|
||||||
|
w.WriteByte('"')
|
||||||
|
for i := 0; i < len(s); i++ {
|
||||||
|
switch c := s[i]; c {
|
||||||
|
case '\n':
|
||||||
|
w.buf = append(w.buf, `\n`...)
|
||||||
|
case '\r':
|
||||||
|
w.buf = append(w.buf, `\r`...)
|
||||||
|
case '\t':
|
||||||
|
w.buf = append(w.buf, `\t`...)
|
||||||
|
case '"':
|
||||||
|
w.buf = append(w.buf, `\"`...)
|
||||||
|
case '\\':
|
||||||
|
w.buf = append(w.buf, `\\`...)
|
||||||
|
default:
|
||||||
|
if isPrint := c >= 0x20 && c < 0x7f; isPrint {
|
||||||
|
w.buf = append(w.buf, c)
|
||||||
|
} else {
|
||||||
|
w.buf = append(w.buf, fmt.Sprintf(`\%03o`, c)...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
w.WriteByte('"')
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *textWriter) writeUnknownFields(b []byte) {
|
||||||
|
if !w.compact {
|
||||||
|
fmt.Fprintf(w, "/* %d unknown bytes */\n", len(b))
|
||||||
|
}
|
||||||
|
|
||||||
|
for len(b) > 0 {
|
||||||
|
num, wtyp, n := protowire.ConsumeTag(b)
|
||||||
|
if n < 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
b = b[n:]
|
||||||
|
|
||||||
|
if wtyp == protowire.EndGroupType {
|
||||||
|
w.indent--
|
||||||
|
w.Write(endBraceNewline)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fmt.Fprint(w, num)
|
||||||
|
if wtyp != protowire.StartGroupType {
|
||||||
|
w.WriteByte(':')
|
||||||
|
}
|
||||||
|
if !w.compact || wtyp == protowire.StartGroupType {
|
||||||
|
w.WriteByte(' ')
|
||||||
|
}
|
||||||
|
switch wtyp {
|
||||||
|
case protowire.VarintType:
|
||||||
|
v, n := protowire.ConsumeVarint(b)
|
||||||
|
if n < 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
b = b[n:]
|
||||||
|
fmt.Fprint(w, v)
|
||||||
|
case protowire.Fixed32Type:
|
||||||
|
v, n := protowire.ConsumeFixed32(b)
|
||||||
|
if n < 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
b = b[n:]
|
||||||
|
fmt.Fprint(w, v)
|
||||||
|
case protowire.Fixed64Type:
|
||||||
|
v, n := protowire.ConsumeFixed64(b)
|
||||||
|
if n < 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
b = b[n:]
|
||||||
|
fmt.Fprint(w, v)
|
||||||
|
case protowire.BytesType:
|
||||||
|
v, n := protowire.ConsumeBytes(b)
|
||||||
|
if n < 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
b = b[n:]
|
||||||
|
fmt.Fprintf(w, "%q", v)
|
||||||
|
case protowire.StartGroupType:
|
||||||
|
w.WriteByte('{')
|
||||||
|
w.indent++
|
||||||
|
default:
|
||||||
|
fmt.Fprintf(w, "/* unknown wire type %d */", wtyp)
|
||||||
|
}
|
||||||
|
w.WriteByte('\n')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// writeExtensions writes all the extensions in m.
|
||||||
|
func (w *textWriter) writeExtensions(m protoreflect.Message) error {
|
||||||
|
md := m.Descriptor()
|
||||||
|
if md.ExtensionRanges().Len() == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type ext struct {
|
||||||
|
desc protoreflect.FieldDescriptor
|
||||||
|
val protoreflect.Value
|
||||||
|
}
|
||||||
|
var exts []ext
|
||||||
|
m.Range(func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool {
|
||||||
|
if fd.IsExtension() {
|
||||||
|
exts = append(exts, ext{fd, v})
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
sort.Slice(exts, func(i, j int) bool {
|
||||||
|
return exts[i].desc.Number() < exts[j].desc.Number()
|
||||||
|
})
|
||||||
|
|
||||||
|
for _, ext := range exts {
|
||||||
|
// For message set, use the name of the message as the extension name.
|
||||||
|
name := string(ext.desc.FullName())
|
||||||
|
if isMessageSet(ext.desc.ContainingMessage()) {
|
||||||
|
name = strings.TrimSuffix(name, ".message_set_extension")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !ext.desc.IsList() {
|
||||||
|
if err := w.writeSingularExtension(name, ext.val, ext.desc); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
lv := ext.val.List()
|
||||||
|
for i := 0; i < lv.Len(); i++ {
|
||||||
|
if err := w.writeSingularExtension(name, lv.Get(i), ext.desc); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *textWriter) writeSingularExtension(name string, v protoreflect.Value, fd protoreflect.FieldDescriptor) error {
|
||||||
|
fmt.Fprintf(w, "[%s]:", name)
|
||||||
|
if !w.compact {
|
||||||
|
w.WriteByte(' ')
|
||||||
|
}
|
||||||
|
if err := w.writeSingularValue(v, fd); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
w.WriteByte('\n')
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *textWriter) writeIndent() {
|
||||||
|
if !w.complete {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for i := 0; i < w.indent*2; i++ {
|
||||||
|
w.buf = append(w.buf, ' ')
|
||||||
|
}
|
||||||
|
w.complete = false
|
||||||
|
}
|
|
@ -1,880 +0,0 @@
|
||||||
// Go support for Protocol Buffers - Google's data interchange format
|
|
||||||
//
|
|
||||||
// Copyright 2010 The Go Authors. All rights reserved.
|
|
||||||
// https://github.com/golang/protobuf
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without
|
|
||||||
// modification, are permitted provided that the following conditions are
|
|
||||||
// met:
|
|
||||||
//
|
|
||||||
// * Redistributions of source code must retain the above copyright
|
|
||||||
// notice, this list of conditions and the following disclaimer.
|
|
||||||
// * Redistributions in binary form must reproduce the above
|
|
||||||
// copyright notice, this list of conditions and the following disclaimer
|
|
||||||
// in the documentation and/or other materials provided with the
|
|
||||||
// distribution.
|
|
||||||
// * Neither the name of Google Inc. nor the names of its
|
|
||||||
// contributors may be used to endorse or promote products derived from
|
|
||||||
// this software without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
package proto
|
|
||||||
|
|
||||||
// Functions for parsing the Text protocol buffer format.
|
|
||||||
// TODO: message sets.
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"reflect"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"unicode/utf8"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Error string emitted when deserializing Any and fields are already set
|
|
||||||
const anyRepeatedlyUnpacked = "Any message unpacked multiple times, or %q already set"
|
|
||||||
|
|
||||||
type ParseError struct {
|
|
||||||
Message string
|
|
||||||
Line int // 1-based line number
|
|
||||||
Offset int // 0-based byte offset from start of input
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *ParseError) Error() string {
|
|
||||||
if p.Line == 1 {
|
|
||||||
// show offset only for first line
|
|
||||||
return fmt.Sprintf("line 1.%d: %v", p.Offset, p.Message)
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("line %d: %v", p.Line, p.Message)
|
|
||||||
}
|
|
||||||
|
|
||||||
type token struct {
|
|
||||||
value string
|
|
||||||
err *ParseError
|
|
||||||
line int // line number
|
|
||||||
offset int // byte number from start of input, not start of line
|
|
||||||
unquoted string // the unquoted version of value, if it was a quoted string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *token) String() string {
|
|
||||||
if t.err == nil {
|
|
||||||
return fmt.Sprintf("%q (line=%d, offset=%d)", t.value, t.line, t.offset)
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("parse error: %v", t.err)
|
|
||||||
}
|
|
||||||
|
|
||||||
type textParser struct {
|
|
||||||
s string // remaining input
|
|
||||||
done bool // whether the parsing is finished (success or error)
|
|
||||||
backed bool // whether back() was called
|
|
||||||
offset, line int
|
|
||||||
cur token
|
|
||||||
}
|
|
||||||
|
|
||||||
func newTextParser(s string) *textParser {
|
|
||||||
p := new(textParser)
|
|
||||||
p.s = s
|
|
||||||
p.line = 1
|
|
||||||
p.cur.line = 1
|
|
||||||
return p
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *textParser) errorf(format string, a ...interface{}) *ParseError {
|
|
||||||
pe := &ParseError{fmt.Sprintf(format, a...), p.cur.line, p.cur.offset}
|
|
||||||
p.cur.err = pe
|
|
||||||
p.done = true
|
|
||||||
return pe
|
|
||||||
}
|
|
||||||
|
|
||||||
// Numbers and identifiers are matched by [-+._A-Za-z0-9]
|
|
||||||
func isIdentOrNumberChar(c byte) bool {
|
|
||||||
switch {
|
|
||||||
case 'A' <= c && c <= 'Z', 'a' <= c && c <= 'z':
|
|
||||||
return true
|
|
||||||
case '0' <= c && c <= '9':
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
switch c {
|
|
||||||
case '-', '+', '.', '_':
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func isWhitespace(c byte) bool {
|
|
||||||
switch c {
|
|
||||||
case ' ', '\t', '\n', '\r':
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func isQuote(c byte) bool {
|
|
||||||
switch c {
|
|
||||||
case '"', '\'':
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *textParser) skipWhitespace() {
|
|
||||||
i := 0
|
|
||||||
for i < len(p.s) && (isWhitespace(p.s[i]) || p.s[i] == '#') {
|
|
||||||
if p.s[i] == '#' {
|
|
||||||
// comment; skip to end of line or input
|
|
||||||
for i < len(p.s) && p.s[i] != '\n' {
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
if i == len(p.s) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if p.s[i] == '\n' {
|
|
||||||
p.line++
|
|
||||||
}
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
p.offset += i
|
|
||||||
p.s = p.s[i:len(p.s)]
|
|
||||||
if len(p.s) == 0 {
|
|
||||||
p.done = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *textParser) advance() {
|
|
||||||
// Skip whitespace
|
|
||||||
p.skipWhitespace()
|
|
||||||
if p.done {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start of non-whitespace
|
|
||||||
p.cur.err = nil
|
|
||||||
p.cur.offset, p.cur.line = p.offset, p.line
|
|
||||||
p.cur.unquoted = ""
|
|
||||||
switch p.s[0] {
|
|
||||||
case '<', '>', '{', '}', ':', '[', ']', ';', ',', '/':
|
|
||||||
// Single symbol
|
|
||||||
p.cur.value, p.s = p.s[0:1], p.s[1:len(p.s)]
|
|
||||||
case '"', '\'':
|
|
||||||
// Quoted string
|
|
||||||
i := 1
|
|
||||||
for i < len(p.s) && p.s[i] != p.s[0] && p.s[i] != '\n' {
|
|
||||||
if p.s[i] == '\\' && i+1 < len(p.s) {
|
|
||||||
// skip escaped char
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
if i >= len(p.s) || p.s[i] != p.s[0] {
|
|
||||||
p.errorf("unmatched quote")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
unq, err := unquoteC(p.s[1:i], rune(p.s[0]))
|
|
||||||
if err != nil {
|
|
||||||
p.errorf("invalid quoted string %s: %v", p.s[0:i+1], err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
p.cur.value, p.s = p.s[0:i+1], p.s[i+1:len(p.s)]
|
|
||||||
p.cur.unquoted = unq
|
|
||||||
default:
|
|
||||||
i := 0
|
|
||||||
for i < len(p.s) && isIdentOrNumberChar(p.s[i]) {
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
if i == 0 {
|
|
||||||
p.errorf("unexpected byte %#x", p.s[0])
|
|
||||||
return
|
|
||||||
}
|
|
||||||
p.cur.value, p.s = p.s[0:i], p.s[i:len(p.s)]
|
|
||||||
}
|
|
||||||
p.offset += len(p.cur.value)
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
errBadUTF8 = errors.New("proto: bad UTF-8")
|
|
||||||
)
|
|
||||||
|
|
||||||
func unquoteC(s string, quote rune) (string, error) {
|
|
||||||
// This is based on C++'s tokenizer.cc.
|
|
||||||
// Despite its name, this is *not* parsing C syntax.
|
|
||||||
// For instance, "\0" is an invalid quoted string.
|
|
||||||
|
|
||||||
// Avoid allocation in trivial cases.
|
|
||||||
simple := true
|
|
||||||
for _, r := range s {
|
|
||||||
if r == '\\' || r == quote {
|
|
||||||
simple = false
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if simple {
|
|
||||||
return s, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
buf := make([]byte, 0, 3*len(s)/2)
|
|
||||||
for len(s) > 0 {
|
|
||||||
r, n := utf8.DecodeRuneInString(s)
|
|
||||||
if r == utf8.RuneError && n == 1 {
|
|
||||||
return "", errBadUTF8
|
|
||||||
}
|
|
||||||
s = s[n:]
|
|
||||||
if r != '\\' {
|
|
||||||
if r < utf8.RuneSelf {
|
|
||||||
buf = append(buf, byte(r))
|
|
||||||
} else {
|
|
||||||
buf = append(buf, string(r)...)
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
ch, tail, err := unescape(s)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
buf = append(buf, ch...)
|
|
||||||
s = tail
|
|
||||||
}
|
|
||||||
return string(buf), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func unescape(s string) (ch string, tail string, err error) {
|
|
||||||
r, n := utf8.DecodeRuneInString(s)
|
|
||||||
if r == utf8.RuneError && n == 1 {
|
|
||||||
return "", "", errBadUTF8
|
|
||||||
}
|
|
||||||
s = s[n:]
|
|
||||||
switch r {
|
|
||||||
case 'a':
|
|
||||||
return "\a", s, nil
|
|
||||||
case 'b':
|
|
||||||
return "\b", s, nil
|
|
||||||
case 'f':
|
|
||||||
return "\f", s, nil
|
|
||||||
case 'n':
|
|
||||||
return "\n", s, nil
|
|
||||||
case 'r':
|
|
||||||
return "\r", s, nil
|
|
||||||
case 't':
|
|
||||||
return "\t", s, nil
|
|
||||||
case 'v':
|
|
||||||
return "\v", s, nil
|
|
||||||
case '?':
|
|
||||||
return "?", s, nil // trigraph workaround
|
|
||||||
case '\'', '"', '\\':
|
|
||||||
return string(r), s, nil
|
|
||||||
case '0', '1', '2', '3', '4', '5', '6', '7':
|
|
||||||
if len(s) < 2 {
|
|
||||||
return "", "", fmt.Errorf(`\%c requires 2 following digits`, r)
|
|
||||||
}
|
|
||||||
ss := string(r) + s[:2]
|
|
||||||
s = s[2:]
|
|
||||||
i, err := strconv.ParseUint(ss, 8, 8)
|
|
||||||
if err != nil {
|
|
||||||
return "", "", fmt.Errorf(`\%s contains non-octal digits`, ss)
|
|
||||||
}
|
|
||||||
return string([]byte{byte(i)}), s, nil
|
|
||||||
case 'x', 'X', 'u', 'U':
|
|
||||||
var n int
|
|
||||||
switch r {
|
|
||||||
case 'x', 'X':
|
|
||||||
n = 2
|
|
||||||
case 'u':
|
|
||||||
n = 4
|
|
||||||
case 'U':
|
|
||||||
n = 8
|
|
||||||
}
|
|
||||||
if len(s) < n {
|
|
||||||
return "", "", fmt.Errorf(`\%c requires %d following digits`, r, n)
|
|
||||||
}
|
|
||||||
ss := s[:n]
|
|
||||||
s = s[n:]
|
|
||||||
i, err := strconv.ParseUint(ss, 16, 64)
|
|
||||||
if err != nil {
|
|
||||||
return "", "", fmt.Errorf(`\%c%s contains non-hexadecimal digits`, r, ss)
|
|
||||||
}
|
|
||||||
if r == 'x' || r == 'X' {
|
|
||||||
return string([]byte{byte(i)}), s, nil
|
|
||||||
}
|
|
||||||
if i > utf8.MaxRune {
|
|
||||||
return "", "", fmt.Errorf(`\%c%s is not a valid Unicode code point`, r, ss)
|
|
||||||
}
|
|
||||||
return string(i), s, nil
|
|
||||||
}
|
|
||||||
return "", "", fmt.Errorf(`unknown escape \%c`, r)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Back off the parser by one token. Can only be done between calls to next().
|
|
||||||
// It makes the next advance() a no-op.
|
|
||||||
func (p *textParser) back() { p.backed = true }
|
|
||||||
|
|
||||||
// Advances the parser and returns the new current token.
|
|
||||||
func (p *textParser) next() *token {
|
|
||||||
if p.backed || p.done {
|
|
||||||
p.backed = false
|
|
||||||
return &p.cur
|
|
||||||
}
|
|
||||||
p.advance()
|
|
||||||
if p.done {
|
|
||||||
p.cur.value = ""
|
|
||||||
} else if len(p.cur.value) > 0 && isQuote(p.cur.value[0]) {
|
|
||||||
// Look for multiple quoted strings separated by whitespace,
|
|
||||||
// and concatenate them.
|
|
||||||
cat := p.cur
|
|
||||||
for {
|
|
||||||
p.skipWhitespace()
|
|
||||||
if p.done || !isQuote(p.s[0]) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
p.advance()
|
|
||||||
if p.cur.err != nil {
|
|
||||||
return &p.cur
|
|
||||||
}
|
|
||||||
cat.value += " " + p.cur.value
|
|
||||||
cat.unquoted += p.cur.unquoted
|
|
||||||
}
|
|
||||||
p.done = false // parser may have seen EOF, but we want to return cat
|
|
||||||
p.cur = cat
|
|
||||||
}
|
|
||||||
return &p.cur
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *textParser) consumeToken(s string) error {
|
|
||||||
tok := p.next()
|
|
||||||
if tok.err != nil {
|
|
||||||
return tok.err
|
|
||||||
}
|
|
||||||
if tok.value != s {
|
|
||||||
p.back()
|
|
||||||
return p.errorf("expected %q, found %q", s, tok.value)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return a RequiredNotSetError indicating which required field was not set.
|
|
||||||
func (p *textParser) missingRequiredFieldError(sv reflect.Value) *RequiredNotSetError {
|
|
||||||
st := sv.Type()
|
|
||||||
sprops := GetProperties(st)
|
|
||||||
for i := 0; i < st.NumField(); i++ {
|
|
||||||
if !isNil(sv.Field(i)) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
props := sprops.Prop[i]
|
|
||||||
if props.Required {
|
|
||||||
return &RequiredNotSetError{fmt.Sprintf("%v.%v", st, props.OrigName)}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return &RequiredNotSetError{fmt.Sprintf("%v.<unknown field name>", st)} // should not happen
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns the index in the struct for the named field, as well as the parsed tag properties.
|
|
||||||
func structFieldByName(sprops *StructProperties, name string) (int, *Properties, bool) {
|
|
||||||
i, ok := sprops.decoderOrigNames[name]
|
|
||||||
if ok {
|
|
||||||
return i, sprops.Prop[i], true
|
|
||||||
}
|
|
||||||
return -1, nil, false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Consume a ':' from the input stream (if the next token is a colon),
|
|
||||||
// returning an error if a colon is needed but not present.
|
|
||||||
func (p *textParser) checkForColon(props *Properties, typ reflect.Type) *ParseError {
|
|
||||||
tok := p.next()
|
|
||||||
if tok.err != nil {
|
|
||||||
return tok.err
|
|
||||||
}
|
|
||||||
if tok.value != ":" {
|
|
||||||
// Colon is optional when the field is a group or message.
|
|
||||||
needColon := true
|
|
||||||
switch props.Wire {
|
|
||||||
case "group":
|
|
||||||
needColon = false
|
|
||||||
case "bytes":
|
|
||||||
// A "bytes" field is either a message, a string, or a repeated field;
|
|
||||||
// those three become *T, *string and []T respectively, so we can check for
|
|
||||||
// this field being a pointer to a non-string.
|
|
||||||
if typ.Kind() == reflect.Ptr {
|
|
||||||
// *T or *string
|
|
||||||
if typ.Elem().Kind() == reflect.String {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
} else if typ.Kind() == reflect.Slice {
|
|
||||||
// []T or []*T
|
|
||||||
if typ.Elem().Kind() != reflect.Ptr {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
} else if typ.Kind() == reflect.String {
|
|
||||||
// The proto3 exception is for a string field,
|
|
||||||
// which requires a colon.
|
|
||||||
break
|
|
||||||
}
|
|
||||||
needColon = false
|
|
||||||
}
|
|
||||||
if needColon {
|
|
||||||
return p.errorf("expected ':', found %q", tok.value)
|
|
||||||
}
|
|
||||||
p.back()
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *textParser) readStruct(sv reflect.Value, terminator string) error {
|
|
||||||
st := sv.Type()
|
|
||||||
sprops := GetProperties(st)
|
|
||||||
reqCount := sprops.reqCount
|
|
||||||
var reqFieldErr error
|
|
||||||
fieldSet := make(map[string]bool)
|
|
||||||
// A struct is a sequence of "name: value", terminated by one of
|
|
||||||
// '>' or '}', or the end of the input. A name may also be
|
|
||||||
// "[extension]" or "[type/url]".
|
|
||||||
//
|
|
||||||
// The whole struct can also be an expanded Any message, like:
|
|
||||||
// [type/url] < ... struct contents ... >
|
|
||||||
for {
|
|
||||||
tok := p.next()
|
|
||||||
if tok.err != nil {
|
|
||||||
return tok.err
|
|
||||||
}
|
|
||||||
if tok.value == terminator {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if tok.value == "[" {
|
|
||||||
// Looks like an extension or an Any.
|
|
||||||
//
|
|
||||||
// TODO: Check whether we need to handle
|
|
||||||
// namespace rooted names (e.g. ".something.Foo").
|
|
||||||
extName, err := p.consumeExtName()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if s := strings.LastIndex(extName, "/"); s >= 0 {
|
|
||||||
// If it contains a slash, it's an Any type URL.
|
|
||||||
messageName := extName[s+1:]
|
|
||||||
mt := MessageType(messageName)
|
|
||||||
if mt == nil {
|
|
||||||
return p.errorf("unrecognized message %q in google.protobuf.Any", messageName)
|
|
||||||
}
|
|
||||||
tok = p.next()
|
|
||||||
if tok.err != nil {
|
|
||||||
return tok.err
|
|
||||||
}
|
|
||||||
// consume an optional colon
|
|
||||||
if tok.value == ":" {
|
|
||||||
tok = p.next()
|
|
||||||
if tok.err != nil {
|
|
||||||
return tok.err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var terminator string
|
|
||||||
switch tok.value {
|
|
||||||
case "<":
|
|
||||||
terminator = ">"
|
|
||||||
case "{":
|
|
||||||
terminator = "}"
|
|
||||||
default:
|
|
||||||
return p.errorf("expected '{' or '<', found %q", tok.value)
|
|
||||||
}
|
|
||||||
v := reflect.New(mt.Elem())
|
|
||||||
if pe := p.readStruct(v.Elem(), terminator); pe != nil {
|
|
||||||
return pe
|
|
||||||
}
|
|
||||||
b, err := Marshal(v.Interface().(Message))
|
|
||||||
if err != nil {
|
|
||||||
return p.errorf("failed to marshal message of type %q: %v", messageName, err)
|
|
||||||
}
|
|
||||||
if fieldSet["type_url"] {
|
|
||||||
return p.errorf(anyRepeatedlyUnpacked, "type_url")
|
|
||||||
}
|
|
||||||
if fieldSet["value"] {
|
|
||||||
return p.errorf(anyRepeatedlyUnpacked, "value")
|
|
||||||
}
|
|
||||||
sv.FieldByName("TypeUrl").SetString(extName)
|
|
||||||
sv.FieldByName("Value").SetBytes(b)
|
|
||||||
fieldSet["type_url"] = true
|
|
||||||
fieldSet["value"] = true
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
var desc *ExtensionDesc
|
|
||||||
// This could be faster, but it's functional.
|
|
||||||
// TODO: Do something smarter than a linear scan.
|
|
||||||
for _, d := range RegisteredExtensions(reflect.New(st).Interface().(Message)) {
|
|
||||||
if d.Name == extName {
|
|
||||||
desc = d
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if desc == nil {
|
|
||||||
return p.errorf("unrecognized extension %q", extName)
|
|
||||||
}
|
|
||||||
|
|
||||||
props := &Properties{}
|
|
||||||
props.Parse(desc.Tag)
|
|
||||||
|
|
||||||
typ := reflect.TypeOf(desc.ExtensionType)
|
|
||||||
if err := p.checkForColon(props, typ); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
rep := desc.repeated()
|
|
||||||
|
|
||||||
// Read the extension structure, and set it in
|
|
||||||
// the value we're constructing.
|
|
||||||
var ext reflect.Value
|
|
||||||
if !rep {
|
|
||||||
ext = reflect.New(typ).Elem()
|
|
||||||
} else {
|
|
||||||
ext = reflect.New(typ.Elem()).Elem()
|
|
||||||
}
|
|
||||||
if err := p.readAny(ext, props); err != nil {
|
|
||||||
if _, ok := err.(*RequiredNotSetError); !ok {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
reqFieldErr = err
|
|
||||||
}
|
|
||||||
ep := sv.Addr().Interface().(Message)
|
|
||||||
if !rep {
|
|
||||||
SetExtension(ep, desc, ext.Interface())
|
|
||||||
} else {
|
|
||||||
old, err := GetExtension(ep, desc)
|
|
||||||
var sl reflect.Value
|
|
||||||
if err == nil {
|
|
||||||
sl = reflect.ValueOf(old) // existing slice
|
|
||||||
} else {
|
|
||||||
sl = reflect.MakeSlice(typ, 0, 1)
|
|
||||||
}
|
|
||||||
sl = reflect.Append(sl, ext)
|
|
||||||
SetExtension(ep, desc, sl.Interface())
|
|
||||||
}
|
|
||||||
if err := p.consumeOptionalSeparator(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is a normal, non-extension field.
|
|
||||||
name := tok.value
|
|
||||||
var dst reflect.Value
|
|
||||||
fi, props, ok := structFieldByName(sprops, name)
|
|
||||||
if ok {
|
|
||||||
dst = sv.Field(fi)
|
|
||||||
} else if oop, ok := sprops.OneofTypes[name]; ok {
|
|
||||||
// It is a oneof.
|
|
||||||
props = oop.Prop
|
|
||||||
nv := reflect.New(oop.Type.Elem())
|
|
||||||
dst = nv.Elem().Field(0)
|
|
||||||
field := sv.Field(oop.Field)
|
|
||||||
if !field.IsNil() {
|
|
||||||
return p.errorf("field '%s' would overwrite already parsed oneof '%s'", name, sv.Type().Field(oop.Field).Name)
|
|
||||||
}
|
|
||||||
field.Set(nv)
|
|
||||||
}
|
|
||||||
if !dst.IsValid() {
|
|
||||||
return p.errorf("unknown field name %q in %v", name, st)
|
|
||||||
}
|
|
||||||
|
|
||||||
if dst.Kind() == reflect.Map {
|
|
||||||
// Consume any colon.
|
|
||||||
if err := p.checkForColon(props, dst.Type()); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Construct the map if it doesn't already exist.
|
|
||||||
if dst.IsNil() {
|
|
||||||
dst.Set(reflect.MakeMap(dst.Type()))
|
|
||||||
}
|
|
||||||
key := reflect.New(dst.Type().Key()).Elem()
|
|
||||||
val := reflect.New(dst.Type().Elem()).Elem()
|
|
||||||
|
|
||||||
// The map entry should be this sequence of tokens:
|
|
||||||
// < key : KEY value : VALUE >
|
|
||||||
// However, implementations may omit key or value, and technically
|
|
||||||
// we should support them in any order. See b/28924776 for a time
|
|
||||||
// this went wrong.
|
|
||||||
|
|
||||||
tok := p.next()
|
|
||||||
var terminator string
|
|
||||||
switch tok.value {
|
|
||||||
case "<":
|
|
||||||
terminator = ">"
|
|
||||||
case "{":
|
|
||||||
terminator = "}"
|
|
||||||
default:
|
|
||||||
return p.errorf("expected '{' or '<', found %q", tok.value)
|
|
||||||
}
|
|
||||||
for {
|
|
||||||
tok := p.next()
|
|
||||||
if tok.err != nil {
|
|
||||||
return tok.err
|
|
||||||
}
|
|
||||||
if tok.value == terminator {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
switch tok.value {
|
|
||||||
case "key":
|
|
||||||
if err := p.consumeToken(":"); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := p.readAny(key, props.MapKeyProp); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := p.consumeOptionalSeparator(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
case "value":
|
|
||||||
if err := p.checkForColon(props.MapValProp, dst.Type().Elem()); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := p.readAny(val, props.MapValProp); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := p.consumeOptionalSeparator(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
p.back()
|
|
||||||
return p.errorf(`expected "key", "value", or %q, found %q`, terminator, tok.value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dst.SetMapIndex(key, val)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check that it's not already set if it's not a repeated field.
|
|
||||||
if !props.Repeated && fieldSet[name] {
|
|
||||||
return p.errorf("non-repeated field %q was repeated", name)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := p.checkForColon(props, dst.Type()); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse into the field.
|
|
||||||
fieldSet[name] = true
|
|
||||||
if err := p.readAny(dst, props); err != nil {
|
|
||||||
if _, ok := err.(*RequiredNotSetError); !ok {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
reqFieldErr = err
|
|
||||||
}
|
|
||||||
if props.Required {
|
|
||||||
reqCount--
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := p.consumeOptionalSeparator(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if reqCount > 0 {
|
|
||||||
return p.missingRequiredFieldError(sv)
|
|
||||||
}
|
|
||||||
return reqFieldErr
|
|
||||||
}
|
|
||||||
|
|
||||||
// consumeExtName consumes extension name or expanded Any type URL and the
|
|
||||||
// following ']'. It returns the name or URL consumed.
|
|
||||||
func (p *textParser) consumeExtName() (string, error) {
|
|
||||||
tok := p.next()
|
|
||||||
if tok.err != nil {
|
|
||||||
return "", tok.err
|
|
||||||
}
|
|
||||||
|
|
||||||
// If extension name or type url is quoted, it's a single token.
|
|
||||||
if len(tok.value) > 2 && isQuote(tok.value[0]) && tok.value[len(tok.value)-1] == tok.value[0] {
|
|
||||||
name, err := unquoteC(tok.value[1:len(tok.value)-1], rune(tok.value[0]))
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return name, p.consumeToken("]")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Consume everything up to "]"
|
|
||||||
var parts []string
|
|
||||||
for tok.value != "]" {
|
|
||||||
parts = append(parts, tok.value)
|
|
||||||
tok = p.next()
|
|
||||||
if tok.err != nil {
|
|
||||||
return "", p.errorf("unrecognized type_url or extension name: %s", tok.err)
|
|
||||||
}
|
|
||||||
if p.done && tok.value != "]" {
|
|
||||||
return "", p.errorf("unclosed type_url or extension name")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return strings.Join(parts, ""), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// consumeOptionalSeparator consumes an optional semicolon or comma.
|
|
||||||
// It is used in readStruct to provide backward compatibility.
|
|
||||||
func (p *textParser) consumeOptionalSeparator() error {
|
|
||||||
tok := p.next()
|
|
||||||
if tok.err != nil {
|
|
||||||
return tok.err
|
|
||||||
}
|
|
||||||
if tok.value != ";" && tok.value != "," {
|
|
||||||
p.back()
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *textParser) readAny(v reflect.Value, props *Properties) error {
|
|
||||||
tok := p.next()
|
|
||||||
if tok.err != nil {
|
|
||||||
return tok.err
|
|
||||||
}
|
|
||||||
if tok.value == "" {
|
|
||||||
return p.errorf("unexpected EOF")
|
|
||||||
}
|
|
||||||
|
|
||||||
switch fv := v; fv.Kind() {
|
|
||||||
case reflect.Slice:
|
|
||||||
at := v.Type()
|
|
||||||
if at.Elem().Kind() == reflect.Uint8 {
|
|
||||||
// Special case for []byte
|
|
||||||
if tok.value[0] != '"' && tok.value[0] != '\'' {
|
|
||||||
// Deliberately written out here, as the error after
|
|
||||||
// this switch statement would write "invalid []byte: ...",
|
|
||||||
// which is not as user-friendly.
|
|
||||||
return p.errorf("invalid string: %v", tok.value)
|
|
||||||
}
|
|
||||||
bytes := []byte(tok.unquoted)
|
|
||||||
fv.Set(reflect.ValueOf(bytes))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
// Repeated field.
|
|
||||||
if tok.value == "[" {
|
|
||||||
// Repeated field with list notation, like [1,2,3].
|
|
||||||
for {
|
|
||||||
fv.Set(reflect.Append(fv, reflect.New(at.Elem()).Elem()))
|
|
||||||
err := p.readAny(fv.Index(fv.Len()-1), props)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
tok := p.next()
|
|
||||||
if tok.err != nil {
|
|
||||||
return tok.err
|
|
||||||
}
|
|
||||||
if tok.value == "]" {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if tok.value != "," {
|
|
||||||
return p.errorf("Expected ']' or ',' found %q", tok.value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
// One value of the repeated field.
|
|
||||||
p.back()
|
|
||||||
fv.Set(reflect.Append(fv, reflect.New(at.Elem()).Elem()))
|
|
||||||
return p.readAny(fv.Index(fv.Len()-1), props)
|
|
||||||
case reflect.Bool:
|
|
||||||
// true/1/t/True or false/f/0/False.
|
|
||||||
switch tok.value {
|
|
||||||
case "true", "1", "t", "True":
|
|
||||||
fv.SetBool(true)
|
|
||||||
return nil
|
|
||||||
case "false", "0", "f", "False":
|
|
||||||
fv.SetBool(false)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
case reflect.Float32, reflect.Float64:
|
|
||||||
v := tok.value
|
|
||||||
// Ignore 'f' for compatibility with output generated by C++, but don't
|
|
||||||
// remove 'f' when the value is "-inf" or "inf".
|
|
||||||
if strings.HasSuffix(v, "f") && tok.value != "-inf" && tok.value != "inf" {
|
|
||||||
v = v[:len(v)-1]
|
|
||||||
}
|
|
||||||
if f, err := strconv.ParseFloat(v, fv.Type().Bits()); err == nil {
|
|
||||||
fv.SetFloat(f)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
case reflect.Int32:
|
|
||||||
if x, err := strconv.ParseInt(tok.value, 0, 32); err == nil {
|
|
||||||
fv.SetInt(x)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(props.Enum) == 0 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
m, ok := enumValueMaps[props.Enum]
|
|
||||||
if !ok {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
x, ok := m[tok.value]
|
|
||||||
if !ok {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
fv.SetInt(int64(x))
|
|
||||||
return nil
|
|
||||||
case reflect.Int64:
|
|
||||||
if x, err := strconv.ParseInt(tok.value, 0, 64); err == nil {
|
|
||||||
fv.SetInt(x)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
case reflect.Ptr:
|
|
||||||
// A basic field (indirected through pointer), or a repeated message/group
|
|
||||||
p.back()
|
|
||||||
fv.Set(reflect.New(fv.Type().Elem()))
|
|
||||||
return p.readAny(fv.Elem(), props)
|
|
||||||
case reflect.String:
|
|
||||||
if tok.value[0] == '"' || tok.value[0] == '\'' {
|
|
||||||
fv.SetString(tok.unquoted)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
case reflect.Struct:
|
|
||||||
var terminator string
|
|
||||||
switch tok.value {
|
|
||||||
case "{":
|
|
||||||
terminator = "}"
|
|
||||||
case "<":
|
|
||||||
terminator = ">"
|
|
||||||
default:
|
|
||||||
return p.errorf("expected '{' or '<', found %q", tok.value)
|
|
||||||
}
|
|
||||||
// TODO: Handle nested messages which implement encoding.TextUnmarshaler.
|
|
||||||
return p.readStruct(fv, terminator)
|
|
||||||
case reflect.Uint32:
|
|
||||||
if x, err := strconv.ParseUint(tok.value, 0, 32); err == nil {
|
|
||||||
fv.SetUint(uint64(x))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
case reflect.Uint64:
|
|
||||||
if x, err := strconv.ParseUint(tok.value, 0, 64); err == nil {
|
|
||||||
fv.SetUint(x)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return p.errorf("invalid %v: %v", v.Type(), tok.value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalText reads a protocol buffer in Text format. UnmarshalText resets pb
|
|
||||||
// before starting to unmarshal, so any existing data in pb is always removed.
|
|
||||||
// If a required field is not set and no other error occurs,
|
|
||||||
// UnmarshalText returns *RequiredNotSetError.
|
|
||||||
func UnmarshalText(s string, pb Message) error {
|
|
||||||
if um, ok := pb.(encoding.TextUnmarshaler); ok {
|
|
||||||
return um.UnmarshalText([]byte(s))
|
|
||||||
}
|
|
||||||
pb.Reset()
|
|
||||||
v := reflect.ValueOf(pb)
|
|
||||||
return newTextParser(s).readStruct(v.Elem(), "")
|
|
||||||
}
|
|
|
@ -0,0 +1,78 @@
|
||||||
|
// Copyright 2019 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package proto
|
||||||
|
|
||||||
|
import (
|
||||||
|
protoV2 "google.golang.org/protobuf/proto"
|
||||||
|
"google.golang.org/protobuf/runtime/protoiface"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Size returns the size in bytes of the wire-format encoding of m.
|
||||||
|
func Size(m Message) int {
|
||||||
|
if m == nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
mi := MessageV2(m)
|
||||||
|
return protoV2.Size(mi)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Marshal returns the wire-format encoding of m.
|
||||||
|
func Marshal(m Message) ([]byte, error) {
|
||||||
|
b, err := marshalAppend(nil, m, false)
|
||||||
|
if b == nil {
|
||||||
|
b = zeroBytes
|
||||||
|
}
|
||||||
|
return b, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var zeroBytes = make([]byte, 0, 0)
|
||||||
|
|
||||||
|
func marshalAppend(buf []byte, m Message, deterministic bool) ([]byte, error) {
|
||||||
|
if m == nil {
|
||||||
|
return nil, ErrNil
|
||||||
|
}
|
||||||
|
mi := MessageV2(m)
|
||||||
|
nbuf, err := protoV2.MarshalOptions{
|
||||||
|
Deterministic: deterministic,
|
||||||
|
AllowPartial: true,
|
||||||
|
}.MarshalAppend(buf, mi)
|
||||||
|
if err != nil {
|
||||||
|
return buf, err
|
||||||
|
}
|
||||||
|
if len(buf) == len(nbuf) {
|
||||||
|
if !mi.ProtoReflect().IsValid() {
|
||||||
|
return buf, ErrNil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nbuf, checkRequiredNotSet(mi)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal parses a wire-format message in b and places the decoded results in m.
|
||||||
|
//
|
||||||
|
// Unmarshal resets m before starting to unmarshal, so any existing data in m is always
|
||||||
|
// removed. Use UnmarshalMerge to preserve and append to existing data.
|
||||||
|
func Unmarshal(b []byte, m Message) error {
|
||||||
|
m.Reset()
|
||||||
|
return UnmarshalMerge(b, m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalMerge parses a wire-format message in b and places the decoded results in m.
|
||||||
|
func UnmarshalMerge(b []byte, m Message) error {
|
||||||
|
mi := MessageV2(m)
|
||||||
|
out, err := protoV2.UnmarshalOptions{
|
||||||
|
AllowPartial: true,
|
||||||
|
Merge: true,
|
||||||
|
}.UnmarshalState(protoiface.UnmarshalInput{
|
||||||
|
Buf: b,
|
||||||
|
Message: mi.ProtoReflect(),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if out.Flags&protoiface.UnmarshalInitialized > 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return checkRequiredNotSet(mi)
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
// Copyright 2019 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package proto
|
||||||
|
|
||||||
|
// Bool stores v in a new bool value and returns a pointer to it.
|
||||||
|
func Bool(v bool) *bool { return &v }
|
||||||
|
|
||||||
|
// Int stores v in a new int32 value and returns a pointer to it.
|
||||||
|
//
|
||||||
|
// Deprecated: Use Int32 instead.
|
||||||
|
func Int(v int) *int32 { return Int32(int32(v)) }
|
||||||
|
|
||||||
|
// Int32 stores v in a new int32 value and returns a pointer to it.
|
||||||
|
func Int32(v int32) *int32 { return &v }
|
||||||
|
|
||||||
|
// Int64 stores v in a new int64 value and returns a pointer to it.
|
||||||
|
func Int64(v int64) *int64 { return &v }
|
||||||
|
|
||||||
|
// Uint32 stores v in a new uint32 value and returns a pointer to it.
|
||||||
|
func Uint32(v uint32) *uint32 { return &v }
|
||||||
|
|
||||||
|
// Uint64 stores v in a new uint64 value and returns a pointer to it.
|
||||||
|
func Uint64(v uint64) *uint64 { return &v }
|
||||||
|
|
||||||
|
// Float32 stores v in a new float32 value and returns a pointer to it.
|
||||||
|
func Float32(v float32) *float32 { return &v }
|
||||||
|
|
||||||
|
// Float64 stores v in a new float64 value and returns a pointer to it.
|
||||||
|
func Float64(v float64) *float64 { return &v }
|
||||||
|
|
||||||
|
// String stores v in a new string value and returns a pointer to it.
|
||||||
|
func String(v string) *string { return &v }
|
|
@ -0,0 +1,363 @@
|
||||||
|
Mozilla Public License, version 2.0
|
||||||
|
|
||||||
|
1. Definitions
|
||||||
|
|
||||||
|
1.1. "Contributor"
|
||||||
|
|
||||||
|
means each individual or legal entity that creates, contributes to the
|
||||||
|
creation of, or owns Covered Software.
|
||||||
|
|
||||||
|
1.2. "Contributor Version"
|
||||||
|
|
||||||
|
means the combination of the Contributions of others (if any) used by a
|
||||||
|
Contributor and that particular Contributor's Contribution.
|
||||||
|
|
||||||
|
1.3. "Contribution"
|
||||||
|
|
||||||
|
means Covered Software of a particular Contributor.
|
||||||
|
|
||||||
|
1.4. "Covered Software"
|
||||||
|
|
||||||
|
means Source Code Form to which the initial Contributor has attached the
|
||||||
|
notice in Exhibit A, the Executable Form of such Source Code Form, and
|
||||||
|
Modifications of such Source Code Form, in each case including portions
|
||||||
|
thereof.
|
||||||
|
|
||||||
|
1.5. "Incompatible With Secondary Licenses"
|
||||||
|
means
|
||||||
|
|
||||||
|
a. that the initial Contributor has attached the notice described in
|
||||||
|
Exhibit B to the Covered Software; or
|
||||||
|
|
||||||
|
b. that the Covered Software was made available under the terms of
|
||||||
|
version 1.1 or earlier of the License, but not also under the terms of
|
||||||
|
a Secondary License.
|
||||||
|
|
||||||
|
1.6. "Executable Form"
|
||||||
|
|
||||||
|
means any form of the work other than Source Code Form.
|
||||||
|
|
||||||
|
1.7. "Larger Work"
|
||||||
|
|
||||||
|
means a work that combines Covered Software with other material, in a
|
||||||
|
separate file or files, that is not Covered Software.
|
||||||
|
|
||||||
|
1.8. "License"
|
||||||
|
|
||||||
|
means this document.
|
||||||
|
|
||||||
|
1.9. "Licensable"
|
||||||
|
|
||||||
|
means having the right to grant, to the maximum extent possible, whether
|
||||||
|
at the time of the initial grant or subsequently, any and all of the
|
||||||
|
rights conveyed by this License.
|
||||||
|
|
||||||
|
1.10. "Modifications"
|
||||||
|
|
||||||
|
means any of the following:
|
||||||
|
|
||||||
|
a. any file in Source Code Form that results from an addition to,
|
||||||
|
deletion from, or modification of the contents of Covered Software; or
|
||||||
|
|
||||||
|
b. any new file in Source Code Form that contains any Covered Software.
|
||||||
|
|
||||||
|
1.11. "Patent Claims" of a Contributor
|
||||||
|
|
||||||
|
means any patent claim(s), including without limitation, method,
|
||||||
|
process, and apparatus claims, in any patent Licensable by such
|
||||||
|
Contributor that would be infringed, but for the grant of the License,
|
||||||
|
by the making, using, selling, offering for sale, having made, import,
|
||||||
|
or transfer of either its Contributions or its Contributor Version.
|
||||||
|
|
||||||
|
1.12. "Secondary License"
|
||||||
|
|
||||||
|
means either the GNU General Public License, Version 2.0, the GNU Lesser
|
||||||
|
General Public License, Version 2.1, the GNU Affero General Public
|
||||||
|
License, Version 3.0, or any later versions of those licenses.
|
||||||
|
|
||||||
|
1.13. "Source Code Form"
|
||||||
|
|
||||||
|
means the form of the work preferred for making modifications.
|
||||||
|
|
||||||
|
1.14. "You" (or "Your")
|
||||||
|
|
||||||
|
means an individual or a legal entity exercising rights under this
|
||||||
|
License. For legal entities, "You" includes any entity that controls, is
|
||||||
|
controlled by, or is under common control with You. For purposes of this
|
||||||
|
definition, "control" means (a) the power, direct or indirect, to cause
|
||||||
|
the direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (b) ownership of more than fifty percent (50%) of the
|
||||||
|
outstanding shares or beneficial ownership of such entity.
|
||||||
|
|
||||||
|
|
||||||
|
2. License Grants and Conditions
|
||||||
|
|
||||||
|
2.1. Grants
|
||||||
|
|
||||||
|
Each Contributor hereby grants You a world-wide, royalty-free,
|
||||||
|
non-exclusive license:
|
||||||
|
|
||||||
|
a. under intellectual property rights (other than patent or trademark)
|
||||||
|
Licensable by such Contributor to use, reproduce, make available,
|
||||||
|
modify, display, perform, distribute, and otherwise exploit its
|
||||||
|
Contributions, either on an unmodified basis, with Modifications, or
|
||||||
|
as part of a Larger Work; and
|
||||||
|
|
||||||
|
b. under Patent Claims of such Contributor to make, use, sell, offer for
|
||||||
|
sale, have made, import, and otherwise transfer either its
|
||||||
|
Contributions or its Contributor Version.
|
||||||
|
|
||||||
|
2.2. Effective Date
|
||||||
|
|
||||||
|
The licenses granted in Section 2.1 with respect to any Contribution
|
||||||
|
become effective for each Contribution on the date the Contributor first
|
||||||
|
distributes such Contribution.
|
||||||
|
|
||||||
|
2.3. Limitations on Grant Scope
|
||||||
|
|
||||||
|
The licenses granted in this Section 2 are the only rights granted under
|
||||||
|
this License. No additional rights or licenses will be implied from the
|
||||||
|
distribution or licensing of Covered Software under this License.
|
||||||
|
Notwithstanding Section 2.1(b) above, no patent license is granted by a
|
||||||
|
Contributor:
|
||||||
|
|
||||||
|
a. for any code that a Contributor has removed from Covered Software; or
|
||||||
|
|
||||||
|
b. for infringements caused by: (i) Your and any other third party's
|
||||||
|
modifications of Covered Software, or (ii) the combination of its
|
||||||
|
Contributions with other software (except as part of its Contributor
|
||||||
|
Version); or
|
||||||
|
|
||||||
|
c. under Patent Claims infringed by Covered Software in the absence of
|
||||||
|
its Contributions.
|
||||||
|
|
||||||
|
This License does not grant any rights in the trademarks, service marks,
|
||||||
|
or logos of any Contributor (except as may be necessary to comply with
|
||||||
|
the notice requirements in Section 3.4).
|
||||||
|
|
||||||
|
2.4. Subsequent Licenses
|
||||||
|
|
||||||
|
No Contributor makes additional grants as a result of Your choice to
|
||||||
|
distribute the Covered Software under a subsequent version of this
|
||||||
|
License (see Section 10.2) or under the terms of a Secondary License (if
|
||||||
|
permitted under the terms of Section 3.3).
|
||||||
|
|
||||||
|
2.5. Representation
|
||||||
|
|
||||||
|
Each Contributor represents that the Contributor believes its
|
||||||
|
Contributions are its original creation(s) or it has sufficient rights to
|
||||||
|
grant the rights to its Contributions conveyed by this License.
|
||||||
|
|
||||||
|
2.6. Fair Use
|
||||||
|
|
||||||
|
This License is not intended to limit any rights You have under
|
||||||
|
applicable copyright doctrines of fair use, fair dealing, or other
|
||||||
|
equivalents.
|
||||||
|
|
||||||
|
2.7. Conditions
|
||||||
|
|
||||||
|
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in
|
||||||
|
Section 2.1.
|
||||||
|
|
||||||
|
|
||||||
|
3. Responsibilities
|
||||||
|
|
||||||
|
3.1. Distribution of Source Form
|
||||||
|
|
||||||
|
All distribution of Covered Software in Source Code Form, including any
|
||||||
|
Modifications that You create or to which You contribute, must be under
|
||||||
|
the terms of this License. You must inform recipients that the Source
|
||||||
|
Code Form of the Covered Software is governed by the terms of this
|
||||||
|
License, and how they can obtain a copy of this License. You may not
|
||||||
|
attempt to alter or restrict the recipients' rights in the Source Code
|
||||||
|
Form.
|
||||||
|
|
||||||
|
3.2. Distribution of Executable Form
|
||||||
|
|
||||||
|
If You distribute Covered Software in Executable Form then:
|
||||||
|
|
||||||
|
a. such Covered Software must also be made available in Source Code Form,
|
||||||
|
as described in Section 3.1, and You must inform recipients of the
|
||||||
|
Executable Form how they can obtain a copy of such Source Code Form by
|
||||||
|
reasonable means in a timely manner, at a charge no more than the cost
|
||||||
|
of distribution to the recipient; and
|
||||||
|
|
||||||
|
b. You may distribute such Executable Form under the terms of this
|
||||||
|
License, or sublicense it under different terms, provided that the
|
||||||
|
license for the Executable Form does not attempt to limit or alter the
|
||||||
|
recipients' rights in the Source Code Form under this License.
|
||||||
|
|
||||||
|
3.3. Distribution of a Larger Work
|
||||||
|
|
||||||
|
You may create and distribute a Larger Work under terms of Your choice,
|
||||||
|
provided that You also comply with the requirements of this License for
|
||||||
|
the Covered Software. If the Larger Work is a combination of Covered
|
||||||
|
Software with a work governed by one or more Secondary Licenses, and the
|
||||||
|
Covered Software is not Incompatible With Secondary Licenses, this
|
||||||
|
License permits You to additionally distribute such Covered Software
|
||||||
|
under the terms of such Secondary License(s), so that the recipient of
|
||||||
|
the Larger Work may, at their option, further distribute the Covered
|
||||||
|
Software under the terms of either this License or such Secondary
|
||||||
|
License(s).
|
||||||
|
|
||||||
|
3.4. Notices
|
||||||
|
|
||||||
|
You may not remove or alter the substance of any license notices
|
||||||
|
(including copyright notices, patent notices, disclaimers of warranty, or
|
||||||
|
limitations of liability) contained within the Source Code Form of the
|
||||||
|
Covered Software, except that You may alter any license notices to the
|
||||||
|
extent required to remedy known factual inaccuracies.
|
||||||
|
|
||||||
|
3.5. Application of Additional Terms
|
||||||
|
|
||||||
|
You may choose to offer, and to charge a fee for, warranty, support,
|
||||||
|
indemnity or liability obligations to one or more recipients of Covered
|
||||||
|
Software. However, You may do so only on Your own behalf, and not on
|
||||||
|
behalf of any Contributor. You must make it absolutely clear that any
|
||||||
|
such warranty, support, indemnity, or liability obligation is offered by
|
||||||
|
You alone, and You hereby agree to indemnify every Contributor for any
|
||||||
|
liability incurred by such Contributor as a result of warranty, support,
|
||||||
|
indemnity or liability terms You offer. You may include additional
|
||||||
|
disclaimers of warranty and limitations of liability specific to any
|
||||||
|
jurisdiction.
|
||||||
|
|
||||||
|
4. Inability to Comply Due to Statute or Regulation
|
||||||
|
|
||||||
|
If it is impossible for You to comply with any of the terms of this License
|
||||||
|
with respect to some or all of the Covered Software due to statute,
|
||||||
|
judicial order, or regulation then You must: (a) comply with the terms of
|
||||||
|
this License to the maximum extent possible; and (b) describe the
|
||||||
|
limitations and the code they affect. Such description must be placed in a
|
||||||
|
text file included with all distributions of the Covered Software under
|
||||||
|
this License. Except to the extent prohibited by statute or regulation,
|
||||||
|
such description must be sufficiently detailed for a recipient of ordinary
|
||||||
|
skill to be able to understand it.
|
||||||
|
|
||||||
|
5. Termination
|
||||||
|
|
||||||
|
5.1. The rights granted under this License will terminate automatically if You
|
||||||
|
fail to comply with any of its terms. However, if You become compliant,
|
||||||
|
then the rights granted under this License from a particular Contributor
|
||||||
|
are reinstated (a) provisionally, unless and until such Contributor
|
||||||
|
explicitly and finally terminates Your grants, and (b) on an ongoing
|
||||||
|
basis, if such Contributor fails to notify You of the non-compliance by
|
||||||
|
some reasonable means prior to 60 days after You have come back into
|
||||||
|
compliance. Moreover, Your grants from a particular Contributor are
|
||||||
|
reinstated on an ongoing basis if such Contributor notifies You of the
|
||||||
|
non-compliance by some reasonable means, this is the first time You have
|
||||||
|
received notice of non-compliance with this License from such
|
||||||
|
Contributor, and You become compliant prior to 30 days after Your receipt
|
||||||
|
of the notice.
|
||||||
|
|
||||||
|
5.2. If You initiate litigation against any entity by asserting a patent
|
||||||
|
infringement claim (excluding declaratory judgment actions,
|
||||||
|
counter-claims, and cross-claims) alleging that a Contributor Version
|
||||||
|
directly or indirectly infringes any patent, then the rights granted to
|
||||||
|
You by any and all Contributors for the Covered Software under Section
|
||||||
|
2.1 of this License shall terminate.
|
||||||
|
|
||||||
|
5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user
|
||||||
|
license agreements (excluding distributors and resellers) which have been
|
||||||
|
validly granted by You or Your distributors under this License prior to
|
||||||
|
termination shall survive termination.
|
||||||
|
|
||||||
|
6. Disclaimer of Warranty
|
||||||
|
|
||||||
|
Covered Software is provided under this License on an "as is" basis,
|
||||||
|
without warranty of any kind, either expressed, implied, or statutory,
|
||||||
|
including, without limitation, warranties that the Covered Software is free
|
||||||
|
of defects, merchantable, fit for a particular purpose or non-infringing.
|
||||||
|
The entire risk as to the quality and performance of the Covered Software
|
||||||
|
is with You. Should any Covered Software prove defective in any respect,
|
||||||
|
You (not any Contributor) assume the cost of any necessary servicing,
|
||||||
|
repair, or correction. This disclaimer of warranty constitutes an essential
|
||||||
|
part of this License. No use of any Covered Software is authorized under
|
||||||
|
this License except under this disclaimer.
|
||||||
|
|
||||||
|
7. Limitation of Liability
|
||||||
|
|
||||||
|
Under no circumstances and under no legal theory, whether tort (including
|
||||||
|
negligence), contract, or otherwise, shall any Contributor, or anyone who
|
||||||
|
distributes Covered Software as permitted above, be liable to You for any
|
||||||
|
direct, indirect, special, incidental, or consequential damages of any
|
||||||
|
character including, without limitation, damages for lost profits, loss of
|
||||||
|
goodwill, work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses, even if such party shall have been
|
||||||
|
informed of the possibility of such damages. This limitation of liability
|
||||||
|
shall not apply to liability for death or personal injury resulting from
|
||||||
|
such party's negligence to the extent applicable law prohibits such
|
||||||
|
limitation. Some jurisdictions do not allow the exclusion or limitation of
|
||||||
|
incidental or consequential damages, so this exclusion and limitation may
|
||||||
|
not apply to You.
|
||||||
|
|
||||||
|
8. Litigation
|
||||||
|
|
||||||
|
Any litigation relating to this License may be brought only in the courts
|
||||||
|
of a jurisdiction where the defendant maintains its principal place of
|
||||||
|
business and such litigation shall be governed by laws of that
|
||||||
|
jurisdiction, without reference to its conflict-of-law provisions. Nothing
|
||||||
|
in this Section shall prevent a party's ability to bring cross-claims or
|
||||||
|
counter-claims.
|
||||||
|
|
||||||
|
9. Miscellaneous
|
||||||
|
|
||||||
|
This License represents the complete agreement concerning the subject
|
||||||
|
matter hereof. If any provision of this License is held to be
|
||||||
|
unenforceable, such provision shall be reformed only to the extent
|
||||||
|
necessary to make it enforceable. Any law or regulation which provides that
|
||||||
|
the language of a contract shall be construed against the drafter shall not
|
||||||
|
be used to construe this License against a Contributor.
|
||||||
|
|
||||||
|
|
||||||
|
10. Versions of the License
|
||||||
|
|
||||||
|
10.1. New Versions
|
||||||
|
|
||||||
|
Mozilla Foundation is the license steward. Except as provided in Section
|
||||||
|
10.3, no one other than the license steward has the right to modify or
|
||||||
|
publish new versions of this License. Each version will be given a
|
||||||
|
distinguishing version number.
|
||||||
|
|
||||||
|
10.2. Effect of New Versions
|
||||||
|
|
||||||
|
You may distribute the Covered Software under the terms of the version
|
||||||
|
of the License under which You originally received the Covered Software,
|
||||||
|
or under the terms of any subsequent version published by the license
|
||||||
|
steward.
|
||||||
|
|
||||||
|
10.3. Modified Versions
|
||||||
|
|
||||||
|
If you create software not governed by this License, and you want to
|
||||||
|
create a new license for such software, you may create and use a
|
||||||
|
modified version of this License if you rename the license and remove
|
||||||
|
any references to the name of the license steward (except to note that
|
||||||
|
such modified license differs from this License).
|
||||||
|
|
||||||
|
10.4. Distributing Source Code Form that is Incompatible With Secondary
|
||||||
|
Licenses If You choose to distribute Source Code Form that is
|
||||||
|
Incompatible With Secondary Licenses under the terms of this version of
|
||||||
|
the License, the notice described in Exhibit B of this License must be
|
||||||
|
attached.
|
||||||
|
|
||||||
|
Exhibit A - Source Code Form License Notice
|
||||||
|
|
||||||
|
This Source Code Form is subject to the
|
||||||
|
terms of the Mozilla Public License, v.
|
||||||
|
2.0. If a copy of the MPL was not
|
||||||
|
distributed with this file, You can
|
||||||
|
obtain one at
|
||||||
|
http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
If it is not possible or desirable to put the notice in a particular file,
|
||||||
|
then You may include the notice in a location (such as a LICENSE file in a
|
||||||
|
relevant directory) where a recipient would be likely to look for such a
|
||||||
|
notice.
|
||||||
|
|
||||||
|
You may add additional accurate notices of copyright ownership.
|
||||||
|
|
||||||
|
Exhibit B - "Incompatible With Secondary Licenses" Notice
|
||||||
|
|
||||||
|
This Source Code Form is "Incompatible
|
||||||
|
With Secondary Licenses", as defined by
|
||||||
|
the Mozilla Public License, v. 2.0.
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
# cleanhttp
|
||||||
|
|
||||||
|
Functions for accessing "clean" Go http.Client values
|
||||||
|
|
||||||
|
-------------
|
||||||
|
|
||||||
|
The Go standard library contains a default `http.Client` called
|
||||||
|
`http.DefaultClient`. It is a common idiom in Go code to start with
|
||||||
|
`http.DefaultClient` and tweak it as necessary, and in fact, this is
|
||||||
|
encouraged; from the `http` package documentation:
|
||||||
|
|
||||||
|
> The Client's Transport typically has internal state (cached TCP connections),
|
||||||
|
so Clients should be reused instead of created as needed. Clients are safe for
|
||||||
|
concurrent use by multiple goroutines.
|
||||||
|
|
||||||
|
Unfortunately, this is a shared value, and it is not uncommon for libraries to
|
||||||
|
assume that they are free to modify it at will. With enough dependencies, it
|
||||||
|
can be very easy to encounter strange problems and race conditions due to
|
||||||
|
manipulation of this shared value across libraries and goroutines (clients are
|
||||||
|
safe for concurrent use, but writing values to the client struct itself is not
|
||||||
|
protected).
|
||||||
|
|
||||||
|
Making things worse is the fact that a bare `http.Client` will use a default
|
||||||
|
`http.Transport` called `http.DefaultTransport`, which is another global value
|
||||||
|
that behaves the same way. So it is not simply enough to replace
|
||||||
|
`http.DefaultClient` with `&http.Client{}`.
|
||||||
|
|
||||||
|
This repository provides some simple functions to get a "clean" `http.Client`
|
||||||
|
-- one that uses the same default values as the Go standard library, but
|
||||||
|
returns a client that does not share any state with other clients.
|
|
@ -0,0 +1,57 @@
|
||||||
|
package cleanhttp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"runtime"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DefaultTransport returns a new http.Transport with similar default values to
|
||||||
|
// http.DefaultTransport, but with idle connections and keepalives disabled.
|
||||||
|
func DefaultTransport() *http.Transport {
|
||||||
|
transport := DefaultPooledTransport()
|
||||||
|
transport.DisableKeepAlives = true
|
||||||
|
transport.MaxIdleConnsPerHost = -1
|
||||||
|
return transport
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultPooledTransport returns a new http.Transport with similar default
|
||||||
|
// values to http.DefaultTransport. Do not use this for transient transports as
|
||||||
|
// it can leak file descriptors over time. Only use this for transports that
|
||||||
|
// will be re-used for the same host(s).
|
||||||
|
func DefaultPooledTransport() *http.Transport {
|
||||||
|
transport := &http.Transport{
|
||||||
|
Proxy: http.ProxyFromEnvironment,
|
||||||
|
DialContext: (&net.Dialer{
|
||||||
|
Timeout: 30 * time.Second,
|
||||||
|
KeepAlive: 30 * time.Second,
|
||||||
|
DualStack: true,
|
||||||
|
}).DialContext,
|
||||||
|
MaxIdleConns: 100,
|
||||||
|
IdleConnTimeout: 90 * time.Second,
|
||||||
|
TLSHandshakeTimeout: 10 * time.Second,
|
||||||
|
ExpectContinueTimeout: 1 * time.Second,
|
||||||
|
MaxIdleConnsPerHost: runtime.GOMAXPROCS(0) + 1,
|
||||||
|
}
|
||||||
|
return transport
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultClient returns a new http.Client with similar default values to
|
||||||
|
// http.Client, but with a non-shared Transport, idle connections disabled, and
|
||||||
|
// keepalives disabled.
|
||||||
|
func DefaultClient() *http.Client {
|
||||||
|
return &http.Client{
|
||||||
|
Transport: DefaultTransport(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultPooledClient returns a new http.Client with similar default values to
|
||||||
|
// http.Client, but with a shared Transport. Do not use this function for
|
||||||
|
// transient clients as it can leak file descriptors over time. Only use this
|
||||||
|
// for clients that will be re-used for the same host(s).
|
||||||
|
func DefaultPooledClient() *http.Client {
|
||||||
|
return &http.Client{
|
||||||
|
Transport: DefaultPooledTransport(),
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
// Package cleanhttp offers convenience utilities for acquiring "clean"
|
||||||
|
// http.Transport and http.Client structs.
|
||||||
|
//
|
||||||
|
// Values set on http.DefaultClient and http.DefaultTransport affect all
|
||||||
|
// callers. This can have detrimental effects, esepcially in TLS contexts,
|
||||||
|
// where client or root certificates set to talk to multiple endpoints can end
|
||||||
|
// up displacing each other, leading to hard-to-debug issues. This package
|
||||||
|
// provides non-shared http.Client and http.Transport structs to ensure that
|
||||||
|
// the configuration will not be overwritten by other parts of the application
|
||||||
|
// or dependencies.
|
||||||
|
//
|
||||||
|
// The DefaultClient and DefaultTransport functions disable idle connections
|
||||||
|
// and keepalives. Without ensuring that idle connections are closed before
|
||||||
|
// garbage collection, short-term clients/transports can leak file descriptors,
|
||||||
|
// eventually leading to "too many open files" errors. If you will be
|
||||||
|
// connecting to the same hosts repeatedly from the same client, you can use
|
||||||
|
// DefaultPooledClient to receive a client that has connection pooling
|
||||||
|
// semantics similar to http.DefaultClient.
|
||||||
|
//
|
||||||
|
package cleanhttp
|
|
@ -0,0 +1 @@
|
||||||
|
module github.com/hashicorp/go-cleanhttp
|
|
@ -0,0 +1,48 @@
|
||||||
|
package cleanhttp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
"unicode"
|
||||||
|
)
|
||||||
|
|
||||||
|
// HandlerInput provides input options to cleanhttp's handlers
|
||||||
|
type HandlerInput struct {
|
||||||
|
ErrStatus int
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrintablePathCheckHandler is a middleware that ensures the request path
|
||||||
|
// contains only printable runes.
|
||||||
|
func PrintablePathCheckHandler(next http.Handler, input *HandlerInput) http.Handler {
|
||||||
|
// Nil-check on input to make it optional
|
||||||
|
if input == nil {
|
||||||
|
input = &HandlerInput{
|
||||||
|
ErrStatus: http.StatusBadRequest,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default to http.StatusBadRequest on error
|
||||||
|
if input.ErrStatus == 0 {
|
||||||
|
input.ErrStatus = http.StatusBadRequest
|
||||||
|
}
|
||||||
|
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if r != nil {
|
||||||
|
// Check URL path for non-printable characters
|
||||||
|
idx := strings.IndexFunc(r.URL.Path, func(c rune) bool {
|
||||||
|
return !unicode.IsPrint(c)
|
||||||
|
})
|
||||||
|
|
||||||
|
if idx != -1 {
|
||||||
|
w.WriteHeader(input.ErrStatus)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if next != nil {
|
||||||
|
next.ServeHTTP(w, r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
})
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
.idea/
|
||||||
|
*.iml
|
||||||
|
*.test
|
||||||
|
.vscode/
|
|
@ -0,0 +1,12 @@
|
||||||
|
sudo: false
|
||||||
|
|
||||||
|
language: go
|
||||||
|
|
||||||
|
go:
|
||||||
|
- 1.12.4
|
||||||
|
|
||||||
|
branches:
|
||||||
|
only:
|
||||||
|
- master
|
||||||
|
|
||||||
|
script: make updatedeps test
|
|
@ -0,0 +1,363 @@
|
||||||
|
Mozilla Public License, version 2.0
|
||||||
|
|
||||||
|
1. Definitions
|
||||||
|
|
||||||
|
1.1. "Contributor"
|
||||||
|
|
||||||
|
means each individual or legal entity that creates, contributes to the
|
||||||
|
creation of, or owns Covered Software.
|
||||||
|
|
||||||
|
1.2. "Contributor Version"
|
||||||
|
|
||||||
|
means the combination of the Contributions of others (if any) used by a
|
||||||
|
Contributor and that particular Contributor's Contribution.
|
||||||
|
|
||||||
|
1.3. "Contribution"
|
||||||
|
|
||||||
|
means Covered Software of a particular Contributor.
|
||||||
|
|
||||||
|
1.4. "Covered Software"
|
||||||
|
|
||||||
|
means Source Code Form to which the initial Contributor has attached the
|
||||||
|
notice in Exhibit A, the Executable Form of such Source Code Form, and
|
||||||
|
Modifications of such Source Code Form, in each case including portions
|
||||||
|
thereof.
|
||||||
|
|
||||||
|
1.5. "Incompatible With Secondary Licenses"
|
||||||
|
means
|
||||||
|
|
||||||
|
a. that the initial Contributor has attached the notice described in
|
||||||
|
Exhibit B to the Covered Software; or
|
||||||
|
|
||||||
|
b. that the Covered Software was made available under the terms of
|
||||||
|
version 1.1 or earlier of the License, but not also under the terms of
|
||||||
|
a Secondary License.
|
||||||
|
|
||||||
|
1.6. "Executable Form"
|
||||||
|
|
||||||
|
means any form of the work other than Source Code Form.
|
||||||
|
|
||||||
|
1.7. "Larger Work"
|
||||||
|
|
||||||
|
means a work that combines Covered Software with other material, in a
|
||||||
|
separate file or files, that is not Covered Software.
|
||||||
|
|
||||||
|
1.8. "License"
|
||||||
|
|
||||||
|
means this document.
|
||||||
|
|
||||||
|
1.9. "Licensable"
|
||||||
|
|
||||||
|
means having the right to grant, to the maximum extent possible, whether
|
||||||
|
at the time of the initial grant or subsequently, any and all of the
|
||||||
|
rights conveyed by this License.
|
||||||
|
|
||||||
|
1.10. "Modifications"
|
||||||
|
|
||||||
|
means any of the following:
|
||||||
|
|
||||||
|
a. any file in Source Code Form that results from an addition to,
|
||||||
|
deletion from, or modification of the contents of Covered Software; or
|
||||||
|
|
||||||
|
b. any new file in Source Code Form that contains any Covered Software.
|
||||||
|
|
||||||
|
1.11. "Patent Claims" of a Contributor
|
||||||
|
|
||||||
|
means any patent claim(s), including without limitation, method,
|
||||||
|
process, and apparatus claims, in any patent Licensable by such
|
||||||
|
Contributor that would be infringed, but for the grant of the License,
|
||||||
|
by the making, using, selling, offering for sale, having made, import,
|
||||||
|
or transfer of either its Contributions or its Contributor Version.
|
||||||
|
|
||||||
|
1.12. "Secondary License"
|
||||||
|
|
||||||
|
means either the GNU General Public License, Version 2.0, the GNU Lesser
|
||||||
|
General Public License, Version 2.1, the GNU Affero General Public
|
||||||
|
License, Version 3.0, or any later versions of those licenses.
|
||||||
|
|
||||||
|
1.13. "Source Code Form"
|
||||||
|
|
||||||
|
means the form of the work preferred for making modifications.
|
||||||
|
|
||||||
|
1.14. "You" (or "Your")
|
||||||
|
|
||||||
|
means an individual or a legal entity exercising rights under this
|
||||||
|
License. For legal entities, "You" includes any entity that controls, is
|
||||||
|
controlled by, or is under common control with You. For purposes of this
|
||||||
|
definition, "control" means (a) the power, direct or indirect, to cause
|
||||||
|
the direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (b) ownership of more than fifty percent (50%) of the
|
||||||
|
outstanding shares or beneficial ownership of such entity.
|
||||||
|
|
||||||
|
|
||||||
|
2. License Grants and Conditions
|
||||||
|
|
||||||
|
2.1. Grants
|
||||||
|
|
||||||
|
Each Contributor hereby grants You a world-wide, royalty-free,
|
||||||
|
non-exclusive license:
|
||||||
|
|
||||||
|
a. under intellectual property rights (other than patent or trademark)
|
||||||
|
Licensable by such Contributor to use, reproduce, make available,
|
||||||
|
modify, display, perform, distribute, and otherwise exploit its
|
||||||
|
Contributions, either on an unmodified basis, with Modifications, or
|
||||||
|
as part of a Larger Work; and
|
||||||
|
|
||||||
|
b. under Patent Claims of such Contributor to make, use, sell, offer for
|
||||||
|
sale, have made, import, and otherwise transfer either its
|
||||||
|
Contributions or its Contributor Version.
|
||||||
|
|
||||||
|
2.2. Effective Date
|
||||||
|
|
||||||
|
The licenses granted in Section 2.1 with respect to any Contribution
|
||||||
|
become effective for each Contribution on the date the Contributor first
|
||||||
|
distributes such Contribution.
|
||||||
|
|
||||||
|
2.3. Limitations on Grant Scope
|
||||||
|
|
||||||
|
The licenses granted in this Section 2 are the only rights granted under
|
||||||
|
this License. No additional rights or licenses will be implied from the
|
||||||
|
distribution or licensing of Covered Software under this License.
|
||||||
|
Notwithstanding Section 2.1(b) above, no patent license is granted by a
|
||||||
|
Contributor:
|
||||||
|
|
||||||
|
a. for any code that a Contributor has removed from Covered Software; or
|
||||||
|
|
||||||
|
b. for infringements caused by: (i) Your and any other third party's
|
||||||
|
modifications of Covered Software, or (ii) the combination of its
|
||||||
|
Contributions with other software (except as part of its Contributor
|
||||||
|
Version); or
|
||||||
|
|
||||||
|
c. under Patent Claims infringed by Covered Software in the absence of
|
||||||
|
its Contributions.
|
||||||
|
|
||||||
|
This License does not grant any rights in the trademarks, service marks,
|
||||||
|
or logos of any Contributor (except as may be necessary to comply with
|
||||||
|
the notice requirements in Section 3.4).
|
||||||
|
|
||||||
|
2.4. Subsequent Licenses
|
||||||
|
|
||||||
|
No Contributor makes additional grants as a result of Your choice to
|
||||||
|
distribute the Covered Software under a subsequent version of this
|
||||||
|
License (see Section 10.2) or under the terms of a Secondary License (if
|
||||||
|
permitted under the terms of Section 3.3).
|
||||||
|
|
||||||
|
2.5. Representation
|
||||||
|
|
||||||
|
Each Contributor represents that the Contributor believes its
|
||||||
|
Contributions are its original creation(s) or it has sufficient rights to
|
||||||
|
grant the rights to its Contributions conveyed by this License.
|
||||||
|
|
||||||
|
2.6. Fair Use
|
||||||
|
|
||||||
|
This License is not intended to limit any rights You have under
|
||||||
|
applicable copyright doctrines of fair use, fair dealing, or other
|
||||||
|
equivalents.
|
||||||
|
|
||||||
|
2.7. Conditions
|
||||||
|
|
||||||
|
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in
|
||||||
|
Section 2.1.
|
||||||
|
|
||||||
|
|
||||||
|
3. Responsibilities
|
||||||
|
|
||||||
|
3.1. Distribution of Source Form
|
||||||
|
|
||||||
|
All distribution of Covered Software in Source Code Form, including any
|
||||||
|
Modifications that You create or to which You contribute, must be under
|
||||||
|
the terms of this License. You must inform recipients that the Source
|
||||||
|
Code Form of the Covered Software is governed by the terms of this
|
||||||
|
License, and how they can obtain a copy of this License. You may not
|
||||||
|
attempt to alter or restrict the recipients' rights in the Source Code
|
||||||
|
Form.
|
||||||
|
|
||||||
|
3.2. Distribution of Executable Form
|
||||||
|
|
||||||
|
If You distribute Covered Software in Executable Form then:
|
||||||
|
|
||||||
|
a. such Covered Software must also be made available in Source Code Form,
|
||||||
|
as described in Section 3.1, and You must inform recipients of the
|
||||||
|
Executable Form how they can obtain a copy of such Source Code Form by
|
||||||
|
reasonable means in a timely manner, at a charge no more than the cost
|
||||||
|
of distribution to the recipient; and
|
||||||
|
|
||||||
|
b. You may distribute such Executable Form under the terms of this
|
||||||
|
License, or sublicense it under different terms, provided that the
|
||||||
|
license for the Executable Form does not attempt to limit or alter the
|
||||||
|
recipients' rights in the Source Code Form under this License.
|
||||||
|
|
||||||
|
3.3. Distribution of a Larger Work
|
||||||
|
|
||||||
|
You may create and distribute a Larger Work under terms of Your choice,
|
||||||
|
provided that You also comply with the requirements of this License for
|
||||||
|
the Covered Software. If the Larger Work is a combination of Covered
|
||||||
|
Software with a work governed by one or more Secondary Licenses, and the
|
||||||
|
Covered Software is not Incompatible With Secondary Licenses, this
|
||||||
|
License permits You to additionally distribute such Covered Software
|
||||||
|
under the terms of such Secondary License(s), so that the recipient of
|
||||||
|
the Larger Work may, at their option, further distribute the Covered
|
||||||
|
Software under the terms of either this License or such Secondary
|
||||||
|
License(s).
|
||||||
|
|
||||||
|
3.4. Notices
|
||||||
|
|
||||||
|
You may not remove or alter the substance of any license notices
|
||||||
|
(including copyright notices, patent notices, disclaimers of warranty, or
|
||||||
|
limitations of liability) contained within the Source Code Form of the
|
||||||
|
Covered Software, except that You may alter any license notices to the
|
||||||
|
extent required to remedy known factual inaccuracies.
|
||||||
|
|
||||||
|
3.5. Application of Additional Terms
|
||||||
|
|
||||||
|
You may choose to offer, and to charge a fee for, warranty, support,
|
||||||
|
indemnity or liability obligations to one or more recipients of Covered
|
||||||
|
Software. However, You may do so only on Your own behalf, and not on
|
||||||
|
behalf of any Contributor. You must make it absolutely clear that any
|
||||||
|
such warranty, support, indemnity, or liability obligation is offered by
|
||||||
|
You alone, and You hereby agree to indemnify every Contributor for any
|
||||||
|
liability incurred by such Contributor as a result of warranty, support,
|
||||||
|
indemnity or liability terms You offer. You may include additional
|
||||||
|
disclaimers of warranty and limitations of liability specific to any
|
||||||
|
jurisdiction.
|
||||||
|
|
||||||
|
4. Inability to Comply Due to Statute or Regulation
|
||||||
|
|
||||||
|
If it is impossible for You to comply with any of the terms of this License
|
||||||
|
with respect to some or all of the Covered Software due to statute,
|
||||||
|
judicial order, or regulation then You must: (a) comply with the terms of
|
||||||
|
this License to the maximum extent possible; and (b) describe the
|
||||||
|
limitations and the code they affect. Such description must be placed in a
|
||||||
|
text file included with all distributions of the Covered Software under
|
||||||
|
this License. Except to the extent prohibited by statute or regulation,
|
||||||
|
such description must be sufficiently detailed for a recipient of ordinary
|
||||||
|
skill to be able to understand it.
|
||||||
|
|
||||||
|
5. Termination
|
||||||
|
|
||||||
|
5.1. The rights granted under this License will terminate automatically if You
|
||||||
|
fail to comply with any of its terms. However, if You become compliant,
|
||||||
|
then the rights granted under this License from a particular Contributor
|
||||||
|
are reinstated (a) provisionally, unless and until such Contributor
|
||||||
|
explicitly and finally terminates Your grants, and (b) on an ongoing
|
||||||
|
basis, if such Contributor fails to notify You of the non-compliance by
|
||||||
|
some reasonable means prior to 60 days after You have come back into
|
||||||
|
compliance. Moreover, Your grants from a particular Contributor are
|
||||||
|
reinstated on an ongoing basis if such Contributor notifies You of the
|
||||||
|
non-compliance by some reasonable means, this is the first time You have
|
||||||
|
received notice of non-compliance with this License from such
|
||||||
|
Contributor, and You become compliant prior to 30 days after Your receipt
|
||||||
|
of the notice.
|
||||||
|
|
||||||
|
5.2. If You initiate litigation against any entity by asserting a patent
|
||||||
|
infringement claim (excluding declaratory judgment actions,
|
||||||
|
counter-claims, and cross-claims) alleging that a Contributor Version
|
||||||
|
directly or indirectly infringes any patent, then the rights granted to
|
||||||
|
You by any and all Contributors for the Covered Software under Section
|
||||||
|
2.1 of this License shall terminate.
|
||||||
|
|
||||||
|
5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user
|
||||||
|
license agreements (excluding distributors and resellers) which have been
|
||||||
|
validly granted by You or Your distributors under this License prior to
|
||||||
|
termination shall survive termination.
|
||||||
|
|
||||||
|
6. Disclaimer of Warranty
|
||||||
|
|
||||||
|
Covered Software is provided under this License on an "as is" basis,
|
||||||
|
without warranty of any kind, either expressed, implied, or statutory,
|
||||||
|
including, without limitation, warranties that the Covered Software is free
|
||||||
|
of defects, merchantable, fit for a particular purpose or non-infringing.
|
||||||
|
The entire risk as to the quality and performance of the Covered Software
|
||||||
|
is with You. Should any Covered Software prove defective in any respect,
|
||||||
|
You (not any Contributor) assume the cost of any necessary servicing,
|
||||||
|
repair, or correction. This disclaimer of warranty constitutes an essential
|
||||||
|
part of this License. No use of any Covered Software is authorized under
|
||||||
|
this License except under this disclaimer.
|
||||||
|
|
||||||
|
7. Limitation of Liability
|
||||||
|
|
||||||
|
Under no circumstances and under no legal theory, whether tort (including
|
||||||
|
negligence), contract, or otherwise, shall any Contributor, or anyone who
|
||||||
|
distributes Covered Software as permitted above, be liable to You for any
|
||||||
|
direct, indirect, special, incidental, or consequential damages of any
|
||||||
|
character including, without limitation, damages for lost profits, loss of
|
||||||
|
goodwill, work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses, even if such party shall have been
|
||||||
|
informed of the possibility of such damages. This limitation of liability
|
||||||
|
shall not apply to liability for death or personal injury resulting from
|
||||||
|
such party's negligence to the extent applicable law prohibits such
|
||||||
|
limitation. Some jurisdictions do not allow the exclusion or limitation of
|
||||||
|
incidental or consequential damages, so this exclusion and limitation may
|
||||||
|
not apply to You.
|
||||||
|
|
||||||
|
8. Litigation
|
||||||
|
|
||||||
|
Any litigation relating to this License may be brought only in the courts
|
||||||
|
of a jurisdiction where the defendant maintains its principal place of
|
||||||
|
business and such litigation shall be governed by laws of that
|
||||||
|
jurisdiction, without reference to its conflict-of-law provisions. Nothing
|
||||||
|
in this Section shall prevent a party's ability to bring cross-claims or
|
||||||
|
counter-claims.
|
||||||
|
|
||||||
|
9. Miscellaneous
|
||||||
|
|
||||||
|
This License represents the complete agreement concerning the subject
|
||||||
|
matter hereof. If any provision of this License is held to be
|
||||||
|
unenforceable, such provision shall be reformed only to the extent
|
||||||
|
necessary to make it enforceable. Any law or regulation which provides that
|
||||||
|
the language of a contract shall be construed against the drafter shall not
|
||||||
|
be used to construe this License against a Contributor.
|
||||||
|
|
||||||
|
|
||||||
|
10. Versions of the License
|
||||||
|
|
||||||
|
10.1. New Versions
|
||||||
|
|
||||||
|
Mozilla Foundation is the license steward. Except as provided in Section
|
||||||
|
10.3, no one other than the license steward has the right to modify or
|
||||||
|
publish new versions of this License. Each version will be given a
|
||||||
|
distinguishing version number.
|
||||||
|
|
||||||
|
10.2. Effect of New Versions
|
||||||
|
|
||||||
|
You may distribute the Covered Software under the terms of the version
|
||||||
|
of the License under which You originally received the Covered Software,
|
||||||
|
or under the terms of any subsequent version published by the license
|
||||||
|
steward.
|
||||||
|
|
||||||
|
10.3. Modified Versions
|
||||||
|
|
||||||
|
If you create software not governed by this License, and you want to
|
||||||
|
create a new license for such software, you may create and use a
|
||||||
|
modified version of this License if you rename the license and remove
|
||||||
|
any references to the name of the license steward (except to note that
|
||||||
|
such modified license differs from this License).
|
||||||
|
|
||||||
|
10.4. Distributing Source Code Form that is Incompatible With Secondary
|
||||||
|
Licenses If You choose to distribute Source Code Form that is
|
||||||
|
Incompatible With Secondary Licenses under the terms of this version of
|
||||||
|
the License, the notice described in Exhibit B of this License must be
|
||||||
|
attached.
|
||||||
|
|
||||||
|
Exhibit A - Source Code Form License Notice
|
||||||
|
|
||||||
|
This Source Code Form is subject to the
|
||||||
|
terms of the Mozilla Public License, v.
|
||||||
|
2.0. If a copy of the MPL was not
|
||||||
|
distributed with this file, You can
|
||||||
|
obtain one at
|
||||||
|
http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
If it is not possible or desirable to put the notice in a particular file,
|
||||||
|
then You may include the notice in a location (such as a LICENSE file in a
|
||||||
|
relevant directory) where a recipient would be likely to look for such a
|
||||||
|
notice.
|
||||||
|
|
||||||
|
You may add additional accurate notices of copyright ownership.
|
||||||
|
|
||||||
|
Exhibit B - "Incompatible With Secondary Licenses" Notice
|
||||||
|
|
||||||
|
This Source Code Form is "Incompatible
|
||||||
|
With Secondary Licenses", as defined by
|
||||||
|
the Mozilla Public License, v. 2.0.
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
default: test
|
||||||
|
|
||||||
|
test:
|
||||||
|
go vet ./...
|
||||||
|
go test -race ./...
|
||||||
|
|
||||||
|
updatedeps:
|
||||||
|
go get -f -t -u ./...
|
||||||
|
go get -f -u ./...
|
||||||
|
|
||||||
|
.PHONY: default test updatedeps
|
|
@ -0,0 +1,61 @@
|
||||||
|
go-retryablehttp
|
||||||
|
================
|
||||||
|
|
||||||
|
[![Build Status](http://img.shields.io/travis/hashicorp/go-retryablehttp.svg?style=flat-square)][travis]
|
||||||
|
[![Go Documentation](http://img.shields.io/badge/go-documentation-blue.svg?style=flat-square)][godocs]
|
||||||
|
|
||||||
|
[travis]: http://travis-ci.org/hashicorp/go-retryablehttp
|
||||||
|
[godocs]: http://godoc.org/github.com/hashicorp/go-retryablehttp
|
||||||
|
|
||||||
|
The `retryablehttp` package provides a familiar HTTP client interface with
|
||||||
|
automatic retries and exponential backoff. It is a thin wrapper over the
|
||||||
|
standard `net/http` client library and exposes nearly the same public API. This
|
||||||
|
makes `retryablehttp` very easy to drop into existing programs.
|
||||||
|
|
||||||
|
`retryablehttp` performs automatic retries under certain conditions. Mainly, if
|
||||||
|
an error is returned by the client (connection errors, etc.), or if a 500-range
|
||||||
|
response code is received (except 501), then a retry is invoked after a wait
|
||||||
|
period. Otherwise, the response is returned and left to the caller to
|
||||||
|
interpret.
|
||||||
|
|
||||||
|
The main difference from `net/http` is that requests which take a request body
|
||||||
|
(POST/PUT et. al) can have the body provided in a number of ways (some more or
|
||||||
|
less efficient) that allow "rewinding" the request body if the initial request
|
||||||
|
fails so that the full request can be attempted again. See the
|
||||||
|
[godoc](http://godoc.org/github.com/hashicorp/go-retryablehttp) for more
|
||||||
|
details.
|
||||||
|
|
||||||
|
Version 0.6.0 and before are compatible with Go prior to 1.12. From 0.6.1 onward, Go 1.12+ is required.
|
||||||
|
|
||||||
|
Example Use
|
||||||
|
===========
|
||||||
|
|
||||||
|
Using this library should look almost identical to what you would do with
|
||||||
|
`net/http`. The most simple example of a GET request is shown below:
|
||||||
|
|
||||||
|
```go
|
||||||
|
resp, err := retryablehttp.Get("/foo")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The returned response object is an `*http.Response`, the same thing you would
|
||||||
|
usually get from `net/http`. Had the request failed one or more times, the above
|
||||||
|
call would block and retry with exponential backoff.
|
||||||
|
|
||||||
|
## Getting a stdlib `*http.Client` with retries
|
||||||
|
|
||||||
|
It's possible to convert a `*retryablehttp.Client` directly to a `*http.Client`.
|
||||||
|
This makes use of retryablehttp broadly applicable with minimal effort. Simply
|
||||||
|
configure a `*retryablehttp.Client` as you wish, and then call `StandardClient()`:
|
||||||
|
|
||||||
|
```go
|
||||||
|
retryClient := retryablehttp.NewClient()
|
||||||
|
retryClient.RetryMax = 10
|
||||||
|
|
||||||
|
standardClient := retryClient.StandardClient() // *http.Client
|
||||||
|
```
|
||||||
|
|
||||||
|
For more usage and examples see the
|
||||||
|
[godoc](http://godoc.org/github.com/hashicorp/go-retryablehttp).
|
|
@ -0,0 +1,705 @@
|
||||||
|
// Package retryablehttp provides a familiar HTTP client interface with
|
||||||
|
// automatic retries and exponential backoff. It is a thin wrapper over the
|
||||||
|
// standard net/http client library and exposes nearly the same public API.
|
||||||
|
// This makes retryablehttp very easy to drop into existing programs.
|
||||||
|
//
|
||||||
|
// retryablehttp performs automatic retries under certain conditions. Mainly, if
|
||||||
|
// an error is returned by the client (connection errors etc), or if a 500-range
|
||||||
|
// response is received, then a retry is invoked. Otherwise, the response is
|
||||||
|
// returned and left to the caller to interpret.
|
||||||
|
//
|
||||||
|
// Requests which take a request body should provide a non-nil function
|
||||||
|
// parameter. The best choice is to provide either a function satisfying
|
||||||
|
// ReaderFunc which provides multiple io.Readers in an efficient manner, a
|
||||||
|
// *bytes.Buffer (the underlying raw byte slice will be used) or a raw byte
|
||||||
|
// slice. As it is a reference type, and we will wrap it as needed by readers,
|
||||||
|
// we can efficiently re-use the request body without needing to copy it. If an
|
||||||
|
// io.Reader (such as a *bytes.Reader) is provided, the full body will be read
|
||||||
|
// prior to the first request, and will be efficiently re-used for any retries.
|
||||||
|
// ReadSeeker can be used, but some users have observed occasional data races
|
||||||
|
// between the net/http library and the Seek functionality of some
|
||||||
|
// implementations of ReadSeeker, so should be avoided if possible.
|
||||||
|
package retryablehttp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"crypto/x509"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"math"
|
||||||
|
"math/rand"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/hashicorp/go-cleanhttp"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// Default retry configuration
|
||||||
|
defaultRetryWaitMin = 1 * time.Second
|
||||||
|
defaultRetryWaitMax = 30 * time.Second
|
||||||
|
defaultRetryMax = 4
|
||||||
|
|
||||||
|
// defaultLogger is the logger provided with defaultClient
|
||||||
|
defaultLogger = log.New(os.Stderr, "", log.LstdFlags)
|
||||||
|
|
||||||
|
// defaultClient is used for performing requests without explicitly making
|
||||||
|
// a new client. It is purposely private to avoid modifications.
|
||||||
|
defaultClient = NewClient()
|
||||||
|
|
||||||
|
// We need to consume response bodies to maintain http connections, but
|
||||||
|
// limit the size we consume to respReadLimit.
|
||||||
|
respReadLimit = int64(4096)
|
||||||
|
|
||||||
|
// A regular expression to match the error returned by net/http when the
|
||||||
|
// configured number of redirects is exhausted. This error isn't typed
|
||||||
|
// specifically so we resort to matching on the error string.
|
||||||
|
redirectsErrorRe = regexp.MustCompile(`stopped after \d+ redirects\z`)
|
||||||
|
|
||||||
|
// A regular expression to match the error returned by net/http when the
|
||||||
|
// scheme specified in the URL is invalid. This error isn't typed
|
||||||
|
// specifically so we resort to matching on the error string.
|
||||||
|
schemeErrorRe = regexp.MustCompile(`unsupported protocol scheme`)
|
||||||
|
)
|
||||||
|
|
||||||
|
// ReaderFunc is the type of function that can be given natively to NewRequest
|
||||||
|
type ReaderFunc func() (io.Reader, error)
|
||||||
|
|
||||||
|
// LenReader is an interface implemented by many in-memory io.Reader's. Used
|
||||||
|
// for automatically sending the right Content-Length header when possible.
|
||||||
|
type LenReader interface {
|
||||||
|
Len() int
|
||||||
|
}
|
||||||
|
|
||||||
|
// Request wraps the metadata needed to create HTTP requests.
|
||||||
|
type Request struct {
|
||||||
|
// body is a seekable reader over the request body payload. This is
|
||||||
|
// used to rewind the request data in between retries.
|
||||||
|
body ReaderFunc
|
||||||
|
|
||||||
|
// Embed an HTTP request directly. This makes a *Request act exactly
|
||||||
|
// like an *http.Request so that all meta methods are supported.
|
||||||
|
*http.Request
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithContext returns wrapped Request with a shallow copy of underlying *http.Request
|
||||||
|
// with its context changed to ctx. The provided ctx must be non-nil.
|
||||||
|
func (r *Request) WithContext(ctx context.Context) *Request {
|
||||||
|
r.Request = r.Request.WithContext(ctx)
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
// BodyBytes allows accessing the request body. It is an analogue to
|
||||||
|
// http.Request's Body variable, but it returns a copy of the underlying data
|
||||||
|
// rather than consuming it.
|
||||||
|
//
|
||||||
|
// This function is not thread-safe; do not call it at the same time as another
|
||||||
|
// call, or at the same time this request is being used with Client.Do.
|
||||||
|
func (r *Request) BodyBytes() ([]byte, error) {
|
||||||
|
if r.body == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
body, err := r.body()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
_, err = buf.ReadFrom(body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return buf.Bytes(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetBody allows setting the request body.
|
||||||
|
//
|
||||||
|
// It is useful if a new body needs to be set without constructing a new Request.
|
||||||
|
func (r *Request) SetBody(rawBody interface{}) error {
|
||||||
|
bodyReader, contentLength, err := getBodyReaderAndContentLength(rawBody)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
r.body = bodyReader
|
||||||
|
r.ContentLength = contentLength
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteTo allows copying the request body into a writer.
|
||||||
|
//
|
||||||
|
// It writes data to w until there's no more data to write or
|
||||||
|
// when an error occurs. The return int64 value is the number of bytes
|
||||||
|
// written. Any error encountered during the write is also returned.
|
||||||
|
// The signature matches io.WriterTo interface.
|
||||||
|
func (r *Request) WriteTo(w io.Writer) (int64, error) {
|
||||||
|
body, err := r.body()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
if c, ok := body.(io.Closer); ok {
|
||||||
|
defer c.Close()
|
||||||
|
}
|
||||||
|
return io.Copy(w, body)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getBodyReaderAndContentLength(rawBody interface{}) (ReaderFunc, int64, error) {
|
||||||
|
var bodyReader ReaderFunc
|
||||||
|
var contentLength int64
|
||||||
|
|
||||||
|
switch body := rawBody.(type) {
|
||||||
|
// If they gave us a function already, great! Use it.
|
||||||
|
case ReaderFunc:
|
||||||
|
bodyReader = body
|
||||||
|
tmp, err := body()
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
if lr, ok := tmp.(LenReader); ok {
|
||||||
|
contentLength = int64(lr.Len())
|
||||||
|
}
|
||||||
|
if c, ok := tmp.(io.Closer); ok {
|
||||||
|
c.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
case func() (io.Reader, error):
|
||||||
|
bodyReader = body
|
||||||
|
tmp, err := body()
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
if lr, ok := tmp.(LenReader); ok {
|
||||||
|
contentLength = int64(lr.Len())
|
||||||
|
}
|
||||||
|
if c, ok := tmp.(io.Closer); ok {
|
||||||
|
c.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// If a regular byte slice, we can read it over and over via new
|
||||||
|
// readers
|
||||||
|
case []byte:
|
||||||
|
buf := body
|
||||||
|
bodyReader = func() (io.Reader, error) {
|
||||||
|
return bytes.NewReader(buf), nil
|
||||||
|
}
|
||||||
|
contentLength = int64(len(buf))
|
||||||
|
|
||||||
|
// If a bytes.Buffer we can read the underlying byte slice over and
|
||||||
|
// over
|
||||||
|
case *bytes.Buffer:
|
||||||
|
buf := body
|
||||||
|
bodyReader = func() (io.Reader, error) {
|
||||||
|
return bytes.NewReader(buf.Bytes()), nil
|
||||||
|
}
|
||||||
|
contentLength = int64(buf.Len())
|
||||||
|
|
||||||
|
// We prioritize *bytes.Reader here because we don't really want to
|
||||||
|
// deal with it seeking so want it to match here instead of the
|
||||||
|
// io.ReadSeeker case.
|
||||||
|
case *bytes.Reader:
|
||||||
|
buf, err := ioutil.ReadAll(body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
bodyReader = func() (io.Reader, error) {
|
||||||
|
return bytes.NewReader(buf), nil
|
||||||
|
}
|
||||||
|
contentLength = int64(len(buf))
|
||||||
|
|
||||||
|
// Compat case
|
||||||
|
case io.ReadSeeker:
|
||||||
|
raw := body
|
||||||
|
bodyReader = func() (io.Reader, error) {
|
||||||
|
_, err := raw.Seek(0, 0)
|
||||||
|
return ioutil.NopCloser(raw), err
|
||||||
|
}
|
||||||
|
if lr, ok := raw.(LenReader); ok {
|
||||||
|
contentLength = int64(lr.Len())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read all in so we can reset
|
||||||
|
case io.Reader:
|
||||||
|
buf, err := ioutil.ReadAll(body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
bodyReader = func() (io.Reader, error) {
|
||||||
|
return bytes.NewReader(buf), nil
|
||||||
|
}
|
||||||
|
contentLength = int64(len(buf))
|
||||||
|
|
||||||
|
// No body provided, nothing to do
|
||||||
|
case nil:
|
||||||
|
|
||||||
|
// Unrecognized type
|
||||||
|
default:
|
||||||
|
return nil, 0, fmt.Errorf("cannot handle type %T", rawBody)
|
||||||
|
}
|
||||||
|
return bodyReader, contentLength, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromRequest wraps an http.Request in a retryablehttp.Request
|
||||||
|
func FromRequest(r *http.Request) (*Request, error) {
|
||||||
|
bodyReader, _, err := getBodyReaderAndContentLength(r.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// Could assert contentLength == r.ContentLength
|
||||||
|
return &Request{bodyReader, r}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewRequest creates a new wrapped request.
|
||||||
|
func NewRequest(method, url string, rawBody interface{}) (*Request, error) {
|
||||||
|
bodyReader, contentLength, err := getBodyReaderAndContentLength(rawBody)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
httpReq, err := http.NewRequest(method, url, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
httpReq.ContentLength = contentLength
|
||||||
|
|
||||||
|
return &Request{bodyReader, httpReq}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Logger interface allows to use other loggers than
|
||||||
|
// standard log.Logger.
|
||||||
|
type Logger interface {
|
||||||
|
Printf(string, ...interface{})
|
||||||
|
}
|
||||||
|
|
||||||
|
// LeveledLogger interface implements the basic methods that a logger library needs
|
||||||
|
type LeveledLogger interface {
|
||||||
|
Error(string, ...interface{})
|
||||||
|
Info(string, ...interface{})
|
||||||
|
Debug(string, ...interface{})
|
||||||
|
Warn(string, ...interface{})
|
||||||
|
}
|
||||||
|
|
||||||
|
// hookLogger adapts an LeveledLogger to Logger for use by the existing hook functions
|
||||||
|
// without changing the API.
|
||||||
|
type hookLogger struct {
|
||||||
|
LeveledLogger
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h hookLogger) Printf(s string, args ...interface{}) {
|
||||||
|
h.Info(fmt.Sprintf(s, args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// RequestLogHook allows a function to run before each retry. The HTTP
|
||||||
|
// request which will be made, and the retry number (0 for the initial
|
||||||
|
// request) are available to users. The internal logger is exposed to
|
||||||
|
// consumers.
|
||||||
|
type RequestLogHook func(Logger, *http.Request, int)
|
||||||
|
|
||||||
|
// ResponseLogHook is like RequestLogHook, but allows running a function
|
||||||
|
// on each HTTP response. This function will be invoked at the end of
|
||||||
|
// every HTTP request executed, regardless of whether a subsequent retry
|
||||||
|
// needs to be performed or not. If the response body is read or closed
|
||||||
|
// from this method, this will affect the response returned from Do().
|
||||||
|
type ResponseLogHook func(Logger, *http.Response)
|
||||||
|
|
||||||
|
// CheckRetry specifies a policy for handling retries. It is called
|
||||||
|
// following each request with the response and error values returned by
|
||||||
|
// the http.Client. If CheckRetry returns false, the Client stops retrying
|
||||||
|
// and returns the response to the caller. If CheckRetry returns an error,
|
||||||
|
// that error value is returned in lieu of the error from the request. The
|
||||||
|
// Client will close any response body when retrying, but if the retry is
|
||||||
|
// aborted it is up to the CheckRetry callback to properly close any
|
||||||
|
// response body before returning.
|
||||||
|
type CheckRetry func(ctx context.Context, resp *http.Response, err error) (bool, error)
|
||||||
|
|
||||||
|
// Backoff specifies a policy for how long to wait between retries.
|
||||||
|
// It is called after a failing request to determine the amount of time
|
||||||
|
// that should pass before trying again.
|
||||||
|
type Backoff func(min, max time.Duration, attemptNum int, resp *http.Response) time.Duration
|
||||||
|
|
||||||
|
// ErrorHandler is called if retries are expired, containing the last status
|
||||||
|
// from the http library. If not specified, default behavior for the library is
|
||||||
|
// to close the body and return an error indicating how many tries were
|
||||||
|
// attempted. If overriding this, be sure to close the body if needed.
|
||||||
|
type ErrorHandler func(resp *http.Response, err error, numTries int) (*http.Response, error)
|
||||||
|
|
||||||
|
// Client is used to make HTTP requests. It adds additional functionality
|
||||||
|
// like automatic retries to tolerate minor outages.
|
||||||
|
type Client struct {
|
||||||
|
HTTPClient *http.Client // Internal HTTP client.
|
||||||
|
Logger interface{} // Customer logger instance. Can be either Logger or LeveledLogger
|
||||||
|
|
||||||
|
RetryWaitMin time.Duration // Minimum time to wait
|
||||||
|
RetryWaitMax time.Duration // Maximum time to wait
|
||||||
|
RetryMax int // Maximum number of retries
|
||||||
|
|
||||||
|
// RequestLogHook allows a user-supplied function to be called
|
||||||
|
// before each retry.
|
||||||
|
RequestLogHook RequestLogHook
|
||||||
|
|
||||||
|
// ResponseLogHook allows a user-supplied function to be called
|
||||||
|
// with the response from each HTTP request executed.
|
||||||
|
ResponseLogHook ResponseLogHook
|
||||||
|
|
||||||
|
// CheckRetry specifies the policy for handling retries, and is called
|
||||||
|
// after each request. The default policy is DefaultRetryPolicy.
|
||||||
|
CheckRetry CheckRetry
|
||||||
|
|
||||||
|
// Backoff specifies the policy for how long to wait between retries
|
||||||
|
Backoff Backoff
|
||||||
|
|
||||||
|
// ErrorHandler specifies the custom error handler to use, if any
|
||||||
|
ErrorHandler ErrorHandler
|
||||||
|
|
||||||
|
loggerInit sync.Once
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewClient creates a new Client with default settings.
|
||||||
|
func NewClient() *Client {
|
||||||
|
return &Client{
|
||||||
|
HTTPClient: cleanhttp.DefaultPooledClient(),
|
||||||
|
Logger: defaultLogger,
|
||||||
|
RetryWaitMin: defaultRetryWaitMin,
|
||||||
|
RetryWaitMax: defaultRetryWaitMax,
|
||||||
|
RetryMax: defaultRetryMax,
|
||||||
|
CheckRetry: DefaultRetryPolicy,
|
||||||
|
Backoff: DefaultBackoff,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) logger() interface{} {
|
||||||
|
c.loggerInit.Do(func() {
|
||||||
|
if c.Logger == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch c.Logger.(type) {
|
||||||
|
case Logger, LeveledLogger:
|
||||||
|
// ok
|
||||||
|
default:
|
||||||
|
// This should happen in dev when they are setting Logger and work on code, not in prod.
|
||||||
|
panic(fmt.Sprintf("invalid logger type passed, must be Logger or LeveledLogger, was %T", c.Logger))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return c.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultRetryPolicy provides a default callback for Client.CheckRetry, which
|
||||||
|
// will retry on connection errors and server errors.
|
||||||
|
func DefaultRetryPolicy(ctx context.Context, resp *http.Response, err error) (bool, error) {
|
||||||
|
// do not retry on context.Canceled or context.DeadlineExceeded
|
||||||
|
if ctx.Err() != nil {
|
||||||
|
return false, ctx.Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
if v, ok := err.(*url.Error); ok {
|
||||||
|
// Don't retry if the error was due to too many redirects.
|
||||||
|
if redirectsErrorRe.MatchString(v.Error()) {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't retry if the error was due to an invalid protocol scheme.
|
||||||
|
if schemeErrorRe.MatchString(v.Error()) {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't retry if the error was due to TLS cert verification failure.
|
||||||
|
if _, ok := v.Err.(x509.UnknownAuthorityError); ok {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The error is likely recoverable so retry.
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check the response code. We retry on 500-range responses to allow
|
||||||
|
// the server time to recover, as 500's are typically not permanent
|
||||||
|
// errors and may relate to outages on the server side. This will catch
|
||||||
|
// invalid response codes as well, like 0 and 999.
|
||||||
|
if resp.StatusCode == 0 || (resp.StatusCode >= 500 && resp.StatusCode != 501) {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultBackoff provides a default callback for Client.Backoff which
|
||||||
|
// will perform exponential backoff based on the attempt number and limited
|
||||||
|
// by the provided minimum and maximum durations.
|
||||||
|
func DefaultBackoff(min, max time.Duration, attemptNum int, resp *http.Response) time.Duration {
|
||||||
|
mult := math.Pow(2, float64(attemptNum)) * float64(min)
|
||||||
|
sleep := time.Duration(mult)
|
||||||
|
if float64(sleep) != mult || sleep > max {
|
||||||
|
sleep = max
|
||||||
|
}
|
||||||
|
return sleep
|
||||||
|
}
|
||||||
|
|
||||||
|
// LinearJitterBackoff provides a callback for Client.Backoff which will
|
||||||
|
// perform linear backoff based on the attempt number and with jitter to
|
||||||
|
// prevent a thundering herd.
|
||||||
|
//
|
||||||
|
// min and max here are *not* absolute values. The number to be multiplied by
|
||||||
|
// the attempt number will be chosen at random from between them, thus they are
|
||||||
|
// bounding the jitter.
|
||||||
|
//
|
||||||
|
// For instance:
|
||||||
|
// * To get strictly linear backoff of one second increasing each retry, set
|
||||||
|
// both to one second (1s, 2s, 3s, 4s, ...)
|
||||||
|
// * To get a small amount of jitter centered around one second increasing each
|
||||||
|
// retry, set to around one second, such as a min of 800ms and max of 1200ms
|
||||||
|
// (892ms, 2102ms, 2945ms, 4312ms, ...)
|
||||||
|
// * To get extreme jitter, set to a very wide spread, such as a min of 100ms
|
||||||
|
// and a max of 20s (15382ms, 292ms, 51321ms, 35234ms, ...)
|
||||||
|
func LinearJitterBackoff(min, max time.Duration, attemptNum int, resp *http.Response) time.Duration {
|
||||||
|
// attemptNum always starts at zero but we want to start at 1 for multiplication
|
||||||
|
attemptNum++
|
||||||
|
|
||||||
|
if max <= min {
|
||||||
|
// Unclear what to do here, or they are the same, so return min *
|
||||||
|
// attemptNum
|
||||||
|
return min * time.Duration(attemptNum)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Seed rand; doing this every time is fine
|
||||||
|
rand := rand.New(rand.NewSource(int64(time.Now().Nanosecond())))
|
||||||
|
|
||||||
|
// Pick a random number that lies somewhere between the min and max and
|
||||||
|
// multiply by the attemptNum. attemptNum starts at zero so we always
|
||||||
|
// increment here. We first get a random percentage, then apply that to the
|
||||||
|
// difference between min and max, and add to min.
|
||||||
|
jitter := rand.Float64() * float64(max-min)
|
||||||
|
jitterMin := int64(jitter) + int64(min)
|
||||||
|
return time.Duration(jitterMin * int64(attemptNum))
|
||||||
|
}
|
||||||
|
|
||||||
|
// PassthroughErrorHandler is an ErrorHandler that directly passes through the
|
||||||
|
// values from the net/http library for the final request. The body is not
|
||||||
|
// closed.
|
||||||
|
func PassthroughErrorHandler(resp *http.Response, err error, _ int) (*http.Response, error) {
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do wraps calling an HTTP method with retries.
|
||||||
|
func (c *Client) Do(req *Request) (*http.Response, error) {
|
||||||
|
if c.HTTPClient == nil {
|
||||||
|
c.HTTPClient = cleanhttp.DefaultPooledClient()
|
||||||
|
}
|
||||||
|
|
||||||
|
logger := c.logger()
|
||||||
|
|
||||||
|
if logger != nil {
|
||||||
|
switch v := logger.(type) {
|
||||||
|
case Logger:
|
||||||
|
v.Printf("[DEBUG] %s %s", req.Method, req.URL)
|
||||||
|
case LeveledLogger:
|
||||||
|
v.Debug("performing request", "method", req.Method, "url", req.URL)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var resp *http.Response
|
||||||
|
var err error
|
||||||
|
|
||||||
|
for i := 0; ; i++ {
|
||||||
|
var code int // HTTP response code
|
||||||
|
|
||||||
|
// Always rewind the request body when non-nil.
|
||||||
|
if req.body != nil {
|
||||||
|
body, err := req.body()
|
||||||
|
if err != nil {
|
||||||
|
c.HTTPClient.CloseIdleConnections()
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
if c, ok := body.(io.ReadCloser); ok {
|
||||||
|
req.Body = c
|
||||||
|
} else {
|
||||||
|
req.Body = ioutil.NopCloser(body)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.RequestLogHook != nil {
|
||||||
|
switch v := logger.(type) {
|
||||||
|
case Logger:
|
||||||
|
c.RequestLogHook(v, req.Request, i)
|
||||||
|
case LeveledLogger:
|
||||||
|
c.RequestLogHook(hookLogger{v}, req.Request, i)
|
||||||
|
default:
|
||||||
|
c.RequestLogHook(nil, req.Request, i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attempt the request
|
||||||
|
resp, err = c.HTTPClient.Do(req.Request)
|
||||||
|
if resp != nil {
|
||||||
|
code = resp.StatusCode
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if we should continue with retries.
|
||||||
|
checkOK, checkErr := c.CheckRetry(req.Context(), resp, err)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
switch v := logger.(type) {
|
||||||
|
case Logger:
|
||||||
|
v.Printf("[ERR] %s %s request failed: %v", req.Method, req.URL, err)
|
||||||
|
case LeveledLogger:
|
||||||
|
v.Error("request failed", "error", err, "method", req.Method, "url", req.URL)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Call this here to maintain the behavior of logging all requests,
|
||||||
|
// even if CheckRetry signals to stop.
|
||||||
|
if c.ResponseLogHook != nil {
|
||||||
|
// Call the response logger function if provided.
|
||||||
|
switch v := logger.(type) {
|
||||||
|
case Logger:
|
||||||
|
c.ResponseLogHook(v, resp)
|
||||||
|
case LeveledLogger:
|
||||||
|
c.ResponseLogHook(hookLogger{v}, resp)
|
||||||
|
default:
|
||||||
|
c.ResponseLogHook(nil, resp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now decide if we should continue.
|
||||||
|
if !checkOK {
|
||||||
|
if checkErr != nil {
|
||||||
|
err = checkErr
|
||||||
|
}
|
||||||
|
c.HTTPClient.CloseIdleConnections()
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// We do this before drainBody because there's no need for the I/O if
|
||||||
|
// we're breaking out
|
||||||
|
remain := c.RetryMax - i
|
||||||
|
if remain <= 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// We're going to retry, consume any response to reuse the connection.
|
||||||
|
if err == nil && resp != nil {
|
||||||
|
c.drainBody(resp.Body)
|
||||||
|
}
|
||||||
|
|
||||||
|
wait := c.Backoff(c.RetryWaitMin, c.RetryWaitMax, i, resp)
|
||||||
|
desc := fmt.Sprintf("%s %s", req.Method, req.URL)
|
||||||
|
if code > 0 {
|
||||||
|
desc = fmt.Sprintf("%s (status: %d)", desc, code)
|
||||||
|
}
|
||||||
|
if logger != nil {
|
||||||
|
switch v := logger.(type) {
|
||||||
|
case Logger:
|
||||||
|
v.Printf("[DEBUG] %s: retrying in %s (%d left)", desc, wait, remain)
|
||||||
|
case LeveledLogger:
|
||||||
|
v.Debug("retrying request", "request", desc, "timeout", wait, "remaining", remain)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
case <-req.Context().Done():
|
||||||
|
c.HTTPClient.CloseIdleConnections()
|
||||||
|
return nil, req.Context().Err()
|
||||||
|
case <-time.After(wait):
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.ErrorHandler != nil {
|
||||||
|
c.HTTPClient.CloseIdleConnections()
|
||||||
|
return c.ErrorHandler(resp, err, c.RetryMax+1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// By default, we close the response body and return an error without
|
||||||
|
// returning the response
|
||||||
|
if resp != nil {
|
||||||
|
resp.Body.Close()
|
||||||
|
}
|
||||||
|
c.HTTPClient.CloseIdleConnections()
|
||||||
|
return nil, fmt.Errorf("%s %s giving up after %d attempts",
|
||||||
|
req.Method, req.URL, c.RetryMax+1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to read the response body so we can reuse this connection.
|
||||||
|
func (c *Client) drainBody(body io.ReadCloser) {
|
||||||
|
defer body.Close()
|
||||||
|
_, err := io.Copy(ioutil.Discard, io.LimitReader(body, respReadLimit))
|
||||||
|
if err != nil {
|
||||||
|
if c.logger() != nil {
|
||||||
|
switch v := c.logger().(type) {
|
||||||
|
case Logger:
|
||||||
|
v.Printf("[ERR] error reading response body: %v", err)
|
||||||
|
case LeveledLogger:
|
||||||
|
v.Error("error reading response body", "error", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get is a shortcut for doing a GET request without making a new client.
|
||||||
|
func Get(url string) (*http.Response, error) {
|
||||||
|
return defaultClient.Get(url)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get is a convenience helper for doing simple GET requests.
|
||||||
|
func (c *Client) Get(url string) (*http.Response, error) {
|
||||||
|
req, err := NewRequest("GET", url, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return c.Do(req)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Head is a shortcut for doing a HEAD request without making a new client.
|
||||||
|
func Head(url string) (*http.Response, error) {
|
||||||
|
return defaultClient.Head(url)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Head is a convenience method for doing simple HEAD requests.
|
||||||
|
func (c *Client) Head(url string) (*http.Response, error) {
|
||||||
|
req, err := NewRequest("HEAD", url, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return c.Do(req)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Post is a shortcut for doing a POST request without making a new client.
|
||||||
|
func Post(url, bodyType string, body interface{}) (*http.Response, error) {
|
||||||
|
return defaultClient.Post(url, bodyType, body)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Post is a convenience method for doing simple POST requests.
|
||||||
|
func (c *Client) Post(url, bodyType string, body interface{}) (*http.Response, error) {
|
||||||
|
req, err := NewRequest("POST", url, body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
req.Header.Set("Content-Type", bodyType)
|
||||||
|
return c.Do(req)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PostForm is a shortcut to perform a POST with form data without creating
|
||||||
|
// a new client.
|
||||||
|
func PostForm(url string, data url.Values) (*http.Response, error) {
|
||||||
|
return defaultClient.PostForm(url, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PostForm is a convenience method for doing simple POST operations using
|
||||||
|
// pre-filled url.Values form data.
|
||||||
|
func (c *Client) PostForm(url string, data url.Values) (*http.Response, error) {
|
||||||
|
return c.Post(url, "application/x-www-form-urlencoded", strings.NewReader(data.Encode()))
|
||||||
|
}
|
||||||
|
|
||||||
|
// StandardClient returns a stdlib *http.Client with a custom Transport, which
|
||||||
|
// shims in a *retryablehttp.Client for added retries.
|
||||||
|
func (c *Client) StandardClient() *http.Client {
|
||||||
|
return &http.Client{
|
||||||
|
Transport: &RoundTripper{Client: c},
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
module github.com/hashicorp/go-retryablehttp
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/hashicorp/go-cleanhttp v0.5.1
|
||||||
|
github.com/hashicorp/go-hclog v0.9.2
|
||||||
|
)
|
||||||
|
|
||||||
|
go 1.13
|
|
@ -0,0 +1,10 @@
|
||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM=
|
||||||
|
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||||
|
github.com/hashicorp/go-hclog v0.9.2 h1:CG6TE5H9/JXsFWJCfoIVpKFIkFe6ysEuHirp4DxCsHI=
|
||||||
|
github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
|
||||||
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
|
@ -0,0 +1,43 @@
|
||||||
|
package retryablehttp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RoundTripper implements the http.RoundTripper interface, using a retrying
|
||||||
|
// HTTP client to execute requests.
|
||||||
|
//
|
||||||
|
// It is important to note that retryablehttp doesn't always act exactly as a
|
||||||
|
// RoundTripper should. This is highly dependent on the retryable client's
|
||||||
|
// configuration.
|
||||||
|
type RoundTripper struct {
|
||||||
|
// The client to use during requests. If nil, the default retryablehttp
|
||||||
|
// client and settings will be used.
|
||||||
|
Client *Client
|
||||||
|
|
||||||
|
// once ensures that the logic to initialize the default client runs at
|
||||||
|
// most once, in a single thread.
|
||||||
|
once sync.Once
|
||||||
|
}
|
||||||
|
|
||||||
|
// init initializes the underlying retryable client.
|
||||||
|
func (rt *RoundTripper) init() {
|
||||||
|
if rt.Client == nil {
|
||||||
|
rt.Client = NewClient()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RoundTrip satisfies the http.RoundTripper interface.
|
||||||
|
func (rt *RoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||||
|
rt.once.Do(rt.init)
|
||||||
|
|
||||||
|
// Convert the request to be retryable.
|
||||||
|
retryableReq, err := FromRequest(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute the request.
|
||||||
|
return rt.Client.Do(retryableReq)
|
||||||
|
}
|
|
@ -1,10 +1,8 @@
|
||||||
language: go
|
language: go
|
||||||
|
|
||||||
go:
|
go:
|
||||||
- 1.10.x
|
|
||||||
- 1.11.x
|
|
||||||
- 1.12.x
|
|
||||||
- 1.13.x
|
- 1.13.x
|
||||||
|
- 1.14.x
|
||||||
- master
|
- master
|
||||||
|
|
||||||
stages:
|
stages:
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
Apache License
|
Apache License
|
||||||
Version 2.0, January 2004
|
Version 2.0, January 2004
|
||||||
http://www.apache.org/licenses/
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
@ -178,7 +178,7 @@ Apache License
|
||||||
APPENDIX: How to apply the Apache License to your work.
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
To apply the Apache License to your work, attach the following
|
To apply the Apache License to your work, attach the following
|
||||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
replaced with your own identifying information. (Don't include
|
replaced with your own identifying information. (Don't include
|
||||||
the brackets!) The text should be enclosed in the appropriate
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
comment syntax for the file format. We also recommend that a
|
comment syntax for the file format. We also recommend that a
|
||||||
|
@ -186,7 +186,7 @@ Apache License
|
||||||
same "printed page" as the copyright notice for easier
|
same "printed page" as the copyright notice for easier
|
||||||
identification within third-party archives.
|
identification within third-party archives.
|
||||||
|
|
||||||
Copyright {yyyy} {name of copyright owner}
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
@ -199,4 +199,3 @@ Apache License
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,7 @@ incompatible changes that were needed to fully support the V4 Gitlab API.
|
||||||
This API client package covers most of the existing Gitlab API calls and is updated regularly
|
This API client package covers most of the existing Gitlab API calls and is updated regularly
|
||||||
to add new and/or missing endpoints. Currently the following services are supported:
|
to add new and/or missing endpoints. Currently the following services are supported:
|
||||||
|
|
||||||
|
- [x] Applications
|
||||||
- [x] Award Emojis
|
- [x] Award Emojis
|
||||||
- [x] Branches
|
- [x] Branches
|
||||||
- [x] Broadcast Messages
|
- [x] Broadcast Messages
|
||||||
|
@ -96,8 +97,21 @@ access different parts of the GitLab API. For example, to list all
|
||||||
users:
|
users:
|
||||||
|
|
||||||
```go
|
```go
|
||||||
git := gitlab.NewClient(nil, "yourtokengoeshere")
|
git, err := gitlab.NewClient("yourtokengoeshere")
|
||||||
//git.SetBaseURL("https://git.mydomain.com/api/v4")
|
if err != nil {
|
||||||
|
log.Fatalf("Failed to create client: %v", err)
|
||||||
|
}
|
||||||
|
users, _, err := git.Users.ListUsers(&gitlab.ListUsersOptions{})
|
||||||
|
```
|
||||||
|
|
||||||
|
There are a few `With...` option functions that can be used to customize
|
||||||
|
the API client. For example, to set a custom base URL:
|
||||||
|
|
||||||
|
```go
|
||||||
|
git, err := gitlab.NewClient("yourtokengoeshere", WithBaseURL("https://git.mydomain.com/api/v4"))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Failed to create client: %v", err)
|
||||||
|
}
|
||||||
users, _, err := git.Users.ListUsers(&gitlab.ListUsersOptions{})
|
users, _, err := git.Users.ListUsers(&gitlab.ListUsersOptions{})
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -105,7 +119,7 @@ Some API methods have optional parameters that can be passed. For example,
|
||||||
to list all projects for user "svanharmelen":
|
to list all projects for user "svanharmelen":
|
||||||
|
|
||||||
```go
|
```go
|
||||||
git := gitlab.NewClient(nil)
|
git := gitlab.NewClient("yourtokengoeshere")
|
||||||
opt := &ListProjectsOptions{Search: gitlab.String("svanharmelen")}
|
opt := &ListProjectsOptions{Search: gitlab.String("svanharmelen")}
|
||||||
projects, _, err := git.Projects.ListProjects(opt)
|
projects, _, err := git.Projects.ListProjects(opt)
|
||||||
```
|
```
|
||||||
|
@ -125,7 +139,10 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
git := gitlab.NewClient(nil, "yourtokengoeshere")
|
git, err := gitlab.NewClient("yourtokengoeshere")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Failed to create client: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
// Create new project
|
// Create new project
|
||||||
p := &gitlab.CreateProjectOptions{
|
p := &gitlab.CreateProjectOptions{
|
||||||
|
@ -166,7 +183,7 @@ For complete usage of go-gitlab, see the full [package docs](https://godoc.org/g
|
||||||
|
|
||||||
## Author
|
## Author
|
||||||
|
|
||||||
Sander van Harmelen (<sander@xanzy.io>)
|
Sander van Harmelen (<sander@vanharmelen.nl>)
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
|
|
@ -39,7 +39,7 @@ type ListAccessRequestsOptions ListOptions
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/access_requests.html#list-access-requests-for-a-group-or-project
|
// https://docs.gitlab.com/ce/api/access_requests.html#list-access-requests-for-a-group-or-project
|
||||||
func (s *AccessRequestsService) ListProjectAccessRequests(pid interface{}, opt *ListAccessRequestsOptions, options ...OptionFunc) ([]*AccessRequest, *Response, error) {
|
func (s *AccessRequestsService) ListProjectAccessRequests(pid interface{}, opt *ListAccessRequestsOptions, options ...RequestOptionFunc) ([]*AccessRequest, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -65,7 +65,7 @@ func (s *AccessRequestsService) ListProjectAccessRequests(pid interface{}, opt *
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/access_requests.html#list-access-requests-for-a-group-or-project
|
// https://docs.gitlab.com/ce/api/access_requests.html#list-access-requests-for-a-group-or-project
|
||||||
func (s *AccessRequestsService) ListGroupAccessRequests(gid interface{}, opt *ListAccessRequestsOptions, options ...OptionFunc) ([]*AccessRequest, *Response, error) {
|
func (s *AccessRequestsService) ListGroupAccessRequests(gid interface{}, opt *ListAccessRequestsOptions, options ...RequestOptionFunc) ([]*AccessRequest, *Response, error) {
|
||||||
group, err := parseID(gid)
|
group, err := parseID(gid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -91,7 +91,7 @@ func (s *AccessRequestsService) ListGroupAccessRequests(gid interface{}, opt *Li
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/access_requests.html#request-access-to-a-group-or-project
|
// https://docs.gitlab.com/ce/api/access_requests.html#request-access-to-a-group-or-project
|
||||||
func (s *AccessRequestsService) RequestProjectAccess(pid interface{}, options ...OptionFunc) (*AccessRequest, *Response, error) {
|
func (s *AccessRequestsService) RequestProjectAccess(pid interface{}, options ...RequestOptionFunc) (*AccessRequest, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -117,7 +117,7 @@ func (s *AccessRequestsService) RequestProjectAccess(pid interface{}, options ..
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/access_requests.html#request-access-to-a-group-or-project
|
// https://docs.gitlab.com/ce/api/access_requests.html#request-access-to-a-group-or-project
|
||||||
func (s *AccessRequestsService) RequestGroupAccess(gid interface{}, options ...OptionFunc) (*AccessRequest, *Response, error) {
|
func (s *AccessRequestsService) RequestGroupAccess(gid interface{}, options ...RequestOptionFunc) (*AccessRequest, *Response, error) {
|
||||||
group, err := parseID(gid)
|
group, err := parseID(gid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -151,7 +151,7 @@ type ApproveAccessRequestOptions struct {
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/access_requests.html#approve-an-access-request
|
// https://docs.gitlab.com/ce/api/access_requests.html#approve-an-access-request
|
||||||
func (s *AccessRequestsService) ApproveProjectAccessRequest(pid interface{}, user int, opt *ApproveAccessRequestOptions, options ...OptionFunc) (*AccessRequest, *Response, error) {
|
func (s *AccessRequestsService) ApproveProjectAccessRequest(pid interface{}, user int, opt *ApproveAccessRequestOptions, options ...RequestOptionFunc) (*AccessRequest, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -176,7 +176,7 @@ func (s *AccessRequestsService) ApproveProjectAccessRequest(pid interface{}, use
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/access_requests.html#approve-an-access-request
|
// https://docs.gitlab.com/ce/api/access_requests.html#approve-an-access-request
|
||||||
func (s *AccessRequestsService) ApproveGroupAccessRequest(gid interface{}, user int, opt *ApproveAccessRequestOptions, options ...OptionFunc) (*AccessRequest, *Response, error) {
|
func (s *AccessRequestsService) ApproveGroupAccessRequest(gid interface{}, user int, opt *ApproveAccessRequestOptions, options ...RequestOptionFunc) (*AccessRequest, *Response, error) {
|
||||||
group, err := parseID(gid)
|
group, err := parseID(gid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -201,7 +201,7 @@ func (s *AccessRequestsService) ApproveGroupAccessRequest(gid interface{}, user
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/access_requests.html#deny-an-access-request
|
// https://docs.gitlab.com/ce/api/access_requests.html#deny-an-access-request
|
||||||
func (s *AccessRequestsService) DenyProjectAccessRequest(pid interface{}, user int, options ...OptionFunc) (*Response, error) {
|
func (s *AccessRequestsService) DenyProjectAccessRequest(pid interface{}, user int, options ...RequestOptionFunc) (*Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -220,7 +220,7 @@ func (s *AccessRequestsService) DenyProjectAccessRequest(pid interface{}, user i
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/access_requests.html#deny-an-access-request
|
// https://docs.gitlab.com/ce/api/access_requests.html#deny-an-access-request
|
||||||
func (s *AccessRequestsService) DenyGroupAccessRequest(gid interface{}, user int, options ...OptionFunc) (*Response, error) {
|
func (s *AccessRequestsService) DenyGroupAccessRequest(gid interface{}, user int, options ...RequestOptionFunc) (*Response, error) {
|
||||||
group, err := parseID(gid)
|
group, err := parseID(gid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -0,0 +1,100 @@
|
||||||
|
//
|
||||||
|
// Copyright 2017, Sander van Harmelen
|
||||||
|
//
|
||||||
|
// 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 gitlab
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
// ApplicationsService handles communication with administrables applications
|
||||||
|
// of the Gitlab API.
|
||||||
|
//
|
||||||
|
// Gitlab API docs : https://docs.gitlab.com/ee/api/applications.html
|
||||||
|
type ApplicationsService struct {
|
||||||
|
client *Client
|
||||||
|
}
|
||||||
|
|
||||||
|
type Application struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
ApplicationID string `json:"application_id"`
|
||||||
|
ApplicationName string `json:"application_name"`
|
||||||
|
Secret string `json:"secret"`
|
||||||
|
CallbackURL string `json:"callback_url"`
|
||||||
|
Confidential bool `json:"confidential"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateApplicationOptions represents the available CreateApplication() options.
|
||||||
|
//
|
||||||
|
// GitLab API docs:
|
||||||
|
// https://docs.gitlab.com/ce/api/applications.html#create-an-application
|
||||||
|
type CreateApplicationOptions struct {
|
||||||
|
Name *string `url:"name,omitempty" json:"name,omitempty"`
|
||||||
|
RedirectURI *string `url:"redirect_uri,omitempty" json:"redirect_uri,omitempty"`
|
||||||
|
Scopes *string `url:"scopes,omitempty" json:"scopes,omitempty"`
|
||||||
|
Confidential *bool `url:"confidential,omitempty" json:"confidential,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateApplication creates a new application owned by the authenticated user.
|
||||||
|
//
|
||||||
|
// Gitlab API docs : https://docs.gitlab.com/ce/api/applications.html#create-an-application
|
||||||
|
func (s *ApplicationsService) CreateApplication(opt *CreateApplicationOptions, options ...RequestOptionFunc) (*Application, *Response, error) {
|
||||||
|
req, err := s.client.NewRequest("POST", "applications", opt, options)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
a := new(Application)
|
||||||
|
resp, err := s.client.Do(req, a)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return a, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
type ListApplicationsOptions ListOptions
|
||||||
|
|
||||||
|
// ListApplications get a list of administrables applications by the authenticated user
|
||||||
|
//
|
||||||
|
// Gitlab API docs : https://docs.gitlab.com/ce/api/applications.html#list-all-applications
|
||||||
|
func (s *ApplicationsService) ListApplications(opt *ListApplicationsOptions, options ...RequestOptionFunc) ([]*Application, *Response, error) {
|
||||||
|
req, err := s.client.NewRequest("GET", "applications", opt, options)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var as []*Application
|
||||||
|
resp, err := s.client.Do(req, &as)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return as, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteApplication removes a specific application.
|
||||||
|
//
|
||||||
|
// GitLab API docs:
|
||||||
|
// https://docs.gitlab.com/ce/api/applications.html#delete-an-application
|
||||||
|
func (s *ApplicationsService) DeleteApplication(application int, options ...RequestOptionFunc) (*Response, error) {
|
||||||
|
u := fmt.Sprintf("applications/%d", application)
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest("DELETE", u, nil, options)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.client.Do(req, nil)
|
||||||
|
}
|
|
@ -66,7 +66,7 @@ type ListAwardEmojiOptions ListOptions
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/award_emoji.html#list-an-awardable-39-s-award-emoji
|
// https://docs.gitlab.com/ce/api/award_emoji.html#list-an-awardable-39-s-award-emoji
|
||||||
func (s *AwardEmojiService) ListMergeRequestAwardEmoji(pid interface{}, mergeRequestIID int, opt *ListAwardEmojiOptions, options ...OptionFunc) ([]*AwardEmoji, *Response, error) {
|
func (s *AwardEmojiService) ListMergeRequestAwardEmoji(pid interface{}, mergeRequestIID int, opt *ListAwardEmojiOptions, options ...RequestOptionFunc) ([]*AwardEmoji, *Response, error) {
|
||||||
return s.listAwardEmoji(pid, awardMergeRequest, mergeRequestIID, opt, options...)
|
return s.listAwardEmoji(pid, awardMergeRequest, mergeRequestIID, opt, options...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,7 +74,7 @@ func (s *AwardEmojiService) ListMergeRequestAwardEmoji(pid interface{}, mergeReq
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/award_emoji.html#list-an-awardable-39-s-award-emoji
|
// https://docs.gitlab.com/ce/api/award_emoji.html#list-an-awardable-39-s-award-emoji
|
||||||
func (s *AwardEmojiService) ListIssueAwardEmoji(pid interface{}, issueIID int, opt *ListAwardEmojiOptions, options ...OptionFunc) ([]*AwardEmoji, *Response, error) {
|
func (s *AwardEmojiService) ListIssueAwardEmoji(pid interface{}, issueIID int, opt *ListAwardEmojiOptions, options ...RequestOptionFunc) ([]*AwardEmoji, *Response, error) {
|
||||||
return s.listAwardEmoji(pid, awardIssue, issueIID, opt, options...)
|
return s.listAwardEmoji(pid, awardIssue, issueIID, opt, options...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,11 +82,11 @@ func (s *AwardEmojiService) ListIssueAwardEmoji(pid interface{}, issueIID int, o
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/award_emoji.html#list-an-awardable-39-s-award-emoji
|
// https://docs.gitlab.com/ce/api/award_emoji.html#list-an-awardable-39-s-award-emoji
|
||||||
func (s *AwardEmojiService) ListSnippetAwardEmoji(pid interface{}, snippetID int, opt *ListAwardEmojiOptions, options ...OptionFunc) ([]*AwardEmoji, *Response, error) {
|
func (s *AwardEmojiService) ListSnippetAwardEmoji(pid interface{}, snippetID int, opt *ListAwardEmojiOptions, options ...RequestOptionFunc) ([]*AwardEmoji, *Response, error) {
|
||||||
return s.listAwardEmoji(pid, awardSnippets, snippetID, opt, options...)
|
return s.listAwardEmoji(pid, awardSnippets, snippetID, opt, options...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *AwardEmojiService) listAwardEmoji(pid interface{}, resource string, resourceID int, opt *ListAwardEmojiOptions, options ...OptionFunc) ([]*AwardEmoji, *Response, error) {
|
func (s *AwardEmojiService) listAwardEmoji(pid interface{}, resource string, resourceID int, opt *ListAwardEmojiOptions, options ...RequestOptionFunc) ([]*AwardEmoji, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -115,7 +115,7 @@ func (s *AwardEmojiService) listAwardEmoji(pid interface{}, resource string, res
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/award_emoji.html#list-an-awardable-39-s-award-emoji
|
// https://docs.gitlab.com/ce/api/award_emoji.html#list-an-awardable-39-s-award-emoji
|
||||||
func (s *AwardEmojiService) GetMergeRequestAwardEmoji(pid interface{}, mergeRequestIID, awardID int, options ...OptionFunc) (*AwardEmoji, *Response, error) {
|
func (s *AwardEmojiService) GetMergeRequestAwardEmoji(pid interface{}, mergeRequestIID, awardID int, options ...RequestOptionFunc) (*AwardEmoji, *Response, error) {
|
||||||
return s.getAwardEmoji(pid, awardMergeRequest, mergeRequestIID, awardID, options...)
|
return s.getAwardEmoji(pid, awardMergeRequest, mergeRequestIID, awardID, options...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -123,7 +123,7 @@ func (s *AwardEmojiService) GetMergeRequestAwardEmoji(pid interface{}, mergeRequ
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/award_emoji.html#list-an-awardable-39-s-award-emoji
|
// https://docs.gitlab.com/ce/api/award_emoji.html#list-an-awardable-39-s-award-emoji
|
||||||
func (s *AwardEmojiService) GetIssueAwardEmoji(pid interface{}, issueIID, awardID int, options ...OptionFunc) (*AwardEmoji, *Response, error) {
|
func (s *AwardEmojiService) GetIssueAwardEmoji(pid interface{}, issueIID, awardID int, options ...RequestOptionFunc) (*AwardEmoji, *Response, error) {
|
||||||
return s.getAwardEmoji(pid, awardIssue, issueIID, awardID, options...)
|
return s.getAwardEmoji(pid, awardIssue, issueIID, awardID, options...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -131,11 +131,11 @@ func (s *AwardEmojiService) GetIssueAwardEmoji(pid interface{}, issueIID, awardI
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/award_emoji.html#list-an-awardable-39-s-award-emoji
|
// https://docs.gitlab.com/ce/api/award_emoji.html#list-an-awardable-39-s-award-emoji
|
||||||
func (s *AwardEmojiService) GetSnippetAwardEmoji(pid interface{}, snippetID, awardID int, options ...OptionFunc) (*AwardEmoji, *Response, error) {
|
func (s *AwardEmojiService) GetSnippetAwardEmoji(pid interface{}, snippetID, awardID int, options ...RequestOptionFunc) (*AwardEmoji, *Response, error) {
|
||||||
return s.getAwardEmoji(pid, awardSnippets, snippetID, awardID, options...)
|
return s.getAwardEmoji(pid, awardSnippets, snippetID, awardID, options...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *AwardEmojiService) getAwardEmoji(pid interface{}, resource string, resourceID, awardID int, options ...OptionFunc) (*AwardEmoji, *Response, error) {
|
func (s *AwardEmojiService) getAwardEmoji(pid interface{}, resource string, resourceID, awardID int, options ...RequestOptionFunc) (*AwardEmoji, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -174,7 +174,7 @@ type CreateAwardEmojiOptions struct {
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/award_emoji.html#award-a-new-emoji
|
// https://docs.gitlab.com/ce/api/award_emoji.html#award-a-new-emoji
|
||||||
func (s *AwardEmojiService) CreateMergeRequestAwardEmoji(pid interface{}, mergeRequestIID int, opt *CreateAwardEmojiOptions, options ...OptionFunc) (*AwardEmoji, *Response, error) {
|
func (s *AwardEmojiService) CreateMergeRequestAwardEmoji(pid interface{}, mergeRequestIID int, opt *CreateAwardEmojiOptions, options ...RequestOptionFunc) (*AwardEmoji, *Response, error) {
|
||||||
return s.createAwardEmoji(pid, awardMergeRequest, mergeRequestIID, opt, options...)
|
return s.createAwardEmoji(pid, awardMergeRequest, mergeRequestIID, opt, options...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -182,7 +182,7 @@ func (s *AwardEmojiService) CreateMergeRequestAwardEmoji(pid interface{}, mergeR
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/award_emoji.html#award-a-new-emoji
|
// https://docs.gitlab.com/ce/api/award_emoji.html#award-a-new-emoji
|
||||||
func (s *AwardEmojiService) CreateIssueAwardEmoji(pid interface{}, issueIID int, opt *CreateAwardEmojiOptions, options ...OptionFunc) (*AwardEmoji, *Response, error) {
|
func (s *AwardEmojiService) CreateIssueAwardEmoji(pid interface{}, issueIID int, opt *CreateAwardEmojiOptions, options ...RequestOptionFunc) (*AwardEmoji, *Response, error) {
|
||||||
return s.createAwardEmoji(pid, awardIssue, issueIID, opt, options...)
|
return s.createAwardEmoji(pid, awardIssue, issueIID, opt, options...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -190,11 +190,11 @@ func (s *AwardEmojiService) CreateIssueAwardEmoji(pid interface{}, issueIID int,
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/award_emoji.html#award-a-new-emoji
|
// https://docs.gitlab.com/ce/api/award_emoji.html#award-a-new-emoji
|
||||||
func (s *AwardEmojiService) CreateSnippetAwardEmoji(pid interface{}, snippetID int, opt *CreateAwardEmojiOptions, options ...OptionFunc) (*AwardEmoji, *Response, error) {
|
func (s *AwardEmojiService) CreateSnippetAwardEmoji(pid interface{}, snippetID int, opt *CreateAwardEmojiOptions, options ...RequestOptionFunc) (*AwardEmoji, *Response, error) {
|
||||||
return s.createAwardEmoji(pid, awardSnippets, snippetID, opt, options...)
|
return s.createAwardEmoji(pid, awardSnippets, snippetID, opt, options...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *AwardEmojiService) createAwardEmoji(pid interface{}, resource string, resourceID int, opt *CreateAwardEmojiOptions, options ...OptionFunc) (*AwardEmoji, *Response, error) {
|
func (s *AwardEmojiService) createAwardEmoji(pid interface{}, resource string, resourceID int, opt *CreateAwardEmojiOptions, options ...RequestOptionFunc) (*AwardEmoji, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -223,7 +223,7 @@ func (s *AwardEmojiService) createAwardEmoji(pid interface{}, resource string, r
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/award_emoji.html#award-a-new-emoji-on-a-note
|
// https://docs.gitlab.com/ce/api/award_emoji.html#award-a-new-emoji-on-a-note
|
||||||
func (s *AwardEmojiService) DeleteIssueAwardEmoji(pid interface{}, issueIID, awardID int, options ...OptionFunc) (*Response, error) {
|
func (s *AwardEmojiService) DeleteIssueAwardEmoji(pid interface{}, issueIID, awardID int, options ...RequestOptionFunc) (*Response, error) {
|
||||||
return s.deleteAwardEmoji(pid, awardMergeRequest, issueIID, awardID, options...)
|
return s.deleteAwardEmoji(pid, awardMergeRequest, issueIID, awardID, options...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -231,7 +231,7 @@ func (s *AwardEmojiService) DeleteIssueAwardEmoji(pid interface{}, issueIID, awa
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/award_emoji.html#award-a-new-emoji-on-a-note
|
// https://docs.gitlab.com/ce/api/award_emoji.html#award-a-new-emoji-on-a-note
|
||||||
func (s *AwardEmojiService) DeleteMergeRequestAwardEmoji(pid interface{}, mergeRequestIID, awardID int, options ...OptionFunc) (*Response, error) {
|
func (s *AwardEmojiService) DeleteMergeRequestAwardEmoji(pid interface{}, mergeRequestIID, awardID int, options ...RequestOptionFunc) (*Response, error) {
|
||||||
return s.deleteAwardEmoji(pid, awardMergeRequest, mergeRequestIID, awardID, options...)
|
return s.deleteAwardEmoji(pid, awardMergeRequest, mergeRequestIID, awardID, options...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -239,7 +239,7 @@ func (s *AwardEmojiService) DeleteMergeRequestAwardEmoji(pid interface{}, mergeR
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/award_emoji.html#award-a-new-emoji-on-a-note
|
// https://docs.gitlab.com/ce/api/award_emoji.html#award-a-new-emoji-on-a-note
|
||||||
func (s *AwardEmojiService) DeleteSnippetAwardEmoji(pid interface{}, snippetID, awardID int, options ...OptionFunc) (*Response, error) {
|
func (s *AwardEmojiService) DeleteSnippetAwardEmoji(pid interface{}, snippetID, awardID int, options ...RequestOptionFunc) (*Response, error) {
|
||||||
return s.deleteAwardEmoji(pid, awardMergeRequest, snippetID, awardID, options...)
|
return s.deleteAwardEmoji(pid, awardMergeRequest, snippetID, awardID, options...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -247,7 +247,7 @@ func (s *AwardEmojiService) DeleteSnippetAwardEmoji(pid interface{}, snippetID,
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/award_emoji.html#delete-an-award-emoji
|
// https://docs.gitlab.com/ce/api/award_emoji.html#delete-an-award-emoji
|
||||||
func (s *AwardEmojiService) deleteAwardEmoji(pid interface{}, resource string, resourceID, awardID int, options ...OptionFunc) (*Response, error) {
|
func (s *AwardEmojiService) deleteAwardEmoji(pid interface{}, resource string, resourceID, awardID int, options ...RequestOptionFunc) (*Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -267,7 +267,7 @@ func (s *AwardEmojiService) deleteAwardEmoji(pid interface{}, resource string, r
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/award_emoji.html#award-emoji-on-notes
|
// https://docs.gitlab.com/ce/api/award_emoji.html#award-emoji-on-notes
|
||||||
func (s *AwardEmojiService) ListIssuesAwardEmojiOnNote(pid interface{}, issueID, noteID int, opt *ListAwardEmojiOptions, options ...OptionFunc) ([]*AwardEmoji, *Response, error) {
|
func (s *AwardEmojiService) ListIssuesAwardEmojiOnNote(pid interface{}, issueID, noteID int, opt *ListAwardEmojiOptions, options ...RequestOptionFunc) ([]*AwardEmoji, *Response, error) {
|
||||||
return s.listAwardEmojiOnNote(pid, awardIssue, issueID, noteID, opt, options...)
|
return s.listAwardEmojiOnNote(pid, awardIssue, issueID, noteID, opt, options...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -276,7 +276,7 @@ func (s *AwardEmojiService) ListIssuesAwardEmojiOnNote(pid interface{}, issueID,
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/award_emoji.html#award-emoji-on-notes
|
// https://docs.gitlab.com/ce/api/award_emoji.html#award-emoji-on-notes
|
||||||
func (s *AwardEmojiService) ListMergeRequestAwardEmojiOnNote(pid interface{}, mergeRequestIID, noteID int, opt *ListAwardEmojiOptions, options ...OptionFunc) ([]*AwardEmoji, *Response, error) {
|
func (s *AwardEmojiService) ListMergeRequestAwardEmojiOnNote(pid interface{}, mergeRequestIID, noteID int, opt *ListAwardEmojiOptions, options ...RequestOptionFunc) ([]*AwardEmoji, *Response, error) {
|
||||||
return s.listAwardEmojiOnNote(pid, awardMergeRequest, mergeRequestIID, noteID, opt, options...)
|
return s.listAwardEmojiOnNote(pid, awardMergeRequest, mergeRequestIID, noteID, opt, options...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -285,11 +285,11 @@ func (s *AwardEmojiService) ListMergeRequestAwardEmojiOnNote(pid interface{}, me
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/award_emoji.html#award-emoji-on-notes
|
// https://docs.gitlab.com/ce/api/award_emoji.html#award-emoji-on-notes
|
||||||
func (s *AwardEmojiService) ListSnippetAwardEmojiOnNote(pid interface{}, snippetIID, noteID int, opt *ListAwardEmojiOptions, options ...OptionFunc) ([]*AwardEmoji, *Response, error) {
|
func (s *AwardEmojiService) ListSnippetAwardEmojiOnNote(pid interface{}, snippetIID, noteID int, opt *ListAwardEmojiOptions, options ...RequestOptionFunc) ([]*AwardEmoji, *Response, error) {
|
||||||
return s.listAwardEmojiOnNote(pid, awardSnippets, snippetIID, noteID, opt, options...)
|
return s.listAwardEmojiOnNote(pid, awardSnippets, snippetIID, noteID, opt, options...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *AwardEmojiService) listAwardEmojiOnNote(pid interface{}, resources string, ressourceID, noteID int, opt *ListAwardEmojiOptions, options ...OptionFunc) ([]*AwardEmoji, *Response, error) {
|
func (s *AwardEmojiService) listAwardEmojiOnNote(pid interface{}, resources string, ressourceID, noteID int, opt *ListAwardEmojiOptions, options ...RequestOptionFunc) ([]*AwardEmoji, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -315,7 +315,7 @@ func (s *AwardEmojiService) listAwardEmojiOnNote(pid interface{}, resources stri
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/award_emoji.html#award-emoji-on-notes
|
// https://docs.gitlab.com/ce/api/award_emoji.html#award-emoji-on-notes
|
||||||
func (s *AwardEmojiService) GetIssuesAwardEmojiOnNote(pid interface{}, issueID, noteID, awardID int, options ...OptionFunc) (*AwardEmoji, *Response, error) {
|
func (s *AwardEmojiService) GetIssuesAwardEmojiOnNote(pid interface{}, issueID, noteID, awardID int, options ...RequestOptionFunc) (*AwardEmoji, *Response, error) {
|
||||||
return s.getSingleNoteAwardEmoji(pid, awardIssue, issueID, noteID, awardID, options...)
|
return s.getSingleNoteAwardEmoji(pid, awardIssue, issueID, noteID, awardID, options...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -324,7 +324,7 @@ func (s *AwardEmojiService) GetIssuesAwardEmojiOnNote(pid interface{}, issueID,
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/award_emoji.html#award-emoji-on-notes
|
// https://docs.gitlab.com/ce/api/award_emoji.html#award-emoji-on-notes
|
||||||
func (s *AwardEmojiService) GetMergeRequestAwardEmojiOnNote(pid interface{}, mergeRequestIID, noteID, awardID int, options ...OptionFunc) (*AwardEmoji, *Response, error) {
|
func (s *AwardEmojiService) GetMergeRequestAwardEmojiOnNote(pid interface{}, mergeRequestIID, noteID, awardID int, options ...RequestOptionFunc) (*AwardEmoji, *Response, error) {
|
||||||
return s.getSingleNoteAwardEmoji(pid, awardMergeRequest, mergeRequestIID, noteID, awardID,
|
return s.getSingleNoteAwardEmoji(pid, awardMergeRequest, mergeRequestIID, noteID, awardID,
|
||||||
options...)
|
options...)
|
||||||
}
|
}
|
||||||
|
@ -333,11 +333,11 @@ func (s *AwardEmojiService) GetMergeRequestAwardEmojiOnNote(pid interface{}, mer
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/award_emoji.html#award-emoji-on-notes
|
// https://docs.gitlab.com/ce/api/award_emoji.html#award-emoji-on-notes
|
||||||
func (s *AwardEmojiService) GetSnippetAwardEmojiOnNote(pid interface{}, snippetIID, noteID, awardID int, options ...OptionFunc) (*AwardEmoji, *Response, error) {
|
func (s *AwardEmojiService) GetSnippetAwardEmojiOnNote(pid interface{}, snippetIID, noteID, awardID int, options ...RequestOptionFunc) (*AwardEmoji, *Response, error) {
|
||||||
return s.getSingleNoteAwardEmoji(pid, awardSnippets, snippetIID, noteID, awardID, options...)
|
return s.getSingleNoteAwardEmoji(pid, awardSnippets, snippetIID, noteID, awardID, options...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *AwardEmojiService) getSingleNoteAwardEmoji(pid interface{}, ressource string, resourceID, noteID, awardID int, options ...OptionFunc) (*AwardEmoji, *Response, error) {
|
func (s *AwardEmojiService) getSingleNoteAwardEmoji(pid interface{}, ressource string, resourceID, noteID, awardID int, options ...RequestOptionFunc) (*AwardEmoji, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -368,7 +368,7 @@ func (s *AwardEmojiService) getSingleNoteAwardEmoji(pid interface{}, ressource s
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/award_emoji.html#award-emoji-on-notes
|
// https://docs.gitlab.com/ce/api/award_emoji.html#award-emoji-on-notes
|
||||||
func (s *AwardEmojiService) CreateIssuesAwardEmojiOnNote(pid interface{}, issueID, noteID int, opt *CreateAwardEmojiOptions, options ...OptionFunc) (*AwardEmoji, *Response, error) {
|
func (s *AwardEmojiService) CreateIssuesAwardEmojiOnNote(pid interface{}, issueID, noteID int, opt *CreateAwardEmojiOptions, options ...RequestOptionFunc) (*AwardEmoji, *Response, error) {
|
||||||
return s.createAwardEmojiOnNote(pid, awardIssue, issueID, noteID, opt, options...)
|
return s.createAwardEmojiOnNote(pid, awardIssue, issueID, noteID, opt, options...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -377,7 +377,7 @@ func (s *AwardEmojiService) CreateIssuesAwardEmojiOnNote(pid interface{}, issueI
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/award_emoji.html#award-emoji-on-notes
|
// https://docs.gitlab.com/ce/api/award_emoji.html#award-emoji-on-notes
|
||||||
func (s *AwardEmojiService) CreateMergeRequestAwardEmojiOnNote(pid interface{}, mergeRequestIID, noteID int, opt *CreateAwardEmojiOptions, options ...OptionFunc) (*AwardEmoji, *Response, error) {
|
func (s *AwardEmojiService) CreateMergeRequestAwardEmojiOnNote(pid interface{}, mergeRequestIID, noteID int, opt *CreateAwardEmojiOptions, options ...RequestOptionFunc) (*AwardEmoji, *Response, error) {
|
||||||
return s.createAwardEmojiOnNote(pid, awardMergeRequest, mergeRequestIID, noteID, opt, options...)
|
return s.createAwardEmojiOnNote(pid, awardMergeRequest, mergeRequestIID, noteID, opt, options...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -385,7 +385,7 @@ func (s *AwardEmojiService) CreateMergeRequestAwardEmojiOnNote(pid interface{},
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/award_emoji.html#award-emoji-on-notes
|
// https://docs.gitlab.com/ce/api/award_emoji.html#award-emoji-on-notes
|
||||||
func (s *AwardEmojiService) CreateSnippetAwardEmojiOnNote(pid interface{}, snippetIID, noteID int, opt *CreateAwardEmojiOptions, options ...OptionFunc) (*AwardEmoji, *Response, error) {
|
func (s *AwardEmojiService) CreateSnippetAwardEmojiOnNote(pid interface{}, snippetIID, noteID int, opt *CreateAwardEmojiOptions, options ...RequestOptionFunc) (*AwardEmoji, *Response, error) {
|
||||||
return s.createAwardEmojiOnNote(pid, awardSnippets, snippetIID, noteID, opt, options...)
|
return s.createAwardEmojiOnNote(pid, awardSnippets, snippetIID, noteID, opt, options...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -393,7 +393,7 @@ func (s *AwardEmojiService) CreateSnippetAwardEmojiOnNote(pid interface{}, snipp
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/award_emoji.html#award-a-new-emoji-on-a-note
|
// https://docs.gitlab.com/ce/api/award_emoji.html#award-a-new-emoji-on-a-note
|
||||||
func (s *AwardEmojiService) createAwardEmojiOnNote(pid interface{}, resource string, resourceID, noteID int, opt *CreateAwardEmojiOptions, options ...OptionFunc) (*AwardEmoji, *Response, error) {
|
func (s *AwardEmojiService) createAwardEmojiOnNote(pid interface{}, resource string, resourceID, noteID int, opt *CreateAwardEmojiOptions, options ...RequestOptionFunc) (*AwardEmoji, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -423,7 +423,7 @@ func (s *AwardEmojiService) createAwardEmojiOnNote(pid interface{}, resource str
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/award_emoji.html#award-emoji-on-notes
|
// https://docs.gitlab.com/ce/api/award_emoji.html#award-emoji-on-notes
|
||||||
func (s *AwardEmojiService) DeleteIssuesAwardEmojiOnNote(pid interface{}, issueID, noteID, awardID int, options ...OptionFunc) (*Response, error) {
|
func (s *AwardEmojiService) DeleteIssuesAwardEmojiOnNote(pid interface{}, issueID, noteID, awardID int, options ...RequestOptionFunc) (*Response, error) {
|
||||||
return s.deleteAwardEmojiOnNote(pid, awardIssue, issueID, noteID, awardID, options...)
|
return s.deleteAwardEmojiOnNote(pid, awardIssue, issueID, noteID, awardID, options...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -432,7 +432,7 @@ func (s *AwardEmojiService) DeleteIssuesAwardEmojiOnNote(pid interface{}, issueI
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/award_emoji.html#award-emoji-on-notes
|
// https://docs.gitlab.com/ce/api/award_emoji.html#award-emoji-on-notes
|
||||||
func (s *AwardEmojiService) DeleteMergeRequestAwardEmojiOnNote(pid interface{}, mergeRequestIID, noteID, awardID int, options ...OptionFunc) (*Response, error) {
|
func (s *AwardEmojiService) DeleteMergeRequestAwardEmojiOnNote(pid interface{}, mergeRequestIID, noteID, awardID int, options ...RequestOptionFunc) (*Response, error) {
|
||||||
return s.deleteAwardEmojiOnNote(pid, awardMergeRequest, mergeRequestIID, noteID, awardID,
|
return s.deleteAwardEmojiOnNote(pid, awardMergeRequest, mergeRequestIID, noteID, awardID,
|
||||||
options...)
|
options...)
|
||||||
}
|
}
|
||||||
|
@ -441,11 +441,11 @@ func (s *AwardEmojiService) DeleteMergeRequestAwardEmojiOnNote(pid interface{},
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/award_emoji.html#award-emoji-on-notes
|
// https://docs.gitlab.com/ce/api/award_emoji.html#award-emoji-on-notes
|
||||||
func (s *AwardEmojiService) DeleteSnippetAwardEmojiOnNote(pid interface{}, snippetIID, noteID, awardID int, options ...OptionFunc) (*Response, error) {
|
func (s *AwardEmojiService) DeleteSnippetAwardEmojiOnNote(pid interface{}, snippetIID, noteID, awardID int, options ...RequestOptionFunc) (*Response, error) {
|
||||||
return s.deleteAwardEmojiOnNote(pid, awardSnippets, snippetIID, noteID, awardID, options...)
|
return s.deleteAwardEmojiOnNote(pid, awardSnippets, snippetIID, noteID, awardID, options...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *AwardEmojiService) deleteAwardEmojiOnNote(pid interface{}, resource string, resourceID, noteID, awardID int, options ...OptionFunc) (*Response, error) {
|
func (s *AwardEmojiService) deleteAwardEmojiOnNote(pid interface{}, resource string, resourceID, noteID, awardID int, options ...RequestOptionFunc) (*Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -66,7 +66,7 @@ type CreateIssueBoardOptions struct {
|
||||||
// CreateIssueBoard creates a new issue board.
|
// CreateIssueBoard creates a new issue board.
|
||||||
//
|
//
|
||||||
// GitLab API docs: https://docs.gitlab.com/ee/api/boards.html#create-a-board-starter
|
// GitLab API docs: https://docs.gitlab.com/ee/api/boards.html#create-a-board-starter
|
||||||
func (s *IssueBoardsService) CreateIssueBoard(pid interface{}, opt *CreateIssueBoardOptions, options ...OptionFunc) (*IssueBoard, *Response, error) {
|
func (s *IssueBoardsService) CreateIssueBoard(pid interface{}, opt *CreateIssueBoardOptions, options ...RequestOptionFunc) (*IssueBoard, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -101,7 +101,7 @@ type UpdateIssueBoardOptions struct {
|
||||||
// UpdateIssueBoard update an issue board.
|
// UpdateIssueBoard update an issue board.
|
||||||
//
|
//
|
||||||
// GitLab API docs: https://docs.gitlab.com/ee/api/boards.html#create-a-board-starter
|
// GitLab API docs: https://docs.gitlab.com/ee/api/boards.html#create-a-board-starter
|
||||||
func (s *IssueBoardsService) UpdateIssueBoard(pid interface{}, board int, opt *UpdateIssueBoardOptions, options ...OptionFunc) (*IssueBoard, *Response, error) {
|
func (s *IssueBoardsService) UpdateIssueBoard(pid interface{}, board int, opt *UpdateIssueBoardOptions, options ...RequestOptionFunc) (*IssueBoard, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -125,7 +125,7 @@ func (s *IssueBoardsService) UpdateIssueBoard(pid interface{}, board int, opt *U
|
||||||
// DeleteIssueBoard deletes an issue board.
|
// DeleteIssueBoard deletes an issue board.
|
||||||
//
|
//
|
||||||
// GitLab API docs: https://docs.gitlab.com/ee/api/boards.html#delete-a-board-starter
|
// GitLab API docs: https://docs.gitlab.com/ee/api/boards.html#delete-a-board-starter
|
||||||
func (s *IssueBoardsService) DeleteIssueBoard(pid interface{}, board int, options ...OptionFunc) (*Response, error) {
|
func (s *IssueBoardsService) DeleteIssueBoard(pid interface{}, board int, options ...RequestOptionFunc) (*Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -148,7 +148,7 @@ type ListIssueBoardsOptions ListOptions
|
||||||
// ListIssueBoards gets a list of all issue boards in a project.
|
// ListIssueBoards gets a list of all issue boards in a project.
|
||||||
//
|
//
|
||||||
// GitLab API docs: https://docs.gitlab.com/ce/api/boards.html#project-board
|
// GitLab API docs: https://docs.gitlab.com/ce/api/boards.html#project-board
|
||||||
func (s *IssueBoardsService) ListIssueBoards(pid interface{}, opt *ListIssueBoardsOptions, options ...OptionFunc) ([]*IssueBoard, *Response, error) {
|
func (s *IssueBoardsService) ListIssueBoards(pid interface{}, opt *ListIssueBoardsOptions, options ...RequestOptionFunc) ([]*IssueBoard, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -172,7 +172,7 @@ func (s *IssueBoardsService) ListIssueBoards(pid interface{}, opt *ListIssueBoar
|
||||||
// GetIssueBoard gets a single issue board of a project.
|
// GetIssueBoard gets a single issue board of a project.
|
||||||
//
|
//
|
||||||
// GitLab API docs: https://docs.gitlab.com/ce/api/boards.html#single-board
|
// GitLab API docs: https://docs.gitlab.com/ce/api/boards.html#single-board
|
||||||
func (s *IssueBoardsService) GetIssueBoard(pid interface{}, board int, options ...OptionFunc) (*IssueBoard, *Response, error) {
|
func (s *IssueBoardsService) GetIssueBoard(pid interface{}, board int, options ...RequestOptionFunc) (*IssueBoard, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -202,7 +202,7 @@ type GetIssueBoardListsOptions ListOptions
|
||||||
// backlog and closed lists.
|
// backlog and closed lists.
|
||||||
//
|
//
|
||||||
// GitLab API docs: https://docs.gitlab.com/ce/api/boards.html#list-board-lists
|
// GitLab API docs: https://docs.gitlab.com/ce/api/boards.html#list-board-lists
|
||||||
func (s *IssueBoardsService) GetIssueBoardLists(pid interface{}, board int, opt *GetIssueBoardListsOptions, options ...OptionFunc) ([]*BoardList, *Response, error) {
|
func (s *IssueBoardsService) GetIssueBoardLists(pid interface{}, board int, opt *GetIssueBoardListsOptions, options ...RequestOptionFunc) ([]*BoardList, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -226,7 +226,7 @@ func (s *IssueBoardsService) GetIssueBoardLists(pid interface{}, board int, opt
|
||||||
// GetIssueBoardList gets a single issue board list.
|
// GetIssueBoardList gets a single issue board list.
|
||||||
//
|
//
|
||||||
// GitLab API docs: https://docs.gitlab.com/ce/api/boards.html#single-board-list
|
// GitLab API docs: https://docs.gitlab.com/ce/api/boards.html#single-board-list
|
||||||
func (s *IssueBoardsService) GetIssueBoardList(pid interface{}, board, list int, options ...OptionFunc) (*BoardList, *Response, error) {
|
func (s *IssueBoardsService) GetIssueBoardList(pid interface{}, board, list int, options ...RequestOptionFunc) (*BoardList, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -262,7 +262,7 @@ type CreateIssueBoardListOptions struct {
|
||||||
// CreateIssueBoardList creates a new issue board list.
|
// CreateIssueBoardList creates a new issue board list.
|
||||||
//
|
//
|
||||||
// GitLab API docs: https://docs.gitlab.com/ce/api/boards.html#new-board-list
|
// GitLab API docs: https://docs.gitlab.com/ce/api/boards.html#new-board-list
|
||||||
func (s *IssueBoardsService) CreateIssueBoardList(pid interface{}, board int, opt *CreateIssueBoardListOptions, options ...OptionFunc) (*BoardList, *Response, error) {
|
func (s *IssueBoardsService) CreateIssueBoardList(pid interface{}, board int, opt *CreateIssueBoardListOptions, options ...RequestOptionFunc) (*BoardList, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -294,7 +294,7 @@ type UpdateIssueBoardListOptions struct {
|
||||||
// UpdateIssueBoardList updates the position of an existing issue board list.
|
// UpdateIssueBoardList updates the position of an existing issue board list.
|
||||||
//
|
//
|
||||||
// GitLab API docs: https://docs.gitlab.com/ce/api/boards.html#edit-board-list
|
// GitLab API docs: https://docs.gitlab.com/ce/api/boards.html#edit-board-list
|
||||||
func (s *IssueBoardsService) UpdateIssueBoardList(pid interface{}, board, list int, opt *UpdateIssueBoardListOptions, options ...OptionFunc) (*BoardList, *Response, error) {
|
func (s *IssueBoardsService) UpdateIssueBoardList(pid interface{}, board, list int, opt *UpdateIssueBoardListOptions, options ...RequestOptionFunc) (*BoardList, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -324,7 +324,7 @@ func (s *IssueBoardsService) UpdateIssueBoardList(pid interface{}, board, list i
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/boards.html#delete-a-board-list
|
// https://docs.gitlab.com/ce/api/boards.html#delete-a-board-list
|
||||||
func (s *IssueBoardsService) DeleteIssueBoardList(pid interface{}, board, list int, options ...OptionFunc) (*Response, error) {
|
func (s *IssueBoardsService) DeleteIssueBoardList(pid interface{}, board, list int, options ...RequestOptionFunc) (*Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -60,7 +60,7 @@ type ListBranchesOptions struct {
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/branches.html#list-repository-branches
|
// https://docs.gitlab.com/ce/api/branches.html#list-repository-branches
|
||||||
func (s *BranchesService) ListBranches(pid interface{}, opts *ListBranchesOptions, options ...OptionFunc) ([]*Branch, *Response, error) {
|
func (s *BranchesService) ListBranches(pid interface{}, opts *ListBranchesOptions, options ...RequestOptionFunc) ([]*Branch, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -85,7 +85,7 @@ func (s *BranchesService) ListBranches(pid interface{}, opts *ListBranchesOption
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/branches.html#get-single-repository-branch
|
// https://docs.gitlab.com/ce/api/branches.html#get-single-repository-branch
|
||||||
func (s *BranchesService) GetBranch(pid interface{}, branch string, options ...OptionFunc) (*Branch, *Response, error) {
|
func (s *BranchesService) GetBranch(pid interface{}, branch string, options ...RequestOptionFunc) (*Branch, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -121,7 +121,7 @@ type ProtectBranchOptions struct {
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/branches.html#protect-repository-branch
|
// https://docs.gitlab.com/ce/api/branches.html#protect-repository-branch
|
||||||
func (s *BranchesService) ProtectBranch(pid interface{}, branch string, opts *ProtectBranchOptions, options ...OptionFunc) (*Branch, *Response, error) {
|
func (s *BranchesService) ProtectBranch(pid interface{}, branch string, opts *ProtectBranchOptions, options ...RequestOptionFunc) (*Branch, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -148,7 +148,7 @@ func (s *BranchesService) ProtectBranch(pid interface{}, branch string, opts *Pr
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/branches.html#unprotect-repository-branch
|
// https://docs.gitlab.com/ce/api/branches.html#unprotect-repository-branch
|
||||||
func (s *BranchesService) UnprotectBranch(pid interface{}, branch string, options ...OptionFunc) (*Branch, *Response, error) {
|
func (s *BranchesService) UnprotectBranch(pid interface{}, branch string, options ...RequestOptionFunc) (*Branch, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -182,7 +182,7 @@ type CreateBranchOptions struct {
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/branches.html#create-repository-branch
|
// https://docs.gitlab.com/ce/api/branches.html#create-repository-branch
|
||||||
func (s *BranchesService) CreateBranch(pid interface{}, opt *CreateBranchOptions, options ...OptionFunc) (*Branch, *Response, error) {
|
func (s *BranchesService) CreateBranch(pid interface{}, opt *CreateBranchOptions, options ...RequestOptionFunc) (*Branch, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -207,7 +207,7 @@ func (s *BranchesService) CreateBranch(pid interface{}, opt *CreateBranchOptions
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/branches.html#delete-repository-branch
|
// https://docs.gitlab.com/ce/api/branches.html#delete-repository-branch
|
||||||
func (s *BranchesService) DeleteBranch(pid interface{}, branch string, options ...OptionFunc) (*Response, error) {
|
func (s *BranchesService) DeleteBranch(pid interface{}, branch string, options ...RequestOptionFunc) (*Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -226,7 +226,7 @@ func (s *BranchesService) DeleteBranch(pid interface{}, branch string, options .
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/branches.html#delete-merged-branches
|
// https://docs.gitlab.com/ce/api/branches.html#delete-merged-branches
|
||||||
func (s *BranchesService) DeleteMergedBranches(pid interface{}, options ...OptionFunc) (*Response, error) {
|
func (s *BranchesService) DeleteMergedBranches(pid interface{}, options ...RequestOptionFunc) (*Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -54,7 +54,7 @@ type ListBroadcastMessagesOptions ListOptions
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/broadcast_messages.html#get-all-broadcast-messages
|
// https://docs.gitlab.com/ce/api/broadcast_messages.html#get-all-broadcast-messages
|
||||||
func (s *BroadcastMessagesService) ListBroadcastMessages(opt *ListBroadcastMessagesOptions, options ...OptionFunc) ([]*BroadcastMessage, *Response, error) {
|
func (s *BroadcastMessagesService) ListBroadcastMessages(opt *ListBroadcastMessagesOptions, options ...RequestOptionFunc) ([]*BroadcastMessage, *Response, error) {
|
||||||
req, err := s.client.NewRequest("GET", "broadcast_messages", opt, options)
|
req, err := s.client.NewRequest("GET", "broadcast_messages", opt, options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -73,7 +73,7 @@ func (s *BroadcastMessagesService) ListBroadcastMessages(opt *ListBroadcastMessa
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/broadcast_messages.html#get-a-specific-broadcast-message
|
// https://docs.gitlab.com/ce/api/broadcast_messages.html#get-a-specific-broadcast-message
|
||||||
func (s *BroadcastMessagesService) GetBroadcastMessage(broadcast int, options ...OptionFunc) (*BroadcastMessage, *Response, error) {
|
func (s *BroadcastMessagesService) GetBroadcastMessage(broadcast int, options ...RequestOptionFunc) (*BroadcastMessage, *Response, error) {
|
||||||
u := fmt.Sprintf("broadcast_messages/%d", broadcast)
|
u := fmt.Sprintf("broadcast_messages/%d", broadcast)
|
||||||
|
|
||||||
req, err := s.client.NewRequest("GET", u, nil, options)
|
req, err := s.client.NewRequest("GET", u, nil, options)
|
||||||
|
@ -107,7 +107,7 @@ type CreateBroadcastMessageOptions struct {
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/broadcast_messages.html#create-a-broadcast-message
|
// https://docs.gitlab.com/ce/api/broadcast_messages.html#create-a-broadcast-message
|
||||||
func (s *BroadcastMessagesService) CreateBroadcastMessage(opt *CreateBroadcastMessageOptions, options ...OptionFunc) (*BroadcastMessage, *Response, error) {
|
func (s *BroadcastMessagesService) CreateBroadcastMessage(opt *CreateBroadcastMessageOptions, options ...RequestOptionFunc) (*BroadcastMessage, *Response, error) {
|
||||||
req, err := s.client.NewRequest("POST", "broadcast_messages", opt, options)
|
req, err := s.client.NewRequest("POST", "broadcast_messages", opt, options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -139,7 +139,7 @@ type UpdateBroadcastMessageOptions struct {
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/broadcast_messages.html#update-a-broadcast-message
|
// https://docs.gitlab.com/ce/api/broadcast_messages.html#update-a-broadcast-message
|
||||||
func (s *BroadcastMessagesService) UpdateBroadcastMessage(broadcast int, opt *UpdateBroadcastMessageOptions, options ...OptionFunc) (*BroadcastMessage, *Response, error) {
|
func (s *BroadcastMessagesService) UpdateBroadcastMessage(broadcast int, opt *UpdateBroadcastMessageOptions, options ...RequestOptionFunc) (*BroadcastMessage, *Response, error) {
|
||||||
u := fmt.Sprintf("broadcast_messages/%d", broadcast)
|
u := fmt.Sprintf("broadcast_messages/%d", broadcast)
|
||||||
|
|
||||||
req, err := s.client.NewRequest("PUT", u, opt, options)
|
req, err := s.client.NewRequest("PUT", u, opt, options)
|
||||||
|
@ -160,7 +160,7 @@ func (s *BroadcastMessagesService) UpdateBroadcastMessage(broadcast int, opt *Up
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/broadcast_messages.html#delete-a-broadcast-message
|
// https://docs.gitlab.com/ce/api/broadcast_messages.html#delete-a-broadcast-message
|
||||||
func (s *BroadcastMessagesService) DeleteBroadcastMessage(broadcast int, options ...OptionFunc) (*Response, error) {
|
func (s *BroadcastMessagesService) DeleteBroadcastMessage(broadcast int, options ...RequestOptionFunc) (*Response, error) {
|
||||||
u := fmt.Sprintf("broadcast_messages/%d", broadcast)
|
u := fmt.Sprintf("broadcast_messages/%d", broadcast)
|
||||||
|
|
||||||
req, err := s.client.NewRequest("DELETE", u, nil, options)
|
req, err := s.client.NewRequest("DELETE", u, nil, options)
|
||||||
|
|
|
@ -32,7 +32,7 @@ type ListCIYMLTemplatesOptions ListOptions
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/templates/gitlab_ci_ymls.html#list-gitlab-ci-yml-templates
|
// https://docs.gitlab.com/ce/api/templates/gitlab_ci_ymls.html#list-gitlab-ci-yml-templates
|
||||||
func (s *CIYMLTemplatesService) ListAllTemplates(opt *ListCIYMLTemplatesOptions, options ...OptionFunc) ([]*CIYMLTemplate, *Response, error) {
|
func (s *CIYMLTemplatesService) ListAllTemplates(opt *ListCIYMLTemplatesOptions, options ...RequestOptionFunc) ([]*CIYMLTemplate, *Response, error) {
|
||||||
req, err := s.client.NewRequest("GET", "templates/gitlab_ci_ymls", opt, options)
|
req, err := s.client.NewRequest("GET", "templates/gitlab_ci_ymls", opt, options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -51,7 +51,7 @@ func (s *CIYMLTemplatesService) ListAllTemplates(opt *ListCIYMLTemplatesOptions,
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/templates/gitlab_ci_ymls.html#single-gitlab-ci-yml-template
|
// https://docs.gitlab.com/ce/api/templates/gitlab_ci_ymls.html#single-gitlab-ci-yml-template
|
||||||
func (s *CIYMLTemplatesService) GetTemplate(key string, options ...OptionFunc) (*CIYMLTemplate, *Response, error) {
|
func (s *CIYMLTemplatesService) GetTemplate(key string, options ...RequestOptionFunc) (*CIYMLTemplate, *Response, error) {
|
||||||
u := fmt.Sprintf("templates/gitlab_ci_ymls/%s", pathEscape(key))
|
u := fmt.Sprintf("templates/gitlab_ci_ymls/%s", pathEscape(key))
|
||||||
|
|
||||||
req, err := s.client.NewRequest("GET", u, nil, options)
|
req, err := s.client.NewRequest("GET", u, nil, options)
|
||||||
|
|
|
@ -0,0 +1,49 @@
|
||||||
|
package gitlab
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
retryablehttp "github.com/hashicorp/go-retryablehttp"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ClientOptionFunc can be used customize a new GitLab API client.
|
||||||
|
type ClientOptionFunc func(*Client) error
|
||||||
|
|
||||||
|
// WithBaseURL sets the base URL for API requests to a custom endpoint.
|
||||||
|
func WithBaseURL(urlStr string) ClientOptionFunc {
|
||||||
|
return func(c *Client) error {
|
||||||
|
return c.setBaseURL(urlStr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithCustomBackoff can be used to configure a custom backoff policy.
|
||||||
|
func WithCustomBackoff(backoff retryablehttp.Backoff) ClientOptionFunc {
|
||||||
|
return func(c *Client) error {
|
||||||
|
c.client.Backoff = backoff
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithCustomRetry can be used to configure a custom retry policy.
|
||||||
|
func WithCustomRetry(checkRetry retryablehttp.CheckRetry) ClientOptionFunc {
|
||||||
|
return func(c *Client) error {
|
||||||
|
c.client.CheckRetry = checkRetry
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithHTTPClient can be used to configure a custom HTTP client.
|
||||||
|
func WithHTTPClient(httpClient *http.Client) ClientOptionFunc {
|
||||||
|
return func(c *Client) error {
|
||||||
|
c.client.HTTPClient = httpClient
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithoutRetries disables the default retry logic.
|
||||||
|
func WithoutRetries() ClientOptionFunc {
|
||||||
|
return func(c *Client) error {
|
||||||
|
c.disableRetries = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
|
@ -76,12 +76,13 @@ type ListCommitsOptions struct {
|
||||||
Path *string `url:"path,omitempty" json:"path,omitempty"`
|
Path *string `url:"path,omitempty" json:"path,omitempty"`
|
||||||
All *bool `url:"all,omitempty" json:"all,omitempty"`
|
All *bool `url:"all,omitempty" json:"all,omitempty"`
|
||||||
WithStats *bool `url:"with_stats,omitempty" json:"with_stats,omitempty"`
|
WithStats *bool `url:"with_stats,omitempty" json:"with_stats,omitempty"`
|
||||||
|
FirstParent *bool `url:"first_parent,omitempty" json:"first_parent,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListCommits gets a list of repository commits in a project.
|
// ListCommits gets a list of repository commits in a project.
|
||||||
//
|
//
|
||||||
// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html#list-commits
|
// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html#list-commits
|
||||||
func (s *CommitsService) ListCommits(pid interface{}, opt *ListCommitsOptions, options ...OptionFunc) ([]*Commit, *Response, error) {
|
func (s *CommitsService) ListCommits(pid interface{}, opt *ListCommitsOptions, options ...RequestOptionFunc) ([]*Commit, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -146,7 +147,7 @@ type GetCommitRefsOptions struct {
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/commits.html#get-references-a-commit-is-pushed-to
|
// https://docs.gitlab.com/ce/api/commits.html#get-references-a-commit-is-pushed-to
|
||||||
func (s *CommitsService) GetCommitRefs(pid interface{}, sha string, opt *GetCommitRefsOptions, options ...OptionFunc) ([]*CommitRef, *Response, error) {
|
func (s *CommitsService) GetCommitRefs(pid interface{}, sha string, opt *GetCommitRefsOptions, options ...RequestOptionFunc) ([]*CommitRef, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -171,7 +172,7 @@ func (s *CommitsService) GetCommitRefs(pid interface{}, sha string, opt *GetComm
|
||||||
// branch or tag.
|
// branch or tag.
|
||||||
//
|
//
|
||||||
// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html#get-a-single-commit
|
// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html#get-a-single-commit
|
||||||
func (s *CommitsService) GetCommit(pid interface{}, sha string, options ...OptionFunc) (*Commit, *Response, error) {
|
func (s *CommitsService) GetCommit(pid interface{}, sha string, options ...RequestOptionFunc) (*Commit, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -214,7 +215,7 @@ type CreateCommitOptions struct {
|
||||||
// CreateCommit creates a commit with multiple files and actions.
|
// CreateCommit creates a commit with multiple files and actions.
|
||||||
//
|
//
|
||||||
// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html#create-a-commit-with-multiple-files-and-actions
|
// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html#create-a-commit-with-multiple-files-and-actions
|
||||||
func (s *CommitsService) CreateCommit(pid interface{}, opt *CreateCommitOptions, options ...OptionFunc) (*Commit, *Response, error) {
|
func (s *CommitsService) CreateCommit(pid interface{}, opt *CreateCommitOptions, options ...RequestOptionFunc) (*Commit, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -263,7 +264,7 @@ type GetCommitDiffOptions ListOptions
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/commits.html#get-the-diff-of-a-commit
|
// https://docs.gitlab.com/ce/api/commits.html#get-the-diff-of-a-commit
|
||||||
func (s *CommitsService) GetCommitDiff(pid interface{}, sha string, opt *GetCommitDiffOptions, options ...OptionFunc) ([]*Diff, *Response, error) {
|
func (s *CommitsService) GetCommitDiff(pid interface{}, sha string, opt *GetCommitDiffOptions, options ...RequestOptionFunc) ([]*Diff, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -320,7 +321,7 @@ type GetCommitCommentsOptions ListOptions
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/commits.html#get-the-comments-of-a-commit
|
// https://docs.gitlab.com/ce/api/commits.html#get-the-comments-of-a-commit
|
||||||
func (s *CommitsService) GetCommitComments(pid interface{}, sha string, opt *GetCommitCommentsOptions, options ...OptionFunc) ([]*CommitComment, *Response, error) {
|
func (s *CommitsService) GetCommitComments(pid interface{}, sha string, opt *GetCommitCommentsOptions, options ...RequestOptionFunc) ([]*CommitComment, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -359,7 +360,7 @@ type PostCommitCommentOptions struct {
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/commits.html#post-comment-to-commit
|
// https://docs.gitlab.com/ce/api/commits.html#post-comment-to-commit
|
||||||
func (s *CommitsService) PostCommitComment(pid interface{}, sha string, opt *PostCommitCommentOptions, options ...OptionFunc) (*CommitComment, *Response, error) {
|
func (s *CommitsService) PostCommitComment(pid interface{}, sha string, opt *PostCommitCommentOptions, options ...RequestOptionFunc) (*CommitComment, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -411,7 +412,7 @@ type CommitStatus struct {
|
||||||
// GetCommitStatuses gets the statuses of a commit in a project.
|
// GetCommitStatuses gets the statuses of a commit in a project.
|
||||||
//
|
//
|
||||||
// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html#get-the-status-of-a-commit
|
// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html#get-the-status-of-a-commit
|
||||||
func (s *CommitsService) GetCommitStatuses(pid interface{}, sha string, opt *GetCommitStatusesOptions, options ...OptionFunc) ([]*CommitStatus, *Response, error) {
|
func (s *CommitsService) GetCommitStatuses(pid interface{}, sha string, opt *GetCommitStatusesOptions, options ...RequestOptionFunc) ([]*CommitStatus, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -447,7 +448,7 @@ type SetCommitStatusOptions struct {
|
||||||
// SetCommitStatus sets the status of a commit in a project.
|
// SetCommitStatus sets the status of a commit in a project.
|
||||||
//
|
//
|
||||||
// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html#post-the-status-to-commit
|
// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html#post-the-status-to-commit
|
||||||
func (s *CommitsService) SetCommitStatus(pid interface{}, sha string, opt *SetCommitStatusOptions, options ...OptionFunc) (*CommitStatus, *Response, error) {
|
func (s *CommitsService) SetCommitStatus(pid interface{}, sha string, opt *SetCommitStatusOptions, options ...RequestOptionFunc) (*CommitStatus, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -472,7 +473,7 @@ func (s *CommitsService) SetCommitStatus(pid interface{}, sha string, opt *SetCo
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/commits.html#list-merge-requests-associated-with-a-commit
|
// https://docs.gitlab.com/ce/api/commits.html#list-merge-requests-associated-with-a-commit
|
||||||
func (s *CommitsService) GetMergeRequestsByCommit(pid interface{}, sha string, options ...OptionFunc) ([]*MergeRequest, *Response, error) {
|
func (s *CommitsService) GetMergeRequestsByCommit(pid interface{}, sha string, options ...RequestOptionFunc) ([]*MergeRequest, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -503,7 +504,7 @@ type CherryPickCommitOptions struct {
|
||||||
// CherryPickCommit cherry picks a commit to a given branch.
|
// CherryPickCommit cherry picks a commit to a given branch.
|
||||||
//
|
//
|
||||||
// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html#cherry-pick-a-commit
|
// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html#cherry-pick-a-commit
|
||||||
func (s *CommitsService) CherryPickCommit(pid interface{}, sha string, opt *CherryPickCommitOptions, options ...OptionFunc) (*Commit, *Response, error) {
|
func (s *CommitsService) CherryPickCommit(pid interface{}, sha string, opt *CherryPickCommitOptions, options ...RequestOptionFunc) (*Commit, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -534,7 +535,7 @@ type RevertCommitOptions struct {
|
||||||
// RevertCommit reverts a commit in a given branch.
|
// RevertCommit reverts a commit in a given branch.
|
||||||
//
|
//
|
||||||
// GitLab API docs: https://docs.gitlab.com/ee/api/commits.html#revert-a-commit
|
// GitLab API docs: https://docs.gitlab.com/ee/api/commits.html#revert-a-commit
|
||||||
func (s *CommitsService) RevertCommit(pid interface{}, sha string, opt *RevertCommitOptions, options ...OptionFunc) (*Commit, *Response, error) {
|
func (s *CommitsService) RevertCommit(pid interface{}, sha string, opt *RevertCommitOptions, options ...RequestOptionFunc) (*Commit, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -571,7 +572,7 @@ type GPGSignature struct {
|
||||||
// GetGPGSiganature gets a GPG signature of a commit.
|
// GetGPGSiganature gets a GPG signature of a commit.
|
||||||
//
|
//
|
||||||
// GitLab API docs: https://docs.gitlab.com/ee/api/commits.html#get-gpg-signature-of-a-commit
|
// GitLab API docs: https://docs.gitlab.com/ee/api/commits.html#get-gpg-signature-of-a-commit
|
||||||
func (s *CommitsService) GetGPGSiganature(pid interface{}, sha string, options ...OptionFunc) (*GPGSignature, *Response, error) {
|
func (s *CommitsService) GetGPGSiganature(pid interface{}, sha string, options ...RequestOptionFunc) (*GPGSignature, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
|
|
@ -24,7 +24,7 @@ type CustomAttribute struct {
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/custom_attributes.html#list-custom-attributes
|
// https://docs.gitlab.com/ce/api/custom_attributes.html#list-custom-attributes
|
||||||
func (s *CustomAttributesService) ListCustomUserAttributes(user int, options ...OptionFunc) ([]*CustomAttribute, *Response, error) {
|
func (s *CustomAttributesService) ListCustomUserAttributes(user int, options ...RequestOptionFunc) ([]*CustomAttribute, *Response, error) {
|
||||||
return s.listCustomAttributes("users", user, options...)
|
return s.listCustomAttributes("users", user, options...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@ func (s *CustomAttributesService) ListCustomUserAttributes(user int, options ...
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/custom_attributes.html#list-custom-attributes
|
// https://docs.gitlab.com/ce/api/custom_attributes.html#list-custom-attributes
|
||||||
func (s *CustomAttributesService) ListCustomGroupAttributes(group int, options ...OptionFunc) ([]*CustomAttribute, *Response, error) {
|
func (s *CustomAttributesService) ListCustomGroupAttributes(group int, options ...RequestOptionFunc) ([]*CustomAttribute, *Response, error) {
|
||||||
return s.listCustomAttributes("groups", group, options...)
|
return s.listCustomAttributes("groups", group, options...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,11 +40,11 @@ func (s *CustomAttributesService) ListCustomGroupAttributes(group int, options .
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/custom_attributes.html#list-custom-attributes
|
// https://docs.gitlab.com/ce/api/custom_attributes.html#list-custom-attributes
|
||||||
func (s *CustomAttributesService) ListCustomProjectAttributes(project int, options ...OptionFunc) ([]*CustomAttribute, *Response, error) {
|
func (s *CustomAttributesService) ListCustomProjectAttributes(project int, options ...RequestOptionFunc) ([]*CustomAttribute, *Response, error) {
|
||||||
return s.listCustomAttributes("projects", project, options...)
|
return s.listCustomAttributes("projects", project, options...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *CustomAttributesService) listCustomAttributes(resource string, id int, options ...OptionFunc) ([]*CustomAttribute, *Response, error) {
|
func (s *CustomAttributesService) listCustomAttributes(resource string, id int, options ...RequestOptionFunc) ([]*CustomAttribute, *Response, error) {
|
||||||
u := fmt.Sprintf("%s/%d/custom_attributes", resource, id)
|
u := fmt.Sprintf("%s/%d/custom_attributes", resource, id)
|
||||||
req, err := s.client.NewRequest("GET", u, nil, options)
|
req, err := s.client.NewRequest("GET", u, nil, options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -63,7 +63,7 @@ func (s *CustomAttributesService) listCustomAttributes(resource string, id int,
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/custom_attributes.html#single-custom-attribute
|
// https://docs.gitlab.com/ce/api/custom_attributes.html#single-custom-attribute
|
||||||
func (s *CustomAttributesService) GetCustomUserAttribute(user int, key string, options ...OptionFunc) (*CustomAttribute, *Response, error) {
|
func (s *CustomAttributesService) GetCustomUserAttribute(user int, key string, options ...RequestOptionFunc) (*CustomAttribute, *Response, error) {
|
||||||
return s.getCustomAttribute("users", user, key, options...)
|
return s.getCustomAttribute("users", user, key, options...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,7 +71,7 @@ func (s *CustomAttributesService) GetCustomUserAttribute(user int, key string, o
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/custom_attributes.html#single-custom-attribute
|
// https://docs.gitlab.com/ce/api/custom_attributes.html#single-custom-attribute
|
||||||
func (s *CustomAttributesService) GetCustomGroupAttribute(group int, key string, options ...OptionFunc) (*CustomAttribute, *Response, error) {
|
func (s *CustomAttributesService) GetCustomGroupAttribute(group int, key string, options ...RequestOptionFunc) (*CustomAttribute, *Response, error) {
|
||||||
return s.getCustomAttribute("groups", group, key, options...)
|
return s.getCustomAttribute("groups", group, key, options...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,11 +79,11 @@ func (s *CustomAttributesService) GetCustomGroupAttribute(group int, key string,
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/custom_attributes.html#single-custom-attribute
|
// https://docs.gitlab.com/ce/api/custom_attributes.html#single-custom-attribute
|
||||||
func (s *CustomAttributesService) GetCustomProjectAttribute(project int, key string, options ...OptionFunc) (*CustomAttribute, *Response, error) {
|
func (s *CustomAttributesService) GetCustomProjectAttribute(project int, key string, options ...RequestOptionFunc) (*CustomAttribute, *Response, error) {
|
||||||
return s.getCustomAttribute("projects", project, key, options...)
|
return s.getCustomAttribute("projects", project, key, options...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *CustomAttributesService) getCustomAttribute(resource string, id int, key string, options ...OptionFunc) (*CustomAttribute, *Response, error) {
|
func (s *CustomAttributesService) getCustomAttribute(resource string, id int, key string, options ...RequestOptionFunc) (*CustomAttribute, *Response, error) {
|
||||||
u := fmt.Sprintf("%s/%d/custom_attributes/%s", resource, id, key)
|
u := fmt.Sprintf("%s/%d/custom_attributes/%s", resource, id, key)
|
||||||
req, err := s.client.NewRequest("GET", u, nil, options)
|
req, err := s.client.NewRequest("GET", u, nil, options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -102,7 +102,7 @@ func (s *CustomAttributesService) getCustomAttribute(resource string, id int, ke
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/custom_attributes.html#set-custom-attribute
|
// https://docs.gitlab.com/ce/api/custom_attributes.html#set-custom-attribute
|
||||||
func (s *CustomAttributesService) SetCustomUserAttribute(user int, c CustomAttribute, options ...OptionFunc) (*CustomAttribute, *Response, error) {
|
func (s *CustomAttributesService) SetCustomUserAttribute(user int, c CustomAttribute, options ...RequestOptionFunc) (*CustomAttribute, *Response, error) {
|
||||||
return s.setCustomAttribute("users", user, c, options...)
|
return s.setCustomAttribute("users", user, c, options...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -110,7 +110,7 @@ func (s *CustomAttributesService) SetCustomUserAttribute(user int, c CustomAttri
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/custom_attributes.html#set-custom-attribute
|
// https://docs.gitlab.com/ce/api/custom_attributes.html#set-custom-attribute
|
||||||
func (s *CustomAttributesService) SetCustomGroupAttribute(group int, c CustomAttribute, options ...OptionFunc) (*CustomAttribute, *Response, error) {
|
func (s *CustomAttributesService) SetCustomGroupAttribute(group int, c CustomAttribute, options ...RequestOptionFunc) (*CustomAttribute, *Response, error) {
|
||||||
return s.setCustomAttribute("groups", group, c, options...)
|
return s.setCustomAttribute("groups", group, c, options...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -118,11 +118,11 @@ func (s *CustomAttributesService) SetCustomGroupAttribute(group int, c CustomAtt
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/custom_attributes.html#set-custom-attribute
|
// https://docs.gitlab.com/ce/api/custom_attributes.html#set-custom-attribute
|
||||||
func (s *CustomAttributesService) SetCustomProjectAttribute(project int, c CustomAttribute, options ...OptionFunc) (*CustomAttribute, *Response, error) {
|
func (s *CustomAttributesService) SetCustomProjectAttribute(project int, c CustomAttribute, options ...RequestOptionFunc) (*CustomAttribute, *Response, error) {
|
||||||
return s.setCustomAttribute("projects", project, c, options...)
|
return s.setCustomAttribute("projects", project, c, options...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *CustomAttributesService) setCustomAttribute(resource string, id int, c CustomAttribute, options ...OptionFunc) (*CustomAttribute, *Response, error) {
|
func (s *CustomAttributesService) setCustomAttribute(resource string, id int, c CustomAttribute, options ...RequestOptionFunc) (*CustomAttribute, *Response, error) {
|
||||||
u := fmt.Sprintf("%s/%d/custom_attributes/%s", resource, id, c.Key)
|
u := fmt.Sprintf("%s/%d/custom_attributes/%s", resource, id, c.Key)
|
||||||
req, err := s.client.NewRequest("PUT", u, c, options)
|
req, err := s.client.NewRequest("PUT", u, c, options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -141,7 +141,7 @@ func (s *CustomAttributesService) setCustomAttribute(resource string, id int, c
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/custom_attributes.html#delete-custom-attribute
|
// https://docs.gitlab.com/ce/api/custom_attributes.html#delete-custom-attribute
|
||||||
func (s *CustomAttributesService) DeleteCustomUserAttribute(user int, key string, options ...OptionFunc) (*Response, error) {
|
func (s *CustomAttributesService) DeleteCustomUserAttribute(user int, key string, options ...RequestOptionFunc) (*Response, error) {
|
||||||
return s.deleteCustomAttribute("users", user, key, options...)
|
return s.deleteCustomAttribute("users", user, key, options...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -149,7 +149,7 @@ func (s *CustomAttributesService) DeleteCustomUserAttribute(user int, key string
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/custom_attributes.html#delete-custom-attribute
|
// https://docs.gitlab.com/ce/api/custom_attributes.html#delete-custom-attribute
|
||||||
func (s *CustomAttributesService) DeleteCustomGroupAttribute(group int, key string, options ...OptionFunc) (*Response, error) {
|
func (s *CustomAttributesService) DeleteCustomGroupAttribute(group int, key string, options ...RequestOptionFunc) (*Response, error) {
|
||||||
return s.deleteCustomAttribute("groups", group, key, options...)
|
return s.deleteCustomAttribute("groups", group, key, options...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -157,11 +157,11 @@ func (s *CustomAttributesService) DeleteCustomGroupAttribute(group int, key stri
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/custom_attributes.html#delete-custom-attribute
|
// https://docs.gitlab.com/ce/api/custom_attributes.html#delete-custom-attribute
|
||||||
func (s *CustomAttributesService) DeleteCustomProjectAttribute(project int, key string, options ...OptionFunc) (*Response, error) {
|
func (s *CustomAttributesService) DeleteCustomProjectAttribute(project int, key string, options ...RequestOptionFunc) (*Response, error) {
|
||||||
return s.deleteCustomAttribute("projects", project, key, options...)
|
return s.deleteCustomAttribute("projects", project, key, options...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *CustomAttributesService) deleteCustomAttribute(resource string, id int, key string, options ...OptionFunc) (*Response, error) {
|
func (s *CustomAttributesService) deleteCustomAttribute(resource string, id int, key string, options ...RequestOptionFunc) (*Response, error) {
|
||||||
u := fmt.Sprintf("%s/%d/custom_attributes/%s", resource, id, key)
|
u := fmt.Sprintf("%s/%d/custom_attributes/%s", resource, id, key)
|
||||||
req, err := s.client.NewRequest("DELETE", u, nil, options)
|
req, err := s.client.NewRequest("DELETE", u, nil, options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -46,7 +46,7 @@ func (k DeployKey) String() string {
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/deploy_keys.html#list-all-deploy-keys
|
// https://docs.gitlab.com/ce/api/deploy_keys.html#list-all-deploy-keys
|
||||||
func (s *DeployKeysService) ListAllDeployKeys(options ...OptionFunc) ([]*DeployKey, *Response, error) {
|
func (s *DeployKeysService) ListAllDeployKeys(options ...RequestOptionFunc) ([]*DeployKey, *Response, error) {
|
||||||
req, err := s.client.NewRequest("GET", "deploy_keys", nil, options)
|
req, err := s.client.NewRequest("GET", "deploy_keys", nil, options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -72,7 +72,7 @@ type ListProjectDeployKeysOptions ListOptions
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/deploy_keys.html#list-project-deploy-keys
|
// https://docs.gitlab.com/ce/api/deploy_keys.html#list-project-deploy-keys
|
||||||
func (s *DeployKeysService) ListProjectDeployKeys(pid interface{}, opt *ListProjectDeployKeysOptions, options ...OptionFunc) ([]*DeployKey, *Response, error) {
|
func (s *DeployKeysService) ListProjectDeployKeys(pid interface{}, opt *ListProjectDeployKeysOptions, options ...RequestOptionFunc) ([]*DeployKey, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -97,7 +97,7 @@ func (s *DeployKeysService) ListProjectDeployKeys(pid interface{}, opt *ListProj
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/deploy_keys.html#single-deploy-key
|
// https://docs.gitlab.com/ce/api/deploy_keys.html#single-deploy-key
|
||||||
func (s *DeployKeysService) GetDeployKey(pid interface{}, deployKey int, options ...OptionFunc) (*DeployKey, *Response, error) {
|
func (s *DeployKeysService) GetDeployKey(pid interface{}, deployKey int, options ...RequestOptionFunc) (*DeployKey, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -134,7 +134,7 @@ type AddDeployKeyOptions struct {
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/deploy_keys.html#add-deploy-key
|
// https://docs.gitlab.com/ce/api/deploy_keys.html#add-deploy-key
|
||||||
func (s *DeployKeysService) AddDeployKey(pid interface{}, opt *AddDeployKeyOptions, options ...OptionFunc) (*DeployKey, *Response, error) {
|
func (s *DeployKeysService) AddDeployKey(pid interface{}, opt *AddDeployKeyOptions, options ...RequestOptionFunc) (*DeployKey, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -159,7 +159,7 @@ func (s *DeployKeysService) AddDeployKey(pid interface{}, opt *AddDeployKeyOptio
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/deploy_keys.html#delete-deploy-key
|
// https://docs.gitlab.com/ce/api/deploy_keys.html#delete-deploy-key
|
||||||
func (s *DeployKeysService) DeleteDeployKey(pid interface{}, deployKey int, options ...OptionFunc) (*Response, error) {
|
func (s *DeployKeysService) DeleteDeployKey(pid interface{}, deployKey int, options ...RequestOptionFunc) (*Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -178,7 +178,7 @@ func (s *DeployKeysService) DeleteDeployKey(pid interface{}, deployKey int, opti
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/deploy_keys.html#enable-deploy-key
|
// https://docs.gitlab.com/ce/api/deploy_keys.html#enable-deploy-key
|
||||||
func (s *DeployKeysService) EnableDeployKey(pid interface{}, deployKey int, options ...OptionFunc) (*DeployKey, *Response, error) {
|
func (s *DeployKeysService) EnableDeployKey(pid interface{}, deployKey int, options ...RequestOptionFunc) (*DeployKey, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
|
|
@ -0,0 +1,221 @@
|
||||||
|
package gitlab
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DeployTokensService handles communication with the deploy tokens related methods
|
||||||
|
// of the GitLab API.
|
||||||
|
//
|
||||||
|
// GitLab API docs: https://docs.gitlab.com/ce/api/deploy_tokens.html
|
||||||
|
type DeployTokensService struct {
|
||||||
|
client *Client
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeployToken represents a GitLab deploy token.
|
||||||
|
type DeployToken struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Username string `json:"username"`
|
||||||
|
ExpiresAt *time.Time `json:"expires_at"`
|
||||||
|
Token string `json:"token,omitempty"`
|
||||||
|
Scopes []string `json:"scopes"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k DeployToken) String() string {
|
||||||
|
return Stringify(k)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListAllDeployTokens gets a list of all deploy tokens.
|
||||||
|
//
|
||||||
|
// GitLab API docs:
|
||||||
|
// https://docs.gitlab.com/ce/api/deploy_tokens.html#list-all-deploy-tokens
|
||||||
|
func (s *DeployTokensService) ListAllDeployTokens(options ...RequestOptionFunc) ([]*DeployToken, *Response, error) {
|
||||||
|
req, err := s.client.NewRequest("GET", "deploy_tokens", nil, options)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var ts []*DeployToken
|
||||||
|
resp, err := s.client.Do(req, &ts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return ts, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListProjectDeployTokensOptions represents the available ListProjectDeployTokens()
|
||||||
|
// options.
|
||||||
|
//
|
||||||
|
// GitLab API docs:
|
||||||
|
// https://docs.gitlab.com/ce/api/deploy_tokens.html#list-project-deploy-tokens
|
||||||
|
type ListProjectDeployTokensOptions ListOptions
|
||||||
|
|
||||||
|
// ListProjectDeployTokens gets a list of a project's deploy tokens.
|
||||||
|
//
|
||||||
|
// GitLab API docs:
|
||||||
|
// https://docs.gitlab.com/ce/api/deploy_tokens.html#list-project-deploy-tokens
|
||||||
|
func (s *DeployTokensService) ListProjectDeployTokens(pid interface{}, opt *ListProjectDeployTokensOptions, options ...RequestOptionFunc) ([]*DeployToken, *Response, error) {
|
||||||
|
project, err := parseID(pid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
u := fmt.Sprintf("projects/%s/deploy_tokens", pathEscape(project))
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest("GET", u, opt, options)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var ts []*DeployToken
|
||||||
|
resp, err := s.client.Do(req, &ts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return ts, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateProjectDeployTokenOptions represents the available CreateProjectDeployToken() options.
|
||||||
|
//
|
||||||
|
// GitLab API docs:
|
||||||
|
// https://docs.gitlab.com/ce/api/deploy_tokens.html#create-a-project-deploy-token
|
||||||
|
type CreateProjectDeployTokenOptions struct {
|
||||||
|
Name *string `url:"name,omitempty" json:"name,omitempty"`
|
||||||
|
ExpiresAt *time.Time `url:"expires_at,omitempty" json:"expires_at,omitempty"`
|
||||||
|
Username *string `url:"username,omitempty" json:"username,omitempty"`
|
||||||
|
Scopes []string `url:"scopes,omitempty" json:"scopes,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateProjectDeployToken creates a new deploy token for a project.
|
||||||
|
//
|
||||||
|
// GitLab API docs:
|
||||||
|
// https://docs.gitlab.com/ce/api/deploy_tokens.html#create-a-project-deploy-token
|
||||||
|
func (s *DeployTokensService) CreateProjectDeployToken(pid interface{}, opt *CreateProjectDeployTokenOptions, options ...RequestOptionFunc) (*DeployToken, *Response, error) {
|
||||||
|
project, err := parseID(pid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
u := fmt.Sprintf("projects/%s/deploy_tokens", pathEscape(project))
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest("POST", u, opt, options)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
t := new(DeployToken)
|
||||||
|
resp, err := s.client.Do(req, t)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return t, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteProjectDeployToken removes a deploy token from the project.
|
||||||
|
//
|
||||||
|
// GitLab API docs:
|
||||||
|
// https://docs.gitlab.com/ce/api/deploy_tokens.html#delete-a-project-deploy-token
|
||||||
|
func (s *DeployTokensService) DeleteProjectDeployToken(pid interface{}, deployToken int, options ...RequestOptionFunc) (*Response, error) {
|
||||||
|
project, err := parseID(pid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
u := fmt.Sprintf("projects/%s/deploy_tokens/%d", pathEscape(project), deployToken)
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest("DELETE", u, nil, options)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.client.Do(req, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListGroupDeployTokensOptions represents the available ListGroupDeployTokens()
|
||||||
|
// options.
|
||||||
|
//
|
||||||
|
// GitLab API docs:
|
||||||
|
// https://docs.gitlab.com/ce/api/deploy_tokens.html#list-group-deploy-deploy-tokens
|
||||||
|
type ListGroupDeployTokensOptions ListOptions
|
||||||
|
|
||||||
|
// ListGroupDeployTokens gets a list of a group’s deploy tokens.
|
||||||
|
//
|
||||||
|
// GitLab API docs:
|
||||||
|
// https://docs.gitlab.com/ce/api/deploy_tokens.html#list-project-deploy-tokens
|
||||||
|
func (s *DeployTokensService) ListGroupDeployTokens(gid interface{}, opt *ListGroupDeployTokensOptions, options ...RequestOptionFunc) ([]*DeployToken, *Response, error) {
|
||||||
|
group, err := parseID(gid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
u := fmt.Sprintf("groups/%s/deploy_tokens", pathEscape(group))
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest("GET", u, opt, options)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var ts []*DeployToken
|
||||||
|
resp, err := s.client.Do(req, &ts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return ts, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateGroupDeployTokenOptions represents the available CreateGroupDeployToken() options.
|
||||||
|
//
|
||||||
|
// GitLab API docs:
|
||||||
|
// https://docs.gitlab.com/ce/api/deploy_tokens.html#create-a-group-deploy-token
|
||||||
|
type CreateGroupDeployTokenOptions struct {
|
||||||
|
Name *string `url:"name,omitempty" json:"name,omitempty"`
|
||||||
|
ExpiresAt *time.Time `url:"expires_at,omitempty" json:"expires_at,omitempty"`
|
||||||
|
Username *string `url:"username,omitempty" json:"username,omitempty"`
|
||||||
|
Scopes []string `url:"scopes,omitempty" json:"scopes,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateGroupDeployToken creates a new deploy token for a group.
|
||||||
|
//
|
||||||
|
// GitLab API docs:
|
||||||
|
// https://docs.gitlab.com/ce/api/deploy_tokens.html#create-a-group-deploy-token
|
||||||
|
func (s *DeployTokensService) CreateGroupDeployToken(gid interface{}, opt *CreateGroupDeployTokenOptions, options ...RequestOptionFunc) (*DeployToken, *Response, error) {
|
||||||
|
group, err := parseID(gid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
u := fmt.Sprintf("groups/%s/deploy_tokens", pathEscape(group))
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest("POST", u, opt, options)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
t := new(DeployToken)
|
||||||
|
resp, err := s.client.Do(req, t)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return t, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteGroupDeployToken removes a deploy token from the group.
|
||||||
|
//
|
||||||
|
// GitLab API docs:
|
||||||
|
// https://docs.gitlab.com/ce/api/deploy_tokens.html#delete-a-group-deploy-token
|
||||||
|
func (s *DeployTokensService) DeleteGroupDeployToken(gid interface{}, deployToken int, options ...RequestOptionFunc) (*Response, error) {
|
||||||
|
group, err := parseID(gid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
u := fmt.Sprintf("groups/%s/deploy_tokens/%d", pathEscape(group), deployToken)
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest("DELETE", u, nil, options)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.client.Do(req, nil)
|
||||||
|
}
|
|
@ -69,12 +69,16 @@ type ListProjectDeploymentsOptions struct {
|
||||||
ListOptions
|
ListOptions
|
||||||
OrderBy *string `url:"order_by,omitempty" json:"order_by,omitempty"`
|
OrderBy *string `url:"order_by,omitempty" json:"order_by,omitempty"`
|
||||||
Sort *string `url:"sort,omitempty" json:"sort,omitempty"`
|
Sort *string `url:"sort,omitempty" json:"sort,omitempty"`
|
||||||
|
UpdatedAfter *time.Time `url:"updated_after,omitempty" json:"updated_after,omitempty"`
|
||||||
|
UpdatedBefore *time.Time `url:"update_before,omitempty" json:"updated_before,omitempty"`
|
||||||
|
Environment *string `url:"environment,omitempty" json:"environment,omitempty"`
|
||||||
|
Status *string `url:"status,omitempty" json:"status,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListProjectDeployments gets a list of deployments in a project.
|
// ListProjectDeployments gets a list of deployments in a project.
|
||||||
//
|
//
|
||||||
// GitLab API docs: https://docs.gitlab.com/ce/api/deployments.html#list-project-deployments
|
// GitLab API docs: https://docs.gitlab.com/ce/api/deployments.html#list-project-deployments
|
||||||
func (s *DeploymentsService) ListProjectDeployments(pid interface{}, opts *ListProjectDeploymentsOptions, options ...OptionFunc) ([]*Deployment, *Response, error) {
|
func (s *DeploymentsService) ListProjectDeployments(pid interface{}, opts *ListProjectDeploymentsOptions, options ...RequestOptionFunc) ([]*Deployment, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -98,7 +102,7 @@ func (s *DeploymentsService) ListProjectDeployments(pid interface{}, opts *ListP
|
||||||
// GetProjectDeployment get a deployment for a project.
|
// GetProjectDeployment get a deployment for a project.
|
||||||
//
|
//
|
||||||
// GitLab API docs: https://docs.gitlab.com/ce/api/deployments.html#get-a-specific-deployment
|
// GitLab API docs: https://docs.gitlab.com/ce/api/deployments.html#get-a-specific-deployment
|
||||||
func (s *DeploymentsService) GetProjectDeployment(pid interface{}, deployment int, options ...OptionFunc) (*Deployment, *Response, error) {
|
func (s *DeploymentsService) GetProjectDeployment(pid interface{}, deployment int, options ...RequestOptionFunc) (*Deployment, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -118,3 +122,71 @@ func (s *DeploymentsService) GetProjectDeployment(pid interface{}, deployment in
|
||||||
|
|
||||||
return d, resp, err
|
return d, resp, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CreateProjectDeploymentOptions represents the available
|
||||||
|
// CreateProjectDeployment() options.
|
||||||
|
//
|
||||||
|
// GitLab API docs: https://docs.gitlab.com/ee/api/deployments.html#create-a-deployment
|
||||||
|
type CreateProjectDeploymentOptions struct {
|
||||||
|
Environment *string `url:"environment,omitempty" json:"environment,omitempty"`
|
||||||
|
Ref *string `url:"ref,omitempty" json:"ref,omitempty"`
|
||||||
|
SHA *string `url:"sha,omitempty" json:"sha,omitempty"`
|
||||||
|
Tag *bool `url:"tag,omitempty" json:"tag,omitempty"`
|
||||||
|
Status *DeploymentStatusValue `url:"status,omitempty" json:"status,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateProjectDeployment creates a project deployment.
|
||||||
|
//
|
||||||
|
// GitLab API docs: https://docs.gitlab.com/ee/api/deployments.html#create-a-deployment
|
||||||
|
func (s *DeploymentsService) CreateProjectDeployment(pid interface{}, opt *CreateProjectDeploymentOptions, options ...RequestOptionFunc) (*Deployment, *Response, error) {
|
||||||
|
project, err := parseID(pid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
u := fmt.Sprintf("projects/%s/deployments", pathEscape(project))
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest("POST", u, opt, options)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
d := new(Deployment)
|
||||||
|
resp, err := s.client.Do(req, &d)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return d, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateProjectDeploymentOptions represents the available
|
||||||
|
// UpdateProjectDeployment() options.
|
||||||
|
//
|
||||||
|
// GitLab API docs: https://docs.gitlab.com/ee/api/deployments.html#updating-a-deployment
|
||||||
|
type UpdateProjectDeploymentOptions struct {
|
||||||
|
Status *DeploymentStatusValue `url:"status,omitempty" json:"status,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateProjectDeployment updates a project deployment.
|
||||||
|
//
|
||||||
|
// GitLab API docs: https://docs.gitlab.com/ee/api/deployments.html#updating-a-deployment
|
||||||
|
func (s *DeploymentsService) UpdateProjectDeployment(pid interface{}, deployment int, opt *UpdateProjectDeploymentOptions, options ...RequestOptionFunc) (*Deployment, *Response, error) {
|
||||||
|
project, err := parseID(pid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
u := fmt.Sprintf("projects/%s/deployments/%d", pathEscape(project), deployment)
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest("PUT", u, opt, options)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
d := new(Deployment)
|
||||||
|
resp, err := s.client.Do(req, &d)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return d, resp, err
|
||||||
|
}
|
||||||
|
|
|
@ -54,7 +54,7 @@ type ListIssueDiscussionsOptions ListOptions
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/discussions.html#list-project-issue-discussion-items
|
// https://docs.gitlab.com/ce/api/discussions.html#list-project-issue-discussion-items
|
||||||
func (s *DiscussionsService) ListIssueDiscussions(pid interface{}, issue int, opt *ListIssueDiscussionsOptions, options ...OptionFunc) ([]*Discussion, *Response, error) {
|
func (s *DiscussionsService) ListIssueDiscussions(pid interface{}, issue int, opt *ListIssueDiscussionsOptions, options ...RequestOptionFunc) ([]*Discussion, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -79,7 +79,7 @@ func (s *DiscussionsService) ListIssueDiscussions(pid interface{}, issue int, op
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/discussions.html#get-single-issue-discussion-item
|
// https://docs.gitlab.com/ce/api/discussions.html#get-single-issue-discussion-item
|
||||||
func (s *DiscussionsService) GetIssueDiscussion(pid interface{}, issue int, discussion string, options ...OptionFunc) (*Discussion, *Response, error) {
|
func (s *DiscussionsService) GetIssueDiscussion(pid interface{}, issue int, discussion string, options ...RequestOptionFunc) (*Discussion, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -118,7 +118,7 @@ type CreateIssueDiscussionOptions struct {
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/discussions.html#create-new-issue-thread
|
// https://docs.gitlab.com/ce/api/discussions.html#create-new-issue-thread
|
||||||
func (s *DiscussionsService) CreateIssueDiscussion(pid interface{}, issue int, opt *CreateIssueDiscussionOptions, options ...OptionFunc) (*Discussion, *Response, error) {
|
func (s *DiscussionsService) CreateIssueDiscussion(pid interface{}, issue int, opt *CreateIssueDiscussionOptions, options ...RequestOptionFunc) (*Discussion, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -153,7 +153,7 @@ type AddIssueDiscussionNoteOptions struct {
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/discussions.html#add-note-to-existing-issue-thread
|
// https://docs.gitlab.com/ce/api/discussions.html#add-note-to-existing-issue-thread
|
||||||
func (s *DiscussionsService) AddIssueDiscussionNote(pid interface{}, issue int, discussion string, opt *AddIssueDiscussionNoteOptions, options ...OptionFunc) (*Note, *Response, error) {
|
func (s *DiscussionsService) AddIssueDiscussionNote(pid interface{}, issue int, discussion string, opt *AddIssueDiscussionNoteOptions, options ...RequestOptionFunc) (*Note, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -192,7 +192,7 @@ type UpdateIssueDiscussionNoteOptions struct {
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/discussions.html#modify-existing-issue-thread-note
|
// https://docs.gitlab.com/ce/api/discussions.html#modify-existing-issue-thread-note
|
||||||
func (s *DiscussionsService) UpdateIssueDiscussionNote(pid interface{}, issue int, discussion string, note int, opt *UpdateIssueDiscussionNoteOptions, options ...OptionFunc) (*Note, *Response, error) {
|
func (s *DiscussionsService) UpdateIssueDiscussionNote(pid interface{}, issue int, discussion string, note int, opt *UpdateIssueDiscussionNoteOptions, options ...RequestOptionFunc) (*Note, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -222,7 +222,7 @@ func (s *DiscussionsService) UpdateIssueDiscussionNote(pid interface{}, issue in
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/discussions.html#delete-an-issue-thread-note
|
// https://docs.gitlab.com/ce/api/discussions.html#delete-an-issue-thread-note
|
||||||
func (s *DiscussionsService) DeleteIssueDiscussionNote(pid interface{}, issue int, discussion string, note int, options ...OptionFunc) (*Response, error) {
|
func (s *DiscussionsService) DeleteIssueDiscussionNote(pid interface{}, issue int, discussion string, note int, options ...RequestOptionFunc) (*Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -254,7 +254,7 @@ type ListSnippetDiscussionsOptions ListOptions
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/discussions.html#list-project-snippet-discussion-items
|
// https://docs.gitlab.com/ce/api/discussions.html#list-project-snippet-discussion-items
|
||||||
func (s *DiscussionsService) ListSnippetDiscussions(pid interface{}, snippet int, opt *ListSnippetDiscussionsOptions, options ...OptionFunc) ([]*Discussion, *Response, error) {
|
func (s *DiscussionsService) ListSnippetDiscussions(pid interface{}, snippet int, opt *ListSnippetDiscussionsOptions, options ...RequestOptionFunc) ([]*Discussion, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -279,7 +279,7 @@ func (s *DiscussionsService) ListSnippetDiscussions(pid interface{}, snippet int
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/discussions.html#get-single-snippet-discussion-item
|
// https://docs.gitlab.com/ce/api/discussions.html#get-single-snippet-discussion-item
|
||||||
func (s *DiscussionsService) GetSnippetDiscussion(pid interface{}, snippet int, discussion string, options ...OptionFunc) (*Discussion, *Response, error) {
|
func (s *DiscussionsService) GetSnippetDiscussion(pid interface{}, snippet int, discussion string, options ...RequestOptionFunc) (*Discussion, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -319,7 +319,7 @@ type CreateSnippetDiscussionOptions struct {
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/discussions.html#create-new-snippet-thread
|
// https://docs.gitlab.com/ce/api/discussions.html#create-new-snippet-thread
|
||||||
func (s *DiscussionsService) CreateSnippetDiscussion(pid interface{}, snippet int, opt *CreateSnippetDiscussionOptions, options ...OptionFunc) (*Discussion, *Response, error) {
|
func (s *DiscussionsService) CreateSnippetDiscussion(pid interface{}, snippet int, opt *CreateSnippetDiscussionOptions, options ...RequestOptionFunc) (*Discussion, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -355,7 +355,7 @@ type AddSnippetDiscussionNoteOptions struct {
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/discussions.html#add-note-to-existing-snippet-thread
|
// https://docs.gitlab.com/ce/api/discussions.html#add-note-to-existing-snippet-thread
|
||||||
func (s *DiscussionsService) AddSnippetDiscussionNote(pid interface{}, snippet int, discussion string, opt *AddSnippetDiscussionNoteOptions, options ...OptionFunc) (*Note, *Response, error) {
|
func (s *DiscussionsService) AddSnippetDiscussionNote(pid interface{}, snippet int, discussion string, opt *AddSnippetDiscussionNoteOptions, options ...RequestOptionFunc) (*Note, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -394,7 +394,7 @@ type UpdateSnippetDiscussionNoteOptions struct {
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/discussions.html#modify-existing-snippet-thread-note
|
// https://docs.gitlab.com/ce/api/discussions.html#modify-existing-snippet-thread-note
|
||||||
func (s *DiscussionsService) UpdateSnippetDiscussionNote(pid interface{}, snippet int, discussion string, note int, opt *UpdateSnippetDiscussionNoteOptions, options ...OptionFunc) (*Note, *Response, error) {
|
func (s *DiscussionsService) UpdateSnippetDiscussionNote(pid interface{}, snippet int, discussion string, note int, opt *UpdateSnippetDiscussionNoteOptions, options ...RequestOptionFunc) (*Note, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -424,7 +424,7 @@ func (s *DiscussionsService) UpdateSnippetDiscussionNote(pid interface{}, snippe
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/discussions.html#delete-a-snippet-thread-note
|
// https://docs.gitlab.com/ce/api/discussions.html#delete-a-snippet-thread-note
|
||||||
func (s *DiscussionsService) DeleteSnippetDiscussionNote(pid interface{}, snippet int, discussion string, note int, options ...OptionFunc) (*Response, error) {
|
func (s *DiscussionsService) DeleteSnippetDiscussionNote(pid interface{}, snippet int, discussion string, note int, options ...RequestOptionFunc) (*Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -456,7 +456,7 @@ type ListGroupEpicDiscussionsOptions ListOptions
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/discussions.html#list-group-epic-discussion-items
|
// https://docs.gitlab.com/ce/api/discussions.html#list-group-epic-discussion-items
|
||||||
func (s *DiscussionsService) ListGroupEpicDiscussions(gid interface{}, epic int, opt *ListGroupEpicDiscussionsOptions, options ...OptionFunc) ([]*Discussion, *Response, error) {
|
func (s *DiscussionsService) ListGroupEpicDiscussions(gid interface{}, epic int, opt *ListGroupEpicDiscussionsOptions, options ...RequestOptionFunc) ([]*Discussion, *Response, error) {
|
||||||
group, err := parseID(gid)
|
group, err := parseID(gid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -484,7 +484,7 @@ func (s *DiscussionsService) ListGroupEpicDiscussions(gid interface{}, epic int,
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/discussions.html#get-single-epic-discussion-item
|
// https://docs.gitlab.com/ce/api/discussions.html#get-single-epic-discussion-item
|
||||||
func (s *DiscussionsService) GetEpicDiscussion(gid interface{}, epic int, discussion string, options ...OptionFunc) (*Discussion, *Response, error) {
|
func (s *DiscussionsService) GetEpicDiscussion(gid interface{}, epic int, discussion string, options ...RequestOptionFunc) (*Discussion, *Response, error) {
|
||||||
group, err := parseID(gid)
|
group, err := parseID(gid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -524,7 +524,7 @@ type CreateEpicDiscussionOptions struct {
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/discussions.html#add-note-to-existing-epic-thread
|
// https://docs.gitlab.com/ce/api/discussions.html#add-note-to-existing-epic-thread
|
||||||
func (s *DiscussionsService) CreateEpicDiscussion(gid interface{}, epic int, opt *CreateEpicDiscussionOptions, options ...OptionFunc) (*Discussion, *Response, error) {
|
func (s *DiscussionsService) CreateEpicDiscussion(gid interface{}, epic int, opt *CreateEpicDiscussionOptions, options ...RequestOptionFunc) (*Discussion, *Response, error) {
|
||||||
group, err := parseID(gid)
|
group, err := parseID(gid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -562,7 +562,7 @@ type AddEpicDiscussionNoteOptions struct {
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/discussions.html#add-note-to-existing-epic-thread
|
// https://docs.gitlab.com/ce/api/discussions.html#add-note-to-existing-epic-thread
|
||||||
func (s *DiscussionsService) AddEpicDiscussionNote(gid interface{}, epic int, discussion string, opt *AddEpicDiscussionNoteOptions, options ...OptionFunc) (*Note, *Response, error) {
|
func (s *DiscussionsService) AddEpicDiscussionNote(gid interface{}, epic int, discussion string, opt *AddEpicDiscussionNoteOptions, options ...RequestOptionFunc) (*Note, *Response, error) {
|
||||||
group, err := parseID(gid)
|
group, err := parseID(gid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -601,7 +601,7 @@ type UpdateEpicDiscussionNoteOptions struct {
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/discussions.html#modify-existing-epic-thread-note
|
// https://docs.gitlab.com/ce/api/discussions.html#modify-existing-epic-thread-note
|
||||||
func (s *DiscussionsService) UpdateEpicDiscussionNote(gid interface{}, epic int, discussion string, note int, opt *UpdateEpicDiscussionNoteOptions, options ...OptionFunc) (*Note, *Response, error) {
|
func (s *DiscussionsService) UpdateEpicDiscussionNote(gid interface{}, epic int, discussion string, note int, opt *UpdateEpicDiscussionNoteOptions, options ...RequestOptionFunc) (*Note, *Response, error) {
|
||||||
group, err := parseID(gid)
|
group, err := parseID(gid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -631,7 +631,7 @@ func (s *DiscussionsService) UpdateEpicDiscussionNote(gid interface{}, epic int,
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/discussions.html#delete-an-epic-thread-note
|
// https://docs.gitlab.com/ce/api/discussions.html#delete-an-epic-thread-note
|
||||||
func (s *DiscussionsService) DeleteEpicDiscussionNote(gid interface{}, epic int, discussion string, note int, options ...OptionFunc) (*Response, error) {
|
func (s *DiscussionsService) DeleteEpicDiscussionNote(gid interface{}, epic int, discussion string, note int, options ...RequestOptionFunc) (*Response, error) {
|
||||||
group, err := parseID(gid)
|
group, err := parseID(gid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -663,7 +663,7 @@ type ListMergeRequestDiscussionsOptions ListOptions
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/discussions.html#list-project-merge-request-discussion-items
|
// https://docs.gitlab.com/ce/api/discussions.html#list-project-merge-request-discussion-items
|
||||||
func (s *DiscussionsService) ListMergeRequestDiscussions(pid interface{}, mergeRequest int, opt *ListMergeRequestDiscussionsOptions, options ...OptionFunc) ([]*Discussion, *Response, error) {
|
func (s *DiscussionsService) ListMergeRequestDiscussions(pid interface{}, mergeRequest int, opt *ListMergeRequestDiscussionsOptions, options ...RequestOptionFunc) ([]*Discussion, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -692,7 +692,7 @@ func (s *DiscussionsService) ListMergeRequestDiscussions(pid interface{}, mergeR
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/discussions.html#get-single-merge-request-discussion-item
|
// https://docs.gitlab.com/ce/api/discussions.html#get-single-merge-request-discussion-item
|
||||||
func (s *DiscussionsService) GetMergeRequestDiscussion(pid interface{}, mergeRequest int, discussion string, options ...OptionFunc) (*Discussion, *Response, error) {
|
func (s *DiscussionsService) GetMergeRequestDiscussion(pid interface{}, mergeRequest int, discussion string, options ...RequestOptionFunc) (*Discussion, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -733,7 +733,7 @@ type CreateMergeRequestDiscussionOptions struct {
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/discussions.html#create-new-merge-request-thread
|
// https://docs.gitlab.com/ce/api/discussions.html#create-new-merge-request-thread
|
||||||
func (s *DiscussionsService) CreateMergeRequestDiscussion(pid interface{}, mergeRequest int, opt *CreateMergeRequestDiscussionOptions, options ...OptionFunc) (*Discussion, *Response, error) {
|
func (s *DiscussionsService) CreateMergeRequestDiscussion(pid interface{}, mergeRequest int, opt *CreateMergeRequestDiscussionOptions, options ...RequestOptionFunc) (*Discussion, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -771,7 +771,7 @@ type ResolveMergeRequestDiscussionOptions struct {
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/discussions.html#resolve-a-merge-request-thread
|
// https://docs.gitlab.com/ce/api/discussions.html#resolve-a-merge-request-thread
|
||||||
func (s *DiscussionsService) ResolveMergeRequestDiscussion(pid interface{}, mergeRequest int, discussion string, opt *ResolveMergeRequestDiscussionOptions, options ...OptionFunc) (*Discussion, *Response, error) {
|
func (s *DiscussionsService) ResolveMergeRequestDiscussion(pid interface{}, mergeRequest int, discussion string, opt *ResolveMergeRequestDiscussionOptions, options ...RequestOptionFunc) (*Discussion, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -811,7 +811,7 @@ type AddMergeRequestDiscussionNoteOptions struct {
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/discussions.html#add-note-to-existing-merge-request-discussion
|
// https://docs.gitlab.com/ce/api/discussions.html#add-note-to-existing-merge-request-discussion
|
||||||
func (s *DiscussionsService) AddMergeRequestDiscussionNote(pid interface{}, mergeRequest int, discussion string, opt *AddMergeRequestDiscussionNoteOptions, options ...OptionFunc) (*Note, *Response, error) {
|
func (s *DiscussionsService) AddMergeRequestDiscussionNote(pid interface{}, mergeRequest int, discussion string, opt *AddMergeRequestDiscussionNoteOptions, options ...RequestOptionFunc) (*Note, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -852,7 +852,7 @@ type UpdateMergeRequestDiscussionNoteOptions struct {
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/discussions.html#modify-existing-merge-request-discussion-note
|
// https://docs.gitlab.com/ce/api/discussions.html#modify-existing-merge-request-discussion-note
|
||||||
func (s *DiscussionsService) UpdateMergeRequestDiscussionNote(pid interface{}, mergeRequest int, discussion string, note int, opt *UpdateMergeRequestDiscussionNoteOptions, options ...OptionFunc) (*Note, *Response, error) {
|
func (s *DiscussionsService) UpdateMergeRequestDiscussionNote(pid interface{}, mergeRequest int, discussion string, note int, opt *UpdateMergeRequestDiscussionNoteOptions, options ...RequestOptionFunc) (*Note, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -883,7 +883,7 @@ func (s *DiscussionsService) UpdateMergeRequestDiscussionNote(pid interface{}, m
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/discussions.html#delete-a-merge-request-discussion-note
|
// https://docs.gitlab.com/ce/api/discussions.html#delete-a-merge-request-discussion-note
|
||||||
func (s *DiscussionsService) DeleteMergeRequestDiscussionNote(pid interface{}, mergeRequest int, discussion string, note int, options ...OptionFunc) (*Response, error) {
|
func (s *DiscussionsService) DeleteMergeRequestDiscussionNote(pid interface{}, mergeRequest int, discussion string, note int, options ...RequestOptionFunc) (*Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -915,7 +915,7 @@ type ListCommitDiscussionsOptions ListOptions
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/discussions.html#list-project-commit-discussion-items
|
// https://docs.gitlab.com/ce/api/discussions.html#list-project-commit-discussion-items
|
||||||
func (s *DiscussionsService) ListCommitDiscussions(pid interface{}, commit string, opt *ListCommitDiscussionsOptions, options ...OptionFunc) ([]*Discussion, *Response, error) {
|
func (s *DiscussionsService) ListCommitDiscussions(pid interface{}, commit string, opt *ListCommitDiscussionsOptions, options ...RequestOptionFunc) ([]*Discussion, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -944,7 +944,7 @@ func (s *DiscussionsService) ListCommitDiscussions(pid interface{}, commit strin
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/discussions.html#get-single-commit-discussion-item
|
// https://docs.gitlab.com/ce/api/discussions.html#get-single-commit-discussion-item
|
||||||
func (s *DiscussionsService) GetCommitDiscussion(pid interface{}, commit string, discussion string, options ...OptionFunc) (*Discussion, *Response, error) {
|
func (s *DiscussionsService) GetCommitDiscussion(pid interface{}, commit string, discussion string, options ...RequestOptionFunc) (*Discussion, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -984,7 +984,7 @@ type CreateCommitDiscussionOptions struct {
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/discussions.html#create-new-commit-thread
|
// https://docs.gitlab.com/ce/api/discussions.html#create-new-commit-thread
|
||||||
func (s *DiscussionsService) CreateCommitDiscussion(pid interface{}, commit string, opt *CreateCommitDiscussionOptions, options ...OptionFunc) (*Discussion, *Response, error) {
|
func (s *DiscussionsService) CreateCommitDiscussion(pid interface{}, commit string, opt *CreateCommitDiscussionOptions, options ...RequestOptionFunc) (*Discussion, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -1022,7 +1022,7 @@ type AddCommitDiscussionNoteOptions struct {
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/discussions.html#add-note-to-existing-commit-thread
|
// https://docs.gitlab.com/ce/api/discussions.html#add-note-to-existing-commit-thread
|
||||||
func (s *DiscussionsService) AddCommitDiscussionNote(pid interface{}, commit string, discussion string, opt *AddCommitDiscussionNoteOptions, options ...OptionFunc) (*Note, *Response, error) {
|
func (s *DiscussionsService) AddCommitDiscussionNote(pid interface{}, commit string, discussion string, opt *AddCommitDiscussionNoteOptions, options ...RequestOptionFunc) (*Note, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -1061,7 +1061,7 @@ type UpdateCommitDiscussionNoteOptions struct {
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/discussions.html#modify-an-existing-commit-thread-note
|
// https://docs.gitlab.com/ce/api/discussions.html#modify-an-existing-commit-thread-note
|
||||||
func (s *DiscussionsService) UpdateCommitDiscussionNote(pid interface{}, commit string, discussion string, note int, opt *UpdateCommitDiscussionNoteOptions, options ...OptionFunc) (*Note, *Response, error) {
|
func (s *DiscussionsService) UpdateCommitDiscussionNote(pid interface{}, commit string, discussion string, note int, opt *UpdateCommitDiscussionNoteOptions, options ...RequestOptionFunc) (*Note, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -1091,7 +1091,7 @@ func (s *DiscussionsService) UpdateCommitDiscussionNote(pid interface{}, commit
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/discussions.html#delete-a-commit-thread-note
|
// https://docs.gitlab.com/ce/api/discussions.html#delete-a-commit-thread-note
|
||||||
func (s *DiscussionsService) DeleteCommitDiscussionNote(pid interface{}, commit string, discussion string, note int, options ...OptionFunc) (*Response, error) {
|
func (s *DiscussionsService) DeleteCommitDiscussionNote(pid interface{}, commit string, discussion string, note int, options ...RequestOptionFunc) (*Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -56,7 +56,7 @@ type ListEnvironmentsOptions ListOptions
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ee/api/environments.html#list-environments
|
// https://docs.gitlab.com/ee/api/environments.html#list-environments
|
||||||
func (s *EnvironmentsService) ListEnvironments(pid interface{}, opts *ListEnvironmentsOptions, options ...OptionFunc) ([]*Environment, *Response, error) {
|
func (s *EnvironmentsService) ListEnvironments(pid interface{}, opts *ListEnvironmentsOptions, options ...RequestOptionFunc) ([]*Environment, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -81,7 +81,7 @@ func (s *EnvironmentsService) ListEnvironments(pid interface{}, opts *ListEnviro
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ee/api/environments.html#get-a-specific-environment
|
// https://docs.gitlab.com/ee/api/environments.html#get-a-specific-environment
|
||||||
func (s *EnvironmentsService) GetEnvironment(pid interface{}, environment int, options ...OptionFunc) (*Environment, *Response, error) {
|
func (s *EnvironmentsService) GetEnvironment(pid interface{}, environment int, options ...RequestOptionFunc) (*Environment, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -118,7 +118,7 @@ type CreateEnvironmentOptions struct {
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ee/api/environments.html#create-a-new-environment
|
// https://docs.gitlab.com/ee/api/environments.html#create-a-new-environment
|
||||||
func (s *EnvironmentsService) CreateEnvironment(pid interface{}, opt *CreateEnvironmentOptions, options ...OptionFunc) (*Environment, *Response, error) {
|
func (s *EnvironmentsService) CreateEnvironment(pid interface{}, opt *CreateEnvironmentOptions, options ...RequestOptionFunc) (*Environment, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -152,7 +152,7 @@ type EditEnvironmentOptions struct {
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ee/api/environments.html#edit-an-existing-environment
|
// https://docs.gitlab.com/ee/api/environments.html#edit-an-existing-environment
|
||||||
func (s *EnvironmentsService) EditEnvironment(pid interface{}, environment int, opt *EditEnvironmentOptions, options ...OptionFunc) (*Environment, *Response, error) {
|
func (s *EnvironmentsService) EditEnvironment(pid interface{}, environment int, opt *EditEnvironmentOptions, options ...RequestOptionFunc) (*Environment, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -177,7 +177,7 @@ func (s *EnvironmentsService) EditEnvironment(pid interface{}, environment int,
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/environments.html#remove-a-environment-from-a-group-or-project
|
// https://docs.gitlab.com/ce/api/environments.html#remove-a-environment-from-a-group-or-project
|
||||||
func (s *EnvironmentsService) DeleteEnvironment(pid interface{}, environment int, options ...OptionFunc) (*Response, error) {
|
func (s *EnvironmentsService) DeleteEnvironment(pid interface{}, environment int, options ...RequestOptionFunc) (*Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -196,7 +196,7 @@ func (s *EnvironmentsService) DeleteEnvironment(pid interface{}, environment int
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/environments.html#stop-an-environment
|
// https://docs.gitlab.com/ce/api/environments.html#stop-an-environment
|
||||||
func (s *EnvironmentsService) StopEnvironment(pid interface{}, environmentID int, options ...OptionFunc) (*Response, error) {
|
func (s *EnvironmentsService) StopEnvironment(pid interface{}, environmentID int, options ...RequestOptionFunc) (*Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -71,7 +71,7 @@ type ListGroupEpicsOptions struct {
|
||||||
// parameters page and per_page to return the list of group epics.
|
// parameters page and per_page to return the list of group epics.
|
||||||
//
|
//
|
||||||
// GitLab API docs: https://docs.gitlab.com/ee/api/epics.html#list-epics-for-a-group
|
// GitLab API docs: https://docs.gitlab.com/ee/api/epics.html#list-epics-for-a-group
|
||||||
func (s *EpicsService) ListGroupEpics(gid interface{}, opt *ListGroupEpicsOptions, options ...OptionFunc) ([]*Epic, *Response, error) {
|
func (s *EpicsService) ListGroupEpics(gid interface{}, opt *ListGroupEpicsOptions, options ...RequestOptionFunc) ([]*Epic, *Response, error) {
|
||||||
group, err := parseID(gid)
|
group, err := parseID(gid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -95,7 +95,7 @@ func (s *EpicsService) ListGroupEpics(gid interface{}, opt *ListGroupEpicsOption
|
||||||
// GetEpic gets a single group epic.
|
// GetEpic gets a single group epic.
|
||||||
//
|
//
|
||||||
// GitLab API docs: https://docs.gitlab.com/ee/api/epics.html#single-epic
|
// GitLab API docs: https://docs.gitlab.com/ee/api/epics.html#single-epic
|
||||||
func (s *EpicsService) GetEpic(gid interface{}, epic int, options ...OptionFunc) (*Epic, *Response, error) {
|
func (s *EpicsService) GetEpic(gid interface{}, epic int, options ...RequestOptionFunc) (*Epic, *Response, error) {
|
||||||
group, err := parseID(gid)
|
group, err := parseID(gid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -132,7 +132,7 @@ type CreateEpicOptions struct {
|
||||||
// CreateEpic creates a new group epic.
|
// CreateEpic creates a new group epic.
|
||||||
//
|
//
|
||||||
// GitLab API docs: https://docs.gitlab.com/ee/api/epics.html#new-epic
|
// GitLab API docs: https://docs.gitlab.com/ee/api/epics.html#new-epic
|
||||||
func (s *EpicsService) CreateEpic(gid interface{}, opt *CreateEpicOptions, options ...OptionFunc) (*Epic, *Response, error) {
|
func (s *EpicsService) CreateEpic(gid interface{}, opt *CreateEpicOptions, options ...RequestOptionFunc) (*Epic, *Response, error) {
|
||||||
group, err := parseID(gid)
|
group, err := parseID(gid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -171,7 +171,7 @@ type UpdateEpicOptions struct {
|
||||||
// to mark an epic as closed.
|
// to mark an epic as closed.
|
||||||
//
|
//
|
||||||
// GitLab API docs: https://docs.gitlab.com/ee/api/epics.html#update-epic
|
// GitLab API docs: https://docs.gitlab.com/ee/api/epics.html#update-epic
|
||||||
func (s *EpicsService) UpdateEpic(gid interface{}, epic int, opt *UpdateEpicOptions, options ...OptionFunc) (*Epic, *Response, error) {
|
func (s *EpicsService) UpdateEpic(gid interface{}, epic int, opt *UpdateEpicOptions, options ...RequestOptionFunc) (*Epic, *Response, error) {
|
||||||
group, err := parseID(gid)
|
group, err := parseID(gid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -195,7 +195,7 @@ func (s *EpicsService) UpdateEpic(gid interface{}, epic int, opt *UpdateEpicOpti
|
||||||
// DeleteEpic deletes a single group epic.
|
// DeleteEpic deletes a single group epic.
|
||||||
//
|
//
|
||||||
// GitLab API docs: https://docs.gitlab.com/ee/api/epics.html#delete-epic
|
// GitLab API docs: https://docs.gitlab.com/ee/api/epics.html#delete-epic
|
||||||
func (s *EpicsService) DeleteEpic(gid interface{}, epic int, options ...OptionFunc) (*Response, error) {
|
func (s *EpicsService) DeleteEpic(gid interface{}, epic int, options ...RequestOptionFunc) (*Response, error) {
|
||||||
group, err := parseID(gid)
|
group, err := parseID(gid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -13,11 +13,14 @@ type EventType string
|
||||||
const (
|
const (
|
||||||
EventTypeBuild EventType = "Build Hook"
|
EventTypeBuild EventType = "Build Hook"
|
||||||
EventTypeIssue EventType = "Issue Hook"
|
EventTypeIssue EventType = "Issue Hook"
|
||||||
|
EventConfidentialIssue EventType = "Confidential Issue Hook"
|
||||||
EventTypeJob EventType = "Job Hook"
|
EventTypeJob EventType = "Job Hook"
|
||||||
EventTypeMergeRequest EventType = "Merge Request Hook"
|
EventTypeMergeRequest EventType = "Merge Request Hook"
|
||||||
EventTypeNote EventType = "Note Hook"
|
EventTypeNote EventType = "Note Hook"
|
||||||
|
EventConfidentialNote EventType = "Confidential Note Hook"
|
||||||
EventTypePipeline EventType = "Pipeline Hook"
|
EventTypePipeline EventType = "Pipeline Hook"
|
||||||
EventTypePush EventType = "Push Hook"
|
EventTypePush EventType = "Push Hook"
|
||||||
|
EventTypeSystemHook EventType = "System Hook"
|
||||||
EventTypeTagPush EventType = "Tag Push Hook"
|
EventTypeTagPush EventType = "Tag Push Hook"
|
||||||
EventTypeWikiPage EventType = "Wiki Page Hook"
|
EventTypeWikiPage EventType = "Wiki Page Hook"
|
||||||
)
|
)
|
||||||
|
@ -38,6 +41,119 @@ type noteEvent struct {
|
||||||
|
|
||||||
const eventTypeHeader = "X-Gitlab-Event"
|
const eventTypeHeader = "X-Gitlab-Event"
|
||||||
|
|
||||||
|
// HookEventType returns the event type for the given request.
|
||||||
|
func HookEventType(r *http.Request) EventType {
|
||||||
|
return EventType(r.Header.Get(eventTypeHeader))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseHook tries to parse both web- and system hooks.
|
||||||
|
//
|
||||||
|
// Example usage:
|
||||||
|
//
|
||||||
|
// func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
|
// payload, err := ioutil.ReadAll(r.Body)
|
||||||
|
// if err != nil { ... }
|
||||||
|
// event, err := gitlab.ParseHook(gitlab.HookEventType(r), payload)
|
||||||
|
// if err != nil { ... }
|
||||||
|
// switch event := event.(type) {
|
||||||
|
// case *gitlab.PushEvent:
|
||||||
|
// processPushEvent(event)
|
||||||
|
// case *gitlab.MergeEvent:
|
||||||
|
// processMergeEvent(event)
|
||||||
|
// ...
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
func ParseHook(eventType EventType, payload []byte) (event interface{}, err error) {
|
||||||
|
switch eventType {
|
||||||
|
case EventTypeSystemHook:
|
||||||
|
return ParseSystemhook(payload)
|
||||||
|
default:
|
||||||
|
return ParseWebhook(eventType, payload)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseSystemhook parses the event payload. For recognized event types, a
|
||||||
|
// value of the corresponding struct type will be returned. An error will be
|
||||||
|
// returned for unrecognized event types.
|
||||||
|
//
|
||||||
|
// Example usage:
|
||||||
|
//
|
||||||
|
// func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
|
// payload, err := ioutil.ReadAll(r.Body)
|
||||||
|
// if err != nil { ... }
|
||||||
|
// event, err := gitlab.ParseSystemhook(payload)
|
||||||
|
// if err != nil { ... }
|
||||||
|
// switch event := event.(type) {
|
||||||
|
// case *gitlab.PushSystemEvent:
|
||||||
|
// processPushSystemEvent(event)
|
||||||
|
// case *gitlab.MergeSystemEvent:
|
||||||
|
// processMergeSystemEvent(event)
|
||||||
|
// ...
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
func ParseSystemhook(payload []byte) (event interface{}, err error) {
|
||||||
|
e := &systemHookEvent{}
|
||||||
|
err = json.Unmarshal(payload, e)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch e.EventName {
|
||||||
|
case "push":
|
||||||
|
event = &PushSystemEvent{}
|
||||||
|
case "tag_push":
|
||||||
|
event = &TagPushSystemEvent{}
|
||||||
|
case "repository_update":
|
||||||
|
event = &RepositoryUpdateSystemEvent{}
|
||||||
|
case
|
||||||
|
"project_create",
|
||||||
|
"project_update",
|
||||||
|
"project_destroy",
|
||||||
|
"project_transfer",
|
||||||
|
"project_rename":
|
||||||
|
event = &ProjectSystemEvent{}
|
||||||
|
case
|
||||||
|
"group_create",
|
||||||
|
"group_destroy",
|
||||||
|
"group_rename":
|
||||||
|
event = &GroupSystemEvent{}
|
||||||
|
case
|
||||||
|
"key_create",
|
||||||
|
"key_destroy":
|
||||||
|
event = &KeySystemEvent{}
|
||||||
|
case
|
||||||
|
"user_create",
|
||||||
|
"user_destroy",
|
||||||
|
"user_rename":
|
||||||
|
event = &UserSystemEvent{}
|
||||||
|
case
|
||||||
|
"user_add_to_group",
|
||||||
|
"user_remove_from_group",
|
||||||
|
"user_update_for_group":
|
||||||
|
event = &UserGroupSystemEvent{}
|
||||||
|
case
|
||||||
|
"user_add_to_team",
|
||||||
|
"user_remove_from_team",
|
||||||
|
"user_update_for_team":
|
||||||
|
event = &UserTeamSystemEvent{}
|
||||||
|
default:
|
||||||
|
switch e.ObjectKind {
|
||||||
|
case "merge_request":
|
||||||
|
event = &MergeEvent{}
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unexpected system hook type %s", e.EventName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := json.Unmarshal(payload, event); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return event, nil
|
||||||
|
}
|
||||||
|
|
||||||
// WebhookEventType returns the event type for the given request.
|
// WebhookEventType returns the event type for the given request.
|
||||||
func WebhookEventType(r *http.Request) EventType {
|
func WebhookEventType(r *http.Request) EventType {
|
||||||
return EventType(r.Header.Get(eventTypeHeader))
|
return EventType(r.Header.Get(eventTypeHeader))
|
||||||
|
@ -52,7 +168,7 @@ func WebhookEventType(r *http.Request) EventType {
|
||||||
// func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
// func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
// payload, err := ioutil.ReadAll(r.Body)
|
// payload, err := ioutil.ReadAll(r.Body)
|
||||||
// if err != nil { ... }
|
// if err != nil { ... }
|
||||||
// event, err := gitlab.ParseWebhook(gitlab.WebhookEventType(r), payload)
|
// event, err := gitlab.ParseWebhook(gitlab.HookEventType(r), payload)
|
||||||
// if err != nil { ... }
|
// if err != nil { ... }
|
||||||
// switch event := event.(type) {
|
// switch event := event.(type) {
|
||||||
// case *gitlab.PushEvent:
|
// case *gitlab.PushEvent:
|
||||||
|
@ -67,7 +183,7 @@ func ParseWebhook(eventType EventType, payload []byte) (event interface{}, err e
|
||||||
switch eventType {
|
switch eventType {
|
||||||
case EventTypeBuild:
|
case EventTypeBuild:
|
||||||
event = &BuildEvent{}
|
event = &BuildEvent{}
|
||||||
case EventTypeIssue:
|
case EventTypeIssue, EventConfidentialIssue:
|
||||||
event = &IssueEvent{}
|
event = &IssueEvent{}
|
||||||
case EventTypeJob:
|
case EventTypeJob:
|
||||||
event = &JobEvent{}
|
event = &JobEvent{}
|
||||||
|
@ -81,7 +197,7 @@ func ParseWebhook(eventType EventType, payload []byte) (event interface{}, err e
|
||||||
event = &TagEvent{}
|
event = &TagEvent{}
|
||||||
case EventTypeWikiPage:
|
case EventTypeWikiPage:
|
||||||
event = &WikiPageEvent{}
|
event = &WikiPageEvent{}
|
||||||
case EventTypeNote:
|
case EventTypeNote, EventConfidentialNote:
|
||||||
note := ¬eEvent{}
|
note := ¬eEvent{}
|
||||||
err := json.Unmarshal(payload, note)
|
err := json.Unmarshal(payload, note)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -0,0 +1,133 @@
|
||||||
|
package gitlab
|
||||||
|
|
||||||
|
// systemHookEvent is used to pre-process events to determine the
|
||||||
|
// system hook event type.
|
||||||
|
type systemHookEvent struct {
|
||||||
|
BaseSystemEvent
|
||||||
|
ObjectKind string `json:"object_kind"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// BaseSystemEvent contains system hook's common properties.
|
||||||
|
//
|
||||||
|
// GitLab API docs:
|
||||||
|
// https://docs.gitlab.com/ee/system_hooks/system_hooks.html
|
||||||
|
type BaseSystemEvent struct {
|
||||||
|
EventName string `json:"event_name"`
|
||||||
|
CreatedAt string `json:"created_at"`
|
||||||
|
UpdatedAt string `json:"updated_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProjectSystemEvent represents a project system event.
|
||||||
|
//
|
||||||
|
// GitLab API docs:
|
||||||
|
// https://docs.gitlab.com/ee/system_hooks/system_hooks.html
|
||||||
|
type ProjectSystemEvent struct {
|
||||||
|
BaseSystemEvent
|
||||||
|
Name string `json:"name"`
|
||||||
|
Path string `json:"path"`
|
||||||
|
PathWithNamespace string `json:"path_with_namespace"`
|
||||||
|
ProjectID int `json:"project_id"`
|
||||||
|
OwnerName string `json:"owner_name"`
|
||||||
|
OwnerEmail string `json:"owner_email"`
|
||||||
|
ProjectVisibility string `json:"project_visibility"`
|
||||||
|
OldPathWithNamespace string `json:"old_path_with_namespace,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GroupSystemEvent represents a group system event.
|
||||||
|
//
|
||||||
|
// GitLab API docs:
|
||||||
|
// https://docs.gitlab.com/ee/system_hooks/system_hooks.html
|
||||||
|
type GroupSystemEvent struct {
|
||||||
|
BaseSystemEvent
|
||||||
|
Name string `json:"name"`
|
||||||
|
Path string `json:"path"`
|
||||||
|
PathWithNamespace string `json:"full_path"`
|
||||||
|
GroupID int `json:"group_id"`
|
||||||
|
OwnerName string `json:"owner_name"`
|
||||||
|
OwnerEmail string `json:"owner_email"`
|
||||||
|
ProjectVisibility string `json:"project_visibility"`
|
||||||
|
OldPath string `json:"old_path,omitempty"`
|
||||||
|
OldPathWithNamespace string `json:"old_full_path,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// KeySystemEvent represents a key system event.
|
||||||
|
//
|
||||||
|
// GitLab API docs:
|
||||||
|
// https://docs.gitlab.com/ee/system_hooks/system_hooks.html
|
||||||
|
type KeySystemEvent struct {
|
||||||
|
BaseSystemEvent
|
||||||
|
ID int `json:"id"`
|
||||||
|
Username string `json:"username"`
|
||||||
|
Key string `json:"key"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// UserSystemEvent represents a user system event.
|
||||||
|
//
|
||||||
|
// GitLab API docs:
|
||||||
|
// https://docs.gitlab.com/ee/system_hooks/system_hooks.html
|
||||||
|
type UserSystemEvent struct {
|
||||||
|
BaseSystemEvent
|
||||||
|
ID int `json:"user_id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Username string `json:"username"`
|
||||||
|
OldUsername string `json:"old_username,omitempty"`
|
||||||
|
Email string `json:"email"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// UserGroupSystemEvent represents a user group system event.
|
||||||
|
//
|
||||||
|
// GitLab API docs:
|
||||||
|
// https://docs.gitlab.com/ee/system_hooks/system_hooks.html
|
||||||
|
type UserGroupSystemEvent struct {
|
||||||
|
BaseSystemEvent
|
||||||
|
ID int `json:"user_id"`
|
||||||
|
Name string `json:"user_name"`
|
||||||
|
Username string `json:"user_username"`
|
||||||
|
Email string `json:"user_email"`
|
||||||
|
GroupID int `json:"group_id"`
|
||||||
|
GroupName string `json:"group_name"`
|
||||||
|
GroupPath string `json:"group_path"`
|
||||||
|
GroupAccess string `json:"group_access"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// UserTeamSystemEvent represents a user team system event.
|
||||||
|
//
|
||||||
|
// GitLab API docs:
|
||||||
|
// https://docs.gitlab.com/ee/system_hooks/system_hooks.html
|
||||||
|
type UserTeamSystemEvent struct {
|
||||||
|
BaseSystemEvent
|
||||||
|
ID int `json:"user_id"`
|
||||||
|
Name string `json:"user_name"`
|
||||||
|
Username string `json:"user_username"`
|
||||||
|
Email string `json:"user_email"`
|
||||||
|
ProjectID int `json:"project_id"`
|
||||||
|
ProjectName string `json:"project_name"`
|
||||||
|
ProjectPath string `json:"project_path"`
|
||||||
|
ProjectPathWithNamespace string `json:"project_path_with_namespace"`
|
||||||
|
ProjectVisibility string `json:"project_visibility"`
|
||||||
|
AccessLevel string `json:"access_level"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// PushSystemEvent represents a push system event.
|
||||||
|
//
|
||||||
|
// GitLab API docs:
|
||||||
|
// https://docs.gitlab.com/ee/system_hooks/system_hooks.html
|
||||||
|
type PushSystemEvent struct {
|
||||||
|
BaseSystemEvent
|
||||||
|
}
|
||||||
|
|
||||||
|
// TagPushSystemEvent represents a tag push system event.
|
||||||
|
//
|
||||||
|
// GitLab API docs:
|
||||||
|
// https://docs.gitlab.com/ee/system_hooks/system_hooks.html
|
||||||
|
type TagPushSystemEvent struct {
|
||||||
|
BaseSystemEvent
|
||||||
|
}
|
||||||
|
|
||||||
|
// RepositoryUpdateSystemEvent represents a repository updated system event.
|
||||||
|
//
|
||||||
|
// GitLab API docs:
|
||||||
|
// https://docs.gitlab.com/ee/system_hooks/system_hooks.html
|
||||||
|
type RepositoryUpdateSystemEvent struct {
|
||||||
|
BaseSystemEvent
|
||||||
|
}
|
|
@ -128,6 +128,7 @@ type IssueEvent struct {
|
||||||
ObjectKind string `json:"object_kind"`
|
ObjectKind string `json:"object_kind"`
|
||||||
User *User `json:"user"`
|
User *User `json:"user"`
|
||||||
Project struct {
|
Project struct {
|
||||||
|
ID int `json:"id"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Description string `json:"description"`
|
Description string `json:"description"`
|
||||||
AvatarURL string `json:"avatar_url"`
|
AvatarURL string `json:"avatar_url"`
|
||||||
|
@ -203,6 +204,7 @@ type JobEvent struct {
|
||||||
BuildFinishedAt string `json:"build_finished_at"`
|
BuildFinishedAt string `json:"build_finished_at"`
|
||||||
BuildDuration float64 `json:"build_duration"`
|
BuildDuration float64 `json:"build_duration"`
|
||||||
BuildAllowFailure bool `json:"build_allow_failure"`
|
BuildAllowFailure bool `json:"build_allow_failure"`
|
||||||
|
PipelineID int `json:"pipeline_id"`
|
||||||
ProjectID int `json:"project_id"`
|
ProjectID int `json:"project_id"`
|
||||||
ProjectName string `json:"project_name"`
|
ProjectName string `json:"project_name"`
|
||||||
User struct {
|
User struct {
|
||||||
|
@ -335,6 +337,7 @@ type MergeCommentEvent struct {
|
||||||
SourceProjectID int `json:"source_project_id"`
|
SourceProjectID int `json:"source_project_id"`
|
||||||
AuthorID int `json:"author_id"`
|
AuthorID int `json:"author_id"`
|
||||||
AssigneeID int `json:"assignee_id"`
|
AssigneeID int `json:"assignee_id"`
|
||||||
|
AssigneeIDs []int `json:"assignee_ids"`
|
||||||
Title string `json:"title"`
|
Title string `json:"title"`
|
||||||
CreatedAt string `json:"created_at"`
|
CreatedAt string `json:"created_at"`
|
||||||
UpdatedAt string `json:"updated_at"`
|
UpdatedAt string `json:"updated_at"`
|
||||||
|
@ -348,9 +351,7 @@ type MergeCommentEvent struct {
|
||||||
LockedAt string `json:"locked_at"`
|
LockedAt string `json:"locked_at"`
|
||||||
UpdatedByID int `json:"updated_by_id"`
|
UpdatedByID int `json:"updated_by_id"`
|
||||||
MergeError string `json:"merge_error"`
|
MergeError string `json:"merge_error"`
|
||||||
MergeParams struct {
|
MergeParams *MergeParams `json:"merge_params"`
|
||||||
ForceRemoveSourceBranch string `json:"force_remove_source_branch"`
|
|
||||||
} `json:"merge_params"`
|
|
||||||
MergeWhenPipelineSucceeds bool `json:"merge_when_pipeline_succeeds"`
|
MergeWhenPipelineSucceeds bool `json:"merge_when_pipeline_succeeds"`
|
||||||
MergeUserID int `json:"merge_user_id"`
|
MergeUserID int `json:"merge_user_id"`
|
||||||
MergeCommitSHA string `json:"merge_commit_sha"`
|
MergeCommitSHA string `json:"merge_commit_sha"`
|
||||||
|
@ -524,6 +525,7 @@ type MergeEvent struct {
|
||||||
SourceProjectID int `json:"source_project_id"`
|
SourceProjectID int `json:"source_project_id"`
|
||||||
AuthorID int `json:"author_id"`
|
AuthorID int `json:"author_id"`
|
||||||
AssigneeID int `json:"assignee_id"`
|
AssigneeID int `json:"assignee_id"`
|
||||||
|
AssigneeIDs []int `json:"assignee_ids"`
|
||||||
Title string `json:"title"`
|
Title string `json:"title"`
|
||||||
CreatedAt string `json:"created_at"` // Should be *time.Time (see Gitlab issue #21468)
|
CreatedAt string `json:"created_at"` // Should be *time.Time (see Gitlab issue #21468)
|
||||||
UpdatedAt string `json:"updated_at"` // Should be *time.Time (see Gitlab issue #21468)
|
UpdatedAt string `json:"updated_at"` // Should be *time.Time (see Gitlab issue #21468)
|
||||||
|
@ -715,6 +717,18 @@ type PipelineEvent struct {
|
||||||
FinishedAt string `json:"finished_at"`
|
FinishedAt string `json:"finished_at"`
|
||||||
Duration int `json:"duration"`
|
Duration int `json:"duration"`
|
||||||
} `json:"object_attributes"`
|
} `json:"object_attributes"`
|
||||||
|
MergeRequest struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
IID int `json:"iid"`
|
||||||
|
Title string `json:"title"`
|
||||||
|
SourceBranch string `json:"source_branch"`
|
||||||
|
SourceProjectID int `json:"source_project_id"`
|
||||||
|
TargetBranch string `json:"target_branch"`
|
||||||
|
TargetProjectID int `json:"target_project_id"`
|
||||||
|
State string `json:"state"`
|
||||||
|
MergeRequestStatus string `json:"merge_status"`
|
||||||
|
URL string `json:"url"`
|
||||||
|
} `json:"merge_request"`
|
||||||
User struct {
|
User struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Username string `json:"username"`
|
Username string `json:"username"`
|
|
@ -82,7 +82,7 @@ type ListContributionEventsOptions struct {
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/events.html#get-user-contribution-events
|
// https://docs.gitlab.com/ce/api/events.html#get-user-contribution-events
|
||||||
func (s *UsersService) ListUserContributionEvents(uid interface{}, opt *ListContributionEventsOptions, options ...OptionFunc) ([]*ContributionEvent, *Response, error) {
|
func (s *UsersService) ListUserContributionEvents(uid interface{}, opt *ListContributionEventsOptions, options ...RequestOptionFunc) ([]*ContributionEvent, *Response, error) {
|
||||||
user, err := parseID(uid)
|
user, err := parseID(uid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -106,7 +106,7 @@ func (s *UsersService) ListUserContributionEvents(uid interface{}, opt *ListCont
|
||||||
// ListCurrentUserContributionEvents gets a list currently authenticated user's events
|
// ListCurrentUserContributionEvents gets a list currently authenticated user's events
|
||||||
//
|
//
|
||||||
// GitLab API docs: https://docs.gitlab.com/ce/api/events.html#list-currently-authenticated-user-39-s-events
|
// GitLab API docs: https://docs.gitlab.com/ce/api/events.html#list-currently-authenticated-user-39-s-events
|
||||||
func (s *EventsService) ListCurrentUserContributionEvents(opt *ListContributionEventsOptions, options ...OptionFunc) ([]*ContributionEvent, *Response, error) {
|
func (s *EventsService) ListCurrentUserContributionEvents(opt *ListContributionEventsOptions, options ...RequestOptionFunc) ([]*ContributionEvent, *Response, error) {
|
||||||
req, err := s.client.NewRequest("GET", "events", opt, options)
|
req, err := s.client.NewRequest("GET", "events", opt, options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -124,7 +124,7 @@ func (s *EventsService) ListCurrentUserContributionEvents(opt *ListContributionE
|
||||||
// ListProjectVisibleEvents gets a list of visible events for a particular project
|
// ListProjectVisibleEvents gets a list of visible events for a particular project
|
||||||
//
|
//
|
||||||
// GitLab API docs: https://docs.gitlab.com/ee/api/events.html#list-a-project-s-visible-events
|
// GitLab API docs: https://docs.gitlab.com/ee/api/events.html#list-a-project-s-visible-events
|
||||||
func (s *EventsService) ListProjectVisibleEvents(pid interface{}, opt *ListContributionEventsOptions, options ...OptionFunc) ([]*ContributionEvent, *Response, error) {
|
func (s *EventsService) ListProjectVisibleEvents(pid interface{}, opt *ListContributionEventsOptions, options ...RequestOptionFunc) ([]*ContributionEvent, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
|
|
@ -38,7 +38,7 @@ func (f Feature) String() string {
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/features.html#list-all-features
|
// https://docs.gitlab.com/ce/api/features.html#list-all-features
|
||||||
func (s *FeaturesService) ListFeatures(options ...OptionFunc) ([]*Feature, *Response, error) {
|
func (s *FeaturesService) ListFeatures(options ...RequestOptionFunc) ([]*Feature, *Response, error) {
|
||||||
req, err := s.client.NewRequest("GET", "features", nil, options)
|
req, err := s.client.NewRequest("GET", "features", nil, options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -56,7 +56,7 @@ func (s *FeaturesService) ListFeatures(options ...OptionFunc) ([]*Feature, *Resp
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/features.html#set-or-create-a-feature
|
// https://docs.gitlab.com/ce/api/features.html#set-or-create-a-feature
|
||||||
func (s *FeaturesService) SetFeatureFlag(name string, value interface{}, options ...OptionFunc) (*Feature, *Response, error) {
|
func (s *FeaturesService) SetFeatureFlag(name string, value interface{}, options ...RequestOptionFunc) (*Feature, *Response, error) {
|
||||||
u := fmt.Sprintf("features/%s", url.PathEscape(name))
|
u := fmt.Sprintf("features/%s", url.PathEscape(name))
|
||||||
|
|
||||||
opt := struct {
|
opt := struct {
|
||||||
|
|
|
@ -47,7 +47,7 @@ type ListTemplatesOptions ListOptions
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/templates/gitignores.html#list-gitignore-templates
|
// https://docs.gitlab.com/ce/api/templates/gitignores.html#list-gitignore-templates
|
||||||
func (s *GitIgnoreTemplatesService) ListTemplates(opt *ListTemplatesOptions, options ...OptionFunc) ([]*GitIgnoreTemplate, *Response, error) {
|
func (s *GitIgnoreTemplatesService) ListTemplates(opt *ListTemplatesOptions, options ...RequestOptionFunc) ([]*GitIgnoreTemplate, *Response, error) {
|
||||||
req, err := s.client.NewRequest("GET", "templates/gitignores", opt, options)
|
req, err := s.client.NewRequest("GET", "templates/gitignores", opt, options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -66,7 +66,7 @@ func (s *GitIgnoreTemplatesService) ListTemplates(opt *ListTemplatesOptions, opt
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/templates/gitignores.html#single-gitignore-template
|
// https://docs.gitlab.com/ce/api/templates/gitignores.html#single-gitignore-template
|
||||||
func (s *GitIgnoreTemplatesService) GetTemplate(key string, options ...OptionFunc) (*GitIgnoreTemplate, *Response, error) {
|
func (s *GitIgnoreTemplatesService) GetTemplate(key string, options ...RequestOptionFunc) (*GitIgnoreTemplate, *Response, error) {
|
||||||
u := fmt.Sprintf("templates/gitignores/%s", url.PathEscape(key))
|
u := fmt.Sprintf("templates/gitignores/%s", url.PathEscape(key))
|
||||||
|
|
||||||
req, err := s.client.NewRequest("GET", u, nil, options)
|
req, err := s.client.NewRequest("GET", u, nil, options)
|
||||||
|
|
|
@ -18,28 +18,35 @@
|
||||||
package gitlab
|
package gitlab
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"math/rand"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/google/go-querystring/query"
|
"github.com/google/go-querystring/query"
|
||||||
|
"github.com/hashicorp/go-cleanhttp"
|
||||||
|
retryablehttp "github.com/hashicorp/go-retryablehttp"
|
||||||
"golang.org/x/oauth2"
|
"golang.org/x/oauth2"
|
||||||
|
"golang.org/x/time/rate"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
defaultBaseURL = "https://gitlab.com/"
|
defaultBaseURL = "https://gitlab.com/"
|
||||||
apiVersionPath = "api/v4/"
|
apiVersionPath = "api/v4/"
|
||||||
userAgent = "go-gitlab"
|
userAgent = "go-gitlab"
|
||||||
|
|
||||||
|
headerRateLimit = "RateLimit-Limit"
|
||||||
|
headerRateReset = "RateLimit-Reset"
|
||||||
)
|
)
|
||||||
|
|
||||||
// authType represents an authentication type within GitLab.
|
// authType represents an authentication type within GitLab.
|
||||||
|
@ -83,6 +90,7 @@ type BuildStateValue string
|
||||||
// These constants represent all valid build states.
|
// These constants represent all valid build states.
|
||||||
const (
|
const (
|
||||||
Pending BuildStateValue = "pending"
|
Pending BuildStateValue = "pending"
|
||||||
|
Created BuildStateValue = "created"
|
||||||
Running BuildStateValue = "running"
|
Running BuildStateValue = "running"
|
||||||
Success BuildStateValue = "success"
|
Success BuildStateValue = "success"
|
||||||
Failed BuildStateValue = "failed"
|
Failed BuildStateValue = "failed"
|
||||||
|
@ -91,6 +99,18 @@ const (
|
||||||
Manual BuildStateValue = "manual"
|
Manual BuildStateValue = "manual"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// DeploymentStatusValue represents a Gitlab deployment status.
|
||||||
|
type DeploymentStatusValue string
|
||||||
|
|
||||||
|
// These constants represent all valid deployment statuses.
|
||||||
|
const (
|
||||||
|
DeploymentStatusCreated DeploymentStatusValue = "created"
|
||||||
|
DeploymentStatusRunning DeploymentStatusValue = "running"
|
||||||
|
DeploymentStatusSuccess DeploymentStatusValue = "success"
|
||||||
|
DeploymentStatusFailed DeploymentStatusValue = "failed"
|
||||||
|
DeploymentStatusCanceled DeploymentStatusValue = "canceled"
|
||||||
|
)
|
||||||
|
|
||||||
// ISOTime represents an ISO 8601 formatted date
|
// ISOTime represents an ISO 8601 formatted date
|
||||||
type ISOTime time.Time
|
type ISOTime time.Time
|
||||||
|
|
||||||
|
@ -215,6 +235,33 @@ const (
|
||||||
PublicVisibility VisibilityValue = "public"
|
PublicVisibility VisibilityValue = "public"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ProjectCreationLevelValue represents a project creation level within GitLab.
|
||||||
|
//
|
||||||
|
// GitLab API docs: https://docs.gitlab.com/ce/api/
|
||||||
|
type ProjectCreationLevelValue string
|
||||||
|
|
||||||
|
// List of available project creation levels.
|
||||||
|
//
|
||||||
|
// GitLab API docs: https://docs.gitlab.com/ce/api/
|
||||||
|
const (
|
||||||
|
NoOneProjectCreation ProjectCreationLevelValue = "noone"
|
||||||
|
MaintainerProjectCreation ProjectCreationLevelValue = "maintainer"
|
||||||
|
DeveloperProjectCreation ProjectCreationLevelValue = "developer"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SubGroupCreationLevelValue represents a sub group creation level within GitLab.
|
||||||
|
//
|
||||||
|
// GitLab API docs: https://docs.gitlab.com/ce/api/
|
||||||
|
type SubGroupCreationLevelValue string
|
||||||
|
|
||||||
|
// List of available sub group creation levels.
|
||||||
|
//
|
||||||
|
// GitLab API docs: https://docs.gitlab.com/ce/api/
|
||||||
|
const (
|
||||||
|
OwnerSubGroupCreationLevelValue SubGroupCreationLevelValue = "owner"
|
||||||
|
MaintainerSubGroupCreationLevelValue SubGroupCreationLevelValue = "maintainer"
|
||||||
|
)
|
||||||
|
|
||||||
// VariableTypeValue represents a variable type within GitLab.
|
// VariableTypeValue represents a variable type within GitLab.
|
||||||
//
|
//
|
||||||
// GitLab API docs: https://docs.gitlab.com/ce/api/
|
// GitLab API docs: https://docs.gitlab.com/ce/api/
|
||||||
|
@ -281,13 +328,23 @@ const (
|
||||||
// A Client manages communication with the GitLab API.
|
// A Client manages communication with the GitLab API.
|
||||||
type Client struct {
|
type Client struct {
|
||||||
// HTTP client used to communicate with the API.
|
// HTTP client used to communicate with the API.
|
||||||
client *http.Client
|
client *retryablehttp.Client
|
||||||
|
|
||||||
// Base URL for API requests. Defaults to the public GitLab API, but can be
|
// Base URL for API requests. Defaults to the public GitLab API, but can be
|
||||||
// set to a domain endpoint to use with a self hosted GitLab server. baseURL
|
// set to a domain endpoint to use with a self hosted GitLab server. baseURL
|
||||||
// should always be specified with a trailing slash.
|
// should always be specified with a trailing slash.
|
||||||
baseURL *url.URL
|
baseURL *url.URL
|
||||||
|
|
||||||
|
// disableRetries is used to disable the default retry logic.
|
||||||
|
disableRetries bool
|
||||||
|
|
||||||
|
// configLimiter is used to make sure the limiter is configured exactly
|
||||||
|
// once and block all other calls until the initial (one) call is done.
|
||||||
|
configureLimiterOnce sync.Once
|
||||||
|
|
||||||
|
// Limiter is used to limit API calls and prevent 429 responses.
|
||||||
|
limiter *rate.Limiter
|
||||||
|
|
||||||
// Token type used to make authenticated API calls.
|
// Token type used to make authenticated API calls.
|
||||||
authType authType
|
authType authType
|
||||||
|
|
||||||
|
@ -302,6 +359,7 @@ type Client struct {
|
||||||
|
|
||||||
// Services used for talking to different parts of the GitLab API.
|
// Services used for talking to different parts of the GitLab API.
|
||||||
AccessRequests *AccessRequestsService
|
AccessRequests *AccessRequestsService
|
||||||
|
Applications *ApplicationsService
|
||||||
AwardEmoji *AwardEmojiService
|
AwardEmoji *AwardEmojiService
|
||||||
Boards *IssueBoardsService
|
Boards *IssueBoardsService
|
||||||
Branches *BranchesService
|
Branches *BranchesService
|
||||||
|
@ -311,6 +369,7 @@ type Client struct {
|
||||||
ContainerRegistry *ContainerRegistryService
|
ContainerRegistry *ContainerRegistryService
|
||||||
CustomAttribute *CustomAttributesService
|
CustomAttribute *CustomAttributesService
|
||||||
DeployKeys *DeployKeysService
|
DeployKeys *DeployKeysService
|
||||||
|
DeployTokens *DeployTokensService
|
||||||
Deployments *DeploymentsService
|
Deployments *DeploymentsService
|
||||||
Discussions *DiscussionsService
|
Discussions *DiscussionsService
|
||||||
Environments *EnvironmentsService
|
Environments *EnvironmentsService
|
||||||
|
@ -382,31 +441,47 @@ type ListOptions struct {
|
||||||
PerPage int `url:"per_page,omitempty" json:"per_page,omitempty"`
|
PerPage int `url:"per_page,omitempty" json:"per_page,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewClient returns a new GitLab API client. If a nil httpClient is
|
// NewClient returns a new GitLab API client. To use API methods which require
|
||||||
// provided, http.DefaultClient will be used. To use API methods which require
|
|
||||||
// authentication, provide a valid private or personal token.
|
// authentication, provide a valid private or personal token.
|
||||||
func NewClient(httpClient *http.Client, token string) *Client {
|
func NewClient(token string, options ...ClientOptionFunc) (*Client, error) {
|
||||||
client := newClient(httpClient)
|
client, err := newClient(options...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
client.authType = privateToken
|
client.authType = privateToken
|
||||||
client.token = token
|
client.token = token
|
||||||
return client
|
return client, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewBasicAuthClient returns a new GitLab API client. If a nil httpClient is
|
// NewBasicAuthClient returns a new GitLab API client. To use API methods which
|
||||||
// provided, http.DefaultClient will be used. To use API methods which require
|
// require authentication, provide a valid username and password.
|
||||||
// authentication, provide a valid username and password.
|
func NewBasicAuthClient(username, password string, options ...ClientOptionFunc) (*Client, error) {
|
||||||
func NewBasicAuthClient(httpClient *http.Client, endpoint, username, password string) (*Client, error) {
|
client, err := newClient(options...)
|
||||||
client := newClient(httpClient)
|
|
||||||
client.authType = basicAuth
|
|
||||||
client.username = username
|
|
||||||
client.password = password
|
|
||||||
client.SetBaseURL(endpoint)
|
|
||||||
|
|
||||||
err := client.requestOAuthToken(context.TODO())
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
client.authType = basicAuth
|
||||||
|
client.username = username
|
||||||
|
client.password = password
|
||||||
|
|
||||||
|
err = client.requestOAuthToken(context.Background())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return client, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewOAuthClient returns a new GitLab API client. To use API methods which
|
||||||
|
// require authentication, provide a valid oauth token.
|
||||||
|
func NewOAuthClient(token string, options ...ClientOptionFunc) (*Client, error) {
|
||||||
|
client, err := newClient(options...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
client.authType = oAuthToken
|
||||||
|
client.token = token
|
||||||
return client, nil
|
return client, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -426,25 +501,31 @@ func (c *Client) requestOAuthToken(ctx context.Context) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewOAuthClient returns a new GitLab API client. If a nil httpClient is
|
func newClient(options ...ClientOptionFunc) (*Client, error) {
|
||||||
// provided, http.DefaultClient will be used. To use API methods which require
|
c := &Client{UserAgent: userAgent}
|
||||||
// authentication, provide a valid oauth token.
|
|
||||||
func NewOAuthClient(httpClient *http.Client, token string) *Client {
|
|
||||||
client := newClient(httpClient)
|
|
||||||
client.authType = oAuthToken
|
|
||||||
client.token = token
|
|
||||||
return client
|
|
||||||
}
|
|
||||||
|
|
||||||
func newClient(httpClient *http.Client) *Client {
|
// Configure the HTTP client.
|
||||||
if httpClient == nil {
|
c.client = &retryablehttp.Client{
|
||||||
httpClient = http.DefaultClient
|
Backoff: c.retryHTTPBackoff,
|
||||||
|
CheckRetry: c.retryHTTPCheck,
|
||||||
|
ErrorHandler: retryablehttp.PassthroughErrorHandler,
|
||||||
|
HTTPClient: cleanhttp.DefaultPooledClient(),
|
||||||
|
RetryWaitMin: 100 * time.Millisecond,
|
||||||
|
RetryWaitMax: 400 * time.Millisecond,
|
||||||
|
RetryMax: 5,
|
||||||
}
|
}
|
||||||
|
|
||||||
c := &Client{client: httpClient, UserAgent: userAgent}
|
// Set the default base URL.
|
||||||
if err := c.SetBaseURL(defaultBaseURL); err != nil {
|
c.setBaseURL(defaultBaseURL)
|
||||||
// Should never happen since defaultBaseURL is our constant.
|
|
||||||
panic(err)
|
// Apply any given client options.
|
||||||
|
for _, fn := range options {
|
||||||
|
if fn == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err := fn(c); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the internal timeStats service.
|
// Create the internal timeStats service.
|
||||||
|
@ -452,6 +533,7 @@ func newClient(httpClient *http.Client) *Client {
|
||||||
|
|
||||||
// Create all the public services.
|
// Create all the public services.
|
||||||
c.AccessRequests = &AccessRequestsService{client: c}
|
c.AccessRequests = &AccessRequestsService{client: c}
|
||||||
|
c.Applications = &ApplicationsService{client: c}
|
||||||
c.AwardEmoji = &AwardEmojiService{client: c}
|
c.AwardEmoji = &AwardEmojiService{client: c}
|
||||||
c.Boards = &IssueBoardsService{client: c}
|
c.Boards = &IssueBoardsService{client: c}
|
||||||
c.Branches = &BranchesService{client: c}
|
c.Branches = &BranchesService{client: c}
|
||||||
|
@ -461,6 +543,7 @@ func newClient(httpClient *http.Client) *Client {
|
||||||
c.ContainerRegistry = &ContainerRegistryService{client: c}
|
c.ContainerRegistry = &ContainerRegistryService{client: c}
|
||||||
c.CustomAttribute = &CustomAttributesService{client: c}
|
c.CustomAttribute = &CustomAttributesService{client: c}
|
||||||
c.DeployKeys = &DeployKeysService{client: c}
|
c.DeployKeys = &DeployKeysService{client: c}
|
||||||
|
c.DeployTokens = &DeployTokensService{client: c}
|
||||||
c.Deployments = &DeploymentsService{client: c}
|
c.Deployments = &DeploymentsService{client: c}
|
||||||
c.Discussions = &DiscussionsService{client: c}
|
c.Discussions = &DiscussionsService{client: c}
|
||||||
c.Environments = &EnvironmentsService{client: c}
|
c.Environments = &EnvironmentsService{client: c}
|
||||||
|
@ -521,7 +604,107 @@ func newClient(httpClient *http.Client) *Client {
|
||||||
c.Version = &VersionService{client: c}
|
c.Version = &VersionService{client: c}
|
||||||
c.Wikis = &WikisService{client: c}
|
c.Wikis = &WikisService{client: c}
|
||||||
|
|
||||||
return c
|
return c, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// retryHTTPCheck provides a callback for Client.CheckRetry which
|
||||||
|
// will retry both rate limit (429) and server (>= 500) errors.
|
||||||
|
func (c *Client) retryHTTPCheck(ctx context.Context, resp *http.Response, err error) (bool, error) {
|
||||||
|
if ctx.Err() != nil {
|
||||||
|
return false, ctx.Err()
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
if !c.disableRetries && (resp.StatusCode == 429 || resp.StatusCode >= 500) {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// retryHTTPBackoff provides a generic callback for Client.Backoff which
|
||||||
|
// will pass through all calls based on the status code of the response.
|
||||||
|
func (c *Client) retryHTTPBackoff(min, max time.Duration, attemptNum int, resp *http.Response) time.Duration {
|
||||||
|
// Use the rate limit backoff function when we are rate limited.
|
||||||
|
if resp != nil && resp.StatusCode == 429 {
|
||||||
|
return rateLimitBackoff(min, max, attemptNum, resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set custom duration's when we experience a service interruption.
|
||||||
|
min = 700 * time.Millisecond
|
||||||
|
max = 900 * time.Millisecond
|
||||||
|
|
||||||
|
return retryablehttp.LinearJitterBackoff(min, max, attemptNum, resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// rateLimitBackoff provides a callback for Client.Backoff which will use the
|
||||||
|
// RateLimit-Reset header to determine the time to wait. We add some jitter
|
||||||
|
// to prevent a thundering herd.
|
||||||
|
//
|
||||||
|
// min and max are mainly used for bounding the jitter that will be added to
|
||||||
|
// the reset time retrieved from the headers. But if the final wait time is
|
||||||
|
// less then min, min will be used instead.
|
||||||
|
func rateLimitBackoff(min, max time.Duration, attemptNum int, resp *http.Response) time.Duration {
|
||||||
|
// rnd is used to generate pseudo-random numbers.
|
||||||
|
rnd := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||||
|
|
||||||
|
// First create some jitter bounded by the min and max durations.
|
||||||
|
jitter := time.Duration(rnd.Float64() * float64(max-min))
|
||||||
|
|
||||||
|
if resp != nil {
|
||||||
|
if v := resp.Header.Get(headerRateReset); v != "" {
|
||||||
|
if reset, _ := strconv.ParseInt(v, 10, 64); reset > 0 {
|
||||||
|
// Only update min if the given time to wait is longer.
|
||||||
|
if wait := time.Until(time.Unix(reset, 0)); wait > min {
|
||||||
|
min = wait
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return min + jitter
|
||||||
|
}
|
||||||
|
|
||||||
|
// configureLimiter configures the rate limiter.
|
||||||
|
func (c *Client) configureLimiter() error {
|
||||||
|
// Set default values for when rate limiting is disabled.
|
||||||
|
limit := rate.Inf
|
||||||
|
burst := 0
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
// Create a new limiter using the calculated values.
|
||||||
|
c.limiter = rate.NewLimiter(limit, burst)
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Create a new request.
|
||||||
|
req, err := http.NewRequest("GET", c.baseURL.String(), nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make a single request to retrieve the rate limit headers.
|
||||||
|
resp, err := c.client.HTTPClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
resp.Body.Close()
|
||||||
|
|
||||||
|
if v := resp.Header.Get(headerRateLimit); v != "" {
|
||||||
|
if rateLimit, _ := strconv.ParseFloat(v, 64); rateLimit > 0 {
|
||||||
|
// The rate limit is based on requests per minute, so for our limiter to
|
||||||
|
// work correctly we devide the limit by 60 to get the limit per second.
|
||||||
|
rateLimit /= 60
|
||||||
|
// Configure the limit and burst using a split of 2/3 for the limit and
|
||||||
|
// 1/3 for the burst. This enables clients to burst 1/3 of the allowed
|
||||||
|
// calls before the limiter kicks in. The remaining calls will then be
|
||||||
|
// spread out evenly using intervals of time.Second / limit which should
|
||||||
|
// prevent hitting the rate limit.
|
||||||
|
limit = rate.Limit(rateLimit * 0.66)
|
||||||
|
burst = int(rateLimit * 0.33)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// BaseURL return a copy of the baseURL.
|
// BaseURL return a copy of the baseURL.
|
||||||
|
@ -530,9 +713,8 @@ func (c *Client) BaseURL() *url.URL {
|
||||||
return &u
|
return &u
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetBaseURL sets the base URL for API requests to a custom endpoint. urlStr
|
// setBaseURL sets the base URL for API requests to a custom endpoint.
|
||||||
// should always be specified with a trailing slash.
|
func (c *Client) setBaseURL(urlStr string) error {
|
||||||
func (c *Client) SetBaseURL(urlStr string) error {
|
|
||||||
// Make sure the given URL end with a slash
|
// Make sure the given URL end with a slash
|
||||||
if !strings.HasSuffix(urlStr, "/") {
|
if !strings.HasSuffix(urlStr, "/") {
|
||||||
urlStr += "/"
|
urlStr += "/"
|
||||||
|
@ -554,11 +736,11 @@ func (c *Client) SetBaseURL(urlStr string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewRequest creates an API request. A relative URL path can be provided in
|
// NewRequest creates an API request. A relative URL path can be provided in
|
||||||
// urlStr, in which case it is resolved relative to the base URL of the Client.
|
// path, in which case it is resolved relative to the base URL of the Client.
|
||||||
// Relative URL paths should always be specified without a preceding slash. If
|
// Relative URL paths should always be specified without a preceding slash. If
|
||||||
// specified, the value pointed to by body is JSON encoded and included as the
|
// specified, the value pointed to by body is JSON encoded and included as the
|
||||||
// request body.
|
// request body.
|
||||||
func (c *Client) NewRequest(method, path string, opt interface{}, options []OptionFunc) (*http.Request, error) {
|
func (c *Client) NewRequest(method, path string, opt interface{}, options []RequestOptionFunc) (*retryablehttp.Request, error) {
|
||||||
u := *c.baseURL
|
u := *c.baseURL
|
||||||
unescaped, err := url.PathUnescape(path)
|
unescaped, err := url.PathUnescape(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -569,7 +751,33 @@ func (c *Client) NewRequest(method, path string, opt interface{}, options []Opti
|
||||||
u.RawPath = c.baseURL.Path + path
|
u.RawPath = c.baseURL.Path + path
|
||||||
u.Path = c.baseURL.Path + unescaped
|
u.Path = c.baseURL.Path + unescaped
|
||||||
|
|
||||||
|
// Create a request specific headers map.
|
||||||
|
reqHeaders := make(http.Header)
|
||||||
|
reqHeaders.Set("Accept", "application/json")
|
||||||
|
|
||||||
|
switch c.authType {
|
||||||
|
case basicAuth, oAuthToken:
|
||||||
|
reqHeaders.Set("Authorization", "Bearer "+c.token)
|
||||||
|
case privateToken:
|
||||||
|
reqHeaders.Set("PRIVATE-TOKEN", c.token)
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.UserAgent != "" {
|
||||||
|
reqHeaders.Set("User-Agent", c.UserAgent)
|
||||||
|
}
|
||||||
|
|
||||||
|
var body interface{}
|
||||||
|
switch {
|
||||||
|
case method == "POST" || method == "PUT":
|
||||||
|
reqHeaders.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
if opt != nil {
|
if opt != nil {
|
||||||
|
body, err = json.Marshal(opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case opt != nil:
|
||||||
q, err := query.Values(opt)
|
q, err := query.Values(opt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -577,53 +785,23 @@ func (c *Client) NewRequest(method, path string, opt interface{}, options []Opti
|
||||||
u.RawQuery = q.Encode()
|
u.RawQuery = q.Encode()
|
||||||
}
|
}
|
||||||
|
|
||||||
req := &http.Request{
|
req, err := retryablehttp.NewRequest(method, u.String(), body)
|
||||||
Method: method,
|
if err != nil {
|
||||||
URL: &u,
|
return nil, err
|
||||||
Proto: "HTTP/1.1",
|
|
||||||
ProtoMajor: 1,
|
|
||||||
ProtoMinor: 1,
|
|
||||||
Header: make(http.Header),
|
|
||||||
Host: u.Host,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, fn := range options {
|
for _, fn := range options {
|
||||||
if fn == nil {
|
if fn == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := fn(req); err != nil {
|
if err := fn(req); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if method == "POST" || method == "PUT" {
|
// Set the request specific headers.
|
||||||
bodyBytes, err := json.Marshal(opt)
|
for k, v := range reqHeaders {
|
||||||
if err != nil {
|
req.Header[k] = v
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
bodyReader := bytes.NewReader(bodyBytes)
|
|
||||||
|
|
||||||
u.RawQuery = ""
|
|
||||||
req.Body = ioutil.NopCloser(bodyReader)
|
|
||||||
req.GetBody = func() (io.ReadCloser, error) {
|
|
||||||
return ioutil.NopCloser(bodyReader), nil
|
|
||||||
}
|
|
||||||
req.ContentLength = int64(bodyReader.Len())
|
|
||||||
req.Header.Set("Content-Type", "application/json")
|
|
||||||
}
|
|
||||||
|
|
||||||
req.Header.Set("Accept", "application/json")
|
|
||||||
|
|
||||||
switch c.authType {
|
|
||||||
case basicAuth, oAuthToken:
|
|
||||||
req.Header.Set("Authorization", "Bearer "+c.token)
|
|
||||||
case privateToken:
|
|
||||||
req.Header.Set("PRIVATE-TOKEN", c.token)
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.UserAgent != "" {
|
|
||||||
req.Header.Set("User-Agent", c.UserAgent)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return req, nil
|
return req, nil
|
||||||
|
@ -691,7 +869,16 @@ func (r *Response) populatePageValues() {
|
||||||
// error if an API error has occurred. If v implements the io.Writer
|
// error if an API error has occurred. If v implements the io.Writer
|
||||||
// interface, the raw response body will be written to v, without attempting to
|
// interface, the raw response body will be written to v, without attempting to
|
||||||
// first decode it.
|
// first decode it.
|
||||||
func (c *Client) Do(req *http.Request, v interface{}) (*Response, error) {
|
func (c *Client) Do(req *retryablehttp.Request, v interface{}) (*Response, error) {
|
||||||
|
// If not yet configured, try to configure the rate limiter. Fail
|
||||||
|
// silently as the limiter will be disabled in case of an error.
|
||||||
|
c.configureLimiterOnce.Do(func() { c.configureLimiter() })
|
||||||
|
|
||||||
|
// Wait will block until the limiter can obtain a new token.
|
||||||
|
if err := c.limiter.Wait(req.Context()); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
resp, err := c.client.Do(req)
|
resp, err := c.client.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -710,8 +897,8 @@ func (c *Client) Do(req *http.Request, v interface{}) (*Response, error) {
|
||||||
|
|
||||||
err = CheckResponse(resp)
|
err = CheckResponse(resp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// even though there was an error, we still return the response
|
// Even though there was an error, we still return the response
|
||||||
// in case the caller wants to inspect it further
|
// in case the caller wants to inspect it further.
|
||||||
return response, err
|
return response, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -826,32 +1013,6 @@ func parseError(raw interface{}) string {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// OptionFunc can be passed to all API requests to make the API call as if you were
|
|
||||||
// another user, provided your private token is from an administrator account.
|
|
||||||
//
|
|
||||||
// GitLab docs: https://docs.gitlab.com/ce/api/README.html#sudo
|
|
||||||
type OptionFunc func(*http.Request) error
|
|
||||||
|
|
||||||
// WithSudo takes either a username or user ID and sets the SUDO request header
|
|
||||||
func WithSudo(uid interface{}) OptionFunc {
|
|
||||||
return func(req *http.Request) error {
|
|
||||||
user, err := parseID(uid)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
req.Header.Set("SUDO", user)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithContext runs the request with the provided context
|
|
||||||
func WithContext(ctx context.Context) OptionFunc {
|
|
||||||
return func(req *http.Request) error {
|
|
||||||
*req = *req.WithContext(ctx)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bool is a helper routine that allocates a new bool value
|
// Bool is a helper routine that allocates a new bool value
|
||||||
// to store v and returns a pointer to it.
|
// to store v and returns a pointer to it.
|
||||||
func Bool(v bool) *bool {
|
func Bool(v bool) *bool {
|
||||||
|
@ -901,6 +1062,14 @@ func BuildState(v BuildStateValue) *BuildStateValue {
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeploymentStatus is a helper routine that allocates a new
|
||||||
|
// DeploymentStatusValue to store v and returns a pointer to it.
|
||||||
|
func DeploymentStatus(v DeploymentStatusValue) *DeploymentStatusValue {
|
||||||
|
p := new(DeploymentStatusValue)
|
||||||
|
*p = v
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
// NotificationLevel is a helper routine that allocates a new NotificationLevelValue
|
// NotificationLevel is a helper routine that allocates a new NotificationLevelValue
|
||||||
// to store v and returns a pointer to it.
|
// to store v and returns a pointer to it.
|
||||||
func NotificationLevel(v NotificationLevelValue) *NotificationLevelValue {
|
func NotificationLevel(v NotificationLevelValue) *NotificationLevelValue {
|
||||||
|
@ -925,6 +1094,22 @@ func Visibility(v VisibilityValue) *VisibilityValue {
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ProjectCreationLevel is a helper routine that allocates a new ProjectCreationLevelValue
|
||||||
|
// to store v and returns a pointer to it.
|
||||||
|
func ProjectCreationLevel(v ProjectCreationLevelValue) *ProjectCreationLevelValue {
|
||||||
|
p := new(ProjectCreationLevelValue)
|
||||||
|
*p = v
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
// SubGroupCreationLevel is a helper routine that allocates a new SubGroupCreationLevelValue
|
||||||
|
// to store v and returns a pointer to it.
|
||||||
|
func SubGroupCreationLevel(v SubGroupCreationLevelValue) *SubGroupCreationLevelValue {
|
||||||
|
p := new(SubGroupCreationLevelValue)
|
||||||
|
*p = v
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
// MergeMethod is a helper routine that allocates a new MergeMethod
|
// MergeMethod is a helper routine that allocates a new MergeMethod
|
||||||
// to sotre v and returns a pointer to it.
|
// to sotre v and returns a pointer to it.
|
||||||
func MergeMethod(v MergeMethodValue) *MergeMethodValue {
|
func MergeMethod(v MergeMethodValue) *MergeMethodValue {
|
||||||
|
|
|
@ -2,10 +2,13 @@ module github.com/xanzy/go-gitlab
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/google/go-querystring v1.0.0
|
github.com/google/go-querystring v1.0.0
|
||||||
|
github.com/hashicorp/go-cleanhttp v0.5.1
|
||||||
|
github.com/hashicorp/go-retryablehttp v0.6.4
|
||||||
github.com/stretchr/testify v1.4.0
|
github.com/stretchr/testify v1.4.0
|
||||||
golang.org/x/net v0.0.0-20181108082009-03003ca0c849 // indirect
|
golang.org/x/net v0.0.0-20181108082009-03003ca0c849 // indirect
|
||||||
golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288
|
golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288
|
||||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f // indirect
|
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f // indirect
|
||||||
|
golang.org/x/time v0.0.0-20191024005414-555d28b269f0
|
||||||
google.golang.org/appengine v1.3.0 // indirect
|
google.golang.org/appengine v1.3.0 // indirect
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,21 @@
|
||||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
|
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
|
||||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
|
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
|
||||||
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
||||||
|
github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM=
|
||||||
|
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||||
|
github.com/hashicorp/go-hclog v0.9.2 h1:CG6TE5H9/JXsFWJCfoIVpKFIkFe6ysEuHirp4DxCsHI=
|
||||||
|
github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
|
||||||
|
github.com/hashicorp/go-retryablehttp v0.6.4 h1:BbgctKO892xEyOXnGiaAwIoSq1QZ/SS4AhjoAh9DnfY=
|
||||||
|
github.com/hashicorp/go-retryablehttp v0.6.4/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
@ -17,6 +26,8 @@ golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAG
|
||||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f h1:Bl/8QSvNqXvPGPGXa2z5xUTmV7VDcZyvRZ+QQXkXTZQ=
|
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f h1:Bl/8QSvNqXvPGPGXa2z5xUTmV7VDcZyvRZ+QQXkXTZQ=
|
||||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs=
|
||||||
|
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
google.golang.org/appengine v1.3.0 h1:FBSsiFRMz3LBeXIomRnVzrQwSDj4ibvcRexLG0LZGQk=
|
google.golang.org/appengine v1.3.0 h1:FBSsiFRMz3LBeXIomRnVzrQwSDj4ibvcRexLG0LZGQk=
|
||||||
google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
|
|
|
@ -44,7 +44,7 @@ type ListGroupBadgesOptions ListOptions
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ee/api/group_badges.html#list-all-badges-of-a-group
|
// https://docs.gitlab.com/ee/api/group_badges.html#list-all-badges-of-a-group
|
||||||
func (s *GroupBadgesService) ListGroupBadges(gid interface{}, opt *ListGroupBadgesOptions, options ...OptionFunc) ([]*GroupBadge, *Response, error) {
|
func (s *GroupBadgesService) ListGroupBadges(gid interface{}, opt *ListGroupBadgesOptions, options ...RequestOptionFunc) ([]*GroupBadge, *Response, error) {
|
||||||
group, err := parseID(gid)
|
group, err := parseID(gid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -69,7 +69,7 @@ func (s *GroupBadgesService) ListGroupBadges(gid interface{}, opt *ListGroupBadg
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ee/api/group_badges.html#get-a-badge-of-a-group
|
// https://docs.gitlab.com/ee/api/group_badges.html#get-a-badge-of-a-group
|
||||||
func (s *GroupBadgesService) GetGroupBadge(gid interface{}, badge int, options ...OptionFunc) (*GroupBadge, *Response, error) {
|
func (s *GroupBadgesService) GetGroupBadge(gid interface{}, badge int, options ...RequestOptionFunc) (*GroupBadge, *Response, error) {
|
||||||
group, err := parseID(gid)
|
group, err := parseID(gid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -103,7 +103,7 @@ type AddGroupBadgeOptions struct {
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ee/api/group_badges.html#add-a-badge-to-a-group
|
// https://docs.gitlab.com/ee/api/group_badges.html#add-a-badge-to-a-group
|
||||||
func (s *GroupBadgesService) AddGroupBadge(gid interface{}, opt *AddGroupBadgeOptions, options ...OptionFunc) (*GroupBadge, *Response, error) {
|
func (s *GroupBadgesService) AddGroupBadge(gid interface{}, opt *AddGroupBadgeOptions, options ...RequestOptionFunc) (*GroupBadge, *Response, error) {
|
||||||
group, err := parseID(gid)
|
group, err := parseID(gid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -137,7 +137,7 @@ type EditGroupBadgeOptions struct {
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ee/api/group_badges.html#edit-a-badge-of-a-group
|
// https://docs.gitlab.com/ee/api/group_badges.html#edit-a-badge-of-a-group
|
||||||
func (s *GroupBadgesService) EditGroupBadge(gid interface{}, badge int, opt *EditGroupBadgeOptions, options ...OptionFunc) (*GroupBadge, *Response, error) {
|
func (s *GroupBadgesService) EditGroupBadge(gid interface{}, badge int, opt *EditGroupBadgeOptions, options ...RequestOptionFunc) (*GroupBadge, *Response, error) {
|
||||||
group, err := parseID(gid)
|
group, err := parseID(gid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -162,7 +162,7 @@ func (s *GroupBadgesService) EditGroupBadge(gid interface{}, badge int, opt *Edi
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ee/api/group_badges.html#remove-a-badge-from-a-group
|
// https://docs.gitlab.com/ee/api/group_badges.html#remove-a-badge-from-a-group
|
||||||
func (s *GroupBadgesService) DeleteGroupBadge(gid interface{}, badge int, options ...OptionFunc) (*Response, error) {
|
func (s *GroupBadgesService) DeleteGroupBadge(gid interface{}, badge int, options ...RequestOptionFunc) (*Response, error) {
|
||||||
group, err := parseID(gid)
|
group, err := parseID(gid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -191,7 +191,7 @@ type GroupBadgePreviewOptions struct {
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ee/api/group_badges.html#preview-a-badge-from-a-group
|
// https://docs.gitlab.com/ee/api/group_badges.html#preview-a-badge-from-a-group
|
||||||
func (s *GroupBadgesService) PreviewGroupBadge(gid interface{}, opt *GroupBadgePreviewOptions, options ...OptionFunc) (*GroupBadge, *Response, error) {
|
func (s *GroupBadgesService) PreviewGroupBadge(gid interface{}, opt *GroupBadgePreviewOptions, options ...RequestOptionFunc) (*GroupBadge, *Response, error) {
|
||||||
group, err := parseID(gid)
|
group, err := parseID(gid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
|
|
@ -56,7 +56,7 @@ type ListGroupIssueBoardsOptions ListOptions
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/group_boards.html#group-board
|
// https://docs.gitlab.com/ce/api/group_boards.html#group-board
|
||||||
func (s *GroupIssueBoardsService) ListGroupIssueBoards(gid interface{}, opt *ListGroupIssueBoardsOptions, options ...OptionFunc) ([]*GroupIssueBoard, *Response, error) {
|
func (s *GroupIssueBoardsService) ListGroupIssueBoards(gid interface{}, opt *ListGroupIssueBoardsOptions, options ...RequestOptionFunc) ([]*GroupIssueBoard, *Response, error) {
|
||||||
group, err := parseID(gid)
|
group, err := parseID(gid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -77,11 +77,45 @@ func (s *GroupIssueBoardsService) ListGroupIssueBoards(gid interface{}, opt *Lis
|
||||||
return gs, resp, err
|
return gs, resp, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CreateGroupIssueBoardOptions represents the available
|
||||||
|
// CreateGroupIssueBoard() options.
|
||||||
|
//
|
||||||
|
// GitLab API docs:
|
||||||
|
// https://docs.gitlab.com/ce/api/group_boards.html#create-a-group-issue-board-premium
|
||||||
|
type CreateGroupIssueBoardOptions struct {
|
||||||
|
Name *string `url:"name" json:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateGroupIssueBoard creates a new issue board.
|
||||||
|
//
|
||||||
|
// GitLab API docs:
|
||||||
|
// https://docs.gitlab.com/ce/api/group_boards.html#create-a-group-issue-board-premium
|
||||||
|
func (s *GroupIssueBoardsService) CreateGroupIssueBoard(gid interface{}, opt *CreateGroupIssueBoardOptions, options ...RequestOptionFunc) (*GroupIssueBoard, *Response, error) {
|
||||||
|
group, err := parseID(gid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
u := fmt.Sprintf("groups/%s/boards", pathEscape(group))
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest("POST", u, opt, options)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
gib := new(GroupIssueBoard)
|
||||||
|
resp, err := s.client.Do(req, gib)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return gib, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
// GetGroupIssueBoard gets a single issue board of a group.
|
// GetGroupIssueBoard gets a single issue board of a group.
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/group_boards.html#single-board
|
// https://docs.gitlab.com/ce/api/group_boards.html#single-board
|
||||||
func (s *GroupIssueBoardsService) GetGroupIssueBoard(gid interface{}, board int, options ...OptionFunc) (*GroupIssueBoard, *Response, error) {
|
func (s *GroupIssueBoardsService) GetGroupIssueBoard(gid interface{}, board int, options ...RequestOptionFunc) (*GroupIssueBoard, *Response, error) {
|
||||||
group, err := parseID(gid)
|
group, err := parseID(gid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -102,6 +136,62 @@ func (s *GroupIssueBoardsService) GetGroupIssueBoard(gid interface{}, board int,
|
||||||
return gib, resp, err
|
return gib, resp, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UpdateGroupIssueBoardOptions represents a group issue board.
|
||||||
|
//
|
||||||
|
// GitLab API docs:
|
||||||
|
// https://docs.gitlab.com/ce/api/group_boards.html#update-a-group-issue-board-premium
|
||||||
|
type UpdateGroupIssueBoardOptions struct {
|
||||||
|
Name *string `url:"name,omitempty" json:"name,omitempty"`
|
||||||
|
AssigneeID *int `url:"assignee_id,omitempty" json:"assignee_id,omitempty"`
|
||||||
|
MilestoneID *int `url:"milestone_id,omitempty" json:"milestone_id,omitempty"`
|
||||||
|
Labels *Labels `url:"labels,omitempty" json:"labels,omitempty"`
|
||||||
|
Weight *int `url:"weight,omitempty" json:"weight,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateIssueBoard updates a single issue board of a group.
|
||||||
|
//
|
||||||
|
// GitLab API docs:
|
||||||
|
// https://docs.gitlab.com/ce/api/group_boards.html#update-a-group-issue-board-premium
|
||||||
|
func (s *GroupIssueBoardsService) UpdateIssueBoard(gid interface{}, board int, opt *UpdateGroupIssueBoardOptions, options ...RequestOptionFunc) (*GroupIssueBoard, *Response, error) {
|
||||||
|
group, err := parseID(gid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
u := fmt.Sprintf("groups/%s/boards/%d", pathEscape(group), board)
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest("PUT", u, opt, options)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
gib := new(GroupIssueBoard)
|
||||||
|
resp, err := s.client.Do(req, gib)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return gib, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteIssueBoard delete a single issue board of a group.
|
||||||
|
//
|
||||||
|
// GitLab API docs:
|
||||||
|
// https://docs.gitlab.com/ce/api/group_boards.html#delete-a-group-issue-board-premium
|
||||||
|
func (s *GroupIssueBoardsService) DeleteIssueBoard(gid interface{}, board int, options ...RequestOptionFunc) (*Response, error) {
|
||||||
|
group, err := parseID(gid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
u := fmt.Sprintf("groups/%s/boards/%d", pathEscape(group), board)
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest("DELETE", u, nil, options)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.client.Do(req, nil)
|
||||||
|
}
|
||||||
|
|
||||||
// ListGroupIssueBoardListsOptions represents the available
|
// ListGroupIssueBoardListsOptions represents the available
|
||||||
// ListGroupIssueBoardLists() options.
|
// ListGroupIssueBoardLists() options.
|
||||||
//
|
//
|
||||||
|
@ -113,7 +203,7 @@ type ListGroupIssueBoardListsOptions ListOptions
|
||||||
// backlog and closed lists.
|
// backlog and closed lists.
|
||||||
//
|
//
|
||||||
// GitLab API docs: https://docs.gitlab.com/ce/api/group_boards.html#list-board-lists
|
// GitLab API docs: https://docs.gitlab.com/ce/api/group_boards.html#list-board-lists
|
||||||
func (s *GroupIssueBoardsService) ListGroupIssueBoardLists(gid interface{}, board int, opt *ListGroupIssueBoardListsOptions, options ...OptionFunc) ([]*BoardList, *Response, error) {
|
func (s *GroupIssueBoardsService) ListGroupIssueBoardLists(gid interface{}, board int, opt *ListGroupIssueBoardListsOptions, options ...RequestOptionFunc) ([]*BoardList, *Response, error) {
|
||||||
group, err := parseID(gid)
|
group, err := parseID(gid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -138,7 +228,7 @@ func (s *GroupIssueBoardsService) ListGroupIssueBoardLists(gid interface{}, boar
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/group_boards.html#single-board-list
|
// https://docs.gitlab.com/ce/api/group_boards.html#single-board-list
|
||||||
func (s *GroupIssueBoardsService) GetGroupIssueBoardList(gid interface{}, board, list int, options ...OptionFunc) (*BoardList, *Response, error) {
|
func (s *GroupIssueBoardsService) GetGroupIssueBoardList(gid interface{}, board, list int, options ...RequestOptionFunc) (*BoardList, *Response, error) {
|
||||||
group, err := parseID(gid)
|
group, err := parseID(gid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -176,7 +266,7 @@ type CreateGroupIssueBoardListOptions struct {
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/group_boards.html#new-board-list
|
// https://docs.gitlab.com/ce/api/group_boards.html#new-board-list
|
||||||
func (s *GroupIssueBoardsService) CreateGroupIssueBoardList(gid interface{}, board int, opt *CreateGroupIssueBoardListOptions, options ...OptionFunc) (*BoardList, *Response, error) {
|
func (s *GroupIssueBoardsService) CreateGroupIssueBoardList(gid interface{}, board int, opt *CreateGroupIssueBoardListOptions, options ...RequestOptionFunc) (*BoardList, *Response, error) {
|
||||||
group, err := parseID(gid)
|
group, err := parseID(gid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -211,7 +301,7 @@ type UpdateGroupIssueBoardListOptions struct {
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/group_boards.html#edit-board-list
|
// https://docs.gitlab.com/ce/api/group_boards.html#edit-board-list
|
||||||
func (s *GroupIssueBoardsService) UpdateIssueBoardList(gid interface{}, board, list int, opt *UpdateGroupIssueBoardListOptions, options ...OptionFunc) ([]*BoardList, *Response, error) {
|
func (s *GroupIssueBoardsService) UpdateIssueBoardList(gid interface{}, board, list int, opt *UpdateGroupIssueBoardListOptions, options ...RequestOptionFunc) ([]*BoardList, *Response, error) {
|
||||||
group, err := parseID(gid)
|
group, err := parseID(gid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -241,7 +331,7 @@ func (s *GroupIssueBoardsService) UpdateIssueBoardList(gid interface{}, board, l
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/group_boards.html#delete-a-board-list
|
// https://docs.gitlab.com/ce/api/group_boards.html#delete-a-board-list
|
||||||
func (s *GroupIssueBoardsService) DeleteGroupIssueBoardList(gid interface{}, board, list int, options ...OptionFunc) (*Response, error) {
|
func (s *GroupIssueBoardsService) DeleteGroupIssueBoardList(gid interface{}, board, list int, options ...RequestOptionFunc) (*Response, error) {
|
||||||
group, err := parseID(gid)
|
group, err := parseID(gid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -44,6 +44,7 @@ type GroupCluster struct {
|
||||||
ClusterType string `json:"cluster_type"`
|
ClusterType string `json:"cluster_type"`
|
||||||
User *User `json:"user"`
|
User *User `json:"user"`
|
||||||
PlatformKubernetes *PlatformKubernetes `json:"platform_kubernetes"`
|
PlatformKubernetes *PlatformKubernetes `json:"platform_kubernetes"`
|
||||||
|
ManagementProject *ManagementProject `json:"management_project"`
|
||||||
Group *Group `json:"group"`
|
Group *Group `json:"group"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,7 +56,7 @@ func (v GroupCluster) String() string {
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ee/api/group_clusters.html#list-group-clusters
|
// https://docs.gitlab.com/ee/api/group_clusters.html#list-group-clusters
|
||||||
func (s *GroupClustersService) ListClusters(pid interface{}, options ...OptionFunc) ([]*GroupCluster, *Response, error) {
|
func (s *GroupClustersService) ListClusters(pid interface{}, options ...RequestOptionFunc) ([]*GroupCluster, *Response, error) {
|
||||||
group, err := parseID(pid)
|
group, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -80,7 +81,7 @@ func (s *GroupClustersService) ListClusters(pid interface{}, options ...OptionFu
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ee/api/group_clusters.html#get-a-single-group-cluster
|
// https://docs.gitlab.com/ee/api/group_clusters.html#get-a-single-group-cluster
|
||||||
func (s *GroupClustersService) GetCluster(pid interface{}, cluster int, options ...OptionFunc) (*GroupCluster, *Response, error) {
|
func (s *GroupClustersService) GetCluster(pid interface{}, cluster int, options ...RequestOptionFunc) (*GroupCluster, *Response, error) {
|
||||||
group, err := parseID(pid)
|
group, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -108,6 +109,7 @@ func (s *GroupClustersService) GetCluster(pid interface{}, cluster int, options
|
||||||
type AddGroupClusterOptions struct {
|
type AddGroupClusterOptions struct {
|
||||||
Name *string `url:"name,omitempty" json:"name,omitempty"`
|
Name *string `url:"name,omitempty" json:"name,omitempty"`
|
||||||
Domain *string `url:"domain,omitempty" json:"domain,omitempty"`
|
Domain *string `url:"domain,omitempty" json:"domain,omitempty"`
|
||||||
|
ManagementProjectID *string `url:"management_project_id,omitempty" json:"management_project_id,omitempty"`
|
||||||
Enabled *bool `url:"enabled,omitempty" json:"enabled,omitempty"`
|
Enabled *bool `url:"enabled,omitempty" json:"enabled,omitempty"`
|
||||||
Managed *bool `url:"managed,omitempty" json:"managed,omitempty"`
|
Managed *bool `url:"managed,omitempty" json:"managed,omitempty"`
|
||||||
EnvironmentScope *string `url:"environment_scope,omitempty" json:"environment_scope,omitempty"`
|
EnvironmentScope *string `url:"environment_scope,omitempty" json:"environment_scope,omitempty"`
|
||||||
|
@ -127,7 +129,7 @@ type AddGroupPlatformKubernetesOptions struct {
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ee/api/group_clusters.html#add-existing-cluster-to-group
|
// https://docs.gitlab.com/ee/api/group_clusters.html#add-existing-cluster-to-group
|
||||||
func (s *GroupClustersService) AddCluster(pid interface{}, opt *AddGroupClusterOptions, options ...OptionFunc) (*GroupCluster, *Response, error) {
|
func (s *GroupClustersService) AddCluster(pid interface{}, opt *AddGroupClusterOptions, options ...RequestOptionFunc) (*GroupCluster, *Response, error) {
|
||||||
group, err := parseID(pid)
|
group, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -157,6 +159,7 @@ type EditGroupClusterOptions struct {
|
||||||
Domain *string `url:"domain,omitempty" json:"domain,omitempty"`
|
Domain *string `url:"domain,omitempty" json:"domain,omitempty"`
|
||||||
EnvironmentScope *string `url:"environment_scope,omitempty" json:"environment_scope,omitempty"`
|
EnvironmentScope *string `url:"environment_scope,omitempty" json:"environment_scope,omitempty"`
|
||||||
PlatformKubernetes *EditGroupPlatformKubernetesOptions `url:"platform_kubernetes_attributes,omitempty" json:"platform_kubernetes_attributes,omitempty"`
|
PlatformKubernetes *EditGroupPlatformKubernetesOptions `url:"platform_kubernetes_attributes,omitempty" json:"platform_kubernetes_attributes,omitempty"`
|
||||||
|
ManagementProjectID *string `url:"management_project_id,omitempty" json:"management_project_id,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// EditGroupPlatformKubernetesOptions represents the available PlatformKubernetes options for editing.
|
// EditGroupPlatformKubernetesOptions represents the available PlatformKubernetes options for editing.
|
||||||
|
@ -170,7 +173,7 @@ type EditGroupPlatformKubernetesOptions struct {
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ee/api/group_clusters.html#edit-group-cluster
|
// https://docs.gitlab.com/ee/api/group_clusters.html#edit-group-cluster
|
||||||
func (s *GroupClustersService) EditCluster(pid interface{}, cluster int, opt *EditGroupClusterOptions, options ...OptionFunc) (*GroupCluster, *Response, error) {
|
func (s *GroupClustersService) EditCluster(pid interface{}, cluster int, opt *EditGroupClusterOptions, options ...RequestOptionFunc) (*GroupCluster, *Response, error) {
|
||||||
group, err := parseID(pid)
|
group, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -195,7 +198,7 @@ func (s *GroupClustersService) EditCluster(pid interface{}, cluster int, opt *Ed
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ee/api/group_clusters.html#delete-group-cluster
|
// https://docs.gitlab.com/ee/api/group_clusters.html#delete-group-cluster
|
||||||
func (s *GroupClustersService) DeleteCluster(pid interface{}, cluster int, options ...OptionFunc) (*Response, error) {
|
func (s *GroupClustersService) DeleteCluster(pid interface{}, cluster int, options ...RequestOptionFunc) (*Response, error) {
|
||||||
group, err := parseID(pid)
|
group, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -0,0 +1,199 @@
|
||||||
|
//
|
||||||
|
// Copyright 2020, Eric Stevens
|
||||||
|
//
|
||||||
|
// 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 gitlab
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GroupHook represents a GitLab group hook.
|
||||||
|
//
|
||||||
|
// GitLab API docs: https://docs.gitlab.com/ce/api/groups.html#list-group-hooks
|
||||||
|
type GroupHook struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
URL string `json:"url"`
|
||||||
|
GroupID int `json:"group_id"`
|
||||||
|
PushEvents bool `json:"push_events"`
|
||||||
|
IssuesEvents bool `json:"issues_events"`
|
||||||
|
ConfidentialIssuesEvents bool `json:"confidential_issues_events"`
|
||||||
|
ConfidentialNoteEvents bool `json:"confidential_note_events"`
|
||||||
|
MergeRequestsEvents bool `json:"merge_requests_events"`
|
||||||
|
TagPushEvents bool `json:"tag_push_events"`
|
||||||
|
NoteEvents bool `json:"note_events"`
|
||||||
|
JobEvents bool `json:"job_events"`
|
||||||
|
PipelineEvents bool `json:"pipeline_events"`
|
||||||
|
WikiPageEvents bool `json:"wiki_page_events"`
|
||||||
|
EnableSSLVerification bool `json:"enable_ssl_verification"`
|
||||||
|
CreatedAt *time.Time `json:"created_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListGroupHooks gets a list of group hooks.
|
||||||
|
//
|
||||||
|
// GitLab API docs: https://docs.gitlab.com/ce/api/groups.html#list-group-hooks
|
||||||
|
func (s *GroupsService) ListGroupHooks(gid interface{}) ([]*GroupHook, *Response, error) {
|
||||||
|
group, err := parseID(gid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
u := fmt.Sprintf("groups/%s/hooks", pathEscape(group))
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest("GET", u, nil, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
var gh []*GroupHook
|
||||||
|
resp, err := s.client.Do(req, &gh)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return gh, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetGroupHook gets a specific hook for a group.
|
||||||
|
//
|
||||||
|
// GitLab API docs:
|
||||||
|
// https://docs.gitlab.com/ce/api/groups.html#get-group-hook
|
||||||
|
func (s *GroupsService) GetGroupHook(pid interface{}, hook int, options ...RequestOptionFunc) (*GroupHook, *Response, error) {
|
||||||
|
group, err := parseID(pid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
u := fmt.Sprintf("groups/%s/hooks/%d", pathEscape(group), hook)
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest("GET", u, nil, options)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
gh := new(GroupHook)
|
||||||
|
resp, err := s.client.Do(req, gh)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return gh, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddGroupHookOptions represents the available AddGroupHook() options.
|
||||||
|
//
|
||||||
|
// GitLab API docs: https://docs.gitlab.com/ee/api/groups.html#add-group-hook
|
||||||
|
type AddGroupHookOptions struct {
|
||||||
|
URL *string `url:"url,omitempty" json:"url,omitempty"`
|
||||||
|
PushEvents *bool `url:"push_events,omitempty" json:"push_events,omitempty"`
|
||||||
|
IssuesEvents *bool `url:"issues_events,omitempty" json:"issues_events,omitempty"`
|
||||||
|
ConfidentialIssuesEvents *bool `url:"confidential_issues_events,omitempty" json:"confidential_issues_events,omitempty"`
|
||||||
|
ConfidentialNoteEvents *bool `url:"confidential_note_events,omitempty" json:"confidential_note_events,omitempty"`
|
||||||
|
MergeRequestsEvents *bool `url:"merge_requests_events,omitempty" json:"merge_requests_events,omitempty"`
|
||||||
|
TagPushEvents *bool `url:"tag_push_events,omitempty" json:"tag_push_events,omitempty"`
|
||||||
|
NoteEvents *bool `url:"note_events,omitempty" json:"note_events,omitempty"`
|
||||||
|
JobEvents *bool `url:"job_events,omitempty" json:"job_events,omitempty"`
|
||||||
|
PipelineEvents *bool `url:"pipeline_events,omitempty" json:"pipeline_events,omitempty"`
|
||||||
|
WikiPageEvents *bool `url:"wiki_page_events,omitempty" json:"wiki_page_events,omitempty"`
|
||||||
|
EnableSSLVerification *bool `url:"enable_ssl_verification,omitempty" json:"enable_ssl_verification,omitempty"`
|
||||||
|
Token *string `url:"token,omitempty" json:"token,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddGroupHook create a new group scoped webhook.
|
||||||
|
//
|
||||||
|
// GitLab API docs: https://docs.gitlab.com/ee/api/groups.html#add-group-hook
|
||||||
|
func (s *GroupsService) AddGroupHook(gid interface{}, opt *AddGroupHookOptions, options ...RequestOptionFunc) (*GroupHook, *Response, error) {
|
||||||
|
group, err := parseID(gid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
u := fmt.Sprintf("groups/%s/hooks", pathEscape(group))
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest("POST", u, opt, options)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
gh := new(GroupHook)
|
||||||
|
resp, err := s.client.Do(req, gh)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return gh, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// EditGroupHookOptions represents the available EditGroupHook() options.
|
||||||
|
//
|
||||||
|
// GitLab API docs:
|
||||||
|
// https://docs.gitlab.com/ce/api/groups.html#edit-group-hook
|
||||||
|
type EditGroupHookOptions struct {
|
||||||
|
URL *string `url:"url,omitempty" json:"url,omitempty"`
|
||||||
|
PushEvents *bool `url:"push_events,omitempty" json:"push_events,omitempty"`
|
||||||
|
IssuesEvents *bool `url:"issues_events,omitempty" json:"issues_events,omitempty"`
|
||||||
|
ConfidentialIssuesEvents *bool `url:"confidential_issues_events,omitempty" json:"confidential_issues_events,omitempty"`
|
||||||
|
ConfidentialNoteEvents *bool `url:"confidential_note_events,omitempty" json:"confidential_note_events,omitempty"`
|
||||||
|
MergeRequestsEvents *bool `url:"merge_requests_events,omitempty" json:"merge_requests_events,omitempty"`
|
||||||
|
TagPushEvents *bool `url:"tag_push_events,omitempty" json:"tag_push_events,omitempty"`
|
||||||
|
NoteEvents *bool `url:"note_events,omitempty" json:"note_events,omitempty"`
|
||||||
|
JobEvents *bool `url:"job_events,omitempty" json:"job_events,omitempty"`
|
||||||
|
PipelineEvents *bool `url:"pipeline_events,omitempty" json:"pipeline_events,omitempty"`
|
||||||
|
WikiPageEvents *bool `url:"wiki_page_events,omitempty" json:"wiki_page_events,omitempty"`
|
||||||
|
EnableSSLVerification *bool `url:"enable_ssl_verification,omitempty" json:"enable_ssl_verification,omitempty"`
|
||||||
|
Token *string `url:"token,omitempty" json:"token,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// EditGroupHook edits a hook for a specified group.
|
||||||
|
//
|
||||||
|
// Gitlab API docs:
|
||||||
|
// https://docs.gitlab.com/ce/api/groups.html#edit-group-hook
|
||||||
|
func (s *GroupsService) EditGroupHook(pid interface{}, hook int, opt *EditGroupHookOptions, options ...RequestOptionFunc) (*GroupHook, *Response, error) {
|
||||||
|
group, err := parseID(pid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
u := fmt.Sprintf("groups/%s/hooks/%d", pathEscape(group), hook)
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest("PUT", u, opt, options)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
gh := new(GroupHook)
|
||||||
|
resp, err := s.client.Do(req, gh)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return gh, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteGroupHook removes a hook from a group. This is an idempotent
|
||||||
|
// method and can be called multiple times.
|
||||||
|
//
|
||||||
|
// GitLab API docs:
|
||||||
|
// https://docs.gitlab.com/ce/api/groups.html#delete-group-hook
|
||||||
|
func (s *GroupsService) DeleteGroupHook(pid interface{}, hook int, options ...RequestOptionFunc) (*Response, error) {
|
||||||
|
group, err := parseID(pid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
u := fmt.Sprintf("groups/%s/hooks/%d", pathEscape(group), hook)
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest("DELETE", u, nil, options)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.client.Do(req, nil)
|
||||||
|
}
|
|
@ -30,7 +30,7 @@ type ListGroupLabelsOptions ListOptions
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/group_labels.html#list-group-labels
|
// https://docs.gitlab.com/ce/api/group_labels.html#list-group-labels
|
||||||
func (s *GroupLabelsService) ListGroupLabels(gid interface{}, opt *ListGroupLabelsOptions, options ...OptionFunc) ([]*GroupLabel, *Response, error) {
|
func (s *GroupLabelsService) ListGroupLabels(gid interface{}, opt *ListGroupLabelsOptions, options ...RequestOptionFunc) ([]*GroupLabel, *Response, error) {
|
||||||
group, err := parseID(gid)
|
group, err := parseID(gid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -62,7 +62,7 @@ type CreateGroupLabelOptions CreateLabelOptions
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/group_labels.html#create-a-new-group-label
|
// https://docs.gitlab.com/ce/api/group_labels.html#create-a-new-group-label
|
||||||
func (s *GroupLabelsService) CreateGroupLabel(gid interface{}, opt *CreateGroupLabelOptions, options ...OptionFunc) (*GroupLabel, *Response, error) {
|
func (s *GroupLabelsService) CreateGroupLabel(gid interface{}, opt *CreateGroupLabelOptions, options ...RequestOptionFunc) (*GroupLabel, *Response, error) {
|
||||||
group, err := parseID(gid)
|
group, err := parseID(gid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -92,7 +92,7 @@ type DeleteGroupLabelOptions DeleteLabelOptions
|
||||||
// DeleteGroupLabel deletes a group label given by its name.
|
// DeleteGroupLabel deletes a group label given by its name.
|
||||||
//
|
//
|
||||||
// GitLab API docs: https://docs.gitlab.com/ce/api/labels.html#delete-a-label
|
// GitLab API docs: https://docs.gitlab.com/ce/api/labels.html#delete-a-label
|
||||||
func (s *GroupLabelsService) DeleteGroupLabel(gid interface{}, opt *DeleteGroupLabelOptions, options ...OptionFunc) (*Response, error) {
|
func (s *GroupLabelsService) DeleteGroupLabel(gid interface{}, opt *DeleteGroupLabelOptions, options ...RequestOptionFunc) (*Response, error) {
|
||||||
group, err := parseID(gid)
|
group, err := parseID(gid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -118,7 +118,7 @@ type UpdateGroupLabelOptions UpdateLabelOptions
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/group_labels.html#update-a-group-label
|
// https://docs.gitlab.com/ce/api/group_labels.html#update-a-group-label
|
||||||
func (s *GroupLabelsService) UpdateGroupLabel(gid interface{}, opt *UpdateGroupLabelOptions, options ...OptionFunc) (*GroupLabel, *Response, error) {
|
func (s *GroupLabelsService) UpdateGroupLabel(gid interface{}, opt *UpdateGroupLabelOptions, options ...RequestOptionFunc) (*GroupLabel, *Response, error) {
|
||||||
group, err := parseID(gid)
|
group, err := parseID(gid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -145,7 +145,7 @@ func (s *GroupLabelsService) UpdateGroupLabel(gid interface{}, opt *UpdateGroupL
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/group_labels.html#subscribe-to-a-group-label
|
// https://docs.gitlab.com/ce/api/group_labels.html#subscribe-to-a-group-label
|
||||||
func (s *GroupLabelsService) SubscribeToGroupLabel(gid interface{}, labelID interface{}, options ...OptionFunc) (*GroupLabel, *Response, error) {
|
func (s *GroupLabelsService) SubscribeToGroupLabel(gid interface{}, labelID interface{}, options ...RequestOptionFunc) (*GroupLabel, *Response, error) {
|
||||||
group, err := parseID(gid)
|
group, err := parseID(gid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -176,7 +176,7 @@ func (s *GroupLabelsService) SubscribeToGroupLabel(gid interface{}, labelID inte
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/group_labels.html#unsubscribe-from-a-group-label
|
// https://docs.gitlab.com/ce/api/group_labels.html#unsubscribe-from-a-group-label
|
||||||
func (s *GroupLabelsService) UnsubscribeFromGroupLabel(gid interface{}, labelID interface{}, options ...OptionFunc) (*Response, error) {
|
func (s *GroupLabelsService) UnsubscribeFromGroupLabel(gid interface{}, labelID interface{}, options ...RequestOptionFunc) (*Response, error) {
|
||||||
group, err := parseID(gid)
|
group, err := parseID(gid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -28,6 +28,17 @@ type GroupMembersService struct {
|
||||||
client *Client
|
client *Client
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GroupMemberSAMLIdentity represents the SAML Identity link for the group member.
|
||||||
|
//
|
||||||
|
// GitLab API docs: https://docs.gitlab.com/ce/api/members.html#list-all-members-of-a-group-or-project
|
||||||
|
// Gitlab MR for API change: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/20357
|
||||||
|
// Gitlab MR for API Doc change: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/25652
|
||||||
|
type GroupMemberSAMLIdentity struct {
|
||||||
|
ExternUID string `json:"extern_uid"`
|
||||||
|
Provider string `json:"provider"`
|
||||||
|
SAMLProviderID int `json:"saml_provider_id"`
|
||||||
|
}
|
||||||
|
|
||||||
// GroupMember represents a GitLab group member.
|
// GroupMember represents a GitLab group member.
|
||||||
//
|
//
|
||||||
// GitLab API docs: https://docs.gitlab.com/ce/api/members.html
|
// GitLab API docs: https://docs.gitlab.com/ce/api/members.html
|
||||||
|
@ -40,6 +51,7 @@ type GroupMember struct {
|
||||||
WebURL string `json:"web_url"`
|
WebURL string `json:"web_url"`
|
||||||
ExpiresAt *ISOTime `json:"expires_at"`
|
ExpiresAt *ISOTime `json:"expires_at"`
|
||||||
AccessLevel AccessLevelValue `json:"access_level"`
|
AccessLevel AccessLevelValue `json:"access_level"`
|
||||||
|
GroupSAMLIdentity *GroupMemberSAMLIdentity `json:"group_saml_identity"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListGroupMembersOptions represents the available ListGroupMembers() and
|
// ListGroupMembersOptions represents the available ListGroupMembers() and
|
||||||
|
@ -57,7 +69,7 @@ type ListGroupMembersOptions struct {
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/members.html#list-all-members-of-a-group-or-project
|
// https://docs.gitlab.com/ce/api/members.html#list-all-members-of-a-group-or-project
|
||||||
func (s *GroupsService) ListGroupMembers(gid interface{}, opt *ListGroupMembersOptions, options ...OptionFunc) ([]*GroupMember, *Response, error) {
|
func (s *GroupsService) ListGroupMembers(gid interface{}, opt *ListGroupMembersOptions, options ...RequestOptionFunc) ([]*GroupMember, *Response, error) {
|
||||||
group, err := parseID(gid)
|
group, err := parseID(gid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -83,7 +95,7 @@ func (s *GroupsService) ListGroupMembers(gid interface{}, opt *ListGroupMembersO
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/members.html#list-all-members-of-a-group-or-project-including-inherited-members
|
// https://docs.gitlab.com/ce/api/members.html#list-all-members-of-a-group-or-project-including-inherited-members
|
||||||
func (s *GroupsService) ListAllGroupMembers(gid interface{}, opt *ListGroupMembersOptions, options ...OptionFunc) ([]*GroupMember, *Response, error) {
|
func (s *GroupsService) ListAllGroupMembers(gid interface{}, opt *ListGroupMembersOptions, options ...RequestOptionFunc) ([]*GroupMember, *Response, error) {
|
||||||
group, err := parseID(gid)
|
group, err := parseID(gid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -118,7 +130,7 @@ type AddGroupMemberOptions struct {
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/members.html#get-a-member-of-a-group-or-project
|
// https://docs.gitlab.com/ce/api/members.html#get-a-member-of-a-group-or-project
|
||||||
func (s *GroupMembersService) GetGroupMember(gid interface{}, user int, options ...OptionFunc) (*GroupMember, *Response, error) {
|
func (s *GroupMembersService) GetGroupMember(gid interface{}, user int, options ...RequestOptionFunc) (*GroupMember, *Response, error) {
|
||||||
group, err := parseID(gid)
|
group, err := parseID(gid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -143,7 +155,7 @@ func (s *GroupMembersService) GetGroupMember(gid interface{}, user int, options
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/members.html#add-a-member-to-a-group-or-project
|
// https://docs.gitlab.com/ce/api/members.html#add-a-member-to-a-group-or-project
|
||||||
func (s *GroupMembersService) AddGroupMember(gid interface{}, opt *AddGroupMemberOptions, options ...OptionFunc) (*GroupMember, *Response, error) {
|
func (s *GroupMembersService) AddGroupMember(gid interface{}, opt *AddGroupMemberOptions, options ...RequestOptionFunc) (*GroupMember, *Response, error) {
|
||||||
group, err := parseID(gid)
|
group, err := parseID(gid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -178,7 +190,7 @@ type EditGroupMemberOptions struct {
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/members.html#edit-a-member-of-a-group-or-project
|
// https://docs.gitlab.com/ce/api/members.html#edit-a-member-of-a-group-or-project
|
||||||
func (s *GroupMembersService) EditGroupMember(gid interface{}, user int, opt *EditGroupMemberOptions, options ...OptionFunc) (*GroupMember, *Response, error) {
|
func (s *GroupMembersService) EditGroupMember(gid interface{}, user int, opt *EditGroupMemberOptions, options ...RequestOptionFunc) (*GroupMember, *Response, error) {
|
||||||
group, err := parseID(gid)
|
group, err := parseID(gid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -203,7 +215,7 @@ func (s *GroupMembersService) EditGroupMember(gid interface{}, user int, opt *Ed
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/members.html#remove-a-member-from-a-group-or-project
|
// https://docs.gitlab.com/ce/api/members.html#remove-a-member-from-a-group-or-project
|
||||||
func (s *GroupMembersService) RemoveGroupMember(gid interface{}, user int, options ...OptionFunc) (*Response, error) {
|
func (s *GroupMembersService) RemoveGroupMember(gid interface{}, user int, options ...RequestOptionFunc) (*Response, error) {
|
||||||
group, err := parseID(gid)
|
group, err := parseID(gid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -65,7 +65,7 @@ type ListGroupMilestonesOptions struct {
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/group_milestones.html#list-group-milestones
|
// https://docs.gitlab.com/ce/api/group_milestones.html#list-group-milestones
|
||||||
func (s *GroupMilestonesService) ListGroupMilestones(gid interface{}, opt *ListGroupMilestonesOptions, options ...OptionFunc) ([]*GroupMilestone, *Response, error) {
|
func (s *GroupMilestonesService) ListGroupMilestones(gid interface{}, opt *ListGroupMilestonesOptions, options ...RequestOptionFunc) ([]*GroupMilestone, *Response, error) {
|
||||||
group, err := parseID(gid)
|
group, err := parseID(gid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -90,7 +90,7 @@ func (s *GroupMilestonesService) ListGroupMilestones(gid interface{}, opt *ListG
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/group_milestones.html#get-single-milestone
|
// https://docs.gitlab.com/ce/api/group_milestones.html#get-single-milestone
|
||||||
func (s *GroupMilestonesService) GetGroupMilestone(gid interface{}, milestone int, options ...OptionFunc) (*GroupMilestone, *Response, error) {
|
func (s *GroupMilestonesService) GetGroupMilestone(gid interface{}, milestone int, options ...RequestOptionFunc) (*GroupMilestone, *Response, error) {
|
||||||
group, err := parseID(gid)
|
group, err := parseID(gid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -126,7 +126,7 @@ type CreateGroupMilestoneOptions struct {
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/group_milestones.html#create-new-milestone
|
// https://docs.gitlab.com/ce/api/group_milestones.html#create-new-milestone
|
||||||
func (s *GroupMilestonesService) CreateGroupMilestone(gid interface{}, opt *CreateGroupMilestoneOptions, options ...OptionFunc) (*GroupMilestone, *Response, error) {
|
func (s *GroupMilestonesService) CreateGroupMilestone(gid interface{}, opt *CreateGroupMilestoneOptions, options ...RequestOptionFunc) (*GroupMilestone, *Response, error) {
|
||||||
group, err := parseID(gid)
|
group, err := parseID(gid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -163,7 +163,7 @@ type UpdateGroupMilestoneOptions struct {
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/group_milestones.html#edit-milestone
|
// https://docs.gitlab.com/ce/api/group_milestones.html#edit-milestone
|
||||||
func (s *GroupMilestonesService) UpdateGroupMilestone(gid interface{}, milestone int, opt *UpdateGroupMilestoneOptions, options ...OptionFunc) (*GroupMilestone, *Response, error) {
|
func (s *GroupMilestonesService) UpdateGroupMilestone(gid interface{}, milestone int, opt *UpdateGroupMilestoneOptions, options ...RequestOptionFunc) (*GroupMilestone, *Response, error) {
|
||||||
group, err := parseID(gid)
|
group, err := parseID(gid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -194,7 +194,7 @@ type GetGroupMilestoneIssuesOptions ListOptions
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/group_milestones.html#get-all-issues-assigned-to-a-single-milestone
|
// https://docs.gitlab.com/ce/api/group_milestones.html#get-all-issues-assigned-to-a-single-milestone
|
||||||
func (s *GroupMilestonesService) GetGroupMilestoneIssues(gid interface{}, milestone int, opt *GetGroupMilestoneIssuesOptions, options ...OptionFunc) ([]*Issue, *Response, error) {
|
func (s *GroupMilestonesService) GetGroupMilestoneIssues(gid interface{}, milestone int, opt *GetGroupMilestoneIssuesOptions, options ...RequestOptionFunc) ([]*Issue, *Response, error) {
|
||||||
group, err := parseID(gid)
|
group, err := parseID(gid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -227,7 +227,7 @@ type GetGroupMilestoneMergeRequestsOptions ListOptions
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/group_milestones.html#get-all-merge-requests-assigned-to-a-single-milestone
|
// https://docs.gitlab.com/ce/api/group_milestones.html#get-all-merge-requests-assigned-to-a-single-milestone
|
||||||
func (s *GroupMilestonesService) GetGroupMilestoneMergeRequests(gid interface{}, milestone int, opt *GetGroupMilestoneMergeRequestsOptions, options ...OptionFunc) ([]*MergeRequest, *Response, error) {
|
func (s *GroupMilestonesService) GetGroupMilestoneMergeRequests(gid interface{}, milestone int, opt *GetGroupMilestoneMergeRequestsOptions, options ...RequestOptionFunc) ([]*MergeRequest, *Response, error) {
|
||||||
group, err := parseID(gid)
|
group, err := parseID(gid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
|
|
@ -39,6 +39,7 @@ type GroupVariable struct {
|
||||||
Value string `json:"value"`
|
Value string `json:"value"`
|
||||||
VariableType VariableTypeValue `json:"variable_type"`
|
VariableType VariableTypeValue `json:"variable_type"`
|
||||||
Protected bool `json:"protected"`
|
Protected bool `json:"protected"`
|
||||||
|
Masked bool `json:"masked"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v GroupVariable) String() string {
|
func (v GroupVariable) String() string {
|
||||||
|
@ -56,7 +57,7 @@ type ListGroupVariablesOptions ListOptions
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ee/api/group_level_variables.html#list-group-variables
|
// https://docs.gitlab.com/ee/api/group_level_variables.html#list-group-variables
|
||||||
func (s *GroupVariablesService) ListVariables(gid interface{}, opt *ListGroupVariablesOptions, options ...OptionFunc) ([]*GroupVariable, *Response, error) {
|
func (s *GroupVariablesService) ListVariables(gid interface{}, opt *ListGroupVariablesOptions, options ...RequestOptionFunc) ([]*GroupVariable, *Response, error) {
|
||||||
group, err := parseID(gid)
|
group, err := parseID(gid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -81,7 +82,7 @@ func (s *GroupVariablesService) ListVariables(gid interface{}, opt *ListGroupVar
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ee/api/group_level_variables.html#show-variable-details
|
// https://docs.gitlab.com/ee/api/group_level_variables.html#show-variable-details
|
||||||
func (s *GroupVariablesService) GetVariable(gid interface{}, key string, options ...OptionFunc) (*GroupVariable, *Response, error) {
|
func (s *GroupVariablesService) GetVariable(gid interface{}, key string, options ...RequestOptionFunc) (*GroupVariable, *Response, error) {
|
||||||
group, err := parseID(gid)
|
group, err := parseID(gid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -112,13 +113,14 @@ type CreateGroupVariableOptions struct {
|
||||||
Value *string `url:"value,omitempty" json:"value,omitempty"`
|
Value *string `url:"value,omitempty" json:"value,omitempty"`
|
||||||
VariableType *VariableTypeValue `url:"variable_type,omitempty" json:"variable_type,omitempty"`
|
VariableType *VariableTypeValue `url:"variable_type,omitempty" json:"variable_type,omitempty"`
|
||||||
Protected *bool `url:"protected,omitempty" json:"protected,omitempty"`
|
Protected *bool `url:"protected,omitempty" json:"protected,omitempty"`
|
||||||
|
Masked *bool `url:"masked,omitempty" json:"masked,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateVariable creates a new group variable.
|
// CreateVariable creates a new group variable.
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ee/api/group_level_variables.html#create-variable
|
// https://docs.gitlab.com/ee/api/group_level_variables.html#create-variable
|
||||||
func (s *GroupVariablesService) CreateVariable(gid interface{}, opt *CreateGroupVariableOptions, options ...OptionFunc) (*GroupVariable, *Response, error) {
|
func (s *GroupVariablesService) CreateVariable(gid interface{}, opt *CreateGroupVariableOptions, options ...RequestOptionFunc) (*GroupVariable, *Response, error) {
|
||||||
group, err := parseID(gid)
|
group, err := parseID(gid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -148,6 +150,7 @@ type UpdateGroupVariableOptions struct {
|
||||||
Value *string `url:"value,omitempty" json:"value,omitempty"`
|
Value *string `url:"value,omitempty" json:"value,omitempty"`
|
||||||
VariableType *VariableTypeValue `url:"variable_type,omitempty" json:"variable_type,omitempty"`
|
VariableType *VariableTypeValue `url:"variable_type,omitempty" json:"variable_type,omitempty"`
|
||||||
Protected *bool `url:"protected,omitempty" json:"protected,omitempty"`
|
Protected *bool `url:"protected,omitempty" json:"protected,omitempty"`
|
||||||
|
Masked *bool `url:"masked,omitempty" json:"masked,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateVariable updates the position of an existing
|
// UpdateVariable updates the position of an existing
|
||||||
|
@ -155,7 +158,7 @@ type UpdateGroupVariableOptions struct {
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ee/api/group_level_variables.html#update-variable
|
// https://docs.gitlab.com/ee/api/group_level_variables.html#update-variable
|
||||||
func (s *GroupVariablesService) UpdateVariable(gid interface{}, key string, opt *UpdateGroupVariableOptions, options ...OptionFunc) (*GroupVariable, *Response, error) {
|
func (s *GroupVariablesService) UpdateVariable(gid interface{}, key string, opt *UpdateGroupVariableOptions, options ...RequestOptionFunc) (*GroupVariable, *Response, error) {
|
||||||
group, err := parseID(gid)
|
group, err := parseID(gid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -180,7 +183,7 @@ func (s *GroupVariablesService) UpdateVariable(gid interface{}, key string, opt
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ee/api/group_level_variables.html#remove-variable
|
// https://docs.gitlab.com/ee/api/group_level_variables.html#remove-variable
|
||||||
func (s *GroupVariablesService) RemoveVariable(gid interface{}, key string, options ...OptionFunc) (*Response, error) {
|
func (s *GroupVariablesService) RemoveVariable(gid interface{}, key string, options ...RequestOptionFunc) (*Response, error) {
|
||||||
group, err := parseID(gid)
|
group, err := parseID(gid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -36,7 +36,8 @@ type Group struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Path string `json:"path"`
|
Path string `json:"path"`
|
||||||
Description string `json:"description"`
|
Description string `json:"description"`
|
||||||
Visibility *VisibilityValue `json:"visibility"`
|
MembershipLock bool `json:"membership_lock"`
|
||||||
|
Visibility VisibilityValue `json:"visibility"`
|
||||||
LFSEnabled bool `json:"lfs_enabled"`
|
LFSEnabled bool `json:"lfs_enabled"`
|
||||||
AvatarURL string `json:"avatar_url"`
|
AvatarURL string `json:"avatar_url"`
|
||||||
WebURL string `json:"web_url"`
|
WebURL string `json:"web_url"`
|
||||||
|
@ -50,16 +51,25 @@ type Group struct {
|
||||||
ShareWithGroupLock bool `json:"share_with_group_lock"`
|
ShareWithGroupLock bool `json:"share_with_group_lock"`
|
||||||
RequireTwoFactorAuth bool `json:"require_two_factor_authentication"`
|
RequireTwoFactorAuth bool `json:"require_two_factor_authentication"`
|
||||||
TwoFactorGracePeriod int `json:"two_factor_grace_period"`
|
TwoFactorGracePeriod int `json:"two_factor_grace_period"`
|
||||||
ProjectCreationLevel string `json:"project_creation_level"`
|
ProjectCreationLevel ProjectCreationLevelValue `json:"project_creation_level"`
|
||||||
AutoDevopsEnabled bool `json:"auto_devops_enabled"`
|
AutoDevopsEnabled bool `json:"auto_devops_enabled"`
|
||||||
SubGroupCreationLevel string `json:"subgroup_creation_level"`
|
SubGroupCreationLevel SubGroupCreationLevelValue `json:"subgroup_creation_level"`
|
||||||
EmailsDisabled bool `json:"emails_disabled"`
|
EmailsDisabled bool `json:"emails_disabled"`
|
||||||
|
MentionsDisabled bool `json:"mentions_disabled"`
|
||||||
RunnersToken string `json:"runners_token"`
|
RunnersToken string `json:"runners_token"`
|
||||||
SharedProjects []*Project `json:"shared_projects"`
|
SharedProjects []*Project `json:"shared_projects"`
|
||||||
LDAPCN string `json:"ldap_cn"`
|
LDAPCN string `json:"ldap_cn"`
|
||||||
LDAPAccess bool `json:"ldap_access"`
|
LDAPAccess AccessLevelValue `json:"ldap_access"`
|
||||||
|
LDAPGroupLinks []*LDAPGroupLink `json:"ldap_group_links"`
|
||||||
SharedRunnersMinutesLimit int `json:"shared_runners_minutes_limit"`
|
SharedRunnersMinutesLimit int `json:"shared_runners_minutes_limit"`
|
||||||
ExtraSharedRunnersMinutesLimit int `json:"extra_shared_runners_minutes_limit"`
|
ExtraSharedRunnersMinutesLimit int `json:"extra_shared_runners_minutes_limit"`
|
||||||
|
MarkedForDeletionOn *ISOTime `json:"marked_for_deletion_on"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type LDAPGroupLink struct {
|
||||||
|
CN string `json:"cn"`
|
||||||
|
GroupAccess AccessLevelValue `json:"group_access"`
|
||||||
|
Provider string `json:"provider"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListGroupsOptions represents the available ListGroups() options.
|
// ListGroupsOptions represents the available ListGroups() options.
|
||||||
|
@ -82,7 +92,7 @@ type ListGroupsOptions struct {
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/groups.html#list-project-groups
|
// https://docs.gitlab.com/ce/api/groups.html#list-project-groups
|
||||||
func (s *GroupsService) ListGroups(opt *ListGroupsOptions, options ...OptionFunc) ([]*Group, *Response, error) {
|
func (s *GroupsService) ListGroups(opt *ListGroupsOptions, options ...RequestOptionFunc) ([]*Group, *Response, error) {
|
||||||
req, err := s.client.NewRequest("GET", "groups", opt, options)
|
req, err := s.client.NewRequest("GET", "groups", opt, options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -100,7 +110,7 @@ func (s *GroupsService) ListGroups(opt *ListGroupsOptions, options ...OptionFunc
|
||||||
// GetGroup gets all details of a group.
|
// GetGroup gets all details of a group.
|
||||||
//
|
//
|
||||||
// GitLab API docs: https://docs.gitlab.com/ce/api/groups.html#details-of-a-group
|
// GitLab API docs: https://docs.gitlab.com/ce/api/groups.html#details-of-a-group
|
||||||
func (s *GroupsService) GetGroup(gid interface{}, options ...OptionFunc) (*Group, *Response, error) {
|
func (s *GroupsService) GetGroup(gid interface{}, options ...RequestOptionFunc) (*Group, *Response, error) {
|
||||||
group, err := parseID(gid)
|
group, err := parseID(gid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -128,17 +138,28 @@ type CreateGroupOptions struct {
|
||||||
Name *string `url:"name,omitempty" json:"name,omitempty"`
|
Name *string `url:"name,omitempty" json:"name,omitempty"`
|
||||||
Path *string `url:"path,omitempty" json:"path,omitempty"`
|
Path *string `url:"path,omitempty" json:"path,omitempty"`
|
||||||
Description *string `url:"description,omitempty" json:"description,omitempty"`
|
Description *string `url:"description,omitempty" json:"description,omitempty"`
|
||||||
|
MembershipLock *bool `url:"membership_lock,omitempty" json:"membership_lock,omitempty"`
|
||||||
Visibility *VisibilityValue `url:"visibility,omitempty" json:"visibility,omitempty"`
|
Visibility *VisibilityValue `url:"visibility,omitempty" json:"visibility,omitempty"`
|
||||||
|
ShareWithGroupLock *bool `url:"share_with_group_lock,omitempty" json:"share_with_group_lock,omitempty"`
|
||||||
|
RequireTwoFactorAuth *bool `url:"require_two_factor_authentication,omitempty" json:"require_two_factor_authentication,omitempty"`
|
||||||
|
TwoFactorGracePeriod *int `url:"two_factor_grace_period,omitempty" json:"two_factor_grace_period,omitempty"`
|
||||||
|
ProjectCreationLevel *ProjectCreationLevelValue `url:"project_creation_level,omitempty" json:"project_creation_level,omitempty"`
|
||||||
|
AutoDevopsEnabled *bool `url:"auto_devops_enabled,omitempty" json:"auto_devops_enabled,omitempty"`
|
||||||
|
SubGroupCreationLevel *SubGroupCreationLevelValue `url:"subgroup_creation_level,omitempty" json:"subgroup_creation_level,omitempty"`
|
||||||
|
EmailsDisabled *bool `url:"emails_disabled,omitempty" json:"emails_disabled,omitempty"`
|
||||||
|
MentionsDisabled *bool `url:"mentions_disabled,omitempty" json:"mentions_disabled,omitempty"`
|
||||||
LFSEnabled *bool `url:"lfs_enabled,omitempty" json:"lfs_enabled,omitempty"`
|
LFSEnabled *bool `url:"lfs_enabled,omitempty" json:"lfs_enabled,omitempty"`
|
||||||
RequestAccessEnabled *bool `url:"request_access_enabled,omitempty" json:"request_access_enabled,omitempty"`
|
RequestAccessEnabled *bool `url:"request_access_enabled,omitempty" json:"request_access_enabled,omitempty"`
|
||||||
ParentID *int `url:"parent_id,omitempty" json:"parent_id,omitempty"`
|
ParentID *int `url:"parent_id,omitempty" json:"parent_id,omitempty"`
|
||||||
|
SharedRunnersMinutesLimit *int `url:"shared_runners_minutes_limit,omitempty" json:"shared_runners_minutes_limit,omitempty"`
|
||||||
|
ExtraSharedRunnersMinutesLimit *int `url:"extra_shared_runners_minutes_limit,omitempty" json:"extra_shared_runners_minutes_limit,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateGroup creates a new project group. Available only for users who can
|
// CreateGroup creates a new project group. Available only for users who can
|
||||||
// create groups.
|
// create groups.
|
||||||
//
|
//
|
||||||
// GitLab API docs: https://docs.gitlab.com/ce/api/groups.html#new-group
|
// GitLab API docs: https://docs.gitlab.com/ce/api/groups.html#new-group
|
||||||
func (s *GroupsService) CreateGroup(opt *CreateGroupOptions, options ...OptionFunc) (*Group, *Response, error) {
|
func (s *GroupsService) CreateGroup(opt *CreateGroupOptions, options ...RequestOptionFunc) (*Group, *Response, error) {
|
||||||
req, err := s.client.NewRequest("POST", "groups", opt, options)
|
req, err := s.client.NewRequest("POST", "groups", opt, options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -158,7 +179,7 @@ func (s *GroupsService) CreateGroup(opt *CreateGroupOptions, options ...OptionFu
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/groups.html#transfer-project-to-group
|
// https://docs.gitlab.com/ce/api/groups.html#transfer-project-to-group
|
||||||
func (s *GroupsService) TransferGroup(gid interface{}, pid interface{}, options ...OptionFunc) (*Group, *Response, error) {
|
func (s *GroupsService) TransferGroup(gid interface{}, pid interface{}, options ...RequestOptionFunc) (*Group, *Response, error) {
|
||||||
group, err := parseID(gid)
|
group, err := parseID(gid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -193,7 +214,7 @@ type UpdateGroupOptions CreateGroupOptions
|
||||||
// administrators.
|
// administrators.
|
||||||
//
|
//
|
||||||
// GitLab API docs: https://docs.gitlab.com/ce/api/groups.html#update-group
|
// GitLab API docs: https://docs.gitlab.com/ce/api/groups.html#update-group
|
||||||
func (s *GroupsService) UpdateGroup(gid interface{}, opt *UpdateGroupOptions, options ...OptionFunc) (*Group, *Response, error) {
|
func (s *GroupsService) UpdateGroup(gid interface{}, opt *UpdateGroupOptions, options ...RequestOptionFunc) (*Group, *Response, error) {
|
||||||
group, err := parseID(gid)
|
group, err := parseID(gid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -217,7 +238,7 @@ func (s *GroupsService) UpdateGroup(gid interface{}, opt *UpdateGroupOptions, op
|
||||||
// DeleteGroup removes group with all projects inside.
|
// DeleteGroup removes group with all projects inside.
|
||||||
//
|
//
|
||||||
// GitLab API docs: https://docs.gitlab.com/ce/api/groups.html#remove-group
|
// GitLab API docs: https://docs.gitlab.com/ce/api/groups.html#remove-group
|
||||||
func (s *GroupsService) DeleteGroup(gid interface{}, options ...OptionFunc) (*Response, error) {
|
func (s *GroupsService) DeleteGroup(gid interface{}, options ...RequestOptionFunc) (*Response, error) {
|
||||||
group, err := parseID(gid)
|
group, err := parseID(gid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -235,7 +256,7 @@ func (s *GroupsService) DeleteGroup(gid interface{}, options ...OptionFunc) (*Re
|
||||||
// SearchGroup get all groups that match your string in their name or path.
|
// SearchGroup get all groups that match your string in their name or path.
|
||||||
//
|
//
|
||||||
// GitLab API docs: https://docs.gitlab.com/ce/api/groups.html#search-for-group
|
// GitLab API docs: https://docs.gitlab.com/ce/api/groups.html#search-for-group
|
||||||
func (s *GroupsService) SearchGroup(query string, options ...OptionFunc) ([]*Group, *Response, error) {
|
func (s *GroupsService) SearchGroup(query string, options ...RequestOptionFunc) ([]*Group, *Response, error) {
|
||||||
var q struct {
|
var q struct {
|
||||||
Search string `url:"search,omitempty" json:"search,omitempty"`
|
Search string `url:"search,omitempty" json:"search,omitempty"`
|
||||||
}
|
}
|
||||||
|
@ -281,7 +302,7 @@ type ListGroupProjectsOptions struct {
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/groups.html#list-a-group-39-s-projects
|
// https://docs.gitlab.com/ce/api/groups.html#list-a-group-39-s-projects
|
||||||
func (s *GroupsService) ListGroupProjects(gid interface{}, opt *ListGroupProjectsOptions, options ...OptionFunc) ([]*Project, *Response, error) {
|
func (s *GroupsService) ListGroupProjects(gid interface{}, opt *ListGroupProjectsOptions, options ...RequestOptionFunc) ([]*Project, *Response, error) {
|
||||||
group, err := parseID(gid)
|
group, err := parseID(gid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -313,7 +334,7 @@ type ListSubgroupsOptions ListGroupsOptions
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/groups.html#list-a-groups-s-subgroups
|
// https://docs.gitlab.com/ce/api/groups.html#list-a-groups-s-subgroups
|
||||||
func (s *GroupsService) ListSubgroups(gid interface{}, opt *ListSubgroupsOptions, options ...OptionFunc) ([]*Group, *Response, error) {
|
func (s *GroupsService) ListSubgroups(gid interface{}, opt *ListSubgroupsOptions, options ...RequestOptionFunc) ([]*Group, *Response, error) {
|
||||||
group, err := parseID(gid)
|
group, err := parseID(gid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -333,3 +354,110 @@ func (s *GroupsService) ListSubgroups(gid interface{}, opt *ListSubgroupsOptions
|
||||||
|
|
||||||
return g, resp, err
|
return g, resp, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ListGroupLDAPLinks lists the group's LDAP links. Available only for users who
|
||||||
|
// can edit groups.
|
||||||
|
//
|
||||||
|
// GitLab API docs:
|
||||||
|
// https://docs.gitlab.com/ee/api/groups.html#list-ldap-group-links-starter
|
||||||
|
func (s *GroupsService) ListGroupLDAPLinks(gid interface{}, options ...RequestOptionFunc) ([]*LDAPGroupLink, *Response, error) {
|
||||||
|
group, err := parseID(gid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
u := fmt.Sprintf("groups/%s/ldap_group_links", pathEscape(group))
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest("GET", u, nil, options)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var gl []*LDAPGroupLink
|
||||||
|
resp, err := s.client.Do(req, &gl)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return gl, resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddGroupLDAPLinkOptions represents the available AddGroupLDAPLink() options.
|
||||||
|
//
|
||||||
|
// GitLab API docs:
|
||||||
|
// https://docs.gitlab.com/ee/api/groups.html#add-ldap-group-link-starter
|
||||||
|
type AddGroupLDAPLinkOptions struct {
|
||||||
|
CN *string `url:"cn,omitempty" json:"cn,omitempty"`
|
||||||
|
GroupAccess *int `url:"group_access,omitempty" json:"group_access,omitempty"`
|
||||||
|
Provider *string `url:"provider,omitempty" json:"provider,ommitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddGroupLDAPLink creates a new group LDAP link. Available only for users who
|
||||||
|
// can edit groups.
|
||||||
|
//
|
||||||
|
// GitLab API docs:
|
||||||
|
// https://docs.gitlab.com/ee/api/groups.html#add-ldap-group-link-starter
|
||||||
|
func (s *GroupsService) AddGroupLDAPLink(gid interface{}, opt *AddGroupLDAPLinkOptions, options ...RequestOptionFunc) (*LDAPGroupLink, *Response, error) {
|
||||||
|
group, err := parseID(gid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
u := fmt.Sprintf("groups/%s/ldap_group_links", pathEscape(group))
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest("POST", u, opt, options)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
gl := new(LDAPGroupLink)
|
||||||
|
resp, err := s.client.Do(req, gl)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return gl, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteGroupLDAPLink deletes a group LDAP link. Available only for users who
|
||||||
|
// can edit groups.
|
||||||
|
//
|
||||||
|
// GitLab API docs:
|
||||||
|
// https://docs.gitlab.com/ee/api/groups.html#delete-ldap-group-link-starter
|
||||||
|
func (s *GroupsService) DeleteGroupLDAPLink(gid interface{}, cn string, options ...RequestOptionFunc) (*Response, error) {
|
||||||
|
group, err := parseID(gid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
u := fmt.Sprintf("groups/%s/ldap_group_links/%s", pathEscape(group), pathEscape(cn))
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest("DELETE", u, nil, options)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.client.Do(req, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteGroupLDAPLinkForProvider deletes a group LDAP link from a specific
|
||||||
|
// provider. Available only for users who can edit groups.
|
||||||
|
//
|
||||||
|
// GitLab API docs:
|
||||||
|
// https://docs.gitlab.com/ee/api/groups.html#delete-ldap-group-link-starter
|
||||||
|
func (s *GroupsService) DeleteGroupLDAPLinkForProvider(gid interface{}, provider, cn string, options ...RequestOptionFunc) (*Response, error) {
|
||||||
|
group, err := parseID(gid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
u := fmt.Sprintf(
|
||||||
|
"groups/%s/ldap_group_links/%s/%s",
|
||||||
|
pathEscape(group),
|
||||||
|
pathEscape(provider),
|
||||||
|
pathEscape(cn),
|
||||||
|
)
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest("DELETE", u, nil, options)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.client.Do(req, nil)
|
||||||
|
}
|
||||||
|
|
|
@ -43,7 +43,7 @@ type IssueLink struct {
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ee/api/issue_links.html#list-issue-relations
|
// https://docs.gitlab.com/ee/api/issue_links.html#list-issue-relations
|
||||||
func (s *IssueLinksService) ListIssueRelations(pid interface{}, issueIID int, options ...OptionFunc) ([]*Issue, *Response, error) {
|
func (s *IssueLinksService) ListIssueRelations(pid interface{}, issueIID int, options ...RequestOptionFunc) ([]*Issue, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -77,7 +77,7 @@ type CreateIssueLinkOptions struct {
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ee/api/issue_links.html#create-an-issue-link
|
// https://docs.gitlab.com/ee/api/issue_links.html#create-an-issue-link
|
||||||
func (s *IssueLinksService) CreateIssueLink(pid interface{}, issueIID int, opt *CreateIssueLinkOptions, options ...OptionFunc) (*IssueLink, *Response, error) {
|
func (s *IssueLinksService) CreateIssueLink(pid interface{}, issueIID int, opt *CreateIssueLinkOptions, options ...RequestOptionFunc) (*IssueLink, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -102,7 +102,7 @@ func (s *IssueLinksService) CreateIssueLink(pid interface{}, issueIID int, opt *
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ee/api/issue_links.html#delete-an-issue-link
|
// https://docs.gitlab.com/ee/api/issue_links.html#delete-an-issue-link
|
||||||
func (s *IssueLinksService) DeleteIssueLink(pid interface{}, issueIID, issueLinkID int, options ...OptionFunc) (*IssueLink, *Response, error) {
|
func (s *IssueLinksService) DeleteIssueLink(pid interface{}, issueIID, issueLinkID int, options ...RequestOptionFunc) (*IssueLink, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
|
|
@ -19,6 +19,7 @@ package gitlab
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
@ -52,6 +53,13 @@ type IssueAssignee struct {
|
||||||
Username string `json:"username"`
|
Username string `json:"username"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IssueReferences represents references of the issue.
|
||||||
|
type IssueReferences struct {
|
||||||
|
Short string `json:"short"`
|
||||||
|
Relative string `json:"relative"`
|
||||||
|
Full string `json:"full"`
|
||||||
|
}
|
||||||
|
|
||||||
// IssueLinks represents links of the issue.
|
// IssueLinks represents links of the issue.
|
||||||
type IssueLinks struct {
|
type IssueLinks struct {
|
||||||
Self string `json:"self"`
|
Self string `json:"self"`
|
||||||
|
@ -66,31 +74,36 @@ type IssueLinks struct {
|
||||||
type Issue struct {
|
type Issue struct {
|
||||||
ID int `json:"id"`
|
ID int `json:"id"`
|
||||||
IID int `json:"iid"`
|
IID int `json:"iid"`
|
||||||
ProjectID int `json:"project_id"`
|
|
||||||
Milestone *Milestone `json:"milestone"`
|
|
||||||
Author *IssueAuthor `json:"author"`
|
|
||||||
Description string `json:"description"`
|
|
||||||
State string `json:"state"`
|
State string `json:"state"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
Author *IssueAuthor `json:"author"`
|
||||||
|
Milestone *Milestone `json:"milestone"`
|
||||||
|
ProjectID int `json:"project_id"`
|
||||||
Assignees []*IssueAssignee `json:"assignees"`
|
Assignees []*IssueAssignee `json:"assignees"`
|
||||||
Assignee *IssueAssignee `json:"assignee"`
|
Assignee *IssueAssignee `json:"assignee"`
|
||||||
|
UpdatedAt *time.Time `json:"updated_at"`
|
||||||
|
ClosedAt *time.Time `json:"closed_at"`
|
||||||
|
Title string `json:"title"`
|
||||||
|
CreatedAt *time.Time `json:"created_at"`
|
||||||
|
Labels Labels `json:"labels"`
|
||||||
Upvotes int `json:"upvotes"`
|
Upvotes int `json:"upvotes"`
|
||||||
Downvotes int `json:"downvotes"`
|
Downvotes int `json:"downvotes"`
|
||||||
Labels Labels `json:"labels"`
|
|
||||||
Title string `json:"title"`
|
|
||||||
UpdatedAt *time.Time `json:"updated_at"`
|
|
||||||
CreatedAt *time.Time `json:"created_at"`
|
|
||||||
ClosedAt *time.Time `json:"closed_at"`
|
|
||||||
Subscribed bool `json:"subscribed"`
|
|
||||||
UserNotesCount int `json:"user_notes_count"`
|
|
||||||
DueDate *ISOTime `json:"due_date"`
|
DueDate *ISOTime `json:"due_date"`
|
||||||
WebURL string `json:"web_url"`
|
WebURL string `json:"web_url"`
|
||||||
|
References *IssueReferences `json:"references"`
|
||||||
TimeStats *TimeStats `json:"time_stats"`
|
TimeStats *TimeStats `json:"time_stats"`
|
||||||
Confidential bool `json:"confidential"`
|
Confidential bool `json:"confidential"`
|
||||||
Weight int `json:"weight"`
|
Weight int `json:"weight"`
|
||||||
DiscussionLocked bool `json:"discussion_locked"`
|
DiscussionLocked bool `json:"discussion_locked"`
|
||||||
|
Subscribed bool `json:"subscribed"`
|
||||||
|
UserNotesCount int `json:"user_notes_count"`
|
||||||
Links *IssueLinks `json:"_links"`
|
Links *IssueLinks `json:"_links"`
|
||||||
IssueLinkID int `json:"issue_link_id"`
|
IssueLinkID int `json:"issue_link_id"`
|
||||||
MergeRequestCount int `json:"merge_requests_count"`
|
MergeRequestCount int `json:"merge_requests_count"`
|
||||||
|
TaskCompletionStatus struct {
|
||||||
|
Count int `json:"count"`
|
||||||
|
CompletedCount int `json:"completed_count"`
|
||||||
|
} `json:"task_completion_status"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i Issue) String() string {
|
func (i Issue) String() string {
|
||||||
|
@ -105,6 +118,12 @@ func (l *Labels) MarshalJSON() ([]byte, error) {
|
||||||
return json.Marshal(strings.Join(*l, ","))
|
return json.Marshal(strings.Join(*l, ","))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// EncodeValues implements the query.EncodeValues interface
|
||||||
|
func (l *Labels) EncodeValues(key string, v *url.Values) error {
|
||||||
|
v.Set(key, strings.Join(*l, ","))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// ListIssuesOptions represents the available ListIssues() options.
|
// ListIssuesOptions represents the available ListIssues() options.
|
||||||
//
|
//
|
||||||
// GitLab API docs: https://docs.gitlab.com/ce/api/issues.html#list-issues
|
// GitLab API docs: https://docs.gitlab.com/ce/api/issues.html#list-issues
|
||||||
|
@ -133,7 +152,7 @@ type ListIssuesOptions struct {
|
||||||
// takes pagination parameters page and per_page to restrict the list of issues.
|
// takes pagination parameters page and per_page to restrict the list of issues.
|
||||||
//
|
//
|
||||||
// GitLab API docs: https://docs.gitlab.com/ce/api/issues.html#list-issues
|
// GitLab API docs: https://docs.gitlab.com/ce/api/issues.html#list-issues
|
||||||
func (s *IssuesService) ListIssues(opt *ListIssuesOptions, options ...OptionFunc) ([]*Issue, *Response, error) {
|
func (s *IssuesService) ListIssues(opt *ListIssuesOptions, options ...RequestOptionFunc) ([]*Issue, *Response, error) {
|
||||||
req, err := s.client.NewRequest("GET", "issues", opt, options)
|
req, err := s.client.NewRequest("GET", "issues", opt, options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -175,7 +194,7 @@ type ListGroupIssuesOptions struct {
|
||||||
// pagination parameters page and per_page to return the list of group issues.
|
// pagination parameters page and per_page to return the list of group issues.
|
||||||
//
|
//
|
||||||
// GitLab API docs: https://docs.gitlab.com/ce/api/issues.html#list-group-issues
|
// GitLab API docs: https://docs.gitlab.com/ce/api/issues.html#list-group-issues
|
||||||
func (s *IssuesService) ListGroupIssues(pid interface{}, opt *ListGroupIssuesOptions, options ...OptionFunc) ([]*Issue, *Response, error) {
|
func (s *IssuesService) ListGroupIssues(pid interface{}, opt *ListGroupIssuesOptions, options ...RequestOptionFunc) ([]*Issue, *Response, error) {
|
||||||
group, err := parseID(pid)
|
group, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -225,7 +244,7 @@ type ListProjectIssuesOptions struct {
|
||||||
// pagination parameters page and per_page to return the list of project issues.
|
// pagination parameters page and per_page to return the list of project issues.
|
||||||
//
|
//
|
||||||
// GitLab API docs: https://docs.gitlab.com/ce/api/issues.html#list-project-issues
|
// GitLab API docs: https://docs.gitlab.com/ce/api/issues.html#list-project-issues
|
||||||
func (s *IssuesService) ListProjectIssues(pid interface{}, opt *ListProjectIssuesOptions, options ...OptionFunc) ([]*Issue, *Response, error) {
|
func (s *IssuesService) ListProjectIssues(pid interface{}, opt *ListProjectIssuesOptions, options ...RequestOptionFunc) ([]*Issue, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -249,7 +268,7 @@ func (s *IssuesService) ListProjectIssues(pid interface{}, opt *ListProjectIssue
|
||||||
// GetIssue gets a single project issue.
|
// GetIssue gets a single project issue.
|
||||||
//
|
//
|
||||||
// GitLab API docs: https://docs.gitlab.com/ce/api/issues.html#single-issues
|
// GitLab API docs: https://docs.gitlab.com/ce/api/issues.html#single-issues
|
||||||
func (s *IssuesService) GetIssue(pid interface{}, issue int, options ...OptionFunc) (*Issue, *Response, error) {
|
func (s *IssuesService) GetIssue(pid interface{}, issue int, options ...RequestOptionFunc) (*Issue, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -291,7 +310,7 @@ type CreateIssueOptions struct {
|
||||||
// CreateIssue creates a new project issue.
|
// CreateIssue creates a new project issue.
|
||||||
//
|
//
|
||||||
// GitLab API docs: https://docs.gitlab.com/ce/api/issues.html#new-issues
|
// GitLab API docs: https://docs.gitlab.com/ce/api/issues.html#new-issues
|
||||||
func (s *IssuesService) CreateIssue(pid interface{}, opt *CreateIssueOptions, options ...OptionFunc) (*Issue, *Response, error) {
|
func (s *IssuesService) CreateIssue(pid interface{}, opt *CreateIssueOptions, options ...RequestOptionFunc) (*Issue, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -333,7 +352,7 @@ type UpdateIssueOptions struct {
|
||||||
// to mark an issue as closed.
|
// to mark an issue as closed.
|
||||||
//
|
//
|
||||||
// GitLab API docs: https://docs.gitlab.com/ce/api/issues.html#edit-issues
|
// GitLab API docs: https://docs.gitlab.com/ce/api/issues.html#edit-issues
|
||||||
func (s *IssuesService) UpdateIssue(pid interface{}, issue int, opt *UpdateIssueOptions, options ...OptionFunc) (*Issue, *Response, error) {
|
func (s *IssuesService) UpdateIssue(pid interface{}, issue int, opt *UpdateIssueOptions, options ...RequestOptionFunc) (*Issue, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -357,7 +376,7 @@ func (s *IssuesService) UpdateIssue(pid interface{}, issue int, opt *UpdateIssue
|
||||||
// DeleteIssue deletes a single project issue.
|
// DeleteIssue deletes a single project issue.
|
||||||
//
|
//
|
||||||
// GitLab API docs: https://docs.gitlab.com/ce/api/issues.html#delete-an-issue
|
// GitLab API docs: https://docs.gitlab.com/ce/api/issues.html#delete-an-issue
|
||||||
func (s *IssuesService) DeleteIssue(pid interface{}, issue int, options ...OptionFunc) (*Response, error) {
|
func (s *IssuesService) DeleteIssue(pid interface{}, issue int, options ...RequestOptionFunc) (*Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -383,7 +402,7 @@ type MoveIssueOptions struct {
|
||||||
// to mark an issue as closed.
|
// to mark an issue as closed.
|
||||||
//
|
//
|
||||||
// GitLab API docs: https://docs.gitlab.com/ee/api/issues.html#move-an-issue
|
// GitLab API docs: https://docs.gitlab.com/ee/api/issues.html#move-an-issue
|
||||||
func (s *IssuesService) MoveIssue(pid interface{}, issue int, opt *MoveIssueOptions, options ...OptionFunc) (*Issue, *Response, error) {
|
func (s *IssuesService) MoveIssue(pid interface{}, issue int, opt *MoveIssueOptions, options ...RequestOptionFunc) (*Issue, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -410,7 +429,7 @@ func (s *IssuesService) MoveIssue(pid interface{}, issue int, opt *MoveIssueOpti
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/merge_requests.html#subscribe-to-a-merge-request
|
// https://docs.gitlab.com/ce/api/merge_requests.html#subscribe-to-a-merge-request
|
||||||
func (s *IssuesService) SubscribeToIssue(pid interface{}, issue int, options ...OptionFunc) (*Issue, *Response, error) {
|
func (s *IssuesService) SubscribeToIssue(pid interface{}, issue int, options ...RequestOptionFunc) (*Issue, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -437,7 +456,7 @@ func (s *IssuesService) SubscribeToIssue(pid interface{}, issue int, options ...
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/merge_requests.html#unsubscribe-from-a-merge-request
|
// https://docs.gitlab.com/ce/api/merge_requests.html#unsubscribe-from-a-merge-request
|
||||||
func (s *IssuesService) UnsubscribeFromIssue(pid interface{}, issue int, options ...OptionFunc) (*Issue, *Response, error) {
|
func (s *IssuesService) UnsubscribeFromIssue(pid interface{}, issue int, options ...RequestOptionFunc) (*Issue, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -470,7 +489,7 @@ type ListMergeRequestsClosingIssueOptions ListOptions
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/issues.html#list-merge-requests-that-will-close-issue-on-merge
|
// https://docs.gitlab.com/ce/api/issues.html#list-merge-requests-that-will-close-issue-on-merge
|
||||||
func (s *IssuesService) ListMergeRequestsClosingIssue(pid interface{}, issue int, opt *ListMergeRequestsClosingIssueOptions, options ...OptionFunc) ([]*MergeRequest, *Response, error) {
|
func (s *IssuesService) ListMergeRequestsClosingIssue(pid interface{}, issue int, opt *ListMergeRequestsClosingIssueOptions, options ...RequestOptionFunc) ([]*MergeRequest, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -503,7 +522,7 @@ type ListMergeRequestsRelatedToIssueOptions ListOptions
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/issues.html#list-merge-requests-related-to-issue
|
// https://docs.gitlab.com/ce/api/issues.html#list-merge-requests-related-to-issue
|
||||||
func (s *IssuesService) ListMergeRequestsRelatedToIssue(pid interface{}, issue int, opt *ListMergeRequestsRelatedToIssueOptions, options ...OptionFunc) ([]*MergeRequest, *Response, error) {
|
func (s *IssuesService) ListMergeRequestsRelatedToIssue(pid interface{}, issue int, opt *ListMergeRequestsRelatedToIssueOptions, options ...RequestOptionFunc) ([]*MergeRequest, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -531,7 +550,7 @@ func (s *IssuesService) ListMergeRequestsRelatedToIssue(pid interface{}, issue i
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/issues.html#set-a-time-estimate-for-an-issue
|
// https://docs.gitlab.com/ce/api/issues.html#set-a-time-estimate-for-an-issue
|
||||||
func (s *IssuesService) SetTimeEstimate(pid interface{}, issue int, opt *SetTimeEstimateOptions, options ...OptionFunc) (*TimeStats, *Response, error) {
|
func (s *IssuesService) SetTimeEstimate(pid interface{}, issue int, opt *SetTimeEstimateOptions, options ...RequestOptionFunc) (*TimeStats, *Response, error) {
|
||||||
return s.timeStats.setTimeEstimate(pid, "issues", issue, opt, options...)
|
return s.timeStats.setTimeEstimate(pid, "issues", issue, opt, options...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -539,7 +558,7 @@ func (s *IssuesService) SetTimeEstimate(pid interface{}, issue int, opt *SetTime
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/issues.html#reset-the-time-estimate-for-an-issue
|
// https://docs.gitlab.com/ce/api/issues.html#reset-the-time-estimate-for-an-issue
|
||||||
func (s *IssuesService) ResetTimeEstimate(pid interface{}, issue int, options ...OptionFunc) (*TimeStats, *Response, error) {
|
func (s *IssuesService) ResetTimeEstimate(pid interface{}, issue int, options ...RequestOptionFunc) (*TimeStats, *Response, error) {
|
||||||
return s.timeStats.resetTimeEstimate(pid, "issues", issue, options...)
|
return s.timeStats.resetTimeEstimate(pid, "issues", issue, options...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -547,7 +566,7 @@ func (s *IssuesService) ResetTimeEstimate(pid interface{}, issue int, options ..
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/issues.html#add-spent-time-for-an-issue
|
// https://docs.gitlab.com/ce/api/issues.html#add-spent-time-for-an-issue
|
||||||
func (s *IssuesService) AddSpentTime(pid interface{}, issue int, opt *AddSpentTimeOptions, options ...OptionFunc) (*TimeStats, *Response, error) {
|
func (s *IssuesService) AddSpentTime(pid interface{}, issue int, opt *AddSpentTimeOptions, options ...RequestOptionFunc) (*TimeStats, *Response, error) {
|
||||||
return s.timeStats.addSpentTime(pid, "issues", issue, opt, options...)
|
return s.timeStats.addSpentTime(pid, "issues", issue, opt, options...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -555,7 +574,7 @@ func (s *IssuesService) AddSpentTime(pid interface{}, issue int, opt *AddSpentTi
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/issues.html#reset-spent-time-for-an-issue
|
// https://docs.gitlab.com/ce/api/issues.html#reset-spent-time-for-an-issue
|
||||||
func (s *IssuesService) ResetSpentTime(pid interface{}, issue int, options ...OptionFunc) (*TimeStats, *Response, error) {
|
func (s *IssuesService) ResetSpentTime(pid interface{}, issue int, options ...RequestOptionFunc) (*TimeStats, *Response, error) {
|
||||||
return s.timeStats.resetSpentTime(pid, "issues", issue, options...)
|
return s.timeStats.resetSpentTime(pid, "issues", issue, options...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -563,6 +582,6 @@ func (s *IssuesService) ResetSpentTime(pid interface{}, issue int, options ...Op
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/issues.html#get-time-tracking-stats
|
// https://docs.gitlab.com/ce/api/issues.html#get-time-tracking-stats
|
||||||
func (s *IssuesService) GetTimeSpent(pid interface{}, issue int, options ...OptionFunc) (*TimeStats, *Response, error) {
|
func (s *IssuesService) GetTimeSpent(pid interface{}, issue int, options ...RequestOptionFunc) (*TimeStats, *Response, error) {
|
||||||
return s.timeStats.getTimeSpent(pid, "issues", issue, options...)
|
return s.timeStats.getTimeSpent(pid, "issues", issue, options...)
|
||||||
}
|
}
|
||||||
|
|
|
@ -89,7 +89,7 @@ type ListJobsOptions struct {
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/jobs.html#list-project-jobs
|
// https://docs.gitlab.com/ce/api/jobs.html#list-project-jobs
|
||||||
func (s *JobsService) ListProjectJobs(pid interface{}, opts *ListJobsOptions, options ...OptionFunc) ([]Job, *Response, error) {
|
func (s *JobsService) ListProjectJobs(pid interface{}, opts *ListJobsOptions, options ...RequestOptionFunc) ([]Job, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -115,7 +115,7 @@ func (s *JobsService) ListProjectJobs(pid interface{}, opts *ListJobsOptions, op
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/jobs.html#list-pipeline-jobs
|
// https://docs.gitlab.com/ce/api/jobs.html#list-pipeline-jobs
|
||||||
func (s *JobsService) ListPipelineJobs(pid interface{}, pipelineID int, opts *ListJobsOptions, options ...OptionFunc) ([]*Job, *Response, error) {
|
func (s *JobsService) ListPipelineJobs(pid interface{}, pipelineID int, opts *ListJobsOptions, options ...RequestOptionFunc) ([]*Job, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -140,7 +140,7 @@ func (s *JobsService) ListPipelineJobs(pid interface{}, pipelineID int, opts *Li
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/jobs.html#get-a-single-job
|
// https://docs.gitlab.com/ce/api/jobs.html#get-a-single-job
|
||||||
func (s *JobsService) GetJob(pid interface{}, jobID int, options ...OptionFunc) (*Job, *Response, error) {
|
func (s *JobsService) GetJob(pid interface{}, jobID int, options ...RequestOptionFunc) (*Job, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -165,7 +165,7 @@ func (s *JobsService) GetJob(pid interface{}, jobID int, options ...OptionFunc)
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/jobs.html#get-job-artifacts
|
// https://docs.gitlab.com/ce/api/jobs.html#get-job-artifacts
|
||||||
func (s *JobsService) GetJobArtifacts(pid interface{}, jobID int, options ...OptionFunc) (io.Reader, *Response, error) {
|
func (s *JobsService) GetJobArtifacts(pid interface{}, jobID int, options ...RequestOptionFunc) (io.Reader, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -200,7 +200,7 @@ type DownloadArtifactsFileOptions struct {
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/jobs.html#download-the-artifacts-file
|
// https://docs.gitlab.com/ce/api/jobs.html#download-the-artifacts-file
|
||||||
func (s *JobsService) DownloadArtifactsFile(pid interface{}, refName string, opt *DownloadArtifactsFileOptions, options ...OptionFunc) (io.Reader, *Response, error) {
|
func (s *JobsService) DownloadArtifactsFile(pid interface{}, refName string, opt *DownloadArtifactsFileOptions, options ...RequestOptionFunc) (io.Reader, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -228,7 +228,7 @@ func (s *JobsService) DownloadArtifactsFile(pid interface{}, refName string, opt
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/jobs.html#download-a-single-artifact-file
|
// https://docs.gitlab.com/ce/api/jobs.html#download-a-single-artifact-file
|
||||||
func (s *JobsService) DownloadSingleArtifactsFile(pid interface{}, jobID int, artifactPath string, options ...OptionFunc) (io.Reader, *Response, error) {
|
func (s *JobsService) DownloadSingleArtifactsFile(pid interface{}, jobID int, artifactPath string, options ...RequestOptionFunc) (io.Reader, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -259,7 +259,7 @@ func (s *JobsService) DownloadSingleArtifactsFile(pid interface{}, jobID int, ar
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/jobs.html#get-a-trace-file
|
// https://docs.gitlab.com/ce/api/jobs.html#get-a-trace-file
|
||||||
func (s *JobsService) GetTraceFile(pid interface{}, jobID int, options ...OptionFunc) (io.Reader, *Response, error) {
|
func (s *JobsService) GetTraceFile(pid interface{}, jobID int, options ...RequestOptionFunc) (io.Reader, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -284,7 +284,7 @@ func (s *JobsService) GetTraceFile(pid interface{}, jobID int, options ...Option
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/jobs.html#cancel-a-job
|
// https://docs.gitlab.com/ce/api/jobs.html#cancel-a-job
|
||||||
func (s *JobsService) CancelJob(pid interface{}, jobID int, options ...OptionFunc) (*Job, *Response, error) {
|
func (s *JobsService) CancelJob(pid interface{}, jobID int, options ...RequestOptionFunc) (*Job, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -309,7 +309,7 @@ func (s *JobsService) CancelJob(pid interface{}, jobID int, options ...OptionFun
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/jobs.html#retry-a-job
|
// https://docs.gitlab.com/ce/api/jobs.html#retry-a-job
|
||||||
func (s *JobsService) RetryJob(pid interface{}, jobID int, options ...OptionFunc) (*Job, *Response, error) {
|
func (s *JobsService) RetryJob(pid interface{}, jobID int, options ...RequestOptionFunc) (*Job, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -335,7 +335,7 @@ func (s *JobsService) RetryJob(pid interface{}, jobID int, options ...OptionFunc
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/jobs.html#erase-a-job
|
// https://docs.gitlab.com/ce/api/jobs.html#erase-a-job
|
||||||
func (s *JobsService) EraseJob(pid interface{}, jobID int, options ...OptionFunc) (*Job, *Response, error) {
|
func (s *JobsService) EraseJob(pid interface{}, jobID int, options ...RequestOptionFunc) (*Job, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -361,7 +361,7 @@ func (s *JobsService) EraseJob(pid interface{}, jobID int, options ...OptionFunc
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/jobs.html#keep-artifacts
|
// https://docs.gitlab.com/ce/api/jobs.html#keep-artifacts
|
||||||
func (s *JobsService) KeepArtifacts(pid interface{}, jobID int, options ...OptionFunc) (*Job, *Response, error) {
|
func (s *JobsService) KeepArtifacts(pid interface{}, jobID int, options ...RequestOptionFunc) (*Job, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -386,7 +386,7 @@ func (s *JobsService) KeepArtifacts(pid interface{}, jobID int, options ...Optio
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/jobs.html#play-a-job
|
// https://docs.gitlab.com/ce/api/jobs.html#play-a-job
|
||||||
func (s *JobsService) PlayJob(pid interface{}, jobID int, options ...OptionFunc) (*Job, *Response, error) {
|
func (s *JobsService) PlayJob(pid interface{}, jobID int, options ...RequestOptionFunc) (*Job, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
|
|
@ -47,7 +47,7 @@ type Key struct {
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ee/api/keys.html#get-ssh-key-with-user-by-id-of-an-ssh-key
|
// https://docs.gitlab.com/ee/api/keys.html#get-ssh-key-with-user-by-id-of-an-ssh-key
|
||||||
func (s *KeysService) GetKeyWithUser(key int, options ...OptionFunc) (*Key, *Response, error) {
|
func (s *KeysService) GetKeyWithUser(key int, options ...RequestOptionFunc) (*Key, *Response, error) {
|
||||||
u := fmt.Sprintf("keys/%d", key)
|
u := fmt.Sprintf("keys/%d", key)
|
||||||
|
|
||||||
req, err := s.client.NewRequest("GET", u, nil, options)
|
req, err := s.client.NewRequest("GET", u, nil, options)
|
||||||
|
|
|
@ -78,7 +78,7 @@ type ListLabelsOptions ListOptions
|
||||||
// ListLabels gets all labels for given project.
|
// ListLabels gets all labels for given project.
|
||||||
//
|
//
|
||||||
// GitLab API docs: https://docs.gitlab.com/ce/api/labels.html#list-labels
|
// GitLab API docs: https://docs.gitlab.com/ce/api/labels.html#list-labels
|
||||||
func (s *LabelsService) ListLabels(pid interface{}, opt *ListLabelsOptions, options ...OptionFunc) ([]*Label, *Response, error) {
|
func (s *LabelsService) ListLabels(pid interface{}, opt *ListLabelsOptions, options ...RequestOptionFunc) ([]*Label, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -112,7 +112,7 @@ type CreateLabelOptions struct {
|
||||||
// color.
|
// color.
|
||||||
//
|
//
|
||||||
// GitLab API docs: https://docs.gitlab.com/ce/api/labels.html#create-a-new-label
|
// GitLab API docs: https://docs.gitlab.com/ce/api/labels.html#create-a-new-label
|
||||||
func (s *LabelsService) CreateLabel(pid interface{}, opt *CreateLabelOptions, options ...OptionFunc) (*Label, *Response, error) {
|
func (s *LabelsService) CreateLabel(pid interface{}, opt *CreateLabelOptions, options ...RequestOptionFunc) (*Label, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -143,7 +143,7 @@ type DeleteLabelOptions struct {
|
||||||
// DeleteLabel deletes a label given by its name.
|
// DeleteLabel deletes a label given by its name.
|
||||||
//
|
//
|
||||||
// GitLab API docs: https://docs.gitlab.com/ce/api/labels.html#delete-a-label
|
// GitLab API docs: https://docs.gitlab.com/ce/api/labels.html#delete-a-label
|
||||||
func (s *LabelsService) DeleteLabel(pid interface{}, opt *DeleteLabelOptions, options ...OptionFunc) (*Response, error) {
|
func (s *LabelsService) DeleteLabel(pid interface{}, opt *DeleteLabelOptions, options ...RequestOptionFunc) (*Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -172,7 +172,7 @@ type UpdateLabelOptions struct {
|
||||||
// one parameter is required, to update the label.
|
// one parameter is required, to update the label.
|
||||||
//
|
//
|
||||||
// GitLab API docs: https://docs.gitlab.com/ce/api/labels.html#edit-an-existing-label
|
// GitLab API docs: https://docs.gitlab.com/ce/api/labels.html#edit-an-existing-label
|
||||||
func (s *LabelsService) UpdateLabel(pid interface{}, opt *UpdateLabelOptions, options ...OptionFunc) (*Label, *Response, error) {
|
func (s *LabelsService) UpdateLabel(pid interface{}, opt *UpdateLabelOptions, options ...RequestOptionFunc) (*Label, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -199,7 +199,7 @@ func (s *LabelsService) UpdateLabel(pid interface{}, opt *UpdateLabelOptions, op
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/labels.html#subscribe-to-a-label
|
// https://docs.gitlab.com/ce/api/labels.html#subscribe-to-a-label
|
||||||
func (s *LabelsService) SubscribeToLabel(pid interface{}, labelID interface{}, options ...OptionFunc) (*Label, *Response, error) {
|
func (s *LabelsService) SubscribeToLabel(pid interface{}, labelID interface{}, options ...RequestOptionFunc) (*Label, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -230,7 +230,7 @@ func (s *LabelsService) SubscribeToLabel(pid interface{}, labelID interface{}, o
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/labels.html#unsubscribe-from-a-label
|
// https://docs.gitlab.com/ce/api/labels.html#unsubscribe-from-a-label
|
||||||
func (s *LabelsService) UnsubscribeFromLabel(pid interface{}, labelID interface{}, options ...OptionFunc) (*Response, error) {
|
func (s *LabelsService) UnsubscribeFromLabel(pid interface{}, labelID interface{}, options ...RequestOptionFunc) (*Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -78,7 +78,7 @@ type AddLicenseOptions struct {
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ee/api/license.html#add-a-new-license
|
// https://docs.gitlab.com/ee/api/license.html#add-a-new-license
|
||||||
func (s *LicenseService) AddLicense(opt *AddLicenseOptions, options ...OptionFunc) (*License, *Response, error) {
|
func (s *LicenseService) AddLicense(opt *AddLicenseOptions, options ...RequestOptionFunc) (*License, *Response, error) {
|
||||||
req, err := s.client.NewRequest("POST", "license", opt, options)
|
req, err := s.client.NewRequest("POST", "license", opt, options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
|
|
@ -44,7 +44,7 @@ type ListLicenseTemplatesOptions struct {
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/templates/licenses.html#list-license-templates
|
// https://docs.gitlab.com/ce/api/templates/licenses.html#list-license-templates
|
||||||
func (s *LicenseTemplatesService) ListLicenseTemplates(opt *ListLicenseTemplatesOptions, options ...OptionFunc) ([]*LicenseTemplate, *Response, error) {
|
func (s *LicenseTemplatesService) ListLicenseTemplates(opt *ListLicenseTemplatesOptions, options ...RequestOptionFunc) ([]*LicenseTemplate, *Response, error) {
|
||||||
req, err := s.client.NewRequest("GET", "templates/licenses", opt, options)
|
req, err := s.client.NewRequest("GET", "templates/licenses", opt, options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -74,7 +74,7 @@ type GetLicenseTemplateOptions struct {
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/templates/licenses.html#single-license-template
|
// https://docs.gitlab.com/ce/api/templates/licenses.html#single-license-template
|
||||||
func (s *LicenseTemplatesService) GetLicenseTemplate(template string, opt *GetLicenseTemplateOptions, options ...OptionFunc) (*LicenseTemplate, *Response, error) {
|
func (s *LicenseTemplatesService) GetLicenseTemplate(template string, opt *GetLicenseTemplateOptions, options ...RequestOptionFunc) (*LicenseTemplate, *Response, error) {
|
||||||
u := fmt.Sprintf("templates/licenses/%s", template)
|
u := fmt.Sprintf("templates/licenses/%s", template)
|
||||||
|
|
||||||
req, err := s.client.NewRequest("GET", u, opt, options)
|
req, err := s.client.NewRequest("GET", u, opt, options)
|
||||||
|
|
|
@ -60,6 +60,38 @@ type MergeRequestApproverGroup struct {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MergeRequestApprovalRule represents a GitLab merge request approval rule.
|
||||||
|
//
|
||||||
|
// GitLab API docs:
|
||||||
|
// https://docs.gitlab.com/ee/api/merge_request_approvals.html#get-merge-request-level-rules
|
||||||
|
type MergeRequestApprovalRule struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
RuleType string `json:"rule_type"`
|
||||||
|
EligibleApprovers []*BasicUser `json:"eligible_approvers"`
|
||||||
|
ApprovalsRequired int `json:"approvals_required"`
|
||||||
|
SourceRule *ProjectApprovalRule `json:"source_rule"`
|
||||||
|
Users []*BasicUser `json:"users"`
|
||||||
|
Groups []*Group `json:"groups"`
|
||||||
|
ContainsHiddenGroups bool `json:"contains_hidden_groups"`
|
||||||
|
ApprovedBy []*BasicUser `json:"approved_by"`
|
||||||
|
Approved bool `json:"approved"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// MergeRequestApprovalState represents a GitLab merge request approval state.
|
||||||
|
//
|
||||||
|
// GitLab API docs:
|
||||||
|
// https://docs.gitlab.com/ee/api/merge_request_approvals.html#get-the-approval-state-of-merge-requests
|
||||||
|
type MergeRequestApprovalState struct {
|
||||||
|
ApprovalRulesOverwritten bool `json:"approval_rules_overwritten"`
|
||||||
|
Rules []*MergeRequestApprovalRule `json:"rules"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// String is a stringify for MergeRequestApprovalRule
|
||||||
|
func (s MergeRequestApprovalRule) String() string {
|
||||||
|
return Stringify(s)
|
||||||
|
}
|
||||||
|
|
||||||
// MergeRequestApproverUser represents GitLab project level merge request approver user.
|
// MergeRequestApproverUser represents GitLab project level merge request approver user.
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
|
@ -81,7 +113,7 @@ type ApproveMergeRequestOptions struct {
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ee/api/merge_request_approvals.html#approve-merge-request
|
// https://docs.gitlab.com/ee/api/merge_request_approvals.html#approve-merge-request
|
||||||
func (s *MergeRequestApprovalsService) ApproveMergeRequest(pid interface{}, mr int, opt *ApproveMergeRequestOptions, options ...OptionFunc) (*MergeRequestApprovals, *Response, error) {
|
func (s *MergeRequestApprovalsService) ApproveMergeRequest(pid interface{}, mr int, opt *ApproveMergeRequestOptions, options ...RequestOptionFunc) (*MergeRequestApprovals, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -106,7 +138,7 @@ func (s *MergeRequestApprovalsService) ApproveMergeRequest(pid interface{}, mr i
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ee/api/merge_request_approvals.html#unapprove-merge-request
|
// https://docs.gitlab.com/ee/api/merge_request_approvals.html#unapprove-merge-request
|
||||||
func (s *MergeRequestApprovalsService) UnapproveMergeRequest(pid interface{}, mr int, options ...OptionFunc) (*Response, error) {
|
func (s *MergeRequestApprovalsService) UnapproveMergeRequest(pid interface{}, mr int, options ...RequestOptionFunc) (*Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -130,16 +162,41 @@ type ChangeMergeRequestApprovalConfigurationOptions struct {
|
||||||
ApprovalsRequired *int `url:"approvals_required,omitempty" json:"approvals_required,omitempty"`
|
ApprovalsRequired *int `url:"approvals_required,omitempty" json:"approvals_required,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ChangeApprovalConfiguration updates the approval configuration of a merge request.
|
// GetConfiguration shows information about single merge request approvals
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ee/api/merge_request_approvals.html#change-approval-configuration
|
// https://docs.gitlab.com/ee/api/merge_request_approvals.html#get-configuration-1
|
||||||
func (s *MergeRequestApprovalsService) ChangeApprovalConfiguration(pid interface{}, mergeRequestIID int, opt *ChangeMergeRequestApprovalConfigurationOptions, options ...OptionFunc) (*MergeRequest, *Response, error) {
|
func (s *MergeRequestApprovalsService) GetConfiguration(pid interface{}, mr int, options ...RequestOptionFunc) (*MergeRequestApprovals, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
u := fmt.Sprintf("projects/%s/merge_requests/%d/approvals", pathEscape(project), mergeRequestIID)
|
u := fmt.Sprintf("projects/%s/merge_requests/%d/approvals", pathEscape(project), mr)
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest("GET", u, nil, options)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
m := new(MergeRequestApprovals)
|
||||||
|
resp, err := s.client.Do(req, m)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return m, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChangeApprovalConfiguration updates the approval configuration of a merge request.
|
||||||
|
//
|
||||||
|
// GitLab API docs:
|
||||||
|
// https://docs.gitlab.com/ee/api/merge_request_approvals.html#change-approval-configuration
|
||||||
|
func (s *MergeRequestApprovalsService) ChangeApprovalConfiguration(pid interface{}, mergeRequest int, opt *ChangeMergeRequestApprovalConfigurationOptions, options ...RequestOptionFunc) (*MergeRequest, *Response, error) {
|
||||||
|
project, err := parseID(pid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
u := fmt.Sprintf("projects/%s/merge_requests/%d/approvals", pathEscape(project), mergeRequest)
|
||||||
|
|
||||||
req, err := s.client.NewRequest("POST", u, opt, options)
|
req, err := s.client.NewRequest("POST", u, opt, options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -169,12 +226,12 @@ type ChangeMergeRequestAllowedApproversOptions struct {
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ee/api/merge_request_approvals.html#change-allowed-approvers-for-merge-request
|
// https://docs.gitlab.com/ee/api/merge_request_approvals.html#change-allowed-approvers-for-merge-request
|
||||||
func (s *MergeRequestApprovalsService) ChangeAllowedApprovers(pid interface{}, mergeRequestIID int, opt *ChangeMergeRequestAllowedApproversOptions, options ...OptionFunc) (*MergeRequest, *Response, error) {
|
func (s *MergeRequestApprovalsService) ChangeAllowedApprovers(pid interface{}, mergeRequest int, opt *ChangeMergeRequestAllowedApproversOptions, options ...RequestOptionFunc) (*MergeRequest, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
u := fmt.Sprintf("projects/%s/merge_requests/%d/approvers", pathEscape(project), mergeRequestIID)
|
u := fmt.Sprintf("projects/%s/merge_requests/%d/approvers", pathEscape(project), mergeRequest)
|
||||||
|
|
||||||
req, err := s.client.NewRequest("PUT", u, opt, options)
|
req, err := s.client.NewRequest("PUT", u, opt, options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -189,3 +246,147 @@ func (s *MergeRequestApprovalsService) ChangeAllowedApprovers(pid interface{}, m
|
||||||
|
|
||||||
return m, resp, err
|
return m, resp, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetApprovalRules requests information about a merge request’s approval rules
|
||||||
|
//
|
||||||
|
// GitLab API docs:
|
||||||
|
// https://docs.gitlab.com/ee/api/merge_request_approvals.html#get-merge-request-level-rules
|
||||||
|
func (s *MergeRequestApprovalsService) GetApprovalRules(pid interface{}, mergeRequest int, options ...RequestOptionFunc) ([]*MergeRequestApprovalRule, *Response, error) {
|
||||||
|
project, err := parseID(pid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
u := fmt.Sprintf("projects/%s/merge_requests/%d/approval_rules", pathEscape(project), mergeRequest)
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest("GET", u, nil, options)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var par []*MergeRequestApprovalRule
|
||||||
|
resp, err := s.client.Do(req, &par)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return par, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetApprovalState requests information about a merge request’s approval state
|
||||||
|
//
|
||||||
|
// GitLab API docs:
|
||||||
|
// https://docs.gitlab.com/ee/api/merge_request_approvals.html#get-the-approval-state-of-merge-requests
|
||||||
|
func (s *MergeRequestApprovalsService) GetApprovalState(pid interface{}, mergeRequest int, options ...RequestOptionFunc) (*MergeRequestApprovalState, *Response, error) {
|
||||||
|
project, err := parseID(pid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
u := fmt.Sprintf("projects/%s/merge_requests/%d/approval_state", pathEscape(project), mergeRequest)
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest("GET", u, nil, options)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var pas *MergeRequestApprovalState
|
||||||
|
resp, err := s.client.Do(req, &pas)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return pas, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateMergeRequestApprovalRuleOptions represents the available CreateApprovalRule()
|
||||||
|
// options.
|
||||||
|
//
|
||||||
|
// GitLab API docs:
|
||||||
|
// https://docs.gitlab.com/ee/api/merge_request_approvals.html#create-merge-request-level-rule
|
||||||
|
type CreateMergeRequestApprovalRuleOptions struct {
|
||||||
|
Name *string `url:"name,omitempty" json:"name,omitempty"`
|
||||||
|
ApprovalsRequired *int `url:"approvals_required,omitempty" json:"approvals_required,omitempty"`
|
||||||
|
ApprovalProjectRuleID *int `url:"approval_project_rule_id,omitempty" json:"approval_project_rule_id,omitempty"`
|
||||||
|
UserIDs []int `url:"user_ids,omitempty" json:"user_ids,omitempty"`
|
||||||
|
GroupIDs []int `url:"group_ids,omitempty" json:"group_ids,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateApprovalRule creates a new MR level approval rule.
|
||||||
|
//
|
||||||
|
// GitLab API docs:
|
||||||
|
// https://docs.gitlab.com/ee/api/merge_request_approvals.html#create-merge-request-level-rule
|
||||||
|
func (s *MergeRequestApprovalsService) CreateApprovalRule(pid interface{}, mergeRequest int, opt *CreateMergeRequestApprovalRuleOptions, options ...RequestOptionFunc) (*MergeRequestApprovalRule, *Response, error) {
|
||||||
|
project, err := parseID(pid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
u := fmt.Sprintf("projects/%s/merge_requests/%d/approval_rules", pathEscape(project), mergeRequest)
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest("POST", u, opt, options)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
par := new(MergeRequestApprovalRule)
|
||||||
|
resp, err := s.client.Do(req, &par)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return par, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateMergeRequestApprovalRuleOptions represents the available UpdateApprovalRule()
|
||||||
|
// options.
|
||||||
|
//
|
||||||
|
// GitLab API docs:
|
||||||
|
// https://docs.gitlab.com/ee/api/merge_request_approvals.html#update-merge-request-level-rule
|
||||||
|
type UpdateMergeRequestApprovalRuleOptions struct {
|
||||||
|
Name *string `url:"name,omitempty" json:"name,omitempty"`
|
||||||
|
ApprovalsRequired *int `url:"approvals_required,omitempty" json:"approvals_required,omitempty"`
|
||||||
|
UserIDs []int `url:"user_ids,omitempty" json:"user_ids,omitempty"`
|
||||||
|
GroupIDs []int `url:"group_ids,omitempty" json:"group_ids,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateApprovalRule updates an existing approval rule with new options.
|
||||||
|
//
|
||||||
|
// GitLab API docs:
|
||||||
|
// https://docs.gitlab.com/ee/api/merge_request_approvals.html#update-merge-request-level-rule
|
||||||
|
func (s *MergeRequestApprovalsService) UpdateApprovalRule(pid interface{}, mergeRequest int, approvalRule int, opt *UpdateMergeRequestApprovalRuleOptions, options ...RequestOptionFunc) (*MergeRequestApprovalRule, *Response, error) {
|
||||||
|
project, err := parseID(pid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
u := fmt.Sprintf("projects/%s/merge_requests/%d/approval_rules/%d", pathEscape(project), mergeRequest, approvalRule)
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest("PUT", u, opt, options)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
par := new(MergeRequestApprovalRule)
|
||||||
|
resp, err := s.client.Do(req, &par)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return par, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteApprovalRule deletes a mr level approval rule.
|
||||||
|
//
|
||||||
|
// GitLab API docs:
|
||||||
|
// https://docs.gitlab.com/ee/api/merge_request_approvals.html#delete-merge-request-level-rule
|
||||||
|
func (s *MergeRequestApprovalsService) DeleteApprovalRule(pid interface{}, mergeRequest int, approvalRule int, options ...RequestOptionFunc) (*Response, error) {
|
||||||
|
project, err := parseID(pid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
u := fmt.Sprintf("projects/%s/merge_requests/%d/approval_rules/%d", pathEscape(project), mergeRequest, approvalRule)
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest("DELETE", u, nil, options)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.client.Do(req, nil)
|
||||||
|
}
|
||||||
|
|
|
@ -64,6 +64,7 @@ type MergeRequest struct {
|
||||||
Subscribed bool `json:"subscribed"`
|
Subscribed bool `json:"subscribed"`
|
||||||
SHA string `json:"sha"`
|
SHA string `json:"sha"`
|
||||||
MergeCommitSHA string `json:"merge_commit_sha"`
|
MergeCommitSHA string `json:"merge_commit_sha"`
|
||||||
|
SquashCommitSHA string `json:"squash_commit_sha"`
|
||||||
UserNotesCount int `json:"user_notes_count"`
|
UserNotesCount int `json:"user_notes_count"`
|
||||||
ChangesCount string `json:"changes_count"`
|
ChangesCount string `json:"changes_count"`
|
||||||
ShouldRemoveSourceBranch bool `json:"should_remove_source_branch"`
|
ShouldRemoveSourceBranch bool `json:"should_remove_source_branch"`
|
||||||
|
@ -97,6 +98,7 @@ type MergeRequest struct {
|
||||||
Count int `json:"count"`
|
Count int `json:"count"`
|
||||||
CompletedCount int `json:"completed_count"`
|
CompletedCount int `json:"completed_count"`
|
||||||
} `json:"task_completion_status"`
|
} `json:"task_completion_status"`
|
||||||
|
HasConflicts bool `json:"has_conflicts"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m MergeRequest) String() string {
|
func (m MergeRequest) String() string {
|
||||||
|
@ -159,7 +161,7 @@ type ListMergeRequestsOptions struct {
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/merge_requests.html#list-merge-requests
|
// https://docs.gitlab.com/ce/api/merge_requests.html#list-merge-requests
|
||||||
func (s *MergeRequestsService) ListMergeRequests(opt *ListMergeRequestsOptions, options ...OptionFunc) ([]*MergeRequest, *Response, error) {
|
func (s *MergeRequestsService) ListMergeRequests(opt *ListMergeRequestsOptions, options ...RequestOptionFunc) ([]*MergeRequest, *Response, error) {
|
||||||
req, err := s.client.NewRequest("GET", "merge_requests", opt, options)
|
req, err := s.client.NewRequest("GET", "merge_requests", opt, options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -204,7 +206,7 @@ type ListGroupMergeRequestsOptions struct {
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/merge_requests.html#list-group-merge-requests
|
// https://docs.gitlab.com/ce/api/merge_requests.html#list-group-merge-requests
|
||||||
func (s *MergeRequestsService) ListGroupMergeRequests(gid interface{}, opt *ListGroupMergeRequestsOptions, options ...OptionFunc) ([]*MergeRequest, *Response, error) {
|
func (s *MergeRequestsService) ListGroupMergeRequests(gid interface{}, opt *ListGroupMergeRequestsOptions, options ...RequestOptionFunc) ([]*MergeRequest, *Response, error) {
|
||||||
group, err := parseID(gid)
|
group, err := parseID(gid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -257,7 +259,7 @@ type ListProjectMergeRequestsOptions struct {
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/merge_requests.html#list-project-merge-requests
|
// https://docs.gitlab.com/ce/api/merge_requests.html#list-project-merge-requests
|
||||||
func (s *MergeRequestsService) ListProjectMergeRequests(pid interface{}, opt *ListProjectMergeRequestsOptions, options ...OptionFunc) ([]*MergeRequest, *Response, error) {
|
func (s *MergeRequestsService) ListProjectMergeRequests(pid interface{}, opt *ListProjectMergeRequestsOptions, options ...RequestOptionFunc) ([]*MergeRequest, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -293,7 +295,7 @@ type GetMergeRequestsOptions struct {
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/merge_requests.html#get-single-mr
|
// https://docs.gitlab.com/ce/api/merge_requests.html#get-single-mr
|
||||||
func (s *MergeRequestsService) GetMergeRequest(pid interface{}, mergeRequest int, opt *GetMergeRequestsOptions, options ...OptionFunc) (*MergeRequest, *Response, error) {
|
func (s *MergeRequestsService) GetMergeRequest(pid interface{}, mergeRequest int, opt *GetMergeRequestsOptions, options ...RequestOptionFunc) (*MergeRequest, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -318,7 +320,7 @@ func (s *MergeRequestsService) GetMergeRequest(pid interface{}, mergeRequest int
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ee/api/merge_request_approvals.html#merge-request-level-mr-approvals
|
// https://docs.gitlab.com/ee/api/merge_request_approvals.html#merge-request-level-mr-approvals
|
||||||
func (s *MergeRequestsService) GetMergeRequestApprovals(pid interface{}, mergeRequest int, options ...OptionFunc) (*MergeRequestApprovals, *Response, error) {
|
func (s *MergeRequestsService) GetMergeRequestApprovals(pid interface{}, mergeRequest int, options ...RequestOptionFunc) (*MergeRequestApprovals, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -350,7 +352,7 @@ type GetMergeRequestCommitsOptions ListOptions
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/merge_requests.html#get-single-mr-commits
|
// https://docs.gitlab.com/ce/api/merge_requests.html#get-single-mr-commits
|
||||||
func (s *MergeRequestsService) GetMergeRequestCommits(pid interface{}, mergeRequest int, opt *GetMergeRequestCommitsOptions, options ...OptionFunc) ([]*Commit, *Response, error) {
|
func (s *MergeRequestsService) GetMergeRequestCommits(pid interface{}, mergeRequest int, opt *GetMergeRequestCommitsOptions, options ...RequestOptionFunc) ([]*Commit, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -376,7 +378,7 @@ func (s *MergeRequestsService) GetMergeRequestCommits(pid interface{}, mergeRequ
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/merge_requests.html#get-single-mr-changes
|
// https://docs.gitlab.com/ce/api/merge_requests.html#get-single-mr-changes
|
||||||
func (s *MergeRequestsService) GetMergeRequestChanges(pid interface{}, mergeRequest int, options ...OptionFunc) (*MergeRequest, *Response, error) {
|
func (s *MergeRequestsService) GetMergeRequestChanges(pid interface{}, mergeRequest int, options ...RequestOptionFunc) (*MergeRequest, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -401,7 +403,7 @@ func (s *MergeRequestsService) GetMergeRequestChanges(pid interface{}, mergeRequ
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/merge_requests.html#list-mr-pipelines
|
// https://docs.gitlab.com/ce/api/merge_requests.html#list-mr-pipelines
|
||||||
func (s *MergeRequestsService) ListMergeRequestPipelines(pid interface{}, mergeRequest int, options ...OptionFunc) ([]*PipelineInfo, *Response, error) {
|
func (s *MergeRequestsService) ListMergeRequestPipelines(pid interface{}, mergeRequest int, options ...RequestOptionFunc) ([]*PipelineInfo, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -434,7 +436,7 @@ type GetIssuesClosedOnMergeOptions ListOptions
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/merge_requests.html#list-issues-that-will-close-on-merge
|
// https://docs.gitlab.com/ce/api/merge_requests.html#list-issues-that-will-close-on-merge
|
||||||
func (s *MergeRequestsService) GetIssuesClosedOnMerge(pid interface{}, mergeRequest int, opt *GetIssuesClosedOnMergeOptions, options ...OptionFunc) ([]*Issue, *Response, error) {
|
func (s *MergeRequestsService) GetIssuesClosedOnMerge(pid interface{}, mergeRequest int, opt *GetIssuesClosedOnMergeOptions, options ...RequestOptionFunc) ([]*Issue, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -479,7 +481,7 @@ type CreateMergeRequestOptions struct {
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/merge_requests.html#create-mr
|
// https://docs.gitlab.com/ce/api/merge_requests.html#create-mr
|
||||||
func (s *MergeRequestsService) CreateMergeRequest(pid interface{}, opt *CreateMergeRequestOptions, options ...OptionFunc) (*MergeRequest, *Response, error) {
|
func (s *MergeRequestsService) CreateMergeRequest(pid interface{}, opt *CreateMergeRequestOptions, options ...RequestOptionFunc) (*MergeRequest, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -524,7 +526,7 @@ type UpdateMergeRequestOptions struct {
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/merge_requests.html#update-mr
|
// https://docs.gitlab.com/ce/api/merge_requests.html#update-mr
|
||||||
func (s *MergeRequestsService) UpdateMergeRequest(pid interface{}, mergeRequest int, opt *UpdateMergeRequestOptions, options ...OptionFunc) (*MergeRequest, *Response, error) {
|
func (s *MergeRequestsService) UpdateMergeRequest(pid interface{}, mergeRequest int, opt *UpdateMergeRequestOptions, options ...RequestOptionFunc) (*MergeRequest, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -549,7 +551,7 @@ func (s *MergeRequestsService) UpdateMergeRequest(pid interface{}, mergeRequest
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/merge_requests.html#delete-a-merge-request
|
// https://docs.gitlab.com/ce/api/merge_requests.html#delete-a-merge-request
|
||||||
func (s *MergeRequestsService) DeleteMergeRequest(pid interface{}, mergeRequest int, options ...OptionFunc) (*Response, error) {
|
func (s *MergeRequestsService) DeleteMergeRequest(pid interface{}, mergeRequest int, options ...RequestOptionFunc) (*Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -585,7 +587,7 @@ type AcceptMergeRequestOptions struct {
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/merge_requests.html#accept-mr
|
// https://docs.gitlab.com/ce/api/merge_requests.html#accept-mr
|
||||||
func (s *MergeRequestsService) AcceptMergeRequest(pid interface{}, mergeRequest int, opt *AcceptMergeRequestOptions, options ...OptionFunc) (*MergeRequest, *Response, error) {
|
func (s *MergeRequestsService) AcceptMergeRequest(pid interface{}, mergeRequest int, opt *AcceptMergeRequestOptions, options ...RequestOptionFunc) (*MergeRequest, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -614,7 +616,7 @@ func (s *MergeRequestsService) AcceptMergeRequest(pid interface{}, mergeRequest
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/merge_requests.html#cancel-merge-when-pipeline-succeeds
|
// https://docs.gitlab.com/ce/api/merge_requests.html#cancel-merge-when-pipeline-succeeds
|
||||||
func (s *MergeRequestsService) CancelMergeWhenPipelineSucceeds(pid interface{}, mergeRequest int, options ...OptionFunc) (*MergeRequest, *Response, error) {
|
func (s *MergeRequestsService) CancelMergeWhenPipelineSucceeds(pid interface{}, mergeRequest int, options ...RequestOptionFunc) (*MergeRequest, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -641,7 +643,7 @@ func (s *MergeRequestsService) CancelMergeWhenPipelineSucceeds(pid interface{},
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/merge_requests.html#rebase-a-merge-request
|
// https://docs.gitlab.com/ce/api/merge_requests.html#rebase-a-merge-request
|
||||||
func (s *MergeRequestsService) RebaseMergeRequest(pid interface{}, mergeRequest int, options ...OptionFunc) (*Response, error) {
|
func (s *MergeRequestsService) RebaseMergeRequest(pid interface{}, mergeRequest int, options ...RequestOptionFunc) (*Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -667,7 +669,7 @@ type GetMergeRequestDiffVersionsOptions ListOptions
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/merge_requests.html#get-mr-diff-versions
|
// https://docs.gitlab.com/ce/api/merge_requests.html#get-mr-diff-versions
|
||||||
func (s *MergeRequestsService) GetMergeRequestDiffVersions(pid interface{}, mergeRequest int, opt *GetMergeRequestDiffVersionsOptions, options ...OptionFunc) ([]*MergeRequestDiffVersion, *Response, error) {
|
func (s *MergeRequestsService) GetMergeRequestDiffVersions(pid interface{}, mergeRequest int, opt *GetMergeRequestDiffVersionsOptions, options ...RequestOptionFunc) ([]*MergeRequestDiffVersion, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -692,7 +694,7 @@ func (s *MergeRequestsService) GetMergeRequestDiffVersions(pid interface{}, merg
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/merge_requests.html#get-a-single-mr-diff-version
|
// https://docs.gitlab.com/ce/api/merge_requests.html#get-a-single-mr-diff-version
|
||||||
func (s *MergeRequestsService) GetSingleMergeRequestDiffVersion(pid interface{}, mergeRequest, version int, options ...OptionFunc) (*MergeRequestDiffVersion, *Response, error) {
|
func (s *MergeRequestsService) GetSingleMergeRequestDiffVersion(pid interface{}, mergeRequest, version int, options ...RequestOptionFunc) (*MergeRequestDiffVersion, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -719,7 +721,7 @@ func (s *MergeRequestsService) GetSingleMergeRequestDiffVersion(pid interface{},
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/merge_requests.html#subscribe-to-a-merge-request
|
// https://docs.gitlab.com/ce/api/merge_requests.html#subscribe-to-a-merge-request
|
||||||
func (s *MergeRequestsService) SubscribeToMergeRequest(pid interface{}, mergeRequest int, options ...OptionFunc) (*MergeRequest, *Response, error) {
|
func (s *MergeRequestsService) SubscribeToMergeRequest(pid interface{}, mergeRequest int, options ...RequestOptionFunc) (*MergeRequest, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -747,7 +749,7 @@ func (s *MergeRequestsService) SubscribeToMergeRequest(pid interface{}, mergeReq
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/merge_requests.html#unsubscribe-from-a-merge-request
|
// https://docs.gitlab.com/ce/api/merge_requests.html#unsubscribe-from-a-merge-request
|
||||||
func (s *MergeRequestsService) UnsubscribeFromMergeRequest(pid interface{}, mergeRequest int, options ...OptionFunc) (*MergeRequest, *Response, error) {
|
func (s *MergeRequestsService) UnsubscribeFromMergeRequest(pid interface{}, mergeRequest int, options ...RequestOptionFunc) (*MergeRequest, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -774,7 +776,7 @@ func (s *MergeRequestsService) UnsubscribeFromMergeRequest(pid interface{}, merg
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/merge_requests.html#create-a-todo
|
// https://docs.gitlab.com/ce/api/merge_requests.html#create-a-todo
|
||||||
func (s *MergeRequestsService) CreateTodo(pid interface{}, mergeRequest int, options ...OptionFunc) (*Todo, *Response, error) {
|
func (s *MergeRequestsService) CreateTodo(pid interface{}, mergeRequest int, options ...RequestOptionFunc) (*Todo, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -799,7 +801,7 @@ func (s *MergeRequestsService) CreateTodo(pid interface{}, mergeRequest int, opt
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/merge_requests.html#set-a-time-estimate-for-a-merge-request
|
// https://docs.gitlab.com/ce/api/merge_requests.html#set-a-time-estimate-for-a-merge-request
|
||||||
func (s *MergeRequestsService) SetTimeEstimate(pid interface{}, mergeRequest int, opt *SetTimeEstimateOptions, options ...OptionFunc) (*TimeStats, *Response, error) {
|
func (s *MergeRequestsService) SetTimeEstimate(pid interface{}, mergeRequest int, opt *SetTimeEstimateOptions, options ...RequestOptionFunc) (*TimeStats, *Response, error) {
|
||||||
return s.timeStats.setTimeEstimate(pid, "merge_requests", mergeRequest, opt, options...)
|
return s.timeStats.setTimeEstimate(pid, "merge_requests", mergeRequest, opt, options...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -807,7 +809,7 @@ func (s *MergeRequestsService) SetTimeEstimate(pid interface{}, mergeRequest int
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/merge_requests.html#reset-the-time-estimate-for-a-merge-request
|
// https://docs.gitlab.com/ce/api/merge_requests.html#reset-the-time-estimate-for-a-merge-request
|
||||||
func (s *MergeRequestsService) ResetTimeEstimate(pid interface{}, mergeRequest int, options ...OptionFunc) (*TimeStats, *Response, error) {
|
func (s *MergeRequestsService) ResetTimeEstimate(pid interface{}, mergeRequest int, options ...RequestOptionFunc) (*TimeStats, *Response, error) {
|
||||||
return s.timeStats.resetTimeEstimate(pid, "merge_requests", mergeRequest, options...)
|
return s.timeStats.resetTimeEstimate(pid, "merge_requests", mergeRequest, options...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -815,7 +817,7 @@ func (s *MergeRequestsService) ResetTimeEstimate(pid interface{}, mergeRequest i
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/merge_requests.html#add-spent-time-for-a-merge-request
|
// https://docs.gitlab.com/ce/api/merge_requests.html#add-spent-time-for-a-merge-request
|
||||||
func (s *MergeRequestsService) AddSpentTime(pid interface{}, mergeRequest int, opt *AddSpentTimeOptions, options ...OptionFunc) (*TimeStats, *Response, error) {
|
func (s *MergeRequestsService) AddSpentTime(pid interface{}, mergeRequest int, opt *AddSpentTimeOptions, options ...RequestOptionFunc) (*TimeStats, *Response, error) {
|
||||||
return s.timeStats.addSpentTime(pid, "merge_requests", mergeRequest, opt, options...)
|
return s.timeStats.addSpentTime(pid, "merge_requests", mergeRequest, opt, options...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -823,7 +825,7 @@ func (s *MergeRequestsService) AddSpentTime(pid interface{}, mergeRequest int, o
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/merge_requests.html#reset-spent-time-for-a-merge-request
|
// https://docs.gitlab.com/ce/api/merge_requests.html#reset-spent-time-for-a-merge-request
|
||||||
func (s *MergeRequestsService) ResetSpentTime(pid interface{}, mergeRequest int, options ...OptionFunc) (*TimeStats, *Response, error) {
|
func (s *MergeRequestsService) ResetSpentTime(pid interface{}, mergeRequest int, options ...RequestOptionFunc) (*TimeStats, *Response, error) {
|
||||||
return s.timeStats.resetSpentTime(pid, "merge_requests", mergeRequest, options...)
|
return s.timeStats.resetSpentTime(pid, "merge_requests", mergeRequest, options...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -831,6 +833,6 @@ func (s *MergeRequestsService) ResetSpentTime(pid interface{}, mergeRequest int,
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/merge_requests.html#get-time-tracking-stats
|
// https://docs.gitlab.com/ce/api/merge_requests.html#get-time-tracking-stats
|
||||||
func (s *MergeRequestsService) GetTimeSpent(pid interface{}, mergeRequest int, options ...OptionFunc) (*TimeStats, *Response, error) {
|
func (s *MergeRequestsService) GetTimeSpent(pid interface{}, mergeRequest int, options ...RequestOptionFunc) (*TimeStats, *Response, error) {
|
||||||
return s.timeStats.getTimeSpent(pid, "merge_requests", mergeRequest, options...)
|
return s.timeStats.getTimeSpent(pid, "merge_requests", mergeRequest, options...)
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,7 +65,7 @@ type ListMilestonesOptions struct {
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/milestones.html#list-project-milestones
|
// https://docs.gitlab.com/ce/api/milestones.html#list-project-milestones
|
||||||
func (s *MilestonesService) ListMilestones(pid interface{}, opt *ListMilestonesOptions, options ...OptionFunc) ([]*Milestone, *Response, error) {
|
func (s *MilestonesService) ListMilestones(pid interface{}, opt *ListMilestonesOptions, options ...RequestOptionFunc) ([]*Milestone, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -90,7 +90,7 @@ func (s *MilestonesService) ListMilestones(pid interface{}, opt *ListMilestonesO
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/milestones.html#get-single-milestone
|
// https://docs.gitlab.com/ce/api/milestones.html#get-single-milestone
|
||||||
func (s *MilestonesService) GetMilestone(pid interface{}, milestone int, options ...OptionFunc) (*Milestone, *Response, error) {
|
func (s *MilestonesService) GetMilestone(pid interface{}, milestone int, options ...RequestOptionFunc) (*Milestone, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -126,7 +126,7 @@ type CreateMilestoneOptions struct {
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/milestones.html#create-new-milestone
|
// https://docs.gitlab.com/ce/api/milestones.html#create-new-milestone
|
||||||
func (s *MilestonesService) CreateMilestone(pid interface{}, opt *CreateMilestoneOptions, options ...OptionFunc) (*Milestone, *Response, error) {
|
func (s *MilestonesService) CreateMilestone(pid interface{}, opt *CreateMilestoneOptions, options ...RequestOptionFunc) (*Milestone, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -163,7 +163,7 @@ type UpdateMilestoneOptions struct {
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/milestones.html#edit-milestone
|
// https://docs.gitlab.com/ce/api/milestones.html#edit-milestone
|
||||||
func (s *MilestonesService) UpdateMilestone(pid interface{}, milestone int, opt *UpdateMilestoneOptions, options ...OptionFunc) (*Milestone, *Response, error) {
|
func (s *MilestonesService) UpdateMilestone(pid interface{}, milestone int, opt *UpdateMilestoneOptions, options ...RequestOptionFunc) (*Milestone, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -188,7 +188,7 @@ func (s *MilestonesService) UpdateMilestone(pid interface{}, milestone int, opt
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/milestones.html#delete-project-milestone
|
// https://docs.gitlab.com/ce/api/milestones.html#delete-project-milestone
|
||||||
func (s *MilestonesService) DeleteMilestone(pid interface{}, milestone int, options ...OptionFunc) (*Response, error) {
|
func (s *MilestonesService) DeleteMilestone(pid interface{}, milestone int, options ...RequestOptionFunc) (*Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -212,7 +212,7 @@ type GetMilestoneIssuesOptions ListOptions
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/milestones.html#get-all-issues-assigned-to-a-single-milestone
|
// https://docs.gitlab.com/ce/api/milestones.html#get-all-issues-assigned-to-a-single-milestone
|
||||||
func (s *MilestonesService) GetMilestoneIssues(pid interface{}, milestone int, opt *GetMilestoneIssuesOptions, options ...OptionFunc) ([]*Issue, *Response, error) {
|
func (s *MilestonesService) GetMilestoneIssues(pid interface{}, milestone int, opt *GetMilestoneIssuesOptions, options ...RequestOptionFunc) ([]*Issue, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -245,7 +245,7 @@ type GetMilestoneMergeRequestsOptions ListOptions
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/milestones.html#get-all-merge-requests-assigned-to-a-single-milestone
|
// https://docs.gitlab.com/ce/api/milestones.html#get-all-merge-requests-assigned-to-a-single-milestone
|
||||||
func (s *MilestonesService) GetMilestoneMergeRequests(pid interface{}, milestone int, opt *GetMilestoneMergeRequestsOptions, options ...OptionFunc) ([]*MergeRequest, *Response, error) {
|
func (s *MilestonesService) GetMilestoneMergeRequests(pid interface{}, milestone int, opt *GetMilestoneMergeRequestsOptions, options ...RequestOptionFunc) ([]*MergeRequest, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
|
|
@ -56,7 +56,7 @@ type ListNamespacesOptions struct {
|
||||||
// ListNamespaces gets a list of projects accessible by the authenticated user.
|
// ListNamespaces gets a list of projects accessible by the authenticated user.
|
||||||
//
|
//
|
||||||
// GitLab API docs: https://docs.gitlab.com/ce/api/namespaces.html#list-namespaces
|
// GitLab API docs: https://docs.gitlab.com/ce/api/namespaces.html#list-namespaces
|
||||||
func (s *NamespacesService) ListNamespaces(opt *ListNamespacesOptions, options ...OptionFunc) ([]*Namespace, *Response, error) {
|
func (s *NamespacesService) ListNamespaces(opt *ListNamespacesOptions, options ...RequestOptionFunc) ([]*Namespace, *Response, error) {
|
||||||
req, err := s.client.NewRequest("GET", "namespaces", opt, options)
|
req, err := s.client.NewRequest("GET", "namespaces", opt, options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -76,7 +76,7 @@ func (s *NamespacesService) ListNamespaces(opt *ListNamespacesOptions, options .
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/namespaces.html#search-for-namespace
|
// https://docs.gitlab.com/ce/api/namespaces.html#search-for-namespace
|
||||||
func (s *NamespacesService) SearchNamespace(query string, options ...OptionFunc) ([]*Namespace, *Response, error) {
|
func (s *NamespacesService) SearchNamespace(query string, options ...RequestOptionFunc) ([]*Namespace, *Response, error) {
|
||||||
var q struct {
|
var q struct {
|
||||||
Search string `url:"search,omitempty" json:"search,omitempty"`
|
Search string `url:"search,omitempty" json:"search,omitempty"`
|
||||||
}
|
}
|
||||||
|
@ -100,7 +100,7 @@ func (s *NamespacesService) SearchNamespace(query string, options ...OptionFunc)
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/namespaces.html#get-namespace-by-id
|
// https://docs.gitlab.com/ce/api/namespaces.html#get-namespace-by-id
|
||||||
func (s *NamespacesService) GetNamespace(id interface{}, options ...OptionFunc) (*Namespace, *Response, error) {
|
func (s *NamespacesService) GetNamespace(id interface{}, options ...RequestOptionFunc) (*Namespace, *Response, error) {
|
||||||
namespace, err := parseID(id)
|
namespace, err := parseID(id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
|
|
@ -102,7 +102,7 @@ type ListIssueNotesOptions struct {
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/notes.html#list-project-issue-notes
|
// https://docs.gitlab.com/ce/api/notes.html#list-project-issue-notes
|
||||||
func (s *NotesService) ListIssueNotes(pid interface{}, issue int, opt *ListIssueNotesOptions, options ...OptionFunc) ([]*Note, *Response, error) {
|
func (s *NotesService) ListIssueNotes(pid interface{}, issue int, opt *ListIssueNotesOptions, options ...RequestOptionFunc) ([]*Note, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -127,7 +127,7 @@ func (s *NotesService) ListIssueNotes(pid interface{}, issue int, opt *ListIssue
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/notes.html#get-single-issue-note
|
// https://docs.gitlab.com/ce/api/notes.html#get-single-issue-note
|
||||||
func (s *NotesService) GetIssueNote(pid interface{}, issue, note int, options ...OptionFunc) (*Note, *Response, error) {
|
func (s *NotesService) GetIssueNote(pid interface{}, issue, note int, options ...RequestOptionFunc) (*Note, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -162,7 +162,7 @@ type CreateIssueNoteOptions struct {
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/notes.html#create-new-issue-note
|
// https://docs.gitlab.com/ce/api/notes.html#create-new-issue-note
|
||||||
func (s *NotesService) CreateIssueNote(pid interface{}, issue int, opt *CreateIssueNoteOptions, options ...OptionFunc) (*Note, *Response, error) {
|
func (s *NotesService) CreateIssueNote(pid interface{}, issue int, opt *CreateIssueNoteOptions, options ...RequestOptionFunc) (*Note, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -196,7 +196,7 @@ type UpdateIssueNoteOptions struct {
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/notes.html#modify-existing-issue-note
|
// https://docs.gitlab.com/ce/api/notes.html#modify-existing-issue-note
|
||||||
func (s *NotesService) UpdateIssueNote(pid interface{}, issue, note int, opt *UpdateIssueNoteOptions, options ...OptionFunc) (*Note, *Response, error) {
|
func (s *NotesService) UpdateIssueNote(pid interface{}, issue, note int, opt *UpdateIssueNoteOptions, options ...RequestOptionFunc) (*Note, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -221,7 +221,7 @@ func (s *NotesService) UpdateIssueNote(pid interface{}, issue, note int, opt *Up
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/notes.html#delete-an-issue-note
|
// https://docs.gitlab.com/ce/api/notes.html#delete-an-issue-note
|
||||||
func (s *NotesService) DeleteIssueNote(pid interface{}, issue, note int, options ...OptionFunc) (*Response, error) {
|
func (s *NotesService) DeleteIssueNote(pid interface{}, issue, note int, options ...RequestOptionFunc) (*Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -251,7 +251,7 @@ type ListSnippetNotesOptions struct {
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/notes.html#list-all-snippet-notes
|
// https://docs.gitlab.com/ce/api/notes.html#list-all-snippet-notes
|
||||||
func (s *NotesService) ListSnippetNotes(pid interface{}, snippet int, opt *ListSnippetNotesOptions, options ...OptionFunc) ([]*Note, *Response, error) {
|
func (s *NotesService) ListSnippetNotes(pid interface{}, snippet int, opt *ListSnippetNotesOptions, options ...RequestOptionFunc) ([]*Note, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -276,7 +276,7 @@ func (s *NotesService) ListSnippetNotes(pid interface{}, snippet int, opt *ListS
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/notes.html#get-single-snippet-note
|
// https://docs.gitlab.com/ce/api/notes.html#get-single-snippet-note
|
||||||
func (s *NotesService) GetSnippetNote(pid interface{}, snippet, note int, options ...OptionFunc) (*Note, *Response, error) {
|
func (s *NotesService) GetSnippetNote(pid interface{}, snippet, note int, options ...RequestOptionFunc) (*Note, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -311,7 +311,7 @@ type CreateSnippetNoteOptions struct {
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/notes.html#create-new-snippet-note
|
// https://docs.gitlab.com/ce/api/notes.html#create-new-snippet-note
|
||||||
func (s *NotesService) CreateSnippetNote(pid interface{}, snippet int, opt *CreateSnippetNoteOptions, options ...OptionFunc) (*Note, *Response, error) {
|
func (s *NotesService) CreateSnippetNote(pid interface{}, snippet int, opt *CreateSnippetNoteOptions, options ...RequestOptionFunc) (*Note, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -345,7 +345,7 @@ type UpdateSnippetNoteOptions struct {
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/notes.html#modify-existing-snippet-note
|
// https://docs.gitlab.com/ce/api/notes.html#modify-existing-snippet-note
|
||||||
func (s *NotesService) UpdateSnippetNote(pid interface{}, snippet, note int, opt *UpdateSnippetNoteOptions, options ...OptionFunc) (*Note, *Response, error) {
|
func (s *NotesService) UpdateSnippetNote(pid interface{}, snippet, note int, opt *UpdateSnippetNoteOptions, options ...RequestOptionFunc) (*Note, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -370,7 +370,7 @@ func (s *NotesService) UpdateSnippetNote(pid interface{}, snippet, note int, opt
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/notes.html#delete-a-snippet-note
|
// https://docs.gitlab.com/ce/api/notes.html#delete-a-snippet-note
|
||||||
func (s *NotesService) DeleteSnippetNote(pid interface{}, snippet, note int, options ...OptionFunc) (*Response, error) {
|
func (s *NotesService) DeleteSnippetNote(pid interface{}, snippet, note int, options ...RequestOptionFunc) (*Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -400,7 +400,7 @@ type ListMergeRequestNotesOptions struct {
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/notes.html#list-all-merge-request-notes
|
// https://docs.gitlab.com/ce/api/notes.html#list-all-merge-request-notes
|
||||||
func (s *NotesService) ListMergeRequestNotes(pid interface{}, mergeRequest int, opt *ListMergeRequestNotesOptions, options ...OptionFunc) ([]*Note, *Response, error) {
|
func (s *NotesService) ListMergeRequestNotes(pid interface{}, mergeRequest int, opt *ListMergeRequestNotesOptions, options ...RequestOptionFunc) ([]*Note, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -425,7 +425,7 @@ func (s *NotesService) ListMergeRequestNotes(pid interface{}, mergeRequest int,
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/notes.html#get-single-merge-request-note
|
// https://docs.gitlab.com/ce/api/notes.html#get-single-merge-request-note
|
||||||
func (s *NotesService) GetMergeRequestNote(pid interface{}, mergeRequest, note int, options ...OptionFunc) (*Note, *Response, error) {
|
func (s *NotesService) GetMergeRequestNote(pid interface{}, mergeRequest, note int, options ...RequestOptionFunc) (*Note, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -459,7 +459,7 @@ type CreateMergeRequestNoteOptions struct {
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/notes.html#create-new-merge-request-note
|
// https://docs.gitlab.com/ce/api/notes.html#create-new-merge-request-note
|
||||||
func (s *NotesService) CreateMergeRequestNote(pid interface{}, mergeRequest int, opt *CreateMergeRequestNoteOptions, options ...OptionFunc) (*Note, *Response, error) {
|
func (s *NotesService) CreateMergeRequestNote(pid interface{}, mergeRequest int, opt *CreateMergeRequestNoteOptions, options ...RequestOptionFunc) (*Note, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -493,7 +493,7 @@ type UpdateMergeRequestNoteOptions struct {
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/notes.html#modify-existing-merge-request-note
|
// https://docs.gitlab.com/ce/api/notes.html#modify-existing-merge-request-note
|
||||||
func (s *NotesService) UpdateMergeRequestNote(pid interface{}, mergeRequest, note int, opt *UpdateMergeRequestNoteOptions, options ...OptionFunc) (*Note, *Response, error) {
|
func (s *NotesService) UpdateMergeRequestNote(pid interface{}, mergeRequest, note int, opt *UpdateMergeRequestNoteOptions, options ...RequestOptionFunc) (*Note, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -518,7 +518,7 @@ func (s *NotesService) UpdateMergeRequestNote(pid interface{}, mergeRequest, not
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/notes.html#delete-a-merge-request-note
|
// https://docs.gitlab.com/ce/api/notes.html#delete-a-merge-request-note
|
||||||
func (s *NotesService) DeleteMergeRequestNote(pid interface{}, mergeRequest, note int, options ...OptionFunc) (*Response, error) {
|
func (s *NotesService) DeleteMergeRequestNote(pid interface{}, mergeRequest, note int, options ...RequestOptionFunc) (*Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -548,7 +548,7 @@ type ListEpicNotesOptions struct {
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ee/api/notes.html#list-all-epic-notes
|
// https://docs.gitlab.com/ee/api/notes.html#list-all-epic-notes
|
||||||
func (s *NotesService) ListEpicNotes(gid interface{}, epic int, opt *ListEpicNotesOptions, options ...OptionFunc) ([]*Note, *Response, error) {
|
func (s *NotesService) ListEpicNotes(gid interface{}, epic int, opt *ListEpicNotesOptions, options ...RequestOptionFunc) ([]*Note, *Response, error) {
|
||||||
group, err := parseID(gid)
|
group, err := parseID(gid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -573,7 +573,7 @@ func (s *NotesService) ListEpicNotes(gid interface{}, epic int, opt *ListEpicNot
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ee/api/notes.html#get-single-epic-note
|
// https://docs.gitlab.com/ee/api/notes.html#get-single-epic-note
|
||||||
func (s *NotesService) GetEpicNote(gid interface{}, epic, note int, options ...OptionFunc) (*Note, *Response, error) {
|
func (s *NotesService) GetEpicNote(gid interface{}, epic, note int, options ...RequestOptionFunc) (*Note, *Response, error) {
|
||||||
group, err := parseID(gid)
|
group, err := parseID(gid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -606,7 +606,7 @@ type CreateEpicNoteOptions struct {
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ee/api/notes.html#create-new-epic-note
|
// https://docs.gitlab.com/ee/api/notes.html#create-new-epic-note
|
||||||
func (s *NotesService) CreateEpicNote(gid interface{}, epic int, opt *CreateEpicNoteOptions, options ...OptionFunc) (*Note, *Response, error) {
|
func (s *NotesService) CreateEpicNote(gid interface{}, epic int, opt *CreateEpicNoteOptions, options ...RequestOptionFunc) (*Note, *Response, error) {
|
||||||
group, err := parseID(gid)
|
group, err := parseID(gid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -638,7 +638,7 @@ type UpdateEpicNoteOptions struct {
|
||||||
// UpdateEpicNote modifies existing note of an epic.
|
// UpdateEpicNote modifies existing note of an epic.
|
||||||
//
|
//
|
||||||
// https://docs.gitlab.com/ee/api/notes.html#modify-existing-epic-note
|
// https://docs.gitlab.com/ee/api/notes.html#modify-existing-epic-note
|
||||||
func (s *NotesService) UpdateEpicNote(gid interface{}, epic, note int, opt *UpdateEpicNoteOptions, options ...OptionFunc) (*Note, *Response, error) {
|
func (s *NotesService) UpdateEpicNote(gid interface{}, epic, note int, opt *UpdateEpicNoteOptions, options ...RequestOptionFunc) (*Note, *Response, error) {
|
||||||
group, err := parseID(gid)
|
group, err := parseID(gid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -662,7 +662,7 @@ func (s *NotesService) UpdateEpicNote(gid interface{}, epic, note int, opt *Upda
|
||||||
// DeleteEpicNote deletes an existing note of a merge request.
|
// DeleteEpicNote deletes an existing note of a merge request.
|
||||||
//
|
//
|
||||||
// https://docs.gitlab.com/ee/api/notes.html#delete-an-epic-note
|
// https://docs.gitlab.com/ee/api/notes.html#delete-an-epic-note
|
||||||
func (s *NotesService) DeleteEpicNote(gid interface{}, epic, note int, options ...OptionFunc) (*Response, error) {
|
func (s *NotesService) DeleteEpicNote(gid interface{}, epic, note int, options ...RequestOptionFunc) (*Response, error) {
|
||||||
group, err := parseID(gid)
|
group, err := parseID(gid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -50,7 +50,7 @@ func (ns NotificationSettings) String() string {
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/notification_settings.html#global-notification-settings
|
// https://docs.gitlab.com/ce/api/notification_settings.html#global-notification-settings
|
||||||
func (s *NotificationSettingsService) GetGlobalSettings(options ...OptionFunc) (*NotificationSettings, *Response, error) {
|
func (s *NotificationSettingsService) GetGlobalSettings(options ...RequestOptionFunc) (*NotificationSettings, *Response, error) {
|
||||||
u := "notification_settings"
|
u := "notification_settings"
|
||||||
|
|
||||||
req, err := s.client.NewRequest("GET", u, nil, options)
|
req, err := s.client.NewRequest("GET", u, nil, options)
|
||||||
|
@ -90,7 +90,7 @@ type NotificationSettingsOptions struct {
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/notification_settings.html#update-global-notification-settings
|
// https://docs.gitlab.com/ce/api/notification_settings.html#update-global-notification-settings
|
||||||
func (s *NotificationSettingsService) UpdateGlobalSettings(opt *NotificationSettingsOptions, options ...OptionFunc) (*NotificationSettings, *Response, error) {
|
func (s *NotificationSettingsService) UpdateGlobalSettings(opt *NotificationSettingsOptions, options ...RequestOptionFunc) (*NotificationSettings, *Response, error) {
|
||||||
if opt.Level != nil && *opt.Level == GlobalNotificationLevel {
|
if opt.Level != nil && *opt.Level == GlobalNotificationLevel {
|
||||||
return nil, nil, errors.New(
|
return nil, nil, errors.New(
|
||||||
"notification level 'global' is not valid for global notification settings")
|
"notification level 'global' is not valid for global notification settings")
|
||||||
|
@ -116,7 +116,7 @@ func (s *NotificationSettingsService) UpdateGlobalSettings(opt *NotificationSett
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/notification_settings.html#group-project-level-notification-settings
|
// https://docs.gitlab.com/ce/api/notification_settings.html#group-project-level-notification-settings
|
||||||
func (s *NotificationSettingsService) GetSettingsForGroup(gid interface{}, options ...OptionFunc) (*NotificationSettings, *Response, error) {
|
func (s *NotificationSettingsService) GetSettingsForGroup(gid interface{}, options ...RequestOptionFunc) (*NotificationSettings, *Response, error) {
|
||||||
group, err := parseID(gid)
|
group, err := parseID(gid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -141,7 +141,7 @@ func (s *NotificationSettingsService) GetSettingsForGroup(gid interface{}, optio
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/notification_settings.html#group-project-level-notification-settings
|
// https://docs.gitlab.com/ce/api/notification_settings.html#group-project-level-notification-settings
|
||||||
func (s *NotificationSettingsService) GetSettingsForProject(pid interface{}, options ...OptionFunc) (*NotificationSettings, *Response, error) {
|
func (s *NotificationSettingsService) GetSettingsForProject(pid interface{}, options ...RequestOptionFunc) (*NotificationSettings, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -166,7 +166,7 @@ func (s *NotificationSettingsService) GetSettingsForProject(pid interface{}, opt
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/notification_settings.html#update-group-project-level-notification-settings
|
// https://docs.gitlab.com/ce/api/notification_settings.html#update-group-project-level-notification-settings
|
||||||
func (s *NotificationSettingsService) UpdateSettingsForGroup(gid interface{}, opt *NotificationSettingsOptions, options ...OptionFunc) (*NotificationSettings, *Response, error) {
|
func (s *NotificationSettingsService) UpdateSettingsForGroup(gid interface{}, opt *NotificationSettingsOptions, options ...RequestOptionFunc) (*NotificationSettings, *Response, error) {
|
||||||
group, err := parseID(gid)
|
group, err := parseID(gid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -191,7 +191,7 @@ func (s *NotificationSettingsService) UpdateSettingsForGroup(gid interface{}, op
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/notification_settings.html#update-group-project-level-notification-settings
|
// https://docs.gitlab.com/ce/api/notification_settings.html#update-group-project-level-notification-settings
|
||||||
func (s *NotificationSettingsService) UpdateSettingsForProject(pid interface{}, opt *NotificationSettingsOptions, options ...OptionFunc) (*NotificationSettings, *Response, error) {
|
func (s *NotificationSettingsService) UpdateSettingsForProject(pid interface{}, opt *NotificationSettingsOptions, options ...RequestOptionFunc) (*NotificationSettings, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
|
|
@ -18,6 +18,7 @@ type PagesDomainsService struct {
|
||||||
// GitLab API docs: https://docs.gitlab.com/ce/api/pages_domains.html
|
// GitLab API docs: https://docs.gitlab.com/ce/api/pages_domains.html
|
||||||
type PagesDomain struct {
|
type PagesDomain struct {
|
||||||
Domain string `json:"domain"`
|
Domain string `json:"domain"`
|
||||||
|
AutoSslEnabled bool `json:"auto_ssl_enabled"`
|
||||||
URL string `json:"url"`
|
URL string `json:"url"`
|
||||||
ProjectID int `json:"project_id"`
|
ProjectID int `json:"project_id"`
|
||||||
Verified bool `json:"verified"`
|
Verified bool `json:"verified"`
|
||||||
|
@ -39,7 +40,7 @@ type ListPagesDomainsOptions ListOptions
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/pages_domains.html#list-pages-domains
|
// https://docs.gitlab.com/ce/api/pages_domains.html#list-pages-domains
|
||||||
func (s *PagesDomainsService) ListPagesDomains(pid interface{}, opt *ListPagesDomainsOptions, options ...OptionFunc) ([]*PagesDomain, *Response, error) {
|
func (s *PagesDomainsService) ListPagesDomains(pid interface{}, opt *ListPagesDomainsOptions, options ...RequestOptionFunc) ([]*PagesDomain, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -64,7 +65,7 @@ func (s *PagesDomainsService) ListPagesDomains(pid interface{}, opt *ListPagesDo
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/pages_domains.html#list-all-pages-domains
|
// https://docs.gitlab.com/ce/api/pages_domains.html#list-all-pages-domains
|
||||||
func (s *PagesDomainsService) ListAllPagesDomains(options ...OptionFunc) ([]*PagesDomain, *Response, error) {
|
func (s *PagesDomainsService) ListAllPagesDomains(options ...RequestOptionFunc) ([]*PagesDomain, *Response, error) {
|
||||||
req, err := s.client.NewRequest("GET", "pages/domains", nil, options)
|
req, err := s.client.NewRequest("GET", "pages/domains", nil, options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -83,7 +84,7 @@ func (s *PagesDomainsService) ListAllPagesDomains(options ...OptionFunc) ([]*Pag
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/pages_domains.html#single-pages-domain
|
// https://docs.gitlab.com/ce/api/pages_domains.html#single-pages-domain
|
||||||
func (s *PagesDomainsService) GetPagesDomain(pid interface{}, domain string, options ...OptionFunc) (*PagesDomain, *Response, error) {
|
func (s *PagesDomainsService) GetPagesDomain(pid interface{}, domain string, options ...RequestOptionFunc) (*PagesDomain, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -107,9 +108,10 @@ func (s *PagesDomainsService) GetPagesDomain(pid interface{}, domain string, opt
|
||||||
// CreatePagesDomainOptions represents the available CreatePagesDomain() options.
|
// CreatePagesDomainOptions represents the available CreatePagesDomain() options.
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// // https://docs.gitlab.com/ce/api/pages_domains.html#create-new-pages-domain
|
// https://docs.gitlab.com/ce/api/pages_domains.html#create-new-pages-domain
|
||||||
type CreatePagesDomainOptions struct {
|
type CreatePagesDomainOptions struct {
|
||||||
Domain *string `url:"domain,omitempty" json:"domain,omitempty"`
|
Domain *string `url:"domain,omitempty" json:"domain,omitempty"`
|
||||||
|
AutoSslEnabled *bool `url:"auto_ssl_enabled,omitempty" json:"auto_ssl_enabled,omitempty"`
|
||||||
Certificate *string `url:"certifiate,omitempty" json:"certifiate,omitempty"`
|
Certificate *string `url:"certifiate,omitempty" json:"certifiate,omitempty"`
|
||||||
Key *string `url:"key,omitempty" json:"key,omitempty"`
|
Key *string `url:"key,omitempty" json:"key,omitempty"`
|
||||||
}
|
}
|
||||||
|
@ -118,7 +120,7 @@ type CreatePagesDomainOptions struct {
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/pages_domains.html#create-new-pages-domain
|
// https://docs.gitlab.com/ce/api/pages_domains.html#create-new-pages-domain
|
||||||
func (s *PagesDomainsService) CreatePagesDomain(pid interface{}, opt *CreatePagesDomainOptions, options ...OptionFunc) (*PagesDomain, *Response, error) {
|
func (s *PagesDomainsService) CreatePagesDomain(pid interface{}, opt *CreatePagesDomainOptions, options ...RequestOptionFunc) (*PagesDomain, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -144,15 +146,16 @@ func (s *PagesDomainsService) CreatePagesDomain(pid interface{}, opt *CreatePage
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/pages_domains.html#update-pages-domain
|
// https://docs.gitlab.com/ce/api/pages_domains.html#update-pages-domain
|
||||||
type UpdatePagesDomainOptions struct {
|
type UpdatePagesDomainOptions struct {
|
||||||
Cerificate *string `url:"certifiate" json:"certifiate"`
|
AutoSslEnabled *bool `url:"auto_ssl_enabled,omitempty" json:"auto_ssl_enabled,omitempty"`
|
||||||
Key *string `url:"key" json:"key"`
|
Certificate *string `url:"certifiate,omitempty" json:"certifiate,omitempty"`
|
||||||
|
Key *string `url:"key,omitempty" json:"key,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdatePagesDomain updates an existing project pages domain.
|
// UpdatePagesDomain updates an existing project pages domain.
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/pages_domains.html#update-pages-domain
|
// https://docs.gitlab.com/ce/api/pages_domains.html#update-pages-domain
|
||||||
func (s *PagesDomainsService) UpdatePagesDomain(pid interface{}, domain string, opt *UpdatePagesDomainOptions, options ...OptionFunc) (*PagesDomain, *Response, error) {
|
func (s *PagesDomainsService) UpdatePagesDomain(pid interface{}, domain string, opt *UpdatePagesDomainOptions, options ...RequestOptionFunc) (*PagesDomain, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -177,7 +180,7 @@ func (s *PagesDomainsService) UpdatePagesDomain(pid interface{}, domain string,
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/pages_domains.html#delete-pages-domain
|
// https://docs.gitlab.com/ce/api/pages_domains.html#delete-pages-domain
|
||||||
func (s *PagesDomainsService) DeletePagesDomain(pid interface{}, domain string, options ...OptionFunc) (*Response, error) {
|
func (s *PagesDomainsService) DeletePagesDomain(pid interface{}, domain string, options ...RequestOptionFunc) (*Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -63,7 +63,7 @@ type ListPipelineSchedulesOptions ListOptions
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/pipeline_schedules.html
|
// https://docs.gitlab.com/ce/api/pipeline_schedules.html
|
||||||
func (s *PipelineSchedulesService) ListPipelineSchedules(pid interface{}, opt *ListPipelineSchedulesOptions, options ...OptionFunc) ([]*PipelineSchedule, *Response, error) {
|
func (s *PipelineSchedulesService) ListPipelineSchedules(pid interface{}, opt *ListPipelineSchedulesOptions, options ...RequestOptionFunc) ([]*PipelineSchedule, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -88,7 +88,7 @@ func (s *PipelineSchedulesService) ListPipelineSchedules(pid interface{}, opt *L
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/pipeline_schedules.html
|
// https://docs.gitlab.com/ce/api/pipeline_schedules.html
|
||||||
func (s *PipelineSchedulesService) GetPipelineSchedule(pid interface{}, schedule int, options ...OptionFunc) (*PipelineSchedule, *Response, error) {
|
func (s *PipelineSchedulesService) GetPipelineSchedule(pid interface{}, schedule int, options ...RequestOptionFunc) (*PipelineSchedule, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -126,7 +126,7 @@ type CreatePipelineScheduleOptions struct {
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/pipeline_schedules.html#create-a-new-pipeline-schedule
|
// https://docs.gitlab.com/ce/api/pipeline_schedules.html#create-a-new-pipeline-schedule
|
||||||
func (s *PipelineSchedulesService) CreatePipelineSchedule(pid interface{}, opt *CreatePipelineScheduleOptions, options ...OptionFunc) (*PipelineSchedule, *Response, error) {
|
func (s *PipelineSchedulesService) CreatePipelineSchedule(pid interface{}, opt *CreatePipelineScheduleOptions, options ...RequestOptionFunc) (*PipelineSchedule, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -164,7 +164,7 @@ type EditPipelineScheduleOptions struct {
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/pipeline_schedules.html#edit-a-pipeline-schedule
|
// https://docs.gitlab.com/ce/api/pipeline_schedules.html#edit-a-pipeline-schedule
|
||||||
func (s *PipelineSchedulesService) EditPipelineSchedule(pid interface{}, schedule int, opt *EditPipelineScheduleOptions, options ...OptionFunc) (*PipelineSchedule, *Response, error) {
|
func (s *PipelineSchedulesService) EditPipelineSchedule(pid interface{}, schedule int, opt *EditPipelineScheduleOptions, options ...RequestOptionFunc) (*PipelineSchedule, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -190,7 +190,7 @@ func (s *PipelineSchedulesService) EditPipelineSchedule(pid interface{}, schedul
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/pipeline_schedules.html#take-ownership-of-a-pipeline-schedule
|
// https://docs.gitlab.com/ce/api/pipeline_schedules.html#take-ownership-of-a-pipeline-schedule
|
||||||
func (s *PipelineSchedulesService) TakeOwnershipOfPipelineSchedule(pid interface{}, schedule int, options ...OptionFunc) (*PipelineSchedule, *Response, error) {
|
func (s *PipelineSchedulesService) TakeOwnershipOfPipelineSchedule(pid interface{}, schedule int, options ...RequestOptionFunc) (*PipelineSchedule, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -215,7 +215,7 @@ func (s *PipelineSchedulesService) TakeOwnershipOfPipelineSchedule(pid interface
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/pipeline_schedules.html#delete-a-pipeline-schedule
|
// https://docs.gitlab.com/ce/api/pipeline_schedules.html#delete-a-pipeline-schedule
|
||||||
func (s *PipelineSchedulesService) DeletePipelineSchedule(pid interface{}, schedule int, options ...OptionFunc) (*Response, error) {
|
func (s *PipelineSchedulesService) DeletePipelineSchedule(pid interface{}, schedule int, options ...RequestOptionFunc) (*Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -245,7 +245,7 @@ type CreatePipelineScheduleVariableOptions struct {
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/pipeline_schedules.html#create-a-new-pipeline-schedule
|
// https://docs.gitlab.com/ce/api/pipeline_schedules.html#create-a-new-pipeline-schedule
|
||||||
func (s *PipelineSchedulesService) CreatePipelineScheduleVariable(pid interface{}, schedule int, opt *CreatePipelineScheduleVariableOptions, options ...OptionFunc) (*PipelineVariable, *Response, error) {
|
func (s *PipelineSchedulesService) CreatePipelineScheduleVariable(pid interface{}, schedule int, opt *CreatePipelineScheduleVariableOptions, options ...RequestOptionFunc) (*PipelineVariable, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -280,7 +280,7 @@ type EditPipelineScheduleVariableOptions struct {
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/pipeline_schedules.html#edit-a-pipeline-schedule-variable
|
// https://docs.gitlab.com/ce/api/pipeline_schedules.html#edit-a-pipeline-schedule-variable
|
||||||
func (s *PipelineSchedulesService) EditPipelineScheduleVariable(pid interface{}, schedule int, key string, opt *EditPipelineScheduleVariableOptions, options ...OptionFunc) (*PipelineVariable, *Response, error) {
|
func (s *PipelineSchedulesService) EditPipelineScheduleVariable(pid interface{}, schedule int, key string, opt *EditPipelineScheduleVariableOptions, options ...RequestOptionFunc) (*PipelineVariable, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -305,7 +305,7 @@ func (s *PipelineSchedulesService) EditPipelineScheduleVariable(pid interface{},
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/pipeline_schedules.html#delete-a-pipeline-schedule-variable
|
// https://docs.gitlab.com/ce/api/pipeline_schedules.html#delete-a-pipeline-schedule-variable
|
||||||
func (s *PipelineSchedulesService) DeletePipelineScheduleVariable(pid interface{}, schedule int, key string, options ...OptionFunc) (*PipelineVariable, *Response, error) {
|
func (s *PipelineSchedulesService) DeletePipelineScheduleVariable(pid interface{}, schedule int, key string, options ...RequestOptionFunc) (*PipelineVariable, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
|
|
@ -38,7 +38,7 @@ type ListPipelineTriggersOptions ListOptions
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/pipeline_triggers.html#list-project-triggers
|
// https://docs.gitlab.com/ce/api/pipeline_triggers.html#list-project-triggers
|
||||||
func (s *PipelineTriggersService) ListPipelineTriggers(pid interface{}, opt *ListPipelineTriggersOptions, options ...OptionFunc) ([]*PipelineTrigger, *Response, error) {
|
func (s *PipelineTriggersService) ListPipelineTriggers(pid interface{}, opt *ListPipelineTriggersOptions, options ...RequestOptionFunc) ([]*PipelineTrigger, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -63,7 +63,7 @@ func (s *PipelineTriggersService) ListPipelineTriggers(pid interface{}, opt *Lis
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/pipeline_triggers.html#get-trigger-details
|
// https://docs.gitlab.com/ce/api/pipeline_triggers.html#get-trigger-details
|
||||||
func (s *PipelineTriggersService) GetPipelineTrigger(pid interface{}, trigger int, options ...OptionFunc) (*PipelineTrigger, *Response, error) {
|
func (s *PipelineTriggersService) GetPipelineTrigger(pid interface{}, trigger int, options ...RequestOptionFunc) (*PipelineTrigger, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -96,7 +96,7 @@ type AddPipelineTriggerOptions struct {
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/pipeline_triggers.html#create-a-project-trigger
|
// https://docs.gitlab.com/ce/api/pipeline_triggers.html#create-a-project-trigger
|
||||||
func (s *PipelineTriggersService) AddPipelineTrigger(pid interface{}, opt *AddPipelineTriggerOptions, options ...OptionFunc) (*PipelineTrigger, *Response, error) {
|
func (s *PipelineTriggersService) AddPipelineTrigger(pid interface{}, opt *AddPipelineTriggerOptions, options ...RequestOptionFunc) (*PipelineTrigger, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -129,7 +129,7 @@ type EditPipelineTriggerOptions struct {
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/pipeline_triggers.html#update-a-project-trigger
|
// https://docs.gitlab.com/ce/api/pipeline_triggers.html#update-a-project-trigger
|
||||||
func (s *PipelineTriggersService) EditPipelineTrigger(pid interface{}, trigger int, opt *EditPipelineTriggerOptions, options ...OptionFunc) (*PipelineTrigger, *Response, error) {
|
func (s *PipelineTriggersService) EditPipelineTrigger(pid interface{}, trigger int, opt *EditPipelineTriggerOptions, options ...RequestOptionFunc) (*PipelineTrigger, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -155,7 +155,7 @@ func (s *PipelineTriggersService) EditPipelineTrigger(pid interface{}, trigger i
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/pipeline_triggers.html#take-ownership-of-a-project-trigger
|
// https://docs.gitlab.com/ce/api/pipeline_triggers.html#take-ownership-of-a-project-trigger
|
||||||
func (s *PipelineTriggersService) TakeOwnershipOfPipelineTrigger(pid interface{}, trigger int, options ...OptionFunc) (*PipelineTrigger, *Response, error) {
|
func (s *PipelineTriggersService) TakeOwnershipOfPipelineTrigger(pid interface{}, trigger int, options ...RequestOptionFunc) (*PipelineTrigger, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -180,7 +180,7 @@ func (s *PipelineTriggersService) TakeOwnershipOfPipelineTrigger(pid interface{}
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/pipeline_triggers.html#remove-a-project-trigger
|
// https://docs.gitlab.com/ce/api/pipeline_triggers.html#remove-a-project-trigger
|
||||||
func (s *PipelineTriggersService) DeletePipelineTrigger(pid interface{}, trigger int, options ...OptionFunc) (*Response, error) {
|
func (s *PipelineTriggersService) DeletePipelineTrigger(pid interface{}, trigger int, options ...RequestOptionFunc) (*Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -209,7 +209,7 @@ type RunPipelineTriggerOptions struct {
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/ci/triggers/README.html#triggering-a-pipeline
|
// https://docs.gitlab.com/ce/ci/triggers/README.html#triggering-a-pipeline
|
||||||
func (s *PipelineTriggersService) RunPipelineTrigger(pid interface{}, opt *RunPipelineTriggerOptions, options ...OptionFunc) (*Pipeline, *Response, error) {
|
func (s *PipelineTriggersService) RunPipelineTrigger(pid interface{}, opt *RunPipelineTriggerOptions, options ...RequestOptionFunc) (*Pipeline, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
|
|
@ -108,6 +108,8 @@ type ListProjectPipelinesOptions struct {
|
||||||
YamlErrors *bool `url:"yaml_errors,omitempty" json:"yaml_errors,omitempty"`
|
YamlErrors *bool `url:"yaml_errors,omitempty" json:"yaml_errors,omitempty"`
|
||||||
Name *string `url:"name,omitempty" json:"name,omitempty"`
|
Name *string `url:"name,omitempty" json:"name,omitempty"`
|
||||||
Username *string `url:"username,omitempty" json:"username,omitempty"`
|
Username *string `url:"username,omitempty" json:"username,omitempty"`
|
||||||
|
UpdatedAfter *time.Time `url:"updated_after,omitempty" json:"updated_after,omitempty"`
|
||||||
|
UpdatedBefore *time.Time `url:"update_before,omitempty" json:"updated_before,omitempty"`
|
||||||
OrderBy *string `url:"order_by,omitempty" json:"order_by,omitempty"`
|
OrderBy *string `url:"order_by,omitempty" json:"order_by,omitempty"`
|
||||||
Sort *string `url:"sort,omitempty" json:"sort,omitempty"`
|
Sort *string `url:"sort,omitempty" json:"sort,omitempty"`
|
||||||
}
|
}
|
||||||
|
@ -115,7 +117,7 @@ type ListProjectPipelinesOptions struct {
|
||||||
// ListProjectPipelines gets a list of project piplines.
|
// ListProjectPipelines gets a list of project piplines.
|
||||||
//
|
//
|
||||||
// GitLab API docs: https://docs.gitlab.com/ce/api/pipelines.html#list-project-pipelines
|
// GitLab API docs: https://docs.gitlab.com/ce/api/pipelines.html#list-project-pipelines
|
||||||
func (s *PipelinesService) ListProjectPipelines(pid interface{}, opt *ListProjectPipelinesOptions, options ...OptionFunc) ([]*PipelineInfo, *Response, error) {
|
func (s *PipelinesService) ListProjectPipelines(pid interface{}, opt *ListProjectPipelinesOptions, options ...RequestOptionFunc) ([]*PipelineInfo, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -139,7 +141,7 @@ func (s *PipelinesService) ListProjectPipelines(pid interface{}, opt *ListProjec
|
||||||
// GetPipeline gets a single project pipeline.
|
// GetPipeline gets a single project pipeline.
|
||||||
//
|
//
|
||||||
// GitLab API docs: https://docs.gitlab.com/ce/api/pipelines.html#get-a-single-pipeline
|
// GitLab API docs: https://docs.gitlab.com/ce/api/pipelines.html#get-a-single-pipeline
|
||||||
func (s *PipelinesService) GetPipeline(pid interface{}, pipeline int, options ...OptionFunc) (*Pipeline, *Response, error) {
|
func (s *PipelinesService) GetPipeline(pid interface{}, pipeline int, options ...RequestOptionFunc) (*Pipeline, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -163,7 +165,7 @@ func (s *PipelinesService) GetPipeline(pid interface{}, pipeline int, options ..
|
||||||
// GetPipelineVariables gets the variables of a single project pipeline.
|
// GetPipelineVariables gets the variables of a single project pipeline.
|
||||||
//
|
//
|
||||||
// GitLab API docs: https://docs.gitlab.com/ce/api/pipelines.html#get-variables-of-a-pipeline
|
// GitLab API docs: https://docs.gitlab.com/ce/api/pipelines.html#get-variables-of-a-pipeline
|
||||||
func (s *PipelinesService) GetPipelineVariables(pid interface{}, pipeline int, options ...OptionFunc) ([]*PipelineVariable, *Response, error) {
|
func (s *PipelinesService) GetPipelineVariables(pid interface{}, pipeline int, options ...RequestOptionFunc) ([]*PipelineVariable, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -195,7 +197,7 @@ type CreatePipelineOptions struct {
|
||||||
// CreatePipeline creates a new project pipeline.
|
// CreatePipeline creates a new project pipeline.
|
||||||
//
|
//
|
||||||
// GitLab API docs: https://docs.gitlab.com/ce/api/pipelines.html#create-a-new-pipeline
|
// GitLab API docs: https://docs.gitlab.com/ce/api/pipelines.html#create-a-new-pipeline
|
||||||
func (s *PipelinesService) CreatePipeline(pid interface{}, opt *CreatePipelineOptions, options ...OptionFunc) (*Pipeline, *Response, error) {
|
func (s *PipelinesService) CreatePipeline(pid interface{}, opt *CreatePipelineOptions, options ...RequestOptionFunc) (*Pipeline, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -220,7 +222,7 @@ func (s *PipelinesService) CreatePipeline(pid interface{}, opt *CreatePipelineOp
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/pipelines.html#retry-failed-builds-in-a-pipeline
|
// https://docs.gitlab.com/ce/api/pipelines.html#retry-failed-builds-in-a-pipeline
|
||||||
func (s *PipelinesService) RetryPipelineBuild(pid interface{}, pipeline int, options ...OptionFunc) (*Pipeline, *Response, error) {
|
func (s *PipelinesService) RetryPipelineBuild(pid interface{}, pipeline int, options ...RequestOptionFunc) (*Pipeline, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -245,7 +247,7 @@ func (s *PipelinesService) RetryPipelineBuild(pid interface{}, pipeline int, opt
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
//https://docs.gitlab.com/ce/api/pipelines.html#cancel-a-pipelines-builds
|
//https://docs.gitlab.com/ce/api/pipelines.html#cancel-a-pipelines-builds
|
||||||
func (s *PipelinesService) CancelPipelineBuild(pid interface{}, pipeline int, options ...OptionFunc) (*Pipeline, *Response, error) {
|
func (s *PipelinesService) CancelPipelineBuild(pid interface{}, pipeline int, options ...RequestOptionFunc) (*Pipeline, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -270,7 +272,7 @@ func (s *PipelinesService) CancelPipelineBuild(pid interface{}, pipeline int, op
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/pipelines.html#delete-a-pipeline
|
// https://docs.gitlab.com/ce/api/pipelines.html#delete-a-pipeline
|
||||||
func (s *PipelinesService) DeletePipeline(pid interface{}, pipeline int, options ...OptionFunc) (*Response, error) {
|
func (s *PipelinesService) DeletePipeline(pid interface{}, pipeline int, options ...RequestOptionFunc) (*Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -37,7 +37,7 @@ type ListProjectBadgesOptions ListOptions
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ee/api/project_badges.html#list-all-badges-of-a-project
|
// https://docs.gitlab.com/ee/api/project_badges.html#list-all-badges-of-a-project
|
||||||
func (s *ProjectBadgesService) ListProjectBadges(pid interface{}, opt *ListProjectBadgesOptions, options ...OptionFunc) ([]*ProjectBadge, *Response, error) {
|
func (s *ProjectBadgesService) ListProjectBadges(pid interface{}, opt *ListProjectBadgesOptions, options ...RequestOptionFunc) ([]*ProjectBadge, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -62,7 +62,7 @@ func (s *ProjectBadgesService) ListProjectBadges(pid interface{}, opt *ListProje
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ee/api/project_badges.html#get-a-badge-of-a-project
|
// https://docs.gitlab.com/ee/api/project_badges.html#get-a-badge-of-a-project
|
||||||
func (s *ProjectBadgesService) GetProjectBadge(pid interface{}, badge int, options ...OptionFunc) (*ProjectBadge, *Response, error) {
|
func (s *ProjectBadgesService) GetProjectBadge(pid interface{}, badge int, options ...RequestOptionFunc) (*ProjectBadge, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -96,7 +96,7 @@ type AddProjectBadgeOptions struct {
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ee/api/project_badges.html#add-a-badge-to-a-project
|
// https://docs.gitlab.com/ee/api/project_badges.html#add-a-badge-to-a-project
|
||||||
func (s *ProjectBadgesService) AddProjectBadge(pid interface{}, opt *AddProjectBadgeOptions, options ...OptionFunc) (*ProjectBadge, *Response, error) {
|
func (s *ProjectBadgesService) AddProjectBadge(pid interface{}, opt *AddProjectBadgeOptions, options ...RequestOptionFunc) (*ProjectBadge, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -130,7 +130,7 @@ type EditProjectBadgeOptions struct {
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ee/api/project_badges.html#edit-a-badge-of-a-project
|
// https://docs.gitlab.com/ee/api/project_badges.html#edit-a-badge-of-a-project
|
||||||
func (s *ProjectBadgesService) EditProjectBadge(pid interface{}, badge int, opt *EditProjectBadgeOptions, options ...OptionFunc) (*ProjectBadge, *Response, error) {
|
func (s *ProjectBadgesService) EditProjectBadge(pid interface{}, badge int, opt *EditProjectBadgeOptions, options ...RequestOptionFunc) (*ProjectBadge, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -156,7 +156,7 @@ func (s *ProjectBadgesService) EditProjectBadge(pid interface{}, badge int, opt
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ee/api/project_badges.html#remove-a-badge-from-a-project
|
// https://docs.gitlab.com/ee/api/project_badges.html#remove-a-badge-from-a-project
|
||||||
func (s *ProjectBadgesService) DeleteProjectBadge(pid interface{}, badge int, options ...OptionFunc) (*Response, error) {
|
func (s *ProjectBadgesService) DeleteProjectBadge(pid interface{}, badge int, options ...RequestOptionFunc) (*Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -185,7 +185,7 @@ type ProjectBadgePreviewOptions struct {
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ee/api/project_badges.html#preview-a-badge-from-a-project
|
// https://docs.gitlab.com/ee/api/project_badges.html#preview-a-badge-from-a-project
|
||||||
func (s *ProjectBadgesService) PreviewProjectBadge(pid interface{}, opt *ProjectBadgePreviewOptions, options ...OptionFunc) (*ProjectBadge, *Response, error) {
|
func (s *ProjectBadgesService) PreviewProjectBadge(pid interface{}, opt *ProjectBadgePreviewOptions, options ...RequestOptionFunc) (*ProjectBadge, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
|
|
@ -44,6 +44,7 @@ type ProjectCluster struct {
|
||||||
ClusterType string `json:"cluster_type"`
|
ClusterType string `json:"cluster_type"`
|
||||||
User *User `json:"user"`
|
User *User `json:"user"`
|
||||||
PlatformKubernetes *PlatformKubernetes `json:"platform_kubernetes"`
|
PlatformKubernetes *PlatformKubernetes `json:"platform_kubernetes"`
|
||||||
|
ManagementProject *ManagementProject `json:"management_project"`
|
||||||
Project *Project `json:"project"`
|
Project *Project `json:"project"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,11 +61,22 @@ type PlatformKubernetes struct {
|
||||||
AuthorizationType string `json:"authorization_type"`
|
AuthorizationType string `json:"authorization_type"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ManagementProject represents a GitLab Project Cluster management_project.
|
||||||
|
type ManagementProject struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
NameWithNamespace string `json:"name_with_namespace"`
|
||||||
|
Path string `json:"path"`
|
||||||
|
PathWithNamespace string `json:"path_with_namespace"`
|
||||||
|
CreatedAt *time.Time `json:"created_at"`
|
||||||
|
}
|
||||||
|
|
||||||
// ListClusters gets a list of all clusters in a project.
|
// ListClusters gets a list of all clusters in a project.
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ee/api/project_clusters.html#list-project-clusters
|
// https://docs.gitlab.com/ee/api/project_clusters.html#list-project-clusters
|
||||||
func (s *ProjectClustersService) ListClusters(pid interface{}, options ...OptionFunc) ([]*ProjectCluster, *Response, error) {
|
func (s *ProjectClustersService) ListClusters(pid interface{}, options ...RequestOptionFunc) ([]*ProjectCluster, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -89,7 +101,7 @@ func (s *ProjectClustersService) ListClusters(pid interface{}, options ...Option
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ee/api/project_clusters.html#get-a-single-project-cluster
|
// https://docs.gitlab.com/ee/api/project_clusters.html#get-a-single-project-cluster
|
||||||
func (s *ProjectClustersService) GetCluster(pid interface{}, cluster int, options ...OptionFunc) (*ProjectCluster, *Response, error) {
|
func (s *ProjectClustersService) GetCluster(pid interface{}, cluster int, options ...RequestOptionFunc) (*ProjectCluster, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -121,6 +133,7 @@ type AddClusterOptions struct {
|
||||||
Managed *bool `url:"managed,omitempty" json:"managed,omitempty"`
|
Managed *bool `url:"managed,omitempty" json:"managed,omitempty"`
|
||||||
EnvironmentScope *string `url:"environment_scope,omitempty" json:"environment_scope,omitempty"`
|
EnvironmentScope *string `url:"environment_scope,omitempty" json:"environment_scope,omitempty"`
|
||||||
PlatformKubernetes *AddPlatformKubernetesOptions `url:"platform_kubernetes_attributes,omitempty" json:"platform_kubernetes_attributes,omitempty"`
|
PlatformKubernetes *AddPlatformKubernetesOptions `url:"platform_kubernetes_attributes,omitempty" json:"platform_kubernetes_attributes,omitempty"`
|
||||||
|
ManagementProjectID *string `url:"management_project_id,omitempty" json:"management_project_id,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddPlatformKubernetesOptions represents the available PlatformKubernetes options for adding.
|
// AddPlatformKubernetesOptions represents the available PlatformKubernetes options for adding.
|
||||||
|
@ -136,7 +149,7 @@ type AddPlatformKubernetesOptions struct {
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ee/api/project_clusters.html#add-existing-cluster-to-project
|
// https://docs.gitlab.com/ee/api/project_clusters.html#add-existing-cluster-to-project
|
||||||
func (s *ProjectClustersService) AddCluster(pid interface{}, opt *AddClusterOptions, options ...OptionFunc) (*ProjectCluster, *Response, error) {
|
func (s *ProjectClustersService) AddCluster(pid interface{}, opt *AddClusterOptions, options ...RequestOptionFunc) (*ProjectCluster, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -165,6 +178,7 @@ type EditClusterOptions struct {
|
||||||
Name *string `url:"name,omitempty" json:"name,omitempty"`
|
Name *string `url:"name,omitempty" json:"name,omitempty"`
|
||||||
Domain *string `url:"domain,omitempty" json:"domain,omitempty"`
|
Domain *string `url:"domain,omitempty" json:"domain,omitempty"`
|
||||||
EnvironmentScope *string `url:"environment_scope,omitempty" json:"environment_scope,omitempty"`
|
EnvironmentScope *string `url:"environment_scope,omitempty" json:"environment_scope,omitempty"`
|
||||||
|
ManagementProjectID *string `url:"management_project_id,omitempty" json:"management_project_id,omitempty"`
|
||||||
PlatformKubernetes *EditPlatformKubernetesOptions `url:"platform_kubernetes_attributes,omitempty" json:"platform_kubernetes_attributes,omitempty"`
|
PlatformKubernetes *EditPlatformKubernetesOptions `url:"platform_kubernetes_attributes,omitempty" json:"platform_kubernetes_attributes,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -180,7 +194,7 @@ type EditPlatformKubernetesOptions struct {
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ee/api/project_clusters.html#edit-project-cluster
|
// https://docs.gitlab.com/ee/api/project_clusters.html#edit-project-cluster
|
||||||
func (s *ProjectClustersService) EditCluster(pid interface{}, cluster int, opt *EditClusterOptions, options ...OptionFunc) (*ProjectCluster, *Response, error) {
|
func (s *ProjectClustersService) EditCluster(pid interface{}, cluster int, opt *EditClusterOptions, options ...RequestOptionFunc) (*ProjectCluster, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -205,7 +219,7 @@ func (s *ProjectClustersService) EditCluster(pid interface{}, cluster int, opt *
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ee/api/project_clusters.html#delete-project-cluster
|
// https://docs.gitlab.com/ee/api/project_clusters.html#delete-project-cluster
|
||||||
func (s *ProjectClustersService) DeleteCluster(pid interface{}, cluster int, options ...OptionFunc) (*Response, error) {
|
func (s *ProjectClustersService) DeleteCluster(pid interface{}, cluster int, options ...RequestOptionFunc) (*Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -74,7 +74,7 @@ type ScheduleExportOptions struct {
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/project_import_export.html#schedule-an-export
|
// https://docs.gitlab.com/ce/api/project_import_export.html#schedule-an-export
|
||||||
func (s *ProjectImportExportService) ScheduleExport(pid interface{}, opt *ScheduleExportOptions, options ...OptionFunc) (*Response, error) {
|
func (s *ProjectImportExportService) ScheduleExport(pid interface{}, opt *ScheduleExportOptions, options ...RequestOptionFunc) (*Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -93,7 +93,7 @@ func (s *ProjectImportExportService) ScheduleExport(pid interface{}, opt *Schedu
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/project_import_export.html#export-status
|
// https://docs.gitlab.com/ce/api/project_import_export.html#export-status
|
||||||
func (s *ProjectImportExportService) ExportStatus(pid interface{}, options ...OptionFunc) (*ExportStatus, *Response, error) {
|
func (s *ProjectImportExportService) ExportStatus(pid interface{}, options ...RequestOptionFunc) (*ExportStatus, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -118,7 +118,7 @@ func (s *ProjectImportExportService) ExportStatus(pid interface{}, options ...Op
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/project_import_export.html#export-download
|
// https://docs.gitlab.com/ce/api/project_import_export.html#export-download
|
||||||
func (s *ProjectImportExportService) ExportDownload(pid interface{}, options ...OptionFunc) ([]byte, *Response, error) {
|
func (s *ProjectImportExportService) ExportDownload(pid interface{}, options ...RequestOptionFunc) ([]byte, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -155,7 +155,7 @@ type ImportFileOptions struct {
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/project_import_export.html#import-a-file
|
// https://docs.gitlab.com/ce/api/project_import_export.html#import-a-file
|
||||||
func (s *ProjectImportExportService) ImportFile(opt *ImportFileOptions, options ...OptionFunc) (*ImportStatus, *Response, error) {
|
func (s *ProjectImportExportService) ImportFile(opt *ImportFileOptions, options ...RequestOptionFunc) (*ImportStatus, *Response, error) {
|
||||||
req, err := s.client.NewRequest("POST", "projects/import", opt, options)
|
req, err := s.client.NewRequest("POST", "projects/import", opt, options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -174,7 +174,7 @@ func (s *ProjectImportExportService) ImportFile(opt *ImportFileOptions, options
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/project_import_export.html#import-status
|
// https://docs.gitlab.com/ce/api/project_import_export.html#import-status
|
||||||
func (s *ProjectImportExportService) ImportStatus(pid interface{}, options ...OptionFunc) (*ImportStatus, *Response, error) {
|
func (s *ProjectImportExportService) ImportStatus(pid interface{}, options ...RequestOptionFunc) (*ImportStatus, *Response, error) {
|
||||||
project, err := parseID(pid)
|
project, err := parseID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue