@ketrics/ketrics-cli 0.2.2 → 0.3.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 +623 -607
- package/dist/src/cli.d.ts.map +1 -1
- package/dist/src/cli.js +2 -2
- package/dist/src/cli.js.map +1 -1
- package/dist/src/version.d.ts +2 -0
- package/dist/src/version.d.ts.map +1 -0
- package/dist/src/version.js +6 -0
- package/dist/src/version.js.map +1 -0
- package/package.json +3 -2
- package/templates/HelloWorld/README.md +83 -106
- package/templates/HelloWorld/backend/package.json +1 -1
- package/templates/HelloWorld/backend/src/database.ts +108 -0
- package/templates/HelloWorld/backend/src/excel.ts +118 -0
- package/templates/HelloWorld/backend/src/http.ts +22 -0
- package/templates/HelloWorld/backend/src/index.ts +105 -29
- package/templates/HelloWorld/backend/src/jobs.ts +47 -0
- package/templates/HelloWorld/backend/src/messages.ts +59 -0
- package/templates/HelloWorld/backend/src/pdf.ts +212 -0
- package/templates/HelloWorld/backend/src/secrets.ts +21 -14
- package/templates/HelloWorld/backend/src/volumes.ts +107 -0
- package/templates/HelloWorld/frontend/package.json +1 -3
- package/templates/HelloWorld/frontend/src/App.css +62 -37
- package/templates/HelloWorld/frontend/src/App.tsx +131 -111
- package/templates/HelloWorld/frontend/src/services/index.ts +11 -11
- package/templates/HelloWorld/tests/test.createInvoicePdf.json +18 -0
- package/templates/HelloWorld/tests/{test.getSecretWithoutGrant.json → test.createSimplePdf.json} +4 -2
- package/templates/HelloWorld/tests/test.createSpreadsheet.json +11 -0
- package/templates/HelloWorld/tests/test.echo.json +2 -2
- package/templates/HelloWorld/tests/test.fetchExternalApi.json +13 -0
- package/templates/HelloWorld/tests/test.getSecret.json +5 -1
- package/templates/HelloWorld/tests/test.info.json +3 -1
- package/templates/HelloWorld/tests/test.listFiles.json +13 -0
- package/templates/HelloWorld/tests/{test.echo2.json → test.queryUsers.json} +3 -3
- package/templates/HelloWorld/tests/{test.greet.json → test.readFile.json} +2 -2
- package/templates/HelloWorld/tests/{test.testWriteFileWithoutVolumeGrant.json → test.saveFile.json} +4 -2
- package/templates/HelloWorld/tests/test.sendNotification.json +14 -0
- package/templates/HelloWorld/backend/src/volume.ts +0 -55
|
@@ -11,23 +11,24 @@ body {
|
|
|
11
11
|
min-height: 100vh;
|
|
12
12
|
display: flex;
|
|
13
13
|
justify-content: center;
|
|
14
|
-
align-items:
|
|
14
|
+
align-items: flex-start;
|
|
15
15
|
padding: 20px;
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
.app {
|
|
19
19
|
background: white;
|
|
20
20
|
border-radius: 12px;
|
|
21
|
-
padding:
|
|
22
|
-
max-width:
|
|
21
|
+
padding: 32px;
|
|
22
|
+
max-width: 700px;
|
|
23
23
|
width: 100%;
|
|
24
24
|
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
|
|
25
|
+
margin-top: 20px;
|
|
25
26
|
}
|
|
26
27
|
|
|
27
28
|
h1 {
|
|
28
29
|
color: #333;
|
|
29
|
-
margin-bottom:
|
|
30
|
-
font-size:
|
|
30
|
+
margin-bottom: 4px;
|
|
31
|
+
font-size: 26px;
|
|
31
32
|
}
|
|
32
33
|
|
|
33
34
|
.subtitle {
|
|
@@ -36,86 +37,110 @@ h1 {
|
|
|
36
37
|
font-size: 14px;
|
|
37
38
|
}
|
|
38
39
|
|
|
39
|
-
.
|
|
40
|
+
.sections {
|
|
40
41
|
display: flex;
|
|
41
|
-
|
|
42
|
-
|
|
42
|
+
flex-direction: column;
|
|
43
|
+
gap: 20px;
|
|
43
44
|
}
|
|
44
45
|
|
|
45
|
-
.
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
transition: border-color 0.2s;
|
|
46
|
+
.section h2 {
|
|
47
|
+
font-size: 14px;
|
|
48
|
+
color: #555;
|
|
49
|
+
margin-bottom: 8px;
|
|
50
|
+
text-transform: uppercase;
|
|
51
|
+
letter-spacing: 0.5px;
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
-
.
|
|
55
|
-
|
|
56
|
-
|
|
54
|
+
.button-group {
|
|
55
|
+
display: flex;
|
|
56
|
+
flex-wrap: wrap;
|
|
57
|
+
gap: 8px;
|
|
57
58
|
}
|
|
58
59
|
|
|
59
60
|
.button {
|
|
60
|
-
padding:
|
|
61
|
+
padding: 8px 16px;
|
|
61
62
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
62
63
|
color: white;
|
|
63
64
|
border: none;
|
|
64
|
-
border-radius:
|
|
65
|
-
font-size:
|
|
66
|
-
font-weight:
|
|
65
|
+
border-radius: 6px;
|
|
66
|
+
font-size: 13px;
|
|
67
|
+
font-weight: 500;
|
|
67
68
|
cursor: pointer;
|
|
68
|
-
transition: transform 0.
|
|
69
|
+
transition: transform 0.15s, box-shadow 0.15s, opacity 0.15s;
|
|
69
70
|
}
|
|
70
71
|
|
|
71
|
-
.button:hover {
|
|
72
|
-
transform: translateY(-
|
|
72
|
+
.button:hover:not(:disabled) {
|
|
73
|
+
transform: translateY(-1px);
|
|
73
74
|
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);
|
|
74
75
|
}
|
|
75
76
|
|
|
76
|
-
.button:active {
|
|
77
|
+
.button:active:not(:disabled) {
|
|
77
78
|
transform: translateY(0);
|
|
78
79
|
}
|
|
79
80
|
|
|
81
|
+
.button:disabled {
|
|
82
|
+
opacity: 0.6;
|
|
83
|
+
cursor: not-allowed;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
.loading {
|
|
87
|
+
color: #667eea;
|
|
88
|
+
font-weight: 500;
|
|
89
|
+
padding: 12px;
|
|
90
|
+
margin-top: 16px;
|
|
91
|
+
}
|
|
92
|
+
|
|
80
93
|
.error {
|
|
81
94
|
color: #e53e3e;
|
|
82
95
|
background: #fed7d7;
|
|
83
96
|
padding: 12px;
|
|
84
97
|
border-radius: 8px;
|
|
85
|
-
margin-
|
|
98
|
+
margin-top: 16px;
|
|
99
|
+
font-size: 14px;
|
|
86
100
|
}
|
|
87
101
|
|
|
88
102
|
.result {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
padding: 12px;
|
|
103
|
+
background: #f0fdf4;
|
|
104
|
+
border: 1px solid #bbf7d0;
|
|
92
105
|
border-radius: 8px;
|
|
93
|
-
margin-
|
|
106
|
+
margin-top: 16px;
|
|
107
|
+
padding: 12px;
|
|
108
|
+
overflow-x: auto;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
.result pre {
|
|
112
|
+
font-size: 12px;
|
|
113
|
+
color: #166534;
|
|
114
|
+
white-space: pre-wrap;
|
|
115
|
+
word-break: break-word;
|
|
116
|
+
margin: 0;
|
|
94
117
|
}
|
|
95
118
|
|
|
96
119
|
.info {
|
|
97
|
-
margin-top:
|
|
98
|
-
padding-top:
|
|
120
|
+
margin-top: 24px;
|
|
121
|
+
padding-top: 20px;
|
|
99
122
|
border-top: 1px solid #e0e0e0;
|
|
100
123
|
}
|
|
101
124
|
|
|
102
125
|
.info h2 {
|
|
103
|
-
font-size:
|
|
126
|
+
font-size: 16px;
|
|
104
127
|
color: #333;
|
|
105
|
-
margin-bottom:
|
|
128
|
+
margin-bottom: 8px;
|
|
106
129
|
}
|
|
107
130
|
|
|
108
131
|
.info p {
|
|
109
132
|
color: #666;
|
|
110
|
-
margin-bottom:
|
|
133
|
+
margin-bottom: 8px;
|
|
111
134
|
line-height: 1.5;
|
|
135
|
+
font-size: 14px;
|
|
112
136
|
}
|
|
113
137
|
|
|
114
138
|
.info ul {
|
|
115
139
|
color: #666;
|
|
116
140
|
padding-left: 20px;
|
|
141
|
+
font-size: 13px;
|
|
117
142
|
}
|
|
118
143
|
|
|
119
144
|
.info li {
|
|
120
|
-
margin-bottom:
|
|
145
|
+
margin-bottom: 4px;
|
|
121
146
|
}
|
|
@@ -1,141 +1,161 @@
|
|
|
1
1
|
import { useState } from "react";
|
|
2
|
-
import { z } from "zod";
|
|
3
2
|
import { apiClient } from "./services";
|
|
4
3
|
|
|
5
|
-
const inputSchema = z.string().min(1, "Input is required").max(100, "Max 100 characters");
|
|
6
|
-
|
|
7
4
|
function App() {
|
|
8
|
-
const [input, setInput] = useState("");
|
|
9
5
|
const [result, setResult] = useState<string | null>(null);
|
|
10
6
|
const [error, setError] = useState<string | null>(null);
|
|
7
|
+
const [loading, setLoading] = useState(false);
|
|
11
8
|
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
if (!validation.success) {
|
|
15
|
-
setError(validation.error.errors[0].message);
|
|
16
|
-
setResult(null);
|
|
17
|
-
return;
|
|
18
|
-
}
|
|
9
|
+
const runFunction = async (fnName: string, payload?: unknown) => {
|
|
10
|
+
setLoading(true);
|
|
19
11
|
setError(null);
|
|
20
|
-
setResult(
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
const handleRunEcho = async () => {
|
|
24
|
-
try {
|
|
25
|
-
const data = await apiClient.run("echo");
|
|
26
|
-
console.log("API Data:", data);
|
|
27
|
-
} catch (err) {
|
|
28
|
-
console.error("API Error:", err);
|
|
29
|
-
}
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
const handleRunSaveFile = async () => {
|
|
33
|
-
try {
|
|
34
|
-
const data = await apiClient.run("saveFile");
|
|
35
|
-
console.log("API Data:", data);
|
|
36
|
-
} catch (err) {
|
|
37
|
-
console.error("API Error:", err);
|
|
38
|
-
}
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
const handleRunReadFile = async () => {
|
|
42
|
-
try {
|
|
43
|
-
const data = await apiClient.run("readFile");
|
|
44
|
-
console.log("API Data:", data);
|
|
45
|
-
} catch (err) {
|
|
46
|
-
console.error("API Error:", err);
|
|
47
|
-
}
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
const handleRunGenerateDownloadUrl = async () => {
|
|
51
|
-
try {
|
|
52
|
-
const data = await apiClient.run("generateDownloadUrl");
|
|
53
|
-
// Trigger download of the file in the url data.result.url
|
|
54
|
-
const downloadUrl = data.result.url;
|
|
55
|
-
const link = document.createElement("a");
|
|
56
|
-
link.href = downloadUrl;
|
|
57
|
-
link.target = "_blank";
|
|
58
|
-
link.download = "data.json";
|
|
59
|
-
document.body.appendChild(link);
|
|
60
|
-
link.click();
|
|
61
|
-
document.body.removeChild(link);
|
|
62
|
-
console.log("downloadUrl:", downloadUrl);
|
|
63
|
-
} catch (err) {
|
|
64
|
-
console.error("API Error:", err);
|
|
65
|
-
}
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
const handleRunTestWriteFileWithoutVolumeGrant = async () => {
|
|
12
|
+
setResult(null);
|
|
69
13
|
try {
|
|
70
|
-
const data = await apiClient.run(
|
|
71
|
-
|
|
14
|
+
const data = await apiClient.run(fnName, payload);
|
|
15
|
+
setResult(JSON.stringify(data, null, 2));
|
|
72
16
|
} catch (err) {
|
|
73
|
-
|
|
17
|
+
setError(err instanceof Error ? err.message : "An error occurred");
|
|
18
|
+
} finally {
|
|
19
|
+
setLoading(false);
|
|
74
20
|
}
|
|
75
21
|
};
|
|
76
22
|
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
} catch (err) {
|
|
82
|
-
console.error("Secret Error:", err);
|
|
83
|
-
}
|
|
84
|
-
};
|
|
85
|
-
|
|
86
|
-
const handleGetSecretWithoutGrant = async () => {
|
|
23
|
+
const handleDownload = async (fnName: string, payload?: unknown) => {
|
|
24
|
+
setLoading(true);
|
|
25
|
+
setError(null);
|
|
26
|
+
setResult(null);
|
|
87
27
|
try {
|
|
88
|
-
const data = await apiClient.run(
|
|
89
|
-
|
|
28
|
+
const data = await apiClient.run(fnName, payload);
|
|
29
|
+
const downloadUrl = data?.result?.downloadUrl;
|
|
30
|
+
if (downloadUrl) {
|
|
31
|
+
const link = document.createElement("a");
|
|
32
|
+
link.href = downloadUrl;
|
|
33
|
+
link.target = "_blank";
|
|
34
|
+
document.body.appendChild(link);
|
|
35
|
+
link.click();
|
|
36
|
+
document.body.removeChild(link);
|
|
37
|
+
setResult(`Download started. URL: ${downloadUrl}`);
|
|
38
|
+
} else {
|
|
39
|
+
setResult(JSON.stringify(data, null, 2));
|
|
40
|
+
}
|
|
90
41
|
} catch (err) {
|
|
91
|
-
|
|
42
|
+
setError(err instanceof Error ? err.message : "An error occurred");
|
|
43
|
+
} finally {
|
|
44
|
+
setLoading(false);
|
|
92
45
|
}
|
|
93
46
|
};
|
|
94
47
|
|
|
95
48
|
return (
|
|
96
49
|
<div className="app">
|
|
97
|
-
<h1>Ketrics
|
|
98
|
-
<p className="subtitle">
|
|
99
|
-
|
|
100
|
-
<div className="
|
|
101
|
-
<
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
</
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
50
|
+
<h1>Ketrics Application</h1>
|
|
51
|
+
<p className="subtitle">SDK Feature Examples</p>
|
|
52
|
+
|
|
53
|
+
<div className="sections">
|
|
54
|
+
<div className="section">
|
|
55
|
+
<h2>General</h2>
|
|
56
|
+
<div className="button-group">
|
|
57
|
+
<button onClick={() => runFunction("echo", { message: "Hello!" })} className="button" disabled={loading}>
|
|
58
|
+
Echo
|
|
59
|
+
</button>
|
|
60
|
+
<button onClick={() => runFunction("info")} className="button" disabled={loading}>
|
|
61
|
+
Info
|
|
62
|
+
</button>
|
|
63
|
+
<button onClick={() => runFunction("fetchExternalApi")} className="button" disabled={loading}>
|
|
64
|
+
HTTP Request
|
|
65
|
+
</button>
|
|
66
|
+
</div>
|
|
67
|
+
</div>
|
|
68
|
+
|
|
69
|
+
<div className="section">
|
|
70
|
+
<h2>Volumes</h2>
|
|
71
|
+
<div className="button-group">
|
|
72
|
+
<button onClick={() => runFunction("saveFile")} className="button" disabled={loading}>
|
|
73
|
+
Save File
|
|
74
|
+
</button>
|
|
75
|
+
<button onClick={() => runFunction("readFile")} className="button" disabled={loading}>
|
|
76
|
+
Read File
|
|
77
|
+
</button>
|
|
78
|
+
<button onClick={() => runFunction("listFiles", { prefix: "output/" })} className="button" disabled={loading}>
|
|
79
|
+
List Files
|
|
80
|
+
</button>
|
|
81
|
+
<button onClick={() => handleDownload("generateDownloadUrl")} className="button" disabled={loading}>
|
|
82
|
+
Download File
|
|
83
|
+
</button>
|
|
84
|
+
<button onClick={() => runFunction("copyFile")} className="button" disabled={loading}>
|
|
85
|
+
Copy File
|
|
86
|
+
</button>
|
|
87
|
+
</div>
|
|
88
|
+
</div>
|
|
89
|
+
|
|
90
|
+
<div className="section">
|
|
91
|
+
<h2>Database</h2>
|
|
92
|
+
<div className="button-group">
|
|
93
|
+
<button onClick={() => runFunction("queryUsers", { limit: 5 })} className="button" disabled={loading}>
|
|
94
|
+
Query Users
|
|
95
|
+
</button>
|
|
96
|
+
<button onClick={() => runFunction("insertRecord", { name: "Test User", email: "test@example.com" })} className="button" disabled={loading}>
|
|
97
|
+
Insert Record
|
|
98
|
+
</button>
|
|
99
|
+
<button onClick={() => runFunction("transferFunds", { fromAccountId: 1, toAccountId: 2, amount: 100 })} className="button" disabled={loading}>
|
|
100
|
+
Transfer Funds
|
|
101
|
+
</button>
|
|
102
|
+
</div>
|
|
103
|
+
</div>
|
|
104
|
+
|
|
105
|
+
<div className="section">
|
|
106
|
+
<h2>Documents</h2>
|
|
107
|
+
<div className="button-group">
|
|
108
|
+
<button onClick={() => handleDownload("createSimplePdf")} className="button" disabled={loading}>
|
|
109
|
+
Create PDF
|
|
110
|
+
</button>
|
|
111
|
+
<button onClick={() => handleDownload("createInvoicePdf")} className="button" disabled={loading}>
|
|
112
|
+
Create Invoice PDF
|
|
113
|
+
</button>
|
|
114
|
+
<button onClick={() => handleDownload("createSpreadsheet")} className="button" disabled={loading}>
|
|
115
|
+
Create Spreadsheet
|
|
116
|
+
</button>
|
|
117
|
+
<button onClick={() => handleDownload("exportDataToExcel")} className="button" disabled={loading}>
|
|
118
|
+
Export to Excel
|
|
119
|
+
</button>
|
|
120
|
+
</div>
|
|
121
|
+
</div>
|
|
122
|
+
|
|
123
|
+
<div className="section">
|
|
124
|
+
<h2>Messaging & Jobs</h2>
|
|
125
|
+
<div className="button-group">
|
|
126
|
+
<button onClick={() => runFunction("sendNotification", { subject: "Test", body: "Hello from the app!" })} className="button" disabled={loading}>
|
|
127
|
+
Send Notification
|
|
128
|
+
</button>
|
|
129
|
+
<button onClick={() => runFunction("scheduleBackgroundJob")} className="button" disabled={loading}>
|
|
130
|
+
Schedule Job
|
|
131
|
+
</button>
|
|
132
|
+
<button onClick={() => runFunction("getSecret")} className="button" disabled={loading}>
|
|
133
|
+
Get Secret
|
|
134
|
+
</button>
|
|
135
|
+
</div>
|
|
136
|
+
</div>
|
|
127
137
|
</div>
|
|
128
138
|
|
|
139
|
+
{loading && <p className="loading">Running...</p>}
|
|
129
140
|
{error && <p className="error">{error}</p>}
|
|
130
|
-
{result &&
|
|
141
|
+
{result && (
|
|
142
|
+
<div className="result">
|
|
143
|
+
<pre>{result}</pre>
|
|
144
|
+
</div>
|
|
145
|
+
)}
|
|
131
146
|
|
|
132
147
|
<div className="info">
|
|
133
148
|
<h2>About</h2>
|
|
134
|
-
<p>This
|
|
149
|
+
<p>This application demonstrates the Ketrics SDK features:</p>
|
|
135
150
|
<ul>
|
|
136
|
-
<li>
|
|
137
|
-
<li>
|
|
138
|
-
<li>
|
|
151
|
+
<li>Volume storage (save, read, list, download, copy files)</li>
|
|
152
|
+
<li>Database connections (query, insert, transactions)</li>
|
|
153
|
+
<li>PDF document generation (simple and invoice-style)</li>
|
|
154
|
+
<li>Excel workbook creation (single and multi-sheet)</li>
|
|
155
|
+
<li>User messaging and notifications</li>
|
|
156
|
+
<li>Background job scheduling</li>
|
|
157
|
+
<li>Secret management</li>
|
|
158
|
+
<li>External HTTP requests</li>
|
|
139
159
|
</ul>
|
|
140
160
|
</div>
|
|
141
161
|
</div>
|
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
import { createAuthManager } from "@ketrics/sdk-frontend";
|
|
2
|
-
import axios from "axios";
|
|
3
2
|
|
|
4
3
|
const auth = createAuthManager();
|
|
5
4
|
|
|
6
5
|
// Initialize auto-refresh
|
|
7
6
|
auth.initAutoRefresh({
|
|
8
7
|
refreshBuffer: 60,
|
|
9
|
-
onTokenUpdated: (
|
|
10
|
-
//
|
|
11
|
-
axios.defaults.headers.common["Authorization"] = `Bearer ${token}`;
|
|
8
|
+
onTokenUpdated: () => {
|
|
9
|
+
// Token is refreshed automatically and retrieved via getAccessToken()
|
|
12
10
|
},
|
|
13
11
|
});
|
|
14
12
|
|
|
@@ -19,7 +17,7 @@ class APIClient {
|
|
|
19
17
|
this.auth = authManager;
|
|
20
18
|
}
|
|
21
19
|
|
|
22
|
-
private async makeRequest(fnName: string) {
|
|
20
|
+
private async makeRequest(fnName: string, payload?: unknown) {
|
|
23
21
|
const runtimeApiUrl = this.auth.getRuntimeApiUrl();
|
|
24
22
|
const tenantId = this.auth.getTenantId();
|
|
25
23
|
const applicationId = this.auth.getApplicationId();
|
|
@@ -32,21 +30,23 @@ class APIClient {
|
|
|
32
30
|
const response = await fetch(`${runtimeApiUrl}/tenants/${tenantId}/applications/${applicationId}/functions/${fnName}`, {
|
|
33
31
|
method: "POST",
|
|
34
32
|
headers: {
|
|
35
|
-
Authorization: `Bearer ${accessToken}`,
|
|
33
|
+
"Authorization": `Bearer ${accessToken}`,
|
|
34
|
+
"Content-Type": "application/json",
|
|
36
35
|
},
|
|
36
|
+
body: JSON.stringify({ payload: payload ?? null }),
|
|
37
37
|
});
|
|
38
38
|
|
|
39
39
|
if (!response.ok) {
|
|
40
|
-
|
|
40
|
+
const errorBody = await response.json().catch(() => null);
|
|
41
|
+
const message = errorBody?.error?.message || `API request failed with status ${response.status}`;
|
|
42
|
+
throw new Error(message);
|
|
41
43
|
}
|
|
42
44
|
|
|
43
45
|
return response.json();
|
|
44
46
|
}
|
|
45
47
|
|
|
46
|
-
async run(fnName: string) {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
return response;
|
|
48
|
+
async run(fnName: string, payload?: unknown) {
|
|
49
|
+
return this.makeRequest(fnName, payload);
|
|
50
50
|
}
|
|
51
51
|
}
|
|
52
52
|
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"endpoint": "/tenants/{{tenantId}}/applications/{{applicationId}}/functions/createInvoicePdf",
|
|
3
|
+
"method": "POST",
|
|
4
|
+
"headers": {
|
|
5
|
+
"Authorization": "Bearer {{token}}",
|
|
6
|
+
"Content-Type": "application/json"
|
|
7
|
+
},
|
|
8
|
+
"body": {
|
|
9
|
+
"payload": {
|
|
10
|
+
"invoiceNumber": "INV-100",
|
|
11
|
+
"customerName": "Acme Corp",
|
|
12
|
+
"items": [
|
|
13
|
+
{ "description": "Consulting", "quantity": 10, "price": 200 },
|
|
14
|
+
{ "description": "Development", "quantity": 40, "price": 150 }
|
|
15
|
+
]
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
package/templates/HelloWorld/tests/{test.getSecretWithoutGrant.json → test.createSimplePdf.json}
RENAMED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
{
|
|
2
|
-
"endpoint": "/tenants/{{tenantId}}/applications/{{applicationId}}/functions/
|
|
2
|
+
"endpoint": "/tenants/{{tenantId}}/applications/{{applicationId}}/functions/createSimplePdf",
|
|
3
3
|
"method": "POST",
|
|
4
4
|
"headers": {
|
|
5
5
|
"Authorization": "Bearer {{token}}",
|
|
6
6
|
"Content-Type": "application/json"
|
|
7
7
|
},
|
|
8
|
-
"body": {
|
|
8
|
+
"body": {
|
|
9
|
+
"payload": null
|
|
10
|
+
}
|
|
9
11
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"endpoint": "/tenants/{{tenantId}}/applications/{{applicationId}}/functions/fetchExternalApi",
|
|
3
|
+
"method": "POST",
|
|
4
|
+
"headers": {
|
|
5
|
+
"Authorization": "Bearer {{token}}",
|
|
6
|
+
"Content-Type": "application/json"
|
|
7
|
+
},
|
|
8
|
+
"body": {
|
|
9
|
+
"payload": {
|
|
10
|
+
"url": "https://jsonplaceholder.typicode.com/posts/1"
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"endpoint": "/tenants/{{tenantId}}/applications/{{applicationId}}/functions/listFiles",
|
|
3
|
+
"method": "POST",
|
|
4
|
+
"headers": {
|
|
5
|
+
"Authorization": "Bearer {{token}}",
|
|
6
|
+
"Content-Type": "application/json"
|
|
7
|
+
},
|
|
8
|
+
"body": {
|
|
9
|
+
"payload": {
|
|
10
|
+
"prefix": "output/"
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
|
-
"endpoint": "/tenants/{{tenantId}}/applications/{{applicationId}}/functions/
|
|
2
|
+
"endpoint": "/tenants/{{tenantId}}/applications/{{applicationId}}/functions/queryUsers",
|
|
3
3
|
"method": "POST",
|
|
4
4
|
"headers": {
|
|
5
5
|
"Authorization": "Bearer {{token}}",
|
|
6
6
|
"Content-Type": "application/json"
|
|
7
7
|
},
|
|
8
8
|
"body": {
|
|
9
|
-
"
|
|
10
|
-
"
|
|
9
|
+
"payload": {
|
|
10
|
+
"limit": 5
|
|
11
11
|
}
|
|
12
12
|
}
|
|
13
13
|
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
|
-
"endpoint": "/tenants/{{tenantId}}/applications/{{applicationId}}/functions/
|
|
2
|
+
"endpoint": "/tenants/{{tenantId}}/applications/{{applicationId}}/functions/readFile",
|
|
3
3
|
"method": "POST",
|
|
4
4
|
"headers": {
|
|
5
5
|
"Authorization": "Bearer {{token}}",
|
|
6
6
|
"Content-Type": "application/json"
|
|
7
7
|
},
|
|
8
8
|
"body": {
|
|
9
|
-
"
|
|
9
|
+
"payload": null
|
|
10
10
|
}
|
|
11
11
|
}
|
package/templates/HelloWorld/tests/{test.testWriteFileWithoutVolumeGrant.json → test.saveFile.json}
RENAMED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
{
|
|
2
|
-
"endpoint": "/tenants/{{tenantId}}/applications/{{applicationId}}/functions/
|
|
2
|
+
"endpoint": "/tenants/{{tenantId}}/applications/{{applicationId}}/functions/saveFile",
|
|
3
3
|
"method": "POST",
|
|
4
4
|
"headers": {
|
|
5
5
|
"Authorization": "Bearer {{token}}",
|
|
6
6
|
"Content-Type": "application/json"
|
|
7
7
|
},
|
|
8
|
-
"body": {
|
|
8
|
+
"body": {
|
|
9
|
+
"payload": null
|
|
10
|
+
}
|
|
9
11
|
}
|