@shoper/cli 0.1.0-24 → 0.1.0-26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/cli/core/cli_setup.js +3 -1
- package/build/cli/ui/error_item.js +7 -0
- package/build/cli/ui/errors_list.js +15 -0
- package/build/theme/commands/delete/theme_delete_command.js +99 -0
- package/build/theme/commands/init/theme_init_command.js +5 -3
- package/build/theme/commands/push/theme_push_command.js +9 -5
- package/build/theme/commands/theme_commands_constants.js +2 -1
- package/build/theme/features/theme/actions/theme_actions_constants.js +1 -2
- package/build/theme/features/theme/delete/api/theme_delete_api.js +13 -0
- package/build/theme/features/theme/delete/http/theme_delete_http_api.js +17 -0
- package/build/theme/features/theme/delete/service/theme_delete_service.js +34 -0
- package/build/theme/features/theme/delete/theme_delete_constants.js +2 -0
- package/build/theme/features/theme/delete/theme_delete_initalizer.js +20 -0
- package/build/theme/features/theme/merge/service/theme_merge_service.js +2 -2
- package/build/theme/features/theme/push/service/theme_push_service.js +18 -5
- package/build/theme/features/theme/push/theme_push_constants.js +1 -0
- package/build/theme/features/theme/push/theme_push_errors_factory.js +5 -3
- package/build/theme/features/theme/utils/checksums/theme_checksums_utils.js +0 -2
- package/build/theme/features/theme/utils/meta_data/theme_meta_data_utils.js +1 -1
- package/build/theme/index.js +3 -1
- package/build/utils/download_file/download_file_utils.js +1 -1
- package/package.json +1 -1
- package/build/theme/commands/push/ui/theme_validation_error.js +0 -6
- package/build/theme/commands/push/ui/theme_validation_errors.js +0 -5
|
@@ -14,6 +14,7 @@ import { ThemeFetchInitializer } from '../../theme/features/theme/fetch/theme_fe
|
|
|
14
14
|
import { ThemeMergeInitializer } from '../../theme/features/theme/merge/theme_merge_initializer.js';
|
|
15
15
|
import { ThemeActionsInitializer } from '../../theme/features/theme/actions/theme_actions_initializer.js';
|
|
16
16
|
import { ThemePushInitializer } from '../../theme/features/theme/push/theme_push_initializer.js';
|
|
17
|
+
import { ThemeDeleteInitializer } from '../../theme/features/theme/delete/theme_delete_initalizer.js';
|
|
17
18
|
tmp.setGracefulCleanup();
|
|
18
19
|
export const cliSetup = async () => {
|
|
19
20
|
//TODO jakis ładny komuniakt błedu
|
|
@@ -34,7 +35,8 @@ export const cliSetup = async () => {
|
|
|
34
35
|
ThemeInitInitializer,
|
|
35
36
|
ThemeMergeInitializer,
|
|
36
37
|
ThemeFetchInitializer,
|
|
37
|
-
ThemePushInitializer
|
|
38
|
+
ThemePushInitializer,
|
|
39
|
+
ThemeDeleteInitializer
|
|
38
40
|
],
|
|
39
41
|
options: {
|
|
40
42
|
featuresTracking: false,
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { Error } from '../../ui/message_box/error.js';
|
|
2
|
+
import { Text } from '../../ui/text.js';
|
|
3
|
+
import React from 'react';
|
|
4
|
+
export const ErrorItem = ({ error, title }) => {
|
|
5
|
+
return (React.createElement(Error, { header: title },
|
|
6
|
+
React.createElement(Text, null, error)));
|
|
7
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { ErrorItem } from './error_item.js';
|
|
3
|
+
import { Error } from '../../ui/message_box/error.js';
|
|
4
|
+
import { Text } from '../../ui/text.js';
|
|
5
|
+
import { Box } from '../../ui/box.js';
|
|
6
|
+
export const ErrorsList = ({ errors }) => {
|
|
7
|
+
if (typeof errors === 'string') {
|
|
8
|
+
return React.createElement(ErrorItem, { error: errors });
|
|
9
|
+
}
|
|
10
|
+
if (Array.isArray(errors)) {
|
|
11
|
+
return errors.map((error, index) => (React.createElement(ErrorItem, { key: index, error: error })));
|
|
12
|
+
}
|
|
13
|
+
return (React.createElement(React.Fragment, null, Object.entries(errors).map(([title, errorMessages], index) => (React.createElement(Error, { key: index, header: title },
|
|
14
|
+
React.createElement(Box, { flexDirection: "column" }, errorMessages.map((message, index) => (React.createElement(Text, { key: index }, message)))))))));
|
|
15
|
+
};
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { Args } from '@oclif/core';
|
|
2
|
+
import { BaseThemeCommand } from '../../class/base_theme_command.js';
|
|
3
|
+
import { CLI_AUTH_API_NAME } from '../../../cli/auth/cli_auth_constants.js';
|
|
4
|
+
import { THEME_ACTION_NOT_FOUND_ERROR_CODE, THEME_ACTIONS_API_NAME, THEME_ACTIONS_TYPES } from '../../features/theme/actions/theme_actions_constants.js';
|
|
5
|
+
import { EXECUTION_CONTEXT_API_NAME } from '../../../cli/features/execution_context/execution_context_constants.js';
|
|
6
|
+
import { renderOnce } from '../../../ui/ui_utils.js';
|
|
7
|
+
import { MissingCredentialsError } from '../../../cli/commands/auth/ui/missing_credentials_error.js';
|
|
8
|
+
import React from 'react';
|
|
9
|
+
import { THEME_DELETE_API_NAME } from '../../features/theme/delete/theme_delete_constants.js';
|
|
10
|
+
import { UnpermittedCommandError } from '../ui/unpermitted_command_error.js';
|
|
11
|
+
import { Error } from '../../../ui/message_box/error.js';
|
|
12
|
+
import { Text } from '../../../ui/text.js';
|
|
13
|
+
import { promptInput } from '../../../ui/prompts/prompt_input.js';
|
|
14
|
+
import { Warning } from '../../../ui/message_box/warning.js';
|
|
15
|
+
import { Info } from '../../../ui/message_box/info.js';
|
|
16
|
+
export class ThemeDeleteCommand extends BaseThemeCommand {
|
|
17
|
+
static summary = 'Permanently deletes the specified theme from your store.';
|
|
18
|
+
static description = 'This action cannot be undone, so make sure you really want to remove this theme.\n\nYou can run this command from a specific theme directory (ID not needed) or outside any theme directory (theme ID required).';
|
|
19
|
+
static examples = [
|
|
20
|
+
{
|
|
21
|
+
description: 'This will delete the theme with ID 123 permanently from your store. Make sure you have a backup if needed.',
|
|
22
|
+
command: '<%= config.bin %> <%= command.id %> 123'
|
|
23
|
+
}
|
|
24
|
+
];
|
|
25
|
+
static hidden = true;
|
|
26
|
+
static args = {
|
|
27
|
+
id: Args.string({
|
|
28
|
+
description: 'Theme id',
|
|
29
|
+
name: 'id',
|
|
30
|
+
required: false,
|
|
31
|
+
type: 'string'
|
|
32
|
+
})
|
|
33
|
+
};
|
|
34
|
+
async run() {
|
|
35
|
+
const data = await this.parse(ThemeDeleteCommand);
|
|
36
|
+
const { args } = data;
|
|
37
|
+
const themeId = args.id;
|
|
38
|
+
const cliAuthApi = this.getApi(CLI_AUTH_API_NAME);
|
|
39
|
+
const executionContextApi = this.getApi(EXECUTION_CONTEXT_API_NAME);
|
|
40
|
+
const credentials = cliAuthApi.getCredentials();
|
|
41
|
+
if (!credentials) {
|
|
42
|
+
renderOnce(React.createElement(MissingCredentialsError, null));
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
//
|
|
46
|
+
// if (!themeId) {
|
|
47
|
+
// renderOnce(<MissingThemeIdError />);
|
|
48
|
+
//
|
|
49
|
+
// return;
|
|
50
|
+
// }
|
|
51
|
+
// const executionContext = await executionContextApi.getExecutionContext();
|
|
52
|
+
//
|
|
53
|
+
// if (executionContext.type === EXECUTION_CONTEXTS.theme) {
|
|
54
|
+
// renderOnce(<ThemeDirectoryContextError />);
|
|
55
|
+
//
|
|
56
|
+
// return;
|
|
57
|
+
// }
|
|
58
|
+
renderOnce(React.createElement(Warning, { header: `Warning: This will permanently delete the theme {Theme_name} (ID: {theme_ID}) and cannot be undone.` }));
|
|
59
|
+
const { input: procced } = await promptInput('Proceed?');
|
|
60
|
+
if (!procced) {
|
|
61
|
+
renderOnce(React.createElement(Info, { header: "Theme deletion was cancelled." }));
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
if (!themeId)
|
|
65
|
+
return;
|
|
66
|
+
const themeActionsApi = this.getApi(THEME_ACTIONS_API_NAME);
|
|
67
|
+
const deleteAction = themeActionsApi.getThemeAction({
|
|
68
|
+
actionType: THEME_ACTIONS_TYPES.delete,
|
|
69
|
+
themeId,
|
|
70
|
+
credentials
|
|
71
|
+
});
|
|
72
|
+
if (!deleteAction) {
|
|
73
|
+
renderOnce(React.createElement(UnpermittedCommandError, { themeId: themeId, commandName: "delete" }));
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
const themeDeleteApi = this.getApi(THEME_DELETE_API_NAME);
|
|
77
|
+
try {
|
|
78
|
+
await themeDeleteApi.deleteTheme({
|
|
79
|
+
actionData: deleteAction.data,
|
|
80
|
+
credentials
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
catch (err) {
|
|
84
|
+
this._handleError(err, themeId);
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
_handleError(err, themeId) {
|
|
89
|
+
if (err?.code === THEME_ACTION_NOT_FOUND_ERROR_CODE && themeId) {
|
|
90
|
+
renderOnce(React.createElement(UnpermittedCommandError, { themeId: themeId, commandName: "init" }));
|
|
91
|
+
}
|
|
92
|
+
if (err?.message) {
|
|
93
|
+
renderOnce(React.createElement(Error, null,
|
|
94
|
+
React.createElement(Text, null, err.message)));
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
this.error(String(err));
|
|
98
|
+
}
|
|
99
|
+
}
|
|
@@ -12,9 +12,9 @@ import { UnpermittedCommandError } from '../ui/unpermitted_command_error.js';
|
|
|
12
12
|
import { MissingCredentialsError } from '../../../cli/commands/auth/ui/missing_credentials_error.js';
|
|
13
13
|
import { ThemeCreatedSuccess } from './ui/theme_created_success.js';
|
|
14
14
|
import { Text } from '../../../ui/text.js';
|
|
15
|
-
import { Error } from '../../../ui/message_box/error.js';
|
|
16
15
|
import { MissingThemeIdError } from '../ui/missing_theme_id_error.js';
|
|
17
16
|
import { Box } from '../../../ui/box.js';
|
|
17
|
+
import { ErrorsList } from '../../../cli/ui/errors_list.js';
|
|
18
18
|
export class ThemeInitCommand extends BaseThemeCommand {
|
|
19
19
|
static summary = 'Creates a copy of an existing theme by duplicating it.';
|
|
20
20
|
static description = 'The new theme will be named after the source theme with a "Copy" suffix and an incremental number.\n\nYou must run this command from the root directory (not inside any theme folder) and provide the ID of the theme you want to duplicate.';
|
|
@@ -73,15 +73,17 @@ export class ThemeInitCommand extends BaseThemeCommand {
|
|
|
73
73
|
catch (err) {
|
|
74
74
|
spinner?.stop();
|
|
75
75
|
this._handleError(err, themeId);
|
|
76
|
+
//TODO analyze why on nodejs v22 this command does not exit properly, even all promises are resolved
|
|
77
|
+
this.exit();
|
|
76
78
|
}
|
|
77
79
|
}
|
|
78
80
|
_handleError(err, themeId) {
|
|
79
81
|
if (err?.code === THEME_ACTION_NOT_FOUND_ERROR_CODE && themeId) {
|
|
80
82
|
renderOnce(React.createElement(UnpermittedCommandError, { themeId: themeId, commandName: "init" }));
|
|
83
|
+
return;
|
|
81
84
|
}
|
|
82
85
|
if (err?.message) {
|
|
83
|
-
renderOnce(React.createElement(
|
|
84
|
-
React.createElement(Text, null, err.message)));
|
|
86
|
+
renderOnce(React.createElement(ErrorsList, { errors: err.message }));
|
|
85
87
|
return;
|
|
86
88
|
}
|
|
87
89
|
this.error(String(err));
|
|
@@ -2,7 +2,7 @@ import { BaseThemeCommand } from '../../class/base_theme_command.js';
|
|
|
2
2
|
import { CLI_AUTH_API_NAME } from '../../../cli/auth/cli_auth_constants.js';
|
|
3
3
|
import { EXECUTION_CONTEXT_API_NAME, EXECUTION_CONTEXTS } from '../../../cli/features/execution_context/execution_context_constants.js';
|
|
4
4
|
import { THEME_ACTION_NOT_FOUND_ERROR_CODE, THEME_ACTIONS_API_NAME, THEME_ACTIONS_TYPES } from '../../features/theme/actions/theme_actions_constants.js';
|
|
5
|
-
import { THEME_ARCHIVE_UPLOAD_ERROR, THEME_PUSH_API_NAME } from '../../features/theme/push/theme_push_constants.js';
|
|
5
|
+
import { THEME_ARCHIVE_UPLOAD_ERROR, THEME_FILES_UPLOAD_ERROR, THEME_PUSH_API_NAME } from '../../features/theme/push/theme_push_constants.js';
|
|
6
6
|
import { ThemeMetaDataUtils } from '../../features/theme/utils/meta_data/theme_meta_data_utils.js';
|
|
7
7
|
import { mapChecksumToTree } from '../../../utils/checksums/checksums_utils.js';
|
|
8
8
|
import { mapToPermissionsTree } from '../../utils/directory_validator/directory_validator_utils.js';
|
|
@@ -20,13 +20,13 @@ import { THEME_FILES_STRUCTURE_FILE_NAME } from '../../features/theme/theme_cons
|
|
|
20
20
|
import { THEME_MERGE_API_NAME } from '../../features/theme/merge/theme_merge_constants.js';
|
|
21
21
|
import { ThemePushSkipInfo } from './ui/theme_push_skip_into.js';
|
|
22
22
|
import { ThemeUnpermittedActionsError } from './ui/theme_unpermitted_actions_error.js';
|
|
23
|
-
import { ThemeValidationErrors } from './ui/theme_validation_errors.js';
|
|
24
23
|
import { Error } from '../../../ui/message_box/error.js';
|
|
25
24
|
import { Text } from '../../../ui/text.js';
|
|
26
25
|
import { ThemeWorkUrlMismatch } from '../ui/theme_work_url_mismatch.js';
|
|
27
26
|
import { THEME_WORK_URL_MISMATCH_ERROR } from '../../features/theme/utils/meta_data/theme_meta_data_constants.js';
|
|
28
27
|
import { ThemeFilesStructureUtils } from '../../features/theme/utils/files_structure/theme_files_structure_utils.js';
|
|
29
28
|
import { ThemeInfoUtils } from '../../features/theme/info/theme_info_utils.js';
|
|
29
|
+
import { ErrorsList } from '../../../cli/ui/errors_list.js';
|
|
30
30
|
export class ThemePushCommand extends BaseThemeCommand {
|
|
31
31
|
static summary = 'Uploads your local theme files to the store and overwrites the current version of the theme in your store.';
|
|
32
32
|
static description = 'Check your local changes before pushing.\n\nYou must run this command from a specific theme directory (ID not needed).';
|
|
@@ -111,16 +111,20 @@ export class ThemePushCommand extends BaseThemeCommand {
|
|
|
111
111
|
return;
|
|
112
112
|
}
|
|
113
113
|
if (err?.code === THEME_ARCHIVE_UPLOAD_ERROR) {
|
|
114
|
-
renderOnce(React.createElement(
|
|
114
|
+
renderOnce(React.createElement(ErrorsList, { errors: err?.details }));
|
|
115
115
|
return;
|
|
116
116
|
}
|
|
117
117
|
if (err?.code === THEME_WORK_URL_MISMATCH_ERROR) {
|
|
118
118
|
this._renderUrlMismatchError();
|
|
119
119
|
return;
|
|
120
120
|
}
|
|
121
|
+
if (err?.code === THEME_FILES_UPLOAD_ERROR) {
|
|
122
|
+
return renderOnce(React.createElement(Error, { header: "Uploading theme files to the shop failed.\n" },
|
|
123
|
+
React.createElement(Text, null, "The rejected files have been removed locally. Please ensure that the files are not corrupted and that the file extensions are supported.")));
|
|
124
|
+
}
|
|
121
125
|
if (err?.message) {
|
|
122
|
-
|
|
123
|
-
|
|
126
|
+
console.log('error message', err.message);
|
|
127
|
+
renderOnce(React.createElement(ErrorsList, { errors: err.message }));
|
|
124
128
|
return;
|
|
125
129
|
}
|
|
126
130
|
this.error(String(err));
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { FeatureApi } from '@dreamcommerce/star_core';
|
|
2
|
+
import { THEME_DELETE_API_NAME } from '../theme_delete_constants.js';
|
|
3
|
+
export class ThemeDeleteApi extends FeatureApi {
|
|
4
|
+
moduleName = THEME_DELETE_API_NAME;
|
|
5
|
+
#service;
|
|
6
|
+
constructor(service) {
|
|
7
|
+
super();
|
|
8
|
+
this.#service = service;
|
|
9
|
+
}
|
|
10
|
+
async deleteTheme(props) {
|
|
11
|
+
return this.#service.deleteTheme(props);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export class ThemeDeleteHttpApi {
|
|
2
|
+
#httpApi;
|
|
3
|
+
constructor(httpApi) {
|
|
4
|
+
this.#httpApi = httpApi;
|
|
5
|
+
}
|
|
6
|
+
deleteTheme({ shopUrl, actionData }) {
|
|
7
|
+
const { method, url } = actionData;
|
|
8
|
+
return this.#httpApi.fetch({
|
|
9
|
+
url: `${shopUrl}${url}`,
|
|
10
|
+
method,
|
|
11
|
+
sanitizeOptions: {
|
|
12
|
+
disable: true
|
|
13
|
+
},
|
|
14
|
+
isPrivate: true
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { STATUS_CODES } from '@dreamcommerce/star_core';
|
|
2
|
+
import { HttpErrorsFactory } from '../../../../../cli/class/errors/http/http_errors_factory.js';
|
|
3
|
+
import { DownloadFileErrorsFactory } from '../../../../../utils/download_file/download_file_errors_factory.js';
|
|
4
|
+
export class ThemeDeleteService {
|
|
5
|
+
#httpApi;
|
|
6
|
+
constructor(httpApi) {
|
|
7
|
+
this.#httpApi = httpApi;
|
|
8
|
+
}
|
|
9
|
+
async deleteTheme({ actionData, credentials }) {
|
|
10
|
+
try {
|
|
11
|
+
const { response } = this.#httpApi.deleteTheme({ actionData, shopUrl: credentials.shopUrl });
|
|
12
|
+
const { data, status } = await response;
|
|
13
|
+
if (status !== STATUS_CODES.ok)
|
|
14
|
+
return;
|
|
15
|
+
//TODO if is success remove theme directory
|
|
16
|
+
return data;
|
|
17
|
+
}
|
|
18
|
+
catch (err) {
|
|
19
|
+
//TODO to basic class
|
|
20
|
+
switch (err.response?.status) {
|
|
21
|
+
case 403:
|
|
22
|
+
throw HttpErrorsFactory.createForbiddenError();
|
|
23
|
+
case 401:
|
|
24
|
+
throw HttpErrorsFactory.createUnauthorizedError();
|
|
25
|
+
case 404:
|
|
26
|
+
throw HttpErrorsFactory.createNotFoundError();
|
|
27
|
+
default:
|
|
28
|
+
if (err.response?.status !== 200) {
|
|
29
|
+
throw DownloadFileErrorsFactory.downloadError(err.response.status);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { FEATURE_CORES_TYPES, HTTP_REQUESTER_API_NAME, SyncFeatureInitializer } from '@dreamcommerce/star_core';
|
|
2
|
+
import { ThemeDeleteApi } from './api/theme_delete_api.js';
|
|
3
|
+
import { ThemeDeleteService } from './service/theme_delete_service.js';
|
|
4
|
+
import { ThemeDeleteHttpApi } from './http/theme_delete_http_api.js';
|
|
5
|
+
import { THEME_DELETE_FEATURE_NAME } from './theme_delete_constants.js';
|
|
6
|
+
export class ThemeDeleteInitializer extends SyncFeatureInitializer {
|
|
7
|
+
static featureName = THEME_DELETE_FEATURE_NAME;
|
|
8
|
+
init() {
|
|
9
|
+
const httpApi = this.getApiSync(HTTP_REQUESTER_API_NAME);
|
|
10
|
+
const service = new ThemeDeleteService(new ThemeDeleteHttpApi(httpApi));
|
|
11
|
+
return {
|
|
12
|
+
cores: [
|
|
13
|
+
{
|
|
14
|
+
type: FEATURE_CORES_TYPES.api,
|
|
15
|
+
instance: new ThemeDeleteApi(service)
|
|
16
|
+
}
|
|
17
|
+
]
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import FSTree from 'fs-tree-diff';
|
|
2
2
|
import { join } from '../../../../../utils/path_utils.js';
|
|
3
3
|
import { computeFileChecksum } from '../../../../../utils/checksums/checksums_utils.js';
|
|
4
|
-
import {
|
|
4
|
+
import { copyFileSync, fileExists, getAllDirectoriesNamesInside } from '../../../../../utils/fs/fs_utils.js';
|
|
5
5
|
import { ThemeChecksumsUtils } from '../../utils/checksums/theme_checksums_utils.js';
|
|
6
6
|
import walkSync from 'walk-sync';
|
|
7
7
|
import { SHOPER_THEME_METADATA_DIR } from '../../../../constants/directory_contstants.js';
|
|
@@ -9,7 +9,7 @@ export class ThemeMergeService {
|
|
|
9
9
|
async applyChanges(fromTheme, toTheme, changes) {
|
|
10
10
|
FSTree.applyPatch(fromTheme, toTheme, changes, {
|
|
11
11
|
create(inputPath, outputPath) {
|
|
12
|
-
|
|
12
|
+
copyFileSync(inputPath, outputPath);
|
|
13
13
|
},
|
|
14
14
|
change(inputPath, outputPath) {
|
|
15
15
|
copyFileSync(inputPath, outputPath);
|
|
@@ -105,18 +105,24 @@ export class ThemePushService {
|
|
|
105
105
|
}
|
|
106
106
|
async _uploadThemeFiles({ filesToUpload, themeRootDir, localFiles, credentials }) {
|
|
107
107
|
try {
|
|
108
|
-
const uploadedImageData = await this._uploadFiles(filesToUpload, credentials);
|
|
108
|
+
const { uploadedImageData, rejectedImageData } = await this._uploadFiles(filesToUpload, credentials);
|
|
109
109
|
const newFilesList = ThemeImagesUtils.updateOriginalFilenameToUploadedFilename(localFiles, uploadedImageData);
|
|
110
110
|
if (uploadedImageData.length)
|
|
111
111
|
await ThemeImagesUtils.removeUploadedOriginalFiles(themeRootDir, uploadedImageData);
|
|
112
|
+
if (rejectedImageData.length)
|
|
113
|
+
await this._removeUploadedThemeFiles(rejectedImageData, themeRootDir);
|
|
112
114
|
await this._createAFilesListFile(themeRootDir, newFilesList);
|
|
113
115
|
}
|
|
114
116
|
catch (err) {
|
|
115
117
|
throw ThemePushErrorsFactory.createErrorWhileUploadingThemeFiles(credentials.shopUrl, err);
|
|
116
118
|
}
|
|
117
119
|
}
|
|
120
|
+
async _removeUploadedThemeFiles(uploadedImageData, themeRootDir) {
|
|
121
|
+
await Promise.all(uploadedImageData.map(({ location, originalFilename }) => removeFile(join(themeRootDir, location, originalFilename))));
|
|
122
|
+
}
|
|
118
123
|
async _uploadFiles(uploadData, credentials) {
|
|
119
124
|
const uploadedImageData = [];
|
|
125
|
+
const rejectedImageData = [];
|
|
120
126
|
await Promise.all(uploadData.map(({ actionData, path }) => this.#themePushHttpApi
|
|
121
127
|
.pushThemeData({
|
|
122
128
|
actionData,
|
|
@@ -124,15 +130,22 @@ export class ThemePushService {
|
|
|
124
130
|
shopUrl: credentials.shopUrl
|
|
125
131
|
})
|
|
126
132
|
.response.then((response) => {
|
|
127
|
-
if (response.status !== 200 || !response.data.isSuccess)
|
|
128
|
-
throw ThemePushErrorsFactory.createErrorWhileUploadingThemeFiles(credentials.shopUrl, response.data?.messages ?? []);
|
|
129
133
|
uploadedImageData.push({
|
|
130
134
|
location: dirname(path),
|
|
131
135
|
originalFilename: basename(path),
|
|
132
136
|
uploadedFilename: response.data.imageId
|
|
133
137
|
});
|
|
138
|
+
})
|
|
139
|
+
.catch(() => {
|
|
140
|
+
rejectedImageData.push({
|
|
141
|
+
location: dirname(path),
|
|
142
|
+
originalFilename: basename(path)
|
|
143
|
+
});
|
|
134
144
|
})));
|
|
135
|
-
return
|
|
145
|
+
return {
|
|
146
|
+
uploadedImageData,
|
|
147
|
+
rejectedImageData
|
|
148
|
+
};
|
|
136
149
|
}
|
|
137
150
|
async _getActionDataForFilesToUpload({ pushAction, filesStructure, executionContext }) {
|
|
138
151
|
const uploadData = [];
|
|
@@ -183,7 +196,7 @@ export class ThemePushService {
|
|
|
183
196
|
return;
|
|
184
197
|
const toUnixStyleFilesList = Object.entries(filesList).reduce((acc, [filePath, value]) => {
|
|
185
198
|
const unixPath = toUnixPath(filePath);
|
|
186
|
-
const finalPath = looksLikeDirectory(unixPath) ?
|
|
199
|
+
const finalPath = looksLikeDirectory(unixPath) ? `${unixPath}${path.posix.sep}` : unixPath;
|
|
187
200
|
return {
|
|
188
201
|
...acc,
|
|
189
202
|
[finalPath]: value
|
|
@@ -3,3 +3,4 @@ export const THEME_PUSH_API_NAME = 'ThemePushApi';
|
|
|
3
3
|
export const THEME_FILES_LIST_FILE_NAME = 'filesList.json';
|
|
4
4
|
export const THEME_MODULE_SETTINGS_FILE_NAME = 'settings.json';
|
|
5
5
|
export const THEME_ARCHIVE_UPLOAD_ERROR = 'theme.push.error_uploading_theme_archive';
|
|
6
|
+
export const THEME_FILES_UPLOAD_ERROR = 'theme.push.error_uploading_theme_files';
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { AppError } from '../../../../cli/class/errors/app/app_error.js';
|
|
2
|
-
import { THEME_ARCHIVE_UPLOAD_ERROR } from './theme_push_constants.js';
|
|
2
|
+
import { THEME_ARCHIVE_UPLOAD_ERROR, THEME_FILES_UPLOAD_ERROR } from './theme_push_constants.js';
|
|
3
3
|
export class ThemePushErrorsFactory {
|
|
4
4
|
static createErrorWhileUploadingThemeFiles(shopUrl, messages) {
|
|
5
5
|
return new AppError({
|
|
6
|
-
code:
|
|
6
|
+
code: THEME_FILES_UPLOAD_ERROR,
|
|
7
7
|
message: `Error while uploading theme files to shop "${shopUrl}"`,
|
|
8
8
|
level: 'error',
|
|
9
|
-
details:
|
|
9
|
+
details: {
|
|
10
|
+
messages
|
|
11
|
+
}
|
|
10
12
|
});
|
|
11
13
|
}
|
|
12
14
|
static createErrorWhileCreatingThemeArchive(shopUrl, error) {
|
|
@@ -25,7 +25,6 @@ export class ThemeChecksumsUtils {
|
|
|
25
25
|
}
|
|
26
26
|
static async createThemeChecksumsFiles(themeDir, checksums) {
|
|
27
27
|
return new Promise((resolve, reject) => {
|
|
28
|
-
// const sortedChecksums = _.pick(checksums, Object.keys(checksums).sort());
|
|
29
28
|
const currentChecksumFilePath = this.getCurrentThemeChecksumsFilePath(themeDir);
|
|
30
29
|
const initialChecksumFilePath = this.getInitialThemeChecksumsFilePath(themeDir);
|
|
31
30
|
const checksumStream = createWriteStream(initialChecksumFilePath);
|
|
@@ -48,7 +47,6 @@ export class ThemeChecksumsUtils {
|
|
|
48
47
|
}
|
|
49
48
|
static async createThemeCurrentChecksumsFile(themeDir, checksums) {
|
|
50
49
|
return new Promise((resolve, reject) => {
|
|
51
|
-
// const sortedChecksums = _.pick(checksums, Object.keys(checksums).sort());
|
|
52
50
|
const currentChecksumFilePath = this.getCurrentThemeChecksumsFilePath(themeDir);
|
|
53
51
|
const checksumStream = createWriteStream(currentChecksumFilePath);
|
|
54
52
|
checksumStream.write(JSON.stringify(checksums, null, JSON_FILE_INDENT));
|
package/build/theme/index.js
CHANGED
|
@@ -6,6 +6,7 @@ import { ThemePushCommand } from './commands/push/theme_push_command.js';
|
|
|
6
6
|
import { ThemeShowChangesCommand } from './commands/theme_show_changes_command.js';
|
|
7
7
|
import { ThemeVerifyCommand } from './commands/theme_verify_command.js';
|
|
8
8
|
import { ThemeInfoCommand } from './commands/info/theme_info_command.js';
|
|
9
|
+
import { ThemeDeleteCommand } from './commands/delete/theme_delete_command.js';
|
|
9
10
|
export const COMMANDS = {
|
|
10
11
|
[THEME_COMMANDS_NAME.list]: ThemeListCommand,
|
|
11
12
|
[THEME_COMMANDS_NAME.pull]: ThemePullCommand,
|
|
@@ -13,5 +14,6 @@ export const COMMANDS = {
|
|
|
13
14
|
[THEME_COMMANDS_NAME.push]: ThemePushCommand,
|
|
14
15
|
[THEME_COMMANDS_NAME.showChanges]: ThemeShowChangesCommand,
|
|
15
16
|
[THEME_COMMANDS_NAME.verify]: ThemeVerifyCommand,
|
|
16
|
-
[THEME_COMMANDS_NAME.info]: ThemeInfoCommand
|
|
17
|
+
[THEME_COMMANDS_NAME.info]: ThemeInfoCommand,
|
|
18
|
+
[THEME_COMMANDS_NAME.delete]: ThemeDeleteCommand
|
|
17
19
|
};
|
|
@@ -41,6 +41,6 @@ export const downloadFile = async ({ dist, request }) => {
|
|
|
41
41
|
throw DownloadFileErrorsFactory.downloadError(err.response.status);
|
|
42
42
|
}
|
|
43
43
|
}
|
|
44
|
+
throw DownloadFileErrorsFactory.downloadError('Unknown error');
|
|
44
45
|
}
|
|
45
|
-
throw DownloadFileErrorsFactory.downloadError('Unknown error');
|
|
46
46
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { Error } from '../../../../ui/message_box/error.js';
|
|
3
|
-
import { Text } from '../../../../ui/text.js';
|
|
4
|
-
export const ThemeValidationError = ({ messages, name }) => {
|
|
5
|
-
return (React.createElement(Error, { header: name }, messages.map((message, index) => (React.createElement(Text, { key: index }, message)))));
|
|
6
|
-
};
|
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { ThemeValidationError } from './theme_validation_error.js';
|
|
3
|
-
export const ThemeValidationErrors = ({ errors }) => {
|
|
4
|
-
return (React.createElement(React.Fragment, null, Object.entries(errors).map(([name, messages]) => (React.createElement(ThemeValidationError, { key: name, name: name, messages: messages })))));
|
|
5
|
-
};
|