Skip to content

#1977: Move group and owner setting to management panel #2024

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions geonode_mapstore_client/client/js/api/geonode/v2/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -377,11 +377,13 @@ export const getUsers = ({
q,
page = 1,
pageSize = 20,
config,
...params
} = {}) => {
return axios.get(
getEndpointUrl(USERS),
{
...config,
params: {
...params,
...(q && {
Expand All @@ -406,11 +408,13 @@ export const getGroups = ({
q,
page = 1,
pageSize = 20,
config,
...params
} = {}) => {
return axios.get(
getEndpointUrl(GROUPS),
{
...config,
params: {
...params,
...(q && {
Expand All @@ -431,6 +435,11 @@ export const getGroups = ({
});
};

export const transferResource = (pk, body) => {
return axios.post(getEndpointUrl(USERS, `/${pk}/transfer_resources`), body)
.then(({ data }) => (data));
};

export const getUserByPk = (pk, apikey) => {
return axios.get(getEndpointUrl(USERS, `/${pk}`), {
params: {
Expand Down
101 changes: 99 additions & 2 deletions geonode_mapstore_client/client/js/epics/__tests__/gnsave-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import { SET_EDIT_PERMISSION } from '@mapstore/framework/actions/styleeditor';
import { configureMap } from '@mapstore/framework/actions/config';

import { selectNode, addLayer } from '@mapstore/framework/actions/layers';
import { START_ASYNC_PROCESS } from '@js/actions/resourceservice';


let mockAxios;
Expand All @@ -49,13 +50,101 @@ describe('gnsave epics', () => {
setTimeout(done);
});
it('should create new map with success (gnSaveContent)', (done) => {
const NUM_ACTIONS = 5;
const metadata = {
title: 'Title',
description: 'Description',
thumbnail: 'thumbnail.jpeg'
};
mockAxios.onPost().reply(() => [200, { map: {} }]);
mockAxios.onPut().reply(() => [200, { output: {} }]);
testEpic(
gnSaveContent,
NUM_ACTIONS,
saveContent(undefined, metadata, false),
(actions) => {
try {
expect(actions.map(({ type }) => type))
.toEqual([
SAVING_RESOURCE,
SAVE_SUCCESS,
SET_RESOURCE,
UPDATE_SINGLE_RESOURCE,
START_ASYNC_PROCESS
]);
} catch (e) {
done(e);
}
done();
},
{
gnresource: {
data: {
resource_type: "map"
},
isCompactPermissionsChanged: true,
compactPermissions: {
users: [],
organizations: [],
groups: []
}
}
}
);
});
it('should update existing map with success (gnSaveContent)', (done) => {
const NUM_ACTIONS = 5;
const id = 1;
const metadata = {
title: 'Title',
description: 'Description',
thumbnail: 'thumbnail.jpeg'
};
mockAxios.onPatch().reply(() => [200, {}]);
mockAxios.onPut().reply(() => [200, { output: {} }]);
testEpic(
gnSaveContent,
NUM_ACTIONS,
saveContent(id, metadata, false, false),
(actions) => {
try {
expect(actions.map(({ type }) => type))
.toEqual([
SAVING_RESOURCE,
SAVE_SUCCESS,
SET_RESOURCE,
UPDATE_SINGLE_RESOURCE,
START_ASYNC_PROCESS
]);
} catch (e) {
done(e);
}
done();
},
{
gnresource: {
data: {
resource_type: "map"
},
isCompactPermissionsChanged: true,
compactPermissions: {
users: [],
organizations: [],
groups: []
}
}
}
);
});
it('should skip permission update when permission is unchanged', (done) => {
const NUM_ACTIONS = 4;
const metadata = {
title: 'Title',
description: 'Description',
thumbnail: 'thumbnail.jpeg'
};
mockAxios.onPost().reply(() => [200, { map: {} }]);
mockAxios.onPut().reply(() => [200, { output: {} }]);
testEpic(
gnSaveContent,
NUM_ACTIONS,
Expand Down Expand Up @@ -110,7 +199,15 @@ describe('gnsave epics', () => {
},
{
gnresource: {
type: "map"
data: {
resource_type: "map"
},
isCompactPermissionsChanged: false,
compactPermissions: {
users: [],
organizations: [],
groups: []
}
}
}
);
Expand Down Expand Up @@ -170,7 +267,7 @@ describe('gnsave epics', () => {
'thumbnail_url': 'thumbnail.jpeg'
};
mockAxios.onGet(new RegExp(`resources/${pk}`))
.reply(200, resource);
.reply(200, {resource});
testEpic(
gnSaveDirectContent,
NUM_ACTIONS,
Expand Down
108 changes: 64 additions & 44 deletions geonode_mapstore_client/client/js/epics/gnsave.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import axios from '@mapstore/framework/libs/ajax';
import { Observable } from 'rxjs';
import get from 'lodash/get';
import castArray from 'lodash/castArray';
import isEqual from 'lodash/isEqual';

import { mapInfoSelector } from '@mapstore/framework/selectors/map';
import { userSelector } from '@mapstore/framework/selectors/security';
Expand Down Expand Up @@ -76,17 +77,18 @@ import {
cleanCompactPermissions,
toGeoNodeMapConfig,
RESOURCE_MANAGEMENT_PROPERTIES,
getDimensions
getDimensions,
GROUP_OWNER_PROPERTIES
} from '@js/utils/ResourceUtils';
import {
ProcessTypes,
ProcessStatus
} from '@js/utils/ResourceServiceUtils';
import { updateDatasetTimeSeries } from '@js/api/geonode/v2/index';
import { transferResource, updateDatasetTimeSeries } from '@js/api/geonode/v2/index';
import { updateNode } from '@mapstore/framework/actions/layers';
import { layersSelector } from '@mapstore/framework/selectors/layers';

const RESOURCE_MANAGEMENT_PROPERTIES_KEYS = Object.keys(RESOURCE_MANAGEMENT_PROPERTIES);
const RESOURCE_MANAGEMENT_PROPERTIES_KEYS = Object.keys({...RESOURCE_MANAGEMENT_PROPERTIES, ...GROUP_OWNER_PROPERTIES});

function parseMapBody(body) {
const geoNodeMap = toGeoNodeMapConfig(body.data);
Expand Down Expand Up @@ -169,11 +171,13 @@ export const gnSaveContent = (action$, store) =>
const contentType = state.gnresource?.type || currentResource?.resource_type;
const data = !currentResource?.['@ms-detail'] ? getDataPayload(state, contentType) : null;
const extent = getExtentPayload(state, contentType);
const { compactPermissions } = getPermissionsPayload(state);
const body = {
'title': action.metadata.name,
...(RESOURCE_MANAGEMENT_PROPERTIES_KEYS.reduce((acc, key) => {
if (currentResource?.[key] !== undefined) {
acc[key] = !!currentResource[key];
const value = typeof currentResource[key] === 'boolean' ? !!currentResource[key] : currentResource[key];
acc[key] = value;
}
return acc;
}, {})),
Expand All @@ -193,22 +197,34 @@ export const gnSaveContent = (action$, store) =>
window.location.reload();
return Observable.empty();
}
return Observable.of(
saveSuccess(resource),
setResource({
...currentResource,
...body,
...resource
}),
updateResource(resource),
...(action.showNotifications
? [
action.showNotifications === true
? successNotification({title: "saveDialog.saveSuccessTitle", message: "saveDialog.saveSuccessMessage"})
: warningNotification(action.showNotifications)
]
: []),
...actions // additional actions to be dispatched
return Observable.merge(
Observable.of(
saveSuccess(resource),
setResource({
...currentResource,
...body,
...resource
}),
updateResource(resource),
...(action.showNotifications
? [
action.showNotifications === true
? successNotification({title: "saveDialog.saveSuccessTitle", message: "saveDialog.saveSuccessMessage"})
: warningNotification(action.showNotifications)
]
: []),
...actions // additional actions to be dispatched
),
...(compactPermissions ? [
Observable.defer(() =>
updateCompactPermissionsByPk(action.id, cleanCompactPermissions(compactPermissions))
.then(output => ({ resource: currentResource, output, processType: ProcessTypes.PERMISSIONS_RESOURCE }))
.catch((error) => ({ resource: currentResource, error: error?.data?.detail || error?.statusText || error?.message || true, processType: ProcessTypes.PERMISSIONS_RESOURCE }))
)
.switchMap((payload) => {
return Observable.of(startAsyncProcess(payload));
})
] : [])
);
})
.catch((error) => {
Expand Down Expand Up @@ -264,22 +280,35 @@ export const gnSaveDirectContent = (action$, store) =>
const state = store.getState();
const mapInfo = mapInfoSelector(state);
const resourceId = mapInfo?.id || getResourceId(state);
const { compactPermissions, geoLimits } = getPermissionsPayload(state);
const { geoLimits } = getPermissionsPayload(state);
const currentResource = getResourceData(state);

return Observable.defer(() => axios.all([
getResourceByPk(resourceId),
...(geoLimits
? geoLimits.map((limits) =>
limits.features.length === 0
? deleteGeoLimits(resourceId, limits.id, limits.type)
.catch(() => ({ error: true, resourceId, limits }))
: updateGeoLimits(resourceId, limits.id, limits.type, { features: limits.features })
.catch(() => ({ error: true, resourceId, limits }))
)
: [])
]))
.switchMap(([resource, ...geoLimitsResponses]) => {
const newOwner = get(currentResource, 'owner.pk', null);
const currentOwner = get(state, 'gnresource.initialResource.owner.pk', null);
const userId = userSelector(state)?.pk;
let transferOwnership;
if (newOwner && userId && !isEqual(currentOwner, newOwner)) {
transferOwnership = { currentOwner, newOwner, resources: [Number(resourceId)] };
}

// resource information should be saved in a synchronous manner
// i.e transfer ownership (if any) followed by resource data and finally permissions
return Observable.concat(
transferOwnership ? Observable.defer(() => transferResource(userId, transferOwnership)) : Promise.resolve(),
Observable.defer(() => axios.all([
getResourceByPk(resourceId),
...(geoLimits
? geoLimits.map((limits) =>
limits.features.length === 0
? deleteGeoLimits(resourceId, limits.id, limits.type)
.catch(() => ({ error: true, resourceId, limits }))
: updateGeoLimits(resourceId, limits.id, limits.type, { features: limits.features })
.catch(() => ({ error: true, resourceId, limits }))
)
: [])
]))).toArray()
.switchMap(([, responses]) => {
const [resource, ...geoLimitsResponses] = responses;
const geoLimitsErrors = geoLimitsResponses.filter(({ error }) => error);
const name = getResourceName(state);
const description = getResourceDescription(state);
Expand All @@ -290,16 +319,6 @@ export const gnSaveDirectContent = (action$, store) =>
href: resource?.href
};
return Observable.concat(
...(compactPermissions ? [
Observable.defer(() =>
updateCompactPermissionsByPk(resourceId, cleanCompactPermissions(compactPermissions))
.then(output => ({ resource: currentResource, output, processType: ProcessTypes.PERMISSIONS_RESOURCE }))
.catch((error) => ({ resource: currentResource, error: error?.data?.detail || error?.statusText || error?.message || true, processType: ProcessTypes.PERMISSIONS_RESOURCE }))
)
.switchMap((payload) => {
return Observable.of(startAsyncProcess(payload));
})
] : []),
Observable.of(
saveContent(
resourceId,
Expand All @@ -313,6 +332,7 @@ export const gnSaveDirectContent = (action$, store) =>
: true /* showNotification */),
resetGeoLimits()
)

);
})
.catch((error) => {
Expand Down
Loading