Skip to content

Commit

Permalink
Merge branch 'feature-integrations-manager' into update-merge-master-…
Browse files Browse the repository at this point in the history
…docs
  • Loading branch information
elasticmachine authored Nov 13, 2019
2 parents a3def91 + a4b2ae7 commit efe2297
Show file tree
Hide file tree
Showing 9 changed files with 238 additions and 130 deletions.
19 changes: 7 additions & 12 deletions x-pack/legacy/plugins/integrations_manager/common/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { PLUGIN } from './constants';
import { AssetType, CategoryId } from './types';
import { CategoryId } from './types';

export const API_ROOT = `/api/${PLUGIN.ID}`;
export const API_LIST_PATTERN = `${API_ROOT}/list`;
export const API_INFO_PATTERN = `${API_ROOT}/package/{pkgkey}`;
export const API_INSTALL_PATTERN = `${API_ROOT}/install/{pkgkey}/{asset?}`;
export const API_DELETE_PATTERN = `${API_ROOT}/delete/{pkgkey}/{asset?}`;
export const API_INSTALL_PATTERN = `${API_ROOT}/install/{pkgkey}`;
export const API_DELETE_PATTERN = `${API_ROOT}/delete/{pkgkey}`;
export const API_CATEGORIES_PATTERN = `${API_ROOT}/categories`;

