class PoolManager(BaseManager): """ Manage DevOps Pools Attributes: See BaseManager """ def __init__(self, base_url='https://{}.visualstudio.com', creds=None, organization_name="", project_name=""): """Inits PoolManager""" super(PoolManager, self).__init__(creds, organization_name=organization_name, project_name=project_name) base_url = base_url.format(organization_name) self._config = Configuration(base_url=base_url) self._client = ServiceClient(creds, self._config) client_models = { k: v for k, v in models.__dict__.items() if isinstance(v, type) } self._deserialize = Deserializer(client_models) self._user_mgr = UserManager(creds=self._creds) def list_pools(self): """List what pools this project has""" project = self._get_project_by_name(self._project_name) # Construct URL url = "/" + project.id + "/_apis/distributedtask/queues?actionFilter=16" #construct header parameters header_paramters = {} if self._user_mgr.is_msa_account(): header_paramters['X-VSS-ForceMsaPassThrough'] = 'true' header_paramters['Accept'] = 'application/json' # Construct and send request request = self._client.get(url, headers=header_paramters) response = self._client.send(request) # Handle Response deserialized = None if response.status_code // 100 != 2: logging.error("GET %s", request.url) logging.error("response: %s", response.status_code) logging.error(response.text) raise HttpOperationError(self._deserialize, response) else: deserialized = self._deserialize('Pools', response) return deserialized def close_connection(self): self._client.close()
class BaseGithubManager(object): def __init__(self, base_url='https://api.github.com', pat=None): """Inits UserManager as to be able to send the right requests""" self._pat = pat self._config = Configuration(base_url=base_url) self._client = ServiceClient(None, self._config) def construct_github_request_header(self, pat=None): headers = {"Accept": "application/vnd.github.v3+json"} if pat: headers["Authorization"] = "token {pat}".format(pat=pat) elif self._pat: headers["Authorization"] = "token {pat}".format(pat=self._pat) return headers def close_connection(self): self._client.close()
class UserManager(object): """ Get details about a user Attributes: See BaseManager """ def __init__(self, base_url='https://peprodscussu2.portalext.visualstudio.com', creds=None): """Inits UserManager as to be able to send the right requests""" self._config = Configuration(base_url=base_url) self._client = ServiceClient(creds, self._config) #create the deserializer for the models client_models = { k: v for k, v in models.__dict__.items() if isinstance(v, type) } self._deserialize = Deserializer(client_models) def get_user_id(self, msa=False): """Get the user id""" header_parameters = {} if msa: header_parameters['X-VSS-ForceMsaPassThrough'] = 'true' header_parameters['Accept'] = 'application/json' request = self._client.get('/_apis/AzureTfs/UserContext') response = self._client.send(request, header_parameters) # Handle Response deserialized = None if response.status_code // 100 != 2: logging.error("GET %s", request.url) logging.error("response: %s", response.status_code) logging.error(response.text) raise HttpOperationError(self._deserialize, response) else: deserialized = self._deserialize('User', response) return deserialized def close_connection(self): self._client.close()
class OrganizationManager(): """ Manage DevOps organizations Create or list existing organizations Attributes: config: url configuration client: authentication client dserialize: deserializer to process http responses into python classes """ def __init__(self, base_url='https://app.vssps.visualstudio.com', creds=None, create_organization_url='https://app.vsaex.visualstudio.com'): """Inits OrganizationManager""" self._creds = creds self._config = Configuration(base_url=base_url) self._client = ServiceClient(creds, self._config) #need to make a secondary client for the creating organization as it uses a different base url self._create_organization_config = Configuration( base_url=create_organization_url) self._create_organization_client = ServiceClient( creds, self._create_organization_config) self._list_region_config = Configuration( base_url='https://aex.dev.azure.com') self._list_region_client = ServiceClient( creds, self._create_organization_config) client_models = { k: v for k, v in models.__dict__.items() if isinstance(v, type) } self._deserialize = Deserializer(client_models) def validate_organization_name(self, organization_name): """Validate an organization name by checking it does not already exist and that it fits name restrictions""" if organization_name is None: return models.ValidateAccountName( valid=False, message="The organization_name cannot be None") if re.search("[^0-9A-Za-z-]", organization_name): return models.ValidateAccountName( valid=False, message="""The name supplied contains forbidden characters. Only alphanumeric characters and dashes are allowed. Please try another organization name.""" ) #construct url url = '/_AzureSpsAccount/ValidateAccountName' #construct query parameters query_paramters = {} query_paramters['accountName'] = organization_name #construct header parameters header_paramters = {} header_paramters['Accept'] = 'application/json' request = self._client.get(url, params=query_paramters) response = self._client.send(request, headers=header_paramters) # Handle Response deserialized = None if response.status_code // 100 != 2: logging.error("GET %s", request.url) logging.error("response: %s", response.status_code) logging.error(response.text) raise HttpOperationError(self._deserialize, response) else: deserialized = self._deserialize('ValidateAccountName', response) return deserialized def list_organizations(self): """List what organizations this user is part of""" user_manager = UserManager(creds=self._creds) user_id_aad = user_manager.get_user_id(msa=False) user_id_msa = user_manager.get_user_id(msa=True) if (user_id_aad.id == user_id_msa.id): # Only need to do the one request as ids are the same organizations = self._list_organizations_request(user_id_aad.id) else: def uniq(lst): last = object() for item in lst: if item == last: continue yield item last = item # Need to do a request for each of the ids and then combine them organizations_aad = self._list_organizations_request( user_id_aad.id, msa=False) organizations_msa = self._list_organizations_request( user_id_msa.id, msa=True) organizations = organizations_aad # Now we just want to take the set of these two lists! organizations.value = list( uniq(organizations_aad.value + organizations_msa.value)) organizations.count = len(organizations.value) return organizations def _list_organizations_request(self, member_id, msa=False): url = '/_apis/Commerce/Subscription' query_paramters = {} query_paramters['memberId'] = member_id query_paramters['includeMSAAccounts'] = True query_paramters['queryOnlyOwnerAccounts'] = True query_paramters['inlcudeDisabledAccounts'] = False query_paramters['providerNamespaceId'] = 'VisualStudioOnline' #construct header parameters header_parameters = {} if msa: header_parameters['X-VSS-ForceMsaPassThrough'] = 'true' header_parameters['Accept'] = 'application/json' request = self._client.get(url, params=query_paramters) response = self._client.send(request, headers=header_parameters) # Handle Response deserialized = None if response.status_code // 100 != 2: logging.error("GET %s", request.url) logging.error("response: %s", response.status_code) logging.error(response.text) raise HttpOperationError(self._deserialize, response) else: deserialized = self._deserialize('Organizations', response) return deserialized def create_organization(self, region_code, organization_name): """Create a new organization for user""" url = '/_apis/HostAcquisition/collections' #construct query parameters query_paramters = {} query_paramters['collectionName'] = organization_name query_paramters['preferredRegion'] = region_code query_paramters['api-version'] = '4.0-preview.1' #construct header parameters header_paramters = {} header_paramters['Accept'] = 'application/json' header_paramters['Content-Type'] = 'application/json' #construct the payload payload = {} payload[ 'VisualStudio.Services.HostResolution.UseCodexDomainForHostCreation'] = 'true' request = self._create_organization_client.post(url=url, params=query_paramters, content=payload) response = self._create_organization_client.send( request, headers=header_paramters) # Handle Response deserialized = None if response.status_code // 100 != 2: logging.error("GET %s", request.url) logging.error("response: %s", response.status_code) logging.error(response.text) raise HttpOperationError(self._deserialize, response) else: deserialized = self._deserialize('NewOrganization', response) return deserialized def list_regions(self): """List what regions organizations can exist in""" # Construct URL url = '/_apis/hostacquisition/regions' #construct header parameters header_paramters = {} header_paramters['Accept'] = 'application/json' # Construct and send request request = self._list_region_client.get(url, headers=header_paramters) response = self._list_region_client.send(request) # Handle Response deserialized = None if response.status_code // 100 != 2: logging.error("GET %s", request.url) logging.error("response: %s", response.status_code) logging.error(response.text) raise HttpOperationError(self._deserialize, response) else: deserialized = self._deserialize('Regions', response) return deserialized def close_connection(self): """Close the sessions""" self._client.close() self._create_organization_client.close()
class UserManager(object): """ Get details about a user Attributes: See BaseManager """ def __init__(self, base_url='https://peprodscussu2.portalext.visualstudio.com', creds=None): """Inits UserManager as to be able to send the right requests""" self._config = Configuration(base_url=base_url) self._client = ServiceClient(creds, self._config) #create the deserializer for the models client_models = {k: v for k, v in models.__dict__.items() if isinstance(v, type)} self._deserialize = Deserializer(client_models) # create cache for two user type self._cache_aad_user = None self._cache_msa_user = None def is_msa_account(self): user_id_aad = self.get_user(msa=False).id user_id_msa = self.get_user(msa=True).id return user_id_aad != user_id_msa def get_user(self, msa=False): # Try to get from cache if msa is True and self._cache_msa_user is not None: return self._cache_msa_user if msa is False and self._cache_aad_user is not None: return self._cache_aad_user header_parameters = {} header_parameters['X-VSS-ForceMsaPassThrough'] = 'true' if msa else 'false' header_parameters['Accept'] = 'application/json' request = self._client.get('/_apis/AzureTfs/UserContext') response = self._client.send(request, header_parameters) # Handle Response deserialized = None if response.status_code // 100 != 2: logging.error("GET %s", request.url) logging.error("response: %s", response.status_code) logging.error(response.text) raise HttpOperationError(self._deserialize, response) else: deserialized = self._deserialize('User', response) # Write to cache if msa is True and self._cache_msa_user is None: self._cache_msa_user = deserialized if msa is False and self._cache_aad_user is None: self._cache_aad_user = deserialized return deserialized @property def aad_id(self): return self.get_user(msa=False).id @property def msa_id(self): return self.get_user(msa=True).id def close_connection(self): self._client.close()