change folder structure

This commit is contained in:
Hugo Renard 2022-02-15 14:09:03 +01:00
parent 4f1fa62c83
commit 60f9cc8b2c
Signed by: hougo
GPG key ID: 3A285FD470209C59
25 changed files with 285 additions and 316 deletions

View file

@ -1,76 +0,0 @@
import {
IHttp,
IHttpRequest,
IHttpResponse,
IRead,
} from "@rocket.chat/apps-engine/definition/accessors";
export class RcHttp implements IHttp {
private readonly baseUrl = "http://localhost:3000/api/v1";
private readonly http: IHttp;
private readonly read: IRead;
constructor(http: IHttp, read: IRead) {
this.http = http;
this.read = read;
}
public async get(url: string, content?: any): Promise<IHttpResponse> {
return this.http.get(
this.buildUrl(url),
await this.buildOptions(content),
);
}
public async post(url: string, content?: any): Promise<IHttpResponse> {
return this.http.post(
this.buildUrl(url),
await this.buildOptions(content),
);
}
public async put(url: string, content?: any): Promise<IHttpResponse> {
return this.http.put(
this.buildUrl(url),
await this.buildOptions(content),
);
}
public async del(url: string, content?: any): Promise<IHttpResponse> {
return this.http.del(
this.buildUrl(url),
await this.buildOptions(content),
);
}
public async patch(url: string, content?: any): Promise<IHttpResponse> {
return this.http.patch(
this.buildUrl(url),
await this.buildOptions(content),
);
}
private buildUrl(url: string): string {
return `${this.baseUrl}/${url}`;
}
private async buildOptions(content?: any): Promise<IHttpRequest> {
const options: IHttpRequest = {
headers: {
"X-User-Id": await this.read
.getEnvironmentReader()
.getSettings()
.getValueById("rc-user-id"),
"X-Auth-Token": await this.read
.getEnvironmentReader()
.getSettings()
.getValueById("rc-token"),
"Content-Type": "application/json",
},
};
if (content !== undefined) {
options.content = JSON.stringify(content);
}
return options;
}
}

View file

