Skip to content

0.0.9-alpha#9

Merged
ReneSchwarzer merged 51 commits intomainfrom
develop
Oct 31, 2025
Merged

0.0.9-alpha#9
ReneSchwarzer merged 51 commits intomainfrom
develop

Conversation

@ReneSchwarzer
Copy link
Member

This pull request introduces several improvements and updates to the project infrastructure, documentation, and test utilities. The most significant changes include the addition of a new GitHub Actions workflow for running unit tests, updates to documentation links and branding to reflect the new organization, enhancements to the build and documentation generation pipeline, and improvements to test fixtures and assertion utilities.

CI/CD and Build Pipeline Updates:

  • Added a new GitHub Actions workflow (.github/workflows/test.yml) to automatically build the project and run xUnit unit tests on pull requests and manual triggers. This ensures code quality and stability through automated testing.
  • Enhanced the documentation generation workflow (.github/workflows/generate-docs.yml) by building WebExpress.WebCore, preparing the wwwroot directory, and generating an API toc.yaml for improved navigation in API docs. Also fixed the artifact upload path.

Documentation and Branding Updates:

  • Updated all documentation and README links, as well as banner image URLs, to point to the new webexpress-framework organization instead of the previous user repository. This ensures consistency and proper attribution across the project. [1] [2] [3] [4] [5] [6] [7] [8]
  • Changed the API documentation output path in docs/docfx.json for improved compatibility with the new build pipeline.
  • Updated the table of contents in docs/toc.yml to link directly to the API documentation folder, improving navigation.

Test Infrastructure and Utilities:

  • Added a new assertion utility AssertExtensions in src/WebExpress.WebCore.Test/Fixture/AssertExtensions.cs to facilitate flexible string comparison in tests, supporting placeholders and whitespace normalization.
  • Improved test fixture setup in UnitTestFixture.cs by making the mock server context and component hub creation more robust and flexible. [1] [2]
  • Added a sample CSS file for styling test output in src/WebExpress.WebCore.Test/Assets/css/my-css.css.

API and Identity Model Adjustments:

  • Updated the mock identity group and factory to use "Policies" instead of "Roles" for group assignments, reflecting a shift in terminology and structure in the test data model. [1] [2]

License Update:

  • Updated the copyright year in the LICENSE file to 2025.

- now registers each individual asset instead of just the base path
@ReneSchwarzer ReneSchwarzer self-assigned this Oct 29, 2025
@ReneSchwarzer ReneSchwarzer requested a review from Copilot October 29, 2025 17:21
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This is a major refactoring and feature addition PR for the WebExpress.WebCore framework, bumping the version from 0.0.8.0 to 0.0.9.0. The changes introduce significant improvements to REST API handling, identity management terminology updates, new validation infrastructure, and various architectural enhancements.

Key Changes:

  1. Introduced comprehensive REST API validation infrastructure with fluent API support (RestApiValidator, RestApiValidationResult, RestApiError)
  2. Renamed "Role" to "Policy" throughout the identity management system for clearer terminology
  3. Added new response types (ResponseNoContent, ResponseUnprocessableEntity, ResponseCreated, ResponseMovedTemporarily, ResponseMovedPermanently) and removed deprecated redirect responses
  4. Introduced IIncludeManager for managing JavaScript and CSS client resources per application
  5. Refactored context path references to "Route" for consistency
  6. Enhanced HTML element manipulation with fluent APIs and improved CSS handling
  7. Added session cleanup functionality with configurable timeout

Reviewed Changes

Copilot reviewed 165 out of 165 changed files in this pull request and generated 63 comments.

