Add project-wide instruction files for Fuchs migration
Playwright Tests / test (push) Has been cancelled
Playwright Tests / test (push) Has been cancelled
Added detailed instruction files for configuration, controller structure, C# standards, OCORE library usage, ImageSharp licensing, and testing. These documents define rules for settings, DI, file layout, package management, and test practices to ensure consistency and compliance during the .NET 10 migration.
This commit is contained in:
@@ -0,0 +1,39 @@
|
||||
---
|
||||
applyTo: "Fuchs/**,Fuchs_DataService/**"
|
||||
---
|
||||
|
||||
# Configuration & Secrets Instructions
|
||||
|
||||
## Settings Source
|
||||
- All application settings live in `Fuchs/appsettings.json`. **Never** use `Web.config` or `System.Configuration.ConfigurationManager` for app settings.
|
||||
- App-specific settings are nested under the `"Fuchs"` key, e.g. `_config["Fuchs:SMS_APIKey"]`.
|
||||
- Connection strings live under the standard `"ConnectionStrings"` key and are read via `IConfiguration.GetConnectionString(...)`.
|
||||
- `appsettings.Development.json` (git-ignored) overrides secrets for local development.
|
||||
|
||||
## Startup Order
|
||||
- Call `FuchsOcmsIntranet.Initialize(configuration)` at app start in `Program.cs` **before** DI registration.
|
||||
- `Fuchs_intranet` receives `IConfiguration` via its constructor — inject it, never read config statically.
|
||||
|
||||
## Azure Key Vault — Secret Naming
|
||||
Secret names must satisfy `^[0-9a-zA-Z-]+$` (alphanumerics and hyphens only; no underscores, dots, or spaces).
|
||||
|
||||
- Hierarchy levels are separated by `--` (double hyphen), which maps to `:` in `IConfiguration`.
|
||||
- Underscores within a name segment are encoded as a single `-` in Key Vault and decoded back to `_` when the key is reconstructed.
|
||||
- The app prefix `fuchs` is prepended to every secret name.
|
||||
- Format: `{appname}--{Section}--{key-with-hyphens-for-underscores}`
|
||||
|
||||
### Examples
|
||||
| Key Vault name | `IConfiguration` key |
|
||||
|----------------|----------------------|
|
||||
| `fuchs--ConnectionStrings--ocms-ConnectionString` | `ConnectionStrings:ocms_ConnectionString` |
|
||||
| `fuchs--Fuchs--SMS-APIKey` | `Fuchs:SMS_APIKey` |
|
||||
| `fuchs--Fuchs--Email--Main--password` | `Fuchs:Email:Main:password` |
|
||||
|
||||
## Adding a New Secret
|
||||
1. Replace every `_` in the original config key with `-` for the Key Vault name.
|
||||
2. Add the entry to `ManagedSecretKeys` in `appsettings.json` using the same hyphenated form **without** the `fuchs--` prefix.
|
||||
3. Read it through `IConfiguration` with the underscore form (`Fuchs:SMS_APIKey`).
|
||||
|
||||
## Secret Management Wiring
|
||||
- Secret management is provided by `OCORE_web.Secrets.SecretManagementWebExtensions.AddSecretManagement(...)` (called in `Program.cs`).
|
||||
- Do **not** create a local `SecretManagementExtensions` stub in Fuchs — it collides with the OCORE_web extension and causes ambiguous extension-method resolution.
|
||||
@@ -0,0 +1,43 @@
|
||||
---
|
||||
applyTo: "Fuchs/Controllers/**,Fuchs/code/**"
|
||||
---
|
||||
|
||||
# IntranetController Instructions
|
||||
|
||||
## Overview
|
||||
The Fuchs intranet **is** the entire website, served from `/`. There is a single MVC controller, `IntranetController`, split into **partial files** by domain. There are no areas.
|
||||
|
||||
## Routing
|
||||
| Route | Action | Purpose |
|
||||
|-------|--------|---------|
|
||||
| `/{fn?}/{id?}/{code?}` | `Index` | Returns the SPA shell view `intranet`. `[AllowAnonymous]`. |
|
||||
| `/do/{fn?}/{id?}/{code?}` | `Do` | Central API dispatcher (GET + POST). `[AllowAnonymous]`, gated internally. |
|
||||
|
||||
## Partial File Layout
|
||||
Each partial lives in `Fuchs/Controllers/` and maps to a VB original under `Fuchs/code/`:
|
||||
|
||||
| Partial | Domain | Dispatch entry |
|
||||
|---------|--------|----------------|
|
||||
| `IntranetController.cs` | Core: routing, auth, login/logout, account, MFR | `Do(...)` switch |
|
||||
| `IntranetController.Invoices.cs` / `.Invoices2.cs` | Invoices | `Do_Process_Invoices` |
|
||||
| `IntranetController.Reminder.cs` | Reminders | `Do_Process_Reminder` |
|
||||
| `IntranetController.Requests.cs` | Requests | `Do_Process_Requests` |
|
||||
| `IntranetController.Reports.cs` | Reports | `Do_Process_Reports` |
|
||||
| `IntranetController.Banking.cs` | Banking (MT940) | `Do_Process_Bankings` |
|
||||
|
||||
## Dispatcher Pattern
|
||||
- `Do(...)` normalizes `fn`/`id`/`code` (lowercase `fn`, null-coalesce others) then routes via a `switch` expression to `Do_Process_*` helpers or inline `Handle*` methods.
|
||||
- Each domain handler is itself a `switch` on `id` (the sub-function) returning an `IActionResult`.
|
||||
- Wrap the whole dispatch in a single `try/catch`; log via `_intranet.debug_log(...)` and return `ServerError()` on unhandled exceptions. Do not add per-case try/catch unless a case needs special recovery.
|
||||
|
||||
## Authentication Gate
|
||||
- The unauthenticated allow-list logic in `Do(...)` must keep its braces: unauthenticated users are rejected with `Unauthorized401()` **only** when the function is not in `_allowedNonAuth`, not `login`/`logout`, and not in `_allowedGet` (checked as both `fn` and `fn|id`).
|
||||
- Add new anonymous endpoints by extending `_allowedNonAuth` (full-anonymous) or `_allowedGet` (read-only GET links), never by removing the gate.
|
||||
|
||||
## Conventions
|
||||
- Use the `StdParamlist(...)` helpers to build `SqlParameter` lists; they pre-populate `@authuser` from `UserAccountID`.
|
||||
- Use `SqlOpt(fn, id, code)` to pass `FIS_SQLOptions` to OCORE SQL helpers.
|
||||
- Use `DbSec` (`_intranet.GetDbSecurity(UserAccountID)`) for the `Security:` argument on SQL calls.
|
||||
- Return JSON via the OCORE `JSONAsync(...)` helper, not `Json(...)`.
|
||||
- Use the status helpers `Unauthorized401()`, `BadRequest400()`, `ServerError(...)` rather than raw `StatusCode(...)`.
|
||||
- All controller actions that perform I/O must be `async Task<IActionResult>`.
|
||||
@@ -0,0 +1,29 @@
|
||||
---
|
||||
applyTo: "**/*.cs"
|
||||
---
|
||||
|
||||
# C# Coding Standards
|
||||
|
||||
## Language & Target
|
||||
- All code must be written in C# targeting **.NET 10**.
|
||||
- The original Fuchs intranet was VB.NET; any remaining VB must be converted to C# during migration. Original VB reference lives at `D:\My Programming\PWProjects\Fuchs\Fuchs\Areas\Intranet`.
|
||||
|
||||
## Style
|
||||
- Follow standard C# naming: **PascalCase** for classes and methods, **camelCase** for locals and parameters, `_camelCase` for private fields.
|
||||
- `ImplicitUsings` and `Nullable` are enabled in the Fuchs project — honor nullable annotations and avoid redundant `using` directives.
|
||||
- Prefer modern, performance-oriented features: `async`/`await` for I/O, LINQ for data manipulation, dependency injection for testability, `switch` expressions, target-typed `new`, and collection initializers.
|
||||
- Only add comments when they match the existing style or explain non-obvious logic. Do not over-comment.
|
||||
|
||||
## File Size
|
||||
- Keep files to a soft limit of **400** lines (hard max **600**).
|
||||
- Proactively refactor larger files into smaller, focused classes/partials — this is why `IntranetController` is split into domain partials.
|
||||
|
||||
## Dependency Injection
|
||||
- Inject dependencies via constructor (`ILogger<T>`, `IConfiguration`, services). Do not use service-locator or static singletons in new class-level code.
|
||||
- Library classes accept optional loggers (`ILogger<T>?`) defaulting to `NullLogger<T>.Instance` — see `logging.instructions.md`.
|
||||
|
||||
## Packages
|
||||
- Do not upgrade `Spire.PDF` beyond `8.10.5`.
|
||||
- For builds failing because `SixLabors.ImageSharp` (v4.0.0+) requires a license, see `imagesharp.instructions.md` before downgrading.
|
||||
- Keep `MailKit`/`MimeKit` versions aligned with OCORE's referenced versions to avoid `NU1605` package-downgrade-as-error.
|
||||
- Only add or update packages when necessary; prefer existing/OCORE libraries.
|
||||
@@ -0,0 +1,29 @@
|
||||
---
|
||||
applyTo: "Fuchs/**,OCORE/**,OCORE_web/**,OCORE_web_pdf/**"
|
||||
---
|
||||
|
||||
# SixLabors ImageSharp Instructions
|
||||
|
||||
## Licensing (v4.0.0+)
|
||||
- `SixLabors.ImageSharp` v4.0.0+ requires a license; builds fail validation without one.
|
||||
- **Do not downgrade ImageSharp** to avoid the license requirement.
|
||||
- Each project that references ImageSharp needs its own discoverable `sixlabors.lic` file in the project root.
|
||||
- Tracked license files exist for `OCORE`, `OCORE_web`, and `Fuchs`. Use `OCORE_web/OCORE_web/sixlabors.lic` as the canonical Community-license template.
|
||||
- Do **not** create untracked local duplicates of `sixlabors.lic` — an untracked copy blocks `git pull` when upstream adds a tracked one.
|
||||
|
||||
## License File Format
|
||||
A single-line Community license payload:
|
||||
```
|
||||
Id=...;Kind=Community;ExpiryDateUtc=...;Key=...
|
||||
```
|
||||
|
||||
## v4.0.0 API / Namespace Changes
|
||||
When updating or fixing ImageSharp-related code, account for these breaking moves:
|
||||
|
||||
| Removed / old | Use instead |
|
||||
|---------------|-------------|
|
||||
| `using SixLabors.ImageSharp.ColorSpaces;` | Removed — delete if unused |
|
||||
| `using SixLabors.ImageSharp.Web.DependencyInjection;` | `using SixLabors.ImageSharp.Web;` |
|
||||
|
||||
- `AddImageSharp(...)` / `UseImageSharp()` resolve from `SixLabors.ImageSharp.Web` (plus `.Commands` and `.Processors` for related types).
|
||||
- After any ImageSharp version change, rebuild and confirm middleware bootstrap in `OCORE_web/OCORE_web/web/OCORE_appbuilder.cs` still compiles.
|
||||
@@ -0,0 +1,32 @@
|
||||
---
|
||||
applyTo: "Fuchs/**,Fuchs_DataService/**,OCORE/**,OCORE_web/**,OCORE_web_pdf/**"
|
||||
---
|
||||
|
||||
# OCORE Libraries Instructions
|
||||
|
||||
## Preferred Libraries
|
||||
- Make use of **OCORE** / **OCORE_web** libraries wherever possible for common tasks: logging, configuration, data access, web helpers, and security.
|
||||
- For PDF-related work, prefer **OCORE_web_pdf** / OCORE PDF functions over rewriting from scratch.
|
||||
- **Do not use OCMS or OCMS_sharp.** Use only OCORE or OCORE_web. In particular, never use `OCMS.ocms_debug.debug_log`.
|
||||
|
||||
## Common OCORE Entry Points
|
||||
| Area | Namespace / helper |
|
||||
|------|--------------------|
|
||||
| SQL access | `OCORE.SQL.sql` (`getSQLDatatable_async`, `getSQLValue_async`, `SQL_VarChar`, ...) |
|
||||
| Async MVC/JSON | `OCORE.web.mvc_helper_async` (`JSONAsync`, ...) |
|
||||
| Security | `OCORE.security` (`DatabaseSecurity`, ...) |
|
||||
| Dictionaries/commons | `OCORE.commons`, `OCORE.OCORE_dictionaries` |
|
||||
| Web bootstrapping | `OCORE_web` app-builder / middleware helpers |
|
||||
| Secret management | `OCORE_web.Secrets.SecretManagementWebExtensions` |
|
||||
|
||||
## SQL Helper Conventions
|
||||
- Pass parameters using `SQL_VarChar(...)` and the controller `StdParamlist(...)` helpers (auto-injects `@authuser`).
|
||||
- Always pass `Security: DbSec` and `options: SqlOpt(fn, id, code)` to the OCORE SQL helpers.
|
||||
- Prefer the `_async` variants for all database I/O.
|
||||
|
||||
## Repository Sync
|
||||
- OCORE, OCORE_web, OCORE_web_pdf, and OCORE_Charting are separate git repositories pulled alongside Fuchs.
|
||||
- After pulling OCORE* updates, rebuild and re-run tests, and re-check shared dependency versions (e.g., MailKit/MimeKit) for `NU1605` downgrade conflicts.
|
||||
|
||||
## ImageSharp (used transitively by OCORE/OCORE_web)
|
||||
- See `imagesharp.instructions.md` for license handling and the v4.0.0 namespace changes.
|
||||
@@ -0,0 +1,24 @@
|
||||
---
|
||||
applyTo: "Fuchs.Tests/**,**/*Tests/**,**/*Tests.cs"
|
||||
---
|
||||
|
||||
# Testing Instructions
|
||||
|
||||
## Framework
|
||||
- Tests use **xUnit** on **.NET 10** (e.g., `Fuchs.Tests`).
|
||||
- Run via Visual Studio Test Explorer or `dotnet test`.
|
||||
|
||||
## Conventions
|
||||
- One test class per unit under test; name it `<TypeUnderTest>Tests`.
|
||||
- Name test methods `Method_Scenario_ExpectedResult` (e.g., `ReadBalance_EmptyString_ThrowsInvalidDataException`).
|
||||
- Use `[Theory]` + `[InlineData]` for parameterized cases; `[Fact]` for single cases.
|
||||
- Arrange / Act / Assert structure; keep tests deterministic and independent (no shared mutable state, no real network/DB calls).
|
||||
|
||||
## What to Cover
|
||||
- Pure parsing/algorithmic helpers (MT940 parsing, date/decimal parsing, HTML cleanup, OData envelope handling, entity helpers) — these are the highest-value, side-effect-free targets.
|
||||
- For library classes that accept `ILogger<T>?`, pass `NullLogger<T>.Instance` (or a test logger) in tests.
|
||||
|
||||
## Workflow
|
||||
- After any code change, build the full solution and run the affected test project before concluding.
|
||||
- Treat a green run (e.g., `Fuchs.Tests` all passing) as the validation gate; never leave the suite red.
|
||||
- When fixing a bug, add or update a test that reproduces it where practical.
|
||||
Reference in New Issue
Block a user