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

Request entity versions in EntityShow, not diffs #880

Merged
merged 1 commit into from
Oct 27, 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
12 changes: 7 additions & 5 deletions src/components/entity/activity.vue
Original file line number Diff line number Diff line change
Expand Up @@ -39,16 +39,18 @@ defineOptions({

// The component does not assume that this data will exist when the component is
// created.
const { audits, diffs, resourceStates } = useRequestData();
const { initiallyLoading, dataExists } = resourceStates([audits, diffs]);
const { audits, entityVersions, resourceStates } = useRequestData();
const { initiallyLoading, dataExists } = resourceStates([audits, entityVersions]);

const feed = computed(() => {
const result = [];
let diffIndex = diffs.length - 1;
let versionIndex = entityVersions.length - 1;
for (const audit of audits) {
if (audit.action === 'entity.update.version') {
result.push({ entry: audit, diff: diffs[diffIndex], submission: audit.details.submission });
diffIndex -= 1;
const { submission } = audit.details;
const entityVersion = entityVersions[versionIndex];
versionIndex -= 1;
result.push({ entry: audit, submission, entityVersion });
} else if (audit.action === 'entity.create') {
result.push({ entry: audit });
const { details } = audit;
Expand Down
46 changes: 46 additions & 0 deletions src/components/entity/diff.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<!--
Copyright 2023 ODK Central Developers
See the NOTICE file at the top-level directory of this distribution and at
https://github.com/getodk/central-frontend/blob/master/NOTICE.

This file is part of ODK Central. It is subject to the license terms in
the LICENSE file found in the top-level directory of this distribution and at
https://www.apache.org/licenses/LICENSE-2.0. No part of ODK Central,
including this file, may be copied, modified, propagated, or distributed
except according to the terms contained in the LICENSE file.
-->
<template>
<div>
<diff-item v-for="key of entityVersion.serverDiff" :key="key" :path="[key]"
:old="propOrLabel(oldVersion, key)"
:new="propOrLabel(entityVersion, key)"/>
</div>
</template>

<script>
const propOrLabel = (version, key) =>
(key === 'label' ? version.label : version.data[key]);
</script>

<script setup>
import { computed } from 'vue';

import DiffItem from '../diff-item.vue';
import { useRequestData } from '../../request-data';

defineOptions({
name: 'EntityDiff'
});
const props = defineProps({
entityVersion: {
type: Object,
required: true
}
});

// The component assumes that this data will exist when the component is
// created.
const { entityVersions } = useRequestData();
const oldVersion = computed(() =>
entityVersions[props.entityVersion.version - 2]);
</script>
9 changes: 3 additions & 6 deletions src/components/entity/feed-entry.vue
Original file line number Diff line number Diff line change
Expand Up @@ -86,10 +86,7 @@ except according to the terms contained in the LICENSE file.
</template>
</template>
<template #body>
<template v-if="diff != null">
<diff-item v-for="change of diff" :key="change.propertyName"
:path="[change.propertyName]" :old="change.old" :new="change.new"/>
</template>
<entity-diff v-if="entityVersion != null" :entity-version="entityVersion"/>
</template>
</feed-entry>
</template>
Expand All @@ -99,7 +96,7 @@ import { computed, inject } from 'vue';
import { useI18n } from 'vue-i18n';

import ActorLink from '../actor-link.vue';
import DiffItem from '../diff-item.vue';
import EntityDiff from './diff.vue';
import FeedEntry from '../feed-entry.vue';

import useReviewState from '../../composables/review-state';
Expand All @@ -115,7 +112,7 @@ const props = defineProps({
required: true
},
submission: Object,
diff: Array
entityVersion: Object
});
const projectId = inject('projectId');
const datasetName = inject('datasetName');
Expand Down
7 changes: 4 additions & 3 deletions src/components/entity/show.vue
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ provide('projectId', props.projectId);
provide('datasetName', props.datasetName);

const { project, dataset } = useRequestData();
const { entity, audits, diffs } = useEntity();
const { entity, audits, entityVersions } = useEntity();

Promise.allSettled([
entity.request({
Expand All @@ -96,8 +96,9 @@ const fetchActivityData = () => Promise.allSettled([
audits.request({
url: apiPaths.entityAudits(props.projectId, props.datasetName, props.uuid)
}),
diffs.request({
url: apiPaths.entityDiffs(props.projectId, props.datasetName, props.uuid)
entityVersions.request({
url: apiPaths.entityVersions(props.projectId, props.datasetName, props.uuid),
extended: true
})
]);
fetchActivityData();
Expand Down
4 changes: 2 additions & 2 deletions src/request-data/entity.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,6 @@ export default () => {
transformResponse: ({ data }) => reactive(data)
}));
const audits = createResource('audits');
const diffs = createResource('diffs');
return { entity, audits, diffs };
const entityVersions = createResource('entityVersions');
return { entity, audits, entityVersions };
};
2 changes: 1 addition & 1 deletion src/util/request.js
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ export const apiPaths = {
odataEntities: datasetPath('.svc/Entities'),
entity: entityPath(''),
entityAudits: entityPath('/audits'),
entityDiffs: entityPath('/diffs'),
entityVersions: entityPath('/versions'),
fieldKeys: projectPath('/app-users'),
serverUrlForFieldKey: (token, projectId) =>
`/v1/key/${token}/projects/${projectId}`,
Expand Down
2 changes: 1 addition & 1 deletion test/components/entity/activity.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ const mountComponent = () => mount(EntityActivity, {
entity: testData.extendedEntities.last(),
audits: testData.extendedAudits.sorted()
.filter(({ action }) => action.startsWith('entity.')),
diffs: []
entityVersions: testData.extendedEntityVersions.sorted()
}),
router: mockRouter('/projects/1/entity-lists/trees/entities/e')
}
Expand Down
56 changes: 56 additions & 0 deletions test/components/entity/diff.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import DiffItem from '../../../src/components/diff-item.vue';
import EntityDiff from '../../../src/components/entity/diff.vue';

import useEntity from '../../../src/request-data/entity';

import testData from '../../data';
import { mount } from '../../util/lifecycle';
import { testRequestData } from '../../util/request-data';

const mountComponent = () => mount(EntityDiff, {
props: { entityVersion: testData.extendedEntityVersions.last() },
container: {
requestData: testRequestData([useEntity], {
entityVersions: testData.extendedEntityVersions.sorted()
})
}
});

describe('EntityDiff', () => {
it('renders a DiffItem for each change', () => {
testData.extendedEntities.createPast(1, {
label: 'dogwood',
data: { height: '1', circumference: '2' }
});
testData.extendedEntityVersions.createPast(1, {
label: 'Dogwood',
data: { height: '3', circumference: '4' }
});
const component = mountComponent();
component.findAllComponents(DiffItem).length.should.equal(3);
});

it('passes the correct props to the DiffItem for a property change', () => {
testData.extendedEntities.createPast(1, {
data: { height: '1' }
});
testData.extendedEntityVersions.createPast(1, {
data: { height: '2' }
});
const component = mountComponent();
const props = component.getComponent(DiffItem).props();
props.new.should.equal('2');
props.old.should.equal('1');
props.path.should.eql(['height']);
});

it('passes the correct props to the DiffItem for a label change', () => {
testData.extendedEntities.createPast(1, { label: 'dogwood' });
testData.extendedEntityVersions.createPast(1, { label: 'Dogwood' });
const component = mountComponent();
const props = component.getComponent(DiffItem).props();
props.new.should.equal('Dogwood');
props.old.should.equal('dogwood');
props.path.should.eql(['label']);
});
});
70 changes: 32 additions & 38 deletions test/components/entity/feed-entry.spec.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { RouterLinkStub } from '@vue/test-utils';

import ActorLink from '../../../src/components/actor-link.vue';
import DiffItem from '../../../src/components/diff-item.vue';
import EntityDiff from '../../../src/components/entity/diff.vue';
import EntityFeedEntry from '../../../src/components/entity/feed-entry.vue';
import FeedEntry from '../../../src/components/feed-entry.vue';

Expand All @@ -19,11 +19,17 @@ const mountComponent = (options) =>
global: {
provide: { projectId: '1', datasetName: 'trees' }
},
props: { entry: testData.extendedAudits.last() },
props: {
entry: testData.extendedAudits.last(),
entityVersion: testData.extendedEntityVersions.size > 1
? testData.extendedEntityVersions.last()
: null
},
container: {
router: mockRouter('/projects/1/entity-lists/trees/entities/e'),
requestData: testRequestData([useEntity], {
entity: testData.extendedEntities.last()
entity: testData.extendedEntities.last(),
entityVersions: testData.extendedEntityVersions.sorted()
})
}
}));
Expand Down Expand Up @@ -203,53 +209,32 @@ describe('EntityFeedEntry', () => {
});

describe('entity.update.version (via API) audit event', () => {
const updateEntity = () => {
const audit = testData.extendedAudits
.createPast(1, { action: 'entity.update.version', details: {} })
.last();
return { entry: audit, diff: [] };
};
beforeEach(() => {
testData.extendedEntityVersions.createPast(1);
testData.extendedAudits.createPast(1, {
action: 'entity.update.version',
details: {}
});
});

it('shows the correct icon', () => {
const component = mountComponent({ props: updateEntity() });
const component = mountComponent();
const icon = component.find('.feed-entry-title .icon-pencil');
icon.exists().should.be.true();
});

it('shows the correct text', () => {
const component = mountComponent({ props: updateEntity() });
const component = mountComponent();
const text = component.get('.feed-entry-title').text();
text.should.equal('Data updated by Alice');
});

it('links to the user', () => {
const component = mountComponent({ props: updateEntity() });
const component = mountComponent();
const title = component.get('.feed-entry-title');
const actorLink = title.getComponent(ActorLink);
actorLink.props().actor.displayName.should.equal('Alice');
});

it('renders a DiffItem for each change', () => {
const diff = [
{ new: '1', old: '10', propertyName: 'height' },
{ new: '2', old: '20', propertyName: 'circumference' }
];
const component = mountComponent({
props: { ...updateEntity(), diff }
});
component.findAllComponents(DiffItem).length.should.equal(2);
});

it('passes the correct props to the DiffItem', () => {
const diff = [{ new: '1', old: '10', propertyName: 'height' }];
const component = mountComponent({
props: { ...updateEntity(), diff }
});
const props = component.getComponent(DiffItem).props();
props.new.should.equal('1');
props.old.should.equal('10');
props.path.should.eql(['height']);
});
});

describe('entity.update.version (via submission) audit event', () => {
Expand All @@ -269,6 +254,7 @@ describe('EntityFeedEntry', () => {
.createPast(1, { instanceId: 's', ...options })
.last();

testData.extendedEntityVersions.createPast(1);
const audit = testData.extendedAudits
.createPast(1, {
action: 'entity.update.version',
Expand All @@ -278,7 +264,6 @@ describe('EntityFeedEntry', () => {

return {
entry: audit,
diffs: [],
submission: deleted ? null : { ...submission, xmlFormId: 'f' }
};
};
Expand Down Expand Up @@ -324,13 +309,22 @@ describe('EntityFeedEntry', () => {
});
});

it('renders a diff for an entity.update.version event', () => {
testData.extendedEntityVersions.createPast(1);
testData.extendedAudits.createPast(1, {
action: 'entity.update.version',
details: {}
});
const diff = mountComponent().getComponent(EntityDiff);
diff.props().entityVersion.version.should.equal(2);
});

it('shows when an audit log event was logged', () => {
testData.extendedEntityVersions.createPast(1);
const audit = testData.extendedAudits
.createPast(1, { action: 'entity.update.version', details: {} })
.last();
const component = mountComponent({
props: { entry: audit, diff: [] }
});
const component = mountComponent();
component.getComponent(FeedEntry).props().iso.should.equal(audit.loggedAt);
});

Expand Down
4 changes: 2 additions & 2 deletions test/components/entity/show.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ describe('EntityShow', () => {
{ url: '/v1/projects/1', extended: true },
{ url: '/v1/projects/1/datasets/%C3%A1', extended: true },
{ url: '/v1/projects/1/datasets/%C3%A1/entities/e/audits' },
{ url: '/v1/projects/1/datasets/%C3%A1/entities/e/diffs' }
{ url: '/v1/projects/1/datasets/%C3%A1/entities/e/versions', extended: true }
]);
});

Expand Down Expand Up @@ -97,7 +97,7 @@ describe('EntityShow', () => {
submit().testRequests([
null,
{ url: '/v1/projects/1/datasets/%C3%A1/entities/e/audits' },
{ url: '/v1/projects/1/datasets/%C3%A1/entities/e/diffs' }
{ url: '/v1/projects/1/datasets/%C3%A1/entities/e/versions', extended: true }
]));

it('hides the modal', async () => {
Expand Down
6 changes: 3 additions & 3 deletions test/unit/request.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -333,9 +333,9 @@ describe('util/request', () => {
path.should.equal('/v1/projects/1/datasets/%C3%A1/entities/e/audits');
});

it('entityDiffs', () => {
const path = apiPaths.entityDiffs(1, 'á', 'e');
path.should.equal('/v1/projects/1/datasets/%C3%A1/entities/e/diffs');
it('entityVersions', () => {
const path = apiPaths.entityVersions(1, 'á', 'e');
path.should.equal('/v1/projects/1/datasets/%C3%A1/entities/e/versions');
});

it('fieldKeys', () => {
Expand Down
2 changes: 1 addition & 1 deletion test/util/http/data.js
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ const responsesByComponent = {
dataset: true,
audits: () => testData.extendedAudits.sorted()
.filter(({ action }) => action.startsWith('entity.')),
diffs: () => []
entityVersions: () => testData.extendedEntityVersions.sorted()
}),

UserHome: [],
Expand Down