diff --git a/itest/test/attachments.test.ts b/itest/test/attachments.test.ts index 3a48c0f..ac7897b 100644 --- a/itest/test/attachments.test.ts +++ b/itest/test/attachments.test.ts @@ -79,6 +79,8 @@ test('Create attachment for multiple bugs at once', async () => { }); test('Get single attachment', async () => { + await expect(api.getAttachment(1)).resolves.toMatchInlineSnapshot(); + await expect(api.getAttachment(1)).resolves.toEqual({ bug_id: bugs[0], content_type: 'image/png', diff --git a/src/link.ts b/src/link.ts index be1fc58..4c88db8 100644 --- a/src/link.ts +++ b/src/link.ts @@ -1,5 +1,5 @@ import { URLSearchParams, URL } from 'url'; -import { z } from 'zod'; +import { z, ZodSchema } from 'zod'; import axios, { AxiosRequestConfig } from 'axios'; import { loginResponseSchema } from './types'; @@ -28,10 +28,10 @@ function isError(payload: unknown): payload is ApiError { return payload && typeof payload == 'object' && payload.error; } -async function performRequest( - config: AxiosRequestConfig, - schema: z.Schema, -): Promise { +async function performRequest< + TSchema extends ZodSchema, + KValues extends z.infer, +>(config: AxiosRequestConfig, schema: TSchema): Promise { try { let response = await axios.request({ ...config, @@ -67,10 +67,10 @@ export abstract class BugzillaLink { this.instance = new URL('rest/', instance); } - protected abstract request( - config: AxiosRequestConfig, - schema: z.Schema, - ): Promise; + protected abstract request< + TSchema extends ZodSchema, + KValues extends z.infer, + >(config: AxiosRequestConfig, schema: TSchema): Promise; protected buildURL(path: string, query?: SearchParams): URL { let url = new URL(path, this.instance); @@ -80,11 +80,11 @@ export abstract class BugzillaLink { return url; } - public async get( + public async get>( path: string, - schema: z.Schema, + schema: TSchema, searchParams?: SearchParams, - ): Promise { + ): Promise { return this.request( { url: this.buildURL(path, searchParams).toString(), @@ -93,12 +93,16 @@ export abstract class BugzillaLink { ); } - public async post( + public async post< + R, + TSchema extends ZodSchema, + KValues extends z.infer, + >( path: string, - schema: z.Schema, + schema: TSchema, content: R, searchParams?: SearchParams, - ): Promise { + ): Promise { return this.request( { url: this.buildURL(path, searchParams).toString(), @@ -112,12 +116,16 @@ export abstract class BugzillaLink { ); } - public async put( + public async put< + R, + TSchema extends ZodSchema, + KValues extends z.infer, + >( path: string, - schema: z.Schema, + schema: TSchema, content: R, searchParams?: SearchParams, - ): Promise { + ): Promise { return this.request( { url: this.buildURL(path, searchParams).toString(), @@ -133,10 +141,10 @@ export abstract class BugzillaLink { } export class PublicLink extends BugzillaLink { - protected async request( - config: AxiosRequestConfig, - schema: z.Schema, - ): Promise { + protected async request< + TSchema extends ZodSchema, + KValues extends z.infer, + >(config: AxiosRequestConfig, schema: TSchema): Promise { return performRequest(config, schema); } } @@ -149,10 +157,10 @@ export class ApiKeyLink extends BugzillaLink { super(instance); } - protected async request( - config: AxiosRequestConfig, - schema: z.Schema, - ): Promise { + protected async request< + TSchema extends ZodSchema, + KValues extends z.infer, + >(config: AxiosRequestConfig, schema: TSchema): Promise { return performRequest( { ...config, @@ -198,10 +206,10 @@ export class PasswordLink extends BugzillaLink { return loginInfo.token; } - protected async request( - config: AxiosRequestConfig, - schema: z.Schema, - ): Promise { + protected async request< + TSchema extends ZodSchema, + KValues extends z.infer, + >(config: AxiosRequestConfig, schema: TSchema): Promise { if (!this.token) { this.token = await this.login(); } diff --git a/src/types.ts b/src/types.ts index c03e0aa..51b58b0 100644 --- a/src/types.ts +++ b/src/types.ts @@ -144,9 +144,29 @@ export const commentsTemplateSchema = z.object({ export type CommentsTemplate = z.infer; +function castObjectToMap( + keyValidator: K, + valueValidator: V, +) { + return z.record(keyValidator, valueValidator).transform(record => { + let result = new Map, z.infer>(); + + for (let [k, v] of Object.entries(record)) { + // if (typeof k === 'number') { + // result.set(k, v); + // } + if (k?.toString()) { + result.set(k.toString(), v); + } + } + + return result; + }); +} + export const commentsSchema = z.object({ - bugs: z.map(z.number(), commentsTemplateSchema), - comments: z.map(z.number(), commentSchema), + bugs: castObjectToMap(z.number(), commentsTemplateSchema), + comments: castObjectToMap(z.number(), commentSchema), }); export type Comments = z.infer; @@ -222,7 +242,7 @@ export const updateBugContentSchema = z.object({ cc: updateListSchema(z.string()).optional(), is_cc_accessible: z.boolean().optional(), comment: createCommentContentSchema.optional(), - comment_is_private: z.map(z.number(), z.boolean()).optional(), + comment_is_private: castObjectToMap(z.number(), z.boolean()).optional(), comment_tags: z.array(z.string()).optional(), component: z.string().optional(), deadline: z.string().datetime().optional(), @@ -262,7 +282,7 @@ export const updatedBugSchema = z.object({ id: z.number(), alias: z.array(z.string()), last_change_time: z.string().datetime(), - changes: z.map(z.string(), changesSchema), + changes: castObjectToMap(z.string(), changesSchema), }); export type UpdatedBug = z.infer; @@ -294,8 +314,8 @@ export const attachmentSchema = z.object({ export type Attachment = z.infer; export const attachmentsSchema = z.object({ - bugs: z.map(z.number(), z.array(attachmentSchema)), - attachments: z.map(z.number(), attachmentSchema), + bugs: castObjectToMap(z.number(), z.array(attachmentSchema)), + attachments: castObjectToMap(z.number(), attachmentSchema), }); export type Attachments = z.infer; @@ -343,7 +363,7 @@ export type UpdateAttachmentContent = z.infer< export const updatedAttachmentSchema = z.object({ id: z.number(), last_change_time: z.string().datetime(), - changes: z.map(z.string(), changesSchema), + changes: castObjectToMap(z.string(), changesSchema), }); export type UpdatedAttachment = z.infer;