tale-js-sdk 0.1.4 → 1.0.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/dist/acl/index.d.ts +232 -102
- package/dist/acl/index.js +431 -489
- package/dist/acl/types.d.ts +95 -161
- package/dist/common/types.d.ts +5 -2
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/rbac/index.d.ts +464 -2
- package/dist/rbac/index.js +1123 -2
- package/dist/rbac/types.d.ts +73 -304
- package/package.json +1 -1
package/dist/acl/index.js
CHANGED
|
@@ -1,747 +1,689 @@
|
|
|
1
|
-
import { getAppToken } from
|
|
2
|
-
import { ApiError, ConfigurationError, NetworkError } from
|
|
3
|
-
//
|
|
1
|
+
import { getAppToken } from "../token.js";
|
|
2
|
+
import { ApiError, ConfigurationError, NetworkError } from "../errors.js";
|
|
3
|
+
// ==================== ACL Record Management (v2) ====================
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
5
|
+
* Gets an ACL record by ID.
|
|
6
6
|
*
|
|
7
|
-
* @param
|
|
8
|
-
* @param options - Optional configuration
|
|
9
|
-
* @returns Promise resolving to
|
|
7
|
+
* @param recordId - Record ID
|
|
8
|
+
* @param options - Optional configuration
|
|
9
|
+
* @returns Promise resolving to ACL record information
|
|
10
10
|
* @throws {ConfigurationError} When required environment variables are missing
|
|
11
|
-
* @throws {ApiError} When API request fails
|
|
11
|
+
* @throws {ApiError} When API request fails
|
|
12
12
|
* @throws {NetworkError} When network request fails
|
|
13
13
|
*
|
|
14
14
|
* @example
|
|
15
15
|
* ```typescript
|
|
16
|
-
* import {
|
|
16
|
+
* import { getAclRecord } from '@tale/client';
|
|
17
17
|
*
|
|
18
18
|
* try {
|
|
19
|
-
* const
|
|
20
|
-
*
|
|
21
|
-
* subject_type: 'user',
|
|
22
|
-
* subject_id: 'user_123',
|
|
23
|
-
* resource_type: 'document',
|
|
24
|
-
* resource_id: 'doc_456',
|
|
25
|
-
* effect_type: 'allow',
|
|
26
|
-
* priority: 50,
|
|
27
|
-
* description: 'Allow user to read document'
|
|
28
|
-
* });
|
|
29
|
-
* console.log('ACL record created:', result.record.record_id);
|
|
19
|
+
* const record = await getAclRecord('record_id_here');
|
|
20
|
+
* console.log('Record effect:', record.effect_type);
|
|
30
21
|
* } catch (error) {
|
|
31
|
-
* console.error('Failed to
|
|
22
|
+
* console.error('Failed to get ACL record:', error.message);
|
|
32
23
|
* }
|
|
33
24
|
* ```
|
|
34
25
|
*/
|
|
35
|
-
export async function
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
throw new ApiError('template_id is required for ACL record creation', 400, '9400');
|
|
26
|
+
export async function getAclRecord(recordId, options) {
|
|
27
|
+
if (!recordId || recordId.trim() === "") {
|
|
28
|
+
throw new ApiError("record_id is required", 400, "9400");
|
|
39
29
|
}
|
|
40
|
-
|
|
41
|
-
|
|
30
|
+
const token = options?.appToken ?? (await getAppToken(options));
|
|
31
|
+
const env = globalThis?.process?.env ?? import.meta?.env ?? undefined;
|
|
32
|
+
const base = options?.baseUrl ?? env?.TALE_BASE_URL ?? undefined;
|
|
33
|
+
if (!base) {
|
|
34
|
+
throw new ConfigurationError("Missing required environment variable: TALE_BASE_URL");
|
|
42
35
|
}
|
|
43
|
-
|
|
44
|
-
|
|
36
|
+
const url = String(base).replace(/\/+$/, "") +
|
|
37
|
+
`/acl/v2/records/${encodeURIComponent(recordId)}`;
|
|
38
|
+
let response;
|
|
39
|
+
try {
|
|
40
|
+
response = await globalThis.fetch(url, {
|
|
41
|
+
method: "GET",
|
|
42
|
+
headers: {
|
|
43
|
+
"Content-Type": "application/json",
|
|
44
|
+
"x-t-token": token,
|
|
45
|
+
},
|
|
46
|
+
});
|
|
45
47
|
}
|
|
46
|
-
|
|
47
|
-
throw new
|
|
48
|
+
catch (error) {
|
|
49
|
+
throw new NetworkError(`Failed to get ACL record: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
48
50
|
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
+
const json = (await response.json());
|
|
52
|
+
if (json.code !== 200) {
|
|
53
|
+
const errorMsg = typeof json.msg === "string" ? json.msg : "Failed to get ACL record";
|
|
54
|
+
throw new ApiError(errorMsg, response.status, json.code);
|
|
51
55
|
}
|
|
52
|
-
if (!
|
|
53
|
-
throw new ApiError(
|
|
56
|
+
if (!json.data) {
|
|
57
|
+
throw new ApiError("Invalid ACL record response: missing data", response.status);
|
|
54
58
|
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
59
|
+
return json.data;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Lists ACL records with pagination and optional filtering.
|
|
63
|
+
*
|
|
64
|
+
* @param options - Optional parameters for pagination and filtering
|
|
65
|
+
* @returns Promise resolving to paginated ACL record list
|
|
66
|
+
* @throws {ConfigurationError} When required environment variables are missing
|
|
67
|
+
* @throws {ApiError} When API request fails
|
|
68
|
+
* @throws {NetworkError} When network request fails
|
|
69
|
+
*
|
|
70
|
+
* @example
|
|
71
|
+
* ```typescript
|
|
72
|
+
* import { listAclRecords } from '@tale/client';
|
|
73
|
+
*
|
|
74
|
+
* try {
|
|
75
|
+
* const result = await listAclRecords({
|
|
76
|
+
* page: 0,
|
|
77
|
+
* size: 20,
|
|
78
|
+
* resource_type: 'document'
|
|
79
|
+
* });
|
|
80
|
+
* console.log(`Found ${result.totalElements} records`);
|
|
81
|
+
* } catch (error) {
|
|
82
|
+
* console.error('Failed to list ACL records:', error.message);
|
|
83
|
+
* }
|
|
84
|
+
* ```
|
|
85
|
+
*/
|
|
86
|
+
export async function listAclRecords(options) {
|
|
87
|
+
const token = options?.appToken ?? (await getAppToken(options));
|
|
58
88
|
const env = globalThis?.process?.env ?? import.meta?.env ?? undefined;
|
|
59
89
|
const base = options?.baseUrl ?? env?.TALE_BASE_URL ?? undefined;
|
|
60
90
|
if (!base) {
|
|
61
|
-
throw new ConfigurationError(
|
|
62
|
-
}
|
|
63
|
-
const url = String(base).replace(/\/+$/, '') + '/acl/v1/records';
|
|
64
|
-
// Validate priority range
|
|
65
|
-
if (recordData.priority !== undefined && (recordData.priority < 0 || recordData.priority > 100)) {
|
|
66
|
-
throw new ApiError('priority must be between 0 and 100', 400, '9400');
|
|
91
|
+
throw new ConfigurationError("Missing required environment variable: TALE_BASE_URL");
|
|
67
92
|
}
|
|
68
|
-
|
|
69
|
-
const
|
|
70
|
-
|
|
71
|
-
|
|
93
|
+
const url = new URL(String(base).replace(/\/+$/, "") + "/acl/v2/records");
|
|
94
|
+
const queryParams = {
|
|
95
|
+
page: 0,
|
|
96
|
+
size: 20,
|
|
97
|
+
...options,
|
|
72
98
|
};
|
|
99
|
+
Object.entries(queryParams).forEach(([key, value]) => {
|
|
100
|
+
if (value !== undefined) {
|
|
101
|
+
url.searchParams.append(key, String(value));
|
|
102
|
+
}
|
|
103
|
+
});
|
|
73
104
|
let response;
|
|
74
105
|
try {
|
|
75
|
-
response = await globalThis.fetch(url, {
|
|
76
|
-
method:
|
|
106
|
+
response = await globalThis.fetch(url.toString(), {
|
|
107
|
+
method: "GET",
|
|
77
108
|
headers: {
|
|
78
|
-
|
|
79
|
-
|
|
109
|
+
"Content-Type": "application/json",
|
|
110
|
+
"x-t-token": token,
|
|
80
111
|
},
|
|
81
|
-
body: JSON.stringify(finalRecordData),
|
|
82
112
|
});
|
|
83
113
|
}
|
|
84
114
|
catch (error) {
|
|
85
|
-
throw new NetworkError(`Failed to
|
|
115
|
+
throw new NetworkError(`Failed to list ACL records: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
86
116
|
}
|
|
87
|
-
|
|
88
|
-
try {
|
|
89
|
-
const responseJson = await response.json();
|
|
90
|
-
json = responseJson;
|
|
91
|
-
}
|
|
92
|
-
catch (error) {
|
|
93
|
-
throw new ApiError(`Failed to parse ACL record creation response: ${error instanceof Error ? error.message : 'Invalid JSON'}`, response.status);
|
|
94
|
-
}
|
|
95
|
-
// Handle API errors
|
|
117
|
+
const json = (await response.json());
|
|
96
118
|
if (json.code !== 200) {
|
|
97
|
-
const errorMsg = typeof json.msg ===
|
|
119
|
+
const errorMsg = typeof json.msg === "string" ? json.msg : "Failed to list ACL records";
|
|
98
120
|
throw new ApiError(errorMsg, response.status, json.code);
|
|
99
121
|
}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
throw new ApiError('Invalid ACL record creation response: missing record data', response.status);
|
|
122
|
+
if (!json.data || !Array.isArray(json.data.content)) {
|
|
123
|
+
throw new ApiError("Invalid ACL records response: missing data", response.status);
|
|
103
124
|
}
|
|
104
125
|
return json.data;
|
|
105
126
|
}
|
|
106
127
|
/**
|
|
107
|
-
*
|
|
128
|
+
* Creates a new ACL record.
|
|
108
129
|
*
|
|
109
|
-
* @param
|
|
110
|
-
* @param
|
|
111
|
-
* @
|
|
112
|
-
* @returns Promise resolving to the updated ACL record information
|
|
130
|
+
* @param request - ACL record creation request
|
|
131
|
+
* @param options - Optional configuration
|
|
132
|
+
* @returns Promise resolving to created ACL record
|
|
113
133
|
* @throws {ConfigurationError} When required environment variables are missing
|
|
114
|
-
* @throws {ApiError} When API request fails
|
|
134
|
+
* @throws {ApiError} When API request fails
|
|
115
135
|
* @throws {NetworkError} When network request fails
|
|
136
|
+
*
|
|
137
|
+
* @example
|
|
138
|
+
* ```typescript
|
|
139
|
+
* import { createAclRecord } from '@tale/client';
|
|
140
|
+
*
|
|
141
|
+
* try {
|
|
142
|
+
* const record = await createAclRecord({
|
|
143
|
+
* subject_type: 'user',
|
|
144
|
+
* subject_id: 'user123',
|
|
145
|
+
* resource_type: 'document',
|
|
146
|
+
* resource_id: 'doc456',
|
|
147
|
+
* effect_type: 'allow'
|
|
148
|
+
* });
|
|
149
|
+
* console.log('Record created:', record.record_id);
|
|
150
|
+
* } catch (error) {
|
|
151
|
+
* console.error('Failed to create ACL record:', error.message);
|
|
152
|
+
* }
|
|
153
|
+
* ```
|
|
116
154
|
*/
|
|
117
|
-
export async function
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
throw new ApiError('record_id is required for ACL record update', 400, '9400');
|
|
155
|
+
export async function createAclRecord(request, options) {
|
|
156
|
+
if (!request.subject_type || request.subject_type.trim() === "") {
|
|
157
|
+
throw new ApiError("subject_type is required", 400, "9400");
|
|
121
158
|
}
|
|
122
|
-
if (
|
|
123
|
-
throw new ApiError(
|
|
159
|
+
if (!request.resource_type || request.resource_type.trim() === "") {
|
|
160
|
+
throw new ApiError("resource_type is required", 400, "9400");
|
|
124
161
|
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
throw new ApiError('priority must be between 0 and 100', 400, '9400');
|
|
162
|
+
if (!request.effect_type || request.effect_type.trim() === "") {
|
|
163
|
+
throw new ApiError("effect_type is required", 400, "9400");
|
|
128
164
|
}
|
|
129
|
-
|
|
130
|
-
const token = options?.appToken ?? await getAppToken(options);
|
|
131
|
-
// Determine base URL
|
|
165
|
+
const token = options?.appToken ?? (await getAppToken(options));
|
|
132
166
|
const env = globalThis?.process?.env ?? import.meta?.env ?? undefined;
|
|
133
167
|
const base = options?.baseUrl ?? env?.TALE_BASE_URL ?? undefined;
|
|
134
168
|
if (!base) {
|
|
135
|
-
throw new ConfigurationError(
|
|
169
|
+
throw new ConfigurationError("Missing required environment variable: TALE_BASE_URL");
|
|
136
170
|
}
|
|
137
|
-
const url = String(base).replace(/\/+$/,
|
|
171
|
+
const url = String(base).replace(/\/+$/, "") + "/acl/v2/records";
|
|
138
172
|
let response;
|
|
139
173
|
try {
|
|
140
174
|
response = await globalThis.fetch(url, {
|
|
141
|
-
method:
|
|
175
|
+
method: "POST",
|
|
142
176
|
headers: {
|
|
143
|
-
|
|
144
|
-
|
|
177
|
+
"Content-Type": "application/json",
|
|
178
|
+
"x-t-token": token,
|
|
145
179
|
},
|
|
146
|
-
body: JSON.stringify(
|
|
180
|
+
body: JSON.stringify(request),
|
|
147
181
|
});
|
|
148
182
|
}
|
|
149
183
|
catch (error) {
|
|
150
|
-
throw new NetworkError(`Failed to
|
|
151
|
-
}
|
|
152
|
-
let json;
|
|
153
|
-
try {
|
|
154
|
-
const responseJson = await response.json();
|
|
155
|
-
json = responseJson;
|
|
184
|
+
throw new NetworkError(`Failed to create ACL record: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
156
185
|
}
|
|
157
|
-
|
|
158
|
-
throw new ApiError(`Failed to parse ACL record update response: ${error instanceof Error ? error.message : 'Invalid JSON'}`, response.status);
|
|
159
|
-
}
|
|
160
|
-
// Handle API errors
|
|
186
|
+
const json = (await response.json());
|
|
161
187
|
if (json.code !== 200) {
|
|
162
|
-
const errorMsg = typeof json.msg ===
|
|
188
|
+
const errorMsg = typeof json.msg === "string" ? json.msg : "Failed to create ACL record";
|
|
163
189
|
throw new ApiError(errorMsg, response.status, json.code);
|
|
164
190
|
}
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
throw new ApiError('Invalid ACL record update response: missing record data', response.status);
|
|
191
|
+
if (!json.data) {
|
|
192
|
+
throw new ApiError("Invalid ACL record creation response: missing data", response.status);
|
|
168
193
|
}
|
|
169
194
|
return json.data;
|
|
170
195
|
}
|
|
171
196
|
/**
|
|
172
|
-
*
|
|
197
|
+
* Batch creates ACL records.
|
|
173
198
|
*
|
|
174
|
-
* @param
|
|
175
|
-
* @param options - Optional configuration
|
|
176
|
-
* @returns Promise resolving to
|
|
199
|
+
* @param requests - Array of ACL record creation requests
|
|
200
|
+
* @param options - Optional configuration
|
|
201
|
+
* @returns Promise resolving to batch creation response with success/failure details
|
|
177
202
|
* @throws {ConfigurationError} When required environment variables are missing
|
|
178
|
-
* @throws {ApiError} When API request fails
|
|
203
|
+
* @throws {ApiError} When API request fails
|
|
179
204
|
* @throws {NetworkError} When network request fails
|
|
205
|
+
*
|
|
206
|
+
* @example
|
|
207
|
+
* ```typescript
|
|
208
|
+
* import { batchCreateAclRecords } from '@tale/client';
|
|
209
|
+
*
|
|
210
|
+
* try {
|
|
211
|
+
* const result = await batchCreateAclRecords([
|
|
212
|
+
* {
|
|
213
|
+
* subject_type: 'user',
|
|
214
|
+
* subject_id: 'user1',
|
|
215
|
+
* resource_type: 'document',
|
|
216
|
+
* effect_type: 'allow'
|
|
217
|
+
* },
|
|
218
|
+
* {
|
|
219
|
+
* subject_type: 'user',
|
|
220
|
+
* subject_id: 'user2',
|
|
221
|
+
* resource_type: 'document',
|
|
222
|
+
* effect_type: 'deny'
|
|
223
|
+
* }
|
|
224
|
+
* ]);
|
|
225
|
+
* console.log(`Created ${result.success_count}, Failed ${result.failure_count}`);
|
|
226
|
+
* } catch (error) {
|
|
227
|
+
* console.error('Failed to batch create ACL records:', error.message);
|
|
228
|
+
* }
|
|
229
|
+
* ```
|
|
180
230
|
*/
|
|
181
|
-
export async function
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
// Use provided app token or get one from token service
|
|
187
|
-
const token = options?.appToken ?? await getAppToken(options);
|
|
188
|
-
// Determine base URL
|
|
231
|
+
export async function batchCreateAclRecords(requests, options) {
|
|
232
|
+
if (!Array.isArray(requests) || requests.length === 0) {
|
|
233
|
+
throw new ApiError("requests is required and must be a non-empty array", 400, "9400");
|
|
234
|
+
}
|
|
235
|
+
const token = options?.appToken ?? (await getAppToken(options));
|
|
189
236
|
const env = globalThis?.process?.env ?? import.meta?.env ?? undefined;
|
|
190
237
|
const base = options?.baseUrl ?? env?.TALE_BASE_URL ?? undefined;
|
|
191
238
|
if (!base) {
|
|
192
|
-
throw new ConfigurationError(
|
|
239
|
+
throw new ConfigurationError("Missing required environment variable: TALE_BASE_URL");
|
|
193
240
|
}
|
|
194
|
-
const url = String(base).replace(/\/+$/,
|
|
241
|
+
const url = String(base).replace(/\/+$/, "") + "/acl/v2/records/batch";
|
|
195
242
|
let response;
|
|
196
243
|
try {
|
|
197
244
|
response = await globalThis.fetch(url, {
|
|
198
|
-
method:
|
|
245
|
+
method: "POST",
|
|
199
246
|
headers: {
|
|
200
|
-
|
|
201
|
-
|
|
247
|
+
"Content-Type": "application/json",
|
|
248
|
+
"x-t-token": token,
|
|
202
249
|
},
|
|
250
|
+
body: JSON.stringify(requests),
|
|
203
251
|
});
|
|
204
252
|
}
|
|
205
253
|
catch (error) {
|
|
206
|
-
throw new NetworkError(`Failed to
|
|
254
|
+
throw new NetworkError(`Failed to batch create ACL records: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
207
255
|
}
|
|
208
|
-
|
|
209
|
-
try {
|
|
210
|
-
const responseJson = await response.json();
|
|
211
|
-
json = responseJson;
|
|
212
|
-
}
|
|
213
|
-
catch (error) {
|
|
214
|
-
throw new ApiError(`Failed to parse ACL record deletion response: ${error instanceof Error ? error.message : 'Invalid JSON'}`, response.status);
|
|
215
|
-
}
|
|
216
|
-
// Handle API errors
|
|
256
|
+
const json = (await response.json());
|
|
217
257
|
if (json.code !== 200) {
|
|
218
|
-
const errorMsg = typeof json.msg ===
|
|
258
|
+
const errorMsg = typeof json.msg === "string"
|
|
259
|
+
? json.msg
|
|
260
|
+
: "Failed to batch create ACL records";
|
|
219
261
|
throw new ApiError(errorMsg, response.status, json.code);
|
|
220
262
|
}
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
throw new ApiError('Invalid ACL record deletion response: deletion not confirmed', response.status);
|
|
263
|
+
if (!json.data) {
|
|
264
|
+
throw new ApiError("Invalid ACL batch creation response: missing data", response.status);
|
|
224
265
|
}
|
|
225
266
|
return json.data;
|
|
226
267
|
}
|
|
227
268
|
/**
|
|
228
|
-
*
|
|
269
|
+
* Updates an existing ACL record.
|
|
229
270
|
*
|
|
230
|
-
* @param recordId -
|
|
231
|
-
* @param
|
|
232
|
-
* @
|
|
271
|
+
* @param recordId - Record ID to update
|
|
272
|
+
* @param request - Update request with fields to modify
|
|
273
|
+
* @param options - Optional configuration
|
|
274
|
+
* @returns Promise resolving to updated ACL record
|
|
233
275
|
* @throws {ConfigurationError} When required environment variables are missing
|
|
234
|
-
* @throws {ApiError} When API request fails
|
|
276
|
+
* @throws {ApiError} When API request fails
|
|
235
277
|
* @throws {NetworkError} When network request fails
|
|
278
|
+
*
|
|
279
|
+
* @example
|
|
280
|
+
* ```typescript
|
|
281
|
+
* import { updateAclRecord } from '@tale/client';
|
|
282
|
+
*
|
|
283
|
+
* try {
|
|
284
|
+
* const record = await updateAclRecord('record_id_here', {
|
|
285
|
+
* effect_type: 'deny',
|
|
286
|
+
* priority: 100
|
|
287
|
+
* });
|
|
288
|
+
* console.log('Record updated:', record.record_id);
|
|
289
|
+
* } catch (error) {
|
|
290
|
+
* console.error('Failed to update ACL record:', error.message);
|
|
291
|
+
* }
|
|
292
|
+
* ```
|
|
236
293
|
*/
|
|
237
|
-
export async function
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
// Use provided app token or get one from token service
|
|
243
|
-
const token = options?.appToken ?? await getAppToken(options);
|
|
244
|
-
// Determine base URL
|
|
294
|
+
export async function updateAclRecord(recordId, request, options) {
|
|
295
|
+
if (!recordId || recordId.trim() === "") {
|
|
296
|
+
throw new ApiError("record_id is required", 400, "9400");
|
|
297
|
+
}
|
|
298
|
+
const token = options?.appToken ?? (await getAppToken(options));
|
|
245
299
|
const env = globalThis?.process?.env ?? import.meta?.env ?? undefined;
|
|
246
300
|
const base = options?.baseUrl ?? env?.TALE_BASE_URL ?? undefined;
|
|
247
301
|
if (!base) {
|
|
248
|
-
throw new ConfigurationError(
|
|
302
|
+
throw new ConfigurationError("Missing required environment variable: TALE_BASE_URL");
|
|
249
303
|
}
|
|
250
|
-
const url = String(base).replace(/\/+$/,
|
|
304
|
+
const url = String(base).replace(/\/+$/, "") +
|
|
305
|
+
`/acl/v2/records/${encodeURIComponent(recordId)}`;
|
|
251
306
|
let response;
|
|
252
307
|
try {
|
|
253
308
|
response = await globalThis.fetch(url, {
|
|
254
|
-
method:
|
|
309
|
+
method: "PUT",
|
|
255
310
|
headers: {
|
|
256
|
-
|
|
257
|
-
|
|
311
|
+
"Content-Type": "application/json",
|
|
312
|
+
"x-t-token": token,
|
|
258
313
|
},
|
|
314
|
+
body: JSON.stringify(request),
|
|
259
315
|
});
|
|
260
316
|
}
|
|
261
317
|
catch (error) {
|
|
262
|
-
throw new NetworkError(`Failed to
|
|
263
|
-
}
|
|
264
|
-
let json;
|
|
265
|
-
try {
|
|
266
|
-
const responseJson = await response.json();
|
|
267
|
-
json = responseJson;
|
|
318
|
+
throw new NetworkError(`Failed to update ACL record: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
268
319
|
}
|
|
269
|
-
|
|
270
|
-
throw new ApiError(`Failed to parse ACL record response: ${error instanceof Error ? error.message : 'Invalid JSON'}`, response.status);
|
|
271
|
-
}
|
|
272
|
-
// Handle API errors
|
|
320
|
+
const json = (await response.json());
|
|
273
321
|
if (json.code !== 200) {
|
|
274
|
-
const errorMsg = typeof json.msg ===
|
|
322
|
+
const errorMsg = typeof json.msg === "string" ? json.msg : "Failed to update ACL record";
|
|
275
323
|
throw new ApiError(errorMsg, response.status, json.code);
|
|
276
324
|
}
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
throw new ApiError('Invalid ACL record response: missing record data', response.status);
|
|
325
|
+
if (!json.data) {
|
|
326
|
+
throw new ApiError("Invalid ACL record update response: missing data", response.status);
|
|
280
327
|
}
|
|
281
328
|
return json.data;
|
|
282
329
|
}
|
|
283
330
|
/**
|
|
284
|
-
*
|
|
331
|
+
* Deletes an ACL record.
|
|
285
332
|
*
|
|
286
|
-
* @param
|
|
287
|
-
* @
|
|
333
|
+
* @param recordId - Record ID to delete
|
|
334
|
+
* @param options - Optional configuration
|
|
335
|
+
* @returns Promise that resolves when deletion is successful
|
|
288
336
|
* @throws {ConfigurationError} When required environment variables are missing
|
|
289
|
-
* @throws {ApiError} When API request fails
|
|
337
|
+
* @throws {ApiError} When API request fails
|
|
290
338
|
* @throws {NetworkError} When network request fails
|
|
339
|
+
*
|
|
340
|
+
* @example
|
|
341
|
+
* ```typescript
|
|
342
|
+
* import { deleteAclRecord } from '@tale/client';
|
|
343
|
+
*
|
|
344
|
+
* try {
|
|
345
|
+
* await deleteAclRecord('record_id_here');
|
|
346
|
+
* console.log('ACL record deleted successfully');
|
|
347
|
+
* } catch (error) {
|
|
348
|
+
* console.error('Failed to delete ACL record:', error.message);
|
|
349
|
+
* }
|
|
350
|
+
* ```
|
|
291
351
|
*/
|
|
292
|
-
export async function
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
352
|
+
export async function deleteAclRecord(recordId, options) {
|
|
353
|
+
if (!recordId || recordId.trim() === "") {
|
|
354
|
+
throw new ApiError("record_id is required", 400, "9400");
|
|
355
|
+
}
|
|
356
|
+
const token = options?.appToken ?? (await getAppToken(options));
|
|
296
357
|
const env = globalThis?.process?.env ?? import.meta?.env ?? undefined;
|
|
297
358
|
const base = options?.baseUrl ?? env?.TALE_BASE_URL ?? undefined;
|
|
298
359
|
if (!base) {
|
|
299
|
-
throw new ConfigurationError(
|
|
300
|
-
}
|
|
301
|
-
// Build URL with query parameters
|
|
302
|
-
const url = new URL(String(base).replace(/\/+$/, '') + '/acl/v1/records');
|
|
303
|
-
// Add query parameters with defaults
|
|
304
|
-
const queryParams = {
|
|
305
|
-
page: 0,
|
|
306
|
-
size: 20,
|
|
307
|
-
sort_by: 'priority',
|
|
308
|
-
sort_direction: 'desc',
|
|
309
|
-
...options
|
|
310
|
-
};
|
|
311
|
-
// Add parameters to URL
|
|
312
|
-
if (queryParams.template_id) {
|
|
313
|
-
url.searchParams.append('template_id', queryParams.template_id);
|
|
314
|
-
}
|
|
315
|
-
if (queryParams.subject_type) {
|
|
316
|
-
url.searchParams.append('subject_type', queryParams.subject_type);
|
|
317
|
-
}
|
|
318
|
-
if (queryParams.subject_id) {
|
|
319
|
-
url.searchParams.append('subject_id', queryParams.subject_id);
|
|
320
|
-
}
|
|
321
|
-
if (queryParams.subject_identifier) {
|
|
322
|
-
url.searchParams.append('subject_identifier', queryParams.subject_identifier);
|
|
323
|
-
}
|
|
324
|
-
if (queryParams.resource_type) {
|
|
325
|
-
url.searchParams.append('resource_type', queryParams.resource_type);
|
|
326
|
-
}
|
|
327
|
-
if (queryParams.resource_id) {
|
|
328
|
-
url.searchParams.append('resource_id', queryParams.resource_id);
|
|
329
|
-
}
|
|
330
|
-
if (queryParams.resource_identifier) {
|
|
331
|
-
url.searchParams.append('resource_identifier', queryParams.resource_identifier);
|
|
332
|
-
}
|
|
333
|
-
if (queryParams.page !== undefined) {
|
|
334
|
-
url.searchParams.append('page', String(queryParams.page));
|
|
335
|
-
}
|
|
336
|
-
if (queryParams.size !== undefined) {
|
|
337
|
-
url.searchParams.append('size', String(queryParams.size));
|
|
338
|
-
}
|
|
339
|
-
if (queryParams.sort_by) {
|
|
340
|
-
url.searchParams.append('sort', `${queryParams.sort_by},${queryParams.sort_direction || 'desc'}`);
|
|
360
|
+
throw new ConfigurationError("Missing required environment variable: TALE_BASE_URL");
|
|
341
361
|
}
|
|
362
|
+
const url = String(base).replace(/\/+$/, "") +
|
|
363
|
+
`/acl/v2/records/${encodeURIComponent(recordId)}`;
|
|
342
364
|
let response;
|
|
343
365
|
try {
|
|
344
|
-
response = await globalThis.fetch(url
|
|
345
|
-
method:
|
|
366
|
+
response = await globalThis.fetch(url, {
|
|
367
|
+
method: "DELETE",
|
|
346
368
|
headers: {
|
|
347
|
-
|
|
348
|
-
|
|
369
|
+
"Content-Type": "application/json",
|
|
370
|
+
"x-t-token": token,
|
|
349
371
|
},
|
|
350
372
|
});
|
|
351
373
|
}
|
|
352
374
|
catch (error) {
|
|
353
|
-
throw new NetworkError(`Failed to
|
|
354
|
-
}
|
|
355
|
-
let json;
|
|
356
|
-
try {
|
|
357
|
-
const responseJson = await response.json();
|
|
358
|
-
json = responseJson;
|
|
359
|
-
}
|
|
360
|
-
catch (error) {
|
|
361
|
-
throw new ApiError(`Failed to parse ACL records list response: ${error instanceof Error ? error.message : 'Invalid JSON'}`, response.status);
|
|
375
|
+
throw new NetworkError(`Failed to delete ACL record: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
362
376
|
}
|
|
363
|
-
|
|
377
|
+
const json = (await response.json());
|
|
364
378
|
if (json.code !== 200) {
|
|
365
|
-
const errorMsg = typeof json.msg ===
|
|
379
|
+
const errorMsg = typeof json.msg === "string" ? json.msg : "Failed to delete ACL record";
|
|
366
380
|
throw new ApiError(errorMsg, response.status, json.code);
|
|
367
381
|
}
|
|
368
|
-
// Validate response structure
|
|
369
|
-
if (!json.data || !Array.isArray(json.data.content)) {
|
|
370
|
-
throw new ApiError('Invalid ACL records list response: missing required data', response.status);
|
|
371
|
-
}
|
|
372
|
-
return json.data;
|
|
373
382
|
}
|
|
374
|
-
//
|
|
383
|
+
// ==================== ACL Template Management ====================
|
|
375
384
|
/**
|
|
376
|
-
*
|
|
385
|
+
* Gets an ACL template by ID or code.
|
|
377
386
|
*
|
|
378
|
-
* @param
|
|
379
|
-
* @param options - Optional configuration
|
|
380
|
-
* @returns Promise resolving to
|
|
387
|
+
* @param templateId - Template ID or code
|
|
388
|
+
* @param options - Optional configuration
|
|
389
|
+
* @returns Promise resolving to ACL template information
|
|
381
390
|
* @throws {ConfigurationError} When required environment variables are missing
|
|
382
|
-
* @throws {ApiError} When API request fails
|
|
391
|
+
* @throws {ApiError} When API request fails
|
|
383
392
|
* @throws {NetworkError} When network request fails
|
|
384
393
|
*
|
|
385
394
|
* @example
|
|
386
395
|
* ```typescript
|
|
387
|
-
* import {
|
|
396
|
+
* import { getAclTemplate } from '@tale/client';
|
|
388
397
|
*
|
|
389
398
|
* try {
|
|
390
|
-
* const
|
|
391
|
-
*
|
|
392
|
-
* template_code: 'user_doc_read',
|
|
393
|
-
* subject_type: 'user',
|
|
394
|
-
* resource_type: 'document',
|
|
395
|
-
* effect_type: 'allow',
|
|
396
|
-
* default_priority: 30,
|
|
397
|
-
* description: 'Template for allowing users to read documents'
|
|
398
|
-
* });
|
|
399
|
-
* console.log('ACL template created:', result.template.template_id);
|
|
399
|
+
* const template = await getAclTemplate('template_code_here');
|
|
400
|
+
* console.log('Template name:', template.template_name);
|
|
400
401
|
* } catch (error) {
|
|
401
|
-
* console.error('Failed to
|
|
402
|
+
* console.error('Failed to get ACL template:', error.message);
|
|
402
403
|
* }
|
|
403
404
|
* ```
|
|
404
405
|
*/
|
|
405
|
-
export async function
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
throw new ApiError('template_name is required for ACL template creation', 400, '9400');
|
|
406
|
+
export async function getAclTemplate(templateId, options) {
|
|
407
|
+
if (!templateId || templateId.trim() === "") {
|
|
408
|
+
throw new ApiError("template_id is required", 400, "9400");
|
|
409
409
|
}
|
|
410
|
-
|
|
411
|
-
throw new ApiError('template_code is required for ACL template creation', 400, '9400');
|
|
412
|
-
}
|
|
413
|
-
if (templateData.effect_type && !['allow', 'deny', 'inherit'].includes(templateData.effect_type)) {
|
|
414
|
-
throw new ApiError('effect_type must be one of: allow, deny, inherit', 400, '9400');
|
|
415
|
-
}
|
|
416
|
-
// Validate default priority range
|
|
417
|
-
if (templateData.default_priority !== undefined && (templateData.default_priority < 0 || templateData.default_priority > 100)) {
|
|
418
|
-
throw new ApiError('default_priority must be between 0 and 100', 400, '9400');
|
|
419
|
-
}
|
|
420
|
-
// Use provided app token or get one from token service
|
|
421
|
-
const token = options?.appToken ?? await getAppToken(options);
|
|
422
|
-
// Determine base URL
|
|
410
|
+
const token = options?.appToken ?? (await getAppToken(options));
|
|
423
411
|
const env = globalThis?.process?.env ?? import.meta?.env ?? undefined;
|
|
424
412
|
const base = options?.baseUrl ?? env?.TALE_BASE_URL ?? undefined;
|
|
425
413
|
if (!base) {
|
|
426
|
-
throw new ConfigurationError(
|
|
427
|
-
}
|
|
428
|
-
const url = String(base).replace(/\/+$/,
|
|
429
|
-
|
|
430
|
-
const finalTemplateData = {
|
|
431
|
-
...templateData,
|
|
432
|
-
default_priority: templateData.default_priority ?? 50,
|
|
433
|
-
effect_type: templateData.effect_type ?? 'allow'
|
|
434
|
-
};
|
|
414
|
+
throw new ConfigurationError("Missing required environment variable: TALE_BASE_URL");
|
|
415
|
+
}
|
|
416
|
+
const url = String(base).replace(/\/+$/, "") +
|
|
417
|
+
`/acl/v1/templates/${encodeURIComponent(templateId)}`;
|
|
435
418
|
let response;
|
|
436
419
|
try {
|
|
437
420
|
response = await globalThis.fetch(url, {
|
|
438
|
-
method:
|
|
421
|
+
method: "GET",
|
|
439
422
|
headers: {
|
|
440
|
-
|
|
441
|
-
|
|
423
|
+
"Content-Type": "application/json",
|
|
424
|
+
"x-t-token": token,
|
|
442
425
|
},
|
|
443
|
-
body: JSON.stringify(finalTemplateData),
|
|
444
426
|
});
|
|
445
427
|
}
|
|
446
428
|
catch (error) {
|
|
447
|
-
throw new NetworkError(`Failed to
|
|
448
|
-
}
|
|
449
|
-
let json;
|
|
450
|
-
try {
|
|
451
|
-
const responseJson = await response.json();
|
|
452
|
-
json = responseJson;
|
|
429
|
+
throw new NetworkError(`Failed to get ACL template: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
453
430
|
}
|
|
454
|
-
|
|
455
|
-
throw new ApiError(`Failed to parse ACL template creation response: ${error instanceof Error ? error.message : 'Invalid JSON'}`, response.status);
|
|
456
|
-
}
|
|
457
|
-
// Handle API errors
|
|
431
|
+
const json = (await response.json());
|
|
458
432
|
if (json.code !== 200) {
|
|
459
|
-
const errorMsg = typeof json.msg ===
|
|
433
|
+
const errorMsg = typeof json.msg === "string" ? json.msg : "Failed to get ACL template";
|
|
460
434
|
throw new ApiError(errorMsg, response.status, json.code);
|
|
461
435
|
}
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
throw new ApiError('Invalid ACL template creation response: missing template data', response.status);
|
|
436
|
+
if (!json.data) {
|
|
437
|
+
throw new ApiError("Invalid ACL template response: missing data", response.status);
|
|
465
438
|
}
|
|
466
439
|
return json.data;
|
|
467
440
|
}
|
|
468
441
|
/**
|
|
469
|
-
*
|
|
442
|
+
* Lists ACL templates with pagination and optional filtering.
|
|
470
443
|
*
|
|
471
|
-
* @param
|
|
472
|
-
* @
|
|
473
|
-
* @param options - Optional configuration for the request
|
|
474
|
-
* @returns Promise resolving to the updated ACL template information
|
|
444
|
+
* @param options - Optional parameters for pagination and filtering
|
|
445
|
+
* @returns Promise resolving to paginated ACL template list
|
|
475
446
|
* @throws {ConfigurationError} When required environment variables are missing
|
|
476
|
-
* @throws {ApiError} When API request fails
|
|
447
|
+
* @throws {ApiError} When API request fails
|
|
477
448
|
* @throws {NetworkError} When network request fails
|
|
449
|
+
*
|
|
450
|
+
* @example
|
|
451
|
+
* ```typescript
|
|
452
|
+
* import { listAclTemplates } from '@tale/client';
|
|
453
|
+
*
|
|
454
|
+
* try {
|
|
455
|
+
* const result = await listAclTemplates({
|
|
456
|
+
* page: 0,
|
|
457
|
+
* size: 20,
|
|
458
|
+
* resource_type: 'document'
|
|
459
|
+
* });
|
|
460
|
+
* console.log(`Found ${result.totalElements} templates`);
|
|
461
|
+
* } catch (error) {
|
|
462
|
+
* console.error('Failed to list ACL templates:', error.message);
|
|
463
|
+
* }
|
|
464
|
+
* ```
|
|
478
465
|
*/
|
|
479
|
-
export async function
|
|
480
|
-
|
|
481
|
-
if (!templateId || templateId.trim() === '') {
|
|
482
|
-
throw new ApiError('template_id is required for ACL template update', 400, '9400');
|
|
483
|
-
}
|
|
484
|
-
if (updateData.effect_type && !['allow', 'deny', 'inherit'].includes(updateData.effect_type)) {
|
|
485
|
-
throw new ApiError('effect_type must be one of: allow, deny, inherit', 400, '9400');
|
|
486
|
-
}
|
|
487
|
-
// Validate default priority range
|
|
488
|
-
if (updateData.default_priority !== undefined && (updateData.default_priority < 0 || updateData.default_priority > 100)) {
|
|
489
|
-
throw new ApiError('default_priority must be between 0 and 100', 400, '9400');
|
|
490
|
-
}
|
|
491
|
-
// Use provided app token or get one from token service
|
|
492
|
-
const token = options?.appToken ?? await getAppToken(options);
|
|
493
|
-
// Determine base URL
|
|
466
|
+
export async function listAclTemplates(options) {
|
|
467
|
+
const token = options?.appToken ?? (await getAppToken(options));
|
|
494
468
|
const env = globalThis?.process?.env ?? import.meta?.env ?? undefined;
|
|
495
469
|
const base = options?.baseUrl ?? env?.TALE_BASE_URL ?? undefined;
|
|
496
470
|
if (!base) {
|
|
497
|
-
throw new ConfigurationError(
|
|
471
|
+
throw new ConfigurationError("Missing required environment variable: TALE_BASE_URL");
|
|
498
472
|
}
|
|
499
|
-
const url = String(base).replace(/\/+$/,
|
|
473
|
+
const url = new URL(String(base).replace(/\/+$/, "") + "/acl/v1/templates");
|
|
474
|
+
const queryParams = {
|
|
475
|
+
page: 0,
|
|
476
|
+
size: 20,
|
|
477
|
+
sort_by: "createdAt",
|
|
478
|
+
sort_direction: "desc",
|
|
479
|
+
...options,
|
|
480
|
+
};
|
|
481
|
+
Object.entries(queryParams).forEach(([key, value]) => {
|
|
482
|
+
if (value !== undefined) {
|
|
483
|
+
url.searchParams.append(key === "sortBy" ? "sort_by" : key === "sortDirection" ? "sort_direction" : key, String(value));
|
|
484
|
+
}
|
|
485
|
+
});
|
|
500
486
|
let response;
|
|
501
487
|
try {
|
|
502
|
-
response = await globalThis.fetch(url, {
|
|
503
|
-
method:
|
|
488
|
+
response = await globalThis.fetch(url.toString(), {
|
|
489
|
+
method: "GET",
|
|
504
490
|
headers: {
|
|
505
|
-
|
|
506
|
-
|
|
491
|
+
"Content-Type": "application/json",
|
|
492
|
+
"x-t-token": token,
|
|
507
493
|
},
|
|
508
|
-
body: JSON.stringify(updateData),
|
|
509
494
|
});
|
|
510
495
|
}
|
|
511
496
|
catch (error) {
|
|
512
|
-
throw new NetworkError(`Failed to
|
|
497
|
+
throw new NetworkError(`Failed to list ACL templates: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
513
498
|
}
|
|
514
|
-
|
|
515
|
-
try {
|
|
516
|
-
const responseJson = await response.json();
|
|
517
|
-
json = responseJson;
|
|
518
|
-
}
|
|
519
|
-
catch (error) {
|
|
520
|
-
throw new ApiError(`Failed to parse ACL template update response: ${error instanceof Error ? error.message : 'Invalid JSON'}`, response.status);
|
|
521
|
-
}
|
|
522
|
-
// Handle API errors
|
|
499
|
+
const json = (await response.json());
|
|
523
500
|
if (json.code !== 200) {
|
|
524
|
-
const errorMsg = typeof json.msg ===
|
|
501
|
+
const errorMsg = typeof json.msg === "string" ? json.msg : "Failed to list ACL templates";
|
|
525
502
|
throw new ApiError(errorMsg, response.status, json.code);
|
|
526
503
|
}
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
throw new ApiError('Invalid ACL template update response: missing template data', response.status);
|
|
504
|
+
if (!json.data || !Array.isArray(json.data.content)) {
|
|
505
|
+
throw new ApiError("Invalid ACL templates response: missing data", response.status);
|
|
530
506
|
}
|
|
531
507
|
return json.data;
|
|
532
508
|
}
|
|
533
509
|
/**
|
|
534
|
-
*
|
|
510
|
+
* Creates a new ACL template.
|
|
535
511
|
*
|
|
536
|
-
* @param
|
|
537
|
-
* @param options - Optional configuration
|
|
538
|
-
* @returns Promise resolving to
|
|
512
|
+
* @param request - ACL template creation request
|
|
513
|
+
* @param options - Optional configuration
|
|
514
|
+
* @returns Promise resolving to created ACL template
|
|
539
515
|
* @throws {ConfigurationError} When required environment variables are missing
|
|
540
|
-
* @throws {ApiError} When API request fails
|
|
516
|
+
* @throws {ApiError} When API request fails
|
|
541
517
|
* @throws {NetworkError} When network request fails
|
|
518
|
+
*
|
|
519
|
+
* @example
|
|
520
|
+
* ```typescript
|
|
521
|
+
* import { createAclTemplate } from '@tale/client';
|
|
522
|
+
*
|
|
523
|
+
* try {
|
|
524
|
+
* const template = await createAclTemplate({
|
|
525
|
+
* template_name: 'Document Access',
|
|
526
|
+
* template_code: 'doc_access',
|
|
527
|
+
* subject_type: 'user',
|
|
528
|
+
* resource_type: 'document',
|
|
529
|
+
* effect_type: 'allow'
|
|
530
|
+
* });
|
|
531
|
+
* console.log('Template created:', template.template_id);
|
|
532
|
+
* } catch (error) {
|
|
533
|
+
* console.error('Failed to create ACL template:', error.message);
|
|
534
|
+
* }
|
|
535
|
+
* ```
|
|
542
536
|
*/
|
|
543
|
-
export async function
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
537
|
+
export async function createAclTemplate(request, options) {
|
|
538
|
+
if (!request.template_name || request.template_name.trim() === "") {
|
|
539
|
+
throw new ApiError("template_name is required", 400, "9400");
|
|
540
|
+
}
|
|
541
|
+
if (!request.template_code || request.template_code.trim() === "") {
|
|
542
|
+
throw new ApiError("template_code is required", 400, "9400");
|
|
543
|
+
}
|
|
544
|
+
const token = options?.appToken ?? (await getAppToken(options));
|
|
551
545
|
const env = globalThis?.process?.env ?? import.meta?.env ?? undefined;
|
|
552
546
|
const base = options?.baseUrl ?? env?.TALE_BASE_URL ?? undefined;
|
|
553
547
|
if (!base) {
|
|
554
|
-
throw new ConfigurationError(
|
|
548
|
+
throw new ConfigurationError("Missing required environment variable: TALE_BASE_URL");
|
|
555
549
|
}
|
|
556
|
-
const url = String(base).replace(/\/+$/,
|
|
550
|
+
const url = String(base).replace(/\/+$/, "") + "/acl/v1/templates";
|
|
557
551
|
let response;
|
|
558
552
|
try {
|
|
559
553
|
response = await globalThis.fetch(url, {
|
|
560
|
-
method:
|
|
554
|
+
method: "POST",
|
|
561
555
|
headers: {
|
|
562
|
-
|
|
563
|
-
|
|
556
|
+
"Content-Type": "application/json",
|
|
557
|
+
"x-t-token": token,
|
|
564
558
|
},
|
|
559
|
+
body: JSON.stringify(request),
|
|
565
560
|
});
|
|
566
561
|
}
|
|
567
562
|
catch (error) {
|
|
568
|
-
throw new NetworkError(`Failed to
|
|
569
|
-
}
|
|
570
|
-
let json;
|
|
571
|
-
try {
|
|
572
|
-
const responseJson = await response.json();
|
|
573
|
-
json = responseJson;
|
|
574
|
-
}
|
|
575
|
-
catch (error) {
|
|
576
|
-
throw new ApiError(`Failed to parse ACL template deletion response: ${error instanceof Error ? error.message : 'Invalid JSON'}`, response.status);
|
|
563
|
+
throw new NetworkError(`Failed to create ACL template: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
577
564
|
}
|
|
578
|
-
|
|
565
|
+
const json = (await response.json());
|
|
579
566
|
if (json.code !== 200) {
|
|
580
|
-
const errorMsg = typeof json.msg ===
|
|
567
|
+
const errorMsg = typeof json.msg === "string" ? json.msg : "Failed to create ACL template";
|
|
581
568
|
throw new ApiError(errorMsg, response.status, json.code);
|
|
582
569
|
}
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
throw new ApiError('Invalid ACL template deletion response: deletion not confirmed', response.status);
|
|
570
|
+
if (!json.data) {
|
|
571
|
+
throw new ApiError("Invalid ACL template creation response: missing data", response.status);
|
|
586
572
|
}
|
|
587
573
|
return json.data;
|
|
588
574
|
}
|
|
589
575
|
/**
|
|
590
|
-
*
|
|
576
|
+
* Updates an existing ACL template.
|
|
591
577
|
*
|
|
592
|
-
* @param templateId -
|
|
593
|
-
* @param
|
|
594
|
-
* @
|
|
578
|
+
* @param templateId - Template ID or code to update
|
|
579
|
+
* @param request - Update request with fields to modify
|
|
580
|
+
* @param options - Optional configuration
|
|
581
|
+
* @returns Promise resolving to updated ACL template
|
|
595
582
|
* @throws {ConfigurationError} When required environment variables are missing
|
|
596
|
-
* @throws {ApiError} When API request fails
|
|
583
|
+
* @throws {ApiError} When API request fails
|
|
597
584
|
* @throws {NetworkError} When network request fails
|
|
585
|
+
*
|
|
586
|
+
* @example
|
|
587
|
+
* ```typescript
|
|
588
|
+
* import { updateAclTemplate } from '@tale/client';
|
|
589
|
+
*
|
|
590
|
+
* try {
|
|
591
|
+
* const template = await updateAclTemplate('template_code_here', {
|
|
592
|
+
* template_name: 'Updated Template Name',
|
|
593
|
+
* effect_type: 'deny'
|
|
594
|
+
* });
|
|
595
|
+
* console.log('Template updated:', template.template_id);
|
|
596
|
+
* } catch (error) {
|
|
597
|
+
* console.error('Failed to update ACL template:', error.message);
|
|
598
|
+
* }
|
|
599
|
+
* ```
|
|
598
600
|
*/
|
|
599
|
-
export async function
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
// Use provided app token or get one from token service
|
|
605
|
-
const token = options?.appToken ?? await getAppToken(options);
|
|
606
|
-
// Determine base URL
|
|
601
|
+
export async function updateAclTemplate(templateId, request, options) {
|
|
602
|
+
if (!templateId || templateId.trim() === "") {
|
|
603
|
+
throw new ApiError("template_id is required", 400, "9400");
|
|
604
|
+
}
|
|
605
|
+
const token = options?.appToken ?? (await getAppToken(options));
|
|
607
606
|
const env = globalThis?.process?.env ?? import.meta?.env ?? undefined;
|
|
608
607
|
const base = options?.baseUrl ?? env?.TALE_BASE_URL ?? undefined;
|
|
609
608
|
if (!base) {
|
|
610
|
-
throw new ConfigurationError(
|
|
609
|
+
throw new ConfigurationError("Missing required environment variable: TALE_BASE_URL");
|
|
611
610
|
}
|
|
612
|
-
const url = String(base).replace(/\/+$/,
|
|
611
|
+
const url = String(base).replace(/\/+$/, "") +
|
|
612
|
+
`/acl/v1/templates/${encodeURIComponent(templateId)}`;
|
|
613
613
|
let response;
|
|
614
614
|
try {
|
|
615
615
|
response = await globalThis.fetch(url, {
|
|
616
|
-
method:
|
|
616
|
+
method: "PUT",
|
|
617
617
|
headers: {
|
|
618
|
-
|
|
619
|
-
|
|
618
|
+
"Content-Type": "application/json",
|
|
619
|
+
"x-t-token": token,
|
|
620
620
|
},
|
|
621
|
+
body: JSON.stringify(request),
|
|
621
622
|
});
|
|
622
623
|
}
|
|
623
624
|
catch (error) {
|
|
624
|
-
throw new NetworkError(`Failed to
|
|
625
|
+
throw new NetworkError(`Failed to update ACL template: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
625
626
|
}
|
|
626
|
-
|
|
627
|
-
try {
|
|
628
|
-
const responseJson = await response.json();
|
|
629
|
-
json = responseJson;
|
|
630
|
-
}
|
|
631
|
-
catch (error) {
|
|
632
|
-
throw new ApiError(`Failed to parse ACL template response: ${error instanceof Error ? error.message : 'Invalid JSON'}`, response.status);
|
|
633
|
-
}
|
|
634
|
-
// Handle API errors
|
|
627
|
+
const json = (await response.json());
|
|
635
628
|
if (json.code !== 200) {
|
|
636
|
-
const errorMsg = typeof json.msg ===
|
|
629
|
+
const errorMsg = typeof json.msg === "string" ? json.msg : "Failed to update ACL template";
|
|
637
630
|
throw new ApiError(errorMsg, response.status, json.code);
|
|
638
631
|
}
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
throw new ApiError('Invalid ACL template response: missing template data', response.status);
|
|
632
|
+
if (!json.data) {
|
|
633
|
+
throw new ApiError("Invalid ACL template update response: missing data", response.status);
|
|
642
634
|
}
|
|
643
635
|
return json.data;
|
|
644
636
|
}
|
|
645
637
|
/**
|
|
646
|
-
*
|
|
638
|
+
* Deletes an ACL template.
|
|
647
639
|
*
|
|
648
|
-
* @param
|
|
649
|
-
* @
|
|
640
|
+
* @param templateId - Template ID or code to delete
|
|
641
|
+
* @param options - Optional configuration
|
|
642
|
+
* @returns Promise that resolves when deletion is successful
|
|
650
643
|
* @throws {ConfigurationError} When required environment variables are missing
|
|
651
|
-
* @throws {ApiError} When API request fails
|
|
644
|
+
* @throws {ApiError} When API request fails
|
|
652
645
|
* @throws {NetworkError} When network request fails
|
|
646
|
+
*
|
|
647
|
+
* @example
|
|
648
|
+
* ```typescript
|
|
649
|
+
* import { deleteAclTemplate } from '@tale/client';
|
|
650
|
+
*
|
|
651
|
+
* try {
|
|
652
|
+
* await deleteAclTemplate('template_code_here');
|
|
653
|
+
* console.log('ACL template deleted successfully');
|
|
654
|
+
* } catch (error) {
|
|
655
|
+
* console.error('Failed to delete ACL template:', error.message);
|
|
656
|
+
* }
|
|
657
|
+
* ```
|
|
653
658
|
*/
|
|
654
|
-
export async function
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
659
|
+
export async function deleteAclTemplate(templateId, options) {
|
|
660
|
+
if (!templateId || templateId.trim() === "") {
|
|
661
|
+
throw new ApiError("template_id is required", 400, "9400");
|
|
662
|
+
}
|
|
663
|
+
const token = options?.appToken ?? (await getAppToken(options));
|
|
658
664
|
const env = globalThis?.process?.env ?? import.meta?.env ?? undefined;
|
|
659
665
|
const base = options?.baseUrl ?? env?.TALE_BASE_URL ?? undefined;
|
|
660
666
|
if (!base) {
|
|
661
|
-
throw new ConfigurationError(
|
|
662
|
-
}
|
|
663
|
-
// Build URL with query parameters
|
|
664
|
-
const url = new URL(String(base).replace(/\/+$/, '') + '/acl/v1/templates');
|
|
665
|
-
// Add query parameters with defaults
|
|
666
|
-
const queryParams = {
|
|
667
|
-
page: 0,
|
|
668
|
-
size: 20,
|
|
669
|
-
sort_by: 'created_at',
|
|
670
|
-
sort_direction: 'desc',
|
|
671
|
-
...options
|
|
672
|
-
};
|
|
673
|
-
// Add parameters to URL
|
|
674
|
-
if (queryParams.resource_type) {
|
|
675
|
-
url.searchParams.append('resource_type', queryParams.resource_type);
|
|
676
|
-
}
|
|
677
|
-
if (queryParams.subject_type) {
|
|
678
|
-
url.searchParams.append('subject_type', queryParams.subject_type);
|
|
679
|
-
}
|
|
680
|
-
if (queryParams.page !== undefined) {
|
|
681
|
-
url.searchParams.append('page', String(queryParams.page));
|
|
682
|
-
}
|
|
683
|
-
if (queryParams.size !== undefined) {
|
|
684
|
-
url.searchParams.append('size', String(queryParams.size));
|
|
685
|
-
}
|
|
686
|
-
if (queryParams.sort_by) {
|
|
687
|
-
url.searchParams.append('sort_by', queryParams.sort_by);
|
|
688
|
-
}
|
|
689
|
-
if (queryParams.sort_direction) {
|
|
690
|
-
url.searchParams.append('sort_direction', queryParams.sort_direction);
|
|
667
|
+
throw new ConfigurationError("Missing required environment variable: TALE_BASE_URL");
|
|
691
668
|
}
|
|
669
|
+
const url = String(base).replace(/\/+$/, "") +
|
|
670
|
+
`/acl/v1/templates/${encodeURIComponent(templateId)}`;
|
|
692
671
|
let response;
|
|
693
672
|
try {
|
|
694
|
-
response = await globalThis.fetch(url
|
|
695
|
-
method:
|
|
673
|
+
response = await globalThis.fetch(url, {
|
|
674
|
+
method: "DELETE",
|
|
696
675
|
headers: {
|
|
697
|
-
|
|
698
|
-
|
|
676
|
+
"Content-Type": "application/json",
|
|
677
|
+
"x-t-token": token,
|
|
699
678
|
},
|
|
700
679
|
});
|
|
701
680
|
}
|
|
702
681
|
catch (error) {
|
|
703
|
-
throw new NetworkError(`Failed to
|
|
682
|
+
throw new NetworkError(`Failed to delete ACL template: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
704
683
|
}
|
|
705
|
-
|
|
706
|
-
try {
|
|
707
|
-
const responseJson = await response.json();
|
|
708
|
-
json = responseJson;
|
|
709
|
-
}
|
|
710
|
-
catch (error) {
|
|
711
|
-
throw new ApiError(`Failed to parse ACL templates list response: ${error instanceof Error ? error.message : 'Invalid JSON'}`, response.status);
|
|
712
|
-
}
|
|
713
|
-
// Handle API errors
|
|
684
|
+
const json = (await response.json());
|
|
714
685
|
if (json.code !== 200) {
|
|
715
|
-
const errorMsg = typeof json.msg ===
|
|
686
|
+
const errorMsg = typeof json.msg === "string" ? json.msg : "Failed to delete ACL template";
|
|
716
687
|
throw new ApiError(errorMsg, response.status, json.code);
|
|
717
688
|
}
|
|
718
|
-
// Validate response structure
|
|
719
|
-
if (!json.data || !Array.isArray(json.data.content)) {
|
|
720
|
-
throw new ApiError('Invalid ACL templates list response: missing required data', response.status);
|
|
721
|
-
}
|
|
722
|
-
return json.data;
|
|
723
689
|
}
|
|
724
|
-
// ===== ACL 核心工具函数 (AclCoreUtils) =====
|
|
725
|
-
/**
|
|
726
|
-
* ACL 核心工具函数,包含跨应用通用的业务逻辑
|
|
727
|
-
*/
|
|
728
|
-
export const AclCoreUtils = {
|
|
729
|
-
/**
|
|
730
|
-
* 验证优先级范围
|
|
731
|
-
*/
|
|
732
|
-
validatePriority(priority) {
|
|
733
|
-
return priority >= 0 && priority <= 100;
|
|
734
|
-
},
|
|
735
|
-
/**
|
|
736
|
-
* 获取可用的效果类型
|
|
737
|
-
*/
|
|
738
|
-
getEffectTypes() {
|
|
739
|
-
return ['allow', 'deny', 'inherit'];
|
|
740
|
-
},
|
|
741
|
-
/**
|
|
742
|
-
* 获取可用的主体类型
|
|
743
|
-
*/
|
|
744
|
-
getSubjectTypes() {
|
|
745
|
-
return ['user', 'role', 'group'];
|
|
746
|
-
}
|
|
747
|
-
};
|