diff --git a/web/services/webhook.service.ts b/web/services/webhook.service.ts index abfe7c46d..11b3971cb 100644 --- a/web/services/webhook.service.ts +++ b/web/services/webhook.service.ts @@ -26,7 +26,7 @@ export class WebhookService extends APIService { }); } - async createWebhook(workspaceSlug: string, data: {}): Promise { + async createWebhook(workspaceSlug: string, data: Partial): Promise { return this.post(`/api/workspaces/${workspaceSlug}/webhooks/`, data) .then((response) => response?.data) .catch((error) => { @@ -34,7 +34,7 @@ export class WebhookService extends APIService { }); } - async updateWebhook(workspaceSlug: string, webhookId: string, data: {}): Promise { + async updateWebhook(workspaceSlug: string, webhookId: string, data: Partial): Promise { return this.patch(`/api/workspaces/${workspaceSlug}/webhooks/${webhookId}/`, data) .then((response) => response?.data) .catch((error) => { diff --git a/web/store/api-token.store.ts b/web/store/api-token.store.ts new file mode 100644 index 000000000..1083b583b --- /dev/null +++ b/web/store/api-token.store.ts @@ -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, + }; + } +} diff --git a/web/store/dataMaps/api-token.data.store.ts b/web/store/dataMaps/api-token.data.store.ts new file mode 100644 index 000000000..b08747b67 --- /dev/null +++ b/web/store/dataMaps/api-token.data.store.ts @@ -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; + // actions + addAPIToken: (apiToken: IApiToken) => void; + deleteAPIToken: (apiTokenId: string) => void; + getAPITokenById: (apiTokenId: string) => APIModel | undefined; +} + +export class APITokenData implements IAPITokenData { + // observables + apiTokenMap: Record = {}; + // 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]); +} diff --git a/web/store/dataMaps/index.ts b/web/store/dataMaps/index.ts index 00770a073..d23009dc0 100644 --- a/web/store/dataMaps/index.ts +++ b/web/store/dataMaps/index.ts @@ -1,14 +1,21 @@ 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"; export class DataStore { workspace: IWorkspaceData; issue: IIssueData; + webhook: IWebhookData; + apiToken: IAPITokenData; constructor() { this.workspace = new WorkspaceData(this); + this.webhook = new WebhookData(this); + this.apiToken = new APITokenData(this); + this.issue = new IssueData(this); } - resetOnSignout() {} + resetOnSignOut() {} } diff --git a/web/store/dataMaps/webhook.data.store.ts b/web/store/dataMaps/webhook.data.store.ts new file mode 100644 index 000000000..c80b45e28 --- /dev/null +++ b/web/store/dataMaps/webhook.data.store.ts @@ -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; + // actions + addWebhook: (webhooks: IWebhook) => void; + deleteWebhook: (webhookId: string) => void; + getWebhookById: (webhookId: string) => WebhookModel | undefined; +} + +export class WebhookData implements IWebhookData { + // observables + webhookMap: Record = {}; + // 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]); +} diff --git a/web/store/dataMaps/workspace.data.store.ts b/web/store/dataMaps/workspace.data.store.ts index 801ce458e..245d3557d 100644 --- a/web/store/dataMaps/workspace.data.store.ts +++ b/web/store/dataMaps/workspace.data.store.ts @@ -1,37 +1,55 @@ -import { makeObservable, observable } from "mobx"; -import { WorkspaceModel } from "store/workspace.store"; -import { DataStore } from "."; -import { IWorkspace } from "@plane/types"; +import { action, makeObservable, observable } from "mobx"; +import { computedFn } from "mobx-utils"; import { set } from "lodash"; +// store +import { DataStore } from "."; +import { WorkspaceModel } from "store/workspace.store"; +// types +import { IWorkspace } from "@plane/types"; export interface IWorkspaceData { + // observables workspaceMap: Record; - + // actions addWorkspace: (workspaces: IWorkspace) => void; deleteWorkspace: (workspaceId: string) => void; - getWorkspacebyId: (workspaceId: string) => WorkspaceModel | undefined; + getWorkspaceById: (workspaceId: string) => WorkspaceModel | undefined; } export class WorkspaceData implements IWorkspaceData { + // observables workspaceMap: Record = {}; - // data store dataStore; constructor(_dataStore: DataStore) { makeObservable(this, { + // observables workspaceMap: observable, + // actions + addWorkspace: action, + deleteWorkspace: action, }); 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)); - }; - 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]); } diff --git a/web/store/root.store.ts b/web/store/root.store.ts index d9dd18a82..3cbb96334 100644 --- a/web/store/root.store.ts +++ b/web/store/root.store.ts @@ -12,5 +12,5 @@ export class RootStore { this.user = new UserModel(this.data); } - resetOnSignout() {} + resetOnSignOut() {} } diff --git a/web/store/user.store.ts b/web/store/user.store.ts index 19a593f9e..3628946f5 100644 --- a/web/store/user.store.ts +++ b/web/store/user.store.ts @@ -26,14 +26,14 @@ export class UserModel implements IUserModel { } /** - * get workspace info from the array of workspaces in the store using workspace slug - * @param workspaceSlug + * @description get workspace info from the array of workspaces in the store using workspace slug + * @param {string} workspaceSlug */ getWorkspaceBySlug = (workspaceSlug: string) => Object.values(this.workspaces ?? {})?.find((w) => w.slug == workspaceSlug) || null; /** - * fetch user workspaces from API + * @description fetch user workspaces from API */ fetchWorkspaces = async () => { const workspaceResponse = await this.workspaceService.userWorkspaces(); @@ -48,8 +48,8 @@ export class UserModel implements IUserModel { }; /** - * create workspace using the workspace data - * @param data + * @description create workspace using the workspace data + * @param {Partial} data */ createWorkspace = async (data: Partial) => await this.workspaceService.createWorkspace(data).then((response) => { @@ -61,8 +61,8 @@ export class UserModel implements IUserModel { }); /** - * delete workspace using the workspace slug - * @param workspaceSlug + * @description delete workspace using the workspace slug + * @param {string} workspaceSlug */ deleteWorkspace = async (workspaceSlug: string) => await this.workspaceService.deleteWorkspace(workspaceSlug).then(() => { diff --git a/web/store/webhook.store.ts b/web/store/webhook.store.ts index 8e6a84870..92eb31671 100644 --- a/web/store/webhook.store.ts +++ b/web/store/webhook.store.ts @@ -46,12 +46,16 @@ export class WebhookModel implements IWebhookModel { constructor(webhook: IWebhook, _dataStore: DataStore) { makeObservable(this, { // model observables + created_at: observable.ref, cycle: observable.ref, + id: observable.ref, is_active: observable.ref, issue: observable.ref, issue_comment: observable.ref, module: observable.ref, project: observable.ref, + secret_key: observable.ref, + updated_at: observable.ref, url: observable.ref, // computed asJSON: computed, @@ -97,11 +101,11 @@ export class WebhookModel implements IWebhookModel { /** * @description update a webhook using the data - * @param workspaceSlug - * @param data + * @param {string} workspaceSlug + * @param {Partial} data */ updateWebhook = async (workspaceSlug: string, data: Partial) => { - const originalData = { ...this }; + const originalData = this.asJSON; // optimistically update the store runInAction(() => { Object.entries(data).forEach(([key, value]) => { diff --git a/web/store/workspace.store.ts b/web/store/workspace.store.ts index 23cf2eba8..069ca030d 100644 --- a/web/store/workspace.store.ts +++ b/web/store/workspace.store.ts @@ -3,8 +3,12 @@ import set from "lodash/set"; import { DataStore } from "./dataMaps"; // services import { WorkspaceService } from "services/workspace.service"; +import { WebhookService } from "services/webhook.service"; +import { APITokenService } from "services/api_token.service"; // 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 { // model observables @@ -21,13 +25,25 @@ export interface IWorkspaceModel { url: string; updated_at: Date; updated_by: string; + // data maps + webhooks: Record; + apiTokens: Record; // computed - toJSON: IWorkspace; - // actions + asJSON: IWorkspace; + // workspace actions updateWorkspace: (data: Partial) => Promise; + // webhook actions + fetchWebhooks: () => Promise; + createWebhook: (data: Partial) => Promise; + deleteWebhook: (webhookId: string) => Promise; + // API token actions + fetchApiTokens: () => Promise; + createApiToken: (data: Partial) => Promise; + deleteApiToken: (tokenId: string) => Promise; } export class WorkspaceModel implements IWorkspaceModel { + // model observables created_at: Date; created_by: string; id: string; @@ -41,10 +57,15 @@ export class WorkspaceModel implements IWorkspaceModel { updated_at: Date; updated_by: string; url: string; + // data maps + webhooks: Record = {}; + apiTokens: Record = {}; // root store dataStore; // services workspaceService; + webhookService; + apiTokenService; constructor(workspace: IWorkspace, _dataStore: DataStore) { makeObservable(this, { @@ -62,14 +83,27 @@ export class WorkspaceModel implements IWorkspaceModel { updated_at: observable.ref, updated_by: observable.ref, url: observable.ref, + // data maps + webhooks: observable, + apiTokens: observable, // computed - toJSON: computed, - // actions + asJSON: computed, + // workspace actions updateWorkspace: action, + // webhook actions + fetchWebhooks: action, + createWebhook: action, + deleteWebhook: action, + // API token actions + fetchApiTokens: action, + createApiToken: action, + deleteApiToken: action, }); this.dataStore = _dataStore; // services this.workspaceService = new WorkspaceService(); + this.webhookService = new WebhookService(); + this.apiTokenService = new APITokenService(); this.created_at = workspace.created_at; this.created_by = workspace.created_by; @@ -89,7 +123,7 @@ export class WorkspaceModel implements IWorkspaceModel { /** * @description returns the workspace data in JSON format */ - get toJSON() { + get asJSON() { return { created_at: this.created_at, created_by: this.created_by, @@ -109,10 +143,10 @@ export class WorkspaceModel implements IWorkspaceModel { /** * @description update workspace using the new workspace data - * @param data + * @param {Partial} data */ updateWorkspace = async (data: Partial) => { - const originalData = { ...this }; + const originalData = this.asJSON; // optimistically update the store runInAction(() => { Object.entries(data).forEach(([key, value]) => { @@ -133,4 +167,88 @@ export class WorkspaceModel implements IWorkspaceModel { 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} data + */ + createWebhook = async (data: Partial) => + 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} data + */ + createApiToken = async (data: Partial) => + 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]; + }); + }); }