Skip to content

Commit

Permalink
feat(lark): support async iterator
Browse files Browse the repository at this point in the history
  • Loading branch information
shigma committed Jan 7, 2025
1 parent fc60874 commit 03d255c
Show file tree
Hide file tree
Showing 32 changed files with 1,901 additions and 949 deletions.
63 changes: 48 additions & 15 deletions adapters/lark/scripts/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,7 @@ async function start() {
const extras: string[] = []
let returnType = `${apiType}Response`

let paginationRequest: { queryType?: string } | undefined
for (const property of detail.request.path?.properties || []) {
args.push(`${property.name}: ${formatType(property, project.imports)}`)
}
Expand All @@ -242,20 +243,27 @@ async function start() {
args.push(`body: ${name}`)
}
if (detail.request.query?.properties?.length) {
const name = `${apiType}Query`
let queryType = `${apiType}Query`
const keys = detail.request.query.properties.map(s => s.name)
if (keys.includes('page_token') && keys.includes('page_size')) {
const properties = detail.request.query.properties.filter(s => s.name !== 'page_token' && s.name !== 'page_size')
project.requests.push(`export interface ${name} extends Pagination {\n${generateParams(properties, project.imports)}}`)
if (properties.length) {
project.requests.push(`export interface ${queryType} {\n${generateParams(properties, project.imports)}}`)
paginationRequest = { queryType }
queryType = `${queryType} & Pagination`
} else {
queryType = 'Pagination'
paginationRequest = {}
}
project.internalImports.add('Pagination')
} else {
project.requests.push(`export interface ${name} {\n${generateParams(detail.request.query.properties, project.imports)}}`)
project.requests.push(`export interface ${queryType} {\n${generateParams(detail.request.query.properties, project.imports)}}`)
}
args.push(`query?: ${name}`)
args.push(`query?: ${queryType}`)
}

let paginationResponse: { innerType: string; tokenKey: string; itemsKey: string } | undefined
if (detail.supportFileDownload) {
// detail.response.contentType === ''
returnType = 'ArrayBuffer'
extras.push(`type: 'binary'`)
} else {
Expand All @@ -271,23 +279,25 @@ async function start() {
returnType = 'void'
} else {
const keys = (data.properties || []).map(v => v.name)
let pagination: [string, Schema] | undefined
if (keys.includes('has_more') && keys.includes('page_token') && keys.length === 3) {
const list = (data.properties || []).find(v => v.name !== 'has_more' && v.name !== 'page_token')!
let pagination: [string, string, Schema] | undefined
if (keys.includes('has_more') && (keys.includes('page_token') || keys.includes('next_page_token')) && keys.length === 3) {
const list = (data.properties || []).find(v => !['has_more', 'page_token', 'next_page_token'].includes(v.name))!
if (list.type === 'list') {
pagination = [list.name, list.items!]
const tokenKey = keys.includes('page_token') ? 'page_token' : 'next_page_token'
pagination = [list.name, tokenKey, list.items!]
}
}
if (pagination) {
const [key, item] = pagination
let inner = formatType(item, project.imports)
if (item.type === 'object' && item.properties && !item.ref) {
const [itemsKey, tokenKey, schema] = pagination
let innerType = formatType(schema, project.imports)
if (schema.type === 'object' && schema.properties && !schema.ref) {
const name = `${apiType}Item`
project.responses.push(`export interface ${name} ${inner}`)
inner = name
project.responses.push(`export interface ${name} ${innerType}`)
innerType = name
}
returnType = key === 'items' ? `Paginated<${inner}>` : `Paginated<${inner}, '${key}'>`
returnType = itemsKey === 'items' ? `Paginated<${innerType}>` : `Paginated<${innerType}, '${itemsKey}'>`
project.internalImports.add('Paginated')
paginationResponse = { innerType, tokenKey, itemsKey }
} else {
if (detail.pagination) {
console.log(`unsupported pagination (${keys.join(', ')}), see https://open.feishu.cn${summary.fullPath}}`)
Expand All @@ -309,6 +319,29 @@ async function start() {
${method}(${args.join(', ')}): Promise<${returnType}>
`)

if (paginationRequest && paginationResponse) {
const paginationArgs = args.slice(0, -1)
const argIndex = paginationArgs.length
if (paginationRequest.queryType) {
paginationArgs.push(`query?: ${paginationRequest.queryType}`)
}
project.methods.push(dedent`
/**
* ${summary.name}
* @see https://open.feishu.cn${summary.fullPath}
*/
${method}Iter(${paginationArgs.join(', ')}): AsyncIterator<${paginationResponse.innerType}>
`)
const props: string[] = [`argIndex: ${argIndex}`]
if (paginationResponse.itemsKey !== 'items') {
props.push(`itemsKey: '${paginationResponse.itemsKey}'`)
}
if (paginationResponse.tokenKey !== 'page_token') {
props.push(`tokenKey: '${paginationResponse.tokenKey}'`)
}
extras.push(`pagination: { ${props.join(', ')} }`)
}

const path = detail.apiPath.replace(/:([0-9a-zA-Z_]+)/g, '{$1}')
project.defines[path] ||= {}
project.defines[path][detail.httpMethod] = extras.length
Expand Down
27 changes: 27 additions & 0 deletions adapters/lark/src/internal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ export type Paginated<T, K extends string = 'items'> = {

export interface InternalRoute {
name: string
pagination?: {
argIndex: number
itemsKey?: string
tokenKey?: string
}
multipart?: boolean
type?: 'raw-json' | 'binary'
}
Expand Down Expand Up @@ -93,6 +98,28 @@ export class Internal {
return response.data.data
}
}

if (route.pagination) {
const { argIndex, itemsKey = 'items', tokenKey = 'page_token' } = route.pagination
Internal.prototype[route.name + 'Iter'] = async function (this: Internal, ...args: any[]) {
let list: Paginated<any>
const getList = async () => {
args[argIndex] = { ...args[argIndex], page_token: list?.[tokenKey] }
list = await this[route.name](...args)
}
return {
async next() {
if (list?.[itemsKey].length) return { done: false, value: list[itemsKey].shift() }
if (!list.has_more) return { done: true, value: undefined }
await getList()
return this.next()
},
[Symbol.asyncIterator]() {
return this
},
}
}
}
}
}
}
Expand Down
22 changes: 16 additions & 6 deletions adapters/lark/src/types/acs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,12 @@ declare module '../internal' {
* 获取用户列表
* @see https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/acs-v1/user/list
*/
listAcsUser(query?: ListAcsUserQuery): Promise<Paginated<User>>
listAcsUser(query?: ListAcsUserQuery & Pagination): Promise<Paginated<User>>
/**
* 获取用户列表
* @see https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/acs-v1/user/list
*/
listAcsUserIter(query?: ListAcsUserQuery): AsyncIterator<User>
/**
* 上传人脸图片
* @see https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/acs-v1/user-face/update
Expand Down Expand Up @@ -67,7 +72,12 @@ declare module '../internal' {
* 获取门禁记录列表
* @see https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/acs-v1/access_record/list
*/
listAcsAccessRecord(query?: ListAcsAccessRecordQuery): Promise<Paginated<AccessRecord>>
listAcsAccessRecord(query?: ListAcsAccessRecordQuery & Pagination): Promise<Paginated<AccessRecord>>
/**
* 获取门禁记录列表
* @see https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/acs-v1/access_record/list
*/
listAcsAccessRecordIter(query?: ListAcsAccessRecordQuery): AsyncIterator<AccessRecord>
/**
* 下载开门时的人脸识别图片
* @see https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/acs-v1/access_record-access_photo/get
Expand All @@ -91,7 +101,7 @@ export interface GetAcsUserQuery {
user_id_type?: 'user_id' | 'union_id' | 'open_id'
}

export interface ListAcsUserQuery extends Pagination {
export interface ListAcsUserQuery {
/** 此次调用中使用的用户ID的类型 */
user_id_type?: 'user_id' | 'union_id' | 'open_id'
}
Expand Down Expand Up @@ -163,7 +173,7 @@ export interface CreateAcsVisitorQuery {
user_id_type?: 'user_id' | 'union_id' | 'open_id'
}

export interface ListAcsAccessRecordQuery extends Pagination {
export interface ListAcsAccessRecordQuery {
/** 记录开始时间,单位秒 */
from: number
/** 记录结束时间,单位秒,时间跨度不能超过30天 */
Expand Down Expand Up @@ -204,7 +214,7 @@ Internal.define({
GET: 'getAcsUser',
},
'/open-apis/acs/v1/users': {
GET: 'listAcsUser',
GET: { name: 'listAcsUser', pagination: { argIndex: 0 } },
},
'/open-apis/acs/v1/users/{user_id}/face': {
PUT: { name: 'updateAcsUserFace', multipart: true },
Expand All @@ -228,7 +238,7 @@ Internal.define({
GET: 'listAcsDevice',
},
'/open-apis/acs/v1/access_records': {
GET: 'listAcsAccessRecord',
GET: { name: 'listAcsAccessRecord', pagination: { argIndex: 0 } },
},
'/open-apis/acs/v1/access_records/{access_record_id}/access_photo': {
GET: { name: 'getAcsAccessRecordAccessPhoto', type: 'binary' },
Expand Down
55 changes: 40 additions & 15 deletions adapters/lark/src/types/admin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,22 @@ declare module '../internal' {
* 获取部门维度的用户活跃和功能使用数据
* @see https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/admin-v1/admin_dept_stat/list
*/
listAdminAdminDeptStat(query?: ListAdminAdminDeptStatQuery): Promise<Paginated<AdminDeptStat>>
listAdminAdminDeptStat(query?: ListAdminAdminDeptStatQuery & Pagination): Promise<Paginated<AdminDeptStat>>
/**
* 获取部门维度的用户活跃和功能使用数据
* @see https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/admin-v1/admin_dept_stat/list
*/
listAdminAdminDeptStatIter(query?: ListAdminAdminDeptStatQuery): AsyncIterator<AdminDeptStat>
/**
* 获取用户维度的用户活跃和功能使用数据
* @see https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/admin-v1/admin_user_stat/list
*/
listAdminAdminUserStat(query?: ListAdminAdminUserStatQuery & Pagination): Promise<Paginated<AdminUserStat>>
/**
* 获取用户维度的用户活跃和功能使用数据
* @see https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/admin-v1/admin_user_stat/list
*/
listAdminAdminUserStat(query?: ListAdminAdminUserStatQuery): Promise<Paginated<AdminUserStat>>
listAdminAdminUserStatIter(query?: ListAdminAdminUserStatQuery): AsyncIterator<AdminUserStat>
/**
* 创建勋章
* @see https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/admin-v1/badge/create
Expand All @@ -37,7 +47,12 @@ declare module '../internal' {
* 获取勋章列表
* @see https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/admin-v1/badge/list
*/
listAdminBadge(query?: ListAdminBadgeQuery): Promise<Paginated<Badge, 'badges'>>
listAdminBadge(query?: ListAdminBadgeQuery & Pagination): Promise<Paginated<Badge, 'badges'>>
/**
* 获取勋章列表
* @see https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/admin-v1/badge/list
*/
listAdminBadgeIter(query?: ListAdminBadgeQuery): AsyncIterator<Badge>
/**
* 获取勋章详情
* @see https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/admin-v1/badge/get
Expand All @@ -62,7 +77,12 @@ declare module '../internal' {
* 获取授予名单列表
* @see https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/admin-v1/badge-grant/list
*/
listAdminBadgeGrant(badge_id: string, query?: ListAdminBadgeGrantQuery): Promise<Paginated<Grant, 'grants'>>
listAdminBadgeGrant(badge_id: string, query?: ListAdminBadgeGrantQuery & Pagination): Promise<Paginated<Grant, 'grants'>>
/**
* 获取授予名单列表
* @see https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/admin-v1/badge-grant/list
*/
listAdminBadgeGrantIter(badge_id: string, query?: ListAdminBadgeGrantQuery): AsyncIterator<Grant>
/**
* 获取授予名单详情
* @see https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/admin-v1/badge-grant/get
Expand All @@ -72,7 +92,12 @@ declare module '../internal' {
* 获取行为审计日志数据
* @see https://open.feishu.cn/document/ukTMukTMukTM/uQjM5YjL0ITO24CNykjN/audit_log/audit_data_get
*/
listAdminAuditInfo(query?: ListAdminAuditInfoQuery): Promise<Paginated<AuditInfo>>
listAdminAuditInfo(query?: ListAdminAuditInfoQuery & Pagination): Promise<Paginated<AuditInfo>>
/**
* 获取行为审计日志数据
* @see https://open.feishu.cn/document/ukTMukTMukTM/uQjM5YjL0ITO24CNykjN/audit_log/audit_data_get
*/
listAdminAuditInfoIter(query?: ListAdminAuditInfoQuery): AsyncIterator<AuditInfo>
}
}

Expand All @@ -88,7 +113,7 @@ export interface ResetAdminPasswordQuery {
user_id_type: 'open_id' | 'union_id' | 'user_id'
}

export interface ListAdminAdminDeptStatQuery extends Pagination {
export interface ListAdminAdminDeptStatQuery {
/** 部门ID类型 */
department_id_type: 'department_id' | 'open_department_id'
/** 起始日期(包含),格式是YYYY-mm-dd */
Expand All @@ -105,7 +130,7 @@ export interface ListAdminAdminDeptStatQuery extends Pagination {
with_product_version?: boolean
}

export interface ListAdminAdminUserStatQuery extends Pagination {
export interface ListAdminAdminUserStatQuery {
/** 此次调用中使用的用户ID的类型 */
user_id_type?: 'user_id' | 'union_id' | 'open_id'
/** 部门ID类型 */
Expand Down Expand Up @@ -159,7 +184,7 @@ export interface CreateAdminBadgeImageForm {
image_type: 1 | 2
}

export interface ListAdminBadgeQuery extends Pagination {
export interface ListAdminBadgeQuery {
/** 租户内唯一的勋章名称,精确匹配。 */
name?: string
}
Expand Down Expand Up @@ -216,7 +241,7 @@ export interface UpdateAdminBadgeGrantQuery {
department_id_type?: 'department_id' | 'open_department_id'
}

export interface ListAdminBadgeGrantQuery extends Pagination {
export interface ListAdminBadgeGrantQuery {
/** 用户 ID 类型 */
user_id_type?: 'open_id' | 'union_id' | 'user_id'
/** 此次调用中使用的部门ID的类型。 */
Expand All @@ -232,7 +257,7 @@ export interface GetAdminBadgeGrantQuery {
department_id_type?: 'department_id' | 'open_department_id'
}

export interface ListAdminAuditInfoQuery extends Pagination {
export interface ListAdminAuditInfoQuery {
/** 此次调用中使用的用户ID的类型 */
user_id_type?: 'user_id' | 'union_id' | 'open_id'
/** 日志时间范围: 结束时间. 格式: 秒级时间戳. 默认值: 此刻 */
Expand Down Expand Up @@ -297,14 +322,14 @@ Internal.define({
POST: 'resetAdminPassword',
},
'/open-apis/admin/v1/admin_dept_stats': {
GET: 'listAdminAdminDeptStat',
GET: { name: 'listAdminAdminDeptStat', pagination: { argIndex: 0 } },
},
'/open-apis/admin/v1/admin_user_stats': {
GET: 'listAdminAdminUserStat',
GET: { name: 'listAdminAdminUserStat', pagination: { argIndex: 0 } },
},
'/open-apis/admin/v1/badges': {
POST: 'createAdminBadge',
GET: 'listAdminBadge',
GET: { name: 'listAdminBadge', pagination: { argIndex: 0, itemsKey: 'badges' } },
},
'/open-apis/admin/v1/badges/{badge_id}': {
PUT: 'updateAdminBadge',
Expand All @@ -315,14 +340,14 @@ Internal.define({
},
'/open-apis/admin/v1/badges/{badge_id}/grants': {
POST: 'createAdminBadgeGrant',
GET: 'listAdminBadgeGrant',
GET: { name: 'listAdminBadgeGrant', pagination: { argIndex: 1, itemsKey: 'grants' } },
},
'/open-apis/admin/v1/badges/{badge_id}/grants/{grant_id}': {
DELETE: 'deleteAdminBadgeGrant',
PUT: 'updateAdminBadgeGrant',
GET: 'getAdminBadgeGrant',
},
'/open-apis/admin/v1/audit_infos': {
GET: 'listAdminAuditInfo',
GET: { name: 'listAdminAuditInfo', pagination: { argIndex: 0 } },
},
})
Loading

0 comments on commit 03d255c

Please sign in to comment.