@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.
Files changed (63) hide show
  1. package/.kiro/settings/lsp.json +198 -0
  2. package/MODULE_NAMING_CHANGES.md +81 -0
  3. package/PARAMETER_NAMING_CHANGES.md +106 -0
  4. package/README.md +4 -4
  5. package/package.json +10 -4
  6. package/src/client.ts +4 -4
  7. package/src/index.ts +48 -35
  8. package/src/lib/analytics/types.ts +8 -0
  9. package/src/lib/{App → app}/types.ts +14 -6
  10. package/src/lib/auth/AuthClient.ts +8 -7
  11. package/src/lib/auth/types.ts +108 -6
  12. package/src/lib/{Database → database}/DatabaseClient.ts +15 -10
  13. package/src/lib/{Database → database}/types.ts +4 -7
  14. package/src/lib/functions/types.ts +25 -0
  15. package/src/lib/{Graphs → graphs}/GraphClient.ts +9 -8
  16. package/src/lib/{Graphs → graphs}/types.ts +12 -5
  17. package/src/lib/policy/PolicyClient.ts +79 -0
  18. package/src/lib/policy/types.ts +40 -0
  19. package/src/lib/secrets/SecretsClient.ts +75 -0
  20. package/src/lib/secrets/types.ts +59 -0
  21. package/src/lib/settings/types.ts +9 -0
  22. package/src/lib/{Storage → storage}/StorageClient.ts +27 -9
  23. package/src/lib/storage/types.ts +86 -0
  24. package/src/lib/users/UserClient.ts +55 -0
  25. package/src/lib/users/types.ts +104 -0
  26. package/src/lib-internal/errors/ErrorClient.ts +98 -8
  27. package/src/lib-internal/errors/index.ts +3 -25
  28. package/src/lib-internal/errors/types.ts +18 -95
  29. package/src/lib-internal/http/HttpClient.ts +83 -45
  30. package/src/lib-internal/routes/UserRoutes.ts +3 -1
  31. package/src/lib-internal/token/TokenClient.ts +1 -1
  32. package/src/types.ts +20 -1
  33. package/src/utils/utils.ts +5 -1
  34. package/tests/fixtures/mockClient.ts +19 -0
  35. package/tests/unit/analytics/AnalyticsClient.test.ts +84 -0
  36. package/tests/unit/app/AppClient.test.ts +114 -0
  37. package/tests/unit/auth/AuthClient.test.ts +131 -0
  38. package/tests/unit/client/Client.test.ts +70 -0
  39. package/tests/unit/database/DatabaseClient.test.ts +304 -0
  40. package/tests/unit/edge-cases/robustness.test.ts +259 -0
  41. package/tests/unit/errors/errors.test.ts +209 -0
  42. package/tests/unit/functions/FunctionsClient.test.ts +99 -0
  43. package/tests/unit/graphs/GraphClient.test.ts +329 -0
  44. package/tests/unit/policy/PolicyClient.test.ts +184 -0
  45. package/tests/unit/secrets/SecretsClient.test.ts +146 -0
  46. package/tests/unit/settings/SettingsClient.test.ts +50 -0
  47. package/tests/unit/storage/StorageClient.test.ts +251 -0
  48. package/tests/unit/users/UserClient.test.ts +150 -0
  49. package/vitest.config.ts +7 -0
  50. package/src/lib/Analytics/types.ts +0 -7
  51. package/src/lib/Function/types.ts +0 -17
  52. package/src/lib/Policy/PolicyClient.ts +0 -29
  53. package/src/lib/Policy/types.ts +0 -24
  54. package/src/lib/Secrets/SecretsClient.ts +0 -39
  55. package/src/lib/Secrets/types.ts +0 -17
  56. package/src/lib/Settings/types.ts +0 -4
  57. package/src/lib/Storage/types.ts +0 -41
  58. package/src/lib/user/UserClient.ts +0 -52
  59. package/src/lib/user/types.ts +0 -111
  60. /package/src/lib/{Analytics → analytics}/AnalyticsClient.ts +0 -0
  61. /package/src/lib/{App → app}/AppClient.ts +0 -0
  62. /package/src/lib/{Function → functions}/FunctionsClient.ts +0 -0
  63. /package/src/lib/{Settings → settings}/SettingsClient.ts +0 -0
