@ketrics/ketrics-cli 0.2.3 → 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/version.d.ts +1 -1
- package/dist/src/version.js +1 -1
- package/package.json +1 -1
- 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
|
@@ -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
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"endpoint": "/tenants/{{tenantId}}/applications/{{applicationId}}/functions/sendNotification",
|
|
3
|
+
"method": "POST",
|
|
4
|
+
"headers": {
|
|
5
|
+
"Authorization": "Bearer {{token}}",
|
|
6
|
+
"Content-Type": "application/json"
|
|
7
|
+
},
|
|
8
|
+
"body": {
|
|
9
|
+
"payload": {
|
|
10
|
+
"subject": "Test Notification",
|
|
11
|
+
"body": "This is a test notification from the Ketrics CLI."
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
const testWriteFileWithoutVolumeGrant = async () => {
|
|
2
|
-
const volume = await ketrics.Volume.connect("test2");
|
|
3
|
-
const buffer = Buffer.from("This should fail");
|
|
4
|
-
const putResults = await volume.put("files/fail.txt", buffer);
|
|
5
|
-
return { putResults };
|
|
6
|
-
};
|
|
7
|
-
|
|
8
|
-
const saveFile = async () => {
|
|
9
|
-
const volume = await ketrics.Volume.connect("test-volume");
|
|
10
|
-
|
|
11
|
-
const data = {
|
|
12
|
-
id: ketrics.application.id,
|
|
13
|
-
code: ketrics.application.code,
|
|
14
|
-
name: ketrics.application.name,
|
|
15
|
-
version: ketrics.application.version,
|
|
16
|
-
deploymentId: ketrics.application.deploymentId,
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
// Write text/JSON content
|
|
20
|
-
const resultData = await volume.put("output/data.json", JSON.stringify(data), {
|
|
21
|
-
contentType: "application/json",
|
|
22
|
-
});
|
|
23
|
-
console.log(resultData.key, resultData.etag, resultData.size);
|
|
24
|
-
|
|
25
|
-
// Write binary content
|
|
26
|
-
const buffer = Buffer.from("Hello World");
|
|
27
|
-
const resultBuffer = await volume.put("files/hello.txt", buffer);
|
|
28
|
-
|
|
29
|
-
return {
|
|
30
|
-
data,
|
|
31
|
-
dataFile: { key: resultData.key, etag: resultData.etag, size: resultData.size },
|
|
32
|
-
bufferFile: { key: resultBuffer.key, etag: resultBuffer.etag, size: resultBuffer.size },
|
|
33
|
-
};
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
const readFile = async () => {
|
|
37
|
-
const volume = await ketrics.Volume.connect("test-volume");
|
|
38
|
-
const file = await volume.get("output/data.json");
|
|
39
|
-
if (!file) {
|
|
40
|
-
throw new Error("File not found");
|
|
41
|
-
}
|
|
42
|
-
const content = await file.content.toString();
|
|
43
|
-
return {
|
|
44
|
-
content: content,
|
|
45
|
-
parsed: JSON.parse(content),
|
|
46
|
-
};
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
const generateDownloadUrl = async () => {
|
|
50
|
-
const volume = await ketrics.Volume.connect("test-volume");
|
|
51
|
-
const results = await volume.generateDownloadUrl("output/data.json"); // URL valid for 1 hour
|
|
52
|
-
return { url: results.url };
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
export { saveFile, readFile, generateDownloadUrl, testWriteFileWithoutVolumeGrant };
|