From 0a3434192cdb42b9a69741c89f8e60d120b00bdf Mon Sep 17 00:00:00 2001 From: Kristen Tian <105667444+kristenTian@users.noreply.github.com> Date: Tue, 16 Aug 2022 16:12:14 -0700 Subject: [PATCH] Integrate index pattern with new data client (#2146) Signed-off-by: Kristen Tian --- .../__snapshots__/index_patterns.test.ts.snap | 1 + .../index_patterns/index_patterns.ts | 10 ++++-- .../data/common/index_patterns/types.ts | 1 + .../index_patterns_api_client.ts | 7 ++-- .../fetcher/index_patterns_fetcher.ts | 6 ++-- .../field_capabilities/field_capabilities.ts | 4 +-- .../fetcher/lib/opensearch_api.ts | 23 +++++++++++-- .../fetcher/lib/resolve_time_pattern.ts | 7 ++-- .../data/server/index_patterns/routes.ts | 15 +++++++-- .../step_index_pattern/step_index_pattern.tsx | 33 ++++++++++++++++--- .../step_time_field/step_time_field.tsx | 6 ++-- .../create_index_pattern_wizard.tsx | 29 +++++++++++----- .../lib/get_indices.ts | 27 +++++++++++++-- .../index_pattern_table.tsx | 20 ++++++----- .../server/routes/resolve_index.ts | 10 ++++++ 15 files changed, 155 insertions(+), 44 deletions(-) diff --git a/src/plugins/data/common/index_patterns/index_patterns/__snapshots__/index_patterns.test.ts.snap b/src/plugins/data/common/index_patterns/index_patterns/__snapshots__/index_patterns.test.ts.snap index e015fd0b5e19..5f2338b77aa1 100644 --- a/src/plugins/data/common/index_patterns/index_patterns/__snapshots__/index_patterns.test.ts.snap +++ b/src/plugins/data/common/index_patterns/index_patterns/__snapshots__/index_patterns.test.ts.snap @@ -2,6 +2,7 @@ exports[`IndexPatterns savedObjectToSpec 1`] = ` Object { + "dataSourceRef": undefined, "fields": Object {}, "id": "id", "intervalName": undefined, diff --git a/src/plugins/data/common/index_patterns/index_patterns/index_patterns.ts b/src/plugins/data/common/index_patterns/index_patterns/index_patterns.ts index 4ec31928257e..05c34ea817e4 100644 --- a/src/plugins/data/common/index_patterns/index_patterns/index_patterns.ts +++ b/src/plugins/data/common/index_patterns/index_patterns/index_patterns.ts @@ -29,8 +29,8 @@ */ import { i18n } from '@osd/i18n'; +import _ from 'lodash'; import { SavedObjectsClientCommon } from '../..'; - import { createIndexPatternCache } from '.'; import { IndexPattern } from './index_pattern'; import { @@ -238,6 +238,7 @@ export class IndexPatternsService { metaFields, type: options.type, params: options.params || {}, + dataSourceId: options.dataSourceId, }); }; @@ -254,6 +255,7 @@ export class IndexPatternsService { ...options, type: indexPattern.type, params: indexPattern.typeMeta && indexPattern.typeMeta.params, + dataSourceId: indexPattern.dataSourceRef?.id, }); /** @@ -355,12 +357,14 @@ export class IndexPatternsService { typeMeta, type, }, + references, } = savedObject; const parsedSourceFilters = sourceFilters ? JSON.parse(sourceFilters) : undefined; const parsedTypeMeta = typeMeta ? JSON.parse(typeMeta) : undefined; const parsedFieldFormatMap = fieldFormatMap ? JSON.parse(fieldFormatMap) : {}; const parsedFields: FieldSpec[] = fields ? JSON.parse(fields) : []; + const dataSourceRef = !_.isEmpty(references) ? references[0] : undefined; this.addFormatsToFields(parsedFields, parsedFieldFormatMap); return { @@ -373,6 +377,7 @@ export class IndexPatternsService { fields: this.fieldArrayToMap(parsedFields), typeMeta: parsedTypeMeta, type, + dataSourceRef, }; }; @@ -401,7 +406,7 @@ export class IndexPatternsService { } const spec = this.savedObjectToSpec(savedObject); - const { title, type, typeMeta } = spec; + const { title, type, typeMeta, dataSourceRef } = spec; const parsedFieldFormats: FieldFormatMap = savedObject.attributes.fieldFormatMap ? JSON.parse(savedObject.attributes.fieldFormatMap) : {}; @@ -415,6 +420,7 @@ export class IndexPatternsService { metaFields: await this.config.get(UI_SETTINGS.META_FIELDS), type, params: typeMeta && typeMeta.params, + dataSourceId: dataSourceRef?.id, }) : spec.fields; } catch (err) { diff --git a/src/plugins/data/common/index_patterns/types.ts b/src/plugins/data/common/index_patterns/types.ts index 2200fb85d8b1..108a93a3725b 100644 --- a/src/plugins/data/common/index_patterns/types.ts +++ b/src/plugins/data/common/index_patterns/types.ts @@ -113,6 +113,7 @@ export interface GetFieldsOptions { params?: any; lookBack?: boolean; metaFields?: string[]; + dataSourceId?: string; } export interface IIndexPatternsApiClient { diff --git a/src/plugins/data/public/index_patterns/index_patterns/index_patterns_api_client.ts b/src/plugins/data/public/index_patterns/index_patterns/index_patterns_api_client.ts index c1486943ef4b..5f048e5cf758 100644 --- a/src/plugins/data/public/index_patterns/index_patterns/index_patterns_api_client.ts +++ b/src/plugins/data/public/index_patterns/index_patterns/index_patterns_api_client.ts @@ -60,7 +60,7 @@ export class IndexPatternsApiClient implements IIndexPatternsApiClient { } getFieldsForTimePattern(options: GetFieldsOptions = {}) { - const { pattern, lookBack, metaFields } = options; + const { pattern, lookBack, metaFields, dataSourceId } = options; const url = this._getUrl(['_fields_for_time_pattern']); @@ -68,11 +68,12 @@ export class IndexPatternsApiClient implements IIndexPatternsApiClient { pattern, look_back: lookBack, meta_fields: metaFields, + data_source: dataSourceId, }).then((resp: any) => resp.fields); } getFieldsForWildcard(options: GetFieldsOptions = {}) { - const { pattern, metaFields, type, params } = options; + const { pattern, metaFields, type, params, dataSourceId } = options; let url; let query; @@ -83,12 +84,14 @@ export class IndexPatternsApiClient implements IIndexPatternsApiClient { pattern, meta_fields: metaFields, params: JSON.stringify(params), + data_source: dataSourceId, }; } else { url = this._getUrl(['_fields_for_wildcard']); query = { pattern, meta_fields: metaFields, + data_source: dataSourceId, }; } diff --git a/src/plugins/data/server/index_patterns/fetcher/index_patterns_fetcher.ts b/src/plugins/data/server/index_patterns/fetcher/index_patterns_fetcher.ts index 9144e505133c..a53e06cdea0a 100644 --- a/src/plugins/data/server/index_patterns/fetcher/index_patterns_fetcher.ts +++ b/src/plugins/data/server/index_patterns/fetcher/index_patterns_fetcher.ts @@ -28,7 +28,7 @@ * under the License. */ -import { LegacyAPICaller } from 'opensearch-dashboards/server'; +import { LegacyAPICaller, OpenSearchClient } from 'opensearch-dashboards/server'; import { getFieldCapabilities, resolveTimePattern, createNoMatchingIndicesError } from './lib'; @@ -48,9 +48,9 @@ interface FieldSubType { } export class IndexPatternsFetcher { - private _callDataCluster: LegacyAPICaller; + private _callDataCluster: LegacyAPICaller | OpenSearchClient; - constructor(callDataCluster: LegacyAPICaller) { + constructor(callDataCluster: LegacyAPICaller | OpenSearchClient) { this._callDataCluster = callDataCluster; } diff --git a/src/plugins/data/server/index_patterns/fetcher/lib/field_capabilities/field_capabilities.ts b/src/plugins/data/server/index_patterns/fetcher/lib/field_capabilities/field_capabilities.ts index 0aee1222a361..57d59ce5a486 100644 --- a/src/plugins/data/server/index_patterns/fetcher/lib/field_capabilities/field_capabilities.ts +++ b/src/plugins/data/server/index_patterns/fetcher/lib/field_capabilities/field_capabilities.ts @@ -30,7 +30,7 @@ import { defaults, keyBy, sortBy } from 'lodash'; -import { LegacyAPICaller } from 'opensearch-dashboards/server'; +import { LegacyAPICaller, OpenSearchClient } from 'opensearch-dashboards/server'; import { callFieldCapsApi } from '../opensearch_api'; import { FieldCapsResponse, readFieldCapsResponse } from './field_caps_response'; import { mergeOverrides } from './overrides'; @@ -47,7 +47,7 @@ import { FieldDescriptor } from '../../index_patterns_fetcher'; * @return {Promise>} */ export async function getFieldCapabilities( - callCluster: LegacyAPICaller, + callCluster: LegacyAPICaller | OpenSearchClient, indices: string | string[] = [], metaFields: string[] = [], fieldCapsOptions?: { allowNoIndices: boolean } diff --git a/src/plugins/data/server/index_patterns/fetcher/lib/opensearch_api.ts b/src/plugins/data/server/index_patterns/fetcher/lib/opensearch_api.ts index 9e090a414628..ed1cd0e89ee8 100644 --- a/src/plugins/data/server/index_patterns/fetcher/lib/opensearch_api.ts +++ b/src/plugins/data/server/index_patterns/fetcher/lib/opensearch_api.ts @@ -28,7 +28,7 @@ * under the License. */ -import { LegacyAPICaller } from 'opensearch-dashboards/server'; +import { LegacyAPICaller, OpenSearchClient } from 'opensearch-dashboards/server'; import { convertOpenSearchError } from './errors'; import { FieldCapsResponse } from './field_capabilities'; @@ -57,10 +57,18 @@ export interface IndexAliasResponse { * @return {Promise} */ export async function callIndexAliasApi( - callCluster: LegacyAPICaller, + callCluster: LegacyAPICaller | OpenSearchClient, indices: string[] | string ): Promise { try { + if ('transport' in callCluster) { + return ((await callCluster.indices.getAlias({ + index: indices, + ignore_unavailable: true, + allow_no_indices: true, + })) as unknown) as Promise; // todo: Pending #... verify test + } + return (await callCluster('indices.getAlias', { index: indices, ignoreUnavailable: true, @@ -84,11 +92,20 @@ export async function callIndexAliasApi( * @return {Promise} */ export async function callFieldCapsApi( - callCluster: LegacyAPICaller, + callCluster: LegacyAPICaller | OpenSearchClient, indices: string[] | string, fieldCapsOptions: { allowNoIndices: boolean } = { allowNoIndices: false } ) { try { + if ('transport' in callCluster) { + return ((await callCluster.fieldCaps({ + index: indices, + fields: '*', + ignore_unavailable: true, + allow_no_indices: fieldCapsOptions.allowNoIndices, + })) as unknown) as FieldCapsResponse; // todo: Pending #... verify test + } + return (await callCluster('fieldCaps', { index: indices, fields: '*', diff --git a/src/plugins/data/server/index_patterns/fetcher/lib/resolve_time_pattern.ts b/src/plugins/data/server/index_patterns/fetcher/lib/resolve_time_pattern.ts index cff81b650204..7b19ff78646f 100644 --- a/src/plugins/data/server/index_patterns/fetcher/lib/resolve_time_pattern.ts +++ b/src/plugins/data/server/index_patterns/fetcher/lib/resolve_time_pattern.ts @@ -31,7 +31,7 @@ import { chain } from 'lodash'; import moment from 'moment'; -import { LegacyAPICaller } from 'opensearch-dashboards/server'; +import { LegacyAPICaller, OpenSearchClient } from 'opensearch-dashboards/server'; import { timePatternToWildcard } from './time_pattern_to_wildcard'; import { callIndexAliasApi, IndicesAliasResponse } from './opensearch_api'; @@ -47,7 +47,10 @@ import { callIndexAliasApi, IndicesAliasResponse } from './opensearch_api'; * and the indices that actually match the time * pattern (matches); */ -export async function resolveTimePattern(callCluster: LegacyAPICaller, timePattern: string) { +export async function resolveTimePattern( + callCluster: LegacyAPICaller | OpenSearchClient, + timePattern: string +) { const aliases = await callIndexAliasApi(callCluster, timePatternToWildcard(timePattern)); const allIndexDetails = chain(aliases) diff --git a/src/plugins/data/server/index_patterns/routes.ts b/src/plugins/data/server/index_patterns/routes.ts index 83632615cd84..ab08c5754467 100644 --- a/src/plugins/data/server/index_patterns/routes.ts +++ b/src/plugins/data/server/index_patterns/routes.ts @@ -53,11 +53,12 @@ export function registerRoutes(http: HttpServiceSetup) { meta_fields: schema.oneOf([schema.string(), schema.arrayOf(schema.string())], { defaultValue: [], }), + data_source: schema.maybe(schema.string()), }), }, }, async (context, request, response) => { - const { callAsCurrentUser } = context.core.opensearch.legacy.client; + const callAsCurrentUser = decideClient(context, request); const indexPatterns = new IndexPatternsFetcher(callAsCurrentUser); const { pattern, meta_fields: metaFields } = request.query; @@ -112,11 +113,13 @@ export function registerRoutes(http: HttpServiceSetup) { meta_fields: schema.oneOf([schema.string(), schema.arrayOf(schema.string())], { defaultValue: [], }), + data_source: schema.maybe(schema.string()), }), }, }, async (context: RequestHandlerContext, request: any, response: any) => { - const { callAsCurrentUser } = context.core.opensearch.legacy.client; + const callAsCurrentUser = decideClient(context, request); + const indexPatterns = new IndexPatternsFetcher(callAsCurrentUser); const { pattern, interval, look_back: lookBack, meta_fields: metaFields } = request.query; @@ -147,3 +150,11 @@ export function registerRoutes(http: HttpServiceSetup) { } ); } + +const decideClient = (context: RequestHandlerContext, request: any) => { + if (request.query.data_source) { + // todo: # Pending + } + + return context.core.opensearch.legacy.client.callAsCurrentUser; +}; diff --git a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_index_pattern/step_index_pattern.tsx b/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_index_pattern/step_index_pattern.tsx index af94ff6f1f05..c9449213f603 100644 --- a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_index_pattern/step_index_pattern.tsx +++ b/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_index_pattern/step_index_pattern.tsx @@ -54,7 +54,7 @@ import { Header } from './components/header'; import { context as contextType } from '../../../../../../opensearch_dashboards_react/public'; import { IndexPatternCreationConfig } from '../../../../../../../plugins/index_pattern_management/public'; import { MatchedItem } from '../../types'; -import { IndexPatternManagmentContextValue } from '../../../../types'; +import { DataSourceRef, IndexPatternManagmentContextValue } from '../../../../types'; interface StepIndexPatternProps { allIndices: MatchedItem[]; @@ -63,6 +63,7 @@ interface StepIndexPatternProps { goToNextStep: (query: string, timestampField?: string) => void; initialQuery?: string; showSystemIndices: boolean; + dataSourceRef?: DataSourceRef; } interface StepIndexPatternState { @@ -163,7 +164,8 @@ export class StepIndexPattern extends Component { - const { indexPatternCreationType } = this.props; + const { indexPatternCreationType, dataSourceRef } = this.props; + const dataSourceId = dataSourceRef?.id; const { existingIndexPatterns } = this.state; const { http } = this.context.services; const getIndexTags = (indexName: string) => indexPatternCreationType.getIndexTags(indexName); @@ -179,7 +181,14 @@ export class StepIndexPattern extends Component void; indexPatternCreationType: IndexPatternCreationConfig; selectedTimeField?: string; + dataSourceRef?: DataSourceRef; } interface StepTimeFieldState { @@ -116,7 +117,7 @@ export class StepTimeField extends Component { - const { indexPattern: pattern } = this.props; + const { indexPattern: pattern, dataSourceRef } = this.props; const { getFetchForWildcardOptions } = this.props.indexPatternCreationType; this.setState({ isFetchingTimeFields: true }); @@ -124,6 +125,7 @@ export class StepTimeField extends Component { const { http } = this.context.services; + const { dataSourceRef } = this.state; + const dataSourceId = dataSourceRef?.id; const getIndexTags = (indexName: string) => this.state.indexPatternCreationType.getIndexTags(indexName); const searchClient = this.context.services.data.search.search; @@ -155,9 +160,9 @@ export class CreateIndexPatternWizard extends Component< ); // query local and remote indices, updating state independently - ensureMinimumTime( + const queryLocalAndRemotePromise = ensureMinimumTime( this.catchAndWarn( - getIndices({ http, getIndexTags, pattern: '*', searchClient }), + getIndices({ http, getIndexTags, pattern: '*', searchClient, dataSourceId }), [], indicesFailMsg @@ -166,16 +171,18 @@ export class CreateIndexPatternWizard extends Component< this.setState({ allIndices, isInitiallyLoadingIndices: false }) ); - this.catchAndWarn( + const queryWithFallbackPromise = this.catchAndWarn( // if we get an error from remote cluster query, supply fallback value that allows user entry. // ['a'] is fallback value - getIndices({ http, getIndexTags, pattern: '*:*', searchClient }), + getIndices({ http, getIndexTags, pattern: '*:*', searchClient, dataSourceId }), ['a'], clustersFailMsg ).then((remoteIndices: string[] | MatchedItem[]) => this.setState({ remoteClustersExist: !!remoteIndices.length }) ); + + await Promise.all([queryLocalAndRemotePromise, queryWithFallbackPromise]); }; createIndexPattern = async (timeFieldName: string | undefined, indexPatternId: string) => { @@ -232,8 +239,10 @@ export class CreateIndexPatternWizard extends Component< }; goToNextFromDataSource = (dataSourceRef: DataSourceRef) => { - this.setState({ dataSourceRef }); - this.goToNextStep(); + this.setState({ isInitiallyLoadingIndices: true, dataSourceRef }, async () => { + await this.fetchData(); + this.goToNextStep(); + }); }; goToNextStep = () => { @@ -261,7 +270,7 @@ export class CreateIndexPatternWizard extends Component< } renderContent() { - const { allIndices, isInitiallyLoadingIndices, step, indexPattern } = this.state; + const { allIndices, isInitiallyLoadingIndices, step, indexPattern, dataSourceRef } = this.state; if (isInitiallyLoadingIndices) { return ; @@ -297,6 +306,7 @@ export class CreateIndexPatternWizard extends Component< this.state.indexPatternCreationType.getShowSystemIndices() && this.state.step === INDEX_PATTERN_STEP } + dataSourceRef={dataSourceRef} /> ); @@ -313,6 +323,7 @@ export class CreateIndexPatternWizard extends Component< createIndexPattern={this.createIndexPattern} indexPatternCreationType={this.state.indexPatternCreationType} selectedTimeField={this.state.selectedTimeField} + dataSourceRef={dataSourceRef} /> ); diff --git a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/lib/get_indices.ts b/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/lib/get_indices.ts index 9d68b93aa7fd..bc3b4e8ad7f7 100644 --- a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/lib/get_indices.ts +++ b/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/lib/get_indices.ts @@ -80,6 +80,7 @@ export const searchResponseToArray = ( }; export const getIndicesViaSearch = async ({ + // todo: #2151 getIndexTags, pattern, searchClient, @@ -118,15 +119,19 @@ export const getIndicesViaResolve = async ({ getIndexTags, pattern, showAllIndices, + dataSourceId, }: { http: HttpStart; getIndexTags: IndexPatternCreationConfig['getIndexTags']; pattern: string; showAllIndices: boolean; -}) => - http + dataSourceId?: string; +}) => { + const query = buildQuery(showAllIndices, dataSourceId); + + return http .get(`/internal/index-pattern-management/resolve_index/${pattern}`, { - query: showAllIndices ? { expand_wildcards: 'all' } : undefined, + query, }) .then((response) => { if (!response) { @@ -135,6 +140,7 @@ export const getIndicesViaResolve = async ({ return responseToItemArray(response, getIndexTags); } }); +}; /** * Takes two MatchedItem[]s and returns a merged set, with the second set prrioritized over the first based on name @@ -168,12 +174,14 @@ export async function getIndices({ pattern: rawPattern, showAllIndices = false, searchClient, + dataSourceId, }: { http: HttpStart; getIndexTags?: IndexPatternCreationConfig['getIndexTags']; pattern: string; showAllIndices?: boolean; searchClient: DataPublicPluginStart['search']['search']; + dataSourceId?: string; }): Promise { const pattern = rawPattern.trim(); const isCCS = pattern.indexOf(':') !== -1; @@ -202,6 +210,7 @@ export async function getIndices({ getIndexTags, pattern, showAllIndices, + dataSourceId, }).catch(() => []); requests.push(promiseResolve); @@ -264,3 +273,15 @@ export const responseToItemArray = ( return sortBy(source, 'name'); }; + +const buildQuery = (showAllIndices: boolean, dataSourceId?: string) => { + const query = {} as any; + if (showAllIndices) { + query.expand_wildcards = 'all'; + } + if (dataSourceId) { + query.data_source = dataSourceId; + } + + return query; +}; diff --git a/src/plugins/index_pattern_management/public/components/index_pattern_table/index_pattern_table.tsx b/src/plugins/index_pattern_management/public/components/index_pattern_table/index_pattern_table.tsx index 9a412ec89e18..37944cbec76e 100644 --- a/src/plugins/index_pattern_management/public/components/index_pattern_table/index_pattern_table.tsx +++ b/src/plugins/index_pattern_management/public/components/index_pattern_table/index_pattern_table.tsx @@ -109,7 +109,7 @@ export const IndexPatternTable = ({ canSave, history }: Props) => { const [creationOptions, setCreationOptions] = useState([]); const [sources, setSources] = useState([]); const [remoteClustersExist, setRemoteClustersExist] = useState(false); - const [isLoadingSources, setIsLoadingSources] = useState(true); + const [isLoadingSources, setIsLoadingSources] = useState(!dataSourceEnabled); const [isLoadingIndexPatterns, setIsLoadingIndexPatterns] = useState(true); setBreadcrumbs(getListBreadcrumbs()); @@ -150,14 +150,16 @@ export const IndexPatternTable = ({ canSave, history }: Props) => { }; useEffect(() => { - getIndices({ http, pattern: '*', searchClient }).then((dataSources) => { - setSources(dataSources.filter(removeAliases)); - setIsLoadingSources(false); - }); - getIndices({ http, pattern: '*:*', searchClient }).then((dataSources) => - setRemoteClustersExist(!!dataSources.filter(removeAliases).length) - ); - }, [http, creationOptions, searchClient]); + if (!dataSourceEnabled) { + getIndices({ http, pattern: '*', searchClient }).then((dataSources) => { + setSources(dataSources.filter(removeAliases)); + setIsLoadingSources(false); + }); + getIndices({ http, pattern: '*:*', searchClient }).then((dataSources) => + setRemoteClustersExist(!!dataSources.filter(removeAliases).length) + ); + } + }, [http, creationOptions, searchClient, dataSourceEnabled]); chrome.docTitle.change(title); diff --git a/src/plugins/index_pattern_management/server/routes/resolve_index.ts b/src/plugins/index_pattern_management/server/routes/resolve_index.ts index 091039458212..f70e6a41d385 100644 --- a/src/plugins/index_pattern_management/server/routes/resolve_index.ts +++ b/src/plugins/index_pattern_management/server/routes/resolve_index.ts @@ -49,6 +49,7 @@ export function registerResolveIndexRoute(router: IRouter): void { schema.literal('none'), ]) ), + data_source: schema.maybe(schema.string()), }), }, }, @@ -56,6 +57,15 @@ export function registerResolveIndexRoute(router: IRouter): void { const queryString = req.query.expand_wildcards ? { expand_wildcards: req.query.expand_wildcards } : null; + + if (req.query.data_source) { + // const result = await (await context.data_source.opensearch.getClient(req.query.data_source)).indices.resolveIndex({ + // name: encodeURIComponent(req.params.query), + // expand_wildcards: req.query.expand_wildcards, + // }); + // return res.ok({ body: result.body }); //todo: Pending # + } + const result = await context.core.opensearch.legacy.client.callAsCurrentUser( 'transport.request', {