This repository has been archived on 2024-09-23. You can view files and clone it, but cannot push or open issues or pull requests.
rocketchat-scim/ScimEndpoint.ts
2022-02-15 13:38:33 +01:00

139 lines
4.2 KiB
TypeScript

import {
HttpStatusCode,
IHttp,
IHttpResponse,
IModify,
IPersistence,
IRead,
} from "@rocket.chat/apps-engine/definition/accessors";
import {
ApiEndpoint,
IApiEndpointInfo,
IApiRequest,
IApiResponse,
} from "@rocket.chat/apps-engine/definition/api";
import { IApp } from "@rocket.chat/apps-engine/definition/IApp";
import { Context } from "./Context";
import { ConflictError } from "./errors/ConflictError";
import { EmptyRequestError } from "./errors/EmptyRequestError";
import { EmptyResponseError } from "./errors/EmptyResponseError";
import { JsonParseError } from "./errors/JsonParseError";
import { SCIMError, SCIMErrorType } from "./scim/Error";
type ApiEndpointMethod = (
request: IApiRequest,
endpoint: IApiEndpointInfo,
read: IRead,
modify: IModify,
http: IHttp,
persis: IPersistence,
) => Promise<IApiResponse>;
export interface IScimEndpoint {
_get?(ctx: Context): Promise<IApiResponse>;
_post?(ctx: Context): Promise<IApiResponse>;
_put?(ctx: Context): Promise<IApiResponse>;
_delete?(ctx: Context): Promise<IApiResponse>;
}
export abstract class ScimEndpoint extends ApiEndpoint {
public get: ApiEndpointMethod | undefined;
public post: ApiEndpointMethod | undefined;
public put: ApiEndpointMethod | undefined;
public delete: ApiEndpointMethod | undefined;
constructor(app: IApp) {
super(app);
this.get = this.wrapMethod("get");
this.post = this.wrapMethod("post");
this.put = this.wrapMethod("put");
this.delete = this.wrapMethod("delete");
}
protected success(content?: any): IApiResponse {
return this.response({
status: HttpStatusCode.OK,
content,
});
}
protected response(response: IApiResponse): IApiResponse {
if (response.headers === undefined) {
response.headers = {};
}
response.headers["Content-Type"] = "application/scim+json";
return response;
}
protected error(error: SCIMError): IApiResponse {
return this.response({
status: parseInt(error.status, 10),
content: error,
});
}
protected parseResponse(response: IHttpResponse): any {
if (!response.content) {
throw new EmptyResponseError();
}
let content: any;
try {
content = JSON.parse(response.content);
} catch (e) {
throw new JsonParseError();
}
return content;
}
protected hasContent(request: IApiRequest) {
if (!request.content || Object.keys(request.content).length === 0) {
throw new EmptyRequestError();
}
}
protected handleError(o: any) {
if (!o.success) {
if (o.error?.includes("already in use")) {
throw new ConflictError(
o.error.includes("@") ? "email" : "username",
);
}
if (o.error?.includes("not found")) {
}
throw new Error(o.error);
}
}
private wrapMethod(name: string): ApiEndpointMethod | undefined {
const method = this[`_${name}`];
if (method === undefined || typeof method !== "function") {
return undefined;
}
return async (
request: IApiRequest,
endpoint: IApiEndpointInfo,
read: IRead,
modify: IModify,
http: IHttp,
persis: IPersistence,
): Promise<IApiResponse> => {
try {
return await method.bind(this)(
new Context(request, endpoint, read, modify, http, persis),
);
} catch (e) {
let err: SCIMError;
if (e.toSCIMError && typeof e.toSCIMError === "function") {
err = e.toSCIMError();
} else {
err = new SCIMError()
.setStatus(HttpStatusCode.INTERNAL_SERVER_ERROR)
.setScimType(SCIMErrorType.INVALID_VALUE)
.setDetail(e.message);
}
return this.error(err);
}
};
}
}