[ARVADOS-WORKBENCH2] updated: 1.3.0-236-g3b7eae04
Git user
git at public.curoverse.com
Sun Dec 23 05:44:56 EST 2018
Summary of changes:
src/models/search-bar.ts | 8 +--
.../common-service/common-resource-service.ts | 5 +-
src/services/groups-service/groups-service.ts | 22 ++++---
src/store/auth/auth-action-session.ts | 2 +-
src/store/search-bar/search-bar-actions.test.ts | 9 ++-
src/store/search-bar/search-bar-actions.ts | 77 ++++++++++++++--------
.../search-results-middleware-service.ts | 35 ++++++----
.../form-fields/search-bar-form-fields.tsx | 12 ++--
8 files changed, 97 insertions(+), 73 deletions(-)
via 3b7eae043b41eaebd5cd78d3a74584bcc1290ee7 (commit)
from 1b438678854a42f00e19f351caf0004c7d46ff8c (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
commit 3b7eae043b41eaebd5cd78d3a74584bcc1290ee7
Author: Daniel Kos <unodgs at gmail.com>
Date: Sun Dec 23 11:44:49 2018 +0100
Add multi cluster search
Feature #14348
Arvados-DCO-1.1-Signed-off-by: Daniel Kos <unodgs at gmail.com>
diff --git a/src/models/search-bar.ts b/src/models/search-bar.ts
index 798f9c8f..effaeed4 100644
--- a/src/models/search-bar.ts
+++ b/src/models/search-bar.ts
@@ -6,7 +6,7 @@ import { ResourceKind } from '~/models/resource';
export type SearchBarAdvanceFormData = {
type?: ResourceKind;
- cluster?: ClusterObjectType;
+ cluster?: string;
projectUuid?: string;
inTrash: boolean;
dateFrom: string;
@@ -21,9 +21,3 @@ export interface PropertyValue {
key: string;
value: string;
}
-
-export enum ClusterObjectType {
- INDIANAPOLIS = "indianapolis",
- KAISERAUGST = "kaiseraugst",
- PENZBERG = "penzberg"
-}
diff --git a/src/services/common-service/common-resource-service.ts b/src/services/common-service/common-resource-service.ts
index 471c32fa..17c287d2 100644
--- a/src/services/common-service/common-resource-service.ts
+++ b/src/services/common-service/common-resource-service.ts
@@ -2,7 +2,6 @@
//
// SPDX-License-Identifier: AGPL-3.0
-import * as _ from "lodash";
import { AxiosInstance } from "axios";
import { Resource } from "src/models/resource";
import { ApiActions } from "~/services/api/api-actions";
@@ -18,11 +17,9 @@ export enum CommonResourceServiceError {
}
export class CommonResourceService<T extends Resource> extends CommonService<T> {
-
- constructor(serverApi: AxiosInstance, resourceType: string, actions: ApiActions) {
+ constructor(serverApi: AxiosInstance, resourceType: string, actions: ApiActions) {
super(serverApi, resourceType, actions);
}
-
}
export const getCommonResourceServiceError = (errorResponse: any) => {
diff --git a/src/services/groups-service/groups-service.ts b/src/services/groups-service/groups-service.ts
index 668a0ac5..7dfd0c13 100644
--- a/src/services/groups-service/groups-service.ts
+++ b/src/services/groups-service/groups-service.ts
@@ -5,7 +5,7 @@
import * as _ from "lodash";
import { CommonResourceService } from '~/services/common-service/common-resource-service';
import { ListResults, ListArguments } from '~/services/common-service/common-service';
-import { AxiosInstance } from "axios";
+import { AxiosInstance, AxiosRequestConfig } from "axios";
import { CollectionResource } from "~/models/collection";
import { ProjectResource } from "~/models/project";
import { ProcessResource } from "~/models/process";
@@ -13,6 +13,7 @@ import { ResourceKind } from '~/models/resource';
import { TrashableResourceService } from "~/services/common-service/trashable-resource-service";
import { ApiActions } from "~/services/api/api-actions";
import { GroupResource } from "~/models/group";
+import { Session } from "~/models/session";
export interface ContentsArguments {
limit?: number;
@@ -39,7 +40,7 @@ export class GroupsService<T extends GroupResource = GroupResource> extends Tras
super(serverApi, "groups", actions);
}
- async contents(uuid: string, args: ContentsArguments = {}): Promise<ListResults<GroupContentsResource>> {
+ async contents(uuid: string, args: ContentsArguments = {}, session?: Session): Promise<ListResults<GroupContentsResource>> {
const { filters, order, ...other } = args;
const params = {
...other,
@@ -48,17 +49,18 @@ export class GroupsService<T extends GroupResource = GroupResource> extends Tras
};
const pathUrl = uuid ? `${uuid}/contents` : 'contents';
+
+ const cfg: AxiosRequestConfig = { params: CommonResourceService.mapKeys(_.snakeCase)(params) };
+ if (session) {
+ cfg.baseURL = session.baseUrl;
+ }
+
const response = await CommonResourceService.defaultResponse(
- this.serverApi
- .get(this.resourceType + pathUrl, {
- params: CommonResourceService.mapKeys(_.snakeCase)(params)
- }),
- this.actions,
- false
- );
+ this.serverApi.get(this.resourceType + pathUrl, cfg), this.actions, false
+ );
const { items, ...res } = response;
- const mappedItems = items.map((item: GroupContentsResource) => {
+ const mappedItems = (items || []).map((item: GroupContentsResource) => {
const mappedItem = TrashableResourceService.mapKeys(_.camelCase)(item);
if (item.kind === ResourceKind.COLLECTION || item.kind === ResourceKind.PROJECT) {
const { properties } = item;
diff --git a/src/store/auth/auth-action-session.ts b/src/store/auth/auth-action-session.ts
index e5e2e575..46ecc441 100644
--- a/src/store/auth/auth-action-session.ts
+++ b/src/store/auth/auth-action-session.ts
@@ -99,7 +99,7 @@ const clusterLogin = async (clusterId: string, baseUrl: string, activeSession: S
};
};
-const getActiveSession = (sessions: Session[]): Session | undefined => sessions.find(s => s.active);
+export const getActiveSession = (sessions: Session[]): Session | undefined => sessions.find(s => s.active);
export const validateCluster = async (remoteHost: string, clusterId: string, activeSession: Session): Promise<{ user: User; token: string, baseUrl: string }> => {
const baseUrl = await getRemoteHostBaseUrl(remoteHost);
diff --git a/src/store/search-bar/search-bar-actions.test.ts b/src/store/search-bar/search-bar-actions.test.ts
index aa6e4759..ea290b4d 100644
--- a/src/store/search-bar/search-bar-actions.test.ts
+++ b/src/store/search-bar/search-bar-actions.test.ts
@@ -4,7 +4,6 @@
import { getAdvancedDataFromQuery, getQueryFromAdvancedData, parseSearchQuery } from "~/store/search-bar/search-bar-actions";
import { ResourceKind } from "~/models/resource";
-import { ClusterObjectType } from "~/models/search-bar";
describe('search-bar-actions', () => {
describe('parseSearchQuery', () => {
@@ -95,11 +94,11 @@ describe('search-bar-actions', () => {
});
it('should correctly build advanced data record from query #2', () => {
- const r = getAdvancedDataFromQuery('document from:2017-08-01 pdf has:filesize:101mb is:trashed type:arvados#collection cluster:indianapolis');
+ const r = getAdvancedDataFromQuery('document from:2017-08-01 pdf has:filesize:101mb is:trashed type:arvados#collection cluster:c97qx');
expect(r).toEqual({
searchValue: 'document pdf',
type: ResourceKind.COLLECTION,
- cluster: ClusterObjectType.INDIANAPOLIS,
+ cluster: 'c97qx',
projectUuid: undefined,
inTrash: true,
dateFrom: '2017-08-01',
@@ -119,7 +118,7 @@ describe('search-bar-actions', () => {
const q = getQueryFromAdvancedData({
searchValue: 'document pdf',
type: ResourceKind.COLLECTION,
- cluster: ClusterObjectType.INDIANAPOLIS,
+ cluster: 'c97qx',
projectUuid: undefined,
inTrash: true,
dateFrom: '2017-08-01',
@@ -131,7 +130,7 @@ describe('search-bar-actions', () => {
saveQuery: false,
queryName: ''
});
- expect(q).toBe('document pdf type:arvados#collection cluster:indianapolis is:trashed from:2017-08-01 has:filesize:101mb');
+ expect(q).toBe('document pdf type:arvados#collection cluster:c97qx is:trashed from:2017-08-01 has:filesize:101mb');
});
});
});
diff --git a/src/store/search-bar/search-bar-actions.ts b/src/store/search-bar/search-bar-actions.ts
index 199ec3f9..cfa07ebc 100644
--- a/src/store/search-bar/search-bar-actions.ts
+++ b/src/store/search-bar/search-bar-actions.ts
@@ -15,11 +15,12 @@ import { GroupClass } from '~/models/group';
import { SearchView } from '~/store/search-bar/search-bar-reducer';
import { navigateTo, navigateToSearchResults } from '~/store/navigation/navigation-action';
import { snackbarActions, SnackbarKind } from '~/store/snackbar/snackbar-actions';
-import { ClusterObjectType, PropertyValue, SearchBarAdvanceFormData } from '~/models/search-bar';
+import { PropertyValue, SearchBarAdvanceFormData } from '~/models/search-bar';
import { debounce } from 'debounce';
import * as _ from "lodash";
import { getModifiedKeysValues } from "~/common/objects";
import { activateSearchBarProject } from "~/store/search-bar/search-bar-tree-actions";
+import { Session } from "~/models/session";
export const searchBarActions = unionize({
SET_CURRENT_VIEW: ofType<string>(),
@@ -205,13 +206,18 @@ const searchGroups = (searchValue: string, limit: number) =>
const currentView = getState().searchBar.currentView;
if (searchValue || currentView === SearchView.ADVANCED) {
- const filters = getFilters('name', searchValue);
- const { items } = await services.groupsService.contents('', {
- filters,
- limit,
- recursive: true
+ const sq = parseSearchQuery(searchValue);
+ const clusterId = getSearchQueryFirstProp(sq, 'cluster');
+ const sessions = getSearchSessions(clusterId, getState().auth.sessions);
+ sessions.forEach(async session => {
+ const filters = getFilters('name', searchValue, sq);
+ const { items } = await services.groupsService.contents('', {
+ filters,
+ limit,
+ recursive: true
+ }, session);
+ dispatch(searchBarActions.SET_SEARCH_RESULTS(items));
});
- dispatch(searchBarActions.SET_SEARCH_RESULTS(items));
}
};
@@ -288,7 +294,7 @@ export const getQueryFromAdvancedData = (data: SearchBarAdvanceFormData, prevDat
return value;
};
-export interface ParseSearchQuery {
+export class ParseSearchQuery {
hasKeywords: boolean;
values: string[];
properties: {
@@ -351,9 +357,9 @@ export const parseSearchQuery: (query: string) => ParseSearchQuery = (searchValu
return { hasKeywords: keywordsCnt > 0, values, properties };
};
-const getFirstProp = (sq: ParseSearchQuery, name: string) => sq.properties[name] && sq.properties[name][0];
-const getPropValue = (sq: ParseSearchQuery, name: string, value: string) => sq.properties[name] && sq.properties[name].find((v: string) => v === value);
-const getProperties = (sq: ParseSearchQuery): PropertyValue[] => {
+export const getSearchQueryFirstProp = (sq: ParseSearchQuery, name: string) => sq.properties[name] && sq.properties[name][0];
+export const getSearchQueryPropValue = (sq: ParseSearchQuery, name: string, value: string) => sq.properties[name] && sq.properties[name].find((v: string) => v === value);
+export const getSearchQueryProperties = (sq: ParseSearchQuery): PropertyValue[] => {
if (sq.properties.has) {
return sq.properties.has.map((value: string) => {
const v = value.split(':');
@@ -371,23 +377,26 @@ export const getAdvancedDataFromQuery = (query: string): SearchBarAdvanceFormDat
return {
searchValue: sq.values.join(' '),
- type: getFirstProp(sq, 'type') as ResourceKind,
- cluster: getFirstProp(sq, 'cluster') as ClusterObjectType,
- projectUuid: getFirstProp(sq, 'project'),
- inTrash: getPropValue(sq, 'is', 'trashed') !== undefined,
- dateFrom: getFirstProp(sq, 'from'),
- dateTo: getFirstProp(sq, 'to'),
- properties: getProperties(sq),
+ type: getSearchQueryFirstProp(sq, 'type') as ResourceKind,
+ cluster: getSearchQueryFirstProp(sq, 'cluster'),
+ projectUuid: getSearchQueryFirstProp(sq, 'project'),
+ inTrash: getSearchQueryPropValue(sq, 'is', 'trashed') !== undefined,
+ dateFrom: getSearchQueryFirstProp(sq, 'from'),
+ dateTo: getSearchQueryFirstProp(sq, 'to'),
+ properties: getSearchQueryProperties(sq),
saveQuery: false,
queryName: ''
};
};
-export const getFilters = (filterName: string, searchValue: string): string => {
+export const getSearchSessions = (clusterId: string | undefined, sessions: Session[]): Session[] => {
+ return sessions.filter(s => s.loggedIn && (!clusterId || s.clusterId === clusterId));
+};
+
+export const getFilters = (filterName: string, searchValue: string, sq: ParseSearchQuery): string => {
const filter = new FilterBuilder();
- const sq = parseSearchQuery(searchValue);
- const resourceKind = getFirstProp(sq, 'type') as ResourceKind;
+ const resourceKind = getSearchQueryFirstProp(sq, 'type') as ResourceKind;
let prefix = '';
switch (resourceKind) {
@@ -402,11 +411,16 @@ export const getFilters = (filterName: string, searchValue: string): string => {
break;
}
+ const isTrashed = getSearchQueryPropValue(sq, 'is', 'trashed');
+
if (!sq.hasKeywords) {
filter
.addILike(filterName, searchValue, GroupContentsResourcePrefix.COLLECTION)
- .addILike(filterName, searchValue, GroupContentsResourcePrefix.PROCESS)
.addILike(filterName, searchValue, GroupContentsResourcePrefix.PROJECT);
+
+ if (isTrashed) {
+ filter.addILike(filterName, searchValue, GroupContentsResourcePrefix.PROCESS);
+ }
} else {
if (prefix) {
sq.values.forEach(v =>
@@ -416,33 +430,38 @@ export const getFilters = (filterName: string, searchValue: string): string => {
sq.values.forEach(v => {
filter
.addILike(filterName, v, GroupContentsResourcePrefix.COLLECTION)
- .addILike(filterName, v, GroupContentsResourcePrefix.PROCESS)
.addILike(filterName, v, GroupContentsResourcePrefix.PROJECT);
+
+ if (isTrashed) {
+ filter.addILike(filterName, v, GroupContentsResourcePrefix.PROCESS);
+ }
});
}
- if (getPropValue(sq, 'is', 'trashed')) {
+ if (isTrashed) {
filter.addEqual("is_trashed", true);
}
- const projectUuid = getFirstProp(sq, 'project');
+ const projectUuid = getSearchQueryFirstProp(sq, 'project');
if (projectUuid) {
filter.addEqual('uuid', projectUuid, prefix);
}
- const dateFrom = getFirstProp(sq, 'from');
+ const dateFrom = getSearchQueryFirstProp(sq, 'from');
if (dateFrom) {
filter.addGte('modified_at', buildDateFilter(dateFrom));
}
- const dateTo = getFirstProp(sq, 'to');
+ const dateTo = getSearchQueryFirstProp(sq, 'to');
if (dateTo) {
filter.addLte('modified_at', buildDateFilter(dateTo));
}
- const props = getProperties(sq);
+ const props = getSearchQueryProperties(sq);
props.forEach(p => {
- // filter.addILike(`properties.${p.key}`, p.value);
+ if (p.value) {
+ filter.addILike(`properties.${p.key}`, p.value);
+ }
filter.addExists(p.key);
});
}
diff --git a/src/store/search-results-panel/search-results-middleware-service.ts b/src/store/search-results-panel/search-results-middleware-service.ts
index aa09838e..5e806e71 100644
--- a/src/store/search-results-panel/search-results-middleware-service.ts
+++ b/src/store/search-results-panel/search-results-middleware-service.ts
@@ -15,8 +15,14 @@ import { OrderDirection, OrderBuilder } from '~/services/api/order-builder';
import { GroupContentsResource, GroupContentsResourcePrefix } from "~/services/groups-service/groups-service";
import { ListResults } from '~/services/common-service/common-service';
import { searchResultsPanelActions } from '~/store/search-results-panel/search-results-panel-actions';
-import { getFilters } from '~/store/search-bar/search-bar-actions';
+import {
+ getFilters,
+ getSearchQueryFirstProp,
+ getSearchSessions, ParseSearchQuery,
+ parseSearchQuery
+} from '~/store/search-bar/search-bar-actions';
import { getSortColumn } from "~/store/data-explorer/data-explorer-reducer";
+import { Session } from "~/models/session";
export class SearchResultsMiddlewareService extends DataExplorerMiddlewareService {
constructor(private services: ServiceRepository, id: string) {
@@ -28,19 +34,24 @@ export class SearchResultsMiddlewareService extends DataExplorerMiddlewareServic
const userUuid = state.auth.user!.uuid;
const dataExplorer = getDataExplorer(state.dataExplorer, this.getId());
const searchValue = state.searchBar.searchValue;
- try {
- const response = await this.services.groupsService.contents('', getParams(dataExplorer, searchValue));
- api.dispatch(updateResources(response.items));
- api.dispatch(setItems(response));
- } catch {
- api.dispatch(couldNotFetchSearchResults());
- }
+ const sq = parseSearchQuery(searchValue);
+ const clusterId = getSearchQueryFirstProp(sq, 'cluster');
+ const sessions = getSearchSessions(clusterId, state.auth.sessions);
+ sessions.forEach(async session => {
+ try {
+ const response = await this.services.groupsService.contents(userUuid, getParams(dataExplorer, searchValue, sq), session);
+ api.dispatch(updateResources(response.items));
+ api.dispatch(setItems(response));
+ } catch {
+ api.dispatch(couldNotFetchSearchResults(session));
+ }
+ });
}
}
-export const getParams = (dataExplorer: DataExplorer, searchValue: string) => ({
+export const getParams = (dataExplorer: DataExplorer, searchValue: string, sq: ParseSearchQuery) => ({
...dataExplorerToListParams(dataExplorer),
- filters: getFilters('name', searchValue),
+ filters: getFilters('name', searchValue, sq),
order: getOrder(dataExplorer)
});
@@ -69,8 +80,8 @@ export const setItems = (listResults: ListResults<GroupContentsResource>) =>
items: listResults.items.map(resource => resource.uuid),
});
-const couldNotFetchSearchResults = () =>
+const couldNotFetchSearchResults = (session: Session) =>
snackbarActions.OPEN_SNACKBAR({
- message: 'Could not fetch search results.',
+ message: `Could not fetch search results for ${session.clusterId}.`,
kind: SnackbarKind.ERROR
});
diff --git a/src/views-components/form-fields/search-bar-form-fields.tsx b/src/views-components/form-fields/search-bar-form-fields.tsx
index 6fb23498..8de48ea7 100644
--- a/src/views-components/form-fields/search-bar-form-fields.tsx
+++ b/src/views-components/form-fields/search-bar-form-fields.tsx
@@ -8,7 +8,6 @@ import { TextField, DateTextField } from "~/components/text-field/text-field";
import { CheckboxField } from '~/components/checkbox-field/checkbox-field';
import { NativeSelectField } from '~/components/select-field/select-field';
import { ResourceKind } from '~/models/resource';
-import { ClusterObjectType } from '~/models/search-bar';
import { HomeTreePicker } from '~/views-components/projects-tree-picker/home-tree-picker';
import { SEARCH_BAR_ADVANCE_FORM_PICKER_ID } from '~/store/search-bar/search-bar-actions';
import { SearchBarAdvancedPropertiesView } from '~/views-components/search-bar/search-bar-advanced-properties-view';
@@ -39,10 +38,13 @@ interface SearchBarClusterFieldProps {
export const SearchBarClusterField = connect(
(state: RootState) => ({
- clusters: [{key: '', value: 'Any'}].concat(state.auth.sessions.map(s => ({
- key: s.clusterId,
- value: s.clusterId
- })))
+ clusters: [{key: '', value: 'Any'}].concat(
+ state.auth.sessions
+ .filter(s => s.loggedIn)
+ .map(s => ({
+ key: s.clusterId,
+ value: s.clusterId
+ })))
}))((props: SearchBarClusterFieldProps) => <Field
name='cluster'
component={NativeSelectField}
-----------------------------------------------------------------------
hooks/post-receive
--
More information about the arvados-commits
mailing list