Skip to content

Commit

Permalink
12696 automatically add payment data type to applicationmetadata when…
Browse files Browse the repository at this point in the history
… adding payment task (#12776)

* Add/delete dataType when taskType is payment or signing

* Support multiple data types for multiple payment or signing tasks

* Fix PR comments
  • Loading branch information
standeren committed May 13, 2024
1 parent 10c7c04 commit b477008
Show file tree
Hide file tree
Showing 25 changed files with 582 additions and 48 deletions.
18 changes: 18 additions & 0 deletions backend/src/Designer/Controllers/ProcessModelingController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -146,5 +146,23 @@ await _processModelingService.SaveProcessDefinitionFromTemplateAsync(editingCont
Stream processDefinitionStream = _processModelingService.GetProcessDefinitionStream(editingContext);
return new FileStreamResult(processDefinitionStream, MediaTypeNames.Text.Plain);
}

[HttpPost("data-type/{dataTypeId}")]
public async Task<ActionResult> AddDataTypeToApplicationMetadata(string org, string repo, [FromRoute] string dataTypeId, CancellationToken cancellationToken)
{
string developer = AuthenticationHelper.GetDeveloperUserName(HttpContext);
var editingContext = AltinnRepoEditingContext.FromOrgRepoDeveloper(org, repo, developer);
await _processModelingService.AddDataTypeToApplicationMetadataAsync(editingContext, dataTypeId, cancellationToken);
return Ok();
}

[HttpDelete("data-type/{dataTypeId}")]
public async Task<ActionResult> DeleteDataTypeFromApplicationMetadata(string org, string repo, [FromRoute] string dataTypeId, CancellationToken cancellationToken)
{
string developer = AuthenticationHelper.GetDeveloperUserName(HttpContext);
var editingContext = AltinnRepoEditingContext.FromOrgRepoDeveloper(org, repo, developer);
await _processModelingService.DeleteDataTypeFromApplicationMetadataAsync(editingContext, dataTypeId, cancellationToken);
return Ok();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Altinn.Platform.Storage.Interface.Models;
using Altinn.Studio.Designer.Infrastructure.GitRepository;
using Altinn.Studio.Designer.Models;
using Altinn.Studio.Designer.Models.App;
using Altinn.Studio.Designer.Services.Interfaces;
using NuGet.Versioning;

Expand Down Expand Up @@ -53,6 +55,33 @@ public Stream GetProcessDefinitionStream(AltinnRepoEditingContext altinnRepoEdit
return altinnAppGitRepository.GetProcessDefinitionFile();
}

public async Task AddDataTypeToApplicationMetadataAsync(AltinnRepoEditingContext altinnRepoEditingContext, string dataTypeId, CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
AltinnAppGitRepository altinnAppGitRepository = _altinnGitRepositoryFactory.GetAltinnAppGitRepository(altinnRepoEditingContext.Org, altinnRepoEditingContext.Repo, altinnRepoEditingContext.Developer);
ApplicationMetadata applicationMetadata = await altinnAppGitRepository.GetApplicationMetadata(cancellationToken);
if (!applicationMetadata.DataTypes.Exists(dataType => dataType.Id == dataTypeId))
{
applicationMetadata.DataTypes.Add(new DataType
{
Id = dataTypeId,
AllowedContentTypes = ["application/json"],
MaxCount = 1,
EnablePdfCreation = false
});
await altinnAppGitRepository.SaveApplicationMetadata(applicationMetadata);
}
}

public async Task DeleteDataTypeFromApplicationMetadataAsync(AltinnRepoEditingContext altinnRepoEditingContext, string dataTypeId, CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
AltinnAppGitRepository altinnAppGitRepository = _altinnGitRepositoryFactory.GetAltinnAppGitRepository(altinnRepoEditingContext.Org, altinnRepoEditingContext.Repo, altinnRepoEditingContext.Developer);
ApplicationMetadata applicationMetadata = await altinnAppGitRepository.GetApplicationMetadata(cancellationToken);
applicationMetadata.DataTypes.RemoveAll(dataType => dataType.Id == dataTypeId);
await altinnAppGitRepository.SaveApplicationMetadata(applicationMetadata);
}

private IEnumerable<string> EnumerateTemplateResources(SemanticVersion version)
{
return typeof(ProcessModelingService).Assembly.GetManifestResourceNames()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,5 +39,25 @@ public interface IProcessModelingService
/// <param name="altinnRepoEditingContext">An <see cref="AltinnRepoEditingContext"/>.</param>
/// <returns>A <see cref="Stream"/> of a process definition file.</returns>
Stream GetProcessDefinitionStream(AltinnRepoEditingContext altinnRepoEditingContext);

/// <summary>
/// Adds a simple dataType to applicationMetadata.
/// Used for adding a dataType when signing and payment tasks are added to the process.
/// </summary>
/// <param name="altinnRepoEditingContext">An <see cref="AltinnRepoEditingContext"/>.</param>
/// <param name="dataTypeId">Id for the added data type</param>
/// <param name="cancellationToken">A <see cref="CancellationToken"/> that observes if operation is cancelled.</param>
Task AddDataTypeToApplicationMetadataAsync(AltinnRepoEditingContext altinnRepoEditingContext,
string dataTypeId, CancellationToken cancellationToken = default);

/// <summary>
/// Deletes a simple dataType from applicationMetadata.
/// Used for deleting a dataType when signing and payment tasks are removed from the process.
/// </summary>
/// <param name="altinnRepoEditingContext">An <see cref="AltinnRepoEditingContext"/>.</param>
/// <param name="dataTypeId">Id for the data type to remove</param>
/// <param name="cancellationToken">A <see cref="CancellationToken"/> that observes if operation is cancelled.</param>
Task DeleteDataTypeFromApplicationMetadataAsync(AltinnRepoEditingContext altinnRepoEditingContext,
string dataTypeId, CancellationToken cancellationToken = default);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Text.Json;
using System.Threading.Tasks;
using Altinn.Platform.Storage.Interface.Models;
using Designer.Tests.Controllers.ApiTests;
using Designer.Tests.Utils;
using FluentAssertions;
using Microsoft.AspNetCore.Mvc.Testing;
using Xunit;

namespace Designer.Tests.Controllers.ProcessModelingController
{
public class AddDataTypeToApplicationMetadataTests : DisagnerEndpointsTestsBase<AddDataTypeToApplicationMetadataTests>, IClassFixture<WebApplicationFactory<Program>>
{
private static string VersionPrefix(string org, string repository, string dataTypeId) => $"/designer/api/{org}/{repository}/process-modelling/data-type/{dataTypeId}";

public AddDataTypeToApplicationMetadataTests(WebApplicationFactory<Program> factory) : base(factory)
{
}

[Theory]
[InlineData("ttd", "empty-app", "testUser", "paymentInformation-1234")]
public async Task AddDataTypeToApplicationMetadata_ShouldAddDataTypeAndReturnOK(string org, string app, string developer, string dataTypeId)
{
string targetRepository = TestDataHelper.GenerateTestRepoName();
await CopyRepositoryForTest(org, app, developer, targetRepository);
string url = VersionPrefix(org, targetRepository, dataTypeId);

using var request = new HttpRequestMessage(HttpMethod.Post, url);
using var response = await HttpClient.SendAsync(request);
response.StatusCode.Should().Be(HttpStatusCode.OK);

string appMetadataString = TestDataHelper.GetFileFromRepo(org, targetRepository, developer, "App/config/applicationmetadata.json");
Application appMetadata = JsonSerializer.Deserialize<Application>(appMetadataString, new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
});
DataType expectedDataType = new()
{
Id = dataTypeId,
AllowedContentTypes = new List<string>() { "application/json" },
MaxCount = 1,
MinCount = 0,
EnablePdfCreation = false,
EnableFileScan = false,
ValidationErrorOnPendingFileScan = false,
EnabledFileAnalysers = new List<string>(),
EnabledFileValidators = new List<string>()
};

appMetadata.DataTypes.Count.Should().Be(2);
appMetadata.DataTypes.Find(dataType => dataType.Id == dataTypeId).Should().BeEquivalentTo(expectedDataType);
}

[Theory]
[InlineData("ttd", "empty-app", "testUser", "ref-data-as-pdf")]
public async Task AddDataTypeToApplicationMetadataWhenExists_ShouldNotAddDataTypeAndReturnOK(string org, string app, string developer, string dataTypeId)
{
string targetRepository = TestDataHelper.GenerateTestRepoName();
await CopyRepositoryForTest(org, app, developer, targetRepository);
string url = VersionPrefix(org, targetRepository, dataTypeId);
using var request = new HttpRequestMessage(HttpMethod.Post, url);
using var response = await HttpClient.SendAsync(request);

response.StatusCode.Should().Be(HttpStatusCode.OK);

string appMetadataString = TestDataHelper.GetFileFromRepo(org, targetRepository, developer, "App/config/applicationmetadata.json");
Application appMetadata = JsonSerializer.Deserialize<Application>(appMetadataString, new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
});

appMetadata.DataTypes.Count.Should().Be(1);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Text.Json;
using System.Threading.Tasks;
using Altinn.Platform.Storage.Interface.Models;
using Designer.Tests.Controllers.ApiTests;
using Designer.Tests.Utils;
using FluentAssertions;
using Microsoft.AspNetCore.Mvc.Testing;
using Xunit;

namespace Designer.Tests.Controllers.ProcessModelingController
{
public class DeleteDataTypeFromApplicationMetadataTests : DisagnerEndpointsTestsBase<DeleteDataTypeFromApplicationMetadataTests>, IClassFixture<WebApplicationFactory<Program>>
{
private static string VersionPrefix(string org, string repository, string dataTypeId) => $"/designer/api/{org}/{repository}/process-modelling/data-type/{dataTypeId}";

public DeleteDataTypeFromApplicationMetadataTests(WebApplicationFactory<Program> factory) : base(factory)
{
}

[Theory]
[InlineData("ttd", "empty-app", "testUser", "ref-data-as-pdf")]
public async Task DeleteDataTypeFromApplicationMetadata_ShouldDeleteDataTypeAndReturnOK(string org, string app, string developer, string dataTypeId)
{
string targetRepository = TestDataHelper.GenerateTestRepoName();
await CopyRepositoryForTest(org, app, developer, targetRepository);
string url = VersionPrefix(org, targetRepository, dataTypeId);

using var request = new HttpRequestMessage(HttpMethod.Delete, url);
using var response = await HttpClient.SendAsync(request);
response.StatusCode.Should().Be(HttpStatusCode.OK);

string appMetadataString = TestDataHelper.GetFileFromRepo(org, targetRepository, developer, "App/config/applicationmetadata.json");
Application appMetadata = JsonSerializer.Deserialize<Application>(appMetadataString, new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
});

appMetadata.DataTypes.Count.Should().Be(0);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import { useDeleteLayoutSetMutation } from '../../hooks/mutations/useDeleteLayou
import { useAppMetadataModelIdsQuery } from 'app-shared/hooks/queries/useAppMetadataModelIdsQuery';
import { useUpdateProcessDataTypeMutation } from '../../hooks/mutations/useUpdateProcessDataTypeMutation';
import type { MetaDataForm } from 'app-shared/types/BpmnMetaDataForm';
import { useAddDataTypeToAppMetadata } from '../../hooks/mutations/useAddDataTypeToAppMetadata';
import { useDeleteDataTypeFromAppMetadata } from '../../hooks/mutations/useDeleteDataTypeFromAppMetadata';

enum SyncClientsName {
FileSyncSuccess = 'FileSyncSuccess',
Expand All @@ -45,6 +47,8 @@ export const ProcessEditor = (): React.ReactElement => {
);
const { mutate: mutateDataType, isPending: updateDataTypePending } =
useUpdateProcessDataTypeMutation(org, app);
const { mutate: addDataTypeToAppMetadata } = useAddDataTypeToAppMetadata(org, app);
const { mutate: deleteDataTypeFromAppMetadata } = useDeleteDataTypeFromAppMetadata(org, app);
const existingCustomReceiptName: string | undefined = useCustomReceiptLayoutSetName(org, app);
const { data: availableDataModelIds, isPending: availableDataModelIdsPending } =
useAppMetadataModelIdsQuery(org, app);
Expand Down Expand Up @@ -109,6 +113,8 @@ export const ProcessEditor = (): React.ReactElement => {
appLibVersion={appLibData.backendVersion}
bpmnXml={hasBpmnQueryError ? null : bpmnXml}
mutateDataType={mutateDataType}
addDataTypeToAppMetadata={addDataTypeToAppMetadata}
deleteDataTypeFromAppMetadata={deleteDataTypeFromAppMetadata}
saveBpmn={saveBpmnXml}
/>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { renderHookWithMockStore } from '../../test/mocks';
import { queriesMock } from 'app-shared/mocks/queriesMock';
import { waitFor } from '@testing-library/react';
import { useAddDataTypeToAppMetadata } from './useAddDataTypeToAppMetadata';

const org = 'org';
const app = 'app';
const dataTypeId = 'paymentInformation-1234';

describe('useAddDataTypeToAppMetadata', () => {
it('Calls addDataTypeToAppMetadata with correct arguments and payload', async () => {
const addDataTypeToAppMetadata = renderHookWithMockStore()(() =>
useAddDataTypeToAppMetadata(org, app),
).renderHookResult.result;
await addDataTypeToAppMetadata.current.mutateAsync({
dataTypeId,
});
await waitFor(() => expect(addDataTypeToAppMetadata.current.isSuccess).toBe(true));

expect(queriesMock.addDataTypeToAppMetadata).toHaveBeenCalledTimes(1);
expect(queriesMock.addDataTypeToAppMetadata).toHaveBeenCalledWith(org, app, dataTypeId);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { useMutation } from '@tanstack/react-query';
import { useServicesContext } from 'app-shared/contexts/ServicesContext';

export const useAddDataTypeToAppMetadata = (org: string, app: string) => {
const { addDataTypeToAppMetadata } = useServicesContext();

return useMutation({
mutationFn: ({ dataTypeId }: { dataTypeId: string }) =>
addDataTypeToAppMetadata(org, app, dataTypeId),
});
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { renderHookWithMockStore } from '../../test/mocks';
import { queriesMock } from 'app-shared/mocks/queriesMock';
import { useDeleteDataTypeFromAppMetadata } from './useDeleteDataTypeFromAppMetadata';
import { waitFor } from '@testing-library/react';

const org = 'org';
const app = 'app';
const dataTypeId = 'paymentInformation-1234';

describe('useDeleteDataTypeFromAppMetadata', () => {
it('Calls deleteDataTypeFromAppMetadata with correct arguments and payload', async () => {
const deleteDataTypeFromAppMetadata = renderHookWithMockStore()(() =>
useDeleteDataTypeFromAppMetadata(org, app),
).renderHookResult.result;
await deleteDataTypeFromAppMetadata.current.mutateAsync({
dataTypeId,
});
await waitFor(() => expect(deleteDataTypeFromAppMetadata.current.isSuccess).toBe(true));

expect(queriesMock.deleteDataTypeFromAppMetadata).toHaveBeenCalledTimes(1);
expect(queriesMock.deleteDataTypeFromAppMetadata).toHaveBeenCalledWith(org, app, dataTypeId);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { useMutation } from '@tanstack/react-query';
import { useServicesContext } from 'app-shared/contexts/ServicesContext';

export const useDeleteDataTypeFromAppMetadata = (org: string, app: string) => {
const { deleteDataTypeFromAppMetadata } = useServicesContext();

return useMutation({
mutationFn: ({ dataTypeId }: { dataTypeId: string }) =>
deleteDataTypeFromAppMetadata(org, app, dataTypeId),
});
};
1 change: 1 addition & 0 deletions frontend/language/src/nb.json
Original file line number Diff line number Diff line change
Expand Up @@ -776,6 +776,7 @@
"process_editor.configuration_panel_name_label": "Navn: ",
"process_editor.configuration_panel_no_task_message": "Velg en oppgave i diagrammet til venstre for å se detaljer om valgt oppgave.",
"process_editor.configuration_panel_no_task_title": "Ingen oppgave er valgt",
"process_editor.configuration_panel_payment_task": "Oppgave: Betaling",
"process_editor.configuration_panel_select_datamodel": "Velg en datamodell",
"process_editor.configuration_panel_set_datamodel": "Datamodell:",
"process_editor.configuration_panel_set_datamodel_link": "Legg til datamodell",
Expand Down
2 changes: 2 additions & 0 deletions frontend/packages/process-editor/src/ProcessEditor.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ const defaultProps: ProcessEditorProps = {
deleteLayoutSet: jest.fn(),
mutateLayoutSet: jest.fn(),
mutateDataType: jest.fn(),
addDataTypeToAppMetadata: jest.fn(),
deleteDataTypeFromAppMetadata: jest.fn(),
};

const renderProcessEditor = (bpmnXml: string) => {
Expand Down
6 changes: 6 additions & 0 deletions frontend/packages/process-editor/src/ProcessEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ export type ProcessEditorProps = {
deleteLayoutSet: BpmnApiContextProps['deleteLayoutSet'];
mutateLayoutSet: BpmnApiContextProps['mutateLayoutSet'];
mutateDataType: BpmnApiContextProps['mutateDataType'];
addDataTypeToAppMetadata: BpmnApiContextProps['addDataTypeToAppMetadata'];
deleteDataTypeFromAppMetadata: BpmnApiContextProps['deleteDataTypeFromAppMetadata'];
saveBpmn: (bpmnXml: string, metaData?: MetaDataForm) => void;
};

Expand All @@ -38,6 +40,8 @@ export const ProcessEditor = ({
deleteLayoutSet,
mutateLayoutSet,
mutateDataType,
addDataTypeToAppMetadata,
deleteDataTypeFromAppMetadata,
saveBpmn,
}: ProcessEditorProps): JSX.Element => {
const { t } = useTranslation();
Expand All @@ -61,6 +65,8 @@ export const ProcessEditor = ({
deleteLayoutSet={deleteLayoutSet}
mutateLayoutSet={mutateLayoutSet}
mutateDataType={mutateDataType}
addDataTypeToAppMetadata={addDataTypeToAppMetadata}
deleteDataTypeFromAppMetadata={deleteDataTypeFromAppMetadata}
saveBpmn={saveBpmn}
>
<BpmnConfigPanelFormContextProvider>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { shouldDisplayFeature } from 'app-shared/utils/featureToggleUtils';
import { generateRandomId } from 'app-shared/utils/generateRandomId';

const supportedEntries = ['create.exclusive-gateway', 'create.start-event', 'create.end-event'];

Expand Down Expand Up @@ -58,8 +59,16 @@ class SupportedPaletteProvider {
}),
signatureConfig: bpmnFactory.create('altinn:SignatureConfig', {
dataTypesToSign: bpmnFactory.create('altinn:DataTypesToSign', {
dataType: ['Model'],
dataTypes: [],
}),
signatureDataType: bpmnFactory.create(
'altinn:SignatureDataType',
{
dataType: bpmnFactory.create('altinn:DataType', {
dataType: `signatureInformation-${generateRandomId(4)}`,
}),
}, // What about uniqueFromSignaturesInDataTypes
),
}),
}),
],
Expand Down Expand Up @@ -127,7 +136,9 @@ class SupportedPaletteProvider {
}),
paymentConfig: bpmnFactory.create('altinn:PaymentConfig', {
paymentDataType: bpmnFactory.create('altinn:PaymentDataType', {
dataType: ['paymentInformation'],
dataType: bpmnFactory.create('altinn:DataType', {
dataType: `paymentInformation-${generateRandomId(4)}`,
}),
}),
}),
}),
Expand Down
Loading

0 comments on commit b477008

Please sign in to comment.