diff --git a/polyapi/cli.py b/polyapi/cli.py index e99cded..62c3cad 100644 --- a/polyapi/cli.py +++ b/polyapi/cli.py @@ -113,11 +113,13 @@ def generate_command(args): fn_add_parser.add_argument("--execution-api-key", required=False, default="", help="API key for execution (for server functions only).") fn_add_parser.add_argument("--disable-ai", "--skip-generate", action="store_true", help="Pass --disable-ai skip AI generation of missing descriptions") fn_add_parser.add_argument("--generate-contexts", type=str, help="Server function only – only include certain contexts to speed up function execution") + fn_add_parser.add_argument("--visibility", type=str, default="environment", help="Specifies the visibility of a function. Options: PUBLIC, TENANT, ENVIRONMENT. Case insensitive") def add_function(args): initialize_config() logs_enabled = args.logs == "enabled" if args.logs else None err = "" + visibility = args.visibility.upper() if args.server and args.client: err = "Specify either `--server` or `--client`. Found both." elif not args.server and not args.client: @@ -126,6 +128,8 @@ def add_function(args): err = "Option `logs` is only for server functions (--server)." elif args.generate_contexts and not args.server: err = "Option `generate-contexts` is only for server functions (--server)." + elif visibility not in ["PUBLIC", "TENANT", "ENVIRONMENT"]: + err = "Invalid visiblity argument, visibility must be one of ['PUBLIC', 'TENANT', 'ENVIRONMENT']" if err: print_red("ERROR") @@ -142,7 +146,8 @@ def add_function(args): logs_enabled=logs_enabled, generate=not args.disable_ai, execution_api_key=args.execution_api_key, - generate_contexts=args.generate_contexts + generate_contexts=args.generate_contexts, + visibility=visibility ) fn_add_parser.set_defaults(command=add_function) diff --git a/polyapi/function_cli.py b/polyapi/function_cli.py index bdadd0d..219bbb0 100644 --- a/polyapi/function_cli.py +++ b/polyapi/function_cli.py @@ -25,6 +25,7 @@ def function_add_or_update( server: bool, logs_enabled: Optional[bool], generate_contexts: Optional[str], + visibility: Optional[str], generate: bool = True, execution_api_key: str = "" ): @@ -55,6 +56,7 @@ def function_add_or_update( "description": description or parsed["types"]["description"], "code": code, "language": "python", + "visibility": visibility or "ENVIRONMENT", "returnType": get_jsonschema_type(return_type), "arguments": [{**p, "key": p["name"], "type": get_jsonschema_type(p["type"])} for p in parsed["types"]["params"]], "logsEnabled": logs_enabled, diff --git a/polyapi/generate.py b/polyapi/generate.py index 16793fc..4397499 100644 --- a/polyapi/generate.py +++ b/polyapi/generate.py @@ -503,7 +503,7 @@ def add_function_file( # Read current __init__.py content if it exists init_content = "" if os.path.exists(init_path): - with open(init_path, "r") as f: + with open(init_path, "r", encoding='utf-8') as f: init_content = f.read() # Prepare new content to append to __init__.py @@ -511,12 +511,12 @@ def add_function_file( # Use temporary files for atomic writes # Write to __init__.py atomically - with tempfile.NamedTemporaryFile(mode="w", delete=False, dir=full_path, suffix=".tmp") as temp_init: + with tempfile.NamedTemporaryFile(mode="w", delete=False, dir=full_path, suffix=".tmp", encoding='utf-8') as temp_init: temp_init.write(new_init_content) temp_init_path = temp_init.name # Write to function file atomically - with tempfile.NamedTemporaryFile(mode="w", delete=False, dir=full_path, suffix=".tmp") as temp_func: + with tempfile.NamedTemporaryFile(mode="w", delete=False, dir=full_path, suffix=".tmp", encoding='utf-8') as temp_func: temp_func.write(func_type_defs) temp_func_path = temp_func.name diff --git a/polyapi/parser.py b/polyapi/parser.py index f474693..1485202 100644 --- a/polyapi/parser.py +++ b/polyapi/parser.py @@ -464,6 +464,12 @@ def visit_FunctionDef(self, node: ast.FunctionDef): if node.name == self._name: # Parse docstring which may contain param types and descriptions self._extract_docstring_from_function(node) + + if node.args.kwonlyargs: + print_red("ERROR") + print("Function signature has keyword-only arguments (after `*`). Please use only positional arguments.") + sys.exit(1) + function_args = [arg for arg in node.args.args] docstring_params = deployable["types"]["params"] parsed_params = [] diff --git a/polyapi/poly_tables.py b/polyapi/poly_tables.py index 3b51913..358a1f2 100644 --- a/polyapi/poly_tables.py +++ b/polyapi/poly_tables.py @@ -6,6 +6,21 @@ from polyapi.typedefs import TableSpecDto from polyapi.constants import JSONSCHEMA_TO_PYTHON_TYPE_MAP +def scrub(data) -> Dict[str, Any]: + if (not data or not isinstance(data, (Dict, List))): return data + if isinstance(data, List): + return [scrub(item) for item in data] + else: + temp = {} + secrets = ["x_api_key", "x-api-key", "access_token", "access-token", "authorization", "api_key", "api-key", "apikey", "accesstoken", "token", "password", "key"] + for key, value in data.items(): + if isinstance(value, (Dict, List)): + temp[key] = scrub(data[key]) + elif key.lower() in secrets: + temp[key] = '********' + else: + temp[key] = data[key] + return temp def scrub_keys(e: Exception) -> Dict[str, Any]: """ @@ -16,7 +31,7 @@ def scrub_keys(e: Exception) -> Dict[str, Any]: "error": str(e), "type": type(e).__name__, "message": str(e), - "args": getattr(e, 'args', None) + "args": scrub(getattr(e, 'args', None)) } diff --git a/polyapi/utils.py b/polyapi/utils.py index 4c803c0..6a7b475 100644 --- a/polyapi/utils.py +++ b/polyapi/utils.py @@ -24,7 +24,7 @@ def init_the_init(full_path: str, code_imports: Optional[str] = None) -> None: if not os.path.exists(init_path): if code_imports is None: code_imports = CODE_IMPORTS - with open(init_path, "w") as f: + with open(init_path, "w", encoding='utf-8') as f: f.write(code_imports) @@ -33,7 +33,7 @@ def add_import_to_init(full_path: str, next: str, code_imports: Optional[str] = init_the_init(full_path, code_imports=code_imports) init_path = os.path.join(full_path, "__init__.py") - with open(init_path, "a+") as f: + with open(init_path, "a+", encoding='utf-8') as f: import_stmt = "from . import {}\n".format(next) f.seek(0) lines = f.readlines() diff --git a/pyproject.toml b/pyproject.toml index 18031a6..b208338 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,7 +3,7 @@ requires = ["setuptools>=61.2", "wheel"] [project] name = "polyapi-python" -version = "0.3.9" +version = "0.3.10" description = "The Python Client for PolyAPI, the IPaaS by Developers for Developers" authors = [{ name = "Dan Fellin", email = "dan@polyapi.io" }] dependencies = [