Skip to content
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
5 changes: 5 additions & 0 deletions .cursor/rules/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Cursor (optional)

**Cursor** users: start at **[`AGENTS.md`](../../AGENTS.md)** at the repository root. All conventions live in **`skills/*/SKILL.md`**.

This folder only points contributors to **`AGENTS.md`** so editor-specific configuration does not duplicate the canonical documentation.
56 changes: 56 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Contentstack Utils .NET – Agent guide

**Universal entry point** for contributors and AI agents. Detailed conventions live in **`skills/*/SKILL.md`**.

## What this repo is

| Field | Detail |
|-------|--------|
| **Name:** | [contentstack-utils-dotnet](https://github.com/contentstack/contentstack-utils-dotnet) |
| **Purpose:** | NuGet library **`contentstack.utils`**: JSON RTE → HTML rendering, embedded entry/asset rendering, GQL-oriented helpers, variant metadata (`GetVariantMetadataTags` / `GetVariantAliases`), Live Preview–style editable tags (`addEditableTags`). This is **not** a full Contentstack Delivery or Management HTTP SDK. |
| **Out of scope (if any):** | No built-in HTTP client to Contentstack APIs; consumers use the Contentstack .NET Delivery SDK or other clients. This package focuses on parsing, rendering, and JSON helpers. |

## Tech stack (at a glance)

| Area | Details |
|------|---------|
| **Language** | C#; library multi-targets **netstandard2.0**, **net47**, **net472** ([`Contentstack.Utils/Contentstack.Utils.csproj`](Contentstack.Utils/Contentstack.Utils.csproj)); test project **net7.0** ([`Contentstack.Utils.Tests/Contentstack.Utils.Tests.csproj`](Contentstack.Utils.Tests/Contentstack.Utils.Tests.csproj)). |
| **Build** | **dotnet** + solution [`Contentstack.Utils.sln`](Contentstack.Utils.sln); shared package version in [`Directory.Build.props`](Directory.Build.props) (e.g. `Version` **1.2.0**). |
| **Tests** | **xUnit**, **Microsoft.NET.Test.Sdk**, **coverlet.collector**; JSON fixtures under [`Contentstack.Utils.Tests/Resources/`](Contentstack.Utils.Tests/Resources/). |
| **Lint / coverage** | No repo-level `.editorconfig` or format workflow; tests use **Coverlet** (`XPlat code coverage`) via the shell scripts. |
| **Key dependencies** | **HtmlAgilityPack**, **Newtonsoft.Json** in the library; tests reference compatible Newtonsoft.Json. |

## Commands (quick reference)

| Command type | Command |
|--------------|---------|
| **Build** | `dotnet build Contentstack.Utils.sln` (add `-c Release` for release configuration). |
| **Test (quick, local)** | `dotnet test Contentstack.Utils.sln` — from repo root; no TRX/coverage (fastest feedback). |
| **Test (CI parity)** | `sh ./Scripts/run-unit-test-case.sh` — clears `Contentstack.Utils.Tests/TestResults`, runs `dotnet test` on the test project with TRX logging and coverage. |
| **Test + HTML coverage report** | `bash ./Scripts/run-test-case.sh` — tests the **solution**, then runs `python3 Scripts/generate_test_report.py` to emit HTML under `Contentstack.Utils.Tests/TestResults/Coverage-.../index.html`. |
| **Pack (release)** | `dotnet pack -c Release -o out` (see [`.github/workflows/nuget-publish.yml`](.github/workflows/nuget-publish.yml)). |
| **SCA (local parity with CI)** | `dotnet restore ./Contentstack.Utils.sln`, then from `Contentstack.Utils`: `snyk test` (requires Snyk CLI and `SNYK_TOKEN`; see [`sca-scan.yml`](.github/workflows/sca-scan.yml)). |

**CI:** [`.github/workflows/unit-test.yml`](.github/workflows/unit-test.yml) runs `run-unit-test-case.sh` on Windows for `pull_request` and `push`.

## CI and automation (workflows)

| Workflow | Trigger | Role |
|----------|---------|------|
| [`unit-test.yml`](.github/workflows/unit-test.yml) | `pull_request`, `push` | Windows: unit tests via `Scripts/run-unit-test-case.sh`. |
| [`check-branch.yml`](.github/workflows/check-branch.yml) | `pull_request` | Merges into **`master`** are only allowed from **`staging`** (otherwise fails with a PR comment). |
| [`nuget-publish.yml`](.github/workflows/nuget-publish.yml) | `release` (created) | `dotnet pack -c Release -o out`; push package to NuGet.org and GitHub Packages. |
| [`sca-scan.yml`](.github/workflows/sca-scan.yml) | PR (opened, synchronize, reopened) | Ubuntu: `dotnet restore`, **Snyk** `snyk test` under `Contentstack.Utils`. |
| [`policy-scan.yml`](.github/workflows/policy-scan.yml) | PR (public repos) | Requires `SECURITY.md` and a license file containing the current year. |
| [`codeql-analysis.yml`](.github/workflows/codeql-analysis.yml) | `pull_request` (branches `*`) | CodeQL analysis for **csharp** (autobuild). |
| [`issues-jira.yml`](.github/workflows/issues-jira.yml) | `issues` (opened) | Creates Jira tickets from GitHub issues (secrets). |

## Where the documentation lives: skills

| Skill | Path | What it covers |
|-------|------|----------------|
| Dev workflow | [`skills/dev-workflow/SKILL.md`](skills/dev-workflow/SKILL.md) | Branches, build/test/pack, CI, security workflows, CODEOWNERS, Talisman. |
| Testing | [`skills/testing/SKILL.md`](skills/testing/SKILL.md) | xUnit layout, fixtures, mocks, coverage scripts, alignment with CI. |
| Code review | [`skills/code-review/SKILL.md`](skills/code-review/SKILL.md) | PR expectations, checklist, security notes. |
| Contentstack Utils (API) | [`skills/contentstack-utils/SKILL.md`](skills/contentstack-utils/SKILL.md) | Public API, package boundaries, `Utils` / GQL / variants, dependencies. |
| C# style (this repo) | [`skills/csharp-style/SKILL.md`](skills/csharp-style/SKILL.md) | Folder layout, namespaces, naming consistency, TFMs. |
5 changes: 5 additions & 0 deletions skills/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Skills – Contentstack Utils .NET

Source of truth for detailed guidance is **`skills/*/SKILL.md`**. Read **[`AGENTS.md`](../AGENTS.md)** first, then open the skill that matches your task.

Each folder contains **`SKILL.md`** with YAML frontmatter (`name`, `description`).
31 changes: 31 additions & 0 deletions skills/code-review/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
---
name: code-review
description: Use for PR expectations, review checklist, docs/changelog, and security considerations in contentstack-utils-dotnet.
---

# Code review – Contentstack Utils .NET

## When to use

- Opening or reviewing a pull request.
- Deciding whether README/CHANGELOG updates or extra tests are required.
- Assessing impact of new dependencies or public API changes.

## Instructions

### Branch and ownership

- Merges into **`master`** are expected from **`staging`** only (see [`check-branch.yml`](../../.github/workflows/check-branch.yml)). Align your PR base/head with team process.
- [`CODEOWNERS`](../../CODEOWNERS) may request reviews from **`@contentstack/devex-pr-reviewers`** and security admins for workflow or `.snyk` changes.

### Checklist

- **Behavior**: New or changed logic in [`Contentstack.Utils`](../../Contentstack.Utils/) should have **xUnit** coverage in [`Contentstack.Utils.Tests`](../../Contentstack.Utils.Tests/) unless truly non-functional (e.g. comment-only).
- **Public API**: User-visible changes should update [`README.md`](../../README.md) and [`CHANGELOG.md`](../../CHANGELOG.md) as appropriate.
- **Multi-targeting**: The library builds **netstandard2.0**, **net47**, **net472**. After API or dependency changes, verify `dotnet build` succeeds for all target frameworks (or rely on CI).
- **Dependencies**: New or upgraded NuGet packages affect **Snyk** ([`sca-scan.yml`](../../.github/workflows/sca-scan.yml)) and **CodeQL** ([`codeql-analysis.yml`](../../.github/workflows/codeql-analysis.yml)). Ensure licenses and advisories are acceptable.
- **Secrets**: Never commit API keys, tokens, or connection strings. CI uses GitHub secrets only.

### Severity labels (optional)

Teams may use **Blocker** (must fix before merge), **Major** (should fix or track), **Minor** (nit / follow-up)—keep comments actionable.
125 changes: 125 additions & 0 deletions skills/contentstack-utils/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
---
name: contentstack-utils
description: Use for the contentstack.utils NuGet API—Utils, GQL, Options, variants, converters, code flows, and integration boundaries.
---

# Contentstack Utils (API) – Contentstack Utils .NET

## When to use

- Implementing or extending JSON RTE → HTML rendering or embedded content behavior.
- Adding node types, converters, or variant/metadata helpers.
- Choosing whether logic belongs in this package vs the Contentstack .NET Delivery SDK.

## Instructions

### Package and namespaces

- **NuGet package id:** `contentstack.utils` ([`Contentstack.Utils.csproj`](../../Contentstack.Utils/Contentstack.Utils.csproj)).
- **Root namespace:** `Contentstack.Utils`. Sub-namespaces follow folders: **`Contentstack.Utils.Models`**, **`Contentstack.Utils.Interfaces`**, **`Contentstack.Utils.Enums`**, **`Contentstack.Utils.Extensions`**, **`Contentstack.Utils.Converters`**.

### Models (library)

Source files under [`Contentstack.Utils/Models/`](../../Contentstack.Utils/Models/):

- [`JsonRTENode.cs`](../../Contentstack.Utils/Models/JsonRTENode.cs), [`JsonRTENodes.cs`](../../Contentstack.Utils/Models/JsonRTENodes.cs) — GQL-shaped RTE wrappers.
- [`Metadata.cs`](../../Contentstack.Utils/Models/Metadata.cs) — embed metadata; implicit conversions from **HtmlAgilityPack** `HtmlNode` and RTE `Node`.
- [`Node.cs`](../../Contentstack.Utils/Models/Node.cs), [`TextNode.cs`](../../Contentstack.Utils/Models/TextNode.cs) — RTE tree nodes.
- [`Options.cs`](../../Contentstack.Utils/Models/Options.cs) — default rendering; subclass for custom HTML.

### Interfaces (embedding and rendering)

- **`IEntryEmbedable`** — [`IEntryEmbedable.cs`](../../Contentstack.Utils/Interfaces/IEntryEmbedable.cs): `embeddedItems` map for resolving references.
- **`IEmbeddedObject`**, **`IEmbeddedEntry`**, **`EditableEntry`**, **`IEmbeddedAsset`** — [`IEmbeddedObject.cs`](../../Contentstack.Utils/Interfaces/IEmbeddedObject.cs).
- **`IRenderable`**, **`NodeChildrenCallBack`** — [`IOptions.cs`](../../Contentstack.Utils/Interfaces/IOptions.cs).
- **`IEdges<T>`** — [`IEdges.cs`](../../Contentstack.Utils/Interfaces/IEdges.cs) (GQL edge list). Spelling **`IEntryEmbedable`** matches the existing public API.

### Core type: `Contentstack.Utils.Utils`

[`Utils.cs`](../../Contentstack.Utils/Utils.cs) exposes static helpers including:

- **`JsonToHtml`**: Overloads for `Node`, `List<Node>` — see [Code flows](#code-flows).
- **`RenderContent`**: Overloads for `string` and `List<string>` HTML — see [Code flows](#code-flows).
- **`Utils.GQL`**: `JsonToHtml` for `JsonRTENode<T>` / `JsonRTENodes<T>` where `T : IEmbeddedObject` — see [Code flows](#code-flows).
- **`addEditableTags`**: Adds Live Preview–style **`data-cslp`** (or object-shaped) metadata on **`EditableEntry`** graphs for a given content type and locale (see [`Utils.addEditableTags`](../../Contentstack.Utils/Utils.cs)).
- **Variants / metadata:**
- **`GetVariantAliases`** — reads each entry’s `publish_details` → `variants` object and collects non-empty **`alias`** values per variant entry (implementation: **`ExtractVariantAliasesFromEntry`** in [`Utils.cs`](../../Contentstack.Utils/Utils.cs), approx. lines 401–426).
- **`GetVariantMetadataTags`** — wraps alias data into a **`JObject`** with key **`data-csvariants`** (compact JSON string).
- **`GetDataCsvariantsAttribute`** — **Obsolete**; use **`GetVariantMetadataTags`** instead (same behavior; see XML comments on [`Utils`](../../Contentstack.Utils/Utils.cs)).

### Rendering hooks (`Options` / `IRenderable`)

Subclass or configure **[`Options.cs`](../../Contentstack.Utils/Models/Options.cs)** (`Options : IRenderable`) to override:

- **`RenderOption(IEmbeddedObject, Metadata)`**
- **`RenderNode(string nodeType, Node, NodeChildrenCallBack)`**
- **`RenderMark(MarkType, string text, string className, string Id)`**

### JSON serialization

- [`NodeJsonConverter.cs`](../../Contentstack.Utils/Converters/NodeJsonConverter.cs) and [`RTEJsonConverter.cs`](../../Contentstack.Utils/Converters/RTEJsonConverter.cs) integrate with **Newtonsoft.Json** for RTE node graphs. New node shapes should follow the same converter and model patterns.

### Dependencies

- **HtmlAgilityPack**: HTML load, query, and embedded-object replacement for `RenderContent`.
- **Newtonsoft.Json**: JSON models (`JObject`, `JArray`, converters). Upgrades should stay compatible with consumers and pass Snyk/CI.

### Out of scope

- This library **does not** ship an HTTP client for Contentstack Delivery or Management APIs. Apps typically use **Contentstack .NET SDK** (or REST) to fetch entries, then use **Utils** to render RTE HTML or process JSON. Keep HTTP, auth, and caching in application or SDK layers.

### Authoritative source

- **Signatures and full behavior** live in C# under [`Contentstack.Utils/`](../../Contentstack.Utils/). This skill summarizes; when in doubt, read the implementation (especially [`Utils.cs`](../../Contentstack.Utils/Utils.cs)).

## Code flows

Stepwise behavior with pointers into [`Utils.cs`](../../Contentstack.Utils/Utils.cs). Line numbers are approximate; verify against the file after large edits.

### RTE JSON → HTML (`JsonToHtml`)

1. **`JsonToHtml(Node, Options)`** (approx. lines 97–113) builds a **`referenceToHtml`** delegate: for **`reference`** nodes it calls **`findEmbeddedObject`** using **`options.entry.embeddedItems`**, then **`options.RenderOption`** when a match exists.
2. **`nodeChildrenToHtml`** (lines 115–118) concatenates HTML for each child via **`nodeToHtml`**.
3. **`nodeToHtml`** (lines 121–131): **`type == "text"`** → **`textToHtml`** (lines 133+), which applies marks in order via **`RenderMark`**; **`type == "reference"`** → **`referenceToHtml`**; **otherwise** → **`options.RenderNode`** with a callback that recurses into **`nodeChildrenToHtml`**.

### GQL RTE (`Utils.GQL.JsonToHtml`)

- Reuses **`nodeChildrenToHtml`** / **`nodeToHtml`**. **`referenceToHtml`** is built by **`GQL.refernceToHtml`** (lines 32–50): find **`IEdges<T>`** where **`edge.Node`** matches **`Metadata.ItemUid`** and **`ContentTypeUid`**, then **`options.RenderOption(edge.Node, metadata)`**. Entry points: **`GQL.JsonToHtml(JsonRTENode<T>, ...)`** (lines 17–20) and list overload (lines 22–30).

### HTML string with embeds (`RenderContent`)

1. **`RenderContent(string, Options)`** (lines 64–82): **`HtmlDocument.LoadHtml`**, then **`FindEmbeddedObject`** extension on [`HtmlDocumentExtension.cs`](../../Contentstack.Utils/Extensions/HtmlDocumentExtension.cs) (selects nodes with class **`embedded-asset`** or **`embedded-entry`**).
2. Each **HtmlNode** is passed to the callback; **`Metadata`** is produced via **implicit conversion** from **`HtmlNode`** ([`Metadata.cs`](../../Contentstack.Utils/Models/Metadata.cs), lines 46–68).
3. **`findEmbeddedObject`** resolves **`IEmbeddedObject`** from **`options.entry.embeddedItems`**; result string replaces **`metadata.OuterHTML`** in the accumulated HTML.

### Variant metadata (`GetVariantAliases` / `GetVariantMetadataTags`)

- **`ExtractVariantAliasesFromEntry`**: requires **`entry["publish_details"]`** as **`JObject`**, then **`publish_details["variants"]`** as **`JObject`**; for each property, reads **`alias`** from the nested object when present (lines 401–426).
- **`GetVariantMetadataTags`** builds **`{ "data-csvariants": <compact JSON string> }`** from **`GetVariantAliases`** results (lines 365–380).

### Diagrams (high level)

```mermaid
flowchart TD
rteEntry[JsonToHtml_Node_or_List]
rteEntry --> nodeChildren[nodeChildrenToHtml]
nodeChildren --> nodeTo[nodeToHtml]
nodeTo -->|text| textTo[textToHtml_RenderMark_chain]
nodeTo -->|reference| refDel[referenceToHtml_findEmbeddedObject_RenderOption]
nodeTo -->|other_types| renderNode[RenderNode_recursive_callback]
```

```mermaid
flowchart TD
htmlEntry[RenderContent_string]
htmlEntry --> load[HtmlDocument_LoadHtml]
load --> find[FindEmbeddedObject_extension]
find --> meta[HtmlNode_to_Metadata_implicit]
meta --> resolve[findEmbeddedObject_embeddedItems]
resolve --> replace[Replace_OuterHTML_with_RenderOption]
```

## References

- [Product README](../../README.md) — install and usage examples.
- [Contentstack .NET Utils on NuGet](https://www.nuget.org/packages/contentstack.utils) — package listing (verify version).
40 changes: 40 additions & 0 deletions skills/csharp-style/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
---
name: csharp-style
description: Use for folder placement, namespaces, naming consistency, and multi-target framework rules in Contentstack.Utils.
---

# C# style (this repo) – Contentstack Utils .NET

## When to use

- Adding new types to the library or tests.
- Choosing namespaces and file locations to match existing structure.
- Ensuring changes remain compatible with **netstandard2.0** and desktop TFMs.

## Instructions

### Library folder taxonomy

Under [`Contentstack.Utils/`](../../Contentstack.Utils/), place new code according to its role:

| Folder | Contents |
|--------|----------|
| `Interfaces/` | [`IEntryEmbedable.cs`](../../Contentstack.Utils/Interfaces/IEntryEmbedable.cs) (`IEntryEmbedable`); [`IEmbeddedObject.cs`](../../Contentstack.Utils/Interfaces/IEmbeddedObject.cs) (`IEmbeddedObject`, `IEmbeddedEntry`, `EditableEntry`, `IEmbeddedAsset`); [`IOptions.cs`](../../Contentstack.Utils/Interfaces/IOptions.cs) (`IRenderable`, `NodeChildrenCallBack`); [`IEdges.cs`](../../Contentstack.Utils/Interfaces/IEdges.cs) (`IEdges<T>`). |
| `Models/` | DTOs and [`Options.cs`](../../Contentstack.Utils/Models/Options.cs) (`Options` base class). |
| `Enums/` | [`MarkType`](../../Contentstack.Utils/Enums/MarkType.cs), [`StyleType`](../../Contentstack.Utils/Enums/StyleType.cs), [`EmbedItemType`](../../Contentstack.Utils/Enums/EmbedItemType.cs). |
| `Extensions/` | Extension methods — [`HtmlDocumentExtension.cs`](../../Contentstack.Utils/Extensions/HtmlDocumentExtension.cs) (`FindEmbeddedObject` on **HtmlAgilityPack** `HtmlDocument`). |
| `Converters/` | Newtonsoft.Json converters for RTE/node JSON ([`NodeJsonConverter.cs`](../../Contentstack.Utils/Converters/NodeJsonConverter.cs), [`RTEJsonConverter.cs`](../../Contentstack.Utils/Converters/RTEJsonConverter.cs)). |
| `Constants/` | Shared strings — [`ErrorMessages.cs`](../../Contentstack.Utils/Constants/ErrorMessages.cs). |

### Namespaces

- Mirror the folder structure: `Contentstack.Utils.Interfaces`, `Contentstack.Utils.Models`, and so on. Keep **`Contentstack.Utils`** as the root for public surface consistency.

### Naming consistency

- The codebase mixes typical .NET **PascalCase** for most public members with some **camelCase** public static names (notably **`addEditableTags`** on [`Utils`](../../Contentstack.Utils/Utils.cs)). For new code, **prefer consistency with the nearest existing API** in the same class or feature area rather than blindly applying IDE defaults, so consumers see uniform style per subsystem.

### Target frameworks

- **Library** ([`Contentstack.Utils.csproj`](../../Contentstack.Utils/Contentstack.Utils.csproj)): **netstandard2.0**, **net47**, **net472**. Public APIs must be implementable on all targets; avoid APIs that only compile on .NET 5+ unless you intentionally raise baselines with a major version policy.
- **Tests** ([`Contentstack.Utils.Tests.csproj`](../../Contentstack.Utils.Tests/Contentstack.Utils.Tests.csproj)): **net7.0** only—fine for test-only APIs; do not assume test-only APIs exist in the library.
Loading
Loading