Skip to content

Commit 64cbf3e

Browse files
committed
Use openapi spec to find return resource
1 parent e0651c8 commit 64cbf3e

File tree

5 files changed

+40
-71
lines changed

5 files changed

+40
-71
lines changed

generate-routes.ts

Lines changed: 34 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import { resolve } from 'node:path'
44
import { openapi } from '@seamapi/types/connect'
55
import { camelCase, paramCase, pascalCase, snakeCase } from 'change-case'
66
import { ESLint } from 'eslint'
7-
import pluralize from 'pluralize'
87
import { format, resolveConfig } from 'prettier'
98

109
const rootClassPath = resolve('src', 'lib', 'seam', 'connect', 'client.ts')
@@ -53,24 +52,14 @@ const endpointResources: Partial<
5352
| null
5453
>
5554
> = {
56-
'/access_codes/generate_code': 'generated_code',
57-
'/access_codes/create_multiple': 'access_codes',
58-
'/access_codes/pull_backup_access_code': 'backup_access_code',
59-
'/access_codes/unmanaged/convert_to_managed': null,
60-
'/acs/access_groups/list_users': 'acs_users',
61-
'/acs/access_groups/remove_user': null,
62-
'/acs/users/add_to_access_group': null,
63-
'/acs/users/remove_from_access_group': null,
55+
'/access_codes/delete': null,
56+
'/access_codes/unmanaged/delete': null,
57+
'/access_codes/update': null,
6458
'/connect_webviews/view': null,
65-
'/devices/list_device_providers': 'device_providers',
66-
'/locks/lock_door': 'action_attempt',
67-
'/locks/unlock_door': 'action_attempt',
68-
'/noise_sensors/noise_thresholds/create': null, // could return action action_attempt
69-
'/thermostats/cool': null,
70-
'/thermostats/heat': null,
71-
'/thermostats/heat_cool': null,
72-
'/thermostats/off': null,
73-
'/thermostats/set_fan_mode': null,
59+
'/noise_sensors/noise_thresholds/create': null,
60+
'/noise_sensors/noise_thresholds/delete': null,
61+
'/noise_sensors/noise_thresholds/update': null,
62+
'/thermostats/climate_setting_schedules/update': null,
7463
'/workspaces/reset_sandbox': null,
7564
}
7665

@@ -88,7 +77,7 @@ interface Endpoint {
8877
requestFormat: 'params' | 'body'
8978
}
9079

91-
type Method = 'GET' | 'POST' | 'DELETE' | 'PUT' | 'PATCH'
80+
type Method = 'GET' | 'POST'
9281

9382
interface ClassMeta {
9483
constructors: string
@@ -150,46 +139,48 @@ const createEndpoint = (
150139
namespace,
151140
path: endpointPath,
152141
method,
153-
resource: deriveResource(endpointPath, routePath, name, method),
142+
resource: deriveResource(endpointPath, method),
154143
requestFormat: ['GET', 'DELETE'].includes(method) ? 'params' : 'body',
155144
}
156145
}
157146

