[arvados-workbench2] updated: 2.4.0-56-g0bd774be

git repository hosting git at public.arvados.org
Mon May 23 16:29:47 UTC 2022


Summary of changes:
 package.json                                       |  3 +
 .../api-client-authorization-service.test.ts       | 21 ++++-
 .../api-client-authorization-service.ts            |  9 ++-
 src/store/sharing-dialog/sharing-dialog-actions.ts |  4 +-
 .../sharing-dialog/sharing-dialog-component.tsx    | 74 +++++++++++++++---
 .../sharing-dialog/sharing-dialog.tsx              |  4 +-
 yarn.lock                                          | 91 +++++++++++++++++++++-
 7 files changed, 183 insertions(+), 23 deletions(-)

       via  0bd774be1714eb7bb815cef3d44803eac6c735ec (commit)
       via  4f26fc6e11a411c11fc481b937c2e073f4858081 (commit)
      from  709996fdd46c5993944753fab7919676e0cbd60d (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 0bd774be1714eb7bb815cef3d44803eac6c735ec
Author: Lucas Di Pentima <lucas.dipentima at curii.com>
Date:   Mon May 23 13:26:02 2022 -0300

    16115: Adds inline DateTime picker to the sharing URLs dialog.
    
    We're not currently exporting the API.MaxTokenLifetime configuration, so
    client-side expires_at validation is not possible; added an explanation note
    for the cases when the expiration date is capped by the backend.
    
    Arvados-DCO-1.1-Signed-off-by: Lucas Di Pentima <lucas.dipentima at curii.com>

diff --git a/package.json b/package.json
index 210045ba..a8b3ee81 100644
--- a/package.json
+++ b/package.json
@@ -3,6 +3,7 @@
   "version": "0.1.0",
   "private": true,
   "dependencies": {
+    "@date-io/date-fns": "1",
     "@fortawesome/fontawesome-svg-core": "1.2.28",
     "@fortawesome/free-solid-svg-icons": "5.13.0",
     "@fortawesome/react-fontawesome": "0.1.9",
@@ -28,6 +29,7 @@
     "caniuse-lite": "1.0.30001299",
     "classnames": "2.2.6",
     "cwlts": "1.15.29",
+    "date-fns": "^2.28.0",
     "debounce": "1.2.0",
     "elliptic": "6.5.4",
     "file-saver": "2.0.1",
@@ -40,6 +42,7 @@
     "lodash-es": "4.17.14",
     "lodash.mergewith": "4.6.2",
     "lodash.template": "4.5.0",
+    "material-ui-pickers": "^2.2.4",
     "mem": "4.0.0",
     "moment": "2.29.1",
     "parse-duration": "0.4.4",
diff --git a/src/views-components/sharing-dialog/sharing-dialog-component.tsx b/src/views-components/sharing-dialog/sharing-dialog-component.tsx
index 259390aa..0fa0056c 100644
--- a/src/views-components/sharing-dialog/sharing-dialog-component.tsx
+++ b/src/views-components/sharing-dialog/sharing-dialog-component.tsx
@@ -13,6 +13,9 @@ import {
     Paper,
     Tabs,
     Tab,
+    Checkbox,
+    FormControlLabel,
+    Typography,
 } from '@material-ui/core';
 import {
     StyleRulesCallback,
@@ -27,6 +30,14 @@ import {
 } from 'models/resource';
 import { SharingInvitationForm } from './sharing-invitation-form';
 import { SharingManagementForm } from './sharing-management-form';
+import {
+    BasePicker,
+    Calendar,
+    MuiPickersUtilsProvider,
+    TimePickerView
+} from 'material-ui-pickers';
+import DateFnsUtils from "@date-io/date-fns";
+import moment from 'moment';
 
 export interface SharingDialogDataProps {
     open: boolean;
@@ -37,7 +48,7 @@ export interface SharingDialogDataProps {
 export interface SharingDialogActionProps {
     onClose: () => void;
     onSave: () => void;
-    onCreateSharingToken: () => void;
+    onCreateSharingToken: (d: Date | undefined) => () => void;
     refreshPermissions: () => void;
 }
 enum SharingDialogTab {
@@ -49,12 +60,22 @@ export default (props: SharingDialogDataProps & SharingDialogActionProps) => {
         onClose, onSave, onCreateSharingToken, refreshPermissions } = props;
     const showTabs = extractUuidObjectType(sharedResourceUuid) === ResourceObjectType.COLLECTION;
     const [tabNr, setTabNr] = React.useState<number>(SharingDialogTab.PERMISSIONS);
+    const [expDate, setExpDate] = React.useState<Date>();
+    const [withExpiration, setWithExpiration] = React.useState<boolean>(false);
 
     // Sets up the dialog depending on the resource type
     if (!showTabs && tabNr !== SharingDialogTab.PERMISSIONS) {
         setTabNr(SharingDialogTab.PERMISSIONS);
     }
 
+    React.useEffect(() => {
+        if (!withExpiration) {
+            setExpDate(undefined);
+        } else {
+            setExpDate(moment().add(1, 'hour').toDate());
+        }
+    }, [withExpiration]);
+
     return <Dialog
         {...{ open, onClose }}
         className="sharing-dialog"
@@ -95,30 +116,59 @@ export default (props: SharingDialogDataProps & SharingDialogActionProps) => {
                 <Grid item md={12}>
                     <SharingInvitationForm />
                 </Grid> }
+                { tabNr === SharingDialogTab.URLS && withExpiration && <>
+                <Grid item container direction='row' md={12}>
+                    <MuiPickersUtilsProvider utils={DateFnsUtils}>
+                        <BasePicker autoOk value={expDate} onChange={setExpDate}>
+                        {({ date, handleChange }) => (<>
+                            <Grid item md={6}>
+                                <Calendar date={date} minDate={new Date()} maxDate={undefined}
+                                    onChange={handleChange} />
+                            </Grid>
+                            <Grid item md={6}>
+                                <TimePickerView type="hours" date={date} ampm={false}
+                                    onMinutesChange={() => {}}
+                                    onSecondsChange={() => {}}
+                                    onHourChange={handleChange}
+                                />
+                            </Grid>
+                        </>)}
+                        </BasePicker>
+                    </MuiPickersUtilsProvider>
+                </Grid>
+                <Grid item md={12}>
+                    <Typography variant='caption' align='center'>
+                        Maximum expiration date may be limited by the cluster configuration.
+                    </Typography>
+                </Grid>
+                </> }
                 <Grid item xs />
-                { tabNr === SharingDialogTab.URLS &&
+                { tabNr === SharingDialogTab.URLS && <>
+                <Grid item><FormControlLabel
+                    control={<Checkbox color="primary" checked={withExpiration}
+                        onChange={(e) => setWithExpiration(e.target.checked)} />}
+                    label="With expiration" />
+                </Grid>
                 <Grid item>
-                    <Button
-                        variant="contained"
-                        color="primary"
-                        onClick={onCreateSharingToken}>
+                    <Button variant="contained" color="primary"
+                        onClick={onCreateSharingToken(expDate)}>
                         Create sharing URL
                     </Button>
                 </Grid>
-                }
+                </>}
                 { tabNr === SharingDialogTab.PERMISSIONS &&
                 <Grid item>
-                    <Button
-                        variant='contained'
-                        color='primary'
-                        onClick={onSave}
+                    <Button onClick={onSave} variant="contained" color="primary"
                         disabled={!saveEnabled}>
                         Save changes
                     </Button>
                 </Grid>
                 }
                 <Grid item>
-                    <Button onClick={onClose}>
+                    <Button onClick={() => {
+                        onClose();
+                        setWithExpiration(false);
+                    }}>
                         Close
                     </Button>
                 </Grid>
diff --git a/src/views-components/sharing-dialog/sharing-dialog.tsx b/src/views-components/sharing-dialog/sharing-dialog.tsx
index e48983a2..8cfc58f7 100644
--- a/src/views-components/sharing-dialog/sharing-dialog.tsx
+++ b/src/views-components/sharing-dialog/sharing-dialog.tsx
@@ -43,8 +43,8 @@ const mapDispatchToProps = (dispatch: Dispatch, { ...props }: Props): SharingDia
     onSave: () => {
         dispatch<any>(saveSharingDialogChanges);
     },
-    onCreateSharingToken: () => {
-        dispatch<any>(createSharingToken);
+    onCreateSharingToken: (d: Date) => () => {
+        dispatch<any>(createSharingToken(d));
     },
     refreshPermissions: () => {
         dispatch<any>(initializeManagementForm);
diff --git a/yarn.lock b/yarn.lock
index faa2b251..13ea553a 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1710,6 +1710,24 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@date-io/core at npm:^1.3.13":
+  version: 1.3.13
+  resolution: "@date-io/core at npm:1.3.13"
+  checksum: 5a9e9d1de20f0346a3c7d2d5946190caef4bfb0b64d82ba1f4c566657a9192667c94ebe7f438d11d4286d9c190974daad4fb2159294225cd8af4d9a140239879
+  languageName: node
+  linkType: hard
+
+"@date-io/date-fns at npm:1":
+  version: 1.3.13
+  resolution: "@date-io/date-fns at npm:1.3.13"
+  dependencies:
+    "@date-io/core": ^1.3.13
+  peerDependencies:
+    date-fns: ^2.0.0
+  checksum: 0026c0e538ea4add57a11936ff6bdb07e99f25275f8bb28c4702bbb7e82c3a41b3e8124132aa719180d462c01a26a3b4801e41b7349cdb73813749d4bf5e8fbd
+  languageName: node
+  linkType: hard
+
 "@fortawesome/fontawesome-common-types at npm:^0.2.28":
   version: 0.2.35
   resolution: "@fortawesome/fontawesome-common-types at npm:0.2.35"
@@ -2716,6 +2734,15 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@types/react-text-mask at npm:^5.4.3":
+  version: 5.4.11
+  resolution: "@types/react-text-mask at npm:5.4.11"
+  dependencies:
+    "@types/react": "*"
+  checksum: 4defba1467e61b73bfdae74d0b1bea0f27846aabf5283f137fa372ef05bf23accfdf04fffaba33272e9eff5abf00a74863e9c24ca6974c731d73f3fae6efc577
+  languageName: node
+  linkType: hard
+
 "@types/react-transition-group at npm:^2.0.8":
   version: 2.9.2
   resolution: "@types/react-transition-group at npm:2.9.2"
@@ -3710,6 +3737,7 @@ __metadata:
   version: 0.0.0-use.local
   resolution: "arvados-workbench-2 at workspace:."
   dependencies:
+    "@date-io/date-fns": 1
     "@fortawesome/fontawesome-svg-core": 1.2.28
     "@fortawesome/free-solid-svg-icons": 5.13.0
     "@fortawesome/react-fontawesome": 0.1.9
@@ -3752,6 +3780,7 @@ __metadata:
     classnames: 2.2.6
     cwlts: 1.15.29
     cypress: 6.3.0
+    date-fns: ^2.28.0
     debounce: 1.2.0
     elliptic: 6.5.4
     enzyme: 3.11.0
@@ -3767,6 +3796,7 @@ __metadata:
     lodash-es: 4.17.14
     lodash.mergewith: 4.6.2
     lodash.template: 4.5.0
+    material-ui-pickers: ^2.2.4
     mem: 4.0.0
     moment: 2.29.1
     node-sass: ^4.9.4
@@ -5325,6 +5355,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"clsx at npm:^1.0.2":
+  version: 1.1.1
+  resolution: "clsx at npm:1.1.1"
+  checksum: ff052650329773b9b245177305fc4c4dc3129f7b2be84af4f58dc5defa99538c61d4207be7419405a5f8f3d92007c954f4daba5a7b74e563d5de71c28c830063
+  languageName: node
+  linkType: hard
+
 "co at npm:^4.6.0":
   version: 4.6.0
   resolution: "co at npm:4.6.0"
@@ -6267,6 +6304,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"date-fns at npm:^2.28.0":
+  version: 2.28.0
+  resolution: "date-fns at npm:2.28.0"
+  checksum: a0516b2e4f99b8bffc6cc5193349f185f195398385bdcaf07f17c2c4a24473c99d933eb0018be4142a86a6d46cb0b06be6440ad874f15e795acbedd6fd727a1f
+  languageName: node
+  linkType: hard
+
 "debounce at npm:1.2.0":
   version: 1.2.0
   resolution: "debounce at npm:1.2.0"
@@ -11738,6 +11782,25 @@ __metadata:
   languageName: node
   linkType: hard
 
+"material-ui-pickers at npm:^2.2.4":
+  version: 2.2.4
+  resolution: "material-ui-pickers at npm:2.2.4"
+  dependencies:
+    "@types/react-text-mask": ^5.4.3
+    clsx: ^1.0.2
+    react-event-listener: ^0.6.6
+    react-text-mask: ^5.4.3
+    react-transition-group: ^2.5.3
+    tslib: ^1.9.3
+  peerDependencies:
+    "@material-ui/core": ^3.2.0
+    prop-types: ^15.6.0
+    react: ^16.3.0
+    react-dom: ^16.3.0
+  checksum: be93e30a824c347ede9f82c6adc92748807ebc9665f00ed86b62b580748ca03470823871337d554659d6a6cb6d5898d3636a7fed9e4f2d9cbfa295c196d8c008
+  languageName: node
+  linkType: hard
+
 "md5.js at npm:^1.3.4":
   version: 1.3.5
   resolution: "md5.js at npm:1.3.5"
@@ -14501,6 +14564,17 @@ __metadata:
   languageName: node
   linkType: hard
 
+"prop-types at npm:^15.5.6":
+  version: 15.8.1
+  resolution: "prop-types at npm:15.8.1"
+  dependencies:
+    loose-envify: ^1.4.0
+    object-assign: ^4.1.1
+    react-is: ^16.13.1
+  checksum: c056d3f1c057cb7ff8344c645450e14f088a915d078dcda795041765047fa080d38e5d626560ccaac94a4e16e3aa15f3557c1a9a8d1174530955e992c675e459
+  languageName: node
+  linkType: hard
+
 "proxy-addr at npm:~2.0.5":
   version: 2.0.7
   resolution: "proxy-addr at npm:2.0.7"
@@ -14858,7 +14932,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"react-event-listener at npm:^0.6.2":
+"react-event-listener at npm:^0.6.2, react-event-listener at npm:^0.6.6":
   version: 0.6.6
   resolution: "react-event-listener at npm:0.6.6"
   dependencies:
@@ -15095,6 +15169,17 @@ __metadata:
   languageName: node
   linkType: hard
 
+"react-text-mask at npm:^5.4.3":
+  version: 5.4.3
+  resolution: "react-text-mask at npm:5.4.3"
+  dependencies:
+    prop-types: ^15.5.6
+  peerDependencies:
+    react: ^0.14.0 || ^15.0.0 || ^16.0.0
+  checksum: ee9c560f47d2f67d0193636eeea36852503d6d7bfd16d75ecb8170256606923d786bbb3511971deedbd01136340acf597fe2b6ba0be3cddb2a17a602767eb7b9
+  languageName: node
+  linkType: hard
+
 "react-transition-group at npm:2.5.0":
   version: 2.5.0
   resolution: "react-transition-group at npm:2.5.0"
@@ -15110,7 +15195,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"react-transition-group at npm:^2.2.1":
+"react-transition-group at npm:^2.2.1, react-transition-group at npm:^2.5.3":
   version: 2.9.0
   resolution: "react-transition-group at npm:2.9.0"
   dependencies:
@@ -17661,7 +17746,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"tslib at npm:^1.8.0, tslib at npm:^1.8.1, tslib at npm:^1.9.0":
+"tslib at npm:^1.8.0, tslib at npm:^1.8.1, tslib at npm:^1.9.0, tslib at npm:^1.9.3":
   version: 1.14.1
   resolution: "tslib at npm:1.14.1"
   checksum: dbe628ef87f66691d5d2959b3e41b9ca0045c3ee3c7c7b906cc1e328b39f199bb1ad9e671c39025bd56122ac57dfbf7385a94843b1cc07c60a4db74795829acd

commit 4f26fc6e11a411c11fc481b937c2e073f4858081
Author: Lucas Di Pentima <lucas.dipentima at curii.com>
Date:   Thu May 19 13:22:00 2022 -0300

    16115: Adds expiration date param to sharing url service method.
    
    Arvados-DCO-1.1-Signed-off-by: Lucas Di Pentima <lucas.dipentima at curii.com>

diff --git a/src/services/api-client-authorization-service/api-client-authorization-service.test.ts b/src/services/api-client-authorization-service/api-client-authorization-service.test.ts
index 3a271f53..4dd01b87 100644
--- a/src/services/api-client-authorization-service/api-client-authorization-service.test.ts
+++ b/src/services/api-client-authorization-service/api-client-authorization-service.test.ts
@@ -24,7 +24,7 @@ describe('ApiClientAuthorizationService', () => {
             expect(() => apiClientAuthorizationService.createCollectionSharingToken("foo")).toThrowError("UUID foo is not a collection");
         });
 
-        it('should make a create request with proper scopes', async () => {
+        it('should make a create request with proper scopes and no expiration date', async () => {
             serverApi.post = jest.fn(() => Promise.resolve(
                 { data: { uuid: 'zzzzz-4zz18-0123456789abcde' } }
             ));
@@ -40,6 +40,25 @@ describe('ApiClientAuthorizationService', () => {
                 }
             );
         });
+
+        it('should make a create request with proper scopes and expiration date', async () => {
+            serverApi.post = jest.fn(() => Promise.resolve(
+                { data: { uuid: 'zzzzz-4zz18-0123456789abcde' } }
+            ));
+            const uuid = 'zzzzz-4zz18-0123456789abcde'
+            const expDate = new Date(2022, 8, 28, 12, 0, 0);
+            await apiClientAuthorizationService.createCollectionSharingToken(uuid, expDate);
+            expect(serverApi.post).toHaveBeenCalledWith(
+                '/api_client_authorizations', {
+                    scopes: [
+                        `GET /arvados/v1/collections/${uuid}`,
+                        `GET /arvados/v1/collections/${uuid}/`,
+                        `GET /arvados/v1/keep_services/accessible`,
+                    ],
+                    expires_at: expDate.toUTCString()
+                }
+            );
+        });
     });
 
     describe('listCollectionSharingToken', () => {
diff --git a/src/services/api-client-authorization-service/api-client-authorization-service.ts b/src/services/api-client-authorization-service/api-client-authorization-service.ts
index 7c985dbb..dbda0a42 100644
--- a/src/services/api-client-authorization-service/api-client-authorization-service.ts
+++ b/src/services/api-client-authorization-service/api-client-authorization-service.ts
@@ -14,17 +14,20 @@ export class ApiClientAuthorizationService extends CommonService<ApiClientAuthor
         super(serverApi, "api_client_authorizations", actions);
     }
 
-    createCollectionSharingToken(uuid: string): Promise<ApiClientAuthorization> {
+    createCollectionSharingToken(uuid: string, expDate: Date | undefined): Promise<ApiClientAuthorization> {
         if (extractUuidObjectType(uuid) !== ResourceObjectType.COLLECTION) {
             throw new Error(`UUID ${uuid} is not a collection`);
         }
-        return this.create({
+        const data = {
             scopes: [
                 `GET /arvados/v1/collections/${uuid}`,
                 `GET /arvados/v1/collections/${uuid}/`,
                 `GET /arvados/v1/keep_services/accessible`,
             ]
-        });
+        }
+        return expDate !== undefined
+            ? this.create({...data, expiresAt: expDate.toUTCString()})
+            : this.create(data);
     }
 
     listCollectionSharingTokens(uuid: string): Promise<ListResults<ApiClientAuthorization>> {
diff --git a/src/store/sharing-dialog/sharing-dialog-actions.ts b/src/store/sharing-dialog/sharing-dialog-actions.ts
index 3eec0b59..ffd81fb7 100644
--- a/src/store/sharing-dialog/sharing-dialog-actions.ts
+++ b/src/store/sharing-dialog/sharing-dialog-actions.ts
@@ -61,14 +61,14 @@ export interface SharingDialogData {
     refresh: () => void;
 }
 
-export const createSharingToken = async (dispatch: Dispatch, getState: () => RootState, { apiClientAuthorizationService }: ServiceRepository) => {
+export const createSharingToken = (expDate: Date | undefined) => async (dispatch: Dispatch, getState: () => RootState, { apiClientAuthorizationService }: ServiceRepository) => {
     const dialog = getDialog<SharingDialogData>(getState().dialog, SHARING_DIALOG_NAME);
     if (dialog) {
         const resourceUuid = dialog.data.resourceUuid;
         if (extractUuidObjectType(resourceUuid) === ResourceObjectType.COLLECTION) {
             dispatch(progressIndicatorActions.START_WORKING(SHARING_DIALOG_NAME));
             try {
-                const sharingToken = await apiClientAuthorizationService.createCollectionSharingToken(resourceUuid);
+                const sharingToken = await apiClientAuthorizationService.createCollectionSharingToken(resourceUuid, expDate);
                 dispatch(resourcesActions.SET_RESOURCES([sharingToken]));
                 dispatch(snackbarActions.OPEN_SNACKBAR({
                     message: 'Sharing URL created',

-----------------------------------------------------------------------


hooks/post-receive
-- 




More information about the arvados-commits mailing list