Skip to content

Commit c367754

Browse files
authored
Merge pull request #119 from hcengineering/improve-find-validation
Improve find methods schemas to convert to valid types
2 parents c0ea5ce + 59a6233 commit c367754

File tree

3 files changed

+56
-36
lines changed

3 files changed

+56
-36
lines changed
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"changes": [
3+
{
4+
"packageName": "@hcengineering/communication-server",
5+
"comment": "Improve validation of find methods params",
6+
"type": "patch"
7+
}
8+
],
9+
"packageName": "@hcengineering/communication-server"
10+
}

packages/server/src/__tests__/middleware/validate.test.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1003,9 +1003,10 @@ describe('ValidateMiddleware', () => {
10031003

10041004
describe('findMessagesGroups', () => {
10051005
it('should validate correct params', async () => {
1006-
const params = { cardId: 'card-123' as CardID }
1006+
const paramsRaw = { cardId: 'card-123' as CardID, fromDate: { less: '2025-10-20T18:59:17.593Z' as any } }
1007+
const params = { cardId: 'card-123' as CardID, fromDate: { less: new Date('2025-10-20T18:59:17.593Z') } }
10071008

1008-
await middleware.findMessagesGroups(session, params)
1009+
await middleware.findMessagesGroups(session, paramsRaw)
10091010

10101011
expect(mockNext.findMessagesGroups).toHaveBeenCalledWith(session, params)
10111012
})

packages/server/src/middleware/validate.ts

Lines changed: 43 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -22,27 +22,35 @@ import {
2222
PeerEventType
2323
} from '@hcengineering/communication-sdk-types'
2424
import {
25+
type AccountUuid,
26+
type BlobID,
27+
type CardID,
28+
type CardType,
2529
type Collaborator,
30+
type ContextID,
2631
type FindCollaboratorsParams,
2732
type FindLabelsParams,
28-
FindMessagesGroupParams,
33+
type FindMessagesGroupParams,
2934
type FindNotificationContextParams,
3035
type FindNotificationsParams,
3136
type Label,
32-
MessagesGroup,
37+
type LabelID,
38+
type MessageID,
39+
type MessagesGroup,
3340
type Notification,
3441
type NotificationContext,
42+
NotificationType,
3543
SortingOrder
3644
} from '@hcengineering/communication-types'
37-
import { z } from 'zod'
45+
import { z, ZodString, ZodType, ZodTypeDef } from 'zod'
3846
import { isBlobAttachmentType, isLinkPreviewAttachmentType } from '@hcengineering/communication-shared'
3947

4048
import type { Enriched, Middleware, Subscription } from '../types'
4149
import { BaseMiddleware } from './base'
4250
import { ApiError } from '../error'
4351

4452
export class ValidateMiddleware extends BaseMiddleware implements Middleware {
45-
private validate<T>(data: unknown, schema: z.ZodType<T>): T {
53+
private validate<T>(data: unknown, schema: z.ZodType<T, ZodTypeDef, any>): T {
4654
const validationResult = schema.safeParse(data)
4755
if (!validationResult.success) {
4856
const errors = validationResult.error.errors.map((err) => err.message)
@@ -52,37 +60,33 @@ export class ValidateMiddleware extends BaseMiddleware implements Middleware {
5260
return validationResult.data
5361
}
5462

55-
async findMessagesGroups (session: SessionData, params: FindMessagesGroupParams): Promise<MessagesGroup[]> {
56-
this.validate(params, FindMessagesGroupsParamsSchema)
57-
return await this.provideFindMessagesGroups(session, params)
63+
async findMessagesGroups (session: SessionData, params: unknown): Promise<MessagesGroup[]> {
64+
const validParams: FindMessagesGroupParams = this.validate(params, FindMessagesGroupsParamsSchema)
65+
return await this.provideFindMessagesGroups(session, validParams)
5866
}
5967

6068
async findNotificationContexts (
6169
session: SessionData,
62-
params: FindNotificationContextParams,
70+
params: unknown,
6371
queryId?: Subscription
6472
): Promise<NotificationContext[]> {
65-
this.validate(params, FindNotificationContextParamsSchema)
66-
return await this.provideFindNotificationContexts(session, params, queryId)
73+
const validParams: FindNotificationContextParams = this.validate(params, FindNotificationContextParamsSchema)
74+
return await this.provideFindNotificationContexts(session, validParams, queryId)
6775
}
6876

69-
async findNotifications (
70-
session: SessionData,
71-
params: FindNotificationsParams,
72-
queryId?: Subscription
73-
): Promise<Notification[]> {
74-
this.validate(params, FindNotificationsParamsSchema)
75-
return await this.provideFindNotifications(session, params, queryId)
77+
async findNotifications (session: SessionData, params: unknown, queryId?: Subscription): Promise<Notification[]> {
78+
const validParams: FindNotificationsParams = this.validate(params, FindNotificationsParamsSchema)
79+
return await this.provideFindNotifications(session, validParams, queryId)
7680
}
7781

78-
async findLabels (session: SessionData, params: FindLabelsParams, queryId?: Subscription): Promise<Label[]> {
79-
this.validate(params, FindLabelsParamsSchema)
80-
return await this.provideFindLabels(session, params, queryId)
82+
async findLabels (session: SessionData, params: unknown, queryId?: Subscription): Promise<Label[]> {
83+
const validParams: FindLabelsParams = this.validate(params, FindLabelsParamsSchema)
84+
return await this.provideFindLabels(session, validParams, queryId)
8185
}
8286

83-
async findCollaborators (session: SessionData, params: FindCollaboratorsParams): Promise<Collaborator[]> {
84-
this.validate(params, FindCollaboratorsParamsSchema)
85-
return await this.provideFindCollaborators(session, params)
87+
async findCollaborators (session: SessionData, params: unknown): Promise<Collaborator[]> {
88+
const validParams: FindCollaboratorsParams = this.validate(params, FindCollaboratorsParamsSchema)
89+
return await this.provideFindCollaborators(session, validParams)
8690
}
8791

8892
async event (session: SessionData, event: Enriched<Event>, derived: boolean): Promise<EventResult> {
@@ -146,21 +150,26 @@ export class ValidateMiddleware extends BaseMiddleware implements Middleware {
146150
}
147151
}
148152

153+
function brandedString<Type extends string> (base: ZodString): ZodType<Type, ZodTypeDef, string> {
154+
return base.transform((s): Type => s as Type)
155+
}
156+
149157
const WorkspaceUuidSchema = z.string().uuid()
150-
const AccountUuidSchema = z.string()
151-
const BlobIDSchema = z.string().uuid()
158+
const AccountUuidSchema = brandedString<AccountUuid>(z.string())
159+
const BlobIDSchema = brandedString<BlobID>(z.string().uuid())
152160
const AttachmentIDSchema = z.string().uuid()
153-
const CardIDSchema = z.string()
154-
const CardTypeSchema = z.string()
155-
const ContextIDSchema = z.string()
161+
const CardIDSchema = brandedString<CardID>(z.string())
162+
const CardTypeSchema = brandedString<CardType>(z.string())
163+
const ContextIDSchema = brandedString<ContextID>(z.string())
156164
const DateSchema = z.coerce.date()
157-
const LabelIDSchema = z.string()
165+
const LabelIDSchema = brandedString<LabelID>(z.string())
158166
const MarkdownSchema = z.string()
159167
const MessageExtraSchema = z.any()
160-
const MessageIDSchema = z.string()
168+
const MessageIDSchema = brandedString<MessageID>(z.string())
161169
const MessageTypeSchema = z.string()
162170
const SocialIDSchema = z.string()
163171
const SortingOrderSchema = z.union([z.literal(SortingOrder.Ascending), z.literal(SortingOrder.Descending)])
172+
const NotificationTypeSchema = z.nativeEnum(NotificationType)
164173

165174
const BlobParamsSchema = z.object({
166175
blobId: BlobIDSchema,
@@ -224,7 +233,7 @@ const FindNotificationContextParamsSchema = FindParamsSchema.extend({
224233
account: z.union([AccountUuidSchema, z.array(AccountUuidSchema)]).optional(),
225234
notifications: z
226235
.object({
227-
type: z.string().optional(),
236+
type: NotificationTypeSchema.optional(),
228237
limit: z.number(),
229238
order: SortingOrderSchema,
230239
read: z.boolean().optional(),
@@ -243,7 +252,7 @@ const FindMessagesGroupsParamsSchema = FindParamsSchema.extend({
243252

244253
const FindNotificationsParamsSchema = FindParamsSchema.extend({
245254
contextId: ContextIDSchema.optional(),
246-
type: z.string().optional(),
255+
type: NotificationTypeSchema.optional(),
247256
read: z.boolean().optional(),
248257
created: DateOrRecordSchema.optional(),
249258
account: z.union([AccountUuidSchema, z.array(AccountUuidSchema)]).optional(),
@@ -259,7 +268,7 @@ const FindLabelsParamsSchema = FindParamsSchema.extend({
259268
}).strict()
260269

261270
const FindCollaboratorsParamsSchema = FindParamsSchema.extend({
262-
cardId: CardIDSchema.optional(),
271+
cardId: CardIDSchema,
263272
account: z.union([AccountUuidSchema, z.array(AccountUuidSchema)]).optional()
264273
}).strict()
265274

@@ -279,7 +288,7 @@ const CreateMessageEventSchema = BaseEventSchema.extend({
279288
cardId: CardIDSchema,
280289
cardType: CardTypeSchema,
281290

282-
messageId: MessageIDSchema.max(22).optional(),
291+
messageId: brandedString<MessageID>(z.string().max(22)).optional(),
283292
messageType: MessageTypeSchema,
284293

285294
content: MarkdownSchema,

0 commit comments

Comments
 (0)