Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Beats/initial ui #20994

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* 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 React from 'react';
import styled from 'styled-components';

import { EuiPage, EuiPageBody, EuiPageContent, EuiPageContentBody, EuiTitle } from '@elastic/eui';

interface PrimaryLayoutProps {
title: string;
actionSection: React.ReactNode;
}

const HeaderContainer = styled.div`
display: flex;
justify-content: space-between;
align-items: flex-start;
padding: 24px 24px 0;
margin-bottom: 16px;
`;

export const PrimaryLayout: React.SFC<PrimaryLayoutProps> = ({
actionSection,
title,
children,
}) => (
<EuiPage>
<EuiPageBody>
<EuiPageContent>
<HeaderContainer>
<EuiTitle>
<h1>{title}</h1>
</EuiTitle>
{actionSection}
</HeaderContainer>
<EuiPageContentBody>{children}</EuiPageContentBody>
</EuiPageContent>
</EuiPageBody>
</EuiPage>
);
10 changes: 6 additions & 4 deletions x-pack/plugins/beats_management/public/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,19 @@

import React from 'react';
import { BASE_PATH } from '../common/constants';
import { compose } from './lib/compose/kibana';
import { compose } from './lib/compose/memory';
import { FrontendLibs } from './lib/lib';

// import * as euiVars from '@elastic/eui/dist/eui_theme_k6_light.json';
// import { ThemeProvider } from 'styled-components';
import { PageRouter } from './routes';
import { PageRouter } from './router';

// TODO use theme provided from parentApp when kibana supports it
import '@elastic/eui/dist/eui_theme_light.css';

