Skip to content

Commit 2949191

Browse files
authored
feat(tables-metadata): Add codelist parameter to tables metadata endpoint PXWEB2-654 (#375)
* Add codelist handling and update API methods - Introduced `FixVariableRefsAndApplyCodelists` method in `ISelectionHandler` for fixing variable references and applying codelists. - Changed visibility of `FixVariableRefsAndApplyCodelists` in `SelectionHandler` to public. - Updated `GetMetadataById` in `TableApiController` to accept a new `codelist` parameter. - Added logic in `TableApiController` to process the `codelist` and handle potential errors. - Created `CreateVariablesSelectionFromCodelists` method in `SelectionUtil` to build `VariablesSelection` from codelists. - Updated `PxWeb.Api2.Server` package version to `2.0.0-beta.17`. * Preserve value notes and remove no valid cellnotes when changing codelist * Added tests for SelectionUtil * Added edge cases test for SelectionHander * Refactored code and added test * Did some minor improvment that SonarCloud suggested to fix * Some more improvments
1 parent cdf409d commit 2949191

File tree

9 files changed

+320
-3
lines changed

9 files changed

+320
-3
lines changed
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
using Note = PCAxis.Paxiom.Note;
2+
3+
namespace PxWeb.UnitTests.Data
4+
{
5+
[TestClass]
6+
public class PaxiomFixUtilTests
7+
{
8+
[TestMethod]
9+
public void ExtractNotes_WhenNoNotes_ReturnsEmptyDictionary()
10+
{
11+
// Arrange
12+
var variable = new Variable();
13+
14+
// Act
15+
var result = PaxiomFixUtil.ExtractNotes(variable);
16+
// Assert
17+
Assert.AreEqual(0, result.Count);
18+
}
19+
20+
[TestMethod]
21+
public void ExtractNotes_WhenNotesExist_ReturnsNotes()
22+
{
23+
// Arrange
24+
var variable = new Variable("VAR1", "VAR1", PlacementType.Heading, 1);
25+
var value = new Value("v1");
26+
PaxiomUtil.SetCode(value, "v1");
27+
variable.Values.Add(value);
28+
value = new Value("v2");
29+
PaxiomUtil.SetCode(value, "v2");
30+
variable.Values.Add(value);
31+
value = new Value("v3");
32+
PaxiomUtil.SetCode(value, "v3");
33+
variable.Values.Add(value);
34+
variable.Values[0].AddNote(new Note("My note", NoteType.Value, true));
35+
36+
// Act
37+
var result = PaxiomFixUtil.ExtractNotes(variable);
38+
// Assert
39+
Assert.AreEqual(1, result.Count);
40+
Assert.IsTrue(result.ContainsKey("v1"));
41+
Assert.AreEqual(1, result["v1"].Count);
42+
}
43+
44+
[TestMethod]
45+
public void RestoreNotes_WhenNotesExist_ReturnsNotes()
46+
{
47+
// Arrange
48+
var variable = new Variable("VAR1", "VAR1", PlacementType.Heading, 1);
49+
var value = new Value("v1");
50+
PaxiomUtil.SetCode(value, "v1");
51+
variable.Values.Add(value);
52+
value = new Value("v2");
53+
PaxiomUtil.SetCode(value, "v2");
54+
variable.Values.Add(value);
55+
value = new Value("v3");
56+
PaxiomUtil.SetCode(value, "v3");
57+
variable.Values.Add(value);
58+
var notes = new Dictionary<string, Notes>();
59+
var list = new Notes();
60+
list.Add(new Note("My note", NoteType.Value, true));
61+
notes.Add("v1", list);
62+
63+
// Act
64+
PaxiomFixUtil.RestoreNotes(variable, notes);
65+
66+
// Assert
67+
Assert.AreEqual(1, variable.Values[0].Notes.Count);
68+
}
69+
70+
[TestMethod]
71+
public void RestoreNotes_WhenNotesNotApplicable_ReturnsNotes()
72+
{
73+
// Arrange
74+
var variable = new Variable("VAR1", "VAR1", PlacementType.Heading, 1);
75+
var value = new Value("v1");
76+
PaxiomUtil.SetCode(value, "v1");
77+
variable.Values.Add(value);
78+
value = new Value("v2");
79+
PaxiomUtil.SetCode(value, "v2");
80+
variable.Values.Add(value);
81+
value = new Value("v3");
82+
PaxiomUtil.SetCode(value, "v3");
83+
variable.Values.Add(value);
84+
var notes = new Dictionary<string, Notes>();
85+
var list = new Notes();
86+
list.Add(new Note("My note", NoteType.Value, true));
87+
notes.Add("v4", list);
88+
89+
// Act
90+
PaxiomFixUtil.RestoreNotes(variable, notes);
91+
92+
// Assert
93+
Assert.IsFalse(variable.Values[0].HasNotes());
94+
}
95+
96+
[TestMethod]
97+
public void CleanCellnotes_WhenNoCellNotes_NoException()
98+
{
99+
// Arrange
100+
var variable = new Variable("VAR1", "VAR1", PlacementType.Heading, 1);
101+
var value = new Value("v1");
102+
PaxiomUtil.SetCode(value, "v1");
103+
variable.Values.Add(value);
104+
value = new Value("v2");
105+
PaxiomUtil.SetCode(value, "v2");
106+
variable.Values.Add(value);
107+
value = new Value("v3");
108+
PaxiomUtil.SetCode(value, "v3");
109+
variable.Values.Add(value);
110+
111+
var meta = new PXMeta();
112+
113+
// Act
114+
var result = PaxiomFixUtil.CleanCellnotes(meta, variable);
115+
116+
// Assert
117+
Assert.AreEqual(0, result);
118+
}
119+
120+
[TestMethod]
121+
public void CleanCellnotes_WhenCellNotes_MatchingWillBeRemoved()
122+
{
123+
// Arrange
124+
var variable = new Variable("VAR1", "VAR1", PlacementType.Heading, 1);
125+
var value = new Value("v1");
126+
PaxiomUtil.SetCode(value, "v1");
127+
variable.Values.Add(value);
128+
value = new Value("v2");
129+
PaxiomUtil.SetCode(value, "v2");
130+
variable.Values.Add(value);
131+
value = new Value("v3");
132+
PaxiomUtil.SetCode(value, "v3");
133+
variable.Values.Add(value);
134+
135+
var meta = new PXMeta();
136+
137+
var cellNote = new CellNote();
138+
cellNote.Conditions.Add(new VariableValuePair("VAR1", "v4"));
139+
cellNote.Conditions.Add(new VariableValuePair("VAR2", "v3"));
140+
meta.CellNotes.Add(cellNote);
141+
142+
cellNote = new CellNote();
143+
cellNote.Conditions.Add(new VariableValuePair("VAR2", "v3"));
144+
meta.CellNotes.Add(cellNote);
145+
146+
cellNote = new CellNote();
147+
cellNote.Conditions.Add(new VariableValuePair("VAR1", "v3"));
148+
meta.CellNotes.Add(cellNote);
149+
150+
// Act
151+
var result = PaxiomFixUtil.CleanCellnotes(meta, variable);
152+
153+
// Assert
154+
Assert.AreEqual(1, result);
155+
Assert.AreEqual(2, meta.CellNotes.Count);
156+
}
157+
}
158+
}

PxWeb.UnitTests/Data/SelectionHandlerTests.cs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,47 @@ public void Convert_InvalidSelection_ReturnsValidSelection()
132132
Assert.AreEqual(3, result.Length);
133133
}
134134