@@ -0,0 +1,150 @@
1
+ import { describe, it, expect, vi, beforeEach } from 'vitest'
2
+ import { User } from '../../../src/lib/users/UserClient.js'
3
+ import { Client } from '../../../src/client.js'
4
+
5
+ const mockHttpClient = {
6
+ get: vi.fn(),
7
+ post: vi.fn(),
8
+ put: vi.fn(),
9
+ delete: vi.fn()
10
+ }
11
+
12
+ const mockClient = {
13
+ getConfig: () => ({ apiKey: 'test-key', appSlug: 'test-app', apiUrl: 'https://api.test.com' }),
14
+ httpClient: mockHttpClient
15
+ } as unknown as Client
16
+
17
+ describe('User', () => {
18
+ beforeEach(() => {
19
+ vi.clearAllMocks()
20
+ })
21
+
22
+ describe('getUser()', () => {
23
+ it('fetches user by username', async () => {
24
+ const userData = { username: 'john_doe', email: 'john@example.com' }
25
+ mockHttpClient.get.mockResolvedValue(userData)
26
+
27
+ const user = new User(mockClient)
28
+ const result = await user.getUser('john_doe')
29
+
30
+ expect(mockHttpClient.get).toHaveBeenCalledWith('api/users/john_doe/')
31
+ expect(result).toEqual(userData)
32
+ })
33
+ })
34
+
35
+ describe('createUser()', () => {
36
+ it('creates user with provided data', async () => {
37
+ const createData = {
38
+ username: 'new_user',
39
+ email: 'new@example.com',
40
+ password: 'pass123',
41
+ confirm_password: 'pass123',
42
+ first_name: 'New',
43
+ last_name: 'User',
44
+ is_active: true,
45
+ is_staff: false,
46
+ attributes: ''
47
+ }
48
+ const response = { id: '1', ...createData }
49
+ mockHttpClient.post.mockResolvedValue(response)
50
+
51
+ const user = new User(mockClient)
52
+ const result = await user.createUser(createData)
53
+
54
+ expect(mockHttpClient.post).toHaveBeenCalledWith('api/users/', createData)
55
+ expect(result).toEqual(response)
56
+ })
57
+ })
58
+
59
+ describe('updateUser()', () => {
60
+ it('updates user by username', async () => {
61
+ const updateData = { email: 'updated@example.com', first_name: 'Updated' }
62
+ const response = { username: 'john_doe', ...updateData }
63
+ mockHttpClient.put.mockResolvedValue(response)
64
+
65
+ const user = new User(mockClient)
66
+ const result = await user.updateUser('john_doe', updateData)
67
+
68
+ expect(mockHttpClient.put).toHaveBeenCalledWith('api/users/john_doe/', updateData)
69
+ expect(result).toEqual(response)
70
+ })
71
+ })
72
+
73
+ describe('deleteUser()', () => {
74
+ it('deletes user by username', async () => {
75
+ mockHttpClient.delete.mockResolvedValue(undefined)
76
+
77
+ const user = new User(mockClient)
78
+ await user.deleteUser('john_doe')
79
+
80
+ expect(mockHttpClient.delete).toHaveBeenCalledWith('api/users/john_doe/')
81
+ })
82
+ })
83
+
84
+ describe('list()', () => {
85
+ it('lists users with filters', async () => {
86
+ const users = [{ username: 'user1' }, { username: 'user2' }]
87
+ mockHttpClient.get.mockResolvedValue(users)
88
+
89
+ const user = new User(mockClient)
90
+ const result = await user.list({ search: 'john', is_active: true, page: 1, page_size: 20 })
91
+
92
+ expect(mockHttpClient.get).toHaveBeenCalledWith(
93
+ expect.stringContaining('api/users/')
94
+ )
95
+ expect(result).toEqual(users)
96
+ })
97
+
98
+ it('builds query string from filters', async () => {
99
+ mockHttpClient.get.mockResolvedValue([])
100
+
101
+ const user = new User(mockClient)
102
+ await user.list({ search: 'test', ordering: '-date_joined' })
103
+
104
+ const url = mockHttpClient.get.mock.calls[0][0]
105
+ expect(url).toContain('search=test')
106
+ expect(url).toContain('ordering=-date_joined')
107
+ })
108
+ })
109
+
110
+ describe('getUserApps()', () => {
111
+ it('fetches apps for user', async () => {
112
+ const apps = [{ name: 'App1', slug: 'app1' }, { name: 'App2', slug: 'app2' }]
113
+ mockHttpClient.get.mockResolvedValue(apps)
114
+
115
+ const user = new User(mockClient)
116
+ const result = await user.getUserApps('john_doe')
117
+
118
+ expect(mockHttpClient.get).toHaveBeenCalledWith('api/users/john_doe/apps/')
119
+ expect(result).toEqual(apps)
120
+ })
121
+ })
122
+
123
+ describe('assignRoles()', () => {
124
+ it('assigns roles to user', async () => {
125
+ const request = { username: 'john_doe', roles: ['admin', 'editor'] }
126
+ const response = { success: true }
127
+ mockHttpClient.post.mockResolvedValue(response)
128
+
129
+ const user = new User(mockClient)
130
+ const result = await user.assignRoles(request)
131
+
132
+ expect(mockHttpClient.post).toHaveBeenCalledWith('api/assign/roles/', request)
133
+ expect(result).toEqual(response)
134
+ })
135
+ })
136
+
137
+ describe('revokeRoles()', () => {
138
+ it('revokes roles from user', async () => {
139
+ const request = { username: 'john_doe', roles: ['admin'] }
140
+ const response = { success: true }
141
+ mockHttpClient.delete.mockResolvedValue(response)
142
+
143
+ const user = new User(mockClient)
144
+ const result = await user.revokeRoles(request)
145
+
146
+ expect(mockHttpClient.delete).toHaveBeenCalledWith('api/revoke/roles/', request)
147
+ expect(result).toEqual(response)
148
+ })
149
+ })
150
+ })
@@ -0,0 +1,7 @@
1
+ import { defineConfig } from 'vitest/config'
2
+
3
+ export default defineConfig({
4
+ test: {
5
+ include: ['tests/**/*.test.ts']
6
+ }
7
+ })
@@ -1,7 +0,0 @@
1
- export interface AnalyticsRequest {
2
- params?: Record<string, unknown>
3
- }
4
-
5
- export interface AnalyticsResponse<T = unknown> {
6
- data: T | null
7
- }
@@ -1,17 +0,0 @@
1
- export interface FunctionRequest {
2
- async?: boolean
3
- params?: Record<string, unknown>
4
- }
5
-
6
- export interface FunctionInvocation {
7
- invocation_id: number
8
- celery_task_id: string
9
- status: string
10
- created_at: string
11
- updated_at: string
12
- }
13
-
14
- export interface FunctionResponse<T = unknown> {
15
- data: T | null
16
- invocation: FunctionInvocation
17
- }
@@ -1,29 +0,0 @@
1
- import type { Client } from "../../client.js"
2
- import { PolicyRoutes } from "../../lib-internal/routes/PolicyRoutes.js"
3
- import type { TaruviConfig } from "../../types.js"
4
- import type { Resources } from "./types.js"
5
-
6
- export class Policy {
7
- private client: Client
8
- private config: TaruviConfig
9
- constructor(client: Client) {
10
- this.client = client
11
- this.config = this.client.getConfig()
12
- }
13
-
14
- async checkResource(resources: Resources) {
15
- const url = PolicyRoutes.baseUrl(this.config.appSlug) + PolicyRoutes.checkResource
16
- const body = JSON.stringify({
17
- resources: resources.map(r => ({
18
- resource: {
19
- kind: `${r.entityType}:${r.tableName}`,
20
- id: r.recordId,
21
- attr: r.attributes || {}
22
- },
23
- actions: r.actions
24
- }))
25
- })
26
-
27
- return await this.client.httpClient.post(url, body)
28
- }
29
- }
@@ -1,24 +0,0 @@
1
- export interface Principal {
2
- id: string
3
- roles: string[]
4
- attr: Record<string, unknown>
5
- }
6
-
7
- export type Resource = {
8
- kind: string
9
- id: string
10
- attr: Record<string, unknown>
11
- };
12
-
13
- export type Resources = {
14
- entityType: string
15
- tableName: string
16
- recordId: string
17
- attributes: Record<string, unknown>
18
- actions: string[]
19
- }[]
20
-
21
- export type ResourceCheckResponse = {
22
- allowed: boolean
23
- reason: string
24
- }
@@ -1,39 +0,0 @@
1
- import type { Client } from "../../client.js";
2
- import type { SecretsUrlParams } from "./types.js";
3
- import { HttpMethod } from "../../lib-internal/http/types.js";
4
- import { SecretsRoutes } from "../../lib-internal/routes/SecretsRoutes.js";
5
-
6
- export class Secrets {
7
- private client: Client
8
- private urlParams: SecretsUrlParams
9
- private body: object | undefined
10
- private method: HttpMethod
11
-
12
- constructor(client: Client, urlParams: SecretsUrlParams = {}, body?: object, method: HttpMethod = HttpMethod.GET) {
13
- this.client = client
14
- this.urlParams = urlParams
15
- this.body = body
16
- this.method = method
17
- }
18
-
19
- list(): Secrets {
20
- return new Secrets(this.client, { path: SecretsRoutes.baseUrl }, undefined, HttpMethod.GET)
21
- }
22
-
23
- get(key: string): Secrets {
24
- const path = SecretsRoutes.get(key)
25
- return new Secrets(this.client, { ...this.urlParams, path }, undefined, HttpMethod.GET)
26
- }
27
-
28
- async execute<T = unknown>(): Promise<T> {
29
- const url = this.urlParams.path ?? SecretsRoutes.baseUrl
30
-
31
- switch (this.method) {
32
- case HttpMethod.PUT:
33
- return await this.client.httpClient.put<T>(url, this.body)
34
- case HttpMethod.GET:
35
- default:
36
- return await this.client.httpClient.get<T>(url)
37
- }
38
- }
39
- }
@@ -1,17 +0,0 @@
1
- // Internal types
2
- export interface SecretsUrlParams {
3
- path?: string
4
- }
5
-
6
- // Request types
7
- export interface SecretRequest {
8
- value: string
9
- }
10
-
11
- // Response types
12
- export interface SecretResponse {
13
- key: string
14
- value: string
15
- created_at?: string
16
- updated_at?: string
17
- }
@@ -1,4 +0,0 @@
1
- // Response types
2
- export interface SettingsResponse {
3
- [key: string]: unknown
4
- }
@@ -1,41 +0,0 @@
1
- // Internal types
2
- export interface BucketUrlParams {
3
- appSlug: string
4
- bucket: string
5
- path?: string | undefined
6
- upload?: string
7
- delete?: string
8
- }
9
-
10
- export interface BucketFileUpload {
11
- files: []
12
- path: string
13
- metadata?: Record<string, unknown>
14
- }
15
-
16
- // Request types
17
- export interface StorageRequest {
18
- files: File[]
19
- paths: string[]
20
- metadatas: object[]
21
- }
22
-
23
- export interface StorageUpdateRequest {
24
- metadata?: Record<string, unknown>
25
- visibility?: 'public' | 'private'
26
- }
27
-
28
- // Response types
29
- export interface StorageResponse {
30
- id: number
31
- uuid: string
32
- filename: string
33
- file_path: string
34
- file_url: string
35
- size: number
36
- mimetype: string
37
- metadata?: Record<string, unknown>
38
- visibility?: string
39
- created_at: string
40
- updated_at: string
41
- }
@@ -1,52 +0,0 @@
1
- import type { Client } from "../../client.js";
2
- import type { UserCreateRequest, UserCreateResponse, UserDataResponse, UserList, UserUpdateRequest, UserAppsResponse } 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
- // - getUserProfile
15
- async getUserData(): Promise<UserDataResponse> {
16
- return await this.client.httpClient.get<UserDataResponse>(UserRoutes.getCurrentUser())
17
- }
18
-
19
- // - updateUser
20
- async updateUser(username: string, body: UserUpdateRequest): Promise<UserCreateResponse> {
21
- return await this.client.httpClient.put(UserRoutes.updateUser(username), body)
22
- }
23
-
24
- async getUser(username: string): Promise<UserDataResponse> {
25
- return await this.client.httpClient.get<UserDataResponse>(UserRoutes.getUser(username))
26
- }
27
-
28
- async list(filters: UserList) {
29
- const queryString = buildQueryString(filters as unknown as Record<string, unknown>)
30
- return await this.client.httpClient.get(UserRoutes.listUser(queryString))
31
- }
32
-
33
- async getUserApps(username: string): Promise<UserAppsResponse> {
34
- return await this.client.httpClient.get<UserAppsResponse>(UserRoutes.getUserApps(username))
35
- }
36
-
37
- // - createUser
38
- async createUser(userData: UserCreateRequest): Promise<UserCreateResponse> {
39
- return await this.client.httpClient.post<UserCreateResponse, UserCreateRequest>(
40
- UserRoutes.baseUrl,
41
- userData
42
- )
43
- }
44
-
45
- // - deleteUser
46
- async deleteUser(username: string): Promise<void> {
47
- return await this.client.httpClient.delete(UserRoutes.deleteUser(username))
48
- }
49
-
50
- // TODO: Implement user management methods
51
- // - token getter (access JWT token)
52
- }
@@ -1,111 +0,0 @@
1
- export interface UserCreateRequest {
2
- // Required fields
3
- username: string
4
- email: string
5
- first_name: string
6
- last_name: string
7
- password: string
8
- confirm_password: string
9
- // Optional fields
10
- is_active?: boolean
11
- is_staff?: boolean
12
- attributes?: string
13
- }
14
-
15
- export interface UserCreateResponse {
16
- id: number
17
- uuid: string
18
- username: string
19
- email: string
20
- first_name: string
21
- last_name: string
22
- is_active: boolean
23
- is_staff: boolean
24
- is_superuser: boolean
25
- is_deleted: boolean
26
- date_joined: string
27
- }
28
-
29
- export interface UserGroup {
30
- id: number
31
- name: string
32
- }
33
-
34
- export interface UserPermission {
35
- id: number
36
- name: string
37
- codename: string
38
- content_type: string // "app_label.model"
39
- }
40
-
41
- export interface UserRole {
42
- name: string
43
- slug: string
44
- type: "app_role"
45
- app_slug: string
46
- source: "direct" | "site_role" | "inherited"
47
- }
48
-
49
- export interface UserDataResponse {
50
- id: number
51
- username: string
52
- email: string
53
- first_name: string
54
- last_name: string
55
- full_name: string
56
- is_active: boolean
57
- is_staff: boolean
58
- is_superuser: boolean
59
- is_deleted: boolean
60
- date_joined: string // ISO 8601 date-time string
61
- last_login: string // ISO 8601 date-time string
62
- groups: UserGroup[]
63
- user_permissions: UserPermission[]
64
- attributes: Record<string, unknown>
65
- missing_attributes: string[]
66
- roles: UserRole[]
67
- }
68
-
69
- export interface UserUpdateRequest {
70
- username?: string
71
- email?: string
72
- first_name?: string
73
- last_name?: string
74
- is_active?: boolean
75
- is_staff?: boolean
76
- }
77
-
78
- export interface UserUpdateResponse {
79
- id: number
80
- uuid: string
81
- username: string
82
- email: string
83
- first_name: string
84
- last_name: string
85
- is_active: boolean
86
- is_staff: boolean
87
- is_superuser: boolean
88
- is_deleted: boolean
89
- date_joined: string
90
- }
91
-
92
- export interface UserList {
93
- search: string
94
- is_active: boolean
95
- is_staff: boolean
96
- is_superuser: boolean
97
- is_deleted: boolean
98
- ordering: string
99
- page: Number
100
- page_size: Number
101
- }
102
-
103
- export interface UserApp {
104
- name: string
105
- slug: string
106
- icon: string
107
- url: string
108
- display_name: string
109
- }
110
-
111
- export type UserAppsResponse = UserApp[]
File without changes