Show a summary per file
File Description
WebExpress.WebCore.csproj Version bump to 0.0.9.0, repository URL updates, configuration changes
WebUri/UriEndpoint.cs, IUri.cs Added SetFragment method for URI fragment manipulation
WebUri/UriAuthority.cs Fixed typo: "adress" → "address"
WebRestApi/*.cs New validation infrastructure with fluent API for request validation
WebMessage/*.cs New response types, content type enum, BadRequestException
WebIdentity/*.cs Renamed "Role" to "Policy" across identity management
WebInclude/*.cs New include manager for client-side JavaScript/CSS resources
WebHtml/*.cs Enhanced HTML element APIs with fluent methods, CSS utilities
WebSession/*.cs Added session cleanup method with timeout support
WebStatusPage/*.cs Added description field support
WebTask/*.cs Generic type parameter renamed from T to TTask, task removal delayed
WebPackage/*.cs Enhanced package change detection, improved extraction logic
HttpServer.cs, WebEx.cs Added events for initialization/start/exit, improved error handling
ApplicationContext.cs Renamed ContextPath to Route
Comments suppressed due to low confidence (9)

src/WebExpress.WebCore/WebIdentity/IdentityManager.cs:217

  • This foreach loop implicitly filters its target sequence - consider filtering the sequence explicitly using '.Where(...)'.
    src/WebExpress.WebCore/WebIdentity/IdentityManager.cs:480
  • This foreach loop implicitly filters its target sequence - consider filtering the sequence explicitly using '.Where(...)'.
    src/WebExpress.WebCore/WebPackage/PackageManager.cs:466
  • Variable package may be null at this access as suggested by this null check.
    src/WebExpress.WebCore/WebPlugin/PluginManager.cs:355
  • Variable this._httpServerContext may be null at this access as suggested by this null check.
    src/WebExpress.WebCore/WebPlugin/PluginManager.cs:227
  • Variable this._httpServerContext may be null at this access as suggested by this null check.
    src/WebExpress.WebCore/WebPlugin/PluginManager.cs:277
  • Variable this._httpServerContext may be null at this access as suggested by this null check.
    src/WebExpress.WebCore/WebPlugin/PluginManager.cs:288
  • Variable this._httpServerContext may be null at this access as suggested by this null check.
    src/WebExpress.WebCore/WebPlugin/PluginManager.cs:333
  • Variable this._httpServerContext may be null at this access as suggested by this null check.
    src/WebExpress.WebCore/WebPlugin/PluginManager.cs:344
  • Variable this._httpServerContext may be null at this access as suggested by this null check.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +461 to +468
/// <summary>
/// Sets the fragment component of the URI.
/// </summary>
/// <returns>A new IUri instance with the updated fragment. The original URI remains unchanged.</returns>
public IUri SetFragment(string fragment)
{
return new UriEndpoint(Scheme, Authority, fragment, Query, PathSegments);
}
Copy link

Copilot AI Oct 29, 2025

Choose a reason for hiding this comment

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

The SetFragment method documentation is missing a parameter description for the fragment parameter. Add <param name=\"fragment\">The fragment value to set in the URI.</param> to complete the documentation.

Copilot uses AI. Check for mistakes.
/// <param name="id">The id of the task.</param>
/// <param name="handler">The event handler.</param>
/// <param name="args">The event argument.</param>
/// <typeparam name="TTask">The type of the task.</typeparam>-
Copy link

Copilot AI Oct 29, 2025

Choose a reason for hiding this comment

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

Remove the trailing hyphen at the end of the XML documentation comment.

Copilot uses AI. Check for mistakes.
Comment on lines +57 to +58
/// This method appends the specified errors to the existing collection. If
/// the array isempty, no changes are made.
Copy link

Copilot AI Oct 29, 2025

Choose a reason for hiding this comment

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

Corrected spelling of 'isempty' to 'is empty'.

Copilot uses AI. Check for mistakes.
Comment on lines +70 to +71
/// This method appends the specified errors to the existing collection. If
/// the array isempty, no changes are made.
Copy link

Copilot AI Oct 29, 2025

Choose a reason for hiding this comment

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

Corrected spelling of 'isempty' to 'is empty'.

Copilot uses AI. Check for mistakes.
Comment on lines +562 to +563
/// A function that evaluates the request and returns true" if the
/// condition is met; otherwise,false.
Copy link

Copilot AI Oct 29, 2025

Choose a reason for hiding this comment

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

Remove the extra quotation mark after 'true' and add a space before 'false'.

Copilot uses AI. Check for mistakes.
Comment on lines +259 to +263
catch (Exception)
{
// ignore errors while enumerating plugin output files
continue;
}
Copy link

Copilot AI Oct 29, 2025

Choose a reason for hiding this comment

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

Generic catch clause.

Copilot uses AI. Check for mistakes.
Comment on lines +275 to +279
catch
{
// fallback to file name if relative path fails
relativePath = Path.GetFileName(fileName);
}
Copy link

Copilot AI Oct 29, 2025

Choose a reason for hiding this comment

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

Generic catch clause.

Copilot uses AI. Check for mistakes.
Comment on lines +348 to +351
catch (Exception)
{
// ignore errors while enumerating and continue with parent
}
Copy link

Copilot AI Oct 29, 2025

Choose a reason for hiding this comment

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

Generic catch clause.

Copilot uses AI. Check for mistakes.
Comment on lines +357 to +360
catch
{
path = null;
}
Copy link

Copilot AI Oct 29, 2025

Choose a reason for hiding this comment

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

Generic catch clause.

Copilot uses AI. Check for mistakes.
Comment on lines +635 to +639
catch (Exception ex)
{
// keep running even if cleanup fails
_httpServerContext.Log.Exception(ex);
}
Copy link

Copilot AI Oct 29, 2025

Choose a reason for hiding this comment

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

Generic catch clause.

Copilot uses AI. Check for mistakes.
@ReneSchwarzer ReneSchwarzer changed the title Develop 0.0.9-alpha Oct 29, 2025
@ReneSchwarzer ReneSchwarzer requested a review from Copilot October 30, 2025 18:05
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

Copilot reviewed 165 out of 165 changed files in this pull request and generated 19 comments.

Comments suppressed due to low confidence (8)

src/WebExpress.WebCore/WebPackage/PackageBuilder.cs:1

  • In HtmlElementMetadataBase.cs, the Href setter now uses TrimEnd() which removes all trailing whitespace. The original code had logic to ensure a trailing slash was added if missing, but the new implementation removes this entirely. This appears to be incorrect as the variable name url suggests it should still be a valid URL, and the comment about ensuring trailing slash suggests this was intentional behavior that has been lost.
    src/WebExpress.WebCore/WebPackage/PackageManager.cs:466
  • Variable package may be null at this access as suggested by this null check.
    src/WebExpress.WebCore/WebPlugin/PluginManager.cs:355
  • Variable this._httpServerContext may be null at this access as suggested by this null check.
    src/WebExpress.WebCore/WebPlugin/PluginManager.cs:227
  • Variable this._httpServerContext may be null at this access as suggested by this null check.
    src/WebExpress.WebCore/WebPlugin/PluginManager.cs:277
  • Variable this._httpServerContext may be null at this access as suggested by this null check.
    src/WebExpress.WebCore/WebPlugin/PluginManager.cs:288
  • Variable this._httpServerContext may be null at this access as suggested by this null check.
    src/WebExpress.WebCore/WebPlugin/PluginManager.cs:333
  • Variable this._httpServerContext may be null at this access as suggested by this null check.
    src/WebExpress.WebCore/WebPlugin/PluginManager.cs:344
  • Variable this._httpServerContext may be null at this access as suggested by this null check.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

/// <returns>A new IUri instance with the updated fragment. The original URI remains unchanged.</returns>
public IUri SetFragment(string fragment)
{
return new UriEndpoint(Scheme, Authority, fragment, Query, PathSegments);
Copy link

Copilot AI Oct 30, 2025

Choose a reason for hiding this comment

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

The SetFragment method appears to be passing parameters in the wrong order. Based on the UriEndpoint constructor signature visible elsewhere in the codebase, the fragment parameter should typically come after Query, not before it. The correct order should likely be: new UriEndpoint(Scheme, Authority, PathSegments, Query, fragment) or similar, depending on the actual constructor signature.

Copilot uses AI. Check for mistakes.
Comment on lines +104 to +107
System.Threading.Tasks.Task.Delay(30000).ContinueWith(_ =>
{
WebEx.ComponentHub.TaskManager.RemoveTask(this);
});
Copy link

Copilot AI Oct 30, 2025

Choose a reason for hiding this comment

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

The task removal is now delayed by 30 seconds without awaiting the continuation. This could lead to a race condition where the task is accessed after it should have been removed but before the 30-second delay completes. Additionally, if the task is cancelled or the application shuts down, the continuation may still execute. Consider using CancellationToken or ensuring proper cleanup.

Copilot uses AI. Check for mistakes.
Comment on lines +420 to +426
else if (ContainsOnlyTextNodes(_elements, out var text))
{
closeTag = CloseTag;
nl = false;

builder.Append(text);
}
Copy link

Copilot AI Oct 30, 2025

Choose a reason for hiding this comment

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

The logic at line 422 sets closeTag = CloseTag, but this appears redundant since closeTag is a local boolean that's only used to determine whether to call ToPostString. The original code set closeTag = true for text-only content. Setting it to the property value CloseTag may not produce the intended behavior for self-closing elements.

Copilot uses AI. Check for mistakes.
Comment on lines +136 to +150
foreach (var customAttribute in permissionType.CustomAttributes
.Where(x => x.AttributeType.GetInterfaces().Contains(typeof(IPolicyAttribute))))
{
if
(
customAttribute.AttributeType.Name == typeof(PolicyAttribute<>).Name &&
customAttribute.AttributeType.Namespace == typeof(PolicyAttribute<>).Namespace)
{
var type = customAttribute.AttributeType.GenericTypeArguments.FirstOrDefault();
if (type != null && !policyTypes.Contains(type))
{
policyTypes.Add(type);
}
}
}
Copy link

Copilot AI Oct 30, 2025

Choose a reason for hiding this comment

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

This foreach loop implicitly filters its target sequence - consider filtering the sequence explicitly using '.Where(...)'.

Copilot uses AI. Check for mistakes.
Comment on lines +209 to +220
foreach (var customAttribute in policyType.CustomAttributes
.Where(x => x.AttributeType.GetInterfaces().Contains(typeof(IPermissionAttribute))))
{
if (customAttribute.AttributeType.Name == typeof(PermissionAttribute<>).Name && customAttribute.AttributeType.Namespace == typeof(PermissionAttribute<>).Namespace)
{
var type = customAttribute.AttributeType.GenericTypeArguments.FirstOrDefault();
if (type != null && !permissionTypes.Contains(type))
{
permissionTypes.Add(type);
}
}
}
Copy link

Copilot AI Oct 30, 2025

Choose a reason for hiding this comment

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

This foreach loop implicitly filters its target sequence - consider filtering the sequence explicitly using '.Where(...)'.

Copilot uses AI. Check for mistakes.
// permissions
foreach (var permissionType in assembly.GetTypes().Where
(
x => x.IsClass == true &&
Copy link

Copilot AI Oct 30, 2025

Choose a reason for hiding this comment

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

The expression 'A == true' can be simplified to 'A'.

Copilot uses AI. Check for mistakes.
// policies
foreach (var policyType in assembly.GetTypes().Where
(
x => x.IsClass == true &&
Copy link

Copilot AI Oct 30, 2025

Choose a reason for hiding this comment

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

The expression 'A == true' can be simplified to 'A'.

Copilot uses AI. Check for mistakes.
Comment on lines +271 to 275
catch
{
// fallback to file name if relative path fails
relativePath = Path.GetFileName(fileName);
}
Copy link

Copilot AI Oct 30, 2025

Choose a reason for hiding this comment

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

Generic catch clause.

Copilot uses AI. Check for mistakes.
Comment on lines +343 to +346
catch (Exception)
{
// ignore errors while enumerating and continue with parent
}
Copy link

Copilot AI Oct 30, 2025

Choose a reason for hiding this comment

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

Generic catch clause.

Copilot uses AI. Check for mistakes.
Comment on lines +352 to +355
catch
{
path = null;
}
Copy link

Copilot AI Oct 30, 2025

Choose a reason for hiding this comment

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

Generic catch clause.

Copilot uses AI. Check for mistakes.
@ReneSchwarzer ReneSchwarzer requested a review from Copilot October 30, 2025 19:28
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

Copilot reviewed 165 out of 165 changed files in this pull request and generated 5 comments.

Comments suppressed due to low confidence (8)

src/WebExpress.WebCore/WebTask/Task.cs:105

  • Disposable 'CancellationTokenSource' is created but not disposed.
    src/WebExpress.WebCore/WebPackage/PackageManager.cs:466
  • Variable package may be null at this access as suggested by this null check.
    src/WebExpress.WebCore/WebPlugin/PluginManager.cs:355
  • Variable this._httpServerContext may be null at this access as suggested by this null check.
    src/WebExpress.WebCore/WebPlugin/PluginManager.cs:227
  • Variable this._httpServerContext may be null at this access as suggested by this null check.
    src/WebExpress.WebCore/WebPlugin/PluginManager.cs:277
  • Variable this._httpServerContext may be null at this access as suggested by this null check.
    src/WebExpress.WebCore/WebPlugin/PluginManager.cs:288
  • Variable this._httpServerContext may be null at this access as suggested by this null check.
    src/WebExpress.WebCore/WebPlugin/PluginManager.cs:333
  • Variable this._httpServerContext may be null at this access as suggested by this null check.
    src/WebExpress.WebCore/WebPlugin/PluginManager.cs:344
  • Variable this._httpServerContext may be null at this access as suggested by this null check.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

IHtmlElement Add(params IHtmlAttribute[] attributes);

/// <summary>
/// Clear all elements frrom the html element.
Copy link

Copilot AI Oct 30, 2025

Choose a reason for hiding this comment

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

Corrected spelling of 'frrom' to 'from'.

Copilot uses AI. Check for mistakes.
{
var matches = Directory
.GetFiles(path, "*.*", SearchOption.AllDirectories)
.Where(x => x.Replace('\\', '/').EndsWith(normalizedTail, StringComparison.OrdinalIgnoreCase));
Copy link

Copilot AI Oct 30, 2025

Choose a reason for hiding this comment

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

The string replace operation x.Replace('\\', '/') is performed for every file in the directory during the filter operation. Consider performing the replace once on the normalized tail before the Where clause, or caching the normalized file paths to avoid repeated string allocations during enumeration.

Copilot uses AI. Check for mistakes.
/// <returns>True if any group grants the permission, false otherwise.</returns>
public bool CheckAccess(IApplicationContext applicationContext, IIdentity identity, Type permission)
{
return (identity?.Groups ?? []).Any(group => CheckAccess(applicationContext, group, permission));
Copy link

Copilot AI Oct 30, 2025

Choose a reason for hiding this comment

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

[nitpick] Using LINQ's Any with a lambda for recursive checking is more concise, but the foreach loop in the original code was clearer for debugging. If the permission check fails, it will be harder to determine which group was being evaluated. Consider keeping the foreach pattern for better debuggability.

Copilot uses AI. Check for mistakes.
var httpServerContext = UnitTestFixture.CreateHttpServerContextMock();
var componentHub = UnitTestFixture.CreateComponentHubMock(httpServerContext);
var packageManager = componentHub.PackageManager as PackageManager;
var packagePath = httpServerContext.PackagePath;
Copy link

Copilot AI Oct 30, 2025

Choose a reason for hiding this comment

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

Call to 'System.IO.Path.Combine'.

Suggested change
var packagePath = httpServerContext.PackagePath;
var packagePath = httpServerContext.PackagePath;
if (string.IsNullOrEmpty(packagePath))
{
throw new ArgumentException("Package path must not be null or empty.", nameof(packagePath));
}

Copilot uses AI. Check for mistakes.
}
builder.Append(nestedListText);
break;
case HtmlElement element:
Copy link

Copilot AI Oct 30, 2025

Choose a reason for hiding this comment

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

This assignment to element is useless, since its value is never read.

Copilot uses AI. Check for mistakes.
@ReneSchwarzer ReneSchwarzer merged commit 12fc57a into main Oct 31, 2025
1 check failed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants