Support space-specific default routes (#44678)
This commit is contained in:
parent
51d734e9b8
commit
0bfa7ca5c6
|
@ -18,10 +18,6 @@
|
||||||
# default to `true` starting in Kibana 7.0.
|
# default to `true` starting in Kibana 7.0.
|
||||||
#server.rewriteBasePath: false
|
#server.rewriteBasePath: false
|
||||||
|
|
||||||
# Specifies the default route when opening Kibana. You can use this setting to modify
|
|
||||||
# the landing page when opening Kibana.
|
|
||||||
#server.defaultRoute: /app/kibana
|
|
||||||
|
|
||||||
# The maximum payload size in bytes for incoming server requests.
|
# The maximum payload size in bytes for incoming server requests.
|
||||||
#server.maxPayloadBytes: 1048576
|
#server.maxPayloadBytes: 1048576
|
||||||
|
|
||||||
|
|
|
@ -256,10 +256,6 @@ deprecation warning at startup. This setting cannot end in a slash (`/`).
|
||||||
`server.customResponseHeaders:`:: *Default: `{}`* Header names and values to
|
`server.customResponseHeaders:`:: *Default: `{}`* Header names and values to
|
||||||
send on all responses to the client from the Kibana server.
|
send on all responses to the client from the Kibana server.
|
||||||
|
|
||||||
[[server-default]]`server.defaultRoute:`:: *Default: "/app/kibana"* This setting
|
|
||||||
specifies the default route when opening Kibana. You can use this setting to
|
|
||||||
modify the landing page when opening Kibana. Supported on {ece}.
|
|
||||||
|
|
||||||
`server.host:`:: *Default: "localhost"* This setting specifies the host of the
|
`server.host:`:: *Default: "localhost"* This setting specifies the host of the
|
||||||
back end server.
|
back end server.
|
||||||
|
|
||||||
|
|
|
@ -70,6 +70,7 @@ exports[`AdvancedSettings should render normally 1`] = `
|
||||||
"readonly": false,
|
"readonly": false,
|
||||||
"requiresPageReload": false,
|
"requiresPageReload": false,
|
||||||
"type": "array",
|
"type": "array",
|
||||||
|
"validation": undefined,
|
||||||
"value": undefined,
|
"value": undefined,
|
||||||
},
|
},
|
||||||
Object {
|
Object {
|
||||||
|
@ -88,6 +89,7 @@ exports[`AdvancedSettings should render normally 1`] = `
|
||||||
"readonly": false,
|
"readonly": false,
|
||||||
"requiresPageReload": false,
|
"requiresPageReload": false,
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
|
"validation": undefined,
|
||||||
"value": undefined,
|
"value": undefined,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@ -108,6 +110,7 @@ exports[`AdvancedSettings should render normally 1`] = `
|
||||||
"readonly": false,
|
"readonly": false,
|
||||||
"requiresPageReload": false,
|
"requiresPageReload": false,
|
||||||
"type": "string",
|
"type": "string",
|
||||||
|
"validation": undefined,
|
||||||
"value": undefined,
|
"value": undefined,
|
||||||
},
|
},
|
||||||
Object {
|
Object {
|
||||||
|
@ -126,6 +129,7 @@ exports[`AdvancedSettings should render normally 1`] = `
|
||||||
"readonly": false,
|
"readonly": false,
|
||||||
"requiresPageReload": false,
|
"requiresPageReload": false,
|
||||||
"type": "image",
|
"type": "image",
|
||||||
|
"validation": undefined,
|
||||||
"value": undefined,
|
"value": undefined,
|
||||||
},
|
},
|
||||||
Object {
|
Object {
|
||||||
|
@ -146,6 +150,7 @@ exports[`AdvancedSettings should render normally 1`] = `
|
||||||
"readonly": false,
|
"readonly": false,
|
||||||
"requiresPageReload": false,
|
"requiresPageReload": false,
|
||||||
"type": "json",
|
"type": "json",
|
||||||
|
"validation": undefined,
|
||||||
"value": undefined,
|
"value": undefined,
|
||||||
},
|
},
|
||||||
Object {
|
Object {
|
||||||
|
@ -164,6 +169,7 @@ exports[`AdvancedSettings should render normally 1`] = `
|
||||||
"readonly": false,
|
"readonly": false,
|
||||||
"requiresPageReload": false,
|
"requiresPageReload": false,
|
||||||
"type": "number",
|
"type": "number",
|
||||||
|
"validation": undefined,
|
||||||
"value": undefined,
|
"value": undefined,
|
||||||
},
|
},
|
||||||
Object {
|
Object {
|
||||||
|
@ -186,6 +192,7 @@ exports[`AdvancedSettings should render normally 1`] = `
|
||||||
"readonly": false,
|
"readonly": false,
|
||||||
"requiresPageReload": false,
|
"requiresPageReload": false,
|
||||||
"type": "select",
|
"type": "select",
|
||||||
|
"validation": undefined,
|
||||||
"value": undefined,
|
"value": undefined,
|
||||||
},
|
},
|
||||||
Object {
|
Object {
|
||||||
|
@ -204,6 +211,7 @@ exports[`AdvancedSettings should render normally 1`] = `
|
||||||
"readonly": false,
|
"readonly": false,
|
||||||
"requiresPageReload": false,
|
"requiresPageReload": false,
|
||||||
"type": "string",
|
"type": "string",
|
||||||
|
"validation": undefined,
|
||||||
"value": undefined,
|
"value": undefined,
|
||||||
},
|
},
|
||||||
Object {
|
Object {
|
||||||
|
@ -222,6 +230,7 @@ exports[`AdvancedSettings should render normally 1`] = `
|
||||||
"readonly": false,
|
"readonly": false,
|
||||||
"requiresPageReload": false,
|
"requiresPageReload": false,
|
||||||
"type": "json",
|
"type": "json",
|
||||||
|
"validation": undefined,
|
||||||
"value": undefined,
|
"value": undefined,
|
||||||
},
|
},
|
||||||
Object {
|
Object {
|
||||||
|
@ -240,6 +249,7 @@ exports[`AdvancedSettings should render normally 1`] = `
|
||||||
"readonly": false,
|
"readonly": false,
|
||||||
"requiresPageReload": false,
|
"requiresPageReload": false,
|
||||||
"type": "markdown",
|
"type": "markdown",
|
||||||
|
"validation": undefined,
|
||||||
"value": undefined,
|
"value": undefined,
|
||||||
},
|
},
|
||||||
Object {
|
Object {
|
||||||
|
@ -258,6 +268,7 @@ exports[`AdvancedSettings should render normally 1`] = `
|
||||||
"readonly": false,
|
"readonly": false,
|
||||||
"requiresPageReload": false,
|
"requiresPageReload": false,
|
||||||
"type": "number",
|
"type": "number",
|
||||||
|
"validation": undefined,
|
||||||
"value": undefined,
|
"value": undefined,
|
||||||
},
|
},
|
||||||
Object {
|
Object {
|
||||||
|
@ -280,6 +291,7 @@ exports[`AdvancedSettings should render normally 1`] = `
|
||||||
"readonly": false,
|
"readonly": false,
|
||||||
"requiresPageReload": false,
|
"requiresPageReload": false,
|
||||||
"type": "select",
|
"type": "select",
|
||||||
|
"validation": undefined,
|
||||||
"value": undefined,
|
"value": undefined,
|
||||||
},
|
},
|
||||||
Object {
|
Object {
|
||||||
|
@ -298,6 +310,7 @@ exports[`AdvancedSettings should render normally 1`] = `
|
||||||
"readonly": false,
|
"readonly": false,
|
||||||
"requiresPageReload": false,
|
"requiresPageReload": false,
|
||||||
"type": "string",
|
"type": "string",
|
||||||
|
"validation": undefined,
|
||||||
"value": undefined,
|
"value": undefined,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@ -342,6 +355,7 @@ exports[`AdvancedSettings should render normally 1`] = `
|
||||||
"readonly": false,
|
"readonly": false,
|
||||||
"requiresPageReload": false,
|
"requiresPageReload": false,
|
||||||
"type": "array",
|
"type": "array",
|
||||||
|
"validation": undefined,
|
||||||
"value": undefined,
|
"value": undefined,
|
||||||
},
|
},
|
||||||
Object {
|
Object {
|
||||||
|
@ -360,6 +374,7 @@ exports[`AdvancedSettings should render normally 1`] = `
|
||||||
"readonly": false,
|
"readonly": false,
|
||||||
"requiresPageReload": false,
|
"requiresPageReload": false,
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
|
"validation": undefined,
|
||||||
"value": undefined,
|
"value": undefined,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@ -380,6 +395,7 @@ exports[`AdvancedSettings should render normally 1`] = `
|
||||||
"readonly": false,
|
"readonly": false,
|
||||||
"requiresPageReload": false,
|
"requiresPageReload": false,
|
||||||
"type": "string",
|
"type": "string",
|
||||||
|
"validation": undefined,
|
||||||
"value": undefined,
|
"value": undefined,
|
||||||
},
|
},
|
||||||
Object {
|
Object {
|
||||||
|
@ -398,6 +414,7 @@ exports[`AdvancedSettings should render normally 1`] = `
|
||||||
"readonly": false,
|
"readonly": false,
|
||||||
"requiresPageReload": false,
|
"requiresPageReload": false,
|
||||||
"type": "image",
|
"type": "image",
|
||||||
|
"validation": undefined,
|
||||||
"value": undefined,
|
"value": undefined,
|
||||||
},
|
},
|
||||||
Object {
|
Object {
|
||||||
|
@ -418,6 +435,7 @@ exports[`AdvancedSettings should render normally 1`] = `
|
||||||
"readonly": false,
|
"readonly": false,
|
||||||
"requiresPageReload": false,
|
"requiresPageReload": false,
|
||||||
"type": "json",
|
"type": "json",
|
||||||
|
"validation": undefined,
|
||||||
"value": undefined,
|
"value": undefined,
|
||||||
},
|
},
|
||||||
Object {
|
Object {
|
||||||
|
@ -436,6 +454,7 @@ exports[`AdvancedSettings should render normally 1`] = `
|
||||||
"readonly": false,
|
"readonly": false,
|
||||||
"requiresPageReload": false,
|
"requiresPageReload": false,
|
||||||
"type": "number",
|
"type": "number",
|
||||||
|
"validation": undefined,
|
||||||
"value": undefined,
|
"value": undefined,
|
||||||
},
|
},
|
||||||
Object {
|
Object {
|
||||||
|
@ -458,6 +477,7 @@ exports[`AdvancedSettings should render normally 1`] = `
|
||||||
"readonly": false,
|
"readonly": false,
|
||||||
"requiresPageReload": false,
|
"requiresPageReload": false,
|
||||||
"type": "select",
|
"type": "select",
|
||||||
|
"validation": undefined,
|
||||||
"value": undefined,
|
"value": undefined,
|
||||||
},
|
},
|
||||||
Object {
|
Object {
|
||||||
|
@ -476,6 +496,7 @@ exports[`AdvancedSettings should render normally 1`] = `
|
||||||
"readonly": false,
|
"readonly": false,
|
||||||
"requiresPageReload": false,
|
"requiresPageReload": false,
|
||||||
"type": "string",
|
"type": "string",
|
||||||
|
"validation": undefined,
|
||||||
"value": undefined,
|
"value": undefined,
|
||||||
},
|
},
|
||||||
Object {
|
Object {
|
||||||
|
@ -494,6 +515,7 @@ exports[`AdvancedSettings should render normally 1`] = `
|
||||||
"readonly": false,
|
"readonly": false,
|
||||||
"requiresPageReload": false,
|
"requiresPageReload": false,
|
||||||
"type": "json",
|
"type": "json",
|
||||||
|
"validation": undefined,
|
||||||
"value": undefined,
|
"value": undefined,
|
||||||
},
|
},
|
||||||
Object {
|
Object {
|
||||||
|
@ -512,6 +534,7 @@ exports[`AdvancedSettings should render normally 1`] = `
|
||||||
"readonly": false,
|
"readonly": false,
|
||||||
"requiresPageReload": false,
|
"requiresPageReload": false,
|
||||||
"type": "markdown",
|
"type": "markdown",
|
||||||
|
"validation": undefined,
|
||||||
"value": undefined,
|
"value": undefined,
|
||||||
},
|
},
|
||||||
Object {
|
Object {
|
||||||
|
@ -530,6 +553,7 @@ exports[`AdvancedSettings should render normally 1`] = `
|
||||||
"readonly": false,
|
"readonly": false,
|
||||||
"requiresPageReload": false,
|
"requiresPageReload": false,
|
||||||
"type": "number",
|
"type": "number",
|
||||||
|
"validation": undefined,
|
||||||
"value": undefined,
|
"value": undefined,
|
||||||
},
|
},
|
||||||
Object {
|
Object {
|
||||||
|
@ -552,6 +576,7 @@ exports[`AdvancedSettings should render normally 1`] = `
|
||||||
"readonly": false,
|
"readonly": false,
|
||||||
"requiresPageReload": false,
|
"requiresPageReload": false,
|
||||||
"type": "select",
|
"type": "select",
|
||||||
|
"validation": undefined,
|
||||||
"value": undefined,
|
"value": undefined,
|
||||||
},
|
},
|
||||||
Object {
|
Object {
|
||||||
|
@ -570,6 +595,7 @@ exports[`AdvancedSettings should render normally 1`] = `
|
||||||
"readonly": false,
|
"readonly": false,
|
||||||
"requiresPageReload": false,
|
"requiresPageReload": false,
|
||||||
"type": "string",
|
"type": "string",
|
||||||
|
"validation": undefined,
|
||||||
"value": undefined,
|
"value": undefined,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@ -689,6 +715,7 @@ exports[`AdvancedSettings should render read-only when saving is disabled 1`] =
|
||||||
"readonly": false,
|
"readonly": false,
|
||||||
"requiresPageReload": false,
|
"requiresPageReload": false,
|
||||||
"type": "string",
|
"type": "string",
|
||||||
|
"validation": undefined,
|
||||||
"value": undefined,
|
"value": undefined,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@ -731,6 +758,7 @@ exports[`AdvancedSettings should render read-only when saving is disabled 1`] =
|
||||||
"readonly": false,
|
"readonly": false,
|
||||||
"requiresPageReload": false,
|
"requiresPageReload": false,
|
||||||
"type": "string",
|
"type": "string",
|
||||||
|
"validation": undefined,
|
||||||
"value": undefined,
|
"value": undefined,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@ -868,6 +896,7 @@ exports[`AdvancedSettings should render specific setting if given setting key 1`
|
||||||
"readonly": false,
|
"readonly": false,
|
||||||
"requiresPageReload": false,
|
"requiresPageReload": false,
|
||||||
"type": "string",
|
"type": "string",
|
||||||
|
"validation": undefined,
|
||||||
"value": undefined,
|
"value": undefined,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@ -910,6 +939,7 @@ exports[`AdvancedSettings should render specific setting if given setting key 1`
|
||||||
"readonly": false,
|
"readonly": false,
|
||||||
"requiresPageReload": false,
|
"requiresPageReload": false,
|
||||||
"type": "string",
|
"type": "string",
|
||||||
|
"validation": undefined,
|
||||||
"value": undefined,
|
"value": undefined,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
|
@ -3707,3 +3707,422 @@ exports[`Field for string setting should render user value if there is user valu
|
||||||
/>
|
/>
|
||||||
</EuiFlexGroup>
|
</EuiFlexGroup>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`Field for stringWithValidation setting should render as read only if saving is disabled 1`] = `
|
||||||
|
<EuiFlexGroup
|
||||||
|
className="mgtAdvancedSettings__field"
|
||||||
|
>
|
||||||
|
<EuiFlexItem
|
||||||
|
grow={false}
|
||||||
|
>
|
||||||
|
<EuiDescribedFormGroup
|
||||||
|
className="mgtAdvancedSettings__fieldWrapper"
|
||||||
|
description={
|
||||||
|
<React.Fragment>
|
||||||
|
<div
|
||||||
|
dangerouslySetInnerHTML={
|
||||||
|
Object {
|
||||||
|
"__html": "Description for String test validation setting",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</React.Fragment>
|
||||||
|
}
|
||||||
|
fullWidth={false}
|
||||||
|
gutterSize="l"
|
||||||
|
idAria="string:test-validation:setting-aria"
|
||||||
|
title={
|
||||||
|
<h3>
|
||||||
|
String test validation setting
|
||||||
|
|
||||||
|
</h3>
|
||||||
|
}
|
||||||
|
titleSize="xs"
|
||||||
|
>
|
||||||
|
<EuiFormRow
|
||||||
|
className="mgtAdvancedSettings__fieldRow"
|
||||||
|
describedByIds={
|
||||||
|
Array [
|
||||||
|
"string:test-validation:setting-aria",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
display="row"
|
||||||
|
error={null}
|
||||||
|
fullWidth={false}
|
||||||
|
hasEmptyLabelSpace={false}
|
||||||
|
helpText={null}
|
||||||
|
isInvalid={false}
|
||||||
|
label="string:test-validation:setting"
|
||||||
|
labelType="label"
|
||||||
|
>
|
||||||
|
<EuiFieldText
|
||||||
|
aria-label="string test validation setting"
|
||||||
|
compressed={false}
|
||||||
|
data-test-subj="advancedSetting-editField-string:test-validation:setting"
|
||||||
|
disabled={true}
|
||||||
|
fullWidth={false}
|
||||||
|
isLoading={false}
|
||||||
|
onChange={[Function]}
|
||||||
|
onKeyDown={[Function]}
|
||||||
|
value="foo-default"
|
||||||
|
/>
|
||||||
|
</EuiFormRow>
|
||||||
|
</EuiDescribedFormGroup>
|
||||||
|
</EuiFlexItem>
|
||||||
|
<EuiFlexItem
|
||||||
|
grow={false}
|
||||||
|
/>
|
||||||
|
</EuiFlexGroup>
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Field for stringWithValidation setting should render as read only with help text if overridden 1`] = `
|
||||||
|
<EuiFlexGroup
|
||||||
|
className="mgtAdvancedSettings__field"
|
||||||
|
>
|
||||||
|
<EuiFlexItem
|
||||||
|
grow={false}
|
||||||
|
>
|
||||||
|
<EuiDescribedFormGroup
|
||||||
|
className="mgtAdvancedSettings__fieldWrapper"
|
||||||
|
description={
|
||||||
|
<React.Fragment>
|
||||||
|
<div
|
||||||
|
dangerouslySetInnerHTML={
|
||||||
|
Object {
|
||||||
|
"__html": "Description for String test validation setting",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<React.Fragment>
|
||||||
|
<EuiSpacer
|
||||||
|
size="s"
|
||||||
|
/>
|
||||||
|
<EuiText
|
||||||
|
size="xs"
|
||||||
|
>
|
||||||
|
<React.Fragment>
|
||||||
|
<FormattedMessage
|
||||||
|
defaultMessage="Default: {value}"
|
||||||
|
id="kbn.management.settings.field.defaultValueText"
|
||||||
|
values={
|
||||||
|
Object {
|
||||||
|
"value": <EuiCode>
|
||||||
|
foo-default
|
||||||
|
</EuiCode>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</React.Fragment>
|
||||||
|
</EuiText>
|
||||||
|
</React.Fragment>
|
||||||
|
</React.Fragment>
|
||||||
|
}
|
||||||
|
fullWidth={false}
|
||||||
|
gutterSize="l"
|
||||||
|
idAria="string:test-validation:setting-aria"
|
||||||
|
title={
|
||||||
|
<h3>
|
||||||
|
String test validation setting
|
||||||
|
|
||||||
|
</h3>
|
||||||
|
}
|
||||||
|
titleSize="xs"
|
||||||
|
>
|
||||||
|
<EuiFormRow
|
||||||
|
className="mgtAdvancedSettings__fieldRow"
|
||||||
|
describedByIds={
|
||||||
|
Array [
|
||||||
|
"string:test-validation:setting-aria",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
display="row"
|
||||||
|
error={null}
|
||||||
|
fullWidth={false}
|
||||||
|
hasEmptyLabelSpace={false}
|
||||||
|
helpText={
|
||||||
|
<EuiText
|
||||||
|
size="xs"
|
||||||
|
>
|
||||||
|
<FormattedMessage
|
||||||
|
defaultMessage="This setting is overridden by the Kibana server and can not be changed."
|
||||||
|
id="kbn.management.settings.field.helpText"
|
||||||
|
values={Object {}}
|
||||||
|
/>
|
||||||
|
</EuiText>
|
||||||
|
}
|
||||||
|
isInvalid={false}
|
||||||
|
label="string:test-validation:setting"
|
||||||
|
labelType="label"
|
||||||
|
>
|
||||||
|
<EuiFieldText
|
||||||
|
aria-label="string test validation setting"
|
||||||
|
compressed={false}
|
||||||
|
data-test-subj="advancedSetting-editField-string:test-validation:setting"
|
||||||
|
disabled={true}
|
||||||
|
fullWidth={false}
|
||||||
|
isLoading={false}
|
||||||
|
onChange={[Function]}
|
||||||
|
onKeyDown={[Function]}
|
||||||
|
value="fooUserValue"
|
||||||
|
/>
|
||||||
|
</EuiFormRow>
|
||||||
|
</EuiDescribedFormGroup>
|
||||||
|
</EuiFlexItem>
|
||||||
|
<EuiFlexItem
|
||||||
|
grow={false}
|
||||||
|
/>
|
||||||
|
</EuiFlexGroup>
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Field for stringWithValidation setting should render custom setting icon if it is custom 1`] = `
|
||||||
|
<EuiFlexGroup
|
||||||
|
className="mgtAdvancedSettings__field"
|
||||||
|
>
|
||||||
|
<EuiFlexItem
|
||||||
|
grow={false}
|
||||||
|
>
|
||||||
|
<EuiDescribedFormGroup
|
||||||
|
className="mgtAdvancedSettings__fieldWrapper"
|
||||||
|
description={
|
||||||
|
<React.Fragment>
|
||||||
|
<div
|
||||||
|
dangerouslySetInnerHTML={
|
||||||
|
Object {
|
||||||
|
"__html": "Description for String test validation setting",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</React.Fragment>
|
||||||
|
}
|
||||||
|
fullWidth={false}
|
||||||
|
gutterSize="l"
|
||||||
|
idAria="string:test-validation:setting-aria"
|
||||||
|
title={
|
||||||
|
<h3>
|
||||||
|
String test validation setting
|
||||||
|
<EuiIconTip
|
||||||
|
aria-label="Custom setting"
|
||||||
|
color="primary"
|
||||||
|
content={
|
||||||
|
<FormattedMessage
|
||||||
|
defaultMessage="Custom setting"
|
||||||
|
id="kbn.management.settings.field.customSettingTooltip"
|
||||||
|
values={Object {}}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
type="asterisk"
|
||||||
|
/>
|
||||||
|
</h3>
|
||||||
|
}
|
||||||
|
titleSize="xs"
|
||||||
|
>
|
||||||
|
<EuiFormRow
|
||||||
|
className="mgtAdvancedSettings__fieldRow"
|
||||||
|
describedByIds={
|
||||||
|
Array [
|
||||||
|
"string:test-validation:setting-aria",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
display="row"
|
||||||
|
error={null}
|
||||||
|
fullWidth={false}
|
||||||
|
hasEmptyLabelSpace={false}
|
||||||
|
helpText={null}
|
||||||
|
isInvalid={false}
|
||||||
|
label="string:test-validation:setting"
|
||||||
|
labelType="label"
|
||||||
|
>
|
||||||
|
<EuiFieldText
|
||||||
|
aria-label="string test validation setting"
|
||||||
|
compressed={false}
|
||||||
|
data-test-subj="advancedSetting-editField-string:test-validation:setting"
|
||||||
|
disabled={false}
|
||||||
|
fullWidth={false}
|
||||||
|
isLoading={false}
|
||||||
|
onChange={[Function]}
|
||||||
|
onKeyDown={[Function]}
|
||||||
|
value="foo-default"
|
||||||
|
/>
|
||||||
|
</EuiFormRow>
|
||||||
|
</EuiDescribedFormGroup>
|
||||||
|
</EuiFlexItem>
|
||||||
|
<EuiFlexItem
|
||||||
|
grow={false}
|
||||||
|
/>
|
||||||
|
</EuiFlexGroup>
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Field for stringWithValidation setting should render default value if there is no user value set 1`] = `
|
||||||
|
<EuiFlexGroup
|
||||||
|
className="mgtAdvancedSettings__field"
|
||||||
|
>
|
||||||
|
<EuiFlexItem
|
||||||
|
grow={false}
|
||||||
|
>
|
||||||
|
<EuiDescribedFormGroup
|
||||||
|
className="mgtAdvancedSettings__fieldWrapper"
|
||||||
|
description={
|
||||||
|
<React.Fragment>
|
||||||
|
<div
|
||||||
|
dangerouslySetInnerHTML={
|
||||||
|
Object {
|
||||||
|
"__html": "Description for String test validation setting",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</React.Fragment>
|
||||||
|
}
|
||||||
|
fullWidth={false}
|
||||||
|
gutterSize="l"
|
||||||
|
idAria="string:test-validation:setting-aria"
|
||||||
|
title={
|
||||||
|
<h3>
|
||||||
|
String test validation setting
|
||||||
|
|
||||||
|
</h3>
|
||||||
|
}
|
||||||
|
titleSize="xs"
|
||||||
|
>
|
||||||
|
<EuiFormRow
|
||||||
|
className="mgtAdvancedSettings__fieldRow"
|
||||||
|
describedByIds={
|
||||||
|
Array [
|
||||||
|
"string:test-validation:setting-aria",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
display="row"
|
||||||
|
error={null}
|
||||||
|
fullWidth={false}
|
||||||
|
hasEmptyLabelSpace={false}
|
||||||
|
helpText={null}
|
||||||
|
isInvalid={false}
|
||||||
|
label="string:test-validation:setting"
|
||||||
|
labelType="label"
|
||||||
|
>
|
||||||
|
<EuiFieldText
|
||||||
|
aria-label="string test validation setting"
|
||||||
|
compressed={false}
|
||||||
|
data-test-subj="advancedSetting-editField-string:test-validation:setting"
|
||||||
|
disabled={false}
|
||||||
|
fullWidth={false}
|
||||||
|
isLoading={false}
|
||||||
|
onChange={[Function]}
|
||||||
|
onKeyDown={[Function]}
|
||||||
|
value="foo-default"
|
||||||
|
/>
|
||||||
|
</EuiFormRow>
|
||||||
|
</EuiDescribedFormGroup>
|
||||||
|
</EuiFlexItem>
|
||||||
|
<EuiFlexItem
|
||||||
|
grow={false}
|
||||||
|
/>
|
||||||
|
</EuiFlexGroup>
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Field for stringWithValidation setting should render user value if there is user value is set 1`] = `
|
||||||
|
<EuiFlexGroup
|
||||||
|
className="mgtAdvancedSettings__field"
|
||||||
|
>
|
||||||
|
<EuiFlexItem
|
||||||
|
grow={false}
|
||||||
|
>
|
||||||
|
<EuiDescribedFormGroup
|
||||||
|
className="mgtAdvancedSettings__fieldWrapper"
|
||||||
|
description={
|
||||||
|
<React.Fragment>
|
||||||
|
<div
|
||||||
|
dangerouslySetInnerHTML={
|
||||||
|
Object {
|
||||||
|
"__html": "Description for String test validation setting",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<React.Fragment>
|
||||||
|
<EuiSpacer
|
||||||
|
size="s"
|
||||||
|
/>
|
||||||
|
<EuiText
|
||||||
|
size="xs"
|
||||||
|
>
|
||||||
|
<React.Fragment>
|
||||||
|
<FormattedMessage
|
||||||
|
defaultMessage="Default: {value}"
|
||||||
|
id="kbn.management.settings.field.defaultValueText"
|
||||||
|
values={
|
||||||
|
Object {
|
||||||
|
"value": <EuiCode>
|
||||||
|
foo-default
|
||||||
|
</EuiCode>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</React.Fragment>
|
||||||
|
</EuiText>
|
||||||
|
</React.Fragment>
|
||||||
|
</React.Fragment>
|
||||||
|
}
|
||||||
|
fullWidth={false}
|
||||||
|
gutterSize="l"
|
||||||
|
idAria="string:test-validation:setting-aria"
|
||||||
|
title={
|
||||||
|
<h3>
|
||||||
|
String test validation setting
|
||||||
|
|
||||||
|
</h3>
|
||||||
|
}
|
||||||
|
titleSize="xs"
|
||||||
|
>
|
||||||
|
<EuiFormRow
|
||||||
|
className="mgtAdvancedSettings__fieldRow"
|
||||||
|
describedByIds={
|
||||||
|
Array [
|
||||||
|
"string:test-validation:setting-aria",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
display="row"
|
||||||
|
error={null}
|
||||||
|
fullWidth={false}
|
||||||
|
hasEmptyLabelSpace={false}
|
||||||
|
helpText={
|
||||||
|
<span>
|
||||||
|
<span>
|
||||||
|
<EuiLink
|
||||||
|
aria-label="Reset string test validation setting to default"
|
||||||
|
color="primary"
|
||||||
|
data-test-subj="advancedSetting-resetField-string:test-validation:setting"
|
||||||
|
onClick={[Function]}
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<FormattedMessage
|
||||||
|
defaultMessage="Reset to default"
|
||||||
|
id="kbn.management.settings.field.resetToDefaultLinkText"
|
||||||
|
values={Object {}}
|
||||||
|
/>
|
||||||
|
</EuiLink>
|
||||||
|
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
}
|
||||||
|
isInvalid={false}
|
||||||
|
label="string:test-validation:setting"
|
||||||
|
labelType="label"
|
||||||
|
>
|
||||||
|
<EuiFieldText
|
||||||
|
aria-label="string test validation setting"
|
||||||
|
compressed={false}
|
||||||
|
data-test-subj="advancedSetting-editField-string:test-validation:setting"
|
||||||
|
disabled={false}
|
||||||
|
fullWidth={false}
|
||||||
|
isLoading={false}
|
||||||
|
onChange={[Function]}
|
||||||
|
onKeyDown={[Function]}
|
||||||
|
value="fooUserValue"
|
||||||
|
/>
|
||||||
|
</EuiFormRow>
|
||||||
|
</EuiDescribedFormGroup>
|
||||||
|
</EuiFlexItem>
|
||||||
|
<EuiFlexItem
|
||||||
|
grow={false}
|
||||||
|
/>
|
||||||
|
</EuiFlexGroup>
|
||||||
|
`;
|
||||||
|
|
|
@ -166,7 +166,7 @@ class FieldUI extends PureComponent {
|
||||||
|
|
||||||
onFieldChange = (e) => {
|
onFieldChange = (e) => {
|
||||||
const value = e.target.value;
|
const value = e.target.value;
|
||||||
const { type } = this.props.setting;
|
const { type, validation } = this.props.setting;
|
||||||
const { unsavedValue } = this.state;
|
const { unsavedValue } = this.state;
|
||||||
|
|
||||||
let newUnsavedValue = undefined;
|
let newUnsavedValue = undefined;
|
||||||
|
@ -181,8 +181,21 @@ class FieldUI extends PureComponent {
|
||||||
default:
|
default:
|
||||||
newUnsavedValue = value;
|
newUnsavedValue = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let isInvalid = false;
|
||||||
|
let error = undefined;
|
||||||
|
|
||||||
|
if (validation && validation.regex) {
|
||||||
|
if (!validation.regex.test(newUnsavedValue)) {
|
||||||
|
error = validation.message;
|
||||||
|
isInvalid = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
unsavedValue: newUnsavedValue,
|
unsavedValue: newUnsavedValue,
|
||||||
|
isInvalid,
|
||||||
|
error
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -143,6 +143,22 @@ const settings = {
|
||||||
isOverridden: false,
|
isOverridden: false,
|
||||||
options: null,
|
options: null,
|
||||||
},
|
},
|
||||||
|
stringWithValidation: {
|
||||||
|
name: 'string:test-validation:setting',
|
||||||
|
ariaName: 'string test validation setting',
|
||||||
|
displayName: 'String test validation setting',
|
||||||
|
description: 'Description for String test validation setting',
|
||||||
|
type: 'string',
|
||||||
|
validation: {
|
||||||
|
regex: new RegExp('/^foo'),
|
||||||
|
message: 'must start with "foo"'
|
||||||
|
},
|
||||||
|
value: undefined,
|
||||||
|
defVal: 'foo-default',
|
||||||
|
isCustom: false,
|
||||||
|
isOverridden: false,
|
||||||
|
options: null,
|
||||||
|
}
|
||||||
};
|
};
|
||||||
const userValues = {
|
const userValues = {
|
||||||
array: ['user', 'value'],
|
array: ['user', 'value'],
|
||||||
|
@ -153,6 +169,10 @@ const userValues = {
|
||||||
number: 10,
|
number: 10,
|
||||||
select: 'banana',
|
select: 'banana',
|
||||||
string: 'foo',
|
string: 'foo',
|
||||||
|
stringWithValidation: 'fooUserValue'
|
||||||
|
};
|
||||||
|
const invalidUserValues = {
|
||||||
|
stringWithValidation: 'invalidUserValue'
|
||||||
};
|
};
|
||||||
const save = jest.fn(() => Promise.resolve());
|
const save = jest.fn(() => Promise.resolve());
|
||||||
const clear = jest.fn(() => Promise.resolve());
|
const clear = jest.fn(() => Promise.resolve());
|
||||||
|
@ -392,6 +412,16 @@ describe('Field', () => {
|
||||||
const userValue = userValues[type];
|
const userValue = userValues[type];
|
||||||
const fieldUserValue = type === 'array' ? userValue.join(', ') : userValue;
|
const fieldUserValue = type === 'array' ? userValue.join(', ') : userValue;
|
||||||
|
|
||||||
|
if (setting.validation) {
|
||||||
|
const invalidUserValue = invalidUserValues[type];
|
||||||
|
it('should display an error when validation fails', async () => {
|
||||||
|
component.instance().onFieldChange({ target: { value: invalidUserValue } });
|
||||||
|
component.update();
|
||||||
|
const errorMessage = component.find('.euiFormErrorText').text();
|
||||||
|
expect(errorMessage).toEqual(setting.validation.message);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
it('should be able to change value and cancel', async () => {
|
it('should be able to change value and cancel', async () => {
|
||||||
component.instance().onFieldChange({ target: { value: fieldUserValue } });
|
component.instance().onFieldChange({ target: { value: fieldUserValue } });
|
||||||
component.update();
|
component.update();
|
||||||
|
|
|
@ -43,7 +43,7 @@ describe('Settings', function () {
|
||||||
def = {
|
def = {
|
||||||
value: 'the original',
|
value: 'the original',
|
||||||
description: 'the one and only',
|
description: 'the one and only',
|
||||||
options: 'all the options'
|
options: 'all the options',
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -76,6 +76,18 @@ describe('Settings', function () {
|
||||||
expect(invoke({ def }).type).to.equal('array');
|
expect(invoke({ def }).type).to.equal('array');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('that contains a validation object', function () {
|
||||||
|
it('constructs a validation regex with message', function () {
|
||||||
|
def.validation = {
|
||||||
|
regexString: '^foo',
|
||||||
|
message: 'must start with "foo"'
|
||||||
|
};
|
||||||
|
const result = invoke({ def });
|
||||||
|
expect(result.validation.regex).to.be.a(RegExp);
|
||||||
|
expect(result.validation.message).to.equal('must start with "foo"');
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('when not given a setting definition object', function () {
|
describe('when not given a setting definition object', function () {
|
||||||
|
@ -94,6 +106,10 @@ describe('Settings', function () {
|
||||||
it('sets options to undefined', function () {
|
it('sets options to undefined', function () {
|
||||||
expect(invoke().options).to.be.undefined;
|
expect(invoke().options).to.be.undefined;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('sets validation to undefined', function () {
|
||||||
|
expect(invoke().validation).to.be.undefined;
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -43,6 +43,10 @@ export function toEditableConfig({ def, name, value, isCustom, isOverridden }) {
|
||||||
defVal: def.value,
|
defVal: def.value,
|
||||||
type: getValType(def, value),
|
type: getValType(def, value),
|
||||||
description: def.description,
|
description: def.description,
|
||||||
|
validation: def.validation ? {
|
||||||
|
regex: new RegExp(def.validation.regexString),
|
||||||
|
message: def.validation.message
|
||||||
|
} : undefined,
|
||||||
options: def.options,
|
options: def.options,
|
||||||
optionLabels: def.optionLabels,
|
optionLabels: def.optionLabels,
|
||||||
requiresPageReload: !!def.requiresPageReload,
|
requiresPageReload: !!def.requiresPageReload,
|
||||||
|
|
|
@ -55,6 +55,24 @@ export function getUiSettingDefaults() {
|
||||||
'buildNum': {
|
'buildNum': {
|
||||||
readonly: true
|
readonly: true
|
||||||
},
|
},
|
||||||
|
'defaultRoute': {
|
||||||
|
name: i18n.translate('kbn.advancedSettings.defaultRoute.defaultRouteTitle', {
|
||||||
|
defaultMessage: 'Default route',
|
||||||
|
}),
|
||||||
|
value: '/app/kibana',
|
||||||
|
validation: {
|
||||||
|
regexString: '^\/',
|
||||||
|
message: i18n.translate('kbn.advancedSettings.defaultRoute.defaultRouteValidationMessage', {
|
||||||
|
defaultMessage: 'The route must start with a slash ("/")',
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
description:
|
||||||
|
i18n.translate('kbn.advancedSettings.defaultRoute.defaultRouteText', {
|
||||||
|
defaultMessage: 'This setting specifies the default route when opening Kibana. ' +
|
||||||
|
'You can use this setting to modify the landing page when opening Kibana. ' +
|
||||||
|
'The route must start with a slash ("/").',
|
||||||
|
}),
|
||||||
|
},
|
||||||
'query:queryString:options': {
|
'query:queryString:options': {
|
||||||
name: i18n.translate('kbn.advancedSettings.query.queryStringOptionsTitle', {
|
name: i18n.translate('kbn.advancedSettings.query.queryStringOptionsTitle', {
|
||||||
defaultMessage: 'Query string options',
|
defaultMessage: 'Query string options',
|
||||||
|
|
|
@ -78,7 +78,7 @@ export default () => Joi.object({
|
||||||
server: Joi.object({
|
server: Joi.object({
|
||||||
uuid: Joi.string().guid().default(),
|
uuid: Joi.string().guid().default(),
|
||||||
name: Joi.string().default(os.hostname()),
|
name: Joi.string().default(os.hostname()),
|
||||||
defaultRoute: Joi.string().default('/app/kibana').regex(/^\//, `start with a slash`),
|
defaultRoute: Joi.string().regex(/^\//, `start with a slash`),
|
||||||
customResponseHeaders: Joi.object().unknown(true).default({}),
|
customResponseHeaders: Joi.object().unknown(true).default({}),
|
||||||
xsrf: Joi.object({
|
xsrf: Joi.object({
|
||||||
disableProtection: Joi.boolean().default(false),
|
disableProtection: Joi.boolean().default(false),
|
||||||
|
|
|
@ -95,6 +95,7 @@ const cspRules = (settings, log) => {
|
||||||
|
|
||||||
const deprecations = [
|
const deprecations = [
|
||||||
//server
|
//server
|
||||||
|
rename('server.defaultRoute', 'uiSettings.overrides.defaultRoute'),
|
||||||
unused('server.xsrf.token'),
|
unused('server.xsrf.token'),
|
||||||
unused('uiSettings.enabled'),
|
unused('uiSettings.enabled'),
|
||||||
rename('optimize.lazy', 'optimize.watch'),
|
rename('optimize.lazy', 'optimize.watch'),
|
||||||
|
|
|
@ -62,6 +62,24 @@ describe('server/config', function () {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('server.defaultRoute', () => {
|
||||||
|
it('renames to uiSettings.overrides.defaultRoute when specified', () => {
|
||||||
|
const settings = {
|
||||||
|
server: {
|
||||||
|
defaultRoute: '/app/foo',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(transformDeprecations(settings)).toEqual({
|
||||||
|
uiSettings: {
|
||||||
|
overrides: {
|
||||||
|
defaultRoute: '/app/foo'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('csp.rules', () => {
|
describe('csp.rules', () => {
|
||||||
describe('with nonce source', () => {
|
describe('with nonce source', () => {
|
||||||
it('logs a warning', () => {
|
it('logs a warning', () => {
|
||||||
|
@ -74,20 +92,18 @@ describe('server/config', function () {
|
||||||
const log = jest.fn();
|
const log = jest.fn();
|
||||||
transformDeprecations(settings, log);
|
transformDeprecations(settings, log);
|
||||||
expect(log.mock.calls).toMatchInlineSnapshot(`
|
expect(log.mock.calls).toMatchInlineSnapshot(`
|
||||||
Array [
|
Array [
|
||||||
Array [
|
Array [
|
||||||
"csp.rules no longer supports the {nonce} syntax. Replacing with 'self' in script-src",
|
"csp.rules no longer supports the {nonce} syntax. Replacing with 'self' in script-src",
|
||||||
],
|
],
|
||||||
]
|
]
|
||||||
`);
|
`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('replaces a nonce', () => {
|
it('replaces a nonce', () => {
|
||||||
expect(
|
expect(
|
||||||
transformDeprecations(
|
transformDeprecations({ csp: { rules: [`script-src 'nonce-{nonce}'`] } }, jest.fn()).csp
|
||||||
{ csp: { rules: [`script-src 'nonce-{nonce}'`] } },
|
.rules
|
||||||
jest.fn()
|
|
||||||
).csp.rules
|
|
||||||
).toEqual([`script-src 'self'`]);
|
).toEqual([`script-src 'self'`]);
|
||||||
expect(
|
expect(
|
||||||
transformDeprecations(
|
transformDeprecations(
|
||||||
|
@ -158,12 +174,12 @@ describe('server/config', function () {
|
||||||
const log = jest.fn();
|
const log = jest.fn();
|
||||||
transformDeprecations({ csp: { rules: [`script-src 'unsafe-eval'`] } }, log);
|
transformDeprecations({ csp: { rules: [`script-src 'unsafe-eval'`] } }, log);
|
||||||
expect(log.mock.calls).toMatchInlineSnapshot(`
|
expect(log.mock.calls).toMatchInlineSnapshot(`
|
||||||
Array [
|
Array [
|
||||||
Array [
|
Array [
|
||||||
"csp.rules must contain the 'self' source. Automatically adding to script-src.",
|
"csp.rules must contain the 'self' source. Automatically adding to script-src.",
|
||||||
],
|
],
|
||||||
]
|
]
|
||||||
`);
|
`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('adds self', () => {
|
it('adds self', () => {
|
||||||
|
|
|
@ -25,6 +25,7 @@ import Boom from 'boom';
|
||||||
import { setupVersionCheck } from './version_check';
|
import { setupVersionCheck } from './version_check';
|
||||||
import { registerHapiPlugins } from './register_hapi_plugins';
|
import { registerHapiPlugins } from './register_hapi_plugins';
|
||||||
import { setupBasePathProvider } from './setup_base_path_provider';
|
import { setupBasePathProvider } from './setup_base_path_provider';
|
||||||
|
import { setupDefaultRouteProvider } from './setup_default_route_provider';
|
||||||
import { setupXsrf } from './xsrf';
|
import { setupXsrf } from './xsrf';
|
||||||
|
|
||||||
export default async function (kbnServer, server, config) {
|
export default async function (kbnServer, server, config) {
|
||||||
|
@ -33,6 +34,8 @@ export default async function (kbnServer, server, config) {
|
||||||
|
|
||||||
setupBasePathProvider(kbnServer);
|
setupBasePathProvider(kbnServer);
|
||||||
|
|
||||||
|
setupDefaultRouteProvider(server);
|
||||||
|
|
||||||
await registerHapiPlugins(server);
|
await registerHapiPlugins(server);
|
||||||
|
|
||||||
// provide a simple way to expose static directories
|
// provide a simple way to expose static directories
|
||||||
|
@ -86,10 +89,8 @@ export default async function (kbnServer, server, config) {
|
||||||
server.route({
|
server.route({
|
||||||
path: '/',
|
path: '/',
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
handler(req, h) {
|
async handler(req, h) {
|
||||||
const basePath = req.getBasePath();
|
return h.redirect(await req.getDefaultRoute());
|
||||||
const defaultRoute = config.get('server.defaultRoute');
|
|
||||||
return h.redirect(`${basePath}${defaultRoute}`);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,87 @@
|
||||||
|
/*
|
||||||
|
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||||
|
* license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright
|
||||||
|
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||||
|
* the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
* not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
jest.mock('../../../ui/ui_settings/ui_settings_mixin', () => {
|
||||||
|
return jest.fn();
|
||||||
|
});
|
||||||
|
|
||||||
|
import * as kbnTestServer from '../../../../test_utils/kbn_server';
|
||||||
|
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
|
||||||
|
import { Root } from '../../../../core/server/root';
|
||||||
|
|
||||||
|
let mockDefaultRouteSetting: any = '';
|
||||||
|
|
||||||
|
describe('default route provider', () => {
|
||||||
|
let root: Root;
|
||||||
|
beforeAll(async () => {
|
||||||
|
root = kbnTestServer.createRoot();
|
||||||
|
|
||||||
|
await root.setup();
|
||||||
|
await root.start();
|
||||||
|
|
||||||
|
const kbnServer = kbnTestServer.getKbnServer(root);
|
||||||
|
|
||||||
|
kbnServer.server.decorate('request', 'getUiSettingsService', function() {
|
||||||
|
return {
|
||||||
|
get: (key: string) => {
|
||||||
|
if (key === 'defaultRoute') {
|
||||||
|
return Promise.resolve(mockDefaultRouteSetting);
|
||||||
|
}
|
||||||
|
throw Error(`unsupported ui setting: ${key}`);
|
||||||
|
},
|
||||||
|
getDefaults: () => {
|
||||||
|
return Promise.resolve({
|
||||||
|
defaultRoute: {
|
||||||
|
value: '/app/kibana',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}, 30000);
|
||||||
|
|
||||||
|
afterAll(async () => await root.shutdown());
|
||||||
|
|
||||||
|
it('redirects to the configured default route', async function() {
|
||||||
|
mockDefaultRouteSetting = '/app/some/default/route';
|
||||||
|
|
||||||
|
const { status, header } = await kbnTestServer.request.get(root, '/');
|
||||||
|
expect(status).toEqual(302);
|
||||||
|
expect(header).toMatchObject({
|
||||||
|
location: '/app/some/default/route',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const invalidRoutes = [
|
||||||
|
'http://not-your-kibana.com',
|
||||||
|
'///example.com',
|
||||||
|
'//example.com',
|
||||||
|
' //example.com',
|
||||||
|
];
|
||||||
|
for (const route of invalidRoutes) {
|
||||||
|
it(`falls back to /app/kibana when the configured route (${route}) is not a valid relative path`, async function() {
|
||||||
|
mockDefaultRouteSetting = route;
|
||||||
|
|
||||||
|
const { status, header } = await kbnTestServer.request.get(root, '/');
|
||||||
|
expect(status).toEqual(302);
|
||||||
|
expect(header).toMatchObject({
|
||||||
|
location: '/app/kibana',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
74
src/legacy/server/http/setup_default_route_provider.ts
Normal file
74
src/legacy/server/http/setup_default_route_provider.ts
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
/*
|
||||||
|
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||||
|
* license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright
|
||||||
|
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||||
|
* the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
* not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Legacy } from 'kibana';
|
||||||
|
import { parse } from 'url';
|
||||||
|
|
||||||
|
export function setupDefaultRouteProvider(server: Legacy.Server) {
|
||||||
|
server.decorate('request', 'getDefaultRoute', async function() {
|
||||||
|
// @ts-ignore
|
||||||
|
const request: Legacy.Request = this;
|
||||||
|
|
||||||
|
const serverBasePath: string = server.config().get('server.basePath');
|
||||||
|
|
||||||
|
const uiSettings = request.getUiSettingsService();
|
||||||
|
|
||||||
|
const defaultRoute = await uiSettings.get('defaultRoute');
|
||||||
|
const qualifiedDefaultRoute = `${request.getBasePath()}${defaultRoute}`;
|
||||||
|
|
||||||
|
if (isRelativePath(qualifiedDefaultRoute, serverBasePath)) {
|
||||||
|
return qualifiedDefaultRoute;
|
||||||
|
} else {
|
||||||
|
server.log(
|
||||||
|
['http', 'warn'],
|
||||||
|
`Ignoring configured default route of '${defaultRoute}', as it is malformed.`
|
||||||
|
);
|
||||||
|
|
||||||
|
const fallbackRoute = (await uiSettings.getDefaults()).defaultRoute.value;
|
||||||
|
|
||||||
|
const qualifiedFallbackRoute = `${request.getBasePath()}${fallbackRoute}`;
|
||||||
|
return qualifiedFallbackRoute;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function isRelativePath(candidatePath: string, basePath = '') {
|
||||||
|
// validate that `candidatePath` is not attempting a redirect to somewhere
|
||||||
|
// outside of this Kibana install
|
||||||
|
const { protocol, hostname, port, pathname } = parse(
|
||||||
|
candidatePath,
|
||||||
|
false /* parseQueryString */,
|
||||||
|
true /* slashesDenoteHost */
|
||||||
|
);
|
||||||
|
|
||||||
|
// We should explicitly compare `protocol`, `port` and `hostname` to null to make sure these are not
|
||||||
|
// detected in the URL at all. For example `hostname` can be empty string for Node URL parser, but
|
||||||
|
// browser (because of various bwc reasons) processes URL differently (e.g. `///abc.com` - for browser
|
||||||
|
// hostname is `abc.com`, but for Node hostname is an empty string i.e. everything between schema (`//`)
|
||||||
|
// and the first slash that belongs to path.
|
||||||
|
if (protocol !== null || hostname !== null || port !== null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!String(pathname).startsWith(basePath)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
1
src/legacy/server/kbn_server.d.ts
vendored
1
src/legacy/server/kbn_server.d.ts
vendored
|
@ -83,6 +83,7 @@ declare module 'hapi' {
|
||||||
interface Request {
|
interface Request {
|
||||||
getSavedObjectsClient(options?: SavedObjectsClientProviderOptions): SavedObjectsClientContract;
|
getSavedObjectsClient(options?: SavedObjectsClientProviderOptions): SavedObjectsClientContract;
|
||||||
getBasePath(): string;
|
getBasePath(): string;
|
||||||
|
getDefaultRoute(): Promise<string>;
|
||||||
getUiSettingsService(): any;
|
getUiSettingsService(): any;
|
||||||
getCapabilities(): Promise<Capabilities>;
|
getCapabilities(): Promise<Capabilities>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@ import {
|
||||||
import { FormattedMessage, InjectedIntl } from '@kbn/i18n/react';
|
import { FormattedMessage, InjectedIntl } from '@kbn/i18n/react';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import { getSpaceColor } from '../../../../../../../../../spaces/common';
|
import { getSpaceColor } from '../../../../../../../../../spaces/public/lib/space_attributes';
|
||||||
import { Space } from '../../../../../../../../../spaces/common/model/space';
|
import { Space } from '../../../../../../../../../spaces/common/model/space';
|
||||||
import {
|
import {
|
||||||
FeaturesPrivileges,
|
FeaturesPrivileges,
|
||||||
|
|
|
@ -14,7 +14,7 @@ import {
|
||||||
import { InjectedIntl } from '@kbn/i18n/react';
|
import { InjectedIntl } from '@kbn/i18n/react';
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import { Space } from '../../../../../../../../../spaces/common/model/space';
|
import { Space } from '../../../../../../../../../spaces/common/model/space';
|
||||||
import { getSpaceColor } from '../../../../../../../../../spaces/common/space_attributes';
|
import { getSpaceColor } from '../../../../../../../../../spaces/public/lib/space_attributes';
|
||||||
|
|
||||||
const spaceToOption = (space?: Space, currentSelection?: 'global' | 'spaces') => {
|
const spaceToOption = (space?: Space, currentSelection?: 'global' | 'spaces') => {
|
||||||
if (!space) {
|
if (!space) {
|
||||||
|
|
|
@ -21,3 +21,8 @@ export const MAX_SPACE_INITIALS = 2;
|
||||||
* @type {string}
|
* @type {string}
|
||||||
*/
|
*/
|
||||||
export const KIBANA_SPACES_STATS_TYPE = 'spaces';
|
export const KIBANA_SPACES_STATS_TYPE = 'spaces';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The path to enter a space.
|
||||||
|
*/
|
||||||
|
export const ENTER_SPACE_PATH = '/spaces/enter';
|
||||||
|
|
|
@ -7,4 +7,4 @@
|
||||||
export { isReservedSpace } from './is_reserved_space';
|
export { isReservedSpace } from './is_reserved_space';
|
||||||
export { MAX_SPACE_INITIALS } from './constants';
|
export { MAX_SPACE_INITIALS } from './constants';
|
||||||
|
|
||||||
export { getSpaceInitials, getSpaceColor } from './space_attributes';
|
export { getSpaceIdFromPath, addSpaceIdToPath } from './lib/spaces_url_parser';
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
* or more contributor license agreements. Licensed under the Elastic License;
|
* or more contributor license agreements. Licensed under the Elastic License;
|
||||||
* you may not use this file except in compliance with the Elastic License.
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
*/
|
*/
|
||||||
import { DEFAULT_SPACE_ID } from '../../common/constants';
|
import { DEFAULT_SPACE_ID } from '../constants';
|
||||||
import { addSpaceIdToPath, getSpaceIdFromPath } from './spaces_url_parser';
|
import { addSpaceIdToPath, getSpaceIdFromPath } from './spaces_url_parser';
|
||||||
|
|
||||||
describe('getSpaceIdFromPath', () => {
|
describe('getSpaceIdFromPath', () => {
|
|
@ -3,7 +3,7 @@
|
||||||
* or more contributor license agreements. Licensed under the Elastic License;
|
* or more contributor license agreements. Licensed under the Elastic License;
|
||||||
* you may not use this file except in compliance with the Elastic License.
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
*/
|
*/
|
||||||
import { DEFAULT_SPACE_ID } from '../../common/constants';
|
import { DEFAULT_SPACE_ID } from '../constants';
|
||||||
|
|
||||||
export function getSpaceIdFromPath(
|
export function getSpaceIdFromPath(
|
||||||
requestBasePath: string = '/',
|
requestBasePath: string = '/',
|
|
@ -14,12 +14,11 @@ import { AuditLogger } from '../../server/lib/audit_logger';
|
||||||
import mappings from './mappings.json';
|
import mappings from './mappings.json';
|
||||||
import { wrapError } from './server/lib/errors';
|
import { wrapError } from './server/lib/errors';
|
||||||
import { getActiveSpace } from './server/lib/get_active_space';
|
import { getActiveSpace } from './server/lib/get_active_space';
|
||||||
import { getSpaceSelectorUrl } from './server/lib/get_space_selector_url';
|
|
||||||
import { migrateToKibana660 } from './server/lib/migrations';
|
import { migrateToKibana660 } from './server/lib/migrations';
|
||||||
import { plugin } from './server/new_platform';
|
import { plugin } from './server/new_platform';
|
||||||
import { SecurityPlugin } from '../security';
|
import { SecurityPlugin } from '../security';
|
||||||
import { SpacesServiceSetup } from './server/new_platform/spaces_service/spaces_service';
|
import { SpacesServiceSetup } from './server/new_platform/spaces_service/spaces_service';
|
||||||
import { initSpaceSelectorView } from './server/routes/views';
|
import { initSpaceSelectorView, initEnterSpaceView } from './server/routes/views';
|
||||||
|
|
||||||
export interface SpacesPlugin {
|
export interface SpacesPlugin {
|
||||||
getSpaceId: SpacesServiceSetup['getSpaceId'];
|
getSpaceId: SpacesServiceSetup['getSpaceId'];
|
||||||
|
@ -88,7 +87,7 @@ export const spaces = (kibana: Record<string, any>) =>
|
||||||
return {
|
return {
|
||||||
spaces: [],
|
spaces: [],
|
||||||
activeSpace: null,
|
activeSpace: null,
|
||||||
spaceSelectorURL: getSpaceSelectorUrl(server.config()),
|
serverBasePath: server.config().get('server.basePath'),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
async replaceInjectedVars(
|
async replaceInjectedVars(
|
||||||
|
@ -181,6 +180,7 @@ export const spaces = (kibana: Record<string, any>) =>
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
initEnterSpaceView(server);
|
||||||
initSpaceSelectorView(server);
|
initSpaceSelectorView(server);
|
||||||
|
|
||||||
server.expose('getSpaceId', (request: any) => spacesService.getSpaceId(request));
|
server.expose('getSpaceId', (request: any) => spacesService.getSpaceId(request));
|
||||||
|
|
|
@ -6,9 +6,9 @@
|
||||||
|
|
||||||
import { EuiAvatar, isValidHex } from '@elastic/eui';
|
import { EuiAvatar, isValidHex } from '@elastic/eui';
|
||||||
import React, { SFC } from 'react';
|
import React, { SFC } from 'react';
|
||||||
import { getSpaceColor, getSpaceInitials, MAX_SPACE_INITIALS } from '../../common';
|
import { MAX_SPACE_INITIALS } from '../../common';
|
||||||
import { Space } from '../../common/model/space';
|
import { Space } from '../../common/model/space';
|
||||||
import { getSpaceImageUrl } from '../../common/space_attributes';
|
import { getSpaceColor, getSpaceInitials, getSpaceImageUrl } from '../lib/space_attributes';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
space: Partial<Space>;
|
space: Partial<Space>;
|
||||||
|
|
|
@ -5,3 +5,4 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export { SpacesManager } from './spaces_manager';
|
export { SpacesManager } from './spaces_manager';
|
||||||
|
export { getSpaceInitials, getSpaceColor, getSpaceImageUrl } from './space_attributes';
|
||||||
|
|
|
@ -5,8 +5,8 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { VISUALIZATION_COLORS } from '@elastic/eui';
|
import { VISUALIZATION_COLORS } from '@elastic/eui';
|
||||||
import { MAX_SPACE_INITIALS } from './constants';
|
import { Space } from '../../common/model/space';
|
||||||
import { Space } from './model/space';
|
import { MAX_SPACE_INITIALS } from '../../common';
|
||||||
|
|
||||||
// code point for lowercase "a"
|
// code point for lowercase "a"
|
||||||
const FALLBACK_CODE_POINT = 97;
|
const FALLBACK_CODE_POINT = 97;
|
|
@ -3,21 +3,18 @@
|
||||||
* or more contributor license agreements. Licensed under the Elastic License;
|
* or more contributor license agreements. Licensed under the Elastic License;
|
||||||
* you may not use this file except in compliance with the Elastic License.
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
*/
|
*/
|
||||||
import { i18n } from '@kbn/i18n';
|
|
||||||
import { toastNotifications } from 'ui/notify';
|
|
||||||
import { EventEmitter } from 'events';
|
import { EventEmitter } from 'events';
|
||||||
import { kfetch } from 'ui/kfetch';
|
import { kfetch } from 'ui/kfetch';
|
||||||
import { SavedObjectsManagementRecord } from 'ui/management/saved_objects_management';
|
import { SavedObjectsManagementRecord } from 'ui/management/saved_objects_management';
|
||||||
import { Space } from '../../common/model/space';
|
import { Space } from '../../common/model/space';
|
||||||
import { GetSpacePurpose } from '../../common/model/types';
|
import { GetSpacePurpose } from '../../common/model/types';
|
||||||
import { CopySavedObjectsToSpaceResponse } from './copy_saved_objects_to_space/types';
|
import { CopySavedObjectsToSpaceResponse } from './copy_saved_objects_to_space/types';
|
||||||
|
import { ENTER_SPACE_PATH } from '../../common/constants';
|
||||||
|
import { addSpaceIdToPath } from '../../common';
|
||||||
|
|
||||||
export class SpacesManager extends EventEmitter {
|
export class SpacesManager extends EventEmitter {
|
||||||
private spaceSelectorURL: string;
|
constructor(private readonly serverBasePath: string) {
|
||||||
|
|
||||||
constructor(spaceSelectorURL: string) {
|
|
||||||
super();
|
super();
|
||||||
this.spaceSelectorURL = spaceSelectorURL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async getSpaces(purpose?: GetSpacePurpose): Promise<Space[]> {
|
public async getSpaces(purpose?: GetSpacePurpose): Promise<Space[]> {
|
||||||
|
@ -89,36 +86,14 @@ export class SpacesManager extends EventEmitter {
|
||||||
}
|
}
|
||||||
|
|
||||||
public async changeSelectedSpace(space: Space) {
|
public async changeSelectedSpace(space: Space) {
|
||||||
await kfetch({
|
window.location.href = addSpaceIdToPath(this.serverBasePath, space.id, ENTER_SPACE_PATH);
|
||||||
pathname: `/api/spaces/v1/space/${encodeURIComponent(space.id)}/select`,
|
|
||||||
method: 'POST',
|
|
||||||
})
|
|
||||||
.then(response => {
|
|
||||||
if (response.location) {
|
|
||||||
window.location = response.location;
|
|
||||||
} else {
|
|
||||||
this._displayError();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(() => this._displayError());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public redirectToSpaceSelector() {
|
public redirectToSpaceSelector() {
|
||||||
window.location.href = this.spaceSelectorURL;
|
window.location.href = `${this.serverBasePath}/spaces/space_selector`;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async requestRefresh() {
|
public async requestRefresh() {
|
||||||
this.emit('request_refresh');
|
this.emit('request_refresh');
|
||||||
}
|
}
|
||||||
|
|
||||||
public _displayError() {
|
|
||||||
toastNotifications.addDanger({
|
|
||||||
title: i18n.translate('xpack.spaces.spacesManager.unableToChangeSpaceWarningTitle', {
|
|
||||||
defaultMessage: 'Unable to change your Space',
|
|
||||||
}),
|
|
||||||
text: i18n.translate('xpack.spaces.spacesManager.unableToChangeSpaceWarningDescription', {
|
|
||||||
defaultMessage: 'please try again later',
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,11 +18,11 @@ import {
|
||||||
} from '@elastic/eui';
|
} from '@elastic/eui';
|
||||||
import { InjectedIntl, injectI18n } from '@kbn/i18n/react';
|
import { InjectedIntl, injectI18n } from '@kbn/i18n/react';
|
||||||
|
|
||||||
|
import { getSpaceColor, getSpaceInitials } from '../../../../lib/space_attributes';
|
||||||
import { encode, imageTypes } from '../../../../../common/lib/dataurl';
|
import { encode, imageTypes } from '../../../../../common/lib/dataurl';
|
||||||
|
|
||||||
import { MAX_SPACE_INITIALS } from '../../../../../common/constants';
|
import { MAX_SPACE_INITIALS } from '../../../../../common/constants';
|
||||||
import { Space } from '../../../../../common/model/space';
|
import { Space } from '../../../../../common/model/space';
|
||||||
import { getSpaceColor, getSpaceInitials } from '../../../../../common/space_attributes';
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
space: Partial<Space>;
|
space: Partial<Space>;
|
||||||
|
|
|
@ -24,7 +24,7 @@ const MANAGE_SPACES_KEY = 'spaces';
|
||||||
|
|
||||||
routes.defaults(/\/management/, {
|
routes.defaults(/\/management/, {
|
||||||
resolve: {
|
resolve: {
|
||||||
spacesManagementSection(activeSpace: any, spaceSelectorURL: string) {
|
spacesManagementSection(activeSpace: any, serverBasePath: string) {
|
||||||
function getKibanaSection() {
|
function getKibanaSection() {
|
||||||
return management.getSection('kibana');
|
return management.getSection('kibana');
|
||||||
}
|
}
|
||||||
|
@ -49,7 +49,7 @@ routes.defaults(/\/management/, {
|
||||||
|
|
||||||
// Customize Saved Objects Management
|
// Customize Saved Objects Management
|
||||||
const action = new CopyToSpaceSavedObjectsManagementAction(
|
const action = new CopyToSpaceSavedObjectsManagementAction(
|
||||||
new SpacesManager(spaceSelectorURL),
|
new SpacesManager(serverBasePath),
|
||||||
activeSpace.space
|
activeSpace.space
|
||||||
);
|
);
|
||||||
// This route resolve function executes any time the management screen is loaded, and we want to ensure
|
// This route resolve function executes any time the management screen is loaded, and we want to ensure
|
||||||
|
|
|
@ -22,11 +22,11 @@ routes.when('/management/spaces/list', {
|
||||||
template,
|
template,
|
||||||
k7Breadcrumbs: getListBreadcrumbs,
|
k7Breadcrumbs: getListBreadcrumbs,
|
||||||
requireUICapability: 'management.kibana.spaces',
|
requireUICapability: 'management.kibana.spaces',
|
||||||
controller($scope: any, spacesNavState: SpacesNavState, spaceSelectorURL: string) {
|
controller($scope: any, spacesNavState: SpacesNavState, serverBasePath: string) {
|
||||||
$scope.$$postDigest(async () => {
|
$scope.$$postDigest(async () => {
|
||||||
const domNode = document.getElementById(reactRootNodeId);
|
const domNode = document.getElementById(reactRootNodeId);
|
||||||
|
|
||||||
const spacesManager = new SpacesManager(spaceSelectorURL);
|
const spacesManager = new SpacesManager(serverBasePath);
|
||||||
|
|
||||||
render(
|
render(
|
||||||
<I18nContext>
|
<I18nContext>
|
||||||
|
@ -49,11 +49,11 @@ routes.when('/management/spaces/create', {
|
||||||
template,
|
template,
|
||||||
k7Breadcrumbs: getCreateBreadcrumbs,
|
k7Breadcrumbs: getCreateBreadcrumbs,
|
||||||
requireUICapability: 'management.kibana.spaces',
|
requireUICapability: 'management.kibana.spaces',
|
||||||
controller($scope: any, spacesNavState: SpacesNavState, spaceSelectorURL: string) {
|
controller($scope: any, spacesNavState: SpacesNavState, serverBasePath: string) {
|
||||||
$scope.$$postDigest(async () => {
|
$scope.$$postDigest(async () => {
|
||||||
const domNode = document.getElementById(reactRootNodeId);
|
const domNode = document.getElementById(reactRootNodeId);
|
||||||
|
|
||||||
const spacesManager = new SpacesManager(spaceSelectorURL);
|
const spacesManager = new SpacesManager(serverBasePath);
|
||||||
|
|
||||||
render(
|
render(
|
||||||
<I18nContext>
|
<I18nContext>
|
||||||
|
@ -85,14 +85,14 @@ routes.when('/management/spaces/edit/:spaceId', {
|
||||||
$route: any,
|
$route: any,
|
||||||
chrome: any,
|
chrome: any,
|
||||||
spacesNavState: SpacesNavState,
|
spacesNavState: SpacesNavState,
|
||||||
spaceSelectorURL: string
|
serverBasePath: string
|
||||||
) {
|
) {
|
||||||
$scope.$$postDigest(async () => {
|
$scope.$$postDigest(async () => {
|
||||||
const domNode = document.getElementById(reactRootNodeId);
|
const domNode = document.getElementById(reactRootNodeId);
|
||||||
|
|
||||||
const { spaceId } = $route.current.params;
|
const { spaceId } = $route.current.params;
|
||||||
|
|
||||||
const spacesManager = new SpacesManager(spaceSelectorURL);
|
const spacesManager = new SpacesManager(serverBasePath);
|
||||||
|
|
||||||
render(
|
render(
|
||||||
<I18nContext>
|
<I18nContext>
|
||||||
|
|
|
@ -54,9 +54,9 @@ chromeHeaderNavControlsRegistry.register((chrome: any, activeSpace: any) => ({
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const spaceSelectorURL = chrome.getInjected('spaceSelectorURL');
|
const serverBasePath = chrome.getInjected('serverBasePath');
|
||||||
|
|
||||||
spacesManager = new SpacesManager(spaceSelectorURL);
|
spacesManager = new SpacesManager(serverBasePath);
|
||||||
|
|
||||||
ReactDOM.render(
|
ReactDOM.render(
|
||||||
<I18nContext>
|
<I18nContext>
|
||||||
|
|
|
@ -21,10 +21,10 @@ import { SpaceSelector } from './space_selector';
|
||||||
const module = uiModules.get('spaces_selector', []);
|
const module = uiModules.get('spaces_selector', []);
|
||||||
module.controller(
|
module.controller(
|
||||||
'spacesSelectorController',
|
'spacesSelectorController',
|
||||||
($scope: any, spaces: Space[], spaceSelectorURL: string) => {
|
($scope: any, spaces: Space[], serverBasePath: string) => {
|
||||||
const domNode = document.getElementById('spaceSelectorRoot');
|
const domNode = document.getElementById('spaceSelectorRoot');
|
||||||
|
|
||||||
const spacesManager = new SpacesManager(spaceSelectorURL);
|
const spacesManager = new SpacesManager(serverBasePath);
|
||||||
|
|
||||||
render(
|
render(
|
||||||
<I18nContext>
|
<I18nContext>
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
import { Space } from '../../common/model/space';
|
import { Space } from '../../common/model/space';
|
||||||
import { wrapError } from './errors';
|
import { wrapError } from './errors';
|
||||||
import { SpacesClient } from './spaces_client';
|
import { SpacesClient } from './spaces_client';
|
||||||
import { getSpaceIdFromPath } from './spaces_url_parser';
|
import { getSpaceIdFromPath } from '../../common';
|
||||||
|
|
||||||
export async function getActiveSpace(
|
export async function getActiveSpace(
|
||||||
spacesClient: SpacesClient,
|
spacesClient: SpacesClient,
|
||||||
|
|
|
@ -428,7 +428,7 @@ describe('onPostAuthInterceptor', () => {
|
||||||
);
|
);
|
||||||
}, 30000);
|
}, 30000);
|
||||||
|
|
||||||
it('allows the request to continue when accessing the root of a non-default space', async () => {
|
it('redirects to the "enter space" endpoint when accessing the root of a non-default space', async () => {
|
||||||
const spaces = [
|
const spaces = [
|
||||||
{
|
{
|
||||||
id: 'default',
|
id: 'default',
|
||||||
|
@ -449,9 +449,8 @@ describe('onPostAuthInterceptor', () => {
|
||||||
|
|
||||||
const { response, spacesService } = await request('/s/a-space', spaces);
|
const { response, spacesService } = await request('/s/a-space', spaces);
|
||||||
|
|
||||||
// OSS handles this redirection for us
|
|
||||||
expect(response.status).toEqual(302);
|
expect(response.status).toEqual(302);
|
||||||
expect(response.header.location).toEqual(`/s/a-space${defaultRoute}`);
|
expect(response.header.location).toEqual(`/s/a-space/spaces/enter`);
|
||||||
|
|
||||||
expect(spacesService.scopedClient).toHaveBeenCalledWith(
|
expect(spacesService.scopedClient).toHaveBeenCalledWith(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
|
@ -463,7 +462,7 @@ describe('onPostAuthInterceptor', () => {
|
||||||
}, 30000);
|
}, 30000);
|
||||||
|
|
||||||
describe('with a single available space', () => {
|
describe('with a single available space', () => {
|
||||||
it('it redirects to the defaultRoute within the context of the single Space when navigating to Kibana root', async () => {
|
it('it redirects to the "enter space" endpoint within the context of the single Space when navigating to Kibana root', async () => {
|
||||||
const spaces = [
|
const spaces = [
|
||||||
{
|
{
|
||||||
id: 'a-space',
|
id: 'a-space',
|
||||||
|
@ -477,7 +476,7 @@ describe('onPostAuthInterceptor', () => {
|
||||||
const { response, spacesService } = await request('/', spaces);
|
const { response, spacesService } = await request('/', spaces);
|
||||||
|
|
||||||
expect(response.status).toEqual(302);
|
expect(response.status).toEqual(302);
|
||||||
expect(response.header.location).toEqual(`/s/a-space${defaultRoute}`);
|
expect(response.header.location).toEqual(`/s/a-space/spaces/enter`);
|
||||||
|
|
||||||
expect(spacesService.scopedClient).toHaveBeenCalledWith(
|
expect(spacesService.scopedClient).toHaveBeenCalledWith(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
|
@ -488,7 +487,7 @@ describe('onPostAuthInterceptor', () => {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('it redirects to the defaultRoute within the context of the Default Space when navigating to Kibana root', async () => {
|
it('it redirects to the "enter space" endpoint within the context of the Default Space when navigating to Kibana root', async () => {
|
||||||
// This is very similar to the test above, but this handles the condition where the only available space is the Default Space,
|
// This is very similar to the test above, but this handles the condition where the only available space is the Default Space,
|
||||||
// which does not have a URL Context. In this scenario, the end result is the same as the other test, but the final URL the user
|
// which does not have a URL Context. In this scenario, the end result is the same as the other test, but the final URL the user
|
||||||
// is redirected to does not contain a space identifier (e.g., /s/foo)
|
// is redirected to does not contain a space identifier (e.g., /s/foo)
|
||||||
|
@ -506,7 +505,7 @@ describe('onPostAuthInterceptor', () => {
|
||||||
const { response, spacesService } = await request('/', spaces);
|
const { response, spacesService } = await request('/', spaces);
|
||||||
|
|
||||||
expect(response.status).toEqual(302);
|
expect(response.status).toEqual(302);
|
||||||
expect(response.header.location).toEqual(defaultRoute);
|
expect(response.header.location).toEqual('/spaces/enter');
|
||||||
expect(spacesService.scopedClient).toHaveBeenCalledWith(
|
expect(spacesService.scopedClient).toHaveBeenCalledWith(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
headers: expect.objectContaining({
|
headers: expect.objectContaining({
|
||||||
|
|
|
@ -6,12 +6,12 @@
|
||||||
import { Logger, CoreSetup } from 'src/core/server';
|
import { Logger, CoreSetup } from 'src/core/server';
|
||||||
import { Space } from '../../../common/model/space';
|
import { Space } from '../../../common/model/space';
|
||||||
import { wrapError } from '../errors';
|
import { wrapError } from '../errors';
|
||||||
import { addSpaceIdToPath } from '../spaces_url_parser';
|
|
||||||
import { XPackMainPlugin } from '../../../../xpack_main/xpack_main';
|
import { XPackMainPlugin } from '../../../../xpack_main/xpack_main';
|
||||||
import { SpacesServiceSetup } from '../../new_platform/spaces_service/spaces_service';
|
import { SpacesServiceSetup } from '../../new_platform/spaces_service/spaces_service';
|
||||||
import { LegacyAPI } from '../../new_platform/plugin';
|
import { LegacyAPI } from '../../new_platform/plugin';
|
||||||
import { getSpaceSelectorUrl } from '../get_space_selector_url';
|
import { getSpaceSelectorUrl } from '../get_space_selector_url';
|
||||||
import { DEFAULT_SPACE_ID } from '../../../common/constants';
|
import { DEFAULT_SPACE_ID, ENTER_SPACE_PATH } from '../../../common/constants';
|
||||||
|
import { addSpaceIdToPath } from '../../../common';
|
||||||
|
|
||||||
export interface OnPostAuthInterceptorDeps {
|
export interface OnPostAuthInterceptorDeps {
|
||||||
getLegacyAPI(): LegacyAPI;
|
getLegacyAPI(): LegacyAPI;
|
||||||
|
@ -28,7 +28,7 @@ export function initSpacesOnPostAuthRequestInterceptor({
|
||||||
log,
|
log,
|
||||||
http,
|
http,
|
||||||
}: OnPostAuthInterceptorDeps) {
|
}: OnPostAuthInterceptorDeps) {
|
||||||
const { serverBasePath, serverDefaultRoute } = getLegacyAPI().legacyConfig;
|
const { serverBasePath } = getLegacyAPI().legacyConfig;
|
||||||
|
|
||||||
http.registerOnPostAuth(async (request, response, toolkit) => {
|
http.registerOnPostAuth(async (request, response, toolkit) => {
|
||||||
const path = request.url.pathname!;
|
const path = request.url.pathname!;
|
||||||
|
@ -38,6 +38,7 @@ export function initSpacesOnPostAuthRequestInterceptor({
|
||||||
// The root of kibana is also the root of the defaut space,
|
// The root of kibana is also the root of the defaut space,
|
||||||
// since the default space does not have a URL Identifier (i.e., `/s/foo`).
|
// since the default space does not have a URL Identifier (i.e., `/s/foo`).
|
||||||
const isRequestingKibanaRoot = path === '/' && spaceId === DEFAULT_SPACE_ID;
|
const isRequestingKibanaRoot = path === '/' && spaceId === DEFAULT_SPACE_ID;
|
||||||
|
const isRequestingSpaceRoot = path === '/' && spaceId !== DEFAULT_SPACE_ID;
|
||||||
const isRequestingApplication = path.startsWith('/app');
|
const isRequestingApplication = path.startsWith('/app');
|
||||||
|
|
||||||
const spacesClient = await spacesService.scopedClient(request);
|
const spacesClient = await spacesService.scopedClient(request);
|
||||||
|
@ -54,7 +55,7 @@ export function initSpacesOnPostAuthRequestInterceptor({
|
||||||
// No need for an interstitial screen where there is only one possible outcome.
|
// No need for an interstitial screen where there is only one possible outcome.
|
||||||
const space = spaces[0];
|
const space = spaces[0];
|
||||||
|
|
||||||
const destination = addSpaceIdToPath(serverBasePath, space.id, serverDefaultRoute);
|
const destination = addSpaceIdToPath(serverBasePath, space.id, ENTER_SPACE_PATH);
|
||||||
return response.redirected({ headers: { location: destination } });
|
return response.redirected({ headers: { location: destination } });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,6 +73,9 @@ export function initSpacesOnPostAuthRequestInterceptor({
|
||||||
statusCode: wrappedError.output.statusCode,
|
statusCode: wrappedError.output.statusCode,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
} else if (isRequestingSpaceRoot) {
|
||||||
|
const destination = addSpaceIdToPath(serverBasePath, spaceId, ENTER_SPACE_PATH);
|
||||||
|
return response.redirected({ headers: { location: destination } });
|
||||||
}
|
}
|
||||||
|
|
||||||
// This condition should only happen after selecting a space, or when transitioning from one application to another
|
// This condition should only happen after selecting a space, or when transitioning from one application to another
|
||||||
|
|
|
@ -11,9 +11,9 @@ import {
|
||||||
} from 'src/core/server';
|
} from 'src/core/server';
|
||||||
import { format } from 'url';
|
import { format } from 'url';
|
||||||
import { DEFAULT_SPACE_ID } from '../../../common/constants';
|
import { DEFAULT_SPACE_ID } from '../../../common/constants';
|
||||||
import { getSpaceIdFromPath } from '../spaces_url_parser';
|
|
||||||
import { modifyUrl } from '../utils/url';
|
import { modifyUrl } from '../utils/url';
|
||||||
import { LegacyAPI } from '../../new_platform/plugin';
|
import { LegacyAPI } from '../../new_platform/plugin';
|
||||||
|
import { getSpaceIdFromPath } from '../../../common';
|
||||||
|
|
||||||
export interface OnRequestInterceptorDeps {
|
export interface OnRequestInterceptorDeps {
|
||||||
getLegacyAPI(): LegacyAPI;
|
getLegacyAPI(): LegacyAPI;
|
||||||
|
|
|
@ -20,7 +20,6 @@ import { checkLicense } from '../lib/check_license';
|
||||||
import { spacesSavedObjectsClientWrapperFactory } from '../lib/saved_objects_client/saved_objects_client_wrapper_factory';
|
import { spacesSavedObjectsClientWrapperFactory } from '../lib/saved_objects_client/saved_objects_client_wrapper_factory';
|
||||||
import { SpacesAuditLogger } from '../lib/audit_logger';
|
import { SpacesAuditLogger } from '../lib/audit_logger';
|
||||||
import { createSpacesTutorialContextFactory } from '../lib/spaces_tutorial_context_factory';
|
import { createSpacesTutorialContextFactory } from '../lib/spaces_tutorial_context_factory';
|
||||||
import { initInternalApis } from '../routes/api/v1';
|
|
||||||
import { initExternalSpacesApi } from '../routes/api/external';
|
import { initExternalSpacesApi } from '../routes/api/external';
|
||||||
import { getSpacesUsageCollector } from '../lib/get_spaces_usage_collector';
|
import { getSpacesUsageCollector } from '../lib/get_spaces_usage_collector';
|
||||||
import { SpacesService } from './spaces_service';
|
import { SpacesService } from './spaces_service';
|
||||||
|
@ -178,13 +177,6 @@ export class Plugin {
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
initInternalApis({
|
|
||||||
legacyRouter: legacyAPI.router,
|
|
||||||
getLegacyAPI: this.getLegacyAPI,
|
|
||||||
spacesService,
|
|
||||||
xpackMain: xpackMainPlugin,
|
|
||||||
});
|
|
||||||
|
|
||||||
initExternalSpacesApi({
|
initExternalSpacesApi({
|
||||||
legacyRouter: legacyAPI.router,
|
legacyRouter: legacyAPI.router,
|
||||||
log: this.log,
|
log: this.log,
|
||||||
|
|
|
@ -13,9 +13,9 @@ import {
|
||||||
SavedObjectsErrorHelpers,
|
SavedObjectsErrorHelpers,
|
||||||
} from 'src/core/server';
|
} from 'src/core/server';
|
||||||
import { DEFAULT_SPACE_ID } from '../../../common/constants';
|
import { DEFAULT_SPACE_ID } from '../../../common/constants';
|
||||||
import { getSpaceIdFromPath } from '../../lib/spaces_url_parser';
|
|
||||||
import { createOptionalPlugin } from '../../../../../server/lib/optional_plugin';
|
import { createOptionalPlugin } from '../../../../../server/lib/optional_plugin';
|
||||||
import { LegacyAPI } from '../plugin';
|
import { LegacyAPI } from '../plugin';
|
||||||
|
import { getSpaceIdFromPath } from '../../../common';
|
||||||
|
|
||||||
const mockLogger = {
|
const mockLogger = {
|
||||||
trace: jest.fn(),
|
trace: jest.fn(),
|
||||||
|
|
|
@ -12,11 +12,11 @@ import { OptionalPlugin } from '../../../../../server/lib/optional_plugin';
|
||||||
import { DEFAULT_SPACE_ID } from '../../../common/constants';
|
import { DEFAULT_SPACE_ID } from '../../../common/constants';
|
||||||
import { SecurityPlugin } from '../../../../security';
|
import { SecurityPlugin } from '../../../../security';
|
||||||
import { SpacesClient } from '../../lib/spaces_client';
|
import { SpacesClient } from '../../lib/spaces_client';
|
||||||
import { getSpaceIdFromPath, addSpaceIdToPath } from '../../lib/spaces_url_parser';
|
|
||||||
import { SpacesConfigType } from '../config';
|
import { SpacesConfigType } from '../config';
|
||||||
import { namespaceToSpaceId, spaceIdToNamespace } from '../../lib/utils/namespace';
|
import { namespaceToSpaceId, spaceIdToNamespace } from '../../lib/utils/namespace';
|
||||||
import { LegacyAPI } from '../plugin';
|
import { LegacyAPI } from '../plugin';
|
||||||
import { Space } from '../../../common/model/space';
|
import { Space } from '../../../common/model/space';
|
||||||
|
import { getSpaceIdFromPath, addSpaceIdToPath } from '../../../common';
|
||||||
|
|
||||||
type RequestFacade = KibanaRequest | Legacy.Request;
|
type RequestFacade = KibanaRequest | Legacy.Request;
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,6 @@ import { createSpaces } from './create_spaces';
|
||||||
import { ExternalRouteDeps } from '../external';
|
import { ExternalRouteDeps } from '../external';
|
||||||
import { SpacesService } from '../../../new_platform/spaces_service';
|
import { SpacesService } from '../../../new_platform/spaces_service';
|
||||||
import { SpacesAuditLogger } from '../../../lib/audit_logger';
|
import { SpacesAuditLogger } from '../../../lib/audit_logger';
|
||||||
import { InternalRouteDeps } from '../v1';
|
|
||||||
import { LegacyAPI } from '../../../new_platform/plugin';
|
import { LegacyAPI } from '../../../new_platform/plugin';
|
||||||
|
|
||||||
interface KibanaServer extends Legacy.Server {
|
interface KibanaServer extends Legacy.Server {
|
||||||
|
@ -79,9 +78,7 @@ async function readStreamToCompletion(stream: Readable) {
|
||||||
return (createPromiseFromStreams([stream, createConcatStream([])]) as unknown) as any[];
|
return (createPromiseFromStreams([stream, createConcatStream([])]) as unknown) as any[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createTestHandler(
|
export function createTestHandler(initApiFn: (deps: ExternalRouteDeps) => void) {
|
||||||
initApiFn: (deps: ExternalRouteDeps & InternalRouteDeps) => void
|
|
||||||
) {
|
|
||||||
const teardowns: TeardownFn[] = [];
|
const teardowns: TeardownFn[] = [];
|
||||||
|
|
||||||
const spaces = createSpaces();
|
const spaces = createSpaces();
|
||||||
|
@ -254,7 +251,6 @@ export function createTestHandler(
|
||||||
});
|
});
|
||||||
|
|
||||||
initApiFn({
|
initApiFn({
|
||||||
getLegacyAPI: () => legacyAPI,
|
|
||||||
routePreCheckLicenseFn: pre,
|
routePreCheckLicenseFn: pre,
|
||||||
savedObjects: server.savedObjects,
|
savedObjects: server.savedObjects,
|
||||||
spacesService,
|
spacesService,
|
||||||
|
|
|
@ -1,36 +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;
|
|
||||||
* you may not use this file except in compliance with the Elastic License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { Legacy } from 'kibana';
|
|
||||||
import { XPackMainPlugin } from '../../../../../xpack_main/xpack_main';
|
|
||||||
import { routePreCheckLicense } from '../../../lib/route_pre_check_license';
|
|
||||||
import { initInternalSpacesApi } from './spaces';
|
|
||||||
import { SpacesServiceSetup } from '../../../new_platform/spaces_service/spaces_service';
|
|
||||||
import { LegacyAPI } from '../../../new_platform/plugin';
|
|
||||||
|
|
||||||
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
|
|
||||||
|
|
||||||
interface RouteDeps {
|
|
||||||
xpackMain: XPackMainPlugin;
|
|
||||||
spacesService: SpacesServiceSetup;
|
|
||||||
getLegacyAPI(): LegacyAPI;
|
|
||||||
legacyRouter: Legacy.Server['route'];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface InternalRouteDeps extends Omit<RouteDeps, 'xpackMain'> {
|
|
||||||
routePreCheckLicenseFn: any;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function initInternalApis({ xpackMain, ...rest }: RouteDeps) {
|
|
||||||
const routePreCheckLicenseFn = routePreCheckLicense({ xpackMain });
|
|
||||||
|
|
||||||
const deps: InternalRouteDeps = {
|
|
||||||
...rest,
|
|
||||||
routePreCheckLicenseFn,
|
|
||||||
};
|
|
||||||
|
|
||||||
initInternalSpacesApi(deps);
|
|
||||||
}
|
|
|
@ -1,93 +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;
|
|
||||||
* you may not use this file except in compliance with the Elastic License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
jest.mock('../../../lib/route_pre_check_license', () => {
|
|
||||||
return {
|
|
||||||
routePreCheckLicense: () => (request: any, h: any) => h.continue,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
jest.mock('../../../../../../server/lib/get_client_shield', () => {
|
|
||||||
return {
|
|
||||||
getClient: () => {
|
|
||||||
return {
|
|
||||||
callWithInternalUser: jest.fn(() => {
|
|
||||||
return;
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
},
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
import Boom from 'boom';
|
|
||||||
import { createTestHandler, RequestRunner, TeardownFn } from '../__fixtures__';
|
|
||||||
import { initInternalSpacesApi } from './spaces';
|
|
||||||
|
|
||||||
describe('Spaces API', () => {
|
|
||||||
let request: RequestRunner;
|
|
||||||
let teardowns: TeardownFn[];
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
const setup = createTestHandler(initInternalSpacesApi);
|
|
||||||
|
|
||||||
request = setup.request;
|
|
||||||
teardowns = setup.teardowns;
|
|
||||||
});
|
|
||||||
|
|
||||||
afterEach(async () => {
|
|
||||||
await Promise.all(teardowns.splice(0).map(fn => fn()));
|
|
||||||
});
|
|
||||||
|
|
||||||
test('POST space/{id}/select should respond with the new space location', async () => {
|
|
||||||
const { response } = await request('POST', '/api/spaces/v1/space/a-space/select');
|
|
||||||
|
|
||||||
const { statusCode, payload } = response;
|
|
||||||
|
|
||||||
expect(statusCode).toEqual(200);
|
|
||||||
|
|
||||||
const result = JSON.parse(payload);
|
|
||||||
expect(result.location).toEqual('/s/a-space');
|
|
||||||
});
|
|
||||||
|
|
||||||
test(`returns result of routePreCheckLicense`, async () => {
|
|
||||||
const { response } = await request('POST', '/api/spaces/v1/space/a-space/select', {
|
|
||||||
preCheckLicenseImpl: () => Boom.forbidden('test forbidden message'),
|
|
||||||
expectSpacesClientCall: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
const { statusCode, payload } = response;
|
|
||||||
|
|
||||||
expect(statusCode).toEqual(403);
|
|
||||||
expect(JSON.parse(payload)).toMatchObject({
|
|
||||||
message: 'test forbidden message',
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
test('POST space/{id}/select should respond with 404 when the space is not found', async () => {
|
|
||||||
const { response } = await request('POST', '/api/spaces/v1/space/not-a-space/select');
|
|
||||||
|
|
||||||
const { statusCode } = response;
|
|
||||||
|
|
||||||
expect(statusCode).toEqual(404);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('POST space/{id}/select should respond with the new space location when a server.basePath is in use', async () => {
|
|
||||||
const testConfig = {
|
|
||||||
'server.basePath': '/my/base/path',
|
|
||||||
};
|
|
||||||
|
|
||||||
const { response } = await request('POST', '/api/spaces/v1/space/a-space/select', {
|
|
||||||
testConfig,
|
|
||||||
});
|
|
||||||
|
|
||||||
const { statusCode, payload } = response;
|
|
||||||
|
|
||||||
expect(statusCode).toEqual(200);
|
|
||||||
|
|
||||||
const result = JSON.parse(payload);
|
|
||||||
expect(result.location).toEqual('/my/base/path/s/a-space');
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,51 +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;
|
|
||||||
* you may not use this file except in compliance with the Elastic License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import Boom from 'boom';
|
|
||||||
import { Space } from '../../../../common/model/space';
|
|
||||||
import { wrapError } from '../../../lib/errors';
|
|
||||||
import { SpacesClient } from '../../../lib/spaces_client';
|
|
||||||
import { addSpaceIdToPath } from '../../../lib/spaces_url_parser';
|
|
||||||
import { getSpaceById } from '../../lib';
|
|
||||||
import { InternalRouteDeps } from '.';
|
|
||||||
|
|
||||||
export function initInternalSpacesApi(deps: InternalRouteDeps) {
|
|
||||||
const { legacyRouter, spacesService, getLegacyAPI, routePreCheckLicenseFn } = deps;
|
|
||||||
|
|
||||||
legacyRouter({
|
|
||||||
method: 'POST',
|
|
||||||
path: '/api/spaces/v1/space/{id}/select',
|
|
||||||
async handler(request: any) {
|
|
||||||
const { savedObjects, legacyConfig } = getLegacyAPI();
|
|
||||||
|
|
||||||
const { SavedObjectsClient } = savedObjects;
|
|
||||||
const spacesClient: SpacesClient = await spacesService.scopedClient(request);
|
|
||||||
const id = request.params.id;
|
|
||||||
|
|
||||||
const basePath = legacyConfig.serverBasePath;
|
|
||||||
const defaultRoute = legacyConfig.serverDefaultRoute;
|
|
||||||
try {
|
|
||||||
const existingSpace: Space | null = await getSpaceById(
|
|
||||||
spacesClient,
|
|
||||||
id,
|
|
||||||
SavedObjectsClient.errors
|
|
||||||
);
|
|
||||||
if (!existingSpace) {
|
|
||||||
return Boom.notFound();
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
location: addSpaceIdToPath(basePath, existingSpace.id, defaultRoute),
|
|
||||||
};
|
|
||||||
} catch (error) {
|
|
||||||
return wrapError(error);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
options: {
|
|
||||||
pre: [routePreCheckLicenseFn],
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License;
|
||||||
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Legacy } from 'kibana';
|
||||||
|
import { ENTER_SPACE_PATH } from '../../../common/constants';
|
||||||
|
import { wrapError } from '../../lib/errors';
|
||||||
|
|
||||||
|
export function initEnterSpaceView(server: Legacy.Server) {
|
||||||
|
server.route({
|
||||||
|
method: 'GET',
|
||||||
|
path: ENTER_SPACE_PATH,
|
||||||
|
async handler(request, h) {
|
||||||
|
try {
|
||||||
|
return h.redirect(await request.getDefaultRoute());
|
||||||
|
} catch (e) {
|
||||||
|
server.log(['spaces', 'error'], `Error navigating to space: ${e}`);
|
||||||
|
return wrapError(e);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
|
@ -5,3 +5,4 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export { initSpaceSelectorView } from './space_selector';
|
export { initSpaceSelectorView } from './space_selector';
|
||||||
|
export { initEnterSpaceView } from './enter_space';
|
||||||
|
|
|
@ -11074,8 +11074,6 @@
|
||||||
"xpack.spaces.spaceSelector.findSpacePlaceholder": "スペースを検索",
|
"xpack.spaces.spaceSelector.findSpacePlaceholder": "スペースを検索",
|
||||||
"xpack.spaces.spaceSelector.noSpacesMatchSearchCriteriaDescription": "検索条件に一致するスペースがありません",
|
"xpack.spaces.spaceSelector.noSpacesMatchSearchCriteriaDescription": "検索条件に一致するスペースがありません",
|
||||||
"xpack.spaces.spaceSelector.selectSpacesTitle": "スペースの選択",
|
"xpack.spaces.spaceSelector.selectSpacesTitle": "スペースの選択",
|
||||||
"xpack.spaces.spacesManager.unableToChangeSpaceWarningDescription": "後程再試行してください",
|
|
||||||
"xpack.spaces.spacesManager.unableToChangeSpaceWarningTitle": "スペースを変更できません",
|
|
||||||
"xpack.spaces.spacesTitle": "スペース",
|
"xpack.spaces.spacesTitle": "スペース",
|
||||||
"xpack.spaces.management.copyToSpace.actionDescription": "この保存されたオブジェクトを 1 つまたは複数のスペースにコピーします。",
|
"xpack.spaces.management.copyToSpace.actionDescription": "この保存されたオブジェクトを 1 つまたは複数のスペースにコピーします。",
|
||||||
"xpack.spaces.management.copyToSpace.actionTitle": "スペースにコピー",
|
"xpack.spaces.management.copyToSpace.actionTitle": "スペースにコピー",
|
||||||
|
|
|
@ -11076,8 +11076,6 @@
|
||||||
"xpack.spaces.spaceSelector.findSpacePlaceholder": "查找工作区",
|
"xpack.spaces.spaceSelector.findSpacePlaceholder": "查找工作区",
|
||||||
"xpack.spaces.spaceSelector.noSpacesMatchSearchCriteriaDescription": "没有匹配搜索条件的空间",
|
"xpack.spaces.spaceSelector.noSpacesMatchSearchCriteriaDescription": "没有匹配搜索条件的空间",
|
||||||
"xpack.spaces.spaceSelector.selectSpacesTitle": "选择您的空间",
|
"xpack.spaces.spaceSelector.selectSpacesTitle": "选择您的空间",
|
||||||
"xpack.spaces.spacesManager.unableToChangeSpaceWarningDescription": "请稍后重试",
|
|
||||||
"xpack.spaces.spacesManager.unableToChangeSpaceWarningTitle": "无法更改空间",
|
|
||||||
"xpack.spaces.spacesTitle": "工作区",
|
"xpack.spaces.spacesTitle": "工作区",
|
||||||
"xpack.spaces.management.copyToSpace.actionDescription": "将此已保存对象复制到一个或多个工作区",
|
"xpack.spaces.management.copyToSpace.actionDescription": "将此已保存对象复制到一个或多个工作区",
|
||||||
"xpack.spaces.management.copyToSpace.actionTitle": "复制到工作区",
|
"xpack.spaces.management.copyToSpace.actionTitle": "复制到工作区",
|
||||||
|
|
60
x-pack/test/functional/apps/spaces/enter_space.ts
Normal file
60
x-pack/test/functional/apps/spaces/enter_space.ts
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License;
|
||||||
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
|
*/
|
||||||
|
import { FtrProviderContext } from '../../ftr_provider_context';
|
||||||
|
|
||||||
|
export default function enterSpaceFunctonalTests({
|
||||||
|
getService,
|
||||||
|
getPageObjects,
|
||||||
|
}: FtrProviderContext) {
|
||||||
|
const esArchiver = getService('esArchiver');
|
||||||
|
const kibanaServer = getService('kibanaServer');
|
||||||
|
const PageObjects = getPageObjects(['security', 'spaceSelector']);
|
||||||
|
|
||||||
|
describe('Enter Space', function() {
|
||||||
|
this.tags('smoke');
|
||||||
|
before(async () => await esArchiver.load('spaces/enter_space'));
|
||||||
|
after(async () => await esArchiver.unload('spaces/enter_space'));
|
||||||
|
|
||||||
|
afterEach(async () => {
|
||||||
|
await PageObjects.security.logout();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('allows user to navigate to different spaces, respecting the configured default route', async () => {
|
||||||
|
const spaceId = 'another-space';
|
||||||
|
|
||||||
|
await PageObjects.security.login(null, null, {
|
||||||
|
expectSpaceSelector: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
await PageObjects.spaceSelector.clickSpaceCard(spaceId);
|
||||||
|
|
||||||
|
await PageObjects.spaceSelector.expectRoute(spaceId, '/app/kibana/#/dashboard');
|
||||||
|
|
||||||
|
await PageObjects.spaceSelector.openSpacesNav();
|
||||||
|
|
||||||
|
// change spaces
|
||||||
|
|
||||||
|
await PageObjects.spaceSelector.clickSpaceAvatar('default');
|
||||||
|
|
||||||
|
await PageObjects.spaceSelector.expectRoute('default', '/app/canvas');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('falls back to the default home page when the configured default route is malformed', async () => {
|
||||||
|
await kibanaServer.uiSettings.replace({ defaultRoute: 'http://example.com/evil' });
|
||||||
|
|
||||||
|
// This test only works with the default space, as other spaces have an enforced relative url of `${serverBasePath}/s/space-id/${defaultRoute}`
|
||||||
|
const spaceId = 'default';
|
||||||
|
|
||||||
|
await PageObjects.security.login(null, null, {
|
||||||
|
expectSpaceSelector: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
await PageObjects.spaceSelector.clickSpaceCard(spaceId);
|
||||||
|
|
||||||
|
await PageObjects.spaceSelector.expectHomePage(spaceId);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
|
@ -12,5 +12,6 @@ export default function spacesApp({ loadTestFile }: FtrProviderContext) {
|
||||||
loadTestFile(require.resolve('./copy_saved_objects'));
|
loadTestFile(require.resolve('./copy_saved_objects'));
|
||||||
loadTestFile(require.resolve('./feature_controls/spaces_security'));
|
loadTestFile(require.resolve('./feature_controls/spaces_security'));
|
||||||
loadTestFile(require.resolve('./spaces_selection'));
|
loadTestFile(require.resolve('./spaces_selection'));
|
||||||
|
loadTestFile(require.resolve('./enter_space'));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,287 @@
|
||||||
|
{
|
||||||
|
"type": "index",
|
||||||
|
"value": {
|
||||||
|
"index": ".kibana",
|
||||||
|
"mappings": {
|
||||||
|
"properties": {
|
||||||
|
"config": {
|
||||||
|
"dynamic": "true",
|
||||||
|
"properties": {
|
||||||
|
"buildNum": {
|
||||||
|
"type": "keyword"
|
||||||
|
},
|
||||||
|
"dateFormat:tz": {
|
||||||
|
"fields": {
|
||||||
|
"keyword": {
|
||||||
|
"ignore_above": 256,
|
||||||
|
"type": "keyword"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
"defaultRoute": {
|
||||||
|
"type": "keyword"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"dashboard": {
|
||||||
|
"dynamic": "strict",
|
||||||
|
"properties": {
|
||||||
|
"description": {
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
"hits": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"kibanaSavedObjectMeta": {
|
||||||
|
"properties": {
|
||||||
|
"searchSourceJSON": {
|
||||||
|
"type": "text"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"optionsJSON": {
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
"panelsJSON": {
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
"refreshInterval": {
|
||||||
|
"properties": {
|
||||||
|
"display": {
|
||||||
|
"type": "keyword"
|
||||||
|
},
|
||||||
|
"pause": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"section": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"value": {
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"timeFrom": {
|
||||||
|
"type": "keyword"
|
||||||
|
},
|
||||||
|
"timeRestore": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"timeTo": {
|
||||||
|
"type": "keyword"
|
||||||
|
},
|
||||||
|
"title": {
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
"uiStateJSON": {
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
"version": {
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"index-pattern": {
|
||||||
|
"dynamic": "strict",
|
||||||
|
"properties": {
|
||||||
|
"fieldFormatMap": {
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
"fields": {
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
"intervalName": {
|
||||||
|
"type": "keyword"
|
||||||
|
},
|
||||||
|
"notExpandable": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"sourceFilters": {
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
"timeFieldName": {
|
||||||
|
"type": "keyword"
|
||||||
|
},
|
||||||
|
"title": {
|
||||||
|
"type": "text"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"search": {
|
||||||
|
"dynamic": "strict",
|
||||||
|
"properties": {
|
||||||
|
"columns": {
|
||||||
|
"type": "keyword"
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
"hits": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"kibanaSavedObjectMeta": {
|
||||||
|
"properties": {
|
||||||
|
"searchSourceJSON": {
|
||||||
|
"type": "text"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"sort": {
|
||||||
|
"type": "keyword"
|
||||||
|
},
|
||||||
|
"title": {
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
"version": {
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"server": {
|
||||||
|
"dynamic": "strict",
|
||||||
|
"properties": {
|
||||||
|
"uuid": {
|
||||||
|
"type": "keyword"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"space": {
|
||||||
|
"properties": {
|
||||||
|
"_reserved": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"color": {
|
||||||
|
"type": "keyword"
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
"disabledFeatures": {
|
||||||
|
"type": "keyword"
|
||||||
|
},
|
||||||
|
"initials": {
|
||||||
|
"type": "keyword"
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"fields": {
|
||||||
|
"keyword": {
|
||||||
|
"ignore_above": 2048,
|
||||||
|
"type": "keyword"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"type": "text"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"spaceId": {
|
||||||
|
"type": "keyword"
|
||||||
|
},
|
||||||
|
"timelion-sheet": {
|
||||||
|
"dynamic": "strict",
|
||||||
|
"properties": {
|
||||||
|
"description": {
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
"hits": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"kibanaSavedObjectMeta": {
|
||||||
|
"properties": {
|
||||||
|
"searchSourceJSON": {
|
||||||
|
"type": "text"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"timelion_chart_height": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"timelion_columns": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"timelion_interval": {
|
||||||
|
"type": "keyword"
|
||||||
|
},
|
||||||
|
"timelion_other_interval": {
|
||||||
|
"type": "keyword"
|
||||||
|
},
|
||||||
|
"timelion_rows": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"timelion_sheet": {
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
"title": {
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
"version": {
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"type": "keyword"
|
||||||
|
},
|
||||||
|
"url": {
|
||||||
|
"dynamic": "strict",
|
||||||
|
"properties": {
|
||||||
|
"accessCount": {
|
||||||
|
"type": "long"
|
||||||
|
},
|
||||||
|
"accessDate": {
|
||||||
|
"type": "date"
|
||||||
|
},
|
||||||
|
"createDate": {
|
||||||
|
"type": "date"
|
||||||
|
},
|
||||||
|
"url": {
|
||||||
|
"fields": {
|
||||||
|
"keyword": {
|
||||||
|
"ignore_above": 2048,
|
||||||
|
"type": "keyword"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"type": "text"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"visualization": {
|
||||||
|
"dynamic": "strict",
|
||||||
|
"properties": {
|
||||||
|
"description": {
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
"kibanaSavedObjectMeta": {
|
||||||
|
"properties": {
|
||||||
|
"searchSourceJSON": {
|
||||||
|
"type": "text"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"savedSearchId": {
|
||||||
|
"type": "keyword"
|
||||||
|
},
|
||||||
|
"title": {
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
"uiStateJSON": {
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
"version": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"visState": {
|
||||||
|
"type": "text"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"settings": {
|
||||||
|
"index": {
|
||||||
|
"number_of_replicas": "1",
|
||||||
|
"number_of_shards": "1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -28,14 +28,18 @@ export function SpaceSelectorPageProvider({ getService, getPageObjects }) {
|
||||||
}
|
}
|
||||||
|
|
||||||
async expectHomePage(spaceId) {
|
async expectHomePage(spaceId) {
|
||||||
|
return await this.expectRoute(spaceId, `/app/kibana#/home`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async expectRoute(spaceId, route) {
|
||||||
return await retry.try(async () => {
|
return await retry.try(async () => {
|
||||||
log.debug(`expectHomePage(${spaceId})`);
|
log.debug(`expectRoute(${spaceId}, ${route})`);
|
||||||
await find.byCssSelector('[data-test-subj="kibanaChrome"] nav:not(.ng-hide) ', 20000);
|
await find.byCssSelector('[data-test-subj="kibanaChrome"] nav:not(.ng-hide) ', 20000);
|
||||||
const url = await browser.getCurrentUrl();
|
const url = await browser.getCurrentUrl();
|
||||||
if (spaceId === 'default') {
|
if (spaceId === 'default') {
|
||||||
expect(url).to.contain(`/app/kibana#/home`);
|
expect(url).to.contain(route);
|
||||||
} else {
|
} else {
|
||||||
expect(url).to.contain(`/s/${spaceId}/app/kibana#/home`);
|
expect(url).to.contain(`/s/${spaceId}${route}`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,125 +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;
|
|
||||||
* you may not use this file except in compliance with the Elastic License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import expect from '@kbn/expect';
|
|
||||||
import { SuperTest } from 'supertest';
|
|
||||||
import { DEFAULT_SPACE_ID } from '../../../../legacy/plugins/spaces/common/constants';
|
|
||||||
import { getUrlPrefix } from '../lib/space_test_utils';
|
|
||||||
import { DescribeFn, TestDefinitionAuthentication } from '../lib/types';
|
|
||||||
|
|
||||||
interface SelectTest {
|
|
||||||
statusCode: number;
|
|
||||||
response: (resp: { [key: string]: any }) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface SelectTests {
|
|
||||||
default: SelectTest;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface SelectTestDefinition {
|
|
||||||
user?: TestDefinitionAuthentication;
|
|
||||||
currentSpaceId: string;
|
|
||||||
selectSpaceId: string;
|
|
||||||
tests: SelectTests;
|
|
||||||
}
|
|
||||||
|
|
||||||
const nonExistantSpaceId = 'not-a-space';
|
|
||||||
|
|
||||||
export function selectTestSuiteFactory(esArchiver: any, supertest: SuperTest<any>) {
|
|
||||||
const createExpectEmptyResult = () => (resp: { [key: string]: any }) => {
|
|
||||||
expect(resp.body).to.eql('');
|
|
||||||
};
|
|
||||||
|
|
||||||
const createExpectNotFoundResult = () => (resp: { [key: string]: any }) => {
|
|
||||||
expect(resp.body).to.eql({
|
|
||||||
error: 'Not Found',
|
|
||||||
message: 'Not Found',
|
|
||||||
statusCode: 404,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const createExpectRbacForbidden = (spaceId: any) => (resp: { [key: string]: any }) => {
|
|
||||||
expect(resp.body).to.eql({
|
|
||||||
statusCode: 403,
|
|
||||||
error: 'Forbidden',
|
|
||||||
message: `Unauthorized to get ${spaceId} space`,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const createExpectResults = (spaceId: string) => (resp: { [key: string]: any }) => {
|
|
||||||
const allSpaces = [
|
|
||||||
{
|
|
||||||
id: 'default',
|
|
||||||
name: 'Default Space',
|
|
||||||
description: 'This is the default space',
|
|
||||||
disabledFeatures: [],
|
|
||||||
_reserved: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'space_1',
|
|
||||||
name: 'Space 1',
|
|
||||||
description: 'This is the first test space',
|
|
||||||
disabledFeatures: [],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'space_2',
|
|
||||||
name: 'Space 2',
|
|
||||||
description: 'This is the second test space',
|
|
||||||
disabledFeatures: [],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
expect(resp.body).to.eql(allSpaces.find(space => space.id === spaceId));
|
|
||||||
};
|
|
||||||
|
|
||||||
const createExpectSpaceResponse = (spaceId: string) => (resp: { [key: string]: any }) => {
|
|
||||||
if (spaceId === DEFAULT_SPACE_ID) {
|
|
||||||
expectDefaultSpaceResponse(resp);
|
|
||||||
} else {
|
|
||||||
expect(resp.body).to.eql({
|
|
||||||
location: `/s/${spaceId}/app/kibana`,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const expectDefaultSpaceResponse = (resp: { [key: string]: any }) => {
|
|
||||||
expect(resp.body).to.eql({
|
|
||||||
location: `/app/kibana`,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const makeSelectTest = (describeFn: DescribeFn) => (
|
|
||||||
description: string,
|
|
||||||
{ user = {}, currentSpaceId, selectSpaceId, tests }: SelectTestDefinition
|
|
||||||
) => {
|
|
||||||
describeFn(description, () => {
|
|
||||||
before(() => esArchiver.load('saved_objects/spaces'));
|
|
||||||
after(() => esArchiver.unload('saved_objects/spaces'));
|
|
||||||
|
|
||||||
it(`should return ${tests.default.statusCode}`, async () => {
|
|
||||||
return supertest
|
|
||||||
.post(`${getUrlPrefix(currentSpaceId)}/api/spaces/v1/space/${selectSpaceId}/select`)
|
|
||||||
.auth(user.username, user.password)
|
|
||||||
.expect(tests.default.statusCode)
|
|
||||||
.then(tests.default.response);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const selectTest = makeSelectTest(describe);
|
|
||||||
// @ts-ignore
|
|
||||||
selectTest.only = makeSelectTest(describe.only);
|
|
||||||
|
|
||||||
return {
|
|
||||||
createExpectEmptyResult,
|
|
||||||
createExpectNotFoundResult,
|
|
||||||
createExpectRbacForbidden,
|
|
||||||
createExpectResults,
|
|
||||||
createExpectSpaceResponse,
|
|
||||||
expectDefaultSpaceResponse,
|
|
||||||
nonExistantSpaceId,
|
|
||||||
selectTest,
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -25,7 +25,6 @@ export default function({ loadTestFile, getService }: TestInvoker) {
|
||||||
loadTestFile(require.resolve('./delete'));
|
loadTestFile(require.resolve('./delete'));
|
||||||
loadTestFile(require.resolve('./get_all'));
|
loadTestFile(require.resolve('./get_all'));
|
||||||
loadTestFile(require.resolve('./get'));
|
loadTestFile(require.resolve('./get'));
|
||||||
loadTestFile(require.resolve('./select'));
|
|
||||||
loadTestFile(require.resolve('./update'));
|
loadTestFile(require.resolve('./update'));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,341 +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;
|
|
||||||
* you may not use this file except in compliance with the Elastic License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { AUTHENTICATION } from '../../common/lib/authentication';
|
|
||||||
import { SPACES } from '../../common/lib/spaces';
|
|
||||||
import { TestInvoker } from '../../common/lib/types';
|
|
||||||
import { selectTestSuiteFactory } from '../../common/suites/select';
|
|
||||||
|
|
||||||
// eslint-disable-next-line import/no-default-export
|
|
||||||
export default function selectSpaceTestSuite({ getService }: TestInvoker) {
|
|
||||||
const supertestWithoutAuth = getService('supertestWithoutAuth');
|
|
||||||
const esArchiver = getService('esArchiver');
|
|
||||||
|
|
||||||
const {
|
|
||||||
selectTest,
|
|
||||||
nonExistantSpaceId,
|
|
||||||
createExpectSpaceResponse,
|
|
||||||
createExpectRbacForbidden,
|
|
||||||
createExpectNotFoundResult,
|
|
||||||
} = selectTestSuiteFactory(esArchiver, supertestWithoutAuth);
|
|
||||||
|
|
||||||
describe('select', () => {
|
|
||||||
// Tests with users that have privileges globally in Kibana
|
|
||||||
[
|
|
||||||
{
|
|
||||||
currentSpaceId: SPACES.DEFAULT.spaceId,
|
|
||||||
selectSpaceId: SPACES.SPACE_1.spaceId,
|
|
||||||
users: {
|
|
||||||
noAccess: AUTHENTICATION.NOT_A_KIBANA_USER,
|
|
||||||
superuser: AUTHENTICATION.SUPERUSER,
|
|
||||||
allGlobally: AUTHENTICATION.KIBANA_RBAC_USER,
|
|
||||||
readGlobally: AUTHENTICATION.KIBANA_RBAC_DASHBOARD_ONLY_USER,
|
|
||||||
legacyAll: AUTHENTICATION.KIBANA_LEGACY_USER,
|
|
||||||
dualAll: AUTHENTICATION.KIBANA_DUAL_PRIVILEGES_USER,
|
|
||||||
dualRead: AUTHENTICATION.KIBANA_DUAL_PRIVILEGES_DASHBOARD_ONLY_USER,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
currentSpaceId: SPACES.SPACE_1.spaceId,
|
|
||||||
selectSpaceId: SPACES.DEFAULT.spaceId,
|
|
||||||
users: {
|
|
||||||
noAccess: AUTHENTICATION.NOT_A_KIBANA_USER,
|
|
||||||
superuser: AUTHENTICATION.SUPERUSER,
|
|
||||||
allGlobally: AUTHENTICATION.KIBANA_RBAC_USER,
|
|
||||||
readGlobally: AUTHENTICATION.KIBANA_RBAC_DASHBOARD_ONLY_USER,
|
|
||||||
legacyAll: AUTHENTICATION.KIBANA_LEGACY_USER,
|
|
||||||
dualAll: AUTHENTICATION.KIBANA_DUAL_PRIVILEGES_USER,
|
|
||||||
dualRead: AUTHENTICATION.KIBANA_DUAL_PRIVILEGES_DASHBOARD_ONLY_USER,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
].forEach(scenario => {
|
|
||||||
selectTest(
|
|
||||||
`user with no access selects ${scenario.selectSpaceId} space from the ${scenario.currentSpaceId} space`,
|
|
||||||
{
|
|
||||||
currentSpaceId: scenario.currentSpaceId,
|
|
||||||
selectSpaceId: scenario.selectSpaceId,
|
|
||||||
user: scenario.users.noAccess,
|
|
||||||
tests: {
|
|
||||||
default: {
|
|
||||||
statusCode: 403,
|
|
||||||
response: createExpectRbacForbidden(scenario.selectSpaceId),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
selectTest(
|
|
||||||
`superuser selects ${scenario.selectSpaceId} space from the ${scenario.currentSpaceId} space`,
|
|
||||||
{
|
|
||||||
currentSpaceId: scenario.currentSpaceId,
|
|
||||||
selectSpaceId: scenario.selectSpaceId,
|
|
||||||
user: scenario.users.superuser,
|
|
||||||
tests: {
|
|
||||||
default: {
|
|
||||||
statusCode: 200,
|
|
||||||
response: createExpectSpaceResponse(scenario.selectSpaceId),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
selectTest(
|
|
||||||
`rbac user with all globally selects ${scenario.selectSpaceId} space from the ${scenario.currentSpaceId} space`,
|
|
||||||
{
|
|
||||||
currentSpaceId: scenario.currentSpaceId,
|
|
||||||
selectSpaceId: scenario.selectSpaceId,
|
|
||||||
user: scenario.users.allGlobally,
|
|
||||||
tests: {
|
|
||||||
default: {
|
|
||||||
statusCode: 200,
|
|
||||||
response: createExpectSpaceResponse(scenario.selectSpaceId),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
selectTest(
|
|
||||||
`dual-privileges user selects ${scenario.selectSpaceId} space from the ${scenario.currentSpaceId}`,
|
|
||||||
{
|
|
||||||
currentSpaceId: scenario.currentSpaceId,
|
|
||||||
selectSpaceId: scenario.selectSpaceId,
|
|
||||||
user: scenario.users.dualAll,
|
|
||||||
tests: {
|
|
||||||
default: {
|
|
||||||
statusCode: 200,
|
|
||||||
response: createExpectSpaceResponse(scenario.selectSpaceId),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
selectTest(
|
|
||||||
`legacy user selects ${scenario.selectSpaceId} space from the ${scenario.currentSpaceId}`,
|
|
||||||
{
|
|
||||||
currentSpaceId: scenario.currentSpaceId,
|
|
||||||
selectSpaceId: scenario.selectSpaceId,
|
|
||||||
user: scenario.users.legacyAll,
|
|
||||||
tests: {
|
|
||||||
default: {
|
|
||||||
statusCode: 403,
|
|
||||||
response: createExpectRbacForbidden(scenario.selectSpaceId),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
selectTest(
|
|
||||||
`user with read globally selects ${scenario.selectSpaceId} space from the
|
|
||||||
${scenario.currentSpaceId} space`,
|
|
||||||
{
|
|
||||||
currentSpaceId: scenario.currentSpaceId,
|
|
||||||
selectSpaceId: scenario.selectSpaceId,
|
|
||||||
user: scenario.users.readGlobally,
|
|
||||||
tests: {
|
|
||||||
default: {
|
|
||||||
statusCode: 200,
|
|
||||||
response: createExpectSpaceResponse(scenario.selectSpaceId),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
selectTest(
|
|
||||||
`dual-privileges readonly user selects ${scenario.selectSpaceId} space from
|
|
||||||
the ${scenario.currentSpaceId}`,
|
|
||||||
{
|
|
||||||
currentSpaceId: scenario.currentSpaceId,
|
|
||||||
selectSpaceId: scenario.selectSpaceId,
|
|
||||||
user: scenario.users.dualRead,
|
|
||||||
tests: {
|
|
||||||
default: {
|
|
||||||
statusCode: 200,
|
|
||||||
response: createExpectSpaceResponse(scenario.selectSpaceId),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Select the same space that you're currently in with users which have space specific privileges.
|
|
||||||
// Our intent is to ensure that you have privileges at the space that you're selecting.
|
|
||||||
[
|
|
||||||
{
|
|
||||||
spaceId: SPACES.DEFAULT.spaceId,
|
|
||||||
users: {
|
|
||||||
allAtSpace: AUTHENTICATION.KIBANA_RBAC_DEFAULT_SPACE_ALL_USER,
|
|
||||||
readAtSpace: AUTHENTICATION.KIBANA_RBAC_DEFAULT_SPACE_READ_USER,
|
|
||||||
allAtOtherSpace: AUTHENTICATION.KIBANA_RBAC_SPACE_1_ALL_USER,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
spaceId: SPACES.SPACE_1.spaceId,
|
|
||||||
users: {
|
|
||||||
allAtSpace: AUTHENTICATION.KIBANA_RBAC_SPACE_1_ALL_USER,
|
|
||||||
readAtSpace: AUTHENTICATION.KIBANA_RBAC_SPACE_1_READ_USER,
|
|
||||||
allAtOtherSpace: AUTHENTICATION.KIBANA_RBAC_DEFAULT_SPACE_ALL_USER,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
].forEach(scenario => {
|
|
||||||
selectTest(
|
|
||||||
`rbac user with all at space can select ${scenario.spaceId}
|
|
||||||
from the same space`,
|
|
||||||
{
|
|
||||||
currentSpaceId: scenario.spaceId,
|
|
||||||
selectSpaceId: scenario.spaceId,
|
|
||||||
user: scenario.users.allAtSpace,
|
|
||||||
tests: {
|
|
||||||
default: {
|
|
||||||
statusCode: 200,
|
|
||||||
response: createExpectSpaceResponse(scenario.spaceId),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
selectTest(
|
|
||||||
`rbac user with read at space can select ${scenario.spaceId}
|
|
||||||
from the same space`,
|
|
||||||
{
|
|
||||||
currentSpaceId: scenario.spaceId,
|
|
||||||
selectSpaceId: scenario.spaceId,
|
|
||||||
user: scenario.users.readAtSpace,
|
|
||||||
tests: {
|
|
||||||
default: {
|
|
||||||
statusCode: 200,
|
|
||||||
response: createExpectSpaceResponse(scenario.spaceId),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
selectTest(
|
|
||||||
`rbac user with all at other space cannot select ${scenario.spaceId}
|
|
||||||
from the same space`,
|
|
||||||
{
|
|
||||||
currentSpaceId: scenario.spaceId,
|
|
||||||
selectSpaceId: scenario.spaceId,
|
|
||||||
user: scenario.users.allAtOtherSpace,
|
|
||||||
tests: {
|
|
||||||
default: {
|
|
||||||
statusCode: 403,
|
|
||||||
response: createExpectRbacForbidden(scenario.spaceId),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Select a different space with users that only have privileges at certain spaces. Our intent
|
|
||||||
// is to ensure that a user can select a space based on their privileges at the space that they're selecting
|
|
||||||
// not at the space that they're currently in.
|
|
||||||
[
|
|
||||||
{
|
|
||||||
currentSpaceId: SPACES.SPACE_2.spaceId,
|
|
||||||
selectSpaceId: SPACES.SPACE_1.spaceId,
|
|
||||||
users: {
|
|
||||||
userWithAllAtSpace: AUTHENTICATION.KIBANA_RBAC_SPACE_1_ALL_USER,
|
|
||||||
userWithAllAtOtherSpace: AUTHENTICATION.KIBANA_RBAC_SPACE_2_ALL_USER,
|
|
||||||
userWithAllAtBothSpaces: AUTHENTICATION.KIBANA_RBAC_SPACE_1_2_ALL_USER,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
].forEach(scenario => {
|
|
||||||
selectTest(
|
|
||||||
`rbac user with all at ${scenario.selectSpaceId} can select ${scenario.selectSpaceId}
|
|
||||||
from ${scenario.currentSpaceId}`,
|
|
||||||
{
|
|
||||||
currentSpaceId: scenario.currentSpaceId,
|
|
||||||
selectSpaceId: scenario.selectSpaceId,
|
|
||||||
user: scenario.users.userWithAllAtSpace,
|
|
||||||
tests: {
|
|
||||||
default: {
|
|
||||||
statusCode: 200,
|
|
||||||
response: createExpectSpaceResponse(scenario.selectSpaceId),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
selectTest(
|
|
||||||
`rbac user with all at both spaces can select ${scenario.selectSpaceId}
|
|
||||||
from ${scenario.currentSpaceId}`,
|
|
||||||
{
|
|
||||||
currentSpaceId: scenario.currentSpaceId,
|
|
||||||
selectSpaceId: scenario.selectSpaceId,
|
|
||||||
user: scenario.users.userWithAllAtBothSpaces,
|
|
||||||
tests: {
|
|
||||||
default: {
|
|
||||||
statusCode: 200,
|
|
||||||
response: createExpectSpaceResponse(scenario.selectSpaceId),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
selectTest(
|
|
||||||
`rbac user with all at ${scenario.currentSpaceId} space cannot select ${scenario.selectSpaceId}
|
|
||||||
from ${scenario.currentSpaceId}`,
|
|
||||||
{
|
|
||||||
currentSpaceId: scenario.currentSpaceId,
|
|
||||||
selectSpaceId: scenario.selectSpaceId,
|
|
||||||
user: scenario.users.userWithAllAtOtherSpace,
|
|
||||||
tests: {
|
|
||||||
default: {
|
|
||||||
statusCode: 403,
|
|
||||||
response: createExpectRbacForbidden(scenario.selectSpaceId),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Select non-existent spaces and ensure we get a 404 or a 403
|
|
||||||
describe('non-existent space', () => {
|
|
||||||
[
|
|
||||||
{
|
|
||||||
currentSpaceId: SPACES.DEFAULT.spaceId,
|
|
||||||
selectSpaceId: nonExistantSpaceId,
|
|
||||||
users: {
|
|
||||||
userWithAllGlobally: AUTHENTICATION.KIBANA_RBAC_USER,
|
|
||||||
userWithAllAtSpace: AUTHENTICATION.KIBANA_RBAC_DEFAULT_SPACE_ALL_USER,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
currentSpaceId: SPACES.SPACE_1.spaceId,
|
|
||||||
selectSpaceId: nonExistantSpaceId,
|
|
||||||
users: {
|
|
||||||
userWithAllGlobally: AUTHENTICATION.KIBANA_RBAC_USER,
|
|
||||||
userWithAllAtSpace: AUTHENTICATION.KIBANA_RBAC_SPACE_1_ALL_USER,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
].forEach(scenario => {
|
|
||||||
selectTest(`rbac user with all globally cannot access non-existent space`, {
|
|
||||||
currentSpaceId: scenario.currentSpaceId,
|
|
||||||
selectSpaceId: scenario.selectSpaceId,
|
|
||||||
user: scenario.users.userWithAllGlobally,
|
|
||||||
tests: {
|
|
||||||
default: {
|
|
||||||
statusCode: 404,
|
|
||||||
response: createExpectNotFoundResult(),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
selectTest(`rbac user with all at space cannot access non-existent space`, {
|
|
||||||
currentSpaceId: scenario.currentSpaceId,
|
|
||||||
selectSpaceId: scenario.selectSpaceId,
|
|
||||||
user: scenario.users.userWithAllAtSpace,
|
|
||||||
tests: {
|
|
||||||
default: {
|
|
||||||
statusCode: 403,
|
|
||||||
response: createExpectRbacForbidden(scenario.selectSpaceId),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
|
@ -17,7 +17,6 @@ export default function spacesOnlyTestSuite({ loadTestFile }: TestInvoker) {
|
||||||
loadTestFile(require.resolve('./delete'));
|
loadTestFile(require.resolve('./delete'));
|
||||||
loadTestFile(require.resolve('./get_all'));
|
loadTestFile(require.resolve('./get_all'));
|
||||||
loadTestFile(require.resolve('./get'));
|
loadTestFile(require.resolve('./get'));
|
||||||
loadTestFile(require.resolve('./select'));
|
|
||||||
loadTestFile(require.resolve('./update'));
|
loadTestFile(require.resolve('./update'));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,74 +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;
|
|
||||||
* you may not use this file except in compliance with the Elastic License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { SPACES } from '../../common/lib/spaces';
|
|
||||||
import { TestInvoker } from '../../common/lib/types';
|
|
||||||
import { selectTestSuiteFactory } from '../../common/suites/select';
|
|
||||||
|
|
||||||
// eslint-disable-next-line import/no-default-export
|
|
||||||
export default function selectSpaceTestSuite({ getService }: TestInvoker) {
|
|
||||||
const supertestWithoutAuth = getService('supertestWithoutAuth');
|
|
||||||
const esArchiver = getService('esArchiver');
|
|
||||||
|
|
||||||
const {
|
|
||||||
selectTest,
|
|
||||||
createExpectSpaceResponse,
|
|
||||||
createExpectNotFoundResult,
|
|
||||||
nonExistantSpaceId,
|
|
||||||
} = selectTestSuiteFactory(esArchiver, supertestWithoutAuth);
|
|
||||||
|
|
||||||
describe('select', () => {
|
|
||||||
[
|
|
||||||
{
|
|
||||||
spaceId: SPACES.DEFAULT.spaceId,
|
|
||||||
otherSpaceId: SPACES.SPACE_1.spaceId,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
spaceId: SPACES.SPACE_1.spaceId,
|
|
||||||
otherSpaceId: SPACES.DEFAULT.spaceId,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
spaceId: SPACES.SPACE_1.spaceId,
|
|
||||||
otherSpaceId: SPACES.SPACE_2.spaceId,
|
|
||||||
},
|
|
||||||
].forEach(scenario => {
|
|
||||||
selectTest(`can select ${scenario.otherSpaceId} from ${scenario.spaceId}`, {
|
|
||||||
currentSpaceId: scenario.spaceId,
|
|
||||||
selectSpaceId: scenario.otherSpaceId,
|
|
||||||
tests: {
|
|
||||||
default: {
|
|
||||||
statusCode: 200,
|
|
||||||
response: createExpectSpaceResponse(scenario.otherSpaceId),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('non-existant space', () => {
|
|
||||||
[
|
|
||||||
{
|
|
||||||
spaceId: SPACES.DEFAULT.spaceId,
|
|
||||||
otherSpaceId: nonExistantSpaceId,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
spaceId: SPACES.SPACE_1.spaceId,
|
|
||||||
otherSpaceId: nonExistantSpaceId,
|
|
||||||
},
|
|
||||||
].forEach(scenario => {
|
|
||||||
selectTest(`cannot select non-existant space from ${scenario.spaceId}`, {
|
|
||||||
currentSpaceId: scenario.spaceId,
|
|
||||||
selectSpaceId: scenario.otherSpaceId,
|
|
||||||
tests: {
|
|
||||||
default: {
|
|
||||||
statusCode: 404,
|
|
||||||
response: createExpectNotFoundResult(),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
Loading…
Reference in a new issue