From b34302b164fd262ffa8c867b8faad162777d770f Mon Sep 17 00:00:00 2001 From: pkupczyk <piotr.kupczyk@id.ethz.ch> Date: Tue, 9 Aug 2022 13:15:07 +0200 Subject: [PATCH] SSDM-12090 : Personal Access Tokens: Admin UI Tool - first version of the view with "session name" field only --- openbis_ng_ui/src/js/common/consts/ids.js | 4 +- openbis_ng_ui/src/js/common/messages.js | 8 + .../src/js/components/tools/Tools.jsx | 2 +- .../form/pat/PersonalAccessTokenForm.jsx | 133 +++++++++++++++- .../pat/PersonalAccessTokenFormButtons.jsx | 57 +++++++ .../pat/PersonalAccessTokenFormController.js | 88 +++++++++++ .../PersonalAccessTokenFormControllerAdd.js | 41 +++++ ...PersonalAccessTokenFormControllerChange.js | 30 ++++ .../PersonalAccessTokenFormControllerLoad.js | 65 ++++++++ ...PersonalAccessTokenFormControllerRemove.js | 29 ++++ .../PersonalAccessTokenFormControllerSave.js | 74 +++++++++ ...ccessTokenFormControllerSelectionChange.js | 30 ++++ ...rsonalAccessTokenFormControllerValidate.js | 46 ++++++ .../form/pat/PersonalAccessTokenFormFacade.js | 18 +++ .../pat/PersonalAccessTokenFormParameters.jsx | 149 ++++++++++++++++++ openbis_ng_ui/src/js/services/openbis/api.js | 4 + openbis_ng_ui/src/js/services/openbis/dto.js | 8 + 17 files changed, 783 insertions(+), 3 deletions(-) create mode 100644 openbis_ng_ui/src/js/components/tools/form/pat/PersonalAccessTokenFormButtons.jsx create mode 100644 openbis_ng_ui/src/js/components/tools/form/pat/PersonalAccessTokenFormController.js create mode 100644 openbis_ng_ui/src/js/components/tools/form/pat/PersonalAccessTokenFormControllerAdd.js create mode 100644 openbis_ng_ui/src/js/components/tools/form/pat/PersonalAccessTokenFormControllerChange.js create mode 100644 openbis_ng_ui/src/js/components/tools/form/pat/PersonalAccessTokenFormControllerLoad.js create mode 100644 openbis_ng_ui/src/js/components/tools/form/pat/PersonalAccessTokenFormControllerRemove.js create mode 100644 openbis_ng_ui/src/js/components/tools/form/pat/PersonalAccessTokenFormControllerSave.js create mode 100644 openbis_ng_ui/src/js/components/tools/form/pat/PersonalAccessTokenFormControllerSelectionChange.js create mode 100644 openbis_ng_ui/src/js/components/tools/form/pat/PersonalAccessTokenFormControllerValidate.js create mode 100644 openbis_ng_ui/src/js/components/tools/form/pat/PersonalAccessTokenFormFacade.js create mode 100644 openbis_ng_ui/src/js/components/tools/form/pat/PersonalAccessTokenFormParameters.jsx diff --git a/openbis_ng_ui/src/js/common/consts/ids.js b/openbis_ng_ui/src/js/common/consts/ids.js index 8bbfa70cf03..3e38633948d 100644 --- a/openbis_ng_ui/src/js/common/consts/ids.js +++ b/openbis_ng_ui/src/js/common/consts/ids.js @@ -19,6 +19,7 @@ const ENTITY_VALIDATION_PLUGINS_GRID_ID = 'entity_validation_plugins_grid' const QUERIES_GRID_ID = 'queries_grid' const HISTORY_OF_DELETION_GRID_ID = 'history_of_deletion_grid' const HISTORY_OF_FREEZING_GRID_ID = 'history_of_freezing_grid' +const PERSONAL_ACCESS_TOKEN_GRID_ID = 'personal_access_token_grid' export default { WEB_APP_ID, @@ -41,5 +42,6 @@ export default { ENTITY_VALIDATION_PLUGINS_GRID_ID, QUERIES_GRID_ID, HISTORY_OF_DELETION_GRID_ID, - HISTORY_OF_FREEZING_GRID_ID + HISTORY_OF_FREEZING_GRID_ID, + PERSONAL_ACCESS_TOKEN_GRID_ID } diff --git a/openbis_ng_ui/src/js/common/messages.js b/openbis_ng_ui/src/js/common/messages.js index 22f4c57b821..d34bded6f16 100644 --- a/openbis_ng_ui/src/js/common/messages.js +++ b/openbis_ng_ui/src/js/common/messages.js @@ -10,6 +10,7 @@ const keys = { ADD_ROLE: 'ADD_ROLE', ADD_SECTION: 'ADD_SECTION', ADD_TERM: 'ADD_TERM', + ADD_TOKEN: 'ADD_TOKEN', ADD_USER: 'ADD_USER', ALL: 'ALL', ALL_COLUMNS: 'ALL_COLUMNS', @@ -143,6 +144,7 @@ const keys = { PARAMETERS: 'PARAMETERS', PARENTS: 'PARENTS', PASSWORD: 'PASSWORD', + PERSONAL_ACCESS_TOKEN: 'PERSONAL_ACCESS_TOKEN', PERSONAL_ACCESS_TOKENS: 'PERSONAL_ACCESS_TOKENS', PLAIN_TEXT: 'PLAIN_TEXT', PLUGIN: 'PLUGIN', @@ -172,6 +174,7 @@ const keys = { REGISTRATOR: 'REGISTRATOR', REMOVE: 'REMOVE', REMOVE_TERM: 'REMOVE_TERM', + REMOVE_TOKEN: 'REMOVE_TOKEN', RESULT: 'RESULT', RESULTS: 'RESULTS', RESULTS_RANGE: 'RESULTS_RANGE', @@ -193,6 +196,7 @@ const keys = { SECTION_IS_USED: 'SECTION_IS_USED', SELECTED_ROWS: 'SELECTED_ROWS', SELECTED_ROWS_NOT_VISIBLE_DUE_TO_FILTERING_AND_PAGING: 'SELECTED_ROWS_NOT_VISIBLE_DUE_TO_FILTERING_AND_PAGING', + SESSION_NAME: 'SESSION_NAME', SHOW: 'SHOW', SHOW_CONTAINER: 'SHOW_CONTAINER', SHOW_DETAILS: 'SHOW_DETAILS', @@ -248,6 +252,7 @@ const messages_en = { [keys.ADD_ROLE]: 'Add Role', [keys.ADD_SECTION]: 'Add Section', [keys.ADD_TERM]: 'Add Term', + [keys.ADD_TOKEN]: 'Add Token', [keys.ADD_USER]: 'Add User', [keys.ALL]: 'All', [keys.ALL_COLUMNS]: 'All Columns', @@ -381,6 +386,7 @@ const messages_en = { [keys.PARAMETERS]: 'Parameters', [keys.PARENTS]: 'Parents', [keys.PASSWORD]: 'Password', + [keys.PERSONAL_ACCESS_TOKEN]: 'Personal Access Token', [keys.PERSONAL_ACCESS_TOKENS]: 'Personal Access Tokens', [keys.PLAIN_TEXT]: 'Plain Text', [keys.PLUGIN]: 'Plugin', @@ -410,6 +416,7 @@ const messages_en = { [keys.REGISTRATOR]: 'Registrator', [keys.REMOVE]: 'Remove', [keys.REMOVE_TERM]: 'Remove Term', + [keys.REMOVE_TOKEN]: 'Remove Token', [keys.RESULTS]: 'Results', [keys.RESULTS_RANGE]: '${0} of ${1}', [keys.RESULT]: 'Result', @@ -431,6 +438,7 @@ const messages_en = { [keys.SECTION_IS_USED]: 'This section contains property assignments which are already used by existing entities of "${0}" type. Removing it is also going to remove ${1} existing property value(s) - data will be lost! Are you sure you want to proceed?', [keys.SELECTED_ROWS]: 'Selected Rows', [keys.SELECTED_ROWS_NOT_VISIBLE_DUE_TO_FILTERING_AND_PAGING]: 'Some selected rows are not visible due to the chosen filtering and paging.', + [keys.SESSION_NAME]: 'Session name', [keys.SHOW]: 'show', [keys.SHOW_CONTAINER]: 'Show Container', [keys.SHOW_DETAILS]: 'Show details', diff --git a/openbis_ng_ui/src/js/components/tools/Tools.jsx b/openbis_ng_ui/src/js/components/tools/Tools.jsx index 54c4e47cef8..22cf0d0febe 100644 --- a/openbis_ng_ui/src/js/components/tools/Tools.jsx +++ b/openbis_ng_ui/src/js/components/tools/Tools.jsx @@ -63,7 +63,7 @@ class Tools extends React.PureComponent { return <ToolSearch searchText={object.id} /> } else if (object.type === objectType.OVERVIEW) { if (object.id === objectType.PERSONAL_ACCESS_TOKEN) { - return <PersonalAccessTokenForm /> + return <PersonalAccessTokenForm object={object} /> } else { return <ToolSearch objectType={object.id} /> } diff --git a/openbis_ng_ui/src/js/components/tools/form/pat/PersonalAccessTokenForm.jsx b/openbis_ng_ui/src/js/components/tools/form/pat/PersonalAccessTokenForm.jsx index 317a2a5c3e9..6fb7dfe811b 100644 --- a/openbis_ng_ui/src/js/components/tools/form/pat/PersonalAccessTokenForm.jsx +++ b/openbis_ng_ui/src/js/components/tools/form/pat/PersonalAccessTokenForm.jsx @@ -1,8 +1,139 @@ import React from 'react' +import autoBind from 'auto-bind' +import ComponentContext from '@src/js/components/common/ComponentContext.js' +import PageWithTwoPanels from '@src/js/components/common/page/PageWithTwoPanels.jsx' +import GridWithSettings from '@src/js/components/common/grid/GridWithSettings.jsx' +import GridContainer from '@src/js/components/common/grid/GridContainer.jsx' +import PersonalAccessTokenFormController from '@src/js/components/tools/form/pat/PersonalAccessTokenFormController.js' +import PersonalAccessTokenFormFacade from '@src/js/components/tools/form/pat/PersonalAccessTokenFormFacade.js' +import PersonalAccessTokenFormParameters from '@src/js/components/tools/form/pat/PersonalAccessTokenFormParameters.jsx' +import PersonalAccessTokenFormButtons from '@src/js/components/tools/form/pat/PersonalAccessTokenFormButtons.jsx' +import ids from '@src/js/common/consts/ids.js' +import messages from '@src/js/common/messages.js' +import logger from '@src/js/common/logger.js' + +const columns = [ + { + name: 'sessionName', + label: messages.get(messages.SESSION_NAME), + getValue: ({ row }) => row.sessionName.value + } +] class PersonalAccessTokenForm extends React.PureComponent { + constructor(props) { + super(props) + autoBind(this) + + this.state = {} + + if (this.props.controller) { + this.controller = this.props.controller + } else { + this.controller = new PersonalAccessTokenFormController( + new PersonalAccessTokenFormFacade() + ) + } + + this.controller.init(new ComponentContext(this)) + } + + componentDidMount() { + this.controller.load() + } + + handleClickContainer() { + this.controller.handleSelectionChange() + } + + handleSelectedRowChange(row) { + const { controller } = this + if (row) { + controller.handleSelectionChange({ + id: row.id + }) + } else { + controller.handleSelectionChange() + } + } + + handleGridControllerRef(gridController) { + this.controller.gridController = gridController + } + render() { - return <div>Personal Access Token Form</div> + logger.log(logger.DEBUG, 'PersonalAccessTokenForm.render') + + const { loadId, loading, loaded } = this.state + + return ( + <PageWithTwoPanels + key={loadId} + loading={loading} + loaded={loaded} + object={{}} + renderMainPanel={() => this.renderMainPanel()} + renderAdditionalPanel={() => this.renderAdditionalPanel()} + renderButtons={() => this.renderButtons()} + /> + ) + } + + renderMainPanel() { + const { pats, selection } = this.state + + return ( + <GridContainer onClick={this.handleClickContainer}> + <GridWithSettings + id={ids.PERSONAL_ACCESS_TOKEN_GRID_ID} + controllerRef={this.handleGridControllerRef} + header={messages.get(messages.PERSONAL_ACCESS_TOKENS)} + columns={columns} + rows={pats} + sort='sessionName' + selectable={true} + selectedRowId={selection ? selection.params.id : null} + onSelectedRowChange={this.handleSelectedRowChange} + /> + </GridContainer> + ) + } + + renderAdditionalPanel() { + const { controller } = this + const { pats, selection, selectedRow, mode } = this.state + + return ( + <PersonalAccessTokenFormParameters + controller={controller} + pats={pats} + selection={selection} + selectedRow={selectedRow} + mode={mode} + onChange={controller.handleChange} + onSelectionChange={controller.handleSelectionChange} + onBlur={controller.handleBlur} + /> + ) + } + + renderButtons() { + const { controller } = this + const { pats, selection, changed, mode } = this.state + + return ( + <PersonalAccessTokenFormButtons + pats={pats} + selection={selection} + changed={changed} + mode={mode} + onEdit={controller.handleEdit} + onSave={controller.handleSave} + onCancel={controller.handleCancel} + onAdd={controller.handleAdd} + onRemove={controller.handleRemove} + /> + ) } } diff --git a/openbis_ng_ui/src/js/components/tools/form/pat/PersonalAccessTokenFormButtons.jsx b/openbis_ng_ui/src/js/components/tools/form/pat/PersonalAccessTokenFormButtons.jsx new file mode 100644 index 00000000000..4ec9adaa026 --- /dev/null +++ b/openbis_ng_ui/src/js/components/tools/form/pat/PersonalAccessTokenFormButtons.jsx @@ -0,0 +1,57 @@ +import React from 'react' +import PageMode from '@src/js/components/common/page/PageMode.js' +import PageButtons from '@src/js/components/common/page/PageButtons.jsx' +import Button from '@src/js/components/common/form/Button.jsx' +import messages from '@src/js/common/messages.js' +import logger from '@src/js/common/logger.js' + +class PersonalAccessTokenFormButtons extends React.PureComponent { + constructor(props) { + super(props) + } + + render() { + logger.log(logger.DEBUG, 'PersonalAccessTokenFormButtons.render') + + const { mode, onEdit, onSave, onCancel, changed } = this.props + + return ( + <PageButtons + mode={mode} + changed={changed} + onEdit={onEdit} + onSave={onSave} + onCancel={onCancel} + renderAdditionalButtons={params => this.renderAdditionalButtons(params)} + /> + ) + } + + renderAdditionalButtons({ mode, classes }) { + if (mode === PageMode.EDIT) { + const { selection, onAdd, onRemove } = this.props + + return ( + <React.Fragment> + <Button + name='addToken' + label={messages.get(messages.ADD_TOKEN)} + styles={{ root: classes.button }} + onClick={onAdd} + /> + <Button + name='removeToken' + label={messages.get(messages.REMOVE_TOKEN)} + styles={{ root: classes.button }} + disabled={!selection} + onClick={onRemove} + /> + </React.Fragment> + ) + } else { + return null + } + } +} + +export default PersonalAccessTokenFormButtons diff --git a/openbis_ng_ui/src/js/components/tools/form/pat/PersonalAccessTokenFormController.js b/openbis_ng_ui/src/js/components/tools/form/pat/PersonalAccessTokenFormController.js new file mode 100644 index 00000000000..4ed059c0d3b --- /dev/null +++ b/openbis_ng_ui/src/js/components/tools/form/pat/PersonalAccessTokenFormController.js @@ -0,0 +1,88 @@ +import autoBind from 'auto-bind' +import PageControllerChanged from '@src/js/components/common/page/PageControllerChanged.js' +import PageControllerEdit from '@src/js/components/common/page/PageControllerEdit.js' +import PageControllerCancel from '@src/js/components/common/page/PageControllerCancel.js' +import PersonalAccessTokenFormControllerLoad from '@src/js/components/tools/form/pat/PersonalAccessTokenFormControllerLoad.js' +import PersonalAccessTokenFormControllerAdd from '@src/js/components/tools/form/pat/PersonalAccessTokenFormControllerAdd.js' +import PersonalAccessTokenFormControllerRemove from '@src/js/components/tools/form/pat/PersonalAccessTokenFormControllerRemove.js' +import PersonalAccessTokenFormControllerValidate from '@src/js/components/tools/form/pat/PersonalAccessTokenFormControllerValidate.js' +import PersonalAccessTokenFormControllerChange from '@src/js/components/tools/form/pat/PersonalAccessTokenFormControllerChange.js' +import PersonalAccessTokenFormControllerSelectionChange from '@src/js/components/tools/form/pat/PersonalAccessTokenFormControllerSelectionChange.js' +import PersonalAccessTokenFormControllerSave from '@src/js/components/tools/form/pat/PersonalAccessTokenFormControllerSave.js' +import pages from '@src/js/common/consts/pages.js' + +export default class PersonalAccessTokenFormController { + constructor(facade) { + autoBind(this) + this.facade = facade + } + + getPage() { + return pages.TOOLS + } + + init(context) { + this.context = context + this.object = context.getProps().object + } + + load() { + return new PersonalAccessTokenFormControllerLoad(this).execute() + } + + validate(autofocus) { + return new PersonalAccessTokenFormControllerValidate(this).execute( + autofocus + ) + } + + changed(changed) { + return new PageControllerChanged(this).execute(changed) + } + + handleEdit() { + return new PageControllerEdit(this).execute() + } + + handleCancel() { + return new PageControllerCancel(this).execute() + } + + handleAdd() { + return new PersonalAccessTokenFormControllerAdd(this).execute() + } + + handleRemove() { + return new PersonalAccessTokenFormControllerRemove(this).execute() + } + + handleChange(params) { + return new PersonalAccessTokenFormControllerChange(this).execute(params) + } + + handleBlur() { + return this.validate() + } + + handleSelectionChange(params) { + return new PersonalAccessTokenFormControllerSelectionChange(this).execute( + params + ) + } + + handleSave() { + return new PersonalAccessTokenFormControllerSave(this).execute() + } + + getFacade() { + return this.facade + } + + getContext() { + return this.context + } + + getObject() { + return this.object + } +} diff --git a/openbis_ng_ui/src/js/components/tools/form/pat/PersonalAccessTokenFormControllerAdd.js b/openbis_ng_ui/src/js/components/tools/form/pat/PersonalAccessTokenFormControllerAdd.js new file mode 100644 index 00000000000..fe03b8eed43 --- /dev/null +++ b/openbis_ng_ui/src/js/components/tools/form/pat/PersonalAccessTokenFormControllerAdd.js @@ -0,0 +1,41 @@ +import _ from 'lodash' +import FormUtil from '@src/js/components/common/form/FormUtil.js' + +export default class PersonalAccessTokenFormControllerAdd { + constructor(controller) { + this.controller = controller + this.context = controller.context + this.gridController = controller.gridController + } + + async execute() { + let { pats } = this.context.getState() + + const newPat = { + id: _.uniqueId('pat-'), + sessionName: FormUtil.createField({}) + } + + const newPats = Array.from(pats) + newPats.push(newPat) + + await this.context.setState(state => ({ + ...state, + pats: newPats, + selection: { + params: { + id: newPat.id, + part: 'code' + } + } + })) + + if (this.gridController) { + await this.gridController.load() + await this.gridController.selectRow(newPat.id) + await this.gridController.showRow(newPat.id) + } + + await this.controller.changed(true) + } +} diff --git a/openbis_ng_ui/src/js/components/tools/form/pat/PersonalAccessTokenFormControllerChange.js b/openbis_ng_ui/src/js/components/tools/form/pat/PersonalAccessTokenFormControllerChange.js new file mode 100644 index 00000000000..07dbd338588 --- /dev/null +++ b/openbis_ng_ui/src/js/components/tools/form/pat/PersonalAccessTokenFormControllerChange.js @@ -0,0 +1,30 @@ +import FormUtil from '@src/js/components/common/form/FormUtil.js' + +export default class PersonalAccessTokenFormControllerChange { + constructor(controller) { + this.controller = controller + this.context = controller.context + this.gridController = controller.gridController + } + + async execute(params) { + await this.context.setState(state => { + const { newCollection } = FormUtil.changeCollectionItemField( + state.pats, + params.id, + params.field, + params.value + ) + return { + pats: newCollection + } + }) + + if (this.gridController) { + await this.gridController.load() + await this.gridController.showRow(params.id) + } + + await this.controller.changed(true) + } +} diff --git a/openbis_ng_ui/src/js/components/tools/form/pat/PersonalAccessTokenFormControllerLoad.js b/openbis_ng_ui/src/js/components/tools/form/pat/PersonalAccessTokenFormControllerLoad.js new file mode 100644 index 00000000000..c3d18d3d945 --- /dev/null +++ b/openbis_ng_ui/src/js/components/tools/form/pat/PersonalAccessTokenFormControllerLoad.js @@ -0,0 +1,65 @@ +import _ from 'lodash' +import PageMode from '@src/js/components/common/page/PageMode.js' +import FormValidator from '@src/js/components/common/form/FormValidator.js' +import FormUtil from '@src/js/components/common/form/FormUtil.js' +import AppController from '@src/js/components/AppController.js' + +export default class PersonalAccessTokenFormControllerLoad { + constructor(controller) { + this.controller = controller + this.context = controller.context + this.facade = controller.facade + } + + async execute() { + await this.context.setState({ + loading: true, + mode: PageMode.VIEW, + validate: FormValidator.MODE_BASIC + }) + + try { + const loadedPats = await this.facade.loadPats() + const pats = loadedPats.map(loadedPat => this._createPat(loadedPat)) + const selection = this._createSelection(pats) + + return this.context.setState({ + pats, + selection, + original: { + pats: pats.map(pat => pat.original) + } + }) + } catch (error) { + AppController.getInstance().errorChange(error) + } finally { + this.controller.changed(false) + this.context.setState({ + loadId: _.uniqueId('load'), + loaded: true, + loading: false + }) + } + } + + _createPat(loadedPat) { + const pat = { + id: _.get(loadedPat, 'hash'), + hash: FormUtil.createField({ + value: _.get(loadedPat, 'hash', null), + enabled: false + }), + sessionName: FormUtil.createField({ + value: _.get(loadedPat, 'sessionName', null), + enabled: false + }), + original: _.cloneDeep(loadedPat) + } + return pat + } + + _createSelection() { + // TODO + return null + } +} diff --git a/openbis_ng_ui/src/js/components/tools/form/pat/PersonalAccessTokenFormControllerRemove.js b/openbis_ng_ui/src/js/components/tools/form/pat/PersonalAccessTokenFormControllerRemove.js new file mode 100644 index 00000000000..5bbc05df726 --- /dev/null +++ b/openbis_ng_ui/src/js/components/tools/form/pat/PersonalAccessTokenFormControllerRemove.js @@ -0,0 +1,29 @@ +export default class PersonalAccessTokenFormControllerRemove { + constructor(controller) { + this.controller = controller + this.context = controller.context + this.gridController = controller.gridController + } + + async execute() { + const { selection, pats } = this.context.getState() + + const patIndex = pats.findIndex(pat => pat.id === selection.params.id) + + const newPats = Array.from(pats) + newPats.splice(patIndex, 1) + + await this.context.setState(state => ({ + ...state, + pats: newPats, + selection: null + })) + + if (this.gridController) { + await this.gridController.selectRow(null) + await this.gridController.load() + } + + await this.controller.changed(true) + } +} diff --git a/openbis_ng_ui/src/js/components/tools/form/pat/PersonalAccessTokenFormControllerSave.js b/openbis_ng_ui/src/js/components/tools/form/pat/PersonalAccessTokenFormControllerSave.js new file mode 100644 index 00000000000..5da75b2d4e8 --- /dev/null +++ b/openbis_ng_ui/src/js/components/tools/form/pat/PersonalAccessTokenFormControllerSave.js @@ -0,0 +1,74 @@ +import _ from 'lodash' +import FormValidator from '@src/js/components/common/form/FormValidator.js' +import FormUtil from '@src/js/components/common/form/FormUtil.js' +import AppController from '@src/js/components/AppController.js' +import openbis from '@src/js/services/openbis.js' + +export default class PersonalAccessTokenFormControllerSave { + constructor(controller) { + this.controller = controller + this.context = controller.context + this.facade = controller.facade + } + + async execute() { + try { + await this.context.setState({ + validate: FormValidator.MODE_FULL + }) + + const valid = await this.controller.validate(true) + if (!valid) { + return + } + + await this.context.setState({ + loading: true + }) + + const state = this.context.getState() + const pats = this._preparePats(state.pats) + const operations = [] + + state.original.pats.forEach(originalPat => { + const pat = _.find(pats, ['id', originalPat.id]) + if (!pat) { + operations.push(this._deletePatOperation(originalPat)) + } + }) + + pats.forEach(pat => { + if (!pat.original) { + operations.push(this._createPatOperation(pat)) + } + }) + + const options = new openbis.SynchronousOperationExecutionOptions() + options.setExecuteInOrder(true) + await this.facade.executeOperations(operations, options) + } catch (error) { + AppController.getInstance().errorChange(error) + } finally { + this.context.setState({ + loading: false + }) + } + } + + _preparePats(pats) { + return pats.map(pat => FormUtil.trimFields(pat)) + } + + _createPatOperation(pat) { + const creation = new openbis.PersonalAccessTokenCreation() + creation.setSessionName(pat.sessionName.value) + return new openbis.CreatePersonalAccessTokensOperation([creation]) + } + + _deletePatOperation(pat) { + const patId = new openbis.PersonalAccessTokenPermId(pat.hash.value) + const options = new openbis.PersonalAccessTokenDeletionOptions() + options.setReason('deleted via ng_ui') + return new openbis.DeletePersonalAccessTokensOperation([patId], options) + } +} diff --git a/openbis_ng_ui/src/js/components/tools/form/pat/PersonalAccessTokenFormControllerSelectionChange.js b/openbis_ng_ui/src/js/components/tools/form/pat/PersonalAccessTokenFormControllerSelectionChange.js new file mode 100644 index 00000000000..35f5a61a30b --- /dev/null +++ b/openbis_ng_ui/src/js/components/tools/form/pat/PersonalAccessTokenFormControllerSelectionChange.js @@ -0,0 +1,30 @@ +export default class PersonalAccessTokenFormControllerSelectionChange { + constructor(controller) { + this.context = controller.context + this.gridController = controller.gridController + } + + async execute(params) { + let selection = null + + if (params) { + selection = { + params + } + } + + this.context.setState(state => ({ + ...state, + selection + })) + + if (this.gridController) { + await this.gridController.selectRow( + selection ? selection.params.id : null + ) + await this.context.setState({ + selectedRow: this.gridController.getSelectedRow() + }) + } + } +} diff --git a/openbis_ng_ui/src/js/components/tools/form/pat/PersonalAccessTokenFormControllerValidate.js b/openbis_ng_ui/src/js/components/tools/form/pat/PersonalAccessTokenFormControllerValidate.js new file mode 100644 index 00000000000..18b66fff2f9 --- /dev/null +++ b/openbis_ng_ui/src/js/components/tools/form/pat/PersonalAccessTokenFormControllerValidate.js @@ -0,0 +1,46 @@ +import PageControllerValidate from '@src/js/components/common/page/PageConrollerValidate.js' +import messages from '@src/js/common/messages.js' + +export default class PersonalAccessTokenFormControllerValidate extends PageControllerValidate { + validate(validator) { + const { pats } = this.context.getState() + + const newPats = this._validatePats(validator, pats) + + return { + pats: newPats + } + } + + async select(firstError) { + const { pats } = this.context.getState() + + if (pats.includes(firstError.object)) { + await this.setSelection({ + params: { + id: firstError.object.id, + part: firstError.name + } + }) + + if (this.controller.gridController) { + await this.controller.gridController.showRow(firstError.object.id) + } + } + } + + _validatePats(validator, pats) { + pats.forEach(pat => { + this._validatePat(validator, pat) + }) + return validator.withErrors(pats) + } + + _validatePat(validator, pat) { + validator.validateNotEmpty( + pat, + 'sessionName', + messages.get(messages.SESSION_NAME) + ) + } +} diff --git a/openbis_ng_ui/src/js/components/tools/form/pat/PersonalAccessTokenFormFacade.js b/openbis_ng_ui/src/js/components/tools/form/pat/PersonalAccessTokenFormFacade.js new file mode 100644 index 00000000000..464d5a42fff --- /dev/null +++ b/openbis_ng_ui/src/js/components/tools/form/pat/PersonalAccessTokenFormFacade.js @@ -0,0 +1,18 @@ +import openbis from '@src/js/services/openbis.js' + +export default class PersonalAccessTokenFormFacade { + async loadPats() { + const criteria = new openbis.PersonalAccessTokenSearchCriteria() + const fo = new openbis.PersonalAccessTokenFetchOptions() + fo.withOwner() + fo.withRegistrator() + fo.withModifier() + return openbis.searchPersonalAccessTokens(criteria, fo).then(result => { + return result.getObjects() + }) + } + + async executeOperations(operations, options) { + return openbis.executeOperations(operations, options) + } +} diff --git a/openbis_ng_ui/src/js/components/tools/form/pat/PersonalAccessTokenFormParameters.jsx b/openbis_ng_ui/src/js/components/tools/form/pat/PersonalAccessTokenFormParameters.jsx new file mode 100644 index 00000000000..5eab0c91e73 --- /dev/null +++ b/openbis_ng_ui/src/js/components/tools/form/pat/PersonalAccessTokenFormParameters.jsx @@ -0,0 +1,149 @@ +import React from 'react' +import { withStyles } from '@material-ui/core/styles' +import Container from '@src/js/components/common/form/Container.jsx' +import Header from '@src/js/components/common/form/Header.jsx' +import TextField from '@src/js/components/common/form/TextField.jsx' +import Message from '@src/js/components/common/form/Message.jsx' +import messages from '@src/js/common/messages.js' +import logger from '@src/js/common/logger.js' + +const styles = theme => ({ + field: { + paddingBottom: theme.spacing(1) + } +}) + +class PersonalAccessTokenFormParameters extends React.PureComponent { + constructor(props) { + super(props) + this.state = {} + this.references = { + sessionName: React.createRef() + } + this.handleChange = this.handleChange.bind(this) + this.handleFocus = this.handleFocus.bind(this) + this.handleBlur = this.handleBlur.bind(this) + } + + componentDidMount() { + this.focus() + } + + componentDidUpdate(prevProps) { + const prevSelection = prevProps.selection + const selection = this.props.selection + + if (prevSelection !== selection) { + this.focus() + } + } + + focus() { + const pat = this.getPat(this.props) + if (pat && this.props.selection) { + const { part } = this.props.selection.params + if (part) { + const reference = this.references[part] + if (reference && reference.current) { + reference.current.focus() + } + } + } + } + + handleChange(event) { + const pat = this.getPat(this.props) + this.props.onChange({ + id: pat.id, + field: event.target.name, + value: event.target.value + }) + } + + handleFocus(event) { + const pat = this.getPat(this.props) + this.props.onSelectionChange({ + id: pat.id, + part: event.target.name + }) + } + + handleBlur() { + this.props.onBlur() + } + + render() { + logger.log(logger.DEBUG, 'PersonalAccessTokenFormParameters.render') + + const pat = this.getPat(this.props) + if (!pat) { + return null + } + + return ( + <Container> + <Header>{messages.get(messages.PERSONAL_ACCESS_TOKEN)}</Header> + {this.renderMessageVisible(pat)} + {this.renderSessionName(pat)} + </Container> + ) + } + + renderMessageVisible() { + const { classes, selectedRow } = this.props + + if (selectedRow && !selectedRow.visible) { + return ( + <div className={classes.field}> + <Message type='warning'> + {messages.get( + messages.OBJECT_NOT_VISIBLE_DUE_TO_FILTERING_AND_PAGING + )} + </Message> + </div> + ) + } else { + return null + } + } + + renderSessionName(pat) { + const { visible, enabled, error, value } = { ...pat.sessionName } + + if (!visible) { + return null + } + + const { mode, classes } = this.props + return ( + <div className={classes.field}> + <TextField + reference={this.references.sessionName} + label={messages.get(messages.SESSION_NAME)} + name='sessionName' + mandatory={true} + error={error} + disabled={!enabled} + value={value} + mode={mode} + onChange={this.handleChange} + onFocus={this.handleFocus} + onBlur={this.handleBlur} + /> + </div> + ) + } + + getPat(props) { + let { pats, selection } = props + + if (selection) { + let [pat] = pats.filter(pat => pat.id === selection.params.id) + return pat + } else { + return null + } + } +} + +export default withStyles(styles)(PersonalAccessTokenFormParameters) diff --git a/openbis_ng_ui/src/js/services/openbis/api.js b/openbis_ng_ui/src/js/services/openbis/api.js index 4ff7c613e4a..8e2afeb60c0 100644 --- a/openbis_ng_ui/src/js/services/openbis/api.js +++ b/openbis_ng_ui/src/js/services/openbis/api.js @@ -82,6 +82,10 @@ class Facade { return this.promise(this.v3.searchPlugins(criteria, fo)) } + searchPersonalAccessTokens(criteria, fo) { + return this.promise(this.v3.searchPersonalAccessTokens(criteria, fo)) + } + searchQueries(criteria, fo) { return this.promise(this.v3.searchQueries(criteria, fo)) } diff --git a/openbis_ng_ui/src/js/services/openbis/dto.js b/openbis_ng_ui/src/js/services/openbis/dto.js index da6325faa8c..43638748423 100644 --- a/openbis_ng_ui/src/js/services/openbis/dto.js +++ b/openbis_ng_ui/src/js/services/openbis/dto.js @@ -53,6 +53,14 @@ const CLASS_FULL_NAMES = [ 'as/dto/material/update/MaterialTypeUpdate', 'as/dto/material/update/UpdateMaterialTypesOperation', 'as/dto/operation/SynchronousOperationExecutionOptions', + 'as/dto/pat/create/CreatePersonalAccessTokensOperation', + 'as/dto/pat/create/PersonalAccessTokenCreation', + 'as/dto/pat/delete/DeletePersonalAccessTokensOperation', + 'as/dto/pat/delete/PersonalAccessTokenDeletionOptions', + 'as/dto/pat/fetchoptions/PersonalAccessTokenFetchOptions', + 'as/dto/pat/fetchoptions/PersonalAccessTokenFetchOptions', + 'as/dto/pat/id/PersonalAccessTokenPermId', + 'as/dto/pat/search/PersonalAccessTokenSearchCriteria', 'as/dto/person/create/CreatePersonsOperation', 'as/dto/person/create/PersonCreation', 'as/dto/person/delete/PersonDeletionOptions', -- GitLab