Skip to content

Commit e3a2dc7

Browse files
committed
Add support for passing a list of task ids to the pdf component for auto pdf.
1 parent 60c709d commit e3a2dc7

File tree

6 files changed

+165
-8
lines changed

6 files changed

+165
-8
lines changed

src/Altinn.App.Core/Internal/Pdf/IPdfService.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,14 @@ public interface IPdfService
2424
/// <param name="taskId">The task id for which the PDF is generated.</param>
2525
/// <param name="dataTypeId">The data type to use when storing the PDF.</param>
2626
/// <param name="fileNameTextResourceElementId">A text resource element id for the file name of the PDF. If no text resource is found, the literal value will be used. If null, a default file name will be used.</param>
27+
/// <param name="autoGeneratePdfForTaskIds">Enable auto-pdf for a list of tasks. Will not respect pdfLayoutName on those tasks, but use the main layout-set of the given tasks and render the components in summary mode. This setting will be ignored if the PDF task has a pdf layout set defined.</param>
2728
/// <param name="ct">Cancellation token for when a request should be stopped before it's completed.</param>
2829
Task GenerateAndStorePdf(
2930
Instance instance,
3031
string taskId,
3132
string? dataTypeId,
3233
string? fileNameTextResourceElementId,
34+
List<string>? autoGeneratePdfForTaskIds = null,
3335
CancellationToken ct = default
3436
) => GenerateAndStorePdf(instance, taskId, ct);
3537

src/Altinn.App.Core/Internal/Pdf/PdfService.cs

Lines changed: 57 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ public async Task GenerateAndStorePdf(Instance instance, string taskId, Cancella
7272
{
7373
using var activity = _telemetry?.StartGenerateAndStorePdfActivity(instance, taskId);
7474

75-
await GenerateAndStorePdfInternal(instance, taskId, null, null, ct);
75+
await GenerateAndStorePdfInternal(instance, taskId, null, null, null, ct);
7676
}
7777

7878
/// <inheritdoc/>
@@ -81,12 +81,20 @@ public async Task GenerateAndStorePdf(
8181
string taskId,
8282
string? dataTypeId,
8383
string? fileNameTextResourceElementId,
84+
List<string>? autoGeneratePdfForTaskIds = null,
8485
CancellationToken ct = default
8586
)
8687
{
8788
using var activity = _telemetry?.StartGenerateAndStorePdfActivity(instance, taskId);
8889

89-
await GenerateAndStorePdfInternal(instance, taskId, dataTypeId, fileNameTextResourceElementId, ct);
90+
await GenerateAndStorePdfInternal(
91+
instance,
92+
taskId,
93+
dataTypeId,
94+
fileNameTextResourceElementId,
95+
autoGeneratePdfForTaskIds,
96+
ct
97+
);
9098
}
9199

92100
/// <inheritdoc/>
@@ -102,7 +110,7 @@ public async Task<Stream> GeneratePdf(Instance instance, string taskId, bool isP
102110

103111
TextResource? textResource = await GetTextResource(instance, language);
104112

105-
return await GeneratePdfContent(instance, language, isPreview, textResource, ct);
113+
return await GeneratePdfContent(instance, language, isPreview, textResource, null, ct);
106114
}
107115

108116
/// <inheritdoc/>
@@ -116,6 +124,7 @@ private async Task GenerateAndStorePdfInternal(
116124
string taskId,
117125
string? dataTypeId,
118126
string? fileNameTextResourceElementId,
127+
List<string>? autoGeneratePdfForTaskIds = null,
119128
CancellationToken ct = default
120129
)
121130
{
@@ -127,7 +136,14 @@ private async Task GenerateAndStorePdfInternal(
127136

128137
TextResource? textResource = await GetTextResource(instance, language);
129138

130-
await using Stream pdfContent = await GeneratePdfContent(instance, language, false, textResource, ct);
139+
await using Stream pdfContent = await GeneratePdfContent(
140+
instance,
141+
language,
142+
false,
143+
textResource,
144+
autoGeneratePdfForTaskIds,
145+
ct
146+
);
131147

132148
string fileName = GetFileName(instance, textResource, fileNameTextResourceElementId);
133149
await _dataClient.InsertBinaryData(
@@ -146,6 +162,7 @@ private async Task<Stream> GeneratePdfContent(
146162
string language,
147163
bool isPreview,
148164
TextResource? textResource,
165+
List<string>? autoGeneratePdfForTaskIds,
149166
CancellationToken ct
150167
)
151168
{
@@ -154,7 +171,11 @@ CancellationToken ct
154171
.AppPdfPagePathTemplate.ToLowerInvariant()
155172
.Replace("{instanceid}", instance.Id);
156173

157-
Uri uri = BuildUri(baseUrl, pagePath, language);
174+
List<KeyValuePair<string, string>> autoPdfTaskIdsQueryParams = CreateAutoPdfTaskIdsQueryParams(
175+
autoGeneratePdfForTaskIds
176+
);
177+
178+
Uri uri = BuildUri(baseUrl, pagePath, language, autoPdfTaskIdsQueryParams);
158179

159180
bool displayFooter = _pdfGeneratorSettings.DisplayFooter;
160181

@@ -174,7 +195,12 @@ CancellationToken ct
174195
return pdfContent;
175196
}
176197

177-
private static Uri BuildUri(string baseUrl, string pagePath, string language)
198+
private static Uri BuildUri(
199+
string baseUrl,
200+
string pagePath,
201+
string language,
202+
List<KeyValuePair<string, string>>? additionalQueryParams = null
203+
)
178204
{
179205
// Uses string manipulation instead of UriBuilder, since UriBuilder messes up
180206
// query parameters in combination with hash fragments in the url.
@@ -188,6 +214,14 @@ private static Uri BuildUri(string baseUrl, string pagePath, string language)
188214
url += $"?lang={language}";
189215
}
190216

217+
if (additionalQueryParams != null)
218+
{
219+
foreach (KeyValuePair<string, string> param in additionalQueryParams)
220+
{
221+
url += $"&{param.Key}={param.Value}";
222+
}
223+
}
224+
191225
return new Uri(url);
192226
}
193227

@@ -365,4 +399,21 @@ private string GetFooterContent(Instance instance, TextResource? textResource)
365399
</div>";
366400
return footerTemplate;
367401
}
402+
403+
private static List<KeyValuePair<string, string>> CreateAutoPdfTaskIdsQueryParams(
404+
List<string>? autoGeneratePdfForTaskIds
405+
)
406+
{
407+
List<KeyValuePair<string, string>> additionalQueryParams = [];
408+
// Create query param array for autoGeneratePdfForTaskIds if provided, task=1&task=2 etc.
409+
if (autoGeneratePdfForTaskIds != null && autoGeneratePdfForTaskIds.Count != 0)
410+
{
411+
foreach (string taskId in autoGeneratePdfForTaskIds)
412+
{
413+
additionalQueryParams.Add(new KeyValuePair<string, string>("task", taskId));
414+
}
415+
}
416+
417+
return additionalQueryParams;
418+
}
368419
}

src/Altinn.App.Core/Internal/Process/Elements/AltinnExtensionProperties/AltinnPdfConfiguration.cs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,24 @@ public sealed class AltinnPdfConfiguration
1919
[XmlElement("filename", Namespace = "http://altinn.no/process")]
2020
public string? Filename { get; set; }
2121

22+
/// <summary>
23+
/// Enable auto-pdf for a list of tasks. Will not respect pdfLayoutName on those tasks, but use the main layout-set of the given tasks and render the components in summary mode. This setting will be ignored if the PDF task has a pdf layout set defined.
24+
/// </summary>
25+
[XmlArray(ElementName = "autoPdfTaskIds", Namespace = "http://altinn.no/process", IsNullable = true)]
26+
[XmlArrayItem(ElementName = "taskId", Namespace = "http://altinn.no/process")]
27+
public List<string>? AutoPdfTaskIds { get; set; } = [];
28+
2229
internal ValidAltinnPdfConfiguration Validate()
2330
{
2431
string? normalizedDataTypeId = string.IsNullOrWhiteSpace(DataTypeId) ? null : DataTypeId.Trim();
2532
string? normalizedFilename = string.IsNullOrWhiteSpace(Filename) ? null : Filename.Trim();
2633

27-
return new ValidAltinnPdfConfiguration(normalizedDataTypeId, normalizedFilename);
34+
return new ValidAltinnPdfConfiguration(normalizedDataTypeId, normalizedFilename, AutoPdfTaskIds);
2835
}
2936
}
3037

31-
internal readonly record struct ValidAltinnPdfConfiguration(string? DataTypeId, string? Filename);
38+
internal readonly record struct ValidAltinnPdfConfiguration(
39+
string? DataTypeId,
40+
string? Filename,
41+
List<string>? AutoPdfTaskIds
42+
);

src/Altinn.App.Core/Internal/Process/ProcessTasks/ServiceTasks/PdfServiceTask.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ await _pdfService.GenerateAndStorePdf(
4343
taskId,
4444
config.DataTypeId,
4545
config.Filename,
46+
config.AutoPdfTaskIds,
4647
context.CancellationToken
4748
);
4849

test/Altinn.App.Core.Tests/Internal/Pdf/PdfServiceTests.cs

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,59 @@ public void GetOverridenLanguage_NoLanguageInQuery_ShouldReturnNull()
299299
language.Should().BeNull();
300300
}
301301

302+
[Fact]
303+
public async Task GenerateAndStorePdf_WithAutoGeneratePdfForTaskIds_ShouldIncludeTaskIdsInUri()
304+
{
305+
// Arrange
306+
var autoGeneratePdfForTaskIds = new List<string> { "Task_1", "Task_2", "Task_3" };
307+
308+
_pdfGeneratorClient.Setup(s =>
309+
s.GeneratePdf(It.IsAny<Uri>(), It.IsAny<string?>(), It.IsAny<CancellationToken>())
310+
);
311+
_generalSettingsOptions.Value.ExternalAppBaseUrl = "https://{org}.apps.{hostName}/{org}/{app}";
312+
313+
var target = SetupPdfService(
314+
pdfGeneratorClient: _pdfGeneratorClient,
315+
generalSettingsOptions: _generalSettingsOptions
316+
);
317+
318+
Instance instance = new()
319+
{
320+
Id = $"509378/{Guid.NewGuid()}",
321+
AppId = "digdir/not-really-an-app",
322+
Org = "digdir",
323+
};
324+
325+
// Act
326+
await target.GenerateAndStorePdf(
327+
instance,
328+
"Task_PDF",
329+
null,
330+
null,
331+
autoGeneratePdfForTaskIds,
332+
CancellationToken.None
333+
);
334+
335+
// Assert
336+
_pdfGeneratorClient.Verify(
337+
s =>
338+
s.GeneratePdf(
339+
It.Is<Uri>(u =>
340+
u.Scheme == "https"
341+
&& u.Host == $"{instance.Org}.apps.{HostName}"
342+
&& u.AbsoluteUri.Contains(instance.AppId)
343+
&& u.AbsoluteUri.Contains(instance.Id)
344+
&& u.AbsoluteUri.Contains("task=Task_1")
345+
&& u.AbsoluteUri.Contains("task=Task_2")
346+
&& u.AbsoluteUri.Contains("task=Task_3")
347+
),
348+
It.Is<string?>(s => s == null),
349+
It.IsAny<CancellationToken>()
350+
),
351+
Times.Once
352+
);
353+
}
354+
302355
private PdfService SetupPdfService(
303356
Mock<IAppResources>? appResources = null,
304357
Mock<IDataClient>? dataClient = null,

test/Altinn.App.Core.Tests/Internal/Process/ServiceTasks/PdfServiceTaskTests.cs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,48 @@ public async Task Execute_Should_Call_GenerateAndStorePdf()
5858
instance.Process.CurrentTask.ElementId,
5959
null,
6060
FileName,
61+
It.IsAny<List<string>?>(),
6162
It.IsAny<CancellationToken>()
6263
),
6364
Times.Once
6465
);
6566
}
67+
68+
[Fact]
69+
public async Task Execute_Should_Pass_AutoPdfTaskIds_To_PdfService()
70+
{
71+
// Arrange
72+
var taskIds = new List<string> { "Task_1", "Task_2", "Task_3" };
73+
74+
_processReaderMock
75+
.Setup(x => x.GetAltinnTaskExtension("pdfTask"))
76+
.Returns(
77+
new AltinnTaskExtension
78+
{
79+
TaskType = "pdf",
80+
PdfConfiguration = new AltinnPdfConfiguration { Filename = "test.pdf", AutoPdfTaskIds = taskIds },
81+
}
82+
);
83+
84+
var instance = new Instance
85+
{
86+
Process = new ProcessState { CurrentTask = new ProcessElementInfo { ElementId = "pdfTask" } },
87+
};
88+
89+
var instanceMutatorMock = new Mock<IInstanceDataMutator>();
90+
instanceMutatorMock.Setup(x => x.Instance).Returns(instance);
91+
92+
var parameters = new ServiceTaskContext { InstanceDataMutator = instanceMutatorMock.Object };
93+
94+
var serviceTask = new PdfServiceTask(_pdfServiceMock.Object, _processReaderMock.Object, _loggerMock.Object);
95+
96+
// Act
97+
await serviceTask.Execute(parameters);
98+
99+
// Assert
100+
_pdfServiceMock.Verify(
101+
x => x.GenerateAndStorePdf(instance, "pdfTask", null, "test.pdf", taskIds, It.IsAny<CancellationToken>()),
102+
Times.Once
103+
);
104+
}
66105
}

0 commit comments

Comments
 (0)