Skip to content
Closed

Dev #2329

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
17 changes: 13 additions & 4 deletions .github/workflows/sonarsource-scan.yml
Original file line number Diff line number Diff line change
Expand Up @@ -62,12 +62,15 @@ jobs:
run: |
VERSION="${{ vars.UGM_BUILD_VERSION }}"
echo "Debug: UGM_BUILD_VERSION variable value: '$VERSION'"
if [ -z "$VERSION" ]; then
echo "BUILD_VERSION=1.0.0-dev" >> $GITHUB_ENV
echo "Using fallback version: 1.0.0-dev (UGM_BUILD_VERSION variable not set)"
else
if [ -n "$VERSION" ]; then
echo "BUILD_VERSION=$VERSION" >> $GITHUB_ENV
echo "Using project version: $VERSION"
# Replace ${BUILD_VERSION} with actual value
sed -i "s/\${BUILD_VERSION}/$VERSION/g" applications/Unity.GrantManager/sonar-project.properties
else
echo "UGM_BUILD_VERSION variable not set - removing sonar.projectVersion property"
# Remove the projectVersion line entirely if no version is available
sed -i "/sonar.projectVersion=/d" applications/Unity.GrantManager/sonar-project.properties
fi

- name: Restore dependencies
Expand All @@ -78,6 +81,12 @@ jobs:
working-directory: ./applications/Unity.GrantManager
run: dotnet build Unity.GrantManager.sln --no-restore

- name: SonarCloud sonar.projectVersion
run: |
echo "BUILD_VERSION environment variable: $BUILD_VERSION"
echo "Updated sonar-project.properties:"
cat applications/Unity.GrantManager/sonar-project.properties | grep "sonar.projectVersion" || echo "No projectVersion set"