135+
[TestMethod]
136+
public void FixVariableRefsAndApplyCodelists_WhenNoExistingVaiableSpecified_ReturnFalseAndProblem()
137+
{
138+
// Arrange
139+
var codelist = new Dictionary<string, string>();
140+
codelist.Add("THIS_IS_A_INVALID_CODE", "m1");
141+
var variablesSelection = SelectionUtil.CreateVariablesSelectionFromCodelists(codelist);
142+
var configMock = GetConfigMock();
143+
var handler = new SelectionHandler(configMock.Object);
144+
var model = ModelStore.CreateModelA();
145+
var builderMock = new Mock<IPXModelBuilder>();
146+
builderMock.Setup(x => x.Model).Returns(model);
147+
handler.ExpandAndVerfiySelections(variablesSelection, builderMock.Object, out var problem);
148+
// Act
149+
var result = handler.FixVariableRefsAndApplyCodelists(builderMock.Object, variablesSelection, out problem);
150+
// Assert
151+
Assert.IsFalse(result);
152+
Assert.IsNotNull(problem);
153+
}
154+
155+
[TestMethod]
156+
public void FixVariableRefsAndApplyCodelists_WhenNoExistingCodeListSpecified_ReturnFalseAndProblem()
157+
{
158+
// Arrange
159+
var codelist = new Dictionary<string, string>();
160+
codelist.Add("measure", "THIS_VALUE_DOES_NOT_EXIST");
161+
var variablesSelection = SelectionUtil.CreateVariablesSelectionFromCodelists(codelist);
162+
var configMock = GetConfigMock();
163+
var handler = new SelectionHandler(configMock.Object);
164+
var model = ModelStore.CreateModelA();
165+
var builderMock = new Mock<IPXModelBuilder>();
166+
builderMock.Setup(x => x.Model).Returns(model);
167+
handler.ExpandAndVerfiySelections(variablesSelection, builderMock.Object, out var problem);
168+
// Act
169+
var result = handler.FixVariableRefsAndApplyCodelists(builderMock.Object, variablesSelection, out problem);
170+
// Assert
171+
Assert.IsFalse(result);
172+
Assert.IsNotNull(problem);
173+
}
174+
175+
135176
private static VariablesSelection CreateValidSelection()
136177
{
137178
var selection = new VariablesSelection();

PxWeb.UnitTests/Helpers/SelectionUtilTests.cs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,5 +102,38 @@ public void UseDefaultSelection_OneSelectionsDefined_ReturnsFalse()
102102
Assert.IsFalse(useDefaultSelection);
103103

104104
}
105+
106+
[TestMethod]
107+
public void CreateVariablesSelectionFromCodelists_WhenNoCodelist_ReturnsEmptySelection()
108+
{
109+
// Arrange
110+
var codelist = new Dictionary<string, string>();
111+
// Act
112+
var result = SelectionUtil.CreateVariablesSelectionFromCodelists(codelist);
113+
// Assert
114+
Assert.IsNotNull(result);
115+
Assert.AreEqual(0, result.Selection.Count);
116+
Assert.IsNotNull(result.Placement);
117+
Assert.AreEqual(0, result.Placement.Heading.Count);
118+
Assert.AreEqual(0, result.Placement.Stub.Count);
119+
}
120+
121+
[TestMethod]
122+
public void VariablesSelectionFromCodelists_WhenOneCodelist_ReturnsSelectionWithOneValue()
123+
{
124+
// Arrange
125+
var codelist = new Dictionary<string, string>
126+
{
127+
{ "A", "B" }
128+
};
129+
// Act
130+
var result = SelectionUtil.CreateVariablesSelectionFromCodelists(codelist);
131+
// Assert
132+
Assert.IsNotNull(result);
133+
Assert.AreEqual(1, result.Selection.Count);
134+
Assert.AreEqual("A", result.Selection[0].VariableCode);
135+
Assert.AreEqual("B", result.Selection[0].CodeList);
136+
137+
}
105138
}
106139
}

