Skip to content

Commit c7a8bc0

Browse files
committed
feat: implement http methods that 'accumulates'
1 parent de9ef63 commit c7a8bc0

File tree

1 file changed

+203
-16
lines changed
  • apps/meteor/app/api/server

1 file changed

+203
-16
lines changed

apps/meteor/app/api/server/api.ts

Lines changed: 203 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ import { Logger } from '@rocket.chat/logger';
33
import { Users } from '@rocket.chat/models';
44
import { Random } from '@rocket.chat/random';
55
import type { JoinPathPattern, Method } from '@rocket.chat/rest-typings';
6+
import { ajv } from '@rocket.chat/rest-typings/src/v1/Ajv';
67
import { wrapExceptions } from '@rocket.chat/tools';
8+
import type { ValidateFunction } from 'ajv';
79
import express from 'express';
810
import type { Request, Response } from 'express';
911
import { Accounts } from 'meteor/accounts-base';
@@ -29,6 +31,8 @@ import type {
2931
PartialThis,
3032
SuccessResult,
3133
TypedThis,
34+
TypedAction,
35+
TypedOptions,
3236
UnauthorizedResult,
3337
} from './definition';
3438
import { getUserInfo } from './helpers/getUserInfo';
@@ -141,7 +145,14 @@ const generateConnection = (
141145
clientAddress: ipAddress,
142146
});
143147

