cartography 0.106.0rc2__py3-none-any.whl → 0.107.0rc1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of cartography might be problematic. Click here for more details.

Files changed (78) hide show
  1. cartography/_version.py +2 -2
  2. cartography/cli.py +131 -2
  3. cartography/config.py +42 -0
  4. cartography/driftdetect/cli.py +3 -2
  5. cartography/intel/airbyte/__init__.py +105 -0
  6. cartography/intel/airbyte/connections.py +120 -0
  7. cartography/intel/airbyte/destinations.py +81 -0
  8. cartography/intel/airbyte/organizations.py +59 -0
  9. cartography/intel/airbyte/sources.py +78 -0
  10. cartography/intel/airbyte/tags.py +64 -0
  11. cartography/intel/airbyte/users.py +106 -0
  12. cartography/intel/airbyte/util.py +122 -0
  13. cartography/intel/airbyte/workspaces.py +63 -0
  14. cartography/intel/aws/__init__.py +1 -0
  15. cartography/intel/aws/cloudtrail_management_events.py +364 -0
  16. cartography/intel/aws/codebuild.py +132 -0
  17. cartography/intel/aws/resources.py +4 -0
  18. cartography/intel/aws/sns.py +62 -2
  19. cartography/intel/entra/users.py +84 -42
  20. cartography/intel/scaleway/__init__.py +127 -0
  21. cartography/intel/scaleway/iam/__init__.py +0 -0
  22. cartography/intel/scaleway/iam/apikeys.py +71 -0
  23. cartography/intel/scaleway/iam/applications.py +71 -0
  24. cartography/intel/scaleway/iam/groups.py +71 -0
  25. cartography/intel/scaleway/iam/users.py +71 -0
  26. cartography/intel/scaleway/instances/__init__.py +0 -0
  27. cartography/intel/scaleway/instances/flexibleips.py +86 -0
  28. cartography/intel/scaleway/instances/instances.py +92 -0
  29. cartography/intel/scaleway/projects.py +79 -0
  30. cartography/intel/scaleway/storage/__init__.py +0 -0
  31. cartography/intel/scaleway/storage/snapshots.py +86 -0
  32. cartography/intel/scaleway/storage/volumes.py +84 -0
  33. cartography/intel/scaleway/utils.py +37 -0
  34. cartography/intel/sentinelone/__init__.py +63 -0
  35. cartography/intel/sentinelone/account.py +140 -0
  36. cartography/intel/sentinelone/agent.py +139 -0
  37. cartography/intel/sentinelone/api.py +113 -0
  38. cartography/intel/sentinelone/utils.py +9 -0
  39. cartography/models/airbyte/__init__.py +0 -0
  40. cartography/models/airbyte/connection.py +138 -0
  41. cartography/models/airbyte/destination.py +75 -0
  42. cartography/models/airbyte/organization.py +19 -0
  43. cartography/models/airbyte/source.py +75 -0
  44. cartography/models/airbyte/stream.py +74 -0
  45. cartography/models/airbyte/tag.py +69 -0
  46. cartography/models/airbyte/user.py +111 -0
  47. cartography/models/airbyte/workspace.py +46 -0
  48. cartography/models/aws/cloudtrail/management_events.py +64 -0
  49. cartography/models/aws/codebuild/__init__.py +0 -0
  50. cartography/models/aws/codebuild/project.py +49 -0
  51. cartography/models/aws/ecs/containers.py +19 -0
  52. cartography/models/aws/ecs/task_definitions.py +38 -0
  53. cartography/models/aws/sns/topic_subscription.py +74 -0
  54. cartography/models/entra/user.py +17 -51
  55. cartography/models/scaleway/__init__.py +0 -0
  56. cartography/models/scaleway/iam/__init__.py +0 -0
  57. cartography/models/scaleway/iam/apikey.py +96 -0
  58. cartography/models/scaleway/iam/application.py +52 -0
  59. cartography/models/scaleway/iam/group.py +95 -0
  60. cartography/models/scaleway/iam/user.py +60 -0
  61. cartography/models/scaleway/instance/__init__.py +0 -0
  62. cartography/models/scaleway/instance/flexibleip.py +52 -0
  63. cartography/models/scaleway/instance/instance.py +118 -0
  64. cartography/models/scaleway/organization.py +19 -0
  65. cartography/models/scaleway/project.py +48 -0
  66. cartography/models/scaleway/storage/__init__.py +0 -0
  67. cartography/models/scaleway/storage/snapshot.py +78 -0
  68. cartography/models/scaleway/storage/volume.py +51 -0
  69. cartography/models/sentinelone/__init__.py +1 -0
  70. cartography/models/sentinelone/account.py +40 -0
  71. cartography/models/sentinelone/agent.py +50 -0
  72. cartography/sync.py +11 -4
  73. {cartography-0.106.0rc2.dist-info → cartography-0.107.0rc1.dist-info}/METADATA +20 -16
  74. {cartography-0.106.0rc2.dist-info → cartography-0.107.0rc1.dist-info}/RECORD +78 -18
  75. {cartography-0.106.0rc2.dist-info → cartography-0.107.0rc1.dist-info}/WHEEL +0 -0
  76. {cartography-0.106.0rc2.dist-info → cartography-0.107.0rc1.dist-info}/entry_points.txt +0 -0
  77. {cartography-0.106.0rc2.dist-info → cartography-0.107.0rc1.dist-info}/licenses/LICENSE +0 -0
  78. {cartography-0.106.0rc2.dist-info → cartography-0.107.0rc1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,139 @@
1
+ import logging
2
+ from typing import Any
3
+
4
+ import neo4j
5
+
6
+ from cartography.client.core.tx import load
7
+ from cartography.graph.job import GraphJob
8
+ from cartography.intel.sentinelone.api import get_paginated_results
9
+ from cartography.models.sentinelone.agent import S1AgentSchema
10
+ from cartography.util import timeit
11
+
12
+ logger = logging.getLogger(__name__)
13
+
14
+
15
+ @timeit
16
+ def get_agents(api_url: str, api_token: str, account_id: str) -> list[dict[str, Any]]:
17
+ """
18
+ Get agent data from SentinelOne API
19
+ :param api_url: The SentinelOne API URL
20
+ :param api_token: The SentinelOne API token
21
+ :param account_id: The SentinelOne account ID
22
+ :return: Raw agent data from API
23
+ """
24
+ logger.info(f"Retrieving SentinelOne agent data for account {account_id}")
25
+
26
+ agents = get_paginated_results(
27
+ api_url=api_url,
28
+ endpoint="web/api/v2.1/agents",
29
+ api_token=api_token,
30
+ params={
31
+ "accountIds": account_id,
32
+ "limit": 1000,
33
+ },
34
+ )
35
+
36
+ logger.info(f"Retrieved {len(agents)} agents from SentinelOne account {account_id}")
37
+ return agents
38
+
39
+
40
+ @timeit
41
+ def transform_agents(agent_list: list[dict[str, Any]]) -> list[dict[str, Any]]:
42
+ """
43
+ Transform SentinelOne agent data for loading into Neo4j
44
+ :param agent_list: Raw agent data from the API
45
+ :return: Transformed agent data
46
+ """
47
+ result: list[dict[str, Any]] = []
48
+
49
+ for agent in agent_list:
50
+ transformed_agent = {
51
+ # Required fields - use direct access (will raise KeyError if missing)
52
+ "id": agent["id"],
53
+ # Optional fields - use .get() with None default
54
+ "uuid": agent.get("uuid"),
55
+ "computer_name": agent.get("computerName"),
56
+ "firewall_enabled": agent.get("firewallEnabled"),
57
+ "os_name": agent.get("osName"),
58
+ "os_revision": agent.get("osRevision"),
59
+ "domain": agent.get("domain"),
60
+ "last_active": agent.get("lastActiveDate"),
61
+ "last_successful_scan": agent.get("lastSuccessfulScanDate"),
62
+ "scan_status": agent.get("scanStatus"),
63
+ "serial_number": agent.get("serialNumber"),
64
+ }
65
+ result.append(transformed_agent)
66
+
67
+ return result
68
+
69
+
70
+ @timeit
71
+ def load_agents(
72
+ neo4j_session: neo4j.Session,
73
+ data: list[dict[str, Any]],
74
+ account_id: str,
75
+ update_tag: int,
76
+ ) -> None:
77
+ """
78
+ Load SentinelOne agent data into Neo4j
79
+ :param neo4j_session: Neo4j session
80
+ :param data: The transformed agent data
81
+ :param account_id: The SentinelOne account ID
82
+ :param update_tag: Update tag for tracking data freshness
83
+ :return: None
84
+ """
85
+ logger.info(
86
+ f"Loading {len(data)} SentinelOne agents into Neo4j for account {account_id}"
87
+ )
88
+ load(
89
+ neo4j_session,
90
+ S1AgentSchema(),
91
+ data,
92
+ lastupdated=update_tag,
93
+ S1_ACCOUNT_ID=account_id,
94
+ )
95
+
96
+
97
+ @timeit
98
+ def cleanup(
99
+ neo4j_session: neo4j.Session, common_job_parameters: dict[str, Any]
100
+ ) -> None:
101
+ """
102
+ Remove stale SentinelOne agent data from Neo4j
103
+ :param neo4j_session: Neo4j session
104
+ :param common_job_parameters: Common job parameters for cleanup
105
+ :return: None
106
+ """
107
+ logger.debug("Running S1Agent cleanup job")
108
+ GraphJob.from_node_schema(S1AgentSchema(), common_job_parameters).run(neo4j_session)
109
+
110
+
111
+ @timeit
112
+ def sync(
113
+ neo4j_session: neo4j.Session,
114
+ common_job_parameters: dict[str, Any],
115
+ ) -> None:
116
+ """
117
+ Sync SentinelOne agents using the standard sync pattern
118
+ :param neo4j_session: Neo4j session
119
+ :param common_job_parameters: Common job parameters containing API_URL, API_TOKEN, S1_ACCOUNT_ID, UPDATE_TAG
120
+ :return: None
121
+ """
122
+ api_url = common_job_parameters["API_URL"]
123
+ api_token = common_job_parameters["API_TOKEN"]
124
+ account_id = common_job_parameters["S1_ACCOUNT_ID"]
125
+ update_tag = common_job_parameters["UPDATE_TAG"]
126
+
127
+ logger.info(f"Syncing SentinelOne agent data for account {account_id}")
128
+
129
+ # 1. GET - Fetch data from API
130
+ agents_raw_data = get_agents(api_url, api_token, account_id)
131
+
132
+ # 2. TRANSFORM - Shape data for ingestion
133
+ transformed_data = transform_agents(agents_raw_data)
134
+
135
+ # 3. LOAD - Ingest to Neo4j using data model
136
+ load_agents(neo4j_session, transformed_data, account_id, update_tag)
137
+
138
+ # 4. CLEANUP - Remove stale data
139
+ cleanup(neo4j_session, common_job_parameters)
@@ -0,0 +1,113 @@
1
+ import logging
2
+ from typing import Any
3
+
4
+ import requests
5
+
6
+ from cartography.util import timeit
7
+
8
+ logger = logging.getLogger(__name__)
9
+ # Connect and read timeouts of 60 seconds each
10
+ _TIMEOUT = (60, 60)
11
+
12
+
13
+ @timeit
14
+ def call_sentinelone_api(
15
+ api_url: str,
16
+ endpoint: str,
17
+ api_token: str,
18
+ method: str = "GET",
19
+ params: dict[str, Any] | None = None,
20
+ data: dict[str, Any] | None = None,
21
+ ) -> dict[str, Any]:
22
+ """
23
+ Call the SentinelOne API
24
+ :param api_url: The base URL for the SentinelOne API
25
+ :param endpoint: The API endpoint to call
26
+ :param api_token: The API token for authentication
27
+ :param method: The HTTP method to use (default is GET)
28
+ :param params: Query parameters to include in the request
29
+ :param data: Data to include in the request body for POST/PUT methods
30
+ :return: The JSON response from the API
31
+ """
32
+ full_url = f"{api_url.rstrip('/')}/{endpoint.lstrip('/')}"
33
+
34
+ headers = {
35
+ "Accept": "application/json",
36
+ "Authorization": f"ApiToken {api_token}",
37
+ "Content-Type": "application/json",
38
+ }
39
+
40
+ try:
41
+ logger.debug(
42
+ "SentinelOne: %s %s",
43
+ method,
44
+ full_url,
45
+ )
46
+
47
+ response = requests.request(
48
+ method=method,
49
+ url=full_url,
50
+ headers=headers,
51
+ params=params,
52
+ json=data,
53
+ timeout=_TIMEOUT,
54
+ )
55
+
56
+ # Raise an exception for HTTP errors
57
+ response.raise_for_status()
58
+
59
+ except requests.exceptions.Timeout:
60
+ logger.warning(f"SentinelOne: Request to '{full_url}' timed out.")
61
+ raise
62
+ except requests.exceptions.RequestException as e:
63
+ logger.error(f"SentinelOne API request failed: {e}")
64
+ raise
65
+
66
+ return response.json()
67
+
68
+
69
+ def get_paginated_results(
70
+ api_url: str,
71
+ endpoint: str,
72
+ api_token: str,
73
+ params: dict[str, Any] | None = None,
74
+ ) -> list[dict[str, Any]]:
75
+ """
76
+ Handle cursor-based pagination for SentinelOne API requests
77
+ :param api_url: The base URL for the SentinelOne API
78
+ :param endpoint: The API endpoint to call
79
+ :param api_token: The API token for authentication
80
+ :param params: Query parameters to include in the request
81
+ :return: A list of all items from all pages
82
+ """
83
+ query_params = params or {}
84
+
85
+ # Set default pagination parameters if not provided
86
+ if "limit" not in query_params:
87
+ query_params["limit"] = 100
88
+
89
+ next_cursor = None
90
+ total_items = []
91
+
92
+ while True:
93
+ if next_cursor:
94
+ query_params["cursor"] = next_cursor
95
+
96
+ response = call_sentinelone_api(
97
+ api_url=api_url,
98
+ endpoint=endpoint,
99
+ api_token=api_token,
100
+ params=query_params,
101
+ )
102
+
103
+ items = response.get("data", [])
104
+ if not items:
105
+ break
106
+
107
+ total_items.extend(items)
108
+ pagination = response.get("pagination", {})
109
+ next_cursor = pagination.get("nextCursor")
110
+ if not next_cursor:
111
+ break
112
+
113
+ return total_items
@@ -0,0 +1,9 @@
1
+ import re
2
+
3
+
4
+ def get_application_id(name: str, vendor: str) -> str:
5
+ name_normalized = name.strip().lower().replace(" ", "_")
6
+ vendor_normalized = vendor.strip().lower().replace(" ", "_")
7
+ name_normalized = re.sub(r"[^\w]", "", name_normalized)
8
+ vendor_normalized = re.sub(r"[^\w\s]", "", vendor_normalized)
9
+ return f"{vendor_normalized}:{name_normalized}"
File without changes
@@ -0,0 +1,138 @@
1
+ from dataclasses import dataclass
2
+
3
+ from cartography.models.core.common import PropertyRef
4
+ from cartography.models.core.nodes import CartographyNodeProperties
5
+ from cartography.models.core.nodes import CartographyNodeSchema
6
+ from cartography.models.core.relationships import CartographyRelProperties
7
+ from cartography.models.core.relationships import CartographyRelSchema
8
+ from cartography.models.core.relationships import LinkDirection
9
+ from cartography.models.core.relationships import make_target_node_matcher
10
+ from cartography.models.core.relationships import OtherRelationships
11
+ from cartography.models.core.relationships import TargetNodeMatcher
12
+
13
+
14
+ @dataclass(frozen=True)
15
+ class AirbyteConnectionNodeProperties(CartographyNodeProperties):
16
+ id: PropertyRef = PropertyRef("connectionId")
17
+ name: PropertyRef = PropertyRef("name")
18
+ namespace_format: PropertyRef = PropertyRef("namespaceFormat")
19
+ prefix: PropertyRef = PropertyRef("prefix")
20
+ status: PropertyRef = PropertyRef("status")
21
+ data_residency: PropertyRef = PropertyRef("dataResidency")
22
+ non_breaking_schema_updates_behavior: PropertyRef = PropertyRef(
23
+ "nonBreakingSchemaUpdatesBehavior"
24
+ )
25
+ namespace_definition: PropertyRef = PropertyRef("namespaceDefinition")
26
+ lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
27
+
28
+
29
+ @dataclass(frozen=True)
30
+ class AirbyteConnectionToOrganizationRelProperties(CartographyRelProperties):
31
+ lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
32
+
33
+
34
+ @dataclass(frozen=True)
35
+ # (:AirbyteOrganization)-[:RESOURCE]->(:AirbyteConnection)
36
+ class AirbyteConnectionToOrganizationRel(CartographyRelSchema):
37
+ target_node_label: str = "AirbyteOrganization"
38
+ target_node_matcher: TargetNodeMatcher = make_target_node_matcher(
39
+ {"id": PropertyRef("ORG_ID", set_in_kwargs=True)},
40
+ )
41
+ direction: LinkDirection = LinkDirection.INWARD
42
+ rel_label: str = "RESOURCE"
43
+ properties: AirbyteConnectionToOrganizationRelProperties = (
44
+ AirbyteConnectionToOrganizationRelProperties()
45
+ )
46
+
47
+
48
+ @dataclass(frozen=True)
49
+ class AirbyteConnectionToWorkspaceRelProperties(CartographyRelProperties):
50
+ lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
51
+
52
+
53
+ @dataclass(frozen=True)
54
+ # (:AirbyteWorkspace)-[:CONTAINS]->(:AirbyteConnection)
55
+ class AirbyteConnectionToWorkspaceRel(CartographyRelSchema):
56
+ target_node_label: str = "AirbyteWorkspace"
57
+ target_node_matcher: TargetNodeMatcher = make_target_node_matcher(
58
+ {"id": PropertyRef("workspaceId")},
59
+ )
60
+ direction: LinkDirection = LinkDirection.INWARD
61
+ rel_label: str = "CONTAINS"
62
+ properties: AirbyteConnectionToWorkspaceRelProperties = (
63
+ AirbyteConnectionToWorkspaceRelProperties()
64
+ )
65
+
66
+
67
+ @dataclass(frozen=True)
68
+ class AirbyteConnectionToSourceRelProperties(CartographyRelProperties):
69
+ lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
70
+
71
+
72
+ @dataclass(frozen=True)
73
+ # (:AirbyteSource)<-[:SYNC_FROM]-(:AirbyteConnection)
74
+ class AirbyteConnectionToSourceRel(CartographyRelSchema):
75
+ target_node_label: str = "AirbyteSource"
76
+ target_node_matcher: TargetNodeMatcher = make_target_node_matcher(
77
+ {"id": PropertyRef("sourceId")},
78
+ )
79
+ direction: LinkDirection = LinkDirection.OUTWARD
80
+ rel_label: str = "SYNC_FROM"
81
+ properties: AirbyteConnectionToSourceRelProperties = (
82
+ AirbyteConnectionToSourceRelProperties()
83
+ )
84
+
85
+
86
+ @dataclass(frozen=True)
87
+ class AirbyteConnectionToDestinationRelProperties(CartographyRelProperties):
88
+ lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
89
+
90
+
91
+ @dataclass(frozen=True)
92
+ # (:AirbyteDestination)<-[:SYNC_TO]-(:AirbyteConnection)
93
+ class AirbyteConnectionToDestinationRel(CartographyRelSchema):
94
+ target_node_label: str = "AirbyteDestination"
95
+ target_node_matcher: TargetNodeMatcher = make_target_node_matcher(
96
+ {"id": PropertyRef("destinationId")},
97
+ )
98
+ direction: LinkDirection = LinkDirection.OUTWARD
99
+ rel_label: str = "SYNC_TO"
100
+ properties: AirbyteConnectionToDestinationRelProperties = (
101
+ AirbyteConnectionToDestinationRelProperties()
102
+ )
103
+
104
+
105
+ @dataclass(frozen=True)
106
+ class AirbyteConnectionToTagRelProperties(CartographyRelProperties):
107
+ lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
108
+
109
+
110
+ @dataclass(frozen=True)
111
+ # (:AirbyteTag)<-[:TAGGED]-(:AirbyteConnection)
112
+ class AirbyteConnectionToTagRel(CartographyRelSchema):
113
+ target_node_label: str = "AirbyteTag"
114
+ target_node_matcher: TargetNodeMatcher = make_target_node_matcher(
115
+ {"id": PropertyRef("tags_ids", one_to_many=True)},
116
+ )
117
+ direction: LinkDirection = LinkDirection.OUTWARD
118
+ rel_label: str = "TAGGED"
119
+ properties: AirbyteConnectionToTagRelProperties = (
120
+ AirbyteConnectionToTagRelProperties()
121
+ )
122
+
123
+
124
+ @dataclass(frozen=True)
125
+ class AirbyteConnectionSchema(CartographyNodeSchema):
126
+ label: str = "AirbyteConnection"
127
+ properties: AirbyteConnectionNodeProperties = AirbyteConnectionNodeProperties()
128
+ sub_resource_relationship: AirbyteConnectionToOrganizationRel = (
129
+ AirbyteConnectionToOrganizationRel()
130
+ )
131
+ other_relationships: OtherRelationships = OtherRelationships(
132
+ [
133
+ AirbyteConnectionToWorkspaceRel(),
134
+ AirbyteConnectionToSourceRel(),
135
+ AirbyteConnectionToDestinationRel(),
136
+ AirbyteConnectionToTagRel(),
137
+ ]
138
+ )
@@ -0,0 +1,75 @@
1
+ from dataclasses import dataclass
2
+
3
+ from cartography.models.core.common import PropertyRef
4
+ from cartography.models.core.nodes import CartographyNodeProperties
5
+ from cartography.models.core.nodes import CartographyNodeSchema
6
+ from cartography.models.core.relationships import CartographyRelProperties
7
+ from cartography.models.core.relationships import CartographyRelSchema
8
+ from cartography.models.core.relationships import LinkDirection
9
+ from cartography.models.core.relationships import make_target_node_matcher
10
+ from cartography.models.core.relationships import OtherRelationships
11
+ from cartography.models.core.relationships import TargetNodeMatcher
12
+
13
+
14
+ @dataclass(frozen=True)
15
+ class AirbyteDestinationNodeProperties(CartographyNodeProperties):
16
+ id: PropertyRef = PropertyRef("destinationId")
17
+ name: PropertyRef = PropertyRef("name")
18
+ type: PropertyRef = PropertyRef("destinationType")
19
+ config_host: PropertyRef = PropertyRef("configuration.host")
20
+ config_port: PropertyRef = PropertyRef("configuration.port")
21
+ config_name: PropertyRef = PropertyRef("configuration.name")
22
+ config_region: PropertyRef = PropertyRef("configuration.region")
23
+ config_endpoint: PropertyRef = PropertyRef("configuration.endpoint")
24
+ config_account: PropertyRef = PropertyRef("configuration.account")
25
+ lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
26
+
27
+
28
+ @dataclass(frozen=True)
29
+ class AirbyteDestinationToOrganizationRelProperties(CartographyRelProperties):
30
+ lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
31
+
32
+
33
+ @dataclass(frozen=True)
34
+ # (:AirbyteOrganization)-[:RESOURCE]->(:AirbyteDestination)
35
+ class AirbyteDestinationToOrganizationRel(CartographyRelSchema):
36
+ target_node_label: str = "AirbyteOrganization"
37
+ target_node_matcher: TargetNodeMatcher = make_target_node_matcher(
38
+ {"id": PropertyRef("ORG_ID", set_in_kwargs=True)},
39
+ )
40
+ direction: LinkDirection = LinkDirection.INWARD
41
+ rel_label: str = "RESOURCE"
42
+ properties: AirbyteDestinationToOrganizationRelProperties = (
43
+ AirbyteDestinationToOrganizationRelProperties()
44
+ )
45
+
46
+
47
+ @dataclass(frozen=True)
48
+ class AirbyteDestinationToWorkspaceRelProperties(CartographyRelProperties):
49
+ lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
50
+
51
+
52
+ @dataclass(frozen=True)
53
+ # (:AirbyteWorkspace)-[:CONTAINS]->(:AirbyteDestination)
54
+ class AirbyteDestinationToWorkspaceRel(CartographyRelSchema):
55
+ target_node_label: str = "AirbyteWorkspace"
56
+ target_node_matcher: TargetNodeMatcher = make_target_node_matcher(
57
+ {"id": PropertyRef("workspaceId")},
58
+ )
59
+ direction: LinkDirection = LinkDirection.INWARD
60
+ rel_label: str = "CONTAINS"
61
+ properties: AirbyteDestinationToWorkspaceRelProperties = (
62
+ AirbyteDestinationToWorkspaceRelProperties()
63
+ )
64
+
65
+
66
+ @dataclass(frozen=True)
67
+ class AirbyteDestinationSchema(CartographyNodeSchema):
68
+ label: str = "AirbyteDestination"
69
+ properties: AirbyteDestinationNodeProperties = AirbyteDestinationNodeProperties()
70
+ sub_resource_relationship: AirbyteDestinationToOrganizationRel = (
71
+ AirbyteDestinationToOrganizationRel()
72
+ )
73
+ other_relationships: OtherRelationships = OtherRelationships(
74
+ [AirbyteDestinationToWorkspaceRel()]
75
+ )
@@ -0,0 +1,19 @@
1
+ from dataclasses import dataclass
2
+
3
+ from cartography.models.core.common import PropertyRef
4
+ from cartography.models.core.nodes import CartographyNodeProperties
5
+ from cartography.models.core.nodes import CartographyNodeSchema
6
+
7
+
8
+ @dataclass(frozen=True)
9
+ class AirbyteOrganizationNodeProperties(CartographyNodeProperties):
10
+ id: PropertyRef = PropertyRef("organizationId")
11
+ name: PropertyRef = PropertyRef("organizationName")
12
+ email: PropertyRef = PropertyRef("email")
13
+ lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
14
+
15
+
16
+ @dataclass(frozen=True)
17
+ class AirbyteOrganizationSchema(CartographyNodeSchema):
18
+ label: str = "AirbyteOrganization"
19
+ properties: AirbyteOrganizationNodeProperties = AirbyteOrganizationNodeProperties()
@@ -0,0 +1,75 @@
1
+ from dataclasses import dataclass
2
+
3
+ from cartography.models.core.common import PropertyRef
4
+ from cartography.models.core.nodes import CartographyNodeProperties
5
+ from cartography.models.core.nodes import CartographyNodeSchema
6
+ from cartography.models.core.relationships import CartographyRelProperties
7
+ from cartography.models.core.relationships import CartographyRelSchema
8
+ from cartography.models.core.relationships import LinkDirection
9
+ from cartography.models.core.relationships import make_target_node_matcher
10
+ from cartography.models.core.relationships import OtherRelationships
11
+ from cartography.models.core.relationships import TargetNodeMatcher
12
+
13
+
14
+ @dataclass(frozen=True)
15
+ class AirbyteSourceNodeProperties(CartographyNodeProperties):
16
+ id: PropertyRef = PropertyRef("sourceId")
17
+ lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
18
+ name: PropertyRef = PropertyRef("name")
19
+ type: PropertyRef = PropertyRef("sourceType")
20
+ config_host: PropertyRef = PropertyRef("configuration.host")
21
+ config_port: PropertyRef = PropertyRef("configuration.port")
22
+ config_name: PropertyRef = PropertyRef("configuration.name")
23
+ config_region: PropertyRef = PropertyRef("configuration.region")
24
+ config_endpoint: PropertyRef = PropertyRef("configuration.endpoint")
25
+ config_account: PropertyRef = PropertyRef("configuration.account")
26
+
27
+
28
+ @dataclass(frozen=True)
29
+ class AirbyteSourceToOrganizationRelProperties(CartographyRelProperties):
30
+ lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
31
+
32
+
33
+ @dataclass(frozen=True)
34
+ # (:AirbyteOrganization)-[:RESOURCE]->(:AirbyteSource)
35
+ class AirbyteSourceToOrganizationRel(CartographyRelSchema):
36
+ target_node_label: str = "AirbyteOrganization"
37
+ target_node_matcher: TargetNodeMatcher = make_target_node_matcher(
38
+ {"id": PropertyRef("ORG_ID", set_in_kwargs=True)},
39
+ )
40
+ direction: LinkDirection = LinkDirection.INWARD
41
+ rel_label: str = "RESOURCE"
42
+ properties: AirbyteSourceToOrganizationRelProperties = (
43
+ AirbyteSourceToOrganizationRelProperties()
44
+ )
45
+
46
+
47
+ @dataclass(frozen=True)
48
+ class AirbyteSourceToWorkspaceRelProperties(CartographyRelProperties):
49
+ lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
50
+
51
+
52
+ @dataclass(frozen=True)
53
+ # (:AirbyteWorkspace)-[:CONTAINS]->(:AirbyteSource)
54
+ class AirbyteSourceToWorkspaceRel(CartographyRelSchema):
55
+ target_node_label: str = "AirbyteWorkspace"
56
+ target_node_matcher: TargetNodeMatcher = make_target_node_matcher(
57
+ {"id": PropertyRef("workspaceId")},
58
+ )
59
+ direction: LinkDirection = LinkDirection.INWARD
60
+ rel_label: str = "CONTAINS"
61
+ properties: AirbyteSourceToWorkspaceRelProperties = (
62
+ AirbyteSourceToWorkspaceRelProperties()
63
+ )
64
+
65
+
66
+ @dataclass(frozen=True)
67
+ class AirbyteSourceSchema(CartographyNodeSchema):
68
+ label: str = "AirbyteSource"
69
+ properties: AirbyteSourceNodeProperties = AirbyteSourceNodeProperties()
70
+ sub_resource_relationship: AirbyteSourceToOrganizationRel = (
71
+ AirbyteSourceToOrganizationRel()
72
+ )
73
+ other_relationships: OtherRelationships = OtherRelationships(
74
+ [AirbyteSourceToWorkspaceRel()]
75
+ )
@@ -0,0 +1,74 @@
1
+ from dataclasses import dataclass
2
+
3
+ from cartography.models.core.common import PropertyRef
4
+ from cartography.models.core.nodes import CartographyNodeProperties
5
+ from cartography.models.core.nodes import CartographyNodeSchema
6
+ from cartography.models.core.relationships import CartographyRelProperties
7
+ from cartography.models.core.relationships import CartographyRelSchema
8
+ from cartography.models.core.relationships import LinkDirection
9
+ from cartography.models.core.relationships import make_target_node_matcher
10
+ from cartography.models.core.relationships import OtherRelationships
11
+ from cartography.models.core.relationships import TargetNodeMatcher
12
+
13
+
14
+ @dataclass(frozen=True)
15
+ class AirbyteStreamNodeProperties(CartographyNodeProperties):
16
+ id: PropertyRef = PropertyRef("streamId")
17
+ lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
18
+ name: PropertyRef = PropertyRef("name")
19
+ sync_mode: PropertyRef = PropertyRef("syncMode")
20
+ cursor_field: PropertyRef = PropertyRef("cursorField")
21
+ primary_key: PropertyRef = PropertyRef("primaryKey")
22
+ include_files: PropertyRef = PropertyRef("includeFiles")
23
+ selected_fields: PropertyRef = PropertyRef("selectedFields")
24
+ mappers: PropertyRef = PropertyRef("mappers")
25
+
26
+
27
+ @dataclass(frozen=True)
28
+ class AirbyteStreamToOrganizationRelProperties(CartographyRelProperties):
29
+ lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
30
+
31
+
32
+ @dataclass(frozen=True)
33
+ # (:AirbyteOrganization)-[:RESOURCE]->(:AirbyteStream)
34
+ class AirbyteStreamToOrganizationRel(CartographyRelSchema):
35
+ target_node_label: str = "AirbyteOrganization"
36
+ target_node_matcher: TargetNodeMatcher = make_target_node_matcher(
37
+ {"id": PropertyRef("ORG_ID", set_in_kwargs=True)},
38
+ )
39
+ direction: LinkDirection = LinkDirection.INWARD
40
+ rel_label: str = "RESOURCE"
41
+ properties: AirbyteStreamToOrganizationRelProperties = (
42
+ AirbyteStreamToOrganizationRelProperties()
43
+ )
44
+
45
+
46
+ @dataclass(frozen=True)
47
+ class AirbyteStreamToConnectionRelProperties(CartographyRelProperties):
48
+ lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
49
+
50
+
51
+ @dataclass(frozen=True)
52
+ # (:AirbyteConnection)-[:HAS]->(:AirbyteStream)
53
+ class AirbyteStreamToConnectionRel(CartographyRelSchema):
54
+ target_node_label: str = "AirbyteConnection"
55
+ target_node_matcher: TargetNodeMatcher = make_target_node_matcher(
56
+ {"id": PropertyRef("connectionId")},
57
+ )
58
+ direction: LinkDirection = LinkDirection.INWARD
59
+ rel_label: str = "HAS"
60
+ properties: AirbyteStreamToOrganizationRelProperties = (
61
+ AirbyteStreamToOrganizationRelProperties()
62
+ )
63
+
64
+
65
+ @dataclass(frozen=True)
66
+ class AirbyteStreamSchema(CartographyNodeSchema):
67
+ label: str = "AirbyteStream"
68
+ properties: AirbyteStreamNodeProperties = AirbyteStreamNodeProperties()
69
+ sub_resource_relationship: AirbyteStreamToOrganizationRel = (
70
+ AirbyteStreamToOrganizationRel()
71
+ )
72
+ other_relationships: OtherRelationships = OtherRelationships(
73
+ [AirbyteStreamToConnectionRel()]
74
+ )