diff --git a/src/components/entity/activity.vue b/src/components/entity/activity.vue index bd69273ae..a63b94ad6 100644 --- a/src/components/entity/activity.vue +++ b/src/components/entity/activity.vue @@ -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; diff --git a/src/components/entity/diff.vue b/src/components/entity/diff.vue new file mode 100644 index 000000000..b3825e158 --- /dev/null +++ b/src/components/entity/diff.vue @@ -0,0 +1,46 @@ + + + + + + diff --git a/src/components/entity/feed-entry.vue b/src/components/entity/feed-entry.vue index 615ec9f59..07ce2fc0a 100644 --- a/src/components/entity/feed-entry.vue +++ b/src/components/entity/feed-entry.vue @@ -86,10 +86,7 @@ except according to the terms contained in the LICENSE file. @@ -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'; @@ -115,7 +112,7 @@ const props = defineProps({ required: true }, submission: Object, - diff: Array + entityVersion: Object }); const projectId = inject('projectId'); const datasetName = inject('datasetName'); diff --git a/src/components/entity/show.vue b/src/components/entity/show.vue index 7d07f0f3b..b32b6a800 100644 --- a/src/components/entity/show.vue +++ b/src/components/entity/show.vue @@ -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({ @@ -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(); diff --git a/src/request-data/entity.js b/src/request-data/entity.js index b48cc937e..77d07427d 100644 --- a/src/request-data/entity.js +++ b/src/request-data/entity.js @@ -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 }; }; diff --git a/src/util/request.js b/src/util/request.js index 8a41b7200..2d3148eaa 100644 --- a/src/util/request.js +++ b/src/util/request.js @@ -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}`, diff --git a/test/components/entity/activity.spec.js b/test/components/entity/activity.spec.js index b93aaa35a..765d683de 100644 --- a/test/components/entity/activity.spec.js +++ b/test/components/entity/activity.spec.js @@ -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') } diff --git a/test/components/entity/diff.spec.js b/test/components/entity/diff.spec.js new file mode 100644 index 000000000..2859cd0b0 --- /dev/null +++ b/test/components/entity/diff.spec.js @@ -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']); + }); +}); diff --git a/test/components/entity/feed-entry.spec.js b/test/components/entity/feed-entry.spec.js index 1271b02e5..17c4441e8 100644 --- a/test/components/entity/feed-entry.spec.js +++ b/test/components/entity/feed-entry.spec.js @@ -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'; @@ -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() }) } })); @@ -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', () => { @@ -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', @@ -278,7 +264,6 @@ describe('EntityFeedEntry', () => { return { entry: audit, - diffs: [], submission: deleted ? null : { ...submission, xmlFormId: 'f' } }; }; @@ -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); }); diff --git a/test/components/entity/show.spec.js b/test/components/entity/show.spec.js index 58df128dc..e64fac0aa 100644 --- a/test/components/entity/show.spec.js +++ b/test/components/entity/show.spec.js @@ -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 } ]); }); @@ -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 () => { diff --git a/test/unit/request.spec.js b/test/unit/request.spec.js index fbc89fbb1..a7ed49411 100644 --- a/test/unit/request.spec.js +++ b/test/unit/request.spec.js @@ -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', () => { diff --git a/test/util/http/data.js b/test/util/http/data.js index d027d006e..0212e1960 100644 --- a/test/util/http/data.js +++ b/test/util/http/data.js @@ -131,7 +131,7 @@ const responsesByComponent = { dataset: true, audits: () => testData.extendedAudits.sorted() .filter(({ action }) => action.startsWith('entity.')), - diffs: () => [] + entityVersions: () => testData.extendedEntityVersions.sorted() }), UserHome: [],