Skip to content
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
67 changes: 67 additions & 0 deletions SysML2.NET.Tests/PSM/DTO/DataMapperTestFixture.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// ------------------------------------------------------------------------------------------------
// <copyright file="DataMapperTestFixture.cs" company="Starion Group S.A.">
//
// Copyright (C) 2022-2026 Starion Group S.A.
// SPDX-License-Identifier: Apache-2.0
//
// </copyright>
// ------------------------------------------------------------------------------------------------

namespace SysML2.NET.Tests.PSM.DTO
{
using System;

using NUnit.Framework;

using SysML2.NET.Common;
using SysML2.NET.Core.DTO.Root.Namespaces;
using SysML2.NET.PSM.DTO;

[TestFixture]
public class DataMapperTestFixture
{
[Test]
public void VerifyToPim()
{
Assert.That(DataMapper.ToPim(null), Is.Null);
Assert.That(() => DataMapper.ToPim(new UnmodelledDataRequest()), Throws.TypeOf<NotSupportedException>());

var element = new Namespace { Id = Guid.NewGuid() };
var externalData = new ExternalDataRequest { Id = Guid.NewGuid() };
var externalRelationship = new ExternalRelationshipRequest { Id = Guid.NewGuid(), Language = "the language" };
var projectUsage = new ProjectUsageRequest { Id = Guid.NewGuid(), UsedProject = Guid.NewGuid() };

using (Assert.EnterMultipleScope())
{
Assert.That(DataMapper.ToPim(element), Is.SameAs(element));
Assert.That(DataMapper.ToPim(externalData), Is.TypeOf<SysML2.NET.PIM.DTO.ExternalData>().With.Property("Id").EqualTo(externalData.Id));
Assert.That(DataMapper.ToPim(externalRelationship), Is.TypeOf<SysML2.NET.PIM.DTO.ExternalRelationship>().With.Property("Language").EqualTo("the language"));
Assert.That(DataMapper.ToPim(projectUsage), Is.TypeOf<SysML2.NET.PIM.DTO.ProjectUsage>().With.Property("Id").EqualTo(projectUsage.Id));
}
}

[Test]
public void VerifyToRequest()
{
Assert.That(DataMapper.ToRequest(null), Is.Null);
Assert.That(() => DataMapper.ToRequest(new ExternalData { Id = Guid.NewGuid() }), Throws.TypeOf<NotSupportedException>());

var element = new Namespace { Id = Guid.NewGuid() };
var externalData = new SysML2.NET.PIM.DTO.ExternalData { Id = Guid.NewGuid() };
var externalRelationship = new SysML2.NET.PIM.DTO.ExternalRelationship { Id = Guid.NewGuid(), Language = "the language" };
var projectUsage = new SysML2.NET.PIM.DTO.ProjectUsage { Id = Guid.NewGuid() };

using (Assert.EnterMultipleScope())
{
Assert.That(DataMapper.ToRequest(element), Is.SameAs(element));
Assert.That(DataMapper.ToRequest(externalData), Is.TypeOf<ExternalDataRequest>().With.Property("Id").EqualTo(externalData.Id));
Assert.That(DataMapper.ToRequest(externalRelationship), Is.TypeOf<ExternalRelationshipRequest>().With.Property("Language").EqualTo("the language"));
Assert.That(DataMapper.ToRequest(projectUsage), Is.TypeOf<ProjectUsageRequest>().With.Property("Id").EqualTo(projectUsage.Id));
}
}

private sealed class UnmodelledDataRequest : IDataRequest
{
}
}
}
128 changes: 126 additions & 2 deletions SysML2.NET.Tests/PSM/DTO/PimMappingTestFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -445,6 +445,130 @@ public void VerifyDataIdentityRoundTrip()
}
}

[Test]
public void VerifyDataVersionRequestRoundTrip()
{
Assert.That(() => new DataVersionRequest().FromPim(null), Throws.TypeOf<ArgumentNullException>());

var record = new PIM.DTO.DataVersion
{
Id = Guid.NewGuid(),
Alias = [.. TwoAliases],
Description = "the description",
Name = "the version",
Commit = Guid.NewGuid()
};

var empty = RoundTrip<DataVersionRequest, PIM.DTO.DataVersion>(record);

using (Assert.EnterMultipleScope())
{
Assert.That(empty.Identity, Is.Null);
Assert.That(empty.Payload, Is.Null);
Assert.That(empty.Id, Is.EqualTo(Guid.Empty));
Assert.That(empty.Commit, Is.EqualTo(Guid.Empty));
}

var identity = new PIM.DTO.DataIdentity { Id = Guid.NewGuid(), Name = "the identity" };
var payload = new PIM.DTO.ExternalData { Id = Guid.NewGuid(), ResourceIdentifier = "https://example.org/the-resource" };
record.Identity = identity;
record.Payload = payload;

var result = RoundTrip<DataVersionRequest, PIM.DTO.DataVersion>(record);

using (Assert.EnterMultipleScope())
{
Assert.That(result.Alias, Is.EqualTo(TwoAliases));
Assert.That(result.Description, Is.EqualTo(record.Description));
Assert.That(result.Name, Is.EqualTo(record.Name));
Assert.That(result.Identity.Id, Is.EqualTo(identity.Id));
Assert.That(result.Payload, Is.TypeOf<PIM.DTO.ExternalData>());
Assert.That(result.Payload.Id, Is.EqualTo(payload.Id));
Assert.That(((PIM.DTO.ExternalData)result.Payload).ResourceIdentifier, Is.EqualTo(payload.ResourceIdentifier));
}
}

[Test]
public void VerifyCommitRequestRoundTrip()
{
var commitRequest = new CommitRequest();

using (Assert.EnterMultipleScope())
{
Assert.That(() => commitRequest.FromPim(null), Throws.TypeOf<ArgumentNullException>());
Assert.That(() => commitRequest.FromPim(new PIM.DTO.Commit(), null), Throws.TypeOf<ArgumentNullException>());
Assert.That(() => commitRequest.FromPim(null, []), Throws.TypeOf<ArgumentNullException>());
}

var record = new PIM.DTO.Commit
{
Id = Guid.NewGuid(),
Alias = [.. TwoAliases],
Description = "the description",
Name = "the commit",
Change = [Guid.NewGuid()]
};

commitRequest.FromPim(record);

using (Assert.EnterMultipleScope())
{
Assert.That(commitRequest.Name, Is.EqualTo(record.Name));
Assert.That(commitRequest.Change, Has.Count.EqualTo(0));
Assert.That(() => commitRequest.FromPim(record, []), Throws.TypeOf<ArgumentException>());
}

var (emptyCommit, emptyChange) = new CommitRequest().ToPimWithChange();

using (Assert.EnterMultipleScope())
{
Assert.That(emptyCommit.Change, Has.Count.EqualTo(0));
Assert.That(emptyChange, Has.Count.EqualTo(0));
}

var request = new CommitRequest
{
Alias = [.. TwoAliases],
Description = "the description",
Name = "the commit",
Change =
[
new DataVersionRequest { Name = "the first version", Identity = new DataIdentityRequest { Id = Guid.NewGuid() } },
new DataVersionRequest { Name = "the second version", Payload = new ExternalDataRequest { Id = Guid.NewGuid() } }
]
};

var (commit, change) = request.ToPimWithChange();

using (Assert.EnterMultipleScope())
{
Assert.That(commit.Id, Is.Not.EqualTo(Guid.Empty));
Assert.That(commit.Alias, Is.EqualTo(TwoAliases));
Assert.That(commit.Name, Is.EqualTo(request.Name));
Assert.That(change, Has.Count.EqualTo(2));
Assert.That(commit.Change, Is.EqualTo(change.Select(version => version.Id)));
Assert.That(change.Select(version => version.Commit), Is.All.EqualTo(commit.Id));
Assert.That(change[0].Name, Is.EqualTo("the first version"));
Assert.That(change[0].Identity.Id, Is.EqualTo(request.Change[0].Identity.Id));
Assert.That(change[1].Payload, Is.TypeOf<PIM.DTO.ExternalData>());
Assert.That(request.ToPim().Change, Has.Count.EqualTo(2));
}

var restored = new CommitRequest();
restored.FromPim(commit, [.. change.Reverse()]);

using (Assert.EnterMultipleScope())
{
Assert.That(restored.Alias, Is.EqualTo(TwoAliases));
Assert.That(restored.Description, Is.EqualTo(request.Description));
Assert.That(restored.Name, Is.EqualTo(request.Name));
Assert.That(restored.Change, Has.Count.EqualTo(2));
Assert.That(restored.Change[0].Name, Is.EqualTo("the first version"));
Assert.That(restored.Change[0].Identity.Id, Is.EqualTo(request.Change[0].Identity.Id));
Assert.That(restored.Change[1].Payload, Is.TypeOf<ExternalDataRequest>());
}
}

