@elastic/synthetics 1.0.0-beta.26 → 1.0.0-beta.29

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 (67) hide show
  1. package/README.md +1 -1
  2. package/dist/cli.js +24 -14
  3. package/dist/cli.js.map +1 -1
  4. package/dist/common_types.d.ts +0 -1
  5. package/dist/common_types.d.ts.map +1 -1
  6. package/dist/core/gatherer.js +2 -2
  7. package/dist/core/gatherer.js.map +1 -1
  8. package/dist/core/runner.d.ts.map +1 -1
  9. package/dist/core/runner.js +5 -1
  10. package/dist/core/runner.js.map +1 -1
  11. package/dist/dsl/locations.d.ts +37 -0
  12. package/dist/dsl/locations.d.ts.map +1 -0
  13. package/dist/dsl/locations.js +41 -0
  14. package/dist/dsl/locations.js.map +1 -0
  15. package/dist/dsl/monitor.d.ts +3 -2
  16. package/dist/dsl/monitor.d.ts.map +1 -1
  17. package/dist/dsl/monitor.js +2 -1
  18. package/dist/dsl/monitor.js.map +1 -1
  19. package/dist/generator/index.d.ts +9 -1
  20. package/dist/generator/index.d.ts.map +1 -1
  21. package/dist/generator/index.js +57 -11
  22. package/dist/generator/index.js.map +1 -1
  23. package/dist/generator/utils.d.ts +1 -0
  24. package/dist/generator/utils.d.ts.map +1 -1
  25. package/dist/generator/utils.js +14 -1
  26. package/dist/generator/utils.js.map +1 -1
  27. package/dist/options.d.ts +0 -5
  28. package/dist/options.d.ts.map +1 -1
  29. package/dist/options.js +20 -28
  30. package/dist/options.js.map +1 -1
  31. package/dist/push/bundler.d.ts.map +1 -1
  32. package/dist/push/bundler.js +4 -9
  33. package/dist/push/bundler.js.map +1 -1
  34. package/dist/push/index.d.ts.map +1 -1
  35. package/dist/push/index.js +1 -4
  36. package/dist/push/index.js.map +1 -1
  37. package/dist/push/monitor.d.ts.map +1 -1
  38. package/dist/push/monitor.js +2 -7
  39. package/dist/push/monitor.js.map +1 -1
  40. package/dist/push/plugin.d.ts.map +1 -1
  41. package/dist/push/plugin.js +1 -0
  42. package/dist/push/plugin.js.map +1 -1
  43. package/dist/push/request.d.ts +0 -1
  44. package/dist/push/request.d.ts.map +1 -1
  45. package/dist/push/request.js +6 -17
  46. package/dist/push/request.js.map +1 -1
  47. package/dist/tsconfig.tsbuildinfo +1 -1
  48. package/package.json +6 -4
  49. package/src/cli.ts +37 -17
  50. package/src/common_types.ts +0 -1
  51. package/src/core/gatherer.ts +2 -2
  52. package/src/core/runner.ts +8 -1
  53. package/src/dsl/locations.ts +38 -0
  54. package/src/dsl/monitor.ts +6 -2
  55. package/src/generator/index.ts +70 -17
  56. package/src/generator/utils.ts +13 -0
  57. package/src/options.ts +23 -29
  58. package/src/push/bundler.ts +4 -8
  59. package/src/push/index.ts +2 -6
  60. package/src/push/monitor.ts +2 -11
  61. package/src/push/plugin.ts +1 -0
  62. package/src/push/request.ts +6 -18
  63. package/templates/.github/workflows/run-synthetics.yml +26 -0
  64. package/templates/README.md +21 -0
  65. package/templates/journeys/advanced-example-helpers.ts +54 -0
  66. package/templates/journeys/advanced-example.journey.ts +43 -0
  67. package/templates/synthetics.config.ts +7 -0
package/src/cli.ts CHANGED
@@ -26,6 +26,7 @@
26
26
  */
27
27
 
28
28
  import { program, Option } from 'commander';
29
+ import { cwd } from 'process';
29
30
  import { CliArgs, PushOptions } from './common_types';
30
31
  import { reporters } from './reporters';
31
32
  import { normalizeOptions, parseThrottling } from './options';
@@ -65,7 +66,7 @@ program
65
66
  .option('--inline', 'Run inline journeys from heartbeat')
