atlan-application-sdk 2.1.0__py3-none-any.whl → 2.1.1__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.
@@ -15,6 +15,7 @@ Example:
15
15
 
16
16
  import os
17
17
  from abc import ABC
18
+ from datetime import datetime, timedelta
18
19
  from typing import Any, Dict, Generic, Optional, TypeVar
19
20
 
20
21
  from pydantic import BaseModel
@@ -62,6 +63,7 @@ class ActivitiesState(BaseModel, Generic[HandlerType]):
62
63
  model_config = {"arbitrary_types_allowed": True}
63
64
  handler: Optional[HandlerType] = None
64
65
  workflow_args: Optional[Dict[str, Any]] = None
66
+ last_updated_timestamp: Optional[datetime] = None
65
67
 
66
68
 
67
69
  ActivitiesStateType = TypeVar("ActivitiesStateType", bound=ActivitiesState)
@@ -113,12 +115,15 @@ class ActivitiesInterface(ABC, Generic[ActivitiesStateType]):
113
115
  Note:
114
116
  The workflow ID is automatically retrieved from the current activity context.
115
117
  If no state exists for the current workflow, a new one will be created.
118
+ This method also updates the last_updated_timestamp to enable time-based
119
+ state refresh functionality.
116
120
  """
117
121
  workflow_id = get_workflow_id()
118
122
  if not self._state.get(workflow_id):
119
123
  self._state[workflow_id] = ActivitiesState()
120
124
 
121
125
  self._state[workflow_id].workflow_args = workflow_args
126
+ self._state[workflow_id].last_updated_timestamp = datetime.now()
122
127
 
123
128
  async def _get_state(self, workflow_args: Dict[str, Any]) -> ActivitiesStateType:
124
129
  """Retrieve the state for the current workflow.
@@ -142,6 +147,15 @@ class ActivitiesInterface(ABC, Generic[ActivitiesStateType]):
142
147
  workflow_id = get_workflow_id()
143
148
  if workflow_id not in self._state:
144
149
  await self._set_state(workflow_args)
150
+
151
+ else:
152
+ current_timestamp = datetime.now()
153
+ # if difference of current_timestamp and last_updated_timestamp is greater than 15 minutes, then again _set_state
154
+ last_updated = self._state[workflow_id].last_updated_timestamp
155
+ if last_updated and current_timestamp - last_updated > timedelta(
156
+ minutes=15
157
+ ):
158
+ await self._set_state(workflow_args)
145
159
  return self._state[workflow_id]
146
160
  except OrchestratorError as e:
147
161
  logger.error(
@@ -1,4 +1,5 @@
1
1
  import os
2
+ from datetime import datetime
2
3
  from typing import (
3
4
  TYPE_CHECKING,
4
5
  Any,
@@ -60,6 +61,7 @@ class BaseSQLMetadataExtractionActivitiesState(ActivitiesState):
60
61
  sql_client: Optional[BaseSQLClient] = None
61
62
  handler: Optional[BaseSQLHandler] = None
62
63
  transformer: Optional[TransformerInterface] = None
64
+ last_updated_timestamp: Optional[datetime] = None
63
65
 
64
66
 
65
67
  class BaseSQLMetadataExtractionActivities(ActivitiesInterface):
@@ -149,13 +151,30 @@ class BaseSQLMetadataExtractionActivities(ActivitiesInterface):
149
151
 
150
152
  Args:
151
153
  workflow_args (Dict[str, Any]): Arguments passed to the workflow.
154
+
155
+ Note:
156
+ This method creates and configures the new SQL client before closing
157
+ the old one to ensure state is never left with a closed client if
158
+ initialization fails. The timestamp is only updated after the new
159
+ client is successfully created and assigned.
152
160
  """
153
161
  workflow_id = get_workflow_id()
154
162
  if not self._state.get(workflow_id):
155
163
  self._state[workflow_id] = BaseSQLMetadataExtractionActivitiesState()
156
164
 
157
- await super()._set_state(workflow_args)
165
+ existing_state = self._state[workflow_id]
166
+
167
+ # Update workflow_args early, but preserve old timestamp until new client is ready
168
+ # This ensures that if initialization fails, the state can still be refreshed
169
+ existing_state.workflow_args = workflow_args
170
+
171
+ # Store reference to old client for cleanup after new client is ready
172
+ old_sql_client = None
173
+ if existing_state and existing_state.sql_client is not None:
174
+ old_sql_client = existing_state.sql_client
158
175
 
176
+ # Create and configure new client BEFORE closing old one
177
+ # This ensures state is never left with a closed client if initialization fails
159
178
  sql_client = self.sql_client_class()
160
179
 
161
180
  # Load credentials BEFORE creating handler to avoid race condition
@@ -165,10 +184,29 @@ class BaseSQLMetadataExtractionActivities(ActivitiesInterface):
165
184
  )
166
185
  await sql_client.load(credentials)
167
186
 
168
- # Assign sql_client and handler to state AFTER credentials are loaded
187
+ # Only after new client is successfully created and configured,
188
+ # close old client and assign new one to state
189
+ if old_sql_client is not None:
190
+ try:
191
+ await old_sql_client.close()
192
+ logger.debug(
193
+ f"Closed existing SQL client for workflow {workflow_id} during state refresh"
194
+ )
195
+ except Exception as e:
196
+ logger.warning(
197
+ f"Failed to close existing SQL client for workflow {workflow_id}: {e}",
198
+ exc_info=True,
199
+ )
200
+ # Continue even if close fails - new client is already ready
201
+
202
+ # Assign sql_client and handler to state AFTER new client is ready
169
203
  self._state[workflow_id].sql_client = sql_client
170
204
  handler = self.handler_class(sql_client)
171
205
  self._state[workflow_id].handler = handler
206
+ # Update timestamp only after successful client creation and assignment
207
+ # This ensures that if initialization fails, the old timestamp remains
208
+ # and the state can be refreshed again immediately
209
+ self._state[workflow_id].last_updated_timestamp = datetime.now()
172
210
 
173
211
  # Create transformer with required parameters from ApplicationConstants
174
212
  transformer_params = {
@@ -4,6 +4,7 @@ from typing import Any, Dict, List, Optional, Tuple, Type
4
4
 
5
5
  import daft
6
6
  import yaml
7
+ from daft.functions import to_struct, when
7
8
  from pyatlan.model.enums import AtlanConnectorType
8
9
 
9
10
  from application_sdk.observability.logger_adaptor import get_logger
@@ -227,7 +228,7 @@ class QueryBasedTransformer(TransformerInterface):
227
228
  # Only create a struct if we have fields
228
229
  if struct_fields:
229
230
  # Create the struct first
230
- struct = daft.struct(*struct_fields)
231
+ struct = to_struct(*struct_fields)
231
232
 
232
233
  # If we have non-null checks, apply them
233
234
  if non_null_fields:
@@ -236,8 +237,8 @@ class QueryBasedTransformer(TransformerInterface):
236
237
  for check in non_null_fields[1:]:
237
238
  any_non_null = any_non_null | check
238
239
 
239
- # Use if_else on the any_non_null Expression
240
- return any_non_null.if_else(struct, None).alias(prefix)
240
+ # Use when().otherwise() for conditional expression (replaces if_else in daft 0.7+)
241
+ return when(any_non_null, struct).otherwise(None).alias(prefix)
241
242
 
242
243
  return struct.alias(prefix)
243
244
 
@@ -2,4 +2,4 @@
2
2
  Version information for the application_sdk package.
3
3
  """
4
4
 
5
- __version__ = "2.1.0"
5
+ __version__ = "2.1.1"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: atlan-application-sdk
3
- Version: 2.1.0
3
+ Version: 2.1.1
4
4
  Summary: Atlan Application SDK is a Python library for developing applications on the Atlan Platform
5
5
  Project-URL: Repository, https://github.com/atlanhq/application-sdk
6
6
  Project-URL: Documentation, https://github.com/atlanhq/application-sdk/README.md
@@ -31,7 +31,7 @@ Requires-Dist: pydantic<2.13.0,>=2.10.6
31
31
  Requires-Dist: python-dotenv<1.3.0,>=1.1.0
32
32
  Requires-Dist: uvloop<0.23.0,>=0.21.0; sys_platform != 'win32'
33
33
  Provides-Extra: daft
34
- Requires-Dist: daft<0.8.0,>=0.4.12; extra == 'daft'
34
+ Requires-Dist: daft<0.8.0,>=0.7.1; extra == 'daft'
35
35
  Provides-Extra: distributed-lock
36
36
  Requires-Dist: redis[hiredis]<7.2.0,>=5.2.0; extra == 'distributed-lock'
37
37
  Provides-Extra: iam-auth
@@ -1,8 +1,8 @@
1
1
  application_sdk/__init__.py,sha256=2e2mvmLJ5dxmJGPELtb33xwP-j6JMdoIuqKycEn7hjg,151
2
2
  application_sdk/constants.py,sha256=TvdmKQShVWBNQZdVF2y-fxuE31FmeraTnqQ9jT_n5XY,11567
3
- application_sdk/version.py,sha256=b8rehaDy9Q_sCU-sBPVls3ZFH8_ZNacFO5aQrIaO8ts,84
3
+ application_sdk/version.py,sha256=sNbvXviG7NgxM58lOHKhbZfERat5qAJNr3UZy_toVQs,84
4
4
  application_sdk/worker.py,sha256=DLMocpHvvwpdAopyXhxwM7ftaNlKvZMQfkgy1MFyiik,7561
5
- application_sdk/activities/__init__.py,sha256=6SiefuOPUDGfN3z6oPY4RkQLiUmkHpoDy5xadzpDzAw,11588
5
+ application_sdk/activities/__init__.py,sha256=i7iY6aL1VFg185n2rLLvD_sI2BA9zJ33jL5rD_sY__U,12350
6
6
  application_sdk/activities/lock_management.py,sha256=6Wdf3jMKitoarHQP91PIJOoGFz4aaOLS_40c7n1yAOA,3902
7
7
  application_sdk/activities/.cursor/BUGBOT.md,sha256=FNykX5aMkdOhzgpiGqstOnSp9JN63iR2XP3onU4AGh8,15843
8
8
  application_sdk/activities/common/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -12,7 +12,7 @@ application_sdk/activities/common/utils.py,sha256=ngyFmiZnMCAQtyu6vGeAlkzwNkM29M
12
12
  application_sdk/activities/metadata_extraction/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
13
  application_sdk/activities/metadata_extraction/base.py,sha256=ENFojpxqKdN_eVSL4iet3cGfylPOfcl1jnflfo4zhs8,3920
14
14
  application_sdk/activities/metadata_extraction/rest.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
- application_sdk/activities/metadata_extraction/sql.py,sha256=IkI1ZhOKyoSwosRT-g8c8IDBuFBq7mwyHLpDvwYO_B4,25451
15
+ application_sdk/activities/metadata_extraction/sql.py,sha256=CmE77EsgbOuDL5AKaRCnq1jApJnDWNVxx-RZ49cJwus,27415
16
16
  application_sdk/activities/query_extraction/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
17
17
  application_sdk/activities/query_extraction/sql.py,sha256=Gsa79R8CYY0uyt3rA2nLMfQs8-C4_zg1pJ_yYSF2cZw,21193
18
18
  application_sdk/application/__init__.py,sha256=vcrQsqlfmGvKcCZuOtHHaNRqHSGdXlEDftkb8Tv_shI,9867
@@ -138,7 +138,7 @@ application_sdk/transformers/atlas/__init__.py,sha256=fw3D8bBtt61SseAfYut3JZddpX
138
138
  application_sdk/transformers/atlas/sql.py,sha256=rkQXNZ7oebts5oF5E_Bw8NpcHHKScU0TmKciH_1l_k4,50419
139
139
  application_sdk/transformers/common/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
140
140
  application_sdk/transformers/common/utils.py,sha256=4ISMIQ0Gzghmi31p51FOFm5KLF7XF-fmH9PVT7i0DFE,4899
141
- application_sdk/transformers/query/__init__.py,sha256=yG1dGP3NhUizwkCgyFAzsr9SV9uWYZKjXoCWPrsIxVw,17358
141
+ application_sdk/transformers/query/__init__.py,sha256=4uVCU-NfDe08PlffjWQ5p4smQa7c518IL2rDgIk6694,17446
142
142
  application_sdk/transformers/query/templates/column.yaml,sha256=EXLYwGXN7LKT-v51n2EZnY99o6vHucyFaVSpM-sUSXw,7679
143
143
  application_sdk/transformers/query/templates/database.yaml,sha256=SD1hJg5LI7gsBHQL5mW341sa51EkhcsIDDFlIOi9zdk,1374
144
144
  application_sdk/transformers/query/templates/extras-procedure.yaml,sha256=XhAfVY4zm99K8fcgkYA1XPLv4ks-SA6SzMO3SMtQ60s,2298
@@ -152,8 +152,8 @@ application_sdk/workflows/metadata_extraction/__init__.py,sha256=jHUe_ZBQ66jx8bg
152
152
  application_sdk/workflows/metadata_extraction/sql.py,sha256=6ZaVt84n-8U2ZvR9GR7uIJKv5v8CuyQjhlnoRJvDszc,12435
153
153
  application_sdk/workflows/query_extraction/__init__.py,sha256=n066_CX5RpJz6DIxGMkKS3eGSRg03ilaCtsqfJWQb7Q,117
154
154
  application_sdk/workflows/query_extraction/sql.py,sha256=kT_JQkLCRZ44ZpaC4QvPL6DxnRIIVh8gYHLqRbMI-hA,4826
155
- atlan_application_sdk-2.1.0.dist-info/METADATA,sha256=XTa1eREHTJv_zkHQhKdCtxe-txAkf9pmIqSEch_zbUA,5806
156
- atlan_application_sdk-2.1.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
157
- atlan_application_sdk-2.1.0.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
158
- atlan_application_sdk-2.1.0.dist-info/licenses/NOTICE,sha256=A-XVVGt3KOYuuMmvSMIFkg534F1vHiCggEBp4Ez3wGk,1041
159
- atlan_application_sdk-2.1.0.dist-info/RECORD,,
155
+ atlan_application_sdk-2.1.1.dist-info/METADATA,sha256=Vc2uG2FMhuNXyZFXmGMmvc_LRpCBaNTcQEHpSV8NpOE,5805
156
+ atlan_application_sdk-2.1.1.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
157
+ atlan_application_sdk-2.1.1.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
158
+ atlan_application_sdk-2.1.1.dist-info/licenses/NOTICE,sha256=A-XVVGt3KOYuuMmvSMIFkg534F1vHiCggEBp4Ez3wGk,1041
159
+ atlan_application_sdk-2.1.1.dist-info/RECORD,,