@scalekit-sdk/node 2.0.1 → 2.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.
Files changed (77) hide show
  1. package/jest.config.js +15 -0
  2. package/lib/core.d.ts +1 -1
  3. package/lib/core.js +31 -31
  4. package/lib/core.js.map +1 -1
  5. package/lib/errors/base-exception.d.ts +32 -0
  6. package/lib/errors/base-exception.js +238 -0
  7. package/lib/errors/base-exception.js.map +1 -0
  8. package/lib/errors/index.d.ts +2 -0
  9. package/lib/errors/index.js +20 -0
  10. package/lib/errors/index.js.map +1 -0
  11. package/lib/errors/specific-exceptions.d.ts +39 -0
  12. package/lib/errors/specific-exceptions.js +90 -0
  13. package/lib/errors/specific-exceptions.js.map +1 -0
  14. package/lib/index.d.ts +1 -0
  15. package/lib/index.js +1 -0
  16. package/lib/index.js.map +1 -1
  17. package/lib/pkg/grpc/scalekit/v1/commons/commons_pb.d.ts +34 -87
  18. package/lib/pkg/grpc/scalekit/v1/commons/commons_pb.js +31 -120
  19. package/lib/pkg/grpc/scalekit/v1/commons/commons_pb.js.map +1 -1
  20. package/lib/pkg/grpc/scalekit/v1/connections/connections_connect.d.ts +19 -10
  21. package/lib/pkg/grpc/scalekit/v1/connections/connections_connect.js +18 -9
  22. package/lib/pkg/grpc/scalekit/v1/connections/connections_connect.js.map +1 -1
  23. package/lib/pkg/grpc/scalekit/v1/connections/connections_pb.d.ts +209 -6
  24. package/lib/pkg/grpc/scalekit/v1/connections/connections_pb.js +272 -5
  25. package/lib/pkg/grpc/scalekit/v1/connections/connections_pb.js.map +1 -1
  26. package/lib/pkg/grpc/scalekit/v1/domains/domains_pb.d.ts +29 -0
  27. package/lib/pkg/grpc/scalekit/v1/domains/domains_pb.js +40 -1
  28. package/lib/pkg/grpc/scalekit/v1/domains/domains_pb.js.map +1 -1
  29. package/lib/pkg/grpc/scalekit/v1/errdetails/errdetails_pb.d.ts +25 -0
  30. package/lib/pkg/grpc/scalekit/v1/errdetails/errdetails_pb.js +38 -1
  31. package/lib/pkg/grpc/scalekit/v1/errdetails/errdetails_pb.js.map +1 -1
  32. package/lib/pkg/grpc/scalekit/v1/organizations/organizations_connect.d.ts +21 -1
  33. package/lib/pkg/grpc/scalekit/v1/organizations/organizations_connect.js +20 -0
  34. package/lib/pkg/grpc/scalekit/v1/organizations/organizations_connect.js.map +1 -1
  35. package/lib/pkg/grpc/scalekit/v1/organizations/organizations_pb.d.ts +110 -5
  36. package/lib/pkg/grpc/scalekit/v1/organizations/organizations_pb.js +164 -5
  37. package/lib/pkg/grpc/scalekit/v1/organizations/organizations_pb.js.map +1 -1
  38. package/lib/pkg/grpc/scalekit/v1/users/users_connect.d.ts +48 -1
  39. package/lib/pkg/grpc/scalekit/v1/users/users_connect.js +47 -0
  40. package/lib/pkg/grpc/scalekit/v1/users/users_connect.js.map +1 -1
  41. package/lib/pkg/grpc/scalekit/v1/users/users_pb.d.ts +280 -4
  42. package/lib/pkg/grpc/scalekit/v1/users/users_pb.js +449 -11
  43. package/lib/pkg/grpc/scalekit/v1/users/users_pb.js.map +1 -1
  44. package/lib/scalekit.d.ts +3 -3
  45. package/lib/scalekit.js +35 -22
  46. package/lib/scalekit.js.map +1 -1
  47. package/lib/types/user.d.ts +1 -1
  48. package/lib/user.d.ts +10 -3
  49. package/lib/user.js +26 -5
  50. package/lib/user.js.map +1 -1
  51. package/package.json +6 -2
  52. package/src/core.ts +31 -32
  53. package/src/errors/base-exception.ts +262 -0
  54. package/src/errors/index.ts +3 -0
  55. package/src/errors/specific-exceptions.ts +88 -0
  56. package/src/index.ts +3 -1
  57. package/src/pkg/grpc/scalekit/v1/commons/commons_pb.ts +49 -129
  58. package/src/pkg/grpc/scalekit/v1/connections/connections_connect.ts +19 -10
  59. package/src/pkg/grpc/scalekit/v1/connections/connections_pb.ts +377 -8
  60. package/src/pkg/grpc/scalekit/v1/domains/domains_pb.ts +44 -0
  61. package/src/pkg/grpc/scalekit/v1/errdetails/errdetails_pb.ts +49 -0
  62. package/src/pkg/grpc/scalekit/v1/organizations/organizations_connect.ts +21 -1
  63. package/src/pkg/grpc/scalekit/v1/organizations/organizations_pb.ts +218 -5
  64. package/src/pkg/grpc/scalekit/v1/users/users_connect.ts +48 -1
  65. package/src/pkg/grpc/scalekit/v1/users/users_pb.ts +558 -6
  66. package/src/scalekit.ts +39 -23
  67. package/src/types/user.ts +1 -1
  68. package/src/user.ts +34 -7
  69. package/tests/README.md +25 -0
  70. package/tests/connection.test.ts +42 -0
  71. package/tests/directory.test.ts +46 -0
  72. package/tests/organization.test.ts +65 -0
  73. package/tests/passwordless.test.ts +108 -0
  74. package/tests/scalekit.test.ts +104 -0
  75. package/tests/setup.ts +34 -0
  76. package/tests/users.test.ts +168 -0
  77. package/tests/utils/test-data.ts +248 -0
