salesforce-data-customcode 6.1.0.dev3__py3-none-any.whl → 6.1.0.dev5__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.
- datacustomcode/__init__.py +5 -0
- datacustomcode/cli.py +30 -5
- datacustomcode/client.py +267 -192
- datacustomcode/config.py +5 -0
- datacustomcode/config.yaml +0 -6
- datacustomcode/constants.py +9 -1
- datacustomcode/deploy.py +58 -24
- datacustomcode/function/runtime.py +0 -16
- datacustomcode/io/reader/base.py +42 -0
- datacustomcode/io/writer/base.py +45 -0
- datacustomcode/io/writer/csv.py +8 -0
- datacustomcode/io/writer/print.py +7 -0
- datacustomcode/run.py +11 -7
- datacustomcode/scan.py +164 -29
- datacustomcode/template.py +13 -1
- datacustomcode/templates/script/examples/streaming_deltas/entrypoint.py +62 -0
- datacustomcode/templates/script/jupyterlab.sh +18 -4
- {salesforce_data_customcode-6.1.0.dev3.dist-info → salesforce_data_customcode-6.1.0.dev5.dist-info}/METADATA +42 -5
- {salesforce_data_customcode-6.1.0.dev3.dist-info → salesforce_data_customcode-6.1.0.dev5.dist-info}/RECORD +22 -44
- datacustomcode/named_credential/__init__.py +0 -28
- datacustomcode/named_credential/base.py +0 -54
- datacustomcode/named_credential/default.py +0 -93
- datacustomcode/named_credential/direct/__init__.py +0 -19
- datacustomcode/named_credential/direct/auth.py +0 -63
- datacustomcode/named_credential/direct/credentials.py +0 -121
- datacustomcode/named_credential/direct/transport.py +0 -110
- datacustomcode/named_credential/direct/url_resolver.py +0 -112
- datacustomcode/named_credential/errors.py +0 -36
- datacustomcode/named_credential/spark_base.py +0 -93
- datacustomcode/named_credential/spark_default.py +0 -154
- datacustomcode/named_credential/types/__init__.py +0 -14
- datacustomcode/named_credential/types/http_method.py +0 -29
- datacustomcode/named_credential/types/http_request.py +0 -63
- datacustomcode/named_credential/types/http_request_builder.py +0 -55
- datacustomcode/named_credential/types/http_response.py +0 -43
- datacustomcode/named_credential/types/http_response_builder.py +0 -24
- datacustomcode/named_credential_config.py +0 -105
- datacustomcode/templates/function/example/chunking_with_external_callout/README.md +0 -119
- datacustomcode/templates/function/example/chunking_with_external_callout/config.json +0 -3
- datacustomcode/templates/function/example/chunking_with_external_callout/entrypoint.py +0 -161
- datacustomcode/templates/function/example/chunking_with_external_callout/external_callout_config.json +0 -11
- datacustomcode/templates/function/example/chunking_with_external_callout/tests/test.json +0 -16
- {salesforce_data_customcode-6.1.0.dev3.dist-info → salesforce_data_customcode-6.1.0.dev5.dist-info}/WHEEL +0 -0
- {salesforce_data_customcode-6.1.0.dev3.dist-info → salesforce_data_customcode-6.1.0.dev5.dist-info}/entry_points.txt +0 -0
- {salesforce_data_customcode-6.1.0.dev3.dist-info → salesforce_data_customcode-6.1.0.dev5.dist-info}/licenses/LICENSE.txt +0 -0
datacustomcode/scan.py
CHANGED
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
from __future__ import annotations
|
|
16
16
|
|
|
17
17
|
import ast
|
|
18
|
+
import copy
|
|
18
19
|
import json
|
|
19
20
|
import os
|
|
20
21
|
import sys
|
|
@@ -32,6 +33,8 @@ import pydantic
|
|
|
32
33
|
from datacustomcode.version import get_version
|
|
33
34
|
|
|
34
35
|
DATA_ACCESS_METHODS = ["read_dlo", "read_dmo", "write_to_dlo", "write_to_dmo"]
|
|
36
|
+
STREAMING_READ_METHODS = ["read_dlo_deltas", "read_dmo_deltas"]
|
|
37
|
+
STREAMING_WRITE_METHODS = ["write_dlo_deltas"]
|
|
35
38
|
|
|
36
39
|
DATA_TRANSFORM_CONFIG_TEMPLATE = {
|
|
37
40
|
"sdkVersion": get_version(),
|
|
@@ -43,6 +46,20 @@ DATA_TRANSFORM_CONFIG_TEMPLATE = {
|
|
|
43
46
|
},
|
|
44
47
|
}
|
|
45
48
|
|
|
49
|
+
STREAMING_TRANSFORM_CONFIG_TEMPLATE = {
|
|
50
|
+
"sdkVersion": get_version(),
|
|
51
|
+
"entryPoint": "",
|
|
52
|
+
"dataspace": "default",
|
|
53
|
+
"streamingSource": {
|
|
54
|
+
"type": "dlo",
|
|
55
|
+
"name": "",
|
|
56
|
+
},
|
|
57
|
+
"permissions": {
|
|
58
|
+
"read": {},
|
|
59
|
+
"write": {},
|
|
60
|
+
},
|
|
61
|
+
}
|
|
62
|
+
|
|
46
63
|
FUNCTION_CONFIG_TEMPLATE = {
|
|
47
64
|
"entryPoint": "",
|
|
48
65
|
}
|
|
@@ -160,6 +177,35 @@ class DataAccessLayerCalls(pydantic.BaseModel):
|
|
|
160
177
|
return next(iter(self.write_to_dmo))
|
|
161
178
|
|
|
162
179
|
|
|
180
|
+
class StreamingDataAccessLayerCalls(pydantic.BaseModel):
|
|
181
|
+
read_dlo_deltas: bool
|
|
182
|
+
read_dmo_deltas: bool
|
|
183
|
+
write_dlo_deltas: frozenset[str]
|
|
184
|
+
|
|
185
|
+
@pydantic.model_validator(mode="after")
|
|
186
|
+
def validate_access_layer(self) -> StreamingDataAccessLayerCalls:
|
|
187
|
+
if self.read_dlo_deltas and self.read_dmo_deltas:
|
|
188
|
+
raise ValueError(
|
|
189
|
+
"Cannot read DLO and DMO deltas in the same streaming transform."
|
|
190
|
+
)
|
|
191
|
+
if not self.read_dlo_deltas and not self.read_dmo_deltas:
|
|
192
|
+
raise ValueError(
|
|
193
|
+
"A streaming transform must read from at least one DLO or DMO "
|
|
194
|
+
"delta stream (read_dlo_deltas / read_dmo_deltas)."
|
|
195
|
+
)
|
|
196
|
+
if not self.write_dlo_deltas:
|
|
197
|
+
raise ValueError(
|
|
198
|
+
"A streaming transform must write to at least one DLO via "
|
|
199
|
+
"write_dlo_deltas."
|
|
200
|
+
)
|
|
201
|
+
return self
|
|
202
|
+
|
|
203
|
+
@property
|
|
204
|
+
def read_layer(self) -> str:
|
|
205
|
+
"""Return the read source layer, ``"dlo"`` or ``"dmo"``."""
|
|
206
|
+
return "dlo" if self.read_dlo_deltas else "dmo"
|
|
207
|
+
|
|
208
|
+
|
|
163
209
|
class ClientMethodVisitor(ast.NodeVisitor):
|
|
164
210
|
"""AST Visitor that finds all instances of Client read/write method calls."""
|
|
165
211
|
|
|
@@ -168,6 +214,9 @@ class ClientMethodVisitor(ast.NodeVisitor):
|
|
|
168
214
|
self._read_dmo_instances: set[str] = set()
|
|
169
215
|
self._write_to_dlo_instances: set[str] = set()
|
|
170
216
|
self._write_to_dmo_instances: set[str] = set()
|
|
217
|
+
self._read_dlo_deltas: bool = False
|
|
218
|
+
self._read_dmo_deltas: bool = False
|
|
219
|
+
self._write_dlo_deltas_instances: set[str] = set()
|
|
171
220
|
self.variable_values: Dict[str, Union[str, None]] = {}
|
|
172
221
|
|
|
173
222
|
def visit_Assign(self, node: ast.Assign) -> None:
|
|
@@ -189,14 +238,15 @@ class ClientMethodVisitor(ast.NodeVisitor):
|
|
|
189
238
|
node.func.value, ast.Name
|
|
190
239
|
):
|
|
191
240
|
method_name = node.func.attr
|
|
241
|
+
|
|
242
|
+
if method_name == "read_dlo_deltas":
|
|
243
|
+
self._read_dlo_deltas = True
|
|
244
|
+
elif method_name == "read_dmo_deltas":
|
|
245
|
+
self._read_dmo_deltas = True
|
|
246
|
+
|
|
192
247
|
if method_name in DATA_ACCESS_METHODS and node.args:
|
|
193
248
|
arg = node.args[0]
|
|
194
|
-
name =
|
|
195
|
-
|
|
196
|
-
if isinstance(arg, ast.Constant) and isinstance(arg.value, str):
|
|
197
|
-
name = arg.value
|
|
198
|
-
elif isinstance(arg, ast.Name) and arg.id in self.variable_values:
|
|
199
|
-
name = self.variable_values[arg.id]
|
|
249
|
+
name = self._resolve_name_arg(arg)
|
|
200
250
|
|
|
201
251
|
if name:
|
|
202
252
|
if method_name == "read_dlo":
|
|
@@ -207,8 +257,29 @@ class ClientMethodVisitor(ast.NodeVisitor):
|
|
|
207
257
|
self._write_to_dlo_instances.add(name)
|
|
208
258
|
elif method_name == "write_to_dmo":
|
|
209
259
|
self._write_to_dmo_instances.add(name)
|
|
260
|
+
elif method_name in STREAMING_WRITE_METHODS and node.args:
|
|
261
|
+
name = self._resolve_name_arg(node.args[0])
|
|
262
|
+
if name and method_name == "write_dlo_deltas":
|
|
263
|
+
self._write_dlo_deltas_instances.add(name)
|
|
210
264
|
self.generic_visit(node)
|
|
211
265
|
|
|
266
|
+
def _resolve_name_arg(self, arg: ast.expr) -> Union[str, None]:
|
|
267
|
+
"""Resolve a string-literal or tracked-variable first argument."""
|
|
268
|
+
if isinstance(arg, ast.Constant) and isinstance(arg.value, str):
|
|
269
|
+
return arg.value
|
|
270
|
+
if isinstance(arg, ast.Name) and arg.id in self.variable_values:
|
|
271
|
+
return self.variable_values[arg.id]
|
|
272
|
+
return None
|
|
273
|
+
|
|
274
|
+
@property
|
|
275
|
+
def is_streaming(self) -> bool:
|
|
276
|
+
"""Whether any streaming (delta) access method was found."""
|
|
277
|
+
return (
|
|
278
|
+
self._read_dlo_deltas
|
|
279
|
+
or self._read_dmo_deltas
|
|
280
|
+
or bool(self._write_dlo_deltas_instances)
|
|
281
|
+
)
|
|
282
|
+
|
|
212
283
|
def found(self) -> DataAccessLayerCalls:
|
|
213
284
|
return DataAccessLayerCalls(
|
|
214
285
|
read_dlo=frozenset(self._read_dlo_instances),
|
|
@@ -217,6 +288,13 @@ class ClientMethodVisitor(ast.NodeVisitor):
|
|
|
217
288
|
write_to_dmo=frozenset(self._write_to_dmo_instances),
|
|
218
289
|
)
|
|
219
290
|
|
|
291
|
+
def found_streaming(self) -> StreamingDataAccessLayerCalls:
|
|
292
|
+
return StreamingDataAccessLayerCalls(
|
|
293
|
+
read_dlo_deltas=self._read_dlo_deltas,
|
|
294
|
+
read_dmo_deltas=self._read_dmo_deltas,
|
|
295
|
+
write_dlo_deltas=frozenset(self._write_dlo_deltas_instances),
|
|
296
|
+
)
|
|
297
|
+
|
|
220
298
|
|
|
221
299
|
class ImportVisitor(ast.NodeVisitor):
|
|
222
300
|
"""AST Visitor that extracts external package imports from Python code."""
|
|
@@ -301,23 +379,51 @@ def write_requirements_file(file_path: str) -> str:
|
|
|
301
379
|
return requirements_path
|
|
302
380
|
|
|
303
381
|
|
|
304
|
-
def
|
|
305
|
-
"""
|
|
382
|
+
def _visit_file(file_path: str) -> ClientMethodVisitor:
|
|
383
|
+
"""Parse a Python file and return the populated method visitor."""
|
|
306
384
|
with open(file_path, "r") as f:
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
385
|
+
tree = ast.parse(f.read())
|
|
386
|
+
visitor = ClientMethodVisitor()
|
|
387
|
+
visitor.visit(tree)
|
|
388
|
+
return visitor
|
|
389
|
+
|
|
390
|
+
|
|
391
|
+
def scan_file(file_path: str) -> DataAccessLayerCalls:
|
|
392
|
+
"""Scan a single Python file for batch Client read/write method calls."""
|
|
393
|
+
return _visit_file(file_path).found()
|
|
394
|
+
|
|
395
|
+
|
|
396
|
+
def scan_file_streaming(file_path: str) -> StreamingDataAccessLayerCalls:
|
|
397
|
+
"""Scan a single Python file for StreamingClient delta method calls."""
|
|
398
|
+
return _visit_file(file_path).found_streaming()
|
|
399
|
+
|
|
312
400
|
|
|
401
|
+
def file_is_streaming(file_path: str) -> bool:
|
|
402
|
+
"""Return whether the entrypoint uses streaming (delta) access methods."""
|
|
403
|
+
return _visit_file(file_path).is_streaming
|
|
313
404
|
|
|
314
|
-
|
|
315
|
-
|
|
405
|
+
|
|
406
|
+
def dc_config_json_from_file(
|
|
407
|
+
file_path: str, type: str, streaming: bool = False
|
|
408
|
+
) -> dict[str, Any]:
|
|
409
|
+
"""Create a Data Cloud Custom Code config JSON from a script.
|
|
410
|
+
|
|
411
|
+
Args:
|
|
412
|
+
file_path: Path to the entrypoint.
|
|
413
|
+
type: Package type, ``"script"`` or ``"function"``.
|
|
414
|
+
streaming: For scripts, a streaming
|
|
415
|
+
(``streamingSource``) config instead of a batch one.
|
|
416
|
+
"""
|
|
316
417
|
config: dict[str, Any]
|
|
317
418
|
if type == "script":
|
|
318
|
-
|
|
419
|
+
template = (
|
|
420
|
+
STREAMING_TRANSFORM_CONFIG_TEMPLATE
|
|
421
|
+
if streaming
|
|
422
|
+
else DATA_TRANSFORM_CONFIG_TEMPLATE
|
|
423
|
+
)
|
|
424
|
+
config = copy.deepcopy(template)
|
|
319
425
|
elif type == "function":
|
|
320
|
-
config =
|
|
426
|
+
config = copy.deepcopy(FUNCTION_CONFIG_TEMPLATE)
|
|
321
427
|
config["entryPoint"] = os.path.basename(file_path)
|
|
322
428
|
return config
|
|
323
429
|
|
|
@@ -372,22 +478,51 @@ def update_config(file_path: str) -> dict[str, Any]:
|
|
|
372
478
|
|
|
373
479
|
if package_type == "script":
|
|
374
480
|
existing_config["dataspace"] = get_dataspace(existing_config)
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
if output.read_dlo:
|
|
378
|
-
read["dlo"] = list(output.read_dlo)
|
|
379
|
-
else:
|
|
380
|
-
read["dmo"] = list(output.read_dmo)
|
|
381
|
-
write: dict[str, list[str]] = {}
|
|
382
|
-
if output.write_to_dlo:
|
|
383
|
-
write["dlo"] = list(output.write_to_dlo)
|
|
481
|
+
if file_is_streaming(file_path):
|
|
482
|
+
_update_streaming_config(existing_config, file_path)
|
|
384
483
|
else:
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
484
|
+
existing_config.pop("streamingSource", None)
|
|
485
|
+
output = scan_file(file_path)
|
|
486
|
+
read: dict[str, list[str]] = {}
|
|
487
|
+
if output.read_dlo:
|
|
488
|
+
read["dlo"] = list(output.read_dlo)
|
|
489
|
+
else:
|
|
490
|
+
read["dmo"] = list(output.read_dmo)
|
|
491
|
+
write: dict[str, list[str]] = {}
|
|
492
|
+
if output.write_to_dlo:
|
|
493
|
+
write["dlo"] = list(output.write_to_dlo)
|
|
494
|
+
else:
|
|
495
|
+
write["dmo"] = list(output.write_to_dmo)
|
|
496
|
+
|
|
497
|
+
existing_config["permissions"] = {"read": read, "write": write}
|
|
388
498
|
return existing_config
|
|
389
499
|
|
|
390
500
|
|
|
501
|
+
def _update_streaming_config(existing_config: dict[str, Any], file_path: str) -> None:
|
|
502
|
+
output = scan_file_streaming(file_path)
|
|
503
|
+
read_layer = output.read_layer
|
|
504
|
+
|
|
505
|
+
source = existing_config.get("streamingSource")
|
|
506
|
+
if not isinstance(source, dict):
|
|
507
|
+
source = {}
|
|
508
|
+
source_name = source.get("name", "")
|
|
509
|
+
existing_config["streamingSource"] = {"type": read_layer, "name": source_name}
|
|
510
|
+
|
|
511
|
+
if not source_name:
|
|
512
|
+
logger.warning(
|
|
513
|
+
"streamingSource.name is empty in config.json. A streaming "
|
|
514
|
+
"transform must declare its read source; set streamingSource.name "
|
|
515
|
+
"to the DLO/DMO the transform reads from."
|
|
516
|
+
)
|
|
517
|
+
|
|
518
|
+
read_names = [source_name] if source_name else []
|
|
519
|
+
write_names = list(output.write_dlo_deltas)
|
|
520
|
+
existing_config["permissions"] = {
|
|
521
|
+
"read": {read_layer: read_names},
|
|
522
|
+
"write": {"dlo": write_names},
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
|
|
391
526
|
def get_dataspace(existing_config: dict[str, str]) -> str:
|
|
392
527
|
if "dataspace" in existing_config:
|
|
393
528
|
dataspace_value = existing_config["dataspace"]
|
datacustomcode/template.py
CHANGED
|
@@ -23,8 +23,12 @@ from datacustomcode.constants import FEATURE_TEMPLATE_MAPPING
|
|
|
23
23
|
script_template_dir = os.path.join(os.path.dirname(__file__), "templates", "script")
|
|
24
24
|
function_template_dir = os.path.join(os.path.dirname(__file__), "templates", "function")
|
|
25
25
|
|
|
26
|
+
STREAMING_EXAMPLE_ENTRYPOINT = os.path.join(
|
|
27
|
+
script_template_dir, "examples", "streaming_deltas", "entrypoint.py"
|
|
28
|
+
)
|
|
26
29
|
|
|
27
|
-
|
|
30
|
+
|
|
31
|
+
def copy_script_template(target_dir: str, streaming: bool = False) -> None:
|
|
28
32
|
"""Copy the template to the target directory."""
|
|
29
33
|
os.makedirs(target_dir, exist_ok=True)
|
|
30
34
|
|
|
@@ -39,6 +43,14 @@ def copy_script_template(target_dir: str) -> None:
|
|
|
39
43
|
logger.debug(f"Copying file {source} to {destination}...")
|
|
40
44
|
shutil.copy2(source, destination)
|
|
41
45
|
|
|
46
|
+
if streaming:
|
|
47
|
+
destination = os.path.join(target_dir, "payload", "entrypoint.py")
|
|
48
|
+
logger.debug(
|
|
49
|
+
f"Copying streaming example {STREAMING_EXAMPLE_ENTRYPOINT} to "
|
|
50
|
+
f"{destination}..."
|
|
51
|
+
)
|
|
52
|
+
shutil.copy2(STREAMING_EXAMPLE_ENTRYPOINT, destination)
|
|
53
|
+
|
|
42
54
|
|
|
43
55
|
def copy_function_template(target_dir: str, use_in_feature: Optional[str]) -> None:
|
|
44
56
|
os.makedirs(target_dir, exist_ok=True)
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"""Streaming BYOC transform: read a DLO change feed and write the deltas back.
|
|
2
|
+
|
|
3
|
+
This example is the streaming counterpart to a normal batch entrypoint. Instead
|
|
4
|
+
of a batch ``Client`` with ``read_dlo`` / ``write_to_dlo`` (which read and write
|
|
5
|
+
a bounded snapshot), it uses a :class:`StreamingClient` and its streaming delta
|
|
6
|
+
methods.
|
|
7
|
+
|
|
8
|
+
The first run of a streaming job will use the run mode INITIAL_SYNC which behaves
|
|
9
|
+
like a batch run on the streaming source. A streaming transform can also use run
|
|
10
|
+
mode REBUILD to do the same thing on demand. Note that these will process all
|
|
11
|
+
source rows and overwrite the target.
|
|
12
|
+
|
|
13
|
+
The transform in between is ordinary PySpark. Because the source is a change
|
|
14
|
+
feed, keep the metadata columns on the DataFrame you hand to
|
|
15
|
+
``write_dlo_deltas`` — the sink relies on them to merge changes correctly.
|
|
16
|
+
|
|
17
|
+
This entrypoint only runs inside the Data Cloud runtime;
|
|
18
|
+
the local ``datacustomcode run`` readers/writers raise
|
|
19
|
+
``NotImplementedError`` for the delta methods.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
from pyspark.sql import DataFrame
|
|
23
|
+
from pyspark.sql.functions import col, upper
|
|
24
|
+
|
|
25
|
+
from datacustomcode.client import (
|
|
26
|
+
RunMode,
|
|
27
|
+
StreamingClient,
|
|
28
|
+
get_run_mode,
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def main():
|
|
33
|
+
target_dlo = "Account_std_copy__dll"
|
|
34
|
+
client = StreamingClient()
|
|
35
|
+
|
|
36
|
+
if get_run_mode() == RunMode.DELTA_SYNC:
|
|
37
|
+
# Streaming DataFrame over the source DLO's change feed.
|
|
38
|
+
dataframe = client.read_dlo_deltas()
|
|
39
|
+
# Ordinary PySpark transform.
|
|
40
|
+
transformed = transform(dataframe)
|
|
41
|
+
|
|
42
|
+
# Start the streaming write. write_dlo_deltas returns the StreamingQuery;
|
|
43
|
+
# the trigger and checkpoint location are provided by the runtime.
|
|
44
|
+
query = client.write_dlo_deltas(target_dlo, transformed)
|
|
45
|
+
|
|
46
|
+
# Drive the query's lifecycle. In the streaming runtime this blocks until
|
|
47
|
+
# the job is stopped by the platform.
|
|
48
|
+
query.awaitTermination()
|
|
49
|
+
else:
|
|
50
|
+
# initial sync and rebuild read the entire streaming source DLO and
|
|
51
|
+
# write using a server-decided mode based on the run mode
|
|
52
|
+
dataframe = client.read_dlo()
|
|
53
|
+
transformed = transform(dataframe)
|
|
54
|
+
client.auto_write_to_dlo(target_dlo, transformed)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def transform(dataframe: DataFrame) -> DataFrame:
|
|
58
|
+
return dataframe.withColumn("description__c", upper(col("description__c")))
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
if __name__ == "__main__":
|
|
62
|
+
main()
|
|
@@ -45,13 +45,24 @@ check_docker() {
|
|
|
45
45
|
echo "Docker daemon is running"
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
+
# Function to check if openssl is installed
|
|
49
|
+
check_openssl() {
|
|
50
|
+
if ! command -v openssl &> /dev/null; then
|
|
51
|
+
echo "openssl is not installed. It is required to generate a secure JupyterLab access token."
|
|
52
|
+
exit 1
|
|
53
|
+
fi
|
|
54
|
+
}
|
|
55
|
+
|
|
48
56
|
# Function to start Jupyter server
|
|
49
57
|
start_jupyter() {
|
|
50
58
|
echo "Building the docker image"
|
|
51
59
|
docker build -t datacloud-customcode .
|
|
52
60
|
|
|
61
|
+
local TOKEN
|
|
62
|
+
TOKEN=$(openssl rand -hex 32)
|
|
63
|
+
|
|
53
64
|
echo "Running the docker container"
|
|
54
|
-
docker run -d --rm -p 8888:8888 \
|
|
65
|
+
docker run -d --rm -p 127.0.0.1:8888:8888 \
|
|
55
66
|
-v $(pwd):/workspace \
|
|
56
67
|
--name jupyter-server \
|
|
57
68
|
datacloud-customcode jupyter lab \
|
|
@@ -59,12 +70,14 @@ start_jupyter() {
|
|
|
59
70
|
--port=8888 \
|
|
60
71
|
--no-browser \
|
|
61
72
|
--allow-root \
|
|
62
|
-
--NotebookApp.token=
|
|
63
|
-
--NotebookApp.password='' \
|
|
73
|
+
--NotebookApp.token="$TOKEN" \
|
|
64
74
|
--notebook-dir=/workspace
|
|
65
75
|
|
|
66
76
|
sleep 3 # Wait for server to start
|
|
67
|
-
|
|
77
|
+
local URL
|
|
78
|
+
URL="http://localhost:8888/?token=$TOKEN"
|
|
79
|
+
echo "Opening $URL"
|
|
80
|
+
open_browser $URL
|
|
68
81
|
}
|
|
69
82
|
|
|
70
83
|
# Function to stop Jupyter server
|
|
@@ -82,6 +95,7 @@ stop_jupyter() {
|
|
|
82
95
|
case "$1" in
|
|
83
96
|
"start")
|
|
84
97
|
check_docker
|
|
98
|
+
check_openssl
|
|
85
99
|
start_jupyter
|
|
86
100
|
;;
|
|
87
101
|
"stop")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: salesforce-data-customcode
|
|
3
|
-
Version: 6.1.0.
|
|
3
|
+
Version: 6.1.0.dev5
|
|
4
4
|
Summary: Data Cloud Custom Code SDK
|
|
5
5
|
License-Expression: Apache-2.0
|
|
6
6
|
License-File: LICENSE.txt
|
|
@@ -170,15 +170,22 @@ Your Python dependencies can be packaged as .py files, .zip archives (containing
|
|
|
170
170
|
|
|
171
171
|
## API
|
|
172
172
|
|
|
173
|
-
Your entry point script will define logic using the `Client` object which
|
|
173
|
+
Your entry point script will define logic using the `Client` object (for batch transforms) or the `StreamingClient` object (for streaming delta transforms), which wrap the data access layers. Both are singletons; a single transform should use one or the other, not both.
|
|
174
174
|
|
|
175
|
-
You should only need the following methods:
|
|
175
|
+
For a batch transform, use `Client`. You should only need the following methods:
|
|
176
176
|
* `find_file_path(file_name)` – Resolve a bundled file (placed under `payload/files/`) to a `pathlib.Path` that exists. Works the same locally and inside Data Cloud — see [Bundled file resolution](#bundled-file-resolution) below for the full lookup order. Raises `FileNotFoundError` if the file isn't found.
|
|
177
177
|
* `read_dlo(name)` – Read from a Data Lake Object by name
|
|
178
178
|
* `read_dmo(name)` – Read from a Data Model Object by name
|
|
179
179
|
* `write_to_dlo(name, spark_dataframe, write_mode)` – Write to a Data Model Object by name with a Spark dataframe
|
|
180
180
|
* `write_to_dmo(name, spark_dataframe, write_mode)` – Write to a Data Lake Object by name with a Spark dataframe
|
|
181
181
|
|
|
182
|
+
For a streaming (delta) transform, use `StreamingClient`, which exposes the streaming counterparts:
|
|
183
|
+
* `read_dlo_deltas()` – Read the streaming change feed (deltas) of a Data Lake Object as a streaming DataFrame.
|
|
184
|
+
* `read_dmo_deltas()` – Read the streaming change feed (deltas) of a Data Model Object as a streaming DataFrame.
|
|
185
|
+
* `write_dlo_deltas(name, spark_dataframe)` – Write a streaming DataFrame of deltas to a Data Lake Object; returns the started `StreamingQuery`
|
|
186
|
+
|
|
187
|
+
`find_file_path`, `llm_gateway_generate_text`, and `einstein_predict` are available on both clients.
|
|
188
|
+
|
|
182
189
|
For example:
|
|
183
190
|
```python
|
|
184
191
|
from datacustomcode import Client
|
|
@@ -194,6 +201,36 @@ client.write_to_dlo('output_DLO')
|
|
|
194
201
|
> [!WARNING]
|
|
195
202
|
> Currently we only support reading from DMOs and writing to DMOs or reading from DLOs and writing to DLOs, but they cannot mix.
|
|
196
203
|
|
|
204
|
+
### Streaming (delta) transforms
|
|
205
|
+
|
|
206
|
+
Streaming BYOC transforms process a Data Lake Object's Change Data Feed continuously instead of reading a bounded snapshot. Use a `StreamingClient` and its `*_deltas` methods in place of the batch `Client` read/write methods:
|
|
207
|
+
|
|
208
|
+
```python
|
|
209
|
+
from pyspark.sql.functions import col, upper
|
|
210
|
+
|
|
211
|
+
from datacustomcode import StreamingClient
|
|
212
|
+
|
|
213
|
+
client = StreamingClient()
|
|
214
|
+
|
|
215
|
+
# read_dlo_deltas returns a *streaming* DataFrame over the change feed.
|
|
216
|
+
# The runtime resolves the single streaming source, so no name is passed.
|
|
217
|
+
deltas = client.read_dlo_deltas()
|
|
218
|
+
|
|
219
|
+
# Ordinary PySpark transform.
|
|
220
|
+
transformed = deltas.withColumn("description__c", upper(col("description__c")))
|
|
221
|
+
|
|
222
|
+
# write_dlo_deltas starts a streaming query and returns the StreamingQuery.
|
|
223
|
+
# The runtime owns the trigger and checkpoint location; you
|
|
224
|
+
# choose only the target table.
|
|
225
|
+
query = client.write_dlo_deltas("Output__dll", transformed)
|
|
226
|
+
query.awaitTermination()
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
Notes:
|
|
230
|
+
|
|
231
|
+
- These methods only run inside the Data Cloud streaming (`DELTA_SYNC`) runtime. Locally (`datacustomcode run`) they raise `NotImplementedError`, since there is no change feed to stream.
|
|
232
|
+
- A complete runnable entry point is provided in [`examples/streaming_deltas/entrypoint.py`](src/datacustomcode/templates/script/examples/streaming_deltas/entrypoint.py).
|
|
233
|
+
|
|
197
234
|
### Bundled file resolution
|
|
198
235
|
|
|
199
236
|
Place bundled files (CSVs, prompt files, etc.) under `payload/files/`. The same `client.find_file_path("data.csv")` call resolves consistently across all three runtimes:
|
|
@@ -497,7 +534,7 @@ exploration. Instead of running an entire script, one can run one code cell at
|
|
|
497
534
|
|
|
498
535
|
You can read more about Jupyter Notebooks here: https://jupyter.org/
|
|
499
536
|
|
|
500
|
-
1. Within the root project of your package folder, run `./jupyterlab.sh start`
|
|
537
|
+
1. Within the root project of your package folder, run `./jupyterlab.sh start`. This prints an access token and opens an already-authenticated JupyterLab session in your browser. If the browser doesn't open automatically, copy the printed `http://localhost:8888/?token=...` URL into your browser.
|
|
501
538
|
1. Double-click on "account.ipynb" file, which provides a starting point for a notebook
|
|
502
539
|
1. Use shift+enter to execute each cell within the notebook. Add/edit/delete cells of code as needed for your data exploration.
|
|
503
540
|
1. Don't forget to run `./jupyterlab.sh stop` to stop the docker container
|
|
@@ -588,5 +625,5 @@ If you're using OAuth Tokens authentication, the initial configure will retrieve
|
|
|
588
625
|
## Other docs
|
|
589
626
|
|
|
590
627
|
- [Troubleshooting](./docs/troubleshooting.md)
|
|
591
|
-
- [
|
|
628
|
+
- [Contributing](./CONTRIBUTING.md)
|
|
592
629
|
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
datacustomcode/__init__.py,sha256=
|
|
1
|
+
datacustomcode/__init__.py,sha256=5vvWRk5gb2mZIaVZ9BSPRL45uKSUwmSerxhLp6A4Q-4,2815
|
|
2
2
|
datacustomcode/auth.py,sha256=fpSjhIBdv9trC8yq2vuljAix_Euu-4Ah7HDCGhYjOxI,8309
|
|
3
|
-
datacustomcode/cli.py,sha256=
|
|
4
|
-
datacustomcode/client.py,sha256=
|
|
3
|
+
datacustomcode/cli.py,sha256=K4LHL5xrb7-erXbjYz7pT4Kg4jG6Wf81c9wl_8tLabE,14162
|
|
4
|
+
datacustomcode/client.py,sha256=1vX41y2KvKC-qvs48Vrp0ylpQovytECXS4LrsAdMCC4,26568
|
|
5
5
|
datacustomcode/cmd.py,sha256=ZMs46aydJw2EaU26JgCtZmnqESQFHvvaJz10hnjZTBk,3537
|
|
6
6
|
datacustomcode/common_config.py,sha256=SAUnxj3kqmOeWwPmFoYq4tuxokMgURVm4QwcGi-avL4,1928
|
|
7
|
-
datacustomcode/config.py,sha256=
|
|
8
|
-
datacustomcode/config.yaml,sha256=
|
|
9
|
-
datacustomcode/constants.py,sha256=
|
|
7
|
+
datacustomcode/config.py,sha256=lqed3jcWfoIweikSFZelaAoyBa0cXXScL8O6vaqdeRU,4372
|
|
8
|
+
datacustomcode/config.yaml,sha256=ldssmQYPghmYSSPaelm3F2mC99njavhrWsINRSzWymM,933
|
|
9
|
+
datacustomcode/constants.py,sha256=NCGptcql30En5N7WAH96TYD_DTzHFYdh1sZZ5f327cs,1686
|
|
10
10
|
datacustomcode/credentials.py,sha256=D-7Zd3Nh_wStdj8_wUy5cnC8M_mdbfBijhIAi-2EbXs,9467
|
|
11
|
-
datacustomcode/deploy.py,sha256=
|
|
11
|
+
datacustomcode/deploy.py,sha256=HvKoXcUfBwBRsTWCDp22Qg3fJV1qRZ-TFNQ-Od8AATE,23441
|
|
12
12
|
datacustomcode/einstein_platform_client.py,sha256=ON2B_m--vdbCVVMVcLc81T-BRZ5-UuxVCer8E6OEQPA,4083
|
|
13
13
|
datacustomcode/einstein_platform_config.py,sha256=6lb_FRbEzdt5a8-2cR15f13ZKw674uvRJ4Xq0o_pQXE,1490
|
|
14
14
|
datacustomcode/einstein_predictions/__init__.py,sha256=_RIDTqcEHDRu6AsCsj9lRBykNkHI8Ch8JBILL1ioPtA,1237
|
|
@@ -27,19 +27,19 @@ datacustomcode/function/__init__.py,sha256=oag8FevDxbx1vR0tFhtxPd5SvmUEYDj8esRo4
|
|
|
27
27
|
datacustomcode/function/base.py,sha256=H_i80GcsFScvHJJ6bYLry5phTpsEU1KubpR8D3qnsjo,691
|
|
28
28
|
datacustomcode/function/feature_types/__init__.py,sha256=gamfOD1VnAtEslRBpqh-yKiVjkG_wYWYdSatGIfsN-w,621
|
|
29
29
|
datacustomcode/function/feature_types/chunking.py,sha256=_tCCHj7slD59-aLLEZUFRPOX37q_MEKkL4thi9CNwMw,7180
|
|
30
|
-
datacustomcode/function/runtime.py,sha256=
|
|
30
|
+
datacustomcode/function/runtime.py,sha256=jUduf-pve-0JAv0Lg0HPyLCZ9_Hgcxe25dI7t-DgX9o,3696
|
|
31
31
|
datacustomcode/function_utils.py,sha256=y6vaoSSaJ9CeHhjLCNyqzJT4vx6yCePWRxNLz38ddjQ,12920
|
|
32
32
|
datacustomcode/io/__init__.py,sha256=gamfOD1VnAtEslRBpqh-yKiVjkG_wYWYdSatGIfsN-w,621
|
|
33
33
|
datacustomcode/io/base.py,sha256=gbwZWWVUbCbGR4jIg_4h4qOz8tOMjE4RDTleD23WFKo,973
|
|
34
34
|
datacustomcode/io/reader/__init__.py,sha256=gamfOD1VnAtEslRBpqh-yKiVjkG_wYWYdSatGIfsN-w,621
|
|
35
|
-
datacustomcode/io/reader/base.py,sha256=
|
|
35
|
+
datacustomcode/io/reader/base.py,sha256=2hAqaZvzHvvr5KBEgQSjUrHpidcHkgUXXTbGQG-b8io,3176
|
|
36
36
|
datacustomcode/io/reader/query_api.py,sha256=eVrohrcnTnhSMsGfPRHu5XltjFtGG0hyHt1o4q1hp2w,9340
|
|
37
37
|
datacustomcode/io/reader/sf_cli.py,sha256=x5QacVqRZaZSyph1_wwxo67s8r29wsOcUsOaiF9cDWE,6324
|
|
38
38
|
datacustomcode/io/reader/utils.py,sha256=HlHhPZoHfmWA3mF8kTnd3m4Nd2exaz4NsOJJfQY7Pew,1656
|
|
39
39
|
datacustomcode/io/writer/__init__.py,sha256=gamfOD1VnAtEslRBpqh-yKiVjkG_wYWYdSatGIfsN-w,621
|
|
40
|
-
datacustomcode/io/writer/base.py,sha256=
|
|
41
|
-
datacustomcode/io/writer/csv.py,sha256=
|
|
42
|
-
datacustomcode/io/writer/print.py,sha256=
|
|
40
|
+
datacustomcode/io/writer/base.py,sha256=W0QmsmszCjOIYPlEqbAjld0O855CAFvZjm_FT7szyb4,3806
|
|
41
|
+
datacustomcode/io/writer/csv.py,sha256=LYQpTZlIQdn0RRx_dRah22V2Vgm-InKP4KHRKfWGiRY,1963
|
|
42
|
+
datacustomcode/io/writer/print.py,sha256=Tpy5-0NmJ_renC6uah5TdHQZFzDbx23ltm1tIPK0978,5388
|
|
43
43
|
datacustomcode/llm_gateway/__init__.py,sha256=rcoGpSkv36tZuAOcTxKkhNpM0qV03qgmer_ej07Vmfc,1088
|
|
44
44
|
datacustomcode/llm_gateway/base.py,sha256=CTUhZiZ0JFlmWj6kQpTR4_-mUvqkBKl_Fkdvv9VwxY8,1262
|
|
45
45
|
datacustomcode/llm_gateway/default.py,sha256=QiwrDaUvrh00I-fYYhmogTtqC5r0fmm-JrXxNQLvTc0,1854
|
|
@@ -53,31 +53,13 @@ datacustomcode/llm_gateway/types/generate_text_response.py,sha256=NSvj6nvzR4cZ0c
|
|
|
53
53
|
datacustomcode/llm_gateway/types/generate_text_response_builder.py,sha256=6MV6JZuTyHUId3Slw9k7m0KBgjj-5S9HI4qtd94Jl2A,1272
|
|
54
54
|
datacustomcode/llm_gateway_config.py,sha256=ECm9CFqOFOaLHwenvVFAjiRSzG9Eg9fY4pb8nHbQ34c,3312
|
|
55
55
|
datacustomcode/mixin.py,sha256=ZtROqO1W1_D7MV8lw4cw6zzcchJLoZS0aAiXrolYR4k,4914
|
|
56
|
-
datacustomcode/named_credential/__init__.py,sha256=XD_UI8ggOkFvkqCOZI_3dP1AkviqPIZFOt3RI1_RDe4,1163
|
|
57
|
-
datacustomcode/named_credential/base.py,sha256=-LMskZkKNoaFg-Zc80M_4IPLF7ru5pfnbkfLI7YXrFc,1816
|
|
58
|
-
datacustomcode/named_credential/default.py,sha256=z03B0daZvTPiTmJWGAtDCICB1srelESg7QmPH2iRK5o,3339
|
|
59
|
-
datacustomcode/named_credential/direct/__init__.py,sha256=zqErMI1GqEhseLnYkrWtq3WipRi_dP153c8wwLvgI-o,799
|
|
60
|
-
datacustomcode/named_credential/direct/auth.py,sha256=U09jO0Pn3OEOEudmvcBmtkwoXqE0Ae_Mv6TAMqb3eG4,2201
|
|
61
|
-
datacustomcode/named_credential/direct/credentials.py,sha256=fibIZTyBT1IbAGFjXwAXqYcQt9o_sB8zFmuixGmibE4,4534
|
|
62
|
-
datacustomcode/named_credential/direct/transport.py,sha256=UL5eJJsyoAjr8ovJDQKQY3O20DivvbWFPGRyM8Y2or4,4078
|
|
63
|
-
datacustomcode/named_credential/direct/url_resolver.py,sha256=KItv12Az0qoxnVacCTcM9t9AY7qB9_tqPWMgiP-7Y6s,3823
|
|
64
|
-
datacustomcode/named_credential/errors.py,sha256=zyMEgljTf85digXrLrlR9HBh5Un4WtG0WsZx0K4EWt0,1212
|
|
65
|
-
datacustomcode/named_credential/spark_base.py,sha256=w34B-Pg6m_hcUqUbjmB2b6zUme_MSgg6VeghbMMYYN4,3288
|
|
66
|
-
datacustomcode/named_credential/spark_default.py,sha256=DY_STX9-1le8cS5M_vGqB3VUtQ2XCZZYDAmKId7YekA,5219
|
|
67
|
-
datacustomcode/named_credential/types/__init__.py,sha256=gamfOD1VnAtEslRBpqh-yKiVjkG_wYWYdSatGIfsN-w,621
|
|
68
|
-
datacustomcode/named_credential/types/http_method.py,sha256=NFf8emmW_Y73q8q2pseWHqOQ-1SoFwKp2vCMr1OdmEU,952
|
|
69
|
-
datacustomcode/named_credential/types/http_request.py,sha256=LXW4EcvUfyOJHf8-VtFSRWy6JsTJJsd0N4-X6WxxFnA,2170
|
|
70
|
-
datacustomcode/named_credential/types/http_request_builder.py,sha256=qdToSlX2xw3FzuLF6Kas3VzCp1ubdXZb4_WkjQ5fBY0,1862
|
|
71
|
-
datacustomcode/named_credential/types/http_response.py,sha256=ZqX52RMjSdn8OpHSDkKJo9730QRx8o4_ZT-1noSMRe8,1480
|
|
72
|
-
datacustomcode/named_credential/types/http_response_builder.py,sha256=MoUjpT-P5W2uT1-h1rIfKpBQHpMj_ZA_nUmn-FyBdbQ,896
|
|
73
|
-
datacustomcode/named_credential_config.py,sha256=M07-oWzYj0KTrfA0fqvk3BQvaaDaw7C6nxbR0s3lOUk,3645
|
|
74
56
|
datacustomcode/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
75
|
-
datacustomcode/run.py,sha256=
|
|
76
|
-
datacustomcode/scan.py,sha256=
|
|
57
|
+
datacustomcode/run.py,sha256=Ky58Di6Nh7gk6Ic-5Jxt3Y4xB3vRNmzlUcoSlK-OLvo,8169
|
|
58
|
+
datacustomcode/scan.py,sha256=9rq1Wae_tYACJykaOmBOv1covYvOSdky8w13exnmyJ0,19170
|
|
77
59
|
datacustomcode/spark/__init__.py,sha256=12drVVlRiczCxOQw-EzuGtLsikCM8baBXvDEgwvelCI,860
|
|
78
60
|
datacustomcode/spark/base.py,sha256=tlGqM4LxuLoDa7OxJF5nVeb7phO5uvD1DHBQeH7NMMs,1036
|
|
79
61
|
datacustomcode/spark/default.py,sha256=aMB8CaTPYwHbQE_7XqBLQUZPGRdDJcRL72qTVh0aG7M,1433
|
|
80
|
-
datacustomcode/template.py,sha256=
|
|
62
|
+
datacustomcode/template.py,sha256=1u0U86coPX7-8g0ZixCbFGDBOMNcOqMhTXdJDAN9TaU,3726
|
|
81
63
|
datacustomcode/templates/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
82
64
|
datacustomcode/templates/function/.devcontainer/devcontainer.json,sha256=21RNTadhC-rynON2-VOtr5U174Hxnr_MA4RQ4592rsg,166
|
|
83
65
|
datacustomcode/templates/function/Dockerfile.dependencies,sha256=AfHRddm5l3ujv4vdrf0d-SMB-qxPCOa0XQm6ptP2Euw,174
|
|
@@ -86,11 +68,6 @@ datacustomcode/templates/function/build_native_dependencies.sh,sha256=dk9vhzlObd
|
|
|
86
68
|
datacustomcode/templates/function/chunking/payload/config.json,sha256=RBNvo1WzZ4oRRq0W9-hknpT7T8If536DEMBg9hyq_4o,2
|
|
87
69
|
datacustomcode/templates/function/chunking/payload/entrypoint.py,sha256=tzpQJjODulwzLilOoKdOo8GjpdIlCDByjnawpHIQYQA,4517
|
|
88
70
|
datacustomcode/templates/function/chunking/requirements.txt,sha256=Ih0KsVdiTdo7KuIsqFB_AJMRT2r0ao_xDEb3hiDmf48,46
|
|
89
|
-
datacustomcode/templates/function/example/chunking_with_external_callout/README.md,sha256=RIgoUysFniAE4lC5LiZl5u55Rro1w5gmM2mMnsQF_HI,4716
|
|
90
|
-
datacustomcode/templates/function/example/chunking_with_external_callout/config.json,sha256=EvjuOVssRg2SjcUb5F50RRMGDqag_f69bv8AOflaK1g,38
|
|
91
|
-
datacustomcode/templates/function/example/chunking_with_external_callout/entrypoint.py,sha256=QqJLeJHgtW4RkDwTaUIsb2ng_ODwwClXpn5F7XyXBes,5296
|
|
92
|
-
datacustomcode/templates/function/example/chunking_with_external_callout/external_callout_config.json,sha256=PUCmw_humbVoCyswFneuGYx0INbqSTPOumDqtGOztiM,320
|
|
93
|
-
datacustomcode/templates/function/example/chunking_with_external_callout/tests/test.json,sha256=ZqUthCT9F9HoyT493tXg4nFDSbEAeaFHY1iHo-9Qx7g,2703
|
|
94
71
|
datacustomcode/templates/function/example/chunking_with_llm/config.json,sha256=EvjuOVssRg2SjcUb5F50RRMGDqag_f69bv8AOflaK1g,38
|
|
95
72
|
datacustomcode/templates/function/example/chunking_with_llm/entrypoint.py,sha256=6AMVf_busCCDALktG8vkCtjnzJheMgM-OOLML5iLKOg,3437
|
|
96
73
|
datacustomcode/templates/function/example/chunking_with_llm/files/chunking_prompt.txt,sha256=w_gSYNXwZmbMTMjZ_YYwm1IpMsVpI3hyC3ew60zqs_0,446
|
|
@@ -111,15 +88,16 @@ datacustomcode/templates/script/account.ipynb,sha256=LIbxgiVxflNASdspF2lfpMKkKAT
|
|
|
111
88
|
datacustomcode/templates/script/build_native_dependencies.sh,sha256=ICRrp4f1ATBwPaUiuVGjY-MwiubFswNJLf8gMGU6YNg,197
|
|
112
89
|
datacustomcode/templates/script/examples/employee_hierarchy/employee_data.csv,sha256=C7ggLBfoyi3M2BdMLNyOeKqF-5OO-Da76lkwgWg2cVQ,302
|
|
113
90
|
datacustomcode/templates/script/examples/employee_hierarchy/entrypoint.py,sha256=Mfm3iQtEHTQRW6cmZTwRKdTh3IeGWR-teXrd6x-YLSY,2223
|
|
114
|
-
datacustomcode/templates/script/
|
|
91
|
+
datacustomcode/templates/script/examples/streaming_deltas/entrypoint.py,sha256=UQr1EhJURFFGI5626efvv-MxGHltzcpJcEJVmz5ZQdo,2333
|
|
92
|
+
datacustomcode/templates/script/jupyterlab.sh,sha256=0O_rPZ8w2DM1hxPo9Wig9j-ymhS2dRzVO2PlFClWdlA,2786
|
|
115
93
|
datacustomcode/templates/script/payload/config.json,sha256=0d2mEMt4NHIeOUTsgiPMuRKdOLIQxmh-Wq-IfEqU-gc,28
|
|
116
94
|
datacustomcode/templates/script/payload/entrypoint.py,sha256=4Uph0ILa5ukk_6C90paK3uzQn0zZvNLr9ho3o_ZwErQ,2474
|
|
117
95
|
datacustomcode/templates/script/requirements-dev.txt,sha256=OWwuy1awesqOZOS3Zizmdd6BDnSePpGFN5bq5IrALvc,176
|
|
118
96
|
datacustomcode/templates/script/requirements.txt,sha256=qJbqzs5z0Hrx590U3T7dHq_m8UQEx2TdhgrJSz-QHIQ,40
|
|
119
97
|
datacustomcode/token_provider.py,sha256=qA_e4vqSXnxn0MccFFPULWO8ZeUOEAu5QpBnoYCg-fc,6839
|
|
120
98
|
datacustomcode/version.py,sha256=9LlbVrzwBvut1L308QbwNBgxdQk5CrghH39JARVSxms,989
|
|
121
|
-
salesforce_data_customcode-6.1.0.
|
|
122
|
-
salesforce_data_customcode-6.1.0.
|
|
123
|
-
salesforce_data_customcode-6.1.0.
|
|
124
|
-
salesforce_data_customcode-6.1.0.
|
|
125
|
-
salesforce_data_customcode-6.1.0.
|
|
99
|
+
salesforce_data_customcode-6.1.0.dev5.dist-info/METADATA,sha256=VCspB8hb-Qia1FvP5047hBU8M8uEO-VcuNP-6Wf_7nc,27417
|
|
100
|
+
salesforce_data_customcode-6.1.0.dev5.dist-info/WHEEL,sha256=eY7nduwzv-ldUxpzbRlxwvC693Hg6PX8bWDjEHjZ_dk,88
|
|
101
|
+
salesforce_data_customcode-6.1.0.dev5.dist-info/entry_points.txt,sha256=WpQ94UB7UuRCYGOLtJV3vAgm1tl7iVf43VYRWIuNu5Y,74
|
|
102
|
+
salesforce_data_customcode-6.1.0.dev5.dist-info/licenses/LICENSE.txt,sha256=iOi8EmQpfkFhMENi7VYtkl2EqS14pEOccoXHiW2dyPU,11443
|
|
103
|
+
salesforce_data_customcode-6.1.0.dev5.dist-info/RECORD,,
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
# Copyright (c) 2025, Salesforce, Inc.
|
|
2
|
-
# SPDX-License-Identifier: Apache-2
|
|
3
|
-
#
|
|
4
|
-
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
-
# you may not use this file except in compliance with the License.
|
|
6
|
-
# You may obtain a copy of the License at
|
|
7
|
-
#
|
|
8
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
-
#
|
|
10
|
-
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
-
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
-
# See the License for the specific language governing permissions and
|
|
14
|
-
# limitations under the License.
|
|
15
|
-
|
|
16
|
-
from datacustomcode.named_credential.base import NamedCredential
|
|
17
|
-
from datacustomcode.named_credential.default import DefaultNamedCredential
|
|
18
|
-
from datacustomcode.named_credential.errors import NamedCredentialCallError
|
|
19
|
-
from datacustomcode.named_credential.spark_base import SparkNamedCredential
|
|
20
|
-
from datacustomcode.named_credential.spark_default import DefaultSparkNamedCredential
|
|
21
|
-
|
|
22
|
-
__all__ = [
|
|
23
|
-
"DefaultNamedCredential",
|
|
24
|
-
"DefaultSparkNamedCredential",
|
|
25
|
-
"NamedCredential",
|
|
26
|
-
"NamedCredentialCallError",
|
|
27
|
-
"SparkNamedCredential",
|
|
28
|
-
]
|