Skip to content

Commit

Permalink
[Synthetics] Private location public API's (#169376)
Browse files Browse the repository at this point in the history
Co-authored-by: Abdul Wahab Zahid <[email protected]>
Co-authored-by: Justin Kambic <[email protected]>
  • Loading branch information
3 people authored Nov 6, 2023
1 parent ed4ef2a commit d2a54d9
Show file tree
Hide file tree
Showing 42 changed files with 586 additions and 237 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
[[create-private-location-api]]
== Create Private Location API
++++
<titleabbrev>Create Private Location</titleabbrev>
++++

Creates a private location with the following schema.

=== {api-request-title}

`POST <kibana host>:<port>/api/synthetics/private_locations`

`POST <kibana host>:<port>/s/<space_id>/api/synthetics/private_locations`

=== {api-prereq-title}

You must have `all` privileges for the *Synthetics and Uptime* feature in the *{observability}* section of the
<<kibana-feature-privileges,{kib} feature privileges>>.

[[private-location-request-body]]
==== Request body

The request body should contain the following attributes:

`label`::
(Required, string) A label for the private location.

`agentPolicyId`::
(Required, string) The ID of the agent policy associated with the private location.

`tags`::
(Optional, array of strings) An array of tags to categorize the private location.

`geo`::
(Optional, object) Geographic coordinates (WGS84) for the location. It should include the following attributes:

- `lat` (Required, number): The latitude of the location.
- `lon` (Required, number): The longitude of the location.

[[private-location-create-example]]
==== Example

Here is an example of a POST request to create a private location:

[source,sh]
--------------------------------------------------
POST /api/private_locations
{
"label": "Private Location 1",
"agentPolicyId": "abcd1234",
"tags": ["private", "testing"],
"geo": {
"lat": 40.7128,
"lon": -74.0060
}
}
--------------------------------------------------

The API returns the created private location as follows:

[source,json]
--------------------------------------------------
{
"id": "unique-location-id",
"label": "Private Location 1",
"agentPolicyId": "abcd1234",
"tags": ["private", "testing"],
"geo": {
"lat": 40.7128,
"lon": -74.0060
}
}
--------------------------------------------------

If the `agentPolicyId` is already used by an existing private location, or if the `label` already exists, the API will return a `400 Bad Request` response with a corresponding error message.
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
[[delete-private-location-api]]
== Delete Private Location API
++++
<titleabbrev>Delete Private Location</titleabbrev>
++++

Deletes a private location using the provided location ID.

=== {api-request-title}

`DELETE <kibana host>:<port>/api/synthetics/private_locations/<location_id>`

`DELETE <kibana host>:<port>/s/<space_id>/api/synthetics/private_locations/<location_id>`

=== {api-prereq-title}

You must have `all` privileges for the *Synthetics and Uptime* feature in the *{observability}* section of the
<<kibana-feature-privileges,{kib} feature privileges>>.


[[private-location-delete-params]]
==== Path Parameters

`location_id`::
(Required, string) The unique identifier of the private location to be deleted. It must be between 1 and 1024 characters.

[[private-location-delete-example]]
==== Example

Here is an example of a DELETE request to delete a private location:

[source,sh]
--------------------------------------------------
DELETE /api/private-locations/<location_id>
--------------------------------------------------

The API does not return a response body for deletion, but it will return an appropriate status code upon successful deletion.

This API will delete the private location with the specified `locationId`.

A location cannot be deleted if it has associated monitors in use. You must delete all monitors associated with the location before deleting the location.
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
[[get-private-locations-api]]
== Get Private Locations API
++++
<titleabbrev>Get Private Locations</titleabbrev>
++++

Retrieves a list of private locations or a single private location by ID.

=== {api-request-title}

`GET <kibana host>:<port>/api/synthetics/private_locations`

`GET <kibana host>:<port>/s/<space_id>/api/synthetics/private_locations`


=== {api-prereq-title}

You must have `read` privileges for the *Synthetics and Uptime* feature in the *{observability}* section of the
<<kibana-feature-privileges,{kib} feature privileges>>.

[[private-locations-list-response-example]]
==== List Response Example

The API returns a JSON array of private locations when accessing the list endpoint, with each private location having the following attributes:

- `label` (string): A label for the private location.
- `id` (string): The unique identifier of the private location.
- `agentPolicyId` (string): The ID of the agent policy associated with the private location.
- `isInvalid` (boolean): Indicates whether the location is invalid. If `true`, the location is invalid, which means the agent policy associated with the location is deleted.
- `geo` (object): Geographic coordinates for the location, including `lat` and `lon`.
- `namespace` (string): The namespace of the location, which is the same as the namespace of the agent policy associated with the location.

Here's an example list response:

[source,json]
--------------------------------------------------
[
{
"label": "Test private location",
"id": "fleet-server-policy",
"agentPolicyId": "fleet-server-policy",
"isInvalid": false,
"geo": {
"lat": 0,
"lon": 0
},
"namespace": "default"
},
{
"label": "Test private location 2",
"id": "691225b0-6ced-11ee-8f5a-376306ee85ae",
"agentPolicyId": "691225b0-6ced-11ee-8f5a-376306ee85ae",
"isInvalid": false,
"geo": {
"lat": 0,
"lon": 0
},
"namespace": "test"
}
]
--------------------------------------------------

[[private-location-by-id-response-example]]
==== Get by ID/Label Response Example

The API returns a JSON object of a single private location when accessing the endpoint with a specific `id`, with the same attributes as in the list response.

Here's an example request for a single location by ID:

[source,sh]
--------------------------------------------------
GET api/synthetics/private_locations/<location_id>
--------------------------------------------------

or by label:

[source,sh]
--------------------------------------------------
GET api/synthetics/private_locations/<Location label>
--------------------------------------------------

Here's an example response object:

[source,json]
--------------------------------------------------
{
"label": "Test private location",
"id": "test-private-location-id",
"agentPolicyId": "test-private-location-id",
"isServiceManaged": false,
"isInvalid": false,
"geo": {
"lat": 0,
"lon": 0
},
"namespace": "default"
}
--------------------------------------------------
9 changes: 9 additions & 0 deletions docs/api/synthetics/synthetics-api.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,17 @@ The following APIs are available for Synthetics.

* <<delete-parameters-api, Delete Parameter API>> to delete a parameter.

* <<create-private-location-api, Create Private Location API>> to create a private location

* <<delete-private-location-api, Delete Private Location API>> to delete a private location

* <<get-private-locations-api, Get Private Locations API>> to get a list of private locations

include::params/add-param.asciidoc[leveloffset=+1]
include::params/get-params.asciidoc[leveloffset=+1]
include::params/edit-param.asciidoc[leveloffset=+1]
include::params/delete-param.asciidoc[leveloffset=+1]
include::private-locations/create-private-location.asciidoc[leveloffset=+1]
include::private-locations/delete-private-location.asciidoc[leveloffset=+1]
include::private-locations/get-private-locations.asciidoc[leveloffset=+1]

Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