[Test]
public void VerifyContractCoverage()
{
Expand All @@ -463,8 +587,8 @@ public void VerifyContractCoverage()

using (Assert.EnterMultipleScope())
{
Assert.That(mappable, Has.Count.EqualTo(23));
Assert.That(unmapped, Is.EquivalentTo(["Error", "CommitRequest", "DataVersionRequest", "MergeOutcome"]));
Assert.That(mappable, Has.Count.EqualTo(25));
Assert.That(unmapped, Is.EquivalentTo(["Error", "MergeOutcome"]));
Assert.That(mappable, Does.Contain("PrimitiveConstraint"));
}
}
Expand Down
63 changes: 63 additions & 0 deletions SysML2.NET/PSM/DTO/DataMapper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// ------------------------------------------------------------------------------------------------
// <copyright file="DataMapper.cs" company="Starion Group S.A.">
//
// Copyright (C) 2022-2026 Starion Group S.A.
// SPDX-License-Identifier: Apache-2.0
//
// </copyright>
// ------------------------------------------------------------------------------------------------

namespace SysML2.NET.PSM.DTO
{
using System;

using SysML2.NET.Common;

/// <summary>
/// Maps the payload of a data version between the Platform Independent Model and the REST/HTTP PSM request
/// </summary>
/// <remarks>
/// The payload alternatives are reached through <see cref="IData" /> and <see cref="IDataRequest" />,
/// which carry no Platform Independent Model counterpart of their own, so the dispatch lives here.
/// </remarks>
public static class DataMapper
{
/// <summary>
/// Maps a REST/HTTP PSM payload request onto its Platform Independent Model counterpart
/// </summary>
/// <param name="request">The payload to map.</param>
/// <returns>The Platform Independent Model payload, or <c>null</c> when the payload is <c>null</c>.</returns>
/// <exception cref="NotSupportedException">Thrown when the payload is of an alternative the Platform Independent Model does not model.</exception>
public static IData ToPim(IDataRequest request)
{
return request switch
{
null => null,
ExternalDataRequest externalData => externalData.ToPim(),
ExternalRelationshipRequest externalRelationship => externalRelationship.ToPim(),
ProjectUsageRequest projectUsage => projectUsage.ToPim(),
IData element => element,
_ => throw new NotSupportedException($"A {request.GetType().Name} has no Platform Independent Model counterpart.")
};
}

/// <summary>
/// Maps a Platform Independent Model payload onto its REST/HTTP PSM request alternative
/// </summary>
/// <param name="data">The payload to map.</param>
/// <returns>The request payload, or <c>null</c> when the payload is <c>null</c>.</returns>
/// <exception cref="NotSupportedException">Thrown when the payload is of a type the REST/HTTP PSM request does not model.</exception>
public static IDataRequest ToRequest(IData data)
{
return data switch
{
null => null,
PIM.DTO.ExternalData externalData => PimMapper.Map<ExternalDataRequest, PIM.DTO.ExternalData>(externalData),
PIM.DTO.ExternalRelationship externalRelationship => PimMapper.Map<ExternalRelationshipRequest, PIM.DTO.ExternalRelationship>(externalRelationship),
PIM.DTO.ProjectUsage projectUsage => PimMapper.Map<ProjectUsageRequest, PIM.DTO.ProjectUsage>(projectUsage),
IDataRequest element => element,
_ => throw new NotSupportedException($"A {data.GetType().Name} has no REST/HTTP PSM request counterpart.")
};
}
}
}
3 changes: 0 additions & 3 deletions SysML2.NET/PSM/DTO/IRequestOfTRecord.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,6 @@ namespace SysML2.NET.PSM.DTO
/// <typeparam name="TRecord">
/// The Platform Independent Model type that this request model corresponds to.
/// </typeparam>
/// <remarks>
/// Requests whose payload the Platform Independent Model cannot carry implement the non generic <see cref="IRequest"/> only.
/// </remarks>
public interface IRequest<TRecord> : IRequest, IPimMappable<TRecord>
{
}
Expand Down
108 changes: 108 additions & 0 deletions SysML2.NET/PSM/DTO/Mapping/CommitRequest.Mapping.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
// ------------------------------------------------------------------------------------------------
// <copyright file="CommitRequest.Mapping.cs" company="Starion Group S.A.">
//
// Copyright (C) 2022-2026 Starion Group S.A.
// SPDX-License-Identifier: Apache-2.0
//
// </copyright>
// ------------------------------------------------------------------------------------------------

