Skip to content

Commit 4b3f83c

Browse files
authored
fix: extract links from rich text field [FUS-556] (#1050)
The Merge App performs a sorting during `apply` command that puts all entries with links to other entries at the end of the list to ensure that all the links are published before the main entry. This however was not the case for rich text fields, which caused issues with merging. This PR introduces a function that detects links to other entries in rich text field and sorts them down accordingly.
1 parent 730b670 commit 4b3f83c

File tree

3 files changed

+97
-3
lines changed

3 files changed

+97
-3
lines changed

src/engine/utils/sort-entries-by-reference.ts

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { AddedChangesetItem } from '../types'
1+
import { AddedChangesetItem, ChangesetEntityLink } from '../types'
22
import { FieldsType } from 'contentful'
33

44
type LinksPerEntry = { index: number; linkIndexes: number[] }
@@ -42,6 +42,9 @@ function getLinkedEntries(entries: AddedChangesetItem[]): LinksPerEntry[] {
4242
return getFieldEntriesIndex(field, entries)
4343
} else if (isEntryArrayLink(field)) {
4444
return field.map((item: FieldsType) => getFieldEntriesIndex(item, entries))
45+
} else if (isRichTextField(field)) {
46+
const links = extractLinksFromContent(field, entry.entity)
47+
return links.map((item: FieldsType) => getFieldEntriesIndex(item, entries))
4548
}
4649
})
4750

@@ -65,6 +68,28 @@ function isEntryArrayLink(item: FieldsType): boolean {
6568
return Array.isArray(item) && item.length > 0 && isEntryLink(item[0])
6669
}
6770

71+
function isRichTextField(item: FieldsType): boolean {
72+
return item.nodeType === 'document'
73+
}
74+
75+
function extractLinksFromContent({ content }: FieldsType, sourceEntity: ChangesetEntityLink['entity']) {
76+
const { id: sourceId } = sourceEntity.sys
77+
const links = content.reduce((acc: FieldsType[], field: FieldsType) => {
78+
if (['embedded-entry-block', 'embedded-entry-inline', 'entry-hyperlink'].includes(field.nodeType)) {
79+
const { id: targetId } = field.data.target.sys
80+
if (targetId !== sourceId) {
81+
acc.push(field.data.target)
82+
}
83+
} else if (field.content?.length) {
84+
const linksFromContent = extractLinksFromContent(field, sourceEntity)
85+
acc.push(...linksFromContent)
86+
}
87+
return acc
88+
}, [])
89+
90+
return links
91+
}
92+
6893
/**
6994
* From https://github.com/millermedeiros/amd-utils/blob/master/src/array/sort.js
7095
* MIT Licensed

test/integration/commands/apply/index.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import fancy from './../register-plugins'
66
import { createChangeset } from '../../../../src/engine/utils/create-changeset'
77
import { createAddTwoItemsChangeset } from '../fixtures/add-two-items-changeset'
88

9-
describe('create command', () => {
9+
describe('apply command', () => {
1010
const spaceId = process.env.CONTENTFUL_SPACE_ID!
1111
if (!spaceId) {
1212
throw new Error('Please provide a `CONTENTFUL_SPACE_ID`')

test/unit/engine/utils/sort-entries-by-reference.test.ts

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { createChangesetItemWithData } from '../../../../src/test/helpers/create
66
describe('sortEntriesByReference', () => {
77
const referencedItem: AddedChangesetItem = createChangesetItemWithData('lesson', 'added-entry')
88
const referencedItem1: AddedChangesetItem = createChangesetItemWithData('lesson', 'added-entry1')
9+
const referencedItem2: AddedChangesetItem = createChangesetItemWithData('lesson', 'added-entry2')
910
const itemWithOneLink: AddedChangesetItem = createChangesetItemWithData('lesson1', 'added-entry-with-one-link', {
1011
referenceField: {
1112
'en-US': {
@@ -39,6 +40,68 @@ describe('sortEntriesByReference', () => {
3940
},
4041
)
4142

43+
const itemWithRichTextLinks: AddedChangesetItem = createChangesetItemWithData(
44+
'lesson4',
45+
'added-entry-with-rich-text-link',
46+
{
47+
richTextField: {
48+
'en-US': {
49+
nodeType: 'document',
50+
data: {},
51+
content: [
52+
{
53+
nodeType: 'embedded-entry-inline',
54+
content: [],
55+
data: {
56+
target: {
57+
sys: {
58+
type: 'Link',
59+
linkType: 'Entry',
60+
id: 'added-entry',
61+
},
62+
},
63+
},
64+
},
65+
{ nodeType: 'text', marks: [], value: 'some text', data: {} },
66+
{
67+
nodeType: 'paragraph',
68+
content: [
69+
{ nodeType: 'text', marks: [], value: 'some text', data: {} },
70+
{
71+
nodeType: 'embedded-entry-block',
72+
content: [],
73+
data: {
74+
target: {
75+
sys: {
76+
type: 'Link',
77+
linkType: 'Entry',
78+
id: 'added-entry1',
79+
},
80+
},
81+
},
82+
},
83+
{
84+
nodeType: 'entry-hyperlink',
85+
content: [],
86+
data: {
87+
target: {
88+
sys: {
89+
type: 'Link',
90+
linkType: 'Entry',
91+
id: 'added-entry2',
92+
},
93+
},
94+
},
95+
},
96+
],
97+
data: {},
98+
},
99+
],
100+
},
101+
},
102+
},
103+
)
104+
42105
const nonReferencedItem = createChangesetItemWithData('lesson', 'non-referenced-entry')
43106

44107
it('should order based on references if the linked entry is present in the changeset', () => {
@@ -79,7 +142,7 @@ describe('sortEntriesByReference', () => {
79142
])
80143
})
81144

82-
it('should not reorder there are no links in the changeset', () => {
145+
it('should not reorder if there are no links in the changeset', () => {
83146
expect(sortEntriesByReference([nonReferencedItem, referencedItem, referencedItem1])).to.deep.equal([
84147
nonReferencedItem,
85148
referencedItem,
@@ -120,4 +183,10 @@ describe('sortEntriesByReference', () => {
120183
itemWithArrayLinks,
121184
])
122185
})
186+
187+
it('should reorder entries linked in rich text fields', () => {
188+
expect(
189+
sortEntriesByReference([itemWithRichTextLinks, referencedItem, referencedItem1, referencedItem2]),
190+
).to.deep.equal([referencedItem, referencedItem1, referencedItem2, itemWithRichTextLinks])
191+
})
123192
})

0 commit comments

Comments
 (0)