Merge branch 'chore/refactor-mobx' of github.com:makeplane/plane into chore/refactor-mobx

This commit is contained in:
rahulramesha 2024-03-05 15:56:06 +05:30
commit 0b8e23101c
10 changed files with 400 additions and 35 deletions

View File

@ -26,7 +26,7 @@ export class WebhookService extends APIService {
}); });
} }
async createWebhook(workspaceSlug: string, data: {}): Promise<IWebhook> { async createWebhook(workspaceSlug: string, data: Partial<IWebhook>): Promise<IWebhook> {
return this.post(`/api/workspaces/${workspaceSlug}/webhooks/`, data) return this.post(`/api/workspaces/${workspaceSlug}/webhooks/`, data)
.then((response) => response?.data) .then((response) => response?.data)
.catch((error) => { .catch((error) => {
@ -34,7 +34,7 @@ export class WebhookService extends APIService {
}); });
} }
async updateWebhook(workspaceSlug: string, webhookId: string, data: {}): Promise<IWebhook> { async updateWebhook(workspaceSlug: string, webhookId: string, data: Partial<IWebhook>): Promise<IWebhook> {
return this.patch(`/api/workspaces/${workspaceSlug}/webhooks/${webhookId}/`, data) return this.patch(`/api/workspaces/${workspaceSlug}/webhooks/${webhookId}/`, data)
.then((response) => response?.data) .then((response) => response?.data)
.catch((error) => { .catch((error) => {

View File

@ -0,0 +1,110 @@
import { computed, makeObservable, observable } from "mobx";
import { DataStore } from "./dataMaps";
// services
import { APITokenService } from "services/api_token.service";
// types
import { IApiToken } from "@plane/types";
export interface IAPITokenModel {
// model observables
created_at: string;
created_by: string;
description: string;
expired_at: string | null;
id: string;
is_active: boolean;
label: string;
last_used: string | null;
updated_at: string;
updated_by: string;
user: string;
user_type: number;
token?: string;
workspace: string;
// computed
asJSON: IApiToken;
}
export class APIModel implements IAPITokenModel {
// model observables
created_at: string;
created_by: string;
description: string;
expired_at: string | null;
id: string;
is_active: boolean;
label: string;
last_used: string | null;
updated_at: string;
updated_by: string;
user: string;
user_type: number;
token?: string;
workspace: string;
// root store
dataStore;
// services
apiTokenService;
constructor(apiToken: IApiToken, _dataStore: DataStore) {
makeObservable(this, {
// model observables
created_at: observable.ref,
created_by: observable.ref,
description: observable.ref,
expired_at: observable.ref,
id: observable.ref,
is_active: observable.ref,
label: observable.ref,
last_used: observable.ref,
updated_at: observable.ref,
updated_by: observable.ref,
user: observable.ref,
user_type: observable.ref,
token: observable.ref,
workspace: observable.ref,
// computed
asJSON: computed,
});
this.dataStore = _dataStore;
// services
this.apiTokenService = new APITokenService();
this.created_at = apiToken.created_at;
this.created_by = apiToken.created_by;
this.description = apiToken.description;
this.expired_at = apiToken.expired_at;
this.id = apiToken.id;
this.is_active = apiToken.is_active;
this.label = apiToken.label;
this.last_used = apiToken.last_used;
this.updated_at = apiToken.updated_at;
this.updated_by = apiToken.updated_by;
this.user = apiToken.user;
this.user_type = apiToken.user_type;
this.token = apiToken.token;
this.workspace = apiToken.workspace;
}
/**
* @description returns the API token data in JSON format
*/
get asJSON() {
return {
created_at: this.created_at,
created_by: this.created_by,
description: this.description,
expired_at: this.expired_at,
id: this.id,
is_active: this.is_active,
label: this.label,
last_used: this.last_used,
updated_at: this.updated_at,
updated_by: this.updated_by,
user: this.user,
user_type: this.user_type,
token: this.token,
workspace: this.workspace,
};
}
}

View File

@ -0,0 +1,54 @@
import { action, makeObservable, observable } from "mobx";
import { computedFn } from "mobx-utils";
import { set } from "lodash";
// store
import { DataStore } from ".";
import { APIModel } from "store/api-token.store";
// types
import { IApiToken } from "@plane/types";
export interface IAPITokenData {
// observables
apiTokenMap: Record<string, APIModel>;
// actions
addAPIToken: (apiToken: IApiToken) => void;
deleteAPIToken: (apiTokenId: string) => void;
getAPITokenById: (apiTokenId: string) => APIModel | undefined;
}
export class APITokenData implements IAPITokenData {
// observables
apiTokenMap: Record<string, APIModel> = {};
// data store
dataStore;
constructor(_dataStore: DataStore) {
makeObservable(this, {
// observables
apiTokenMap: observable,
// actions
addAPIToken: action,
deleteAPIToken: action,
});
this.dataStore = _dataStore;
}
/**
* @description add a API token to the API token map
* @param {IApiToken} apiToken
*/
addAPIToken = (apiToken: IApiToken) => set(this.apiTokenMap, [apiToken.id], new APIModel(apiToken, this.dataStore));
/**
* @description delete a API token from the API token map
* @param {string} apiTokenId
*/
deleteAPIToken = (apiTokenId: string) => delete this.apiTokenMap[apiTokenId];
/**
* @description get a API token model by its id
* @param {string} apiTokenId
* @returns {APIModel | undefined} API token model
*/
getAPITokenById = computedFn((apiTokenId: string) => this.apiTokenMap[apiTokenId]);
}

View File

@ -1,14 +1,21 @@
import { IIssueData, IssueData } from "./issue.data.store"; import { IIssueData, IssueData } from "./issue.data.store";
import { APITokenData, IAPITokenData } from "./api-token.data.store";
import { IWebhookData, WebhookData } from "./webhook.data.store";
import { IWorkspaceData, WorkspaceData } from "./workspace.data.store"; import { IWorkspaceData, WorkspaceData } from "./workspace.data.store";
export class DataStore { export class DataStore {
workspace: IWorkspaceData; workspace: IWorkspaceData;
issue: IIssueData; issue: IIssueData;
webhook: IWebhookData;
apiToken: IAPITokenData;
constructor() { constructor() {
this.workspace = new WorkspaceData(this); this.workspace = new WorkspaceData(this);
this.webhook = new WebhookData(this);
this.apiToken = new APITokenData(this);
this.issue = new IssueData(this); this.issue = new IssueData(this);
} }
resetOnSignout() {} resetOnSignOut() {}
} }

View File

@ -0,0 +1,54 @@
import { action, makeObservable, observable } from "mobx";
import { computedFn } from "mobx-utils";
import { set } from "lodash";
// store
import { DataStore } from ".";
import { WebhookModel } from "store/webhook.store";
// types
import { IWebhook } from "@plane/types";
export interface IWebhookData {
// observables
webhookMap: Record<string, WebhookModel>;
// actions
addWebhook: (webhooks: IWebhook) => void;
deleteWebhook: (webhookId: string) => void;
getWebhookById: (webhookId: string) => WebhookModel | undefined;
}
export class WebhookData implements IWebhookData {
// observables
webhookMap: Record<string, WebhookModel> = {};
// data store
dataStore;
constructor(_dataStore: DataStore) {
makeObservable(this, {
// observables
webhookMap: observable,
// actions
addWebhook: action,
deleteWebhook: action,
});
this.dataStore = _dataStore;
}
/**
* @description add a webhook to the webhook map
* @param {IWebhook} webhook
*/
addWebhook = (webhook: IWebhook) => set(this.webhookMap, [webhook.id], new WebhookModel(webhook, this.dataStore));
/**
* @description delete a webhook from the webhook map
* @param {string} webhookId
*/
deleteWebhook = (webhookId: string) => delete this.webhookMap[webhookId];
/**
* @description get a webhook model by its id
* @param {string} webhookId
* @returns {WebhookModel | undefined} webhook model
*/
getWebhookById = computedFn((webhookId: string) => this.webhookMap[webhookId]);
}

View File

@ -1,37 +1,55 @@
import { makeObservable, observable } from "mobx"; import { action, makeObservable, observable } from "mobx";
import { WorkspaceModel } from "store/workspace.store"; import { computedFn } from "mobx-utils";
import { DataStore } from ".";
import { IWorkspace } from "@plane/types";
import { set } from "lodash"; import { set } from "lodash";
// store
import { DataStore } from ".";
import { WorkspaceModel } from "store/workspace.store";
// types
import { IWorkspace } from "@plane/types";
export interface IWorkspaceData { export interface IWorkspaceData {
// observables
workspaceMap: Record<string, WorkspaceModel>; workspaceMap: Record<string, WorkspaceModel>;
// actions
addWorkspace: (workspaces: IWorkspace) => void; addWorkspace: (workspaces: IWorkspace) => void;
deleteWorkspace: (workspaceId: string) => void; deleteWorkspace: (workspaceId: string) => void;
getWorkspacebyId: (workspaceId: string) => WorkspaceModel | undefined; getWorkspaceById: (workspaceId: string) => WorkspaceModel | undefined;
} }
export class WorkspaceData implements IWorkspaceData { export class WorkspaceData implements IWorkspaceData {
// observables
workspaceMap: Record<string, WorkspaceModel> = {}; workspaceMap: Record<string, WorkspaceModel> = {};
// data store // data store
dataStore; dataStore;
constructor(_dataStore: DataStore) { constructor(_dataStore: DataStore) {
makeObservable(this, { makeObservable(this, {
// observables
workspaceMap: observable, workspaceMap: observable,
// actions
addWorkspace: action,
deleteWorkspace: action,
}); });
this.dataStore = _dataStore; this.dataStore = _dataStore;
} }
addWorkspace = (workspace: IWorkspace) => { /**
* @description add a workspace to the workspace map
* @param {IWorkspace} workspace
*/
addWorkspace = (workspace: IWorkspace) =>
set(this.workspaceMap, [workspace.id], new WorkspaceModel(workspace, this.dataStore)); set(this.workspaceMap, [workspace.id], new WorkspaceModel(workspace, this.dataStore));
};
deleteWorkspace = (workspaceId: string) => { /**
delete this.workspaceMap[workspaceId]; * @description delete a workspace from the workspace map
}; * @param {string} workspaceId
*/
deleteWorkspace = (workspaceId: string) => delete this.workspaceMap[workspaceId];
getWorkspacebyId = (workspaceId: string) => this.workspaceMap[workspaceId]; /**
* @description get a workspace model by its id
* @param {string} workspaceId
* @returns {WorkspaceModel | undefined} workspace model
*/
getWorkspaceById = computedFn((workspaceId: string) => this.workspaceMap[workspaceId]);
} }

View File

@ -12,5 +12,5 @@ export class RootStore {
this.user = new UserModel(this.data); this.user = new UserModel(this.data);
} }
resetOnSignout() {} resetOnSignOut() {}
} }

View File

@ -26,14 +26,14 @@ export class UserModel implements IUserModel {
} }
/** /**
* get workspace info from the array of workspaces in the store using workspace slug * @description get workspace info from the array of workspaces in the store using workspace slug
* @param workspaceSlug * @param {string} workspaceSlug
*/ */
getWorkspaceBySlug = (workspaceSlug: string) => getWorkspaceBySlug = (workspaceSlug: string) =>
Object.values(this.workspaces ?? {})?.find((w) => w.slug == workspaceSlug) || null; Object.values(this.workspaces ?? {})?.find((w) => w.slug == workspaceSlug) || null;
/** /**
* fetch user workspaces from API * @description fetch user workspaces from API
*/ */
fetchWorkspaces = async () => { fetchWorkspaces = async () => {
const workspaceResponse = await this.workspaceService.userWorkspaces(); const workspaceResponse = await this.workspaceService.userWorkspaces();
@ -48,8 +48,8 @@ export class UserModel implements IUserModel {
}; };
/** /**
* create workspace using the workspace data * @description create workspace using the workspace data
* @param data * @param {Partial<IWorkspace>} data
*/ */
createWorkspace = async (data: Partial<IWorkspace>) => createWorkspace = async (data: Partial<IWorkspace>) =>
await this.workspaceService.createWorkspace(data).then((response) => { await this.workspaceService.createWorkspace(data).then((response) => {
@ -61,8 +61,8 @@ export class UserModel implements IUserModel {
}); });
/** /**
* delete workspace using the workspace slug * @description delete workspace using the workspace slug
* @param workspaceSlug * @param {string} workspaceSlug
*/ */
deleteWorkspace = async (workspaceSlug: string) => deleteWorkspace = async (workspaceSlug: string) =>
await this.workspaceService.deleteWorkspace(workspaceSlug).then(() => { await this.workspaceService.deleteWorkspace(workspaceSlug).then(() => {

View File

@ -46,12 +46,16 @@ export class WebhookModel implements IWebhookModel {
constructor(webhook: IWebhook, _dataStore: DataStore) { constructor(webhook: IWebhook, _dataStore: DataStore) {
makeObservable(this, { makeObservable(this, {
// model observables // model observables
created_at: observable.ref,
cycle: observable.ref, cycle: observable.ref,
id: observable.ref,
is_active: observable.ref, is_active: observable.ref,
issue: observable.ref, issue: observable.ref,
issue_comment: observable.ref, issue_comment: observable.ref,
module: observable.ref, module: observable.ref,
project: observable.ref, project: observable.ref,
secret_key: observable.ref,
updated_at: observable.ref,
url: observable.ref, url: observable.ref,
// computed // computed
asJSON: computed, asJSON: computed,
@ -97,11 +101,11 @@ export class WebhookModel implements IWebhookModel {
/** /**
* @description update a webhook using the data * @description update a webhook using the data
* @param workspaceSlug * @param {string} workspaceSlug
* @param data * @param {Partial<IWebhook>} data
*/ */
updateWebhook = async (workspaceSlug: string, data: Partial<IWebhook>) => { updateWebhook = async (workspaceSlug: string, data: Partial<IWebhook>) => {
const originalData = { ...this }; const originalData = this.asJSON;
// optimistically update the store // optimistically update the store
runInAction(() => { runInAction(() => {
Object.entries(data).forEach(([key, value]) => { Object.entries(data).forEach(([key, value]) => {

View File

@ -3,8 +3,12 @@ import set from "lodash/set";
import { DataStore } from "./dataMaps"; import { DataStore } from "./dataMaps";
// services // services
import { WorkspaceService } from "services/workspace.service"; import { WorkspaceService } from "services/workspace.service";
import { WebhookService } from "services/webhook.service";
import { APITokenService } from "services/api_token.service";
// types // types
import { IUser, IWorkspace } from "@plane/types"; import { IApiToken, IUser, IWebhook, IWorkspace } from "@plane/types";
import { IWebhookModel } from "./webhook.store";
import { IAPITokenModel } from "./api-token.store";
export interface IWorkspaceModel { export interface IWorkspaceModel {
// model observables // model observables
@ -21,13 +25,25 @@ export interface IWorkspaceModel {
url: string; url: string;
updated_at: Date; updated_at: Date;
updated_by: string; updated_by: string;
// data maps
webhooks: Record<string, IWebhookModel>;
apiTokens: Record<string, IAPITokenModel>;
// computed // computed
toJSON: IWorkspace; asJSON: IWorkspace;
// actions // workspace actions
updateWorkspace: (data: Partial<IWorkspace>) => Promise<IWorkspace>; updateWorkspace: (data: Partial<IWorkspace>) => Promise<IWorkspace>;
// webhook actions
fetchWebhooks: () => Promise<IWebhook[]>;
createWebhook: (data: Partial<IWebhook>) => Promise<IWebhook>;
deleteWebhook: (webhookId: string) => Promise<void>;
// API token actions
fetchApiTokens: () => Promise<IApiToken[]>;
createApiToken: (data: Partial<IApiToken>) => Promise<IApiToken>;
deleteApiToken: (tokenId: string) => Promise<void>;
} }
export class WorkspaceModel implements IWorkspaceModel { export class WorkspaceModel implements IWorkspaceModel {
// model observables
created_at: Date; created_at: Date;
created_by: string; created_by: string;
id: string; id: string;
@ -41,10 +57,15 @@ export class WorkspaceModel implements IWorkspaceModel {
updated_at: Date; updated_at: Date;
updated_by: string; updated_by: string;
url: string; url: string;
// data maps
webhooks: Record<string, IWebhookModel> = {};
apiTokens: Record<string, IAPITokenModel> = {};
// root store // root store
dataStore; dataStore;
// services // services
workspaceService; workspaceService;
webhookService;
apiTokenService;
constructor(workspace: IWorkspace, _dataStore: DataStore) { constructor(workspace: IWorkspace, _dataStore: DataStore) {
makeObservable(this, { makeObservable(this, {
@ -62,14 +83,27 @@ export class WorkspaceModel implements IWorkspaceModel {
updated_at: observable.ref, updated_at: observable.ref,
updated_by: observable.ref, updated_by: observable.ref,
url: observable.ref, url: observable.ref,
// data maps
webhooks: observable,
apiTokens: observable,
// computed // computed
toJSON: computed, asJSON: computed,
// actions // workspace actions
updateWorkspace: action, updateWorkspace: action,
// webhook actions
fetchWebhooks: action,
createWebhook: action,
deleteWebhook: action,
// API token actions
fetchApiTokens: action,
createApiToken: action,
deleteApiToken: action,
}); });
this.dataStore = _dataStore; this.dataStore = _dataStore;
// services // services
this.workspaceService = new WorkspaceService(); this.workspaceService = new WorkspaceService();
this.webhookService = new WebhookService();
this.apiTokenService = new APITokenService();
this.created_at = workspace.created_at; this.created_at = workspace.created_at;
this.created_by = workspace.created_by; this.created_by = workspace.created_by;
@ -89,7 +123,7 @@ export class WorkspaceModel implements IWorkspaceModel {
/** /**
* @description returns the workspace data in JSON format * @description returns the workspace data in JSON format
*/ */
get toJSON() { get asJSON() {
return { return {
created_at: this.created_at, created_at: this.created_at,
created_by: this.created_by, created_by: this.created_by,
@ -109,10 +143,10 @@ export class WorkspaceModel implements IWorkspaceModel {
/** /**
* @description update workspace using the new workspace data * @description update workspace using the new workspace data
* @param data * @param {Partial<IWorkspace>} data
*/ */
updateWorkspace = async (data: Partial<IWorkspace>) => { updateWorkspace = async (data: Partial<IWorkspace>) => {
const originalData = { ...this }; const originalData = this.asJSON;
// optimistically update the store // optimistically update the store
runInAction(() => { runInAction(() => {
Object.entries(data).forEach(([key, value]) => { Object.entries(data).forEach(([key, value]) => {
@ -133,4 +167,88 @@ export class WorkspaceModel implements IWorkspaceModel {
throw error; throw error;
} }
}; };
// webhook actions
/**
* @description fetch all the webhooks of the workspace
*/
fetchWebhooks = async () => {
const webhooksResponse = await this.webhookService.fetchWebhooksList(this.slug);
runInAction(() => {
webhooksResponse.forEach((webhook) => {
this.dataStore.webhook.addWebhook(webhook);
set(this.webhooks, [webhook.id], this.dataStore.webhook.webhookMap[webhook.id]);
});
});
return webhooksResponse;
};
/**
* @description create webhook using the webhook data
* @param {Partial<IWebhook>} data
*/
createWebhook = async (data: Partial<IWebhook>) =>
await this.webhookService.createWebhook(this.slug, data).then((response) => {
runInAction(() => {
this.dataStore.webhook.addWebhook(response);
set(this.webhooks, [response.id], this.dataStore.webhook.webhookMap[response.id]);
});
return response;
});
/**
* @description delete a webhook using webhook id
* @param {string} webhookId
*/
deleteWebhook = async (webhookId: string) =>
await this.webhookService.deleteWebhook(this.slug, webhookId).then(() => {
this.dataStore.webhook.deleteWebhook(webhookId);
runInAction(() => {
delete this.webhooks[webhookId];
});
});
// API token actions
/**
* @description fetch all the API tokens of the workspace
*/
fetchApiTokens = async () => {
const apiTokensResponse = await this.apiTokenService.getApiTokens(this.slug);
runInAction(() => {
apiTokensResponse.forEach((apiToken) => {
this.dataStore.apiToken.addAPIToken(apiToken);
set(this.apiTokens, [apiToken.id], this.dataStore.apiToken.apiTokenMap[apiToken.id]);
});
});
return apiTokensResponse;
};
/**
* @description create API token using data
* @param {Partial<IApiToken>} data
*/
createApiToken = async (data: Partial<IApiToken>) =>
await this.apiTokenService.createApiToken(this.slug, data).then((response) => {
runInAction(() => {
this.dataStore.apiToken.addAPIToken(response);
set(this.apiTokens, [response.id], this.dataStore.apiToken.apiTokenMap[response.id]);
});
return response;
});
/**
* @description delete API token using token id
* @param {string} tokenId
*/
deleteApiToken = async (tokenId: string) =>
await this.apiTokenService.deleteApiToken(this.slug, tokenId).then(() => {
this.dataStore.apiToken.deleteAPIToken(tokenId);
runInAction(() => {
delete this.apiTokens[tokenId];
});
});
} }