Skip to content

Commit

Permalink
Merge branch 'main' into APP-5019
Browse files Browse the repository at this point in the history
  • Loading branch information
Aryamanz29 authored Feb 3, 2025
2 parents c11a225 + e39cac0 commit 099b973
Show file tree
Hide file tree
Showing 71 changed files with 2,811 additions and 169 deletions.
12 changes: 9 additions & 3 deletions .github/workflows/pyatlan-pr.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [3.8, 3.9]
# Specify version as a string
# https://github.com/actions/setup-python/issues/160"
python-version: ["3.8", "3.12", "3.13"]

steps:
- name: Checkout code
Expand Down Expand Up @@ -39,7 +41,9 @@ jobs:
files: ${{ steps.distribute-integration-test-files.outputs.files }}
strategy:
matrix:
python-version: [3.8, 3.9]
# Specify version as a string
# https://github.com/actions/setup-python/issues/160"
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"]

steps:
- name: Checkout code
Expand Down Expand Up @@ -94,7 +98,9 @@ jobs:
- name: Set up Python 3.9
uses: actions/setup-python@v5
with:
python-version: 3.9
# Specify version as a string
# https://github.com/actions/setup-python/issues/160"
python-version: "3.9"

- name: Install dependencies
run: |
Expand Down
25 changes: 25 additions & 0 deletions HISTORY.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,28 @@
## 4.1.0 (January 28, 2025)

### New Features

- Added support for the following workflow packages:

**Connectors**
- OracleCrawler

**Utils**
- LineageBuilder
- LineageGenerator
- APITokenConnectionAdmin

- Extended the `WorkflowClient.run()` method to accept raw workflow `JSON` strings.
- Added support for creating `OpenLineage` connections via `OpenLineageClient.create_connection()`.

### Bug Fixes

- Updated the maximum API token Time-to-Expire (TTE) to `5` years. Previously, the value was set to `13` years (`409,968,000` seconds), which was reverted to `5` years due to an integer overflow issue in Keycloak. For more details, see [Keycloak Issue #19671](https://github.com/keycloak/keycloak/issues/19671).

### QOL Improvements

- Increased the default read timeout for `AtlanClient` to `900` seconds (`15` minutes).

## 4.0.2 (January 22, 2025)

### New Features
Expand Down
10 changes: 10 additions & 0 deletions docs/asset/applicationfield.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
.. _applicationfield:

ApplicationField
================

.. module:: pyatlan.model.assets
:no-index:

.. autoclass:: ApplicationField
:members:
1 change: 1 addition & 0 deletions docs/assets.rst
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ You can interact with all of the following different kinds of assets:
asset/anomalocheck
asset/app
asset/application
asset/applicationfield
asset/asset
asset/atlasglossary
asset/atlasglossarycategory
Expand Down
2 changes: 1 addition & 1 deletion pyatlan/cache/abstract_asset_cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ def _get_by_qualified_name(self, qualified_name: str, allow_refresh: bool = True
qualified_name,
AtlanConnectorType._get_connector_type_from_qualified_name(
qualified_name
),
).value,
)
return self._get_by_guid(guid=guid, allow_refresh=False)

Expand Down
4 changes: 2 additions & 2 deletions pyatlan/client/atlan.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,8 +144,8 @@ class AtlanClient(BaseSettings):
_default_client: "ClassVar[Optional[AtlanClient]]" = None
base_url: Union[Literal["INTERNAL"], HttpUrl]
api_key: str
connect_timeout: float = 30.0
read_timeout: float = 120.0
connect_timeout: float = 30.0 # 30 secs
read_timeout: float = 900.0 # 15 mins
retry: Retry = DEFAULT_RETRY
_session: requests.Session = PrivateAttr(default_factory=get_session)
_request_params: dict = PrivateAttr()
Expand Down
2 changes: 1 addition & 1 deletion pyatlan/client/open_lineage.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,6 @@ def send(
)
):
raise ErrorCode.OPENLINEAGE_NOT_CONFIGURED.exception_with_parameters(
connector_type
connector_type.value
) from e
raise e
25 changes: 22 additions & 3 deletions pyatlan/client/workflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ def _handle_workflow_types(self, workflow):
detail = results[0].source
else:
raise ErrorCode.NO_PRIOR_RUN_AVAILABLE.exception_with_parameters(
workflow
workflow.value
)
elif isinstance(workflow, WorkflowSearchResult):
detail = workflow.source
Expand Down Expand Up @@ -323,9 +323,20 @@ def rerun(
)
return WorkflowRunResponse(**raw_json)

