Skip to content
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
29 changes: 29 additions & 0 deletions .github/workflows/secrets-scan.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: Secrets Scan
on:
pull_request:
types: [opened, synchronize, reopened]
jobs:
security-secrets:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: '2'
ref: '${{ github.event.pull_request.head.ref }}'
- run: |
git reset --soft HEAD~1
- name: Install Talisman
run: |
# Download Talisman
wget https://github.com/thoughtworks/talisman/releases/download/v1.37.0/talisman_linux_amd64 -O talisman

# Checksum verification
checksum=$(sha256sum ./talisman | awk '{print $1}')
if [ "$checksum" != "8e0ae8bb7b160bf10c4fa1448beb04a32a35e63505b3dddff74a092bccaaa7e4" ]; then exit 1; fi

# Make it executable
chmod +x talisman
- name: Run talisman
run: |
# Run Talisman with the pre-commit hook
./talisman --githook pre-commit
4 changes: 2 additions & 2 deletions .talismanrc
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ fileignoreconfig:
ignore_detectors:
- filecontent
- filename: package-lock.json
checksum: 030162c3c72502ccac30f3ce0172f1c2ae31a794544e0e1096771326780ea21b
checksum: 88174adc8b9dcedecf549defe09988aa4ca95940801e923d829123ba2f3ef6f4
- filename: src/entry-editable.ts
checksum: f9c4694229205fca252bb087482a3e408c6ad3b237cd108e337bcff49458db5c
checksum: 3ba7af9ed1c1adef2e2bd5610099716562bebb8ba750d4b41ddda99fc9eaf115
- filename: .husky/pre-commit
checksum: 5baabd7d2c391648163f9371f0e5e9484f8fb90fa2284cfc378732ec3192c193
5 changes: 4 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
# Changelog

## [1.5.0](https://github.com/contentstack/contentstack-utils-javascript/tree/v1.4.1) (2025-09-01)
## [1.4.3](https://github.com/contentstack/contentstack-utils-javascript/tree/v1.4.3) (2025-09-22)
- Fix data-cslp generation logic in case of applied_variants

## [1.4.2](https://github.com/contentstack/contentstack-utils-javascript/tree/v1.4.2) (2025-09-01)
- Improve null checks in find embedded entry and find embedded asset functions

## [1.4.1](https://github.com/contentstack/contentstack-utils-javascript/tree/v1.4.1) (2025-05-26)
Expand Down
12 changes: 1 addition & 11 deletions CODEOWNERS
Original file line number Diff line number Diff line change
@@ -1,11 +1 @@
* @contentstack/devex-pr-reviewers

.github/workflows/sca-scan.yml @contentstack/security-admin

.github/workflows/codeql-anaylsis.yml @contentstack/security-admin

**/.snyk @contentstack/security-admin

.github/workflows/policy-scan.yml @contentstack/security-admin

.github/workflows/issues-jira.yml @contentstack/security-admin
* @contentstack/security-admin
147 changes: 144 additions & 3 deletions __test__/entry-editable.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import { EntryModel } from '../src'
import { addTags } from '../src/entry-editable'
import { entry_global_field, entry_global_field_multiple, entry_modular_block, entry_reference, entry_with_text } from './mock/entry-editable-mock'
import { entryMultipleContent } from './mock/entry-multiple-rich-text-content'
import { entry_global_field, entry_global_field_multiple, entry_modular_block, entry_reference, entry_with_text, entry_with_applied_variants, entry_with_parent_path_variants } from './mock/entry-editable-mock'