export enum SYNTHETICS_API_URLS {
// public apis
PRIVATE_LOCATIONS = `/api/synthetics/private_locations`,
PARAMS = `/api/synthetics/params`,

// Service end points
Expand All @@ -28,7 +29,6 @@ export enum SYNTHETICS_API_URLS {
OVERVIEW_STATUS = `/internal/synthetics/overview_status`,
INDEX_SIZE = `/internal/synthetics/index_size`,
AGENT_POLICIES = `/internal/synthetics/agent_policies`,
PRIVATE_LOCATIONS = `/internal/synthetics/private_locations`,
PRIVATE_LOCATIONS_MONITORS = `/internal/synthetics/private_locations/monitors`,
SYNC_GLOBAL_PARAMS = `/internal/synthetics/sync_global_params`,
ENABLE_DEFAULT_ALERTING = `/internal/synthetics/enable_default_alerting`,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,11 @@ export type DynamicSettings = t.TypeOf<typeof DynamicSettingsCodec>;
export type DefaultEmail = t.TypeOf<typeof DefaultEmailCodec>;
export type DynamicSettingsSaveResponse = t.TypeOf<typeof DynamicSettingsSaveCodec>;

export const LocationMonitorsType = t.type({
status: t.number,
payload: t.array(
t.type({
id: t.string,
count: t.number,
})
),
});
export const LocationMonitorsType = t.array(
t.type({
id: t.string,
count: t.number,
})
);

export type LocationMonitorsResponse = t.TypeOf<typeof LocationMonitorsType>;
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ export const PrivateLocationCodec = t.intersection([
label: t.string,
id: t.string,
agentPolicyId: t.string,
concurrentMonitors: t.number,
}),
t.partial({
isServiceManaged: t.boolean,
Expand All @@ -26,8 +25,6 @@ export const PrivateLocationCodec = t.intersection([
}),
]);

export const SyntheticsPrivateLocationsType = t.type({
locations: t.array(PrivateLocationCodec),
});
export const SyntheticsPrivateLocationsType = t.array(PrivateLocationCodec);
export type PrivateLocation = t.TypeOf<typeof PrivateLocationCodec>;
export type SyntheticsPrivateLocations = t.TypeOf<typeof SyntheticsPrivateLocationsType>;
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,8 @@ import {
cleanMonitorListState,
} from '../../state';
import { MONITOR_ADD_ROUTE } from '../../../../../common/constants/ui';
import { PrivateLocation } from '../../../../../common/runtime_types';
import { SimpleMonitorForm } from './simple_monitor_form';
import { AddLocationFlyout } from '../settings/private_locations/add_location_flyout';
import { AddLocationFlyout, NewLocation } from '../settings/private_locations/add_location_flyout';

export const GettingStartedPage = () => {
const dispatch = useDispatch();
Expand Down Expand Up @@ -111,7 +110,7 @@ export const GettingStartedOnPrem = () => {

const { onSubmit, privateLocations, loading } = usePrivateLocationsAPI();

const handleSubmit = (formData: PrivateLocation) => {
const handleSubmit = (formData: NewLocation) => {
onSubmit(formData);
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,16 @@ import { PrivateLocation } from '../../../../../../common/runtime_types';
import { LocationForm } from './location_form';
import { ManageEmptyState } from './manage_empty_state';

export type NewLocation = Omit<PrivateLocation, 'id'>;

export const AddLocationFlyout = ({
onSubmit,
setIsOpen,
privateLocations,
isLoading,
}: {
isLoading: boolean;
onSubmit: (val: PrivateLocation) => void;
onSubmit: (val: NewLocation) => void;
setIsOpen: (val: boolean) => void;
privateLocations: PrivateLocation[];
}) => {
Expand All @@ -44,12 +46,10 @@ export const AddLocationFlyout = ({
defaultValues: {
label: '',
agentPolicyId: '',
id: '',
geo: {
lat: 0,
lon: 0,
},
concurrentMonitors: 1,
},
});

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* 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 React from 'react';
import { i18n } from '@kbn/i18n';
import { EuiButtonIcon, EuiCopy, EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui';

export const CopyName = ({ text }: { text: string }) => {
return (
<EuiFlexGroup gutterSize="s" alignItems="center">
<EuiFlexItem grow={false}>
<EuiText>{text}</EuiText>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiCopy textToCopy={text}>
{(copy) => (
<EuiButtonIcon
data-test-subj="syntheticsCopyNameButton"
color="text"
iconType="copy"
onClick={copy}
aria-label={i18n.translate('xpack.synthetics.copyName.copyNameButtonIconLabel', {
defaultMessage: 'Copy name',
})}
/>
)}
</EuiCopy>
</EuiFlexItem>
</EuiFlexGroup>
);
};
Loading

0 comments on commit d2a54d9

Please sign in to comment.