Skip to content
This repository was archived by the owner on Feb 26, 2025. It is now read-only.

Commit f70c0e8

Browse files
kenjinpjulienmachon
authored andcommitted
Bugfix/793 disable deprecate for older resources (#406)
* check to see if resource is lates before adding deprecate button * remove log, add better error message * clean up code a bit * add comments for ActionType * rephrase error message
1 parent 5626fb5 commit f70c0e8

File tree

3 files changed

+137
-64
lines changed

3 files changed

+137
-64
lines changed
Lines changed: 46 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,21 @@
11
import * as React from 'react';
2-
import { Tooltip, Button, Popconfirm } from 'antd';
3-
import './ResourceActions.less';
4-
import {
5-
isFile,
6-
chainPredicates,
7-
not,
8-
isDefaultElasticView,
9-
isDeprecated,
10-
isView,
11-
} from '../../utils/nexusMaybe';
2+
import { Tooltip, Button, Popconfirm, notification } from 'antd';
123
import { Resource } from '@bbp/nexus-sdk';
134

14-
const actionTypes = [
15-
{
16-
name: 'deprecateResource',
17-
predicate: chainPredicates([isDefaultElasticView, not(isDeprecated)]),
18-
title: 'Deprecate this resource',
19-
shortTitle: 'Dangerously Deprecate',
20-
message: (
21-
<div>
22-
<h3>Warning!</h3>
23-
<p>
24-
Deprecating this resource <em>WILL ABSOLUTELY</em> break this
25-
application for this project. Are you sure you want to deprecate it?
26-
</p>
27-
</div>
28-
),
29-
icon: 'delete',
30-
danger: true,
31-
},
32-
{
33-
name: 'deprecateResource',
34-
predicate: chainPredicates([not(isDeprecated), not(isDefaultElasticView)]),
35-
title: 'Deprecate this resource',
36-
message: "Are you sure you'd like to deprecate this resource?",
37-
shortTitle: 'Deprecate',
38-
icon: 'delete',
39-
danger: true,
40-
},
41-
{
42-
name: 'goToView',
43-
predicate: isView,
44-
title: 'Query this view',
45-
shortTitle: 'Query',
46-
icon: 'search',
47-
},
48-
{
49-
name: 'downloadFile',
50-
predicate: isFile,
51-
title: 'Download this file',
52-
shortTitle: 'Download',
53-
icon: 'download',
54-
},
55-
];
5+
import './ResourceActions.less';
6+
7+
export type ActionType = {
8+
name: string; // A unique name for your action type
9+
// predicate: This function will be called with the resource passed
10+
// to test if we want to display this Action Button
11+
predicate: (resource: Resource) => Promise<boolean>;
12+
title: string; // A long title displayed on the confirm popup or tooltip
13+
shortTitle: string; // Displayed on Button
14+
// message: a longer message to be displayed on on the confirmation popup
15+
message?: React.ReactElement | string;
16+
icon: string; // An icon for the button
17+
danger?: boolean; // should we use a confirmation popup and color the button red?
18+
};
5619

5720
const makeButton = ({
5821
title,
@@ -94,30 +57,50 @@ const makeButton = ({
9457
</div>
9558
);
9659

97-
const makeActions = (
60+
const makeActionButtons = async (
9861
resource: Resource,
9962
actionDispatchers: {
10063
[key: string]: () => void;
101-
}
102-
) =>
103-
actionTypes
104-
.filter(action => action.predicate(resource))
64+
},
65+
actionTypes: ActionType[]
66+
) => {
67+
const appliedActions = await Promise.all(
68+
actionTypes.map(async action => {
69+
return await action.predicate(resource);
70+
})
71+
);
72+
return actionTypes
73+
.filter((action, index) => appliedActions[index])
10574
.map(action =>
10675
makeButton(action)(resource, actionDispatchers[action.name])
10776
);
77+
};
10878

10979
const ResourceActions: React.FunctionComponent<{
11080
resource: Resource;
11181
actions: {
11282
[key: string]: () => void;
11383
};
84+
actionTypes: ActionType[];
11485
}> = props => {
115-
const { resource, actions } = props;
116-
return (
117-
<section className="resource-actions">
118-
{makeActions(resource, actions)}
119-
</section>
120-
);
86+
const { resource, actions, actionTypes } = props;
87+
const [actionButtons, setActionButtons] = React.useState<
88+
React.ReactElement[]
89+
>([]);
90+
91+
React.useEffect(() => {
92+
makeActionButtons(resource, actions, actionTypes)
93+
.then(setActionButtons)
94+
.catch((error: Error) => {
95+
notification.error({
96+
message:
97+
'There was an error while fetching information about this resource',
98+
description: error.message,
99+
});
100+
});
101+
}, [resource._self, resource._rev]);
102+
103+
return <section className="resource-actions">{actionButtons}</section>;
121104
};
122105

123106
export default ResourceActions;

src/shared/containers/ResourceActions.tsx

Lines changed: 86 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,15 @@ import { useNexusContext } from '@bbp/react-nexus';
88
import ResourceActions from '../components/ResourceActions';
99
import { getResourceLabelsAndIdsFromSelf, getResourceLabel } from '../utils';
1010
import { download } from '../utils/download';
11+
import {
12+
isFile,
13+
chainPredicates,
14+
not,
15+
isDefaultElasticView,
16+
isDeprecated,
17+
isView,
18+
toPromise,
19+
} from '../utils/nexusMaybe';
1120

1221
const ResourceActionsContainer: React.FunctionComponent<{
1322
resource: Resource;
@@ -31,6 +40,76 @@ const ResourceActionsContainer: React.FunctionComponent<{
3140
} = getResourceLabelsAndIdsFromSelf(resource._self);
3241
const nexus = useNexusContext();
3342

43+
const isLatestResource = async (resource: Resource) => {
44+
// TODO: remove this if / when
45+
// https://github.com/BlueBrain/nexus/issues/898 is implemented
46+
const latest = await nexus.httpGet({
47+
path: resource._self,
48+
headers: {
49+
Accept: 'application/json', // just in case it's a file
50+
},
51+
});
52+
return resource._rev === latest._rev;
53+
};
54+
55+
const actionTypes = [
56+
{
57+
name: 'deprecateResource',
58+
predicate: async (resource: Resource) => {
59+
const isLatest = await isLatestResource(resource);
60+
return (
61+
isLatest &&
62+
chainPredicates([isDefaultElasticView, not(isDeprecated)])(resource)
63+
);
64+
},
65+
title: 'Deprecate this resource',
66+
shortTitle: 'Dangerously Deprecate',
67+
message: (
68+
<div>
69+
<h3>Warning!</h3>
70+
<p>
71+
This is your default ElasticSearch View. Deprecating this resource
72+
will break this application for this project. Are you sure you want
73+
to deprecate it?
74+
</p>
75+
</div>
76+
),
77+
icon: 'delete',
78+
danger: true,
79+
},
80+
{
81+
name: 'deprecateResource',
82+
predicate: async (resource: Resource) => {
83+
const isLatest = await isLatestResource(resource);
84+
return (
85+
isLatest &&
86+
chainPredicates([not(isDeprecated), not(isDefaultElasticView)])(
87+
resource
88+
)
89+
);
90+
},
91+
title: 'Deprecate this resource',
92+
message: "Are you sure you'd like to deprecate this resource?",
93+
shortTitle: 'Deprecate',
94+
icon: 'delete',
95+
danger: true,
96+
},
97+
{
98+
name: 'goToView',
99+
predicate: toPromise(isView),
100+
title: 'Query this view',
101+
shortTitle: 'Query',
102+
icon: 'search',
103+
},
104+
{
105+
name: 'downloadFile',
106+
predicate: toPromise(isFile),
107+
title: 'Download this file',
108+
shortTitle: 'Download',
109+
icon: 'download',
110+
},
111+
];
112+
34113
const actions = {
35114
deprecateResource: async () => {
36115
try {
@@ -78,7 +157,13 @@ const ResourceActionsContainer: React.FunctionComponent<{
78157
},
79158
};
80159

81-
return <ResourceActions resource={resource} actions={actions} />;
160+
return (
161+
<ResourceActions
162+
resource={resource}
163+
actions={actions}
164+
actionTypes={actionTypes}
165+
/>
166+
);
82167
};
83168

84169
const mapDispatchToProps = (dispatch: any) => ({

src/shared/utils/nexusMaybe.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,8 @@ export const isDefaultElasticView = chainPredicates([
3636
]);
3737
export const isSparqlView = isOfType(SPARQL_VIEW);
3838
export const isFile = isOfType(NEXUS_FILE_TYPE);
39+
export const toPromise = (func: Function) => {
40+
return function() {
41+
return Promise.resolve(func(arguments));
42+
};
43+
};

0 commit comments

Comments
 (0)