common/api/resource_manager_api_adapter.py (69 lines of code) (raw):
# Copyright 2025 Google LLC
#
# 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
#
# https://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.
"""
This module provides an adapter for interacting with the
Google Cloud Data Catalog API.
It includes functionality for searching and retrieving tag templates
and entry groups.
Classes:
- DatacatalogApiAdapter: An adapter class for interacting with the
Data Catalog API.
"""
from common.utils import get_logger
from google.cloud import resourcemanager
from common.exceptions import FormatException
from common.entities import Project
from googleapiclient import discovery
from googleapiclient.errors import HttpError
from functools import cache
class ResourceManagerApiAdapter:
"""
An adapter class for interacting with the Google Cloud Asset API.
"""
def __init__(self):
"""
Initializes the ResourceManagerApiAdapter with a ResourceManager client.
"""
self.project_client = resourcemanager.ProjectsClient()
self.api_client = discovery.build("cloudresourcemanager", "v1")
self._logger = get_logger()
@cache
def get_project_number(self, project_id: str) -> str:
project = self.project_client.get_project(
name=f"projects/{project_id}"
)
return self.project_client.parse_common_project_path(
project.name
)["project"]
@cache
def get_organization_number(self, project_id: str) -> str:
ancestry = self.get_project_ancestry(project_id)
for ancestry_type, resource_id in ancestry:
if ancestry_type == Project.AncestryType.ORGANIZATION:
return resource_id
def get_project_ancestry(
self, project_id: str
) -> list[tuple[Project.AncestryType, str]]:
try:
response = (
self.api_client.projects()
.getAncestry(projectId=project_id)
.execute()
)
except HttpError as e:
if e.status_code == 403:
error_msg = (f"Not enough permissions for project {project_id}"
f" or project does not exists")
raise HttpError(
e.resp,
error_msg.encode("utf-8"),
uri=e.uri
)
elif e.status_code == 400:
error_msg = f"Incorrect project name: {project_id}"
raise HttpError(
e.resp,
error_msg.encode("utf-8"),
uri=e.uri
)
raise e
result = []
for item in response["ancestor"]:
ancestor = item["resourceId"]
if ancestor["type"] == "folder":
result.append((Project.AncestryType.FOLDER, ancestor["id"]))
elif ancestor["type"] == "organization":
result.append(
(Project.AncestryType.ORGANIZATION, ancestor["id"])
)
elif ancestor["type"] == "project":
pass
else:
raise FormatException(
"The parent is neither a folder, an organization, "
f"nor a project: {ancestor["type"]}"
)
return result