mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
Search indices consume onboarding token (#211755)
## Summary This consumes the onboarding token propagated by Cloud to determine which workflow to show in Serverless and ECH. Best way to test this locally when running on localhost:5601: In Serverless: - Go to http://localhost:5601/app/cloud/onboarding?next=/app/elasticsearch&onboarding_token=vector - You should be redirected to the getting started flow - Switch to code view and you should have vector search selected - Go to http://localhost:5601/app/cloud/onboarding?next=/app/enterprise_search/overview&onboarding_token=vectorsearch - You should be redirected to the getting started flow - Switch to code view and now you should have vector search selected --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
cb71dff86e
commit
6507cc3fd0
20 changed files with 156 additions and 21 deletions
|
@ -13,3 +13,5 @@ export const plugin = async (initializerContext: PluginInitializerContext) => {
|
|||
const { CloudPlugin } = await import('./plugin');
|
||||
return new CloudPlugin(initializerContext);
|
||||
};
|
||||
|
||||
export { getOnboardingToken } from './saved_objects';
|
||||
|
|
|
@ -253,7 +253,7 @@ export class CloudPlugin implements Plugin<CloudSetup, CloudStart> {
|
|||
const nextCandidateRoute = parseNextURL(request.url.href);
|
||||
|
||||
const route = nextCandidateRoute === '/' ? defaultRoute : nextCandidateRoute;
|
||||
// need to get reed of ../../ to make sure we will not be out of space basePath
|
||||
// need to get rid of ../../ to make sure we will not be out of space basePath
|
||||
const normalizedRoute = new URL(route, 'https://localhost');
|
||||
|
||||
const queryOnboardingToken = request.query?.onboarding_token ?? undefined;
|
||||
|
@ -265,6 +265,7 @@ export class CloudPlugin implements Plugin<CloudSetup, CloudStart> {
|
|||
: undefined;
|
||||
|
||||
const solutionType = this.config.onboarding?.default_solution;
|
||||
|
||||
if (queryOnboardingToken || queryOnboardingSecurity) {
|
||||
core
|
||||
.getStartServices()
|
||||
|
|
|
@ -5,7 +5,9 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { Logger, SavedObjectsServiceSetup } from '@kbn/core/server';
|
||||
import { Logger, SavedObjectsClientContract, SavedObjectsServiceSetup } from '@kbn/core/server';
|
||||
import { CloudDataAttributes } from '../../common/types';
|
||||
import { CLOUD_DATA_SAVED_OBJECT_ID } from '../routes/constants';
|
||||
|
||||
export const CLOUD_DATA_SAVED_OBJECT_TYPE = 'cloud' as const;
|
||||
|
||||
|
@ -25,3 +27,19 @@ export function setupSavedObjects(savedObjects: SavedObjectsServiceSetup, logger
|
|||
modelVersions: {},
|
||||
});
|
||||
}
|
||||
|
||||
// needs a client with permissions to read the cloud data saved object
|
||||
export async function getOnboardingToken(
|
||||
savedObjectsClient: SavedObjectsClientContract
|
||||
): Promise<string | null> {
|
||||
let cloudDataSo = null;
|
||||
try {
|
||||
cloudDataSo = await savedObjectsClient.get<CloudDataAttributes>(
|
||||
CLOUD_DATA_SAVED_OBJECT_TYPE,
|
||||
CLOUD_DATA_SAVED_OBJECT_ID
|
||||
);
|
||||
} catch (error) {
|
||||
cloudDataSo = null;
|
||||
}
|
||||
return cloudDataSo?.attributes.onboardingData?.token || null;
|
||||
}
|
||||
|
|
|
@ -9,3 +9,5 @@ export * from './src/connector_icon';
|
|||
export * from './src/decorative_horizontal_stepper';
|
||||
export * from './src/form_info_field/form_info_field';
|
||||
export * from './src/search_empty_prompt';
|
||||
export * from './src/constants';
|
||||
export * from './src/types';
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
export const WORKFLOW_LOCALSTORAGE_KEY = 'search_onboarding_workflow';
|
8
x-pack/solutions/search/packages/shared-ui/src/types.ts
Normal file
8
x-pack/solutions/search/packages/shared-ui/src/types.ts
Normal file
|
@ -0,0 +1,8 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
export type WorkflowId = 'default' | 'vector' | 'semantic';
|
|
@ -13,3 +13,5 @@ export const POST_CREATE_INDEX_ROUTE = '/internal/search_indices/indices/create'
|
|||
export const INDEX_DOCUMENT_ROUTE = '/internal/search_indices/{indexName}/documents/{id}';
|
||||
|
||||
export const SEARCH_DOCUMENTS_ROUTE = '/internal/search_indices/{indexName}/documents/search';
|
||||
|
||||
export const GET_ONBOARDING_TOKEN_ROUTE = '/internal/search_indices/onboarding_token';
|
||||
|
|
|
@ -9,6 +9,10 @@ export interface IndicesStatusResponse {
|
|||
indexNames: string[];
|
||||
}
|
||||
|
||||
export interface OnboardingTokenResponse {
|
||||
token: string | null;
|
||||
}
|
||||
|
||||
export interface UserStartPrivilegesResponse {
|
||||
privileges: {
|
||||
canCreateApiKeys: boolean;
|
||||
|
|
|
@ -6,8 +6,7 @@
|
|||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
export type WorkflowId = 'default' | 'vector' | 'semantic';
|
||||
import { WorkflowId } from '@kbn/search-shared-ui';
|
||||
|
||||
export interface Workflow {
|
||||
title: string;
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
import React, { useCallback, useState } from 'react';
|
||||
|
||||
import { WorkflowId } from '@kbn/search-shared-ui';
|
||||
import type { IndicesStatusResponse } from '../../../common';
|
||||
|
||||
import { AnalyticsEvents } from '../../analytics/constants';
|
||||
|
@ -22,7 +23,6 @@ import { CreateIndexPanel } from '../shared/create_index_panel/create_index_pane
|
|||
|
||||
import { CreateIndexCodeView } from './create_index_code_view';
|
||||
import { CreateIndexUIView } from './create_index_ui_view';
|
||||
import { WorkflowId } from '../../code_examples/workflows';
|
||||
import { useWorkflow } from '../shared/hooks/use_workflow';
|
||||
|
||||
function initCreateIndexState() {
|
||||
|
|
|
@ -27,6 +27,10 @@ jest.mock('../../hooks/use_elasticsearch_url', () => ({
|
|||
useElasticsearchUrl: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock('../../hooks/api/use_onboarding_data', () => ({
|
||||
useOnboardingTokenQuery: jest.fn().mockReturnValue({ data: { token: 'default' } }),
|
||||
}));
|
||||
|
||||
jest.mock('@kbn/search-api-keys-components', () => ({
|
||||
useSearchApiKey: jest.fn().mockReturnValue({ apiKey: 'test-api-key' }),
|
||||
}));
|
||||
|
|
|
@ -12,6 +12,7 @@ import { i18n } from '@kbn/i18n';
|
|||
import { TryInConsoleButton } from '@kbn/try-in-console';
|
||||
|
||||
import { useSearchApiKey } from '@kbn/search-api-keys-components';
|
||||
import { WorkflowId } from '@kbn/search-shared-ui';
|
||||
import { useKibana } from '../../hooks/use_kibana';
|
||||
import { IngestCodeSnippetParameters } from '../../types';
|
||||
import { LanguageSelector } from '../shared/language_selector';
|
||||
|
@ -24,7 +25,6 @@ import { generateSampleDocument } from '../../utils/document_generation';
|
|||
import { getDefaultCodingLanguage } from '../../utils/language';
|
||||
import { GuideSelector } from '../shared/guide_selector';
|
||||
import { useWorkflow } from '../shared/hooks/use_workflow';
|
||||
import { WorkflowId } from '../../code_examples/workflows';
|
||||
|
||||
export const exampleTexts = [
|
||||
'Yellowstone National Park is one of the largest national parks in the United States. It ranges from the Wyoming to Montana and Idaho, and contains an area of 2,219,791 acress across three different states. Its most famous for hosting the geyser Old Faithful and is centered on the Yellowstone Caldera, the largest super volcano on the American continent. Yellowstone is host to hundreds of species of animal, many of which are endangered or threatened. Most notably, it contains free-ranging herds of bison and elk, alongside bears, cougars and wolves. The national park receives over 4.5 million visitors annually and is a UNESCO World Heritage Site.',
|
||||
|
|
|
@ -17,6 +17,7 @@ import { TryInConsoleButton } from '@kbn/try-in-console';
|
|||
|
||||
import { useSearchApiKey } from '@kbn/search-api-keys-components';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { WorkflowId } from '@kbn/search-shared-ui';
|
||||
import { Languages, AvailableLanguages, LanguageOptions } from '../../code_examples';
|
||||
|
||||
import { useUsageTracker } from '../../hooks/use_usage_tracker';
|
||||
|
@ -27,7 +28,7 @@ import { APIKeyCallout } from './api_key_callout';
|
|||
import { CodeSample } from './code_sample';
|
||||
import { LanguageSelector } from './language_selector';
|
||||
import { GuideSelector } from './guide_selector';
|
||||
import { Workflow, WorkflowId } from '../../code_examples/workflows';
|
||||
import { Workflow } from '../../code_examples/workflows';
|
||||
import { CreateIndexCodeExamples } from '../../types';
|
||||
|
||||
export interface CreateIndexCodeViewProps {
|
||||
|
|
|
@ -9,7 +9,8 @@ import React from 'react';
|
|||
|
||||
import { EuiCard, EuiText, EuiFlexGroup, EuiFlexItem, EuiTourStep } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { WorkflowId, workflows } from '../../code_examples/workflows';
|
||||
import { WorkflowId } from '@kbn/search-shared-ui';
|
||||
import { workflows } from '../../code_examples/workflows';
|
||||
import { useGuideTour } from './hooks/use_guide_tour';
|
||||
|
||||
interface GuideSelectorProps {
|
||||
|
|
|
@ -5,18 +5,20 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { useState } from 'react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { WORKFLOW_LOCALSTORAGE_KEY, WorkflowId } from '@kbn/search-shared-ui';
|
||||
import {
|
||||
DenseVectorIngestDataCodeExamples,
|
||||
SemanticIngestDataCodeExamples,
|
||||
DefaultIngestDataCodeExamples,
|
||||
} from '../../../code_examples/ingest_data';
|
||||
import { WorkflowId, workflows } from '../../../code_examples/workflows';
|
||||
import { workflows } from '../../../code_examples/workflows';
|
||||
import {
|
||||
DefaultCodeExamples,
|
||||
DenseVectorCodeExamples,
|
||||
SemanticCodeExamples,
|
||||
} from '../../../code_examples/create_index';
|
||||
import { useOnboardingTokenQuery } from '../../../hooks/api/use_onboarding_data';
|
||||
|
||||
const workflowIdToCreateIndexExamples = (type: WorkflowId) => {
|
||||
switch (type) {
|
||||
|
@ -40,24 +42,43 @@ const workflowIdToIngestDataExamples = (type: WorkflowId) => {
|
|||
}
|
||||
};
|
||||
|
||||
const WORKFLOW_LOCALSTORAGE_KEY = 'search_onboarding_workflow';
|
||||
|
||||
function isWorkflowId(value: string | null): value is WorkflowId {
|
||||
return value === 'default' || value === 'vector' || value === 'semantic';
|
||||
}
|
||||
|
||||
// possible onboarding tokens now: 'general' | 'vector' | 'timeseries' | 'semantic' for serverless, 'vectorsearch' or 'search' for hosted
|
||||
// note: test with http://localhost:5601/app/cloud/onboarding?next=/app/elasticsearch&onboarding_token=vector in Serverless
|
||||
// http://localhost:5601/app/cloud/onboarding?next=/app/enterprise_search/overview&onboarding_token=vector in Hosted
|
||||
|
||||
function onboardingTokenToWorkflowId(token: string | undefined | null): WorkflowId {
|
||||
switch (token) {
|
||||
case 'vector':
|
||||
return 'vector';
|
||||
case 'vectorsearch':
|
||||
return 'vector';
|
||||
case 'semantic':
|
||||
return 'semantic';
|
||||
default:
|
||||
return 'default';
|
||||
}
|
||||
}
|
||||
|
||||
export const useWorkflow = () => {
|
||||
// TODO: in the future this will be dynamic based on the onboarding token
|
||||
// or project sub-type
|
||||
const localStorageWorkflow = localStorage.getItem(WORKFLOW_LOCALSTORAGE_KEY);
|
||||
const [selectedWorkflowId, setSelectedWorkflowId] = useState<WorkflowId>(
|
||||
isWorkflowId(localStorageWorkflow) ? localStorageWorkflow : 'default'
|
||||
);
|
||||
const workflowId = isWorkflowId(localStorageWorkflow) ? localStorageWorkflow : null;
|
||||
const [selectedWorkflowId, setSelectedWorkflowId] = useState<WorkflowId>(workflowId || 'default');
|
||||
const { data } = useOnboardingTokenQuery();
|
||||
|
||||
useEffect(() => {
|
||||
if (data?.token && !localStorageWorkflow) {
|
||||
setSelectedWorkflowId(onboardingTokenToWorkflowId(data.token));
|
||||
}
|
||||
}, [data, localStorageWorkflow]);
|
||||
return {
|
||||
selectedWorkflowId,
|
||||
setSelectedWorkflowId: (workflowId: WorkflowId) => {
|
||||
localStorage.setItem(WORKFLOW_LOCALSTORAGE_KEY, workflowId);
|
||||
setSelectedWorkflowId(workflowId);
|
||||
setSelectedWorkflowId: (newWorkflowId: WorkflowId) => {
|
||||
localStorage.setItem(WORKFLOW_LOCALSTORAGE_KEY, newWorkflowId);
|
||||
setSelectedWorkflowId(newWorkflowId);
|
||||
},
|
||||
workflow: workflows.find((workflow) => workflow.id === selectedWorkflowId),
|
||||
createIndexExamples: workflowIdToCreateIndexExamples(selectedWorkflowId),
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import { WorkflowId } from '@kbn/search-shared-ui';
|
||||
import type { IndicesStatusResponse } from '../../../common';
|
||||
|
||||
import { AnalyticsEvents } from '../../analytics/constants';
|
||||
|
@ -23,7 +24,6 @@ import { CreateIndexFormState, CreateIndexViewMode } from '../../types';
|
|||
import { CreateIndexPanel } from '../shared/create_index_panel/create_index_panel';
|
||||
import { useKibana } from '../../hooks/use_kibana';
|
||||
import { useUserPrivilegesQuery } from '../../hooks/api/use_user_permissions';
|
||||
import { WorkflowId } from '../../code_examples/workflows';
|
||||
import { useWorkflow } from '../shared/hooks/use_workflow';
|
||||
|
||||
function initCreateIndexState(): CreateIndexFormState {
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
export enum QueryKeys {
|
||||
FetchIndex = 'fetchIndex',
|
||||
FetchMapping = 'fetchMapping',
|
||||
FetchOnboardingToken = 'fetchOnboardingToken',
|
||||
FetchSearchIndicesStatus = 'fetchSearchIndicesStatus',
|
||||
FetchUserStartPrivileges = 'fetchUserStartPrivileges',
|
||||
SearchDocuments = 'searchDocuments',
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import type { UseQueryResult } from '@tanstack/react-query';
|
||||
|
||||
import { GET_ONBOARDING_TOKEN_ROUTE } from '../../../common/routes';
|
||||
import type { OnboardingTokenResponse } from '../../../common/types';
|
||||
import { QueryKeys } from '../../constants';
|
||||
|
||||
import { useKibana } from '../use_kibana';
|
||||
|
||||
export const useOnboardingTokenQuery = (): UseQueryResult<OnboardingTokenResponse> => {
|
||||
const { http } = useKibana().services;
|
||||
|
||||
return useQuery({
|
||||
refetchInterval: false,
|
||||
retry: true,
|
||||
queryKey: [QueryKeys.FetchOnboardingToken],
|
||||
queryFn: () => http.get<OnboardingTokenResponse>(GET_ONBOARDING_TOKEN_ROUTE),
|
||||
});
|
||||
};
|
|
@ -12,9 +12,11 @@ import { registerSearchApiKeysRoutes } from '@kbn/search-api-keys-server';
|
|||
import { registerIndicesRoutes } from './indices';
|
||||
import { registerStatusRoutes } from './status';
|
||||
import { registerDocumentRoutes } from './documents';
|
||||
import { registerOnboardingRoutes } from './onboarding';
|
||||
|
||||
export function defineRoutes(router: IRouter, logger: Logger) {
|
||||
registerIndicesRoutes(router, logger);
|
||||
registerOnboardingRoutes(router, logger);
|
||||
registerStatusRoutes(router, logger);
|
||||
registerSearchApiKeysRoutes(router, logger);
|
||||
registerDocumentRoutes(router, logger);
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { IRouter } from '@kbn/core/server';
|
||||
import type { Logger } from '@kbn/logging';
|
||||
import { getOnboardingToken } from '@kbn/cloud-plugin/server';
|
||||
|
||||
import { GET_ONBOARDING_TOKEN_ROUTE } from '../../common/routes';
|
||||
|
||||
export function registerOnboardingRoutes(router: IRouter, logger: Logger) {
|
||||
router.get(
|
||||
{
|
||||
path: GET_ONBOARDING_TOKEN_ROUTE,
|
||||
validate: {},
|
||||
options: {
|
||||
access: 'internal',
|
||||
},
|
||||
},
|
||||
async (context, _request, response) => {
|
||||
const core = await context.core;
|
||||
const savedObjectsClient = core.savedObjects.getClient({ includedHiddenTypes: ['cloud'] });
|
||||
const token = await getOnboardingToken(savedObjectsClient);
|
||||
const body = { token };
|
||||
|
||||
return response.ok({
|
||||
body,
|
||||
headers: { 'content-type': 'application/json' },
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue