Skip to content

Commit c5d1fa0

Browse files
authored
Merge pull request #503 from DrDroidLab/feature/argo-and-jira-integration
Feature/argo and jira integration
2 parents 899cfb4 + 2927eb5 commit c5d1fa0

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+2823
-71
lines changed
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import logging
2+
3+
from executor.source_processors.argocd_api_processor import ArgoCDAPIProcessor
4+
from connectors.assets.extractor.metadata_extractor import SourceMetadataExtractor
5+
from protos.base_pb2 import Source, SourceModelType
6+
from utils.logging_utils import log_function_call
7+
8+
logger = logging.getLogger(__name__)
9+
10+
11+
class ArgoCDSourceMetadataExtractor(SourceMetadataExtractor):
12+
13+
def __init__(self, argocd_server, argocd_token, account_id=None, connector_id=None):
14+
self.argocd_processor = ArgoCDAPIProcessor(argocd_server, argocd_token)
15+
super().__init__(account_id, connector_id, Source.ARGOCD)
16+
17+
@log_function_call
18+
def extract_applications(self, save_to_db=False):
19+
model_data = {}
20+
model_type = SourceModelType.ARGOCD_APPS
21+
try:
22+
applications = self.argocd_processor.get_deployment_info()
23+
if not applications:
24+
return model_data
25+
26+
for application in applications.get('items', []): # Iterate over application items
27+
try:
28+
app_name = application.get("metadata", {}).get("name", "") # Access the name safely
29+
path = application.get("spec", {}).get("source", {}).get("path", "") # Access the path safely
30+
if app_name:
31+
model_data[app_name] = {"name": app_name, "path": path}
32+
if save_to_db:
33+
self.create_or_update_model_metadata(model_type, app_name,
34+
model_data[app_name]) # Move inside loop
35+
except KeyError as e:
36+
logger.error(f"Missing key {e} in application: {application}") # Handle missing keys
37+
except Exception as e:
38+
logger.error(f'Error extracting ArgoCD applications: {e}')
39+
40+
return model_data
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import logging
2+
3+
from executor.source_processors.jira_api_processor import JiraApiProcessor
4+
from connectors.assets.extractor.metadata_extractor import SourceMetadataExtractor
5+
from protos.base_pb2 import Source, SourceModelType
6+
from utils.logging_utils import log_function_call
7+
8+
logger = logging.getLogger(__name__)
9+
10+
11+
class JiraSourceMetadataExtractor(SourceMetadataExtractor):
12+
13+
def __init__(self, jira_cloud_api_key, jira_domain, jira_email, account_id=None, connector_id=None):
14+
self.jira_processor = JiraApiProcessor(jira_cloud_api_key, jira_domain, jira_email)
15+
super().__init__(account_id, connector_id, Source.JIRA_CLOUD)
16+
17+
@log_function_call
18+
def extract_projects(self, save_to_db=False):
19+
model_data = {}
20+
model_type = SourceModelType.JIRA_PROJECT
21+
try:
22+
projects = self.jira_processor.list_all_projects()
23+
if not projects:
24+
return model_data
25+
for project in projects:
26+
model_data[project['key']] = project
27+
if save_to_db:
28+
self.create_or_update_model_metadata(model_type, project['key'], project)
29+
except Exception as e:
30+
logger.error(f'Error extracting Jira projects: {e}')
31+
return model_data
32+
33+
@log_function_call
34+
def extract_users(self, save_to_db=False):
35+
model_data = {}
36+
model_type = SourceModelType.JIRA_USER
37+
try:
38+
users = self.jira_processor.list_all_users()
39+
if not users:
40+
return model_data
41+
for user in users:
42+
if 'accountType' in user and user['accountType'] == 'atlassian':
43+
model_data[user['accountId']] = user
44+
if save_to_db:
45+
self.create_or_update_model_metadata(model_type, user['accountId'], user)
46+
except Exception as e:
47+
logger.error(f'Error extracting Jira users: {e}')
48+
return model_data

connectors/assets/extractor/metadata_extractor_facade.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
from connectors.assets.extractor.metadata_extractor import SourceMetadataExtractor
1111
from connectors.assets.extractor.mimir_metadata_extractor import MimirSourceMetadataExtractor
1212
from connectors.assets.extractor.newrelic_metadata_extractor import NewrelicSourceMetadataExtractor
13+
from connectors.assets.extractor.jira_metadata_extractor import JiraSourceMetadataExtractor
14+
from connectors.assets.extractor.argocd_metadata_extractor import ArgoCDSourceMetadataExtractor
1315
from protos.base_pb2 import Source
1416

1517

@@ -40,3 +42,5 @@ def get_connector_metadata_extractor_class(self, connector_type: Source):
4042
source_metadata_extractor_facade.register(Source.AZURE, AzureConnectorMetadataExtractor)
4143
source_metadata_extractor_facade.register(Source.GKE, GkeSourceMetadataExtractor)
4244
source_metadata_extractor_facade.register(Source.ELASTIC_SEARCH, ElasticSearchSourceMetadataExtractor)
45+
source_metadata_extractor_facade.register(Source.JIRA_CLOUD, JiraSourceMetadataExtractor)
46+
source_metadata_extractor_facade.register(Source.ARGOCD, ArgoCDSourceMetadataExtractor)
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
from datetime import datetime, timezone
2+
3+
from google.protobuf.wrappers_pb2 import UInt64Value, StringValue
4+
5+
from connectors.assets.manager.asset_manager import ConnectorAssetManager as SourceAssetManager
6+
from protos.connectors.assets.argocd_asset_pb2 import ArgoCDAppsAssetOptions, ArgoCDAppsAssetModel, \
7+
ArgoCDAssets, ArgoCDAssetModel
8+
from protos.connectors.connector_pb2 import Connector as ConnectorProto
9+
from protos.base_pb2 import Source, SourceModelType
10+
from protos.connectors.assets.asset_pb2 import ConnectorModelTypeOptions, AccountConnectorAssetsModelFilters, \
11+
AccountConnectorAssets
12+
13+
14+
class ArgoCDAssetManager(SourceAssetManager):
15+
16+
def __init__(self):
17+
# print("Inside ArgoCDAssetManager")
18+
self.source = Source.ARGOCD
19+
self.asset_type_callable_map = {
20+
SourceModelType.ARGOCD_APPS: {
21+
'options': self.get_apps_options,
22+
'values': self.get_apps_values,
23+
}
24+
}
25+
26+
@staticmethod
27+
def get_apps_options(apps_assets) -> ConnectorModelTypeOptions:
28+
all_apps = []
29+
for asset in apps_assets:
30+
all_apps.append(asset.metadata.get('name', asset.model_uid))
31+
apps_options = ArgoCDAppsAssetOptions(apps=all_apps)
32+
return ConnectorModelTypeOptions(model_type=SourceModelType.ARGOCD_APPS,
33+
argocd_apps_model_options=apps_options)
34+
35+
@staticmethod
36+
def get_apps_values(connector: ConnectorProto, filters: AccountConnectorAssetsModelFilters,
37+
apps_assets):
38+
which_one_of = filters.WhichOneof('filters')
39+
if which_one_of and which_one_of != 'argocd_apps_model_filters':
40+
raise ValueError(f"Invalid filter: {which_one_of}")
41+
42+
options: ArgoCDAppsAssetOptions = filters.argocd_apps_model_filters
43+
filter_apps = options.apps
44+
if filter_apps:
45+
apps_assets = apps_assets.filter(metadata__name__in=filter_apps)
46+
47+
argocd_asset_protos = []
48+
49+
for asset in apps_assets:
50+
key = asset.model_uid
51+
metadata = asset.metadata
52+
name = metadata.get("name", "")
53+
path = metadata.get("path", "")
54+
55+
argocd_asset_protos.append(ArgoCDAssetModel(
56+
id=UInt64Value(value=asset.id),
57+
connector_type=asset.connector_type,
58+
type=asset.model_type,
59+
last_updated=int(asset.updated_at.replace(tzinfo=timezone.utc).timestamp()) if (
60+
asset.updated_at) else None,
61+
argocd_apps=ArgoCDAppsAssetModel(name=StringValue(value=name),
62+
path=StringValue(value=path))))
63+
64+
return AccountConnectorAssets(argocd=ArgoCDAssets(assets=argocd_asset_protos))

