* Add license load metric to About screen - Add REST endpoint to fetch license load percentage - Display load metric in About screen next to server version Fixes: https://mattermost.atlassian.net/browse/MM-63728 * MM-63728: Address PR feedback from enahum - Move license load metric fetch to a remote action - Use isMinimumServerVersion to check for server 10.8.0 or higher 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * MM-63728: Simplify getLicenseLoadMetric to directly return number 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * MM-63728: Move getLicenseLoadMetric to dedicated license.ts file - Create new remote action file specifically for license-related functions - Add test file for the license actions - Update imports in about.tsx 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * MM-63728: Remove redundant license check in about.tsx - Rely on getLicenseLoadMetric to handle the license check 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * MM-63728: Update E2E tests for license load metric - Add license load metric test IDs to about screen - Update E2E test to check for load metric when license is enabled - Handle cases where server might not support the feature 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * recover app/actions/remote/general.ts * MM-63728: Return error from getLicenseLoadMetric instead of silent failure - Remove silent failure and debug logging - Return the error object when API call fails - Update the About component to handle possible error responses - Update tests to verify error handling 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * MM-63728: Remove groupLabel parameter from getLicenseLoadMetric - Remove groupLabel parameter from client getLicenseLoadMetric method in interface and implementation - Update client tests to reflect the parameter removal - Update license action test to verify no parameter is passed 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * simplify about screen checks * MM-63728: Use jest.mocked and real version checks in license tests - Removed isMinimumServerVersion mock, letting tests use real version checking - Used proper type casting for mock Client - Added comprehensive version compatibility test cases - Simplified test setup 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * MM-63728: Remove redundant server version test - Removed redundant test for different server versions - Existing tests already cover the necessary version compatibility cases 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * MM-63728: Refactor license test to use better mocking pattern - Refactored the test file to use a better mocking pattern similar to custom_emoji.test.ts - Simplified mock declarations using jest.mock() - Added type import for Client for better readability - Improved type casting for mock objects 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * npm run fix --------- Co-authored-by: Claude <noreply@anthropic.com>
132 lines
4.8 KiB
TypeScript
132 lines
4.8 KiB
TypeScript
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
// See LICENSE.txt for license information.
|
|
|
|
import {buildQueryString} from '@utils/helpers';
|
|
|
|
import {PER_PAGE_DEFAULT} from './constants';
|
|
import ClientError from './error';
|
|
|
|
import type ClientBase from './base';
|
|
|
|
type PoliciesResponse<T> = {
|
|
policies: T[];
|
|
total_count: number;
|
|
}
|
|
|
|
export interface ClientGeneralMix {
|
|
ping: (deviceId?: string, timeoutInterval?: number, groupLabel?: RequestGroupLabel) => Promise<any>;
|
|
logClientError: (message: string, level?: string) => Promise<any>;
|
|
getClientConfigOld: (groupLabel?: RequestGroupLabel) => Promise<ClientConfig>;
|
|
getClientLicenseOld: (groupLabel?: RequestGroupLabel) => Promise<ClientLicense>;
|
|
getLicenseLoadMetric: () => Promise<{load: number}>;
|
|
getTimezones: () => Promise<string[]>;
|
|
getGlobalDataRetentionPolicy: (groupLabel?: RequestGroupLabel) => Promise<GlobalDataRetentionPolicy>;
|
|
getTeamDataRetentionPolicies: (userId: string, page?: number, perPage?: number, groupLabel?: RequestGroupLabel) => Promise<PoliciesResponse<TeamDataRetentionPolicy>>;
|
|
getChannelDataRetentionPolicies: (userId: string, page?: number, perPage?: number, groupLabel?: RequestGroupLabel) => Promise<PoliciesResponse<ChannelDataRetentionPolicy>>;
|
|
getRolesByNames: (rolesNames: string[], groupLabel?: RequestGroupLabel) => Promise<Role[]>;
|
|
getRedirectLocation: (urlParam: string) => Promise<Record<string, string>>;
|
|
sendPerformanceReport: (batch: PerformanceReport) => Promise<{}>;
|
|
}
|
|
|
|
const ClientGeneral = <TBase extends Constructor<ClientBase>>(superclass: TBase) => class extends superclass {
|
|
ping = async (deviceId?: string, timeoutInterval?: number, groupLabel?: RequestGroupLabel) => {
|
|
let url = `${this.urlVersion}/system/ping?time=${Date.now()}`;
|
|
if (deviceId) {
|
|
url = `${url}&device_id=${deviceId}`;
|
|
}
|
|
return this.doFetch(
|
|
url,
|
|
{method: 'get', timeoutInterval, groupLabel},
|
|
false,
|
|
);
|
|
};
|
|
|
|
logClientError = async (message: string, level = 'ERROR') => {
|
|
const url = `${this.urlVersion}/logs`;
|
|
|
|
if (!this.enableLogging) {
|
|
throw new ClientError(this.apiClient.baseUrl, {
|
|
message: 'Logging disabled.',
|
|
url,
|
|
});
|
|
}
|
|
|
|
return this.doFetch(
|
|
url,
|
|
{method: 'post', body: {message, level}},
|
|
);
|
|
};
|
|
|
|
getClientConfigOld = async (groupLabel?: RequestGroupLabel) => {
|
|
return this.doFetch(
|
|
`${this.urlVersion}/config/client?format=old`,
|
|
{method: 'get', groupLabel},
|
|
);
|
|
};
|
|
|
|
getClientLicenseOld = async (groupLabel?: RequestGroupLabel) => {
|
|
return this.doFetch(
|
|
`${this.urlVersion}/license/client?format=old`,
|
|
{method: 'get', groupLabel},
|
|
);
|
|
};
|
|
|
|
getLicenseLoadMetric = async () => {
|
|
return this.doFetch(
|
|
`${this.urlVersion}/license/load_metric`,
|
|
{method: 'get'},
|
|
);
|
|
};
|
|
|
|
getTimezones = async () => {
|
|
return this.doFetch(
|
|
`${this.getTimezonesRoute()}`,
|
|
{method: 'get'},
|
|
);
|
|
};
|
|
|
|
getGlobalDataRetentionPolicy = (groupLabel?: RequestGroupLabel) => {
|
|
return this.doFetch(
|
|
`${this.getGlobalDataRetentionRoute()}/policy`,
|
|
{method: 'get', groupLabel},
|
|
);
|
|
};
|
|
|
|
getTeamDataRetentionPolicies = (userId: string, page = 0, perPage = PER_PAGE_DEFAULT, groupLabel?: RequestGroupLabel) => {
|
|
return this.doFetch(
|
|
`${this.getGranularDataRetentionRoute(userId)}/team_policies${buildQueryString({page, per_page: perPage})}`,
|
|
{method: 'get', groupLabel},
|
|
);
|
|
};
|
|
|
|
getChannelDataRetentionPolicies = (userId: string, page = 0, perPage = PER_PAGE_DEFAULT, groupLabel?: RequestGroupLabel) => {
|
|
return this.doFetch(
|
|
`${this.getGranularDataRetentionRoute(userId)}/channel_policies${buildQueryString({page, per_page: perPage})}`,
|
|
{method: 'get', groupLabel},
|
|
);
|
|
};
|
|
|
|
getRolesByNames = async (rolesNames: string[], groupLabel?: RequestGroupLabel) => {
|
|
return this.doFetch(
|
|
`${this.getRolesRoute()}/names`,
|
|
{method: 'post', body: rolesNames, groupLabel},
|
|
);
|
|
};
|
|
|
|
getRedirectLocation = async (urlParam: string) => {
|
|
if (!urlParam.length) {
|
|
return Promise.resolve();
|
|
}
|
|
const url = `${this.getRedirectLocationRoute()}${buildQueryString({url: urlParam})}`;
|
|
return this.doFetch(url, {method: 'get'});
|
|
};
|
|
|
|
sendPerformanceReport = async (report: PerformanceReport) => {
|
|
return this.doFetch(
|
|
this.getPerformanceRoute(),
|
|
{method: 'post', body: report},
|
|
);
|
|
};
|
|
};
|
|
|
|
export default ClientGeneral;
|