Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
66 commits
Select commit Hold shift + click to select a range
0ddcb61
Add support for service tasks, and add new service tasks for pdf gen…
bjorntore Jun 17, 2025
b1bcc4d
Remove default implementation of hook methods in standard service tasks.
bjorntore Jun 30, 2025
3ad4174
Skip validation if service task.
bjorntore Jun 30, 2025
2e79565
Always move to next for service tasks for now. Remoe service task res…
bjorntore Jul 2, 2025
21f71e8
Return element type on current task and process tasks to the client.
bjorntore Jul 4, 2025
fcc13bf
Some process engine method renaming.
bjorntore Jul 4, 2025
35097ee
Move current process state validation in process next into it's own m…
bjorntore Jul 4, 2025
4602c0a
Move "move next" check below result check.
bjorntore Jul 4, 2025
2cd3e82
Update PublicApi_ShouldNotChange_Unintentionally.verified.txt
bjorntore Aug 5, 2025
9fef45a
IServiceTask: Rename from params to context. Remove detailed error fi…
bjorntore Aug 6, 2025
1632b82
Remove ElementType again, as frontend didn't end up using it.
bjorntore Aug 6, 2025
6dba954
Revert adding Common namespace within ProcessTasks namespace.
bjorntore Aug 6, 2025
b8fa40e
Revert NextElement to ProcessNext rename in ProcessController.
bjorntore Aug 6, 2025
3b33b4a
IProcessEngine: Revert the rename from Next to ProcessNext back to re…
bjorntore Aug 6, 2025
c132415
Add remark about saving of data when execute returns successful result.
bjorntore Aug 6, 2025
75eab85
Log service task failures as error.
bjorntore Aug 6, 2025
d375ce4
Add test for error message when process is ended.
bjorntore Aug 7, 2025
8e058b5
Return bpmn element type to frontend.
bjorntore Aug 27, 2025
010385d
Remove useless variables.
bjorntore Sep 5, 2025
0124a54
Merge branch 'main' into feat/pdf-service-task
bjorntore Sep 5, 2025
dbdb128
Merge branch 'main' into feat/pdf-service-task
bjorntore Sep 10, 2025
3efdf3e
Merge branch 'main' into feat/pdf-service-task
bjorntore Sep 25, 2025
81aa1c3
Add default body for new method on IPdfService to avoid breaking change.
bjorntore Sep 25, 2025
064c17c
Sanitize a log.
bjorntore Sep 25, 2025
ae04d4c
Nitpick: Use PascalCase for named placeholders.
bjorntore Sep 25, 2025
8fa3a92
Prevent NRE in ProcessReader when no service tasks exists.
bjorntore Sep 25, 2025
dd37c8a
Add usings in PdfService to dispose streams when done.
bjorntore Sep 25, 2025
1da33f0
Hmm
bjorntore Sep 25, 2025
74bcaa7
Hmm 2
bjorntore Sep 25, 2025
8e21b26
Add support for custom pdf data type.
bjorntore Sep 25, 2025
f7f6191
Try catch in standard service tasks.
bjorntore Sep 25, 2025
e0139d3
Ensure no empty strings in AltinnPdfConfiguration.
bjorntore Sep 25, 2025
6559707
Make our service tasks internal sealed.
bjorntore Sep 25, 2025
45432b1
Revert "Try catch in standard service tasks."
bjorntore Sep 25, 2025
1962825
Use serviceTaskResult helper methods.
bjorntore Sep 25, 2025
defee86
Make pdf config sealed.
bjorntore Sep 25, 2025
debeef0
Add ability to stop EFormidlingServiceTask from sending eFormidling s…
bjorntore Sep 25, 2025
a80248f
Refactor configuration of eFormidling to enable setting up everything…
bjorntore Sep 26, 2025
80c6b82
Summary doc improvement.
bjorntore Sep 26, 2025
be2d6ab
Fix receiver config.
bjorntore Sep 29, 2025
168e184
Avoid breaking change in IEFormidlingReceivers.
bjorntore Sep 29, 2025
c37baab
nullcheck
bjorntore Sep 29, 2025
9376556
Don't have common eformidling config provider anyways. Just have a he…
bjorntore Sep 30, 2025
dab8d2d
Remove usage of FluentAssertions.
bjorntore Oct 1, 2025
2cd9848
assert receiver
bjorntore Oct 1, 2025
8d7ece0
Add test for exception when legacy eformidling config is null.
bjorntore Oct 1, 2025
76b634e
Fix legacy config provider name and try to make AltinnEFormidlingconf…
bjorntore Oct 1, 2025
5cad79d
add unit tests for AltinnEFormidlingConfigurationTests validate
bjorntore Oct 1, 2025
7b0a284
Change "Enabled" to "Disabled" for eFormidling. Active choice to disa…
bjorntore Oct 1, 2025
8102c43
nitpick
bjorntore Oct 1, 2025
3d73516
Merge branch 'main' into feat/pdf-service-task
bjorntore Oct 2, 2025
4769da7
Merge branch 'main' into feat/pdf-service-task
bjorntore Oct 6, 2025
60c709d
Tweak token names in DefaultEformidlingService.
bjorntore Oct 7, 2025
e3a2dc7
Add support for passing a list of task ids to the pdf component for a…
bjorntore Oct 7, 2025
e6cc72c
Add public api verified changes after adding auto pdf.
bjorntore Oct 8, 2025
5819534
Untested subform pdf generation
bjorntore Oct 8, 2025
d7f89a0
SubformServiceTask authorization.
bjorntore Oct 10, 2025
d8b34d7
Add task id and fake page segment to url when generating subform pdf.
bjorntore Oct 10, 2025
a6d461a
accept empty current task
bjorntore Oct 10, 2025
d7816bc
Merge branch 'feat/pdf-service-task' into feat/subform-service-task
bjorntore Oct 30, 2025
46f908c
Some fixes after merge.
bjorntore Oct 30, 2025
9a7aa51
remove comment
bjorntore Oct 30, 2025
1e7f20a
Merge branch 'main' into feat/subform-service-task
bjorntore Oct 31, 2025
3840cd9
Don't use obsolete method.
bjorntore Oct 31, 2025
ba1e72a
remove unused variable
bjorntore Oct 31, 2025
81ae63c
Merge branch 'main' into feat/subform-service-task
bjorntore Oct 31, 2025
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
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,7 @@ private static void AddProcessServices(IServiceCollection services)