connectors/assets/manager/asset_manager_facade.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
from connectors.assets.manager.postgres_assets_manager import PostgresAssetManager
1515
from connectors.assets.manager.bash_assets_manager import BashAssetManager
1616
from connectors.assets.manager.slack_assets_manager import SlackAssetManager
17+
from connectors.assets.manager.jira_asset_manager import JiraAssetManager
18+
from connectors.assets.manager.argocd_asset_manager import ArgoCDAssetManager
1719
from protos.connectors.assets.asset_pb2 import \
1820
AccountConnectorAssetsModelFilters as AccountConnectorAssetsModelFiltersProto
1921
from protos.base_pb2 import Source, SourceModelType
@@ -65,3 +67,5 @@ def get_asset_model_values(self, connector: ConnectorProto, model_type: SourceMo
6567
asset_manager_facade.register(Source.AZURE, AzureAssetManager())
6668
asset_manager_facade.register(Source.GKE, GkeAssetManager())
6769
asset_manager_facade.register(Source.ELASTIC_SEARCH, ElasticSearchAssetManager())
70+
asset_manager_facade.register(Source.JIRA_CLOUD, JiraAssetManager())
71+
asset_manager_facade.register(Source.ARGOCD, ArgoCDAssetManager())
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
from datetime import timezone
2+
3+
from google.protobuf.wrappers_pb2 import UInt64Value, StringValue
4+
5+
from connectors.assets.manager.asset_manager import ConnectorAssetManager as SourceAssetManager
6+
from protos.connectors.assets.jira_asset_pb2 import JiraProjectAssetOptions, JiraAssetModel, JiraProjectAssetModel, \
7+
JiraAssets, JiraUserAssetModel, JiraUserAssetOptions
8+
from protos.connectors.connector_pb2 import Connector as ConnectorProto
9+
from protos.base_pb2 import Source, SourceModelType
10+
from protos.connectors.assets.asset_pb2 import ConnectorModelTypeOptions, AccountConnectorAssetsModelFilters, \
11+
AccountConnectorAssets
12+
13+
14+
class JiraAssetManager(SourceAssetManager):
15+
16+
def __init__(self):
17+
self.source = Source.JIRA_CLOUD
18+
self.asset_type_callable_map = {
19+
SourceModelType.JIRA_PROJECT: {
20+
'options': self.get_project_options,
21+
'values': self.get_project_values,
22+
},
23+
SourceModelType.JIRA_USER: {
24+
'options': self.get_user_options,
25+
'values': self.get_user_values,
26+
}
27+
}
28+
29+
@staticmethod
30+
def get_project_options(project_assets) -> ConnectorModelTypeOptions:
31+
all_projects = []
32+
for asset in project_assets:
33+
all_projects.append(asset.metadata.get('name', asset.model_uid))
34+
project_options = JiraProjectAssetOptions(p=all_projects)
35+
return ConnectorModelTypeOptions(model_type=SourceModelType.JIRA_PROJECT,
36+
jira_project_model_options=project_options)
37+
38+
@staticmethod
39+
def get_project_values(connector: ConnectorProto, filters: AccountConnectorAssetsModelFilters,
40+
project_assets):
41+
which_one_of = filters.WhichOneof('filters')
42+
if which_one_of and which_one_of != 'jira_project_model_filters':
43+
raise ValueError(f"Invalid filter: {which_one_of}")
44+
options: JiraProjectAssetOptions = filters.jira_project_model_filters
45+
filter_project_names = options.names
46+
if filter_project_names:
47+
project_assets = project_assets.filter(metadata__name__in=filter_project_names)
48+
49+
jira_asset_protos = []
50+
for asset in project_assets:
51+
key = asset.model_uid
52+
metadata = asset.metadata
53+
project_id = metadata.get('id', None)
54+
name = metadata.get('name', '')
55+
self_url = metadata.get('self', '')
56+
57+
jira_asset_protos.append(JiraAssetModel(
58+
id=UInt64Value(value=asset.id),
59+
connector_type=asset.connector_type,
60+
type=asset.model_type,
61+
last_updated=int(asset.updated_at.replace(tzinfo=timezone.utc).timestamp()) if (
62+
asset.updated_at) else None,
63+
jira_project=JiraProjectAssetModel(id=StringValue(value=project_id), key=StringValue(value=key),
64+
name=StringValue(value=name), self_url=StringValue(value=self_url))))
65+
66+
return AccountConnectorAssets(jira_cloud=JiraAssets(assets=jira_asset_protos))
67+
68+
@staticmethod
69+
def get_user_options(user_assets) -> ConnectorModelTypeOptions:
70+
all_users = []
71+
for asset in user_assets:
72+
all_users.append(asset.metadata.get('displayName', asset.model_uid))
73+
user_options = JiraUserAssetOptions(display_names=all_users)
74+
return ConnectorModelTypeOptions(model_type=SourceModelType.JIRA_USER,
75+
jira_user_model_options=user_options)
76+
77+
@staticmethod
78+
def get_user_values(connector: ConnectorProto, filters: AccountConnectorAssetsModelFilters,
79+
user_assets):
80+
which_one_of = filters.WhichOneof('filters')
81+
if which_one_of and which_one_of != 'jira_user_model_filters':
82+
raise ValueError(f"Invalid filter: {which_one_of}")
83+
options: JiraUserAssetOptions = filters.jira_user_model_filters
84+
filter_users = options.display_names
85+
if filter_users:
86+
user_assets = user_assets.filter(metadata__displayName__in=filter_users)
87+
88+
jira_asset_protos = []
89+
for asset in user_assets:
90+
account_id = asset.model_uid
91+
metadata = asset.metadata
92+
display_name = metadata.get('displayName', '')
93+
self_url = metadata.get('self', '')
94+
95+
jira_asset_protos.append(JiraAssetModel(
96+
id=UInt64Value(value=asset.id),
97+
connector_type=asset.connector_type,
98+
type=asset.model_type,
99+
last_updated=int(asset.updated_at.replace(tzinfo=timezone.utc).timestamp()) if (
100+
asset.updated_at) else None,
101+
jira_user=JiraUserAssetModel(account_id=StringValue(value=account_id),
102+
display_name=StringValue(value=display_name),
103+
self_url=StringValue(value=self_url))))
104+
105+
return AccountConnectorAssets(jira_cloud=JiraAssets(assets=jira_asset_protos))

0 commit comments

Comments
 (0)