@elastic/synthetics 1.0.0-beta.30 → 1.0.0-beta.33
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 +4 -1
- package/dist/common_types.d.ts.map +1 -1
- package/dist/core/expect.d.ts +25 -25
- package/dist/core/expect.d.ts.map +1 -1
- package/dist/core/expect.js.map +1 -1
- package/dist/core/gatherer.d.ts.map +1 -1
- package/dist/core/gatherer.js +3 -1
- package/dist/core/gatherer.js.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/index.js +5 -1
- package/dist/dsl/index.js.map +1 -1
- package/dist/dsl/journey.d.ts +2 -1
- package/dist/dsl/journey.d.ts.map +1 -1
- package/dist/dsl/journey.js +5 -1
- package/dist/dsl/journey.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 +10 -5
- package/dist/helpers.d.ts.map +1 -1
- package/dist/helpers.js +41 -10
- package/dist/helpers.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/loader.js +11 -3
- package/dist/loader.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/plugins/index.js +5 -1
- package/dist/plugins/index.js.map +1 -1
- package/dist/push/bundler.d.ts.map +1 -1
- package/dist/push/bundler.js +12 -3
- package/dist/push/bundler.js.map +1 -1
- package/dist/push/index.d.ts +12 -1
- package/dist/push/index.d.ts.map +1 -1
- package/dist/push/index.js +137 -8
- package/dist/push/index.js.map +1 -1
- package/dist/push/monitor.d.ts +7 -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/plugin.d.ts.map +1 -1
- package/dist/push/plugin.js +11 -2
- package/dist/push/plugin.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 +22 -22
- package/dist/push/request.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +9 -9
- package/src/cli.ts +79 -59
- package/src/common_types.ts +4 -0
- package/src/core/expect.ts +58 -24
- package/src/core/gatherer.ts +9 -2
- package/src/core/runner.ts +5 -6
- package/src/dsl/journey.ts +8 -1
- 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 +40 -12
- package/src/index.ts +1 -0
- package/src/loader.ts +11 -2
- 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 +177 -10
- package/src/push/monitor.ts +38 -9
- package/src/push/plugin.ts +6 -1
- package/src/push/request.ts +27 -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/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
|
+
}
|
package/src/helpers.ts
CHANGED
|
@@ -23,11 +23,11 @@
|
|
|
23
23
|
*
|
|
24
24
|
*/
|
|
25
25
|
|
|
26
|
-
import { red, green, yellow, cyan, bold } from 'kleur/colors';
|
|
26
|
+
import { red, green, yellow, cyan, bold, grey } from 'kleur/colors';
|
|
27
27
|
import os from 'os';
|
|
28
28
|
import { resolve, join, dirname } from 'path';
|
|
29
29
|
import fs from 'fs';
|
|
30
|
-
import {
|
|
30
|
+
import { lstat, readdir } from 'fs/promises';
|
|
31
31
|
import { performance } from 'perf_hooks';
|
|
32
32
|
import sourceMapSupport from '@cspotcode/source-map-support';
|
|
33
33
|
import {
|
|
@@ -38,13 +38,6 @@ import {
|
|
|
38
38
|
ThrottlingOptions,
|
|
39
39
|
} from './common_types';
|
|
40
40
|
|
|
41
|
-
const lstatAsync = promisify(fs.lstat);
|
|
42
|
-
const readdirAsync = promisify(fs.readdir);
|
|
43
|
-
|
|
44
|
-
export const readFileAsync = promisify(fs.readFile);
|
|
45
|
-
export const writeFileAsync = promisify(fs.writeFile);
|
|
46
|
-
export const mkdirAsync = promisify(fs.mkdir);
|
|
47
|
-
|
|
48
41
|
const SEPARATOR = '\n';
|
|
49
42
|
|
|
50
43
|
export function noop() {}
|
|
@@ -177,11 +170,11 @@ export async function totalist(
|
|
|
177
170
|
pre = ''
|
|
178
171
|
) {
|
|
179
172
|
dir = resolve('.', dir);
|
|
180
|
-
await
|
|
173
|
+
await readdir(dir).then(arr => {
|
|
181
174
|
return Promise.all(
|
|
182
175
|
arr.map(str => {
|
|
183
176
|
const abs = join(dir, str);
|
|
184
|
-
return
|
|
177
|
+
return lstat(abs).then(stats =>
|
|
185
178
|
stats.isDirectory()
|
|
186
179
|
? totalist(abs, callback, join(pre, str))
|
|
187
180
|
: callback(join(pre, str), abs)
|
|
@@ -254,12 +247,18 @@ export function formatError(error: Error) {
|
|
|
254
247
|
}
|
|
255
248
|
|
|
256
249
|
const cwd = process.cwd();
|
|
250
|
+
/**
|
|
251
|
+
* All the settings that are related to the Synthetics is stored
|
|
252
|
+
* under this directory
|
|
253
|
+
* Examples: Screenshots, Project setup
|
|
254
|
+
*/
|
|
255
|
+
export const SYNTHETICS_PATH = join(cwd, '.synthetics');
|
|
257
256
|
/**
|
|
258
257
|
* Synthetics cache path that is based on the process id to make sure
|
|
259
258
|
* each process does not modify the caching layer used by other process
|
|
260
259
|
* once we move to executing journeys in parallel
|
|
261
260
|
*/
|
|
262
|
-
export const CACHE_PATH = join(
|
|
261
|
+
export const CACHE_PATH = join(SYNTHETICS_PATH, process.pid.toString());
|
|
263
262
|
|
|
264
263
|
export function getDurationInUs(duration: number) {
|
|
265
264
|
return Math.trunc(duration * 1e6);
|
|
@@ -322,6 +321,23 @@ export function wrapFnWithLocation<A extends unknown[], R>(
|
|
|
322
321
|
};
|
|
323
322
|
}
|
|
324
323
|
|
|
324
|
+
// Safely parse ND JSON (Newline delimitted JSON) chunks
|
|
325
|
+
export function safeNDJSONParse(chunks: string[]) {
|
|
326
|
+
// chunks may not be at proper newline boundaries, so we make sure everything is split
|
|
327
|
+
// on proper newlines
|
|
328
|
+
chunks = Array.isArray(chunks) ? chunks : [chunks];
|
|
329
|
+
const lines = chunks.join('\n').split(/\r?\n/);
|
|
330
|
+
return lines
|
|
331
|
+
.filter(l => l.match(/\S/)) // remove blank lines
|
|
332
|
+
.map(line => {
|
|
333
|
+
try {
|
|
334
|
+
return JSON.parse(line);
|
|
335
|
+
} catch (e) {
|
|
336
|
+
throw `Error ${e} could not parse data '${line}'`;
|
|
337
|
+
}
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
|
|
325
341
|
// Console helpers
|
|
326
342
|
export function write(message: string) {
|
|
327
343
|
process.stderr.write(message + '\n');
|
|
@@ -331,6 +347,10 @@ export function progress(message: string) {
|
|
|
331
347
|
write(cyan(bold(`${symbols.progress} ${message}`)));
|
|
332
348
|
}
|
|
333
349
|
|
|
350
|
+
export function apiProgress(message: string) {
|
|
351
|
+
write(grey(`> ${message}`));
|
|
352
|
+
}
|
|
353
|
+
|
|
334
354
|
export function error(message: string) {
|
|
335
355
|
write(red(message));
|
|
336
356
|
}
|
|
@@ -338,3 +358,11 @@ export function error(message: string) {
|
|
|
338
358
|
export function done(message: string) {
|
|
339
359
|
write(bold(green(`${symbols['succeeded']} ${message}`)));
|
|
340
360
|
}
|
|
361
|
+
|
|
362
|
+
export function aborted(message: string) {
|
|
363
|
+
write(bold(yellow(`${symbols['warning']} ${message}`)));
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
export function removeTrailingSlash(url: string) {
|
|
367
|
+
return url.replace(/\/+$/, '');
|
|
368
|
+
}
|
package/src/index.ts
CHANGED
package/src/loader.ts
CHANGED
|
@@ -93,10 +93,19 @@ const loadInlineScript = source => {
|
|
|
93
93
|
'browser',
|
|
94
94
|
'params',
|
|
95
95
|
'expect',
|
|
96
|
+
'request',
|
|
96
97
|
source
|
|
97
98
|
);
|
|
98
|
-
journey('inline', ({ page, context, browser, params }) => {
|
|
99
|
-
scriptFn.apply(null, [
|
|
99
|
+
journey('inline', ({ page, context, browser, params, request }) => {
|
|
100
|
+
scriptFn.apply(null, [
|
|
101
|
+
step,
|
|
102
|
+
page,
|
|
103
|
+
context,
|
|
104
|
+
browser,
|
|
105
|
+
params,
|
|
106
|
+
expect,
|
|
107
|
+
request,
|
|
108
|
+
]);
|
|
100
109
|
});
|
|
101
110
|
};
|
|
102
111
|
|
|
@@ -0,0 +1,108 @@
|
|
|
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
|
+
import {
|
|
27
|
+
formatAPIError,
|
|
28
|
+
formatNotFoundError,
|
|
29
|
+
sendRequest,
|
|
30
|
+
ok,
|
|
31
|
+
} from '../push/request';
|
|
32
|
+
import { indent, removeTrailingSlash, write } from '../helpers';
|
|
33
|
+
|
|
34
|
+
export type LocationCmdOptions = {
|
|
35
|
+
auth: string;
|
|
36
|
+
url: string;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
type LocationMetadata = {
|
|
40
|
+
id: string;
|
|
41
|
+
label: string;
|
|
42
|
+
isServiceManaged: boolean;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export type LocationAPIResponse = {
|
|
46
|
+
locations: Array<LocationMetadata>;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const PRIVATE_KEYWORD = '(private)';
|
|
50
|
+
|
|
51
|
+
export async function getLocations(options: LocationCmdOptions) {
|
|
52
|
+
const url =
|
|
53
|
+
removeTrailingSlash(options.url) + '/internal/uptime/service/locations';
|
|
54
|
+
const { body, statusCode } = await sendRequest({
|
|
55
|
+
url,
|
|
56
|
+
method: 'GET',
|
|
57
|
+
auth: options.auth,
|
|
58
|
+
});
|
|
59
|
+
if (statusCode === 404) {
|
|
60
|
+
throw formatNotFoundError(await body.text());
|
|
61
|
+
}
|
|
62
|
+
if (!ok(statusCode)) {
|
|
63
|
+
const { error, message } = await body.json();
|
|
64
|
+
throw formatAPIError(statusCode, error, message);
|
|
65
|
+
}
|
|
66
|
+
return ((await body.json()) as LocationAPIResponse).locations;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function formatLocations(locations: Array<LocationMetadata>) {
|
|
70
|
+
const formatted: Array<string> = [];
|
|
71
|
+
for (const location of locations) {
|
|
72
|
+
let name = location.label;
|
|
73
|
+
if (location.isServiceManaged) {
|
|
74
|
+
[, name] = name.includes('-') ? name.split('-') : [, name];
|
|
75
|
+
name = name.toLowerCase().trim().split(' ').join('_');
|
|
76
|
+
} else {
|
|
77
|
+
name = `${name}${PRIVATE_KEYWORD}`;
|
|
78
|
+
}
|
|
79
|
+
formatted.push(name);
|
|
80
|
+
}
|
|
81
|
+
return formatted;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export function groupLocations(locations: Array<string>) {
|
|
85
|
+
const grouped = { locations: [], privateLocations: [] };
|
|
86
|
+
for (const loc of locations) {
|
|
87
|
+
if (loc.includes(PRIVATE_KEYWORD)) {
|
|
88
|
+
grouped.privateLocations.push(loc.replace(PRIVATE_KEYWORD, ''));
|
|
89
|
+
} else {
|
|
90
|
+
grouped.locations.push(loc);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return grouped;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export function renderLocations(locations: Array<string>) {
|
|
97
|
+
let outer = 'Available locations: \n';
|
|
98
|
+
let inner = '';
|
|
99
|
+
for (const location of locations) {
|
|
100
|
+
inner += `* ${location}\n`;
|
|
101
|
+
}
|
|
102
|
+
outer += indent(inner);
|
|
103
|
+
outer += `\nSet default location for monitors via
|
|
104
|
+
- Synthetics config file 'monitors.locations' | 'monitors.privateLocations' field
|
|
105
|
+
- Monitor API 'monitor.use({ locations: ["japan"], privateLocations: ["custom-location"] }')`;
|
|
106
|
+
|
|
107
|
+
write(outer);
|
|
108
|
+
}
|
|
File without changes
|
package/src/options.ts
CHANGED
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
*/
|
|
25
25
|
|
|
26
26
|
import merge from 'deepmerge';
|
|
27
|
+
import { createOption } from 'commander';
|
|
27
28
|
import { readConfig } from './config';
|
|
28
29
|
import { getNetworkConditions, DEFAULT_THROTTLING_OPTIONS } from './helpers';
|
|
29
30
|
import type { CliArgs, RunOptions, ThrottlingOptions } from './common_types';
|
|
@@ -136,6 +137,8 @@ export function normalizeOptions(cliArgs: CliArgs): RunOptions {
|
|
|
136
137
|
|
|
137
138
|
options.schedule = cliArgs.schedule ?? monitor?.schedule;
|
|
138
139
|
options.locations = cliArgs.locations ?? monitor?.locations;
|
|
140
|
+
options.privateLocations =
|
|
141
|
+
cliArgs.privateLocations ?? monitor?.privateLocations;
|
|
139
142
|
|
|
140
143
|
return options;
|
|
141
144
|
}
|
|
@@ -171,3 +174,38 @@ export function parseThrottling(value: string, prev?: string) {
|
|
|
171
174
|
}
|
|
172
175
|
return JSON.parse(value || prev);
|
|
173
176
|
}
|
|
177
|
+
|
|
178
|
+
export function getCommonCommandOpts() {
|
|
179
|
+
const params = createOption(
|
|
180
|
+
'-p, --params <jsonstring>',
|
|
181
|
+
'JSON object that gets injected to all journeys'
|
|
182
|
+
);
|
|
183
|
+
params.argParser(JSON.parse);
|
|
184
|
+
|
|
185
|
+
const playwrightOpts = createOption(
|
|
186
|
+
'--playwright-options <jsonstring>',
|
|
187
|
+
'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.'
|
|
188
|
+
);
|
|
189
|
+
playwrightOpts.argParser(JSON.parse);
|
|
190
|
+
|
|
191
|
+
const pattern = createOption(
|
|
192
|
+
'--pattern <pattern>',
|
|
193
|
+
'RegExp file patterns to search inside directory'
|
|
194
|
+
);
|
|
195
|
+
const tags = createOption(
|
|
196
|
+
'--tags <name...>',
|
|
197
|
+
'run only journeys with a tag that matches the glob'
|
|
198
|
+
);
|
|
199
|
+
const match = createOption(
|
|
200
|
+
'--match <name>',
|
|
201
|
+
'run only journeys with a name or tag that matches the glob'
|
|
202
|
+
);
|
|
203
|
+
|
|
204
|
+
return {
|
|
205
|
+
params,
|
|
206
|
+
playwrightOpts,
|
|
207
|
+
pattern,
|
|
208
|
+
tags,
|
|
209
|
+
match,
|
|
210
|
+
};
|
|
211
|
+
}
|
package/src/push/bundler.ts
CHANGED
|
@@ -65,12 +65,17 @@ export class Bundler {
|
|
|
65
65
|
const archive = archiver('zip', {
|
|
66
66
|
zlib: { level: 9 },
|
|
67
67
|
});
|
|
68
|
-
archive.on('error',
|
|
68
|
+
archive.on('error', reject);
|
|
69
69
|
output.on('close', fulfill);
|
|
70
70
|
archive.pipe(output);
|
|
71
71
|
for (const [path, content] of this.moduleMap.entries()) {
|
|
72
72
|
const relativePath = relativeToCwd(path);
|
|
73
|
-
|
|
73
|
+
// Date is fixed to Unix epoch so the file metadata is
|
|
74
|
+
// not modified everytime when files are bundled
|
|
75
|
+
archive.append(content, {
|
|
76
|
+
name: relativePath,
|
|
77
|
+
date: new Date('1970-01-01'),
|
|
78
|
+
});
|
|
74
79
|
}
|
|
75
80
|
archive.finalize();
|
|
76
81
|
});
|