Implemented devops decorator and a get method that retrieves object properties by a key.
All checks were successful
/ unit-tests (push) Successful in 10s

This commit is contained in:
2025-11-04 08:37:35 +01:00
parent 9748230745
commit 05c9e5184c

View File

@@ -2,12 +2,13 @@ from __future__ import annotations
import requests import requests
import urllib.parse import urllib.parse
from uuid import UUID from uuid import UUID
from string import Template
DEVOPS_SCOPE = "https://app.vssps.visualstudio.com/.default" DEVOPS_SCOPE = "https://app.vssps.visualstudio.com/.default"
DEVOPS_API_VERSION = "7.1" DEVOPS_API_VERSION = "7.1"
# Define a class decorator # Define a class decorator
def auto_properties(mapping: dict[str,str] | None = None): def auto_properties(mapping: dict[str,str]):
def make_property(name: str): def make_property(name: str):
private_var = f"_{name}" private_var = f"_{name}"
@@ -53,6 +54,15 @@ def auto_properties(mapping: dict[str,str] | None = None):
return cls return cls
return decorator return decorator
def devops(key: str, get_url: str, list_url: str = None, params: dict = {}):
def decorator(cls):
cls.__entity_key__ = key
cls.__entity_get_url__ = get_url # Use $key in the URL
cls.__entity_list_url__ = list_url # Use $key in the URL
cls.__entity_params__ = params
return cls
return decorator
class DevOps(): class DevOps():
"""Base class for DevOps entities.""" """Base class for DevOps entities."""
@@ -77,7 +87,24 @@ class DevOps():
r.raise_for_status() # Ensure we raise an error for bad responses r.raise_for_status() # Ensure we raise an error for bad responses
return r return r
def _get_entity(self, key_name: str, get_url: str, params: dict = {}) -> object: def _get(self, key: str):
if not hasattr(self.__class__, "__entity_key__") or not hasattr(self.__class__, "__entity_get_url__"):
raise NotImplementedError("Called _get on a class that has not been decorated with @devops.")
setattr(self, f"_{self.__class__.__entity_key__}", key) # Set the entity key
# Build the URL
url = Template(self.__class__.__entity_get_url__).substitute(key=key)
# Build parameters with key substituted
params = {}
if hasattr(self.__class__, "__entity_params__"):
params = {k: Template(v).substitute(key=key) for k, v in self.__class__.__entity_params__.items()}
# Fetch the object data from the URL
r = self._get_url_path(url, params=params)
# Populate attributes
self.from_json(r.json())
def _get_entity(self, key_name: str, get_url: str, params: dict = {}):
""" """
Each entity class can use this method to populate its attributes, by defining Each entity class can use this method to populate its attributes, by defining
its own _get method that calls this one with the key name, its own _get method that calls this one with the key name,
@@ -129,6 +156,7 @@ class Organization(DevOps):
"url": "url", "url": "url",
"description": "description" "description": "description"
}) })
@devops("id", "_apis/projects/$key", "_apis/projects")
class Project(DevOps): class Project(DevOps):
def _get(self): def _get(self):
self._get_entity( self._get_entity(