@elastic/synthetics 1.0.0-beta.31 → 1.0.0-beta.32
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/dist/cli.js +51 -26
- package/dist/cli.js.map +1 -1
- package/dist/common_types.d.ts +2 -0
- package/dist/common_types.d.ts.map +1 -1
- package/dist/core/runner.d.ts.map +1 -1
- package/dist/core/runner.js +4 -3
- package/dist/core/runner.js.map +1 -1
- package/dist/dsl/monitor.d.ts +2 -1
- package/dist/dsl/monitor.d.ts.map +1 -1
- package/dist/dsl/monitor.js +2 -2
- package/dist/dsl/monitor.js.map +1 -1
- package/dist/generator/index.d.ts +11 -3
- package/dist/generator/index.d.ts.map +1 -1
- package/dist/generator/index.js +98 -27
- package/dist/generator/index.js.map +1 -1
- package/dist/generator/utils.d.ts +2 -1
- package/dist/generator/utils.d.ts.map +1 -1
- package/dist/generator/utils.js +27 -4
- package/dist/generator/utils.js.map +1 -1
- package/dist/helpers.d.ts +9 -5
- package/dist/helpers.d.ts.map +1 -1
- package/dist/helpers.js +37 -10
- package/dist/helpers.js.map +1 -1
- package/dist/locations/index.d.ts +45 -0
- package/dist/locations/index.d.ts.map +1 -0
- package/dist/locations/index.js +90 -0
- package/dist/locations/index.js.map +1 -0
- package/dist/{dsl/locations.d.ts → locations/public-locations.d.ts} +1 -1
- package/dist/locations/public-locations.d.ts.map +1 -0
- package/dist/{dsl/locations.js → locations/public-locations.js} +1 -1
- package/dist/locations/public-locations.js.map +1 -0
- package/dist/options.d.ts +7 -0
- package/dist/options.d.ts.map +1 -1
- package/dist/options.js +21 -1
- package/dist/options.js.map +1 -1
- package/dist/push/bundler.d.ts.map +1 -1
- package/dist/push/bundler.js +7 -2
- package/dist/push/bundler.js.map +1 -1
- package/dist/push/index.d.ts +6 -1
- package/dist/push/index.d.ts.map +1 -1
- package/dist/push/index.js +124 -5
- package/dist/push/index.js.map +1 -1
- package/dist/push/monitor.d.ts +2 -0
- package/dist/push/monitor.d.ts.map +1 -1
- package/dist/push/monitor.js +23 -7
- package/dist/push/monitor.js.map +1 -1
- package/dist/push/request.d.ts +8 -7
- package/dist/push/request.d.ts.map +1 -1
- package/dist/push/request.js +21 -22
- package/dist/push/request.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/cli.ts +79 -59
- package/src/common_types.ts +2 -0
- package/src/core/runner.ts +5 -6
- package/src/dsl/monitor.ts +2 -1
- package/src/generator/index.ts +138 -34
- package/src/generator/utils.ts +29 -3
- package/src/helpers.ts +35 -11
- package/src/locations/index.ts +108 -0
- package/src/{dsl/locations.ts → locations/public-locations.ts} +0 -0
- package/src/options.ts +38 -0
- package/src/push/bundler.ts +7 -2
- package/src/push/index.ts +150 -7
- package/src/push/monitor.ts +37 -9
- package/src/push/request.ts +26 -34
- package/templates/journeys/advanced-example-helpers.ts +9 -9
- package/templates/journeys/advanced-example.journey.ts +6 -6
- package/templates/journeys/example.journey.ts +1 -1
- package/templates/synthetics.config.ts +2 -1
- package/dist/dsl/locations.d.ts.map +0 -1
- package/dist/dsl/locations.js.map +0 -1
package/src/cli.ts
CHANGED
|
@@ -29,19 +29,37 @@ import { program, Option } from 'commander';
|
|
|
29
29
|
import { cwd } from 'process';
|
|
30
30
|
import { CliArgs, PushOptions } from './common_types';
|
|
31
31
|
import { reporters } from './reporters';
|
|
32
|
-
import {
|
|
32
|
+
import {
|
|
33
|
+
normalizeOptions,
|
|
34
|
+
parseThrottling,
|
|
35
|
+
getCommonCommandOpts,
|
|
36
|
+
} from './options';
|
|
33
37
|
import { loadTestFiles } from './loader';
|
|
34
38
|
import { run } from './';
|
|
35
39
|
import { runner } from './core';
|
|
36
40
|
import { SyntheticsLocations } from './dsl/monitor';
|
|
37
|
-
import {
|
|
41
|
+
import {
|
|
42
|
+
push,
|
|
43
|
+
loadSettings,
|
|
44
|
+
validateSettings,
|
|
45
|
+
catchIncorrectSettings,
|
|
46
|
+
} from './push';
|
|
47
|
+
import {
|
|
48
|
+
formatLocations,
|
|
49
|
+
getLocations,
|
|
50
|
+
renderLocations,
|
|
51
|
+
LocationCmdOptions,
|
|
52
|
+
} from './locations';
|
|
38
53
|
import { resolve } from 'path';
|
|
39
54
|
import { Generator } from './generator';
|
|
40
55
|
import { error } from './helpers';
|
|
56
|
+
import { LocationsMap } from './locations/public-locations';
|
|
41
57
|
|
|
42
58
|
/* eslint-disable-next-line @typescript-eslint/no-var-requires */
|
|
43
59
|
const { name, version } = require('../package.json');
|
|
44
60
|
|
|
61
|
+
const { params, pattern, tags, match, playwrightOpts } = getCommonCommandOpts();
|
|
62
|
+
|
|
45
63
|
program
|
|
46
64
|
.name(`npx ${name}`)
|
|
47
65
|
.usage('[options] [dir] [files] file')
|
|
@@ -49,20 +67,15 @@ program
|
|
|
49
67
|
'-c, --config <path>',
|
|
50
68
|
'configuration path (default: synthetics.config.js)'
|
|
51
69
|
)
|
|
52
|
-
.
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
)
|
|
70
|
+
.addOption(pattern)
|
|
71
|
+
.addOption(tags)
|
|
72
|
+
.addOption(match)
|
|
73
|
+
.addOption(params)
|
|
57
74
|
.addOption(
|
|
58
75
|
new Option('--reporter <value>', `output repoter format`).choices(
|
|
59
76
|
Object.keys(reporters)
|
|
60
77
|
)
|
|
61
78
|
)
|
|
62
|
-
.option(
|
|
63
|
-
'--pattern <pattern>',
|
|
64
|
-
'RegExp file patterns to search inside directory'
|
|
65
|
-
)
|
|
66
79
|
.option('--inline', 'Run inline journeys from heartbeat')
|
|
67
80
|
.option('-r, --require <modules...>', 'module(s) to preload')
|
|
68
81
|
.option('--no-headless', 'run browser in headful mode')
|
|
@@ -81,14 +94,6 @@ program
|
|
|
81
94
|
'--dry-run',
|
|
82
95
|
"don't actually execute anything, report only registered journeys"
|
|
83
96
|
)
|
|
84
|
-
.option(
|
|
85
|
-
'--tags <name...>',
|
|
86
|
-
'run only journeys with a tag that matches the glob'
|
|
87
|
-
)
|
|
88
|
-
.option(
|
|
89
|
-
'--match <name>',
|
|
90
|
-
'run only journeys with a name or tag that matches the glob'
|
|
91
|
-
)
|
|
92
97
|
.option(
|
|
93
98
|
'--outfd <fd>',
|
|
94
99
|
'specify a file descriptor for logs. Default is stdout',
|
|
@@ -117,11 +122,7 @@ program
|
|
|
117
122
|
{}
|
|
118
123
|
)
|
|
119
124
|
.option('--no-throttling', 'Turns off default network throttling.')
|
|
120
|
-
.
|
|
121
|
-
'--playwright-options <jsonstring>',
|
|
122
|
-
'JSON object to pass in custom Playwright options for the agent. Options passed will be merged with Playwright options defined in your synthetics.config.js file. Options defined via --playwright-options take precedence.',
|
|
123
|
-
JSON.parse
|
|
124
|
-
)
|
|
125
|
+
.addOption(playwrightOpts)
|
|
125
126
|
.version(version)
|
|
126
127
|
.description('Run synthetic tests')
|
|
127
128
|
.action(async (cliArgs: CliArgs) => {
|
|
@@ -149,19 +150,11 @@ program
|
|
|
149
150
|
program
|
|
150
151
|
.command('push')
|
|
151
152
|
.description(
|
|
152
|
-
'Push journeys in the current directory to create monitors within the Kibana monitor management UI'
|
|
153
|
+
'Push all journeys in the current directory to create monitors within the Kibana monitor management UI'
|
|
153
154
|
)
|
|
154
|
-
.
|
|
155
|
-
'--
|
|
156
|
-
'
|
|
157
|
-
)
|
|
158
|
-
.option(
|
|
159
|
-
'--tags <name...>',
|
|
160
|
-
'push only journeys with a tag that matches the glob'
|
|
161
|
-
)
|
|
162
|
-
.option(
|
|
163
|
-
'--match <name>',
|
|
164
|
-
'push only journeys with a name or tag that matches the glob'
|
|
155
|
+
.requiredOption(
|
|
156
|
+
'--auth <auth>',
|
|
157
|
+
'API key used for Kibana authentication(https://www.elastic.co/guide/en/kibana/master/api-keys.html).'
|
|
165
158
|
)
|
|
166
159
|
.option(
|
|
167
160
|
'--schedule <time-in-minutes>',
|
|
@@ -174,35 +167,37 @@ program
|
|
|
174
167
|
'default list of locations from which your monitors will run.'
|
|
175
168
|
).choices(SyntheticsLocations)
|
|
176
169
|
)
|
|
177
|
-
.
|
|
178
|
-
'--
|
|
179
|
-
'
|
|
170
|
+
.option(
|
|
171
|
+
'--private-locations <locations...>',
|
|
172
|
+
'default list of private locations from which your monitors will run.'
|
|
180
173
|
)
|
|
181
|
-
.
|
|
182
|
-
.
|
|
183
|
-
'--
|
|
184
|
-
'
|
|
174
|
+
.option('--url <url>', 'Kibana URL to upload the monitors')
|
|
175
|
+
.option(
|
|
176
|
+
'--project <id>',
|
|
177
|
+
'id that will be used for logically grouping monitors'
|
|
185
178
|
)
|
|
186
179
|
.option(
|
|
187
180
|
'--space <space>',
|
|
188
|
-
'the target Kibana spaces for the pushed monitors — spaces help you organise pushed monitors.'
|
|
189
|
-
'default'
|
|
181
|
+
'the target Kibana spaces for the pushed monitors — spaces help you organise pushed monitors.'
|
|
190
182
|
)
|
|
183
|
+
.addOption(pattern)
|
|
184
|
+
.addOption(tags)
|
|
185
|
+
.addOption(match)
|
|
186
|
+
.addOption(params)
|
|
187
|
+
.addOption(playwrightOpts)
|
|
191
188
|
.action(async (cmdOpts: PushOptions) => {
|
|
192
189
|
try {
|
|
190
|
+
const settings = await loadSettings();
|
|
193
191
|
await loadTestFiles({ inline: false }, [cwd()]);
|
|
194
|
-
const options = normalizeOptions({
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
throw error(`Set default location for all monitors via CLI as '--locations <locations...>' OR
|
|
202
|
-
configure via Synthetics config file under 'monitors.locations' field.`);
|
|
203
|
-
}
|
|
192
|
+
const options = normalizeOptions({
|
|
193
|
+
...program.opts(),
|
|
194
|
+
...settings,
|
|
195
|
+
...cmdOpts,
|
|
196
|
+
}) as PushOptions;
|
|
197
|
+
validateSettings(options);
|
|
198
|
+
await catchIncorrectSettings(settings, options);
|
|
204
199
|
const monitors = runner.buildMonitors(options);
|
|
205
|
-
await push(monitors,
|
|
200
|
+
await push(monitors, options);
|
|
206
201
|
} catch (e) {
|
|
207
202
|
e && console.error(e);
|
|
208
203
|
process.exit(1);
|
|
@@ -211,9 +206,9 @@ program
|
|
|
211
206
|
|
|
212
207
|
// Init command
|
|
213
208
|
program
|
|
214
|
-
.command('init
|
|
215
|
-
.description('
|
|
216
|
-
.action(async (dir
|
|
209
|
+
.command('init [dir]')
|
|
210
|
+
.description('Initialize Elastic synthetics project')
|
|
211
|
+
.action(async (dir = '') => {
|
|
217
212
|
try {
|
|
218
213
|
const generator = await new Generator(resolve(process.cwd(), dir));
|
|
219
214
|
await generator.setup();
|
|
@@ -223,4 +218,29 @@ program
|
|
|
223
218
|
}
|
|
224
219
|
});
|
|
225
220
|
|
|
221
|
+
// Locations command
|
|
222
|
+
program
|
|
223
|
+
.command('locations')
|
|
224
|
+
.description(
|
|
225
|
+
`List all locations to run the synthetics monitors. Pass optional '--url' and '--auth' to list private locations.`
|
|
226
|
+
)
|
|
227
|
+
.option('--url <url>', 'Kibana URL to fetch all public and private locations')
|
|
228
|
+
.option(
|
|
229
|
+
'--auth <auth>',
|
|
230
|
+
'API key used for Kibana authentication(https://www.elastic.co/guide/en/kibana/master/api-keys.html).'
|
|
231
|
+
)
|
|
232
|
+
.action(async (cmdOpts: LocationCmdOptions) => {
|
|
233
|
+
try {
|
|
234
|
+
let locations = Object.keys(LocationsMap);
|
|
235
|
+
if (cmdOpts.auth && cmdOpts.url) {
|
|
236
|
+
const allLocations = await getLocations(cmdOpts);
|
|
237
|
+
locations = formatLocations(allLocations);
|
|
238
|
+
}
|
|
239
|
+
renderLocations(locations);
|
|
240
|
+
} catch (e) {
|
|
241
|
+
e && error(e);
|
|
242
|
+
process.exit(1);
|
|
243
|
+
}
|
|
244
|
+
});
|
|
245
|
+
|
|
226
246
|
program.parse(process.argv);
|
package/src/common_types.ts
CHANGED
|
@@ -212,6 +212,7 @@ type BaseArgs = {
|
|
|
212
212
|
throttling?: ThrottlingOptions;
|
|
213
213
|
schedule?: MonitorConfig['schedule'];
|
|
214
214
|
locations?: MonitorConfig['locations'];
|
|
215
|
+
privateLocations?: MonitorConfig['privateLocations'];
|
|
215
216
|
};
|
|
216
217
|
|
|
217
218
|
export type CliArgs = BaseArgs & {
|
|
@@ -247,6 +248,7 @@ export type PushOptions = {
|
|
|
247
248
|
space: string;
|
|
248
249
|
schedule?: MonitorConfig['schedule'];
|
|
249
250
|
locations?: MonitorConfig['locations'];
|
|
251
|
+
privateLocations?: MonitorConfig['privateLocations'];
|
|
250
252
|
};
|
|
251
253
|
|
|
252
254
|
export type PlaywrightOptions = LaunchOptions & BrowserContextOptions;
|
package/src/core/runner.ts
CHANGED
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
*/
|
|
25
25
|
|
|
26
26
|
import { join } from 'path';
|
|
27
|
-
import { rm } from 'fs/promises';
|
|
27
|
+
import { mkdir, rm, writeFile } from 'fs/promises';
|
|
28
28
|
import { Journey } from '../dsl/journey';
|
|
29
29
|
import { Step } from '../dsl/step';
|
|
30
30
|
import { reporters, Reporter } from '../reporters';
|
|
@@ -34,8 +34,6 @@ import {
|
|
|
34
34
|
getTimestamp,
|
|
35
35
|
runParallel,
|
|
36
36
|
generateUniqueId,
|
|
37
|
-
mkdirAsync,
|
|
38
|
-
writeFileAsync,
|
|
39
37
|
} from '../helpers';
|
|
40
38
|
import {
|
|
41
39
|
HooksCallback,
|
|
@@ -87,7 +85,7 @@ export default class Runner {
|
|
|
87
85
|
* For each journey we create the screenshots folder for
|
|
88
86
|
* caching all screenshots and clear them at end of each journey
|
|
89
87
|
*/
|
|
90
|
-
await
|
|
88
|
+
await mkdir(this.screenshotPath, { recursive: true });
|
|
91
89
|
return {
|
|
92
90
|
start,
|
|
93
91
|
params: options.params,
|
|
@@ -116,7 +114,7 @@ export default class Runner {
|
|
|
116
114
|
timestamp: getTimestamp(),
|
|
117
115
|
data: buffer.toString('base64'),
|
|
118
116
|
};
|
|
119
|
-
await
|
|
117
|
+
await writeFile(
|
|
120
118
|
join(Runner.screenshotPath, fileName),
|
|
121
119
|
JSON.stringify(screenshot)
|
|
122
120
|
);
|
|
@@ -381,7 +379,7 @@ export default class Runner {
|
|
|
381
379
|
/**
|
|
382
380
|
* Set up the directory for caching screenshots
|
|
383
381
|
*/
|
|
384
|
-
await
|
|
382
|
+
await mkdir(CACHE_PATH, { recursive: true });
|
|
385
383
|
}
|
|
386
384
|
|
|
387
385
|
buildMonitors(options: RunOptions) {
|
|
@@ -393,6 +391,7 @@ export default class Runner {
|
|
|
393
391
|
throttling: options.throttling,
|
|
394
392
|
schedule: options.schedule,
|
|
395
393
|
locations: options.locations,
|
|
394
|
+
privateLocations: options.privateLocations,
|
|
396
395
|
params: options.params,
|
|
397
396
|
playwrightOptions: options.playwrightOptions,
|
|
398
397
|
});
|
package/src/dsl/monitor.ts
CHANGED
|
@@ -31,7 +31,7 @@ import {
|
|
|
31
31
|
Params,
|
|
32
32
|
PlaywrightOptions,
|
|
33
33
|
} from '../common_types';
|
|
34
|
-
import { LocationsMap } from '
|
|
34
|
+
import { LocationsMap } from '../locations/public-locations';
|
|
35
35
|
|
|
36
36
|
export type SyntheticsLocationsType = keyof typeof LocationsMap;
|
|
37
37
|
export const SyntheticsLocations = Object.keys(
|
|
@@ -45,6 +45,7 @@ export type MonitorConfig = {
|
|
|
45
45
|
schedule?: number;
|
|
46
46
|
enabled?: boolean;
|
|
47
47
|
locations?: SyntheticsLocationsType[];
|
|
48
|
+
privateLocations?: string[];
|
|
48
49
|
throttling?: ThrottlingOptions;
|
|
49
50
|
screenshot?: ScreenshotOptions;
|
|
50
51
|
params?: Params;
|
package/src/generator/index.ts
CHANGED
|
@@ -27,34 +27,56 @@ import { existsSync } from 'fs';
|
|
|
27
27
|
import { mkdir, readFile, writeFile } from 'fs/promises';
|
|
28
28
|
import { bold, cyan, yellow } from 'kleur/colors';
|
|
29
29
|
import { join, relative, dirname, basename } from 'path';
|
|
30
|
-
|
|
31
|
-
import {
|
|
32
|
-
import { progress, write as stdWrite } from '../helpers';
|
|
33
|
-
import {
|
|
30
|
+
// @ts-ignore-next-line: has no exported member 'Input'
|
|
31
|
+
import { prompt, Input } from 'enquirer';
|
|
32
|
+
import { progress, removeTrailingSlash, write as stdWrite } from '../helpers';
|
|
33
|
+
import {
|
|
34
|
+
getPackageManager,
|
|
35
|
+
replaceTemplates,
|
|
36
|
+
runCommand,
|
|
37
|
+
cloudIDToKibanaURL,
|
|
38
|
+
} from './utils';
|
|
39
|
+
import { formatLocations, getLocations, groupLocations } from '../locations';
|
|
34
40
|
|
|
35
|
-
// Templates that are required for setting up new
|
|
36
|
-
// synthetics project
|
|
41
|
+
// Templates that are required for setting up new synthetics project
|
|
37
42
|
const templateDir = join(__dirname, '..', '..', 'templates');
|
|
38
43
|
|
|
39
|
-
type
|
|
40
|
-
|
|
44
|
+
export type ProjectSettings = {
|
|
45
|
+
url: string;
|
|
46
|
+
space: string;
|
|
47
|
+
project: string;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
type PromptOptions = ProjectSettings & {
|
|
51
|
+
locations: Array<string>;
|
|
52
|
+
privateLocations: Array<string>;
|
|
41
53
|
schedule: number;
|
|
42
54
|
};
|
|
43
55
|
|
|
44
56
|
// exported for testing
|
|
45
|
-
export const
|
|
46
|
-
'.github/workflows/run-synthetics.yml',
|
|
47
|
-
'README.md',
|
|
57
|
+
export const REGULAR_FILES_PATH = [
|
|
48
58
|
'journeys/example.journey.ts',
|
|
49
|
-
'journeys/advanced-example.journey.ts',
|
|
50
59
|
'journeys/advanced-example-helpers.ts',
|
|
60
|
+
'journeys/advanced-example.journey.ts',
|
|
61
|
+
'.github/workflows/run-synthetics.yml',
|
|
62
|
+
'README.md',
|
|
51
63
|
];
|
|
64
|
+
export const SETTINGS_PATH = '.synthetics/project.json';
|
|
65
|
+
export const CONFIG_PATH = 'synthetics.config.ts';
|
|
66
|
+
|
|
67
|
+
// Files to be overriden by default if the project is initialized multiple times
|
|
68
|
+
const DEFAULT_OVERRIDES = [SETTINGS_PATH, CONFIG_PATH];
|
|
52
69
|
|
|
53
70
|
export class Generator {
|
|
54
71
|
pkgManager = 'npm';
|
|
55
72
|
constructor(public projectDir: string) {}
|
|
56
73
|
|
|
57
74
|
async directory() {
|
|
75
|
+
progress(
|
|
76
|
+
`Initializing Synthetics project in '${
|
|
77
|
+
relative(process.cwd(), this.projectDir) || '.'
|
|
78
|
+
}'`
|
|
79
|
+
);
|
|
58
80
|
if (!existsSync(this.projectDir)) {
|
|
59
81
|
await mkdir(this.projectDir);
|
|
60
82
|
}
|
|
@@ -64,12 +86,58 @@ export class Generator {
|
|
|
64
86
|
if (process.env.TEST_QUESTIONS) {
|
|
65
87
|
return JSON.parse(process.env.TEST_QUESTIONS);
|
|
66
88
|
}
|
|
67
|
-
|
|
89
|
+
|
|
90
|
+
const { onCloud } = await prompt<{ onCloud: string }>({
|
|
91
|
+
type: 'confirm',
|
|
92
|
+
name: 'onCloud',
|
|
93
|
+
initial: 'y',
|
|
94
|
+
message: 'Do you use Elastic Cloud',
|
|
95
|
+
});
|
|
96
|
+
const url = await new Input({
|
|
97
|
+
header: onCloud
|
|
98
|
+
? yellow(
|
|
99
|
+
'Get cloud.id from your deployment https://www.elastic.co/guide/en/cloud/current/ec-cloud-id.html'
|
|
100
|
+
)
|
|
101
|
+
: '',
|
|
102
|
+
message: onCloud
|
|
103
|
+
? 'What is your cloud.id'
|
|
104
|
+
: 'What is the url of your Kibana instance',
|
|
105
|
+
name: 'url',
|
|
106
|
+
required: true,
|
|
107
|
+
result(value) {
|
|
108
|
+
return onCloud ? cloudIDToKibanaURL(value) : value;
|
|
109
|
+
},
|
|
110
|
+
}).run();
|
|
111
|
+
|
|
112
|
+
const auth = await new Input({
|
|
113
|
+
name: 'auth',
|
|
114
|
+
header: yellow(
|
|
115
|
+
`Generate API key from Kibana ${removeTrailingSlash(
|
|
116
|
+
url
|
|
117
|
+
)}/app/uptime/manage-monitors/all`
|
|
118
|
+
),
|
|
119
|
+
required: true,
|
|
120
|
+
message: 'What is your API key',
|
|
121
|
+
}).run();
|
|
122
|
+
|
|
123
|
+
const allLocations = await getLocations({ url, auth });
|
|
124
|
+
const locChoices = formatLocations(allLocations);
|
|
125
|
+
if (locChoices.length === 0) {
|
|
126
|
+
// TODO - Add link to docs
|
|
127
|
+
throw 'Follow the docs to set up your first private locations - <link>';
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const monitorQues = [
|
|
68
131
|
{
|
|
69
132
|
type: 'select',
|
|
70
133
|
name: 'locations',
|
|
71
|
-
|
|
72
|
-
|
|
134
|
+
hint: '(Use <space> to select, <return> to submit)',
|
|
135
|
+
message: 'Select the locations where you want to run monitors',
|
|
136
|
+
choices: locChoices,
|
|
137
|
+
multiple: true,
|
|
138
|
+
validate(value) {
|
|
139
|
+
return value.length === 0 ? `Select at least one option.` : true;
|
|
140
|
+
},
|
|
73
141
|
},
|
|
74
142
|
{
|
|
75
143
|
type: 'numeral',
|
|
@@ -77,26 +145,50 @@ export class Generator {
|
|
|
77
145
|
message: 'Set default schedule in minutes for all monitors',
|
|
78
146
|
initial: 10,
|
|
79
147
|
},
|
|
148
|
+
{
|
|
149
|
+
type: 'input',
|
|
150
|
+
name: 'project',
|
|
151
|
+
message: 'Choose project id to logically group monitors',
|
|
152
|
+
initial: basename(this.projectDir),
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
type: 'input',
|
|
156
|
+
name: 'space',
|
|
157
|
+
message: 'Choose the target Kibana space',
|
|
158
|
+
initial: 'default',
|
|
159
|
+
},
|
|
80
160
|
];
|
|
81
|
-
|
|
161
|
+
|
|
162
|
+
// Split and group private and public locations from the answered list.
|
|
163
|
+
const answers = await prompt<PromptOptions>(monitorQues);
|
|
164
|
+
const { locations, privateLocations } = groupLocations(answers.locations);
|
|
165
|
+
return { ...answers, url, locations, privateLocations };
|
|
82
166
|
}
|
|
83
167
|
|
|
84
168
|
async files(answers: PromptOptions) {
|
|
85
169
|
const fileMap = new Map<string, string>();
|
|
170
|
+
|
|
171
|
+
// Setup project.json
|
|
172
|
+
const projectJSON = {
|
|
173
|
+
url: answers.url,
|
|
174
|
+
space: answers.space,
|
|
175
|
+
project: answers.project,
|
|
176
|
+
};
|
|
177
|
+
fileMap.set(SETTINGS_PATH, JSON.stringify(projectJSON, null, 2) + '\n');
|
|
178
|
+
|
|
86
179
|
// Setup Synthetics config file
|
|
87
|
-
const configFile = 'synthetics.config.ts';
|
|
88
180
|
fileMap.set(
|
|
89
|
-
|
|
181
|
+
CONFIG_PATH,
|
|
90
182
|
replaceTemplates(
|
|
91
|
-
await readFile(join(templateDir,
|
|
183
|
+
await readFile(join(templateDir, CONFIG_PATH), 'utf-8'),
|
|
92
184
|
answers
|
|
93
185
|
)
|
|
94
186
|
);
|
|
95
187
|
|
|
96
188
|
// Setup non-templated files
|
|
97
189
|
Promise.all(
|
|
98
|
-
|
|
99
|
-
fileMap.set(
|
|
190
|
+
REGULAR_FILES_PATH.map(async file => {
|
|
191
|
+
fileMap.set(file, await readFile(join(templateDir, file), 'utf-8'));
|
|
100
192
|
})
|
|
101
193
|
).catch(e => {
|
|
102
194
|
throw e;
|
|
@@ -110,20 +202,31 @@ export class Generator {
|
|
|
110
202
|
|
|
111
203
|
async createFile(relativePath: string, content: string, override = false) {
|
|
112
204
|
const absolutePath = join(this.projectDir, relativePath);
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
205
|
+
|
|
206
|
+
if (
|
|
207
|
+
!override &&
|
|
208
|
+
!DEFAULT_OVERRIDES.includes(relativePath) &&
|
|
209
|
+
existsSync(absolutePath)
|
|
210
|
+
) {
|
|
211
|
+
const { override } = await prompt<{ override: boolean }>({
|
|
212
|
+
type: 'confirm',
|
|
213
|
+
name: 'override',
|
|
214
|
+
message: `File ${relativePath} already exists. Override it?`,
|
|
215
|
+
initial: false,
|
|
216
|
+
});
|
|
217
|
+
if (!override) return;
|
|
117
218
|
}
|
|
219
|
+
|
|
220
|
+
progress(`Writing ${relative(process.cwd(), absolutePath)}.`);
|
|
221
|
+
await mkdir(dirname(absolutePath), { recursive: true });
|
|
222
|
+
await writeFile(absolutePath, content, 'utf-8');
|
|
118
223
|
}
|
|
119
224
|
|
|
120
225
|
async package() {
|
|
121
226
|
this.pkgManager = await getPackageManager(this.projectDir);
|
|
122
227
|
const commands = new Map<string, string>();
|
|
123
228
|
commands.set(
|
|
124
|
-
`
|
|
125
|
-
this.pkgManager == 'yarn' ? 'Yarn' : 'NPM'
|
|
126
|
-
}`,
|
|
229
|
+
`Setting up project using ${this.pkgManager == 'yarn' ? 'Yarn' : 'NPM'}`,
|
|
127
230
|
this.pkgManager == 'yarn' ? 'yarn init -y' : 'npm init -y'
|
|
128
231
|
);
|
|
129
232
|
|
|
@@ -131,8 +234,8 @@ export class Generator {
|
|
|
131
234
|
commands.set(
|
|
132
235
|
`Installing @elastic/synthetics library`,
|
|
133
236
|
this.pkgManager == 'yarn'
|
|
134
|
-
? `yarn add -dev ${pkgName}`
|
|
135
|
-
: `npm i -d ${pkgName}`
|
|
237
|
+
? `yarn add -dev ${pkgName} --silent`
|
|
238
|
+
: `npm i -d ${pkgName} --quiet`
|
|
136
239
|
);
|
|
137
240
|
|
|
138
241
|
// Execute commands
|
|
@@ -158,8 +261,7 @@ export class Generator {
|
|
|
158
261
|
pkgJSON.scripts.test = 'npx @elastic/synthetics journeys';
|
|
159
262
|
|
|
160
263
|
// Add push command
|
|
161
|
-
|
|
162
|
-
pkgJSON.scripts.push = `npx @elastic/synthetics push journeys --project ${project} --url http://localhost:5601 --auth apiKey`;
|
|
264
|
+
pkgJSON.scripts.push = `npx @elastic/synthetics push --auth $SYNTHETICS_API_KEY`;
|
|
163
265
|
|
|
164
266
|
await this.createFile(
|
|
165
267
|
filename,
|
|
@@ -189,10 +291,12 @@ All set, you can run below commands inside: ${this.projectDir}:
|
|
|
189
291
|
|
|
190
292
|
Run synthetic tests: ${cyan(runCommand(this.pkgManager, 'test'))}
|
|
191
293
|
|
|
192
|
-
Push monitors to Kibana: ${cyan(
|
|
294
|
+
Push monitors to Kibana: ${cyan(
|
|
295
|
+
'SYNTHETICS_API_KEY=<value> ' + runCommand(this.pkgManager, 'push')
|
|
296
|
+
)}
|
|
193
297
|
|
|
194
298
|
${yellow(
|
|
195
|
-
'Make sure to
|
|
299
|
+
'Make sure to configure the SYNTHETICS_API_KEY before pushing monitors to Kibana.'
|
|
196
300
|
)}
|
|
197
301
|
|
|
198
302
|
Visit https://www.elastic.co/guide/en/observability/current/synthetic-run-tests.html to learn more.
|
package/src/generator/utils.ts
CHANGED
|
@@ -40,10 +40,15 @@ export function runCommand(pkgManager: string, command: string) {
|
|
|
40
40
|
return `npm run ${command}`;
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
export function replaceTemplates(input: string,
|
|
44
|
-
for (const key in
|
|
45
|
-
const finalValue =
|
|
43
|
+
export function replaceTemplates(input: string, values: Record<string, any>) {
|
|
44
|
+
for (const key in values) {
|
|
45
|
+
const finalValue = values[key];
|
|
46
46
|
input = input.replace(new RegExp(`'{{` + key + `}}'`, 'g'), () => {
|
|
47
|
+
if (Array.isArray(finalValue)) {
|
|
48
|
+
return String(finalValue)
|
|
49
|
+
.split(',')
|
|
50
|
+
.map(f => `'${f}'`);
|
|
51
|
+
}
|
|
47
52
|
if (typeof finalValue == 'number') {
|
|
48
53
|
return Number(finalValue);
|
|
49
54
|
}
|
|
@@ -52,3 +57,24 @@ export function replaceTemplates(input: string, literals: Record<string, any>) {
|
|
|
52
57
|
}
|
|
53
58
|
return input;
|
|
54
59
|
}
|
|
60
|
+
|
|
61
|
+
export function cloudIDToKibanaURL(id: string) {
|
|
62
|
+
const ID_SEPARATOR = ':';
|
|
63
|
+
const VALUE_SEPARATOR = '$';
|
|
64
|
+
// Ignore values before the separator
|
|
65
|
+
const index = id.lastIndexOf(ID_SEPARATOR);
|
|
66
|
+
if (index >= 0) {
|
|
67
|
+
id = id.substring(index + 1);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Decode the base64 string
|
|
71
|
+
const decoded = Buffer.from(id, 'base64').toString();
|
|
72
|
+
const words = decoded.split(VALUE_SEPARATOR);
|
|
73
|
+
|
|
74
|
+
// construct the kibana and ES urls from decoded value
|
|
75
|
+
const [host, port = '443'] = words[0].split(ID_SEPARATOR);
|
|
76
|
+
const [kibanaId, kibanaPort = port] = words[2].split(ID_SEPARATOR);
|
|
77
|
+
const kibanaURL = `https://${kibanaId}.${host}:${kibanaPort}`;
|
|
78
|
+
|
|
79
|
+
return kibanaURL;
|
|
80
|
+
}
|