- name: SonarCloud Scan
uses: SonarSource/sonarqube-scan-action@v7
with:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,26 +121,21 @@ private static List<WorksheetComponentMetaDataItemDto> ParseDataGridField(Custom
var worksheetName = SanitizeName(worksheet.Name);
var dataGridName = SanitizeName(field.Key);

// Track whether we successfully extracted columns from CHEFS schema
bool extractedFromChefs = false;

// If dynamic is true, try to extract columns from form schema
if (dataGridDefinition.Dynamic)
{
var headerMappingKey = MatchHeaderMapping(field.Name + ".DataGrid", submissionHeaderMapping);
List<DataGridDefinitionColumn>? dynamicColumns = null;

if (!string.IsNullOrWhiteSpace(headerMappingKey))
{
dynamicColumns = ExtractDynamicDataGridColumns(headerMappingKey, formSchema);
}

if (dynamicColumns != null && dynamicColumns.Count > 0)
{
// We found columns in the form schema, use them
// CHEFS schema includes ALL columns (both static and dynamic), so we mark this as extracted
extractedFromChefs = true;

// We found dynamic columns in the CHEFS form schema; emit them first.
// Any statically-defined columns on the DataGrid are merged in below.
foreach (var column in dynamicColumns)
{
// Use the key for the component Key (becomes PropertyName), sanitize for ID
Expand Down Expand Up @@ -179,16 +174,25 @@ private static List<WorksheetComponentMetaDataItemDto> ParseDataGridField(Custom
}
}

// Process additional defined columns only if we haven't already extracted them from CHEFS
// When dynamic is true and CHEFS extraction succeeded, the CHEFS schema already includes
// all columns (both static and dynamic), so we skip this to avoid duplicates
if (!extractedFromChefs && dataGridDefinition.Columns != null && dataGridDefinition.Columns.Count > 0)
// Process additional defined columns from the DataGrid definition.
// For mixed grids (Dynamic == true), CHEFS only returns the dynamic wide columns and omits
// statically-defined ones, so we must merge them in here (dynamic wins on key collision).
if (dataGridDefinition.Columns != null && dataGridDefinition.Columns.Count > 0)
{
// Create a component for each column in the DataGrid
var existingKeys = new HashSet<string>(
components.Select(c => c.Key ?? string.Empty),
StringComparer.OrdinalIgnoreCase);

foreach (var column in dataGridDefinition.Columns)
{
// Skip columns that were already emitted from the CHEFS extraction
if (existingKeys.Contains(column.Name))
{
continue;
}

var columnName = SanitizeName(column.Name);

var component = new WorksheetComponentMetaDataItemDto
{
Id = $"{field.Id}_{columnName}",
Expand All @@ -199,8 +203,9 @@ private static List<WorksheetComponentMetaDataItemDto> ParseDataGridField(Custom
TypePath = $"worksheet->section->datagrid->{MapDataGridColumnType(column.Type)}",
DataPath = $"({worksheetName}){dataGridName}->{column.Name}"
};

components.Add(component);
existingKeys.Add(column.Name);
}
}
else if (!dataGridDefinition.Dynamic && (dataGridDefinition.Columns == null || dataGridDefinition.Columns.Count == 0))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ public async Task ParseDataGridField_DynamicWithFormSchema_ShouldExtractColumnsF
}

[Fact]
public async Task ParseDataGridField_DynamicWithFormSchema_ShouldSkipDefinedColumnsWhenChefsExtracted()
public async Task ParseDataGridField_DynamicWithFormSchema_ShouldMergeDefinedColumnsWithChefsExtracted()
{
// Arrange
using var uow = _unitOfWorkManager.Begin();
Expand All @@ -238,7 +238,7 @@ public async Task ParseDataGridField_DynamicWithFormSchema_ShouldSkipDefinedColu
await _worksheetRepository.InsertAsync(worksheet, true);
await uow.SaveChangesAsync();

// Definition has both dynamic=true AND static columns defined
// Definition has both dynamic=true AND static columns defined (mixed grid scenario)
var field = new CustomField(Guid.NewGuid(), "testDataGrid", "TestWorksheet", "Test DataGrid",
CustomFieldType.DataGrid,
@"{""dynamic"": true, ""columns"": [{""name"": ""staticCol"", ""type"": ""Text""}], ""summaryOption"": ""None""}");
Expand All @@ -264,16 +264,19 @@ public async Task ParseDataGridField_DynamicWithFormSchema_ShouldSkipDefinedColu
// Act
var result = WorksheetFieldSchemaParser.ParseField(field, worksheet, formSchema, submissionHeaderMapping);

// Assert — CHEFS extraction succeeded, so static columns should be skipped to avoid duplicates
// Assert — CHEFS only returns dynamic columns; statically-defined columns must still be emitted
result.ShouldNotBeNull();
result.Count.ShouldBe(1);
result.ShouldNotContain(c => c.Key == "staticCol");
result.Count.ShouldBe(2);
result.ShouldNotContain(c => c.Key == "dynamic_columns");

var dynamicCol = result.First();
dynamicCol.Key.ShouldBe("dynamicCol");
var dynamicCol = result.FirstOrDefault(c => c.Key == "dynamicCol");
dynamicCol.ShouldNotBeNull();
dynamicCol.Label.ShouldBe("Dynamic Column");
dynamicCol.Type.ShouldBe("Text");

var staticCol = result.FirstOrDefault(c => c.Key == "staticCol");
staticCol.ShouldNotBeNull();
staticCol.Type.ShouldBe("Text");
}

[Fact]
Expand Down Expand Up @@ -312,6 +315,44 @@ public async Task ParseDataGridField_DynamicWithNoHeaderMapping_ShouldFallBackTo
result.ShouldContain(c => c.Key == "col1");
}

[Fact]
public async Task ParseDataGridField_DynamicOnlyWithNoHeaderMapping_ShouldReturnOnlyPlaceholder()
{
// Arrange
using var uow = _unitOfWorkManager.Begin();

var worksheet = new Worksheet(Guid.NewGuid(), "TestWorksheet", "Test Worksheet");
var section = new WorksheetSection(Guid.NewGuid(), "TestSection");
worksheet.Sections.Add(section);

await _worksheetRepository.InsertAsync(worksheet, true);
await uow.SaveChangesAsync();

// Purely dynamic grid: dynamic=true and no static columns defined
var field = new CustomField(Guid.NewGuid(), "testDataGrid", "TestWorksheet", "Test DataGrid",
CustomFieldType.DataGrid,
@"{""dynamic"": true, ""columns"": [], ""summaryOption"": ""None""}");
section.AddField(field);
await uow.SaveChangesAsync();

worksheet = await _worksheetRepository.GetAsync(worksheet.Id);

// Header mapping does NOT contain an entry for this field, so CHEFS extraction cannot resolve
var submissionHeaderMapping = @"{""unrelated_key.DataGrid"": ""someGrid""}";
var formSchema = @"{ ""components"": [] }";

// Act
var result = WorksheetFieldSchemaParser.ParseField(field, worksheet, formSchema, submissionHeaderMapping);

// Assert — dynamic-only with no CHEFS resolution should yield exactly the placeholder
result.ShouldNotBeNull();
result.Count.ShouldBe(1);

var placeholder = result.First();
placeholder.Key.ShouldBe("dynamic_columns");
placeholder.Type.ShouldBe("Dynamic");
}

[Fact]
public async Task ParseDataGridField_DynamicWithNestedFormSchema_ShouldFindDataGridInPanel()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -792,18 +792,22 @@ $(function () {

if (newValue !== oldValue) {
$input.val(newValue);

// Clear any previous validation state
$input.removeClass('is-valid is-invalid');
$input.siblings('.invalid-feedback, .valid-feedback').remove();

// Validate the new value
validateColumnNameInput($input, newValue, path);
updatedCount++;
}
}
});

// Re-validate every input — uniqueness is a cross-row property, so rows whose
// value did not change can still transition from invalid to valid (and vice versa)
// once the rest of the table has been rewritten.
$('#ReportConfigurationTable .column-name-input').each(function () {
const $input = $(this);
const path = $input.data('path');
const value = $input.val().trim();

validateColumnNameInput($input, value, path);
});

if (updatedCount > 0) {
markAsChanged();
abp.message.success(`Successfully generated unique column names. ${updatedCount} column name(s) were updated.`);
Expand Down
1 change: 1 addition & 0 deletions applications/Unity.GrantManager/sonar-project.properties
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ sonar.host.url=https://sonarcloud.io

# Project metadata
sonar.projectName=Unity
sonar.projectVersion=${BUILD_VERSION}
sonar.projectDescription=Grant management application for the Province of British Columbia

# Source code settings
Expand Down
Loading
Loading