Skip to content

Commit eda03ed

Browse files
committed
update: added skill files
1 parent 8b7cf4a commit eda03ed

10 files changed

Lines changed: 676 additions & 0 deletions

File tree

.cursor/rules/README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Cursor (optional)
2+
3+
**Cursor** users: start at **[AGENTS.md](../../AGENTS.md)**. All conventions live in **`skills/*/SKILL.md`**.
4+
5+
This folder only points contributors to **AGENTS.md** so editor-specific config does not duplicate the canonical docs.

AGENTS.md

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# Contentstack Model Generator – Agent guide
2+
3+
**Universal entry point** for contributors and AI agents. Detailed conventions live in **`skills/*/SKILL.md`**.
4+
5+
## What this repo is
6+
7+
| Field | Detail |
8+
| --- | --- |
9+
| **Name:** | [contentstack/contentstack-model-generator](https://github.com/contentstack/contentstack-model-generator) |
10+
| **Purpose:** | A **.NET global tool** (`contentstack.model.generator`) that connects to the Content Management API (CMA), fetches content types and global fields from a stack, and **emits C# model classes** (and helpers) under a `Models` output folder. |
11+
| **Out of scope (if any):** | This package is a **CLI code generator**, not a runtime Contentstack SDK for apps. Product install and CLI flags are documented in [README.md](README.md). |
12+
13+
## Tech stack (at a glance)
14+
15+
| Area | Details |
16+
| --- | --- |
17+
| **Language** | C# — tool targets **.NET 7** (`net7.0`); test project targets **.NET 9** (`net9.0`). |
18+
| **Build** | .NET SDK — solution [contentstack.model.generator/contentstack.model.generator.sln](contentstack.model.generator/contentstack.model.generator.sln); main project [contentstack.model.generator/contentstack.model.generator.csproj](contentstack.model.generator/contentstack.model.generator.csproj) (`PackAsTool`, `PackOnBuild`). |
19+
| **Tests** | xUnit — project [contentstack.model.generator.tests/contentstack.model.generator.tests.csproj](contentstack.model.generator.tests/contentstack.model.generator.tests.csproj); Moq; coverlet.collector. |
20+
| **Lint / coverage** | Built-in .NET analyzers; coverlet for coverage in tests. No repo-wide `dotnet format` / StyleCop config in-tree at time of writing. |
21+
| **Tool dependencies** | [contentstack.model.generator.csproj](contentstack.model.generator/contentstack.model.generator.csproj): McMaster.Extensions.CommandLineUtils, Newtonsoft.Json; HTTP via [HTTPRequestHandler.cs](contentstack.model.generator/CMA/HTTPRequestHandler.cs) (`HttpClient`). |
22+
| **Consumer projects (generated `Models/`)** | Emitted C# references **Contentstack.Core**, **Contentstack.Utils**, **Newtonsoft.Json**, and **Markdig** (e.g. generated `ContentstackStringExtension`). Those NuGet packages must be added to the **application that uses the generated code**, not to the generator tool project. |
23+
24+
## Commands (quick reference)
25+
26+
| Command type | Command |
27+
| --- | --- |
28+
| **Restore** | `dotnet restore contentstack.model.generator/contentstack.model.generator.sln` |
29+
| **Build** | `dotnet build contentstack.model.generator/contentstack.model.generator.sln` |
30+
| **Test** | `dotnet test contentstack.model.generator/contentstack.model.generator.sln` |
31+
| **Pack (tool NuGet)** | From [contentstack.model.generator/](contentstack.model.generator/): CI uses `dotnet pack -c Release -o out` ([nuget-publish.yml](.github/workflows/nuget-publish.yml)), producing `.nupkg` under `contentstack.model.generator/out/`. Local builds also use **`PackOnBuild`** and **`PackageOutputPath`** (`./nupkg`), so packages may appear under `contentstack.model.generator/nupkg/` unless you override with `-o`. |
32+
| **Install globally (users)** | See [README.md](README.md) (`dotnet tool install -g contentstack.model.generator`). |
33+
34+
**CI / automation:** [.github/workflows/](.github/workflows/) — e.g. branch check ([check-branch.yml](.github/workflows/check-branch.yml)), SCA ([sca-scan.yml](.github/workflows/sca-scan.yml)), CodeQL ([codeql-analysis.yml](.github/workflows/codeql-analysis.yml)), NuGet publish on release ([nuget-publish.yml](.github/workflows/nuget-publish.yml)). **No workflow currently runs `dotnet test`** — run tests locally before opening a PR (see [skills/dev-workflow/SKILL.md](skills/dev-workflow/SKILL.md)).
35+
36+
## Where the documentation lives: skills
37+
38+
| Skill | Path | What it covers |
39+
| --- | --- | --- |
40+
| **Dev workflow** | [skills/dev-workflow/SKILL.md](skills/dev-workflow/SKILL.md) | Branches, CI, build/test/pack commands, solution paths. |
41+
| **Testing** | [skills/testing/SKILL.md](skills/testing/SKILL.md) | Test layout, xUnit/Moq/coverlet, test file map, secrets/credentials. |
42+
| **Code review** | [skills/code-review/SKILL.md](skills/code-review/SKILL.md) | PR checklist, branch policy, security scans. |
43+
| **C# / .NET** | [skills/csharp-dotnet/SKILL.md](skills/csharp-dotnet/SKILL.md) | TFMs, namespaces, project layout conventions. |
44+
| **Framework (libraries)** | [skills/framework/SKILL.md](skills/framework/SKILL.md) | McMaster CLI, Newtonsoft.Json, HTTP client, CMA `Config` / base URL—third-party and platform glue. |
45+
| **Product (CLI + CMA + codegen)** | [skills/contentstack-model-generator/SKILL.md](skills/contentstack-model-generator/SKILL.md) | End-to-end runtime flow, layers, extension points. |
46+
47+
Quick links to each **SKILL.md**: [skills/README.md](skills/README.md).
48+
49+
## Using Cursor (optional)
50+
51+
If you use **Cursor**, [.cursor/rules/README.md](.cursor/rules/README.md) points to **[AGENTS.md](AGENTS.md)** so editor-specific config does not duplicate the canonical docs.
Lines changed: 248 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,248 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using Newtonsoft.Json;
4+
using contentstack.model.generator.Model;
5+
6+
namespace Contentstack.Model.Generator.Model
7+
{
8+
/// <summary>
9+
/// Represents the response from the OAuth app authorization API.
10+
/// </summary>
11+
public class OAuthAppAuthorizationResponse
12+
{
13+
14+
[JsonProperty("data")]
15+
public OAuthAppAuthorizationData[] Data { get; set; }
16+
}
17+
18+
/// <summary>
19+
/// Represents OAuth app authorization data.
20+
/// </summary>
21+
public class OAuthAppAuthorizationData
22+
{
23+
24+
[JsonProperty("authorization_uid")]
25+
public string AuthorizationUid { get; set; }
26+
27+
28+
[JsonProperty("user")]
29+
public OAuthUser User { get; set; }
30+
}
31+
32+
33+
public class OAuthUser
34+
{
35+
36+
[JsonProperty("uid")]
37+
public string Uid { get; set; }
38+
}
39+
40+
/// <summary>
41+
/// Configuration options for OAuth authentication.
42+
/// </summary>
43+
public class OAuthOptions
44+
{
45+
/// <summary>
46+
/// The OAuth application ID. Defaults to the Contentstack app ID.
47+
/// </summary>
48+
public string AppId { get; set; } = "6400aa06db64de001a31c8a9";
49+
50+
/// <summary>
51+
/// The OAuth client ID. Defaults to the Contentstack client ID.
52+
/// </summary>
53+
public string ClientId { get; set; } = "Ie0FEfTzlfAHL4xM";
54+
55+
/// <summary>
56+
/// The redirect URI for OAuth callbacks. Defaults to localhost:8184.
57+
/// </summary>
58+
public string RedirectUri { get; set; } = "http://localhost:8184";
59+
60+
/// <summary>
61+
/// The OAuth client secret. If provided, PKCE flow will be skipped.
62+
/// If null or empty, PKCE flow will be used for enhanced security.
63+
/// </summary>
64+
public string ClientSecret { get; set; }
65+
66+
/// <summary>
67+
/// The OAuth response type. Defaults to "code" for authorization code flow.
68+
/// </summary>
69+
public string ResponseType { get; set; } = "code";
70+
71+
/// <summary>
72+
/// The OAuth scopes to request. Optional array of permission scopes.
73+
/// </summary>
74+
public string[] Scope { get; set; }
75+
76+
/// <summary>
77+
/// Indicates whether PKCE (Proof Key for Code Exchange) flow should be used.
78+
/// This is automatically determined based on whether ClientSecret is provided.
79+
/// </summary>
80+
public bool UsePkce => string.IsNullOrEmpty(ClientSecret);
81+
82+
/// <summary>
83+
/// Validates the OAuth options configuration.
84+
/// </summary>
85+
/// <returns>True if the configuration is valid, false otherwise.</returns>
86+
public bool IsValid()
87+
{
88+
return IsValid(out _);
89+
}
90+
91+
/// <summary>
92+
/// Validates the OAuth options configuration and provides detailed error information.
93+
/// </summary>
94+
/// <param name="errorMessage">The validation error message if validation fails.</param>
95+
/// <returns>True if the configuration is valid, false otherwise.</returns>
96+
public bool IsValid(out string errorMessage)
97+
{
98+
errorMessage = null;
99+
100+
if (string.IsNullOrWhiteSpace(AppId))
101+
{
102+
errorMessage = "AppId is required for OAuth configuration.";
103+
return false;
104+
}
105+
106+
if (string.IsNullOrWhiteSpace(ClientId))
107+
{
108+
errorMessage = "ClientId is required for OAuth configuration.";
109+
return false;
110+
}
111+
112+
if (string.IsNullOrWhiteSpace(RedirectUri))
113+
{
114+
errorMessage = "RedirectUri is required for OAuth configuration.";
115+
return false;
116+
}
117+
118+
if (!Uri.TryCreate(RedirectUri, UriKind.Absolute, out var redirectUri))
119+
{
120+
errorMessage = "RedirectUri must be a valid absolute URI.";
121+
return false;
122+
}
123+
124+
if (redirectUri.Scheme != "http" && redirectUri.Scheme != "https")
125+
{
126+
errorMessage = "RedirectUri must use http or https scheme.";
127+
return false;
128+
}
129+
130+
if (string.IsNullOrWhiteSpace(ResponseType))
131+
{
132+
errorMessage = "ResponseType is required for OAuth configuration.";
133+
return false;
134+
}
135+
136+
if (ResponseType != "code")
137+
{
138+
errorMessage = "ResponseType must be 'code' for authorization code flow.";
139+
return false;
140+
}
141+
142+
// For traditional OAuth flow (non-PKCE), client secret is required
143+
if (!UsePkce && string.IsNullOrWhiteSpace(ClientSecret))
144+
{
145+
errorMessage = "ClientSecret is required for traditional OAuth flow. Use PKCE flow (leave ClientSecret empty) for public clients.";
146+
return false;
147+
}
148+
149+
return true;
150+
}
151+
152+
/// <summary>
153+
/// Validates the OAuth options configuration and throws an exception if invalid.
154+
/// </summary>
155+
/// <exception cref="OAuthConfigurationException">Thrown when the configuration is invalid.</exception>
156+
public void Validate()
157+
{
158+
if (!IsValid(out var errorMessage))
159+
{
160+
throw new Exceptions.OAuthConfigurationException(errorMessage);
161+
}
162+
}
163+
164+
/// <summary>
165+
/// Gets a string representation of the OAuth options for debugging.
166+
/// </summary>
167+
/// <returns>A string representation of the OAuth options.</returns>
168+
public override string ToString()
169+
{
170+
return $"OAuthOptions: AppId={AppId}, ClientId={ClientId}, RedirectUri={RedirectUri}, " +
171+
$"ResponseType={ResponseType}, UsePkce={UsePkce}, HasScope={Scope?.Length > 0}";
172+
}
173+
}
174+
175+
/// <summary>
176+
/// Represents the response from OAuth token exchange operations.
177+
/// </summary>
178+
public class OAuthResponse
179+
{
180+
181+
[JsonProperty("access_token")]
182+
public string AccessToken { get; set; }
183+
184+
185+
[JsonProperty("refresh_token")]
186+
public string RefreshToken { get; set; }
187+
188+
189+
[JsonProperty("expires_in")]
190+
public int ExpiresIn { get; set; }
191+
192+
193+
[JsonProperty("organization_uid")]
194+
public string OrganizationUid { get; set; }
195+
196+
197+
[JsonProperty("user_uid")]
198+
public string UserUid { get; set; }
199+
}
200+
/// <summary>
201+
/// Represents OAuth tokens stored in memory for cross-SDK access.
202+
/// This class enables sharing OAuth tokens between the Management SDK and other SDKs
203+
/// </summary>
204+
public class OAuthTokens
205+
{
206+
207+
public string AccessToken { get; set; }
208+
209+
public string RefreshToken { get; set; }
210+
211+
public DateTime ExpiresAt { get; set; }
212+
213+
public string OrganizationUid { get; set; }
214+
215+
public string UserUid { get; set; }
216+
217+
public string ClientId { get; set; }
218+
219+
public string AppId { get; set; }
220+
221+
public bool IsExpired => ExpiresAt == DateTime.MinValue || DateTime.UtcNow >= ExpiresAt;
222+
223+
public bool NeedsRefresh
224+
{
225+
get
226+
{
227+
// If ExpiresAt is not set or is MinValue, consider it expired
228+
if (ExpiresAt == DateTime.MinValue)
229+
return true;
230+
231+
try
232+
{
233+
// Check if we need to refresh (5 minutes before expiration)
234+
var refreshTime = ExpiresAt.AddMinutes(-5);
235+
return DateTime.UtcNow >= refreshTime || IsExpired;
236+
}
237+
catch (ArgumentOutOfRangeException)
238+
{
239+
// If the calculation results in an unrepresentable DateTime, consider it expired
240+
return true;
241+
}
242+
}
243+
}
244+
245+
public bool IsValid => !string.IsNullOrEmpty(AccessToken) && !IsExpired;
246+
}
247+
248+
}

skills/README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Skills – Contentstack Model Generator
2+
3+
**Entry point:** [AGENTS.md](../AGENTS.md) — commands, stack, CI, and **which skill to open**.
4+
5+
Each folder holds **SKILL.md** (YAML frontmatter: `name`, `description`).
6+
7+
- [dev-workflow/SKILL.md](dev-workflow/SKILL.md)
8+
- [testing/SKILL.md](testing/SKILL.md)
9+
- [code-review/SKILL.md](code-review/SKILL.md)
10+
- [csharp-dotnet/SKILL.md](csharp-dotnet/SKILL.md)
11+
- [framework/SKILL.md](framework/SKILL.md)
12+
- [contentstack-model-generator/SKILL.md](contentstack-model-generator/SKILL.md)

skills/code-review/SKILL.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
---
2+
name: code-review
3+
description: PR checklist, branch policy, versioning notes, and security scan context for this repository.
4+
---
5+
6+
# Code review – Contentstack Model Generator
7+
8+
## When to use
9+
10+
- Opening or reviewing a pull request.
11+
- Checking release readiness (version, changelog, package metadata).
12+
- Ensuring changes align with automated security and branch rules.
13+
14+
## Instructions
15+
16+
### Branch policy
17+
18+
- PRs **into `master`** must be **from `staging`** per [.github/workflows/check-branch.yml](../../.github/workflows/check-branch.yml). Verify head/base before approving merges to `master`.
19+
- If the PR does not target `master`, the same rule may not apply; still follow team conventions for default branch.
20+
21+
### Review checklist
22+
23+
- **Build and tests:** `dotnet build` and `dotnet test` on `contentstack.model.generator/contentstack.model.generator.sln` succeed (CI does not run `dotnet test`; see [dev-workflow/SKILL.md](../dev-workflow/SKILL.md)).
24+
- **Release versioning:** For releases, keep `[VersionOption]` in [ModelGenerator.cs](../../contentstack.model.generator/ModelGenerator.cs) (CLI `--version`), **`PackageVersion` / `ReleaseVersion`** in [contentstack.model.generator.csproj](../../contentstack.model.generator/contentstack.model.generator.csproj), and **[CHANGELOG.md](../../CHANGELOG.md)** aligned.
25+
- **Auth paths:** Traditional `authtoken` vs `--oauth` remain mutually consistent; no accidental logging of secrets.
26+
- **Codegen output:** Changes to `ModelGenerator` or templates do not break emitted class shapes without **[CHANGELOG.md](../../CHANGELOG.md)** and version bump in **[contentstack.model.generator.csproj](../../contentstack.model.generator/contentstack.model.generator.csproj)** when releasing.
27+
- **Dependencies:** New package references are justified; Snyk/SCA will scan on PRs ([sca-scan.yml](../../.github/workflows/sca-scan.yml)).
28+
- **Security-sensitive code:** HTTP, OAuth, and error handling reviewed for information disclosure and robust failure modes.
29+
30+
### Automated security analysis
31+
32+
- **Snyk (SCA):** [.github/workflows/sca-scan.yml](../../.github/workflows/sca-scan.yml) — dependency vulnerabilities.
33+
- **CodeQL:** [.github/workflows/codeql-analysis.yml](../../.github/workflows/codeql-analysis.yml) — static analysis for C#.
34+
35+
Treat new findings from these workflows as blockers until triaged or waived with documented rationale.
36+
37+
### Product and licensing
38+
39+
- **License** and **copyright** in packaged output follow `contentstack.model.generator.csproj` and [LICENSE](../../LICENSE) / packaged `LICENSE.txt` as applicable.
40+
- **Security:** Report vulnerabilities per [SECURITY.md](../../SECURITY.md) (not via public GitHub issues).

0 commit comments

Comments
 (0)