= ({ disabled = false }) =
.json,
postCode: POST,
@@ -106,17 +106,6 @@ export const DocumentCreationButtons: React.FC = ({ disabled = false }) =
)}
description=""
icon={}
- betaBadgeLabel={i18n.translate(
- 'xpack.enterpriseSearch.appSearch.documentCreation.buttons.betaTitle',
- { defaultMessage: 'Beta' }
- )}
- betaBadgeTooltipContent={i18n.translate(
- 'xpack.enterpriseSearch.appSearch.documentCreation.buttons.betaTooltip',
- {
- defaultMessage:
- 'The Elastic Crawler is not GA. Please help us by reporting any bugs.',
- }
- )}
to={crawlerLink}
isDisabled={disabled}
/>
diff --git a/x-pack/plugins/file_upload/common/index.ts b/x-pack/plugins/file_upload/common/index.ts
index f4d74984a7d78..58159dfc3d7ef 100644
--- a/x-pack/plugins/file_upload/common/index.ts
+++ b/x-pack/plugins/file_upload/common/index.ts
@@ -5,5 +5,8 @@
* 2.0.
*/
+// TODO: https://github.com/elastic/kibana/issues/110898
+/* eslint-disable @kbn/eslint/no_export_all */
+
export * from './constants';
export * from './types';
diff --git a/x-pack/plugins/file_upload/public/index.ts b/x-pack/plugins/file_upload/public/index.ts
index 262e399242291..b9e289ef00eeb 100644
--- a/x-pack/plugins/file_upload/public/index.ts
+++ b/x-pack/plugins/file_upload/public/index.ts
@@ -5,6 +5,9 @@
* 2.0.
*/
+// TODO: https://github.com/elastic/kibana/issues/110898
+/* eslint-disable @kbn/eslint/no_export_all */
+
import { FileUploadPlugin } from './plugin';
export function plugin() {
diff --git a/x-pack/plugins/file_upload/public/lazy_load_bundle/index.ts b/x-pack/plugins/file_upload/public/lazy_load_bundle/index.ts
index 9c7c6ff1e5180..192a7ffb5e782 100644
--- a/x-pack/plugins/file_upload/public/lazy_load_bundle/index.ts
+++ b/x-pack/plugins/file_upload/public/lazy_load_bundle/index.ts
@@ -44,15 +44,18 @@ export async function lazyLoadModules(): Promise {
return loadModulesPromise;
}
- loadModulesPromise = new Promise(async (resolve) => {
- const { JsonUploadAndParse, importerFactory, IndexNameForm } = await import('./lazy');
-
- resolve({
- JsonUploadAndParse,
- importerFactory,
- getHttp,
- IndexNameForm,
- });
+ loadModulesPromise = new Promise(async (resolve, reject) => {
+ try {
+ const { JsonUploadAndParse, importerFactory, IndexNameForm } = await import('./lazy');
+ resolve({
+ JsonUploadAndParse,
+ importerFactory,
+ getHttp,
+ IndexNameForm,
+ });
+ } catch (error) {
+ reject(error);
+ }
});
return loadModulesPromise;
}
diff --git a/x-pack/plugins/fleet/common/index.ts b/x-pack/plugins/fleet/common/index.ts
index 7805fee2e780a..029460c1750c2 100644
--- a/x-pack/plugins/fleet/common/index.ts
+++ b/x-pack/plugins/fleet/common/index.ts
@@ -5,6 +5,9 @@
* 2.0.
*/
+// TODO: https://github.com/elastic/kibana/issues/110901
+/* eslint-disable @kbn/eslint/no_export_all */
+
export * from './constants';
export * from './services';
export * from './types';
diff --git a/x-pack/plugins/fleet/common/services/validate_package_policy.ts b/x-pack/plugins/fleet/common/services/validate_package_policy.ts
index 5107cbf8121d7..67df65b2f12bf 100644
--- a/x-pack/plugins/fleet/common/services/validate_package_policy.ts
+++ b/x-pack/plugins/fleet/common/services/validate_package_policy.ts
@@ -75,7 +75,7 @@ export const validatePackagePolicy = (
const packageVars = Object.entries(packagePolicy.vars || {});
if (packageVars.length) {
validationResults.vars = packageVars.reduce((results, [name, varEntry]) => {
- results[name] = validatePackagePolicyConfig(varEntry, packageVarsByName[name]);
+ results[name] = validatePackagePolicyConfig(varEntry, packageVarsByName[name], name);
return results;
}, {} as ValidationEntry);
}
@@ -138,7 +138,8 @@ export const validatePackagePolicy = (
results[name] = input.enabled
? validatePackagePolicyConfig(
configEntry,
- inputVarDefsByPolicyTemplateAndType[inputKey][name]
+ inputVarDefsByPolicyTemplateAndType[inputKey][name],
+ name
)
: null;
return results;
@@ -161,7 +162,7 @@ export const validatePackagePolicy = (
(results, [name, configEntry]) => {
results[name] =
streamVarDefs && streamVarDefs[name] && input.enabled && stream.enabled
- ? validatePackagePolicyConfig(configEntry, streamVarDefs[name])
+ ? validatePackagePolicyConfig(configEntry, streamVarDefs[name], name)
: null;
return results;
},
@@ -183,12 +184,14 @@ export const validatePackagePolicy = (
if (Object.entries(validationResults.inputs!).length === 0) {
validationResults.inputs = null;
}
+
return validationResults;
};
export const validatePackagePolicyConfig = (
configEntry: PackagePolicyConfigRecordEntry,
- varDef: RegistryVarsEntry
+ varDef: RegistryVarsEntry,
+ varName: string
): string[] | null => {
const errors = [];
const { value } = configEntry;
@@ -198,6 +201,13 @@ export const validatePackagePolicyConfig = (
parsedValue = value.trim();
}
+ if (varDef === undefined) {
+ // eslint-disable-next-line no-console
+ console.debug(`No variable definition for ${varName} found`);
+
+ return null;
+ }
+
if (varDef.required) {
if (parsedValue === undefined || (typeof parsedValue === 'string' && !parsedValue)) {
errors.push(
diff --git a/x-pack/plugins/fleet/dev_docs/diagrams/README.md b/x-pack/plugins/fleet/dev_docs/diagrams/README.md
new file mode 100644
index 0000000000000..9266f2963bda8
--- /dev/null
+++ b/x-pack/plugins/fleet/dev_docs/diagrams/README.md
@@ -0,0 +1,5 @@
+# Fleet Diagrams
+
+This directory contains diagrams for various components and concepts around the Fleet application. Diagrams are created using https://excalidraw.com/. To edit a diagram, open the `.excalidraw` file in the Excalidraw web application, then save the file and overwrite that same `.excalidraw` file. Also, be sure to export your diagram as a `.png` and update the image version of the diagram you're editing.
+
+Excalidraw is an open source diagramming tool for creating simple, effective diagrams and illustrations. To learn more about it, check out their GitHub repository: https://github.com/excalidraw/excalidraw.
diff --git a/x-pack/plugins/fleet/dev_docs/diagrams/agent_and_package_policies/agent_and_package_policies.excalidraw b/x-pack/plugins/fleet/dev_docs/diagrams/agent_and_package_policies/agent_and_package_policies.excalidraw
new file mode 100644
index 0000000000000..322072e9b3fc1
--- /dev/null
+++ b/x-pack/plugins/fleet/dev_docs/diagrams/agent_and_package_policies/agent_and_package_policies.excalidraw
@@ -0,0 +1,1474 @@
+{
+ "type": "excalidraw",
+ "version": 2,
+ "source": "https://excalidraw.com",
+ "elements": [
+ {
+ "type": "rectangle",
+ "version": 51,
+ "versionNonce": 1267934455,
+ "isDeleted": false,
+ "id": "jCSDMPOBujx9LJaInpsmb",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 0,
+ "opacity": 100,
+ "angle": 0,
+ "x": 361.341552734375,
+ "y": 277.3671875,
+ "strokeColor": "#000000",
+ "backgroundColor": "#4c6ef5",
+ "width": 337.774169921875,
+ "height": 287.30877685546875,
+ "seed": 153566809,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElementIds": [
+ "ICRtE__eCR4UTo54frrx-",
+ "k37m5R6GkzTaorYtt3Xob"
+ ]
+ },
+ {
+ "type": "rectangle",
+ "version": 173,
+ "versionNonce": 1761775033,
+ "isDeleted": false,
+ "id": "PAD-D1XTaAjZXPugghCBO",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 0,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1414.7705078125,
+ "y": 781.1680567930013,
+ "strokeColor": "#000000",
+ "backgroundColor": "#868e96",
+ "width": 166.18115234374997,
+ "height": 170.1334263612956,
+ "seed": 1715473209,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElementIds": []
+ },
+ {
+ "type": "rectangle",
+ "version": 152,
+ "versionNonce": 768008215,
+ "isDeleted": false,
+ "id": "kPJHqu5N5Nz07F4OyyRZ2",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 0,
+ "opacity": 100,
+ "angle": 0,
+ "x": 972.4078369140625,
+ "y": 1035.4315913388996,
+ "strokeColor": "#000000",
+ "backgroundColor": "#868e96",
+ "width": 166.18115234374997,
+ "height": 170.1334263612956,
+ "seed": 1147943961,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElementIds": []
+ },
+ {
+ "type": "rectangle",
+ "version": 200,
+ "versionNonce": 1510222295,
+ "isDeleted": false,
+ "id": "WCA2gYGRwxjLLbGTL7lQz",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 0,
+ "opacity": 100,
+ "angle": 0,
+ "x": 413.5852922712054,
+ "y": 1332.2280739700382,
+ "strokeColor": "#000000",
+ "backgroundColor": "#868e96",
+ "width": 166.18115234374997,
+ "height": 170.1334263612956,
+ "seed": 1939820793,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElementIds": []
+ },
+ {
+ "type": "text",
+ "version": 64,
+ "versionNonce": 267668567,
+ "isDeleted": false,
+ "id": "oAAcAXFQUIh1OXXiBgjIy",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 0,
+ "opacity": 100,
+ "angle": 0,
+ "x": 462.53369140625,
+ "y": 232.99232482910156,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 111,
+ "height": 23,
+ "seed": 2036124121,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElementIds": [],
+ "fontSize": 20,
+ "fontFamily": 2,
+ "text": "Agent Policy",
+ "baseline": 18,
+ "textAlign": "left",
+ "verticalAlign": "top"
+ },
+ {
+ "type": "text",
+ "version": 710,
+ "versionNonce": 1450617207,
+ "isDeleted": false,
+ "id": "ZFPuYyK96Pnyx_DiXJaIj",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 0,
+ "opacity": 100,
+ "angle": 0,
+ "x": 771.77685546875,
+ "y": 28.173828125,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 497,
+ "height": 90,
+ "seed": 1589126841,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElementIds": [],
+ "fontSize": 16,
+ "fontFamily": 2,
+ "text": "From the Fleet UI:\n\"Agent policies are used to manage settings across a group of agents.\nYou can add integrations to your agent policy to specify what data\nyour agents collect. When you edit an agent policy, you can use Fleet\nto deploy updates to a specified group of agents.\"",
+ "baseline": 86,
+ "textAlign": "left",
+ "verticalAlign": "top"
+ },
+ {
+ "type": "rectangle",
+ "version": 144,
+ "versionNonce": 1857381081,
+ "isDeleted": false,
+ "id": "8IzDo67r_O6baz2uZKak2",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 0,
+ "opacity": 100,
+ "angle": 0,
+ "x": 400.1982421875,
+ "y": 319.10589599609375,
+ "strokeColor": "#000000",
+ "backgroundColor": "#fa5252",
+ "width": 97.571044921875,
+ "height": 72.79846191406249,
+ "seed": 1045751705,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElementIds": []
+ },
+ {
+ "type": "rectangle",
+ "version": 169,
+ "versionNonce": 1660536185,
+ "isDeleted": false,
+ "id": "r5G4QOIwl4hHi0J7gGnAH",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 0,
+ "opacity": 100,
+ "angle": 0,
+ "x": 555.88818359375,
+ "y": 317.6759948730469,
+ "strokeColor": "#000000",
+ "backgroundColor": "#fa5252",
+ "width": 97.571044921875,
+ "height": 72.79846191406249,
+ "seed": 308710521,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElementIds": []
+ },
+ {
+ "type": "rectangle",
+ "version": 231,
+ "versionNonce": 1853140569,
+ "isDeleted": false,
+ "id": "DI_-yig5vVdW0SzPQR9oo",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 0,
+ "opacity": 100,
+ "angle": 0,
+ "x": 480.9833984375,
+ "y": 438.5256042480469,
+ "strokeColor": "#000000",
+ "backgroundColor": "#fa5252",
+ "width": 97.571044921875,
+ "height": 72.79846191406249,
+ "seed": 1118868825,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElementIds": []
+ },
+ {
+ "type": "text",
+ "version": 82,
+ "versionNonce": 317743767,
+ "isDeleted": false,
+ "id": "yfHUHwT3vzgVC1MhduPmt",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 0,
+ "opacity": 100,
+ "angle": 0,
+ "x": 413.1236572265625,
+ "y": 334.87489318847656,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 62,
+ "height": 36,
+ "seed": 1921417785,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElementIds": [],
+ "fontSize": 16,
+ "fontFamily": 2,
+ "text": "Package\nPolicy",
+ "baseline": 32,
+ "textAlign": "center",
+ "verticalAlign": "middle"
+ },
+ {
+ "type": "text",
+ "version": 105,
+ "versionNonce": 1175844791,
+ "isDeleted": false,
+ "id": "S1f9df1N4LLQftkY_gave",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 0,
+ "opacity": 100,
+ "angle": 0,
+ "x": 568.8135986328125,
+ "y": 333.4449920654297,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 62,
+ "height": 36,
+ "seed": 248238873,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElementIds": [],
+ "fontSize": 16,
+ "fontFamily": 2,
+ "text": "Package\nPolicy",
+ "baseline": 32,
+ "textAlign": "center",
+ "verticalAlign": "middle"
+ },
+ {
+ "type": "text",
+ "version": 169,
+ "versionNonce": 1712056535,
+ "isDeleted": false,
+ "id": "4uxqA-SJ4vBI6xhYKNg_v",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 0,
+ "opacity": 100,
+ "angle": 0,
+ "x": 493.9088134765625,
+ "y": 454.2946014404297,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 62,
+ "height": 36,
+ "seed": 468977657,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElementIds": [],
+ "fontSize": 16,
+ "fontFamily": 2,
+ "text": "Package\nPolicy",
+ "baseline": 32,
+ "textAlign": "center",
+ "verticalAlign": "middle"
+ },
+ {
+ "type": "line",
+ "version": 123,
+ "versionNonce": 1805071671,
+ "isDeleted": false,
+ "id": "cBItdwVqKieCg1ZEXRgGV",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 0,
+ "opacity": 100,
+ "angle": 0,
+ "x": 159.45556640625,
+ "y": 217.94335937500003,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 222.562255859375,
+ "height": 131.63372802734375,
+ "seed": 1141022937,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElementIds": [],
+ "startBinding": null,
+ "endBinding": null,
+ "lastCommittedPoint": null,
+ "startArrowhead": null,
+ "endArrowhead": null,
+ "points": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 222.562255859375,
+ 131.63372802734375
+ ]
+ ]
+ },
+ {
+ "type": "text",
+ "version": 372,
+ "versionNonce": 1502405593,
+ "isDeleted": false,
+ "id": "JQ1mX_4n7ge602lI_TuZN",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 0,
+ "opacity": 100,
+ "angle": 0,
+ "x": -142.84326171875,
+ "y": 127.57491387261291,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 426,
+ "height": 72,
+ "seed": 613096889,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElementIds": [],
+ "fontSize": 16,
+ "fontFamily": 2,
+ "text": "A Package Policy defines the package-specific configuration\nvalues for a given package. This is things like \"log file path\"\nor \"authentication token\" that each integration needs to\nproperly find and work with your data",
+ "baseline": 68,
+ "textAlign": "left",
+ "verticalAlign": "top"
+ },
+ {
+ "type": "line",
+ "version": 65,
+ "versionNonce": 1104581495,
+ "isDeleted": false,
+ "id": "C4-1o5prqQ6DoEaEXwJTp",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 0,
+ "opacity": 100,
+ "angle": 0,
+ "x": 762.08740234375,
+ "y": 52.033111572265625,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 178.7421875,
+ "height": 176.862060546875,
+ "seed": 1555103385,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElementIds": [],
+ "startBinding": null,
+ "endBinding": null,
+ "lastCommittedPoint": null,
+ "startArrowhead": null,
+ "endArrowhead": null,
+ "points": [
+ [
+ 0,
+ 0
+ ],
+ [
+ -178.7421875,
+ 176.862060546875
+ ]
+ ]
+ },
+ {
+ "type": "text",
+ "version": 52,
+ "versionNonce": 1009442583,
+ "isDeleted": false,
+ "id": "xcT17WwuVaLLV0-QkLnST",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 0,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1480.361083984375,
+ "y": 856.234769973649,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 33,
+ "height": 18,
+ "seed": 1529332601,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElementIds": [],
+ "fontSize": 16,
+ "fontFamily": 2,
+ "text": "Host",
+ "baseline": 14,
+ "textAlign": "center",
+ "verticalAlign": "middle"
+ },
+ {
+ "type": "text",
+ "version": 31,
+ "versionNonce": 1271718967,
+ "isDeleted": false,
+ "id": "9PEFqQm8bDloEjfLbiv_E",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 0,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1037.9984130859375,
+ "y": 1110.4983045195472,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 33,
+ "height": 18,
+ "seed": 1815276633,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElementIds": [],
+ "fontSize": 16,
+ "fontFamily": 2,
+ "text": "Host",
+ "baseline": 14,
+ "textAlign": "center",
+ "verticalAlign": "middle"
+ },
+ {
+ "type": "text",
+ "version": 77,
+ "versionNonce": 1597064535,
+ "isDeleted": false,
+ "id": "ZVH75zUF1nLScxrub3BtK",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 0,
+ "opacity": 100,
+ "angle": 0,
+ "x": 479.5023193359375,
+ "y": 1407.6214124297037,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 33,
+ "height": 18,
+ "seed": 128317753,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElementIds": [],
+ "fontSize": 16,
+ "fontFamily": 2,
+ "text": "Host",
+ "baseline": 14,
+ "textAlign": "center",
+ "verticalAlign": "middle"
+ },
+ {
+ "type": "rectangle",
+ "version": 78,
+ "versionNonce": 1781660793,
+ "isDeleted": false,
+ "id": "TQHtZyAi5ZtWXfPJOuG4h",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 0,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1368.635480608259,
+ "y": 751.2757611955917,
+ "strokeColor": "#000000",
+ "backgroundColor": "#fab005",
+ "width": 73.6383056640625,
+ "height": 61.18141174316406,
+ "seed": 1592027673,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElementIds": []
+ },
+ {
+ "type": "rectangle",
+ "version": 59,
+ "versionNonce": 114186137,
+ "isDeleted": false,
+ "id": "6Qh6yaaWu_smWgF8lh7ts",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 0,
+ "opacity": 100,
+ "angle": 0,
+ "x": 926.2728097098216,
+ "y": 1005.5392957414899,
+ "strokeColor": "#000000",
+ "backgroundColor": "#fab005",
+ "width": 73.6383056640625,
+ "height": 61.18141174316406,
+ "seed": 719437561,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElementIds": [
+ "zlS4f5wvhfJmvxzL36_uN"
+ ]
+ },
+ {
+ "type": "rectangle",
+ "version": 104,
+ "versionNonce": 239709239,
+ "isDeleted": false,
+ "id": "dYVSd7vzqd5mQVhpIJhoU",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 0,
+ "opacity": 100,
+ "angle": 0,
+ "x": 367.7767159598217,
+ "y": 1302.6624036516464,
+ "strokeColor": "#000000",
+ "backgroundColor": "#fab005",
+ "width": 73.6383056640625,
+ "height": 61.18141174316406,
+ "seed": 975271897,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElementIds": [
+ "VXjQzRP5z49NVL_qeGJ8K"
+ ]
+ },
+ {
+ "type": "text",
+ "version": 62,
+ "versionNonce": 95191671,
+ "isDeleted": false,
+ "id": "SG27_ypnubzKpWUjog7Ju",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 0,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1377.767560686384,
+ "y": 722.1002393450058,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 93,
+ "height": 18,
+ "seed": 1052185785,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElementIds": [],
+ "fontSize": 16,
+ "fontFamily": 2,
+ "text": "Elastic Agent",
+ "baseline": 14,
+ "textAlign": "left",
+ "verticalAlign": "top"
+ },
+ {
+ "type": "text",
+ "version": 41,
+ "versionNonce": 1879251863,
+ "isDeleted": false,
+ "id": "ME2vZZNpg21fqavHSGyAe",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 0,
+ "opacity": 100,
+ "angle": 0,
+ "x": 935.4048897879466,
+ "y": 976.363773890904,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 93,
+ "height": 18,
+ "seed": 1548096921,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElementIds": [],
+ "fontSize": 16,
+ "fontFamily": 2,
+ "text": "Elastic Agent",
+ "baseline": 14,
+ "textAlign": "left",
+ "verticalAlign": "top"
+ },
+ {
+ "type": "text",
+ "version": 88,
+ "versionNonce": 1114214583,
+ "isDeleted": false,
+ "id": "uBaJ2BrvmY41RYKO9AHWc",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 0,
+ "opacity": 100,
+ "angle": 0,
+ "x": 376.9087960379467,
+ "y": 1273.4868818010605,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 93,
+ "height": 18,
+ "seed": 1979841145,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElementIds": [
+ "fvhIgP7_0OlLXgaCi-Gcx"
+ ],
+ "fontSize": 16,
+ "fontFamily": 2,
+ "text": "Elastic Agent",
+ "baseline": 14,
+ "textAlign": "left",
+ "verticalAlign": "top"
+ },
+ {
+ "type": "rectangle",
+ "version": 142,
+ "versionNonce": 869511383,
+ "isDeleted": false,
+ "id": "qbJ3HumuDumzmdo6S2uzH",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 0,
+ "opacity": 100,
+ "angle": 0,
+ "x": 736.9051339285717,
+ "y": 635.138371058873,
+ "strokeColor": "#000000",
+ "backgroundColor": "#15aabf",
+ "width": 162.408447265625,
+ "height": 148.80645751953125,
+ "seed": 1018053465,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElementIds": [
+ "ICRtE__eCR4UTo54frrx-",
+ "zovK4QCBSc4despIqL_Px",
+ "zlS4f5wvhfJmvxzL36_uN",
+ "fvhIgP7_0OlLXgaCi-Gcx"
+ ]
+ },
+ {
+ "type": "text",
+ "version": 102,
+ "versionNonce": 646210007,
+ "isDeleted": false,
+ "id": "b-H9gE4GD7_H2Th50aq7g",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 0,
+ "opacity": 100,
+ "angle": 0,
+ "x": 794.1091134207592,
+ "y": 699.2558789934433,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 36,
+ "height": 18,
+ "seed": 2090758201,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElementIds": [],
+ "fontSize": 16,
+ "fontFamily": 2,
+ "text": "Fleet",
+ "baseline": 14,
+ "textAlign": "center",
+ "verticalAlign": "middle"
+ },
+ {
+ "type": "arrow",
+ "version": 91,
+ "versionNonce": 326695159,
+ "isDeleted": false,
+ "id": "ICRtE__eCR4UTo54frrx-",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 0,
+ "opacity": 100,
+ "angle": 0,
+ "x": 719.3434884207592,
+ "y": 394.94458443777916,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 146.2176513671875,
+ "height": 229.08706665039068,
+ "seed": 733348121,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElementIds": [],
+ "startBinding": {
+ "elementId": "jCSDMPOBujx9LJaInpsmb",
+ "focus": -0.33950373459679334,
+ "gap": 20.227765764509172
+ },
+ "endBinding": {
+ "elementId": "qbJ3HumuDumzmdo6S2uzH",
+ "focus": 0.11061733892271751,
+ "gap": 11.106719970703125
+ },
+ "lastCommittedPoint": null,
+ "startArrowhead": null,
+ "endArrowhead": "arrow",
+ "points": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 146.2176513671875,
+ 25.181121826171875
+ ],
+ [
+ 119.8638916015625,
+ 229.08706665039068
+ ]
+ ]
+ },
+ {
+ "type": "arrow",
+ "version": 62,
+ "versionNonce": 704430615,
+ "isDeleted": false,
+ "id": "zovK4QCBSc4despIqL_Px",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 0,
+ "opacity": 100,
+ "angle": 0,
+ "x": 511.8593575613842,
+ "y": 604.3765760149277,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 210.1806640625,
+ "height": 135.83206176757812,
+ "seed": 929592825,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElementIds": [],
+ "startBinding": null,
+ "endBinding": {
+ "elementId": "qbJ3HumuDumzmdo6S2uzH",
+ "focus": -0.1822256724307969,
+ "gap": 14.8651123046875
+ },
+ "lastCommittedPoint": null,
+ "startArrowhead": null,
+ "endArrowhead": "arrow",
+ "points": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 32.21954345703125,
+ 135.83206176757812
+ ],
+ [
+ 210.1806640625,
+ 125.29045104980469
+ ]
+ ]
+ },
+ {
+ "type": "arrow",
+ "version": 193,
+ "versionNonce": 191522615,
+ "isDeleted": false,
+ "id": "LegMRWZJRxEaNbMqed2xD",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 0,
+ "opacity": 100,
+ "angle": 0,
+ "x": 932.9982735770092,
+ "y": 702.8953290666855,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 411.3313205592334,
+ "height": 77.14139261320236,
+ "seed": 314134233,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElementIds": [],
+ "startBinding": null,
+ "endBinding": null,
+ "lastCommittedPoint": null,
+ "startArrowhead": null,
+ "endArrowhead": "arrow",
+ "points": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 411.3313205592334,
+ 77.14139261320236
+ ]
+ ]
+ },
+ {
+ "type": "arrow",
+ "version": 81,
+ "versionNonce": 1982228567,
+ "isDeleted": false,
+ "id": "zlS4f5wvhfJmvxzL36_uN",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 0,
+ "opacity": 100,
+ "angle": 0,
+ "x": 832.9335763113842,
+ "y": 805.9544873918808,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 84.757080078125,
+ "height": 189.8906707763672,
+ "seed": 71044025,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElementIds": [],
+ "startBinding": {
+ "elementId": "qbJ3HumuDumzmdo6S2uzH",
+ "focus": 0.24655542632406335,
+ "gap": 22.009658813476562
+ },
+ "endBinding": {
+ "elementId": "6Qh6yaaWu_smWgF8lh7ts",
+ "focus": -0.5432643496291523,
+ "gap": 9.69413757324196
+ },
+ "lastCommittedPoint": null,
+ "startArrowhead": null,
+ "endArrowhead": "arrow",
+ "points": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 84.757080078125,
+ 189.8906707763672
+ ]
+ ]
+ },
+ {
+ "type": "arrow",
+ "version": 66,
+ "versionNonce": 1745175927,
+ "isDeleted": false,
+ "id": "fvhIgP7_0OlLXgaCi-Gcx",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 0,
+ "opacity": 100,
+ "angle": 0,
+ "x": 741.0500313895092,
+ "y": 812.9967237200058,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 294.090576171875,
+ "height": 447.2994232177732,
+ "seed": 873737369,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElementIds": [],
+ "startBinding": {
+ "elementId": "qbJ3HumuDumzmdo6S2uzH",
+ "focus": 0.06946984295991476,
+ "gap": 29.051895141601562
+ },
+ "endBinding": {
+ "elementId": "uBaJ2BrvmY41RYKO9AHWc",
+ "focus": 0.02169307095704409,
+ "gap": 13.190734863281477
+ },
+ "lastCommittedPoint": null,
+ "startArrowhead": null,
+ "endArrowhead": "arrow",
+ "points": [
+ [
+ 0,
+ 0
+ ],
+ [
+ -294.090576171875,
+ 447.2994232177732
+ ]
+ ]
+ },
+ {
+ "type": "text",
+ "version": 276,
+ "versionNonce": 493358839,
+ "isDeleted": false,
+ "id": "y1SRfopGf18PxGafEF1WF",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 0,
+ "opacity": 100,
+ "angle": 0,
+ "x": 935.0032784598218,
+ "y": 598.9334302629745,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 375,
+ "height": 72,
+ "seed": 2046112121,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElementIds": [],
+ "fontSize": 16,
+ "fontFamily": 2,
+ "text": "Fleet deploys changes in agent and package policies\nto the appropriate instances of Elastic Agent on \nyour hosts\n",
+ "baseline": 68,
+ "textAlign": "left",
+ "verticalAlign": "top"
+ },
+ {
+ "type": "diamond",
+ "version": 88,
+ "versionNonce": 714250359,
+ "isDeleted": false,
+ "id": "Ad3mw-Goi0x9E-DxQFE5k",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 0,
+ "opacity": 100,
+ "angle": 0,
+ "x": -271.1817190987721,
+ "y": 827.6216626848495,
+ "strokeColor": "#000000",
+ "backgroundColor": "#40c057",
+ "width": 199.51019287109375,
+ "height": 204.44686889648438,
+ "seed": 1797429849,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElementIds": [
+ "VXjQzRP5z49NVL_qeGJ8K",
+ "k37m5R6GkzTaorYtt3Xob",
+ "QyaYrSSog2TXjwVv5EhBZ"
+ ]
+ },
+ {
+ "type": "text",
+ "version": 57,
+ "versionNonce": 1277196855,
+ "isDeleted": false,
+ "id": "0bSe54FwYFi2BASHcvDDp",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 0,
+ "opacity": 100,
+ "angle": 0,
+ "x": -221.1610674176895,
+ "y": 915.6001020159042,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 95,
+ "height": 18,
+ "seed": 678422329,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElementIds": [],
+ "fontSize": 16,
+ "fontFamily": 2,
+ "text": "Elasticsearch",
+ "baseline": 14,
+ "textAlign": "center",
+ "verticalAlign": "middle"
+ },
+ {
+ "type": "arrow",
+ "version": 104,
+ "versionNonce": 1741115895,
+ "isDeleted": false,
+ "id": "VXjQzRP5z49NVL_qeGJ8K",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 0,
+ "opacity": 100,
+ "angle": 0,
+ "x": 363.6754586356029,
+ "y": 1358.575184413365,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 541.7135620117188,
+ "height": 325.7031249999998,
+ "seed": 1816172569,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElementIds": [],
+ "startBinding": {
+ "elementId": "dYVSd7vzqd5mQVhpIJhoU",
+ "focus": -0.7695999316996307,
+ "gap": 4.10125732421875
+ },
+ "endBinding": {
+ "elementId": "Ad3mw-Goi0x9E-DxQFE5k",
+ "focus": 0.49594887042146074,
+ "gap": 12.8125333757949
+ },
+ "lastCommittedPoint": null,
+ "startArrowhead": null,
+ "endArrowhead": "arrow",
+ "points": [
+ [
+ 0,
+ 0
+ ],
+ [
+ -419.03289794921875,
+ 10.7666015625
+ ],
+ [
+ -541.7135620117188,
+ -314.9365234374998
+ ]
+ ]
+ },
+ {
+ "type": "arrow",
+ "version": 111,
+ "versionNonce": 1964505879,
+ "isDeleted": false,
+ "id": "k37m5R6GkzTaorYtt3Xob",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 0,
+ "opacity": 100,
+ "angle": 0,
+ "x": 345.0048043387279,
+ "y": 562.4162793840683,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 469.6044921875,
+ "height": 305.5834197998047,
+ "seed": 1485319417,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElementIds": [],
+ "startBinding": {
+ "elementId": "jCSDMPOBujx9LJaInpsmb",
+ "focus": -0.08228998680334919,
+ "gap": 16.336748395647078
+ },
+ "endBinding": {
+ "elementId": "Ad3mw-Goi0x9E-DxQFE5k",
+ "focus": -0.30691531065972416,
+ "gap": 5.313236572271876
+ },
+ "lastCommittedPoint": null,
+ "startArrowhead": null,
+ "endArrowhead": "arrow",
+ "points": [
+ [
+ 0,
+ 0
+ ],
+ [
+ -469.6044921875,
+ 305.5834197998047
+ ]
+ ]
+ },
+ {
+ "type": "text",
+ "version": 277,
+ "versionNonce": 688651575,
+ "isDeleted": false,
+ "id": "SYRzOizCOgBI6P2KVJ7aR",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 0,
+ "opacity": 100,
+ "angle": 0,
+ "x": -177.04597691127208,
+ "y": 597.6700025285995,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 376,
+ "height": 36,
+ "seed": 80356825,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElementIds": [],
+ "fontSize": 16,
+ "fontFamily": 2,
+ "text": "Agent and package policies are stored as documents\nin an Elasticsearch index",
+ "baseline": 32,
+ "textAlign": "left",
+ "verticalAlign": "top"
+ },
+ {
+ "type": "text",
+ "version": 220,
+ "versionNonce": 9847287,
+ "isDeleted": false,
+ "id": "epU2HYr0tZyZXYpcYtyKW",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 0,
+ "opacity": 100,
+ "angle": 0,
+ "x": -118.87825230189708,
+ "y": 1152.7445417131696,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 479,
+ "height": 36,
+ "seed": 1276773049,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElementIds": [],
+ "fontSize": 16,
+ "fontFamily": 2,
+ "text": "Instances of Elastic Agent log data into Elasticsearch\nbased on the configuration provided by Agent and Package Policies",
+ "baseline": 32,
+ "textAlign": "left",
+ "verticalAlign": "top"
+ },
+ {
+ "type": "text",
+ "version": 343,
+ "versionNonce": 783899511,
+ "isDeleted": false,
+ "id": "GzkxiI7rD24zcJpAvYSQr",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 0,
+ "opacity": 100,
+ "angle": 0,
+ "x": -286.5030691964283,
+ "y": 272.74301583426353,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 470,
+ "height": 72,
+ "seed": 1528513433,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElementIds": [],
+ "fontSize": 16,
+ "fontFamily": 2,
+ "text": "A Package Policy also defines the version of its package,\nwhich is where we can have to deal with conflicts when upgrading.\nIf a newer version of a package contains breaking changes, we\ncan't automatically upgrade it for you.",
+ "baseline": 68,
+ "textAlign": "left",
+ "verticalAlign": "top"
+ },
+ {
+ "type": "rectangle",
+ "version": 61,
+ "versionNonce": 281444761,
+ "isDeleted": false,
+ "id": "oLLo7vMvZV-LCw7Qf5ykm",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 0,
+ "opacity": 100,
+ "angle": 0,
+ "x": -752.4035517838441,
+ "y": 697.4313855852402,
+ "strokeColor": "#000000",
+ "backgroundColor": "#15aabf",
+ "width": 222.31741373975953,
+ "height": 140.634765625,
+ "seed": 2114021497,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElementIds": [
+ "QyaYrSSog2TXjwVv5EhBZ"
+ ]
+ },
+ {
+ "type": "text",
+ "version": 153,
+ "versionNonce": 1957082329,
+ "isDeleted": false,
+ "id": "MU7q34-wC6O4jjGFpCBfK",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 0,
+ "opacity": 100,
+ "angle": 0,
+ "x": -721.4141758510042,
+ "y": 748.2946515764511,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 153,
+ "height": 36,
+ "seed": 781185369,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElementIds": [],
+ "fontSize": 16,
+ "fontFamily": 2,
+ "text": "Integrations UI\n\"Browse Integrations\"",
+ "baseline": 32,
+ "textAlign": "center",
+ "verticalAlign": "middle"
+ },
+ {
+ "type": "arrow",
+ "version": 86,
+ "versionNonce": 703364567,
+ "isDeleted": false,
+ "id": "QyaYrSSog2TXjwVv5EhBZ",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 0,
+ "opacity": 100,
+ "angle": 0,
+ "x": -502.6587698800221,
+ "y": 765.8922315325058,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 259.82879638671875,
+ "height": 99.79354858398438,
+ "seed": 1645895225,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElementIds": [],
+ "startBinding": {
+ "elementId": "oLLo7vMvZV-LCw7Qf5ykm",
+ "focus": -0.08478862194745694,
+ "gap": 27.4273681640625
+ },
+ "endBinding": {
+ "elementId": "Ad3mw-Goi0x9E-DxQFE5k",
+ "focus": 0.1744581519544163,
+ "gap": 24.518520059056954
+ },
+ "lastCommittedPoint": null,
+ "startArrowhead": null,
+ "endArrowhead": "arrow",
+ "points": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 111.47491455078125,
+ 3.54339599609375
+ ],
+ [
+ 259.82879638671875,
+ 99.79354858398438
+ ]
+ ]
+ },
+ {
+ "type": "text",
+ "version": 387,
+ "versionNonce": 1859398071,
+ "isDeleted": false,
+ "id": "nh1QLEj1izgVCnunVEO2_",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 0,
+ "opacity": 100,
+ "angle": 0,
+ "x": -849.8984549386158,
+ "y": 866.8778424944198,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 469,
+ "height": 54,
+ "seed": 394538777,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElementIds": [],
+ "fontSize": 16,
+ "fontFamily": 2,
+ "text": "Installing a package (or updating it) fetches and downloads\ndata/configuration for various Kibana assets like visualization\ndashboards, and saved queries that are persisted to Elasticsearch",
+ "baseline": 50,
+ "textAlign": "left",
+ "verticalAlign": "top"
+ },
+ {
+ "type": "text",
+ "version": 73,
+ "versionNonce": 2083773143,
+ "isDeleted": false,
+ "id": "d7Q1XMjK0XTHyx14X-aqp",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 0,
+ "opacity": 100,
+ "angle": 0,
+ "x": -454.7702811104908,
+ "y": 724.5928388323105,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 143,
+ "height": 18,
+ "seed": 625009657,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElementIds": [],
+ "fontSize": 16,
+ "fontFamily": 2,
+ "text": "Package Installation",
+ "baseline": 14,
+ "textAlign": "left",
+ "verticalAlign": "top"
+ },
+ {
+ "id": "A2f1bpo856LR6-K-v3DT_",
+ "type": "text",
+ "x": 910.3316127232135,
+ "y": -119.16315133231035,
+ "width": 540,
+ "height": 23,
+ "angle": 0,
+ "strokeColor": "#000000",
+ "backgroundColor": "#868e96",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 0,
+ "opacity": 100,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "seed": 1192664215,
+ "version": 635,
+ "versionNonce": 1706323607,
+ "isDeleted": false,
+ "boundElementIds": null,
+ "text": "https://www.elastic.co/guide/en/fleet/current/agent-policy.html",
+ "fontSize": 20,
+ "fontFamily": 2,
+ "textAlign": "left",
+ "verticalAlign": "top",
+ "baseline": 18
+ },
+ {
+ "id": "-XfKzGYSopxn4u_7uIdbS",
+ "type": "text",
+ "x": 988.6153738839284,
+ "y": -162.61463710239912,
+ "width": 295,
+ "height": 23,
+ "angle": 0,
+ "strokeColor": "#000000",
+ "backgroundColor": "#868e96",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 0,
+ "opacity": 100,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "seed": 1130826905,
+ "version": 527,
+ "versionNonce": 1358998105,
+ "isDeleted": false,
+ "boundElementIds": null,
+ "text": "Further reading in the Fleet docs:",
+ "fontSize": 20,
+ "fontFamily": 2,
+ "textAlign": "left",
+ "verticalAlign": "top",
+ "baseline": 18
+ },
+ {
+ "id": "cyDCNynYv30bjeaXKD9Ba",
+ "type": "text",
+ "x": -85.06382533482173,
+ "y": -163.4971575055796,
+ "width": 784,
+ "height": 41,
+ "angle": 0,
+ "strokeColor": "#000000",
+ "backgroundColor": "#868e96",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 0,
+ "opacity": 100,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "seed": 73034167,
+ "version": 238,
+ "versionNonce": 20813655,
+ "isDeleted": false,
+ "boundElementIds": null,
+ "text": "Fleet: Agent Policy and Package Policy Overview",
+ "fontSize": 36,
+ "fontFamily": 2,
+ "textAlign": "left",
+ "verticalAlign": "top",
+ "baseline": 33
+ }
+ ],
+ "appState": {
+ "gridSize": null,
+ "viewBackgroundColor": "#ffffff"
+ }
+}
\ No newline at end of file
diff --git a/x-pack/plugins/fleet/dev_docs/diagrams/agent_and_package_policies/agent_and_package_policies.png b/x-pack/plugins/fleet/dev_docs/diagrams/agent_and_package_policies/agent_and_package_policies.png
new file mode 100644
index 0000000000000..833aaea548baf
Binary files /dev/null and b/x-pack/plugins/fleet/dev_docs/diagrams/agent_and_package_policies/agent_and_package_policies.png differ
diff --git a/x-pack/plugins/fleet/dev_docs/diagrams/architecture_overview/architecture_overview.excalidraw b/x-pack/plugins/fleet/dev_docs/diagrams/architecture_overview/architecture_overview.excalidraw
new file mode 100644
index 0000000000000..bfd73693d156d
--- /dev/null
+++ b/x-pack/plugins/fleet/dev_docs/diagrams/architecture_overview/architecture_overview.excalidraw
@@ -0,0 +1,1155 @@
+{
+ "type": "excalidraw",
+ "version": 2,
+ "source": "https://excalidraw.com",
+ "elements": [
+ {
+ "type": "rectangle",
+ "version": 179,
+ "versionNonce": 685899255,
+ "isDeleted": false,
+ "id": "V2N6gERGsLagZObwBdXd7",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 0,
+ "opacity": 100,
+ "angle": 0,
+ "x": 436.238525390625,
+ "y": 395.07281494140625,
+ "strokeColor": "#000000",
+ "backgroundColor": "#40c057",
+ "width": 213.742431640625,
+ "height": 173.1971435546875,
+ "seed": 988730617,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElementIds": [
+ "mTWnMpUdsVV8ceAQPLs9-",
+ "pvB3kVjq9ORzyG3vGuzUv",
+ "nDieSVBYlEY32xmCjba1n",
+ "XfXZwtl0lYMcyaqGm81Kp"
+ ]
+ },
+ {
+ "type": "rectangle",
+ "version": 735,
+ "versionNonce": 449044823,
+ "isDeleted": false,
+ "id": "kIj9AWA5qKL3NUJwu4eLZ",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 0,
+ "opacity": 100,
+ "angle": 0,
+ "x": 992.779296875,
+ "y": 396.75579833984375,
+ "strokeColor": "#000000",
+ "backgroundColor": "#40c057",
+ "width": 344.20092773437506,
+ "height": 190.58715820312506,
+ "seed": 1071204249,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElementIds": [
+ "mTWnMpUdsVV8ceAQPLs9-"
+ ]
+ },
+ {
+ "type": "text",
+ "version": 133,
+ "versionNonce": 1290191769,
+ "isDeleted": false,
+ "id": "Sq56zbCalmhyWY5U75WQO",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 0,
+ "opacity": 100,
+ "angle": 0,
+ "x": 483.9796142578125,
+ "y": 464.82843017578125,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 119,
+ "height": 23,
+ "seed": 1562982743,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElementIds": [],
+ "fontSize": 20,
+ "fontFamily": 2,
+ "text": "Elasticsearch",
+ "baseline": 18,
+ "textAlign": "center",
+ "verticalAlign": "middle"
+ },
+ {
+ "type": "text",
+ "version": 361,
+ "versionNonce": 1821089399,
+ "isDeleted": false,
+ "id": "CR7I2o5UwGFuW4BgnWZaG",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 0,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1128.2576904296875,
+ "y": 417.8507080078125,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 62,
+ "height": 23,
+ "seed": 989026935,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElementIds": [],
+ "fontSize": 20,
+ "fontFamily": 2,
+ "text": "Kibana",
+ "baseline": 18,
+ "textAlign": "center",
+ "verticalAlign": "middle"
+ },
+ {
+ "type": "arrow",
+ "version": 1062,
+ "versionNonce": 710731897,
+ "isDeleted": false,
+ "id": "mTWnMpUdsVV8ceAQPLs9-",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 0,
+ "opacity": 100,
+ "angle": 0,
+ "x": 982.638282831786,
+ "y": 478.7558104844152,
+ "strokeColor": "#000000",
+ "backgroundColor": "#40c057",
+ "width": 315.93228450395384,
+ "height": 0,
+ "seed": 36584409,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElementIds": [],
+ "startBinding": {
+ "elementId": "kIj9AWA5qKL3NUJwu4eLZ",
+ "focus": 0.1395011823705664,
+ "gap": 10.141014043214
+ },
+ "endBinding": {
+ "elementId": "V2N6gERGsLagZObwBdXd7",
+ "focus": -0.03366771731329646,
+ "gap": 16.725041296582162
+ },
+ "lastCommittedPoint": null,
+ "startArrowhead": "arrow",
+ "endArrowhead": "arrow",
+ "points": [
+ [
+ 0,
+ 0
+ ],
+ [
+ -315.93228450395384,
+ 0
+ ]
+ ]
+ },
+ {
+ "type": "text",
+ "version": 408,
+ "versionNonce": 1688123287,
+ "isDeleted": false,
+ "id": "TyDRowB3-A60lvI91x-o2",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 0,
+ "opacity": 100,
+ "angle": 0,
+ "x": 121.66904994419644,
+ "y": 270.96874509538947,
+ "strokeColor": "#000000",
+ "backgroundColor": "#40c057",
+ "width": 534,
+ "height": 92,
+ "seed": 1004167511,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElementIds": [],
+ "fontSize": 20,
+ "fontFamily": 2,
+ "text": "Elasticsearch is the database and search engine that serves\nas our primary data storage solution.\n\nhttps://github.com/elastic/elasticsearch",
+ "baseline": 87,
+ "textAlign": "left",
+ "verticalAlign": "top"
+ },
+ {
+ "type": "text",
+ "version": 368,
+ "versionNonce": 1132163417,
+ "isDeleted": false,
+ "id": "5FN2StmgJi8nXLKDFU7EK",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 0,
+ "opacity": 100,
+ "angle": 0,
+ "x": 924.5206560407366,
+ "y": 234.77730233328708,
+ "strokeColor": "#000000",
+ "backgroundColor": "#40c057",
+ "width": 691,
+ "height": 115,
+ "seed": 963312409,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElementIds": [],
+ "fontSize": 20,
+ "fontFamily": 2,
+ "text": "Kibana is an application for drawing insights from data stored in Elasticsearch.\nIt allows users to create visualizations, dashboards, and generally \"work with\"\ntheir Elasticsearch data.\n\nhttps://github.com/elastic/kibana",
+ "baseline": 110,
+ "textAlign": "left",
+ "verticalAlign": "top"
+ },
+ {
+ "type": "rectangle",
+ "version": 342,
+ "versionNonce": 1704493145,
+ "isDeleted": false,
+ "id": "2YasfPXKDkgcXLd5j2qZs",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 0,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1049.251220703125,
+ "y": 510.0099792480471,
+ "strokeColor": "#000000",
+ "backgroundColor": "#fa5252",
+ "width": 74.8330078125,
+ "height": 45.19232177734375,
+ "seed": 65317399,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElementIds": [
+ "r-wdwO5ci-a7MpE-y5HHT"
+ ]
+ },
+ {
+ "type": "rectangle",
+ "version": 487,
+ "versionNonce": 1301667385,
+ "isDeleted": false,
+ "id": "Bjhq9XGHwH9zHMRZsbgW-",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 0,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1159.63134765625,
+ "y": 509.9478149414065,
+ "strokeColor": "#000000",
+ "backgroundColor": "#fa5252",
+ "width": 124.51538085937506,
+ "height": 44.797912597656264,
+ "seed": 476169783,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElementIds": [
+ "1MDWgu4QN3q_Sd2HSW47y"
+ ]
+ },
+ {
+ "type": "text",
+ "version": 46,
+ "versionNonce": 1709768151,
+ "isDeleted": false,
+ "id": "mlrD-I4ZhtIrHXwvei4Wd",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 0,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1063.972412109375,
+ "y": 521.0379638671877,
+ "strokeColor": "#000000",
+ "backgroundColor": "#fa5252",
+ "width": 44,
+ "height": 23,
+ "seed": 241556921,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElementIds": [
+ "XfXZwtl0lYMcyaqGm81Kp"
+ ],
+ "fontSize": 20,
+ "fontFamily": 2,
+ "text": "Fleet",
+ "baseline": 18,
+ "textAlign": "center",
+ "verticalAlign": "middle"
+ },
+ {
+ "type": "text",
+ "version": 75,
+ "versionNonce": 126856985,
+ "isDeleted": false,
+ "id": "xcyVzTHZBNYhZvx5HN3XN",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 0,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1168.656982421875,
+ "y": 522.1023864746096,
+ "strokeColor": "#000000",
+ "backgroundColor": "#fa5252",
+ "width": 105,
+ "height": 23,
+ "seed": 743264983,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElementIds": [
+ "ZTtsb0ncxBs3kQPYAaOx6",
+ "1MDWgu4QN3q_Sd2HSW47y"
+ ],
+ "fontSize": 20,
+ "fontFamily": 2,
+ "text": "Integrations",
+ "baseline": 18,
+ "textAlign": "center",
+ "verticalAlign": "middle"
+ },
+ {
+ "type": "text",
+ "version": 620,
+ "versionNonce": 1037642487,
+ "isDeleted": false,
+ "id": "rPQSe2HeOxRDG2k3smf1c",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 0,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1525.3031005859375,
+ "y": 453.37584686279325,
+ "strokeColor": "#000000",
+ "backgroundColor": "#fa5252",
+ "width": 690,
+ "height": 184,
+ "seed": 1082783449,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElementIds": [
+ "r-wdwO5ci-a7MpE-y5HHT",
+ "ZTtsb0ncxBs3kQPYAaOx6"
+ ],
+ "fontSize": 20,
+ "fontFamily": 2,
+ "text": "Fleet and Integrations are both Kibana applications. Kibana's architecture\nallows for many plugins/application to exist and register themselves as distinct\nexperiences. The Fleet team mainly works on these two Kibana applications \nwithin the `xpack/plugins/fleet` directory in the Kibana codebase.\n\nhttps://github.com/elastic/kibana/tree/master/x-pack/plugins/fleet\nhttps://www.elastic.co/guide/en/fleet/current/fleet-overview.html\n",
+ "baseline": 179,
+ "textAlign": "left",
+ "verticalAlign": "top"
+ },
+ {
+ "type": "arrow",
+ "version": 599,
+ "versionNonce": 131884025,
+ "isDeleted": false,
+ "id": "r-wdwO5ci-a7MpE-y5HHT",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 0,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1093.01806640625,
+ "y": 565.9965515136721,
+ "strokeColor": "#000000",
+ "backgroundColor": "#fa5252",
+ "width": 416.0875244140625,
+ "height": 54.26768493652344,
+ "seed": 776118103,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElementIds": [],
+ "startBinding": {
+ "elementId": "2YasfPXKDkgcXLd5j2qZs",
+ "focus": -0.1668170334553714,
+ "gap": 10.79425048828125
+ },
+ "endBinding": {
+ "elementId": "rPQSe2HeOxRDG2k3smf1c",
+ "focus": 0.26039354359839256,
+ "gap": 16.197509765625
+ },
+ "lastCommittedPoint": null,
+ "startArrowhead": null,
+ "endArrowhead": null,
+ "points": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 123.56658935546875,
+ 54.26768493652344
+ ],
+ [
+ 416.0875244140625,
+ 2.990859186771104
+ ]
+ ]
+ },
+ {
+ "type": "arrow",
+ "version": 653,
+ "versionNonce": 1032238103,
+ "isDeleted": false,
+ "id": "ZTtsb0ncxBs3kQPYAaOx6",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 0,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1288.723876953125,
+ "y": 533.1626204834321,
+ "strokeColor": "#000000",
+ "backgroundColor": "#fa5252",
+ "width": 222.1070556640625,
+ "height": 0.7645838520107873,
+ "seed": 2076436631,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElementIds": [],
+ "startBinding": {
+ "elementId": "xcyVzTHZBNYhZvx5HN3XN",
+ "focus": -0.06053626019021739,
+ "gap": 15.06689453125
+ },
+ "endBinding": {
+ "elementId": "rPQSe2HeOxRDG2k3smf1c",
+ "focus": 0.10957668138586957,
+ "gap": 14.47216796875
+ },
+ "lastCommittedPoint": null,
+ "startArrowhead": null,
+ "endArrowhead": null,
+ "points": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 222.1070556640625,
+ 0.7645838520107873
+ ]
+ ]
+ },
+ {
+ "type": "rectangle",
+ "version": 735,
+ "versionNonce": 1249869017,
+ "isDeleted": false,
+ "id": "Imn7uBkfBKF8X1xHiyTHH",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 0,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1013.7548479352681,
+ "y": 960.8284748622353,
+ "strokeColor": "#000000",
+ "backgroundColor": "#15aabf",
+ "width": 138.45977783203125,
+ "height": 104.25430297851562,
+ "seed": 1995457209,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElementIds": [
+ "XfXZwtl0lYMcyaqGm81Kp",
+ "rcBs7GMovRmTAgrwdgREc"
+ ]
+ },
+ {
+ "type": "rectangle",
+ "version": 879,
+ "versionNonce": 1021712695,
+ "isDeleted": false,
+ "id": "zIFms6BgkiaXuCZ3Dstjq",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 0,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1843.5347726004468,
+ "y": 225.40433502197283,
+ "strokeColor": "#000000",
+ "backgroundColor": "#15aabf",
+ "width": 138.45977783203125,
+ "height": 104.25430297851562,
+ "seed": 800927735,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElementIds": [
+ "XfXZwtl0lYMcyaqGm81Kp",
+ "rcBs7GMovRmTAgrwdgREc",
+ "1MDWgu4QN3q_Sd2HSW47y"
+ ]
+ },
+ {
+ "type": "rectangle",
+ "version": 551,
+ "versionNonce": 2131524025,
+ "isDeleted": false,
+ "id": "bLDMJ_hQTzNBpgToU5_Yl",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 0,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1425.2330322265625,
+ "y": 1206.882377624512,
+ "strokeColor": "#000000",
+ "backgroundColor": "#15aabf",
+ "width": 138.45977783203125,
+ "height": 104.25430297851562,
+ "seed": 1106799383,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElementIds": []
+ },
+ {
+ "type": "rectangle",
+ "version": 485,
+ "versionNonce": 559667799,
+ "isDeleted": false,
+ "id": "7JDfqTDwIN2xz4PmaL4gw",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 0,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1654.8622436523438,
+ "y": 1208.4525833129885,
+ "strokeColor": "#000000",
+ "backgroundColor": "#15aabf",
+ "width": 138.45977783203125,
+ "height": 104.25430297851562,
+ "seed": 49925015,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElementIds": []
+ },
+ {
+ "type": "rectangle",
+ "version": 557,
+ "versionNonce": 380033689,
+ "isDeleted": false,
+ "id": "qDVE2iKSwuTdBdChLu82k",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 0,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1543.7459106445312,
+ "y": 1353.0563125610354,
+ "strokeColor": "#000000",
+ "backgroundColor": "#15aabf",
+ "width": 138.45977783203125,
+ "height": 104.25430297851562,
+ "seed": 1016936217,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElementIds": []
+ },
+ {
+ "type": "text",
+ "version": 686,
+ "versionNonce": 800880503,
+ "isDeleted": false,
+ "id": "oj0dvttEN4vwF-Kl_1lHe",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 0,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1051.7705644880025,
+ "y": 989.6699055262978,
+ "strokeColor": "#000000",
+ "backgroundColor": "#15aabf",
+ "width": 59,
+ "height": 46,
+ "seed": 529766617,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElementIds": [],
+ "fontSize": 20,
+ "fontFamily": 2,
+ "text": "Fleet\nServer",
+ "baseline": 41,
+ "textAlign": "center",
+ "verticalAlign": "middle"
+ },
+ {
+ "type": "text",
+ "version": 412,
+ "versionNonce": 302129017,
+ "isDeleted": false,
+ "id": "Rlg7jCRCcqGnqdtvrL4rK",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 0,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1460.5632019042969,
+ "y": 1230.2098159790041,
+ "strokeColor": "#000000",
+ "backgroundColor": "#15aabf",
+ "width": 59,
+ "height": 46,
+ "seed": 1214821849,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElementIds": [],
+ "fontSize": 20,
+ "fontFamily": 2,
+ "text": "Elastic\nAgent",
+ "baseline": 41,
+ "textAlign": "center",
+ "verticalAlign": "middle"
+ },
+ {
+ "type": "text",
+ "version": 445,
+ "versionNonce": 508326039,
+ "isDeleted": false,
+ "id": "edULedUulQbCH7XzbB3oc",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 0,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1689.2619323730469,
+ "y": 1234.8031692504885,
+ "strokeColor": "#000000",
+ "backgroundColor": "#15aabf",
+ "width": 59,
+ "height": 46,
+ "seed": 453746009,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElementIds": [],
+ "fontSize": 20,
+ "fontFamily": 2,
+ "text": "Elastic\nAgent",
+ "baseline": 41,
+ "textAlign": "center",
+ "verticalAlign": "middle"
+ },
+ {
+ "type": "text",
+ "version": 517,
+ "versionNonce": 258066521,
+ "isDeleted": false,
+ "id": "cFajl1LyB1pqxV__qFwAR",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 0,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1578.1455993652344,
+ "y": 1379.4068984985354,
+ "strokeColor": "#000000",
+ "backgroundColor": "#15aabf",
+ "width": 59,
+ "height": 46,
+ "seed": 1346904823,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElementIds": [],
+ "fontSize": 20,
+ "fontFamily": 2,
+ "text": "Elastic\nAgent",
+ "baseline": 41,
+ "textAlign": "center",
+ "verticalAlign": "middle"
+ },
+ {
+ "type": "text",
+ "version": 1007,
+ "versionNonce": 368912151,
+ "isDeleted": false,
+ "id": "WGfbkzIrz40PuSkc72Cce",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 0,
+ "opacity": 100,
+ "angle": 0,
+ "x": 609.0722743443082,
+ "y": 969.6241356985911,
+ "strokeColor": "#000000",
+ "backgroundColor": "#15aabf",
+ "width": 532,
+ "height": 138,
+ "seed": 1963132567,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElementIds": [
+ "XfXZwtl0lYMcyaqGm81Kp"
+ ],
+ "fontSize": 20,
+ "fontFamily": 2,
+ "text": "Fleet Server is a specialized instance of \nElastic agent that facilitates communication\nbetween agents and Kibana \n\nhttps://github.com/elastic/fleet-server\nhttps://www.elastic.co/guide/en/fleet/current/fleet-server.html",
+ "baseline": 133,
+ "textAlign": "left",
+ "verticalAlign": "top"
+ },
+ {
+ "type": "rectangle",
+ "version": 262,
+ "versionNonce": 412702009,
+ "isDeleted": false,
+ "id": "pvM13ftbV86OXPQbe4T6t",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "dotted",
+ "roughness": 0,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1384.455749511719,
+ "y": 1174.0904769897463,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 481.5649414062498,
+ "height": 308.5127258300781,
+ "seed": 1990800761,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElementIds": [
+ "rcBs7GMovRmTAgrwdgREc",
+ "pvB3kVjq9ORzyG3vGuzUv",
+ "yUYmtiUOwJFi9clnke8SS",
+ "nDieSVBYlEY32xmCjba1n"
+ ]
+ },
+ {
+ "type": "text",
+ "version": 310,
+ "versionNonce": 1480357591,
+ "isDeleted": false,
+ "id": "jxL8eFo0sUVQDJe3gOSKD",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "dotted",
+ "roughness": 0,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1542.77392578125,
+ "y": 1137.9500503540041,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 166,
+ "height": 23,
+ "seed": 1837892663,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElementIds": [],
+ "fontSize": 20,
+ "fontFamily": 2,
+ "text": "User Infrastructure",
+ "baseline": 18,
+ "textAlign": "left",
+ "verticalAlign": "top"
+ },
+ {
+ "type": "text",
+ "version": 337,
+ "versionNonce": 822887961,
+ "isDeleted": false,
+ "id": "YrnyLD8I1NiBU9BmUbTJQ",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "dotted",
+ "roughness": 0,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1912.2590942382812,
+ "y": 1175.54305267334,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 775,
+ "height": 138,
+ "seed": 1455174073,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElementIds": [],
+ "fontSize": 20,
+ "fontFamily": 2,
+ "text": "Users run many instances of Elastic Agent across their own infrastructure. Elastic Agent\nis a single unified process that ships data from a user's various hosts to Elasticsearch.\n\nhttps://www.elastic.co/guide/en/fleet/current/elastic-agent-installation-configuration.html\n\n",
+ "baseline": 133,
+ "textAlign": "left",
+ "verticalAlign": "top"
+ },
+ {
+ "type": "arrow",
+ "version": 1049,
+ "versionNonce": 1767212023,
+ "isDeleted": false,
+ "id": "rcBs7GMovRmTAgrwdgREc",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 0,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1477.545755637168,
+ "y": 1163.9214096069338,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 312.0071552604936,
+ "height": 147.13359654894543,
+ "seed": 1054102999,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElementIds": [],
+ "startBinding": {
+ "elementId": "pvM13ftbV86OXPQbe4T6t",
+ "focus": 0.3546665319688088,
+ "gap": 10.169067382812386
+ },
+ "endBinding": {
+ "elementId": "Imn7uBkfBKF8X1xHiyTHH",
+ "focus": -0.4140166821578487,
+ "gap": 13.323974609375
+ },
+ "lastCommittedPoint": null,
+ "startArrowhead": "arrow",
+ "endArrowhead": "arrow",
+ "points": [
+ [
+ 0,
+ 0
+ ],
+ [
+ -312.0071552604936,
+ -147.13359654894543
+ ]
+ ]
+ },
+ {
+ "type": "arrow",
+ "version": 1183,
+ "versionNonce": 1287536889,
+ "isDeleted": false,
+ "id": "XfXZwtl0lYMcyaqGm81Kp",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 0,
+ "opacity": 100,
+ "angle": 0,
+ "x": 647.0474255528405,
+ "y": 588.5818083021378,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 425.2489452715446,
+ "height": 357.9939657544336,
+ "seed": 265609785,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElementIds": [],
+ "startBinding": {
+ "elementId": "V2N6gERGsLagZObwBdXd7",
+ "focus": 0.10993484764691264,
+ "gap": 20.31184980604405
+ },
+ "endBinding": {
+ "elementId": "WGfbkzIrz40PuSkc72Cce",
+ "focus": 0.8810288444398049,
+ "gap": 23.0483616420197
+ },
+ "lastCommittedPoint": null,
+ "startArrowhead": "arrow",
+ "endArrowhead": "arrow",
+ "points": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 425.2489452715446,
+ 357.9939657544336
+ ]
+ ]
+ },
+ {
+ "type": "text",
+ "version": 789,
+ "versionNonce": 1242557719,
+ "isDeleted": false,
+ "id": "zkoyoEzKXeU5U4HIjMdDH",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 0,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1116.3367222377235,
+ "y": 749.5580869402207,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 500,
+ "height": 92,
+ "seed": 577559831,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElementIds": [],
+ "fontSize": 20,
+ "fontFamily": 2,
+ "text": "Fleet allows users to configure and manage their Elastic\nagent instances through policies and integrations. A user\ncan install and configure many integrations and many\nvariations of policies to fit their needs.",
+ "baseline": 87,
+ "textAlign": "left",
+ "verticalAlign": "top"
+ },
+ {
+ "type": "text",
+ "version": 164,
+ "versionNonce": 285365209,
+ "isDeleted": false,
+ "id": "tjvU1XwUIN5tQqqWHNLC0",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 0,
+ "opacity": 100,
+ "angle": 0,
+ "x": 492.0342668805806,
+ "y": 1412.2311761038645,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 375,
+ "height": 23,
+ "seed": 47976599,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElementIds": [],
+ "fontSize": 20,
+ "fontFamily": 2,
+ "text": "Elastic Agent ships data into Elasticsearch",
+ "baseline": 18,
+ "textAlign": "left",
+ "verticalAlign": "top"
+ },
+ {
+ "type": "text",
+ "version": 218,
+ "versionNonce": 903885367,
+ "isDeleted": false,
+ "id": "pSUCExEI8cZ734R4RVt_J",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 0,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1873.7646615164624,
+ "y": 243.03148651123064,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 78,
+ "height": 69,
+ "seed": 918997657,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElementIds": [],
+ "fontSize": 20,
+ "fontFamily": 2,
+ "text": "Elastic\nPackage\nRegistry",
+ "baseline": 64,
+ "textAlign": "center",
+ "verticalAlign": "middle"
+ },
+ {
+ "type": "text",
+ "version": 322,
+ "versionNonce": 1025274041,
+ "isDeleted": false,
+ "id": "xd1MPZCvDDnUaOOjuVJ60",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 0,
+ "opacity": 100,
+ "angle": 0,
+ "x": 2025.6554391043528,
+ "y": 233.5708541870119,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 599,
+ "height": 92,
+ "seed": 1783431543,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElementIds": [],
+ "fontSize": 20,
+ "fontFamily": 2,
+ "text": "EPR is a service that hosts Elastic Agent packages/integrations and\nfacilitates their installation\n\nhttps://github.com/elastic/integrations",
+ "baseline": 87,
+ "textAlign": "left",
+ "verticalAlign": "top"
+ },
+ {
+ "type": "arrow",
+ "version": 474,
+ "versionNonce": 95081303,
+ "isDeleted": false,
+ "id": "1MDWgu4QN3q_Sd2HSW47y",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 0,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1263.4936615088634,
+ "y": 494.9764491489956,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 558.7165610078778,
+ "height": 213.48906026083841,
+ "seed": 103520537,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElementIds": [],
+ "startBinding": {
+ "elementId": "Bjhq9XGHwH9zHMRZsbgW-",
+ "focus": -0.4649028979780671,
+ "gap": 14.971365792410893
+ },
+ "endBinding": {
+ "elementId": "zIFms6BgkiaXuCZ3Dstjq",
+ "focus": 0.3899896247441985,
+ "gap": 21.324550083705617
+ },
+ "lastCommittedPoint": null,
+ "startArrowhead": "arrow",
+ "endArrowhead": "arrow",
+ "points": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 558.7165610078778,
+ -213.48906026083841
+ ]
+ ]
+ },
+ {
+ "type": "arrow",
+ "version": 110,
+ "versionNonce": 497021337,
+ "isDeleted": false,
+ "id": "nDieSVBYlEY32xmCjba1n",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 0,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1374.3800223214287,
+ "y": 1324.043823242188,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 907.2858537946428,
+ "height": 745.058855329241,
+ "seed": 1198688215,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElementIds": [],
+ "startBinding": {
+ "elementId": "pvM13ftbV86OXPQbe4T6t",
+ "focus": 0.04062669074692265,
+ "gap": 10.075727190290195
+ },
+ "endBinding": {
+ "elementId": "V2N6gERGsLagZObwBdXd7",
+ "focus": 0.7306499810608883,
+ "gap": 17.800667898995812
+ },
+ "lastCommittedPoint": null,
+ "startArrowhead": "arrow",
+ "endArrowhead": "arrow",
+ "points": [
+ [
+ 0,
+ 0
+ ],
+ [
+ -869.7869001116071,
+ 7.085658482142662
+ ],
+ [
+ -907.2858537946428,
+ -737.9731968470984
+ ]
+ ]
+ },
+ {
+ "type": "text",
+ "version": 221,
+ "versionNonce": 29642871,
+ "isDeleted": false,
+ "id": "mdNyGlOBthXbeBh3YNbYi",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 0,
+ "opacity": 100,
+ "angle": 0,
+ "x": 956.0401785714289,
+ "y": -1.6747523716515573,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 642,
+ "height": 41,
+ "seed": 2068439865,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElementIds": [],
+ "fontSize": 36,
+ "fontFamily": 2,
+ "text": "Fleet - High Level Architecture Overview",
+ "baseline": 33,
+ "textAlign": "left",
+ "verticalAlign": "top"
+ }
+ ],
+ "appState": {
+ "gridSize": null,
+ "viewBackgroundColor": "#ffffff"
+ }
+}
\ No newline at end of file
diff --git a/x-pack/plugins/fleet/dev_docs/diagrams/architecture_overview/architecture_overview.png b/x-pack/plugins/fleet/dev_docs/diagrams/architecture_overview/architecture_overview.png
new file mode 100644
index 0000000000000..1457228f2e04a
Binary files /dev/null and b/x-pack/plugins/fleet/dev_docs/diagrams/architecture_overview/architecture_overview.png differ
diff --git a/x-pack/plugins/fleet/public/applications/fleet/hooks/use_breadcrumbs.tsx b/x-pack/plugins/fleet/public/applications/fleet/hooks/use_breadcrumbs.tsx
index 3b0ab9c62ca11..3149a454c6c52 100644
--- a/x-pack/plugins/fleet/public/applications/fleet/hooks/use_breadcrumbs.tsx
+++ b/x-pack/plugins/fleet/public/applications/fleet/hooks/use_breadcrumbs.tsx
@@ -113,6 +113,24 @@ const breadcrumbGetters: {
}),
},
],
+ upgrade_package_policy: ({ policyName, policyId }) => [
+ BASE_BREADCRUMB,
+ {
+ href: pagePathGetters.policies()[1],
+ text: i18n.translate('xpack.fleet.breadcrumbs.policiesPageTitle', {
+ defaultMessage: 'Agent policies',
+ }),
+ },
+ {
+ href: pagePathGetters.policy_details({ policyId })[1],
+ text: policyName,
+ },
+ {
+ text: i18n.translate('xpack.fleet.breadcrumbs.upgradePacagePolicyPageTitle', {
+ defaultMessage: 'Upgrade integration ',
+ }),
+ },
+ ],
agent_list: () => [
BASE_BREADCRUMB,
{
diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/services/has_invalid_but_required_var.ts b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/services/has_invalid_but_required_var.ts
index e204d86b51511..bf75b05f41b8d 100644
--- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/services/has_invalid_but_required_var.ts
+++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/services/has_invalid_but_required_var.ts
@@ -22,7 +22,11 @@ export const hasInvalidButRequiredVar = (
registryVar.required &&
(!packagePolicyVars ||
!packagePolicyVars[registryVar.name] ||
- validatePackagePolicyConfig(packagePolicyVars[registryVar.name], registryVar)?.length)
+ validatePackagePolicyConfig(
+ packagePolicyVars[registryVar.name],
+ registryVar,
+ registryVar.name
+ )?.length)
)
)
);
diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/index.tsx
index 67cff3e2d0304..ea027f95eb9e8 100644
--- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/index.tsx
+++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/index.tsx
@@ -525,15 +525,14 @@ export const EditPackagePolicyForm = memo<{
/>
) : (
<>
- {from === 'package' || from === 'package-edit' ? (
-
- ) : (
-
- )}
+
{formState === 'CONFIRM' && (
setFormState('VALID')}
/>
)}
-
{isUpgrade && dryRunData && (
<>
>
)}
-
{configurePackage}
-
{/* Extra space to accomodate the EuiBottomBar height */}
-
@@ -602,14 +597,56 @@ export const EditPackagePolicyForm = memo<{
);
});
-const PoliciesBreadcrumb: React.FunctionComponent<{ policyName: string; policyId: string }> = ({
- policyName,
- policyId,
-}) => {
+const Breadcrumb = memo<{
+ agentPolicyName: string;
+ from: EditPackagePolicyFrom;
+ packagePolicyName: string;
+ pkgkey: string;
+ pkgTitle: string;
+ policyId: string;
+}>(({ agentPolicyName, from, packagePolicyName, pkgkey, pkgTitle, policyId }) => {
+ let breadcrumb = ;
+
+ if (
+ from === 'package' ||
+ from === 'package-edit' ||
+ from === 'upgrade-from-integrations-policy-list'
+ ) {
+ breadcrumb = (
+
+ );
+ } else if (from === 'upgrade-from-fleet-policy-list') {
+ breadcrumb = ;
+ }
+
+ return breadcrumb;
+});
+
+const IntegrationsBreadcrumb = memo<{
+ pkgTitle: string;
+ policyName: string;
+ pkgkey: string;
+}>(({ pkgTitle, policyName, pkgkey }) => {
+ useIntegrationsBreadcrumbs('integration_policy_edit', { policyName, pkgTitle, pkgkey });
+ return null;
+});
+
+const PoliciesBreadcrumb: React.FunctionComponent<{
+ policyName: string;
+ policyId: string;
+}> = ({ policyName, policyId }) => {
useBreadcrumbs('edit_integration', { policyName, policyId });
return null;
};
+const UpgradeBreadcrumb: React.FunctionComponent<{
+ policyName: string;
+ policyId: string;
+}> = ({ policyName, policyId }) => {
+ useBreadcrumbs('upgrade_package_policy', { policyName, policyId });
+ return null;
+};
+
const UpgradeStatusCallout: React.FunctionComponent<{
dryRunData: UpgradePackagePolicyDryRunResponse;
}> = ({ dryRunData }) => {
@@ -658,7 +695,7 @@ const UpgradeStatusCallout: React.FunctionComponent<{
)}
- {isReadyForUpgrade ? (
+ {isReadyForUpgrade && currentPackagePolicy ? (
);
};
-
-const IntegrationsBreadcrumb = memo<{
- pkgTitle: string;
- policyName: string;
- pkgkey: string;
-}>(({ pkgTitle, policyName, pkgkey }) => {
- useIntegrationsBreadcrumbs('integration_policy_edit', { policyName, pkgTitle, pkgkey });
- return null;
-});
diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/policies/package_policies.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/policies/package_policies.tsx
index bdcfb1ebb6534..677ca545029fe 100644
--- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/policies/package_policies.tsx
+++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/policies/package_policies.tsx
@@ -220,7 +220,7 @@ export const PackagePoliciesPage = ({ name, version }: PackagePoliciesPanelProps
const hasUpgrade =
!!updatableIntegrationRecord &&
updatableIntegrationRecord.policiesToUpgrade.some(
- ({ id }) => id === packagePolicy.policy_id
+ ({ pkgPolicyId }) => pkgPolicyId === packagePolicy.id
);
return (
diff --git a/x-pack/plugins/fleet/public/index.ts b/x-pack/plugins/fleet/public/index.ts
index 585dc70ce2c42..b54d00eafdaab 100644
--- a/x-pack/plugins/fleet/public/index.ts
+++ b/x-pack/plugins/fleet/public/index.ts
@@ -5,6 +5,9 @@
* 2.0.
*/
+// TODO: https://github.com/elastic/kibana/issues/110901
+/* eslint-disable @kbn/eslint/no_export_all */
+
import type { PluginInitializerContext } from 'src/core/public';
import { FleetPlugin } from './plugin';
diff --git a/x-pack/plugins/fleet/server/services/package_policy.ts b/x-pack/plugins/fleet/server/services/package_policy.ts
index 6f74550ad45b7..8ff3c20b7aa15 100644
--- a/x-pack/plugins/fleet/server/services/package_policy.ts
+++ b/x-pack/plugins/fleet/server/services/package_policy.ts
@@ -944,7 +944,7 @@ export function overridePackageInputs(
// If there's no corresponding input on the original package policy, just
// take the override value from the new package as-is. This case typically
// occurs when inputs or package policies are added/removed between versions.
- if (!originalInput) {
+ if (originalInput === undefined) {
inputs.push(override as NewPackagePolicyInput);
continue;
}
@@ -958,7 +958,7 @@ export function overridePackageInputs(
}
if (override.vars) {
- originalInput = deepMergeVars(originalInput, override);
+ originalInput = deepMergeVars(originalInput, override) as NewPackagePolicyInput;
}
if (override.streams) {
@@ -967,6 +967,11 @@ export function overridePackageInputs(
(s) => s.data_stream.dataset === stream.data_stream.dataset
);
+ if (originalStream === undefined) {
+ originalInput.streams.push(stream);
+ continue;
+ }
+
if (typeof stream.enabled !== 'undefined' && originalStream) {
originalStream.enabled = stream.enabled;
}
@@ -1015,12 +1020,12 @@ export function overridePackageInputs(
}
function deepMergeVars(original: any, override: any): any {
- const result = { ...original };
-
- if (!result.vars || !override.vars) {
- return;
+ if (!original.vars) {
+ original.vars = { ...override.vars };
}
+ const result = { ...original };
+
const overrideVars = Array.isArray(override.vars)
? override.vars
: Object.entries(override.vars!).map(([key, rest]) => ({
@@ -1030,11 +1035,6 @@ function deepMergeVars(original: any, override: any): any {
for (const { name, ...overrideVal } of overrideVars) {
const originalVar = original.vars[name];
-
- if (!result.vars) {
- result.vars = {};
- }
-
result.vars[name] = { ...overrideVal, ...originalVar };
}
diff --git a/x-pack/plugins/graph/kibana.json b/x-pack/plugins/graph/kibana.json
index e13cd8a0adba1..463893e2425ac 100644
--- a/x-pack/plugins/graph/kibana.json
+++ b/x-pack/plugins/graph/kibana.json
@@ -9,7 +9,7 @@
"configPath": ["xpack", "graph"],
"requiredBundles": ["kibanaUtils", "kibanaReact", "home"],
"owner": {
- "name": "Kibana App",
- "githubTeam": "kibana-app"
+ "name": "Vis Editors",
+ "githubTeam": "kibana-vis-editors"
}
}
diff --git a/x-pack/plugins/graph/public/_main.scss b/x-pack/plugins/graph/public/_main.scss
index 6b32de32c06d0..22a849b0b2a60 100644
--- a/x-pack/plugins/graph/public/_main.scss
+++ b/x-pack/plugins/graph/public/_main.scss
@@ -21,6 +21,7 @@
*/
.gphNoUserSelect {
+ padding-right: $euiSizeXS;
user-select: none;
-webkit-touch-callout: none;
-webkit-tap-highlight-color: transparent;
diff --git a/x-pack/plugins/graph/public/angular/templates/_index.scss b/x-pack/plugins/graph/public/angular/templates/_index.scss
deleted file mode 100644
index 0e603b5c98cbe..0000000000000
--- a/x-pack/plugins/graph/public/angular/templates/_index.scss
+++ /dev/null
@@ -1,3 +0,0 @@
-@import './graph';
-@import './sidebar';
-@import './inspect';
diff --git a/x-pack/plugins/graph/public/angular/templates/index.html b/x-pack/plugins/graph/public/angular/templates/index.html
deleted file mode 100644
index 14c37cab9d9fd..0000000000000
--- a/x-pack/plugins/graph/public/angular/templates/index.html
+++ /dev/null
@@ -1,362 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/x-pack/plugins/graph/public/angular/templates/listing_ng_wrapper.html b/x-pack/plugins/graph/public/angular/templates/listing_ng_wrapper.html
deleted file mode 100644
index b2363ffbaa641..0000000000000
--- a/x-pack/plugins/graph/public/angular/templates/listing_ng_wrapper.html
+++ /dev/null
@@ -1,13 +0,0 @@
-
diff --git a/x-pack/plugins/graph/public/app.js b/x-pack/plugins/graph/public/app.js
deleted file mode 100644
index 13661798cabe6..0000000000000
--- a/x-pack/plugins/graph/public/app.js
+++ /dev/null
@@ -1,646 +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 _ from 'lodash';
-import { i18n } from '@kbn/i18n';
-import React from 'react';
-import { Provider } from 'react-redux';
-import { isColorDark, hexToRgb } from '@elastic/eui';
-
-import { toMountPoint } from '../../../../src/plugins/kibana_react/public';
-import { showSaveModal } from '../../../../src/plugins/saved_objects/public';
-
-import appTemplate from './angular/templates/index.html';
-import listingTemplate from './angular/templates/listing_ng_wrapper.html';
-import { getReadonlyBadge } from './badge';
-
-import { GraphApp } from './components/app';
-import { VennDiagram } from './components/venn_diagram';
-import { Listing } from './components/listing';
-import { Settings } from './components/settings';
-import { GraphVisualization } from './components/graph_visualization';
-
-import { createWorkspace } from './angular/graph_client_workspace.js';
-import { getEditUrl, getNewPath, getEditPath, setBreadcrumbs } from './services/url';
-import { createCachedIndexPatternProvider } from './services/index_pattern_cache';
-import { urlTemplateRegex } from './helpers/url_template';
-import { asAngularSyncedObservable } from './helpers/as_observable';
-import { colorChoices } from './helpers/style_choices';
-import { createGraphStore, datasourceSelector, hasFieldsSelector } from './state_management';
-import { formatHttpError } from './helpers/format_http_error';
-import {
- findSavedWorkspace,
- getSavedWorkspace,
- deleteSavedWorkspace,
-} from './helpers/saved_workspace_utils';
-import { InspectPanel } from './components/inspect_panel/inspect_panel';
-
-export function initGraphApp(angularModule, deps) {
- const {
- chrome,
- toastNotifications,
- savedObjectsClient,
- indexPatterns,
- addBasePath,
- getBasePath,
- data,
- capabilities,
- coreStart,
- storage,
- canEditDrillDownUrls,
- graphSavePolicy,
- overlays,
- savedObjects,
- setHeaderActionMenu,
- uiSettings,
- } = deps;
-
- const app = angularModule;
-
- app.directive('vennDiagram', function (reactDirective) {
- return reactDirective(VennDiagram);
- });
-
- app.directive('graphVisualization', function (reactDirective) {
- return reactDirective(GraphVisualization);
- });
-
- app.directive('graphListing', function (reactDirective) {
- return reactDirective(Listing, [
- ['coreStart', { watchDepth: 'reference' }],
- ['createItem', { watchDepth: 'reference' }],
- ['findItems', { watchDepth: 'reference' }],
- ['deleteItems', { watchDepth: 'reference' }],
- ['editItem', { watchDepth: 'reference' }],
- ['getViewUrl', { watchDepth: 'reference' }],
- ['listingLimit', { watchDepth: 'reference' }],
- ['hideWriteControls', { watchDepth: 'reference' }],
- ['capabilities', { watchDepth: 'reference' }],
- ['initialFilter', { watchDepth: 'reference' }],
- ['initialPageSize', { watchDepth: 'reference' }],
- ]);
- });
-
- app.directive('graphApp', function (reactDirective) {
- return reactDirective(
- GraphApp,
- [
- ['storage', { watchDepth: 'reference' }],
- ['isInitialized', { watchDepth: 'reference' }],
- ['currentIndexPattern', { watchDepth: 'reference' }],
- ['indexPatternProvider', { watchDepth: 'reference' }],
- ['isLoading', { watchDepth: 'reference' }],
- ['onQuerySubmit', { watchDepth: 'reference' }],
- ['initialQuery', { watchDepth: 'reference' }],
- ['confirmWipeWorkspace', { watchDepth: 'reference' }],
- ['coreStart', { watchDepth: 'reference' }],
- ['noIndexPatterns', { watchDepth: 'reference' }],
- ['reduxStore', { watchDepth: 'reference' }],
- ['pluginDataStart', { watchDepth: 'reference' }],
- ],
- { restrict: 'A' }
- );
- });
-
- app.directive('graphVisualization', function (reactDirective) {
- return reactDirective(GraphVisualization, undefined, { restrict: 'A' });
- });
-
- app.directive('inspectPanel', function (reactDirective) {
- return reactDirective(
- InspectPanel,
- [
- ['showInspect', { watchDepth: 'reference' }],
- ['lastRequest', { watchDepth: 'reference' }],
- ['lastResponse', { watchDepth: 'reference' }],
- ['indexPattern', { watchDepth: 'reference' }],
- ['uiSettings', { watchDepth: 'reference' }],
- ],
- { restrict: 'E' },
- {
- uiSettings,
- }
- );
- });
-
- app.config(function ($routeProvider) {
- $routeProvider
- .when('/home', {
- template: listingTemplate,
- badge: getReadonlyBadge,
- controller: function ($location, $scope) {
- $scope.listingLimit = savedObjects.settings.getListingLimit();
- $scope.initialPageSize = savedObjects.settings.getPerPage();
- $scope.create = () => {
- $location.url(getNewPath());
- };
- $scope.find = (search) => {
- return findSavedWorkspace(
- { savedObjectsClient, basePath: coreStart.http.basePath },
- search,
- $scope.listingLimit
- );
- };
- $scope.editItem = (workspace) => {
- $location.url(getEditPath(workspace));
- };
- $scope.getViewUrl = (workspace) => getEditUrl(addBasePath, workspace);
- $scope.delete = (workspaces) =>
- deleteSavedWorkspace(
- savedObjectsClient,
- workspaces.map(({ id }) => id)
- );
- $scope.capabilities = capabilities;
- $scope.initialFilter = $location.search().filter || '';
- $scope.coreStart = coreStart;
- setBreadcrumbs({ chrome });
- },
- })
- .when('/workspace/:id?', {
- template: appTemplate,
- badge: getReadonlyBadge,
- resolve: {
- savedWorkspace: function ($rootScope, $route, $location) {
- return $route.current.params.id
- ? getSavedWorkspace(savedObjectsClient, $route.current.params.id).catch(function (e) {
- toastNotifications.addError(e, {
- title: i18n.translate('xpack.graph.missingWorkspaceErrorMessage', {
- defaultMessage: "Couldn't load graph with ID",
- }),
- });
- $rootScope.$eval(() => {
- $location.path('/home');
- $location.replace();
- });
- // return promise that never returns to prevent the controller from loading
- return new Promise();
- })
- : getSavedWorkspace(savedObjectsClient);
- },
- indexPatterns: function () {
- return savedObjectsClient
- .find({
- type: 'index-pattern',
- fields: ['title', 'type'],
- perPage: 10000,
- })
- .then((response) => response.savedObjects);
- },
- GetIndexPatternProvider: function () {
- return indexPatterns;
- },
- },
- })
- .otherwise({
- redirectTo: '/home',
- });
- });
-
- //======== Controller for basic UI ==================
- app.controller('graphuiPlugin', function ($scope, $route, $location) {
- function handleError(err) {
- const toastTitle = i18n.translate('xpack.graph.errorToastTitle', {
- defaultMessage: 'Graph Error',
- description: '"Graph" is a product name and should not be translated.',
- });
- if (err instanceof Error) {
- toastNotifications.addError(err, {
- title: toastTitle,
- });
- } else {
- toastNotifications.addDanger({
- title: toastTitle,
- text: String(err),
- });
- }
- }
-
- async function handleHttpError(error) {
- toastNotifications.addDanger(formatHttpError(error));
- }
-
- // Replacement function for graphClientWorkspace's comms so
- // that it works with Kibana.
- function callNodeProxy(indexName, query, responseHandler) {
- const request = {
- body: JSON.stringify({
- index: indexName,
- query: query,
- }),
- };
- $scope.loading = true;
- return coreStart.http
- .post('../api/graph/graphExplore', request)
- .then(function (data) {
- const response = data.resp;
- if (response.timed_out) {
- toastNotifications.addWarning(
- i18n.translate('xpack.graph.exploreGraph.timedOutWarningText', {
- defaultMessage: 'Exploration timed out',
- })
- );
- }
- responseHandler(response);
- })
- .catch(handleHttpError)
- .finally(() => {
- $scope.loading = false;
- $scope.$digest();
- });
- }
-
- //Helper function for the graphClientWorkspace to perform a query
- const callSearchNodeProxy = function (indexName, query, responseHandler) {
- const request = {
- body: JSON.stringify({
- index: indexName,
- body: query,
- }),
- };
- $scope.loading = true;
- coreStart.http
- .post('../api/graph/searchProxy', request)
- .then(function (data) {
- const response = data.resp;
- responseHandler(response);
- })
- .catch(handleHttpError)
- .finally(() => {
- $scope.loading = false;
- $scope.$digest();
- });
- };
-
- $scope.indexPatternProvider = createCachedIndexPatternProvider(
- $route.current.locals.GetIndexPatternProvider.get
- );
-
- const store = createGraphStore({
- basePath: getBasePath(),
- addBasePath,
- indexPatternProvider: $scope.indexPatternProvider,
- indexPatterns: $route.current.locals.indexPatterns,
- createWorkspace: (indexPattern, exploreControls) => {
- const options = {
- indexName: indexPattern,
- vertex_fields: [],
- // Here we have the opportunity to look up labels for nodes...
- nodeLabeller: function () {
- // console.log(newNodes);
- },
- changeHandler: function () {
- //Allows DOM to update with graph layout changes.
- $scope.$apply();
- },
- graphExploreProxy: callNodeProxy,
- searchProxy: callSearchNodeProxy,
- exploreControls,
- };
- $scope.workspace = createWorkspace(options);
- },
- setLiveResponseFields: (fields) => {
- $scope.liveResponseFields = fields;
- },
- setUrlTemplates: (urlTemplates) => {
- $scope.urlTemplates = urlTemplates;
- },
- getWorkspace: () => {
- return $scope.workspace;
- },
- getSavedWorkspace: () => {
- return $route.current.locals.savedWorkspace;
- },
- notifications: coreStart.notifications,
- http: coreStart.http,
- overlays: coreStart.overlays,
- savedObjectsClient,
- showSaveModal,
- setWorkspaceInitialized: () => {
- $scope.workspaceInitialized = true;
- },
- savePolicy: graphSavePolicy,
- changeUrl: (newUrl) => {
- $scope.$evalAsync(() => {
- $location.url(newUrl);
- });
- },
- notifyAngular: () => {
- $scope.$digest();
- },
- chrome,
- I18nContext: coreStart.i18n.Context,
- });
-
- // register things on scope passed down to react components
- $scope.pluginDataStart = data;
- $scope.storage = storage;
- $scope.coreStart = coreStart;
- $scope.loading = false;
- $scope.reduxStore = store;
- $scope.savedWorkspace = $route.current.locals.savedWorkspace;
-
- // register things for legacy angular UI
- const allSavingDisabled = graphSavePolicy === 'none';
- $scope.spymode = 'request';
- $scope.colors = colorChoices;
- $scope.isColorDark = (color) => isColorDark(...hexToRgb(color));
- $scope.nodeClick = function (n, $event) {
- //Selection logic - shift key+click helps selects multiple nodes
- // Without the shift key we deselect all prior selections (perhaps not
- // a great idea for touch devices with no concept of shift key)
- if (!$event.shiftKey) {
- const prevSelection = n.isSelected;
- $scope.workspace.selectNone();
- n.isSelected = prevSelection;
- }
-
- if ($scope.workspace.toggleNodeSelection(n)) {
- $scope.selectSelected(n);
- } else {
- $scope.detail = null;
- }
- };
-
- $scope.clickEdge = function (edge) {
- $scope.workspace.getAllIntersections($scope.handleMergeCandidatesCallback, [
- edge.topSrc,
- edge.topTarget,
- ]);
- };
-
- $scope.submit = function (searchTerm) {
- $scope.workspaceInitialized = true;
- const numHops = 2;
- if (searchTerm.startsWith('{')) {
- try {
- const query = JSON.parse(searchTerm);
- if (query.vertices) {
- // Is a graph explore request
- $scope.workspace.callElasticsearch(query);
- } else {
- // Is a regular query DSL query
- $scope.workspace.search(query, $scope.liveResponseFields, numHops);
- }
- } catch (err) {
- handleError(err);
- }
- return;
- }
- $scope.workspace.simpleSearch(searchTerm, $scope.liveResponseFields, numHops);
- };
-
- $scope.selectSelected = function (node) {
- $scope.detail = {
- latestNodeSelection: node,
- };
- return ($scope.selectedSelectedVertex = node);
- };
-
- $scope.isSelectedSelected = function (node) {
- return $scope.selectedSelectedVertex === node;
- };
-
- $scope.openUrlTemplate = function (template) {
- const url = template.url;
- const newUrl = url.replace(urlTemplateRegex, template.encoder.encode($scope.workspace));
- window.open(newUrl, '_blank');
- };
-
- $scope.aceLoaded = (editor) => {
- editor.$blockScrolling = Infinity;
- };
-
- $scope.setDetail = function (data) {
- $scope.detail = data;
- };
-
- function canWipeWorkspace(callback, text, options) {
- if (!hasFieldsSelector(store.getState())) {
- callback();
- return;
- }
- const confirmModalOptions = {
- confirmButtonText: i18n.translate('xpack.graph.leaveWorkspace.confirmButtonLabel', {
- defaultMessage: 'Leave anyway',
- }),
- title: i18n.translate('xpack.graph.leaveWorkspace.modalTitle', {
- defaultMessage: 'Unsaved changes',
- }),
- 'data-test-subj': 'confirmModal',
- ...options,
- };
-
- overlays
- .openConfirm(
- text ||
- i18n.translate('xpack.graph.leaveWorkspace.confirmText', {
- defaultMessage: 'If you leave now, you will lose unsaved changes.',
- }),
- confirmModalOptions
- )
- .then((isConfirmed) => {
- if (isConfirmed) {
- callback();
- }
- });
- }
- $scope.confirmWipeWorkspace = canWipeWorkspace;
-
- $scope.performMerge = function (parentId, childId) {
- let found = true;
- while (found) {
- found = false;
- for (const i in $scope.detail.mergeCandidates) {
- if ($scope.detail.mergeCandidates.hasOwnProperty(i)) {
- const mc = $scope.detail.mergeCandidates[i];
- if (mc.id1 === childId || mc.id2 === childId) {
- $scope.detail.mergeCandidates.splice(i, 1);
- found = true;
- break;
- }
- }
- }
- }
- $scope.workspace.mergeIds(parentId, childId);
- $scope.detail = null;
- };
-
- $scope.handleMergeCandidatesCallback = function (termIntersects) {
- const mergeCandidates = [];
- termIntersects.forEach((ti) => {
- mergeCandidates.push({
- id1: ti.id1,
- id2: ti.id2,
- term1: ti.term1,
- term2: ti.term2,
- v1: ti.v1,
- v2: ti.v2,
- overlap: ti.overlap,
- });
- });
- $scope.detail = { mergeCandidates };
- };
-
- // ===== Menubar configuration =========
- $scope.setHeaderActionMenu = setHeaderActionMenu;
- $scope.topNavMenu = [];
- $scope.topNavMenu.push({
- key: 'new',
- label: i18n.translate('xpack.graph.topNavMenu.newWorkspaceLabel', {
- defaultMessage: 'New',
- }),
- description: i18n.translate('xpack.graph.topNavMenu.newWorkspaceAriaLabel', {
- defaultMessage: 'New Workspace',
- }),
- tooltip: i18n.translate('xpack.graph.topNavMenu.newWorkspaceTooltip', {
- defaultMessage: 'Create a new workspace',
- }),
- run: function () {
- canWipeWorkspace(function () {
- $scope.$evalAsync(() => {
- if ($location.url() === '/workspace/') {
- $route.reload();
- } else {
- $location.url('/workspace/');
- }
- });
- });
- },
- testId: 'graphNewButton',
- });
-
- // if saving is disabled using uiCapabilities, we don't want to render the save
- // button so it's consistent with all of the other applications
- if (capabilities.save) {
- // allSavingDisabled is based on the xpack.graph.savePolicy, we'll maintain this functionality
-
- $scope.topNavMenu.push({
- key: 'save',
- label: i18n.translate('xpack.graph.topNavMenu.saveWorkspace.enabledLabel', {
- defaultMessage: 'Save',
- }),
- description: i18n.translate('xpack.graph.topNavMenu.saveWorkspace.enabledAriaLabel', {
- defaultMessage: 'Save workspace',
- }),
- tooltip: () => {
- if (allSavingDisabled) {
- return i18n.translate('xpack.graph.topNavMenu.saveWorkspace.disabledTooltip', {
- defaultMessage:
- 'No changes to saved workspaces are permitted by the current save policy',
- });
- } else {
- return i18n.translate('xpack.graph.topNavMenu.saveWorkspace.enabledTooltip', {
- defaultMessage: 'Save this workspace',
- });
- }
- },
- disableButton: function () {
- return allSavingDisabled || !hasFieldsSelector(store.getState());
- },
- run: () => {
- store.dispatch({
- type: 'x-pack/graph/SAVE_WORKSPACE',
- payload: $route.current.locals.savedWorkspace,
- });
- },
- testId: 'graphSaveButton',
- });
- }
- $scope.topNavMenu.push({
- key: 'inspect',
- disableButton: function () {
- return $scope.workspace === null;
- },
- label: i18n.translate('xpack.graph.topNavMenu.inspectLabel', {
- defaultMessage: 'Inspect',
- }),
- description: i18n.translate('xpack.graph.topNavMenu.inspectAriaLabel', {
- defaultMessage: 'Inspect',
- }),
- run: () => {
- $scope.$evalAsync(() => {
- const curState = $scope.menus.showInspect;
- $scope.closeMenus();
- $scope.menus.showInspect = !curState;
- });
- },
- });
-
- $scope.topNavMenu.push({
- key: 'settings',
- disableButton: function () {
- return datasourceSelector(store.getState()).type === 'none';
- },
- label: i18n.translate('xpack.graph.topNavMenu.settingsLabel', {
- defaultMessage: 'Settings',
- }),
- description: i18n.translate('xpack.graph.topNavMenu.settingsAriaLabel', {
- defaultMessage: 'Settings',
- }),
- run: () => {
- const settingsObservable = asAngularSyncedObservable(
- () => ({
- blocklistedNodes: $scope.workspace ? [...$scope.workspace.blocklistedNodes] : undefined,
- unblocklistNode: $scope.workspace ? $scope.workspace.unblocklist : undefined,
- canEditDrillDownUrls: canEditDrillDownUrls,
- }),
- $scope.$digest.bind($scope)
- );
- coreStart.overlays.openFlyout(
- toMountPoint(
-
-
-
- ),
- {
- size: 'm',
- closeButtonAriaLabel: i18n.translate('xpack.graph.settings.closeLabel', {
- defaultMessage: 'Close',
- }),
- 'data-test-subj': 'graphSettingsFlyout',
- ownFocus: true,
- className: 'gphSettingsFlyout',
- maxWidth: 520,
- }
- );
- },
- });
-
- // Allow URLs to include a user-defined text query
- if ($route.current.params.query) {
- $scope.initialQuery = $route.current.params.query;
- const unbind = $scope.$watch('workspace', () => {
- if (!$scope.workspace) {
- return;
- }
- unbind();
- $scope.submit($route.current.params.query);
- });
- }
-
- $scope.menus = {
- showSettings: false,
- };
-
- $scope.closeMenus = () => {
- _.forOwn($scope.menus, function (_, key) {
- $scope.menus[key] = false;
- });
- };
-
- // Deal with situation of request to open saved workspace
- if ($route.current.locals.savedWorkspace.id) {
- store.dispatch({
- type: 'x-pack/graph/LOAD_WORKSPACE',
- payload: $route.current.locals.savedWorkspace,
- });
- } else {
- $scope.noIndexPatterns = $route.current.locals.indexPatterns.length === 0;
- }
- });
- //End controller
-}
diff --git a/x-pack/plugins/graph/public/application.ts b/x-pack/plugins/graph/public/application.ts
index 4d4b3c34de52b..7461a7b5fc172 100644
--- a/x-pack/plugins/graph/public/application.ts
+++ b/x-pack/plugins/graph/public/application.ts
@@ -5,20 +5,8 @@
* 2.0.
*/
-// inner angular imports
-// these are necessary to bootstrap the local angular.
-// They can stay even after NP cutover
-import angular from 'angular';
-import { i18nDirective, i18nFilter, I18nProvider } from '@kbn/i18n/angular';
+import { i18n } from '@kbn/i18n';
-import 'brace';
-import 'brace/mode/json';
-
-// required for i18nIdDirective and `ngSanitize` angular module
-import 'angular-sanitize';
-// required for ngRoute
-import 'angular-route';
-// type imports
import {
ChromeStart,
CoreStart,
@@ -28,23 +16,21 @@ import {
OverlayStart,
AppMountParameters,
IUiSettingsClient,
+ Capabilities,
+ ScopedHistory,
} from 'kibana/public';
-// @ts-ignore
-import { initGraphApp } from './app';
+import ReactDOM from 'react-dom';
import { DataPlugin, IndexPatternsContract } from '../../../../src/plugins/data/public';
import { LicensingPluginStart } from '../../licensing/public';
import { checkLicense } from '../common/check_license';
import { NavigationPublicPluginStart as NavigationStart } from '../../../../src/plugins/navigation/public';
import { Storage } from '../../../../src/plugins/kibana_utils/public';
-import {
- configureAppAngularModule,
- createTopNavDirective,
- createTopNavHelper,
- KibanaLegacyStart,
-} from '../../../../src/plugins/kibana_legacy/public';
+import { KibanaLegacyStart } from '../../../../src/plugins/kibana_legacy/public';
import './index.scss';
import { SavedObjectsStart } from '../../../../src/plugins/saved_objects/public';
+import { GraphSavePolicy } from './types';
+import { graphRouter } from './router';
/**
* These are dependencies of the Graph app besides the base dependencies
@@ -58,7 +44,7 @@ export interface GraphDependencies {
coreStart: CoreStart;
element: HTMLElement;
appBasePath: string;
- capabilities: Record>;
+ capabilities: Capabilities;
navigation: NavigationStart;
licensing: LicensingPluginStart;
chrome: ChromeStart;
@@ -70,22 +56,32 @@ export interface GraphDependencies {
getBasePath: () => string;
storage: Storage;
canEditDrillDownUrls: boolean;
- graphSavePolicy: string;
+ graphSavePolicy: GraphSavePolicy;
overlays: OverlayStart;
savedObjects: SavedObjectsStart;
kibanaLegacy: KibanaLegacyStart;
setHeaderActionMenu: AppMountParameters['setHeaderActionMenu'];
uiSettings: IUiSettingsClient;
+ history: ScopedHistory;
}
-export const renderApp = ({ appBasePath, element, kibanaLegacy, ...deps }: GraphDependencies) => {
+export type GraphServices = Omit;
+
+export const renderApp = ({ history, kibanaLegacy, element, ...deps }: GraphDependencies) => {
+ const { chrome, capabilities } = deps;
kibanaLegacy.loadFontAwesome();
- const graphAngularModule = createLocalAngularModule(deps.navigation);
- configureAppAngularModule(
- graphAngularModule,
- { core: deps.core, env: deps.pluginInitializerContext.env },
- true
- );
+
+ if (!capabilities.graph.save) {
+ chrome.setBadge({
+ text: i18n.translate('xpack.graph.badge.readOnly.text', {
+ defaultMessage: 'Read only',
+ }),
+ tooltip: i18n.translate('xpack.graph.badge.readOnly.tooltip', {
+ defaultMessage: 'Unable to save Graph workspaces',
+ }),
+ iconType: 'glasses',
+ });
+ }
const licenseSubscription = deps.licensing.license$.subscribe((license) => {
const info = checkLicense(license);
@@ -105,59 +101,19 @@ export const renderApp = ({ appBasePath, element, kibanaLegacy, ...deps }: Graph
}
});
- initGraphApp(graphAngularModule, deps);
- const $injector = mountGraphApp(appBasePath, element);
+ // dispatch synthetic hash change event to update hash history objects
+ // this is necessary because hash updates triggered by using popState won't trigger this event naturally.
+ const unlistenParentHistory = history.listen(() => {
+ window.dispatchEvent(new HashChangeEvent('hashchange'));
+ });
+
+ const app = graphRouter(deps);
+ ReactDOM.render(app, element);
+ element.setAttribute('class', 'gphAppWrapper');
+
return () => {
licenseSubscription.unsubscribe();
- $injector.get('$rootScope').$destroy();
+ unlistenParentHistory();
+ ReactDOM.unmountComponentAtNode(element);
};
};
-
-const mainTemplate = (basePath: string) => `
-
-
-`;
-
-const moduleName = 'app/graph';
-
-const thirdPartyAngularDependencies = ['ngSanitize', 'ngRoute', 'react', 'ui.bootstrap'];
-
-function mountGraphApp(appBasePath: string, element: HTMLElement) {
- const mountpoint = document.createElement('div');
- mountpoint.setAttribute('class', 'gphAppWrapper');
- // eslint-disable-next-line no-unsanitized/property
- mountpoint.innerHTML = mainTemplate(appBasePath);
- // bootstrap angular into detached element and attach it later to
- // make angular-within-angular possible
- const $injector = angular.bootstrap(mountpoint, [moduleName]);
- element.appendChild(mountpoint);
- element.setAttribute('class', 'gphAppWrapper');
- return $injector;
-}
-
-function createLocalAngularModule(navigation: NavigationStart) {
- createLocalI18nModule();
- createLocalTopNavModule(navigation);
-
- const graphAngularModule = angular.module(moduleName, [
- ...thirdPartyAngularDependencies,
- 'graphI18n',
- 'graphTopNav',
- ]);
- return graphAngularModule;
-}
-
-function createLocalTopNavModule(navigation: NavigationStart) {
- angular
- .module('graphTopNav', ['react'])
- .directive('kbnTopNav', createTopNavDirective)
- .directive('kbnTopNavHelper', createTopNavHelper(navigation.ui));
-}
-
-function createLocalI18nModule() {
- angular
- .module('graphI18n', [])
- .provider('i18n', I18nProvider)
- .filter('i18n', i18nFilter)
- .directive('i18nId', i18nDirective);
-}
diff --git a/x-pack/plugins/graph/public/components/listing.tsx b/x-pack/plugins/graph/public/apps/listing_route.tsx
similarity index 64%
rename from x-pack/plugins/graph/public/components/listing.tsx
rename to x-pack/plugins/graph/public/apps/listing_route.tsx
index 53fdab4a02885..e7457f18005e6 100644
--- a/x-pack/plugins/graph/public/components/listing.tsx
+++ b/x-pack/plugins/graph/public/apps/listing_route.tsx
@@ -5,30 +5,72 @@
* 2.0.
*/
+import React, { Fragment, useCallback, useEffect } from 'react';
import { i18n } from '@kbn/i18n';
import { FormattedMessage, I18nProvider } from '@kbn/i18n/react';
-import React, { Fragment } from 'react';
import { EuiEmptyPrompt, EuiLink, EuiButton } from '@elastic/eui';
-
-import { CoreStart, ApplicationStart } from 'kibana/public';
+import { ApplicationStart } from 'kibana/public';
+import { useHistory, useLocation } from 'react-router-dom';
import { TableListView } from '../../../../../src/plugins/kibana_react/public';
+import { deleteSavedWorkspace, findSavedWorkspace } from '../helpers/saved_workspace_utils';
+import { getEditPath, getEditUrl, getNewPath, setBreadcrumbs } from '../services/url';
import { GraphWorkspaceSavedObject } from '../types';
+import { GraphServices } from '../application';
-export interface ListingProps {
- coreStart: CoreStart;
- createItem: () => void;
- findItems: (query: string) => Promise<{ total: number; hits: GraphWorkspaceSavedObject[] }>;
- deleteItems: (records: GraphWorkspaceSavedObject[]) => Promise;
- editItem: (record: GraphWorkspaceSavedObject) => void;
- getViewUrl: (record: GraphWorkspaceSavedObject) => string;
- listingLimit: number;
- hideWriteControls: boolean;
- capabilities: { save: boolean; delete: boolean };
- initialFilter: string;
- initialPageSize: number;
+export interface ListingRouteProps {
+ deps: GraphServices;
}
-export function Listing(props: ListingProps) {
+export function ListingRoute({
+ deps: { chrome, savedObjects, savedObjectsClient, coreStart, capabilities, addBasePath },
+}: ListingRouteProps) {
+ const listingLimit = savedObjects.settings.getListingLimit();
+ const initialPageSize = savedObjects.settings.getPerPage();
+ const history = useHistory();
+ const query = new URLSearchParams(useLocation().search);
+ const initialFilter = query.get('filter') || '';
+
+ useEffect(() => {
+ setBreadcrumbs({ chrome });
+ }, [chrome]);
+
+ const createItem = useCallback(() => {
+ history.push(getNewPath());
+ }, [history]);
+
+ const findItems = useCallback(
+ (search: string) => {
+ return findSavedWorkspace(
+ { savedObjectsClient, basePath: coreStart.http.basePath },
+ search,
+ listingLimit
+ );
+ },
+ [coreStart.http.basePath, listingLimit, savedObjectsClient]
+ );
+
+ const editItem = useCallback(
+ (savedWorkspace: GraphWorkspaceSavedObject) => {
+ history.push(getEditPath(savedWorkspace));
+ },
+ [history]
+ );
+
+ const getViewUrl = useCallback(
+ (savedWorkspace: GraphWorkspaceSavedObject) => getEditUrl(addBasePath, savedWorkspace),
+ [addBasePath]
+ );
+
+ const deleteItems = useCallback(
+ async (savedWorkspaces: GraphWorkspaceSavedObject[]) => {
+ await deleteSavedWorkspace(
+ savedObjectsClient,
+ savedWorkspaces.map((cur) => cur.id!)
+ );
+ },
+ [savedObjectsClient]
+ );
+
return (
{
+ /**
+ * It's temporary workaround, which should be removed after migration `workspace` to redux.
+ * Ref holds mutable `workspace` object. After each `workspace.methodName(...)` call
+ * (which might mutate `workspace` somehow), react state needs to be updated using
+ * `workspace.changeHandler()`.
+ */
+ const workspaceRef = useRef();
+ /**
+ * Providing `workspaceRef.current` to the hook dependencies or components itself
+ * will not leads to updates, therefore `renderCounter` is used to update react state.
+ */
+ const [renderCounter, setRenderCounter] = useState(0);
+ const history = useHistory();
+ const urlQuery = new URLSearchParams(useLocation().search).get('query');
+
+ const indexPatternProvider = useMemo(
+ () => createCachedIndexPatternProvider(getIndexPatternProvider.get),
+ [getIndexPatternProvider.get]
+ );
+
+ const { loading, callNodeProxy, callSearchNodeProxy, handleSearchQueryError } = useGraphLoader({
+ toastNotifications,
+ coreStart,
+ });
+
+ const services = useMemo(
+ () => ({
+ appName: 'graph',
+ storage,
+ data,
+ ...coreStart,
+ }),
+ [coreStart, data, storage]
+ );
+
+ const [store] = useState(() =>
+ createGraphStore({
+ basePath: getBasePath(),
+ addBasePath,
+ indexPatternProvider,
+ createWorkspace: (indexPattern, exploreControls) => {
+ const options = {
+ indexName: indexPattern,
+ vertex_fields: [],
+ // Here we have the opportunity to look up labels for nodes...
+ nodeLabeller() {
+ // console.log(newNodes);
+ },
+ changeHandler: () => setRenderCounter((cur) => cur + 1),
+ graphExploreProxy: callNodeProxy,
+ searchProxy: callSearchNodeProxy,
+ exploreControls,
+ };
+ const createdWorkspace = (workspaceRef.current = createWorkspace(options));
+ return createdWorkspace;
+ },
+ getWorkspace: () => workspaceRef.current,
+ notifications: coreStart.notifications,
+ http: coreStart.http,
+ overlays: coreStart.overlays,
+ savedObjectsClient,
+ showSaveModal,
+ savePolicy: graphSavePolicy,
+ changeUrl: (newUrl) => history.push(newUrl),
+ notifyReact: () => setRenderCounter((cur) => cur + 1),
+ chrome,
+ I18nContext: coreStart.i18n.Context,
+ handleSearchQueryError,
+ })
+ );
+
+ const { savedWorkspace, indexPatterns } = useWorkspaceLoader({
+ workspaceRef,
+ store,
+ savedObjectsClient,
+ toastNotifications,
+ });
+
+ if (!savedWorkspace || !indexPatterns) {
+ return null;
+ }
+
+ return (
+
+
+
+
+
+
+
+ );
+};
diff --git a/x-pack/plugins/graph/public/badge.js b/x-pack/plugins/graph/public/badge.js
deleted file mode 100644
index 128e30ee3f019..0000000000000
--- a/x-pack/plugins/graph/public/badge.js
+++ /dev/null
@@ -1,24 +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 { i18n } from '@kbn/i18n';
-
-export function getReadonlyBadge(uiCapabilities) {
- if (uiCapabilities.graph.save) {
- return null;
- }
-
- return {
- text: i18n.translate('xpack.graph.badge.readOnly.text', {
- defaultMessage: 'Read only',
- }),
- tooltip: i18n.translate('xpack.graph.badge.readOnly.tooltip', {
- defaultMessage: 'Unable to save Graph workspaces',
- }),
- iconType: 'glasses',
- };
-}
diff --git a/x-pack/plugins/graph/public/angular/templates/_graph.scss b/x-pack/plugins/graph/public/components/_graph.scss
similarity index 75%
rename from x-pack/plugins/graph/public/angular/templates/_graph.scss
rename to x-pack/plugins/graph/public/components/_graph.scss
index 5c2f5d5f7a881..706389304067c 100644
--- a/x-pack/plugins/graph/public/angular/templates/_graph.scss
+++ b/x-pack/plugins/graph/public/components/_graph.scss
@@ -1,11 +1,3 @@
-@mixin gphSvgText() {
- font-family: $euiFontFamily;
- font-size: $euiSizeS;
- line-height: $euiSizeM;
- fill: $euiColorDarkShade;
- color: $euiColorDarkShade;
-}
-
/**
* THE SVG Graph
* 1. Calculated px values come from the open/closed state of the global nav sidebar
diff --git a/x-pack/plugins/graph/public/components/_index.scss b/x-pack/plugins/graph/public/components/_index.scss
index a06209e7e4d34..743c24c896426 100644
--- a/x-pack/plugins/graph/public/components/_index.scss
+++ b/x-pack/plugins/graph/public/components/_index.scss
@@ -7,3 +7,6 @@
@import './settings/index';
@import './legacy_icon/index';
@import './field_manager/index';
+@import './graph';
+@import './sidebar';
+@import './inspect';
diff --git a/x-pack/plugins/graph/public/angular/templates/_inspect.scss b/x-pack/plugins/graph/public/components/_inspect.scss
similarity index 100%
rename from x-pack/plugins/graph/public/angular/templates/_inspect.scss
rename to x-pack/plugins/graph/public/components/_inspect.scss
diff --git a/x-pack/plugins/graph/public/angular/templates/_sidebar.scss b/x-pack/plugins/graph/public/components/_sidebar.scss
similarity index 82%
rename from x-pack/plugins/graph/public/angular/templates/_sidebar.scss
rename to x-pack/plugins/graph/public/components/_sidebar.scss
index e784649b250fa..831032231fe8c 100644
--- a/x-pack/plugins/graph/public/angular/templates/_sidebar.scss
+++ b/x-pack/plugins/graph/public/components/_sidebar.scss
@@ -24,6 +24,10 @@
padding: $euiSizeXS;
border-radius: $euiBorderRadius;
margin-bottom: $euiSizeXS;
+
+ & > span {
+ padding-right: $euiSizeXS;
+ }
}
.gphSidebar__panel {
@@ -35,8 +39,9 @@
* Vertex Select
*/
-.gphVertexSelect__button {
- margin: $euiSizeXS $euiSizeXS $euiSizeXS 0;
+.vertexSelectionTypesBar {
+ margin-top: 0;
+ margin-bottom: 0;
}
/**
@@ -68,15 +73,24 @@
background: $euiColorLightShade;
}
+/**
+ * Link summary
+ */
+
+.gphDrillDownIconLinks {
+ margin-top: .5 * $euiSizeXS;
+ margin-bottom: .5 * $euiSizeXS;
+}
+
/**
* Link summary
*/
.gphLinkSummary__term--1 {
- color:$euiColorDanger;
+ color: $euiColorDanger;
}
.gphLinkSummary__term--2 {
- color:$euiColorPrimary;
+ color: $euiColorPrimary;
}
.gphLinkSummary__term--1-2 {
color: mix($euiColorDanger, $euiColorPrimary);
diff --git a/x-pack/plugins/graph/public/components/app.tsx b/x-pack/plugins/graph/public/components/app.tsx
deleted file mode 100644
index fbe7f2d3ebe86..0000000000000
--- a/x-pack/plugins/graph/public/components/app.tsx
+++ /dev/null
@@ -1,76 +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 { EuiSpacer } from '@elastic/eui';
-
-import { DataPublicPluginStart } from 'src/plugins/data/public';
-import { Provider } from 'react-redux';
-import React, { useState } from 'react';
-import { I18nProvider } from '@kbn/i18n/react';
-import { CoreStart } from 'kibana/public';
-import { IStorageWrapper } from 'src/plugins/kibana_utils/public';
-import { FieldManager } from './field_manager';
-import { SearchBarProps, SearchBar } from './search_bar';
-import { GraphStore } from '../state_management';
-import { GuidancePanel } from './guidance_panel';
-import { GraphTitle } from './graph_title';
-
-import { KibanaContextProvider } from '../../../../../src/plugins/kibana_react/public';
-
-export interface GraphAppProps extends SearchBarProps {
- coreStart: CoreStart;
- // This is not named dataStart because of Angular treating data- prefix differently
- pluginDataStart: DataPublicPluginStart;
- storage: IStorageWrapper;
- reduxStore: GraphStore;
- isInitialized: boolean;
- noIndexPatterns: boolean;
-}
-
-export function GraphApp(props: GraphAppProps) {
- const [pickerOpen, setPickerOpen] = useState(false);
- const {
- coreStart,
- pluginDataStart,
- storage,
- reduxStore,
- noIndexPatterns,
- ...searchBarProps
- } = props;
-
- return (
-
-
-
- <>
- {props.isInitialized && }
-
-
-
-
-
- {!props.isInitialized && (
- {
- setPickerOpen(true);
- }}
- />
- )}
- >
-
-
-
- );
-}
diff --git a/x-pack/plugins/graph/public/components/control_panel/control_panel.tsx b/x-pack/plugins/graph/public/components/control_panel/control_panel.tsx
new file mode 100644
index 0000000000000..2946bc8ad56f5
--- /dev/null
+++ b/x-pack/plugins/graph/public/components/control_panel/control_panel.tsx
@@ -0,0 +1,143 @@
+/*
+ * 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 { connect } from 'react-redux';
+import {
+ ControlType,
+ TermIntersect,
+ UrlTemplate,
+ Workspace,
+ WorkspaceField,
+ WorkspaceNode,
+} from '../../types';
+import { urlTemplateRegex } from '../../helpers/url_template';
+import { SelectionToolBar } from './selection_tool_bar';
+import { ControlPanelToolBar } from './control_panel_tool_bar';
+import { SelectStyle } from './select_style';
+import { SelectedNodeEditor } from './selected_node_editor';
+import { MergeCandidates } from './merge_candidates';
+import { DrillDowns } from './drill_downs';
+import { DrillDownIconLinks } from './drill_down_icon_links';
+import { GraphState, liveResponseFieldsSelector, templatesSelector } from '../../state_management';
+import { SelectedNodeItem } from './selected_node_item';
+
+export interface TargetOptions {
+ toFields: WorkspaceField[];
+}
+
+interface ControlPanelProps {
+ renderCounter: number;
+ workspace: Workspace;
+ control: ControlType;
+ selectedNode?: WorkspaceNode;
+ colors: string[];
+ mergeCandidates: TermIntersect[];
+ onSetControl: (control: ControlType) => void;
+ selectSelected: (node: WorkspaceNode) => void;
+}
+
+interface ControlPanelStateProps {
+ urlTemplates: UrlTemplate[];
+ liveResponseFields: WorkspaceField[];
+}
+
+const ControlPanelComponent = ({
+ workspace,
+ liveResponseFields,
+ urlTemplates,
+ control,
+ selectedNode,
+ colors,
+ mergeCandidates,
+ onSetControl,
+ selectSelected,
+}: ControlPanelProps & ControlPanelStateProps) => {
+ const hasNodes = workspace.nodes.length === 0;
+
+ const openUrlTemplate = (template: UrlTemplate) => {
+ const url = template.url;
+ const newUrl = url.replace(urlTemplateRegex, template.encoder.encode(workspace!));
+ window.open(newUrl, '_blank');
+ };
+
+ const onSelectedFieldClick = (node: WorkspaceNode) => {
+ selectSelected(node);
+ workspace.changeHandler();
+ };
+
+ const onDeselectNode = (node: WorkspaceNode) => {
+ workspace.deselectNode(node);
+ workspace.changeHandler();
+ onSetControl('none');
+ };
+
+ return (
+
+ );
+};
+
+export const ControlPanel = connect((state: GraphState) => ({
+ urlTemplates: templatesSelector(state),
+ liveResponseFields: liveResponseFieldsSelector(state),
+}))(ControlPanelComponent);
diff --git a/x-pack/plugins/graph/public/components/control_panel/control_panel_tool_bar.tsx b/x-pack/plugins/graph/public/components/control_panel/control_panel_tool_bar.tsx
new file mode 100644
index 0000000000000..37a9c003f7682
--- /dev/null
+++ b/x-pack/plugins/graph/public/components/control_panel/control_panel_tool_bar.tsx
@@ -0,0 +1,230 @@
+/*
+ * 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 { EuiFlexGroup, EuiFlexItem, EuiToolTip } from '@elastic/eui';
+import { ControlType, Workspace, WorkspaceField } from '../../types';
+
+interface ControlPanelToolBarProps {
+ workspace: Workspace;
+ liveResponseFields: WorkspaceField[];
+ onSetControl: (action: ControlType) => void;
+}
+
+export const ControlPanelToolBar = ({
+ workspace,
+ onSetControl,
+ liveResponseFields,
+}: ControlPanelToolBarProps) => {
+ const haveNodes = workspace.nodes.length === 0;
+
+ const undoButtonMsg = i18n.translate('xpack.graph.sidebar.topMenu.undoButtonTooltip', {
+ defaultMessage: 'Undo',
+ });
+ const redoButtonMsg = i18n.translate('xpack.graph.sidebar.topMenu.redoButtonTooltip', {
+ defaultMessage: 'Redo',
+ });
+ const expandButtonMsg = i18n.translate(
+ 'xpack.graph.sidebar.topMenu.expandSelectionButtonTooltip',
+ {
+ defaultMessage: 'Expand selection',
+ }
+ );
+ const addLinksButtonMsg = i18n.translate('xpack.graph.sidebar.topMenu.addLinksButtonTooltip', {
+ defaultMessage: 'Add links between existing terms',
+ });
+ const removeVerticesButtonMsg = i18n.translate(
+ 'xpack.graph.sidebar.topMenu.removeVerticesButtonTooltip',
+ {
+ defaultMessage: 'Remove vertices from workspace',
+ }
+ );
+ const blocklistButtonMsg = i18n.translate('xpack.graph.sidebar.topMenu.blocklistButtonTooltip', {
+ defaultMessage: 'Block selection from appearing in workspace',
+ });
+ const customStyleButtonMsg = i18n.translate(
+ 'xpack.graph.sidebar.topMenu.customStyleButtonTooltip',
+ {
+ defaultMessage: 'Custom style selected vertices',
+ }
+ );
+ const drillDownButtonMsg = i18n.translate('xpack.graph.sidebar.topMenu.drillDownButtonTooltip', {
+ defaultMessage: 'Drill down',
+ });
+ const runLayoutButtonMsg = i18n.translate('xpack.graph.sidebar.topMenu.runLayoutButtonTooltip', {
+ defaultMessage: 'Run layout',
+ });
+ const pauseLayoutButtonMsg = i18n.translate(
+ 'xpack.graph.sidebar.topMenu.pauseLayoutButtonTooltip',
+ {
+ defaultMessage: 'Pause layout',
+ }
+ );
+
+ const onUndoClick = () => workspace.undo();
+ const onRedoClick = () => workspace.redo();
+ const onExpandButtonClick = () => {
+ onSetControl('none');
+ workspace.expandSelecteds({ toFields: liveResponseFields });
+ };
+ const onAddLinksClick = () => workspace.fillInGraph();
+ const onRemoveVerticesClick = () => {
+ onSetControl('none');
+ workspace.deleteSelection();
+ };
+ const onBlockListClick = () => workspace.blocklistSelection();
+ const onCustomStyleClick = () => onSetControl('style');
+ const onDrillDownClick = () => onSetControl('drillDowns');
+ const onRunLayoutClick = () => workspace.runLayout();
+ const onPauseLayoutClick = () => {
+ workspace.stopLayout();
+ workspace.changeHandler();
+ };
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {(workspace.nodes.length === 0 || workspace.force === null) && (
+
+
+
+
+
+ )}
+
+ {workspace.force !== null && workspace.nodes.length > 0 && (
+
+
+
+
+
+ )}
+
+ );
+};
diff --git a/x-pack/plugins/graph/public/components/control_panel/drill_down_icon_links.tsx b/x-pack/plugins/graph/public/components/control_panel/drill_down_icon_links.tsx
new file mode 100644
index 0000000000000..8d92d6ca04007
--- /dev/null
+++ b/x-pack/plugins/graph/public/components/control_panel/drill_down_icon_links.tsx
@@ -0,0 +1,61 @@
+/*
+ * 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 { EuiFlexGroup, EuiFlexItem, EuiToolTip } from '@elastic/eui';
+import React from 'react';
+import { UrlTemplate } from '../../types';
+
+interface UrlTemplateButtonsProps {
+ urlTemplates: UrlTemplate[];
+ hasNodes: boolean;
+ openUrlTemplate: (template: UrlTemplate) => void;
+}
+
+export const DrillDownIconLinks = ({
+ hasNodes,
+ urlTemplates,
+ openUrlTemplate,
+}: UrlTemplateButtonsProps) => {
+ const drillDownsWithIcons = urlTemplates.filter(
+ ({ icon }: UrlTemplate) => icon && icon.class !== ''
+ );
+
+ if (drillDownsWithIcons.length === 0) {
+ return null;
+ }
+
+ const drillDowns = drillDownsWithIcons.map((cur) => {
+ const onUrlTemplateClick = () => openUrlTemplate(cur);
+
+ return (
+
+
+
+
+
+ );
+ });
+
+ return (
+
+ {drillDowns}
+
+ );
+};
diff --git a/x-pack/plugins/graph/public/components/control_panel/drill_downs.tsx b/x-pack/plugins/graph/public/components/control_panel/drill_downs.tsx
new file mode 100644
index 0000000000000..9d0dfdc7ba705
--- /dev/null
+++ b/x-pack/plugins/graph/public/components/control_panel/drill_downs.tsx
@@ -0,0 +1,55 @@
+/*
+ * 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 { UrlTemplate } from '../../types';
+
+interface DrillDownsProps {
+ urlTemplates: UrlTemplate[];
+ openUrlTemplate: (template: UrlTemplate) => void;
+}
+
+export const DrillDowns = ({ urlTemplates, openUrlTemplate }: DrillDownsProps) => {
+ return (
+
+
+
+ {i18n.translate('xpack.graph.sidebar.drillDownsTitle', {
+ defaultMessage: 'Drill-downs',
+ })}
+
+
+
+ {urlTemplates.length === 0 && (
+
+ {i18n.translate('xpack.graph.sidebar.drillDowns.noDrillDownsHelpText', {
+ defaultMessage: 'Configure drill-downs from the settings menu',
+ })}
+
+ )}
+
+
+ {urlTemplates.map((urlTemplate) => {
+ const onOpenUrlTemplate = () => openUrlTemplate(urlTemplate);
+
+ return (
+ -
+ {urlTemplate.icon && (
+ {urlTemplate.icon?.code}
+ )}
+
+ {urlTemplate.description}
+
+
+ );
+ })}
+
+
+
+ );
+};
diff --git a/x-pack/plugins/graph/public/components/control_panel/index.ts b/x-pack/plugins/graph/public/components/control_panel/index.ts
new file mode 100644
index 0000000000000..7c3ab15baea2d
--- /dev/null
+++ b/x-pack/plugins/graph/public/components/control_panel/index.ts
@@ -0,0 +1,8 @@
+/*
+ * 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 * from './control_panel';
diff --git a/x-pack/plugins/graph/public/components/control_panel/merge_candidates.tsx b/x-pack/plugins/graph/public/components/control_panel/merge_candidates.tsx
new file mode 100644
index 0000000000000..cc380993ef996
--- /dev/null
+++ b/x-pack/plugins/graph/public/components/control_panel/merge_candidates.tsx
@@ -0,0 +1,137 @@
+/*
+ * 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 { EuiToolTip } from '@elastic/eui';
+import { ControlType, TermIntersect, Workspace } from '../../types';
+import { VennDiagram } from '../venn_diagram';
+
+interface MergeCandidatesProps {
+ workspace: Workspace;
+ mergeCandidates: TermIntersect[];
+ onSetControl: (control: ControlType) => void;
+}
+
+export const MergeCandidates = ({
+ workspace,
+ mergeCandidates,
+ onSetControl,
+}: MergeCandidatesProps) => {
+ const performMerge = (parentId: string, childId: string) => {
+ const tempMergeCandidates = [...mergeCandidates];
+ let found = true;
+ while (found) {
+ found = false;
+
+ for (let i = 0; i < tempMergeCandidates.length; i++) {
+ const term = tempMergeCandidates[i];
+ if (term.id1 === childId || term.id2 === childId) {
+ tempMergeCandidates.splice(i, 1);
+ found = true;
+ break;
+ }
+ }
+ }
+ workspace.mergeIds(parentId, childId);
+ onSetControl('none');
+ };
+
+ return (
+
+
+
+ {i18n.translate('xpack.graph.sidebar.linkSummaryTitle', {
+ defaultMessage: 'Link summary',
+ })}
+
+ {mergeCandidates.map((mc) => {
+ const mergeTerm1ToTerm2ButtonMsg = i18n.translate(
+ 'xpack.graph.sidebar.linkSummary.mergeTerm1ToTerm2ButtonTooltip',
+ {
+ defaultMessage: 'Merge {term1} into {term2}',
+ values: { term1: mc.term1, term2: mc.term2 },
+ }
+ );
+ const mergeTerm2ToTerm1ButtonMsg = i18n.translate(
+ 'xpack.graph.sidebar.linkSummary.mergeTerm2ToTerm1ButtonTooltip',
+ {
+ defaultMessage: 'Merge {term2} into {term1}',
+ values: { term1: mc.term1, term2: mc.term2 },
+ }
+ );
+ const leftTermCountMsg = i18n.translate(
+ 'xpack.graph.sidebar.linkSummary.leftTermCountTooltip',
+ {
+ defaultMessage: '{count} documents have term {term}',
+ values: { count: mc.v1, term: mc.term1 },
+ }
+ );
+ const bothTermsCountMsg = i18n.translate(
+ 'xpack.graph.sidebar.linkSummary.bothTermsCountTooltip',
+ {
+ defaultMessage: '{count} documents have both terms',
+ values: { count: mc.overlap },
+ }
+ );
+ const rightTermCountMsg = i18n.translate(
+ 'xpack.graph.sidebar.linkSummary.rightTermCountTooltip',
+ {
+ defaultMessage: '{count} documents have term {term}',
+ values: { count: mc.v2, term: mc.term2 },
+ }
+ );
+
+ const onMergeTerm1ToTerm2Click = () => performMerge(mc.id2, mc.id1);
+ const onMergeTerm2ToTerm1Click = () => performMerge(mc.id1, mc.id2);
+
+ return (
+
+
+
+
+
+
+ {mc.term1}
+ {mc.term2}
+
+
+
+
+
+
+
+
+
+ {mc.v1}
+
+
+ ({mc.overlap})
+
+
+ {mc.v2}
+
+
+ );
+ })}
+
+ );
+};
diff --git a/x-pack/plugins/graph/public/components/control_panel/select_style.tsx b/x-pack/plugins/graph/public/components/control_panel/select_style.tsx
new file mode 100644
index 0000000000000..2dbefc7d24459
--- /dev/null
+++ b/x-pack/plugins/graph/public/components/control_panel/select_style.tsx
@@ -0,0 +1,45 @@
+/*
+ * 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 { Workspace } from '../../types';
+
+interface SelectStyleProps {
+ workspace: Workspace;
+ colors: string[];
+}
+
+export const SelectStyle = ({ colors, workspace }: SelectStyleProps) => {
+ return (
+
+
+
+ {i18n.translate('xpack.graph.sidebar.styleVerticesTitle', {
+ defaultMessage: 'Style selected vertices',
+ })}
+
+
+
+ {colors.map((c) => {
+ const onSelectColor = () => {
+ workspace.colorSelected(c);
+ workspace.changeHandler();
+ };
+ return (
+
+ );
+ })}
+
+
+ );
+};
diff --git a/x-pack/plugins/graph/public/components/control_panel/selected_node_editor.tsx b/x-pack/plugins/graph/public/components/control_panel/selected_node_editor.tsx
new file mode 100644
index 0000000000000..a0eed56fac672
--- /dev/null
+++ b/x-pack/plugins/graph/public/components/control_panel/selected_node_editor.tsx
@@ -0,0 +1,100 @@
+/*
+ * 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 { EuiToolTip } from '@elastic/eui';
+import { FormattedMessage } from '@kbn/i18n/react';
+import { Workspace, WorkspaceNode } from '../../types';
+
+interface SelectedNodeEditorProps {
+ workspace: Workspace;
+ selectedNode: WorkspaceNode;
+}
+
+export const SelectedNodeEditor = ({ workspace, selectedNode }: SelectedNodeEditorProps) => {
+ const groupButtonMsg = i18n.translate('xpack.graph.sidebar.groupButtonTooltip', {
+ defaultMessage: 'group the currently selected items into {latestSelectionLabel}',
+ values: { latestSelectionLabel: selectedNode.label },
+ });
+ const ungroupButtonMsg = i18n.translate('xpack.graph.sidebar.ungroupButtonTooltip', {
+ defaultMessage: 'ungroup {latestSelectionLabel}',
+ values: { latestSelectionLabel: selectedNode.label },
+ });
+
+ const onGroupButtonClick = () => {
+ workspace.groupSelections(selectedNode);
+ };
+ const onClickUngroup = () => {
+ workspace.ungroup(selectedNode);
+ };
+ const onChangeSelectedVertexLabel = (event: React.ChangeEvent) => {
+ selectedNode.label = event.target.value;
+ workspace.changeHandler();
+ };
+
+ return (
+
+
+ {selectedNode.icon && }
+ {selectedNode.data.field} {selectedNode.data.term}
+
+
+ {(workspace.selectedNodes.length > 1 ||
+ (workspace.selectedNodes.length > 0 && workspace.selectedNodes[0] !== selectedNode)) && (
+
+
+
+ )}
+
+ {selectedNode.numChildren > 0 && (
+
+
+
+ )}
+
+
+
+ );
+};
diff --git a/x-pack/plugins/graph/public/components/control_panel/selected_node_item.tsx b/x-pack/plugins/graph/public/components/control_panel/selected_node_item.tsx
new file mode 100644
index 0000000000000..11df3b5d52086
--- /dev/null
+++ b/x-pack/plugins/graph/public/components/control_panel/selected_node_item.tsx
@@ -0,0 +1,63 @@
+/*
+ * 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 { hexToRgb, isColorDark } from '@elastic/eui';
+import classNames from 'classnames';
+import React from 'react';
+import { WorkspaceNode } from '../../types';
+
+const isHexColorDark = (color: string) => isColorDark(...hexToRgb(color));
+
+interface SelectedNodeItemProps {
+ node: WorkspaceNode;
+ isHighlighted: boolean;
+ onDeselectNode: (node: WorkspaceNode) => void;
+ onSelectedFieldClick: (node: WorkspaceNode) => void;
+}
+
+export const SelectedNodeItem = ({
+ node,
+ isHighlighted,
+ onSelectedFieldClick,
+ onDeselectNode,
+}: SelectedNodeItemProps) => {
+ const fieldClasses = classNames('gphSelectionList__field', {
+ ['gphSelectionList__field--selected']: isHighlighted,
+ });
+ const fieldIconClasses = classNames('fa', 'gphNode__text', 'gphSelectionList__icon', {
+ ['gphNode__text--inverse']: isHexColorDark(node.color),
+ });
+
+ return (
+ onSelectedFieldClick(node)}>
+
+ {node.label}
+ {node.numChildren > 0 && (+{node.numChildren})}
+
+ );
+};
diff --git a/x-pack/plugins/graph/public/components/control_panel/selection_tool_bar.tsx b/x-pack/plugins/graph/public/components/control_panel/selection_tool_bar.tsx
new file mode 100644
index 0000000000000..e2e9771a8e9ef
--- /dev/null
+++ b/x-pack/plugins/graph/public/components/control_panel/selection_tool_bar.tsx
@@ -0,0 +1,136 @@
+/*
+ * 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 { EuiFlexGroup, EuiFlexItem, EuiToolTip } from '@elastic/eui';
+import { ControlType, Workspace } from '../../types';
+
+interface SelectionToolBarProps {
+ workspace: Workspace;
+ onSetControl: (data: ControlType) => void;
+}
+
+export const SelectionToolBar = ({ workspace, onSetControl }: SelectionToolBarProps) => {
+ const haveNodes = workspace.nodes.length === 0;
+
+ const selectAllButtonMsg = i18n.translate(
+ 'xpack.graph.sidebar.selections.selectAllButtonTooltip',
+ {
+ defaultMessage: 'Select all',
+ }
+ );
+ const selectNoneButtonMsg = i18n.translate(
+ 'xpack.graph.sidebar.selections.selectNoneButtonTooltip',
+ {
+ defaultMessage: 'Select none',
+ }
+ );
+ const invertSelectionButtonMsg = i18n.translate(
+ 'xpack.graph.sidebar.selections.invertSelectionButtonTooltip',
+ {
+ defaultMessage: 'Invert selection',
+ }
+ );
+ const selectNeighboursButtonMsg = i18n.translate(
+ 'xpack.graph.sidebar.selections.selectNeighboursButtonTooltip',
+ {
+ defaultMessage: 'Select neighbours',
+ }
+ );
+
+ const onSelectAllClick = () => {
+ onSetControl('none');
+ workspace.selectAll();
+ workspace.changeHandler();
+ };
+ const onSelectNoneClick = () => {
+ onSetControl('none');
+ workspace.selectNone();
+ workspace.changeHandler();
+ };
+ const onInvertSelectionClick = () => {
+ onSetControl('none');
+ workspace.selectInvert();
+ workspace.changeHandler();
+ };
+ const onSelectNeighboursClick = () => {
+ onSetControl('none');
+ workspace.selectNeighbours();
+ workspace.changeHandler();
+ };
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
diff --git a/x-pack/plugins/graph/public/components/graph_visualization/_graph_visualization.scss b/x-pack/plugins/graph/public/components/graph_visualization/_graph_visualization.scss
index caef2b6987ddd..0853ab4114595 100644
--- a/x-pack/plugins/graph/public/components/graph_visualization/_graph_visualization.scss
+++ b/x-pack/plugins/graph/public/components/graph_visualization/_graph_visualization.scss
@@ -1,3 +1,11 @@
+@mixin gphSvgText() {
+ font-family: $euiFontFamily;
+ font-size: $euiSizeS;
+ line-height: $euiSizeM;
+ fill: $euiColorDarkShade;
+ color: $euiColorDarkShade;
+}
+
.gphVisualization {
flex: 1;
display: flex;
diff --git a/x-pack/plugins/graph/public/components/graph_visualization/graph_visualization.test.tsx b/x-pack/plugins/graph/public/components/graph_visualization/graph_visualization.test.tsx
index f49b5bfd32da8..1ae556a79edcb 100644
--- a/x-pack/plugins/graph/public/components/graph_visualization/graph_visualization.test.tsx
+++ b/x-pack/plugins/graph/public/components/graph_visualization/graph_visualization.test.tsx
@@ -7,15 +7,13 @@
import React from 'react';
import { shallow } from 'enzyme';
-import {
- GraphVisualization,
- GroupAwareWorkspaceNode,
- GroupAwareWorkspaceEdge,
-} from './graph_visualization';
+import { GraphVisualization } from './graph_visualization';
+import { Workspace, WorkspaceEdge, WorkspaceNode } from '../../types';
describe('graph_visualization', () => {
- const nodes: GroupAwareWorkspaceNode[] = [
+ const nodes: WorkspaceNode[] = [
{
+ id: '1',
color: 'black',
data: {
field: 'A',
@@ -37,6 +35,7 @@ describe('graph_visualization', () => {
y: 5,
},
{
+ id: '2',
color: 'red',
data: {
field: 'B',
@@ -58,6 +57,7 @@ describe('graph_visualization', () => {
y: 9,
},
{
+ id: '3',
color: 'yellow',
data: {
field: 'C',
@@ -79,7 +79,7 @@ describe('graph_visualization', () => {
y: 9,
},
];
- const edges: GroupAwareWorkspaceEdge[] = [
+ const edges: WorkspaceEdge[] = [
{
isSelected: true,
label: '',
@@ -101,9 +101,32 @@ describe('graph_visualization', () => {
width: 2.2,
},
];
+ const workspace = ({
+ nodes,
+ edges,
+ selectNone: () => {},
+ changeHandler: jest.fn(),
+ toggleNodeSelection: jest.fn().mockImplementation((node: WorkspaceNode) => {
+ return !node.isSelected;
+ }),
+ getAllIntersections: jest.fn(),
+ } as unknown) as jest.Mocked;
+
+ beforeEach(() => {
+ jest.clearAllMocks();
+ });
+
it('should render empty workspace without data', () => {
- expect(shallow( {}} nodeClick={() => {}} />))
- .toMatchInlineSnapshot(`
+ expect(
+ shallow(
+ {}}
+ onSetControl={() => {}}
+ onSetMergeCandidates={() => {}}
+ />
+ )
+ ).toMatchInlineSnapshot(`