services.AddTransient<IServiceTask, PdfServiceTask>();
services.AddTransient<IServiceTask, EFormidlingServiceTask>();
services.AddTransient<IServiceTask, SubformPdfServiceTask>();
}

private static void AddActionServices(IServiceCollection services)
Expand Down
23 changes: 21 additions & 2 deletions src/Altinn.App.Core/Internal/Pdf/IPdfService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,32 @@ Task GenerateAndStorePdf(
string? customFileNameTextResourceKey,
List<string>? autoGeneratePdfForTaskIds = null,
CancellationToken ct = default
) => GenerateAndStorePdf(instance, taskId, ct);
) => throw new NotImplementedException();

/// <summary>
/// Generate a PDF of what the user can currently see from the given instance of an app. Saves the PDF
/// to storage as a new binary file associated with the predefined PDF data type in most apps.
/// </summary>
/// <param name="instance">The instance details.</param>
/// <param name="taskId">The task id for which the PDF is generated</param>
/// <param name="subformComponentId">The subform component identifier.</param>
/// <param name="subformDataElementId">The subform data element identifier.</param>
/// <param name="customFileNameTextResourceKey">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>
/// <param name="ct">Cancellation token for when a request should be stopped before it's completed.</param>
Task GenerateAndStoreSubformPdfs(
Instance instance,
string taskId,
string? customFileNameTextResourceKey,
string subformComponentId,
string subformDataElementId,
CancellationToken ct
) => throw new NotImplementedException();

/// <summary>
/// Generate a PDF of what the user can currently see from the given instance of an app.
/// </summary>
/// <param name="instance">The instance details.</param>
/// <param name="taskId">The task id for which the pdf is generated</param>
/// <param name="taskId">The task id for which the PDF is generated</param>
/// <param name="ct">Cancellation token for when a request should be stopped before it's completed.</param>
Task<Stream> GeneratePdf(Instance instance, string taskId, CancellationToken ct);

Expand Down
63 changes: 60 additions & 3 deletions src/Altinn.App.Core/Internal/Pdf/PdfService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ public async Task GenerateAndStorePdf(Instance instance, string taskId, Cancella
{
using var activity = _telemetry?.StartGenerateAndStorePdfActivity(instance, taskId);

await GenerateAndStorePdfInternal(instance, taskId, null, null, ct);
await GenerateAndStorePdfInternal(instance, taskId, null, null, null, null, ct);
}

/// <inheritdoc/>
Expand All @@ -80,11 +80,34 @@ await GenerateAndStorePdfInternal(
instance,
taskId,
customFileNameTextResourceKey,
null,
null,
autoGeneratePdfForTaskIds,
ct
);
}

/// <inheritdoc/>
public async Task GenerateAndStoreSubformPdfs(
Instance instance,
string taskId,
string? customFileNameTextResourceKey,
string subformComponentId,
string subformDataElementId,
CancellationToken ct
)
{
await GenerateAndStorePdfInternal(
instance,
taskId,
customFileNameTextResourceKey,
subformComponentId,
subformDataElementId,
null,
ct
);
}

/// <inheritdoc/>
public async Task<Stream> GeneratePdf(Instance instance, string taskId, bool isPreview, CancellationToken ct)
{
Expand All @@ -96,7 +119,7 @@ public async Task<Stream> GeneratePdf(Instance instance, string taskId, bool isP

var language = GetOverriddenLanguage(queries) ?? await auth.GetLanguage();

return await GeneratePdfContent(instance, language, isPreview, null, ct);
return await GeneratePdfContent(instance, language, isPreview, null, null, null, ct);
}

/// <inheritdoc/>
Expand All @@ -109,6 +132,8 @@ private async Task GenerateAndStorePdfInternal(
Instance instance,
string taskId,
string? customFileNameTextResourceKey,
string? subformComponentId,
string? subformDataElementId,
List<string>? autoGeneratePdfForTaskIds = null,
CancellationToken ct = default
)
Expand All @@ -123,6 +148,8 @@ private async Task GenerateAndStorePdfInternal(
instance,
language,
false,
subformComponentId,
subformDataElementId,
autoGeneratePdfForTaskIds,
ct
);
Expand All @@ -143,6 +170,8 @@ private async Task<Stream> GeneratePdfContent(
Instance instance,
string language,
bool isPreview,
string? subformComponentId,
string? subformDataElementId,
List<string>? autoGeneratePdfForTaskIds,
CancellationToken ct
)
Expand All @@ -156,7 +185,15 @@ CancellationToken ct
autoGeneratePdfForTaskIds
);

Uri uri = BuildUri(baseUrl, pagePath, language, autoPdfTaskIdsQueryParams);
Uri uri = BuildUri(
instance.Process?.CurrentTask?.ElementId ?? string.Empty,
baseUrl,
pagePath,
language,
subformComponentId,
subformDataElementId,
autoPdfTaskIdsQueryParams
);

bool displayFooter = _pdfGeneratorSettings.DisplayFooter;

Expand All @@ -177,15 +214,35 @@ CancellationToken ct
}

