shoplazza-cli 0.0.1
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/.editorconfig +28 -0
- package/.prettierrc +9 -0
- package/LICENSE +21 -0
- package/README.md +208 -0
- package/bin/shoplazza +117 -0
- package/fixtures/assets/blog.scss +74 -0
- package/fixtures/assets/cart_modal.scss +450 -0
- package/fixtures/assets/collection_detail.js +234 -0
- package/fixtures/assets/collection_detail.scss +345 -0
- package/fixtures/assets/collection_list.scss +11 -0
- package/fixtures/assets/collection_slider.scss +169 -0
- package/fixtures/assets/feature_columns.scss +26 -0
- package/fixtures/assets/feature_product.scss +109 -0
- package/fixtures/assets/footer.js +58 -0
- package/fixtures/assets/footer.scss +337 -0
- package/fixtures/assets/four_images.scss +29 -0
- package/fixtures/assets/gallery.scss +55 -0
- package/fixtures/assets/header.js +178 -0
- package/fixtures/assets/header.scss +929 -0
- package/fixtures/assets/image_text.scss +72 -0
- package/fixtures/assets/logo_bar.scss +11 -0
- package/fixtures/assets/newsletter.scss +90 -0
- package/fixtures/assets/not_found.scss +39 -0
- package/fixtures/assets/page_detail.scss +16 -0
- package/fixtures/assets/pagination.scss +150 -0
- package/fixtures/assets/postcss.config.js +6 -0
- package/fixtures/assets/product_description.scss +88 -0
- package/fixtures/assets/product_detail.js +634 -0
- package/fixtures/assets/product_detail.scss +1106 -0
- package/fixtures/assets/relative_product.scss +45 -0
- package/fixtures/assets/reviews.scss +70 -0
- package/fixtures/assets/rich_text.scss +71 -0
- package/fixtures/assets/search.js +87 -0
- package/fixtures/assets/search.scss +67 -0
- package/fixtures/assets/slide.scss +51 -0
- package/fixtures/assets/slider.scss +141 -0
- package/fixtures/assets/theme.css +976 -0
- package/fixtures/assets/theme.scss +1100 -0
- package/fixtures/assets/three_images.scss +20 -0
- package/fixtures/assets/tools.scss +23 -0
- package/fixtures/assets/two_images.scss +24 -0
- package/fixtures/assets/video.scss +45 -0
- package/fixtures/assets/video_text.scss +63 -0
- package/fixtures/config/settings_data.json +107 -0
- package/fixtures/config/settings_schema.json +690 -0
- package/fixtures/layout/theme.liquid +76 -0
- package/fixtures/locales/ar-SA.json +212 -0
- package/fixtures/locales/de-DE.json +290 -0
- package/fixtures/locales/en-US.json +290 -0
- package/fixtures/locales/es-ES.json +290 -0
- package/fixtures/locales/fr-FR.json +290 -0
- package/fixtures/locales/id-ID.json +212 -0
- package/fixtures/locales/it-IT.json +212 -0
- package/fixtures/locales/ja-JP.json +289 -0
- package/fixtures/locales/ko-KR.json +290 -0
- package/fixtures/locales/nl-NL.json +290 -0
- package/fixtures/locales/pl-PL.json +290 -0
- package/fixtures/locales/pt-PT.json +212 -0
- package/fixtures/locales/ru-RU.json +212 -0
- package/fixtures/locales/th-TH.json +212 -0
- package/fixtures/locales/zh-CN.json +290 -0
- package/fixtures/locales/zh-TW.json +290 -0
- package/fixtures/sections/apps.liquid +47 -0
- package/fixtures/sections/blog.liquid +137 -0
- package/fixtures/sections/collection_desc.liquid +34 -0
- package/fixtures/sections/collection_detail.liquid +436 -0
- package/fixtures/sections/collection_image.liquid +104 -0
- package/fixtures/sections/collection_list.liquid +161 -0
- package/fixtures/sections/collection_name.liquid +34 -0
- package/fixtures/sections/collection_slider.liquid +330 -0
- package/fixtures/sections/feature_columns.liquid +275 -0
- package/fixtures/sections/feature_product.liquid +227 -0
- package/fixtures/sections/footer.liquid +488 -0
- package/fixtures/sections/four_images.liquid +160 -0
- package/fixtures/sections/gallery.liquid +258 -0
- package/fixtures/sections/header.liquid +1157 -0
- package/fixtures/sections/html.liquid +40 -0
- package/fixtures/sections/image_text.liquid +350 -0
- package/fixtures/sections/instagram_plus.liquid +393 -0
- package/fixtures/sections/logo_bar.liquid +183 -0
- package/fixtures/sections/newsletter.liquid +225 -0
- package/fixtures/sections/not_found.liquid +39 -0
- package/fixtures/sections/overlay_image.liquid +648 -0
- package/fixtures/sections/page_detail.liquid +39 -0
- package/fixtures/sections/photo_collection.liquid +433 -0
- package/fixtures/sections/product_description.liquid +208 -0
- package/fixtures/sections/product_detail.liquid +611 -0
- package/fixtures/sections/products.liquid +216 -0
- package/fixtures/sections/relative_product.liquid +121 -0
- package/fixtures/sections/reviews.liquid +115 -0
- package/fixtures/sections/rich_text.liquid +157 -0
- package/fixtures/sections/search.liquid +163 -0
- package/fixtures/sections/slide.liquid +719 -0
- package/fixtures/sections/three_images.liquid +157 -0
- package/fixtures/sections/two_images.liquid +125 -0
- package/fixtures/sections/video.liquid +95 -0
- package/fixtures/sections/video_text.liquid +128 -0
- package/fixtures/snippets/bgset.liquid +21 -0
- package/fixtures/snippets/card_title.liquid +8 -0
- package/fixtures/snippets/cart_modal.liquid +74 -0
- package/fixtures/snippets/collection.liquid +77 -0
- package/fixtures/snippets/collection_filter_modal.liquid +56 -0
- package/fixtures/snippets/default_image_4.liquid +14 -0
- package/fixtures/snippets/default_image_6.liquid +18 -0
- package/fixtures/snippets/default_image_8.liquid +23 -0
- package/fixtures/snippets/four_images_item.liquid +8 -0
- package/fixtures/snippets/header_ads.liquid +95 -0
- package/fixtures/snippets/hero_image.liquid +94 -0
- package/fixtures/snippets/icon_video_play_large.liquid +1 -0
- package/fixtures/snippets/icon_video_play_medium.liquid +4 -0
- package/fixtures/snippets/icon_video_play_small.liquid +4 -0
- package/fixtures/snippets/lazyimg.liquid +22 -0
- package/fixtures/snippets/lazyimg_art.liquid +36 -0
- package/fixtures/snippets/lazysizes.liquid +41 -0
- package/fixtures/snippets/link.liquid +2 -0
- package/fixtures/snippets/pagination.liquid +48 -0
- package/fixtures/snippets/product.liquid +126 -0
- package/fixtures/snippets/product_art_tpl.liquid +152 -0
- package/fixtures/snippets/product_info_body.liquid +337 -0
- package/fixtures/snippets/product_info_tpl.liquid +423 -0
- package/fixtures/snippets/product_label.liquid +46 -0
- package/fixtures/snippets/settings.liquid +295 -0
- package/fixtures/snippets/social-meta-tags.liquid +106 -0
- package/fixtures/snippets/video_html.liquid +11 -0
- package/fixtures/snippets/video_source.liquid +98 -0
- package/fixtures/snippets/video_thumb_icon.liquid +2 -0
- package/fixtures/templates/404.liquid +1 -0
- package/fixtures/templates/collection.liquid +92 -0
- package/fixtures/templates/index.liquid +206 -0
- package/fixtures/templates/page.liquid +1 -0
- package/fixtures/templates/product.liquid +99 -0
- package/fixtures/templates/search.liquid +1 -0
- package/jest.config.js +192 -0
- package/lib/__tests__/log.test.js +15 -0
- package/lib/__tests__/utils.test.js +69 -0
- package/lib/auth/__mocks__/getCode.js +7 -0
- package/lib/auth/__mocks__/index.js +0 -0
- package/lib/auth/child.js +23 -0
- package/lib/auth/getCode.js +35 -0
- package/lib/auth/index.js +91 -0
- package/lib/commands/__tests__/login.test.js +77 -0
- package/lib/commands/__tests__/logout.test.js +29 -0
- package/lib/commands/__tests__/store.test.js +44 -0
- package/lib/commands/__tests__/switch.test.js +45 -0
- package/lib/commands/login.js +99 -0
- package/lib/commands/logout.js +14 -0
- package/lib/commands/store.js +14 -0
- package/lib/commands/switch.js +52 -0
- package/lib/commands/theme/__tests__/delete.test.js +49 -0
- package/lib/commands/theme/__tests__/init.test.js +21 -0
- package/lib/commands/theme/__tests__/list.test.js +80 -0
- package/lib/commands/theme/__tests__/package.test.js +17 -0
- package/lib/commands/theme/__tests__/publish.test.js +61 -0
- package/lib/commands/theme/__tests__/pull.test.js +69 -0
- package/lib/commands/theme/__tests__/push.test.js +63 -0
- package/lib/commands/theme/__tests__/serve.test.js +107 -0
- package/lib/commands/theme/delete.js +64 -0
- package/lib/commands/theme/init.js +51 -0
- package/lib/commands/theme/list.js +28 -0
- package/lib/commands/theme/package.js +37 -0
- package/lib/commands/theme/publish.js +56 -0
- package/lib/commands/theme/pull.js +62 -0
- package/lib/commands/theme/push.js +106 -0
- package/lib/commands/theme/serve.js +153 -0
- package/lib/commands/theme/share.js +20 -0
- package/lib/commands/version.js +6 -0
- package/lib/config.js +5 -0
- package/lib/db/__mocks__/index.js +9 -0
- package/lib/db/__tests__/analytics.test.js +19 -0
- package/lib/db/__tests__/user.test.js +20 -0
- package/lib/db/analytics.js +48 -0
- package/lib/db/index.js +9 -0
- package/lib/db/user.js +68 -0
- package/lib/log.js +13 -0
- package/lib/openAPI/__mocks__/index.js +20 -0
- package/lib/openAPI/api.js +76 -0
- package/lib/openAPI/index.js +46 -0
- package/lib/report.js +37 -0
- package/lib/tracing.js +50 -0
- package/lib/utils.js +48 -0
- package/package.json +54 -0
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
const axios = require('axios');
|
|
2
|
+
const chalk = require('chalk');
|
|
3
|
+
const Sentry = require('@sentry/node');
|
|
4
|
+
const querystring = require('querystring');
|
|
5
|
+
const { get, set, empty } = require('../db/user');
|
|
6
|
+
const log = require('../log');
|
|
7
|
+
const { SSO_AUTH_URL, ACCOUNT_URL, CLIENT_ID, REDIRECT_URI } = require('../config');
|
|
8
|
+
|
|
9
|
+
exports.postAccessToken = async (code) => {
|
|
10
|
+
try {
|
|
11
|
+
const { data } = await axios.post(
|
|
12
|
+
`${SSO_AUTH_URL}/api/oauth/token`,
|
|
13
|
+
querystring.stringify({
|
|
14
|
+
client_id: CLIENT_ID,
|
|
15
|
+
code,
|
|
16
|
+
grant_type: 'authorization_code',
|
|
17
|
+
redirect_uri: REDIRECT_URI
|
|
18
|
+
}),
|
|
19
|
+
{
|
|
20
|
+
headers: {
|
|
21
|
+
'Content-Type': 'application/x-www-form-urlencoded'
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
);
|
|
25
|
+
empty();
|
|
26
|
+
set({
|
|
27
|
+
access_token: data.access_token,
|
|
28
|
+
session_id: data.session_id
|
|
29
|
+
});
|
|
30
|
+
} catch (err) {
|
|
31
|
+
log.error(chalk.red('\n✗ Failed to post access token'));
|
|
32
|
+
process.exit(-1);
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
exports.getUserInfo = async () => {
|
|
37
|
+
try {
|
|
38
|
+
const { data } = await axios.get(`${SSO_AUTH_URL}/api/sso/current/users`, {
|
|
39
|
+
headers: {
|
|
40
|
+
Cookie: `awesomev2=${get('session_id')}`
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
const activeUser = data.users.find((user) => user.active) || data.users[0];
|
|
44
|
+
set({
|
|
45
|
+
user_id: activeUser.user_id
|
|
46
|
+
});
|
|
47
|
+
} catch (err) {
|
|
48
|
+
log.error(chalk.red('✗ Failed to get user info'));
|
|
49
|
+
process.exit(-1);
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
exports.postExchangeToken = async (storeDomain, options = {}) => {
|
|
54
|
+
return new Promise(async (resolve, reject) => {
|
|
55
|
+
try {
|
|
56
|
+
const { data } = await axios.post(
|
|
57
|
+
`${ACCOUNT_URL}/api/accounts/store/token`,
|
|
58
|
+
{
|
|
59
|
+
user_id: get('user_id'),
|
|
60
|
+
domain: storeDomain
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
headers: {
|
|
64
|
+
Cookie: `awesomev2=${get('session_id')}`
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
);
|
|
68
|
+
set({
|
|
69
|
+
store_domain: storeDomain,
|
|
70
|
+
exchange_token: data.access_token
|
|
71
|
+
});
|
|
72
|
+
resolve();
|
|
73
|
+
} catch (err) {
|
|
74
|
+
reject(err);
|
|
75
|
+
Sentry.captureException(err);
|
|
76
|
+
empty();
|
|
77
|
+
if (!options.ignoreLogError) {
|
|
78
|
+
if (err?.response?.status === 401) {
|
|
79
|
+
log.error(chalk.red(`\n✗ You are not authorized to edit themes on ${storeDomain}.`));
|
|
80
|
+
log.info(
|
|
81
|
+
chalk.green(
|
|
82
|
+
'Check if your user is activated, has permission to edit themes at the store, and try to re-login.'
|
|
83
|
+
)
|
|
84
|
+
);
|
|
85
|
+
} else {
|
|
86
|
+
log.error(chalk.red(`\n✗ ${storeDomain} is not a valid store.`));
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
};
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
const { login } = require('../login');
|
|
2
|
+
const axios = require('axios');
|
|
3
|
+
const { get, empty } = require('../../db/user');
|
|
4
|
+
const MockAdapter = require('axios-mock-adapter');
|
|
5
|
+
const { SSO_AUTH_URL, ACCOUNT_URL } = require('../../config');
|
|
6
|
+
|
|
7
|
+
jest.mock('../../db');
|
|
8
|
+
jest.mock('../../auth/getCode');
|
|
9
|
+
jest.mock('inquirer', () => ({
|
|
10
|
+
prompt: () => Promise.resolve({ confirm: 'Yes' })
|
|
11
|
+
}));
|
|
12
|
+
jest.mock('ora', () => () => ({
|
|
13
|
+
start: () => ({
|
|
14
|
+
stop: () => {}
|
|
15
|
+
})
|
|
16
|
+
}));
|
|
17
|
+
|
|
18
|
+
describe('login', () => {
|
|
19
|
+
let mock;
|
|
20
|
+
|
|
21
|
+
beforeEach(() => {
|
|
22
|
+
mock = new MockAdapter(axios);
|
|
23
|
+
empty();
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
afterEach(() => {
|
|
27
|
+
mock.reset();
|
|
28
|
+
empty();
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('success', async () => {
|
|
32
|
+
mock.onPost(`${SSO_AUTH_URL}/api/oauth/token`).replyOnce(200, {
|
|
33
|
+
access_token: 'access_token',
|
|
34
|
+
session_id: 'session_id'
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
mock.onGet(`${SSO_AUTH_URL}/api/sso/current/users`).replyOnce(200, {
|
|
38
|
+
users: [
|
|
39
|
+
{
|
|
40
|
+
user_id: 'user_id'
|
|
41
|
+
}
|
|
42
|
+
]
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
mock.onPost(`${ACCOUNT_URL}/api/accounts/store/token`).replyOnce(200, {
|
|
46
|
+
access_token: 'exchange_token'
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
console.log = jest.fn;
|
|
50
|
+
await login({
|
|
51
|
+
store: 'developer.myshoplaza.com'
|
|
52
|
+
});
|
|
53
|
+
expect(get('access_token')).toBe('access_token');
|
|
54
|
+
expect(get('session_id')).toBe('session_id');
|
|
55
|
+
expect(get('user_id')).toBe('user_id');
|
|
56
|
+
expect(get('exchange_token')).toBe('exchange_token');
|
|
57
|
+
expect(get('store_domain')).toBe('developer.myshoplaza.com');
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('failed', async () => {
|
|
61
|
+
const mockExit = jest.spyOn(process, 'exit').mockImplementation((number) => {
|
|
62
|
+
throw new Error('process.exit: ' + number);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
mock.onPost(`${SSO_AUTH_URL}/api/oauth/token`).replyOnce(500, {});
|
|
66
|
+
await login({
|
|
67
|
+
store: 'developer-failed.myshoplaza.com'
|
|
68
|
+
});
|
|
69
|
+
expect(mockExit).toHaveBeenCalledWith(-1);
|
|
70
|
+
|
|
71
|
+
expect(get('access_token')).toBe(null);
|
|
72
|
+
expect(get('session_id')).toBe(null);
|
|
73
|
+
expect(get('user_id')).toBe(null);
|
|
74
|
+
expect(get('exchange_token')).toBe(null);
|
|
75
|
+
expect(get('store_domain')).toBe(null);
|
|
76
|
+
});
|
|
77
|
+
});
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
const logout = require('../logout');
|
|
2
|
+
const { get, set, empty } = require('../../db/user');
|
|
3
|
+
|
|
4
|
+
jest.mock('../../db');
|
|
5
|
+
|
|
6
|
+
describe('logout', () => {
|
|
7
|
+
beforeEach(() => {
|
|
8
|
+
empty();
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
it('success', async () => {
|
|
12
|
+
set({
|
|
13
|
+
user_id: 'user_id',
|
|
14
|
+
session_id: 'session_id',
|
|
15
|
+
access_token: 'access_token',
|
|
16
|
+
exchange_token: 'exchange_token'
|
|
17
|
+
});
|
|
18
|
+
expect(get('access_token')).toBe('access_token');
|
|
19
|
+
expect(get('session_id')).toBe('session_id');
|
|
20
|
+
expect(get('user_id')).toBe('user_id');
|
|
21
|
+
expect(get('exchange_token')).toBe('exchange_token');
|
|
22
|
+
console.log = jest.fn;
|
|
23
|
+
logout();
|
|
24
|
+
expect(get('access_token')).toBe(null);
|
|
25
|
+
expect(get('session_id')).toBe(null);
|
|
26
|
+
expect(get('user_id')).toBe(null);
|
|
27
|
+
expect(get('exchange_token')).toBe(null);
|
|
28
|
+
});
|
|
29
|
+
});
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
const store = require('../store');
|
|
2
|
+
const chalk = require('chalk');
|
|
3
|
+
const { set, empty } = require('../../db/user');
|
|
4
|
+
const MockAdapter = require('axios-mock-adapter');
|
|
5
|
+
const openAPI = require('../../openAPI');
|
|
6
|
+
|
|
7
|
+
jest.mock('../../db');
|
|
8
|
+
jest.mock('../../openAPI');
|
|
9
|
+
|
|
10
|
+
describe('store', () => {
|
|
11
|
+
let mock;
|
|
12
|
+
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
set({
|
|
15
|
+
store_domain: 'developer.myshoplaza.com'
|
|
16
|
+
});
|
|
17
|
+
mock = new MockAdapter(openAPI);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
afterEach(() => {
|
|
21
|
+
mock.reset();
|
|
22
|
+
empty();
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('success', async () => {
|
|
26
|
+
mock.onGet(`https://developer.myshoplaza.com/openapi/2020-07/shop`).replyOnce(200, {
|
|
27
|
+
shop: {
|
|
28
|
+
domain: 'developer.myshoplaza.com'
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
console.log = jest.fn();
|
|
32
|
+
await store();
|
|
33
|
+
expect(console.log.mock.calls[0][0]).toBe(
|
|
34
|
+
`You're currently logged into ${chalk.green('developer.myshoplaza.com')}`
|
|
35
|
+
);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('failed', async () => {
|
|
39
|
+
mock.onGet(`https://developer.myshoplaza.com/openapi/2020-07/shop`).replyOnce(500);
|
|
40
|
+
console.log = jest.fn();
|
|
41
|
+
await store();
|
|
42
|
+
expect(console.log.mock.calls[0][0]).toBe(chalk.red(`✗ Failed to get store`));
|
|
43
|
+
});
|
|
44
|
+
});
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
const axios = require('axios');
|
|
2
|
+
const chalk = require('chalk');
|
|
3
|
+
const switchCommand = require('../switch');
|
|
4
|
+
const { get, set, empty } = require('../../db/user');
|
|
5
|
+
const MockAdapter = require('axios-mock-adapter');
|
|
6
|
+
const { ACCOUNT_URL } = require('../../config');
|
|
7
|
+
|
|
8
|
+
jest.mock('../../db');
|
|
9
|
+
|
|
10
|
+
describe('switch', () => {
|
|
11
|
+
let mock;
|
|
12
|
+
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
mock = new MockAdapter(axios);
|
|
15
|
+
empty();
|
|
16
|
+
set({ access_token: 'access_token' });
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
afterEach(() => {
|
|
20
|
+
mock.reset();
|
|
21
|
+
empty();
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('switch success', async () => {
|
|
25
|
+
mock.onPost(`${ACCOUNT_URL}/api/accounts/store/token`).replyOnce(200, {
|
|
26
|
+
access_token: 'exchange_token'
|
|
27
|
+
});
|
|
28
|
+
console.log = jest.fn;
|
|
29
|
+
await switchCommand({
|
|
30
|
+
store: 'developer.myshoplaza.com'
|
|
31
|
+
});
|
|
32
|
+
expect(get('access_token')).toBe('access_token');
|
|
33
|
+
expect(get('exchange_token')).toBe('exchange_token');
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('switch failed', async () => {
|
|
37
|
+
mock.onPost(`${ACCOUNT_URL}/api/accounts/store/token`).replyOnce(500, {});
|
|
38
|
+
console.log = jest.fn;
|
|
39
|
+
await switchCommand({
|
|
40
|
+
store: 'developer-failed.myshoplaza.com'
|
|
41
|
+
});
|
|
42
|
+
expect(get('access_token')).toBe(null);
|
|
43
|
+
expect(get('exchange_token')).toBe(null);
|
|
44
|
+
});
|
|
45
|
+
});
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
const chalk = require('chalk');
|
|
2
|
+
const inquirer = require('inquirer');
|
|
3
|
+
const Sentry = require('@sentry/node');
|
|
4
|
+
const ora = require('ora');
|
|
5
|
+
const { get } = require('../db/user');
|
|
6
|
+
const { hasBeenSetAnalytics, setAnalyticsConfig } = require('../db/analytics');
|
|
7
|
+
const log = require('../log');
|
|
8
|
+
const { getShopDetail } = require('../openAPI/api');
|
|
9
|
+
const getCode = require('../auth/getCode');
|
|
10
|
+
const { postAccessToken, getUserInfo, postExchangeToken } = require('../auth');
|
|
11
|
+
let spinner;
|
|
12
|
+
|
|
13
|
+
const checkAndLogin = async (store) => {
|
|
14
|
+
if (get('exchange_token')) {
|
|
15
|
+
if (get('store_domain') === store) {
|
|
16
|
+
try {
|
|
17
|
+
await getShopDetail({ ignoreLogError: true });
|
|
18
|
+
log.info(`${chalk.green('✓')} Already logged in to ${chalk.green(store)}`);
|
|
19
|
+
return;
|
|
20
|
+
} catch (err) {
|
|
21
|
+
// Ignore
|
|
22
|
+
}
|
|
23
|
+
} else {
|
|
24
|
+
try {
|
|
25
|
+
await postExchangeToken(store, { ignoreLogError: true });
|
|
26
|
+
log.info(`Logged into ${chalk.green(get('store_domain'))}`);
|
|
27
|
+
return;
|
|
28
|
+
} catch (err) {
|
|
29
|
+
// Ignore
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const code = await getCode();
|
|
35
|
+
spinner = ora(`Logging in to ${chalk.green(store)}`).start();
|
|
36
|
+
await postAccessToken(code);
|
|
37
|
+
await getUserInfo();
|
|
38
|
+
await postExchangeToken(store);
|
|
39
|
+
spinner?.stop?.();
|
|
40
|
+
log.info(`${chalk.green('✓')} Finalizing authentication`);
|
|
41
|
+
log.info(`Logged into ${chalk.green(get('store_domain'))}`);
|
|
42
|
+
requestAnalyticsIfNeeded();
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const requestAnalyticsIfNeeded = async () => {
|
|
46
|
+
const user_id = get('user_id');
|
|
47
|
+
if (hasBeenSetAnalytics(user_id)) return;
|
|
48
|
+
try {
|
|
49
|
+
const answers = await inquirer.prompt([
|
|
50
|
+
{
|
|
51
|
+
name: 'confirm',
|
|
52
|
+
type: 'list',
|
|
53
|
+
message: `Are you sure you want to enable usage reporting?`,
|
|
54
|
+
choices: ['Yes', 'No']
|
|
55
|
+
}
|
|
56
|
+
]);
|
|
57
|
+
setAnalyticsConfig({
|
|
58
|
+
user_id,
|
|
59
|
+
enabled: answers.confirm === 'Yes' ? 1 : 0
|
|
60
|
+
});
|
|
61
|
+
} catch (error) {
|
|
62
|
+
log.error(chalk.red('✗ Failed to authenticate'));
|
|
63
|
+
Sentry.captureException(err);
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
exports.login = async (options) => {
|
|
68
|
+
try {
|
|
69
|
+
if (options.store) {
|
|
70
|
+
await checkAndLogin(options.store);
|
|
71
|
+
} else {
|
|
72
|
+
const answers = await inquirer.prompt([
|
|
73
|
+
{
|
|
74
|
+
name: 'store',
|
|
75
|
+
type: 'input',
|
|
76
|
+
message: 'The store domain (Eg: developer.myshoplaza.com)'
|
|
77
|
+
}
|
|
78
|
+
]);
|
|
79
|
+
if (answers.store) {
|
|
80
|
+
if (!/.+\.myshoplaza\.com/.test(answers.store)) {
|
|
81
|
+
log.error(
|
|
82
|
+
chalk.red(
|
|
83
|
+
`✗ Invalid store provided ${answers.store}. Please provide the store in the following format: developer.myshoplaza.com`
|
|
84
|
+
)
|
|
85
|
+
);
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
await checkAndLogin(answers.store);
|
|
89
|
+
} else {
|
|
90
|
+
log.error(chalk.red(`✗ Please input the store domain.`));
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
} catch (error) {
|
|
95
|
+
spinner?.stop?.();
|
|
96
|
+
log.error(chalk.red(`✗ Failed to authenticate`));
|
|
97
|
+
Sentry.captureException(error);
|
|
98
|
+
}
|
|
99
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
const Sentry = require('@sentry/node');
|
|
2
|
+
const chalk = require('chalk');
|
|
3
|
+
const { empty } = require('../db/user');
|
|
4
|
+
const log = require('../log');
|
|
5
|
+
|
|
6
|
+
module.exports = () => {
|
|
7
|
+
try {
|
|
8
|
+
empty();
|
|
9
|
+
log.info('Successfully logged out of your account');
|
|
10
|
+
} catch (error) {
|
|
11
|
+
log.error(chalk.red(`✗ Failed to logout`));
|
|
12
|
+
Sentry.captureException(error);
|
|
13
|
+
}
|
|
14
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
const chalk = require('chalk');
|
|
2
|
+
const Sentry = require('@sentry/node');
|
|
3
|
+
const { getShopDetail } = require('../openAPI/api');
|
|
4
|
+
const log = require('../log');
|
|
5
|
+
|
|
6
|
+
module.exports = async () => {
|
|
7
|
+
try {
|
|
8
|
+
const { data } = await getShopDetail();
|
|
9
|
+
log.info(`You're currently logged into ${chalk.green(data.shop.domain)}`);
|
|
10
|
+
} catch (error) {
|
|
11
|
+
log.error(chalk.red(`✗ Failed to get store`));
|
|
12
|
+
Sentry.captureException(error);
|
|
13
|
+
}
|
|
14
|
+
};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
const chalk = require('chalk');
|
|
2
|
+
const inquirer = require('inquirer');
|
|
3
|
+
const Sentry = require('@sentry/node');
|
|
4
|
+
const { get } = require('../db/user');
|
|
5
|
+
const log = require('../log');
|
|
6
|
+
const { postExchangeToken } = require('../auth');
|
|
7
|
+
|
|
8
|
+
const switchStore = async (store) => {
|
|
9
|
+
if (!get('access_token')) {
|
|
10
|
+
log.error(chalk.red(`✗ Please login again with ${chalk.cyan('shoplazza login')}`));
|
|
11
|
+
} else {
|
|
12
|
+
try {
|
|
13
|
+
await postExchangeToken(store);
|
|
14
|
+
log.info(`Switched store to ${chalk.green(store)}`);
|
|
15
|
+
} catch (error) {
|
|
16
|
+
log.error(chalk.red(`✗ Failed to switch store`));
|
|
17
|
+
Sentry.captureException(error);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
module.exports = async (options) => {
|
|
23
|
+
try {
|
|
24
|
+
if (options.store) {
|
|
25
|
+
return switchStore(options.store);
|
|
26
|
+
} else {
|
|
27
|
+
const answers = await inquirer.prompt([
|
|
28
|
+
{
|
|
29
|
+
name: 'store',
|
|
30
|
+
type: 'input',
|
|
31
|
+
message: 'The store domain (Eg: developer.myshoplaza.com )'
|
|
32
|
+
}
|
|
33
|
+
]);
|
|
34
|
+
if (answers.store) {
|
|
35
|
+
if (!/.+\.myshoplaza\.com/.test(answers.store)) {
|
|
36
|
+
log.error(
|
|
37
|
+
chalk.red(
|
|
38
|
+
`✗ Invalid store provided ${answers.store}. Please provide the store in the following format: developer.myshoplaza.com`
|
|
39
|
+
)
|
|
40
|
+
);
|
|
41
|
+
process.exit(-1);
|
|
42
|
+
}
|
|
43
|
+
return switchStore(answers.store);
|
|
44
|
+
} else {
|
|
45
|
+
log.error(chalk.red(`✗ Please input the store domain.`));
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
} catch (error) {
|
|
49
|
+
log.error(chalk.red(`✗ Failed to switch store`));
|
|
50
|
+
Sentry.captureException(error);
|
|
51
|
+
}
|
|
52
|
+
};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
const MockAdapter = require('axios-mock-adapter');
|
|
2
|
+
const chalk = require('chalk');
|
|
3
|
+
const deleteCommand = require('../delete');
|
|
4
|
+
const openAPI = require('../../../openAPI');
|
|
5
|
+
const { set, empty } = require('../../../db/user');
|
|
6
|
+
|
|
7
|
+
jest.mock('inquirer', () => ({
|
|
8
|
+
prompt: () => Promise.resolve({ confirm: 'Yes' })
|
|
9
|
+
}));
|
|
10
|
+
jest.mock('../../../db');
|
|
11
|
+
jest.mock('../../../openAPI/index');
|
|
12
|
+
|
|
13
|
+
describe('delete theme', () => {
|
|
14
|
+
let mock;
|
|
15
|
+
|
|
16
|
+
beforeEach(() => {
|
|
17
|
+
set({
|
|
18
|
+
store_domain: 'developer.myshoplaza.com'
|
|
19
|
+
});
|
|
20
|
+
mock = new MockAdapter(openAPI);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
afterEach(() => {
|
|
24
|
+
mock.reset();
|
|
25
|
+
empty();
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('delete success', async () => {
|
|
29
|
+
mock.onGet(`https://developer.myshoplaza.com/openapi/2020-07/themes/theme_id`).replyOnce(200, {
|
|
30
|
+
data: { name: 'test' }
|
|
31
|
+
});
|
|
32
|
+
mock.onDelete(`https://developer.myshoplaza.com/openapi/2020-07/themes/theme_id`).replyOnce(200);
|
|
33
|
+
|
|
34
|
+
console.log = jest.fn();
|
|
35
|
+
await deleteCommand({ theme: 'theme_id' });
|
|
36
|
+
expect(console.log.mock.calls[0][0]).toBe(chalk.green(`✓ test (theme_id) theme deleted`));
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('delete failed', async () => {
|
|
40
|
+
mock.onGet(`https://developer.myshoplaza.com/openapi/2020-07/themes/theme_id`).replyOnce(200, {
|
|
41
|
+
data: { name: 'test' }
|
|
42
|
+
});
|
|
43
|
+
mock.onDelete(`https://developer.myshoplaza.com/openapi/2020-07/themes/theme_id`).replyOnce(500);
|
|
44
|
+
|
|
45
|
+
console.log = jest.fn();
|
|
46
|
+
await deleteCommand({ theme: 'theme_id' });
|
|
47
|
+
expect(console.log.mock.calls[0][0]).toBe(chalk.red(`✗ Failed to delete theme`));
|
|
48
|
+
});
|
|
49
|
+
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
const fs = require('fs-extra');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const init = require('../init');
|
|
4
|
+
const execa = require('execa');
|
|
5
|
+
|
|
6
|
+
jest.mock('execa', () => jest.fn());
|
|
7
|
+
|
|
8
|
+
describe('theme init', () => {
|
|
9
|
+
it('clone repo', async () => {
|
|
10
|
+
jest.setTimeout(20 * 1000);
|
|
11
|
+
if (fs.existsSync(path.join(process.cwd(), 'test'))) {
|
|
12
|
+
fs.rmSync(path.join(process.cwd(), 'test'), { recursive: true });
|
|
13
|
+
}
|
|
14
|
+
expect(fs.existsSync(path.join(process.cwd(), 'test'))).toBe(false);
|
|
15
|
+
console.log = jest.fn;
|
|
16
|
+
await init({ name: 'test' });
|
|
17
|
+
expect(execa).toHaveBeenCalledWith('git', ['clone', 'https://github.com/Shoplazza/bing', 'test']);
|
|
18
|
+
expect(fs.existsSync(path.join(process.cwd(), 'test'))).toBe(true);
|
|
19
|
+
fs.rmSync(path.join(process.cwd(), 'test'), { recursive: true });
|
|
20
|
+
});
|
|
21
|
+
});
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
const chalk = require('chalk');
|
|
2
|
+
const MockAdapter = require('axios-mock-adapter');
|
|
3
|
+
const listCommand = require('../list');
|
|
4
|
+
const openAPI = require('../../../openAPI');
|
|
5
|
+
const { set, empty } = require('../../../db/user');
|
|
6
|
+
|
|
7
|
+
jest.mock('ora', () => () => ({
|
|
8
|
+
start: () => ({
|
|
9
|
+
stop: () => {}
|
|
10
|
+
})
|
|
11
|
+
}));
|
|
12
|
+
jest.mock('../../../db');
|
|
13
|
+
jest.mock('../../../openAPI/index');
|
|
14
|
+
|
|
15
|
+
describe('theme list', () => {
|
|
16
|
+
let mock;
|
|
17
|
+
|
|
18
|
+
beforeEach(() => {
|
|
19
|
+
set({
|
|
20
|
+
store_domain: 'developer.myshoplaza.com'
|
|
21
|
+
});
|
|
22
|
+
mock = new MockAdapter(openAPI);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
afterEach(() => {
|
|
26
|
+
mock.reset();
|
|
27
|
+
empty();
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('list info', async () => {
|
|
31
|
+
mock.onGet(`https://developer.myshoplaza.com/openapi/2020-07/themes`).replyOnce(200, {
|
|
32
|
+
data: {
|
|
33
|
+
themes: [
|
|
34
|
+
{
|
|
35
|
+
id: 'theme_id1',
|
|
36
|
+
name: 'theme1'
|
|
37
|
+
}
|
|
38
|
+
]
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
mock.onGet(`https://developer.myshoplaza.com/openapi/2020-07/themes/default-theme`).replyOnce(200, {
|
|
42
|
+
data: {
|
|
43
|
+
id: 'theme_id',
|
|
44
|
+
name: 'default_theme'
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
console.log = jest.fn();
|
|
49
|
+
await listCommand();
|
|
50
|
+
expect(console.log.mock.calls[0][0]).toBe(
|
|
51
|
+
`${chalk.yellow('⭑')} List of ${chalk.green('developer.myshoplaza.com')} themes:`
|
|
52
|
+
);
|
|
53
|
+
expect(console.log.mock.calls[1][0]).toBe(
|
|
54
|
+
[
|
|
55
|
+
{
|
|
56
|
+
id: 'theme_id',
|
|
57
|
+
name: 'default_theme'
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
id: 'theme_id1',
|
|
61
|
+
name: 'theme1'
|
|
62
|
+
}
|
|
63
|
+
].reduce(
|
|
64
|
+
(acc, theme) =>
|
|
65
|
+
(acc += `${theme.name} (${chalk.green(theme.id)}) ${
|
|
66
|
+
theme.id === 'theme_id' ? chalk.green('[live]') : chalk.yellow('[unpublished]')
|
|
67
|
+
}\n`),
|
|
68
|
+
''
|
|
69
|
+
)
|
|
70
|
+
);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('fetch failed', async () => {
|
|
74
|
+
mock.onGet(`https://developer.myshoplaza.com/openapi/2020-07/themes/default-theme`).replyOnce(500);
|
|
75
|
+
|
|
76
|
+
console.log = jest.fn();
|
|
77
|
+
await listCommand();
|
|
78
|
+
expect(console.log.mock.calls[0][0]).toBe(chalk.red(`✗ Failed to get theme list`));
|
|
79
|
+
});
|
|
80
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
const chalk = require('chalk');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const fs = require('fs-extra');
|
|
4
|
+
const { packageCommand } = require('../package');
|
|
5
|
+
const { fstat } = require('fs');
|
|
6
|
+
|
|
7
|
+
describe('theme package', () => {
|
|
8
|
+
it('zip theme', async () => {
|
|
9
|
+
process.chdir(path.join(process.cwd(), '/fixtures'));
|
|
10
|
+
console.log = jest.fn();
|
|
11
|
+
await packageCommand();
|
|
12
|
+
expect(console.log.mock.calls[0][0]).toBe(`${chalk.green('✓')} Theme packaged in TestLifeStyle-1.0.zip`);
|
|
13
|
+
expect(fs.existsSync(path.join(process.cwd(), 'TestLifeStyle-1.0.zip'))).toBe(true);
|
|
14
|
+
fs.rmSync(path.join(process.cwd(), 'TestLifeStyle-1.0.zip'));
|
|
15
|
+
process.chdir(path.join(process.cwd(), '../'));
|
|
16
|
+
});
|
|
17
|
+
});
|