IAnonymousPaths
| APIs for denoting certain paths for not requiring authentication |
| [basePath](./kibana-plugin-public.httpservicebase.basepath.md) | IBasePath
| APIs for manipulating the basePath on URL segments. |
| [delete](./kibana-plugin-public.httpservicebase.delete.md) | HttpHandler
| Makes an HTTP request with the DELETE method. See [HttpHandler](./kibana-plugin-public.httphandler.md) for options. |
| [fetch](./kibana-plugin-public.httpservicebase.fetch.md) | HttpHandler
| Makes an HTTP request. Defaults to a GET request unless overriden. See [HttpHandler](./kibana-plugin-public.httphandler.md) for options. |
diff --git a/docs/development/core/public/kibana-plugin-public.ianonymouspaths.isanonymous.md b/docs/development/core/public/kibana-plugin-public.ianonymouspaths.isanonymous.md
new file mode 100644
index 0000000000000..92a87668b6ef0
--- /dev/null
+++ b/docs/development/core/public/kibana-plugin-public.ianonymouspaths.isanonymous.md
@@ -0,0 +1,24 @@
+
+
+[Home](./index.md) > [kibana-plugin-public](./kibana-plugin-public.md) > [IAnonymousPaths](./kibana-plugin-public.ianonymouspaths.md) > [isAnonymous](./kibana-plugin-public.ianonymouspaths.isanonymous.md)
+
+## IAnonymousPaths.isAnonymous() method
+
+Determines whether the provided path doesn't require authentication
+
+Signature:
+
+```typescript
+isAnonymous(path: string): boolean;
+```
+
+## Parameters
+
+| Parameter | Type | Description |
+| --- | --- | --- |
+| path | string
| |
+
+Returns:
+
+`boolean`
+
diff --git a/docs/development/core/public/kibana-plugin-public.ianonymouspaths.md b/docs/development/core/public/kibana-plugin-public.ianonymouspaths.md
new file mode 100644
index 0000000000000..3e5caf49695c2
--- /dev/null
+++ b/docs/development/core/public/kibana-plugin-public.ianonymouspaths.md
@@ -0,0 +1,21 @@
+
+
+[Home](./index.md) > [kibana-plugin-public](./kibana-plugin-public.md) > [IAnonymousPaths](./kibana-plugin-public.ianonymouspaths.md)
+
+## IAnonymousPaths interface
+
+APIs for denoting paths as not requiring authentication
+
+Signature:
+
+```typescript
+export interface IAnonymousPaths
+```
+
+## Methods
+
+| Method | Description |
+| --- | --- |
+| [isAnonymous(path)](./kibana-plugin-public.ianonymouspaths.isanonymous.md) | Determines whether the provided path doesn't require authentication |
+| [register(path)](./kibana-plugin-public.ianonymouspaths.register.md) | Register path
as not requiring authentication |
+
diff --git a/docs/development/core/public/kibana-plugin-public.ianonymouspaths.register.md b/docs/development/core/public/kibana-plugin-public.ianonymouspaths.register.md
new file mode 100644
index 0000000000000..88c615da05155
--- /dev/null
+++ b/docs/development/core/public/kibana-plugin-public.ianonymouspaths.register.md
@@ -0,0 +1,24 @@
+
+
+[Home](./index.md) > [kibana-plugin-public](./kibana-plugin-public.md) > [IAnonymousPaths](./kibana-plugin-public.ianonymouspaths.md) > [register](./kibana-plugin-public.ianonymouspaths.register.md)
+
+## IAnonymousPaths.register() method
+
+Register `path` as not requiring authentication
+
+Signature:
+
+```typescript
+register(path: string): void;
+```
+
+## Parameters
+
+| Parameter | Type | Description |
+| --- | --- | --- |
+| path | string
| |
+
+Returns:
+
+`void`
+
diff --git a/docs/development/core/public/kibana-plugin-public.md b/docs/development/core/public/kibana-plugin-public.md
index e787621c3aaf9..57ab8bedde95e 100644
--- a/docs/development/core/public/kibana-plugin-public.md
+++ b/docs/development/core/public/kibana-plugin-public.md
@@ -57,6 +57,7 @@ The plugin integrates with the core system via lifecycle events: `setup`
| [HttpResponse](./kibana-plugin-public.httpresponse.md) | |
| [HttpServiceBase](./kibana-plugin-public.httpservicebase.md) | |
| [I18nStart](./kibana-plugin-public.i18nstart.md) | I18nStart.Context is required by any localizable React component from @kbn/i18n and @elastic/eui packages and is supposed to be used as the topmost component for any i18n-compatible React tree. |
+| [IAnonymousPaths](./kibana-plugin-public.ianonymouspaths.md) | APIs for denoting paths as not requiring authentication |
| [IBasePath](./kibana-plugin-public.ibasepath.md) | APIs for manipulating the basePath on URL segments. |
| [IContextContainer](./kibana-plugin-public.icontextcontainer.md) | An object that handles registration of context providers and configuring handlers with context. |
| [IHttpFetchError](./kibana-plugin-public.ihttpfetcherror.md) | |
diff --git a/docs/maps/heatmap-layer.asciidoc b/docs/maps/heatmap-layer.asciidoc
index 9d456c59b69ef..77b6d929a931c 100644
--- a/docs/maps/heatmap-layer.asciidoc
+++ b/docs/maps/heatmap-layer.asciidoc
@@ -13,6 +13,6 @@ You can create a heat map layer from the following data source:
Set *Show as* to *heat map*.
The index must contain at least one field mapped as {ref}/geo-point.html[geo_point].
-NOTE: Only count and sum metric aggregations are available with the grid aggregation source and heat map layers.
-Mean, median, min, and max are turned off because the heat map will blend nearby values.
+NOTE: Only count, sum, unique count metric aggregations are available with the grid aggregation source and heat map layers.
+Average, min, and max are turned off because the heat map will blend nearby values.
Blending two average values would make the cluster more prominent, even though it just might literally mean that these nearby areas are average.
diff --git a/src/core/public/http/anonymous_paths.test.ts b/src/core/public/http/anonymous_paths.test.ts
new file mode 100644
index 0000000000000..bf9212f625f1e
--- /dev/null
+++ b/src/core/public/http/anonymous_paths.test.ts
@@ -0,0 +1,107 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { AnonymousPaths } from './anonymous_paths';
+import { BasePath } from './base_path_service';
+
+describe('#register', () => {
+ it(`allows paths that don't start with /`, () => {
+ const basePath = new BasePath('/foo');
+ const anonymousPaths = new AnonymousPaths(basePath);
+ anonymousPaths.register('bar');
+ });
+
+ it(`allows paths that end with '/'`, () => {
+ const basePath = new BasePath('/foo');
+ const anonymousPaths = new AnonymousPaths(basePath);
+ anonymousPaths.register('/bar/');
+ });
+});
+
+describe('#isAnonymous', () => {
+ it('returns true for registered paths', () => {
+ const basePath = new BasePath('/foo');
+ const anonymousPaths = new AnonymousPaths(basePath);
+ anonymousPaths.register('/bar');
+ expect(anonymousPaths.isAnonymous('/foo/bar')).toBe(true);
+ });
+
+ it('returns true for paths registered with a trailing slash, but call "isAnonymous" with no trailing slash', () => {
+ const basePath = new BasePath('/foo');
+ const anonymousPaths = new AnonymousPaths(basePath);
+ anonymousPaths.register('/bar/');
+ expect(anonymousPaths.isAnonymous('/foo/bar')).toBe(true);
+ });
+
+ it('returns true for paths registered without a trailing slash, but call "isAnonymous" with a trailing slash', () => {
+ const basePath = new BasePath('/foo');
+ const anonymousPaths = new AnonymousPaths(basePath);
+ anonymousPaths.register('/bar');
+ expect(anonymousPaths.isAnonymous('/foo/bar/')).toBe(true);
+ });
+
+ it('returns true for paths registered without a starting slash', () => {
+ const basePath = new BasePath('/foo');
+ const anonymousPaths = new AnonymousPaths(basePath);
+ anonymousPaths.register('bar');
+ expect(anonymousPaths.isAnonymous('/foo/bar')).toBe(true);
+ });
+
+ it('returns true for paths registered with a starting slash', () => {
+ const basePath = new BasePath('/foo');
+ const anonymousPaths = new AnonymousPaths(basePath);
+ anonymousPaths.register('/bar');
+ expect(anonymousPaths.isAnonymous('/foo/bar')).toBe(true);
+ });
+
+ it('when there is no basePath and calling "isAnonymous" without a starting slash, returns true for paths registered with a starting slash', () => {
+ const basePath = new BasePath('/');
+ const anonymousPaths = new AnonymousPaths(basePath);
+ anonymousPaths.register('/bar');
+ expect(anonymousPaths.isAnonymous('bar')).toBe(true);
+ });
+
+ it('when there is no basePath and calling "isAnonymous" with a starting slash, returns true for paths registered with a starting slash', () => {
+ const basePath = new BasePath('/');
+ const anonymousPaths = new AnonymousPaths(basePath);
+ anonymousPaths.register('/bar');
+ expect(anonymousPaths.isAnonymous('/bar')).toBe(true);
+ });
+
+ it('returns true for paths whose capitalization is different', () => {
+ const basePath = new BasePath('/foo');
+ const anonymousPaths = new AnonymousPaths(basePath);
+ anonymousPaths.register('/BAR');
+ expect(anonymousPaths.isAnonymous('/foo/bar')).toBe(true);
+ });
+
+ it('returns false for other paths', () => {
+ const basePath = new BasePath('/foo');
+ const anonymousPaths = new AnonymousPaths(basePath);
+ anonymousPaths.register('/bar');
+ expect(anonymousPaths.isAnonymous('/foo/foo')).toBe(false);
+ });
+
+ it('returns false for sub-paths of registered paths', () => {
+ const basePath = new BasePath('/foo');
+ const anonymousPaths = new AnonymousPaths(basePath);
+ anonymousPaths.register('/bar');
+ expect(anonymousPaths.isAnonymous('/foo/bar/baz')).toBe(false);
+ });
+});
diff --git a/src/core/public/http/anonymous_paths.ts b/src/core/public/http/anonymous_paths.ts
new file mode 100644
index 0000000000000..300c4d64df353
--- /dev/null
+++ b/src/core/public/http/anonymous_paths.ts
@@ -0,0 +1,53 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { IAnonymousPaths, IBasePath } from 'src/core/public';
+
+export class AnonymousPaths implements IAnonymousPaths {
+ private readonly paths = new Set