diff --git a/.buildkite/scripts/steps/artifacts/docker_image.sh b/.buildkite/scripts/steps/artifacts/docker_image.sh
index caa2f02908e031..3d75d301ce8e7f 100755
--- a/.buildkite/scripts/steps/artifacts/docker_image.sh
+++ b/.buildkite/scripts/steps/artifacts/docker_image.sh
@@ -7,13 +7,14 @@ set -euo pipefail
source .buildkite/scripts/steps/artifacts/env.sh
GIT_ABBREV_COMMIT=${BUILDKITE_COMMIT:0:12}
-KIBANA_IMAGE="docker.elastic.co/kibana-ci/kibana:git-$GIT_ABBREV_COMMIT"
+KIBANA_IMAGE_INPUT="docker.elastic.co/kibana-ci/kibana-serverless:git-$GIT_ABBREV_COMMIT"
+KIBANA_IMAGE_OUTPUT="docker.elastic.co/kibana-ci/kibana:git-$GIT_ABBREV_COMMIT"
echo "--- Verify manifest does not already exist"
echo "$KIBANA_DOCKER_PASSWORD" | docker login -u "$KIBANA_DOCKER_USERNAME" --password-stdin docker.elastic.co
trap 'docker logout docker.elastic.co' EXIT
-if docker manifest inspect $KIBANA_IMAGE &> /dev/null; then
+if docker manifest inspect $KIBANA_IMAGE_OUTPUT &> /dev/null; then
echo "Manifest already exists, exiting"
exit 1
fi
@@ -32,37 +33,36 @@ node scripts/build \
--skip-docker-contexts
echo "--- Tag images"
-docker rmi "$KIBANA_IMAGE"
+docker rmi "$KIBANA_IMAGE_INPUT"
docker load < "target/kibana-serverless-$BASE_VERSION-docker-image.tar.gz"
-docker tag "$KIBANA_IMAGE" "$KIBANA_IMAGE-amd64"
+docker tag "$KIBANA_IMAGE_INPUT" "$KIBANA_IMAGE_OUTPUT-amd64"
-docker rmi "$KIBANA_IMAGE"
+docker rmi "$KIBANA_IMAGE_INPUT"
docker load < "target/kibana-serverless-$BASE_VERSION-docker-image-aarch64.tar.gz"
-docker tag "$KIBANA_IMAGE" "$KIBANA_IMAGE-arm64"
+docker tag "$KIBANA_IMAGE_INPUT" "$KIBANA_IMAGE_OUTPUT-arm64"
echo "--- Push images"
-docker image push "$KIBANA_IMAGE-arm64"
-docker image push "$KIBANA_IMAGE-amd64"
+docker image push "$KIBANA_IMAGE_OUTPUT-arm64"
+docker image push "$KIBANA_IMAGE_OUTPUT-amd64"
echo "--- Create manifest"
-docker rmi "$KIBANA_IMAGE"
docker manifest create \
- "$KIBANA_IMAGE" \
- --amend "$KIBANA_IMAGE-arm64" \
- --amend "$KIBANA_IMAGE-amd64"
+ "$KIBANA_IMAGE_OUTPUT" \
+ --amend "$KIBANA_IMAGE_OUTPUT-arm64" \
+ --amend "$KIBANA_IMAGE_OUTPUT-amd64"
echo "--- Push manifest"
-docker manifest push "$KIBANA_IMAGE"
+docker manifest push "$KIBANA_IMAGE_OUTPUT"
docker logout docker.elastic.co
cat << EOF | buildkite-agent annotate --style "info" --context image
### Container Images
- Manifest: \`$KIBANA_IMAGE\`
+ Manifest: \`$KIBANA_IMAGE_OUTPUT\`
- AMD64: \`$KIBANA_IMAGE-amd64\`
+ AMD64: \`$KIBANA_IMAGE_OUTPUT-amd64\`
- ARM64: \`$KIBANA_IMAGE-arm64\`
+ ARM64: \`$KIBANA_IMAGE_OUTPUT-arm64\`
EOF
echo "--- Build dependencies report"
diff --git a/fleet_packages.json b/fleet_packages.json
index eef982351e4551..34d807ac46b9c1 100644
--- a/fleet_packages.json
+++ b/fleet_packages.json
@@ -34,7 +34,7 @@
},
{
"name": "endpoint",
- "version": "8.7.1"
+ "version": "8.8.0"
},
{
"name": "fleet_server",
diff --git a/src/dev/build/tasks/os_packages/create_os_package_tasks.ts b/src/dev/build/tasks/os_packages/create_os_package_tasks.ts
index 13a2209e2e47ea..8563c19f9d5386 100644
--- a/src/dev/build/tasks/os_packages/create_os_package_tasks.ts
+++ b/src/dev/build/tasks/os_packages/create_os_package_tasks.ts
@@ -81,7 +81,7 @@ export const CreateDockerUbuntu: Task = {
};
export const CreateDockerServerless: Task = {
- description: 'Creating Docker Ubuntu image',
+ description: 'Creating Docker Serverless image',
async run(config, log, build) {
await runDockerGenerator(config, log, build, {
diff --git a/x-pack/plugins/cloud_defend/public/components/control_yaml_view/hooks/policy_schema.json b/x-pack/plugins/cloud_defend/public/components/control_yaml_view/hooks/policy_schema.json
index af0d68d0df3eed..9f6ecfe4b8da57 100644
--- a/x-pack/plugins/cloud_defend/public/components/control_yaml_view/hooks/policy_schema.json
+++ b/x-pack/plugins/cloud_defend/public/components/control_yaml_view/hooks/policy_schema.json
@@ -66,10 +66,10 @@
"required": ["containerImageName"]
},
{
- "required": ["containerImageFullName"]
+ "required": ["containerImageTag"]
},
{
- "required": ["containerImageTag"]
+ "required": ["containerImageFullName"]
},
{
"required": ["kubernetesClusterId"]
@@ -100,6 +100,19 @@
"name": {
"type": "string"
},
+ "operation": {
+ "type": "array",
+ "minItems": 1,
+ "items": {
+ "enum": [
+ "createExecutable",
+ "modifyExecutable",
+ "createFile",
+ "modifyFile",
+ "deleteFile"
+ ]
+ }
+ },
"containerImageName": {
"type": "array",
"minItems": 1,
@@ -159,19 +172,6 @@
"type": "string"
}
},
- "operation": {
- "type": "array",
- "minItems": 1,
- "items": {
- "enum": [
- "createExecutable",
- "modifyExecutable",
- "createFile",
- "modifyFile",
- "deleteFile"
- ]
- }
- },
"targetFilePath": {
"type": "array",
"minItems": 1,
@@ -214,10 +214,10 @@
"required": ["containerImageName"]
},
{
- "required": ["containerImageFullName"]
+ "required": ["containerImageTag"]
},
{
- "required": ["containerImageTag"]
+ "required": ["containerImageFullName"]
},
{
"required": ["kubernetesClusterId"]
@@ -248,6 +248,13 @@
"name": {
"type": "string"
},
+ "operation": {
+ "type": "array",
+ "minItems": 1,
+ "items": {
+ "enum": ["fork", "exec"]
+ }
+ },
"containerImageName": {
"type": "array",
"minItems": 1,
@@ -307,13 +314,6 @@
"type": "string"
}
},
- "operation": {
- "type": "array",
- "minItems": 1,
- "items": {
- "enum": ["fork", "exec"]
- }
- },
"processExecutable": {
"type": "array",
"minItems": 1,
diff --git a/x-pack/plugins/cloud_security_posture/public/common/utils/get_cvsscore_color.test.ts b/x-pack/plugins/cloud_security_posture/public/common/utils/get_cvsscore_color.test.ts
deleted file mode 100644
index 67eab9cddd7354..00000000000000
--- a/x-pack/plugins/cloud_security_posture/public/common/utils/get_cvsscore_color.test.ts
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-import { euiThemeVars } from '@kbn/ui-theme';
-import { getCvsScoreColor } from './get_cvsscore_color';
-
-describe('getCvsScoreColor', () => {
- it('returns correct color for low severity score', () => {
- expect(getCvsScoreColor(1.5)).toBe(euiThemeVars.euiColorVis0);
- });
-
- it('returns correct color for medium severity score', () => {
- expect(getCvsScoreColor(5.5)).toBe(euiThemeVars.euiColorVis7);
- });
-
- it('returns correct color for high severity score', () => {
- expect(getCvsScoreColor(7.9)).toBe(euiThemeVars.euiColorVis9);
- });
-
- it('returns correct color for critical severity score', () => {
- expect(getCvsScoreColor(10.0)).toBe(euiThemeVars.euiColorDanger);
- });
-
- it('returns error message for invalid score', () => {
- expect(getCvsScoreColor(-1)).toBe(undefined);
- });
-
- it('returns error message for invalid score when score is 0.0', () => {
- expect(getCvsScoreColor(0.0)).toBe(undefined);
- });
-});
diff --git a/x-pack/plugins/cloud_security_posture/public/common/utils/get_cvsscore_color.ts b/x-pack/plugins/cloud_security_posture/public/common/utils/get_vulnerability_colors.ts
similarity index 54%
rename from x-pack/plugins/cloud_security_posture/public/common/utils/get_cvsscore_color.ts
rename to x-pack/plugins/cloud_security_posture/public/common/utils/get_vulnerability_colors.ts
index cbde6d2b885bdf..56a3d2d2e6088d 100644
--- a/x-pack/plugins/cloud_security_posture/public/common/utils/get_cvsscore_color.ts
+++ b/x-pack/plugins/cloud_security_posture/public/common/utils/get_vulnerability_colors.ts
@@ -8,15 +8,28 @@
import { euiThemeVars } from '@kbn/ui-theme';
export const getCvsScoreColor = (score: number): string | undefined => {
- if (score >= 0.1 && score <= 3.9) {
+ if (score <= 4) {
return euiThemeVars.euiColorVis0; // low severity
- } else if (score >= 4.0 && score <= 6.9) {
+ } else if (score >= 4 && score <= 7) {
return euiThemeVars.euiColorVis7; // medium severity
- } else if (score >= 7.0 && score <= 8.9) {
+ } else if (score >= 7 && score <= 9) {
return euiThemeVars.euiColorVis9; // high severity
- } else if (score >= 9.0 && score <= 10.0) {
+ } else if (score >= 9) {
return euiThemeVars.euiColorDanger; // critical severity
- } else {
- return undefined; // if the score is not within the valid range
+ }
+};
+
+export const getSeverityStatusColor = (severity: string): string | undefined => {
+ switch (severity) {
+ case 'LOW':
+ return euiThemeVars.euiColorVis0;
+ case 'MEDIUM':
+ return euiThemeVars.euiColorVis7;
+ case 'HIGH':
+ return euiThemeVars.euiColorVis9;
+ case 'CRITICAL':
+ return euiThemeVars.euiColorDanger;
+ default:
+ return '#aaa';
}
};
diff --git a/x-pack/plugins/cloud_security_posture/public/common/utils/get_vulnerabiltity_colors.test.ts b/x-pack/plugins/cloud_security_posture/public/common/utils/get_vulnerabiltity_colors.test.ts
new file mode 100644
index 00000000000000..e96e6f8149f114
--- /dev/null
+++ b/x-pack/plugins/cloud_security_posture/public/common/utils/get_vulnerabiltity_colors.test.ts
@@ -0,0 +1,53 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { euiThemeVars } from '@kbn/ui-theme';
+import { getCvsScoreColor, getSeverityStatusColor } from './get_vulnerability_colors';
+
+describe('getCvsScoreColor', () => {
+ it('returns correct color for low severity score', () => {
+ expect(getCvsScoreColor(1.5)).toBe(euiThemeVars.euiColorVis0);
+ });
+
+ it('returns correct color for medium severity score', () => {
+ expect(getCvsScoreColor(5.5)).toBe(euiThemeVars.euiColorVis7);
+ });
+
+ it('returns correct color for high severity score', () => {
+ expect(getCvsScoreColor(7.9)).toBe(euiThemeVars.euiColorVis9);
+ });
+
+ it('returns correct color for critical severity score', () => {
+ expect(getCvsScoreColor(10.0)).toBe(euiThemeVars.euiColorDanger);
+ });
+
+ it('returns correct color for low severity score for undefined value', () => {
+ expect(getCvsScoreColor(-0.2)).toBe(euiThemeVars.euiColorVis0);
+ });
+});
+
+describe('getSeverityStatusColor', () => {
+ it('should return the correct color for LOW severity', () => {
+ expect(getSeverityStatusColor('LOW')).toBe(euiThemeVars.euiColorVis0);
+ });
+
+ it('should return the correct color for MEDIUM severity', () => {
+ expect(getSeverityStatusColor('MEDIUM')).toBe(euiThemeVars.euiColorVis7);
+ });
+
+ it('should return the correct color for HIGH severity', () => {
+ expect(getSeverityStatusColor('HIGH')).toBe(euiThemeVars.euiColorVis9);
+ });
+
+ it('should return the correct color for CRITICAL severity', () => {
+ expect(getSeverityStatusColor('CRITICAL')).toBe(euiThemeVars.euiColorDanger);
+ });
+
+ it('should return #aaa for an unknown severity', () => {
+ expect(getSeverityStatusColor('UNKNOWN')).toBe('#aaa');
+ });
+});
diff --git a/x-pack/plugins/cloud_security_posture/public/components/vulnerability_badges.tsx b/x-pack/plugins/cloud_security_posture/public/components/vulnerability_badges.tsx
index 4275ed433d85e7..bf776f27c04a15 100644
--- a/x-pack/plugins/cloud_security_posture/public/components/vulnerability_badges.tsx
+++ b/x-pack/plugins/cloud_security_posture/public/components/vulnerability_badges.tsx
@@ -9,7 +9,7 @@ import { EuiBadge, EuiIcon, EuiTextColor } from '@elastic/eui';
import React from 'react';
import { css } from '@emotion/react';
import { float } from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
-import { getCvsScoreColor } from '../common/utils/get_cvsscore_color';
+import { getCvsScoreColor, getSeverityStatusColor } from '../common/utils/get_vulnerability_colors';
interface CVSScoreBadgeProps {
score: float;
@@ -18,7 +18,6 @@ interface CVSScoreBadgeProps {
interface SeverityStatusBadgeProps {
status: string;
- score?: float;
}
export const CVSScoreBadge = ({ score, version }: CVSScoreBadgeProps) => {
@@ -54,8 +53,8 @@ export const CVSScoreBadge = ({ score, version }: CVSScoreBadgeProps) => {
);
};
-export const SeverityStatusBadge = ({ score, status }: SeverityStatusBadgeProps) => {
- const color = score ? getCvsScoreColor(score) : undefined;
+export const SeverityStatusBadge = ({ status }: SeverityStatusBadgeProps) => {
+ const color = getSeverityStatusColor(status);
return (
{status}
diff --git a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/vulnerabilities.tsx b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/vulnerabilities.tsx
index 38b9dfcacf9d0f..f048347d686b5c 100644
--- a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/vulnerabilities.tsx
+++ b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/vulnerabilities.tsx
@@ -53,7 +53,7 @@ export const Vulnerabilities = () => {
const { data, isLoading, error } = useFilteredDataView(LATEST_VULNERABILITIES_INDEX_PATTERN);
const getSetupStatus = useCspSetupStatusApi();
- if (getSetupStatus?.data?.vuln_mgmt.status !== 'indexed') return ;
+ if (getSetupStatus?.data?.vuln_mgmt?.status !== 'indexed') return ;
if (error) {
return ;
@@ -247,12 +247,7 @@ const VulnerabilitiesContent = ({ dataView }: { dataView: DataView }) => {
if (!vulnerabilityRow.vulnerability.severity) {
return null;
}
- return (
-
- );
+ return ;
}
if (columnId === vulnerabilitiesColumns.package_version) {
return (
diff --git a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/vulnerabilities_finding_flyout/vulnerability_finding_flyout.tsx b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/vulnerabilities_finding_flyout/vulnerability_finding_flyout.tsx
index 5ea65846950bf3..195c74c8032419 100644
--- a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/vulnerabilities_finding_flyout/vulnerability_finding_flyout.tsx
+++ b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/vulnerabilities_finding_flyout/vulnerability_finding_flyout.tsx
@@ -27,10 +27,7 @@ import { VulnerabilityOverviewTab } from './vulnerability_overview_tab';
import { VulnerabilityJsonTab } from './vulnerability_json_tab';
import { SeverityStatusBadge } from '../../../components/vulnerability_badges';
import { VulnerabilityRecord } from '../types';
-import {
- SEVERITY_STATUS_VULNERABILITY_FLYOUT,
- TAB_ID_VULNERABILITY_FLYOUT,
-} from '../test_subjects';
+import { TAB_ID_VULNERABILITY_FLYOUT } from '../test_subjects';
const overviewTabId = 'overview';
const jsonTabId = 'json';
@@ -110,11 +107,8 @@ export const VulnerabilityFindingFlyout = ({
gap: ${euiThemeVars.euiSizeS};
`}
>
-
-
+
+
({
})),
}));
-const mockNotableAnomaliesSearch = jest.fn().mockResolvedValue({});
+const mockAnomaliesSearch = jest.fn().mockResolvedValue({});
jest.mock('../api/anomalies_search', () => ({
- notableAnomaliesSearch: jest
- .fn()
- .mockImplementation((params: unknown) => mockNotableAnomaliesSearch(params)),
+ anomaliesSearch: jest.fn().mockImplementation((params: unknown) => mockAnomaliesSearch(params)),
}));
-describe('useNotableAnomaliesSearch', () => {
+describe('useAggregatedAnomaliesByJob', () => {
beforeEach(() => {
jest.clearAllMocks();
});
it('returns the initial value', () => {
- const { result } = renderHook(() => useNotableAnomaliesSearch({ skip: true, from, to }), {
+ const { result } = renderHook(() => useAggregatedAnomaliesByJob({ skip: true, from, to }), {
wrapper: TestProviders,
});
expect(result.current.data.length).toEqual(0);
});
- it('calls notableAnomaliesSearch when skip is false', async () => {
+ it('calls anomaliesSearch when skip is false', async () => {
await act(async () => {
- renderHook(() => useNotableAnomaliesSearch({ skip: false, from, to }), {
+ renderHook(() => useAggregatedAnomaliesByJob({ skip: false, from, to }), {
wrapper: TestProviders,
});
});
- expect(mockNotableAnomaliesSearch).toHaveBeenCalled();
+ expect(mockAnomaliesSearch).toHaveBeenCalled();
});
- it('does NOT call notableAnomaliesSearch when skip is true', async () => {
+ it('does NOT call anomaliesSearch when skip is true', async () => {
await act(async () => {
- renderHook(() => useNotableAnomaliesSearch({ skip: true, from, to }), {
+ renderHook(() => useAggregatedAnomaliesByJob({ skip: true, from, to }), {
wrapper: TestProviders,
});
});
- expect(mockNotableAnomaliesSearch).not.toHaveBeenCalled();
+ expect(mockAnomaliesSearch).not.toHaveBeenCalled();
});
it('refetch calls useSecurityJobs().refetch', async () => {
await act(async () => {
const { result, waitForNextUpdate } = renderHook(
- () => useNotableAnomaliesSearch({ skip: false, from, to }),
+ () => useAggregatedAnomaliesByJob({ skip: false, from, to }),
{
wrapper: TestProviders,
}
@@ -91,11 +89,11 @@ describe('useNotableAnomaliesSearch', () => {
it('returns formated data', async () => {
await act(async () => {
const jobCount = { key: jobId, doc_count: 99 };
- mockNotableAnomaliesSearch.mockResolvedValue({
+ mockAnomaliesSearch.mockResolvedValue({
aggregations: { number_of_anomalies: { buckets: [jobCount] } },
});
const { result, waitForNextUpdate } = renderHook(
- () => useNotableAnomaliesSearch({ skip: false, from, to }),
+ () => useAggregatedAnomaliesByJob({ skip: false, from, to }),
{
wrapper: TestProviders,
}
@@ -141,7 +139,7 @@ describe('useNotableAnomaliesSearch', () => {
},
};
- mockNotableAnomaliesSearch.mockResolvedValue({
+ mockAnomaliesSearch.mockResolvedValue({
aggregations: { number_of_anomalies: { buckets: [fistJobCount, secondJobCount] } },
});
@@ -153,7 +151,7 @@ describe('useNotableAnomaliesSearch', () => {
});
const { result, waitForNextUpdate } = renderHook(
- () => useNotableAnomaliesSearch({ skip: false, from, to }),
+ () => useAggregatedAnomaliesByJob({ skip: false, from, to }),
{
wrapper: TestProviders,
}
@@ -170,9 +168,9 @@ describe('useNotableAnomaliesSearch', () => {
it('does not throw error when aggregations is undefined', async () => {
await act(async () => {
- mockNotableAnomaliesSearch.mockResolvedValue({});
+ mockAnomaliesSearch.mockResolvedValue({});
const { waitForNextUpdate } = renderHook(
- () => useNotableAnomaliesSearch({ skip: false, from, to }),
+ () => useAggregatedAnomaliesByJob({ skip: false, from, to }),
{
wrapper: TestProviders,
}
diff --git a/x-pack/plugins/security_solution/public/common/components/ml/anomaly/use_anomalies_search.ts b/x-pack/plugins/security_solution/public/common/components/ml/anomaly/use_anomalies_search.ts
index baa4ac03623e14..9a5314d0a0d0bf 100644
--- a/x-pack/plugins/security_solution/public/common/components/ml/anomaly/use_anomalies_search.ts
+++ b/x-pack/plugins/security_solution/public/common/components/ml/anomaly/use_anomalies_search.ts
@@ -12,9 +12,7 @@ import { DEFAULT_ANOMALY_SCORE } from '../../../../../common/constants';
import * as i18n from './translations';
import { useUiSetting$ } from '../../../lib/kibana';
import { useAppToasts } from '../../../hooks/use_app_toasts';
-import { notableAnomaliesSearch } from '../api/anomalies_search';
-import type { NotableAnomaliesJobId } from '../../../../overview/components/entity_analytics/anomalies/config';
-import { NOTABLE_ANOMALIES_IDS } from '../../../../overview/components/entity_analytics/anomalies/config';
+import { anomaliesSearch } from '../api/anomalies_search';
import { getAggregatedAnomaliesQuery } from '../../../../overview/components/entity_analytics/anomalies/query';
import type { inputsModel } from '../../../store';
import { useSecurityJobs } from '../../ml_popover/hooks/use_security_jobs';
@@ -26,23 +24,23 @@ export enum AnomalyEntity {
}
export interface AnomaliesCount {
- name: NotableAnomaliesJobId | string;
+ name: string;
count: number;
entity: AnomalyEntity;
job?: SecurityJob;
}
-interface UseNotableAnomaliesSearchProps {
+interface UseAggregatedAnomaliesByJobProps {
skip: boolean;
from: string;
to: string;
}
-export const useNotableAnomaliesSearch = ({
+export const useAggregatedAnomaliesByJob = ({
skip,
from,
to,
-}: UseNotableAnomaliesSearchProps): {
+}: UseAggregatedAnomaliesByJobProps): {
isLoading: boolean;
data: AnomaliesCount[];
refetch: inputsModel.Refetch;
@@ -60,23 +58,17 @@ export const useNotableAnomaliesSearch = ({
const { addError } = useAppToasts();
const [anomalyScoreThreshold] = useUiSetting$(DEFAULT_ANOMALY_SCORE);
- const { notableAnomaliesJobs, query } = useMemo(() => {
- const newNotableAnomaliesJobs = securityJobs.filter(({ id }) =>
- NOTABLE_ANOMALIES_IDS.some((notableJobId) => id === notableJobId)
- );
-
- const newQuery = getAggregatedAnomaliesQuery({
- jobIds: newNotableAnomaliesJobs.map(({ id }) => id),
- anomalyScoreThreshold,
- from,
- to,
- });
-
- return {
- query: newQuery,
- notableAnomaliesJobs: newNotableAnomaliesJobs,
- };
- }, [securityJobs, anomalyScoreThreshold, from, to]);
+ const { query } = useMemo(
+ () => ({
+ query: getAggregatedAnomaliesQuery({
+ jobIds: securityJobs.map(({ id }) => id),
+ anomalyScoreThreshold,
+ from,
+ to,
+ }),
+ }),
+ [securityJobs, anomalyScoreThreshold, from, to]
+ );
useEffect(() => {
let isSubscribed = true;
@@ -85,16 +77,16 @@ export const useNotableAnomaliesSearch = ({
async function fetchAnomaliesSearch() {
if (!isSubscribed) return;
- if (skip || !isMlUser || notableAnomaliesJobs.length === 0) {
+ if (skip || !isMlUser || securityJobs.length === 0) {
setLoading(false);
return;
}
setLoading(true);
try {
- const response = await notableAnomaliesSearch(
+ const response = await anomaliesSearch(
{
- jobIds: notableAnomaliesJobs.filter((job) => job.isInstalled).map(({ id }) => id),
+ jobIds: securityJobs.filter((job) => job.isInstalled).map(({ id }) => id),
query,
},
abortCtrl.signal
@@ -103,7 +95,7 @@ export const useNotableAnomaliesSearch = ({
if (isSubscribed) {
setLoading(false);
const buckets = response.aggregations?.number_of_anomalies.buckets ?? [];
- setData(formatResultData(buckets, notableAnomaliesJobs));
+ setData(formatResultData(buckets, securityJobs));
}
} catch (error) {
if (isSubscribed && error.name !== 'AbortError') {
@@ -119,7 +111,7 @@ export const useNotableAnomaliesSearch = ({
isSubscribed = false;
abortCtrl.abort();
};
- }, [skip, isMlUser, addError, query, notableAnomaliesJobs, refetchJobs]);
+ }, [skip, isMlUser, addError, query, securityJobs, refetchJobs]);
return { isLoading: loading || jobsLoading, data, refetch: refetchJobs };
};
@@ -129,16 +121,14 @@ function formatResultData(
key: string;
doc_count: number;
}>,
- notableAnomaliesJobs: SecurityJob[]
+ anomaliesJobs: SecurityJob[]
): AnomaliesCount[] {
- const unsortedAnomalies: AnomaliesCount[] = NOTABLE_ANOMALIES_IDS.map((notableJobId) => {
- const job = notableAnomaliesJobs.find(({ id }) => id === notableJobId);
-
+ const unsortedAnomalies: AnomaliesCount[] = anomaliesJobs.map((job) => {
const bucket = buckets.find(({ key }) => key === job?.id);
const hasUserName = has("entity.hits.hits[0]._source['user.name']", bucket);
return {
- name: job?.customSettings?.security_app_display_name ?? notableJobId,
+ name: job?.customSettings?.security_app_display_name ?? job.id,
count: bucket?.doc_count ?? 0,
entity: hasUserName ? AnomalyEntity.User : AnomalyEntity.Host,
job,
diff --git a/x-pack/plugins/security_solution/public/common/components/ml/api/anomalies_search.ts b/x-pack/plugins/security_solution/public/common/components/ml/api/anomalies_search.ts
index 966137c616f99c..d7cfa924b0de07 100644
--- a/x-pack/plugins/security_solution/public/common/components/ml/api/anomalies_search.ts
+++ b/x-pack/plugins/security_solution/public/common/components/ml/api/anomalies_search.ts
@@ -5,7 +5,6 @@
* 2.0.
*/
-import type { NotableAnomaliesJobId } from '../../../../overview/components/entity_analytics/anomalies/config';
import { KibanaServices } from '../../../lib/kibana';
export interface Body {
@@ -17,14 +16,14 @@ export interface AnomaliesSearchResponse {
aggregations?: {
number_of_anomalies: {
buckets: Array<{
- key: NotableAnomaliesJobId;
+ key: string;
doc_count: number;
}>;
};
};
}
-export const notableAnomaliesSearch = async (
+export const anomaliesSearch = async (
body: Body,
signal: AbortSignal
): Promise => {
diff --git a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/config.ts b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/config.ts
deleted file mode 100644
index 1396568384b171..00000000000000
--- a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/config.ts
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-export const NOTABLE_ANOMALIES_IDS = [
- 'auth_rare_source_ip_for_a_user',
- 'packetbeat_dns_tunneling',
- 'packetbeat_rare_server_domain',
- 'packetbeat_rare_dns_question',
- 'suspicious_login_activity',
- 'v3_windows_anomalous_script',
- 'high_count_network_denies',
- 'v3_windows_anomalous_process_all_hosts',
- 'v3_linux_rare_metadata_process',
- 'packetbeat_rare_user_agent',
- 'v3_linux_anomalous_process_all_hosts',
- 'packetbeat_rare_urls',
- 'v3_windows_anomalous_path_activity',
- 'v3_windows_anomalous_process_creation',
- 'v3_linux_system_process_discovery',
- 'v3_linux_system_user_discovery',
- 'high_count_by_destination_country',
- 'auth_high_count_logon_events',
- 'v3_linux_anomalous_user_name',
- 'v3_rare_process_by_host_windows',
- 'v3_linux_anomalous_network_activity',
- 'auth_high_count_logon_fails',
- 'auth_high_count_logon_events_for_a_source_ip',
- 'v3_linux_rare_metadata_user',
- 'rare_destination_country',
- 'v3_linux_system_information_discovery',
- 'v3_linux_rare_user_compiler',
- 'v3_windows_anomalous_user_name',
- 'v3_rare_process_by_host_linux',
- 'v3_windows_anomalous_network_activity',
- 'auth_rare_hour_for_a_user',
- 'v3_windows_rare_metadata_user',
- 'v3_windows_rare_user_type10_remote_login',
- 'v3_linux_anomalous_network_port_activity',
- 'v3_linux_rare_sudo_user',
- 'v3_windows_anomalous_service',
- 'v3_windows_rare_metadata_process',
- 'v3_windows_rare_user_runas_event',
- 'v3_linux_network_connection_discovery',
- 'v3_linux_network_configuration_discovery',
- 'auth_rare_user',
- 'high_count_network_events',
-] as const;
-
-export type NotableAnomaliesJobId = typeof NOTABLE_ANOMALIES_IDS[number];
diff --git a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/index.test.tsx b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/index.test.tsx
index 58f075ff76b21d..3e6b85d7beca9f 100644
--- a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/index.test.tsx
+++ b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/index.test.tsx
@@ -30,7 +30,7 @@ jest.mock('../../../../common/utils/route/use_route_spy', () => ({
]),
}));
-const mockUseNotableAnomaliesSearch = jest.fn().mockReturnValue({
+const mockUseAggregatedAnomaliesByJob = jest.fn().mockReturnValue({
isLoading: false,
data: [],
refetch: jest.fn(),
@@ -51,7 +51,7 @@ jest.mock('../../../../common/components/ml/anomaly/use_anomalies_search', () =>
);
return {
...original,
- useNotableAnomaliesSearch: () => mockUseNotableAnomaliesSearch(),
+ useAggregatedAnomaliesByJob: () => mockUseAggregatedAnomaliesByJob(),
};
});
@@ -95,7 +95,7 @@ describe('EntityAnalyticsAnomalies', () => {
entity: AnomalyEntity.User,
};
- mockUseNotableAnomaliesSearch.mockReturnValue({
+ mockUseAggregatedAnomaliesByJob.mockReturnValue({
isLoading: false,
data: [jobCount],
refetch: jest.fn(),
@@ -126,7 +126,7 @@ describe('EntityAnalyticsAnomalies', () => {
entity: AnomalyEntity.User,
};
- mockUseNotableAnomaliesSearch.mockReturnValue({
+ mockUseAggregatedAnomaliesByJob.mockReturnValue({
isLoading: false,
data: [jobCount],
refetch: jest.fn(),
@@ -152,7 +152,7 @@ describe('EntityAnalyticsAnomalies', () => {
entity: AnomalyEntity.User,
};
- mockUseNotableAnomaliesSearch.mockReturnValue({
+ mockUseAggregatedAnomaliesByJob.mockReturnValue({
isLoading: false,
data: [jobCount],
refetch: jest.fn(),
@@ -178,7 +178,7 @@ describe('EntityAnalyticsAnomalies', () => {
entity: AnomalyEntity.User,
};
- mockUseNotableAnomaliesSearch.mockReturnValue({
+ mockUseAggregatedAnomaliesByJob.mockReturnValue({
isLoading: false,
data: [jobCount],
refetch: jest.fn(),
@@ -208,7 +208,7 @@ describe('EntityAnalyticsAnomalies', () => {
entity: AnomalyEntity.User,
};
- mockUseNotableAnomaliesSearch.mockReturnValue({
+ mockUseAggregatedAnomaliesByJob.mockReturnValue({
isLoading: false,
data: [jobCount],
refetch: jest.fn(),
@@ -232,7 +232,7 @@ describe('EntityAnalyticsAnomalies', () => {
entity: AnomalyEntity.User,
};
- mockUseNotableAnomaliesSearch.mockReturnValue({
+ mockUseAggregatedAnomaliesByJob.mockReturnValue({
isLoading: true,
data: [jobCount],
refetch: jest.fn(),
@@ -260,7 +260,7 @@ describe('EntityAnalyticsAnomalies', () => {
entity: AnomalyEntity.User,
};
- mockUseNotableAnomaliesSearch.mockReturnValue({
+ mockUseAggregatedAnomaliesByJob.mockReturnValue({
isLoading: false,
data: [jobCount],
refetch: jest.fn(),
@@ -288,7 +288,7 @@ describe('EntityAnalyticsAnomalies', () => {
entity: AnomalyEntity.User,
};
- mockUseNotableAnomaliesSearch.mockReturnValue({
+ mockUseAggregatedAnomaliesByJob.mockReturnValue({
isLoading: false,
data: [jobCount],
refetch: jest.fn(),
diff --git a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/index.tsx b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/index.tsx
index 44213690721ea3..e72369c97379d9 100644
--- a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/index.tsx
+++ b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/index.tsx
@@ -20,7 +20,7 @@ import { HeaderSection } from '../../../../common/components/header_section';
import { useQueryToggle } from '../../../../common/containers/query_toggle';
import { LastUpdatedAt } from '../../../../common/components/last_updated_at';
import * as i18n from './translations';
-import { useNotableAnomaliesSearch } from '../../../../common/components/ml/anomaly/use_anomalies_search';
+import { useAggregatedAnomaliesByJob } from '../../../../common/components/ml/anomaly/use_anomalies_search';
import { useAnomaliesColumns } from './columns';
import { useQueryInspector } from '../../../../common/components/page/manage_query';
import { useGlobalTime } from '../../../../common/containers/use_global_time';
@@ -66,7 +66,7 @@ export const EntityAnalyticsAnomalies = () => {
isLoading: isSearchLoading,
data,
refetch,
- } = useNotableAnomaliesSearch({
+ } = useAggregatedAnomaliesByJob({
skip: !toggleStatus,
from,
to,
diff --git a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/translations.ts b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/translations.ts
index d8dbcd8664c959..733a0d8728f3c7 100644
--- a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/translations.ts
+++ b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/translations.ts
@@ -10,7 +10,7 @@ import { i18n } from '@kbn/i18n';
export const ANOMALIES_TITLE = i18n.translate(
'xpack.securitySolution.entityAnalytics.anomalies.anomaliesTitle',
{
- defaultMessage: 'Notable Anomalies',
+ defaultMessage: 'Anomalies',
}
);
diff --git a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/header/index.tsx b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/header/index.tsx
index 85cc8c00438970..46d37f15a6f689 100644
--- a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/header/index.tsx
+++ b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/header/index.tsx
@@ -25,7 +25,7 @@ import { hostsActions } from '../../../../explore/hosts/store';
import { usersActions } from '../../../../explore/users/store';
import { getTabsOnUsersUrl } from '../../../../common/components/link_to/redirect_to_users';
import { UsersTableType } from '../../../../explore/users/store/model';
-import { useNotableAnomaliesSearch } from '../../../../common/components/ml/anomaly/use_anomalies_search';
+import { useAggregatedAnomaliesByJob } from '../../../../common/components/ml/anomaly/use_anomalies_search';
import { useGlobalTime } from '../../../../common/containers/use_global_time';
import { useMlCapabilities } from '../../../../common/components/ml/hooks/use_ml_capabilities';
import { useQueryInspector } from '../../../../common/components/page/manage_query';
@@ -70,7 +70,7 @@ export const EntityAnalyticsHeader = () => {
riskEntity: RiskScoreEntity.user,
});
- const { data } = useNotableAnomaliesSearch({ skip: false, from, to });
+ const { data } = useAggregatedAnomaliesByJob({ skip: false, from, to });
const dispatch = useDispatch();
const getSecuritySolutionLinkProps = useGetSecuritySolutionLinkProps();
diff --git a/x-pack/plugins/security_solution/public/overview/links.ts b/x-pack/plugins/security_solution/public/overview/links.ts
index 07cc2a491cf025..020d257c02cbef 100644
--- a/x-pack/plugins/security_solution/public/overview/links.ts
+++ b/x-pack/plugins/security_solution/public/overview/links.ts
@@ -82,7 +82,7 @@ export const entityAnalyticsLinks: LinkItem = {
landingImage: entityAnalyticsDashboard,
description: i18n.translate('xpack.securitySolution.appLinks.entityAnalyticsDescription', {
defaultMessage:
- 'Entity analytics, notable anomalies, and threats to narrow down the monitoring surface area.',
+ 'Entity analytics, anomalies, and threats to narrow down the monitoring surface area.',
}),
path: ENTITY_ANALYTICS_PATH,
capabilities: [`${SERVER_APP_ID}.show`],
diff --git a/x-pack/plugins/synthetics/common/runtime_types/monitor_management/synthetics_params.ts b/x-pack/plugins/synthetics/common/runtime_types/monitor_management/synthetics_params.ts
index 0a20795950d856..078dfa7b45d049 100644
--- a/x-pack/plugins/synthetics/common/runtime_types/monitor_management/synthetics_params.ts
+++ b/x-pack/plugins/synthetics/common/runtime_types/monitor_management/synthetics_params.ts
@@ -7,7 +7,7 @@
import * as t from 'io-ts';
-export const SyntheticsParamCode = t.intersection([
+export const SyntheticsParamSOCodec = t.intersection([
t.interface({
key: t.string,
value: t.string,
@@ -19,4 +19,18 @@ export const SyntheticsParamCode = t.intersection([
}),
]);
-export type SyntheticsParam = t.TypeOf;
+export type SyntheticsParamSO = t.TypeOf;
+
+export const SyntheticsParamRequestCodec = t.intersection([
+ t.interface({
+ key: t.string,
+ value: t.string,
+ }),
+ t.partial({
+ description: t.string,
+ tags: t.array(t.string),
+ share_across_spaces: t.boolean,
+ }),
+]);
+
+export type SyntheticsParamRequest = t.TypeOf;
diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/settings/global_params/add_param_flyout.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/settings/global_params/add_param_flyout.tsx
index 7f89c6dc0d3c03..64f19ad901003d 100644
--- a/x-pack/plugins/synthetics/public/apps/synthetics/components/settings/global_params/add_param_flyout.tsx
+++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/settings/global_params/add_param_flyout.tsx
@@ -5,6 +5,7 @@
* 2.0.
*/
import React, { useCallback, useEffect, useState } from 'react';
+import { ALL_SPACES_ID } from '@kbn/security-plugin/public';
import {
EuiFlyout,
EuiFlyoutBody,
@@ -25,7 +26,7 @@ import { useDispatch } from 'react-redux';
import { apiService } from '../../../../../utils/api_service';
import { ClientPluginsStart } from '../../../../../plugin';
import { ListParamItem } from './params_list';
-import { SyntheticsParam } from '../../../../../../common/runtime_types';
+import { SyntheticsParamSO } from '../../../../../../common/runtime_types';
import { useFormWrapped } from '../../../../../hooks/use_form_wrapped';
import { AddParamForm } from './add_param_form';
import { SYNTHETICS_API_URLS } from '../../../../../../common/constants';
@@ -46,7 +47,7 @@ export const AddParamFlyout = ({
const { id, ...dataToSave } = isEditingItem ?? {};
- const form = useFormWrapped({
+ const form = useFormWrapped({
mode: 'onSubmit',
reValidateMode: 'onChange',
shouldFocusError: true,
@@ -66,7 +67,7 @@ export const AddParamFlyout = ({
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [setIsEditingItem]);
- const [paramData, setParamData] = useState(null);
+ const [paramData, setParamData] = useState(null);
const { application } = useKibana().services;
@@ -74,15 +75,24 @@ export const AddParamFlyout = ({
if (!paramData) {
return;
}
+ const { namespaces, ...paramRequest } = paramData;
+ const shareAcrossSpaces = namespaces?.includes(ALL_SPACES_ID);
if (isEditingItem) {
- return apiService.put(SYNTHETICS_API_URLS.PARAMS, { id, ...paramData });
+ return apiService.put(SYNTHETICS_API_URLS.PARAMS, {
+ id,
+ ...paramRequest,
+ share_across_spaces: shareAcrossSpaces,
+ });
}
- return apiService.post(SYNTHETICS_API_URLS.PARAMS, paramData);
+ return apiService.post(SYNTHETICS_API_URLS.PARAMS, {
+ ...paramRequest,
+ share_across_spaces: shareAcrossSpaces,
+ });
}, [paramData]);
const canSave = (application?.capabilities.uptime.save ?? false) as boolean;
- const onSubmit = (formData: SyntheticsParam) => {
+ const onSubmit = (formData: SyntheticsParamSO) => {
setParamData(formData);
};
diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/settings/global_params/add_param_form.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/settings/global_params/add_param_form.tsx
index af079c474afec0..240314dc3b06fe 100644
--- a/x-pack/plugins/synthetics/public/apps/synthetics/components/settings/global_params/add_param_form.tsx
+++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/settings/global_params/add_param_form.tsx
@@ -5,6 +5,7 @@
* 2.0.
*/
import React from 'react';
+import { ALL_SPACES_ID } from '@kbn/security-plugin/public';
import {
EuiCheckbox,
EuiComboBox,
@@ -15,7 +16,7 @@ import {
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { Controller, useFormContext, useFormState } from 'react-hook-form';
-import { SyntheticsParam } from '../../../../../../common/runtime_types';
+import { SyntheticsParamSO } from '../../../../../../common/runtime_types';
import { ListParamItem } from './params_list';
export const AddParamForm = ({
@@ -25,8 +26,8 @@ export const AddParamForm = ({
items: ListParamItem[];
isEditingItem: ListParamItem | null;
}) => {
- const { register, control } = useFormContext();
- const { errors } = useFormState();
+ const { register, control } = useFormContext();
+ const { errors } = useFormState();
const tagsList = items.reduce((acc, item) => {
const tags = item.tags || [];
@@ -119,7 +120,7 @@ export const AddParamForm = ({
aria-label={NAMESPACES_LABEL}
onChange={(e) => {
if (e.target.checked) {
- field.onChange(['*']);
+ field.onChange([ALL_SPACES_ID]);
} else {
field.onChange([]);
}
diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/settings/global_params/params_list.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/settings/global_params/params_list.tsx
index e8f43c081d2da6..8f06492eb879ba 100644
--- a/x-pack/plugins/synthetics/public/apps/synthetics/components/settings/global_params/params_list.tsx
+++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/settings/global_params/params_list.tsx
@@ -24,12 +24,12 @@ import { EuiBasicTableColumn } from '@elastic/eui/src/components/basic_table/bas
import { useDebounce } from 'react-use';
import { TableTitle } from '../../common/components/table_title';
import { ParamsText } from './params_text';
-import { SyntheticsParam } from '../../../../../../common/runtime_types';
+import { SyntheticsParamSO } from '../../../../../../common/runtime_types';
import { useParamsList } from '../hooks/use_params_list';
import { AddParamFlyout } from './add_param_flyout';
import { DeleteParam } from './delete_param';
-export interface ListParamItem extends SyntheticsParam {
+export interface ListParamItem extends SyntheticsParamSO {
id: string;
}
diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/settings/hooks/use_params_list.ts b/x-pack/plugins/synthetics/public/apps/synthetics/components/settings/hooks/use_params_list.ts
index 9b566f61074387..ad95851ea68c0b 100644
--- a/x-pack/plugins/synthetics/public/apps/synthetics/components/settings/hooks/use_params_list.ts
+++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/settings/hooks/use_params_list.ts
@@ -8,13 +8,13 @@
import { useFetcher } from '@kbn/observability-plugin/public';
import { SavedObject } from '@kbn/core-saved-objects-common';
import { useMemo } from 'react';
-import { SyntheticsParam } from '../../../../../../common/runtime_types';
+import { SyntheticsParamSO } from '../../../../../../common/runtime_types';
import { apiService } from '../../../../../utils/api_service';
import { SYNTHETICS_API_URLS } from '../../../../../../common/constants';
export const useParamsList = (lastRefresh: number) => {
const { data, loading } = useFetcher<
- Promise<{ data: Array> }>
+ Promise<{ data: Array> }>
>(() => {
return apiService.get(SYNTHETICS_API_URLS.PARAMS);
}, [lastRefresh]);
diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/test_now_mode/hooks/use_run_once_errors.ts b/x-pack/plugins/synthetics/public/apps/synthetics/components/test_now_mode/hooks/use_run_once_errors.ts
index 6e0d8c0faf0efd..9859b5b0216bc9 100644
--- a/x-pack/plugins/synthetics/public/apps/synthetics/components/test_now_mode/hooks/use_run_once_errors.ts
+++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/test_now_mode/hooks/use_run_once_errors.ts
@@ -15,7 +15,9 @@ export function useRunOnceErrors({
serviceError,
errors,
locations,
+ showErrors = true,
}: {
+ showErrors?: boolean;
testRunId: string;
serviceError?: Error;
errors: ServiceLocationErrors;
@@ -55,40 +57,59 @@ export function useRunOnceErrors({
const expectPings =
publicLocations.length - (locationErrors ?? []).filter(({ locationId }) => !!locationId).length;
+ const locationErrorReasons = useMemo(() => {
+ return (locationErrors ?? [])
+ .map(({ error }) => error?.reason)
+ .filter((reason) => !!reason)
+ .filter((reason, i, arr) => arr.indexOf(reason) === i);
+ }, [locationErrors]);
const hasBlockingError =
!!runOnceServiceError ||
(locationErrors?.length && locationErrors?.length === publicLocations.length);
const errorMessages = useMemo(() => {
if (hasBlockingError) {
- return [{ name: 'Error', message: PushErrorService, title: PushErrorLabel }];
+ return locationErrorReasons.length === 1
+ ? [
+ {
+ name: 'Error',
+ message: locationErrorReasons[0] ?? PushErrorService,
+ title: RunErrorLabel,
+ },
+ ]
+ : [{ name: 'Error', message: PushErrorService, title: PushErrorLabel }];
} else if (locationErrors?.length > 0) {
// If only some of the locations were unsuccessful
return locationErrors
- .map(({ locationId }) => locationsById[locationId])
- .filter((location) => !!location)
- .map((location) => ({
+ .map(({ locationId, error }) => ({ location: locationsById[locationId], error }))
+ .filter((locationWithError) => !!locationWithError.location)
+ .map(({ location, error }) => ({
name: 'Error',
- message: getLocationTestErrorLabel(location.label),
+ message: getLocationTestErrorLabel(location.label, error?.reason ?? ''),
title: RunErrorLabel,
}));
}
return [];
- }, [locationsById, locationErrors, hasBlockingError]);
+ }, [locationsById, locationErrors, locationErrorReasons, hasBlockingError]);
useEffect(() => {
- errorMessages.forEach(
- ({ name, message, title }: { name: string; message: string; title: string }) => {
- kibanaService.toasts.addError({ name, message }, { title });
- }
- );
- }, [errorMessages]);
+ if (showErrors) {
+ errorMessages.forEach(
+ ({ name, message, title }: { name: string; message: string; title: string }) => {
+ kibanaService.toasts.addError({ name, message }, { title });
+ }
+ );
+ }
+ }, [errorMessages, showErrors]);
return {
expectPings,
hasBlockingError,
- blockingErrorMessage: hasBlockingError ? PushErrorService : null,
+ blockingErrorTitle: hasBlockingError ? PushErrorLabel : null,
+ blockingErrorMessage: hasBlockingError
+ ? `${PushErrorService} ${errorMessages[0]?.message}`
+ : null,
errorMessages,
};
}
@@ -101,10 +122,10 @@ const RunErrorLabel = i18n.translate('xpack.synthetics.testRun.runErrorLabel', {
defaultMessage: 'Error running test',
});
-const getLocationTestErrorLabel = (locationName: string) =>
- i18n.translate('xpack.synthetics.testRun.runErrorLocation', {
- defaultMessage: 'Failed to run monitor on location {locationName}.',
- values: { locationName },
+const getLocationTestErrorLabel = (locationName: string, reason: string) =>
+ i18n.translate('xpack.synthetics.testRun.runErrorLocation.reason', {
+ defaultMessage: 'Failed to run monitor on location {locationName}. {reason}',
+ values: { locationName, reason },
});
const PushErrorService = i18n.translate('xpack.synthetics.testRun.pushError', {
diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/test_now_mode/manual_test_run_mode/manual_test_run_mode.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/test_now_mode/manual_test_run_mode/manual_test_run_mode.tsx
index 4ed8aed3974d5c..1ab96ee2de55f5 100644
--- a/x-pack/plugins/synthetics/public/apps/synthetics/components/test_now_mode/manual_test_run_mode/manual_test_run_mode.tsx
+++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/test_now_mode/manual_test_run_mode/manual_test_run_mode.tsx
@@ -15,11 +15,14 @@ import { Locations } from '../../../../../../common/runtime_types';
export function ManualTestRunMode({
manualTestRun,
onDone,
+ showErrors,
}: {
+ showErrors: boolean;
manualTestRun: ManualTestRun;
onDone: (testRunId: string) => void;
}) {
const { expectPings } = useRunOnceErrors({
+ showErrors,
testRunId: manualTestRun.testRunId!,
locations: (manualTestRun.monitor!.locations ?? []) as Locations,
errors: manualTestRun.errors ?? [],
diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/test_now_mode/test_now_mode.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/test_now_mode/test_now_mode.tsx
index f3c37e71fa5654..842f82b3821293 100644
--- a/x-pack/plugins/synthetics/public/apps/synthetics/components/test_now_mode/test_now_mode.tsx
+++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/test_now_mode/test_now_mode.tsx
@@ -26,12 +26,13 @@ export function TestNowMode({
testRun: TestRun;
onDone: (testRunId: string) => void;
}) {
- const { hasBlockingError, blockingErrorMessage, expectPings } = useRunOnceErrors({
- testRunId: testRun.id,
- serviceError,
- errors: errors ?? [],
- locations: (testRun.monitor.locations ?? []) as Locations,
- });
+ const { hasBlockingError, blockingErrorTitle, blockingErrorMessage, expectPings } =
+ useRunOnceErrors({
+ testRunId: testRun.id,
+ serviceError,
+ errors: errors ?? [],
+ locations: (testRun.monitor.locations ?? []) as Locations,
+ });
useEffect(() => {
if (!isPushing && (!testRun.id || hasBlockingError)) {
@@ -46,7 +47,9 @@ export function TestNowMode({
return (
{(hasBlockingError && !isPushing && (
-
+
+ {blockingErrorMessage}
+
)) ||
null}
diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/test_now_mode/test_now_mode_flyout_container.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/test_now_mode/test_now_mode_flyout_container.tsx
index a0b7e4c6b30472..ddf21c869c1a29 100644
--- a/x-pack/plugins/synthetics/public/apps/synthetics/components/test_now_mode/test_now_mode_flyout_container.tsx
+++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/test_now_mode/test_now_mode_flyout_container.tsx
@@ -17,13 +17,6 @@ import {
testNowRunsSelector,
TestRunStatus,
} from '../../state/manual_test_runs';
-import { MonitorFields } from '../../../../../common/runtime_types';
-
-export interface TestRun {
- id: string;
- monitor: MonitorFields;
- runOnceMode: boolean;
-}
export function TestNowModeFlyoutContainer() {
const dispatch = useDispatch();
@@ -82,12 +75,15 @@ export function TestNowModeFlyoutContainer() {
return (
<>
{Object.values(testNowRuns)
- .filter((val) => val.testRunId)
+ .filter(
+ (val) => val.testRunId && (val.status === 'in-progress' || val.status === 'loading')
+ )
.map((manualTestRun) => (
))}
{flyout}
diff --git a/x-pack/plugins/synthetics/server/routes/monitor_cruds/add_monitor_project_legacy.ts b/x-pack/plugins/synthetics/server/routes/monitor_cruds/add_monitor_project_legacy.ts
index cfb1ca2f680e37..41d2b5f15ad800 100644
--- a/x-pack/plugins/synthetics/server/routes/monitor_cruds/add_monitor_project_legacy.ts
+++ b/x-pack/plugins/synthetics/server/routes/monitor_cruds/add_monitor_project_legacy.ts
@@ -5,14 +5,12 @@
* 2.0.
*/
import { schema } from '@kbn/config-schema';
-import { DEFAULT_SPACE_ID } from '@kbn/spaces-plugin/common';
+import { i18n } from '@kbn/i18n';
import { UMServerLibs } from '../../legacy_uptime/lib/lib';
import { ProjectMonitor } from '../../../common/runtime_types';
import { SyntheticsStreamingRouteFactory } from '../../legacy_uptime/routes/types';
import { API_URLS } from '../../../common/constants';
-import { getAllLocations } from '../../synthetics_service/get_all_locations';
-import { ProjectMonitorFormatterLegacy } from '../../synthetics_service/project_monitor/project_monitor_formatter_legacy';
const MAX_PAYLOAD_SIZE = 1048576 * 20; // 20MiB
@@ -33,63 +31,34 @@ export const addSyntheticsProjectMonitorRouteLegacy: SyntheticsStreamingRouteFac
maxBytes: MAX_PAYLOAD_SIZE,
},
},
- handler: async ({
- request,
- savedObjectsClient,
- server,
- syntheticsMonitorClient,
- subject,
- }): Promise => {
+ handler: async ({ server, subject, request }): Promise => {
const monitors = (request.body?.monitors as ProjectMonitor[]) || [];
-
- try {
- const { id: spaceId } = (await server.spaces?.spacesService.getActiveSpace(request)) ?? {
- id: DEFAULT_SPACE_ID,
- };
-
- const { keep_stale: keepStale, project: projectId } = request.body || {};
- const { publicLocations, privateLocations } = await getAllLocations({
- server,
- syntheticsMonitorClient,
- savedObjectsClient,
- });
- const encryptedSavedObjectsClient = server.encryptedSavedObjects.getClient();
-
- const pushMonitorFormatter = new ProjectMonitorFormatterLegacy({
- projectId,
- spaceId,
- keepStale,
- locations: publicLocations,
- privateLocations,
- encryptedSavedObjectsClient,
- savedObjectsClient,
- monitors,
- server,
- syntheticsMonitorClient,
- request,
- subject,
- });
-
- await pushMonitorFormatter.configureAllProjectMonitors();
-
- subject?.next({
- createdMonitors: pushMonitorFormatter.createdMonitors,
- updatedMonitors: pushMonitorFormatter.updatedMonitors,
- staleMonitors: pushMonitorFormatter.staleMonitors,
- deletedMonitors: pushMonitorFormatter.deletedMonitors,
- failedMonitors: pushMonitorFormatter.failedMonitors,
- failedStaleMonitors: pushMonitorFormatter.failedStaleMonitors,
- });
- } catch (error) {
- if (error?.output?.statusCode === 404) {
- const spaceId = server.spaces?.spacesService.getSpaceId(request) ?? DEFAULT_SPACE_ID;
- subject?.next(`Unable to create monitors. Kibana space '${spaceId}' does not exist.`);
- subject?.next({ failedMonitors: monitors.map((m) => m.id) });
- } else {
- subject?.error(error);
+ const failureMessage = i18n.translate('xpack.synthetics.server.projectMonitors.legacy.error', {
+ defaultMessage: `Failed to create monitor`,
+ });
+ const deprecationNotice = i18n.translate(
+ 'xpack.synthetics.server.projectMonitors.legacy.deprecation',
+ {
+ defaultMessage: `This version of @elastic/synthetics is not supported in Kibana {version}. Please upgrade to ^1.0.0.`,
+ values: {
+ version: server.stackVersion,
+ },
}
- } finally {
- subject?.complete();
- }
+ );
+
+ subject?.next(deprecationNotice);
+ subject?.next({
+ failedMonitors: monitors.map((m) => ({
+ id: m.id,
+ reason: failureMessage,
+ details: deprecationNotice,
+ })),
+ createdMonitors: [],
+ updatedMonitors: [],
+ staleMonitors: [],
+ deletedMonitors: [],
+ failedStaleMonitors: [],
+ });
+ subject?.complete();
},
});
diff --git a/x-pack/plugins/synthetics/server/routes/settings/add_param.ts b/x-pack/plugins/synthetics/server/routes/settings/add_param.ts
index a563ee83d2d551..401dda0ce3045c 100644
--- a/x-pack/plugins/synthetics/server/routes/settings/add_param.ts
+++ b/x-pack/plugins/synthetics/server/routes/settings/add_param.ts
@@ -6,8 +6,9 @@
*/
import { schema } from '@kbn/config-schema';
+import { ALL_SPACES_ID } from '@kbn/security-plugin/common/constants';
import { DEFAULT_SPACE_ID } from '@kbn/spaces-plugin/common';
-import { SyntheticsParam } from '../../../common/runtime_types';
+import { SyntheticsParamRequest, SyntheticsParamSO } from '../../../common/runtime_types';
import { syntheticsParamType } from '../../../common/types/saved_objects';
import { SyntheticsRestApiRouteFactory } from '../../legacy_uptime/routes/types';
import { SYNTHETICS_API_URLS } from '../../../common/constants';
@@ -15,25 +16,35 @@ import { SYNTHETICS_API_URLS } from '../../../common/constants';
export const addSyntheticsParamsRoute: SyntheticsRestApiRouteFactory = () => ({
method: 'POST',
path: SYNTHETICS_API_URLS.PARAMS,
-
validate: {
body: schema.object({
key: schema.string(),
value: schema.string(),
description: schema.maybe(schema.string()),
tags: schema.maybe(schema.arrayOf(schema.string())),
- namespaces: schema.maybe(schema.arrayOf(schema.string())),
+ share_across_spaces: schema.maybe(schema.boolean()),
}),
},
writeAccess: true,
- handler: async ({ request, server, savedObjectsClient }): Promise => {
- const { namespaces, ...data } = request.body as SyntheticsParam;
- const spaceId = server.spaces?.spacesService.getSpaceId(request) ?? DEFAULT_SPACE_ID;
+ handler: async ({ request, response, server, savedObjectsClient }): Promise => {
+ try {
+ const { id: spaceId } = (await server.spaces?.spacesService.getActiveSpace(request)) ?? {
+ id: DEFAULT_SPACE_ID,
+ };
+ const { share_across_spaces: shareAcrossSpaces, ...data } =
+ request.body as SyntheticsParamRequest;
- const result = await savedObjectsClient.create(syntheticsParamType, data, {
- initialNamespaces: (namespaces ?? []).length > 0 ? namespaces : [spaceId],
- });
+ const result = await savedObjectsClient.create(syntheticsParamType, data, {
+ initialNamespaces: shareAcrossSpaces ? [ALL_SPACES_ID] : [spaceId],
+ });
+ return { data: result };
+ } catch (error) {
+ if (error.output?.statusCode === 404) {
+ const spaceId = server.spaces?.spacesService.getSpaceId(request) ?? DEFAULT_SPACE_ID;
+ return response.notFound({ body: { message: `Kibana space '${spaceId}' does not exist` } });
+ }
- return { data: result };
+ throw error;
+ }
},
});
diff --git a/x-pack/plugins/synthetics/server/routes/settings/edit_param.ts b/x-pack/plugins/synthetics/server/routes/settings/edit_param.ts
index 82160111ef791d..2d1925f4c0182a 100644
--- a/x-pack/plugins/synthetics/server/routes/settings/edit_param.ts
+++ b/x-pack/plugins/synthetics/server/routes/settings/edit_param.ts
@@ -6,7 +6,8 @@
*/
import { schema } from '@kbn/config-schema';
-import { SyntheticsParam } from '../../../common/runtime_types';
+import { DEFAULT_SPACE_ID } from '@kbn/spaces-plugin/common';
+import { SyntheticsParamRequest } from '../../../common/runtime_types';
import { syntheticsParamType } from '../../../common/types/saved_objects';
import { SyntheticsRestApiRouteFactory } from '../../legacy_uptime/routes/types';
import { SYNTHETICS_API_URLS } from '../../../common/constants';
@@ -21,15 +22,33 @@ export const editSyntheticsParamsRoute: SyntheticsRestApiRouteFactory = () => ({
value: schema.string(),
description: schema.maybe(schema.string()),
tags: schema.maybe(schema.arrayOf(schema.string())),
- namespaces: schema.maybe(schema.arrayOf(schema.string())),
+ share_across_spaces: schema.maybe(schema.boolean()),
}),
},
writeAccess: true,
- handler: async ({ savedObjectsClient, request, server }): Promise => {
- const { namespaces, id, ...data } = request.body as SyntheticsParam & { id: string };
+ handler: async ({ savedObjectsClient, request, response, server }): Promise => {
+ try {
+ const { id: _spaceId } = (await server.spaces?.spacesService.getActiveSpace(request)) ?? {
+ id: DEFAULT_SPACE_ID,
+ };
+ const {
+ share_across_spaces: shareAcrossSpaces,
+ id,
+ ...data
+ } = request.body as SyntheticsParamRequest & {
+ id: string;
+ };
- const result = await savedObjectsClient.update(syntheticsParamType, id, data);
+ const result = await savedObjectsClient.update(syntheticsParamType, id, data);
- return { data: result };
+ return { data: result };
+ } catch (error) {
+ if (error.output?.statusCode === 404) {
+ const spaceId = server.spaces?.spacesService.getSpaceId(request) ?? DEFAULT_SPACE_ID;
+ return response.notFound({ body: { message: `Kibana space '${spaceId}' does not exist` } });
+ }
+
+ throw error;
+ }
},
});
diff --git a/x-pack/plugins/synthetics/server/routes/settings/params.ts b/x-pack/plugins/synthetics/server/routes/settings/params.ts
index e713ca7a33ffc2..31e6770e598a18 100644
--- a/x-pack/plugins/synthetics/server/routes/settings/params.ts
+++ b/x-pack/plugins/synthetics/server/routes/settings/params.ts
@@ -15,35 +15,46 @@ export const getSyntheticsParamsRoute: SyntheticsRestApiRouteFactory = () => ({
method: 'GET',
path: SYNTHETICS_API_URLS.PARAMS,
validate: {},
- handler: async ({ savedObjectsClient, request, server }): Promise => {
- const encryptedSavedObjectsClient = server.encryptedSavedObjects.getClient();
-
- const spaceId = server.spaces?.spacesService.getSpaceId(request) ?? DEFAULT_SPACE_ID;
-
- const canSave =
- (await server.coreStart?.capabilities.resolveCapabilities(request)).uptime.save ?? false;
-
- if (canSave) {
- const finder =
- await encryptedSavedObjectsClient.createPointInTimeFinderDecryptedAsInternalUser({
+ handler: async ({ savedObjectsClient, request, response, server }): Promise => {
+ try {
+ const encryptedSavedObjectsClient = server.encryptedSavedObjects.getClient();
+
+ const { id: spaceId } = (await server.spaces?.spacesService.getActiveSpace(request)) ?? {
+ id: DEFAULT_SPACE_ID,
+ };
+
+ const canSave =
+ (await server.coreStart?.capabilities.resolveCapabilities(request)).uptime.save ?? false;
+
+ if (canSave) {
+ const finder =
+ await encryptedSavedObjectsClient.createPointInTimeFinderDecryptedAsInternalUser({
+ type: syntheticsParamType,
+ perPage: 1000,
+ namespaces: [spaceId],
+ });
+
+ const hits: SavedObjectsFindResult[] = [];
+ for await (const result of finder.find()) {
+ hits.push(...result.saved_objects);
+ }
+
+ return { data: hits };
+ } else {
+ const data = await savedObjectsClient.find({
type: syntheticsParamType,
- perPage: 1000,
- namespaces: [spaceId],
+ perPage: 10000,
});
- const hits: SavedObjectsFindResult[] = [];
- for await (const result of finder.find()) {
- hits.push(...result.saved_objects);
+ return { data: data.saved_objects };
+ }
+ } catch (error) {
+ if (error.output?.statusCode === 404) {
+ const spaceId = server.spaces?.spacesService.getSpaceId(request) ?? DEFAULT_SPACE_ID;
+ return response.notFound({ body: { message: `Kibana space '${spaceId}' does not exist` } });
}
- return { data: hits };
- } else {
- const data = await savedObjectsClient.find({
- type: syntheticsParamType,
- perPage: 10000,
- });
-
- return { data: data.saved_objects };
+ throw error;
}
},
});
diff --git a/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/project_monitor_formatter_legacy.test.ts b/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/project_monitor_formatter_legacy.test.ts
deleted file mode 100644
index a7ae725337b068..00000000000000
--- a/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/project_monitor_formatter_legacy.test.ts
+++ /dev/null
@@ -1,633 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-import { loggerMock } from '@kbn/logging-mocks';
-import { savedObjectsClientMock } from '@kbn/core/server/mocks';
-import {
- INSUFFICIENT_FLEET_PERMISSIONS,
- ProjectMonitorFormatterLegacy,
-} from './project_monitor_formatter_legacy';
-import { ConfigKey, DataStream, LocationStatus } from '../../../common/runtime_types';
-import { DEFAULT_FIELDS } from '../../../common/constants/monitor_defaults';
-import { times } from 'lodash';
-import { SyntheticsService } from '../synthetics_service';
-import { UptimeServerSetup } from '../../legacy_uptime/lib/adapters';
-import { encryptedSavedObjectsMock } from '@kbn/encrypted-saved-objects-plugin/server/mocks';
-import { SyntheticsMonitorClient } from '../synthetics_monitor/synthetics_monitor_client';
-import { httpServerMock } from '@kbn/core-http-server-mocks';
-import { Subject } from 'rxjs';
-import { formatSecrets } from '../utils';
-
-import * as telemetryHooks from '../../routes/telemetry/monitor_upgrade_sender';
-import { formatLocation } from '../../../common/utils/location_formatter';
-import { mockEncryptedSO } from '../utils/mocks';
-
-const testMonitors = [
- {
- type: 'browser',
- throttling: { download: 5, upload: 3, latency: 20 },
- schedule: 3,
- locations: [],
- privateLocations: ['Test private location'],
- params: { url: 'http://localhost:8080' },
- playwrightOptions: {
- userAgent:
- 'Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1',
- viewport: { width: 375, height: 667 },
- deviceScaleFactor: 2,
- isMobile: true,
- hasTouch: true,
- headless: true,
- },
- name: 'check if title is present 10 0',
- id: 'check if title is present 10 0',
- tags: [],
- content:
- 'UEsDBBQACAAIAAAAIQAAAAAAAAAAAAAAAAAQAAAAYmFzaWMuam91cm5leS50c2WQQU7DQAxF9znFV8QiUUOmXcCCUMQl2NdMnWbKJDMaO6Ilyt0JASQkNv9Z1teTZWNAIqwP5kU4iZGOug863u7uDXsSddbIddCOl0kMX6iPnsVoOAYxryTO1ucwpoGvtUrm+hiSYsLProIoxwp8iWwVM9oUeuTP/9V5k7UhofCscNhj2yx4xN2CzabElOHXWRxsx/YNroU69QwniImFB8Vui5vJzYcKxYRIJ66WTNQL5hL7p1WD9aYi9zQOtgPFGPNqecJ1sCj+tAB6J6erpj4FDcW3qh6TL5u1Mq/8yjn7BFBLBwhGDIWc4QAAAEkBAABQSwECLQMUAAgACAAAACEARgyFnOEAAABJAQAAEAAAAAAAAAAAACAApIEAAAAAYmFzaWMuam91cm5leS50c1BLBQYAAAAAAQABAD4AAAAfAQAAAAA=',
- filter: { match: 'check if title is present 10 0' },
- hash: 'lleklrkelkj',
- },
- {
- type: 'browser',
- throttling: { download: 5, upload: 3, latency: 20 },
- schedule: 3,
- locations: [],
- privateLocations: ['Test private location'],
- params: { url: 'http://localhost:8080' },
- playwrightOptions: {
- userAgent:
- 'Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1',
- viewport: { width: 375, height: 667 },
- deviceScaleFactor: 2,
- isMobile: true,
- hasTouch: true,
- headless: true,
- },
- name: 'check if title is present 10 1',
- id: 'check if title is present 10 1',
- tags: [],
- content:
- 'UEsDBBQACAAIAAAAIQAAAAAAAAAAAAAAAAAQAAAAYmFzaWMuam91cm5leS50c2WQQU7DQAxF9znFV8QiUUOmXcCCUMQl2NdMnWbKJDMaO6Ilyt0JASQkNv9Z1teTZWNAIqwP5kU4iZGOug863u7uDXsSddbIddCOl0kMX6iPnsVoOAYxryTO1ucwpoGvtUrm+hiSYsLProIoxwp8iWwVM9oUeuTP/9V5k7UhofCscNhj2yx4xN2CzabElOHXWRxsx/YNroU69QwniImFB8Vui5vJzYcKxYRIJ66WTNQL5hL7p1WD9aYi9zQOtgPFGPNqecJ1sCj+tAB6J6erpj4FDcW3qh6TL5u1Mq/8yjn7BFBLBwhGDIWc4QAAAEkBAABQSwECLQMUAAgACAAAACEARgyFnOEAAABJAQAAEAAAAAAAAAAAACAApIEAAAAAYmFzaWMuam91cm5leS50c1BLBQYAAAAAAQABAD4AAAAfAQAAAAA=',
- filter: { match: 'check if title is present 10 1' },
- hash: 'lleklrkelkj',
- },
-];
-
-const privateLocations = times(1).map((n) => {
- return {
- id: `loc-${n}`,
- label: 'Test private location',
- geo: {
- lat: 0,
- lon: 0,
- },
- isServiceManaged: false,
- agentPolicyId: `loc-${n}`,
- concurrentMonitors: 1,
- };
-});
-
-describe('ProjectMonitorFormatterLegacy', () => {
- const mockEsClient = {
- search: jest.fn(),
- };
- const logger = loggerMock.create();
-
- const kibanaRequest = httpServerMock.createKibanaRequest();
-
- const soClient = savedObjectsClientMock.create();
-
- const serverMock: UptimeServerSetup = {
- logger,
- uptimeEsClient: mockEsClient,
- authSavedObjectsClient: soClient,
- config: {
- service: {
- username: 'dev',
- password: '12345',
- manifestUrl: 'http://localhost:8080/api/manifest',
- },
- },
- spaces: {
- spacesService: {
- getSpaceId: jest.fn().mockReturnValue('test-space'),
- },
- },
- encryptedSavedObjects: mockEncryptedSO(),
- } as unknown as UptimeServerSetup;
-
- const syntheticsService = new SyntheticsService(serverMock);
-
- syntheticsService.addConfig = jest.fn();
- syntheticsService.editConfig = jest.fn();
- syntheticsService.deleteConfigs = jest.fn();
-
- const encryptedSavedObjectsClient = encryptedSavedObjectsMock.createStart().getClient();
-
- const locations = times(3).map((n) => {
- return {
- id: `loc-${n}`,
- label: `Location ${n}`,
- url: `https://example.com/${n}`,
- geo: {
- lat: 0,
- lon: 0,
- },
- isServiceManaged: true,
- status: LocationStatus.GA,
- };
- });
-
- const monitorClient = new SyntheticsMonitorClient(syntheticsService, serverMock);
-
- it('should return errors', async () => {
- const testSubject = new Subject();
-
- testSubject.next = jest.fn();
-
- const pushMonitorFormatter = new ProjectMonitorFormatterLegacy({
- projectId: 'test-project',
- spaceId: 'default-space',
- keepStale: false,
- locations,
- privateLocations,
- encryptedSavedObjectsClient,
- savedObjectsClient: soClient,
- monitors: testMonitors,
- server: serverMock,
- syntheticsMonitorClient: monitorClient,
- request: kibanaRequest,
- subject: testSubject,
- });
-
- pushMonitorFormatter.getProjectMonitorsForProject = jest.fn().mockResolvedValue([]);
-
- await pushMonitorFormatter.configureAllProjectMonitors();
-
- expect(testSubject.next).toHaveBeenNthCalledWith(
- 1,
- 'check if title is present 10 0: failed to create or update monitor'
- );
- expect(testSubject.next).toHaveBeenNthCalledWith(
- 2,
- 'check if title is present 10 1: failed to create or update monitor'
- );
-
- expect({
- createdMonitors: pushMonitorFormatter.createdMonitors,
- updatedMonitors: pushMonitorFormatter.updatedMonitors,
- staleMonitors: pushMonitorFormatter.staleMonitors,
- deletedMonitors: pushMonitorFormatter.deletedMonitors,
- failedMonitors: pushMonitorFormatter.failedMonitors,
- failedStaleMonitors: pushMonitorFormatter.failedStaleMonitors,
- }).toStrictEqual({
- createdMonitors: [],
- deletedMonitors: [],
- failedMonitors: [
- {
- details: "Cannot read properties of undefined (reading 'authz')",
- id: 'check if title is present 10 0',
- payload: testMonitors[0],
- reason: 'Failed to create or update monitor',
- },
- {
- details: "Cannot read properties of undefined (reading 'authz')",
- id: 'check if title is present 10 1',
- payload: testMonitors[1],
- reason: 'Failed to create or update monitor',
- },
- ],
- failedStaleMonitors: [],
- staleMonitors: [],
- updatedMonitors: [],
- });
- });
-
- it('throws fleet permission error', async () => {
- const testSubject = new Subject();
-
- serverMock.fleet = {
- authz: {
- fromRequest: jest
- .fn()
- .mockResolvedValue({ integrations: { writeIntegrationPolicies: false } }),
- },
- } as any;
-
- const pushMonitorFormatter = new ProjectMonitorFormatterLegacy({
- projectId: 'test-project',
- spaceId: 'default-space',
- keepStale: false,
- locations,
- privateLocations,
- encryptedSavedObjectsClient,
- savedObjectsClient: soClient,
- monitors: testMonitors,
- server: serverMock,
- syntheticsMonitorClient: monitorClient,
- request: kibanaRequest,
- subject: testSubject,
- });
-
- pushMonitorFormatter.getProjectMonitorsForProject = jest.fn().mockResolvedValue([]);
-
- await pushMonitorFormatter.configureAllProjectMonitors();
-
- expect({
- createdMonitors: pushMonitorFormatter.createdMonitors,
- updatedMonitors: pushMonitorFormatter.updatedMonitors,
- staleMonitors: pushMonitorFormatter.staleMonitors,
- deletedMonitors: pushMonitorFormatter.deletedMonitors,
- failedMonitors: pushMonitorFormatter.failedMonitors,
- failedStaleMonitors: pushMonitorFormatter.failedStaleMonitors,
- }).toStrictEqual({
- createdMonitors: [],
- deletedMonitors: [],
- failedMonitors: [
- {
- details: INSUFFICIENT_FLEET_PERMISSIONS,
- id: 'check if title is present 10 0',
- payload: testMonitors[0],
- reason: 'Failed to create or update monitor',
- },
- {
- details: INSUFFICIENT_FLEET_PERMISSIONS,
- id: 'check if title is present 10 1',
- payload: testMonitors[1],
- reason: 'Failed to create or update monitor',
- },
- ],
- failedStaleMonitors: [],
- staleMonitors: [],
- updatedMonitors: [],
- });
- });
-
- it('catches errors from bulk edit method', async () => {
- const testSubject = new Subject();
-
- serverMock.fleet = {
- authz: {
- fromRequest: jest
- .fn()
- .mockResolvedValue({ integrations: { writeIntegrationPolicies: true } }),
- },
- } as any;
-
- const pushMonitorFormatter = new ProjectMonitorFormatterLegacy({
- projectId: 'test-project',
- spaceId: 'default-space',
- keepStale: false,
- locations,
- privateLocations,
- encryptedSavedObjectsClient,
- savedObjectsClient: soClient,
- monitors: testMonitors,
- server: serverMock,
- syntheticsMonitorClient: monitorClient,
- request: kibanaRequest,
- subject: testSubject,
- });
-
- pushMonitorFormatter.getProjectMonitorsForProject = jest.fn().mockResolvedValue([]);
-
- await pushMonitorFormatter.configureAllProjectMonitors();
-
- expect({
- createdMonitors: pushMonitorFormatter.createdMonitors,
- updatedMonitors: pushMonitorFormatter.updatedMonitors,
- staleMonitors: pushMonitorFormatter.staleMonitors,
- deletedMonitors: pushMonitorFormatter.deletedMonitors,
- failedMonitors: pushMonitorFormatter.failedMonitors,
- failedStaleMonitors: pushMonitorFormatter.failedStaleMonitors,
- }).toEqual({
- createdMonitors: [],
- updatedMonitors: [],
- staleMonitors: [],
- deletedMonitors: [],
- failedMonitors: [
- {
- details: "Cannot read properties of undefined (reading 'buildPackagePolicyFromPackage')",
- payload: payloadData,
- reason: 'Failed to create 2 monitors',
- },
- ],
- failedStaleMonitors: [],
- });
- });
-
- it('configures project monitors when there are errors', async () => {
- const testSubject = new Subject();
-
- serverMock.fleet = {
- authz: {
- fromRequest: jest
- .fn()
- .mockResolvedValue({ integrations: { writeIntegrationPolicies: true } }),
- },
- } as any;
-
- soClient.bulkCreate = jest.fn().mockResolvedValue({ saved_objects: [] });
-
- const pushMonitorFormatter = new ProjectMonitorFormatterLegacy({
- projectId: 'test-project',
- spaceId: 'default-space',
- keepStale: false,
- locations,
- privateLocations,
- encryptedSavedObjectsClient,
- savedObjectsClient: soClient,
- monitors: testMonitors,
- server: serverMock,
- syntheticsMonitorClient: monitorClient,
- request: kibanaRequest,
- subject: testSubject,
- });
-
- pushMonitorFormatter.getProjectMonitorsForProject = jest.fn().mockResolvedValue([]);
-
- await pushMonitorFormatter.configureAllProjectMonitors();
-
- expect({
- createdMonitors: pushMonitorFormatter.createdMonitors,
- updatedMonitors: pushMonitorFormatter.updatedMonitors,
- staleMonitors: pushMonitorFormatter.staleMonitors,
- deletedMonitors: pushMonitorFormatter.deletedMonitors,
- failedMonitors: pushMonitorFormatter.failedMonitors,
- failedStaleMonitors: pushMonitorFormatter.failedStaleMonitors,
- }).toEqual({
- createdMonitors: [],
- updatedMonitors: [],
- staleMonitors: [],
- deletedMonitors: [],
- failedMonitors: [
- {
- details: "Cannot read properties of undefined (reading 'buildPackagePolicyFromPackage')",
- payload: payloadData,
- reason: 'Failed to create 2 monitors',
- },
- ],
- failedStaleMonitors: [],
- });
- });
-
- it('shows errors thrown by fleet api', async () => {
- const testSubject = new Subject();
-
- serverMock.fleet = {
- authz: {
- fromRequest: jest
- .fn()
- .mockResolvedValue({ integrations: { writeIntegrationPolicies: true } }),
- },
- packagePolicyService: {},
- } as any;
-
- soClient.bulkCreate = jest.fn().mockResolvedValue({ saved_objects: soResult });
-
- const pushMonitorFormatter = new ProjectMonitorFormatterLegacy({
- projectId: 'test-project',
- spaceId: 'default-space',
- keepStale: false,
- locations,
- privateLocations,
- encryptedSavedObjectsClient,
- savedObjectsClient: soClient,
- monitors: testMonitors,
- server: serverMock,
- syntheticsMonitorClient: monitorClient,
- request: kibanaRequest,
- subject: testSubject,
- });
-
- pushMonitorFormatter.getProjectMonitorsForProject = jest.fn().mockResolvedValue([]);
-
- await pushMonitorFormatter.configureAllProjectMonitors();
-
- expect({
- createdMonitors: pushMonitorFormatter.createdMonitors,
- updatedMonitors: pushMonitorFormatter.updatedMonitors,
- staleMonitors: pushMonitorFormatter.staleMonitors,
- deletedMonitors: pushMonitorFormatter.deletedMonitors,
- failedMonitors: pushMonitorFormatter.failedMonitors,
- failedStaleMonitors: pushMonitorFormatter.failedStaleMonitors,
- }).toEqual({
- createdMonitors: [],
- updatedMonitors: [],
- staleMonitors: [],
- deletedMonitors: [],
- failedMonitors: [
- {
- details:
- 'this.server.fleet.packagePolicyService.buildPackagePolicyFromPackage is not a function',
- reason: 'Failed to create 2 monitors',
- payload: payloadData,
- },
- ],
- failedStaleMonitors: [],
- });
- });
-
- it('creates project monitors when no errors', async () => {
- const testSubject = new Subject();
-
- serverMock.fleet = {
- authz: {
- fromRequest: jest
- .fn()
- .mockResolvedValue({ integrations: { writeIntegrationPolicies: true } }),
- },
- } as any;
-
- soClient.bulkCreate = jest.fn().mockResolvedValue({ saved_objects: soResult });
-
- monitorClient.addMonitors = jest.fn().mockReturnValue({});
-
- const telemetrySpy = jest
- .spyOn(telemetryHooks, 'sendTelemetryEvents')
- .mockImplementation(jest.fn());
-
- const pushMonitorFormatter = new ProjectMonitorFormatterLegacy({
- projectId: 'test-project',
- spaceId: 'default-space',
- keepStale: false,
- locations,
- privateLocations,
- encryptedSavedObjectsClient,
- savedObjectsClient: soClient,
- monitors: testMonitors,
- server: serverMock,
- syntheticsMonitorClient: monitorClient,
- request: kibanaRequest,
- subject: testSubject,
- });
-
- pushMonitorFormatter.getProjectMonitorsForProject = jest.fn().mockResolvedValue([]);
-
- await pushMonitorFormatter.configureAllProjectMonitors();
-
- expect(soClient.bulkCreate).toHaveBeenCalledWith(
- expect.arrayContaining([
- expect.objectContaining({
- ...soData[0],
- attributes: {
- ...soData[0].attributes,
- [ConfigKey.MONITOR_QUERY_ID]: expect.any(String),
- [ConfigKey.CONFIG_ID]: expect.any(String),
- },
- }),
- expect.objectContaining({
- ...soData[1],
- attributes: {
- ...soData[1].attributes,
- [ConfigKey.MONITOR_QUERY_ID]: expect.any(String),
- [ConfigKey.CONFIG_ID]: expect.any(String),
- },
- }),
- ])
- );
-
- expect(telemetrySpy).toHaveBeenCalledTimes(2);
-
- expect({
- createdMonitors: pushMonitorFormatter.createdMonitors,
- updatedMonitors: pushMonitorFormatter.updatedMonitors,
- staleMonitors: pushMonitorFormatter.staleMonitors,
- deletedMonitors: pushMonitorFormatter.deletedMonitors,
- failedMonitors: pushMonitorFormatter.failedMonitors,
- failedStaleMonitors: pushMonitorFormatter.failedStaleMonitors,
- }).toEqual({
- createdMonitors: ['check if title is present 10 0', 'check if title is present 10 1'],
- updatedMonitors: [],
- staleMonitors: [],
- deletedMonitors: [],
- failedMonitors: [],
- failedStaleMonitors: [],
- });
- });
-});
-
-const payloadData = [
- {
- ...DEFAULT_FIELDS[DataStream.BROWSER],
- __ui: {
- script_source: {
- file_name: '',
- is_generated_script: false,
- },
- },
- config_id: '',
- custom_heartbeat_id: 'check if title is present 10 0-test-project-default-space',
- enabled: true,
- 'filter_journeys.match': 'check if title is present 10 0',
- 'filter_journeys.tags': [],
- form_monitor_type: 'multistep',
- ignore_https_errors: false,
- journey_id: 'check if title is present 10 0',
- locations: privateLocations.map((l) => formatLocation(l)),
- name: 'check if title is present 10 0',
- namespace: 'default_space',
- origin: 'project',
- original_space: 'default-space',
- params: '{"url":"http://localhost:8080"}',
- playwright_options:
- '{"userAgent":"Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1","viewport":{"width":375,"height":667},"deviceScaleFactor":2,"isMobile":true,"hasTouch":true,"headless":true}',
- playwright_text_assertion: '',
- project_id: 'test-project',
- schedule: {
- number: '3',
- unit: 'm',
- },
- screenshots: 'on',
- 'service.name': '',
- 'source.inline.script': '',
- 'source.project.content':
- 'UEsDBBQACAAIAAAAIQAAAAAAAAAAAAAAAAAQAAAAYmFzaWMuam91cm5leS50c2WQQU7DQAxF9znFV8QiUUOmXcCCUMQl2NdMnWbKJDMaO6Ilyt0JASQkNv9Z1teTZWNAIqwP5kU4iZGOug863u7uDXsSddbIddCOl0kMX6iPnsVoOAYxryTO1ucwpoGvtUrm+hiSYsLProIoxwp8iWwVM9oUeuTP/9V5k7UhofCscNhj2yx4xN2CzabElOHXWRxsx/YNroU69QwniImFB8Vui5vJzYcKxYRIJ66WTNQL5hL7p1WD9aYi9zQOtgPFGPNqecJ1sCj+tAB6J6erpj4FDcW3qh6TL5u1Mq/8yjn7BFBLBwhGDIWc4QAAAEkBAABQSwECLQMUAAgACAAAACEARgyFnOEAAABJAQAAEAAAAAAAAAAAACAApIEAAAAAYmFzaWMuam91cm5leS50c1BLBQYAAAAAAQABAD4AAAAfAQAAAAA=',
- 'ssl.certificate': '',
- 'ssl.certificate_authorities': '',
- 'ssl.key': '',
- 'ssl.key_passphrase': '',
- 'ssl.supported_protocols': ['TLSv1.1', 'TLSv1.2', 'TLSv1.3'],
- 'ssl.verification_mode': 'full',
- synthetics_args: [],
- tags: [],
- timeout: null,
- type: 'browser',
- 'url.port': null,
- urls: '',
- id: '',
- hash: 'lleklrkelkj',
- },
- {
- ...DEFAULT_FIELDS[DataStream.BROWSER],
- __ui: {
- script_source: {
- file_name: '',
- is_generated_script: false,
- },
- },
- config_id: '',
- custom_heartbeat_id: 'check if title is present 10 1-test-project-default-space',
- enabled: true,
- 'filter_journeys.match': 'check if title is present 10 1',
- 'filter_journeys.tags': [],
- form_monitor_type: 'multistep',
- ignore_https_errors: false,
- journey_id: 'check if title is present 10 1',
- locations: privateLocations.map((l) => formatLocation(l)),
- name: 'check if title is present 10 1',
- namespace: 'default_space',
- origin: 'project',
- original_space: 'default-space',
- params: '{"url":"http://localhost:8080"}',
- playwright_options:
- '{"userAgent":"Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1","viewport":{"width":375,"height":667},"deviceScaleFactor":2,"isMobile":true,"hasTouch":true,"headless":true}',
- playwright_text_assertion: '',
- project_id: 'test-project',
- schedule: {
- number: '3',
- unit: 'm',
- },
- screenshots: 'on',
- 'service.name': '',
- 'source.inline.script': '',
- 'source.project.content':
- 'UEsDBBQACAAIAAAAIQAAAAAAAAAAAAAAAAAQAAAAYmFzaWMuam91cm5leS50c2WQQU7DQAxF9znFV8QiUUOmXcCCUMQl2NdMnWbKJDMaO6Ilyt0JASQkNv9Z1teTZWNAIqwP5kU4iZGOug863u7uDXsSddbIddCOl0kMX6iPnsVoOAYxryTO1ucwpoGvtUrm+hiSYsLProIoxwp8iWwVM9oUeuTP/9V5k7UhofCscNhj2yx4xN2CzabElOHXWRxsx/YNroU69QwniImFB8Vui5vJzYcKxYRIJ66WTNQL5hL7p1WD9aYi9zQOtgPFGPNqecJ1sCj+tAB6J6erpj4FDcW3qh6TL5u1Mq/8yjn7BFBLBwhGDIWc4QAAAEkBAABQSwECLQMUAAgACAAAACEARgyFnOEAAABJAQAAEAAAAAAAAAAAACAApIEAAAAAYmFzaWMuam91cm5leS50c1BLBQYAAAAAAQABAD4AAAAfAQAAAAA=',
- 'ssl.certificate': '',
- 'ssl.certificate_authorities': '',
- 'ssl.key': '',
- 'ssl.key_passphrase': '',
- 'ssl.supported_protocols': ['TLSv1.1', 'TLSv1.2', 'TLSv1.3'],
- 'ssl.verification_mode': 'full',
- synthetics_args: [],
- tags: [],
- timeout: null,
- type: 'browser',
- 'url.port': null,
- urls: '',
- id: '',
- hash: 'lleklrkelkj',
- },
-];
-
-const soData = [
- {
- attributes: formatSecrets({
- ...payloadData[0],
- revision: 1,
- } as any),
- type: 'synthetics-monitor',
- },
- {
- attributes: formatSecrets({
- ...payloadData[1],
- revision: 1,
- } as any),
- type: 'synthetics-monitor',
- },
-];
-
-const soResult = soData.map((so) => ({ id: 'test-id', ...so }));
diff --git a/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/project_monitor_formatter_legacy.ts b/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/project_monitor_formatter_legacy.ts
deleted file mode 100644
index eb2daaadf8d848..00000000000000
--- a/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/project_monitor_formatter_legacy.ts
+++ /dev/null
@@ -1,539 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-import type { Subject } from 'rxjs';
-import { omit, isEqual } from 'lodash';
-import { KibanaRequest } from '@kbn/core/server';
-import {
- SavedObjectsUpdateResponse,
- SavedObjectsClientContract,
- SavedObjectsFindResult,
-} from '@kbn/core/server';
-import pMap from 'p-map';
-import { EncryptedSavedObjectsClient } from '@kbn/encrypted-saved-objects-plugin/server';
-import { syncNewMonitorBulk } from '../../routes/monitor_cruds/bulk_cruds/add_monitor_bulk';
-import { deleteMonitorBulk } from '../../routes/monitor_cruds/bulk_cruds/delete_monitor_bulk';
-import { SyntheticsMonitorClient } from '../synthetics_monitor/synthetics_monitor_client';
-import { syncEditedMonitorBulk } from '../../routes/monitor_cruds/bulk_cruds/edit_monitor_bulk';
-import {
- ConfigKey,
- SyntheticsMonitorWithSecrets,
- EncryptedSyntheticsMonitor,
- ServiceLocationErrors,
- ProjectMonitor,
- Locations,
- SyntheticsMonitor,
- MonitorFields,
- PrivateLocation,
-} from '../../../common/runtime_types';
-import { syntheticsMonitorType } from '../../legacy_uptime/lib/saved_objects/synthetics_monitor';
-import type { UptimeServerSetup } from '../../legacy_uptime/lib/adapters';
-import { formatSecrets, normalizeSecrets } from '../utils/secrets';
-import {
- validateProjectMonitor,
- validateMonitor,
- ValidationResult,
-} from '../../routes/monitor_cruds/monitor_validation';
-import { normalizeProjectMonitor } from './normalizers';
-
-interface StaleMonitor {
- stale: boolean;
- journeyId: string;
- savedObjectId: string;
-}
-type StaleMonitorMap = Record;
-type FailedError = Array<{ id?: string; reason: string; details: string; payload?: object }>;
-
-export const INSUFFICIENT_FLEET_PERMISSIONS =
- 'Insufficient permissions. In order to configure private locations, you must have Fleet and Integrations write permissions. To resolve, please generate a new API key with a user who has Fleet and Integrations write permissions.';
-
-export class ProjectMonitorFormatterLegacy {
- private projectId: string;
- private spaceId: string;
- private keepStale: boolean;
- private locations: Locations;
- private privateLocations: PrivateLocation[];
- private savedObjectsClient: SavedObjectsClientContract;
- private encryptedSavedObjectsClient: EncryptedSavedObjectsClient;
- private staleMonitorsMap: StaleMonitorMap = {};
- private monitors: ProjectMonitor[] = [];
- public createdMonitors: string[] = [];
- public deletedMonitors: string[] = [];
- public updatedMonitors: string[] = [];
- public staleMonitors: string[] = [];
- public failedMonitors: FailedError = [];
- public failedStaleMonitors: FailedError = [];
- private server: UptimeServerSetup;
- private projectFilter: string;
- private syntheticsMonitorClient: SyntheticsMonitorClient;
- private request: KibanaRequest;
- private subject?: Subject;
-
- private writeIntegrationPoliciesPermissions?: boolean;
-
- constructor({
- locations,
- privateLocations,
- keepStale,
- savedObjectsClient,
- encryptedSavedObjectsClient,
- projectId,
- spaceId,
- monitors,
- server,
- syntheticsMonitorClient,
- request,
- subject,
- }: {
- locations: Locations;
- privateLocations: PrivateLocation[];
- keepStale: boolean;
- savedObjectsClient: SavedObjectsClientContract;
- encryptedSavedObjectsClient: EncryptedSavedObjectsClient;
- projectId: string;
- spaceId: string;
- monitors: ProjectMonitor[];
- server: UptimeServerSetup;
- syntheticsMonitorClient: SyntheticsMonitorClient;
- request: KibanaRequest;
- subject?: Subject;
- }) {
- this.projectId = projectId;
- this.spaceId = spaceId;
- this.locations = locations;
- this.privateLocations = privateLocations;
- this.keepStale = keepStale;
- this.savedObjectsClient = savedObjectsClient;
- this.encryptedSavedObjectsClient = encryptedSavedObjectsClient;
- this.syntheticsMonitorClient = syntheticsMonitorClient;
- this.monitors = monitors;
- this.server = server;
- this.projectFilter = `${syntheticsMonitorType}.attributes.${ConfigKey.PROJECT_ID}: "${this.projectId}"`;
- this.request = request;
- this.subject = subject;
- }
-
- public configureAllProjectMonitors = async () => {
- const existingMonitors = await this.getProjectMonitorsForProject();
- this.staleMonitorsMap = await this.getStaleMonitorsMap(existingMonitors);
-
- const normalizedNewMonitors: SyntheticsMonitor[] = [];
- const normalizedUpdateMonitors: Array<{
- previousMonitor: SavedObjectsFindResult;
- monitor: SyntheticsMonitor;
- }> = [];
-
- for (const monitor of this.monitors) {
- const previousMonitor = existingMonitors.find(
- (monitorObj) =>
- (monitorObj.attributes as SyntheticsMonitor)[ConfigKey.JOURNEY_ID] === monitor.id
- );
-
- const normM = await this.validateProjectMonitor({
- monitor,
- });
-
- if (normM) {
- if (previousMonitor) {
- this.updatedMonitors.push(monitor.id);
- if (this.staleMonitorsMap[monitor.id]) {
- this.staleMonitorsMap[monitor.id].stale = false;
- }
- normalizedUpdateMonitors.push({ monitor: normM as MonitorFields, previousMonitor });
- } else {
- normalizedNewMonitors.push(normM as MonitorFields);
- }
- }
- }
-
- await this.createMonitorsBulk(normalizedNewMonitors);
-
- const { updatedCount } = await this.updateMonitorsBulk(normalizedUpdateMonitors);
-
- if (normalizedUpdateMonitors.length > 0) {
- let updateMessage = '';
- if (updatedCount > 0) {
- updateMessage = `${updatedCount} monitor${
- updatedCount > 1 ? 's' : ''
- } updated successfully.`;
- }
-
- const noChanges = normalizedUpdateMonitors.length - updatedCount;
- let noChangeMessage = '';
- if (noChanges > 0) {
- noChangeMessage = `${noChanges} monitor${noChanges > 1 ? 's' : ''} found with no changes.`;
- }
-
- this.handleStreamingMessage({
- message: `${updateMessage} ${noChangeMessage}`,
- });
- }
-
- await this.handleStaleMonitors();
- };
-
- validatePermissions = async ({ monitor }: { monitor: ProjectMonitor }) => {
- if (this.writeIntegrationPoliciesPermissions || (monitor.privateLocations ?? []).length === 0) {
- return;
- }
- const {
- integrations: { writeIntegrationPolicies },
- } = await this.server.fleet.authz.fromRequest(this.request);
-
- this.writeIntegrationPoliciesPermissions = writeIntegrationPolicies;
-
- if (!writeIntegrationPolicies) {
- throw new Error(INSUFFICIENT_FLEET_PERMISSIONS);
- }
- };
-
- validateProjectMonitor = async ({ monitor }: { monitor: ProjectMonitor }) => {
- try {
- await this.validatePermissions({ monitor });
-
- const { normalizedFields: normalizedMonitor, errors } = normalizeProjectMonitor({
- monitor,
- locations: this.locations,
- privateLocations: this.privateLocations,
- projectId: this.projectId,
- namespace: this.spaceId,
- version: this.server.stackVersion,
- });
-
- if (errors.length) {
- this.failedMonitors.push(...errors);
- this.handleStreamingMessage({
- message: `${monitor.id}: failed to create or update monitor`,
- });
- return null;
- }
-
- /* Validates that the payload sent from the synthetics agent is valid */
- const { valid: isMonitorPayloadValid } = this.validateMonitor({
- validationResult: validateProjectMonitor(
- {
- ...monitor,
- type: normalizedMonitor[ConfigKey.MONITOR_TYPE],
- },
- this.locations,
- this.privateLocations
- ),
- monitorId: monitor.id,
- });
-
- if (!isMonitorPayloadValid) {
- return null;
- }
-
- /* Validates that the normalized monitor is a valid monitor saved object type */
- const { valid: isNormalizedMonitorValid, decodedMonitor } = this.validateMonitor({
- validationResult: validateMonitor(normalizedMonitor as MonitorFields),
- monitorId: monitor.id,
- });
-
- if (!isNormalizedMonitorValid || !decodedMonitor) {
- return null;
- }
-
- return decodedMonitor;
- } catch (e) {
- this.server.logger.error(e);
- this.failedMonitors.push({
- id: monitor.id,
- reason: 'Failed to create or update monitor',
- details: e.message,
- payload: monitor,
- });
- this.handleStreamingMessage({ message: `${monitor.id}: failed to create or update monitor` });
- if (this.staleMonitorsMap[monitor.id]) {
- this.staleMonitorsMap[monitor.id].stale = false;
- }
- }
- };
-
- private getStaleMonitorsMap = async (
- existingMonitors: Array>
- ): Promise => {
- const staleMonitors: StaleMonitorMap = {};
-
- existingMonitors.forEach((savedObject) => {
- const journeyId = (savedObject.attributes as SyntheticsMonitor)[ConfigKey.JOURNEY_ID];
- if (journeyId) {
- staleMonitors[journeyId] = {
- stale: true,
- savedObjectId: savedObject.id,
- journeyId,
- };
- }
- });
-
- return staleMonitors;
- };
-
- public getProjectMonitorsForProject = async () => {
- const finder = this.savedObjectsClient.createPointInTimeFinder({
- type: syntheticsMonitorType,
- perPage: 1000,
- filter: this.projectFilter,
- });
-
- const hits: Array> = [];
- for await (const result of finder.find()) {
- hits.push(
- ...(result.saved_objects as Array>)
- );
- }
-
- await finder.close();
-
- return hits;
- };
-
- private createMonitorsBulk = async (monitors: SyntheticsMonitor[]) => {
- try {
- if (monitors.length > 0) {
- const { newMonitors } = await syncNewMonitorBulk({
- normalizedMonitors: monitors,
- server: this.server,
- syntheticsMonitorClient: this.syntheticsMonitorClient,
- soClient: this.savedObjectsClient,
- request: this.request,
- privateLocations: this.privateLocations,
- spaceId: this.spaceId,
- });
-
- if (newMonitors && newMonitors.length === monitors.length) {
- this.createdMonitors.push(...monitors.map((monitor) => monitor[ConfigKey.JOURNEY_ID]!));
- this.handleStreamingMessage({
- message: `${monitors.length} monitor${
- monitors.length > 1 ? 's' : ''
- } created successfully.`,
- });
- } else {
- this.failedMonitors.push({
- reason: `Failed to create ${monitors.length} monitors`,
- details: 'Failed to create monitors',
- payload: monitors,
- });
- this.handleStreamingMessage({
- message: `Failed to create ${monitors.length} monitors`,
- });
- }
- }
- } catch (e) {
- this.server.logger.error(e);
- this.failedMonitors.push({
- reason: `Failed to create ${monitors.length} monitors`,
- details: e.message,
- payload: monitors,
- });
- this.handleStreamingMessage({
- message: `Failed to create ${monitors.length} monitors`,
- });
- }
- };
-
- private getDecryptedMonitors = async (
- monitors: Array>
- ) => {
- return await pMap(
- monitors,
- async (monitor) =>
- this.encryptedSavedObjectsClient.getDecryptedAsInternalUser(
- syntheticsMonitorType,
- monitor.id,
- {
- namespace: monitor.namespaces?.[0],
- }
- ),
- { concurrency: 500 }
- );
- };
-
- private updateMonitorsBulk = async (
- monitors: Array<{
- monitor: SyntheticsMonitor;
- previousMonitor: SavedObjectsFindResult;
- }>
- ): Promise<{
- editedMonitors: Array>;
- errors: ServiceLocationErrors;
- updatedCount: number;
- }> => {
- const decryptedPreviousMonitors = await this.getDecryptedMonitors(
- monitors.map((m) => m.previousMonitor)
- );
-
- const monitorsToUpdate = [];
-
- for (let i = 0; i < decryptedPreviousMonitors.length; i++) {
- const decryptedPreviousMonitor = decryptedPreviousMonitors[i];
- const previousMonitor = monitors[i].previousMonitor;
- const normalizedMonitor = monitors[i].monitor;
-
- const keysToOmit = [ConfigKey.REVISION, ConfigKey.MONITOR_QUERY_ID, ConfigKey.CONFIG_ID];
- const { attributes: normalizedPreviousMonitorAttributes } =
- normalizeSecrets(decryptedPreviousMonitor);
- const hasMonitorBeenEdited = !isEqual(
- omit(normalizedMonitor, keysToOmit),
- omit(normalizedPreviousMonitorAttributes, keysToOmit)
- );
-
- if (hasMonitorBeenEdited) {
- const monitorWithRevision = formatSecrets({
- ...normalizedPreviousMonitorAttributes,
- ...normalizedMonitor,
- revision: (previousMonitor.attributes[ConfigKey.REVISION] || 0) + 1,
- });
- monitorsToUpdate.push({
- normalizedMonitor,
- previousMonitor,
- monitorWithRevision,
- decryptedPreviousMonitor,
- });
- }
- }
-
- const { editedMonitors } = await syncEditedMonitorBulk({
- monitorsToUpdate,
- server: this.server,
- syntheticsMonitorClient: this.syntheticsMonitorClient,
- savedObjectsClient: this.savedObjectsClient,
- request: this.request,
- privateLocations: this.privateLocations,
- spaceId: this.spaceId,
- });
- return {
- editedMonitors: editedMonitors ?? [],
- errors: [],
- updatedCount: monitorsToUpdate.length,
- };
- };
-
- private handleStaleMonitors = async () => {
- try {
- const staleMonitorsList = Object.values(this.staleMonitorsMap).filter(
- (monitor) => monitor.stale === true
- );
-
- const encryptedMonitors = await this.savedObjectsClient.bulkGet(
- staleMonitorsList.map((staleMonitor) => ({
- id: staleMonitor.savedObjectId,
- type: syntheticsMonitorType,
- }))
- );
-
- let monitors = encryptedMonitors.saved_objects;
-
- const hasPrivateMonitor = monitors.some((monitor) =>
- monitor.attributes.locations.some((location) => !location.isServiceManaged)
- );
-
- if (hasPrivateMonitor) {
- const {
- integrations: { writeIntegrationPolicies },
- } = await this.server.fleet.authz.fromRequest(this.request);
- if (!writeIntegrationPolicies) {
- monitors = monitors.filter((monitor) => {
- const hasPrivateLocation = monitor.attributes.locations.some(
- (location) => !location.isServiceManaged
- );
- if (hasPrivateLocation) {
- const journeyId = (monitor.attributes as MonitorFields)[ConfigKey.JOURNEY_ID]!;
- const monitorName = (monitor.attributes as MonitorFields)[ConfigKey.NAME]!;
- this.handleStreamingMessage({
- message: `Monitor ${journeyId} could not be deleted`,
- });
- this.failedStaleMonitors.push({
- id: journeyId,
- reason: 'Failed to delete stale monitor',
- details: `Unable to delete Synthetics package policy for monitor ${monitorName}. Fleet write permissions are needed to use Synthetics private locations.`,
- });
- }
- return !hasPrivateLocation;
- });
- }
- }
-
- const chunkSize = 100;
- for (let i = 0; i < monitors.length; i += chunkSize) {
- const chunkMonitors = monitors.slice(i, i + chunkSize);
- try {
- if (!this.keepStale) {
- await deleteMonitorBulk({
- monitors: chunkMonitors,
- savedObjectsClient: this.savedObjectsClient,
- server: this.server,
- syntheticsMonitorClient: this.syntheticsMonitorClient,
- request: this.request,
- });
-
- for (const sm of chunkMonitors) {
- const journeyId = (sm.attributes as MonitorFields)[ConfigKey.JOURNEY_ID]!;
-
- this.deletedMonitors.push(journeyId);
- this.handleStreamingMessage({
- message: `Monitor ${journeyId} deleted successfully`,
- });
- }
- } else {
- chunkMonitors.forEach((sm) => {
- const journeyId = (sm.attributes as MonitorFields)[ConfigKey.JOURNEY_ID]!;
- this.staleMonitors.push(journeyId);
- });
- }
- } catch (e) {
- chunkMonitors.forEach((sm) => {
- const journeyId = (sm.attributes as MonitorFields)[ConfigKey.JOURNEY_ID]!;
-
- this.handleStreamingMessage({
- message: `Monitor ${journeyId} could not be deleted`,
- });
- this.failedStaleMonitors.push({
- id: journeyId,
- reason: 'Failed to delete stale monitor',
- details: e.message,
- payload: staleMonitorsList.find(
- (staleMonitor) => staleMonitor.savedObjectId === sm.id
- ),
- });
- });
- this.server.logger.error(e);
- }
- }
- } catch (e) {
- this.server.logger.error(e);
- }
- };
-
- private handleStreamingMessage = ({ message }: { message: string }) => {
- if (this.subject) {
- this.subject?.next(message);
- }
- };
-
- private validateMonitor = ({
- validationResult,
- monitorId,
- }: {
- validationResult: ValidationResult;
- monitorId: string;
- }) => {
- const { reason: message, details, payload: validationPayload, valid } = validationResult;
- if (!valid) {
- this.failedMonitors.push({
- id: monitorId,
- reason: message,
- details,
- payload: validationPayload,
- });
- if (this.staleMonitorsMap[monitorId]) {
- this.staleMonitorsMap[monitorId].stale = false;
- }
- }
- return validationResult;
- };
-}
diff --git a/x-pack/plugins/synthetics/server/synthetics_service/synthetics_service.ts b/x-pack/plugins/synthetics/server/synthetics_service/synthetics_service.ts
index e4ef7cbbb6e67c..3e420bf478decb 100644
--- a/x-pack/plugins/synthetics/server/synthetics_service/synthetics_service.ts
+++ b/x-pack/plugins/synthetics/server/synthetics_service/synthetics_service.ts
@@ -42,7 +42,7 @@ import {
ServiceLocations,
SyntheticsMonitorWithId,
SyntheticsMonitorWithSecrets,
- SyntheticsParam,
+ SyntheticsParamSO,
ThrottlingOptions,
} from '../../common/runtime_types';
import { getServiceLocations } from './get_service_locations';
@@ -555,10 +555,10 @@ export class SyntheticsService {
async getSyntheticsParams({ spaceId }: { spaceId?: string } = {}) {
const encryptedClient = this.server.encryptedSavedObjects.getClient();
- const paramsBySpace: Record> = {};
+ const paramsBySpace: Record> = Object.create(null);
const finder =
- await encryptedClient.createPointInTimeFinderDecryptedAsInternalUser({
+ await encryptedClient.createPointInTimeFinderDecryptedAsInternalUser({
type: syntheticsParamType,
perPage: 1000,
namespaces: spaceId ? [spaceId] : undefined,
@@ -568,7 +568,7 @@ export class SyntheticsService {
response.saved_objects.forEach((param) => {
param.namespaces?.forEach((namespace) => {
if (!paramsBySpace[namespace]) {
- paramsBySpace[namespace] = {};
+ paramsBySpace[namespace] = Object.create(null);
}
paramsBySpace[namespace][param.attributes.key] = param.attributes.value;
});
diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json
index 007b67ef87e4d9..b7b73bf88696f6 100644
--- a/x-pack/plugins/translations/translations/fr-FR.json
+++ b/x-pack/plugins/translations/translations/fr-FR.json
@@ -34235,7 +34235,6 @@
"xpack.synthetics.synthetics.waterfall.requestsTotalMessage.first": "{count} premier(s)",
"xpack.synthetics.tableTitle.showing": "Affichage de {count} sur {total} {label}",
"xpack.synthetics.tagsList.filter": "Cliquez pour filtrer la liste avec la balise {tag}",
- "xpack.synthetics.testRun.runErrorLocation": "Impossible d'exécuter le moniteur sur l'emplacement {locationName}.",
"xpack.synthetics.testRunDetailsRoute.title": "Détails de l'exécution du test | {baseTitle}",
"xpack.synthetics.waterfall.networkRequests.count": "Affichage de {countShown} sur {total} {networkRequestsLabel}",
"xpack.synthetics.waterfall.networkRequests.pluralizedCount": "{total, plural, one {requête réseau} other {requêtes réseau}}",
diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json
index 5632c75886cf76..2c0b9f632acc90 100644
--- a/x-pack/plugins/translations/translations/ja-JP.json
+++ b/x-pack/plugins/translations/translations/ja-JP.json
@@ -34214,7 +34214,6 @@
"xpack.synthetics.synthetics.waterfall.requestsTotalMessage.first": "最初の{count}件",
"xpack.synthetics.tableTitle.showing": "{total}件の{label}中{count}件を表示中",
"xpack.synthetics.tagsList.filter": "クリックして、タグ{tag}を使用して一覧をフィルターします",
- "xpack.synthetics.testRun.runErrorLocation": "場所{locationName}でモニターを実行できませんでした。",
"xpack.synthetics.testRunDetailsRoute.title": "テスト実行詳細 | {baseTitle}",
"xpack.synthetics.waterfall.networkRequests.count": "{total}件の{networkRequestsLabel}中{countShown}件を表示中",
"xpack.synthetics.waterfall.networkRequests.pluralizedCount": "{total, plural, other {ネットワークリクエスト}}",
diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json
index c58a2c54778da3..18e59de9cedbf0 100644
--- a/x-pack/plugins/translations/translations/zh-CN.json
+++ b/x-pack/plugins/translations/translations/zh-CN.json
@@ -34231,7 +34231,6 @@
"xpack.synthetics.synthetics.waterfall.requestsTotalMessage.first": "前 {count} 个",
"xpack.synthetics.tableTitle.showing": "正在显示第 {count} 个(共 {total} 个){label}",
"xpack.synthetics.tagsList.filter": "单击以筛选带 {tag} 标签的列表",
- "xpack.synthetics.testRun.runErrorLocation": "无法在位置 {locationName} 运行监测。",
"xpack.synthetics.testRunDetailsRoute.title": "测试运行详情 | {baseTitle}",
"xpack.synthetics.waterfall.networkRequests.count": "正在显示第 {countShown} 个(共 {total} 个){networkRequestsLabel}",
"xpack.synthetics.waterfall.networkRequests.pluralizedCount": "{total, plural, other {网络请求}}",
diff --git a/x-pack/test/api_integration/apis/synthetics/add_edit_params.ts b/x-pack/test/api_integration/apis/synthetics/add_edit_params.ts
new file mode 100644
index 00000000000000..678cee4463a9a5
--- /dev/null
+++ b/x-pack/test/api_integration/apis/synthetics/add_edit_params.ts
@@ -0,0 +1,278 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+import { v4 as uuidv4 } from 'uuid';
+import { SYNTHETICS_API_URLS } from '@kbn/synthetics-plugin/common/constants';
+import expect from '@kbn/expect';
+import { syntheticsParamType } from '@kbn/synthetics-plugin/common/types/saved_objects';
+import { FtrProviderContext } from '../../ftr_provider_context';
+
+export default function ({ getService }: FtrProviderContext) {
+ describe('AddEditParams', function () {
+ this.tags('skipCloud');
+ const supertestAPI = getService('supertest');
+ const kServer = getService('kibanaServer');
+ const testParam = {
+ key: 'test',
+ value: 'test',
+ };
+
+ before(async () => {
+ await supertestAPI.post('/api/fleet/setup').set('kbn-xsrf', 'true').send().expect(200);
+ await supertestAPI
+ .post('/api/fleet/epm/packages/synthetics/0.12.0')
+ .set('kbn-xsrf', 'true')
+ .send({ force: true })
+ .expect(200);
+
+ await kServer.savedObjects.clean({ types: [syntheticsParamType] });
+ });
+
+ it('adds a test param', async () => {
+ await supertestAPI
+ .post(SYNTHETICS_API_URLS.PARAMS)
+ .set('kbn-xsrf', 'true')
+ .send(testParam)
+ .expect(200);
+
+ const getResponse = await supertestAPI
+ .get(SYNTHETICS_API_URLS.PARAMS)
+ .set('kbn-xsrf', 'true')
+ .expect(200);
+
+ expect(getResponse.body.data[0].attributes).eql(testParam);
+ });
+
+ it('handles tags and description', async () => {
+ const tagsAndDescription = {
+ tags: ['a tag'],
+ description: 'test description',
+ };
+ const testParam2 = {
+ ...testParam,
+ ...tagsAndDescription,
+ };
+ await supertestAPI
+ .post(SYNTHETICS_API_URLS.PARAMS)
+ .set('kbn-xsrf', 'true')
+ .send(testParam2)
+ .expect(200);
+
+ const getResponse = await supertestAPI
+ .get(SYNTHETICS_API_URLS.PARAMS)
+ .set('kbn-xsrf', 'true')
+ .expect(200);
+
+ expect(getResponse.body.data[0].attributes).eql(testParam2);
+ });
+
+ it('handles editing a param', async () => {
+ const updatedParam = {
+ key: 'testUpdated',
+ value: 'testUpdated',
+ tags: ['a tag'],
+ description: 'test description',
+ };
+
+ await supertestAPI
+ .post(SYNTHETICS_API_URLS.PARAMS)
+ .set('kbn-xsrf', 'true')
+ .send(testParam)
+ .expect(200);
+
+ const getResponse = await supertestAPI
+ .get(SYNTHETICS_API_URLS.PARAMS)
+ .set('kbn-xsrf', 'true')
+ .expect(200);
+ const param = getResponse.body.data[0];
+ expect(param.attributes).eql(testParam);
+
+ await supertestAPI
+ .put(SYNTHETICS_API_URLS.PARAMS)
+ .set('kbn-xsrf', 'true')
+ .send({ ...updatedParam, id: param.id })
+ .expect(200);
+
+ const updatedGetResponse = await supertestAPI
+ .get(SYNTHETICS_API_URLS.PARAMS)
+ .set('kbn-xsrf', 'true')
+ .expect(200);
+ const updatedParamSO = updatedGetResponse.body.data[0];
+ expect(updatedParamSO.attributes).eql(updatedParam);
+ });
+
+ it('handles spaces', async () => {
+ const SPACE_ID = `test-space-${uuidv4()}`;
+ const SPACE_NAME = `test-space-name ${uuidv4()}`;
+
+ await kServer.spaces.create({ id: SPACE_ID, name: SPACE_NAME });
+
+ await supertestAPI
+ .post(`/s/${SPACE_ID}${SYNTHETICS_API_URLS.PARAMS}`)
+ .set('kbn-xsrf', 'true')
+ .send(testParam)
+ .expect(200);
+
+ const getResponse = await supertestAPI
+ .get(`/s/${SPACE_ID}${SYNTHETICS_API_URLS.PARAMS}`)
+ .set('kbn-xsrf', 'true')
+ .expect(200);
+
+ expect(getResponse.body.data[0].namespaces).eql([SPACE_ID]);
+ expect(getResponse.body.data[0].attributes).eql(testParam);
+ });
+
+ it('handles editing a param in spaces', async () => {
+ const SPACE_ID = `test-space-${uuidv4()}`;
+ const SPACE_NAME = `test-space-name ${uuidv4()}`;
+
+ await kServer.spaces.create({ id: SPACE_ID, name: SPACE_NAME });
+
+ const updatedParam = {
+ key: 'testUpdated',
+ value: 'testUpdated',
+ tags: ['a tag'],
+ description: 'test description',
+ };
+
+ await supertestAPI
+ .post(`/s/${SPACE_ID}${SYNTHETICS_API_URLS.PARAMS}`)
+ .set('kbn-xsrf', 'true')
+ .send(testParam)
+ .expect(200);
+
+ const getResponse = await supertestAPI
+ .get(`/s/${SPACE_ID}${SYNTHETICS_API_URLS.PARAMS}`)
+ .set('kbn-xsrf', 'true')
+ .expect(200);
+ const param = getResponse.body.data[0];
+ expect(param.attributes).eql(testParam);
+
+ await supertestAPI
+ .put(`/s/${SPACE_ID}${SYNTHETICS_API_URLS.PARAMS}`)
+ .set('kbn-xsrf', 'true')
+ .send({ ...updatedParam, id: param.id })
+ .expect(200);
+
+ const updatedGetResponse = await supertestAPI
+ .get(`/s/${SPACE_ID}${SYNTHETICS_API_URLS.PARAMS}`)
+ .set('kbn-xsrf', 'true')
+ .expect(200);
+ const updatedParamSO = updatedGetResponse.body.data[0];
+ expect(updatedParamSO.attributes).eql(updatedParam);
+ });
+
+ it('does not allow editing a param in created in one space in a different space', async () => {
+ const SPACE_ID = `test-space-${uuidv4()}`;
+ const SPACE_NAME = `test-space-name ${uuidv4()}`;
+ const SPACE_ID_TWO = `test-space-${uuidv4()}-two`;
+ const SPACE_NAME_TWO = `test-space-name ${uuidv4()} 2`;
+
+ await kServer.spaces.create({ id: SPACE_ID, name: SPACE_NAME });
+ await kServer.spaces.create({ id: SPACE_ID_TWO, name: SPACE_NAME_TWO });
+
+ const updatedParam = {
+ key: 'testUpdated',
+ value: 'testUpdated',
+ tags: ['a tag'],
+ description: 'test description',
+ };
+
+ await supertestAPI
+ .post(`/s/${SPACE_ID}${SYNTHETICS_API_URLS.PARAMS}`)
+ .set('kbn-xsrf', 'true')
+ .send(testParam)
+ .expect(200);
+
+ const getResponse = await supertestAPI
+ .get(`/s/${SPACE_ID}${SYNTHETICS_API_URLS.PARAMS}`)
+ .set('kbn-xsrf', 'true')
+ .expect(200);
+ const param = getResponse.body.data[0];
+ expect(param.attributes).eql(testParam);
+
+ // space does exist so get request should be 200
+ await supertestAPI
+ .get(`/s/${SPACE_ID_TWO}${SYNTHETICS_API_URLS.PARAMS}`)
+ .set('kbn-xsrf', 'true')
+ .expect(200);
+
+ await supertestAPI
+ .put(`/s/${SPACE_ID_TWO}${SYNTHETICS_API_URLS.PARAMS}`)
+ .set('kbn-xsrf', 'true')
+ .send({ ...updatedParam, id: param.id })
+ .expect(404);
+
+ const updatedGetResponse = await supertestAPI
+ .get(`/s/${SPACE_ID}${SYNTHETICS_API_URLS.PARAMS}`)
+ .set('kbn-xsrf', 'true')
+ .expect(200);
+ const updatedParamSO = updatedGetResponse.body.data[0];
+ expect(updatedParamSO.attributes).eql(testParam);
+ });
+
+ it('handles invalid spaces', async () => {
+ const SPACE_ID = `test-space-${uuidv4()}`;
+ const SPACE_NAME = `test-space-name ${uuidv4()}`;
+
+ await kServer.spaces.create({ id: SPACE_ID, name: SPACE_NAME });
+
+ await supertestAPI
+ .post(`/s/doesnotexist${SYNTHETICS_API_URLS.PARAMS}`)
+ .set('kbn-xsrf', 'true')
+ .send(testParam)
+ .expect(404);
+ });
+
+ it('handles editing with invalid spaces', async () => {
+ const updatedParam = {
+ key: 'testUpdated',
+ value: 'testUpdated',
+ tags: ['a tag'],
+ description: 'test description',
+ };
+
+ await supertestAPI
+ .post(SYNTHETICS_API_URLS.PARAMS)
+ .set('kbn-xsrf', 'true')
+ .send(testParam)
+ .expect(200);
+ const getResponse = await supertestAPI
+ .get(SYNTHETICS_API_URLS.PARAMS)
+ .set('kbn-xsrf', 'true')
+ .expect(200);
+ const param = getResponse.body.data[0];
+ expect(param.attributes).eql(testParam);
+
+ await supertestAPI
+ .put(`/s/doesnotexist${SYNTHETICS_API_URLS.PARAMS}`)
+ .set('kbn-xsrf', 'true')
+ .send({ ...updatedParam, id: param.id })
+ .expect(404);
+ });
+
+ it('handles share across spaces', async () => {
+ const SPACE_ID = `test-space-${uuidv4()}`;
+ const SPACE_NAME = `test-space-name ${uuidv4()}`;
+
+ await kServer.spaces.create({ id: SPACE_ID, name: SPACE_NAME });
+
+ await supertestAPI
+ .post(`/s/${SPACE_ID}${SYNTHETICS_API_URLS.PARAMS}`)
+ .set('kbn-xsrf', 'true')
+ .send({ ...testParam, share_across_spaces: true })
+ .expect(200);
+
+ const getResponse = await supertestAPI
+ .get(`/s/${SPACE_ID}${SYNTHETICS_API_URLS.PARAMS}`)
+ .set('kbn-xsrf', 'true')
+ .expect(200);
+
+ expect(getResponse.body.data[0].namespaces).eql(['*']);
+ expect(getResponse.body.data[0].attributes).eql(testParam);
+ });
+ });
+}
diff --git a/x-pack/test/api_integration/apis/synthetics/add_monitor_project.ts b/x-pack/test/api_integration/apis/synthetics/add_monitor_project.ts
index 7674d2fbc534fe..632e3473024183 100644
--- a/x-pack/test/api_integration/apis/synthetics/add_monitor_project.ts
+++ b/x-pack/test/api_integration/apis/synthetics/add_monitor_project.ts
@@ -1758,9 +1758,9 @@ export default function ({ getService }: FtrProviderContext) {
expect(message).to.eql(REQUEST_TOO_LARGE);
} finally {
await supertest
- .put(API_URLS.SYNTHETICS_MONITORS_PROJECT_LEGACY)
+ .put(API_URLS.SYNTHETICS_MONITORS_PROJECT_UPDATE.replace('{projectName}', project))
.set('kbn-xsrf', 'true')
- .send({ ...projectMonitors, keep_stale: false, project });
+ .send({ ...projectMonitors, project });
}
});
diff --git a/x-pack/test/api_integration/apis/synthetics/add_monitor_project_legacy.ts b/x-pack/test/api_integration/apis/synthetics/add_monitor_project_legacy.ts
deleted file mode 100644
index 0e2cec6c3a5c35..00000000000000
--- a/x-pack/test/api_integration/apis/synthetics/add_monitor_project_legacy.ts
+++ /dev/null
@@ -1,2178 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-import fetch, { BodyInit, HeadersInit, Response } from 'node-fetch';
-import { v4 as uuidv4 } from 'uuid';
-import expect from '@kbn/expect';
-import { format as formatUrl } from 'url';
-import {
- ConfigKey,
- LegacyProjectMonitorsRequest,
-} from '@kbn/synthetics-plugin/common/runtime_types';
-import { API_URLS } from '@kbn/synthetics-plugin/common/constants';
-import { formatKibanaNamespace } from '@kbn/synthetics-plugin/common/formatters';
-import { syntheticsMonitorType } from '@kbn/synthetics-plugin/server/legacy_uptime/lib/saved_objects/synthetics_monitor';
-import { PackagePolicy } from '@kbn/fleet-plugin/common';
-import {
- PROFILE_VALUES_ENUM,
- PROFILES_MAP,
-} from '@kbn/synthetics-plugin/common/constants/monitor_defaults';
-import { FtrProviderContext } from '../../ftr_provider_context';
-import { getFixtureJson } from '../uptime/rest/helper/get_fixture_json';
-import { PrivateLocationTestService } from './services/private_location_test_service';
-import { comparePolicies } from './sample_data/test_policy';
-import { getTestProjectSyntheticsPolicy } from './sample_data/test_project_monitor_policy';
-
-export default function ({ getService }: FtrProviderContext) {
- describe('AddProjectLegacyMonitors', function () {
- this.tags('skipCloud');
-
- const supertest = getService('supertest');
- const config = getService('config');
- const kibanaServerUrl = formatUrl(config.get('servers.kibana'));
- const supertestWithoutAuth = getService('supertestWithoutAuth');
- const security = getService('security');
- const kibanaServer = getService('kibanaServer');
- const projectMonitorEndpoint = kibanaServerUrl + API_URLS.SYNTHETICS_MONITORS_PROJECT_LEGACY;
-
- let projectMonitors: LegacyProjectMonitorsRequest;
- let httpProjectMonitors: LegacyProjectMonitorsRequest;
- let tcpProjectMonitors: LegacyProjectMonitorsRequest;
- let icmpProjectMonitors: LegacyProjectMonitorsRequest;
-
- let testPolicyId = '';
- const testPrivateLocations = new PrivateLocationTestService(getService);
-
- const setUniqueIds = (request: LegacyProjectMonitorsRequest) => {
- return {
- ...request,
- monitors: request.monitors.map((monitor) => ({ ...monitor, id: uuidv4() })),
- };
- };
-
- const deleteMonitor = async (
- journeyId: string,
- projectId: string,
- space: string = 'default',
- username: string = '',
- password: string = ''
- ) => {
- try {
- const response = await supertest
- .get(`/s/${space}${API_URLS.SYNTHETICS_MONITORS}`)
- .auth(username, password)
- .query({
- filter: `${syntheticsMonitorType}.attributes.journey_id: "${journeyId}" AND ${syntheticsMonitorType}.attributes.project_id: "${projectId}"`,
- })
- .set('kbn-xsrf', 'true')
- .expect(200);
- const { monitors } = response.body;
- if (monitors[0]?.id) {
- await supertest
- .delete(`/s/${space}${API_URLS.SYNTHETICS_MONITORS}/${monitors[0].id}`)
- .set('kbn-xsrf', 'true')
- .send(projectMonitors)
- .expect(200);
- }
- } catch (e) {
- // eslint-disable-next-line no-console
- console.error(e);
- }
- };
-
- before(async () => {
- await supertest.post('/api/fleet/setup').set('kbn-xsrf', 'true').send().expect(200);
- await supertest
- .post('/api/fleet/epm/packages/synthetics/0.12.0')
- .set('kbn-xsrf', 'true')
- .send({ force: true })
- .expect(200);
-
- const testPolicyName = 'Fleet test server policy' + Date.now();
- const apiResponse = await testPrivateLocations.addFleetPolicy(testPolicyName);
- testPolicyId = apiResponse.body.item.id;
- await testPrivateLocations.setTestLocations([testPolicyId]);
- });
-
- beforeEach(() => {
- projectMonitors = setUniqueIds(getFixtureJson('project_browser_monitor'));
- httpProjectMonitors = setUniqueIds(getFixtureJson('project_http_monitor'));
- tcpProjectMonitors = setUniqueIds(getFixtureJson('project_tcp_monitor'));
- icmpProjectMonitors = setUniqueIds(getFixtureJson('project_icmp_monitor'));
- });
-
- it('project monitors - handles browser monitors', async () => {
- const successfulMonitors = [projectMonitors.monitors[0]];
-
- try {
- const messages = await parseStreamApiResponse(
- projectMonitorEndpoint,
- JSON.stringify(projectMonitors)
- );
-
- expect(messages).to.have.length(2);
- expect(messages[1].updatedMonitors).eql([]);
- expect(messages[1].createdMonitors).eql(successfulMonitors.map((monitor) => monitor.id));
- expect(messages[1].failedMonitors).eql([]);
-
- for (const monitor of successfulMonitors) {
- const journeyId = monitor.id;
- const createdMonitorsResponse = await supertest
- .get(API_URLS.SYNTHETICS_MONITORS)
- .query({ filter: `${syntheticsMonitorType}.attributes.journey_id: ${journeyId}` })
- .set('kbn-xsrf', 'true')
- .expect(200);
-
- const decryptedCreatedMonitor = await supertest
- .get(`${API_URLS.SYNTHETICS_MONITORS}/${createdMonitorsResponse.body.monitors[0].id}`)
- .set('kbn-xsrf', 'true')
- .expect(200);
-
- expect(decryptedCreatedMonitor.body.attributes).to.eql({
- __ui: {
- script_source: {
- file_name: '',
- is_generated_script: false,
- },
- },
- config_id: decryptedCreatedMonitor.body.id,
- custom_heartbeat_id: `${journeyId}-test-suite-default`,
- enabled: true,
- alert: {
- status: {
- enabled: true,
- },
- },
- 'filter_journeys.match': 'check if title is present',
- 'filter_journeys.tags': [],
- form_monitor_type: 'multistep',
- ignore_https_errors: false,
- journey_id: journeyId,
- locations: [
- {
- geo: {
- lat: 0,
- lon: 0,
- },
- id: 'localhost',
- isServiceManaged: true,
- label: 'Local Synthetics Service',
- },
- ],
- name: 'check if title is present',
- namespace: 'default',
- origin: 'project',
- original_space: 'default',
- playwright_options: '{"headless":true,"chromiumSandbox":false}',
- playwright_text_assertion: '',
- project_id: 'test-suite',
- params: '',
- revision: 1,
- schedule: {
- number: '10',
- unit: 'm',
- },
- screenshots: 'on',
- 'service.name': '',
- synthetics_args: [],
- tags: [],
- throttling: PROFILES_MAP[PROFILE_VALUES_ENUM.DEFAULT],
- 'ssl.certificate': '',
- 'ssl.certificate_authorities': '',
- 'ssl.supported_protocols': ['TLSv1.1', 'TLSv1.2', 'TLSv1.3'],
- 'ssl.verification_mode': 'full',
- 'ssl.key': '',
- 'ssl.key_passphrase': '',
- 'source.inline.script': '',
- 'source.project.content':
- 'UEsDBBQACAAIAON5qVQAAAAAAAAAAAAAAAAfAAAAZXhhbXBsZXMvdG9kb3MvYmFzaWMuam91cm5leS50c22Q0WrDMAxF3/sVF7MHB0LMXlc6RvcN+wDPVWNviW0sdUsp/fe5SSiD7UFCWFfHujIGlpnkybwxFTZfoY/E3hsaLEtwhs9RPNWKDU12zAOxkXRIbN4tB9d9pFOJdO6EN2HMqQguWN9asFBuQVMmJ7jiWNII9fIXrbabdUYr58l9IhwhQQZCYORCTFFUC31Btj21NRc7Mq4Nds+4bDD/pNVgT9F52Jyr2Fa+g75LAPttg8yErk+S9ELpTmVotlVwnfNCuh2lepl3+JflUmSBJ3uggt1v9INW/lHNLKze9dJe1J3QJK8pSvWkm6aTtCet5puq+x63+AFQSwcIAPQ3VfcAAACcAQAAUEsBAi0DFAAIAAgA43mpVAD0N1X3AAAAnAEAAB8AAAAAAAAAAAAgAKSBAAAAAGV4YW1wbGVzL3RvZG9zL2Jhc2ljLmpvdXJuZXkudHNQSwUGAAAAAAEAAQBNAAAARAEAAAAA',
- timeout: null,
- type: 'browser',
- 'url.port': null,
- urls: '',
- id: `${journeyId}-test-suite-default`,
- hash: 'ekrjelkjrelkjre',
- });
- }
- } finally {
- await Promise.all([
- successfulMonitors.map((monitor) => {
- return deleteMonitor(monitor.id, httpProjectMonitors.project);
- }),
- ]);
- }
- });
-
- it('project monitors - handles http monitors', async () => {
- const kibanaVersion = await kibanaServer.version.get();
- const successfulMonitors = [httpProjectMonitors.monitors[1]];
-
- try {
- const messages = await parseStreamApiResponse(
- projectMonitorEndpoint,
- JSON.stringify(httpProjectMonitors)
- );
-
- expect(messages).to.have.length(3);
- expect(messages[2].updatedMonitors).eql([]);
- expect(messages[2].createdMonitors).eql(successfulMonitors.map((monitor) => monitor.id));
- expect(messages[2].failedMonitors).eql([
- {
- id: httpProjectMonitors.monitors[0].id,
- details: `\`http\` project monitors must have exactly one value for field \`urls\` in version \`${kibanaVersion}\`. Your monitor was not created or updated.`,
- reason: 'Invalid Heartbeat configuration',
- },
- {
- id: httpProjectMonitors.monitors[0].id,
- details: `The following Heartbeat options are not supported for ${httpProjectMonitors.monitors[0].type} project monitors in ${kibanaVersion}: check.response.body|unsupportedKey.nestedUnsupportedKey. You monitor was not created or updated.`,
- reason: 'Unsupported Heartbeat option',
- },
- ]);
-
- for (const monitor of successfulMonitors) {
- const journeyId = monitor.id;
- const isTLSEnabled = Object.keys(monitor).some((key) => key.includes('ssl'));
- const createdMonitorsResponse = await supertest
- .get(API_URLS.SYNTHETICS_MONITORS)
- .query({ filter: `${syntheticsMonitorType}.attributes.journey_id: ${journeyId}` })
- .set('kbn-xsrf', 'true')
- .expect(200);
-
- const decryptedCreatedMonitor = await supertest
- .get(`${API_URLS.SYNTHETICS_MONITORS}/${createdMonitorsResponse.body.monitors[0].id}`)
- .set('kbn-xsrf', 'true')
- .expect(200);
-
- expect(decryptedCreatedMonitor.body.attributes).to.eql({
- __ui: {
- is_tls_enabled: isTLSEnabled,
- },
- 'check.request.method': 'POST',
- 'check.response.status': ['200'],
- config_id: decryptedCreatedMonitor.body.id,
- custom_heartbeat_id: `${journeyId}-test-suite-default`,
- 'check.response.body.negative': [],
- 'check.response.body.positive': ['Saved', 'saved'],
- 'check.response.json': [
- { description: 'check status', expression: 'foo.bar == "myValue"' },
- ],
- 'check.response.headers': {},
- 'check.request.body': {
- type: 'text',
- value: '',
- },
- 'check.request.headers': {
- 'Content-Type': 'application/x-www-form-urlencoded',
- },
- enabled: false,
- alert: {
- status: {
- enabled: true,
- },
- },
- form_monitor_type: 'http',
- journey_id: journeyId,
- locations: [
- {
- geo: {
- lat: 0,
- lon: 0,
- },
- id: 'localhost',
- isServiceManaged: true,
- label: 'Local Synthetics Service',
- },
- ],
- max_redirects: '0',
- name: monitor.name,
- namespace: 'default',
- origin: 'project',
- original_space: 'default',
- project_id: 'test-suite',
- username: '',
- password: '',
- proxy_url: '',
- proxy_headers: {},
- 'response.include_body': 'always',
- 'response.include_headers': false,
- 'response.include_body_max_bytes': '900',
- revision: 1,
- schedule: {
- number: '60',
- unit: 'm',
- },
- 'service.name': '',
- 'ssl.certificate': '',
- 'ssl.certificate_authorities': '',
- 'ssl.supported_protocols': ['TLSv1.1', 'TLSv1.2', 'TLSv1.3'],
- 'ssl.verification_mode': isTLSEnabled ? 'strict' : 'full',
- 'ssl.key': '',
- 'ssl.key_passphrase': '',
- tags: Array.isArray(monitor.tags) ? monitor.tags : monitor.tags?.split(','),
- timeout: '80',
- type: 'http',
- urls: Array.isArray(monitor.urls) ? monitor.urls?.[0] : monitor.urls,
- 'url.port': null,
- id: `${journeyId}-test-suite-default`,
- hash: 'ekrjelkjrelkjre',
- ipv6: true,
- ipv4: true,
- mode: 'any',
- });
- }
- } finally {
- await Promise.all([
- successfulMonitors.map((monitor) => {
- return deleteMonitor(monitor.id, httpProjectMonitors.project);
- }),
- ]);
- }
- });
-
- it('project monitors - handles tcp monitors', async () => {
- const successfulMonitors = [tcpProjectMonitors.monitors[0], tcpProjectMonitors.monitors[1]];
- const kibanaVersion = await kibanaServer.version.get();
-
- try {
- const messages = await parseStreamApiResponse(
- projectMonitorEndpoint,
- JSON.stringify(tcpProjectMonitors)
- );
-
- expect(messages).to.have.length(3);
- expect(messages[2].updatedMonitors).eql([]);
- expect(messages[2].createdMonitors).eql(successfulMonitors.map((monitor) => monitor.id));
- expect(messages[2].failedMonitors).eql([
- {
- id: tcpProjectMonitors.monitors[2].id,
- details: `\`tcp\` project monitors must have exactly one value for field \`hosts\` in version \`${kibanaVersion}\`. Your monitor was not created or updated.`,
- reason: 'Invalid Heartbeat configuration',
- },
- {
- id: tcpProjectMonitors.monitors[2].id,
- details: `The following Heartbeat options are not supported for ${tcpProjectMonitors.monitors[0].type} project monitors in ${kibanaVersion}: ports|unsupportedKey.nestedUnsupportedKey. You monitor was not created or updated.`,
- reason: 'Unsupported Heartbeat option',
- },
- ]);
-
- for (const monitor of successfulMonitors) {
- const journeyId = monitor.id;
- const isTLSEnabled = Object.keys(monitor).some((key) => key.includes('ssl'));
- const createdMonitorsResponse = await supertest
- .get(API_URLS.SYNTHETICS_MONITORS)
- .query({ filter: `${syntheticsMonitorType}.attributes.journey_id: ${journeyId}` })
- .set('kbn-xsrf', 'true')
- .expect(200);
-
- const decryptedCreatedMonitor = await supertest
- .get(`${API_URLS.SYNTHETICS_MONITORS}/${createdMonitorsResponse.body.monitors[0].id}`)
- .set('kbn-xsrf', 'true')
- .expect(200);
-
- expect(decryptedCreatedMonitor.body.attributes).to.eql({
- __ui: {
- is_tls_enabled: isTLSEnabled,
- },
- config_id: decryptedCreatedMonitor.body.id,
- custom_heartbeat_id: `${journeyId}-test-suite-default`,
- 'check.receive': '',
- 'check.send': '',
- enabled: true,
- alert: {
- status: {
- enabled: true,
- },
- },
- form_monitor_type: 'tcp',
- journey_id: journeyId,
- locations: [
- {
- geo: {
- lat: 0,
- lon: 0,
- },
- id: 'localhost',
- isServiceManaged: true,
- label: 'Local Synthetics Service',
- },
- ],
- name: monitor.name,
- namespace: 'default',
- origin: 'project',
- original_space: 'default',
- project_id: 'test-suite',
- revision: 1,
- schedule: {
- number: '1',
- unit: 'm',
- },
- proxy_url: '',
- proxy_use_local_resolver: false,
- 'service.name': '',
- 'ssl.certificate': '',
- 'ssl.certificate_authorities': '',
- 'ssl.supported_protocols': ['TLSv1.1', 'TLSv1.2', 'TLSv1.3'],
- 'ssl.verification_mode': isTLSEnabled ? 'strict' : 'full',
- 'ssl.key': '',
- 'ssl.key_passphrase': '',
- tags: Array.isArray(monitor.tags) ? monitor.tags : monitor.tags?.split(','),
- timeout: '16',
- type: 'tcp',
- hosts: Array.isArray(monitor.hosts) ? monitor.hosts?.[0] : monitor.hosts,
- 'url.port': null,
- urls: '',
- id: `${journeyId}-test-suite-default`,
- hash: 'ekrjelkjrelkjre',
- ipv6: true,
- ipv4: true,
- mode: 'any',
- });
- }
- } finally {
- await Promise.all([
- successfulMonitors.map((monitor) => {
- return deleteMonitor(monitor.id, tcpProjectMonitors.project);
- }),
- ]);
- }
- });
-
- it('project monitors - handles icmp monitors', async () => {
- const successfulMonitors = [icmpProjectMonitors.monitors[0], icmpProjectMonitors.monitors[1]];
- const kibanaVersion = await kibanaServer.version.get();
-
- try {
- const messages = await parseStreamApiResponse(
- projectMonitorEndpoint,
- JSON.stringify(icmpProjectMonitors)
- );
-
- expect(messages).to.have.length(3);
- expect(messages[2].updatedMonitors).eql([]);
- expect(messages[2].createdMonitors).eql(successfulMonitors.map((monitor) => monitor.id));
- expect(messages[2].failedMonitors).eql([
- {
- id: icmpProjectMonitors.monitors[2].id,
- details: `\`icmp\` project monitors must have exactly one value for field \`hosts\` in version \`${kibanaVersion}\`. Your monitor was not created or updated.`,
- reason: 'Invalid Heartbeat configuration',
- },
- {
- id: icmpProjectMonitors.monitors[2].id,
- details: `The following Heartbeat options are not supported for ${icmpProjectMonitors.monitors[0].type} project monitors in ${kibanaVersion}: unsupportedKey.nestedUnsupportedKey. You monitor was not created or updated.`,
- reason: 'Unsupported Heartbeat option',
- },
- ]);
-
- for (const monitor of successfulMonitors) {
- const journeyId = monitor.id;
- const createdMonitorsResponse = await supertest
- .get(API_URLS.SYNTHETICS_MONITORS)
- .query({ filter: `${syntheticsMonitorType}.attributes.journey_id: ${journeyId}` })
- .set('kbn-xsrf', 'true')
- .expect(200);
-
- const decryptedCreatedMonitor = await supertest
- .get(`${API_URLS.SYNTHETICS_MONITORS}/${createdMonitorsResponse.body.monitors[0].id}`)
- .set('kbn-xsrf', 'true')
- .expect(200);
-
- expect(decryptedCreatedMonitor.body.attributes).to.eql({
- config_id: decryptedCreatedMonitor.body.id,
- custom_heartbeat_id: `${journeyId}-test-suite-default`,
- enabled: true,
- alert: {
- status: {
- enabled: true,
- },
- },
- form_monitor_type: 'icmp',
- journey_id: journeyId,
- locations: [
- {
- geo: {
- lat: 0,
- lon: 0,
- },
- id: 'localhost',
- isServiceManaged: true,
- label: 'Local Synthetics Service',
- },
- {
- geo: {
- lat: '',
- lon: '',
- },
- id: testPolicyId,
- isServiceManaged: false,
- label: 'Test private location 0',
- },
- ],
- name: monitor.name,
- namespace: 'default',
- origin: 'project',
- original_space: 'default',
- project_id: 'test-suite',
- revision: 1,
- schedule: {
- number: '1',
- unit: 'm',
- },
- 'service.name': '',
- tags: Array.isArray(monitor.tags) ? monitor.tags : monitor.tags?.split(','),
- timeout: '16',
- type: 'icmp',
- hosts: Array.isArray(monitor.hosts) ? monitor.hosts?.[0] : monitor.hosts,
- wait:
- monitor.wait?.slice(-1) === 's'
- ? monitor.wait?.slice(0, -1)
- : `${parseInt(monitor.wait?.slice(0, -1) || '1', 10) * 60}`,
- id: `${journeyId}-test-suite-default`,
- hash: 'ekrjelkjrelkjre',
- ipv6: true,
- ipv4: true,
- mode: 'any',
- });
- }
- } finally {
- await Promise.all([
- successfulMonitors.map((monitor) => {
- return deleteMonitor(monitor.id, icmpProjectMonitors.project);
- }),
- ]);
- }
- });
-
- it('project monitors - returns a list of successfully created monitors', async () => {
- try {
- const messages = await parseStreamApiResponse(
- projectMonitorEndpoint,
- JSON.stringify(projectMonitors)
- );
-
- expect(messages).to.have.length(2);
- expect(messages[1].updatedMonitors).eql([]);
- expect(messages[1].failedMonitors).eql([]);
- expect(messages[1].createdMonitors).eql(
- projectMonitors.monitors.map((monitor) => monitor.id)
- );
- } finally {
- await Promise.all([
- projectMonitors.monitors.map((monitor) => {
- return deleteMonitor(monitor.id, projectMonitors.project);
- }),
- ]);
- }
- });
-
- it('project monitors - returns error if the space does not exist', async () => {
- const messages = await parseStreamApiResponse(
- kibanaServerUrl + '/s/i_dont_exist' + API_URLS.SYNTHETICS_MONITORS_PROJECT_LEGACY,
- JSON.stringify(projectMonitors)
- );
-
- expect(messages).to.have.length(2);
- expect(messages[0]).to.equal(
- "Unable to create monitors. Kibana space 'i_dont_exist' does not exist."
- );
- expect(messages[1].failedMonitors).to.eql(projectMonitors.monitors.map((m) => m.id));
- });
-
- it('project monitors - returns a list of successfully updated monitors', async () => {
- try {
- await supertest
- .put(API_URLS.SYNTHETICS_MONITORS_PROJECT_LEGACY)
- .set('kbn-xsrf', 'true')
- .send(projectMonitors);
-
- const messages = await parseStreamApiResponse(
- projectMonitorEndpoint,
- JSON.stringify(projectMonitors)
- );
-
- expect(messages).to.have.length(2);
- expect(messages[0]).eql(' 1 monitor found with no changes.');
- expect(messages[1].createdMonitors).eql([]);
- expect(messages[1].failedMonitors).eql([]);
- expect(messages[1].updatedMonitors).eql(
- projectMonitors.monitors.map((monitor) => monitor.id)
- );
- } finally {
- await Promise.all([
- projectMonitors.monitors.map((monitor) => {
- return deleteMonitor(monitor.id, projectMonitors.project);
- }),
- ]);
- }
- });
-
- it('project monitors - does not increment monitor revision unless a change has been made', async () => {
- try {
- await supertest
- .put(API_URLS.SYNTHETICS_MONITORS_PROJECT_LEGACY)
- .set('kbn-xsrf', 'true')
- .send(projectMonitors);
-
- await supertest
- .put(API_URLS.SYNTHETICS_MONITORS_PROJECT_LEGACY)
- .set('kbn-xsrf', 'true')
- .send(projectMonitors);
-
- const updatedMonitorsResponse = await Promise.all(
- projectMonitors.monitors.map((monitor) => {
- return supertest
- .get(API_URLS.SYNTHETICS_MONITORS)
- .query({ filter: `${syntheticsMonitorType}.attributes.journey_id: ${monitor.id}` })
- .set('kbn-xsrf', 'true')
- .expect(200);
- })
- );
-
- updatedMonitorsResponse.forEach((response) => {
- expect(response.body.monitors[0].attributes.revision).eql(1);
- });
- } finally {
- await Promise.all([
- projectMonitors.monitors.map((monitor) => {
- return deleteMonitor(monitor.id, projectMonitors.project);
- }),
- ]);
- }
- });
-
- it('project monitors - increments monitor revision when a change has been made', async () => {
- try {
- await supertest
- .put(API_URLS.SYNTHETICS_MONITORS_PROJECT_LEGACY)
- .set('kbn-xsrf', 'true')
- .send(projectMonitors);
-
- const editedMonitors = {
- ...projectMonitors,
- monitors: projectMonitors.monitors.map((monitor) => ({
- ...monitor,
- content: 'changed content',
- })),
- };
-
- const messages = await parseStreamApiResponse(
- projectMonitorEndpoint,
- JSON.stringify(editedMonitors)
- );
-
- const updatedMonitorsResponse = await Promise.all(
- projectMonitors.monitors.map((monitor) => {
- return supertest
- .get(API_URLS.SYNTHETICS_MONITORS)
- .query({ filter: `${syntheticsMonitorType}.attributes.journey_id: ${monitor.id}` })
- .set('kbn-xsrf', 'true')
- .expect(200);
- })
- );
-
- updatedMonitorsResponse.forEach((response) => {
- expect(response.body.monitors[0].attributes.revision).eql(2);
- });
- expect(messages[0]).eql('1 monitor updated successfully. ');
- } finally {
- await Promise.all([
- projectMonitors.monitors.map((monitor) => {
- return deleteMonitor(monitor.id, projectMonitors.project);
- }),
- ]);
- }
- });
-
- it('project monitors - does not delete monitors when keep stale is true', async () => {
- const secondMonitor = { ...projectMonitors.monitors[0], id: 'test-id-2' };
- const testMonitors = [projectMonitors.monitors[0], secondMonitor];
-
- try {
- await supertest
- .put(API_URLS.SYNTHETICS_MONITORS_PROJECT_LEGACY)
- .set('kbn-xsrf', 'true')
- .send({
- ...projectMonitors,
- monitors: testMonitors,
- });
-
- const messages = await parseStreamApiResponse(
- projectMonitorEndpoint,
- JSON.stringify(projectMonitors)
- );
-
- expect(messages).to.have.length(2);
- expect(messages[0]).eql(' 1 monitor found with no changes.');
- expect(messages[1].createdMonitors).eql([]);
- expect(messages[1].failedMonitors).eql([]);
- expect(messages[1].deletedMonitors).eql([]);
- expect(messages[1].updatedMonitors).eql([projectMonitors.monitors[0].id]);
- expect(messages[1].staleMonitors).eql([secondMonitor.id]);
- // does not delete the stale monitor
- const getResponse = await supertest
- .get(API_URLS.SYNTHETICS_MONITORS)
- .query({
- filter: `${syntheticsMonitorType}.attributes.journey_id: ${secondMonitor.id}`,
- })
- .set('kbn-xsrf', 'true')
- .expect(200);
-
- const { monitors } = getResponse.body;
-
- expect(monitors.length).eql(1);
- } finally {
- await Promise.all([
- testMonitors.map((monitor) => {
- return deleteMonitor(monitor.id, projectMonitors.project);
- }),
- ]);
- }
- });
-
- it('project monitors - deletes monitors when keep stale is false', async () => {
- const secondMonitor = { ...projectMonitors.monitors[0], id: 'test-id-2' };
- const testMonitors = [projectMonitors.monitors[0], secondMonitor];
-
- try {
- await supertest
- .put(API_URLS.SYNTHETICS_MONITORS_PROJECT_LEGACY)
- .set('kbn-xsrf', 'true')
- .send({
- ...projectMonitors,
- keep_stale: false,
- monitors: testMonitors,
- project: 'test-project-2',
- });
-
- const messages = await parseStreamApiResponse(
- projectMonitorEndpoint,
- JSON.stringify({
- ...projectMonitors,
- keep_stale: false,
- project: 'test-project-2',
- })
- );
-
- expect(messages).to.have.length(3);
-
- // expect monitor to have been deleted
- const getResponse = await supertest
- .get(API_URLS.SYNTHETICS_MONITORS)
- .query({
- filter: `${syntheticsMonitorType}.attributes.journey_id: ${secondMonitor.id}`,
- })
- .set('kbn-xsrf', 'true')
- .expect(200);
-
- const { monitors } = getResponse.body;
- expect(monitors[0]).eql(undefined);
- expect(messages[0]).eql(` 1 monitor found with no changes.`);
- expect(messages[1]).eql(`Monitor ${secondMonitor.id} deleted successfully`);
- expect(messages[2].createdMonitors).eql([]);
- expect(messages[2].failedMonitors).eql([]);
- expect(messages[2].updatedMonitors).eql([projectMonitors.monitors[0].id]);
- expect(messages[2].deletedMonitors).eql([secondMonitor.id]);
- expect(messages[2].staleMonitors).eql([]);
- } finally {
- await Promise.all([
- testMonitors.map((monitor) => {
- return deleteMonitor(monitor.id, projectMonitors.project);
- }),
- ]);
- }
- });
-
- it('project monitors - does not delete monitors from different suites when keep stale is false', async () => {
- const secondMonitor = { ...projectMonitors.monitors[0], id: 'test-id-2' };
- const testMonitors = [projectMonitors.monitors[0], secondMonitor];
- const testprojectId = 'test-suite-2';
- try {
- await parseStreamApiResponse(
- projectMonitorEndpoint,
- JSON.stringify({
- ...projectMonitors,
- keep_stale: false,
- monitors: testMonitors,
- })
- );
-
- const messages = await parseStreamApiResponse(
- projectMonitorEndpoint,
- JSON.stringify({
- ...projectMonitors,
- keep_stale: false,
- project: testprojectId,
- })
- );
-
- expect(messages).to.have.length(2);
- expect(messages[1].createdMonitors).eql([projectMonitors.monitors[0].id]);
- expect(messages[1].failedMonitors).eql([]);
- expect(messages[1].deletedMonitors).eql([]);
- expect(messages[1].updatedMonitors).eql([]);
- expect(messages[1].staleMonitors).eql([]);
-
- // expect monitor not to have been deleted
- const getResponse = await supertest
- .get(API_URLS.SYNTHETICS_MONITORS)
- .query({
- filter: `${syntheticsMonitorType}.attributes.journey_id: ${secondMonitor.id}`,
- })
- .set('kbn-xsrf', 'true')
- .expect(200);
-
- const { monitors } = getResponse.body;
-
- expect(monitors.length).eql(1);
- } finally {
- await Promise.all([
- testMonitors.map((monitor) => {
- return deleteMonitor(monitor.id, projectMonitors.project);
- }),
- ]);
-
- await Promise.all([
- testMonitors.map((monitor) => {
- return deleteMonitor(monitor.id, testprojectId);
- }),
- ]);
- }
- });
-
- it('project monitors - does not delete a monitor from the same suite in a different space', async () => {
- const secondMonitor = { ...projectMonitors.monitors[0], id: 'test-id-2' };
- const testMonitors = [projectMonitors.monitors[0], secondMonitor];
- const username = 'admin';
- const roleName = `synthetics_admin`;
- const password = `${username}-password`;
- const SPACE_ID = `test-space-${uuidv4()}`;
- const SPACE_NAME = `test-space-name ${uuidv4()}`;
- await kibanaServer.spaces.create({ id: SPACE_ID, name: SPACE_NAME });
- try {
- await security.role.create(roleName, {
- kibana: [
- {
- feature: {
- uptime: ['all'],
- },
- spaces: ['*'],
- },
- ],
- });
- await security.user.create(username, {
- password,
- roles: [roleName],
- full_name: 'a kibana user',
- });
- await parseStreamApiResponse(
- projectMonitorEndpoint,
- JSON.stringify({ ...projectMonitors, keep_stale: false, monitors: testMonitors }),
- {
- Authorization:
- 'Basic ' + Buffer.from(`${username}:${password}`, 'binary').toString('base64'),
- }
- );
-
- const spaceUrl =
- kibanaServerUrl + `/s/${SPACE_ID}${API_URLS.SYNTHETICS_MONITORS_PROJECT_LEGACY}`;
-
- const messages = await parseStreamApiResponse(
- spaceUrl,
- JSON.stringify({ ...projectMonitors, keep_stale: false }),
- {
- Authorization:
- 'Basic ' + Buffer.from(`${username}:${password}`, 'binary').toString('base64'),
- }
- );
- expect(messages).to.have.length(2);
- expect(messages[1].createdMonitors).eql([projectMonitors.monitors[0].id]);
- expect(messages[1].failedMonitors).eql([]);
- expect(messages[1].deletedMonitors).eql([]);
- expect(messages[1].updatedMonitors).eql([]);
- expect(messages[1].staleMonitors).eql([]);
-
- const getResponse = await supertestWithoutAuth
- .get(API_URLS.SYNTHETICS_MONITORS)
- .auth(username, password)
- .query({
- filter: `${syntheticsMonitorType}.attributes.journey_id: ${secondMonitor.id}`,
- })
- .set('kbn-xsrf', 'true')
- .expect(200);
- const { monitors } = getResponse.body;
- expect(monitors.length).eql(1);
- } finally {
- await Promise.all([
- testMonitors.map((monitor) => {
- return deleteMonitor(
- monitor.id,
- projectMonitors.project,
- 'default',
- username,
- password
- );
- }),
- ]);
- await deleteMonitor(
- projectMonitors.monitors[0].id,
- projectMonitors.project,
- SPACE_ID,
- username,
- password
- );
- await security.user.delete(username);
- await security.role.delete(roleName);
- }
- });
-
- it('project monitors - validates monitor type', async () => {
- try {
- const messages = await parseStreamApiResponse(
- projectMonitorEndpoint,
- JSON.stringify({
- ...projectMonitors,
- monitors: [{ ...projectMonitors.monitors[0], schedule: '3m', tags: '' }],
- })
- );
-
- expect(messages).to.have.length(1);
- expect(messages[0].updatedMonitors).eql([]);
- expect(messages[0].failedMonitors).eql([
- {
- details: 'Invalid value "3m" supplied to "schedule"',
- id: projectMonitors.monitors[0].id,
- payload: {
- content:
- 'UEsDBBQACAAIAON5qVQAAAAAAAAAAAAAAAAfAAAAZXhhbXBsZXMvdG9kb3MvYmFzaWMuam91cm5leS50c22Q0WrDMAxF3/sVF7MHB0LMXlc6RvcN+wDPVWNviW0sdUsp/fe5SSiD7UFCWFfHujIGlpnkybwxFTZfoY/E3hsaLEtwhs9RPNWKDU12zAOxkXRIbN4tB9d9pFOJdO6EN2HMqQguWN9asFBuQVMmJ7jiWNII9fIXrbabdUYr58l9IhwhQQZCYORCTFFUC31Btj21NRc7Mq4Nds+4bDD/pNVgT9F52Jyr2Fa+g75LAPttg8yErk+S9ELpTmVotlVwnfNCuh2lepl3+JflUmSBJ3uggt1v9INW/lHNLKze9dJe1J3QJK8pSvWkm6aTtCet5puq+x63+AFQSwcIAPQ3VfcAAACcAQAAUEsBAi0DFAAIAAgA43mpVAD0N1X3AAAAnAEAAB8AAAAAAAAAAAAgAKSBAAAAAGV4YW1wbGVzL3RvZG9zL2Jhc2ljLmpvdXJuZXkudHNQSwUGAAAAAAEAAQBNAAAARAEAAAAA',
- filter: {
- match: 'check if title is present',
- },
- id: projectMonitors.monitors[0].id,
- locations: ['localhost'],
- name: 'check if title is present',
- params: {},
- playwrightOptions: {
- chromiumSandbox: false,
- headless: true,
- },
- schedule: '3m',
- tags: '',
- throttling: {
- download: 5,
- latency: 20,
- upload: 3,
- },
- type: 'browser',
- hash: 'ekrjelkjrelkjre',
- },
- reason: "Couldn't save or update monitor because of an invalid configuration.",
- },
- ]);
- expect(messages[0].createdMonitors).eql([]);
- } finally {
- await Promise.all([
- projectMonitors.monitors.map((monitor) => {
- return deleteMonitor(monitor.id, projectMonitors.project);
- }),
- ]);
- }
- });
-
- it('project monitors - saves space as data stream namespace', async () => {
- const username = 'admin';
- const roleName = `synthetics_admin`;
- const password = `${username}-password`;
- const SPACE_ID = `test-space-${uuidv4()}`;
- const SPACE_NAME = `test-space-name ${uuidv4()}`;
- await kibanaServer.spaces.create({ id: SPACE_ID, name: SPACE_NAME });
- try {
- await security.role.create(roleName, {
- kibana: [
- {
- feature: {
- uptime: ['all'],
- },
- spaces: ['*'],
- },
- ],
- });
- await security.user.create(username, {
- password,
- roles: [roleName],
- full_name: 'a kibana user',
- });
- await supertestWithoutAuth
- .put(`/s/${SPACE_ID}${API_URLS.SYNTHETICS_MONITORS_PROJECT_LEGACY}`)
- .auth(username, password)
- .set('kbn-xsrf', 'true')
- .send(projectMonitors)
- .expect(200);
- // expect monitor not to have been deleted
- const getResponse = await supertestWithoutAuth
- .get(`/s/${SPACE_ID}${API_URLS.SYNTHETICS_MONITORS}`)
- .auth(username, password)
- .query({
- filter: `${syntheticsMonitorType}.attributes.journey_id: ${projectMonitors.monitors[0].id}`,
- })
- .set('kbn-xsrf', 'true')
- .expect(200);
- const { monitors } = getResponse.body;
- expect(monitors.length).eql(1);
- expect(monitors[0].attributes[ConfigKey.NAMESPACE]).eql(formatKibanaNamespace(SPACE_ID));
- } finally {
- await deleteMonitor(
- projectMonitors.monitors[0].id,
- projectMonitors.project,
- SPACE_ID,
- username,
- password
- );
- await security.user.delete(username);
- await security.role.delete(roleName);
- }
- });
-
- it('project monitors - formats custom id appropriately', async () => {
- const username = 'admin';
- const roleName = `synthetics_admin`;
- const password = `${username}-password`;
- const SPACE_ID = `test-space-${uuidv4()}`;
- const SPACE_NAME = `test-space-name ${uuidv4()}`;
- await kibanaServer.spaces.create({ id: SPACE_ID, name: SPACE_NAME });
- try {
- await security.role.create(roleName, {
- kibana: [
- {
- feature: {
- uptime: ['all'],
- },
- spaces: ['*'],
- },
- ],
- });
- await security.user.create(username, {
- password,
- roles: [roleName],
- full_name: 'a kibana user',
- });
- await supertestWithoutAuth
- .put(`/s/${SPACE_ID}${API_URLS.SYNTHETICS_MONITORS_PROJECT_LEGACY}`)
- .auth(username, password)
- .set('kbn-xsrf', 'true')
- .send(projectMonitors)
- .expect(200);
- // expect monitor not to have been deleted
- const getResponse = await supertestWithoutAuth
- .get(`/s/${SPACE_ID}${API_URLS.SYNTHETICS_MONITORS}`)
- .auth(username, password)
- .query({
- filter: `${syntheticsMonitorType}.attributes.journey_id: ${projectMonitors.monitors[0].id}`,
- })
- .set('kbn-xsrf', 'true')
- .expect(200);
- const { monitors } = getResponse.body;
- expect(monitors.length).eql(1);
- expect(monitors[0].attributes[ConfigKey.CUSTOM_HEARTBEAT_ID]).eql(
- `${projectMonitors.monitors[0].id}-${projectMonitors.project}-${SPACE_ID}`
- );
- } finally {
- await deleteMonitor(
- projectMonitors.monitors[0].id,
- projectMonitors.project,
- SPACE_ID,
- username,
- password
- );
- await security.user.delete(username);
- await security.role.delete(roleName);
- }
- });
-
- it('project monitors - is able to decrypt monitor when updated after hydration', async () => {
- try {
- await supertest
- .put(API_URLS.SYNTHETICS_MONITORS_PROJECT_LEGACY)
- .set('kbn-xsrf', 'true')
- .send(projectMonitors);
-
- const response = await supertest
- .get(API_URLS.SYNTHETICS_MONITORS)
- .query({
- filter: `${syntheticsMonitorType}.attributes.journey_id: ${projectMonitors.monitors[0].id}`,
- })
- .set('kbn-xsrf', 'true')
- .expect(200);
-
- const { monitors } = response.body;
-
- // add urls and ports to mimic hydration
- const updates = {
- [ConfigKey.URLS]: 'https://modified-host.com',
- [ConfigKey.PORT]: 443,
- };
-
- const modifiedMonitor = { ...monitors[0]?.attributes, ...updates };
-
- await supertest
- .put(API_URLS.SYNTHETICS_MONITORS + '/' + monitors[0]?.id)
- .set('kbn-xsrf', 'true')
- .send(modifiedMonitor)
- .expect(200);
-
- // update project monitor via push api
- const messages = await parseStreamApiResponse(
- projectMonitorEndpoint,
- JSON.stringify(projectMonitors)
- );
- expect(messages).to.have.length(2);
- expect(messages[0]).eql('1 monitor updated successfully. ');
- expect(messages[1].updatedMonitors).eql([projectMonitors.monitors[0].id]);
-
- // ensure that monitor can still be decrypted
- await supertest
- .get(API_URLS.SYNTHETICS_MONITORS + '/' + monitors[0]?.id)
- .set('kbn-xsrf', 'true')
- .expect(200);
- } finally {
- await Promise.all([
- projectMonitors.monitors.map((monitor) => {
- return deleteMonitor(monitor.id, projectMonitors.project);
- }),
- ]);
- }
- });
-
- it('project monitors - is able to enable and disable monitors', async () => {
- try {
- await supertest
- .put(API_URLS.SYNTHETICS_MONITORS_PROJECT_LEGACY)
- .set('kbn-xsrf', 'true')
- .send(projectMonitors);
-
- await supertest
- .put(API_URLS.SYNTHETICS_MONITORS_PROJECT_LEGACY)
- .set('kbn-xsrf', 'true')
- .send({
- ...projectMonitors,
- monitors: [
- {
- ...projectMonitors.monitors[0],
- enabled: false,
- },
- ],
- })
- .expect(200);
- const response = await supertest
- .get(API_URLS.SYNTHETICS_MONITORS)
- .query({
- filter: `${syntheticsMonitorType}.attributes.journey_id: ${projectMonitors.monitors[0].id}`,
- })
- .set('kbn-xsrf', 'true')
- .expect(200);
- const { monitors } = response.body;
- expect(monitors[0].attributes.enabled).eql(false);
- } finally {
- await Promise.all([
- projectMonitors.monitors.map((monitor) => {
- return deleteMonitor(monitor.id, projectMonitors.project);
- }),
- ]);
- }
- });
-
- it('project monitors - returns a failed monitor when user defines a private location without fleet permissions', async () => {
- const secondMonitor = {
- ...projectMonitors.monitors[0],
- id: 'test-id-2',
- privateLocations: ['Test private location 0'],
- };
- const testMonitors = [projectMonitors.monitors[0], secondMonitor];
- const username = 'admin';
- const roleName = 'uptime read only';
- const password = `${username}-password`;
- try {
- await security.role.create(roleName, {
- kibana: [
- {
- feature: {
- uptime: ['all'],
- },
- spaces: ['*'],
- },
- ],
- });
- await security.user.create(username, {
- password,
- roles: [roleName],
- full_name: 'a kibana user',
- });
-
- const messages = await parseStreamApiResponse(
- kibanaServerUrl + API_URLS.SYNTHETICS_MONITORS_PROJECT_LEGACY,
- JSON.stringify({
- ...projectMonitors,
- keep_stale: false,
- monitors: testMonitors,
- }),
- {
- Authorization:
- 'Basic ' + Buffer.from(`${username}:${password}`, 'binary').toString('base64'),
- }
- );
-
- expect(messages).to.have.length(3);
- expect(messages[0]).to.eql('test-id-2: failed to create or update monitor');
- expect(messages[1]).to.eql(`1 monitor created successfully.`);
- expect(messages[2]).to.eql({
- createdMonitors: [testMonitors[0].id],
- updatedMonitors: [],
- staleMonitors: [],
- deletedMonitors: [],
- failedMonitors: [
- {
- details:
- 'Insufficient permissions. In order to configure private locations, you must have Fleet and Integrations write permissions. To resolve, please generate a new API key with a user who has Fleet and Integrations write permissions.',
- id: 'test-id-2',
- payload: {
- content:
- 'UEsDBBQACAAIAON5qVQAAAAAAAAAAAAAAAAfAAAAZXhhbXBsZXMvdG9kb3MvYmFzaWMuam91cm5leS50c22Q0WrDMAxF3/sVF7MHB0LMXlc6RvcN+wDPVWNviW0sdUsp/fe5SSiD7UFCWFfHujIGlpnkybwxFTZfoY/E3hsaLEtwhs9RPNWKDU12zAOxkXRIbN4tB9d9pFOJdO6EN2HMqQguWN9asFBuQVMmJ7jiWNII9fIXrbabdUYr58l9IhwhQQZCYORCTFFUC31Btj21NRc7Mq4Nds+4bDD/pNVgT9F52Jyr2Fa+g75LAPttg8yErk+S9ELpTmVotlVwnfNCuh2lepl3+JflUmSBJ3uggt1v9INW/lHNLKze9dJe1J3QJK8pSvWkm6aTtCet5puq+x63+AFQSwcIAPQ3VfcAAACcAQAAUEsBAi0DFAAIAAgA43mpVAD0N1X3AAAAnAEAAB8AAAAAAAAAAAAgAKSBAAAAAGV4YW1wbGVzL3RvZG9zL2Jhc2ljLmpvdXJuZXkudHNQSwUGAAAAAAEAAQBNAAAARAEAAAAA',
- filter: {
- match: 'check if title is present',
- },
- id: 'test-id-2',
- locations: ['localhost'],
- name: 'check if title is present',
- params: {},
- playwrightOptions: {
- chromiumSandbox: false,
- headless: true,
- },
- privateLocations: ['Test private location 0'],
- schedule: 10,
- tags: [],
- throttling: {
- download: 5,
- latency: 20,
- upload: 3,
- },
- hash: 'ekrjelkjrelkjre',
- },
- reason: 'Failed to create or update monitor',
- },
- ],
- failedStaleMonitors: [],
- });
- } finally {
- await Promise.all([
- testMonitors.map((monitor) => {
- return deleteMonitor(
- monitor.id,
- projectMonitors.project,
- 'default',
- username,
- password
- );
- }),
- ]);
- await security.user.delete(username);
- await security.role.delete(roleName);
- }
- });
-
- it('project monitors - returns a failed monitor when user tries to delete a monitor without fleet permissions', async () => {
- const secondMonitor = {
- ...projectMonitors.monitors[0],
- id: 'test-id-2',
- privateLocations: ['Test private location 0'],
- };
- const testMonitors = [projectMonitors.monitors[0], secondMonitor];
- const username = 'test-username';
- const roleName = 'uptime read only';
- const password = `test-password`;
- try {
- await security.role.create(roleName, {
- kibana: [
- {
- feature: {
- uptime: ['all'],
- },
- spaces: ['*'],
- },
- ],
- });
- await security.user.create(username, {
- password,
- roles: [roleName],
- full_name: 'a kibana user',
- });
-
- await parseStreamApiResponse(
- kibanaServerUrl + API_URLS.SYNTHETICS_MONITORS_PROJECT_LEGACY,
- JSON.stringify({
- ...projectMonitors,
- keep_stale: false,
- monitors: testMonitors,
- })
- );
-
- const messages = await parseStreamApiResponse(
- kibanaServerUrl + API_URLS.SYNTHETICS_MONITORS_PROJECT_LEGACY,
- JSON.stringify({
- ...projectMonitors,
- keep_stale: false,
- monitors: [],
- }),
- {
- Authorization:
- 'Basic ' + Buffer.from(`${username}:${password}`, 'binary').toString('base64'),
- }
- );
-
- expect(messages).to.have.length(3);
- expect(
- messages.filter((msg) => msg === `Monitor ${testMonitors[1].id} could not be deleted`)
- ).to.have.length(1);
- expect(
- messages.filter((msg) => msg === `Monitor ${testMonitors[0].id} deleted successfully`)
- ).to.have.length(1);
- expect(messages[2]).to.eql({
- createdMonitors: [],
- updatedMonitors: [],
- staleMonitors: [],
- deletedMonitors: [testMonitors[0].id],
- failedMonitors: [],
- failedStaleMonitors: [
- {
- details:
- 'Unable to delete Synthetics package policy for monitor check if title is present. Fleet write permissions are needed to use Synthetics private locations.',
- id: 'test-id-2',
- reason: 'Failed to delete stale monitor',
- },
- ],
- });
-
- const messages2 = await parseStreamApiResponse(
- kibanaServerUrl + API_URLS.SYNTHETICS_MONITORS_PROJECT_LEGACY,
- JSON.stringify({
- ...projectMonitors,
- keep_stale: false,
- monitors: [],
- })
- );
-
- expect(messages2).to.have.length(2);
- expect(messages2[0]).to.eql(`Monitor ${testMonitors[1].id} deleted successfully`);
- expect(messages2[1]).to.eql({
- createdMonitors: [],
- updatedMonitors: [],
- staleMonitors: [],
- deletedMonitors: [testMonitors[1].id],
- failedMonitors: [],
- failedStaleMonitors: [],
- });
- } finally {
- await Promise.all([
- testMonitors.map((monitor) => {
- return deleteMonitor(
- monitor.id,
- projectMonitors.project,
- 'default',
- username,
- password
- );
- }),
- ]);
- await security.user.delete(username);
- await security.role.delete(roleName);
- }
- });
-
- it('project monitors - returns a successful monitor when user defines a private location with fleet permissions', async () => {
- const secondMonitor = {
- ...projectMonitors.monitors[0],
- id: 'test-id-2',
- privateLocations: ['Test private location 0'],
- };
- const testMonitors = [projectMonitors.monitors[0], secondMonitor];
- const username = 'admin';
- const roleName = 'uptime with fleet';
- const password = `${username}-password`;
- try {
- await security.role.create(roleName, {
- kibana: [
- {
- feature: {
- uptime: ['all'],
- fleetv2: ['all'],
- fleet: ['all'],
- },
- spaces: ['*'],
- },
- ],
- });
- await security.user.create(username, {
- password,
- roles: [roleName],
- full_name: 'a kibana user',
- });
- const messages = await parseStreamApiResponse(
- projectMonitorEndpoint,
- JSON.stringify({
- ...projectMonitors,
- keep_stale: false,
- monitors: testMonitors,
- })
- );
- expect(messages).to.have.length(2);
- expect(messages).to.eql([
- `2 monitors created successfully.`,
- {
- createdMonitors: [testMonitors[0].id, 'test-id-2'],
- updatedMonitors: [],
- staleMonitors: [],
- deletedMonitors: [],
- failedMonitors: [],
- failedStaleMonitors: [],
- },
- ]);
- } finally {
- await Promise.all([
- testMonitors.map((monitor) => {
- return deleteMonitor(
- monitor.id,
- projectMonitors.project,
- 'default',
- username,
- password
- );
- }),
- ]);
- await security.user.delete(username);
- await security.role.delete(roleName);
- }
- });
-
- it('creates integration policies for project monitors with private locations', async () => {
- try {
- await supertest
- .put(API_URLS.SYNTHETICS_MONITORS_PROJECT_LEGACY)
- .set('kbn-xsrf', 'true')
- .send({
- ...projectMonitors,
- monitors: [
- { ...projectMonitors.monitors[0], privateLocations: ['Test private location 0'] },
- ],
- });
-
- const monitorsResponse = await supertest
- .get(API_URLS.SYNTHETICS_MONITORS)
- .query({
- filter: `${syntheticsMonitorType}.attributes.journey_id: ${projectMonitors.monitors[0].id}`,
- })
- .set('kbn-xsrf', 'true')
- .expect(200);
-
- const apiResponsePolicy = await supertest.get(
- '/api/fleet/package_policies?page=1&perPage=2000&kuery=ingest-package-policies.package.name%3A%20synthetics'
- );
-
- const packagePolicy = apiResponsePolicy.body.items.find(
- (pkgPolicy: PackagePolicy) =>
- pkgPolicy.id ===
- `${
- monitorsResponse.body.monitors[0].attributes[ConfigKey.CUSTOM_HEARTBEAT_ID]
- }-${testPolicyId}`
- );
- expect(packagePolicy.name).eql(
- `${projectMonitors.monitors[0].id}-${projectMonitors.project}-default-Test private location 0`
- );
- expect(packagePolicy.policy_id).eql(testPolicyId);
-
- const configId = monitorsResponse.body.monitors[0].id;
- const id = monitorsResponse.body.monitors[0].attributes[ConfigKey.CUSTOM_HEARTBEAT_ID];
-
- comparePolicies(
- packagePolicy,
- getTestProjectSyntheticsPolicy({
- inputs: {},
- name: 'check if title is present-Test private location 0',
- id,
- configId,
- locationName: 'Test private location 0',
- })
- );
- } finally {
- await deleteMonitor(projectMonitors.monitors[0].id, projectMonitors.project);
-
- const packagesResponse = await supertest.get(
- '/api/fleet/package_policies?page=1&perPage=2000&kuery=ingest-package-policies.package.name%3A%20synthetics'
- );
- expect(packagesResponse.body.items.length).eql(0);
- }
- });
-
- it('deletes integration policies for project monitors when private location is removed from the monitor - lightweight', async () => {
- const monitorRequest = {
- ...httpProjectMonitors,
- monitors: [
- { ...httpProjectMonitors.monitors[1], privateLocations: ['Test private location 0'] },
- ],
- };
- try {
- await supertest
- .put(API_URLS.SYNTHETICS_MONITORS_PROJECT_LEGACY)
- .set('kbn-xsrf', 'true')
- .send(monitorRequest);
-
- const monitorsResponse = await supertest
- .get(API_URLS.SYNTHETICS_MONITORS)
- .query({
- filter: `${syntheticsMonitorType}.attributes.journey_id: ${monitorRequest.monitors[0].id}`,
- })
- .set('kbn-xsrf', 'true')
- .expect(200);
-
- const apiResponsePolicy = await supertest.get(
- '/api/fleet/package_policies?page=1&perPage=2000&kuery=ingest-package-policies.package.name%3A%20synthetics'
- );
-
- const packagePolicy = apiResponsePolicy.body.items.find(
- (pkgPolicy: PackagePolicy) =>
- pkgPolicy.id ===
- `${
- monitorsResponse.body.monitors[0].attributes[ConfigKey.CUSTOM_HEARTBEAT_ID]
- }-${testPolicyId}`
- );
-
- expect(packagePolicy.policy_id).eql(testPolicyId);
-
- await supertest
- .put(API_URLS.SYNTHETICS_MONITORS_PROJECT_LEGACY)
- .set('kbn-xsrf', 'true')
- .send({
- ...monitorRequest,
- monitors: [{ ...monitorRequest.monitors[0], privateLocations: [] }],
- });
-
- const apiResponsePolicy2 = await supertest.get(
- '/api/fleet/package_policies?page=1&perPage=2000&kuery=ingest-package-policies.package.name%3A%20synthetics'
- );
-
- const packagePolicy2 = apiResponsePolicy2.body.items.find(
- (pkgPolicy: PackagePolicy) =>
- pkgPolicy.id ===
- `${
- monitorsResponse.body.monitors[0].attributes[ConfigKey.CUSTOM_HEARTBEAT_ID]
- }-${testPolicyId}`
- );
-
- expect(packagePolicy2).eql(undefined);
- } finally {
- await deleteMonitor(projectMonitors.monitors[0].id, projectMonitors.project);
- }
- });
-
- it('deletes integration policies for project monitors when private location is removed from the monitor', async () => {
- try {
- await supertest
- .put(API_URLS.SYNTHETICS_MONITORS_PROJECT_LEGACY)
- .set('kbn-xsrf', 'true')
- .send({
- ...projectMonitors,
- monitors: [
- { ...projectMonitors.monitors[0], privateLocations: ['Test private location 0'] },
- ],
- });
-
- const monitorsResponse = await supertest
- .get(API_URLS.SYNTHETICS_MONITORS)
- .query({
- filter: `${syntheticsMonitorType}.attributes.journey_id: ${projectMonitors.monitors[0].id}`,
- })
- .set('kbn-xsrf', 'true')
- .expect(200);
-
- const apiResponsePolicy = await supertest.get(
- '/api/fleet/package_policies?page=1&perPage=2000&kuery=ingest-package-policies.package.name%3A%20synthetics'
- );
-
- const packagePolicy = apiResponsePolicy.body.items.find(
- (pkgPolicy: PackagePolicy) =>
- pkgPolicy.id ===
- `${
- monitorsResponse.body.monitors[0].attributes[ConfigKey.CUSTOM_HEARTBEAT_ID]
- }-${testPolicyId}`
- );
-
- expect(packagePolicy.policy_id).eql(testPolicyId);
-
- const configId = monitorsResponse.body.monitors[0].id;
- const id = monitorsResponse.body.monitors[0].attributes[ConfigKey.CUSTOM_HEARTBEAT_ID];
-
- comparePolicies(
- packagePolicy,
- getTestProjectSyntheticsPolicy({
- inputs: {},
- name: 'check if title is present-Test private location 0',
- id,
- configId,
- locationName: 'Test private location 0',
- })
- );
-
- await supertest
- .put(API_URLS.SYNTHETICS_MONITORS_PROJECT_LEGACY)
- .set('kbn-xsrf', 'true')
- .send({
- ...projectMonitors,
- monitors: [{ ...projectMonitors.monitors[0], privateLocations: [] }],
- });
-
- const apiResponsePolicy2 = await supertest.get(
- '/api/fleet/package_policies?page=1&perPage=2000&kuery=ingest-package-policies.package.name%3A%20synthetics'
- );
-
- const packagePolicy2 = apiResponsePolicy2.body.items.find(
- (pkgPolicy: PackagePolicy) =>
- pkgPolicy.id ===
- `${
- monitorsResponse.body.monitors[0].attributes[ConfigKey.CUSTOM_HEARTBEAT_ID]
- }-${testPolicyId}`
- );
-
- expect(packagePolicy2).eql(undefined);
- } finally {
- await deleteMonitor(projectMonitors.monitors[0].id, projectMonitors.project);
-
- const apiResponsePolicy2 = await supertest.get(
- '/api/fleet/package_policies?page=1&perPage=2000&kuery=ingest-package-policies.package.name%3A%20synthetics'
- );
- expect(apiResponsePolicy2.body.items.length).eql(0);
- }
- });
-
- it('deletes integration policies when project monitors are deleted', async () => {
- try {
- await supertest
- .put(API_URLS.SYNTHETICS_MONITORS_PROJECT_LEGACY)
- .set('kbn-xsrf', 'true')
- .send({
- ...projectMonitors,
- monitors: [
- { ...projectMonitors.monitors[0], privateLocations: ['Test private location 0'] },
- ],
- })
- .expect(200);
-
- const monitorsResponse = await supertest
- .get(API_URLS.SYNTHETICS_MONITORS)
- .query({
- filter: `${syntheticsMonitorType}.attributes.journey_id: ${projectMonitors.monitors[0].id}`,
- })
- .set('kbn-xsrf', 'true')
- .expect(200);
-
- const apiResponsePolicy = await supertest.get(
- '/api/fleet/package_policies?page=1&perPage=2000&kuery=ingest-package-policies.package.name%3A%20synthetics'
- );
-
- const packagePolicy = apiResponsePolicy.body.items.find(
- (pkgPolicy: PackagePolicy) =>
- pkgPolicy.id ===
- monitorsResponse.body.monitors[0].attributes[ConfigKey.CUSTOM_HEARTBEAT_ID] +
- '-' +
- testPolicyId
- );
-
- expect(packagePolicy.policy_id).eql(testPolicyId);
-
- const configId = monitorsResponse.body.monitors[0].id;
- const id = monitorsResponse.body.monitors[0].attributes[ConfigKey.CUSTOM_HEARTBEAT_ID];
-
- comparePolicies(
- packagePolicy,
- getTestProjectSyntheticsPolicy({
- inputs: {},
- name: 'check if title is present-Test private location 0',
- id,
- configId,
- locationName: 'Test private location 0',
- })
- );
-
- await supertest
- .put(API_URLS.SYNTHETICS_MONITORS_PROJECT_LEGACY)
- .set('kbn-xsrf', 'true')
- .send({
- ...projectMonitors,
- monitors: [],
- keep_stale: false,
- });
-
- const monitorsResponse2 = await supertest
- .get(API_URLS.SYNTHETICS_MONITORS)
- .query({
- filter: `${syntheticsMonitorType}.attributes.journey_id: ${projectMonitors.monitors[0].id}`,
- })
- .set('kbn-xsrf', 'true')
- .expect(200);
-
- expect(monitorsResponse2.body.monitors.length).eql(0);
-
- await new Promise((resolve) => {
- setTimeout(() => resolve(null), 3000);
- });
-
- const apiResponsePolicy2 = await supertest.get(
- '/api/fleet/package_policies?page=1&perPage=2000&kuery=ingest-package-policies.package.name%3A%20synthetics'
- );
-
- const packagePolicy2 = apiResponsePolicy2.body.items.find(
- (pkgPolicy: PackagePolicy) =>
- pkgPolicy.id ===
- monitorsResponse.body.monitors[0].attributes[ConfigKey.CUSTOM_HEARTBEAT_ID] +
- '-' +
- testPolicyId
- );
-
- expect(packagePolicy2).eql(undefined);
- } finally {
- await deleteMonitor(projectMonitors.monitors[0].id, projectMonitors.project);
-
- const apiResponsePolicy2 = await supertest.get(
- '/api/fleet/package_policies?page=1&perPage=2000&kuery=ingest-package-policies.package.name%3A%20synthetics'
- );
- expect(apiResponsePolicy2.body.items.length).eql(0);
- }
- });
-
- it('deletes integration policies when project monitors are deleted - lightweight', async () => {
- const monitorRequest = {
- ...httpProjectMonitors,
- monitors: [
- { ...httpProjectMonitors.monitors[1], privateLocations: ['Test private location 0'] },
- ],
- };
- try {
- await supertest
- .put(API_URLS.SYNTHETICS_MONITORS_PROJECT_LEGACY)
- .set('kbn-xsrf', 'true')
- .send(monitorRequest);
-
- const monitorsResponse = await supertest
- .get(API_URLS.SYNTHETICS_MONITORS)
- .query({
- filter: `${syntheticsMonitorType}.attributes.journey_id: ${monitorRequest.monitors[0].id}`,
- })
- .set('kbn-xsrf', 'true')
- .expect(200);
-
- const apiResponsePolicy = await supertest.get(
- '/api/fleet/package_policies?page=1&perPage=2000&kuery=ingest-package-policies.package.name%3A%20synthetics'
- );
-
- const packagePolicy = apiResponsePolicy.body.items.find(
- (pkgPolicy: PackagePolicy) =>
- pkgPolicy.id ===
- `${
- monitorsResponse.body.monitors[0].attributes[ConfigKey.CUSTOM_HEARTBEAT_ID]
- }-${testPolicyId}`
- );
-
- expect(packagePolicy.policy_id).eql(testPolicyId);
-
- const configId = monitorsResponse.body.monitors[0].id;
- const id = monitorsResponse.body.monitors[0].attributes[ConfigKey.CUSTOM_HEARTBEAT_ID];
-
- const httpInput = packagePolicy.inputs.find(
- (input: any) => input.type === 'synthetics/http'
- );
- expect(httpInput).to.eql({
- type: 'synthetics/http',
- policy_template: 'synthetics',
- enabled: true,
- streams: [
- {
- enabled: true,
- data_stream: {
- elasticsearch: {
- privileges: {
- indices: ['auto_configure', 'create_doc', 'read'],
- },
- },
- type: 'synthetics',
- dataset: 'http',
- },
- vars: {
- __ui: { value: '{"is_tls_enabled":true}', type: 'yaml' },
- enabled: { value: false, type: 'bool' },
- type: { value: 'http', type: 'text' },
- name: { value: 'My Monitor 3', type: 'text' },
- schedule: { value: '"@every 60m"', type: 'text' },
- urls: { value: 'http://localhost:9200', type: 'text' },
- 'service.name': { value: '', type: 'text' },
- timeout: { value: '80s', type: 'text' },
- max_redirects: { value: '0', type: 'integer' },
- proxy_url: { value: '', type: 'text' },
- proxy_headers: { value: null, type: 'yaml' },
- tags: { value: '["tag2","tag2"]', type: 'yaml' },
- username: { value: '', type: 'text' },
- password: { value: '', type: 'password' },
- 'response.include_headers': { value: false, type: 'bool' },
- 'response.include_body': { value: 'always', type: 'text' },
- 'response.include_body_max_bytes': { value: '900', type: 'text' },
- 'check.request.method': { value: 'POST', type: 'text' },
- 'check.request.headers': {
- value: '{"Content-Type":"application/x-www-form-urlencoded"}',
- type: 'yaml',
- },
- 'check.request.body': { value: null, type: 'yaml' },
- 'check.response.status': { value: '["200"]', type: 'yaml' },
- 'check.response.headers': { value: null, type: 'yaml' },
- 'check.response.body.positive': { value: '["Saved","saved"]', type: 'yaml' },
- 'check.response.body.negative': { value: null, type: 'yaml' },
- 'check.response.json': {
- value: '[{"description":"check status","expression":"foo.bar == \\"myValue\\""}]',
- type: 'yaml',
- },
- 'ssl.certificate_authorities': { value: null, type: 'yaml' },
- 'ssl.certificate': { value: null, type: 'yaml' },
- 'ssl.key': { value: null, type: 'yaml' },
- 'ssl.key_passphrase': { value: null, type: 'text' },
- 'ssl.verification_mode': { value: 'strict', type: 'text' },
- 'ssl.supported_protocols': {
- value: '["TLSv1.1","TLSv1.2","TLSv1.3"]',
- type: 'yaml',
- },
- location_name: { value: 'Test private location 0', type: 'text' },
- id: {
- value: id,
- type: 'text',
- },
- config_id: { value: configId, type: 'text' },
- run_once: { value: false, type: 'bool' },
- origin: { value: 'project', type: 'text' },
- 'monitor.project.id': {
- type: 'text',
- value: 'test-suite',
- },
- 'monitor.project.name': {
- type: 'text',
- value: 'test-suite',
- },
- ipv4: { type: 'bool', value: true },
- ipv6: { type: 'bool', value: true },
- mode: { type: 'text', value: 'any' },
- },
- id: `synthetics/http-http-${id}-${testPolicyId}`,
- compiled_stream: {
- __ui: { is_tls_enabled: true },
- type: 'http',
- name: 'My Monitor 3',
- id,
- origin: 'project',
- enabled: false,
- urls: 'http://localhost:9200',
- schedule: '@every 60m',
- timeout: '80s',
- max_redirects: 0,
- tags: ['tag2', 'tag2'],
- 'response.include_headers': false,
- 'response.include_body': 'always',
- 'check.request.method': 'POST',
- 'check.request.headers': { 'Content-Type': 'application/x-www-form-urlencoded' },
- 'check.response.status': ['200'],
- 'check.response.body.positive': ['Saved', 'saved'],
- 'ssl.verification_mode': 'strict',
- 'ssl.supported_protocols': ['TLSv1.1', 'TLSv1.2', 'TLSv1.3'],
- 'run_from.geo.name': 'Test private location 0',
- 'run_from.id': 'Test private location 0',
- 'check.response.json': [
- {
- description: 'check status',
- expression: 'foo.bar == "myValue"',
- },
- ],
- ipv4: true,
- ipv6: true,
- mode: 'any',
- processors: [
- {
- add_fields: {
- target: '',
- fields: {
- 'monitor.fleet_managed': true,
- config_id: configId,
- 'monitor.project.id': 'test-suite',
- 'monitor.project.name': 'test-suite',
- },
- },
- },
- ],
- },
- },
- ],
- });
-
- await supertest
- .put(API_URLS.SYNTHETICS_MONITORS_PROJECT_LEGACY)
- .set('kbn-xsrf', 'true')
- .send({
- ...monitorRequest,
- monitors: [],
- });
-
- const apiResponsePolicy2 = await supertest.get(
- '/api/fleet/package_policies?page=1&perPage=2000&kuery=ingest-package-policies.package.name%3A%20synthetics'
- );
-
- const packagePolicy2 = apiResponsePolicy2.body.items.find(
- (pkgPolicy: PackagePolicy) =>
- pkgPolicy.id ===
- `${
- monitorsResponse.body.monitors[0].attributes[ConfigKey.CUSTOM_HEARTBEAT_ID]
- } - ${testPolicyId}`
- );
-
- expect(packagePolicy2).eql(undefined);
- } finally {
- await deleteMonitor(httpProjectMonitors.monitors[1].id, httpProjectMonitors.project);
-
- const apiResponsePolicy2 = await supertest.get(
- '/api/fleet/package_policies?page=1&perPage=2000&kuery=ingest-package-policies.package.name%3A%20synthetics'
- );
- expect(apiResponsePolicy2.body.items.length).eql(0);
- }
- });
-
- it('handles updating package policies when project monitors are updated', async () => {
- try {
- await supertest
- .put(API_URLS.SYNTHETICS_MONITORS_PROJECT_LEGACY)
- .set('kbn-xsrf', 'true')
- .send({
- ...projectMonitors,
- monitors: [
- {
- ...projectMonitors.monitors[0],
- privateLocations: ['Test private location 0'],
- },
- ],
- });
-
- const monitorsResponse = await supertest
- .get(API_URLS.SYNTHETICS_MONITORS)
- .query({
- filter: `${syntheticsMonitorType}.attributes.journey_id: ${projectMonitors.monitors[0].id}`,
- })
- .set('kbn-xsrf', 'true')
- .expect(200);
-
- const apiResponsePolicy = await supertest.get(
- '/api/fleet/package_policies?page=1&perPage=2000&kuery=ingest-package-policies.package.name%3A%20synthetics'
- );
-
- const configId = monitorsResponse.body.monitors[0].id;
- const id = monitorsResponse.body.monitors[0].attributes[ConfigKey.CUSTOM_HEARTBEAT_ID];
- const policyId = `${id}-${testPolicyId}`;
-
- const packagePolicy = apiResponsePolicy.body.items.find(
- (pkgPolicy: PackagePolicy) => pkgPolicy.id === policyId
- );
-
- expect(packagePolicy.policy_id).eql(testPolicyId);
-
- comparePolicies(
- packagePolicy,
- getTestProjectSyntheticsPolicy({
- inputs: {},
- name: 'check if title is present-Test private location 0',
- id,
- configId,
- locationName: 'Test private location 0',
- })
- );
-
- await supertest
- .put(API_URLS.SYNTHETICS_MONITORS_PROJECT_LEGACY)
- .set('kbn-xsrf', 'true')
- .send({
- ...projectMonitors,
- monitors: [
- {
- ...projectMonitors.monitors[0],
- privateLocations: ['Test private location 0'],
- enabled: false,
- },
- ],
- });
-
- const apiResponsePolicy2 = await supertest.get(
- '/api/fleet/package_policies?page=1&perPage=2000&kuery=ingest-package-policies.package.name%3A%20synthetics'
- );
-
- const configId2 = monitorsResponse.body.monitors[0].id;
- const id2 = monitorsResponse.body.monitors[0].attributes[ConfigKey.CUSTOM_HEARTBEAT_ID];
- const policyId2 = `${id}-${testPolicyId}`;
-
- const packagePolicy2 = apiResponsePolicy2.body.items.find(
- (pkgPolicy: PackagePolicy) => pkgPolicy.id === policyId2
- );
-
- comparePolicies(
- packagePolicy2,
- getTestProjectSyntheticsPolicy({
- inputs: { enabled: { value: false, type: 'bool' } },
- name: 'check if title is present-Test private location 0',
- id: id2,
- configId: configId2,
- locationName: 'Test private location 0',
- })
- );
- } finally {
- await deleteMonitor(projectMonitors.monitors[0].id, projectMonitors.project);
-
- const apiResponsePolicy2 = await supertest.get(
- '/api/fleet/package_policies?page=1&perPage=2000&kuery=ingest-package-policies.package.name%3A%20synthetics'
- );
- expect(apiResponsePolicy2.body.items.length).eql(0);
- }
- });
-
- it('handles location formatting for both private and public locations', async () => {
- try {
- await supertest
- .put(API_URLS.SYNTHETICS_MONITORS_PROJECT_LEGACY)
- .set('kbn-xsrf', 'true')
- .send({
- ...projectMonitors,
- monitors: [
- { ...projectMonitors.monitors[0], privateLocations: ['Test private location 0'] },
- ],
- });
-
- const updatedMonitorsResponse = await Promise.all(
- projectMonitors.monitors.map((monitor) => {
- return supertest
- .get(API_URLS.SYNTHETICS_MONITORS)
- .query({ filter: `${syntheticsMonitorType}.attributes.journey_id: ${monitor.id}` })
- .set('kbn-xsrf', 'true')
- .expect(200);
- })
- );
-
- updatedMonitorsResponse.forEach((response) => {
- expect(response.body.monitors[0].attributes.locations).eql([
- {
- id: 'localhost',
- label: 'Local Synthetics Service',
- geo: { lat: 0, lon: 0 },
- isServiceManaged: true,
- },
- {
- label: 'Test private location 0',
- isServiceManaged: false,
- id: testPolicyId,
- geo: {
- lat: '',
- lon: '',
- },
- },
- ]);
- });
- } finally {
- await Promise.all([
- projectMonitors.monitors.map((monitor) => {
- return deleteMonitor(monitor.id, projectMonitors.project);
- }),
- ]);
- }
- });
- });
-}
-
-/**
- * Borrowed from AIOPS test code: https://github.com/elastic/kibana/blob/23a7ac2c2e2b1f64daa17b914e86989b1fde750c/x-pack/test/api_integration/apis/aiops/explain_log_rate_spikes.ts
- * Receives a stream and parses the messages until the stream closes.
- */
-async function* parseStream(stream: NodeJS.ReadableStream) {
- let partial = '';
-
- try {
- for await (const value of stream) {
- const full = `${partial}${value}`;
- const parts = full.split('\n');
- const last = parts.pop();
-
- partial = last ?? '';
-
- const event = parts.map((p) => JSON.parse(p));
-
- for (const events of event) {
- yield events;
- }
- }
- } catch (error) {
- yield { type: 'error', payload: error.toString() };
- }
-}
-
-/**
- * Helper function to process the results of the module's stream parsing helper function.
- */
-async function getMessages(stream: NodeJS.ReadableStream | null) {
- if (stream === null) return [];
- const data: any[] = [];
- for await (const action of parseStream(stream)) {
- data.push(action);
- }
- return data;
-}
-
-/**
- * This type is intended to highlight any break between shared parameter contracts defined in
- * the module's streaming endpoint helper functions.
- */
-type StreamApiFunction = (
- url: string,
- body?: BodyInit,
- extraHeaders?: HeadersInit,
- method?: string
-) => T;
-
-/**
- * This helps the test file have DRY code when it comes to calling
- * the same streaming endpoint over and over by defining some selective defaults.
- */
-export const parseStreamApiResponse: StreamApiFunction> = async (
- url: string,
- body?: BodyInit,
- extraHeaders?: HeadersInit,
- method = 'PUT'
-) => {
- const streamResponse = await callStreamApi(url, body, extraHeaders, method);
- return getMessages(streamResponse.body);
-};
-
-/**
- * This helps the test file have DRY code when it comes to calling
- * the same streaming endpoint over and over by defining some selective defaults.
- */
-const callStreamApi: StreamApiFunction> = async (
- url: string,
- body?: BodyInit,
- extraHeaders?: HeadersInit,
- method = 'PUT'
-) => {
- return fetch(url, {
- method,
- headers: {
- 'Content-Type': 'application/json',
- 'kbn-xsrf': 'stream',
- ...extraHeaders,
- },
- body,
- });
-};
diff --git a/x-pack/test/api_integration/apis/synthetics/delete_monitor_project.ts b/x-pack/test/api_integration/apis/synthetics/delete_monitor_project.ts
index 13bcab980d248d..d4fe302b956a20 100644
--- a/x-pack/test/api_integration/apis/synthetics/delete_monitor_project.ts
+++ b/x-pack/test/api_integration/apis/synthetics/delete_monitor_project.ts
@@ -5,7 +5,6 @@
* 2.0.
*/
import { v4 as uuidv4 } from 'uuid';
-import { format as formatUrl } from 'url';
import { ConfigKey, ProjectMonitorsRequest } from '@kbn/synthetics-plugin/common/runtime_types';
import { INSUFFICIENT_FLEET_PERMISSIONS } from '@kbn/synthetics-plugin/server/synthetics_service/project_monitor/project_monitor_formatter';
import { REQUEST_TOO_LARGE } from '@kbn/synthetics-plugin/server/routes/monitor_cruds/delete_monitor_project';
@@ -16,19 +15,15 @@ import expect from '@kbn/expect';
import { FtrProviderContext } from '../../ftr_provider_context';
import { getFixtureJson } from '../uptime/rest/helper/get_fixture_json';
import { PrivateLocationTestService } from './services/private_location_test_service';
-import { parseStreamApiResponse } from './add_monitor_project_legacy';
export default function ({ getService }: FtrProviderContext) {
describe('DeleteProjectMonitors', function () {
this.tags('skipCloud');
const supertest = getService('supertest');
- const config = getService('config');
const supertestWithoutAuth = getService('supertestWithoutAuth');
const security = getService('security');
- const kibanaServerUrl = formatUrl(config.get('servers.kibana'));
const kibanaServer = getService('kibanaServer');
- const projectMonitorEndpoint = kibanaServerUrl + API_URLS.SYNTHETICS_MONITORS_PROJECT_LEGACY;
let projectMonitors: ProjectMonitorsRequest;
@@ -70,16 +65,20 @@ export default function ({ getService }: FtrProviderContext) {
name: `test-name-${i}`,
});
}
+ const monitorsToDelete = monitors.map((monitor) => monitor.id);
try {
- await parseStreamApiResponse(
- projectMonitorEndpoint,
- JSON.stringify({
- ...projectMonitors,
- project,
- monitors,
- })
- );
+ await supertest
+ .put(API_URLS.SYNTHETICS_MONITORS_PROJECT_UPDATE.replace('{projectName}', project))
+ .set('kbn-xsrf', 'true')
+ .send({ monitors: monitors.slice(0, 250) })
+ .expect(200);
+
+ await supertest
+ .put(API_URLS.SYNTHETICS_MONITORS_PROJECT_UPDATE.replace('{projectName}', project))
+ .set('kbn-xsrf', 'true')
+ .send({ monitors: monitors.slice(250, 251) })
+ .expect(200);
const savedObjectsResponse = await supertest
.get(API_URLS.SYNTHETICS_MONITORS)
@@ -89,7 +88,6 @@ export default function ({ getService }: FtrProviderContext) {
.set('kbn-xsrf', 'true');
const { total } = savedObjectsResponse.body;
expect(total).to.eql(251);
- const monitorsToDelete = monitors.map((monitor) => monitor.id);
const response = await supertest
.delete(API_URLS.SYNTHETICS_MONITORS_PROJECT_DELETE.replace('{projectName}', project))
@@ -99,15 +97,16 @@ export default function ({ getService }: FtrProviderContext) {
const { message } = response.body;
expect(message).to.eql(REQUEST_TOO_LARGE);
} finally {
- await parseStreamApiResponse(
- projectMonitorEndpoint,
- JSON.stringify({
- ...projectMonitors,
- project,
- keep_stale: false,
- monitors: [],
- })
- );
+ await supertest
+ .delete(API_URLS.SYNTHETICS_MONITORS_PROJECT_DELETE.replace('{projectName}', project))
+ .set('kbn-xsrf', 'true')
+ .send({ monitors: monitorsToDelete.slice(0, 250) })
+ .expect(200);
+ await supertest
+ .delete(API_URLS.SYNTHETICS_MONITORS_PROJECT_DELETE.replace('{projectName}', project))
+ .set('kbn-xsrf', 'true')
+ .send({ monitors: monitorsToDelete.slice(250, 251) })
+ .expect(200);
}
});
@@ -123,14 +122,11 @@ export default function ({ getService }: FtrProviderContext) {
const project = 'test-brower-suite';
try {
- await parseStreamApiResponse(
- projectMonitorEndpoint,
- JSON.stringify({
- ...projectMonitors,
- project,
- monitors,
- })
- );
+ await supertest
+ .put(API_URLS.SYNTHETICS_MONITORS_PROJECT_UPDATE.replace('{projectName}', project))
+ .set('kbn-xsrf', 'true')
+ .send({ monitors })
+ .expect(200);
const savedObjectsResponse = await supertest
.get(API_URLS.SYNTHETICS_MONITORS)
@@ -161,15 +157,12 @@ export default function ({ getService }: FtrProviderContext) {
const { total: totalAfterDeletion } = responseAfterDeletion.body;
expect(totalAfterDeletion).to.eql(1);
} finally {
- await parseStreamApiResponse(
- projectMonitorEndpoint,
- JSON.stringify({
- ...projectMonitors,
- project,
- keep_stale: false,
- monitors: [],
- })
- );
+ const monitorsToDelete = monitors.map((monitor) => monitor.id);
+ await supertest
+ .delete(API_URLS.SYNTHETICS_MONITORS_PROJECT_DELETE.replace('{projectName}', project))
+ .set('kbn-xsrf', 'true')
+ .send({ monitors: monitorsToDelete })
+ .expect(200);
}
});
@@ -179,23 +172,17 @@ export default function ({ getService }: FtrProviderContext) {
const secondProject = 'second-project';
try {
- await parseStreamApiResponse(
- projectMonitorEndpoint,
- JSON.stringify({
- ...projectMonitors,
- project,
- monitors,
- })
- );
+ await supertest
+ .put(API_URLS.SYNTHETICS_MONITORS_PROJECT_UPDATE.replace('{projectName}', project))
+ .set('kbn-xsrf', 'true')
+ .send({ monitors })
+ .expect(200);
- await parseStreamApiResponse(
- projectMonitorEndpoint,
- JSON.stringify({
- ...projectMonitors,
- project: secondProject,
- monitors,
- })
- );
+ await supertest
+ .put(API_URLS.SYNTHETICS_MONITORS_PROJECT_UPDATE.replace('{projectName}', secondProject))
+ .set('kbn-xsrf', 'true')
+ .send({ monitors })
+ .expect(200);
const savedObjectsResponse = await supertest
.get(API_URLS.SYNTHETICS_MONITORS)
@@ -244,24 +231,20 @@ export default function ({ getService }: FtrProviderContext) {
expect(totalAfterDeletion).to.eql(0);
expect(secondProjectTotalAfterDeletion).to.eql(monitors.length);
} finally {
- await parseStreamApiResponse(
- projectMonitorEndpoint,
- JSON.stringify({
- ...projectMonitors,
- project,
- keep_stale: false,
- monitors: [],
- })
- );
- await parseStreamApiResponse(
- projectMonitorEndpoint,
- JSON.stringify({
- ...projectMonitors,
- project: secondProject,
- keep_stale: false,
- monitors: [],
- })
- );
+ const monitorsToDelete = monitors.map((monitor) => monitor.id);
+
+ await supertest
+ .delete(API_URLS.SYNTHETICS_MONITORS_PROJECT_DELETE.replace('{projectName}', project))
+ .set('kbn-xsrf', 'true')
+ .send({ monitors: monitorsToDelete })
+ .expect(200);
+ await supertest
+ .delete(
+ API_URLS.SYNTHETICS_MONITORS_PROJECT_DELETE.replace('{projectName}', secondProject)
+ )
+ .set('kbn-xsrf', 'true')
+ .send({ monitors: monitorsToDelete })
+ .expect(200);
}
});
@@ -270,27 +253,25 @@ export default function ({ getService }: FtrProviderContext) {
const project = 'test-brower-suite';
const SPACE_ID = `test-space-${uuidv4()}`;
const SPACE_NAME = `test-space-name ${uuidv4()}`;
- const secondSpaceProjectMonitorApiRoute = `${kibanaServerUrl}/s/${SPACE_ID}${API_URLS.SYNTHETICS_MONITORS_PROJECT_LEGACY}`;
await kibanaServer.spaces.create({ id: SPACE_ID, name: SPACE_NAME });
try {
- await parseStreamApiResponse(
- projectMonitorEndpoint,
- JSON.stringify({
- ...projectMonitors,
- project,
- monitors,
- })
- );
+ await supertest
+ .put(API_URLS.SYNTHETICS_MONITORS_PROJECT_UPDATE.replace('{projectName}', project))
+ .set('kbn-xsrf', 'true')
+ .send({ monitors })
+ .expect(200);
- await parseStreamApiResponse(
- secondSpaceProjectMonitorApiRoute,
- JSON.stringify({
- ...projectMonitors,
- project,
- monitors,
- })
- );
+ await supertest
+ .put(
+ `/s/${SPACE_ID}${API_URLS.SYNTHETICS_MONITORS_PROJECT_UPDATE.replace(
+ '{projectName}',
+ project
+ )}`
+ )
+ .set('kbn-xsrf', 'true')
+ .send({ monitors })
+ .expect(200);
const savedObjectsResponse = await supertest
.get(API_URLS.SYNTHETICS_MONITORS)
@@ -345,24 +326,24 @@ export default function ({ getService }: FtrProviderContext) {
expect(totalAfterDeletion).to.eql(monitors.length);
expect(secondProjectTotalAfterDeletion).to.eql(0);
} finally {
- await parseStreamApiResponse(
- projectMonitorEndpoint,
- JSON.stringify({
- ...projectMonitors,
- project,
- keep_stale: false,
- monitors: [],
- })
- );
- await parseStreamApiResponse(
- secondSpaceProjectMonitorApiRoute,
- JSON.stringify({
- ...projectMonitors,
- project,
- keep_stale: false,
- monitors: [],
- })
- );
+ const monitorsToDelete = monitors.map((monitor) => monitor.id);
+
+ await supertest
+ .delete(
+ `/s/${SPACE_ID}${API_URLS.SYNTHETICS_MONITORS_PROJECT_DELETE.replace(
+ '{projectName}',
+ project
+ )}`
+ )
+ .set('kbn-xsrf', 'true')
+ .send({ monitors: monitorsToDelete })
+ .expect(200);
+
+ await supertest
+ .delete(API_URLS.SYNTHETICS_MONITORS_PROJECT_DELETE.replace('{projectName}', project))
+ .set('kbn-xsrf', 'true')
+ .send({ monitors: monitorsToDelete })
+ .expect(200);
}
});
@@ -373,14 +354,11 @@ export default function ({ getService }: FtrProviderContext) {
const project = 'test-brower-suite';
try {
- await parseStreamApiResponse(
- projectMonitorEndpoint,
- JSON.stringify({
- ...projectMonitors,
- project,
- monitors,
- })
- );
+ await supertest
+ .put(API_URLS.SYNTHETICS_MONITORS_PROJECT_UPDATE.replace('{projectName}', project))
+ .set('kbn-xsrf', 'true')
+ .send({ monitors })
+ .expect(200);
const savedObjectsResponse = await supertest
.get(API_URLS.SYNTHETICS_MONITORS)
@@ -436,15 +414,13 @@ export default function ({ getService }: FtrProviderContext) {
);
expect(packagePolicy2).to.be(undefined);
} finally {
- await parseStreamApiResponse(
- projectMonitorEndpoint,
- JSON.stringify({
- ...projectMonitors,
- project,
- keep_stale: false,
- monitors: [],
- })
- );
+ const monitorsToDelete = monitors.map((monitor) => monitor.id);
+
+ await supertest
+ .delete(API_URLS.SYNTHETICS_MONITORS_PROJECT_DELETE.replace('{projectName}', project))
+ .set('kbn-xsrf', 'true')
+ .send({ monitors: monitorsToDelete })
+ .expect(200);
}
});
@@ -477,15 +453,11 @@ export default function ({ getService }: FtrProviderContext) {
full_name: 'a kibana user',
});
- await parseStreamApiResponse(
- projectMonitorEndpoint,
- JSON.stringify({
- ...projectMonitors,
- project,
- keep_stale: false,
- monitors: testMonitors,
- })
- );
+ await supertest
+ .put(API_URLS.SYNTHETICS_MONITORS_PROJECT_UPDATE.replace('{projectName}', project))
+ .set('kbn-xsrf', 'true')
+ .send({ monitors: testMonitors })
+ .expect(200);
const savedObjectsResponse = await supertest
.get(API_URLS.SYNTHETICS_MONITORS)
@@ -507,15 +479,11 @@ export default function ({ getService }: FtrProviderContext) {
.expect(403);
expect(message).to.eql(INSUFFICIENT_FLEET_PERMISSIONS);
} finally {
- await parseStreamApiResponse(
- projectMonitorEndpoint,
- JSON.stringify({
- ...projectMonitors,
- project,
- keep_stale: false,
- monitors: [],
- })
- );
+ await supertest
+ .delete(API_URLS.SYNTHETICS_MONITORS_PROJECT_DELETE.replace('{projectName}', project))
+ .set('kbn-xsrf', 'true')
+ .send({ monitors: monitorsToDelete })
+ .expect(200);
await security.user.delete(username);
await security.role.delete(roleName);
}
diff --git a/x-pack/test/api_integration/apis/synthetics/get_monitor_project.ts b/x-pack/test/api_integration/apis/synthetics/get_monitor_project.ts
index beefd6c5618292..f683e04cea46ba 100644
--- a/x-pack/test/api_integration/apis/synthetics/get_monitor_project.ts
+++ b/x-pack/test/api_integration/apis/synthetics/get_monitor_project.ts
@@ -6,7 +6,6 @@
*/
import { v4 as uuidv4 } from 'uuid';
import type SuperTest from 'supertest';
-import { format as formatUrl } from 'url';
import {
LegacyProjectMonitorsRequest,
ProjectMonitor,
@@ -17,16 +16,12 @@ import expect from '@kbn/expect';
import { FtrProviderContext } from '../../ftr_provider_context';
import { getFixtureJson } from '../uptime/rest/helper/get_fixture_json';
import { PrivateLocationTestService } from './services/private_location_test_service';
-import { parseStreamApiResponse } from './add_monitor_project_legacy';
export default function ({ getService }: FtrProviderContext) {
describe('GetProjectMonitors', function () {
this.tags('skipCloud');
const supertest = getService('supertest');
- const config = getService('config');
- const kibanaServerUrl = formatUrl(config.get('servers.kibana'));
- const projectMonitorEndpoint = kibanaServerUrl + API_URLS.SYNTHETICS_MONITORS_PROJECT_LEGACY;
let projectMonitors: LegacyProjectMonitorsRequest;
let httpProjectMonitors: LegacyProjectMonitorsRequest;
@@ -76,14 +71,28 @@ export default function ({ getService }: FtrProviderContext) {
}
try {
- await parseStreamApiResponse(
- projectMonitorEndpoint,
- JSON.stringify({
- ...projectMonitors,
- project,
- monitors,
+ await supertest
+ .put(API_URLS.SYNTHETICS_MONITORS_PROJECT_UPDATE.replace('{projectName}', project))
+ .set('kbn-xsrf', 'true')
+ .send({
+ monitors: monitors.slice(0, 250),
})
- );
+ .expect(200);
+
+ await supertest
+ .put(API_URLS.SYNTHETICS_MONITORS_PROJECT_UPDATE.replace('{projectName}', project))
+ .set('kbn-xsrf', 'true')
+ .send({
+ monitors: monitors.slice(250, 500),
+ })
+ .expect(200);
+ await supertest
+ .put(API_URLS.SYNTHETICS_MONITORS_PROJECT_UPDATE.replace('{projectName}', project))
+ .set('kbn-xsrf', 'true')
+ .send({
+ monitors: monitors.slice(500, 600),
+ })
+ .expect(200);
const firstPageResponse = await supertest
.get(API_URLS.SYNTHETICS_MONITORS_PROJECT.replace('{projectName}', project))
@@ -108,15 +117,23 @@ export default function ({ getService }: FtrProviderContext) {
expect(secondPageMonitors.length).to.eql(100);
checkFields([...firstPageMonitors, ...secondPageMonitors], monitors);
} finally {
- await parseStreamApiResponse(
- projectMonitorEndpoint,
- JSON.stringify({
- ...projectMonitors,
- project,
- keep_stale: false,
- monitors: [],
- })
- );
+ const monitorsToDelete = monitors.map((monitor) => monitor.id);
+
+ await supertest
+ .delete(API_URLS.SYNTHETICS_MONITORS_PROJECT_DELETE.replace('{projectName}', project))
+ .set('kbn-xsrf', 'true')
+ .send({ monitors: monitorsToDelete.slice(0, 250) })
+ .expect(200);
+ await supertest
+ .delete(API_URLS.SYNTHETICS_MONITORS_PROJECT_DELETE.replace('{projectName}', project))
+ .set('kbn-xsrf', 'true')
+ .send({ monitors: monitorsToDelete.slice(250, 500) })
+ .expect(200);
+ await supertest
+ .delete(API_URLS.SYNTHETICS_MONITORS_PROJECT_DELETE.replace('{projectName}', project))
+ .set('kbn-xsrf', 'true')
+ .send({ monitors: monitorsToDelete.slice(500, 600) })
+ .expect(200);
}
});
@@ -132,14 +149,28 @@ export default function ({ getService }: FtrProviderContext) {
}
try {
- await parseStreamApiResponse(
- projectMonitorEndpoint,
- JSON.stringify({
- ...projectMonitors,
- project,
- monitors,
+ await supertest
+ .put(API_URLS.SYNTHETICS_MONITORS_PROJECT_UPDATE.replace('{projectName}', project))
+ .set('kbn-xsrf', 'true')
+ .send({
+ monitors: monitors.slice(0, 250),
})
- );
+ .expect(200);
+
+ await supertest
+ .put(API_URLS.SYNTHETICS_MONITORS_PROJECT_UPDATE.replace('{projectName}', project))
+ .set('kbn-xsrf', 'true')
+ .send({
+ monitors: monitors.slice(250, 500),
+ })
+ .expect(200);
+ await supertest
+ .put(API_URLS.SYNTHETICS_MONITORS_PROJECT_UPDATE.replace('{projectName}', project))
+ .set('kbn-xsrf', 'true')
+ .send({
+ monitors: monitors.slice(500, 600),
+ })
+ .expect(200);
const firstPageResponse = await supertest
.get(API_URLS.SYNTHETICS_MONITORS_PROJECT.replace('{projectName}', project))
@@ -168,15 +199,23 @@ export default function ({ getService }: FtrProviderContext) {
expect(secondPageProjectMonitors.length).to.eql(100);
checkFields([...firstPageProjectMonitors, ...secondPageProjectMonitors], monitors);
} finally {
- await parseStreamApiResponse(
- projectMonitorEndpoint,
- JSON.stringify({
- ...projectMonitors,
- project,
- keep_stale: false,
- monitors: [],
- })
- );
+ const monitorsToDelete = monitors.map((monitor) => monitor.id);
+
+ await supertest
+ .delete(API_URLS.SYNTHETICS_MONITORS_PROJECT_DELETE.replace('{projectName}', project))
+ .set('kbn-xsrf', 'true')
+ .send({ monitors: monitorsToDelete.slice(0, 250) })
+ .expect(200);
+ await supertest
+ .delete(API_URLS.SYNTHETICS_MONITORS_PROJECT_DELETE.replace('{projectName}', project))
+ .set('kbn-xsrf', 'true')
+ .send({ monitors: monitorsToDelete.slice(250, 500) })
+ .expect(200);
+ await supertest
+ .delete(API_URLS.SYNTHETICS_MONITORS_PROJECT_DELETE.replace('{projectName}', project))
+ .set('kbn-xsrf', 'true')
+ .send({ monitors: monitorsToDelete.slice(500, 600) })
+ .expect(200);
}
});
@@ -192,14 +231,28 @@ export default function ({ getService }: FtrProviderContext) {
}
try {
- await parseStreamApiResponse(
- projectMonitorEndpoint,
- JSON.stringify({
- ...projectMonitors,
- project,
- monitors,
+ await supertest
+ .put(API_URLS.SYNTHETICS_MONITORS_PROJECT_UPDATE.replace('{projectName}', project))
+ .set('kbn-xsrf', 'true')
+ .send({
+ monitors: monitors.slice(0, 250),
+ })
+ .expect(200);
+
+ await supertest
+ .put(API_URLS.SYNTHETICS_MONITORS_PROJECT_UPDATE.replace('{projectName}', project))
+ .set('kbn-xsrf', 'true')
+ .send({
+ monitors: monitors.slice(250, 500),
})
- );
+ .expect(200);
+ await supertest
+ .put(API_URLS.SYNTHETICS_MONITORS_PROJECT_UPDATE.replace('{projectName}', project))
+ .set('kbn-xsrf', 'true')
+ .send({
+ monitors: monitors.slice(500, 600),
+ })
+ .expect(200);
const firstPageResponse = await supertest
.get(API_URLS.SYNTHETICS_MONITORS_PROJECT.replace('{projectName}', project))
@@ -228,15 +281,23 @@ export default function ({ getService }: FtrProviderContext) {
expect(secondPageProjectMonitors.length).to.eql(100);
checkFields([...firstPageProjectMonitors, ...secondPageProjectMonitors], monitors);
} finally {
- await parseStreamApiResponse(
- projectMonitorEndpoint,
- JSON.stringify({
- ...projectMonitors,
- project,
- keep_stale: false,
- monitors: [],
- })
- );
+ const monitorsToDelete = monitors.map((monitor) => monitor.id);
+
+ await supertest
+ .delete(API_URLS.SYNTHETICS_MONITORS_PROJECT_DELETE.replace('{projectName}', project))
+ .set('kbn-xsrf', 'true')
+ .send({ monitors: monitorsToDelete.slice(0, 250) })
+ .expect(200);
+ await supertest
+ .delete(API_URLS.SYNTHETICS_MONITORS_PROJECT_DELETE.replace('{projectName}', project))
+ .set('kbn-xsrf', 'true')
+ .send({ monitors: monitorsToDelete.slice(250, 500) })
+ .expect(200);
+ await supertest
+ .delete(API_URLS.SYNTHETICS_MONITORS_PROJECT_DELETE.replace('{projectName}', project))
+ .set('kbn-xsrf', 'true')
+ .send({ monitors: monitorsToDelete.slice(500, 600) })
+ .expect(200);
}
});
@@ -252,15 +313,28 @@ export default function ({ getService }: FtrProviderContext) {
}
try {
- await parseStreamApiResponse(
- projectMonitorEndpoint,
- JSON.stringify({
- ...projectMonitors,
- project,
- monitors,
+ await supertest
+ .put(API_URLS.SYNTHETICS_MONITORS_PROJECT_UPDATE.replace('{projectName}', project))
+ .set('kbn-xsrf', 'true')
+ .send({
+ monitors: monitors.slice(0, 250),
})
- );
+ .expect(200);
+ await supertest
+ .put(API_URLS.SYNTHETICS_MONITORS_PROJECT_UPDATE.replace('{projectName}', project))
+ .set('kbn-xsrf', 'true')
+ .send({
+ monitors: monitors.slice(250, 500),
+ })
+ .expect(200);
+ await supertest
+ .put(API_URLS.SYNTHETICS_MONITORS_PROJECT_UPDATE.replace('{projectName}', project))
+ .set('kbn-xsrf', 'true')
+ .send({
+ monitors: monitors.slice(500, 600),
+ })
+ .expect(200);
const firstPageResponse = await supertest
.get(API_URLS.SYNTHETICS_MONITORS_PROJECT.replace('{projectName}', project))
.set('kbn-xsrf', 'true')
@@ -289,15 +363,23 @@ export default function ({ getService }: FtrProviderContext) {
checkFields([...firstPageProjectMonitors, ...secondPageProjectMonitors], monitors);
} finally {
- await parseStreamApiResponse(
- projectMonitorEndpoint,
- JSON.stringify({
- ...projectMonitors,
- project,
- keep_stale: false,
- monitors: [],
- })
- );
+ const monitorsToDelete = monitors.map((monitor) => monitor.id);
+
+ await supertest
+ .delete(API_URLS.SYNTHETICS_MONITORS_PROJECT_DELETE.replace('{projectName}', project))
+ .set('kbn-xsrf', 'true')
+ .send({ monitors: monitorsToDelete.slice(0, 250) })
+ .expect(200);
+ await supertest
+ .delete(API_URLS.SYNTHETICS_MONITORS_PROJECT_DELETE.replace('{projectName}', project))
+ .set('kbn-xsrf', 'true')
+ .send({ monitors: monitorsToDelete.slice(250, 500) })
+ .expect(200);
+ await supertest
+ .delete(API_URLS.SYNTHETICS_MONITORS_PROJECT_DELETE.replace('{projectName}', project))
+ .set('kbn-xsrf', 'true')
+ .send({ monitors: monitorsToDelete.slice(500, 600) })
+ .expect(200);
}
});
@@ -313,15 +395,28 @@ export default function ({ getService }: FtrProviderContext) {
}
try {
- await parseStreamApiResponse(
- projectMonitorEndpoint,
- JSON.stringify({
- ...projectMonitors,
- project: projectName,
- keep_stale: false,
- monitors,
+ await supertest
+ .put(API_URLS.SYNTHETICS_MONITORS_PROJECT_UPDATE.replace('{projectName}', projectName))
+ .set('kbn-xsrf', 'true')
+ .send({
+ monitors: monitors.slice(0, 250),
})
- );
+ .expect(200);
+
+ await supertest
+ .put(API_URLS.SYNTHETICS_MONITORS_PROJECT_UPDATE.replace('{projectName}', projectName))
+ .set('kbn-xsrf', 'true')
+ .send({
+ monitors: monitors.slice(250, 500),
+ })
+ .expect(200);
+ await supertest
+ .put(API_URLS.SYNTHETICS_MONITORS_PROJECT_UPDATE.replace('{projectName}', projectName))
+ .set('kbn-xsrf', 'true')
+ .send({
+ monitors: monitors.slice(500, 600),
+ })
+ .expect(200);
const firstPageResponse = await supertest
.get(
@@ -355,20 +450,44 @@ export default function ({ getService }: FtrProviderContext) {
checkFields([...firstPageProjectMonitors, ...secondPageProjectMonitors], monitors);
} finally {
- await parseStreamApiResponse(
- projectMonitorEndpoint,
- JSON.stringify({
- ...projectMonitors,
- project: projectName,
- keep_stale: false,
- monitors: [],
- })
- );
+ const monitorsToDelete = monitors.map((monitor) => monitor.id);
+
+ await supertest
+ .delete(
+ API_URLS.SYNTHETICS_MONITORS_PROJECT_DELETE.replace(
+ '{projectName}',
+ encodeURI(projectName)
+ )
+ )
+ .set('kbn-xsrf', 'true')
+ .send({ monitors: monitorsToDelete.slice(0, 250) })
+ .expect(200);
+ await supertest
+ .delete(
+ API_URLS.SYNTHETICS_MONITORS_PROJECT_DELETE.replace(
+ '{projectName}',
+ encodeURI(projectName)
+ )
+ )
+ .set('kbn-xsrf', 'true')
+ .send({ monitors: monitorsToDelete.slice(250, 500) })
+ .expect(200);
+ await supertest
+ .delete(
+ API_URLS.SYNTHETICS_MONITORS_PROJECT_DELETE.replace(
+ '{projectName}',
+ encodeURI(projectName)
+ )
+ )
+ .set('kbn-xsrf', 'true')
+ .send({ monitors: monitorsToDelete.slice(500, 600) })
+ .expect(200);
}
});
it('project monitors - handles per_page parameter', async () => {
const monitors = [];
+ const project = 'test-suite';
const perPage = 250;
for (let i = 0; i < 600; i++) {
monitors.push({
@@ -379,20 +498,36 @@ export default function ({ getService }: FtrProviderContext) {
}
try {
- await parseStreamApiResponse(
- projectMonitorEndpoint,
- JSON.stringify({
- ...projectMonitors,
- monitors,
+ await supertest
+ .put(API_URLS.SYNTHETICS_MONITORS_PROJECT_UPDATE.replace('{projectName}', project))
+ .set('kbn-xsrf', 'true')
+ .send({
+ monitors: monitors.slice(0, 250),
+ })
+ .expect(200);
+
+ await supertest
+ .put(API_URLS.SYNTHETICS_MONITORS_PROJECT_UPDATE.replace('{projectName}', project))
+ .set('kbn-xsrf', 'true')
+ .send({
+ monitors: monitors.slice(250, 500),
})
- );
+ .expect(200);
+ await supertest
+ .put(API_URLS.SYNTHETICS_MONITORS_PROJECT_UPDATE.replace('{projectName}', project))
+ .set('kbn-xsrf', 'true')
+ .send({
+ monitors: monitors.slice(500, 600),
+ })
+ .expect(200);
+
let count = Number.MAX_VALUE;
let afterId;
const fullResponse: ProjectMonitorMetaData[] = [];
let page = 1;
while (count >= 250) {
const response: SuperTest.Response = await supertest
- .get(API_URLS.SYNTHETICS_MONITORS_PROJECT.replace('{projectName}', 'test-suite'))
+ .get(API_URLS.SYNTHETICS_MONITORS_PROJECT.replace('{projectName}', project))
.set('kbn-xsrf', 'true')
.query({
per_page: perPage,
@@ -417,14 +552,23 @@ export default function ({ getService }: FtrProviderContext) {
// expect(fullResponse.length).to.eql(600);
// checkFields(fullResponse, monitors);
} finally {
- await parseStreamApiResponse(
- projectMonitorEndpoint,
- JSON.stringify({
- ...projectMonitors,
- keep_stale: false,
- monitors: [],
- })
- );
+ const monitorsToDelete = monitors.map((monitor) => monitor.id);
+
+ await supertest
+ .delete(API_URLS.SYNTHETICS_MONITORS_PROJECT_DELETE.replace('{projectName}', project))
+ .set('kbn-xsrf', 'true')
+ .send({ monitors: monitorsToDelete.slice(0, 250) })
+ .expect(200);
+ await supertest
+ .delete(API_URLS.SYNTHETICS_MONITORS_PROJECT_DELETE.replace('{projectName}', project))
+ .set('kbn-xsrf', 'true')
+ .send({ monitors: monitorsToDelete.slice(250, 500) })
+ .expect(200);
+ await supertest
+ .delete(API_URLS.SYNTHETICS_MONITORS_PROJECT_DELETE.replace('{projectName}', project))
+ .set('kbn-xsrf', 'true')
+ .send({ monitors: monitorsToDelete.slice(500, 600) })
+ .expect(200);
}
});
});
diff --git a/x-pack/test/api_integration/apis/synthetics/index.ts b/x-pack/test/api_integration/apis/synthetics/index.ts
index 29603ce3481aea..19720b43fcd3cb 100644
--- a/x-pack/test/api_integration/apis/synthetics/index.ts
+++ b/x-pack/test/api_integration/apis/synthetics/index.ts
@@ -20,7 +20,6 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) {
loadTestFile(require.resolve('./get_monitor'));
loadTestFile(require.resolve('./get_monitor_overview'));
loadTestFile(require.resolve('./add_monitor'));
- loadTestFile(require.resolve('./add_monitor_project_legacy'));
loadTestFile(require.resolve('./add_monitor_project'));
loadTestFile(require.resolve('./get_monitor_project'));
loadTestFile(require.resolve('./add_monitor_private_location'));
@@ -29,5 +28,6 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) {
loadTestFile(require.resolve('./delete_monitor_project'));
loadTestFile(require.resolve('./synthetics_enablement'));
loadTestFile(require.resolve('./sync_global_params'));
+ loadTestFile(require.resolve('./add_edit_params'));
});
}
diff --git a/x-pack/test/api_integration/apis/synthetics/sync_global_params.ts b/x-pack/test/api_integration/apis/synthetics/sync_global_params.ts
index f5ff56d2ee506f..3a44de5cbcf02e 100644
--- a/x-pack/test/api_integration/apis/synthetics/sync_global_params.ts
+++ b/x-pack/test/api_integration/apis/synthetics/sync_global_params.ts
@@ -7,7 +7,7 @@
import {
ConfigKey,
HTTPFields,
- SyntheticsParam,
+ SyntheticsParamSO,
} from '@kbn/synthetics-plugin/common/runtime_types';
import { API_URLS, SYNTHETICS_API_URLS } from '@kbn/synthetics-plugin/common/constants';
import { omit } from 'lodash';
@@ -158,7 +158,7 @@ export default function ({ getService }: FtrProviderContext) {
expect(apiResponse.status).eql(200);
- apiResponse.body.data.forEach(({ attributes }: SavedObject) => {
+ apiResponse.body.data.forEach(({ attributes }: SavedObject) => {
params[attributes.key] = attributes.value;
});
});