Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[NEW] Create releases tab in the marketplace app info page #25965

Merged
merged 17 commits into from
Jun 29, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions apps/meteor/app/apps/server/communication/rest.js
Original file line number Diff line number Diff line change
Expand Up @@ -528,6 +528,38 @@ export class AppsRestApi {
},
);

this.api.addRoute(
':id/versions',
{ authRequired: true, permissionsRequired: ['manage-apps'] },
{
get() {
const baseUrl = orchestrator.getMarketplaceUrl();

const headers = {}; // DO NOT ATTACH THE FRAMEWORK/ENGINE VERSION HERE.
const token = getWorkspaceAccessToken();
if (token) {
headers.Authorization = `Bearer ${token}`;
}

let result;
try {
result = HTTP.get(`${baseUrl}/v1/apps/${this.urlParams.id}`, {
headers,
});
} catch (e) {
return handleError('Unable to access Marketplace. Does the server has access to the internet?', e);
}

if (!result || result.statusCode !== 200) {
orchestrator.getRocketChatLogger().error('Error getting the App versions from the Marketplace:', result.data);
return API.v1.failure();
}

return API.v1.success({ apps: result.data });
},
},
);

this.api.addRoute(
':id/sync',
{ authRequired: true, permissionsRequired: ['manage-apps'] },
Expand Down
3 changes: 1 addition & 2 deletions apps/meteor/client/views/admin/apps/APIsDisplay.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { IApiEndpointMetadata } from '@rocket.chat/apps-engine/definition/api';
import { Box, Divider } from '@rocket.chat/fuselage';
import { Box } from '@rocket.chat/fuselage';
import { useAbsoluteUrl, useTranslation } from '@rocket.chat/ui-contexts';
import React, { FC, Fragment } from 'react';

Expand All @@ -16,7 +16,6 @@ const APIsDisplay: FC<APIsDisplayProps> = ({ apis }) => {

return (
<>
<Divider />
<Box display='flex' flexDirection='column'>
<Box fontScale='h4' mb='x12'>
{t('APIs')}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Box, Skeleton, Margins } from '@rocket.chat/fuselage';
import React, { FC } from 'react';

const LogsLoading: FC = () => (
const AccordionLoading: FC = () => (
<Box w='full' alignSelf='center'>
<Margins block='x2'>
<Skeleton variant='rect' width='100%' height='x80' />
Expand All @@ -11,4 +11,4 @@ const LogsLoading: FC = () => (
</Box>
);

export default LogsLoading;
export default AccordionLoading;
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,22 @@ import { ExternalLink } from '@rocket.chat/ui-client';
import { TranslationKey, useTranslation } from '@rocket.chat/ui-contexts';
import React, { FC } from 'react';

import APIsDisplay from './APIsDisplay';
import ScreenshotCarouselAnchor from './components/ScreenshotCarouselAnchor';
import { AppInfo } from './definitions/AppInfo';

type AppDetailsPageContentProps = {
type AppDetailsProps = {
app: AppInfo;
};

const AppDetailsPageContent: FC<AppDetailsPageContentProps> = ({ app }) => {
const AppDetails: FC<AppDetailsProps> = ({ app }) => {
const {
author: { homepage, support },
detailedDescription,
description,
categories = [],
screenshots,
apis,
} = app;

const t = useTranslation();
Expand Down Expand Up @@ -90,10 +92,12 @@ const AppDetailsPageContent: FC<AppDetailsPageContentProps> = ({ app }) => {
</Box>
</Box>
</Box>

<Box is='section'>{apis?.length && <APIsDisplay apis={apis || []} />}</Box>
</Margins>
</Box>
</Box>
);
};

export default AppDetailsPageContent;
export default AppDetails;
4 changes: 2 additions & 2 deletions apps/meteor/client/views/admin/apps/AppDetailsHeader.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Box } from '@rocket.chat/fuselage';
import { useTranslation } from '@rocket.chat/ui-contexts';
import { formatDistanceStrict } from 'date-fns';
import moment from 'moment';
import React, { ReactElement } from 'react';

import AppAvatar from '../../../components/avatar/AppAvatar';
Expand All @@ -12,7 +12,7 @@ import { App } from './types';
const AppDetailsHeader = ({ app }: { app: App }): ReactElement => {
const t = useTranslation();
const { iconFileData, name, author, version, iconFileContent, installed, isSubscribed, modifiedAt, bundledIn, description } = app;
const lastUpdated = modifiedAt && formatDistanceStrict(new Date(modifiedAt), new Date(), { addSuffix: false });
const lastUpdated = modifiedAt && moment(modifiedAt).fromNow();

return (
<Box display='flex' flexDirection='row' mbe='x20' w='full'>
Expand Down
45 changes: 30 additions & 15 deletions apps/meteor/client/views/admin/apps/AppDetailsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ import React, { useState, useCallback, useRef, FC } from 'react';
import { ISettings } from '../../../../app/apps/client/@types/IOrchestrator';
import { Apps } from '../../../../app/apps/client/orchestrator';
import Page from '../../../components/Page';
import APIsDisplay from './APIsDisplay';
import AppDetails from './AppDetails';
import AppDetailsHeader from './AppDetailsHeader';
import AppDetailsPageContent from './AppDetailsPageContent';
import AppLogsPage from './AppLogsPage';
import AppSecurityPage from './AppSecurityPage';
import AppLogs from './AppLogs';
import AppReleases from './AppReleases';
import AppSecurity from './AppSecurity';
import LoadingDetails from './LoadingDetails';
import SettingsDisplay from './SettingsDisplay';
import { handleAPIError } from './helpers';
Expand All @@ -26,8 +26,9 @@ const AppDetailsPage: FC<{ id: string }> = function AppDetailsPage({ id }) {
const settingsRef = useRef<Record<string, ISetting['value']>>({});
const appData = useAppInfo(id);

const [, urlParams] = useCurrentRoute();
const [routeName, urlParams] = useCurrentRoute();
const appsRoute = useRoute('admin-apps');
const marketplaceRoute = useRoute('admin-marketplace');
const tab = useRouteParameter('tab');

const [currentRouteName] = useCurrentRoute();
Expand All @@ -38,8 +39,7 @@ const AppDetailsPage: FC<{ id: string }> = function AppDetailsPage({ id }) {
const router = useRoute(currentRouteName);
const handleReturn = useMutableCallback((): void => router.push({}));

const { installed, settings, apis, privacyPolicySummary, permissions, tosLink, privacyLink } = appData || {};
const showApis = apis?.length;
const { installed, settings, privacyPolicySummary, permissions, tosLink, privacyLink } = appData || {};

const saveAppSettings = useCallback(async () => {
const { current } = settingsRef;
Expand All @@ -58,8 +58,14 @@ const AppDetailsPage: FC<{ id: string }> = function AppDetailsPage({ id }) {
setIsSaving(false);
}, [id, settings]);

const handleTabClick = (tab: 'details' | 'security' | 'logs' | 'settings'): void => {
appsRoute.replace({ ...urlParams, tab });
const handleTabClick = (tab: 'details' | 'security' | 'releases' | 'settings' | 'logs'): void => {
if (routeName === 'admin-marketplace') {
marketplaceRoute.replace({ ...urlParams, tab });
}

if (routeName === 'admin-apps') {
appsRoute.replace({ ...urlParams, tab });
}
};

return (
Expand Down Expand Up @@ -89,35 +95,44 @@ const AppDetailsPage: FC<{ id: string }> = function AppDetailsPage({ id }) {
</Tabs.Item>
)}
{Boolean(installed) && (
<Tabs.Item onClick={(): void => handleTabClick('logs')} selected={tab === 'logs'}>
{t('Logs')}
<Tabs.Item onClick={(): void => handleTabClick('releases')} selected={tab === 'releases'}>
{t('Releases')}
</Tabs.Item>
)}
{Boolean(installed && settings && Object.values(settings).length) && (
<Tabs.Item onClick={(): void => handleTabClick('settings')} selected={tab === 'settings'}>
{t('Settings')}
</Tabs.Item>
)}
{Boolean(installed) && (
<Tabs.Item onClick={(): void => handleTabClick('logs')} selected={tab === 'logs'}>
{t('Logs')}
</Tabs.Item>
)}
</Tabs>

{Boolean(!tab || tab === 'details') && <AppDetailsPageContent app={appData} />}
{Boolean((!tab || tab === 'details') && !!showApis) && <APIsDisplay apis={apis || []} />}
{Boolean(!tab || tab === 'details') && <AppDetails app={appData} />}

{tab === 'security' && (
<AppSecurityPage
<AppSecurity
privacyPolicySummary={privacyPolicySummary}
appPermissions={permissions}
tosLink={tosLink}
privacyLink={privacyLink}
/>
)}
{tab === 'logs' && <AppLogsPage id={id} />}

{tab === 'releases' && <AppReleases id={id} />}

{Boolean(tab === 'settings' && settings && Object.values(settings).length) && (
<SettingsDisplay
settings={settings || ({} as ISettings)}
setHasUnsavedChanges={setHasUnsavedChanges}
settingsRef={settingsRef}
/>
)}

{tab === 'logs' && <AppLogs id={id} />}
</>
)}
</Box>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,9 @@ import { useSafely } from '@rocket.chat/fuselage-hooks';
import { useEndpoint } from '@rocket.chat/ui-contexts';
import React, { useCallback, useState, useEffect } from 'react';

import Page from '../../../components/Page';
import { useFormatDateAndTime } from '../../../hooks/useFormatDateAndTime';
import AccordionLoading from './AccordionLoading';
import LogItem from './LogItem';
import LogsLoading from './LogsLoading';

const useAppWithLogs = ({ id }) => {
const [data, setData] = useSafely(useState({}));
Expand All @@ -32,7 +31,7 @@ const useAppWithLogs = ({ id }) => {
return [filteredData, total, fetchData];
};

function AppLogsPage({ id, ...props }) {
function AppLogs({ id }) {
const formatDateAndTime = useFormatDateAndTime();

const [app] = useAppWithLogs({ id });
Expand All @@ -41,32 +40,28 @@ function AppLogsPage({ id, ...props }) {
const showData = !loading && !app.error;

return (
<Page flexDirection='column' {...props}>
<Page.ScrollableContent>
{loading && <LogsLoading />}
{app.error && (
<Box maxWidth='x600' alignSelf='center' fontScale='hh21'>
{app.error.message}
</Box>
)}
{showData && (
<>
<Accordion width='100%' alignSelf='center'>
{app.logs &&
app.logs.map((log) => (
<LogItem
key={log._createdAt}
title={`${formatDateAndTime(log._createdAt)}: "${log.method}" (${log.totalTime}ms)`}
instanceId={log.instanceId}
entries={log.entries}
/>
))}
</Accordion>
</>
)}
</Page.ScrollableContent>
</Page>
<>
{loading && <AccordionLoading />}
{app.error && (
<Box maxWidth='x600' alignSelf='center' fontScale='hh21'>
{app.error.message}
</Box>
)}
{showData && (
<Accordion width='100%' alignSelf='center'>
{app.logs &&
app.logs.map((log) => (
<LogItem
key={log._createdAt}
title={`${formatDateAndTime(log._createdAt)}: "${log.method}" (${log.totalTime}ms)`}
instanceId={log.instanceId}
entries={log.entries}
/>
))}
</Accordion>
)}
</>
);
}

export default AppLogsPage;
export default AppLogs;
52 changes: 52 additions & 0 deletions apps/meteor/client/views/admin/apps/AppReleases.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { Accordion } from '@rocket.chat/fuselage';
import React, { useEffect, useState } from 'react';

import { useEndpointData } from '../../../hooks/useEndpointData';
import { AsyncStatePhase } from '../../../lib/asyncState/AsyncStatePhase';
import AccordionLoading from './AccordionLoading';
import ReleaseItem from './ReleaseItem';

type release = {
version: string;
createdDate: string;
detailedChangelog: {
raw: string;
rendered: string;
};
};

const AppReleases = ({ id }: { id: string }): JSX.Element => {
const { value, phase, error } = useEndpointData(`/apps/${id}/versions`);

const [releases, setReleases] = useState([] as release[]);

const isLoading = phase === AsyncStatePhase.LOADING;
const isSuccess = phase === AsyncStatePhase.RESOLVED;
const didFail = phase === AsyncStatePhase.REJECTED || error;

useEffect(() => {
if (isSuccess && value?.apps) {
const { apps } = value;

setReleases(
apps.map((app) => ({
version: app.version,
createdDate: app.createdDate,
detailedChangelog: app.detailedChangelog,
})),
);
}
}, [isSuccess, value]);

return (
<>
<Accordion width='100%' alignSelf='center'>
{didFail && error}
{isLoading && <AccordionLoading />}
{isSuccess && releases.length && releases.map((release) => <ReleaseItem release={release} key={release.version} />)}
</Accordion>
</>
);
};

export default AppReleases;
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@ import { Box, Margins } from '@rocket.chat/fuselage';
import { TranslationKey, useTranslation } from '@rocket.chat/ui-contexts';
import React, { FC } from 'react';

type AppSecurityPageProps = {
type AppSecurityProps = {
privacyPolicySummary: string | undefined;
appPermissions: AppPermission[] | undefined;
tosLink: string | undefined;
privacyLink: string | undefined;
};

const AppSecurityPage: FC<AppSecurityPageProps> = ({ privacyPolicySummary, appPermissions, tosLink, privacyLink }) => {
const AppSecurity: FC<AppSecurityProps> = ({ privacyPolicySummary, appPermissions, tosLink, privacyLink }) => {
const t = useTranslation();

const defaultPermissions = [
Expand Down Expand Up @@ -89,4 +89,4 @@ const AppSecurityPage: FC<AppSecurityPageProps> = ({ privacyPolicySummary, appPe
);
};

export default AppSecurityPage;
export default AppSecurity;
Loading