vsts.py 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835
  1. from urllib.parse import parse_qs, urlencode, urlparse
  2. import responses
  3. from sentry.integrations.vsts import VstsIntegrationProvider
  4. from sentry.testutils import IntegrationTestCase
  5. class VstsIntegrationTestCase(IntegrationTestCase):
  6. provider = VstsIntegrationProvider
  7. def setUp(self):
  8. super().setUp()
  9. self.access_token = "9d646e20-7a62-4bcc-abc0-cb2d4d075e36"
  10. self.refresh_token = "32004633-a3c0-4616-9aa0-a40632adac77"
  11. self.vsts_account_id = "c8a585ae-b61f-4ba6-833c-9e8d5d1674d8"
  12. self.vsts_account_name = "MyVSTSAccount"
  13. self.vsts_account_uri = "https://MyVSTSAccount.vssps.visualstudio.com:443/"
  14. self.vsts_base_url = "https://MyVSTSAccount.visualstudio.com/"
  15. self.vsts_user_id = "d6245f20-2af8-44f4-9451-8107cb2767db"
  16. self.vsts_user_name = "Foo Bar"
  17. self.vsts_user_email = "foobar@example.com"
  18. self.repo_id = "47166099-3e16-4868-9137-22ac6b05b06e"
  19. self.repo_name = "cool-service"
  20. self.project_a = {"id": "eb6e4656-77fc-42a1-9181-4c6d8e9da5d1", "name": "ProjectA"}
  21. self.project_b = {"id": "6ce954b1-ce1f-45d1-b94d-e6bf2464ba2c", "name": "ProjectB"}
  22. responses.start()
  23. self._stub_vsts()
  24. def tearDown(self):
  25. responses.stop()
  26. def _stub_vsts(self):
  27. responses.reset()
  28. responses.add(
  29. responses.POST,
  30. "https://app.vssps.visualstudio.com/oauth2/token",
  31. json={
  32. "access_token": self.access_token,
  33. "token_type": "grant",
  34. "expires_in": 300, # seconds (5 min)
  35. "refresh_token": self.refresh_token,
  36. },
  37. )
  38. responses.add(
  39. responses.GET,
  40. "https://app.vssps.visualstudio.com/_apis/accounts?memberId=%s&api-version=4.1"
  41. % self.vsts_user_id,
  42. json={
  43. "count": 1,
  44. "value": [
  45. {
  46. "accountId": self.vsts_account_id,
  47. "accountUri": self.vsts_account_uri,
  48. "accountName": self.vsts_account_name,
  49. "properties": {},
  50. }
  51. ],
  52. },
  53. )
  54. responses.add(
  55. responses.GET,
  56. "https://app.vssps.visualstudio.com/_apis/resourceareas/79134C72-4A58-4B42-976C-04E7115F32BF?hostId=%s&api-preview=5.0-preview.1"
  57. % self.vsts_account_id,
  58. json={"locationUrl": self.vsts_base_url},
  59. )
  60. responses.add(
  61. responses.GET,
  62. "https://app.vssps.visualstudio.com/_apis/profile/profiles/me?api-version=1.0",
  63. json={
  64. "id": self.vsts_user_id,
  65. "displayName": self.vsts_user_name,
  66. "emailAddress": self.vsts_user_email,
  67. },
  68. )
  69. responses.add(
  70. responses.GET,
  71. "https://app.vssps.visualstudio.com/_apis/connectionData/",
  72. json={"authenticatedUser": {"subjectDescriptor": self.vsts_account_id}},
  73. )
  74. responses.add(
  75. responses.GET,
  76. f"https://{self.vsts_account_name.lower()}.visualstudio.com/_apis/projects",
  77. json={"value": [self.project_a, self.project_b], "count": 2},
  78. )
  79. responses.add(
  80. responses.POST,
  81. f"https://{self.vsts_account_name.lower()}.visualstudio.com/_apis/hooks/subscriptions",
  82. json=CREATE_SUBSCRIPTION,
  83. )
  84. responses.add(
  85. responses.GET,
  86. f"https://{self.vsts_account_name.lower()}.visualstudio.com/_apis/git/repositories",
  87. json={
  88. "value": [
  89. {
  90. "id": self.repo_id,
  91. "name": self.repo_name,
  92. "project": {"name": self.project_a["name"]},
  93. }
  94. ]
  95. },
  96. )
  97. responses.add(
  98. responses.GET,
  99. f"https://{self.vsts_account_name.lower()}.visualstudio.com/ProjectA/_apis/git/repositories/ProjectA",
  100. json={
  101. "repository": {
  102. "id": self.repo_id,
  103. "name": self.repo_name,
  104. "project": {"name": self.project_a["name"]},
  105. }
  106. },
  107. )
  108. for project in [self.project_a, self.project_b]:
  109. responses.add(
  110. responses.GET,
  111. "https://{}.visualstudio.com/{}/_apis/wit/workitemtypes/{}/states".format(
  112. self.vsts_account_name.lower(), project["id"], "Bug"
  113. ),
  114. json={
  115. "count": 6,
  116. "value": [
  117. {"name": "resolve_status"},
  118. {"name": "resolve_when"},
  119. {"name": "regression_status"},
  120. {"name": "sync_comments"},
  121. {"name": "sync_forward_assignment"},
  122. {"name": "sync_reverse_assignment"},
  123. ],
  124. },
  125. )
  126. responses.add(
  127. responses.GET,
  128. "https://{}.visualstudio.com/{}/_apis/wit/workitemtypes/{}/states".format(
  129. self.vsts_account_name.lower(), project["id"], "Issue"
  130. ),
  131. json={
  132. "count": 0,
  133. "value": [],
  134. },
  135. )
  136. responses.add(
  137. responses.GET,
  138. "https://{}.visualstudio.com/{}/_apis/wit/workitemtypes/{}/states".format(
  139. self.vsts_account_name.lower(), project["id"], "Task"
  140. ),
  141. json={
  142. "count": 0,
  143. "value": [],
  144. },
  145. )
  146. def make_init_request(self, path=None, body=None):
  147. return self.client.get(path or self.init_path, body or {})
  148. def make_oauth_redirect_request(self, state):
  149. return self.client.get(
  150. "{}?{}".format(self.setup_path, urlencode({"code": "oauth-code", "state": state}))
  151. )
  152. def assert_vsts_oauth_redirect(self, redirect):
  153. assert redirect.scheme == "https"
  154. assert redirect.netloc == "app.vssps.visualstudio.com"
  155. assert redirect.path == "/oauth2/authorize"
  156. def assert_account_selection(self, response, account_id=None):
  157. account_id = account_id or self.vsts_account_id
  158. assert response.status_code == 200
  159. assert f'<option value="{account_id}"'.encode() in response.content
  160. def assert_installation(self):
  161. # Initial request to the installation URL for VSTS
  162. resp = self.make_init_request()
  163. redirect = urlparse(resp["Location"])
  164. assert resp.status_code == 302
  165. self.assert_vsts_oauth_redirect(redirect)
  166. query = parse_qs(redirect.query)
  167. # OAuth redirect back to Sentry (identity_pipeline_view)
  168. resp = self.make_oauth_redirect_request(query["state"][0])
  169. self.assert_account_selection(resp)
  170. # User choosing which VSTS Account to use (AccountConfigView)
  171. # Final step.
  172. resp = self.client.post(
  173. self.setup_path, {"account": self.vsts_account_id, "provider": "vsts"}
  174. )
  175. return resp
  176. COMPARE_COMMITS_EXAMPLE = b"""
  177. {
  178. "count": 1,
  179. "value": [
  180. {
  181. "commitId": "6c36052c58bde5e57040ebe6bdb9f6a52c906fff",
  182. "author": {
  183. "name": "max bittker",
  184. "email": "max@sentry.io",
  185. "date": "2018-04-24T00:03:18Z"
  186. },
  187. "committer": {
  188. "name": "max bittker",
  189. "email": "max@sentry.io",
  190. "date": "2018-04-24T00:03:18Z"
  191. },
  192. "comment": "Updated README.md",
  193. "commentTruncated": true,
  194. "changeCounts": {"Add": 0, "Edit": 1, "Delete": 0},
  195. "url":
  196. "https://mbittker.visualstudio.com/_apis/git/repositories/b1e25999-c080-4ea1-8c61-597c4ec41f06/commits/6c36052c58bde5e57040ebe6bdb9f6a52c906fff",
  197. "remoteUrl":
  198. "https://mbittker.visualstudio.com/_git/MyFirstProject/commit/6c36052c58bde5e57040ebe6bdb9f6a52c906fff"
  199. }
  200. ]
  201. }
  202. """
  203. COMMIT_DETAILS_EXAMPLE = r"""
  204. {
  205. "_links": {
  206. "changes": {
  207. "href": "https://mbittker.visualstudio.com/_apis/git/repositories/666ffcce-8ffa-46ec-bccf-b93b55bb2320/commits/6c36052c58bde5e57040ebe6bdb9f6a52c906fff/changes"
  208. },
  209. "repository": {
  210. "href": "https://mbittker.visualstudio.com/_apis/git/repositories/666ffcce-8ffa-46ec-bccf-b93b55bb2320"
  211. },
  212. "self": {
  213. "href": "https://mbittker.visualstudio.com/_apis/git/repositories/666ffcce-8ffa-46ec-bccf-b93b55bb2320/commits/6c36052c58bde5e57040ebe6bdb9f6a52c906fff"
  214. },
  215. "web": {
  216. "href": "https://mbittker.visualstudio.com/_git/MyFirstProject/commit/6c36052c58bde5e57040ebe6bdb9f6a52c906fff"
  217. }
  218. },
  219. "author": {
  220. "date": "2018-11-23T15:59:19Z",
  221. "email": "max@sentry.io",
  222. "imageUrl": "https://www.gravatar.com/avatar/1cee8d752bcad4c172d60e56bb398c11?r=g&d=mm",
  223. "name": "max bitker"
  224. },
  225. "comment": "Updated README.md\n\nSecond line\n\nFixes SENTRY-1",
  226. "commitId": "6c36052c58bde5e57040ebe6bdb9f6a52c906fff",
  227. "committer": {
  228. "date": "2018-11-23T15:59:19Z",
  229. "email": "max@sentry.io",
  230. "imageUrl": "https://www.gravatar.com/avatar/1cee8d752bcad4c172d60e56bb398c11?r=g&d=mm",
  231. "name": "max bittker"
  232. },
  233. "parents": [
  234. "641e82ce0ed14f3cf3670b0bf5f669d7fbd40a68"
  235. ],
  236. "push": {
  237. "date": "2018-11-23T16:01:10.7246278Z",
  238. "pushId": 2,
  239. "pushedBy": {
  240. "_links": {
  241. "avatar": {
  242. "href": "https://mbittker.visualstudio.com/_apis/GraphProfile/MemberAvatars/msa.NjI0ZGRhOWMtODgyZC03ZmRhLTk3OWItZTdhMjI5MWMzMzBk"
  243. }
  244. },
  245. "descriptor": "msa.NjI0ZGRhOWMtODgyZC03ZmRhLTk3OWItZTdhMjI5MWMzMzBk",
  246. "displayName": "Mark Story",
  247. "id": "624dda9c-882d-6fda-979b-e7a2291c330d",
  248. "imageUrl": "https://mbittker.visualstudio.com/_api/_common/identityImage?id=624dda9c-882d-6fda-979b-e7a2291c330d",
  249. "uniqueName": "mark@mark-story.com",
  250. "url": "https://mbittker.visualstudio.com/Aa365971d-9897-47eb-becf-c5142d33db08/_apis/Identities/624dda9c-882d-6fda-979b-e7a2291c330d"
  251. }
  252. },
  253. "remoteUrl": "https://mbittker.visualstudio.com/MyFirstProject/_git/box-of-things/commit/6c36052c58bde5e57040ebe6bdb9f6a52c906fff",
  254. "treeId": "026257a5e53eb923497c0217ef76e567f3a60088",
  255. "url": "https://mbittker.visualstudio.com/_apis/git/repositories/666ffcce-8ffa-46ec-bccf-b93b55bb2320/commits/6c36052c58bde5e57040ebe6bdb9f6a52c906fff"
  256. }
  257. """
  258. FILE_CHANGES_EXAMPLE = b"""
  259. {
  260. "changeCounts": {"Edit": 1},
  261. "changes": [
  262. {
  263. "item": {
  264. "objectId": "b48e843656a0a12926a0bcedefe8ef3710fe2867",
  265. "originalObjectId": "270b590a4edf3f19aa7acc7b57379729e34fc681",
  266. "gitObjectType": "blob",
  267. "commitId": "6c36052c58bde5e57040ebe6bdb9f6a52c906fff",
  268. "path": "/README.md",
  269. "url":
  270. "https://mbittker.visualstudio.com/DefaultCollection/_apis/git/repositories/b1e25999-c080-4ea1-8c61-597c4ec41f06/items/README.md?versionType=Commit&version=6c36052c58bde5e57040ebe6bdb9f6a52c906fff"
  271. },
  272. "changeType": "edit"
  273. }
  274. ]
  275. }
  276. """
  277. WORK_ITEM_RESPONSE = """{
  278. "id": 309,
  279. "rev": 1,
  280. "fields": {
  281. "System.AreaPath": "Fabrikam-Fiber-Git",
  282. "System.TeamProject": "Fabrikam-Fiber-Git",
  283. "System.IterationPath": "Fabrikam-Fiber-Git",
  284. "System.WorkItemType": "Product Backlog Item",
  285. "System.State": "New",
  286. "System.Reason": "New backlog item",
  287. "System.CreatedDate": "2015-01-07T18:13:01.807Z",
  288. "System.CreatedBy": "Jamal Hartnett <fabrikamfiber4@hotmail.com>",
  289. "System.ChangedDate": "2015-01-07T18:13:01.807Z",
  290. "System.ChangedBy": "Jamal Hartnett <fabrikamfiber4@hotmail.com>",
  291. "System.Title": "Hello",
  292. "Microsoft.VSTS.Scheduling.Effort": 8,
  293. "WEF_6CB513B6E70E43499D9FC94E5BBFB784_Kanban.Column": "New",
  294. "System.Description": "Fix this."
  295. },
  296. "_links": {
  297. "self": {
  298. "href": "https://fabrikam-fiber-inc.visualstudio.com/DefaultCollection/_apis/wit/workItems/309"
  299. },
  300. "workItemUpdates": {
  301. "href": "https://fabrikam-fiber-inc.visualstudio.com/DefaultCollection/_apis/wit/workItems/309/updates"
  302. },
  303. "workItemRevisions": {
  304. "href": "https://fabrikam-fiber-inc.visualstudio.com/DefaultCollection/_apis/wit/workItems/309/revisions"
  305. },
  306. "workItemHistory": {
  307. "href": "https://fabrikam-fiber-inc.visualstudio.com/DefaultCollection/_apis/wit/workItems/309/history"
  308. },
  309. "html": {
  310. "href": "https://fabrikam-fiber-inc.visualstudio.com/web/wi.aspx?pcguid=d81542e4-cdfa-4333-b082-1ae2d6c3ad16&id=309"
  311. },
  312. "workItemType": {
  313. "href": "https://fabrikam-fiber-inc.visualstudio.com/DefaultCollection/6ce954b1-ce1f-45d1-b94d-e6bf2464ba2c/_apis/wit/workItemTypes/Product%20Backlog%20Item"
  314. },
  315. "fields": {
  316. "href": "https://fabrikam-fiber-inc.visualstudio.com/DefaultCollection/_apis/wit/fields"
  317. }
  318. },
  319. "url": "https://fabrikam-fiber-inc.visualstudio.com/DefaultCollection/_apis/wit/workItems/309"
  320. }"""
  321. GET_USERS_RESPONSE = b"""{
  322. "count": 4,
  323. "value": [
  324. {
  325. "subjectKind": "user",
  326. "cuid": "ec09a4d8-d914-4f28-9e39-23d52b683f90",
  327. "domain": "Build",
  328. "principalName": "51ac8d19-6694-459f-a65e-bec30e9e2e33",
  329. "mailAddress": "",
  330. "origin": "vsts",
  331. "originId": "ec09a4d8-d914-4f28-9e39-23d52b683f90",
  332. "displayName": "Project Collection Build Service (Ftottentest2)",
  333. "_links": {
  334. "self": {
  335. "href": "https://fabrikam.vssps.visualstudio.com/_apis/graph/users/TWljcm9zb2Z0LlRlYW1Gb3VuZGF0aW9uLlNlcnZpY2VJZGVudGl0eTtmMzViOTAxNS1jZGU4LTQ4MzQtYTFkNS0wOWU4ZjM1OWNiODU6QnVpbGQ6NTFhYzhkMTktNjY5NC00NTlmLWE2NWUtYmVjMzBlOWUyZTMz"
  336. },
  337. "memberships": {
  338. "href": "https://fabrikam.vssps.visualstudio.com/_apis/graph/memberships/TWljcm9zb2Z0LlRlYW1Gb3VuZGF0aW9uLlNlcnZpY2VJZGVudGl0eTtmMzViOTAxNS1jZGU4LTQ4MzQtYTFkNS0wOWU4ZjM1OWNiODU6QnVpbGQ6NTFhYzhkMTktNjY5NC00NTlmLWE2NWUtYmVjMzBlOWUyZTMz"
  339. }
  340. },
  341. "url": "https://fabrikam.vssps.visualstudio.com/_apis/graph/users/TWljcm9zb2Z0LlRlYW1Gb3VuZGF0aW9uLlNlcnZpY2VJZGVudGl0eTtmMzViOTAxNS1jZGU4LTQ4MzQtYTFkNS0wOWU4ZjM1OWNiODU6QnVpbGQ6NTFhYzhkMTktNjY5NC00NTlmLWE2NWUtYmVjMzBlOWUyZTMz",
  342. "descriptor": "TWljcm9zb2Z0LlRlYW1Gb3VuZGF0aW9uLlNlcnZpY2VJZGVudGl0eTtmMzViOTAxNS1jZGU4LTQ4MzQtYTFkNS0wOWU4ZjM1OWNiODU6QnVpbGQ6NTFhYzhkMTktNjY5NC00NTlmLWE2NWUtYmVjMzBlOWUyZTMz"
  343. },
  344. {
  345. "subjectKind": "user",
  346. "metaType": "member",
  347. "cuid": "00ca946b-2fe9-4f2a-ae2f-40d5c48001bc",
  348. "domain": "LOCAL AUTHORITY",
  349. "principalName": "TeamFoundationService (TEAM FOUNDATION)",
  350. "mailAddress": "",
  351. "origin": "vsts",
  352. "originId": "00ca946b-2fe9-4f2a-ae2f-40d5c48001bc",
  353. "displayName": "TeamFoundationService (TEAM FOUNDATION)",
  354. "_links": {
  355. "self": {
  356. "href": "https://fabrikam.vssps.visualstudio.com/_apis/graph/users/TWljcm9zb2Z0LklkZW50aXR5TW9kZWwuQ2xhaW1zLkNsYWltc0lkZW50aXR5Ozc3ODlmMDlkLWUwNTMtNGYyZS1iZGVlLTBjOGY4NDc2YTRiYw"
  357. },
  358. "memberships": {
  359. "href": "https://fabrikam.vssps.visualstudio.com/_apis/graph/memberships/TWljcm9zb2Z0LklkZW50aXR5TW9kZWwuQ2xhaW1zLkNsYWltc0lkZW50aXR5Ozc3ODlmMDlkLWUwNTMtNGYyZS1iZGVlLTBjOGY4NDc2YTRiYw"
  360. }
  361. },
  362. "url": "https://fabrikam.vssps.visualstudio.com/_apis/graph/users/TWljcm9zb2Z0LklkZW50aXR5TW9kZWwuQ2xhaW1zLkNsYWltc0lkZW50aXR5Ozc3ODlmMDlkLWUwNTMtNGYyZS1iZGVlLTBjOGY4NDc2YTRiYw",
  363. "descriptor": "TWljcm9zb2Z0LklkZW50aXR5TW9kZWwuQ2xhaW1zLkNsYWltc0lkZW50aXR5Ozc3ODlmMDlkLWUwNTMtNGYyZS1iZGVlLTBjOGY4NDc2YTRiYw"
  364. },
  365. {
  366. "subjectKind": "user",
  367. "metaType": "member",
  368. "cuid": "ddd94918-1fc8-459b-994a-cca86c4fbe95",
  369. "domain": "TEAM FOUNDATION",
  370. "principalName": "Anonymous",
  371. "mailAddress": "",
  372. "origin": "vsts",
  373. "originId": "ddd94918-1fc8-459b-994a-cca86c4fbe95",
  374. "displayName": "Anonymous",
  375. "_links": {
  376. "self": {
  377. "href": "https://fabrikam.vssps.visualstudio.com/_apis/graph/users/TWljcm9zb2Z0LlRlYW1Gb3VuZGF0aW9uLlVuYXV0aGVudGljYXRlZElkZW50aXR5O1MtMS0wLTA"
  378. },
  379. "memberships": {
  380. "href": "https://fabrikam.vssps.visualstudio.com/_apis/graph/memberships/TWljcm9zb2Z0LlRlYW1Gb3VuZGF0aW9uLlVuYXV0aGVudGljYXRlZElkZW50aXR5O1MtMS0wLTA"
  381. }
  382. },
  383. "url": "https://fabrikam.vssps.visualstudio.com/_apis/graph/users/TWljcm9zb2Z0LlRlYW1Gb3VuZGF0aW9uLlVuYXV0aGVudGljYXRlZElkZW50aXR5O1MtMS0wLTA",
  384. "descriptor": "TWljcm9zb2Z0LlRlYW1Gb3VuZGF0aW9uLlVuYXV0aGVudGljYXRlZElkZW50aXR5O1MtMS0wLTA"
  385. },
  386. {
  387. "subjectKind": "user",
  388. "metaType": "member",
  389. "cuid": "65903f92-53dc-61b3-bb0e-e69cfa1cb719",
  390. "domain": "45aa3d2d-7442-473d-b4d3-3c670da9dd96",
  391. "principalName": "ftotten@vscsi.us",
  392. "mailAddress": "ftotten@vscsi.us",
  393. "origin": "aad",
  394. "originId": "4be8f294-000d-4431-8506-57420b88e204",
  395. "displayName": "Francis Totten",
  396. "_links": {
  397. "self": {
  398. "href": "https://fabrikam.vssps.visualstudio.com/_apis/graph/users/TWljcm9zb2Z0LklkZW50aXR5TW9kZWwuQ2xhaW1zLkNsYWltc0lkZW50aXR5OzQ1YWEzZDJkLTc0NDItNDczZC1iNGQzLTNjNjcwZGE5ZGQ5NlxmdG90dGVuQHZzY3NpLnVz"
  399. },
  400. "memberships": {
  401. "href": "https://fabrikam.vssps.visualstudio.com/_apis/graph/memberships/TWljcm9zb2Z0LklkZW50aXR5TW9kZWwuQ2xhaW1zLkNsYWltc0lkZW50aXR5OzQ1YWEzZDJkLTc0NDItNDczZC1iNGQzLTNjNjcwZGE5ZGQ5NlxmdG90dGVuQHZzY3NpLnVz"
  402. }
  403. },
  404. "url": "https://fabrikam.vssps.visualstudio.com/_apis/graph/users/TWljcm9zb2Z0LklkZW50aXR5TW9kZWwuQ2xhaW1zLkNsYWltc0lkZW50aXR5OzQ1YWEzZDJkLTc0NDItNDczZC1iNGQzLTNjNjcwZGE5ZGQ5NlxmdG90dGVuQHZzY3NpLnVz",
  405. "descriptor": "TWljcm9zb2Z0LklkZW50aXR5TW9kZWwuQ2xhaW1zLkNsYWltc0lkZW50aXR5OzQ1YWEzZDJkLTc0NDItNDczZC1iNGQzLTNjNjcwZGE5ZGQ5NlxmdG90dGVuQHZzY3NpLnVz"
  406. }
  407. ]
  408. }
  409. """
  410. CREATE_SUBSCRIPTION = {
  411. "id": "fd672255-8b6b-4769-9260-beea83d752ce",
  412. "url": "https://fabrikam.visualstudio.com/_apis/hooks/subscriptions/fd672255-8b6b-4769-9260-beea83d752ce",
  413. "publisherId": "tfs",
  414. "eventType": "workitem.update",
  415. "resourceVersion": "1.0-preview.1",
  416. "eventDescription": "WorkItem Updated",
  417. "consumerId": "webHooks",
  418. "consumerActionId": "httpRequest",
  419. "actionDescription": "To host myservice",
  420. "createdBy": {"id": "00ca946b-2fe9-4f2a-ae2f-40d5c48001bc"},
  421. "createdDate": "2014-10-27T15:37:24.873Z",
  422. "modifiedBy": {"id": "00ca946b-2fe9-4f2a-ae2f-40d5c48001bc"},
  423. "modifiedDate": "2014-10-27T15:37:26.23Z",
  424. "publisherInputs": {
  425. "buildStatus": "Failed",
  426. "definitionName": "MyWebSite CI",
  427. "hostId": "d81542e4-cdfa-4333-b082-1ae2d6c3ad16",
  428. "projectId": "6ce954b1-ce1f-45d1-b94d-e6bf2464ba2c",
  429. "tfsSubscriptionId": "3e8b33e7-426d-4c92-9bf9-58e163dd7dd5",
  430. },
  431. "consumerInputs": {"url": "https://myservice/newreceiver"},
  432. }
  433. WORK_ITEM_UPDATED = {
  434. "resourceContainers": {
  435. "project": {
  436. "id": "c0bf429a-c03c-4a99-9336-d45be74db5a6",
  437. "baseUrl": "https://laurynsentry.visualstudio.com/",
  438. },
  439. "account": {
  440. "id": "90e9a854-eb98-4c56-ae1a-035a0f331dd6",
  441. "baseUrl": "https://laurynsentry.visualstudio.com/",
  442. },
  443. "collection": {
  444. "id": "80ded3e8-3cd3-43b1-9f96-52032624aa3a",
  445. "baseUrl": "https://laurynsentry.visualstudio.com/",
  446. },
  447. },
  448. "resource": {
  449. "revisedBy": {
  450. "displayName": "lauryn",
  451. "name": "lauryn <lauryn@sentry.io>",
  452. "url": "https://app.vssps.visualstudio.com/A90e9a854-eb98-4c56-ae1a-035a0f331dd6/_apis/Identities/21354f98-ab06-67d9-b974-5a54d992082e",
  453. "imageUrl": "https://laurynsentry.visualstudio.com/_api/_common/identityImage?id=21354f98-ab06-67d9-b974-5a54d992082e",
  454. "descriptor": "msa.MjEzNTRmOTgtYWIwNi03N2Q5LWI5NzQtNWE1NGQ5OTIwODJl",
  455. "_links": {
  456. "avatar": {
  457. "href": "https://laurynsentry.visualstudio.com/_apis/GraphProfile/MemberAvatars/msa.MjEzNTRmOTgtYWIwNi03N2Q5LWI5NzQtNWE1NGQ5OTIwODJl"
  458. }
  459. },
  460. "uniqueName": "lauryn@sentry.io",
  461. "id": "21354f98-ab06-67d9-b974-5a54d992082e",
  462. },
  463. "revisedDate": "9999-01-01T00:00:00Z",
  464. "url": "https://laurynsentry.visualstudio.com/c0bf429a-c03c-4a99-9336-d45be74db5a6/_apis/wit/workItems/31/updates/2",
  465. "fields": {
  466. "System.AuthorizedDate": {
  467. "newValue": "2018-07-05T20:52:14.777Z",
  468. "oldValue": "2018-07-05T20:51:58.927Z",
  469. },
  470. "System.AssignedTo": {
  471. "newValue": "lauryn <lauryn@sentry.io>",
  472. "oldValue": "lauryn2 <lauryn2@sentry.io>",
  473. },
  474. "System.Watermark": {"newValue": 78, "oldValue": 77},
  475. "System.Rev": {"newValue": 2, "oldValue": 1},
  476. "System.RevisedDate": {
  477. "newValue": "9999-01-01T00:00:00Z",
  478. "oldValue": "2018-07-05T20:52:14.777Z",
  479. },
  480. "System.ChangedDate": {
  481. "newValue": "2018-07-05T20:52:14.777Z",
  482. "oldValue": "2018-07-05T20:51:58.927Z",
  483. },
  484. },
  485. "workItemId": 31,
  486. "rev": 2,
  487. "_links": {
  488. "self": {
  489. "href": "https://laurynsentry.visualstudio.com/c0bf429a-c03c-4a99-9336-d45be74db5a6/_apis/wit/workItems/31/updates/2"
  490. },
  491. "workItemUpdates": {
  492. "href": "https://laurynsentry.visualstudio.com/c0bf429a-c03c-4a99-9336-d45be74db5a6/_apis/wit/workItems/31/updates"
  493. },
  494. "html": {
  495. "href": "https://laurynsentry.visualstudio.com/web/wi.aspx?pcguid=80ded3e8-3cd3-43b1-9f96-52032624aa3a&id=31"
  496. },
  497. "parent": {
  498. "href": "https://laurynsentry.visualstudio.com/c0bf429a-c03c-4a99-9336-d45be74db5a6/_apis/wit/workItems/31"
  499. },
  500. },
  501. "id": 2,
  502. "revision": {
  503. "url": "https://laurynsentry.visualstudio.com/c0bf429a-c03c-4a99-9336-d45be74db5a6/_apis/wit/workItems/31/revisions/2",
  504. "fields": {
  505. "System.AreaPath": "MyFirstProject",
  506. "System.WorkItemType": "Bug",
  507. "System.Reason": "New",
  508. "System.Title": "NameError: global name 'BitbucketRepositoryProvider' is not defined",
  509. "Microsoft.VSTS.Common.Priority": 2,
  510. "System.CreatedBy": "lauryn <lauryn@sentry.io>",
  511. "System.AssignedTo": "lauryn <lauryn@sentry.io>",
  512. "System.CreatedDate": "2018-07-05T20:51:58.927Z",
  513. "System.TeamProject": "MyFirstProject",
  514. "Microsoft.VSTS.Common.Severity": "3 - Medium",
  515. "Microsoft.VSTS.Common.ValueArea": "Business",
  516. "System.State": "New",
  517. "System.Description": "<p><a href=\"https://lauryn.ngrok.io/sentry/internal/issues/55/\">https://lauryn.ngrok.io/sentry/internal/issues/55/</a></p>\n<pre><code>NameError: global name 'BitbucketRepositoryProvider' is not defined\n(1 additional frame(s) were not displayed)\n...\n File &quot;sentry/runner/__init__.py&quot;, line 125, in configure\n configure(ctx, py, yaml, skip_service_validation)\n File &quot;sentry/runner/settings.py&quot;, line 152, in configure\n skip_service_validation=skip_service_validation\n File &quot;sentry/runner/initializer.py&quot;, line 315, in initialize_app\n register_plugins(settings)\n File &quot;sentry/runner/initializer.py&quot;, line 60, in register_plugins\n integration.setup()\n File &quot;sentry/integrations/bitbucket/integration.py&quot;, line 78, in setup\n BitbucketRepositoryProvider,\n\nNameError: global name 'BitbucketRepositoryProvider' is not defined\n</code></pre>\n",
  518. "System.ChangedBy": "lauryn <lauryn@sentry.io>",
  519. "System.ChangedDate": "2018-07-05T20:52:14.777Z",
  520. "Microsoft.VSTS.Common.StateChangeDate": "2018-07-05T20:51:58.927Z",
  521. "System.IterationPath": "MyFirstProject",
  522. },
  523. "rev": 2,
  524. "id": 31,
  525. "_links": {
  526. "self": {
  527. "href": "https://laurynsentry.visualstudio.com/c0bf429a-c03c-4a99-9336-d45be74db5a6/_apis/wit/workItems/31/revisions/2"
  528. },
  529. "workItemRevisions": {
  530. "href": "https://laurynsentry.visualstudio.com/c0bf429a-c03c-4a99-9336-d45be74db5a6/_apis/wit/workItems/31/revisions"
  531. },
  532. "parent": {
  533. "href": "https://laurynsentry.visualstudio.com/c0bf429a-c03c-4a99-9336-d45be74db5a6/_apis/wit/workItems/31"
  534. },
  535. },
  536. },
  537. },
  538. "eventType": "workitem.updated",
  539. "detailedMessage": None,
  540. "createdDate": "2018-07-05T20:52:16.3051288Z",
  541. "id": "18f51331-2640-4bce-9ebd-c59c855956a2",
  542. "resourceVersion": "1.0",
  543. "notificationId": 1,
  544. "subscriptionId": "7bf628eb-b3a7-4fb2-ab4d-8b60f2e8cb9b",
  545. "publisherId": "tfs",
  546. "message": None,
  547. }
  548. WORK_ITEM_UNASSIGNED = {
  549. "resourceContainers": {
  550. "project": {
  551. "id": "c0bf429a-c03c-4a99-9336-d45be74db5a6",
  552. "baseUrl": "https://laurynsentry.visualstudio.com/",
  553. },
  554. "account": {
  555. "id": "90e9a854-eb98-4c56-ae1a-035a0f331dd6",
  556. "baseUrl": "https://laurynsentry.visualstudio.com/",
  557. },
  558. "collection": {
  559. "id": "80ded3e8-3cd3-43b1-9f96-52032624aa3a",
  560. "baseUrl": "https://laurynsentry.visualstudio.com/",
  561. },
  562. },
  563. "resource": {
  564. "revisedBy": {
  565. "displayName": "lauryn",
  566. "name": "lauryn <lauryn@sentry.io>",
  567. "url": "https://app.vssps.visualstudio.com/A90e9a854-eb98-4c56-ae1a-035a0f331dd6/_apis/Identities/21354f98-ab06-67d9-b974-5a54d992082e",
  568. "imageUrl": "https://laurynsentry.visualstudio.com/_api/_common/identityImage?id=21354f98-ab06-67d9-b974-5a54d992082e",
  569. "descriptor": "msa.MjEzNTRmOTgtYWIwNi03N2Q5LWI5NzQtNWE1NGQ5OTIwODJl",
  570. "_links": {
  571. "avatar": {
  572. "href": "https://laurynsentry.visualstudio.com/_apis/GraphProfile/MemberAvatars/msa.MjEzNTRmOTgtYWIwNi03N2Q5LWI5NzQtNWE1NGQ5OTIwODJl"
  573. }
  574. },
  575. "uniqueName": "lauryn@sentry.io",
  576. "id": "21354f98-ab06-67d9-b974-5a54d992082e",
  577. },
  578. "revisedDate": "9999-01-01T00:00:00 Z",
  579. "url": "https://laurynsentry.visualstudio.com/c0bf429a-c03c-4a99-9336-d45be74db5a6/_apis/wit/workItems/33/updates/3",
  580. "fields": {
  581. "System.AuthorizedDate": {
  582. "newValue": "2018-07-05T23:23:09.493 Z",
  583. "oldValue": "2018-07-05T23:21:38.243 Z",
  584. },
  585. "System.AssignedTo": {"oldValue": "lauryn <lauryn@sentry.io>"},
  586. "System.Watermark": {"newValue": 83, "oldValue": 82},
  587. "System.Rev": {"newValue": 3, "oldValue": 2},
  588. "System.RevisedDate": {
  589. "newValue": "9999-01-01T00:00:00 Z",
  590. "oldValue": "2018-07-05T23:23:09.493 Z",
  591. },
  592. "System.ChangedDate": {
  593. "newValue": "2018-07-05T23:23:09.493 Z",
  594. "oldValue": "2018-07-05T23:21:38.243 Z",
  595. },
  596. },
  597. "workItemId": 33,
  598. "rev": 3,
  599. "_links": {
  600. "self": {
  601. "href": "https://laurynsentry.visualstudio.com/c0bf429a-c03c-4a99-9336-d45be74db5a6/_apis/wit/workItems/33/updates/3"
  602. },
  603. "workItemUpdates": {
  604. "href": "https://laurynsentry.visualstudio.com/c0bf429a-c03c-4a99-9336-d45be74db5a6/_apis/wit/workItems/33/updates"
  605. },
  606. "html": {
  607. "href": "https://laurynsentry.visualstudio.com/web/wi.aspx?pcguid=80ded3e8-3cd3-43b1-9f96-52032624aa3a&id=33"
  608. },
  609. "parent": {
  610. "href": "https://laurynsentry.visualstudio.com/c0bf429a-c03c-4a99-9336-d45be74db5a6/_apis/wit/workItems/33"
  611. },
  612. },
  613. "id": 3,
  614. "revision": {
  615. "url": "https://laurynsentry.visualstudio.com/c0bf429a-c03c-4a99-9336-d45be74db5a6/_apis/wit/workItems/33/revisions/3",
  616. "fields": {
  617. "System.AreaPath": "MyFirstProject",
  618. "System.WorkItemType": "Bug",
  619. "System.Reason": "New",
  620. "System.Title": "NotImplementedError:Visual Studio Team Services requires an organization_id",
  621. "Microsoft.VSTS.Common.Priority": 2,
  622. "System.CreatedBy": "lauryn <lauryn@sentry.io>",
  623. "Microsoft.VSTS.Common.StateChangeDate": "2018-07-05T23:21:25.847 Z",
  624. "System.CreatedDate": "2018-07-05T23:21:25.847 Z",
  625. "System.TeamProject": "MyFirstProject",
  626. "Microsoft.VSTS.Common.ValueArea": "Business",
  627. "System.State": "New",
  628. "System.Description": '<p><a href="https: //lauryn.ngrok.io/sentry/internal/issues/196/">https: //lauryn.ngrok.io/sentry/internal/issues/196/</a></p>\n<pre><code>NotImplementedError:Visual Studio Team Services requires an organization_id\n(57 additional frame(s) were not displayed)\n...\n File &quot;sentry/tasks/base.py&quot;',
  629. "System.ChangedBy": "lauryn <lauryn@sentry.io>",
  630. "System.ChangedDate": "2018-07-05T23:23:09.493 Z",
  631. "Microsoft.VSTS.Common.Severity": "3 - Medium",
  632. "System.IterationPath": "MyFirstProject",
  633. },
  634. "rev": 3,
  635. "id": 33,
  636. "_links": {
  637. "self": {
  638. "href": "https://laurynsentry.visualstudio.com/c0bf429a-c03c-4a99-9336-d45be74db5a6/_apis/wit/workItems/33/revisions/3"
  639. },
  640. "workItemRevisions": {
  641. "href": "https://laurynsentry.visualstudio.com/c0bf429a-c03c-4a99-9336-d45be74db5a6/_apis/wit/workItems/33/revisions"
  642. },
  643. "parent": {
  644. "href": "https://laurynsentry.visualstudio.com/c0bf429a-c03c-4a99-9336-d45be74db5a6/_apis/wit/workItems/33"
  645. },
  646. },
  647. },
  648. },
  649. "eventType": "workitem.updated",
  650. "detailedMessage": None,
  651. "createdDate": "2018-07-05T23:23:11.1935112 Z",
  652. "id": "cc349c85-6595-4939-9b69-f89480be6a26",
  653. "resourceVersion": "1.0",
  654. "notificationId": 2,
  655. "subscriptionId": "7405a600-6a25-48e6-81b6-1dde044783ad",
  656. "publisherId": "tfs",
  657. "message": None,
  658. }
  659. WORK_ITEM_UPDATED_STATUS = {
  660. "resourceContainers": {
  661. "project": {
  662. "id": "c0bf429a-c03c-4a99-9336-d45be74db5a6",
  663. "baseUrl": "https://laurynsentry.visualstudio.com/",
  664. },
  665. "account": {
  666. "id": "90e9a854-eb98-4c56-ae1a-035a0f331dd6",
  667. "baseUrl": "https://laurynsentry.visualstudio.com/",
  668. },
  669. "collection": {
  670. "id": "80ded3e8-3cd3-43b1-9f96-52032624aa3a",
  671. "baseUrl": "https://laurynsentry.visualstudio.com/",
  672. },
  673. },
  674. "resource": {
  675. "revisedBy": {
  676. "displayName": "lauryn",
  677. "name": "lauryn <lauryn@sentry.io>",
  678. "url": "https://app.vssps.visualstudio.com/A90e9a854-eb98-4c56-ae1a-035a0f331dd6/_apis/Identities/21354f98-ab06-67d9-b974-5a54d992082e",
  679. "imageUrl": "https://laurynsentry.visualstudio.com/_api/_common/identityImage?id=21354f98-ab06-67d9-b974-5a54d992082e",
  680. "descriptor": "msa.MjEzNTRmOTgtYWIwNi03N2Q5LWI5NzQtNWE1NGQ5OTIwODJl",
  681. "_links": {
  682. "avatar": {
  683. "href": "https://laurynsentry.visualstudio.com/_apis/GraphProfile/MemberAvatars/msa.MjEzNTRmOTgtYWIwNi03N2Q5LWI5NzQtNWE1NGQ5OTIwODJl"
  684. }
  685. },
  686. "uniqueName": "lauryn@sentry.io",
  687. "id": "21354f98-ab06-67d9-b974-5a54d992082e",
  688. },
  689. "revisedDate": "9999-01-01T00:00:00 Z",
  690. "url": "https://laurynsentry.visualstudio.com/c0bf429a-c03c-4a99-9336-d45be74db5a6/_apis/wit/workItems/33/updates/3",
  691. "fields": {
  692. "System.AuthorizedDate": {
  693. "newValue": "2018-07-05T23:23:09.493 Z",
  694. "oldValue": "2018-07-05T23:21:38.243 Z",
  695. },
  696. "System.State": {"oldValue": "New", "newValue": "Resolved"},
  697. "System.Watermark": {"newValue": 83, "oldValue": 82},
  698. "System.Rev": {"newValue": 3, "oldValue": 2},
  699. "System.RevisedDate": {
  700. "newValue": "9999-01-01T00:00:00 Z",
  701. "oldValue": "2018-07-05T23:23:09.493 Z",
  702. },
  703. "System.ChangedDate": {
  704. "newValue": "2018-07-05T23:23:09.493 Z",
  705. "oldValue": "2018-07-05T23:21:38.243 Z",
  706. },
  707. },
  708. "workItemId": 33,
  709. "rev": 3,
  710. "_links": {
  711. "self": {
  712. "href": "https://laurynsentry.visualstudio.com/c0bf429a-c03c-4a99-9336-d45be74db5a6/_apis/wit/workItems/33/updates/3"
  713. },
  714. "workItemUpdates": {
  715. "href": "https://laurynsentry.visualstudio.com/c0bf429a-c03c-4a99-9336-d45be74db5a6/_apis/wit/workItems/33/updates"
  716. },
  717. "html": {
  718. "href": "https://laurynsentry.visualstudio.com/web/wi.aspx?pcguid=80ded3e8-3cd3-43b1-9f96-52032624aa3a&id=33"
  719. },
  720. "parent": {
  721. "href": "https://laurynsentry.visualstudio.com/c0bf429a-c03c-4a99-9336-d45be74db5a6/_apis/wit/workItems/33"
  722. },
  723. },
  724. "id": 3,
  725. "revision": {
  726. "url": "https://laurynsentry.visualstudio.com/c0bf429a-c03c-4a99-9336-d45be74db5a6/_apis/wit/workItems/33/revisions/3",
  727. "fields": {
  728. "System.AreaPath": "MyFirstProject",
  729. "System.WorkItemType": "Bug",
  730. "System.Reason": "New",
  731. "System.Title": "NotImplementedError:Visual Studio Team Services requires an organization_id",
  732. "Microsoft.VSTS.Common.Priority": 2,
  733. "System.CreatedBy": "lauryn <lauryn@sentry.io>",
  734. "Microsoft.VSTS.Common.StateChangeDate": "2018-07-05T23:21:25.847 Z",
  735. "System.CreatedDate": "2018-07-05T23:21:25.847 Z",
  736. "System.TeamProject": "MyFirstProject",
  737. "Microsoft.VSTS.Common.ValueArea": "Business",
  738. "System.State": "New",
  739. "System.Description": '<p><a href="https: //lauryn.ngrok.io/sentry/internal/issues/196/">https: //lauryn.ngrok.io/sentry/internal/issues/196/</a></p>\n<pre><code>NotImplementedError:Visual Studio Team Services requires an organization_id\n(57 additional frame(s) were not displayed)\n...\n File &quot;sentry/tasks/base.py&quot;',
  740. "System.ChangedBy": "lauryn <lauryn@sentry.io>",
  741. "System.ChangedDate": "2018-07-05T23:23:09.493 Z",
  742. "Microsoft.VSTS.Common.Severity": "3 - Medium",
  743. "System.IterationPath": "MyFirstProject",
  744. },
  745. "rev": 3,
  746. "id": 33,
  747. "_links": {
  748. "self": {
  749. "href": "https://laurynsentry.visualstudio.com/c0bf429a-c03c-4a99-9336-d45be74db5a6/_apis/wit/workItems/33/revisions/3"
  750. },
  751. "workItemRevisions": {
  752. "href": "https://laurynsentry.visualstudio.com/c0bf429a-c03c-4a99-9336-d45be74db5a6/_apis/wit/workItems/33/revisions"
  753. },
  754. "parent": {
  755. "href": "https://laurynsentry.visualstudio.com/c0bf429a-c03c-4a99-9336-d45be74db5a6/_apis/wit/workItems/33"
  756. },
  757. },
  758. },
  759. },
  760. "eventType": "workitem.updated",
  761. "detailedMessage": None,
  762. "createdDate": "2018-07-05T23:23:11.1935112 Z",
  763. "id": "cc349c85-6595-4939-9b69-f89480be6a26",
  764. "resourceVersion": "1.0",
  765. "notificationId": 2,
  766. "subscriptionId": "7405a600-6a25-48e6-81b6-1dde044783ad",
  767. "publisherId": "tfs",
  768. "message": None,
  769. }
  770. WORK_ITEM_STATES = {
  771. "count": 5,
  772. "value": [
  773. {"name": "New", "color": "b2b2b2", "category": "Proposed"},
  774. {"name": "Active", "color": "007acc", "category": "InProgress"},
  775. {"name": "CustomState", "color": "5688E0", "category": "InProgress"},
  776. {"name": "Resolved", "color": "ff9d00", "category": "Resolved"},
  777. {"name": "Closed", "color": "339933", "category": "Completed"},
  778. ],
  779. }
  780. GET_PROJECTS_RESPONSE = """{
  781. "count": 1,
  782. "value": [{
  783. "id": "ac7c05bb-7f8e-4880-85a6-e08f37fd4a10",
  784. "name": "Fabrikam-Fiber-Git",
  785. "url": "https://jess-dev.visualstudio.com/_apis/projects/ac7c05bb-7f8e-4880-85a6-e08f37fd4a10",
  786. "state": "wellFormed",
  787. "revision": 16,
  788. "visibility": "private"
  789. }]
  790. }"""