Skip to content

Commit 1e6b667

Browse files
committed
Updated init and scan to support functions.
1 parent a26f459 commit 1e6b667

32 files changed

Lines changed: 599 additions & 138 deletions

src/datacustomcode/cli.py

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -145,18 +145,25 @@ def deploy(
145145

146146
@cli.command()
147147
@click.argument("directory", default=".")
148-
def init(directory: str):
149-
from datacustomcode.scan import dc_config_json_from_file
150-
from datacustomcode.template import copy_template
148+
@click.option("--type", default="script", type=click.Choice(["script", "function"]))
149+
def init(directory: str, type: str):
150+
from datacustomcode.scan import dc_config_json_from_file, update_config
151+
from datacustomcode.template import copy_function_template, copy_script_template
151152

152153
click.echo("Copying template to " + click.style(directory, fg="blue", bold=True))
153-
copy_template(directory)
154+
if type == "script":
155+
copy_script_template(directory)
156+
elif type == "function":
157+
copy_function_template(directory)
154158
entrypoint_path = os.path.join(directory, "payload", "entrypoint.py")
155159
config_location = os.path.join(os.path.dirname(entrypoint_path), "config.json")
156-
config_json = dc_config_json_from_file(entrypoint_path)
160+
config_json = dc_config_json_from_file(entrypoint_path, type)
157161
with open(config_location, "w") as f:
158162
json.dump(config_json, f, indent=2)
159163

164+
updated_config_json = update_config(entrypoint_path)
165+
with open(config_location, "w") as f:
166+
json.dump(updated_config_json, f, indent=2)
160167
click.echo(
161168
"Start developing by updating the code in "
162169
+ click.style(entrypoint_path, fg="blue", bold=True)
@@ -176,15 +183,15 @@ def init(directory: str):
176183
"--no-requirements", is_flag=True, help="Skip generating requirements.txt file"
177184
)
178185
def scan(filename: str, config: str, dry_run: bool, no_requirements: bool):
179-
from datacustomcode.scan import dc_config_json_from_file, write_requirements_file
186+
from datacustomcode.scan import update_config, write_requirements_file
180187

181188
config_location = config or os.path.join(os.path.dirname(filename), "config.json")
182189
click.echo(
183190
"Dumping scan results to config file: "
184191
+ click.style(config_location, fg="blue", bold=True)
185192
)
186193
click.echo("Scanning " + click.style(filename, fg="blue", bold=True) + "...")
187-
config_json = dc_config_json_from_file(filename)
194+
config_json = update_config(filename)
188195

189196
click.secho(json.dumps(config_json, indent=2), fg="yellow")
190197
if not dry_run:

src/datacustomcode/scan.py

Lines changed: 53 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -37,14 +37,20 @@
3737

3838
DATA_TRANSFORM_CONFIG_TEMPLATE = {
3939
"sdkVersion": get_version(),
40+
"type": "script",
4041
"entryPoint": "",
41-
"dataspace": "",
42+
"dataspace": "default",
4243
"permissions": {
4344
"read": {},
4445
"write": {},
4546
},
4647
}
4748

49+
FUNCTION_CONFIG_TEMPLATE = {
50+
"sdkVersion": get_version(),
51+
"type": "function",
52+
"entryPoint": "",
53+
}
4854
STANDARD_LIBS = set(sys.stdlib_module_names)
4955

5056

@@ -230,57 +236,66 @@ def scan_file(file_path: str) -> DataAccessLayerCalls:
230236
return visitor.found()
231237

232238

233-
def dc_config_json_from_file(file_path: str) -> dict[str, Any]:
239+
def dc_config_json_from_file(file_path: str, type: str) -> dict[str, Any]:
234240
"""Create a Data Cloud Custom Code config JSON from a script."""
235-
output = scan_file(file_path)
236-
config = DATA_TRANSFORM_CONFIG_TEMPLATE.copy()
241+
config: dict[str, Any]
242+
if type == "script":
243+
config = DATA_TRANSFORM_CONFIG_TEMPLATE.copy()
244+
elif type == "function":
245+
config = FUNCTION_CONFIG_TEMPLATE.copy()
237246
config["entryPoint"] = file_path.rpartition("/")[-1]
247+
return config
238248

249+
250+
def update_config(file_path: str) -> dict[str, Any]:
239251
file_dir = os.path.dirname(file_path)
240252
config_json_path = os.path.join(file_dir, "config.json")
241-
253+
existing_config: dict[str, Any]
242254
if os.path.exists(config_json_path) and os.path.isfile(config_json_path):
243255
try:
244256
with open(config_json_path, "r") as f:
245257
existing_config = json.load(f)
246-
247-
if "dataspace" in existing_config:
248-
dataspace_value = existing_config["dataspace"]
249-
if not dataspace_value or (
250-
isinstance(dataspace_value, str) and dataspace_value.strip() == ""
251-
):
252-
logger.warning(
253-
f"dataspace in {config_json_path} is empty or None. "
254-
f"Updating config file to use dataspace 'default'. "
255-
)
256-
config["dataspace"] = "default"
257-
else:
258-
config["dataspace"] = dataspace_value
259-
else:
260-
raise ValueError(
261-
f"dataspace must be defined in {config_json_path}. "
262-
f"Please add a 'dataspace' field to the config.json file. "
263-
)
264258
except json.JSONDecodeError as e:
265259
raise ValueError(
266260
f"Failed to parse JSON from {config_json_path}: {e}"
267261
) from e
268262
except OSError as e:
269263
raise OSError(f"Failed to read config file {config_json_path}: {e}") from e
270264
else:
271-
config["dataspace"] = "default"
272-
273-
read: dict[str, list[str]] = {}
274-
if output.read_dlo:
275-
read["dlo"] = list(output.read_dlo)
276-
else:
277-
read["dmo"] = list(output.read_dmo)
278-
write: dict[str, list[str]] = {}
279-
if output.write_to_dlo:
280-
write["dlo"] = list(output.write_to_dlo)
265+
raise ValueError(f"config.json not found at {config_json_path}")
266+
if existing_config["type"] == "script":
267+
existing_config["dataspace"] = get_dataspace(existing_config)
268+
output = scan_file(file_path)
269+
read: dict[str, list[str]] = {}
270+
if output.read_dlo:
271+
read["dlo"] = list(output.read_dlo)
272+
else:
273+
read["dmo"] = list(output.read_dmo)
274+
write: dict[str, list[str]] = {}
275+
if output.write_to_dlo:
276+
write["dlo"] = list(output.write_to_dlo)
277+
else:
278+
write["dmo"] = list(output.write_to_dmo)
279+
280+
existing_config["permissions"] = {"read": read, "write": write}
281+
return existing_config
282+
283+
284+
def get_dataspace(existing_config: dict[str, str]) -> str:
285+
if "dataspace" in existing_config:
286+
dataspace_value = existing_config["dataspace"]
287+
if not dataspace_value or (
288+
isinstance(dataspace_value, str) and dataspace_value.strip() == ""
289+
):
290+
logger.warning(
291+
"dataspace is empty or None. "
292+
"Updating config file to use dataspace 'default'. "
293+
)
294+
return "default"
295+
else:
296+
return dataspace_value
281297
else:
282-
write["dmo"] = list(output.write_to_dmo)
283-
284-
config["permissions"] = {"read": read, "write": write}
285-
286-
return config
298+
raise ValueError(
299+
"dataspace must be defined. "
300+
"Please add a 'dataspace' field to the config.json file. "
301+
)

src/datacustomcode/template.py

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,31 @@
1717

1818
from loguru import logger
1919

20-
template_dir = os.path.join(os.path.dirname(__file__), "templates")
20+
script_template_dir = os.path.join(os.path.dirname(__file__), "templates", "script")
21+
function_template_dir = os.path.join(os.path.dirname(__file__), "templates", "function")
2122

2223

23-
def copy_template(target_dir: str) -> None:
24+
def copy_script_template(target_dir: str) -> None:
2425
"""Copy the template to the target directory."""
2526
os.makedirs(target_dir, exist_ok=True)
2627

27-
for item in os.listdir(template_dir):
28-
source = os.path.join(template_dir, item)
28+
for item in os.listdir(script_template_dir):
29+
source = os.path.join(script_template_dir, item)
30+
destination = os.path.join(target_dir, item)
31+
32+
if os.path.isdir(source):
33+
logger.debug(f"Copying directory {source} to {destination}...")
34+
shutil.copytree(source, destination, dirs_exist_ok=True)
35+
else:
36+
logger.debug(f"Copying file {source} to {destination}...")
37+
shutil.copy2(source, destination)
38+
39+
40+
def copy_function_template(target_dir: str) -> None:
41+
os.makedirs(target_dir, exist_ok=True)
42+
43+
for item in os.listdir(function_template_dir):
44+
source = os.path.join(function_template_dir, item)
2945
destination = os.path.join(target_dir, item)
3046

3147
if os.path.isdir(source):

src/datacustomcode/templates/.devcontainer/devcontainer.json renamed to src/datacustomcode/templates/function/.devcontainer/devcontainer.json

File renamed without changes.
File renamed without changes.

src/datacustomcode/templates/Dockerfile.dependencies renamed to src/datacustomcode/templates/function/Dockerfile.dependencies

File renamed without changes.
File renamed without changes.
File renamed without changes.

src/datacustomcode/templates/build_native_dependencies.sh renamed to src/datacustomcode/templates/function/build_native_dependencies.sh

File renamed without changes.

src/datacustomcode/templates/examples/employee_hierarchy/employee_data.csv renamed to src/datacustomcode/templates/function/examples/employee_hierarchy/employee_data.csv

File renamed without changes.

0 commit comments

Comments
 (0)