@taruvi/sdk 1.3.2 → 1.3.4-beta.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/.kiro/settings/lsp.json +198 -0
- package/MODULE_NAMING_CHANGES.md +81 -0
- package/PARAMETER_NAMING_CHANGES.md +106 -0
- package/README.md +4 -4
- package/package.json +10 -4
- package/src/client.ts +4 -4
- package/src/index.ts +48 -35
- package/src/lib/analytics/types.ts +8 -0
- package/src/lib/{App → app}/types.ts +14 -6
- package/src/lib/auth/AuthClient.ts +8 -7
- package/src/lib/auth/types.ts +108 -6
- package/src/lib/{Database → database}/DatabaseClient.ts +15 -10
- package/src/lib/{Database → database}/types.ts +4 -7
- package/src/lib/functions/types.ts +25 -0
- package/src/lib/{Graphs → graphs}/GraphClient.ts +9 -8
- package/src/lib/{Graphs → graphs}/types.ts +12 -5
- package/src/lib/policy/PolicyClient.ts +79 -0
- package/src/lib/policy/types.ts +40 -0
- package/src/lib/secrets/SecretsClient.ts +75 -0
- package/src/lib/secrets/types.ts +59 -0
- package/src/lib/settings/types.ts +9 -0
- package/src/lib/{Storage → storage}/StorageClient.ts +27 -9
- package/src/lib/storage/types.ts +86 -0
- package/src/lib/users/UserClient.ts +55 -0
- package/src/lib/users/types.ts +104 -0
- package/src/lib-internal/errors/ErrorClient.ts +98 -8
- package/src/lib-internal/errors/index.ts +3 -25
- package/src/lib-internal/errors/types.ts +18 -95
- package/src/lib-internal/http/HttpClient.ts +83 -45
- package/src/lib-internal/routes/UserRoutes.ts +3 -1
- package/src/lib-internal/token/TokenClient.ts +1 -1
- package/src/types.ts +20 -1
- package/src/utils/utils.ts +5 -1
- package/tests/fixtures/mockClient.ts +19 -0
- package/tests/unit/analytics/AnalyticsClient.test.ts +84 -0
- package/tests/unit/app/AppClient.test.ts +114 -0
- package/tests/unit/auth/AuthClient.test.ts +131 -0
- package/tests/unit/client/Client.test.ts +70 -0
- package/tests/unit/database/DatabaseClient.test.ts +304 -0
- package/tests/unit/edge-cases/robustness.test.ts +259 -0
- package/tests/unit/errors/errors.test.ts +209 -0
- package/tests/unit/functions/FunctionsClient.test.ts +99 -0
- package/tests/unit/graphs/GraphClient.test.ts +329 -0
- package/tests/unit/policy/PolicyClient.test.ts +184 -0
- package/tests/unit/secrets/SecretsClient.test.ts +146 -0
- package/tests/unit/settings/SettingsClient.test.ts +50 -0
- package/tests/unit/storage/StorageClient.test.ts +251 -0
- package/tests/unit/users/UserClient.test.ts +150 -0
- package/vitest.config.ts +7 -0
- package/src/lib/Analytics/types.ts +0 -7
- package/src/lib/Function/types.ts +0 -17
- package/src/lib/Policy/PolicyClient.ts +0 -29
- package/src/lib/Policy/types.ts +0 -24
- package/src/lib/Secrets/SecretsClient.ts +0 -39
- package/src/lib/Secrets/types.ts +0 -17
- package/src/lib/Settings/types.ts +0 -4
- package/src/lib/Storage/types.ts +0 -41
- package/src/lib/user/UserClient.ts +0 -52
- package/src/lib/user/types.ts +0 -111
- /package/src/lib/{Analytics → analytics}/AnalyticsClient.ts +0 -0
- /package/src/lib/{App → app}/AppClient.ts +0 -0
- /package/src/lib/{Function → functions}/FunctionsClient.ts +0 -0
- /package/src/lib/{Settings → settings}/SettingsClient.ts +0 -0
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import type { TaruviResponse } from "../../types.js"
|
|
2
|
+
|
|
3
|
+
// Internal types - all optional since they're built incrementally via builder pattern
|
|
4
|
+
export interface BucketUrlParams {
|
|
5
|
+
appSlug?: string
|
|
6
|
+
bucket?: string
|
|
7
|
+
path?: string
|
|
8
|
+
upload?: string
|
|
9
|
+
delete?: string
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface BucketFileUpload {
|
|
13
|
+
files: []
|
|
14
|
+
path: string
|
|
15
|
+
metadata?: Record<string, unknown>
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Request types
|
|
19
|
+
export interface StorageRequest {
|
|
20
|
+
files: File[]
|
|
21
|
+
paths: string[]
|
|
22
|
+
metadatas: object[]
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface StorageUpdateRequest {
|
|
26
|
+
metadata?: Record<string, unknown>
|
|
27
|
+
visibility?: 'public' | 'private'
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Storage object - matches StorageObjectSerializer from API
|
|
31
|
+
export interface StorageObject {
|
|
32
|
+
id: number
|
|
33
|
+
uuid: string
|
|
34
|
+
bucket?: number
|
|
35
|
+
bucket_slug?: string
|
|
36
|
+
bucket_name?: string
|
|
37
|
+
filename: string
|
|
38
|
+
file_path: string
|
|
39
|
+
file_url: string
|
|
40
|
+
size: number
|
|
41
|
+
mimetype: string
|
|
42
|
+
metadata?: Record<string, unknown>
|
|
43
|
+
visibility?: string
|
|
44
|
+
created_at: string
|
|
45
|
+
updated_at: string
|
|
46
|
+
created_by?: number
|
|
47
|
+
modified_by?: number
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Response types - uses standard wrapper
|
|
51
|
+
export type StorageResponse = TaruviResponse<StorageObject>
|
|
52
|
+
export type StorageListResponse = TaruviResponse<StorageObject[]>
|
|
53
|
+
|
|
54
|
+
// Batch upload response
|
|
55
|
+
export interface StorageUploadBatchResponse {
|
|
56
|
+
status: "success" | "error"
|
|
57
|
+
message: string
|
|
58
|
+
data: {
|
|
59
|
+
uploaded_count: number
|
|
60
|
+
failed_count: number
|
|
61
|
+
total: number
|
|
62
|
+
successful: Array<{
|
|
63
|
+
index: number
|
|
64
|
+
path: string
|
|
65
|
+
object: StorageObject
|
|
66
|
+
}>
|
|
67
|
+
failed: Array<{
|
|
68
|
+
index: number
|
|
69
|
+
path: string
|
|
70
|
+
error: string
|
|
71
|
+
}>
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Batch delete response
|
|
76
|
+
export interface StorageDeleteBatchResponse {
|
|
77
|
+
status: "success" | "error"
|
|
78
|
+
message: string
|
|
79
|
+
data: {
|
|
80
|
+
deleted_count: number
|
|
81
|
+
failed: Array<{
|
|
82
|
+
path: string
|
|
83
|
+
error: string
|
|
84
|
+
}>
|
|
85
|
+
}
|
|
86
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { Client } from "../../client.js";
|
|
2
|
+
import type { UserCreateRequest, UserResponse, UserListResponse, UserListFilters, UserUpdateRequest, UserAppsResponse, AssignRolesRequest, RevokeRolesRequest, RolesResponse } from "./types.js";
|
|
3
|
+
import { UserRoutes } from "../../lib-internal/routes/UserRoutes.js";
|
|
4
|
+
import { buildQueryString } from "../../utils/utils.js";
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
export class User {
|
|
8
|
+
private client: Client
|
|
9
|
+
|
|
10
|
+
constructor(client: Client) {
|
|
11
|
+
this.client = client
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
async updateUser(username: string, body: UserUpdateRequest): Promise<UserResponse> {
|
|
15
|
+
return await this.client.httpClient.put(UserRoutes.updateUser(username), body)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
async getUser(username: string): Promise<UserResponse> {
|
|
19
|
+
return await this.client.httpClient.get<UserResponse>(UserRoutes.getUser(username))
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async list(filters?: UserListFilters): Promise<UserListResponse> {
|
|
23
|
+
const queryString = buildQueryString(filters as unknown as Record<string, unknown>)
|
|
24
|
+
return await this.client.httpClient.get<UserListResponse>(UserRoutes.listUser(queryString))
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async getUserApps(username: string): Promise<UserAppsResponse> {
|
|
28
|
+
return await this.client.httpClient.get<UserAppsResponse>(UserRoutes.getUserApps(username))
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async createUser(userData: UserCreateRequest): Promise<UserResponse> {
|
|
32
|
+
return await this.client.httpClient.post<UserResponse, UserCreateRequest>(
|
|
33
|
+
UserRoutes.baseUrl,
|
|
34
|
+
userData
|
|
35
|
+
)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async deleteUser(username: string): Promise<void> {
|
|
39
|
+
return await this.client.httpClient.delete(UserRoutes.deleteUser(username))
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async assignRoles(request: AssignRolesRequest): Promise<RolesResponse> {
|
|
43
|
+
return await this.client.httpClient.post<RolesResponse, AssignRolesRequest>(
|
|
44
|
+
UserRoutes.assignRoles(),
|
|
45
|
+
request
|
|
46
|
+
)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async revokeRoles(request: RevokeRolesRequest): Promise<RolesResponse> {
|
|
50
|
+
return await this.client.httpClient.delete<RolesResponse>(
|
|
51
|
+
UserRoutes.revokeRoles(),
|
|
52
|
+
request
|
|
53
|
+
)
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import type { TaruviResponse } from "../../types.js"
|
|
2
|
+
|
|
3
|
+
export interface UserCreateRequest {
|
|
4
|
+
username: string
|
|
5
|
+
email: string
|
|
6
|
+
password: string
|
|
7
|
+
confirm_password: string
|
|
8
|
+
first_name: string
|
|
9
|
+
last_name: string
|
|
10
|
+
is_active?: boolean
|
|
11
|
+
is_staff?: boolean
|
|
12
|
+
is_cloud_user?: boolean
|
|
13
|
+
attributes?: Record<string, unknown>
|
|
14
|
+
role_slugs?: string[]
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface UserData {
|
|
18
|
+
id: number
|
|
19
|
+
uuid?: string
|
|
20
|
+
username: string
|
|
21
|
+
email: string
|
|
22
|
+
first_name: string
|
|
23
|
+
last_name: string
|
|
24
|
+
full_name?: string
|
|
25
|
+
is_active: boolean
|
|
26
|
+
is_staff: boolean
|
|
27
|
+
is_superuser?: boolean
|
|
28
|
+
is_deleted: boolean
|
|
29
|
+
date_joined: string
|
|
30
|
+
last_login?: string
|
|
31
|
+
groups?: UserGroup[]
|
|
32
|
+
user_permissions?: UserPermission[]
|
|
33
|
+
attributes?: Record<string, unknown>
|
|
34
|
+
missing_attributes?: string[]
|
|
35
|
+
roles?: UserRole[]
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface UserGroup {
|
|
39
|
+
id: number
|
|
40
|
+
name: string
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface UserPermission {
|
|
44
|
+
id: number
|
|
45
|
+
name: string
|
|
46
|
+
codename: string
|
|
47
|
+
content_type: string
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export interface UserRole {
|
|
51
|
+
name: string
|
|
52
|
+
slug: string
|
|
53
|
+
type: string
|
|
54
|
+
app_slug: string
|
|
55
|
+
source: "direct" | "site_role" | "inherited"
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export interface UserUpdateRequest {
|
|
59
|
+
username?: string
|
|
60
|
+
email?: string
|
|
61
|
+
first_name?: string
|
|
62
|
+
last_name?: string
|
|
63
|
+
is_active?: boolean
|
|
64
|
+
is_staff?: boolean
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export interface UserListFilters {
|
|
68
|
+
search?: string
|
|
69
|
+
is_active?: boolean
|
|
70
|
+
is_staff?: boolean
|
|
71
|
+
is_superuser?: boolean
|
|
72
|
+
is_deleted?: boolean
|
|
73
|
+
ordering?: string
|
|
74
|
+
page?: number
|
|
75
|
+
page_size?: number
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export interface UserApp {
|
|
79
|
+
name: string
|
|
80
|
+
slug: string
|
|
81
|
+
icon: string
|
|
82
|
+
url: string
|
|
83
|
+
display_name: string
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Response types - uses standard wrapper
|
|
87
|
+
export type UserResponse = TaruviResponse<UserData>
|
|
88
|
+
export type UserListResponse = TaruviResponse<UserData[]>
|
|
89
|
+
export type UserAppsResponse = TaruviResponse<UserApp[]>
|
|
90
|
+
|
|
91
|
+
export interface AssignRolesRequest {
|
|
92
|
+
roles: string[]
|
|
93
|
+
usernames: string[]
|
|
94
|
+
expires_at?: string
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export interface RevokeRolesRequest {
|
|
98
|
+
roles: string[]
|
|
99
|
+
usernames: string[]
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export type RolesResponse = TaruviResponse<{
|
|
103
|
+
count: number
|
|
104
|
+
}>
|
|
@@ -1,12 +1,102 @@
|
|
|
1
|
-
|
|
1
|
+
import { ErrorCode, type ErrorResponseBody } from './types.js'
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
/**
|
|
4
|
+
* Base SDK error. All typed errors extend this.
|
|
5
|
+
*/
|
|
6
|
+
export class TaruviError extends Error {
|
|
7
|
+
public readonly code: string
|
|
8
|
+
public readonly statusCode: number
|
|
9
|
+
public readonly detail: string | undefined
|
|
10
|
+
public readonly errors: Record<string, unknown> | undefined
|
|
11
|
+
public readonly data: unknown
|
|
12
|
+
|
|
13
|
+
constructor(message: string, statusCode: number, code: string = ErrorCode.INTERNAL_ERROR, detail?: string, errors?: Record<string, unknown>, data?: unknown) {
|
|
14
|
+
super(message)
|
|
15
|
+
this.name = 'TaruviError'
|
|
16
|
+
this.statusCode = statusCode
|
|
17
|
+
this.code = code
|
|
18
|
+
this.detail = detail
|
|
19
|
+
this.errors = errors
|
|
20
|
+
this.data = data
|
|
5
21
|
}
|
|
22
|
+
}
|
|
6
23
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
24
|
+
export class ValidationError extends TaruviError {
|
|
25
|
+
constructor(message = 'Validation failed', detail?: string, errors?: Record<string, unknown>) {
|
|
26
|
+
super(message, 400, ErrorCode.VALIDATION_ERROR, detail, errors)
|
|
27
|
+
this.name = 'ValidationError'
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export class AuthError extends TaruviError {
|
|
32
|
+
constructor(message = 'Authentication required') {
|
|
33
|
+
super(message, 401, ErrorCode.UNAUTHORIZED)
|
|
34
|
+
this.name = 'AuthError'
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export class ForbiddenError extends TaruviError {
|
|
39
|
+
constructor(message = 'Permission denied') {
|
|
40
|
+
super(message, 403, ErrorCode.FORBIDDEN)
|
|
41
|
+
this.name = 'ForbiddenError'
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export class NotFoundError extends TaruviError {
|
|
46
|
+
constructor(message = 'Resource not found') {
|
|
47
|
+
super(message, 404, ErrorCode.NOT_FOUND)
|
|
48
|
+
this.name = 'NotFoundError'
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export class ConflictError extends TaruviError {
|
|
53
|
+
constructor(message = 'Resource conflict', detail?: string) {
|
|
54
|
+
super(message, 409, ErrorCode.CONFLICT, detail)
|
|
55
|
+
this.name = 'ConflictError'
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export class TimeoutError extends TaruviError {
|
|
60
|
+
constructor(message = 'Request timeout') {
|
|
61
|
+
super(message, 504, ErrorCode.GATEWAY_TIMEOUT)
|
|
62
|
+
this.name = 'TimeoutError'
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export class NetworkError extends TaruviError {
|
|
67
|
+
constructor(message = 'Network error') {
|
|
68
|
+
super(message, 0, ErrorCode.NETWORK_ERROR)
|
|
69
|
+
this.name = 'NetworkError'
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Maps HTTP status + response body to the appropriate typed error.
|
|
75
|
+
*/
|
|
76
|
+
export function createErrorFromResponse(statusCode: number, body?: ErrorResponseBody): TaruviError {
|
|
77
|
+
const message = body?.message || 'Request failed'
|
|
78
|
+
const code = body?.code || ErrorCode.INTERNAL_ERROR
|
|
79
|
+
const detail = body?.detail
|
|
80
|
+
const errors = body?.errors
|
|
81
|
+
const data = body?.data
|
|
82
|
+
|
|
83
|
+
switch (statusCode) {
|
|
84
|
+
case 400:
|
|
85
|
+
if (code === ErrorCode.VALIDATION_ERROR) {
|
|
86
|
+
return new ValidationError(message, detail, errors)
|
|
87
|
+
}
|
|
88
|
+
return new TaruviError(message, 400, code, detail, errors, data)
|
|
89
|
+
case 401:
|
|
90
|
+
return new AuthError(message)
|
|
91
|
+
case 403:
|
|
92
|
+
return new ForbiddenError(message)
|
|
93
|
+
case 404:
|
|
94
|
+
return new NotFoundError(message)
|
|
95
|
+
case 409:
|
|
96
|
+
return new ConflictError(message, detail)
|
|
97
|
+
case 504:
|
|
98
|
+
return new TimeoutError(message)
|
|
99
|
+
default:
|
|
100
|
+
return new TaruviError(message, statusCode, code, detail, errors, data)
|
|
101
|
+
}
|
|
12
102
|
}
|
|
@@ -1,25 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
* This module provides comprehensive error handling for the Taruvi SDK
|
|
5
|
-
* including typed error classes, error codes, and utility functions.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
// Export error client
|
|
9
|
-
export { ErrorClient } from './ErrorClient.js';
|
|
10
|
-
|
|
11
|
-
// TODO: Export all error classes when implemented
|
|
12
|
-
// - TaruviError
|
|
13
|
-
// - AuthError
|
|
14
|
-
// - DatabaseError
|
|
15
|
-
// - StorageError
|
|
16
|
-
// - NetworkError
|
|
17
|
-
// - ValidationError
|
|
18
|
-
// - FunctionError
|
|
19
|
-
|
|
20
|
-
// TODO: Export types when implemented
|
|
21
|
-
// - ErrorCode
|
|
22
|
-
// - ErrorSeverity
|
|
23
|
-
// - ErrorResponse
|
|
24
|
-
// - HttpErrorResponse
|
|
25
|
-
// - ValidationErrorDetail
|
|
1
|
+
export { TaruviError, ValidationError, AuthError, ForbiddenError, NotFoundError, ConflictError, TimeoutError, NetworkError, createErrorFromResponse } from './ErrorClient.js'
|
|
2
|
+
export { ErrorCode } from './types.js'
|
|
3
|
+
export type { ErrorResponseBody } from './types.js'
|
|
@@ -1,105 +1,28 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Error
|
|
3
|
-
|
|
4
|
-
export enum ErrorSeverity {
|
|
5
|
-
LOW = 'low',
|
|
6
|
-
MEDIUM = 'medium',
|
|
7
|
-
HIGH = 'high',
|
|
8
|
-
CRITICAL = 'critical'
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Standard error codes used across the SDK
|
|
2
|
+
* Error codes matching the backend's ErrorCode enum.
|
|
3
|
+
* Maps 1:1 with base.responses.error_codes.ErrorCode in Python.
|
|
13
4
|
*/
|
|
14
5
|
export enum ErrorCode {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
INTERNAL_ERROR = 'INTERNAL_ERROR',
|
|
18
|
-
TIMEOUT_ERROR = 'TIMEOUT_ERROR',
|
|
19
|
-
|
|
20
|
-
// Network errors (1100-1199)
|
|
21
|
-
NETWORK_ERROR = 'NETWORK_ERROR',
|
|
22
|
-
CONNECTION_ERROR = 'CONNECTION_ERROR',
|
|
23
|
-
REQUEST_FAILED = 'REQUEST_FAILED',
|
|
24
|
-
|
|
25
|
-
// Authentication errors (1200-1299)
|
|
26
|
-
AUTH_ERROR = 'AUTH_ERROR',
|
|
6
|
+
BAD_REQUEST = 'BAD_REQUEST',
|
|
7
|
+
VALIDATION_ERROR = 'VALIDATION_ERROR',
|
|
27
8
|
UNAUTHORIZED = 'UNAUTHORIZED',
|
|
28
|
-
TOKEN_EXPIRED = 'TOKEN_EXPIRED',
|
|
29
|
-
TOKEN_INVALID = 'TOKEN_INVALID',
|
|
30
|
-
SESSION_EXPIRED = 'SESSION_EXPIRED',
|
|
31
|
-
INVALID_CREDENTIALS = 'INVALID_CREDENTIALS',
|
|
32
|
-
|
|
33
|
-
// Authorization errors (1300-1399)
|
|
34
9
|
FORBIDDEN = 'FORBIDDEN',
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
MISSING_REQUIRED_FIELD = 'MISSING_REQUIRED_FIELD',
|
|
41
|
-
INVALID_FORMAT = 'INVALID_FORMAT',
|
|
42
|
-
|
|
43
|
-
// Database errors (1500-1599)
|
|
44
|
-
DATABASE_ERROR = 'DATABASE_ERROR',
|
|
45
|
-
QUERY_FAILED = 'QUERY_FAILED',
|
|
46
|
-
RECORD_NOT_FOUND = 'RECORD_NOT_FOUND',
|
|
47
|
-
DUPLICATE_ENTRY = 'DUPLICATE_ENTRY',
|
|
48
|
-
CONSTRAINT_VIOLATION = 'CONSTRAINT_VIOLATION',
|
|
49
|
-
|
|
50
|
-
// Storage errors (1600-1699)
|
|
51
|
-
STORAGE_ERROR = 'STORAGE_ERROR',
|
|
52
|
-
FILE_NOT_FOUND = 'FILE_NOT_FOUND',
|
|
53
|
-
FILE_TOO_LARGE = 'FILE_TOO_LARGE',
|
|
54
|
-
INVALID_FILE_TYPE = 'INVALID_FILE_TYPE',
|
|
55
|
-
UPLOAD_FAILED = 'UPLOAD_FAILED',
|
|
56
|
-
DOWNLOAD_FAILED = 'DOWNLOAD_FAILED',
|
|
57
|
-
|
|
58
|
-
// Function errors (1700-1799)
|
|
59
|
-
FUNCTION_ERROR = 'FUNCTION_ERROR',
|
|
60
|
-
FUNCTION_NOT_FOUND = 'FUNCTION_NOT_FOUND',
|
|
61
|
-
FUNCTION_EXECUTION_FAILED = 'FUNCTION_EXECUTION_FAILED',
|
|
62
|
-
FUNCTION_TIMEOUT = 'FUNCTION_TIMEOUT',
|
|
63
|
-
|
|
64
|
-
// Rate limiting errors (1800-1899)
|
|
65
|
-
RATE_LIMIT_EXCEEDED = 'RATE_LIMIT_EXCEEDED',
|
|
66
|
-
QUOTA_EXCEEDED = 'QUOTA_EXCEEDED',
|
|
67
|
-
|
|
68
|
-
// Client errors (1900-1999)
|
|
69
|
-
CONFIGURATION_ERROR = 'CONFIGURATION_ERROR',
|
|
70
|
-
INVALID_API_KEY = 'INVALID_API_KEY',
|
|
71
|
-
INVALID_CONFIG = 'INVALID_CONFIG'
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Base error response structure
|
|
76
|
-
*/
|
|
77
|
-
export interface ErrorResponse {
|
|
78
|
-
code: ErrorCode;
|
|
79
|
-
message: string;
|
|
80
|
-
severity: ErrorSeverity;
|
|
81
|
-
timestamp: string;
|
|
82
|
-
requestId?: string | undefined;
|
|
83
|
-
metadata?: Record<string, any> | undefined;
|
|
84
|
-
cause?: Error | undefined;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* HTTP error response from the server
|
|
89
|
-
*/
|
|
90
|
-
export interface HttpErrorResponse {
|
|
91
|
-
status: number;
|
|
92
|
-
statusText: string;
|
|
93
|
-
message?: string;
|
|
94
|
-
code?: string;
|
|
95
|
-
details?: any;
|
|
10
|
+
NOT_FOUND = 'NOT_FOUND',
|
|
11
|
+
CONFLICT = 'CONFLICT',
|
|
12
|
+
INTERNAL_ERROR = 'INTERNAL_ERROR',
|
|
13
|
+
GATEWAY_TIMEOUT = 'GATEWAY_TIMEOUT',
|
|
14
|
+
NETWORK_ERROR = 'NETWORK_ERROR'
|
|
96
15
|
}
|
|
97
16
|
|
|
98
17
|
/**
|
|
99
|
-
*
|
|
18
|
+
* Backend error response shape.
|
|
19
|
+
* Matches AppException.to_dict() output.
|
|
100
20
|
*/
|
|
101
|
-
export interface
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
21
|
+
export interface ErrorResponseBody {
|
|
22
|
+
status: 'error'
|
|
23
|
+
code: string
|
|
24
|
+
message: string
|
|
25
|
+
detail?: string
|
|
26
|
+
errors?: Record<string, unknown>
|
|
27
|
+
data?: unknown
|
|
105
28
|
}
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import type { TaruviConfig } from "../../types.js";
|
|
2
2
|
import type { TokenClient } from "../token/TokenClient.js";
|
|
3
|
-
import axios from "axios";
|
|
3
|
+
import axios, { AxiosError } from "axios";
|
|
4
|
+
import { createErrorFromResponse, NetworkError, TaruviError } from "../errors/index.js";
|
|
5
|
+
import type { ErrorResponseBody } from "../errors/index.js";
|
|
4
6
|
|
|
5
7
|
/**
|
|
6
8
|
* HttpClient handles all HTTP requests to the Taruvi API.
|
|
7
|
-
* Automatically adds authentication headers
|
|
8
|
-
*
|
|
9
|
-
* - X-Session-Token: User session token for authenticated requests
|
|
9
|
+
* Automatically adds authentication headers and converts
|
|
10
|
+
* error responses to typed SDK errors.
|
|
10
11
|
*
|
|
11
12
|
* @internal
|
|
12
13
|
*/
|
|
@@ -36,56 +37,93 @@ export class HttpClient {
|
|
|
36
37
|
return headers
|
|
37
38
|
}
|
|
38
39
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
}
|
|
43
|
-
return data
|
|
44
|
-
}
|
|
40
|
+
private handleError(error: unknown): never {
|
|
41
|
+
if (error instanceof TaruviError) {
|
|
42
|
+
throw error
|
|
43
|
+
}
|
|
45
44
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
body,
|
|
51
|
-
{
|
|
52
|
-
headers: this.getAuthHeaders(isFormData)
|
|
45
|
+
if (error instanceof AxiosError) {
|
|
46
|
+
if (error.response) {
|
|
47
|
+
const body = error.response.data as ErrorResponseBody | undefined
|
|
48
|
+
throw createErrorFromResponse(error.response.status, body)
|
|
53
49
|
}
|
|
54
|
-
|
|
55
|
-
|
|
50
|
+
// No response — network error
|
|
51
|
+
throw new NetworkError(error.message)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Unknown error
|
|
55
|
+
throw error
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
async
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
{
|
|
63
|
-
headers: this.getAuthHeaders(isFormData)
|
|
58
|
+
async get<T>(endpoint: string): Promise<T> {
|
|
59
|
+
try {
|
|
60
|
+
const { data } = await axios.get<T>(`${this.config.apiUrl}/${endpoint}`, {
|
|
61
|
+
headers: this.getAuthHeaders()
|
|
64
62
|
})
|
|
63
|
+
return data
|
|
64
|
+
} catch (error) {
|
|
65
|
+
this.handleError(error)
|
|
66
|
+
}
|
|
67
|
+
}
|
|
65
68
|
|
|
66
|
-
|
|
69
|
+
async post<T, D = unknown>(endpoint: string, body: D): Promise<T> {
|
|
70
|
+
try {
|
|
71
|
+
const isFormData = body instanceof FormData
|
|
72
|
+
const { data } = await axios.post<T>(
|
|
73
|
+
`${this.config.apiUrl}/${endpoint}`,
|
|
74
|
+
body,
|
|
75
|
+
{
|
|
76
|
+
headers: this.getAuthHeaders(isFormData)
|
|
77
|
+
}
|
|
78
|
+
)
|
|
79
|
+
return data
|
|
80
|
+
} catch (error) {
|
|
81
|
+
this.handleError(error)
|
|
82
|
+
}
|
|
67
83
|
}
|
|
68
84
|
|
|
69
|
-
async
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
{
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
85
|
+
async put<T, D = unknown>(endpoint: string, body: D): Promise<T> {
|
|
86
|
+
try {
|
|
87
|
+
const isFormData = body instanceof FormData
|
|
88
|
+
const { data } = await axios.put<T>(`${this.config.apiUrl}/${endpoint}`,
|
|
89
|
+
body,
|
|
90
|
+
{
|
|
91
|
+
headers: this.getAuthHeaders(isFormData)
|
|
92
|
+
})
|
|
93
|
+
return data
|
|
94
|
+
} catch (error) {
|
|
95
|
+
this.handleError(error)
|
|
96
|
+
}
|
|
78
97
|
}
|
|
79
98
|
|
|
80
|
-
async
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
99
|
+
async delete<T, D = unknown>(endpoint: string, body?: D): Promise<T> {
|
|
100
|
+
try {
|
|
101
|
+
const { data } = await axios.delete<T>(
|
|
102
|
+
`${this.config.apiUrl}/${endpoint}`,
|
|
103
|
+
{
|
|
104
|
+
headers: this.getAuthHeaders(),
|
|
105
|
+
data: body
|
|
106
|
+
}
|
|
107
|
+
)
|
|
108
|
+
return data
|
|
109
|
+
} catch (error) {
|
|
110
|
+
this.handleError(error)
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
async patch<T, D = unknown>(endpoint: string, body: D): Promise<T> {
|
|
115
|
+
try {
|
|
116
|
+
const isFormData = body instanceof FormData
|
|
117
|
+
const { data } = await axios.patch<T>(
|
|
118
|
+
`${this.config.apiUrl}/${endpoint}`,
|
|
119
|
+
body,
|
|
120
|
+
{
|
|
121
|
+
headers: this.getAuthHeaders(isFormData)
|
|
122
|
+
}
|
|
123
|
+
)
|
|
124
|
+
return data
|
|
125
|
+
} catch (error) {
|
|
126
|
+
this.handleError(error)
|
|
127
|
+
}
|
|
90
128
|
}
|
|
91
129
|
}
|
|
@@ -5,5 +5,7 @@ export const UserRoutes = {
|
|
|
5
5
|
updateUser: (username: string) => `${UserRoutes.baseUrl}${username}/`,
|
|
6
6
|
deleteUser: (username: string) => `${UserRoutes.baseUrl}${username}/`,
|
|
7
7
|
listUser: (filter: string) => `${UserRoutes.baseUrl}${filter}`,
|
|
8
|
-
getUserApps: (username: string) => `${UserRoutes.baseUrl}${username}/apps
|
|
8
|
+
getUserApps: (username: string) => `${UserRoutes.baseUrl}${username}/apps/`,
|
|
9
|
+
assignRoles: () => `api/assign/roles/`,
|
|
10
|
+
revokeRoles: () => `api/revoke/roles/`
|
|
9
11
|
} as const
|