diff --git a/web/store/workspace/api-token.store.ts b/web/store/workspace/api-token.store.ts new file mode 100644 index 000000000..69a614062 --- /dev/null +++ b/web/store/workspace/api-token.store.ts @@ -0,0 +1,184 @@ +// mobx +import { action, observable, makeObservable, runInAction } from "mobx"; +import { APITokenService } from "services/api_token.service"; +import { RootStore } from "../root.store"; +// types +import { IApiToken } from "types/api_token"; + +export interface IApiTokenStore { + // states + loader: boolean; + error: any | null; + + // observables + apiTokens: Record | null; + + // computed actions + getApiTokenById: (apiTokenId: string) => IApiToken | null; + + // actions + fetchApiTokens: (workspaceSlug: string) => Promise; + fetchApiTokenDetails: (workspaceSlug: string, tokenId: string) => Promise; + createApiToken: (workspaceSlug: string, data: Partial) => Promise; + deleteApiToken: (workspaceSlug: string, tokenId: string) => Promise; +} + +export class ApiTokenStore implements IApiTokenStore { + // states + loader: boolean = false; + error: any | null = null; + + // observables + apiTokens: Record | null = null; + + // services + apiTokenService; + // root store + rootStore; + + constructor(_rootStore: RootStore) { + makeObservable(this, { + // states + loader: observable.ref, + error: observable.ref, + + // observables + apiTokens: observable, + + // computed actions + getApiTokenById: action, + + // actions + fetchApiTokens: action, + fetchApiTokenDetails: action, + createApiToken: action, + deleteApiToken: action, + }); + + // services + this.apiTokenService = new APITokenService(); + // root store + this.rootStore = _rootStore; + } + + /** + * get API token by id + * @param apiTokenId + */ + getApiTokenById = (apiTokenId: string) => { + if (!this.apiTokens) return null; + + return this.apiTokens[apiTokenId] || null; + }; + + /** + * fetch all the API tokens for a workspace + * @param workspaceSlug + */ + fetchApiTokens = async (workspaceSlug: string) => { + try { + this.loader = true; + this.error = null; + + const response = await this.apiTokenService.getApiTokens(workspaceSlug); + + const apiTokensObject: { [apiTokenId: string]: IApiToken } = response.reduce((accumulator, currentWebhook) => { + if (currentWebhook && currentWebhook.id) { + return { ...accumulator, [currentWebhook.id]: currentWebhook }; + } + return accumulator; + }, {}); + + runInAction(() => { + this.apiTokens = apiTokensObject; + }); + + return response; + } catch (error) { + runInAction(() => { + this.error = error; + }); + + throw error; + } + }; + + /** + * fetch API token details using token id + * @param workspaceSlug + * @param tokenId + */ + fetchApiTokenDetails = async (workspaceSlug: string, tokenId: string) => { + try { + this.loader = true; + this.error = null; + + const response = await this.apiTokenService.retrieveApiToken(workspaceSlug, tokenId); + + runInAction(() => { + this.apiTokens = { ...this.apiTokens, [response.id]: response }; + }); + + return response; + } catch (error) { + runInAction(() => { + this.error = error; + }); + + throw error; + } + }; + + /** + * create API token using data + * @param workspaceSlug + * @param data + */ + createApiToken = async (workspaceSlug: string, data: Partial) => { + try { + this.loader = true; + this.error = null; + + const response = await this.apiTokenService.createApiToken(workspaceSlug, data); + + runInAction(() => { + this.apiTokens = { ...this.apiTokens, [response.id]: response }; + }); + + return response; + } catch (error) { + runInAction(() => { + this.error = error; + }); + + throw error; + } + }; + + /** + * delete API token using token id + * @param workspaceSlug + * @param tokenId + */ + deleteApiToken = async (workspaceSlug: string, tokenId: string) => { + try { + this.loader = true; + this.error = null; + + await this.apiTokenService.deleteApiToken(workspaceSlug, tokenId); + + const updatedApiTokens = { ...this.apiTokens }; + delete updatedApiTokens[tokenId]; + + runInAction(() => { + this.apiTokens = updatedApiTokens; + }); + } catch (error) { + runInAction(() => { + this.error = error; + }); + + throw error; + } + }; +} diff --git a/web/store/workspace/index.ts b/web/store/workspace/index.ts new file mode 100644 index 000000000..24c60b66c --- /dev/null +++ b/web/store/workspace/index.ts @@ -0,0 +1,245 @@ +import { action, computed, observable, makeObservable, runInAction } from "mobx"; +import { RootStore } from "../root.store"; +// types +import { IWorkspace } from "types"; +// services +import { WorkspaceService } from "services/workspace.service"; +// sub-stores +import { WebhookStore } from "./webhook.store"; +import { ApiTokenStore } from "./api-token.store"; + +export interface IWorkspaceStore { + // states + loader: boolean; + error: any | null; + + // observables + workspaces: IWorkspace[] | undefined; + + // computed + currentWorkspace: IWorkspace | null; + workspacesCreatedByCurrentUser: IWorkspace[] | null; + + // computed actions + getWorkspaceBySlug: (workspaceSlug: string) => IWorkspace | null; + getWorkspaceById: (workspaceId: string) => IWorkspace | null; + + // actions + fetchWorkspaces: () => Promise; + createWorkspace: (data: Partial) => Promise; + updateWorkspace: (workspaceSlug: string, data: Partial) => Promise; + deleteWorkspace: (workspaceSlug: string) => Promise; + + // sub-stores + webhook: WebhookStore; + apiToken: ApiTokenStore; +} + +export class WorkspaceStore implements IWorkspaceStore { + // states + loader: boolean = false; + error: any | null = null; + + // observables + workspaces: IWorkspace[] | undefined = []; + + // services + workspaceService; + // root store + rootStore; + // sub-stores + webhook: WebhookStore; + apiToken: ApiTokenStore; + + constructor(_rootStore: RootStore) { + makeObservable(this, { + // states + loader: observable.ref, + error: observable.ref, + + // observables + workspaces: observable, + + // computed + currentWorkspace: computed, + workspacesCreatedByCurrentUser: computed, + + // computed actions + getWorkspaceBySlug: action, + getWorkspaceById: action, + + // actions + fetchWorkspaces: action, + createWorkspace: action, + updateWorkspace: action, + deleteWorkspace: action, + }); + + // services + this.workspaceService = new WorkspaceService(); + // root store + this.rootStore = _rootStore; + // sub-stores + this.webhook = new WebhookStore(_rootStore); + this.apiToken = new ApiTokenStore(_rootStore); + } + + /** + * computed value of current workspace based on workspace slug saved in the query store + */ + get currentWorkspace() { + const workspaceSlug = this.rootStore.app.router.query?.workspaceSlug; + + if (!workspaceSlug) return null; + + return this.workspaces?.find((workspace) => workspace.slug === workspaceSlug.toString()) || null; + } + + /** + * computed value of all the workspaces created by the current logged in user + */ + get workspacesCreatedByCurrentUser() { + if (!this.workspaces) return null; + + const user = this.rootStore.user.currentUser; + + if (!user) return null; + + return this.workspaces.filter((w) => w.created_by === user?.id); + } + + /** + * get workspace info from the array of workspaces in the store using workspace slug + * @param workspaceSlug + */ + getWorkspaceBySlug = (workspaceSlug: string) => this.workspaces?.find((w) => w.slug == workspaceSlug) || null; + + /** + * get workspace info from the array of workspaces in the store using workspace id + * @param workspaceId + */ + getWorkspaceById = (workspaceId: string) => this.workspaces?.find((w) => w.id == workspaceId) || null; + + /** + * fetch user workspaces from API + */ + fetchWorkspaces = async () => { + try { + this.loader = true; + this.error = null; + + const workspaceResponse = await this.workspaceService.userWorkspaces(); + + runInAction(() => { + this.workspaces = workspaceResponse; + this.loader = false; + this.error = null; + }); + + return workspaceResponse; + } catch (error) { + console.log("Failed to fetch user workspaces in workspace store", error); + + runInAction(() => { + this.loader = false; + this.error = error; + this.workspaces = []; + }); + + throw error; + } + }; + + /** + * create workspace using the workspace data + * @param data + */ + createWorkspace = async (data: Partial) => { + try { + runInAction(() => { + this.loader = true; + this.error = null; + }); + + const response = await this.workspaceService.createWorkspace(data); + + runInAction(() => { + this.loader = false; + this.error = null; + this.workspaces = [...(this.workspaces ?? []), response]; + }); + + return response; + } catch (error) { + runInAction(() => { + this.loader = false; + this.error = error; + }); + + throw error; + } + }; + + /** + * update workspace using the workspace slug and new workspace data + * @param workspaceSlug + * @param data + */ + updateWorkspace = async (workspaceSlug: string, data: Partial) => { + const newWorkspaces = this.workspaces?.map((w) => (w.slug === workspaceSlug ? { ...w, ...data } : w)); + + try { + runInAction(() => { + this.loader = true; + this.error = null; + }); + + const response = await this.workspaceService.updateWorkspace(workspaceSlug, data); + + runInAction(() => { + this.loader = false; + this.error = null; + this.workspaces = newWorkspaces; + }); + + return response; + } catch (error) { + runInAction(() => { + this.loader = false; + this.error = error; + }); + + throw error; + } + }; + + /** + * delete workspace using the workspace slug + * @param workspaceSlug + */ + deleteWorkspace = async (workspaceSlug: string) => { + const newWorkspaces = this.workspaces?.filter((w) => w.slug !== workspaceSlug); + + try { + runInAction(() => { + this.loader = true; + this.error = null; + }); + + await this.workspaceService.deleteWorkspace(workspaceSlug); + + runInAction(() => { + this.loader = false; + this.error = null; + this.workspaces = newWorkspaces; + }); + } catch (error) { + runInAction(() => { + this.loader = false; + this.error = error; + }); + + throw error; + } + }; +} diff --git a/web/store/workspace/webhook.store.ts b/web/store/workspace/webhook.store.ts new file mode 100644 index 000000000..e5fda609a --- /dev/null +++ b/web/store/workspace/webhook.store.ts @@ -0,0 +1,261 @@ +// mobx +import { action, observable, makeObservable, computed, runInAction } from "mobx"; +import { IWebhook } from "types"; +import { WebhookService } from "services/webhook.service"; +import { RootStore } from "../root.store"; + +export interface IWebhookStore { + // states + loader: boolean; + error: any | null; + + // observables + webhooks: Record | null; + webhookSecretKey: string | null; + + // computed + currentWebhook: IWebhook | null; + + // computed actions + getWebhookById: (webhookId: string) => IWebhook | null; + + // actions + fetchWebhooks: (workspaceSlug: string) => Promise; + fetchWebhookById: (workspaceSlug: string, webhookId: string) => Promise; + createWebhook: ( + workspaceSlug: string, + data: Partial + ) => Promise<{ webHook: IWebhook; secretKey: string | null }>; + updateWebhook: (workspaceSlug: string, webhookId: string, data: Partial) => Promise; + removeWebhook: (workspaceSlug: string, webhookId: string) => Promise; + regenerateSecretKey: ( + workspaceSlug: string, + webhookId: string + ) => Promise<{ webHook: IWebhook; secretKey: string | null }>; + clearSecretKey: () => void; +} + +export class WebhookStore implements IWebhookStore { + // states + loader: boolean = false; + error: any | null = null; + + // observables + webhooks: Record | null = null; + webhookSecretKey: string | null = null; + + // services + webhookService; + // root store + rootStore; + + constructor(_rootStore: RootStore) { + makeObservable(this, { + // states + loader: observable.ref, + error: observable.ref, + + // observables + webhooks: observable, + webhookSecretKey: observable.ref, + + // computed + currentWebhook: computed, + + // computed actions + getWebhookById: action, + + // actions + fetchWebhooks: action, + fetchWebhookById: action, + createWebhook: action, + updateWebhook: action, + removeWebhook: action, + regenerateSecretKey: action, + clearSecretKey: action, + }); + + // services + this.webhookService = new WebhookService(); + // root store + this.rootStore = _rootStore; + } + + /** + * computed value of current webhook based on webhook id saved in the query store + */ + get currentWebhook() { + const webhookId = this.rootStore.app.router.query?.webhookId; + + if (!webhookId) return null; + + const currentWebhook = this.webhooks?.[webhookId] ?? null; + return currentWebhook; + } + + /** + * get webhook info from the object of webhooks in the store using webhook id + * @param webhookId + */ + getWebhookById = (webhookId: string) => this.webhooks?.[webhookId] || null; + + /** + * fetch all the webhooks for a workspace + * @param workspaceSlug + */ + fetchWebhooks = async (workspaceSlug: string) => { + try { + this.loader = true; + this.error = null; + + const webhookResponse = await this.webhookService.fetchWebhooksList(workspaceSlug); + + const webHookObject: { [webhookId: string]: IWebhook } = webhookResponse.reduce((accumulator, currentWebhook) => { + if (currentWebhook && currentWebhook.id) { + return { ...accumulator, [currentWebhook.id]: currentWebhook }; + } + return accumulator; + }, {}); + + runInAction(() => { + this.webhooks = webHookObject; + this.loader = false; + this.error = null; + }); + + return webhookResponse; + } catch (error) { + this.loader = false; + this.error = error; + + throw error; + } + }; + + /** + * fetch webhook info from API using webhook id + * @param workspaceSlug + * @param webhookId + */ + fetchWebhookById = async (workspaceSlug: string, webhookId: string) => { + try { + const webhookResponse = await this.webhookService.fetchWebhookDetails(workspaceSlug, webhookId); + + runInAction(() => { + this.webhooks = { + ...this.webhooks, + [webhookResponse.id]: webhookResponse, + }; + }); + + return webhookResponse; + } catch (error) { + throw error; + } + }; + + /** + * create a new webhook for a workspace using the data + * @param workspaceSlug + * @param data + */ + createWebhook = async (workspaceSlug: string, data: Partial) => { + try { + const webhookResponse = await this.webhookService.createWebhook(workspaceSlug, data); + + const _secretKey = webhookResponse?.secret_key ?? null; + delete webhookResponse?.secret_key; + const _webhooks = this.webhooks; + + if (webhookResponse && webhookResponse.id && _webhooks) _webhooks[webhookResponse.id] = webhookResponse; + + runInAction(() => { + this.webhookSecretKey = _secretKey || null; + this.webhooks = _webhooks; + }); + + return { webHook: webhookResponse, secretKey: _secretKey }; + } catch (error) { + throw error; + } + }; + + /** + * update a webhook using the data + * @param workspaceSlug + * @param webhookId + * @param data + */ + updateWebhook = async (workspaceSlug: string, webhookId: string, data: Partial) => { + try { + let _webhooks = this.webhooks; + + if (webhookId && _webhooks && this.webhooks) + _webhooks = { ..._webhooks, [webhookId]: { ...this.webhooks[webhookId], ...data } }; + + runInAction(() => { + this.webhooks = _webhooks; + }); + + const webhookResponse = await this.webhookService.updateWebhook(workspaceSlug, webhookId, data); + + return webhookResponse; + } catch (error) { + this.fetchWebhooks(workspaceSlug); + throw error; + } + }; + + /** + * delete a webhook using webhook id + * @param workspaceSlug + * @param webhookId + */ + removeWebhook = async (workspaceSlug: string, webhookId: string) => { + try { + await this.webhookService.deleteWebhook(workspaceSlug, webhookId); + + const _webhooks = this.webhooks ?? {}; + delete _webhooks[webhookId]; + runInAction(() => { + this.webhooks = _webhooks; + }); + } catch (error) { + throw error; + } + }; + + /** + * regenerate secret key for a webhook using webhook id + * @param workspaceSlug + * @param webhookId + */ + regenerateSecretKey = async (workspaceSlug: string, webhookId: string) => { + try { + const webhookResponse = await this.webhookService.regenerateSecretKey(workspaceSlug, webhookId); + + const _secretKey = webhookResponse?.secret_key ?? null; + delete webhookResponse?.secret_key; + const _webhooks = this.webhooks; + + if (_webhooks && webhookResponse && webhookResponse.id) { + _webhooks[webhookResponse.id] = webhookResponse; + } + + runInAction(() => { + this.webhookSecretKey = _secretKey || null; + this.webhooks = _webhooks; + }); + return { webHook: webhookResponse, secretKey: _secretKey }; + } catch (error) { + throw error; + } + }; + + /** + * clear secret key from the store + */ + clearSecretKey = () => { + this.webhookSecretKey = null; + }; +}