@@ -0,0 +1,168 @@
1
+ import ScalekitClient from '../src/scalekit';
2
+ import { CreateUserRequest, UpdateUserRequest } from '../src/types/user';
3
+ import { describe, it, expect, beforeEach, afterEach } from '@jest/globals';
4
+ import { TestDataGenerator, TestOrganizationManager, TestUserManager } from './utils/test-data';
5
+
6
+ describe('Users', () => {
7
+ let client: ScalekitClient;
8
+ let testOrg: string;
9
+ let userId: string | null = null;
10
+ let sharedUserData: CreateUserRequest;
11
+
12
+ beforeEach(async () => {
13
+ // Use global client
14
+ client = global.client;
15
+
16
+ // Create test organization for each test
17
+ testOrg = await TestOrganizationManager.createTestOrganization(client);
18
+
19
+ // Create a shared user for testing
20
+ sharedUserData = TestDataGenerator.generateUserData();
21
+ const createResponse = await client.user.createUserAndMembership(testOrg, sharedUserData);
22
+ userId = createResponse.user?.id || null;
23
+ expect(userId).toBeDefined();
24
+ });
25
+
26
+ afterEach(async () => {
27
+ // Clean up test resources
28
+ if (userId) {
29
+ await TestUserManager.cleanupTestUser(client, testOrg, userId);
30
+ userId = null;
31
+ }
32
+
33
+ // Clean up test organization
34
+ await TestOrganizationManager.cleanupTestOrganization(client, testOrg);
35
+ });
36
+
37
+ describe('listOrganizationUsers', () => {
38
+ it('should list users by organization', async () => {
39
+ // List users in the organization
40
+ const usersList = await client.user.listOrganizationUsers(testOrg, TestDataGenerator.generatePaginationParams());
41
+
42
+ expect(usersList).toBeDefined();
43
+ expect(usersList.users).toBeDefined();
44
+ expect(Array.isArray(usersList.users)).toBe(true);
45
+ expect(usersList.users.length).toBeGreaterThan(0);
46
+
47
+ // Verify basic user attributes
48
+ const firstUser = usersList.users[0];
49
+ expect(firstUser.id).toBeDefined();
50
+ expect(firstUser.email).toBeDefined();
51
+ expect(firstUser.environmentId).toBeDefined();
52
+ });
53
+
54
+ it('should handle pagination', async () => {
55
+ const firstPage = await client.user.listOrganizationUsers(testOrg, TestDataGenerator.generatePaginationParams(5));
56
+
57
+ expect(firstPage).toBeDefined();
58
+ expect(firstPage.users.length).toBeLessThanOrEqual(5);
59
+
60
+ if (firstPage.nextPageToken) {
61
+ const secondPage = await client.user.listOrganizationUsers(testOrg, {
62
+ pageSize: 5,
63
+ pageToken: firstPage.nextPageToken
64
+ });
65
+
66
+ expect(secondPage).toBeDefined();
67
+ expect(secondPage.users).toBeDefined();
68
+ }
69
+ });
70
+ });
71
+
72
+ describe('getUser', () => {
73
+ it('should get user by ID', async () => {
74
+ // Retrieve the user by ID
75
+ const user = await client.user.getUser(userId!);
76
+
77
+ expect(user).toBeDefined();
78
+ expect(user.user).toBeDefined();
79
+ expect(user.user?.id).toBe(userId);
80
+ expect(user.user?.email).toBe(sharedUserData.email);
81
+ });
82
+ });
83
+
84
+ describe('createUserAndMembership', () => {
85
+ it('should create user and membership', async () => {
86
+ // Create a new user for this specific test
87
+ const userData = TestDataGenerator.generateUserData();
88
+ let newUserId: string | null = null;
89
+
90
+ try {
91
+ const response = await client.user.createUserAndMembership(testOrg, userData);
92
+
93
+ expect(response).toBeDefined();
94
+ expect(response.user).toBeDefined();
95
+ expect(response.user?.id).toBeDefined();
96
+ expect(response.user?.email).toBe(userData.email);
97
+ expect(response.user?.metadata?.source).toBe('test');
98
+
99
+ newUserId = response.user?.id || null;
100
+ } finally {
101
+ // Clean up the new user created in this test
102
+ if (newUserId) {
103
+ await TestUserManager.cleanupTestUser(client, testOrg, newUserId);
104
+ }
105
+ }
106
+ });
107
+
108
+ it('should throw error when email is missing', async () => {
109
+ const userData = TestDataGenerator.generateUserData({ email: '' }); // Empty email
110
+
111
+ await expect(
112
+ client.user.createUserAndMembership(testOrg, userData)
113
+ ).rejects.toThrow('email is required');
114
+ });
115
+
116
+ it('should throw error when organizationId is missing', async () => {
117
+ const userData = TestDataGenerator.generateUserData({ email: 'test@example.com' });
118
+
119
+ await expect(
120
+ client.user.createUserAndMembership('', userData)
121
+ ).rejects.toThrow('organizationId is required');
122
+ });
123
+ });
124
+
125
+ describe('updateUser', () => {
126
+ it('should update user', async () => {
127
+ // Modify the shared user
128
+ const updateData = TestDataGenerator.generateUserUpdateData();
129
+
130
+ const updatedUser = await client.user.updateUser(userId!, updateData);
131
+
132
+ expect(updatedUser).toBeDefined();
133
+ expect(updatedUser.user).toBeDefined();
134
+ expect(updatedUser.user?.id).toBe(userId);
135
+ expect(updatedUser.user?.userProfile?.firstName).toBe('Updated');
136
+ expect(updatedUser.user?.userProfile?.lastName).toBe('Name');
137
+ });
138
+ });
139
+
140
+ describe('resendInvite', () => {
141
+ it('should resend invite to user', async () => {
142
+ // Resend invite to the shared user
143
+ const resendResponse = await client.user.resendInvite(testOrg, userId!);
144
+
145
+ // Verify the response structure
146
+ expect(resendResponse).toBeDefined();
147
+ expect(resendResponse.invite).toBeDefined();
148
+ expect(resendResponse.invite?.userId).toBe(userId);
149
+ expect(resendResponse.invite?.organizationId).toBe(testOrg);
150
+ expect(resendResponse.invite?.status).toBe('PENDING_INVITE');
151
+ expect(resendResponse.invite?.createdAt).toBeDefined();
152
+ expect(resendResponse.invite?.expiresAt).toBeDefined();
153
+ expect(resendResponse.invite?.resentCount).toBe(1);
154
+ });
155
+
156
+ it('should throw error when organizationId is missing', async () => {
157
+ await expect(
158
+ client.user.resendInvite('', userId!)
159
+ ).rejects.toThrow('organizationId is required');
160
+ });
161
+
162
+ it('should throw error when userId is missing', async () => {
163
+ await expect(
164
+ client.user.resendInvite(testOrg, '')
165
+ ).rejects.toThrow('userId is required');
166
+ });
167
+ });
168
+ });
@@ -0,0 +1,248 @@
1
+ import { CreateUserRequest, UpdateUserRequest } from '../../src/types/user';
2
+ import { TemplateType } from '../../src/pkg/grpc/scalekit/v1/auth/passwordless_pb';
3
+
4
+ /**
5
+ * Test data generation utilities to reduce redundancy across test files
6
+ */
7
+
8
+ export class TestDataGenerator {
9
+ /**
10
+ * Generate a unique timestamp-based identifier
11
+ */
12
+ static generateUniqueId(): string {
13
+ return Date.now().toString();
14
+ }
15
+
16
+ /**
17
+ * Generate a unique email address for testing
18
+ */
19
+ static generateUniqueEmail(): string {
20
+ return `test.user.${this.generateUniqueId()}@example.com`;
21
+ }
22
+
23
+ /**
24
+ * Generate test organization data
25
+ */
26
+ static generateOrganizationData() {
27
+ const uniqueId = this.generateUniqueId();
28
+ return {
29
+ name: `Test Org ${uniqueId}`,
30
+ externalId: `ext_org_${uniqueId}`
31
+ };
32
+ }
33
+
34
+ /**
35
+ * Generate test user data
36
+ */
37
+ static generateUserData(overrides: Partial<CreateUserRequest> = {}): CreateUserRequest {
38
+ const uniqueEmail = this.generateUniqueEmail();
39
+
40
+ return {
41
+ email: uniqueEmail,
42
+ userProfile: {
43
+ firstName: 'Test',
44
+ lastName: 'User'
45
+ },
46
+ metadata: {
47
+ source: 'test'
48
+ },
49
+ ...overrides
50
+ };
51
+ }
52
+
53
+ /**
54
+ * Generate test user update data
55
+ */
56
+ static generateUserUpdateData(overrides: Partial<UpdateUserRequest> = {}): UpdateUserRequest {
57
+ return {
58
+ userProfile: {
59
+ firstName: 'Updated',
60
+ lastName: 'Name'
61
+ },
62
+ ...overrides
63
+ };
64
+ }
65
+
66
+ /**
67
+ * Generate test passwordless email data
68
+ */
69
+ static generatePasswordlessEmailData(overrides: any = {}) {
70
+ return {
71
+ template: TemplateType.SIGNIN,
72
+ state: 'test-state',
73
+ expiresIn: 3600,
74
+ magiclinkAuthUri: 'https://example.com/auth/callback',
75
+ ...overrides
76
+ };
77
+ }
78
+
79
+ /**
80
+ * Generate test passwordless email with template variables
81
+ */
82
+ static generatePasswordlessEmailWithTemplateData(overrides: any = {}) {
83
+ return {
84
+ template: TemplateType.SIGNUP,
85
+ templateVariables: {
86
+ companyName: 'Test Company',
87
+ appName: 'Test App'
88
+ },
89
+ magiclinkAuthUri: 'https://example.com/auth/callback',
90
+ ...overrides
91
+ };
92
+ }
93
+
94
+ /**
95
+ * Generate test webhook data for verification
96
+ */
97
+ static generateWebhookData() {
98
+ const secret = 'whsec_test-secret';
99
+ const payload = '{"test": "data"}';
100
+ const timestamp = Math.floor(Date.now() / 1000).toString();
101
+ const webhookId = 'msg_test_webhook_id';
102
+
103
+ // Generate valid signature for testing
104
+ const crypto = require('crypto');
105
+ const data = `${webhookId}.${timestamp}.${payload}`;
106
+ const hmac = crypto.createHmac('sha256', Buffer.from('test-secret', 'base64'));
107
+ hmac.update(data);
108
+ const computedSignature = hmac.digest('base64');
109
+ const signature = `v1,${computedSignature}`;
110
+
111
+ return {
112
+ secret,
113
+ payload,
114
+ timestamp,
115
+ webhookId,
116
+ signature,
117
+ headers: {
118
+ 'webhook-id': webhookId,
119
+ 'webhook-timestamp': timestamp,
120
+ 'webhook-signature': signature
121
+ }
122
+ };
123
+ }
124
+
125
+ /**
126
+ * Generate test authorization URL options
127
+ */
128
+ static generateAuthorizationUrlOptions(overrides: any = {}) {
129
+ return {
130
+ scopes: ['openid', 'profile'],
131
+ state: 'test-state',
132
+ nonce: 'test-nonce',
133
+ prompt: 'login',
134
+ ...overrides
135
+ };
136
+ }
137
+
138
+ /**
139
+ * Generate test PKCE parameters
140
+ */
141
+ static generatePKCEParams() {
142
+ return {
143
+ codeChallenge: 'test-challenge',
144
+ codeChallengeMethod: 'S256'
145
+ };
146
+ }
147
+
148
+ /**
149
+ * Generate test pagination parameters
150
+ */
151
+ static generatePaginationParams(pageSize: number = 10) {
152
+ return {
153
+ pageSize,
154
+ pageToken: ''
155
+ };
156
+ }
157
+
158
+ /**
159
+ * Generate test credential data for passwordless verification
160
+ */
161
+ static generateCredentialData(type: 'code' | 'linkToken' = 'code') {
162
+ if (type === 'code') {
163
+ return { code: 'mock-code' };
164
+ } else {
165
+ return { linkToken: 'mock-link-token' };
166
+ }
167
+ }
168
+ }
169
+
170
+ /**
171
+ * Test organization management utilities
172
+ */
173
+ export class TestOrganizationManager {
174
+ /**
175
+ * Create a test organization and return its ID
176
+ */
177
+ static async createTestOrganization(client: any): Promise<string> {
178
+ const orgData = TestDataGenerator.generateOrganizationData();
179
+ const orgResponse = await client.organization.createOrganization(
180
+ orgData.name,
181
+ { externalId: orgData.externalId }
182
+ );
183
+
184
+ const testOrg = orgResponse.organization?.id || '';
185
+ if (!testOrg) {
186
+ throw new Error('Failed to create test organization');
187
+ }
188
+
189
+ return testOrg;
190
+ }
191
+
192
+ /**
193
+ * Clean up a test organization
194
+ */
195
+ static async cleanupTestOrganization(client: any, testOrg: string): Promise<void> {
196
+ if (testOrg) {
197
+ try {
198
+ await client.organization.deleteOrganization(testOrg);
199
+ } catch (error) {
200
+ // Organization may already be deleted
201
+ }
202
+ }
203
+ }
204
+ }
205
+
206
+ /**
207
+ * Test user management utilities
208
+ */
209
+ export class TestUserManager {
210
+ /**
211
+ * Create a test user and return user data
212
+ */
213
+ static async createTestUser(client: any, testOrg: string, overrides: Partial<CreateUserRequest> = {}) {
214
+ const userData = TestDataGenerator.generateUserData(overrides);
215
+ const createResponse = await client.user.createUserAndMembership(testOrg, userData);
216
+ const createdUserId = createResponse.user?.id;
217
+
218
+ if (!createdUserId) {
219
+ throw new Error('Failed to create test user');
220
+ }
221
+
222
+ return {
223
+ userId: createdUserId,
224
+ userData,
225
+ response: createResponse
226
+ };
227
+ }
228
+
229
+ /**
230
+ * Clean up a test user
231
+ */
232
+ static async cleanupTestUser(client: any, testOrg: string, userId: string): Promise<void> {
233
+ if (userId) {
234
+ try {
235
+ // Remove membership if it exists
236
+ await client.user.deleteMembership(testOrg, userId);
237
+ } catch (error) {
238
+ // Membership may not exist
239
+ }
240
+
241
+ try {
242
+ await client.user.deleteUser(userId);
243
+ } catch (error) {
244
+ // User may already be deleted
245
+ }
246
+ }
247
+ }
248
+ }