66
67
  .option('-r, --require <modules...>', 'module(s) to preload')
67
68
  .option('--no-headless', 'run browser in headful mode')
68
- .option('--sandbox', 'enable chromium sandboxing', false)
69
+ .option('--sandbox', 'enable chromium sandboxing')
69
70
  .option('--rich-events', 'Mimics a heartbeat run')
70
71
  .option(
71
72
  '--capability <features...>',
@@ -81,12 +82,12 @@ program
81
82
  "don't actually execute anything, report only registered journeys"
82
83
  )
83
84
  .option(
84
- '--match <name>',
85
- 'run only journeys with a name or tags that matches the glob'
85
+ '--tags <name...>',
86
+ 'run only journeys with a tag that matches the glob'
86
87
  )
87
88
  .option(
88
- '--tags <name...>',
89
- 'run only journeys with the given tag(s), or globs'
89
+ '--match <name>',
90
+ 'run only journeys with a name or tag that matches the glob'
90
91
  )
91
92
  .option(
92
93
  '--outfd <fd>',
@@ -146,9 +147,21 @@ program
146
147
 
147
148
  // Push command
148
149
  program
149
- .command('push [files...]')
150
+ .command('push')
150
151
  .description(
151
- 'Push monitors to create new montors with Kibana monitor management UI'
152
+ 'Push journeys in the current directory to create monitors within the Kibana monitor management UI'
153
+ )
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'
152
165
  )
153
166
  .option(
154
167
  '--schedule <time-in-minutes>',
@@ -158,33 +171,40 @@ program
158
171
  .addOption(
159
172
  new Option(
160
173
  '--locations <locations...>',
161
- 'The default list of locations from which your monitors will run.'
174
+ 'default list of locations from which your monitors will run.'
162
175
  ).choices(SyntheticsLocations)
163
176
  )
164
177
  .requiredOption(
165
- '--project <id/name>',
166
- 'project/repository that will be used for grouping the monitors.'
178
+ '--project <project-id>',
179
+ 'id that will be used for logically grouping monitors'
167
180
  )
168
181
  .requiredOption('--url <url>', 'kibana URL to upload the monitors')
169
182
  .requiredOption(
170
183
  '--auth <auth>',
171
- 'user authentication (in user:password format) or API key that will be used to communicate with Kibana.'
184
+ 'API key used for Kibana authentication(https://www.elastic.co/guide/en/kibana/master/api-keys.html).'
172
185
  )
173
186
  .option(
174
187
  '--space <space>',
175
188
  'the target Kibana spaces for the pushed monitors — spaces help you organise pushed monitors.',
176
189
  'default'
177
190
  )
178
- .option('--delete', 'automatically delete the stale monitors.')
179
- .action(async (files, cmdOpts: PushOptions) => {
191
+ .action(async (cmdOpts: PushOptions) => {
180
192
  try {
181
- const cliArgs = { inline: false };
182
- await loadTestFiles(cliArgs, files);
193
+ await loadTestFiles({ inline: false }, [cwd()]);
183
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
+ }
184
204
  const monitors = runner.buildMonitors(options);
185
205
  await push(monitors, cmdOpts);
186
206
  } catch (e) {
187
- console.error(e);
207
+ e && console.error(e);
188
208
  process.exit(1);
189
209
  }
190
210
  });
@@ -198,7 +218,7 @@ program
198
218
  const generator = await new Generator(resolve(process.cwd(), dir));
199
219
  await generator.setup();
200
220
  } catch (e) {
201
- error(e);
221
+ e && error(e);
202
222
  process.exit(1);
203
223
  }
204
224
  });
@@ -243,7 +243,6 @@ export type PushOptions = {
243
243
  url: string;
244
244
  project: string;
245
245
  space: string;
246
- delete: boolean;
247
246
  schedule?: MonitorConfig['schedule'];
248
247
  locations?: MonitorConfig['locations'];
249
248
  };
@@ -65,12 +65,12 @@ export class Gatherer {
65
65
  }
66
66
 
