Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: Update swagger summaries, add scopes, global description #1929

Merged
merged 7 commits into from
Feb 21, 2025
Merged
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
154 changes: 106 additions & 48 deletions docs/schema/V1/swagger.verified.json

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public override bool IsValid(ValidationContext<T> context, Guid? value)
public override string Name { get; } = "Uuid7Validator";

protected override string GetDefaultMessageTemplate(string errorCode) =>
"Invalid {PropertyName}. Expected big endian UUIDv7 format. Got '{PropertyValue}'. See [link TBD].";
"Invalid {PropertyName}. Expected big endian UUIDv7 format. Got '{PropertyValue}'.";
}

internal sealed class IsValidUuidV7TimestampValidator<T> : PropertyValidator<T, Guid?>
Expand All @@ -46,5 +46,5 @@ public override bool IsValid(ValidationContext<T> context, Guid? value)
public override string Name { get; } = "Uuid7TimestampValidator";

protected override string GetDefaultMessageTemplate(string errorCode)
=> "Invalid {PropertyName}. Expected the unix timestamp portion of the UUIDv7 to be in the past. Extrapolated '{date}' from '{PropertyValue}'. See [link TBD].";
=> "Invalid {PropertyName}. Expected the unix timestamp portion of the UUIDv7 to be in the past. Extrapolated '{date}' from '{PropertyValue}'.";
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ internal static class Constants

