@cloudstreamsoftware/claude-tools 1.0.0 → 1.2.0
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.
- package/README.md +152 -37
- package/agents/INDEX.md +183 -0
- package/agents/architect.md +247 -0
- package/agents/build-error-resolver.md +555 -0
- package/agents/catalyst-deployer.md +132 -0
- package/agents/code-reviewer.md +121 -0
- package/agents/compliance-auditor.md +148 -0
- package/agents/creator-architect.md +395 -0
- package/agents/deluge-reviewer.md +98 -0
- package/agents/doc-updater.md +471 -0
- package/agents/e2e-runner.md +711 -0
- package/agents/planner.md +122 -0
- package/agents/refactor-cleaner.md +309 -0
- package/agents/security-reviewer.md +582 -0
- package/agents/tdd-guide.md +302 -0
- package/bin/cloudstream-setup.js +16 -6
- package/config/versions.json +63 -0
- package/dist/hooks/hooks.json +209 -0
- package/dist/index.js +47 -0
- package/dist/lib/asset-value.js +609 -0
- package/dist/lib/client-manager.js +300 -0
- package/dist/lib/command-matcher.js +242 -0
- package/dist/lib/cross-session-patterns.js +754 -0
- package/dist/lib/intent-classifier.js +1075 -0
- package/dist/lib/package-manager.js +374 -0
- package/dist/lib/recommendation-engine.js +597 -0
- package/dist/lib/session-memory.js +489 -0
- package/dist/lib/skill-effectiveness.js +486 -0
- package/dist/lib/skill-matcher.js +595 -0
- package/dist/lib/tutorial-metrics.js +242 -0
- package/dist/lib/tutorial-progress.js +209 -0
- package/dist/lib/tutorial-renderer.js +431 -0
- package/dist/lib/utils.js +380 -0
- package/dist/lib/verify-formatter.js +143 -0
- package/dist/lib/workflow-state.js +249 -0
- package/hooks/hooks.json +209 -0
- package/package.json +5 -1
- package/scripts/aggregate-sessions.js +290 -0
- package/scripts/branch-name-validator.js +291 -0
- package/scripts/build.js +101 -0
- package/scripts/commands/client-switch.js +231 -0
- package/scripts/deprecate-skill.js +610 -0
- package/scripts/diagnose.js +324 -0
- package/scripts/doc-freshness.js +168 -0
- package/scripts/generate-weekly-digest.js +393 -0
- package/scripts/health-check.js +270 -0
- package/scripts/hooks/credential-check.js +101 -0
- package/scripts/hooks/evaluate-session.js +81 -0
- package/scripts/hooks/pre-compact.js +66 -0
- package/scripts/hooks/prompt-analyzer.js +276 -0
- package/scripts/hooks/prompt-router.js +422 -0
- package/scripts/hooks/quality-gate-enforcer.js +371 -0
- package/scripts/hooks/session-end.js +156 -0
- package/scripts/hooks/session-start.js +195 -0
- package/scripts/hooks/skill-injector.js +333 -0
- package/scripts/hooks/suggest-compact.js +58 -0
- package/scripts/lib/asset-value.js +609 -0
- package/scripts/lib/client-manager.js +300 -0
- package/scripts/lib/command-matcher.js +242 -0
- package/scripts/lib/cross-session-patterns.js +754 -0
- package/scripts/lib/intent-classifier.js +1075 -0
- package/scripts/lib/package-manager.js +374 -0
- package/scripts/lib/recommendation-engine.js +597 -0
- package/scripts/lib/session-memory.js +489 -0
- package/scripts/lib/skill-effectiveness.js +486 -0
- package/scripts/lib/skill-matcher.js +595 -0
- package/scripts/lib/tutorial-metrics.js +242 -0
- package/scripts/lib/tutorial-progress.js +209 -0
- package/scripts/lib/tutorial-renderer.js +431 -0
- package/scripts/lib/utils.js +380 -0
- package/scripts/lib/verify-formatter.js +143 -0
- package/scripts/lib/workflow-state.js +249 -0
- package/scripts/onboard.js +363 -0
- package/scripts/quarterly-report.js +692 -0
- package/scripts/setup-package-manager.js +204 -0
- package/scripts/sync-upstream.js +391 -0
- package/scripts/test.js +108 -0
- package/scripts/tutorial-runner.js +351 -0
- package/scripts/validate-all.js +201 -0
- package/scripts/verifiers/agents.js +245 -0
- package/scripts/verifiers/config.js +186 -0
- package/scripts/verifiers/environment.js +123 -0
- package/scripts/verifiers/hooks.js +188 -0
- package/scripts/verifiers/index.js +38 -0
- package/scripts/verifiers/persistence.js +140 -0
- package/scripts/verifiers/plugin.js +215 -0
- package/scripts/verifiers/skills.js +209 -0
- package/scripts/verify-setup.js +164 -0
- package/skills/INDEX.md +157 -0
- package/skills/backend-patterns/SKILL.md +586 -0
- package/skills/backend-patterns/catalyst-patterns.md +128 -0
- package/skills/bigquery-patterns/SKILL.md +27 -0
- package/skills/bigquery-patterns/performance-optimization.md +518 -0
- package/skills/bigquery-patterns/query-patterns.md +372 -0
- package/skills/bigquery-patterns/schema-design.md +78 -0
- package/skills/cloudstream-project-template/SKILL.md +20 -0
- package/skills/cloudstream-project-template/structure.md +65 -0
- package/skills/coding-standards/SKILL.md +524 -0
- package/skills/coding-standards/deluge-standards.md +83 -0
- package/skills/compliance-patterns/SKILL.md +28 -0
- package/skills/compliance-patterns/hipaa/audit-requirements.md +251 -0
- package/skills/compliance-patterns/hipaa/baa-process.md +298 -0
- package/skills/compliance-patterns/hipaa/data-archival-strategy.md +387 -0
- package/skills/compliance-patterns/hipaa/phi-handling.md +52 -0
- package/skills/compliance-patterns/pci-dss/saq-a-requirements.md +307 -0
- package/skills/compliance-patterns/pci-dss/tokenization-patterns.md +382 -0
- package/skills/compliance-patterns/pci-dss/zoho-checkout-patterns.md +56 -0
- package/skills/compliance-patterns/soc2/access-controls.md +344 -0
- package/skills/compliance-patterns/soc2/audit-logging.md +458 -0
- package/skills/compliance-patterns/soc2/change-management.md +403 -0
- package/skills/compliance-patterns/soc2/deluge-execution-logging.md +407 -0
- package/skills/consultancy-workflows/SKILL.md +19 -0
- package/skills/consultancy-workflows/client-isolation.md +21 -0
- package/skills/consultancy-workflows/documentation-automation.md +454 -0
- package/skills/consultancy-workflows/handoff-procedures.md +257 -0
- package/skills/consultancy-workflows/knowledge-capture.md +513 -0
- package/skills/consultancy-workflows/time-tracking.md +26 -0
- package/skills/continuous-learning/SKILL.md +84 -0
- package/skills/continuous-learning/config.json +18 -0
- package/skills/continuous-learning/evaluate-session.sh +60 -0
- package/skills/continuous-learning-v2/SKILL.md +126 -0
- package/skills/continuous-learning-v2/config.json +61 -0
- package/skills/frontend-patterns/SKILL.md +635 -0
- package/skills/frontend-patterns/zoho-widget-patterns.md +103 -0
- package/skills/gcp-data-engineering/SKILL.md +36 -0
- package/skills/gcp-data-engineering/bigquery/performance-optimization.md +337 -0
- package/skills/gcp-data-engineering/dataflow/error-handling.md +496 -0
- package/skills/gcp-data-engineering/dataflow/pipeline-patterns.md +444 -0
- package/skills/gcp-data-engineering/dbt/model-organization.md +63 -0
- package/skills/gcp-data-engineering/dbt/testing-patterns.md +503 -0
- package/skills/gcp-data-engineering/medallion-architecture/bronze-layer.md +60 -0
- package/skills/gcp-data-engineering/medallion-architecture/gold-layer.md +311 -0
- package/skills/gcp-data-engineering/medallion-architecture/layer-transitions.md +517 -0
- package/skills/gcp-data-engineering/medallion-architecture/silver-layer.md +305 -0
- package/skills/gcp-data-engineering/zoho-to-gcp/data-extraction.md +543 -0
- package/skills/gcp-data-engineering/zoho-to-gcp/real-time-vs-batch.md +337 -0
- package/skills/security-review/SKILL.md +498 -0
- package/skills/security-review/compliance-checklist.md +53 -0
- package/skills/strategic-compact/SKILL.md +67 -0
- package/skills/tdd-workflow/SKILL.md +413 -0
- package/skills/tdd-workflow/zoho-testing.md +124 -0
- package/skills/tutorial/SKILL.md +249 -0
- package/skills/tutorial/docs/ACCESSIBILITY.md +169 -0
- package/skills/tutorial/lessons/00-philosophy-and-workflow.md +198 -0
- package/skills/tutorial/lessons/01-basics.md +81 -0
- package/skills/tutorial/lessons/02-training.md +86 -0
- package/skills/tutorial/lessons/03-commands.md +109 -0
- package/skills/tutorial/lessons/04-workflows.md +115 -0
- package/skills/tutorial/lessons/05-compliance.md +116 -0
- package/skills/tutorial/lessons/06-zoho.md +121 -0
- package/skills/tutorial/lessons/07-hooks-system.md +277 -0
- package/skills/tutorial/lessons/08-mcp-servers.md +316 -0
- package/skills/tutorial/lessons/09-client-management.md +215 -0
- package/skills/tutorial/lessons/10-testing-e2e.md +260 -0
- package/skills/tutorial/lessons/11-skills-deep-dive.md +272 -0
- package/skills/tutorial/lessons/12-rules-system.md +326 -0
- package/skills/tutorial/lessons/13-golden-standard-graduation.md +213 -0
- package/skills/tutorial/lessons/14-fork-setup-and-sync.md +312 -0
- package/skills/tutorial/lessons/15-living-examples-system.md +221 -0
- package/skills/tutorial/tracks/accelerated/README.md +134 -0
- package/skills/tutorial/tracks/accelerated/assessment/checkpoint-1.md +161 -0
- package/skills/tutorial/tracks/accelerated/assessment/checkpoint-2.md +175 -0
- package/skills/tutorial/tracks/accelerated/day-1-core-concepts.md +234 -0
- package/skills/tutorial/tracks/accelerated/day-2-essential-commands.md +270 -0
- package/skills/tutorial/tracks/accelerated/day-3-workflow-mastery.md +305 -0
- package/skills/tutorial/tracks/accelerated/day-4-compliance-zoho.md +304 -0
- package/skills/tutorial/tracks/accelerated/day-5-hooks-skills.md +344 -0
- package/skills/tutorial/tracks/accelerated/day-6-client-testing.md +386 -0
- package/skills/tutorial/tracks/accelerated/day-7-graduation.md +369 -0
- package/skills/zoho-patterns/CHANGELOG.md +108 -0
- package/skills/zoho-patterns/SKILL.md +446 -0
- package/skills/zoho-patterns/analytics/dashboard-patterns.md +352 -0
- package/skills/zoho-patterns/analytics/zoho-to-bigquery-pipeline.md +427 -0
- package/skills/zoho-patterns/catalyst/appsail-deployment.md +349 -0
- package/skills/zoho-patterns/catalyst/context-close-patterns.md +354 -0
- package/skills/zoho-patterns/catalyst/cron-batch-processing.md +374 -0
- package/skills/zoho-patterns/catalyst/function-patterns.md +439 -0
- package/skills/zoho-patterns/creator/form-design.md +304 -0
- package/skills/zoho-patterns/creator/publish-api-patterns.md +313 -0
- package/skills/zoho-patterns/creator/widget-integration.md +306 -0
- package/skills/zoho-patterns/creator/workflow-automation.md +253 -0
- package/skills/zoho-patterns/deluge/api-patterns.md +468 -0
- package/skills/zoho-patterns/deluge/batch-processing.md +403 -0
- package/skills/zoho-patterns/deluge/cross-app-integration.md +356 -0
- package/skills/zoho-patterns/deluge/error-handling.md +423 -0
- package/skills/zoho-patterns/deluge/syntax-reference.md +65 -0
- package/skills/zoho-patterns/integration/cors-proxy-architecture.md +426 -0
- package/skills/zoho-patterns/integration/crm-books-native-sync.md +277 -0
- package/skills/zoho-patterns/integration/oauth-token-management.md +461 -0
- package/skills/zoho-patterns/integration/zoho-flow-patterns.md +334 -0
|
@@ -0,0 +1,468 @@
|
|
|
1
|
+
# Deluge API Patterns
|
|
2
|
+
|
|
3
|
+
## invokeUrl Fundamentals
|
|
4
|
+
|
|
5
|
+
> **CRITICAL:** invokeUrl has a hard 40-second timeout. If the external API doesn't respond within 40 seconds, the call FAILS with no retry. ALWAYS wrap in try-catch.
|
|
6
|
+
|
|
7
|
+
> **NEVER** hardcode OAuth tokens in Deluge scripts. Always use Connections.
|
|
8
|
+
|
|
9
|
+
## Basic invokeUrl with Try-Catch
|
|
10
|
+
|
|
11
|
+
```deluge
|
|
12
|
+
// ALWAYS use this pattern for API calls
|
|
13
|
+
try
|
|
14
|
+
{
|
|
15
|
+
response = invokeUrl [
|
|
16
|
+
url: "https://api.example.com/v1/data"
|
|
17
|
+
type: GET
|
|
18
|
+
headers: {"Content-Type": "application/json", "Accept": "application/json"}
|
|
19
|
+
connection: "my-api-connection"
|
|
20
|
+
];
|
|
21
|
+
|
|
22
|
+
// Check response status
|
|
23
|
+
responseCode = response.getStatusCode();
|
|
24
|
+
if (responseCode == 200)
|
|
25
|
+
{
|
|
26
|
+
data = response.toJSON();
|
|
27
|
+
info "API call successful: " + data.size() + " records";
|
|
28
|
+
}
|
|
29
|
+
else
|
|
30
|
+
{
|
|
31
|
+
info "API returned error: " + responseCode + " - " + response.toString();
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
catch (e)
|
|
35
|
+
{
|
|
36
|
+
info "API call failed: " + e.toString();
|
|
37
|
+
// Log error for debugging
|
|
38
|
+
errorLog = Map();
|
|
39
|
+
errorLog.put("Endpoint", "https://api.example.com/v1/data");
|
|
40
|
+
errorLog.put("Error", e.toString());
|
|
41
|
+
errorLog.put("Timestamp", zoho.currenttime);
|
|
42
|
+
zoho.creator.createRecord("admin-app", "API_Error_Log", errorLog);
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Connection Usage
|
|
47
|
+
|
|
48
|
+
Connections manage OAuth token refresh automatically:
|
|
49
|
+
|
|
50
|
+
```deluge
|
|
51
|
+
// Using a Zoho CRM connection
|
|
52
|
+
response = invokeUrl [
|
|
53
|
+
url: "https://www.zohoapis.com/crm/v5/Deals"
|
|
54
|
+
type: GET
|
|
55
|
+
connection: "zoho-crm-connection"
|
|
56
|
+
];
|
|
57
|
+
|
|
58
|
+
// Using an external API connection (e.g., Stripe)
|
|
59
|
+
response = invokeUrl [
|
|
60
|
+
url: "https://api.stripe.com/v1/customers"
|
|
61
|
+
type: GET
|
|
62
|
+
connection: "stripe-connection"
|
|
63
|
+
];
|
|
64
|
+
|
|
65
|
+
// Connection handles token refresh, you just use it
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Creating Connections
|
|
69
|
+
|
|
70
|
+
1. Go to Setup > Developer > Connections
|
|
71
|
+
2. Pick service (Zoho, custom OAuth, or API key)
|
|
72
|
+
3. Define scopes (minimum necessary)
|
|
73
|
+
4. Authorize and test
|
|
74
|
+
5. Use connection name in `invokeUrl`
|
|
75
|
+
|
|
76
|
+
> **WARNING:** If a Connection's OAuth grant expires (user revokes, org changes), all scripts using it will fail silently. Monitor for auth errors.
|
|
77
|
+
|
|
78
|
+
## Response Handling
|
|
79
|
+
|
|
80
|
+
### JSON Response Parsing
|
|
81
|
+
|
|
82
|
+
```deluge
|
|
83
|
+
try
|
|
84
|
+
{
|
|
85
|
+
response = invokeUrl [
|
|
86
|
+
url: "https://api.example.com/users"
|
|
87
|
+
type: GET
|
|
88
|
+
connection: "example-connection"
|
|
89
|
+
];
|
|
90
|
+
|
|
91
|
+
// Parse JSON response
|
|
92
|
+
jsonData = response.toJSON();
|
|
93
|
+
|
|
94
|
+
// Handle different response structures
|
|
95
|
+
if (jsonData.containsKey("data"))
|
|
96
|
+
{
|
|
97
|
+
records = jsonData.get("data");
|
|
98
|
+
for each record in records
|
|
99
|
+
{
|
|
100
|
+
name = record.get("name");
|
|
101
|
+
email = record.get("email");
|
|
102
|
+
info name + ": " + email;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
else if (jsonData.containsKey("error"))
|
|
106
|
+
{
|
|
107
|
+
info "API Error: " + jsonData.get("error").get("message");
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
catch (e)
|
|
111
|
+
{
|
|
112
|
+
info "Parse or network error: " + e.toString();
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Status Code Checking
|
|
117
|
+
|
|
118
|
+
```deluge
|
|
119
|
+
try
|
|
120
|
+
{
|
|
121
|
+
response = invokeUrl [
|
|
122
|
+
url: "https://api.example.com/resource"
|
|
123
|
+
type: GET
|
|
124
|
+
connection: "example-connection"
|
|
125
|
+
];
|
|
126
|
+
|
|
127
|
+
statusCode = response.getStatusCode();
|
|
128
|
+
|
|
129
|
+
if (statusCode == 200)
|
|
130
|
+
{
|
|
131
|
+
// Success
|
|
132
|
+
return response.toJSON();
|
|
133
|
+
}
|
|
134
|
+
else if (statusCode == 401)
|
|
135
|
+
{
|
|
136
|
+
// Auth failed - connection may need reauthorization
|
|
137
|
+
info "AUTH FAILED - Reauthorize connection";
|
|
138
|
+
sendmail [
|
|
139
|
+
from: zoho.adminuserid
|
|
140
|
+
to: "admin@company.com"
|
|
141
|
+
subject: "[URGENT] API Connection Auth Failed"
|
|
142
|
+
message: "Connection needs reauthorization. Endpoint: /resource"
|
|
143
|
+
];
|
|
144
|
+
}
|
|
145
|
+
else if (statusCode == 429)
|
|
146
|
+
{
|
|
147
|
+
// Rate limited
|
|
148
|
+
info "Rate limited - schedule retry";
|
|
149
|
+
}
|
|
150
|
+
else if (statusCode >= 500)
|
|
151
|
+
{
|
|
152
|
+
// Server error - transient, may retry
|
|
153
|
+
info "Server error: " + statusCode;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
catch (e)
|
|
157
|
+
{
|
|
158
|
+
// Network timeout or connection refused
|
|
159
|
+
info "Network error: " + e.toString();
|
|
160
|
+
}
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## Retry Pattern
|
|
164
|
+
|
|
165
|
+
```deluge
|
|
166
|
+
// Retry wrapper function
|
|
167
|
+
// Arguments: url (TEXT), method (TEXT), body (TEXT), connectionName (TEXT)
|
|
168
|
+
maxRetries = 3;
|
|
169
|
+
lastError = "";
|
|
170
|
+
result = null;
|
|
171
|
+
|
|
172
|
+
for each attempt in {1, 2, 3}
|
|
173
|
+
{
|
|
174
|
+
try
|
|
175
|
+
{
|
|
176
|
+
if (method == "GET")
|
|
177
|
+
{
|
|
178
|
+
response = invokeUrl [
|
|
179
|
+
url: url
|
|
180
|
+
type: GET
|
|
181
|
+
connection: connectionName
|
|
182
|
+
];
|
|
183
|
+
}
|
|
184
|
+
else if (method == "POST")
|
|
185
|
+
{
|
|
186
|
+
response = invokeUrl [
|
|
187
|
+
url: url
|
|
188
|
+
type: POST
|
|
189
|
+
headers: {"Content-Type": "application/json"}
|
|
190
|
+
body: body
|
|
191
|
+
connection: connectionName
|
|
192
|
+
];
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
statusCode = response.getStatusCode();
|
|
196
|
+
|
|
197
|
+
if (statusCode >= 200 && statusCode < 300)
|
|
198
|
+
{
|
|
199
|
+
result = response.toJSON();
|
|
200
|
+
break; // Success, exit retry loop
|
|
201
|
+
}
|
|
202
|
+
else if (statusCode == 429 || statusCode >= 500)
|
|
203
|
+
{
|
|
204
|
+
// Retryable error - will retry on next loop iteration
|
|
205
|
+
lastError = "HTTP " + statusCode;
|
|
206
|
+
// NOTE: Deluge has no sleep/delay mechanism.
|
|
207
|
+
// Retries happen immediately. For true backoff,
|
|
208
|
+
// use a Scheduled Function with retry state in a form record.
|
|
209
|
+
}
|
|
210
|
+
else
|
|
211
|
+
{
|
|
212
|
+
// Non-retryable error (4xx except 429)
|
|
213
|
+
lastError = "HTTP " + statusCode + ": " + response.toString();
|
|
214
|
+
break;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
catch (e)
|
|
218
|
+
{
|
|
219
|
+
lastError = e.toString();
|
|
220
|
+
// Network error - worth retrying
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
if (result == null)
|
|
225
|
+
{
|
|
226
|
+
info "All retries failed. Last error: " + lastError;
|
|
227
|
+
}
|
|
228
|
+
return result;
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
## Pagination of API Responses
|
|
232
|
+
|
|
233
|
+
### Offset-Based Pagination
|
|
234
|
+
|
|
235
|
+
```deluge
|
|
236
|
+
// Fetch all pages from a paginated API
|
|
237
|
+
allRecords = List();
|
|
238
|
+
page = 1;
|
|
239
|
+
pageSize = 200; // Max per request
|
|
240
|
+
hasMore = true;
|
|
241
|
+
|
|
242
|
+
while (hasMore)
|
|
243
|
+
{
|
|
244
|
+
try
|
|
245
|
+
{
|
|
246
|
+
url = "https://api.example.com/records?page=" + page + "&limit=" + pageSize;
|
|
247
|
+
response = invokeUrl [
|
|
248
|
+
url: url
|
|
249
|
+
type: GET
|
|
250
|
+
connection: "example-connection"
|
|
251
|
+
];
|
|
252
|
+
|
|
253
|
+
data = response.toJSON();
|
|
254
|
+
records = data.get("records");
|
|
255
|
+
|
|
256
|
+
if (records.size() > 0)
|
|
257
|
+
{
|
|
258
|
+
for each record in records
|
|
259
|
+
{
|
|
260
|
+
allRecords.add(record);
|
|
261
|
+
}
|
|
262
|
+
page = page + 1;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
if (records.size() < pageSize)
|
|
266
|
+
{
|
|
267
|
+
hasMore = false;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// Safety: prevent infinite loop and statement limit
|
|
271
|
+
if (page > 50)
|
|
272
|
+
{
|
|
273
|
+
info "Safety limit: stopped at page 50";
|
|
274
|
+
hasMore = false;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
catch (e)
|
|
278
|
+
{
|
|
279
|
+
info "Pagination failed at page " + page + ": " + e.toString();
|
|
280
|
+
hasMore = false;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
info "Total records fetched: " + allRecords.size();
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
### Cursor-Based Pagination
|
|
288
|
+
|
|
289
|
+
```deluge
|
|
290
|
+
allRecords = List();
|
|
291
|
+
cursor = "";
|
|
292
|
+
hasMore = true;
|
|
293
|
+
|
|
294
|
+
while (hasMore)
|
|
295
|
+
{
|
|
296
|
+
try
|
|
297
|
+
{
|
|
298
|
+
url = "https://api.example.com/records";
|
|
299
|
+
if (cursor != "")
|
|
300
|
+
{
|
|
301
|
+
url = url + "?cursor=" + cursor;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
response = invokeUrl [
|
|
305
|
+
url: url
|
|
306
|
+
type: GET
|
|
307
|
+
connection: "example-connection"
|
|
308
|
+
];
|
|
309
|
+
|
|
310
|
+
data = response.toJSON();
|
|
311
|
+
allRecords.addAll(data.get("data"));
|
|
312
|
+
|
|
313
|
+
if (data.containsKey("next_cursor") && data.get("next_cursor") != null)
|
|
314
|
+
{
|
|
315
|
+
cursor = data.get("next_cursor");
|
|
316
|
+
}
|
|
317
|
+
else
|
|
318
|
+
{
|
|
319
|
+
hasMore = false;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
catch (e)
|
|
323
|
+
{
|
|
324
|
+
info "Cursor pagination error: " + e.toString();
|
|
325
|
+
hasMore = false;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
## POST with JSON Body
|
|
331
|
+
|
|
332
|
+
```deluge
|
|
333
|
+
try
|
|
334
|
+
{
|
|
335
|
+
payload = Map();
|
|
336
|
+
payload.put("name", input.Customer_Name);
|
|
337
|
+
payload.put("email", input.Email);
|
|
338
|
+
payload.put("amount", input.Total_Amount);
|
|
339
|
+
payload.put("line_items", List());
|
|
340
|
+
|
|
341
|
+
for each item in input.Line_Items
|
|
342
|
+
{
|
|
343
|
+
lineItem = Map();
|
|
344
|
+
lineItem.put("description", item.Description);
|
|
345
|
+
lineItem.put("quantity", item.Quantity);
|
|
346
|
+
lineItem.put("unit_price", item.Unit_Price);
|
|
347
|
+
payload.get("line_items").add(lineItem);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
response = invokeUrl [
|
|
351
|
+
url: "https://api.example.com/invoices"
|
|
352
|
+
type: POST
|
|
353
|
+
headers: {"Content-Type": "application/json"}
|
|
354
|
+
body: payload.toString()
|
|
355
|
+
connection: "example-connection"
|
|
356
|
+
];
|
|
357
|
+
|
|
358
|
+
if (response.getStatusCode() == 201)
|
|
359
|
+
{
|
|
360
|
+
externalId = response.toJSON().get("id");
|
|
361
|
+
input.External_Invoice_ID = externalId;
|
|
362
|
+
info "Invoice created externally: " + externalId;
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
catch (e)
|
|
366
|
+
{
|
|
367
|
+
info "Invoice creation failed: " + e.toString();
|
|
368
|
+
}
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
## Multipart Form Data / File Upload
|
|
372
|
+
|
|
373
|
+
```deluge
|
|
374
|
+
// Upload a file from Creator to an external API
|
|
375
|
+
fileField = input.Attachment; // File field from form
|
|
376
|
+
|
|
377
|
+
try
|
|
378
|
+
{
|
|
379
|
+
response = invokeUrl [
|
|
380
|
+
url: "https://api.example.com/upload"
|
|
381
|
+
type: POST
|
|
382
|
+
files: fileField
|
|
383
|
+
connection: "example-connection"
|
|
384
|
+
];
|
|
385
|
+
|
|
386
|
+
if (response.getStatusCode() == 200)
|
|
387
|
+
{
|
|
388
|
+
uploadResult = response.toJSON();
|
|
389
|
+
input.External_File_URL = uploadResult.get("url");
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
catch (e)
|
|
393
|
+
{
|
|
394
|
+
info "File upload failed: " + e.toString();
|
|
395
|
+
}
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
### Downloading Files
|
|
399
|
+
|
|
400
|
+
```deluge
|
|
401
|
+
try
|
|
402
|
+
{
|
|
403
|
+
fileResponse = invokeUrl [
|
|
404
|
+
url: "https://api.example.com/files/12345/download"
|
|
405
|
+
type: GET
|
|
406
|
+
response_format: FILE
|
|
407
|
+
connection: "example-connection"
|
|
408
|
+
];
|
|
409
|
+
|
|
410
|
+
// Attach to a form field
|
|
411
|
+
input.Downloaded_File = fileResponse;
|
|
412
|
+
}
|
|
413
|
+
catch (e)
|
|
414
|
+
{
|
|
415
|
+
info "File download failed: " + e.toString();
|
|
416
|
+
}
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
## Batch API Calls
|
|
420
|
+
|
|
421
|
+
> **WARNING:** Each invokeUrl counts toward the 5000-statement limit AND each has a 40-second timeout risk. Batch carefully.
|
|
422
|
+
|
|
423
|
+
```deluge
|
|
424
|
+
// Batch pattern: collect, then single API call
|
|
425
|
+
batchPayload = List();
|
|
426
|
+
|
|
427
|
+
records = zoho.creator.getRecords("app", "Pending_Sync", "", 1, 50);
|
|
428
|
+
for each record in records
|
|
429
|
+
{
|
|
430
|
+
item = Map();
|
|
431
|
+
item.put("id", record.get("External_ID"));
|
|
432
|
+
item.put("status", record.get("Status"));
|
|
433
|
+
batchPayload.add(item);
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
// Single batch API call instead of 50 individual calls
|
|
437
|
+
if (batchPayload.size() > 0)
|
|
438
|
+
{
|
|
439
|
+
try
|
|
440
|
+
{
|
|
441
|
+
response = invokeUrl [
|
|
442
|
+
url: "https://api.example.com/batch-update"
|
|
443
|
+
type: POST
|
|
444
|
+
headers: {"Content-Type": "application/json"}
|
|
445
|
+
body: {"records": batchPayload}.toString()
|
|
446
|
+
connection: "example-connection"
|
|
447
|
+
];
|
|
448
|
+
|
|
449
|
+
info "Batch update: " + response.getStatusCode();
|
|
450
|
+
}
|
|
451
|
+
catch (e)
|
|
452
|
+
{
|
|
453
|
+
info "Batch call failed: " + e.toString();
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
## Common Pitfalls
|
|
459
|
+
|
|
460
|
+
| Pitfall | Consequence | Prevention |
|
|
461
|
+
|---------|-------------|-----------|
|
|
462
|
+
| No try-catch on invokeUrl | Script crashes, no error info | ALWAYS wrap in try-catch |
|
|
463
|
+
| Hardcoded tokens | Security risk, token expiry breaks | Use Connections |
|
|
464
|
+
| No status code check | Silent failures | Always check getStatusCode() |
|
|
465
|
+
| Unlimited pagination loop | 5000 statement limit hit | Add page count safety limit |
|
|
466
|
+
| Large payload in single call | 40-second timeout | Chunk data, batch endpoints |
|
|
467
|
+
| Ignoring rate limits (429) | Subsequent calls blocked | Implement backoff/retry |
|
|
468
|
+
| Not logging errors | Can't debug production issues | Log to custom form/report |
|