@@ -9,9 +9,10 @@ import { format, resolveConfig } from 'prettier'
9
9
const rootClassPath = resolve ( 'src' , 'lib' , 'seam' , 'connect' , 'client.ts' )
10
10
const routeOutputPath = resolve ( 'src' , 'lib' , 'seam' , 'connect' , 'routes' )
11
11
12
- const routePaths : string [ ] = [
12
+ const routePaths = [
13
13
'/access_codes' ,
14
14
'/access_codes/unmanaged' ,
15
+ '/acs' ,
15
16
'/acs/access_groups' ,
16
17
'/acs/credentials' ,
17
18
'/acs/systems' ,
@@ -24,12 +25,23 @@ const routePaths: string[] = [
24
25
'/devices/unmanaged' ,
25
26
'/events' ,
26
27
'/locks' ,
28
+ '/noise_sensors' ,
27
29
'/noise_sensors/noise_thresholds' ,
28
30
'/thermostats/climate_setting_schedules' ,
29
31
'/thermostats' ,
30
32
'/webhooks' ,
31
33
'/workspaces' ,
32
- ]
34
+ ] as const
35
+
36
+ const routePathSubresources : Partial <
37
+ Record < ( typeof routePaths ) [ number ] , string [ ] >
38
+ > = {
39
+ '/access_codes' : [ 'unmanaged' ] ,
40
+ '/acs' : [ 'access_groups' , 'credentials' , 'systems' , 'users' ] ,
41
+ '/devices' : [ 'unmanaged' ] ,
42
+ '/noise_sensors' : [ 'noise_thresholds' ] ,
43
+ '/thermostats' : [ 'climate_setting_schedules' ] ,
44
+ }
33
45
34
46
const ignoredEndpointPaths = [
35
47
'/access_codes/simulate/create_unmanaged_access_code' ,
@@ -38,7 +50,7 @@ const ignoredEndpointPaths = [
38
50
'/health/get_service_health' ,
39
51
'/health/service/[service_name]' ,
40
52
'/noise_sensors/simulate/trigger_noise_threshold' ,
41
- ]
53
+ ] as const
42
54
43
55
const endpointResources : Partial <
44
56
Record <
@@ -61,11 +73,12 @@ const endpointResources: Partial<
61
73
'/noise_sensors/noise_thresholds/update' : null ,
62
74
'/thermostats/climate_setting_schedules/update' : null ,
63
75
'/workspaces/reset_sandbox' : null ,
64
- }
76
+ } as const
65
77
66
78
interface Route {
67
79
namespace : string
68
80
endpoints : Endpoint [ ]
81
+ subresources : string [ ]
69
82
}
70
83
71
84
interface Endpoint {
@@ -91,7 +104,9 @@ const createRoutes = (): Route[] => {
91
104
( path ) =>
92
105
! routePaths . some ( ( routePath ) => isEndpointUnderRoute ( path , routePath ) ) ,
93
106
)
94
- . filter ( ( path ) => ! ignoredEndpointPaths . includes ( path ) )
107
+ . filter (
108
+ ( path ) => ! ( ignoredEndpointPaths as unknown as string [ ] ) . includes ( path ) ,
109
+ )
95
110
96
111
if ( unmatchedEndpointPaths . length > 0 ) {
97
112
// eslint-disable-next-line no-console
@@ -105,7 +120,7 @@ const createRoutes = (): Route[] => {
105
120
return routePaths . map ( createRoute )
106
121
}
107
122
108
- const createRoute = ( routePath : string ) : Route => {
123
+ const createRoute = ( routePath : ( typeof routePaths ) [ number ] ) : Route => {
109
124
const endpointPaths = Object . keys ( openapi . paths ) . filter ( ( path ) =>
110
125
isEndpointUnderRoute ( path , routePath ) ,
111
126
)
@@ -114,6 +129,7 @@ const createRoute = (routePath: string): Route => {
114
129
115
130
return {
116
131
namespace,
132
+ subresources : routePathSubresources [ routePath ] ?? [ ] ,
117
133
endpoints : endpointPaths . map ( ( endpointPath ) =>
118
134
createEndpoint ( namespace , routePath , endpointPath ) ,
119
135
) ,
@@ -207,14 +223,14 @@ const renderRoute = (route: Route, { constructors }: ClassMeta): string => `
207
223
* Do not edit this file or add other files to this directory.
208
224
*/
209
225
210
- ${ renderImports ( ) }
226
+ ${ renderImports ( route ) }
211
227
212
228
${ renderClass ( route , { constructors } ) }
213
229
214
230
${ renderExports ( route ) }
215
231
`
216
232
217
- const renderImports = ( ) : string =>
233
+ const renderImports = ( { namespace , subresources } : Route ) : string =>
218
234
`
219
235
import type { RouteRequestParams, RouteResponse, RouteRequestBody } from '@seamapi/types/connect'
220
236
import { Axios } from 'axios'
@@ -232,10 +248,21 @@ import {
232
248
type SeamHttpOptionsWithClientSessionToken,
233
249
} from 'lib/seam/connect/client-options.js'
234
250
import { parseOptions } from 'lib/seam/connect/parse-options.js'
251
+ ${ subresources
252
+ . map ( ( subresource ) => renderSubresourceImport ( subresource , namespace ) )
253
+ . join ( '\n' ) }
254
+ `
255
+ const renderSubresourceImport = (
256
+ subresource : string ,
257
+ namespace : string ,
258
+ ) : string => `
259
+ import {
260
+ SeamHttp${ pascalCase ( namespace ) } ${ pascalCase ( subresource ) }
261
+ } from './${ paramCase ( namespace ) } -${ paramCase ( subresource ) } .js'
235
262
`
236
263
237
264
const renderClass = (
238
- { namespace, endpoints } : Route ,
265
+ { namespace, endpoints, subresources } : Route ,
239
266
{ constructors } : ClassMeta ,
240
267
) : string =>
241
268
`
@@ -247,6 +274,10 @@ export class SeamHttp${pascalCase(namespace)} {
247
274
. replaceAll ( ': SeamHttp ' , `: SeamHttp${ pascalCase ( namespace ) } ` )
248
275
. replaceAll ( 'new SeamHttp(' , `new SeamHttp${ pascalCase ( namespace ) } (` ) }
249
276
277
+ ${ subresources
278
+ . map ( ( subresource ) => renderSubresourceMethod ( subresource , namespace ) )
279
+ . join ( '\n' ) }
280
+
250
281
${ endpoints . map ( renderClassMethod ) . join ( '\n' ) }
251
282
}
252
283
`
@@ -287,6 +318,19 @@ const renderClassMethod = ({
287
318
}
288
319
`
289
320
321
+ const renderSubresourceMethod = (
322
+ subresource : string ,
323
+ namespace : string ,
324
+ ) : string => `
325
+ get ${ camelCase ( subresource ) } (): SeamHttp${ pascalCase (
326
+ namespace ,
327
+ ) } ${ pascalCase ( subresource ) } {
328
+ return SeamHttp${ pascalCase ( namespace ) } ${ pascalCase (
329
+ subresource ,
330
+ ) } .fromClient(this.client)
331
+ }
332
+ `
333
+
290
334
const renderExports = ( route : Route ) : string =>
291
335
route . endpoints . map ( renderEndpointExports ) . join ( '\n' )
292
336
@@ -322,17 +366,6 @@ const renderResponseType = ({
322
366
} : Pick < Endpoint , 'name' | 'namespace' > ) : string =>
323
367
[ pascalCase ( namespace ) , pascalCase ( name ) , 'Response' ] . join ( '' )
324
368
325
- const write = async ( data : string , ...path : string [ ] ) : Promise < void > => {
326
- const filePath = resolve ( ...path )
327
- await writeFile (
328
- filePath ,
329
- '// Generated empty file to allow ESLint parsing by filename' ,
330
- )
331
- const fixedOutput = await eslintFixOutput ( data , filePath )
332
- const prettyOutput = await prettierOutput ( fixedOutput , filePath )
333
- await writeFile ( filePath , prettyOutput )
334
- }
335
-
336
369
const getClassConstructors = ( data : string ) : string => {
337
370
const lines = data . split ( '\n' )
338
371
@@ -351,6 +384,34 @@ const getClassConstructors = (data: string): string => {
351
384
return lines . slice ( startIdx , endIdx ) . join ( '\n' )
352
385
}
353
386
387
+ const writeRoute = async ( route : Route ) : Promise < void > => {
388
+ const rootClass = await readFile ( rootClassPath )
389
+ const constructors = getClassConstructors ( rootClass . toString ( ) )
390
+ await write (
391
+ renderRoute ( route , { constructors } ) ,
392
+ routeOutputPath ,
393
+ `${ paramCase ( route . namespace ) } .ts` ,
394
+ )
395
+ }
396
+
397
+ const writeRoutesIndex = async ( routes : Route [ ] ) : Promise < void > => {
398
+ const exports = routes . map (
399
+ ( route ) => `export * from './${ paramCase ( route . namespace ) } .js'` ,
400
+ )
401
+ await write ( exports . join ( '\n' ) , routeOutputPath , `index.ts` )
402
+ }
403
+
404
+ const write = async ( data : string , ...path : string [ ] ) : Promise < void > => {
405
+ const filePath = resolve ( ...path )
406
+ await writeFile (
407
+ filePath ,
408
+ '// Generated empty file to allow ESLint parsing by filename' ,
409
+ )
410
+ const fixedOutput = await eslintFixOutput ( data , filePath )
411
+ const prettyOutput = await prettierOutput ( fixedOutput , filePath )
412
+ await writeFile ( filePath , prettyOutput )
413
+ }
414
+
354
415
const prettierOutput = async (
355
416
data : string ,
356
417
filepath : string ,
@@ -387,14 +448,6 @@ const eslintFixOutput = async (
387
448
return linted . output ?? linted . source ?? data
388
449
}
389
450
390
- const writeRoute = async ( route : Route ) : Promise < void > => {
391
- const rootClass = await readFile ( rootClassPath )
392
- const constructors = getClassConstructors ( rootClass . toString ( ) )
393
- await write (
394
- renderRoute ( route , { constructors } ) ,
395
- routeOutputPath ,
396
- `${ paramCase ( route . namespace ) } .ts` ,
397
- )
398
- }
399
-
400
- await Promise . all ( createRoutes ( ) . map ( writeRoute ) )
451
+ const routes = createRoutes ( )
452
+ await Promise . all ( routes . map ( writeRoute ) )
453
+ await writeRoutesIndex ( routes )
0 commit comments