158147
const deriveResource = (
159148
endpointPath: string,
160-
routePath: string,
161-
name: string,
162149
method: Method,
163150
): string | null => {
164151
if (isEndpointResource(endpointPath)) {
165152
return endpointResources[endpointPath] ?? null
166153
}
167-
if (['DELETE', 'PATCH', 'PUT'].includes(method)) return null
168-
if (['update', 'delete'].includes(name)) return null
169-
const group = deriveGroupFromRoutePath(routePath)
170-
if (group == null) throw new Error(`Could not parse group from ${routePath}`)
171-
if (name === 'list') return group
172-
return pluralize.singular(group)
173-
}
174-
175-
const deriveGroupFromRoutePath = (routePath: string): string | undefined => {
176-
const parts = routePath.split('/').slice(1)
177-
178-
if (routePath.endsWith('/unmanaged')) {
179-
return parts[0]
180-
}
181-
182-
if (routePath.startsWith('/acs')) {
183-
return [parts[0], parts[1]].join('_')
184-
}
185154

186-
if (parts.length === 2) {
187-
return parts[1]
155+
if (isOpenApiPath(endpointPath)) {
156+
const spec = openapi.paths[endpointPath]
157+
const methodKey = method.toLowerCase()
158+
159+
if (methodKey === 'post' && 'post' in spec) {
160+
const response = spec.post.responses[200]
161+
if (!('content' in response)) return null
162+
return deriveResourceFromSchema(
163+
response.content['application/json']?.schema?.properties ?? {},
164+
)
165+
}
166+
167+
if (methodKey === 'get' && 'get' in spec) {
168+
const response = spec.get.responses[200]
169+
if (!('content' in response)) {
170+
throw new Error(`Missing resource for ${method} ${endpointPath}`)
171+
}
172+
return deriveResourceFromSchema(
173+
response.content['application/json']?.schema?.properties ?? {},
174+
)
175+
}
188176
}
189177

190-
return parts[0]
178+
throw new Error(`Could not derive resource for ${method} ${endpointPath}`)
191179
}
192180

181+
const deriveResourceFromSchema = (properties: object): string | null =>
182+
Object.keys(properties).filter((key) => key !== 'ok')[0] ?? null
183+
193184
const deriveSemanticMethod = (methods: string[]): Method => {
194185
if (methods.includes('get')) return 'GET'
195186
if (methods.includes('post')) return 'POST'

package-lock.json

Lines changed: 0 additions & 17 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,6 @@
9090
"@seamapi/types": "^1.14.0",
9191
"@types/eslint": "^8.44.2",
9292
"@types/node": "^18.11.18",
93-
"@types/pluralize": "^0.0.31",
9493
"ava": "^5.0.1",
9594
"c8": "^8.0.0",
9695
"change-case": "^4.1.2",
@@ -103,7 +102,6 @@
103102
"eslint-plugin-simple-import-sort": "^10.0.0",
104103
"eslint-plugin-unused-imports": "^3.0.0",
105104
"landlubber": "^1.0.0",
106-
"pluralize": "^8.0.0",
107105
"prettier": "^3.0.0",
108106
"tsc-alias": "^1.8.2",
109107
"tsup": "^7.2.0",

src/lib/seam/connect/routes/acs-access-groups.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -64,15 +64,12 @@ export class SeamHttpAcsAccessGroups {
6464
return new SeamHttpAcsAccessGroups(opts)
6565
}
6666

67-
async addUser(
68-
body: AcsAccessGroupsAddUserBody,
69-
): Promise<AcsAccessGroupsAddUserResponse['acs_access_group']> {
70-
const { data } = await this.client.request<AcsAccessGroupsAddUserResponse>({
67+
async addUser(body: AcsAccessGroupsAddUserBody): Promise<void> {
68+
await this.client.request<AcsAccessGroupsAddUserResponse>({
7169
url: '/acs/access_groups/add_user',
7270
method: 'post',
7371
data: body,
7472
})
75-
return data.acs_access_group
7673
}
7774

7875
async create(

src/lib/seam/connect/routes/locks.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,22 +64,22 @@ export class SeamHttpLocks {
6464
return new SeamHttpLocks(opts)
6565
}
6666

67-
async get(body: LocksGetBody): Promise<LocksGetResponse['lock']> {
67+
async get(body: LocksGetBody): Promise<LocksGetResponse['device']> {
6868
const { data } = await this.client.request<LocksGetResponse>({
6969
url: '/locks/get',
7070
method: 'post',
7171
data: body,
7272
})
73-
return data.lock
73+
return data.device
7474
}
7575

76-
async list(body: LocksListBody): Promise<LocksListResponse['locks']> {
76+
async list(body: LocksListBody): Promise<LocksListResponse['devices']> {
7777
const { data } = await this.client.request<LocksListResponse>({
7878
url: '/locks/list',
7979
method: 'post',
8080
data: body,
8181
})
82-
return data.locks
82+
return data.devices
8383
}
8484

8585
async lockDoor(

0 commit comments

Comments
 (0)