internal static class SwaggerSummary
{
internal const string GlobalDescription = "Dialogporten API description for both enduser and serviceowner users, as well as open metadata information for public key material.<br><br>All operations* described within this document require authentication and authorization. Read more at <a href=\"https://docs.altinn.studio/dialogporten/user-guides/authenticating/\">https://docs.altinn.studio/dialogporten/user-guides/authenticating/</a><br><br><strong>All GET operations* and POST operations may return or contain, respectively, personal identifiable information (national identity numbers and names).</strong><br><br>For more information about this product, see <a href=\"https://docs.altinn.studio/dialogporten\">https://docs.altinn.studio/dialogporten</a><br><br><em>* Except the metadata APIs";
internal const string ReturnedResult = "Successfully returned the dialog {0}.";
internal const string Created = "The UUID of the created dialog {0}. A relative URL to the newly created activity is set in the \"Location\" header.";
internal const string Deleted = "The dialog {0} was deleted successfully.";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
using Digdir.Domain.Dialogporten.Application.Common.Authorization;
using Digdir.Domain.Dialogporten.WebApi.Endpoints.V1.EndUser;
using Digdir.Domain.Dialogporten.WebApi.Endpoints.V1.ServiceOwner;
using NSwag;
using NSwag.Generation.Processors;
using NSwag.Generation.Processors.Contexts;

namespace Digdir.Domain.Dialogporten.WebApi.Common.Json;

public sealed class SecurityRequirementsOperationProcessor : IOperationProcessor
{
private const string JwtBearerAuth = "JWTBearerAuth";
private const string ServiceOwnerSearchPath = "/api/v1/serviceowner/dialogs";

public bool Process(OperationProcessorContext context)
{
var securityRequirement = context.OperationDescription.Operation.Security?.FirstOrDefault();
if (securityRequirement == null || !securityRequirement.TryGetValue(JwtBearerAuth, out var value))
{
return true;
}

securityRequirement[JwtBearerAuth] =
context.OperationDescription.Operation.Tags.FirstOrDefault() switch
{
var tag when string.Equals(tag, ServiceOwnerGroup.RoutePrefix, StringComparison.OrdinalIgnoreCase)
=> IsServiceOwnerSearchEndpoint(context.OperationDescription)
? new[] { AuthorizationScope.ServiceProvider, AuthorizationScope.ServiceProviderSearch }
: new[] { AuthorizationScope.ServiceProvider },

var tag when string.Equals(tag, EndUserGroup.RoutePrefix, StringComparison.OrdinalIgnoreCase)
=> new[] { AuthorizationScope.EndUser },

_ => value
};

return true;
}

private static bool IsServiceOwnerSearchEndpoint(OpenApiOperationDescription description)
=> description is { Path: ServiceOwnerSearchPath, Method: OpenApiOperationMethod.Get };
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ public GetDialogActivityEndpointSummary()
{
Summary = "Gets a single dialog activity";
Description = """
Gets a single activity belonging to a dialog. For more information see the documentation (link TBD).
Gets a single activity belonging to a dialog.
""";
Responses[StatusCodes.Status200OK] = Constants.SwaggerSummary.ReturnedResult.FormatInvariant("activity");
Responses[StatusCodes.Status401Unauthorized] = Constants.SwaggerSummary.EndUserAuthenticationFailure;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ public GetDialogSeenLogEndpointSummary()
{
Summary = "Gets a single dialog seen log record";
Description = """
Gets a single dialog seen log record. For more information see the documentation (link TBD).
Gets a single dialog seen log record.
""";

Responses[StatusCodes.Status200OK] = Constants.SwaggerSummary.ReturnedResult.FormatInvariant("seen log record");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public SearchDialogSeenLogEndpointSummary()
const string summary = "Gets all seen log records for a dialog";
Summary = summary;
Description = $"""
{summary}. For more information see the documentation (link TBD).
{summary}.
""";

Responses[StatusCodes.Status200OK] = Constants.SwaggerSummary.ReturnedResult.FormatInvariant("seen log records");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public GetDialogTransmissionEndpointSummary()
{
Summary = "Gets a single dialog transmission";
Description = """
Gets a single transmission belonging to a dialog. For more information see the documentation (link TBD).
Gets a single transmission belonging to a dialog.
""";
Responses[StatusCodes.Status200OK] = Constants.SwaggerSummary.ReturnedResult.FormatInvariant("transmission");
Responses[StatusCodes.Status401Unauthorized] = Constants.SwaggerSummary.EndUserAuthenticationFailure.FormatInvariant(AuthorizationScope.EndUser);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ public GetDialogEndpointSummary()
{
Summary = "Gets a single dialog";
Description = """
Gets a single dialog aggregate. For more information see the documentation (link TBD).
Gets a single dialog aggregate.
""";

Responses[StatusCodes.Status200OK] =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public SearchDialogEndpointSummary()
{
Summary = "Gets a list of dialogs";
Description = """
Performs a search for dialogs, returning a paginated list of dialogs. For more information see the documentation (link TBD).
Performs a search for dialogs, returning a paginated list of dialogs.

* All date parameters must contain explicit time zone. Example: 2023-10-27T10:00:00Z or 2023-10-27T10:00:00+01:00
* See "continuationToken" in the response for how to get the next page of results.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ public GetPartiesEndpointSummary()
{
Summary = "Gets the list of authorized parties for the end user";
Description = """
Gets the list of authorized parties for the end user. For more information see the documentation (link TBD).
Gets the list of authorized parties for the end user.
""";

Responses[StatusCodes.Status200OK] = "The list of authorized parties for the end user";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public CreateDialogActivityEndpointSummary()
{
Summary = "Adds a activity to a dialogs activity history";
Description = $"""
The activity is created with the given configuration. For more information see the documentation (link TBD).
The activity is created with the given configuration.

{Constants.SwaggerSummary.OptimisticConcurrencyNote}
""";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public GetDialogActivityEndpointSummary()
{
Summary = "Gets a single dialog activity";
Description = """
Gets a single activity belonging to a dialog. For more information see the documentation (link TBD).
Gets a single activity belonging to a dialog.
""";
Responses[StatusCodes.Status200OK] = Constants.SwaggerSummary.ReturnedResult.FormatInvariant("activity");
Responses[StatusCodes.Status401Unauthorized] = Constants.SwaggerSummary.ServiceOwnerAuthenticationFailure.FormatInvariant(AuthorizationScope.ServiceProvider);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ public GetDialogSeenLogEndpointSummary()
{
Summary = "Gets a single dialog seen log record";
Description = """
Gets a single dialog seen log record. For more information see the documentation (link TBD).
Gets a single dialog seen log record.
""";

Responses[StatusCodes.Status200OK] = Constants.SwaggerSummary.ReturnedResult.FormatInvariant("seen log record");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public SearchDialogSeenLogEndpointSummary()
const string summary = "Gets all seen log records for a dialog";
Summary = summary;
Description = $"""
{summary}. For more information see the documentation (link TBD).
{summary}.
""";

Responses[StatusCodes.Status200OK] = Constants.SwaggerSummary.ReturnedResult.FormatInvariant("seen log records");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public CreateDialogTransmissionEndpointSummary()
{
Summary = "Adds a transmission to a dialog";
Description = $"""
The transmission is created with the given configuration. For more information see the documentation (link TBD).
The transmission is created with the given configuration.

{Constants.SwaggerSummary.OptimisticConcurrencyNote}
""";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public GetDialogTransmissionEndpointSummary()
{
Summary = "Gets a single dialog transmission";
Description = """
Gets a single transmission belonging to a dialog. For more information see the documentation (link TBD).
Gets a single transmission belonging to a dialog.
""";
Responses[StatusCodes.Status200OK] = Constants.SwaggerSummary.ReturnedResult.FormatInvariant("transmission");
Responses[StatusCodes.Status401Unauthorized] = Constants.SwaggerSummary.ServiceOwnerAuthenticationFailure.FormatInvariant(AuthorizationScope.ServiceProvider);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public CreateDialogEndpointSummary()
{
Summary = "Creates a new dialog";
Description = """
The dialog is created with the given configuration. For more information see the documentation (link TBD).
The dialog is created with the given configuration.

For detailed information on validation rules, see [the source for CreateDialogCommandValidator](https://github.com/altinn/dialogporten/blob/main/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Create/CreateDialogCommandValidator.cs)
""";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public DeleteDialogEndpointSummary()
{
Summary = "Deletes a dialog";
Description = """
Deletes a given dialog (soft delete). For more information see the documentation (link TBD).
Deletes a given dialog (soft delete).

Note that the dialog will still be available on the single details endpoint, but will have a deleted status. It will not appear on the list endpoint for either service owners nor end users.
If end users attempt to access the dialog via the details endpoint, they will get a 410 Gone response.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public GetDialogEndpointSummary()
{
Summary = "Gets a single dialog";
Description = """
Gets a single dialog aggregate. For more information see the documentation (link TBD).
Gets a single dialog aggregate.

Note that this operation may return deleted dialogs (see the field `DeletedAt`).
""";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public PurgeDialogEndpointSummary()
{
Summary = "Permanently deletes a dialog";
Description = """
Deletes a given dialog (hard delete). For more information see the documentation (link TBD).
Deletes a given dialog (hard delete).

Optimistic concurrency control is implemented using the If-Match header. Supply the Revision value from the GetDialog endpoint to ensure that the dialog is not deleted by another request in the meantime.
""";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ public RestoreDialogEndpointSummary()
{
Summary = "Restore a dialog";
Description = """
Restore a dialog. For more information see the documentation (link TBD).
Restore a dialog.
""";

Responses[StatusCodes.Status204NoContent] = Constants.SwaggerSummary.Restored.FormatInvariant("aggregate");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public ListDialogEndpointSummary()
{
Summary = "Gets a list of dialogs";
Description = """
Performs a search for dialogs, returning a paginated list of dialogs. For more information see the documentation (link TBD).
Performs a search for dialogs, returning a paginated list of dialogs.

* All date parameters must contain explicit time zone. Example: 2023-10-27T10:00:00Z or 2023-10-27T10:00:00+01:00
* See "continuationToken" in the response for how to get the next page of results.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public UpdateDialogEndpointSummary()
{
Summary = "Replaces a dialog";
Description = $"""
Replaces a given dialog with the supplied model. For more information see the documentation (link TBD).
Replaces a given dialog with the supplied model.

{Constants.SwaggerSummary.OptimisticConcurrencyNote}
""";
Expand Down
4 changes: 4 additions & 0 deletions src/Digdir.Domain.Dialogporten.WebApi/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ static void BuildAndRun(string[] args)
document.RemoveSystemStringHeaderTitles();
};
s.Title = "Dialogporten";
s.Description = Constants.SwaggerSummary.GlobalDescription;
s.DocumentName = "v1";
s.Version = "v1";

Expand All @@ -150,6 +151,9 @@ static void BuildAndRun(string[] args)

// Adding ResponseHeaders for PATCH MVC controller
s.OperationProcessors.Add(new ProducesResponseHeaderOperationProcessor());

// Adding required scopes to security definitions
s.OperationProcessors.Add(new SecurityRequirementsOperationProcessor());
};
})
.AddControllers(options => options.InputFormatters.Insert(0, JsonPatchInputFormatter.Get()))
Expand Down
Loading