polyapi-python 0.0.35__py3-none-any.whl → 0.1.0__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.
polyapi/api.py CHANGED
@@ -1,31 +1,8 @@
1
- import os
2
1
  from typing import Any, Dict, List, Tuple
3
2
 
4
- from polyapi.constants import BASIC_PYTHON_TYPES, JSONSCHEMA_TO_PYTHON_TYPE_MAP
5
- from polyapi.typedefs import PropertySpecification, PropertyType
6
- from polyapi.utils import append_init, camelCase
7
- from polyapi.schema import generate_schema_types, clean_title
3
+ from polyapi.typedefs import PropertySpecification
4
+ from polyapi.utils import add_type_import_path, camelCase, parse_arguments, get_type_and_def
8
5
 
9
- # map the function type from the spec type to the function execute type
10
- TEMPLATE_FUNCTION_TYPE_MAP = {
11
- "apiFunction": "api",
12
- "serverFunction": "server",
13
- }
14
-
15
- SERVER_DEFS_TEMPLATE = """
16
- from typing import List, Dict, Any, TypedDict
17
- {args_def}
18
- {return_type_def}
19
- """
20
-
21
- SERVER_FUNCTION_TEMPLATE = """
22
- def {function_name}(
23
- {args}
24
- ) -> {return_type_name}:
25
- "{function_description}"
26
- resp = execute("{function_type}", "{function_id}", {data})
27
- return {return_action}
28
- """
29
6
 
30
7
  API_DEFS_TEMPLATE = """
31
8
  from typing import List, Dict, Any, TypedDict
@@ -47,109 +24,7 @@ def {function_name}(
47
24
  """
48
25
 
49
26
 
