});
});
+ it('creates collection from selected files of another collection', () => {
+ cy.createCollection(adminUser.token, {
+ name: `Test Collection ${Math.floor(Math.random() * 999999)}`,
+ owner_uuid: activeUser.user.uuid,
+ preserve_version: true,
+ manifest_text: ". 37b51d194a7513e45b56f6524f2d51f2+3 0:3:foo 0:3:bar\n"
+ })
+ .as('collection').then(function () {
+ // Visit collection, check basic information
+ cy.loginAs(activeUser)
+ cy.goToPath(`/collections/${this.collection.uuid}`);
+
+ cy.get('[data-cy=collection-files-panel]').within(() => {
+ cy.get('input[type=checkbox]').first().click();
+ });
+
+ cy.get('[data-cy=collection-files-panel-options-btn]').click();
+ cy.get('[data-cy=context-menu]').contains('Create a new collection with selected').click();
+
+ cy.get('[data-cy=form-dialog]').contains('Projects').click();
+
+ cy.get('[data-cy=form-submit-btn]').click();
+
+ cy.get('.layout-pane-primary', { wait: 12000 }).contains('Projects').click();
+
+ cy.get('main').contains(`Files extracted from: ${this.collection.name}`).should('exist');
+ });
+ });
+
it('creates new collection with properties on home project', function () {
cy.loginAs(activeUser);
cy.goToPath(`/projects/${activeUser.user.uuid}`);
cy.getAll('@mySharedWritableProject')
.then(function ([mySharedWritableProject]) {
cy.loginAs(activeUser);
-
+
cy.get('[data-cy=side-panel-tree]').contains('Projects').click();
const newProjectName = `New project name ${mySharedWritableProject.name}`;
addToFavorites: false
});
- cy.getAll('@writableProject')
- .then(function ([writableProject]) {
- cy.loginAs(activeUser);
-
- cy.get('[data-cy=side-panel-tree]').contains('Projects').click();
-
- cy.get('[data-cy=side-panel-tree]').contains(writableProject.name).should('exist');
-
- cy.trashGroup(activeUser.token, writableProject.uuid);
-
+ cy.getAll('@writableProject').then(function ([writableProject]) {
+ cy.loginAs(activeUser);
+ cy.get('[data-cy=side-panel-tree]')
+ .contains('Projects').click();
+ cy.get('[data-cy=side-panel-tree]')
+ .contains(writableProject.name).should('exist');
+ cy.trashGroup(activeUser.token, writableProject.uuid).then(() => {
cy.contains('Refresh').click();
-
cy.contains(writableProject.name).should('not.exist');
});
+ });
});
})
return "v" + process.env.REACT_APP_VERSION;
} else {
const getBuildNumber = "BN-" + (process.env.REACT_APP_BUILD_NUMBER || "dev");
- const getGitCommit = "GIT-" + (process.env.REACT_APP_GIT_COMMIT || "latest").substr(0, 7);
+ const getGitCommit = "GIT-" + (process.env.REACT_APP_GIT_COMMIT || "latest").substring(0, 7);
return getBuildNumber + " / " + getGitCommit;
}
};
const u = new URL(url);
u.pathname = u.pathname.replace(/\/\//, '/');
if (u.pathname[u.pathname.length - 1] === '/') {
- u.pathname = u.pathname.substr(0, u.pathname.length - 1);
+ u.pathname = u.pathname.substring(0, u.pathname.length - 1);
}
return u.toString();
}
];
const dataTable = mount(<DataTable
columns={columns}
+ working={false}
items={["item 1"]}
onFiltersChange={jest.fn()}
onRowClick={jest.fn()}
];
const dataTable = mount(<DataTable
columns={columns}
+ working={false}
items={["item 1"]}
onFiltersChange={jest.fn()}
onRowClick={jest.fn()}
</TableRow>
</TableHead>
<TableBody className={classes.tableBody}>
- {
- this.props.working ?
- <div className={classes.loader}>
- <DataTableDefaultView
- icon={PendingIcon}
- messages={['Loading data, please wait.']} />
- </div> : items.map(this.renderBodyRow)
- }
+ { this.props.working !== undefined && !this.props.working && items.map(this.renderBodyRow) }
</TableBody>
</Table>
+ { this.props.working &&
+ <div className={classes.loader}>
+ <DataTableDefaultView
+ icon={PendingIcon}
+ messages={['Loading data, please wait.']} />
+ </div> }
{items.length === 0 && this.props.working !== undefined && !this.props.working && this.renderNoItemsPlaceholder()}
</div>
</div>;
export const getNavUrl = (uuid: string, config: FederationConfig) => {
const path = getResourceUrl(uuid) || "";
- const cls = uuid.substr(0, 5);
+ const cls = uuid.substring(0, 5);
if (cls === config.localCluster || extractUuidKind(uuid) === ResourceKind.USER || COLLECTION_PDH_REGEX.exec(uuid)) {
return path;
} else if (config.remoteHostsConfig[cls]) {
if (url0) {
let idx0 = url0.length - 1;
while (url0[idx0] === '/') { --idx0; }
- u0 = url0.substr(0, idx0 + 1);
+ u0 = url0.substring(0, idx0 + 1);
}
let u1 = "";
if (url1) {
let idx1 = 0;
while (url1[idx1] === '/') { ++idx1; }
- u1 = url1.substr(idx1);
+ u1 = url1.substring(idx1);
}
let url = u0;
if (u1.length > 0) {
this.getStorage().setItem(API_TOKEN_KEY, token);
const sp = token.split('/');
if (sp.length === 3) {
- this.getStorage().setItem(HOME_CLUSTER, sp[1].substr(0, 5));
+ this.getStorage().setItem(HOME_CLUSTER, sp[1].substring(0, 5));
}
}
throw new Error(invalidV2Token);
}
let salted = secret;
- if (uuid.substr(0, 5) !== clusterId) {
+ if (uuid.substring(0, 5) !== clusterId) {
shaObj.setHMACKey(secret, "TEXT");
shaObj.update(clusterId);
salted = shaObj.getHMAC("HEX");
const auth = getState().auth;
config = dispatch<any>(getConfig);
- // If federated token, get user & token data from the token issuing cluster
- if (tokenParts.length === 3 && tokenParts[1].substring(0, 5) !== auth.localCluster) {
- config = await getRemoteHostConfig(auth.remoteHosts[tokenParts[1].substring(0, 5)]);
+ // If the token is from a LoginCluster federation, get user & token data
+ // from the token issuing cluster.
+ const lc = (config as Config).loginCluster
+ const tokenCluster = tokenParts.length === 3
+ ? tokenParts[1].substring(0, 5)
+ : undefined;
+ if (tokenCluster && tokenCluster !== auth.localCluster &&
+ lc && lc === tokenCluster) {
+ config = await getRemoteHostConfig(auth.remoteHosts[tokenCluster]);
}
const svc = createServices(config, { progressFn: () => { }, errorFn: () => { } });
apiToken: token,
apiTokenExpiration: tokenExpiration,
apiTokenLocation: tokenLocation,
- homeCluster: user.uuid.substr(0, 5)
+ homeCluster: user.uuid.substring(0, 5)
}),
LOGIN: () => state,
LOGOUT: () => ({ ...state, apiToken: undefined }),
USER_DETAILS_SUCCESS: (user: User) =>
- ({ ...state, user, homeCluster: user.uuid.substr(0, 5) }),
+ ({ ...state, user, homeCluster: user.uuid.substring(0, 5) }),
SET_SSH_KEYS: (sshKeys: SshKeyResource[]) => ({ ...state, sshKeys }),
ADD_SSH_KEY: (sshKey: SshKeyResource) =>
({ ...state, sshKeys: state.sshKeys.concat(sshKey) }),
import { snackbarActions, SnackbarKind } from 'store/snackbar/snackbar-actions';
import { getCommonResourceServiceError, CommonResourceServiceError } from 'services/common-service/common-resource-service';
import { progressIndicatorActions } from "store/progress-indicator/progress-indicator-actions";
-import { initProjectsTreePicker } from 'store/tree-picker/tree-picker-actions';
+import { initProjectsTreePicker } from "store/tree-picker/tree-picker-actions";
export const COLLECTION_PARTIAL_COPY_FORM_NAME = 'COLLECTION_PARTIAL_COPY_DIALOG';
export const COLLECTION_PARTIAL_COPY_TO_SELECTED_COLLECTION = 'COLLECTION_PARTIAL_COPY_TO_SELECTED_DIALOG';
const currentCollection = getState().collectionPanel.item;
if (currentCollection) {
const initialData = {
- name: currentCollection.name,
+ name: `Files extracted from: ${currentCollection.name}`,
description: currentCollection.description,
- projectUuid: ''
+ projectUuid: undefined
};
dispatch(initialize(COLLECTION_PARTIAL_COPY_FORM_NAME, initialData));
dispatch<any>(resetPickerProjectTree());
try {
api.dispatch(progressIndicatorActions.START_WORKING(this.getId()));
const response = await this.services.groupsService.contents(projectUuid, getParams(dataExplorer, !!isProjectTrashed));
- api.dispatch(progressIndicatorActions.PERSIST_STOP_WORKING(this.getId()));
const resourceUuids = response.items.map(item => item.uuid);
api.dispatch<any>(updateFavorites(resourceUuids));
api.dispatch<any>(updatePublicFavorites(resourceUuids));
async (dispatch: Dispatch<any>, getState: () => RootState, services: ServiceRepository) => {
const userUuid = getUserUuid(getState());
dispatch(setIsProjectPanelTrashed(false));
- if (userUuid) {
- if (extractUuidKind(uuid) === ResourceKind.USER && userUuid !== uuid) {
- // Load another users home projects
- dispatch(finishLoadingProject(uuid));
- } else if (userUuid !== uuid) {
- const match = await loadGroupContentsResource({ uuid, userUuid, services });
- match({
- OWNED: async project => {
- await dispatch(activateSidePanelTreeItem(uuid));
- dispatch<any>(setSidePanelBreadcrumbs(uuid));
- dispatch(finishLoadingProject(project));
- },
- SHARED: project => {
- dispatch<any>(setSharedWithMeBreadcrumbs(uuid));
- dispatch(activateSidePanelTreeItem(uuid));
- dispatch(finishLoadingProject(project));
- },
- TRASHED: project => {
- dispatch<any>(setTrashBreadcrumbs(uuid));
- dispatch(setIsProjectPanelTrashed(true));
- dispatch(activateSidePanelTreeItem(SidePanelTreeCategory.TRASH));
- dispatch(finishLoadingProject(project));
- }
- });
- } else {
- await dispatch(activateSidePanelTreeItem(userUuid));
- dispatch<any>(setSidePanelBreadcrumbs(userUuid));
- dispatch(finishLoadingProject(userUuid));
- }
+ if (!userUuid) {
+ return;
+ }
+ if (extractUuidKind(uuid) === ResourceKind.USER && userUuid !== uuid) {
+ // Load another users home projects
+ dispatch(finishLoadingProject(uuid));
+ } else if (userUuid !== uuid) {
+ await dispatch(finishLoadingProject(uuid));
+ const match = await loadGroupContentsResource({ uuid, userUuid, services });
+ match({
+ OWNED: async () => {
+ await dispatch(activateSidePanelTreeItem(uuid));
+ dispatch<any>(setSidePanelBreadcrumbs(uuid));
+ },
+ SHARED: async () => {
+ await dispatch(activateSidePanelTreeItem(uuid));
+ dispatch<any>(setSharedWithMeBreadcrumbs(uuid));
+ },
+ TRASHED: async () => {
+ await dispatch(activateSidePanelTreeItem(SidePanelTreeCategory.TRASH));
+ dispatch<any>(setTrashBreadcrumbs(uuid));
+ dispatch(setIsProjectPanelTrashed(true));
+ }
+ });
+ } else {
+ await dispatch(finishLoadingProject(userUuid));
+ await dispatch(activateSidePanelTreeItem(userUuid));
+ dispatch<any>(setSidePanelBreadcrumbs(userUuid));
}
});
export const ResourceCluster = (props: { uuid: string }) => {
const CLUSTER_ID_LENGTH = 5;
const pos = props.uuid.length > CLUSTER_ID_LENGTH ? props.uuid.indexOf('-') : 5;
- const clusterId = pos >= CLUSTER_ID_LENGTH ? props.uuid.substr(0, pos) : '';
+ const clusterId = pos >= CLUSTER_ID_LENGTH ? props.uuid.substring(0, pos) : '';
const ci = pos >= CLUSTER_ID_LENGTH ? (((((
(props.uuid.charCodeAt(0) * props.uuid.charCodeAt(1))
+ props.uuid.charCodeAt(2))
title="Account Management"
key={currentRoute}>
<MenuItem disabled>
- {getUserDisplayName(user)} {user.uuid.substr(0, 5) !== localCluster && `(${user.uuid.substr(0, 5)})`}
+ {getUserDisplayName(user)} {user.uuid.substring(0, 5) !== localCluster && `(${user.uuid.substring(0, 5)})`}
</MenuItem>
{user.isActive && accountMenuItems}
<Divider />
import * as Routes from 'routes/routes';
import { toggleDetailsPanel } from 'store/details-panel/details-panel-action';
import RefreshButton from "components/refresh-button/refresh-button";
-import { reloadProjectMatchingUuid } from "store/workbench/workbench-actions";
import { loadSidePanelTreeProjects } from "store/side-panel-tree/side-panel-tree-actions";
type CssRules = "infoTooltip";
onDetailsPanelToggle: () => dispatch<any>(toggleDetailsPanel()),
onRefreshButtonClick: (id) => {
dispatch<any>(loadSidePanelTreeProjects(id));
- dispatch<any>(reloadProjectMatchingUuid([id]));
}
}))(
withStyles(styles)(
import {
ProcessStatus,
ResourceName,
- ResourceOwner,
+ ResourceOwnerWithName,
ResourceType,
ContainerRunTime,
ResourceCreatedAtDate
selected: true,
configurable: true,
filters: createTree(),
- render: uuid => <ResourceOwner uuid={uuid} />
+ render: uuid => <ResourceOwnerWithName uuid={uuid} />
},
{
name: AllProcessesPanelColumnNames.CREATED_AT,
ResourceFileSize,
ResourceLastModifiedDate,
ResourceName,
- ResourceOwner,
+ ResourceOwnerWithName,
ResourceType
} from 'views-components/data-explorer/renderers';
import { FavoriteIcon } from 'components/icon/icon';
selected: false,
configurable: true,
filters: createTree(),
- render: uuid => <ResourceOwner uuid={uuid} />
+ render: uuid => <ResourceOwnerWithName uuid={uuid} />
},
{
name: FavoritePanelColumnNames.FILE_SIZE,
const disp: JSX.Element[] = [];
disp.push(<span><b>{user.email}</b> ({user.username}, {user.uuid})</span>);
if (showCluster) {
- const homeCluster = user.uuid.substr(0, 5);
+ const homeCluster = user.uuid.substring(0, 5);
disp.push(<span> hosted on cluster <b>{homeCluster}</b> and </span>);
}
if (showCreatedAt) {
This a remote account. You can link a local Arvados account to this one.
After linking, you can access the local account's data by logging into the
<b>{localCluster}</b> cluster as user <b>{targetUser.email}</b>
- from <b>{targetUser.uuid.substr(0, 5)}</b>.
+ from <b>{targetUser.uuid.substring(0, 5)}</b>.
</Grid >
<Grid item>
<Button color="primary" variant="contained" onClick={() => startLinking(LinkAccountType.ADD_LOCAL_TO_REMOTE)}>
type LocalClusterProp = { localCluster: string };
const renderField: React.ComponentType<WrappedFieldProps & LocalClusterProp> = ({ input, localCluster }) => (
- <span>{localCluster === input.value.substr(0, 5) ? "" : "federated"} user {input.value}</span>
+ <span>{localCluster === input.value.substring(0, 5) ? "" : "federated"} user {input.value}</span>
);
export const MyAccountPanelRoot = withStyles(styles)(
ResourceLastModifiedDate,
ProcessStatus,
ResourceType,
- ResourceOwner
+ ResourceOwnerWithName
} from 'views-components/data-explorer/renderers';
import { ProjectIcon } from 'components/icon/icon';
import { ResourceName } from 'views-components/data-explorer/renderers';
selected: false,
configurable: true,
filters: createTree(),
- render: uuid => <ResourceOwner uuid={uuid} />
+ render: uuid => <ResourceOwnerWithName uuid={uuid} />
},
{
name: ProjectPanelColumnNames.FILE_SIZE,
ResourceLastModifiedDate,
ResourceType,
ResourceName,
- ResourceOwner
+ ResourceOwnerWithName
} from 'views-components/data-explorer/renderers';
import { PublicFavoriteIcon } from 'components/icon/icon';
import { Dispatch } from 'redux';
selected: false,
configurable: true,
filters: createTree(),
- render: uuid => <ResourceOwner uuid={uuid} />
+ render: uuid => <ResourceOwnerWithName uuid={uuid} />
},
{
name: PublicFavoritePanelColumnNames.FILE_SIZE,
ResourceFileSize,
ResourceLastModifiedDate,
ResourceName,
- ResourceOwner,
+ ResourceOwnerWithName,
ResourceStatus,
ResourceType
} from 'views-components/data-explorer/renderers';
selected: true,
configurable: true,
filters: createTree(),
- render: uuid => <ResourceOwner uuid={uuid} />
+ render: uuid => <ResourceOwnerWithName uuid={uuid} />
},
{
name: SearchResultsPanelColumnNames.FILE_SIZE,
export const SearchResultsPanelView = withStyles(styles, { withTheme: true })(
(props: SearchResultsPanelProps & WithStyles<CssRules, true>) => {
- const homeCluster = props.user.uuid.substr(0, 5);
+ const homeCluster = props.user.uuid.substring(0, 5);
const loggedIn = props.sessions.filter((ss) => ss.loggedIn && ss.userIsActive);
return <span data-cy='search-results'><DataExplorer
id={SEARCH_RESULTS_PANEL_ID}
~/go/bin/arvados-server install -type test || exit 1
echo "Launching arvados in test mode..."
-VOC_DIR=$(mktemp -d | cut -d \/ -f3) # Removes the /tmp/ part
-cp ${VOCABULARY_CONF} /tmp/${VOC_DIR}/voc.json
-sed -i "s/VocabularyPath: \".*\"/VocabularyPath: \"\/tmp\/${VOC_DIR}\/voc.json\"/" ${ARVADOS_CONF}
+TMPSUBDIR=$(mktemp -d -p /tmp | cut -d \/ -f3) # Removes the /tmp/ part for the regex below
+TMPDIR=/tmp/${TMPSUBDIR}
+cp ${VOCABULARY_CONF} ${TMPDIR}/voc.json
+cp ${ARVADOS_CONF} ${TMPDIR}/arvados.yml
+sed -i "s/VocabularyPath: \".*\"/VocabularyPath: \"\/tmp\/${TMPSUBDIR}\/voc.json\"/" ${TMPDIR}/arvados.yml
coproc arvboot (~/go/bin/arvados-server boot \
-type test \
- -config ${ARVADOS_CONF} \
+ -config ${TMPDIR}/arvados.yml \
-no-workbench1 \
-own-temporary-database \
-timeout 20m 2> ${ARVADOS_LOG})