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
141 changes: 141 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Project Overview

Shesha is an open-source Low-Code development framework for .NET developers. It combines ASP.NET Core, ABP.io, React, and Next.js with drag-and-drop form building capabilities to reduce code requirements by 80%+ for typical business applications.

## Repository Structure

```
shesha-framework/
├── shesha-core/ # .NET backend framework (main solution)
├── shesha-reactjs/ # React/TypeScript component library (@shesha-io/reactjs)
├── shesha-starter/ # Starter template for new projects
├── shesha-functional-tests/ # Functional test suite
```

## Build Commands

### Backend (.NET 8.0)

```bash
# Build the framework
dotnet build shesha-core/Shesha.sln --configuration Release

# Run backend tests (requires Docker for Testcontainers)
cd shesha-core/test/Shesha.Tests
dotnet test Shesha.Tests.csproj --configuration Release
```

### Frontend (Node.js 20+, npm 10+)

```bash
# Install dependencies
cd shesha-reactjs
npm install

# Build the library (includes lint check)
npm run build

# Run tests
npm test

# Run single test file
npm test -- --testPathPattern="path/to/test"

# Watch mode for development
npm run start

# Lint
npm run lint
npm run lint-fix

# Type check (strict null checks)
npm run type-check
```

### Starter Application

```bash
# Frontend development
cd shesha-starter/frontend
npm install
npm run dev

# Production build
npm run build
npm run start
```

## Architecture

### Backend Layered Architecture

1. **Domain Layer** (`Shesha.Core`) - Domain entities, interfaces, business logic
2. **Framework Layer** (`Shesha.Framework`) - Dynamic CRUD, metadata, configuration studio, JSON logic engine
3. **Application Layer** (`Shesha.Application`) - Application services, DTOs, CRUD app services
4. **Web Layer** (`Shesha.Web.Host`, `Shesha.Web.Core`) - API controllers, authorization, Swagger

### ABP Module System

Modules inherit from `SheshaModule` with lifecycle hooks:
- `PreInitialize()` - Register services
- `Initialize()` - Configure services
- `InitializeConfigurationAsync()` - Async initialization

Key modules: `Shesha.NHibernate` (ORM), `Shesha.GraphQL`, `Shesha.Scheduler` (HangFire), `Shesha.FluentMigrator`

### Frontend Architecture

**Provider Pattern** - Context providers for state management:
- `SheshaApplicationProvider` - App-level configuration
- `FormDesignerProvider` - Form builder context
- `DataContextProvider` - Data management (appContext, pageContext, formContext)

**Key Directories** (`shesha-reactjs/src/`):
- `components/` - 40+ UI components (dataTable, autocomplete, modal, etc.)
- `designer-components/` - Form builder components
- `providers/` - Context providers
- `hooks/` - Custom React hooks (useGet, useMutate, useFormExpression, etc.)
- `apis/` - API integration layer

### Dynamic CRUD Pattern

Backend automatically generates CRUD APIs from domain entities via `DynamicCrudAppService`. Enable through entity configuration - no boilerplate code needed.

### DataContext (v0.44+)

Replaces GlobalState to prevent unnecessary re-renders:
- `appContext` - App-wide data
- `pageContext` - Page-scoped data
- `formContext` - Form-scoped data

## Testing

### Backend Tests
- Framework: xUnit with Testcontainers
- **Requires Docker daemon running**
- Tests run against SQL Server/PostgreSQL in containers

### Frontend Tests
- Framework: Jest with React Testing Library
- Config: `jest.config.js`
- Test files: `*.test.ts` or `*.spec.tsx` in `src/`

## Key Patterns

### Form Configuration
- Default values set via `onAfterDataLoad` scripts (not DefaultValue property)
- Scripts execute in dependency order
- Direct write access to `data` and `context` without SetFieldValue

### Commits
Use conventional commits via Commitizen: `npm run commit`

## Database Support

- Primary: SQL Server, PostgreSQL (via NHibernate)
- Additional: MongoDB (Shesha.MongoRepository), PostGIS for geographic data
- Migrations: FluentMigrator
211 changes: 211 additions & 0 deletions shesha-core/Shesha.sln

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public class SheshaNHibernateInterceptor : EmptyInterceptor

