salesforce-data-customcode 0.1.4__tar.gz → 0.1.6__tar.gz
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.
- salesforce_data_customcode-0.1.6/PKG-INFO +261 -0
- salesforce_data_customcode-0.1.6/README.md +237 -0
- {salesforce_data_customcode-0.1.4 → salesforce_data_customcode-0.1.6}/pyproject.toml +1 -1
- {salesforce_data_customcode-0.1.4 → salesforce_data_customcode-0.1.6}/src/datacustomcode/cli.py +21 -2
- {salesforce_data_customcode-0.1.4 → salesforce_data_customcode-0.1.6}/src/datacustomcode/deploy.py +68 -15
- {salesforce_data_customcode-0.1.4 → salesforce_data_customcode-0.1.6}/src/datacustomcode/scan.py +134 -0
- salesforce_data_customcode-0.1.6/src/datacustomcode/templates/account.ipynb +86 -0
- {salesforce_data_customcode-0.1.4 → salesforce_data_customcode-0.1.6}/src/datacustomcode/templates/jupyterlab.sh +2 -2
- salesforce_data_customcode-0.1.4/PKG-INFO +0 -159
- salesforce_data_customcode-0.1.4/README.md +0 -135
- {salesforce_data_customcode-0.1.4 → salesforce_data_customcode-0.1.6}/LICENSE.txt +0 -0
- {salesforce_data_customcode-0.1.4 → salesforce_data_customcode-0.1.6}/src/datacustomcode/__init__.py +0 -0
- {salesforce_data_customcode-0.1.4 → salesforce_data_customcode-0.1.6}/src/datacustomcode/client.py +0 -0
- {salesforce_data_customcode-0.1.4 → salesforce_data_customcode-0.1.6}/src/datacustomcode/cmd.py +0 -0
- {salesforce_data_customcode-0.1.4 → salesforce_data_customcode-0.1.6}/src/datacustomcode/config.py +0 -0
- {salesforce_data_customcode-0.1.4 → salesforce_data_customcode-0.1.6}/src/datacustomcode/config.yaml +0 -0
- {salesforce_data_customcode-0.1.4 → salesforce_data_customcode-0.1.6}/src/datacustomcode/credentials.py +0 -0
- {salesforce_data_customcode-0.1.4 → salesforce_data_customcode-0.1.6}/src/datacustomcode/io/__init__.py +0 -0
- {salesforce_data_customcode-0.1.4 → salesforce_data_customcode-0.1.6}/src/datacustomcode/io/base.py +0 -0
- {salesforce_data_customcode-0.1.4 → salesforce_data_customcode-0.1.6}/src/datacustomcode/io/reader/__init__.py +0 -0
- {salesforce_data_customcode-0.1.4 → salesforce_data_customcode-0.1.6}/src/datacustomcode/io/reader/base.py +0 -0
- {salesforce_data_customcode-0.1.4 → salesforce_data_customcode-0.1.6}/src/datacustomcode/io/reader/query_api.py +0 -0
- {salesforce_data_customcode-0.1.4 → salesforce_data_customcode-0.1.6}/src/datacustomcode/io/writer/__init__.py +0 -0
- {salesforce_data_customcode-0.1.4 → salesforce_data_customcode-0.1.6}/src/datacustomcode/io/writer/base.py +0 -0
- {salesforce_data_customcode-0.1.4 → salesforce_data_customcode-0.1.6}/src/datacustomcode/io/writer/csv.py +0 -0
- {salesforce_data_customcode-0.1.4 → salesforce_data_customcode-0.1.6}/src/datacustomcode/io/writer/print.py +0 -0
- {salesforce_data_customcode-0.1.4 → salesforce_data_customcode-0.1.6}/src/datacustomcode/mixin.py +0 -0
- {salesforce_data_customcode-0.1.4 → salesforce_data_customcode-0.1.6}/src/datacustomcode/py.typed +0 -0
- {salesforce_data_customcode-0.1.4 → salesforce_data_customcode-0.1.6}/src/datacustomcode/run.py +0 -0
- {salesforce_data_customcode-0.1.4 → salesforce_data_customcode-0.1.6}/src/datacustomcode/template.py +0 -0
- {salesforce_data_customcode-0.1.4 → salesforce_data_customcode-0.1.6}/src/datacustomcode/templates/.devcontainer/devcontainer.json +0 -0
- {salesforce_data_customcode-0.1.4 → salesforce_data_customcode-0.1.6}/src/datacustomcode/templates/Dockerfile +0 -0
- {salesforce_data_customcode-0.1.4 → salesforce_data_customcode-0.1.6}/src/datacustomcode/templates/README.md +0 -0
- {salesforce_data_customcode-0.1.4 → salesforce_data_customcode-0.1.6}/src/datacustomcode/templates/payload/config.json +0 -0
- {salesforce_data_customcode-0.1.4 → salesforce_data_customcode-0.1.6}/src/datacustomcode/templates/payload/entrypoint.py +0 -0
- {salesforce_data_customcode-0.1.4 → salesforce_data_customcode-0.1.6}/src/datacustomcode/templates/requirements-dev.txt +0 -0
- {salesforce_data_customcode-0.1.4 → salesforce_data_customcode-0.1.6}/src/datacustomcode/templates/requirements.txt +0 -0
- {salesforce_data_customcode-0.1.4 → salesforce_data_customcode-0.1.6}/src/datacustomcode/version.py +0 -0
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: salesforce-data-customcode
|
|
3
|
+
Version: 0.1.6
|
|
4
|
+
Summary: Data Cloud Custom Code SDK
|
|
5
|
+
License: Apache-2.0
|
|
6
|
+
Requires-Python: >=3.10,<3.12
|
|
7
|
+
Classifier: Development Status :: 4 - Beta
|
|
8
|
+
Classifier: Operating System :: Unix
|
|
9
|
+
Classifier: Programming Language :: Python
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
13
|
+
Requires-Dist: click (>=8.1.8,<9.0.0)
|
|
14
|
+
Requires-Dist: loguru (>=0.7.3,<0.8.0)
|
|
15
|
+
Requires-Dist: numpy
|
|
16
|
+
Requires-Dist: pandas
|
|
17
|
+
Requires-Dist: pydantic (>=1.8.2,<3.0.0)
|
|
18
|
+
Requires-Dist: pyspark
|
|
19
|
+
Requires-Dist: pyyaml (>=6.0,<7.0)
|
|
20
|
+
Requires-Dist: salesforce-cdp-connector
|
|
21
|
+
Requires-Dist: setuptools_scm (>=7.1.0,<8.0.0)
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
|
|
24
|
+
# Data Cloud Custom Code SDK
|
|
25
|
+
|
|
26
|
+
This package provides a development kit for creating custom data transformations in [Data Cloud](https://www.salesforce.com/data/). It allows you to write your own data processing logic in Python while leveraging Data Cloud's infrastructure for data access and running data transformations, mapping execution into Data Cloud data structures like [Data Model Objects](https://help.salesforce.com/s/articleView?id=data.c360_a_data_model_objects.htm&type=5) and [Data Lake Objects](https://help.salesforce.com/s/articleView?id=sf.c360_a_data_lake_objects.htm&language=en_US&type=5).
|
|
27
|
+
|
|
28
|
+
More specifically, this codebase gives you ability to test code locally before pushing to Data Cloud's remote execution engine, greatly reducing how long it takes to develop.
|
|
29
|
+
|
|
30
|
+
Use of this project with Salesforce is subject to the [TERMS OF USE](./TERMS_OF_USE.md)
|
|
31
|
+
|
|
32
|
+
## Prerequisites
|
|
33
|
+
|
|
34
|
+
- Python 3.11 (If your system version is different, we recommend using [pyenv](https://github.com/pyenv/pyenv) to configure 3.11)
|
|
35
|
+
- [Azul Zulu OpenJDK 17.x](https://www.azul.com/downloads/?version=java-17-lts&package=jdk#zulu)
|
|
36
|
+
- Docker support like [Docker Desktop](https://docs.docker.com/desktop/)
|
|
37
|
+
- A salesforce org, with some DLOs or DMOs with data
|
|
38
|
+
- A [connected app](#creating-a-connected-app)
|
|
39
|
+
|
|
40
|
+
## Installation
|
|
41
|
+
The SDK can be downloaded directly from PyPI with `pip`:
|
|
42
|
+
```
|
|
43
|
+
pip install salesforce-data-customcode
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
You can verify it was properly installed via CLI:
|
|
47
|
+
```
|
|
48
|
+
datacustomcode version
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Quick start
|
|
52
|
+
Ensure you have all the [prerequisites](#prerequisites) prepared on your machine.
|
|
53
|
+
|
|
54
|
+
To get started, create a directory and initialize a new project with the CLI:
|
|
55
|
+
```zsh
|
|
56
|
+
mkdir datacloud && cd datacloud
|
|
57
|
+
python3.11 -m venv .venv
|
|
58
|
+
source .venv/bin/activate
|
|
59
|
+
pip install salesforce-data-customcode
|
|
60
|
+
datacustomcode init my_package
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
This will yield all necessary files to get started:
|
|
64
|
+
```
|
|
65
|
+
.
|
|
66
|
+
├── Dockerfile
|
|
67
|
+
├── README.md
|
|
68
|
+
├── requirements.txt
|
|
69
|
+
├── requirements-dev.txt
|
|
70
|
+
├── payload
|
|
71
|
+
│ ├── config.json
|
|
72
|
+
│ ├── entrypoint.py
|
|
73
|
+
├── jupyterlab.sh
|
|
74
|
+
└── requirements.txt
|
|
75
|
+
```
|
|
76
|
+
* `Dockerfile` <span style="color:grey;font-style:italic;">(Do not update)</span> – Development container emulating the remote execution environment.
|
|
77
|
+
* `requirements-dev.txt` <span style="color:grey;font-style:italic;">(Do not update)</span> – These are the dependencies for the development environment.
|
|
78
|
+
* `jupyterlab.sh` <span style="color:grey;font-style:italic;">(Do not update)</span> – Helper script for setting up Jupyter.
|
|
79
|
+
* `requirements.txt` – Here you define the requirements that you will need for your script.
|
|
80
|
+
* `payload` – This folder will be compressed and deployed to the remote execution environment.
|
|
81
|
+
* `config.json` – This config defines permissions on the back and can be generated programmatically with `scan` CLI method.
|
|
82
|
+
* `entrypoint.py` – The script that defines the data transformation logic.
|
|
83
|
+
|
|
84
|
+
A functional entrypoint.py is provided so you can run once you've configured your connected app:
|
|
85
|
+
```zsh
|
|
86
|
+
cd my_package
|
|
87
|
+
datacustomcode configure
|
|
88
|
+
datacustomcode run ./payload/entrypoint.py
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
> [!IMPORTANT]
|
|
92
|
+
> The example entrypoint.py requires a `Account_Home__dll` DLO to be present. And in order to deploy the script (next step), the output DLO (which is `Account_Home_copy__dll` in the example entrypoint.py) also needs to exist and be in the same dataspace as `Account_Home__dll`.
|
|
93
|
+
|
|
94
|
+
After modifying the `entrypoint.py` as needed, using any dependencies you add in the `.venv` virtual environment, you can run this script in Data Cloud:
|
|
95
|
+
```zsh
|
|
96
|
+
datacustomcode scan ./payload/entrypoint.py
|
|
97
|
+
datacustomcode deploy --path ./payload --name my_custom_script
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
> [!TIP]
|
|
101
|
+
> The `deploy` process can take several minutes. If you'd like more feedback on the underlying process, you can add `--debug` to the command like `datacustomcode --debug deploy --path ./payload --name my_custom_script`
|
|
102
|
+
|
|
103
|
+
You can now use the Salesforce Data Cloud UI to find the created Data Transform and use the `Run Now` button to run it.
|
|
104
|
+
Once the Data Transform run is successful, check the DLO your script is writing to and verify the correct records were added.
|
|
105
|
+
|
|
106
|
+
## API
|
|
107
|
+
|
|
108
|
+
Your entry point script will define logic using the `Client` object which wraps data access layers.
|
|
109
|
+
|
|
110
|
+
You should only need the following methods:
|
|
111
|
+
* `read_dlo(name)` – Read from a Data Lake Object by name
|
|
112
|
+
* `read_dmo(name)` – Read from a Data Model Object by name
|
|
113
|
+
* `write_to_dlo(name, spark_dataframe, write_mode)` – Write to a Data Model Object by name with a Spark dataframe
|
|
114
|
+
* `write_to_dmo(name, spark_dataframe, write_mode)` – Write to a Data Lake Object by name with a Spark dataframe
|
|
115
|
+
|
|
116
|
+
For example:
|
|
117
|
+
```
|
|
118
|
+
from datacustomcode import Client
|
|
119
|
+
|
|
120
|
+
client = Client()
|
|
121
|
+
|
|
122
|
+
sdf = client.read_dlo('my_DLO')
|
|
123
|
+
# some transformations
|
|
124
|
+
# ...
|
|
125
|
+
client.write_to_dlo('output_DLO')
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
> [!WARNING]
|
|
130
|
+
> Currently we only support reading from DMOs and writing to DMOs or reading from DLOs and writing to DLOs, but they cannot mix.
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
## CLI
|
|
134
|
+
|
|
135
|
+
The Data Cloud Custom Code SDK provides a command-line interface (CLI) with the following commands:
|
|
136
|
+
|
|
137
|
+
### Global Options
|
|
138
|
+
- `--debug`: Enable debug-level logging
|
|
139
|
+
|
|
140
|
+
### Commands
|
|
141
|
+
|
|
142
|
+
#### `datacustomcode version`
|
|
143
|
+
Display the current version of the package.
|
|
144
|
+
|
|
145
|
+
#### `datacustomcode configure`
|
|
146
|
+
Configure credentials for connecting to Data Cloud.
|
|
147
|
+
|
|
148
|
+
Options:
|
|
149
|
+
- `--profile TEXT`: Credential profile name (default: "default")
|
|
150
|
+
- `--username TEXT`: Salesforce username
|
|
151
|
+
- `--password TEXT`: Salesforce password
|
|
152
|
+
- `--client-id TEXT`: Connected App Client ID
|
|
153
|
+
- `--client-secret TEXT`: Connected App Client Secret
|
|
154
|
+
- `--login-url TEXT`: Salesforce login URL
|
|
155
|
+
|
|
156
|
+
#### `datacustomcode deploy`
|
|
157
|
+
Deploy a transformation job to Data Cloud.
|
|
158
|
+
|
|
159
|
+
Options:
|
|
160
|
+
- `--profile TEXT`: Credential profile name (default: "default")
|
|
161
|
+
- `--path TEXT`: Path to the code directory (default: ".")
|
|
162
|
+
- `--name TEXT`: Name of the transformation job [required]
|
|
163
|
+
- `--version TEXT`: Version of the transformation job (default: "0.0.1")
|
|
164
|
+
- `--description TEXT`: Description of the transformation job (default: "")
|
|
165
|
+
|
|
166
|
+
#### `datacustomcode init`
|
|
167
|
+
Initialize a new development environment with a template.
|
|
168
|
+
|
|
169
|
+
Argument:
|
|
170
|
+
- `DIRECTORY`: Directory to create project in (default: ".")
|
|
171
|
+
|
|
172
|
+
#### `datacustomcode scan`
|
|
173
|
+
Scan a Python file to generate a Data Cloud configuration.
|
|
174
|
+
|
|
175
|
+
Argument:
|
|
176
|
+
- `FILENAME`: Python file to scan
|
|
177
|
+
|
|
178
|
+
Options:
|
|
179
|
+
- `--config TEXT`: Path to save the configuration file (default: same directory as FILENAME)
|
|
180
|
+
- `--dry-run`: Preview the configuration without saving to a file
|
|
181
|
+
|
|
182
|
+
#### `datacustomcode run`
|
|
183
|
+
Run an entrypoint file locally for testing.
|
|
184
|
+
|
|
185
|
+
Argument:
|
|
186
|
+
- `ENTRYPOINT`: Path to entrypoint Python file
|
|
187
|
+
|
|
188
|
+
Options:
|
|
189
|
+
- `--config-file TEXT`: Path to configuration file
|
|
190
|
+
- `--dependencies TEXT`: Additional dependencies (can be specified multiple times)
|
|
191
|
+
|
|
192
|
+
#### `datacustomcode zip`
|
|
193
|
+
Zip a transformation job in preparation to upload to Data Cloud.
|
|
194
|
+
|
|
195
|
+
Options:
|
|
196
|
+
- `--path TEXT`: Path to the code directory (default: ".")
|
|
197
|
+
|
|
198
|
+
## Docker usage
|
|
199
|
+
|
|
200
|
+
After initializing a project with `datacustomcode init my_package`, you might notice a Dockerfile. This file isn't used for the
|
|
201
|
+
[Quick Start](#quick-start) approach above, which uses virtual environments, until the `zip` or `deploy` commands are used. When using dependencies
|
|
202
|
+
that include [native features](https://spark.apache.org/docs/latest/api/python/user_guide/python_packaging.html#using-pyspark-native-features)
|
|
203
|
+
like C++ or C interop, the platform and architecture may be different between your machine and Data Cloud compute. This is all taken care of
|
|
204
|
+
in the `zip` and `deploy` commands, which utilize the Dockerfile which starts `FROM` an image compatible with Data Cloud. However, you may
|
|
205
|
+
want to build, run, and test your script on your machine using the same platform and architecture as Data Cloud. You can use the sections below
|
|
206
|
+
to test your script in this manner.
|
|
207
|
+
|
|
208
|
+
### VS Code Dev Containers
|
|
209
|
+
|
|
210
|
+
Within your `init`ed package, you will find a `.devcontainer` folder which allows you to run a docker container while developing inside of it.
|
|
211
|
+
|
|
212
|
+
Read more about Dev Containers here: https://code.visualstudio.com/docs/devcontainers/containers.
|
|
213
|
+
|
|
214
|
+
1. Install the VS Code extension "Dev Containers" by microsoft.com.
|
|
215
|
+
1. Open your package folder in VS Code, ensuring that the `.devcontainer` folder is at the root of the File Explorer
|
|
216
|
+
1. Bring up the Command Palette (on mac: Cmd + Shift + P), and select "Dev Containers: Rebuild and Reopen in Container"
|
|
217
|
+
1. Allow the docker image to be built, then you're ready to develop
|
|
218
|
+
1. Now if you open a terminal (within the Dev Container window) and `datacustomcode run ./payload/entrypoint.py`, it will run inside a docker container that more closely resembles Data Cloud compute than your machine
|
|
219
|
+
|
|
220
|
+
> [!IMPORTANT]
|
|
221
|
+
> Dev Containers get their own tmp file storage, so you'll need to re-run `datacustomcode configure` every time you "Rebuild and Reopen in Container".
|
|
222
|
+
|
|
223
|
+
### JupyterLab
|
|
224
|
+
|
|
225
|
+
Within your `init`ed package, you will find a `jupyterlab.sh` file that can open a jupyter notebook for you. Jupyter notebooks, in
|
|
226
|
+
combination with Data Cloud's [Query Editor](https://help.salesforce.com/s/articleView?id=data.c360_a_add_queries_to_a_query_workspace.htm&type=5)
|
|
227
|
+
and [Data Explorer](https://help.salesforce.com/s/articleView?id=data.c360_a_data_explorer.htm&type=5), can be extremely helpful for data
|
|
228
|
+
exploration. Instead of running an entire script, one can run one code cell at a time as they discover and experiment with the DLO or DMO data.
|
|
229
|
+
|
|
230
|
+
You can read more about Jupyter Notebooks here: https://jupyter.org/
|
|
231
|
+
|
|
232
|
+
1. Within the root project of your package folder, run `./jupyterlab.sh start`
|
|
233
|
+
1. Double-click on "account.ipynb" file, which provides a starting point for a notebook
|
|
234
|
+
1. Use shift+enter to execute each cell within the notebook. Add/edit/delete cells of code as needed for your data exploration.
|
|
235
|
+
1. Don't forget to run `./jupyterlab.sh stop` to stop the docker container
|
|
236
|
+
|
|
237
|
+
> [!IMPORTANT]
|
|
238
|
+
> JupyterLab uses its own tmp file storage, so you'll need to re-run `datacustomcode configure` each time you `./jupyterlab.sh start`.
|
|
239
|
+
|
|
240
|
+
## Prerequisite details
|
|
241
|
+
|
|
242
|
+
### Creating a connected app
|
|
243
|
+
|
|
244
|
+
1. Log in to salesforce as an admin. In the top right corner, click on the gear icon and go to `Setup`
|
|
245
|
+
2. In the left hand side, search for "App Manager" and select the `App Manager` underneath `Apps`
|
|
246
|
+
3. Click on `New Connected App` in the upper right
|
|
247
|
+
4. Fill in the required fields within the `Basic Information` section
|
|
248
|
+
5. Under the `API (Enable OAuth Settings)` section:
|
|
249
|
+
1. Click on the checkbox to Enable OAuth Settings.
|
|
250
|
+
2. Provide a callback URL like http://localhost:55555/callback
|
|
251
|
+
3. In the Selected OAuth Scopes, make sure that `refresh_token`, `api`, `cdp_query_api`, `cdp_profile_api` is selected.
|
|
252
|
+
4. Click on Save to save the connected app
|
|
253
|
+
6. From the detail page that opens up afterwards, click the "Manage Consumer Details" button to find your client id and client secret
|
|
254
|
+
7. Go back to `Setup`, then `OAuth and OpenID Connect Settings`, and enable the "Allow OAuth Username-Password Flows" option
|
|
255
|
+
|
|
256
|
+
You now have all fields necessary for the `datacustomcode configure` command.
|
|
257
|
+
|
|
258
|
+
## Other docs
|
|
259
|
+
|
|
260
|
+
[Troubleshooting](./docs/troubleshooting.md)
|
|
261
|
+
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
# Data Cloud Custom Code SDK
|
|
2
|
+
|
|
3
|
+
This package provides a development kit for creating custom data transformations in [Data Cloud](https://www.salesforce.com/data/). It allows you to write your own data processing logic in Python while leveraging Data Cloud's infrastructure for data access and running data transformations, mapping execution into Data Cloud data structures like [Data Model Objects](https://help.salesforce.com/s/articleView?id=data.c360_a_data_model_objects.htm&type=5) and [Data Lake Objects](https://help.salesforce.com/s/articleView?id=sf.c360_a_data_lake_objects.htm&language=en_US&type=5).
|
|
4
|
+
|
|
5
|
+
More specifically, this codebase gives you ability to test code locally before pushing to Data Cloud's remote execution engine, greatly reducing how long it takes to develop.
|
|
6
|
+
|
|
7
|
+
Use of this project with Salesforce is subject to the [TERMS OF USE](./TERMS_OF_USE.md)
|
|
8
|
+
|
|
9
|
+
## Prerequisites
|
|
10
|
+
|
|
11
|
+
- Python 3.11 (If your system version is different, we recommend using [pyenv](https://github.com/pyenv/pyenv) to configure 3.11)
|
|
12
|
+
- [Azul Zulu OpenJDK 17.x](https://www.azul.com/downloads/?version=java-17-lts&package=jdk#zulu)
|
|
13
|
+
- Docker support like [Docker Desktop](https://docs.docker.com/desktop/)
|
|
14
|
+
- A salesforce org, with some DLOs or DMOs with data
|
|
15
|
+
- A [connected app](#creating-a-connected-app)
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
The SDK can be downloaded directly from PyPI with `pip`:
|
|
19
|
+
```
|
|
20
|
+
pip install salesforce-data-customcode
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
You can verify it was properly installed via CLI:
|
|
24
|
+
```
|
|
25
|
+
datacustomcode version
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Quick start
|
|
29
|
+
Ensure you have all the [prerequisites](#prerequisites) prepared on your machine.
|
|
30
|
+
|
|
31
|
+
To get started, create a directory and initialize a new project with the CLI:
|
|
32
|
+
```zsh
|
|
33
|
+
mkdir datacloud && cd datacloud
|
|
34
|
+
python3.11 -m venv .venv
|
|
35
|
+
source .venv/bin/activate
|
|
36
|
+
pip install salesforce-data-customcode
|
|
37
|
+
datacustomcode init my_package
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
This will yield all necessary files to get started:
|
|
41
|
+
```
|
|
42
|
+
.
|
|
43
|
+
├── Dockerfile
|
|
44
|
+
├── README.md
|
|
45
|
+
├── requirements.txt
|
|
46
|
+
├── requirements-dev.txt
|
|
47
|
+
├── payload
|
|
48
|
+
│ ├── config.json
|
|
49
|
+
│ ├── entrypoint.py
|
|
50
|
+
├── jupyterlab.sh
|
|
51
|
+
└── requirements.txt
|
|
52
|
+
```
|
|
53
|
+
* `Dockerfile` <span style="color:grey;font-style:italic;">(Do not update)</span> – Development container emulating the remote execution environment.
|
|
54
|
+
* `requirements-dev.txt` <span style="color:grey;font-style:italic;">(Do not update)</span> – These are the dependencies for the development environment.
|
|
55
|
+
* `jupyterlab.sh` <span style="color:grey;font-style:italic;">(Do not update)</span> – Helper script for setting up Jupyter.
|
|
56
|
+
* `requirements.txt` – Here you define the requirements that you will need for your script.
|
|
57
|
+
* `payload` – This folder will be compressed and deployed to the remote execution environment.
|
|
58
|
+
* `config.json` – This config defines permissions on the back and can be generated programmatically with `scan` CLI method.
|
|
59
|
+
* `entrypoint.py` – The script that defines the data transformation logic.
|
|
60
|
+
|
|
61
|
+
A functional entrypoint.py is provided so you can run once you've configured your connected app:
|
|
62
|
+
```zsh
|
|
63
|
+
cd my_package
|
|
64
|
+
datacustomcode configure
|
|
65
|
+
datacustomcode run ./payload/entrypoint.py
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
> [!IMPORTANT]
|
|
69
|
+
> The example entrypoint.py requires a `Account_Home__dll` DLO to be present. And in order to deploy the script (next step), the output DLO (which is `Account_Home_copy__dll` in the example entrypoint.py) also needs to exist and be in the same dataspace as `Account_Home__dll`.
|
|
70
|
+
|
|
71
|
+
After modifying the `entrypoint.py` as needed, using any dependencies you add in the `.venv` virtual environment, you can run this script in Data Cloud:
|
|
72
|
+
```zsh
|
|
73
|
+
datacustomcode scan ./payload/entrypoint.py
|
|
74
|
+
datacustomcode deploy --path ./payload --name my_custom_script
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
> [!TIP]
|
|
78
|
+
> The `deploy` process can take several minutes. If you'd like more feedback on the underlying process, you can add `--debug` to the command like `datacustomcode --debug deploy --path ./payload --name my_custom_script`
|
|
79
|
+
|
|
80
|
+
You can now use the Salesforce Data Cloud UI to find the created Data Transform and use the `Run Now` button to run it.
|
|
81
|
+
Once the Data Transform run is successful, check the DLO your script is writing to and verify the correct records were added.
|
|
82
|
+
|
|
83
|
+
## API
|
|
84
|
+
|
|
85
|
+
Your entry point script will define logic using the `Client` object which wraps data access layers.
|
|
86
|
+
|
|
87
|
+
You should only need the following methods:
|
|
88
|
+
* `read_dlo(name)` – Read from a Data Lake Object by name
|
|
89
|
+
* `read_dmo(name)` – Read from a Data Model Object by name
|
|
90
|
+
* `write_to_dlo(name, spark_dataframe, write_mode)` – Write to a Data Model Object by name with a Spark dataframe
|
|
91
|
+
* `write_to_dmo(name, spark_dataframe, write_mode)` – Write to a Data Lake Object by name with a Spark dataframe
|
|
92
|
+
|
|
93
|
+
For example:
|
|
94
|
+
```
|
|
95
|
+
from datacustomcode import Client
|
|
96
|
+
|
|
97
|
+
client = Client()
|
|
98
|
+
|
|
99
|
+
sdf = client.read_dlo('my_DLO')
|
|
100
|
+
# some transformations
|
|
101
|
+
# ...
|
|
102
|
+
client.write_to_dlo('output_DLO')
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
> [!WARNING]
|
|
107
|
+
> Currently we only support reading from DMOs and writing to DMOs or reading from DLOs and writing to DLOs, but they cannot mix.
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
## CLI
|
|
111
|
+
|
|
112
|
+
The Data Cloud Custom Code SDK provides a command-line interface (CLI) with the following commands:
|
|
113
|
+
|
|
114
|
+
### Global Options
|
|
115
|
+
- `--debug`: Enable debug-level logging
|
|
116
|
+
|
|
117
|
+
### Commands
|
|
118
|
+
|
|
119
|
+
#### `datacustomcode version`
|
|
120
|
+
Display the current version of the package.
|
|
121
|
+
|
|
122
|
+
#### `datacustomcode configure`
|
|
123
|
+
Configure credentials for connecting to Data Cloud.
|
|
124
|
+
|
|
125
|
+
Options:
|
|
126
|
+
- `--profile TEXT`: Credential profile name (default: "default")
|
|
127
|
+
- `--username TEXT`: Salesforce username
|
|
128
|
+
- `--password TEXT`: Salesforce password
|
|
129
|
+
- `--client-id TEXT`: Connected App Client ID
|
|
130
|
+
- `--client-secret TEXT`: Connected App Client Secret
|
|
131
|
+
- `--login-url TEXT`: Salesforce login URL
|
|
132
|
+
|
|
133
|
+
#### `datacustomcode deploy`
|
|
134
|
+
Deploy a transformation job to Data Cloud.
|
|
135
|
+
|
|
136
|
+
Options:
|
|
137
|
+
- `--profile TEXT`: Credential profile name (default: "default")
|
|
138
|
+
- `--path TEXT`: Path to the code directory (default: ".")
|
|
139
|
+
- `--name TEXT`: Name of the transformation job [required]
|
|
140
|
+
- `--version TEXT`: Version of the transformation job (default: "0.0.1")
|
|
141
|
+
- `--description TEXT`: Description of the transformation job (default: "")
|
|
142
|
+
|
|
143
|
+
#### `datacustomcode init`
|
|
144
|
+
Initialize a new development environment with a template.
|
|
145
|
+
|
|
146
|
+
Argument:
|
|
147
|
+
- `DIRECTORY`: Directory to create project in (default: ".")
|
|
148
|
+
|
|
149
|
+
#### `datacustomcode scan`
|
|
150
|
+
Scan a Python file to generate a Data Cloud configuration.
|
|
151
|
+
|
|
152
|
+
Argument:
|
|
153
|
+
- `FILENAME`: Python file to scan
|
|
154
|
+
|
|
155
|
+
Options:
|
|
156
|
+
- `--config TEXT`: Path to save the configuration file (default: same directory as FILENAME)
|
|
157
|
+
- `--dry-run`: Preview the configuration without saving to a file
|
|
158
|
+
|
|
159
|
+
#### `datacustomcode run`
|
|
160
|
+
Run an entrypoint file locally for testing.
|
|
161
|
+
|
|
162
|
+
Argument:
|
|
163
|
+
- `ENTRYPOINT`: Path to entrypoint Python file
|
|
164
|
+
|
|
165
|
+
Options:
|
|
166
|
+
- `--config-file TEXT`: Path to configuration file
|
|
167
|
+
- `--dependencies TEXT`: Additional dependencies (can be specified multiple times)
|
|
168
|
+
|
|
169
|
+
#### `datacustomcode zip`
|
|
170
|
+
Zip a transformation job in preparation to upload to Data Cloud.
|
|
171
|
+
|
|
172
|
+
Options:
|
|
173
|
+
- `--path TEXT`: Path to the code directory (default: ".")
|
|
174
|
+
|
|
175
|
+
## Docker usage
|
|
176
|
+
|
|
177
|
+
After initializing a project with `datacustomcode init my_package`, you might notice a Dockerfile. This file isn't used for the
|
|
178
|
+
[Quick Start](#quick-start) approach above, which uses virtual environments, until the `zip` or `deploy` commands are used. When using dependencies
|
|
179
|
+
that include [native features](https://spark.apache.org/docs/latest/api/python/user_guide/python_packaging.html#using-pyspark-native-features)
|
|
180
|
+
like C++ or C interop, the platform and architecture may be different between your machine and Data Cloud compute. This is all taken care of
|
|
181
|
+
in the `zip` and `deploy` commands, which utilize the Dockerfile which starts `FROM` an image compatible with Data Cloud. However, you may
|
|
182
|
+
want to build, run, and test your script on your machine using the same platform and architecture as Data Cloud. You can use the sections below
|
|
183
|
+
to test your script in this manner.
|
|
184
|
+
|
|
185
|
+
### VS Code Dev Containers
|
|
186
|
+
|
|
187
|
+
Within your `init`ed package, you will find a `.devcontainer` folder which allows you to run a docker container while developing inside of it.
|
|
188
|
+
|
|
189
|
+
Read more about Dev Containers here: https://code.visualstudio.com/docs/devcontainers/containers.
|
|
190
|
+
|
|
191
|
+
1. Install the VS Code extension "Dev Containers" by microsoft.com.
|
|
192
|
+
1. Open your package folder in VS Code, ensuring that the `.devcontainer` folder is at the root of the File Explorer
|
|
193
|
+
1. Bring up the Command Palette (on mac: Cmd + Shift + P), and select "Dev Containers: Rebuild and Reopen in Container"
|
|
194
|
+
1. Allow the docker image to be built, then you're ready to develop
|
|
195
|
+
1. Now if you open a terminal (within the Dev Container window) and `datacustomcode run ./payload/entrypoint.py`, it will run inside a docker container that more closely resembles Data Cloud compute than your machine
|
|
196
|
+
|
|
197
|
+
> [!IMPORTANT]
|
|
198
|
+
> Dev Containers get their own tmp file storage, so you'll need to re-run `datacustomcode configure` every time you "Rebuild and Reopen in Container".
|
|
199
|
+
|
|
200
|
+
### JupyterLab
|
|
201
|
+
|
|
202
|
+
Within your `init`ed package, you will find a `jupyterlab.sh` file that can open a jupyter notebook for you. Jupyter notebooks, in
|
|
203
|
+
combination with Data Cloud's [Query Editor](https://help.salesforce.com/s/articleView?id=data.c360_a_add_queries_to_a_query_workspace.htm&type=5)
|
|
204
|
+
and [Data Explorer](https://help.salesforce.com/s/articleView?id=data.c360_a_data_explorer.htm&type=5), can be extremely helpful for data
|
|
205
|
+
exploration. Instead of running an entire script, one can run one code cell at a time as they discover and experiment with the DLO or DMO data.
|
|
206
|
+
|
|
207
|
+
You can read more about Jupyter Notebooks here: https://jupyter.org/
|
|
208
|
+
|
|
209
|
+
1. Within the root project of your package folder, run `./jupyterlab.sh start`
|
|
210
|
+
1. Double-click on "account.ipynb" file, which provides a starting point for a notebook
|
|
211
|
+
1. Use shift+enter to execute each cell within the notebook. Add/edit/delete cells of code as needed for your data exploration.
|
|
212
|
+
1. Don't forget to run `./jupyterlab.sh stop` to stop the docker container
|
|
213
|
+
|
|
214
|
+
> [!IMPORTANT]
|
|
215
|
+
> JupyterLab uses its own tmp file storage, so you'll need to re-run `datacustomcode configure` each time you `./jupyterlab.sh start`.
|
|
216
|
+
|
|
217
|
+
## Prerequisite details
|
|
218
|
+
|
|
219
|
+
### Creating a connected app
|
|
220
|
+
|
|
221
|
+
1. Log in to salesforce as an admin. In the top right corner, click on the gear icon and go to `Setup`
|
|
222
|
+
2. In the left hand side, search for "App Manager" and select the `App Manager` underneath `Apps`
|
|
223
|
+
3. Click on `New Connected App` in the upper right
|
|
224
|
+
4. Fill in the required fields within the `Basic Information` section
|
|
225
|
+
5. Under the `API (Enable OAuth Settings)` section:
|
|
226
|
+
1. Click on the checkbox to Enable OAuth Settings.
|
|
227
|
+
2. Provide a callback URL like http://localhost:55555/callback
|
|
228
|
+
3. In the Selected OAuth Scopes, make sure that `refresh_token`, `api`, `cdp_query_api`, `cdp_profile_api` is selected.
|
|
229
|
+
4. Click on Save to save the connected app
|
|
230
|
+
6. From the detail page that opens up afterwards, click the "Manage Consumer Details" button to find your client id and client secret
|
|
231
|
+
7. Go back to `Setup`, then `OAuth and OpenID Connect Settings`, and enable the "Allow OAuth Username-Password Flows" option
|
|
232
|
+
|
|
233
|
+
You now have all fields necessary for the `datacustomcode configure` command.
|
|
234
|
+
|
|
235
|
+
## Other docs
|
|
236
|
+
|
|
237
|
+
[Troubleshooting](./docs/troubleshooting.md)
|
{salesforce_data_customcode-0.1.4 → salesforce_data_customcode-0.1.6}/src/datacustomcode/cli.py
RENAMED
|
@@ -69,6 +69,15 @@ def configure(
|
|
|
69
69
|
).update_ini(profile=profile)
|
|
70
70
|
|
|
71
71
|
|
|
72
|
+
@cli.command()
|
|
73
|
+
@click.argument("path", default="payload")
|
|
74
|
+
def zip(path: str):
|
|
75
|
+
from datacustomcode.deploy import zip
|
|
76
|
+
|
|
77
|
+
logger.debug("Zipping project")
|
|
78
|
+
zip(path)
|
|
79
|
+
|
|
80
|
+
|
|
72
81
|
@cli.command()
|
|
73
82
|
@click.option("--profile", default="default")
|
|
74
83
|
@click.option("--path", default="payload")
|
|
@@ -127,8 +136,11 @@ def init(directory: str):
|
|
|
127
136
|
@click.argument("filename")
|
|
128
137
|
@click.option("--config")
|
|
129
138
|
@click.option("--dry-run", is_flag=True)
|
|
130
|
-
|
|
131
|
-
|
|
139
|
+
@click.option(
|
|
140
|
+
"--no-requirements", is_flag=True, help="Skip generating requirements.txt file"
|
|
141
|
+
)
|
|
142
|
+
def scan(filename: str, config: str, dry_run: bool, no_requirements: bool):
|
|
143
|
+
from datacustomcode.scan import dc_config_json_from_file, write_requirements_file
|
|
132
144
|
|
|
133
145
|
config_location = config or os.path.join(os.path.dirname(filename), "config.json")
|
|
134
146
|
click.echo(
|
|
@@ -143,6 +155,13 @@ def scan(filename: str, config: str, dry_run: bool):
|
|
|
143
155
|
with open(config_location, "w") as f:
|
|
144
156
|
json.dump(config_json, f, indent=2)
|
|
145
157
|
|
|
158
|
+
if not no_requirements:
|
|
159
|
+
requirements_path = write_requirements_file(filename)
|
|
160
|
+
click.echo(
|
|
161
|
+
"Generated requirements file: "
|
|
162
|
+
+ click.style(requirements_path, fg="blue", bold=True)
|
|
163
|
+
)
|
|
164
|
+
|
|
146
165
|
|
|
147
166
|
@cli.command()
|
|
148
167
|
@click.argument("entrypoint")
|
{salesforce_data_customcode-0.1.4 → salesforce_data_customcode-0.1.6}/src/datacustomcode/deploy.py
RENAMED
|
@@ -169,25 +169,14 @@ def prepare_dependency_archive(directory: str) -> None:
|
|
|
169
169
|
archive_file = os.path.join(archives_dir, DEPENDENCIES_ARCHIVE_NAME)
|
|
170
170
|
with tarfile.open(archive_file, "w:gz") as tar:
|
|
171
171
|
for file in os.listdir(temp_dir):
|
|
172
|
+
# Exclude requirements.txt from the archive
|
|
173
|
+
if file == "requirements.txt":
|
|
174
|
+
continue
|
|
172
175
|
tar.add(os.path.join(temp_dir, file), arcname=file)
|
|
173
176
|
|
|
174
177
|
logger.debug(f"Dependencies downloaded and archived to {archive_file}")
|
|
175
178
|
|
|
176
179
|
|
|
177
|
-
def zip_and_upload_directory(directory: str, file_upload_url: str) -> None:
|
|
178
|
-
file_upload_url = unescape(file_upload_url)
|
|
179
|
-
|
|
180
|
-
logger.debug(f"Zipping directory... {directory}")
|
|
181
|
-
shutil.make_archive(ZIP_FILE_NAME.rstrip(".zip"), "zip", directory)
|
|
182
|
-
|
|
183
|
-
logger.debug(f"Uploading deployment to {file_upload_url}")
|
|
184
|
-
with open(ZIP_FILE_NAME, "rb") as zip_file:
|
|
185
|
-
response = requests.put(
|
|
186
|
-
file_upload_url, data=zip_file, headers={"Content-Type": "application/zip"}
|
|
187
|
-
)
|
|
188
|
-
response.raise_for_status()
|
|
189
|
-
|
|
190
|
-
|
|
191
180
|
class DeploymentsResponse(BaseModel):
|
|
192
181
|
deploymentStatus: str
|
|
193
182
|
|
|
@@ -325,6 +314,69 @@ def create_data_transform(
|
|
|
325
314
|
return response
|
|
326
315
|
|
|
327
316
|
|
|
317
|
+
def has_nonempty_requirements_file(directory: str) -> bool:
|
|
318
|
+
"""
|
|
319
|
+
Check if requirements.txt exists in the given directory and has at least
|
|
320
|
+
one non-comment line.
|
|
321
|
+
Args:
|
|
322
|
+
directory (str): The directory to check for requirements.txt.
|
|
323
|
+
Returns:
|
|
324
|
+
bool: True if requirements.txt exists and has a non-comment line,
|
|
325
|
+
False otherwise.
|
|
326
|
+
"""
|
|
327
|
+
# Look for requirements.txt in the parent directory of the given directory
|
|
328
|
+
requirements_path = os.path.join(os.path.dirname(directory), "requirements.txt")
|
|
329
|
+
|
|
330
|
+
try:
|
|
331
|
+
if os.path.isfile(requirements_path):
|
|
332
|
+
with open(requirements_path, "r", encoding="utf-8") as f:
|
|
333
|
+
for line in f:
|
|
334
|
+
# Consider non-empty if any line is not a comment (ignoring
|
|
335
|
+
# leading whitespace)
|
|
336
|
+
if line.strip() and not line.lstrip().startswith("#"):
|
|
337
|
+
return True
|
|
338
|
+
except Exception as e:
|
|
339
|
+
logger.error(f"Error reading requirements.txt: {e}")
|
|
340
|
+
return False
|
|
341
|
+
|
|
342
|
+
|
|
343
|
+
def upload_zip(file_upload_url: str) -> None:
|
|
344
|
+
file_upload_url = unescape(file_upload_url)
|
|
345
|
+
with open(ZIP_FILE_NAME, "rb") as zip_file:
|
|
346
|
+
response = requests.put(
|
|
347
|
+
file_upload_url, data=zip_file, headers={"Content-Type": "application/zip"}
|
|
348
|
+
)
|
|
349
|
+
response.raise_for_status()
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
def zip(
|
|
353
|
+
directory: str,
|
|
354
|
+
):
|
|
355
|
+
# Create a zip file excluding .DS_Store files
|
|
356
|
+
import zipfile
|
|
357
|
+
|
|
358
|
+
# prepare payload only if requirements.txt is non-empty
|
|
359
|
+
if has_nonempty_requirements_file(directory):
|
|
360
|
+
prepare_dependency_archive(directory)
|
|
361
|
+
else:
|
|
362
|
+
logger.info(
|
|
363
|
+
f"Skipping dependency archive: requirements.txt is missing or empty "
|
|
364
|
+
f"in {directory}"
|
|
365
|
+
)
|
|
366
|
+
|
|
367
|
+
logger.debug(f"Zipping directory... {directory}")
|
|
368
|
+
|
|
369
|
+
with zipfile.ZipFile(ZIP_FILE_NAME, "w", zipfile.ZIP_DEFLATED) as zipf:
|
|
370
|
+
for root, dirs, files in os.walk(directory):
|
|
371
|
+
# Skip .DS_Store files when adding to zip
|
|
372
|
+
for file in files:
|
|
373
|
+
if file != ".DS_Store":
|
|
374
|
+
file_path = os.path.join(root, file)
|
|
375
|
+
zipf.write(file_path)
|
|
376
|
+
|
|
377
|
+
logger.debug(f"Created zip file: {ZIP_FILE_NAME}")
|
|
378
|
+
|
|
379
|
+
|
|
328
380
|
def deploy_full(
|
|
329
381
|
directory: str,
|
|
330
382
|
metadata: TransformationJobMetadata,
|
|
@@ -340,7 +392,8 @@ def deploy_full(
|
|
|
340
392
|
|
|
341
393
|
# create deployment and upload payload
|
|
342
394
|
deployment = create_deployment(access_token, metadata)
|
|
343
|
-
|
|
395
|
+
zip(directory)
|
|
396
|
+
upload_zip(deployment.fileUploadUrl)
|
|
344
397
|
wait_for_deployment(access_token, metadata, callback)
|
|
345
398
|
|
|
346
399
|
# create data transform
|
{salesforce_data_customcode-0.1.4 → salesforce_data_customcode-0.1.6}/src/datacustomcode/scan.py
RENAMED
|
@@ -15,9 +15,12 @@
|
|
|
15
15
|
from __future__ import annotations
|
|
16
16
|
|
|
17
17
|
import ast
|
|
18
|
+
import os
|
|
18
19
|
from typing import (
|
|
19
20
|
Any,
|
|
21
|
+
ClassVar,
|
|
20
22
|
Dict,
|
|
23
|
+
Set,
|
|
21
24
|
Union,
|
|
22
25
|
)
|
|
23
26
|
|
|
@@ -131,6 +134,137 @@ class ClientMethodVisitor(ast.NodeVisitor):
|
|
|
131
134
|
)
|
|
132
135
|
|
|
133
136
|
|
|
137
|
+
class ImportVisitor(ast.NodeVisitor):
|
|
138
|
+
"""AST Visitor that extracts external package imports from Python code."""
|
|
139
|
+
|
|
140
|
+
# Standard library modules that should be excluded from requirements
|
|
141
|
+
STANDARD_LIBS: ClassVar[set[str]] = {
|
|
142
|
+
"abc",
|
|
143
|
+
"argparse",
|
|
144
|
+
"ast",
|
|
145
|
+
"asyncio",
|
|
146
|
+
"base64",
|
|
147
|
+
"collections",
|
|
148
|
+
"configparser",
|
|
149
|
+
"contextlib",
|
|
150
|
+
"copy",
|
|
151
|
+
"csv",
|
|
152
|
+
"datetime",
|
|
153
|
+
"enum",
|
|
154
|
+
"functools",
|
|
155
|
+
"glob",
|
|
156
|
+
"hashlib",
|
|
157
|
+
"http",
|
|
158
|
+
"importlib",
|
|
159
|
+
"inspect",
|
|
160
|
+
"io",
|
|
161
|
+
"itertools",
|
|
162
|
+
"json",
|
|
163
|
+
"logging",
|
|
164
|
+
"math",
|
|
165
|
+
"os",
|
|
166
|
+
"pathlib",
|
|
167
|
+
"pickle",
|
|
168
|
+
"random",
|
|
169
|
+
"re",
|
|
170
|
+
"shutil",
|
|
171
|
+
"site",
|
|
172
|
+
"socket",
|
|
173
|
+
"sqlite3",
|
|
174
|
+
"string",
|
|
175
|
+
"subprocess",
|
|
176
|
+
"sys",
|
|
177
|
+
"tempfile",
|
|
178
|
+
"threading",
|
|
179
|
+
"time",
|
|
180
|
+
"traceback",
|
|
181
|
+
"typing",
|
|
182
|
+
"uuid",
|
|
183
|
+
"warnings",
|
|
184
|
+
"xml",
|
|
185
|
+
"zipfile",
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
# Additional packages to exclude from requirements.txt
|
|
189
|
+
EXCLUDED_PACKAGES: ClassVar[set[str]] = {
|
|
190
|
+
"datacustomcode", # Internal package
|
|
191
|
+
"pyspark", # Provided by the runtime environment
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
def __init__(self) -> None:
|
|
195
|
+
self.imports: Set[str] = set()
|
|
196
|
+
|
|
197
|
+
def visit_Import(self, node: ast.Import) -> None:
|
|
198
|
+
"""Visit an import statement (e.g., import os, sys)."""
|
|
199
|
+
for name in node.names:
|
|
200
|
+
# Get the top-level package name
|
|
201
|
+
package = name.name.split(".")[0]
|
|
202
|
+
if (
|
|
203
|
+
package not in self.STANDARD_LIBS
|
|
204
|
+
and package not in self.EXCLUDED_PACKAGES
|
|
205
|
+
and not package.startswith("_")
|
|
206
|
+
):
|
|
207
|
+
self.imports.add(package)
|
|
208
|
+
self.generic_visit(node)
|
|
209
|
+
|
|
210
|
+
def visit_ImportFrom(self, node: ast.ImportFrom) -> None:
|
|
211
|
+
"""Visit a from-import statement (e.g., from os import path)."""
|
|
212
|
+
if node.module is not None:
|
|
213
|
+
# Get the top-level package
|
|
214
|
+
package = node.module.split(".")[0]
|
|
215
|
+
if (
|
|
216
|
+
package not in self.STANDARD_LIBS
|
|
217
|
+
and package not in self.EXCLUDED_PACKAGES
|
|
218
|
+
and not package.startswith("_")
|
|
219
|
+
):
|
|
220
|
+
self.imports.add(package)
|
|
221
|
+
self.generic_visit(node)
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
def scan_file_for_imports(file_path: str) -> Set[str]:
|
|
225
|
+
"""Scan a Python file for external package imports."""
|
|
226
|
+
with open(file_path, "r") as f:
|
|
227
|
+
code = f.read()
|
|
228
|
+
tree = ast.parse(code)
|
|
229
|
+
visitor = ImportVisitor()
|
|
230
|
+
visitor.visit(tree)
|
|
231
|
+
return visitor.imports
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def write_requirements_file(file_path: str) -> str:
|
|
235
|
+
"""
|
|
236
|
+
Scan a Python file for imports and write them to requirements.txt.
|
|
237
|
+
|
|
238
|
+
Args:
|
|
239
|
+
file_path: Path to the Python file to scan
|
|
240
|
+
|
|
241
|
+
Returns:
|
|
242
|
+
Path to the generated requirements.txt file
|
|
243
|
+
"""
|
|
244
|
+
imports = scan_file_for_imports(file_path)
|
|
245
|
+
|
|
246
|
+
# Write requirements.txt in the parent directory of the Python file
|
|
247
|
+
file_dir = os.path.dirname(file_path)
|
|
248
|
+
parent_dir = os.path.dirname(file_dir) if file_dir else "."
|
|
249
|
+
requirements_path = os.path.join(parent_dir, "requirements.txt")
|
|
250
|
+
|
|
251
|
+
# If the file exists, read existing requirements and merge with new ones
|
|
252
|
+
existing_requirements = set()
|
|
253
|
+
if os.path.exists(requirements_path):
|
|
254
|
+
with open(requirements_path, "r") as f:
|
|
255
|
+
existing_requirements = {line.strip() for line in f if line.strip()}
|
|
256
|
+
|
|
257
|
+
# Merge existing requirements with newly discovered ones
|
|
258
|
+
all_requirements = existing_requirements.union(imports)
|
|
259
|
+
|
|
260
|
+
# Write the combined requirements
|
|
261
|
+
with open(requirements_path, "w") as f:
|
|
262
|
+
for package in sorted(all_requirements):
|
|
263
|
+
f.write(f"{package}\n")
|
|
264
|
+
|
|
265
|
+
return requirements_path
|
|
266
|
+
|
|
267
|
+
|
|
134
268
|
def scan_file(file_path: str) -> DataAccessLayerCalls:
|
|
135
269
|
"""Scan a single Python file for Client read/write method calls."""
|
|
136
270
|
with open(file_path, "r") as f:
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
{
|
|
2
|
+
"cells": [
|
|
3
|
+
{
|
|
4
|
+
"cell_type": "code",
|
|
5
|
+
"execution_count": null,
|
|
6
|
+
"id": "0",
|
|
7
|
+
"metadata": {},
|
|
8
|
+
"outputs": [],
|
|
9
|
+
"source": [
|
|
10
|
+
"from datacustomcode.client import Client\n",
|
|
11
|
+
"from datacustomcode.io.writer.base import WriteMode\n",
|
|
12
|
+
"from pyspark.sql.functions import col, upper"
|
|
13
|
+
]
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"cell_type": "code",
|
|
17
|
+
"execution_count": null,
|
|
18
|
+
"id": "1",
|
|
19
|
+
"metadata": {},
|
|
20
|
+
"outputs": [],
|
|
21
|
+
"source": [
|
|
22
|
+
"client = Client()\n",
|
|
23
|
+
"\n",
|
|
24
|
+
"df = client.read_dlo(\"Account_Home__dll\")"
|
|
25
|
+
]
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"cell_type": "code",
|
|
29
|
+
"execution_count": null,
|
|
30
|
+
"id": "2",
|
|
31
|
+
"metadata": {},
|
|
32
|
+
"outputs": [],
|
|
33
|
+
"source": [
|
|
34
|
+
"# Perform transformations on the DataFrame\n",
|
|
35
|
+
"df_upper1 = df.withColumn(\"Description__c\", upper(col(\"Description__c\")))\n",
|
|
36
|
+
"\n",
|
|
37
|
+
"# Drop specific columns related to relationships\n",
|
|
38
|
+
"df_upper1 = df_upper1.drop(\"KQ_ParentId__c\")\n",
|
|
39
|
+
"df_upper1 = df_upper1.drop(\"KQ_Id__c\")\n",
|
|
40
|
+
"\n",
|
|
41
|
+
"df_upper1.show()"
|
|
42
|
+
]
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"cell_type": "code",
|
|
46
|
+
"execution_count": null,
|
|
47
|
+
"id": "3",
|
|
48
|
+
"metadata": {},
|
|
49
|
+
"outputs": [],
|
|
50
|
+
"source": [
|
|
51
|
+
"# Save the transformed DataFrame\n",
|
|
52
|
+
"dlo_name = \"Account_Home_copy__dll\"\n",
|
|
53
|
+
"client.write_to_dlo(dlo_name, df_upper1, write_mode=WriteMode.APPEND)"
|
|
54
|
+
]
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
"cell_type": "code",
|
|
58
|
+
"execution_count": null,
|
|
59
|
+
"id": "4",
|
|
60
|
+
"metadata": {},
|
|
61
|
+
"outputs": [],
|
|
62
|
+
"source": []
|
|
63
|
+
}
|
|
64
|
+
],
|
|
65
|
+
"metadata": {
|
|
66
|
+
"kernelspec": {
|
|
67
|
+
"display_name": "Python 3 (ipykernel)",
|
|
68
|
+
"language": "python",
|
|
69
|
+
"name": "python3"
|
|
70
|
+
},
|
|
71
|
+
"language_info": {
|
|
72
|
+
"codemirror_mode": {
|
|
73
|
+
"name": "ipython",
|
|
74
|
+
"version": 3
|
|
75
|
+
},
|
|
76
|
+
"file_extension": ".py",
|
|
77
|
+
"mimetype": "text/x-python",
|
|
78
|
+
"name": "python",
|
|
79
|
+
"nbconvert_exporter": "python",
|
|
80
|
+
"pygments_lexer": "ipython3",
|
|
81
|
+
"version": "3.11.11"
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
"nbformat": 4,
|
|
85
|
+
"nbformat_minor": 5
|
|
86
|
+
}
|
|
@@ -48,13 +48,13 @@ check_docker() {
|
|
|
48
48
|
# Function to start Jupyter server
|
|
49
49
|
start_jupyter() {
|
|
50
50
|
echo "Building the docker image"
|
|
51
|
-
docker build -t datacloud-
|
|
51
|
+
docker build -t datacloud-customcode .
|
|
52
52
|
|
|
53
53
|
echo "Running the docker container"
|
|
54
54
|
docker run -d --rm -p 8888:8888 \
|
|
55
55
|
-v $(pwd):/workspace \
|
|
56
56
|
--name jupyter-server \
|
|
57
|
-
datacloud-
|
|
57
|
+
datacloud-customcode jupyter lab \
|
|
58
58
|
--ip=0.0.0.0 \
|
|
59
59
|
--port=8888 \
|
|
60
60
|
--no-browser \
|
|
@@ -1,159 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.3
|
|
2
|
-
Name: salesforce-data-customcode
|
|
3
|
-
Version: 0.1.4
|
|
4
|
-
Summary: Data Cloud Custom Code SDK
|
|
5
|
-
License: Apache-2.0
|
|
6
|
-
Requires-Python: >=3.10,<3.12
|
|
7
|
-
Classifier: Development Status :: 4 - Beta
|
|
8
|
-
Classifier: Operating System :: Unix
|
|
9
|
-
Classifier: Programming Language :: Python
|
|
10
|
-
Classifier: Programming Language :: Python :: 3
|
|
11
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
12
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
13
|
-
Requires-Dist: click (>=8.1.8,<9.0.0)
|
|
14
|
-
Requires-Dist: loguru (>=0.7.3,<0.8.0)
|
|
15
|
-
Requires-Dist: numpy
|
|
16
|
-
Requires-Dist: pandas
|
|
17
|
-
Requires-Dist: pydantic (>=1.8.2,<3.0.0)
|
|
18
|
-
Requires-Dist: pyspark
|
|
19
|
-
Requires-Dist: pyyaml (>=6.0,<7.0)
|
|
20
|
-
Requires-Dist: salesforce-cdp-connector
|
|
21
|
-
Requires-Dist: setuptools_scm (>=7.1.0,<8.0.0)
|
|
22
|
-
Description-Content-Type: text/markdown
|
|
23
|
-
|
|
24
|
-
# Data Cloud Custom Code SDK
|
|
25
|
-
|
|
26
|
-
<img src="https://img.shields.io/badge/version-0.1.0-blue" alt="license">
|
|
27
|
-
|
|
28
|
-
This package provides a development kit for creating custom data transformations in [Data Cloud](https://www.salesforce.com/data/). It allows you to write your own data processing logic in Python while leveraging Data Cloud's infrastructure for data access and running data transformations, mapping execution into Data Cloud data structures like [Data Model Objects](https://help.salesforce.com/s/articleView?id=data.c360_a_data_model_objects.htm&type=5) and [Data Lake Objects](https://help.salesforce.com/s/articleView?id=sf.c360_a_data_lake_objects.htm&language=en_US&type=5).
|
|
29
|
-
|
|
30
|
-
More specifically, this codebase gives you ability to test code locally before pushing to Data Cloud's remote execution engine, greatly reducing how long it takes to develop.
|
|
31
|
-
|
|
32
|
-
Use of this project with Salesforce is subject to the [TERMS OF USE](./TERMS_OF_USE.md)
|
|
33
|
-
|
|
34
|
-
## Installation
|
|
35
|
-
The SDK can be downloaded directly from PyPI with `pip`:
|
|
36
|
-
```
|
|
37
|
-
pip install salesforce-data-customcode
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
You can verify it was properly installed via CLI:
|
|
41
|
-
```
|
|
42
|
-
datacustomcode version
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
## Development Setup
|
|
46
|
-
We offer two built-in development interfaces: `devcontainers` and Jupyter, but you can set up any tool you would like manually.
|
|
47
|
-
|
|
48
|
-
To get started, use the CLI to initialize a new development environment:
|
|
49
|
-
```
|
|
50
|
-
datacustomcode init [DIRECTORY TO DUMP NEW REPO]
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
This will yield all necessary files to get started:
|
|
54
|
-
```
|
|
55
|
-
.
|
|
56
|
-
├── Dockerfile
|
|
57
|
-
├── README.md
|
|
58
|
-
├── requirements.txt
|
|
59
|
-
├── requirements-dev.txt
|
|
60
|
-
├── payload
|
|
61
|
-
│ ├── config.json
|
|
62
|
-
│ ├── entrypoint.py
|
|
63
|
-
├── jupyterlab.sh
|
|
64
|
-
└── requirements.txt
|
|
65
|
-
```
|
|
66
|
-
* `Dockerfile` <span style="color:grey;font-style:italic;">(Do not update)</span> – Development container emulating the remote execution environment.
|
|
67
|
-
* `requirements-dev.txt` <span style="color:grey;font-style:italic;">(Do not update)</span> – These are the dependencies for the development environment.
|
|
68
|
-
* `jupyterlab.sh` <span style="color:grey;font-style:italic;">(Do not update)</span> – Helper script for setting up Jupyter.
|
|
69
|
-
* `requirements.txt` – Here you define the requirements that you will need remotely
|
|
70
|
-
* `payload` – This folder will be compressed and deployed to the remote execution environment.
|
|
71
|
-
* `config.json` – This config defines permissions on the back and can be generated programmatically with `scan` CLI method.
|
|
72
|
-
* `entrypoint.py` – The script that defines the data transformation logic.
|
|
73
|
-
|
|
74
|
-
## API
|
|
75
|
-
|
|
76
|
-
You entry point script will define logic using the `Client` object which wraps data access layers.
|
|
77
|
-
|
|
78
|
-
You should only need the following methods:
|
|
79
|
-
* `read_dlo(name)` – Read from a Data Lake Object by name
|
|
80
|
-
* `read_dmo(name)` – Read from a Data Model Object by name
|
|
81
|
-
* `write_to_dlo(name, spark_dataframe, write_mode)` – Write to a Data Model Object by name with a Spark dataframe
|
|
82
|
-
* `write_to_dmo(name, spark_dataframe, write_mode)` – Write to a Data Lake Object by name with a Spark dataframe
|
|
83
|
-
|
|
84
|
-
For example:
|
|
85
|
-
```
|
|
86
|
-
from datacustomcode import Client
|
|
87
|
-
|
|
88
|
-
client = Client()
|
|
89
|
-
|
|
90
|
-
sdf = client.read_dlo('my_DLO')
|
|
91
|
-
# some transformations
|
|
92
|
-
# ...
|
|
93
|
-
client.write_to_dlo('output_DLO')
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
> [!WARNING]
|
|
98
|
-
> Currently we only support reading from DMOs and writing to DMOs or reading from DLOs and writing to DLOs, but they cannot mix.
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
## CLI
|
|
102
|
-
|
|
103
|
-
The Data Cloud Custom Code SDK provides a command-line interface (CLI) with the following commands:
|
|
104
|
-
|
|
105
|
-
### Global Options
|
|
106
|
-
- `--debug`: Enable debug-level logging
|
|
107
|
-
|
|
108
|
-
### Commands
|
|
109
|
-
|
|
110
|
-
#### `datacustomcode version`
|
|
111
|
-
Display the current version of the package.
|
|
112
|
-
|
|
113
|
-
#### `datacustomcode configure`
|
|
114
|
-
Configure credentials for connecting to Data Cloud.
|
|
115
|
-
|
|
116
|
-
Options:
|
|
117
|
-
- `--profile TEXT`: Credential profile name (default: "default")
|
|
118
|
-
- `--username TEXT`: Salesforce username
|
|
119
|
-
- `--password TEXT`: Salesforce password
|
|
120
|
-
- `--client-id TEXT`: Connected App Client ID
|
|
121
|
-
- `--client-secret TEXT`: Connected App Client Secret
|
|
122
|
-
- `--login-url TEXT`: Salesforce login URL
|
|
123
|
-
|
|
124
|
-
#### `datacustomcode deploy`
|
|
125
|
-
Deploy a transformation job to Data Cloud.
|
|
126
|
-
|
|
127
|
-
Options:
|
|
128
|
-
- `--profile TEXT`: Credential profile name (default: "default")
|
|
129
|
-
- `--path TEXT`: Path to the code directory (default: ".")
|
|
130
|
-
- `--name TEXT`: Name of the transformation job [required]
|
|
131
|
-
- `--version TEXT`: Version of the transformation job (default: "0.0.1")
|
|
132
|
-
- `--description TEXT`: Description of the transformation job (default: "")
|
|
133
|
-
|
|
134
|
-
#### `datacustomcode init`
|
|
135
|
-
Initialize a new development environment with a template.
|
|
136
|
-
|
|
137
|
-
Argument:
|
|
138
|
-
- `DIRECTORY`: Directory to create project in (default: ".")
|
|
139
|
-
|
|
140
|
-
#### `datacustomcode scan`
|
|
141
|
-
Scan a Python file to generate a Data Cloud configuration.
|
|
142
|
-
|
|
143
|
-
Argument:
|
|
144
|
-
- `FILENAME`: Python file to scan
|
|
145
|
-
|
|
146
|
-
Options:
|
|
147
|
-
- `--config TEXT`: Path to save the configuration file (default: same directory as FILENAME)
|
|
148
|
-
- `--dry-run`: Preview the configuration without saving to a file
|
|
149
|
-
|
|
150
|
-
#### `datacustomcode run`
|
|
151
|
-
Run an entrypoint file locally for testing.
|
|
152
|
-
|
|
153
|
-
Argument:
|
|
154
|
-
- `ENTRYPOINT`: Path to entrypoint Python file
|
|
155
|
-
|
|
156
|
-
Options:
|
|
157
|
-
- `--config-file TEXT`: Path to configuration file
|
|
158
|
-
- `--dependencies TEXT`: Additional dependencies (can be specified multiple times)
|
|
159
|
-
|
|
@@ -1,135 +0,0 @@
|
|
|
1
|
-
# Data Cloud Custom Code SDK
|
|
2
|
-
|
|
3
|
-
<img src="https://img.shields.io/badge/version-0.1.0-blue" alt="license">
|
|
4
|
-
|
|
5
|
-
This package provides a development kit for creating custom data transformations in [Data Cloud](https://www.salesforce.com/data/). It allows you to write your own data processing logic in Python while leveraging Data Cloud's infrastructure for data access and running data transformations, mapping execution into Data Cloud data structures like [Data Model Objects](https://help.salesforce.com/s/articleView?id=data.c360_a_data_model_objects.htm&type=5) and [Data Lake Objects](https://help.salesforce.com/s/articleView?id=sf.c360_a_data_lake_objects.htm&language=en_US&type=5).
|
|
6
|
-
|
|
7
|
-
More specifically, this codebase gives you ability to test code locally before pushing to Data Cloud's remote execution engine, greatly reducing how long it takes to develop.
|
|
8
|
-
|
|
9
|
-
Use of this project with Salesforce is subject to the [TERMS OF USE](./TERMS_OF_USE.md)
|
|
10
|
-
|
|
11
|
-
## Installation
|
|
12
|
-
The SDK can be downloaded directly from PyPI with `pip`:
|
|
13
|
-
```
|
|
14
|
-
pip install salesforce-data-customcode
|
|
15
|
-
```
|
|
16
|
-
|
|
17
|
-
You can verify it was properly installed via CLI:
|
|
18
|
-
```
|
|
19
|
-
datacustomcode version
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
## Development Setup
|
|
23
|
-
We offer two built-in development interfaces: `devcontainers` and Jupyter, but you can set up any tool you would like manually.
|
|
24
|
-
|
|
25
|
-
To get started, use the CLI to initialize a new development environment:
|
|
26
|
-
```
|
|
27
|
-
datacustomcode init [DIRECTORY TO DUMP NEW REPO]
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
This will yield all necessary files to get started:
|
|
31
|
-
```
|
|
32
|
-
.
|
|
33
|
-
├── Dockerfile
|
|
34
|
-
├── README.md
|
|
35
|
-
├── requirements.txt
|
|
36
|
-
├── requirements-dev.txt
|
|
37
|
-
├── payload
|
|
38
|
-
│ ├── config.json
|
|
39
|
-
│ ├── entrypoint.py
|
|
40
|
-
├── jupyterlab.sh
|
|
41
|
-
└── requirements.txt
|
|
42
|
-
```
|
|
43
|
-
* `Dockerfile` <span style="color:grey;font-style:italic;">(Do not update)</span> – Development container emulating the remote execution environment.
|
|
44
|
-
* `requirements-dev.txt` <span style="color:grey;font-style:italic;">(Do not update)</span> – These are the dependencies for the development environment.
|
|
45
|
-
* `jupyterlab.sh` <span style="color:grey;font-style:italic;">(Do not update)</span> – Helper script for setting up Jupyter.
|
|
46
|
-
* `requirements.txt` – Here you define the requirements that you will need remotely
|
|
47
|
-
* `payload` – This folder will be compressed and deployed to the remote execution environment.
|
|
48
|
-
* `config.json` – This config defines permissions on the back and can be generated programmatically with `scan` CLI method.
|
|
49
|
-
* `entrypoint.py` – The script that defines the data transformation logic.
|
|
50
|
-
|
|
51
|
-
## API
|
|
52
|
-
|
|
53
|
-
You entry point script will define logic using the `Client` object which wraps data access layers.
|
|
54
|
-
|
|
55
|
-
You should only need the following methods:
|
|
56
|
-
* `read_dlo(name)` – Read from a Data Lake Object by name
|
|
57
|
-
* `read_dmo(name)` – Read from a Data Model Object by name
|
|
58
|
-
* `write_to_dlo(name, spark_dataframe, write_mode)` – Write to a Data Model Object by name with a Spark dataframe
|
|
59
|
-
* `write_to_dmo(name, spark_dataframe, write_mode)` – Write to a Data Lake Object by name with a Spark dataframe
|
|
60
|
-
|
|
61
|
-
For example:
|
|
62
|
-
```
|
|
63
|
-
from datacustomcode import Client
|
|
64
|
-
|
|
65
|
-
client = Client()
|
|
66
|
-
|
|
67
|
-
sdf = client.read_dlo('my_DLO')
|
|
68
|
-
# some transformations
|
|
69
|
-
# ...
|
|
70
|
-
client.write_to_dlo('output_DLO')
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
> [!WARNING]
|
|
75
|
-
> Currently we only support reading from DMOs and writing to DMOs or reading from DLOs and writing to DLOs, but they cannot mix.
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
## CLI
|
|
79
|
-
|
|
80
|
-
The Data Cloud Custom Code SDK provides a command-line interface (CLI) with the following commands:
|
|
81
|
-
|
|
82
|
-
### Global Options
|
|
83
|
-
- `--debug`: Enable debug-level logging
|
|
84
|
-
|
|
85
|
-
### Commands
|
|
86
|
-
|
|
87
|
-
#### `datacustomcode version`
|
|
88
|
-
Display the current version of the package.
|
|
89
|
-
|
|
90
|
-
#### `datacustomcode configure`
|
|
91
|
-
Configure credentials for connecting to Data Cloud.
|
|
92
|
-
|
|
93
|
-
Options:
|
|
94
|
-
- `--profile TEXT`: Credential profile name (default: "default")
|
|
95
|
-
- `--username TEXT`: Salesforce username
|
|
96
|
-
- `--password TEXT`: Salesforce password
|
|
97
|
-
- `--client-id TEXT`: Connected App Client ID
|
|
98
|
-
- `--client-secret TEXT`: Connected App Client Secret
|
|
99
|
-
- `--login-url TEXT`: Salesforce login URL
|
|
100
|
-
|
|
101
|
-
#### `datacustomcode deploy`
|
|
102
|
-
Deploy a transformation job to Data Cloud.
|
|
103
|
-
|
|
104
|
-
Options:
|
|
105
|
-
- `--profile TEXT`: Credential profile name (default: "default")
|
|
106
|
-
- `--path TEXT`: Path to the code directory (default: ".")
|
|
107
|
-
- `--name TEXT`: Name of the transformation job [required]
|
|
108
|
-
- `--version TEXT`: Version of the transformation job (default: "0.0.1")
|
|
109
|
-
- `--description TEXT`: Description of the transformation job (default: "")
|
|
110
|
-
|
|
111
|
-
#### `datacustomcode init`
|
|
112
|
-
Initialize a new development environment with a template.
|
|
113
|
-
|
|
114
|
-
Argument:
|
|
115
|
-
- `DIRECTORY`: Directory to create project in (default: ".")
|
|
116
|
-
|
|
117
|
-
#### `datacustomcode scan`
|
|
118
|
-
Scan a Python file to generate a Data Cloud configuration.
|
|
119
|
-
|
|
120
|
-
Argument:
|
|
121
|
-
- `FILENAME`: Python file to scan
|
|
122
|
-
|
|
123
|
-
Options:
|
|
124
|
-
- `--config TEXT`: Path to save the configuration file (default: same directory as FILENAME)
|
|
125
|
-
- `--dry-run`: Preview the configuration without saving to a file
|
|
126
|
-
|
|
127
|
-
#### `datacustomcode run`
|
|
128
|
-
Run an entrypoint file locally for testing.
|
|
129
|
-
|
|
130
|
-
Argument:
|
|
131
|
-
- `ENTRYPOINT`: Path to entrypoint Python file
|
|
132
|
-
|
|
133
|
-
Options:
|
|
134
|
-
- `--config-file TEXT`: Path to configuration file
|
|
135
|
-
- `--dependencies TEXT`: Additional dependencies (can be specified multiple times)
|
|
File without changes
|
{salesforce_data_customcode-0.1.4 → salesforce_data_customcode-0.1.6}/src/datacustomcode/__init__.py
RENAMED
|
File without changes
|
{salesforce_data_customcode-0.1.4 → salesforce_data_customcode-0.1.6}/src/datacustomcode/client.py
RENAMED
|
File without changes
|
{salesforce_data_customcode-0.1.4 → salesforce_data_customcode-0.1.6}/src/datacustomcode/cmd.py
RENAMED
|
File without changes
|
{salesforce_data_customcode-0.1.4 → salesforce_data_customcode-0.1.6}/src/datacustomcode/config.py
RENAMED
|
File without changes
|
{salesforce_data_customcode-0.1.4 → salesforce_data_customcode-0.1.6}/src/datacustomcode/config.yaml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{salesforce_data_customcode-0.1.4 → salesforce_data_customcode-0.1.6}/src/datacustomcode/io/base.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{salesforce_data_customcode-0.1.4 → salesforce_data_customcode-0.1.6}/src/datacustomcode/mixin.py
RENAMED
|
File without changes
|
{salesforce_data_customcode-0.1.4 → salesforce_data_customcode-0.1.6}/src/datacustomcode/py.typed
RENAMED
|
File without changes
|
{salesforce_data_customcode-0.1.4 → salesforce_data_customcode-0.1.6}/src/datacustomcode/run.py
RENAMED
|
File without changes
|
{salesforce_data_customcode-0.1.4 → salesforce_data_customcode-0.1.6}/src/datacustomcode/template.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{salesforce_data_customcode-0.1.4 → salesforce_data_customcode-0.1.6}/src/datacustomcode/version.py
RENAMED
|
File without changes
|