Skip to content

Commit

Permalink
EUIfy Watcher (#35301)
Browse files Browse the repository at this point in the history
  • Loading branch information
cjcenizal authored and alisonelizabeth committed Jun 29, 2019
1 parent 6165e19 commit c2a14e7
Show file tree
Hide file tree
Showing 451 changed files with 10,865 additions and 10,871 deletions.
1 change: 1 addition & 0 deletions x-pack/dev-tools/jest/create_jest_config.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export function createJestConfig({
'^ui/(.*)': `${kibanaDirectory}/src/legacy/ui/public/$1`,
'uiExports/(.*)': `${kibanaDirectory}/src/dev/jest/mocks/file_mocks.js`,
'^src/core/(.*)': `${kibanaDirectory}/src/core/$1`,
'^plugins/watcher/models/(.*)': `${xPackKibanaDirectory}/legacy/plugins/watcher/public/models/$1`,
'^plugins/([^\/.]*)(.*)': `${kibanaDirectory}/src/legacy/core_plugins/$1/public$2`,
'^legacy/plugins/xpack_main/(.*);': `${xPackKibanaDirectory}/legacy/plugins/xpack_main/public/$1`,
'\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$':
Expand Down
121 changes: 12 additions & 109 deletions x-pack/legacy/plugins/watcher/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,32 +4,19 @@ This plugins adopts some conventions in addition to or in place of conventions i

## Folder structure
```
common/
constants/ // constants used across client and server
lib/ // helpers used across client and server
types/ // TS definitions
public/
directives/ (This is the same as *A, but is for directives that are used cross-view)
services/
watch/
index.js (no code here; only `export from watch.js`)
watch.js
notifications/
index.js (no code here; only `export from notifications.js`)
notifications.js
...
views/
edit/
...
list/
directives/ (*A)
my_directive_name/
directives/ (Subcomponents of my_directive_name are defined here, and this follows the same structure as *A)
index.js (no code here; only `export from my_directive_name.js`)
my_directive_name.js
my_directive_name.html
index.js (imports the directives in this folder, i.e.,my_directive_name)
routes/
index.js (no code here; only imports routes.js)
routes.js
index.js
components/ // common React components
constants/ // constants used on the client
lib/ // helpers used on the client
models/ // client models
sections/ // Sections of the app with corresponding React components
watch_edit
watch_list
watch_status
server/
lib/
screenshots/
Expand All @@ -44,90 +31,6 @@ server/
say_hello.js
```

## Data Services

api calls:
- GET /watch/{id}
- PUT /watch/{id}

using the service

```js
import watch from './services/watch'

watch.get(...)
watch.put(...)
```

## Services / Lib
- Shared code that requires state should be made into a service. For example, see `pageService`.
- Shared code that doesn't require state (e.g. a simple helper function) should be made a lib.
For example, see `clamp`.

## Controller classes
- All functions in controller classes should be defined as arrow function constants. This is to ensure the `this` context is consistent, regardless of where it is being called.

GOOD
```
controller: class WatchListController {
onQueryChanged = (query) => {...};
}
```

BAD
```
controller: class WatchListController {
onQueryChanged(query) {...};
}
```

```
controller: class WatchListController {
constructor() {
this.onQueryChanged = (query) => {...};
}
}
```

- Constructors should be used to initialize state and define $scope.$watch(es)

GOOD
```
controllerAs: 'watchList',
bindToController: true,
scope: { foo: '=' },
controller: class WatchListController {
constructor() {
this.foo = this.foo || 'default';
$scope.$watch('watchList.foo', () => {
console.log('foo changed, fool');
});
}
}
```
## Event handlers

Event handler functions should be named with the following pattern:

> `on<Verb>`, in present tense
In case there is ambiguity about _what_ the verb is acting upon a noun should be included like so:

> `on<Noun><Verb>`, in present tense
GOOD
```
onDelete
onWatchDelete
```

BAD
```
onDeleted
onWatchDeleted
```

## Data Flow

We have a layered architecture in the Watcher UI codebase, with each layer performing a specific function to the data as it flows through it.
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 { getWatch } from '../../../test/fixtures';

export const WATCH_ID = 'my-test-watch';

export const WATCH = { watch: getWatch({ id: WATCH_ID }) };
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
/*
* 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 sinon, { SinonFakeServer } from 'sinon';
import { ROUTES } from '../../../common/constants';

const { API_ROOT } = ROUTES;

type HttpResponse = Record<string, any> | any[];

const mockResponse = (defaultResponse: HttpResponse, response: HttpResponse) => [
200,
{ 'Content-Type': 'application/json' },
JSON.stringify({ ...defaultResponse, ...response }),
];

// Register helpers to mock HTTP Requests
const registerHttpRequestMockHelpers = (server: SinonFakeServer) => {
const setLoadWatchesResponse = (response: HttpResponse = {}) => {
const defaultResponse = { watches: [] };

server.respondWith('GET', `${API_ROOT}/watches`, mockResponse(defaultResponse, response));
};

const setLoadWatchResponse = (response: HttpResponse = {}) => {
const defaultResponse = { watch: {} };
server.respondWith('GET', `${API_ROOT}/watch/:id`, mockResponse(defaultResponse, response));
};

const setLoadWatchHistoryResponse = (response: HttpResponse = {}) => {
const defaultResponse = { watchHistoryItems: [] };
server.respondWith(
'GET',
`${API_ROOT}/watch/:id/history?startTime=*`,
mockResponse(defaultResponse, response)
);
};

const setLoadWatchHistoryItemResponse = (response: HttpResponse = {}) => {
const defaultResponse = { watchHistoryItem: {} };
server.respondWith('GET', `${API_ROOT}/history/:id`, mockResponse(defaultResponse, response));
};

const setDeleteWatchResponse = (response?: HttpResponse, error?: any) => {
const status = error ? error.status || 400 : 200;
const body = error ? JSON.stringify(error.body) : JSON.stringify(response);

server.respondWith('POST', `${API_ROOT}/watches/delete`, [
status,
{ 'Content-Type': 'application/json' },
body,
]);
};

const setSaveWatchResponse = (id: string, response?: HttpResponse, error?: any) => {
const status = error ? error.status || 400 : 200;
const body = error ? JSON.stringify(error.body) : JSON.stringify(response);

server.respondWith('PUT', `${API_ROOT}/watch/${id}`, [
status,
{ 'Content-Type': 'application/json' },
body,
]);
};

const setLoadExecutionResultResponse = (response: HttpResponse = {}) => {
const defaultResponse = { watchHistoryItem: {} };
server.respondWith('PUT', `${API_ROOT}/watch/execute`, mockResponse(defaultResponse, response));
};

const setLoadMatchingIndicesResponse = (response: HttpResponse = {}) => {
const defaultResponse = { indices: [] };
server.respondWith('POST', `${API_ROOT}/indices`, mockResponse(defaultResponse, response));
};

const setLoadEsFieldsResponse = (response: HttpResponse = {}) => {
const defaultResponse = { fields: [] };
server.respondWith('POST', `${API_ROOT}/fields`, mockResponse(defaultResponse, response));
};

const setLoadSettingsResponse = (response: HttpResponse = {}) => {
const defaultResponse = { action_types: {} };
server.respondWith('GET', `${API_ROOT}/settings`, mockResponse(defaultResponse, response));
};

const setLoadWatchVisualizeResponse = (response: HttpResponse = {}) => {
const defaultResponse = { visualizeData: {} };
server.respondWith(
'POST',
`${API_ROOT}/watch/visualize`,
mockResponse(defaultResponse, response)
);
};

const setDeactivateWatchResponse = (response: HttpResponse = {}) => {
const defaultResponse = { watchStatus: {} };
server.respondWith(
'PUT',
`${API_ROOT}/watch/:id/deactivate`,
mockResponse(defaultResponse, response)
);
};

const setActivateWatchResponse = (response: HttpResponse = {}) => {
const defaultResponse = { watchStatus: {} };
server.respondWith(
'PUT',
`${API_ROOT}/watch/:id/activate`,
mockResponse(defaultResponse, response)
);
};

const setAcknowledgeWatchResponse = (response: HttpResponse = {}) => {
const defaultResponse = { watchStatus: {} };
server.respondWith(
'PUT',
`${API_ROOT}/watch/:id/action/:actionId/acknowledge`,
mockResponse(defaultResponse, response)
);
};

return {
setLoadWatchesResponse,
setLoadWatchResponse,
setLoadWatchHistoryResponse,
setLoadWatchHistoryItemResponse,
setDeleteWatchResponse,
setSaveWatchResponse,
setLoadExecutionResultResponse,
setLoadMatchingIndicesResponse,
setLoadEsFieldsResponse,
setLoadSettingsResponse,
setLoadWatchVisualizeResponse,
setDeactivateWatchResponse,
setActivateWatchResponse,
setAcknowledgeWatchResponse,
};
};

export const init = () => {
const server = sinon.fakeServer.create();
server.respondImmediately = true;

// Define default response for unhandled requests.
// We make requests to APIs which don't impact the component under test, e.g. UI metric telemetry,
// and we can mock them all with a 200 instead of mocking each one individually.
server.respondWith([200, {}, 'DefaultResponse']);

const httpRequestsMockHelpers = registerHttpRequestMockHelpers(server);

return {
server,
httpRequestsMockHelpers,
};
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* 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 { setup as watchListSetup } from './watch_list.helpers';
import { setup as watchStatusSetup } from './watch_status.helpers';
import { setup as watchCreateJsonSetup } from './watch_create_json.helpers';
import { setup as watchCreateThresholdSetup } from './watch_create_threshold.helpers';
import { setup as watchEditSetup } from './watch_edit.helpers';

export { nextTick, getRandomString, findTestSubject, TestBed } from '../../../../../../test_utils';

export { setupEnvironment } from './setup_environment';

export const pageHelpers = {
watchList: { setup: watchListSetup },
watchStatus: { setup: watchStatusSetup },
watchCreateJson: { setup: watchCreateJsonSetup },
watchCreateThreshold: { setup: watchCreateThresholdSetup },
watchEdit: { setup: watchEditSetup },
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* 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 axios from 'axios';
import axiosXhrAdapter from 'axios/lib/adapters/xhr';
import { init as initHttpRequests } from './http_requests';
import { setHttpClient, setSavedObjectsClient } from '../../../public/lib/api';

const mockHttpClient = axios.create({ adapter: axiosXhrAdapter });

const mockSavedObjectsClient = () => {
return {
find: (_params?: any) => {},
};
};

export const setupEnvironment = () => {
const { server, httpRequestsMockHelpers } = initHttpRequests();

// @ts-ignore
setHttpClient(mockHttpClient);

setSavedObjectsClient(mockSavedObjectsClient());

return {
server,
httpRequestsMockHelpers,
};
};
Loading

0 comments on commit c2a14e7

Please sign in to comment.