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
77 changes: 77 additions & 0 deletions .github/instructions/project.instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
You are assisting on an open-source .NET API endpoint development project.
Default to changes that strengthen Onion Architecture, Clean Code, SOLID, security, and testability.

## Project Overview (Current Scope)

* **DataEngine**: A .NET API service aligned with **IDTA (Industrial Digital Twin Association) specifications** that dynamically generates Asset Administration Shell (AAS) structures (shell descriptors, submodels, and submodel elements).
- On each request, DataEngine loads a template from external registries/repositories. At this time, templates contain only structure and semantic IDs; they do not contain values.
- After DataEngine retrieves the template, it requests a plugin to provide the values needed to populate the template.
- Once DataEngine receives the values from the plugin, it fills the template and returns a complete AAS model to the client.

### Plugin (General Concept)

A **Plugin** is a separate .NET API service that acts as the **data provider** for DataEngine.

Plugins are responsible for:

* Accessing/storing business data (database, files, or external systems).
* Resolving semantic IDs requested by DataEngine.
* Returning metadata and submodel data via JSON schema-based contracts or IDs.
* Exposing a Plugin Manifest that describes supported semantic IDs and capabilities.

### Registry & Repository (General Concept)

The AAS **registry** and **repository** services expose template and descriptor endpoints that DataEngine consumes to retrieve templates.

* These services are external platform dependencies.
* Registry/repository components are **not developed or maintained by this project**.

## High-Level Architecture

```mermaid
flowchart LR

%% Users
UIUser[UI User]
APIUser[API User]

%% External Access
AASViewer["AAS Viewer (UI)"]
APIGateway["API Gateway"]

%% TwinEngine Components
DataEngine["DataEngine API"]
Plugin["Plugin API"]

%% Plugin Storage
PluginDB[(Plugin Database)]

%% External AAS Infrastructure
TemplateRegistry["AAS Template Registry (External)"]
SubmodelRepo["AAS Submodel Template Repository (External)"]
AASRegistry["AAS Registry (External)"]

%% User Flows
UIUser --> AASViewer
APIUser --> APIGateway
AASViewer --> APIGateway

%% Core Communication
APIGateway --> DataEngine
DataEngine --> Plugin
Plugin --> PluginDB

%% External Registry/Repository Integration
DataEngine --> TemplateRegistry
DataEngine --> SubmodelRepo
DataEngine --> AASRegistry
```

### Flow Summary

1. Clients (UI or API) send requests through the API Gateway to **DataEngine**.
2. DataEngine retrieves templates from external **AAS repositories/registries**.
3. Templates contain structure and semantic IDs but no values.
4. DataEngine requests semantic ID values from a **Plugin API** using a JSON schema structure.
5. Plugin resolves values from its database and returns them.
6. DataEngine populates the template and returns a complete AAS model to the client.
66 changes: 66 additions & 0 deletions .github/skills/architecture/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
---
name: architecture
description: 'Ensure .NET/C# code meets best practices for the solution/project.'
---

## Architecture (Onion / Clean Architecture)
- Maintain strict layer separation:
- **DomainModel**: pure domain types only (no ASP.NET Core, no database/ADO.NET, no config/options, no logging).
- **ApplicationLogic**: use cases, domain/application services, interfaces/ports; depends only on DomainModel.
- **Infrastructure**: database, file system, external services; implements ApplicationLogic interfaces.
- **Api**: HTTP/controllers/serialization; delegates to ApplicationLogic.
- Dependency direction: **Api** → **ApplicationLogic** → **DomainModel**; **Infrastructure** → **ApplicationLogic**.
- Never leak infrastructure/ASP.NET types into DomainModel/ApplicationLogic (e.g., `HttpContext`, `ControllerBase`, `DbConnection`, provider-specific types).
- Prefer defining ports (interfaces) in **ApplicationLogic** and implementing them in **Infrastructure**.

## API & DTOs
- Keep controllers thin: validation + request shaping + call handler/service + return mapped DTO.
- Do not return DomainModel types from controllers; always return DTOs under `Api/**/Responses`.
- Favor explicit request/response models over `JsonObject` when feasible; if dynamic JSON is required, isolate it to the API layer.
- Validation:
- Validate route/query/body inputs consistently.
- Keep validation rules close to request DTOs (or dedicated validators) and avoid duplication.

