Skip to content

Commit

Permalink
chore: break down helpers.ts and create new files (RocketChat#28611)
Browse files Browse the repository at this point in the history
  • Loading branch information
felipe-rod123 authored Jun 9, 2023
1 parent 5408835 commit b31ccd4
Show file tree
Hide file tree
Showing 20 changed files with 240 additions and 181 deletions.
29 changes: 29 additions & 0 deletions .changeset/helpers.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
---
"@rocket.chat/meteor": patch
---

chore: break down helpers.ts and create new files

🔀 changed `handleAPIError` import in AppDetailsPage.tsx
🔀 changed `apiCurlGetter` import in AppDetailsAPIs.tsx
🔀 changed `formatPriceAndPurchaseType` import in AppStatusPriceDisplay.tsx

❌ deleted `apiCurlGetter, handleInstallError, handleAPIError, warnAppInstall, warnEnableDisableApp, warnStatusChange, formatPriceAndPurchaseType` and moved them to new files, from helpers.ts

✅ created apiCurlGetter.ts file
✅ created appErroredStatuses.ts file
✅ created formatPrice.ts file
✅ created formatPriceAndPurchaseType.ts file
✅ created formatPricingPlan.ts file
✅ created handleAPIError.ts file
✅ created handleInstallError.ts file
✅ created installApp.ts file
✅ created updateApp.ts file
✅ created warnAppInstal.ts file
✅ created warnEnableDisableApp.ts file
✅ created warnStatusChange.ts file

🔀 changed `handleAPIError` import in useAppInstallationHandler.tsx
🔀 changed `handleAPIError` import in useCategories.ts
🔀 changed `handleAPIError` import in useOpenIncompatibleModal.tsx

Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import React, { useState, useCallback, useRef } from 'react';
import type { ISettings } from '../../../../ee/client/apps/@types/IOrchestrator';
import { Apps } from '../../../../ee/client/apps/orchestrator';
import Page from '../../../components/Page';
import { handleAPIError } from '../helpers';
import { handleAPIError } from '../helpers/handleAPIError';
import { useAppInfo } from '../hooks/useAppInfo';
import AppDetailsPageHeader from './AppDetailsPageHeader';
import AppDetailsPageLoading from './AppDetailsPageLoading';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { useAbsoluteUrl, useTranslation } from '@rocket.chat/ui-contexts';
import type { FC } from 'react';
import React, { Fragment } from 'react';

import { apiCurlGetter } from '../../../helpers';
import { apiCurlGetter } from '../../../helpers/apiCurlGetter';

type AppDetailsAPIsProps = {
apis: IApiEndpointMetadata[];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { useTranslation } from '@rocket.chat/ui-contexts';
import type { FC } from 'react';
import React, { useMemo } from 'react';

import { formatPriceAndPurchaseType } from '../../../helpers';
import { formatPriceAndPurchaseType } from '../../../helpers/formatPriceAndPurchaseType';

type AppStatusPriceDisplayProps = {
purchaseType: PurchaseType;
Expand Down
176 changes: 3 additions & 173 deletions apps/meteor/client/views/marketplace/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,33 +1,13 @@
import { AppStatus } from '@rocket.chat/apps-engine/definition/AppStatus';
import type { IApiEndpointMetadata } from '@rocket.chat/apps-engine/definition/api';
import type { App, AppPricingPlan, PurchaseType } from '@rocket.chat/core-typings';
import type { App } from '@rocket.chat/core-typings';
import semver from 'semver';

// import { t } from '../../../app/utils/client';
import { t } from '../../../app/utils/lib/i18n';
import { Utilities } from '../../../ee/lib/misc/Utilities';
import { dispatchToastMessage } from '../../lib/toast';
import { appErroredStatuses } from './helpers/appErroredStatuses';

export const appEnabledStatuses = [AppStatus.AUTO_ENABLED, AppStatus.MANUALLY_ENABLED];

// eslint-disable-next-line @typescript-eslint/naming-convention
interface ApiError {
xhr: {
responseJSON: {
error: string;
status: string;
messages: string[];
payload?: any;
};
};
}

const appErroredStatuses = [
AppStatus.COMPILER_ERROR_DISABLED,
AppStatus.ERROR_DISABLED,
AppStatus.INVALID_SETTINGS_DISABLED,
AppStatus.INVALID_LICENSE_DISABLED,
];

export type Actions = 'update' | 'install' | 'purchase' | 'request';

type appButtonResponseProps = {
Expand All @@ -54,116 +34,8 @@ export type appStatusSpanResponseProps = {
tooltipText?: string;
};

type PlanType = 'Subscription' | 'Paid' | 'Free';

type FormattedPriceAndPlan = {
type: PlanType;
price: string;
};

type appButtonPropsType = App & { isAdminUser: boolean; endUserRequested: boolean };

export const apiCurlGetter =
(absoluteUrl: (path: string) => string) =>
(method: string, api: IApiEndpointMetadata): string[] => {
const example = api.examples?.[method];
return Utilities.curl({
url: absoluteUrl(api.computedPath),
method,
params: example?.params,
query: example?.query,
content: example?.content,
headers: example?.headers,
auth: '',
}).split('\n');
};

export function handleInstallError(apiError: ApiError | Error): void {
if (apiError instanceof Error) {
dispatchToastMessage({ type: 'error', message: apiError.message });
return;
}

if (!apiError.xhr || !apiError.xhr.responseJSON) {
return;
}

const { status, messages, error, payload = null } = apiError.xhr.responseJSON;

let message: string;

switch (status) {
case 'storage_error':
message = messages.join('');
break;
case 'app_user_error':
message = messages.join('');
if (payload?.username) {
message = t('Apps_User_Already_Exists', { username: payload.username });
}
break;
default:
if (error) {
message = error;
} else {
message = t('There_has_been_an_error_installing_the_app');
}
}

dispatchToastMessage({ type: 'error', message });
}

const shouldHandleErrorAsWarning = (message: string): boolean => {
const warnings = ['Could not reach the Marketplace'];

return warnings.includes(message);
};

export const handleAPIError = (error: unknown): void => {
if (error instanceof Error) {
const { message } = error;

if (shouldHandleErrorAsWarning(message)) {
dispatchToastMessage({ type: 'warning', message });
return;
}

dispatchToastMessage({ type: 'error', message });
}
};

export const warnAppInstall = (appName: string, status: AppStatus): void => {
if (appErroredStatuses.includes(status)) {
dispatchToastMessage({ type: 'error', message: (t(`App_status_${status}`), appName) });
return;
}

dispatchToastMessage({ type: 'success', message: `${appName} installed` });
};

export const warnEnableDisableApp = (appName: string, status: AppStatus, type: string): void => {
if (appErroredStatuses.includes(status)) {
dispatchToastMessage({ type: 'error', message: (t(`App_status_${status}`), appName) });
return;
}

if (type === 'enable') {
dispatchToastMessage({ type: 'success', message: `${appName} enabled` });
return;
}

dispatchToastMessage({ type: 'success', message: `${appName} disabled` });
};

export const warnStatusChange = (appName: string, status: AppStatus): void => {
if (appErroredStatuses.includes(status)) {
dispatchToastMessage({ type: 'error', message: (t(`App_status_${status}`), appName) });
return;
}

dispatchToastMessage({ type: 'info', message: (t(`App_status_${status}`), appName) });
};

export const appButtonProps = ({
installed,
version,
Expand Down Expand Up @@ -386,45 +258,3 @@ export const appMultiStatusProps = (

return statuses;
};

const formatPrice = (price: number): string => `\$${price.toFixed(2)}`;

const formatPricingPlan = ({ strategy, price, tiers = [], trialDays }: AppPricingPlan): string => {
const { perUnit = false } = (Array.isArray(tiers) && tiers.find((tier) => tier.price === price)) || {};

const pricingPlanTranslationString = [
'Apps_Marketplace_pricingPlan',
Array.isArray(tiers) && tiers.length > 0 && '+*',
strategy,
trialDays && 'trialDays',
perUnit && 'perUser',
]
.filter(Boolean)
.join('_');

return t(pricingPlanTranslationString, {
price: formatPrice(price),
trialDays,
});
};

export const formatPriceAndPurchaseType = (
purchaseType: PurchaseType,
pricingPlans: AppPricingPlan[],
price: number,
): FormattedPriceAndPlan => {
if (purchaseType === 'subscription') {
const type = 'Subscription';
if (!pricingPlans || !Array.isArray(pricingPlans) || pricingPlans.length === 0) {
return { type, price: '-' };
}

return { type, price: formatPricingPlan(pricingPlans[0]) };
}

if (price > 0) {
return { type: 'Paid', price: formatPrice(price) };
}

return { type: 'Free', price: '-' };
};
18 changes: 18 additions & 0 deletions apps/meteor/client/views/marketplace/helpers/apiCurlGetter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import type { IApiEndpointMetadata } from '@rocket.chat/apps-engine/definition/api';

import { Utilities } from '../../../../ee/lib/misc/Utilities';

export const apiCurlGetter =
(absoluteUrl: (path: string) => string) =>
(method: string, api: IApiEndpointMetadata): string[] => {
const example = api.examples?.[method];
return Utilities.curl({
url: absoluteUrl(api.computedPath),
method,
params: example?.params,
query: example?.query,
content: example?.content,
headers: example?.headers,
auth: '',
}).split('\n');
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { AppStatus } from '@rocket.chat/apps-engine/definition/AppStatus';

export const appErroredStatuses = [
AppStatus.COMPILER_ERROR_DISABLED,
AppStatus.ERROR_DISABLED,
AppStatus.INVALID_SETTINGS_DISABLED,
AppStatus.INVALID_LICENSE_DISABLED,
];
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const formatPrice = (price: number): string => `\$${price.toFixed(2)}`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import type { PurchaseType, AppPricingPlan } from '@rocket.chat/core-typings';

import { formatPrice } from './formatPrice';
import { formatPricingPlan } from './formatPricingPlan';

type PlanType = 'Subscription' | 'Paid' | 'Free';

type FormattedPriceAndPlan = {
type: PlanType;
price: string;
};

export const formatPriceAndPurchaseType = (
purchaseType: PurchaseType,
pricingPlans: AppPricingPlan[],
price: number,
): FormattedPriceAndPlan => {
if (purchaseType === 'subscription') {
const type = 'Subscription';
if (!pricingPlans || !Array.isArray(pricingPlans) || pricingPlans.length === 0) {
return { type, price: '-' };
}

return { type, price: formatPricingPlan(pricingPlans[0]) };
}

if (price > 0) {
return { type: 'Paid', price: formatPrice(price) };
}

return { type: 'Free', price: '-' };
};
23 changes: 23 additions & 0 deletions apps/meteor/client/views/marketplace/helpers/formatPricingPlan.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import type { AppPricingPlan } from '@rocket.chat/core-typings';

import { t } from '../../../../app/utils/lib/i18n';
import { formatPrice } from './formatPrice';

export const formatPricingPlan = ({ strategy, price, tiers = [], trialDays }: AppPricingPlan): string => {
const { perUnit = false } = (Array.isArray(tiers) && tiers.find((tier) => tier.price === price)) || {};

const pricingPlanTranslationString = [
'Apps_Marketplace_pricingPlan',
Array.isArray(tiers) && tiers.length > 0 && '+*',
strategy,
trialDays && 'trialDays',
perUnit && 'perUser',
]
.filter(Boolean)
.join('_');

return t(pricingPlanTranslationString, {
price: formatPrice(price),
trialDays,
});
};
20 changes: 20 additions & 0 deletions apps/meteor/client/views/marketplace/helpers/handleAPIError.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { dispatchToastMessage } from '../../../lib/toast';

const shouldHandleErrorAsWarning = (message: string): boolean => {
const warnings = ['Could not reach the Marketplace'];

return warnings.includes(message);
};

export const handleAPIError = (error: unknown): void => {
if (error instanceof Error) {
const { message } = error;

if (shouldHandleErrorAsWarning(message)) {
dispatchToastMessage({ type: 'warning', message });
return;
}

dispatchToastMessage({ type: 'error', message });
}
};
Loading

0 comments on commit b31ccd4

Please sign in to comment.