@unito/integration-cli 0.55.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/LICENSE +3 -0
- package/README.md +32 -0
- package/bin/run +11 -0
- package/bin/run.cmd +3 -0
- package/dist/.eslintrc.d.ts +10 -0
- package/dist/.eslintrc.js +20 -0
- package/dist/integrationGenerator/errors.d.ts +2 -0
- package/dist/integrationGenerator/errors.js +6 -0
- package/dist/integrationGenerator/index.d.ts +2 -0
- package/dist/integrationGenerator/index.js +5 -0
- package/dist/integrationGenerator/integrationBoilerplate/.dockerignore +3 -0
- package/dist/integrationGenerator/integrationBoilerplate/.eslintrc.js +74 -0
- package/dist/integrationGenerator/integrationBoilerplate/.nvmrc +1 -0
- package/dist/integrationGenerator/integrationBoilerplate/.prettierignore +1 -0
- package/dist/integrationGenerator/integrationBoilerplate/.prettierrc +7 -0
- package/dist/integrationGenerator/integrationBoilerplate/.unito.json +1 -0
- package/dist/integrationGenerator/integrationBoilerplate/Dockerfile +38 -0
- package/dist/integrationGenerator/integrationBoilerplate/README.md +21 -0
- package/dist/integrationGenerator/integrationBoilerplate/integrationBoilerplate/.dockerignore +3 -0
- package/dist/integrationGenerator/integrationBoilerplate/integrationBoilerplate/.eslintrc.js +74 -0
- package/dist/integrationGenerator/integrationBoilerplate/integrationBoilerplate/.nvmrc +1 -0
- package/dist/integrationGenerator/integrationBoilerplate/integrationBoilerplate/.prettierignore +1 -0
- package/dist/integrationGenerator/integrationBoilerplate/integrationBoilerplate/.prettierrc +7 -0
- package/dist/integrationGenerator/integrationBoilerplate/integrationBoilerplate/.unito.json +1 -0
- package/dist/integrationGenerator/integrationBoilerplate/integrationBoilerplate/Dockerfile +38 -0
- package/dist/integrationGenerator/integrationBoilerplate/integrationBoilerplate/README.md +21 -0
- package/dist/integrationGenerator/integrationBoilerplate/integrationBoilerplate/package.json +43 -0
- package/dist/integrationGenerator/integrationBoilerplate/integrationBoilerplate/src/index.ts +94 -0
- package/dist/integrationGenerator/integrationBoilerplate/integrationBoilerplate/src/logger.ts +55 -0
- package/dist/integrationGenerator/integrationBoilerplate/integrationBoilerplate/src/middlewares/additionalLoggingContext.ts +22 -0
- package/dist/integrationGenerator/integrationBoilerplate/integrationBoilerplate/src/middlewares/correlationId.ts +13 -0
- package/dist/integrationGenerator/integrationBoilerplate/integrationBoilerplate/src/middlewares/credentials.ts +38 -0
- package/dist/integrationGenerator/integrationBoilerplate/integrationBoilerplate/src/request.ts +59 -0
- package/dist/integrationGenerator/integrationBoilerplate/integrationBoilerplate/src/routes/index.ts +11 -0
- package/dist/integrationGenerator/integrationBoilerplate/integrationBoilerplate/src/routes/me.ts +15 -0
- package/dist/integrationGenerator/integrationBoilerplate/integrationBoilerplate/src/routes/root.ts +12 -0
- package/dist/integrationGenerator/integrationBoilerplate/integrationBoilerplate/tsconfig.json +37 -0
- package/dist/integrationGenerator/integrationBoilerplate/package.json +43 -0
- package/dist/integrationGenerator/integrationBoilerplate/src/index.ts +90 -0
- package/dist/integrationGenerator/integrationBoilerplate/src/logger.ts +37 -0
- package/dist/integrationGenerator/integrationBoilerplate/src/middlewares/correlationId.ts +18 -0
- package/dist/integrationGenerator/integrationBoilerplate/src/middlewares/credentials.ts +38 -0
- package/dist/integrationGenerator/integrationBoilerplate/src/request.ts +59 -0
- package/dist/integrationGenerator/integrationBoilerplate/src/routes/index.ts +11 -0
- package/dist/integrationGenerator/integrationBoilerplate/src/routes/me.ts +15 -0
- package/dist/integrationGenerator/integrationBoilerplate/src/routes/root.ts +12 -0
- package/dist/integrationGenerator/integrationBoilerplate/tsconfig.json +37 -0
- package/dist/integrationGenerator/src/index.d.ts +1 -0
- package/dist/integrationGenerator/src/index.js +5 -0
- package/dist/integrationGenerator/src/resources/index.d.ts +1 -0
- package/dist/integrationGenerator/src/resources/index.js +5 -0
- package/dist/integrationGenerator/src/resources/integration.d.ts +9 -0
- package/dist/integrationGenerator/src/resources/integration.js +60 -0
- package/dist/integrationGenerator/test/resources/integration.test.d.ts +1 -0
- package/dist/integrationGenerator/test/resources/integration.test.js +51 -0
- package/dist/schemas/authorization.json +204 -0
- package/dist/schemas/automation.json +81 -0
- package/dist/schemas/configuration.json +89 -0
- package/dist/scripts/generateTypes.d.ts +8 -0
- package/dist/scripts/generateTypes.js +44 -0
- package/dist/src/baseCommand.d.ts +14 -0
- package/dist/src/baseCommand.js +39 -0
- package/dist/src/commands/activity.d.ts +12 -0
- package/dist/src/commands/activity.js +75 -0
- package/dist/src/commands/dev.d.ts +15 -0
- package/dist/src/commands/dev.js +123 -0
- package/dist/src/commands/encrypt.d.ts +11 -0
- package/dist/src/commands/encrypt.js +50 -0
- package/dist/src/commands/init.d.ts +10 -0
- package/dist/src/commands/init.js +51 -0
- package/dist/src/commands/invite.d.ts +11 -0
- package/dist/src/commands/invite.js +71 -0
- package/dist/src/commands/login.d.ts +11 -0
- package/dist/src/commands/login.js +76 -0
- package/dist/src/commands/oauth2.d.ts +10 -0
- package/dist/src/commands/oauth2.js +99 -0
- package/dist/src/commands/publish.d.ts +28 -0
- package/dist/src/commands/publish.js +302 -0
- package/dist/src/commands/test.d.ts +9 -0
- package/dist/src/commands/test.js +165 -0
- package/dist/src/commands/upgrade.d.ts +7 -0
- package/dist/src/commands/upgrade.js +88 -0
- package/dist/src/configurationTypes.d.ts +209 -0
- package/dist/src/configurationTypes.js +49 -0
- package/dist/src/errors.d.ts +38 -0
- package/dist/src/errors.js +159 -0
- package/dist/src/hooks/init/displayLogo.d.ts +3 -0
- package/dist/src/hooks/init/displayLogo.js +37 -0
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.js +5 -0
- package/dist/src/oauth2Helper/oauth2Helper.d.ts +63 -0
- package/dist/src/oauth2Helper/oauth2Helper.js +235 -0
- package/dist/src/oauth2Helper/types.d.ts +22 -0
- package/dist/src/oauth2Helper/types.js +2 -0
- package/dist/src/resources/configuration.d.ts +30 -0
- package/dist/src/resources/configuration.js +191 -0
- package/dist/src/resources/decryption.d.ts +5 -0
- package/dist/src/resources/decryption.js +62 -0
- package/dist/src/resources/fileSystem.d.ts +2 -0
- package/dist/src/resources/fileSystem.js +22 -0
- package/dist/src/resources/globalConfiguration.d.ts +13 -0
- package/dist/src/resources/globalConfiguration.js +44 -0
- package/dist/src/resources/integrations.d.ts +2 -0
- package/dist/src/resources/integrations.js +17 -0
- package/dist/src/resources/integrationsPlatform.d.ts +2 -0
- package/dist/src/resources/integrationsPlatform.js +33 -0
- package/dist/src/services/integrationsPlatform.d.ts +36 -0
- package/dist/src/services/integrationsPlatform.js +162 -0
- package/dist/src/services/oauth2Helper.d.ts +3 -0
- package/dist/src/services/oauth2Helper.js +34 -0
- package/dist/test/commands/activity.test.d.ts +1 -0
- package/dist/test/commands/activity.test.js +62 -0
- package/dist/test/commands/dev.test.d.ts +1 -0
- package/dist/test/commands/dev.test.js +139 -0
- package/dist/test/commands/encrypt.test.d.ts +1 -0
- package/dist/test/commands/encrypt.test.js +73 -0
- package/dist/test/commands/init.test.d.ts +1 -0
- package/dist/test/commands/init.test.js +45 -0
- package/dist/test/commands/invite.test.d.ts +1 -0
- package/dist/test/commands/invite.test.js +56 -0
- package/dist/test/commands/login.test.d.ts +1 -0
- package/dist/test/commands/login.test.js +90 -0
- package/dist/test/commands/oauth2.test.d.ts +1 -0
- package/dist/test/commands/oauth2.test.js +104 -0
- package/dist/test/commands/publish.test.d.ts +1 -0
- package/dist/test/commands/publish.test.js +429 -0
- package/dist/test/commands/test.test.d.ts +1 -0
- package/dist/test/commands/test.test.js +171 -0
- package/dist/test/commands/upgrade.test.d.ts +1 -0
- package/dist/test/commands/upgrade.test.js +47 -0
- package/dist/test/errors.test.d.ts +1 -0
- package/dist/test/errors.test.js +96 -0
- package/dist/test/helpers/init.d.ts +1 -0
- package/dist/test/helpers/init.js +6 -0
- package/dist/test/mocha.hooks.d.ts +2 -0
- package/dist/test/mocha.hooks.js +37 -0
- package/dist/test/oauth2Helper/oauth2Helper.test.d.ts +1 -0
- package/dist/test/oauth2Helper/oauth2Helper.test.js +150 -0
- package/dist/test/resources/configuration.test.d.ts +1 -0
- package/dist/test/resources/configuration.test.js +586 -0
- package/dist/test/resources/decryption.test.d.ts +1 -0
- package/dist/test/resources/decryption.test.js +68 -0
- package/dist/test/resources/globalConfiguration.test.d.ts +1 -0
- package/dist/test/resources/globalConfiguration.test.js +32 -0
- package/dist/test/services/integrationsPlatform.test.d.ts +1 -0
- package/dist/test/services/integrationsPlatform.test.js +168 -0
- package/dist/test/services/oauth2Helper.test.d.ts +1 -0
- package/dist/test/services/oauth2Helper.test.js +85 -0
- package/oclif.manifest.json +423 -0
- package/package.json +98 -0
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const tslib_1 = require("tslib");
|
|
4
|
+
const core_1 = require("@oclif/core");
|
|
5
|
+
const inquirer_1 = tslib_1.__importDefault(require("inquirer"));
|
|
6
|
+
const ngrok_1 = tslib_1.__importDefault(require("ngrok"));
|
|
7
|
+
const chalk_1 = tslib_1.__importDefault(require("chalk"));
|
|
8
|
+
const gradient = tslib_1.__importStar(require("gradient-string"));
|
|
9
|
+
const json_colorizer_1 = tslib_1.__importDefault(require("json-colorizer"));
|
|
10
|
+
const child_process_1 = tslib_1.__importDefault(require("child_process"));
|
|
11
|
+
const tmp_1 = tslib_1.__importDefault(require("tmp"));
|
|
12
|
+
const crypto_1 = tslib_1.__importDefault(require("crypto"));
|
|
13
|
+
const baseCommand_1 = require("../baseCommand");
|
|
14
|
+
const errors_1 = require("../errors");
|
|
15
|
+
const integrations_1 = require("../resources/integrations");
|
|
16
|
+
const GlobalConfiguration = tslib_1.__importStar(require("../resources/globalConfiguration"));
|
|
17
|
+
const IntegrationsPlatform = tslib_1.__importStar(require("../services/integrationsPlatform"));
|
|
18
|
+
const IntegrationConfiguration = tslib_1.__importStar(require("../resources/configuration"));
|
|
19
|
+
const fileSystem_1 = require("../resources/fileSystem");
|
|
20
|
+
const integrationsPlatform_1 = require("../resources/integrationsPlatform");
|
|
21
|
+
class Publish extends baseCommand_1.BaseCommand {
|
|
22
|
+
static description = 'Publish your integration';
|
|
23
|
+
static examples = ['<%= config.bin %> <%= command.id %>'];
|
|
24
|
+
static flags = {
|
|
25
|
+
environment: core_1.Flags.custom({
|
|
26
|
+
description: 'the environment of the platform',
|
|
27
|
+
options: Object.values(GlobalConfiguration.Environment),
|
|
28
|
+
default: GlobalConfiguration.Environment.Production,
|
|
29
|
+
})(),
|
|
30
|
+
'registry-only': core_1.Flags.boolean({
|
|
31
|
+
description: '(advanced) only update the registry',
|
|
32
|
+
default: false,
|
|
33
|
+
hidden: true,
|
|
34
|
+
exclusive: ['preview', 'live-preview'],
|
|
35
|
+
}),
|
|
36
|
+
preview: core_1.Flags.boolean({
|
|
37
|
+
description: 'preview the integration - your integration will run and be privately accessible on the target environment',
|
|
38
|
+
default: false,
|
|
39
|
+
exclusive: ['registry-only', 'live-preview'],
|
|
40
|
+
}),
|
|
41
|
+
'live-preview': core_1.Flags.boolean({
|
|
42
|
+
description: 'live-preview the integration - Useful to iterate quickly during development, your integration runs locally and is privately accessible on the target environment',
|
|
43
|
+
default: false,
|
|
44
|
+
exclusive: ['registry-only', 'preview'],
|
|
45
|
+
}),
|
|
46
|
+
'config-path': core_1.Flags.string({
|
|
47
|
+
summary: 'relative path to a custom ".unito.json" file',
|
|
48
|
+
description: `Use a custom configuration file instead of the default '.unito.json' or other environment specific
|
|
49
|
+
ones.
|
|
50
|
+
|
|
51
|
+
If you want to force the CLI to use a specific configuration file, you can use this flag to specify the relative
|
|
52
|
+
path from your integration's root folder (with a leading '/').
|
|
53
|
+
|
|
54
|
+
Usage: <%= config.bin %> <%= command.id %> --config-path=/myCustomConfig.json`,
|
|
55
|
+
}),
|
|
56
|
+
force: core_1.Flags.boolean({
|
|
57
|
+
description: 'bypass the confirmation prompt and force the command to run - this is useful for CI/CD pipelines',
|
|
58
|
+
default: false,
|
|
59
|
+
hidden: true,
|
|
60
|
+
}),
|
|
61
|
+
};
|
|
62
|
+
async catch(error) {
|
|
63
|
+
/* istanbul ignore if */
|
|
64
|
+
if ((0, errors_1.handleError)(error)) {
|
|
65
|
+
this.exit(-1);
|
|
66
|
+
}
|
|
67
|
+
throw error;
|
|
68
|
+
}
|
|
69
|
+
async run() {
|
|
70
|
+
(0, integrations_1.validateIsIntegrationDirectory)();
|
|
71
|
+
const { flags } = await this.parse(Publish);
|
|
72
|
+
// Read the configurations.
|
|
73
|
+
const globalConfiguration = await GlobalConfiguration.read(this.config.configDir);
|
|
74
|
+
const environment = flags.environment ?? GlobalConfiguration.Environment.Production;
|
|
75
|
+
const integrationConfiguration = await IntegrationConfiguration.getConfiguration(environment, flags['config-path']);
|
|
76
|
+
if (integrationConfiguration.authorizations) {
|
|
77
|
+
integrationConfiguration.authorizations = integrationConfiguration.authorizations.filter(authorization => !authorization.development);
|
|
78
|
+
}
|
|
79
|
+
// Login to the platform.
|
|
80
|
+
await (0, integrationsPlatform_1.validateAuthenticated)(globalConfiguration, environment);
|
|
81
|
+
// Package the integration.
|
|
82
|
+
tmp_1.default.setGracefulCleanup();
|
|
83
|
+
if (flags['live-preview']) {
|
|
84
|
+
await this.livePreviewIntegration(integrationConfiguration);
|
|
85
|
+
}
|
|
86
|
+
else if (flags['registry-only']) {
|
|
87
|
+
let proceed = true;
|
|
88
|
+
if (!flags.force && environment === GlobalConfiguration.Environment.Production) {
|
|
89
|
+
({ proceed } = await inquirer_1.default.prompt({
|
|
90
|
+
name: 'proceed',
|
|
91
|
+
message: '🙈🔫 You are about to update an integration in production directly. This is unusual, you should go through the normal publish and review process, are you sure you want to proceed?',
|
|
92
|
+
type: 'confirm',
|
|
93
|
+
}));
|
|
94
|
+
}
|
|
95
|
+
if (proceed) {
|
|
96
|
+
core_1.ux.log(`🙈 Hang on tight, updating Integration ${integrationConfiguration.name} information's in ${environment}!`);
|
|
97
|
+
await this.updateRegistry(integrationConfiguration);
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
core_1.ux.log('🙊 Abort! You are safe now 🍸 relax');
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
// Switch to a temporary directory to avoid modifying local files.
|
|
105
|
+
this.copyAndMoveToTmpDir();
|
|
106
|
+
if (flags.preview) {
|
|
107
|
+
await this.previewIntegration(integrationConfiguration);
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
await this.publishIntegration(integrationConfiguration);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
async livePreviewIntegration(integrationConfiguration) {
|
|
115
|
+
// Get the profile.
|
|
116
|
+
core_1.ux.action.start('Get profile');
|
|
117
|
+
const profile = await IntegrationsPlatform.getProfile();
|
|
118
|
+
core_1.ux.action.stop();
|
|
119
|
+
// Generate a new name.
|
|
120
|
+
const newName = `${integrationConfiguration.name}-live-preview-${this.hashEmail(profile.email)}`;
|
|
121
|
+
// Update the secrets.
|
|
122
|
+
const updatedConfiguration = await this.generateNewSecrets(integrationConfiguration, newName);
|
|
123
|
+
// Update the name.
|
|
124
|
+
updatedConfiguration.name = newName;
|
|
125
|
+
// Update the display name.
|
|
126
|
+
if (updatedConfiguration.ui?.displayName) {
|
|
127
|
+
updatedConfiguration.ui.displayName = [
|
|
128
|
+
updatedConfiguration.ui.displayName,
|
|
129
|
+
'Live Preview',
|
|
130
|
+
this.hashEmail(profile.email),
|
|
131
|
+
].join(' - ');
|
|
132
|
+
}
|
|
133
|
+
// Start ngrok & update the base url.
|
|
134
|
+
core_1.ux.action.start('Starting ngrok');
|
|
135
|
+
updatedConfiguration.baseUrl = await ngrok_1.default.connect(9200);
|
|
136
|
+
core_1.ux.action.stop();
|
|
137
|
+
// Update the registry.
|
|
138
|
+
await this.updateRegistry(updatedConfiguration);
|
|
139
|
+
// Launch the process.
|
|
140
|
+
core_1.ux.log('Launching the integration');
|
|
141
|
+
child_process_1.default.spawn('npm', ['run', 'dev'], {
|
|
142
|
+
stdio: 'inherit',
|
|
143
|
+
env: { ...process.env, NODE_ENV: 'development', PORT: '9200' },
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
async updateRegistry(integrationConfiguration) {
|
|
147
|
+
const integrations = await IntegrationsPlatform.getIntegrations();
|
|
148
|
+
const existing = integrations.find(integration => integration.name === integrationConfiguration.name);
|
|
149
|
+
// Create or update the integration.
|
|
150
|
+
let updated;
|
|
151
|
+
for (const authorization of integrationConfiguration.authorizations ?? []) {
|
|
152
|
+
authorization.instructionsImage = await this.getInstructionsImage(authorization.name);
|
|
153
|
+
authorization.instructionsMarkdown = await this.getInstructionsMarkdown(authorization.name);
|
|
154
|
+
}
|
|
155
|
+
if (existing) {
|
|
156
|
+
core_1.ux.action.start('Integration found. Updating');
|
|
157
|
+
updated = await IntegrationsPlatform.updateIntegration(existing.id, integrationConfiguration);
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
core_1.ux.action.start('Integration not found. Creating');
|
|
161
|
+
updated = await IntegrationsPlatform.createIntegration(integrationConfiguration);
|
|
162
|
+
}
|
|
163
|
+
core_1.ux.action.stop();
|
|
164
|
+
// Summary of the operation.
|
|
165
|
+
core_1.ux.log('');
|
|
166
|
+
core_1.ux.log(`The integration was ${existing ? 'updated' : 'created'} with the following information:`);
|
|
167
|
+
core_1.ux.log('');
|
|
168
|
+
core_1.ux.log((0, json_colorizer_1.default)(this.indent(JSON.stringify(updated, null, 2))));
|
|
169
|
+
core_1.ux.log('');
|
|
170
|
+
core_1.ux.log('The authorization instructions were updated as follow:');
|
|
171
|
+
core_1.ux.log('');
|
|
172
|
+
for (const authorization of integrationConfiguration.authorizations ?? []) {
|
|
173
|
+
core_1.ux.log(` ${authorization.name}:`);
|
|
174
|
+
core_1.ux.log(` image: ${authorization.instructionsImage ? '<set>' : '<unset>'}`);
|
|
175
|
+
core_1.ux.log(` markdown: ${authorization.instructionsMarkdown ? '<set>' : '<unset>'}`);
|
|
176
|
+
}
|
|
177
|
+
core_1.ux.log('');
|
|
178
|
+
}
|
|
179
|
+
copyAndMoveToTmpDir() {
|
|
180
|
+
// Copy integration in tmp folder.
|
|
181
|
+
core_1.ux.action.start('Packaging');
|
|
182
|
+
const archivePath = this.archiveIntegration();
|
|
183
|
+
const tmpDir = tmp_1.default.dirSync();
|
|
184
|
+
child_process_1.default.execSync(`unzip ${archivePath} -d ${tmpDir.name}`);
|
|
185
|
+
// Change the working directory.
|
|
186
|
+
process.chdir(tmpDir.name);
|
|
187
|
+
child_process_1.default.execSync('git init . --initial-branch=main');
|
|
188
|
+
}
|
|
189
|
+
async previewIntegration(integrationConfiguration) {
|
|
190
|
+
// Get the profile.
|
|
191
|
+
core_1.ux.action.start('Get profile');
|
|
192
|
+
const profile = await IntegrationsPlatform.getProfile();
|
|
193
|
+
core_1.ux.action.stop();
|
|
194
|
+
// Generate a new name.
|
|
195
|
+
const newName = `${integrationConfiguration.name}-preview-${this.hashEmail(profile.email)}`;
|
|
196
|
+
// Update the secrets.
|
|
197
|
+
const updatedConfiguration = await this.generateNewSecrets(integrationConfiguration, newName);
|
|
198
|
+
core_1.ux.action.start('Creating preview');
|
|
199
|
+
// Update the name.
|
|
200
|
+
updatedConfiguration.name = newName;
|
|
201
|
+
// Update the display name.
|
|
202
|
+
if (updatedConfiguration.ui?.displayName) {
|
|
203
|
+
updatedConfiguration.ui.displayName = [
|
|
204
|
+
updatedConfiguration.ui.displayName,
|
|
205
|
+
'Preview',
|
|
206
|
+
this.hashEmail(profile.email),
|
|
207
|
+
].join(' - ');
|
|
208
|
+
}
|
|
209
|
+
// Write the updated configuration on disk.
|
|
210
|
+
await IntegrationConfiguration.writeConfiguration(updatedConfiguration);
|
|
211
|
+
core_1.ux.action.stop();
|
|
212
|
+
// Publish the integration.
|
|
213
|
+
await this.publishIntegration(updatedConfiguration);
|
|
214
|
+
}
|
|
215
|
+
async publishIntegration(integrationConfiguration) {
|
|
216
|
+
// Packaging the integration.
|
|
217
|
+
core_1.ux.action.start('Packaging');
|
|
218
|
+
// Write the final configuration on disk.
|
|
219
|
+
await IntegrationConfiguration.writeConfiguration(integrationConfiguration);
|
|
220
|
+
const archivePath = this.archiveIntegration();
|
|
221
|
+
core_1.ux.action.stop();
|
|
222
|
+
// Publish the integration.
|
|
223
|
+
core_1.ux.action.start('Publishing');
|
|
224
|
+
await IntegrationsPlatform.publishIntegration(archivePath);
|
|
225
|
+
core_1.ux.action.stop();
|
|
226
|
+
// Summary of the operation.
|
|
227
|
+
core_1.ux.log('');
|
|
228
|
+
core_1.ux.log(['Your integration', gradient.fruit(integrationConfiguration.name), 'is being published.'].join(' '));
|
|
229
|
+
core_1.ux.log(chalk_1.default.yellowBright("Note that if the code of your integration did not change, it won't be republished."));
|
|
230
|
+
}
|
|
231
|
+
indent(paragraph, tabs = 1) {
|
|
232
|
+
return paragraph
|
|
233
|
+
.split('\n')
|
|
234
|
+
.map(line => [' '.repeat(Math.max(0, tabs) * 2), line].join(''))
|
|
235
|
+
.join('\n');
|
|
236
|
+
}
|
|
237
|
+
archiveIntegration() {
|
|
238
|
+
const archivePath = tmp_1.default.tmpNameSync({ postfix: `.zip` });
|
|
239
|
+
child_process_1.default.execSync(`git ls-files --cached --others --exclude-standard | zip -@ ${archivePath}`, {
|
|
240
|
+
cwd: process.cwd(),
|
|
241
|
+
env: { ...process.env },
|
|
242
|
+
});
|
|
243
|
+
return archivePath;
|
|
244
|
+
}
|
|
245
|
+
hashEmail(email) {
|
|
246
|
+
return crypto_1.default.createHash('shake256', { outputLength: 2 }).update(email).digest('hex');
|
|
247
|
+
}
|
|
248
|
+
async generateNewSecrets(integrationConfiguration, newIntegrationName) {
|
|
249
|
+
core_1.ux.action.start(`Reencrypting with name ${newIntegrationName}`);
|
|
250
|
+
const updatedConfiguration = JSON.parse(JSON.stringify(integrationConfiguration)); // deep copy.
|
|
251
|
+
for (const authorization of updatedConfiguration.authorizations ?? []) {
|
|
252
|
+
/* istanbul ignore if */
|
|
253
|
+
if (!authorization.oauth2) {
|
|
254
|
+
continue;
|
|
255
|
+
}
|
|
256
|
+
for (const [fieldName, value] of Object.entries(authorization.oauth2)) {
|
|
257
|
+
const newlyEncryptedData = await this.reencryptData(integrationConfiguration.name, [authorization.name, fieldName].join(':'), value, newIntegrationName);
|
|
258
|
+
if (newlyEncryptedData) {
|
|
259
|
+
authorization.oauth2[fieldName] = newlyEncryptedData;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
for (const [key, secret] of Object.entries(updatedConfiguration.secrets ?? {})) {
|
|
264
|
+
const newlyEncryptedData = await this.reencryptData(integrationConfiguration.name, `secrets:${key}`, secret, newIntegrationName);
|
|
265
|
+
if (newlyEncryptedData) {
|
|
266
|
+
updatedConfiguration.secrets[key] = newlyEncryptedData;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
core_1.ux.action.stop();
|
|
270
|
+
return updatedConfiguration;
|
|
271
|
+
}
|
|
272
|
+
async reencryptData(originalIntegrationName, key, encryptedData, newIntegrationName) {
|
|
273
|
+
if (typeof encryptedData !== 'string') {
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
if (!encryptedData.startsWith('unito-secret-')) {
|
|
277
|
+
core_1.ux.log(`Skipping ${key}`);
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
core_1.ux.log(`Reencrypting ${key}`);
|
|
281
|
+
const { encryptedData: reencryptedData } = await IntegrationsPlatform.reencryptData(originalIntegrationName, encryptedData, newIntegrationName);
|
|
282
|
+
return reencryptedData;
|
|
283
|
+
}
|
|
284
|
+
async getInstructionsImage(authName) {
|
|
285
|
+
const extensions = ['png', 'jpg', 'jpeg'];
|
|
286
|
+
for (const ext of extensions) {
|
|
287
|
+
const fileName = [process.cwd(), 'assets', `instructions.${authName}.${ext}`].join('/');
|
|
288
|
+
const fileBuffer = await (0, fileSystem_1.getFileBuffer)(fileName, 400 * 1024); // 400 KB size limit
|
|
289
|
+
if (fileBuffer) {
|
|
290
|
+
const mimeType = ext === 'png' ? 'image/png' : 'image/jpeg';
|
|
291
|
+
return `data:${mimeType};base64,${fileBuffer.toString('base64')}`;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
return null;
|
|
295
|
+
}
|
|
296
|
+
async getInstructionsMarkdown(authName) {
|
|
297
|
+
const fileName = [process.cwd(), 'assets', `instructions.${authName}.md`].join('/');
|
|
298
|
+
const fileBuffer = await (0, fileSystem_1.getFileBuffer)(fileName, 64 * 1024); // 64 KB size limit
|
|
299
|
+
return fileBuffer?.toString('utf8') ?? null;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
exports.default = Publish;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { FlagInput } from '@oclif/core/lib/interfaces/parser';
|
|
2
|
+
import { BaseCommand } from '../baseCommand';
|
|
3
|
+
export default class Test extends BaseCommand<typeof Test> {
|
|
4
|
+
static description: string;
|
|
5
|
+
static examples: string[];
|
|
6
|
+
static flags: FlagInput;
|
|
7
|
+
catch(error: Error): Promise<void>;
|
|
8
|
+
run(): Promise<void>;
|
|
9
|
+
}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const tslib_1 = require("tslib");
|
|
4
|
+
const core_1 = require("@oclif/core");
|
|
5
|
+
const child_process_1 = tslib_1.__importDefault(require("child_process"));
|
|
6
|
+
const crawlerDriver_1 = require("@unito/integration-debugger/dist/src/services/crawlerDriver");
|
|
7
|
+
const baseCommand_1 = require("../baseCommand");
|
|
8
|
+
const errors_1 = require("../errors");
|
|
9
|
+
const GlobalConfiguration = tslib_1.__importStar(require("../resources/globalConfiguration"));
|
|
10
|
+
const integrations_1 = require("../resources/integrations");
|
|
11
|
+
const configuration_1 = require("../resources/configuration");
|
|
12
|
+
const decryption_1 = require("../resources/decryption");
|
|
13
|
+
class Test extends baseCommand_1.BaseCommand {
|
|
14
|
+
static description = 'Test your integration';
|
|
15
|
+
static examples = ['<%= config.bin %> <%= command.id %>'];
|
|
16
|
+
static flags = {
|
|
17
|
+
environment: core_1.Flags.custom({
|
|
18
|
+
description: 'the environment of the platform',
|
|
19
|
+
options: Object.values(GlobalConfiguration.Environment),
|
|
20
|
+
default: GlobalConfiguration.Environment.Production,
|
|
21
|
+
})(),
|
|
22
|
+
verbose: core_1.Flags.boolean({
|
|
23
|
+
description: 'output more (debug) information',
|
|
24
|
+
default: false,
|
|
25
|
+
}),
|
|
26
|
+
'starting-path': core_1.Flags.string({
|
|
27
|
+
description: 'starting path of the crawler',
|
|
28
|
+
default: '/',
|
|
29
|
+
}),
|
|
30
|
+
'starting-operation': core_1.Flags.custom({
|
|
31
|
+
description: 'starting operation of the crawler',
|
|
32
|
+
options: Object.values(crawlerDriver_1.Operation),
|
|
33
|
+
default: crawlerDriver_1.Operation.GetItem,
|
|
34
|
+
})(),
|
|
35
|
+
'output-path': core_1.Flags.string({
|
|
36
|
+
description: 'output report in JSON format at the specified path',
|
|
37
|
+
}),
|
|
38
|
+
'credential-payload': core_1.Flags.string({
|
|
39
|
+
description: '(advanced) credential to use.',
|
|
40
|
+
}),
|
|
41
|
+
'test-account': core_1.Flags.string({
|
|
42
|
+
description: 'Test account to use.',
|
|
43
|
+
options: Object.values(configuration_1.CredentialScope),
|
|
44
|
+
default: configuration_1.CredentialScope.DEVELOPMENT,
|
|
45
|
+
}),
|
|
46
|
+
crawlMode: core_1.Flags.string({
|
|
47
|
+
description: 'mode to use while crawling the integration graph',
|
|
48
|
+
options: Object.values(configuration_1.CrawlMode),
|
|
49
|
+
default: configuration_1.CrawlMode.FULL,
|
|
50
|
+
}),
|
|
51
|
+
checks: core_1.Flags.string({
|
|
52
|
+
description: 'checks to perform while crawling the integration graph',
|
|
53
|
+
multiple: true,
|
|
54
|
+
default: [],
|
|
55
|
+
}),
|
|
56
|
+
debug: core_1.Flags.boolean({
|
|
57
|
+
description: 'Log launch command to console before running it - including decrypted values - for debugging only',
|
|
58
|
+
hidden: true,
|
|
59
|
+
default: false,
|
|
60
|
+
}),
|
|
61
|
+
'config-path': core_1.Flags.string({
|
|
62
|
+
summary: 'relative path to a custom ".unito.json" file',
|
|
63
|
+
description: `Use a custom configuration file instead of the default '.unito.json' or other environment specific
|
|
64
|
+
ones.
|
|
65
|
+
|
|
66
|
+
If you want to force the CLI to use a specific configuration file, you can use this flag to specify the relative
|
|
67
|
+
path from your integration's root folder (with a leading '/').
|
|
68
|
+
|
|
69
|
+
Usage: <%= config.bin %> <%= command.id %> --config-path=/myCustomConfig.json`,
|
|
70
|
+
}),
|
|
71
|
+
};
|
|
72
|
+
async catch(error) {
|
|
73
|
+
/* istanbul ignore if */
|
|
74
|
+
if ((0, errors_1.handleError)(error)) {
|
|
75
|
+
this.exit(-1);
|
|
76
|
+
}
|
|
77
|
+
throw error;
|
|
78
|
+
}
|
|
79
|
+
async run() {
|
|
80
|
+
(0, integrations_1.validateIsIntegrationDirectory)();
|
|
81
|
+
const { flags } = await this.parse(Test);
|
|
82
|
+
// Install NPM dependencies.
|
|
83
|
+
core_1.ux.action.start('Installing NPM dependencies');
|
|
84
|
+
child_process_1.default.execSync('npm install', {
|
|
85
|
+
env: { ...process.env, NODE_ENV: 'development' },
|
|
86
|
+
stdio: ['ignore', 'ignore', 'inherit'],
|
|
87
|
+
});
|
|
88
|
+
core_1.ux.action.stop();
|
|
89
|
+
// Resolve the configuration.
|
|
90
|
+
const environment = flags.environment ?? GlobalConfiguration.Environment.Production;
|
|
91
|
+
const configuration = await (0, configuration_1.getConfiguration)(environment, flags['config-path']);
|
|
92
|
+
let credentialPayload = flags['credential-payload'];
|
|
93
|
+
// Default to test account's credentials if none are specifically provided
|
|
94
|
+
if (credentialPayload === null || credentialPayload === undefined) {
|
|
95
|
+
const decryptedCredentials = await (0, decryption_1.decryptTestAccountCredentials)(configuration, environment, flags['test-account'], this.config.configDir);
|
|
96
|
+
credentialPayload = JSON.stringify(decryptedCredentials);
|
|
97
|
+
}
|
|
98
|
+
if (!credentialPayload || credentialPayload === '{}') {
|
|
99
|
+
throw new errors_1.MissingCredentialsError();
|
|
100
|
+
}
|
|
101
|
+
// Decrypt secrets, if necessary.
|
|
102
|
+
const secrets = await (0, decryption_1.decryptSecrets)(configuration, environment, this.config.configDir);
|
|
103
|
+
// Launch the tests.
|
|
104
|
+
const commandArguments = [
|
|
105
|
+
`${process.env.NODE_MODULES_FOLDER}/@unito/integration-debugger/dist/src/index.js`,
|
|
106
|
+
'--integration-url=http://localhost:9200',
|
|
107
|
+
'--spawn-process=npm run dev',
|
|
108
|
+
`--credential-payload=${credentialPayload}`,
|
|
109
|
+
`--secrets-payload=${JSON.stringify(secrets)}`,
|
|
110
|
+
'--non-interactive',
|
|
111
|
+
`--starting-path=${flags['starting-path']}`,
|
|
112
|
+
`--starting-operation=${flags['starting-operation']}`,
|
|
113
|
+
];
|
|
114
|
+
if (configuration.graphRelativeUrl) {
|
|
115
|
+
commandArguments.push(`--graph-relative-url=${configuration.graphRelativeUrl}`);
|
|
116
|
+
}
|
|
117
|
+
if (configuration.credentialAccountRelativeUrl) {
|
|
118
|
+
commandArguments.push(`--credential-account-relative-url=${configuration.credentialAccountRelativeUrl}`);
|
|
119
|
+
}
|
|
120
|
+
if (configuration.webhookParsingRelativeUrl) {
|
|
121
|
+
commandArguments.push(`--webhook-parsing-relative-url=${configuration.webhookParsingRelativeUrl}`);
|
|
122
|
+
}
|
|
123
|
+
if (configuration.webhookSubscriptionsRelativeUrl) {
|
|
124
|
+
commandArguments.push(`--webhook-subscriptions-relative-url=${configuration.webhookSubscriptionsRelativeUrl}`);
|
|
125
|
+
}
|
|
126
|
+
if (configuration.webhookAcknowledgeRelativeUrl) {
|
|
127
|
+
commandArguments.push(`--webhook-acknowledge-relative-url=${configuration.webhookAcknowledgeRelativeUrl}`);
|
|
128
|
+
}
|
|
129
|
+
if (flags.checks?.length) {
|
|
130
|
+
commandArguments.push(`--checks=${flags.checks.join(',')}`);
|
|
131
|
+
}
|
|
132
|
+
if (flags.verbose) {
|
|
133
|
+
commandArguments.push('--verbose');
|
|
134
|
+
}
|
|
135
|
+
if (flags['output-path']) {
|
|
136
|
+
commandArguments.push(`--output-path=${flags['output-path']}`);
|
|
137
|
+
}
|
|
138
|
+
if (flags['crawlMode'] === configuration_1.CrawlMode.SAMPLE) {
|
|
139
|
+
commandArguments.push('--operation-collection-items-per-page=10');
|
|
140
|
+
commandArguments.push('--operation-collection-follow-next-pages=false');
|
|
141
|
+
}
|
|
142
|
+
if (flags['crawlMode'] === configuration_1.CrawlMode.SINGLE) {
|
|
143
|
+
commandArguments.push('--operation-collection-items-per-page=1');
|
|
144
|
+
commandArguments.push('--operation-collection-follow-next-pages=false');
|
|
145
|
+
}
|
|
146
|
+
if (flags.debug) {
|
|
147
|
+
core_1.ux.log(`Launching... node ${commandArguments.join(' ')}`);
|
|
148
|
+
}
|
|
149
|
+
const child = child_process_1.default.spawn('node', commandArguments, {
|
|
150
|
+
detached: true,
|
|
151
|
+
stdio: 'inherit',
|
|
152
|
+
env: { ...process.env, NODE_ENV: 'development', PORT: '9200' },
|
|
153
|
+
});
|
|
154
|
+
// istanbul ignore next
|
|
155
|
+
child.on('exit', (code) => {
|
|
156
|
+
process.exit(code);
|
|
157
|
+
});
|
|
158
|
+
// Window change (ex: resize) signal.
|
|
159
|
+
// istanbul ignore next
|
|
160
|
+
process.on('SIGWINCH', () => {
|
|
161
|
+
child.kill('SIGWINCH');
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
exports.default = Test;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const tslib_1 = require("tslib");
|
|
4
|
+
const core_1 = require("@oclif/core");
|
|
5
|
+
const child_process_1 = tslib_1.__importDefault(require("child_process"));
|
|
6
|
+
const chalk_1 = tslib_1.__importDefault(require("chalk"));
|
|
7
|
+
const baseCommand_1 = require("../baseCommand");
|
|
8
|
+
const errors_1 = require("../errors");
|
|
9
|
+
const errors_2 = require("../errors");
|
|
10
|
+
class Upgrade extends baseCommand_1.BaseCommand {
|
|
11
|
+
static description = 'Upgrade the CLI';
|
|
12
|
+
static examples = ['<%= config.bin %> <%= command.id %>'];
|
|
13
|
+
/* istanbul ignore next */
|
|
14
|
+
async catch(error) {
|
|
15
|
+
if ((0, errors_1.handleError)(error)) {
|
|
16
|
+
this.exit(-1);
|
|
17
|
+
}
|
|
18
|
+
throw error;
|
|
19
|
+
}
|
|
20
|
+
async run() {
|
|
21
|
+
//
|
|
22
|
+
// Check for the presence of UNITO_GITHUB_PKG_TOKEN.
|
|
23
|
+
//
|
|
24
|
+
// Necessary until we deploy to a public NPM repository.
|
|
25
|
+
//
|
|
26
|
+
core_1.ux.action.start('Checking for the presence of UNITO_GITHUB_PKG_TOKEN');
|
|
27
|
+
// istanbul ignore next
|
|
28
|
+
if (!process.env.UNITO_GITHUB_PKG_TOKEN) {
|
|
29
|
+
throw new errors_2.MissingEnvironmentVariableError('UNITO_GITHUB_PKG_TOKEN');
|
|
30
|
+
}
|
|
31
|
+
core_1.ux.action.stop('found');
|
|
32
|
+
//
|
|
33
|
+
// Global command options
|
|
34
|
+
//
|
|
35
|
+
const packageName = '@unitoio/integration-cli';
|
|
36
|
+
const execOptions = { env: { ...process.env } };
|
|
37
|
+
const npmOptions = [
|
|
38
|
+
'--global',
|
|
39
|
+
'--registry=https://npm.pkg.github.com/unitoio',
|
|
40
|
+
'--//npm.pkg.github.com/:_authToken=$UNITO_GITHUB_PKG_TOKEN',
|
|
41
|
+
].join(' ');
|
|
42
|
+
//
|
|
43
|
+
// Current version
|
|
44
|
+
//
|
|
45
|
+
core_1.ux.action.start('Checking the current version');
|
|
46
|
+
const currentVersion = child_process_1.default
|
|
47
|
+
.execSync(`npm list ${npmOptions} --depth 0 | grep ${packageName} | sed "s/.*@\\([^@]*\\)$/\\1/"`, execOptions)
|
|
48
|
+
.toString()
|
|
49
|
+
.trim();
|
|
50
|
+
core_1.ux.action.stop(currentVersion);
|
|
51
|
+
//
|
|
52
|
+
// Next version
|
|
53
|
+
//
|
|
54
|
+
core_1.ux.action.start('Checking the available version');
|
|
55
|
+
const newVersion = child_process_1.default
|
|
56
|
+
.execSync(`npm show ${npmOptions} ${packageName} version`, execOptions)
|
|
57
|
+
.toString()
|
|
58
|
+
.trim();
|
|
59
|
+
core_1.ux.action.stop(newVersion);
|
|
60
|
+
//
|
|
61
|
+
// Latest version
|
|
62
|
+
//
|
|
63
|
+
const latestVersion = currentVersion === newVersion;
|
|
64
|
+
if (latestVersion) {
|
|
65
|
+
core_1.ux.log(chalk_1.default.yellowBright("You already have the latest version but let's make sure it is working properly!"));
|
|
66
|
+
}
|
|
67
|
+
//
|
|
68
|
+
// Validate / Upgrade the package globally.
|
|
69
|
+
//
|
|
70
|
+
core_1.ux.action.start(`${latestVersion ? 'Validating' : 'Upgrading'} the package`);
|
|
71
|
+
// To make sure we have a clean version of the CLI, with updated packages, we force the re-installation.
|
|
72
|
+
child_process_1.default.execSync(`npm install --force ${npmOptions} ${packageName}`, {
|
|
73
|
+
...execOptions,
|
|
74
|
+
stdio: ['ignore', 'ignore', 'inherit'],
|
|
75
|
+
});
|
|
76
|
+
core_1.ux.action.stop();
|
|
77
|
+
//
|
|
78
|
+
// Validated / Upgraded.
|
|
79
|
+
//
|
|
80
|
+
if (latestVersion) {
|
|
81
|
+
core_1.ux.log(chalk_1.default.greenBright(`The CLI is valid! Enjoy!`));
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
core_1.ux.log(chalk_1.default.greenBright(`Upgraded from ${currentVersion} to ${newVersion}! Enjoy!`));
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
exports.default = Upgrade;
|