@ -10,11 +10,11 @@ import {
import { App } from "@rocket.chat/apps-engine/definition/App"; import { App } from "@rocket.chat/apps-engine/definition/App";
import { IAppInfo } from "@rocket.chat/apps-engine/definition/metadata"; import { IAppInfo } from "@rocket.chat/apps-engine/definition/metadata";
import { SettingType } from "@rocket.chat/apps-engine/definition/settings"; import { SettingType } from "@rocket.chat/apps-engine/definition/settings";
import { GroupEndpoint } from "./GroupEndpoint"; import { GroupEndpoint } from "./src/endpoints/GroupEndpoint";
import { GroupsEndpoint } from "./GroupsEndpoint"; import { GroupsEndpoint } from "./src/endpoints/GroupsEndpoint";
import { UserEndpoint } from "./UserEndpoint"; import { UserEndpoint } from "./src/endpoints/UserEndpoint";
import { UsersEndpoint } from "./UsersEndpoint"; import { UsersEndpoint } from "./src/endpoints/UsersEndpoint";
export class ScimApp extends App { export class ScimApp extends App {
constructor(info: IAppInfo, logger: ILogger, accessors: IAppAccessors) { constructor(info: IAppInfo, logger: ILogger, accessors: IAppAccessors) {

View file

@ -1,70 +0,0 @@
import { RcSdk } from "./RcSdk";
interface ITeamRemoveMemberBody {
teamId: string;
userId: string;
}
interface ITeamAddMemberBody {
teamId: string;
members: Array<{
userId: string;
roles: Array<string>;
}>;
}
interface ITeamDeleteBody {
teamId: string;
roomsToRemove?: Array<string>;
}
interface ITeamCreateBody {
name: string;
type: 0 | 1;
members?: Array<string>;
room?: {
readOnly: boolean;
};
}
export class RcSdkTeam {
private sdk: RcSdk;
constructor(sdk: RcSdk) {
this.sdk = sdk;
}
public async listAll(): Promise<any> {
const response = await this.sdk.get(`teams.listAll`);
return this.sdk.parseResponse(response);
}
public async members(teamId: string): Promise<any> {
const response = await this.sdk.get(`teams.members?teamId=${teamId}`);
return this.sdk.parseResponse(response);
}
public async info(teamId: string): Promise<any> {
const response = await this.sdk.get(`teams.info?teamId=${teamId}`);
return this.sdk.parseResponse(response);
}
public async delete(body: ITeamDeleteBody): Promise<any> {
const response = await this.sdk.post(`teams.delete`, body);
return this.sdk.parseResponse(response);
}
public async create(body: ITeamCreateBody): Promise<any> {
const response = await this.sdk.post(`teams.create`, body);
return this.sdk.parseResponse(response);
}
public async removeMember(body: ITeamRemoveMemberBody): Promise<any> {
const response = await this.sdk.post(`teams.removeMember`, body);
return this.sdk.parseResponse(response);
}
public async addMembers(body: ITeamAddMemberBody): Promise<any> {
const response = await this.sdk.post(`teams.addMembers`, body);
return this.sdk.parseResponse(response);
}
}

View file

@ -1,35 +0,0 @@
import { RcSdk } from "./RcSdk";
export class RcSdkUser {
private sdk: RcSdk;
constructor(sdk: RcSdk) {
this.sdk = sdk;
}
public async list(): Promise<any> {
const response = await this.sdk.get(
`users.list?query={"type":{"$eq":"user"}}&fields={"createdAt":1}`,
);
return this.sdk.parseResponse(response);
}
public async info(userId: string): Promise<any> {
const response = await this.sdk.get(`users.info?userId=${userId}`);
return this.sdk.parseResponse(response);
}
public async update(body: IUserUpdate): Promise<any> {
const response = await this.sdk.post(`users.update`, body);
return this.sdk.parseResponse(response);
}
public async delete(body: IUserDelete): Promise<any> {
const response = await this.sdk.post(`users.delete`);
return this.sdk.parseResponse(response);
}
public async create(body: IUserCreate): Promise<any> {
const response = await this.sdk.post(`users.create`, body);
return this.sdk.parseResponse(response);
}
}

62
rest.ts
View file

@ -1,62 +0,0 @@
interface IUserCreate {
email: string;
name: string;
password: string;
username: string;
active?: boolean;
roles?: Array<string>;
joinDefaultChannels?: boolean;
requirePasswordChange?: boolean;
sendWelcomeEmail?: boolean;
verified?: boolean;
customFields?: any;
}
interface IUserUpdate {
userId: string;
data: {
email?: string;
name?: string;
password?: string;
username?: string;
active?: boolean;
roles?: Array<string>;
joinDefaultChannels?: boolean;
requirePasswordChange?: boolean;
sendWelcomeEmail?: boolean;
verified?: boolean;
customFields?: any;
};
}
interface IUserDelete {
userId: string;
confirmRelinquish?: boolean;
}
interface IUser {
_id: string;
emails: Array<{ address: string; verified: boolean }>;
name: string;
username: string;
active?: boolean;
createdAt: string;
_updatedAt?: string;
}
interface ITeam {
_id: string;
name: string;
active?: boolean;
createdAt: string;
_updatedAt?: string;
}
interface ITeamMember {
user: {
_id: string;
name: string;
username: string;
};
createdAt: string;
}

View file

@ -8,8 +8,8 @@ import {
IApiEndpointInfo, IApiEndpointInfo,
IApiRequest, IApiRequest,
} from "@rocket.chat/apps-engine/definition/api"; } from "@rocket.chat/apps-engine/definition/api";
import { EmptyRequestError } from "./errors/EmptyRequestError"; import { EmptyRequestError } from "../errors/EmptyRequestError";
import { RcSdk } from "./rc-sdk/RcSdk"; import { RcSdk } from "../rc-sdk/RcSdk";
export class Context { export class Context {
public readonly rc: RcSdk; public readonly rc: RcSdk;

View file

@ -1,8 +1,7 @@
import { HttpStatusCode } from "@rocket.chat/apps-engine/definition/accessors"; import { HttpStatusCode } from "@rocket.chat/apps-engine/definition/accessors";
import { IApiResponse } from "@rocket.chat/apps-engine/definition/api"; import { IApiResponse } from "@rocket.chat/apps-engine/definition/api";
import { SCIMGroup } from "../scim/Group";
import { Context } from "./Context"; import { Context } from "./Context";
import { SCIMGroup } from "./scim/Group";
import { SCIMUser } from "./scim/User";
import { IScimEndpoint, ScimEndpoint } from "./ScimEndpoint"; import { IScimEndpoint, ScimEndpoint } from "./ScimEndpoint";
export class GroupEndpoint extends ScimEndpoint implements IScimEndpoint { export class GroupEndpoint extends ScimEndpoint implements IScimEndpoint {
@ -33,7 +32,7 @@ export class GroupEndpoint extends ScimEndpoint implements IScimEndpoint {
SCIMGroup.fromPlain(ctx.content()).members.map((x) => x.value), SCIMGroup.fromPlain(ctx.content()).members.map((x) => x.value),
); );
const currentIds = new Set<string>( const currentIds = new Set<string>(
membersRaw.members.map((x: ITeamMember) => x.user._id), membersRaw.members.map((x) => x.user._id),
); );
const removeMember = async (userId: string) => { const removeMember = async (userId: string) => {
const removeMembersRaw = await ctx.rc.team.removeMember({ const removeMembersRaw = await ctx.rc.team.removeMember({

View file

@ -1,21 +1,8 @@
import { import { HttpStatusCode } from "@rocket.chat/apps-engine/definition/accessors";
HttpStatusCode, import { IApiResponse } from "@rocket.chat/apps-engine/definition/api";
IHttp, import { SCIMGroup } from "../scim/Group";
IModify, import { SCIMListResponse } from "../scim/ListResponse";
IPersistence,
IRead,
} from "@rocket.chat/apps-engine/definition/accessors";
import {
IApiEndpointInfo,
IApiRequest,
IApiResponse,
} from "@rocket.chat/apps-engine/definition/api";
import crypto = require("crypto");
import { Context } from "./Context"; import { Context } from "./Context";
import { RcHttp } from "./RcHttp";
import { SCIMGroup } from "./scim/Group";
import { SCIMListResponse } from "./scim/ListResponse";
import { SCIMUser } from "./scim/User";
import { IScimEndpoint, ScimEndpoint } from "./ScimEndpoint"; import { IScimEndpoint, ScimEndpoint } from "./ScimEndpoint";
export class GroupsEndpoint extends ScimEndpoint implements IScimEndpoint { export class GroupsEndpoint extends ScimEndpoint implements IScimEndpoint {
@ -24,7 +11,7 @@ export class GroupsEndpoint extends ScimEndpoint implements IScimEndpoint {
public async _get(ctx: Context): Promise<IApiResponse> { public async _get(ctx: Context): Promise<IApiResponse> {
const teamsRaw = await ctx.rc.team.listAll(); const teamsRaw = await ctx.rc.team.listAll();
this.handleError(teamsRaw); this.handleError(teamsRaw);
const groups = teamsRaw.teams.map(async (team: ITeam) => { const groups = teamsRaw.teams.map(async (team) => {
const membersRaw = await ctx.rc.team.members(team._id); const membersRaw = await ctx.rc.team.members(team._id);
this.handleError(membersRaw); this.handleError(membersRaw);
return SCIMGroup.fromRC(team, membersRaw.members); return SCIMGroup.fromRC(team, membersRaw.members);

View file

@ -13,12 +13,12 @@ import {
IApiResponse, IApiResponse,
} from "@rocket.chat/apps-engine/definition/api"; } from "@rocket.chat/apps-engine/definition/api";
import { IApp } from "@rocket.chat/apps-engine/definition/IApp"; import { IApp } from "@rocket.chat/apps-engine/definition/IApp";
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";
import { Context } from "./Context"; 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 = ( type ApiEndpointMethod = (
request: IApiRequest, request: IApiRequest,

View file

@ -1,7 +1,7 @@
import { HttpStatusCode } from "@rocket.chat/apps-engine/definition/accessors"; import { HttpStatusCode } from "@rocket.chat/apps-engine/definition/accessors";
import { IApiResponse } from "@rocket.chat/apps-engine/definition/api"; import { IApiResponse } from "@rocket.chat/apps-engine/definition/api";
import { SCIMUser } from "../scim/User";
import { Context } from "./Context"; import { Context } from "./Context";
import { SCIMUser } from "./scim/User";
import { IScimEndpoint, ScimEndpoint } from "./ScimEndpoint"; import { IScimEndpoint, ScimEndpoint } from "./ScimEndpoint";
export class UserEndpoint extends ScimEndpoint implements IScimEndpoint { export class UserEndpoint extends ScimEndpoint implements IScimEndpoint {
@ -15,9 +15,21 @@ export class UserEndpoint extends ScimEndpoint implements IScimEndpoint {
} }
public async _put(ctx: Context): Promise<IApiResponse> { public async _put(ctx: Context): Promise<IApiResponse> {
const o = await ctx.rc.user.update( const u = SCIMUser.fromPlain(ctx.content());
this.scimToUserUpdate(ctx.id(), SCIMUser.fromPlain(ctx.content())), const o = await ctx.rc.user.update({
); userId: ctx.id(),
data: {
email: u.getEmail(),
name: u.displayName,
username: u.userName,
active: u.active,
verified: true,
customFields: {
scimExternalId: u.externalId,
},
},
});
this.handleError(o); this.handleError(o);
const user = SCIMUser.fromRC(o.user); const user = SCIMUser.fromRC(o.user);
return this.success(user); return this.success(user);
@ -33,20 +45,4 @@ export class UserEndpoint extends ScimEndpoint implements IScimEndpoint {
status: HttpStatusCode.NO_CONTENT, status: HttpStatusCode.NO_CONTENT,
}); });
} }
private scimToUserUpdate(userId: string, user: SCIMUser): IUserUpdate {
return {
userId,
data: {
email: user.getEmail(),
name: user.displayName,
username: user.userName,
active: user.active,
verified: true,
customFields: {
scimExternalId: user.externalId,
},
},
};
}
} }

View file

@ -1,9 +1,9 @@
import { HttpStatusCode } from "@rocket.chat/apps-engine/definition/accessors"; import { HttpStatusCode } from "@rocket.chat/apps-engine/definition/accessors";
import { IApiResponse } from "@rocket.chat/apps-engine/definition/api"; import { IApiResponse } from "@rocket.chat/apps-engine/definition/api";
import crypto = require("crypto"); import crypto = require("crypto");
import { SCIMListResponse } from "../scim/ListResponse";
import { SCIMUser } from "../scim/User";
import { Context } from "./Context"; import { Context } from "./Context";
import { SCIMListResponse } from "./scim/ListResponse";
import { SCIMUser } from "./scim/User";
import { IScimEndpoint, ScimEndpoint } from "./ScimEndpoint"; import { IScimEndpoint, ScimEndpoint } from "./ScimEndpoint";
export class UsersEndpoint extends ScimEndpoint implements IScimEndpoint { export class UsersEndpoint extends ScimEndpoint implements IScimEndpoint {
@ -19,9 +19,20 @@ export class UsersEndpoint extends ScimEndpoint implements IScimEndpoint {
} }
public async _post(ctx: Context): Promise<IApiResponse> { public async _post(ctx: Context): Promise<IApiResponse> {
const o = await ctx.rc.user.create( const u = SCIMUser.fromPlain(ctx.content());
this.scimToUserCreate(SCIMUser.fromPlain(ctx.content())), const o = await ctx.rc.user.create({
); email: u.getEmail(),
name:
u.displayName ||
`${u.name.givenName} ${u.name.familyName}` ||
u.userName,
username: u.userName,
password: crypto.randomBytes(64).toString("base64").slice(0, 64),
verified: true,
customFields: {
scimExternalId: u.externalId,
},
});
this.handleError(o); this.handleError(o);
const user = SCIMUser.fromRC(o.user); const user = SCIMUser.fromRC(o.user);
return this.response({ return this.response({
@ -29,20 +40,4 @@ export class UsersEndpoint extends ScimEndpoint implements IScimEndpoint {
content: user, content: user,
}); });
} }
private scimToUserCreate(user: SCIMUser): IUserCreate {
return {
email: user.getEmail(),
name:
user.displayName ||
`${user.name.givenName} ${user.name.familyName}` ||
user.userName,
username: user.userName,
password: crypto.randomBytes(64).toString("base64").slice(0, 64),
verified: true,
customFields: {
scimExternalId: user.externalId,
},
};
}
} }

132
src/rc-sdk/RcSdkTeam.ts Normal file
View file

@ -0,0 +1,132 @@
import { RcSdk } from "./RcSdk";
interface ITeamRemoveMemberBody {
teamId: string;
userId: string;
}
interface ITeamAddMemberBody {
teamId: string;
members: Array<{
userId: string;
roles: Array<string>;
}>;
}
interface ITeamDeleteBody {
teamId: string;
roomsToRemove?: Array<string>;
}
interface ITeamCreateBody {
name: string;
type: 0 | 1;
members?: Array<string>;
room?: {
readOnly: boolean;
};
}
export interface ITeam {
_id: string;
name: string;
active?: boolean;
createdAt: string;
_updatedAt?: string;
}
export interface ITeamMember {
user: {
_id: string;
name: string;
username: string;
};
createdAt: string;
}
interface ITeamListAllResponse {
teams: Array<ITeam>;
count: number;
offset: number;
total: number;
success: boolean;
error?: string;
}
interface ITeamMembersResponse {
members: Array<ITeamMember>;
count: number;
offset: number;
total: number;
success: boolean;
error?: string;
}
interface ITeamInfoResponse {
teamInfo: ITeam;
success: boolean;
error?: string;
}
interface ITeamDeleteResponse {
teamInfo: ITeam;
success: boolean;
error?: string;
}
interface ITeamCreateResponse {
team: ITeam;
success: boolean;
error?: string;
}
interface ITeamMemberResponse {
success: boolean;
error?: string;
}
export class RcSdkTeam {
private sdk: RcSdk;
constructor(sdk: RcSdk) {
this.sdk = sdk;
}
public async listAll(): Promise<ITeamListAllResponse> {
const response = await this.sdk.get(`teams.listAll`);
return this.sdk.parseResponse(response);
}
public async members(teamId: string): Promise<ITeamMembersResponse> {
const response = await this.sdk.get(`teams.members?teamId=${teamId}`);
return this.sdk.parseResponse(response);
}
public async info(teamId: string): Promise<ITeamInfoResponse> {
const response = await this.sdk.get(`teams.info?teamId=${teamId}`);
return this.sdk.parseResponse(response);
}
public async delete(body: ITeamDeleteBody): Promise<ITeamDeleteResponse> {
const response = await this.sdk.post(`teams.delete`, body);
return this.sdk.parseResponse(response);
}
public async create(body: ITeamCreateBody): Promise<ITeamCreateResponse> {
const response = await this.sdk.post(`teams.create`, body);
return this.sdk.parseResponse(response);
}
public async removeMember(
body: ITeamRemoveMemberBody,
): Promise<ITeamMemberResponse> {
const response = await this.sdk.post(`teams.removeMember`, body);
return this.sdk.parseResponse(response);
}
public async addMembers(
body: ITeamAddMemberBody,
): Promise<ITeamMemberResponse> {
const response = await this.sdk.post(`teams.addMembers`, body);
return this.sdk.parseResponse(response);
}
}

101
src/rc-sdk/RcSdkUser.ts Normal file
View file

@ -0,0 +1,101 @@
import { RcSdk } from "./RcSdk";
interface IUserCreate {
email: string;
name: string;
password: string;
username: string;
active?: boolean;
roles?: Array<string>;
joinDefaultChannels?: boolean;
requirePasswordChange?: boolean;
sendWelcomeEmail?: boolean;
verified?: boolean;
customFields?: any;
}
interface IUserUpdate {
userId: string;
data: {
email?: string;
name?: string;
password?: string;
username?: string;
active?: boolean;
roles?: Array<string>;
joinDefaultChannels?: boolean;
requirePasswordChange?: boolean;
sendWelcomeEmail?: boolean;
verified?: boolean;
customFields?: any;
};
}
interface IUserDelete {
userId: string;
confirmRelinquish?: boolean;
}
export interface IUser {
_id: string;
emails: Array<{ address: string; verified: boolean }>;
name: string;
username: string;
active?: boolean;
createdAt: string;
_updatedAt?: string;
}
interface IUserListResponse {
users: Array<IUser>;
count: number;
offset: number;
total: number;
success: boolean;
error?: string;
}
interface IUserResponse {
user: IUser;
success: boolean;
error?: string;
}
interface IUserDeleteResponse {
success: boolean;
error?: string;
}
export class RcSdkUser {
private sdk: RcSdk;
constructor(sdk: RcSdk) {
this.sdk = sdk;
}
public async list(): Promise<IUserListResponse> {
const response = await this.sdk.get(
`users.list?query={"type":{"$eq":"user"}}&fields={"createdAt":1}`,
);
return this.sdk.parseResponse(response);
}
public async info(userId: string): Promise<IUserResponse> {
const response = await this.sdk.get(`users.info?userId=${userId}`);
return this.sdk.parseResponse(response);
}
public async update(body: IUserUpdate): Promise<IUserResponse> {
const response = await this.sdk.post(`users.update`, body);
return this.sdk.parseResponse(response);
}
public async delete(body: IUserDelete): Promise<IUserDeleteResponse> {
const response = await this.sdk.post(`users.delete`);
return this.sdk.parseResponse(response);
}
public async create(body: IUserCreate): Promise<IUserResponse> {
const response = await this.sdk.post(`users.create`, body);
return this.sdk.parseResponse(response);
}
}

View file

@ -1,3 +1,4 @@
import { ITeam, ITeamMember } from "../rc-sdk/RcSdkTeam";
import { ISCIMGroupMember, ISCIMResource } from "./Interfaces"; import { ISCIMGroupMember, ISCIMResource } from "./Interfaces";
import { SCIMMeta } from "./Meta"; import { SCIMMeta } from "./Meta";

View file

@ -1,3 +1,4 @@
import { IUser } from "../rc-sdk/RcSdkUser";
import { ISCIMResource, ISCIMUserEmail, ISCIMUserName } from "./Interfaces"; import { ISCIMResource, ISCIMUserEmail, ISCIMUserName } from "./Interfaces";
import { SCIMMeta } from "./Meta"; import { SCIMMeta } from "./Meta";