@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.
Files changed (37) hide show
  1. package/README.md +623 -607
  2. package/dist/src/cli.d.ts.map +1 -1
  3. package/dist/src/cli.js +2 -2
  4. package/dist/src/cli.js.map +1 -1
  5. package/dist/src/version.d.ts +2 -0
  6. package/dist/src/version.d.ts.map +1 -0
  7. package/dist/src/version.js +6 -0
  8. package/dist/src/version.js.map +1 -0
  9. package/package.json +3 -2
  10. package/templates/HelloWorld/README.md +83 -106
  11. package/templates/HelloWorld/backend/package.json +1 -1
  12. package/templates/HelloWorld/backend/src/database.ts +108 -0
  13. package/templates/HelloWorld/backend/src/excel.ts +118 -0
  14. package/templates/HelloWorld/backend/src/http.ts +22 -0
  15. package/templates/HelloWorld/backend/src/index.ts +105 -29
  16. package/templates/HelloWorld/backend/src/jobs.ts +47 -0
  17. package/templates/HelloWorld/backend/src/messages.ts +59 -0
  18. package/templates/HelloWorld/backend/src/pdf.ts +212 -0
  19. package/templates/HelloWorld/backend/src/secrets.ts +21 -14
  20. package/templates/HelloWorld/backend/src/volumes.ts +107 -0
  21. package/templates/HelloWorld/frontend/package.json +1 -3
  22. package/templates/HelloWorld/frontend/src/App.css +62 -37
  23. package/templates/HelloWorld/frontend/src/App.tsx +131 -111
  24. package/templates/HelloWorld/frontend/src/services/index.ts +11 -11
  25. package/templates/HelloWorld/tests/test.createInvoicePdf.json +18 -0
  26. package/templates/HelloWorld/tests/{test.getSecretWithoutGrant.json → test.createSimplePdf.json} +4 -2
  27. package/templates/HelloWorld/tests/test.createSpreadsheet.json +11 -0
  28. package/templates/HelloWorld/tests/test.echo.json +2 -2
  29. package/templates/HelloWorld/tests/test.fetchExternalApi.json +13 -0
  30. package/templates/HelloWorld/tests/test.getSecret.json +5 -1
  31. package/templates/HelloWorld/tests/test.info.json +3 -1
  32. package/templates/HelloWorld/tests/test.listFiles.json +13 -0
  33. package/templates/HelloWorld/tests/{test.echo2.json → test.queryUsers.json} +3 -3
  34. package/templates/HelloWorld/tests/{test.greet.json → test.readFile.json} +2 -2
  35. package/templates/HelloWorld/tests/{test.testWriteFileWithoutVolumeGrant.json → test.saveFile.json} +4 -2
  36. package/templates/HelloWorld/tests/test.sendNotification.json +14 -0
  37. 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: center;
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: 40px;
22
- max-width: 500px;
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: 8px;
30
- font-size: 28px;
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
- .form {
40
+ .sections {
40
41
  display: flex;
41
- gap: 12px;
42
- margin-bottom: 16px;
42
+ flex-direction: column;
43
+ gap: 20px;
43
44
  }
44
45
 
45
- .input {
46
- flex: 1;
47
- padding: 12px 16px;
48
- border: 2px solid #e0e0e0;
49
- border-radius: 8px;
50
- font-size: 16px;
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
- .input:focus {
55
- outline: none;
56
- border-color: #667eea;
54
+ .button-group {
55
+ display: flex;
56
+ flex-wrap: wrap;
57
+ gap: 8px;
57
58
  }
58
59
 
59
60
  .button {
60
- padding: 12px 24px;
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: 8px;
65
- font-size: 16px;
66
- font-weight: 600;
65
+ border-radius: 6px;
66
+ font-size: 13px;
67
+ font-weight: 500;
67
68
  cursor: pointer;
68
- transition: transform 0.2s, box-shadow 0.2s;
69
+ transition: transform 0.15s, box-shadow 0.15s, opacity 0.15s;
69
70
  }
70
71
 
71
- .button:hover {
72
- transform: translateY(-2px);
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-bottom: 16px;
98
+ margin-top: 16px;
99
+ font-size: 14px;
86
100
  }
87
101
 
88
102
  .result {
89
- color: #38a169;
90
- background: #c6f6d5;
91
- padding: 12px;
103
+ background: #f0fdf4;
104
+ border: 1px solid #bbf7d0;
92
105
  border-radius: 8px;
93
- margin-bottom: 16px;
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: 32px;
98
- padding-top: 24px;
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: 18px;
126
+ font-size: 16px;
104
127
  color: #333;
105
- margin-bottom: 12px;
128
+ margin-bottom: 8px;
106
129
  }
107
130
 
108
131
  .info p {
109
132
  color: #666;
110
- margin-bottom: 12px;
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: 6px;
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 handleSubmit = () => {
13
- const validation = inputSchema.safeParse(input);
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(`You submitted: ${validation.data}`);
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("testWriteFileWithoutVolumeGrant");
71
- console.log("API Data:", data);
14
+ const data = await apiClient.run(fnName, payload);
15
+ setResult(JSON.stringify(data, null, 2));
72
16
  } catch (err) {
73
- console.error("API Error:", err);
17
+ setError(err instanceof Error ? err.message : "An error occurred");
18
+ } finally {
19
+ setLoading(false);
74
20
  }
75
21
  };
76
22
 
77
- const handleGetSecret = async () => {
78
- try {
79
- const data = await apiClient.run("getSecret");
80
- console.log("Secret Data:", data);
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("getSecretWithoutGrant");
89
- console.log("Secret Data:", data);
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
- console.error("Secret Error:", err);
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 Test Application</h1>
98
- <p className="subtitle">Frontend Demo with Zod Validation</p>
99
-
100
- <div className="form">
101
- <input type="text" value={input} onChange={(e) => setInput(e.target.value)} placeholder="Enter something..." className="input" />
102
- <button onClick={handleSubmit} className="button">
103
- Submit
104
- </button>
105
-
106
- <button onClick={handleRunEcho} className="button">
107
- Test Echo API
108
- </button>
109
- <button onClick={handleRunSaveFile} className="button">
110
- Test Save File API
111
- </button>
112
- <button onClick={handleRunReadFile} className="button">
113
- Test Read File API
114
- </button>
115
- <button onClick={handleRunGenerateDownloadUrl} className="button">
116
- Test Generate Download URL API
117
- </button>
118
- <button onClick={handleRunTestWriteFileWithoutVolumeGrant} className="button">
119
- Test Write File Without Volume Grant API
120
- </button>
121
- <button onClick={handleGetSecret} className="button">
122
- Get Secret with Grant
123
- </button>
124
- <button onClick={handleGetSecretWithoutGrant} className="button">
125
- Get Secret without Grant
126
- </button>
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 && <p className="result">{result}</p>}
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 is a test application for the Ketrics platform. It demonstrates:</p>
149
+ <p>This application demonstrates the Ketrics SDK features:</p>
135
150
  <ul>
136
- <li>React + TypeScript + Vite setup</li>
137
- <li>Zod schema validation</li>
138
- <li>Deployment via Ketrics CLI</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: (token) => {
10
- // Update your HTTP client headers
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
- throw new Error(`API request failed with status ${response.status}`);
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
- const response = await this.makeRequest(fnName);
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
+ }
@@ -1,9 +1,11 @@
1
1
  {
2
- "endpoint": "/tenants/{{tenantId}}/applications/{{applicationId}}/functions/getSecretWithoutGrant",
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,11 @@
1
+ {
2
+ "endpoint": "/tenants/{{tenantId}}/applications/{{applicationId}}/functions/createSpreadsheet",
3
+ "method": "POST",
4
+ "headers": {
5
+ "Authorization": "Bearer {{token}}",
6
+ "Content-Type": "application/json"
7
+ },
8
+ "body": {
9
+ "payload": null
10
+ }
11
+ }
@@ -6,8 +6,8 @@
6
6
  "Content-Type": "application/json"
7
7
  },
8
8
  "body": {
9
- "data": {
10
- "hello": "world"
9
+ "payload": {
10
+ "message": "Hello, World!"
11
11
  }
12
12
  }
13
13
  }
@@ -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
+ }
@@ -5,5 +5,9 @@
5
5
  "Authorization": "Bearer {{token}}",
6
6
  "Content-Type": "application/json"
7
7
  },
8
- "body": {}
8
+ "body": {
9
+ "payload": {
10
+ "code": "apikey"
11
+ }
12
+ }
9
13
  }
@@ -5,5 +5,7 @@
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/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/echo2",
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
- "data": {
10
- "hello": "world"
9
+ "payload": {
10
+ "limit": 5
11
11
  }
12
12
  }
13
13
  }
@@ -1,11 +1,11 @@
1
1
  {
2
- "endpoint": "/tenants/{{tenantId}}/applications/{{applicationId}}/functions/greet",
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
- "name": "Localhost"
9
+ "payload": null
10
10
  }
11
11
  }
@@ -1,9 +1,11 @@
1
1
  {
2
- "endpoint": "/tenants/{{tenantId}}/applications/{{applicationId}}/functions/testWriteFileWithoutVolumeGrant",
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
  }