67
67
  static async getUserAgent(userAgent?: string) {
68
- const syntheticsIdentifier = ' Elastic/Synthetics';
69
68
  if (!userAgent) {
70
69
  const session = await Gatherer.browser.newBrowserCDPSession();
71
70
  ({ userAgent } = await session.send('Browser.getVersion'));
71
+ return userAgent + ' Elastic/Synthetics';
72
72
  }
73
- return userAgent + syntheticsIdentifier;
73
+ return userAgent;
74
74
  }
75
75
 
76
76
  static setNetworkConditions(
@@ -97,11 +97,11 @@ export default class Runner {
97
97
  }
98
98
 
99
99
  async captureScreenshot(page: Driver['page'], step: Step) {
100
- await page.waitForLoadState('load');
101
100
  const buffer = await page
102
101
  .screenshot({
103
102
  type: 'jpeg',
104
103
  quality: 80,
104
+ timeout: 5000,
105
105
  })
106
106
  .catch(() => {});
107
107
  /**
@@ -397,9 +397,16 @@ export default class Runner {
397
397
  playwrightOptions: options.playwrightOptions,
398
398
  });
399
399
 
400
+ const { match, tags } = options;
401
+
400
402
  const monitors: Monitor[] = [];
401
403
  for (const journey of this.journeys) {
404
+ if (!journey.isMatch(match, tags)) {
405
+ continue;
406
+ }
407
+
402
408
  this.#currentJourney = journey;
409
+
403
410
  /**
404
411
  * Execute dummy callback to get all monitor specific
405
412
  * configurations for the current journey
@@ -0,0 +1,38 @@
1
+ /**
2
+ * MIT License
3
+ *
4
+ * Copyright (c) 2020-present, Elastic NV
5
+ *
6
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ * of this software and associated documentation files (the "Software"), to deal
8
+ * in the Software without restriction, including without limitation the rights
9
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ * copies of the Software, and to permit persons to whom the Software is
11
+ * furnished to do so, subject to the following conditions:
12
+ *
13
+ * The above copyright notice and this permission notice shall be included in
14
+ * all copies or substantial portions of the Software.
15
+ *
16
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
+ * THE SOFTWARE.
23
+ *
24
+ */
25
+
26
+ // DO NOT EDIT - UPDATE WITH `npm run build:locations`
27
+ export const LocationsMap = {
28
+ japan: 'asia-northeast1-a',
29
+ india: 'asia-south1-a',
30
+ singapore: 'asia-southeast1-a',
31
+ australia_east: 'australia-southeast1-a',
32
+ united_kingdom: 'europe-west2-a',
33
+ germany: 'europe-west3-a',
34
+ canada_east: 'northamerica-northeast1-a',
35
+ brazil: 'southamerica-east1-a',
36
+ us_east: 'us-east4-a',
37
+ us_west: 'us-west1-a',
38
+ };
@@ -31,9 +31,13 @@ import {
31
31
  Params,
32
32
  PlaywrightOptions,
33
33
  } from '../common_types';
34
+ import { LocationsMap } from './locations';
35
+
36
+ export type SyntheticsLocationsType = keyof typeof LocationsMap;
37
+ export const SyntheticsLocations = Object.keys(
38
+ LocationsMap
39
+ ) as SyntheticsLocationsType[];
34
40
 
35
- export const SyntheticsLocations = ['US East', 'EU West'] as const;
36
- export type SyntheticsLocationsType = typeof SyntheticsLocations[number];
37
41
  export type MonitorConfig = {
38
42
  id?: string;
39
43
  name?: string;
@@ -27,13 +27,29 @@ 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';
30
32
  import { progress, write as stdWrite } from '../helpers';
31
- import { getPackageManager, runCommand } from './utils';
33
+ import { getPackageManager, replaceTemplates, runCommand } from './utils';
32
34
 
33
35
  // Templates that are required for setting up new
34
36
  // synthetics project
35
37
  const templateDir = join(__dirname, '..', '..', 'templates');
36
38
 
39
+ type PromptOptions = {
40
+ locations: string;
41
+ schedule: number;
42
+ };
43
+
44
+ // exported for testing
45
+ export const regularFiles = [
46
+ '.github/workflows/run-synthetics.yml',
47
+ 'README.md',
48
+ 'journeys/example.journey.ts',
49
+ 'journeys/advanced-example.journey.ts',
50
+ 'journeys/advanced-example-helpers.ts',
51
+ ];
52
+
37
53
  export class Generator {
38
54
  pkgManager = 'npm';
39
55
  constructor(public projectDir: string) {}
@@ -44,26 +60,47 @@ export class Generator {
44
60
  }
45
61
  }
46
62
 
47
- async files() {
63
+ async questions() {
64
+ if (process.env.TEST_QUESTIONS) {
65
+ return JSON.parse(process.env.TEST_QUESTIONS);
66
+ }
67
+ const question = [
68
+ {
69
+ type: 'select',
70
+ name: 'locations',
71
+ message: 'Select the default location where you want to run monitors.',
72
+ choices: SyntheticsLocations,
73
+ },
74
+ {
75
+ type: 'numeral',
76
+ name: 'schedule',
77
+ message: 'Set default schedule in minutes for all monitors',
78
+ initial: 10,
79
+ },
80
+ ];
81
+ return await prompt<PromptOptions>(question);
82
+ }
83
+
84
+ async files(answers: PromptOptions) {
48
85
  const fileMap = new Map<string, string>();
49
86
  // Setup Synthetics config file
50
87
  const configFile = 'synthetics.config.ts';
51
88
  fileMap.set(
52
89
  configFile,
53
- await readFile(join(templateDir, configFile), 'utf-8')
90
+ replaceTemplates(
91
+ await readFile(join(templateDir, configFile), 'utf-8'),
92
+ answers
93
+ )
54
94
  );
55
95
 
56
- // Setup example journey file
57
- const journeyFile = 'journeys/example.journey.ts';
58
- fileMap.set(
59
- journeyFile,
60
- await readFile(join(templateDir, journeyFile), 'utf-8')
61
- );
62
- // Setup gitignore
63
- fileMap.set(
64
- '.gitignore',
65
- await readFile(join(templateDir, '.gitignore'), 'utf-8')
66
- );
96
+ // Setup non-templated files
97
+ Promise.all(
98
+ regularFiles.map(async fn => {
99
+ fileMap.set(fn, await readFile(join(templateDir, fn), 'utf-8'));
100
+ })
101
+ ).catch(e => {
102
+ throw e;
103
+ });
67
104
 
68
105
  // Create files
69
106
  for (const [relativePath, content] of fileMap) {
@@ -122,7 +159,7 @@ export class Generator {
122
159
 
123
160
  // Add push command
124
161
  const project = basename(this.projectDir);
125
- pkgJSON.scripts.push = `npx @elastic/synthetics push journeys --project ${project} --url http://localhost:5601 --auth basic-auth`;
162
+ pkgJSON.scripts.push = `npx @elastic/synthetics push journeys --project ${project} --url http://localhost:5601 --auth apiKey`;
126
163
 
127
164
  await this.createFile(
128
165
  filename,
@@ -131,6 +168,20 @@ export class Generator {
131
168
  );
132
169
  }
133
170
 
171
+ async patchGitIgnore() {
172
+ const gitIgnorePath = join(this.projectDir, '.gitignore');
173
+ let gitIgnore = '';
174
+ if (existsSync(gitIgnorePath)) {
175
+ const contents = await readFile(gitIgnorePath, 'utf-8');
176
+ gitIgnore += contents.trimEnd() + '\n';
177
+ }
178
+ if (!gitIgnore.includes('node_modules')) {
179
+ gitIgnore += 'node_modules/\n';
180
+ }
181
+ gitIgnore += '.synthetics/\n';
182
+ await writeFile(gitIgnorePath, gitIgnore, 'utf-8');
183
+ }
184
+
134
185
  banner() {
135
186
  stdWrite(
136
187
  bold(`
@@ -141,7 +192,7 @@ All set, you can run below commands inside: ${this.projectDir}:
141
192
  Push monitors to Kibana: ${cyan(runCommand(this.pkgManager, 'push'))}
142
193
 
143
194
  ${yellow(
144
- 'Make sure to update the Kibana url and authentication info before pushing monitors to Kibana.'
195
+ 'Make sure to update the Kibana url and api keys before pushing monitors to Kibana.'
145
196
  )}
146
197
 
147
198
  Visit https://www.elastic.co/guide/en/observability/master/synthetics-journeys.html to learn more.
@@ -151,9 +202,11 @@ Visit https://www.elastic.co/guide/en/observability/master/synthetics-journeys.h
151
202
 
152
203
  async setup() {
153
204
  await this.directory();
205
+ const answers = await this.questions();
154
206
  await this.package();
155
- await this.files();
207
+ await this.files(answers);
156
208
  await this.patchPkgJSON();
209
+ await this.patchGitIgnore();
157
210
  this.banner();
158
211
  }
159
212
  }
@@ -39,3 +39,16 @@ export function runCommand(pkgManager: string, command: string) {
39
39
  }
40
40
  return `npm run ${command}`;
41
41
  }
42
+
43
+ export function replaceTemplates(input: string, literals: Record<string, any>) {
44
+ for (const key in literals) {
45
+ const finalValue = literals[key];
46
+ input = input.replace(new RegExp(`'{{` + key + `}}'`, 'g'), () => {
47
+ if (typeof finalValue == 'number') {
48
+ return Number(finalValue);
49
+ }
50
+ return finalValue;
51
+ });
52
+ }
53
+ return input;
54
+ }
package/src/options.ts CHANGED
@@ -27,7 +27,6 @@ import merge from 'deepmerge';
27
27
  import { readConfig } from './config';
28
28
  import { getNetworkConditions, DEFAULT_THROTTLING_OPTIONS } from './helpers';
29
29
  import type { CliArgs, RunOptions, ThrottlingOptions } from './common_types';
30
- import { MonitorConfig } from './dsl/monitor';
31
30
 
32
31
  /**
33
32
  * Set debug based on DEBUG ENV and -d flags
@@ -100,21 +99,30 @@ export function normalizeOptions(cliArgs: CliArgs): RunOptions {
100
99
  */
101
100
  options.params = Object.freeze(merge(config.params, cliArgs.params || {}));
102
101
 
103
- options.playwrightOptions = merge.all([
104
- config.playwrightOptions || {},
105
- cliArgs.playwrightOptions || {},
106
- {
107
- headless: cliArgs.headless,
108
- chromiumSandbox: cliArgs.sandbox,
109
- ignoreHTTPSErrors: cliArgs.ignoreHttpsErrors,
110
- },
111
- ]);
102
+ /**
103
+ * Merge playwright options from CLI and Synthetics config
104
+ * and prefer individual options over other option
105
+ */
106
+ const playwrightOpts = merge(
107
+ config.playwrightOptions,
108
+ cliArgs.playwrightOptions || {}
109
+ );
110
+ options.playwrightOptions = {
111
+ ...playwrightOpts,
112
+ headless: cliArgs.headless,
113
+ chromiumSandbox: cliArgs.sandbox ?? playwrightOpts?.chromiumSandbox,
114
+ ignoreHTTPSErrors:
115
+ cliArgs.ignoreHttpsErrors ?? playwrightOpts?.ignoreHTTPSErrors,
116
+ };
112
117
 
113
- const defaults = getDefaultMonitorConfig();
118
+ /**
119
+ * Get the default monitor config from synthetics.config.ts file
120
+ */
121
+ const monitor = config.monitor;
114
122
  if (cliArgs.throttling) {
115
123
  const throttleConfig = merge.all([
116
- defaults.throttling,
117
- config.monitor?.throttling || {},
124
+ DEFAULT_THROTTLING_OPTIONS,
125
+ monitor?.throttling || {},
118
126
  cliArgs.throttling as ThrottlingOptions,
119
127
  ]);
120
128
  options.throttling = throttleConfig;
@@ -126,26 +134,12 @@ export function normalizeOptions(cliArgs: CliArgs): RunOptions {
126
134
  options.throttling = {};
127
135
  }
128
136
 
129
- options.locations =
130
- cliArgs.locations ?? config.monitor?.locations ?? defaults.locations;
131
-
132
- options.schedule =
133
- cliArgs.schedule ?? config.monitor?.schedule ?? defaults.schedule;
137
+ options.schedule = cliArgs.schedule ?? monitor?.schedule;
138
+ options.locations = cliArgs.locations ?? monitor?.locations;
134
139
 
135
140
  return options;
136
141
  }
137
142
 
138
- /**
139
- * Get the default monitor configuration for all journeys
140
- */
141
- export function getDefaultMonitorConfig(): MonitorConfig {
142
- return {
143
- throttling: DEFAULT_THROTTLING_OPTIONS,
144
- locations: ['US East'],
145
- schedule: 10,
146
- };
147
- }
148
-
149
143
  /**
150
144
  * Parses the throttling CLI settings and also
151
145
  * adapts to the format to keep the backwards compatability
@@ -52,15 +52,11 @@ export class Bundler {
52
52
  plugins: [MultiAssetPlugin(addToMap)],
53
53
  },
54
54
  };
55
- try {
56
- const result = await esbuild.build(options);
57
- if (result.errors.length > 0) {
58
- throw result.errors;
59
- }
60
- this.moduleMap.set(absPath, result.outputFiles[0].text);
61
- } catch (e) {
62
- console.error(e);
55
+ const result = await esbuild.build(options);
56
+ if (result.errors.length > 0) {
57
+ throw result.errors;
63
58
  }
59
+ this.moduleMap.set(absPath, result.outputFiles[0].text);
64
60
  }
65
61
 
66
62
  async zip(outputPath: string) {
package/src/push/index.ts CHANGED
@@ -28,13 +28,12 @@ import {
28
28
  ok,
29
29
  formatAPIError,
30
30
  formatFailedMonitors,
31
- formatStaleMonitors,
32
31
  formatNotFoundError,
33
32
  } from './request';
34
33
  import { Monitor } from '../dsl/monitor';
35
34
  import { PushOptions } from '../common_types';
36
35
  import { buildMonitorSchema } from './monitor';
37
- import { progress, error, done, write } from '../helpers';
36
+ import { progress, error, done } from '../helpers';
38
37
 
39
38
  export async function push(monitors: Monitor[], options: PushOptions) {
40
39
  progress(`preparing all monitors`);
@@ -49,13 +48,10 @@ export async function push(monitors: Monitor[], options: PushOptions) {
49
48
  const { error, message } = await body.json();
50
49
  throw formatAPIError(statusCode, error, message);
51
50
  }
52
- const { staleMonitors, failedMonitors } = await body.json();
51
+ const { failedMonitors } = await body.json();
53
52
  if (failedMonitors.length > 0) {
54
53
  throw formatFailedMonitors(failedMonitors);
55
54
  }
56
- if (staleMonitors.length > 0) {
57
- write(formatStaleMonitors());
58
- }
59
55
  done('Pushed');
60
56
  } catch (e) {
61
57
  error(e);
@@ -27,11 +27,8 @@ import { mkdir } from 'fs/promises';
27
27
  import { join } from 'path';
28
28
  import { Bundler } from './bundler';
29
29
  import { CACHE_PATH } from '../helpers';
30
- import {
31
- Monitor,
32
- MonitorConfig,
33
- SyntheticsLocationsType,
34
- } from '../dsl/monitor';
30
+ import { LocationsMap } from '../dsl/locations';
31
+ import { Monitor, MonitorConfig } from '../dsl/monitor';
35
32
 
36
33
  export type MonitorSchema = Omit<MonitorConfig, 'locations'> & {
37
34
  content: string;
@@ -39,12 +36,6 @@ export type MonitorSchema = Omit<MonitorConfig, 'locations'> & {
39
36
  filter: Monitor['filter'];
40
37
  };
41
38
 
42
- // Internal representation of Locations that would be used when
43
- // talking to the Kibana API
44
- const LocationsMap: Record<SyntheticsLocationsType, string> = {
45
- 'US East': 'us-east4-a',
46
- 'EU West': 'europe-west2-a',
47
- };
48
39
  function translateLocation(locations: MonitorConfig['locations']) {
49
40
  return locations.map(loc => LocationsMap[loc]).filter(Boolean);
50
41
  }
@@ -33,6 +33,7 @@ export function commonOptions(): esbuild.BuildOptions {
33
33
  external: ['node_modules', '@elastic/synthetics'],
34
34
  minify: false,
35
35
  platform: 'node',
36
+ logLevel: 'silent',
36
37
  format: 'esm',
37
38
  write: false,
38
39
  outExtension: {
@@ -23,7 +23,7 @@
23
23
  *
24
24
  */
25
25
 
26
- import { bold, gray, yellow } from 'kleur/colors';
26
+ import { bold } from 'kleur/colors';
27
27
  import { request } from 'undici';
28
28
  import { PushOptions } from '../common_types';
29
29
  import { indent, symbols } from '../helpers';
@@ -38,16 +38,11 @@ export type APISchema = {
38
38
  monitors: MonitorSchema[];
39
39
  };
40
40
 
41
- function encodeAuth(auth: string) {
42
- if (auth.includes(':')) {
43
- return Buffer.from(auth).toString('base64');
44
- }
45
- return auth;
46
- }
47
-
48
41
  function getAPIUrl(options: PushOptions) {
42
+ // remove trialing / as well
49
43
  return (
50
- options.url + `/s/${options.space}/api/synthetics/service/project/monitors`
44
+ options.url.replace(/\/+$/, '') +
45
+ `/s/${options.space}/api/synthetics/service/project/monitors`
51
46
  );
52
47
  }
53
48
 
@@ -61,7 +56,7 @@ export async function createMonitors(
61
56
  ) {
62
57
  const schema: APISchema = {
63
58
  project: options.project,
64
- keep_stale: !options.delete,
59
+ keep_stale: false,
65
60
  monitors,
66
61
  };
67
62
 
@@ -69,7 +64,7 @@ export async function createMonitors(
69
64
  method: 'PUT',
70
65
  body: JSON.stringify(schema),
71
66
  headers: {
72
- authorization: `Basic ${encodeAuth(options.auth)}`,
67
+ authorization: `ApiKey ${options.auth}`,
73
68
  'content-type': 'application/json',
74
69
  'user-agent': `Elastic/Synthetics ${version}`,
75
70
  'kbn-xsrf': 'true',
@@ -114,10 +109,3 @@ export function formatFailedMonitors(errors: APIMonitorError[]) {
114
109
  }
115
110
  return outer;
116
111
  }
117
-
118
- export function formatStaleMonitors() {
119
- let outer = bold(yellow(`${symbols['warning']} Found stale monitors\n`));
120
- const inner = gray(`Pass --delete to remove all the stale monitors.`);
121
- outer += indent(inner, ' ');
122
- return outer;
123
- }
@@ -0,0 +1,26 @@
1
+ name: Elastic Synthetics Tests
2
+ on:
3
+ push:
4
+ branches: [main, master]
5
+ pull_request:
6
+ branches: [main, master]
7
+ schedule:
8
+ - cron: '* 0 * * *'
9
+ jobs:
10
+ test:
11
+ runs-on: ubuntu-latest
12
+ steps:
13
+ - uses: actions/checkout@v2
14
+ - uses: actions/setup-node@v2
15
+ with:
16
+ node-version: 16
17
+ - run: npm install
18
+ - run: "SYNTHETICS_JUNIT_FILE='junit-synthetics.xml' npx @elastic/synthetics . --reporter=junit"
19
+ continue-on-error: true
20
+ - name: Publish Unit Test Results
21
+ uses: EnricoMi/publish-unit-test-result-action@v1.19
22
+ if: always()
23
+ with:
24
+ files: '**/junit-*.xml'
25
+ fail_on: 'test failures'
26
+ check_name: Elastic Synthetics Tests
@@ -0,0 +1,21 @@
1
+ ## Get started with Elastic Synthetics!
2
+
3
+ Browse the directory structure here to see an example skeleton of an [Elastic Synthetics](https://www.elastic.co/observability/synthetic-monitoring) project.
4
+ Make sure to [read the docs](https://www.elastic.co/guide/en/observability/current/monitor-uptime-synthetics.html) as well if you're unfamiliar with Elastic Synthetics.
5
+
6
+ This directory was created with the command `npx @elastic/synthetics <target-directory>`, with `<target-directory>` being the name of the directory you'd like it to make.
7
+ You can always use this command to scafold new Synthetics projects.
8
+
9
+ Key places to look:
10
+
11
+ - The `synthetics.config.ts` file contains configuration for your project.
12
+ - The `journeys` directory contains both basic and more advanced examples of using synthetics. It tests a publicly hosted [Todos List](https://elastic.github.io/synthetics-demo/).
13
+ - The `.github` directory contains an example github action, demonstrating the use of a CI service for automatically running tests on merges and PR creation.
14
+
15
+ To run the tests locally, use the following command:
16
+
17
+ ```bash
18
+ npm run test
19
+ ```
20
+
21
+ Curious to learn more? [Read the docs](https://www.elastic.co/guide/en/observability/current/monitor-uptime-synthetics.html) to learn about pushing configurations to an Elastic cloud stack and more.