export interface ListParams {
Expand All @@ -32,15 +32,10 @@ export function getInfoPath(pkgkey: string) {
export function getFilePath(filePath: string) {
return `${API_ROOT}${filePath}`;
}

export function getInstallPath(pkgkey: string, asset?: AssetType) {
return API_INSTALL_PATTERN.replace('{pkgkey}', pkgkey)
.replace('{asset?}', asset || '')
.replace(/\/$/, ''); // trim trailing slash
export function getInstallPath(pkgkey: string) {
return API_INSTALL_PATTERN.replace('{pkgkey}', pkgkey).replace(/\/$/, ''); // trim trailing slash
}

export function getRemovePath(pkgkey: string, asset?: AssetType) {
return API_DELETE_PATTERN.replace('{pkgkey}', pkgkey)
.replace('{asset?}', asset || '')
.replace(/\/$/, ''); // trim trailing slash
export function getRemovePath(pkgkey: string) {
return API_DELETE_PATTERN.replace('{pkgkey}', pkgkey).replace(/\/$/, ''); // trim trailing slash
}
17 changes: 9 additions & 8 deletions x-pack/legacy/plugins/integrations_manager/common/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,15 @@ export { Request, ResponseToolkit, Server, ServerRoute } from 'hapi';

export type InstallationStatus = Installed['status'] | NotInstalled['status'];

export type AssetType =
| 'config'
| 'dashboard'
| 'index-pattern'
| 'ingest-pipeline'
| 'search'
| 'timelion-sheet'
| 'visualization';
export enum AssetType {
config = 'config',
dashboard = 'dashboard',
visualization = 'visualization',
search = 'search',
ingestPipeline = 'ingest-pipeline',
indexPattern = 'index-pattern',
timelionSheet = 'timelion-sheet',
}

// Registry's response types
// from /search
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/*
* 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, { useState, useRef, useLayoutEffect, Fragment, useCallback } from 'react';
import { EuiButtonEmpty, EuiSpacer, EuiButton, EuiHorizontalRule } from '@elastic/eui';
import euiStyled from '../../../../common/eui_styled_components';

const BottomFade = euiStyled.div`
width: 100%;
background: ${props =>
`linear-gradient(${props.theme.eui.euiColorEmptyShade}00 0%, ${props.theme.eui.euiColorEmptyShade} 100%)`};
margin-top: -${props => parseInt(props.theme.eui.spacerSizes.xl, 10) * 2}px;
height: ${props => parseInt(props.theme.eui.spacerSizes.xl, 10) * 2}px;
position: absolute;
`;
const ContentCollapseContainer = euiStyled.div`
position: relative;
`;
const CollapseButtonContainer = euiStyled.div`
display: inline-block;
background-color: ${props => props.theme.eui.euiColorGhost};
position: absolute;
left: 50%;
transform: translateX(-50%);
top: ${props => parseInt(props.theme.eui.euiButtonHeight, 10) / 2}px;
`;
const CollapseButtonTop = euiStyled(EuiButtonEmpty)`
float: right;
`;

const CollapseButton = ({
open,
toggleCollapse,
}: {
open: boolean;
toggleCollapse: () => void;
}) => {
return (
<div style={{ position: 'relative' }}>
<EuiSpacer size="m" />
<EuiHorizontalRule />
<CollapseButtonContainer>
<EuiButton onClick={toggleCollapse} iconType={`arrow${open ? 'Up' : 'Down'}`}>
{open ? 'Collapse' : 'Read more'}
</EuiButton>
</CollapseButtonContainer>
</div>
);
};

export const ContentCollapse = ({ children }: { children: React.ReactNode }) => {
const [open, setOpen] = useState<boolean>(false);
const [height, setHeight] = useState<number | string>('auto');
const [collapsible, setCollapsible] = useState<boolean>(true);
const contentEl = useRef<HTMLDivElement>(null);
const collapsedHeight = 360;

// if content is too small, don't collapse
useLayoutEffect(
() =>
contentEl.current && contentEl.current.clientHeight < collapsedHeight
? setCollapsible(false)
: setHeight(collapsedHeight),
[]
);

const clickOpen = useCallback(() => {
setOpen(!open);
}, [open]);

return (
<Fragment>
{collapsible ? (
<ContentCollapseContainer>
<div
ref={contentEl}
style={{ height: `${open ? 'auto' : `${height}px`}`, overflow: 'hidden' }}
>
{open && (
<CollapseButtonTop onClick={clickOpen} iconType={`arrow${open ? 'Up' : 'Down'}`}>
Collapse
</CollapseButtonTop>
)}
{children}
</div>
{!open && <BottomFade />}
<CollapseButton open={open} toggleCollapse={clickOpen} />
</ContentCollapseContainer>
) : (
<div>{children}</div>
)}
</Fragment>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import ReactMarkdown from 'react-markdown';
import { getFileByPath } from '../../data';
import { markdownRenderers } from './markdown_renderers';
import { useLinks } from '../../hooks';
import { ContentCollapse } from '../../components/content_collapse';

export function Readme({
readmePath,
Expand Down Expand Up @@ -40,11 +41,13 @@ export function Readme({
return (
<Fragment>
{markdown !== undefined ? (
<ReactMarkdown
transformImageUri={handleImageUri}
renderers={markdownRenderers}
source={markdown}
/>
<ContentCollapse>
<ReactMarkdown
transformImageUri={handleImageUri}
renderers={markdownRenderers}
source={markdown}
/>
</ContentCollapse>
) : (
<EuiText>
{/* simulates a long page of text loading */}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,24 +27,19 @@ interface ListPackagesRequest extends Request {
query: Request['query'] & SearchParams;
}

interface PackageRequest extends Request {
interface PackageInfoRequest extends Request {
params: {
pkgkey: string;
};
}

interface InstallAssetRequest extends Request {
params: AssetRequestParams;
}

interface DeleteAssetRequest extends Request {
params: AssetRequestParams;
interface InstallDeletePackageRequest extends Request {
params: {
pkgkey: string;
asset: AssetType;
};
}

type AssetRequestParams = PackageRequest['params'] & {
asset?: AssetType;
};

export async function handleGetCategories(req: Request, extra: Extra) {
return getCategories();
}
Expand All @@ -59,7 +54,7 @@ export async function handleGetList(req: ListPackagesRequest, extra: Extra) {
return packageList;
}

export async function handleGetInfo(req: PackageRequest, extra: Extra) {
export async function handleGetInfo(req: PackageInfoRequest, extra: Extra) {
const { pkgkey } = req.params;
const savedObjectsClient = getClient(req);
const packageInfo = await getPackageInfo({ savedObjectsClient, pkgkey });
Expand All @@ -81,26 +76,20 @@ export const handleGetFile = async (req: Request, extra: Extra) => {
return epmResponse;
};

export async function handleRequestInstall(req: InstallAssetRequest, extra: Extra) {
const { pkgkey, asset } = req.params;
if (!asset) throw new Error('Unhandled empty/default asset case');

export async function handleRequestInstall(req: InstallDeletePackageRequest, extra: Extra) {
const { pkgkey } = req.params;
const savedObjectsClient = getClient(req);
const callCluster = getClusterAccessor(extra.context.esClient, req);
const object = await installPackage({
return await installPackage({
savedObjectsClient,
pkgkey,
asset,
callCluster,
});

return object;
}

export async function handleRequestDelete(req: DeleteAssetRequest, extra: Extra) {
export async function handleRequestDelete(req: InstallDeletePackageRequest, extra: Extra) {
const { pkgkey } = req.params;
const savedObjectsClient = getClient(req);
const deleted = await removeInstallation({ savedObjectsClient, pkgkey });
const callCluster = getClusterAccessor(extra.context.esClient, req);
const deleted = await removeInstallation({ savedObjectsClient, pkgkey, callCluster });

return deleted;
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ export * from './handlers';
export type CallESAsCurrentUser = ScopedClusterClient['callAsCurrentUser'];

export const SAVED_OBJECT_TYPES = new Set<AssetType>([
'config',
'dashboard',
'index-pattern',
'search',
'timelion-sheet',
'visualization',
AssetType.config,
AssetType.dashboard,
AssetType.indexPattern,
AssetType.search,
AssetType.timelionSheet,
AssetType.visualization,
]);

export function getClusterAccessor(esClient: IClusterClient, req: Request) {
Expand All @@ -40,6 +40,6 @@ export function createInstallableFrom<T>(from: T, savedObject?: Installation): I
};
}

export function assetUsesObjects(asset: AssetType) {
return SAVED_OBJECT_TYPES.has(asset);
export function assetUsesObjects(assetType: AssetType) {
return SAVED_OBJECT_TYPES.has(assetType);
}
Loading

0 comments on commit efe2297

Please sign in to comment.