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
tokarchuk/v1.17
6543 5 years ago committed by GitHub
parent 5c092eb0ef
commit 82dbb34c9c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 9
      go.mod
  2. 36
      go.sum
  3. 7
      modules/migrations/gitlab.go
  4. 324
      vendor/github.com/golang/protobuf/proto/buffer.go
  5. 253
      vendor/github.com/golang/protobuf/proto/clone.go
  6. 427
      vendor/github.com/golang/protobuf/proto/decode.go
  7. 63
      vendor/github.com/golang/protobuf/proto/defaults.go
  8. 105
      vendor/github.com/golang/protobuf/proto/deprecated.go
  9. 356
      vendor/github.com/golang/protobuf/proto/discard.go
  10. 203
      vendor/github.com/golang/protobuf/proto/encode.go
  11. 301
      vendor/github.com/golang/protobuf/proto/equal.go
  12. 769
      vendor/github.com/golang/protobuf/proto/extensions.go
  13. 965
      vendor/github.com/golang/protobuf/proto/lib.go
  14. 181
      vendor/github.com/golang/protobuf/proto/message_set.go
  15. 360
      vendor/github.com/golang/protobuf/proto/pointer_reflect.go
  16. 313
      vendor/github.com/golang/protobuf/proto/pointer_unsafe.go
  17. 648
      vendor/github.com/golang/protobuf/proto/properties.go
  18. 167
      vendor/github.com/golang/protobuf/proto/proto.go
  19. 323
      vendor/github.com/golang/protobuf/proto/registry.go
  20. 2776
      vendor/github.com/golang/protobuf/proto/table_marshal.go
  21. 654
      vendor/github.com/golang/protobuf/proto/table_merge.go
  22. 2053
      vendor/github.com/golang/protobuf/proto/table_unmarshal.go
  23. 845
      vendor/github.com/golang/protobuf/proto/text.go
  24. 801
      vendor/github.com/golang/protobuf/proto/text_decode.go
  25. 560
      vendor/github.com/golang/protobuf/proto/text_encode.go
  26. 880
      vendor/github.com/golang/protobuf/proto/text_parser.go
  27. 78
      vendor/github.com/golang/protobuf/proto/wire.go
  28. 34
      vendor/github.com/golang/protobuf/proto/wrappers.go
  29. 363
      vendor/github.com/hashicorp/go-cleanhttp/LICENSE
  30. 30
      vendor/github.com/hashicorp/go-cleanhttp/README.md
  31. 57
      vendor/github.com/hashicorp/go-cleanhttp/cleanhttp.go
  32. 20
      vendor/github.com/hashicorp/go-cleanhttp/doc.go
  33. 1
      vendor/github.com/hashicorp/go-cleanhttp/go.mod
  34. 48
      vendor/github.com/hashicorp/go-cleanhttp/handlers.go
  35. 4
      vendor/github.com/hashicorp/go-retryablehttp/.gitignore
  36. 12
      vendor/github.com/hashicorp/go-retryablehttp/.travis.yml
  37. 363
      vendor/github.com/hashicorp/go-retryablehttp/LICENSE
  38. 11
      vendor/github.com/hashicorp/go-retryablehttp/Makefile
  39. 61
      vendor/github.com/hashicorp/go-retryablehttp/README.md
  40. 705
      vendor/github.com/hashicorp/go-retryablehttp/client.go
  41. 8
      vendor/github.com/hashicorp/go-retryablehttp/go.mod
  42. 10
      vendor/github.com/hashicorp/go-retryablehttp/go.sum
  43. 43
      vendor/github.com/hashicorp/go-retryablehttp/roundtripper.go
  44. 4
      vendor/github.com/xanzy/go-gitlab/.travis.yml
  45. 7
      vendor/github.com/xanzy/go-gitlab/LICENSE
  46. 27
      vendor/github.com/xanzy/go-gitlab/README.md
  47. 16
      vendor/github.com/xanzy/go-gitlab/access_requests.go
  48. 100
      vendor/github.com/xanzy/go-gitlab/applications.go
  49. 64
      vendor/github.com/xanzy/go-gitlab/award_emojis.go
  50. 20
      vendor/github.com/xanzy/go-gitlab/boards.go
  51. 14
      vendor/github.com/xanzy/go-gitlab/branches.go
  52. 10
      vendor/github.com/xanzy/go-gitlab/broadcast_messages.go
  53. 4
      vendor/github.com/xanzy/go-gitlab/ci_yml_templates.go
  54. 49
      vendor/github.com/xanzy/go-gitlab/client_options.go
  55. 39
      vendor/github.com/xanzy/go-gitlab/commits.go
  56. 32
      vendor/github.com/xanzy/go-gitlab/custom_attributes.go
  57. 12
      vendor/github.com/xanzy/go-gitlab/deploy_keys.go
  58. 221
      vendor/github.com/xanzy/go-gitlab/deploy_tokens.go
  59. 80
      vendor/github.com/xanzy/go-gitlab/deployments.go
  60. 62
      vendor/github.com/xanzy/go-gitlab/discussions.go
  61. 12
      vendor/github.com/xanzy/go-gitlab/environments.go
  62. 10
      vendor/github.com/xanzy/go-gitlab/epics.go
  63. 140
      vendor/github.com/xanzy/go-gitlab/event_parsing.go
  64. 133
      vendor/github.com/xanzy/go-gitlab/event_systemhook_types.go
  65. 86
      vendor/github.com/xanzy/go-gitlab/event_webhook_types.go
  66. 6
      vendor/github.com/xanzy/go-gitlab/events.go
  67. 4
      vendor/github.com/xanzy/go-gitlab/feature_flags.go
  68. 4
      vendor/github.com/xanzy/go-gitlab/gitignore_templates.go
  69. 391
      vendor/github.com/xanzy/go-gitlab/gitlab.go
  70. 3
      vendor/github.com/xanzy/go-gitlab/go.mod
  71. 11
      vendor/github.com/xanzy/go-gitlab/go.sum
  72. 12
      vendor/github.com/xanzy/go-gitlab/group_badges.go
  73. 104
      vendor/github.com/xanzy/go-gitlab/group_boards.go
  74. 33
      vendor/github.com/xanzy/go-gitlab/group_clusters.go
  75. 199
      vendor/github.com/xanzy/go-gitlab/group_hooks.go
  76. 12
      vendor/github.com/xanzy/go-gitlab/group_labels.go
  77. 40
      vendor/github.com/xanzy/go-gitlab/group_members.go
  78. 12
      vendor/github.com/xanzy/go-gitlab/group_milestones.go
  79. 13
      vendor/github.com/xanzy/go-gitlab/group_variables.go
  80. 216
      vendor/github.com/xanzy/go-gitlab/groups.go
  81. 6
      vendor/github.com/xanzy/go-gitlab/issue_links.go
  82. 107
      vendor/github.com/xanzy/go-gitlab/issues.go
  83. 24
      vendor/github.com/xanzy/go-gitlab/jobs.go
  84. 2
      vendor/github.com/xanzy/go-gitlab/keys.go
  85. 12
      vendor/github.com/xanzy/go-gitlab/labels.go
  86. 2
      vendor/github.com/xanzy/go-gitlab/license.go
  87. 4
      vendor/github.com/xanzy/go-gitlab/license_templates.go
  88. 213
      vendor/github.com/xanzy/go-gitlab/merge_request_approvals.go
  89. 52
      vendor/github.com/xanzy/go-gitlab/merge_requests.go
  90. 14
      vendor/github.com/xanzy/go-gitlab/milestones.go
  91. 6
      vendor/github.com/xanzy/go-gitlab/namespaces.go
  92. 40
      vendor/github.com/xanzy/go-gitlab/notes.go
  93. 12
      vendor/github.com/xanzy/go-gitlab/notifications.go
  94. 27
      vendor/github.com/xanzy/go-gitlab/pages_domains.go
  95. 18
      vendor/github.com/xanzy/go-gitlab/pipeline_schedules.go
  96. 14
      vendor/github.com/xanzy/go-gitlab/pipeline_triggers.go
  97. 34
      vendor/github.com/xanzy/go-gitlab/pipelines.go
  98. 12
      vendor/github.com/xanzy/go-gitlab/project_badges.go
  99. 44
      vendor/github.com/xanzy/go-gitlab/project_clusters.go
  100. 10
      vendor/github.com/xanzy/go-gitlab/project_import_export.go
  101. Some files were not shown because too many files have changed in this diff Show More

