Skip to content

referencedsource implementation for derivedentries #773

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: main
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
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,8 @@ The derive function is expected to return an object with the desired target fiel

- **`toReferenceField : string`** _(required)_ – ID of the field on the source content type in which to insert the reference

- **`shouldReferenceSource : bool`** _(optional)_ – Flag that changes the `toReferenceField` behavior to use the target content type instead of the source. Derived reference field will reference the source. (default `false`)

- **`derivedFields : array`** _(required)_ – Array of the field IDs on the target content type

- **`identityKey: function (fields): string`** _(required)_ - Called once per source entry. Returns the ID used for the derived entry, which is also used for de-duplication so that multiple source entries can link to the same derived entry.
Expand All @@ -310,6 +312,7 @@ migration.deriveLinkedEntries({
derivedContentType: 'owner',
from: ['owner'],
toReferenceField: 'ownerRef',
shouldReferenceSource: true,
derivedFields: ['firstName', 'lastName'],
identityKey: async (fromFields) => {
return fromFields.owner['en-US'].toLowerCase().replace(' ', '-');
Expand Down
2 changes: 2 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,8 @@ export interface IDeriveLinkedEntriesConfig {
identityKey: (fromFields: ContentFields) => string,
/** (optional) – If true, both the source and the derived entries will be published. If false, both will remain in draft state (default true) */
shouldPublish?: boolean,
/** (optional) - If true, changes reference field from source->target to target->source (default: false) */
shouldReferenceSource?: boolean,
/**
* (required) – Function that generates the field values for the derived entry.
* fields is an object containing each of the from fields. Each field will contain their current localized values (i.e. fields == {myField: {'en-US': 'my field value'}})
Expand Down
45 changes: 31 additions & 14 deletions src/lib/action/entry-derive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,27 @@ import isDefined from '../utils/is-defined'
import Entry from '../entities/entry'
import * as _ from 'lodash'

/**
* Helper function that sets a link field to another entry
*/
function setReferenceField(entry: Entry, contentType: ContentType, referenceField: string, referenceValue: string, locales: string[]): void {
const field = contentType.fields.getField(referenceField)
entry.setField(referenceField, {})
for (const locale of locales) {
const sys = {
type: 'Link',
linkType: 'Entry',
id: referenceValue
}
const fieldValue = (field.type === 'Array') ? [{ sys }] : { sys }
entry.setFieldForLocale(referenceField, locale, fieldValue)
}
}
class EntryDeriveAction extends APIAction {
private contentTypeId: string
private fromFields: string[]
private referenceField: string
private shouldReferenceSource: boolean
private derivedContentType: string
private deriveEntryForLocale: (inputFields: any, locale: string) => Promise<any>
private identityKey: (fromFields: any) => Promise<string>
Expand All @@ -21,6 +38,7 @@ class EntryDeriveAction extends APIAction {
this.contentTypeId = contentTypeId
this.fromFields = entryDerivation.from
this.referenceField = entryDerivation.toReferenceField
this.shouldReferenceSource = entryDerivation.shouldReferenceSource
this.derivedContentType = entryDerivation.derivedContentType
this.deriveEntryForLocale = entryDerivation.deriveEntryForLocale
this.identityKey = entryDerivation.identityKey
Expand All @@ -31,6 +49,7 @@ class EntryDeriveAction extends APIAction {
const entries: Entry[] = await api.getEntriesForContentType(this.contentTypeId)
const locales: string[] = await api.getLocalesForSpace()
const sourceContentType: ContentType = await api.getContentType(this.contentTypeId)
const derivedContentType: ContentType = await api.getContentType(this.derivedContentType)

for (const entry of entries) {
const inputs = _.pick(entry.fields, this.fromFields)
Expand Down Expand Up @@ -92,26 +111,24 @@ class EntryDeriveAction extends APIAction {
}

}

if (this.shouldReferenceSource) {
setReferenceField(targetEntry, derivedContentType, this.referenceField, entry.id, locales)
}

await api.saveEntry(targetEntry.id)
if (this.shouldPublish) {
await api.publishEntry(targetEntry.id)
}
}
const field = sourceContentType.fields.getField(this.referenceField)
entry.setField(this.referenceField, {})
for (const locale of locales) {
const sys = {
type: 'Link',
linkType: 'Entry',
id: newEntryId
}
const fieldValue = (field.type === 'Array') ? [{ sys }] : { sys }
entry.setFieldForLocale(this.referenceField, locale, fieldValue)
}

await api.saveEntry(entry.id)
if (this.shouldPublish) {
await api.publishEntry(entry.id)
if (!this.shouldReferenceSource) {
setReferenceField(entry, sourceContentType, this.referenceField, newEntryId, locales)

await api.saveEntry(entry.id)
if (this.shouldPublish) {
await api.publishEntry(entry.id)
}
}
}
}
Expand Down
1 change: 1 addition & 0 deletions src/lib/interfaces/entry-derive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export default interface EntryDerive {
derivedContentType: string,
from: string[],
toReferenceField: string,
shouldReferenceSource?: boolean,
derivedFields: string[],
identityKey: (fromFields: any) => Promise<string>
shouldPublish?: boolean,
Expand Down
67 changes: 67 additions & 0 deletions test/unit/lib/actions/entry-derive.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,73 @@ describe('Entry Derive', function () {
expect(updateEntryWithLinkFields.ownerRef['en-US'].sys.id).to.eq(batches[0].requests[0].data.sys.id); // id of linked object is same as id of target object
});

it('respects shouldReferenceSource', async function () {
const action = new EntryDeriveAction('dog', {
derivedContentType: 'owner',
from: ['owner'],
toReferenceField: 'dogRef',
shouldReferenceSource: true,
derivedFields: ['firstName', 'lastName'],
identityKey: async (fromFields) => {
return fromFields.owner['en-US'].toLowerCase().replace(' ', '-');
},
shouldPublish: true,
deriveEntryForLocale: async (inputFields, locale) => {
if (locale !== 'en-US') {
return;
}
const [firstName, lastName] = inputFields.owner[locale].split(' ');
return {
firstName,
lastName
};
}
});

const contentTypes = new Map();
contentTypes.set('dog', new ContentType({
sys: {
id: 'dog'
}
}));
contentTypes.set('owner', new ContentType({
sys: {
id: 'owner'
},
fields: [{
name: 'dogRef',
id: 'dogRef',
type: 'Symbol'
}]
}));

const entries = [
new Entry(makeApiEntry({
id: '246',
contentTypeId: 'dog',
version: 1,
fields: {
owner: {
'en-US': 'john doe'
}
}
}))
];

const api = new OfflineApi({ contentTypes, entries, locales: ['en-US'] });
api.startRecordingRequests(null);
await action.applyTo(api);
api.stopRecordingRequests();
const batches = await api.getRequestBatches();
expect(batches[0].requests.length).to.eq(2);
const createTargetEntryFields = batches[0].requests[0].data.fields;
expect(createTargetEntryFields.firstName['en-US']).to.eq('john'); // target entry has first and last name
expect(createTargetEntryFields.lastName['en-US']).to.eq('doe');
expect(typeof createTargetEntryFields.dogRef['en-US'].sys).to.eq('object'); // request to update entry is n to 1 link
expect(createTargetEntryFields.dogRef['en-US'].sys.type).to.eq('Link');
expect(createTargetEntryFields.dogRef['en-US'].sys.id).to.eq('246'); // id of linked object is same as id of target object
});

it('respects shouldPublish', async function () {
const action = new EntryDeriveAction('dog', {
derivedContentType: 'owner',
Expand Down