kailash 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.
- kailash/__init__.py +31 -0
- kailash/__main__.py +11 -0
- kailash/cli/__init__.py +5 -0
- kailash/cli/commands.py +563 -0
- kailash/manifest.py +778 -0
- kailash/nodes/__init__.py +23 -0
- kailash/nodes/ai/__init__.py +26 -0
- kailash/nodes/ai/agents.py +417 -0
- kailash/nodes/ai/models.py +488 -0
- kailash/nodes/api/__init__.py +52 -0
- kailash/nodes/api/auth.py +567 -0
- kailash/nodes/api/graphql.py +480 -0
- kailash/nodes/api/http.py +598 -0
- kailash/nodes/api/rate_limiting.py +572 -0
- kailash/nodes/api/rest.py +665 -0
- kailash/nodes/base.py +1032 -0
- kailash/nodes/base_async.py +128 -0
- kailash/nodes/code/__init__.py +32 -0
- kailash/nodes/code/python.py +1021 -0
- kailash/nodes/data/__init__.py +125 -0
- kailash/nodes/data/readers.py +496 -0
- kailash/nodes/data/sharepoint_graph.py +623 -0
- kailash/nodes/data/sql.py +380 -0
- kailash/nodes/data/streaming.py +1168 -0
- kailash/nodes/data/vector_db.py +964 -0
- kailash/nodes/data/writers.py +529 -0
- kailash/nodes/logic/__init__.py +6 -0
- kailash/nodes/logic/async_operations.py +702 -0
- kailash/nodes/logic/operations.py +551 -0
- kailash/nodes/transform/__init__.py +5 -0
- kailash/nodes/transform/processors.py +379 -0
- kailash/runtime/__init__.py +6 -0
- kailash/runtime/async_local.py +356 -0
- kailash/runtime/docker.py +697 -0
- kailash/runtime/local.py +434 -0
- kailash/runtime/parallel.py +557 -0
- kailash/runtime/runner.py +110 -0
- kailash/runtime/testing.py +347 -0
- kailash/sdk_exceptions.py +307 -0
- kailash/tracking/__init__.py +7 -0
- kailash/tracking/manager.py +885 -0
- kailash/tracking/metrics_collector.py +342 -0
- kailash/tracking/models.py +535 -0
- kailash/tracking/storage/__init__.py +0 -0
- kailash/tracking/storage/base.py +113 -0
- kailash/tracking/storage/database.py +619 -0
- kailash/tracking/storage/filesystem.py +543 -0
- kailash/utils/__init__.py +0 -0
- kailash/utils/export.py +924 -0
- kailash/utils/templates.py +680 -0
- kailash/visualization/__init__.py +62 -0
- kailash/visualization/api.py +732 -0
- kailash/visualization/dashboard.py +951 -0
- kailash/visualization/performance.py +808 -0
- kailash/visualization/reports.py +1471 -0
- kailash/workflow/__init__.py +15 -0
- kailash/workflow/builder.py +245 -0
- kailash/workflow/graph.py +827 -0
- kailash/workflow/mermaid_visualizer.py +628 -0
- kailash/workflow/mock_registry.py +63 -0
- kailash/workflow/runner.py +302 -0
- kailash/workflow/state.py +238 -0
- kailash/workflow/visualization.py +588 -0
- kailash-0.1.0.dist-info/METADATA +710 -0
- kailash-0.1.0.dist-info/RECORD +69 -0
- kailash-0.1.0.dist-info/WHEEL +5 -0
- kailash-0.1.0.dist-info/entry_points.txt +2 -0
- kailash-0.1.0.dist-info/licenses/LICENSE +21 -0
- kailash-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,567 @@
|
|
1
|
+
"""Authentication nodes for API requests in the Kailash SDK.
|
2
|
+
|
3
|
+
This module provides nodes for handling various authentication methods for API
|
4
|
+
requests. These nodes can be used in workflows to authenticate requests to
|
5
|
+
external services.
|
6
|
+
|
7
|
+
Key Components:
|
8
|
+
- BasicAuthNode: Basic authentication (username/password)
|
9
|
+
- OAuth2Node: OAuth 2.0 authentication flow
|
10
|
+
- APIKeyNode: API key authentication
|
11
|
+
"""
|
12
|
+
|
13
|
+
import base64
|
14
|
+
import time
|
15
|
+
from typing import Any, Dict
|
16
|
+
|
17
|
+
import requests
|
18
|
+
|
19
|
+
from kailash.nodes.api.http import HTTPRequestNode
|
20
|
+
from kailash.nodes.base import Node, NodeParameter, register_node
|
21
|
+
from kailash.sdk_exceptions import NodeExecutionError, NodeValidationError
|
22
|
+
|
23
|
+
|
24
|
+
@register_node(alias="BasicAuth")
|
25
|
+
class BasicAuthNode(Node):
|
26
|
+
"""Node for adding Basic Authentication to API requests.
|
27
|
+
|
28
|
+
This node generates HTTP headers for Basic Authentication and can be used
|
29
|
+
to authenticate requests to APIs that support this method.
|
30
|
+
|
31
|
+
Design Purpose:
|
32
|
+
- Provide a simple way to add Basic Auth to API requests
|
33
|
+
- Abstract away the encoding details
|
34
|
+
- Support secure credential handling
|
35
|
+
|
36
|
+
Upstream Usage:
|
37
|
+
- Workflow: Creates and configures with API credentials
|
38
|
+
- HTTP/REST nodes: Consume the auth headers
|
39
|
+
|
40
|
+
Downstream Consumers:
|
41
|
+
- HTTPRequestNode: Uses auth headers for requests
|
42
|
+
- RESTClientNode: Uses auth headers for API calls
|
43
|
+
"""
|
44
|
+
|
45
|
+
def get_parameters(self) -> Dict[str, NodeParameter]:
|
46
|
+
"""Define the parameters this node accepts.
|
47
|
+
|
48
|
+
Returns:
|
49
|
+
Dictionary of parameter definitions
|
50
|
+
"""
|
51
|
+
return {
|
52
|
+
"username": NodeParameter(
|
53
|
+
name="username",
|
54
|
+
type=str,
|
55
|
+
required=True,
|
56
|
+
description="Username for Basic Authentication",
|
57
|
+
),
|
58
|
+
"password": NodeParameter(
|
59
|
+
name="password",
|
60
|
+
type=str,
|
61
|
+
required=True,
|
62
|
+
description="Password for Basic Authentication",
|
63
|
+
),
|
64
|
+
}
|
65
|
+
|
66
|
+
def get_output_schema(self) -> Dict[str, NodeParameter]:
|
67
|
+
"""Define the output schema for this node.
|
68
|
+
|
69
|
+
Returns:
|
70
|
+
Dictionary of output parameter definitions
|
71
|
+
"""
|
72
|
+
return {
|
73
|
+
"headers": NodeParameter(
|
74
|
+
name="headers",
|
75
|
+
type=dict,
|
76
|
+
required=True,
|
77
|
+
description="HTTP headers with Basic Authentication",
|
78
|
+
),
|
79
|
+
"auth_type": NodeParameter(
|
80
|
+
name="auth_type",
|
81
|
+
type=str,
|
82
|
+
required=True,
|
83
|
+
description="Authentication type (always 'basic')",
|
84
|
+
),
|
85
|
+
}
|
86
|
+
|
87
|
+
def run(self, **kwargs) -> Dict[str, Any]:
|
88
|
+
"""Generate Basic Authentication headers.
|
89
|
+
|
90
|
+
Args:
|
91
|
+
username (str): Username for authentication
|
92
|
+
password (str): Password for authentication
|
93
|
+
|
94
|
+
Returns:
|
95
|
+
Dictionary containing:
|
96
|
+
headers: HTTP headers with Basic Authentication
|
97
|
+
auth_type: Authentication type ('basic')
|
98
|
+
"""
|
99
|
+
username = kwargs.get("username")
|
100
|
+
password = kwargs.get("password")
|
101
|
+
|
102
|
+
# Validate required parameters
|
103
|
+
if not username:
|
104
|
+
raise NodeValidationError("Username is required for Basic Authentication")
|
105
|
+
|
106
|
+
if not password:
|
107
|
+
raise NodeValidationError("Password is required for Basic Authentication")
|
108
|
+
|
109
|
+
# Generate auth string
|
110
|
+
auth_string = f"{username}:{password}"
|
111
|
+
encoded_auth = base64.b64encode(auth_string.encode()).decode()
|
112
|
+
|
113
|
+
# Create headers
|
114
|
+
headers = {"Authorization": f"Basic {encoded_auth}"}
|
115
|
+
|
116
|
+
return {"headers": headers, "auth_type": "basic"}
|
117
|
+
|
118
|
+
|
119
|
+
@register_node(alias="OAuth2")
|
120
|
+
class OAuth2Node(Node):
|
121
|
+
"""Node for handling OAuth 2.0 authentication flows.
|
122
|
+
|
123
|
+
This node supports various OAuth 2.0 flows including:
|
124
|
+
- Client Credentials
|
125
|
+
- Password Grant
|
126
|
+
- Authorization Code (with PKCE)
|
127
|
+
- Refresh Token
|
128
|
+
|
129
|
+
It handles token acquisition, storage, and renewal for API requests.
|
130
|
+
|
131
|
+
Design Purpose:
|
132
|
+
- Simplify OAuth 2.0 authentication for API requests
|
133
|
+
- Handle token lifecycle (request, store, refresh)
|
134
|
+
- Support multiple OAuth flows
|
135
|
+
- Abstract away OAuth implementation details
|
136
|
+
|
137
|
+
Upstream Usage:
|
138
|
+
- Workflow: Creates and configures with OAuth credentials
|
139
|
+
- HTTP/REST nodes: Consume the auth headers
|
140
|
+
|
141
|
+
Downstream Consumers:
|
142
|
+
- HTTPRequestNode: Uses auth headers for requests
|
143
|
+
- RESTClientNode: Uses auth headers for API calls
|
144
|
+
"""
|
145
|
+
|
146
|
+
def __init__(self, **kwargs):
|
147
|
+
"""Initialize the OAuth2 node.
|
148
|
+
|
149
|
+
Args:
|
150
|
+
token_url (str): OAuth token endpoint URL
|
151
|
+
client_id (str): OAuth client ID
|
152
|
+
client_secret (str, optional): OAuth client secret
|
153
|
+
grant_type (str): OAuth grant type
|
154
|
+
scope (str, optional): OAuth scopes (space-separated)
|
155
|
+
username (str, optional): Username for password grant
|
156
|
+
password (str, optional): Password for password grant
|
157
|
+
refresh_token (str, optional): Refresh token for refresh flow
|
158
|
+
**kwargs: Additional parameters passed to base Node
|
159
|
+
"""
|
160
|
+
super().__init__(**kwargs)
|
161
|
+
self.http_node = HTTPRequestNode(**kwargs)
|
162
|
+
self.token_data = None # Will store token information
|
163
|
+
self.token_expires_at = 0 # Timestamp when token expires
|
164
|
+
|
165
|
+
def get_parameters(self) -> Dict[str, NodeParameter]:
|
166
|
+
"""Define the parameters this node accepts.
|
167
|
+
|
168
|
+
Returns:
|
169
|
+
Dictionary of parameter definitions
|
170
|
+
"""
|
171
|
+
return {
|
172
|
+
"token_url": NodeParameter(
|
173
|
+
name="token_url",
|
174
|
+
type=str,
|
175
|
+
required=True,
|
176
|
+
description="OAuth token endpoint URL",
|
177
|
+
),
|
178
|
+
"client_id": NodeParameter(
|
179
|
+
name="client_id", type=str, required=True, description="OAuth client ID"
|
180
|
+
),
|
181
|
+
"client_secret": NodeParameter(
|
182
|
+
name="client_secret",
|
183
|
+
type=str,
|
184
|
+
required=False,
|
185
|
+
default=None,
|
186
|
+
description="OAuth client secret",
|
187
|
+
),
|
188
|
+
"grant_type": NodeParameter(
|
189
|
+
name="grant_type",
|
190
|
+
type=str,
|
191
|
+
required=True,
|
192
|
+
default="client_credentials",
|
193
|
+
description="OAuth grant type (client_credentials, password, authorization_code, refresh_token)",
|
194
|
+
),
|
195
|
+
"scope": NodeParameter(
|
196
|
+
name="scope",
|
197
|
+
type=str,
|
198
|
+
required=False,
|
199
|
+
default=None,
|
200
|
+
description="OAuth scopes (space-separated)",
|
201
|
+
),
|
202
|
+
"username": NodeParameter(
|
203
|
+
name="username",
|
204
|
+
type=str,
|
205
|
+
required=False,
|
206
|
+
default=None,
|
207
|
+
description="Username (for password grant)",
|
208
|
+
),
|
209
|
+
"password": NodeParameter(
|
210
|
+
name="password",
|
211
|
+
type=str,
|
212
|
+
required=False,
|
213
|
+
default=None,
|
214
|
+
description="Password (for password grant)",
|
215
|
+
),
|
216
|
+
"refresh_token": NodeParameter(
|
217
|
+
name="refresh_token",
|
218
|
+
type=str,
|
219
|
+
required=False,
|
220
|
+
default=None,
|
221
|
+
description="Refresh token (for refresh_token grant)",
|
222
|
+
),
|
223
|
+
"token_storage": NodeParameter(
|
224
|
+
name="token_storage",
|
225
|
+
type=dict,
|
226
|
+
required=False,
|
227
|
+
default=None,
|
228
|
+
description="Token storage configuration",
|
229
|
+
),
|
230
|
+
"auto_refresh": NodeParameter(
|
231
|
+
name="auto_refresh",
|
232
|
+
type=bool,
|
233
|
+
required=False,
|
234
|
+
default=True,
|
235
|
+
description="Whether to automatically refresh expired tokens",
|
236
|
+
),
|
237
|
+
}
|
238
|
+
|
239
|
+
def get_output_schema(self) -> Dict[str, NodeParameter]:
|
240
|
+
"""Define the output schema for this node.
|
241
|
+
|
242
|
+
Returns:
|
243
|
+
Dictionary of output parameter definitions
|
244
|
+
"""
|
245
|
+
return {
|
246
|
+
"headers": NodeParameter(
|
247
|
+
name="headers",
|
248
|
+
type=dict,
|
249
|
+
required=True,
|
250
|
+
description="HTTP headers with OAuth token",
|
251
|
+
),
|
252
|
+
"token_data": NodeParameter(
|
253
|
+
name="token_data",
|
254
|
+
type=dict,
|
255
|
+
required=True,
|
256
|
+
description="Complete token response data",
|
257
|
+
),
|
258
|
+
"auth_type": NodeParameter(
|
259
|
+
name="auth_type",
|
260
|
+
type=str,
|
261
|
+
required=True,
|
262
|
+
description="Authentication type (always 'oauth2')",
|
263
|
+
),
|
264
|
+
"expires_in": NodeParameter(
|
265
|
+
name="expires_in",
|
266
|
+
type=int,
|
267
|
+
required=False,
|
268
|
+
description="Seconds until token expiration",
|
269
|
+
),
|
270
|
+
}
|
271
|
+
|
272
|
+
def _get_token(self, **kwargs) -> Dict[str, Any]:
|
273
|
+
"""Get an OAuth token using the configured grant type.
|
274
|
+
|
275
|
+
This method handles different grant types with appropriate parameters.
|
276
|
+
|
277
|
+
Args:
|
278
|
+
kwargs: Parameters from run method
|
279
|
+
|
280
|
+
Returns:
|
281
|
+
Dictionary containing token response data
|
282
|
+
|
283
|
+
Raises:
|
284
|
+
NodeExecutionError: If token acquisition fails
|
285
|
+
"""
|
286
|
+
token_url = kwargs.get("token_url")
|
287
|
+
client_id = kwargs.get("client_id")
|
288
|
+
client_secret = kwargs.get("client_secret")
|
289
|
+
grant_type = kwargs.get("grant_type", "client_credentials")
|
290
|
+
scope = kwargs.get("scope")
|
291
|
+
username = kwargs.get("username")
|
292
|
+
password = kwargs.get("password")
|
293
|
+
refresh_token = (
|
294
|
+
kwargs.get("refresh_token")
|
295
|
+
or self.token_data
|
296
|
+
and self.token_data.get("refresh_token")
|
297
|
+
)
|
298
|
+
|
299
|
+
# Build request data based on grant type
|
300
|
+
data = {"grant_type": grant_type, "client_id": client_id}
|
301
|
+
|
302
|
+
if client_secret:
|
303
|
+
data["client_secret"] = client_secret
|
304
|
+
|
305
|
+
if scope:
|
306
|
+
data["scope"] = scope
|
307
|
+
|
308
|
+
# Add grant-specific parameters
|
309
|
+
if grant_type == "password":
|
310
|
+
if not username or not password:
|
311
|
+
raise NodeValidationError(
|
312
|
+
"Username and password are required for password grant type"
|
313
|
+
)
|
314
|
+
data["username"] = username
|
315
|
+
data["password"] = password
|
316
|
+
|
317
|
+
elif grant_type == "refresh_token":
|
318
|
+
if not refresh_token:
|
319
|
+
raise NodeValidationError(
|
320
|
+
"Refresh token is required for refresh_token grant type"
|
321
|
+
)
|
322
|
+
data["refresh_token"] = refresh_token
|
323
|
+
|
324
|
+
# Make token request
|
325
|
+
self.logger.info(
|
326
|
+
f"Requesting OAuth token from {token_url} using {grant_type} grant"
|
327
|
+
)
|
328
|
+
|
329
|
+
headers = {"Content-Type": "application/x-www-form-urlencoded"}
|
330
|
+
|
331
|
+
# Some OAuth servers require Basic auth with client credentials
|
332
|
+
if client_id and client_secret:
|
333
|
+
auth_string = f"{client_id}:{client_secret}"
|
334
|
+
encoded_auth = base64.b64encode(auth_string.encode()).decode()
|
335
|
+
headers["Authorization"] = f"Basic {encoded_auth}"
|
336
|
+
|
337
|
+
# Remove from form body if using auth header
|
338
|
+
if "client_secret" in data:
|
339
|
+
del data["client_secret"]
|
340
|
+
|
341
|
+
try:
|
342
|
+
response = requests.post(token_url, data=data, headers=headers, timeout=30)
|
343
|
+
|
344
|
+
response.raise_for_status()
|
345
|
+
token_data = response.json()
|
346
|
+
|
347
|
+
# Check for required fields in response
|
348
|
+
if "access_token" not in token_data:
|
349
|
+
raise NodeExecutionError(
|
350
|
+
f"Invalid OAuth response: missing access_token. Response: {token_data}"
|
351
|
+
)
|
352
|
+
|
353
|
+
return token_data
|
354
|
+
|
355
|
+
except requests.RequestException as e:
|
356
|
+
raise NodeExecutionError(f"Failed to acquire OAuth token: {str(e)}") from e
|
357
|
+
except ValueError as e:
|
358
|
+
raise NodeExecutionError(
|
359
|
+
f"Failed to parse OAuth token response: {str(e)}"
|
360
|
+
) from e
|
361
|
+
|
362
|
+
def run(self, **kwargs) -> Dict[str, Any]:
|
363
|
+
"""Get OAuth authentication headers.
|
364
|
+
|
365
|
+
This method handles token acquisition, caching, and renewal based on
|
366
|
+
the configured OAuth flow.
|
367
|
+
|
368
|
+
Args:
|
369
|
+
token_url (str): OAuth token endpoint URL
|
370
|
+
client_id (str): OAuth client ID
|
371
|
+
client_secret (str, optional): OAuth client secret
|
372
|
+
grant_type (str): OAuth grant type
|
373
|
+
scope (str, optional): OAuth scopes
|
374
|
+
username (str, optional): Username for password grant
|
375
|
+
password (str, optional): Password for password grant
|
376
|
+
refresh_token (str, optional): Refresh token for refresh flow
|
377
|
+
token_storage (dict, optional): Token storage configuration
|
378
|
+
auto_refresh (bool, optional): Whether to auto-refresh expired tokens
|
379
|
+
|
380
|
+
Returns:
|
381
|
+
Dictionary containing:
|
382
|
+
headers: HTTP headers with OAuth token
|
383
|
+
token_data: Complete token response
|
384
|
+
auth_type: Authentication type ('oauth2')
|
385
|
+
expires_in: Seconds until token expiration
|
386
|
+
"""
|
387
|
+
force_refresh = kwargs.get("force_refresh", False)
|
388
|
+
auto_refresh = kwargs.get("auto_refresh", True)
|
389
|
+
|
390
|
+
current_time = time.time()
|
391
|
+
|
392
|
+
# Check if we need to refresh the token
|
393
|
+
if (
|
394
|
+
not self.token_data
|
395
|
+
or force_refresh
|
396
|
+
or (auto_refresh and current_time >= self.token_expires_at)
|
397
|
+
):
|
398
|
+
|
399
|
+
# Get new token
|
400
|
+
self.token_data = self._get_token(**kwargs)
|
401
|
+
|
402
|
+
# Calculate expiration time
|
403
|
+
expires_in = self.token_data.get("expires_in", 3600) # Default 1 hour
|
404
|
+
self.token_expires_at = current_time + expires_in
|
405
|
+
|
406
|
+
self.logger.info(
|
407
|
+
f"Acquired new OAuth token, expires in {expires_in} seconds"
|
408
|
+
)
|
409
|
+
|
410
|
+
# Calculate current expiration time
|
411
|
+
current_expires_in = max(0, int(self.token_expires_at - current_time))
|
412
|
+
|
413
|
+
# Create headers
|
414
|
+
headers = {"Authorization": f"Bearer {self.token_data['access_token']}"}
|
415
|
+
|
416
|
+
return {
|
417
|
+
"headers": headers,
|
418
|
+
"token_data": self.token_data,
|
419
|
+
"auth_type": "oauth2",
|
420
|
+
"expires_in": current_expires_in,
|
421
|
+
}
|
422
|
+
|
423
|
+
|
424
|
+
@register_node(alias="APIKey")
|
425
|
+
class APIKeyNode(Node):
|
426
|
+
"""Node for API key authentication.
|
427
|
+
|
428
|
+
This node handles API key authentication in various formats:
|
429
|
+
- Header-based (e.g., "X-API-Key: key123")
|
430
|
+
- Query parameter (e.g., "?api_key=key123")
|
431
|
+
- Request body parameter
|
432
|
+
|
433
|
+
Design Purpose:
|
434
|
+
- Simplify API key authentication for API requests
|
435
|
+
- Support different API key placement formats
|
436
|
+
- Abstract away implementation details
|
437
|
+
|
438
|
+
Upstream Usage:
|
439
|
+
- Workflow: Creates and configures with API key
|
440
|
+
- HTTP/REST nodes: Consume the auth data
|
441
|
+
|
442
|
+
Downstream Consumers:
|
443
|
+
- HTTPRequestNode: Uses auth data for requests
|
444
|
+
- RESTClientNode: Uses auth data for API calls
|
445
|
+
"""
|
446
|
+
|
447
|
+
def get_parameters(self) -> Dict[str, NodeParameter]:
|
448
|
+
"""Define the parameters this node accepts.
|
449
|
+
|
450
|
+
Returns:
|
451
|
+
Dictionary of parameter definitions
|
452
|
+
"""
|
453
|
+
return {
|
454
|
+
"api_key": NodeParameter(
|
455
|
+
name="api_key", type=str, required=True, description="API key value"
|
456
|
+
),
|
457
|
+
"location": NodeParameter(
|
458
|
+
name="location",
|
459
|
+
type=str,
|
460
|
+
required=True,
|
461
|
+
default="header",
|
462
|
+
description="Where to place the API key (header, query, body)",
|
463
|
+
),
|
464
|
+
"param_name": NodeParameter(
|
465
|
+
name="param_name",
|
466
|
+
type=str,
|
467
|
+
required=True,
|
468
|
+
default="X-API-Key",
|
469
|
+
description="Parameter name for the API key",
|
470
|
+
),
|
471
|
+
"prefix": NodeParameter(
|
472
|
+
name="prefix",
|
473
|
+
type=str,
|
474
|
+
required=False,
|
475
|
+
default=None,
|
476
|
+
description="Prefix for the API key value (e.g., 'Bearer')",
|
477
|
+
),
|
478
|
+
}
|
479
|
+
|
480
|
+
def get_output_schema(self) -> Dict[str, NodeParameter]:
|
481
|
+
"""Define the output schema for this node.
|
482
|
+
|
483
|
+
Returns:
|
484
|
+
Dictionary of output parameter definitions
|
485
|
+
"""
|
486
|
+
return {
|
487
|
+
"headers": NodeParameter(
|
488
|
+
name="headers",
|
489
|
+
type=dict,
|
490
|
+
required=False,
|
491
|
+
description="HTTP headers with API key (if location=header)",
|
492
|
+
),
|
493
|
+
"query_params": NodeParameter(
|
494
|
+
name="query_params",
|
495
|
+
type=dict,
|
496
|
+
required=False,
|
497
|
+
description="Query parameters with API key (if location=query)",
|
498
|
+
),
|
499
|
+
"body_params": NodeParameter(
|
500
|
+
name="body_params",
|
501
|
+
type=dict,
|
502
|
+
required=False,
|
503
|
+
description="Body parameters with API key (if location=body)",
|
504
|
+
),
|
505
|
+
"auth_type": NodeParameter(
|
506
|
+
name="auth_type",
|
507
|
+
type=str,
|
508
|
+
required=True,
|
509
|
+
description="Authentication type (always 'api_key')",
|
510
|
+
),
|
511
|
+
}
|
512
|
+
|
513
|
+
def run(self, **kwargs) -> Dict[str, Any]:
|
514
|
+
"""Generate API key authentication data.
|
515
|
+
|
516
|
+
Args:
|
517
|
+
api_key (str): API key value
|
518
|
+
location (str): Where to place the API key (header, query, body)
|
519
|
+
param_name (str): Parameter name for the API key
|
520
|
+
prefix (str, optional): Prefix for the API key value
|
521
|
+
|
522
|
+
Returns:
|
523
|
+
Dictionary containing:
|
524
|
+
headers: HTTP headers with API key (if location=header)
|
525
|
+
query_params: Query parameters with API key (if location=query)
|
526
|
+
body_params: Body parameters with API key (if location=body)
|
527
|
+
auth_type: Authentication type ('api_key')
|
528
|
+
"""
|
529
|
+
api_key = kwargs.get("api_key")
|
530
|
+
location = kwargs.get("location", "header").lower()
|
531
|
+
param_name = kwargs.get("param_name", "X-API-Key")
|
532
|
+
prefix = kwargs.get("prefix")
|
533
|
+
|
534
|
+
# Validate required parameters
|
535
|
+
if not api_key:
|
536
|
+
raise NodeValidationError("API key is required")
|
537
|
+
|
538
|
+
if location not in ("header", "query", "body"):
|
539
|
+
raise NodeValidationError(
|
540
|
+
f"Invalid API key location: {location}. "
|
541
|
+
"Must be one of: header, query, body"
|
542
|
+
)
|
543
|
+
|
544
|
+
# Format API key value
|
545
|
+
key_value = api_key
|
546
|
+
if prefix:
|
547
|
+
key_value = f"{prefix} {api_key}"
|
548
|
+
|
549
|
+
# Create result based on location
|
550
|
+
result = {"auth_type": "api_key"}
|
551
|
+
|
552
|
+
if location == "header":
|
553
|
+
result["headers"] = {param_name: key_value}
|
554
|
+
result["query_params"] = {}
|
555
|
+
result["body_params"] = {}
|
556
|
+
|
557
|
+
elif location == "query":
|
558
|
+
result["headers"] = {}
|
559
|
+
result["query_params"] = {param_name: key_value}
|
560
|
+
result["body_params"] = {}
|
561
|
+
|
562
|
+
elif location == "body":
|
563
|
+
result["headers"] = {}
|
564
|
+
result["query_params"] = {}
|
565
|
+
result["body_params"] = {param_name: key_value}
|
566
|
+
|
567
|
+
return result
|