Serenity 6.4.3 Release Notes (2023-01-07)
Flutter-Based Mobile Applications for Android/iOS (StartSharp Enterprise)
You can now explore our new Flutter
-based mobile applications at https://demo.serenity.is.
These samples showcase integration with Serenity application services using OpenID Connect/JWT authentication and include the WorkLog module implementation, localization, and dark theme options.
Please note that access to the source code and support for the mobile application is exclusively available to StartSharp Enterprise
customers.
OpenIddict Integration and JWT Authentication Options (StartSharp)
We have introduced a new package called Serenity.Pro.OpenIddict
for StartSharp customers. This package facilitates JWT authentication through the OpenID Connect
protocol.
To enable OpenIddict
in a new StartSharp project, simply set the Enabled
flag to true
in the appsettings.json
file:
OpenIdSettings: {
`Enabled`: true
}
We will provide a document with instructions on adding OpenID integration to an existing StartSharp project.
Introducing AutoValidateAntiforgeryIgnoreBearerFilter Attribute
Antiforgery in ASP.NET Core is a mechanism designed to prevent request forgery attacks. You can learn more about it at the following link:
https://learn.microsoft.com/en-us/aspnet/core/security/anti-request-forgery?view=aspnetcore-7.0
While anti-forgery tokens mechanism in ASP.NET Core helps protect against such attacks when using Cookies
authentication, it can be challenging to validate the anti-forgery token when utilizing JWT
and Bearer
authentication headers, particularly in mobile application service calls.
To address this issue, we have introduced a new attribute called AutoValidateAntiforgeryIgnoreBearerAttribute
that you can use in place of AutoValidateAntiforgeryTokenAttribute
in the Startup.cs
file:
- options.Filters.Add(typeof(AutoValidateAntiforgeryTokenAttribute));
+ options.Filters.Add(typeof(AutoValidateAntiforgeryIgnoreBearerAttribute));
Unlike AutoValidateAntiforgeryTokenAttribute
, AutoValidateAntiforgeryIgnoreBearerAttribute
will bypass anti-forgery validation if the request contains an Authentication: Bearer
header and does not include any cookie headers.
We believe this change will not introduce any security vulnerabilities, as we believe that XCSRF attacks are not possible with such requests. However, if you have concerns regarding this risk, you should avoid using this attribute.
New Glassy Light Theme (StartSharp)
We have introduced a new theme called Glassy Light (glassy-light)
to the Serenity.Pro.Theme
with background and transparency effects:
[Breaking Change]
Renaming of Q.getTypeName
to Q.getTypeShortName
The full name for a type registered with @Decorators.registerClass
can usually be accessed using Q.getTypeFullName
. Previously, we had a Q.getTypeName
method that returned only the name portion (e.g., without the namespace), similar to the Type.Name
property in .NET. However, this method was often confused with getTypeFullName
, so we renamed it to Q.getTypeShortName
.
We have also improved the handling of some internal type properties, such as the __typeName
static property, which stored the full name for registered types. This property is now non-enumerable, along with its internal counterpart __typeName$
, which has been removed. These properties were originally necessary for TypeScript/Saltaralle and older browsers like IE9, but we no longer support them.
Improved Handling of Type Registries such as EnumTypeRegistry
, EditorTypeRegistry
, and FormatterTypeRegistry
Serenity's type system relies on various type registries to locate types, such as editors and enums, using their registration names or keys. There is also a Q.initTypes
method that is called after the document.ready
event and scans the global object (window
) to discover types.
Some of the registries did not function correctly if the initialization method was not executed or if they were unable to find types registered or loaded after the initialization method ran (e.g., dynamically loaded scripts or dynamic imports). This occasionally caused issues, especially after switching to ES modules.
All of the type registries have been rewritten to use a unified discovery mechanism and can locate types that are dynamically loaded after initialization or even if the initialization method has not yet been executed, as long as the types have decorators like registerClass
with a proper key.
This should significantly reduce the occurrence of error messages such as Editor XYZ could not be found
, etc. In rare cases where the system does not work, you will at least receive a more informative error message like the following:
SomeSampleName formatter class not found!
Ensure there is a formatter type under the project root namespace with namespace parts starting with capital letters, like MyProject.MyModule.MyFormatter.
If using ES modules, make sure the formatter type has a decorator like @Decorators.registerFormatter('MyProject.MyModule.MyFormatter') with the full name of your formatter type and "side-effect-import" this formatter class from the current "page.ts/grid.ts/dialog.ts" file (import "./thepath/to/MyFormatter.ts").
After applying fixes, build and run "node ./tsbuild.js" (or "tsc" if using namespaces) from the project folder.
[Breaking Change]
Default HTML Escaping for Toastr Notification Functions like Q.notifyError
The notification library that we rely on, Toastr, did not HTML-escape messages by default, making it vulnerable to HTML script injection attacks if untrusted user input was passed to notification methods such as Q.notifyAlert
, etc. While recent versions of Toastr have added an escape option, it is not the default.
To address this issue, we now HTML-escape any message strings passed to these functions by default and set Toastr's default HTML escape option to true.
This change may cause issues if you were using one of these methods and expected it to accept HTML input, as Toastr does by default. In this case, you will need to pass escapeHtml: false
(after evaluating the risks and manually escaping any user input) to continue accepting HTML input.
Q.notifyAlert(`<p>Some HTML message<b>User Entered:</b> ${htmlEncode(userInput)}</p>`, { escapeHtml: false });
Removed jQuery ScrollIntoView Dependency
When you click a category link in a Serenity form, we scroll the relevant category into view using the native JavaScript version (if the jQuery scrollintoview
plugin is not available).
You may remove the following script from appsettings.bundles.json:
"~/Serenity.Assets/Scripts/jquery.scrollintoview.js",
Removed jquery.iframe-transport.js
Dependency
This script was previously used by jquery.fileupload
for older browsers like IE 9, but it is no longer necessary as we no longer support Internet Explorer.
You may remove it from appsettings.bundles.json:
"~/Serenity.Assets/Scripts/jquery.iframe-transport.js",
Removed Serenity.Pro.UI.js
Dependency (StartSharp)
Serenity.Pro.UI is a legacy project that integrates Serenity Widgets as React components. It is currently used only by the Email client sample. If you don't use it, you may safely remove it from your bundles:
"~/Serenity.Pro.UI/index.js",
The email client sample is currently being rewritten with ES modules, and after that is completed, Serenity.Pro.UI will become obsolete.
Removed TemplateBundle, Introduced ColumnAndFormBundle
You might have the following bundle definition in your appsettings.bundles.json
:
"Site": [
"dynamic://ColumnsBundle",
"dynamic://FormBundle",
"dynamic://TemplateBundle"
"dynamic://FormBundle",
//...
]
The TemplateBundle
contained dialog templates and other templates that were defined in *.ts.html
or .Template.html
files. Since these templates are no longer used, and assuming you don't have any such files in your project, you can safely remove the TemplateBundle
.
The ColumnsBundle
and FormBundle
still contain essential column and form definitions, but they are now bundled together in a package named ColumnAndFormBundle
. You can replace the bundle definition above with the following:
"Site": [
"dynamic://ColumnAndFormBundle",
//...
]
Rewrote Dashboard with ES Modules (StartSharp)
As part of our transition to ES modules, we have rewritten the Dashboard page using ES modules. In this process, we removed many legacy and outdated components, such as jquery.knob
, jquery.icheck
, jquery.sparkline
, and jvectormap
. Instead, we have incorporated modern alternatives like ChartJS
and jsvectormap
.
Additionally, we have optimized the size of the Dashboard page by using data URIs for profile images and the WEBP
image format, which offers superior compression compared to PNG
and JPG
formats.
For more details, please refer to the latest DashboardIndex.cshtml
and DashboardPage.ts
in the StartSharp repository.
GroupItemMetadataProvider is Ported to ES Modules
We continue the process of porting SlickGrid plugins to SleekGrid and ES modules. The latest plugin to undergo this transition is GroupItemMetadataProvider
.
This plugin now offers the ability to display totals directly on group rows. This feature complements or replaces separate total rows, which are typically hidden when groups are collapsed. A StartSharp sample is currently in progress...
Q.alert and Q.confirm Methods are Suffixed with Dialog
The methods in the Q
namespace (@serenity-is/corelib/q) that display message dialogs, such as alert
, confirm
, and prompt
, were occasionally confused with the browser's default methods of the same names, especially after the switch to ES modules, which removed the Q.
namespace prefix.
To prevent this issue, we have added the suffix Dialog
to these methods. For example, Q.alert
has become Q.alertDialog
, and Q.confirm
is now Q.confirmDialog
.
The old functions are still available for the time being, but they are considered obsolete, and we recommend avoiding their use.
The TsBuild Trigger Argument is Now Obsolete
In your package.json, you may have the following:
"scripts": {
"prepare": "node ./tsbuild.js --trigger"
}
This allowed us to restore ES module typings from your project and package references to the node_modules
directory after running the npm install
command. The npm install
command deleted the fake module folders we created, so we had to recreate them after the installation was completed.
To trigger the target that handles restoration, we used a fake _trigger.ts
file. This prompted Visual Studio to run a design-time build when its file system watcher detected a change in a TypeScript file. However, this method did not work correctly if Visual Studio was not open during the npm install
. We have now discovered a more effective way to address this issue.
"scripts": {
"prepare": "dotnet build -target:RestoreTypings"
This command runs the RestoreTypings
target directly using MSBuild
, eliminating the reliance on Visual Studio's behavior.
[Breaking Change]
Some Row Properties Are Now Only Available via IRow or IEditableRow Interface
In the base Row
class, there are many properties and methods, such as TrackWithChecks
, TrackAssignments
, IdField
, NameField
, IgnoreConstraints
, and IsAnyFieldAssigned
, that are mostly implementation details and are rarely used. However, they still appear in IntelliSense when viewing the member list of an entity in Visual Studio.
Even though our JsonRowConverter
omits these properties during serialization/deserialization, System.Text.Json
(which we will be switching to soon) and several third-party tools, such as ApiExplorer
used by Swagger
, consider them properties that should be passed to/from service requests. This can even break the Swagger UI as it tries to traverse the IdField
and NameField
properties, which may contain circular references through RowFields
and Joins
metadata.
These properties are now only explicitly available via the IRow
interface. If you accessed them directly, you may need to cast your object, for example, "((IRow)myRow).IdField"
instead of "myRow.IdField"
.
There was also another set of properties that were only intended to be used with desktop applications such as WinForms and WPF, including BeginEdit
, EndEdit
, IsAnyFieldChanged
, and PreviousValues
. These properties are now exclusively accessible via the IEditableRow
interface.
Issues Causing Swagger UI Failures Are Resolved
Several changes were made to the Row
class, including making some properties only available via the IRow
interface. These changes were made to improve the completion list for row instances and resolve issues with Swagger integration.
Other changes to the way ServiceEndpoint
actions handle parameter binding should also resolve several issues with Swagger. These issues included expecting the service request object to be passed from the query string instead of the request body and assuming that IDbConnection
and IUnitOfWork
arguments are passed from the client.
While Swagger is not required to make your services available to third parties, it can be a useful tool for providing a user-friendly interface for others to discover and try your API.
We have not enabled Swagger by default in StartSharp
because we believe it is not needed by most users. However, we will be adding a tutorial on enabling Swagger integration in the StartSharp
repository for those who are interested.
[Breaking Change]
Slick.Event
is Renamed to Slick.EventEmitter
, and Slick.EventHandler
is Renamed to Slick.EventSubscriber
Similar to the Q.alert
style of functions, the Event
and EventHandler
types in the Slick
namespace were causing confusion with the default browser types. To resolve this, the following renames were made:
Slick.Event
was changed toSlick.EventEmitter
Slick.EventHandler
was changed toSlick.EventSubscriber
Slick.Handler
was changed toSlick.EventListener
Note that these types are typically only used with the SleekGrid
and Serenity
libraries. If you have used the old names, you may need to apply these changes.
The old names are still available at runtime for compatibility with legacy plugins, but the type names are not present in the TypeScript declarations.
Added TransactionSettings
with IsolationLevel
and DeferStart
Options
A new option type called TransactionSettings
has been added, which includes the following properties:
public class TransactionSettings
{
public IsolationLevel? IsolationLevel { get; set; }
public bool? DeferStart { get; set; }
}
It is used to control the UnitOfWork
transaction parameters that are automatically created in ServiceEndpoint
actions when the action includes an IUnitOfWork
typed parameter:
public SaveResponse Update(IUnitOfWork uow, SaveRequest<MyRow> request)
When an action includes an IUnitOfWork
parameter, Serenity will automatically create a UnitOfWork
object, which wraps a database transaction. You can now use the new TransactionSettingsAttribute
to override the transaction isolation level and defer transaction start options for actions or their controllers:
[TransactionSettings(IsolationLevel.ReadCommitted, DeferStart = true)]
public class SomeController : ServiceEndpoint
{
public SaveResponse Update(IUnitOfWork uow, SaveRequest<MyRow> request)
}
The UnitOfWork
constructor now includes optional IsolationLevel
and DeferStart
arguments to control the transaction isolation level and whether to defer the start of the transaction.
The default for IsolationLevel
is Unspecified
, which means the isolation level is determined by the connection provider as it was before.
By default, when a UnitOfWork
object is created, the connection is opened, and the transaction is started. This means that even if the method or request handler being called does not need to open a connection or start a transaction, it will still be done.
For example, if a save handler receives an entity with a null value for a non-null field, it will raise a validation error before any database operations are performed. In this case, the connection and transaction are not needed, but they are still created in the calling action.
This is not a problem for actions with an IDbConnection
argument, as the connections are not opened until they are needed. However, UnitOfWork
objects had to open the connection and start the transaction in their constructor.
The DeferStart
argument allows you to delay starting the transaction until the connection is opened. The StateChange
event of the DbConnection
is used to detect when the transaction is needed for the first time, such as when the connection state changes to Open
.
The default behavior is still to start the transaction immediately, as this has been the previous behavior, and changing it may have unexpected side effects. However, if you are aware of the potential risks and want to override the default behavior, you can do so in the Startup.cs
file:
services.Configure<TransactionSettings>(opt => opt.DeferStart = true);
Improved TSTypeLister
Performance
The TSTypeLister
class is used by sergen
and Serenity.Pro.Coder
to extract types from the TypeScript files in your project. It first needs to identify the list of included files in your tsconfig.json
, which it does by performing a directory scan for .ts
files in your project directory.
Previously, this directory scan would include full scans of large folders such as node_modules
, obj
, bin
, etc., even if the tsconfig.json
included a pattern like Modules/**/*
in the includes
field.
We have now optimized the directory scanner to avoid unnecessary scans of these large folders when such an includes
pattern is present. This can lead to significant improvements in build time, particularly for source generators that run in the background for every change in the source code. In some projects, we have seen a 10x improvement, with transform durations dropping from 5 seconds to 500ms.