Skip to content

Commit

Permalink
Merge branch 'master' into feature-fleet-metadata-ecs
Browse files Browse the repository at this point in the history
  • Loading branch information
elasticmachine authored Apr 30, 2020
2 parents 296ddea + 3c56a8e commit 366680b
Show file tree
Hide file tree
Showing 187 changed files with 3,274 additions and 1,089 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->

[Home](./index.md) &gt; [kibana-plugin-core-public](./kibana-plugin-core-public.md) &gt; [AppBase](./kibana-plugin-core-public.appbase.md) &gt; [defaultPath](./kibana-plugin-core-public.appbase.defaultpath.md)

## AppBase.defaultPath property

Allow to define the default path a user should be directed to when navigating to the app. When defined, this value will be used as a default for the `path` option when calling [navigateToApp](./kibana-plugin-core-public.applicationstart.navigatetoapp.md)<!-- -->\`<!-- -->, and will also be appended to the [application navLink](./kibana-plugin-core-public.chromenavlink.md) in the navigation bar.

<b>Signature:</b>

```typescript
defaultPath?: string;
```
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export interface AppBase
| [capabilities](./kibana-plugin-core-public.appbase.capabilities.md) | <code>Partial&lt;Capabilities&gt;</code> | Custom capabilities defined by the app. |
| [category](./kibana-plugin-core-public.appbase.category.md) | <code>AppCategory</code> | The category definition of the product See [AppCategory](./kibana-plugin-core-public.appcategory.md) See DEFAULT\_APP\_CATEGORIES for more reference |
| [chromeless](./kibana-plugin-core-public.appbase.chromeless.md) | <code>boolean</code> | Hide the UI chrome when the application is mounted. Defaults to <code>false</code>. Takes precedence over chrome service visibility settings. |
| [defaultPath](./kibana-plugin-core-public.appbase.defaultpath.md) | <code>string</code> | Allow to define the default path a user should be directed to when navigating to the app. When defined, this value will be used as a default for the <code>path</code> option when calling [navigateToApp](./kibana-plugin-core-public.applicationstart.navigatetoapp.md)<!-- -->\`<!-- -->, and will also be appended to the [application navLink](./kibana-plugin-core-public.chromenavlink.md) in the navigation bar. |
| [euiIconType](./kibana-plugin-core-public.appbase.euiicontype.md) | <code>string</code> | A EUI iconType that will be used for the app's icon. This icon takes precendence over the <code>icon</code> property. |
| [icon](./kibana-plugin-core-public.appbase.icon.md) | <code>string</code> | A URL to an image file used as an icon. Used as a fallback if <code>euiIconType</code> is not provided. |
| [id](./kibana-plugin-core-public.appbase.id.md) | <code>string</code> | The unique identifier of the application |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@ Defines the list of fields that can be updated via an [AppUpdater](./kibana-plug
<b>Signature:</b>

```typescript
export declare type AppUpdatableFields = Pick<AppBase, 'status' | 'navLinkStatus' | 'tooltip'>;
export declare type AppUpdatableFields = Pick<AppBase, 'status' | 'navLinkStatus' | 'tooltip' | 'defaultPath'>;
```
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,5 @@ export interface ChromeNavLink
| [subUrlBase](./kibana-plugin-core-public.chromenavlink.suburlbase.md) | <code>string</code> | A url base that legacy apps can set to match deep URLs to an application. |
| [title](./kibana-plugin-core-public.chromenavlink.title.md) | <code>string</code> | The title of the application. |
| [tooltip](./kibana-plugin-core-public.chromenavlink.tooltip.md) | <code>string</code> | A tooltip shown when hovering over an app link. |
| [url](./kibana-plugin-core-public.chromenavlink.url.md) | <code>string</code> | A url that legacy apps can set to deep link into their applications. |
| [url](./kibana-plugin-core-public.chromenavlink.url.md) | <code>string</code> | The route used to open the [default path](./kibana-plugin-core-public.appbase.defaultpath.md) of an application. If unset, <code>baseUrl</code> will be used instead. |

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

## ChromeNavLink.url property

> Warning: This API is now obsolete.
>
>
A url that legacy apps can set to deep link into their applications.
The route used to open the [default path](./kibana-plugin-core-public.appbase.defaultpath.md) of an application. If unset, `baseUrl` will be used instead.

<b>Signature:</b>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,36 @@ A migration function for a [saved object type](./kibana-plugin-core-server.saved
<b>Signature:</b>

```typescript
export declare type SavedObjectMigrationFn = (doc: SavedObjectUnsanitizedDoc, context: SavedObjectMigrationContext) => SavedObjectUnsanitizedDoc;
export declare type SavedObjectMigrationFn<InputAttributes = unknown, MigratedAttributes = unknown> = (doc: SavedObjectUnsanitizedDoc<InputAttributes>, context: SavedObjectMigrationContext) => SavedObjectUnsanitizedDoc<MigratedAttributes>;
```

## Example


```typescript
const migrateProperty: SavedObjectMigrationFn = (doc, { log }) => {
if(doc.attributes.someProp === null) {
log.warn('Skipping migration');
} else {
doc.attributes.someProp = migrateProperty(doc.attributes.someProp);
}

return doc;
interface TypeV1Attributes {
someKey: string;
obsoleteProperty: number;
}

interface TypeV2Attributes {
someKey: string;
newProperty: string;
}

const migrateToV2: SavedObjectMigrationFn<TypeV1Attributes, TypeV2Attributes> = (doc, { log }) => {
const { obsoleteProperty, ...otherAttributes } = doc.attributes;
// instead of mutating `doc` we make a shallow copy so that we can use separate types for the input
// and output attributes. We don't need to make a deep copy, we just need to ensure that obsolete
// attributes are not present on the returned doc.
return {
...doc,
attributes: {
...otherAttributes,
newProperty: migrate(obsoleteProperty),
},
};
};

```

Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@ Describes Saved Object documents that have passed through the migration framewor
<b>Signature:</b>

```typescript
export declare type SavedObjectSanitizedDoc = SavedObjectDoc & Referencable;
export declare type SavedObjectSanitizedDoc<T = unknown> = SavedObjectDoc<T> & Referencable;
```
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@ Describes Saved Object documents from Kibana &lt; 7.0.0 which don't have a `refe
<b>Signature:</b>

```typescript
export declare type SavedObjectUnsanitizedDoc = SavedObjectDoc & Partial<Referencable>;
export declare type SavedObjectUnsanitizedDoc<T = unknown> = SavedObjectDoc<T> & Partial<Referencable>;
```
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@
<b>Signature:</b>

```typescript
export declare function getTime(indexPattern: IIndexPattern | undefined, timeRange: TimeRange, forceNow?: Date): import("../..").RangeFilter | undefined;
export declare function getTime(indexPattern: IIndexPattern | undefined, timeRange: TimeRange, options?: {
forceNow?: Date;
fieldName?: string;
}): import("../..").RangeFilter | undefined;
```

## Parameters
Expand All @@ -16,7 +19,7 @@ export declare function getTime(indexPattern: IIndexPattern | undefined, timeRan
| --- | --- | --- |
| indexPattern | <code>IIndexPattern &#124; undefined</code> | |
| timeRange | <code>TimeRange</code> | |
| forceNow | <code>Date</code> | |
| options | <code>{</code><br/><code> forceNow?: Date;</code><br/><code> fieldName?: string;</code><br/><code>}</code> | |

<b>Returns:</b>

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->

[Home](./index.md) &gt; [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) &gt; [IIndexPattern](./kibana-plugin-plugins-data-public.iindexpattern.md) &gt; [getTimeField](./kibana-plugin-plugins-data-public.iindexpattern.gettimefield.md)

## IIndexPattern.getTimeField() method

<b>Signature:</b>

```typescript
getTimeField?(): IFieldType | undefined;
```
<b>Returns:</b>
`IFieldType | undefined`
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,9 @@ export interface IIndexPattern
| [title](./kibana-plugin-plugins-data-public.iindexpattern.title.md) | <code>string</code> | |
| [type](./kibana-plugin-plugins-data-public.iindexpattern.type.md) | <code>string</code> | |

## Methods

| Method | Description |
| --- | --- |
| [getTimeField()](./kibana-plugin-plugins-data-public.iindexpattern.gettimefield.md) | |

Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
| [getEsPreference(uiSettings, sessionId)](./kibana-plugin-plugins-data-public.getespreference.md) | |
| [getQueryLog(uiSettings, storage, appName, language)](./kibana-plugin-plugins-data-public.getquerylog.md) | |
| [getSearchErrorType({ message })](./kibana-plugin-plugins-data-public.getsearcherrortype.md) | |
| [getTime(indexPattern, timeRange, forceNow)](./kibana-plugin-plugins-data-public.gettime.md) | |
| [getTime(indexPattern, timeRange, options)](./kibana-plugin-plugins-data-public.gettime.md) | |
| [plugin(initializerContext)](./kibana-plugin-plugins-data-public.plugin.md) | |

## Interfaces
Expand Down
2 changes: 1 addition & 1 deletion src/core/MIGRATION_EXAMPLES.md
Original file line number Diff line number Diff line change
Expand Up @@ -957,7 +957,7 @@ const migration = (doc, log) => {...}
Would be converted to:
```typescript
const migration: SavedObjectMigrationFn = (doc, { log }) => {...}
const migration: SavedObjectMigrationFn<OldAttributes, MigratedAttributes> = (doc, { log }) => {...}
```
### Remarks
Expand Down
87 changes: 85 additions & 2 deletions src/core/public/application/application_service.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ describe('#setup()', () => {
).toThrowErrorMatchingInlineSnapshot(`"Applications cannot be registered after \\"setup\\""`);
});

it('allows to register a statusUpdater for the application', async () => {
it('allows to register an AppUpdater for the application', async () => {
const setup = service.setup(setupDeps);

const pluginId = Symbol('plugin');
Expand Down Expand Up @@ -118,6 +118,7 @@ describe('#setup()', () => {
updater$.next(app => ({
status: AppStatus.inaccessible,
tooltip: 'App inaccessible due to reason',
defaultPath: 'foo/bar',
}));

applications = await applications$.pipe(take(1)).toPromise();
Expand All @@ -128,6 +129,7 @@ describe('#setup()', () => {
legacy: false,
navLinkStatus: AppNavLinkStatus.default,
status: AppStatus.inaccessible,
defaultPath: 'foo/bar',
tooltip: 'App inaccessible due to reason',
})
);
Expand Down Expand Up @@ -209,7 +211,7 @@ describe('#setup()', () => {
});
});

describe('registerAppStatusUpdater', () => {
describe('registerAppUpdater', () => {
it('updates status fields', async () => {
const setup = service.setup(setupDeps);

Expand Down Expand Up @@ -413,6 +415,36 @@ describe('#setup()', () => {
})
);
});

it('allows to update the basePath', async () => {
const setup = service.setup(setupDeps);

const pluginId = Symbol('plugin');
setup.register(pluginId, createApp({ id: 'app1' }));

const updater = new BehaviorSubject<AppUpdater>(app => ({}));
setup.registerAppUpdater(updater);

const start = await service.start(startDeps);
await start.navigateToApp('app1');
expect(MockHistory.push).toHaveBeenCalledWith('/app/app1', undefined);
MockHistory.push.mockClear();

updater.next(app => ({ defaultPath: 'default-path' }));
await start.navigateToApp('app1');
expect(MockHistory.push).toHaveBeenCalledWith('/app/app1/default-path', undefined);
MockHistory.push.mockClear();

updater.next(app => ({ defaultPath: 'another-path' }));
await start.navigateToApp('app1');
expect(MockHistory.push).toHaveBeenCalledWith('/app/app1/another-path', undefined);
MockHistory.push.mockClear();

updater.next(app => ({}));
await start.navigateToApp('app1');
expect(MockHistory.push).toHaveBeenCalledWith('/app/app1', undefined);
MockHistory.push.mockClear();
});
});

it("`registerMountContext` calls context container's registerContext", () => {
Expand Down Expand Up @@ -676,6 +708,57 @@ describe('#start()', () => {
expect(MockHistory.push).toHaveBeenCalledWith('/custom/path#/hash/router/path', undefined);
});

it('preserves trailing slash when path contains a hash', async () => {
const { register } = service.setup(setupDeps);

register(Symbol(), createApp({ id: 'app2', appRoute: '/custom/app-path' }));

const { navigateToApp } = await service.start(startDeps);
await navigateToApp('app2', { path: '#/' });
expect(MockHistory.push).toHaveBeenCalledWith('/custom/app-path#/', undefined);
MockHistory.push.mockClear();

await navigateToApp('app2', { path: '#/foo/bar/' });
expect(MockHistory.push).toHaveBeenCalledWith('/custom/app-path#/foo/bar/', undefined);
MockHistory.push.mockClear();

await navigateToApp('app2', { path: '/path#/' });
expect(MockHistory.push).toHaveBeenCalledWith('/custom/app-path/path#/', undefined);
MockHistory.push.mockClear();

await navigateToApp('app2', { path: '/path#/hash/' });
expect(MockHistory.push).toHaveBeenCalledWith('/custom/app-path/path#/hash/', undefined);
MockHistory.push.mockClear();

await navigateToApp('app2', { path: '/path/' });
expect(MockHistory.push).toHaveBeenCalledWith('/custom/app-path/path', undefined);
MockHistory.push.mockClear();
});

it('appends the defaultPath when the path parameter is not specified', async () => {
const { register } = service.setup(setupDeps);

register(Symbol(), createApp({ id: 'app1', defaultPath: 'default/path' }));
register(
Symbol(),
createApp({ id: 'app2', appRoute: '/custom-app-path', defaultPath: '/my-base' })
);

const { navigateToApp } = await service.start(startDeps);

await navigateToApp('app1', { path: 'defined-path' });
expect(MockHistory.push).toHaveBeenCalledWith('/app/app1/defined-path', undefined);

await navigateToApp('app1', {});
expect(MockHistory.push).toHaveBeenCalledWith('/app/app1/default/path', undefined);

await navigateToApp('app2', { path: 'defined-path' });
expect(MockHistory.push).toHaveBeenCalledWith('/custom-app-path/defined-path', undefined);

await navigateToApp('app2', {});
expect(MockHistory.push).toHaveBeenCalledWith('/custom-app-path/my-base', undefined);
});

it('includes state if specified', async () => {
const { register } = service.setup(setupDeps);

Expand Down
12 changes: 5 additions & 7 deletions src/core/public/application/application_service.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import {
Mounter,
} from './types';
import { getLeaveAction, isConfirmAction } from './application_leave';
import { appendAppPath } from './utils';

interface SetupDeps {
context: ContextSetup;
Expand Down Expand Up @@ -81,13 +82,7 @@ const getAppUrl = (mounters: Map<string, Mounter>, appId: string, path: string =
const appBasePath = mounters.get(appId)?.appRoute
? `/${mounters.get(appId)!.appRoute}`
: `/app/${appId}`;

// Only preppend slash if not a hash or query path
path = path.startsWith('#') || path.startsWith('?') ? path : `/${path}`;

return `${appBasePath}${path}`
.replace(/\/{2,}/g, '/') // Remove duplicate slashes
.replace(/\/$/, ''); // Remove trailing slash
return appendAppPath(appBasePath, path);
};

const allApplicationsFilter = '__ALL__';
Expand Down Expand Up @@ -290,6 +285,9 @@ export class ApplicationService {
},
navigateToApp: async (appId, { path, state }: { path?: string; state?: any } = {}) => {
if (await this.shouldNavigate(overlays)) {
if (path === undefined) {
path = applications$.value.get(appId)?.defaultPath;
}
this.appLeaveHandlers.delete(this.currentAppId$.value!);
this.navigate!(getAppUrl(availableMounters, appId, path), state);
this.currentAppId$.next(appId);
Expand Down
15 changes: 13 additions & 2 deletions src/core/public/application/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,13 @@ export interface AppBase {
*/
navLinkStatus?: AppNavLinkStatus;

/**
* Allow to define the default path a user should be directed to when navigating to the app.
* When defined, this value will be used as a default for the `path` option when calling {@link ApplicationStart.navigateToApp | navigateToApp}`,
* and will also be appended to the {@link ChromeNavLink | application navLink} in the navigation bar.
*/
defaultPath?: string;

/**
* An {@link AppUpdater} observable that can be used to update the application {@link AppUpdatableFields} at runtime.
*
Expand Down Expand Up @@ -187,7 +194,10 @@ export enum AppNavLinkStatus {
* Defines the list of fields that can be updated via an {@link AppUpdater}.
* @public
*/
export type AppUpdatableFields = Pick<AppBase, 'status' | 'navLinkStatus' | 'tooltip'>;
export type AppUpdatableFields = Pick<
AppBase,
'status' | 'navLinkStatus' | 'tooltip' | 'defaultPath'
>;

/**
* Updater for applications.
Expand Down Expand Up @@ -642,7 +652,8 @@ export interface ApplicationStart {
* Navigate to a given app
*
* @param appId
* @param options.path - optional path inside application to deep link to
* @param options.path - optional path inside application to deep link to.
* If undefined, will use {@link AppBase.defaultPath | the app's default path}` as default.
* @param options.state - optional state to forward to the application
*/
navigateToApp(appId: string, options?: { path?: string; state?: any }): Promise<void>;
Expand Down
Loading

0 comments on commit 366680b

Please sign in to comment.