## Error Handling
- Use centralized exception handling via `ErrorController` (avoid scattered controller-level try/catch).
- Return stable JSON error contracts with correct HTTP status codes.
- Map **base exception types** in `ErrorController` (for example `NotFoundException`, `BadRequestException`, `ServiceUnavailableException`) rather than listing every custom exception.
- Ensure custom exceptions inherit from the correct base type and define safe, reusable default messages (`public const string DefaultMessage`).
- Keep exception messages human-readable, consistent in tone, and safe for external consumers (no stack traces, internal endpoint details, or sensitive data).
- Keep exception organization consistent:
- `ApplicationLogic/Exceptions/Base`
- `ApplicationLogic/Exceptions/Application`
- `ApplicationLogic/Exceptions/Infrastructure`
- At boundaries, translate Infrastructure exceptions into Application exceptions before they reach the API. In some cases, the Infrastructure layer may directly throw application-level exceptions — especially if it has enough domain context to do so.
- Keep API mapping focused on base exception types; custom exceptions should inherit from the correct base.
- Define safe reusable messages in custom exceptions (prefer `public const string DefaultMessage`).
- Translate infrastructure exceptions at boundaries before returning to API consumers.
- Do not leak raw/internal exception details to clients; return stable error contracts and log safely.

## Clean Code & SOLID
- Ensure SOLID principles compliance
- Avoid code duplication through base classes and utilities
- Prefer small, cohesive classes and methods.
- Watch for SRP violations (especially “handler/service” classes growing too large): extract focused collaborators.
- Prefer descriptive names; avoid abbreviations unless well-known.
- Avoid duplicate logic; introduce shared helpers only if reuse is clear.
- Follow existing patterns in this repo:
- `Api/**/Handler/*Handler.cs` orchestrates and delegates.
- `ApplicationLogic/Services/**` contains business/use-case logic.
- `Infrastructure/**` contains DB execution and provider implementations.

## Configuration & Settings

- Use strongly-typed configuration classes with data annotations
- Implement validation attributes (Required, NotEmptyOrWhitespace)
- Use IConfiguration binding for settings
- Support appsettings.json configuration files

### Unit Test Method Naming
Use one consistent pattern (choose the closest match to surrounding tests):
- Preferred: `{MethodUnderTest}_When{Condition}_{ReturnsOrThrows}{Expected}`
- Example: `ExecuteQueryAsync_WhenNoRows_ThrowsResourceNotFoundException`
- Also acceptable (existing pattern): `{MethodUnderTest}_Should{Expectation}_When{Condition}`
- Example: `DecodeBase64_ShouldThrow_OnNullOrWhitespace`
- For async tests, keep the `Async` suffix in the method under test portion of the name.

106 changes: 106 additions & 0 deletions .github/skills/asset-administration-shell-domain/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
---
name: asset-administration-shell-domain
description: Guidance for working with Asset Administration Shell (AAS) concepts, structures, and JSON representations when generating code or APIs
---

## 1. Core Concepts

### Asset Administration Shell (AAS)
- Represents the **digital twin of an asset**.
- Key fields:
- `id` (globally unique identifier)
- `idShort` (human-readable identifier)
- `assetInformation`
- `submodels`

Each asset can contain multiple **submodels** describing different aspects.

---

## 2. Submodels
- Define specific aspects of an asset:
- Technical data
- Operational data
- Identification
- Documentation
- Condition monitoring

**Structure:**
- `id`
- `idShort`
- `semanticId`
- `submodelElements`

---

## 3. Submodel Elements
Submodel elements represent structured data inside a submodel.

**Types include:**
- Property
- MultiLanguageProperty
- Range
- File
- Blob
- ReferenceElement
- RelationshipElement
- SubmodelElementCollection
- SubmodelElementList

Elements can be **nested recursively**.

---

## 4. Identifiers
- **id** → Globally unique identifier for AAS or Submodel.
- **idShort** → Human-readable identifier within the AAS structure.
- **semanticId** → Reference defining the semantic meaning of an element (often linked to IEC CDD or ECLASS dictionaries).

---

## 5. References
Used to connect elements:
- **ModelReference** → Points to other AAS objects.
- **GlobalReference** → Points to external definitions.

---

## 6. JSON Representation
AAS data is commonly exchanged in JSON.

**Characteristics:**
- Deeply nested objects
- Arrays of submodel elements
- Semantic ID references
- Structured data types


---

## 7. Coding Guidelines
When generating **C# or API code** for AAS:
- Use strongly typed models (avoid dynamic objects).
- Respect hierarchical structure:
- AssetAdministrationShell → Submodel → SubmodelElement
- Handle nested collections correctly.
- Use `System.Text.Json` for serialization.
- Preserve semantics of `semanticId` and `idShort`.

---

## 8. API Design Guidelines
For APIs handling AAS data:
- Use RESTful endpoints.
- Return structured JSON consistent with AAS.
- Support retrieval of:
- AAS
- Submodels
- Submodel elements
- Validate identifiers and semantic references.

---