50
- def _map_primitive_types(type_: str) -> str:
51
- # Define your mapping logic here
52
- return JSONSCHEMA_TO_PYTHON_TYPE_MAP.get(type_, "Any")
53
-
54
-
55
- def _get_type(type_spec: PropertyType) -> Tuple[str, str]:
56
- if type_spec["kind"] == "plain":
57
- value = type_spec["value"]
58
- if value.endswith("[]"):
59
- primitive = _map_primitive_types(value[:-2])
60
- return f"List[{primitive}]", ""
61
- else:
62
- return _map_primitive_types(value), ""
63
- elif type_spec["kind"] == "primitive":
64
- return _map_primitive_types(type_spec["type"]), ""
65
- elif type_spec["kind"] == "array":
66
- if type_spec.get("items"):
67
- items = type_spec["items"]
68
- if items.get("$ref"):
69
- return "ResponseType", generate_schema_types(type_spec, root="ResponseType") # type: ignore
70
- else:
71
- item_type, _ = _get_type(items)
72
- title = f"List[{item_type}]"
73
- title = clean_title(title)
74
- return title, ""
75
- else:
76
- return "List", ""
77
- elif type_spec["kind"] == "void":
78
- return "None", ""
79
- elif type_spec["kind"] == "object":
80
- if type_spec.get("schema"):
81
- schema = type_spec["schema"]
82
- title = schema.get("title", "")
83
- if title:
84
- assert isinstance(title, str)
85
- title = clean_title(title)
86
- return title, generate_schema_types(schema, root=title) # type: ignore
87
- elif schema.get("items"):
88
- # fallback to schema $ref name if no explicit title
89
- items = schema.get("items") # type: ignore
90
- title = items.get("title", "") # type: ignore
91
- if not title:
92
- # title is actually a reference to another schema
93
- title = items.get("$ref", "") # type: ignore
94
-
95
- title = title.rsplit("/", 1)[-1]
96
- title = clean_title(title)
97
- if not title:
98
- return "List", ""
99
-
100
- title = f"List[{title}]"
101
- return title, generate_schema_types(schema, root=title)
102
- else:
103
- return "Any", ""
104
- else:
105
- return "Dict", ""
106
- elif type_spec["kind"] == "any":
107
- return "Any", ""
108
- else:
109
- return "Any", ""
110
-
111
-
112
- def _parse_arguments(function_name: str, arguments: List[PropertySpecification]) -> Tuple[str, str]:
113
- args_def = []
114
- arg_string = ""
115
- for idx, a in enumerate(arguments):
116
- arg_type, arg_def = _get_type(a["type"])
117
- if arg_def:
118
- args_def.append(arg_def)
119
- a["name"] = camelCase(a["name"])
120
- arg_string += f" {a['name']}: {_add_type_import_path(function_name, arg_type)}"
121
- description = a.get("description", "")
122
- if description:
123
- if idx == len(arguments) - 1:
124
- arg_string += f" # {description}\n"
125
- else:
126
- arg_string += f", # {description}\n"
127
- else:
128
- arg_string += ",\n"
129
- return arg_string.rstrip("\n"), "\n\n".join(args_def)
130
-
131
-
132
- def _add_type_import_path(function_name: str, arg: str) -> str:
133
- """ if not basic type, coerce to camelCase and add the import path
134
- """
135
- if arg in BASIC_PYTHON_TYPES:
136
- return arg
137
-
138
- if arg.startswith("List["):
139
- sub = arg[5:-1]
140
- if sub in BASIC_PYTHON_TYPES:
141
- return arg
142
- else:
143
- if '"' in sub:
144
- sub = sub.replace('"', "")
145
- return f'List["_{function_name}.{camelCase(sub)}"]'
146
- else:
147
- return f'List[_{function_name}.{camelCase(sub)}]'
148
-
149
- return f'_{function_name}.{camelCase(arg)}'
150
-
151
-
152
- def render_function(
27
+ def render_api_function(
153
28
  function_type: str,
154
29
  function_name: str,
155
30
  function_id: str,
@@ -157,134 +32,26 @@ def render_function(
157
32
  arguments: List[PropertySpecification],
158
33
  return_type: Dict[str, Any],
159
34
  ) -> Tuple[str, str]:
35
+ assert function_type == "apiFunction"
160
36
  arg_names = [a["name"] for a in arguments]
161
- args, args_def = _parse_arguments(function_name, arguments)
162
- return_type_name, return_type_def = _get_type(return_type) # type: ignore
37
+ args, args_def = parse_arguments(function_name, arguments)
38
+ return_type_name, return_type_def = get_type_and_def(return_type) # type: ignore
163
39
  data = "{" + ", ".join([f"'{arg}': {camelCase(arg)}" for arg in arg_names]) + "}"
164
- if function_type == "apiFunction":
165
- api_response_type = f"{function_name}Response"
166
- func_type_defs = API_DEFS_TEMPLATE.format(
167
- args_def=args_def,
168
- api_response_type=api_response_type,
169
- return_type_name=return_type_name,
170
- return_type_def=return_type_def,
171
- )
172
- func_str = API_FUNCTION_TEMPLATE.format(
173
- function_type=TEMPLATE_FUNCTION_TYPE_MAP[function_type],
174
- function_name=function_name,
175
- function_id=function_id,
176
- function_description=function_description.replace('"', "'"),
177
- args=args,
178
- data=data,
179
- api_response_type=_add_type_import_path(function_name, api_response_type),
180
- )
181
- else:
182
- func_type_defs = SERVER_DEFS_TEMPLATE.format(
183
- args_def=args_def,
184
- return_type_def=return_type_def,
185
- )
186
- func_str = SERVER_FUNCTION_TEMPLATE.format(
187
- return_type_name=_add_type_import_path(function_name, return_type_name),
188
- function_type=TEMPLATE_FUNCTION_TYPE_MAP[function_type],
189
- function_name=function_name,
190
- function_id=function_id,
191
- function_description=function_description.replace('"', "'"),
192
- args=args,
193
- return_action=_get_server_return_action(return_type_name),
194
- data=data,
195
- )
196
- return func_str, func_type_defs
197
-
198
-
199
- def _get_server_return_action(return_type_name: str) -> str:
200
- if return_type_name == "str":
201
- return_action = "resp.text"
202
- elif return_type_name == "Any":
203
- return_action = "resp.text"
204
- elif return_type_name == "int":
205
- return_action = "int(resp.text.replace('(int) ', ''))"
206
- elif return_type_name == "float":
207
- return_action = "float(resp.text.replace('(float) ', ''))"
208
- elif return_type_name == "bool":
209
- return_action = "False if resp.text == 'False' else True"
210
- else:
211
- return_action = "resp.json()"
212
- return return_action
213
40
 
214
-
215
- def add_function_file(
216
- function_type: str,
217
- full_path: str,
218
- function_name: str,
219
- function_id: str,
220
- function_description: str,
221
- arguments: List[PropertySpecification],
222
- return_type: Dict[str, Any],
223
- ):
224
- # first lets add the import to the __init__
225
- init_path = os.path.join(full_path, "__init__.py")
226
- _init_the_init(init_path)
227
-
228
- func_str, func_type_defs = render_function(
229
- function_type,
230
- function_name,
231
- function_id,
232
- function_description,
233
- arguments,
234
- return_type,
41
+ api_response_type = f"{function_name}Response"
42
+ func_type_defs = API_DEFS_TEMPLATE.format(
43
+ args_def=args_def,
44
+ api_response_type=api_response_type,
45
+ return_type_name=return_type_name,
46
+ return_type_def=return_type_def,
235
47
  )
236
-
237
- with open(init_path, "a") as f:
238
- f.write(f"\n\nfrom . import _{function_name}\n\n{func_str}")
239
-
240
- # now lets add the code!
241
- file_path = os.path.join(full_path, f"_{function_name}.py")
242
- with open(file_path, "w") as f:
243
- f.write(func_type_defs)
244
-
245
-
246
- def _init_the_init(init_path: str) -> None:
247
- if not os.path.exists(init_path):
248
- with open(init_path, "w") as f:
249
- f.write("from typing import List, Dict, Any, TypedDict\nfrom polyapi.execute import execute\nfrom polyapi.exceptions import PolyApiException\n")
250
-
251
-
252
- def create_function(
253
- function_type: str,
254
- path: str,
255
- function_id: str,
256
- function_description: str,
257
- arguments: List[PropertySpecification],
258
- return_type: Dict[str, Any],
259
- ) -> None:
260
- full_path = os.path.dirname(os.path.abspath(__file__))
261
-
262
- folders = path.split(".")
263
- for idx, folder in enumerate(folders):
264
- if idx + 1 == len(folders):
265
- # special handling for final level
266
- add_function_file(
267
- function_type,
268
- full_path,
269
- folder,
270
- function_id,
271
- function_description,
272
- arguments,
273
- return_type,
274
- )
275
- else:
276
- full_path = os.path.join(full_path, folder)
277
- if not os.path.exists(full_path):
278
- os.makedirs(full_path)
279
-
280
- # append to __init__.py file if nested folders
281
- next = folders[idx + 1] if idx + 2 < len(folders) else ""
282
- if next:
283
- _init_the_init(os.path.join(full_path, "__init__.py"))
284
- append_init(full_path, next)
285
-
286
-
287
- def generate_api(api_functions: List) -> None:
288
- for func in api_functions:
289
- create_function(*func)
290
- print("API functions generated!")
48
+ func_str = API_FUNCTION_TEMPLATE.format(
49
+ function_type="api",
50
+ function_name=function_name,
51
+ function_id=function_id,
52
+ function_description=function_description.replace('"', "'"),
53
+ args=args,
54
+ data=data,
55
+ api_response_type=add_type_import_path(function_name, api_response_type),
56
+ )
57
+ return func_str, func_type_defs
polyapi/auth.py ADDED
@@ -0,0 +1,151 @@
1
+ from typing import List, Dict, Any, Tuple
2
+ import uuid
3
+
4
+ from polyapi.typedefs import PropertySpecification
5
+ from polyapi.utils import parse_arguments, get_type_and_def
6
+
7
+
8
+ AUTH_DEFS_TEMPLATE = """
9
+ from typing import List, Dict, Any, TypedDict, Optional
10
+ {args_def}
11
+ {return_type_def}
12
+ """
13
+
14
+ GET_TOKEN_TEMPLATE = """
15
+ import asyncio
16
+ import socketio # type: ignore
17
+ from polyapi.config import get_api_key_and_url
18
+
19
+
20
+ async def getToken(clientId: str, clientSecret: str, scopes: List[str], callback, options: Optional[Dict[str, Any]] = None):
21
+ {description}
22
+ eventsClientId = "{client_id}"
23
+ function_id = "{function_id}"
24
+
25
+ options = options or {{}}
26
+ path = "/auth-providers/{function_id}/execute"
27
+ data = {{
28
+ "clientId": clientId,
29
+ "clientSecret": clientSecret,
30
+ "scopes": scopes,
31
+ "audience": options.get("audience"),
32
+ "callbackUrl": options.get("callbackUrl"),
33
+ "userId": options.get("userId"),
34
+ }}
35
+ resp = execute_post(path, data)
36
+ data = resp.json()
37
+ assert resp.status_code == 201, (resp.status_code, resp.content)
38
+
39
+ token = data.get("token")
40
+ url = data.get("url")
41
+ error = data.get("error")
42
+ if token:
43
+ return callback(token, url, error)
44
+ elif url and options.get("autoCloseOnUrl"):
45
+ return callback(token, url, error)
46
+
47
+ timeout = options.get("timeout", 120)
48
+
49
+ api_key, base_url = get_api_key_and_url()
50
+ socket = socketio.AsyncClient()
51
+ await socket.connect(base_url, transports=['websocket'], namespaces=['/events'])
52
+
53
+ async def closeEventHandler():
54
+ nonlocal socket
55
+ if not socket:
56
+ return
57
+
58
+ del socket.handlers['/events']['handleAuthFunctionEvent:{function_id}']
59
+ await socket.emit('unregisterAuthFunctionEventHandler', {{
60
+ "clientID": eventsClientId,
61
+ "functionId": function_id,
62
+ "apiKey": api_key
63
+ }}, namespace="/events")
64
+ await socket.disconnect()
65
+ socket = None
66
+
67
+
68
+ async def waitUntilTimeout(timeout):
69
+ await asyncio.sleep(timeout)
70
+ await closeEventHandler()
71
+
72
+
73
+ async def handleEvent(data):
74
+ nonlocal options
75
+ callback(data.get('token'), data.get('url'), data.get('error'))
76
+ if data.get('token') and options.get("autoCloseOnToken", True):
77
+ await closeEventHandler()
78
+
79
+
80
+ def registerCallback(registered: bool):
81
+ nonlocal socket
82
+ if registered:
83
+ socket.on('handleAuthFunctionEvent:{function_id}', handleEvent, namespace="/events")
84
+ callback(data.get('token'), data.get('url'), data.get('error'))
85
+
86
+ data2 = {{
87
+ "clientID": eventsClientId,
88
+ "functionId": function_id,
89
+ "apiKey": api_key
90
+ }}
91
+ await socket.emit('registerAuthFunctionEventHandler', data2, namespace="/events", callback=registerCallback)
92
+
93
+ # run timeout task in background
94
+ timeout = options.get("timeout", 120)
95
+ timeout_task = asyncio.create_task(waitUntilTimeout(timeout))
96
+
97
+ # cancel timeout task if socket.wait finishes before timeout up
98
+ await socket.wait()
99
+ timeout_task.cancel()
100
+
101
+ return {{"close": closeEventHandler}}
102
+ """
103
+
104
+ REFRESH_TOKEN_TEMPLATE = """
105
+ def refreshToken(token: str) -> str:
106
+ {description}
107
+ url = "/auth-providers/{function_id}/refresh"
108
+ resp = execute_post(url, {{"token": token}})
109
+ assert resp.status_code == 201, (resp.status_code, resp.content)
110
+ return resp.text
111
+ """
112
+
113
+ REVOKE_TOKEN_TEMPLATE = """
114
+ def revokeToken(token: str) -> None:
115
+ {description}
116
+ url = "/auth-providers/{function_id}/revoke"
117
+ resp = execute_post(url, {{"token": token}})
118
+ assert resp.status_code == 201, (resp.status_code, resp.content)
119
+ """
120
+
121
+
122
+ def render_auth_function(
123
+ function_type: str,
124
+ function_name: str,
125
+ function_id: str,
126
+ function_description: str,
127
+ arguments: List[PropertySpecification],
128
+ return_type: Dict[str, Any],
129
+ ) -> Tuple[str, str]:
130
+ """ renders getToken, revokeToken, refreshToken as appropriate
131
+ """
132
+ args, args_def = parse_arguments(function_name, arguments)
133
+ return_type_name, return_type_def = get_type_and_def(return_type) # type: ignore
134
+ func_type_defs = AUTH_DEFS_TEMPLATE.format(
135
+ args_def=args_def,
136
+ return_type_def=return_type_def,
137
+ )
138
+
139
+ func_str = ""
140
+
141
+ if function_description:
142
+ function_description = f'"""{function_description}"""'
143
+
144
+ if function_name == "getToken":
145
+ func_str = GET_TOKEN_TEMPLATE.format(function_id=function_id, description=function_description, client_id=uuid.uuid4().hex)
146
+ elif function_name == "refreshToken":
147
+ func_str = REFRESH_TOKEN_TEMPLATE.format(function_id=function_id, description=function_description)
148
+ elif function_name == "revokeToken":
149
+ func_str = REVOKE_TOKEN_TEMPLATE.format(function_id=function_id, description=function_description)
150
+
151
+ return func_str, func_type_defs
polyapi/cli.py CHANGED
@@ -1,21 +1,30 @@
1
1
  import argparse
2
2
 
3
+ from polyapi.utils import print_green
4
+
3
5
  from .config import clear_config
4
6
  from .generate import generate, clear
5
7
  from .function_cli import function_add_or_update
6
8
 
7
9
 
8
- CLI_COMMANDS = ["generate", "config", "clear", "function", "help"]
10
+ CLI_COMMANDS = ["setup", "generate", "function", "clear", "help"]
11
+
12
+ CLIENT_DESC = """Commands
13
+ python -m polyapi setup Setup your Poly connection
14
+ python -m polyapi generate Generates Poly library
15
+ python -m polyapi function <command> Manages functions
16
+ python -m polyapi clear Clear current generated Poly library
17
+ """
9
18
 
10
19
 
11
20
  def execute_from_cli():
12
21
  parser = argparse.ArgumentParser(
13
- prog="python -m polyapi", description="PolyAPI Client"
22
+ prog="python -m polyapi", description=CLIENT_DESC, formatter_class=argparse.RawTextHelpFormatter
14
23
  )
15
24
  parser.add_argument("--context", required=False, default="")
16
25
  parser.add_argument("--description", required=False, default="")
17
- parser.add_argument("--server", action="store_true", help="Pass --server if you want this to be a server function. By default, it will be a client function.")
18
- parser.add_argument("--logs", action="store_true", help="Pass --logs if you want to store and see the logs from this function executing")
26
+ parser.add_argument("--server", action="store_true", help="Pass --server when adding function to add a server function. By default, new functions are client.")
27
+ parser.add_argument("--logs", action="store_true", help="Pass --logs when adding function if you want to store and see the function logs.")
19
28
  parser.add_argument("command", choices=CLI_COMMANDS)
20
29
  parser.add_argument("subcommands", nargs="*")
21
30
  args = parser.parse_args()
@@ -24,10 +33,10 @@ def execute_from_cli():
24
33
  if command == "help":
25
34
  parser.print_help()
26
35
  elif command == "generate":
27
- print("Generating...")
36
+ print("Generating Poly functions...", end="")
28
37
  generate()
29
- elif command == "config":
30
- print("Clearing old config...")
38
+ print_green("DONE")
39
+ elif command == "setup":
31
40
  clear_config()
32
41
  generate()
33
42
  elif command == "clear":
polyapi/config.py CHANGED
@@ -69,6 +69,15 @@ def initialize_config():
69
69
 
70
70
 
71
71
  def clear_config():
72
+ if os.environ.get("POLY_API_KEY"):
73
+ print("Using POLY_API_KEY from environment. Please unset environment variable to manually set api key.")
74
+ return
75
+
76
+ global API_KEY
77
+ global API_URL
78
+ API_KEY = None
79
+ API_URL = None
80
+
72
81
  path = get_config_file_path()
73
82
  if os.path.exists(path):
74
83
  os.remove(path)
polyapi/execute.py CHANGED
@@ -5,10 +5,41 @@ from polyapi.exceptions import PolyApiException
5
5
 
6
6
 
7
7
  def execute(function_type, function_id, data) -> Response:
8
+ """ execute a specific function id/type
9
+ """
8
10
  api_key, api_url = get_api_key_and_url()
9
11
  headers = {"Authorization": f"Bearer {api_key}"}
10
12
  url = f"{api_url}/functions/{function_type}/{function_id}/execute"
11
13
  resp = requests.post(url, json=data, headers=headers)
14
+ if resp.status_code != 200 and resp.status_code != 201:
15
+ error_content = resp.content.decode("utf-8", errors="ignore")
16
+ raise PolyApiException(f"{resp.status_code}: {error_content}")
17
+ return resp
18
+
19
+
20
+ def execute_post(path, data):
21
+ api_key, api_url = get_api_key_and_url()
22
+ headers = {"Authorization": f"Bearer {api_key}"}
23
+ resp = requests.post(api_url + path, json=data, headers=headers)
24
+ return resp
25
+
26
+
27
+ def variable_get(variable_id: str) -> Response:
28
+ api_key, base_url = get_api_key_and_url()
29
+ headers = {"Authorization": f"Bearer {api_key}"}
30
+ url = f"{base_url}/variables/{variable_id}/value"
31
+ resp = requests.get(url, headers=headers)
32
+ if resp.status_code != 200 and resp.status_code != 201:
33
+ error_content = resp.content.decode("utf-8", errors="ignore")
34
+ raise PolyApiException(f"{resp.status_code}: {error_content}")
35
+ return resp
36
+
37
+
38
+ def variable_update(variable_id: str, value) -> Response:
39
+ api_key, base_url = get_api_key_and_url()
40
+ headers = {"Authorization": f"Bearer {api_key}"}
41
+ url = f"{base_url}/variables/{variable_id}/value"
42
+ resp = requests.patch(url, data={"value": value}, headers=headers)
12
43
  if resp.status_code != 200 and resp.status_code != 201:
13
44
  error_content = resp.content.decode("utf-8", errors="ignore")
14
45
  raise PolyApiException(f"{resp.status_code}: {error_content}")
polyapi/function_cli.py CHANGED
@@ -8,15 +8,16 @@ from typing_extensions import _TypedDictMeta # type: ignore
8
8
  import requests
9
9
  from stdlib_list import stdlib_list
10
10
  from pydantic import TypeAdapter
11
- from polyapi.generate import generate
11
+ from polyapi.generate import get_functions_and_parse, generate_functions
12
12
  from polyapi.config import get_api_key_and_url
13
13
  from polyapi.constants import PYTHON_TO_JSONSCHEMA_TYPE_MAP
14
- from polyapi.utils import get_auth_headers
14
+ from polyapi.utils import get_auth_headers, print_green, print_red, print_yellow
15
+ import importlib
15
16
 
16
17
 
17
18
  # these libraries are already installed in the base docker image
18
19
  # and shouldnt be included in additional requirements
19
- BASE_REQUIREMENTS = {"requests", "typing_extensions", "jsonschema-gentypes", "pydantic"}
20
+ BASE_REQUIREMENTS = {"polyapi", "requests", "typing_extensions", "jsonschema-gentypes", "pydantic"}
20
21
  all_stdlib_symbols = stdlib_list('.'.join([str(v) for v in sys.version_info[0:2]]))
21
22
  BASE_REQUIREMENTS.update(all_stdlib_symbols) # dont need to pip install stuff in the python standard library
22
23
 
@@ -141,6 +142,14 @@ def _parse_code(code: str, function_name: str):
141
142
  return parsed_args, return_type, return_type_schema, requirements
142
143
 
143
144
 
145
+ def _func_already_exists(context: str, function_name: str) -> bool:
146
+ try:
147
+ module = importlib.import_module(f"polyapi.poly.{context}")
148
+ return bool(getattr(module, function_name, False))
149
+ except ModuleNotFoundError:
150
+ return False
151
+
152
+
144
153
  def function_add_or_update(
145
154
  context: str, description: str, server: bool, logs_enabled: bool, subcommands: List
146
155
  ):
@@ -150,6 +159,9 @@ def function_add_or_update(
150
159
  parser.add_argument("filename")
151
160
  args = parser.parse_args(subcommands)
152
161
 
162
+ verb = "Updating" if _func_already_exists(context, args.function_name) else "Adding"
163
+ print(f"{verb} custom server side function...", end="")
164
+
153
165
  with open(args.filename, "r") as f:
154
166
  code = f.read()
155
167
 
@@ -162,11 +174,13 @@ def function_add_or_update(
162
174
  ) = _parse_code(code, args.function_name)
163
175
 
164
176
  if not return_type:
165
- print(
166
- f"Error: function named {args.function_name} not found as top-level function in file. Exiting."
167
- )
177
+ print_red("ERROR")
178
+ print(f"Function {args.function_name} not found as top-level function in {args.filename}")
168
179
  sys.exit(1)
169
180
 
181
+ if requirements:
182
+ print_yellow('\nPlease note that deploying your functions will take a few minutes because it makes use of libraries other than polyapi.')
183
+
170
184
  data = {
171
185
  "context": context,
172
186
  "name": args.function_name,
@@ -189,13 +203,15 @@ def function_add_or_update(
189
203
  # url = f"{base_url}/functions/client"
190
204
 
191
205
  headers = get_auth_headers(api_key)
192
- print("Adding function...")
193
206
  resp = requests.post(url, headers=headers, json=data)
194
207
  if resp.status_code == 201:
208
+ print_green("DEPLOYED")
195
209
  function_id = resp.json()["id"]
196
- print(f"Function added successfully. Function id is {function_id}")
197
- print("Regenerating library...")
198
- generate()
210
+ print(f"Function ID: {function_id}")
211
+ print("Generating new custom function...", end="")
212
+ functions = get_functions_and_parse(limit_ids=[function_id])
213
+ generate_functions(functions)
214
+ print_green("DONE")
199
215
  else:
200
216
  print("Error adding function.")
201
217
  print(resp.status_code)