Skip to content
Draft
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
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using Abp.Runtime.Validation;
using Abp.UI;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using Shesha.Application.Services.Dto;
using Shesha.ConfigurationItems.Cache;
using Shesha.ConfigurationItems.Distribution;
Expand Down Expand Up @@ -321,5 +322,91 @@ private async Task<ConfigurationItemBase> GetItemAsync(Guid id)
throw new EntityNotFoundException(typeof(ConfigurationItemBase), id);
return item;
}

public async Task<List<ExportTreeFlatItem>> GetExportFlatTreeAsync(ConfigurationItemViewMode mode)
{
var query = Repository.GetAll();
switch (mode)
{
case ConfigurationItemViewMode.Live:
query = query.Where(e => e.VersionStatus == ConfigurationItemVersionStatus.Live);
break;
Comment on lines +326 to +333
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Querying the wrong repository likely excludes most item types

Repository points to ConfigurationItem (the concrete type bound to the CRUD base), while export needs all ConfigurationItemBase derivatives. Use _itemsRepository to include all item types.

-        public async Task<List<ExportTreeFlatItem>> GetExportFlatTreeAsync(ConfigurationItemViewMode mode) 
+        public async Task<List<ExportTreeFlatItem>> GetExportFlatTreeAsync(ConfigurationItemViewMode mode) 
         {
-            var query = Repository.GetAll();
+            var query = _itemsRepository.GetAll();
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public async Task<List<ExportTreeFlatItem>> GetExportFlatTreeAsync(ConfigurationItemViewMode mode)
{
var query = Repository.GetAll();
switch (mode)
{
case ConfigurationItemViewMode.Live:
query = query.Where(e => e.VersionStatus == ConfigurationItemVersionStatus.Live);
break;
public async Task<List<ExportTreeFlatItem>> GetExportFlatTreeAsync(ConfigurationItemViewMode mode)
{
var query = _itemsRepository.GetAll();
switch (mode)
{
case ConfigurationItemViewMode.Live:
query = query.Where(e => e.VersionStatus == ConfigurationItemVersionStatus.Live);
break;
🤖 Prompt for AI Agents
In
shesha-core/src/Shesha.Application/ConfigurationItems/ConfigurationItemAppService.cs
around lines 326 to 333, the method currently uses Repository (which is the
concrete ConfigurationItem repository) but the export must include all
ConfigurationItemBase-derived types; replace Repository.GetAll() with
_itemsRepository.GetAll() (or the equivalent items repository field) so the
query targets the polymorphic base set, then apply the same VersionStatus filter
and subsequent projections to that query.

case ConfigurationItemViewMode.Ready:
{
var statuses = new ConfigurationItemVersionStatus[] {
ConfigurationItemVersionStatus.Live,
ConfigurationItemVersionStatus.Ready
};
query = query.Where(e => e.IsLast && statuses.Contains(e.VersionStatus));
break;
}
case ConfigurationItemViewMode.Latest:
{
var statuses = new ConfigurationItemVersionStatus[] {
ConfigurationItemVersionStatus.Live,
ConfigurationItemVersionStatus.Ready,
ConfigurationItemVersionStatus.Draft
};
query = query.Where(e => e.IsLast && statuses.Contains(e.VersionStatus));
break;
}
default:
break;
}
var items = await query
.OrderBy(e => e.Module!.Name)
.ThenBy(e => e.Name)
.Select(e => new ExportTreeFlatItem
{
Id = e.Id,
ItemType = e.ItemType,
Name = e.Name,
Label = e.Label,
Description = e.Description,
Module = e.Module != null
? new ExportTreeFlatItemModule {
Id = e.Module.Id,
Name = e.Module.Name,
Description = e.Module.Description
}
: null,
Application = e.Application != null
? new ExportTreeFlatItemApplication {
Id = e.Application.Id,
AppKey = e.Application.AppKey,
Name = e.Application.Name,
}
: null,
})
.ToListAsync();

return items;
}

public class ExportTreeFlatItem
{
public Guid Id { get; set; }
public string Name { get; set; }
public string ItemType { get; set; }
public string? Label { get; set; }
public string? Description { get; set; }
public ExportTreeFlatItemModule? Module { get; set; }
public ExportTreeFlatItemApplication? Application { get; set; }

}

public class ExportTreeFlatItemModule
{
public Guid Id { get; set; }
public string Name { get; set; }
public string? Description { get; set; }
}

public class ExportTreeFlatItemApplication
{
public Guid Id { get; set; }
public string AppKey { get; set; }
public string Name { get; set; }
}
}
}
2 changes: 2 additions & 0 deletions shesha-core/src/Shesha.Application/Shesha.Application.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@
<None Remove="ConfigMigrations\package20230411_1238.shaconfig" />
<None Remove="ConfigMigrations\package20231105_1942.shaconfig" />
<None Remove="ConfigMigrations\package20250805_1824.shaconfig" />
<None Remove="ConfigMigrations\package20250910_1513.shaconfig" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="ConfigMigrations\package20240201_1221.shaconfig" />
Expand Down Expand Up @@ -133,6 +134,7 @@
<EmbeddedResource Include="ConfigMigrations\package20230324_1835.shaconfig" />
<EmbeddedResource Include="ConfigMigrations\package20230411_1238.shaconfig" />
<EmbeddedResource Include="ConfigMigrations\package20250805_1824.shaconfig" />
<EmbeddedResource Include="ConfigMigrations\package20250910_1513.shaconfig" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="AsyncFixer" Version="1.6.0">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ namespace Shesha.Configuration.Security
public class SecuritySettings
{
/// <summary>
/// Auto logoff timeout
/// Auto logoff timeout in minutes
/// </summary>
public int AutoLogoffTimeout { get; set; }

Expand Down Expand Up @@ -57,5 +57,20 @@ public class SecuritySettings
[Display(Name = "Default endpoint access", Description = "Used for 'Inherited' endpoint access")]
public RefListPermissionedAccess DefaultEndpointAccess { get; set; }

/// <summary>
/// Enable logout when user is inactive
/// </summary>
public bool LogoutWhenUserInactive { get; set; }

/// <summary>
/// Warning time before logout in seconds
/// </summary>
public int LogoutWarningSeconds { get; set; }

/// <summary>
/// Enable logout when browser is closed/hidden
/// </summary>
public bool LogoutWhenBrowserClosed { get; set; }

Comment on lines +60 to +74
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add validation attributes for new settings to enforce safe ranges

Prevent misconfiguration and align with UI constraints.

 using System.ComponentModel.DataAnnotations;
@@
-        public bool LogoutWhenUserInactive { get; set; }
+        public bool LogoutWhenUserInactive { get; set; }
@@
-        public int LogoutWarningSeconds { get; set; }
+        [Range(10, 300)]
+        public int LogoutWarningSeconds { get; set; }
@@
-        public bool LogoutWhenBrowserClosed { get; set; }
+        public bool LogoutWhenBrowserClosed { get; set; }

Optionally also validate:

-        public int AutoLogoffTimeout { get; set; }
+        [Range(0, 1440)]
+        public int AutoLogoffTimeout { get; set; }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
/// <summary>
/// Enable logout when user is inactive
/// </summary>
public bool LogoutWhenUserInactive { get; set; }
/// <summary>
/// Warning time before logout in seconds
/// </summary>
public int LogoutWarningSeconds { get; set; }
/// <summary>
/// Enable logout when browser is closed/hidden
/// </summary>
public bool LogoutWhenBrowserClosed { get; set; }
/// <summary>
/// Enable logout when user is inactive
/// </summary>
public bool LogoutWhenUserInactive { get; set; }
/// <summary>
/// Warning time before logout in seconds
/// </summary>
[Range(10, 300)]
public int LogoutWarningSeconds { get; set; }
/// <summary>
/// Enable logout when browser is closed/hidden
/// </summary>
public bool LogoutWhenBrowserClosed { get; set; }
🤖 Prompt for AI Agents
In shesha-core/src/Shesha.Framework/Configuration/Security/SecuritySettings.cs
around lines 60 to 74, the new settings lack validation attributes; add data
annotation attributes to enforce safe ranges: decorate LogoutWarningSeconds with
[Range(5, 3600)] (or another agreed minimum/maximum seconds) to prevent
too-small/too-large values, and optionally add [Required] if configuration must
provide the value; no validation attributes are needed for the boolean flags,
but you may add [Display(Name=...)] or [DefaultValue(false)] if desired for
clarity.

}
}
7 changes: 6 additions & 1 deletion shesha-core/src/Shesha.Framework/SheshaFrameworkModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ public class SheshaFrameworkModule : SheshaModule
#endif
};

public bool SkipAppWarmUp { get; set; }

Comment on lines +46 to +47
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

❓ Verification inconclusive

Where is SkipAppWarmUp set? Verify usage across the solution.

Ensure this flag is actually set from startup/config and not left at default false.

Run:


🏁 Script executed:

#!/bin/bash
set -euo pipefail
# Find where the flag is assigned or read
rg -nP '\bSkipAppWarmUp\b' -C2

Length of output: 0


🏁 Script executed:

#!/bin/bash
# Display the first 200 lines of SheshaFrameworkModule for inspection
sed -n '1,200p' shesha-core/src/Shesha.Framework/SheshaFrameworkModule.cs

Length of output: 100


🏁 Script executed:

#!/bin/bash
set -euo pipefail
# Locate SheshaFrameworkModule.cs in the repo
fd --type f --name 'SheshaFrameworkModule.cs'

# Search for any assignments or usages of SkipAppWarmUp throughout the codebase
rg -nP '\bSkipAppWarmUp\b' -C2

Length of output: 250


🏁 Script executed:

#!/bin/bash
set -euo pipefail
# Locate SheshaFrameworkModule.cs in the repository
find . -type f -name '*SheshaFrameworkModule.cs'
# Search for any references or assignments of SkipAppWarmUp across the codebase
rg -n 'SkipAppWarmUp' -C2

Length of output: 0


Wire up or remove SkipAppWarmUp flag
The SkipAppWarmUp property declared in SheshaFrameworkModule.cs (line 46) has no assignments or usages across the codebase. Either bind it from configuration and consume it in your startup logic, or remove this unused flag.

🤖 Prompt for AI Agents
In shesha-core/src/Shesha.Framework/SheshaFrameworkModule.cs around lines 46-47,
the public bool SkipAppWarmUp property is declared but never used; either remove
it or wire it to configuration and use it to control startup warm-up. To fix,
choose one: 1) Remove the property and any dead-code references (if none exist)
to keep the module clean; or 2) Bind it from configuration (add IConfiguration
or IOptions<T> injection where modules are configured), read the value into the
module during initialization, and guard the app warm-up logic (where warm-up
tasks run in startup/bootstrap) with if (!SkipAppWarmUp) { ... } so the flag
actually controls whether warm-up runs.

public SheshaFrameworkModule()
{
}
Expand Down Expand Up @@ -129,7 +131,10 @@ public override void Initialize()
s.DefaultAccountLockoutSeconds.WithDefaultValue(300 /* 5 minutes */);
s.SecuritySettings.WithDefaultValue(new SecuritySettings
{
AutoLogoffTimeout = 0,
AutoLogoffTimeout = 15, // 15 minutes default,
LogoutWhenUserInactive = false,
LogoutWarningSeconds = 60,
LogoutWhenBrowserClosed = false,
UseResetPasswordViaEmailLink = true,
ResetPasswordEmailLinkLifetime = 60,
UseResetPasswordViaSmsOtp = true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using Shesha.DynamicEntities.Dtos;
using Shesha.Extensions;
using Shesha.Metadata;
using Shesha.Reflection;
using Shesha.Utilities;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
Expand Down Expand Up @@ -158,8 +159,11 @@ public bool Validate(object obj, string propertyName, object? value, List<Valida
}
break;
case DataTypes.Number:
var b = double.TryParse(value?.ToString(), out double val);
var stringValue = value?.ToString();
if (string.IsNullOrWhiteSpace(stringValue) && propInfo != null && propInfo.IsNullable())
return true;

var b = double.TryParse(stringValue, out double val);
if (!b)
Comment on lines +162 to 167
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Blank numeric strings can bypass Required; treat whitespace as null and enforce Required.

Current logic returns true for whitespace when the target property is nullable, but it doesn’t honor propConfig.Required for “blank” inputs (value != null). This can let required numeric fields pass as blank.

Apply:

-                    var stringValue = value?.ToString();
-                    if (string.IsNullOrWhiteSpace(stringValue) && propInfo != null && propInfo.IsNullable())
-                        return true;
-
-                    var b = double.TryParse(stringValue, out double val);
+                    var stringValue = value?.ToString();
+                    if (string.IsNullOrWhiteSpace(stringValue))
+                    {
+                        if (propConfig.Required && !propConfig.Suppress)
+                        {
+                            validationResult.Add(new ValidationResult(hasMessage
+                                ? propConfig.ValidationMessage
+                                : $"Property '{friendlyName}' is required."));
+                            return false;
+                        }
+                        if (propInfo != null && propInfo.IsNullable())
+                            return true;
+                    }
+
+                    var b = double.TryParse(stringValue, out double val);

Optional: consider culture-aware parsing to avoid ambiguity with decimals/thousands (requires using System.Globalization;):

var b = double.TryParse(stringValue, NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.CurrentCulture, out double val);
🤖 Prompt for AI Agents
In shesha-core/src/Shesha.Framework/Validations/EntityPropertyValidator.cs
around lines 162 to 167, the validator currently treats whitespace-only numeric
strings as null when the property is nullable, which allows required numeric
fields to bypass validation; change the logic so that if value is a
whitespace-only string it is treated as null only when value == null (i.e.,
preserve that blank input is still a provided value) and enforce
propConfig.Required for non-null blank inputs—specifically, do not return true
for whitespace when value != null and propConfig.Required is set; additionally,
replace the current double.TryParse with a culture-aware parse call (e.g., use
NumberStyles.Float | NumberStyles.AllowThousands and CultureInfo.CurrentCulture)
to reliably parse numeric input across locales.

{
validationResult.Add(new ValidationResult($"Property '{friendlyName}' should be in a number format"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,20 @@ namespace Shesha.Warmup
public class ApplicationWarmupService : IAsyncEventHandler<DatabaseInitializedEventData>, ITransientDependency
{
private readonly IModelConfigurationManager _entityConfigs;
public ILogger Logger = NullLogger.Instance;
private readonly SheshaFrameworkModule _frameworkModule;
public ILogger Logger = NullLogger.Instance;

public ApplicationWarmupService(IModelConfigurationManager entityConfigs)
public ApplicationWarmupService(IModelConfigurationManager entityConfigs, SheshaFrameworkModule frameworkModule)
{
_entityConfigs = entityConfigs;
_frameworkModule = frameworkModule;
}

public Task HandleEventAsync(DatabaseInitializedEventData eventData)
{
if (_frameworkModule.SkipAppWarmUp)
return Task.CompletedTask;

var task = Task.Run(async () =>
{
try
Expand Down
2 changes: 1 addition & 1 deletion shesha-core/src/Shesha.Web.Host/appsettings.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"ConnectionStrings": {
"Default": "Data Source=.; Initial Catalog=function-shesha-test;Integrated Security=SSPI;TrustServerCertificate=True;"
"Default": "Server=localhost,1433;Initial Catalog=enterpriseproject;Persist Security Info=False;User ID=sa;Password=@123Shesha;MultipleActiveResultSets=True;Encrypt=True;TrustServerCertificate=True;Connection Timeout=60;"
},
"App": {
"ServerRootAddress": "http://sheshacore-demo.boxfusion.co.za",
Expand Down
3 changes: 2 additions & 1 deletion shesha-core/test/Shesha.Tests/SheshaTestModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,10 @@ namespace Shesha.Tests
)]
public class SheshaTestModule : AbpModule
{
public SheshaTestModule(SheshaNHibernateModule nhModule)
public SheshaTestModule(SheshaNHibernateModule nhModule, SheshaFrameworkModule frwkModule)
{
nhModule.SkipDbSeed = false; // Set to false to apply DB Migration files on start up
frwkModule.SkipAppWarmUp = true;
}

public override void PreInitialize()
Expand Down
Loading