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