public SheshaNHibernateInterceptor(IIocManager iocManager)
{
EntityChangeEventHelper = default!;
EntityChangeEventHelper = NullEntityChangeEventHelper.Instance;
_iocManager = iocManager;

_abpSession =
Expand Down Expand Up @@ -103,8 +103,8 @@ public override bool OnSave(object entity, object id, object[] state, string[] p
}
}

EntityChangeEventHelper.TriggerEntityCreatingEvent(entity);
EntityChangeEventHelper.TriggerEntityCreatedEventOnUowCompleted(entity);
EntityChangeEventHelper?.TriggerEntityCreatingEvent(entity);
EntityChangeEventHelper?.TriggerEntityCreatedEventOnUowCompleted(entity);

TriggerDomainEvents(entity);
EntityHistoryHelper?.AddEntityChange(entity);
Expand Down Expand Up @@ -141,7 +141,7 @@ public override bool OnFlushDirty(object entity, object id, object[] currentStat
}
}

if (entity is ISoftDelete && entity.As<ISoftDelete>().IsDeleted)
if (entity is ISoftDelete && entity.As<ISoftDelete>().IsDeleted && previousState != null)
{
//Is deleted before? Normally, a deleted entity should not be updated later but I preferred to check it.
var previousIsDeleted = false;
Expand Down Expand Up @@ -182,19 +182,19 @@ public override bool OnFlushDirty(object entity, object id, object[] currentStat
}
}

EntityChangeEventHelper.TriggerEntityDeletingEvent(entity);
EntityChangeEventHelper.TriggerEntityDeletedEventOnUowCompleted(entity);
EntityChangeEventHelper?.TriggerEntityDeletingEvent(entity);
EntityChangeEventHelper?.TriggerEntityDeletedEventOnUowCompleted(entity);
}
else
{
EntityChangeEventHelper.TriggerEntityUpdatingEvent(entity);
EntityChangeEventHelper.TriggerEntityUpdatedEventOnUowCompleted(entity);
EntityChangeEventHelper?.TriggerEntityUpdatingEvent(entity);
EntityChangeEventHelper?.TriggerEntityUpdatedEventOnUowCompleted(entity);
}
}
else
{
EntityChangeEventHelper.TriggerEntityUpdatingEvent(entity);
EntityChangeEventHelper.TriggerEntityUpdatedEventOnUowCompleted(entity);
EntityChangeEventHelper?.TriggerEntityUpdatingEvent(entity);
EntityChangeEventHelper?.TriggerEntityUpdatedEventOnUowCompleted(entity);
}

TriggerDomainEvents(entity);
Expand All @@ -206,8 +206,8 @@ public override bool OnFlushDirty(object entity, object id, object[] currentStat

public override void OnDelete(object entity, object id, object[] state, string[] propertyNames, IType[] types)
{
EntityChangeEventHelper.TriggerEntityDeletingEvent(entity);
EntityChangeEventHelper.TriggerEntityDeletedEventOnUowCompleted(entity);
EntityChangeEventHelper?.TriggerEntityDeletingEvent(entity);
EntityChangeEventHelper?.TriggerEntityDeletedEventOnUowCompleted(entity);

TriggerDomainEvents(entity);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
using Abp.Dependency;
using Abp.Dependency;
using Castle.Windsor.MsDependencyInjection;
using Microsoft.Extensions.DependencyInjection;
using Shesha.Identity;

namespace Shesha.Tests.DependencyInjection
namespace Shesha.Testing.DependencyInjection
{
public static class ServiceCollectionRegistrar
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
namespace Shesha.Tests.Fixtures
namespace Shesha.Testing.Fixtures
{
/// <summary>
/// Database fixture
/// Database fixture providing connection configuration for integration tests.
/// </summary>
public interface IDatabaseFixture
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using Xunit;

namespace Shesha.Testing.Fixtures
{
/// <summary>
/// Shared local SQL Server fixture collection.
/// </summary>
[CollectionDefinition(Name)]
public class LocalSqlServerCollection : ICollectionFixture<LocalSqlServerFixture>
{
public const string Name = "LocalSqlServer";
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Configuration;
using System.Threading.Tasks;
using Xunit;

namespace Shesha.Tests.Fixtures
namespace Shesha.Testing.Fixtures
{
/// <summary>
/// xUnit fixture for a local SQL Server instance. Reads connection from appsettings.Test.json.
/// </summary>
public class LocalSqlServerFixture : IDatabaseFixture, IAsyncLifetime
{
public string ConnectionString { get; private set; }
public string ConnectionString { get; private set; } = default!;

public DbmsType DbmsType { get; private set; } = DbmsType.SQLServer;

Expand All @@ -16,7 +19,7 @@ public LocalSqlServerFixture()

public Task InitializeAsync()
{
var config = new ConfigurationBuilder().AddJsonFile("appsettings.json").Build();
var config = new ConfigurationBuilder().AddJsonFile("appsettings.Test.json").Build();
DbmsType = config.GetDbmsType();
ConnectionString = config.GetRequiredConnectionString("TestDB");

Expand Down
13 changes: 13 additions & 0 deletions shesha-core/src/Shesha.Testing/Fixtures/PostgreSqlCollection.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using Xunit;

namespace Shesha.Testing.Fixtures
{
/// <summary>
/// Shared PostgreSql fixture (Testcontainers).
/// </summary>
[CollectionDefinition(Name)]
public class PostgreSqlCollection : ICollectionFixture<PostgreSqlFixture>
{
public const string Name = "SharedPostgreSql";
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
using System.Threading.Tasks;
using System.Threading.Tasks;
using Testcontainers.PostgreSql;
using Xunit;

namespace Shesha.Tests.Fixtures
namespace Shesha.Testing.Fixtures
{
/// <summary>
/// xUnit fixture that spins up a PostgreSQL Docker container via Testcontainers.
/// </summary>
[Collection("Sequential")]
public class PostgreSqlFixture : IDatabaseFixture, IAsyncLifetime
{
Expand All @@ -13,32 +16,29 @@ public class PostgreSqlFixture : IDatabaseFixture, IAsyncLifetime

private readonly PostgreSqlContainer _postgresContainer;

public string ConnectionString { get; private set; }
public string ConnectionString { get; private set; } = default!;

public DbmsType DbmsType => DbmsType.PostgreSQL;

public PostgreSqlFixture()
{
// Configure the PostgreSQL container
_postgresContainer = new PostgreSqlBuilder()
.WithImage("boxfusionregistry.azurecr.io/shesha-framework-postgresql:1.0") // Use the latest PostgreSQL image
.WithDatabase(DbName) // Set the database name
.WithUsername(Login) // Set the username
.WithPassword(Password) // Set the password
.WithExposedPort(5432) // Expose the default PostgreSQL port
.WithImage("boxfusionregistry.azurecr.io/shesha-framework-postgresql:1.0")
.WithDatabase(DbName)
.WithUsername(Login)
.WithPassword(Password)
.WithExposedPort(5432)
.Build();
}

public async Task InitializeAsync()
{
// Start the container and initialize the connection string
await _postgresContainer.StartAsync();
ConnectionString = $"Host=127.0.0.1;Port={_postgresContainer.GetMappedPublicPort(5432)};Database={DbName};Username={Login};Password={Password};";
Comment thread
ihouvet marked this conversation as resolved.
}

public async Task DisposeAsync()
{
// Stop and remove the container
await _postgresContainer.StopAsync();
}
}
Expand Down
13 changes: 13 additions & 0 deletions shesha-core/src/Shesha.Testing/Fixtures/SqlServerCollection.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using Xunit;

namespace Shesha.Testing.Fixtures
{
/// <summary>
/// Shared Sql Server fixture (Testcontainers).
/// </summary>
[CollectionDefinition(Name)]
public class SqlServerCollection : ICollectionFixture<SqlServerFixture>
{
public const string Name = "SharedSqlServer";
}
}
Loading