mirror of
https://github.com/strapi/strapi.git
synced 2025-09-29 02:11:45 +00:00
Merge branch 'develop' into fix/issue_21600
This commit is contained in:
commit
c7c382f99a
5
packages/cli/cloud/src/cloud/command.ts
Normal file
5
packages/cli/cloud/src/cloud/command.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import { Command } from 'commander';
|
||||
|
||||
export function defineCloudNamespace(command: Command): Command {
|
||||
return command.command('cloud').description('Manage Strapi Cloud projects');
|
||||
}
|
7
packages/cli/cloud/src/environment/command.ts
Normal file
7
packages/cli/cloud/src/environment/command.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import { Command } from 'commander';
|
||||
import { defineCloudNamespace } from '../cloud/command';
|
||||
|
||||
export function createEnvironmentCommand(command: Command): Command {
|
||||
const cloud = defineCloudNamespace(command);
|
||||
return cloud.command('environment').description('Manage environments for a Strapi Cloud project');
|
||||
}
|
66
packages/cli/cloud/src/environment/list/action.ts
Normal file
66
packages/cli/cloud/src/environment/list/action.ts
Normal file
@ -0,0 +1,66 @@
|
||||
import chalk from 'chalk';
|
||||
import type { CLIContext } from '../../types';
|
||||
import { cloudApiFactory, local, tokenServiceFactory } from '../../services';
|
||||
import { promptLogin } from '../../login/action';
|
||||
import { trackEvent } from '../../utils/analytics';
|
||||
|
||||
async function getProject(ctx: CLIContext) {
|
||||
const { project } = await local.retrieve();
|
||||
if (!project) {
|
||||
ctx.logger.warn(
|
||||
`\nWe couldn't find a valid local project config.\nPlease link your local project to an existing Strapi Cloud project using the ${chalk.cyan(
|
||||
'link'
|
||||
)} command`
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
return project;
|
||||
}
|
||||
|
||||
export default async (ctx: CLIContext) => {
|
||||
const { getValidToken } = await tokenServiceFactory(ctx);
|
||||
const token = await getValidToken(ctx, promptLogin);
|
||||
const { logger } = ctx;
|
||||
|
||||
if (!token) {
|
||||
return;
|
||||
}
|
||||
|
||||
const project = await getProject(ctx);
|
||||
if (!project) {
|
||||
ctx.logger.debug(`No valid local project configuration was found.`);
|
||||
return;
|
||||
}
|
||||
|
||||
const cloudApiService = await cloudApiFactory(ctx, token);
|
||||
const spinner = logger.spinner('Fetching environments...').start();
|
||||
await trackEvent(ctx, cloudApiService, 'willListEnvironment', {
|
||||
projectInternalName: project.name,
|
||||
});
|
||||
|
||||
try {
|
||||
const {
|
||||
data: { data: environmentsList },
|
||||
} = await cloudApiService.listEnvironments({ name: project.name });
|
||||
spinner.succeed();
|
||||
logger.log(environmentsList);
|
||||
await trackEvent(ctx, cloudApiService, 'didListEnvironment', {
|
||||
projectInternalName: project.name,
|
||||
});
|
||||
} catch (e: any) {
|
||||
if (e.response && e.response.status === 404) {
|
||||
spinner.succeed();
|
||||
logger.warn(
|
||||
`\nThe project associated with this folder does not exist in Strapi Cloud. \nPlease link your local project to an existing Strapi Cloud project using the ${chalk.cyan(
|
||||
'link'
|
||||
)} command`
|
||||
);
|
||||
} else {
|
||||
spinner.fail('An error occurred while fetching environments data from Strapi Cloud.');
|
||||
logger.debug('Failed to list environments', e);
|
||||
}
|
||||
await trackEvent(ctx, cloudApiService, 'didNotListEnvironment', {
|
||||
projectInternalName: project.name,
|
||||
});
|
||||
}
|
||||
};
|
25
packages/cli/cloud/src/environment/list/command.ts
Normal file
25
packages/cli/cloud/src/environment/list/command.ts
Normal file
@ -0,0 +1,25 @@
|
||||
import { type StrapiCloudCommand } from '../../types';
|
||||
import { runAction } from '../../utils/helpers';
|
||||
import action from './action';
|
||||
import { defineCloudNamespace } from '../../cloud/command';
|
||||
|
||||
const command: StrapiCloudCommand = ({ command, ctx }) => {
|
||||
const cloud = defineCloudNamespace(command);
|
||||
|
||||
cloud
|
||||
.command('environments')
|
||||
.description('Alias for cloud environment list')
|
||||
.action(() => runAction('list', action)(ctx));
|
||||
|
||||
const environment = cloud
|
||||
.command('environment')
|
||||
.description('Manage environments for a Strapi Cloud project');
|
||||
environment
|
||||
.command('list')
|
||||
.description('List Strapi Cloud project environments')
|
||||
.option('-d, --debug', 'Enable debugging mode with verbose logs')
|
||||
.option('-s, --silent', "Don't log anything")
|
||||
.action(() => runAction('list', action)(ctx));
|
||||
};
|
||||
|
||||
export default command;
|
12
packages/cli/cloud/src/environment/list/index.ts
Normal file
12
packages/cli/cloud/src/environment/list/index.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import action from './action';
|
||||
import command from './command';
|
||||
import type { StrapiCloudCommandInfo } from '../../types';
|
||||
|
||||
export { action, command };
|
||||
|
||||
export default {
|
||||
name: 'list-environments',
|
||||
description: 'List Strapi Cloud environments',
|
||||
action,
|
||||
command,
|
||||
} as StrapiCloudCommandInfo;
|
@ -6,6 +6,7 @@ import login from './login';
|
||||
import logout from './logout';
|
||||
import createProject from './create-project';
|
||||
import listProjects from './list-projects';
|
||||
import listEnvironments from './environment/list';
|
||||
import { CLIContext } from './types';
|
||||
import { getLocalConfig, saveLocalConfig } from './config/local';
|
||||
|
||||
@ -16,9 +17,10 @@ export const cli = {
|
||||
logout,
|
||||
createProject,
|
||||
listProjects,
|
||||
listEnvironments,
|
||||
};
|
||||
|
||||
const cloudCommands = [deployProject, link, login, logout, listProjects];
|
||||
const cloudCommands = [deployProject, link, login, logout, listProjects, listEnvironments];
|
||||
|
||||
async function initCloudCLIConfig() {
|
||||
const localConfig = await getLocalConfig();
|
||||
|
@ -19,6 +19,8 @@ export type ProjectInfos = {
|
||||
url?: string;
|
||||
};
|
||||
|
||||
export type EnvironmentInfo = Record<string, unknown>;
|
||||
|
||||
export type ProjectInput = Omit<ProjectInfos, 'id'>;
|
||||
|
||||
export type DeployResponse = {
|
||||
@ -32,6 +34,12 @@ export type ListProjectsResponse = {
|
||||
};
|
||||
};
|
||||
|
||||
export type ListEnvironmentsResponse = {
|
||||
data: {
|
||||
data: EnvironmentInfo[] | Record<string, never>;
|
||||
};
|
||||
};
|
||||
|
||||
export type ListLinkProjectsResponse = {
|
||||
data: {
|
||||
data: ProjectInfos[] | Record<string, never>;
|
||||
@ -78,6 +86,8 @@ export interface CloudApiService {
|
||||
|
||||
listLinkProjects(): Promise<AxiosResponse<ListLinkProjectsResponse>>;
|
||||
|
||||
listEnvironments(project: { name: string }): Promise<AxiosResponse<ListEnvironmentsResponse>>;
|
||||
|
||||
getProject(project: { name: string }): Promise<AxiosResponse<GetProjectResponse>>;
|
||||
|
||||
track(event: string, payload?: TrackPayload): Promise<AxiosResponse<void>>;
|
||||
@ -197,6 +207,23 @@ export async function cloudApiFactory(
|
||||
}
|
||||
},
|
||||
|
||||
async listEnvironments({ name }): Promise<AxiosResponse<ListEnvironmentsResponse>> {
|
||||
try {
|
||||
const response = await axiosCloudAPI.get(`/projects/${name}/environments`);
|
||||
|
||||
if (response.status !== 200) {
|
||||
throw new Error('Error fetching cloud environments from the server.');
|
||||
}
|
||||
|
||||
return response;
|
||||
} catch (error) {
|
||||
logger.debug(
|
||||
"🥲 Oops! Couldn't retrieve your project's environments from the server. Please try again."
|
||||
);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
async getProject({ name }): Promise<AxiosResponse<GetProjectResponse>> {
|
||||
try {
|
||||
const response = await axiosCloudAPI.get(`/projects/${name}`);
|
||||
|
@ -39,6 +39,10 @@ export type StrapiCloudCommand = (params: {
|
||||
ctx: CLIContext;
|
||||
}) => void | Command | Promise<Command | void>;
|
||||
|
||||
export type StrapiCloudNamespaceCommand = (params: {
|
||||
command: Command;
|
||||
}) => void | Command | Promise<Command | void>;
|
||||
|
||||
export type StrapiCloudCommandInfo = {
|
||||
name: string;
|
||||
description: string;
|
||||
|
@ -66,7 +66,10 @@ const NpmPackageCard = ({
|
||||
}`;
|
||||
|
||||
const versionRange = semver.validRange(attributes.strapiVersion);
|
||||
const isCompatible = semver.satisfies(strapiAppVersion ?? '', versionRange ?? '');
|
||||
|
||||
const isCompatible = versionRange
|
||||
? semver.satisfies(strapiAppVersion ?? '', versionRange)
|
||||
: false;
|
||||
|
||||
return (
|
||||
<Flex
|
||||
@ -272,28 +275,17 @@ const CardButton = ({
|
||||
return (
|
||||
<Tooltip
|
||||
data-testid={`tooltip-${pluginName}`}
|
||||
label={
|
||||
!versionRange
|
||||
? formatMessage(
|
||||
{
|
||||
id: 'admin.pages.MarketPlacePage.plugin.version.null',
|
||||
defaultMessage:
|
||||
'Unable to verify compatibility with your Strapi version: "{strapiAppVersion}"',
|
||||
},
|
||||
{ strapiAppVersion }
|
||||
)
|
||||
: formatMessage(
|
||||
{
|
||||
id: 'admin.pages.MarketPlacePage.plugin.version',
|
||||
defaultMessage:
|
||||
'Update your Strapi version: "{strapiAppVersion}" to: "{versionRange}"',
|
||||
},
|
||||
{
|
||||
strapiAppVersion,
|
||||
versionRange,
|
||||
}
|
||||
)
|
||||
}
|
||||
label={formatMessage(
|
||||
{
|
||||
id: 'admin.pages.MarketPlacePage.plugin.version',
|
||||
defaultMessage:
|
||||
'Update your Strapi version: "{strapiAppVersion}" to: "{versionRange}"',
|
||||
},
|
||||
{
|
||||
strapiAppVersion,
|
||||
versionRange,
|
||||
}
|
||||
)}
|
||||
>
|
||||
<span>
|
||||
<Button
|
||||
|
@ -62,25 +62,17 @@ describe('Marketplace page - layout', () => {
|
||||
expect(button).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('shows compatibility tooltip message when no version provided', async () => {
|
||||
const { findByTestId, findAllByTestId, user } = render();
|
||||
it('Does not show (copy install command) button', async () => {
|
||||
const { findAllByTestId } = render();
|
||||
|
||||
const alreadyInstalledCard = (await findAllByTestId('npm-package-card')).find((div) =>
|
||||
div.innerHTML.includes('Config Sync')
|
||||
)!;
|
||||
|
||||
const button = within(alreadyInstalledCard)
|
||||
.getByText(/copy install command/i)
|
||||
.closest('button')!;
|
||||
const button = within(alreadyInstalledCard).queryByText(/copy install command/i);
|
||||
|
||||
await user.hover(button);
|
||||
const tooltip = await findByTestId(`tooltip-Config Sync`);
|
||||
|
||||
expect(button).toBeEnabled();
|
||||
expect(tooltip).toBeInTheDocument();
|
||||
expect(tooltip).toHaveTextContent(
|
||||
'Unable to verify compatibility with your Strapi version: "4.1.0"'
|
||||
);
|
||||
// Assert that the button does not show
|
||||
expect(button).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('handles production environment', async () => {
|
||||
|
Loading…
x
Reference in New Issue
Block a user