function startApp(libs: any) {
function startApp(libs: FrontendLibs) {
libs.framework.registerManagementSection('beats', 'Beats Management', BASE_PATH);
libs.framework.render(<PageRouter />);
libs.framework.render(<PageRouter libs={libs} />);
}

startApp(compose());
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* 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 { CMBeat } from '../../../../common/domain_types';

export interface CMBeatsAdapter {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have defined CMPopulatedBeat on my Beats List branch; I assume we will add methods for getting that data here as well?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, the beats adapter will have additional methods for managing this

get(id: string): Promise<CMBeat | null>;
getAll(): Promise<CMBeat[]>;
getWithIds(beatIds: string[]): Promise<CMBeat[]>;
removeTagsFromBeats(removals: BeatsTagAssignment[]): Promise<BeatsTagAssignment[]>;
assignTagsToBeats(assignments: BeatsTagAssignment[]): Promise<BeatsTagAssignment[]>;
}

export interface BeatsTagAssignment {
beatId: string;
tag: string;
idxInRequest?: number;
}

interface BeatsReturnedTagAssignment {
status: number | null;
result?: string;
}

export interface CMAssignmentReturn {
assignments: BeatsReturnedTagAssignment[];
}

export interface BeatsRemovalReturn {
removals: BeatsReturnedTagAssignment[];
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*
* 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 { omit } from 'lodash';

import { CMBeat } from '../../../../common/domain_types';
import { BeatsTagAssignment, CMBeatsAdapter } from './adapter_types';

export class MemoryBeatsAdapter implements CMBeatsAdapter {
private beatsDB: CMBeat[];

constructor(beatsDB: CMBeat[]) {
this.beatsDB = beatsDB;
}

public async get(id: string) {
return this.beatsDB.find(beat => beat.id === id) || null;
}

public async getWithIds(beatIds: string[]) {
return this.beatsDB.filter(beat => beatIds.includes(beat.id));
}

public async getAll() {
return this.beatsDB.map<CMBeat>((beat: any) => omit(beat, ['access_token']));
}

public async removeTagsFromBeats(removals: BeatsTagAssignment[]): Promise<BeatsTagAssignment[]> {
const beatIds = removals.map(r => r.beatId);

const response = this.beatsDB.filter(beat => beatIds.includes(beat.id)).map(beat => {
const tagData = removals.find(r => r.beatId === beat.id);
if (tagData) {
if (beat.tags) {
beat.tags = beat.tags.filter(tag => tag !== tagData.tag);
}
}
return beat;
});

return response.map<any>((item: CMBeat, resultIdx: number) => ({
idxInRequest: removals[resultIdx].idxInRequest,
result: 'updated',
status: 200,
}));
}

public async assignTagsToBeats(assignments: BeatsTagAssignment[]): Promise<BeatsTagAssignment[]> {
const beatIds = assignments.map(r => r.beatId);

this.beatsDB.filter(beat => beatIds.includes(beat.id)).map(beat => {
// get tags that need to be assigned to this beat
const tags = assignments
.filter(a => a.beatId === beat.id)
.map((t: BeatsTagAssignment) => t.tag);

if (tags.length > 0) {
if (!beat.tags) {
beat.tags = [];
}
const nonExistingTags = tags.filter((t: string) => beat.tags && !beat.tags.includes(t));

if (nonExistingTags.length > 0) {
beat.tags = beat.tags.concat(nonExistingTags);
}
}
return beat;
});

return assignments.map<any>((item: BeatsTagAssignment, resultIdx: number) => ({
idxInRequest: assignments[resultIdx].idxInRequest,
result: 'updated',
status: 200,
}));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,9 @@ export class KibanaFrameworkAdapter implements FrameworkAdapter {

private register = (adapterModule: IModule) => {
const adapter = this;
this.routes.when(`/management/beats_management/?`, {
template: '<beats-cm><div id="beatsReactRoot" style="flex-grow: 1;"></div></beats-cm>',
this.routes.when(`/management/beats_management/:view?/:id?/:other?/:other2?`, {
template:
'<beats-cm><div id="beatsReactRoot" style="flex-grow: 1; height: 100vh; background: #f5f5f5"></div></beats-cm>',
controllerAs: 'beatsManagement',
// tslint:disable-next-line: max-classes-per-file
controller: class BeatsManagementController {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/*
* 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 { BeatTag } from '../../../../common/domain_types';

export interface CMTagsAdapter {
getTagsWithIds(tagIds: string[]): Promise<BeatTag[]>;
upsertTag(tag: BeatTag): Promise<{}>;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a general TypeScript question, what's the difference between returning Promise<{}> and something like Promise<any>?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Promise<{}> is saying its returning an object with unknown keys, any could be anything. It's an only slightly more precise placeholder for now

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* 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 { BeatTag } from '../../../../common/domain_types';
import { CMTagsAdapter } from './adapter_types';

export class MemoryTagsAdapter implements CMTagsAdapter {
private tagsDB: BeatTag[] = [];

constructor(tagsDB: BeatTag[]) {
this.tagsDB = tagsDB;
}

public async getTagsWithIds(tagIds: string[]) {
return this.tagsDB.filter(tag => tagIds.includes(tag.id));
}

public async upsertTag(tag: BeatTag) {
const existingTagIndex = this.tagsDB.findIndex(t => t.id === tag.id);
if (existingTagIndex !== -1) {
this.tagsDB[existingTagIndex] = tag;
} else {
this.tagsDB.push(tag);
}
return tag;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/*
* 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.
*/
export interface TokenEnrollmentData {
token: string | null;
expires_on: string;
}

export interface CMTokensAdapter {
createEnrollmentToken(): Promise<TokenEnrollmentData>;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/*
* 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 { CMTokensAdapter, TokenEnrollmentData } from './adapter_types';

export class MemoryTokensAdapter implements CMTokensAdapter {
public async createEnrollmentToken(): Promise<TokenEnrollmentData> {
return {
token: '2jnwkrhkwuehriauhweair',
expires_on: new Date().toJSON(),
};
}
}
14 changes: 13 additions & 1 deletion x-pack/plugins/beats_management/public/lib/compose/kibana.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,30 @@ import { uiModules } from 'ui/modules';
// @ts-ignore: path dynamic for kibana
import routes from 'ui/routes';
// @ts-ignore: path dynamic for kibana
import { MemoryBeatsAdapter } from '../adapters/beats/memory_beats_adapter';
import { KibanaFrameworkAdapter } from '../adapters/framework/kibana_framework_adapter';
import { FrontendLibs } from '../lib';
import { MemoryTagsAdapter } from '../adapters/tags/memory_tags_adapter';
import { MemoryTokensAdapter } from '../adapters/tokens/memory_tokens_adapter';
import { FrontendDomainLibs, FrontendLibs } from '../lib';

export function compose(): FrontendLibs {
// const kbnVersion = (window as any).__KBN__.version;
const tags = new MemoryTagsAdapter([]);
const tokens = new MemoryTokensAdapter();
const beats = new MemoryBeatsAdapter([]);

const domainLibs: FrontendDomainLibs = {
tags,
tokens,
beats,
};
const pluginUIModule = uiModules.get('app/beats_management');

const framework = new KibanaFrameworkAdapter(pluginUIModule, management, routes);

const libs: FrontendLibs = {
framework,
...domainLibs,
};
return libs;
}
40 changes: 40 additions & 0 deletions x-pack/plugins/beats_management/public/lib/compose/memory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* 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 'ui/autoload/all';
// @ts-ignore: path dynamic for kibana
import { management } from 'ui/management';
// @ts-ignore: path dynamic for kibana
import { uiModules } from 'ui/modules';
// @ts-ignore: path dynamic for kibana
import routes from 'ui/routes';
// @ts-ignore: path dynamic for kibana
import { MemoryBeatsAdapter } from '../adapters/beats/memory_beats_adapter';
import { KibanaFrameworkAdapter } from '../adapters/framework/kibana_framework_adapter';
import { MemoryTagsAdapter } from '../adapters/tags/memory_tags_adapter';
import { MemoryTokensAdapter } from '../adapters/tokens/memory_tokens_adapter';

import { FrontendDomainLibs, FrontendLibs } from '../lib';

export function compose(): FrontendLibs {
const tags = new MemoryTagsAdapter([]);
const tokens = new MemoryTokensAdapter();
const beats = new MemoryBeatsAdapter([]);

const domainLibs: FrontendDomainLibs = {
tags,
tokens,
beats,
};
const pluginUIModule = uiModules.get('app/beats_management');

const framework = new KibanaFrameworkAdapter(pluginUIModule, management, routes);
const libs: FrontendLibs = {
...domainLibs,
framework,
};
return libs;
}
12 changes: 10 additions & 2 deletions x-pack/plugins/beats_management/public/lib/lib.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,18 @@
import { IModule, IScope } from 'angular';
import { AxiosRequestConfig } from 'axios';
import React from 'react';
import { CMBeatsAdapter } from './adapters/beats/adapter_types';
import { CMTagsAdapter } from './adapters/tags/adapter_types';
import { CMTokensAdapter } from './adapters/tokens/adapter_types';

export interface FrontendLibs {
export interface FrontendDomainLibs {
beats: CMBeatsAdapter;
tags: CMTagsAdapter;
tokens: CMTokensAdapter;
}

export interface FrontendLibs extends FrontendDomainLibs {
framework: FrameworkAdapter;
// api: ApiAdapter;
}

export interface FrameworkAdapter {
Expand Down
13 changes: 13 additions & 0 deletions x-pack/plugins/beats_management/public/pages/main/activity.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/*
* 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 React from 'react';

export class ActivityPage extends React.PureComponent {
public render() {
return <div>activity logs view</div>;
}
}
Loading