Skip to content

Commit

Permalink
Merge branch '7.x' into backport/7.x/pr-70413
Browse files Browse the repository at this point in the history
  • Loading branch information
elasticmachine authored Jul 6, 2020
2 parents 7f67e82 + 36c5490 commit 4a5eda0
Show file tree
Hide file tree
Showing 113 changed files with 2,451 additions and 1,039 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,8 @@ afterAll(async () => {
await del(TMP_DIR);
});

it('builds expected bundles, saves bundle counts to metadata', async () => {
// FLAKY: https://github.com/elastic/kibana/issues/70762
it.skip('builds expected bundles, saves bundle counts to metadata', async () => {
const config = OptimizerConfig.create({
repoRoot: MOCK_REPO_DIR,
pluginScanDirs: [Path.resolve(MOCK_REPO_DIR, 'plugins')],
Expand Down Expand Up @@ -167,7 +168,8 @@ it('builds expected bundles, saves bundle counts to metadata', async () => {
`);
});

it('uses cache on second run and exist cleanly', async () => {
// FLAKY: https://github.com/elastic/kibana/issues/70764
it.skip('uses cache on second run and exist cleanly', async () => {
const config = OptimizerConfig.create({
repoRoot: MOCK_REPO_DIR,
pluginScanDirs: [Path.resolve(MOCK_REPO_DIR, 'plugins')],
Expand Down
1 change: 1 addition & 0 deletions test/functional/apps/dashboard/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ export default function ({ getService, loadTestFile }) {
after(unloadCurrentData);

loadTestFile(require.resolve('./empty_dashboard'));
loadTestFile(require.resolve('./url_field_formatter'));
loadTestFile(require.resolve('./embeddable_rendering'));
loadTestFile(require.resolve('./create_and_add_embeddables'));
loadTestFile(require.resolve('./edit_embeddable_redirects'));
Expand Down
91 changes: 91 additions & 0 deletions test/functional/apps/dashboard/url_field_formatter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/*
* 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 expect from '@kbn/expect';
import { WebElementWrapper } from 'test/functional/services/lib/web_element_wrapper';
import { FtrProviderContext } from '../../ftr_provider_context';

export default function ({ getService, getPageObjects }: FtrProviderContext) {
const { common, dashboard, settings, timePicker, visChart } = getPageObjects([
'common',
'dashboard',
'settings',
'timePicker',
'visChart',
]);
const esArchiver = getService('esArchiver');
const kibanaServer = getService('kibanaServer');
const testSubjects = getService('testSubjects');
const browser = getService('browser');
const fieldName = 'clientip';

const clickFieldAndCheckUrl = async (fieldLink: WebElementWrapper) => {
const fieldValue = await fieldLink.getVisibleText();
await fieldLink.click();
const windowHandlers = await browser.getAllWindowHandles();
expect(windowHandlers.length).to.equal(2);
await browser.switchToWindow(windowHandlers[1]);
const currentUrl = await browser.getCurrentUrl();
const fieldUrl = common.getHostPort() + '/app/' + fieldValue;
expect(currentUrl).to.equal(fieldUrl);
};

describe('Changing field formatter to Url', () => {
before(async function () {
await esArchiver.load('dashboard/current/kibana');
await kibanaServer.uiSettings.replace({
defaultIndex: '0bf35f60-3dc9-11e8-8660-4d65aa086b3c',
});
await common.navigateToApp('settings');
await settings.clickKibanaIndexPatterns();
await settings.clickIndexPatternLogstash();
await settings.filterField(fieldName);
await settings.openControlsByName(fieldName);
await settings.setFieldFormat('url');
await settings.controlChangeSave();
});

it('applied on dashboard', async () => {
await common.navigateToApp('dashboard');
await dashboard.loadSavedDashboard('dashboard with everything');
await dashboard.waitForRenderComplete();
const fieldLink = await visChart.getFieldLinkInVisTable(`${fieldName}: Descending`, 1);
await clickFieldAndCheckUrl(fieldLink);
});

it('applied on discover', async () => {
await common.navigateToApp('discover');
await timePicker.setAbsoluteRange(
'Sep 19, 2017 @ 06:31:44.000',
'Sep 23, 2018 @ 18:31:44.000'
);
await testSubjects.click('docTableExpandToggleColumn');
const fieldLink = await testSubjects.find(`tableDocViewRow-${fieldName}-value`);
await clickFieldAndCheckUrl(fieldLink);
});

afterEach(async function () {
const windowHandlers = await browser.getAllWindowHandles();
if (windowHandlers.length > 1) {
await browser.closeCurrentWindow();
await browser.switchToWindow(windowHandlers[0]);
}
});
});
}
14 changes: 14 additions & 0 deletions test/functional/page_objects/visualize_chart_page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,20 @@ export function VisualizeChartPageProvider({ getService, getPageObjects }: FtrPr
return element.getVisibleText();
}

public async getFieldLinkInVisTable(fieldName: string, rowIndex: number = 1) {
const tableVis = await testSubjects.find('tableVis');
const $ = await tableVis.parseDomContent();
const headers = $('span[ng-bind="::col.title"]')
.toArray()
.map((header: any) => $(header).text());
const fieldColumnIndex = headers.indexOf(fieldName);
return await find.byCssSelector(
`[data-test-subj="paginated-table-body"] tr:nth-of-type(${rowIndex}) td:nth-of-type(${
fieldColumnIndex + 1
}) a`
);
}

/**
* If you are writing new tests, you should rather look into getTableVisContent method instead.
* @deprecated Use getTableVisContent instead.
Expand Down
37 changes: 37 additions & 0 deletions x-pack/plugins/actions/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ Table of Contents
- [`params`](#params-7)
- [`subActionParams (pushToService)`](#subactionparams-pushtoservice-1)
- [Command Line Utility](#command-line-utility)
- [Developing New Action Types](#developing-new-action-types)

## Terminology

Expand Down Expand Up @@ -606,3 +607,39 @@ $ kbn-action create .slack "post to slack" '{"webhookUrl": "https://hooks.slack.
"version": "WzMsMV0="
}
```

# Developing New Action Types

When creating a new action type, your plugin will eventually call `server.plugins.actions.setup.registerType()` to register the type with the actions plugin, but there are some additional things to think about about and implement.

Consider working with the alerting team on early structure /design feedback of new actions, especially as the APIs and infrastructure are still under development.

## licensing

Currently actions are licensed as "basic" if the action only interacts with the stack, eg the server log and es index actions. Other actions are at least "gold" level.

## plugin location

Currently actions that are licensed as "basic" **MUST** be implemented in the actions plugin, other actions can be implemented in any other plugin that pre-reqs the actions plugin. If the new action is generic across the stack, it probably belongs in the actions plugin, but if your action is very specific to a plugin/solution, it might be easiest to implement it in the plugin/solution. Keep in mind that if Kibana is run without the plugin being enabled, any actions defined in that plugin will not run, nor will those actions be available via APIs or UI.

Actions that take URLs or hostnames should check that those values are whitelisted. The whitelisting utilities are currently internal to the actions plugin, and so such actions will need to be implemented in the actions plugin. Longer-term, we will expose these utilities so they can be used by alerts implemented in other plugins; see [issue #64659](https://github.com/elastic/kibana/issues/64659).

## documentation

You should also create some asciidoc for the new action type. An entry should be made in the action type index - [`docs/user/alerting/action-types.asciidoc`](../../../docs/user/alerting/action-types.asciidoc) which points to a new document for the action type that should be in the directory [`docs/user/alerting/action-types`](../../../docs/user/alerting/action-types).

## tests

The action type should have both jest tests and functional tests. For functional tests, if your action interacts with a 3rd party service via HTTP, you may be able to create a simulator for your service, to test with. See the existing functional test servers in the directory [`x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators/server`](../../test/alerting_api_integration/common/fixtures/plugins/actions_simulators/server)

## action type config and secrets

Action types must define `config` and `secrets` which are used to create connectors. This data should be described with `@kbn/config-schema` object schemas, and you **MUST NOT** use `schema.maybe()` to define properties.

This is due to the fact that the structures are persisted in saved objects, which performs partial updates on the persisted data. If a property value is already persisted, but an update either doesn't include the property, or sets it to `undefined`, the persisted value will not be changed. Beyond this being a semantic error in general, it also ends up invalidating the encryption used to save secrets, and will render the secrets will not be able to be unencrypted later.

Instead of `schema.maybe()`, use `schema.nullable()`, which is the same as `schema.maybe()` except that when passed an `undefined` value, the object returned from the validation will be set to `null`. The resulting type will be `property-type | null`, whereas with `schema.maybe()` it would be `property-type | undefined`.

## user interface

In order to make this action usable in the Kibana UI, you will need to provide all the UI editing aspects of the action. The existing action type user interfaces are defined in [`x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types`](../triggers_actions_ui/public/application/components/builtin_action_types). For more information, see the [UI documentation](../triggers_actions_ui/README.md#create-and-register-new-action-type-ui).
6 changes: 4 additions & 2 deletions x-pack/plugins/apm/common/agent_name.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,12 @@ export function isAgentName(agentName: string): agentName is AgentName {
return AGENT_NAMES.includes(agentName as AgentName);
}

export const RUM_AGENTS = ['js-base', 'rum-js'];

export function isRumAgentName(
agentName: string | undefined
agentName?: string
): agentName is 'js-base' | 'rum-js' {
return agentName === 'js-base' || agentName === 'rum-js';
return RUM_AGENTS.includes(agentName!);
}

export function isJavaAgentName(
Expand Down
7 changes: 1 addition & 6 deletions x-pack/plugins/apm/public/components/app/Home/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ import { ServiceOverview } from '../ServiceOverview';
import { TraceOverview } from '../TraceOverview';
import { RumOverview } from '../RumDashboard';
import { RumOverviewLink } from '../../shared/Links/apm/RumOverviewLink';
import { I18LABELS } from '../RumDashboard/translations';

function getHomeTabs({
serviceMapEnabled = true,
Expand Down Expand Up @@ -109,11 +108,7 @@ export function Home({ tab }: Props) {
<EuiFlexGroup alignItems="center">
<EuiFlexItem grow={false}>
<EuiTitle size="l">
<h1>
{selectedTab.name === 'rum-overview'
? I18LABELS.endUserExperience
: 'APM'}
</h1>
<h1>APM</h1>
</EuiTitle>
</EuiFlexItem>
<EuiFlexItem grow={false}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@ const ClFlexGroup = styled(EuiFlexGroup)`
export function ClientMetrics() {
const { urlParams, uiFilters } = useUrlParams();

const { start, end } = urlParams;
const { start, end, serviceName } = urlParams;

const { data, status } = useFetcher(
(callApmApi) => {
if (start && end) {
if (start && end && serviceName) {
return callApmApi({
pathname: '/api/apm/rum/client-metrics',
params: {
Expand All @@ -35,7 +35,7 @@ export function ClientMetrics() {
});
}
},
[start, end, uiFilters]
[start, end, serviceName, uiFilters]
);

const STAT_STYLE = { width: '240px' };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export interface PercentileRange {
export const PageLoadDistribution = () => {
const { urlParams, uiFilters } = useUrlParams();

const { start, end } = urlParams;
const { start, end, serviceName } = urlParams;

const [percentileRange, setPercentileRange] = useState<PercentileRange>({
min: null,
Expand All @@ -38,7 +38,7 @@ export const PageLoadDistribution = () => {

const { data, status } = useFetcher(
(callApmApi) => {
if (start && end) {
if (start && end && serviceName) {
return callApmApi({
pathname: '/api/apm/rum-client/page-load-distribution',
params: {
Expand All @@ -57,7 +57,14 @@ export const PageLoadDistribution = () => {
});
}
},
[end, start, uiFilters, percentileRange.min, percentileRange.max]
[
end,
start,
serviceName,
uiFilters,
percentileRange.min,
percentileRange.max,
]
);

const onPercentileChange = (min: number, max: number) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@ interface Props {
export const useBreakdowns = ({ percentileRange, field, value }: Props) => {
const { urlParams, uiFilters } = useUrlParams();

const { start, end } = urlParams;
const { start, end, serviceName } = urlParams;

const { min: minP, max: maxP } = percentileRange ?? {};

return useFetcher(
(callApmApi) => {
if (start && end && field && value) {
if (start && end && serviceName && field && value) {
return callApmApi({
pathname: '/api/apm/rum-client/page-load-distribution/breakdown',
params: {
Expand All @@ -43,6 +43,6 @@ export const useBreakdowns = ({ percentileRange, field, value }: Props) => {
});
}
},
[end, start, uiFilters, field, value, minP, maxP]
[end, start, serviceName, uiFilters, field, value, minP, maxP]
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,13 @@ import { BreakdownItem } from '../../../../../typings/ui_filters';
export const PageViewsTrend = () => {
const { urlParams, uiFilters } = useUrlParams();

const { start, end } = urlParams;
const { start, end, serviceName } = urlParams;

const [breakdowns, setBreakdowns] = useState<BreakdownItem[]>([]);

const { data, status } = useFetcher(
(callApmApi) => {
if (start && end) {
if (start && end && serviceName) {
return callApmApi({
pathname: '/api/apm/rum-client/page-view-trends',
params: {
Expand All @@ -40,7 +40,7 @@ export const PageViewsTrend = () => {
});
}
},
[end, start, uiFilters, breakdowns]
[end, start, serviceName, uiFilters, breakdowns]
);

const onBreakdownChange = (values: BreakdownItem[]) => {
Expand Down
Loading

0 comments on commit 4a5eda0

Please sign in to comment.