PxWeb/Code/Api2/DataSelection/ISelectionHandler.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,6 @@ public interface ISelectionHandler
88
{
99
bool ExpandAndVerfiySelections(VariablesSelection variablesSelection, IPXModelBuilder builder, out Problem? problem);
1010
Selection[] Convert(VariablesSelection variablesSelection);
11+
bool FixVariableRefsAndApplyCodelists(IPXModelBuilder builder, VariablesSelection variablesSelection, out Problem? problem);
1112
}
1213
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
using System.Linq;
2+
3+
using PCAxis.Paxiom;
4+
5+
namespace PxWeb.Code.Api2.DataSelection
6+
{
7+
public static class PaxiomFixUtil
8+
{
9+
public static int CleanCellnotes(PXMeta meta, Variable pxVariable)
10+
{
11+
int removed = 0;
12+
for (int i = meta.CellNotes.Count - 1; i >= 0; i--)
13+
{
14+
var cellNote = meta.CellNotes[i];
15+
// Check if there is a condition for the variable with a value that is no longer in the list of values
16+
foreach (var condition in cellNote.Conditions.Where(c => string.Equals(c.VariableCode, pxVariable.Code, StringComparison.OrdinalIgnoreCase) &&
17+
pxVariable.Values.FirstOrDefault(x => x.Code.Equals(c.ValueCode, StringComparison.InvariantCultureIgnoreCase)) is null))
18+
{
19+
meta.CellNotes.RemoveAt(i);
20+
removed++;
21+
}
22+
}
23+
return removed;
24+
}
25+
26+
public static void RestoreNotes(Variable variable, Dictionary<string, Notes> notes)
27+
{
28+
foreach (var valueCode in notes.Keys)
29+
{
30+
if (variable.Values.FirstOrDefault(x => x.Code.Equals(valueCode, System.StringComparison.InvariantCultureIgnoreCase)) is Value value)
31+
{
32+
foreach (var note in notes[valueCode])
33+
{
34+
value.AddNote(note);
35+
}
36+
}
37+
}
38+
}
39+
40+
public static Dictionary<string, Notes> ExtractNotes(Variable variable)
41+
{
42+
43+
// Extract notes
44+
var notes = new Dictionary<string, Notes>();
45+
foreach (var value in variable.Values.Where(v => v.HasNotes()))
46+
{
47+
notes.Add(value.Code, value.Notes);
48+
}
49+
50+
return notes;
51+
}
52+
}
53+
}

PxWeb/Code/Api2/DataSelection/SelectionHandler.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ private static bool ExpandSelectionExpression(VariableSelection variable, Variab
195195
/// <param name="variablesSelection">The VariablesSelection object to verify and apply codelists for</param>
196196
/// <param name="problem">Null if everything is ok, otherwise it describes whats wrong</param>
197197
/// <returns>True if everything was ok, else false</returns>
198-
private static bool FixVariableRefsAndApplyCodelists(IPXModelBuilder builder, VariablesSelection variablesSelection, out Problem? problem)
198+
public bool FixVariableRefsAndApplyCodelists(IPXModelBuilder builder, VariablesSelection variablesSelection, out Problem? problem)
199199
{
200200
problem = null;
201201

@@ -262,6 +262,8 @@ private static bool ApplyCodelist(IPXModelBuilder builder, Variable pxVariable,
262262

263263
if (!string.IsNullOrWhiteSpace(variable.CodeList))
264264
{
265+
var notes = PaxiomFixUtil.ExtractNotes(pxVariable);
266+
265267
if (variable.CodeList.StartsWith("agg_"))
266268
{
267269
if (!ApplyGrouping(builder, pxVariable, variable, out problem))
@@ -281,8 +283,14 @@ private static bool ApplyCodelist(IPXModelBuilder builder, Variable pxVariable,
281283
problem = ProblemUtility.NonExistentCodelist();
282284
return false;
283285
}
286+
287+
// Restore notes
288+
PaxiomFixUtil.RestoreNotes(pxVariable, notes);
289+
// Remove cellnotes
290+
PaxiomFixUtil.CleanCellnotes(builder.Model.Meta, pxVariable);
284291
}
285292

293+
286294
return true;
287295
}
288296

PxWeb/Controllers/Api2/TableApiController.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ public TableApiController(IDataSource dataSource, ILanguageHelper languageHelper
7272
_savedQueryBackendProxy = savedQueryBackendProxy;
7373
}
7474

75-
public override IActionResult GetMetadataById([FromRoute(Name = "id"), Required] string id, [FromQuery(Name = "lang")] string? lang, [FromQuery(Name = "defaultSelection")] bool? defaultSelection)
75+
public override IActionResult GetMetadataById([FromRoute(Name = "id"), Required] string id, [FromQuery(Name = "lang")] string? lang, [FromQuery(Name = "defaultSelection")] bool? defaultSelection, [FromQuery(Name = "codelist")] Dictionary<string, string>? codelist)
7676
{
7777
lang = _languageHelper.HandleLanguage(lang);
7878
IPXModelBuilder? builder = _dataSource.CreateBuilder(id, lang);
@@ -98,6 +98,14 @@ public override IActionResult GetMetadataById([FromRoute(Name = "id"), Required]
9898
_defaultSelectionAlgorithm.GetDefaultSelection(builder);
9999
}
100100
}
101+
else if (codelist is not null && codelist.Keys.Count > 0) //Check that we have codelist specified
102+
{
103+
var selections = SelectionUtil.CreateVariablesSelectionFromCodelists(codelist);
104+
if (!_selectionHandler.FixVariableRefsAndApplyCodelists(builder, selections, out Problem? problem))
105+
{
106+
return BadRequest(problem);
107+
}
108+
}
101109

102110
var model = builder.Model;
103111
Dataset ds = _datasetMapper.Map(model, id, lang);

PxWeb/Helper/Api2/SelectionUtil.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,21 @@ public static VariablesSelection CreateEmptyVariablesSelection()
6666
return selections;
6767
}
6868

69+
public static VariablesSelection CreateVariablesSelectionFromCodelists(Dictionary<string, string> codelist)
70+
{
71+
var selections = CreateEmptyVariablesSelection();
72+
73+
foreach (var key in codelist.Keys)
74+
{
75+
var selection = new VariableSelection();
76+
selection.VariableCode = key;
77+
selection.CodeList = codelist[key];
78+
selections.Selection.Add(selection);
79+
}
80+
81+
return selections;
82+
}
83+
6984
/// <summary>
7085
/// Adds a value to a variable selection. Only adds the value if it is not already in the selection
7186
/// </summary>

PxWeb/PxWeb.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@
4949
<PackageReference Include="PCAxis.Menu.ConfigDatamodelMenu" Version="1.0.8" />
5050
<PackageReference Include="PCAxis.Serializers" Version="1.7.0" />
5151
<PackageReference Include="PcAxis.Sql" Version="1.4.1" />
52-
<PackageReference Include="PxWeb.Api2.Server" Version="2.0.0-beta.16" />
52+
<PackageReference Include="PxWeb.Api2.Server" Version="2.0.0-beta.17" />
5353
<PackageReference Include="Swashbuckle.AspNetCore" Version="8.1.1" />
5454
<PackageReference Include="Swashbuckle.AspNetCore.Annotations" Version="8.1.1" />
5555
<PackageReference Include="Swashbuckle.AspNetCore.Newtonsoft" Version="8.1.1" />

0 commit comments

Comments
 (0)