## 9. References
For deeper study of AAS specifications:
- [Details of the Asset Administration Shell Part 1 V3.0RC02 (PDF)](https://industrialdigitaltwin.org/wp-content/uploads/2022/06/DetailsOfTheAssetAdministrationShell_Part1_V3.0RC02_Final1.pdf)
- [IDTA Specification v3.1.1 – Submodel Elements](https://industrialdigitaltwin.io/aas-specifications/IDTA-01001/v3.1.1/spec-metamodel/submodel-elements.html#entity-attributes)
49 changes: 49 additions & 0 deletions .github/skills/csharp-async/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
---
name: csharp-async
description: 'Get best practices for C# async programming'
---

# C# Async Programming Best Practices

Your goal is to help me follow best practices for asynchronous programming in C#.

## Naming Conventions

- Use the 'Async' suffix for all async methods
- Match method names with their synchronous counterparts when applicable (e.g., `GetDataAsync()` for `GetData()`)

## Return Types

- Return `Task<T>` when the method returns a value
- Return `Task` when the method doesn't return a value
- Consider `ValueTask<T>` for high-performance scenarios to reduce allocations
- Avoid returning `void` for async methods except for event handlers

## Exception Handling

- Use try/catch blocks around await expressions
- Avoid swallowing exceptions in async methods
- Use `ConfigureAwait(false)` when appropriate to prevent deadlocks in library code
- Propagate exceptions with `Task.FromException()` instead of throwing in async Task returning methods

## Performance

- Use `Task.WhenAll()` for parallel execution of multiple tasks
- Use `Task.WhenAny()` for implementing timeouts or taking the first completed task
- Avoid unnecessary async/await when simply passing through task results
- Consider cancellation tokens for long-running operations

## Common Pitfalls

- Never use `.Wait()`, `.Result`, or `.GetAwaiter().GetResult()` in async code
- Avoid mixing blocking and async code
- Don't create async void methods (except for event handlers)
- Always await Task-returning methods

## Implementation Patterns

- Implement the async command pattern for long-running operations
- Use async streams (IAsyncEnumerable<T>) for processing sequences asynchronously
- Consider the task-based asynchronous pattern (TAP) for public APIs

When reviewing my C# code, identify these issues and suggest improvements that follow these best practices.
68 changes: 68 additions & 0 deletions .github/skills/csharp-xunit/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
---
name: csharp-xunit
description: 'Get best practices for XUnit unit testing, including data-driven tests'
---

# XUnit Best Practices

Your goal is to help me write effective unit tests with XUnit, covering both standard and data-driven testing approaches.

## Project Setup

- Use a separate test project with naming convention `[ProjectName].Tests`
- Reference Microsoft.NET.Test.Sdk, xunit, and xunit.runner.visualstudio packages
- Create test classes that match the classes being tested (e.g., `CalculatorTests` for `Calculator`)
- Use .NET SDK test commands: `dotnet test` for running tests

## Test Structure

- No test class attributes required (unlike MSTest/NUnit)
- Use fact-based tests with `[Fact]` attribute for simple tests
- Follow the Arrange-Act-Assert (AAA) pattern
- Name tests using the pattern `MethodName_Scenario_ExpectedBehavior`
- Use constructor for setup and `IDisposable.Dispose()` for teardown
- Use `IClassFixture<T>` for shared context between tests in a class
- Use `ICollectionFixture<T>` for shared context between multiple test classes

## Standard Tests

- Keep tests focused on a single behavior
- Avoid testing multiple behaviors in one test method
- Use clear assertions that express intent
- Include only the assertions needed to verify the test case
- Make tests independent and idempotent (can run in any order)
- Avoid test interdependencies

## Data-Driven Tests

- Use `[Theory]` combined with data source attributes
- Use `[InlineData]` for inline test data
- Use `[MemberData]` for method-based test data
- Use `[ClassData]` for class-based test data
- Create custom data attributes by implementing `DataAttribute`
- Use meaningful parameter names in data-driven tests

## Assertions

- Use `Assert.Equal` for value equality
- Use `Assert.Same` for reference equality
- Use `Assert.True`/`Assert.False` for boolean conditions
- Use `Assert.Contains`/`Assert.DoesNotContain` for collections
- Use `Assert.Matches`/`Assert.DoesNotMatch` for regex pattern matching
- Use `Assert.Throws<T>` or `await Assert.ThrowsAsync<T>` to test exceptions
- Use fluent assertions library for more readable assertions

## Mocking and Isolation

- Consider using Moq or NSubstitute alongside XUnit
- Mock dependencies to isolate units under test
- Use interfaces to facilitate mocking
- Consider using a DI container for complex test setups

## Test Organization

- Group tests by feature or component
- Use `[Trait("Category", "CategoryName")]` for categorization
- Use collection fixtures to group tests with shared dependencies
- Consider output helpers (`ITestOutputHelper`) for test diagnostics
- Skip tests conditionally with `Skip = "reason"` in fact/theory attributes
Loading