Skip to content
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

Do not allow project viewer to update entity or mark it as resolved #896

Merged
merged 2 commits into from
Nov 28, 2023
Merged
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
7 changes: 3 additions & 4 deletions src/components/entity/conflict-summary.vue
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ except according to the terms contained in the LICENSE file.
<div class="panel-body">
<entity-conflict-table v-if="entityVersions.dataExists"
:uuid="entity.uuid" :versions="relevantToConflict"/>
<div class="panel-footer">
<div v-if="project.permits('entity.update')" class="panel-footer">
<span class="icon-arrow-circle-right"></span>
<p>
{{ $t('footer[0]') }}
Expand Down Expand Up @@ -63,9 +63,7 @@ import { useRequestData } from '../../request-data';

const { request, awaitingResponse } = useRequest();
const { t } = useI18n();
// The component does not assume that this data will exist when the component is
// created.
const { dataset, entityVersions } = useRequestData();
const { project, dataset, entityVersions } = useRequestData();
const { alert } = inject('container');

defineOptions({
Expand Down Expand Up @@ -150,6 +148,7 @@ const markAsResolved = () => {
#entity-conflict-table {
margin-left: -15px;
margin-right: -15px;
&:last-child { margin-bottom: -15px; }

// Align the leftmost text of the first column with the icon in the
// .panel-heading.
Expand Down
41 changes: 28 additions & 13 deletions src/components/entity/resolve.vue
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,12 @@ except according to the terms contained in the LICENSE file.
<template #title>{{ $t('title', entity) }}</template>
<template #body>
<div v-if="!success">
<p>{{ $t('instructions[0]', entity) }}</p>
<p>{{ $t('instructions[1]', { markAsResolved: $t('action.markAsResolved') }) }}</p>
<div class="modal-introduction">
<p>{{ $t('instructions[0]', entity) }}</p>
<p v-if="canUpdate">
{{ $t('instructions[1]', { markAsResolved: $t('action.markAsResolved') }) }}
</p>
</div>

<div v-show="tableShown" id="entity-resolve-table-container">
<loading :state="entityVersions.awaitingResponse"/>
Expand All @@ -41,12 +45,14 @@ except according to the terms contained in the LICENSE file.
:aria-disabled="awaitingResponse" target="_blank">
<span class="icon-external-link-square"></span>{{ $t('action.seeMoreDetails') }}
</router-link>
<button type="button" class="btn btn-default edit-entity" :aria-disabled="awaitingResponse" @click="$emit('hide', true)">
<span class="icon-pencil"></span>{{ $t('action.editEntity') }}
</button>
<button type="button" class="btn btn-default mark-as-resolved" :aria-disabled="awaitingResponse" @click="markAsResolve">
<span class="icon-check"></span>{{ $t('action.markAsResolved') }} <spinner :state="awaitingResponse"/>
</button>
<template v-if="canUpdate">
<button type="button" class="btn btn-default edit-entity" :aria-disabled="awaitingResponse" @click="$emit('hide', true)">
<span class="icon-pencil"></span>{{ $t('action.editEntity') }}
</button>
<button type="button" class="btn btn-default mark-as-resolved" :aria-disabled="awaitingResponse" @click="markAsResolve">
<span class="icon-check"></span>{{ $t('action.markAsResolved') }} <spinner :state="awaitingResponse"/>
</button>
</template>
</div>
<div v-else class="success-msg">
<span class="icon-check-circle success"></span> {{ $t('successMessage') }}
Expand All @@ -62,7 +68,7 @@ except according to the terms contained in the LICENSE file.
</template>

<script setup>
import { inject, nextTick, ref, watch } from 'vue';
import { computed, inject, nextTick, ref, watch } from 'vue';
import { useI18n } from 'vue-i18n';

import EntityConflictTable from './conflict-table.vue';
Expand All @@ -73,6 +79,7 @@ import Spinner from '../spinner.vue';
import useEntityVersions from '../../request-data/entity-versions';
import useRequest from '../../composables/request';
import useRoutes from '../../composables/routes';
import { useRequestData } from '../../request-data';
import { apiPaths } from '../../util/request';
import { noop } from '../../util/util';

Expand All @@ -85,8 +92,15 @@ const props = defineProps({
});
const emit = defineEmits(['hide', 'success']);

// Conflict summary table
// The component does not assume that this data will exist when the component is
// created.
const { project } = useRequestData();
const entityVersions = useEntityVersions();

const canUpdate = computed(() =>
project.dataExists && project.permits('entity.update'));

// Conflict summary table
const projectId = inject('projectId');
const datasetName = inject('datasetName');
const alert = inject('alert');
Expand Down Expand Up @@ -156,6 +170,7 @@ watch(() => props.entity, (entity) => {

#entity-resolve {
.modal-dialog { margin-top: 15vh; }
.modal-introduction { margin-bottom: 12px; }

.btn + .btn {
margin-left: 10px;
Expand All @@ -173,11 +188,11 @@ watch(() => props.entity, (entity) => {
margin-left: -$padding-modal-body;
margin-right: -$padding-modal-body;
// If the height of the modal content other than the table is no more than
// 375px, this allows the table to push the modal to 75vh tall. After that,
// 365px, this allows the table to push the modal to 75vh tall. After that,
// the table container will scroll.
max-height: max(calc(75vh - 375px), 175px);
max-height: max(calc(75vh - 365px), 175px);
overflow-y: auto;
padding-top: 5px;
padding-top: 3px;
}
#entity-resolve #entity-conflict-table {
tbody tr { background-color: transparent; }
Expand Down
2 changes: 1 addition & 1 deletion src/components/entity/show.vue
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ except according to the terms contained in the LICENSE file.
<entity-data @update="update.state = true"/>
</div>
<div class="col-xs-8">
<entity-conflict-summary v-if="entity.data?.conflict" :entity="entity" @resolve="afterResolve"/>
<entity-conflict-summary v-if="project.dataExists && entity.data?.conflict" :entity="entity" @resolve="afterResolve"/>
<entity-activity/>
</div>
</div>
Expand Down
28 changes: 20 additions & 8 deletions test/components/entity/conflict-summary.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import useEntityVersions from '../../../src/request-data/entity-versions';
import testData from '../../data';
import { mergeMountOptions, mount } from '../../util/lifecycle';
import { mockHttp } from '../../util/http';
import { mockLogin } from '../../util/session';
import { mockRouter } from '../../util/router';
import { testRequestData } from '../../util/request-data';

Expand All @@ -18,6 +19,7 @@ const mountOptions = (options = undefined) => {
},
container: {
requestData: testRequestData([useEntityVersions], {
project: testData.extendedProjects.last(),
dataset: testData.extendedDatasets.last(),
entityVersions: testData.extendedEntityVersions.sorted()
}),
Expand All @@ -29,17 +31,28 @@ const mountComponent = (options = undefined) =>
mount(EntityConflictSummary, mountOptions(options));

describe('EntityConflictSummary', () => {
it('show the confirmation modal', async () => {
it('does not show the footer to a project viewer', () => {
mockLogin({ role: 'none' });
testData.extendedProjects.createPast(1, { role: 'viewer', datasets: 1 });
testData.extendedEntities.createPast(1);
const component = await mountComponent();
testData.extendedEntityVersions.createPast(2, { baseVersion: 1 });
const component = mountComponent();
component.find('.panel-footer').exists().should.be.false();
component.find('.btn-default').exists().should.be.false();
});

it('show the confirmation modal', async () => {
testData.extendedEntities.createPast(1);
testData.extendedEntityVersions.createPast(2, { baseVersion: 1 });
const component = mountComponent();
component.getComponent(Confirmation).props().state.should.be.false();
await component.get('.btn-default').trigger('click');
component.getComponent(Confirmation).props().state.should.be.true();
});

it('sends the correct request', async () => {
testData.extendedEntities.createPast(1, { uuid: 'e' });
testData.extendedEntityVersions.createPast(2, { baseVersion: 1 });
return mockHttp()
.mount(EntityConflictSummary, mountOptions())
.request(async (component) => {
Expand All @@ -50,7 +63,7 @@ describe('EntityConflictSummary', () => {
.respondWithProblem()
.testRequests([{
method: 'PATCH',
url: '/v1/projects/1/datasets/trees/entities/e?resolve=true&baseVersion=1',
url: '/v1/projects/1/datasets/trees/entities/e?resolve=true&baseVersion=3',
}]);
});

Expand All @@ -70,29 +83,28 @@ describe('EntityConflictSummary', () => {

it('should emit resolve', async () => {
testData.extendedEntities.createPast(1);

testData.extendedEntityVersions.createPast(2, { baseVersion: 1 });
const component = await resolve();

component.emitted().should.have.property('resolve');
});

it('should show alert', async () => {
testData.extendedEntities.createPast(1);

testData.extendedEntityVersions.createPast(2, { baseVersion: 1 });
const component = await resolve();
component.should.alert('success');
});

it('should hide confirmation modal', async () => {
testData.extendedEntities.createPast(1);

testData.extendedEntityVersions.createPast(2, { baseVersion: 1 });
const component = await resolve();

component.getComponent(Confirmation).props().state.should.be.false();
});

it('shows conflict error', () => {
testData.extendedEntities.createPast(1);
testData.extendedEntityVersions.createPast(2, { baseVersion: 1 });
return mockHttp()
.mount(EntityConflictSummary, mountOptions())
.request(async (component) => {
Expand Down
24 changes: 23 additions & 1 deletion test/components/entity/resolve.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import EntityResolve from '../../../src/components/entity/resolve.vue';

import testData from '../../data';
import { mockHttp } from '../../util/http';
import { mockLogin } from '../../util/session';
import { mockRouter } from '../../util/router';

const relevantToConflict = () => testData.extendedEntityVersions.sorted()
Expand All @@ -16,7 +17,7 @@ const showModal = () => {
provide: { projectId: '1', datasetName: dataset.name }
},
container: {
requestData: { dataset },
requestData: { project: testData.extendedProjects.last(), dataset },
router: mockRouter(`/projects/1/entity-lists/${encodeURIComponent(dataset.name)}/entities`)
}
})
Expand Down Expand Up @@ -138,4 +139,25 @@ describe('EntityResolve', () => {
});
});
});

describe('project viewer', () => {
beforeEach(() => {
mockLogin({ role: 'none' });
testData.extendedProjects.createPast(1, { role: 'viewer', datasets: 1 });
testData.extendedEntities.createPast(1);
testData.extendedEntityVersions.createPast(2, { baseVersion: 1 });
});

it('does not show the second paragraph', async () => {
const modal = await showModal();
modal.findAll('p').length.should.equal(1);
});

it('does not show the update or resolve buttons', async () => {
const modal = await showModal();
const btns = modal.findAll('#entity-resolve-table-toggle ~ .btn');
btns.length.should.equal(1);
btns[0].classes('more-details').should.be.true();
});
});
});