Skip to content

Commit adb55fb

Browse files
committed
feat: implement http methods that 'accumulates'
1 parent 55c1672 commit adb55fb

File tree

1 file changed

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

1 file changed

+203
-15
lines changed

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

Lines changed: 203 additions & 15 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 { tracerSpan } from '@rocket.chat/tracing';
8+
import type { ValidateFunction } from 'ajv';
79
import { Accounts } from 'meteor/accounts-base';
810
import { DDP } from 'meteor/ddp';
911
import { DDPCommon } from 'meteor/ddp-common';
@@ -26,6 +28,8 @@ import type {
2628
Options,
2729
PartialThis,
2830
SuccessResult,
31+
TypedAction,
32+
TypedOptions,
2933
UnauthorizedResult,
3034
} from './definition';
3135
import { getUserInfo } from './helpers/getUserInfo';
@@ -137,7 +141,14 @@ const generateConnection = (
137141

138142
let prometheusAPIUserAgent = false;
139143

140-
export class APIClass<TBasePath extends string = ''> extends Restivus {
144+
export class APIClass<
145+
TBasePath extends string = '',
146+
TOperations extends {
147+
[x: string]: unknown;
148+
} = {},
149+
> extends Restivus {
150+
public typedRoutes: Record<string, Record<string, unknown>> = {};
151+
141152
protected apiPath?: string;
142153

143154
public authMethods: ((...args: any[]) => any)[];
@@ -484,6 +495,182 @@ export class APIClass<TBasePath extends string = ''> extends Restivus {
484495
return routeActions.map((action) => this.getFullRouteName(route, action, apiVersion));
485496
}
486497

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

753940
// Allow the endpoints to make usage of the logger which respects the user's settings
754941
(operations[method as keyof Operations<TPathPattern, TOptions>] as Record<string, any>).logger = logger;
755-
});
756942

943+
this.registerTypedRoutesLegacy(method as Method, route, {
944+
...options,
945+
...operations[method as keyof Operations<TPathPattern, TOptions>],
946+
});
947+
});
757948
super.addRoute(route, options, operations);
758949
});
759950
}
@@ -1058,19 +1249,15 @@ const defaultOptionsEndpoint = async function _defaultOptionsEndpoint(this: Rest
10581249
this.done();
10591250
};
10601251

1061-
const createApi = function _createApi(options: { version?: string } = {}): APIClass {
1062-
return new APIClass(
1063-
Object.assign(
1064-
{
1065-
apiPath: 'api/',
1066-
useDefaultAuth: true,
1067-
prettyJson: process.env.NODE_ENV === 'development',
1068-
defaultOptionsEndpoint,
1069-
auth: getUserAuth(),
1070-
},
1071-
options,
1072-
) as IAPIProperties,
1073-
);
1252+
const createApi = function _createApi(options: { version?: string; useDefaultAuth?: true } = {}): APIClass {
1253+
return new APIClass({
1254+
apiPath: 'api/',
1255+
useDefaultAuth: false,
1256+
prettyJson: process.env.NODE_ENV === 'development',
1257+
defaultOptionsEndpoint,
1258+
auth: getUserAuth(),
1259+
...options,
1260+
});
10741261
};
10751262

10761263
export const API: {
@@ -1105,6 +1292,7 @@ export const API: {
11051292
ApiClass: APIClass,
11061293
v1: createApi({
11071294
version: 'v1',
1295+
useDefaultAuth: true,
11081296
}),
11091297
default: createApi(),
11101298
};

0 commit comments

Comments
 (0)