@validate_arguments
@overload
def run(
self, workflow: Workflow, workflow_schedule: Optional[WorkflowSchedule] = None
) -> WorkflowResponse: ...

@overload
def run(
self, workflow: str, workflow_schedule: Optional[WorkflowSchedule] = None
) -> WorkflowResponse: ...

def run(
self,
workflow: Union[Workflow, str],
workflow_schedule: Optional[WorkflowSchedule] = None,
) -> WorkflowResponse:
"""
Run the Atlan workflow with a specific configuration.
Expand All @@ -335,7 +346,7 @@ def run(
Running the workflow multiple times with the same configuration may lead to duplicate assets.
Consider using the "rerun()" method instead to re-execute an existing workflow.
:param workflow: The workflow to run.
:param workflow: workflow object to run or a raw workflow JSON string.
:param workflow_schedule: (Optional) a WorkflowSchedule object containing:
- A cron schedule expression, e.g: `5 4 * * *`.
- The time zone for the cron schedule, e.g: `Europe/Paris`.
Expand All @@ -344,6 +355,14 @@ def run(
:raises ValidationError: If the provided `workflow` is invalid.
:raises AtlanError: on any API communication issue.
"""
validate_type(name="workflow", _type=(Workflow, str), value=workflow)
validate_type(
name="workflow_schedule",
_type=(WorkflowSchedule, None),
value=workflow_schedule,
)
if isinstance(workflow, str):
workflow = Workflow.parse_raw(workflow)
if workflow_schedule:
self._add_schedule(workflow, workflow_schedule)
raw_json = self._client._call_api(
Expand Down
1 change: 1 addition & 0 deletions pyatlan/generator/templates/imports.jinja2
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ from pyatlan.model.structs import (
AwsTag,
AzureTag,
BadgeCondition,
BusinessPolicyRule,
ColumnValueFrequencyMap,
DbtMetricFilter,
GoogleLabel,
Expand Down
38 changes: 38 additions & 0 deletions pyatlan/generator/templates/methods/asset/application_field.jinja2
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@

@overload
@classmethod
def creator(
cls,
*,
name: str,
application_qualified_name: str,
) -> ApplicationField: ...

@overload
@classmethod
def creator(
cls,
*,
name: str,
application_qualified_name: str,
connection_qualified_name: str,
) -> ApplicationField: ...

@classmethod
@init_guid
def creator(
cls,
*,
name: str,
application_qualified_name: str,
connection_qualified_name: Optional[str] = None,
) -> ApplicationField:
validate_required_fields(
["name", "application_qualified_name"], [name, application_qualified_name]
)
attributes = ApplicationField.Attributes.create(
name=name,
application_qualified_name=application_qualified_name,
connection_qualified_name=connection_qualified_name,
)
return cls(attributes=attributes)
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@

@classmethod
@init_guid
def create(
cls,
*,
name: str,
application_qualified_name: str,
connection_qualified_name: Optional[str] = None,
) -> ApplicationField.Attributes:
validate_required_fields(
["name", "application_qualified_name"],
[name, application_qualified_name],
)
if connection_qualified_name:
connector_name = AtlanConnectorType.get_connector_name(
connection_qualified_name
)
else:
connection_qn, connector_name = AtlanConnectorType.get_connector_name(
application_qualified_name, "application_qualified_name", 4
)

return ApplicationField.Attributes(
name=name,
qualified_name=f"{application_qualified_name}/{name}",
connection_qualified_name=connection_qualified_name or connection_qn,
connector_name=connector_name,
application_parent_qualified_name=application_qualified_name,
application_parent=Application.ref_by_qualified_name(application_qualified_name),
)
14 changes: 10 additions & 4 deletions pyatlan/model/api_tokens.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,10 @@ def copy_values(cls, values):


class ApiTokenRequest(AtlanObject):
# The value was previously set to 13 years (409968000 secs).
# It has been reverted to 5 years due to an integer overflow issue in Keycloak.
# https://github.com/keycloak/keycloak/issues/19671
_MAX_VALIDITY: int = 157680000 # 5 years in seconds
display_name: Optional[str] = Field(
default=None,
description="Human-readable name provided when creating the token.",
Expand All @@ -151,11 +155,13 @@ class ApiTokenRequest(AtlanObject):

@root_validator(pre=True)
def set_max_validity(cls, values):
if "validitySeconds" in values and values["validitySeconds"]:
if values["validitySeconds"] < 0:
values["validitySeconds"] = 409968000
if "validity_seconds" in values and values["validity_seconds"]:
if values["validity_seconds"] < 0:
values["validity_seconds"] = cls._MAX_VALIDITY
else:
values["validitySeconds"] = min(values["validitySeconds"], 409968000)
values["validity_seconds"] = min(
values["validity_seconds"], cls._MAX_VALIDITY
)
if "personas" in values and not values["personas"]:
values["personas"] = set()
return values
Expand Down
3 changes: 2 additions & 1 deletion pyatlan/model/assets/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
"Stakeholder",
"AirflowDag",
"AirflowTask",
"ApplicationField",
"Application",
"AdfDataflow",
"AdfDataset",
Expand Down Expand Up @@ -66,8 +67,8 @@
"Column",
"DatabricksUnityCatalogTag",
"SnowflakeStream",
"CalculationView",
"Database",
"CalculationView",
"Procedure",
"SnowflakeTag",
"CosmosMongoDB",
Expand Down
4 changes: 3 additions & 1 deletion pyatlan/model/assets/__init__.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ __all__ = [
"Stakeholder",
"AirflowDag",
"AirflowTask",
"ApplicationField",
"Application",
"AdfDataflow",
"AdfDataset",
Expand Down Expand Up @@ -63,8 +64,8 @@ __all__ = [
"Column",
"DatabricksUnityCatalogTag",
"SnowflakeStream",
"CalculationView",
"Database",
"CalculationView",
"Procedure",
"SnowflakeTag",
"CosmosMongoDB",
Expand Down Expand Up @@ -380,6 +381,7 @@ from .core.anomalo import Anomalo
from .core.anomalo_check import AnomaloCheck
from .core.app import App
from .core.application import Application
from .core.application_field import ApplicationField
from .core.asset import Asset
from .core.atlas_glossary import AtlasGlossary
from .core.atlas_glossary_category import AtlasGlossaryCategory
Expand Down
25 changes: 25 additions & 0 deletions pyatlan/model/assets/business_policy.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
RelationField,
TextField,
)
from pyatlan.model.structs import BusinessPolicyRule

from .core.asset import Asset

Expand Down Expand Up @@ -89,6 +90,12 @@ def __setattr__(self, name, value):
"""
Selected approval workflow id for business policy
"""
BUSINESS_POLICY_RULES: ClassVar[KeywordField] = KeywordField(
"businessPolicyRules", "businessPolicyRules"
)
"""
List of rules applied to this business policy.
"""

EXCEPTIONS_FOR_BUSINESS_POLICY: ClassVar[RelationField] = RelationField(
"exceptionsForBusinessPolicy"
Expand All @@ -113,6 +120,7 @@ def __setattr__(self, name, value):
"business_policy_filter_d_s_l",
"business_policy_base_parent_guid",
"business_policy_selected_approval_w_f",
"business_policy_rules",
"exceptions_for_business_policy",
"related_business_policies",
]
Expand Down Expand Up @@ -255,6 +263,20 @@ def business_policy_selected_approval_w_f(
business_policy_selected_approval_w_f
)

@property
def business_policy_rules(self) -> Optional[List[BusinessPolicyRule]]:
return (
None if self.attributes is None else self.attributes.business_policy_rules
)

@business_policy_rules.setter
def business_policy_rules(
self, business_policy_rules: Optional[List[BusinessPolicyRule]]
):
if self.attributes is None:
self.attributes = self.Attributes()
self.attributes.business_policy_rules = business_policy_rules

@property
def exceptions_for_business_policy(self) -> Optional[List[BusinessPolicyException]]:
return (
Expand Down Expand Up @@ -311,6 +333,9 @@ class Attributes(Asset.Attributes):
business_policy_selected_approval_w_f: Optional[str] = Field(
default=None, description=""
)
business_policy_rules: Optional[List[BusinessPolicyRule]] = Field(
default=None, description=""
)
exceptions_for_business_policy: Optional[List[BusinessPolicyException]] = Field(
default=None, description=""
) # relationship
Expand Down
Loading

0 comments on commit 099b973

Please sign in to comment.