@ -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

@ -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.
// // Use of this source code is governed by a BSD-style
// Copyright 2018 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 "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) { func discardUnknown(m protoreflect.Message) {
if src.isNil() { m.Range(func(fd protoreflect.FieldDescriptor, val protoreflect.Value) bool {
return // Nothing to do. switch {
} // Handle singular message.
case fd.Cardinality() != protoreflect.Repeated:
if atomic.LoadInt32(&di.initialized) == 0 { if fd.Message() != nil {
di.computeDiscardInfo() discardUnknown(m.Get(fd).Message())
}
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)
} }
} // Handle list of messages.
} case fd.IsList():
if fd.Message() != nil {
if di.unrecognized.IsValid() { ls := m.Get(fd).List()
*src.offset(di.unrecognized).toBytes() = nil for i := 0; i < ls.Len(); i++ {
} discardUnknown(ls.Get(i).Message())
}
func (di *discardInfo) computeDiscardInfo() {
di.lock.Lock()
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 {
case !isPointer:
panic(fmt.Sprintf("%v.%s cannot be a direct struct value", t, f.Name))
case isSlice: // E.g., []*pb.T
di := getDiscardInfo(tf)
dfi.discard = func(src pointer) {
sps := src.getPointerSlice()
for _, sp := range sps {
if !sp.isNil() {
di.discard(sp)
}
}
}
default: // E.g., *pb.T
di := getDiscardInfo(tf)
dfi.discard = func(src pointer) {
sp := src.getPointer()
if !sp.isNil() {
di.discard(sp)
}
} }
} }
case reflect.Map: // Handle map of messages.
switch { case fd.IsMap():
case isPointer || isSlice: if fd.MapValue().Message() != nil {
panic(fmt.Sprintf("%v.%s cannot be a pointer to a map or a slice of map values", t, f.Name)) ms := m.Get(fd).Map()
default: // E.g., map[K]V ms.Range(func(_ protoreflect.MapKey, v protoreflect.Value) bool {
if tf.Elem().Kind() == reflect.Ptr { // Proto struct (e.g., *T) discardUnknown(v.Message())
dfi.discard = func(src pointer) { return true
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
if f, ok := t.FieldByName("XXX_unrecognized"); ok {
if f.Type != reflect.TypeOf([]byte{}) {
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) return true
tf := f.Type })
// Unwrap tf to get its most basic type. // Discard unknown fields.
var isPointer, isSlice bool if len(m.GetUnknown()) > 0 {
if tf.Kind() == reflect.Slice && tf.Elem().Kind() != reflect.Uint8 { m.SetUnknown(nil)
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.
// // Use of this source code is governed by a BSD-style
// Copyright 2010 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
/*
* Types and routines for supporting protocol buffer extensions.
*/
import ( import (
"errors" "errors"
"fmt" "fmt"
"io"
"reflect" "reflect"
"strconv"
"sync"
)
// ErrMissingExtension is the error returned by GetExtension if the named extension is not in the message.
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. "google.golang.org/protobuf/encoding/protowire"
type extensionAdapter struct { "google.golang.org/protobuf/proto"
extendableProtoV1 "google.golang.org/protobuf/reflect/protoreflect"
} "google.golang.org/protobuf/reflect/protoregistry"
"google.golang.org/protobuf/runtime/protoiface"
"google.golang.org/protobuf/runtime/protoimpl"
)
func (e extensionAdapter) extensionsWrite() map[int32]Extension { type (
return e.ExtensionMap() // 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
func (e extensionAdapter) extensionsRead() (map[int32]Extension, sync.Locker) { // ExtensionRange represents a range of message extensions.
return e.ExtensionMap(), notLocker{} // Used in code generated by protoc-gen-go.
} ExtensionRange = protoiface.ExtensionRangeV1
// notLocker is a sync.Locker whose Lock and Unlock methods are nops. // Deprecated: Do not use; this is an internal type.
type notLocker struct{} Extension = protoimpl.ExtensionFieldV1
func (n notLocker) Lock() {} // Deprecated: Do not use; this is an internal type.
func (n notLocker) Unlock() {} XXX_InternalExtensions = protoimpl.ExtensionFields
)
// extendable returns the extendableProto interface for the given generated proto message. // ErrMissingExtension reports whether the extension was not present.
// If the proto message has the old extension format, it returns a wrapper that implements var ErrMissingExtension = errors.New("proto: missing extension")
// 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.
// Used in generated code from the protocol compiler.
type ExtensionDesc struct {
ExtendedType Message // nil pointer to the type that is being extended
ExtensionType interface{} // nil pointer to the extension type
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. // ClearExtension removes the the exntesion field from m
func SetRawExtension(base Message, id int32, b []byte) { // either as an explicitly populated field or as an unknown field.
epb, err := extendable(base) func ClearExtension(m Message, xt *ExtensionDesc) {
if err != nil { mr := MessageReflect(m)
if mr == nil || !mr.IsValid() {
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
} })
}
return false
}
// checkExtensionTypes checks that the given extension is valid for pb.
func checkExtensionTypes(pb extendableProto, extension *ExtensionDesc) error {
var pbi interface{} = pb
// Check the extended type.
if ea, ok := pbi.(extensionAdapter); ok {
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() clearUnknown(mr, fieldNum(xt.Field))
_, ok := extmap[extension.Field]
mu.Unlock()
return ok
} }
// ClearExtension removes the given extension from pb. // ClearAllExtensions clears all extensions from m.
func ClearExtension(pb Message, extension *ExtensionDesc) { // This includes populated fields and unknown fields in the extension range.
epb, err := extendable(pb) func ClearAllExtensions(m Message) {
if err != nil { mr := MessageReflect(m)
if mr == nil || !mr.IsValid() {
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, err return nil, errNotExtendable
} }
if extension.ExtendedType != nil { // Retrieve the unknown fields for this extension field.
// can only check type if this is a complete descriptor var bo protoreflect.RawFields
if err := checkExtensionTypes(epb, extension); err != nil { for bi := mr.GetUnknown(); len(bi) > 0; {
return nil, err num, _, n := protowire.ConsumeField(bi)
if int32(num) == xt.Field {
bo = append(bo, bi[:n]...)
} }
bi = bi[n:]
} }
emap, mu := epb.extensionsRead() // For type incomplete descriptors, only retrieve the unknown fields.
if emap == nil { if xt.ExtensionType == nil {
return defaultExtensionValue(extension) return []byte(bo), nil
}
mu.Lock()
defer mu.Unlock()
e, ok := emap[extension.Field]
if !ok {
// 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 { // If the extension field only exists as unknown fields, unmarshal it.
// incomplete descriptor // This is rarely done since proto.Unmarshal eagerly unmarshals extensions.
return e.enc, nil 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 {
v, err := decodeExtension(e.enc, extension) m2 := mr.New()
if err != nil { if err := (proto.UnmarshalOptions{
return nil, err Resolver: extensionResolver{xt},
}.Unmarshal(bo, m2.Interface())); err != nil {
return nil, err
}
if m2.Has(xtd) {
mr.Set(xtd, m2.Get(xtd))
clearUnknown(mr, fieldNum(xt.Field))
}
} }
// Remember the decoded version and drop the encoded version. // Check whether the message has the extension field set or a default.
// That way it is safe to mutate what we return. var pv protoreflect.Value
e.value = extensionAsStorageType(v) switch {
e.desc = extension case mr.Has(xtd):
e.enc = nil pv = mr.Get(xtd)
emap[extension.Field] = e case xtd.HasDefault():
return extensionAsLegacyType(e.value), nil pv = xtd.Default()
} default:
// 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()
}
if sf == nil || sf.value == nil {
// There is no default value.
return nil, ErrMissingExtension
} }
return v, nil
}
if t.Kind() != reflect.Ptr { // extensionResolver is a custom extension resolver that stores a single
// We do not need to return a Ptr, we can directly return sf.value. // extension type that takes precedence over the global registry.
return sf.value, nil type extensionResolver struct{ xt protoreflect.ExtensionType }
}
// We need to return an interface{} that is a pointer to sf.value. func (r extensionResolver) FindExtensionByName(field protoreflect.FullName) (protoreflect.ExtensionType, error) {
value := reflect.New(t).Elem() if xtd := r.xt.TypeDescriptor(); xtd.FullName() == field {
value.Set(reflect.New(value.Type().Elem())) return r.xt, nil
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 return protoregistry.GlobalTypes.FindExtensionByName(field)
} }
// decodeExtension decodes an extension encoded in b. func (r extensionResolver) FindExtensionByNumber(message protoreflect.FullName, field protoreflect.FieldNumber) (protoreflect.ExtensionType, error) {
func decodeExtension(b []byte, extension *ExtensionDesc) (interface{}, error) { if xtd := r.xt.TypeDescriptor(); xtd.ContainingMessage().FullName() == message && xtd.Number() == field {
t := reflect.TypeOf(extension.ExtensionType) return r.xt, nil
unmarshal := typeUnmarshaler(t, extension.Tag)
// t is a pointer to a struct, pointer to basic type or a slice.
// Allocate space to store the pointer/slice.
value := reflect.New(t).Elem()
var err error
for {
x, n := decodeVarint(b)
if n == 0 {
return nil, io.ErrUnexpectedEOF
}
b = b[n:]
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 return protoregistry.GlobalTypes.FindExtensionByNumber(message, field)
} }
// GetExtensions returns a slice of the extensions present in pb that are also listed in es. // GetExtensions returns a list of the extensions values present in m,
// The returned slice has the same length as es; missing extensions will appear as nil elements. // corresponding with the provided list of extension descriptors, xts.
func GetExtensions(pb Message, es []*ExtensionDesc) (extensions []interface{}, err error) { // If an extension is missing in m, the corresponding value is nil.
epb, err := extendable(pb) func GetExtensions(m Message, xts []*ExtensionDesc) ([]interface{}, error) {
if err != nil { mr := MessageReflect(m)
return nil, err if mr == nil || !mr.IsValid() {
return nil, errNotExtendable
} }
extensions = make([]interface{}, len(es))
for i, e := range es { vs := make([]interface{}, len(xts))
extensions[i], err = GetExtension(epb, e) for i, xt := range xts {
if err == ErrMissingExtension { v, err := GetExtension(m, xt)
err = nil
}
if err != nil { if err != nil {
return if err == ErrMissingExtension {
continue
}
return vs, err
} }
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)
} }
return extensions, nil
}
// SetExtension sets the specified extension of pb to the specified value. xtd := xt.TypeDescriptor()
func SetExtension(pb Message, extension *ExtensionDesc, value interface{}) error { if !isValidExtension(mr.Descriptor(), xtd) {
epb, err := extendable(pb) return fmt.Errorf("proto: bad extended type; %T does not extend %T", xt.ExtendedType, m)
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 mr.Set(xtd, xt.ValueOf(v))
// encoder can't distinguish an ErrNil due to a nil extension clearUnknown(mr, fieldNum(xt.Field))
// 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:]
} }
}
// A global registry of extensions. ClearExtension(m, &ExtensionDesc{Field: fnum})
// The generated code will register the generated descriptors by calling RegisterExtension. mr.SetUnknown(append(mr.GetUnknown(), b...))
}
var extensionMaps = make(map[reflect.Type]map[int32]*ExtensionDesc) // ExtensionDescs returns a list of extension descriptors found in m,
// containing descriptors for both populated extension fields in m and
// also unknown fields of m that are in the extension range.
// For the later case, an type incomplete descriptor is provided where only
// the ExtensionDesc.Field field is populated.
// The order of the extension descriptors is undefined.
func ExtensionDescs(m Message) ([]*ExtensionDesc, error) {
mr := MessageReflect(m)
if mr == nil || !mr.IsValid() || mr.Descriptor().ExtensionRanges().Len() == 0 {
return nil, errNotExtendable
}
// RegisterExtension is called from the generated code. // Collect a set of known extension descriptors.
func RegisterExtension(desc *ExtensionDesc) { extDescs := make(map[protoreflect.FieldNumber]*ExtensionDesc)
st := reflect.TypeOf(desc.ExtendedType).Elem() mr.Range(func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool {
m := extensionMaps[st] if fd.IsExtension() {
if m == nil { xt := fd.(protoreflect.ExtensionTypeDescriptor)
m = make(map[int32]*ExtensionDesc) if xd, ok := xt.Type().(*ExtensionDesc); ok {
extensionMaps[st] = m extDescs[fd.Number()] = xd
}
}
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:]
} }
if _, ok := m[desc.Field]; ok {
panic("proto: duplicate extension registered: " + st.String() + " " + strconv.Itoa(int(desc.Field))) // 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)
} }
m[desc.Field] = desc 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()
}
}
case reflect.Slice:
// Represent slice types as a pointer to the value.
if rv.Type().Elem().Kind() != reflect.Uint8 {
rv2 := reflect.New(rv.Type())
rv2.Elem().Set(rv)
v = rv2.Interface()
} }
bi = bi[n:]
} }
return v if bi := m.GetUnknown(); len(bi) != len(bo) {
m.SetUnknown(bo)
}
}
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.
// // Use of this source code is governed by a BSD-style
// Copyright 2010 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
/*
* 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"
)
const debug bool = false
// Constants that identify the encoding of a value on the wire. "google.golang.org/protobuf/reflect/protoreflect"
const ( "google.golang.org/protobuf/runtime/protoimpl"
WireVarint = 0
WireFixed64 = 1
WireBytes = 2
WireStartGroup = 3
WireEndGroup = 4
WireFixed32 = 5
) )
// tagMap is an optimization over map[int]int for typical protocol buffer // StructProperties represents protocol buffer type information for a
// use-cases. Encoded protocol buffers are often in tag order with small tag // generated protobuf message in the open-struct API.
// numbers. //
type tagMap struct { // Deprecated: Do not use.
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
Wire 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
// WireType is the protobuf wire type for the field.
WireType int WireType int
Tag int // Tag is the protobuf field number.
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.
Default string // default value Oneof bool
HasDefault bool // whether an explicit default was provided
// Default is the default value in string form.
stype reflect.Type // set for struct types only Default string
sprop *StructProperties // set for struct types only // HasDefault reports whether the field has a default value.
HasDefault bool
// MapKeyProp is the properties for the key field for a map field.
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 { if len(p.Enum) > 0 {
s += ",enum=" + p.Enum
}
if len(p.Weak) > 0 {
s += ",weak=" + p.Weak
}
if p.Proto3 {
s += ",proto3" s += ",proto3"
} }
if p.oneof { if p.Oneof {
s += ",oneof" s += ",oneof"
} }
if len(p.Enum) > 0 {
s += ",enum=" + p.Enum
}
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
}
}
}
}
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)
} }
tag = strings.TrimPrefix(tag[i:], ",")
} }
} }
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)
}
// Most calls to GetProperties in a long-running program will be
// retrieving details for types we have seen before.
propertiesMu.RLock()
sprop, ok := propertiesMap[t]
propertiesMu.RUnlock()
if ok {
return sprop
} }
p, _ := propertiesCache.LoadOrStore(t, newProperties(t))
propertiesMu.Lock() return p.(*StructProperties)
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 != "" {
print("\n") p.Name = p.OrigName // avoid possible "XXX_" prefix on weak field
} }
prop.Prop = append(prop.Prop, p)
} }
// Re-order prop.order. // Construct a mapping of oneof field names to properties.
sort.Sort(prop) 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
}
}
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. // Determine the struct field that contains this oneof.
for i := 0; i < t.NumField(); i++ { // Each wrapper is assignable to exactly one parent field.
f := t.Field(i) var foundOneof bool
if f.Type.Kind() != reflect.Interface { for i := 0; i < t.NumField() && !foundOneof; i++ {
continue if p.Type.AssignableTo(t.Field(i).Type) {
p.Field = i
foundOneof = true
} }
if !oop.Type.AssignableTo(f.Type) {
continue
}
oop.Field = i
break
} }
prop.OneofTypes[oop.Prop.OrigName] = oop if !foundOneof {
} panic(fmt.Sprintf("%v is not a generated message in the open-struct API", t))
} }
prop.OneofTypes[p.Prop.OrigName] = p
// build required counts
// build tags
reqCount := 0
prop.decoderOrigNames = make(map[string]int)
for i, p := range prop.Prop {
if strings.HasPrefix(p.Name, "XXX_") {
// Internal fields should not appear in tags/origNames maps.
// They are handled specially when encoding and decoding.
continue
}
if p.Required {
reqCount++
} }
prop.decoderTags.put(p.Tag, i)
prop.decoderOrigNames[p.OrigName] = i
} }
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
}
}

@ -70,18 +70,19 @@ func (c Commit) String() string {
// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html#list-repository-commits // GitLab API docs: https://docs.gitlab.com/ce/api/commits.html#list-repository-commits
type ListCommitsOptions struct { type ListCommitsOptions struct {
ListOptions ListOptions
RefName *string `url:"ref_name,omitempty" json:"ref_name,omitempty"` RefName *string `url:"ref_name,omitempty" json:"ref_name,omitempty"`
Since *time.Time `url:"since,omitempty" json:"since,omitempty"` Since *time.Time `url:"since,omitempty" json:"since,omitempty"`
Until *time.Time `url:"until,omitempty" json:"until,omitempty"` Until *time.Time `url:"until,omitempty" json:"until,omitempty"`
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)
}

@ -67,14 +67,18 @@ type Deployment struct {
// https://docs.gitlab.com/ce/api/deployments.html#list-project-deployments // https://docs.gitlab.com/ce/api/deployments.html#list-project-deployments
type ListProjectDeploymentsOptions struct { 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

@ -11,15 +11,18 @@ type EventType string
// List of available event types. // List of available event types.
const ( const (
EventTypeBuild EventType = "Build Hook" EventTypeBuild EventType = "Build Hook"
EventTypeIssue EventType = "Issue Hook" EventTypeIssue EventType = "Issue Hook"
EventTypeJob EventType = "Job Hook" EventConfidentialIssue EventType = "Confidential Issue Hook"
EventTypeMergeRequest EventType = "Merge Request Hook" EventTypeJob EventType = "Job Hook"
EventTypeNote EventType = "Note Hook" EventTypeMergeRequest EventType = "Merge Request Hook"
EventTypePipeline EventType = "Pipeline Hook" EventTypeNote EventType = "Note Hook"
EventTypePush EventType = "Push Hook" EventConfidentialNote EventType = "Confidential Note Hook"
EventTypeTagPush EventType = "Tag Push Hook" EventTypePipeline EventType = "Pipeline Hook"
EventTypeWikiPage EventType = "Wiki Page Hook" EventTypePush EventType = "Push Hook"
EventTypeSystemHook EventType = "System Hook"
EventTypeTagPush EventType = "Tag Push Hook"
EventTypeWikiPage EventType = "Wiki Page Hook"
) )
const ( const (
@ -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 := &noteEvent{} note := &noteEvent{}
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 {
@ -329,42 +331,41 @@ type MergeCommentEvent struct {
} `json:"object_attributes"` } `json:"object_attributes"`
Repository *Repository `json:"repository"` Repository *Repository `json:"repository"`
MergeRequest struct { MergeRequest struct {
ID int `json:"id"` ID int `json:"id"`
TargetBranch string `json:"target_branch"` TargetBranch string `json:"target_branch"`
SourceBranch string `json:"source_branch"` SourceBranch string `json:"source_branch"`
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"`
Title string `json:"title"` AssigneeIDs []int `json:"assignee_ids"`
CreatedAt string `json:"created_at"` Title string `json:"title"`
UpdatedAt string `json:"updated_at"` CreatedAt string `json:"created_at"`
MilestoneID int `json:"milestone_id"` UpdatedAt string `json:"updated_at"`
State string `json:"state"` MilestoneID int `json:"milestone_id"`
MergeStatus string `json:"merge_status"` State string `json:"state"`
TargetProjectID int `json:"target_project_id"` MergeStatus string `json:"merge_status"`
IID int `json:"iid"` TargetProjectID int `json:"target_project_id"`
Description string `json:"description"` IID int `json:"iid"`
Position int `json:"position"` Description string `json:"description"`
LockedAt string `json:"locked_at"` Position int `json:"position"`
UpdatedByID int `json:"updated_by_id"` LockedAt string `json:"locked_at"`
MergeError string `json:"merge_error"` UpdatedByID int `json:"updated_by_id"`
MergeParams struct { MergeError string `json:"merge_error"`
ForceRemoveSourceBranch string `json:"force_remove_source_branch"` MergeParams *MergeParams `json:"merge_params"`
} `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"` DeletedAt string `json:"deleted_at"`
DeletedAt string `json:"deleted_at"` InProgressMergeCommitSHA string `json:"in_progress_merge_commit_sha"`
InProgressMergeCommitSHA string `json:"in_progress_merge_commit_sha"` LockVersion int `json:"lock_version"`
LockVersion int `json:"lock_version"` ApprovalsBeforeMerge string `json:"approvals_before_merge"`
ApprovalsBeforeMerge string `json:"approvals_before_merge"` RebaseCommitSHA string `json:"rebase_commit_sha"`
RebaseCommitSHA string `json:"rebase_commit_sha"` TimeEstimate int `json:"time_estimate"`
TimeEstimate int `json:"time_estimate"` Squash bool `json:"squash"`
Squash bool `json:"squash"` LastEditedAt string `json:"last_edited_at"`
LastEditedAt string `json:"last_edited_at"` LastEditedByID int `json:"last_edited_by_id"`
LastEditedByID int `json:"last_edited_by_id"` Source *Repository `json:"source"`
Source *Repository `json:"source"` Target *Repository `json:"target"`
Target *Repository `json:"target"`
LastCommit struct { LastCommit struct {
ID string `json:"id"` ID string `json:"id"`
Message string `json:"message"` Message string `json:"message"`
@ -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,27 +441,31 @@ 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) if err != nil {
return nil, err
}
client.authType = basicAuth client.authType = basicAuth
client.username = username client.username = username
client.password = password client.password = password
client.SetBaseURL(endpoint)
err := client.requestOAuthToken(context.TODO()) err = client.requestOAuthToken(context.Background())
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -410,6 +473,18 @@ func NewBasicAuthClient(httpClient *http.Client, endpoint, username, password st
return client, nil 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
}
func (c *Client) requestOAuthToken(ctx context.Context) error { func (c *Client) requestOAuthToken(ctx context.Context) error {
config := &oauth2.Config{ config := &oauth2.Config{
Endpoint: oauth2.Endpoint{ Endpoint: oauth2.Endpoint{
@ -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 { // Configure the HTTP client.
client := newClient(httpClient) c.client = &retryablehttp.Client{
client.authType = oAuthToken Backoff: c.retryHTTPBackoff,
client.token = token CheckRetry: c.retryHTTPCheck,
return client ErrorHandler: retryablehttp.PassthroughErrorHandler,
} HTTPClient: cleanhttp.DefaultPooledClient(),
RetryWaitMin: 100 * time.Millisecond,
func newClient(httpClient *http.Client) *Client { RetryWaitMax: 400 * time.Millisecond,
if httpClient == nil { RetryMax: 5,
httpClient = http.DefaultClient
} }
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
if opt != nil { // 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 {
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
@ -106,12 +107,13 @@ func (s *GroupClustersService) GetCluster(pid interface{}, cluster int, options
// 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
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"`
Enabled *bool `url:"enabled,omitempty" json:"enabled,omitempty"` ManagementProjectID *string `url:"management_project_id,omitempty" json:"management_project_id,omitempty"`
Managed *bool `url:"managed,omitempty" json:"managed,omitempty"` Enabled *bool `url:"enabled,omitempty" json:"enabled,omitempty"`
EnvironmentScope *string `url:"environment_scope,omitempty" json:"environment_scope,omitempty"` Managed *bool `url:"managed,omitempty" json:"managed,omitempty"`
PlatformKubernetes *AddGroupPlatformKubernetesOptions `url:"platform_kubernetes_attributes,omitempty" json:"platform_kubernetes_attributes,omitempty"` EnvironmentScope *string `url:"environment_scope,omitempty" json:"environment_scope,omitempty"`
PlatformKubernetes *AddGroupPlatformKubernetesOptions `url:"platform_kubernetes_attributes,omitempty" json:"platform_kubernetes_attributes,omitempty"`
} }
// AddGroupPlatformKubernetesOptions represents the available PlatformKubernetes options for adding. // AddGroupPlatformKubernetesOptions represents the available PlatformKubernetes options for adding.
@ -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
@ -153,10 +155,11 @@ func (s *GroupClustersService) AddCluster(pid interface{}, opt *AddGroupClusterO
// 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
type EditGroupClusterOptions struct { type EditGroupClusterOptions 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"`
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,18 +28,30 @@ 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
type GroupMember struct { type GroupMember struct {
ID int `json:"id"` ID int `json:"id"`
Username string `json:"username"` Username string `json:"username"`
Name string `json:"name"` Name string `json:"name"`
State string `json:"state"` State string `json:"state"`
AvatarURL string `json:"avatar_url"` AvatarURL string `json:"avatar_url"`
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

@ -32,34 +32,44 @@ type GroupsService struct {
// //
// GitLab API docs: https://docs.gitlab.com/ce/api/groups.html // GitLab API docs: https://docs.gitlab.com/ce/api/groups.html
type Group struct { type Group struct {
ID int `json:"id"` ID int `json:"id"`
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"`
LFSEnabled bool `json:"lfs_enabled"` Visibility VisibilityValue `json:"visibility"`
AvatarURL string `json:"avatar_url"` LFSEnabled bool `json:"lfs_enabled"`
WebURL string `json:"web_url"` AvatarURL string `json:"avatar_url"`
RequestAccessEnabled bool `json:"request_access_enabled"` WebURL string `json:"web_url"`
FullName string `json:"full_name"` RequestAccessEnabled bool `json:"request_access_enabled"`
FullPath string `json:"full_path"` FullName string `json:"full_name"`
ParentID int `json:"parent_id"` FullPath string `json:"full_path"`
Projects []*Project `json:"projects"` ParentID int `json:"parent_id"`
Statistics *StorageStatistics `json:"statistics"` Projects []*Project `json:"projects"`
CustomAttributes []*CustomAttribute `json:"custom_attributes"` Statistics *StorageStatistics `json:"statistics"`
ShareWithGroupLock bool `json:"share_with_group_lock"` CustomAttributes []*CustomAttribute `json:"custom_attributes"`
RequireTwoFactorAuth bool `json:"require_two_factor_authentication"` ShareWithGroupLock bool `json:"share_with_group_lock"`
TwoFactorGracePeriod int `json:"two_factor_grace_period"` RequireTwoFactorAuth bool `json:"require_two_factor_authentication"`
ProjectCreationLevel string `json:"project_creation_level"` TwoFactorGracePeriod int `json:"two_factor_grace_period"`
AutoDevopsEnabled bool `json:"auto_devops_enabled"` ProjectCreationLevel ProjectCreationLevelValue `json:"project_creation_level"`
SubGroupCreationLevel string `json:"subgroup_creation_level"` AutoDevopsEnabled bool `json:"auto_devops_enabled"`
EmailsDisabled bool `json:"emails_disabled"` SubGroupCreationLevel SubGroupCreationLevelValue `json:"subgroup_creation_level"`
RunnersToken string `json:"runners_token"` EmailsDisabled bool `json:"emails_disabled"`
SharedProjects []*Project `json:"shared_projects"` MentionsDisabled bool `json:"mentions_disabled"`
LDAPCN string `json:"ldap_cn"` RunnersToken string `json:"runners_token"`
LDAPAccess bool `json:"ldap_access"` SharedProjects []*Project `json:"shared_projects"`
SharedRunnersMinutesLimit int `json:"shared_runners_minutes_limit"` LDAPCN string `json:"ldap_cn"`
ExtraSharedRunnersMinutesLimit int `json:"extra_shared_runners_minutes_limit"` LDAPAccess AccessLevelValue `json:"ldap_access"`
LDAPGroupLinks []*LDAPGroupLink `json:"ldap_group_links"`
SharedRunnersMinutesLimit int `json:"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
@ -125,20 +135,31 @@ func (s *GroupsService) GetGroup(gid interface{}, options ...OptionFunc) (*Group
// //
// 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
type CreateGroupOptions struct { 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"`
Visibility *VisibilityValue `url:"visibility,omitempty" json:"visibility,omitempty"` MembershipLock *bool `url:"membership_lock,omitempty" json:"membership_lock,omitempty"`
LFSEnabled *bool `url:"lfs_enabled,omitempty" json:"lfs_enabled,omitempty"` Visibility *VisibilityValue `url:"visibility,omitempty" json:"visibility,omitempty"`
RequestAccessEnabled *bool `url:"request_access_enabled,omitempty" json:"request_access_enabled,omitempty"` ShareWithGroupLock *bool `url:"share_with_group_lock,omitempty" json:"share_with_group_lock,omitempty"`
ParentID *int `url:"parent_id,omitempty" json:"parent_id,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"`
RequestAccessEnabled *bool `url:"request_access_enabled,omitempty" json:"request_access_enabled,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"`
@ -64,33 +72,38 @@ type IssueLinks struct {
// //
// GitLab API docs: https://docs.gitlab.com/ce/api/issues.html // GitLab API docs: https://docs.gitlab.com/ce/api/issues.html
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"` State string `json:"state"`
Milestone *Milestone `json:"milestone"` Description string `json:"description"`
Author *IssueAuthor `json:"author"` Author *IssueAuthor `json:"author"`
Description string `json:"description"` Milestone *Milestone `json:"milestone"`
State string `json:"state"` ProjectID int `json:"project_id"`
Assignees []*IssueAssignee `json:"assignees"` Assignees []*IssueAssignee `json:"assignees"`
Assignee *IssueAssignee `json:"assignee"` Assignee *IssueAssignee `json:"assignee"`
Upvotes int `json:"upvotes"` UpdatedAt *time.Time `json:"updated_at"`
Downvotes int `json:"downvotes"` ClosedAt *time.Time `json:"closed_at"`
Labels Labels `json:"labels"` Title string `json:"title"`
Title string `json:"title"` CreatedAt *time.Time `json:"created_at"`
UpdatedAt *time.Time `json:"updated_at"` Labels Labels `json:"labels"`
CreatedAt *time.Time `json:"created_at"` Upvotes int `json:"upvotes"`
ClosedAt *time.Time `json:"closed_at"` Downvotes int `json:"downvotes"`
Subscribed bool `json:"subscribed"` DueDate *ISOTime `json:"due_date"`
UserNotesCount int `json:"user_notes_count"` WebURL string `json:"web_url"`
DueDate *ISOTime `json:"due_date"` References *IssueReferences `json:"references"`
WebURL string `json:"web_url"` 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"`
Links *IssueLinks `json:"_links"` UserNotesCount int `json:"user_notes_count"`
IssueLinkID int `json:"issue_link_id"` Links *IssueLinks `json:"_links"`
MergeRequestCount int `json:"merge_requests_count"` IssueLinkID int `json:"issue_link_id"`
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"`
} }
// GetConfiguration shows information about single merge request approvals
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/merge_request_approvals.html#get-configuration-1
func (s *MergeRequestApprovalsService) GetConfiguration(pid interface{}, mr int, options ...RequestOptionFunc) (*MergeRequestApprovals, *Response, error) {
project, err := parseID(pid)
if err != nil {
return nil, nil, err
}
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. // ChangeApprovalConfiguration updates the approval configuration of a merge request.
// //
// 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#change-approval-configuration
func (s *MergeRequestApprovalsService) ChangeApprovalConfiguration(pid interface{}, mergeRequestIID int, opt *ChangeMergeRequestApprovalConfigurationOptions, options ...OptionFunc) (*MergeRequest, *Response, error) { func (s *MergeRequestApprovalsService) ChangeApprovalConfiguration(pid interface{}, mergeRequest int, opt *ChangeMergeRequestApprovalConfigurationOptions, 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/approvals", pathEscape(project), mergeRequestIID) 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,18 +108,19 @@ 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"`
Certificate *string `url:"certifiate,omitempty" json:"certifiate,omitempty"` AutoSslEnabled *bool `url:"auto_ssl_enabled,omitempty" json:"auto_ssl_enabled,omitempty"`
Key *string `url:"key,omitempty" json:"key,omitempty"` Certificate *string `url:"certifiate,omitempty" json:"certifiate,omitempty"`
Key *string `url:"key,omitempty" json:"key,omitempty"`
} }
// CreatePagesDomain creates a new project pages domain. // CreatePagesDomain creates a new project pages domain.
// //
// 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

@ -101,21 +101,23 @@ func (p PipelineInfo) String() string {
// 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
type ListProjectPipelinesOptions struct { type ListProjectPipelinesOptions struct {
ListOptions ListOptions
Scope *string `url:"scope,omitempty" json:"scope,omitempty"` Scope *string `url:"scope,omitempty" json:"scope,omitempty"`
Status *BuildStateValue `url:"status,omitempty" json:"status,omitempty"` Status *BuildStateValue `url:"status,omitempty" json:"status,omitempty"`
Ref *string `url:"ref,omitempty" json:"ref,omitempty"` Ref *string `url:"ref,omitempty" json:"ref,omitempty"`
SHA *string `url:"sha,omitempty" json:"sha,omitempty"` SHA *string `url:"sha,omitempty" json:"sha,omitempty"`
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"`
OrderBy *string `url:"order_by,omitempty" json:"order_by,omitempty"` UpdatedAfter *time.Time `url:"updated_after,omitempty" json:"updated_after,omitempty"`
Sort *string `url:"sort,omitempty" json:"sort,omitempty"` UpdatedBefore *time.Time `url:"update_before,omitempty" json:"updated_before,omitempty"`
OrderBy *string `url:"order_by,omitempty" json:"order_by,omitempty"`
Sort *string `url:"sort,omitempty" json:"sort,omitempty"`
} }
// 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
@ -115,12 +127,13 @@ func (s *ProjectClustersService) GetCluster(pid interface{}, cluster int, option
// 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
type AddClusterOptions struct { type AddClusterOptions 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"`
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"`
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
@ -162,10 +175,11 @@ func (s *ProjectClustersService) AddCluster(pid interface{}, opt *AddClusterOpti
// 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
type EditClusterOptions struct { 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"`
PlatformKubernetes *EditPlatformKubernetesOptions `url:"platform_kubernetes_attributes,omitempty" json:"platform_kubernetes_attributes,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"`
} }
// EditPlatformKubernetesOptions represents the available PlatformKubernetes options for editing. // EditPlatformKubernetesOptions represents the available PlatformKubernetes options for editing.
@ -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…
Cancel
Save