namespace SysML2.NET.PSM.DTO
{
using System;
using System.Collections.Generic;
using System.Linq;

/// <summary>
/// Declares the Platform Independent Model counterpart of the Commit request model
/// </summary>
/// <remarks>
/// The request embeds its change while the Platform Independent Model commit references it, so the mapping yields
/// the commit together with its data versions, bound by identifiers generated at mapping time.
/// </remarks>
public partial class CommitRequest : IRequest<PIM.DTO.Commit>
{
/// <summary>
/// Converts this model into its Platform Independent Model record
/// </summary>
/// <returns>The commit, whose <c>change</c> references data versions that only <see cref="ToPimWithChange" /> returns.</returns>
/// <exception cref="NotSupportedException">Thrown when a change payload has no Platform Independent Model counterpart.</exception>
public PIM.DTO.Commit ToPim()
{
return this.ToPimWithChange().Commit;
}

/// <summary>
/// Converts this model into its Platform Independent Model commit and the data versions that commit changes
/// </summary>
/// <returns>The commit, and its change in the order of the request.</returns>
/// <exception cref="NotSupportedException">Thrown when a change payload has no Platform Independent Model counterpart.</exception>
public (PIM.DTO.Commit Commit, IReadOnlyList<PIM.DTO.DataVersion> Change) ToPimWithChange()
{
var commit = new PIM.DTO.Commit
{
Id = Guid.NewGuid(),
Alias = [.. this.Alias ?? []],
Description = this.Description,
Name = this.Name
};

var change = (this.Change ?? []).Select(request =>
{
var version = request.ToPim();
version.Id = Guid.NewGuid();
version.Commit = commit.Id;
return version;
}).ToList();

commit.Change = [.. change.Select(version => version.Id)];

return (commit, change);
}

/// <summary>
/// Populates this model from its Platform Independent Model record, leaving the change empty
/// </summary>
/// <param name="record">The record to read from.</param>
/// <exception cref="ArgumentNullException">Thrown when the record is <c>null</c>.</exception>
public void FromPim(PIM.DTO.Commit record)
{
if (record == null)
{
throw new ArgumentNullException(nameof(record));
}

Check warning on line 73 in SysML2.NET/PSM/DTO/Mapping/CommitRequest.Mapping.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use 'ArgumentNullException.ThrowIfNull' instead of explicitly throwing a new exception instance

See more on https://sonarcloud.io/project/issues?id=STARIONGROUP_SysML2.NET&issues=AaGDPgRbJxEkuf26ggNl&open=AaGDPgRbJxEkuf26ggNl&pullRequest=397

this.Alias = [.. record.Alias ?? []];
this.Description = record.Description;
this.Name = record.Name;
this.Change = [];
}

/// <summary>
/// Populates this model from its Platform Independent Model record and the data versions its change references
/// </summary>
/// <param name="record">The record to read from.</param>
/// <param name="change">The data versions that resolve the record's <c>change</c> identifiers.</param>
/// <exception cref="ArgumentNullException">Thrown when the record or the data versions are <c>null</c>.</exception>
/// <exception cref="ArgumentException">Thrown when a <c>change</c> identifier resolves to no data version, or two data versions share an identifier.</exception>
/// <exception cref="NotSupportedException">Thrown when a change payload has no REST/HTTP PSM request counterpart.</exception>
public void FromPim(PIM.DTO.Commit record, IEnumerable<PIM.DTO.DataVersion> change)
{
if (change == null)
{
throw new ArgumentNullException(nameof(change));
}

Check warning on line 94 in SysML2.NET/PSM/DTO/Mapping/CommitRequest.Mapping.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use 'ArgumentNullException.ThrowIfNull' instead of explicitly throwing a new exception instance

See more on https://sonarcloud.io/project/issues?id=STARIONGROUP_SysML2.NET&issues=AaGDPgRbJxEkuf26ggNm&open=AaGDPgRbJxEkuf26ggNm&pullRequest=397

this.FromPim(record);

var versions = change.ToDictionary(version => version.Id);

this.Change =
[
.. (record.Change ?? []).Select(identifier => versions.TryGetValue(identifier, out var version)
? PimMapper.Map<DataVersionRequest, PIM.DTO.DataVersion>(version)
: throw new ArgumentException($"The change {identifier} resolves to no data version.", nameof(change)))

Check warning on line 104 in SysML2.NET/PSM/DTO/Mapping/CommitRequest.Mapping.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

The parameter name 'change' is not declared in the argument list.

See more on https://sonarcloud.io/project/issues?id=STARIONGROUP_SysML2.NET&issues=AaGDPgRbJxEkuf26ggNk&open=AaGDPgRbJxEkuf26ggNk&pullRequest=397

Check warning on line 104 in SysML2.NET/PSM/DTO/Mapping/CommitRequest.Mapping.cs

View workflow job for this annotation

GitHub Actions / Build

The parameter name 'change' is not declared in the argument list.

Check warning on line 104 in SysML2.NET/PSM/DTO/Mapping/CommitRequest.Mapping.cs

View workflow job for this annotation

GitHub Actions / Build

The parameter name 'change' is not declared in the argument list.
];
}
}
}
Loading
Loading