private static Uri BuildUri(
string currentTaskId,
string baseUrl,
string pagePath,
string language,
string? subformComponentId,
string? subformDataElementId,
List<KeyValuePair<string, string>>? additionalQueryParams = null
)
{
// Uses string manipulation instead of UriBuilder, since UriBuilder messes up
// query parameters in combination with hash fragments in the url.
string url = baseUrl + pagePath;

if (!string.IsNullOrEmpty(subformComponentId) && !string.IsNullOrEmpty(subformDataElementId))
{
// Remove the ?pdf=1 part temporarily to insert subform segments
int pdfIndex = url.IndexOf("?pdf=1", StringComparison.OrdinalIgnoreCase);
if (pdfIndex > 0)
{
string beforePdf = $"{url[..pdfIndex]}/{currentTaskId}/subform";
string afterPdf = url[pdfIndex..];
url = $"{beforePdf}/{subformComponentId}/{subformDataElementId}/{afterPdf}";
}
else
{
url += $"/{currentTaskId}/subform/{subformComponentId}/{subformDataElementId}";
}
}

string lang = Uri.EscapeDataString(language);
if (url.Contains('?'))
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
using System.Diagnostics.CodeAnalysis;
using System.Xml.Serialization;
using Altinn.App.Core.Internal.App;

namespace Altinn.App.Core.Internal.Process.Elements.AltinnExtensionProperties;

/// <summary>
/// Configuration properties for payments in a process task
/// </summary>
public class AltinnSubformPdfConfiguration
{
/// <summary>
/// Set the filename of the PDF using a text resource key.
/// </summary>
[XmlElement("filenameTextResourceKey", Namespace = "http://altinn.no/process")]
public string? FilenameTextResourceKey { get; set; }

/// <summary>
/// The ID of the subform component to render in the PDFs.
/// </summary>
[XmlElement("subformComponentId", Namespace = "http://altinn.no/process")]
public string? SubformComponentId { get; set; }

/// <summary>
/// The ID of the data type associated with the subform component.
/// </summary>
[XmlElement("subformDatatTypeId", Namespace = "http://altinn.no/process")]
public string? SubformDataTypeId { get; set; }

/// <summary>
/// Whether to generate PDFs for multiple subform data elements in parallel or sequentially.
/// Defaults to false (sequential) to avoid overwhelming the PDF microservice.
/// Set to true only if your PDF microservice can handle concurrent requests.
/// </summary>
[XmlElement("parallelExecution", Namespace = "http://altinn.no/process")]
public bool ParallelExecution { get; set; }

internal ValidAltinnSubformPdfConfiguration Validate()
{
List<string>? errorMessages = null;

string? normalizedFilename = string.IsNullOrWhiteSpace(FilenameTextResourceKey)
? null
: FilenameTextResourceKey.Trim();

if (SubformComponentId.IsNullOrWhitespace(ref errorMessages, $"{nameof(SubformComponentId)} is missing."))
ThrowApplicationConfigException(errorMessages);

if (SubformDataTypeId.IsNullOrWhitespace(ref errorMessages, $"{nameof(SubformDataTypeId)} is missing."))
ThrowApplicationConfigException(errorMessages);

return new ValidAltinnSubformPdfConfiguration(
normalizedFilename,
SubformComponentId,
SubformDataTypeId,
ParallelExecution
);
}

[DoesNotReturn]
private static void ThrowApplicationConfigException(List<string> errorMessages)
{
throw new ApplicationConfigException(
"Subform pdf service task configuration is not valid: " + string.Join(",\n", errorMessages)
);
}
}

internal readonly record struct ValidAltinnSubformPdfConfiguration(
string? FilenameTextResourceKey,
string SubformComponentId,
string SubformDataTypeId,
bool ParallelExecution
);
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,12 @@ public class AltinnTaskExtension
[XmlElement("eFormidlingConfig", Namespace = "http://altinn.no/process")]
public AltinnEFormidlingConfiguration? EFormidlingConfiguration { get; set; }

/// <summary>
/// Gets or sets the configuration for PDF
/// </summary>
[XmlElement("subformPdfConfig", Namespace = "http://altinn.no/process")]
public AltinnSubformPdfConfiguration? SubformPdfConfiguration { get; set; }

/// <summary>
/// Retrieves a configuration item for given environment, in a predictable manner.
/// Specific configurations (those specifying an environment) takes precedence over global configurations.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ public static string[] GetActionsThatAllowProcessNextForTaskType(string taskType
{
return taskType switch
{
"data" or "feedback" or "pdf" or "eFormidling" => ["write"],
"data" or "feedback" or "pdf" or "eFormidling" or "subform-pdf" => ["write"],
"payment" => ["pay", "write"],
"confirmation" => ["confirm"],
"signing" => ["sign", "write"],
Expand Down
Loading
Loading