@commercetools-frontend/mc-scripts 21.0.1 → 21.3.0
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/bin/cli.js +32 -3
- package/build/commands/config:sync.js +171 -0
- package/build/commands/login.js +60 -0
- package/build/utils/auth.js +34 -0
- package/build/utils/auth.spec.js +61 -0
- package/build/utils/create-custom-application.settings.graphql +8 -0
- package/build/utils/credentials-storage.js +72 -0
- package/build/utils/credentials-storage.spec.js +69 -0
- package/build/utils/fetch-custom-application.settings.graphql +35 -0
- package/build/utils/fetch-user-organizations.core.graphql +9 -0
- package/build/utils/graphql-requests.js +105 -0
- package/build/utils/graphql-requests.spec.js +159 -0
- package/build/utils/require-graphql.js +15 -0
- package/build/utils/update-application-id-in-custom-application-config.js +60 -0
- package/build/utils/update-custom-application.settings.graphql +13 -0
- package/build/utils/user-agent.js +15 -0
- package/package.json +33 -24
package/build/bin/cli.js
CHANGED
|
@@ -19,6 +19,8 @@ const dotenvExpand = require('dotenv-expand');
|
|
|
19
19
|
|
|
20
20
|
const spawn = require('react-dev-utils/crossSpawn');
|
|
21
21
|
|
|
22
|
+
const pkg = require('../../package.json');
|
|
23
|
+
|
|
22
24
|
const flags = mri(process.argv.slice(2), {
|
|
23
25
|
alias: {
|
|
24
26
|
help: ['h']
|
|
@@ -50,11 +52,18 @@ Commands:
|
|
|
50
52
|
|
|
51
53
|
serve Serves previously built and compiled application from the "public" folder.
|
|
52
54
|
|
|
55
|
+
login Log in to your Merchant Center account through the CLI, using the cloud environment information from the Custom Application config file. An API token is generated and stored in a configuration file for the related cloud environment, and valid for 36 hours.
|
|
56
|
+
|
|
57
|
+
config:sync Synchronizes the local Custom Application config with the Merchant Center. A new Custom Application will be created if none existed, otherwise it will be updated.
|
|
58
|
+
--dry-run (optional) Executes the command but does not send any mutation request.
|
|
53
59
|
`);
|
|
54
60
|
process.exit(0);
|
|
55
61
|
}
|
|
56
62
|
|
|
57
|
-
const command = commands[0];
|
|
63
|
+
const command = commands[0];
|
|
64
|
+
console.log('');
|
|
65
|
+
console.log(`mc-scripts: v${pkg.version}`);
|
|
66
|
+
console.log(''); // Get the current directory where the CLI is executed from. Usually this is the application folder.
|
|
58
67
|
|
|
59
68
|
const applicationDirectory = fs.realpathSync(process.cwd());
|
|
60
69
|
|
|
@@ -91,7 +100,7 @@ const applicationDirectory = fs.realpathSync(process.cwd());
|
|
|
91
100
|
// Do this as the first thing so that any code reading it knows the right env.
|
|
92
101
|
process.env.NODE_ENV = 'production'; // Get specific flag for this command.
|
|
93
102
|
|
|
94
|
-
const commandArgs = getArgsForCommand(['transformer']);
|
|
103
|
+
const commandArgs = getArgsForCommand(['transformer', 'print-security-headers']);
|
|
95
104
|
proxyCommand(command, {
|
|
96
105
|
commandArgs
|
|
97
106
|
});
|
|
@@ -107,6 +116,26 @@ const applicationDirectory = fs.realpathSync(process.cwd());
|
|
|
107
116
|
break;
|
|
108
117
|
}
|
|
109
118
|
|
|
119
|
+
case 'login':
|
|
120
|
+
{
|
|
121
|
+
// Do this as the first thing so that any code reading it knows the right env.
|
|
122
|
+
process.env.NODE_ENV = 'production';
|
|
123
|
+
proxyCommand(command);
|
|
124
|
+
break;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
case 'config:sync':
|
|
128
|
+
{
|
|
129
|
+
// Do this as the first thing so that any code reading it knows the right env.
|
|
130
|
+
process.env.NODE_ENV = 'production'; // Get specific flag for this command.
|
|
131
|
+
|
|
132
|
+
const commandArgs = getArgsForCommand(['dry-run']);
|
|
133
|
+
proxyCommand(command, {
|
|
134
|
+
commandArgs
|
|
135
|
+
});
|
|
136
|
+
break;
|
|
137
|
+
}
|
|
138
|
+
|
|
110
139
|
default:
|
|
111
140
|
console.log(`Unknown script "${command}".`);
|
|
112
141
|
console.log('Perhaps you need to update mc-scripts?');
|
|
@@ -199,7 +228,7 @@ function loadDotEnvFiles(flags) {
|
|
|
199
228
|
const dotenvFilePath = path.resolve(path.join(applicationDirectory, dotenvFile));
|
|
200
229
|
|
|
201
230
|
if (fs.existsSync(dotenvFilePath)) {
|
|
202
|
-
dotenvExpand(dotenv.config({
|
|
231
|
+
dotenvExpand.expand(dotenv.config({
|
|
203
232
|
path: dotenvFilePath
|
|
204
233
|
}));
|
|
205
234
|
}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const omit = require('lodash/omit');
|
|
4
|
+
|
|
5
|
+
const prompts = require('prompts');
|
|
6
|
+
|
|
7
|
+
const mri = require('mri');
|
|
8
|
+
|
|
9
|
+
const chalk = require('chalk');
|
|
10
|
+
|
|
11
|
+
const {
|
|
12
|
+
processConfig
|
|
13
|
+
} = require('@commercetools-frontend/application-config');
|
|
14
|
+
|
|
15
|
+
const CredentialsStorage = require('../utils/credentials-storage');
|
|
16
|
+
|
|
17
|
+
const {
|
|
18
|
+
fetchCustomApplication,
|
|
19
|
+
updateCustomApplication,
|
|
20
|
+
createCustomApplication,
|
|
21
|
+
fetchUserOrganizations
|
|
22
|
+
} = require('../utils/graphql-requests');
|
|
23
|
+
|
|
24
|
+
const updateApplicationIdInCustomApplicationConfig = require('../utils/update-application-id-in-custom-application-config');
|
|
25
|
+
|
|
26
|
+
const flags = mri(process.argv.slice(2), {
|
|
27
|
+
boolean: ['dry-run']
|
|
28
|
+
});
|
|
29
|
+
const credentialsStorage = new CredentialsStorage();
|
|
30
|
+
|
|
31
|
+
const getMcUrlLink = (mcApiUrl, organizationId, applicationId) => {
|
|
32
|
+
const mcUrl = mcApiUrl.replace('mc-api', 'mc');
|
|
33
|
+
const customAppLink = `${mcUrl}/account/organizations/${organizationId}/custom-applications/owned/${applicationId}`;
|
|
34
|
+
return customAppLink;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const configSync = async () => {
|
|
38
|
+
const applicationConfig = processConfig();
|
|
39
|
+
const {
|
|
40
|
+
data: localCustomAppData
|
|
41
|
+
} = applicationConfig;
|
|
42
|
+
const {
|
|
43
|
+
mcApiUrl
|
|
44
|
+
} = applicationConfig.env;
|
|
45
|
+
|
|
46
|
+
if (!credentialsStorage.isSessionValid(mcApiUrl)) {
|
|
47
|
+
throw new Error(`You don't have a valid session for the ${mcApiUrl} environment. Please, run the "mc-scripts login" command to authenticate yourself.`);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const token = credentialsStorage.getToken(mcApiUrl);
|
|
51
|
+
const fetchedCustomApplication = await fetchCustomApplication({
|
|
52
|
+
mcApiUrl,
|
|
53
|
+
token,
|
|
54
|
+
entryPointUriPath: localCustomAppData.entryPointUriPath
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
if (!fetchedCustomApplication) {
|
|
58
|
+
const userOrganizations = await fetchUserOrganizations({
|
|
59
|
+
mcApiUrl,
|
|
60
|
+
token
|
|
61
|
+
});
|
|
62
|
+
let organizationId, organizationName;
|
|
63
|
+
|
|
64
|
+
if (userOrganizations.total === 0) {
|
|
65
|
+
throw new Error(`It seems you are not an admin of any Organization. Please make sure to be part of the Administrators team of the Organization you want the Custom Application to be configured to.`);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (userOrganizations.total === 1) {
|
|
69
|
+
const [organization] = userOrganizations.results;
|
|
70
|
+
organizationId = organization.id;
|
|
71
|
+
organizationName = organization.name;
|
|
72
|
+
} else {
|
|
73
|
+
const organizationChoices = userOrganizations.results.map(organization => ({
|
|
74
|
+
title: organization.name,
|
|
75
|
+
value: organization.id
|
|
76
|
+
}));
|
|
77
|
+
const {
|
|
78
|
+
organizationId: selectedOrganizationId
|
|
79
|
+
} = await prompts({
|
|
80
|
+
type: 'select',
|
|
81
|
+
name: 'organizationId',
|
|
82
|
+
message: 'Select Organization',
|
|
83
|
+
choices: organizationChoices,
|
|
84
|
+
initial: 0
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
if (!selectedOrganizationId) {
|
|
88
|
+
throw new Error(`No Organization selected, aborting.`);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
organizationId = selectedOrganizationId;
|
|
92
|
+
organizationName = organizationChoices.find(({
|
|
93
|
+
value
|
|
94
|
+
}) => value === organizationId).title;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const {
|
|
98
|
+
confirmation
|
|
99
|
+
} = await prompts({
|
|
100
|
+
type: 'text',
|
|
101
|
+
name: 'confirmation',
|
|
102
|
+
message: `You are about to create a new Custom Application in the "${organizationName}" organization for the ${mcApiUrl} environment. Are you sure you want to proceed?`,
|
|
103
|
+
initial: 'yes'
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
if (!confirmation || confirmation.toLowerCase().charAt(0) !== 'y') {
|
|
107
|
+
console.log(chalk.red('Aborted.'));
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const data = omit(localCustomAppData, ['id']);
|
|
112
|
+
|
|
113
|
+
if (flags['dry-run']) {
|
|
114
|
+
console.log(chalk.gray('DRY RUN mode'));
|
|
115
|
+
console.log(`A new Custom Application would be created for the Organization ${organizationName} with the following payload:`);
|
|
116
|
+
console.log(JSON.stringify(data));
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const createdCustomApplication = await createCustomApplication({
|
|
121
|
+
mcApiUrl,
|
|
122
|
+
token,
|
|
123
|
+
organizationId,
|
|
124
|
+
data
|
|
125
|
+
}); // update applicationID in the custom-application-config file
|
|
126
|
+
|
|
127
|
+
updateApplicationIdInCustomApplicationConfig(createdCustomApplication.id);
|
|
128
|
+
const customAppLink = getMcUrlLink(mcApiUrl, organizationId, createdCustomApplication.id);
|
|
129
|
+
console.log(chalk.green(`Custom Application created.\nThe "applicationId" in your local Custom Application config file has been updated with the application ID.\nYou can see the Custom Application data in the Merchant Center at ${customAppLink}.`));
|
|
130
|
+
return;
|
|
131
|
+
} // TODO: show diff (followup task)
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
const {
|
|
135
|
+
confirmation
|
|
136
|
+
} = await prompts({
|
|
137
|
+
type: 'text',
|
|
138
|
+
name: 'confirmation',
|
|
139
|
+
message: `You are about to update the Custom Application "${localCustomAppData.entryPointUriPath}" in the ${mcApiUrl} environment. Are you sure you want to proceed?`,
|
|
140
|
+
initial: 'yes'
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
if (!confirmation || confirmation.toLowerCase().charAt(0) !== 'y') {
|
|
144
|
+
console.log(chalk.red('Aborted.'));
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const data = omit(localCustomAppData, ['id']);
|
|
149
|
+
|
|
150
|
+
if (flags['dry-run']) {
|
|
151
|
+
console.log(chalk.gray('DRY RUN mode'));
|
|
152
|
+
console.log(`The Custom Application ${data.name} would be updated with the following payload:`);
|
|
153
|
+
console.log(JSON.stringify(data));
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
await updateCustomApplication({
|
|
158
|
+
mcApiUrl,
|
|
159
|
+
token,
|
|
160
|
+
organizationId: fetchedCustomApplication.organizationId,
|
|
161
|
+
data: omit(localCustomAppData, ['id']),
|
|
162
|
+
applicationId: fetchedCustomApplication.application.id
|
|
163
|
+
});
|
|
164
|
+
const customAppLink = getMcUrlLink(mcApiUrl, fetchedCustomApplication.organizationId, fetchedCustomApplication.application.id);
|
|
165
|
+
console.log(chalk.green(`Custom Application updated.\nYou can see the Custom Application data in the Merchant Center at ${customAppLink}.`));
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
configSync().catch(error => {
|
|
169
|
+
console.log(chalk.red(error));
|
|
170
|
+
process.exit(1);
|
|
171
|
+
});
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const prompts = require('prompts');
|
|
4
|
+
|
|
5
|
+
const chalk = require('chalk');
|
|
6
|
+
|
|
7
|
+
const {
|
|
8
|
+
processConfig
|
|
9
|
+
} = require('@commercetools-frontend/application-config');
|
|
10
|
+
|
|
11
|
+
const CredentialsStorage = require('../utils/credentials-storage');
|
|
12
|
+
|
|
13
|
+
const {
|
|
14
|
+
getAuthToken
|
|
15
|
+
} = require('../utils/auth');
|
|
16
|
+
|
|
17
|
+
const credentialsStorage = new CredentialsStorage();
|
|
18
|
+
|
|
19
|
+
const login = async () => {
|
|
20
|
+
const applicationConfig = processConfig();
|
|
21
|
+
const {
|
|
22
|
+
mcApiUrl
|
|
23
|
+
} = applicationConfig.env;
|
|
24
|
+
|
|
25
|
+
if (credentialsStorage.isSessionValid(mcApiUrl)) {
|
|
26
|
+
console.log(`You already have a valid session for the ${mcApiUrl} environment.\n`);
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const {
|
|
31
|
+
email
|
|
32
|
+
} = await prompts({
|
|
33
|
+
type: 'text',
|
|
34
|
+
name: 'email',
|
|
35
|
+
message: 'Email'
|
|
36
|
+
});
|
|
37
|
+
const {
|
|
38
|
+
password
|
|
39
|
+
} = await prompts({
|
|
40
|
+
type: 'invisible',
|
|
41
|
+
name: 'password',
|
|
42
|
+
message: 'Password (hidden)'
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
if (!email || !password) {
|
|
46
|
+
throw new Error(`Missing email or password values. Aborting.`);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const credentials = await getAuthToken(mcApiUrl, {
|
|
50
|
+
email,
|
|
51
|
+
password
|
|
52
|
+
});
|
|
53
|
+
credentialsStorage.setToken(mcApiUrl, credentials);
|
|
54
|
+
console.log(chalk.green(`Login successful for the ${mcApiUrl} environment.\n`));
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
login().catch(error => {
|
|
58
|
+
console.log(chalk.red(error));
|
|
59
|
+
process.exit(1);
|
|
60
|
+
});
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const fetch = require('node-fetch');
|
|
4
|
+
|
|
5
|
+
const userAgent = require('./user-agent');
|
|
6
|
+
|
|
7
|
+
const getAuthToken = async (mcApiUrl, payload) => {
|
|
8
|
+
const response = await fetch(`${mcApiUrl}/tokens/cli`, {
|
|
9
|
+
method: 'POST',
|
|
10
|
+
headers: {
|
|
11
|
+
Accept: 'application/json',
|
|
12
|
+
'Content-Type': 'application/json',
|
|
13
|
+
'x-user-agent': userAgent
|
|
14
|
+
},
|
|
15
|
+
body: JSON.stringify(payload)
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
if (!response.ok) {
|
|
19
|
+
const text = await response.text();
|
|
20
|
+
let parsed;
|
|
21
|
+
|
|
22
|
+
try {
|
|
23
|
+
parsed = JSON.parse(text);
|
|
24
|
+
} catch (error) {}
|
|
25
|
+
|
|
26
|
+
const errorMessage = parsed ? parsed.message : text;
|
|
27
|
+
throw new Error(errorMessage);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const authToken = await response.json();
|
|
31
|
+
return authToken;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
exports.getAuthToken = getAuthToken;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const {
|
|
4
|
+
rest
|
|
5
|
+
} = require('msw');
|
|
6
|
+
|
|
7
|
+
const {
|
|
8
|
+
setupServer
|
|
9
|
+
} = require('msw/node');
|
|
10
|
+
|
|
11
|
+
const {
|
|
12
|
+
getAuthToken
|
|
13
|
+
} = require('./auth');
|
|
14
|
+
|
|
15
|
+
const mockServer = setupServer();
|
|
16
|
+
afterEach(() => {
|
|
17
|
+
mockServer.resetHandlers();
|
|
18
|
+
});
|
|
19
|
+
beforeAll(() => mockServer.listen({
|
|
20
|
+
onUnhandledRequest: 'error'
|
|
21
|
+
}));
|
|
22
|
+
afterAll(() => mockServer.close());
|
|
23
|
+
const mcApiUrl = 'https://mc-api.europe-west1.gcp.commercetools.com';
|
|
24
|
+
describe('when login details are correct', () => {
|
|
25
|
+
beforeEach(() => {
|
|
26
|
+
mockServer.use(rest.post(`${mcApiUrl}/tokens/cli`, (req, res, ctx) => {
|
|
27
|
+
return res(ctx.status(200), ctx.json({
|
|
28
|
+
token: 'hello-world',
|
|
29
|
+
expiresAt: Math.floor(Date.now() / 1000) + 60 * 60 * 36 // 1,5 days
|
|
30
|
+
|
|
31
|
+
}));
|
|
32
|
+
}));
|
|
33
|
+
});
|
|
34
|
+
it('should match returned credentials', async () => {
|
|
35
|
+
const sessionData = await getAuthToken(mcApiUrl, {
|
|
36
|
+
email: 'user@email.com',
|
|
37
|
+
password: 'secret'
|
|
38
|
+
});
|
|
39
|
+
expect(sessionData).toEqual({
|
|
40
|
+
token: 'hello-world',
|
|
41
|
+
expiresAt: expect.any(Number)
|
|
42
|
+
});
|
|
43
|
+
expect(sessionData.expiresAt).toBeGreaterThan(Math.floor(Date.now() / 1000));
|
|
44
|
+
expect(sessionData.expiresAt).toBeLessThanOrEqual(Math.floor(Date.now() / 1000) + 60 * 60 * 36);
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
describe('when login details are incorrect', () => {
|
|
48
|
+
beforeEach(() => {
|
|
49
|
+
mockServer.use(rest.post(`${mcApiUrl}/tokens/cli`, (req, res, ctx) => {
|
|
50
|
+
return res(ctx.status(400), ctx.json({
|
|
51
|
+
message: 'Invalid email or password'
|
|
52
|
+
}));
|
|
53
|
+
}));
|
|
54
|
+
});
|
|
55
|
+
it('should throw error', async () => {
|
|
56
|
+
await expect(async () => await getAuthToken(mcApiUrl, {
|
|
57
|
+
email: 'user@email.com',
|
|
58
|
+
password: 'secret'
|
|
59
|
+
})).rejects.toThrow('Invalid email or password');
|
|
60
|
+
});
|
|
61
|
+
});
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
|
|
5
|
+
const path = require('path');
|
|
6
|
+
|
|
7
|
+
const homedir = require('os').homedir();
|
|
8
|
+
|
|
9
|
+
const credentialsFolderPath = path.join(homedir, `.commercetools`);
|
|
10
|
+
const credentialsFilePath = path.join(credentialsFolderPath, 'mc-credentials.json');
|
|
11
|
+
|
|
12
|
+
class CredentialsStorage {
|
|
13
|
+
static location = credentialsFilePath;
|
|
14
|
+
|
|
15
|
+
constructor() {
|
|
16
|
+
// Ensure the credentials file is present
|
|
17
|
+
if (!fs.existsSync(credentialsFilePath)) {
|
|
18
|
+
fs.mkdirSync(credentialsFolderPath, {
|
|
19
|
+
recursive: true
|
|
20
|
+
}); // Initialize with an empty object
|
|
21
|
+
|
|
22
|
+
this._writeCredentials({});
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
_writeCredentials(credentials) {
|
|
27
|
+
fs.writeFileSync(credentialsFilePath, JSON.stringify(credentials, null, 2), {
|
|
28
|
+
encoding: 'utf8'
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
_loadCredentials() {
|
|
33
|
+
const data = fs.readFileSync(credentialsFilePath, {
|
|
34
|
+
encoding: 'utf8'
|
|
35
|
+
});
|
|
36
|
+
return JSON.parse(data);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
getToken(environmentKey) {
|
|
40
|
+
const allCredentials = this._loadCredentials();
|
|
41
|
+
|
|
42
|
+
if (!this.isSessionValid(environmentKey)) {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return allCredentials[environmentKey].token;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
setToken(environmentKey, credentials) {
|
|
50
|
+
const allCredentials = this._loadCredentials();
|
|
51
|
+
|
|
52
|
+
allCredentials[environmentKey] = credentials;
|
|
53
|
+
|
|
54
|
+
this._writeCredentials(allCredentials);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
isSessionValid(environmentKey) {
|
|
58
|
+
const allCredentials = this._loadCredentials();
|
|
59
|
+
|
|
60
|
+
const credentials = allCredentials[environmentKey];
|
|
61
|
+
|
|
62
|
+
if (!credentials) {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const now = Math.floor(Date.now() / 1000);
|
|
67
|
+
return now < credentials.expiresAt;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
module.exports = CredentialsStorage;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const mock = require('mock-fs');
|
|
4
|
+
|
|
5
|
+
const CredentialsStorage = require('./credentials-storage');
|
|
6
|
+
|
|
7
|
+
afterEach(() => {
|
|
8
|
+
mock.restore();
|
|
9
|
+
});
|
|
10
|
+
const mcApiUrl = 'https://mc-api.europe-west1.gcp.commercetools.com';
|
|
11
|
+
describe('when session is valid', () => {
|
|
12
|
+
let credentialsStorage;
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
mock({
|
|
15
|
+
[CredentialsStorage.location]: JSON.stringify({
|
|
16
|
+
[mcApiUrl]: {
|
|
17
|
+
token: 'hello-world',
|
|
18
|
+
expiresAt: Math.floor(Date.now() / 1000) + 60 * 60 * 36
|
|
19
|
+
}
|
|
20
|
+
})
|
|
21
|
+
});
|
|
22
|
+
credentialsStorage = new CredentialsStorage();
|
|
23
|
+
});
|
|
24
|
+
it('should load credentials and update token', () => {
|
|
25
|
+
expect(credentialsStorage.getToken(mcApiUrl)).toBe('hello-world');
|
|
26
|
+
expect(credentialsStorage.isSessionValid(mcApiUrl)).toBe(true);
|
|
27
|
+
const newSessionData = {
|
|
28
|
+
token: 'fizz-buzz',
|
|
29
|
+
expiresAt: Math.floor(Date.now() / 1000) + 60 * 60 * 36
|
|
30
|
+
};
|
|
31
|
+
credentialsStorage.setToken(mcApiUrl, newSessionData);
|
|
32
|
+
expect(credentialsStorage.getToken(mcApiUrl)).toBe('fizz-buzz');
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
describe('when session is expired', () => {
|
|
36
|
+
let credentialsStorage;
|
|
37
|
+
beforeEach(() => {
|
|
38
|
+
mock({
|
|
39
|
+
[CredentialsStorage.location]: JSON.stringify({
|
|
40
|
+
[mcApiUrl]: {
|
|
41
|
+
token: 'hello-world',
|
|
42
|
+
expiresAt: Math.floor(Date.now() / 1000) - 1
|
|
43
|
+
}
|
|
44
|
+
})
|
|
45
|
+
});
|
|
46
|
+
credentialsStorage = new CredentialsStorage();
|
|
47
|
+
});
|
|
48
|
+
it('should not load credentials', () => {
|
|
49
|
+
expect(credentialsStorage.isSessionValid(mcApiUrl)).toBe(false);
|
|
50
|
+
expect(credentialsStorage.getToken(mcApiUrl)).toBe(null);
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
describe('when credentials file is missing', () => {
|
|
54
|
+
let credentialsStorage;
|
|
55
|
+
beforeEach(() => {
|
|
56
|
+
mock({});
|
|
57
|
+
credentialsStorage = new CredentialsStorage();
|
|
58
|
+
});
|
|
59
|
+
it('should not load credentials and update token', () => {
|
|
60
|
+
expect(credentialsStorage.getToken(mcApiUrl)).toBe(null);
|
|
61
|
+
expect(credentialsStorage.isSessionValid(mcApiUrl)).toBe(false);
|
|
62
|
+
const newSessionData = {
|
|
63
|
+
token: 'fizz-buzz',
|
|
64
|
+
expiresAt: Math.floor(Date.now() / 1000) + 60 * 60 * 36
|
|
65
|
+
};
|
|
66
|
+
credentialsStorage.setToken(mcApiUrl, newSessionData);
|
|
67
|
+
expect(credentialsStorage.getToken(mcApiUrl)).toBe('fizz-buzz');
|
|
68
|
+
});
|
|
69
|
+
});
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
query FetchCustomApplicationFromCli($entryPointUriPath: String!) {
|
|
2
|
+
organizationExtensionForCustomApplication(
|
|
3
|
+
entryPointUriPath: $entryPointUriPath
|
|
4
|
+
) {
|
|
5
|
+
organizationId
|
|
6
|
+
application {
|
|
7
|
+
id
|
|
8
|
+
entryPointUriPath
|
|
9
|
+
name
|
|
10
|
+
description
|
|
11
|
+
url
|
|
12
|
+
icon
|
|
13
|
+
permissions {
|
|
14
|
+
name
|
|
15
|
+
oAuthScopes
|
|
16
|
+
}
|
|
17
|
+
mainMenuLink {
|
|
18
|
+
defaultLabel
|
|
19
|
+
labelAllLocales {
|
|
20
|
+
locale
|
|
21
|
+
value
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
submenuLinks {
|
|
25
|
+
uriPath
|
|
26
|
+
defaultLabel
|
|
27
|
+
permissions
|
|
28
|
+
labelAllLocales {
|
|
29
|
+
locale
|
|
30
|
+
value
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const {
|
|
4
|
+
GraphQLClient
|
|
5
|
+
} = require('graphql-request');
|
|
6
|
+
|
|
7
|
+
const {
|
|
8
|
+
GRAPHQL_TARGETS
|
|
9
|
+
} = require('@commercetools-frontend/constants');
|
|
10
|
+
|
|
11
|
+
const userAgent = require('./user-agent');
|
|
12
|
+
|
|
13
|
+
const requireGraphqlHelper = require('./require-graphql');
|
|
14
|
+
|
|
15
|
+
const requireGraphql = requireGraphqlHelper(__dirname);
|
|
16
|
+
const FetchCustomApplicationFromCli = requireGraphql('./fetch-custom-application.settings.graphql');
|
|
17
|
+
const UpdateCustomApplicationFromCli = requireGraphql('./update-custom-application.settings.graphql');
|
|
18
|
+
const CreateCustomApplicationFromCli = requireGraphql('./create-custom-application.settings.graphql');
|
|
19
|
+
const FetchMyOrganizationsFromCli = requireGraphql('./fetch-user-organizations.core.graphql');
|
|
20
|
+
|
|
21
|
+
const graphQLClient = (uri, token, target = GRAPHQL_TARGETS.SETTINGS_SERVICE) => new GraphQLClient(`${uri}/graphql`, {
|
|
22
|
+
headers: {
|
|
23
|
+
Accept: 'application/json',
|
|
24
|
+
'Content-Type': 'application/json',
|
|
25
|
+
'x-graphql-target': target,
|
|
26
|
+
'x-mc-cli-access-token': token,
|
|
27
|
+
'x-user-agent': userAgent
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
const fetchCustomApplication = async ({
|
|
32
|
+
mcApiUrl,
|
|
33
|
+
token,
|
|
34
|
+
entryPointUriPath
|
|
35
|
+
}) => {
|
|
36
|
+
const variables = {
|
|
37
|
+
entryPointUriPath
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
try {
|
|
41
|
+
const customAppData = await graphQLClient(mcApiUrl, token).request(FetchCustomApplicationFromCli, variables);
|
|
42
|
+
return customAppData.organizationExtensionForCustomApplication;
|
|
43
|
+
} catch (error) {
|
|
44
|
+
throw new Error(error.response.message);
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const updateCustomApplication = async ({
|
|
49
|
+
mcApiUrl,
|
|
50
|
+
token,
|
|
51
|
+
applicationId,
|
|
52
|
+
organizationId,
|
|
53
|
+
data
|
|
54
|
+
}) => {
|
|
55
|
+
const variables = {
|
|
56
|
+
organizationId,
|
|
57
|
+
applicationId,
|
|
58
|
+
data
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
try {
|
|
62
|
+
const updatedCustomAppsData = await graphQLClient(mcApiUrl, token).request(UpdateCustomApplicationFromCli, variables);
|
|
63
|
+
return updatedCustomAppsData.updateCustomApplication;
|
|
64
|
+
} catch (error) {
|
|
65
|
+
throw new Error(error.response.message);
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const createCustomApplication = async ({
|
|
70
|
+
mcApiUrl,
|
|
71
|
+
token,
|
|
72
|
+
organizationId,
|
|
73
|
+
data
|
|
74
|
+
}) => {
|
|
75
|
+
const variables = {
|
|
76
|
+
organizationId,
|
|
77
|
+
data
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
try {
|
|
81
|
+
const createdCustomAppData = await graphQLClient(mcApiUrl, token).request(CreateCustomApplicationFromCli, variables);
|
|
82
|
+
return createdCustomAppData.createCustomApplication;
|
|
83
|
+
} catch (error) {
|
|
84
|
+
throw new Error(error.response.message);
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
const fetchUserOrganizations = async ({
|
|
89
|
+
mcApiUrl,
|
|
90
|
+
token
|
|
91
|
+
}) => {
|
|
92
|
+
try {
|
|
93
|
+
const userOrganizations = await graphQLClient(mcApiUrl, token, GRAPHQL_TARGETS.ADMINISTRATION_SERVICE).request(FetchMyOrganizationsFromCli);
|
|
94
|
+
return userOrganizations.myOrganizations;
|
|
95
|
+
} catch (error) {
|
|
96
|
+
throw new Error(error.response.message);
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
module.exports = {
|
|
101
|
+
fetchCustomApplication,
|
|
102
|
+
updateCustomApplication,
|
|
103
|
+
createCustomApplication,
|
|
104
|
+
fetchUserOrganizations
|
|
105
|
+
};
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const {
|
|
4
|
+
graphql
|
|
5
|
+
} = require('msw');
|
|
6
|
+
|
|
7
|
+
const {
|
|
8
|
+
setupServer
|
|
9
|
+
} = require('msw/node');
|
|
10
|
+
|
|
11
|
+
const {
|
|
12
|
+
createCustomApplication,
|
|
13
|
+
updateCustomApplication,
|
|
14
|
+
fetchCustomApplication,
|
|
15
|
+
fetchUserOrganizations
|
|
16
|
+
} = require('./graphql-requests');
|
|
17
|
+
|
|
18
|
+
const mockServer = setupServer();
|
|
19
|
+
afterEach(() => {
|
|
20
|
+
mockServer.resetHandlers();
|
|
21
|
+
});
|
|
22
|
+
beforeAll(() => mockServer.listen({
|
|
23
|
+
onUnhandledRequest: 'bypass'
|
|
24
|
+
}));
|
|
25
|
+
afterAll(() => mockServer.close());
|
|
26
|
+
const mcApiUrl = 'https://mc-api.europe-west1.gcp.commercetools.com';
|
|
27
|
+
describe('fetch custom application data', () => {
|
|
28
|
+
beforeEach(() => {
|
|
29
|
+
mockServer.use(graphql.query('FetchCustomApplicationFromCli', (req, res, ctx) => {
|
|
30
|
+
return res(ctx.data({
|
|
31
|
+
organizationExtensionForCustomApplication: {
|
|
32
|
+
id: 'test-id',
|
|
33
|
+
organizationId: 'org-id',
|
|
34
|
+
application: {
|
|
35
|
+
url: 'https://test.com',
|
|
36
|
+
name: 'Test name',
|
|
37
|
+
description: 'Test description',
|
|
38
|
+
entryPointUriPath: 'test-custom-app',
|
|
39
|
+
icon: '<svg><path fill="#000000"></path></svg>',
|
|
40
|
+
submenuLinks: [],
|
|
41
|
+
mainMenuLink: [],
|
|
42
|
+
permissions: [{
|
|
43
|
+
oAuthScopes: ['view_products', 'view_customers'],
|
|
44
|
+
name: 'viewTestCustomApp'
|
|
45
|
+
}, {
|
|
46
|
+
oAuthScopes: [],
|
|
47
|
+
name: 'manageTestCustomApp'
|
|
48
|
+
}]
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}));
|
|
52
|
+
}));
|
|
53
|
+
});
|
|
54
|
+
it('should match returned data', async () => {
|
|
55
|
+
const organizationExtensionForCustomApplication = await fetchCustomApplication({
|
|
56
|
+
entryPointUriPath: 'test-custom-app',
|
|
57
|
+
mcApiUrl,
|
|
58
|
+
token: 'test-token'
|
|
59
|
+
});
|
|
60
|
+
expect(organizationExtensionForCustomApplication.application.entryPointUriPath).toEqual('test-custom-app');
|
|
61
|
+
expect(organizationExtensionForCustomApplication.id).toEqual('test-id');
|
|
62
|
+
expect(organizationExtensionForCustomApplication.organizationId).toEqual('org-id');
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
describe('register custom application', () => {
|
|
66
|
+
beforeEach(() => {
|
|
67
|
+
mockServer.use(graphql.mutation('CreateCustomApplicationFromCli', (req, res, ctx) => {
|
|
68
|
+
return res(ctx.data({
|
|
69
|
+
createCustomApplication: {
|
|
70
|
+
id: 'new-test-id',
|
|
71
|
+
application: {
|
|
72
|
+
url: 'https://test.com',
|
|
73
|
+
name: 'New Test name',
|
|
74
|
+
description: 'Test description',
|
|
75
|
+
entryPointUriPath: 'new-test-custom-app',
|
|
76
|
+
icon: '<svg><path fill="#000000"></path></svg>',
|
|
77
|
+
submenuLinks: [],
|
|
78
|
+
mainMenuLink: [],
|
|
79
|
+
permissions: [{
|
|
80
|
+
oAuthScopes: ['view_products', 'view_customers'],
|
|
81
|
+
name: 'viewNewTestCustomApp'
|
|
82
|
+
}, {
|
|
83
|
+
oAuthScopes: [],
|
|
84
|
+
name: 'manageNewTestCustomApp'
|
|
85
|
+
}]
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}));
|
|
89
|
+
}));
|
|
90
|
+
});
|
|
91
|
+
it('should match returned data', async () => {
|
|
92
|
+
const createdCustomAppsData = await createCustomApplication({
|
|
93
|
+
entryPointUriPath: 'new-test-custom-app',
|
|
94
|
+
mcApiUrl,
|
|
95
|
+
token: 'new-test-token'
|
|
96
|
+
});
|
|
97
|
+
expect(createdCustomAppsData.application.entryPointUriPath).toEqual('new-test-custom-app');
|
|
98
|
+
expect(createdCustomAppsData.id).toEqual('new-test-id');
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
describe('update custom application', () => {
|
|
102
|
+
beforeEach(() => {
|
|
103
|
+
mockServer.use(graphql.mutation('UpdateCustomApplicationFromCli', (req, res, ctx) => {
|
|
104
|
+
return res(ctx.data({
|
|
105
|
+
updateCustomApplication: {
|
|
106
|
+
id: 'test-id',
|
|
107
|
+
application: {
|
|
108
|
+
url: 'https://test.com',
|
|
109
|
+
name: 'Updated Test name',
|
|
110
|
+
description: 'Updated Test description',
|
|
111
|
+
entryPointUriPath: 'updated-test-custom-app',
|
|
112
|
+
icon: '<svg><path fill="#000000"></path></svg>',
|
|
113
|
+
submenuLinks: [],
|
|
114
|
+
mainMenuLink: [],
|
|
115
|
+
permissions: [{
|
|
116
|
+
oAuthScopes: ['view_products', 'view_customers'],
|
|
117
|
+
name: 'viewNewTestCustomApp'
|
|
118
|
+
}, {
|
|
119
|
+
oAuthScopes: [],
|
|
120
|
+
name: 'manageNewTestCustomApp'
|
|
121
|
+
}]
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}));
|
|
125
|
+
}));
|
|
126
|
+
});
|
|
127
|
+
it('should match returned data', async () => {
|
|
128
|
+
const updatedCustomAppsData = await updateCustomApplication({
|
|
129
|
+
entryPointUriPath: 'updated-test-custom-app',
|
|
130
|
+
mcApiUrl,
|
|
131
|
+
token: 'test-token'
|
|
132
|
+
});
|
|
133
|
+
expect(updatedCustomAppsData.application.name).toEqual('Updated Test name');
|
|
134
|
+
expect(updatedCustomAppsData.application.description).toEqual('Updated Test description');
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
describe('fetch user organizations', () => {
|
|
138
|
+
beforeEach(() => {
|
|
139
|
+
mockServer.use(graphql.query('FetchMyOrganizationsFromCli', (req, res, ctx) => {
|
|
140
|
+
return res(ctx.data({
|
|
141
|
+
myOrganizations: {
|
|
142
|
+
total: 1,
|
|
143
|
+
results: [{
|
|
144
|
+
id: 'test-organization-id',
|
|
145
|
+
name: 'test-organization-name'
|
|
146
|
+
}]
|
|
147
|
+
}
|
|
148
|
+
}));
|
|
149
|
+
}));
|
|
150
|
+
});
|
|
151
|
+
it('should match returned data', async () => {
|
|
152
|
+
const data = await fetchUserOrganizations({
|
|
153
|
+
mcApiUrl,
|
|
154
|
+
token: 'test-token'
|
|
155
|
+
});
|
|
156
|
+
expect(data.results[0].id).toEqual('test-organization-id');
|
|
157
|
+
expect(data.results[0].name).toEqual('test-organization-name');
|
|
158
|
+
});
|
|
159
|
+
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const path = require('path');
|
|
4
|
+
|
|
5
|
+
const {
|
|
6
|
+
readFileSync
|
|
7
|
+
} = require('fs'); // At the moment there is no proper way of loading `.graphql` files
|
|
8
|
+
// in nodejs. This workaround basically uses `readFileSync` to read the file
|
|
9
|
+
// content as a string.
|
|
10
|
+
// https://github.com/apollographql/graphql-tools/issues/273
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
const requireGraphql = folderPath => filePath => readFileSync(path.join(folderPath, filePath), 'utf-8');
|
|
14
|
+
|
|
15
|
+
module.exports = requireGraphql;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
|
|
5
|
+
const rcfile = require('rcfile');
|
|
6
|
+
|
|
7
|
+
const prettier = require('prettier');
|
|
8
|
+
|
|
9
|
+
const babel = require('@babel/core');
|
|
10
|
+
|
|
11
|
+
const {
|
|
12
|
+
getConfigPath
|
|
13
|
+
} = require('@commercetools-frontend/application-config');
|
|
14
|
+
|
|
15
|
+
function updateApplicationIdInCustomApplicationConfig(applicationId) {
|
|
16
|
+
const filePath = getConfigPath();
|
|
17
|
+
|
|
18
|
+
if (filePath.endsWith('.json')) {
|
|
19
|
+
const customApplicationConfig = require(filePath);
|
|
20
|
+
|
|
21
|
+
customApplicationConfig.env.production.applicationId = applicationId;
|
|
22
|
+
const prettierConfig = rcfile('prettier');
|
|
23
|
+
const formattedData = prettier.format(JSON.stringify(customApplicationConfig), { ...prettierConfig,
|
|
24
|
+
parser: 'json'
|
|
25
|
+
});
|
|
26
|
+
fs.writeFileSync(filePath, formattedData, {
|
|
27
|
+
encoding: 'utf8'
|
|
28
|
+
});
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const result = babel.transformFileSync(filePath, {
|
|
33
|
+
plugins: [function replaceCustomApplicationConfig() {
|
|
34
|
+
return {
|
|
35
|
+
visitor: {
|
|
36
|
+
Identifier(nodePath) {
|
|
37
|
+
if (nodePath.isIdentifier({
|
|
38
|
+
name: 'applicationId'
|
|
39
|
+
})) {
|
|
40
|
+
if (nodePath.findParent(parentPath => parentPath.get('key').isIdentifier({
|
|
41
|
+
name: 'env'
|
|
42
|
+
}))) {
|
|
43
|
+
nodePath.parent.value = babel.types.stringLiteral(applicationId);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
}],
|
|
51
|
+
retainLines: true
|
|
52
|
+
});
|
|
53
|
+
const prettierConfig = rcfile('prettier');
|
|
54
|
+
const formattedData = prettier.format(result.code, prettierConfig);
|
|
55
|
+
fs.writeFileSync(filePath, formattedData, {
|
|
56
|
+
encoding: 'utf8'
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
module.exports = updateApplicationIdInCustomApplicationConfig;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
mutation UpdateCustomApplicationFromCli(
|
|
2
|
+
$organizationId: String!
|
|
3
|
+
$data: CustomApplicationDraftDataInput!
|
|
4
|
+
$applicationId: ID!
|
|
5
|
+
) {
|
|
6
|
+
updateCustomApplication(
|
|
7
|
+
organizationId: $organizationId
|
|
8
|
+
data: $data
|
|
9
|
+
applicationId: $applicationId
|
|
10
|
+
) {
|
|
11
|
+
id
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const createHttpUserAgent = require('@commercetools/http-user-agent');
|
|
4
|
+
|
|
5
|
+
const pkgJson = require('../../package.json');
|
|
6
|
+
|
|
7
|
+
const userAgent = createHttpUserAgent({
|
|
8
|
+
name: 'cli-login',
|
|
9
|
+
libraryName: 'mc-scripts',
|
|
10
|
+
libraryVersion: pkgJson.version,
|
|
11
|
+
contactUrl: 'https://git.io/fjuyC',
|
|
12
|
+
// points to the appkit repo issues
|
|
13
|
+
contactEmail: 'support@commercetools.com'
|
|
14
|
+
});
|
|
15
|
+
module.exports = userAgent;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@commercetools-frontend/mc-scripts",
|
|
3
|
-
"version": "21.0
|
|
3
|
+
"version": "21.3.0",
|
|
4
4
|
"description": "Configuration and scripts for developing a MC application",
|
|
5
5
|
"bugs": "https://github.com/commercetools/merchant-center-application-kit/issues",
|
|
6
6
|
"repository": {
|
|
@@ -22,58 +22,67 @@
|
|
|
22
22
|
"development": ["last 2 firefox versions", "last 2 chrome versions"]
|
|
23
23
|
},
|
|
24
24
|
"scripts": {
|
|
25
|
-
"build": "rimraf build && babel src --out-dir build",
|
|
25
|
+
"build": "rimraf build && babel src --out-dir build && ../../scripts/copy-graphql-files.mjs src build",
|
|
26
26
|
"build:bundles:watch": "yarn build -w"
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@babel/
|
|
30
|
-
"@babel/runtime
|
|
31
|
-
"@
|
|
29
|
+
"@babel/core": "^7.17.8",
|
|
30
|
+
"@babel/runtime": "^7.17.8",
|
|
31
|
+
"@babel/runtime-corejs3": "^7.17.8",
|
|
32
|
+
"@commercetools-frontend/application-config": "21.3.0",
|
|
32
33
|
"@commercetools-frontend/assets": "21.0.0",
|
|
33
|
-
"@commercetools-frontend/babel-preset-mc-app": "21.
|
|
34
|
+
"@commercetools-frontend/babel-preset-mc-app": "21.3.0",
|
|
35
|
+
"@commercetools-frontend/constants": "21.3.0",
|
|
34
36
|
"@commercetools-frontend/mc-dev-authentication": "21.0.0",
|
|
35
|
-
"@commercetools-frontend/mc-html-template": "21.0
|
|
37
|
+
"@commercetools-frontend/mc-html-template": "21.3.0",
|
|
36
38
|
"@pmmmwh/react-refresh-webpack-plugin": "0.5.4",
|
|
37
|
-
"@svgr/webpack": "6.2.
|
|
38
|
-
"autoprefixer": "^10.4.
|
|
39
|
-
"babel-loader": "8.2.
|
|
40
|
-
"browserslist": "^4.
|
|
41
|
-
"
|
|
42
|
-
"
|
|
39
|
+
"@svgr/webpack": "6.2.1",
|
|
40
|
+
"autoprefixer": "^10.4.4",
|
|
41
|
+
"babel-loader": "8.2.4",
|
|
42
|
+
"browserslist": "^4.20.2",
|
|
43
|
+
"chalk": "4.1.2",
|
|
44
|
+
"core-js": "^3.21.1",
|
|
45
|
+
"css-loader": "6.7.1",
|
|
43
46
|
"css-minimizer-webpack-plugin": "3.4.1",
|
|
44
|
-
"dotenv": "
|
|
45
|
-
"dotenv-expand": "
|
|
46
|
-
"fs-extra": "10.0.
|
|
47
|
+
"dotenv": "16.0.0",
|
|
48
|
+
"dotenv-expand": "8.0.3",
|
|
49
|
+
"fs-extra": "10.0.1",
|
|
50
|
+
"graphql-request": "^4.1.0",
|
|
47
51
|
"graphql-tag": "^2.12.6",
|
|
48
52
|
"html-webpack-plugin": "5.5.0",
|
|
49
53
|
"json-loader": "0.5.7",
|
|
50
|
-
"mini-css-extract-plugin": "2.
|
|
54
|
+
"mini-css-extract-plugin": "2.6.0",
|
|
51
55
|
"moment-locales-webpack-plugin": "1.2.0",
|
|
52
56
|
"mri": "1.2.0",
|
|
53
|
-
"postcss": "8.4.
|
|
57
|
+
"postcss": "8.4.12",
|
|
54
58
|
"postcss-custom-media": "8.0.0",
|
|
55
59
|
"postcss-custom-properties": "12.1.4",
|
|
56
|
-
"postcss-import": "14.0
|
|
60
|
+
"postcss-import": "14.1.0",
|
|
57
61
|
"postcss-loader": "6.2.1",
|
|
58
62
|
"postcss-reporter": "7.0.5",
|
|
63
|
+
"prettier": "2.6.1",
|
|
64
|
+
"prompts": "^2.4.2",
|
|
59
65
|
"querystring-es3": "^0.2.1",
|
|
66
|
+
"rcfile": "1.0.3",
|
|
60
67
|
"react-dev-utils": "12.0.0",
|
|
61
|
-
"react-refresh": "0.
|
|
68
|
+
"react-refresh": "0.12.0",
|
|
62
69
|
"serve-handler": "6.1.3",
|
|
63
70
|
"shelljs": "0.8.5",
|
|
64
71
|
"style-loader": "3.3.1",
|
|
65
72
|
"svg-url-loader": "7.1.1",
|
|
66
|
-
"terser-webpack-plugin": "5.3.
|
|
73
|
+
"terser-webpack-plugin": "5.3.1",
|
|
67
74
|
"thread-loader": "3.0.4",
|
|
68
75
|
"url": "^0.11.0",
|
|
69
|
-
"webpack": "5.
|
|
76
|
+
"webpack": "5.70.0",
|
|
70
77
|
"webpack-bundle-analyzer": "4.5.0",
|
|
71
|
-
"webpack-dev-server": "4.7.
|
|
78
|
+
"webpack-dev-server": "4.7.4",
|
|
72
79
|
"webpackbar": "5.0.2"
|
|
73
80
|
},
|
|
74
81
|
"devDependencies": {
|
|
75
|
-
"@babel/plugin-transform-runtime": "^7.
|
|
82
|
+
"@babel/plugin-transform-runtime": "^7.17.0",
|
|
76
83
|
"@babel/preset-env": "^7.16.11",
|
|
84
|
+
"mock-fs": "^5.1.2",
|
|
85
|
+
"msw": "0.39.2",
|
|
77
86
|
"rimraf": "3.0.2"
|
|
78
87
|
},
|
|
79
88
|
"engines": {
|