11import os
22import json
33import time
4+ from json import JSONDecodeError
5+ from typing import Dict , Any
6+
47import requests
58
69
710class AzureDevOps :
811
912 def __init__ (self ):
1013 self .base_url = "https://dev.azure.com/NHSD-APIM/API Platform/_apis/pipelines"
11- self .token = os .environ ["AZURE_TOKEN" ]
12- self .auth = requests .auth .HTTPBasicAuth ("" , self .token )
14+ self .client_id = os .environ ["AZ_CLIENT_ID" ]
15+ self .client_secret = os .environ ["AZ_CLIENT_SECRET" ]
16+ self .client_tenant = os .environ ["AZ_CLIENT_TENANT" ]
17+ self .access_token = self ._get_access_token ()
1318 self .notify_commit_sha = os .environ ["NOTIFY_COMMIT_SHA" ]
1419 self .utils_pr_number = os .environ ["UTILS_PR_NUMBER" ]
1520 self .notify_github_repo = "NHSDigital/api-management-utils"
16- self .api_params = {"api-version" : "6.0-preview.1" }
1721 self .api_request_delay = 60
1822
1923 @staticmethod
@@ -34,16 +38,17 @@ def run_pipeline(self,
3438 run_url = self .base_url + f"/{ pipeline_id } /runs"
3539 request_body = self ._build_request_body (pipeline_branch )
3640
37- response = requests .post (run_url , auth = self .auth , params = self .api_params , json = request_body )
41+ response = self .api_request (
42+ run_url ,
43+ json = request_body ,
44+ method = 'post' ,
45+ )
3846 self .print_response (response , f"Initial request to { run_url } " )
3947
4048 result = "failed"
4149 if response .status_code == 200 :
4250 result = self ._check_pipeline_response (response )
4351 print (f"Result of { service } { pipeline_type } pipeline: { result } " )
44- elif response .status_code == 203 or response .status_code == 401 :
45- print (f"{ response .status_code } : Invalid or expired PAT (Personal Access Token),"
46- f" please verify or renew token" )
4752 else :
4853 print (f"Triggering pipeline: { service } { pipeline_type } failed, status code: { response .status_code } " )
4954 return result
@@ -54,8 +59,8 @@ def _check_pipeline_response(self, response: requests.Response):
5459 while response .status_code == 200 and response .json ()["state" ] == "inProgress" :
5560 time .sleep (self .api_request_delay )
5661 delay = delay + self .api_request_delay
57- response = requests . get (state_url , auth = self . auth , params = self . api_params )
58- self .print_response (response , f"Response from { state_url } after { delay } seconds" )
62+ state_response = self . api_request (state_url )
63+ self .print_response (state_response , f"Response from { state_url } after { delay } seconds" )
5964 return response .json ()["result" ]
6065
6166 def _build_request_body (self , pipeline_branch : str ):
@@ -87,3 +92,54 @@ def _build_request_body(self, pipeline_branch: str):
8792 }
8893 }
8994 }
95+
96+ def _get_access_token (self ):
97+ url = f"https://login.microsoftonline.com/{ self .client_tenant } /oauth2/v2.0/token"
98+ data = {
99+ "client_id" : self .client_id ,
100+ "client_secret" : self .client_secret ,
101+ "grant_type" : "client_credentials" ,
102+ "scope" : "https://app.vssps.visualstudio.com/.default" ,
103+ }
104+ headers = {"Content-Type" : "application/x-www-form-urlencoded" }
105+ res = requests .post (url = url , data = data , headers = headers )
106+ res .raise_for_status ()
107+
108+ return res .json ()["access_token" ]
109+
110+ def api_request (
111+ self ,
112+ uri ,
113+ params : Dict [str , Any ] = None ,
114+ headers : Dict [str , Any ] = None ,
115+ api_version : str = "6.0-preview.1" ,
116+ method : str = "get" ,
117+ max_tries : int = 5 ,
118+ ** kwargs ,
119+ ):
120+ def get_headers ():
121+
122+ _headers = {"Accept" : "application/json" , "Authorization" : f"Bearer { self .access_token } " }
123+ _headers .update (headers or {})
124+ return _headers
125+
126+ _params = {"api-version" : api_version }
127+ _params .update (params or {})
128+ action = getattr (requests , method )
129+
130+ result = action (uri , params = _params , headers = get_headers (), ** kwargs )
131+ tries = 0
132+ while result .status_code not in (200 , 201 , 202 , 204 ):
133+ tries += 1
134+
135+ if tries > max_tries :
136+ break
137+
138+ if result .status_code in (203 , 401 ):
139+ print ("REFRESHING ACCESS TOKEN..." )
140+ self .access_token = self ._get_access_token ()
141+
142+ time .sleep (0.5 * tries )
143+ result = action (uri , params = _params , headers = get_headers (), ** kwargs )
144+
145+ return result
0 commit comments