From fa4cd2c152518e1b01cf87e9b15e42ab813421c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20Haro?= Date: Wed, 13 May 2020 18:45:02 +0100 Subject: [PATCH] [7.x] Remove Newsfeed leftovers from the Legacy codebase (#66084) (#66427) --- src/legacy/core_plugins/newsfeed/index.ts | 71 ---------- src/legacy/core_plugins/newsfeed/package.json | 4 - .../core_plugins/newsfeed/public/index.scss | 3 - .../components/header_alert/_index.scss | 27 ---- .../components/header_alert/header_alert.tsx | 76 ----------- .../plugins/newsfeed/common/constants.ts | 19 +-- src/plugins/newsfeed/kibana.json | 2 +- .../public/components/flyout_list.tsx | 4 +- .../components/newsfeed_header_nav_button.tsx | 2 +- src/plugins/newsfeed/public/lib/api.test.ts | 38 +++--- src/plugins/newsfeed/public/lib/api.ts | 14 +- src/plugins/newsfeed/public/plugin.tsx | 25 ++-- src/plugins/newsfeed/{ => public}/types.ts | 18 ++- src/plugins/newsfeed/server/config.ts | 44 ++++++ src/plugins/newsfeed/server/index.ts | 36 +++++ .../{constants.ts => server/plugin.ts} | 12 +- .../fixtures/plugins/newsfeed/kibana.json | 6 + .../plugins/newsfeed/newsfeed_simulation.ts | 126 ------------------ .../fixtures/plugins/newsfeed/package.json | 7 - .../fixtures/plugins/newsfeed/server/index.ts | 10 +- .../plugins/newsfeed/server/plugin.ts | 97 ++++++++++++++ 21 files changed, 255 insertions(+), 386 deletions(-) delete mode 100644 src/legacy/core_plugins/newsfeed/index.ts delete mode 100644 src/legacy/core_plugins/newsfeed/package.json delete mode 100644 src/legacy/core_plugins/newsfeed/public/index.scss delete mode 100644 src/legacy/core_plugins/newsfeed/public/np_ready/components/header_alert/_index.scss delete mode 100644 src/legacy/core_plugins/newsfeed/public/np_ready/components/header_alert/header_alert.tsx rename test/common/fixtures/plugins/newsfeed/index.ts => src/plugins/newsfeed/common/constants.ts (65%) rename src/plugins/newsfeed/{ => public}/types.ts (80%) create mode 100644 src/plugins/newsfeed/server/config.ts create mode 100644 src/plugins/newsfeed/server/index.ts rename src/plugins/newsfeed/{constants.ts => server/plugin.ts} (81%) create mode 100644 test/common/fixtures/plugins/newsfeed/kibana.json delete mode 100644 test/common/fixtures/plugins/newsfeed/newsfeed_simulation.ts delete mode 100644 test/common/fixtures/plugins/newsfeed/package.json rename src/legacy/core_plugins/newsfeed/constants.ts => test/common/fixtures/plugins/newsfeed/server/index.ts (76%) create mode 100644 test/common/fixtures/plugins/newsfeed/server/plugin.ts diff --git a/src/legacy/core_plugins/newsfeed/index.ts b/src/legacy/core_plugins/newsfeed/index.ts deleted file mode 100644 index cf8852be09a1..000000000000 --- a/src/legacy/core_plugins/newsfeed/index.ts +++ /dev/null @@ -1,71 +0,0 @@ -/* - * 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 { resolve } from 'path'; -import { LegacyPluginApi, LegacyPluginSpec, ArrayOrItem } from 'src/legacy/plugin_discovery/types'; -import { Legacy } from 'kibana'; -import { NewsfeedPluginInjectedConfig } from '../../../plugins/newsfeed/types'; -import { - PLUGIN_ID, - DEFAULT_SERVICE_URLROOT, - DEV_SERVICE_URLROOT, - DEFAULT_SERVICE_PATH, -} from './constants'; - -// eslint-disable-next-line import/no-default-export -export default function(kibana: LegacyPluginApi): ArrayOrItem { - const pluginSpec: Legacy.PluginSpecOptions = { - id: PLUGIN_ID, - config(Joi: any) { - // NewsfeedPluginInjectedConfig in Joi form - return Joi.object({ - enabled: Joi.boolean().default(true), - service: Joi.object({ - pathTemplate: Joi.string().default(DEFAULT_SERVICE_PATH), - urlRoot: Joi.when('$prod', { - is: true, - then: Joi.string().default(DEFAULT_SERVICE_URLROOT), - otherwise: Joi.string().default(DEV_SERVICE_URLROOT), - }), - }).default(), - defaultLanguage: Joi.string().default('en'), - mainInterval: Joi.number().default(120 * 1000), // (2min) How often to retry failed fetches, and/or check if newsfeed items need to be refreshed from remote - fetchInterval: Joi.number().default(86400 * 1000), // (1day) How often to fetch remote and reset the last fetched time - }).default(); - }, - uiExports: { - styleSheetPaths: resolve(__dirname, 'public/index.scss'), - injectDefaultVars(server): NewsfeedPluginInjectedConfig { - const config = server.config(); - return { - newsfeed: { - service: { - pathTemplate: config.get('newsfeed.service.pathTemplate') as string, - urlRoot: config.get('newsfeed.service.urlRoot') as string, - }, - defaultLanguage: config.get('newsfeed.defaultLanguage') as string, - mainInterval: config.get('newsfeed.mainInterval') as number, - fetchInterval: config.get('newsfeed.fetchInterval') as number, - }, - }; - }, - }, - }; - return new kibana.Plugin(pluginSpec); -} diff --git a/src/legacy/core_plugins/newsfeed/package.json b/src/legacy/core_plugins/newsfeed/package.json deleted file mode 100644 index d4d753f32b0f..000000000000 --- a/src/legacy/core_plugins/newsfeed/package.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "newsfeed", - "version": "kibana" -} diff --git a/src/legacy/core_plugins/newsfeed/public/index.scss b/src/legacy/core_plugins/newsfeed/public/index.scss deleted file mode 100644 index a77132379041..000000000000 --- a/src/legacy/core_plugins/newsfeed/public/index.scss +++ /dev/null @@ -1,3 +0,0 @@ -@import 'src/legacy/ui/public/styles/styling_constants'; - -@import './np_ready/components/header_alert/_index'; diff --git a/src/legacy/core_plugins/newsfeed/public/np_ready/components/header_alert/_index.scss b/src/legacy/core_plugins/newsfeed/public/np_ready/components/header_alert/_index.scss deleted file mode 100644 index e25dbd25daaf..000000000000 --- a/src/legacy/core_plugins/newsfeed/public/np_ready/components/header_alert/_index.scss +++ /dev/null @@ -1,27 +0,0 @@ -@import '@elastic/eui/src/components/header/variables'; - -.kbnNews__flyout { - top: $euiHeaderChildSize + 1px; - height: calc(100% - #{$euiHeaderChildSize}); -} - -.kbnNewsFeed__headerAlert.euiHeaderAlert { - margin-bottom: $euiSizeL; - padding: 0 $euiSizeS $euiSizeL; - border-bottom: $euiBorderThin; - border-top: none; - - .euiHeaderAlert__title { - @include euiTitle('xs'); - margin-bottom: $euiSizeS; - } - - .euiHeaderAlert__text { - @include euiFontSizeS; - margin-bottom: $euiSize; - } - - .euiHeaderAlert__action { - @include euiFontSizeS; - } -} diff --git a/src/legacy/core_plugins/newsfeed/public/np_ready/components/header_alert/header_alert.tsx b/src/legacy/core_plugins/newsfeed/public/np_ready/components/header_alert/header_alert.tsx deleted file mode 100644 index c3c3e4144fca..000000000000 --- a/src/legacy/core_plugins/newsfeed/public/np_ready/components/header_alert/header_alert.tsx +++ /dev/null @@ -1,76 +0,0 @@ -/* - * 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 React from 'react'; -import PropTypes from 'prop-types'; -import classNames from 'classnames'; - -import { EuiFlexGroup, EuiFlexItem, EuiI18n } from '@elastic/eui'; - -interface IEuiHeaderAlertProps { - action: JSX.Element; - className?: string; - date: string; - text: string; - title: string; - badge?: JSX.Element; - rest?: string[]; -} - -export const EuiHeaderAlert = ({ - action, - className, - date, - text, - title, - badge, - ...rest -}: IEuiHeaderAlertProps) => { - const classes = classNames('euiHeaderAlert', 'kbnNewsFeed__headerAlert', className); - - const badgeContent = badge || null; - - return ( - - {(dismiss: any) => ( -
- - -
{date}
-
- {badgeContent} -
- -
{title}
-
{text}
-
{action}
-
- )} -
- ); -}; - -EuiHeaderAlert.propTypes = { - action: PropTypes.node, - className: PropTypes.string, - date: PropTypes.node.isRequired, - text: PropTypes.node, - title: PropTypes.node.isRequired, - badge: PropTypes.node, -}; diff --git a/test/common/fixtures/plugins/newsfeed/index.ts b/src/plugins/newsfeed/common/constants.ts similarity index 65% rename from test/common/fixtures/plugins/newsfeed/index.ts rename to src/plugins/newsfeed/common/constants.ts index beee9bb5c606..6bc95873a342 100644 --- a/test/common/fixtures/plugins/newsfeed/index.ts +++ b/src/plugins/newsfeed/common/constants.ts @@ -17,17 +17,10 @@ * under the License. */ -import Hapi from 'hapi'; -import { initPlugin as initNewsfeed } from './newsfeed_simulation'; +export const NEWSFEED_FALLBACK_LANGUAGE = 'en'; +export const NEWSFEED_LAST_FETCH_STORAGE_KEY = 'newsfeed.lastfetchtime'; +export const NEWSFEED_HASH_SET_STORAGE_KEY = 'newsfeed.hashes'; -const NAME = 'newsfeed-FTS-external-service-simulators'; - -// eslint-disable-next-line import/no-default-export -export default function(kibana: any) { - return new kibana.Plugin({ - name: NAME, - init: (server: Hapi.Server) => { - initNewsfeed(server, `/api/_${NAME}`); - }, - }); -} +export const NEWSFEED_DEFAULT_SERVICE_BASE_URL = 'https://feeds.elastic.co'; +export const NEWSFEED_DEV_SERVICE_BASE_URL = 'https://feeds-staging.elastic.co'; +export const NEWSFEED_DEFAULT_SERVICE_PATH = '/kibana/v{VERSION}.json'; diff --git a/src/plugins/newsfeed/kibana.json b/src/plugins/newsfeed/kibana.json index 9d49b42424a0..b9f37b67f692 100644 --- a/src/plugins/newsfeed/kibana.json +++ b/src/plugins/newsfeed/kibana.json @@ -1,6 +1,6 @@ { "id": "newsfeed", "version": "kibana", - "server": false, + "server": true, "ui": true } diff --git a/src/plugins/newsfeed/public/components/flyout_list.tsx b/src/plugins/newsfeed/public/components/flyout_list.tsx index bd554d7d98b7..6e9444c95010 100644 --- a/src/plugins/newsfeed/public/components/flyout_list.tsx +++ b/src/plugins/newsfeed/public/components/flyout_list.tsx @@ -29,11 +29,11 @@ import { EuiButtonEmpty, EuiText, EuiBadge, + EuiHeaderAlert, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; -import { EuiHeaderAlert } from '../../../../legacy/core_plugins/newsfeed/public/np_ready/components/header_alert/header_alert'; import { NewsfeedContext } from './newsfeed_header_nav_button'; -import { NewsfeedItem } from '../../types'; +import { NewsfeedItem } from '../types'; import { NewsEmptyPrompt } from './empty_news'; import { NewsLoadingPrompt } from './loading_news'; diff --git a/src/plugins/newsfeed/public/components/newsfeed_header_nav_button.tsx b/src/plugins/newsfeed/public/components/newsfeed_header_nav_button.tsx index da042f0fce7b..fd938e907107 100644 --- a/src/plugins/newsfeed/public/components/newsfeed_header_nav_button.tsx +++ b/src/plugins/newsfeed/public/components/newsfeed_header_nav_button.tsx @@ -21,7 +21,7 @@ import React, { useState, Fragment, useEffect } from 'react'; import * as Rx from 'rxjs'; import { EuiHeaderSectionItemButton, EuiIcon, EuiNotificationBadge } from '@elastic/eui'; import { NewsfeedFlyout } from './flyout_list'; -import { FetchResult } from '../../types'; +import { FetchResult } from '../types'; export interface INewsfeedContext { setFlyoutVisible: React.Dispatch>; diff --git a/src/plugins/newsfeed/public/lib/api.test.ts b/src/plugins/newsfeed/public/lib/api.test.ts index 9d64dd26da04..5db578f1bd4e 100644 --- a/src/plugins/newsfeed/public/lib/api.test.ts +++ b/src/plugins/newsfeed/public/lib/api.test.ts @@ -22,8 +22,11 @@ import { interval, race } from 'rxjs'; import sinon, { stub } from 'sinon'; import moment from 'moment'; import { HttpSetup } from 'src/core/public'; -import { NEWSFEED_HASH_SET_STORAGE_KEY, NEWSFEED_LAST_FETCH_STORAGE_KEY } from '../../constants'; -import { ApiItem, NewsfeedItem, NewsfeedPluginInjectedConfig } from '../../types'; +import { + NEWSFEED_HASH_SET_STORAGE_KEY, + NEWSFEED_LAST_FETCH_STORAGE_KEY, +} from '../../common/constants'; +import { ApiItem, NewsfeedItem, NewsfeedPluginBrowserConfig } from '../types'; import { NewsfeedApiDriver, getApi } from './api'; const localStorageGet = sinon.stub(); @@ -458,7 +461,7 @@ describe('getApi', () => { } return Promise.reject('wrong args!'); }; - let configMock: NewsfeedPluginInjectedConfig; + let configMock: NewsfeedPluginBrowserConfig; afterEach(() => { jest.resetAllMocks(); @@ -466,15 +469,12 @@ describe('getApi', () => { beforeEach(() => { configMock = { - newsfeed: { - service: { - urlRoot: 'http://fakenews.co', - pathTemplate: '/kibana-test/v{VERSION}.json', - }, - defaultLanguage: 'en', - mainInterval: 86400000, - fetchInterval: 86400000, + service: { + urlRoot: 'http://fakenews.co', + pathTemplate: '/kibana-test/v{VERSION}.json', }, + mainInterval: moment.duration(86400000), + fetchInterval: moment.duration(86400000), }; httpMock = ({ fetch: mockHttpGet, @@ -483,7 +483,7 @@ describe('getApi', () => { it('creates a result', done => { mockHttpGet.mockImplementationOnce(() => Promise.resolve({ items: [] })); - getApi(httpMock, configMock.newsfeed, '6.8.2').subscribe(result => { + getApi(httpMock, configMock, '6.8.2').subscribe(result => { expect(result).toMatchInlineSnapshot(` Object { "error": null, @@ -528,7 +528,7 @@ describe('getApi', () => { mockHttpGet.mockImplementationOnce(getHttpMockWithItems(mockApiItems)); - getApi(httpMock, configMock.newsfeed, '6.8.2').subscribe(result => { + getApi(httpMock, configMock, '6.8.2').subscribe(result => { expect(result).toMatchInlineSnapshot(` Object { "error": null, @@ -568,7 +568,7 @@ describe('getApi', () => { }, ]; mockHttpGet.mockImplementationOnce(getHttpMockWithItems(mockApiItems)); - getApi(httpMock, configMock.newsfeed, '6.8.2').subscribe(result => { + getApi(httpMock, configMock, '6.8.2').subscribe(result => { expect(result).toMatchInlineSnapshot(` Object { "error": null, @@ -595,7 +595,7 @@ describe('getApi', () => { it('forwards an error', done => { mockHttpGet.mockImplementationOnce((arg1, arg2) => Promise.reject('sorry, try again later!')); - getApi(httpMock, configMock.newsfeed, '6.8.2').subscribe(result => { + getApi(httpMock, configMock, '6.8.2').subscribe(result => { expect(result).toMatchInlineSnapshot(` Object { "error": "sorry, try again later!", @@ -623,14 +623,14 @@ describe('getApi', () => { ]; it("retries until fetch doesn't error", done => { - configMock.newsfeed.mainInterval = 10; // fast retry for testing + configMock.mainInterval = moment.duration(10); // fast retry for testing mockHttpGet .mockImplementationOnce(() => Promise.reject('Sorry, try again later!')) .mockImplementationOnce(() => Promise.reject('Sorry, internal server error!')) .mockImplementationOnce(() => Promise.reject("Sorry, it's too cold to go outside!")) .mockImplementationOnce(getHttpMockWithItems(successItems)); - getApi(httpMock, configMock.newsfeed, '6.8.2') + getApi(httpMock, configMock, '6.8.2') .pipe(take(4), toArray()) .subscribe(result => { expect(result).toMatchInlineSnapshot(` @@ -677,13 +677,13 @@ describe('getApi', () => { }); it("doesn't retry if fetch succeeds", done => { - configMock.newsfeed.mainInterval = 10; // fast retry for testing + configMock.mainInterval = moment.duration(10); // fast retry for testing mockHttpGet.mockImplementation(getHttpMockWithItems(successItems)); const timeout$ = interval(1000); // lets us capture some results after a short time let timesFetched = 0; - const get$ = getApi(httpMock, configMock.newsfeed, '6.8.2').pipe( + const get$ = getApi(httpMock, configMock, '6.8.2').pipe( tap(() => { timesFetched++; }) diff --git a/src/plugins/newsfeed/public/lib/api.ts b/src/plugins/newsfeed/public/lib/api.ts index bfeff4aa3e37..2924f3d34066 100644 --- a/src/plugins/newsfeed/public/lib/api.ts +++ b/src/plugins/newsfeed/public/lib/api.ts @@ -26,10 +26,10 @@ import { NEWSFEED_FALLBACK_LANGUAGE, NEWSFEED_LAST_FETCH_STORAGE_KEY, NEWSFEED_HASH_SET_STORAGE_KEY, -} from '../../constants'; -import { NewsfeedPluginInjectedConfig, ApiItem, NewsfeedItem, FetchResult } from '../../types'; +} from '../../common/constants'; +import { ApiItem, NewsfeedItem, FetchResult, NewsfeedPluginBrowserConfig } from '../types'; -type ApiConfig = NewsfeedPluginInjectedConfig['newsfeed']['service']; +type ApiConfig = NewsfeedPluginBrowserConfig['service']; export class NewsfeedApiDriver { private readonly loadedTime = moment().utc(); // the date is compared to time in UTC format coming from the service @@ -167,14 +167,14 @@ export class NewsfeedApiDriver { */ export function getApi( http: HttpSetup, - config: NewsfeedPluginInjectedConfig['newsfeed'], + config: NewsfeedPluginBrowserConfig, kibanaVersion: string ): Rx.Observable { - const userLanguage = i18n.getLocale() || config.defaultLanguage; - const fetchInterval = config.fetchInterval; + const userLanguage = i18n.getLocale(); + const fetchInterval = config.fetchInterval.asMilliseconds(); const driver = new NewsfeedApiDriver(kibanaVersion, userLanguage, fetchInterval); - return Rx.timer(0, config.mainInterval).pipe( + return Rx.timer(0, config.mainInterval.asMilliseconds()).pipe( filter(() => driver.shouldFetch()), mergeMap(() => driver.fetchNewsfeedItems(http, config.service).pipe( diff --git a/src/plugins/newsfeed/public/plugin.tsx b/src/plugins/newsfeed/public/plugin.tsx index d21cf75a1a65..e61070ab184f 100644 --- a/src/plugins/newsfeed/public/plugin.tsx +++ b/src/plugins/newsfeed/public/plugin.tsx @@ -21,9 +21,10 @@ import * as Rx from 'rxjs'; import { catchError, takeUntil } from 'rxjs/operators'; import ReactDOM from 'react-dom'; import React from 'react'; +import moment from 'moment'; import { I18nProvider } from '@kbn/i18n/react'; import { PluginInitializerContext, CoreSetup, CoreStart, Plugin } from 'src/core/public'; -import { FetchResult, NewsfeedPluginInjectedConfig } from '../types'; +import { NewsfeedPluginBrowserConfig } from './types'; import { NewsfeedNavButton, NewsfeedApiFetchResult } from './components/newsfeed_header_nav_button'; import { getApi } from './lib/api'; @@ -32,10 +33,18 @@ export type Start = object; export class NewsfeedPublicPlugin implements Plugin { private readonly kibanaVersion: string; + private readonly config: NewsfeedPluginBrowserConfig; private readonly stop$ = new Rx.ReplaySubject(1); - constructor(initializerContext: PluginInitializerContext) { + constructor(initializerContext: PluginInitializerContext) { this.kibanaVersion = initializerContext.env.packageInfo.version; + const config = initializerContext.config.get(); + this.config = Object.freeze({ + ...config, + // We need wrap them in moment.duration because exposeToBrowser stringifies it. + mainInterval: moment.duration(config.mainInterval), + fetchInterval: moment.duration(config.fetchInterval), + }); } public setup(core: CoreSetup): Setup { @@ -57,16 +66,8 @@ export class NewsfeedPublicPlugin implements Plugin { } private fetchNewsfeed(core: CoreStart) { - const { http, injectedMetadata } = core; - const config = injectedMetadata.getInjectedVar('newsfeed') as - | NewsfeedPluginInjectedConfig['newsfeed'] - | undefined; - - if (!config) { - // running in new platform, injected metadata not available - return new Rx.Observable(); - } - return getApi(http, config, this.kibanaVersion).pipe( + const { http } = core; + return getApi(http, this.config, this.kibanaVersion).pipe( takeUntil(this.stop$), // stop the interval when stop method is called catchError(() => Rx.of(null)) // do not throw error ); diff --git a/src/plugins/newsfeed/types.ts b/src/plugins/newsfeed/public/types.ts similarity index 80% rename from src/plugins/newsfeed/types.ts rename to src/plugins/newsfeed/public/types.ts index 78485c6ee4f5..a9dab6791d7a 100644 --- a/src/plugins/newsfeed/types.ts +++ b/src/plugins/newsfeed/public/types.ts @@ -17,18 +17,16 @@ * under the License. */ -import { Moment } from 'moment'; +import { Duration, Moment } from 'moment'; -export interface NewsfeedPluginInjectedConfig { - newsfeed: { - service: { - urlRoot: string; - pathTemplate: string; - }; - defaultLanguage: string; - mainInterval: number; // how often to check last updated time - fetchInterval: number; // how often to fetch remote service and set last updated +// Ideally, we may want to obtain the type from the configSchema and exposeToBrowser keys... +export interface NewsfeedPluginBrowserConfig { + service: { + urlRoot: string; + pathTemplate: string; }; + mainInterval: Duration; // how often to check last updated time + fetchInterval: Duration; // how often to fetch remote service and set last updated } export interface ApiItem { diff --git a/src/plugins/newsfeed/server/config.ts b/src/plugins/newsfeed/server/config.ts new file mode 100644 index 000000000000..e42066f4d20e --- /dev/null +++ b/src/plugins/newsfeed/server/config.ts @@ -0,0 +1,44 @@ +/* + * 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 { schema, TypeOf } from '@kbn/config-schema'; +import { + NEWSFEED_DEFAULT_SERVICE_PATH, + NEWSFEED_DEFAULT_SERVICE_BASE_URL, + NEWSFEED_DEV_SERVICE_BASE_URL, + NEWSFEED_FALLBACK_LANGUAGE, +} from '../common/constants'; + +export const configSchema = schema.object({ + enabled: schema.boolean({ defaultValue: true }), + service: schema.object({ + pathTemplate: schema.string({ defaultValue: NEWSFEED_DEFAULT_SERVICE_PATH }), + urlRoot: schema.conditional( + schema.contextRef('prod'), + schema.literal(true), // Point to staging if it's not a production release + schema.string({ defaultValue: NEWSFEED_DEFAULT_SERVICE_BASE_URL }), + schema.string({ defaultValue: NEWSFEED_DEV_SERVICE_BASE_URL }) + ), + }), + defaultLanguage: schema.string({ defaultValue: NEWSFEED_FALLBACK_LANGUAGE }), // TODO: Deprecate since no longer used + mainInterval: schema.duration({ defaultValue: '2m' }), // (2min) How often to retry failed fetches, and/or check if newsfeed items need to be refreshed from remote + fetchInterval: schema.duration({ defaultValue: '1d' }), // (1day) How often to fetch remote and reset the last fetched time +}); + +export type NewsfeedConfigType = TypeOf; diff --git a/src/plugins/newsfeed/server/index.ts b/src/plugins/newsfeed/server/index.ts new file mode 100644 index 000000000000..e62bb95df886 --- /dev/null +++ b/src/plugins/newsfeed/server/index.ts @@ -0,0 +1,36 @@ +/* + * 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 { PluginConfigDescriptor } from 'kibana/server'; +import { NewsfeedPlugin } from './plugin'; +import { configSchema, NewsfeedConfigType } from './config'; + +export const config: PluginConfigDescriptor = { + schema: configSchema, + exposeToBrowser: { + service: true, + mainInterval: true, + fetchInterval: true, + }, + deprecations: ({ unused }) => [unused('defaultLanguage')], +}; + +export function plugin() { + return new NewsfeedPlugin(); +} diff --git a/src/plugins/newsfeed/constants.ts b/src/plugins/newsfeed/server/plugin.ts similarity index 81% rename from src/plugins/newsfeed/constants.ts rename to src/plugins/newsfeed/server/plugin.ts index ddcbbb6cb1db..60b69b3977bf 100644 --- a/src/plugins/newsfeed/constants.ts +++ b/src/plugins/newsfeed/server/plugin.ts @@ -17,6 +17,12 @@ * under the License. */ -export const NEWSFEED_FALLBACK_LANGUAGE = 'en'; -export const NEWSFEED_LAST_FETCH_STORAGE_KEY = 'newsfeed.lastfetchtime'; -export const NEWSFEED_HASH_SET_STORAGE_KEY = 'newsfeed.hashes'; +import { Plugin } from 'kibana/server'; + +export class NewsfeedPlugin implements Plugin { + public setup() {} + + public start() {} + + public stop() {} +} diff --git a/test/common/fixtures/plugins/newsfeed/kibana.json b/test/common/fixtures/plugins/newsfeed/kibana.json new file mode 100644 index 000000000000..110b53fc6b2e --- /dev/null +++ b/test/common/fixtures/plugins/newsfeed/kibana.json @@ -0,0 +1,6 @@ +{ + "id": "newsfeed-fixtures", + "version": "kibana", + "server": true, + "ui": false +} diff --git a/test/common/fixtures/plugins/newsfeed/newsfeed_simulation.ts b/test/common/fixtures/plugins/newsfeed/newsfeed_simulation.ts deleted file mode 100644 index 5aa44b48f9d5..000000000000 --- a/test/common/fixtures/plugins/newsfeed/newsfeed_simulation.ts +++ /dev/null @@ -1,126 +0,0 @@ -/* - * 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 Hapi from 'hapi'; - -interface WebhookRequest extends Hapi.Request { - payload: string; -} - -export async function initPlugin(server: Hapi.Server, path: string) { - server.route({ - method: ['GET'], - path: `${path}/kibana/v{version}.json`, - options: { - cors: { - origin: ['*'], - additionalHeaders: [ - 'Sec-Fetch-Mode', - 'Access-Control-Request-Method', - 'Access-Control-Request-Headers', - 'cache-control', - 'x-requested-with', - 'Origin', - 'User-Agent', - 'DNT', - 'content-type', - 'kbn-version', - ], - }, - }, - handler: newsfeedHandler as Hapi.Lifecycle.Method, - }); - - server.route({ - method: ['GET'], - path: `${path}/kibana/crash.json`, - options: { - cors: { - origin: ['*'], - additionalHeaders: [ - 'Sec-Fetch-Mode', - 'Access-Control-Request-Method', - 'Access-Control-Request-Headers', - 'cache-control', - 'x-requested-with', - 'Origin', - 'User-Agent', - 'DNT', - 'content-type', - 'kbn-version', - ], - }, - }, - handler() { - throw new Error('Internal server error'); - }, - }); -} - -function newsfeedHandler(request: WebhookRequest, h: any) { - return htmlResponse(h, 200, JSON.stringify(mockNewsfeed(request.params.version))); -} - -const mockNewsfeed = (version: string) => ({ - items: [ - { - title: { en: `You are functionally testing the newsfeed widget with fixtures!` }, - description: { en: 'See test/common/fixtures/plugins/newsfeed/newsfeed_simulation' }, - link_text: { en: 'Generic feed-viewer could go here' }, - link_url: { en: 'https://feeds.elastic.co' }, - languages: null, - badge: null, - image_url: null, - publish_on: '2019-06-21T00:00:00', - expire_on: '2040-01-31T00:00:00', - hash: '39ca7d409c7eb25f4c69a5a6a11309b2f5ced7ca3f9b3a0109517126e0fd91ca', - }, - { - title: { en: 'Staging too!' }, - description: { en: 'Hello world' }, - link_text: { en: 'Generic feed-viewer could go here' }, - link_url: { en: 'https://feeds-staging.elastic.co' }, - languages: null, - badge: null, - image_url: null, - publish_on: '2019-06-21T00:00:00', - expire_on: '2040-01-31T00:00:00', - hash: 'db445c9443eb50ea2eb15f20edf89cf0f7dac2b058b11cafc2c8c288b6e4ce2a', - }, - { - title: { en: 'This item is expired!' }, - description: { en: 'This should not show up.' }, - link_text: { en: 'Generic feed-viewer could go here' }, - link_url: { en: 'https://feeds-staging.elastic.co' }, - languages: null, - badge: null, - image_url: null, - publish_on: '2019-06-21T00:00:00', - expire_on: '2019-12-31T00:00:00', - hash: 'db445c9443eb50ea2eb15f20edf89cf0f7dac2b058b11cafc2c8c288b6e4ce2a', - }, - ], -}); - -function htmlResponse(h: any, code: number, text: string) { - return h - .response(text) - .type('application/json') - .code(code); -} diff --git a/test/common/fixtures/plugins/newsfeed/package.json b/test/common/fixtures/plugins/newsfeed/package.json deleted file mode 100644 index 5291b1031b0a..000000000000 --- a/test/common/fixtures/plugins/newsfeed/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "newsfeed-fixtures", - "version": "0.0.0", - "kibana": { - "version": "kibana" - } -} diff --git a/src/legacy/core_plugins/newsfeed/constants.ts b/test/common/fixtures/plugins/newsfeed/server/index.ts similarity index 76% rename from src/legacy/core_plugins/newsfeed/constants.ts rename to test/common/fixtures/plugins/newsfeed/server/index.ts index 55a0c51c2ac6..979aadfe8660 100644 --- a/src/legacy/core_plugins/newsfeed/constants.ts +++ b/test/common/fixtures/plugins/newsfeed/server/index.ts @@ -17,7 +17,9 @@ * under the License. */ -export const PLUGIN_ID = 'newsfeed'; -export const DEFAULT_SERVICE_URLROOT = 'https://feeds.elastic.co'; -export const DEV_SERVICE_URLROOT = 'https://feeds-staging.elastic.co'; -export const DEFAULT_SERVICE_PATH = '/kibana/v{VERSION}.json'; +import { PluginInitializerContext } from 'kibana/public'; +import { NewsFeedSimulatorPlugin } from './plugin'; + +export function plugin(initializerContext: PluginInitializerContext) { + return new NewsFeedSimulatorPlugin(initializerContext); +} diff --git a/test/common/fixtures/plugins/newsfeed/server/plugin.ts b/test/common/fixtures/plugins/newsfeed/server/plugin.ts new file mode 100644 index 000000000000..60044d4e0b2d --- /dev/null +++ b/test/common/fixtures/plugins/newsfeed/server/plugin.ts @@ -0,0 +1,97 @@ +/* + * 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 { CoreSetup, Plugin } from 'kibana/server'; +import { PluginInitializerContext } from 'kibana/public'; + +export class NewsFeedSimulatorPlugin implements Plugin { + constructor(private readonly initializerContext: PluginInitializerContext) {} + + public setup({ http }: CoreSetup) { + const router = http.createRouter(); + const version = this.initializerContext.env.packageInfo.version; + + router.get( + { + path: `/api/_newsfeed-FTS-external-service-simulators/kibana/v${version}.json`, + validate: false, + options: { authRequired: false }, + }, + (context, req, res) => { + return res.ok({ body: this.mockNewsfeed() }); + } + ); + + router.get( + { + path: '/api/_newsfeed-FTS-external-service-simulators/kibana/crash.json', + validate: false, + options: { authRequired: false }, + }, + (context, req, res) => { + return res.internalError({ body: new Error('Internal server error') }); + } + ); + } + + public start() {} + + private mockNewsfeed() { + return { + items: [ + { + title: { en: `You are functionally testing the newsfeed widget with fixtures!` }, + description: { en: 'See test/common/fixtures/plugins/newsfeed/newsfeed_simulation' }, + link_text: { en: 'Generic feed-viewer could go here' }, + link_url: { en: 'https://feeds.elastic.co' }, + languages: null, + badge: null, + image_url: null, + publish_on: '2019-06-21T00:00:00', + expire_on: '2040-01-31T00:00:00', + hash: '39ca7d409c7eb25f4c69a5a6a11309b2f5ced7ca3f9b3a0109517126e0fd91ca', + }, + { + title: { en: 'Staging too!' }, + description: { en: 'Hello world' }, + link_text: { en: 'Generic feed-viewer could go here' }, + link_url: { en: 'https://feeds-staging.elastic.co' }, + languages: null, + badge: null, + image_url: null, + publish_on: '2019-06-21T00:00:00', + expire_on: '2040-01-31T00:00:00', + hash: 'db445c9443eb50ea2eb15f20edf89cf0f7dac2b058b11cafc2c8c288b6e4ce2a', + }, + { + title: { en: 'This item is expired!' }, + description: { en: 'This should not show up.' }, + link_text: { en: 'Generic feed-viewer could go here' }, + link_url: { en: 'https://feeds-staging.elastic.co' }, + languages: null, + badge: null, + image_url: null, + publish_on: '2019-06-21T00:00:00', + expire_on: '2019-12-31T00:00:00', + hash: 'db445c9443eb50ea2eb15f20edf89cf0f7dac2b058b11cafc2c8c288b6e4ce2a', + }, + ], + }; + } +}