describe('Entry editable test', () => {
it('Entry with text test', done => {
Expand Down Expand Up @@ -129,4 +127,147 @@ describe('Entry editable test', () => {
done()
})

// Tests for applied variants functionality
describe('Applied Variants Tests', () => {
it('Entry with applied variants should generate v2 tags with variant suffix', done => {
addTags(entry_with_applied_variants, 'entry_asset', false)

// Field with direct variant match should get v2 prefix and variant suffix
expect((entry_with_applied_variants as any)['$']['rich_text_editor']).toEqual('data-cslp=v2:entry_asset.entry_uid_1_variant_1.en-us.rich_text_editor')

// Nested field with direct variant match
expect((entry_with_applied_variants as any)['nested']['$']['field']).toEqual('data-cslp=v2:entry_asset.entry_uid_1_variant_2.en-us.nested.field')

// Field without variant should not have v2 prefix
expect((entry_with_applied_variants as any)['nested']['$']['other_field']).toEqual('data-cslp=entry_asset.entry_uid_1.en-us.nested.other_field')
expect((entry_with_applied_variants as any)['$']['rich_text_editor_multiple']).toEqual('data-cslp=entry_asset.entry_uid_1.en-us.rich_text_editor_multiple')

done()
})

it('Entry with applied variants should return v2 objects when tagsAsObject is true', done => {
addTags(entry_with_applied_variants, 'entry_asset', true)

// Field with direct variant match should get v2 prefix and variant suffix as object
expect((entry_with_applied_variants as any)['$']['rich_text_editor']).toEqual({'data-cslp': 'v2:entry_asset.entry_uid_1_variant_1.en-us.rich_text_editor'})

// Nested field with direct variant match
expect((entry_with_applied_variants as any)['nested']['$']['field']).toEqual({'data-cslp': 'v2:entry_asset.entry_uid_1_variant_2.en-us.nested.field'})

// Field without variant should not have v2 prefix
expect((entry_with_applied_variants as any)['nested']['$']['other_field']).toEqual({'data-cslp': 'entry_asset.entry_uid_1.en-us.nested.other_field'})
expect((entry_with_applied_variants as any)['$']['rich_text_editor_multiple']).toEqual({'data-cslp': 'entry_asset.entry_uid_1.en-us.rich_text_editor_multiple'})

done()
})

it('Entry with parent path variants should find correct variant', done => {
addTags(entry_with_parent_path_variants, 'entry_asset', false)

// Group field should get parent variant
expect((entry_with_parent_path_variants as any)['$']['group']).toEqual('data-cslp=v2:entry_asset.entry_uid_3_parent_variant.en-us.group')
// Field under 'group' parent should get parent variant
expect((entry_with_parent_path_variants as any)['group']['$']['other']).toEqual('data-cslp=v2:entry_asset.entry_uid_3_parent_variant.en-us.group.other')
// Field under 'group.nested' should get parent variant (group is longer match)
expect((entry_with_parent_path_variants as any)['group']['nested']['$']['field']).toEqual('data-cslp=v2:entry_asset.entry_uid_3_parent_variant.en-us.group.nested.field')
// Field with exact deep path match should get deep variant
expect((entry_with_parent_path_variants as any)['group']['nested']['deep']['$']['field']).toEqual('data-cslp=v2:entry_asset.entry_uid_3_deep_variant.en-us.group.nested.deep.field')

// Field with the same starting path should not get parent variant
expect((entry_with_parent_path_variants as any)['$']['group_multiple']).toEqual('data-cslp=entry_asset.entry_uid_3.en-us.group_multiple')

// Modular block content with variant should get v2 prefix and variant suffix
expect((entry_with_parent_path_variants as any)['modular_blocks'][0]['$']['content']).toEqual('data-cslp=v2:entry_asset.entry_uid_3_parent_variant.en-us.modular_blocks.0.content')
// Modular block field inside a variantised parent should get v2 prefix and variant suffix
expect((entry_with_parent_path_variants as any)['modular_blocks'][0]['content']['$']['title']).toEqual('data-cslp=v2:entry_asset.entry_uid_3_parent_variant.en-us.modular_blocks.0.content.title')

// Modular block content without variant should not have v2 prefix and variant suffix
expect((entry_with_parent_path_variants as any)['modular_blocks'][1]['$']['content']).toEqual('data-cslp=entry_asset.entry_uid_3.en-us.modular_blocks.1.content')
// Modular block field inside a non variantised parent should not get v2 prefix and variant suffix
expect((entry_with_parent_path_variants as any)['modular_blocks'][1]['content']['$']['title']).toEqual('data-cslp=entry_asset.entry_uid_3.en-us.modular_blocks.1.content.title')

done()
})

it('Entry with modular block variants should apply variants correctly', done => {
addTags(entry_with_applied_variants, 'entry_asset', false)

// Modular block content with variant should get v2 prefix and variant suffix
expect((entry_with_applied_variants as any)['modular_blocks'][1]['$']['content_from_variant']).toEqual('data-cslp=v2:entry_asset.entry_uid_1_variant_3.en-us.modular_blocks.1.content_from_variant')
// Modular block field inside a variantised parent should get v2 prefix and variant suffix
expect((entry_with_applied_variants as any)['modular_blocks'][1]['content_from_variant']['$']['title']).toEqual('data-cslp=v2:entry_asset.entry_uid_1_variant_3.en-us.modular_blocks.1.content_from_variant.title')
// Field inside a variantised parent with a different variant should get v2 prefix and variant suffix of that variant
expect((entry_with_applied_variants as any)['modular_blocks'][1]['content_from_variant']['$']['different_from_parent_variant']).toEqual('data-cslp=v2:entry_asset.entry_uid_1_variant_4.en-us.modular_blocks.1.content_from_variant.different_from_parent_variant')

// Modular block content without variant should get v2 prefix and variant suffix
expect((entry_with_applied_variants as any)['modular_blocks'][0]['$']['content']).toEqual('data-cslp=entry_asset.entry_uid_1.en-us.modular_blocks.0.content')
// Modular block field without variant should not have v2 prefix and variant suffix
expect((entry_with_applied_variants as any)['modular_blocks'][0]['content']['$']['title']).toEqual('data-cslp=entry_asset.entry_uid_1.en-us.modular_blocks.0.content.title')

done()
})

it('Entry without applied variants should work normally', done => {
addTags(entry_with_text, 'entry_asset', false)

// Should not have v2 prefix when no variants are applied
expect((entry_with_text as any)['$']['rich_text_editor']).toEqual('data-cslp=entry_asset.entry_uid_1.en-us.rich_text_editor')
expect((entry_with_text as any)['$']['rich_text_editor_multiple']).toEqual('data-cslp=entry_asset.entry_uid_1.en-us.rich_text_editor_multiple')

done()
})

it('Entry with empty applied variants should work normally', done => {
const entryWithEmptyVariants = {
...entry_with_text,
_applied_variants: {}
}

addTags(entryWithEmptyVariants, 'entry_asset', false)

// Should not have v2 prefix when variants object is empty
expect((entryWithEmptyVariants as any)['$']['rich_text_editor']).toEqual('data-cslp=entry_asset.entry_uid_1.en-us.rich_text_editor')
expect((entryWithEmptyVariants as any)['$']['rich_text_editor_multiple']).toEqual('data-cslp=entry_asset.entry_uid_1.en-us.rich_text_editor_multiple')

done()
})

it('Variant path sorting should work correctly for nested paths', done => {
const entryWithComplexVariants = {
"_version": 10,
"locale": "en-us",
"uid": "entry_uid_test",
"ACL": {},
"_applied_variants": {
"a": "variant_a",
"a.b": "variant_ab",
"a.b.c": "variant_abc",
"a.b.c.d": "variant_abcd"
},
"a": {
"b": {
"c": {
"d": {
"field": "deep field"
},
"field": "c field"
},
"field": "b field"
},
"field": "a field"
}
}

addTags(entryWithComplexVariants, 'entry_asset', false)

// Should use the longest matching path variant
expect((entryWithComplexVariants as any)['a']['b']['c']['d']['$']['field']).toEqual('data-cslp=v2:entry_asset.entry_uid_test_variant_abcd.en-us.a.b.c.d.field')
expect((entryWithComplexVariants as any)['a']['b']['c']['$']['field']).toEqual('data-cslp=v2:entry_asset.entry_uid_test_variant_abc.en-us.a.b.c.field')
expect((entryWithComplexVariants as any)['a']['b']['$']['field']).toEqual('data-cslp=v2:entry_asset.entry_uid_test_variant_ab.en-us.a.b.field')
expect((entryWithComplexVariants as any)['a']['$']['field']).toEqual('data-cslp=v2:entry_asset.entry_uid_test_variant_a.en-us.a.field')

done()
})
})

})
114 changes: 103 additions & 11 deletions __test__/mock/entry-editable-mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@ const entry_modular_block = {
"<p>module 2&nbsp;</p><figure class=\"embedded-asset\" data-redactor-type=\"embed\" data-widget-code=\"\" data-sys-asset-filelink=\"https://image.url/11.jpg\" data-sys-asset-uid=\"entry_uid_34\" data-sys-asset-filename=\"11.jpg\" data-sys-asset-contenttype=\"image/jpeg\" type=\"asset\" sys-style-type=\"display\"></figure>"
],
"_metadata": {
"uid": "metadata_uid"
"uid": "metadata_uid_1"
}
}
},
},
{
"global_modular": {
Expand All @@ -46,13 +46,13 @@ const entry_modular_block = {
"<p>Module 2</p><div class=\"redactor-component embedded-entry block-entry redactor-component-active\" data-redactor-type=\"embed\" data-widget-code=\"\" data-sys-entry-uid=\"entry_uid_10\" data-sys-entry-locale=\"en-us\" data-sys-content-type-uid=\"0_solve\" data-sys-can-edit=\"true\" sys-style-type=\"block\" type=\"entry\"></div>"
],
"_metadata": {
"uid": "metadata_uid"
"uid": "metadata_uid_1"
}
}
},
}
],
"_metadata": {
"uid": "metadata_uid"
"uid": "metadata_uid_2"
}
}
}
Expand Down Expand Up @@ -106,9 +106,9 @@ const entry_global_field = {
"<p>global modular 2<span class=\"redactor-component embedded-entry inline-entry\" data-redactor-type=\"embed\" data-widget-code=\"\" data-sys-entry-uid=\"entry_uid_12\" data-sys-entry-locale=\"en-us\" data-sys-content-type-uid=\"0_bug_1\" data-sys-can-edit=\"true\" sys-style-type=\"inline\" type=\"entry\"></span></p>"
],
"_metadata": {
"uid": "metadata_uid"
"uid": "metadata_uid_1"
}
}
},
}
]
},
Expand Down Expand Up @@ -139,21 +139,113 @@ const entry_global_field_multiple = {
"<p>Global multiple modular</p><figure class=\"embedded-asset\" data-redactor-type=\"embed\" data-widget-code=\"\" data-sys-asset-filelink=\"https://image.url/11.jpg\" data-sys-asset-uid=\"entry_uid_34\" data-sys-asset-filename=\"11.jpg\" data-sys-asset-contenttype=\"image/jpeg\" type=\"asset\" sys-style-type=\"display\"></figure>\n<p> 2</p>"
],
"_metadata": {
"uid": "metadata_uid"
"uid": "metadata_uid_1"
}
}
},
}
],
"_metadata": {
"uid": "metadata_uid"
"uid": "metadata_uid_1"
}
}
]
}
// Mock entry with applied variants for testing variant functionality
const entry_with_applied_variants = {
"_version": 10,
"locale": "en-us",
"uid": "entry_uid_1",
"ACL": {},
"_applied_variants": {
"rich_text_editor": "variant_1",
"nested.field": "variant_2",
"modular_blocks.content_from_variant.metadata_uid_2": "variant_3",
"modular_blocks.content_from_variant.metadata_uid_2.different_from_parent_variant": "variant_4"
},
"rich_text_editor": "<p>Content with variant</p>",
"rich_text_editor_multiple": [
"<p>Multiple content with variant</p>"
],
"nested": {
"field": "nested field content",
"other_field": "other nested content"
},
"modular_blocks": [
{
"content": {
"title": "modular title",
"_metadata": {
"uid": "metadata_uid_1"
}
}
},
{
"content_from_variant": {
"title": "modular title from variant",
"different_from_parent_variant": "different from parent variant",
"_metadata": {
"uid": "metadata_uid_2"
}
}
}
]
}


// Mock entry with nested parent path variants
const entry_with_parent_path_variants = {
"_version": 10,
"locale": "en-us",
"uid": "entry_uid_3",
"ACL": {},
"_applied_variants": {
"group": "parent_variant",
"group.nested.deep": "deep_variant",
"modular_blocks.content.metadata_uid_1": "parent_variant"
},
"group": {
"nested": {
"field": "nested field",
"deep": {
"field": "deep field"
}
},
"other": "other field"
},
"modular_blocks": [
{
"content": {
"title": "modular title",
"_metadata": {
"uid": "metadata_uid_1"
}
},
},
{
"content": {
"title": "modular title 2",
"_metadata": {
"uid": "metadata_uid_2"
}
},
}
],
"group_multiple": [
{
"other": "other field",
"_metadata": {
"uid": "metadata_uid_1"
}
}
]
}

export {
entry_with_text,
entry_reference,
entry_global_field,
entry_modular_block,
entry_global_field_multiple
entry_global_field_multiple,
entry_with_applied_variants,
entry_with_parent_path_variants
}
Loading
Loading