@ioka-technologies/asyncapi-ts-client-template 0.0.7 → 0.0.10
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/README.md +468 -122
- package/examples/auth-retry/README.md +384 -0
- package/examples/auth-retry/asyncapi.yaml +464 -0
- package/examples/auth-retry/login-flow-example.ts +375 -0
- package/package.json +3 -2
- package/template/helpers/security.js +137 -0
- package/template/src/client.ts.js +24 -2
- package/template/src/models.ts.js +157 -17
- package/template/src/runtime/auth/headers.ts.js +95 -0
- package/template/src/runtime/auth/index.ts.js +15 -0
- package/template/src/runtime/auth/types.ts.js +74 -0
- package/template/src/runtime/retry/index.ts.js +18 -0
- package/template/src/runtime/retry/manager.ts.js +150 -0
- package/template/src/runtime/retry/presets.ts.js +81 -0
- package/template/src/runtime/retry/types.ts.js +60 -0
- package/template/src/runtime/transports/http.ts.js +112 -17
- package/template/src/runtime/transports/websocket.ts.js +28 -3
- package/template/src/runtime/types.ts.js +12 -3
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Login Flow Example - JWT Token Management
|
|
3
|
+
*
|
|
4
|
+
* This example demonstrates how to handle a typical authentication flow:
|
|
5
|
+
* 1. Start with no authentication (or basic auth for login)
|
|
6
|
+
* 2. Call userLogin operation
|
|
7
|
+
* 3. Extract JWT token from response
|
|
8
|
+
* 4. Update client auth configuration
|
|
9
|
+
* 5. Make authenticated requests with the JWT
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import React, { useState, useCallback, useEffect } from 'react';
|
|
13
|
+
|
|
14
|
+
// These would be imported from your generated client
|
|
15
|
+
// import { UserServiceClient } from './generated-client';
|
|
16
|
+
// import { AuthError, UnauthorizedError, AuthCredentials } from './generated-client';
|
|
17
|
+
|
|
18
|
+
// For this example, we'll use placeholder types and classes
|
|
19
|
+
interface AuthCredentials {
|
|
20
|
+
jwt?: string;
|
|
21
|
+
basic?: {
|
|
22
|
+
username: string;
|
|
23
|
+
password: string;
|
|
24
|
+
};
|
|
25
|
+
apikey?: {
|
|
26
|
+
key: string;
|
|
27
|
+
location: 'header' | 'query';
|
|
28
|
+
name: string;
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
class UserServiceClient {
|
|
33
|
+
constructor(config: any) { }
|
|
34
|
+
async connect(): Promise<void> { }
|
|
35
|
+
async disconnect(): Promise<void> { }
|
|
36
|
+
async userLogin(payload: any): Promise<any> { return {}; }
|
|
37
|
+
async getUserProfile(payload: any): Promise<any> { return {}; }
|
|
38
|
+
async updateUserPreferences(payload: any): Promise<void> { }
|
|
39
|
+
userNotifications(callback: (notification: any) => void): () => void { return () => { }; }
|
|
40
|
+
updateAuth(auth: AuthCredentials): void { }
|
|
41
|
+
getAuth(): AuthCredentials | undefined { return {}; }
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
class AuthError extends Error {
|
|
45
|
+
constructor(message: string) {
|
|
46
|
+
super(message);
|
|
47
|
+
this.name = 'AuthError';
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
class UnauthorizedError extends Error {
|
|
52
|
+
constructor(message: string) {
|
|
53
|
+
super(message);
|
|
54
|
+
this.name = 'UnauthorizedError';
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Example login response type (this would be generated from your AsyncAPI spec)
|
|
59
|
+
interface LoginResponse {
|
|
60
|
+
token: string;
|
|
61
|
+
user: {
|
|
62
|
+
id: string;
|
|
63
|
+
email: string;
|
|
64
|
+
name: string;
|
|
65
|
+
};
|
|
66
|
+
expiresAt: string;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Example user profile type
|
|
70
|
+
interface UserProfile {
|
|
71
|
+
id: string;
|
|
72
|
+
email: string;
|
|
73
|
+
name: string;
|
|
74
|
+
preferences: {
|
|
75
|
+
theme: string;
|
|
76
|
+
notifications: boolean;
|
|
77
|
+
};
|
|
78
|
+
lastLoginAt: string;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async function loginFlowExample() {
|
|
82
|
+
// Step 1: Create client without authentication (for login)
|
|
83
|
+
const client = new UserServiceClient({
|
|
84
|
+
type: 'http',
|
|
85
|
+
url: 'https://api.example.com',
|
|
86
|
+
retry: 'balanced'
|
|
87
|
+
// No auth configuration initially
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
await client.connect();
|
|
91
|
+
|
|
92
|
+
try {
|
|
93
|
+
// Step 2: Perform login (this operation doesn't require auth)
|
|
94
|
+
console.log('Logging in...');
|
|
95
|
+
const loginResponse: LoginResponse = await client.userLogin({
|
|
96
|
+
email: 'user@example.com',
|
|
97
|
+
password: 'mypassword'
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
console.log('Login successful!', loginResponse.user);
|
|
101
|
+
|
|
102
|
+
// Step 3: Update client auth configuration with JWT token
|
|
103
|
+
client.updateAuth({
|
|
104
|
+
jwt: loginResponse.token
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
console.log('Auth updated with JWT token');
|
|
108
|
+
|
|
109
|
+
// Step 4: Now make authenticated requests
|
|
110
|
+
// The JWT token will be automatically included in all subsequent requests
|
|
111
|
+
const userProfile: UserProfile = await client.getUserProfile({
|
|
112
|
+
userId: loginResponse.user.id
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
console.log('User profile retrieved:', userProfile);
|
|
116
|
+
|
|
117
|
+
// Step 5: Make other authenticated operations
|
|
118
|
+
await client.updateUserPreferences({
|
|
119
|
+
userId: loginResponse.user.id,
|
|
120
|
+
preferences: {
|
|
121
|
+
theme: 'dark',
|
|
122
|
+
notifications: true
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
console.log('User preferences updated');
|
|
127
|
+
|
|
128
|
+
} catch (error) {
|
|
129
|
+
if (error instanceof UnauthorizedError) {
|
|
130
|
+
console.error('Authentication failed:', error.message);
|
|
131
|
+
// Handle login failure - show login form again
|
|
132
|
+
} else if (error instanceof AuthError) {
|
|
133
|
+
console.error('Auth configuration error:', error.message);
|
|
134
|
+
} else {
|
|
135
|
+
console.error('Unexpected error:', error);
|
|
136
|
+
}
|
|
137
|
+
} finally {
|
|
138
|
+
await client.disconnect();
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Advanced Login Flow with Token Refresh
|
|
144
|
+
*
|
|
145
|
+
* This example shows how to handle token refresh scenarios
|
|
146
|
+
*/
|
|
147
|
+
async function advancedLoginFlowExample() {
|
|
148
|
+
let currentToken: string | null = null;
|
|
149
|
+
let refreshToken: string | null = null;
|
|
150
|
+
|
|
151
|
+
const client = new UserServiceClient({
|
|
152
|
+
type: 'http',
|
|
153
|
+
url: 'https://api.example.com',
|
|
154
|
+
retry: 'balanced',
|
|
155
|
+
authCallbacks: {
|
|
156
|
+
onAuthError: async () => {
|
|
157
|
+
// Handle 401 errors by attempting token refresh
|
|
158
|
+
console.log('Auth error detected, attempting token refresh...');
|
|
159
|
+
|
|
160
|
+
if (refreshToken) {
|
|
161
|
+
try {
|
|
162
|
+
const refreshResponse = await refreshJwtToken(refreshToken);
|
|
163
|
+
currentToken = refreshResponse.token;
|
|
164
|
+
refreshToken = refreshResponse.refreshToken;
|
|
165
|
+
|
|
166
|
+
// Update the client's auth configuration
|
|
167
|
+
client.updateAuth({
|
|
168
|
+
jwt: currentToken || undefined
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
console.log('Token refreshed successfully');
|
|
172
|
+
return true; // Retry the original request
|
|
173
|
+
} catch (refreshError) {
|
|
174
|
+
console.error('Token refresh failed:', refreshError);
|
|
175
|
+
// Redirect to login page
|
|
176
|
+
return false;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return false; // No refresh token available
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
await client.connect();
|
|
186
|
+
|
|
187
|
+
try {
|
|
188
|
+
// Initial login
|
|
189
|
+
const loginResponse = await client.userLogin({
|
|
190
|
+
email: 'user@example.com',
|
|
191
|
+
password: 'mypassword'
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
currentToken = loginResponse.token;
|
|
195
|
+
refreshToken = loginResponse.refreshToken; // Assuming your API provides refresh tokens
|
|
196
|
+
|
|
197
|
+
// Set initial auth
|
|
198
|
+
client.updateAuth({
|
|
199
|
+
jwt: currentToken || undefined
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
// Make authenticated requests - token refresh will be handled automatically
|
|
203
|
+
const userProfile = await client.getUserProfile({
|
|
204
|
+
userId: loginResponse.user.id
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
console.log('User profile:', userProfile);
|
|
208
|
+
|
|
209
|
+
} catch (error) {
|
|
210
|
+
console.error('Login flow failed:', error);
|
|
211
|
+
} finally {
|
|
212
|
+
await client.disconnect();
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* WebSocket Login Flow Example
|
|
218
|
+
*
|
|
219
|
+
* For WebSocket connections, you typically need to reconnect after updating auth
|
|
220
|
+
*/
|
|
221
|
+
async function websocketLoginFlowExample() {
|
|
222
|
+
// Step 1: Create WebSocket client without auth
|
|
223
|
+
const client = new UserServiceClient({
|
|
224
|
+
type: 'websocket',
|
|
225
|
+
url: 'wss://api.example.com/ws',
|
|
226
|
+
retry: 'balanced'
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
await client.connect();
|
|
230
|
+
|
|
231
|
+
try {
|
|
232
|
+
// Step 2: Login via WebSocket
|
|
233
|
+
const loginResponse: LoginResponse = await client.userLogin({
|
|
234
|
+
email: 'user@example.com',
|
|
235
|
+
password: 'mypassword'
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
console.log('Login successful via WebSocket');
|
|
239
|
+
|
|
240
|
+
// Step 3: Disconnect and reconnect with auth
|
|
241
|
+
await client.disconnect();
|
|
242
|
+
|
|
243
|
+
client.updateAuth({
|
|
244
|
+
jwt: loginResponse.token
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
await client.connect(); // Reconnect with auth headers
|
|
248
|
+
|
|
249
|
+
// Step 4: Subscribe to authenticated events
|
|
250
|
+
const unsubscribe = client.userNotifications((notification) => {
|
|
251
|
+
console.log('Received notification:', notification);
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
// Step 5: Make authenticated requests
|
|
255
|
+
const userProfile = await client.getUserProfile({
|
|
256
|
+
userId: loginResponse.user.id
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
console.log('User profile:', userProfile);
|
|
260
|
+
|
|
261
|
+
// Clean up
|
|
262
|
+
setTimeout(() => {
|
|
263
|
+
unsubscribe();
|
|
264
|
+
client.disconnect();
|
|
265
|
+
}, 30000);
|
|
266
|
+
|
|
267
|
+
} catch (error) {
|
|
268
|
+
console.error('WebSocket login flow failed:', error);
|
|
269
|
+
await client.disconnect();
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* React Hook Example for Login Flow
|
|
275
|
+
*/
|
|
276
|
+
function useAuthenticatedClient() {
|
|
277
|
+
const [client, setClient] = useState(null as UserServiceClient | null);
|
|
278
|
+
const [isAuthenticated, setIsAuthenticated] = useState(false);
|
|
279
|
+
const [user, setUser] = useState(null as any);
|
|
280
|
+
|
|
281
|
+
const initializeClient = useCallback(() => {
|
|
282
|
+
const newClient = new UserServiceClient({
|
|
283
|
+
type: 'http',
|
|
284
|
+
url: process.env.REACT_APP_API_URL || 'https://api.example.com',
|
|
285
|
+
retry: 'balanced',
|
|
286
|
+
authCallbacks: {
|
|
287
|
+
onAuthError: async () => {
|
|
288
|
+
// Handle auth errors by clearing auth state
|
|
289
|
+
setIsAuthenticated(false);
|
|
290
|
+
setUser(null);
|
|
291
|
+
return false;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
newClient.connect();
|
|
297
|
+
setClient(newClient);
|
|
298
|
+
return newClient;
|
|
299
|
+
}, []);
|
|
300
|
+
|
|
301
|
+
const login = useCallback(async (email: string, password: string) => {
|
|
302
|
+
const currentClient = client || initializeClient();
|
|
303
|
+
|
|
304
|
+
try {
|
|
305
|
+
const loginResponse = await currentClient.userLogin({ email, password });
|
|
306
|
+
|
|
307
|
+
// Update auth configuration
|
|
308
|
+
currentClient.updateAuth({
|
|
309
|
+
jwt: loginResponse.token
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
setIsAuthenticated(true);
|
|
313
|
+
setUser(loginResponse.user);
|
|
314
|
+
|
|
315
|
+
return loginResponse;
|
|
316
|
+
} catch (error) {
|
|
317
|
+
console.error('Login failed:', error);
|
|
318
|
+
throw error;
|
|
319
|
+
}
|
|
320
|
+
}, [client, initializeClient]);
|
|
321
|
+
|
|
322
|
+
const logout = useCallback(async () => {
|
|
323
|
+
if (client) {
|
|
324
|
+
// Clear auth
|
|
325
|
+
client.updateAuth({});
|
|
326
|
+
await client.disconnect();
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
setIsAuthenticated(false);
|
|
330
|
+
setUser(null);
|
|
331
|
+
setClient(null);
|
|
332
|
+
}, [client]);
|
|
333
|
+
|
|
334
|
+
useEffect(() => {
|
|
335
|
+
return () => {
|
|
336
|
+
if (client) {
|
|
337
|
+
client.disconnect();
|
|
338
|
+
}
|
|
339
|
+
};
|
|
340
|
+
}, [client]);
|
|
341
|
+
|
|
342
|
+
return {
|
|
343
|
+
client,
|
|
344
|
+
isAuthenticated,
|
|
345
|
+
user,
|
|
346
|
+
login,
|
|
347
|
+
logout,
|
|
348
|
+
initializeClient
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// Helper function for token refresh (implement based on your API)
|
|
353
|
+
async function refreshJwtToken(refreshToken: string): Promise<{ token: string; refreshToken: string }> {
|
|
354
|
+
const response = await fetch('/api/auth/refresh', {
|
|
355
|
+
method: 'POST',
|
|
356
|
+
headers: {
|
|
357
|
+
'Content-Type': 'application/json',
|
|
358
|
+
},
|
|
359
|
+
body: JSON.stringify({ refreshToken })
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
if (!response.ok) {
|
|
363
|
+
throw new Error('Token refresh failed');
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
return response.json();
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
// Export examples
|
|
370
|
+
export {
|
|
371
|
+
loginFlowExample,
|
|
372
|
+
advancedLoginFlowExample,
|
|
373
|
+
websocketLoginFlowExample,
|
|
374
|
+
useAuthenticatedClient
|
|
375
|
+
};
|
package/package.json
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ioka-technologies/asyncapi-ts-client-template",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.10",
|
|
4
4
|
"description": "TypeScript AsyncAPI client generator template compatible with rust-asyncapi patterns",
|
|
5
5
|
"main": "template/index.js",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"test": "npm run test:generate",
|
|
8
|
-
"test:generate": "asyncapi generate fromTemplate examples/
|
|
8
|
+
"test:generate": "asyncapi generate fromTemplate examples/auth-retry/asyncapi.yaml . -o test-output-auth-retry --force-write -p enableAuth=true && cd test-output-auth-retry && npm install && npm run build",
|
|
9
|
+
"test:realworld": "asyncapi generate fromTemplate ../examples/realworld/asyncapi.yaml . -o test-output-realworld --force-write -p enableAuth=true",
|
|
9
10
|
"clean": "rm -rf test-output-*"
|
|
10
11
|
},
|
|
11
12
|
"keywords": [
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Security analysis helper functions for TypeScript client template
|
|
3
|
+
* Adapted from rust-server template helpers
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Analyzes operation security requirements from AsyncAPI specification
|
|
8
|
+
*
|
|
9
|
+
* @param {object} operation - AsyncAPI operation object
|
|
10
|
+
* @returns {object} Security analysis result
|
|
11
|
+
*/
|
|
12
|
+
export function analyzeOperationSecurity(operation) {
|
|
13
|
+
try {
|
|
14
|
+
// Check AsyncAPI security field
|
|
15
|
+
const security = operation.security && operation.security();
|
|
16
|
+
if (security && Array.isArray(security) && security.length > 0) {
|
|
17
|
+
return {
|
|
18
|
+
hasSecurityRequirements: true,
|
|
19
|
+
securitySchemes: security,
|
|
20
|
+
requiresAuthentication: true
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Check if operation has security defined in AsyncAPI spec
|
|
25
|
+
const operationJson = operation._json || operation;
|
|
26
|
+
if (operationJson.security && Array.isArray(operationJson.security) && operationJson.security.length > 0) {
|
|
27
|
+
return {
|
|
28
|
+
hasSecurityRequirements: true,
|
|
29
|
+
securitySchemes: operationJson.security,
|
|
30
|
+
requiresAuthentication: true
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return {
|
|
35
|
+
hasSecurityRequirements: false,
|
|
36
|
+
securitySchemes: [],
|
|
37
|
+
requiresAuthentication: false
|
|
38
|
+
};
|
|
39
|
+
} catch (e) {
|
|
40
|
+
return {
|
|
41
|
+
hasSecurityRequirements: false,
|
|
42
|
+
securitySchemes: [],
|
|
43
|
+
requiresAuthentication: false
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Checks if an operation has security requirements
|
|
50
|
+
*
|
|
51
|
+
* @param {object} operation - AsyncAPI operation object
|
|
52
|
+
* @returns {boolean} True if operation has security requirements
|
|
53
|
+
*/
|
|
54
|
+
export function operationRequiresAuth(operation) {
|
|
55
|
+
const analysis = analyzeOperationSecurity(operation);
|
|
56
|
+
return analysis.hasSecurityRequirements;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Checks if the AsyncAPI specification has security schemes defined
|
|
61
|
+
*
|
|
62
|
+
* @param {object} asyncapi - AsyncAPI specification object
|
|
63
|
+
* @returns {boolean} True if security schemes are present
|
|
64
|
+
*/
|
|
65
|
+
export function hasSecuritySchemes(asyncapi) {
|
|
66
|
+
try {
|
|
67
|
+
const components = asyncapi.components();
|
|
68
|
+
if (!components) return false;
|
|
69
|
+
|
|
70
|
+
const securitySchemes = components.securitySchemes();
|
|
71
|
+
return securitySchemes && Object.keys(securitySchemes).length > 0;
|
|
72
|
+
} catch (e) {
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Get security scheme type from AsyncAPI security scheme definition
|
|
79
|
+
*
|
|
80
|
+
* @param {object} securityScheme - AsyncAPI security scheme object
|
|
81
|
+
* @returns {string} Security scheme type ('jwt', 'basic', 'apikey', etc.)
|
|
82
|
+
*/
|
|
83
|
+
export function getSecuritySchemeType(securityScheme) {
|
|
84
|
+
try {
|
|
85
|
+
if (securityScheme.type && typeof securityScheme.type === 'function') {
|
|
86
|
+
return securityScheme.type();
|
|
87
|
+
}
|
|
88
|
+
if (securityScheme.type) {
|
|
89
|
+
return securityScheme.type;
|
|
90
|
+
}
|
|
91
|
+
if (securityScheme._json && securityScheme._json.type) {
|
|
92
|
+
return securityScheme._json.type;
|
|
93
|
+
}
|
|
94
|
+
return 'unknown';
|
|
95
|
+
} catch (e) {
|
|
96
|
+
return 'unknown';
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Extract security requirements for all operations in the AsyncAPI spec
|
|
102
|
+
*
|
|
103
|
+
* @param {object} asyncapi - AsyncAPI specification object
|
|
104
|
+
* @returns {object} Map of operation names to their security requirements
|
|
105
|
+
*/
|
|
106
|
+
export function extractOperationSecurityMap(asyncapi) {
|
|
107
|
+
const securityMap = {};
|
|
108
|
+
|
|
109
|
+
try {
|
|
110
|
+
const operations = asyncapi.operations && asyncapi.operations();
|
|
111
|
+
if (operations) {
|
|
112
|
+
// Handle AsyncAPI parser collection - use .all() method to get array
|
|
113
|
+
const operationArray = operations.all ? operations.all() : Object.values(operations);
|
|
114
|
+
|
|
115
|
+
operationArray.forEach((operation) => {
|
|
116
|
+
// Get operation ID
|
|
117
|
+
let operationId = null;
|
|
118
|
+
if (operation._meta && operation._meta.id) {
|
|
119
|
+
operationId = operation._meta.id;
|
|
120
|
+
} else if (operation.id && typeof operation.id === 'function') {
|
|
121
|
+
operationId = operation.id();
|
|
122
|
+
} else if (operation.id) {
|
|
123
|
+
operationId = operation.id;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (operationId) {
|
|
127
|
+
const securityAnalysis = analyzeOperationSecurity(operation);
|
|
128
|
+
securityMap[operationId] = securityAnalysis;
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
} catch (e) {
|
|
133
|
+
console.warn('Error extracting operation security map:', e.message);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return securityMap;
|
|
137
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/* eslint-disable no-unused-vars */
|
|
2
2
|
import { File } from '@asyncapi/generator-react-sdk';
|
|
3
|
+
import { operationRequiresAuth, extractOperationSecurityMap } from '../helpers/security.js';
|
|
3
4
|
|
|
4
5
|
function generateClient(asyncapi, clientName) {
|
|
5
6
|
// Method name sanitization function
|
|
@@ -9,7 +10,7 @@ function generateClient(asyncapi, clientName) {
|
|
|
9
10
|
// Convert to camelCase and remove invalid characters
|
|
10
11
|
let sanitized = operationId
|
|
11
12
|
// Replace dots, hyphens, underscores, spaces, and forward slashes with camelCase
|
|
12
|
-
.replace(/[.\-_\s
|
|
13
|
+
.replace(/[.\-_\s/]+(.)/g, (_, char) => char.toUpperCase())
|
|
13
14
|
// Remove any remaining invalid characters
|
|
14
15
|
.replace(/[^a-zA-Z0-9]/g, '')
|
|
15
16
|
// Ensure it starts with a lowercase letter if it starts with a number
|
|
@@ -42,6 +43,7 @@ function generateClient(asyncapi, clientName) {
|
|
|
42
43
|
|
|
43
44
|
let content = `import { TransportFactory } from './runtime/transports/factory';
|
|
44
45
|
import { Transport, TransportConfig, RequestOptions, MessageEnvelope } from './runtime/types';
|
|
46
|
+
import { AuthCredentials } from './runtime/auth/types';
|
|
45
47
|
import * as Models from './models';
|
|
46
48
|
|
|
47
49
|
export class ${clientName} {
|
|
@@ -70,6 +72,26 @@ export class ${clientName} {
|
|
|
70
72
|
this.transport.unsubscribe(channel, callback);
|
|
71
73
|
}
|
|
72
74
|
|
|
75
|
+
/**
|
|
76
|
+
* Update authentication configuration
|
|
77
|
+
* @param auth New authentication configuration
|
|
78
|
+
*/
|
|
79
|
+
updateAuth(auth: AuthCredentials): void {
|
|
80
|
+
this.config.auth = auth;
|
|
81
|
+
// If transport supports auth updates, update it
|
|
82
|
+
if (this.transport && typeof (this.transport as any).updateAuth === 'function') {
|
|
83
|
+
(this.transport as any).updateAuth(auth);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Get current authentication configuration
|
|
89
|
+
* @returns Current auth configuration
|
|
90
|
+
*/
|
|
91
|
+
getAuth(): AuthCredentials | undefined {
|
|
92
|
+
return this.config.auth;
|
|
93
|
+
}
|
|
94
|
+
|
|
73
95
|
// Generated operation methods
|
|
74
96
|
`;
|
|
75
97
|
|
|
@@ -374,4 +396,4 @@ module.exports = function ({ asyncapi, params }) {
|
|
|
374
396
|
{generatedContent}
|
|
375
397
|
</File>
|
|
376
398
|
);
|
|
377
|
-
}
|
|
399
|
+
};
|