@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.
Files changed (72) hide show
  1. package/dist/cli.js +51 -26
  2. package/dist/cli.js.map +1 -1
  3. package/dist/common_types.d.ts +2 -0
  4. package/dist/common_types.d.ts.map +1 -1
  5. package/dist/core/runner.d.ts.map +1 -1
  6. package/dist/core/runner.js +4 -3
  7. package/dist/core/runner.js.map +1 -1
  8. package/dist/dsl/monitor.d.ts +2 -1
  9. package/dist/dsl/monitor.d.ts.map +1 -1
  10. package/dist/dsl/monitor.js +2 -2
  11. package/dist/dsl/monitor.js.map +1 -1
  12. package/dist/generator/index.d.ts +11 -3
  13. package/dist/generator/index.d.ts.map +1 -1
  14. package/dist/generator/index.js +98 -27
  15. package/dist/generator/index.js.map +1 -1
  16. package/dist/generator/utils.d.ts +2 -1
  17. package/dist/generator/utils.d.ts.map +1 -1
  18. package/dist/generator/utils.js +27 -4
  19. package/dist/generator/utils.js.map +1 -1
  20. package/dist/helpers.d.ts +9 -5
  21. package/dist/helpers.d.ts.map +1 -1
  22. package/dist/helpers.js +37 -10
  23. package/dist/helpers.js.map +1 -1
  24. package/dist/locations/index.d.ts +45 -0
  25. package/dist/locations/index.d.ts.map +1 -0
  26. package/dist/locations/index.js +90 -0
  27. package/dist/locations/index.js.map +1 -0
  28. package/dist/{dsl/locations.d.ts → locations/public-locations.d.ts} +1 -1
  29. package/dist/locations/public-locations.d.ts.map +1 -0
  30. package/dist/{dsl/locations.js → locations/public-locations.js} +1 -1
  31. package/dist/locations/public-locations.js.map +1 -0
  32. package/dist/options.d.ts +7 -0
  33. package/dist/options.d.ts.map +1 -1
  34. package/dist/options.js +21 -1
  35. package/dist/options.js.map +1 -1
  36. package/dist/push/bundler.d.ts.map +1 -1
  37. package/dist/push/bundler.js +7 -2
  38. package/dist/push/bundler.js.map +1 -1
  39. package/dist/push/index.d.ts +6 -1
  40. package/dist/push/index.d.ts.map +1 -1
  41. package/dist/push/index.js +124 -5
  42. package/dist/push/index.js.map +1 -1
  43. package/dist/push/monitor.d.ts +2 -0
  44. package/dist/push/monitor.d.ts.map +1 -1
  45. package/dist/push/monitor.js +23 -7
  46. package/dist/push/monitor.js.map +1 -1
  47. package/dist/push/request.d.ts +8 -7
  48. package/dist/push/request.d.ts.map +1 -1
  49. package/dist/push/request.js +21 -22
  50. package/dist/push/request.js.map +1 -1
  51. package/dist/tsconfig.tsbuildinfo +1 -1
  52. package/package.json +1 -1
  53. package/src/cli.ts +79 -59
  54. package/src/common_types.ts +2 -0
  55. package/src/core/runner.ts +5 -6
  56. package/src/dsl/monitor.ts +2 -1
  57. package/src/generator/index.ts +138 -34
  58. package/src/generator/utils.ts +29 -3
  59. package/src/helpers.ts +35 -11
  60. package/src/locations/index.ts +108 -0
  61. package/src/{dsl/locations.ts → locations/public-locations.ts} +0 -0
  62. package/src/options.ts +38 -0
  63. package/src/push/bundler.ts +7 -2
  64. package/src/push/index.ts +150 -7
  65. package/src/push/monitor.ts +37 -9
  66. package/src/push/request.ts +26 -34
  67. package/templates/journeys/advanced-example-helpers.ts +9 -9
  68. package/templates/journeys/advanced-example.journey.ts +6 -6
  69. package/templates/journeys/example.journey.ts +1 -1
  70. package/templates/synthetics.config.ts +2 -1
  71. package/dist/dsl/locations.d.ts.map +0 -1
  72. 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 { normalizeOptions, parseThrottling } from './options';
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 { push } from './push';
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
- .option(
53
- '-p, --params <jsonstring>',
54
- 'JSON object that gets injected to all journeys',
55
- JSON.parse
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
- .option(
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
- .option(
155
- '--pattern <pattern>',
156
- 'RegExp file patterns to push inside current directory'
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
- .requiredOption(
178
- '--project <project-id>',
179
- 'id that will be used for logically grouping monitors'
170
+ .option(
171
+ '--private-locations <locations...>',
172
+ 'default list of private locations from which your monitors will run.'
180
173
  )
181
- .requiredOption('--url <url>', 'kibana URL to upload the monitors')
182
- .requiredOption(
183
- '--auth <auth>',
184
- 'API key used for Kibana authentication(https://www.elastic.co/guide/en/kibana/master/api-keys.html).'
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({ ...program.opts(), ...cmdOpts });
195
- if (!options.schedule) {
196
- throw error(`Set default schedule in minutes for all monitors via '--schedule <time-in-minutes>' OR
197
- configure via Synthetics config file under 'monitors.schedule' field.`);
198
- }
199
-
200
- if (!options.locations) {
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, cmdOpts);
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 <project-dir>')
215
- .description('Initalize Elastic synthetics project')
216
- .action(async (dir: string) => {
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);
@@ -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;
@@ -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 mkdirAsync(this.screenshotPath, { recursive: true });
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 writeFileAsync(
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 mkdirAsync(CACHE_PATH, { recursive: true });
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
  });
@@ -31,7 +31,7 @@ import {
31
31
  Params,
32
32
  PlaywrightOptions,
33
33
  } from '../common_types';
34
- import { LocationsMap } from './locations';
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;
@@ -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
- import { prompt } from 'enquirer';
31
- import { SyntheticsLocations } from '../dsl/monitor';
32
- import { progress, write as stdWrite } from '../helpers';
33
- import { getPackageManager, replaceTemplates, runCommand } from './utils';
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 PromptOptions = {
40
- locations: string;
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 regularFiles = [
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
- const question = [
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
- message: 'Select the default location where you want to run monitors.',
72
- choices: SyntheticsLocations,
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
- return await prompt<PromptOptions>(question);
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
- configFile,
181
+ CONFIG_PATH,
90
182
  replaceTemplates(
91
- await readFile(join(templateDir, configFile), 'utf-8'),
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
- regularFiles.map(async fn => {
99
- fileMap.set(fn, await readFile(join(templateDir, fn), 'utf-8'));
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
- if (override || !existsSync(absolutePath)) {
114
- progress(`Writing ${relative(process.cwd(), absolutePath)}.`);
115
- await mkdir(dirname(absolutePath), { recursive: true });
116
- await writeFile(absolutePath, content, 'utf-8');
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
- `Initializing Synthetics project using ${
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
- const project = basename(this.projectDir);
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(runCommand(this.pkgManager, 'push'))}
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 update the Kibana url and api keys before pushing monitors to Kibana.'
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.
@@ -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, literals: Record<string, any>) {
44
- for (const key in literals) {
45
- const finalValue = literals[key];
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
+ }