144-
export class APIClass<TBasePath extends string = ''> {
148+
export class APIClass<
149+
TBasePath extends string = '',
150+
TOperations extends {
151+
[x: string]: unknown;
152+
} = {},
153+
> {
154+
public typedRoutes: Record<string, Record<string, unknown>> = {};
155+
145156
protected apiPath?: string;
146157

147158
readonly version?: string;
@@ -496,6 +507,182 @@ export class APIClass<TBasePath extends string = ''> {
496507
return routeActions.map((action) => this.getFullRouteName(route, action));
497508
}
498509

510+
private registerTypedRoutesLegacy<TSubPathPattern extends string, TOptions extends Options>(
511+
method: Method,
512+
subpath: TSubPathPattern,
513+
options: TOptions,
514+
): void {
515+
const { authRequired, validateParams } = options;
516+
517+
const opt = {
518+
authRequired,
519+
...(validateParams &&
520+
method.toLowerCase() === 'get' &&
521+
('GET' in validateParams
522+
? { query: validateParams.GET }
523+
: {
524+
query: validateParams as ValidateFunction<any>,
525+
})),
526+
527+
...(validateParams &&
528+
method.toLowerCase() === 'post' &&
529+
('POST' in validateParams ? { query: validateParams.POST } : { body: validateParams as ValidateFunction<any> })),
530+
531+
...(validateParams &&
532+
method.toLowerCase() === 'put' &&
533+
('PUT' in validateParams ? { query: validateParams.PUT } : { body: validateParams as ValidateFunction<any> })),
534+
...(validateParams &&
535+
method.toLowerCase() === 'delete' &&
536+
('DELETE' in validateParams ? { query: validateParams.DELETE } : { body: validateParams as ValidateFunction<any> })),
537+
538+
tags: ['Missing Documentation'],
539+
response: {
540+
200: ajv.compile({
541+
type: 'object',
542+
properties: {
543+
success: { type: 'boolean' },
544+
error: { type: 'string' },
545+
},
546+
required: ['success'],
547+
}),
548+
},
549+
};
550+
551+
this.registerTypedRoutes(method, subpath, opt);
552+
}
553+
554+
private registerTypedRoutes<
555+
TSubPathPattern extends string,
556+
TOptions extends TypedOptions,
557+
TPathPattern extends `${TBasePath}/${TSubPathPattern}`,
558+
>(method: Method, subpath: TSubPathPattern, options: TOptions): void {
559+
const path = `/${this._config.apiPath}/${subpath}`.replaceAll('//', '/') as TPathPattern;
560+
this.typedRoutes = this.typedRoutes || {};
561+
this.typedRoutes[path] = this.typedRoutes[subpath] || {};
562+
const { query, authRequired, response, body, tags, ...rest } = options;
563+
this.typedRoutes[path][method.toLowerCase()] = {
564+
...(response && {
565+
responses: Object.fromEntries(
566+
Object.entries(response).map(([status, schema]) => [
567+
status,
568+
{
569+
description: '',
570+
content: {
571+
'application/json': 'schema' in schema ? { schema: schema.schema } : schema,
572+
},
573+
},
574+
]),
575+
),
576+
}),
577+
...(query && {
578+
parameters: [
579+
{
580+
schema: query.schema,
581+
in: 'query',
582+
name: 'query',
583+
required: true,
584+
},
585+
],
586+
}),
587+
...(body && {
588+
requestBody: {
589+
required: true,
590+
content: {
591+
'application/json': { schema: body.schema },
592+
},
593+
},
594+
}),
595+
...(authRequired && {
596+
...rest,
597+
security: [
598+
{
599+
userId: [],
600+
authToken: [],
601+
},
602+
],
603+
}),
604+
tags,
605+
};
606+
}
607+
608+
private method<TSubPathPattern extends string, TOptions extends TypedOptions, TPathPattern extends `${TBasePath}/${TSubPathPattern}`>(
609+
method: Method,
610+
subpath: TSubPathPattern,
611+
options: TOptions,
612+
action: TypedAction<TOptions>,
613+
): APIClass<
614+
TBasePath,
615+
| TOperations
616+
| ({
617+
method: Method;
618+
path: TPathPattern;
619+
} & Omit<TOptions, 'response'>)
620+
> {
621+
this.addRoute([subpath], { ...options }, { [method.toLowerCase()]: { action } } as any);
622+
this.registerTypedRoutes(method, subpath, options);
623+
return this;
624+
}
625+
626+
get<TSubPathPattern extends string, TOptions extends TypedOptions, TPathPattern extends `${TBasePath}/${TSubPathPattern}`>(
627+
subpath: TSubPathPattern,
628+
options: TOptions,
629+
action: TypedAction<TOptions>,
630+
): APIClass<
631+
TBasePath,
632+
| TOperations
633+
| ({
634+
method: 'GET';
635+
path: TPathPattern;
636+
} & Omit<TOptions, 'response'>)
637+
> {
638+
return this.method('GET', subpath, options, action);
639+
}
640+
641+
post<TSubPathPattern extends string, TOptions extends TypedOptions, TPathPattern extends `${TBasePath}/${TSubPathPattern}`>(
642+
subpath: TSubPathPattern,
643+
options: TOptions,
644+
action: TypedAction<TOptions>,
645+
): APIClass<
646+
TBasePath,
647+
| TOperations
648+
| ({
649+
method: 'POST';
650+
path: TPathPattern;
651+
} & Omit<TOptions, 'response'>)
652+
> {
653+
return this.method('POST', subpath, options, action);
654+
}
655+
656+
put<TSubPathPattern extends string, TOptions extends TypedOptions, TPathPattern extends `${TBasePath}/${TSubPathPattern}`>(
657+
subpath: TSubPathPattern,
658+
options: TOptions,
659+
action: TypedAction<TOptions>,
660+
): APIClass<
661+
TBasePath,
662+
| TOperations
663+
| ({
664+
method: 'PUT';
665+
path: TPathPattern;
666+
} & Omit<TOptions, 'response'>)
667+
> {
668+
return this.method('PUT', subpath, options, action);
669+
}
670+
671+
delete<TSubPathPattern extends string, TOptions extends TypedOptions, TPathPattern extends `${TBasePath}/${TSubPathPattern}`>(
672+
subpath: TSubPathPattern,
673+
options: TOptions,
674+
action: TypedAction<TOptions>,
675+
): APIClass<
676+
TBasePath,
677+
| TOperations
678+
| ({
679+
method: 'DELETE';
680+
path: TPathPattern;
681+
} & Omit<TOptions, 'response'>)
682+
> {
683+
return this.method('DELETE', subpath, options, action);
684+
}
685+
499686
addRoute<TSubPathPattern extends string>(
500687
subpath: TSubPathPattern,
501688
operations: Operations<JoinPathPattern<TBasePath, TSubPathPattern>>,
@@ -707,6 +894,11 @@ export class APIClass<TBasePath extends string = ''> {
707894
options: _options,
708895
endpoints: operations[method as keyof Operations<TPathPattern, TOptions>] as unknown as Record<string, string>,
709896
});
897+
898+
this.registerTypedRoutesLegacy(method as Method, route, {
899+
...options,
900+
...operations[method as keyof Operations<TPathPattern, TOptions>],
901+
});
710902
});
711903
});
712904
}
@@ -938,17 +1130,14 @@ export class APIClass<TBasePath extends string = ''> {
9381130
}
9391131
}
9401132

941-
const createApi = function _createApi(options: { version?: string; apiPath?: string } = {}): APIClass {
942-
return new APIClass(
943-
Object.assign(
944-
{
945-
apiPath: 'api/',
946-
useDefaultAuth: true,
947-
prettyJson: process.env.NODE_ENV === 'development',
948-
},
949-
options,
950-
) as IAPIProperties,
951-
);
1133+
const createApi = function _createApi(options: { version?: string; useDefaultAuth?: true } = {}): APIClass {
1134+
return new APIClass({
1135+
apiPath: 'api/',
1136+
useDefaultAuth: false,
1137+
prettyJson: process.env.NODE_ENV === 'development',
1138+
auth: getUserAuth(),
1139+
...options,
1140+
});
9521141
};
9531142

9541143
export const API: {
@@ -982,12 +1171,10 @@ export const API: {
9821171
ApiClass: APIClass,
9831172
api: new Router('/api'),
9841173
v1: createApi({
985-
apiPath: '',
9861174
version: 'v1',
1175+
useDefaultAuth: true,
9871176
}),
988-
default: createApi({
989-
apiPath: '',
990-
}),
1177+
default: createApi({}),
9911178
};
9921179

9931180
settings.watch<string>('Accounts_CustomFields', (value) => {

0 commit comments

Comments
 (0)